From 196ba1fc71e6b4505acf1fbad9cb6042a5616037 Mon Sep 17 00:00:00 2001 From: Philipp Hachtmann Date: Mon, 3 Oct 2016 03:16:44 +0200 Subject: [PATCH] First Commit of my working state This is a SIMH v 3.8 with cool readline additions which were not liked by Bob Supnik. For my H316 and pdp8 stuff, this version seems to work quite well. Furthermore my version has a weird and freaking fast alternative Makefile which compiles SIMH in seconds... Signed-off-by: Philipp Hachtmann --- 0readme_38.txt | 45 + 0readme_ethernet.txt | 315 ++ ALTAIR/altair.txt | 211 + ALTAIR/altair_cpu.c | 1189 +++++ ALTAIR/altair_defs.h | 42 + ALTAIR/altair_dsk.c | 368 ++ ALTAIR/altair_sio.c | 262 ++ ALTAIR/altair_sys.c | 313 ++ AltairZ80/altairz80_cpu.c | 6615 ++++++++++++++++++++++++++++ AltairZ80/altairz80_cpu_nommu.c | 4175 ++++++++++++++++++ AltairZ80/altairz80_defs.h | 119 + AltairZ80/altairz80_dsk.c | 548 +++ AltairZ80/altairz80_hdsk.c | 643 +++ AltairZ80/altairz80_net.c | 301 ++ AltairZ80/altairz80_sio.c | 1475 +++++++ AltairZ80/altairz80_sys.c | 790 ++++ AltairZ80/disasm.c | 1424 ++++++ AltairZ80/flashwriter2.c | 334 ++ AltairZ80/i8272.c | 906 ++++ AltairZ80/i8272.h | 49 + AltairZ80/i86.h | 296 ++ AltairZ80/i86_decode.c | 941 ++++ AltairZ80/i86_ops.c | 5487 +++++++++++++++++++++++ AltairZ80/i86_prim_ops.c | 1539 +++++++ AltairZ80/insns.h | 670 +++ AltairZ80/insnsa.c | 4443 +++++++++++++++++++ AltairZ80/insnsd.c | 4516 +++++++++++++++++++ AltairZ80/mfdc.c | 682 +++ AltairZ80/mfdc.h | 46 + AltairZ80/n8vem.c | 486 +++ AltairZ80/nasm.h | 941 ++++ AltairZ80/s100_64fdc.c | 1541 +++++++ AltairZ80/s100_disk1a.c | 853 ++++ AltairZ80/s100_disk2.c | 574 +++ AltairZ80/s100_fif.c | 467 ++ AltairZ80/s100_mdriveh.c | 260 ++ AltairZ80/s100_mdsad.c | 802 ++++ AltairZ80/s100_scp300f.c | 425 ++ AltairZ80/s100_selchan.c | 208 + AltairZ80/s100_ss1.c | 246 ++ AltairZ80/sim_imd.c | 365 ++ AltairZ80/sim_imd.h | 110 + AltairZ80/vfdhd.c | 650 +++ AltairZ80/vfdhd.h | 43 + AltairZ80/wd179x.c | 864 ++++ AltairZ80/wd179x.h | 67 + GRI/gri_cpu.c | 1075 +++++ GRI/gri_defs.h | 246 ++ GRI/gri_stddev.c | 409 ++ GRI/gri_sys.c | 660 +++ H316/h316_cpu.c | 1489 +++++++ H316/h316_defs.h | 205 + H316/h316_dp.c | 1044 +++++ H316/h316_fhd.c | 455 ++ H316/h316_lp.c | 338 ++ H316/h316_mt.c | 586 +++ H316/h316_stddev.c | 865 ++++ H316/h316_sys.c | 393 ++ HP2100/hp2100_baci.c | 1585 +++++++ HP2100/hp2100_cpu.c | 2781 ++++++++++++ HP2100/hp2100_cpu.h | 232 + HP2100/hp2100_cpu0.c | 137 + HP2100/hp2100_cpu1.c | 870 ++++ HP2100/hp2100_cpu1.h | 282 ++ HP2100/hp2100_cpu2.c | 1125 +++++ HP2100/hp2100_cpu3.c | 820 ++++ HP2100/hp2100_cpu4.c | 1129 +++++ HP2100/hp2100_cpu5.c | 1426 ++++++ HP2100/hp2100_cpu6.c | 811 ++++ HP2100/hp2100_cpu7.c | 945 ++++ HP2100/hp2100_defs.h | 343 ++ HP2100/hp2100_diag.txt | 3371 ++++++++++++++ HP2100/hp2100_dp.c | 974 +++++ HP2100/hp2100_dq.c | 849 ++++ HP2100/hp2100_dr.c | 646 +++ HP2100/hp2100_ds.c | 1599 +++++++ HP2100/hp2100_fp.c | 408 ++ HP2100/hp2100_fp.h | 47 + HP2100/hp2100_fp1.c | 1431 ++++++ HP2100/hp2100_fp1.h | 53 + HP2100/hp2100_ipl.c | 651 +++ HP2100/hp2100_lps.c | 528 +++ HP2100/hp2100_lpt.c | 299 ++ HP2100/hp2100_ms.c | 1126 +++++ HP2100/hp2100_mt.c | 547 +++ HP2100/hp2100_mux.c | 1089 +++++ HP2100/hp2100_stddev.c | 1015 +++++ HP2100/hp2100_sys.c | 720 +++ I1401/i1401_cd.c | 387 ++ I1401/i1401_cpu.c | 1808 ++++++++ I1401/i1401_dat.h | 145 + I1401/i1401_defs.h | 293 ++ I1401/i1401_dp.c | 594 +++ I1401/i1401_iq.c | 184 + I1401/i1401_lp.c | 245 ++ I1401/i1401_mt.c | 408 ++ I1401/i1401_sys.c | 412 ++ I1620/i1620_cd.c | 441 ++ I1620/i1620_cpu.c | 2059 +++++++++ I1620/i1620_defs.h | 226 + I1620/i1620_dp.c | 495 +++ I1620/i1620_fp.c | 419 ++ I1620/i1620_lp.c | 347 ++ I1620/i1620_pt.c | 489 +++ I1620/i1620_sys.c | 527 +++ I1620/i1620_tty.c | 375 ++ I7094/i7094_binloader.c | 214 + I7094/i7094_bug_history.txt | 72 + I7094/i7094_cd.c | 499 +++ I7094/i7094_clk.c | 124 + I7094/i7094_com.c | 1149 +++++ I7094/i7094_cpu.c | 2253 ++++++++++ I7094/i7094_cpu1.c | 837 ++++ I7094/i7094_dat.h | 152 + I7094/i7094_defs.h | 474 ++ I7094/i7094_drm.c | 286 ++ I7094/i7094_dsk.c | 1172 +++++ I7094/i7094_io.c | 1789 ++++++++ I7094/i7094_lp.c | 368 ++ I7094/i7094_mt.c | 826 ++++ I7094/i7094_sys.c | 727 +++ Ibm1130/1130consoleblank.bmp | Bin 0 -> 381318 bytes Ibm1130/1132empty.bmp | Bin 0 -> 6278 bytes Ibm1130/1132full.bmp | Bin 0 -> 6278 bytes Ibm1130/1442empty.bmp | Bin 0 -> 6278 bytes Ibm1130/1442eof.bmp | Bin 0 -> 6278 bytes Ibm1130/1442full.bmp | Bin 0 -> 6278 bytes Ibm1130/1442middle.bmp | Bin 0 -> 6278 bytes Ibm1130/dmsr2v12phases.h | 171 + Ibm1130/dmsr2v12slet.h | 129 + Ibm1130/hand.cur | Bin 0 -> 326 bytes Ibm1130/ibm1130.mak | 435 ++ Ibm1130/ibm1130.rc | 72 + Ibm1130/ibm1130_conin.h | 43 + Ibm1130/ibm1130_conout.h | 58 + Ibm1130/ibm1130_cpu.c | 1883 ++++++++ Ibm1130/ibm1130_cr.c | 2636 +++++++++++ Ibm1130/ibm1130_defs.h | 311 ++ Ibm1130/ibm1130_disk.c | 889 ++++ Ibm1130/ibm1130_fmt.c | 313 ++ Ibm1130/ibm1130_fmt.h | 17 + Ibm1130/ibm1130_gdu.c | 1128 +++++ Ibm1130/ibm1130_gui.c | 1657 +++++++ Ibm1130/ibm1130_plot.c | 634 +++ Ibm1130/ibm1130_prt.c | 819 ++++ Ibm1130/ibm1130_prtwheel.h | 126 + Ibm1130/ibm1130_ptrp.c | 309 ++ Ibm1130/ibm1130_sca.c | 1165 +++++ Ibm1130/ibm1130_stddev.c | 1319 ++++++ Ibm1130/ibm1130_sys.c | 505 +++ Ibm1130/ibm1130_t2741.c | 388 ++ Ibm1130/ibm1130res.h | 17 + Ibm1130/makefile | 74 + Ibm1130/readme1130.txt | 189 + Ibm1130/readme_update.txt | 60 + Ibm1130/utils/asm1130.c | 4585 +++++++++++++++++++ Ibm1130/utils/asm1130.mak | 175 + Ibm1130/utils/bindump.c | 755 ++++ Ibm1130/utils/bindump.mak | 175 + Ibm1130/utils/checkdisk.c | 262 ++ Ibm1130/utils/checkdisk.mak | 177 + Ibm1130/utils/diskview.c | 612 +++ Ibm1130/utils/diskview.mak | 175 + Ibm1130/utils/makefile | 47 + Ibm1130/utils/mkboot.c | 706 +++ Ibm1130/utils/mkboot.mak | 175 + Ibm1130/utils/viewdeck.c | 243 ++ Ibm1130/utils/viewdeck.mak | 176 + Interdata/id16_cpu.c | 1957 +++++++++ Interdata/id16_dboot.c | 357 ++ Interdata/id16_sys.c | 604 +++ Interdata/id32_cpu.c | 2331 ++++++++++ Interdata/id32_dboot.c | 321 ++ Interdata/id32_sys.c | 731 ++++ Interdata/id_defs.h | 481 ++ Interdata/id_diag.txt | 911 ++++ Interdata/id_dp.c | 600 +++ Interdata/id_fd.c | 508 +++ Interdata/id_fp.c | 532 +++ Interdata/id_idc.c | 784 ++++ Interdata/id_io.c | 607 +++ Interdata/id_lp.c | 317 ++ Interdata/id_mt.c | 523 +++ Interdata/id_pas.c | 590 +++ Interdata/id_pt.c | 360 ++ Interdata/id_tt.c | 281 ++ Interdata/id_ttp.c | 279 ++ Interdata/id_uvc.c | 373 ++ LGP/lgp_cpu.c | 733 ++++ LGP/lgp_defs.h | 135 + LGP/lgp_stddev.c | 659 +++ LGP/lgp_sys.c | 372 ++ Makefile | 834 ++++ Makefile.org | 413 ++ Makefile.posted | 302 ++ Makefile.tmp | 403 ++ NOVA/eclipse.txt | 24 + NOVA/eclipse_cpu.c | 6788 +++++++++++++++++++++++++++++ NOVA/eclipse_cpu.o | Bin 0 -> 170636 bytes NOVA/eclipse_tt.c | 428 ++ NOVA/eclipse_tt.o | Bin 0 -> 17120 bytes NOVA/nova_clk.c | 182 + NOVA/nova_cpu.c | 1472 +++++++ NOVA/nova_defs.h | 323 ++ NOVA/nova_dkp.c | 1084 +++++ NOVA/nova_dsk.c | 333 ++ NOVA/nova_lp.c | 155 + NOVA/nova_mta.c | 628 +++ NOVA/nova_plt.c | 161 + NOVA/nova_pt.c | 297 ++ NOVA/nova_qty.c | 1119 +++++ NOVA/nova_sys.c | 1142 +++++ NOVA/nova_tt.c | 262 ++ NOVA/nova_tt1.c | 348 ++ PDP1/pdp1_clk.c | 125 + PDP1/pdp1_cpu.c | 1558 +++++++ PDP1/pdp1_dcs.c | 431 ++ PDP1/pdp1_defs.h | 194 + PDP1/pdp1_diag.txt | 53 + PDP1/pdp1_drm.c | 362 ++ PDP1/pdp1_dt.c | 1085 +++++ PDP1/pdp1_lp.c | 211 + PDP1/pdp1_stddev.c | 614 +++ PDP1/pdp1_sys.c | 629 +++ PDP10/pdp10_bug_history.txt | 25 + PDP10/pdp10_cpu.c | 2332 ++++++++++ PDP10/pdp10_defs.h | 771 ++++ PDP10/pdp10_fe.c | 172 + PDP10/pdp10_ksio.c | 853 ++++ PDP10/pdp10_lp20.c | 663 +++ PDP10/pdp10_mdfp.c | 765 ++++ PDP10/pdp10_pag.c | 844 ++++ PDP10/pdp10_rp.c | 1262 ++++++ PDP10/pdp10_sys.c | 859 ++++ PDP10/pdp10_tim.c | 305 ++ PDP10/pdp10_tu.c | 1233 ++++++ PDP10/pdp10_xtnd.c | 715 +++ PDP11/pdp11_cis.c | 1592 +++++++ PDP11/pdp11_cpu.c | 2993 +++++++++++++ PDP11/pdp11_cpumod.c | 1217 ++++++ PDP11/pdp11_cpumod.h | 301 ++ PDP11/pdp11_cr.c | 1284 ++++++ PDP11/pdp11_cr_dat.h | 622 +++ PDP11/pdp11_dc.c | 651 +++ PDP11/pdp11_defs.h | 847 ++++ PDP11/pdp11_dl.c | 606 +++ PDP11/pdp11_dz.c | 727 +++ PDP11/pdp11_fp.c | 1187 +++++ PDP11/pdp11_hk.c | 1295 ++++++ PDP11/pdp11_io.c | 722 +++ PDP11/pdp11_ke.c | 348 ++ PDP11/pdp11_kg.c | 477 ++ PDP11/pdp11_lp.c | 192 + PDP11/pdp11_mscp.h | 514 +++ PDP11/pdp11_pclk.c | 313 ++ PDP11/pdp11_pt.c | 354 ++ PDP11/pdp11_rc.c | 583 +++ PDP11/pdp11_rf.c | 495 +++ PDP11/pdp11_rh.c | 801 ++++ PDP11/pdp11_rk.c | 737 ++++ PDP11/pdp11_rl.c | 692 +++ PDP11/pdp11_rp.c | 1076 +++++ PDP11/pdp11_rq.c | 2548 +++++++++++ PDP11/pdp11_rx.c | 518 +++ PDP11/pdp11_ry.c | 684 +++ PDP11/pdp11_stddev.c | 483 ++ PDP11/pdp11_sys.c | 1126 +++++ PDP11/pdp11_ta.c | 596 +++ PDP11/pdp11_tc.c | 1300 ++++++ PDP11/pdp11_tm.c | 711 +++ PDP11/pdp11_tq.c | 2174 +++++++++ PDP11/pdp11_ts.c | 1126 +++++ PDP11/pdp11_tu.c | 1009 +++++ PDP11/pdp11_uqssp.h | 169 + PDP11/pdp11_vh.c | 1365 ++++++ PDP11/pdp11_xq.c | 2051 +++++++++ PDP11/pdp11_xq.h | 233 + PDP11/pdp11_xq_bootrom.h | 312 ++ PDP11/pdp11_xu.c | 1560 +++++++ PDP11/pdp11_xu.h | 306 ++ PDP11/txt2cbn.c | 49 + PDP18B/pdp18b_cpu.c | 2227 ++++++++++ PDP18B/pdp18b_defs.h | 493 +++ PDP18B/pdp18b_diag.txt | 110 + PDP18B/pdp18b_drm.c | 248 ++ PDP18B/pdp18b_dt.c | 1484 +++++++ PDP18B/pdp18b_fpp.c | 855 ++++ PDP18B/pdp18b_lp.c | 851 ++++ PDP18B/pdp18b_mt.c | 515 +++ PDP18B/pdp18b_rb.c | 291 ++ PDP18B/pdp18b_rf.c | 355 ++ PDP18B/pdp18b_rp.c | 493 +++ PDP18B/pdp18b_stddev.c | 1123 +++++ PDP18B/pdp18b_sys.c | 1210 +++++ PDP18B/pdp18b_tt1.c | 463 ++ PDP8/pdp8_clk.c | 183 + PDP8/pdp8_cpu.c | 1486 +++++++ PDP8/pdp8_ct.c | 711 +++ PDP8/pdp8_defs.h | 207 + PDP8/pdp8_df.c | 373 ++ PDP8/pdp8_dt.c | 1292 ++++++ PDP8/pdp8_fpp.c | 1387 ++++++ PDP8/pdp8_lp.c | 183 + PDP8/pdp8_mt.c | 644 +++ PDP8/pdp8_pt.c | 288 ++ PDP8/pdp8_rf.c | 437 ++ PDP8/pdp8_rk.c | 449 ++ PDP8/pdp8_rl.c | 679 +++ PDP8/pdp8_rx.c | 730 ++++ PDP8/pdp8_sys.c | 979 +++++ PDP8/pdp8_td.c | 913 ++++ PDP8/pdp8_tsc.c | 158 + PDP8/pdp8_tt.c | 271 ++ PDP8/pdp8_ttx.c | 424 ++ S3/haltguide.txt | 950 ++++ S3/readme_s3.txt | 78 + S3/s3_cd.c | 447 ++ S3/s3_cpu.c | 1834 ++++++++ S3/s3_defs.h | 94 + S3/s3_disk.c | 792 ++++ S3/s3_lp.c | 363 ++ S3/s3_pkb.c | 315 ++ S3/s3_sys.c | 948 ++++ S3/system3.txt | 472 ++ SDS/sds_cpu.c | 1502 +++++++ SDS/sds_defs.h | 409 ++ SDS/sds_diag.txt | 113 + SDS/sds_drm.c | 293 ++ SDS/sds_dsk.c | 376 ++ SDS/sds_io.c | 940 ++++ SDS/sds_lp.c | 309 ++ SDS/sds_mt.c | 480 ++ SDS/sds_mux.c | 549 +++ SDS/sds_rad.c | 307 ++ SDS/sds_stddev.c | 602 +++ SDS/sds_sys.c | 618 +++ VAX/ka655_patch.com | 509 +++ VAX/ka655x.bin | Bin 0 -> 131072 bytes VAX/ka655x.bin_old | Bin 0 -> 131072 bytes VAX/vax780_bug_history.txt | 66 + VAX/vax780_defs.h | 453 ++ VAX/vax780_fload.c | 250 ++ VAX/vax780_mba.c | 765 ++++ VAX/vax780_mem.c | 276 ++ VAX/vax780_sbi.c | 753 ++++ VAX/vax780_stddev.c | 941 ++++ VAX/vax780_syslist.c | 138 + VAX/vax780_uba.c | 1322 ++++++ VAX/vax_cis.c | 1661 +++++++ VAX/vax_cmode.c | 1109 +++++ VAX/vax_cpu.c | 3281 ++++++++++++++ VAX/vax_cpu1.c | 1569 +++++++ VAX/vax_defs.h | 727 +++ VAX/vax_fpa.c | 1463 +++++++ VAX/vax_io.c | 1162 +++++ VAX/vax_mmu.c | 583 +++ VAX/vax_octa.c | 1188 +++++ VAX/vax_stddev.c | 407 ++ VAX/vax_sys.c | 1415 ++++++ VAX/vax_syscm.c | 664 +++ VAX/vax_sysdev.c | 1570 +++++++ VAX/vax_syslist.c | 135 + VAX/vaxmod_defs.h | 476 ++ VAX/vmb.exe | Bin 0 -> 44544 bytes VAX780/pdp11_cr.o | Bin 0 -> 51252 bytes VAX780/pdp11_dz.o | Bin 0 -> 26448 bytes VAX780/pdp11_hk.o | Bin 0 -> 30240 bytes VAX780/pdp11_lp.o | Bin 0 -> 11300 bytes VAX780/pdp11_rl.o | Bin 0 -> 18688 bytes VAX780/pdp11_rp.o | Bin 0 -> 27604 bytes VAX780/pdp11_rq.o | Bin 0 -> 73596 bytes VAX780/pdp11_ry.o | Bin 0 -> 18576 bytes VAX780/pdp11_tq.o | Bin 0 -> 60484 bytes VAX780/pdp11_ts.o | Bin 0 -> 29372 bytes VAX780/pdp11_tu.o | Bin 0 -> 26508 bytes VAX780/pdp11_xu.o | Bin 0 -> 53396 bytes VAX780/vax780_fload.o | Bin 0 -> 9256 bytes VAX780/vax780_mba.o | Bin 0 -> 24544 bytes VAX780/vax780_mem.o | Bin 0 -> 12180 bytes VAX780/vax780_sbi.o | Bin 0 -> 24092 bytes VAX780/vax780_stddev.o | Bin 0 -> 29460 bytes VAX780/vax780_syslist.o | Bin 0 -> 9152 bytes VAX780/vax780_uba.o | Bin 0 -> 41556 bytes VAX780/vax_cis.o | Bin 0 -> 36728 bytes VAX780/vax_cmode.o | Bin 0 -> 31316 bytes VAX780/vax_cpu.o | Bin 0 -> 124048 bytes VAX780/vax_cpu1.o | Bin 0 -> 47036 bytes VAX780/vax_fpa.o | Bin 0 -> 26492 bytes VAX780/vax_mmu.o | Bin 0 -> 22224 bytes VAX780/vax_octa.o | Bin 0 -> 38092 bytes VAX780/vax_sys.o | Bin 0 -> 39876 bytes VAX780/vax_syscm.o | Bin 0 -> 20300 bytes build_mingw.bat | 14 + build_mingw_ether.bat | 15 + common_int64_adr64/scp.o | Bin 0 -> 127380 bytes common_int64_adr64/sim_console.o | Bin 0 -> 26156 bytes common_int64_adr64/sim_ether.o | Bin 0 -> 22024 bytes common_int64_adr64/sim_fio.o | Bin 0 -> 7868 bytes common_int64_adr64/sim_readline.o | Bin 0 -> 4672 bytes common_int64_adr64/sim_sock.o | Bin 0 -> 9792 bytes common_int64_adr64/sim_tape.o | Bin 0 -> 22980 bytes common_int64_adr64/sim_timer.o | Bin 0 -> 19064 bytes common_int64_adr64/sim_tmxr.o | Bin 0 -> 21776 bytes descrip.mms | 1202 +++++ readline-hack-08091901.zip | Bin 0 -> 43136 bytes readline-hack-08092001.zip | Bin 0 -> 49791 bytes readline-hack-08100101.zip | Bin 0 -> 45864 bytes scp.c | 4427 +++++++++++++++++++ scp.diff | 197 + scp.h | 122 + settings | 209 + sim_console.c | 1094 +++++ sim_console.h | 81 + sim_defs.h | 538 +++ sim_ether.c | 1470 +++++++ sim_ether.h | 212 + sim_fio.c | 321 ++ sim_fio.h | 46 + sim_readline.c | 128 + sim_readline.h | 20 + sim_rev.h | 2191 ++++++++++ sim_sock.c | 314 ++ sim_sock.h | 88 + sim_tape.c | 937 ++++ sim_tape.h | 129 + sim_timer.c | 650 +++ sim_timer.h | 69 + sim_tmxr.c | 690 +++ sim_tmxr.h | 103 + 429 files changed, 288160 insertions(+) create mode 100644 0readme_38.txt create mode 100644 0readme_ethernet.txt create mode 100644 ALTAIR/altair.txt create mode 100644 ALTAIR/altair_cpu.c create mode 100644 ALTAIR/altair_defs.h create mode 100644 ALTAIR/altair_dsk.c create mode 100644 ALTAIR/altair_sio.c create mode 100644 ALTAIR/altair_sys.c create mode 100644 AltairZ80/altairz80_cpu.c create mode 100644 AltairZ80/altairz80_cpu_nommu.c create mode 100644 AltairZ80/altairz80_defs.h create mode 100644 AltairZ80/altairz80_dsk.c create mode 100644 AltairZ80/altairz80_hdsk.c create mode 100644 AltairZ80/altairz80_net.c create mode 100644 AltairZ80/altairz80_sio.c create mode 100644 AltairZ80/altairz80_sys.c create mode 100644 AltairZ80/disasm.c create mode 100644 AltairZ80/flashwriter2.c create mode 100644 AltairZ80/i8272.c create mode 100644 AltairZ80/i8272.h create mode 100644 AltairZ80/i86.h create mode 100644 AltairZ80/i86_decode.c create mode 100644 AltairZ80/i86_ops.c create mode 100644 AltairZ80/i86_prim_ops.c create mode 100644 AltairZ80/insns.h create mode 100644 AltairZ80/insnsa.c create mode 100644 AltairZ80/insnsd.c create mode 100644 AltairZ80/mfdc.c create mode 100644 AltairZ80/mfdc.h create mode 100644 AltairZ80/n8vem.c create mode 100644 AltairZ80/nasm.h create mode 100644 AltairZ80/s100_64fdc.c create mode 100644 AltairZ80/s100_disk1a.c create mode 100644 AltairZ80/s100_disk2.c create mode 100644 AltairZ80/s100_fif.c create mode 100644 AltairZ80/s100_mdriveh.c create mode 100644 AltairZ80/s100_mdsad.c create mode 100644 AltairZ80/s100_scp300f.c create mode 100644 AltairZ80/s100_selchan.c create mode 100644 AltairZ80/s100_ss1.c create mode 100644 AltairZ80/sim_imd.c create mode 100644 AltairZ80/sim_imd.h create mode 100644 AltairZ80/vfdhd.c create mode 100644 AltairZ80/vfdhd.h create mode 100644 AltairZ80/wd179x.c create mode 100644 AltairZ80/wd179x.h create mode 100644 GRI/gri_cpu.c create mode 100644 GRI/gri_defs.h create mode 100644 GRI/gri_stddev.c create mode 100644 GRI/gri_sys.c create mode 100644 H316/h316_cpu.c create mode 100644 H316/h316_defs.h create mode 100644 H316/h316_dp.c create mode 100644 H316/h316_fhd.c create mode 100644 H316/h316_lp.c create mode 100644 H316/h316_mt.c create mode 100644 H316/h316_stddev.c create mode 100644 H316/h316_sys.c create mode 100644 HP2100/hp2100_baci.c create mode 100644 HP2100/hp2100_cpu.c create mode 100644 HP2100/hp2100_cpu.h create mode 100644 HP2100/hp2100_cpu0.c create mode 100644 HP2100/hp2100_cpu1.c create mode 100644 HP2100/hp2100_cpu1.h create mode 100644 HP2100/hp2100_cpu2.c create mode 100644 HP2100/hp2100_cpu3.c create mode 100644 HP2100/hp2100_cpu4.c create mode 100644 HP2100/hp2100_cpu5.c create mode 100644 HP2100/hp2100_cpu6.c create mode 100644 HP2100/hp2100_cpu7.c create mode 100644 HP2100/hp2100_defs.h create mode 100644 HP2100/hp2100_diag.txt create mode 100644 HP2100/hp2100_dp.c create mode 100644 HP2100/hp2100_dq.c create mode 100644 HP2100/hp2100_dr.c create mode 100644 HP2100/hp2100_ds.c create mode 100644 HP2100/hp2100_fp.c create mode 100644 HP2100/hp2100_fp.h create mode 100644 HP2100/hp2100_fp1.c create mode 100644 HP2100/hp2100_fp1.h create mode 100644 HP2100/hp2100_ipl.c create mode 100644 HP2100/hp2100_lps.c create mode 100644 HP2100/hp2100_lpt.c create mode 100644 HP2100/hp2100_ms.c create mode 100644 HP2100/hp2100_mt.c create mode 100644 HP2100/hp2100_mux.c create mode 100644 HP2100/hp2100_stddev.c create mode 100644 HP2100/hp2100_sys.c create mode 100644 I1401/i1401_cd.c create mode 100644 I1401/i1401_cpu.c create mode 100644 I1401/i1401_dat.h create mode 100644 I1401/i1401_defs.h create mode 100644 I1401/i1401_dp.c create mode 100644 I1401/i1401_iq.c create mode 100644 I1401/i1401_lp.c create mode 100644 I1401/i1401_mt.c create mode 100644 I1401/i1401_sys.c create mode 100644 I1620/i1620_cd.c create mode 100644 I1620/i1620_cpu.c create mode 100644 I1620/i1620_defs.h create mode 100644 I1620/i1620_dp.c create mode 100644 I1620/i1620_fp.c create mode 100644 I1620/i1620_lp.c create mode 100644 I1620/i1620_pt.c create mode 100644 I1620/i1620_sys.c create mode 100644 I1620/i1620_tty.c create mode 100644 I7094/i7094_binloader.c create mode 100644 I7094/i7094_bug_history.txt create mode 100644 I7094/i7094_cd.c create mode 100644 I7094/i7094_clk.c create mode 100644 I7094/i7094_com.c create mode 100644 I7094/i7094_cpu.c create mode 100644 I7094/i7094_cpu1.c create mode 100644 I7094/i7094_dat.h create mode 100644 I7094/i7094_defs.h create mode 100644 I7094/i7094_drm.c create mode 100644 I7094/i7094_dsk.c create mode 100644 I7094/i7094_io.c create mode 100644 I7094/i7094_lp.c create mode 100644 I7094/i7094_mt.c create mode 100644 I7094/i7094_sys.c create mode 100644 Ibm1130/1130consoleblank.bmp create mode 100644 Ibm1130/1132empty.bmp create mode 100644 Ibm1130/1132full.bmp create mode 100644 Ibm1130/1442empty.bmp create mode 100644 Ibm1130/1442eof.bmp create mode 100644 Ibm1130/1442full.bmp create mode 100644 Ibm1130/1442middle.bmp create mode 100644 Ibm1130/dmsr2v12phases.h create mode 100644 Ibm1130/dmsr2v12slet.h create mode 100644 Ibm1130/hand.cur create mode 100644 Ibm1130/ibm1130.mak create mode 100644 Ibm1130/ibm1130.rc create mode 100644 Ibm1130/ibm1130_conin.h create mode 100644 Ibm1130/ibm1130_conout.h create mode 100644 Ibm1130/ibm1130_cpu.c create mode 100644 Ibm1130/ibm1130_cr.c create mode 100644 Ibm1130/ibm1130_defs.h create mode 100644 Ibm1130/ibm1130_disk.c create mode 100644 Ibm1130/ibm1130_fmt.c create mode 100644 Ibm1130/ibm1130_fmt.h create mode 100644 Ibm1130/ibm1130_gdu.c create mode 100644 Ibm1130/ibm1130_gui.c create mode 100644 Ibm1130/ibm1130_plot.c create mode 100644 Ibm1130/ibm1130_prt.c create mode 100644 Ibm1130/ibm1130_prtwheel.h create mode 100644 Ibm1130/ibm1130_ptrp.c create mode 100644 Ibm1130/ibm1130_sca.c create mode 100644 Ibm1130/ibm1130_stddev.c create mode 100644 Ibm1130/ibm1130_sys.c create mode 100644 Ibm1130/ibm1130_t2741.c create mode 100644 Ibm1130/ibm1130res.h create mode 100644 Ibm1130/makefile create mode 100644 Ibm1130/readme1130.txt create mode 100644 Ibm1130/readme_update.txt create mode 100644 Ibm1130/utils/asm1130.c create mode 100644 Ibm1130/utils/asm1130.mak create mode 100644 Ibm1130/utils/bindump.c create mode 100644 Ibm1130/utils/bindump.mak create mode 100644 Ibm1130/utils/checkdisk.c create mode 100644 Ibm1130/utils/checkdisk.mak create mode 100644 Ibm1130/utils/diskview.c create mode 100644 Ibm1130/utils/diskview.mak create mode 100644 Ibm1130/utils/makefile create mode 100644 Ibm1130/utils/mkboot.c create mode 100644 Ibm1130/utils/mkboot.mak create mode 100644 Ibm1130/utils/viewdeck.c create mode 100644 Ibm1130/utils/viewdeck.mak create mode 100644 Interdata/id16_cpu.c create mode 100644 Interdata/id16_dboot.c create mode 100644 Interdata/id16_sys.c create mode 100644 Interdata/id32_cpu.c create mode 100644 Interdata/id32_dboot.c create mode 100644 Interdata/id32_sys.c create mode 100644 Interdata/id_defs.h create mode 100644 Interdata/id_diag.txt create mode 100644 Interdata/id_dp.c create mode 100644 Interdata/id_fd.c create mode 100644 Interdata/id_fp.c create mode 100644 Interdata/id_idc.c create mode 100644 Interdata/id_io.c create mode 100644 Interdata/id_lp.c create mode 100644 Interdata/id_mt.c create mode 100644 Interdata/id_pas.c create mode 100644 Interdata/id_pt.c create mode 100644 Interdata/id_tt.c create mode 100644 Interdata/id_ttp.c create mode 100644 Interdata/id_uvc.c create mode 100644 LGP/lgp_cpu.c create mode 100644 LGP/lgp_defs.h create mode 100644 LGP/lgp_stddev.c create mode 100644 LGP/lgp_sys.c create mode 100644 Makefile create mode 100644 Makefile.org create mode 100644 Makefile.posted create mode 100644 Makefile.tmp create mode 100644 NOVA/eclipse.txt create mode 100644 NOVA/eclipse_cpu.c create mode 100644 NOVA/eclipse_cpu.o create mode 100644 NOVA/eclipse_tt.c create mode 100644 NOVA/eclipse_tt.o create mode 100644 NOVA/nova_clk.c create mode 100644 NOVA/nova_cpu.c create mode 100644 NOVA/nova_defs.h create mode 100644 NOVA/nova_dkp.c create mode 100644 NOVA/nova_dsk.c create mode 100644 NOVA/nova_lp.c create mode 100644 NOVA/nova_mta.c create mode 100644 NOVA/nova_plt.c create mode 100644 NOVA/nova_pt.c create mode 100644 NOVA/nova_qty.c create mode 100644 NOVA/nova_sys.c create mode 100644 NOVA/nova_tt.c create mode 100644 NOVA/nova_tt1.c create mode 100644 PDP1/pdp1_clk.c create mode 100644 PDP1/pdp1_cpu.c create mode 100644 PDP1/pdp1_dcs.c create mode 100644 PDP1/pdp1_defs.h create mode 100644 PDP1/pdp1_diag.txt create mode 100644 PDP1/pdp1_drm.c create mode 100644 PDP1/pdp1_dt.c create mode 100644 PDP1/pdp1_lp.c create mode 100644 PDP1/pdp1_stddev.c create mode 100644 PDP1/pdp1_sys.c create mode 100644 PDP10/pdp10_bug_history.txt create mode 100644 PDP10/pdp10_cpu.c create mode 100644 PDP10/pdp10_defs.h create mode 100644 PDP10/pdp10_fe.c create mode 100644 PDP10/pdp10_ksio.c create mode 100644 PDP10/pdp10_lp20.c create mode 100644 PDP10/pdp10_mdfp.c create mode 100644 PDP10/pdp10_pag.c create mode 100644 PDP10/pdp10_rp.c create mode 100644 PDP10/pdp10_sys.c create mode 100644 PDP10/pdp10_tim.c create mode 100644 PDP10/pdp10_tu.c create mode 100644 PDP10/pdp10_xtnd.c create mode 100644 PDP11/pdp11_cis.c create mode 100644 PDP11/pdp11_cpu.c create mode 100644 PDP11/pdp11_cpumod.c create mode 100644 PDP11/pdp11_cpumod.h create mode 100644 PDP11/pdp11_cr.c create mode 100644 PDP11/pdp11_cr_dat.h create mode 100644 PDP11/pdp11_dc.c create mode 100644 PDP11/pdp11_defs.h create mode 100644 PDP11/pdp11_dl.c create mode 100644 PDP11/pdp11_dz.c create mode 100644 PDP11/pdp11_fp.c create mode 100644 PDP11/pdp11_hk.c create mode 100644 PDP11/pdp11_io.c create mode 100644 PDP11/pdp11_ke.c create mode 100644 PDP11/pdp11_kg.c create mode 100644 PDP11/pdp11_lp.c create mode 100644 PDP11/pdp11_mscp.h create mode 100644 PDP11/pdp11_pclk.c create mode 100644 PDP11/pdp11_pt.c create mode 100644 PDP11/pdp11_rc.c create mode 100644 PDP11/pdp11_rf.c create mode 100644 PDP11/pdp11_rh.c create mode 100644 PDP11/pdp11_rk.c create mode 100644 PDP11/pdp11_rl.c create mode 100644 PDP11/pdp11_rp.c create mode 100644 PDP11/pdp11_rq.c create mode 100644 PDP11/pdp11_rx.c create mode 100644 PDP11/pdp11_ry.c create mode 100644 PDP11/pdp11_stddev.c create mode 100644 PDP11/pdp11_sys.c create mode 100644 PDP11/pdp11_ta.c create mode 100644 PDP11/pdp11_tc.c create mode 100644 PDP11/pdp11_tm.c create mode 100644 PDP11/pdp11_tq.c create mode 100644 PDP11/pdp11_ts.c create mode 100644 PDP11/pdp11_tu.c create mode 100644 PDP11/pdp11_uqssp.h create mode 100644 PDP11/pdp11_vh.c create mode 100644 PDP11/pdp11_xq.c create mode 100644 PDP11/pdp11_xq.h create mode 100644 PDP11/pdp11_xq_bootrom.h create mode 100644 PDP11/pdp11_xu.c create mode 100644 PDP11/pdp11_xu.h create mode 100644 PDP11/txt2cbn.c create mode 100644 PDP18B/pdp18b_cpu.c create mode 100644 PDP18B/pdp18b_defs.h create mode 100644 PDP18B/pdp18b_diag.txt create mode 100644 PDP18B/pdp18b_drm.c create mode 100644 PDP18B/pdp18b_dt.c create mode 100644 PDP18B/pdp18b_fpp.c create mode 100644 PDP18B/pdp18b_lp.c create mode 100644 PDP18B/pdp18b_mt.c create mode 100644 PDP18B/pdp18b_rb.c create mode 100644 PDP18B/pdp18b_rf.c create mode 100644 PDP18B/pdp18b_rp.c create mode 100644 PDP18B/pdp18b_stddev.c create mode 100644 PDP18B/pdp18b_sys.c create mode 100644 PDP18B/pdp18b_tt1.c create mode 100644 PDP8/pdp8_clk.c create mode 100644 PDP8/pdp8_cpu.c create mode 100644 PDP8/pdp8_ct.c create mode 100644 PDP8/pdp8_defs.h create mode 100644 PDP8/pdp8_df.c create mode 100644 PDP8/pdp8_dt.c create mode 100644 PDP8/pdp8_fpp.c create mode 100644 PDP8/pdp8_lp.c create mode 100644 PDP8/pdp8_mt.c create mode 100644 PDP8/pdp8_pt.c create mode 100644 PDP8/pdp8_rf.c create mode 100644 PDP8/pdp8_rk.c create mode 100644 PDP8/pdp8_rl.c create mode 100644 PDP8/pdp8_rx.c create mode 100644 PDP8/pdp8_sys.c create mode 100644 PDP8/pdp8_td.c create mode 100644 PDP8/pdp8_tsc.c create mode 100644 PDP8/pdp8_tt.c create mode 100644 PDP8/pdp8_ttx.c create mode 100644 S3/haltguide.txt create mode 100644 S3/readme_s3.txt create mode 100644 S3/s3_cd.c create mode 100644 S3/s3_cpu.c create mode 100644 S3/s3_defs.h create mode 100644 S3/s3_disk.c create mode 100644 S3/s3_lp.c create mode 100644 S3/s3_pkb.c create mode 100644 S3/s3_sys.c create mode 100644 S3/system3.txt create mode 100644 SDS/sds_cpu.c create mode 100644 SDS/sds_defs.h create mode 100644 SDS/sds_diag.txt create mode 100644 SDS/sds_drm.c create mode 100644 SDS/sds_dsk.c create mode 100644 SDS/sds_io.c create mode 100644 SDS/sds_lp.c create mode 100644 SDS/sds_mt.c create mode 100644 SDS/sds_mux.c create mode 100644 SDS/sds_rad.c create mode 100644 SDS/sds_stddev.c create mode 100644 SDS/sds_sys.c create mode 100644 VAX/ka655_patch.com create mode 100644 VAX/ka655x.bin create mode 100644 VAX/ka655x.bin_old create mode 100644 VAX/vax780_bug_history.txt create mode 100644 VAX/vax780_defs.h create mode 100644 VAX/vax780_fload.c create mode 100644 VAX/vax780_mba.c create mode 100644 VAX/vax780_mem.c create mode 100644 VAX/vax780_sbi.c create mode 100644 VAX/vax780_stddev.c create mode 100644 VAX/vax780_syslist.c create mode 100644 VAX/vax780_uba.c create mode 100644 VAX/vax_cis.c create mode 100644 VAX/vax_cmode.c create mode 100644 VAX/vax_cpu.c create mode 100644 VAX/vax_cpu1.c create mode 100644 VAX/vax_defs.h create mode 100644 VAX/vax_fpa.c create mode 100644 VAX/vax_io.c create mode 100644 VAX/vax_mmu.c create mode 100644 VAX/vax_octa.c create mode 100644 VAX/vax_stddev.c create mode 100644 VAX/vax_sys.c create mode 100644 VAX/vax_syscm.c create mode 100644 VAX/vax_sysdev.c create mode 100644 VAX/vax_syslist.c create mode 100644 VAX/vaxmod_defs.h create mode 100644 VAX/vmb.exe create mode 100644 VAX780/pdp11_cr.o create mode 100644 VAX780/pdp11_dz.o create mode 100644 VAX780/pdp11_hk.o create mode 100644 VAX780/pdp11_lp.o create mode 100644 VAX780/pdp11_rl.o create mode 100644 VAX780/pdp11_rp.o create mode 100644 VAX780/pdp11_rq.o create mode 100644 VAX780/pdp11_ry.o create mode 100644 VAX780/pdp11_tq.o create mode 100644 VAX780/pdp11_ts.o create mode 100644 VAX780/pdp11_tu.o create mode 100644 VAX780/pdp11_xu.o create mode 100644 VAX780/vax780_fload.o create mode 100644 VAX780/vax780_mba.o create mode 100644 VAX780/vax780_mem.o create mode 100644 VAX780/vax780_sbi.o create mode 100644 VAX780/vax780_stddev.o create mode 100644 VAX780/vax780_syslist.o create mode 100644 VAX780/vax780_uba.o create mode 100644 VAX780/vax_cis.o create mode 100644 VAX780/vax_cmode.o create mode 100644 VAX780/vax_cpu.o create mode 100644 VAX780/vax_cpu1.o create mode 100644 VAX780/vax_fpa.o create mode 100644 VAX780/vax_mmu.o create mode 100644 VAX780/vax_octa.o create mode 100644 VAX780/vax_sys.o create mode 100644 VAX780/vax_syscm.o create mode 100644 build_mingw.bat create mode 100644 build_mingw_ether.bat create mode 100644 common_int64_adr64/scp.o create mode 100644 common_int64_adr64/sim_console.o create mode 100644 common_int64_adr64/sim_ether.o create mode 100644 common_int64_adr64/sim_fio.o create mode 100644 common_int64_adr64/sim_readline.o create mode 100644 common_int64_adr64/sim_sock.o create mode 100644 common_int64_adr64/sim_tape.o create mode 100644 common_int64_adr64/sim_timer.o create mode 100644 common_int64_adr64/sim_tmxr.o create mode 100644 descrip.mms create mode 100644 readline-hack-08091901.zip create mode 100644 readline-hack-08092001.zip create mode 100644 readline-hack-08100101.zip create mode 100644 scp.c create mode 100644 scp.diff create mode 100644 scp.h create mode 100644 settings create mode 100644 sim_console.c create mode 100644 sim_console.h create mode 100644 sim_defs.h create mode 100644 sim_ether.c create mode 100644 sim_ether.h create mode 100644 sim_fio.c create mode 100644 sim_fio.h create mode 100644 sim_readline.c create mode 100644 sim_readline.h create mode 100644 sim_rev.h create mode 100644 sim_sock.c create mode 100644 sim_sock.h create mode 100644 sim_tape.c create mode 100644 sim_tape.h create mode 100644 sim_timer.c create mode 100644 sim_timer.h create mode 100644 sim_tmxr.c create mode 100644 sim_tmxr.h diff --git a/0readme_38.txt b/0readme_38.txt new file mode 100644 index 0000000..3ba9383 --- /dev/null +++ b/0readme_38.txt @@ -0,0 +1,45 @@ +Notes For V3.8 + + +The makefile now works for Linux and most Unix's. Howevr, for Solaris +and MacOS, you must first export the OSTYPE environment variable: + +> export OSTYPE +> make + +Otherwise, you will get build errors. + + +1. New Features + +1.1 3.8-0 + +1.1.1 SCP and Libraries + +- BREAK, NOBREAK, and SHOW BREAK with no argument will set, clear, and + show (respectively) a breakpoint at the current PC. + +1.2 GRI + +- Added support for the GRI-99 processor. + +1.3 HP2100 + +- Added support for the BACI terminal interface. +- Added support for RTE OS/VMA/EMA, SIGNAL, VIS firmware extensions. + +1.4 Nova + +- Added support for 64KW memory (implemented in third-party CPU's). + +1.5 PDP-11 + +- Added support for DC11, RC11, KE11A, KG11A. +- Added modem control support for DL11. +- Added ASCII character support for all 8b devices. + + +2. Bugs Fixed + +Please see the revision history on http://simh.trailing-edge.com or +in the source module sim_rev.h. diff --git a/0readme_ethernet.txt b/0readme_ethernet.txt new file mode 100644 index 0000000..4cc74fb --- /dev/null +++ b/0readme_ethernet.txt @@ -0,0 +1,315 @@ +This file contains information about the SIMH Ethernet package. + +------------------------------------------------------------------------------- + +The XQ emulator is a host-independant software emulation of Digital's +DELQA (M7516) and DEQNA (M7504) Q-bus ethernet cards for the SIMH emulator. + +The XU emulator is a host-independant software emulation of Digital's DEUNA +(M7792/M7793) and DELUA (M7521) Unibus ethernet cards for the SIMH emulator. + +The XQ and XU simulators use the Sim_Ether module to execute host-specific +packet reads and writes, since all operating systems talk to real ethernet +cards/controllers differently. See the comments at the top of sim_ether.c +for the list of currently supported host platforms. + +The Sim_Ether module sets the selected ethernet card into +promiscuous mode to gather all packets, then filters out the packets that it +doesn't want. In Windows, packets having the same source MAC address as the +controller are ignored for WinPCAP compatibility (see Windows notes below). + +If your ethernet card is plugged into a switch, the promiscuous mode setting +should not cause much of a problem, since the switch will still filter out +most of the undesirable traffic. You will only see "excessive" traffic if you +are on a direct or hub(repeater) segment. + +Using the libpcap/WinPcap interface, the simulated computer cannot "talk" to +the host computer via the selected interface, since the packets are not +reflected back to the host. The workaround for this is to use a second NIC in +the host and connect them both into the same network; then the host and the +simulator can communicate over the physical LAN. + +Universal TUN/TAP support provides another solution for the above dual-NIC +problem for systems that support Universal TUN/TAP. Since the TUN/TAP interface +is at a different network level, the host can create a TAP device for the +simulator and then bridge or route packets between the TAP device and the real +network interface. Note that the TAP device and any bridging or routing must be +established before running the simulator; SIMH does not create, bridge, or +route TAP devices for you. + +------------------------------------------------------------------------------- + +Windows notes: + 1. The Windows-specific code uses the WinPCAP 3.0 package from + http://www.winpcap.org. This package for windows simulates the libpcap + package that is freely available for un*x systems. + + 2. You must *install* the WinPCAP runtime package. + + 3. The first time the WinPCAP driver is used, it will be dynamically loaded, + and the user must be an Administrator on the machine to do so. If you need + to run as an unprivileged user, you must set the service to autostart. See + the WinPCAP documentation for details on the static loading workaround. + + 4. If you want to use TAP devices, they must be created before running SIMH. + (TAP component from the OpenVPN project; http://openvpn.sourceforge.net) + + 5. Compaq PATHWORKS 32 v7.2 also enabled bridging for the ethernet adapters + when the DECNET and LAT drivers were installed; TAP was not needed. + + +Building on Windows: + 1. Install WinPCAP 3.0 runtime and the WinPCAP Developer's kit. + + 2. Put the required .h files (bittypes,devioctl,ip6_misc,packet32,pcap, + pcap-stdinc).h from the WinPCAP 3.0 developer's kit in the compiler's path + + 3. Put the required .lib files (packet,wpcap).lib from the WinPCAP 3.0 + developer's kit in the linker's path + + 4. If you're using Borland C++, use COFF2OMF to convert the .lib files into + a format that can be used by the compiler. + + 5. Define USE_NETWORK. + + 6. Build it! + +------------------------------------------------------------------------------- + +Linux, {Free|Net|Open}BSD, OS/X, and Un*x notes: + +----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- + +Sim_Ether has been reworked to be more universal; because of this, you will +need to get a version of libpcap that is 0.9 or greater. This can be +downloaded from www.tcpdump.org - see the comments at the top of Sim_ether.c +for details. + +At the time of this release, the "Current Version" available at: +http://www.tcpdump.org/daily/libpcap-current.tar.gz is the +latest checked-in source code that is actually higher than the released +0.8.3 version number. Specifically, for all platforms, it contains code that +opens the ethernet device in Read/Write mode instead of the Read-Only mode +that previous libpcap versions for platforms which use one of pcap-bpf.c, +pcap-pf.c, or pcap-snit.c. This capabiligy now exists to support a newly +provided generic packet sending capability. + +----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- WARNING ----- + + 1. For all platforms, you must run SIMH(scp) with sufficient privilege to + allow the ethernet card can be set into promiscuous mode and to write + packets through the driver. For most Unix/Unix-like platforms this will + mean running as root. For systems which use bpf devices (NetBSD, + OpenBSD, FreeBSD and OS/X) it is possible to set permissions on the bpf + devices to allow read and write access to users other than root (For + example: chmod 666 /dev/bpf*). Doing this, has its own security issues. + Additional alternative methods for avoiding the 'run as root' requirement + will be welcomed. + + 2. If you want to use TAP devices, they must be created before running SIMH. + +Building on Linux, {Free|Net|Open}BSD, OS/X, Un*x: + + 1. Get/make/install the libpcap package for your operating system. Sources: + All : http://www.tcpdump.org/ + Older versions of libpcap can be found, for various systems, at: + Linux : search for your variant on http://rpmfind.net + OS/X : Apple Developer's site? + + NOTE: These repositories will not likely contain a version + of libpcap greater than 0.8.1 for several years since + other packages in these repositories don't depend on a + later version than they currently have. + + 2. Use 'make USE_NETWORK=1' + + 3. Build it! + +------------------------------------------------------------------------------- + +OpenVMS Alpha notes: + 1. Ethernet support will only work on Alpha VMS 7.3-1 or later, which is + when required VCI promiscuous mode support was added. Hobbyists can + get the required version of VMS from the OpenVMS Alpha Hobbyist Kit 3.0. + + Running a simulator built with ethernet support on a version of VMS prior + to 7.3-1 will behave as if there is no ethernet support built in due to + the inability of the software to set the PCAPVCM into promiscuous mode. + + An example display of fully functional ethernet support: + sim> SHOW XQ ETH + ETH devices: + 0 we0 (VMS Device: _EWA0:) + 1 we1 (VMS Device: _EWB0:) + + An example display when the simulator was built without ethernet support + or is not running the required version of VMS: + sim> SHOW XQ ETH + ETH devices: + no network devices are available + + 2. You must place the PCAPVCM.EXE execlet in SYS$LOADABLE_IMAGES before + running a simulator with ethernet support. Note: This is done by the + build commands in descrip.mms. + + 3. You must have CMKRNL privilege to SHOW or ATTACH an ethernet device; + alternatively, you can INSTALL the simulator with CMKRNL privilege. + + 4. If you use a second adapter to communicate to the host, SOME protocol + that creates an I/O structure (SCS, DECNET, TCP) must be running on the + adapter prior trying to connect with SIMH, or the host may crash. + The execlet is not written to create an I/O structure for the device. + +Building on OpenVMS Alpha: + The current descrip.mms file will build simulators capable of using + ethernet support with them automatically. These currently are: VAX, + PDP11, and PDP10. The descrip.mms driven builds will also build the + pcap library and build and install the VCI execlet. + + 1. Fetch the VMS-PCAP zip file from: + http://simh.trailing-edge.com/sources/vms-pcap.zip + 2. Unzip it into the base of the simh distribution directory. + 3. Build the simulator(s) with MMS or MMK: + $ MMx {VAX,PDP11,PDP10, etc...} + +------------------------------------------------------------------------------- + +VAX simulator support: + +An OpenVMS VAX v7.2 system with DECNET Phase IV, MultiNet 4.4a, and LAT 5.3 has +been successfully run. Other testers have reported success booting NetBSD and +OpenVMS VAX 5.5-2 also. + + +PDP11 simulator support: + +An RT-11 v5.3 system with a freeware TCP/IP stack has been successfully run. +Other testers have reported that RSX with DECNET and the NetBSD operating +systems also work. RSTS/E v10.1 has preliminary support - RSTS/E boots and +enables the XH (XQ) device - DECNET and LAT software have not been tested. + +The XU module has been tested by a third party for basic packet functionality +under a modified RSX11M environment. I am unable to test it in-house until +someone can arrange to send me a disk image containing a stock RSTS/E or +RSX11M+ system image that also contains DECNET, LAT, and/or TCP/IP software. + +------------------------------------------------------------------------------- + +How to debug problems with the ethernet subsystems: + +PLEASE read the host-specific notes in sim_ether.c! + +While running SCP, the following commands can be used to enable debug messages: + + scp> SET DEBUG STDERR + scp> SET XQ DEBUG={ETH|TRC|REG|WRN|CSR|VAR|SAN|SET|PCK} + scp> SET XU DEBUG={ETH|TRC|REG|WRN} + +Documentation of the functionality of these debug modifiers can be found in +pdp11_xq.h and pdp11_xu.h. Inline debugging has replaced the previous #ifdef +style of debugging, which required recompilation before debugging. + +------------------------------------------------------------------------------- + +Things planned for future releases: + 1. PDP-11 bootstrap/bootrom + 2. Full MOP implementation + 3. DESQA support (if someone can get me the user manuals) + 4. DETQA support [DELQA-Turbo] (I have the manual) + +------------------------------------------------------------------------------- + +Things which I need help with: + 1. Information about Remote MOP processing + 2. VAX/PDP-11 hardware diagnotics image files and docs, to test XQ thoroughly. + 3. Feedback on operation with other VAX/PDP-11 OS's. + +------------------------------------------------------------------------------- + +Please send all patches, questions, feedback, clarifications, and help to: + david DOT hittner AT ngc DOT com + +Thanks, and Enjoy!! +Dave + + +=============================================================================== + Change Log +=============================================================================== + +19-Mar-04 Release: + 1. Genericized Sim_Ether code, reduced #ifdefs (David Hittner) + 2. Further refinement of sim_ether, qualified more platforms (Mark Pizzolato) + 3. Added XU module (David Hittner) + 4. Corrected XQ interrupt signalling for PDP11s (David Hittner) + 5. Added inline debugging support (David Hittner) + +------------------------------------------------------------------------------- + +26-Nov-03 Release: + 1. Added VMS support to Sim_Ether; created pcap-vms port (Anders Ahgren) + 2. Added DECNET duplicate detection for Windows (Mark Pizzolato) + 3. Added BPF filtering to increase efficiency (Mark Pizzolato) + 4. Corrected XQ Runt processing (Mark Pizzolato) + 5. Corrected XQ Sofware Reset (Mark Pizzolato) + 6. Corrected XQ Multicast/Promiscuous mode setting/resetting (Mark Pizzolato) + 7. Added Universal TUN/TAP support (Mark Pizzolato) + 8. Added FreeBSD support (Edward Brocklesby) + 9. Corrected interrupts on XQB device (David Hittner) + +------------------------------------------------------------------------------- + +05-Jun-03 Release: + 1. Added SET/SHOW XQ STATS (David Hittner) + 2. Added SHOW XQ FILTERS (David Hittner) + 3. Added ability to split rcv packets into multiple buffers (David Hittner) + 4. Added explicit runt & giant packet processing (David Hittner) + +------------------------------------------------------------------------------- + +30-May-03 Release: + 1. Corrected bug in xq_setmac introduced in v3.0 (multiple people) + 2. Made XQ rcv buffer allocation dynamic to reduce scp size (David Hittner) + 3. Optimized some structs, removed legacy variables (Mark Pizzolato) + 4. Changed #ifdef WIN32 to _WIN32 for consistancy (Mark Pizzolato) + +------------------------------------------------------------------------------- + +06-May-03 Release: + 1. Added second XQ controller (David Hittner) + 2. Added SIMH v3.0 compatibility (David Hittner) + 3. Removed SET ADDRESS functionality (David Hittner) + +------------------------------------------------------------------------------- + +10-Apr-03 Release: + 1. Added preliminary support for RSTS/E (David Hittner) + 2. Added PDP-11 bootrom load via CSR flags (David Hittner) + 3. Support for SPARC linux (Mark Pizzolato) + +------------------------------------------------------------------------------- + +11-Mar-03 Release: + 1. Added support for RT-11 TCP/IP + 2. Corrected interrupts (thanks to Tom Evans and Bob Supnik) + 3. Moved change log to the bottom of the readme file, cleaned up document + +------------------------------------------------------------------------------- + +16-Jan-03 Release: + 1. Added VMScluster support (thanks to Mark Pizzolato) + 2. Verified VAX remote boot functionality (>>>B XQA0) + 3. Added major performance enhancements (thanks to Mark Pizzolato again) + 4. Changed _DEBUG tracers to XQ_DEBUG and ETH_DEBUG + 5. Added local packet processing + 6. Added system id broadcast + +------------------------------------------------------------------------------- + +08-Nov-02 Release: + 1. Added USE_NETWORK conditional to Sim_Ether + 2. Fixed behaviour of SHOW XQ ETH if no devices exist + 3. Added OpenBSD support to Sim_Ether (courtesy of Federico Schwindt) + 4. Added ethX detection simplification (from Megan Gentry) + +=============================================================================== diff --git a/ALTAIR/altair.txt b/ALTAIR/altair.txt new file mode 100644 index 0000000..13f0c36 --- /dev/null +++ b/ALTAIR/altair.txt @@ -0,0 +1,211 @@ +Altair 8800 Simulator +===================== + +1. Background. + + The MITS (Micro Instrumentation and Telemetry Systems) Altair 8800 +was announced on the January 1975 cover of Popular Electronics, which +boasted you could buy and build this powerful computer kit for only $397. +The kit consisted at that time of only the parts to build a case, power +supply, card cage (18 slots), CPU card, and memory card with 256 *bytes* of +memory. Still, thousands were ordered within the first few months after the +announcement, starting the personal computer revolution as we know it today. + + Many laugh at the small size of the that first kit, noting there +were no peripherals and the 256 byte memory size. But the computer was an +open system, and by 1977 MITS and many other small startups had added many +expansion cards to make the Altair quite a respectable little computer. The +"Altair Bus" that made this possible was soon called the S-100 Bus, later +adopted as an industry standard, and eventually became the IEE-696 Bus. + +2. Hardware + + We are simulating a fairly "loaded" Altair 8800 from about 1977, +with the following configuration: + + device simulates + name(s) + + CPU Altair 8800 with Intel 8080 CPU board, 62KB + of RAM, 2K of EPROM with start boot ROM. + 2SIO MITS 88-2SIO Dual Serial Interface Board. Port 1 + is assumed to be connected to a serial "glass + TTY" that is your terminal running the Simulator. + PTR Paper Tape Reader attached to port 2 of the + 2SIO board. + PTP Paper Tape Punch attached to port 2 of the + 2SIO board. This also doubles as a printer + port. + DSK MITS 88-DISK Floppy Disk controller with up + to eight drives. + +2.1 CPU + + We have 2 CPU options that were not present on the original +machine but are useful in the simulator. We also allow you to select +memory sizes, but be aware that some sample software requires the full +64K (i.e. CP/M) and the MITS Disk Basic and Altair DOS require about +a minimum of 24K. + + SET CPU 8080 Simulates the 8080 CPU (normal) + SET CPU Z80 Simulates the later Z80 CPU [At the present time + this is not fully implemented and is not to be + trusted with real Z80 software] + SET CPU ITRAP Causes the simulator to halt if an invalid 8080 + Opcode is detected. + SET CPU NOITRAP Does not stop on an invalid Opcode. This is + how the real 8080 works. + SET CPU 4K + SET CPU 8K + SET CPU 12K + SET CPU 16K + ...... + SET CPU 64K All these set various CPU memory configurations. + The 2K EPROM at the high end of memory is always + present and will always boot. + +The BOOT EPROM card starts at address 177400. Jumping to this address +will always boot drive 0 of the floppy controller. If no valid bootable +software is present there the machine crashes. This is historically +accurate behavior. + +The real 8080, on receiving a HLT (Halt) instruction, freezes the processor +and only an interrupt or CPU hardware reset will restore it. The simulator +is alot nicer, it will halt but send you back to the simulator command line. + +CPU Registers include the following: + + name size comments + + PC 16 The Program Counter + A 8 The accumulator + BC 16 The BC register pair. Register B is the high + 8 bits, C is the lower 8 bits + DE 16 The DE register pair. D is the top 8 bits, E is + the bottom. + HL 16 The HL register pair. H is top, L is bottom. + C 1 Carry flag. + Z 1 Zero Flag. + AC 1 Auxillary Carry flag. + P 1 Parity flag. + S 1 Sign flag. + SR 16 The front panel switches. + BREAK 16 Breakpoint address (377777 to disable). + WRU 8 The interrupt character. This starts as 005 + (ctrl-E) but some Altair software uses this + keystroke so best to change this to something + exotic such as 035 (which is Ctl-]). + + +2.2 The Serial I/O Card (2SIO) + + This simple programmed I/O device provides 2 serial ports to the +outside world, which could be hardware jumpered to support RS-232 plugs or a +TTY current loop interface. The standard I/O addresses assigned by MITS +was 20-21 (octal) for the first port, and 22-23 (octal) for the second. +We follow this standard in the Simulator. + + The simulator directs I/O to/from the first port to the screen. The +second port reads from an attachable "tape reader" file on input, and writes +to an attachable "punch file" on output. These files are considered a +simple stream of 8-bit bytes. + +2.3 The 88-DISK controller. + + The MITS 88-DISK is a simple programmed I/O interface to the MITS +8-inch floppy drive, which was basically a Pertec FD-400 with a power +supply and buffer board builtin. The controller supports neither interrupts +nor DMA, so floppy access required the sustained attention of the CPU. +The standard I/O addresses were 10, 11, and 12 (octal), and we follow the +standard. Details on controlling this hardware are in the altair_dsk.c +source file. + + +3. Sample Software + + Running an Altair in 1977 you would be running either MITS Disk +Extended BASIC, or the brand new and sexy CP/M Operating System from Digital +Research. Or possibly, you ordered Altair DOS back when it was promised in +1975, and are still waiting for it to be delivered in early 1977. + + We have samples of all three for you to check out. We can't go into +the details of how they work, but we'll give you a few hints. + + +3.1 CP/M Version 2.2 + + This version is my own port of the standard CP/M to the Altair. +There were some "official" versions but I don't have them. None were +endorsed or sold by MITS to my knowledge, however. + To boot CP/M: + + sim> attach dsk0 altcpm.dsk + sim> go 177400 + 62K CP/M VERSION 2.2 (ALTAIR 8800) + A>DIR + + CP/M feels like DOS, sort of. DIR will work. I have included all +the standard CP/M utilities, plus a few common public-domain ones. I also +include the sources to the customized BIOS and some other small programs. +TYPE will print an ASCII file. DUMP will dump a binary one. LS is a better +DIR than DIR. ASM will assemble .ASM files to Hex, LOAD will "load" them to +binary format (.COM). ED is a simple editor, #A command will bring the +source file to the buffer, T command will "type" lines, L will move lines, +E exits the editor. 20L20T will move down 20 lines, and type 20. Very +DECish. DDT is the debugger, SUBMIT is a batch-type command processor. +A sample batch file that will assemble and write out the bootable CP/M +image (on drive A) is "SYSGEN.SUB". To run it, type "SUBMIT SYSGEN". + + +3.2 MITS Disk Extended BASIC Version 4.1 + + This was the commonly used software for serious users of the Altair +computer. It is a powerful (but slow) BASIC with some extended commands to +allow it to access and manage the disk. There was no operating system it +ran under. To boot: + + sim> attach dsk0 mbasic.dsk + sim> go 177400 + + MEMORY SIZE? [return] + LINEPRINTER? C [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + NUMBER OF FILES? 3 [return] + NUMBER OF RANDOM FILES? 2 [return] + + 44297 BYTES FREE + ALTAIR BASIC REV. 4.1 + [DISK EXTENDED VERSION] + COPYRIGHT 1977 BY MITS INC. + OK + mount 0 + OK + files + + +3.3 Altair DOS Version 1.0 + + This was long promised but not delivered until it was almost +irrelevant. A short attempted tour will reveal it to be a dog, far inferior +to CP/M. To boot: + + sim> attach dsk0 altdos.dsk + sim> go 177400 + + MEMORY SIZE? 64 [return] + INTERRUPTS? N [return] + HIGHEST DISK NUMBER? 0 [return] (3 here = 4 drive system) + HOW MANY DISK FILES? 3 [return] + HOW MANY RANDOM FILES? 2 [return] + + 056769 BYTES AVAILABLE + DOS MONITOR VER 1.0 + COPYRIGHT 1977 BY MITS INC + .mnt 0 + + .dir 0 + + + + + diff --git a/ALTAIR/altair_cpu.c b/ALTAIR/altair_cpu.c new file mode 100644 index 0000000..740d66f --- /dev/null +++ b/ALTAIR/altair_cpu.c @@ -0,0 +1,1189 @@ +/* altair_cpu.c: MITS Altair Intel 8080 CPU simulator + + Copyright (c) 1997-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + cpu 8080 CPU + + 08-Oct-02 RMS Tied off spurious compiler warnings + + The register state for the 8080 CPU is: + + A<0:7> Accumulator + BC<0:15> BC Register Pair + DE<0:15> DE Register Pair + HL<0:15> HL Register Pair + C carry flag + Z zero flag + S Sign bit + AC Aux carry + P Parity bit + PC<0:15> program counter + SP<0:15> Stack Pointer + + The 8080 is an 8-bit CPU, which uses 16-bit registers to address + up to 64KB of memory. + + The 78 basic instructions come in 1, 2, and 3-byte flavors. + + This routine is the instruction decode routine for the 8080. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + I/O error in I/O simulator + Invalid OP code (if ITRAP is set on CPU) + + 2. Interrupts. + There are 8 possible levels of interrupt, and in effect they + do a hardware CALL instruction to one of 8 possible low + memory addresses. + + 3. Non-existent memory. On the 8080, reads to non-existent memory + return 0377, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + altair_cpu.c add I/O service routines to dev_table + altair_sys.c add pointer to data structures in sim_devices +*/ + + +#include + +#include "altair_defs.h" + +#define UNIT_V_OPSTOP (UNIT_V_UF) /* Stop on Invalid OP? */ +#define UNIT_OPSTOP (1 << UNIT_V_OPSTOP) +#define UNIT_V_CHIP (UNIT_V_UF+1) /* 8080 or Z80 */ +#define UNIT_CHIP (1 << UNIT_V_CHIP) +#define UNIT_V_MSIZE (UNIT_V_UF+2) /* Memory Size */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +unsigned char M[MAXMEMSIZE]; /* memory */ +int32 A = 0; /* accumulator */ +int32 BC = 0; /* BC register pair */ +int32 DE = 0; /* DE register pair */ +int32 HL = 0; /* HL register pair */ +int32 SP = 0; /* Stack pointer */ +int32 C = 0; /* carry flag */ +int32 Z = 0; /* Zero flag */ +int32 AC = 0; /* Aux carry */ +int32 S = 0; /* sign flag */ +int32 P = 0; /* parity flag */ +int32 saved_PC = 0; /* program counter */ +int32 SR = 0; /* switch register */ +int32 INTE = 0; /* Interrupt Enable */ +int32 int_req = 0; /* Interrupt request */ +int32 chip = 0; /* 0 = 8080 chip, 1 = z80 chip */ + +int32 PCX; /* External view of PC */ + +extern int32 sim_int_char; +extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +/* function prototypes */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +void setarith(int32 reg); +void setlogical(int32 reg); +void setinc(int32 reg); +int32 getreg(int32 reg); +void putreg(int32 reg, int32 val); +int32 getpair(int32 reg); +int32 getpush(int32 reg); +void putpush(int32 reg, int32 data); +void putpair(int32 reg, int32 val); +void parity(int32 reg); +int32 cond(int32 con); + +extern int32 sio0s(int32 io, int32 data); +extern int32 sio0d(int32 io, int32 data); +extern int32 sio1s(int32 io, int32 data); +extern int32 sio1d(int32 io, int32 data); +extern int32 dsk10(int32 io, int32 data); +extern int32 dsk11(int32 io, int32 data); +extern int32 dsk12(int32 io, int32 data); +int32 nulldev(int32 io, int32 data); + +/* This is the I/O configuration table. There are 255 possible +device addresses, if a device is plugged to a port it's routine +address is here, 'nulldev' means no device is available +*/ +struct idev { + int32 (*routine)(); +}; +struct idev dev_table[256] = { +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 000 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 004 */ +{&dsk10}, {&dsk11}, {&dsk12}, {&nulldev}, /* 010 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 014 */ +{&sio0s}, {&sio0d}, {&sio1s}, {&sio1d}, /* 020 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 024 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 030 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 034 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 040 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 044 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 050 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 054 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 060 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 064 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 070 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 074 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 100 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 104 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 110 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 114 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 120 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 124 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 130 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 134 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 140 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 144 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 150 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 154 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 160 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 164 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 170 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 174 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 200 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 204 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 210 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 214 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 220 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 224 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 230 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 234 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 240 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 244 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 250 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 254 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 260 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 264 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 270 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 274 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 300 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 304 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 310 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 314 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 320 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 324 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 330 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 334 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 340 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 344 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 350 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 354 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 360 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 364 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 370 */ +{&nulldev}, {&nulldev}, {&nulldev}, {&nulldev} /* 374 */ +}; + +/* Altair MITS standard BOOT EPROM, fits in upper 256K of memory */ + +int32 bootrom[256] = { + 0041, 0000, 0114, 0021, 0030, 0377, 0016, 0346, + 0032, 0167, 0023, 0043, 0015, 0302, 0010, 0377, + 0303, 0000, 0114, 0000, 0000, 0000, 0000, 0000, + 0363, 0061, 0142, 0115, 0257, 0323, 0010, 0076, /* 46000 */ + 0004, 0323, 0011, 0303, 0031, 0114, 0333, 0010, /* 46010 */ + 0346, 0002, 0302, 0016, 0114, 0076, 0002, 0323, /* 46020 */ + 0011, 0333, 0010, 0346, 0100, 0302, 0016, 0114, + 0021, 0000, 0000, 0006, 0000, 0333, 0010, 0346, + 0004, 0302, 0045, 0114, 0076, 0020, 0365, 0325, + 0305, 0325, 0021, 0206, 0200, 0041, 0324, 0114, + 0333, 0011, 0037, 0332, 0070, 0114, 0346, 0037, + 0270, 0302, 0070, 0114, 0333, 0010, 0267, 0372, + 0104, 0114, 0333, 0012, 0167, 0043, 0035, 0312, + 0132, 0114, 0035, 0333, 0012, 0167, 0043, 0302, + 0104, 0114, 0341, 0021, 0327, 0114, 0001, 0200, + 0000, 0032, 0167, 0276, 0302, 0301, 0114, 0200, + 0107, 0023, 0043, 0015, 0302, 0141, 0114, 0032, + 0376, 0377, 0302, 0170, 0114, 0023, 0032, 0270, + 0301, 0353, 0302, 0265, 0114, 0361, 0361, 0052, + 0325, 0114, 0325, 0021, 0000, 0377, 0315, 0316, + 0114, 0321, 0332, 0276, 0114, 0315, 0316, 0114, + 0322, 0256, 0114, 0004, 0004, 0170, 0376, 0040, + 0332, 0054, 0114, 0006, 0001, 0312, 0054, 0114, + 0333, 0010, 0346, 0002, 0302, 0240, 0114, 0076, + 0001, 0323, 0011, 0303, 0043, 0114, 0076, 0200, + 0323, 0010, 0303, 0000, 0000, 0321, 0361, 0075, + 0302, 0056, 0114, 0076, 0103, 0001, 0076, 0117, + 0001, 0076, 0115, 0107, 0076, 0200, 0323, 0010, + 0170, 0323, 0001, 0303, 0311, 0114, 0172, 0274, + 0300, 0173, 0275, 0311, 0204, 0000, 0114, 0044, + 0026, 0126, 0026, 0000, 0000, 0000, 0000, 0000 +}; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 16) }, + { ORDATA (A, A, 8) }, + { ORDATA (BC, BC, 16) }, + { ORDATA (DE, DE, 16) }, + { ORDATA (HL, HL, 16) }, + { ORDATA (SP, SP, 16) }, + { FLDATA (C, C, 16) }, + { FLDATA (Z, Z, 16) }, + { FLDATA (AC, AC, 16) }, + { FLDATA (S, S, 16) }, + { FLDATA (P, P, 16) }, + { FLDATA (INTE, INTE, 16) }, + { ORDATA (SR, SR, 16) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } +}; + +MTAB cpu_mod[] = { + { UNIT_CHIP, UNIT_CHIP, "Z80", "Z80", NULL }, + { UNIT_CHIP, 0, "8080", "8080", NULL }, + { UNIT_OPSTOP, UNIT_OPSTOP, "ITRAP", "ITRAP", NULL }, + { UNIT_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65535, NULL, "64K", &cpu_set_size }, + { 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 16, 1, 8, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL +}; + +int32 sim_instr (void) +{ + extern int32 sim_interval; + int32 PC, IR, OP, DAR, reason, hi, lo, carry, i; + + PC = saved_PC & ADDRMASK; /* load local PC */ + C = C & 0200000; + reason = 0; + + /* Main instruction fetch/decode loop */ + + while (reason == 0) { /* loop until halted */ + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + + if (int_req > 0) { /* interrupt? */ + + /* 8080 interrupts not implemented yet. None were used, + on a standard Altair 8800. All I/O is programmed. */ + + } /* end interrupt */ + + if (sim_brk_summ && + sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + if (PC == 0177400) { /* BOOT PROM address */ + for (i = 0; i < 250; i++) { + M[i + 0177400] = bootrom[i] & 0xFF; + } + } + + PCX = PC; + + IR = OP = M[PC]; /* fetch instruction */ + + PC = (PC + 1) & ADDRMASK; /* increment PC */ + + sim_interval--; + + if (OP == 0166) { /* HLT Instruction*/ + reason = STOP_HALT; + PC--; + continue; + } + + /* Handle below all operations which refer to registers or + register pairs. After that, a large switch statement + takes care of all other opcodes */ + + if ((OP & 0xC0) == 0x40) { /* MOV */ + DAR = getreg(OP & 0x07); + putreg((OP >> 3) & 0x07, DAR); + continue; + } + if ((OP & 0xC7) == 0x06) { /* MVI */ + putreg((OP >> 3) & 0x07, M[PC]); + PC++; + continue; + } + if ((OP & 0xCF) == 0x01) { /* LXI */ + DAR = M[PC] & 0x00ff; + PC++; + DAR = DAR | (M[PC] <<8) & 0xFF00;; + putpair((OP >> 4) & 0x03, DAR); + PC++; + continue; + } + if ((OP & 0xEF) == 0x0A) { /* LDAX */ + DAR = getpair((OP >> 4) & 0x03); + putreg(7, M[DAR]); + continue; + } + if ((OP & 0xEF) == 0x02) { /* STAX */ + DAR = getpair((OP >> 4) & 0x03); + M[DAR] = getreg(7); + continue; + } + + if ((OP & 0xF8) == 0xB8) { /* CMP */ + DAR = A & 0xFF; + DAR -= getreg(OP & 0x07); + setarith(DAR); + continue; + } + if ((OP & 0xC7) == 0xC2) { /* JMP */ + if (cond((OP >> 3) & 0x07) == 1) { + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + PC = (hi << 8) + lo; + } else { + PC += 2; + } + continue; + } + if ((OP & 0xC7) == 0xC4) { /* CALL */ + if (cond((OP >> 3) & 0x07) == 1) { + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + SP--; + M[SP] = (PC >> 8) & 0xff; + SP--; + M[SP] = PC & 0xff; + PC = (hi << 8) + lo; + } else { + PC += 2; + } + continue; + } + if ((OP & 0xC7) == 0xC0) { /* RET */ + if (cond((OP >> 3) & 0x07) == 1) { + PC = M[SP]; + SP++; + PC |= (M[SP] << 8) & 0xff00; + SP++; + } + continue; + } + if ((OP & 0xC7) == 0xC7) { /* RST */ + SP--; + M[SP] = (PC >> 8) & 0xff; + SP--; + M[SP] = PC & 0xff; + PC = OP & 0x38; + continue; + } + + if ((OP & 0xCF) == 0xC5) { /* PUSH */ + DAR = getpush((OP >> 4) & 0x03); + SP--; + M[SP] = (DAR >> 8) & 0xff; + SP--; + M[SP] = DAR & 0xff; + continue; + } + if ((OP & 0xCF) == 0xC1) { /*POP */ + DAR = M[SP]; + SP++; + DAR |= M[SP] << 8; + SP++; + putpush((OP >> 4) & 0x03, DAR); + continue; + } + if ((OP & 0xF8) == 0x80) { /* ADD */ + A += getreg(OP & 0x07); + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xF8) == 0x88) { /* ADC */ + carry = 0; + if (C) carry = 1; + A += getreg(OP & 0x07); + A += carry; + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xF8) == 0x90) { /* SUB */ + A -= getreg(OP & 0x07); + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xF8) == 0x98) { /* SBB */ + carry = 0; + if (C) carry = 1; + A -= (getreg(OP & 0x07)) + carry ; + setarith(A); + A = A & 0xFF; + continue; + } + if ((OP & 0xC7) == 0x04) { /* INR */ + DAR = getreg((OP >> 3) & 0x07); + DAR++; + setinc(DAR); + DAR = DAR & 0xFF; + putreg((OP >> 3) & 0x07, DAR); + continue; + } + if ((OP & 0xC7) == 0x05) { /* DCR */ + DAR = getreg((OP >> 3) & 0x07); + DAR--; + setinc(DAR); + DAR = DAR & 0xFF; + putreg((OP >> 3) & 0x07, DAR); + continue; + } + if ((OP & 0xCF) == 0x03) { /* INX */ + DAR = getpair((OP >> 4) & 0x03); + DAR++; + DAR = DAR & 0xFFFF; + putpair((OP >> 4) & 0x03, DAR); + continue; + } + if ((OP & 0xCF) == 0x0B) { /* DCX */ + DAR = getpair((OP >> 4) & 0x03); + DAR--; + DAR = DAR & 0xFFFF; + putpair((OP >> 4) & 0x03, DAR); + continue; + } + if ((OP & 0xCF) == 0x09) { /* DAD */ + HL += getpair((OP >> 4) & 0x03); + C = 0; + if (HL & 0x10000) + C = 0200000; + HL = HL & 0xFFFF; + continue; + } + if ((OP & 0xF8) == 0xA0) { /* ANA */ + A &= getreg(OP & 0x07); + C = 0; + setlogical(A); + A &= 0xFF; + continue; + } + if ((OP & 0xF8) == 0xA8) { /* XRA */ + A ^= getreg(OP & 0x07); + C = 0; + setlogical(A); + A &= 0xFF; + continue; + } + if ((OP & 0xF8) == 0xB0) { /* ORA */ + A |= getreg(OP & 0x07); + C = 0; + setlogical(A); + A &= 0xFF; + continue; + } + + + + /* The Big Instruction Decode Switch */ + + switch (IR) { + + /* Logical instructions */ + + case 0376: { /* CPI */ + DAR = A & 0xFF; + DAR -= M[PC]; + PC++; + setarith(DAR); + break; + } + case 0346: { /* ANI */ + A &= M[PC]; + PC++; + C = AC = 0; + setlogical(A); + A &= 0xFF; + break; + } + case 0356: { /* XRI */ + A ^= M[PC]; + PC++; + C = AC = 0; + setlogical(A); + A &= 0xFF; + break; + } + case 0366: { /* ORI */ + A |= M[PC]; + PC++; + C = AC = 0; + setlogical(A); + A &= 0xFF; + break; + } + + /* Jump instructions */ + + case 0303: { /* JMP */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + PC = (hi << 8) + lo; + break; + } + case 0351: { /* PCHL */ + PC = HL; + break; + } + case 0315: { /* CALL */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + SP--; + M[SP] = (PC >> 8) & 0xff; + SP--; + M[SP] = PC & 0xff; + PC = (hi << 8) + lo; + break; + } + case 0311: { /* RET */ + PC = M[SP]; + SP++; + PC |= (M[SP] << 8) & 0xff00; + SP++; + break; + } + + /* Data Transfer Group */ + + case 062: { /* STA */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + M[DAR] = A; + break; + } + case 072: { /* LDA */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + A = M[DAR]; + break; + } + case 042: { /* SHLD */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + M[DAR] = HL; + DAR++; + M[DAR] = (HL >>8) & 0x00ff; + break; + } + case 052: { /* LHLD */ + lo = M[PC]; + PC++; + hi = M[PC]; + PC++; + DAR = (hi << 8) + lo; + HL = M[DAR]; + DAR++; + HL = HL | (M[DAR] <<8); + break; + } + case 0353: { /* XCHG */ + DAR = HL; + HL = DE; + DE = DAR; + break; + } + + /* Arithmetic Group */ + + case 0306: { /* ADI */ + A += M[PC]; + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 0316: { /* ACI */ + carry = 0; + if (C) carry = 1; + A += M[PC]; + A += carry; + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 0326: { /* SUI */ + A -= M[PC]; + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 0336: { /* SBI */ + carry = 0; + if (C) carry = 1; + A -= (M[PC] + carry); + PC++; + setarith(A); + A = A & 0xFF; + break; + } + case 047: { /* DAA */ + DAR = A & 0x0F; + if (DAR > 9 || AC > 0) { + DAR += 6; + A &= 0xF0; + A |= DAR & 0x0F; + if (DAR & 0x10) + AC = 0200000; + else + AC = 0; + } + DAR = (A >> 4) & 0x0F; + if (DAR > 9 || AC > 0) { + DAR += 6; + if (AC) DAR++; + A &= 0x0F; + A |= (DAR << 4); + } + if ((DAR << 4) & 0x100) + C = 0200000; + else + C = 0; + if (A & 0x80) { + S = 0200000; + } else { + S = 0; + } + if ((A & 0xff) == 0) + Z = 0200000; + else + Z = 0; + parity(A); + A = A & 0xFF; + break; + } + case 07: { /* RLC */ + C = 0; + C = (A << 9) & 0200000; + A = (A << 1) & 0xFF; + if (C) + A |= 0x01; + break; + } + case 017: { /* RRC */ + C = 0; + if ((A & 0x01) == 1) + C |= 0200000; + A = (A >> 1) & 0xFF; + if (C) + A |= 0x80; + break; + } + case 027: { /* RAL */ + DAR = C; + C = 0; + C = (A << 9) & 0200000; + A = (A << 1) & 0xFF; + if (DAR) + A |= 1; + else + A &= 0xFE; + break; + } + case 037: { /* RAR */ + DAR = C; + C = 0; + if ((A & 0x01) == 1) + C |= 0200000; + A = (A >> 1) & 0xFF; + if (DAR) + A |= 0x80; + else + A &= 0x7F; + break; + } + case 057: { /* CMA */ + A = ~ A; + A &= 0xFF; + break; + } + case 077: { /* CMC */ + C = ~ C; + C &= 0200000; + break; + } + case 067: { /* STC */ + C = 0200000; + break; + } + + /* Stack, I/O & Machine Control Group */ + + case 0: { /* NOP */ + break; + } + case 0343: { /* XTHL */ + lo = M[SP]; + hi = M[SP + 1]; + M[SP] = HL & 0xFF; + M[SP + 1] = (HL >> 8) & 0xFF; + HL = (hi << 8) + lo; + break; + } + case 0371: { /* SPHL */ + SP = HL; + break; + } + case 0373: { /* EI */ + INTE = 0200000; + break; + } + case 0363: { /* DI */ + INTE = 0; + break; + } + case 0333: { /* IN */ + DAR = M[PC] & 0xFF; + PC++; + if (DAR == 0xFF) { + A = (SR >> 8) & 0xFF; + } else { + A = dev_table[DAR].routine(0, 0); + } + break; + } + case 0323: { /* OUT */ + DAR = M[PC] & 0xFF; + PC++; + dev_table[DAR].routine(1, A); + break; + } + + default: { + if (cpu_unit.flags & UNIT_OPSTOP) { + reason = STOP_OPCODE; + PC--; + } + break; + } + } +} + +/* Simulation halted */ + +saved_PC = PC; +return reason; +} + +/* Test an 8080 flag condition and return 1 if true, 0 if false */ +int32 cond(int32 con) +{ + switch (con) { + case 0: + if (Z == 0) return (1); + break; + case 1: + if (Z != 0) return (1); + break; + case 2: + if (C == 0) return (1); + break; + case 3: + if (C != 0) return (1); + break; + case 4: + if (P == 0) return (1); + break; + case 5: + if (P != 0) return (1); + break; + case 6: + if (S == 0) return (1); + break; + case 7: + if (S != 0) return (1); + break; + default: + break; + } + return (0); +} + +/* Set the arry, ign, ero and

arity flags following + an arithmetic operation on 'reg'. +*/ + +void setarith(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x100) + C = 0200000; + else + C = 0; + if (reg & 0x80) { + bc++; + S = 0200000; + } else { + S = 0; + } + if ((reg & 0xff) == 0) + Z = 0200000; + else + Z = 0; + AC = 0; + if (cpu_unit.flags & UNIT_CHIP) { + P = 0; /* parity is zero for *all* arith ops on Z80 */ + } else { + parity(reg); + } +} + +/* Set the arry, ign, ero amd

arity flags following + a logical (bitwise) operation on 'reg'. +*/ + +void setlogical(int32 reg) +{ + C = 0; + if (reg & 0x80) { + S = 0200000; + } else { + S = 0; + } + if ((reg & 0xff) == 0) + Z = 0200000; + else + Z = 0; + AC = 0; + parity(reg); +} + +/* Set the Parity (P) flag based on parity of 'reg', i.e., number + of bits on even: P=0200000, else P=0 +*/ + +void parity(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x01) bc++; + if (reg & 0x02) bc++; + if (reg & 0x04) bc++; + if (reg & 0x08) bc++; + if (reg & 0x10) bc++; + if (reg & 0x20) bc++; + if (reg & 0x40) bc++; + if (reg & 0x80) bc++; + P = ~(bc << 16); + P &= 0200000; +} + +/* Set the ign, ero amd

arity flags following + an INR/DCR operation on 'reg'. +*/ + +void setinc(int32 reg) +{ + int32 bc = 0; + + if (reg & 0x80) { + bc++; + S = 0200000; + } else { + S = 0; + } + if ((reg & 0xff) == 0) + Z = 0200000; + else + Z = 0; + if (cpu_unit.flags & UNIT_CHIP) { + P = 0; /* parity is zero for *all* arith ops on Z80 */ + } else { + parity(reg); + } +} + +/* Get an 8080 register and return it */ +int32 getreg(int32 reg) +{ + switch (reg) { + case 0: + return ((BC >>8) & 0x00ff); + case 1: + return (BC & 0x00FF); + case 2: + return ((DE >>8) & 0x00ff); + case 3: + return (DE & 0x00ff); + case 4: + return ((HL >>8) & 0x00ff); + case 5: + return (HL & 0x00ff); + case 6: + return (M[HL]); + case 7: + return (A); + default: + break; + } + return 0; +} + +/* Put a value into an 8080 register from memory */ +void putreg(int32 reg, int32 val) +{ + switch (reg) { + case 0: + BC = BC & 0x00FF; + BC = BC | (val <<8); + break; + case 1: + BC = BC & 0xFF00; + BC = BC | val; + break; + case 2: + DE = DE & 0x00FF; + DE = DE | (val <<8); + break; + case 3: + DE = DE & 0xFF00; + DE = DE | val; + break; + case 4: + HL = HL & 0x00FF; + HL = HL | (val <<8); + break; + case 5: + HL = HL & 0xFF00; + HL = HL | val; + break; + case 6: + M[HL] = val & 0xff; + break; + case 7: + A = val & 0xff; + default: + break; + } +} + +/* Return the value of a selected register pair */ +int32 getpair(int32 reg) +{ + switch (reg) { + case 0: + return (BC); + case 1: + return (DE); + case 2: + return (HL); + case 3: + return (SP); + default: + break; + } + return 0; +} + +/* Return the value of a selected register pair, in PUSH + format where 3 means A& flags, not SP */ +int32 getpush(int32 reg) +{ + int32 stat; + + switch (reg) { + case 0: + return (BC); + case 1: + return (DE); + case 2: + return (HL); + case 3: + stat = A << 8; + if (S) stat |= 0x80; + if (Z) stat |= 0x40; + if (AC) stat |= 0x10; + if (P) stat |= 0x04; + stat |= 0x02; + if (C) stat |= 0x01; + return (stat); + default: + break; + } + return 0; +} + + +/* Place data into the indicated register pair, in PUSH + format where 3 means A& flags, not SP */ +void putpush(int32 reg, int32 data) +{ + switch (reg) { + case 0: + BC = data; + break; + case 1: + DE = data; + break; + case 2: + HL = data; + break; + case 3: + A = (data >> 8) & 0xff; + S = Z = AC = P = C = 0; + if (data & 0x80) S = 0200000; + if (data & 0x40) Z = 0200000; + if (data & 0x10) AC = 0200000; + if (data & 0x04) P = 0200000; + if (data & 0x01) C = 0200000; + break; + default: + break; + } +} + + +/* Put a value into an 8080 register pair */ +void putpair(int32 reg, int32 val) +{ + switch (reg) { + case 0: + BC = val; + break; + case 1: + DE = val; + break; + case 2: + HL = val; + break; + case 3: + SP = val; + break; + default: + break; + } +} + + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +C = 0; +Z = 0; +saved_PC = 0; +int_req = 0; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & 0377; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr >= MEMSIZE) return SCPE_NXM; + M[addr] = val & 0377; + return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +int32 nulldev(int32 flag, int32 data) +{ + if (flag == 0) + return (0377); + return 0; +} + diff --git a/ALTAIR/altair_defs.h b/ALTAIR/altair_defs.h new file mode 100644 index 0000000..ca8444a --- /dev/null +++ b/ALTAIR/altair_defs.h @@ -0,0 +1,42 @@ +/* altair_defs.h: MITS Altair simulator definitions + + Copyright (c) 1997-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Memory */ + +#define MAXMEMSIZE 65536 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_OPCODE 4 + diff --git a/ALTAIR/altair_dsk.c b/ALTAIR/altair_dsk.c new file mode 100644 index 0000000..4218f1d --- /dev/null +++ b/ALTAIR/altair_dsk.c @@ -0,0 +1,368 @@ +/* altair_dsk.c: MITS Altair 88-DISK Simulator + + Copyright (c) 1997-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + The 88_DISK is a 8-inch floppy controller which can control up + to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives. + Each diskette has physically 77 tracks of 32 137-byte sectors + each. + + The controller is interfaced to the CPU by use of 3 I/O addreses, + standardly, these are device numbers 10, 11, and 12 (octal). + + Address Mode Function + ------- ---- -------- + + 10 Out Selects and enables Controller and Drive + 10 In Indicates status of Drive and Controller + 11 Out Controls Disk Function + 11 In Indicates current sector position of disk + 12 Out Write data + 12 In Read data + + Drive Select Out (Device 10 OUT): + + +---+---+---+---+---+---+---+---+ + | C | X | X | X | Device | + +---+---+---+---+---+---+---+---+ + + C = If this bit is 1, the disk controller selected by 'device' is + cleared. If the bit is zero, 'device' is selected as the + device being controlled by subsequent I/O operations. + X = not used + Device = value zero thru 15, selects drive to be controlled. + + Drive Status In (Device 10 IN): + + +---+---+---+---+---+---+---+---+ + | R | Z | I | X | X | H | M | W | + +---+---+---+---+---+---+---+---+ + + W - When 0, write circuit ready to write another byte. + M - When 0, head movement is allowed + H - When 0, indicates head is loaded for read/write + X - not used (will be 0) + I - When 0, indicates interrupts enabled (not used this simulator) + Z - When 0, indicates head is on track 0 + R - When 0, indicates that read circuit has new byte to read + + Drive Control (Device 11 OUT): + + +---+---+---+---+---+---+---+---+ + | W | C | D | E | U | H | O | I | + +---+---+---+---+---+---+---+---+ + + I - When 1, steps head IN one track + O - When 1, steps head OUT out track + H - When 1, loads head to drive surface + U - When 1, unloads head + E - Enables interrupts (ignored this simulator) + D - Disables interrupts (ignored this simulator) + C - When 1 lowers head current (ignored this simulator) + W - When 1, starts Write Enable sequence: W bit on device 10 + (see above) will go 1 and data will be read from port 12 + until 137 bytes have been read by the controller from + that port. The W bit will go off then, and the sector data + will be written to disk. Before you do this, you must have + stepped the track to the desired number, and waited until + the right sector number is presented on device 11 IN, then + set this bit. + + Sector Position (Device 11 IN): + + As the sectors pass by the read head, they are counted and the + number of the current one is available in this register. + + +---+---+---+---+---+---+---+---+ + | X | X | Sector Number | T | + +---+---+---+---+---+---+---+---+ + + X = Not used + Sector number = binary of the sector number currently under the + head, 0-31. + T = Sector True, is a 1 when the sector is positioned to read or + write. + +*/ + +#include + +#include "altair_defs.h" + +#define UNIT_V_ENABLE (UNIT_V_UF + 0) /* Write Enable */ +#define UNIT_ENABLE (1 << UNIT_V_ENABLE) + +#define DSK_SECTSIZE 137 +#define DSK_SECT 32 +#define DSK_TRACSIZE 4384 +#define DSK_SURF 1 +#define DSK_CYL 77 +#define DSK_SIZE (DSK_SECT * DSK_SURF * DSK_CYL * DSK_SECTSIZE) + +t_stat dsk_svc (UNIT *uptr); +t_stat dsk_reset (DEVICE *dptr); +void writebuf(); + +extern int32 PCX; + +/* Global data on status */ + +int32 cur_disk = 8; /* Currently selected drive */ +int32 cur_track[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; +int32 cur_sect[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; +int32 cur_byte[9] = {0, 0, 0, 0, 0, 0, 0, 0, 377}; +int32 cur_flags[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0}; + +char dskbuf[137]; /* Data Buffer */ +int32 dirty = 0; /* 1 when buffer has unwritten data in it */ +UNIT *dptr; /* fileref to write dirty buffer to */ + +int32 dsk_rwait = 100; /* rotate latency */ + + +/* 88DSK Standard I/O Data Structures */ + +UNIT dsk_unit[] = { + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) } +}; + +REG dsk_reg[] = { + { ORDATA (DISK, cur_disk, 4) }, + { NULL } +}; + +DEVICE dsk_dev = { + "DSK", dsk_unit, dsk_reg, NULL, + 8, 10, 31, 1, 8, 8, + NULL, NULL, &dsk_reset, + NULL, NULL, NULL +}; + +/* Service routines to handle simlulator functions */ + +/* service routine - actually gets char & places in buffer */ + +t_stat dsk_svc (UNIT *uptr) +{ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat dsk_reset (DEVICE *dptr) +{ +cur_disk = 0; +return SCPE_OK; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +/* Disk Controller Status/Select */ + +/* IMPORTANT: The status flags read by port 8 IN instruction are + INVERTED, that is, 0 is true and 1 is false. To handle this, the + simulator keeps it's own status flags as 0=false, 1=true; and + returns the COMPLEMENT of the status flags when read. This makes + setting/testing of the flag bits more logical, yet meets the + simulation requirement that they are reversed in hardware. +*/ + +int32 dsk10(int32 io, int32 data) +{ + + if (io == 0) { /* IN: return flags */ + return ((~cur_flags[cur_disk]) & 0xFF); /* Return the COMPLEMENT! */ + } + + /* OUT: Controller set/reset/enable/disable */ + + if (dirty == 1) + writebuf(); + + /*printf("\n[%o] OUT 10: %x", PCX, data);*/ + cur_disk = data & 0x0F; + if (data & 0x80) { + cur_flags[cur_disk] = 0; /* Disable drive */ + cur_sect[cur_disk = 0377]; + cur_byte[cur_disk = 0377]; + return (0); + } + cur_flags[cur_disk] = 0x1A; /* Enable: head move true */ + cur_sect[cur_disk] = 0377; /* reset internal counters */ + cur_byte[cur_disk] = 0377; + if (cur_track[cur_disk] == 0) + cur_flags[cur_disk] |= 0x40; /* track 0 if there */ + return (0); +} + +/* Disk Drive Status/Functions */ + +int32 dsk11(int32 io, int32 data) +{ + int32 stat; + + if (io == 0) { /* Read sector position */ + /*printf("\n[%o] IN 11", PCX);*/ + if (dirty == 1) + writebuf(); + if (cur_flags[cur_disk] & 0x04) { /* head loaded? */ + cur_sect[cur_disk]++; + if (cur_sect[cur_disk] > 31) + cur_sect[cur_disk] = 0; + cur_byte[cur_disk] = 0377; + stat = cur_sect[cur_disk] << 1; + stat &= 0x3E; /* return 'sector true' bit = 0 (true) */ + stat |= 0xC0; /* set on 'unused' bits */ + return (stat); + } else { + return (0); /* head not loaded - return 0 */ + } + } + + /* Drive functions */ + + if (cur_disk > 7) + return (0); /* no drive selected - can do nothin */ + + /*printf("\n[%o] OUT 11: %x", PCX, data);*/ + if (data & 0x01) { /* Step head in */ + cur_track[cur_disk]++; + if (cur_track[cur_disk] > 76 ) + cur_track[cur_disk] = 76; + if (dirty == 1) + writebuf(); + cur_sect[cur_disk] = 0377; + cur_byte[cur_disk] = 0377; + } + + if (data & 0x02) { /* Step head out */ + cur_track[cur_disk]--; + if (cur_track[cur_disk] < 0) { + cur_track[cur_disk] = 0; + cur_flags[cur_disk] |= 0x40; /* track 0 if there */ + } + if (dirty == 1) + writebuf(); + cur_sect[cur_disk] = 0377; + cur_byte[cur_disk] = 0377; + } + + if (dirty == 1) + writebuf(); + + if (data & 0x04) { /* Head load */ + cur_flags[cur_disk] |= 0x04; /* turn on head loaded bit */ + cur_flags[cur_disk] |= 0x80; /* turn on 'read data available */ + } + + if (data & 0x08) { /* Head Unload */ + cur_flags[cur_disk] &= 0xFB; /* off on 'head loaded' */ + cur_flags[cur_disk] &= 0x7F; /* off on 'read data avail */ + cur_sect[cur_disk] = 0377; + cur_byte[cur_disk] = 0377; + } + + /* Interrupts & head current are ignored */ + + if (data & 0x80) { /* write sequence start */ + cur_byte[cur_disk] = 0; + cur_flags[cur_disk] |= 0x01; /* enter new write data on */ + } + return 0; +} + +/* Disk Data In/Out*/ + +int32 dsk12(int32 io, int32 data) +{ + static int32 rtn, i; + static long pos; + UNIT *uptr; + + uptr = dsk_dev.units + cur_disk; + if (io == 0) { + if ((i = cur_byte[cur_disk]) < 138) { /* just get from buffer */ + cur_byte[cur_disk]++; + return (dskbuf[i] & 0xFF); + } + /* physically read the sector */ + /*printf("\n[%o] IN 12 (READ) T%d S%d", PCX, cur_track[cur_disk], + cur_sect[cur_disk]);*/ + pos = DSK_TRACSIZE * cur_track[cur_disk]; + pos += DSK_SECTSIZE * cur_sect[cur_disk]; + rtn = fseek(uptr -> fileref, pos, 0); + rtn = fread(dskbuf, 137, 1, uptr -> fileref); + cur_byte[cur_disk] = 1; + return (dskbuf[0] & 0xFF); + } else { + if (cur_byte[cur_disk] > 136) { + i = cur_byte[cur_disk]; + dskbuf[i] = data & 0xFF; + writebuf(); + return (0); + } + i = cur_byte[cur_disk]; + dirty = 1; + dptr = uptr; + dskbuf[i] = data & 0xFF; + cur_byte[cur_disk]++; + return (0); + } +} + +void writebuf() +{ + long pos; + int32 rtn, i; + + i = cur_byte[cur_disk]; /* null-fill rest of sector if any */ + while (i < 138) { + dskbuf[i] = 0; + i++; + } + /*printf("\n[%o] OUT 12 (WRITE) T%d S%d", PCX, cur_track[cur_disk], + cur_sect[cur_disk]); i = getch(); */ + pos = DSK_TRACSIZE * cur_track[cur_disk]; /* calc file pos */ + pos += DSK_SECTSIZE * cur_sect[cur_disk]; + rtn = fseek(dptr -> fileref, pos, 0); + rtn = fwrite(dskbuf, 137, 1, dptr -> fileref); + cur_flags[cur_disk] &= 0xFE; /* ENWD off */ + cur_byte[cur_disk] = 0377; + dirty = 0; + return; +} diff --git a/ALTAIR/altair_sio.c b/ALTAIR/altair_sio.c new file mode 100644 index 0000000..abe4cd9 --- /dev/null +++ b/ALTAIR/altair_sio.c @@ -0,0 +1,262 @@ +/* altair_sio: MITS Altair serial I/O card + + Copyright (c) 1997-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + These functions support a simulated MITS 2SIO interface card. + The card had two physical I/O ports which could be connected + to any serial I/O device that would connect to a current loop, + RS232, or TTY interface. Available baud rates were jumper + selectable for each port from 110 to 9600. + + All I/O is via programmed I/O. Each each has a status port + and a data port. A write to the status port can select + some options for the device (0x03 will reset the port). + A read of the status port gets the port status: + + +---+---+---+---+---+---+---+---+ + | X X X X X X O I | + +---+---+---+---+---+---+---+---+ + + I - A 1 in this bit position means a character has been received + on the data port and is ready to be read. + O - A 1 in this bit means the port is ready to receive a character + on the data port and transmit it out over the serial line. + + A read to the data port gets the buffered character, a write + to the data port writes the character to the device. +*/ + +#include + +#include "altair_defs.h" + +#define UNIT_V_ANSI (UNIT_V_UF + 0) /* ANSI mode */ +#define UNIT_ANSI (1 << UNIT_V_ANSI) + +t_stat sio_svc (UNIT *uptr); +t_stat sio_reset (DEVICE *dptr); +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); + +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ + +/* 2SIO Standard I/O Data Structures */ + +UNIT sio_unit = { UDATA (&sio_svc, 0, 0), KBD_POLL_WAIT }; + +REG sio_reg[] = { + { ORDATA (DATA, sio_unit.buf, 8) }, + { ORDATA (STAT, sio_unit.u3, 8) }, + { NULL } +}; + +MTAB sio_mod[] = { + { UNIT_ANSI, 0, "TTY", "TTY", NULL }, + { UNIT_ANSI, UNIT_ANSI, "ANSI", "ANSI", NULL }, + { 0 } +}; + +DEVICE sio_dev = { + "2SIO", &sio_unit, sio_reg, sio_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &sio_reset, + NULL, NULL, NULL +}; + +UNIT ptr_unit = { UDATA (&ptr_svc, UNIT_SEQ + UNIT_ATTABLE, 0), KBD_POLL_WAIT }; + +REG ptr_reg[] = { + { ORDATA (DATA, ptr_unit.buf, 8) }, + { ORDATA (STAT, ptr_unit.u3, 8) }, + { ORDATA (POS, ptr_unit.pos, T_ADDR_W) }, + { NULL } +}; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + NULL, NULL, NULL +}; + +UNIT ptp_unit = { UDATA (&ptp_svc, UNIT_SEQ + UNIT_ATTABLE, 0), KBD_POLL_WAIT }; + +REG ptp_reg[] = { + { ORDATA (DATA, ptp_unit.buf, 8) }, + { ORDATA (STAT, ptp_unit.u3, 8) }, + { ORDATA (POS, ptp_unit.pos, T_ADDR_W) }, + { NULL } +}; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL +}; + +/* Service routines to handle simulator functions */ + +/* service routine - actually gets char & places in buffer */ + +int32 sio_svc (UNIT *uptr) +{ + int32 temp; + + sim_activate (&sio_unit, sio_unit.wait); /* continue poll */ + if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) + return temp; /* no char or error? */ + sio_unit.buf = temp & 0377; /* Save char */ + sio_unit.u3 |= 0x01; /* Set status */ + + /* Do any special character handling here */ + + sio_unit.pos++; + return SCPE_OK; +} + + +int32 ptr_svc (UNIT *uptr) +{ + return SCPE_OK; +} + +int32 ptp_svc (UNIT *uptr) +{ + return SCPE_OK; +} + + +/* Reset routine */ + +int32 sio_reset (DEVICE *dptr) +{ + sio_unit.buf = 0; /* Data */ + sio_unit.u3 = 0x02; /* Status */ + sim_activate (&sio_unit, sio_unit.wait); /* activate unit */ + return SCPE_OK; +} + + +int32 ptr_reset (DEVICE *dptr) +{ + ptr_unit.buf = 0; + ptr_unit.u3 = 0x02; + sim_cancel (&ptr_unit); /* deactivate unit */ + return SCPE_OK; +} + +int32 ptp_reset (DEVICE *dptr) +{ + ptp_unit.buf = 0; + ptp_unit.u3 = 0x02; + sim_cancel (&ptp_unit); /* deactivate unit */ + return SCPE_OK; +} + + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +int32 sio0s(int32 io, int32 data) +{ + if (io == 0) { + return (sio_unit.u3); + } else { + if (data == 0x03) { /* reset port! */ + sio_unit.u3 = 0x02; + sio_unit.buf = 0; + sio_unit.pos = 0; + } + return (0); + } +} + +int32 sio0d(int32 io, int32 data) +{ + if (io == 0) { + sio_unit.u3 = sio_unit.u3 & 0xFE; + return (sio_unit.buf); + } else { + sim_putchar(data); + } + return 0; +} + +/* Port 2 controls the PTR/PTP devices */ + +int32 sio1s(int32 io, int32 data) +{ + if (io == 0) { + if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return 0x02; + if (ptr_unit.u3 != 0) /* No more data? */ + return 0x02; + return (0x03); /* ready to read/write */ + } else { + if (data == 0x03) { + ptr_unit.u3 = 0; + ptr_unit.buf = 0; + ptr_unit.pos = 0; + ptp_unit.u3 = 0; + ptp_unit.buf = 0; + ptp_unit.pos = 0; + } + return (0); + } +} + +int32 sio1d(int32 io, int32 data) +{ + int32 temp; + UNIT *uptr; + + if (io == 0) { + if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return 0; + if (ptr_unit.u3 != 0) + return 0; + uptr = ptr_dev.units; + if ((temp = getc(uptr -> fileref)) == EOF) { /* end of file? */ + ptr_unit.u3 = 0x01; + return 0; + } + ptr_unit.pos++; + return (temp & 0xFF); + } else { + uptr = ptp_dev.units; + putc(data, uptr -> fileref); + ptp_unit.pos++; + } + return 0; +} + diff --git a/ALTAIR/altair_sys.c b/ALTAIR/altair_sys.c new file mode 100644 index 0000000..fa32695 --- /dev/null +++ b/ALTAIR/altair_sys.c @@ -0,0 +1,313 @@ +/* altair_sys.c: MITS Altair system interface + + Copyright (c) 1997-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. +*/ + +#include +#include "altair_defs.h" + +extern DEVICE cpu_dev; +extern DEVICE dsk_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern DEVICE sio_dev; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE lpt_dev; +extern unsigned char M[]; +extern int32 saved_PC; + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "Altair 8800"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &cpu_dev, + &sio_dev, + &ptr_dev, + &ptp_dev, + &dsk_dev, + NULL +}; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O Instruction", + "HALT instruction", + "Breakpoint", + "Invalid Opcode" +}; + +static const char *opcode[] = { +"NOP", "LXI B", "STAX B", "INX B", /* 000-003 */ +"INR B", "DCR B", "MVI B", "RLC", /* 004-007 */ +"???", "DAD B", "LDAX B", "DCX B", /* 010-013 */ +"INR C", "DCR C", "MVI C", "RRC", /* 014-017 */ +"???", "LXI D", "STAX D", "INX D", /* 020-023 */ +"INR D", "DCR D", "MVI D", "RAL", /* 024-027 */ +"???", "DAD D", "LDAX D", "DCX D", /* 030-033 */ +"INR E", "DCR E", "MVI E", "RAR", /* 034-037 */ +"???", "LXI H", "SHLD", "INX H", /* 040-043 */ +"INR H", "DCR H", "MVI H", "DAA", /* 044-047 */ +"???", "DAD H", "LHLD", "DCX H", /* 050-053 */ +"INR L", "DCR L", "MVI L", "CMA", /* 054-057 */ +"???", "LXI SP", "STA", "INX SP", /* 060-063 */ +"INR M", "DCR M", "MVI M", "STC", /* 064-067 */ +"???", "DAD SP", "LDA", "DCX SP", /* 070-073 */ +"INR A", "DCR A", "MVI A", "CMC", /* 074-077 */ +"MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", /* 100-103 */ +"MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A", /* 104-107 */ +"MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E", /* 110-113 */ +"MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A", /* 114-117 */ +"MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", /* 120-123 */ +"MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A", /* 124-127 */ +"MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E", /* 130-133 */ +"MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A", /* 134-137 */ +"MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", /* 140-143 */ +"MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A", /* 144-147 */ +"MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E", /* 150-153 */ +"MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A", /* 154-157 */ +"MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", /* 160-163 */ +"MOV M,H", "MOV M,L", "HLT", "MOV M,A", /* 164-167 */ +"MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E", /* 170-173 */ +"MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A", /* 174-177 */ +"ADD B", "ADD C", "ADD D", "ADD E", /* 200-203 */ +"ADD H", "ADD L", "ADD M", "ADD A", /* 204-207 */ +"ADC B", "ADC C", "ADC D", "ADC E", /* 210-213 */ +"ADC H", "ADC L", "ADC M", "ADC A", /* 214-217 */ +"SUB B", "SUB C", "SUB D", "SUB E", /* 220-223 */ +"SUB H", "SUB L", "SUB M", "SUB A", /* 224-227 */ +"SBB B", "SBB C", "SBB D", "SBB E", /* 230-233 */ +"SBB H", "SBB L", "SBB M", "SBB A", /* 234-237 */ +"ANA B", "ANA C", "ANA D", "ANA E", /* 240-243 */ +"ANA H", "ANA L", "ANA M", "ANA A", /* 244-247 */ +"XRA B", "XRA C", "XRA D", "XRA E", /* 250-253 */ +"XRA H", "XRA L", "XRA M", "XRA A", /* 254-257 */ +"ORA B", "ORA C", "ORA D", "ORA E", /* 260-263 */ +"ORA H", "ORA L", "ORA M", "ORA A", /* 264-267 */ +"CMP B", "CMP C", "CMP D", "CMP E", /* 270-273 */ +"CMP H", "CMP L", "CMP M", "CMP A", /* 274-277 */ +"RNZ", "POP B", "JNZ", "JMP", /* 300-303 */ +"CNZ", "PUSH B", "ADI", "RST 0", /* 304-307 */ +"RZ", "RET", "JZ", "???", /* 310-313 */ +"CZ", "CALL", "ACI", "RST 1", /* 314-317 */ +"RNC", "POP D", "JNC", "OUT", /* 320-323 */ +"CNC", "PUSH D", "SUI", "RST 2", /* 324-327 */ +"RC", "???", "JC", "IN", /* 330-333 */ +"CC", "???", "SBI", "RST 3", /* 334-337 */ +"RPO", "POP H", "JPO", "XTHL", /* 340-343 */ +"CPO", "PUSH H", "ANI", "RST 4", /* 344-347 */ +"RPE", "PCHL", "JPE", "XCHG", /* 350-353 */ +"CPE", "???", "XRI", "RST 5", /* 354-357 */ +"RP", "POP PSW", "JP", "DI", /* 360-363 */ +"CP", "PUSH PSW", "ORI", "RST 6", /* 364-367 */ +"RM", "SPHL", "JM", "EI", /* 370-373 */ +"CM", "???", "CPI", "RST 7", /* 374-377 */ + }; + +int32 oplen[256] = { +1,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1,0,3,1,1,1,1,2,1,0,1,1,1,1,1,2,1, +0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1,0,3,3,1,1,1,2,1,0,1,3,1,1,1,2,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,3,3,3,1,2,1,1,1,3,0,3,3,2,1,1,1,3,2,3,1,2,1,1,0,3,2,3,0,2,1, +1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1,1,1,3,1,3,1,2,1,1,1,3,1,3,0,2,1 }; + +/* This is the binary loader. The input file is considered to be + a string of literal bytes with no format special format. The + load starts at the current value of the PC. +*/ + +int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 i, addr = 0, cnt = 0; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +addr = saved_PC; +while ((i = getc (fileref)) != EOF) { + M[addr] = i; + addr++; + cnt++; +} /* end while */ +printf ("%d Bytes loaded.\n", cnt); +return (SCPE_OK); +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +int32 fprint_sym (FILE *of, int32 addr, uint32 *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, c1, c2, inst, adr; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +c1 = (val[0] >> 8) & 0177; +c2 = val[0] & 0177; +if (sw & SWMASK ('A')) { + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; +} +if (sw & SWMASK ('C')) { + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; +} +if (!(sw & SWMASK ('M'))) return SCPE_ARG; +inst = val[0]; +fprintf (of, "%s", opcode[inst]); +if (oplen[inst] == 2) { + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%o", val[1]); +} +if (oplen[inst] == 3) { + adr = val[1] & 0xFF; + adr |= (val[2] << 8) & 0xff00; + if (strchr(opcode[inst], ' ') != NULL) + fprintf (of, ","); + else fprintf (of, " "); + fprintf (of, "%o", adr); +} +return -(oplen[inst] - 1); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +int32 parse_sym (char *cptr, int32 addr, UNIT *uptr, uint32 *val, int32 sw) +{ +int32 cflag, i = 0, j, r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (uint32) cptr[0]; + return SCPE_OK; +} +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((uint32) cptr[0] << 8) + (uint32) cptr[1]; + return SCPE_OK; +} + +/* An instruction: get opcode (all characters until null, comma, + or numeric (including spaces). +*/ + +while (1) { + if (*cptr == ',' || *cptr == '\0' || + isdigit(*cptr)) + break; + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* Allow for RST which has numeric as part of opcode */ + +if (toupper(gbuf[0]) == 'R' && + toupper(gbuf[1]) == 'S' && + toupper(gbuf[2]) == 'T') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* Allow for 'MOV' which is only opcode that has comma in it. */ + +if (toupper(gbuf[0]) == 'M' && + toupper(gbuf[1]) == 'O' && + toupper(gbuf[2]) == 'V') { + gbuf[i] = toupper(*cptr); + cptr++; + i++; + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* kill trailing spaces if any */ +gbuf[i] = '\0'; +for (j = i - 1; gbuf[j] == ' '; j--) { + gbuf[j] = '\0'; +} + +/* find opcode in table */ +for (j = 0; j < 256; j++) { + if (strcmp(gbuf, opcode[j]) == 0) + break; +} +if (j > 255) /* not found */ + return SCPE_ARG; + +val[0] = j; /* store opcode */ +if (oplen[j] < 2) /* if 1-byter we are done */ + return SCPE_OK; +if (*cptr == ',') cptr++; +cptr = get_glyph(cptr, gbuf, 0); /* get address */ +sscanf(gbuf, "%o", &r); +if (oplen[j] == 2) { + val[1] = r & 0xFF; + return (-1); +} +val[1] = r & 0xFF; +val[2] = (r >> 8) & 0xFF; +return (-2); +} diff --git a/AltairZ80/altairz80_cpu.c b/AltairZ80/altairz80_cpu.c new file mode 100644 index 0000000..7d696cd --- /dev/null +++ b/AltairZ80/altairz80_cpu.c @@ -0,0 +1,6615 @@ +/* altairz80_cpu.c: MITS Altair CPU (8080 and Z80) + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + + Based on work by Charles E Owen (c) 1997 + Code for Z80 CPU from Frank D. Cringle ((c) 1995 under GNU license) +*/ + +#include "altairz80_defs.h" +#include +#define SWITCHCPU_DEFAULT 0xfd + +#if defined (_WIN32) +#include +#else +#include +#endif + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_SIZE_LOG2 6 /* log2 of PCQ_SIZE */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY(PC) if (pcq[pcq_p] != (PC)) { pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = (PC); } + +#define FLAG_C 1 +#define FLAG_N 2 +#define FLAG_P 4 +#define FLAG_H 16 +#define FLAG_Z 64 +#define FLAG_S 128 + +#define SETFLAG(f,c) AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f +#define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) + +#define LOW_DIGIT(x) ((x) & 0xf) +#define HIGH_DIGIT(x) (((x) >> 4) & 0xf) +#define LOW_REGISTER(x) ((x) & 0xff) +#define HIGH_REGISTER(x) (((x) >> 8) & 0xff) + +#define SET_LOW_REGISTER(x, v) x = (((x) & 0xff00) | ((v) & 0xff)) +#define SET_HIGH_REGISTER(x, v) x = (((x) & 0xff) | (((v) & 0xff) << 8)) + +#define PARITY(x) parityTable[(x) & 0xff] +/* SET_PV and SET_PV2 are used to provide correct PARITY flag semantics for the 8080 in cases + where the Z80 uses the overflow flag +*/ +#define SET_PVS(s) ((chiptype == CHIP_TYPE_Z80) ? (((cbits >> 6) ^ (cbits >> 5)) & 4) : (PARITY(s))) +#define SET_PV (SET_PVS(sum)) +#define SET_PV2(x) ((chiptype == CHIP_TYPE_Z80) ? (((temp == (x)) << 2)) : (PARITY(temp))) + +/* CHECK_CPU_8080 must be invoked whenever a Z80 only instruction is executed + In case a Z80 instruction is executed on an 8080 the following two cases exist: + 1) Trapping is enabled: execution stops + 2) Trapping is not enabled: decoding continues with the next byte +*/ +#define CHECK_CPU_8080 \ + if (chiptype == CHIP_TYPE_8080) { \ + if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ + reason = STOP_OPCODE; \ + goto end_decode; \ + } \ + else { \ + sim_brk_pend[0] = FALSE; \ + continue; \ + } \ + } + +/* CHECK_CPU_Z80 must be invoked whenever a non Z80 instruction is executed */ +#define CHECK_CPU_Z80 \ + if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ + reason = STOP_OPCODE; \ + goto end_decode; \ + } + +#define POP(x) { \ + register uint32 y = RAM_PP(SP); \ + x = y + (RAM_PP(SP) << 8); \ +} + +#define JPC(cond) { \ + tStates += 10; \ + if (cond) { \ + PCQ_ENTRY(PCX); \ + PC = GET_WORD(PC); \ + } \ + else { \ + PC += 2; \ + } \ +} + +#define CALLC(cond) { \ + if (cond) { \ + register uint32 adrr = GET_WORD(PC); \ + CHECK_BREAK_WORD(SP - 2); \ + PUSH(PC + 2); \ + PCQ_ENTRY(PCX); \ + PC = adrr; \ + tStates += 17; \ + } \ + else { \ + sim_brk_pend[0] = FALSE; \ + PC += 2; \ + tStates += 10; \ + } \ +} + +extern int32 sim_int_char; +extern int32 sio0s (const int32 port, const int32 io, const int32 data); +extern int32 sio0d (const int32 port, const int32 io, const int32 data); +extern int32 sio1s (const int32 port, const int32 io, const int32 data); +extern int32 sio1d (const int32 port, const int32 io, const int32 data); +extern int32 dsk10 (const int32 port, const int32 io, const int32 data); +extern int32 dsk11 (const int32 port, const int32 io, const int32 data); +extern int32 dsk12 (const int32 port, const int32 io, const int32 data); +extern int32 netStatus (const int32 port, const int32 io, const int32 data); +extern int32 netData (const int32 port, const int32 io, const int32 data); +extern int32 nulldev (const int32 port, const int32 io, const int32 data); +extern int32 hdsk_io (const int32 port, const int32 io, const int32 data); +extern int32 simh_dev (const int32 port, const int32 io, const int32 data); +extern int32 sr_dev (const int32 port, const int32 io, const int32 data); +extern void install_ALTAIRbootROM(void); +extern char messageBuffer[]; +extern void printMessage(void); +extern void do_SIMH_sleep(void); + +extern t_stat sim_instr_nommu(void); +extern uint8 MOPT[MAXBANKSIZE]; +extern t_stat sim_instr_8086(void); +extern t_stat sim_instr_8086(void); +extern void cpu8086reset(void); + +/* function prototypes */ +#ifdef CPUSWITCHER +static t_stat cpu_set_switcher (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_reset_switcher(UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_show_switcher (FILE *st, UNIT *uptr, int32 val, void *desc); +static int32 switchcpu_io (const int32 port, const int32 io, const int32 data); +#endif + +static t_stat cpu_set_altairrom (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_noaltairrom (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_nommu (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_banked (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_nonbanked (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_ramtype (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_chiptype (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_set_memory (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat cpu_clear_command (UNIT *uptr, int32 value, char *cptr, void *desc); +static void cpu_clear(void); +static t_stat cpu_show (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat chip_show (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +static t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw); +static t_stat cpu_reset(DEVICE *dptr); +static t_stat sim_instr_mmu(void); +static uint32 GetBYTE(register uint32 Addr); +static void PutWORD(register uint32 Addr, const register uint32 Value); +static void PutBYTE(register uint32 Addr, const register uint32 Value); +void out(const uint32 Port, const uint32 Value); +uint32 in(const uint32 Port); +void altairz80_init(void); +t_stat sim_instr(void); +t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM); +uint8 GetBYTEWrapper(const uint32 Addr); +void PutBYTEWrapper(const uint32 Addr, const uint32 Value); +int32 getBankSelect(void); +void setBankSelect(const int32 b); +uint32 getCommon(void); +t_stat sim_load(FILE *fileref, char *cptr, char *fnam, int32 flag); +uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +void PutBYTEExtended(register uint32 Addr, const register uint32 Value); +uint32 GetBYTEExtended(register uint32 Addr); + +/* CPU data structures + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { + UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_CPU_ALTAIRROM | + UNIT_CPU_STOPONHALT | UNIT_CPU_MMU, MAXBANKSIZE) +}; + + uint32 PCX = 0; /* external view of PC */ + int32 AF_S; /* AF register */ + int32 BC_S; /* BC register */ + int32 DE_S; /* DE register */ + int32 HL_S; /* HL register */ + int32 IX_S; /* IX register */ + int32 IY_S; /* IY register */ + int32 PC_S = 0; /* program counter */ + int32 SP_S; /* SP register */ + int32 AF1_S; /* alternate AF register */ + int32 BC1_S; /* alternate BC register */ + int32 DE1_S; /* alternate DE register */ + int32 HL1_S; /* alternate HL register */ + int32 IFF_S; /* Interrupt Flip Flop */ + int32 IR_S; /* Interrupt (upper) / Refresh (lower) register */ + int32 AX_S; /* AX register (8086) */ + int32 BX_S; /* BX register (8086) */ + int32 CX_S; /* CX register (8086) */ + int32 DX_S; /* DX register (8086) */ + int32 CS_S; /* CS register (8086) */ + int32 DS_S; /* DS register (8086) */ + int32 ES_S; /* ES register (8086) */ + int32 SS_S; /* SS register (8086) */ + int32 DI_S; /* DI register (8086) */ + int32 SI_S; /* SI register (8086) */ + int32 BP_S; /* BP register (8086) */ + int32 SP8086_S; /* SP register (8086) */ + int32 IP_S; /* IP register (8086) */ + int32 FLAGS_S; /* flags register (8086) */ + int32 SR = 0; /* switch register */ +static int32 bankSelect = 0; /* determines selected memory bank */ +static uint32 common = 0xc000; /* addresses >= 'common' are in common memory */ +static uint32 previousCapacity = MAXBANKSIZE; /* safe for previous memory capacity */ +static uint32 clockFrequency = 0; /* in kHz, 0 means as fast as possible */ +static uint32 sliceLength = 10; /* length of time-slice for CPU speed */ + /* adjustment in milliseconds */ +static uint32 executedTStates = 0; /* executed t-states */ +static uint16 pcq[PCQ_SIZE] = { /* PC queue */ + 0 +}; +static int32 pcq_p = 0; /* PC queue ptr */ +static REG *pcq_r = NULL; /* PC queue reg ptr */ + +/* data structure for IN/OUT instructions */ +struct idev { + int32 (*routine)(const int32, const int32, const int32); +}; + +#ifdef CPUSWITCHER +static int32 switcherPort = SWITCHCPU_DEFAULT; +static struct idev oldSwitcherDevice = { NULL }; +#endif + +REG cpu_reg[] = { + { HRDATA (AF, AF_S, 16) }, + { HRDATA (BC, BC_S, 16) }, + { HRDATA (DE, DE_S, 16) }, + { HRDATA (HL, HL_S, 16) }, + { HRDATA (IX, IX_S, 16) }, + { HRDATA (IY, IY_S, 16) }, + { HRDATA (PC, PC_S, 16 + MAXBANKSLOG2) }, + { HRDATA (SP, SP_S, 16) }, + { HRDATA (AF1, AF1_S, 16) }, + { HRDATA (BC1, BC1_S, 16) }, + { HRDATA (DE1, DE1_S, 16) }, + { HRDATA (HL1, HL1_S, 16) }, + { GRDATA (IFF, IFF_S, 2, 2, 0) }, + { FLDATA (IR, IR_S, 8) }, + { HRDATA (AX, AX_S, 16) }, /* 8086 */ + { GRDATA (AL, AX_S, 16, 8, 0) }, /* 8086, low 8 bits of AX */ + { GRDATA (AH, AX_S, 16, 8, 8) }, /* 8086, high 8 bits of AX */ + { HRDATA (BX, BX_S, 16) }, /* 8086 */ + { GRDATA (BL, BX_S, 16, 8, 0) }, /* 8086, low 8 bits of BX */ + { GRDATA (BH, BX_S, 16, 8, 8) }, /* 8086, high 8 bits of BX */ + { HRDATA (CX, CX_S, 16) }, /* 8086 */ + { GRDATA (CL, CX_S, 16, 8, 0) }, /* 8086, low 8 bits of CX */ + { GRDATA (CH, CX_S, 16, 8, 8) }, /* 8086, high 8 bits of CX */ + { HRDATA (DX, DX_S, 16) }, /* 8086 */ + { GRDATA (DL, DX_S, 16, 8, 0) }, /* 8086, low 8 bits of DX */ + { GRDATA (DH, DX_S, 16, 8, 8) }, /* 8086, high 8 bits of DX */ + { HRDATA (SP86, SP8086_S, 16) }, /* 8086 */ + { HRDATA (BP, BP_S, 16) }, /* 8086, Base Pointer */ + { HRDATA (SI, SI_S, 16) }, /* 8086, Source Index */ + { HRDATA (DI, DI_S, 16) }, /* 8086, Destination Index */ + { HRDATA (CS, CS_S, 16) }, /* 8086, Code Segment */ + { HRDATA (DS, DS_S, 16) }, /* 8086, Data Segment */ + { HRDATA (ES, ES_S, 16) }, /* 8086, Extra Segment */ + { HRDATA (SS, SS_S, 16) }, /* 8086, Stack Segment */ + { HRDATA (FLAGS, FLAGS_S, 16) }, /* 8086, FLAGS */ + { HRDATA (IP, IP_S, 16), REG_RO }, /* 8086, set via PC */ + { FLDATA (OPSTOP, cpu_unit.flags, UNIT_CPU_V_OPSTOP), REG_HRO }, + { HRDATA (SR, SR, 8) }, + { HRDATA (BANK, bankSelect, MAXBANKSLOG2) }, + { HRDATA (COMMON, common, 32) }, +#ifdef CPUSWITCHER + { HRDATA (SWITCHERPORT, switcherPort, 8), }, +#endif + { DRDATA (CLOCK, clockFrequency, 32) }, + { DRDATA (SLICE, sliceLength, 16) }, + { DRDATA (TSTATES, executedTStates, 32), REG_RO }, + { HRDATA (CAPACITY, cpu_unit.capac, 32), REG_RO }, + { HRDATA (PREVCAP, previousCapacity, 32), REG_RO }, + { BRDATA (PCQ, pcq, 16, 16, PCQ_SIZE), REG_RO + REG_CIRC }, + { DRDATA (PCQP, pcq_p, PCQ_SIZE_LOG2), REG_HRO }, + { HRDATA (WRU, sim_int_char, 8) }, + { NULL } +}; + +static MTAB cpu_mod[] = { + { MTAB_XTD | MTAB_VDV, CHIP_TYPE_8080, NULL, "8080", &cpu_set_chiptype }, + { MTAB_XTD | MTAB_VDV, CHIP_TYPE_Z80, NULL, "Z80", &cpu_set_chiptype }, + { MTAB_XTD | MTAB_VDV, CHIP_TYPE_8086, NULL, "8086", &cpu_set_chiptype }, + { UNIT_CPU_OPSTOP, UNIT_CPU_OPSTOP, "ITRAP", "ITRAP", NULL, &chip_show }, + { UNIT_CPU_OPSTOP, 0, "NOITRAP", "NOITRAP", NULL, &chip_show }, + { UNIT_CPU_STOPONHALT, UNIT_CPU_STOPONHALT,"STOPONHALT", "STOPONHALT", NULL }, + { UNIT_CPU_STOPONHALT, 0, "LOOPONHALT", "LOOPONHALT", NULL }, + { UNIT_CPU_BANKED, UNIT_CPU_BANKED, "BANKED", "BANKED", &cpu_set_banked }, + { UNIT_CPU_BANKED, 0, "NONBANKED", "NONBANKED", &cpu_set_nonbanked }, + { UNIT_CPU_ALTAIRROM, UNIT_CPU_ALTAIRROM, "ALTAIRROM", "ALTAIRROM", &cpu_set_altairrom }, + { UNIT_CPU_ALTAIRROM, 0, "NOALTAIRROM", "NOALTAIRROM", &cpu_set_noaltairrom}, + { UNIT_CPU_VERBOSE, UNIT_CPU_VERBOSE, "VERBOSE", "VERBOSE", NULL, &cpu_show }, + { UNIT_CPU_VERBOSE, 0, "QUIET", "QUIET", NULL }, + { MTAB_VDV, 0, NULL, "CLEARMEMORY", &cpu_clear_command }, + { UNIT_CPU_MMU, UNIT_CPU_MMU, "MMU", "MMU", NULL }, + { UNIT_CPU_MMU, 0, "NOMMU", "NOMMU", &cpu_set_nommu }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "MEMORY", &cpu_set_memory }, +#ifdef CPUSWITCHER + { UNIT_CPU_SWITCHER, UNIT_CPU_SWITCHER, "SWITCHER", "SWITCHER", &cpu_set_switcher, &cpu_show_switcher }, + { UNIT_CPU_SWITCHER, 0, "NOSWITCHER", "NOSWITCHER", &cpu_reset_switcher, &cpu_show_switcher }, +#endif + { MTAB_XTD | MTAB_VDV, 0, NULL, "AZ80", &cpu_set_ramtype }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "HRAM", &cpu_set_ramtype }, + { MTAB_XTD | MTAB_VDV, 2, NULL, "VRAM", &cpu_set_ramtype }, + { MTAB_XTD | MTAB_VDV, 3, NULL, "CRAM", &cpu_set_ramtype }, + { MTAB_VDV, 4, NULL, "4KB", &cpu_set_size }, + { MTAB_VDV, 8, NULL, "8KB", &cpu_set_size }, + { MTAB_VDV, 12, NULL, "12KB", &cpu_set_size }, + { MTAB_VDV, 16, NULL, "16KB", &cpu_set_size }, + { MTAB_VDV, 20, NULL, "20KB", &cpu_set_size }, + { MTAB_VDV, 24, NULL, "24KB", &cpu_set_size }, + { MTAB_VDV, 28, NULL, "28KB", &cpu_set_size }, + { MTAB_VDV, 32, NULL, "32KB", &cpu_set_size }, + { MTAB_VDV, 36, NULL, "36KB", &cpu_set_size }, + { MTAB_VDV, 40, NULL, "40KB", &cpu_set_size }, + { MTAB_VDV, 44, NULL, "44KB", &cpu_set_size }, + { MTAB_VDV, 48, NULL, "48KB", &cpu_set_size }, + { MTAB_VDV, 52, NULL, "52KB", &cpu_set_size }, + { MTAB_VDV, 56, NULL, "56KB", &cpu_set_size }, + { MTAB_VDV, 60, NULL, "60KB", &cpu_set_size }, + { MTAB_VDV, 64, NULL, "64KB", &cpu_set_size }, + { 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 16, 1, 16, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, 0, 0, + NULL, NULL, NULL +}; + +/* This is the I/O configuration table. There are 255 possible + device addresses, if a device is plugged to a port it's routine + address is here, 'nulldev' means no device is available +*/ +static struct idev dev_table[256] = { + {&nulldev}, {&nulldev}, {&sio0d}, {&sio0s}, /* 00 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 04 */ + {&dsk10}, {&dsk11}, {&dsk12}, {&nulldev}, /* 08 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 0C */ + {&sio0s}, {&sio0d}, {&sio1s}, {&sio1d}, /* 10 */ + {&sio0s}, {&sio0d}, {&sio0s}, {&sio0d}, /* 14 */ + {&sio0s}, {&sio0d}, {&nulldev}, {&nulldev}, /* 18 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 1C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 20 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 24 */ + {&netStatus},{&netData},{&netStatus},{&netData}, /* 28 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 2C */ + {&nulldev}, {&nulldev}, {&netStatus},{&netData}, /* 30 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 34 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 38 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 3C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 40 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 44 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 48 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 4C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 50 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 54 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 58 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 5C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 60 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 64 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 68 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 6C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 70 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 74 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 78 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 7C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 80 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 84 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 88 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 8C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 90 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 94 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 98 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* 9C */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* A0 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* A4 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* A8 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* AC */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* B0 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* B4 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* B8 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* BC */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* C0 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* C4 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* C8 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* CC */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* D0 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* D4 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* D8 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* DC */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* D0 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* E4 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* E8 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* EC */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* F0 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* F4 */ + {&nulldev}, {&nulldev}, {&nulldev}, {&nulldev}, /* F8 */ + {&nulldev}, {&hdsk_io}, {&simh_dev}, {&sr_dev} /* FC */ +}; + +static int32 ramtype = 0; +int32 chiptype = CHIP_TYPE_8080; + +void out(const uint32 Port, const uint32 Value) { + dev_table[Port & 0xff].routine(Port, 1, Value); +} + +uint32 in(const uint32 Port) { + return dev_table[Port & 0xff].routine(Port, 0, 0); +} + +/* the following tables precompute some common subexpressions + parityTable[i] 0..255 (number of 1's in i is odd) ? 0 : 4 + incTable[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) + decTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2 + cbitsTable[i] 0..511 (i & 0x10) | ((i >> 8) & 1) + cbitsDup8Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | + (((i & 0xff) == 0) << 6) + cbitsDup16Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | (i & 0x28) + cbits2Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | 2 + rrcaTable[i] 0..255 ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) + rraTable[i] 0..255 ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) + addTable[i] 0..511 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) + subTable[i] 0..255 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2 + andTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i] + xororTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i] + rotateShiftTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff] + incZ80Table[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2) + decZ80Table[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2 + cbitsZ80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) + cbitsZ80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | + ((i >> 8) & 1) | (i & 0xa8) + cbits2Z80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 + cbits2Z80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | + (i & 0xa8) + negTable[i] 0..255 (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0) + rrdrldTable[i] 0..255 (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i] + cpTable[i] 0..255 (i & 0x80) | (((i & 0xff) == 0) << 6) +*/ + +/* parityTable[i] = (number of 1's in i is odd) ? 0 : 4, i = 0..255 */ +static const uint8 parityTable[256] = { + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, +}; + +/* incTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4), i = 0..256 */ +static const uint8 incTable[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80 +}; + +/* decTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2, i = 0..255 */ +static const uint8 decTable[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsTable[i] = (i & 0x10) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsDup8Table[i] = (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | + (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 cbitsDup8Table[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1818,0x1918,0x1a18,0x1b18,0x1c18,0x1d18,0x1e18,0x1f18, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730, + 0x3838,0x3938,0x3a38,0x3b38,0x3c38,0x3d38,0x3e38,0x3f38, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5818,0x5918,0x5a18,0x5b18,0x5c18,0x5d18,0x5e18,0x5f18, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7030,0x7130,0x7230,0x7330,0x7430,0x7530,0x7630,0x7730, + 0x7838,0x7938,0x7a38,0x7b38,0x7c38,0x7d38,0x7e38,0x7f38, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9090,0x9190,0x9290,0x9390,0x9490,0x9590,0x9690,0x9790, + 0x9898,0x9998,0x9a98,0x9b98,0x9c98,0x9d98,0x9e98,0x9f98, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0b0,0xb1b0,0xb2b0,0xb3b0,0xb4b0,0xb5b0,0xb6b0,0xb7b0, + 0xb8b8,0xb9b8,0xbab8,0xbbb8,0xbcb8,0xbdb8,0xbeb8,0xbfb8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd090,0xd190,0xd290,0xd390,0xd490,0xd590,0xd690,0xd790, + 0xd898,0xd998,0xda98,0xdb98,0xdc98,0xdd98,0xde98,0xdf98, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0b0,0xf1b0,0xf2b0,0xf3b0,0xf4b0,0xf5b0,0xf6b0,0xf7b0, + 0xf8b8,0xf9b8,0xfab8,0xfbb8,0xfcb8,0xfdb8,0xfeb8,0xffb8, + 0x0041,0x0101,0x0201,0x0301,0x0401,0x0501,0x0601,0x0701, + 0x0809,0x0909,0x0a09,0x0b09,0x0c09,0x0d09,0x0e09,0x0f09, + 0x1011,0x1111,0x1211,0x1311,0x1411,0x1511,0x1611,0x1711, + 0x1819,0x1919,0x1a19,0x1b19,0x1c19,0x1d19,0x1e19,0x1f19, + 0x2021,0x2121,0x2221,0x2321,0x2421,0x2521,0x2621,0x2721, + 0x2829,0x2929,0x2a29,0x2b29,0x2c29,0x2d29,0x2e29,0x2f29, + 0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731, + 0x3839,0x3939,0x3a39,0x3b39,0x3c39,0x3d39,0x3e39,0x3f39, + 0x4001,0x4101,0x4201,0x4301,0x4401,0x4501,0x4601,0x4701, + 0x4809,0x4909,0x4a09,0x4b09,0x4c09,0x4d09,0x4e09,0x4f09, + 0x5011,0x5111,0x5211,0x5311,0x5411,0x5511,0x5611,0x5711, + 0x5819,0x5919,0x5a19,0x5b19,0x5c19,0x5d19,0x5e19,0x5f19, + 0x6021,0x6121,0x6221,0x6321,0x6421,0x6521,0x6621,0x6721, + 0x6829,0x6929,0x6a29,0x6b29,0x6c29,0x6d29,0x6e29,0x6f29, + 0x7031,0x7131,0x7231,0x7331,0x7431,0x7531,0x7631,0x7731, + 0x7839,0x7939,0x7a39,0x7b39,0x7c39,0x7d39,0x7e39,0x7f39, + 0x8081,0x8181,0x8281,0x8381,0x8481,0x8581,0x8681,0x8781, + 0x8889,0x8989,0x8a89,0x8b89,0x8c89,0x8d89,0x8e89,0x8f89, + 0x9091,0x9191,0x9291,0x9391,0x9491,0x9591,0x9691,0x9791, + 0x9899,0x9999,0x9a99,0x9b99,0x9c99,0x9d99,0x9e99,0x9f99, + 0xa0a1,0xa1a1,0xa2a1,0xa3a1,0xa4a1,0xa5a1,0xa6a1,0xa7a1, + 0xa8a9,0xa9a9,0xaaa9,0xaba9,0xaca9,0xada9,0xaea9,0xafa9, + 0xb0b1,0xb1b1,0xb2b1,0xb3b1,0xb4b1,0xb5b1,0xb6b1,0xb7b1, + 0xb8b9,0xb9b9,0xbab9,0xbbb9,0xbcb9,0xbdb9,0xbeb9,0xbfb9, + 0xc081,0xc181,0xc281,0xc381,0xc481,0xc581,0xc681,0xc781, + 0xc889,0xc989,0xca89,0xcb89,0xcc89,0xcd89,0xce89,0xcf89, + 0xd091,0xd191,0xd291,0xd391,0xd491,0xd591,0xd691,0xd791, + 0xd899,0xd999,0xda99,0xdb99,0xdc99,0xdd99,0xde99,0xdf99, + 0xe0a1,0xe1a1,0xe2a1,0xe3a1,0xe4a1,0xe5a1,0xe6a1,0xe7a1, + 0xe8a9,0xe9a9,0xeaa9,0xeba9,0xeca9,0xeda9,0xeea9,0xefa9, + 0xf0b1,0xf1b1,0xf2b1,0xf3b1,0xf4b1,0xf5b1,0xf6b1,0xf7b1, + 0xf8b9,0xf9b9,0xfab9,0xfbb9,0xfcb9,0xfdb9,0xfeb9,0xffb9, +}; + +/* cbitsDup16Table[i] = (i & 0x10) | ((i >> 8) & 1) | (i & 0x28), i = 0..511 */ +static const uint8 cbitsDup16Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, +}; + +/* cbits2Table[i] = (i & 0x10) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrcaTable[i] = ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rrcaTable[256] = { + 0x0000,0x8001,0x0100,0x8101,0x0200,0x8201,0x0300,0x8301, + 0x0400,0x8401,0x0500,0x8501,0x0600,0x8601,0x0700,0x8701, + 0x0808,0x8809,0x0908,0x8909,0x0a08,0x8a09,0x0b08,0x8b09, + 0x0c08,0x8c09,0x0d08,0x8d09,0x0e08,0x8e09,0x0f08,0x8f09, + 0x1000,0x9001,0x1100,0x9101,0x1200,0x9201,0x1300,0x9301, + 0x1400,0x9401,0x1500,0x9501,0x1600,0x9601,0x1700,0x9701, + 0x1808,0x9809,0x1908,0x9909,0x1a08,0x9a09,0x1b08,0x9b09, + 0x1c08,0x9c09,0x1d08,0x9d09,0x1e08,0x9e09,0x1f08,0x9f09, + 0x2020,0xa021,0x2120,0xa121,0x2220,0xa221,0x2320,0xa321, + 0x2420,0xa421,0x2520,0xa521,0x2620,0xa621,0x2720,0xa721, + 0x2828,0xa829,0x2928,0xa929,0x2a28,0xaa29,0x2b28,0xab29, + 0x2c28,0xac29,0x2d28,0xad29,0x2e28,0xae29,0x2f28,0xaf29, + 0x3020,0xb021,0x3120,0xb121,0x3220,0xb221,0x3320,0xb321, + 0x3420,0xb421,0x3520,0xb521,0x3620,0xb621,0x3720,0xb721, + 0x3828,0xb829,0x3928,0xb929,0x3a28,0xba29,0x3b28,0xbb29, + 0x3c28,0xbc29,0x3d28,0xbd29,0x3e28,0xbe29,0x3f28,0xbf29, + 0x4000,0xc001,0x4100,0xc101,0x4200,0xc201,0x4300,0xc301, + 0x4400,0xc401,0x4500,0xc501,0x4600,0xc601,0x4700,0xc701, + 0x4808,0xc809,0x4908,0xc909,0x4a08,0xca09,0x4b08,0xcb09, + 0x4c08,0xcc09,0x4d08,0xcd09,0x4e08,0xce09,0x4f08,0xcf09, + 0x5000,0xd001,0x5100,0xd101,0x5200,0xd201,0x5300,0xd301, + 0x5400,0xd401,0x5500,0xd501,0x5600,0xd601,0x5700,0xd701, + 0x5808,0xd809,0x5908,0xd909,0x5a08,0xda09,0x5b08,0xdb09, + 0x5c08,0xdc09,0x5d08,0xdd09,0x5e08,0xde09,0x5f08,0xdf09, + 0x6020,0xe021,0x6120,0xe121,0x6220,0xe221,0x6320,0xe321, + 0x6420,0xe421,0x6520,0xe521,0x6620,0xe621,0x6720,0xe721, + 0x6828,0xe829,0x6928,0xe929,0x6a28,0xea29,0x6b28,0xeb29, + 0x6c28,0xec29,0x6d28,0xed29,0x6e28,0xee29,0x6f28,0xef29, + 0x7020,0xf021,0x7120,0xf121,0x7220,0xf221,0x7320,0xf321, + 0x7420,0xf421,0x7520,0xf521,0x7620,0xf621,0x7720,0xf721, + 0x7828,0xf829,0x7928,0xf929,0x7a28,0xfa29,0x7b28,0xfb29, + 0x7c28,0xfc29,0x7d28,0xfd29,0x7e28,0xfe29,0x7f28,0xff29, +}; + +/* rraTable[i] = ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rraTable[256] = { + 0x0000,0x0001,0x0100,0x0101,0x0200,0x0201,0x0300,0x0301, + 0x0400,0x0401,0x0500,0x0501,0x0600,0x0601,0x0700,0x0701, + 0x0808,0x0809,0x0908,0x0909,0x0a08,0x0a09,0x0b08,0x0b09, + 0x0c08,0x0c09,0x0d08,0x0d09,0x0e08,0x0e09,0x0f08,0x0f09, + 0x1000,0x1001,0x1100,0x1101,0x1200,0x1201,0x1300,0x1301, + 0x1400,0x1401,0x1500,0x1501,0x1600,0x1601,0x1700,0x1701, + 0x1808,0x1809,0x1908,0x1909,0x1a08,0x1a09,0x1b08,0x1b09, + 0x1c08,0x1c09,0x1d08,0x1d09,0x1e08,0x1e09,0x1f08,0x1f09, + 0x2020,0x2021,0x2120,0x2121,0x2220,0x2221,0x2320,0x2321, + 0x2420,0x2421,0x2520,0x2521,0x2620,0x2621,0x2720,0x2721, + 0x2828,0x2829,0x2928,0x2929,0x2a28,0x2a29,0x2b28,0x2b29, + 0x2c28,0x2c29,0x2d28,0x2d29,0x2e28,0x2e29,0x2f28,0x2f29, + 0x3020,0x3021,0x3120,0x3121,0x3220,0x3221,0x3320,0x3321, + 0x3420,0x3421,0x3520,0x3521,0x3620,0x3621,0x3720,0x3721, + 0x3828,0x3829,0x3928,0x3929,0x3a28,0x3a29,0x3b28,0x3b29, + 0x3c28,0x3c29,0x3d28,0x3d29,0x3e28,0x3e29,0x3f28,0x3f29, + 0x4000,0x4001,0x4100,0x4101,0x4200,0x4201,0x4300,0x4301, + 0x4400,0x4401,0x4500,0x4501,0x4600,0x4601,0x4700,0x4701, + 0x4808,0x4809,0x4908,0x4909,0x4a08,0x4a09,0x4b08,0x4b09, + 0x4c08,0x4c09,0x4d08,0x4d09,0x4e08,0x4e09,0x4f08,0x4f09, + 0x5000,0x5001,0x5100,0x5101,0x5200,0x5201,0x5300,0x5301, + 0x5400,0x5401,0x5500,0x5501,0x5600,0x5601,0x5700,0x5701, + 0x5808,0x5809,0x5908,0x5909,0x5a08,0x5a09,0x5b08,0x5b09, + 0x5c08,0x5c09,0x5d08,0x5d09,0x5e08,0x5e09,0x5f08,0x5f09, + 0x6020,0x6021,0x6120,0x6121,0x6220,0x6221,0x6320,0x6321, + 0x6420,0x6421,0x6520,0x6521,0x6620,0x6621,0x6720,0x6721, + 0x6828,0x6829,0x6928,0x6929,0x6a28,0x6a29,0x6b28,0x6b29, + 0x6c28,0x6c29,0x6d28,0x6d29,0x6e28,0x6e29,0x6f28,0x6f29, + 0x7020,0x7021,0x7120,0x7121,0x7220,0x7221,0x7320,0x7321, + 0x7420,0x7421,0x7520,0x7521,0x7620,0x7621,0x7720,0x7721, + 0x7828,0x7829,0x7928,0x7929,0x7a28,0x7a29,0x7b28,0x7b29, + 0x7c28,0x7c29,0x7d28,0x7d29,0x7e28,0x7e29,0x7f28,0x7f29, +}; + +/* addTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 addTable[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, +}; + +/* subTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2, i = 0..255 */ +static const uint16 subTable[256] = { + 0x0042,0x0102,0x0202,0x0302,0x0402,0x0502,0x0602,0x0702, + 0x080a,0x090a,0x0a0a,0x0b0a,0x0c0a,0x0d0a,0x0e0a,0x0f0a, + 0x1002,0x1102,0x1202,0x1302,0x1402,0x1502,0x1602,0x1702, + 0x180a,0x190a,0x1a0a,0x1b0a,0x1c0a,0x1d0a,0x1e0a,0x1f0a, + 0x2022,0x2122,0x2222,0x2322,0x2422,0x2522,0x2622,0x2722, + 0x282a,0x292a,0x2a2a,0x2b2a,0x2c2a,0x2d2a,0x2e2a,0x2f2a, + 0x3022,0x3122,0x3222,0x3322,0x3422,0x3522,0x3622,0x3722, + 0x382a,0x392a,0x3a2a,0x3b2a,0x3c2a,0x3d2a,0x3e2a,0x3f2a, + 0x4002,0x4102,0x4202,0x4302,0x4402,0x4502,0x4602,0x4702, + 0x480a,0x490a,0x4a0a,0x4b0a,0x4c0a,0x4d0a,0x4e0a,0x4f0a, + 0x5002,0x5102,0x5202,0x5302,0x5402,0x5502,0x5602,0x5702, + 0x580a,0x590a,0x5a0a,0x5b0a,0x5c0a,0x5d0a,0x5e0a,0x5f0a, + 0x6022,0x6122,0x6222,0x6322,0x6422,0x6522,0x6622,0x6722, + 0x682a,0x692a,0x6a2a,0x6b2a,0x6c2a,0x6d2a,0x6e2a,0x6f2a, + 0x7022,0x7122,0x7222,0x7322,0x7422,0x7522,0x7622,0x7722, + 0x782a,0x792a,0x7a2a,0x7b2a,0x7c2a,0x7d2a,0x7e2a,0x7f2a, + 0x8082,0x8182,0x8282,0x8382,0x8482,0x8582,0x8682,0x8782, + 0x888a,0x898a,0x8a8a,0x8b8a,0x8c8a,0x8d8a,0x8e8a,0x8f8a, + 0x9082,0x9182,0x9282,0x9382,0x9482,0x9582,0x9682,0x9782, + 0x988a,0x998a,0x9a8a,0x9b8a,0x9c8a,0x9d8a,0x9e8a,0x9f8a, + 0xa0a2,0xa1a2,0xa2a2,0xa3a2,0xa4a2,0xa5a2,0xa6a2,0xa7a2, + 0xa8aa,0xa9aa,0xaaaa,0xabaa,0xacaa,0xadaa,0xaeaa,0xafaa, + 0xb0a2,0xb1a2,0xb2a2,0xb3a2,0xb4a2,0xb5a2,0xb6a2,0xb7a2, + 0xb8aa,0xb9aa,0xbaaa,0xbbaa,0xbcaa,0xbdaa,0xbeaa,0xbfaa, + 0xc082,0xc182,0xc282,0xc382,0xc482,0xc582,0xc682,0xc782, + 0xc88a,0xc98a,0xca8a,0xcb8a,0xcc8a,0xcd8a,0xce8a,0xcf8a, + 0xd082,0xd182,0xd282,0xd382,0xd482,0xd582,0xd682,0xd782, + 0xd88a,0xd98a,0xda8a,0xdb8a,0xdc8a,0xdd8a,0xde8a,0xdf8a, + 0xe0a2,0xe1a2,0xe2a2,0xe3a2,0xe4a2,0xe5a2,0xe6a2,0xe7a2, + 0xe8aa,0xe9aa,0xeaaa,0xebaa,0xecaa,0xedaa,0xeeaa,0xefaa, + 0xf0a2,0xf1a2,0xf2a2,0xf3a2,0xf4a2,0xf5a2,0xf6a2,0xf7a2, + 0xf8aa,0xf9aa,0xfaaa,0xfbaa,0xfcaa,0xfdaa,0xfeaa,0xffaa, +}; + +/* andTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i], i = 0..255 */ +static const uint16 andTable[256] = { + 0x0054,0x0110,0x0210,0x0314,0x0410,0x0514,0x0614,0x0710, + 0x0818,0x091c,0x0a1c,0x0b18,0x0c1c,0x0d18,0x0e18,0x0f1c, + 0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1610,0x1714, + 0x181c,0x1918,0x1a18,0x1b1c,0x1c18,0x1d1c,0x1e1c,0x1f18, + 0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2630,0x2734, + 0x283c,0x2938,0x2a38,0x2b3c,0x2c38,0x2d3c,0x2e3c,0x2f38, + 0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3634,0x3730, + 0x3838,0x393c,0x3a3c,0x3b38,0x3c3c,0x3d38,0x3e38,0x3f3c, + 0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4610,0x4714, + 0x481c,0x4918,0x4a18,0x4b1c,0x4c18,0x4d1c,0x4e1c,0x4f18, + 0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,0x5614,0x5710, + 0x5818,0x591c,0x5a1c,0x5b18,0x5c1c,0x5d18,0x5e18,0x5f1c, + 0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6634,0x6730, + 0x6838,0x693c,0x6a3c,0x6b38,0x6c3c,0x6d38,0x6e38,0x6f3c, + 0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7630,0x7734, + 0x783c,0x7938,0x7a38,0x7b3c,0x7c38,0x7d3c,0x7e3c,0x7f38, + 0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8690,0x8794, + 0x889c,0x8998,0x8a98,0x8b9c,0x8c98,0x8d9c,0x8e9c,0x8f98, + 0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9694,0x9790, + 0x9898,0x999c,0x9a9c,0x9b98,0x9c9c,0x9d98,0x9e98,0x9f9c, + 0xa0b4,0xa1b0,0xa2b0,0xa3b4,0xa4b0,0xa5b4,0xa6b4,0xa7b0, + 0xa8b8,0xa9bc,0xaabc,0xabb8,0xacbc,0xadb8,0xaeb8,0xafbc, + 0xb0b0,0xb1b4,0xb2b4,0xb3b0,0xb4b4,0xb5b0,0xb6b0,0xb7b4, + 0xb8bc,0xb9b8,0xbab8,0xbbbc,0xbcb8,0xbdbc,0xbebc,0xbfb8, + 0xc094,0xc190,0xc290,0xc394,0xc490,0xc594,0xc694,0xc790, + 0xc898,0xc99c,0xca9c,0xcb98,0xcc9c,0xcd98,0xce98,0xcf9c, + 0xd090,0xd194,0xd294,0xd390,0xd494,0xd590,0xd690,0xd794, + 0xd89c,0xd998,0xda98,0xdb9c,0xdc98,0xdd9c,0xde9c,0xdf98, + 0xe0b0,0xe1b4,0xe2b4,0xe3b0,0xe4b4,0xe5b0,0xe6b0,0xe7b4, + 0xe8bc,0xe9b8,0xeab8,0xebbc,0xecb8,0xedbc,0xeebc,0xefb8, + 0xf0b4,0xf1b0,0xf2b0,0xf3b4,0xf4b0,0xf5b4,0xf6b4,0xf7b0, + 0xf8b8,0xf9bc,0xfabc,0xfbb8,0xfcbc,0xfdb8,0xfeb8,0xffbc, +}; + +/* xororTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 xororTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* rotateShiftTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff], i = 0..255 */ +static const uint8 rotateShiftTable[256] = { + 68, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 4, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, +}; + +/* incZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2), i = 0..256 */ +static const uint8 incZ80Table[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 148,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80, +}; + +/* decZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2, i = 0..255 */ +static const uint8 decZ80Table[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 62, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsZ80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsZ80Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsZ80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | + ((i >> 8) & 1) | (i & 0xa8), i = 0..511 */ +static const uint8 cbitsZ80DupTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, +}; + +/* cbits2Z80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Z80Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* cbits2Z80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | + (i & 0xa8), i = 0..511 */ +static const uint8 cbits2Z80DupTable[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, +}; + +/* negTable[i] = (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0), i = 0..255 */ +static const uint8 negTable[256] = { + 2,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 7,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrdrldTable[i] = (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 rrdrldTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* cpTable[i] = (i & 0x80) | (((i & 0xff) == 0) << 6), i = 0..255 */ +static const uint8 cpTable[256] = { + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +}; + +/* remove comments to generate table contents and define globally NEED_SIM_VM_INIT +static void altairz80_init(void); +void (*sim_vm_init) (void) = &altairz80_init; +static void altairz80_init(void) { +*/ +/* parityTable */ +/* + uint32 i, v; + for (i = 0; i < 256; i++) { + v = ((i & 1) + ((i & 2) >> 1) + ((i & 4) >> 2) + ((i & 8) >> 3) + + ((i & 16) >> 4) + ((i & 32) >> 5) + ((i & 64) >> 6) + ((i & 128) >> 7)) % 2 ? 0 : 4; + printf("%1d,", v); + if ( ((i+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* incTable */ +/* + uint32 temp, v; + for (temp = 0; temp <= 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0) << 4); + printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* decTable */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | (((temp & 0xf) == 0xf) << 4) | 2; + printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* cbitsTable */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1); + printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* cbitsDup8Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1) | ((cbits & 0xff) << 8) | (cbits & 0xa8) | (((cbits & 0xff) == 0) << 6); + printf("0x%04x,", v); + if ( ((cbits+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* cbitsDup16Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1) | (cbits & 0x28); + printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* cbits2Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | ((cbits >> 8) & 1) | 2; + printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* rrcaTable */ +/* + uint32 temp, sum, v; + for (temp = 0; temp < 256; temp++) { + sum = temp >> 1; + v = ((temp & 1) << 15) | (sum << 8) | (sum & 0x28) | (temp & 1); + printf("0x%04x,", v); + if ( ((temp+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* rraTable */ +/* + uint32 temp, sum, v; + for (temp = 0; temp < 256; temp++) { + sum = temp >> 1; + v = (sum << 8) | (sum & 0x28) | (temp & 1); + printf("0x%04x,", v); + if ( ((temp+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* addTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 512; sum++) { + v = ((sum & 0xff) << 8) | (sum & 0xa8) | (((sum & 0xff) == 0) << 6); + printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* subTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = ((sum & 0xff) << 8) | (sum & 0xa8) | (((sum & 0xff) == 0) << 6) | 2; + printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* andTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | 0x10 | parityTable[sum]; + printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* xororTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = (sum << 8) | (sum & 0xa8) | ((sum == 0) << 6) | parityTable[sum]; + printf("0x%04x,", v); + if ( ((sum+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* rotateShiftTable */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | PARITY(temp); + printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* incZ80Table */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0) << 4) | ((temp == 0x80) << 2); + printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* decZ80Table */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (temp & 0xa8) | (((temp & 0xff) == 0) << 6) | + (((temp & 0xf) == 0xf) << 4) | ((temp == 0x7f) << 2) | 2; + printf("%3d,", v); + if ( ((temp+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* cbitsZ80Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1); + printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* cbitsZ80DupTable */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (cbits & 0x10) | (((cbits >> 6) ^ (cbits >> 5)) & 4) | + ((cbits >> 8) & 1) | (cbits & 0xa8); + printf("%3d,", v); + if ( ((cbits+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* cbits2Z80Table */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (((cbits >> 6) ^ (cbits >> 5)) & 4) | (cbits & 0x10) | 2 | ((cbits >> 8) & 1); + printf("%2d,", v); + if ( ((cbits+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* cbits2Z80DupTable */ +/* + uint32 cbits, v; + for (cbits = 0; cbits < 512; cbits++) { + v = (((cbits >> 6) ^ (cbits >> 5)) & 4) | (cbits & 0x10) | 2 | ((cbits >> 8) & 1) | + (cbits & 0xa8); + printf("%3d,", v); + if ( ((cbits+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* negTable */ +/* + uint32 temp, v; + for (temp = 0; temp < 256; temp++) { + v = (((temp & 0x0f) != 0) << 4) | ((temp == 0x80) << 2) | 2 | (temp != 0); + printf("%2d,", v); + if ( ((temp+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* rrdrldTable */ +/* + uint32 acu, v; + for (acu = 0; acu < 256; acu++) { + v = (acu << 8) | (acu & 0xa8) | (((acu & 0xff) == 0) << 6) | parityTable[acu]; + printf("0x%04x,", v); + if ( ((acu+1) & 0x7) == 0) { + printf("\n"); + } + } +*/ +/* cpTable */ +/* + uint32 sum, v; + for (sum = 0; sum < 256; sum++) { + v = (sum & 0x80) | (((sum & 0xff) == 0) << 6); + printf("%3d,", v); + if ( ((sum+1) & 0xf) == 0) { + printf("\n"); + } + } +*/ +/* remove comments to generate table contents +} +*/ + +/* Memory management */ + +#define LOG2PAGESIZE 8 +#define PAGESIZE (1 << LOG2PAGESIZE) + +static uint8 M[MAXMEMORY]; /* RAM which is present */ + +struct mdev { /* Structure to describe a 2^LOG2PAGESIZE byte page of address space */ + /* There are four cases + isRAM isEmpty routine code + TRUE FALSE NULL W page is random access memory (RAM) + FALSE TRUE NULL U no memory at this location + FALSE FALSE NULL R page is read only memory (ROM) + FALSE FALSE not NULL M page is mapped to memory mapped I/O routine + other combinations are undefined! + */ + uint32 isRAM; + uint32 isEmpty; + int32 (*routine)(const int32, const int32, const int32); +}; + +typedef struct mdev MDEV; + +static MDEV ROM_PAGE = {FALSE, FALSE, NULL}; /* this makes a page ROM */ +static MDEV RAM_PAGE = {TRUE, FALSE, NULL}; /* this makes a page RAM */ +static MDEV EMPTY_PAGE = {FALSE, TRUE, NULL}; /* this is non-existing memory */ +static MDEV mmu_table[MAXMEMORY >> LOG2PAGESIZE]; + +/* Memory and I/O Resource Mapping and Unmapping routine. */ +uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap) { + uint32 page, i, addr; + if (resource_type == RESOURCE_TYPE_MEMORY) { + for (i = 0; i < (size >> LOG2PAGESIZE); i++) { + addr = (baseaddr & 0xfff00) + (i << LOG2PAGESIZE); + if ((cpu_unit.flags & UNIT_CPU_BANKED) && (addr < common)) + addr |= bankSelect << MAXBANKSIZELOG2; + page = addr >> LOG2PAGESIZE; + if (cpu_unit.flags & UNIT_CPU_VERBOSE) + printf("%s memory 0x%05x, handler=%p\n", unmap ? "Unmapping" : " Mapping", + addr, routine); + if (unmap) { + if (mmu_table[page].routine == routine) /* unmap only if it was mapped */ + if (MEMORYSIZE < MAXBANKSIZE) + if (addr < MEMORYSIZE) + mmu_table[page] = RAM_PAGE; + else + mmu_table[page] = EMPTY_PAGE; + else + mmu_table[page] = RAM_PAGE; + } + else { + mmu_table[page] = ROM_PAGE; + mmu_table[page].routine = routine; + } + } + } else if (resource_type == RESOURCE_TYPE_IO) { + for (i = baseaddr; i < baseaddr + size; i++) + if (unmap) { + if (dev_table[i & 0xff].routine == routine) { + if (cpu_unit.flags & UNIT_CPU_VERBOSE) + printf("Unmapping IO %04x, handler=%p\n", i, routine); + dev_table[i & 0xff].routine = &nulldev; + } + } + else { + if (cpu_unit.flags & UNIT_CPU_VERBOSE) + printf(" Mapping IO %04x, handler=%p\n", i, routine); + dev_table[i & 0xff].routine = routine; + } + } else { + printf("%s: cannot map unknown resource type %d\n", __FUNCTION__, resource_type); + return -1; + } + return 0; +} + +static void PutBYTE(register uint32 Addr, const register uint32 Value) { + MDEV m; + + Addr &= ADDRMASK; /* registers are NOT guaranteed to be always 16-bit values */ + if ((cpu_unit.flags & UNIT_CPU_BANKED) && (Addr < common)) + Addr |= bankSelect << MAXBANKSIZELOG2; + m = mmu_table[Addr >> LOG2PAGESIZE]; + + if (m.isRAM) M[Addr] = Value; + else if (m.routine) m.routine(Addr, 1, Value); + else if (cpu_unit.flags & UNIT_CPU_VERBOSE) { + if (m.isEmpty) { + MESSAGE_2("Attempt to write to non existing memory " ADDRESS_FORMAT ".", Addr); + } + else { + MESSAGE_2("Attempt to write to ROM " ADDRESS_FORMAT ".", Addr); + } + } +} + +void PutBYTEExtended(register uint32 Addr, const register uint32 Value) { + MDEV m; + + Addr &= ADDRMASKEXTENDED; + m = mmu_table[Addr >> LOG2PAGESIZE]; + + if (m.isRAM) M[Addr] = Value; + else if (m.routine) m.routine(Addr, 1, Value); + else if (cpu_unit.flags & UNIT_CPU_VERBOSE) { + if (m.isEmpty) { + MESSAGE_2("Attempt to write to non existing memory " ADDRESS_FORMAT ".", Addr); + } + else { + MESSAGE_2("Attempt to write to ROM " ADDRESS_FORMAT ".", Addr); + } + } +} + +static void PutWORD(register uint32 Addr, const register uint32 Value) { + PutBYTE(Addr, Value); + PutBYTE(Addr + 1, Value >> 8); +} + +static uint32 GetBYTE(register uint32 Addr) { + MDEV m; + + Addr &= ADDRMASK; /* registers are NOT guaranteed to be always 16-bit values */ + if ((cpu_unit.flags & UNIT_CPU_BANKED) && (Addr < common)) + Addr |= bankSelect << MAXBANKSIZELOG2; + m = mmu_table[Addr >> LOG2PAGESIZE]; + + if (m.isRAM) return M[Addr]; /* RAM */ + if (m.routine) return m.routine(Addr, 0, 0); /* memory mapped I/O */ + if (m.isEmpty) { + if (cpu_unit.flags & UNIT_CPU_VERBOSE) { + MESSAGE_2("Attempt to read from non existing memory " ADDRESS_FORMAT ".", Addr); + } + return 0xff; + } + return M[Addr]; /* ROM */ +} + +uint32 GetBYTEExtended(register uint32 Addr) { + MDEV m; + + Addr &= ADDRMASKEXTENDED; + m = mmu_table[Addr >> LOG2PAGESIZE]; + + if (m.isRAM) return M[Addr]; + if (m.routine) return m.routine(Addr, 0, 0); + if (m.isEmpty) { + if (cpu_unit.flags & UNIT_CPU_VERBOSE) { + MESSAGE_2("Attempt to read from non existing memory " ADDRESS_FORMAT ".", Addr); + } + return 0xff; + } + return M[Addr]; +} + +int32 getBankSelect(void) { + return bankSelect; +} + +void setBankSelect(const int32 b) { + bankSelect = b; +} + +uint32 getCommon(void) { + return common; +} + +/* memory access during a simulation */ +uint8 GetBYTEWrapper(const uint32 Addr) { + if (chiptype == CHIP_TYPE_8086) + return GetBYTEExtended(Addr); + else if (cpu_unit.flags & UNIT_CPU_MMU) + return GetBYTE(Addr); + else + return MOPT[Addr & ADDRMASK]; +} + +/* memory access during a simulation */ +void PutBYTEWrapper(const uint32 Addr, const uint32 Value) { + if (chiptype == CHIP_TYPE_8086) + PutBYTEExtended(Addr, Value); + else if (cpu_unit.flags & UNIT_CPU_MMU) + PutBYTE(Addr, Value); + else + MOPT[Addr & ADDRMASK] = Value & 0xff; +} + +#define RAM_PP(Addr) GetBYTE(Addr++) +#define RAM_MM(Addr) GetBYTE(Addr--) +#define GET_WORD(Addr) (GetBYTE(Addr) | (GetBYTE(Addr + 1) << 8)) +#define PUT_BYTE_PP(a,v) PutBYTE(a++, v) +#define PUT_BYTE_MM(a,v) PutBYTE(a--, v) +#define MM_PUT_BYTE(a,v) PutBYTE(--a, v) + +#define MASK_BRK (TRUE + 1) + +/* this is a modified version of sim_brk_test with two differences: + 1) is does not set sim_brk_pend to FALSE (this is left to the instruction decode) + 2) it returns MASK_BRK if a breakpoint is found but should be ignored +*/ +static int32 sim_brk_lookup (const t_addr loc, const int32 btyp) { + extern t_bool sim_brk_pend[SIM_BKPT_N_SPC]; + extern t_addr sim_brk_ploc[SIM_BKPT_N_SPC]; + extern char *sim_brk_act; + BRKTAB *bp; + if ((bp = sim_brk_fnd (loc)) && /* entry in table? */ + (btyp & bp -> typ) && /* type match? */ + (!sim_brk_pend[0] || (loc != sim_brk_ploc[0])) && /* new location? */ + (--(bp -> cnt) <= 0)) { /* count reach 0? */ + bp -> cnt = 0; /* reset count */ + sim_brk_ploc[0] = loc; /* save location */ + sim_brk_act = bp -> act; /* set up actions */ + sim_brk_pend[0] = TRUE; /* don't do twice */ + return TRUE; + } + return (sim_brk_pend[0] && (loc == sim_brk_ploc[0])) ? MASK_BRK : FALSE; +} + +static void prepareMemoryAccessMessage(t_addr loc) { + extern char memoryAccessMessage[]; + sprintf(memoryAccessMessage, "Memory access breakpoint [%04xh]", loc); +} + +#define PUSH(x) { \ + MM_PUT_BYTE(SP, (x) >> 8); \ + MM_PUT_BYTE(SP, x); \ +} + +#define CHECK_BREAK_BYTE(a) \ + if (sim_brk_summ && sim_brk_test((a) & 0xffff, SWMASK('M'))) { \ + reason = STOP_MEM; \ + prepareMemoryAccessMessage((a) & 0xffff); \ + goto end_decode; \ + } + +#define CHECK_BREAK_TWO_BYTES_EXTENDED(a1, a2, iCode) \ + if (sim_brk_summ) { \ + br1 = sim_brk_lookup((a1) & 0xffff, SWMASK('M')); \ + br2 = br1 ? FALSE : sim_brk_lookup((a2) & 0xffff, SWMASK('M'));\ + if ((br1 == MASK_BRK) || (br2 == MASK_BRK)) { \ + sim_brk_pend[0] = FALSE; \ + } \ + else if (br1 || br2) { \ + reason = STOP_MEM; \ + if (br1) { \ + prepareMemoryAccessMessage((a1) & 0xffff); \ + } \ + else { \ + prepareMemoryAccessMessage((a2) & 0xffff); \ + } \ + iCode; \ + goto end_decode; \ + } \ + else { \ + sim_brk_pend[0] = FALSE; \ + } \ + } + +#define CHECK_BREAK_TWO_BYTES(a1, a2) CHECK_BREAK_TWO_BYTES_EXTENDED(a1, a2,;) + +#define CHECK_BREAK_WORD(a) CHECK_BREAK_TWO_BYTES(a, (a + 1)) + +#define HALTINSTRUCTION 0x76 + +/* Macros for the IN/OUT instructions INI/INIR/IND/INDR/OUTI/OTIR/OUTD/OTDR + + Pre condition + temp == value of register B at entry of the instruction + acu == value of transferred byte (IN or OUT) + Post condition + F is set correctly + + Use INOUTFLAGS_ZERO(x) for INIR/INDR/OTIR/OTDR where + x == (C + 1) & 0xff for INIR + x == L for OTIR and OTDR + x == (C - 1) & 0xff for INDR + Use INOUTFLAGS_NONZERO(x) for INI/IND/OUTI/OUTD where + x == (C + 1) & 0xff for INI + x == L for OUTI and OUTD + x == (C - 1) & 0xff for IND +*/ +#define INOUTFLAGS(syxz, x) \ + AF = (AF & 0xff00) | (syxz) | /* SF, YF, XF, ZF */ \ + ((acu & 0x80) >> 6) | /* NF */ \ + ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ + parityTable[((acu + (x)) & 7) ^ temp] /* PF */ + +#define INOUTFLAGS_ZERO(x) INOUTFLAGS(FLAG_Z, x) +#define INOUTFLAGS_NONZERO(x) \ + INOUTFLAGS((HIGH_REGISTER(BC) & 0xa8) | ((HIGH_REGISTER(BC) == 0) << 6), x) + +t_stat sim_instr (void) { + uint32 i; + t_stat result; + if (chiptype == CHIP_TYPE_8086) return sim_instr_8086(); + if (cpu_unit.flags & UNIT_CPU_MMU) return sim_instr_mmu(); + for (i = 0; i < MAXBANKSIZE; i++) MOPT[i] = M[i]; + result = sim_instr_nommu(); + for (i = 0; i < MAXBANKSIZE; i++) M[i] = MOPT[i]; + return result; +} + +static t_stat sim_instr_mmu (void) { + extern int32 sim_interval; + extern t_bool sim_brk_pend[SIM_BKPT_N_SPC]; + extern int32 timerInterrupt; + extern int32 timerInterruptHandler; + extern int32 keyboardInterrupt; + extern uint32 keyboardInterruptHandler; + extern uint32 sim_os_msec(void); + extern const t_bool rtc_avail; + extern uint32 sim_brk_summ; + int32 reason = 0; + register uint32 specialProcessing; + register uint32 AF; + register uint32 BC; + register uint32 DE; + register uint32 HL; + register uint32 PC; + register uint32 SP; + register uint32 IX; + register uint32 IY; + register uint32 temp = 0; + register uint32 acu = 0; + register uint32 sum; + register uint32 cbits; + register uint32 op; + register uint32 adr; + /* tStates contains the number of t-states executed. One t-state is executed + in one microsecond on a 1MHz CPU. tStates is used for real-time simulations. */ + register uint32 tStates; + uint32 tStatesInSlice; /* number of t-states in 10 mSec time-slice */ + uint32 startTime, now; + int32 br1, br2, tStateModifier = FALSE; + + AF = AF_S; + BC = BC_S; + DE = DE_S; + HL = HL_S; + PC = PC_S & ADDRMASK; + SP = SP_S; + IX = IX_S; + IY = IY_S; + specialProcessing = clockFrequency | timerInterrupt | keyboardInterrupt | sim_brk_summ; + tStates = 0; + if (rtc_avail) { + startTime = sim_os_msec(); + tStatesInSlice = sliceLength*clockFrequency; + } + else { /* make sure that sim_os_msec() is not called later */ + clockFrequency = startTime = tStatesInSlice = 0; + } + + /* main instruction fetch/decode loop */ + while (TRUE) { /* loop until halted */ + if (sim_interval <= 0) { /* check clock queue */ +#if !UNIX_PLATFORM + if ((reason = sim_os_poll_kbd()) == SCPE_STOP) { /* poll on platforms without reliable signalling */ + break; + } +#endif + if ( (reason = sim_process_event()) ) break; + else + specialProcessing = clockFrequency | timerInterrupt | keyboardInterrupt | sim_brk_summ; + } + + if (specialProcessing) { /* quick check for special processing */ + if (clockFrequency && (tStates >= tStatesInSlice)) { + /* clockFrequency != 0 implies that real time clock is available */ + startTime += sliceLength; + tStates -= tStatesInSlice; + if (startTime > (now = sim_os_msec())) { +#if defined (_WIN32) + Sleep(startTime - now); +#else + usleep(1000 * (startTime - now)); +#endif + } + } + + if (timerInterrupt && (IFF_S & 1)) { + timerInterrupt = FALSE; + specialProcessing = clockFrequency | sim_brk_summ; + IFF_S = 0; /* disable interrupts */ + CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (timerInterrupt = TRUE, IFF_S |= 1)); + if ((GetBYTE(PC) == HALTINSTRUCTION) && ((cpu_unit.flags & UNIT_CPU_STOPONHALT) == 0)) { + PUSH(PC + 1); + PCQ_ENTRY(PC); + } + else { + PUSH(PC); + PCQ_ENTRY(PC - 1); + } + PC = timerInterruptHandler & ADDRMASK; + } + + if (keyboardInterrupt && (IFF_S & 1)) { + keyboardInterrupt = FALSE; + specialProcessing = clockFrequency | sim_brk_summ; + IFF_S = 0; /* disable interrupts */ + CHECK_BREAK_TWO_BYTES_EXTENDED(SP - 2, SP - 1, (keyboardInterrupt = TRUE, IFF_S |= 1)); + if ((GetBYTE(PC) == HALTINSTRUCTION) && ((cpu_unit.flags & UNIT_CPU_STOPONHALT) == 0)) { + PUSH(PC + 1); + PCQ_ENTRY(PC); + } + else { + PUSH(PC); + PCQ_ENTRY(PC - 1); + } + PC = keyboardInterruptHandler & ADDRMASK; + } + + if (sim_brk_summ) { + if (sim_brk_lookup(PC, SWMASK('E')) == TRUE) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + if (sim_brk_test(GetBYTE(PC), (1u << SIM_BKPT_V_SPC) | SWMASK('I'))) { /* instruction breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + } + } + + PCX = PC; + sim_interval--; + + /* make sure that each instructions properly sets sim_brk_pend: + 1) Either directly to FALSE if no memory access takes place or + 2) through a call to a Check... routine + */ + switch(RAM_PP(PC)) { + + case 0x00: /* NOP */ + tStates += 4; + sim_brk_pend[0] = FALSE; + break; + + case 0x01: /* LD BC,nnnn */ + tStates += 10; + sim_brk_pend[0] = FALSE; + BC = GET_WORD(PC); + PC += 2; + break; + + case 0x02: /* LD (BC),A */ + tStates += 7; + CHECK_BREAK_BYTE(BC) + PutBYTE(BC, HIGH_REGISTER(AF)); + break; + + case 0x03: /* INC BC */ + tStates += 6; + sim_brk_pend[0] = FALSE; + ++BC; + break; + + case 0x04: /* INC B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC += 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x05: /* DEC B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC -= 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x06: /* LD B,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x07: /* RLCA */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x08: /* EX AF,AF' */ + tStates += 4; + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + temp = AF; + AF = AF1_S; + AF1_S = temp; + break; + + case 0x09: /* ADD HL,BC */ + tStates += 11; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x0a: /* LD A,(BC) */ + tStates += 7; + CHECK_BREAK_BYTE(BC) + SET_HIGH_REGISTER(AF, GetBYTE(BC)); + break; + + case 0x0b: /* DEC BC */ + tStates += 6; + sim_brk_pend[0] = FALSE; + --BC; + break; + + case 0x0c: /* INC C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(BC) + 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x0d: /* DEC C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(BC) - 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x0e: /* LD C,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x0f: /* RRCA */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & 0xc4) | rrcaTable[HIGH_REGISTER(AF)]; + break; + + case 0x10: /* DJNZ dd */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + if ((BC -= 0x100) & 0xff00) { + PCQ_ENTRY(PCX); + PC += (int8) GetBYTE(PC) + 1; + tStates += 13; + } + else { + PC++; + tStates += 8; + } + break; + + case 0x11: /* LD DE,nnnn */ + tStates += 10; + sim_brk_pend[0] = FALSE; + DE = GET_WORD(PC); + PC += 2; + break; + + case 0x12: /* LD (DE),A */ + tStates += 7; + CHECK_BREAK_BYTE(DE) + PutBYTE(DE, HIGH_REGISTER(AF)); + break; + + case 0x13: /* INC DE */ + tStates += 6; + sim_brk_pend[0] = FALSE; + ++DE; + break; + + case 0x14: /* INC D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE += 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x15: /* DEC D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE -= 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x16: /* LD D,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x17: /* RLA */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x18: /* JR dd */ + tStates += 12; + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + PCQ_ENTRY(PCX); + PC += (int8) GetBYTE(PC) + 1; + break; + + case 0x19: /* ADD HL,DE */ + tStates += 11; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x1a: /* LD A,(DE) */ + tStates += 7; + CHECK_BREAK_BYTE(DE) + SET_HIGH_REGISTER(AF, GetBYTE(DE)); + break; + + case 0x1b: /* DEC DE */ + tStates += 6; + sim_brk_pend[0] = FALSE; + --DE; + break; + + case 0x1c: /* INC E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(DE) + 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x1d: /* DEC E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(DE) - 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x1e: /* LD E,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x1f: /* RRA */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = ((AF & 1) << 15) | (AF & 0xc4) | rraTable[HIGH_REGISTER(AF)]; + break; + + case 0x20: /* JR NZ,dd */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + if (TSTFLAG(Z)) { + PC++; + tStates += 7; + } + else { + PCQ_ENTRY(PCX); + PC += (int8) GetBYTE(PC) + 1; + tStates += 12; + } + break; + + case 0x21: /* LD HL,nnnn */ + tStates += 10; + sim_brk_pend[0] = FALSE; + HL = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),HL */ + tStates += 16; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PutWORD(temp, HL); + PC += 2; + break; + + case 0x23: /* INC HL */ + tStates += 6; + sim_brk_pend[0] = FALSE; + ++HL; + break; + + case 0x24: /* INC H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL += 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x25: /* DEC H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL -= 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x26: /* LD H,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x27: /* DAA */ + tStates += 4; + sim_brk_pend[0] = FALSE; + acu = HIGH_REGISTER(AF); + temp = LOW_DIGIT(acu); + cbits = TSTFLAG(C); + if (TSTFLAG(N)) { /* last operation was a subtract */ + int hd = cbits || acu > 0x99; + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + if (temp > 5) { + SETFLAG(H, 0); + } + acu -= 6; + acu &= 0xff; + } + if (hd) acu -= 0x160; /* adjust high digit */ + } + else { /* last operation was an add */ + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + SETFLAG(H, (temp > 9)); + acu += 6; + } + if (cbits || ((acu & 0x1f0) > 0x90)) acu += 0x60; /* adjust high digit */ + } + AF = (AF & 0x12) | rrdrldTable[acu & 0xff] | ((acu >> 8) & 1) | cbits; + break; + + case 0x28: /* JR Z,dd */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + if (TSTFLAG(Z)) { + PCQ_ENTRY(PCX); + PC += (int8) GetBYTE(PC) + 1; + tStates += 12; + } + else { + PC++; + tStates += 7; + } + break; + + case 0x29: /* ADD HL,HL */ + tStates += 11; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + sum = HL + HL; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + HL = sum; + break; + + case 0x2a: /* LD HL,(nnnn) */ + tStates += 16; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + HL = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC HL */ + tStates += 6; + sim_brk_pend[0] = FALSE; + --HL; + break; + + case 0x2c: /* INC L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(HL) + 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x2d: /* DEC L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(HL) - 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x2e: /* LD L,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x2f: /* CPL */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; + break; + + case 0x30: /* JR NC,dd */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + if (TSTFLAG(C)) { + PC++; + tStates += 7; + } + else { + PCQ_ENTRY(PCX); + PC += (int8) GetBYTE(PC) + 1; + tStates += 12; + } + break; + + case 0x31: /* LD SP,nnnn */ + tStates += 10; + sim_brk_pend[0] = FALSE; + SP = GET_WORD(PC); + PC += 2; + break; + + case 0x32: /* LD (nnnn),A */ + tStates += 13; + temp = GET_WORD(PC); + CHECK_BREAK_BYTE(temp); + PutBYTE(temp, HIGH_REGISTER(AF)); + PC += 2; + break; + + case 0x33: /* INC SP */ + tStates += 6; + sim_brk_pend[0] = FALSE; + ++SP; + break; + + case 0x34: /* INC (HL) */ + tStates += 11; + CHECK_BREAK_BYTE(HL); + temp = GetBYTE(HL) + 1; + PutBYTE(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x35: /* DEC (HL) */ + tStates += 11; + CHECK_BREAK_BYTE(HL); + temp = GetBYTE(HL) - 1; + PutBYTE(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x36: /* LD (HL),nn */ + tStates += 10; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, RAM_PP(PC)); + break; + + case 0x37: /* SCF */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | 1; + break; + + case 0x38: /* JR C,dd */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + if (TSTFLAG(C)) { + PCQ_ENTRY(PCX); + PC += (int8) GetBYTE(PC) + 1; + tStates += 12; + } + else { + PC++; + tStates += 7; + } + break; + + case 0x39: /* ADD HL,SP */ + tStates += 11; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x3a: /* LD A,(nnnn) */ + tStates += 13; + temp = GET_WORD(PC); + CHECK_BREAK_BYTE(temp); + SET_HIGH_REGISTER(AF, GetBYTE(temp)); + PC += 2; + break; + + case 0x3b: /* DEC SP */ + tStates += 6; + sim_brk_pend[0] = FALSE; + --SP; + break; + + case 0x3c: /* INC A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF += 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x3d: /* DEC A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF -= 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x3e: /* LD A,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(AF, RAM_PP(PC)); + break; + + case 0x3f: /* CCF */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | ((AF & 1) << 4) | (~AF & 1); + break; + + case 0x40: /* LD B,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x41: /* LD B,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x42: /* LD B,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & 0xff) | (DE & ~0xff); + break; + + case 0x43: /* LD B,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x44: /* LD B,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & 0xff) | (HL & ~0xff); + break; + + case 0x45: /* LD B,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x46: /* LD B,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(BC, GetBYTE(HL)); + break; + + case 0x47: /* LD B,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* LD C,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x49: /* LD C,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x4a: /* LD C,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x4b: /* LD C,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & ~0xff) | (DE & 0xff); + break; + + case 0x4c: /* LD C,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x4d: /* LD C,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & ~0xff) | (HL & 0xff); + break; + + case 0x4e: /* LD C,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + SET_LOW_REGISTER(BC, GetBYTE(HL)); + break; + + case 0x4f: /* LD C,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + BC = (BC & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* LD D,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & 0xff) | (BC & ~0xff); + break; + + case 0x51: /* LD D,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x52: /* LD D,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x53: /* LD D,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x54: /* LD D,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & 0xff) | (HL & ~0xff); + break; + + case 0x55: /* LD D,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x56: /* LD D,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(DE, GetBYTE(HL)); + break; + + case 0x57: /* LD D,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & 0xff) | (AF & ~0xff); + break; + + case 0x58: /* LD E,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x59: /* LD E,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & ~0xff) | (BC & 0xff); + break; + + case 0x5a: /* LD E,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x5b: /* LD E,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x5c: /* LD E,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x5d: /* LD E,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & ~0xff) | (HL & 0xff); + break; + + case 0x5e: /* LD E,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + SET_LOW_REGISTER(DE, GetBYTE(HL)); + break; + + case 0x5f: /* LD E,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + DE = (DE & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x60: /* LD H,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & 0xff) | (BC & ~0xff); + break; + + case 0x61: /* LD H,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x62: /* LD H,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & 0xff) | (DE & ~0xff); + break; + + case 0x63: /* LD H,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x64: /* LD H,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x65: /* LD H,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x66: /* LD H,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(HL, GetBYTE(HL)); + break; + + case 0x67: /* LD H,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & 0xff) | (AF & ~0xff); + break; + + case 0x68: /* LD L,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x69: /* LD L,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & ~0xff) | (BC & 0xff); + break; + + case 0x6a: /* LD L,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x6b: /* LD L,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & ~0xff) | (DE & 0xff); + break; + + case 0x6c: /* LD L,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x6d: /* LD L,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x6e: /* LD L,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + SET_LOW_REGISTER(HL, GetBYTE(HL)); + break; + + case 0x6f: /* LD L,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + HL = (HL & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x70: /* LD (HL),B */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (HL),C */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (HL),D */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (HL),E */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (HL),H */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (HL),L */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, LOW_REGISTER(HL)); + break; + + case HALTINSTRUCTION: /* HALT */ + tStates += 4; + sim_brk_pend[0] = FALSE; + PC--; + if (cpu_unit.flags & UNIT_CPU_STOPONHALT) { + reason = STOP_HALT; + goto end_decode; + } + sim_interval = 0; + do_SIMH_sleep(); /* reduce CPU load in busy wait */ + break; + + case 0x77: /* LD (HL),A */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + PutBYTE(HL, HIGH_REGISTER(AF)); + break; + + case 0x78: /* LD A,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & 0xff) | (BC & ~0xff); + break; + + case 0x79: /* LD A,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x7a: /* LD A,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & 0xff) | (DE & ~0xff); + break; + + case 0x7b: /* LD A,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x7c: /* LD A,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & 0xff) | (HL & ~0xff); + break; + + case 0x7d: /* LD A,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (AF & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x7e: /* LD A,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + SET_HIGH_REGISTER(AF, GetBYTE(HL)); + break; + + case 0x7f: /* LD A,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x80: /* ADD A,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x81: /* ADD A,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x82: /* ADD A,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x83: /* ADD A,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x84: /* ADD A,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x85: /* ADD A,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x86: /* ADD A,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + temp = GetBYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x87: /* ADD A,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + cbits = 2 * HIGH_REGISTER(AF); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x88: /* ADC A,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x89: /* ADC A,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8a: /* ADC A,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8b: /* ADC A,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8c: /* ADC A,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8d: /* ADC A,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8e: /* ADC A,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + temp = GetBYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8f: /* ADC A,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + cbits = 2 * HIGH_REGISTER(AF) + TSTFLAG(C); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x90: /* SUB B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x91: /* SUB C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x92: /* SUB D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x93: /* SUB E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x94: /* SUB H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x95: /* SUB L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x96: /* SUB (HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + temp = GetBYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x97: /* SUB A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = (chiptype == CHIP_TYPE_Z80) ? 0x42 : 0x46; + break; + + case 0x98: /* SBC A,B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x99: /* SBC A,C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9a: /* SBC A,D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9b: /* SBC A,E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9c: /* SBC A,H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9d: /* SBC A,L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9e: /* SBC A,(HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + temp = GetBYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9f: /* SBC A,A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + cbits = -TSTFLAG(C); + AF = subTable[cbits & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PVS(cbits)); + break; + + case 0xa0: /* AND B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF & BC) >> 8) & 0xff]; + break; + + case 0xa1: /* AND C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF >> 8) & BC) & 0xff]; + break; + + case 0xa2: /* AND D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF & DE) >> 8) & 0xff]; + break; + + case 0xa3: /* AND E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF >> 8) & DE) & 0xff]; + break; + + case 0xa4: /* AND H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF & HL) >> 8) & 0xff]; + break; + + case 0xa5: /* AND L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF >> 8) & HL) & 0xff]; + break; + + case 0xa6: /* AND (HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + AF = andTable[((AF >> 8) & GetBYTE(HL)) & 0xff]; + break; + + case 0xa7: /* AND A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = andTable[(AF >> 8) & 0xff]; + break; + + case 0xa8: /* XOR B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF ^ BC) >> 8) & 0xff]; + break; + + case 0xa9: /* XOR C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) ^ BC) & 0xff]; + break; + + case 0xaa: /* XOR D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF ^ DE) >> 8) & 0xff]; + break; + + case 0xab: /* XOR E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) ^ DE) & 0xff]; + break; + + case 0xac: /* XOR H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF ^ HL) >> 8) & 0xff]; + break; + + case 0xad: /* XOR L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) ^ HL) & 0xff]; + break; + + case 0xae: /* XOR (HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + AF = xororTable[((AF >> 8) ^ GetBYTE(HL)) & 0xff]; + break; + + case 0xaf: /* XOR A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = 0x44; + break; + + case 0xb0: /* OR B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF | BC) >> 8) & 0xff]; + break; + + case 0xb1: /* OR C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) | BC) & 0xff]; + break; + + case 0xb2: /* OR D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF | DE) >> 8) & 0xff]; + break; + + case 0xb3: /* OR E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) | DE) & 0xff]; + break; + + case 0xb4: /* OR H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF | HL) >> 8) & 0xff]; + break; + + case 0xb5: /* OR L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) | HL) & 0xff]; + break; + + case 0xb6: /* OR (HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + AF = xororTable[((AF >> 8) | GetBYTE(HL)) & 0xff]; + break; + + case 0xb7: /* OR A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + AF = xororTable[(AF >> 8) & 0xff]; + break; + + case 0xb8: /* CP B */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xb9: /* CP C */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xba: /* CP D */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbb: /* CP E */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbc: /* CP H */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbd: /* CP L */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbe: /* CP (HL) */ + tStates += 7; + CHECK_BREAK_BYTE(HL); + temp = GetBYTE(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbf: /* CP A */ + tStates += 4; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(AF, (HIGH_REGISTER(AF) & 0x28) | (chiptype == CHIP_TYPE_Z80 ? 0x42 : 0x46)); + break; + + case 0xc0: /* RET NZ */ + if (TSTFLAG(Z)) { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + else { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + break; + + case 0xc1: /* POP BC */ + tStates += 10; + CHECK_BREAK_WORD(SP); + POP(BC); + break; + + case 0xc2: /* JP NZ,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(!TSTFLAG(Z)); /* also updates tStates */ + break; + + case 0xc3: /* JP nnnn */ + sim_brk_pend[0] = FALSE; + JPC(1); /* also updates tStates */ + break; + + case 0xc4: /* CALL NZ,nnnn */ + CALLC(!TSTFLAG(Z)); /* also updates tStates */ + break; + + case 0xc5: /* PUSH BC */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(BC); + break; + + case 0xc6: /* ADD A,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xc7: /* RST 0 */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 0; + break; + + case 0xc8: /* RET Z */ + if (TSTFLAG(Z)) { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + else { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + break; + + case 0xc9: /* RET */ + tStates += 10; + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + break; + + case 0xca: /* JP Z,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(TSTFLAG(Z)); /* also updates tStates */ + break; + + case 0xcb: /* CB prefix */ + CHECK_CPU_8080; + adr = HL; + switch ((op = GetBYTE(PC)) & 7) { + + case 0: + sim_brk_pend[0] = tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(BC); + tStates += 8; + break; + + case 1: + sim_brk_pend[0] = tStateModifier = FALSE; + ++PC; + acu = LOW_REGISTER(BC); + tStates += 8; + break; + + case 2: + sim_brk_pend[0] = tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(DE); + tStates += 8; + break; + + case 3: + sim_brk_pend[0] = tStateModifier = FALSE; + ++PC; + acu = LOW_REGISTER(DE); + tStates += 8; + break; + + case 4: + sim_brk_pend[0] = tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(HL); + tStates += 8; + break; + + case 5: + sim_brk_pend[0] = tStateModifier = FALSE; + ++PC; + acu = LOW_REGISTER(HL); + tStates += 8; + break; + + case 6: + CHECK_BREAK_BYTE(adr); + ++PC; + acu = GetBYTE(adr); + tStateModifier = TRUE; + tStates += 15; + break; + + case 7: + sim_brk_pend[0] = tStateModifier = FALSE; + ++PC; + acu = HIGH_REGISTER(AF); + tStates += 8; + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg1; + + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg1; + + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg1; + + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg1; + + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg1; + + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg1: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ + } + break; + + case 0x40: /* BIT */ + if (tStateModifier) tStates -= 3; + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PutBYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xcc: /* CALL Z,nnnn */ + CALLC(TSTFLAG(Z)); /* also updates tStates */ + break; + + case 0xcd: /* CALL nnnn */ + CALLC(1); /* also updates tStates */ + break; + + case 0xce: /* ADC A,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xcf: /* RST 8 */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 8; + break; + + case 0xd0: /* RET NC */ + if (TSTFLAG(C)) { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + else { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + break; + + case 0xd1: /* POP DE */ + tStates += 10; + CHECK_BREAK_WORD(SP); + POP(DE); + break; + + case 0xd2: /* JP NC,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(!TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xd3: /* OUT (nn),A */ + tStates += 11; + sim_brk_pend[0] = FALSE; + out(RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0xd4: /* CALL NC,nnnn */ + CALLC(!TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xd5: /* PUSH DE */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(DE); + break; + + case 0xd6: /* SUB nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xd7: /* RST 10H */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 0x10; + break; + + case 0xd8: /* RET C */ + if (TSTFLAG(C)) { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + else { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + break; + + case 0xd9: /* EXX */ + tStates += 4; + sim_brk_pend[0] = FALSE; + CHECK_CPU_8080; + temp = BC; + BC = BC1_S; + BC1_S = temp; + temp = DE; + DE = DE1_S; + DE1_S = temp; + temp = HL; + HL = HL1_S; + HL1_S = temp; + break; + + case 0xda: /* JP C,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xdb: /* IN A,(nn) */ + tStates += 11; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(AF, in(RAM_PP(PC))); + break; + + case 0xdc: /* CALL C,nnnn */ + CALLC(TSTFLAG(C)); /* also updates tStates */ + break; + + case 0xdd: /* DD prefix */ + CHECK_CPU_8080; + switch (op = RAM_PP(PC)) { + + case 0x09: /* ADD IX,BC */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IX &= ADDRMASK; + BC &= ADDRMASK; + sum = IX + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ BC ^ sum) >> 8]; + IX = sum; + break; + + case 0x19: /* ADD IX,DE */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IX &= ADDRMASK; + DE &= ADDRMASK; + sum = IX + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ DE ^ sum) >> 8]; + IX = sum; + break; + + case 0x21: /* LD IX,nnnn */ + tStates += 14; + sim_brk_pend[0] = FALSE; + IX = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IX */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PutWORD(temp, IX); + PC += 2; + break; + + case 0x23: /* INC IX */ + tStates += 10; + sim_brk_pend[0] = FALSE; + ++IX; + break; + + case 0x24: /* INC IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + IX += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x25: /* DEC IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + IX -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x26: /* LD IXH,nn */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x29: /* ADD IX,IX */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IX &= ADDRMASK; + sum = IX + IX; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IX = sum; + break; + + case 0x2a: /* LD IX,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + IX = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC IX */ + tStates += 10; + sim_brk_pend[0] = FALSE; + --IX; + break; + + case 0x2c: /* INC IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IX) + 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IX) - 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IXL,nn */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x34: /* INC (IX+dd) */ + tStates += 23; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr) + 1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IX+dd) */ + tStates += 23; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr) - 1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IX+dd),nn */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IX,SP */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IX &= ADDRMASK; + SP &= ADDRMASK; + sum = IX + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ SP ^ sum) >> 8]; + IX = sum; + break; + + case 0x44: /* LD B,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x45: /* LD B,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x46: /* LD B,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(BC, GetBYTE(adr)); + break; + + case 0x4c: /* LD C,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x4d: /* LD C,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x4e: /* LD C,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(BC, GetBYTE(adr)); + break; + + case 0x54: /* LD D,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x55: /* LD D,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x56: /* LD D,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(DE, GetBYTE(adr)); + break; + + case 0x5c: /* LD E,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x5d: /* LD E,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x5e: /* LD E,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(DE, GetBYTE(adr)); + break; + + case 0x60: /* LD IXH,B */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IXH,C */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IXH,D */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IXH,E */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IXH,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x65: /* LD IXH,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IX, LOW_REGISTER(IX)); + break; + + case 0x66: /* LD H,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(HL, GetBYTE(adr)); + break; + + case 0x67: /* LD IXH,A */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IXL,B */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IXL,C */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IXL,D */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IXL,E */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IXL,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IX, HIGH_REGISTER(IX)); + break; + + case 0x6d: /* LD IXL,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x6e: /* LD L,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(HL, GetBYTE(adr)); + break; + + case 0x6f: /* LD IXL,A */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IX+dd),B */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IX+dd),C */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IX+dd),D */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IX+dd),E */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IX+dd),H */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IX+dd),L */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IX+dd),A */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IX)); + break; + + case 0x7d: /* LD A,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(AF, LOW_REGISTER(IX)); + break; + + case 0x7e: /* LD A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(AF, GetBYTE(adr)); + break; + + case 0x84: /* ADD A,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IXH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IXL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF & IX) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF >> 8) & IX) & 0xff]; + break; + + case 0xa6: /* AND (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = andTable[((AF >> 8) & GetBYTE(adr)) & 0xff]; + break; + + case 0xac: /* XOR IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF ^ IX) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) ^ IX) & 0xff]; + break; + + case 0xae: /* XOR (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) ^ GetBYTE(adr)) & 0xff]; + break; + + case 0xb4: /* OR IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF | IX) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) | IX) & 0xff]; + break; + + case 0xb6: /* OR (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) | GetBYTE(adr)) & 0xff]; + break; + + case 0xbc: /* CP IXH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IXL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IX+dd) */ + tStates += 19; + adr = IX + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IX + (int8) RAM_PP(PC); + switch ((op = GetBYTE(PC)) & 7) { + + case 0: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + sim_brk_pend[0] = FALSE; + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + sim_brk_pend[0] = FALSE; + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + sim_brk_pend[0] = FALSE; + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + CHECK_BREAK_BYTE(adr); + ++PC; + acu = GetBYTE(adr); + break; + + case 7: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + tStates += 23; + switch (op & 0x38) { + + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg2; + + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg2; + + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg2; + + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg2; + + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg2; + + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg2: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ + } + break; + + case 0x40: /* BIT */ + tStates += 20; + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + tStates += 23; + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + tStates += 23; + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PutBYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IX */ + tStates += 14; + CHECK_BREAK_WORD(SP); + POP(IX); + break; + + case 0xe3: /* EX (SP),IX */ + tStates += 23; + CHECK_BREAK_WORD(SP); + temp = IX; + POP(IX); + PUSH(temp); + break; + + case 0xe5: /* PUSH IX */ + tStates += 15; + CHECK_BREAK_WORD(SP - 2); + PUSH(IX); + break; + + case 0xe9: /* JP (IX) */ + tStates += 8; + sim_brk_pend[0] = FALSE; + PCQ_ENTRY(PCX); + PC = IX; + break; + + case 0xf9: /* LD SP,IX */ + tStates += 10; + sim_brk_pend[0] = FALSE; + SP = IX; + break; + + default: /* ignore DD */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_Z80; + PC--; + } + break; + + case 0xde: /* SBC A,nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xdf: /* RST 18H */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 0x18; + break; + + case 0xe0: /* RET PO */ + if (TSTFLAG(P)) { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + else { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + break; + + case 0xe1: /* POP HL */ + tStates += 10; + CHECK_BREAK_WORD(SP); + POP(HL); + break; + + case 0xe2: /* JP PO,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(!TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xe3: /* EX (SP),HL */ + tStates += 19; + CHECK_BREAK_WORD(SP); + temp = HL; + POP(HL); + PUSH(temp); + break; + + case 0xe4: /* CALL PO,nnnn */ + CALLC(!TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xe5: /* PUSH HL */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(HL); + break; + + case 0xe6: /* AND nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF >> 8) & RAM_PP(PC)) & 0xff]; + break; + + case 0xe7: /* RST 20H */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 0x20; + break; + + case 0xe8: /* RET PE */ + if (TSTFLAG(P)) { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + else { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + break; + + case 0xe9: /* JP (HL) */ + tStates += 4; + sim_brk_pend[0] = FALSE; + PCQ_ENTRY(PCX); + PC = HL; + break; + + case 0xea: /* JP PE,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xeb: /* EX DE,HL */ + tStates += 4; + sim_brk_pend[0] = FALSE; + temp = HL; + HL = DE; + DE = temp; + break; + + case 0xec: /* CALL PE,nnnn */ + CALLC(TSTFLAG(P)); /* also updates tStates */ + break; + + case 0xed: /* ED prefix */ + CHECK_CPU_8080; + switch (op = RAM_PP(PC)) { + + case 0x40: /* IN B,(C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x41: /* OUT (C),B */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), HIGH_REGISTER(BC)); + break; + + case 0x42: /* SBC HL,BC */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL - BC - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ BC ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x43: /* LD (nnnn),BC */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PutWORD(temp, BC); + PC += 2; + break; + + case 0x44: /* NEG */ + + case 0x4C: /* NEG, unofficial */ + + case 0x54: /* NEG, unofficial */ + + case 0x5C: /* NEG, unofficial */ + + case 0x64: /* NEG, unofficial */ + + case 0x6C: /* NEG, unofficial */ + + case 0x74: /* NEG, unofficial */ + + case 0x7C: /* NEG, unofficial */ + tStates += 8; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(AF); + AF = ((~(AF & 0xff00) + 1) & 0xff00); /* AF = (-(AF & 0xff00) & 0xff00); */ + AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | negTable[temp]; + break; + + case 0x45: /* RETN */ + + case 0x55: /* RETN, unofficial */ + + case 0x5D: /* RETN, unofficial */ + + case 0x65: /* RETN, unofficial */ + + case 0x6D: /* RETN, unofficial */ + + case 0x75: /* RETN, unofficial */ + + case 0x7D: /* RETN, unofficial */ + tStates += 14; + IFF_S |= IFF_S >> 1; + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + break; + + case 0x46: /* IM 0 */ + tStates += 8; + sim_brk_pend[0] = FALSE; + /* interrupt mode 0 */ + break; + + case 0x47: /* LD I,A */ + tStates += 9; + sim_brk_pend[0] = FALSE; + IR_S = (IR_S & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* IN C,(C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x49: /* OUT (C),C */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), LOW_REGISTER(BC)); + break; + + case 0x4a: /* ADC HL,BC */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x4b: /* LD BC,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + BC = GET_WORD(temp); + PC += 2; + break; + + case 0x4d: /* RETI */ + tStates += 14; + IFF_S |= IFF_S >> 1; + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + break; + + case 0x4f: /* LD R,A */ + tStates += 9; + sim_brk_pend[0] = FALSE; + IR_S = (IR_S & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* IN D,(C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x51: /* OUT (C),D */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), HIGH_REGISTER(DE)); + break; + + case 0x52: /* SBC HL,DE */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL - DE - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ DE ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x53: /* LD (nnnn),DE */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PutWORD(temp, DE); + PC += 2; + break; + + case 0x56: /* IM 1 */ + tStates += 8; + sim_brk_pend[0] = FALSE; + /* interrupt mode 1 */ + break; + + case 0x57: /* LD A,I */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = (AF & 0x29) | (IR_S & ~0xff) | ((IR_S >> 8) & 0x80) | (((IR_S & ~0xff) == 0) << 6) | ((IFF_S & 2) << 1); + break; + + case 0x58: /* IN E,(C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x59: /* OUT (C),E */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), LOW_REGISTER(DE)); + break; + + case 0x5a: /* ADC HL,DE */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x5b: /* LD DE,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + DE = GET_WORD(temp); + PC += 2; + break; + + case 0x5e: /* IM 2 */ + tStates += 8; + sim_brk_pend[0] = FALSE; + /* interrupt mode 2 */ + break; + + case 0x5f: /* LD A,R */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = (AF & 0x29) | ((IR_S & 0xff) << 8) | (IR_S & 0x80) | + (((IR_S & 0xff) == 0) << 6) | ((IFF_S & 2) << 1); + break; + + case 0x60: /* IN H,(C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x61: /* OUT (C),H */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), HIGH_REGISTER(HL)); + break; + + case 0x62: /* SBC HL,HL */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + sum = HL - HL - TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80DupTable[(sum >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x63: /* LD (nnnn),HL */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PutWORD(temp, HL); + PC += 2; + break; + + case 0x67: /* RRD */ + tStates += 18; + sim_brk_pend[0] = FALSE; + temp = GetBYTE(HL); + acu = HIGH_REGISTER(AF); + PutBYTE(HL, HIGH_DIGIT(temp) | (LOW_DIGIT(acu) << 4)); + AF = rrdrldTable[(acu & 0xf0) | LOW_DIGIT(temp)] | (AF & 1); + break; + + case 0x68: /* IN L,(C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x69: /* OUT (C),L */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), LOW_REGISTER(HL)); + break; + + case 0x6a: /* ADC HL,HL */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + sum = HL + HL + TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80DupTable[sum >> 8]; + HL = sum; + break; + + case 0x6b: /* LD HL,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + HL = GET_WORD(temp); + PC += 2; + break; + + case 0x6f: /* RLD */ + tStates += 18; + sim_brk_pend[0] = FALSE; + temp = GetBYTE(HL); + acu = HIGH_REGISTER(AF); + PutBYTE(HL, (LOW_DIGIT(temp) << 4) | LOW_DIGIT(acu)); + AF = rrdrldTable[(acu & 0xf0) | HIGH_DIGIT(temp)] | (AF & 1); + break; + + case 0x70: /* IN (C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(temp, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x71: /* OUT (C),0 */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), 0); + break; + + case 0x72: /* SBC HL,SP */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL - SP - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ SP ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x73: /* LD (nnnn),SP */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PutWORD(temp, SP); + PC += 2; + break; + + case 0x78: /* IN A,(C) */ + tStates += 12; + sim_brk_pend[0] = FALSE; + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(AF, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x79: /* OUT (C),A */ + tStates += 12; + sim_brk_pend[0] = FALSE; + out(LOW_REGISTER(BC), HIGH_REGISTER(AF)); + break; + + case 0x7a: /* ADC HL,SP */ + tStates += 15; + sim_brk_pend[0] = FALSE; + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x7b: /* LD SP,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + SP = GET_WORD(temp); + PC += 2; + break; + + case 0xa0: /* LDI */ + tStates += 16; + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa1: /* CPI */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = HIGH_REGISTER(AF); + temp = RAM_PP(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. So, first of all INI/INIR: + HF and CF Both set if ((HL) + ((C + 1) & 255) > 255) + PF The parity of (((HL) + ((C + 1) & 255)) & 7) xor B) */ + case 0xa2: /* INI */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = in(LOW_REGISTER(BC)); + PutBYTE(HL, acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + And now the for OUTI/OTIR/OUTD/OTDR instructions. Take state of the L + after the increment or decrement of HL; add the value written to the I/O port + to; call that k for now. If k > 255, then the CF and HF flags are set. The PF + flags is set like the parity of k bitwise and'ed with 7, bitwise xor'ed with B. + HF and CF Both set if ((HL) + L > 255) + PF The parity of ((((HL) + L) & 7) xor B) */ + case 0xa3: /* OUTI */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = GetBYTE(HL); + out(LOW_REGISTER(BC), acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xa8: /* LDD */ + tStates += 16; + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa9: /* CPD */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = HIGH_REGISTER(AF); + temp = RAM_MM(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. And last IND/INDR: + HF and CF Both set if ((HL) + ((C - 1) & 255) > 255) + PF The parity of (((HL) + ((C - 1) & 255)) & 7) xor B) */ + case 0xaa: /* IND */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = in(LOW_REGISTER(BC)); + PutBYTE(HL, acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xab: /* OUTD */ + tStates += 16; + CHECK_BREAK_BYTE(HL); + acu = GetBYTE(HL); + out(LOW_REGISTER(BC), acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xb0: /* LDIR */ + tStates -= 5; + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + tStates += 21; + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb1: /* CPIR */ + tStates -= 5; + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + tStates += 21; + CHECK_BREAK_BYTE(HL); + temp = RAM_PP(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + + case 0xb2: /* INIR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + tStates += 21; + CHECK_BREAK_BYTE(HL); + acu = in(LOW_REGISTER(BC)); + PutBYTE(HL, acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + + case 0xb3: /* OTIR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + tStates += 21; + CHECK_BREAK_BYTE(HL); + acu = GetBYTE(HL); + out(LOW_REGISTER(BC), acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + case 0xb8: /* LDDR */ + tStates -= 5; + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + tStates += 21; + CHECK_BREAK_TWO_BYTES(HL, DE); + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb9: /* CPDR */ + tStates -= 5; + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + tStates += 21; + CHECK_BREAK_BYTE(HL); + temp = RAM_MM(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + + case 0xba: /* INDR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + tStates += 21; + CHECK_BREAK_BYTE(HL); + acu = in(LOW_REGISTER(BC)); + PutBYTE(HL, acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xbb: /* OTDR */ + tStates -= 5; + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + tStates += 21; + CHECK_BREAK_BYTE(HL); + acu = GetBYTE(HL); + out(LOW_REGISTER(BC), acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + default: /* ignore ED and following byte */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_Z80; + } + break; + + case 0xee: /* XOR nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) ^ RAM_PP(PC)) & 0xff]; + break; + + case 0xef: /* RST 28H */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 0x28; + break; + + case 0xf0: /* RET P */ + if (TSTFLAG(S)) { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + else { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + break; + + case 0xf1: /* POP AF */ + tStates += 10; + CHECK_BREAK_WORD(SP); + POP(AF); + break; + + case 0xf2: /* JP P,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(!TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xf3: /* DI */ + tStates += 4; + sim_brk_pend[0] = FALSE; + IFF_S = 0; + break; + + case 0xf4: /* CALL P,nnnn */ + CALLC(!TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xf5: /* PUSH AF */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(AF); + break; + + case 0xf6: /* OR nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) | RAM_PP(PC)) & 0xff]; + break; + + case 0xf7: /* RST 30H */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 0x30; + break; + + case 0xf8: /* RET M */ + if (TSTFLAG(S)) { + CHECK_BREAK_WORD(SP); + PCQ_ENTRY(PCX); + POP(PC); + tStates += 11; + } + else { + sim_brk_pend[0] = FALSE; + tStates += 5; + } + break; + + case 0xf9: /* LD SP,HL */ + tStates += 6; + sim_brk_pend[0] = FALSE; + SP = HL; + break; + + case 0xfa: /* JP M,nnnn */ + sim_brk_pend[0] = FALSE; + JPC(TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xfb: /* EI */ + tStates += 4; + sim_brk_pend[0] = FALSE; + IFF_S = 3; + break; + + case 0xfc: /* CALL M,nnnn */ + CALLC(TSTFLAG(S)); /* also updates tStates */ + break; + + case 0xfd: /* FD prefix */ + CHECK_CPU_8080; + switch (op = RAM_PP(PC)) { + + case 0x09: /* ADD IY,BC */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IY &= ADDRMASK; + BC &= ADDRMASK; + sum = IY + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ BC ^ sum) >> 8]; + IY = sum; + break; + + case 0x19: /* ADD IY,DE */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IY &= ADDRMASK; + DE &= ADDRMASK; + sum = IY + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ DE ^ sum) >> 8]; + IY = sum; + break; + + case 0x21: /* LD IY,nnnn */ + tStates += 14; + sim_brk_pend[0] = FALSE; + IY = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IY */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + PutWORD(temp, IY); + PC += 2; + break; + + case 0x23: /* INC IY */ + tStates += 10; + sim_brk_pend[0] = FALSE; + ++IY; + break; + + case 0x24: /* INC IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + IY += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x25: /* DEC IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + IY -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x26: /* LD IYH,nn */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x29: /* ADD IY,IY */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IY &= ADDRMASK; + sum = IY + IY; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IY = sum; + break; + + case 0x2a: /* LD IY,(nnnn) */ + tStates += 20; + temp = GET_WORD(PC); + CHECK_BREAK_WORD(temp); + IY = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC IY */ + tStates += 10; + sim_brk_pend[0] = FALSE; + --IY; + break; + + case 0x2c: /* INC IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IY) + 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IY) - 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IYL,nn */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x34: /* INC (IY+dd) */ + tStates += 23; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr) + 1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IY+dd) */ + tStates += 23; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr) - 1; + PutBYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IY+dd),nn */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IY,SP */ + tStates += 15; + sim_brk_pend[0] = FALSE; + IY &= ADDRMASK; + SP &= ADDRMASK; + sum = IY + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ SP ^ sum) >> 8]; + IY = sum; + break; + + case 0x44: /* LD B,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x45: /* LD B,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x46: /* LD B,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(BC, GetBYTE(adr)); + break; + + case 0x4c: /* LD C,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x4d: /* LD C,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x4e: /* LD C,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(BC, GetBYTE(adr)); + break; + + case 0x54: /* LD D,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x55: /* LD D,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x56: /* LD D,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(DE, GetBYTE(adr)); + break; + + case 0x5c: /* LD E,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x5d: /* LD E,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x5e: /* LD E,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(DE, GetBYTE(adr)); + break; + + case 0x60: /* LD IYH,B */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IYH,C */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IYH,D */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IYH,E */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IYH,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x65: /* LD IYH,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IY, LOW_REGISTER(IY)); + break; + + case 0x66: /* LD H,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(HL, GetBYTE(adr)); + break; + + case 0x67: /* LD IYH,A */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IYL,B */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IYL,C */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IYL,D */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IYL,E */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IYL,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IY, HIGH_REGISTER(IY)); + break; + + case 0x6d: /* LD IYL,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; /* nop */ + break; + + case 0x6e: /* LD L,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_LOW_REGISTER(HL, GetBYTE(adr)); + break; + + case 0x6f: /* LD IYL,A */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_LOW_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IY+dd),B */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IY+dd),C */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IY+dd),D */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IY+dd),E */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IY+dd),H */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IY+dd),L */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IY+dd),A */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + PutBYTE(adr, HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IY)); + break; + + case 0x7d: /* LD A,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + SET_HIGH_REGISTER(AF, LOW_REGISTER(IY)); + break; + + case 0x7e: /* LD A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + SET_HIGH_REGISTER(AF, GetBYTE(adr)); + break; + + case 0x84: /* ADD A,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IYH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IYL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF & IY) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = andTable[((AF >> 8) & IY) & 0xff]; + break; + + case 0xa6: /* AND (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = andTable[((AF >> 8) & GetBYTE(adr)) & 0xff]; + break; + + case 0xac: /* XOR IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF ^ IY) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) ^ IY) & 0xff]; + break; + + case 0xae: /* XOR (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) ^ GetBYTE(adr)) & 0xff]; + break; + + case 0xb4: /* OR IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF | IY) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + AF = xororTable[((AF >> 8) | IY) & 0xff]; + break; + + case 0xb6: /* OR (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + AF = xororTable[((AF >> 8) | GetBYTE(adr)) & 0xff]; + break; + + case 0xbc: /* CP IYH */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = HIGH_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IYL */ + tStates += 9; + sim_brk_pend[0] = FALSE; + temp = LOW_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IY+dd) */ + tStates += 19; + adr = IY + (int8) RAM_PP(PC); + CHECK_BREAK_BYTE(adr); + temp = GetBYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IY + (int8) RAM_PP(PC); + switch ((op = GetBYTE(PC)) & 7) { + + case 0: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + sim_brk_pend[0] = FALSE; + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + sim_brk_pend[0] = FALSE; + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + sim_brk_pend[0] = FALSE; + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + CHECK_BREAK_BYTE(adr); + ++PC; + acu = GetBYTE(adr); + break; + + case 7: + sim_brk_pend[0] = FALSE; + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + tStates += 23; + switch (op & 0x38) { + + case 0x00: /* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg3; + + case 0x08: /* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg3; + + case 0x10: /* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg3; + + case 0x18: /* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg3; + + case 0x20: /* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x28: /* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg3; + + case 0x30: /* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x38: /* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg3: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + /* !!cbits == 0 if cbits == 0 !!cbits == 1 if cbits > 0 */ + } + break; + + case 0x40: /* BIT */ + tStates += 20; + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + tStates += 23; + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + tStates += 23; + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PutBYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IY */ + tStates += 14; + CHECK_BREAK_WORD(SP); + POP(IY); + break; + + case 0xe3: /* EX (SP),IY */ + tStates += 23; + CHECK_BREAK_WORD(SP); + temp = IY; + POP(IY); + PUSH(temp); + break; + + case 0xe5: /* PUSH IY */ + tStates += 15; + CHECK_BREAK_WORD(SP - 2); + PUSH(IY); + break; + + case 0xe9: /* JP (IY) */ + tStates += 8; + sim_brk_pend[0] = FALSE; + PCQ_ENTRY(PCX); + PC = IY; + break; + + case 0xf9: /* LD SP,IY */ + tStates += 10; + sim_brk_pend[0] = FALSE; + SP = IY; + break; + + default: /* ignore FD */ + sim_brk_pend[0] = FALSE; + CHECK_CPU_Z80; + PC--; + } + break; + + case 0xfe: /* CP nn */ + tStates += 7; + sim_brk_pend[0] = FALSE; + temp = RAM_PP(PC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xff: /* RST 38H */ + tStates += 11; + CHECK_BREAK_WORD(SP - 2); + PUSH(PC); + PCQ_ENTRY(PCX); + PC = 0x38; + } + } + end_decode: + + /* simulation halted */ + PC_S = ((reason == STOP_OPCODE) || (reason == STOP_MEM)) ? PCX : (PC & ADDRMASK); + pcq_r -> qptr = pcq_p; /* update pc q ptr */ + AF_S = AF; + BC_S = BC; + DE_S = DE; + HL_S = HL; + IX_S = IX; + IY_S = IY; + SP_S = SP; + executedTStates = tStates; + return reason; +} + +/* reset routine */ + +static t_stat cpu_reset(DEVICE *dptr) { + extern uint32 sim_brk_types, sim_brk_dflt; /* breakpoint info */ + int32 i; + AF_S = AF1_S = 0; + BC_S = DE_S = HL_S = 0; + BC1_S = DE1_S = HL1_S = 0; + IR_S = IX_S = IY_S = SP_S = 0; + IFF_S = 3; + setBankSelect(0); + cpu8086reset(); + sim_brk_types = (SWMASK('E') | SWMASK('I') | SWMASK('M')); + sim_brk_dflt = SWMASK('E'); + for (i = 0; i < PCQ_SIZE; i++) pcq[i] = 0; + pcq_p = 0; + pcq_r = find_reg("PCQ", NULL, dptr); + if (pcq_r) pcq_r -> qptr = 0; + else return SCPE_IERR; + return SCPE_OK; +} + +t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM) { + int32 i; + if (addr & (PAGESIZE - 1)) return SCPE_IERR; + for (i = 0; i < size; i++) { + if (makeROM && ((i & (PAGESIZE - 1)) == 0)) + mmu_table[(i + addr) >> LOG2PAGESIZE] = ROM_PAGE; + M[i + addr] = bootrom[i] & 0xff; + } + return SCPE_OK; +} + +/* memory examine */ +static t_stat cpu_ex(t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) { + int32 oldBankSelect; + if (chiptype == CHIP_TYPE_8086) *vptr = GetBYTEExtended(addr); + else { + oldBankSelect = getBankSelect(); + setBankSelect((addr >> MAXBANKSIZELOG2) & BANKMASK); + *vptr = GetBYTE(addr & ADDRMASK); + setBankSelect(oldBankSelect); + } + return SCPE_OK; +} + +/* memory deposit */ +static t_stat cpu_dep(t_value val, t_addr addr, UNIT *uptr, int32 sw) { + int32 oldBankSelect; + if (chiptype == CHIP_TYPE_8086) PutBYTEExtended(addr, val); + else { + oldBankSelect = getBankSelect(); + setBankSelect((addr >> MAXBANKSIZELOG2) & BANKMASK); + PutBYTE(addr & ADDRMASK, val); + setBankSelect(oldBankSelect); + } + return SCPE_OK; +} + +static t_stat chip_show(FILE *st, UNIT *uptr, int32 val, void *desc) { + fprintf(st, cpu_unit.flags & UNIT_CPU_OPSTOP ? "ITRAP, " : "NOITRAP, "); + if (chiptype == CHIP_TYPE_8080) fprintf(st, "8080"); + else if (chiptype == CHIP_TYPE_Z80) fprintf(st, "Z80"); + else if (chiptype == CHIP_TYPE_8086) fprintf(st, "8086"); + fprintf(st, ", "); + if (ramtype == 0) fprintf(st, "AZ80"); + else if (ramtype == 1) fprintf(st, "HRAM"); + else if (ramtype == 2) fprintf(st, "VRAM"); + else if (ramtype == 3) fprintf(st, "CRAM"); + return SCPE_OK; +} + +static t_stat cpu_show(FILE *st, UNIT *uptr, int32 val, void *desc) { + uint32 i, maxBanks; + MDEV m; + maxBanks = ((cpu_unit.flags & UNIT_CPU_BANKED) || + (chiptype == CHIP_TYPE_8086)) ? MAXBANKS : 1; + fprintf(st, "VERBOSE,\n "); + for (i = 0; i < 4; i++) fprintf(st, "0123456789ABCDEF"); + fprintf(st, " [16k]"); + for (i = 0; i < (maxBanks * (MAXBANKSIZE >> LOG2PAGESIZE)); i++) { + if ((i & 0x3f) == 0) fprintf(st, "\n%05X: ", (i << LOG2PAGESIZE)); + m = mmu_table[i]; + if (m.isRAM) fprintf(st, "W"); + else if (m.isEmpty) fprintf(st, "U"); + else if (m.routine) fprintf(st, "M"); + else fprintf(st, "R"); + } + return SCPE_OK; +} + +static void cpu_clear(void) { + uint32 i; + for (i = 0; i < MAXMEMORY; i++) M[i] = 0; + for (i = 0; i < (MAXMEMORY >> LOG2PAGESIZE); i++) mmu_table[i] = RAM_PAGE; + for (i = (MEMORYSIZE >> LOG2PAGESIZE); i < (MAXMEMORY >> LOG2PAGESIZE); i++) + mmu_table[i] = EMPTY_PAGE; + if (cpu_unit.flags & UNIT_CPU_ALTAIRROM) install_ALTAIRbootROM(); +} + +static t_stat cpu_clear_command(UNIT *uptr, int32 value, char *cptr, void *desc) { + cpu_clear(); + return SCPE_OK; +} + +static t_stat cpu_set_altairrom(UNIT *uptr, int32 value, char *cptr, void *desc) { + install_ALTAIRbootROM(); + return SCPE_OK; +} + +static t_stat cpu_set_noaltairrom(UNIT *uptr, int32 value, char *cptr, void *desc) { + mmu_table[ALTAIR_ROM_LOW >> LOG2PAGESIZE] = MEMORYSIZE < MAXBANKSIZE ? + EMPTY_PAGE : RAM_PAGE; + return SCPE_OK; +} + +static t_stat cpu_set_nommu(UNIT *uptr, int32 value, char *cptr, void *desc) { + if (chiptype == CHIP_TYPE_8086) { + printf("Cannot switch off MMU for 8086 CPU.\n"); + return SCPE_ARG; + } + if (cpu_unit.flags & UNIT_CPU_BANKED) { + printf("Cannot switch off MMU for banked memory.\n"); + return SCPE_ARG; + } + if (((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) && + (MEMORYSIZE < MAXBANKSIZE)) { + printf("Cannot switch off MMU when memory is %iKB < %iKB.\n", + MEMORYSIZE >> KBLOG2, MAXBANKSIZE >> KBLOG2); + return SCPE_ARG; + } + return SCPE_OK; +} + +static t_stat cpu_set_banked(UNIT *uptr, int32 value, char *cptr, void *desc) { + if ((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) { + if (MEMORYSIZE <= MAXBANKSIZE) previousCapacity = MEMORYSIZE; + MEMORYSIZE = MAXMEMORY; + cpu_dev.awidth = MAXBANKSIZELOG2 + MAXBANKSLOG2; + cpu_clear(); + } + else if (chiptype == CHIP_TYPE_8086) { + printf("Cannot use banked memory for 8086 CPU.\n"); + return SCPE_ARG; + } + return SCPE_OK; +} + +static t_stat cpu_set_nonbanked(UNIT *uptr, int32 value, char *cptr, void *desc) { + if ((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) { + MEMORYSIZE = previousCapacity; + cpu_dev.awidth = MAXBANKSIZELOG2; + cpu_clear(); + } + return SCPE_OK; +} + +static int32 bankseldev(const int32 port, const int32 io, const int32 data) { + if(io) { + switch(ramtype) { + case 1: + if(data & 0x40) { + printf("HRAM: Parity %s" NLP, data & 1 ? "ON" : "OFF"); + } else { + printf("HRAM BANKSEL=%02x" NLP, data); + } + break; + case 2: +/* printf("VRAM BANKSEL=%02x" NLP, data);*/ + switch(data & 0xFF) { + case 0x01: +/* case 0x41: // OASIS uses this for some reason?*/ + setBankSelect(0); + break; + case 0x02: +/* case 0x42: // OASIS uses this for some reason?*/ + setBankSelect(1); + break; + case 0x04: + setBankSelect(2); + break; + case 0x08: + setBankSelect(3); + break; + case 0x10: + setBankSelect(4); + break; + case 0x20: + setBankSelect(5); + break; + case 0x40: + setBankSelect(6); + break; + case 0x80: + setBankSelect(7); + break; + default: +/* printf("Invalid bank select 0x%02x for VRAM" NLP, data);*/ + break; + } + break; + case 3: + printf("CRAM BANKSEL=%02x" NLP, data); + break; + case 0: + default: + break; + } + return 0; + } else { + return(0xFF); + } +} + +static t_stat cpu_set_chiptype(UNIT *uptr, int32 value, char *cptr, void *desc) { + if (chiptype == value) return SCPE_OK; /* nothing to do */ + if ((chiptype == CHIP_TYPE_8080) && (value == CHIP_TYPE_Z80) || + (chiptype == CHIP_TYPE_Z80) && (value == CHIP_TYPE_8080)) { + chiptype = value; + return SCPE_OK; + } + chiptype = value; + if (chiptype == CHIP_TYPE_8086) { + if (MEMORYSIZE <= MAXBANKSIZE) previousCapacity = MEMORYSIZE; + MEMORYSIZE = MAXMEMORY; + cpu_unit.flags &= ~(UNIT_CPU_BANKED | UNIT_CPU_ALTAIRROM); + cpu_unit.flags |= UNIT_CPU_MMU; + cpu_dev.awidth = MAXBANKSIZELOG2 + MAXBANKSLOG2; + cpu_clear(); + } + else if ((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) { + MEMORYSIZE = previousCapacity; + cpu_dev.awidth = MAXBANKSIZELOG2; + cpu_clear(); + } + return SCPE_OK; +} + +#ifdef CPUSWITCHER +static int32 switchcpu_io(const int32 port, const int32 io, const int32 data) { + if (cpu_unit.flags & UNIT_CPU_VERBOSE) { + MESSAGE_5("SWITCH(port=%02x, io=%i, data=%04x(%i)", port, io, data, data); + } + return 0; +} + +static t_stat cpu_show_switcher(FILE *st, UNIT *uptr, int32 val, void *desc) { + if ((cpu_unit.flags & UNIT_CPU_SWITCHER) && (switcherPort >= 0)) + fprintf(st, "SWITCHER=0x%02x", switcherPort); + else fprintf(st, "NOSWITCHER"); + return SCPE_OK; +} + +static t_stat cpu_set_switcher(UNIT *uptr, int32 value, char *cptr, void *desc) { + struct idev safe; + switcherPort &= 0xff; + safe = dev_table[switcherPort]; + if (sim_map_resource(switcherPort, 1, RESOURCE_TYPE_IO, &switchcpu_io, FALSE)) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, switcherPort); + return SCPE_ARG; + } + oldSwitcherDevice = safe; + return SCPE_OK; +} + +static t_stat cpu_reset_switcher(UNIT *uptr, int32 value, char *cptr, void *desc) { + if (sim_map_resource(switcherPort, 1, RESOURCE_TYPE_IO, oldSwitcherDevice.routine, FALSE)) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, switcherPort); + return SCPE_ARG; + } + return SCPE_OK; +} +#endif + +static t_stat cpu_set_ramtype(UNIT *uptr, int32 value, char *cptr, void *desc) { + + if(value == ramtype) { + printf("RAM Selection unchanged\n"); + return SCPE_OK; + } + + switch(ramtype) { + case 1: + printf("Unmapping NorthStar HRAM\n"); + sim_map_resource(0xC0, 1, RESOURCE_TYPE_IO, &bankseldev, TRUE); + break; + case 2: + printf("Unmapping Vector RAM\n"); + sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, TRUE); + break; + case 3: + printf("Unmapping Cromemco RAM\n"); +/* sim_map_resource(0xC0, 1, RESOURCE_TYPE_IO, &bankseldev, TRUE);*/ + break; + case 0: + default: + printf("Unmapping AltairZ80 RAM\n"); + break; + } + + switch(value) { + case 1: + printf("NorthStar HRAM Selected\n"); + sim_map_resource(0xC0, 1, RESOURCE_TYPE_IO, &bankseldev, FALSE); + break; + case 2: + printf("Vector RAM Selected\n"); + sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &bankseldev, FALSE); + break; + case 3: + printf("Cromemco RAM Selected\n"); +/* sim_map_resource(0xC0, 1, RESOURCE_TYPE_IO, &bankseldev, FALSE);*/ + break; + case 0: + default: + printf("AltairZ80 RAM Selected\n"); + break; + } + + ramtype = value; + return SCPE_OK; +} + +/* set memory to 'size' kilo byte */ +static t_stat set_size(uint32 size) { + uint32 maxsize = (((chiptype == CHIP_TYPE_8080) || (chiptype == CHIP_TYPE_Z80)) && + ((cpu_unit.flags & UNIT_CPU_BANKED) == 0)) ? MAXBANKSIZE : MAXMEMORY; + size <<= KBLOG2; + if (cpu_unit.flags & UNIT_CPU_BANKED) size &= ~ADDRMASK; + cpu_unit.flags |= UNIT_CPU_MMU; + if (size < KB) MEMORYSIZE = KB; + else if (size > maxsize) MEMORYSIZE = maxsize; + else MEMORYSIZE = size; + cpu_dev.awidth = MAXBANKSIZELOG2; + if (size > MAXBANKSIZE) cpu_dev.awidth += MAXBANKSLOG2; + cpu_clear(); + return SCPE_OK; +} + +static t_stat cpu_set_size(UNIT *uptr, int32 value, char *cptr, void *desc) { + return set_size(value); +} + +static t_stat cpu_set_memory(UNIT *uptr, int32 value, char *cptr, void *desc) { + uint32 size, result, i; + if (cptr == NULL) return SCPE_ARG; + result = sscanf(cptr, "%i%n", &size, &i); + if ((result == 1) && (cptr[i] == 'K') && ((cptr[i + 1] == 0) || + (cptr[i + 1] == 'B') && (cptr[i + 2] == 0))) + return set_size(size); + return SCPE_ARG; +} + +/* AltairZ80 Simulator initialization */ +void altairz80_init(void) { + cpu_clear(); +} + +void (*sim_vm_init) (void) = &altairz80_init; + +/* This is the binary loader. The input file is considered to be a string of + literal bytes with no special format. The load starts at the current value + of the PC if no start address is given. If the input string ends with ROM + (not case sensitive) the memory area is made read only. + ALTAIRROM/NOALTAIRROM settings are ignored. +*/ + +#define PLURAL(x) (x), (x) == 1 ? "" : "s" + +t_stat sim_load(FILE *fileref, char *cptr, char *fnam, int32 flag) { + uint32 i, addr, cnt = 0, org, pagesModified = 0, makeROM = FALSE; + t_addr j, lo, hi; + char *result; + MDEV m; + char gbuf[CBUFSIZE]; + if (flag) { + result = (chiptype == CHIP_TYPE_8086) ? + get_range(NULL, cptr, &lo, &hi, 16, ADDRMASK | (BANKMASK << MAXBANKSIZELOG2), 0) : + get_range(NULL, cptr, &lo, &hi, 16, ADDRMASK, 0); + if (result == NULL) return SCPE_ARG; + for (j = lo; j <= hi; j++) { + if (putc((chiptype == CHIP_TYPE_8086) ? GetBYTEExtended(j) : GetBYTE(j), fileref) == EOF) return SCPE_IOERR; + } + printf("%d byte%s dumped [%x - %x].\n", PLURAL(hi + 1 - lo), lo, hi); + } + else { + if (*cptr == 0) addr = PC_S; + else { + get_glyph(cptr, gbuf, 0); + if (strcmp(gbuf, "ROM") == 0) { + addr = PC_S; + makeROM = TRUE; + } + else { + addr = strtotv(cptr, &result, 16); + if (cptr == result) return SCPE_ARG; + while (isspace(*result)) result++; + get_glyph(result, gbuf, 0); + if (strcmp(gbuf, "ROM") == 0) makeROM = TRUE; + } + } + /* addr is start address to load to, makeROM == TRUE iff memory should become ROM */ + org = addr; + while ((addr < MAXMEMORY) && ((i = getc(fileref)) != EOF)) { + m = mmu_table[addr >> LOG2PAGESIZE]; + if (!m.isRAM && m.isEmpty) { + mmu_table[addr >> LOG2PAGESIZE] = RAM_PAGE; + pagesModified++; + m = RAM_PAGE; + } + if (makeROM) { + mmu_table[addr >> LOG2PAGESIZE] = ROM_PAGE; + m = ROM_PAGE; + } + if (!m.isRAM && m.routine) m.routine(addr, 1, i); + else M[addr] = i; + addr++; + cnt++; + } /* end while */ + printf("%d byte%s [%d page%s] loaded at %x%s.\n", PLURAL(cnt), + PLURAL((cnt + 0xff) >> 8), org, makeROM ? " [ROM]" : ""); + if (pagesModified) + printf("Warning: %d page%s modified.\n", PLURAL(pagesModified)); + } + return SCPE_OK; +} diff --git a/AltairZ80/altairz80_cpu_nommu.c b/AltairZ80/altairz80_cpu_nommu.c new file mode 100644 index 0000000..67d92be --- /dev/null +++ b/AltairZ80/altairz80_cpu_nommu.c @@ -0,0 +1,4175 @@ +/* altairz80_cpu_opt.c: MITS Altair CPU (8080 and Z80) + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + + Based on work by Charles E Owen (c) 1997 + Code for Z80 CPU from Frank D. Cringle ((c) 1995 under GNU license) +*/ + +#include "altairz80_defs.h" + +#define FLAG_C 1 +#define FLAG_N 2 +#define FLAG_P 4 +#define FLAG_H 16 +#define FLAG_Z 64 +#define FLAG_S 128 + +#define SETFLAG(f,c) AF = (c) ? AF | FLAG_ ## f : AF & ~FLAG_ ## f +#define TSTFLAG(f) ((AF & FLAG_ ## f) != 0) + +#define LOW_DIGIT(x) ((x) & 0xf) +#define HIGH_DIGIT(x) (((x) >> 4) & 0xf) +#define LOW_REGISTER(x) ((x) & 0xff) +#define HIGH_REGISTER(x) (((x) >> 8) & 0xff) + +#define SET_LOW_REGISTER(x, v) x = (((x) & 0xff00) | ((v) & 0xff)) +#define SET_HIGH_REGISTER(x, v) x = (((x) & 0xff) | (((v) & 0xff) << 8)) + +#define PARITY(x) parityTable[(x) & 0xff] +/* SET_PV and SET_PV2 are used to provide correct PARITY flag semantics for the 8080 in cases + where the Z80 uses the overflow flag +*/ +#define SET_PVS(s) ((chiptype == CHIP_TYPE_Z80) ? (((cbits >> 6) ^ (cbits >> 5)) & 4) : (PARITY(s))) +#define SET_PV (SET_PVS(sum)) +#define SET_PV2(x) ((chiptype == CHIP_TYPE_Z80) ? (((temp == (x)) << 2)) : (PARITY(temp))) + +/* CHECK_CPU_8080 must be invoked whenever a Z80 only instruction is executed + In case a Z80 instruction is executed on an 8080 the following two cases exist: + 1) Trapping is enabled: execution stops + 2) Trapping is not enabled: decoding continues with the next byte +*/ +#define CHECK_CPU_8080 \ + if (chiptype == CHIP_TYPE_8080) { \ + if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ + reason = STOP_OPCODE; \ + goto end_decode; \ + } \ + else continue; \ + } + +/* CHECK_CPU_Z80 must be invoked whenever a non Z80 instruction is executed */ +#define CHECK_CPU_Z80 \ + if (cpu_unit.flags & UNIT_CPU_OPSTOP) { \ + reason = STOP_OPCODE; \ + goto end_decode; \ + } + +#define POP(x) { \ + register uint32 y = RAM_PP(SP); \ + x = y + (RAM_PP(SP) << 8); \ +} + +#define JPC(cond) { \ + if (cond) { \ + PC = GET_WORD(PC); \ + } \ + else { \ + PC += 2; \ + } \ +} + +#define CALLC(cond) { \ + if (cond) { \ + register uint32 adrr = GET_WORD(PC); \ + PUSH(PC + 2); \ + PC = adrr; \ + } \ + else { \ + PC += 2; \ + } \ +} + +/* function prototypes */ +t_stat sim_instr_nommu(void); + +extern void out(const uint32 Port, const uint32 Value); +extern uint32 in(const uint32 Port); +extern UNIT cpu_unit; +extern uint32 PCX; /* external view of PC */ +extern int32 AF_S; /* AF register */ +extern int32 BC_S; /* BC register */ +extern int32 DE_S; /* DE register */ +extern int32 HL_S; /* HL register */ +extern int32 IX_S; /* IX register */ +extern int32 IY_S; /* IY register */ +extern int32 PC_S; /* program counter */ +extern int32 SP_S; /* SP register */ +extern int32 AF1_S; /* alternate AF register */ +extern int32 BC1_S; /* alternate BC register */ +extern int32 DE1_S; /* alternate DE register */ +extern int32 HL1_S; /* alternate HL register */ +extern int32 IFF_S; /* Interrupt Flip Flop */ +extern int32 IR_S; /* Interrupt (upper) / Refresh (lower) register */ +extern int32 chiptype; + +/* the following tables precompute some common subexpressions + parityTable[i] 0..255 (number of 1's in i is odd) ? 0 : 4 + incTable[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4) + decTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2 + cbitsTable[i] 0..511 (i & 0x10) | ((i >> 8) & 1) + cbitsDup8Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | + (((i & 0xff) == 0) << 6) + cbitsDup16Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | (i & 0x28) + cbits2Table[i] 0..511 (i & 0x10) | ((i >> 8) & 1) | 2 + rrcaTable[i] 0..255 ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) + rraTable[i] 0..255 ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1) + addTable[i] 0..511 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) + subTable[i] 0..255 ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2 + andTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i] + xororTable[i] 0..255 (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i] + rotateShiftTable[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff] + incZ80Table[i] 0..256! (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2) + decZ80Table[i] 0..255 (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2 + cbitsZ80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) + cbitsZ80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | + ((i >> 8) & 1) | (i & 0xa8) + cbits2Z80Table[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 + cbits2Z80DupTable[i] 0..511 (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | + (i & 0xa8) + negTable[i] 0..255 (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0) + rrdrldTable[i] 0..255 (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i] + cpTable[i] 0..255 (i & 0x80) | (((i & 0xff) == 0) << 6) +*/ + +/* parityTable[i] = (number of 1's in i is odd) ? 0 : 4, i = 0..255 */ +static const uint8 parityTable[256] = { + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 0,4,4,0,4,0,0,4,4,0,0,4,0,4,4,0, + 4,0,0,4,0,4,4,0,0,4,4,0,4,0,0,4, +}; + +/* incTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0) << 4), i = 0..256 */ +static const uint8 incTable[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80 +}; + +/* decTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | (((i & 0xf) == 0xf) << 4) | 2, i = 0..255 */ +static const uint8 decTable[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsTable[i] = (i & 0x10) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsDup8Table[i] = (i & 0x10) | ((i >> 8) & 1) | ((i & 0xff) << 8) | (i & 0xa8) | + (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 cbitsDup8Table[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1010,0x1110,0x1210,0x1310,0x1410,0x1510,0x1610,0x1710, + 0x1818,0x1918,0x1a18,0x1b18,0x1c18,0x1d18,0x1e18,0x1f18, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3030,0x3130,0x3230,0x3330,0x3430,0x3530,0x3630,0x3730, + 0x3838,0x3938,0x3a38,0x3b38,0x3c38,0x3d38,0x3e38,0x3f38, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5010,0x5110,0x5210,0x5310,0x5410,0x5510,0x5610,0x5710, + 0x5818,0x5918,0x5a18,0x5b18,0x5c18,0x5d18,0x5e18,0x5f18, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7030,0x7130,0x7230,0x7330,0x7430,0x7530,0x7630,0x7730, + 0x7838,0x7938,0x7a38,0x7b38,0x7c38,0x7d38,0x7e38,0x7f38, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9090,0x9190,0x9290,0x9390,0x9490,0x9590,0x9690,0x9790, + 0x9898,0x9998,0x9a98,0x9b98,0x9c98,0x9d98,0x9e98,0x9f98, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0b0,0xb1b0,0xb2b0,0xb3b0,0xb4b0,0xb5b0,0xb6b0,0xb7b0, + 0xb8b8,0xb9b8,0xbab8,0xbbb8,0xbcb8,0xbdb8,0xbeb8,0xbfb8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd090,0xd190,0xd290,0xd390,0xd490,0xd590,0xd690,0xd790, + 0xd898,0xd998,0xda98,0xdb98,0xdc98,0xdd98,0xde98,0xdf98, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0b0,0xf1b0,0xf2b0,0xf3b0,0xf4b0,0xf5b0,0xf6b0,0xf7b0, + 0xf8b8,0xf9b8,0xfab8,0xfbb8,0xfcb8,0xfdb8,0xfeb8,0xffb8, + 0x0041,0x0101,0x0201,0x0301,0x0401,0x0501,0x0601,0x0701, + 0x0809,0x0909,0x0a09,0x0b09,0x0c09,0x0d09,0x0e09,0x0f09, + 0x1011,0x1111,0x1211,0x1311,0x1411,0x1511,0x1611,0x1711, + 0x1819,0x1919,0x1a19,0x1b19,0x1c19,0x1d19,0x1e19,0x1f19, + 0x2021,0x2121,0x2221,0x2321,0x2421,0x2521,0x2621,0x2721, + 0x2829,0x2929,0x2a29,0x2b29,0x2c29,0x2d29,0x2e29,0x2f29, + 0x3031,0x3131,0x3231,0x3331,0x3431,0x3531,0x3631,0x3731, + 0x3839,0x3939,0x3a39,0x3b39,0x3c39,0x3d39,0x3e39,0x3f39, + 0x4001,0x4101,0x4201,0x4301,0x4401,0x4501,0x4601,0x4701, + 0x4809,0x4909,0x4a09,0x4b09,0x4c09,0x4d09,0x4e09,0x4f09, + 0x5011,0x5111,0x5211,0x5311,0x5411,0x5511,0x5611,0x5711, + 0x5819,0x5919,0x5a19,0x5b19,0x5c19,0x5d19,0x5e19,0x5f19, + 0x6021,0x6121,0x6221,0x6321,0x6421,0x6521,0x6621,0x6721, + 0x6829,0x6929,0x6a29,0x6b29,0x6c29,0x6d29,0x6e29,0x6f29, + 0x7031,0x7131,0x7231,0x7331,0x7431,0x7531,0x7631,0x7731, + 0x7839,0x7939,0x7a39,0x7b39,0x7c39,0x7d39,0x7e39,0x7f39, + 0x8081,0x8181,0x8281,0x8381,0x8481,0x8581,0x8681,0x8781, + 0x8889,0x8989,0x8a89,0x8b89,0x8c89,0x8d89,0x8e89,0x8f89, + 0x9091,0x9191,0x9291,0x9391,0x9491,0x9591,0x9691,0x9791, + 0x9899,0x9999,0x9a99,0x9b99,0x9c99,0x9d99,0x9e99,0x9f99, + 0xa0a1,0xa1a1,0xa2a1,0xa3a1,0xa4a1,0xa5a1,0xa6a1,0xa7a1, + 0xa8a9,0xa9a9,0xaaa9,0xaba9,0xaca9,0xada9,0xaea9,0xafa9, + 0xb0b1,0xb1b1,0xb2b1,0xb3b1,0xb4b1,0xb5b1,0xb6b1,0xb7b1, + 0xb8b9,0xb9b9,0xbab9,0xbbb9,0xbcb9,0xbdb9,0xbeb9,0xbfb9, + 0xc081,0xc181,0xc281,0xc381,0xc481,0xc581,0xc681,0xc781, + 0xc889,0xc989,0xca89,0xcb89,0xcc89,0xcd89,0xce89,0xcf89, + 0xd091,0xd191,0xd291,0xd391,0xd491,0xd591,0xd691,0xd791, + 0xd899,0xd999,0xda99,0xdb99,0xdc99,0xdd99,0xde99,0xdf99, + 0xe0a1,0xe1a1,0xe2a1,0xe3a1,0xe4a1,0xe5a1,0xe6a1,0xe7a1, + 0xe8a9,0xe9a9,0xeaa9,0xeba9,0xeca9,0xeda9,0xeea9,0xefa9, + 0xf0b1,0xf1b1,0xf2b1,0xf3b1,0xf4b1,0xf5b1,0xf6b1,0xf7b1, + 0xf8b9,0xf9b9,0xfab9,0xfbb9,0xfcb9,0xfdb9,0xfeb9,0xffb9, +}; + +/* cbitsDup16Table[i] = (i & 0x10) | ((i >> 8) & 1) | (i & 0x28), i = 0..511 */ +static const uint8 cbitsDup16Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16,16,16,16,16,16,16,16,24,24,24,24,24,24,24,24, + 32,32,32,32,32,32,32,32,40,40,40,40,40,40,40,40, + 48,48,48,48,48,48,48,48,56,56,56,56,56,56,56,56, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, + 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, + 17,17,17,17,17,17,17,17,25,25,25,25,25,25,25,25, + 33,33,33,33,33,33,33,33,41,41,41,41,41,41,41,41, + 49,49,49,49,49,49,49,49,57,57,57,57,57,57,57,57, +}; + +/* cbits2Table[i] = (i & 0x10) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrcaTable[i] = ((i & 1) << 15) | ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rrcaTable[256] = { + 0x0000,0x8001,0x0100,0x8101,0x0200,0x8201,0x0300,0x8301, + 0x0400,0x8401,0x0500,0x8501,0x0600,0x8601,0x0700,0x8701, + 0x0808,0x8809,0x0908,0x8909,0x0a08,0x8a09,0x0b08,0x8b09, + 0x0c08,0x8c09,0x0d08,0x8d09,0x0e08,0x8e09,0x0f08,0x8f09, + 0x1000,0x9001,0x1100,0x9101,0x1200,0x9201,0x1300,0x9301, + 0x1400,0x9401,0x1500,0x9501,0x1600,0x9601,0x1700,0x9701, + 0x1808,0x9809,0x1908,0x9909,0x1a08,0x9a09,0x1b08,0x9b09, + 0x1c08,0x9c09,0x1d08,0x9d09,0x1e08,0x9e09,0x1f08,0x9f09, + 0x2020,0xa021,0x2120,0xa121,0x2220,0xa221,0x2320,0xa321, + 0x2420,0xa421,0x2520,0xa521,0x2620,0xa621,0x2720,0xa721, + 0x2828,0xa829,0x2928,0xa929,0x2a28,0xaa29,0x2b28,0xab29, + 0x2c28,0xac29,0x2d28,0xad29,0x2e28,0xae29,0x2f28,0xaf29, + 0x3020,0xb021,0x3120,0xb121,0x3220,0xb221,0x3320,0xb321, + 0x3420,0xb421,0x3520,0xb521,0x3620,0xb621,0x3720,0xb721, + 0x3828,0xb829,0x3928,0xb929,0x3a28,0xba29,0x3b28,0xbb29, + 0x3c28,0xbc29,0x3d28,0xbd29,0x3e28,0xbe29,0x3f28,0xbf29, + 0x4000,0xc001,0x4100,0xc101,0x4200,0xc201,0x4300,0xc301, + 0x4400,0xc401,0x4500,0xc501,0x4600,0xc601,0x4700,0xc701, + 0x4808,0xc809,0x4908,0xc909,0x4a08,0xca09,0x4b08,0xcb09, + 0x4c08,0xcc09,0x4d08,0xcd09,0x4e08,0xce09,0x4f08,0xcf09, + 0x5000,0xd001,0x5100,0xd101,0x5200,0xd201,0x5300,0xd301, + 0x5400,0xd401,0x5500,0xd501,0x5600,0xd601,0x5700,0xd701, + 0x5808,0xd809,0x5908,0xd909,0x5a08,0xda09,0x5b08,0xdb09, + 0x5c08,0xdc09,0x5d08,0xdd09,0x5e08,0xde09,0x5f08,0xdf09, + 0x6020,0xe021,0x6120,0xe121,0x6220,0xe221,0x6320,0xe321, + 0x6420,0xe421,0x6520,0xe521,0x6620,0xe621,0x6720,0xe721, + 0x6828,0xe829,0x6928,0xe929,0x6a28,0xea29,0x6b28,0xeb29, + 0x6c28,0xec29,0x6d28,0xed29,0x6e28,0xee29,0x6f28,0xef29, + 0x7020,0xf021,0x7120,0xf121,0x7220,0xf221,0x7320,0xf321, + 0x7420,0xf421,0x7520,0xf521,0x7620,0xf621,0x7720,0xf721, + 0x7828,0xf829,0x7928,0xf929,0x7a28,0xfa29,0x7b28,0xfb29, + 0x7c28,0xfc29,0x7d28,0xfd29,0x7e28,0xfe29,0x7f28,0xff29, +}; + +/* rraTable[i] = ((i >> 1) << 8) | ((i >> 1) & 0x28) | (i & 1), i = 0..255 */ +static const uint16 rraTable[256] = { + 0x0000,0x0001,0x0100,0x0101,0x0200,0x0201,0x0300,0x0301, + 0x0400,0x0401,0x0500,0x0501,0x0600,0x0601,0x0700,0x0701, + 0x0808,0x0809,0x0908,0x0909,0x0a08,0x0a09,0x0b08,0x0b09, + 0x0c08,0x0c09,0x0d08,0x0d09,0x0e08,0x0e09,0x0f08,0x0f09, + 0x1000,0x1001,0x1100,0x1101,0x1200,0x1201,0x1300,0x1301, + 0x1400,0x1401,0x1500,0x1501,0x1600,0x1601,0x1700,0x1701, + 0x1808,0x1809,0x1908,0x1909,0x1a08,0x1a09,0x1b08,0x1b09, + 0x1c08,0x1c09,0x1d08,0x1d09,0x1e08,0x1e09,0x1f08,0x1f09, + 0x2020,0x2021,0x2120,0x2121,0x2220,0x2221,0x2320,0x2321, + 0x2420,0x2421,0x2520,0x2521,0x2620,0x2621,0x2720,0x2721, + 0x2828,0x2829,0x2928,0x2929,0x2a28,0x2a29,0x2b28,0x2b29, + 0x2c28,0x2c29,0x2d28,0x2d29,0x2e28,0x2e29,0x2f28,0x2f29, + 0x3020,0x3021,0x3120,0x3121,0x3220,0x3221,0x3320,0x3321, + 0x3420,0x3421,0x3520,0x3521,0x3620,0x3621,0x3720,0x3721, + 0x3828,0x3829,0x3928,0x3929,0x3a28,0x3a29,0x3b28,0x3b29, + 0x3c28,0x3c29,0x3d28,0x3d29,0x3e28,0x3e29,0x3f28,0x3f29, + 0x4000,0x4001,0x4100,0x4101,0x4200,0x4201,0x4300,0x4301, + 0x4400,0x4401,0x4500,0x4501,0x4600,0x4601,0x4700,0x4701, + 0x4808,0x4809,0x4908,0x4909,0x4a08,0x4a09,0x4b08,0x4b09, + 0x4c08,0x4c09,0x4d08,0x4d09,0x4e08,0x4e09,0x4f08,0x4f09, + 0x5000,0x5001,0x5100,0x5101,0x5200,0x5201,0x5300,0x5301, + 0x5400,0x5401,0x5500,0x5501,0x5600,0x5601,0x5700,0x5701, + 0x5808,0x5809,0x5908,0x5909,0x5a08,0x5a09,0x5b08,0x5b09, + 0x5c08,0x5c09,0x5d08,0x5d09,0x5e08,0x5e09,0x5f08,0x5f09, + 0x6020,0x6021,0x6120,0x6121,0x6220,0x6221,0x6320,0x6321, + 0x6420,0x6421,0x6520,0x6521,0x6620,0x6621,0x6720,0x6721, + 0x6828,0x6829,0x6928,0x6929,0x6a28,0x6a29,0x6b28,0x6b29, + 0x6c28,0x6c29,0x6d28,0x6d29,0x6e28,0x6e29,0x6f28,0x6f29, + 0x7020,0x7021,0x7120,0x7121,0x7220,0x7221,0x7320,0x7321, + 0x7420,0x7421,0x7520,0x7521,0x7620,0x7621,0x7720,0x7721, + 0x7828,0x7829,0x7928,0x7929,0x7a28,0x7a29,0x7b28,0x7b29, + 0x7c28,0x7c29,0x7d28,0x7d29,0x7e28,0x7e29,0x7f28,0x7f29, +}; + +/* addTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6), i = 0..511 */ +static const uint16 addTable[512] = { + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, + 0x0040,0x0100,0x0200,0x0300,0x0400,0x0500,0x0600,0x0700, + 0x0808,0x0908,0x0a08,0x0b08,0x0c08,0x0d08,0x0e08,0x0f08, + 0x1000,0x1100,0x1200,0x1300,0x1400,0x1500,0x1600,0x1700, + 0x1808,0x1908,0x1a08,0x1b08,0x1c08,0x1d08,0x1e08,0x1f08, + 0x2020,0x2120,0x2220,0x2320,0x2420,0x2520,0x2620,0x2720, + 0x2828,0x2928,0x2a28,0x2b28,0x2c28,0x2d28,0x2e28,0x2f28, + 0x3020,0x3120,0x3220,0x3320,0x3420,0x3520,0x3620,0x3720, + 0x3828,0x3928,0x3a28,0x3b28,0x3c28,0x3d28,0x3e28,0x3f28, + 0x4000,0x4100,0x4200,0x4300,0x4400,0x4500,0x4600,0x4700, + 0x4808,0x4908,0x4a08,0x4b08,0x4c08,0x4d08,0x4e08,0x4f08, + 0x5000,0x5100,0x5200,0x5300,0x5400,0x5500,0x5600,0x5700, + 0x5808,0x5908,0x5a08,0x5b08,0x5c08,0x5d08,0x5e08,0x5f08, + 0x6020,0x6120,0x6220,0x6320,0x6420,0x6520,0x6620,0x6720, + 0x6828,0x6928,0x6a28,0x6b28,0x6c28,0x6d28,0x6e28,0x6f28, + 0x7020,0x7120,0x7220,0x7320,0x7420,0x7520,0x7620,0x7720, + 0x7828,0x7928,0x7a28,0x7b28,0x7c28,0x7d28,0x7e28,0x7f28, + 0x8080,0x8180,0x8280,0x8380,0x8480,0x8580,0x8680,0x8780, + 0x8888,0x8988,0x8a88,0x8b88,0x8c88,0x8d88,0x8e88,0x8f88, + 0x9080,0x9180,0x9280,0x9380,0x9480,0x9580,0x9680,0x9780, + 0x9888,0x9988,0x9a88,0x9b88,0x9c88,0x9d88,0x9e88,0x9f88, + 0xa0a0,0xa1a0,0xa2a0,0xa3a0,0xa4a0,0xa5a0,0xa6a0,0xa7a0, + 0xa8a8,0xa9a8,0xaaa8,0xaba8,0xaca8,0xada8,0xaea8,0xafa8, + 0xb0a0,0xb1a0,0xb2a0,0xb3a0,0xb4a0,0xb5a0,0xb6a0,0xb7a0, + 0xb8a8,0xb9a8,0xbaa8,0xbba8,0xbca8,0xbda8,0xbea8,0xbfa8, + 0xc080,0xc180,0xc280,0xc380,0xc480,0xc580,0xc680,0xc780, + 0xc888,0xc988,0xca88,0xcb88,0xcc88,0xcd88,0xce88,0xcf88, + 0xd080,0xd180,0xd280,0xd380,0xd480,0xd580,0xd680,0xd780, + 0xd888,0xd988,0xda88,0xdb88,0xdc88,0xdd88,0xde88,0xdf88, + 0xe0a0,0xe1a0,0xe2a0,0xe3a0,0xe4a0,0xe5a0,0xe6a0,0xe7a0, + 0xe8a8,0xe9a8,0xeaa8,0xeba8,0xeca8,0xeda8,0xeea8,0xefa8, + 0xf0a0,0xf1a0,0xf2a0,0xf3a0,0xf4a0,0xf5a0,0xf6a0,0xf7a0, + 0xf8a8,0xf9a8,0xfaa8,0xfba8,0xfca8,0xfda8,0xfea8,0xffa8, +}; + +/* subTable[i] = ((i & 0xff) << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | 2, i = 0..255 */ +static const uint16 subTable[256] = { + 0x0042,0x0102,0x0202,0x0302,0x0402,0x0502,0x0602,0x0702, + 0x080a,0x090a,0x0a0a,0x0b0a,0x0c0a,0x0d0a,0x0e0a,0x0f0a, + 0x1002,0x1102,0x1202,0x1302,0x1402,0x1502,0x1602,0x1702, + 0x180a,0x190a,0x1a0a,0x1b0a,0x1c0a,0x1d0a,0x1e0a,0x1f0a, + 0x2022,0x2122,0x2222,0x2322,0x2422,0x2522,0x2622,0x2722, + 0x282a,0x292a,0x2a2a,0x2b2a,0x2c2a,0x2d2a,0x2e2a,0x2f2a, + 0x3022,0x3122,0x3222,0x3322,0x3422,0x3522,0x3622,0x3722, + 0x382a,0x392a,0x3a2a,0x3b2a,0x3c2a,0x3d2a,0x3e2a,0x3f2a, + 0x4002,0x4102,0x4202,0x4302,0x4402,0x4502,0x4602,0x4702, + 0x480a,0x490a,0x4a0a,0x4b0a,0x4c0a,0x4d0a,0x4e0a,0x4f0a, + 0x5002,0x5102,0x5202,0x5302,0x5402,0x5502,0x5602,0x5702, + 0x580a,0x590a,0x5a0a,0x5b0a,0x5c0a,0x5d0a,0x5e0a,0x5f0a, + 0x6022,0x6122,0x6222,0x6322,0x6422,0x6522,0x6622,0x6722, + 0x682a,0x692a,0x6a2a,0x6b2a,0x6c2a,0x6d2a,0x6e2a,0x6f2a, + 0x7022,0x7122,0x7222,0x7322,0x7422,0x7522,0x7622,0x7722, + 0x782a,0x792a,0x7a2a,0x7b2a,0x7c2a,0x7d2a,0x7e2a,0x7f2a, + 0x8082,0x8182,0x8282,0x8382,0x8482,0x8582,0x8682,0x8782, + 0x888a,0x898a,0x8a8a,0x8b8a,0x8c8a,0x8d8a,0x8e8a,0x8f8a, + 0x9082,0x9182,0x9282,0x9382,0x9482,0x9582,0x9682,0x9782, + 0x988a,0x998a,0x9a8a,0x9b8a,0x9c8a,0x9d8a,0x9e8a,0x9f8a, + 0xa0a2,0xa1a2,0xa2a2,0xa3a2,0xa4a2,0xa5a2,0xa6a2,0xa7a2, + 0xa8aa,0xa9aa,0xaaaa,0xabaa,0xacaa,0xadaa,0xaeaa,0xafaa, + 0xb0a2,0xb1a2,0xb2a2,0xb3a2,0xb4a2,0xb5a2,0xb6a2,0xb7a2, + 0xb8aa,0xb9aa,0xbaaa,0xbbaa,0xbcaa,0xbdaa,0xbeaa,0xbfaa, + 0xc082,0xc182,0xc282,0xc382,0xc482,0xc582,0xc682,0xc782, + 0xc88a,0xc98a,0xca8a,0xcb8a,0xcc8a,0xcd8a,0xce8a,0xcf8a, + 0xd082,0xd182,0xd282,0xd382,0xd482,0xd582,0xd682,0xd782, + 0xd88a,0xd98a,0xda8a,0xdb8a,0xdc8a,0xdd8a,0xde8a,0xdf8a, + 0xe0a2,0xe1a2,0xe2a2,0xe3a2,0xe4a2,0xe5a2,0xe6a2,0xe7a2, + 0xe8aa,0xe9aa,0xeaaa,0xebaa,0xecaa,0xedaa,0xeeaa,0xefaa, + 0xf0a2,0xf1a2,0xf2a2,0xf3a2,0xf4a2,0xf5a2,0xf6a2,0xf7a2, + 0xf8aa,0xf9aa,0xfaaa,0xfbaa,0xfcaa,0xfdaa,0xfeaa,0xffaa, +}; + +/* andTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | 0x10 | parityTable[i], i = 0..255 */ +static const uint16 andTable[256] = { + 0x0054,0x0110,0x0210,0x0314,0x0410,0x0514,0x0614,0x0710, + 0x0818,0x091c,0x0a1c,0x0b18,0x0c1c,0x0d18,0x0e18,0x0f1c, + 0x1010,0x1114,0x1214,0x1310,0x1414,0x1510,0x1610,0x1714, + 0x181c,0x1918,0x1a18,0x1b1c,0x1c18,0x1d1c,0x1e1c,0x1f18, + 0x2030,0x2134,0x2234,0x2330,0x2434,0x2530,0x2630,0x2734, + 0x283c,0x2938,0x2a38,0x2b3c,0x2c38,0x2d3c,0x2e3c,0x2f38, + 0x3034,0x3130,0x3230,0x3334,0x3430,0x3534,0x3634,0x3730, + 0x3838,0x393c,0x3a3c,0x3b38,0x3c3c,0x3d38,0x3e38,0x3f3c, + 0x4010,0x4114,0x4214,0x4310,0x4414,0x4510,0x4610,0x4714, + 0x481c,0x4918,0x4a18,0x4b1c,0x4c18,0x4d1c,0x4e1c,0x4f18, + 0x5014,0x5110,0x5210,0x5314,0x5410,0x5514,0x5614,0x5710, + 0x5818,0x591c,0x5a1c,0x5b18,0x5c1c,0x5d18,0x5e18,0x5f1c, + 0x6034,0x6130,0x6230,0x6334,0x6430,0x6534,0x6634,0x6730, + 0x6838,0x693c,0x6a3c,0x6b38,0x6c3c,0x6d38,0x6e38,0x6f3c, + 0x7030,0x7134,0x7234,0x7330,0x7434,0x7530,0x7630,0x7734, + 0x783c,0x7938,0x7a38,0x7b3c,0x7c38,0x7d3c,0x7e3c,0x7f38, + 0x8090,0x8194,0x8294,0x8390,0x8494,0x8590,0x8690,0x8794, + 0x889c,0x8998,0x8a98,0x8b9c,0x8c98,0x8d9c,0x8e9c,0x8f98, + 0x9094,0x9190,0x9290,0x9394,0x9490,0x9594,0x9694,0x9790, + 0x9898,0x999c,0x9a9c,0x9b98,0x9c9c,0x9d98,0x9e98,0x9f9c, + 0xa0b4,0xa1b0,0xa2b0,0xa3b4,0xa4b0,0xa5b4,0xa6b4,0xa7b0, + 0xa8b8,0xa9bc,0xaabc,0xabb8,0xacbc,0xadb8,0xaeb8,0xafbc, + 0xb0b0,0xb1b4,0xb2b4,0xb3b0,0xb4b4,0xb5b0,0xb6b0,0xb7b4, + 0xb8bc,0xb9b8,0xbab8,0xbbbc,0xbcb8,0xbdbc,0xbebc,0xbfb8, + 0xc094,0xc190,0xc290,0xc394,0xc490,0xc594,0xc694,0xc790, + 0xc898,0xc99c,0xca9c,0xcb98,0xcc9c,0xcd98,0xce98,0xcf9c, + 0xd090,0xd194,0xd294,0xd390,0xd494,0xd590,0xd690,0xd794, + 0xd89c,0xd998,0xda98,0xdb9c,0xdc98,0xdd9c,0xde9c,0xdf98, + 0xe0b0,0xe1b4,0xe2b4,0xe3b0,0xe4b4,0xe5b0,0xe6b0,0xe7b4, + 0xe8bc,0xe9b8,0xeab8,0xebbc,0xecb8,0xedbc,0xeebc,0xefb8, + 0xf0b4,0xf1b0,0xf2b0,0xf3b4,0xf4b0,0xf5b4,0xf6b4,0xf7b0, + 0xf8b8,0xf9bc,0xfabc,0xfbb8,0xfcbc,0xfdb8,0xfeb8,0xffbc, +}; + +/* xororTable[i] = (i << 8) | (i & 0xa8) | ((i == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 xororTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* rotateShiftTable[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i & 0xff], i = 0..255 */ +static const uint8 rotateShiftTable[256] = { + 68, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 0, 4, 4, 0, 4, 0, 0, 4, 12, 8, 8, 12, 8, 12, 12, 8, + 4, 0, 0, 4, 0, 4, 4, 0, 8, 12, 12, 8, 12, 8, 8, 12, + 36, 32, 32, 36, 32, 36, 36, 32, 40, 44, 44, 40, 44, 40, 40, 44, + 32, 36, 36, 32, 36, 32, 32, 36, 44, 40, 40, 44, 40, 44, 44, 40, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 132,128,128,132,128,132,132,128,136,140,140,136,140,136,136,140, + 128,132,132,128,132,128,128,132,140,136,136,140,136,140,140,136, + 160,164,164,160,164,160,160,164,172,168,168,172,168,172,172,168, + 164,160,160,164,160,164,164,160,168,172,172,168,172,168,168,172, +}; + +/* incZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0) << 4) | ((i == 0x80) << 2), i = 0..256 */ +static const uint8 incZ80Table[257] = { + 80, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 148,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 144,128,128,128,128,128,128,128,136,136,136,136,136,136,136,136, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, + 176,160,160,160,160,160,160,160,168,168,168,168,168,168,168,168, 80, +}; + +/* decZ80Table[i] = (i & 0xa8) | (((i & 0xff) == 0) << 6) | + (((i & 0xf) == 0xf) << 4) | ((i == 0x7f) << 2) | 2, i = 0..255 */ +static const uint8 decZ80Table[256] = { + 66, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 58, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 62, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 130,130,130,130,130,130,130,130,138,138,138,138,138,138,138,154, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, + 162,162,162,162,162,162,162,162,170,170,170,170,170,170,170,186, +}; + +/* cbitsZ80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1), i = 0..511 */ +static const uint8 cbitsZ80Table[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, +}; + +/* cbitsZ80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | + ((i >> 8) & 1) | (i & 0xa8), i = 0..511 */ +static const uint8 cbitsZ80DupTable[512] = { + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8, + 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, + 32, 32, 32, 32, 32, 32, 32, 32, 40, 40, 40, 40, 40, 40, 40, 40, + 48, 48, 48, 48, 48, 48, 48, 48, 56, 56, 56, 56, 56, 56, 56, 56, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 132,132,132,132,132,132,132,132,140,140,140,140,140,140,140,140, + 148,148,148,148,148,148,148,148,156,156,156,156,156,156,156,156, + 164,164,164,164,164,164,164,164,172,172,172,172,172,172,172,172, + 180,180,180,180,180,180,180,180,188,188,188,188,188,188,188,188, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 5, 5, 5, 5, 5, 5, 5, 5, 13, 13, 13, 13, 13, 13, 13, 13, + 21, 21, 21, 21, 21, 21, 21, 21, 29, 29, 29, 29, 29, 29, 29, 29, + 37, 37, 37, 37, 37, 37, 37, 37, 45, 45, 45, 45, 45, 45, 45, 45, + 53, 53, 53, 53, 53, 53, 53, 53, 61, 61, 61, 61, 61, 61, 61, 61, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, + 129,129,129,129,129,129,129,129,137,137,137,137,137,137,137,137, + 145,145,145,145,145,145,145,145,153,153,153,153,153,153,153,153, + 161,161,161,161,161,161,161,161,169,169,169,169,169,169,169,169, + 177,177,177,177,177,177,177,177,185,185,185,185,185,185,185,185, +}; + +/* cbits2Z80Table[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2, i = 0..511 */ +static const uint8 cbits2Z80Table[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 23,23,23,23,23,23,23,23,23,23,23,23,23,23,23,23, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* cbits2Z80DupTable[i] = (i & 0x10) | (((i >> 6) ^ (i >> 5)) & 4) | ((i >> 8) & 1) | 2 | + (i & 0xa8), i = 0..511 */ +static const uint8 cbits2Z80DupTable[512] = { + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 2, 2, 2, 2, 2, 2, 2, 2, 10, 10, 10, 10, 10, 10, 10, 10, + 18, 18, 18, 18, 18, 18, 18, 18, 26, 26, 26, 26, 26, 26, 26, 26, + 34, 34, 34, 34, 34, 34, 34, 34, 42, 42, 42, 42, 42, 42, 42, 42, + 50, 50, 50, 50, 50, 50, 50, 50, 58, 58, 58, 58, 58, 58, 58, 58, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 134,134,134,134,134,134,134,134,142,142,142,142,142,142,142,142, + 150,150,150,150,150,150,150,150,158,158,158,158,158,158,158,158, + 166,166,166,166,166,166,166,166,174,174,174,174,174,174,174,174, + 182,182,182,182,182,182,182,182,190,190,190,190,190,190,190,190, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 7, 7, 7, 7, 7, 7, 7, 7, 15, 15, 15, 15, 15, 15, 15, 15, + 23, 23, 23, 23, 23, 23, 23, 23, 31, 31, 31, 31, 31, 31, 31, 31, + 39, 39, 39, 39, 39, 39, 39, 39, 47, 47, 47, 47, 47, 47, 47, 47, + 55, 55, 55, 55, 55, 55, 55, 55, 63, 63, 63, 63, 63, 63, 63, 63, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, + 131,131,131,131,131,131,131,131,139,139,139,139,139,139,139,139, + 147,147,147,147,147,147,147,147,155,155,155,155,155,155,155,155, + 163,163,163,163,163,163,163,163,171,171,171,171,171,171,171,171, + 179,179,179,179,179,179,179,179,187,187,187,187,187,187,187,187, +}; + +/* negTable[i] = (((i & 0x0f) != 0) << 4) | ((i == 0x80) << 2) | 2 | (i != 0), i = 0..255 */ +static const uint8 negTable[256] = { + 2,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 7,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, + 3,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19, +}; + +/* rrdrldTable[i] = (i << 8) | (i & 0xa8) | (((i & 0xff) == 0) << 6) | parityTable[i], i = 0..255 */ +static const uint16 rrdrldTable[256] = { + 0x0044,0x0100,0x0200,0x0304,0x0400,0x0504,0x0604,0x0700, + 0x0808,0x090c,0x0a0c,0x0b08,0x0c0c,0x0d08,0x0e08,0x0f0c, + 0x1000,0x1104,0x1204,0x1300,0x1404,0x1500,0x1600,0x1704, + 0x180c,0x1908,0x1a08,0x1b0c,0x1c08,0x1d0c,0x1e0c,0x1f08, + 0x2020,0x2124,0x2224,0x2320,0x2424,0x2520,0x2620,0x2724, + 0x282c,0x2928,0x2a28,0x2b2c,0x2c28,0x2d2c,0x2e2c,0x2f28, + 0x3024,0x3120,0x3220,0x3324,0x3420,0x3524,0x3624,0x3720, + 0x3828,0x392c,0x3a2c,0x3b28,0x3c2c,0x3d28,0x3e28,0x3f2c, + 0x4000,0x4104,0x4204,0x4300,0x4404,0x4500,0x4600,0x4704, + 0x480c,0x4908,0x4a08,0x4b0c,0x4c08,0x4d0c,0x4e0c,0x4f08, + 0x5004,0x5100,0x5200,0x5304,0x5400,0x5504,0x5604,0x5700, + 0x5808,0x590c,0x5a0c,0x5b08,0x5c0c,0x5d08,0x5e08,0x5f0c, + 0x6024,0x6120,0x6220,0x6324,0x6420,0x6524,0x6624,0x6720, + 0x6828,0x692c,0x6a2c,0x6b28,0x6c2c,0x6d28,0x6e28,0x6f2c, + 0x7020,0x7124,0x7224,0x7320,0x7424,0x7520,0x7620,0x7724, + 0x782c,0x7928,0x7a28,0x7b2c,0x7c28,0x7d2c,0x7e2c,0x7f28, + 0x8080,0x8184,0x8284,0x8380,0x8484,0x8580,0x8680,0x8784, + 0x888c,0x8988,0x8a88,0x8b8c,0x8c88,0x8d8c,0x8e8c,0x8f88, + 0x9084,0x9180,0x9280,0x9384,0x9480,0x9584,0x9684,0x9780, + 0x9888,0x998c,0x9a8c,0x9b88,0x9c8c,0x9d88,0x9e88,0x9f8c, + 0xa0a4,0xa1a0,0xa2a0,0xa3a4,0xa4a0,0xa5a4,0xa6a4,0xa7a0, + 0xa8a8,0xa9ac,0xaaac,0xaba8,0xacac,0xada8,0xaea8,0xafac, + 0xb0a0,0xb1a4,0xb2a4,0xb3a0,0xb4a4,0xb5a0,0xb6a0,0xb7a4, + 0xb8ac,0xb9a8,0xbaa8,0xbbac,0xbca8,0xbdac,0xbeac,0xbfa8, + 0xc084,0xc180,0xc280,0xc384,0xc480,0xc584,0xc684,0xc780, + 0xc888,0xc98c,0xca8c,0xcb88,0xcc8c,0xcd88,0xce88,0xcf8c, + 0xd080,0xd184,0xd284,0xd380,0xd484,0xd580,0xd680,0xd784, + 0xd88c,0xd988,0xda88,0xdb8c,0xdc88,0xdd8c,0xde8c,0xdf88, + 0xe0a0,0xe1a4,0xe2a4,0xe3a0,0xe4a4,0xe5a0,0xe6a0,0xe7a4, + 0xe8ac,0xe9a8,0xeaa8,0xebac,0xeca8,0xedac,0xeeac,0xefa8, + 0xf0a4,0xf1a0,0xf2a0,0xf3a4,0xf4a0,0xf5a4,0xf6a4,0xf7a0, + 0xf8a8,0xf9ac,0xfaac,0xfba8,0xfcac,0xfda8,0xfea8,0xffac, +}; + +/* cpTable[i] = (i & 0x80) | (((i & 0xff) == 0) << 6), i = 0..255 */ +static const uint8 cpTable[256] = { + 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, + 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128, +}; + +/* Memory management */ + +uint8 MOPT[MAXBANKSIZE]; /* RAM which is present */ + +static uint8 GET_BYTE(register uint32 Addr) { + return MOPT[Addr & ADDRMASK]; +} + +static void PUT_BYTE(register uint32 Addr, register uint32 Value) { + MOPT[Addr & ADDRMASK] = Value; +} + +static void PUT_WORD(register uint32 Addr, register uint32 Value) { + MOPT[Addr & ADDRMASK] = Value; + MOPT[(Addr + 1) & ADDRMASK] = Value >> 8; +} + +static uint16 GET_WORD(register uint32 a) { + return GET_BYTE(a) | (GET_BYTE(a + 1) << 8); +} + +#define RAM_MM(a) GET_BYTE(a--) +#define RAM_PP(a) GET_BYTE(a++) + +#define PUT_BYTE_PP(a,v) PUT_BYTE(a++, v) +#define PUT_BYTE_MM(a,v) PUT_BYTE(a--, v) +#define MM_PUT_BYTE(a,v) PUT_BYTE(--a, v) + +#define PUSH(x) do { \ + MM_PUT_BYTE(SP, (x) >> 8); \ + MM_PUT_BYTE(SP, x); \ +} while (0) + +/* Macros for the IN/OUT instructions INI/INIR/IND/INDR/OUTI/OTIR/OUTD/OTDR + + Pre condition + temp == value of register B at entry of the instruction + acu == value of transferred byte (IN or OUT) + Post condition + F is set correctly + + Use INOUTFLAGS_ZERO(x) for INIR/INDR/OTIR/OTDR where + x == (C + 1) & 0xff for INIR + x == L for OTIR and OTDR + x == (C - 1) & 0xff for INDR + Use INOUTFLAGS_NONZERO(x) for INI/IND/OUTI/OUTD where + x == (C + 1) & 0xff for INI + x == L for OUTI and OUTD + x == (C - 1) & 0xff for IND +*/ +#define INOUTFLAGS(syxz, x) \ + AF = (AF & 0xff00) | (syxz) | /* SF, YF, XF, ZF */ \ + ((acu & 0x80) >> 6) | /* NF */ \ + ((acu + (x)) > 0xff ? (FLAG_C | FLAG_H) : 0) | /* CF, HF */ \ + parityTable[((acu + (x)) & 7) ^ temp] /* PF */ + +#define INOUTFLAGS_ZERO(x) INOUTFLAGS(FLAG_Z, x) +#define INOUTFLAGS_NONZERO(x) \ + INOUTFLAGS((HIGH_REGISTER(BC) & 0xa8) | ((HIGH_REGISTER(BC) == 0) << 6), x) + +t_stat sim_instr_nommu(void) { + extern int32 sim_interval; + extern uint32 sim_brk_summ; + int32 reason = 0; + register uint32 AF; + register uint32 BC; + register uint32 DE; + register uint32 HL; + register uint32 PC; + register uint32 SP; + register uint32 IX; + register uint32 IY; + register uint32 temp = 0; + register uint32 acu = 0; + register uint32 sum; + register uint32 cbits; + register uint32 op; + register uint32 adr; + register int32 l_sim_brk_summ; + + AF = AF_S; + BC = BC_S; + DE = DE_S; + HL = HL_S; + PC = PC_S & ADDRMASK; + SP = SP_S; + IX = IX_S; + IY = IY_S; + l_sim_brk_summ = sim_brk_summ; + + /* main instruction fetch/decode loop */ + while (TRUE) { /* loop until halted */ + if (sim_interval <= 0) { /* check clock queue */ +#if !UNIX_PLATFORM + if ((reason = sim_poll_kbd()) == SCPE_STOP) break; /* poll on platforms without reliable signalling */ +#endif + if ((reason = sim_process_event())) break; + } + + if (l_sim_brk_summ && sim_brk_test(PC, SWMASK('E'))) {/* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + PCX = PC; + sim_interval--; + + switch(RAM_PP(PC)) { + + case 0x00: /* NOP */ + break; + + case 0x01: /* LD BC,nnnn */ + BC = GET_WORD(PC); + PC += 2; + break; + + case 0x02: /* LD (BC),A */ + PUT_BYTE(BC, HIGH_REGISTER(AF)); + break; + + case 0x03: /* INC BC */ + ++BC; + break; + + case 0x04: /* INC B */ + BC += 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x05: /* DEC B */ + BC -= 0x100; + temp = HIGH_REGISTER(BC); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x06: /* LD B,nn */ + SET_HIGH_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x07: /* RLCA */ + AF = ((AF >> 7) & 0x0128) | ((AF << 1) & ~0x1ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x08: /* EX AF,AF' */ + CHECK_CPU_8080; + temp = AF; + AF = AF1_S; + AF1_S = temp; + break; + + case 0x09: /* ADD HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x0a: /* LD A,(BC) */ + SET_HIGH_REGISTER(AF, GET_BYTE(BC)); + break; + + case 0x0b: /* DEC BC */ + --BC; + break; + + case 0x0c: /* INC C */ + temp = LOW_REGISTER(BC) + 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x0d: /* DEC C */ + temp = LOW_REGISTER(BC) - 1; + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x0e: /* LD C,nn */ + SET_LOW_REGISTER(BC, RAM_PP(PC)); + break; + + case 0x0f: /* RRCA */ + AF = (AF & 0xc4) | rrcaTable[HIGH_REGISTER(AF)]; + break; + + case 0x10: /* DJNZ dd */ + CHECK_CPU_8080; + if ((BC -= 0x100) & 0xff00) PC += (int8) GET_BYTE(PC) + 1; + else PC++; + break; + + case 0x11: /* LD DE,nnnn */ + DE = GET_WORD(PC); + PC += 2; + break; + + case 0x12: /* LD (DE),A */ + PUT_BYTE(DE, HIGH_REGISTER(AF)); + break; + + case 0x13: /* INC DE */ + ++DE; + break; + + case 0x14: /* INC D */ + DE += 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x15: /* DEC D */ + DE -= 0x100; + temp = HIGH_REGISTER(DE); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x16: /* LD D,nn */ + SET_HIGH_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x17: /* RLA */ + AF = ((AF << 8) & 0x0100) | ((AF >> 7) & 0x28) | ((AF << 1) & ~0x01ff) | + (AF & 0xc4) | ((AF >> 15) & 1); + break; + + case 0x18: /* JR dd */ + CHECK_CPU_8080; + PC += (int8) GET_BYTE(PC) + 1; + break; + + case 0x19: /* ADD HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x1a: /* LD A,(DE) */ + SET_HIGH_REGISTER(AF, GET_BYTE(DE)); + break; + + case 0x1b: /* DEC DE */ + --DE; + break; + + case 0x1c: /* INC E */ + temp = LOW_REGISTER(DE) + 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x1d: /* DEC E */ + temp = LOW_REGISTER(DE) - 1; + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x1e: /* LD E,nn */ + SET_LOW_REGISTER(DE, RAM_PP(PC)); + break; + + case 0x1f: /* RRA */ + AF = ((AF & 1) << 15) | (AF & 0xc4) | rraTable[HIGH_REGISTER(AF)]; + break; + + case 0x20: /* JR NZ,dd */ + CHECK_CPU_8080; + if (TSTFLAG(Z)) PC++; + else PC += (int8) GET_BYTE(PC) + 1; + break; + + case 0x21: /* LD HL,nnnn */ + HL = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),HL */ + temp = GET_WORD(PC); + PUT_WORD(temp, HL); + PC += 2; + break; + + case 0x23: /* INC HL */ + ++HL; + break; + + case 0x24: /* INC H */ + HL += 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x25: /* DEC H */ + HL -= 0x100; + temp = HIGH_REGISTER(HL); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x26: /* LD H,nn */ + SET_HIGH_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x27: /* DAA */ + acu = HIGH_REGISTER(AF); + temp = LOW_DIGIT(acu); + cbits = TSTFLAG(C); + if (TSTFLAG(N)) { /* last operation was a subtract */ + int hd = cbits || acu > 0x99; + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + if (temp > 5) SETFLAG(H, 0); + acu -= 6; + acu &= 0xff; + } + if (hd) acu -= 0x160; /* adjust high digit */ + } + else { /* last operation was an add */ + if (TSTFLAG(H) || (temp > 9)) { /* adjust low digit */ + SETFLAG(H, (temp > 9)); + acu += 6; + } + if (cbits || ((acu & 0x1f0) > 0x90)) acu += 0x60; /* adjust high digit */ + } + AF = (AF & 0x12) | rrdrldTable[acu & 0xff] | ((acu >> 8) & 1) | cbits; + break; + + case 0x28: /* JR Z,dd */ + CHECK_CPU_8080; + if (TSTFLAG(Z)) PC += (int8) GET_BYTE(PC) + 1; + else PC++; + break; + + case 0x29: /* ADD HL,HL */ + HL &= ADDRMASK; + sum = HL + HL; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + HL = sum; + break; + + case 0x2a: /* LD HL,(nnnn) */ + temp = GET_WORD(PC); + HL = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC HL */ + --HL; + break; + + case 0x2c: /* INC L */ + temp = LOW_REGISTER(HL) + 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x2d: /* DEC L */ + temp = LOW_REGISTER(HL) - 1; + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x2e: /* LD L,nn */ + SET_LOW_REGISTER(HL, RAM_PP(PC)); + break; + + case 0x2f: /* CPL */ + AF = (~AF & ~0xff) | (AF & 0xc5) | ((~AF >> 8) & 0x28) | 0x12; + break; + + case 0x30: /* JR NC,dd */ + CHECK_CPU_8080; + if (TSTFLAG(C)) PC++; + else PC += (int8) GET_BYTE(PC) + 1; + break; + + case 0x31: /* LD SP,nnnn */ + SP = GET_WORD(PC); + PC += 2; + break; + + case 0x32: /* LD (nnnn),A */ + temp = GET_WORD(PC); + PUT_BYTE(temp, HIGH_REGISTER(AF)); + PC += 2; + break; + + case 0x33: /* INC SP */ + ++SP; + break; + + case 0x34: /* INC (HL) */ + temp = GET_BYTE(HL) + 1; + PUT_BYTE(HL, temp); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); + break; + + case 0x35: /* DEC (HL) */ + temp = GET_BYTE(HL) - 1; + PUT_BYTE(HL, temp); + AF = (AF & ~0xfe) | decTable[temp & 0xff] | SET_PV2(0x7f); + break; + + case 0x36: /* LD (HL),nn */ + PUT_BYTE(HL, RAM_PP(PC)); + break; + + case 0x37: /* SCF */ + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | 1; + break; + + case 0x38: /* JR C,dd */ + CHECK_CPU_8080; + if (TSTFLAG(C)) PC += (int8) GET_BYTE(PC) + 1; + else PC++; + break; + + case 0x39: /* ADD HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x3a: /* LD A,(nnnn) */ + temp = GET_WORD(PC); + SET_HIGH_REGISTER(AF, GET_BYTE(temp)); + PC += 2; + break; + + case 0x3b: /* DEC SP */ + --SP; + break; + + case 0x3c: /* INC A */ + AF += 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | incTable[temp] | SET_PV2(0x80); /* SET_PV2 uses temp */ + break; + + case 0x3d: /* DEC A */ + AF -= 0x100; + temp = HIGH_REGISTER(AF); + AF = (AF & ~0xfe) | decTable[temp] | SET_PV2(0x7f); /* SET_PV2 uses temp */ + break; + + case 0x3e: /* LD A,nn */ + SET_HIGH_REGISTER(AF, RAM_PP(PC)); + break; + + case 0x3f: /* CCF */ + AF = (AF & ~0x3b) | ((AF >> 8) & 0x28) | ((AF & 1) << 4) | (~AF & 1); + break; + + case 0x40: /* LD B,B */ + break; + + case 0x41: /* LD B,C */ + BC = (BC & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x42: /* LD B,D */ + BC = (BC & 0xff) | (DE & ~0xff); + break; + + case 0x43: /* LD B,E */ + BC = (BC & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x44: /* LD B,H */ + BC = (BC & 0xff) | (HL & ~0xff); + break; + + case 0x45: /* LD B,L */ + BC = (BC & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x46: /* LD B,(HL) */ + SET_HIGH_REGISTER(BC, GET_BYTE(HL)); + break; + + case 0x47: /* LD B,A */ + BC = (BC & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* LD C,B */ + BC = (BC & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x49: /* LD C,C */ + break; + + case 0x4a: /* LD C,D */ + BC = (BC & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x4b: /* LD C,E */ + BC = (BC & ~0xff) | (DE & 0xff); + break; + + case 0x4c: /* LD C,H */ + BC = (BC & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x4d: /* LD C,L */ + BC = (BC & ~0xff) | (HL & 0xff); + break; + + case 0x4e: /* LD C,(HL) */ + SET_LOW_REGISTER(BC, GET_BYTE(HL)); + break; + + case 0x4f: /* LD C,A */ + BC = (BC & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* LD D,B */ + DE = (DE & 0xff) | (BC & ~0xff); + break; + + case 0x51: /* LD D,C */ + DE = (DE & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x52: /* LD D,D */ + break; + + case 0x53: /* LD D,E */ + DE = (DE & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x54: /* LD D,H */ + DE = (DE & 0xff) | (HL & ~0xff); + break; + + case 0x55: /* LD D,L */ + DE = (DE & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x56: /* LD D,(HL) */ + SET_HIGH_REGISTER(DE, GET_BYTE(HL)); + break; + + case 0x57: /* LD D,A */ + DE = (DE & 0xff) | (AF & ~0xff); + break; + + case 0x58: /* LD E,B */ + DE = (DE & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x59: /* LD E,C */ + DE = (DE & ~0xff) | (BC & 0xff); + break; + + case 0x5a: /* LD E,D */ + DE = (DE & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x5b: /* LD E,E */ + break; + + case 0x5c: /* LD E,H */ + DE = (DE & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x5d: /* LD E,L */ + DE = (DE & ~0xff) | (HL & 0xff); + break; + + case 0x5e: /* LD E,(HL) */ + SET_LOW_REGISTER(DE, GET_BYTE(HL)); + break; + + case 0x5f: /* LD E,A */ + DE = (DE & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x60: /* LD H,B */ + HL = (HL & 0xff) | (BC & ~0xff); + break; + + case 0x61: /* LD H,C */ + HL = (HL & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x62: /* LD H,D */ + HL = (HL & 0xff) | (DE & ~0xff); + break; + + case 0x63: /* LD H,E */ + HL = (HL & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x64: /* LD H,H */ + break; + + case 0x65: /* LD H,L */ + HL = (HL & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x66: /* LD H,(HL) */ + SET_HIGH_REGISTER(HL, GET_BYTE(HL)); + break; + + case 0x67: /* LD H,A */ + HL = (HL & 0xff) | (AF & ~0xff); + break; + + case 0x68: /* LD L,B */ + HL = (HL & ~0xff) | ((BC >> 8) & 0xff); + break; + + case 0x69: /* LD L,C */ + HL = (HL & ~0xff) | (BC & 0xff); + break; + + case 0x6a: /* LD L,D */ + HL = (HL & ~0xff) | ((DE >> 8) & 0xff); + break; + + case 0x6b: /* LD L,E */ + HL = (HL & ~0xff) | (DE & 0xff); + break; + + case 0x6c: /* LD L,H */ + HL = (HL & ~0xff) | ((HL >> 8) & 0xff); + break; + + case 0x6d: /* LD L,L */ + break; + + case 0x6e: /* LD L,(HL) */ + SET_LOW_REGISTER(HL, GET_BYTE(HL)); + break; + + case 0x6f: /* LD L,A */ + HL = (HL & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x70: /* LD (HL),B */ + PUT_BYTE(HL, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (HL),C */ + PUT_BYTE(HL, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (HL),D */ + PUT_BYTE(HL, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (HL),E */ + PUT_BYTE(HL, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (HL),H */ + PUT_BYTE(HL, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (HL),L */ + PUT_BYTE(HL, LOW_REGISTER(HL)); + break; + + case 0x76: /* HALT */ + PC--; + if (cpu_unit.flags & UNIT_CPU_STOPONHALT) { + reason = STOP_HALT; + goto end_decode; + } + sim_interval = 0; + break; + + case 0x77: /* LD (HL),A */ + PUT_BYTE(HL, HIGH_REGISTER(AF)); + break; + + case 0x78: /* LD A,B */ + AF = (AF & 0xff) | (BC & ~0xff); + break; + + case 0x79: /* LD A,C */ + AF = (AF & 0xff) | ((BC & 0xff) << 8); + break; + + case 0x7a: /* LD A,D */ + AF = (AF & 0xff) | (DE & ~0xff); + break; + + case 0x7b: /* LD A,E */ + AF = (AF & 0xff) | ((DE & 0xff) << 8); + break; + + case 0x7c: /* LD A,H */ + AF = (AF & 0xff) | (HL & ~0xff); + break; + + case 0x7d: /* LD A,L */ + AF = (AF & 0xff) | ((HL & 0xff) << 8); + break; + + case 0x7e: /* LD A,(HL) */ + SET_HIGH_REGISTER(AF, GET_BYTE(HL)); + break; + + case 0x7f: /* LD A,A */ + break; + + case 0x80: /* ADD A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x81: /* ADD A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x82: /* ADD A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x83: /* ADD A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x84: /* ADD A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x85: /* ADD A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x86: /* ADD A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x87: /* ADD A,A */ + cbits = 2 * HIGH_REGISTER(AF); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x88: /* ADC A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x89: /* ADC A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8a: /* ADC A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8b: /* ADC A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8c: /* ADC A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8d: /* ADC A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8e: /* ADC A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0x8f: /* ADC A,A */ + cbits = 2 * HIGH_REGISTER(AF) + TSTFLAG(C); + AF = cbitsDup8Table[cbits] | (SET_PVS(cbits)); + break; + + case 0x90: /* SUB B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x91: /* SUB C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x92: /* SUB D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x93: /* SUB E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x94: /* SUB H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x95: /* SUB L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x96: /* SUB (HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x97: /* SUB A */ + AF = (chiptype == CHIP_TYPE_Z80) ? 0x42 : 0x46; + break; + + case 0x98: /* SBC A,B */ + temp = HIGH_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x99: /* SBC A,C */ + temp = LOW_REGISTER(BC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9a: /* SBC A,D */ + temp = HIGH_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9b: /* SBC A,E */ + temp = LOW_REGISTER(DE); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9c: /* SBC A,H */ + temp = HIGH_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9d: /* SBC A,L */ + temp = LOW_REGISTER(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9e: /* SBC A,(HL) */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0x9f: /* SBC A,A */ + cbits = -TSTFLAG(C); + AF = subTable[cbits & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PVS(cbits)); + break; + + case 0xa0: /* AND B */ + AF = andTable[((AF & BC) >> 8) & 0xff]; + break; + + case 0xa1: /* AND C */ + AF = andTable[((AF >> 8) & BC) & 0xff]; + break; + + case 0xa2: /* AND D */ + AF = andTable[((AF & DE) >> 8) & 0xff]; + break; + + case 0xa3: /* AND E */ + AF = andTable[((AF >> 8) & DE) & 0xff]; + break; + + case 0xa4: /* AND H */ + AF = andTable[((AF & HL) >> 8) & 0xff]; + break; + + case 0xa5: /* AND L */ + AF = andTable[((AF >> 8) & HL) & 0xff]; + break; + + case 0xa6: /* AND (HL) */ + AF = andTable[((AF >> 8) & GET_BYTE(HL)) & 0xff]; + break; + + case 0xa7: /* AND A */ + AF = andTable[(AF >> 8) & 0xff]; + break; + + case 0xa8: /* XOR B */ + AF = xororTable[((AF ^ BC) >> 8) & 0xff]; + break; + + case 0xa9: /* XOR C */ + AF = xororTable[((AF >> 8) ^ BC) & 0xff]; + break; + + case 0xaa: /* XOR D */ + AF = xororTable[((AF ^ DE) >> 8) & 0xff]; + break; + + case 0xab: /* XOR E */ + AF = xororTable[((AF >> 8) ^ DE) & 0xff]; + break; + + case 0xac: /* XOR H */ + AF = xororTable[((AF ^ HL) >> 8) & 0xff]; + break; + + case 0xad: /* XOR L */ + AF = xororTable[((AF >> 8) ^ HL) & 0xff]; + break; + + case 0xae: /* XOR (HL) */ + AF = xororTable[((AF >> 8) ^ GET_BYTE(HL)) & 0xff]; + break; + + case 0xaf: /* XOR A */ + AF = 0x44; + break; + + case 0xb0: /* OR B */ + AF = xororTable[((AF | BC) >> 8) & 0xff]; + break; + + case 0xb1: /* OR C */ + AF = xororTable[((AF >> 8) | BC) & 0xff]; + break; + + case 0xb2: /* OR D */ + AF = xororTable[((AF | DE) >> 8) & 0xff]; + break; + + case 0xb3: /* OR E */ + AF = xororTable[((AF >> 8) | DE) & 0xff]; + break; + + case 0xb4: /* OR H */ + AF = xororTable[((AF | HL) >> 8) & 0xff]; + break; + + case 0xb5: /* OR L */ + AF = xororTable[((AF >> 8) | HL) & 0xff]; + break; + + case 0xb6: /* OR (HL) */ + AF = xororTable[((AF >> 8) | GET_BYTE(HL)) & 0xff]; + break; + + case 0xb7: /* OR A */ + AF = xororTable[(AF >> 8) & 0xff]; + break; + + case 0xb8: /* CP B */ + temp = HIGH_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xb9: /* CP C */ + temp = LOW_REGISTER(BC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xba: /* CP D */ + temp = HIGH_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbb: /* CP E */ + temp = LOW_REGISTER(DE); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbc: /* CP H */ + temp = HIGH_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbd: /* CP L */ + temp = LOW_REGISTER(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbe: /* CP (HL) */ + temp = GET_BYTE(HL); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xbf: /* CP A */ + SET_LOW_REGISTER(AF, (HIGH_REGISTER(AF) & 0x28) | (chiptype == CHIP_TYPE_Z80 ? 0x42 : 0x46)); + break; + + case 0xc0: /* RET NZ */ + if (!(TSTFLAG(Z))) POP(PC); + break; + + case 0xc1: /* POP BC */ + POP(BC); + break; + + case 0xc2: /* JP NZ,nnnn */ + JPC(!TSTFLAG(Z)); + break; + + case 0xc3: /* JP nnnn */ + JPC(1); + break; + + case 0xc4: /* CALL NZ,nnnn */ + CALLC(!TSTFLAG(Z)); + break; + + case 0xc5: /* PUSH BC */ + PUSH(BC); + break; + + case 0xc6: /* ADD A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xc7: /* RST 0 */ + PUSH(PC); + PC = 0; + break; + + case 0xc8: /* RET Z */ + if (TSTFLAG(Z)) POP(PC); + break; + + case 0xc9: /* RET */ + POP(PC); + break; + + case 0xca: /* JP Z,nnnn */ + JPC(TSTFLAG(Z)); + break; + + case 0xcb: /* CB prefix */ + CHECK_CPU_8080; + adr = HL; + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg1; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg1; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg1; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg1; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg1; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg1; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg1: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xcc: /* CALL Z,nnnn */ + CALLC(TSTFLAG(Z)); + break; + + case 0xcd: /* CALL nnnn */ + CALLC(1); + break; + + case 0xce: /* ADC A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = addTable[sum] | cbitsTable[cbits] | (SET_PV); + break; + + case 0xcf: /* RST 8 */ + PUSH(PC); + PC = 8; + break; + + case 0xd0: /* RET NC */ + if (!(TSTFLAG(C))) POP(PC); + break; + + case 0xd1: /* POP DE */ + POP(DE); + break; + + case 0xd2: /* JP NC,nnnn */ + JPC(!TSTFLAG(C)); + break; + + case 0xd3: /* OUT (nn),A */ + out(RAM_PP(PC), HIGH_REGISTER(AF)); + break; + + case 0xd4: /* CALL NC,nnnn */ + CALLC(!TSTFLAG(C)); + break; + + case 0xd5: /* PUSH DE */ + PUSH(DE); + break; + + case 0xd6: /* SUB nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xd7: /* RST 10H */ + PUSH(PC); + PC = 0x10; + break; + + case 0xd8: /* RET C */ + if (TSTFLAG(C)) POP(PC); + break; + + case 0xd9: /* EXX */ + CHECK_CPU_8080; + temp = BC; + BC = BC1_S; + BC1_S = temp; + temp = DE; + DE = DE1_S; + DE1_S = temp; + temp = HL; + HL = HL1_S; + HL1_S = temp; + break; + + case 0xda: /* JP C,nnnn */ + JPC(TSTFLAG(C)); + break; + + case 0xdb: /* IN A,(nn) */ + SET_HIGH_REGISTER(AF, in(RAM_PP(PC))); + break; + + case 0xdc: /* CALL C,nnnn */ + CALLC(TSTFLAG(C)); + break; + + case 0xdd: /* DD prefix */ + CHECK_CPU_8080; + switch (op = RAM_PP(PC)) { + + case 0x09: /* ADD IX,BC */ + IX &= ADDRMASK; + BC &= ADDRMASK; + sum = IX + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ BC ^ sum) >> 8]; + IX = sum; + break; + + case 0x19: /* ADD IX,DE */ + IX &= ADDRMASK; + DE &= ADDRMASK; + sum = IX + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ DE ^ sum) >> 8]; + IX = sum; + break; + + case 0x21: /* LD IX,nnnn */ + IX = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IX */ + temp = GET_WORD(PC); + PUT_WORD(temp, IX); + PC += 2; + break; + + case 0x23: /* INC IX */ + ++IX; + break; + + case 0x24: /* INC IXH */ + IX += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x25: /* DEC IXH */ + IX -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IX)]; + break; + + case 0x26: /* LD IXH,nn */ + SET_HIGH_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x29: /* ADD IX,IX */ + IX &= ADDRMASK; + sum = IX + IX; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IX = sum; + break; + + case 0x2a: /* LD IX,(nnnn) */ + temp = GET_WORD(PC); + IX = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC IX */ + --IX; + break; + + case 0x2c: /* INC IXL */ + temp = LOW_REGISTER(IX) + 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IXL */ + temp = LOW_REGISTER(IX) - 1; + SET_LOW_REGISTER(IX, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IXL,nn */ + SET_LOW_REGISTER(IX, RAM_PP(PC)); + break; + + case 0x34: /* INC (IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + temp = GET_BYTE(adr) + 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + temp = GET_BYTE(adr) - 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IX+dd),nn */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IX,SP */ + IX &= ADDRMASK; + SP &= ADDRMASK; + sum = IX + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IX ^ SP ^ sum) >> 8]; + IX = sum; + break; + + case 0x44: /* LD B,IXH */ + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x45: /* LD B,IXL */ + SET_HIGH_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x46: /* LD B,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(BC, GET_BYTE(adr)); + break; + + case 0x4c: /* LD C,IXH */ + SET_LOW_REGISTER(BC, HIGH_REGISTER(IX)); + break; + + case 0x4d: /* LD C,IXL */ + SET_LOW_REGISTER(BC, LOW_REGISTER(IX)); + break; + + case 0x4e: /* LD C,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + SET_LOW_REGISTER(BC, GET_BYTE(adr)); + break; + + case 0x54: /* LD D,IXH */ + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x55: /* LD D,IXL */ + SET_HIGH_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x56: /* LD D,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(DE, GET_BYTE(adr)); + break; + + case 0x5c: /* LD E,IXH */ + SET_LOW_REGISTER(DE, HIGH_REGISTER(IX)); + break; + + case 0x5d: /* LD E,IXL */ + SET_LOW_REGISTER(DE, LOW_REGISTER(IX)); + break; + + case 0x5e: /* LD E,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + SET_LOW_REGISTER(DE, GET_BYTE(adr)); + break; + + case 0x60: /* LD IXH,B */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IXH,C */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IXH,D */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IXH,E */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IXH,IXH */ + break; + + case 0x65: /* LD IXH,IXL */ + SET_HIGH_REGISTER(IX, LOW_REGISTER(IX)); + break; + + case 0x66: /* LD H,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(HL, GET_BYTE(adr)); + break; + + case 0x67: /* LD IXH,A */ + SET_HIGH_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IXL,B */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IXL,C */ + SET_LOW_REGISTER(IX, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IXL,D */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IXL,E */ + SET_LOW_REGISTER(IX, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IXL,IXH */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(IX)); + break; + + case 0x6d: /* LD IXL,IXL */ + break; + + case 0x6e: /* LD L,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + SET_LOW_REGISTER(HL, GET_BYTE(adr)); + break; + + case 0x6f: /* LD IXL,A */ + SET_LOW_REGISTER(IX, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IX+dd),B */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IX+dd),C */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IX+dd),D */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IX+dd),E */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IX+dd),H */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IX+dd),L */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IX+dd),A */ + adr = IX + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IXH */ + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IX)); + break; + + case 0x7d: /* LD A,IXL */ + SET_HIGH_REGISTER(AF, LOW_REGISTER(IX)); + break; + + case 0x7e: /* LD A,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(AF, GET_BYTE(adr)); + break; + + case 0x84: /* ADD A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IXH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IXH */ + temp = HIGH_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IXL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IXL */ + temp = LOW_REGISTER(IX); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IXH */ + AF = andTable[((AF & IX) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IXL */ + AF = andTable[((AF >> 8) & IX) & 0xff]; + break; + + case 0xa6: /* AND (IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + AF = andTable[((AF >> 8) & GET_BYTE(adr)) & 0xff]; + break; + + case 0xac: /* XOR IXH */ + AF = xororTable[((AF ^ IX) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IXL */ + AF = xororTable[((AF >> 8) ^ IX) & 0xff]; + break; + + case 0xae: /* XOR (IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + AF = xororTable[((AF >> 8) ^ GET_BYTE(adr)) & 0xff]; + break; + + case 0xb4: /* OR IXH */ + AF = xororTable[((AF | IX) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IXL */ + AF = xororTable[((AF >> 8) | IX) & 0xff]; + break; + + case 0xb6: /* OR (IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + AF = xororTable[((AF >> 8) | GET_BYTE(adr)) & 0xff]; + break; + + case 0xbc: /* CP IXH */ + temp = HIGH_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IXL */ + temp = LOW_REGISTER(IX); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IX+dd) */ + adr = IX + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IX + (int8) RAM_PP(PC); + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg2; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg2; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg2; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg2; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg2; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg2; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg2: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IX */ + POP(IX); + break; + + case 0xe3: /* EX (SP),IX */ + temp = IX; + POP(IX); + PUSH(temp); + break; + + case 0xe5: /* PUSH IX */ + PUSH(IX); + break; + + case 0xe9: /* JP (IX) */ + PC = IX; + break; + + case 0xf9: /* LD SP,IX */ + SP = IX; + break; + + default: /* ignore DD */ + CHECK_CPU_Z80; + PC--; + } + break; + + case 0xde: /* SBC A,nn */ + temp = RAM_PP(PC); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + cbits = acu ^ temp ^ sum; + AF = subTable[sum & 0xff] | cbitsTable[cbits & 0x1ff] | (SET_PV); + break; + + case 0xdf: /* RST 18H */ + PUSH(PC); + PC = 0x18; + break; + + case 0xe0: /* RET PO */ + if (!(TSTFLAG(P))) POP(PC); + break; + + case 0xe1: /* POP HL */ + POP(HL); + break; + + case 0xe2: /* JP PO,nnnn */ + JPC(!TSTFLAG(P)); + break; + + case 0xe3: /* EX (SP),HL */ + temp = HL; + POP(HL); + PUSH(temp); + break; + + case 0xe4: /* CALL PO,nnnn */ + CALLC(!TSTFLAG(P)); + break; + + case 0xe5: /* PUSH HL */ + PUSH(HL); + break; + + case 0xe6: /* AND nn */ + AF = andTable[((AF >> 8) & RAM_PP(PC)) & 0xff]; + break; + + case 0xe7: /* RST 20H */ + PUSH(PC); + PC = 0x20; + break; + + case 0xe8: /* RET PE */ + if (TSTFLAG(P)) POP(PC); + break; + + case 0xe9: /* JP (HL) */ + PC = HL; + break; + + case 0xea: /* JP PE,nnnn */ + JPC(TSTFLAG(P)); + break; + + case 0xeb: /* EX DE,HL */ + temp = HL; + HL = DE; + DE = temp; + break; + + case 0xec: /* CALL PE,nnnn */ + CALLC(TSTFLAG(P)); + break; + + case 0xed: /* ED prefix */ + CHECK_CPU_8080; + switch (op = RAM_PP(PC)) { + + case 0x40: /* IN B,(C) */ + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x41: /* OUT (C),B */ + out(LOW_REGISTER(BC), HIGH_REGISTER(BC)); + break; + + case 0x42: /* SBC HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL - BC - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ BC ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x43: /* LD (nnnn),BC */ + temp = GET_WORD(PC); + PUT_WORD(temp, BC); + PC += 2; + break; + + case 0x44: /* NEG */ + + case 0x4C: /* NEG, unofficial */ + + case 0x54: /* NEG, unofficial */ + + case 0x5C: /* NEG, unofficial */ + + case 0x64: /* NEG, unofficial */ + + case 0x6C: /* NEG, unofficial */ + + case 0x74: /* NEG, unofficial */ + + case 0x7C: /* NEG, unofficial */ + temp = HIGH_REGISTER(AF); + AF = ((~(AF & 0xff00) + 1) & 0xff00); /* AF = (-(AF & 0xff00) & 0xff00); */ + AF |= ((AF >> 8) & 0xa8) | (((AF & 0xff00) == 0) << 6) | negTable[temp]; + break; + + case 0x45: /* RETN */ + + case 0x55: /* RETN, unofficial */ + + case 0x5D: /* RETN, unofficial */ + + case 0x65: /* RETN, unofficial */ + + case 0x6D: /* RETN, unofficial */ + + case 0x75: /* RETN, unofficial */ + + case 0x7D: /* RETN, unofficial */ + IFF_S |= IFF_S >> 1; + POP(PC); + break; + + case 0x46: /* IM 0 */ + /* interrupt mode 0 */ + break; + + case 0x47: /* LD I,A */ + IR_S = (IR_S & 0xff) | (AF & ~0xff); + break; + + case 0x48: /* IN C,(C) */ + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(BC, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x49: /* OUT (C),C */ + out(LOW_REGISTER(BC), LOW_REGISTER(BC)); + break; + + case 0x4a: /* ADC HL,BC */ + HL &= ADDRMASK; + BC &= ADDRMASK; + sum = HL + BC + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ BC ^ sum) >> 8]; + HL = sum; + break; + + case 0x4b: /* LD BC,(nnnn) */ + temp = GET_WORD(PC); + BC = GET_WORD(temp); + PC += 2; + break; + + case 0x4d: /* RETI */ + IFF_S |= IFF_S >> 1; + POP(PC); + break; + + case 0x4f: /* LD R,A */ + IR_S = (IR_S & ~0xff) | ((AF >> 8) & 0xff); + break; + + case 0x50: /* IN D,(C) */ + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x51: /* OUT (C),D */ + out(LOW_REGISTER(BC), HIGH_REGISTER(DE)); + break; + + case 0x52: /* SBC HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL - DE - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ DE ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x53: /* LD (nnnn),DE */ + temp = GET_WORD(PC); + PUT_WORD(temp, DE); + PC += 2; + break; + + case 0x56: /* IM 1 */ + /* interrupt mode 1 */ + break; + + case 0x57: /* LD A,I */ + AF = (AF & 0x29) | (IR_S & ~0xff) | ((IR_S >> 8) & 0x80) | (((IR_S & ~0xff) == 0) << 6) | ((IFF_S & 2) << 1); + break; + + case 0x58: /* IN E,(C) */ + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(DE, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x59: /* OUT (C),E */ + out(LOW_REGISTER(BC), LOW_REGISTER(DE)); + break; + + case 0x5a: /* ADC HL,DE */ + HL &= ADDRMASK; + DE &= ADDRMASK; + sum = HL + DE + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ DE ^ sum) >> 8]; + HL = sum; + break; + + case 0x5b: /* LD DE,(nnnn) */ + temp = GET_WORD(PC); + DE = GET_WORD(temp); + PC += 2; + break; + + case 0x5e: /* IM 2 */ + /* interrupt mode 2 */ + break; + + case 0x5f: /* LD A,R */ + AF = (AF & 0x29) | ((IR_S & 0xff) << 8) | (IR_S & 0x80) | + (((IR_S & 0xff) == 0) << 6) | ((IFF_S & 2) << 1); + break; + + case 0x60: /* IN H,(C) */ + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x61: /* OUT (C),H */ + out(LOW_REGISTER(BC), HIGH_REGISTER(HL)); + break; + + case 0x62: /* SBC HL,HL */ + HL &= ADDRMASK; + sum = HL - HL - TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80DupTable[(sum >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x63: /* LD (nnnn),HL */ + temp = GET_WORD(PC); + PUT_WORD(temp, HL); + PC += 2; + break; + + case 0x67: /* RRD */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + PUT_BYTE(HL, HIGH_DIGIT(temp) | (LOW_DIGIT(acu) << 4)); + AF = rrdrldTable[(acu & 0xf0) | LOW_DIGIT(temp)] | (AF & 1); + break; + + case 0x68: /* IN L,(C) */ + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(HL, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x69: /* OUT (C),L */ + out(LOW_REGISTER(BC), LOW_REGISTER(HL)); + break; + + case 0x6a: /* ADC HL,HL */ + HL &= ADDRMASK; + sum = HL + HL + TSTFLAG(C); + AF = (AF & ~0xff) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80DupTable[sum >> 8]; + HL = sum; + break; + + case 0x6b: /* LD HL,(nnnn) */ + temp = GET_WORD(PC); + HL = GET_WORD(temp); + PC += 2; + break; + + case 0x6f: /* RLD */ + temp = GET_BYTE(HL); + acu = HIGH_REGISTER(AF); + PUT_BYTE(HL, (LOW_DIGIT(temp) << 4) | LOW_DIGIT(acu)); + AF = rrdrldTable[(acu & 0xf0) | HIGH_DIGIT(temp)] | (AF & 1); + break; + + case 0x70: /* IN (C) */ + temp = in(LOW_REGISTER(BC)); + SET_LOW_REGISTER(temp, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x71: /* OUT (C),0 */ + out(LOW_REGISTER(BC), 0); + break; + + case 0x72: /* SBC HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL - SP - TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbits2Z80Table[((HL ^ SP ^ sum) >> 8) & 0x1ff]; + HL = sum; + break; + + case 0x73: /* LD (nnnn),SP */ + temp = GET_WORD(PC); + PUT_WORD(temp, SP); + PC += 2; + break; + + case 0x78: /* IN A,(C) */ + temp = in(LOW_REGISTER(BC)); + SET_HIGH_REGISTER(AF, temp); + AF = (AF & ~0xfe) | rotateShiftTable[temp & 0xff]; + break; + + case 0x79: /* OUT (C),A */ + out(LOW_REGISTER(BC), HIGH_REGISTER(AF)); + break; + + case 0x7a: /* ADC HL,SP */ + HL &= ADDRMASK; + SP &= ADDRMASK; + sum = HL + SP + TSTFLAG(C); + AF = (AF & ~0xff) | ((sum >> 8) & 0xa8) | (((sum & ADDRMASK) == 0) << 6) | + cbitsZ80Table[(HL ^ SP ^ sum) >> 8]; + HL = sum; + break; + + case 0x7b: /* LD SP,(nnnn) */ + temp = GET_WORD(PC); + SP = GET_WORD(temp); + PC += 2; + break; + + case 0xa0: /* LDI */ + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa1: /* CPI */ + acu = HIGH_REGISTER(AF); + temp = RAM_PP(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. So, first of all INI/INIR: + HF and CF Both set if ((HL) + ((C + 1) & 255) > 255) + PF The parity of (((HL) + ((C + 1) & 255)) & 7) xor B) */ + case 0xa2: /* INI */ + acu = in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + And now the for OUTI/OTIR/OUTD/OTDR instructions. Take state of the L + after the increment or decrement of HL; add the value written to the I/O port + to; call that k for now. If k > 255, then the CF and HF flags are set. The PF + flags is set like the parity of k bitwise and'ed with 7, bitwise xor'ed with B. + HF and CF Both set if ((HL) + L > 255) + PF The parity of ((((HL) + L) & 7) xor B) */ + case 0xa3: /* OUTI */ + acu = GET_BYTE(HL); + out(LOW_REGISTER(BC), acu); + ++HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xa8: /* LDD */ + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4) | + (((--BC & ADDRMASK) != 0) << 2); + break; + + case 0xa9: /* CPD */ + acu = HIGH_REGISTER(AF); + temp = RAM_MM(HL); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | (cbits & 16) | + ((sum - ((cbits >> 4) & 1)) & 8) | + ((--BC & ADDRMASK) != 0) << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + +/* SF, ZF, YF, XF flags are affected by decreasing register B, as in DEC B. + NF flag A is copy of bit 7 of the value read from or written to an I/O port. + INI/INIR/IND/INDR use the C flag in stead of the L register. There is a + catch though, because not the value of C is used, but C + 1 if it's INI/INIR or + C - 1 if it's IND/INDR. And last IND/INDR: + HF and CF Both set if ((HL) + ((C - 1) & 255) > 255) + PF The parity of (((HL) + ((C - 1) & 255)) & 7) xor B) */ + case 0xaa: /* IND */ + acu = in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xab: /* OUTD */ + acu = GET_BYTE(HL); + out(LOW_REGISTER(BC), acu); + --HL; + temp = HIGH_REGISTER(BC); + BC -= 0x100; + INOUTFLAGS_NONZERO(LOW_REGISTER(HL)); + break; + + case 0xb0: /* LDIR */ + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + acu = RAM_PP(HL); + PUT_BYTE_PP(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb1: /* CPIR */ + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + temp = RAM_PP(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + + case 0xb2: /* INIR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + acu = in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) + 1) & 0xff); + break; + + case 0xb3: /* OTIR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + acu = GET_BYTE(HL); + out(LOW_REGISTER(BC), acu); + ++HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + case 0xb8: /* LDDR */ + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + acu = RAM_MM(HL); + PUT_BYTE_MM(DE, acu); + } while (--BC); + acu += HIGH_REGISTER(AF); + AF = (AF & ~0x3e) | (acu & 8) | ((acu & 2) << 4); + break; + + case 0xb9: /* CPDR */ + acu = HIGH_REGISTER(AF); + BC &= ADDRMASK; + if (BC == 0) BC = 0x10000; + do { + temp = RAM_MM(HL); + op = --BC != 0; + sum = acu - temp; + } while (op && sum != 0); + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xfe) | (sum & 0x80) | (!(sum & 0xff) << 6) | + (((sum - ((cbits & 16) >> 4)) & 2) << 4) | + (cbits & 16) | ((sum - ((cbits >> 4) & 1)) & 8) | + op << 2 | 2; + if ((sum & 15) == 8 && (cbits & 16) != 0) AF &= ~8; + break; + + case 0xba: /* INDR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + acu = in(LOW_REGISTER(BC)); + PUT_BYTE(HL, acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO((LOW_REGISTER(BC) - 1) & 0xff); + break; + + case 0xbb: /* OTDR */ + temp = HIGH_REGISTER(BC); + if (temp == 0) temp = 0x100; + do { + acu = GET_BYTE(HL); + out(LOW_REGISTER(BC), acu); + --HL; + } while (--temp); + temp = HIGH_REGISTER(BC); + SET_HIGH_REGISTER(BC, 0); + INOUTFLAGS_ZERO(LOW_REGISTER(HL)); + break; + + default: /* ignore ED and following byte */ + CHECK_CPU_Z80; + } + break; + + case 0xee: /* XOR nn */ + AF = xororTable[((AF >> 8) ^ RAM_PP(PC)) & 0xff]; + break; + + case 0xef: /* RST 28H */ + PUSH(PC); + PC = 0x28; + break; + + case 0xf0: /* RET P */ + if (!(TSTFLAG(S))) POP(PC); + break; + + case 0xf1: /* POP AF */ + POP(AF); + break; + + case 0xf2: /* JP P,nnnn */ + JPC(!TSTFLAG(S)); + break; + + case 0xf3: /* DI */ + IFF_S = 0; + break; + + case 0xf4: /* CALL P,nnnn */ + CALLC(!TSTFLAG(S)); + break; + + case 0xf5: /* PUSH AF */ + PUSH(AF); + break; + + case 0xf6: /* OR nn */ + AF = xororTable[((AF >> 8) | RAM_PP(PC)) & 0xff]; + break; + + case 0xf7: /* RST 30H */ + PUSH(PC); + PC = 0x30; + break; + + case 0xf8: /* RET M */ + if (TSTFLAG(S)) POP(PC); + break; + + case 0xf9: /* LD SP,HL */ + SP = HL; + break; + + case 0xfa: /* JP M,nnnn */ + JPC(TSTFLAG(S)); + break; + + case 0xfb: /* EI */ + IFF_S = 3; + break; + + case 0xfc: /* CALL M,nnnn */ + CALLC(TSTFLAG(S)); + break; + + case 0xfd: /* FD prefix */ + CHECK_CPU_8080; + switch (op = RAM_PP(PC)) { + + case 0x09: /* ADD IY,BC */ + IY &= ADDRMASK; + BC &= ADDRMASK; + sum = IY + BC; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ BC ^ sum) >> 8]; + IY = sum; + break; + + case 0x19: /* ADD IY,DE */ + IY &= ADDRMASK; + DE &= ADDRMASK; + sum = IY + DE; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ DE ^ sum) >> 8]; + IY = sum; + break; + + case 0x21: /* LD IY,nnnn */ + IY = GET_WORD(PC); + PC += 2; + break; + + case 0x22: /* LD (nnnn),IY */ + temp = GET_WORD(PC); + PUT_WORD(temp, IY); + PC += 2; + break; + + case 0x23: /* INC IY */ + ++IY; + break; + + case 0x24: /* INC IYH */ + IY += 0x100; + AF = (AF & ~0xfe) | incZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x25: /* DEC IYH */ + IY -= 0x100; + AF = (AF & ~0xfe) | decZ80Table[HIGH_REGISTER(IY)]; + break; + + case 0x26: /* LD IYH,nn */ + SET_HIGH_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x29: /* ADD IY,IY */ + IY &= ADDRMASK; + sum = IY + IY; + AF = (AF & ~0x3b) | cbitsDup16Table[sum >> 8]; + IY = sum; + break; + + case 0x2a: /* LD IY,(nnnn) */ + temp = GET_WORD(PC); + IY = GET_WORD(temp); + PC += 2; + break; + + case 0x2b: /* DEC IY */ + --IY; + break; + + case 0x2c: /* INC IYL */ + temp = LOW_REGISTER(IY) + 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x2d: /* DEC IYL */ + temp = LOW_REGISTER(IY) - 1; + SET_LOW_REGISTER(IY, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x2e: /* LD IYL,nn */ + SET_LOW_REGISTER(IY, RAM_PP(PC)); + break; + + case 0x34: /* INC (IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + temp = GET_BYTE(adr) + 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | incZ80Table[temp]; + break; + + case 0x35: /* DEC (IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + temp = GET_BYTE(adr) - 1; + PUT_BYTE(adr, temp); + AF = (AF & ~0xfe) | decZ80Table[temp & 0xff]; + break; + + case 0x36: /* LD (IY+dd),nn */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, RAM_PP(PC)); + break; + + case 0x39: /* ADD IY,SP */ + IY &= ADDRMASK; + SP &= ADDRMASK; + sum = IY + SP; + AF = (AF & ~0x3b) | ((sum >> 8) & 0x28) | cbitsTable[(IY ^ SP ^ sum) >> 8]; + IY = sum; + break; + + case 0x44: /* LD B,IYH */ + SET_HIGH_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x45: /* LD B,IYL */ + SET_HIGH_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x46: /* LD B,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(BC, GET_BYTE(adr)); + break; + + case 0x4c: /* LD C,IYH */ + SET_LOW_REGISTER(BC, HIGH_REGISTER(IY)); + break; + + case 0x4d: /* LD C,IYL */ + SET_LOW_REGISTER(BC, LOW_REGISTER(IY)); + break; + + case 0x4e: /* LD C,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + SET_LOW_REGISTER(BC, GET_BYTE(adr)); + break; + + case 0x54: /* LD D,IYH */ + SET_HIGH_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x55: /* LD D,IYL */ + SET_HIGH_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x56: /* LD D,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(DE, GET_BYTE(adr)); + break; + + case 0x5c: /* LD E,IYH */ + SET_LOW_REGISTER(DE, HIGH_REGISTER(IY)); + break; + + case 0x5d: /* LD E,IYL */ + SET_LOW_REGISTER(DE, LOW_REGISTER(IY)); + break; + + case 0x5e: /* LD E,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + SET_LOW_REGISTER(DE, GET_BYTE(adr)); + break; + + case 0x60: /* LD IYH,B */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x61: /* LD IYH,C */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x62: /* LD IYH,D */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x63: /* LD IYH,E */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x64: /* LD IYH,IYH */ + break; + + case 0x65: /* LD IYH,IYL */ + SET_HIGH_REGISTER(IY, LOW_REGISTER(IY)); + break; + + case 0x66: /* LD H,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(HL, GET_BYTE(adr)); + break; + + case 0x67: /* LD IYH,A */ + SET_HIGH_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x68: /* LD IYL,B */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(BC)); + break; + + case 0x69: /* LD IYL,C */ + SET_LOW_REGISTER(IY, LOW_REGISTER(BC)); + break; + + case 0x6a: /* LD IYL,D */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(DE)); + break; + + case 0x6b: /* LD IYL,E */ + SET_LOW_REGISTER(IY, LOW_REGISTER(DE)); + break; + + case 0x6c: /* LD IYL,IYH */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(IY)); + break; + + case 0x6d: /* LD IYL,IYL */ + break; + + case 0x6e: /* LD L,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + SET_LOW_REGISTER(HL, GET_BYTE(adr)); + break; + + case 0x6f: /* LD IYL,A */ + SET_LOW_REGISTER(IY, HIGH_REGISTER(AF)); + break; + + case 0x70: /* LD (IY+dd),B */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(BC)); + break; + + case 0x71: /* LD (IY+dd),C */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, LOW_REGISTER(BC)); + break; + + case 0x72: /* LD (IY+dd),D */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(DE)); + break; + + case 0x73: /* LD (IY+dd),E */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, LOW_REGISTER(DE)); + break; + + case 0x74: /* LD (IY+dd),H */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(HL)); + break; + + case 0x75: /* LD (IY+dd),L */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, LOW_REGISTER(HL)); + break; + + case 0x77: /* LD (IY+dd),A */ + adr = IY + (int8) RAM_PP(PC); + PUT_BYTE(adr, HIGH_REGISTER(AF)); + break; + + case 0x7c: /* LD A,IYH */ + SET_HIGH_REGISTER(AF, HIGH_REGISTER(IY)); + break; + + case 0x7d: /* LD A,IYL */ + SET_HIGH_REGISTER(AF, LOW_REGISTER(IY)); + break; + + case 0x7e: /* LD A,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + SET_HIGH_REGISTER(AF, GET_BYTE(adr)); + break; + + case 0x84: /* ADD A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x85: /* ADD A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x86: /* ADD A,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp; + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8c: /* ADC A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8d: /* ADC A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x8e: /* ADC A,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu + temp + TSTFLAG(C); + AF = addTable[sum] | cbitsZ80Table[acu ^ temp ^ sum]; + break; + + case 0x96: /* SUB (IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x94: /* SUB IYH */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9c: /* SBC A,IYH */ + temp = HIGH_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x95: /* SUB IYL */ + SETFLAG(C, 0);/* fall through, a bit less efficient but smaller code */ + + case 0x9d: /* SBC A,IYL */ + temp = LOW_REGISTER(IY); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0x9e: /* SBC A,(IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + acu = HIGH_REGISTER(AF); + sum = acu - temp - TSTFLAG(C); + AF = addTable[sum & 0xff] | cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xa4: /* AND IYH */ + AF = andTable[((AF & IY) >> 8) & 0xff]; + break; + + case 0xa5: /* AND IYL */ + AF = andTable[((AF >> 8) & IY) & 0xff]; + break; + + case 0xa6: /* AND (IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + AF = andTable[((AF >> 8) & GET_BYTE(adr)) & 0xff]; + break; + + case 0xac: /* XOR IYH */ + AF = xororTable[((AF ^ IY) >> 8) & 0xff]; + break; + + case 0xad: /* XOR IYL */ + AF = xororTable[((AF >> 8) ^ IY) & 0xff]; + break; + + case 0xae: /* XOR (IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + AF = xororTable[((AF >> 8) ^ GET_BYTE(adr)) & 0xff]; + break; + + case 0xb4: /* OR IYH */ + AF = xororTable[((AF | IY) >> 8) & 0xff]; + break; + + case 0xb5: /* OR IYL */ + AF = xororTable[((AF >> 8) | IY) & 0xff]; + break; + + case 0xb6: /* OR (IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + AF = xororTable[((AF >> 8) | GET_BYTE(adr)) & 0xff]; + break; + + case 0xbc: /* CP IYH */ + temp = HIGH_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbd: /* CP IYL */ + temp = LOW_REGISTER(IY); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xbe: /* CP (IY+dd) */ + adr = IY + (int8) RAM_PP(PC); + temp = GET_BYTE(adr); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + cbits2Z80Table[(acu ^ temp ^ sum) & 0x1ff]; + break; + + case 0xcb: /* CB prefix */ + adr = IY + (int8) RAM_PP(PC); + switch ((op = GET_BYTE(PC)) & 7) { + + case 0: + ++PC; + acu = HIGH_REGISTER(BC); + break; + + case 1: + ++PC; + acu = LOW_REGISTER(BC); + break; + + case 2: + ++PC; + acu = HIGH_REGISTER(DE); + break; + + case 3: + ++PC; + acu = LOW_REGISTER(DE); + break; + + case 4: + ++PC; + acu = HIGH_REGISTER(HL); + break; + + case 5: + ++PC; + acu = LOW_REGISTER(HL); + break; + + case 6: + ++PC; + acu = GET_BYTE(adr); + break; + + case 7: + ++PC; + acu = HIGH_REGISTER(AF); + break; + } + switch (op & 0xc0) { + + case 0x00: /* shift/rotate */ + switch (op & 0x38) { + + case 0x00:/* RLC */ + temp = (acu << 1) | (acu >> 7); + cbits = temp & 1; + goto cbshflg3; + + case 0x08:/* RRC */ + temp = (acu >> 1) | (acu << 7); + cbits = temp & 0x80; + goto cbshflg3; + + case 0x10:/* RL */ + temp = (acu << 1) | TSTFLAG(C); + cbits = acu & 0x80; + goto cbshflg3; + + case 0x18:/* RR */ + temp = (acu >> 1) | (TSTFLAG(C) << 7); + cbits = acu & 1; + goto cbshflg3; + + case 0x20:/* SLA */ + temp = acu << 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x28:/* SRA */ + temp = (acu >> 1) | (acu & 0x80); + cbits = acu & 1; + goto cbshflg3; + + case 0x30:/* SLIA */ + temp = (acu << 1) | 1; + cbits = acu & 0x80; + goto cbshflg3; + + case 0x38:/* SRL */ + temp = acu >> 1; + cbits = acu & 1; + cbshflg3: + AF = (AF & ~0xff) | rotateShiftTable[temp & 0xff] | !!cbits; + } + break; + + case 0x40: /* BIT */ + if (acu & (1 << ((op >> 3) & 7))) + AF = (AF & ~0xfe) | 0x10 | (((op & 0x38) == 0x38) << 7); + else AF = (AF & ~0xfe) | 0x54; + if ((op & 7) != 6) AF |= (acu & 0x28); + temp = acu; + break; + + case 0x80: /* RES */ + temp = acu & ~(1 << ((op >> 3) & 7)); + break; + + case 0xc0: /* SET */ + temp = acu | (1 << ((op >> 3) & 7)); + break; + } + switch (op & 7) { + + case 0: + SET_HIGH_REGISTER(BC, temp); + break; + + case 1: + SET_LOW_REGISTER(BC, temp); + break; + + case 2: + SET_HIGH_REGISTER(DE, temp); + break; + + case 3: + SET_LOW_REGISTER(DE, temp); + break; + + case 4: + SET_HIGH_REGISTER(HL, temp); + break; + + case 5: + SET_LOW_REGISTER(HL, temp); + break; + + case 6: + PUT_BYTE(adr, temp); + break; + + case 7: + SET_HIGH_REGISTER(AF, temp); + break; + } + break; + + case 0xe1: /* POP IY */ + POP(IY); + break; + + case 0xe3: /* EX (SP),IY */ + temp = IY; + POP(IY); + PUSH(temp); + break; + + case 0xe5: /* PUSH IY */ + PUSH(IY); + break; + + case 0xe9: /* JP (IY) */ + PC = IY; + break; + + case 0xf9: /* LD SP,IY */ + SP = IY; + break; + + default: /* ignore FD */ + CHECK_CPU_Z80; + PC--; + } + break; + + case 0xfe: /* CP nn */ + temp = RAM_PP(PC); + AF = (AF & ~0x28) | (temp & 0x28); + acu = HIGH_REGISTER(AF); + sum = acu - temp; + cbits = acu ^ temp ^ sum; + AF = (AF & ~0xff) | cpTable[sum & 0xff] | (temp & 0x28) | + (SET_PV) | cbits2Table[cbits & 0x1ff]; + break; + + case 0xff: /* RST 38H */ + PUSH(PC); + PC = 0x38; + } + } + end_decode: + + /* simulation halted */ + PC_S = (reason == STOP_OPCODE) ? PCX : PC; + AF_S = AF; + BC_S = BC; + DE_S = DE; + HL_S = HL; + IX_S = IX; + IY_S = IY; + SP_S = SP; + return reason; +} diff --git a/AltairZ80/altairz80_defs.h b/AltairZ80/altairz80_defs.h new file mode 100644 index 0000000..5e4050d --- /dev/null +++ b/AltairZ80/altairz80_defs.h @@ -0,0 +1,119 @@ +/* altairz80_defs.h: MITS Altair simulator definitions + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + + Based on work by Charles E Owen (c) 1997 +*/ + +#include "sim_defs.h" /* simulator definitions */ + +#define MAXBANKSIZE 65536 /* maximum memory size, a power of 2 */ +#define MAXBANKSIZELOG2 16 /* log2 of MAXBANKSIZE */ +#define MAXBANKS 16 /* max number of memory banks, a power of 2 */ +#define MAXBANKSLOG2 4 /* log2 of MAXBANKS */ +#define MAXMEMORY (MAXBANKS * MAXBANKSIZE) /* maximum, total memory size */ +#define ADDRMASK (MAXBANKSIZE - 1) /* address mask */ +#define ADDRMASKEXTENDED (MAXMEMORY - 1) /* extended address mask */ +#define BANKMASK (MAXBANKS - 1) /* bank mask */ +#define MEMORYSIZE (cpu_unit.capac) /* actual memory size */ +#define KB 1024 /* kilo byte */ +#define KBLOG2 10 /* log2 of KB */ +#define ALTAIR_ROM_LOW 0xff00 /* start address of regular Altair ROM */ +#define RESOURCE_TYPE_MEMORY 1 +#define RESOURCE_TYPE_IO 2 + +#define NUM_OF_DSK 8 /* NUM_OF_DSK must be power of two */ +#define LDA_INSTRUCTION 0x3e /* op-code for LD A,<8-bit value> instruction */ +#define UNIT_NO_OFFSET_1 0x37 /* LD A, */ +#define UNIT_NO_OFFSET_2 0xb4 /* LD a,80h | */ + +#define CHIP_TYPE_8080 0 +#define CHIP_TYPE_Z80 1 +#define CHIP_TYPE_8086 2 + +/* simulator stop codes */ +#define STOP_HALT 0 /* HALT */ +#define STOP_IBKPT 1 /* breakpoint (program counter) */ +#define STOP_MEM 2 /* breakpoint (memory access) */ +#define STOP_OPCODE 3 /* invalid operation encountered (8080, Z80, 8086) */ + +#define UNIT_CPU_V_OPSTOP (UNIT_V_UF+0) /* stop on invalid operation */ +#define UNIT_CPU_OPSTOP (1 << UNIT_CPU_V_OPSTOP) +#define UNIT_CPU_V_BANKED (UNIT_V_UF+1) /* banked memory is used */ +#define UNIT_CPU_BANKED (1 << UNIT_CPU_V_BANKED) +#define UNIT_CPU_V_ALTAIRROM (UNIT_V_UF+2) /* ALTAIR ROM exists */ +#define UNIT_CPU_ALTAIRROM (1 << UNIT_CPU_V_ALTAIRROM) +#define UNIT_CPU_V_VERBOSE (UNIT_V_UF+3) /* warn if ROM is written to */ +#define UNIT_CPU_VERBOSE (1 << UNIT_CPU_V_VERBOSE) +#define UNIT_CPU_V_MMU (UNIT_V_UF+4) /* use MMU and slower CPU */ +#define UNIT_CPU_MMU (1 << UNIT_CPU_V_MMU) +#define UNIT_CPU_V_STOPONHALT (UNIT_V_UF+5) /* stop simulation on HALT */ +#define UNIT_CPU_STOPONHALT (1 << UNIT_CPU_V_STOPONHALT) + +#ifdef CPUSWITCHER +#define UNIT_CPU_V_SWITCHER (UNIT_V_UF+6) /* switcher 8086 <--> 8080/Z80 enabled */ +#define UNIT_CPU_SWITCHER (1 << UNIT_CPU_V_SWITCHER) +#endif + +#define UNIX_PLATFORM (defined (__linux) || defined(__NetBSD__) \ + || defined (__OpenBSD__) || defined (__FreeBSD__) || defined (__APPLE__)) + +#define ADDRESS_FORMAT "[0x%05x]" +#define PC_FORMAT "\n" ADDRESS_FORMAT " " +#define MESSAGE_1(p1) \ + sprintf(messageBuffer,PC_FORMAT p1,PCX); printMessage() +#define MESSAGE_2(p1,p2) \ + sprintf(messageBuffer,PC_FORMAT p1,PCX,p2); printMessage() +#define MESSAGE_3(p1,p2,p3) \ + sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3); printMessage() +#define MESSAGE_4(p1,p2,p3,p4) \ + sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4); printMessage() +#define MESSAGE_5(p1,p2,p3,p4,p5) \ + sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4,p5); printMessage() +#define MESSAGE_6(p1,p2,p3,p4,p5,p6) \ + sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4,p5,p6); printMessage() +#define MESSAGE_7(p1,p2,p3,p4,p5,p6,p7) \ + sprintf(messageBuffer,PC_FORMAT p1,PCX,p2,p3,p4,p5,p6,p7); printMessage() + +/* use NLP for new line printing while the simulation is running */ +#if UNIX_PLATFORM +#define NLP "\r\n" +#else +#define NLP "\n" +#endif + +#define TRACE_PRINT(level, args) if(trace_level & level) { \ + printf args; \ + } + +#if defined (__MWERKS__) && defined (macintosh) +#define __FUNCTION__ __FILE__ +#endif + +typedef struct { + uint32 mem_base; /* Memory Base Address */ + uint32 mem_size; /* Memory Address space requirement */ + uint32 io_base; /* I/O Base Address */ + uint32 io_size; /* I/O Address Space requirement */ +} PNP_INFO; diff --git a/AltairZ80/altairz80_dsk.c b/AltairZ80/altairz80_dsk.c new file mode 100644 index 0000000..bebfe2b --- /dev/null +++ b/AltairZ80/altairz80_dsk.c @@ -0,0 +1,548 @@ +/* altairz80_dsk.c: MITS Altair 88-DISK Simulator + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + + Based on work by Charles E Owen (c) 1997 + + The 88_DISK is a 8-inch floppy controller which can control up + to 16 daisy-chained Pertec FD-400 hard-sectored floppy drives. + Each diskette has physically 77 tracks of 32 137-byte sectors + each. + + The controller is interfaced to the CPU by use of 3 I/O addresses, + standardly, these are device numbers 10, 11, and 12 (octal). + + Address Mode Function + ------- ---- -------- + + 10 Out Selects and enables Controller and Drive + 10 In Indicates status of Drive and Controller + 11 Out Controls Disk Function + 11 In Indicates current sector position of disk + 12 Out Write data + 12 In Read data + + Drive Select Out (Device 10 OUT): + + +---+---+---+---+---+---+---+---+ + | C | X | X | X | Device | + +---+---+---+---+---+---+---+---+ + + C = If this bit is 1, the disk controller selected by 'device' is + cleared. If the bit is zero, 'device' is selected as the + device being controlled by subsequent I/O operations. + X = not used + Device = value zero thru 15, selects drive to be controlled. + + Drive Status In (Device 10 IN): + + +---+---+---+---+---+---+---+---+ + | R | Z | I | X | X | H | M | W | + +---+---+---+---+---+---+---+---+ + + W - When 0, write circuit ready to write another byte. + M - When 0, head movement is allowed + H - When 0, indicates head is loaded for read/write + X - not used (will be 0) + I - When 0, indicates interrupts enabled (not used by this simulator) + Z - When 0, indicates head is on track 0 + R - When 0, indicates that read circuit has new byte to read + + Drive Control (Device 11 OUT): + + +---+---+---+---+---+---+---+---+ + | W | C | D | E | U | H | O | I | + +---+---+---+---+---+---+---+---+ + + I - When 1, steps head IN one track + O - When 1, steps head OUT one track + H - When 1, loads head to drive surface + U - When 1, unloads head + E - Enables interrupts (ignored by this simulator) + D - Disables interrupts (ignored by this simulator) + C - When 1 lowers head current (ignored by this simulator) + W - When 1, starts Write Enable sequence: W bit on device 10 + (see above) will go 1 and data will be read from port 12 + until 137 bytes have been read by the controller from + that port. The W bit will go off then, and the sector data + will be written to disk. Before you do this, you must have + stepped the track to the desired number, and waited until + the right sector number is presented on device 11 IN, then + set this bit. + + Sector Position (Device 11 IN): + + As the sectors pass by the read head, they are counted and the + number of the current one is available in this register. + + +---+---+---+---+---+---+---+---+ + | X | X | Sector Number | T | + +---+---+---+---+---+---+---+---+ + + X = Not used + Sector number = binary of the sector number currently under the + head, 0-31. + T = Sector True, is a 1 when the sector is positioned to read or + write. + +*/ + +#include "altairz80_defs.h" +#include + +#define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK) +#define UNIT_V_DSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_DSK_VERBOSE (1 << UNIT_V_DSK_VERBOSE) +#define DSK_SECTSIZE 137 /* size of sector */ +#define DSK_SECT 32 /* sectors per track */ +#define MAX_TRACKS 254 /* number of tracks, + original Altair has 77 tracks only */ +#define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT) +#define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS) +#define TRACE_IN_OUT 1 +#define TRACE_READ_WRITE 2 +#define TRACE_SECTOR_STUCK 4 +#define TRACE_TRACK_STUCK 8 +#define NUM_OF_DSK_MASK (NUM_OF_DSK - 1) +#define BOOTROM_SIZE_DSK 256 /* size of boot rom */ + + +int32 dsk10(const int32 port, const int32 io, const int32 data); +int32 dsk11(const int32 port, const int32 io, const int32 data); +int32 dsk12(const int32 port, const int32 io, const int32 data); +static t_stat dsk_boot(int32 unitno, DEVICE *dptr); +static t_stat dsk_reset(DEVICE *dptr); +static t_stat dsk_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc); + +extern REG *sim_PC; +extern UNIT cpu_unit; +extern char messageBuffer[]; +extern uint32 PCX; + +extern void printMessage(void); +extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM); +void install_ALTAIRbootROM(void); + +/* global data on status */ + +/* currently selected drive (values are 0 .. NUM_OF_DSK) + current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */ +static int32 current_disk = NUM_OF_DSK; +static int32 current_track [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static int32 current_sector [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static int32 current_byte [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static int32 current_flag [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static uint8 tracks [NUM_OF_DSK] = { MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, + MAX_TRACKS, MAX_TRACKS, MAX_TRACKS, MAX_TRACKS }; +static int32 trace_level = 0; +static int32 in9_count = 0; +static int32 in9_message = FALSE; +static int32 dirty = FALSE; /* TRUE when buffer has unwritten data in it */ +static int32 warnLevelDSK = 3; +static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static int32 warnDSK10 = 0; +static int32 warnDSK11 = 0; +static int32 warnDSK12 = 0; +static int8 dskbuf[DSK_SECTSIZE]; /* data Buffer */ + +/* Altair MITS modified BOOT EPROM, fits in upper 256 byte of memory */ +int32 bootrom_dsk[BOOTROM_SIZE_DSK] = { + 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* ff00-ff07 */ + 0xc2, 0x05, 0xff, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* ff08-ff0f */ + 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* ff10-ff17 */ + 0xff, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* ff18-ff1f */ + 0x21, 0x00, 0x5c, 0x11, 0x33, 0xff, 0x0e, 0x88, /* ff20-ff27 */ + 0x1a, 0x77, 0x13, 0x23, 0x0d, 0xc2, 0x28, 0xff, /* ff28-ff2f */ + 0xc3, 0x00, 0x5c, 0x31, 0x21, 0x5d, 0x3e, 0x00, /* ff30-ff37 */ + 0xd3, 0x08, 0x3e, 0x04, 0xd3, 0x09, 0xc3, 0x19, /* ff38-ff3f */ + 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, 0x0e, 0x5c, /* ff40-ff47 */ + 0x3e, 0x02, 0xd3, 0x09, 0xdb, 0x08, 0xe6, 0x40, /* ff48-ff4f */ + 0xc2, 0x0e, 0x5c, 0x11, 0x00, 0x00, 0x06, 0x08, /* ff50-ff57 */ + 0xc5, 0xd5, 0x11, 0x86, 0x80, 0x21, 0x88, 0x5c, /* ff58-ff5f */ + 0xdb, 0x09, 0x1f, 0xda, 0x2d, 0x5c, 0xe6, 0x1f, /* ff60-ff67 */ + 0xb8, 0xc2, 0x2d, 0x5c, 0xdb, 0x08, 0xb7, 0xfa, /* ff68-ff6f */ + 0x39, 0x5c, 0xdb, 0x0a, 0x77, 0x23, 0x1d, 0xc2, /* ff70-ff77 */ + 0x39, 0x5c, 0xd1, 0x21, 0x8b, 0x5c, 0x06, 0x80, /* ff78-ff7f */ + 0x7e, 0x12, 0x23, 0x13, 0x05, 0xc2, 0x4d, 0x5c, /* ff80-ff87 */ + 0xc1, 0x21, 0x00, 0x5c, 0x7a, 0xbc, 0xc2, 0x60, /* ff88-ff8f */ + 0x5c, 0x7b, 0xbd, 0xd2, 0x80, 0x5c, 0x04, 0x04, /* ff90-ff97 */ + 0x78, 0xfe, 0x20, 0xda, 0x25, 0x5c, 0x06, 0x01, /* ff98-ff9f */ + 0xca, 0x25, 0x5c, 0xdb, 0x08, 0xe6, 0x02, 0xc2, /* ffa0-ffa7 */ + 0x70, 0x5c, 0x3e, 0x01, 0xd3, 0x09, 0x06, 0x00, /* ffa8-ffaf */ + 0xc3, 0x25, 0x5c, 0x3e, 0x80, 0xd3, 0x08, 0xfb, /* ffb0-ffb7 */ + 0xc3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffb8-ffbf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc0-ffc7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffc8-ffcf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd0-ffd7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffd8-ffdf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe0-ffe7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* ffe8-ffef */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff0-fff7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* fff8-ffff */ +}; + +/* 88DSK Standard I/O Data Structures */ + +static UNIT dsk_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) } +}; + +static REG dsk_reg[] = { + { DRDATA (DISK, current_disk, 4) }, + { BRDATA (CURTRACK, current_track, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { BRDATA (CURSECTOR, current_sector, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { BRDATA (CURBYTE, current_byte, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { BRDATA (CURFLAG, current_flag, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { BRDATA (TRACKS, tracks, 10, 8, NUM_OF_DSK), REG_CIRC }, + { HRDATA (TRACELEVEL, trace_level, 16) }, + { DRDATA (IN9COUNT, in9_count, 4), REG_RO }, + { DRDATA (IN9MESSAGE, in9_message, 4), REG_RO }, + { DRDATA (DIRTY, dirty, 4), REG_RO }, + { DRDATA (DSKWL, warnLevelDSK, 32) }, + { BRDATA (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { BRDATA (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { DRDATA (WARNDSK10, warnDSK10, 4), REG_RO }, + { DRDATA (WARNDSK11, warnDSK11, 4), REG_RO }, + { DRDATA (WARNDSK12, warnDSK12, 4), REG_RO }, + { BRDATA (DISKBUFFER, dskbuf, 10, 8, DSK_SECTSIZE), REG_CIRC + REG_RO }, + { NULL } +}; + +static MTAB dsk_mod[] = { + { UNIT_DSK_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_DSK_WLK, UNIT_DSK_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_DSK_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_DSK_VERBOSE, UNIT_DSK_VERBOSE, "VERBOSE", "VERBOSE", &dsk_set_verbose }, + { 0 } +}; + +DEVICE dsk_dev = { + "DSK", dsk_unit, dsk_reg, dsk_mod, + 8, 10, 31, 1, 8, 8, + NULL, NULL, &dsk_reset, + &dsk_boot, NULL, NULL, + NULL, (DEV_DISABLE), 0, + NULL, NULL, NULL +}; + +static void resetDSKWarningFlags(void) { + int32 i; + for (i = 0; i < NUM_OF_DSK; i++) { + warnLock[i] = 0; + warnAttached[i] = 0; + } + warnDSK10 = 0; + warnDSK11 = 0; + warnDSK12 = 0; +} + +static t_stat dsk_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) { + resetDSKWarningFlags(); + return SCPE_OK; +} + +/* returns TRUE iff there exists a disk with VERBOSE */ +static int32 hasVerbose(void) { + int32 i; + for (i = 0; i < NUM_OF_DSK; i++) + if (((dsk_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) return TRUE; + return FALSE; +} + +static char* selectInOut(const int32 io) { + return io == 0 ? "IN" : "OUT"; +} + +/* service routines to handle simulator functions */ +/* reset routine */ + +static t_stat dsk_reset(DEVICE *dptr) { + resetDSKWarningFlags(); + current_disk = NUM_OF_DSK; + trace_level = 0; + in9_count = 0; + in9_message = FALSE; + return SCPE_OK; +} + +void install_ALTAIRbootROM(void) { + assert(install_bootrom(bootrom_dsk, BOOTROM_SIZE_DSK, ALTAIR_ROM_LOW, TRUE) == SCPE_OK); +} + +/* The boot routine modifies the boot ROM in such a way that subsequently + the specified disk is used for boot purposes. +*/ +static t_stat dsk_boot(int32 unitno, DEVICE *dptr) { + if (cpu_unit.flags & (UNIT_CPU_ALTAIRROM | UNIT_CPU_BANKED)) { + /* check whether we are really modifying an LD A,<> instruction */ + if ((bootrom_dsk[UNIT_NO_OFFSET_1 - 1] == LDA_INSTRUCTION) && (bootrom_dsk[UNIT_NO_OFFSET_2 - 1] == LDA_INSTRUCTION)) { + bootrom_dsk[UNIT_NO_OFFSET_1] = unitno & 0xff; /* LD A, */ + bootrom_dsk[UNIT_NO_OFFSET_2] = 0x80 | (unitno & 0xff); /* LD a,80h | */ + } + else { /* Attempt to modify non LD A,<> instructions is refused. */ + printf("Incorrect boot ROM offsets detected.\n"); + return SCPE_IERR; + } + install_ALTAIRbootROM(); /* install modified ROM */ + } + *((int32 *) sim_PC->loc) = ALTAIR_ROM_LOW; + return SCPE_OK; +} + +static int32 dskseek(const UNIT *xptr) { + return fseek(xptr -> fileref, DSK_TRACSIZE * current_track[current_disk] + + DSK_SECTSIZE * current_sector[current_disk], SEEK_SET); +} + +/* precondition: current_disk < NUM_OF_DSK */ +static void writebuf(void) { + int32 i, rtn; + UNIT *uptr; + i = current_byte[current_disk]; /* null-fill rest of sector if any */ + while (i < DSK_SECTSIZE) + dskbuf[i++] = 0; + uptr = dsk_dev.units + current_disk; + if (((uptr -> flags) & UNIT_DSK_WLK) == 0) { /* write enabled */ + if (trace_level & TRACE_READ_WRITE) { + MESSAGE_4("OUT 0x0a (WRITE) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]); + } + if (dskseek(uptr)) { + MESSAGE_4("fseek failed D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]); + } + rtn = fwrite(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref); + if (rtn != 1) { + MESSAGE_4("fwrite failed T%d S%d Return=%d", current_track[current_disk], current_sector[current_disk], rtn); + } + } + else if ( ((uptr -> flags) & UNIT_DSK_VERBOSE) && (warnLock[current_disk] < warnLevelDSK) ) { + /* write locked - print warning message if required */ + warnLock[current_disk]++; +/*05*/ MESSAGE_2("Attempt to write to locked DSK%d - ignored.", current_disk); + } + current_flag[current_disk] &= 0xfe; /* ENWD off */ + current_byte[current_disk] = 0xff; + dirty = FALSE; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. +*/ + +/* Disk Controller Status/Select */ + +/* IMPORTANT: The status flags read by port 8 IN instruction are + INVERTED, that is, 0 is true and 1 is false. To handle this, the + simulator keeps it's own status flags as 0=false, 1=true; and + returns the COMPLEMENT of the status flags when read. This makes + setting/testing of the flag bits more logical, yet meets the + simulation requirement that they are reversed in hardware. +*/ + +int32 dsk10(const int32 port, const int32 io, const int32 data) { + int32 current_disk_flags; + in9_count = 0; + if (io == 0) { /* IN: return flags */ + if (current_disk >= NUM_OF_DSK) { + if (hasVerbose() && (warnDSK10 < warnLevelDSK)) { + warnDSK10++; +/*01*/ MESSAGE_1("Attempt of IN 0x08 on unattached disk - ignored."); + } + return 0xff; /* no drive selected - can do nothing */ + } + return (~current_flag[current_disk]) & 0xff; /* return the COMPLEMENT! */ + } + + /* OUT: Controller set/reset/enable/disable */ + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + if (trace_level & TRACE_IN_OUT) { + MESSAGE_2("OUT 0x08: %x", data); + } + current_disk = data & NUM_OF_DSK_MASK; /* 0 <= current_disk < NUM_OF_DSK */ + current_disk_flags = (dsk_dev.units + current_disk) -> flags; + if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */ + if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) { + warnAttached[current_disk]++; +/*02*/ MESSAGE_2("Attempt to select unattached DSK%d - ignored.", current_disk); + } + current_disk = NUM_OF_DSK; + } + else { + current_sector[current_disk] = 0xff; /* reset internal counters */ + current_byte[current_disk] = 0xff; + current_flag[current_disk] = data & 0x80 ? 0 /* disable drive */ : + (current_track[current_disk] == 0 ? 0x5a /* enable: head move true, track 0 if there */ : + 0x1a); /* enable: head move true */ + } + return 0; /* ignored since OUT */ +} + +/* Disk Drive Status/Functions */ + +int32 dsk11(const int32 port, const int32 io, const int32 data) { + if (current_disk >= NUM_OF_DSK) { + if (hasVerbose() && (warnDSK11 < warnLevelDSK)) { + warnDSK11++; +/*03*/ MESSAGE_2("Attempt of %s 0x09 on unattached disk - ignored.", selectInOut(io)); + } + return 0; /* no drive selected - can do nothing */ + } + + /* now current_disk < NUM_OF_DSK */ + if (io == 0) { /* read sector position */ + in9_count++; + if ((trace_level & TRACE_SECTOR_STUCK) && (in9_count > 2 * DSK_SECT) && (!in9_message)) { + in9_message = TRUE; + MESSAGE_2("Looping on sector find %d.", current_disk); + } + if (trace_level & TRACE_IN_OUT) { + MESSAGE_1("IN 0x09"); + } + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + if (current_flag[current_disk] & 0x04) { /* head loaded? */ + current_sector[current_disk]++; + if (current_sector[current_disk] >= DSK_SECT) + current_sector[current_disk] = 0; + current_byte[current_disk] = 0xff; + return (((current_sector[current_disk] << 1) & 0x3e) /* return 'sector true' bit = 0 (true) */ + | 0xc0); /* set on 'unused' bits */ + } else return 0; /* head not loaded - return 0 */ + } + + in9_count = 0; + /* drive functions */ + + if (trace_level & TRACE_IN_OUT) { + MESSAGE_2("OUT 0x09: %x", data); + } + if (data & 0x01) { /* step head in */ + if ((trace_level & TRACE_TRACK_STUCK) && (current_track[current_disk] == (tracks[current_disk] - 1))) { + MESSAGE_2("Unnecessary step in for disk %d", current_disk); + } + current_track[current_disk]++; + if (current_track[current_disk] > (tracks[current_disk] - 1)) + current_track[current_disk] = (tracks[current_disk] - 1); + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + current_sector[current_disk] = 0xff; + current_byte[current_disk] = 0xff; + } + + if (data & 0x02) { /* step head out */ + if ((trace_level & TRACE_TRACK_STUCK) && (current_track[current_disk] == 0)) { + MESSAGE_2("Unnecessary step out for disk %d", current_disk); + } + current_track[current_disk]--; + if (current_track[current_disk] < 0) { + current_track[current_disk] = 0; + current_flag[current_disk] |= 0x40; /* track 0 if there */ + } + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + current_sector[current_disk] = 0xff; + current_byte[current_disk] = 0xff; + } + + if (dirty) /* implies that current_disk < NUM_OF_DSK */ + writebuf(); + + if (data & 0x04) { /* head load */ + current_flag[current_disk] |= 0x04; /* turn on head loaded bit */ + current_flag[current_disk] |= 0x80; /* turn on 'read data available' */ + } + + if (data & 0x08) { /* head unload */ + current_flag[current_disk] &= 0xfb; /* turn off 'head loaded' bit */ + current_flag[current_disk] &= 0x7f; /* turn off 'read data available' */ + current_sector[current_disk] = 0xff; + current_byte[current_disk] = 0xff; + } + + /* interrupts & head current are ignored */ + + if (data & 0x80) { /* write sequence start */ + current_byte[current_disk] = 0; + current_flag[current_disk] |= 0x01; /* enter new write data on */ + } + return 0; /* ignored since OUT */ +} + +/* Disk Data In/Out */ + +int32 dsk12(const int32 port, const int32 io, const int32 data) { + int32 i; + UNIT *uptr; + + if (current_disk >= NUM_OF_DSK) { + if (hasVerbose() && (warnDSK12 < warnLevelDSK)) { + warnDSK12++; +/*04*/ MESSAGE_2("Attempt of %s 0x0a on unattached disk - ignored.", selectInOut(io)); + } + return 0; + } + + /* now current_disk < NUM_OF_DSK */ + in9_count = 0; + uptr = dsk_dev.units + current_disk; + if (io == 0) { + if (current_byte[current_disk] >= DSK_SECTSIZE) { + /* physically read the sector */ + if (trace_level & TRACE_READ_WRITE) { + MESSAGE_4("IN 0x0a (READ) D%d T%d S%d", current_disk, current_track[current_disk], current_sector[current_disk]); + } + for (i = 0; i < DSK_SECTSIZE; i++) + dskbuf[i] = 0; + dskseek(uptr); + fread(dskbuf, DSK_SECTSIZE, 1, uptr -> fileref); + current_byte[current_disk] = 0; + } + return dskbuf[current_byte[current_disk]++] & 0xff; + } + else { + if (current_byte[current_disk] >= DSK_SECTSIZE) + writebuf(); /* from above we have that current_disk < NUM_OF_DSK */ + else { + dirty = TRUE; /* this guarantees for the next call to writebuf that current_disk < NUM_OF_DSK */ + dskbuf[current_byte[current_disk]++] = data & 0xff; + } + return 0; /* ignored since OUT */ + } +} diff --git a/AltairZ80/altairz80_hdsk.c b/AltairZ80/altairz80_hdsk.c new file mode 100644 index 0000000..5a01f6c --- /dev/null +++ b/AltairZ80/altairz80_hdsk.c @@ -0,0 +1,643 @@ +/* altairz80_hdsk.c: simulated hard disk device to increase capacity + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + + Contains code from Howard M. Harte for defining and changing disk geometry. +*/ + +#include "altairz80_defs.h" +#include + +/* The following routines are based on work from Howard M. Harte */ +static t_stat set_geom(UNIT *uptr, int32 val, char *cptr, void *desc); +static t_stat show_geom(FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat set_format(UNIT *uptr, int32 val, char *cptr, void *desc); +static t_stat show_format(FILE *st, UNIT *uptr, int32 val, void *desc); + +static t_stat hdsk_reset(DEVICE *dptr); +static t_stat hdsk_attach(UNIT *uptr, char *cptr); + +#define UNIT_V_HDSK_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_HDSK_WLK (1 << UNIT_V_HDSK_WLK) +#define UNIT_V_HDSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_HDSK_VERBOSE (1 << UNIT_V_HDSK_VERBOSE) +#define HDSK_MAX_SECTOR_SIZE 1024 /* maximum size of a sector */ +#define HDSK_SECTOR_SIZE u5 /* size of sector */ +#define HDSK_SECTORS_PER_TRACK u4 /* sectors per track */ +#define HDSK_NUMBER_OF_TRACKS u3 /* number of tracks */ +#define HDSK_FORMAT_TYPE u6 /* Disk Format Type */ +#define HDSK_CAPACITY (2048*32*128) /* Default Altair HDSK Capacity */ +#define HDSK_NUMBER 8 /* number of hard disks */ +#define CPM_OK 0 /* indicates to CP/M everything ok */ +#define CPM_ERROR 1 /* indicates to CP/M an error condition */ +#define CPM_EMPTY 0xe5 /* default value for non-existing bytes */ +#define HDSK_NONE 0 +#define HDSK_RESET 1 +#define HDSK_READ 2 +#define HDSK_WRITE 3 +#define HDSK_PARAM 4 +#define HDSK_BOOT_ADDRESS 0x5c00 +#define DPB_NAME_LENGTH 15 +#define BOOTROM_SIZE_HDSK 256 + +extern char messageBuffer[]; +extern uint32 PCX; +extern REG *sim_PC; +extern UNIT cpu_unit; + +extern void install_ALTAIRbootROM(void); +extern void printMessage(void); +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); +extern uint8 GetBYTEWrapper(const uint32 Addr); +extern t_stat install_bootrom(int32 bootrom[], int32 size, int32 addr, int32 makeROM); +extern int32 bootrom_dsk[]; +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +static t_stat hdsk_boot(int32 unitno, DEVICE *dptr); +int32 hdsk_io(const int32 port, const int32 io, const int32 data); + +static int32 hdskLastCommand = HDSK_NONE; +static int32 hdskCommandPosition = 0; +static int32 paramcount = 0; +static int32 selectedDisk; +static int32 selectedSector; +static int32 selectedTrack; +static int32 selectedDMA; +static int32 trace_level = 0; + +typedef struct { + char name[DPB_NAME_LENGTH + 1]; /* name of CP/M disk parameter block */ + t_addr capac; /* capacity */ + uint16 spt; /* sectors per track */ + uint8 bsh; /* data allocation block shift factor */ + uint8 blm; /* data allocation block mask */ + uint8 exm; /* extent mask */ + uint16 dsm; /* maximum data block number */ + uint16 drm; /* total number of directory entries */ + uint8 al0; /* determine reserved directory blocks */ + uint8 al1; /* determine reserved directory blocks */ + uint16 cks; /* size of directory check vector */ + uint16 off; /* number of reserved tracks */ + uint8 psh; /* physical record shift factor, CP/M 3 */ + uint8 phm; /* physical record mask, CP/M 3 */ +} DPB; + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ +} HDSK_INFO; + +static HDSK_INFO hdsk_info_data = { { 0x0000, 0, 0xFD, 1 } }; +/* static HDSK_INFO *hdsk_info = &hdsk_info_data; */ + +/* Note in the following CKS = 0 for fixed media which are not supposed to be changed while CP/M is executing */ +static DPB dpb[] = { +/* name capac spt bsh blm exm dsm drm al0 al1 cks off psh phm */ + { "HDSK", HDSK_CAPACITY, 32, 0x05, 0x1F, 0x01, 0x07f9, 0x03FF, 0xFF, 0x00, 0x0000, 0x0006, 0x00, 0x00 }, /* AZ80 HDSK */ + { "EZ80FL", 131072, 32, 0x03, 0x07, 0x00, 127, 0x003E, 0xC0, 0x00, 0x0000, 0x0000, 0x02, 0x03 }, /* 128K FLASH */ + { "P112", 1474560, 72, 0x04, 0x0F, 0x00, 710, 0x00FE, 0xF0, 0x00, 0x0000, 0x0002, 0x02, 0x03 }, /* 1.44M P112 */ + { "SU720", 737280, 36, 0x04, 0x0F, 0x00, 354, 0x007E, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* 720K Super I/O */ + { "OSB1", 102400, 20, 0x04, 0x0F, 0x01, 45, 0x003F, 0x80, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS SD */ + { "OSB2", 204800, 40, 0x03, 0x07, 0x00, 184, 0x003F, 0xC0, 0x00, 0x0000, 0x0003, 0x02, 0x03 }, /* Osborne1 5.25" SS DD */ + { "NSSS1", 179200, 40, 0x03, 0x07, 0x00, 0xA4, 0x003F, 0xC0, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 1 */ + { "NSSS2", 179200, 40, 0x04, 0x0F, 0x01, 0x51, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar SSDD Format 2 */ + { "NSDS2", 358400, 40, 0x04, 0x0F, 0x01, 0xA9, 0x003F, 0x80, 0x00, 0x0010, 0x0002, 0x02, 0x03 }, /* Northstar DSDD Format 2 */ + { "VGSS", 315392, 32, 0x04, 0x0F, 0x00, 149, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* Vector SS SD */ + { "VGDS", 632784, 32, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0004, 0x02, 0x03 }, /* Vector DS SD */ + /* Note on DISK1A Images: this is a bit of a mess. The first track on the disk is 128x26 bytes (SD) and to make this work + I had to "move" the data from 0x2d00 in the DSK image file down to 0x4000 (2-tracks in). I used WinHex to do it. */ + { "DISK1A", 630784, 64, 0x04, 0x0F, 0x00, 299, 0x007F, 0xC0, 0x00, 0x0020, 0x0002, 0x02, 0x03 }, /* CompuPro Disk1A 8" SS SD */ + { "SSSD8", 256256, 26, 0x03, 0x07, 0x00, 242, 0x003F, 0xC0, 0x00, 0x0000, 0x0002, 0x00, 0x00 }, /* Standard 8" SS SD */ + { "", 0 } +}; + +static UNIT hdsk_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, HDSK_CAPACITY) } +}; + +static REG hdsk_reg[] = { + { DRDATA (HDCMD, hdskLastCommand, 32), REG_RO }, + { DRDATA (HDPOS, hdskCommandPosition, 32), REG_RO }, + { DRDATA (HDDSK, selectedDisk, 32), REG_RO }, + { DRDATA (HDSEC, selectedSector, 32), REG_RO }, + { DRDATA (HDTRK, selectedTrack, 32), REG_RO }, + { DRDATA (HDDMA, selectedDMA, 32), REG_RO }, + { HRDATA (TRACELEVEL, trace_level, 16), }, + { NULL } +}; + +static MTAB hdsk_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { MTAB_XTD|MTAB_VUN|MTAB_VAL, 0, "FORMAT", "FORMAT", &set_format, &show_format, NULL }, + { UNIT_HDSK_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_HDSK_WLK, UNIT_HDSK_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_HDSK_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_HDSK_VERBOSE, UNIT_HDSK_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { MTAB_XTD|MTAB_VUN|MTAB_VAL, 0, "GEOM", "GEOM", &set_geom, &show_geom, NULL }, + { 0 } +}; + +DEVICE hdsk_dev = { + "HDSK", hdsk_unit, hdsk_reg, hdsk_mod, + 8, 10, 31, 1, 8, 8, + NULL, NULL, &hdsk_reset, + &hdsk_boot, &hdsk_attach, NULL, + &hdsk_info_data, (DEV_DISABLE), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat hdsk_reset(DEVICE *dptr) { + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + if (dptr->flags & DEV_DIS) { + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdsk_io, TRUE); + } else { + /* Connect HDSK at base address */ + if (sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &hdsk_io, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); + dptr->flags |= DEV_DIS; + return SCPE_ARG; + } + } + return SCPE_OK; +} + +/* Attach routine */ +static t_stat hdsk_attach(UNIT *uptr, char *cptr) { + t_stat r; + uint32 i; + char unitChar; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Step 1: Determine capacity of this disk */ + uptr -> capac = sim_fsize(uptr -> fileref); /* the file length is a good candidate */ + if (uptr -> capac == 0) { /* file does not exist or has length 0 */ + uptr -> capac = uptr -> HDSK_NUMBER_OF_TRACKS * + uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE; + if (uptr -> capac == 0) + uptr -> capac = HDSK_CAPACITY; + } /* post condition: uptr -> capac > 0 */ + assert(uptr -> capac); + + /* Step 2: Determine format based on disk capacity */ + uptr -> HDSK_FORMAT_TYPE = -1; /* default to unknown format type */ + for (i = 0; dpb[i].capac != 0; i++) { /* find disk parameter block */ + if (dpb[i].capac == uptr -> capac) { /* found if correct capacity */ + uptr -> HDSK_FORMAT_TYPE = i; + break; + } + } + + /* Step 3: Set number of sectors per track and sector size */ + if (uptr -> HDSK_FORMAT_TYPE == -1) { /* Case 1: no disk parameter block found*/ + for (i = 0; i < hdsk_dev.numunits; i++) /* find affected unit number */ + if (&hdsk_unit[i] == uptr) + break; /* found */ + unitChar = '0' + i; + uptr -> HDSK_FORMAT_TYPE = 0; + printf("HDSK%c: WARNING: Unsupported disk capacity, assuming HDSK type with capacity %iKB.\n", + unitChar, uptr -> capac / 1000); + uptr -> flags |= UNIT_HDSK_WLK; + printf("HDSK%c: WARNING: Forcing WRTLCK.\n", unitChar); + /* check whether capacity corresponds to setting of tracks, sectors per track and sector size */ + if (uptr -> capac != (uptr -> HDSK_NUMBER_OF_TRACKS * + uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE)) { + printf("HDSK%c: WARNING: Fixing geometry.\n", unitChar); + if (uptr -> HDSK_SECTORS_PER_TRACK == 0) uptr -> HDSK_SECTORS_PER_TRACK = 32; + if (uptr -> HDSK_SECTOR_SIZE == 0) uptr -> HDSK_SECTOR_SIZE = 128; + } + } + else { /* Case 2: disk parameter block found */ + uptr -> HDSK_SECTORS_PER_TRACK = dpb[uptr -> HDSK_FORMAT_TYPE].spt >> dpb[uptr -> HDSK_FORMAT_TYPE].psh; + uptr -> HDSK_SECTOR_SIZE = (128 << dpb[uptr -> HDSK_FORMAT_TYPE].psh); + } + assert(uptr -> HDSK_SECTORS_PER_TRACK && uptr -> HDSK_SECTOR_SIZE); + + /* Step 4: Number of tracks is smallest number to accomodate capacity */ + uptr -> HDSK_NUMBER_OF_TRACKS = (uptr -> capac + uptr -> HDSK_SECTORS_PER_TRACK * + uptr -> HDSK_SECTOR_SIZE - 1) / (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE); + assert( ( (t_addr) ((uptr -> HDSK_NUMBER_OF_TRACKS - 1) * uptr -> HDSK_SECTORS_PER_TRACK * + uptr -> HDSK_SECTOR_SIZE) < uptr -> capac) && + (uptr -> capac <= (t_addr) (uptr -> HDSK_NUMBER_OF_TRACKS * + uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE) ) ); + return SCPE_OK; +} + +/* Set disk geometry routine */ +static t_stat set_geom(UNIT *uptr, int32 val, char *cptr, void *desc) { + uint32 numberOfTracks, numberOfSectors, sectorSize; + int result, n; + + if (cptr == NULL) return SCPE_ARG; + if (uptr == NULL) return SCPE_IERR; + result = sscanf(cptr, "%d/%d/%d%n", &numberOfTracks, &numberOfSectors, §orSize, &n); + if ((result != 3) || (result == EOF) || (cptr[n] != 0)) { + result = sscanf(cptr, "T:%d/N:%d/S:%d%n", &numberOfTracks, &numberOfSectors, §orSize, &n); + if ((result != 3) || (result == EOF) || (cptr[n] != 0)) return SCPE_ARG; + } + uptr -> HDSK_NUMBER_OF_TRACKS = numberOfTracks; + uptr -> HDSK_SECTORS_PER_TRACK = numberOfSectors; + uptr -> HDSK_SECTOR_SIZE = sectorSize; + uptr -> capac = numberOfTracks * numberOfSectors * sectorSize; + return SCPE_OK; +} + +/* Show disk geometry routine */ +static t_stat show_geom(FILE *st, UNIT *uptr, int32 val, void *desc) { + if (uptr == NULL) return SCPE_IERR; + fprintf(st, "T:%d/N:%d/S:%d", uptr -> HDSK_NUMBER_OF_TRACKS, + uptr -> HDSK_SECTORS_PER_TRACK, uptr -> HDSK_SECTOR_SIZE); + return SCPE_OK; +} + +#define QUOTE1(text) #text +#define QUOTE2(text) QUOTE1(text) +/* Set disk format routine */ +static t_stat set_format(UNIT *uptr, int32 val, char *cptr, void *desc) { + char fmtname[DPB_NAME_LENGTH + 1]; + int32 i; + + if (cptr == NULL) return SCPE_ARG; + if (uptr == NULL) return SCPE_IERR; + if (sscanf(cptr, "%" QUOTE2(DPB_NAME_LENGTH) "s", fmtname) == 0) return SCPE_ARG; + for (i = 0; dpb[i].capac != 0; i++) { + if (strncmp(fmtname, dpb[i].name, strlen(fmtname)) == 0) { + uptr -> HDSK_FORMAT_TYPE = i; + uptr -> capac = dpb[i].capac; /* Set capacity */ + + /* Configure physical disk geometry */ + uptr -> HDSK_SECTOR_SIZE = (128 << dpb[uptr -> HDSK_FORMAT_TYPE].psh); + uptr -> HDSK_SECTORS_PER_TRACK = dpb[uptr -> HDSK_FORMAT_TYPE].spt >> dpb[uptr -> HDSK_FORMAT_TYPE].psh; + uptr -> HDSK_NUMBER_OF_TRACKS = (uptr -> capac + + uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE - 1) / + (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE); + + return SCPE_OK; + } + } + return SCPE_ARG; +} + +/* Show disk format routine */ +static t_stat show_format(FILE *st, UNIT *uptr, int32 val, void *desc) { + if (uptr == NULL) return SCPE_IERR; + fprintf(st, "%s", dpb[uptr -> HDSK_FORMAT_TYPE].name); + return SCPE_OK; +} + +static int32 bootrom_hdsk[BOOTROM_SIZE_HDSK] = { + 0xf3, 0x06, 0x80, 0x3e, 0x0e, 0xd3, 0xfe, 0x05, /* 5c00-5c07 */ + 0xc2, 0x05, 0x5c, 0x3e, 0x16, 0xd3, 0xfe, 0x3e, /* 5c08-5c0f */ + 0x12, 0xd3, 0xfe, 0xdb, 0xfe, 0xb7, 0xca, 0x20, /* 5c10-5c17 */ + 0x5c, 0x3e, 0x0c, 0xd3, 0xfe, 0xaf, 0xd3, 0xfe, /* 5c18-5c1f */ + 0x06, 0x20, 0x3e, 0x01, 0xd3, 0xfd, 0x05, 0xc2, /* 5c20-5c27 */ + 0x24, 0x5c, 0x11, 0x08, 0x00, 0x21, 0x00, 0x00, /* 5c28-5c2f */ + 0x0e, 0xb8, 0x3e, 0x02, 0xd3, 0xfd, 0x3a, 0x37, /* 5c30-5c37 */ + 0xff, 0xd6, 0x08, 0xd3, 0xfd, 0x7b, 0xd3, 0xfd, /* 5c38-5c3f */ + 0x7a, 0xd3, 0xfd, 0xaf, 0xd3, 0xfd, 0x7d, 0xd3, /* 5c40-5c47 */ + 0xfd, 0x7c, 0xd3, 0xfd, 0xdb, 0xfd, 0xb7, 0xca, /* 5c48-5c4f */ + 0x53, 0x5c, 0x76, 0x79, 0x0e, 0x80, 0x09, 0x4f, /* 5c50-5c57 */ + 0x0d, 0xc2, 0x60, 0x5c, 0xfb, 0xc3, 0x00, 0x00, /* 5c58-5c5f */ + 0x1c, 0x1c, 0x7b, 0xfe, 0x20, 0xca, 0x73, 0x5c, /* 5c60-5c67 */ + 0xfe, 0x21, 0xc2, 0x32, 0x5c, 0x1e, 0x00, 0x14, /* 5c68-5c6f */ + 0xc3, 0x32, 0x5c, 0x1e, 0x01, 0xc3, 0x32, 0x5c, /* 5c70-5c77 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c78-5c7f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c80-5c87 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c88-5c8f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c90-5c97 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5c98-5c9f */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca0-5ca7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ca8-5caf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb0-5cb7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cb8-5cbf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc0-5cc7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cc8-5ccf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd0-5cd7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cd8-5cdf */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce0-5ce7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5ce8-5cef */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf0-5cf7 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 5cf8-5cff */ +}; + +static t_stat hdsk_boot(int32 unitno, DEVICE *dptr) { + if (MEMORYSIZE < 24*KB) { + printf("Need at least 24KB RAM to boot from hard disk.\n"); + return SCPE_ARG; + } + if (cpu_unit.flags & (UNIT_CPU_ALTAIRROM | UNIT_CPU_BANKED)) { + /* check whether we are really modifying an LD A,<> instruction */ + if (bootrom_dsk[UNIT_NO_OFFSET_1 - 1] == LDA_INSTRUCTION) + bootrom_dsk[UNIT_NO_OFFSET_1] = (unitno + NUM_OF_DSK) & 0xff; /* LD A, */ + else { /* Attempt to modify non LD A,<> instructions is refused. */ + printf("Incorrect boot ROM offset detected.\n"); + return SCPE_IERR; + } + install_ALTAIRbootROM(); /* install modified ROM */ + } + assert(install_bootrom(bootrom_hdsk, BOOTROM_SIZE_HDSK, HDSK_BOOT_ADDRESS, FALSE) == SCPE_OK); + *((int32 *) sim_PC->loc) = HDSK_BOOT_ADDRESS; + return SCPE_OK; +} + +/* returns TRUE iff there exists a disk with VERBOSE */ +static int32 hdsk_hasVerbose(void) { + int32 i; + for (i = 0; i < HDSK_NUMBER; i++) + if (hdsk_dev.units[i].flags & UNIT_HDSK_VERBOSE) return TRUE; + return FALSE; +} + +/* The hard disk port is 0xfd. It understands the following commands. + + 1. Reset + ld b,32 + ld a,HDSK_RESET + l: out (0fdh),a + dec b + jp nz,l + + 2. Read / write + ; parameter block + cmd: db HDSK_READ or HDSK_WRITE + hd: db 0 ; 0 .. 7, defines hard disk to be used + sector: db 0 ; 0 .. 31, defines sector + track: dw 0 ; 0 .. 2047, defines track + dma: dw 0 ; defines where result is placed in memory + + ; routine to execute + ld b,7 ; size of parameter block + ld hl,cmd ; start address of parameter block + l: ld a,(hl) ; get byte of parameter block + out (0fdh),a ; send it to port + inc hl ; point to next byte + dec b ; decrement counter + jp nz,l ; again, if not done + in a,(0fdh) ; get result code + + 3. Retrieve Disk Parameters from controller (Howard M. Harte) + Reads a 19-byte parameter block from the disk controller. + This parameter block is in CP/M DPB format for the first 17 bytes, + and the last two bytes are the lsb/msb of the disk's physical + sector size. + + ; routine to execute + ld a,hdskParam ; hdskParam = 4 + out (hdskPort),a ; Send 'get parameters' command, hdskPort = 0fdh + ld a,(diskno) + out (hdskPort),a ; Send selected HDSK number + ld b,17 + 1: in a,(hdskPort) ; Read 17-bytes of DPB + ld (hl), a + inc hl + djnz 1 + in a,(hdskPort) ; Read LSB of disk's physical sector size. + ld (hsecsiz), a + in a,(hdskPort) ; Read MSB of disk's physical sector size. + ld (hsecsiz+1), a + +*/ + +/* check the parameters and return TRUE iff parameters are correct or have been repaired */ +static int32 checkParameters(void) { + UNIT *uptr = &hdsk_dev.units[selectedDisk]; + int32 currentFlag; + if ((selectedDisk < 0) || (selectedDisk >= HDSK_NUMBER)) { + if (hdsk_hasVerbose()) { + MESSAGE_2("HDSK%d does not exist, will use HDSK0 instead.", selectedDisk); + } + selectedDisk = 0; + } + currentFlag = hdsk_dev.units[selectedDisk].flags; + if ((currentFlag & UNIT_ATT) == 0) { + if (currentFlag & UNIT_HDSK_VERBOSE) { + MESSAGE_2("HDSK%d is not attached.", selectedDisk); + } + return FALSE; /* cannot read or write */ + } + if ((selectedSector < 0) || (selectedSector >= uptr -> HDSK_SECTORS_PER_TRACK)) { + if (currentFlag & UNIT_HDSK_VERBOSE) { + MESSAGE_4("HDSK%d: 0 <= Sector=%02d < %d violated, will use 0 instead.", + selectedDisk, selectedSector, uptr -> HDSK_SECTORS_PER_TRACK); + } + selectedSector = 0; + } + if ((selectedTrack < 0) || (selectedTrack >= uptr -> HDSK_NUMBER_OF_TRACKS)) { + if (currentFlag & UNIT_HDSK_VERBOSE) { + MESSAGE_4("HDSK%d: 0 <= Track=%04d < %04d violated, will use 0 instead.", + selectedDisk, selectedTrack, uptr -> HDSK_NUMBER_OF_TRACKS); + } + selectedTrack = 0; + } + selectedDMA &= ADDRMASK; + if (trace_level) { + MESSAGE_7("%s HDSK%d Track=%04d Sector=%02d Len=%04d DMA=%04x", + (hdskLastCommand == HDSK_READ) ? "Read" : "Write", + selectedDisk, selectedTrack, selectedSector, uptr -> HDSK_SECTOR_SIZE, selectedDMA); + } + return TRUE; +} + +static int32 doSeek(void) { + UNIT *uptr = &hdsk_dev.units[selectedDisk]; + if (fseek(uptr -> fileref, + (uptr -> HDSK_SECTORS_PER_TRACK * uptr -> HDSK_SECTOR_SIZE) * selectedTrack + + (uptr -> HDSK_SECTOR_SIZE * selectedSector), SEEK_SET)) { + if ((uptr -> flags) & UNIT_HDSK_VERBOSE) { + MESSAGE_4("Could not access HDSK%d Sector=%02d Track=%04d.", + selectedDisk, selectedSector, selectedTrack); + } + return CPM_ERROR; + } + else return CPM_OK; +} + +uint8 hdskbuf[HDSK_MAX_SECTOR_SIZE] = { 0 }; /* data buffer */ + +static int32 doRead(void) { + int32 i; + UNIT *uptr = &hdsk_dev.units[selectedDisk]; + if (doSeek()) return CPM_ERROR; + + if (fread(hdskbuf, uptr -> HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) { + for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) hdskbuf[i] = CPM_EMPTY; + if ((uptr -> flags) & UNIT_HDSK_VERBOSE) { + MESSAGE_4("Could not read HDSK%d Sector=%02d Track=%04d.", + selectedDisk, selectedSector, selectedTrack); + } + return CPM_OK; /* allows the creation of empty hard disks */ + } + for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) PutBYTEWrapper(selectedDMA + i, hdskbuf[i]); + return CPM_OK; +} + +static int32 doWrite(void) { + int32 i; + + UNIT *uptr = &hdsk_dev.units[selectedDisk]; + if (((uptr -> flags) & UNIT_HDSK_WLK) == 0) { /* write enabled */ + if (doSeek()) return CPM_ERROR; + for (i = 0; i < uptr -> HDSK_SECTOR_SIZE; i++) hdskbuf[i] = GetBYTEWrapper(selectedDMA + i); + if (fwrite(hdskbuf, uptr -> HDSK_SECTOR_SIZE, 1, uptr -> fileref) != 1) { + if ((uptr -> flags) & UNIT_HDSK_VERBOSE) { + MESSAGE_4("Could not write HDSK%d Sector=%02d Track=%04d.", + selectedDisk, selectedSector, selectedTrack); + } + return CPM_ERROR; + } + } + else { + if ((uptr -> flags) & UNIT_HDSK_VERBOSE) { + MESSAGE_4("Could not write to locked HDSK%d Sector=%02d Track=%04d.", + selectedDisk, selectedSector, selectedTrack); + } + return CPM_ERROR; + } + return CPM_OK; +} + +static int32 hdsk_in(const int32 port) { + UNIT *uptr = &hdsk_dev.units[selectedDisk]; + + int32 result; + if ((hdskCommandPosition == 6) && ((hdskLastCommand == HDSK_READ) || (hdskLastCommand == HDSK_WRITE))) { + result = checkParameters() ? ((hdskLastCommand == HDSK_READ) ? doRead() : doWrite()) : CPM_ERROR; + hdskLastCommand = HDSK_NONE; + hdskCommandPosition = 0; + return result; + } else if (hdskLastCommand == HDSK_PARAM) { + DPB current = dpb[uptr -> HDSK_FORMAT_TYPE]; + uint8 params[17]; + params[ 0] = current.spt & 0xff; params[ 1] = (current.spt >> 8) & 0xff; + params[ 2] = current.bsh; + params[ 3] = current.blm; + params[ 4] = current.exm; + params[ 5] = current.dsm & 0xff; params[ 6] = (current.dsm >> 8) & 0xff; + params[ 7] = current.drm & 0xff; params[ 8] = (current.drm >> 8) & 0xff; + params[ 9] = current.al0; + params[10] = current.al1; + params[11] = current.cks & 0xff; params[12] = (current.cks >> 8) & 0xff; + params[13] = current.off & 0xff; params[14] = (current.off >> 8) & 0xff; + params[15] = current.psh; + params[16] = current.phm; + if (++paramcount >= 19) hdskLastCommand = HDSK_NONE; + if (paramcount <= 17) + return params[paramcount - 1]; + else if (paramcount == 18) + return (uptr -> HDSK_SECTOR_SIZE & 0xff); + else if (paramcount == 19) + return (uptr -> HDSK_SECTOR_SIZE >> 8); + else { + MESSAGE_2("HDSK%d Get parameter error.", selectedDisk); + } + + } + else if (hdsk_hasVerbose()) { + MESSAGE_4("Illegal IN command detected (port=%03xh, cmd=%d, pos=%d).", + port, hdskLastCommand, hdskCommandPosition); + } + return CPM_OK; +} + +static int32 hdsk_out(const int32 port, const int32 data) { + switch(hdskLastCommand) { + + case HDSK_PARAM: + paramcount = 0; + selectedDisk = data; + break; + + case HDSK_READ: + + case HDSK_WRITE: + switch(hdskCommandPosition) { + + case 0: + selectedDisk = data; + hdskCommandPosition++; + break; + + case 1: + selectedSector = data; + hdskCommandPosition++; + break; + + case 2: + selectedTrack = data; + hdskCommandPosition++; + break; + + case 3: + selectedTrack += (data << 8); + hdskCommandPosition++; + break; + + case 4: + selectedDMA = data; + hdskCommandPosition++; + break; + + case 5: + selectedDMA += (data << 8); + hdskCommandPosition++; + break; + + default: + hdskLastCommand = HDSK_NONE; + hdskCommandPosition = 0; + } + break; + + default: + if ((HDSK_RESET <= data) && (data <= HDSK_PARAM)) + hdskLastCommand = data; + else { + if (hdsk_hasVerbose()) { + MESSAGE_3("Illegal OUT command detected (port=%03xh, cmd=%d).", + port, data); + } + hdskLastCommand = HDSK_RESET; + } + hdskCommandPosition = 0; + } + return 0; /* ignored, since OUT */ +} + +int32 hdsk_io(const int32 port, const int32 io, const int32 data) { + return io == 0 ? hdsk_in(port) : hdsk_out(port, data); +} diff --git a/AltairZ80/altairz80_net.c b/AltairZ80/altairz80_net.c new file mode 100644 index 0000000..3b4607b --- /dev/null +++ b/AltairZ80/altairz80_net.c @@ -0,0 +1,301 @@ +/* altairz80_net.c: networking capability + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. +*/ + +#include "altairz80_defs.h" +#include "sim_sock.h" +extern uint32 PCX; +extern char messageBuffer[]; +extern void printMessage(void); + +#define UNIT_V_SERVER (UNIT_V_UF + 0) /* define machine as a server */ +#define UNIT_SERVER (1 << UNIT_V_SERVER) +#define NET_INIT_POLL_SERVER 16000 +#define NET_INIT_POLL_CLIENT 15000 + +static t_stat net_attach (UNIT *uptr, char *cptr); +static t_stat net_detach (UNIT *uptr); +static t_stat net_reset (DEVICE *dptr); +static t_stat net_svc (UNIT *uptr); +static t_stat set_net (UNIT *uptr, int32 value, char *cptr, void *desc); +int32 netStatus (const int32 port, const int32 io, const int32 data); +int32 netData (const int32 port, const int32 io, const int32 data); + +#define MAX_CONNECTIONS 2 /* maximal number of server connections */ +#define BUFFER_LENGTH 512 /* length of input and output buffer */ +#define NET_ACCEPT 1 /* bit masks for trace_level */ +#define NET_DROP 2 +#define NET_IN 4 +#define NET_OUT 8 +static int32 trace_level = 0; + +static struct { + int32 Z80StatusPort; /* Z80 status port associated with this ioSocket, read only */ + int32 Z80DataPort; /* Z80 data port associated with this ioSocket, read only */ + SOCKET masterSocket; /* server master socket, only defined at [1] */ + SOCKET ioSocket; /* accepted server socket or connected client socket, 0 iff free */ + char inputBuffer[BUFFER_LENGTH]; /* buffer for input characters read from ioSocket */ + int32 inputPosRead; /* position of next character to read from buffer */ + int32 inputPosWrite; /* position of next character to append to input buffer from ioSocket */ + int32 inputSize; /* number of characters in circular input buffer */ + char outputBuffer[BUFFER_LENGTH];/* buffer for output characters to be written to ioSocket */ + int32 outputPosRead; /* position of next character to write to ioSocket */ + int32 outputPosWrite; /* position of next character to append to output buffer */ + int32 outputSize; /* number of characters in circular output buffer */ +} serviceDescriptor[MAX_CONNECTIONS+1] = { /* serviceDescriptor[0] holds the information for a client */ +/* stat dat ms ios in inPR inPW inS out outPR outPW outS */ + {50, 51, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* client Z80 port 50 and 51 */ + {40, 41, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0}, /* server Z80 port 40 and 41 */ + {42, 43, 0, 0, {0}, 0, 0, 0, {0}, 0, 0, 0} /* server Z80 port 42 and 43 */ +}; + +static UNIT net_unit = { + UDATA (&net_svc, UNIT_ATTABLE, 0), + 0, /* wait, set in attach */ + 0, /* u3 = Port */ + 0, /* u4 = IP of host */ + 0, /* u5, unused */ + 0, /* u6, unused */ +}; + +static REG net_reg[] = { + { DRDATA (POLL, net_unit.wait, 32) }, + { HRDATA (IPHOST, net_unit.u4, 32), REG_RO }, + { DRDATA (PORT, net_unit.u3, 32), REG_RO }, + { HRDATA (TRACELEVEL, trace_level, 32) }, + { NULL } +}; + +static MTAB net_mod[] = { + { UNIT_SERVER, 0, "CLIENT", "CLIENT", &set_net}, /* machine is a client */ + { UNIT_SERVER, UNIT_SERVER, "SERVER", "SERVER", &set_net}, /* machine is a server */ + { 0 } +}; + +DEVICE net_dev = { + "NET", &net_unit, net_reg, net_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &net_reset, + NULL, &net_attach, &net_detach, + NULL, 0, 0, + NULL, NULL, NULL +}; + +static t_stat set_net(UNIT *uptr, int32 value, char *cptr, void *desc) { + char temp[CBUFSIZE]; + if ((net_unit.flags & UNIT_ATT) && ((net_unit.flags & UNIT_SERVER) != value)) { + strncpy(temp, net_unit.filename, CBUFSIZE); /* save name for later attach */ + net_detach(&net_unit); + net_unit.flags ^= UNIT_SERVER; /* now switch from client to server and vice versa */ + net_attach(uptr, temp); + return SCPE_OK; + } + return SCPE_OK; +} + +static void serviceDescriptor_reset(const uint32 i) { + serviceDescriptor[i].inputPosRead = 0; + serviceDescriptor[i].inputPosWrite = 0; + serviceDescriptor[i].inputSize = 0; + serviceDescriptor[i].outputPosRead = 0; + serviceDescriptor[i].outputPosWrite = 0; + serviceDescriptor[i].outputSize = 0; +} + +static t_stat net_reset(DEVICE *dptr) { + uint32 i; + if (net_unit.flags & UNIT_ATT) + sim_activate(&net_unit, net_unit.wait); /* start poll */ + for (i = 0; i <= MAX_CONNECTIONS; i++) + serviceDescriptor_reset(i); + return SCPE_OK; +} + +static t_stat net_attach(UNIT *uptr, char *cptr) { + uint32 i, ipa, ipp; + t_stat r = get_ipaddr(cptr, &ipa, &ipp); + if (r != SCPE_OK) return SCPE_ARG; + if (ipa == 0) ipa = 0x7F000001; /* localhost = 127.0.0.1 */ + if (ipp == 0) ipp = 3000; + net_unit.u3 = ipp; + net_unit.u4 = ipa; + net_reset(&net_dev); + for (i = 0; i <= MAX_CONNECTIONS; i++) serviceDescriptor[i].ioSocket = 0; + if (net_unit.flags & UNIT_SERVER) { + net_unit.wait = NET_INIT_POLL_SERVER; + serviceDescriptor[1].masterSocket = sim_master_sock(ipp); + if (serviceDescriptor[1].masterSocket == INVALID_SOCKET) return SCPE_IOERR; + } + else { + net_unit.wait = NET_INIT_POLL_CLIENT; + serviceDescriptor[0].ioSocket = sim_connect_sock(ipa, ipp); + if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR; + } + net_unit.flags |= UNIT_ATT; + net_unit.filename = (char *) calloc(CBUFSIZE, sizeof (char)); /* alloc name buf */ + if (net_unit.filename == NULL) return SCPE_MEM; + strncpy(net_unit.filename, cptr, CBUFSIZE); /* save name */ + return SCPE_OK; +} + +static t_stat net_detach(UNIT *uptr) { + uint32 i; + if (!(net_unit.flags & UNIT_ATT)) return SCPE_OK; /* if not attached simply return */ + if (net_unit.flags & UNIT_SERVER) + sim_close_sock(serviceDescriptor[1].masterSocket, TRUE); + for (i = 0; i <= MAX_CONNECTIONS; i++) + if (serviceDescriptor[i].ioSocket) + sim_close_sock(serviceDescriptor[i].ioSocket, FALSE); + free(net_unit.filename); /* free port string */ + net_unit.filename = NULL; + net_unit.flags &= ~UNIT_ATT; /* not attached */ + return SCPE_OK; +} + +/* cannot use sim_check_conn to check whether read will return an error */ +static t_stat net_svc(UNIT *uptr) { + int32 i, j, k, r; + SOCKET s; + static char svcBuffer[BUFFER_LENGTH]; + if (net_unit.flags & UNIT_ATT) { /* cannot remove due to following else */ + sim_activate(&net_unit, net_unit.wait); /* continue poll */ + if (net_unit.flags & UNIT_SERVER) { + for (i = 1; i <= MAX_CONNECTIONS; i++) + if (serviceDescriptor[i].ioSocket == 0) { + s = sim_accept_conn(serviceDescriptor[1].masterSocket, NULL); + if (s != INVALID_SOCKET) { + serviceDescriptor[i].ioSocket = s; + if (trace_level & NET_ACCEPT) { + MESSAGE_3("Accepted connection %i with socket %i.", i, s); + } + } + } + } + else if (serviceDescriptor[0].ioSocket == 0) { + serviceDescriptor[0].ioSocket = sim_connect_sock(net_unit.u4, net_unit.u3); + if (serviceDescriptor[0].ioSocket == INVALID_SOCKET) return SCPE_IOERR; + printf("\rWaiting for server ... Type g (possibly twice) when ready" NLP); + return SCPE_STOP; + } + for (i = 0; i <= MAX_CONNECTIONS; i++) + if (serviceDescriptor[i].ioSocket) { + if (serviceDescriptor[i].inputSize < BUFFER_LENGTH) { /* there is space left in inputBuffer */ + r = sim_read_sock(serviceDescriptor[i].ioSocket, svcBuffer, + BUFFER_LENGTH - serviceDescriptor[i].inputSize); + if (r == -1) { + if (trace_level & NET_DROP) { + MESSAGE_3("Drop connection %i with socket %i.", i, serviceDescriptor[i].ioSocket); + } + sim_close_sock(serviceDescriptor[i].ioSocket, FALSE); + serviceDescriptor[i].ioSocket = 0; + serviceDescriptor_reset(i); + continue; + } + else { + for (j = 0; j < r; j++) { + serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosWrite++] = svcBuffer[j]; + if (serviceDescriptor[i].inputPosWrite == BUFFER_LENGTH) + serviceDescriptor[i].inputPosWrite = 0; + } + serviceDescriptor[i].inputSize += r; + } + } + if (serviceDescriptor[i].outputSize > 0) { /* there is something to write in outputBuffer */ + k = serviceDescriptor[i].outputPosRead; + for (j = 0; j < serviceDescriptor[i].outputSize; j++) { + svcBuffer[j] = serviceDescriptor[i].outputBuffer[k++]; + if (k == BUFFER_LENGTH) k = 0; + } + r = sim_write_sock(serviceDescriptor[i].ioSocket, svcBuffer, serviceDescriptor[i].outputSize); + if (r >= 0) { + serviceDescriptor[i].outputSize -= r; + serviceDescriptor[i].outputPosRead += r; + if (serviceDescriptor[i].outputPosRead >= BUFFER_LENGTH) + serviceDescriptor[i].outputPosRead -= BUFFER_LENGTH; + } + else printf("write %i" NLP, r); + } + } + } + return SCPE_OK; +} + +int32 netStatus(const int32 port, const int32 io, const int32 data) { + uint32 i; + if ((net_unit.flags & UNIT_ATT) == 0) return 0; + net_svc(&net_unit); + if (io == 0) /* IN */ + for (i = 0; i <= MAX_CONNECTIONS; i++) + if (serviceDescriptor[i].Z80StatusPort == port) + return (serviceDescriptor[i].inputSize > 0 ? 1 : 0) | + (serviceDescriptor[i].outputSize < BUFFER_LENGTH ? 2 : 0); + return 0; +} + +int32 netData(const int32 port, const int32 io, const int32 data) { + uint32 i; + char result; + if ((net_unit.flags & UNIT_ATT) == 0) return 0; + net_svc(&net_unit); + for (i = 0; i <= MAX_CONNECTIONS; i++) + if (serviceDescriptor[i].Z80DataPort == port) + if (io == 0) { /* IN */ + if (serviceDescriptor[i].inputSize == 0) { + printf("re-read from %i" NLP, port); + result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead > 0 ? + serviceDescriptor[i].inputPosRead - 1 : BUFFER_LENGTH - 1]; + } + else { + result = serviceDescriptor[i].inputBuffer[serviceDescriptor[i].inputPosRead++]; + if (serviceDescriptor[i].inputPosRead == BUFFER_LENGTH) + serviceDescriptor[i].inputPosRead = 0; + serviceDescriptor[i].inputSize--; + } + if (trace_level & NET_IN) { + MESSAGE_4(" IN(%i)=%03xh (%c)", port, (result & 0xff), + (32 <= (result & 0xff)) && ((result & 0xff) <= 127) ? (result & 0xff) : '?'); + } + return result; + } + else { /* OUT */ + if (serviceDescriptor[i].outputSize == BUFFER_LENGTH) { + printf("over-write %i to %i" NLP, data, port); + serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite > 0 ? + serviceDescriptor[i].outputPosWrite - 1 : BUFFER_LENGTH - 1] = data; + } + else { + serviceDescriptor[i].outputBuffer[serviceDescriptor[i].outputPosWrite++] = data; + if (serviceDescriptor[i].outputPosWrite== BUFFER_LENGTH) + serviceDescriptor[i].outputPosWrite = 0; + serviceDescriptor[i].outputSize++; + } + if (trace_level & NET_OUT) { + MESSAGE_4("OUT(%i)=%03xh (%c)", port, data, + (32 <= data) && (data <= 127) ? data : '?'); + } + return 0; + } + return 0; +} diff --git a/AltairZ80/altairz80_sio.c b/AltairZ80/altairz80_sio.c new file mode 100644 index 0000000..201dddd --- /dev/null +++ b/AltairZ80/altairz80_sio.c @@ -0,0 +1,1475 @@ +/* altairz80_sio.c: MITS Altair serial I/O card + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + + Based on work by Charles E Owen (c) 1997 + + These functions support a simulated MITS 2SIO interface card. + The card had two physical I/O ports which could be connected + to any serial I/O device that would connect to a current loop, + RS232, or TTY interface. Available baud rates were jumper + selectable for each port from 110 to 9600. + + All I/O is via programmed I/O. Each device has a status port + and a data port. A write to the status port can select + some options for the device (0x03 will reset the port). + A read of the status port gets the port status: + + +---+---+---+---+---+---+---+---+ + | X | X | X | X | X | X | O | I | + +---+---+---+---+---+---+---+---+ + + I - A 1 in this bit position means a character has been received + on the data port and is ready to be read. + O - A 1 in this bit means the port is ready to receive a character + on the data port and transmit it out over the serial line. + + A read to the data port gets the buffered character, a write + to the data port writes the character to the device. +*/ + +#include + +#include "altairz80_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include +#include +#if UNIX_PLATFORM +#include +#elif defined (_WIN32) +#include +#endif + +#define UNIT_V_SIO_ANSI (UNIT_V_UF + 0) /* ANSI mode, strip bit 8 on output */ +#define UNIT_SIO_ANSI (1 << UNIT_V_SIO_ANSI) +#define UNIT_V_SIO_UPPER (UNIT_V_UF + 1) /* upper case mode */ +#define UNIT_SIO_UPPER (1 << UNIT_V_SIO_UPPER) +#define UNIT_V_SIO_BS (UNIT_V_UF + 2) /* map delete to backspace */ +#define UNIT_SIO_BS (1 << UNIT_V_SIO_BS) +#define UNIT_V_SIO_VERBOSE (UNIT_V_UF + 3) /* verbose mode, i.e. show error messages */ +#define UNIT_SIO_VERBOSE (1 << UNIT_V_SIO_VERBOSE) +#define UNIT_V_SIO_MAP (UNIT_V_UF + 4) /* mapping mode on */ +#define UNIT_SIO_MAP (1 << UNIT_V_SIO_MAP) +#define UNIT_V_SIO_BELL (UNIT_V_UF + 5) /* ^G (bell character) rings bell */ +#define UNIT_SIO_BELL (1 << UNIT_V_SIO_BELL) +#define UNIT_V_SIO_INTERRUPT (UNIT_V_UF + 6) /* create keyboard interrupts */ +#define UNIT_SIO_INTERRUPT (1 << UNIT_V_SIO_INTERRUPT) +#define UNIT_V_SIO_SLEEP (UNIT_V_UF + 7) /* sleep after keyboard status check */ +#define UNIT_SIO_SLEEP (1 << UNIT_V_SIO_SLEEP) + +#define UNIT_V_SIMH_VERBOSE (UNIT_V_UF + 0) /* verbose mode for SIMH pseudo device */ +#define UNIT_SIMH_VERBOSE (1 << UNIT_V_SIMH_VERBOSE) +#define UNIT_V_SIMH_TIMERON (UNIT_V_UF + 1) /* SIMH pseudo device timer generate interrupts */ +#define UNIT_SIMH_TIMERON (1 << UNIT_V_SIMH_TIMERON) + +#define TERMINALS 4 /* lines per mux */ +#define SIO_CAN_READ 0x01 /* bit 0 is set iff character available */ +#define SIO_CAN_WRITE 0x02 /* bit 1 is set iff character can be sent */ +#define SIO_RESET 0x03 /* Command to reset SIO */ +#define VGSIO_CAN_READ 0x02 /* bit 1 is set iff character available */ +#define VGSIO_CAN_WRITE 0x01 /* bit 0 is set iff character can be sent */ +#define KBD_HAS_CHAR 0x40 /* bit 6 is set iff character available */ +#define KBD_HAS_NO_CHAR 0x01 /* bit 0 is set iff no character is available */ + +#define BACKSPACE_CHAR 0x08 /* backspace character */ +#define DELETE_CHAR 0x7f /* delete character */ +#define CONTROLC_CHAR 0x03 /* control C character */ +#define CONTROLG_CHAR 0x07 /* control G char., rings bell when displayed */ +#define CONTROLZ_CHAR 0x1a /* control Z character */ + +#define PORT_TABLE_SIZE 256 /* size of port mapping table */ +#define SLEEP_ALLOWED_START_DEFAULT 100 /* default initial value for sleepAllowedCounter*/ + +static t_stat sio_set_verbose (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat simh_dev_set_timeron (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat simh_dev_set_timeroff (UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat sio_reset(DEVICE *dptr); +static t_stat sio_attach(UNIT *uptr, char *cptr); +static t_stat sio_detach(UNIT *uptr); +static t_stat ptr_reset(DEVICE *dptr); +static t_stat ptp_reset(DEVICE *dptr); +static t_stat toBool(char tf, int *result); +static t_stat sio_dev_set_port(UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat sio_dev_show_port(FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat sio_dev_set_interrupton(UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat sio_dev_set_interruptoff(UNIT *uptr, int32 value, char *cptr, void *desc); +static t_stat sio_svc(UNIT *uptr); +static t_stat simh_dev_reset(DEVICE *dptr); +static t_stat simh_svc(UNIT *uptr); +int32 nulldev (const int32 port, const int32 io, const int32 data); +int32 sr_dev (const int32 port, const int32 io, const int32 data); +int32 simh_dev (const int32 port, const int32 io, const int32 data); +int32 sio0d (const int32 port, const int32 io, const int32 data); +int32 sio0s (const int32 port, const int32 io, const int32 data); +int32 sio1d (const int32 port, const int32 io, const int32 data); +int32 sio1s (const int32 port, const int32 io, const int32 data); +void printMessage(void); +void do_SIMH_sleep(void); +static void pollConnection(void); +static int32 mapCharacter(int32 ch); +static void checkSleep(void); +static void voidSleep(void); + +extern int32 getBankSelect(void); +extern void setBankSelect(const int32 b); +extern uint32 getCommon(void); +extern uint8 GetBYTEWrapper(const uint32 Addr); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +extern int32 chiptype; +extern const t_bool rtc_avail; +extern FILE *sim_log; +extern uint32 PCX; +extern int32 sim_switches; +extern const char *scp_error_messages[]; +extern int32 SR; +extern UNIT cpu_unit; +extern volatile int32 stop_cpu; +extern int32 sim_interval; + +/* SIMH pseudo device status registers */ +/* ZSDOS clock definitions */ +static time_t ClockZSDOSDelta = 0; /* delta between real clock and Altair clock */ +static int32 setClockZSDOSPos = 0; /* determines state for receiving address of parameter block */ +static int32 setClockZSDOSAdr = 0; /* address in M of 6 byte parameter block for setting time */ +static int32 getClockZSDOSPos = 0; /* determines state for sending clock information */ + +/* CPM3 clock definitions */ +static time_t ClockCPM3Delta = 0; /* delta between real clock and Altair clock */ +static int32 setClockCPM3Pos = 0; /* determines state for receiving address of parameter block */ +static int32 setClockCPM3Adr = 0; /* address in M of 5 byte parameter block for setting time */ +static int32 getClockCPM3Pos = 0; /* determines state for sending clock information */ +static int32 daysCPM3SinceOrg = 0; /* days since 1 Jan 1978 */ + +/* interrupt related */ +static uint32 timeOfNextInterrupt; /* time when next interrupt is scheduled */ + int32 timerInterrupt = FALSE; /* timer interrupt pending */ + int32 timerInterruptHandler = 0x0fc00; /* default address of interrupt handling routine */ +static int32 setTimerInterruptAdrPos= 0; /* determines state for receiving timerInterruptHandler */ +static int32 timerDelta = 100; /* interrupt every 100 ms */ +static int32 setTimerDeltaPos = 0; /* determines state for receiving timerDelta */ + +/* stop watch and timer related */ +static uint32 stopWatchDelta = 0; /* stores elapsed time of stop watch */ +static int32 getStopWatchDeltaPos = 0; /* determines the state for receiving stopWatchDelta */ +static uint32 stopWatchNow = 0; /* stores starting time of stop watch */ +static int32 markTimeSP = 0; /* stack pointer for timer stack */ + + /* default time in microseconds to sleep for SIMHSleepCmd */ +#if defined (_WIN32) +static uint32 SIMHSleep = 1000; /* Sleep uses milliseconds */ +#elif defined (__MWERKS__) && defined (macintosh) +static uint32 SIMHSleep = 0; /* no sleep on Macintosh OS9 */ +#else +static uint32 SIMHSleep = 100; /* on other platforms 100 micro seconds is good enough */ +#endif +static uint32 sleepAllowedCounter = 0; /* only sleep on no character available when == 0 */ +static uint32 sleepAllowedStart = SLEEP_ALLOWED_START_DEFAULT; /* default start for above counter */ + +/* miscellaneous */ +static int32 versionPos = 0; /* determines state for sending device identifier */ +static int32 lastCPMStatus = 0; /* result of last attachCPM command */ +static int32 lastCommand = 0; /* most recent command processed on port 0xfeh */ +static int32 getCommonPos = 0; /* determines state for sending the 'common' register */ + +/* support for wild card expansion */ +#if UNIX_PLATFORM +static glob_t globS; +static uint32 globPosNameList = 0; +static int32 globPosName = 0; +static int32 globValid = FALSE; +static int32 globError = 0; +#elif defined (_WIN32) +static WIN32_FIND_DATA FindFileData; +static HANDLE hFind = INVALID_HANDLE_VALUE; +static int32 globFinished = FALSE; +static int32 globValid = FALSE; +static int32 globPosName = 0; +#endif + +/* SIO status registers */ +static int32 warnLevelSIO = 3; /* display at most 'warnLevelSIO' times the same warning */ +static int32 warnUnattachedPTP = 0; /* display a warning message if < warnLevel and SIO set to + VERBOSE and output to PTP without an attached file */ +static int32 warnUnattachedPTR = 0; /* display a warning message if < warnLevel and SIO set to + VERBOSE and attempt to read from PTR without an attached file */ +static int32 warnPTREOF = 0; /* display a warning message if < warnLevel and SIO set to + VERBOSE and attempt to read from PTR past EOF */ +static int32 warnUnassignedPort = 0; /* display a warning message if < warnLevel and SIO set to + VERBOSE and attempt to perform IN or OUT on an unassigned PORT */ + + int32 keyboardInterrupt = FALSE; /* keyboard interrupt pending */ + uint32 keyboardInterruptHandler = 0x0038;/* address of keyboard interrupt handler */ + +static TMLN TerminalLines[TERMINALS] = { /* four terminals */ + { 0 } +}; + +static TMXR altairTMXR = { /* mux descriptor */ + TERMINALS, 0, 0, TerminalLines +}; + +static UNIT sio_unit = { + UDATA (&sio_svc, UNIT_ATTABLE | UNIT_SIO_MAP | UNIT_SIO_SLEEP, 0), + 100000, /* wait */ + FALSE, /* u3 = FALSE, no character available in buffer */ + FALSE, /* u4 = FALSE, terminal input is not attached to a file */ + FALSE, /* u5 = FALSE, terminal input has not yet reached EOF */ + 0 /* u6 = 0, not used */ +}; + +static REG sio_reg[] = { + { DRDATA (SIOWLEV, warnLevelSIO, 32) }, + { DRDATA (WRNUPTP, warnUnattachedPTP, 32) }, + { DRDATA (WRNUPTR, warnUnattachedPTR, 32) }, + { DRDATA (WRNPTRE, warnPTREOF, 32) }, + { DRDATA (WRUPORT, warnUnassignedPort, 32) }, + { HRDATA (FILEATT, sio_unit.u4, 8), REG_RO }, /* TRUE iff terminal input is attached to a file */ + { HRDATA (FILEEOF, sio_unit.u5, 8), REG_RO }, /* TRUE iff terminal input file has reached EOF */ + { HRDATA (TSTATUS, sio_unit.u3, 8) }, /* TRUE iff a character available in sio_unit.buf */ + { DRDATA (TBUFFER, sio_unit.buf, 8) }, /* input buffer for one character */ + { DRDATA (KEYBDI, keyboardInterrupt, 3), REG_RO }, + { HRDATA (KEYBDH, keyboardInterruptHandler, 16) }, + { NULL } +}; + +static MTAB sio_mod[] = { + { UNIT_SIO_ANSI, 0, "TTY", "TTY", NULL }, /* keep bit 8 as is for output */ + { UNIT_SIO_ANSI, UNIT_SIO_ANSI, "ANSI", "ANSI", NULL }, /* set bit 8 to 0 before output */ + { UNIT_SIO_UPPER, 0, "ALL", "ALL", NULL }, /* do not change case of input characters */ + { UNIT_SIO_UPPER, UNIT_SIO_UPPER, "UPPER", "UPPER", NULL }, /* change input characters to upper case */ + { UNIT_SIO_BS, 0, "BS", "BS", NULL }, /* map delete to backspace */ + { UNIT_SIO_BS, UNIT_SIO_BS, "DEL", "DEL", NULL }, /* map backspace to delete */ + { UNIT_SIO_VERBOSE, 0, "QUIET", "QUIET", NULL }, /* quiet, no error messages */ + { UNIT_SIO_VERBOSE, UNIT_SIO_VERBOSE, "VERBOSE", "VERBOSE", &sio_set_verbose }, + /* verbose, display warning messages */ + { UNIT_SIO_MAP, 0, "NOMAP", "NOMAP", NULL }, /* disable character mapping */ + { UNIT_SIO_MAP, UNIT_SIO_MAP, "MAP", "MAP", NULL }, /* enable all character mapping */ + { UNIT_SIO_BELL, 0, "BELL", "BELL", NULL }, /* enable bell character */ + { UNIT_SIO_BELL, UNIT_SIO_BELL, "NOBELL", "NOBELL", NULL }, /* suppress ringing the bell */ + { UNIT_SIO_SLEEP, 0, "NOSLEEP", "NOSLEEP", NULL }, /* no sleep after keyboard status check */ + { UNIT_SIO_SLEEP, UNIT_SIO_SLEEP, "SLEEP", "SLEEP", NULL }, /* sleep after keyboard status check */ + /* no keyboard interrupts */ + { UNIT_SIO_INTERRUPT, 0, "NOINTERRUPT","NOINTERRUPT",&sio_dev_set_interruptoff }, + /* create keyboard interrupts */ + { UNIT_SIO_INTERRUPT, UNIT_SIO_INTERRUPT, "INTERRUPT","INTERRUPT",&sio_dev_set_interrupton }, + { MTAB_XTD|MTAB_VDV|MTAB_VAL, 0, "PORT", "PORT", &sio_dev_set_port, &sio_dev_show_port }, + { 0 } +}; + +DEVICE sio_dev = { + "SIO", &sio_unit, sio_reg, sio_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &sio_reset, + NULL, &sio_attach, &sio_detach, + NULL, 0, 0, + NULL, NULL, NULL }; + +static UNIT ptr_unit = { + UDATA (NULL, UNIT_SEQ | UNIT_ATTABLE | UNIT_ROABLE, 0) +}; + +static REG ptr_reg[] = { + { HRDATA (STAT, ptr_unit.u3, 8) }, + { NULL } +}; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + NULL, NULL, NULL, + NULL, 0, 0, + NULL, NULL, NULL +}; + +static UNIT ptp_unit = { + UDATA (NULL, UNIT_SEQ + UNIT_ATTABLE, 0) +}; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, NULL, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + NULL, 0, 0, + NULL, NULL, NULL +}; + +/* Synthetic device SIMH for communication + between Altair and SIMH environment using port 0xfe */ +static UNIT simh_unit = { + UDATA (&simh_svc, 0, 0), KBD_POLL_WAIT +}; + +static REG simh_reg[] = { + { DRDATA (CZD, ClockZSDOSDelta, 32) }, + { DRDATA (SCZP, setClockZSDOSPos, 8), REG_RO }, + { HRDATA (SCZA, setClockZSDOSAdr, 16), REG_RO }, + { DRDATA (GCZP, getClockZSDOSPos, 8), REG_RO }, + + { DRDATA (CC3D, ClockCPM3Delta, 32) }, + { DRDATA (SC3DP, setClockCPM3Pos, 8), REG_RO }, + { HRDATA (SC3DA, setClockCPM3Adr, 16), REG_RO }, + { DRDATA (GC3DP, getClockCPM3Pos, 8), REG_RO }, + { DRDATA (D3DO, daysCPM3SinceOrg, 32), REG_RO }, + + { DRDATA (TOFNI, timeOfNextInterrupt, 32), REG_RO }, + { DRDATA (TIMI, timerInterrupt, 3) }, + { HRDATA (TIMH, timerInterruptHandler, 16) }, + { DRDATA (STIAP, setTimerInterruptAdrPos,8), REG_RO }, + { DRDATA (TIMD, timerDelta, 32) }, + { DRDATA (STDP, setTimerDeltaPos, 8), REG_RO }, + { DRDATA (SLEEP, SIMHSleep, 32) }, + { DRDATA (VOSLP, sleepAllowedStart, 32) }, + + { DRDATA (STPDT, stopWatchDelta, 32), REG_RO }, + { DRDATA (STPOS, getStopWatchDeltaPos, 8), REG_RO }, + { DRDATA (STPNW, stopWatchNow, 32), REG_RO }, + { DRDATA (MTSP, markTimeSP, 8), REG_RO }, + + { DRDATA (VPOS, versionPos, 8), REG_RO }, + { DRDATA (LCPMS, lastCPMStatus, 8), REG_RO }, + { DRDATA (LCMD, lastCommand, 8), REG_RO }, + { DRDATA (CPOS, getCommonPos, 8), REG_RO }, + { NULL } +}; + +static MTAB simh_mod[] = { + /* quiet, no warning messages */ + { UNIT_SIMH_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, display warning messages */ + { UNIT_SIMH_VERBOSE, UNIT_SIMH_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + /* timer generated interrupts are off */ + { UNIT_SIMH_TIMERON, 0, "TIMEROFF", "TIMEROFF", &simh_dev_set_timeroff }, + /* timer generated interrupts are on */ + { UNIT_SIMH_TIMERON, UNIT_SIMH_TIMERON, "TIMERON", "TIMERON", &simh_dev_set_timeron }, + { 0 } +}; + +DEVICE simh_device = { + "SIMH", &simh_unit, simh_reg, simh_mod, + 1, 10, 31, 1, 16, 4, + NULL, NULL, &simh_dev_reset, + NULL, NULL, NULL, + NULL, 0, 0, + NULL, NULL, NULL +}; + +char messageBuffer[256] = { 0 }; + +void printMessage(void) { + printf(messageBuffer); + printf(NLP); + if (sim_log) { + fprintf(sim_log, messageBuffer); + fprintf(sim_log,"\n"); + } +} + +static void resetSIOWarningFlags(void) { + warnUnattachedPTP = warnUnattachedPTR = warnPTREOF = warnUnassignedPort = 0; +} + +static t_stat sio_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) { + resetSIOWarningFlags(); + return SCPE_OK; +} + +static t_stat sio_attach(UNIT *uptr, char *cptr) { + t_stat r = SCPE_IERR; + sio_unit.u3 = FALSE; /* no character in terminal input buffer */ + get_uint(cptr, 10, 65535, &r); /* attempt to get port, discard result */ + if (r == SCPE_OK) { /* string can be interpreted as port number */ + sio_unit.u4 = FALSE; /* terminal input is not attached to a file */ + return tmxr_attach(&altairTMXR, uptr, cptr); /* attach mux */ + } + sio_unit.u4 = TRUE; /* terminal input is attached to a file */ + sio_unit.u5 = FALSE; /* EOF not yet reached */ + return attach_unit(uptr, cptr); +} + +static t_stat sio_detach(UNIT *uptr) { + sio_unit.u3 = FALSE; /* no character in terminal input buffer */ + if (sio_unit.u4) { /* is terminal input attached to a file? */ + sio_unit.u4 = FALSE; /* not anymore, detach */ + return detach_unit(uptr); + } + return tmxr_detach(&altairTMXR, uptr); +} + +static void pollConnection(void) { + if (sio_unit.flags & UNIT_ATT) { + int32 temp = tmxr_poll_conn(&altairTMXR); /* poll connection */ + if (temp >= 0) + TerminalLines[temp].rcve = 1; /* enable receive */ + tmxr_poll_rx(&altairTMXR); /* poll input */ + tmxr_poll_tx(&altairTMXR); /* poll output */ + } +} + +/* reset routines */ +static t_stat sio_reset(DEVICE *dptr) { + int32 i; + sio_unit.u3 = FALSE; /* no character in terminal input buffer */ + resetSIOWarningFlags(); + if (sio_unit.u4) { /* is terminal input attached to a file? */ + rewind(sio_unit.fileref); /* yes, rewind input */ + sio_unit.u5 = FALSE; /* EOF not yet reached */ + } + else if (sio_unit.flags & UNIT_ATT) + for (i = 0; i < TERMINALS; i++) + if (TerminalLines[i].conn) + tmxr_reset_ln(&TerminalLines[i]); + return SCPE_OK; +} + +static t_stat ptr_reset(DEVICE *dptr) { + resetSIOWarningFlags(); + ptr_unit.u3 = FALSE; /* End Of File not yet reached */ + if (ptr_unit.flags & UNIT_ATT) /* attached? */ + rewind(ptr_unit.fileref); + return SCPE_OK; +} + +static t_stat ptp_reset(DEVICE *dptr) { + resetSIOWarningFlags(); + return SCPE_OK; +} + +static int32 mapCharacter(int32 ch) { + ch &= 0xff; + if (sio_unit.flags & UNIT_SIO_MAP) { + if (sio_unit.flags & UNIT_SIO_BS) { + if (ch == BACKSPACE_CHAR) + return DELETE_CHAR; + } + else if (ch == DELETE_CHAR) + return BACKSPACE_CHAR; + if (sio_unit.flags & UNIT_SIO_UPPER) + return toupper(ch); + } + return ch; +} + +/* I/O instruction handlers, called from the CPU module when an + IN or OUT instruction is issued. + + Each function is passed an 'io' flag, where 0 means a read from + the port, and 1 means a write to the port. On input, the actual + input is passed as the return value, on output, 'data' is written + to the device. + + Port 1 controls console I/O. We distinguish three cases: + 1) SIO attached to a file (i.e. input taken from a file ) + 2) SIO attached to a port (i.e. Telnet console I/O ) + 3) SIO not attached to a port (i.e. "regular" console I/O ) +*/ + +typedef struct { + int32 port; /* this information belongs to port number 'port' */ + int32 terminalLine; /* map to this 'terminalLine' */ + int32 sio_can_read; /* bit mask to indicate that one can read from this port */ + int32 sio_cannot_read; /* bit mask to indicate that one cannot read from this port */ + int32 sio_can_write; /* bit mask to indicate that one can write to this port */ + int32 hasReset; /* TRUE iff SIO has reset command */ + int32 sio_reset; /* reset command */ + int32 hasOUT; /* TRUE iff port supports OUT command */ + int32 isBuiltin; /* TRUE iff mapping is built in */ +} SIO_PORT_INFO; + +static SIO_PORT_INFO port_table[PORT_TABLE_SIZE] = { + {0x00, 0, KBD_HAS_CHAR, KBD_HAS_NO_CHAR, SIO_CAN_WRITE, FALSE, 0, FALSE, TRUE }, + {0x01, 0, 0, 0, 0, FALSE, 0, FALSE, TRUE }, + {0x02, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, TRUE, TRUE }, + {0x03, 0, VGSIO_CAN_READ, 0, VGSIO_CAN_WRITE, FALSE, 0, FALSE, TRUE }, + {0x10, 0, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, + {0x14, 1, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, + {0x16, 2, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, + {0x18, 3, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, FALSE, TRUE }, + {0x11, 0, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, + {0x15, 1, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, + {0x17, 2, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, + {0x19, 3, SIO_CAN_READ, 0, SIO_CAN_WRITE, TRUE, SIO_RESET, TRUE, TRUE }, + {-1, 0, 0, 0, 0, 0, 0, 0, 0} /* must be last */ +}; + +static SIO_PORT_INFO lookupPortInfo(const int32 port, int32 *position) { + int32 i = 0; + while ((port_table[i].port != -1) && (port_table[i].port != port)) i++; + *position = i; + return port_table[i]; +} + +/* keyboard idle detection: sleep when feature enabled, no character available + (duty of caller) and operation not voided (e.g. when output is available) */ +static void checkSleep(void) { + if (sio_unit.flags & UNIT_SIO_SLEEP) { + if (sleepAllowedCounter) sleepAllowedCounter--; + else do_SIMH_sleep(); + } +} + +/* void sleep for next 'sleepAllowedStart' tests */ +static void voidSleep(void) { + sleepAllowedCounter = sleepAllowedStart; +} + +/* generic status port for keyboard input / terminal output */ +int32 sio0s(const int32 port, const int32 io, const int32 data) { + int32 ch, result; + SIO_PORT_INFO spi = lookupPortInfo(port, &ch); + assert(spi.port == port); + pollConnection(); + if (io == 0) { /* IN */ + if (sio_unit.u4) /* attached to a file? */ + if (sio_unit.u5) /* EOF reached? */ + sio_detach(&sio_unit); /* detach file and switch to keyboard input */ + else return spi.sio_can_read | spi.sio_can_write; + if (sio_unit.flags & UNIT_ATT) { /* attached to a port? */ + if (tmxr_rqln(&TerminalLines[spi.terminalLine])) + result = spi.sio_can_read; + else { + result = spi.sio_cannot_read; + checkSleep(); + } + return result | /* read possible if character available */ + (TerminalLines[spi.terminalLine].conn && TerminalLines[spi.terminalLine].xmte ? spi.sio_can_write : 0x00); + /* write possible if connected and transmit + enabled */ + } + if (sio_unit.u3) /* character available? */ + return spi.sio_can_read | spi.sio_can_write; + ch = sim_poll_kbd(); /* no, try to get a character */ + if (ch) { /* character available? */ + if (ch == SCPE_STOP) { /* stop CPU in case ^E (default) was typed */ + stop_cpu = TRUE; + sim_interval = 0; /* detect stop condition as soon as possible*/ + return spi.sio_can_write | spi.sio_cannot_read; /* do not consume stop character */ + } + sio_unit.u3 = TRUE; /* indicate character available */ + sio_unit.buf = ch; /* store character in buffer */ + return spi.sio_can_read | spi.sio_can_write; + } + checkSleep(); + return spi.sio_can_write | spi.sio_cannot_read; + } /* OUT follows, no fall-through from IN */ + if (spi.hasReset && (data == spi.sio_reset)) /* reset command */ + sio_unit.u3 = FALSE; /* indicate that no character is available */ + return 0x00; /* ignored since OUT */ +} + +/* generic data port for keyboard input / terminal output */ +int32 sio0d(const int32 port, const int32 io, const int32 data) { + int32 ch; + SIO_PORT_INFO spi = lookupPortInfo(port, &ch); + assert(spi.port == port); + pollConnection(); + if (io == 0) { /* IN */ + if (sio_unit.u4) { /* attached to a file? */ + if (sio_unit.u5) { /* EOF reached? */ + sio_detach(&sio_unit); /* detach file and switch to keyboard input */ + return CONTROLC_CHAR; /* this time return ^C after all */ + } + if ((ch = getc(sio_unit.fileref)) == EOF) { /* end of file? */ + sio_unit.u5 = TRUE; /* terminal input file has reached EOF */ + return CONTROLC_CHAR; /* result is ^C (= CP/M interrupt) */ + } + return mapCharacter(ch); /* return mapped character */ + } + if (sio_unit.flags & UNIT_ATT) + return mapCharacter(tmxr_getc_ln(&TerminalLines[spi.terminalLine])); + sio_unit.u3 = FALSE; /* no character is available any more */ + return mapCharacter(sio_unit.buf); /* return previous character */ + } /* OUT follows, no fall-through from IN */ + if (spi.hasOUT) { + ch = sio_unit.flags & UNIT_SIO_ANSI ? data & 0x7f : data; /* clear highest bit in ANSI mode */ + if ((ch != CONTROLG_CHAR) || !(sio_unit.flags & UNIT_SIO_BELL)) { + voidSleep(); + if ((sio_unit.flags & UNIT_ATT) && (!sio_unit.u4)) /* attached to a port and not to a file */ + tmxr_putc_ln(&TerminalLines[spi.terminalLine], ch); /* status ignored */ + else + sim_putchar(ch); + } + } + return 0x00; /* ignored since OUT */ +} + +/* PTR/PTP status port */ +int32 sio1s(const int32 port, const int32 io, const int32 data) { + if (io == 0) { /* IN */ + /* reset I bit iff PTR unit not attached or + no more data available. O bit is always + set since write always possible. */ + if ((ptr_unit.flags & UNIT_ATT) == 0) { /* PTR is not attached */ + if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnattachedPTR < warnLevelSIO)) { + warnUnattachedPTR++; +/*06*/ MESSAGE_2("Attempt to test status of unattached PTR[0x%02x]. 0x02 returned.", port); + } + return SIO_CAN_WRITE; + } + /* if EOF then SIO_CAN_WRITE else + (SIO_CAN_WRITE and SIO_CAN_READ) */ + return ptr_unit.u3 ? SIO_CAN_WRITE : (SIO_CAN_READ | SIO_CAN_WRITE); + } /* OUT follows */ + if (data == SIO_RESET) + ptr_unit.u3 = FALSE; /* reset EOF indicator */ + return 0x00; /* ignored since OUT */ +} + +/* PTR/PTP data port */ +int32 sio1d(const int32 port, const int32 io, const int32 data) { + int32 ch; + if (io == 0) { /* IN */ + if (ptr_unit.u3) { /* EOF reached, no more data available */ + if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnPTREOF < warnLevelSIO)) { + warnPTREOF++; +/*07*/ MESSAGE_2("PTR[0x%02x] attempted to read past EOF. 0x00 returned.", port); + } + return 0x00; + } + if ((ptr_unit.flags & UNIT_ATT) == 0) { /* not attached */ + if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnattachedPTR < warnLevelSIO)) { + warnUnattachedPTR++; +/*08*/ MESSAGE_2("Attempt to read from unattached PTR[0x%02x]. 0x00 returned.", port); + } + return 0x00; + } + if ((ch = getc(ptr_unit.fileref)) == EOF) { /* end of file? */ + ptr_unit.u3 = TRUE; /* remember EOF reached */ + return CONTROLZ_CHAR; /* ^Z denotes end of text file in CP/M */ + } + return ch & 0xff; + } /* OUT follows */ + if (ptp_unit.flags & UNIT_ATT) /* unit must be attached */ + putc(data, ptp_unit.fileref); + /* else ignore data */ + else if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnattachedPTP < warnLevelSIO)) { + warnUnattachedPTP++; +/*09*/ MESSAGE_3("Attempt to output '0x%02x' to unattached PTP[0x%02x] - ignored.", data, port); + } + return 0x00; /* ignored since OUT */ +} + +static t_stat toBool(char tf, int *result) { + if (tf == 'T') { + *result = TRUE; + return SCPE_OK; + } + if (tf == 'F') { + *result = FALSE; + return SCPE_OK; + } + return SCPE_ARG; +} + +static void show_sio_port_info(FILE *st, SIO_PORT_INFO sip) { + if (sio_unit.flags & UNIT_SIO_VERBOSE) + fprintf(st, "(Port=%02x/Terminal=%1i/Read=0x%02x/NotRead=0x%02x/" + "Write=0x%02x/Reset?=%s/Reset=0x%02x/Data?=%s)", + sip.port, sip.terminalLine, sip.sio_can_read, sip.sio_cannot_read, + sip.sio_can_write, sip.hasReset ? "True" : "False", sip.sio_reset, + sip.hasOUT ? "True" : "False"); + else + fprintf(st, "(%02x/%1i/%02x/%02x/%02x/%s/%02x/%s)", + sip.port, sip.terminalLine, sip.sio_can_read, sip.sio_cannot_read, + sip.sio_can_write, sip.hasReset ? "T" : "F", sip.sio_reset, + sip.hasOUT ? "T" : "F"); +} + +static uint32 equalSIP(SIO_PORT_INFO x, SIO_PORT_INFO y) { + /* isBuiltin is not relevant for equality, only for display */ + return (x.port == y.port) && (x.terminalLine == y.terminalLine) && + (x.sio_can_read == y.sio_can_read) && (x.sio_cannot_read == y.sio_cannot_read) && + (x.sio_can_write == y.sio_can_write) && (x.hasReset == y.hasReset) && + (x.sio_reset == y.sio_reset) && (x.hasOUT == y.hasOUT); +} + +static t_stat sio_dev_set_port(UNIT *uptr, int32 value, char *cptr, void *desc) { + int32 result, n, position; + SIO_PORT_INFO sip = { 0 }, old; + char hasReset, hasOUT; + if (cptr == NULL) return SCPE_ARG; + result = sscanf(cptr, "%x%n", &sip.port, &n); + if ((result == 1) && (cptr[n] == 0)) { + old = lookupPortInfo(sip.port, &position); + if (old.port == -1) { + printf("No mapping for port 0x%02x exists - cannot remove.\n", sip.port); + return SCPE_ARG; + } + do { + port_table[position] = port_table[position + 1]; + position++; + } + while (port_table[position].port != -1); + sim_map_resource(sip.port, 1, RESOURCE_TYPE_IO, &nulldev, FALSE); + if (sio_unit.flags & UNIT_SIO_VERBOSE) { + printf("Removing mapping for port 0x%02x.\n\t", sip.port); + show_sio_port_info(stdout, old); + } + return SCPE_OK; + } + result = sscanf(cptr, "%x/%d/%x/%x/%x/%1c/%x/%1c%n", &sip.port, + &sip.terminalLine, &sip.sio_can_read, &sip.sio_cannot_read, + &sip.sio_can_write, &hasReset, &sip.sio_reset, &hasOUT, &n); + if ((result != 8) || (result == EOF) || (cptr[n] != 0)) return SCPE_ARG; + result = toBool(hasReset, &sip.hasReset); + if (result != SCPE_OK) return result; + result = toBool(hasOUT, &sip.hasOUT); + if (result != SCPE_OK) return result; + if (sip.port != (sip.port & 0xff)) { + printf("Truncating port 0x%x to 0x%02x.\n", sip.port, sip.port & 0xff); + sip.port &= 0xff; + } + old = lookupPortInfo(sip.port, &position); + if (old.port == sip.port) { + if (sio_unit.flags & UNIT_SIO_VERBOSE) { + printf("Replacing mapping for port 0x%02x.\n\t", sip.port); + show_sio_port_info(stdout, old); + printf("-> "); + show_sio_port_info(stdout, sip); + if (equalSIP(sip, old)) printf("[identical]"); + } + } + else { + port_table[position + 1] = old; + if (sio_unit.flags & UNIT_SIO_VERBOSE) { + printf("Adding mapping for port 0x%02x.\n\t", sip.port); + show_sio_port_info(stdout, sip); + } + } + if (sio_unit.flags & UNIT_SIO_VERBOSE) printf("\n"); + port_table[position] = sip; + sim_map_resource(sip.port, 1, RESOURCE_TYPE_IO, (sip.hasOUT || + (sip.sio_can_read == 0) && (sip.sio_cannot_read == 0) && + (sip.sio_can_write == 0)) ? &sio0d : &sio0s, FALSE); + return SCPE_OK; +} + +static t_stat sio_dev_show_port(FILE *st, UNIT *uptr, int32 val, void *desc) { + int32 i, first = TRUE; + for (i = 0; port_table[i].port != -1; i++) + if (!port_table[i].isBuiltin) { + if (first) first = FALSE; + else fprintf(st, " "); + show_sio_port_info(st, port_table[i]); + } + if (first) fprintf(st, "no extra port"); + return SCPE_OK; +} + +static t_stat sio_dev_set_interrupton(UNIT *uptr, int32 value, char *cptr, void *desc) { + keyboardInterrupt = FALSE; + return sim_activate(&sio_unit, sio_unit.wait); /* activate unit */ +} + +static t_stat sio_dev_set_interruptoff(UNIT *uptr, int32 value, char *cptr, void *desc) { + keyboardInterrupt = FALSE; + sim_cancel(&sio_unit); + return SCPE_OK; +} + +static t_stat sio_svc(UNIT *uptr) { + if (sio0s(0, 0, 0) & KBD_HAS_CHAR) { + keyboardInterrupt = TRUE; + } + if (sio_unit.flags & UNIT_SIO_INTERRUPT) + sim_activate(&sio_unit, sio_unit.wait); /* activate unit */ + return SCPE_OK; +} + +int32 nulldev(const int32 port, const int32 io, const int32 data) { + if ((sio_unit.flags & UNIT_SIO_VERBOSE) && (warnUnassignedPort < warnLevelSIO)) { + warnUnassignedPort++; + if (io == 0) { + MESSAGE_2("Attempt to input from unassigned port 0x%04x - ignored.", port); + } + else { + MESSAGE_3("Attempt to output 0x%02x to unassigned port 0x%04x - ignored.", data, port); + } + } + return io == 0 ? 0xff : 0; +} + +int32 sr_dev(const int32 port, const int32 io, const int32 data) { + return io == 0 ? SR : 0; +} + +static int32 toBCD(const int32 x) { + return (x / 10) * 16 + (x % 10); +} + +static int32 fromBCD(const int32 x) { + return 10 * ((0xf0 & x) >> 4) + (0x0f & x); +} + +/* Z80 or 8080 programs communicate with the SIMH pseudo device via port 0xfe. + The following principles apply: + + 1) For commands that do not require parameters and do not return results + ld a, + out (0feh),a + Special case is the reset command which needs to be send 128 times to make + sure that the internal state is properly reset. + + 2) For commands that require parameters and do not return results + ld a, + out (0feh),a + ld a, + out (0feh),a + ld a, + out (0feh),a + ... + Note: The calling program must send all parameter bytes. Otherwise + the pseudo device is left in an undefined state. + + 3) For commands that do not require parameters and return results + ld a, + out (0feh),a + in a,(0feh) ; contains first byte of result + in a,(0feh) ; contains second byte of result + ... + Note: The calling program must request all bytes of the result. Otherwise + the pseudo device is left in an undefined state. + + 4) Commands requiring parameters and returning results do not exist currently. + +*/ + +enum simhPseudoDeviceCommands { /* do not change order or remove commands, add only at the end */ + printTimeCmd, /* 0 print the current time in milliseconds */ + startTimerCmd, /* 1 start a new timer on the top of the timer stack */ + stopTimerCmd, /* 2 stop timer on top of timer stack and show time difference */ + resetPTRCmd, /* 3 reset the PTR device */ + attachPTRCmd, /* 4 attach the PTR device */ + detachPTRCmd, /* 5 detach the PTR device */ + getSIMHVersionCmd, /* 6 get the current version of the SIMH pseudo device */ + getClockZSDOSCmd, /* 7 get the current time in ZSDOS format */ + setClockZSDOSCmd, /* 8 set the current time in ZSDOS format */ + getClockCPM3Cmd, /* 9 get the current time in CP/M 3 format */ + setClockCPM3Cmd, /* 10 set the current time in CP/M 3 format */ + getBankSelectCmd, /* 11 get the selected bank */ + setBankSelectCmd, /* 12 set the selected bank */ + getCommonCmd, /* 13 get the base address of the common memory segment */ + resetSIMHInterfaceCmd, /* 14 reset the SIMH pseudo device */ + showTimerCmd, /* 15 show time difference to timer on top of stack */ + attachPTPCmd, /* 16 attach PTP to the file with name at beginning of CP/M command line*/ + detachPTPCmd, /* 17 detach PTP */ + hasBankedMemoryCmd, /* 18 determines whether machine has banked memory */ + setZ80CPUCmd, /* 19 set the CPU to a Z80 */ + set8080CPUCmd, /* 20 set the CPU to an 8080 */ + startTimerInterruptsCmd, /* 21 start timer interrupts */ + stopTimerInterruptsCmd, /* 22 stop timer interrupts */ + setTimerDeltaCmd, /* 23 set the timer interval in which interrupts occur */ + setTimerInterruptAdrCmd, /* 24 set the address to call by timer interrupts */ + resetStopWatchCmd, /* 25 reset the millisecond stop watch */ + readStopWatchCmd, /* 26 read the millisecond stop watch */ + SIMHSleepCmd, /* 27 let SIMH sleep for SIMHSleep microseconds */ + getHostOSPathSeparator, /* 28 obtain the file path separator of the OS under which SIMH runs */ + getHostFilenames /* 29 perform wildcard expansion and obtain list of file names */ +}; + +#define CPM_COMMAND_LINE_LENGTH 128 +#define TIMER_STACK_LIMIT 10 /* stack depth of timer stack */ +static uint32 markTime[TIMER_STACK_LIMIT]; /* timer stack */ +static struct tm currentTime; +static int32 currentTimeValid = FALSE; +static char version[] = "SIMH003"; + +static t_stat simh_dev_reset(DEVICE *dptr) { + currentTimeValid = FALSE; + ClockZSDOSDelta = 0; + setClockZSDOSPos = 0; + getClockZSDOSPos = 0; + ClockCPM3Delta = 0; + setClockCPM3Pos = 0; + getClockCPM3Pos = 0; + getStopWatchDeltaPos = 0; + getCommonPos = 0; + setTimerDeltaPos = 0; + setTimerInterruptAdrPos = 0; + markTimeSP = 0; + versionPos = 0; + lastCommand = 0; + lastCPMStatus = SCPE_OK; + timerInterrupt = FALSE; + if (simh_unit.flags & UNIT_SIMH_TIMERON) + simh_dev_set_timeron(NULL, 0, NULL, NULL); + return SCPE_OK; +} + +static void warnNoRealTimeClock(void) { + if (simh_unit.flags & UNIT_SIMH_VERBOSE) { + MESSAGE_1("Sorry - no real time clock available."); + } +} + +static t_stat simh_dev_set_timeron(UNIT *uptr, int32 value, char *cptr, void *desc) { + if (rtc_avail) { + timeOfNextInterrupt = sim_os_msec() + timerDelta; + return sim_activate(&simh_unit, simh_unit.wait); /* activate unit */ + } + warnNoRealTimeClock(); + return SCPE_ARG; +} + +static t_stat simh_dev_set_timeroff(UNIT *uptr, int32 value, char *cptr, void *desc) { + timerInterrupt = FALSE; + sim_cancel(&simh_unit); + return SCPE_OK; +} + +static t_stat simh_svc(UNIT *uptr) { + uint32 n = sim_os_msec(); + if (n >= timeOfNextInterrupt) { + timerInterrupt = TRUE; + timeOfNextInterrupt += timerDelta; + if (n >= timeOfNextInterrupt) /* time of next interrupt is not in the future */ + timeOfNextInterrupt = n + timerDelta; /* make sure it is in the future! */ + } + if (simh_unit.flags & UNIT_SIMH_TIMERON) + sim_activate(&simh_unit, simh_unit.wait); /* activate unit */ + return SCPE_OK; +} + +static char cpmCommandLine[CPM_COMMAND_LINE_LENGTH]; +static void createCPMCommandLine(void) { + int32 i, len = (GetBYTEWrapper(0x80) & 0x7f); /* 0x80 contains length of command line, discard first char */ + for (i = 0; i < len - 1; i++) + cpmCommandLine[i] = (char)GetBYTEWrapper(0x82 + i); /* the first char, typically ' ', is discarded */ + cpmCommandLine[i] = 0; /* make C string */ +} + +/* The CP/M command line is used as the name of a file and UNIT* uptr is attached to it. */ +static void attachCPM(UNIT *uptr) { + createCPMCommandLine(); + if (uptr == &ptr_unit) + sim_switches = SWMASK('R'); + else if (uptr == &ptp_unit) + sim_switches = SWMASK('W') | SWMASK('C'); /* 'C' option makes sure that file is properly truncated + if it had existed before */ + lastCPMStatus = attach_unit(uptr, cpmCommandLine); + if ((lastCPMStatus != SCPE_OK) && (simh_unit.flags & UNIT_SIMH_VERBOSE)) { + MESSAGE_3("Cannot open '%s' (%s).", cpmCommandLine, scp_error_messages[lastCPMStatus - SCPE_BASE]); + /* must keep curly braces as MESSAGE_N is a macro with two statements */ + } +} + +/* setClockZSDOSAdr points to 6 byte block in M: YY MM DD HH MM SS in BCD notation */ +static void setClockZSDOS(void) { + struct tm newTime; + int32 year = fromBCD(GetBYTEWrapper(setClockZSDOSAdr)); + newTime.tm_year = year < 50 ? year + 100 : year; + newTime.tm_mon = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 1)) - 1; + newTime.tm_mday = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 2)); + newTime.tm_hour = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 3)); + newTime.tm_min = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 4)); + newTime.tm_sec = fromBCD(GetBYTEWrapper(setClockZSDOSAdr + 5)); + ClockZSDOSDelta = mktime(&newTime) - time(NULL); +} + +#define SECONDS_PER_MINUTE 60 +#define SECONDS_PER_HOUR (60 * SECONDS_PER_MINUTE) +#define SECONDS_PER_DAY (24 * SECONDS_PER_HOUR) +static time_t mkCPM3Origin(void) { + struct tm date; + date.tm_year = 77; + date.tm_mon = 11; + date.tm_mday = 31; + date.tm_hour = 0; + date.tm_min = 0; + date.tm_sec = 0; + return mktime(&date); +} + +/* setClockCPM3Adr points to 5 byte block in M: + 0 - 1 int16: days since 31 Dec 77 + 2 BCD byte: HH + 3 BCD byte: MM + 4 BCD byte: SS */ +static void setClockCPM3(void) { + ClockCPM3Delta = mkCPM3Origin() + + (GetBYTEWrapper(setClockCPM3Adr) + GetBYTEWrapper(setClockCPM3Adr + 1) * 256) * SECONDS_PER_DAY + + fromBCD(GetBYTEWrapper(setClockCPM3Adr + 2)) * SECONDS_PER_HOUR + + fromBCD(GetBYTEWrapper(setClockCPM3Adr + 3)) * SECONDS_PER_MINUTE + + fromBCD(GetBYTEWrapper(setClockCPM3Adr + 4)) - time(NULL); +} + +static int32 simh_in(const int32 port) { + int32 result = 0; + switch(lastCommand) { + + case getHostFilenames: +#if UNIX_PLATFORM + if (globValid) + if (globPosNameList < globS.gl_pathc) { + if (!(result = globS.gl_pathv[globPosNameList][globPosName++])) { + globPosNameList++; + globPosName = 0; + } + } + else { + globValid = FALSE; + lastCommand = 0; + globfree(&globS); + } +#elif defined (_WIN32) + if (globValid) + if (globFinished) + globValid = FALSE; + else if (!(result = FindFileData.cFileName[globPosName++])) { + globPosName = 0; + if (!FindNextFile(hFind, &FindFileData)) { + globFinished = TRUE; + FindClose(hFind); + hFind = INVALID_HANDLE_VALUE; + } + } +#else + lastCommand = 0; +#endif + break; + + case attachPTRCmd: + + case attachPTPCmd: + result = lastCPMStatus; + lastCommand = 0; + break; + + case getClockZSDOSCmd: + if (currentTimeValid) + switch(getClockZSDOSPos) { + + case 0: + result = toBCD(currentTime.tm_year > 99 ? + currentTime.tm_year - 100 : currentTime.tm_year); + getClockZSDOSPos = 1; + break; + + case 1: + result = toBCD(currentTime.tm_mon + 1); + getClockZSDOSPos = 2; + break; + + case 2: + result = toBCD(currentTime.tm_mday); + getClockZSDOSPos = 3; + break; + + case 3: + result = toBCD(currentTime.tm_hour); + getClockZSDOSPos = 4; + break; + + case 4: + result = toBCD(currentTime.tm_min); + getClockZSDOSPos = 5; + break; + + case 5: + result = toBCD(currentTime.tm_sec); + getClockZSDOSPos = lastCommand = 0; + break; + } + else + result = getClockZSDOSPos = lastCommand = 0; + break; + + case getClockCPM3Cmd: + if (currentTimeValid) + switch(getClockCPM3Pos) { + case 0: + result = daysCPM3SinceOrg & 0xff; + getClockCPM3Pos = 1; + break; + + case 1: + result = (daysCPM3SinceOrg >> 8) & 0xff; + getClockCPM3Pos = 2; + break; + + case 2: + result = toBCD(currentTime.tm_hour); + getClockCPM3Pos = 3; + break; + + case 3: + result = toBCD(currentTime.tm_min); + getClockCPM3Pos = 4; + break; + + case 4: + result = toBCD(currentTime.tm_sec); + getClockCPM3Pos = lastCommand = 0; + break; + } + else + result = getClockCPM3Pos = lastCommand = 0; + break; + + case getSIMHVersionCmd: + result = version[versionPos++]; + if (result == 0) + versionPos = lastCommand = 0; + break; + + case getBankSelectCmd: + if (cpu_unit.flags & UNIT_CPU_BANKED) + result = getBankSelect(); + else { + result = 0; + if (simh_unit.flags & UNIT_SIMH_VERBOSE) { + MESSAGE_1("Get selected bank ignored for non-banked memory."); + } + } + lastCommand = 0; + break; + + case getCommonCmd: + if (getCommonPos == 0) { + result = getCommon() & 0xff; + getCommonPos = 1; + } + else { + result = (getCommon() >> 8) & 0xff; + getCommonPos = lastCommand = 0; + } + break; + + case hasBankedMemoryCmd: + result = cpu_unit.flags & UNIT_CPU_BANKED ? MAXBANKS : 0; + lastCommand = 0; + break; + + case readStopWatchCmd: + if (getStopWatchDeltaPos == 0) { + result = stopWatchDelta & 0xff; + getStopWatchDeltaPos = 1; + } + else { + result = (stopWatchDelta >> 8) & 0xff; + getStopWatchDeltaPos = lastCommand = 0; + } + break; + + case getHostOSPathSeparator: +#if defined (__MWERKS__) && defined (macintosh) + result = ':'; /* colon on Macintosh OS 9 */ +#elif defined (_WIN32) + result = '\\'; /* back slash in Windows */ +#else + result = '/'; /* slash in UNIX */ +#endif + break; + + default: + if (simh_unit.flags & UNIT_SIMH_VERBOSE) { + MESSAGE_2("Undefined IN from SIMH pseudo device on port %03xh ignored.", + port); + } + result = lastCommand = 0; + } + return result; +} + +void do_SIMH_sleep(void) { +#if defined (_WIN32) + if ((SIMHSleep / 1000) && !sio_unit.u4) /* time to sleep and SIO not attached to a file */ + Sleep(SIMHSleep / 1000); +#else + if (SIMHSleep && !sio_unit.u4) /* time to sleep and SIO not attached to a file */ + usleep(SIMHSleep); +#endif +} + +static int32 simh_out(const int32 port, const int32 data) { + time_t now; + switch(lastCommand) { + + case setClockZSDOSCmd: + if (setClockZSDOSPos == 0) { + setClockZSDOSAdr = data; + setClockZSDOSPos = 1; + } + else { + setClockZSDOSAdr |= (data << 8); + setClockZSDOS(); + setClockZSDOSPos = lastCommand = 0; + } + break; + + case setClockCPM3Cmd: + if (setClockCPM3Pos == 0) { + setClockCPM3Adr = data; + setClockCPM3Pos = 1; + } + else { + setClockCPM3Adr |= (data << 8); + setClockCPM3(); + setClockCPM3Pos = lastCommand = 0; + } + break; + + case setBankSelectCmd: + if (cpu_unit.flags & UNIT_CPU_BANKED) + setBankSelect(data & BANKMASK); + else if (simh_unit.flags & UNIT_SIMH_VERBOSE) { + MESSAGE_2("Set selected bank to %i ignored for non-banked memory.", data & 3); + } + lastCommand = 0; + break; + + case setTimerDeltaCmd: + if (setTimerDeltaPos == 0) { + timerDelta = data; + setTimerDeltaPos = 1; + } + else { + timerDelta |= (data << 8); + setTimerDeltaPos = lastCommand = 0; + } + break; + + case setTimerInterruptAdrCmd: + if (setTimerInterruptAdrPos == 0) { + timerInterruptHandler = data; + setTimerInterruptAdrPos = 1; + } + else { + timerInterruptHandler |= (data << 8); + setTimerInterruptAdrPos = lastCommand = 0; + } + break; + + default: + lastCommand = data; + switch(data) { + + case getHostFilenames: +#if UNIX_PLATFORM + if (!globValid) { + globValid = TRUE; + globPosNameList = globPosName = 0; + createCPMCommandLine(); + globError = glob(cpmCommandLine, GLOB_ERR, NULL, &globS); + if (globError) { + if (simh_unit.flags & UNIT_SIMH_VERBOSE) { + MESSAGE_3("Cannot expand '%s'. Error is %i.", cpmCommandLine, globError); + } + globfree(&globS); + globValid = FALSE; + } + } +#elif defined (_WIN32) + if (!globValid) { + globValid = TRUE; + globPosName = 0; + globFinished = FALSE; + createCPMCommandLine(); + hFind = FindFirstFile(cpmCommandLine, &FindFileData); + if (hFind == INVALID_HANDLE_VALUE) { + if (simh_unit.flags & UNIT_SIMH_VERBOSE) { + MESSAGE_3("Cannot expand '%s'. Error is %lu.", cpmCommandLine, GetLastError()); + } + globValid = FALSE; + } + } +#endif + break; + + case SIMHSleepCmd: + do_SIMH_sleep(); + break; + + case printTimeCmd: /* print time */ + if (rtc_avail) { + MESSAGE_2("Current time in milliseconds = %d.", sim_os_msec()); + } + else { + warnNoRealTimeClock(); + } + break; + + case startTimerCmd: /* create a new timer on top of stack */ + if (rtc_avail) + if (markTimeSP < TIMER_STACK_LIMIT) + markTime[markTimeSP++] = sim_os_msec(); + else { + MESSAGE_1("Timer stack overflow."); + } + else warnNoRealTimeClock(); + break; + + case stopTimerCmd: /* stop timer on top of stack and show time difference */ + if (rtc_avail) + if (markTimeSP > 0) { + uint32 delta = sim_os_msec() - markTime[--markTimeSP]; + MESSAGE_2("Timer stopped. Elapsed time in milliseconds = %d.", delta); + } + else { + MESSAGE_1("No timer active."); + } + else warnNoRealTimeClock(); + break; + + case resetPTRCmd: /* reset ptr device */ + ptr_reset(NULL); + break; + + case attachPTRCmd: /* attach ptr to the file with name at beginning of CP/M command line */ + attachCPM(&ptr_unit); + break; + + case detachPTRCmd: /* detach ptr */ + detach_unit(&ptr_unit); + break; + + case getSIMHVersionCmd: + versionPos = 0; + break; + + case getClockZSDOSCmd: + time(&now); + now += ClockZSDOSDelta; + currentTime = *localtime(&now); + currentTimeValid = TRUE; + getClockZSDOSPos = 0; + break; + + case setClockZSDOSCmd: + setClockZSDOSPos = 0; + break; + + case getClockCPM3Cmd: + time(&now); + now += ClockCPM3Delta; + currentTime = *localtime(&now); + currentTimeValid = TRUE; + daysCPM3SinceOrg = (int32) ((now - mkCPM3Origin()) / SECONDS_PER_DAY); + getClockCPM3Pos = 0; + break; + + case setClockCPM3Cmd: + setClockCPM3Pos = 0; + break; + + case getBankSelectCmd: + case setBankSelectCmd: + case getCommonCmd: + case hasBankedMemoryCmd: + case getHostOSPathSeparator: + break; + + case resetSIMHInterfaceCmd: + markTimeSP = 0; + lastCommand = 0; +#if UNIX_PLATFORM + if (globValid) { + globValid = FALSE; + globfree(&globS); + } +#elif defined (_WIN32) + if (globValid) { + globValid = FALSE; + if (hFind != INVALID_HANDLE_VALUE) { + FindClose(hFind); + } + } +#endif + break; + + case showTimerCmd: /* show time difference to timer on top of stack */ + if (rtc_avail) + if (markTimeSP > 0) { + uint32 delta = sim_os_msec() - markTime[markTimeSP - 1]; + MESSAGE_2("Timer running. Elapsed in milliseconds = %d.", delta); + } + else { + MESSAGE_1("No timer active."); + } + else warnNoRealTimeClock(); + break; + + case attachPTPCmd: /* attach ptp to the file with name at beginning of CP/M command line */ + attachCPM(&ptp_unit); + break; + + case detachPTPCmd: /* detach ptp */ + detach_unit(&ptp_unit); + break; + + case setZ80CPUCmd: + chiptype = CHIP_TYPE_Z80; + break; + + case set8080CPUCmd: + chiptype = CHIP_TYPE_8080; + break; + + case startTimerInterruptsCmd: + if (simh_dev_set_timeron(NULL, 0, NULL, NULL) == SCPE_OK) { + timerInterrupt = FALSE; + simh_unit.flags |= UNIT_SIMH_TIMERON; + } + break; + + case stopTimerInterruptsCmd: + simh_unit.flags &= ~UNIT_SIMH_TIMERON; + simh_dev_set_timeroff(NULL, 0, NULL, NULL); + break; + + case setTimerDeltaCmd: + setTimerDeltaPos = 0; + break; + + case setTimerInterruptAdrCmd: + setTimerInterruptAdrPos = 0; + break; + + case resetStopWatchCmd: + stopWatchNow = rtc_avail ? sim_os_msec() : 0; + break; + + case readStopWatchCmd: + getStopWatchDeltaPos = 0; + stopWatchDelta = rtc_avail ? sim_os_msec() - stopWatchNow : 0; + break; + + default: + if (simh_unit.flags & UNIT_SIMH_VERBOSE) { + MESSAGE_3("Unknown command (%i) to SIMH pseudo device on port %03xh ignored.", + data, port); + } + } + } + return 0x00; /* ignored, since OUT */ +} + +/* port 0xfe is a device for communication SIMH <--> Altair machine */ +int32 simh_dev(const int32 port, const int32 io, const int32 data) { + return io == 0 ? simh_in(port) : simh_out(port, data); +} diff --git a/AltairZ80/altairz80_sys.c b/AltairZ80/altairz80_sys.c new file mode 100644 index 0000000..5b2066c --- /dev/null +++ b/AltairZ80/altairz80_sys.c @@ -0,0 +1,790 @@ +/* altairz80_sys.c: MITS Altair system interface + + Copyright (c) 2002-2008, Peter Schorn + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + + Based on work by Charles E Owen (c) 1997 + Disassembler from Marat Fayzullin ((c) 1995, 1996, 1997 - Commercial use prohibited) +*/ + +#include +#include "altairz80_defs.h" + +#define SIM_EMAX 6 + +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern DEVICE cpu_dev; +extern DEVICE sio_dev; +extern DEVICE simh_device; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE dsk_dev; +extern DEVICE hdsk_dev; +extern DEVICE net_dev; + +extern DEVICE mfdc_dev; +extern DEVICE fw2_dev; +extern DEVICE fif_dev; +extern DEVICE vfdhd_dev; +extern DEVICE mdsad_dev; +extern DEVICE nsfpb_dev; +extern DEVICE disk1a_dev; +extern DEVICE disk2_dev; +extern DEVICE selchan_dev; +extern DEVICE ss1_dev; +extern DEVICE i8272_dev; +extern DEVICE mdriveh_dev; + +extern DEVICE cromfdc_dev; +extern DEVICE wd179x_dev; +extern DEVICE n8vem_dev; +extern DEVICE scp300f_dev; + +#ifdef USE_FPC +extern DEVICE fpc_dev; +#endif /* USE_FPC */ + +extern int32 chiptype; +extern long disasm (unsigned char *data, char *output, int segsize, long offset); + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); /* psco */ +t_stat parse_sym(char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw); /* psco */ + +t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); + +/* SCP data structures + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages +*/ + +char sim_name[] = "Altair 8800 (Z80)"; +REG *sim_PC = &cpu_reg[6]; +int32 sim_emax = SIM_EMAX; +DEVICE *sim_devices[] = { + &cpu_dev, &sio_dev, &simh_device, &ptr_dev, &ptp_dev, &dsk_dev, + &hdsk_dev, &net_dev, &mfdc_dev, &fw2_dev, &fif_dev, &vfdhd_dev, &mdsad_dev, + &disk1a_dev, &disk2_dev, &selchan_dev, &ss1_dev, &i8272_dev, &mdriveh_dev, + &cromfdc_dev, &wd179x_dev, &n8vem_dev, &scp300f_dev, +#ifdef USE_FPC + &fpc_dev, +#endif /* USE_FPC */ + NULL +}; + +char memoryAccessMessage[80]; +const char *sim_stop_messages[] = { + "HALT instruction", + "Breakpoint", + memoryAccessMessage, + "Invalid Opcode" +}; + +static char *const Mnemonics8080[] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "NOP", "LXI B,#h", "STAX B", "INX B", "INR B", "DCR B", "MVI B,*h", "RLC", /* 00-07 */ + "DB 09h", "DAD B", "LDAX B", "DCX B", "INR C", "DCR C", "MVI C,*h", "RRC", /* 08-0f */ + "DB 10h", "LXI D,#h", "STAX D", "INX D", "INR D", "DCR D", "MVI D,*h", "RAL", /* 10-17 */ + "DB 18h", "DAD D", "LDAX D", "DCX D", "INR E", "DCR E", "MVI E,*h", "RAR", /* 18-1f */ + "DB 20h", "LXI H,#h", "SHLD #h", "INX H", "INR H", "DCR H", "MVI H,*h", "DAA", /* 20-27 */ + "DB 28h", "DAD H", "LHLD #h", "DCX H", "INR L", "DCR L", "MVI L,*h", "CMA", /* 28-2f */ + "DB 30h", "LXI SP,#h", "STA #h", "INX SP", "INR M", "DCR M", "MVI M,*h", "STC", /* 30-37 */ + "DB 38h", "DAD SP", "LDA #h", "DCX SP", "INR A", "DCR A", "MVI A,*h", "CMC", /* 38-3f */ + "MOV B,B", "MOV B,C", "MOV B,D", "MOV B,E", "MOV B,H", "MOV B,L", "MOV B,M", "MOV B,A", /* 40-47 */ + "MOV C,B", "MOV C,C", "MOV C,D", "MOV C,E", "MOV C,H", "MOV C,L", "MOV C,M", "MOV C,A", /* 48-4f */ + "MOV D,B", "MOV D,C", "MOV D,D", "MOV D,E", "MOV D,H", "MOV D,L", "MOV D,M", "MOV D,A", /* 50-57 */ + "MOV E,B", "MOV E,C", "MOV E,D", "MOV E,E", "MOV E,H", "MOV E,L", "MOV E,M", "MOV E,A", /* 58-5f */ + "MOV H,B", "MOV H,C", "MOV H,D", "MOV H,E", "MOV H,H", "MOV H,L", "MOV H,M", "MOV H,A", /* 60-67 */ + "MOV L,B", "MOV L,C", "MOV L,D", "MOV L,E", "MOV L,H", "MOV L,L", "MOV L,M", "MOV L,A", /* 68-6f */ + "MOV M,B", "MOV M,C", "MOV M,D", "MOV M,E", "MOV M,H", "MOV M,L", "HLT", "MOV M,A", /* 70-77 */ + "MOV A,B", "MOV A,C", "MOV A,D", "MOV A,E", "MOV A,H", "MOV A,L", "MOV A,M", "MOV A,A", /* 78-7f */ + "ADD B", "ADD C", "ADD D", "ADD E", "ADD H", "ADD L", "ADD M", "ADD A", /* 80-87 */ + "ADC B", "ADC C", "ADC D", "ADC E", "ADC H", "ADC L", "ADC M", "ADC A", /* 88-8f */ + "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB M", "SUB A", /* 90-97 */ + "SBB B", "SBB C", "SBB D", "SBB E", "SBB H", "SBB L", "SBB M", "SBB A", /* 98-9f */ + "ANA B", "ANA C", "ANA D", "ANA E", "ANA H", "ANA L", "ANA M", "ANA A", /* a0-a7 */ + "XRA B", "XRA C", "XRA D", "XRA E", "XRA H", "XRA L", "XRA M", "XRA A", /* a8-af */ + "ORA B", "ORA C", "ORA D", "ORA E", "ORA H", "ORA L", "ORA M", "ORA A", /* b0-b7 */ + "CMP B", "CMP C", "CMP D", "CMP E", "CMP H", "CMP L", "CMP M", "CMP A", /* b8-bf */ + "RNZ", "POP B", "JNZ #h", "JMP #h", "CNZ #h", "PUSH B", "ADI *h", "RST 0", /* c0-c7 */ + "RZ", "RET", "JZ #h", "DB CBh", "CZ #h", "CALL #h", "ACI *h", "RST 1", /* c8-cf */ + "RNC", "POP D", "JNC #h", "OUT *h", "CNC #h", "PUSH D", "SUI *h", "RST 2", /* d0-d7 */ + "RC", "DB D9h", "JC #h", "IN *h", "CC #h", "DB DDh", "SBI *h", "RST 3", /* d8-df */ + "RPO", "POP H", "JPO #h", "XTHL", "CPO #h", "PUSH H", "ANI *h", "RST 4", /* e0-e7 */ + "RPE", "PCHL", "JPE #h", "XCHG", "CPE #h", "DB EDh", "XRI *h", "RST 5", /* e8-ef */ + "RP", "POP PSW", "JP #h", "DI", "CP #h", "PUSH PSW", "ORI *h", "RST 6", /* f0-f7 */ + "RM", "SPHL", "JM #h", "EI", "CM #h", "DB FDh", "CPI *h", "RST 7" /* f8-ff */ +}; + +static char *const MnemonicsZ80[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", /* 00-07 */ + "EX AF,AF'", "ADD HL,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", /* 08-0f */ + "DJNZ $h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", /* 10-17 */ + "JR $h", "ADD HL,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", /* 18-1f */ + "JR NZ,$h", "LD HL,#h", "LD (#h),HL", "INC HL", "INC H", "DEC H", "LD H,*h", "DAA", /* 20-27 */ + "JR Z,$h", "ADD HL,HL", "LD HL,(#h)", "DEC HL", "INC L", "DEC L", "LD L,*h", "CPL", /* 28-2f */ + "JR NC,$h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (HL)", "DEC (HL)", "LD (HL),*h", "SCF", /* 30-37 */ + "JR C,$h", "ADD HL,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", /* 38-3f */ + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,H", "LD B,L", "LD B,(HL)", "LD B,A", /* 40-47 */ + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,H", "LD C,L", "LD C,(HL)", "LD C,A", /* 48-4f */ + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,H", "LD D,L", "LD D,(HL)", "LD D,A", /* 50-57 */ + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,H", "LD E,L", "LD E,(HL)", "LD E,A", /* 58-5f */ + "LD H,B", "LD H,C", "LD H,D", "LD H,E", "LD H,H", "LD H,L", "LD H,(HL)", "LD H,A", /* 60-67 */ + "LD L,B", "LD L,C", "LD L,D", "LD L,E", "LD L,H", "LD L,L", "LD L,(HL)", "LD L,A", /* 68-6f */ + "LD (HL),B", "LD (HL),C", "LD (HL),D", "LD (HL),E", "LD (HL),H", "LD (HL),L", "HALT", "LD (HL),A", /* 70-77 */ + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,H", "LD A,L", "LD A,(HL)", "LD A,A", /* 78-7f */ + "ADD A,B", "ADD A,C", "ADD A,D", "ADD A,E", "ADD A,H", "ADD A,L", "ADD A,(HL)", "ADD A,A", /* 80-87 */ + "ADC A,B", "ADC A,C", "ADC A,D", "ADC A,E", "ADC A,H", "ADC A,L", "ADC A,(HL)", "ADC A,A", /* 88-8f */ + "SUB B", "SUB C", "SUB D", "SUB E", "SUB H", "SUB L", "SUB (HL)", "SUB A", /* 90-97 */ + "SBC A,B", "SBC A,C", "SBC A,D", "SBC A,E", "SBC A,H", "SBC A,L", "SBC A,(HL)", "SBC A,A", /* 98-9f */ + "AND B", "AND C", "AND D", "AND E", "AND H", "AND L", "AND (HL)", "AND A", /* a0-a7 */ + "XOR B", "XOR C", "XOR D", "XOR E", "XOR H", "XOR L", "XOR (HL)", "XOR A", /* a8-af */ + "OR B", "OR C", "OR D", "OR E", "OR H", "OR L", "OR (HL)", "OR A", /* b0-b7 */ + "CP B", "CP C", "CP D", "CP E", "CP H", "CP L", "CP (HL)", "CP A", /* b8-bf */ + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD A,*h", "RST 00h", /* c0-c7 */ + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC A,*h", "RST 08h", /* c8-cf */ + "RET NC", "POP DE", "JP NC,#h", "OUT (*h),A", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", /* d0-d7 */ + "RET C", "EXX", "JP C,#h", "IN A,(*h)", "CALL C,#h", "PFX_DD", "SBC A,*h", "RST 18h", /* d8-df */ + "RET PO", "POP HL", "JP PO,#h", "EX (SP),HL", "CALL PO,#h", "PUSH HL", "AND *h", "RST 20h", /* e0-e7 */ + "RET PE", "LD PC,HL", "JP PE,#h", "EX DE,HL", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", /* e8-ef */ + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", /* f0-f7 */ + "RET M", "LD SP,HL", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" /* f8-ff */ +}; + +static char *const MnemonicsCB[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (HL)", "RLC A", /* 00-07 */ + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (HL)", "RRC A", /* 08-0f */ + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (HL)", "RL A", /* 10-17 */ + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (HL)", "RR A", /* 18-1f */ + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (HL)", "SLA A", /* 20-27 */ + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (HL)", "SRA A", /* 28-2f */ + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (HL)", "SLL A", /* 30-37 */ + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (HL)", "SRL A", /* 38-3f */ + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(HL)", "BIT 0,A", /* 40-47 */ + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(HL)", "BIT 1,A", /* 48-4f */ + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(HL)", "BIT 2,A", /* 50-57 */ + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(HL)", "BIT 3,A", /* 58-5f */ + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(HL)", "BIT 4,A", /* 60-67 */ + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(HL)", "BIT 5,A", /* 68-6f */ + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(HL)", "BIT 6,A", /* 70-77 */ + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(HL)", "BIT 7,A", /* 78-7f */ + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(HL)", "RES 0,A", /* 80-87 */ + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(HL)", "RES 1,A", /* 88-8f */ + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(HL)", "RES 2,A", /* 90-97 */ + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(HL)", "RES 3,A", /* 98-9f */ + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(HL)", "RES 4,A", /* a0-a7 */ + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(HL)", "RES 5,A", /* a8-af */ + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(HL)", "RES 6,A", /* b0-b7 */ + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(HL)", "RES 7,A", /* b8-bf */ + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(HL)", "SET 0,A", /* c0-c7 */ + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(HL)", "SET 1,A", /* c8-cf */ + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(HL)", "SET 2,A", /* d0-d7 */ + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(HL)", "SET 3,A", /* d8-df */ + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(HL)", "SET 4,A", /* e0-e7 */ + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(HL)", "SET 5,A", /* e8-ef */ + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(HL)", "SET 6,A", /* f0-f7 */ + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(HL)", "SET 7,A" /* f8-ff */ +}; + +static char *const MnemonicsED[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "DB EDh,00h", "DB EDh,01h", "DB EDh,02h", "DB EDh,03h", "DB EDh,04h", "DB EDh,05h", "DB EDh,06h", "DB EDh,07h", /* 00-07 */ + "DB EDh,08h", "DB EDh,09h", "DB EDh,0Ah", "DB EDh,0Bh", "DB EDh,0Ch", "DB EDh,0Dh", "DB EDh,0Eh", "DB EDh,0Fh", /* 08-0f */ + "DB EDh,10h", "DB EDh,11h", "DB EDh,12h", "DB EDh,13h", "DB EDh,14h", "DB EDh,15h", "DB EDh,16h", "DB EDh,17h", /* 10-17 */ + "DB EDh,18h", "DB EDh,19h", "DB EDh,1Ah", "DB EDh,1Bh", "DB EDh,1Ch", "DB EDh,1Dh", "DB EDh,1Eh", "DB EDh,1Fh", /* 18-1f */ + "DB EDh,20h", "DB EDh,21h", "DB EDh,22h", "DB EDh,23h", "DB EDh,24h", "DB EDh,25h", "DB EDh,26h", "DB EDh,27h", /* 20-27 */ + "DB EDh,28h", "DB EDh,29h", "DB EDh,2Ah", "DB EDh,2Bh", "DB EDh,2Ch", "DB EDh,2Dh", "DB EDh,2Eh", "DB EDh,2Fh", /* 28-2f */ + "DB EDh,30h", "DB EDh,31h", "DB EDh,32h", "DB EDh,33h", "DB EDh,34h", "DB EDh,35h", "DB EDh,36h", "DB EDh,37h", /* 30-37 */ + "DB EDh,38h", "DB EDh,39h", "DB EDh,3Ah", "DB EDh,3Bh", "DB EDh,3Ch", "DB EDh,3Dh", "DB EDh,3Eh", "DB EDh,3Fh", /* 38-3f */ + "IN B,(C)", "OUT (C),B", "SBC HL,BC", "LD (#h),BC", "NEG", "RETN", "IM 0", "LD I,A", /* 40-47 */ + "IN C,(C)", "OUT (C),C", "ADC HL,BC", "LD BC,(#h)", "DB EDh,4Ch", "RETI", "DB EDh,4Eh", "LD R,A", /* 48-4f */ + "IN D,(C)", "OUT (C),D", "SBC HL,DE", "LD (#h),DE", "DB EDh,54h", "DB EDh,55h", "IM 1", "LD A,I", /* 50-57 */ + "IN E,(C)", "OUT (C),E", "ADC HL,DE", "LD DE,(#h)", "DB EDh,5Ch", "DB EDh,5Dh", "IM 2", "LD A,R", /* 58-5f */ + "IN H,(C)", "OUT (C),H", "SBC HL,HL", "LD (#h),HL", "DB EDh,64h", "DB EDh,65h", "DB EDh,66h", "RRD", /* 60-67 */ + "IN L,(C)", "OUT (C),L", "ADC HL,HL", "LD HL,(#h)", "DB EDh,6Ch", "DB EDh,6Dh", "DB EDh,6Eh", "RLD", /* 68-6f */ + "IN F,(C)", "DB EDh,71h", "SBC HL,SP", "LD (#h),SP", "DB EDh,74h", "DB EDh,75h", "DB EDh,76h", "DB EDh,77h", /* 70-77 */ + "IN A,(C)", "OUT (C),A", "ADC HL,SP", "LD SP,(#h)", "DB EDh,7Ch", "DB EDh,7Dh", "DB EDh,7Eh", "DB EDh,7Fh", /* 78-7f */ + "DB EDh,80h", "DB EDh,81h", "DB EDh,82h", "DB EDh,83h", "DB EDh,84h", "DB EDh,85h", "DB EDh,86h", "DB EDh,87h", /* 80-87 */ + "DB EDh,88h", "DB EDh,89h", "DB EDh,8Ah", "DB EDh,8Bh", "DB EDh,8Ch", "DB EDh,8Dh", "DB EDh,8Eh", "DB EDh,8Fh", /* 88-8f */ + "DB EDh,90h", "DB EDh,91h", "DB EDh,92h", "DB EDh,93h", "DB EDh,94h", "DB EDh,95h", "DB EDh,96h", "DB EDh,97h", /* 90-97 */ + "DB EDh,98h", "DB EDh,99h", "DB EDh,9Ah", "DB EDh,9Bh", "DB EDh,9Ch", "DB EDh,9Dh", "DB EDh,9Eh", "DB EDh,9Fh", /* 98-9f */ + "LDI", "CPI", "INI", "OUTI", "DB EDh,A4h", "DB EDh,A5h", "DB EDh,A6h", "DB EDh,A7h", /* a0-a7 */ + "LDD", "CPD", "IND", "OUTD", "DB EDh,ACh", "DB EDh,ADh", "DB EDh,AEh", "DB EDh,AFh", /* a8-af */ + "LDIR", "CPIR", "INIR", "OTIR", "DB EDh,B4h", "DB EDh,B5h", "DB EDh,B6h", "DB EDh,B7h", /* b0-b7 */ + "LDDR", "CPDR", "INDR", "OTDR", "DB EDh,BCh", "DB EDh,BDh", "DB EDh,BEh", "DB EDh,BFh", /* b8-bf */ + "DB EDh,C0h", "DB EDh,C1h", "DB EDh,C2h", "DB EDh,C3h", "DB EDh,C4h", "DB EDh,C5h", "DB EDh,C6h", "DB EDh,C7h", /* c0-c7 */ + "DB EDh,C8h", "DB EDh,C9h", "DB EDh,CAh", "DB EDh,CBh", "DB EDh,CCh", "DB EDh,CDh", "DB EDh,CEh", "DB EDh,CFh", /* c8-cf */ + "DB EDh,D0h", "DB EDh,D1h", "DB EDh,D2h", "DB EDh,D3h", "DB EDh,D4h", "DB EDh,D5h", "DB EDh,D6h", "DB EDh,D7h", /* d0-d7 */ + "DB EDh,D8h", "DB EDh,D9h", "DB EDh,DAh", "DB EDh,DBh", "DB EDh,DCh", "DB EDh,DDh", "DB EDh,DEh", "DB EDh,DFh", /* d8-df */ + "DB EDh,E0h", "DB EDh,E1h", "DB EDh,E2h", "DB EDh,E3h", "DB EDh,E4h", "DB EDh,E5h", "DB EDh,E6h", "DB EDh,E7h", /* e0-e7 */ + "DB EDh,E8h", "DB EDh,E9h", "DB EDh,EAh", "DB EDh,EBh", "DB EDh,ECh", "DB EDh,EDh", "DB EDh,EEh", "DB EDh,EFh", /* e8-ef */ + "DB EDh,F0h", "DB EDh,F1h", "DB EDh,F2h", "DB EDh,F3h", "DB EDh,F4h", "DB EDh,F5h", "DB EDh,F6h", "DB EDh,F7h", /* f0-f7 */ + "DB EDh,F8h", "DB EDh,F9h", "DB EDh,FAh", "DB EDh,FBh", "DB EDh,FCh", "DB EDh,FDh", "DB EDh,FEh", "DB EDh,FFh" /* f8-ff */ +}; + +static char *const MnemonicsXX[256] = { +/* 0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "NOP", "LD BC,#h", "LD (BC),A", "INC BC", "INC B", "DEC B", "LD B,*h", "RLCA", /* 00-07 */ + "EX AF,AF'", "ADD I%,BC", "LD A,(BC)", "DEC BC", "INC C", "DEC C", "LD C,*h", "RRCA", /* 08-0f */ + "DJNZ $h", "LD DE,#h", "LD (DE),A", "INC DE", "INC D", "DEC D", "LD D,*h", "RLA", /* 10-17 */ + "JR $h", "ADD I%,DE", "LD A,(DE)", "DEC DE", "INC E", "DEC E", "LD E,*h", "RRA", /* 18-1f */ + "JR NZ,$h", "LD I%,#h", "LD (#h),I%", "INC I%", "INC I%H", "DEC I%H", "LD I%H,*h", "DAA", /* 20-27 */ + "JR Z,$h", "ADD I%,I%", "LD I%,(#h)", "DEC I%", "INC I%L", "DEC I%L", "LD I%L,*h", "CPL", /* 28-2f */ + "JR NC,$h", "LD SP,#h", "LD (#h),A", "INC SP", "INC (I%+^h)", "DEC (I%+^h)", "LD (I%+^h),*h","SCF", /* 30-37 */ + "JR C,$h", "ADD I%,SP", "LD A,(#h)", "DEC SP", "INC A", "DEC A", "LD A,*h", "CCF", /* 38-3f */ + "LD B,B", "LD B,C", "LD B,D", "LD B,E", "LD B,I%H", "LD B,I%L", "LD B,(I%+^h)", "LD B,A", /* 40-47 */ + "LD C,B", "LD C,C", "LD C,D", "LD C,E", "LD C,I%H", "LD C,I%L", "LD C,(I%+^h)", "LD C,A", /* 48-4f */ + "LD D,B", "LD D,C", "LD D,D", "LD D,E", "LD D,I%H", "LD D,I%L", "LD D,(I%+^h)", "LD D,A", /* 50-57 */ + "LD E,B", "LD E,C", "LD E,D", "LD E,E", "LD E,I%H", "LD E,I%L", "LD E,(I%+^h)", "LD E,A", /* 58-5f */ + "LD I%H,B", "LD I%H,C", "LD I%H,D", "LD I%H,E", "LD I%H,I%H", "LD I%H,I%L", "LD H,(I%+^h)", "LD I%H,A", /* 60-67 */ + "LD I%L,B", "LD I%L,C", "LD I%L,D", "LD I%L,E", "LD I%L,I%H", "LD I%L,I%L", "LD L,(I%+^h)", "LD I%L,A", /* 68-6f */ + "LD (I%+^h),B", "LD (I%+^h),C", "LD (I%+^h),D", "LD (I%+^h),E", "LD (I%+^h),H", "LD (I%+^h),L", "HALT", "LD (I%+^h),A", /* 70-77 */ + "LD A,B", "LD A,C", "LD A,D", "LD A,E", "LD A,I%H", "LD A,I%L", "LD A,(I%+^h)", "LD A,A", /* 78-7f */ + "ADD A,B", "ADD A,C", "ADD A,D", "ADD A,E", "ADD A,I%H", "ADD A,I%L", "ADD A,(I%+^h)","ADD A,A", /* 80-87 */ + "ADC A,B", "ADC A,C", "ADC A,D", "ADC A,E", "ADC A,I%H", "ADC A,I%L", "ADC A,(I%+^h)","ADC A,A", /* 88-8f */ + "SUB B", "SUB C", "SUB D", "SUB E", "SUB I%H", "SUB I%L", "SUB (I%+^h)", "SUB A", /* 90-97 */ + "SBC A,B", "SBC A,C", "SBC A,D", "SBC A,E", "SBC A,I%H", "SBC A,I%L", "SBC A,(I%+^h)","SBC A,A", /* 98-9f */ + "AND B", "AND C", "AND D", "AND E", "AND I%H", "AND I%L", "AND (I%+^h)", "AND A", /* a0-a7 */ + "XOR B", "XOR C", "XOR D", "XOR E", "XOR I%H", "XOR I%L", "XOR (I%+^h)", "XOR A", /* a8-af */ + "OR B", "OR C", "OR D", "OR E", "OR I%H", "OR I%L", "OR (I%+^h)", "OR A", /* b0-b7 */ + "CP B", "CP C", "CP D", "CP E", "CP I%H", "CP I%L", "CP (I%+^h)", "CP A", /* b8-bf */ + "RET NZ", "POP BC", "JP NZ,#h", "JP #h", "CALL NZ,#h", "PUSH BC", "ADD A,*h", "RST 00h", /* c8-cf */ + "RET Z", "RET", "JP Z,#h", "PFX_CB", "CALL Z,#h", "CALL #h", "ADC A,*h", "RST 08h", /* c8-cf */ + "RET NC", "POP DE", "JP NC,#h", "OUT (*h),A", "CALL NC,#h", "PUSH DE", "SUB *h", "RST 10h", /* d0-d7 */ + "RET C", "EXX", "JP C,#h", "IN A,(*h)", "CALL C,#h", "PFX_DD", "SBC A,*h", "RST 18h", /* d8-df */ + "RET PO", "POP I%", "JP PO,#h", "EX (SP),I%", "CALL PO,#h", "PUSH I%", "AND *h", "RST 20h", /* e0-e7 */ + "RET PE", "LD PC,I%", "JP PE,#h", "EX DE,I%", "CALL PE,#h", "PFX_ED", "XOR *h", "RST 28h", /* e8-ef */ + "RET P", "POP AF", "JP P,#h", "DI", "CALL P,#h", "PUSH AF", "OR *h", "RST 30h", /* f0-f7 */ + "RET M", "LD SP,I%", "JP M,#h", "EI", "CALL M,#h", "PFX_FD", "CP *h", "RST 38h" /* f8-ff */ +}; + +static char *const MnemonicsXCB[256] = { +/*0/8 1/9 2/A 3/B 4/C 5/D 6/E 7/F */ + "RLC B", "RLC C", "RLC D", "RLC E", "RLC H", "RLC L", "RLC (I%@h)", "RLC A", /* 00-07 */ + "RRC B", "RRC C", "RRC D", "RRC E", "RRC H", "RRC L", "RRC (I%@h)", "RRC A", /* 08-0f */ + "RL B", "RL C", "RL D", "RL E", "RL H", "RL L", "RL (I%@h)", "RL A", /* 10-17 */ + "RR B", "RR C", "RR D", "RR E", "RR H", "RR L", "RR (I%@h)", "RR A", /* 18-1f */ + "SLA B", "SLA C", "SLA D", "SLA E", "SLA H", "SLA L", "SLA (I%@h)", "SLA A", /* 20-27 */ + "SRA B", "SRA C", "SRA D", "SRA E", "SRA H", "SRA L", "SRA (I%@h)", "SRA A", /* 28-2f */ + "SLL B", "SLL C", "SLL D", "SLL E", "SLL H", "SLL L", "SLL (I%@h)", "SLL A", /* 30-37 */ + "SRL B", "SRL C", "SRL D", "SRL E", "SRL H", "SRL L", "SRL (I%@h)", "SRL A", /* 38-3f */ + "BIT 0,B", "BIT 0,C", "BIT 0,D", "BIT 0,E", "BIT 0,H", "BIT 0,L", "BIT 0,(I%@h)", "BIT 0,A", /* 40-47 */ + "BIT 1,B", "BIT 1,C", "BIT 1,D", "BIT 1,E", "BIT 1,H", "BIT 1,L", "BIT 1,(I%@h)", "BIT 1,A", /* 48-4f */ + "BIT 2,B", "BIT 2,C", "BIT 2,D", "BIT 2,E", "BIT 2,H", "BIT 2,L", "BIT 2,(I%@h)", "BIT 2,A", /* 50-57 */ + "BIT 3,B", "BIT 3,C", "BIT 3,D", "BIT 3,E", "BIT 3,H", "BIT 3,L", "BIT 3,(I%@h)", "BIT 3,A", /* 58-5f */ + "BIT 4,B", "BIT 4,C", "BIT 4,D", "BIT 4,E", "BIT 4,H", "BIT 4,L", "BIT 4,(I%@h)", "BIT 4,A", /* 60-67 */ + "BIT 5,B", "BIT 5,C", "BIT 5,D", "BIT 5,E", "BIT 5,H", "BIT 5,L", "BIT 5,(I%@h)", "BIT 5,A", /* 68-6f */ + "BIT 6,B", "BIT 6,C", "BIT 6,D", "BIT 6,E", "BIT 6,H", "BIT 6,L", "BIT 6,(I%@h)", "BIT 6,A", /* 70-77 */ + "BIT 7,B", "BIT 7,C", "BIT 7,D", "BIT 7,E", "BIT 7,H", "BIT 7,L", "BIT 7,(I%@h)", "BIT 7,A", /* 78-7f */ + "RES 0,B", "RES 0,C", "RES 0,D", "RES 0,E", "RES 0,H", "RES 0,L", "RES 0,(I%@h)", "RES 0,A", /* 80-87 */ + "RES 1,B", "RES 1,C", "RES 1,D", "RES 1,E", "RES 1,H", "RES 1,L", "RES 1,(I%@h)", "RES 1,A", /* 88-8f */ + "RES 2,B", "RES 2,C", "RES 2,D", "RES 2,E", "RES 2,H", "RES 2,L", "RES 2,(I%@h)", "RES 2,A", /* 90-97 */ + "RES 3,B", "RES 3,C", "RES 3,D", "RES 3,E", "RES 3,H", "RES 3,L", "RES 3,(I%@h)", "RES 3,A", /* 98-9f */ + "RES 4,B", "RES 4,C", "RES 4,D", "RES 4,E", "RES 4,H", "RES 4,L", "RES 4,(I%@h)", "RES 4,A", /* a0-a7 */ + "RES 5,B", "RES 5,C", "RES 5,D", "RES 5,E", "RES 5,H", "RES 5,L", "RES 5,(I%@h)", "RES 5,A", /* a8-af */ + "RES 6,B", "RES 6,C", "RES 6,D", "RES 6,E", "RES 6,H", "RES 6,L", "RES 6,(I%@h)", "RES 6,A", /* b0-b7 */ + "RES 7,B", "RES 7,C", "RES 7,D", "RES 7,E", "RES 7,H", "RES 7,L", "RES 7,(I%@h)", "RES 7,A", /* b8-bf */ + "SET 0,B", "SET 0,C", "SET 0,D", "SET 0,E", "SET 0,H", "SET 0,L", "SET 0,(I%@h)", "SET 0,A", /* c0-c7 */ + "SET 1,B", "SET 1,C", "SET 1,D", "SET 1,E", "SET 1,H", "SET 1,L", "SET 1,(I%@h)", "SET 1,A", /* c8-cf */ + "SET 2,B", "SET 2,C", "SET 2,D", "SET 2,E", "SET 2,H", "SET 2,L", "SET 2,(I%@h)", "SET 2,A", /* d0-d7 */ + "SET 3,B", "SET 3,C", "SET 3,D", "SET 3,E", "SET 3,H", "SET 3,L", "SET 3,(I%@h)", "SET 3,A", /* d8-df */ + "SET 4,B", "SET 4,C", "SET 4,D", "SET 4,E", "SET 4,H", "SET 4,L", "SET 4,(I%@h)", "SET 4,A", /* e0-e7 */ + "SET 5,B", "SET 5,C", "SET 5,D", "SET 5,E", "SET 5,H", "SET 5,L", "SET 5,(I%@h)", "SET 5,A", /* e8-ef */ + "SET 6,B", "SET 6,C", "SET 6,D", "SET 6,E", "SET 6,H", "SET 6,L", "SET 6,(I%@h)", "SET 6,A", /* f0-f7 */ + "SET 7,B", "SET 7,C", "SET 7,D", "SET 7,E", "SET 7,H", "SET 7,L", "SET 7,(I%@h)", "SET 7,A" /* f8-ff */ +}; + +/* Symbolic disassembler + + Inputs: + *val = instructions to disassemble + useZ80Mnemonics = > 0 iff Z80 mnemonics are to be used + addr = current PC + Outputs: + *S = output text + + DAsm is Copyright (C) Marat Fayzullin 1995,1996,1997 + You are not allowed to distribute this software + commercially. + +*/ + +static int32 DAsm(char *S, const uint32 *val, const int32 useZ80Mnemonics, const int32 addr) { + char R[128], H[10], C = '\0', *T, *P; + uint8 J = 0, Offset = 0; + uint16 B = 0; + + if (useZ80Mnemonics) + switch(val[B]) { + + case 0xcb: + B++; + T = MnemonicsCB[val[B++]]; + break; + + case 0xed: + B++; + T = MnemonicsED[val[B++]]; + break; + + case 0xdd: + + case 0xfd: + C = (val[B++] == 0xdd) ? 'X' : 'Y'; + if (val[B] == 0xcb) { + B++; + Offset = val[B++]; + J = 1; + T = MnemonicsXCB[val[B++]]; + } + else T = MnemonicsXX[val[B++]]; + break; + + default: + T = MnemonicsZ80[val[B++]]; + } + else T = Mnemonics8080[val[B++]]; + + if ( (P = strchr(T, '^')) ) { + strncpy(R, T, P - T); + R[P - T] = '\0'; + sprintf(H, "%02X", val[B++]); + strcat(R, H); + strcat(R, P + 1); + } + else strcpy(R, T); + if ( (P = strchr(R, '%')) ) { + *P = C; + if ( (P = strchr(P + 1, '%')) ) *P = C; + } + + if ( (P = strchr(R, '*')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + sprintf(H, "%02X", val[B++]); + strcat(S, H); + strcat(S, P + 1); + } + else if ( (P = strchr(R, '@')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + if (!J) Offset = val[B++]; + strcat(S, Offset & 0x80 ? "-" : "+"); + J = Offset & 0x80 ? 256 - Offset : Offset; + sprintf(H, "%02X", J); + strcat(S, H); + strcat(S, P + 1); + } + else if ( (P = strchr(R, '$')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + Offset = val[B++]; + sprintf(H, "%04X", (addr + 2 + (Offset & 0x80 ? (Offset - 256) : Offset)) & 0xFFFF); + strcat(S, H); + strcat(S, P + 1); + } + else if ( (P = strchr(R, '#')) ) { + strncpy(S, R, P - R); + S[P - R] = '\0'; + sprintf(H, "%04X", val[B] + 256 * val[B + 1]); + strcat(S, H); + strcat(S, P + 1); + B += 2; + } + else strcpy(S, R); + return(B); +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +t_stat fprint_sym(FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) { + char disasm_result[128]; + int32 ch = val[0] & 0x7f; + long r; + unsigned char vals[SIM_EMAX]; + int32 i; + if (sw & (SWMASK('A') | SWMASK('C'))) { + fprintf(of, ((0x20 <= ch) && (ch < 0x7f)) ? "'%c'" : "%02x", ch); + return SCPE_OK; + } + if (!(sw & SWMASK('M'))) return SCPE_ARG; + if (chiptype == CHIP_TYPE_8086) { + for (i = 0; i < SIM_EMAX; i++) vals[i] = val[i] & 0xff; + r = disasm(vals, disasm_result, 16, addr); + } + else + r = DAsm(disasm_result, val, chiptype == CHIP_TYPE_Z80, addr); + fprintf(of, "%s", disasm_result); + return 1 - r; +} + +/* checkbase determines the base of the number (ch, *numString) + and returns FALSE if the number is bad */ +static int32 checkbase(char ch, const char *numString) { + int32 decimal = (ch <= '9'); + if (toupper(ch) == 'H') return FALSE; + while (isxdigit(ch = *numString++)) if (ch > '9') decimal = FALSE; + return toupper(ch) == 'H' ? 16 : (decimal ? 10 : FALSE); +} + +static int32 numok(char ch, const char **numString, const int32 minvalue, + const int32 maxvalue, const int32 requireSign, int32 *result) { + int32 sign = 1, value = 0, base; + if (requireSign) + if (ch == '+') ch = *(*numString)++; + else if (ch == '-') { + sign = -1; + ch = *(*numString)++; + } + else return FALSE; + if (!(base = checkbase(ch, *numString))) return FALSE; + while (isxdigit(ch)) { + value = base * value + ((ch <= '9') ? (ch - '0') : (toupper(ch) - 'A' + 10)); + ch = *(*numString)++; + } + if (toupper(ch) != 'H') (*numString)--; + *result = value * sign; + return (minvalue <= value) && (value <= maxvalue); +} + +static int32 match(const char *pattern, const char *input, char *xyFirst, char *xy, int32 *number, int32 *star, + int32 *at, int32 *hat, int32 *dollar) { + char pat = *pattern++; + char inp = *input++; + while ((pat) && (inp)) { + switch(pat) { + + case '_': /* patterns containing '_' should never match */ + return FALSE; + + case ',': + if (inp == ' ') { + inp = *input++; + continue; + } /* otherwise fall through */ + + case ' ': + if (inp != pat) return FALSE; + pat = *pattern++; + inp = *input++; + while (inp == ' ') inp = *input++; + continue; + + case '%': + inp = toupper(inp); + if ((inp == 'X') || (inp == 'Y')) + if (*xyFirst) /* make sure that second '%' corresponds to first */ + if (*xyFirst == inp) *xy = inp; + else return FALSE; + else { /* take note of first '%' for later */ + *xyFirst = inp; + *xy = inp; + } + else return FALSE; + break; + + case '#': + if (numok(inp, &input, 0, 65535, FALSE, number)) pattern++; /* skip h */ + else return FALSE; + break; + + case '*': + if (numok(inp, &input, 0, 255, FALSE, star)) pattern++; /* skip h */ + else return FALSE; + break; + + case '@': + if (numok(inp, &input, -128, 65535, TRUE, at)) pattern++; /* skip h */ + else return FALSE; + break; + + case '$': + if (numok(inp, &input, 0, 65535, FALSE, dollar)) pattern++; /* skip h */ + else return FALSE; + break; + + case '^': + if (numok(inp, &input, 0, 255, FALSE, hat)) pattern++; /* skip h */ + else return FALSE; + break; + + default: + if (toupper(pat) != toupper(inp)) return FALSE; + } + pat = *pattern++; + inp = *input++; + } + while (inp == ' ') inp = *input++; + return (pat == 0) && (inp == 0); +} + +static int32 checkXY(const char xy) { + return xy == 'X' ? 0xdd : 0xfd; /* else is 'Y' */ +} + +static int32 parse_X80(const char *cptr, const int32 addr, uint32 *val, char *const Mnemonics[]) { + char xyFirst = 0, xy; + int32 op, number, star, at, hat, dollar; + for (op = 0; op < 256; op++) { + number = star = at = dollar = -129; + if (match(Mnemonics[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + val[0] = op; + if (number >= 0) { + val[1] = (0xff) & number; + val[2] = (0xff) & (number >> 8); + return -2; /* two additional bytes returned */ + } + else if (star >= 0) { + val[1] = (0xff) & star; + return -1; /* one additional byte returned */ + } + else if (at > -129) + if ((-128 <= at) && (at <= 127)) { + val[1] = (int8)(at); + return -1; /* one additional byte returned */ + } + else return SCPE_ARG; + else if (dollar >= 0) { + dollar -= addr + 2; /* relative translation */ + if ((-128 <= dollar) && (dollar <= 127)) { + val[1] = (int8)(dollar); + return -1; /* one additional byte returned */ + } + else return SCPE_ARG; + } + else return SCPE_OK; + } + } + if (Mnemonics == Mnemonics8080) return SCPE_ARG; + + for (op = 0; op < 256; op++) + if (match(MnemonicsCB[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + val[0] = 0xcb; + val[1] = op; + return -1; /* one additional byte returned */ + } + + for (op = 0; op < 256; op++) { + number = -1; + if (match(MnemonicsED[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + val[0] = 0xed; + val[1] = op; + if (number >= 0) { + val[2] = (0xff) & number; + val[3] = (0xff) & (number >> 8); + return -3; /* three additional bytes returned */ + } + else return -1; /* one additional byte returned */ + } + } + + for (op = 0; op < 256; op++) { + number = star = hat = -1; + xy = 0; + if (match(MnemonicsXX[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + /* all matches must have contained a '%' character */ + if (!(val[0] = checkXY(xy))) return SCPE_ARG; + val[1] = op; + if (number >= 0) { + val[2] = (0xff) & number; + val[3] = (0xff) & (number >> 8); + return -3; /* three additional bytes returned */ + } + else if ((star >= 0) && (hat >= 0)) { + val[2] = (0xff) & hat; + val[3] = (0xff) & star; + return -3; /* three additional bytes returned */ + } + else if (star >= 0) { + val[2] = (0xff) & star; + return -2; /* two additional bytes returned */ + } + else if (hat >= 0) { + val[2] = (0xff) & hat; + return -2; /* two additional bytes returned */ + } + else return -1; /* one additional byte returned */ + } + } + + for (op = 0; op < 256; op++) { + at = -129; + xy = 0; + if (match(MnemonicsXCB[op], cptr, &xyFirst, &xy, &number, &star, &at, &hat, &dollar)) { + /* all matches must have contained a '%' character */ + if (!(val[0] = checkXY(xy))) return SCPE_ARG; + val[1] = 0xcb; + if (at > -129) val[2] = (int8) (at); + else { + printf("Offset expected.\n"); + return SCPE_ARG; + } + val[3] = op; + return -3; /* three additional bytes returned */ + } + } + return SCPE_ARG; +} + + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ +t_stat parse_sym(char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) { + while (isspace(*cptr)) cptr++; /* absorb spaces */ + if ((sw & (SWMASK('A') | SWMASK('C'))) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have one char */ + val[0] = (uint32) cptr[0]; + return SCPE_OK; + } + return parse_X80(cptr, addr, val, chiptype == CHIP_TYPE_Z80 ? MnemonicsZ80 : Mnemonics8080); +} + +/* Set Memory Base Address routine */ +t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc) +{ + DEVICE *dptr; + PNP_INFO *pnp; + uint32 newba; + t_stat r; + + if (cptr == NULL) return SCPE_ARG; + if (uptr == NULL) return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) return SCPE_IERR; + pnp = (PNP_INFO *) dptr->ctxt; + if (pnp == NULL) return SCPE_IERR; + + newba = get_uint (cptr, 16, 0xFFFF, &r); + if (r != SCPE_OK) return r; + + if ((newba > 0xFFFF) || + (newba % pnp->mem_size)) return SCPE_ARG; + + if(dptr->flags & DEV_DIS) { + printf("device not enabled yet.\n"); + pnp->mem_base = newba & ~(pnp->mem_size-1); + } else { + dptr->flags |= DEV_DIS; + dptr->reset(dptr); + pnp->mem_base = newba & ~(pnp->mem_size-1); + dptr->flags &= ~DEV_DIS; + dptr->reset(dptr); + } + + return SCPE_OK; +} + +/* Show Base Address routine */ +t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc) +{ + DEVICE *dptr; + PNP_INFO *pnp; + + if (uptr == NULL) return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) return SCPE_IERR; + pnp = (PNP_INFO *) dptr->ctxt; + if (pnp == NULL) return SCPE_IERR; + + fprintf(st, "MEM=0x%04X-0x%04X", pnp->mem_base, pnp->mem_base+pnp->mem_size-1); + return SCPE_OK; +} + +/* Set Memory Base Address routine */ +t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc) +{ + DEVICE *dptr; + PNP_INFO *pnp; + uint32 newba; + t_stat r; + + if (cptr == NULL) return SCPE_ARG; + if (uptr == NULL) return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) return SCPE_IERR; + pnp = (PNP_INFO *) dptr->ctxt; + if (pnp == NULL) return SCPE_IERR; + + newba = get_uint (cptr, 16, 0xFF, &r); + if (r != SCPE_OK) return r; + + if ((newba > 0xFF) || + (newba % pnp->io_size)) return SCPE_ARG; + + if(dptr->flags & DEV_DIS) { + printf("device not enabled yet.\n"); + pnp->io_base = newba & ~(pnp->io_size-1); + } else { + dptr->flags |= DEV_DIS; + dptr->reset(dptr); + pnp->io_base = newba & ~(pnp->io_size-1); + dptr->flags &= ~DEV_DIS; + dptr->reset(dptr); + } + + return SCPE_OK; +} + +/* Show I/O Base Address routine */ +t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc) +{ + DEVICE *dptr; + PNP_INFO *pnp; + + if (uptr == NULL) return SCPE_IERR; + dptr = find_dev_from_unit (uptr); + if (dptr == NULL) return SCPE_IERR; + pnp = (PNP_INFO *) dptr->ctxt; + if (pnp == NULL) return SCPE_IERR; + + fprintf(st, "I/O=0x%02X-0x%02X", pnp->io_base, pnp->io_base+pnp->io_size-1); + return SCPE_OK; +} + diff --git a/AltairZ80/disasm.c b/AltairZ80/disasm.c new file mode 100644 index 0000000..8baaf15 --- /dev/null +++ b/AltairZ80/disasm.c @@ -0,0 +1,1424 @@ +/* disasm.c where all the _work_ gets done in the Netwide Disassembler + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + * + * initial version 27/iii/95 by Simon Tatham + */ + +#include +#include + +#include "nasm.h" +#include "insns.h" + +/* names.c included source file defining instruction and register + * names for the Netwide [Dis]Assembler + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + */ + +static const char *conditions[] = { /* condition code names */ + "a", "ae", "b", "be", "c", "e", "g", "ge", "l", "le", "na", "nae", + "nb", "nbe", "nc", "ne", "ng", "nge", "nl", "nle", "no", "np", + "ns", "nz", "o", "p", "pe", "po", "s", "z" +}; + +/* Register names automatically generated from regs.dat */ +/* automatically generated from ./regs.dat - do not edit */ +static const char *reg_names[] = { + "ah", + "al", + "ax", + "bh", + "bl", + "bp", + "bx", + "ch", + "cl", + "cr0", + "cr1", + "cr2", + "cr3", + "cr4", + "cr5", + "cr6", + "cr7", + "cs", + "cx", + "dh", + "di", + "dl", + "dr0", + "dr1", + "dr2", + "dr3", + "dr4", + "dr5", + "dr6", + "dr7", + "ds", + "dx", + "eax", + "ebp", + "ebx", + "ecx", + "edi", + "edx", + "es", + "esi", + "esp", + "fs", + "gs", + "mm0", + "mm1", + "mm2", + "mm3", + "mm4", + "mm5", + "mm6", + "mm7", + "segr6", + "segr7", + "si", + "sp", + "ss", + "st0", + "st1", + "st2", + "st3", + "st4", + "st5", + "st6", + "st7", + "tr0", + "tr1", + "tr2", + "tr3", + "tr4", + "tr5", + "tr6", + "tr7", + "xmm0", + "xmm1", + "xmm2", + "xmm3", + "xmm4", + "xmm5", + "xmm6", + "xmm7" +}; + +/* Instruction names automatically generated from insns.dat */ +/* This file is auto-generated from insns.dat by insns.pl - don't edit it */ +/* This file in included by names.c */ +static const char *insn_names[] = { + "aaa", + "aad", + "aam", + "aas", + "adc", + "add", + "addpd", + "addps", + "addsd", + "addss", + "addsubpd", + "addsubps", + "and", + "andnpd", + "andnps", + "andpd", + "andps", + "arpl", + "bound", + "bsf", + "bsr", + "bswap", + "bt", + "btc", + "btr", + "bts", + "call", + "cbw", + "cdq", + "clc", + "cld", + "clflush", + "cli", + "clts", + "cmc", + "cmp", + "cmpeqpd", + "cmpeqps", + "cmpeqsd", + "cmpeqss", + "cmplepd", + "cmpleps", + "cmplesd", + "cmpless", + "cmpltpd", + "cmpltps", + "cmpltsd", + "cmpltss", + "cmpneqpd", + "cmpneqps", + "cmpneqsd", + "cmpneqss", + "cmpnlepd", + "cmpnleps", + "cmpnlesd", + "cmpnless", + "cmpnltpd", + "cmpnltps", + "cmpnltsd", + "cmpnltss", + "cmpordpd", + "cmpordps", + "cmpordsd", + "cmpordss", + "cmppd", + "cmpps", + "cmpsb", + "cmpsd", + "cmpss", + "cmpsw", + "cmpunordpd", + "cmpunordps", + "cmpunordsd", + "cmpunordss", + "cmpxchg", + "cmpxchg486", + "cmpxchg8b", + "comisd", + "comiss", + "cpuid", + "cvtdq2pd", + "cvtdq2ps", + "cvtpd2dq", + "cvtpd2pi", + "cvtpd2ps", + "cvtpi2pd", + "cvtpi2ps", + "cvtps2dq", + "cvtps2pd", + "cvtps2pi", + "cvtsd2si", + "cvtsd2ss", + "cvtsi2sd", + "cvtsi2ss", + "cvtss2sd", + "cvtss2si", + "cvttpd2dq", + "cvttpd2pi", + "cvttps2dq", + "cvttps2pi", + "cvttsd2si", + "cvttss2si", + "cwd", + "cwde", + "daa", + "das", + "db", + "dd", + "dec", + "div", + "divpd", + "divps", + "divsd", + "divss", + "dq", + "dt", + "dw", + "emms", + "enter", + "equ", + "f2xm1", + "fabs", + "fadd", + "faddp", + "fbld", + "fbstp", + "fchs", + "fclex", + "fcmovb", + "fcmovbe", + "fcmove", + "fcmovnb", + "fcmovnbe", + "fcmovne", + "fcmovnu", + "fcmovu", + "fcom", + "fcomi", + "fcomip", + "fcomp", + "fcompp", + "fcos", + "fdecstp", + "fdisi", + "fdiv", + "fdivp", + "fdivr", + "fdivrp", + "femms", + "feni", + "ffree", + "ffreep", + "fiadd", + "ficom", + "ficomp", + "fidiv", + "fidivr", + "fild", + "fimul", + "fincstp", + "finit", + "fist", + "fistp", + "fisttp", + "fisub", + "fisubr", + "fld", + "fld1", + "fldcw", + "fldenv", + "fldl2e", + "fldl2t", + "fldlg2", + "fldln2", + "fldpi", + "fldz", + "fmul", + "fmulp", + "fnclex", + "fndisi", + "fneni", + "fninit", + "fnop", + "fnsave", + "fnstcw", + "fnstenv", + "fnstsw", + "fpatan", + "fprem", + "fprem1", + "fptan", + "frndint", + "frstor", + "fsave", + "fscale", + "fsetpm", + "fsin", + "fsincos", + "fsqrt", + "fst", + "fstcw", + "fstenv", + "fstp", + "fstsw", + "fsub", + "fsubp", + "fsubr", + "fsubrp", + "ftst", + "fucom", + "fucomi", + "fucomip", + "fucomp", + "fucompp", + "fwait", + "fxam", + "fxch", + "fxrstor", + "fxsave", + "fxtract", + "fyl2x", + "fyl2xp1", + "haddpd", + "haddps", + "hlt", + "hsubpd", + "hsubps", + "ibts", + "icebp", + "idiv", + "imul", + "in", + "inc", + "incbin", + "insb", + "insd", + "insw", + "int", + "int01", + "int03", + "int1", + "int3", + "into", + "invd", + "invlpg", + "iret", + "iretd", + "iretw", + "jcxz", + "jecxz", + "jmp", + "jmpe", + "lahf", + "lar", + "lddqu", + "ldmxcsr", + "lds", + "lea", + "leave", + "les", + "lfence", + "lfs", + "lgdt", + "lgs", + "lidt", + "lldt", + "lmsw", + "loadall", + "loadall286", + "lodsb", + "lodsd", + "lodsw", + "loop", + "loope", + "loopne", + "loopnz", + "loopz", + "lsl", + "lss", + "ltr", + "maskmovdqu", + "maskmovq", + "maxpd", + "maxps", + "maxsd", + "maxss", + "mfence", + "minpd", + "minps", + "minsd", + "minss", + "monitor", + "mov", + "movapd", + "movaps", + "movd", + "movddup", + "movdq2q", + "movdqa", + "movdqu", + "movhlps", + "movhpd", + "movhps", + "movlhps", + "movlpd", + "movlps", + "movmskpd", + "movmskps", + "movntdq", + "movnti", + "movntpd", + "movntps", + "movntq", + "movq", + "movq2dq", + "movsb", + "movsd", + "movshdup", + "movsldup", + "movss", + "movsw", + "movsx", + "movupd", + "movups", + "movzx", + "mul", + "mulpd", + "mulps", + "mulsd", + "mulss", + "mwait", + "neg", + "nop", + "not", + "or", + "orpd", + "orps", + "out", + "outsb", + "outsd", + "outsw", + "packssdw", + "packsswb", + "packuswb", + "paddb", + "paddd", + "paddq", + "paddsb", + "paddsiw", + "paddsw", + "paddusb", + "paddusw", + "paddw", + "pand", + "pandn", + "pause", + "paveb", + "pavgb", + "pavgusb", + "pavgw", + "pcmpeqb", + "pcmpeqd", + "pcmpeqw", + "pcmpgtb", + "pcmpgtd", + "pcmpgtw", + "pdistib", + "pextrw", + "pf2id", + "pf2iw", + "pfacc", + "pfadd", + "pfcmpeq", + "pfcmpge", + "pfcmpgt", + "pfmax", + "pfmin", + "pfmul", + "pfnacc", + "pfpnacc", + "pfrcp", + "pfrcpit1", + "pfrcpit2", + "pfrsqit1", + "pfrsqrt", + "pfsub", + "pfsubr", + "pi2fd", + "pi2fw", + "pinsrw", + "pmachriw", + "pmaddwd", + "pmagw", + "pmaxsw", + "pmaxub", + "pminsw", + "pminub", + "pmovmskb", + "pmulhriw", + "pmulhrwa", + "pmulhrwc", + "pmulhuw", + "pmulhw", + "pmullw", + "pmuludq", + "pmvgezb", + "pmvlzb", + "pmvnzb", + "pmvzb", + "pop", + "popa", + "popad", + "popaw", + "popf", + "popfd", + "popfw", + "por", + "prefetch", + "prefetchnta", + "prefetcht0", + "prefetcht1", + "prefetcht2", + "prefetchw", + "psadbw", + "pshufd", + "pshufhw", + "pshuflw", + "pshufw", + "pslld", + "pslldq", + "psllq", + "psllw", + "psrad", + "psraw", + "psrld", + "psrldq", + "psrlq", + "psrlw", + "psubb", + "psubd", + "psubq", + "psubsb", + "psubsiw", + "psubsw", + "psubusb", + "psubusw", + "psubw", + "pswapd", + "punpckhbw", + "punpckhdq", + "punpckhqdq", + "punpckhwd", + "punpcklbw", + "punpckldq", + "punpcklqdq", + "punpcklwd", + "push", + "pusha", + "pushad", + "pushaw", + "pushf", + "pushfd", + "pushfw", + "pxor", + "rcl", + "rcpps", + "rcpss", + "rcr", + "rdmsr", + "rdpmc", + "rdshr", + "rdtsc", + "resb", + "resd", + "resq", + "rest", + "resw", + "ret", + "retf", + "retn", + "rol", + "ror", + "rsdc", + "rsldt", + "rsm", + "rsqrtps", + "rsqrtss", + "rsts", + "sahf", + "sal", + "salc", + "sar", + "sbb", + "scasb", + "scasd", + "scasw", + "sfence", + "sgdt", + "shl", + "shld", + "shr", + "shrd", + "shufpd", + "shufps", + "sidt", + "sldt", + "smi", + "smint", + "smintold", + "smsw", + "sqrtpd", + "sqrtps", + "sqrtsd", + "sqrtss", + "stc", + "std", + "sti", + "stmxcsr", + "stosb", + "stosd", + "stosw", + "str", + "sub", + "subpd", + "subps", + "subsd", + "subss", + "svdc", + "svldt", + "svts", + "syscall", + "sysenter", + "sysexit", + "sysret", + "test", + "ucomisd", + "ucomiss", + "ud0", + "ud1", + "ud2", + "umov", + "unpckhpd", + "unpckhps", + "unpcklpd", + "unpcklps", + "verr", + "verw", + "wait", + "wbinvd", + "wrmsr", + "wrshr", + "xadd", + "xbts", + "xchg", + "xlat", + "xlatb", + "xor", + "xorpd", + "xorps", + "xstore" +}; + +/* Conditional instructions */ +static const char *icn[] = { + "cmov", + "j", + "set" +}; + +/* and the corresponding opcodes */ +static int ico[] = { + I_CMOVcc, + I_Jcc, + I_SETcc +}; + +#define INSN_MAX 32 /* one instruction can't be longer than this */ +long disasm (unsigned char *data, char *output, int segsize, long offset); +extern struct itemplate **itable[]; + +/* + * Flags that go into the `segment' field of `insn' structures + * during disassembly. + */ +#define SEG_RELATIVE 1 +#define SEG_32BIT 2 +#define SEG_RMREG 4 +#define SEG_DISP8 8 +#define SEG_DISP16 16 +#define SEG_DISP32 32 +#define SEG_NODISP 64 +#define SEG_SIGNED 128 + +static int whichreg(long regflags, int regval) +{ + /* automatically generated from ./regs.dat - do not edit */ + static const int creg [] = {R_CR0,R_CR1,R_CR2,R_CR3,R_CR4,R_CR5,R_CR6,R_CR7}; + static const int dreg [] = {R_DR0,R_DR1,R_DR2,R_DR3,R_DR4,R_DR5,R_DR6,R_DR7}; + static const int fpureg [] = {R_ST0,R_ST1,R_ST2,R_ST3,R_ST4,R_ST5,R_ST6,R_ST7}; + static const int mmxreg [] = {R_MM0,R_MM1,R_MM2,R_MM3,R_MM4,R_MM5,R_MM6,R_MM7}; + static const int reg16 [] = {R_AX,R_CX,R_DX,R_BX,R_SP,R_BP,R_SI,R_DI}; + static const int reg32 [] = {R_EAX,R_ECX,R_EDX,R_EBX,R_ESP,R_EBP,R_ESI,R_EDI}; + static const int reg8 [] = {R_AL,R_CL,R_DL,R_BL,R_AH,R_CH,R_DH,R_BH}; + static const int sreg [] = {R_ES,R_CS,R_SS,R_DS,R_FS,R_GS,R_SEGR6,R_SEGR7}; + static const int treg [] = {R_TR0,R_TR1,R_TR2,R_TR3,R_TR4,R_TR5,R_TR6,R_TR7}; + static const int xmmreg [] = {R_XMM0,R_XMM1,R_XMM2,R_XMM3,R_XMM4,R_XMM5,R_XMM6,R_XMM7}; + + if (!(REG_AL & ~regflags)) + return R_AL; + if (!(REG_AX & ~regflags)) + return R_AX; + if (!(REG_EAX & ~regflags)) + return R_EAX; + if (!(REG_DL & ~regflags)) + return R_DL; + if (!(REG_DX & ~regflags)) + return R_DX; + if (!(REG_EDX & ~regflags)) + return R_EDX; + if (!(REG_CL & ~regflags)) + return R_CL; + if (!(REG_CX & ~regflags)) + return R_CX; + if (!(REG_ECX & ~regflags)) + return R_ECX; + if (!(FPU0 & ~regflags)) + return R_ST0; + if (!(REG_CS & ~regflags)) + return (regval == 1) ? R_CS : 0; + if (!(REG_DESS & ~regflags)) + return (regval == 0 || regval == 2 || regval == 3 ? sreg[regval] : 0); + if (!(REG_FSGS & ~regflags)) + return (regval == 4 || regval == 5 ? sreg[regval] : 0); + if (!(REG_SEG67 & ~regflags)) + return (regval == 6 || regval == 7 ? sreg[regval] : 0); + + /* All the entries below look up regval in an 8-entry array */ + if (regval < 0 || regval > 7) + return 0; + + if (!((REGMEM|BITS8) & ~regflags)) + return reg8[regval]; + if (!((REGMEM|BITS16) & ~regflags)) + return reg16[regval]; + if (!((REGMEM|BITS32) & ~regflags)) + return reg32[regval]; + if (!(REG_SREG & ~regflags)) + return sreg[regval]; + if (!(REG_CREG & ~regflags)) + return creg[regval]; + if (!(REG_DREG & ~regflags)) + return dreg[regval]; + if (!(REG_TREG & ~regflags)) + return treg[regval]; + if (!(FPUREG & ~regflags)) + return fpureg[regval]; + if (!(MMXREG & ~regflags)) + return mmxreg[regval]; + if (!(XMMREG & ~regflags)) + return xmmreg[regval]; + + return 0; +} + +static const char *whichcond(int condval) +{ + static int conds[] = { + C_O, C_NO, C_C, C_NC, C_Z, C_NZ, C_NA, C_A, + C_S, C_NS, C_PE, C_PO, C_L, C_NL, C_NG, C_G + }; + return conditions[conds[condval]]; +} + +/* + * Process an effective address (ModRM) specification. + */ +static unsigned char *do_ea (unsigned char *data, int modrm, int asize, + int segsize, operand *op) +{ + int mod, rm, scale, index, base; + + mod = (modrm >> 6) & 03; + rm = modrm & 07; + + if (mod == 3) { /* pure register version */ + op->basereg = rm; + op->segment |= SEG_RMREG; + return data; + } + + op->addr_size = 0; + + if (asize == 16) { + /* + * specifies the displacement size (none, byte or + * word), and specifies the register combination. + * Exception: mod=0,rm=6 does not specify [BP] as one might + * expect, but instead specifies [disp16]. + */ + op->indexreg = op->basereg = -1; + op->scale = 1; /* always, in 16 bits */ + switch (rm) { + case 0: op->basereg = R_BX; op->indexreg = R_SI; break; + case 1: op->basereg = R_BX; op->indexreg = R_DI; break; + case 2: op->basereg = R_BP; op->indexreg = R_SI; break; + case 3: op->basereg = R_BP; op->indexreg = R_DI; break; + case 4: op->basereg = R_SI; break; + case 5: op->basereg = R_DI; break; + case 6: op->basereg = R_BP; break; + case 7: op->basereg = R_BX; break; + } + if (rm == 6 && mod == 0) { /* special case */ + op->basereg = -1; + if (segsize != 16) + op->addr_size = 16; + mod = 2; /* fake disp16 */ + } + switch (mod) { + case 0: + op->segment |= SEG_NODISP; + break; + case 1: + op->segment |= SEG_DISP8; + op->offset = (signed char) *data++; + break; + case 2: + op->segment |= SEG_DISP16; + op->offset = *data++; + op->offset |= ((unsigned) *data++) << 8; + break; + } + return data; + } else { + /* + * Once again, specifies displacement size (this time + * none, byte or *dword*), while specifies the base + * register. Again, [EBP] is missing, replaced by a pure + * disp32 (this time that's mod=0,rm=*5*). However, rm=4 + * indicates not a single base register, but instead the + * presence of a SIB byte... + */ + op->indexreg = -1; + switch (rm) { + case 0: op->basereg = R_EAX; break; + case 1: op->basereg = R_ECX; break; + case 2: op->basereg = R_EDX; break; + case 3: op->basereg = R_EBX; break; + case 5: op->basereg = R_EBP; break; + case 6: op->basereg = R_ESI; break; + case 7: op->basereg = R_EDI; break; + } + if (rm == 5 && mod == 0) { + op->basereg = -1; + if (segsize != 32) + op->addr_size = 32; + mod = 2; /* fake disp32 */ + } + if (rm == 4) { /* process SIB */ + scale = (*data >> 6) & 03; + index = (*data >> 3) & 07; + base = *data & 07; + data++; + + op->scale = 1 << scale; + switch (index) { + case 0: op->indexreg = R_EAX; break; + case 1: op->indexreg = R_ECX; break; + case 2: op->indexreg = R_EDX; break; + case 3: op->indexreg = R_EBX; break; + case 4: op->indexreg = -1; break; + case 5: op->indexreg = R_EBP; break; + case 6: op->indexreg = R_ESI; break; + case 7: op->indexreg = R_EDI; break; + } + + switch (base) { + case 0: op->basereg = R_EAX; break; + case 1: op->basereg = R_ECX; break; + case 2: op->basereg = R_EDX; break; + case 3: op->basereg = R_EBX; break; + case 4: op->basereg = R_ESP; break; + case 6: op->basereg = R_ESI; break; + case 7: op->basereg = R_EDI; break; + case 5: + if (mod == 0) { + mod = 2; + op->basereg = -1; + } else + op->basereg = R_EBP; + break; + } + } + switch (mod) { + case 0: + op->segment |= SEG_NODISP; + break; + case 1: + op->segment |= SEG_DISP8; + op->offset = (signed char) *data++; + break; + case 2: + op->segment |= SEG_DISP32; + op->offset = *data++; + op->offset |= ((unsigned) *data++) << 8; + op->offset |= ((long) *data++) << 16; + op->offset |= ((long) *data++) << 24; + break; + } + return data; + } +} + +/* + * Determine whether the instruction template in t corresponds to the data + * stream in data. Return the number of bytes matched if so. + */ +static int matches (struct itemplate *t, unsigned char *data, int asize, + int osize, int segsize, int rep, insn *ins) +{ + unsigned char * r = (unsigned char *)(t->code); + unsigned char * origdata = data; + int a_used = FALSE, o_used = FALSE; + int drep = 0; + + if ( rep == 0xF2 ) + drep = P_REPNE; + else if ( rep == 0xF3 ) + drep = P_REP; + + while (*r) + { + int c = *r++; + if (c >= 01 && c <= 03) { + while (c--) + if (*r++ != *data++) + return FALSE; + } + if (c == 04) { + switch (*data++) { + case 0x07: ins->oprs[0].basereg = 0; break; + case 0x17: ins->oprs[0].basereg = 2; break; + case 0x1F: ins->oprs[0].basereg = 3; break; + default: return FALSE; + } + } + if (c == 05) { + switch (*data++) { + case 0xA1: ins->oprs[0].basereg = 4; break; + case 0xA9: ins->oprs[0].basereg = 5; break; + default: return FALSE; + } + } + if (c == 06) { + switch (*data++) { + case 0x06: ins->oprs[0].basereg = 0; break; + case 0x0E: ins->oprs[0].basereg = 1; break; + case 0x16: ins->oprs[0].basereg = 2; break; + case 0x1E: ins->oprs[0].basereg = 3; break; + default: return FALSE; + } + } + if (c == 07) { + switch (*data++) { + case 0xA0: ins->oprs[0].basereg = 4; break; + case 0xA8: ins->oprs[0].basereg = 5; break; + default: return FALSE; + } + } + if (c >= 010 && c <= 012) { + int t = *r++, d = *data++; + if (d < t || d > t+7) + return FALSE; + else { + ins->oprs[c-010].basereg = d-t; + ins->oprs[c-010].segment |= SEG_RMREG; + } + } + if (c == 017) + if (*data++) + return FALSE; + if (c >= 014 && c <= 016) { + ins->oprs[c-014].offset = (signed char) *data++; + ins->oprs[c-014].segment |= SEG_SIGNED; + } + if (c >= 020 && c <= 022) + ins->oprs[c-020].offset = *data++; + if (c >= 024 && c <= 026) + ins->oprs[c-024].offset = *data++; + if (c >= 030 && c <= 032) { + ins->oprs[c-030].offset = *data++; + ins->oprs[c-030].offset |= (((unsigned) *data++) << 8); + } + if (c >= 034 && c <= 036) { + ins->oprs[c-034].offset = *data++; + ins->oprs[c-034].offset |= (((unsigned) *data++) << 8); + if (osize == 32) { + ins->oprs[c-034].offset |= (((long) *data++) << 16); + ins->oprs[c-034].offset |= (((long) *data++) << 24); + } + if (segsize != asize) + ins->oprs[c-034].addr_size = asize; + } + if (c >= 040 && c <= 042) { + ins->oprs[c-040].offset = *data++; + ins->oprs[c-040].offset |= (((unsigned) *data++) << 8); + ins->oprs[c-040].offset |= (((long) *data++) << 16); + ins->oprs[c-040].offset |= (((long) *data++) << 24); + } + if (c >= 044 && c <= 046) { + ins->oprs[c-044].offset = *data++; + ins->oprs[c-044].offset |= (((unsigned) *data++) << 8); + if (asize == 32) { + ins->oprs[c-044].offset |= (((long) *data++) << 16); + ins->oprs[c-044].offset |= (((long) *data++) << 24); + } + if (segsize != asize) + ins->oprs[c-044].addr_size = asize; + } + if (c >= 050 && c <= 052) { + ins->oprs[c-050].offset = (signed char) *data++; + ins->oprs[c-050].segment |= SEG_RELATIVE; + } + if (c >= 060 && c <= 062) { + ins->oprs[c-060].offset = *data++; + ins->oprs[c-060].offset |= (((unsigned) *data++) << 8); + ins->oprs[c-060].segment |= SEG_RELATIVE; + ins->oprs[c-060].segment &= ~SEG_32BIT; + } + if (c >= 064 && c <= 066) { + ins->oprs[c-064].offset = *data++; + ins->oprs[c-064].offset |= (((unsigned) *data++) << 8); + if (osize == 32) { + ins->oprs[c-064].offset |= (((long) *data++) << 16); + ins->oprs[c-064].offset |= (((long) *data++) << 24); + ins->oprs[c-064].segment |= SEG_32BIT; + } else + ins->oprs[c-064].segment &= ~SEG_32BIT; + ins->oprs[c-064].segment |= SEG_RELATIVE; + if (segsize != osize) { + ins->oprs[c-064].type = + (ins->oprs[c-064].type & NON_SIZE) + | ((osize == 16) ? BITS16 : BITS32); + } + } + if (c >= 070 && c <= 072) { + ins->oprs[c-070].offset = *data++; + ins->oprs[c-070].offset |= (((unsigned) *data++) << 8); + ins->oprs[c-070].offset |= (((long) *data++) << 16); + ins->oprs[c-070].offset |= (((long) *data++) << 24); + ins->oprs[c-070].segment |= SEG_32BIT | SEG_RELATIVE; + } + if (c >= 0100 && c < 0130) { + int modrm = *data++; + ins->oprs[c & 07].basereg = (modrm >> 3) & 07; + ins->oprs[c & 07].segment |= SEG_RMREG; + data = do_ea (data, modrm, asize, segsize, + &ins->oprs[(c >> 3) & 07]); + } + if (c >= 0130 && c <= 0132) { + ins->oprs[c-0130].offset = *data++; + ins->oprs[c-0130].offset |= (((unsigned) *data++) << 8); + } + if (c >= 0140 && c <= 0142) { + ins->oprs[c-0140].offset = *data++; + ins->oprs[c-0140].offset |= (((unsigned) *data++) << 8); + ins->oprs[c-0140].offset |= (((long) *data++) << 16); + ins->oprs[c-0140].offset |= (((long) *data++) << 24); + } + if (c >= 0200 && c <= 0277) { + int modrm = *data++; + if (((modrm >> 3) & 07) != (c & 07)) + return FALSE; /* spare field doesn't match up */ + data = do_ea (data, modrm, asize, segsize, + &ins->oprs[(c >> 3) & 07]); + } + if (c >= 0300 && c <= 0302) { + if (asize) + ins->oprs[c-0300].segment |= SEG_32BIT; + else + ins->oprs[c-0300].segment &= ~SEG_32BIT; + a_used = TRUE; + } + if (c == 0310) { + if (asize == 32) + return FALSE; + else + a_used = TRUE; + } + if (c == 0311) { + if (asize == 16) + return FALSE; + else + a_used = TRUE; + } + if (c == 0312) { + if (asize != segsize) + return FALSE; + else + a_used = TRUE; + } + if (c == 0320) { + if (osize == 32) + return FALSE; + else + o_used = TRUE; + } + if (c == 0321) { + if (osize == 16) + return FALSE; + else + o_used = TRUE; + } + if (c == 0322) { + if (osize != segsize) + return FALSE; + else + o_used = TRUE; + } + if (c == 0330) { + int t = *r++, d = *data++; + if (d < t || d > t+15) + return FALSE; + else + ins->condition = d - t; + } + if (c == 0331) { + if ( rep ) + return FALSE; + } + if (c == 0332) { + if (drep == P_REP) + drep = P_REPE; + } + if (c == 0333) { + if ( rep != 0xF3 ) + return FALSE; + drep = 0; + } + } + + /* + * Check for unused rep or a/o prefixes. + */ + ins->nprefix = 0; + if (drep) + ins->prefixes[ins->nprefix++] = drep; + if (!a_used && asize != segsize) + ins->prefixes[ins->nprefix++] = (asize == 16 ? P_A16 : P_A32); + if (!o_used && osize != segsize) + ins->prefixes[ins->nprefix++] = (osize == 16 ? P_O16 : P_O32); + + return data - origdata; +} + +long disasm (unsigned char *data, char *output, int segsize, long offset) +{ + struct itemplate **p, **best_p; + int length, best_length = 0; + char *segover; + int rep, lock, asize, osize, i, slen, colon; + unsigned char *origdata; + int works; + insn tmp_ins, ins; + unsigned long goodness, best; + + /* + * Scan for prefixes. + */ + asize = osize = segsize; + segover = NULL; + ins.condition = ins.nprefix = rep = lock = 0; + origdata = data; + for (;;) { + if (*data == 0xF3 || *data == 0xF2) + rep = *data++; + else if (*data == 0xF0) + lock = *data++; + else if (*data == 0x2E || *data == 0x36 || *data == 0x3E || + *data == 0x26 || *data == 0x64 || *data == 0x65) { + switch (*data++) { + case 0x2E: segover = "cs"; break; + case 0x36: segover = "ss"; break; + case 0x3E: segover = "ds"; break; + case 0x26: segover = "es"; break; + case 0x64: segover = "fs"; break; + case 0x65: segover = "gs"; break; + } + } else if (*data == 0x66) + osize = 48 - segsize, data++; + else if (*data == 0x67) + asize = 48 - segsize, data++; + else + break; + } + + tmp_ins.oprs[0].segment = tmp_ins.oprs[1].segment = + tmp_ins.oprs[2].segment = + tmp_ins.oprs[0].addr_size = tmp_ins.oprs[1].addr_size = + tmp_ins.oprs[2].addr_size = (segsize == 16 ? 0 : SEG_32BIT); + tmp_ins.condition = -1; + best = ~0UL; /* Worst possible */ + best_p = NULL; + for (p = itable[*data]; *p; p++) { + if ( (length = matches(*p, data, asize, osize, + segsize, rep, &tmp_ins)) ) { + works = TRUE; + /* + * Final check to make sure the types of r/m match up. + */ + for (i = 0; i < (*p)->operands; i++) { + if ( + /* If it's a mem-only EA but we have a register, die. */ + ((tmp_ins.oprs[i].segment & SEG_RMREG) && + !(MEMORY & ~(*p)->opd[i])) || + + /* If it's a reg-only EA but we have a memory ref, die. */ + (!(tmp_ins.oprs[i].segment & SEG_RMREG) && + !(REGNORM & ~(*p)->opd[i]) && + !((*p)->opd[i] & REG_SMASK)) || + + /* Register type mismatch (eg FS vs REG_DESS): die. */ + ((((*p)->opd[i] & (REGISTER | FPUREG)) || + (tmp_ins.oprs[i].segment & SEG_RMREG)) && + !whichreg ((*p)->opd[i], tmp_ins.oprs[i].basereg))) { + works = FALSE; + break; + } + } + + if (works) { + goodness = (*p)->flags & IF_PFMASK; + if ( goodness < best ) { + /* This is the best one found so far */ + best = goodness; + best_p = p; + best_length = length; + ins = tmp_ins; + } + } + } + } + + if (!best_p) + return 0; /* no instruction was matched */ + + /* Pick the best match */ + p = best_p; + length = best_length; + + slen = 0; + + if (lock) + slen += sprintf(output+slen, "lock "); + for (i = 0; i < ins.nprefix; i++) + switch (ins.prefixes[i]) { + case P_REP: slen += sprintf(output+slen, "rep "); break; + case P_REPE: slen += sprintf(output+slen, "repe "); break; + case P_REPNE: slen += sprintf(output+slen, "repne "); break; + case P_A16: slen += sprintf(output+slen, "a16 "); break; + case P_A32: slen += sprintf(output+slen, "a32 "); break; + case P_O16: slen += sprintf(output+slen, "o16 "); break; + case P_O32: slen += sprintf(output+slen, "o32 "); break; + } + + for (i = 0; i < elements(ico); i++) + if ((*p)->opcode == ico[i]) { + slen += sprintf(output+slen, "%s%s", icn[i], + whichcond(ins.condition)); + break; + } + if (i >= elements(ico)) + slen += sprintf(output+slen, "%s", insn_names[(*p)->opcode]); + colon = FALSE; + length += data - origdata; /* fix up for prefixes */ + for (i=0; i<(*p)->operands; i++) { + output[slen++] = (colon ? ':' : i==0 ? ' ' : ','); + + if (ins.oprs[i].segment & SEG_RELATIVE) { + ins.oprs[i].offset += offset + length; + /* + * sort out wraparound + */ + if (!(ins.oprs[i].segment & SEG_32BIT)) + ins.oprs[i].offset &= 0xFFFF; + } + + if ((*p)->opd[i] & COLON) + colon = TRUE; + else + colon = FALSE; + + if (((*p)->opd[i] & (REGISTER | FPUREG)) || + (ins.oprs[i].segment & SEG_RMREG)) + { + ins.oprs[i].basereg = whichreg ((*p)->opd[i], + ins.oprs[i].basereg); + if ( (*p)->opd[i] & TO ) + slen += sprintf(output+slen, "to "); + slen += sprintf(output+slen, "%s", + reg_names[ins.oprs[i].basereg-EXPR_REG_START]); + } else if (!(UNITY & ~(*p)->opd[i])) { + output[slen++] = '1'; + } else if ( (*p)->opd[i] & IMMEDIATE ) { + if ( (*p)->opd[i] & BITS8 ) { + slen += sprintf(output+slen, "byte "); + if (ins.oprs[i].segment & SEG_SIGNED) { + if (ins.oprs[i].offset < 0) { + ins.oprs[i].offset *= -1; + output[slen++] = '-'; + } else + output[slen++] = '+'; + } + } else if ( (*p)->opd[i] & BITS16 ) { + slen += sprintf(output+slen, "word "); + } else if ( (*p)->opd[i] & BITS32 ) { + slen += sprintf(output+slen, "dword "); + } else if ( (*p)->opd[i] & NEAR ) { + slen += sprintf(output+slen, "near "); + } else if ( (*p)->opd[i] & SHORT ) { + slen += sprintf(output+slen, "short "); + } + slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); + } else if ( !(MEM_OFFS & ~(*p)->opd[i]) ) { + slen += sprintf(output+slen, "[%s%s%s0x%lx]", + (segover ? segover : ""), + (segover ? ":" : ""), + (ins.oprs[i].addr_size == 32 ? "dword " : + ins.oprs[i].addr_size == 16 ? "word " : ""), + ins.oprs[i].offset); + segover = NULL; + } else if ( !(REGMEM & ~(*p)->opd[i]) ) { + int started = FALSE; + if ( (*p)->opd[i] & BITS8 ) + slen += sprintf(output+slen, "byte "); + if ( (*p)->opd[i] & BITS16 ) + slen += sprintf(output+slen, "word "); + if ( (*p)->opd[i] & BITS32 ) + slen += sprintf(output+slen, "dword "); + if ( (*p)->opd[i] & BITS64 ) + slen += sprintf(output+slen, "qword "); + if ( (*p)->opd[i] & BITS80 ) + slen += sprintf(output+slen, "tword "); + if ( (*p)->opd[i] & FAR ) + slen += sprintf(output+slen, "far "); + if ( (*p)->opd[i] & NEAR ) + slen += sprintf(output+slen, "near "); + output[slen++] = '['; + if (ins.oprs[i].addr_size) + slen += sprintf(output+slen, "%s", + (ins.oprs[i].addr_size == 32 ? "dword " : + ins.oprs[i].addr_size == 16 ? "word " : "")); + if (segover) { + slen += sprintf(output+slen, "%s:", segover); + segover = NULL; + } + if (ins.oprs[i].basereg != -1) { + slen += sprintf(output+slen, "%s", + reg_names[(ins.oprs[i].basereg - + EXPR_REG_START)]); + started = TRUE; + } + if (ins.oprs[i].indexreg != -1) { + if (started) + output[slen++] = '+'; + slen += sprintf(output+slen, "%s", + reg_names[(ins.oprs[i].indexreg - + EXPR_REG_START)]); + if (ins.oprs[i].scale > 1) + slen += sprintf(output+slen, "*%d", ins.oprs[i].scale); + started = TRUE; + } + if (ins.oprs[i].segment & SEG_DISP8) { + int sign = '+'; + if (ins.oprs[i].offset & 0x80) { + ins.oprs[i].offset = - (signed char) ins.oprs[i].offset; + sign = '-'; + } + slen += sprintf(output+slen, "%c0x%lx", sign, + ins.oprs[i].offset); + } else if (ins.oprs[i].segment & SEG_DISP16) { + if (started) + output[slen++] = '+'; + slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); + } else if (ins.oprs[i].segment & SEG_DISP32) { + if (started) + output[slen++] = '+'; + slen += sprintf(output+slen, "0x%lx", ins.oprs[i].offset); + } + output[slen++] = ']'; + } else { + slen += sprintf(output+slen, "", i); + } + } + output[slen] = '\0'; + if (segover) { /* unused segment override */ + char *p = output; + int count = slen+1; + while (count--) + p[count+3] = p[count]; + strncpy (output, segover, 2); + output[2] = ' '; + } + return length; +} diff --git a/AltairZ80/flashwriter2.c b/AltairZ80/flashwriter2.c new file mode 100644 index 0000000..7ff8d54 --- /dev/null +++ b/AltairZ80/flashwriter2.c @@ -0,0 +1,334 @@ +/************************************************************************* + * * + * $Id: flashwriter2.c 1753 2008-01-02 16:36:47Z Hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http:/*www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Vector Graphic, Inc. FlashWriter II module for SIMH * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG*/ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#else +#include "sim_sock.h" +#endif + +#include "sim_tmxr.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + + +extern int32 sio0s(const int32 port, const int32 io, const int32 data); +extern int32 sio0d(const int32 port, const int32 io, const int32 data); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +static char ansibuf[10]; + +#define FW2_MAX_BOARDS 4 +#define UNIT_V_FW2_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_FW2_VERBOSE (1 << UNIT_V_FW2_VERBOSE) +#define FW2_CAPACITY (2048) /* FlashWriter II Memory Size */ + +typedef struct { + UNIT *uptr; /* UNIT pointer */ + uint8 cur_FL_Row; /* Current Flashwriter Row */ + uint8 cur_FL_Col; /* Current Flashwriter Column */ + uint8 FL_Row; + uint8 FL_Col; + uint8 reversevideo; /* Flag set if reverse video is currently on */ + uint8 M[FW2_CAPACITY]; /* FlashWriter 2K Video Memory */ +} FW2_INFO; + +static FW2_INFO *fw2_info[4]; +static uint8 port_map[4] = { 0x11, 0x15, 0x17, 0x19 }; + +static int32 fw2dev(const int32 Addr, const int32 rw, const int32 data); +static t_stat fw2_reset(DEVICE *dptr); +static t_stat fw2_attach(UNIT *uptr, char *cptr); +static t_stat fw2_detach(UNIT *uptr); +static uint8 FW2_Read(const uint32 Addr); +static uint8 FW2_Write(const uint32 Addr, uint8 cData); +static t_stat get_base_address(char *cptr, uint32 *baseaddr); + +static int32 FWIITrace = FALSE; + +static UNIT fw2_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, FW2_CAPACITY) } +}; + +static REG fw2_reg[] = { + { DRDATA (FWIITRACE, FWIITrace, 8), }, + { NULL } +}; + +static MTAB fw2_mod[] = { + /* quiet, no warning messages */ + { UNIT_FW2_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_FW2_VERBOSE, UNIT_FW2_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE fw2_dev = { + "FWII", fw2_unit, fw2_reg, fw2_mod, + FW2_MAX_BOARDS, 10, 31, 1, FW2_MAX_BOARDS, FW2_MAX_BOARDS, + NULL, NULL, &fw2_reset, + NULL, &fw2_attach, &fw2_detach, + NULL, 0, 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat fw2_reset(DEVICE *dptr) +{ + return SCPE_OK; +} + +/* Attach routine */ +static t_stat fw2_attach(UNIT *uptr, char *cptr) +{ + t_stat r; + unsigned int i = 0; + uint32 baseaddr; + char *tptr; + + r = get_base_address(cptr, &baseaddr); + if(r != SCPE_OK) /* error?*/ + return r; + + DBG_PRINT(("%s\n", __FUNCTION__)); + + for(i = 0; i < FW2_MAX_BOARDS; i++) { + if(&fw2_dev.units[i] == uptr) { + if(uptr->flags & UNIT_FW2_VERBOSE) { + printf("Attaching unit %d\n at %04x", i, baseaddr); + } + break; + } + } + + fw2_info[i] = calloc(1, sizeof(FW2_INFO)); + fw2_info[i]->uptr = uptr; + fw2_info[i]->uptr->u3 = baseaddr; + + if(sim_map_resource(baseaddr, FW2_CAPACITY, RESOURCE_TYPE_MEMORY, &fw2dev, FALSE) != 0) { + printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, baseaddr); + return SCPE_ARG; + } + + if(sim_map_resource(0x00, 1, RESOURCE_TYPE_IO, &sio0s, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, 0x00); + return SCPE_ARG; + } + + if(sim_map_resource(0x01, 1, RESOURCE_TYPE_IO, &sio0d, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, 0x01); + return SCPE_ARG; + } + + tptr = (char *) malloc (strlen (cptr) + 3); /* get string buf */ + if (tptr == NULL) return SCPE_MEM; /* no more mem? */ + sprintf(tptr, "0x%04x", baseaddr); /* copy base address */ + uptr->filename = tptr; /* save */ + uptr->flags = uptr->flags | UNIT_ATT; + return SCPE_OK; +} + + +/* Detach routine */ +static t_stat fw2_detach(UNIT *uptr) +{ + uint8 i; + + DBG_PRINT(("%s\n", __FUNCTION__)); + + for(i = 0; i < FW2_MAX_BOARDS; i++) { + if(&fw2_dev.units[i] == uptr) { + break; + } + } + + if (i >= FW2_MAX_BOARDS) return SCPE_ARG; + + /* Disconnect FlashWriter2: unmap memory and I/O resources */ + sim_map_resource(fw2_info[i]->uptr->u3, FW2_CAPACITY, RESOURCE_TYPE_MEMORY, &fw2dev, TRUE); + sim_map_resource(0x00, 1, RESOURCE_TYPE_IO, &sio0s, TRUE); + sim_map_resource(0x01, 1, RESOURCE_TYPE_IO, &sio0d, TRUE); + + if(fw2_info[i]) { + free(fw2_info[1]); + } + + free (uptr->filename); /* free base address string */ + uptr->filename = NULL; + uptr->flags = uptr->flags & ~UNIT_ATT; /* not attached */ + return SCPE_OK; +} + +static t_stat get_base_address(char *cptr, uint32 *baseaddr) { + uint32 b; + sscanf(cptr, "%x", &b); + if(b & (FW2_CAPACITY-1)) { + printf("FWII must be on a %d-byte boundary.\n", FW2_CAPACITY); + return SCPE_ARG; + } + *baseaddr = b & ~(FW2_CAPACITY-1); + return SCPE_OK; +} + +extern int32 getBankSelect(void); + +/* This is the main entry point into the Flashwriter2 emulation. */ +static int32 fw2dev(const int32 Addr, const int32 rw, const int32 data) +{ + int32 bank = getBankSelect(); + if(bank == 0) { + if(rw == 0) { /* Read */ + return(FW2_Read(Addr)); + } else { /* Write */ + return(FW2_Write(Addr, data)); + } + } else return 0xff; +} + + +static uint8 FW2_Write(const uint32 Addr, uint8 Value) +{ + FW2_INFO *fw2 = NULL; + uint8 FL_Row; + uint8 FL_Col; + uint32 baseaddr = 0; + uint8 i; + uint8 outchar; + uint8 port; + + for(i = 0; i < FW2_MAX_BOARDS; i++) { + if(fw2_info[i] != NULL) { + baseaddr = fw2_info[i]->uptr->u3; + if((Addr >= baseaddr) && (Addr < (baseaddr + FW2_CAPACITY))) { + break; + } + } + } + + if(i == FW2_MAX_BOARDS) { + return 0; + } + + fw2 = fw2_info[i]; + port = port_map[i]; + + fw2->M[Addr - baseaddr] = Value; + + /* Only print if it is in the visible part of the Flashwriter memory */ + if((Addr >= baseaddr) && (Addr < (baseaddr + (80 * 24)))) { + FL_Col = ((Addr-baseaddr) % 80) + 1; + FL_Row = ((Addr-baseaddr) / 80) + 1; + + if(Value & 0x80) { /* reverse video */ + if(fw2->reversevideo == 0) { + fw2->reversevideo = 1; + sprintf(ansibuf, "\x1b[07m"); + for(i=0;ireversevideo == 1) { + fw2->reversevideo = 0; + sprintf(ansibuf, "\x1b[00m"); + for(i=0;icur_FL_Row == FL_Row) && (FL_Col == fw2->cur_FL_Col + 1)) { + sio0d(port, 1, outchar); + } else { + /* ESC[#;#H */ + sprintf(ansibuf, "\x1b[%d;%dH%c", FL_Row, FL_Col, outchar); + for(i=0;icur_FL_Col = FL_Col; + fw2->cur_FL_Row = FL_Row; + } + + return(1); +} + + +static uint8 FW2_Read(const uint32 Addr) +{ + uint32 baseaddr = 0; + uint8 i; + + for(i = 0; i < FW2_MAX_BOARDS; i++) { + if(fw2_info[i] != NULL) { + baseaddr = fw2_info[i]->uptr->u3; + if((Addr >= baseaddr) && (Addr < (baseaddr + FW2_CAPACITY))) { + break; + } + } + } + + if(i == FW2_MAX_BOARDS) { + return 0xFF; + } + + return(fw2_info[i]->M[Addr - baseaddr]); +} diff --git a/AltairZ80/i8272.c b/AltairZ80/i8272.c new file mode 100644 index 0000000..da2a4e3 --- /dev/null +++ b/AltairZ80/i8272.c @@ -0,0 +1,906 @@ +/************************************************************************* + * * + * $Id: i8272.c 1773 2008-01-11 05:46:19Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Generic Intel 8272 Disk Controller module for SIMH. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/* Change log: + - 19-Apr-2008, Tony Nicholson, added other .IMD formats +*/ + +/*#define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#include "sim_imd.h" +#include "i8272.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 +#define VERBOSE_MSG 0x80 + +#define I8272_MAX_DRIVES 4 +#define I8272_SECTOR_LEN 8192 + +#define CMD_PHASE 0 +#define EXEC_PHASE 1 +#define DATA_PHASE 2 + +typedef union { + uint8 raw[I8272_SECTOR_LEN]; +} SECTOR_FORMAT; + +typedef struct { + UNIT *uptr; + DISK_INFO *imd; + uint8 ntracks; /* number of tracks */ + uint8 nheads; /* number of heads */ + uint32 sectsize; /* sector size, not including pre/postamble */ + uint8 track; /* Current Track */ + uint8 ready; /* Is drive ready? */ +} I8272_DRIVE_INFO; + +typedef struct { + PNP_INFO pnp; /* Plug-n-Play Information */ + uint32 fdc_dma_addr;/* DMA Transfer Address */ + uint8 fdc_msr; /* 8272 Main Status Register */ + uint8 fdc_phase; /* Phase that the 8272 is currently in */ + uint8 fdc_srt; /* Step Rate in ms */ + uint8 fdc_hut; /* Head Unload Time in ms */ + uint8 fdc_hlt; /* Head Load Time in ms */ + uint8 fdc_nd; /* Non-DMA Mode 1=Non-DMA, 0=DMA */ + uint8 fdc_head; /* H Head Number */ + uint8 fdc_sector; /* R Record (Sector) */ + uint8 fdc_sec_len; /* N Sector Length */ + uint8 fdc_eot; /* EOT End of Track (Final sector number of cyl) */ + uint8 fdc_gpl; /* GPL Gap3 Length */ + uint8 fdc_dtl; /* DTL Data Length */ + uint8 fdc_mt; /* Multiple sectors */ + uint8 fdc_mfm; /* MFM mode */ + uint8 fdc_sk; /* Skip Deleted Data */ + uint8 fdc_hds; /* Head Select */ + uint8 fdc_fillbyte; /* Fill-byte used for FORMAT TRACK */ + uint8 fdc_sc; /* Sector count for FORMAT TRACK */ + uint8 fdc_status[3];/* Status Register Bytes */ + uint8 fdc_seek_end; /* Seek was executed successfully */ + uint8 cmd_index; /* Index of command byte */ + uint8 cmd[10]; /* Storage for current command */ + uint8 cmd_len; /* FDC Command Length */ + uint8 result_index; /* Index of result byte */ + uint8 result[10]; /* Result data */ + uint8 result_len; /* FDC Result Length */ + uint8 sel_drive; /* Currently selected drive */ + I8272_DRIVE_INFO drive[I8272_MAX_DRIVES]; +} I8272_INFO; + +static SECTOR_FORMAT sdata; +extern uint32 PCX; +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +/* These are needed for DMA. PIO Mode has not been implemented yet. */ +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); +extern uint8 GetBYTEWrapper(const uint32 Addr); + +#define UNIT_V_I8272_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_I8272_WLK (1 << UNIT_V_I8272_WLK) +#define UNIT_V_I8272_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_I8272_VERBOSE (1 << UNIT_V_I8272_VERBOSE) +#define I8272_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ +#define I8272_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */ +#define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ +#define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ +#define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ + +/* Intel 8272 Commands */ +#define I8272_READ_TRACK 0x02 +#define I8272_SPECIFY 0x03 +#define I8272_SENSE_DRIVE_STATUS 0x04 +#define I8272_WRITE_DATA 0x05 +#define I8272_READ_DATA 0x06 +#define I8272_RECALIBRATE 0x07 +#define I8272_SENSE_INTR_STATUS 0x08 +#define I8272_WRITE_DELETED_DATA 0x09 +#define I8272_READ_ID 0x0A +#define I8272_READ_DELETED_DATA 0x0C +#define I8272_FORMAT_TRACK 0x0D +#define I8272_SEEK 0x0F +#define I8272_SCAN_EQUAL 0x11 +#define I8272_SCAN_LOW_EQUAL 0x19 +#define I8272_SCAN_HIGH_EQUAL 0x1D + +/* SENSE DRIVE STATUS bit definitions */ +#define DRIVE_STATUS_TWO_SIDED 0x08 +#define DRIVE_STATUS_TRACK0 0x10 +#define DRIVE_STATUS_READY 0x20 +#define DRIVE_STATUS_WP 0x40 +#define DRIVE_STATUS_FAULT 0x80 + +static int32 trace_level = 0; /* Disable all tracing by default. */ +static int32 bootstrap = 0; + +static int32 i8272dev(const int32 port, const int32 io, const int32 data); +static t_stat i8272_reset(DEVICE *dptr); +int32 find_unit_index (UNIT *uptr); + +I8272_INFO i8272_info_data = { { 0x0, 0, 0xC0, 2 } }; +I8272_INFO *i8272_info = &i8272_info_data; + +static UNIT i8272_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, I8272_CAPACITY) } +}; + +static REG i8272_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { DRDATA (BOOTSTRAP, bootstrap, 10), }, + { NULL } +}; + +static MTAB i8272_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_I8272_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_I8272_WLK, UNIT_I8272_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_I8272_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_I8272_VERBOSE, UNIT_I8272_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE i8272_dev = { + "I8272", i8272_unit, i8272_reg, i8272_mod, + I8272_MAX_DRIVES, 10, 31, 1, I8272_MAX_DRIVES, I8272_MAX_DRIVES, + NULL, NULL, &i8272_reset, + NULL, &i8272_attach, &i8272_detach, + &i8272_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +static uint8 I8272_Setup_Cmd(uint8 fdc_cmd); + + +/* Reset routine */ +static t_stat i8272_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &i8272dev, TRUE); + } else { + /* Connect I/O Ports at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &i8272dev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + return SCPE_OK; +} + + +/* find_unit_index find index of a unit + + Inputs: + uptr = pointer to unit + Outputs: + result = index of device +*/ +int32 find_unit_index (UNIT *uptr) +{ + DEVICE *dptr; + uint32 i; + + if (uptr == NULL) return (-1); + dptr = find_dev_from_unit(uptr); + for(i=0; inumunits; i++) { + if(dptr->units + i == uptr) { + break; + } + } + if(i == dptr->numunits) { + return (-1); + } + return (i); +} + +/* Attach routine */ +t_stat i8272_attach(UNIT *uptr, char *cptr) +{ + char header[4]; + t_stat r; + int32 i = 0; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + + i = find_unit_index(uptr); + + if (i == -1) { + return (SCPE_IERR); + } + + DBG_PRINT(("Attach I8272%d\n", i)); + i8272_info->drive[i].uptr = uptr; + + /* Default to drive not ready */ + i8272_info->drive[i].ready = 0; + + if(uptr->capac > 0) { + fgets(header, 4, uptr->fileref); + if(!strcmp(header, "IMD")) { + uptr->u3 = IMAGE_TYPE_IMD; + } else if(!strcmp(header, "CPT")) { + printf("CPT images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_CPT; + i8272_detach(uptr); + return SCPE_OPENERR; + } else { + printf("DSK images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_DSK; + i8272_detach(uptr); + return SCPE_OPENERR; + } + } else { + /* creating file, must be DSK format. */ + printf("Cannot create images, must start with a I8272 IMD image.\n"); + uptr->u3 = IMAGE_TYPE_DSK; + i8272_detach(uptr); + return SCPE_OPENERR; + } + + if (uptr->flags & UNIT_I8272_VERBOSE) + printf("I8272%d: attached to '%s', type=%s, len=%d\n", i, cptr, + uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + + if(uptr->u3 == IMAGE_TYPE_IMD) { + if(uptr->capac < I8272_CAPACITY_SSSD) { /*was 318000 but changed to allow 8inch SSSD disks*/ + printf("IMD file too small for use with SIMH.\nCopy an existing file and format it with CP/M.\n"); + i8272_detach(uptr); + return SCPE_OPENERR; + } + + if (uptr->flags & UNIT_I8272_VERBOSE) + printf("--------------------------------------------------------\n"); + i8272_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_I8272_VERBOSE)); + i8272_info->drive[i].ready = 1; + if (uptr->flags & UNIT_I8272_VERBOSE) + printf("\n"); + } else { + i8272_info->drive[i].imd = NULL; + } + + return SCPE_OK; +} + + +/* Detach routine */ +t_stat i8272_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + i = find_unit_index(uptr); + + if (i == -1) { + return (SCPE_IERR); + } + + DBG_PRINT(("Detach I8272%d\n", i)); + diskClose(i8272_info->drive[i].imd); + i8272_info->drive[i].ready = 0; + + r = detach_unit(uptr); /* detach unit */ + if ( r != SCPE_OK) + return r; + + return SCPE_OK; +} + + +static int32 i8272dev(const int32 port, const int32 io, const int32 data) +{ + DBG_PRINT(("I8272: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x" NLP, + PCX, io ? "OUT" : " IN", port, data)); + if(io) { + I8272_Write(port, data); + return 0; + } else { + return(I8272_Read(port)); + } +} + +uint8 I8272_Set_DMA(const uint32 dma_addr) +{ + i8272_info->fdc_dma_addr = dma_addr & 0xFFFFFF; + + return 0; +} + +static uint8 floorlog2(unsigned int n) +{ + /* Compute log2(n) */ + uint8 r = 0; + if(n >= 1<<16) { n >>=16; r += 16; } + if(n >= 1<< 8) { n >>= 8; r += 8; } + if(n >= 1<< 4) { n >>= 4; r += 4; } + if(n >= 1<< 2) { n >>= 2; r += 2; } + if(n >= 1<< 1) { r += 1; } + return ((n == 0) ? (0xFF) : r); /* 0xFF is error return value */ +} + +uint8 I8272_Read(const uint32 Addr) +{ + uint8 cData; + I8272_DRIVE_INFO *pDrive; + + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + + if(pDrive->uptr == NULL) { + return 0xFF; + } + + cData = 0x00; + + switch(Addr & 0x3) { + case I8272_FDC_MSR: + cData = i8272_info->fdc_msr | 0x80; + if(i8272_info->fdc_phase == 0) { + cData &= ~0x40; + } else { + cData |= 0x40; + } + + TRACE_PRINT(STATUS_MSG, ("I8272: " ADDRESS_FORMAT " RD FDC MSR = 0x%02x" NLP, PCX, cData)); + break; + case I8272_FDC_DATA: + if(i8272_info->fdc_phase == DATA_PHASE) { + cData = i8272_info->result[i8272_info->result_index]; + i8272_info->result_index ++; + if(i8272_info->result_index == i8272_info->result_len) { + TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " result phase complete." NLP, PCX)); + i8272_info->fdc_phase = 0; + } + } + + TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " RD Data, phase=%d, [%d]=0x%02x" NLP, PCX, i8272_info->fdc_phase, i8272_info->result_index-1, cData)); + + break; + default: + TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " Cannot read register %x" NLP, PCX, Addr)); + cData = 0xFF; + } + + return (cData); +} + +static char *messages[0x20] = { +/* 0 1 2 3 */ + "Undefined Command 0x0","Undefined Command 0x1","Read Track", "Specify", +/* 4 5 6 7 */ + "Sense Drive Status", "Write Data", "Read Data", "Recalibrate", +/* 8 9 A B */ + "Sense Interrupt Status", "Write Deleted Data", "Read ID", "Undefined Command 0xB", +/* C D E F */ + "Read Deleted Data", "Format Track", "Undefined Command 0xE","Seek", +/* 10 11 12 13 */ + "Undefined Command 0x10","Scan Equal", "Undefined Command 0x12","Undefined Command 0x13", +/* 14 15 16 17 */ + "Undefined Command 0x14","Undefined Command 0x15","Undefined Command 0x16","Undefined Command 0x17", +/* 18 19 1A 1B */ + "Undefined Command 0x18","Scan Low Equal", "Undefined Command 0x1A","Undefined Command 0x1B", +/* 1C 1D 1E 1F */ + "Undefined Command 0x1C","Scan High Equal", "Undefined Command 0x1E","Undefined Command 0x1F" +}; + +uint8 I8272_Write(const uint32 Addr, uint8 cData) +{ + I8272_DRIVE_INFO *pDrive; + unsigned int flags; + unsigned int readlen; + uint8 disk_read = 0; + int32 i; + + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + + if(pDrive->uptr == NULL) { + return 0xFF; + } + + switch(Addr & 0x3) { + case I8272_FDC_MSR: + TRACE_PRINT(WR_DATA_MSG, ("I8272: " ADDRESS_FORMAT " WR Drive Select Reg=%02x" NLP, + PCX, cData)); + break; + case I8272_FDC_DATA: + TRACE_PRINT(VERBOSE_MSG, ("I8272: " ADDRESS_FORMAT " WR Data, phase=%d, index=%d" NLP, + PCX, i8272_info->fdc_phase, i8272_info->cmd_index)); + if(i8272_info->fdc_phase == CMD_PHASE) { + i8272_info->cmd[i8272_info->cmd_index] = cData; + + if(i8272_info->cmd_index == 0) { + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " CMD=0x%02x[%s]" NLP, + PCX, cData & 0x1F, messages[cData & 0x1F])); + I8272_Setup_Cmd(cData & 0x1F); + } + i8272_info->cmd_index ++; + + if(i8272_info->cmd_len == i8272_info->cmd_index) { + i8272_info->cmd_index = 0; + i8272_info->fdc_phase = EXEC_PHASE; + } + } + + if(i8272_info->fdc_phase == EXEC_PHASE) { + switch(i8272_info->cmd[0] & 0x1F) { + case I8272_READ_DATA: + case I8272_WRITE_DATA: + case I8272_READ_DELETED_DATA: + case I8272_WRITE_DELETED_DATA: + case I8272_READ_TRACK: + case I8272_SCAN_LOW_EQUAL: + case I8272_SCAN_HIGH_EQUAL: + case I8272_SCAN_EQUAL: + i8272_info->fdc_mt = (i8272_info->cmd[0] & 0x80) >> 7; + i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; + i8272_info->fdc_sk = (i8272_info->cmd[0] & 0x20) >> 5; + i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; + i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + if(pDrive->uptr == NULL) { + return 0xFF; + } + + if(pDrive->track != i8272_info->cmd[2]) { + i8272_info->fdc_seek_end = 1; + } else { + i8272_info->fdc_seek_end = 0; + } + pDrive->track = i8272_info->cmd[2]; + i8272_info->fdc_head = i8272_info->cmd[3]; + i8272_info->fdc_sector = i8272_info->cmd[4]; + i8272_info->fdc_sec_len = i8272_info->cmd[5]; + i8272_info->fdc_eot = i8272_info->cmd[6]; + i8272_info->fdc_gpl = i8272_info->cmd[7]; + i8272_info->fdc_dtl = i8272_info->cmd[8]; + + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT + " CMD=0x%02x[%s]: Drive: %d, %s %s, C=%d. H=%d. S=%d, N=%d, EOT=%02x, GPL=%02x, DTL=%02x" NLP, + PCX, + i8272_info->cmd[0] & 0x1F, + messages[i8272_info->cmd[0] & 0x1F], + i8272_info->sel_drive, + i8272_info->fdc_mt ? "Multi" : "Single", + i8272_info->fdc_mfm ? "MFM" : "FM", + pDrive->track, + i8272_info->fdc_head, + i8272_info->fdc_sector, + i8272_info->fdc_sec_len, + i8272_info->fdc_eot, + i8272_info->fdc_gpl, + i8272_info->fdc_dtl)); + + i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2; + i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 3); + i8272_info->fdc_status[0] |= 0x40; + + i8272_info->fdc_status[1] = 0; + i8272_info->fdc_status[2] = 0; + + i8272_info->result[0] = i8272_info->fdc_status[0]; + i8272_info->result[1] = i8272_info->fdc_status[1]; + i8272_info->result[2] = i8272_info->fdc_status[2]; + i8272_info->result[3] = pDrive->track; + i8272_info->result[4] = i8272_info->fdc_head; + i8272_info->result[5] = i8272_info->fdc_sector; + i8272_info->result[6] = i8272_info->fdc_sec_len; + break; + case I8272_READ_ID: /* READ ID */ + i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; + i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; + i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + if(pDrive->uptr == NULL) { + return 0xFF; + } + /* Compute the i8272 "N" value from the sectorsize of this */ + /* disk's current track - i.e. N = log2(sectsize) - log2(128) */ + /* The calculation also works for non-standard format disk images with */ + /* sectorsizes of 2048, 4096 and 8192 bytes */ + i8272_info->fdc_sec_len = floorlog2( + pDrive->imd->track[pDrive->track][i8272_info->fdc_head].sectsize) - 7; + if(i8272_info->fdc_sec_len == 0xF8) { /*Error calculating N*/ + return 0xFF; + } + i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2; + i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 3); + + i8272_info->fdc_status[1] = 0; + i8272_info->fdc_status[2] = 0; + + i8272_info->result[0] = i8272_info->fdc_status[0]; + i8272_info->result[1] = i8272_info->fdc_status[1]; + i8272_info->result[2] = i8272_info->fdc_status[2]; + i8272_info->result[3] = pDrive->track; + i8272_info->result[4] = i8272_info->fdc_head; + i8272_info->result[5] = i8272_info->fdc_sector; + i8272_info->result[6] = i8272_info->fdc_sec_len; /*was hardcoded to 0x3*/ + break; + case I8272_RECALIBRATE: /* RECALIBRATE */ + i8272_info->sel_drive = i8272_info->cmd[1] & 3; + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + if(pDrive->uptr == NULL) { + return 0xFF; + } + + pDrive->track = 0; + i8272_info->fdc_phase = 0; /* No result phase */ + pDrive->track = 0; + i8272_info->fdc_seek_end = 1; + TRACE_PRINT(SEEK_MSG, ("I8272: " ADDRESS_FORMAT " Recalibrate: Drive 0x%02x" NLP, + PCX, i8272_info->sel_drive)); + break; + case I8272_FORMAT_TRACK: /* FORMAT A TRACK */ + i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; + i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; + i8272_info->fdc_head = i8272_info->fdc_hds; /* psco added */ + i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + if(pDrive->uptr == NULL) { + return 0xFF; + } + + if(pDrive->track != i8272_info->cmd[2]) { + i8272_info->fdc_seek_end = 1; + } else { + i8272_info->fdc_seek_end = 0; + } + i8272_info->fdc_sec_len = i8272_info->cmd[2]; + i8272_info->fdc_sc = i8272_info->cmd[3]; + i8272_info->fdc_gpl = i8272_info->cmd[4]; + i8272_info->fdc_fillbyte = i8272_info->cmd[5]; + + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Format Drive: %d, %s, C=%d. H=%d. N=%d, SC=%d, GPL=%02x, FILL=%02x" NLP, + PCX, + i8272_info->sel_drive, + i8272_info->fdc_mfm ? "MFM" : "FM", + pDrive->track, + i8272_info->fdc_head, + i8272_info->fdc_sec_len, + i8272_info->fdc_sc, + i8272_info->fdc_gpl, + i8272_info->fdc_fillbyte)); + + i8272_info->fdc_status[0] = (i8272_info->fdc_hds & 1) << 2; + i8272_info->fdc_status[0] |= (i8272_info->sel_drive & 3); + /*i8272_info->fdc_status[0] |= 0x40; psco removed */ + + i8272_info->fdc_status[1] = 0; + i8272_info->fdc_status[2] = 0; + + i8272_info->result[0] = i8272_info->fdc_status[0]; + i8272_info->result[1] = i8272_info->fdc_status[1]; + i8272_info->result[2] = i8272_info->fdc_status[2]; + i8272_info->result[3] = pDrive->track; + i8272_info->result[4] = i8272_info->fdc_head; + i8272_info->result[5] = i8272_info->fdc_sector; + i8272_info->result[6] = i8272_info->fdc_sec_len; + break; + case I8272_SENSE_INTR_STATUS: /* SENSE INTERRUPT STATUS */ + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Sense Interrupt Status" NLP, PCX)); + i8272_info->result[0] = i8272_info->fdc_seek_end ? 0x20 : 0x00; /* SEEK_END */ + i8272_info->result[0] |= i8272_info->sel_drive; + i8272_info->result[1] = pDrive->track; + break; + case I8272_SPECIFY: /* SPECIFY */ + i8272_info->fdc_srt = 16 - ((i8272_info->cmd[1] & 0xF0) >> 4); + i8272_info->fdc_hut = (i8272_info->cmd[1] & 0x0F) * 16; + i8272_info->fdc_hlt = ((i8272_info->cmd[2] & 0xFE) >> 1) * 2; + i8272_info->fdc_nd = (i8272_info->cmd[2] & 0x01); + i8272_info->fdc_phase = 0; /* No result phase */ + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Specify: SRT=%d, HUT=%d, HLT=%d, ND=%s" NLP, + PCX, + i8272_info->fdc_srt, + i8272_info->fdc_hut, + i8272_info->fdc_hlt, + i8272_info->fdc_nd ? "NON-DMA" : "DMA")); + break; + case I8272_SENSE_DRIVE_STATUS: /* Setup Status3 Byte */ + i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; + i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + if(pDrive->uptr == NULL) { + return 0xFF; + } + + i8272_info->result[0] = (pDrive->ready) ? DRIVE_STATUS_READY : 0; /* Drive Ready */ + if(imdGetSides(pDrive->imd) == 2) { + i8272_info->result[0] |= DRIVE_STATUS_TWO_SIDED; /* Two-sided? */ + } + if(imdIsWriteLocked(pDrive->imd)) { + i8272_info->result[0] |= DRIVE_STATUS_WP; /* Write Protected? */ + } + i8272_info->result[0] |= (i8272_info->fdc_hds & 1) << 2; + i8272_info->result[0] |= (i8272_info->sel_drive & 3); + i8272_info->result[0] |= (pDrive->track == 0) ? DRIVE_STATUS_TRACK0 : 0x00; /* Track 0 */ + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Sense Drive Status = %02x" NLP, + PCX, i8272_info->result[0])); + break; + case I8272_SEEK: /* SEEK */ + i8272_info->fdc_mt = (i8272_info->cmd[0] & 0x80) >> 7; + i8272_info->fdc_mfm = (i8272_info->cmd[0] & 0x40) >> 6; + i8272_info->fdc_sk = (i8272_info->cmd[0] & 0x20) >> 5; + i8272_info->fdc_hds = (i8272_info->cmd[1] & 0x04) >> 2; + i8272_info->sel_drive = (i8272_info->cmd[1] & 0x03); + pDrive = &i8272_info->drive[i8272_info->sel_drive]; + if(pDrive->uptr == NULL) { + return 0xFF; + } + + pDrive->track = i8272_info->cmd[2]; + i8272_info->fdc_seek_end = 1; + TRACE_PRINT(SEEK_MSG, ("I8272: " ADDRESS_FORMAT " Seek %d" NLP, + PCX, pDrive->track)); + break; + default: /* INVALID */ + break; + } + + if(i8272_info->fdc_phase == EXEC_PHASE) { + switch(i8272_info->cmd[0] & 0x1F) { + case I8272_READ_TRACK: + printf("I8272: " ADDRESS_FORMAT " Read a track (untested.)" NLP, PCX); + i8272_info->fdc_sector = 1; /* Read entire track from sector 1...eot */ + case I8272_READ_DATA: + case I8272_READ_DELETED_DATA: + disk_read = 1; + case I8272_WRITE_DATA: + case I8272_WRITE_DELETED_DATA: + for(;i8272_info->fdc_sector<=i8272_info->fdc_eot;i8272_info->fdc_sector++) { + TRACE_PRINT(RD_DATA_MSG, ("I8272: " ADDRESS_FORMAT " %s Data, sector: %d sector len=%d" NLP, + PCX, disk_read ? "RD" : "WR", + i8272_info->fdc_sector, + 128 << i8272_info->fdc_sec_len)); + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } + if(disk_read) { /* Read sector */ + sectRead(pDrive->imd, + pDrive->track, + i8272_info->fdc_head, + i8272_info->fdc_sector, + sdata.raw, + 128 << i8272_info->fdc_sec_len, + &flags, + &readlen); + + for(i=0;i<(128 << i8272_info->fdc_sec_len);i++) { + PutBYTEWrapper(i8272_info->fdc_dma_addr, sdata.raw[i]); + i8272_info->fdc_dma_addr++; + } + TRACE_PRINT(RD_DATA_MSG, ("I8272: " ADDRESS_FORMAT " Data transferred to RAM at 0x%06x" NLP, + PCX, i8272_info->fdc_dma_addr)); + } else { /* Write */ + for(i=0;i<(128 << i8272_info->fdc_sec_len);i++) { + sdata.raw[i] = GetBYTEWrapper(i8272_info->fdc_dma_addr); + i8272_info->fdc_dma_addr++; + } + TRACE_PRINT(WR_DATA_MSG, ("I8272: " ADDRESS_FORMAT " Data transferred from RAM at 0x%06x" NLP, + PCX, i8272_info->fdc_dma_addr)); + sectWrite(pDrive->imd, + pDrive->track, + i8272_info->fdc_head, + i8272_info->fdc_sector, + sdata.raw, + 128 << i8272_info->fdc_sec_len, + &flags, + &readlen); + } + + i8272_info->result[5] = i8272_info->fdc_sector; + i8272_info->result[1] = 0x80; + break; + case IMAGE_TYPE_DSK: + printf("%s: DSK Format not supported" NLP, __FUNCTION__); + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" NLP, __FUNCTION__); + break; + } + } + break; + case I8272_FORMAT_TRACK: /* FORMAT A TRACK */ + for(i8272_info->fdc_sector = 1;i8272_info->fdc_sector<=i8272_info->fdc_sc;i8272_info->fdc_sector++) { + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Format Track %d, Sector=%d, len=%d" NLP, + PCX, + pDrive->track, + i8272_info->fdc_sector, + 128 << i8272_info->fdc_sec_len)); + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } + TRACE_PRINT(WR_DATA_MSG, ("%s: Write: imd=%p t=%i h=%i s=%i l=%i" NLP, + __FUNCTION__, pDrive->imd, pDrive->track, i8272_info->fdc_head, + i8272_info->fdc_sector, 128 << i8272_info->fdc_sec_len)); + memset(sdata.raw, i8272_info->fdc_fillbyte, 128 << i8272_info->fdc_sec_len); + sectWrite(pDrive->imd, + pDrive->track, + i8272_info->fdc_head, + i8272_info->fdc_sector, + sdata.raw, + 128 << i8272_info->fdc_sec_len, + &flags, + &readlen); + i8272_info->result[1] = 0x80; + i8272_info->result[5] = i8272_info->fdc_sector; + break; + case IMAGE_TYPE_DSK: + printf("%s: DSK Format not supported" NLP, __FUNCTION__); + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" NLP, __FUNCTION__); + break; + } + } + break; + + case I8272_SCAN_LOW_EQUAL: /* SCAN LOW OR EQUAL */ + case I8272_SCAN_HIGH_EQUAL: /* SCAN HIGH OR EQUAL */ + case I8272_SCAN_EQUAL: /* SCAN EQUAL */ + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT " Scan Data" NLP, + PCX)); + printf("I8272: " ADDRESS_FORMAT " Scan not implemented." NLP, PCX); + break; + case I8272_READ_ID: /* READ ID */ + TRACE_PRINT(CMD_MSG, ("I8272: " ADDRESS_FORMAT + " READ ID Drive %d result ST0=%02x ST1=%02x ST2=%02x C=%02x H=%02x R=%02x N=%02x" + NLP, PCX, i8272_info->sel_drive, i8272_info->result[0], + i8272_info->result[1],i8272_info->result[2],i8272_info->result[3], + i8272_info->result[4],i8272_info->result[5],i8272_info->result[6])); + break; + + default: + break; + } + } + + + if(i8272_info->result_len != 0) { + i8272_info->fdc_phase ++; + } else { + i8272_info->fdc_phase = 0; + } + + i8272_info->result_index = 0; + } + + + break; + } + + cData = 0x00; + + return (cData); +} + +static uint8 I8272_Setup_Cmd(uint8 fdc_cmd) +{ + uint8 result = 0; + + switch(fdc_cmd) { + case I8272_READ_DATA: + case I8272_WRITE_DATA: + case I8272_READ_DELETED_DATA: + case I8272_WRITE_DELETED_DATA: + case I8272_READ_TRACK: + case I8272_SCAN_LOW_EQUAL: + case I8272_SCAN_HIGH_EQUAL: + case I8272_SCAN_EQUAL: + i8272_info->cmd_len = 9; + i8272_info->result_len = 7; + break; + case I8272_READ_ID: /* READ ID */ + i8272_info->cmd_len = 2; + i8272_info->result_len = 7; + break; + case I8272_RECALIBRATE: /* RECALIBRATE */ + i8272_info->cmd_len = 2; + i8272_info->result_len = 0; + break; + case I8272_FORMAT_TRACK: /* FORMAT A TRACK */ + i8272_info->cmd_len = 6; + i8272_info->result_len = 7; + break; + case I8272_SENSE_INTR_STATUS: /* SENSE INTERRUPT STATUS */ + i8272_info->cmd_len = 1; + i8272_info->result_len = 2; + break; + case I8272_SPECIFY: /* SPECIFY */ + i8272_info->cmd_len = 3; + i8272_info->result_len = 0; + break; + case I8272_SENSE_DRIVE_STATUS: /* SENSE DRIVE STATUS */ + i8272_info->cmd_len = 2; + i8272_info->result_len = 1; + break; + case I8272_SEEK: /* SEEK */ + i8272_info->cmd_len = 3; + i8272_info->result_len = 0; + break; + default: /* INVALID */ + i8272_info->cmd_len = 1; + i8272_info->result_len = 1; + result = -1; + break; + } + return (result); +} + diff --git a/AltairZ80/i8272.h b/AltairZ80/i8272.h new file mode 100644 index 0000000..c3fce09 --- /dev/null +++ b/AltairZ80/i8272.h @@ -0,0 +1,49 @@ +/************************************************************************* + * * + * $Id: i8272.h 1759 2008-01-05 04:36:46Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Generic Intel 8272 Disk Controller module for SIMH. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +extern t_stat i8272_attach(UNIT *uptr, char *cptr); +extern t_stat i8272_detach(UNIT *uptr); +extern uint8 I8272_Set_DMA(const uint32 dma_addr); +extern uint8 I8272_Read(const uint32 Addr); +extern uint8 I8272_Write(const uint32 Addr, uint8 cData); + +#define I8272_FDC_MSR 0 /* R=FDC Main Status Register, W=Drive Select Register */ +#define I8272_FDC_DATA 1 /* R/W FDC Data Register */ diff --git a/AltairZ80/i86.h b/AltairZ80/i86.h new file mode 100644 index 0000000..06e4c8e --- /dev/null +++ b/AltairZ80/i86.h @@ -0,0 +1,296 @@ +/* + * Dos/PC Emulator + * Copyright (C) 1991 Jim Hudgens + * + * + * The file is part of GDE. + * + * GDE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * GDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDE; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* 8086 support structs and definitions */ +/* definition of the registers */ + +/* general EAX,EBX,ECX, EDX type registers. + Note that for portability, and speed, the issue of byte + swapping is not addressed in the registers. All registers + are stored in the default format available on the + host machine. The only critical issue is that the + registers should line up EXACTLY in the same manner as + they do in the 386. That is: + + EAX & 0xff === AL + EAX & 0xffff == AX + + etc. The result is that alot of the calculations can then be + done using the native instruction set fully. +*/ + +/* Endian Logic +Priority 1: If LOWFIRST is defined, use it. LOWFIRST must be 1 if the + lower part of a 16 bit quantity comes first in memory, otherwise + LOWFIRST must be 0 +Priority 2: If __BIG_ENDIAN__ is defined, use it to define LOWFIRST accordingly +Priority 3: OS 9 on Macintosh needs LOWFIRST 0 +Priority 4: Use LOWFIRST 1 as default +*/ + +#ifndef LOWFIRST +#ifdef __BIG_ENDIAN__ +#if __BIG_ENDIAN__ +#define LOWFIRST 0 +#else +#define LOWFIRST 1 +#endif +#elif defined (__MWERKS__) && defined (macintosh) +#define LOWFIRST 0 +#else +#define LOWFIRST 1 +#endif +#endif + +#if LOWFIRST +typedef struct { uint16 x_reg; } I16_reg_t; +typedef struct { uint8 l_reg, h_reg; } I8_reg_t; +#else +typedef struct { uint16 x_reg; } I16_reg_t; +typedef struct { uint8 h_reg, l_reg; } I8_reg_t; +#endif + +typedef union +{ + I16_reg_t I16_reg; + I8_reg_t I8_reg; +} i386_general_register; + +struct i386_general_regs +{ + i386_general_register A, B, C, D; +}; + +typedef struct i386_general_regs Gen_reg_t; + +struct i386_special_regs +{ + i386_general_register SP, BP, SI, DI, IP; + uint32 FLAGS; +}; + +/* + * segment registers here represent the 16 bit quantities + * CS, DS, ES, SS + * + * segment pointers --- used to speed up the expressions: + * q = m->R_CSP + m->R_IP; + * fetched = *q; + * m->R_IP += 1; + * compared to: + * fetched = GetBYTEExtended(((uint32)m->R_CS << 4) + (m->R_IP++)); + * Save at least one shift, more if doing two byte moves. + */ +struct i386_segment_regs +{ + uint16 CS, DS, SS, ES, FS, GS; +}; + +/* 8 bit registers */ +#define R_AH Gn_regs.A.I8_reg.h_reg +#define R_AL Gn_regs.A.I8_reg.l_reg +#define R_BH Gn_regs.B.I8_reg.h_reg +#define R_BL Gn_regs.B.I8_reg.l_reg +#define R_CH Gn_regs.C.I8_reg.h_reg +#define R_CL Gn_regs.C.I8_reg.l_reg +#define R_DH Gn_regs.D.I8_reg.h_reg +#define R_DL Gn_regs.D.I8_reg.l_reg + +/* 16 bit registers */ +#define R_AX Gn_regs.A.I16_reg.x_reg +#define R_BX Gn_regs.B.I16_reg.x_reg +#define R_CX Gn_regs.C.I16_reg.x_reg +#define R_DX Gn_regs.D.I16_reg.x_reg + +/* special registers */ +#define R_SP Sp_regs.SP.I16_reg.x_reg +#define R_BP Sp_regs.BP.I16_reg.x_reg +#define R_SI Sp_regs.SI.I16_reg.x_reg +#define R_DI Sp_regs.DI.I16_reg.x_reg +#define R_IP Sp_regs.IP.I16_reg.x_reg +#define R_FLG Sp_regs.FLAGS + +/* segment registers */ +#define R_CS Sg_regs.CS +#define R_DS Sg_regs.DS +#define R_SS Sg_regs.SS +#define R_ES Sg_regs.ES + +/* 8088 has top 4 bits of the flags set to 1 */ +/* Also, bit#1 is set. This is (not well) documented behavior. */ +/* see note in userman.tex about the subtleties of dealing with */ +/* code which attempts to detect the host processor. */ +/* This is def'd as F_ALWAYS_ON */ +#define F_ALWAYS_ON (0xf002) /* flag bits always on */ + +/* following bits masked in to a 16bit quantity */ +#define F_CF 0x1 /* CARRY flag */ +#define F_PF 0x4 /* PARITY flag */ +#define F_AF 0x10 /* AUX flag */ +#define F_ZF 0x40 /* ZERO flag */ +#define F_SF 0x80 /* SIGN flag */ +#define F_TF 0x100 /* TRAP flag */ +#define F_IF 0x200 /* INTERRUPT ENABLE flag */ +#define F_DF 0x400 /* DIR flag */ +#define F_OF 0x800 /* OVERFLOW flag */ + +/* + * DEFINE A MASK FOR ONLY THOSE FLAG BITS WE WILL EVER PASS BACK + * (via PUSHF) + */ +#define F_MSK (F_CF|F_PF|F_AF|F_ZF|F_SF|F_TF|F_IF|F_DF|F_OF) + +#define TOGGLE_FLAG(M,FLAG) (M)->R_FLG ^= FLAG +#define SET_FLAG(M,FLAG) (M)->R_FLG |= FLAG +#define CLEAR_FLAG(M, FLAG) (M)->R_FLG &= ~FLAG +#define ACCESS_FLAG(M,FLAG) ((M)->R_FLG & (FLAG)) + +#define CONDITIONAL_SET_FLAG(COND,M,FLAG) \ + if (COND) SET_FLAG(M,FLAG); else CLEAR_FLAG(M,FLAG) + +/* emulator machine state. */ +/* segment usage control */ +#define SYSMODE_SEG_DS_SS 0x01 +#define SYSMODE_SEGOVR_CS 0x02 +#define SYSMODE_SEGOVR_DS 0x04 +#define SYSMODE_SEGOVR_ES 0x08 +#define SYSMODE_SEGOVR_SS 0x10 + +#define SYSMODE_SEGMASK (SYSMODE_SEG_DS_SS | SYSMODE_SEGOVR_CS | \ + SYSMODE_SEGOVR_DS | SYSMODE_SEGOVR_ES | SYSMODE_SEGOVR_SS) + +#define SYSMODE_PREFIX_REPE 0x20 +#define SYSMODE_PREFIX_REPNE 0x40 + +#define INTR_SYNCH 0x1 +#define INTR_HALTED 0x4 +#define INTR_ILLEGAL_OPCODE 0x8 + +/* INSTRUCTION DECODING STUFF */ +#define FETCH_DECODE_MODRM(m,mod,rh,rl) fetch_decode_modrm(m,&mod,&rh,&rl) +#define DECODE_RM_BYTE_REGISTER(m,r) decode_rm_byte_register(m,r) +#define DECODE_RM_WORD_REGISTER(m,r) decode_rm_word_register(m,r) +#define DECODE_CLEAR_SEGOVR(m) m->sysmode &= ~(SYSMODE_SEGMASK) + +typedef struct pc_env PC_ENV; +struct pc_env +{ + /* The registers!! */ + struct i386_general_regs Gn_regs; + struct i386_special_regs Sp_regs; + struct i386_segment_regs Sg_regs; + /* our flags structrure. This contains information on + REPE prefix 2 bits repe,repne + SEGMENT overrides 5 bits normal,DS,SS,CS,ES + Delayed flag set 3 bits (zero, signed, parity) + reserved 6 bits + interrupt # 8 bits instruction raised interrupt + BIOS video segregs 4 bits + Interrupt Pending 1 bits + Extern interrupt 1 bits + Halted 1 bits + */ + long sysmode; + uint8 intno; +}; + +/* GLOBAL */ +volatile int intr; + +void halt_sys (PC_ENV *sys); +void fetch_decode_modrm (PC_ENV *m, uint16 *mod, uint16 *regh, uint16 *regl); +uint8 *decode_rm_byte_register (PC_ENV *m, int reg); +uint16 *decode_rm_word_register (PC_ENV *m, int reg); +uint16 *decode_rm_seg_register (PC_ENV *m, int reg); +uint8 fetch_byte_imm (PC_ENV *m); +uint16 fetch_word_imm (PC_ENV *m); +uint16 decode_rm00_address (PC_ENV *m, int rm); +uint16 decode_rm01_address (PC_ENV *m, int rm); +uint16 decode_rm10_address (PC_ENV *m, int rm); +uint8 fetch_data_byte (PC_ENV *m, uint16 offset); +uint8 fetch_data_byte_abs (PC_ENV *m, uint16 segment, uint16 offset); +uint16 fetch_data_word (PC_ENV *m, uint16 offset); +uint16 fetch_data_word_abs (PC_ENV *m, uint16 segment, uint16 offset); +void store_data_byte (PC_ENV *m, uint16 offset, uint8 val); +void store_data_byte_abs (PC_ENV *m, uint16 segment, uint16 offset, uint8 val); +void store_data_word (PC_ENV *m, uint16 offset, uint16 val); +void store_data_word_abs (PC_ENV *m, uint16 segment, uint16 offset, uint16 val); + +typedef void (*OP)(PC_ENV *m); +extern OP i86_optab[256]; + +/* PRIMITIVE OPERATIONS */ + +uint8 aad_word (PC_ENV *m, uint16 d); +uint16 aam_word (PC_ENV *m, uint8 d); +uint8 adc_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 adc_word (PC_ENV *m, uint16 d, uint16 s); +uint8 add_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 add_word (PC_ENV *m, uint16 d, uint16 s); +uint8 and_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 and_word (PC_ENV *m, uint16 d, uint16 s); +uint8 cmp_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 cmp_word (PC_ENV *m, uint16 d, uint16 s); +uint8 dec_byte (PC_ENV *m, uint8 d); +uint16 dec_word (PC_ENV *m, uint16 d); +uint8 inc_byte (PC_ENV *m, uint8 d); +uint16 inc_word (PC_ENV *m, uint16 d); +uint8 or_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 or_word (PC_ENV *m, uint16 d, uint16 s); +uint8 neg_byte (PC_ENV *m, uint8 s); +uint16 neg_word (PC_ENV *m, uint16 s); +uint8 not_byte (PC_ENV *m, uint8 s); +uint16 not_word (PC_ENV *m, uint16 s); +uint16 mem_access_word (PC_ENV *m, int addr); +void push_word (PC_ENV *m, uint16 w); +uint16 pop_word (PC_ENV *m); +uint8 rcl_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 rcl_word (PC_ENV *m, uint16 d, uint16 s); +uint8 rcr_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 rcr_word (PC_ENV *m, uint16 d, uint16 s); +uint8 rol_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 rol_word (PC_ENV *m, uint16 d, uint16 s); +uint8 ror_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 ror_word (PC_ENV *m, uint16 d, uint16 s); +uint8 shl_byte (PC_ENV *m, uint8 d, uint8 s) ; +uint16 shl_word (PC_ENV *m, uint16 d, uint16 s); +uint8 shr_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 shr_word (PC_ENV *m, uint16 d, uint16 s); +uint8 sar_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 sar_word (PC_ENV *m, uint16 d, uint16 s); +uint8 sbb_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 sbb_word (PC_ENV *m, uint16 d, uint16 s); +uint8 sub_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 sub_word (PC_ENV *m, uint16 d, uint16 s); +void test_byte (PC_ENV *m, uint8 d, uint8 s); +void test_word (PC_ENV *m, uint16 d, uint16 s); +uint8 xor_byte (PC_ENV *m, uint8 d, uint8 s); +uint16 xor_word (PC_ENV *m, uint16 d, uint16 s); +void imul_byte (PC_ENV *m, uint8 s); +void imul_word (PC_ENV *m, uint16 s); +void mul_byte (PC_ENV *m, uint8 s); +void mul_word (PC_ENV *m, uint16 s); +void idiv_byte (PC_ENV *m, uint8 s); +void idiv_word (PC_ENV *m, uint16 s); +void div_byte (PC_ENV *m, uint8 s); +void div_word (PC_ENV *m, uint16 s); diff --git a/AltairZ80/i86_decode.c b/AltairZ80/i86_decode.c new file mode 100644 index 0000000..8129159 --- /dev/null +++ b/AltairZ80/i86_decode.c @@ -0,0 +1,941 @@ +/* + * Dos/PC Emulator + * Copyright (C) 1991 Jim Hudgens + * + * + * The file is part of GDE. + * + * GDE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * GDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDE; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "altairz80_defs.h" +#include "i86.h" + +extern uint32 GetBYTEExtended(register uint32 Addr); +extern void PutBYTEExtended(register uint32 Addr, const register uint32 Value); +extern char messageBuffer[]; +extern void printMessage(void); +extern int32 AX_S; /* AX register (8086) */ +extern int32 BX_S; /* BX register (8086) */ +extern int32 CX_S; /* CX register (8086) */ +extern int32 DX_S; /* DX register (8086) */ +extern int32 CS_S; /* CS register (8086) */ +extern int32 DS_S; /* DS register (8086) */ +extern int32 ES_S; /* ES register (8086) */ +extern int32 SS_S; /* SS register (8086) */ +extern int32 DI_S; /* DI register (8086) */ +extern int32 SI_S; /* SI register (8086) */ +extern int32 BP_S; /* BP register (8086) */ +extern int32 SP8086_S; /* SP register (8086) */ +extern int32 IP_S; /* IP register (8086) */ +extern int32 FLAGS_S; /* flags register (8086) */ +extern int32 PC_S; /* PC register (8080/Z80/8086), 20 bit */ +extern int32 sim_interval; +extern uint32 PCX; /* external view of PC */ +extern uint32 sim_brk_summ; +extern UNIT cpu_unit; + +void i86_intr_raise(PC_ENV *m,uint8 intrnum); +void cpu8086reset(void); +t_stat sim_instr_8086(void); + +/* $Log: $ + * Revision 0.05 1992/04/12 23:16:42 hudgens + * Many changes. Added support for the QUICK_FETCH option, + * so that memory accesses are faster. Now compiles with gcc -Wall + * and gcc -traditional and Sun cc. + * + * Revision 0.04 1991/07/30 01:59:56 hudgens + * added copyright. + * + * Revision 0.03 1991/06/03 01:02:09 hudgens + * fixed minor problems due to unsigned to signed short integer + * promotions. + * + * Revision 0.02 1991/03/31 01:29:39 hudgens + * Fixed segment handling (overrides, default segment accessed) in + * routines decode_rmXX_address and the {fetch,store}_data_{byte,word}. + * + * Revision 0.01 1991/03/30 21:59:49 hudgens + * Initial checkin. + * + * + */ + +/* this file includes subroutines which do: + stuff involving decoding instruction formats. + stuff involving accessess of immediate data via IP. + etc. +*/ + +static void i86_intr_handle(PC_ENV *m) +{ uint16 tmp; + uint8 intno; + if (intr & INTR_SYNCH) /* raised by something */ + { + intno = m->intno; + tmp = (uint16) mem_access_word(m, intno * 4); + { + tmp = m->R_FLG; + push_word(m, tmp); + CLEAR_FLAG(m, F_IF); + CLEAR_FLAG(m, F_TF); + /* [JCE] If we're interrupting between a segment override (or REP override) + * and the following instruction, decrease IP to get back to the prefix */ + if (m->sysmode & (SYSMODE_SEGMASK | SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) + { + --m->R_IP; + } + /* [JCE] CS and IP were the wrong way round... */ + push_word(m, m->R_CS); + push_word(m, m->R_IP); + tmp = mem_access_word(m, intno * 4); + m->R_IP = tmp; + tmp = mem_access_word(m, intno * 4 + 2); + m->R_CS = tmp; + } + intr &= ~INTR_SYNCH; /* [JCE] Dealt with, reset flag */ + } + /* The interrupt code can't pick up the segment override status. */ + DECODE_CLEAR_SEGOVR(m); +} + +void i86_intr_raise(PC_ENV *m,uint8 intrnum) +{ + m->intno = intrnum; + intr |= INTR_SYNCH; +} + +static PC_ENV cpu8086; + +static void setViewRegisters(void) { + FLAGS_S = cpu8086.R_FLG; + AX_S = cpu8086.R_AX; + BX_S = cpu8086.R_BX; + CX_S = cpu8086.R_CX; + DX_S = cpu8086.R_DX; + SP8086_S = cpu8086.R_SP; + BP_S = cpu8086.R_BP; + SI_S = cpu8086.R_SI; + DI_S = cpu8086.R_DI; + ES_S = cpu8086.R_ES; + CS_S = cpu8086.R_CS; + SS_S = cpu8086.R_SS; + DS_S = cpu8086.R_DS; + IP_S = cpu8086.R_IP; +} + +static void setCPURegisters(void) { + cpu8086.R_FLG = FLAGS_S; + cpu8086.R_AX = AX_S; + cpu8086.R_BX = BX_S; + cpu8086.R_CX = CX_S; + cpu8086.R_DX = DX_S; + cpu8086.R_SP = SP8086_S; + cpu8086.R_BP = BP_S; + cpu8086.R_SI = SI_S; + cpu8086.R_DI = DI_S; + cpu8086.R_ES = ES_S; + cpu8086.R_CS = CS_S; + cpu8086.R_SS = SS_S; + cpu8086.R_DS = DS_S; + cpu8086.R_IP = IP_S; +} + +void cpu8086reset(void) { + cpu8086.R_AX = 0x1961; + if ((cpu8086.R_AH != 0x19) || (cpu8086.R_AL != 0x61)) { + printf("Fatal endian error - make sure to compile with '#define LOWFIRST %i'\n", 1 - LOWFIRST); + exit(1); + } + /* 16 bit registers */ + cpu8086.R_AX = 0; + cpu8086.R_BX = 0; + cpu8086.R_CX = 0; + cpu8086.R_DX = 0; + /* special registers */ + cpu8086.R_SP = 0; + cpu8086.R_BP = 0; + cpu8086.R_SI = 0; + cpu8086.R_DI = 0; + cpu8086.R_IP = 0; + cpu8086.R_FLG = F_ALWAYS_ON; + /* segment registers */ + cpu8086.R_CS = 0; + cpu8086.R_DS = 0; + cpu8086.R_SS = 0; + cpu8086.R_ES = 0; + setViewRegisters(); +} + +static uint32 getFullPC(void) { + return cpu8086.R_IP + (cpu8086.R_CS << 4); +} + +t_stat sim_instr_8086(void) { + t_stat reason = SCPE_OK; + uint8 op1; + int32 newIP; + setCPURegisters(); + intr = 0; + newIP = PC_S - 16 * CS_S; + if ((0 <= newIP) && (newIP <= 0xffff)) cpu8086.R_IP = newIP; + else { + if (CS_S != ((PC_S & 0xf0000) >> 4)) { + cpu8086.R_CS = (PC_S & 0xf0000) >> 4; + if (cpu_unit.flags & UNIT_CPU_VERBOSE) { + MESSAGE_2("Segment register CS set to %04x", cpu8086.R_CS); + } + } + cpu8086.R_IP = PC_S & 0xffff; + } + while (TRUE) { /* loop until halted */ + if (sim_interval <= 0) { /* check clock queue */ +#if !UNIX_PLATFORM + if ((reason = sim_os_poll_kbd()) == SCPE_STOP) /* poll on platforms without reliable signalling */ + break; +#endif + if ( (reason = sim_process_event()) ) break; + } + if (sim_brk_summ && sim_brk_test(getFullPC(), SWMASK('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + PCX = getFullPC(); + op1 = GetBYTEExtended((((uint32)cpu8086.R_CS<<4) + cpu8086.R_IP) & 0xFFFFF); + if (sim_brk_summ && sim_brk_test(op1, (1u << SIM_BKPT_V_SPC) | SWMASK('I'))) { /* instruction breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + sim_interval--; + cpu8086.R_IP++; + (*(i86_optab[op1]))(&cpu8086); + if (intr & INTR_HALTED) { + reason = STOP_HALT; + intr &= ~INTR_HALTED; + break; + } + if (intr & INTR_ILLEGAL_OPCODE) { + intr &= ~INTR_ILLEGAL_OPCODE; + if (cpu_unit.flags & UNIT_CPU_OPSTOP) { + reason = STOP_OPCODE; + break; + } + } + if (((intr & INTR_SYNCH) && (cpu8086.intno == 0 || cpu8086.intno == 2)) || + (ACCESS_FLAG(&cpu8086, F_IF))) { + /* [JCE] Reversed the sense of this ACCESS_FLAG; it's set for interrupts + enabled, not interrupts blocked i.e. either not blockable (intr 0 or 2) + or the IF flag not set so interrupts not blocked */ + /* hharte: if a segment override exists, then treat that as "atomic" and do not handle + * an interrupt until the override is cleared. + * Not sure if this is the way an 8086 really works, need to find out for sure. + * Also, what about the REPE prefix? + */ + if((cpu8086.sysmode & SYSMODE_SEGMASK) == 0) { + i86_intr_handle(&cpu8086); + } + } + } + PC_S = (reason == STOP_HALT) | (reason == STOP_OPCODE) ? PCX : getFullPC(); + setViewRegisters(); + return reason; +} + +void halt_sys(PC_ENV *m) +{ + intr |= INTR_HALTED; +} + +/* once the instruction is fetched, an optional byte follows which + has 3 fields encoded in it. This routine fetches the byte + and breaks into the three fields. + This has been changed, in an attempt to reduce the amount of + executed code for this frequently executed subroutine. If this + works, then it may pay to somehow inline it. + */ + +#ifdef NOTDEF +/* this code generated the following table */ +main() +{ int i; + printf("\n\nstruct modrm{ uint8 mod,rh,rl;} modrmtab[] = {\n"); + for (i=0; i<256; i++) + { + printf("{%d,%d,%d}, ",((i&0xc0)>>6),((i&0x38)>>3),(i&0x07)); + if (i%4==3) + printf("/* %d to %d */\n",i&0xfc,i); + } + printf("};\n\n"); +} +#endif + +struct modrm { uint16 mod, rh, rl; }; +static struct modrm modrmtab[] = { + {0,0,0}, {0,0,1}, {0,0,2}, {0,0,3}, /* 0 to 3 */ + {0,0,4}, {0,0,5}, {0,0,6}, {0,0,7}, /* 4 to 7 */ + {0,1,0}, {0,1,1}, {0,1,2}, {0,1,3}, /* 8 to 11 */ + {0,1,4}, {0,1,5}, {0,1,6}, {0,1,7}, /* 12 to 15 */ + {0,2,0}, {0,2,1}, {0,2,2}, {0,2,3}, /* 16 to 19 */ + {0,2,4}, {0,2,5}, {0,2,6}, {0,2,7}, /* 20 to 23 */ + {0,3,0}, {0,3,1}, {0,3,2}, {0,3,3}, /* 24 to 27 */ + {0,3,4}, {0,3,5}, {0,3,6}, {0,3,7}, /* 28 to 31 */ + {0,4,0}, {0,4,1}, {0,4,2}, {0,4,3}, /* 32 to 35 */ + {0,4,4}, {0,4,5}, {0,4,6}, {0,4,7}, /* 36 to 39 */ + {0,5,0}, {0,5,1}, {0,5,2}, {0,5,3}, /* 40 to 43 */ + {0,5,4}, {0,5,5}, {0,5,6}, {0,5,7}, /* 44 to 47 */ + {0,6,0}, {0,6,1}, {0,6,2}, {0,6,3}, /* 48 to 51 */ + {0,6,4}, {0,6,5}, {0,6,6}, {0,6,7}, /* 52 to 55 */ + {0,7,0}, {0,7,1}, {0,7,2}, {0,7,3}, /* 56 to 59 */ + {0,7,4}, {0,7,5}, {0,7,6}, {0,7,7}, /* 60 to 63 */ + {1,0,0}, {1,0,1}, {1,0,2}, {1,0,3}, /* 64 to 67 */ + {1,0,4}, {1,0,5}, {1,0,6}, {1,0,7}, /* 68 to 71 */ + {1,1,0}, {1,1,1}, {1,1,2}, {1,1,3}, /* 72 to 75 */ + {1,1,4}, {1,1,5}, {1,1,6}, {1,1,7}, /* 76 to 79 */ + {1,2,0}, {1,2,1}, {1,2,2}, {1,2,3}, /* 80 to 83 */ + {1,2,4}, {1,2,5}, {1,2,6}, {1,2,7}, /* 84 to 87 */ + {1,3,0}, {1,3,1}, {1,3,2}, {1,3,3}, /* 88 to 91 */ + {1,3,4}, {1,3,5}, {1,3,6}, {1,3,7}, /* 92 to 95 */ + {1,4,0}, {1,4,1}, {1,4,2}, {1,4,3}, /* 96 to 99 */ + {1,4,4}, {1,4,5}, {1,4,6}, {1,4,7}, /* 100 to 103 */ + {1,5,0}, {1,5,1}, {1,5,2}, {1,5,3}, /* 104 to 107 */ + {1,5,4}, {1,5,5}, {1,5,6}, {1,5,7}, /* 108 to 111 */ + {1,6,0}, {1,6,1}, {1,6,2}, {1,6,3}, /* 112 to 115 */ + {1,6,4}, {1,6,5}, {1,6,6}, {1,6,7}, /* 116 to 119 */ + {1,7,0}, {1,7,1}, {1,7,2}, {1,7,3}, /* 120 to 123 */ + {1,7,4}, {1,7,5}, {1,7,6}, {1,7,7}, /* 124 to 127 */ + {2,0,0}, {2,0,1}, {2,0,2}, {2,0,3}, /* 128 to 131 */ + {2,0,4}, {2,0,5}, {2,0,6}, {2,0,7}, /* 132 to 135 */ + {2,1,0}, {2,1,1}, {2,1,2}, {2,1,3}, /* 136 to 139 */ + {2,1,4}, {2,1,5}, {2,1,6}, {2,1,7}, /* 140 to 143 */ + {2,2,0}, {2,2,1}, {2,2,2}, {2,2,3}, /* 144 to 147 */ + {2,2,4}, {2,2,5}, {2,2,6}, {2,2,7}, /* 148 to 151 */ + {2,3,0}, {2,3,1}, {2,3,2}, {2,3,3}, /* 152 to 155 */ + {2,3,4}, {2,3,5}, {2,3,6}, {2,3,7}, /* 156 to 159 */ + {2,4,0}, {2,4,1}, {2,4,2}, {2,4,3}, /* 160 to 163 */ + {2,4,4}, {2,4,5}, {2,4,6}, {2,4,7}, /* 164 to 167 */ + {2,5,0}, {2,5,1}, {2,5,2}, {2,5,3}, /* 168 to 171 */ + {2,5,4}, {2,5,5}, {2,5,6}, {2,5,7}, /* 172 to 175 */ + {2,6,0}, {2,6,1}, {2,6,2}, {2,6,3}, /* 176 to 179 */ + {2,6,4}, {2,6,5}, {2,6,6}, {2,6,7}, /* 180 to 183 */ + {2,7,0}, {2,7,1}, {2,7,2}, {2,7,3}, /* 184 to 187 */ + {2,7,4}, {2,7,5}, {2,7,6}, {2,7,7}, /* 188 to 191 */ + {3,0,0}, {3,0,1}, {3,0,2}, {3,0,3}, /* 192 to 195 */ + {3,0,4}, {3,0,5}, {3,0,6}, {3,0,7}, /* 196 to 199 */ + {3,1,0}, {3,1,1}, {3,1,2}, {3,1,3}, /* 200 to 203 */ + {3,1,4}, {3,1,5}, {3,1,6}, {3,1,7}, /* 204 to 207 */ + {3,2,0}, {3,2,1}, {3,2,2}, {3,2,3}, /* 208 to 211 */ + {3,2,4}, {3,2,5}, {3,2,6}, {3,2,7}, /* 212 to 215 */ + {3,3,0}, {3,3,1}, {3,3,2}, {3,3,3}, /* 216 to 219 */ + {3,3,4}, {3,3,5}, {3,3,6}, {3,3,7}, /* 220 to 223 */ + {3,4,0}, {3,4,1}, {3,4,2}, {3,4,3}, /* 224 to 227 */ + {3,4,4}, {3,4,5}, {3,4,6}, {3,4,7}, /* 228 to 231 */ + {3,5,0}, {3,5,1}, {3,5,2}, {3,5,3}, /* 232 to 235 */ + {3,5,4}, {3,5,5}, {3,5,6}, {3,5,7}, /* 236 to 239 */ + {3,6,0}, {3,6,1}, {3,6,2}, {3,6,3}, /* 240 to 243 */ + {3,6,4}, {3,6,5}, {3,6,6}, {3,6,7}, /* 244 to 247 */ + {3,7,0}, {3,7,1}, {3,7,2}, {3,7,3}, /* 248 to 251 */ + {3,7,4}, {3,7,5}, {3,7,6}, {3,7,7}, /* 252 to 255 */ +}; + +void fetch_decode_modrm(PC_ENV *m, uint16 *mod, uint16 *regh, uint16 *regl) +{ uint8 fetched; + register struct modrm *p; + /* do the fetch in real mode. Shift the CS segment register + over by 4 bits, and add in the IP register. Index into + the system memory. + */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + fetched = GetBYTEExtended(((m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); + +#ifdef NOTDEF + *mod = ((fetched&0xc0)>>6); + *regh= ((fetched&0x38)>>3); + *regl= (fetched&0x7); +#else + p = modrmtab + fetched; + *mod = p->mod; + *regh= p->rh; + *regl= p->rl; +#endif + +} + +/* + return a pointer to the register given by the R/RM field of + the modrm byte, for byte operands. + Also enables the decoding of instructions. +*/ +uint8 *decode_rm_byte_register(PC_ENV *m, int reg) +{ + switch(reg) + { + case 0: + return &m->R_AL; + break; + case 1: + return &m->R_CL; + break; + case 2: + return &m->R_DL; + break; + case 3: + return &m->R_BL; + break; + case 4: + return &m->R_AH; + break; + case 5: + return &m->R_CH; + break; + case 6: + return &m->R_DH; + break; + case 7: + return &m->R_BH; + break; + } + halt_sys(m); + return NULL; /* NOT REACHED OR REACHED ON ERROR */ +} + +/* + return a pointer to the register given by the R/RM field of + the modrm byte, for word operands. + Also enables the decoding of instructions. +*/ +uint16 *decode_rm_word_register(PC_ENV *m, int reg) +{ + switch(reg) + { + case 0: + return &m->R_AX; + break; + case 1: + return &m->R_CX; + break; + case 2: + return &m->R_DX; + break; + case 3: + return &m->R_BX; + break; + case 4: + return &m->R_SP; + break; + case 5: + return &m->R_BP; + break; + case 6: + return &m->R_SI; + break; + case 7: + return &m->R_DI; + break; + } + halt_sys(m); + return NULL; /* NOTREACHED OR REACHED ON ERROR*/ +} + +/* + return a pointer to the register given by the R/RM field of + the modrm byte, for word operands, modified from above + for the weirdo special case of segreg operands. + Also enables the decoding of instructions. +*/ +uint16 *decode_rm_seg_register(PC_ENV *m, int reg) +{ + switch(reg) + { + case 0: + return &m->R_ES; + break; + case 1: + return &m->R_CS; + break; + case 2: + return &m->R_SS; + break; + case 3: + return &m->R_DS; + break; + case 4: + case 5: + case 6: + case 7: + break; + } + halt_sys(m); + return NULL; /* NOT REACHED OR REACHED ON ERROR */ +} + +/* once the instruction is fetched, an optional byte follows which + has 3 fields encoded in it. This routine fetches the byte + and breaks into the three fields. +*/ +uint8 fetch_byte_imm(PC_ENV *m) +{ + uint8 fetched; + /* do the fetch in real mode. Shift the CS segment register + over by 4 bits, and add in the IP register. Index into + the system memory. + */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + fetched = GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); + return fetched; +} + +uint16 fetch_word_imm(PC_ENV *m) +{ + uint16 fetched; + /* do the fetch in real mode. Shift the CS segment register + over by 4 bits, and add in the IP register. Index into + the system PC_ENVory. + */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + fetched = GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF); + fetched |= (GetBYTEExtended((((uint32)m->R_CS << 4) + (m->R_IP++)) & 0xFFFFF) << 8); + return fetched; +} + +/* + return the offset given by mod=00 addressing. + Also enables the decoding of instructions. +*/ +uint16 decode_rm00_address(PC_ENV *m, int rm) +{ + uint16 offset; + /* note the code which specifies the corresponding segment (ds vs ss) + below in the case of [BP+..]. The assumption here is that at the + point that this subroutine is called, the bit corresponding to + SYSMODE_SEG_DS_SS will be zero. After every instruction + except the segment override instructions, this bit (as well + as any bits indicating segment overrides) will be clear. So + if a SS access is needed, set this bit. Otherwise, DS access + occurs (unless any of the segment override bits are set). + */ + switch(rm) + { + case 0: + return (int16)m->R_BX + (int16)m->R_SI; + break; + case 1: + return (int16)m->R_BX + (int16)m->R_DI; + break; + case 2: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + (int16)m->R_SI; + break; + case 3: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + (int16)m->R_DI; + break; + case 4: + return m->R_SI; + break; + case 5: + return m->R_DI; + break; + case 6: + offset = (int16)fetch_word_imm(m); + return offset; + break; + case 7: + return m->R_BX; + } + halt_sys(m); + return 0; +} + +/* + return the offset given by mod=01 addressing. + Also enables the decoding of instructions. +*/ +uint16 decode_rm01_address(PC_ENV *m, int rm) +{ + int8 displacement; + /* note comment on decode_rm00_address above */ + displacement = (int8)fetch_byte_imm(m); /* !!!! Check this */ + switch(rm) + { + case 0: + return (int16)m->R_BX + (int16)m->R_SI + displacement; + break; + case 1: + return (int16)m->R_BX + (int16)m->R_DI + displacement; + break; + case 2: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + (int16)m->R_SI + displacement; + break; + case 3: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + (int16)m->R_DI + displacement; + break; + case 4: + return (int16)m->R_SI + displacement; + break; + case 5: + return (int16)m->R_DI + displacement; + break; + case 6: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + displacement; + break; + case 7: + return (int16)m->R_BX + displacement; + break; + } + halt_sys(m); + return 0; /* SHOULD NOT HAPPEN */ +} + +/* + return the offset given by mod=01 addressing. + Also enables the decoding of instructions. +*/ +uint16 decode_rm10_address(PC_ENV *m, int rm) +{ + int16 displacement; + /* note comment on decode_rm00_address above */ + displacement = (int16)fetch_word_imm(m); + switch(rm) + { + case 0: + return (int16)m->R_BX + (int16)m->R_SI + displacement; + break; + case 1: + return (int16)m->R_BX + (int16)m->R_DI + displacement; + break; + case 2: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + (int16)m->R_SI + displacement; + break; + case 3: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + (int16)m->R_DI + displacement; + break; + case 4: + return (int16)m->R_SI + displacement; + break; + case 5: + return (int16)m->R_DI + displacement; + break; + case 6: + m->sysmode |= SYSMODE_SEG_DS_SS; + return (int16)m->R_BP + displacement; + break; + case 7: + return (int16)m->R_BX + displacement; + break; + } + halt_sys(m); + return 0; + /*NOTREACHED */ +} + +/* fetch a byte of data, given an offset, the current register set, + and a descriptor for memory. +*/ +uint8 fetch_data_byte(PC_ENV *m, uint16 offset) +{ + register uint8 value; + /* this code originally completely broken, and never showed + up since the DS segments === SS segment in all test cases. + It had been originally assumed, that all access to data would + involve the DS register unless there was a segment override. + Not so. Address modes such as -3[BP] or 10[BP+SI] all + refer to addresses relative to the SS. So, at the minimum, + all decodings of addressing modes would have to set/clear + a bit describing whether the access is relative to DS or SS. + That is the function of the cpu-state-varible m->sysmode. + There are several potential states: + repe prefix seen (handled elsewhere) + repne prefix seen (ditto) + cs segment override + ds segment override + es segment override + ss segment override + ds/ss select (in absense of override) + Each of the above 7 items are handled with a bit in the sysmode + field. + The latter 5 can be implemented as a simple state machine: + */ + switch(m->sysmode & SYSMODE_SEGMASK) + { + case 0: + /* default case: use ds register */ + value = GetBYTEExtended(((uint32)m->R_DS<<4) + offset); + break; + case SYSMODE_SEG_DS_SS: + /* non-overridden, use ss register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF); + break; + case SYSMODE_SEGOVR_CS: + /* ds overridden */ + case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: + /* ss overridden, use cs register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_CS << 4) + offset) & 0xFFFFF); + break; + case SYSMODE_SEGOVR_DS: + /* ds overridden --- shouldn't happen, but hey. */ + case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ds register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF); + break; + case SYSMODE_SEGOVR_ES: + /* ds overridden */ + case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: + /* ss overridden, use es register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_ES << 4) + offset) & 0xFFFFF); + break; + case SYSMODE_SEGOVR_SS: + /* ds overridden */ + case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ss register === should not happen */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF); + break; + default: + printf("error: should not happen: multiple overrides. " NLP); + value = 0; + halt_sys(m); + } + return value; +} + +/* fetch a byte of data, given an offset, the current register set, + and a descriptor for memory. +*/ +uint8 fetch_data_byte_abs(PC_ENV *m, uint16 segment, uint16 offset) +{ + register uint8 value; + uint32 addr; + /* note, cannot change this, since we do not know the ID of the segment. */ +/* [JCE] Simulate wrap at top of memory (the A20 gate) */ +/* addr = (segment << 4) + offset; */ + addr = ((segment << 4) + offset) & 0xFFFFF; + value = GetBYTEExtended(addr); + return value; +} + +/* fetch a byte of data, given an offset, the current register set, + and a descriptor for memory. +*/ +uint16 fetch_data_word(PC_ENV *m, uint16 offset) +{ + uint16 value; + /* See note above in fetch_data_byte. */ + switch(m->sysmode & SYSMODE_SEGMASK) + { + case 0: + /* default case: use ds register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF) + | (GetBYTEExtended((((uint32)m->R_DS << 4) + + (uint16)(offset + 1)) & 0xFFFFF) << 8); + break; + case SYSMODE_SEG_DS_SS: + /* non-overridden, use ss register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF) + | (GetBYTEExtended((((uint32)m->R_SS << 4) + + (uint16)(offset + 1)) & 0xFFFFF) << 8); + break; + case SYSMODE_SEGOVR_CS: + /* ds overridden */ + case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: + /* ss overridden, use cs register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_CS << 4) + offset) & 0xFFFFF) + | (GetBYTEExtended((((uint32)m->R_CS << 4) + + (uint16)(offset + 1)) & 0xFFFFF) << 8); + break; + case SYSMODE_SEGOVR_DS: + /* ds overridden --- shouldn't happen, but hey. */ + case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ds register */ + /* [JCE] Wrap at 1Mb (the A20 gate) */ + value = GetBYTEExtended((((uint32)m->R_DS << 4) + offset) & 0xFFFFF) + | (GetBYTEExtended((((uint32)m->R_DS << 4) + + (uint16)(offset + 1)) & 0xFFFFF) << 8); + break; + case SYSMODE_SEGOVR_ES: + /* ds overridden */ + case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: + /* ss overridden, use es register */ + value = GetBYTEExtended((((uint32)m->R_ES << 4) + offset) & 0xFFFFF) + | (GetBYTEExtended((((uint32)m->R_ES << 4) + + (uint16)(offset + 1)) & 0xFFFFF) << 8); + break; + case SYSMODE_SEGOVR_SS: + /* ds overridden */ + case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ss register === should not happen */ + value = GetBYTEExtended((((uint32)m->R_SS << 4) + offset) & 0xFFFFF) + | (GetBYTEExtended((((uint32)m->R_SS << 4) + + (uint16)(offset + 1)) & 0xFFFFF) << 8); + break; + default: + printf("error: should not happen: multiple overrides. " NLP); + value = 0; + halt_sys(m); + } + return value; +} + +/* fetch a byte of data, given an offset, the current register set, + and a descriptor for memory. +*/ +uint16 fetch_data_word_abs(PC_ENV *m, uint16 segment, uint16 offset) +{ + uint16 value; + uint32 addr; +/* [JCE] Simulate wrap at top of memory (the A20 gate) */ +/* addr = (segment << 4) + offset; */ + addr = ((segment << 4) + offset) & 0xFFFFF; + value = GetBYTEExtended(addr) | (GetBYTEExtended(addr + 1) << 8); + return value; +} + +/* Store a byte of data, given an offset, the current register set, + and a descriptor for memory. +*/ +void store_data_byte(PC_ENV *m, uint16 offset, uint8 val) +{ + /* See note above in fetch_data_byte. */ + uint32 addr; + register uint16 segment; + switch(m->sysmode & SYSMODE_SEGMASK) + { + case 0: + /* default case: use ds register */ + segment = m->R_DS; + break; + case SYSMODE_SEG_DS_SS: + /* non-overridden, use ss register */ + segment = m->R_SS; + break; + case SYSMODE_SEGOVR_CS: + /* ds overridden */ + case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: + /* ss overridden, use cs register */ + segment = m->R_CS; + break; + case SYSMODE_SEGOVR_DS: + /* ds overridden --- shouldn't happen, but hey. */ + case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ds register */ + segment = m->R_DS; + break; + case SYSMODE_SEGOVR_ES: + /* ds overridden */ + case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: + /* ss overridden, use es register */ + segment = m->R_ES; + break; + case SYSMODE_SEGOVR_SS: + /* ds overridden */ + case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ss register === should not happen */ + segment = m->R_SS; + break; + default: + printf("error: should not happen: multiple overrides. " NLP); + segment = 0; + halt_sys(m); + } +/* [JCE] Simulate wrap at top of memory (the A20 gate) */ +/* addr = (segment << 4) + offset; */ + addr = (((uint32)segment << 4) + offset) & 0xFFFFF; + PutBYTEExtended(addr, val); +} + +void store_data_byte_abs(PC_ENV *m, uint16 segment, uint16 offset, uint8 val) +{ + register uint32 addr; +/* [JCE] Simulate wrap at top of memory (the A20 gate) */ +/* addr = (segment << 4) + offset; */ + addr = (((uint32)segment << 4) + offset) & 0xFFFFF; + PutBYTEExtended(addr, val); +} + +/* Store a word of data, given an offset, the current register set, + and a descriptor for memory. +*/ +void store_data_word(PC_ENV *m, uint16 offset, uint16 val) +{ + register uint32 addr; + register uint16 segment; + /* See note above in fetch_data_byte. */ + switch(m->sysmode & SYSMODE_SEGMASK) + { + case 0: + /* default case: use ds register */ + segment = m->R_DS; + break; + case SYSMODE_SEG_DS_SS: + /* non-overridden, use ss register */ + segment = m->R_SS; + break; + case SYSMODE_SEGOVR_CS: + /* ds overridden */ + case SYSMODE_SEGOVR_CS|SYSMODE_SEG_DS_SS: + /* ss overridden, use cs register */ + segment = m->R_CS; + break; + case SYSMODE_SEGOVR_DS: + /* ds overridden --- shouldn't happen, but hey. */ + case SYSMODE_SEGOVR_DS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ds register */ + segment = m->R_DS; + break; + case SYSMODE_SEGOVR_ES: + /* ds overridden */ + case SYSMODE_SEGOVR_ES|SYSMODE_SEG_DS_SS: + /* ss overridden, use es register */ + segment = m->R_ES; + break; + case SYSMODE_SEGOVR_SS: + /* ds overridden */ + case SYSMODE_SEGOVR_SS|SYSMODE_SEG_DS_SS: + /* ss overridden, use ss register === should not happen */ + segment = m->R_SS; + break; + default: + printf("error: should not happen: multiple overrides." NLP); + segment = 0; + halt_sys(m); + } +/* [JCE] Simulate wrap at top of memory (the A20 gate) */ +/* addr = (segment << 4) + offset; */ + addr = (((uint32)segment << 4) + offset) & 0xFFFFF; + PutBYTEExtended(addr, val & 0xff); + PutBYTEExtended(addr + 1, val >> 8); +} + +void store_data_word_abs(PC_ENV *m, uint16 segment, uint16 offset, uint16 val) +{ + register uint32 addr; + /* [JCE] Wrap at top of memory */ + addr = ((segment << 4) + offset) & 0xFFFFF; + PutBYTEExtended(addr, val & 0xff); + PutBYTEExtended(addr + 1, val >> 8); +} diff --git a/AltairZ80/i86_ops.c b/AltairZ80/i86_ops.c new file mode 100644 index 0000000..145e35f --- /dev/null +++ b/AltairZ80/i86_ops.c @@ -0,0 +1,5487 @@ +/* + * Dos/PC Emulator + * Copyright (C) 1991 Jim Hudgens + * + * + * The file is part of GDE. + * + * GDE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * GDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDE; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "altairz80_defs.h" +#include "i86.h" + +extern void out(const uint32 Port, const uint32 Value); +extern uint32 in(const uint32 Port); + +/* $Log: i86_ops.c,v $ + * Revision 0.11 1991/07/30 02:02:04 hudgens + * added copyright. + * + * Revision 0.10 1991/07/21 18:22:08 hudgens + * Fixed problem with scans, which was the result of the loop break + * condition being incorrect when used in conjunction with the repe + * or repne prefixes. Eureka. pkzip/pkunzip now compress/decompress + * correctly. + * + * Revision 0.9 1991/07/21 03:33:18 hudgens + * fixed popf so that it appears to be the same as an 8088 popf, and always + * sets the high 4 bits of the flags. + * + * Revision 0.8 1991/07/21 01:44:11 hudgens + * fixed aad and aam instructions. + * + * Revision 0.7 1991/07/21 00:31:24 hudgens + * Fixed iret so that it works correctly. + * + * Revision 0.6 1991/07/20 16:54:50 hudgens + * removed the 8087 coprocessor operations. Moved to i87_ops.c + * + * Revision 0.5 1991/07/17 03:50:10 hudgens + * Minor modifications. + * + * Revision 0.4 1991/06/18 02:48:41 hudgens + * Fixed a problem with scasb and scasw. + * + * Revision 0.3 1991/06/03 01:01:10 hudgens + * fixed minor problems due to unsigned to signed short integer + * promotions. + * + * Revision 0.2 1991/03/31 01:32:10 hudgens + * fixed segment handling. Added calls to DECODE_CLEAR_SEGOVR in + * many places in the code. Should work much better now. + * + * Revision 0.1 1991/03/30 21:15:48 hudgens + * Initial checkin to RCS. + * + * + */ + +/* 2/23/91 fixed decode for operand x87. */ + +/* partially mechanically generated file....(based on the optable) */ +/* + There are approximately 250 subroutines in here, which correspond + to the 256 byte-"opcodes" found on the 8086. The table which + dispatches this is found in the files optab.[ch]. + + Each opcode proc has a comment preceeding it which gives it's table + address. Several opcodes are missing (undefined) in the table. + + Each proc includes information for decoding (DECODE_PRINTF and + and misc functions ( + Many of the procedures are *VERY* similar in coding. This has + allowed for a very large amount of code to be generated in a fairly + short amount of time (i.e. cut, paste, and modify). + The result is that much of the code below could + have been folded into subroutines for a large reduction in size of + this file. The downside would be that there would be a penalty in + execution speed. The file could also have been *MUCH* larger by + inlining certain functions which were called. This could have + resulted even faster execution. The prime directive I used to decide + whether to inline the code or to modularize it, was basically: 1) no + unnecessary subroutine calls, 2) no routines more than about 200 lines + in size, and 3) modularize any code that I might not get right the first + time. The fetch_* subroutines fall into the latter category. The + The decode_* fall into the second category. The coding of the + "switch(mod){ .... }" in many of the subroutines below falls into the + first category. Especially, the coding of {add,and,or,sub,...}_{byte,word} + subroutines are an especially glaring case of the third guideline. + Since so much of the code is cloned from other modules (compare + opcode #00 to opcode #01), making the basic operations subroutine calls + is especially important; otherwise mistakes in coding an "add" + would represent a nightmare in maintenance. + + So, without further ado, ... +*/ + +extern char parity_tab[]; + +static void i86op_illegal_op(PC_ENV *m) +{ + intr |= INTR_ILLEGAL_OPCODE; +} + +/*opcode=0x00*/ +static void i86op_add_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = add_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = add_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = add_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = add_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x01*/ +static void i86op_add_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = add_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = add_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = add_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = add_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x02*/ +static void i86op_add_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = add_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = add_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = add_byte(m, * destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = add_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x03*/ +static void i86op_add_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = add_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = add_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = add_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = add_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x04*/ +static void i86op_add_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + m->R_AL = add_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x05*/ +static void i86op_add_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + m->R_AX = add_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x06*/ +static void i86op_push_ES(PC_ENV *m) +{ + push_word(m,m->R_ES); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x07*/ +static void i86op_pop_ES(PC_ENV *m) +{ + m->R_ES = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x08*/ +static void i86op_or_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = or_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = or_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = or_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = or_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x09*/ +static void i86op_or_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = or_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = or_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = or_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = or_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x0a*/ +static void i86op_or_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = or_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = or_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = or_byte(m, * destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = or_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x0b*/ +static void i86op_or_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = or_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = or_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = or_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = or_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x0c*/ +static void i86op_or_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + m->R_AL = or_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x0d*/ +static void i86op_or_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + m->R_AX = or_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x0e*/ +static void i86op_push_CS(PC_ENV *m) +{ + push_word(m,m->R_CS); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x0f === ILLEGAL OP*/ + +/*opcode=0x10*/ +static void i86op_adc_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = adc_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = adc_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = adc_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = adc_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x11*/ +static void i86op_adc_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = adc_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = adc_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = adc_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = adc_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x12*/ +static void i86op_adc_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = adc_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = adc_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = adc_byte(m, * destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = adc_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x13*/ +static void i86op_adc_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = adc_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = adc_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = adc_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = adc_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x14*/ +static void i86op_adc_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + m->R_AL = adc_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x15*/ +static void i86op_adc_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + m->R_AX = adc_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x16*/ +static void i86op_push_SS(PC_ENV *m) +{ + push_word(m,m->R_SS); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x17*/ +static void i86op_pop_SS(PC_ENV *m) +{ + m->R_SS = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x18*/ +static void i86op_sbb_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = sbb_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = sbb_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = sbb_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = sbb_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x19*/ +static void i86op_sbb_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = sbb_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = sbb_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = sbb_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = sbb_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x1a*/ +static void i86op_sbb_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = sbb_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = sbb_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = sbb_byte(m, * destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = sbb_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x1b*/ +static void i86op_sbb_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = sbb_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = sbb_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = sbb_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = sbb_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x1c*/ +static void i86op_sbb_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + m->R_AL = sbb_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x1d*/ +static void i86op_sbb_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + m->R_AX = sbb_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x1e*/ +static void i86op_push_DS(PC_ENV *m) +{ + push_word(m,m->R_DS); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x1f*/ +static void i86op_pop_DS(PC_ENV *m) +{ + m->R_DS = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x20*/ +static void i86op_and_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = and_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = and_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = and_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = and_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x21*/ +static void i86op_and_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = and_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = and_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = and_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = and_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x22*/ +static void i86op_and_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = and_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = and_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = and_byte(m, * destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = and_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x23*/ +static void i86op_and_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = and_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = and_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = and_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = and_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x24*/ +static void i86op_and_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + m->R_AL = and_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x25*/ +static void i86op_and_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + m->R_AX = and_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x26*/ +static void i86op_segovr_ES(PC_ENV *m) +{ + m->sysmode |= SYSMODE_SEGOVR_ES; + /* note the lack of DECODE_CLEAR_SEGOVR(r) + since, here is one of 4 opcode subroutines we do not + want to do this. + */ +} + +/*opcode=0x27*/ +static void i86op_daa(PC_ENV *m) +{ + uint16 dbyte; + dbyte = m->R_AL; + if (ACCESS_FLAG(m,F_AF)|| (dbyte&0xf) > 9) + { + dbyte += 6; + if (dbyte&0x100) + SET_FLAG(m, F_CF); + SET_FLAG(m, F_AF); + } + else + CLEAR_FLAG(m, F_AF); + if (ACCESS_FLAG(m,F_CF) || (dbyte&0xf0) > 0x90) + { + dbyte += 0x60; + SET_FLAG(m, F_CF); + } + else + CLEAR_FLAG(m, F_CF); + m->R_AL = (uint8) dbyte; + CONDITIONAL_SET_FLAG((m->R_AL & 0x80),m,F_SF); + CONDITIONAL_SET_FLAG((m->R_AL == 0), m,F_ZF); + CONDITIONAL_SET_FLAG((parity_tab[m->R_AL]),m,F_PF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x28*/ +static void i86op_sub_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = sub_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = sub_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = sub_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = sub_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x29*/ +static void i86op_sub_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = sub_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = sub_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = sub_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = sub_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x2a*/ +static void i86op_sub_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = sub_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = sub_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = sub_byte(m, * destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = sub_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x2b*/ +static void i86op_sub_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = sub_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = sub_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = sub_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = sub_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x2c*/ +static void i86op_sub_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + m->R_AL = sub_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x2d*/ +static void i86op_sub_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + m->R_AX = sub_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x2e*/ +static void i86op_segovr_CS(PC_ENV *m) +{ + m->sysmode |= SYSMODE_SEGOVR_CS; + /* note no DECODE_CLEAR_SEGOVER here. */ +} + +/*opcode=0x2f*/ +static void i86op_das(PC_ENV *m) +{ + uint16 dbyte; + dbyte = m->R_AL; + if ( ACCESS_FLAG(m,F_AF) || (dbyte&0xf) > 9) + { + dbyte -= 6; + if (dbyte&0x100) /* XXXXX --- this is WRONG */ + SET_FLAG(m, F_CF); + SET_FLAG(m, F_AF); + } + else + CLEAR_FLAG(m, F_AF); + if (ACCESS_FLAG(m,F_CF) || (dbyte&0xf0) > 0x90) + { + dbyte -= 0x60; + SET_FLAG(m, F_CF); + } + else + CLEAR_FLAG(m, F_CF); + m->R_AL = (uint8) dbyte; + CONDITIONAL_SET_FLAG(m->R_AL & 0x80,m,F_SF); + CONDITIONAL_SET_FLAG(m->R_AL == 0,m,F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[m->R_AL],m,F_PF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x30*/ +static void i86op_xor_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = xor_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = xor_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + destval = xor_byte(m, destval, *srcreg); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = xor_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x31*/ +static void i86op_xor_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = xor_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = xor_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destval = xor_word(m, destval, *srcreg); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = xor_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x32*/ +static void i86op_xor_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = xor_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = xor_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = xor_byte(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = xor_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x33*/ +static void i86op_xor_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = xor_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = xor_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = xor_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = xor_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x34*/ +static void i86op_xor_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + m->R_AL = xor_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x35*/ +static void i86op_xor_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + m->R_AX = xor_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x36*/ +static void i86op_segovr_SS(PC_ENV *m) +{ + m->sysmode |= SYSMODE_SEGOVR_SS; + /* no DECODE_CLEAR_SEGOVER ! */ +} + +/*opcode=0x37*/ +static void i86op_aaa(PC_ENV *m) +{ + if ( (m->R_AL & 0xf) > 0x9 || ACCESS_FLAG(m,F_AF)) + { + m->R_AL += 0x6; + m->R_AH += 1; + SET_FLAG(m, F_AF); + SET_FLAG(m, F_CF); + } + else + { + CLEAR_FLAG(m, F_CF); + CLEAR_FLAG(m, F_AF); + } + m->R_AL &= 0xf; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x38*/ +static void i86op_cmp_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + cmp_byte(m, destval, *srcreg); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + cmp_byte(m, destval, *srcreg); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + cmp_byte(m, destval, *srcreg); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + cmp_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x39*/ +static void i86op_cmp_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + cmp_word(m, destval, *srcreg); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + cmp_word(m, destval, *srcreg); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + cmp_word(m, destval, *srcreg); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + cmp_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x3a*/ +static void i86op_cmp_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + cmp_byte(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + cmp_byte(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + cmp_byte(m, * destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + cmp_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x3b*/ +static void i86op_cmp_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + cmp_word(m, *destreg, srcval); + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + cmp_word(m, *destreg, srcval); + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + cmp_word(m, *destreg, srcval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + cmp_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x3c*/ +static void i86op_cmp_byte_AL_IMM(PC_ENV *m) +{ + uint8 srcval; + srcval = fetch_byte_imm(m); + cmp_byte(m, m->R_AL, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x3d*/ +static void i86op_cmp_word_AX_IMM(PC_ENV *m) +{ + uint16 srcval; + srcval = fetch_word_imm(m); + cmp_word(m, m->R_AX, srcval); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x3e*/ +static void i86op_segovr_DS(PC_ENV *m) +{ + m->sysmode |= SYSMODE_SEGOVR_DS; + /* NO DECODE_CLEAR_SEGOVR! */ +} + +/*opcode=0x3f*/ +static void i86op_aas(PC_ENV *m) +{ + /* ???? Check out the subtraction here. Will this ?ever? cause + the contents of R_AL or R_AH to be affected incorrectly since + they are being subtracted from *and* are unsigned. + Should put an assertion in here. + */ + if ( (m->R_AL & 0xf) > 0x9 || ACCESS_FLAG(m,F_AF)) + { + m->R_AL -= 0x6; + m->R_AH -= 1; + SET_FLAG(m, F_AF); + SET_FLAG(m, F_CF); + } + else + { + CLEAR_FLAG(m, F_CF); + CLEAR_FLAG(m, F_AF); + } + m->R_AL &= 0xf; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x40*/ +static void i86op_inc_AX(PC_ENV *m) +{ + m->R_AX = inc_word(m,m->R_AX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x41*/ +static void i86op_inc_CX(PC_ENV *m) +{ + m->R_CX = inc_word(m,m->R_CX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x42*/ +static void i86op_inc_DX(PC_ENV *m) +{ + m->R_DX = inc_word(m,m->R_DX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x43*/ +static void i86op_inc_BX(PC_ENV *m) +{ + m->R_BX = inc_word(m,m->R_BX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x44*/ +static void i86op_inc_SP(PC_ENV *m) +{ + m->R_SP = inc_word(m,m->R_SP); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x45*/ +static void i86op_inc_BP(PC_ENV *m) +{ + m->R_BP = inc_word(m,m->R_BP); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x46*/ +static void i86op_inc_SI(PC_ENV *m) +{ + m->R_SI = inc_word(m,m->R_SI); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x47*/ +static void i86op_inc_DI(PC_ENV *m) +{ + m->R_DI = inc_word(m,m->R_DI); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x48*/ +static void i86op_dec_AX(PC_ENV *m) +{ + m->R_AX = dec_word(m,m->R_AX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x49*/ +static void i86op_dec_CX(PC_ENV *m) +{ + m->R_CX = dec_word(m,m->R_CX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x4a*/ +static void i86op_dec_DX(PC_ENV *m) +{ + m->R_DX = dec_word(m,m->R_DX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x4b*/ +static void i86op_dec_BX(PC_ENV *m) +{ + m->R_BX = dec_word(m,m->R_BX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x4c*/ +static void i86op_dec_SP(PC_ENV *m) +{ + m->R_SP = dec_word(m,m->R_SP); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x4d*/ +static void i86op_dec_BP(PC_ENV *m) +{ + m->R_BP = dec_word(m,m->R_BP); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x4e*/ +static void i86op_dec_SI(PC_ENV *m) +{ + m->R_SI = dec_word(m,m->R_SI); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x4f*/ +static void i86op_dec_DI(PC_ENV *m) +{ + m->R_DI = dec_word(m,m->R_DI); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x50*/ +static void i86op_push_AX(PC_ENV *m) +{ + push_word(m,m->R_AX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x51*/ +static void i86op_push_CX(PC_ENV *m) +{ + push_word(m,m->R_CX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x52*/ +static void i86op_push_DX(PC_ENV *m) +{ + push_word(m,m->R_DX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x53*/ +static void i86op_push_BX(PC_ENV *m) +{ + push_word(m,m->R_BX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x54*/ +static void i86op_push_SP(PC_ENV *m) +{ + /* .... Note this weirdness: One book I have access to + claims that the value pushed here is actually sp-2. I.e. + it decrements the stackpointer, and then pushes it. The 286 + I have does it this way. Changing this causes many problems.*/ + /* changed to push SP-2, since this *IS* how a 8088 does this */ + push_word(m,m->R_SP-2); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x55*/ +static void i86op_push_BP(PC_ENV *m) +{ + push_word(m,m->R_BP); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x56*/ +static void i86op_push_SI(PC_ENV *m) +{ + push_word(m,m->R_SI); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x57*/ +static void i86op_push_DI(PC_ENV *m) +{ + push_word(m,m->R_DI); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x58*/ +static void i86op_pop_AX(PC_ENV *m) +{ + m->R_AX = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x59*/ +static void i86op_pop_CX(PC_ENV *m) +{ + m->R_CX = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x5a*/ +static void i86op_pop_DX(PC_ENV *m) +{ + m->R_DX = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x5b*/ +static void i86op_pop_BX(PC_ENV *m) +{ + m->R_BX = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x5c*/ +static void i86op_pop_SP(PC_ENV *m) +{ + m->R_SP = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x5d*/ +static void i86op_pop_BP(PC_ENV *m) +{ + m->R_BP = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x5e*/ +static void i86op_pop_SI(PC_ENV *m) +{ + m->R_SI = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x5f*/ +static void i86op_pop_DI(PC_ENV *m) +{ + m->R_DI = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x60 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x61 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x62 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x63 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x64 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x65 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x66 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x67 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x68 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x69 ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x6a ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x6b ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x6c ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x6d ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x6e ILLEGAL OP, calls i86op_illegal_op() */ +/*opcode=0x6f ILLEGAL OP, calls i86op_illegal_op() */ + +/*opcode=0x70*/ +static void i86op_jump_near_O(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if overflow flag is set */ + offset = (int8) fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (ACCESS_FLAG(m,F_OF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x71*/ +static void i86op_jump_near_NO(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if overflow is not set */ + offset = (int8) fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (!ACCESS_FLAG(m,F_OF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x72*/ +static void i86op_jump_near_B(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if carry flag is set. */ + offset = (int8)fetch_byte_imm(m); /* sign extended ??? */ + target = (int16)(m->R_IP) + offset; + if (ACCESS_FLAG(m, F_CF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x73*/ +static void i86op_jump_near_NB(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if carry flag is clear. */ + offset = (int8)fetch_byte_imm(m); /* sign extended ??? */ + target = (int16)(m->R_IP) + offset; + if (!ACCESS_FLAG(m,F_CF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x74*/ +static void i86op_jump_near_Z(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if zero flag is set. */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (ACCESS_FLAG(m, F_ZF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x75*/ +static void i86op_jump_near_NZ(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if zero flag is clear. */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (!ACCESS_FLAG(m, F_ZF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x76*/ +static void i86op_jump_near_BE(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if carry flag is set or if the zero + flag is set. */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (ACCESS_FLAG(m,F_CF) || ACCESS_FLAG(m,F_ZF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x77*/ +static void i86op_jump_near_NBE(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if carry flag is clear and if the zero + flag is clear */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (!(ACCESS_FLAG(m,F_CF)||ACCESS_FLAG(m,F_ZF))) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x78*/ +static void i86op_jump_near_S(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if sign flag is set */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (ACCESS_FLAG(m,F_SF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x79*/ +static void i86op_jump_near_NS(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if sign flag is clear */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (!ACCESS_FLAG(m,F_SF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x7a*/ +static void i86op_jump_near_P(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if parity flag is set (even parity) */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (ACCESS_FLAG(m, F_PF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x7b*/ +static void i86op_jump_near_NP(PC_ENV *m) +{ + int8 offset; + uint16 target; + /* jump to byte offset if parity flag is clear (odd parity) */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + if (!ACCESS_FLAG(m, F_PF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/* JHH fixed till here... */ + +/*opcode=0x7c*/ +static void i86op_jump_near_L(PC_ENV *m) +{ + int8 offset; + uint16 target; + int sf,of; + /* jump to byte offset if sign flag not equal to overflow flag. */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + /* note: + * this is the simplest expression i could think of which + * expresses SF != OF. m->R_FLG&F_SF either equals x80 or x00. + * Similarly m->R_FLG&F_OF either equals x800 or x000. + * The former shifted right by 7 puts a 1 or 0 in bit 0. + * The latter shifter right by 11 puts a 1 or 0 in bit 0. + * if the two expressions are the same, i.e. equal, then + * a zero results from the xor. If they are not equal, + * then a 1 results, and the jump is taken. + */ + sf = ACCESS_FLAG(m,F_SF) != 0; + of = ACCESS_FLAG(m,F_OF) != 0; + /* was: if ( ((m->R_FLG & F_SF)>>7) ^ ((m->R_FLG & F_OF) >> 11))*/ + if (sf ^ of) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x7d*/ +static void i86op_jump_near_NL(PC_ENV *m) +{ + int8 offset; + uint16 target; + int sf,of; + /* jump to byte offset if sign flag not equal to overflow flag. */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + sf = ACCESS_FLAG(m,F_SF) != 0; + of = ACCESS_FLAG(m,F_OF) != 0; + /* note: inverse of above, but using == instead of xor. */ + /* was: if (((m->R_FLG & F_SF)>>7) == ((m->R_FLG & F_OF) >> 11))*/ + if (sf == of) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x7e*/ +static void i86op_jump_near_LE(PC_ENV *m) +{ + int8 offset; + uint16 target; + int sf,of; + /* jump to byte offset if sign flag not equal to overflow flag + or the zero flag is set */ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + sf = ACCESS_FLAG(m,F_SF) != 0; + of = ACCESS_FLAG(m,F_OF) != 0; + /* note: modification of JL */ + /* sf != of */ + /* was: if ((((m->R_FLG & F_SF)>>7) ^ ((m->R_FLG & F_OF) >> 11)) + || (m->R_FLG & F_ZF) ) */ + if ( (sf ^ of) || ACCESS_FLAG(m,F_ZF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x7f*/ +static void i86op_jump_near_NLE(PC_ENV *m) +{ + int8 offset; + uint16 target; + int sf,of; + /* jump to byte offset if sign flag equal to overflow flag. + and the zero flag is clear*/ + offset = (int8)fetch_byte_imm(m); + target = (int16)(m->R_IP) + offset; + sf = ACCESS_FLAG(m,F_SF) != 0; + of = ACCESS_FLAG(m,F_OF) != 0; + +/* if (((m->R_FLG & F_SF)>>7) == ((m->R_FLG & F_OF) >> 11) + && (!(m->R_FLG & F_ZF))) */ + if ( ( sf == of ) && !ACCESS_FLAG(m,F_ZF)) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +static uint8 (*opc80_byte_operation[])(PC_ENV *m,uint8 d,uint8 s) = +{ + add_byte,/*00*/ + or_byte, /*01*/ + adc_byte,/*02*/ + sbb_byte,/*03*/ + and_byte,/*04*/ + sub_byte,/*05*/ + xor_byte,/*06*/ + cmp_byte,/*07*/ +}; + +/*opcode=0x80*/ +static void i86op_opc80_byte_RM_IMM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg; + uint16 destoffset; + uint8 imm; + uint8 destval; + /* weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imm = fetch_byte_imm(m); + destval = (*opc80_byte_operation[rh])(m, destval, imm); + if (rh != 7) store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imm = fetch_byte_imm(m); + destval = (*opc80_byte_operation[rh])(m, destval, imm); + if (rh != 7) store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imm = fetch_byte_imm(m); + destval = (*opc80_byte_operation[rh])(m, destval, imm); + if (rh != 7) store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + imm = fetch_byte_imm(m); + destval = (*opc80_byte_operation[rh])(m, *destreg, imm); + if (rh != 7) *destreg = destval; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +static uint16 (*opc81_word_operation[])(PC_ENV *m,uint16 d,uint16 s) = +{ add_word,/*00*/ + or_word, /*01*/ + adc_word,/*02*/ + sbb_word,/*03*/ + and_word,/*04*/ + sub_word,/*05*/ + xor_word,/*06*/ + cmp_word,/*07*/ +}; + +/*opcode=0x81*/ +static void i86op_opc81_word_RM_IMM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg; + uint16 destoffset; + uint16 imm; + uint16 destval; + /* weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + imm = fetch_word_imm(m); + destval = (*opc81_word_operation[rh])(m, destval, imm); + if (rh != 7) store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + imm = fetch_word_imm(m); + destval = (*opc81_word_operation[rh])(m, destval, imm); + if (rh != 7) store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + imm = fetch_word_imm(m); + destval = (*opc81_word_operation[rh])(m, destval, imm); + if (rh != 7) store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + imm = fetch_word_imm(m); + destval = (*opc81_word_operation[rh])(m, *destreg, imm); + if (rh != 7) *destreg = destval; + break; + } + DECODE_CLEAR_SEGOVR(m); + } + +static uint8 (*opc82_byte_operation[])(PC_ENV *m,uint8 s,uint8 d) = +{ + add_byte,/*00*/ + or_byte, /*01*/ /*YYY UNUSED ????*/ + adc_byte,/*02*/ + sbb_byte,/*03*/ + and_byte,/*04*/ /*YYY UNUSED ????*/ + sub_byte,/*05*/ + xor_byte,/*06*/ /*YYY UNUSED ????*/ + cmp_byte,/*07*/ +}; + +/*opcode=0x82*/ +static void i86op_opc82_byte_RM_IMM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg; + uint16 destoffset; + uint8 imm; + uint8 destval; + /* weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + Similar to opcode 81, except that the immediate byte + is sign extended to a word length. + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imm = fetch_byte_imm(m); + destval = (*opc82_byte_operation[rh])(m, destval, imm); + if (rh != 7) store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imm = fetch_byte_imm(m); + destval = (*opc82_byte_operation[rh])(m, destval, imm); + if (rh != 7) store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imm = fetch_byte_imm(m); + destval = (*opc82_byte_operation[rh])(m, destval, imm); + if (rh != 7) store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + imm = fetch_byte_imm(m); + destval = (*opc82_byte_operation[rh])(m, *destreg, imm); + if (rh != 7) *destreg = destval; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +static uint16 (*opc83_word_operation[])(PC_ENV *m,uint16 s,uint16 d) = +{ + add_word,/*00*/ + or_word, /*01*/ /*YYY UNUSED ????*/ + adc_word,/*02*/ + sbb_word,/*03*/ + and_word,/*04*/ /*YYY UNUSED ????*/ + sub_word,/*05*/ + xor_word,/*06*/ /*YYY UNUSED ????*/ + cmp_word,/*07*/ +}; + +/*opcode=0x83*/ +static void i86op_opc83_word_RM_IMM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg; + uint16 destoffset; + uint16 imm; + uint16 destval; + /* weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + Similar to opcode 81, except that the immediate byte + is sign extended to a word length. + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + imm = (int8)fetch_byte_imm(m); + destval = (*opc83_word_operation[rh])(m, destval, imm); + if (rh != 7) store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + imm = (int8)fetch_byte_imm(m); + destval = (*opc83_word_operation[rh])(m, destval, imm); + if (rh != 7) store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + imm = (int8) fetch_byte_imm(m); + destval = (*opc83_word_operation[rh])(m, destval, imm); + if (rh != 7) store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + imm = (int8) fetch_byte_imm(m); + destval = (*opc83_word_operation[rh])(m, *destreg, imm); + if (rh != 7) *destreg = destval; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x84*/ +static void i86op_test_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + test_byte(m, destval, *srcreg); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + test_byte(m, destval, *srcreg); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + test_byte(m, destval, *srcreg); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + test_byte(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); + } + +/*opcode=0x85*/ +static void i86op_test_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + test_word(m, destval, *srcreg); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + test_word(m, destval, *srcreg); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + test_word(m, destval, *srcreg); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + test_word(m, *destreg, *srcreg); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x86*/ +static void i86op_xchg_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + uint8 destval; + uint8 tmp; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = *destreg; + *destreg = tmp; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x87*/ +static void i86op_xchg_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + uint16 tmp; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = destval; + destval = tmp; + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + tmp = *srcreg; + *srcreg = *destreg; + *destreg = tmp; + break; + } + DECODE_CLEAR_SEGOVR(m); + } + +/*opcode=0x88*/ +static void i86op_mov_byte_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 destoffset; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + store_data_byte(m,destoffset,*srcreg); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + store_data_byte(m,destoffset,*srcreg); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + store_data_byte(m,destoffset,*srcreg); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + srcreg = DECODE_RM_BYTE_REGISTER(m,rh); + *destreg = *srcreg; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x89*/ +static void i86op_mov_word_RM_R(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + store_data_word(m,destoffset,*srcreg); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + store_data_word(m,destoffset,*srcreg); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + store_data_word(m,destoffset,*srcreg); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + *destreg = *srcreg; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x8a*/ +static void i86op_mov_byte_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg,*srcreg; + uint16 srcoffset; + uint8 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = srcval; + break; + case 1: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = srcval; + break; + case 2: + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_byte(m,srcoffset); + *destreg = srcval; + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rh); + srcreg = DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = *srcreg; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + + /*opcode=0x8b*/ +static void i86op_mov_word_R_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = srcval; + break; + case 1: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = srcval; + break; + case 2: + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = srcval; + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = *srcreg; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x8c*/ +static void i86op_mov_word_RM_SR(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + srcreg = decode_rm_seg_register(m,rh); + destval = *srcreg; + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + srcreg = decode_rm_seg_register(m,rh); + destval = *srcreg; + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + srcreg = decode_rm_seg_register(m,rh); + destval = *srcreg; + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + srcreg = decode_rm_seg_register(m,rh); + *destreg = *srcreg; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x8d*/ +static void i86op_lea_word_R_M(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *srcreg; + uint16 destoffset; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destoffset=decode_rm00_address(m,rl); + *srcreg = destoffset; + break; + case 1: + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destoffset=decode_rm01_address(m,rl); + *srcreg = destoffset; + break; + case 2: + srcreg = DECODE_RM_WORD_REGISTER(m,rh); + destoffset=decode_rm10_address(m,rl); + *srcreg = destoffset; + break; + case 3: /* register to register */ + /* undefined. Do nothing. */ + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x8e*/ +static void i86op_mov_word_SR_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg,*srcreg; + uint16 srcoffset; + uint16 srcval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destreg = decode_rm_seg_register(m,rh); + srcoffset=decode_rm00_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = srcval; + break; + case 1: + destreg = decode_rm_seg_register(m,rh); + srcoffset=decode_rm01_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = srcval; + break; + case 2: + destreg = decode_rm_seg_register(m,rh); + srcoffset = decode_rm10_address(m,rl); + srcval = fetch_data_word(m,srcoffset); + *destreg = srcval; + break; + case 3: /* register to register */ + destreg = decode_rm_seg_register(m,rh); + srcreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = *srcreg; + break; + } + /*\ + * clean up, and reset all the R_xSP pointers to the correct + * locations. This is about 3x too much overhead (doing all the + * segreg ptrs when only one is needed, but this instruction + * *cannot* be that common, and this isn't too much work anyway. + \*/ + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x8f*/ +static void i86op_pop_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg; + uint16 destoffset; + uint16 destval; + FETCH_DECODE_MODRM(m,mod,rh,rl); + if (rh != 0) + { + halt_sys(m); + } + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = pop_word( m); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = pop_word(m); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = pop_word(m); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = pop_word(m); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x90*/ +static void i86op_nop(PC_ENV *m) +{ + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x91*/ +static void i86op_xchg_word_AX_CX(PC_ENV *m) +{ + uint16 tmp; + tmp = m->R_AX; + m->R_AX = m->R_CX; + m->R_CX = tmp; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x92*/ +static void i86op_xchg_word_AX_DX(PC_ENV *m) +{ + uint16 tmp; + tmp = m->R_AX; + m->R_AX = m->R_DX; + m->R_DX = tmp; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x93*/ +static void i86op_xchg_word_AX_BX(PC_ENV *m) +{ + uint16 tmp; + tmp = m->R_AX; + m->R_AX = m->R_BX; + m->R_BX = tmp; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x94*/ +static void i86op_xchg_word_AX_SP(PC_ENV *m) +{ + uint16 tmp; + tmp = m->R_AX; + m->R_AX = m->R_SP; + m->R_SP = tmp; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x95*/ +static void i86op_xchg_word_AX_BP(PC_ENV *m) +{ + uint16 tmp; + tmp = m->R_AX; + m->R_AX = m->R_BP; + m->R_BP = tmp; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x96*/ +static void i86op_xchg_word_AX_SI(PC_ENV *m) +{ + uint16 tmp; + tmp = m->R_AX; + m->R_AX = m->R_SI; + m->R_SI = tmp; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x97*/ +static void i86op_xchg_word_AX_DI(PC_ENV *m) +{ + uint16 tmp; + tmp = m->R_AX; + m->R_AX = m->R_DI; + m->R_DI = tmp; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x98*/ +static void i86op_cbw(PC_ENV *m) +{ + if (m->R_AL & 0x80) + { + m->R_AH = 0xff; + } + else + { + m->R_AH = 0x0; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x99*/ +static void i86op_cwd(PC_ENV *m) +{ + if (m->R_AX & 0x8000) + { + m->R_DX = 0xffff; + } + else + { + m->R_DX = 0x0; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x9a*/ +static void i86op_call_far_IMM(PC_ENV *m) +{ + uint16 farseg,faroff; + faroff = fetch_word_imm(m); + farseg = fetch_word_imm(m); + /* XXX + HOOKED INTERRUPT VECTORS CALLING INTO OUR "BIOS" + WILL CAUSE PROBLEMS UNLESS ALL INTERSEGMENT STUFF IS + CHECKED FOR BIOS ACCESS. CHECK NEEDED HERE. + FOR MOMENT, LET IT ALONE. + */ + push_word(m,m->R_CS); + m->R_CS = farseg; + push_word(m,m->R_IP); + m->R_IP = faroff; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x9b*/ +static void i86op_wait(PC_ENV *m) +{ + /* NADA. */ + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x9c*/ +static void i86op_pushf_word(PC_ENV *m) +{ + uint16 flags; + flags = m->R_FLG; + /* clear out *all* bits not representing flags */ + flags &= F_MSK; + /* TURN ON CHARACTERISTIC BITS OF FLAG FOR 8088 */ + flags |= F_ALWAYS_ON; + push_word(m,flags); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x9d*/ +static void i86op_popf_word(PC_ENV *m) +{ + m->R_FLG = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x9e*/ +static void i86op_sahf(PC_ENV *m) +{ + /* clear the lower bits of the flag register */ + m->R_FLG &= 0xffffff00; + /* or in the AH register into the flags register */ + m->R_FLG |= m->R_AH; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0x9f*/ +static void i86op_lahf(PC_ENV *m) +{ + m->R_AH = m->R_FLG & 0xff; + /*undocumented TC++ behavior??? Nope. It's documented, but + you have too look real hard to notice it. */ + m->R_AH |= 0x2; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa0*/ +static void i86op_mov_AL_M_IMM(PC_ENV *m) +{ + uint16 offset; + uint8 destval; + offset = fetch_word_imm(m); + destval = fetch_data_byte(m,offset); + m->R_AL = destval; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa1*/ +static void i86op_mov_AX_M_IMM(PC_ENV *m) +{ + uint16 offset; + uint16 destval; + offset = fetch_word_imm(m); + destval = fetch_data_word(m,offset); + m->R_AX = destval; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa2*/ +static void i86op_mov_M_AL_IMM(PC_ENV *m) +{ + uint16 offset; + offset = fetch_word_imm(m); + store_data_byte(m,offset,m->R_AL); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa3*/ +static void i86op_mov_M_AX_IMM(PC_ENV *m) +{ + uint16 offset; + offset = fetch_word_imm(m); + store_data_word(m,offset,m->R_AX); + DECODE_CLEAR_SEGOVR(m); +} + +/* JHH CLEANED */ + +/*opcode=0xa4*/ +static void i86op_movs_byte(PC_ENV *m) +{ + uint8 val; + int inc; + if (ACCESS_FLAG(m,F_DF)) /* down */ + inc = -1; + else + inc = 1; + if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) + { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val = fetch_data_byte(m,m->R_SI); + store_data_byte_abs(m,m->R_ES,m->R_DI,val); + m->R_CX -= 1; + m->R_SI += inc; + m->R_DI += inc; + } + m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + else + { + val = fetch_data_byte(m,m->R_SI); + store_data_byte_abs(m,m->R_ES,m->R_DI,val); + m->R_SI += inc; + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa5*/ +static void i86op_movs_word(PC_ENV *m) +{ + int16 val; + int inc; + if (ACCESS_FLAG(m, F_DF)) /* down */ + inc = -2; + else + inc = 2; + if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) + { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val = fetch_data_word(m,m->R_SI); + store_data_word_abs(m,m->R_ES,m->R_DI,val); + m->R_CX -= 1; + m->R_SI += inc; + m->R_DI += inc; + } + m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + else + { + val = fetch_data_word(m,m->R_SI); + store_data_word_abs(m,m->R_ES,m->R_DI,val); + m->R_SI += inc; + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa6*/ +static void i86op_cmps_byte(PC_ENV *m) +{ + int8 val1,val2; + int inc; + if (ACCESS_FLAG(m,F_DF)) /* down */ + inc = -1; + else + inc = 1; + if (m->sysmode & SYSMODE_PREFIX_REPE) + { + /* REPE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val1 = fetch_data_byte(m,m->R_SI); + val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); + cmp_byte(m, val1,val2); + m->R_CX -= 1; + m->R_SI += inc; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)==0) break; + } + m->sysmode &= ~SYSMODE_PREFIX_REPE; + } + else if (m->sysmode & SYSMODE_PREFIX_REPNE) + { + /* REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val1 = fetch_data_byte(m,m->R_SI); + val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); + cmp_byte(m, val1,val2); + m->R_CX -= 1; + m->R_SI += inc; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ + } + m->sysmode &= ~SYSMODE_PREFIX_REPNE; + } + else + { + val1 = fetch_data_byte(m,m->R_SI); + val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); + cmp_byte(m, val1,val2); + m->R_SI += inc; + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa7*/ +static void i86op_cmps_word(PC_ENV *m) +{ + int16 val1,val2; + int inc; + if (ACCESS_FLAG(m,F_DF)) /* down */ + inc = -2; + else + inc = 2; + if (m->sysmode & SYSMODE_PREFIX_REPE) + { + /* REPE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val1 = fetch_data_word(m,m->R_SI); + val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); + cmp_word(m, val1,val2); + m->R_CX -= 1; + m->R_SI += inc; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)==0) break; + } + m->sysmode &= ~SYSMODE_PREFIX_REPE; + } + else if (m->sysmode & SYSMODE_PREFIX_REPNE) + { + /* REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val1 = fetch_data_word(m,m->R_SI); + val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); + cmp_word(m, val1,val2); + m->R_CX -= 1; + m->R_SI += inc; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ + } + m->sysmode &= ~SYSMODE_PREFIX_REPNE; + } + else + { + val1 = fetch_data_word(m,m->R_SI); + val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); + cmp_word(m, val1,val2); + m->R_SI += inc; + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xa8*/ +static void i86op_test_AL_IMM(PC_ENV *m) +{ + int imm; + imm = fetch_byte_imm(m); + test_byte(m, m->R_AL, imm); + DECODE_CLEAR_SEGOVR(m); + } + +/*opcode=0xa9*/ +static void i86op_test_AX_IMM(PC_ENV *m) +{ + int imm; + imm = fetch_word_imm(m); + test_word(m, m->R_AX, imm); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xaa*/ +static void i86op_stos_byte(PC_ENV *m) +{ + int inc; + if (ACCESS_FLAG(m, F_DF)) /* down */ + inc = -1; + else + inc = 1; + if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) + { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + store_data_byte_abs(m,m->R_ES,m->R_DI,m->R_AL); + m->R_CX -= 1; + m->R_DI += inc; + } + m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + else + { + store_data_byte_abs(m,m->R_ES,m->R_DI,m->R_AL); + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xab*/ +static void i86op_stos_word(PC_ENV *m) +{ + int inc; + if (ACCESS_FLAG(m, F_DF)) /* down */ + inc = -2; + else + inc = 2; + if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) + { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + store_data_word_abs(m,m->R_ES,m->R_DI,m->R_AX); + m->R_CX -= 1; + m->R_DI += inc; + } + m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + else + { + store_data_word_abs(m,m->R_ES,m->R_DI,m->R_AX); + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xac*/ +static void i86op_lods_byte(PC_ENV *m) +{ + int inc; + if (ACCESS_FLAG(m,F_DF)) /* down */ + inc = -1; + else + inc = 1; + if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) + { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + m->R_AL = fetch_data_byte(m,m->R_SI); + m->R_CX -= 1; + m->R_SI += inc; + } + m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + else + { + m->R_AL = fetch_data_byte(m,m->R_SI); + m->R_SI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xad*/ +static void i86op_lods_word(PC_ENV *m) +{ + int inc; + if (ACCESS_FLAG(m,F_DF)) /* down */ + inc = -2; + else + inc = 2; + if (m->sysmode & (SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE)) + { + /* dont care whether REPE or REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + m->R_AX = fetch_data_word(m,m->R_SI); + m->R_CX -= 1; + m->R_SI += inc; + } + m->sysmode &= ~(SYSMODE_PREFIX_REPE | SYSMODE_PREFIX_REPNE); + } + else + { + m->R_AX = fetch_data_word(m,m->R_SI); + m->R_SI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xae*/ +static void i86op_scas_byte(PC_ENV *m) +{ + int8 val2; + int inc; + if (ACCESS_FLAG(m,F_DF)) /* down */ + inc = -1; + else + inc = 1; + if (m->sysmode & SYSMODE_PREFIX_REPE) + { + /* REPE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); + cmp_byte(m, m->R_AL,val2); + m->R_CX -= 1; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)==0) break; + } + m->sysmode &= ~SYSMODE_PREFIX_REPE; + } + else if (m->sysmode & SYSMODE_PREFIX_REPNE) + { + /* REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); + cmp_byte(m, m->R_AL,val2); + m->R_CX -= 1; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ + } + m->sysmode &= ~SYSMODE_PREFIX_REPNE; + } + else + { + val2 = fetch_data_byte_abs(m,m->R_ES,m->R_DI); + cmp_byte(m, m->R_AL,val2); + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xaf*/ +static void i86op_scas_word(PC_ENV *m) +{ + int16 val2; + int inc; + if (ACCESS_FLAG(m, F_DF)) /* down */ + inc = -2; + else + inc = 2; + if (m->sysmode & SYSMODE_PREFIX_REPE) + { + /* REPE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); + cmp_word(m,m->R_AX,val2); + m->R_CX -= 1; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)==0) break; + } + m->sysmode &= ~SYSMODE_PREFIX_REPE; + } + else if (m->sysmode & SYSMODE_PREFIX_REPNE) + { + /* REPNE */ + /* move them until CX is ZERO. */ + while (m->R_CX != 0) + { + val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); + cmp_word(m, m->R_AX,val2); + m->R_CX -= 1; + m->R_DI += inc; + if (ACCESS_FLAG(m,F_ZF)) break; /* zero flag set means equal */ + } + m->sysmode &= ~SYSMODE_PREFIX_REPNE; + } + else + { + val2 = fetch_data_word_abs(m,m->R_ES,m->R_DI); + cmp_word(m, m->R_AX,val2); + m->R_DI += inc; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb0*/ +static void i86op_mov_byte_AL_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_AL = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb1*/ +static void i86op_mov_byte_CL_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_CL = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb2*/ +static void i86op_mov_byte_DL_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_DL = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb3*/ +static void i86op_mov_byte_BL_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_BL = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb4*/ +static void i86op_mov_byte_AH_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_AH = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb5*/ +static void i86op_mov_byte_CH_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_CH = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb6*/ +static void i86op_mov_byte_DH_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_DH = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb7*/ +static void i86op_mov_byte_BH_IMM(PC_ENV *m) +{ + uint8 imm; + imm = fetch_byte_imm(m); + m->R_BH = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb8*/ +static void i86op_mov_word_AX_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_AX = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xb9*/ +static void i86op_mov_word_CX_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_CX = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xba*/ +static void i86op_mov_word_DX_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_DX = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xbb*/ +static void i86op_mov_word_BX_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_BX = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xbc*/ +static void i86op_mov_word_SP_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_SP = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xbd*/ +static void i86op_mov_word_BP_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_BP = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xbe*/ +static void i86op_mov_word_SI_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_SI = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xbf*/ +static void i86op_mov_word_DI_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_DI = imm; + DECODE_CLEAR_SEGOVR(m); +} + +/* c0 === ILLEGAL OPERAND */ +/* c1 === ILLEGAL OPERAND */ + +/*opcode=0xc2*/ +static void i86op_ret_near_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_IP = pop_word(m); + m->R_SP += imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xc3*/ +static void i86op_ret_near(PC_ENV *m) +{ + m->R_IP = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xc4*/ +static void i86op_les_R_IMM(PC_ENV *m) +{ + uint16 mod,rh,rl; + uint16 *dstreg; + uint16 srcoffset; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + dstreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + *dstreg = fetch_data_word(m,srcoffset); + m->R_ES = fetch_data_word(m,srcoffset+2); + break; + case 1: + dstreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + *dstreg = fetch_data_word(m,srcoffset); + m->R_ES = fetch_data_word(m,srcoffset+2); + break; + case 2: + dstreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + *dstreg = fetch_data_word(m,srcoffset); + m->R_ES = fetch_data_word(m,srcoffset+2); + break; + case 3: /* register to register */ + /* UNDEFINED! */ + ; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xc5*/ +static void i86op_lds_R_IMM(PC_ENV *m) +{ + uint16 mod,rh,rl; + uint16 *dstreg; + uint16 srcoffset; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + dstreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm00_address(m,rl); + *dstreg = fetch_data_word(m,srcoffset); + m->R_DS = fetch_data_word(m,srcoffset+2); + break; + case 1: + dstreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm01_address(m,rl); + *dstreg = fetch_data_word(m,srcoffset); + m->R_DS = fetch_data_word(m,srcoffset+2); + break; + case 2: + dstreg = DECODE_RM_WORD_REGISTER(m,rh); + srcoffset=decode_rm10_address(m,rl); + *dstreg = fetch_data_word(m,srcoffset); + m->R_DS = fetch_data_word(m,srcoffset+2); + break; + case 3: /* register to register */ + /* UNDEFINED! */ + ; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xc6*/ +static void i86op_mov_byte_RM_IMM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg; + uint16 destoffset; + uint8 imm; + FETCH_DECODE_MODRM(m,mod,rh,rl); + if (rh != 0) + { + halt_sys(m); + } + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + imm = fetch_byte_imm(m); + store_data_byte(m,destoffset,imm); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + imm = fetch_byte_imm(m); + store_data_byte(m,destoffset,imm); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + imm = fetch_byte_imm(m); + store_data_byte(m,destoffset,imm); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + imm = fetch_byte_imm(m); + *destreg = imm; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xc7*/ +static void i86op_mov_word_RM_IMM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg; + uint16 destoffset; + uint16 imm; + FETCH_DECODE_MODRM(m,mod,rh,rl); + if (rh != 0) + { + halt_sys(m); + } + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + imm = fetch_word_imm(m); + store_data_word(m,destoffset,imm); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + imm = fetch_word_imm(m); + store_data_word(m,destoffset,imm); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + imm = fetch_word_imm(m); + store_data_word(m,destoffset,imm); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + imm = fetch_word_imm(m); + *destreg = imm; + break; + } + DECODE_CLEAR_SEGOVR(m); + } + +/*opcode=0xc8 ILLEGAL OP*/ +/*opcode=0xc9 ILLEGAL OP*/ + +/*opcode=0xca*/ +static void i86op_ret_far_IMM(PC_ENV *m) +{ + uint16 imm; + imm = fetch_word_imm(m); + m->R_IP = pop_word(m); + m->R_CS = pop_word(m); + m->R_SP += imm; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xcb*/ +static void i86op_ret_far(PC_ENV *m) +{ + m->R_IP = pop_word(m); + m->R_CS = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xcc*/ +static void i86op_int3(PC_ENV *m) +{ + uint16 tmp; + tmp = (uint16) mem_access_word(m, 3 * 4); + /* access the segment register */ + { + tmp = m->R_FLG; + push_word(m, tmp); + CLEAR_FLAG(m, F_IF); + CLEAR_FLAG(m, F_TF); +/* [JCE] If we're interrupting between a segment override (or REP override) + * and the following instruction, decrease IP to get back to the prefix */ + if (m->sysmode & (SYSMODE_SEGMASK | + SYSMODE_PREFIX_REPE | + SYSMODE_PREFIX_REPNE)) + { + --m->R_IP; + } + push_word(m, m->R_CS); + push_word(m, m->R_IP); +/* [JCE] CS and IP were the wrong way round... */ + tmp = mem_access_word(m, 3 * 4); + m->R_IP = tmp; + tmp = mem_access_word(m, 3 * 4 + 2); + m->R_CS = tmp; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xcd*/ +static void i86op_int_IMM(PC_ENV *m) +{ + uint16 tmp; + uint8 intnum; + intnum = fetch_byte_imm(m); + tmp = mem_access_word(m, intnum * 4); + { + tmp = m->R_FLG; + push_word(m, tmp); + CLEAR_FLAG(m, F_IF); + CLEAR_FLAG(m, F_TF); +/* [JCE] If we're interrupting between a segment override (or REP override) + * and the following instruction, decrease IP to get back to the prefix */ + if (m->sysmode & (SYSMODE_SEGMASK | + SYSMODE_PREFIX_REPE | + SYSMODE_PREFIX_REPNE)) + { + --m->R_IP; + } + push_word(m, m->R_CS); + push_word(m, m->R_IP); +/* [JCE] CS and IP were the wrong way round... */ + tmp = mem_access_word(m, intnum * 4); + m->R_IP = tmp; + tmp = mem_access_word(m, intnum * 4 + 2); + m->R_CS = tmp; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xce*/ +static void i86op_into(PC_ENV *m) +{ + uint16 tmp; + if (ACCESS_FLAG(m,F_OF)) + { + tmp = mem_access_word(m, 4 * 4); + { + tmp = m->R_FLG; + push_word(m, tmp); + CLEAR_FLAG(m, F_IF); + CLEAR_FLAG(m, F_TF); +/* [JCE] If we're interrupting between a segment override (or REP override) + * and the following instruction, decrease IP to get back to the prefix */ + if (m->sysmode & (SYSMODE_SEGMASK | + SYSMODE_PREFIX_REPE | + SYSMODE_PREFIX_REPNE)) + { + --m->R_IP; + } + push_word(m, m->R_CS); + push_word(m, m->R_IP); +/* [JCE] CS and IP were the wrong way round... */ + tmp = mem_access_word(m, 4 * 4); + m->R_IP = tmp; + tmp = mem_access_word(m, 4 * 4 + 2); + m->R_CS = tmp; + } + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xcf*/ +static void i86op_iret(PC_ENV *m) +{ + m->R_IP = pop_word(m); + m->R_CS = pop_word(m); + m->R_FLG = pop_word(m); + DECODE_CLEAR_SEGOVR(m); +} + +static uint8 (*opcD0_byte_operation[])(PC_ENV *m,uint8 d, uint8 s) = + /* used by opcodes d0 and d2. */ +{ + rol_byte, + ror_byte, + rcl_byte, + rcr_byte, + shl_byte, + shr_byte, + shl_byte, /* sal_byte === shl_byte by definition */ + sar_byte, +}; + +/* opcode=0xd0*/ +static void i86op_opcD0_byte_RM_1(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg; + uint16 destoffset; + uint8 destval; + /* Yet another weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = (*opcD0_byte_operation[rh])(m, destval,1); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = (*opcD0_byte_operation[rh])(m, destval, 1); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = (*opcD0_byte_operation[rh])(m, destval, 1); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + destval = (*opcD0_byte_operation[rh])(m, *destreg, 1); + *destreg = destval; + break; + } + DECODE_CLEAR_SEGOVR(m); + } + +static uint16 (*opcD1_word_operation[])(PC_ENV *m,uint16 s,uint16 d) = + /* used by opcodes d1 and d3. */ +{ rol_word, + ror_word, + rcl_word, + rcr_word, + shl_word, + shr_word, + shl_word, /* sal_byte === shl_byte by definition */ + sar_word, +}; + +/* opcode=0xd1*/ +static void i86op_opcD1_word_RM_1(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg; + uint16 destoffset; + uint16 destval; + /* Yet another weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = (*opcD1_word_operation[rh])(m, destval,1); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = (*opcD1_word_operation[rh])(m, destval, 1); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = (*opcD1_word_operation[rh])(m, destval, 1); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + destval = (*opcD1_word_operation[rh])(m, *destreg, 1); + *destreg = destval; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xd2*/ +static void i86op_opcD2_byte_RM_CL(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg; + uint16 destoffset; + uint8 destval; + uint8 amt; + /* Yet another weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + amt = m->R_CL; + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = (*opcD0_byte_operation[rh])(m, destval,amt); + store_data_byte(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = (*opcD0_byte_operation[rh])(m, destval, amt); + store_data_byte(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = (*opcD0_byte_operation[rh])(m, destval, amt); + store_data_byte(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + destval = (*opcD0_byte_operation[rh])(m, *destreg, amt); + *destreg = destval; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xd3*/ +static void i86op_opcD3_word_RM_CL(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg; + uint16 destoffset; + uint16 destval; + uint8 amt; + /* Yet another weirdo special case instruction format. Part of the + opcode held below in "RH". Doubly nested case would + result, except that the decoded instruction + */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + amt = m->R_CL; + /* know operation, decode the mod byte to find the addressing + mode. */ + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = (*opcD1_word_operation[rh])(m, destval, amt); + store_data_word(m,destoffset,destval); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = (*opcD1_word_operation[rh])(m, destval, amt); + store_data_word(m,destoffset,destval); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = (*opcD1_word_operation[rh])(m, destval, amt); + store_data_word(m,destoffset,destval); + break; + case 3: /* register to register */ + destreg = DECODE_RM_WORD_REGISTER(m,rl); + *destreg = (*opcD1_word_operation[rh])(m, *destreg, amt); + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +static void sys_fatal(int error, char *fmt, ...) +{ + va_list p; + va_start(p, fmt); + fprintf(stderr, "Fatal error: "); + if (error != 0) + { + fprintf(stderr, "<%d>",error); + fprintf(stderr,"%s",strerror(error)); + } + vfprintf(stderr, fmt, p); + va_end(p); + fprintf(stderr, NLP "Exiting..." NLP); + exit(1); +} + +/* opcode=0xd4*/ +static void i86op_aam(PC_ENV *m) +{ uint8 a; + a = fetch_byte_imm(m); /* this is a stupid encoding. */ + if (a != 10) sys_fatal(0,"error decoding aam" NLP); + /* note the type change here --- returning AL and AH in AX. */ + m->R_AX = aam_word(m,m->R_AL); + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xd5*/ +static void i86op_aad(PC_ENV *m) +{ uint8 a; + a = fetch_byte_imm(m); + m->R_AX = aad_word(m,m->R_AX); + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xd6 ILLEGAL OPCODE */ + +/* opcode=0xd7 */ +static void i86op_xlat(PC_ENV *m) +{ + uint16 addr; + addr = m->R_BX + (uint8)m->R_AL; + m->R_AL = fetch_data_byte(m,addr); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe0*/ +static void i86op_loopne(PC_ENV *m) +{ + int16 ip; + ip = (int8)fetch_byte_imm(m); + ip += (int16)m->R_IP; + m->R_CX -= 1; + if (m->R_CX != 0 && !ACCESS_FLAG(m,F_ZF)) /* CX != 0 and !ZF */ + m->R_IP = ip; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe1*/ +static void i86op_loope(PC_ENV *m) +{ + int16 ip; + ip = (int8)fetch_byte_imm(m); + ip += (int16)m->R_IP; + m->R_CX -= 1; + if (m->R_CX != 0 && ACCESS_FLAG(m,F_ZF)) /* CX != 0 and ZF */ + m->R_IP = ip; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe2*/ +static void i86op_loop(PC_ENV *m) +{ + int16 ip; + ip = (int8)fetch_byte_imm(m); + ip += (int16)m->R_IP; + m->R_CX -= 1; + if (m->R_CX != 0) + m->R_IP = ip; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe3*/ +static void i86op_jcxz(PC_ENV *m) +{ + int16 offset,target; + /* jump to byte offset if overflow flag is set */ + offset = (int8)fetch_byte_imm(m); /* sign extended ??? */ + target = (int16)m->R_IP + offset; + if (m->R_CX == 0) + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe4*/ +static void i86op_in_byte_AL_IMM(PC_ENV *m) +{ + uint8 port; + port = (uint8)fetch_byte_imm(m); + m->R_AL = in(port); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe5*/ +static void i86op_in_word_AX_IMM(PC_ENV *m) +{ + uint8 port; + port = (uint8)fetch_byte_imm(m); + m->R_AX = in(port); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe6*/ +static void i86op_out_byte_IMM_AL(PC_ENV *m) +{ + uint8 port; + port = (uint8)fetch_byte_imm(m); + out(port, m->R_AL); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe7*/ +static void i86op_out_word_IMM_AX(PC_ENV *m) +{ + uint8 port; + port = (uint8)fetch_byte_imm(m); + out(port, m->R_AX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe8*/ +static void i86op_call_near_IMM(PC_ENV *m) +{ + int16 ip; + /* weird. Thought this was a signed disp! */ + ip = (int16)fetch_word_imm(m); + ip += (int16)m->R_IP; /* CHECK SIGN */ + push_word(m,m->R_IP); + m->R_IP = ip; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xe9*/ +static void i86op_jump_near_IMM(PC_ENV *m) +{ + int ip; + /* weird. Thought this was a signed disp too! */ + ip = (int16)fetch_word_imm(m); + ip += (int16)m->R_IP; /* CHECK SIGN */ + m->R_IP = ip; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xea*/ +static void i86op_jump_far_IMM(PC_ENV *m) +{ + uint16 cs,ip; + ip = fetch_word_imm(m); + cs = fetch_word_imm(m); + m->R_IP = ip; + m->R_CS = cs; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xeb*/ +static void i86op_jump_byte_IMM(PC_ENV *m) +{ + int8 offset; + uint16 target; + offset = (int8) fetch_byte_imm(m); /* CHECK */ +/* printf("jump byte imm offset=%d\n",offset);*/ + target = (int16) m->R_IP + offset; + m->R_IP = target; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xec*/ +static void i86op_in_byte_AL_DX(PC_ENV *m) +{ + m->R_AL = in(m->R_DX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xed*/ +static void i86op_in_word_AX_DX(PC_ENV *m) +{ + m->R_AX = in(m->R_DX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xee*/ +static void i86op_out_byte_DX_AL(PC_ENV *m) +{ + out(m->R_DX, m->R_AL); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xef*/ +static void i86op_out_word_DX_AX(PC_ENV *m) +{ + out(m->R_DX, m->R_AX); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf0*/ +static void i86op_lock(PC_ENV *m) +{ + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf1 ILLEGAL OPERATION*/ + +/*opcode=0xf2*/ +static void i86op_repne(PC_ENV *m) +{ + m->sysmode |= SYSMODE_PREFIX_REPNE; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf3*/ +static void i86op_repe(PC_ENV *m) +{ + m->sysmode |= SYSMODE_PREFIX_REPE; + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf4*/ +static void i86op_halt(PC_ENV *m) +{ + halt_sys(m); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf5*/ +static void i86op_cmc(PC_ENV *m) +{ + /* complement the carry flag. */ + TOGGLE_FLAG(m,F_CF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf6*/ +static void i86op_opcF6_byte_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint8 *destreg; + uint16 destoffset; + uint8 destval,srcval; + /* long, drawn out code follows. Double switch for a total + of 32 cases. */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: /* mod=00 */ + switch(rh) + { + case 0: /* test byte imm */ + destoffset=decode_rm00_address(m,rl); + srcval = fetch_byte_imm(m); + destval = fetch_data_byte(m,destoffset); + test_byte(m, destval, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = not_byte(m, destval); + store_data_byte(m,destoffset,destval); + break; + case 3: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = neg_byte(m, destval); + store_data_byte(m,destoffset,destval); + break; + case 4: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + mul_byte(m, destval); + break; + case 5: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imul_byte(m, destval); + break; + case 6: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + div_byte(m, destval); + break; + case 7: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_byte(m,destoffset); + idiv_byte(m, destval); + break; + } + break; /* end mod==00 */ + case 1: /* mod=01 */ + switch(rh) + { + case 0: /* test byte imm */ + destoffset=decode_rm01_address(m,rl); + srcval = fetch_byte_imm(m); + destval = fetch_data_byte(m,destoffset); + test_byte(m, destval, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = not_byte(m, destval); + store_data_byte(m,destoffset,destval); + break; + case 3: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = neg_byte(m, destval); + store_data_byte(m,destoffset,destval); + break; + case 4: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + mul_byte(m, destval); + break; + case 5: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imul_byte(m, destval); + break; + case 6: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + div_byte(m, destval); + break; + case 7: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_byte(m,destoffset); + idiv_byte(m, destval); + break; + } + break; /* end mod==01 */ + case 2: /* mod=10 */ + switch(rh) + { + case 0: /* test byte imm */ + destoffset=decode_rm10_address(m,rl); + srcval = fetch_byte_imm(m); + destval = fetch_data_byte(m,destoffset); + test_byte(m, destval, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = not_byte(m, destval); + store_data_byte(m,destoffset,destval); + break; + case 3: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + destval = neg_byte(m, destval); + store_data_byte(m,destoffset,destval); + break; + case 4: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + mul_byte(m, destval); + break; + case 5: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + imul_byte(m, destval); + break; + case 6: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + div_byte(m, destval); + break; + case 7: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_byte(m,destoffset); + idiv_byte(m, destval); + break; + } + break; /* end mod==10 */ + case 3: /* mod=11 */ + switch(rh) + { + case 0: /* test byte imm */ + destreg=DECODE_RM_BYTE_REGISTER(m,rl); + srcval = fetch_byte_imm(m); + test_byte(m, *destreg, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destreg=DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = not_byte(m, *destreg); + break; + case 3: + destreg=DECODE_RM_BYTE_REGISTER(m,rl); + *destreg = neg_byte(m, *destreg); + break; + case 4: + destreg=DECODE_RM_BYTE_REGISTER(m,rl); + mul_byte(m, *destreg); /*!!! */ + break; + case 5: + destreg=DECODE_RM_BYTE_REGISTER(m,rl); + imul_byte(m, *destreg); + break; + case 6: + destreg=DECODE_RM_BYTE_REGISTER(m,rl); + div_byte(m, *destreg); + break; + case 7: + destreg=DECODE_RM_BYTE_REGISTER(m,rl); + idiv_byte(m, *destreg); + break; + } + break; /* end mod==11 */ + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf7*/ +static void i86op_opcF7_word_RM(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 *destreg; + uint16 destoffset; + uint16 destval,srcval; + /* long, drawn out code follows. Double switch for a total + of 32 cases. */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: /* mod=00 */ + switch(rh) + { + case 0: /* test word imm */ + destoffset=decode_rm00_address(m,rl); + srcval = fetch_word_imm(m); + destval = fetch_data_word(m,destoffset); + test_word(m, destval, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = not_word(m, destval); + store_data_word(m,destoffset,destval); + break; + case 3: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = neg_word(m, destval); + store_data_word(m,destoffset,destval); + break; + case 4: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + mul_word(m, destval); + break; + case 5: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + imul_word(m, destval); + break; + case 6: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + div_word(m, destval); + break; + case 7: + destoffset=decode_rm00_address(m,rl); + destval = fetch_data_word(m,destoffset); + idiv_word(m, destval); + break; + } + break; /* end mod==00 */ + case 1: /* mod=01 */ + switch(rh) + { + case 0: /* test word imm */ + destoffset=decode_rm01_address(m,rl); + srcval = fetch_word_imm(m); + destval = fetch_data_word(m,destoffset); + test_word(m, destval, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = not_word(m, destval); + store_data_word(m,destoffset,destval); + break; + case 3: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = neg_word(m, destval); + store_data_word(m,destoffset,destval); + break; + case 4: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + mul_word(m, destval); + break; + case 5: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + imul_word(m, destval); + break; + case 6: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + div_word(m, destval); + break; + case 7: + destoffset=decode_rm01_address(m,rl); + destval = fetch_data_word(m,destoffset); + idiv_word(m, destval); + break; + } + break; /* end mod==01 */ + case 2: /* mod=10 */ + switch(rh) + { + case 0: /* test word imm */ + destoffset=decode_rm10_address(m,rl); + srcval = fetch_word_imm(m); + destval = fetch_data_word(m,destoffset); + test_word(m, destval, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = not_word(m, destval); + store_data_word(m,destoffset,destval); + break; + case 3: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + destval = neg_word(m, destval); + store_data_word(m,destoffset,destval); + break; + case 4: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + mul_word(m, destval); + break; + case 5: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + imul_word(m, destval); + break; + case 6: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + div_word(m, destval); + break; + case 7: + destoffset=decode_rm10_address(m,rl); + destval = fetch_data_word(m,destoffset); + idiv_word(m, destval); + break; + } + break; /* end mod==10 */ + case 3: /* mod=11 */ + switch(rh) + { + case 0: /* test word imm */ + destreg=DECODE_RM_WORD_REGISTER(m,rl); + srcval = fetch_word_imm(m); + test_word(m, *destreg, srcval); + break; + case 1: + halt_sys(m); + break; + case 2: + destreg=DECODE_RM_WORD_REGISTER(m,rl); + *destreg = not_word(m, *destreg); + break; + case 3: + destreg=DECODE_RM_WORD_REGISTER(m,rl); + *destreg = neg_word(m, *destreg); + break; + case 4: + destreg=DECODE_RM_WORD_REGISTER(m,rl); + mul_word(m, *destreg); /*!!! */ + break; + case 5: + destreg=DECODE_RM_WORD_REGISTER(m,rl); + imul_word(m, *destreg); + break; + case 6: + destreg=DECODE_RM_WORD_REGISTER(m,rl); + div_word(m, *destreg); + break; + case 7: + destreg=DECODE_RM_WORD_REGISTER(m,rl); + idiv_word(m, *destreg); + break; + } + break; /* end mod==11 */ + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf8*/ +static void i86op_clc(PC_ENV *m) +{ + /* clear the carry flag. */ + CLEAR_FLAG(m, F_CF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xf9*/ +static void i86op_stc(PC_ENV *m) +{ + /* set the carry flag. */ + SET_FLAG(m, F_CF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xfa*/ +static void i86op_cli(PC_ENV *m) +{ + /* clear interrupts. */ + CLEAR_FLAG(m, F_IF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xfb*/ +static void i86op_sti(PC_ENV *m) +{ + /* enable interrupts. */ + SET_FLAG(m, F_IF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xfc*/ +static void i86op_cld(PC_ENV *m) +{ + /* clear interrupts. */ + CLEAR_FLAG(m, F_DF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xfd*/ +static void i86op_std(PC_ENV *m) +{ + /* clear interrupts. */ + SET_FLAG(m, F_DF); + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xfe*/ +static void i86op_opcFE_byte_RM(PC_ENV *m) +{ + /* Yet another damned special case instruction. */ + uint16 mod,rh,rl; + uint8 destval; + uint16 destoffset; + uint8 *destreg; + /* ARRGH, ANOTHER GODDAMN SPECIAL CASE INSTRUCTION!!! */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + switch (rh) + { + case 0: /* inc word ptr ... */ + destval = fetch_data_byte(m,destoffset); + destval = inc_byte(m,destval); + store_data_byte(m,destoffset,destval); + break; + case 1: /* dec word ptr ... */ + destval = fetch_data_byte(m,destoffset); + destval = dec_byte(m,destval); + store_data_byte(m,destoffset,destval); + break; + } + break; + case 1: + destoffset=decode_rm01_address(m,rl); + switch (rh) + { + case 0: + destval = fetch_data_byte(m,destoffset); + destval = inc_byte(m,destval); + store_data_byte(m,destoffset,destval); + break; + case 1: + destval = fetch_data_byte(m,destoffset); + destval = dec_byte(m,destval); + store_data_byte(m,destoffset,destval); + break; + } + break; + case 2: + destoffset=decode_rm10_address(m,rl); + switch (rh) + { + case 0: + destval = fetch_data_byte(m,destoffset); + destval = inc_byte(m,destval); + store_data_byte(m,destoffset,destval); + break; + case 1: + destval = fetch_data_byte(m,destoffset); + destval = dec_byte(m,destval); + store_data_byte(m,destoffset,destval); + break; + } + break; + case 3: + destreg = DECODE_RM_BYTE_REGISTER(m,rl); + switch (rh) + { + case 0: + *destreg = inc_byte(m,*destreg); + break; + case 1: + *destreg = dec_byte(m,*destreg); + break; + } + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/*opcode=0xff*/ +static void i86op_opcFF_word_RM(PC_ENV *m) +{ + uint16 mod,rh,rl; + uint16 destval,destval2; + uint16 destoffset; + uint16 *destreg; + /* ANOTHER DAMN SPECIAL CASE INSTRUCTION!!! */ + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + switch (rh) + { + case 0: /* inc word ptr ... */ + destval = fetch_data_word(m,destoffset); + destval = inc_word(m,destval); + store_data_word(m,destoffset,destval); + break; + case 1: /* dec word ptr ... */ + destval = fetch_data_word(m,destoffset); + destval = dec_word(m,destval); + store_data_word(m,destoffset,destval); + break; + case 2: /* call word ptr ... */ + destval = fetch_data_word(m,destoffset); + push_word(m,m->R_IP); + m->R_IP = destval; + break; + case 3: /* call far ptr ... */ + destval = fetch_data_word(m,destoffset); + destval2 = fetch_data_word(m,destoffset+2); + push_word(m,m->R_CS); + m->R_CS = destval2; + push_word(m,m->R_IP); + m->R_IP = destval; + break; + case 4: /* jmp word ptr ... */ + destval = fetch_data_word(m,destoffset); + m->R_IP = destval; + break; + case 5: /* jmp far ptr ... */ + destval = fetch_data_word(m,destoffset); + destval2 = fetch_data_word(m,destoffset+2); + m->R_IP = destval; + m->R_CS = destval2; + break; + case 6: /* push word ptr ... */ + destval = fetch_data_word(m,destoffset); + push_word(m,destval); + break; + } + break; + case 1: + destoffset=decode_rm01_address(m,rl); + switch (rh) + { + case 0: + destval = fetch_data_word(m,destoffset); + destval = inc_word(m,destval); + store_data_word(m,destoffset,destval); + break; + case 1: + destval = fetch_data_word(m,destoffset); + destval = dec_word(m,destval); + store_data_word(m,destoffset,destval); + break; + case 2: /* call word ptr ... */ + destval = fetch_data_word(m,destoffset); + push_word(m,m->R_IP); + m->R_IP = destval; + break; + case 3: /* call far ptr ... */ + destval = fetch_data_word(m,destoffset); + destval2 = fetch_data_word(m,destoffset+2); + push_word(m,m->R_CS); + m->R_CS = destval2; + push_word(m,m->R_IP); + m->R_IP = destval; + break; + case 4: /* jmp word ptr ... */ + destval = fetch_data_word(m,destoffset); + m->R_IP = destval; + break; + case 5: /* jmp far ptr ... */ + destval = fetch_data_word(m,destoffset); + destval2 = fetch_data_word(m,destoffset+2); + m->R_IP = destval; + m->R_CS = destval2; + break; + case 6: /* push word ptr ... */ + destval = fetch_data_word(m,destoffset); + push_word(m,destval); + break; + } + break; + case 2: + destoffset=decode_rm10_address(m,rl); + switch (rh) + { + case 0: + destval = fetch_data_word(m,destoffset); + destval = inc_word(m,destval); + store_data_word(m,destoffset,destval); + break; + case 1: + destval = fetch_data_word(m,destoffset); + destval = dec_word(m,destval); + store_data_word(m,destoffset,destval); + break; + case 2: /* call word ptr ... */ + destval = fetch_data_word(m,destoffset); + push_word(m,m->R_IP); + m->R_IP = destval; + break; + case 3: /* call far ptr ... */ + destval = fetch_data_word(m,destoffset); + destval2 = fetch_data_word(m,destoffset+2); + push_word(m,m->R_CS); + m->R_CS = destval2; + push_word(m,m->R_IP); + m->R_IP = destval; + break; + case 4: /* jmp word ptr ... */ + destval = fetch_data_word(m,destoffset); + m->R_IP = destval; + break; + case 5: /* jmp far ptr ... */ + destval = fetch_data_word(m,destoffset); + destval2 = fetch_data_word(m,destoffset+2); + m->R_IP = destval; + m->R_CS = destval2; + break; + case 6: /* push word ptr ... */ + destval = fetch_data_word(m,destoffset); + push_word(m,destval); + break; + } + break; + case 3: + destreg = DECODE_RM_WORD_REGISTER(m,rl); + switch (rh) + { + case 0: + *destreg = inc_word(m,*destreg); + break; + case 1: + *destreg = dec_word(m,*destreg); + break; + case 2: /* call word ptr ... */ + push_word(m,m->R_IP); + m->R_IP = *destreg; + break; + case 3: /* jmp far ptr ... */ + halt_sys(m); + break; + case 4: /* jmp ... */ + m->R_IP = (uint16)(*destreg); + break; + case 5: /* jmp far ptr ... */ + halt_sys(m); + break; + case 6: + push_word(m,*destreg); + break; + } + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xd8*/ +static void i86op_esc_coprocess_d8(PC_ENV *m) +{ + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xd9*/ +static void i86op_esc_coprocess_d9(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 destoffset; + uint8 stkelem; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + break; + case 3: /* register to register */ + stkelem = (uint8) rl; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xda*/ +static void i86op_esc_coprocess_da(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 destoffset; + uint8 stkelem; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + break; + case 3: /* register to register */ + stkelem = (uint8) rl; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xdb*/ +static void i86op_esc_coprocess_db(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 destoffset; +/* uint8 stkelem;*/ + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + break; + case 3: /* register to register */ + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xdc*/ +static void i86op_esc_coprocess_dc(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 destoffset; + uint8 stkelem; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + break; + case 3: /* register to register */ + stkelem = (uint8) rl; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xdd*/ +static void i86op_esc_coprocess_dd(PC_ENV *m) +{ + uint16 mod,rl,rh; + uint16 destoffset; + uint8 stkelem; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + break; + case 3: /* register to register */ + stkelem = (uint8) rl; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xde*/ +static void i86op_esc_coprocess_de(PC_ENV *m) + { + uint16 mod,rl,rh; + uint16 destoffset; + uint8 stkelem; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + break; + case 3: /* register to register */ + stkelem = (uint8) rl; + break; + } + DECODE_CLEAR_SEGOVR(m); +} + +/* opcode=0xdf*/ +static void i86op_esc_coprocess_df(PC_ENV *m) + { + uint16 mod,rl,rh; + uint16 destoffset; + uint8 stkelem; + FETCH_DECODE_MODRM(m,mod,rh,rl); + switch (mod) + { + case 0: + destoffset=decode_rm00_address(m,rl); + break; + case 1: + destoffset=decode_rm01_address(m,rl); + break; + case 2: + destoffset=decode_rm10_address(m,rl); + break; + case 3: /* register to register */ + stkelem = (uint8) rl; + break; + } + DECODE_CLEAR_SEGOVR(m); + } + +/* OPERAND TABLE */ + +OP i86_optab[256] = { + +/* 0x00 */ i86op_add_byte_RM_R, +/* 0x01 */ i86op_add_word_RM_R, +/* 0x02 */ i86op_add_byte_R_RM, +/* 0x03 */ i86op_add_word_R_RM, +/* 0x04 */ i86op_add_byte_AL_IMM, +/* 0x05 */ i86op_add_word_AX_IMM, +/* 0x06 */ i86op_push_ES, +/* 0x07 */ i86op_pop_ES, + +/* 0x08 */ i86op_or_byte_RM_R, +/* 0x09 */ i86op_or_word_RM_R, +/* 0x0a */ i86op_or_byte_R_RM, +/* 0x0b */ i86op_or_word_R_RM, +/* 0x0c */ i86op_or_byte_AL_IMM, +/* 0x0d */ i86op_or_word_AX_IMM, +/* 0x0e */ i86op_push_CS, +/* 0x0f */ i86op_illegal_op, + +/* 0x10 */ i86op_adc_byte_RM_R, +/* 0x11 */ i86op_adc_word_RM_R, +/* 0x12 */ i86op_adc_byte_R_RM, +/* 0x13 */ i86op_adc_word_R_RM, +/* 0x14 */ i86op_adc_byte_AL_IMM, +/* 0x15 */ i86op_adc_word_AX_IMM, +/* 0x16 */ i86op_push_SS, +/* 0x17 */ i86op_pop_SS, + +/* 0x18 */ i86op_sbb_byte_RM_R, +/* 0x19 */ i86op_sbb_word_RM_R, +/* 0x1a */ i86op_sbb_byte_R_RM, +/* 0x1b */ i86op_sbb_word_R_RM, +/* 0x1c */ i86op_sbb_byte_AL_IMM, +/* 0x1d */ i86op_sbb_word_AX_IMM, +/* 0x1e */ i86op_push_DS, +/* 0x1f */ i86op_pop_DS, + +/* 0x20 */ i86op_and_byte_RM_R, +/* 0x21 */ i86op_and_word_RM_R, +/* 0x22 */ i86op_and_byte_R_RM, +/* 0x23 */ i86op_and_word_R_RM, +/* 0x24 */ i86op_and_byte_AL_IMM, +/* 0x25 */ i86op_and_word_AX_IMM, +/* 0x26 */ i86op_segovr_ES, +/* 0x27 */ i86op_daa, + +/* 0x28 */ i86op_sub_byte_RM_R, +/* 0x29 */ i86op_sub_word_RM_R, +/* 0x2a */ i86op_sub_byte_R_RM, +/* 0x2b */ i86op_sub_word_R_RM, +/* 0x2c */ i86op_sub_byte_AL_IMM, +/* 0x2d */ i86op_sub_word_AX_IMM, +/* 0x2e */ i86op_segovr_CS, +/* 0x2f */ i86op_das, + +/* 0x30 */ i86op_xor_byte_RM_R, +/* 0x31 */ i86op_xor_word_RM_R, +/* 0x32 */ i86op_xor_byte_R_RM, +/* 0x33 */ i86op_xor_word_R_RM, +/* 0x34 */ i86op_xor_byte_AL_IMM, +/* 0x35 */ i86op_xor_word_AX_IMM, +/* 0x36 */ i86op_segovr_SS, +/* 0x37 */ i86op_aaa, + +/* 0x38 */ i86op_cmp_byte_RM_R, +/* 0x39 */ i86op_cmp_word_RM_R, +/* 0x3a */ i86op_cmp_byte_R_RM, +/* 0x3b */ i86op_cmp_word_R_RM, +/* 0x3c */ i86op_cmp_byte_AL_IMM, +/* 0x3d */ i86op_cmp_word_AX_IMM, +/* 0x3e */ i86op_segovr_DS, +/* 0x3f */ i86op_aas, + +/* 0x40 */ i86op_inc_AX, +/* 0x41 */ i86op_inc_CX, +/* 0x42 */ i86op_inc_DX, +/* 0x43 */ i86op_inc_BX, +/* 0x44 */ i86op_inc_SP, +/* 0x45 */ i86op_inc_BP, +/* 0x46 */ i86op_inc_SI, +/* 0x47 */ i86op_inc_DI, + +/* 0x48 */ i86op_dec_AX, +/* 0x49 */ i86op_dec_CX, +/* 0x4a */ i86op_dec_DX, +/* 0x4b */ i86op_dec_BX, +/* 0x4c */ i86op_dec_SP, +/* 0x4d */ i86op_dec_BP, +/* 0x4e */ i86op_dec_SI, +/* 0x4f */ i86op_dec_DI, + +/* 0x50 */ i86op_push_AX, +/* 0x51 */ i86op_push_CX, +/* 0x52 */ i86op_push_DX, +/* 0x53 */ i86op_push_BX, +/* 0x54 */ i86op_push_SP, +/* 0x55 */ i86op_push_BP, +/* 0x56 */ i86op_push_SI, +/* 0x57 */ i86op_push_DI, + +/* 0x58 */ i86op_pop_AX, +/* 0x59 */ i86op_pop_CX, +/* 0x5a */ i86op_pop_DX, +/* 0x5b */ i86op_pop_BX, +/* 0x5c */ i86op_pop_SP, +/* 0x5d */ i86op_pop_BP, +/* 0x5e */ i86op_pop_SI, +/* 0x5f */ i86op_pop_DI, + +/* 0x60 */ i86op_illegal_op, +/* 0x61 */ i86op_illegal_op, +/* 0x62 */ i86op_illegal_op, +/* 0x63 */ i86op_illegal_op, +/* 0x64 */ i86op_illegal_op, +/* 0x65 */ i86op_illegal_op, +/* 0x66 */ i86op_illegal_op, +/* 0x67 */ i86op_illegal_op, + +/* 0x68 */ i86op_illegal_op, +/* 0x69 */ i86op_illegal_op, +/* 0x6a */ i86op_illegal_op, +/* 0x6b */ i86op_illegal_op, +/* 0x6c */ i86op_illegal_op, +/* 0x6d */ i86op_illegal_op, +/* 0x6e */ i86op_illegal_op, +/* 0x6f */ i86op_illegal_op, + +/* 0x70 */ i86op_jump_near_O, +/* 0x71 */ i86op_jump_near_NO, +/* 0x72 */ i86op_jump_near_B, +/* 0x73 */ i86op_jump_near_NB, +/* 0x74 */ i86op_jump_near_Z, +/* 0x75 */ i86op_jump_near_NZ, +/* 0x76 */ i86op_jump_near_BE, +/* 0x77 */ i86op_jump_near_NBE, + +/* 0x78 */ i86op_jump_near_S, +/* 0x79 */ i86op_jump_near_NS, +/* 0x7a */ i86op_jump_near_P, +/* 0x7b */ i86op_jump_near_NP, +/* 0x7c */ i86op_jump_near_L, +/* 0x7d */ i86op_jump_near_NL, +/* 0x7e */ i86op_jump_near_LE, +/* 0x7f */ i86op_jump_near_NLE, + +/* 0x80 */ i86op_opc80_byte_RM_IMM, +/* 0x81 */ i86op_opc81_word_RM_IMM, +/* 0x82 */ i86op_opc82_byte_RM_IMM, +/* 0x83 */ i86op_opc83_word_RM_IMM, +/* 0x84 */ i86op_test_byte_RM_R, +/* 0x85 */ i86op_test_word_RM_R, +/* 0x86 */ i86op_xchg_byte_RM_R, +/* 0x87 */ i86op_xchg_word_RM_R, + +/* 0x88 */ i86op_mov_byte_RM_R, +/* 0x89 */ i86op_mov_word_RM_R, +/* 0x8a */ i86op_mov_byte_R_RM, +/* 0x8b */ i86op_mov_word_R_RM, +/* 0x8c */ i86op_mov_word_RM_SR, +/* 0x8d */ i86op_lea_word_R_M, +/* 0x8e */ i86op_mov_word_SR_RM, +/* 0x8f */ i86op_pop_RM, + +/* 0x90 */ i86op_nop, +/* 0x91 */ i86op_xchg_word_AX_CX, +/* 0x92 */ i86op_xchg_word_AX_DX, +/* 0x93 */ i86op_xchg_word_AX_BX, +/* 0x94 */ i86op_xchg_word_AX_SP, +/* 0x95 */ i86op_xchg_word_AX_BP , +/* 0x96 */ i86op_xchg_word_AX_SI , +/* 0x97 */ i86op_xchg_word_AX_DI , + +/* 0x98 */ i86op_cbw, +/* 0x99 */ i86op_cwd, +/* 0x9a */ i86op_call_far_IMM, +/* 0x9b */ i86op_wait, +/* 0x9c */ i86op_pushf_word, +/* 0x9d */ i86op_popf_word, +/* 0x9e */ i86op_sahf, +/* 0x9f */ i86op_lahf, + +/* 0xa0 */ i86op_mov_AL_M_IMM, +/* 0xa1 */ i86op_mov_AX_M_IMM, +/* 0xa2 */ i86op_mov_M_AL_IMM, +/* 0xa3 */ i86op_mov_M_AX_IMM, +/* 0xa4 */ i86op_movs_byte, +/* 0xa5 */ i86op_movs_word, +/* 0xa6 */ i86op_cmps_byte, +/* 0xa7 */ i86op_cmps_word, +/* 0xa8 */ i86op_test_AL_IMM, +/* 0xa9 */ i86op_test_AX_IMM, +/* 0xaa */ i86op_stos_byte, +/* 0xab */ i86op_stos_word, +/* 0xac */ i86op_lods_byte, +/* 0xad */ i86op_lods_word, +/* 0xac */ i86op_scas_byte, +/* 0xad */ i86op_scas_word, + +/* 0xb0 */ i86op_mov_byte_AL_IMM, +/* 0xb1 */ i86op_mov_byte_CL_IMM, +/* 0xb2 */ i86op_mov_byte_DL_IMM, +/* 0xb3 */ i86op_mov_byte_BL_IMM, +/* 0xb4 */ i86op_mov_byte_AH_IMM, +/* 0xb5 */ i86op_mov_byte_CH_IMM, +/* 0xb6 */ i86op_mov_byte_DH_IMM, +/* 0xb7 */ i86op_mov_byte_BH_IMM, + +/* 0xb8 */ i86op_mov_word_AX_IMM, +/* 0xb9 */ i86op_mov_word_CX_IMM, +/* 0xba */ i86op_mov_word_DX_IMM, +/* 0xbb */ i86op_mov_word_BX_IMM, +/* 0xbc */ i86op_mov_word_SP_IMM, +/* 0xbd */ i86op_mov_word_BP_IMM, +/* 0xbe */ i86op_mov_word_SI_IMM, +/* 0xbf */ i86op_mov_word_DI_IMM, + +/* 0xc0 */ i86op_illegal_op, +/* 0xc1 */ i86op_illegal_op, +/* 0xc2 */ i86op_ret_near_IMM, +/* 0xc3 */ i86op_ret_near, +/* 0xc4 */ i86op_les_R_IMM, +/* 0xc5 */ i86op_lds_R_IMM, +/* 0xc6 */ i86op_mov_byte_RM_IMM, +/* 0xc7 */ i86op_mov_word_RM_IMM, +/* 0xc8 */ i86op_illegal_op, +/* 0xc9 */ i86op_illegal_op, +/* 0xca */ i86op_ret_far_IMM, +/* 0xcb */ i86op_ret_far, +/* 0xcc */ i86op_int3, +/* 0xcd */ i86op_int_IMM, +/* 0xce */ i86op_into, +/* 0xcf */ i86op_iret, + +/* 0xd0 */ i86op_opcD0_byte_RM_1, +/* 0xd1 */ i86op_opcD1_word_RM_1, +/* 0xd2 */ i86op_opcD2_byte_RM_CL, +/* 0xd3 */ i86op_opcD3_word_RM_CL, +/* 0xd4 */ i86op_aam, +/* 0xd5 */ i86op_aad, +/* 0xd6 */ i86op_illegal_op, +/* 0xd7 */ i86op_xlat, +/* 0xd8 */ i86op_esc_coprocess_d8, +/* 0xd9 */ i86op_esc_coprocess_d9, +/* 0xda */ i86op_esc_coprocess_da, +/* 0xdb */ i86op_esc_coprocess_db, +/* 0xdc */ i86op_esc_coprocess_dc, +/* 0xdd */ i86op_esc_coprocess_dd, +/* 0xde */ i86op_esc_coprocess_de, +/* 0xdf */ i86op_esc_coprocess_df, + +/* 0xe0 */ i86op_loopne, +/* 0xe1 */ i86op_loope, +/* 0xe2 */ i86op_loop, +/* 0xe3 */ i86op_jcxz, +/* 0xe4 */ i86op_in_byte_AL_IMM, +/* 0xe5 */ i86op_in_word_AX_IMM, +/* 0xe6 */ i86op_out_byte_IMM_AL, +/* 0xe7 */ i86op_out_word_IMM_AX, + +/* 0xe8 */ i86op_call_near_IMM, +/* 0xe9 */ i86op_jump_near_IMM, +/* 0xea */ i86op_jump_far_IMM, +/* 0xeb */ i86op_jump_byte_IMM, +/* 0xec */ i86op_in_byte_AL_DX, +/* 0xed */ i86op_in_word_AX_DX, +/* 0xee */ i86op_out_byte_DX_AL, +/* 0xef */ i86op_out_word_DX_AX, + +/* 0xf0 */ i86op_lock, +/* 0xf1 */ i86op_illegal_op, +/* 0xf2 */ i86op_repne, +/* 0xf3 */ i86op_repe, +/* 0xf4 */ i86op_halt, +/* 0xf5 */ i86op_cmc, +/* 0xf6 */ i86op_opcF6_byte_RM, +/* 0xf7 */ i86op_opcF7_word_RM, + +/* 0xf8 */ i86op_clc, +/* 0xf9 */ i86op_stc, +/* 0xfa */ i86op_cli, +/* 0xfb */ i86op_sti, +/* 0xfc */ i86op_cld, +/* 0xfd */ i86op_std, +/* 0xfe */ i86op_opcFE_byte_RM, +/* 0xff */ i86op_opcFF_word_RM, + +}; diff --git a/AltairZ80/i86_prim_ops.c b/AltairZ80/i86_prim_ops.c new file mode 100644 index 0000000..a1fb769 --- /dev/null +++ b/AltairZ80/i86_prim_ops.c @@ -0,0 +1,1539 @@ +/* + * Dos/PC Emulator + * Copyright (C) 1991 Jim Hudgens + * + * + * The file is part of GDE. + * + * GDE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 1, or (at your option) + * any later version. + * + * GDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GDE; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "altairz80_defs.h" +#include "i86.h" + +extern uint32 GetBYTEExtended(register uint32 Addr); +extern void PutBYTEExtended(register uint32 Addr, const register uint32 Value); + +/* $Log: i86_prim_ops.c,v $ + * Revision 0.9 2003-01-10 23:33:10 jce + * fixed some more warnings under gcc -Wall + * + * Revision 0.8 1992/04/11 21:58:13 hudgens + * fixed some code causing warnings under gcc -Wall + * + * Revision 0.7 1991/07/30 02:04:34 hudgens + * added copyright. + * + * Revision 0.6 1991/07/21 16:50:37 hudgens + * fixed all flags in the bit shift and rotate instructions so that they + * finally agree. + * Also fixed the flags associated with IMUL and MUL instructions. + * + * Revision 0.5 1991/07/21 01:28:16 hudgens + * added support for aad and aam primitives. + * + * Revision 0.4 1991/07/20 22:26:25 hudgens + * fixed problem with sign extension in subroutine mem_access_word. + * + * Revision 0.3 1991/07/17 03:48:22 hudgens + * fixed bugs having to do with the setting of flags in the + * shift and rotate operations. Also, fixed sign extension problem + * with push_word and pop_word. + * + * Revision 0.2 1991/04/01 02:36:00 hudgens + * Fixed several nasty bugs dealing with flag setting in the subroutines + * sub_byte, sub_word, sbb_byte, sbb_word, and test_word. The results + * now agree with the PC on both of the testaopb and testaopw tests. + * + * Revision 0.1 1991/03/30 21:13:37 hudgens + * Initial checkin. + * + * + */ + +/* [JCE] Stop gcc -Wall complaining */ +void i86_intr_raise(PC_ENV *m, uint8 intrnum); + +/* the following table was generated using the following + code (running on an IBM AT, Turbo C++ 2.0), for all values of i + between 0 and 255. AL is loaded with i's value, and then the + operation "and al,al" sets the parity flag. The flags are pushed + onto the stack, and then popped back into AX. Then AX is + returned. So the value of each table entry represents the + parity of its index into the table. This results in a somewhat + faster mechanism for parity calculations than the straightforward + method. + andflags(i,res) int *res; { + int flags; + _AX = i; asm and al,al; asm pushf; *res = _AX; + asm pop ax; flags = _AX; return flags; + } + */ + +char parity_tab[] = { +/*0*/ 1, /*1*/ 0, /*2*/ 0, /*3*/ 1, +/*4*/ 0, /*5*/ 1, /*6*/ 1, /*7*/ 0, +/*8*/ 0, /*9*/ 1, /*a*/ 1, /*b*/ 0, +/*c*/ 1, /*d*/ 0, /*e*/ 0, /*f*/ 1, +/*10*/ 0, /*11*/ 1, /*12*/ 1, /*13*/ 0, +/*14*/ 1, /*15*/ 0, /*16*/ 0, /*17*/ 1, +/*18*/ 1, /*19*/ 0, /*1a*/ 0, /*1b*/ 1, +/*1c*/ 0, /*1d*/ 1, /*1e*/ 1, /*1f*/ 0, +/*20*/ 0, /*21*/ 1, /*22*/ 1, /*23*/ 0, +/*24*/ 1, /*25*/ 0, /*26*/ 0, /*27*/ 1, +/*28*/ 1, /*29*/ 0, /*2a*/ 0, /*2b*/ 1, +/*2c*/ 0, /*2d*/ 1, /*2e*/ 1, /*2f*/ 0, +/*30*/ 1, /*31*/ 0, /*32*/ 0, /*33*/ 1, +/*34*/ 0, /*35*/ 1, /*36*/ 1, /*37*/ 0, +/*38*/ 0, /*39*/ 1, /*3a*/ 1, /*3b*/ 0, +/*3c*/ 1, /*3d*/ 0, /*3e*/ 0, /*3f*/ 1, +/*40*/ 0, /*41*/ 1, /*42*/ 1, /*43*/ 0, +/*44*/ 1, /*45*/ 0, /*46*/ 0, /*47*/ 1, +/*48*/ 1, /*49*/ 0, /*4a*/ 0, /*4b*/ 1, +/*4c*/ 0, /*4d*/ 1, /*4e*/ 1, /*4f*/ 0, +/*50*/ 1, /*51*/ 0, /*52*/ 0, /*53*/ 1, +/*54*/ 0, /*55*/ 1, /*56*/ 1, /*57*/ 0, +/*58*/ 0, /*59*/ 1, /*5a*/ 1, /*5b*/ 0, +/*5c*/ 1, /*5d*/ 0, /*5e*/ 0, /*5f*/ 1, +/*60*/ 1, /*61*/ 0, /*62*/ 0, /*63*/ 1, +/*64*/ 0, /*65*/ 1, /*66*/ 1, /*67*/ 0, +/*68*/ 0, /*69*/ 1, /*6a*/ 1, /*6b*/ 0, +/*6c*/ 1, /*6d*/ 0, /*6e*/ 0, /*6f*/ 1, +/*70*/ 0, /*71*/ 1, /*72*/ 1, /*73*/ 0, +/*74*/ 1, /*75*/ 0, /*76*/ 0, /*77*/ 1, +/*78*/ 1, /*79*/ 0, /*7a*/ 0, /*7b*/ 1, +/*7c*/ 0, /*7d*/ 1, /*7e*/ 1, /*7f*/ 0, +/*80*/ 0, /*81*/ 1, /*82*/ 1, /*83*/ 0, +/*84*/ 1, /*85*/ 0, /*86*/ 0, /*87*/ 1, +/*88*/ 1, /*89*/ 0, /*8a*/ 0, /*8b*/ 1, +/*8c*/ 0, /*8d*/ 1, /*8e*/ 1, /*8f*/ 0, +/*90*/ 1, /*91*/ 0, /*92*/ 0, /*93*/ 1, +/*94*/ 0, /*95*/ 1, /*96*/ 1, /*97*/ 0, +/*98*/ 0, /*99*/ 1, /*9a*/ 1, /*9b*/ 0, +/*9c*/ 1, /*9d*/ 0, /*9e*/ 0, /*9f*/ 1, +/*a0*/ 1, /*a1*/ 0, /*a2*/ 0, /*a3*/ 1, +/*a4*/ 0, /*a5*/ 1, /*a6*/ 1, /*a7*/ 0, +/*a8*/ 0, /*a9*/ 1, /*aa*/ 1, /*ab*/ 0, +/*ac*/ 1, /*ad*/ 0, /*ae*/ 0, /*af*/ 1, +/*b0*/ 0, /*b1*/ 1, /*b2*/ 1, /*b3*/ 0, +/*b4*/ 1, /*b5*/ 0, /*b6*/ 0, /*b7*/ 1, +/*b8*/ 1, /*b9*/ 0, /*ba*/ 0, /*bb*/ 1, +/*bc*/ 0, /*bd*/ 1, /*be*/ 1, /*bf*/ 0, +/*c0*/ 1, /*c1*/ 0, /*c2*/ 0, /*c3*/ 1, +/*c4*/ 0, /*c5*/ 1, /*c6*/ 1, /*c7*/ 0, +/*c8*/ 0, /*c9*/ 1, /*ca*/ 1, /*cb*/ 0, +/*cc*/ 1, /*cd*/ 0, /*ce*/ 0, /*cf*/ 1, +/*d0*/ 0, /*d1*/ 1, /*d2*/ 1, /*d3*/ 0, +/*d4*/ 1, /*d5*/ 0, /*d6*/ 0, /*d7*/ 1, +/*d8*/ 1, /*d9*/ 0, /*da*/ 0, /*db*/ 1, +/*dc*/ 0, /*dd*/ 1, /*de*/ 1, /*df*/ 0, +/*e0*/ 0, /*e1*/ 1, /*e2*/ 1, /*e3*/ 0, +/*e4*/ 1, /*e5*/ 0, /*e6*/ 0, /*e7*/ 1, +/*e8*/ 1, /*e9*/ 0, /*ea*/ 0, /*eb*/ 1, +/*ec*/ 0, /*ed*/ 1, /*ee*/ 1, /*ef*/ 0, +/*f0*/ 1, /*f1*/ 0, /*f2*/ 0, /*f3*/ 1, +/*f4*/ 0, /*f5*/ 1, /*f6*/ 1, /*f7*/ 0, +/*f8*/ 0, /*f9*/ 1, /*fa*/ 1, /*fb*/ 0, +/*fc*/ 1, /*fd*/ 0, /*fe*/ 0, /*ff*/ 1, +}; + +char xor_0x3_tab[] = { 0, 1, 1, 0 }; + +/* CARRY CHAIN CALCULATION. + This represents a somewhat expensive calculation which is + apparently required to emulate the setting of the OF and + AF flag. The latter is not so important, but the former is. + The overflow flag is the XOR of the top two bits of the + carry chain for an addition (similar for subtraction). + Since we do not want to simulate the addition in a bitwise + manner, we try to calculate the carry chain given the + two operands and the result. + + So, given the following table, which represents the + addition of two bits, we can derive a formula for + the carry chain. + + a b cin r cout + 0 0 0 0 0 + 0 0 1 1 0 + 0 1 0 1 0 + 0 1 1 0 1 + 1 0 0 1 0 + 1 0 1 0 1 + 1 1 0 0 1 + 1 1 1 1 1 + + Construction of table for cout: + + ab + r \ 00 01 11 10 + |------------------ + 0 | 0 1 1 1 + 1 | 0 0 1 0 + + By inspection, one gets: cc = ab + r'(a + b) + + That represents alot of operations, but NO CHOICE.... + +BORROW CHAIN CALCULATION. + The following table represents the + subtraction of two bits, from which we can derive a formula for + the borrow chain. + + a b bin r bout + 0 0 0 0 0 + 0 0 1 1 1 + 0 1 0 1 1 + 0 1 1 0 1 + 1 0 0 1 0 + 1 0 1 0 0 + 1 1 0 0 0 + 1 1 1 1 1 + + Construction of table for cout: + + ab + r \ 00 01 11 10 + |------------------ + 0 | 0 1 0 0 + 1 | 1 1 1 0 + + By inspection, one gets: bc = a'b + r(a' + b) + + */ + +uint8 aad_word(PC_ENV *m, uint16 d) +{ + uint16 l; + uint8 hb,lb; + hb = (d>>8)&0xff; + lb = (d&0xff); + l = lb + 10 * hb; + CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(l == 0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF); + return (uint8) l; +} + +uint16 aam_word(PC_ENV *m, uint8 d) +{ + uint16 h,l; + h = d / 10; + l = d % 10; + l |= (h<<8); + CONDITIONAL_SET_FLAG(l & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(l == 0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[l & 0xff], m, F_PF); + return l; +} + +uint8 adc_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint16 res; /* all operands in native machine order */ + register uint16 cc; + if (ACCESS_FLAG(m,F_CF) ) + res = 1 + d + s; + else + res = d + s; + CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); + return (uint8) res; +} + +uint16 adc_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 cc; + if (ACCESS_FLAG(m,F_CF) ) + res = 1 + d + s; + else + res = d + s; + /* set the carry flag to be bit 8 */ + CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); + return res; +} + +/* Given flags=f, and bytes d (dest) and s (source) + perform the add and set the flags and the result back to + *d. USE NATIVE MACHINE ORDER... +*/ +uint8 add_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint16 res; /* all operands in native machine order */ + register uint16 cc; + res = d + s; + /* set the carry flag to be bit 8 */ + CONDITIONAL_SET_FLAG(res & 0x100, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); + return (uint8) res; +} + +/* Given flags=f, and bytes d (dest) and s (source) + perform the add and set the flags and the result back to + *d. USE NATIVE MACHINE ORDER... +*/ +uint16 add_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 cc; + res = d + s; + /* set the carry flag to be bit 8 */ + CONDITIONAL_SET_FLAG(res & 0x10000, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (s & d) | ((~res) & (s | d)); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); + return res; +} + +/* + Flags m->R_FLG, dest *d, source *s, do a bitwise and of the + source and destination, and then store back to the + destination. Size=byte. +*/ +uint8 and_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint8 res; /* all operands in native machine order */ + res = d & s; + /* set the flags */ + CLEAR_FLAG(m, F_OF); + CLEAR_FLAG(m, F_CF); + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); + return res; +} + +/* + Flags m->R_FLG, dest *d, source *s, do a bitwise and of the + source and destination, and then store back to the + destination. Size=byte. +*/ +uint16 and_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint16 res; /* all operands in native machine order */ + res = d & s; + /* set the flags */ + CLEAR_FLAG(m, F_OF); + CLEAR_FLAG(m, F_CF); + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + return res; +} + +uint8 cmp_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + res = d - s; + CLEAR_FLAG(m, F_CF); + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return d; /* long story why this is needed. Look at opcode + 0x80 in ops.c, for an idea why this is necessary.*/ +} + +uint16 cmp_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + res = d - s; + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return d; +} + +uint8 dec_byte(PC_ENV *m, uint8 d) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + res = d - 1; + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + /* based on sub_byte, uses s==1. */ + bc= (res&(~d|1))|(~d&1); + /* carry flag unchanged */ + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res; +} + +uint16 dec_word(PC_ENV *m, uint16 d) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + res = d - 1; + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + /* based on the sub_byte routine, with s==1 */ + bc= (res&(~d|1))|(~d&1); + /* carry flag unchanged */ + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res; +} + +/* Given flags=f, and byte d (dest) + perform the inc and set the flags and the result back to + d. USE NATIVE MACHINE ORDER... +*/ +uint8 inc_byte(PC_ENV *m, uint8 d) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 cc; + res = d + 1; + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = ((1 & d) | (~res)) & (1 | d); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); + return res; +} + +/* Given flags=f, and byte d (dest) + perform the inc and set the flags and the result back to + *d. USE NATIVE MACHINE ORDER... +*/ +uint16 inc_word(PC_ENV *m, uint16 d) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 cc; + res = d + 1; + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the carry chain SEE NOTE AT TOP.*/ + cc = (1 & d) | ((~res) & (1 | d)); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(cc>>14)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(cc&0x8, m, F_AF); + return res ; +} + +uint8 or_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint8 res; /* all operands in native machine order */ + res = d | s; + CLEAR_FLAG(m, F_OF); + CLEAR_FLAG(m, F_CF); + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); + return res; +} + +uint16 or_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint16 res; /* all operands in native machine order */ + res = d | s; + /* set the carry flag to be bit 8 */ + CLEAR_FLAG(m, F_OF); + CLEAR_FLAG(m, F_CF); + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + return res; +} + +uint8 neg_byte(PC_ENV *m, uint8 s) +{ + register uint8 res; + register uint8 bc; + CONDITIONAL_SET_FLAG(s!=0, m, F_CF); + res = -s; + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); + /* calculate the borrow chain --- modified such that d=0. + substitutiing d=0 into bc= res&(~d|s)|(~d&s); + (the one used for sub) and simplifying, since ~d=0xff..., + ~d|s == 0xffff..., and res&0xfff... == res. Similarly + ~d&s == s. So the simplified result is:*/ + bc= res|s; + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res; +} + +uint16 neg_word(PC_ENV *m, uint16 s) +{ + register uint16 res; + register uint16 bc; + CONDITIONAL_SET_FLAG(s!=0, m, F_CF); + res = -s; + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain --- modified such that d=0. + substitutiing d=0 into bc= res&(~d|s)|(~d&s); + (the one used for sub) and simplifying, since ~d=0xff..., + ~d|s == 0xffff..., and res&0xfff... == res. Similarly + ~d&s == s. So the simplified result is:*/ + bc= res|s; + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res; +} + +uint8 not_byte(PC_ENV *m, uint8 s) +{ + return ~s; +} + +uint16 not_word(PC_ENV *m, uint16 s) +{ + return ~s; +} + +/* access stuff from absolute location in memory. + no segment registers are involved. + */ +uint16 mem_access_word(PC_ENV *m, int addr) +{ + /* Load in two steps. Native byte order independent */ + return GetBYTEExtended(addr) | (GetBYTEExtended(addr + 1) << 8); +} + +/* given the register_set r, and memory descriptor m, + and word w, push w onto the stack. + w ASSUMED IN NATIVE MACHINE ORDER. Doesn't matter in this case??? + */ +void push_word(PC_ENV *m, uint16 w) +{ + m->R_SP --; + PutBYTEExtended((m->R_SS << 4) + m->R_SP, w >> 8); + m->R_SP --; + PutBYTEExtended((m->R_SS << 4) + m->R_SP, w & 0xff); +} + +/* given the memory descriptor m, + and word w, pop word from the stack. + */ +uint16 pop_word(PC_ENV *m) +{ + register uint16 res; + res = GetBYTEExtended((m->R_SS << 4) + m->R_SP); + m->R_SP++; + res |= GetBYTEExtended((m->R_SS << 4) + m->R_SP) << 8; + m->R_SP++; + return res; +} + +/***************************************************************** + BEGIN region consisting of bit shifts and rotates, + much of which may be wrong. Large hirsute factor. +*****************************************************************/ +uint8 rcl_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint32 res, cnt, mask,cf; + /* s is the rotate distance. It varies from 0 - 8. */ + /* have + CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 + want to rotate through the carry by "s" bits. We could + loop, but that's inefficient. So the width is 9, + and we split into three parts: + The new carry flag (was B_n) + the stuff in B_n-1 .. B_0 + the stuff in B_7 .. B_n+1 + The new rotate is done mod 9, and given this, + for a rotation of n bits (mod 9) the new carry flag is + then located n bits from the MSB. The low part is + then shifted up cnt bits, and the high part is or'd + in. Using CAPS for new values, and lowercase for the + original values, this can be expressed as: + IF n > 0 + 1) CF <- b_(8-n) + 2) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 + 3) B_(n-1) <- cf + 4) B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) + I think this is correct. + */ + res = d; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 9)) /* not a typo, do nada if cnt==0 */ + { + /* extract the new CARRY FLAG. */ + /* CF <- b_(8-n) */ + cf = (d >> (8-cnt)) & 0x1; + /* get the low stuff which rotated + into the range B_7 .. B_cnt */ + /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_0 */ + /* note that the right hand side done by the mask */ + res = (d << cnt) & 0xff; + /* now the high stuff which rotated around + into the positions B_cnt-2 .. B_0 */ + /* B_(n-2) .. B_0 <- b_7 .. b_(8-(n-1)) */ + /* shift it downward, 7-(n-2) = 9-n positions. + and mask off the result before or'ing in. + */ + mask = (1<<(cnt-1)) - 1; + res |= (d >> (9-cnt)) & mask; + /* if the carry flag was set, or it in. */ + if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ + { + /* B_(n-1) <- cf */ + res |= 1 << (cnt-1); + } + /* set the new carry flag, based on the variable "cf" */ + CONDITIONAL_SET_FLAG(cf, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + /* parenthesized this expression since it appears to + be causing OF to be misset */ + CONDITIONAL_SET_FLAG(cnt==1&& + xor_0x3_tab[cf+((res>>6)&0x2)], + m, F_OF); + } + return res & 0xff; +} + +uint16 rcl_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res, cnt, mask,cf; + /* see analysis above. */ + /* width here is 16 bits + carry bit */ + res = d; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 17)) /* not a typo, do nada if cnt==0 */ + { + /* extract the new CARRY FLAG. */ + /* CF <- b_(16-n) */ + cf = (d >> (16-cnt)) & 0x1; + /* get the low stuff which rotated + into the range B_15 .. B_cnt */ + /* B_(15) .. B_(n) <- b_(16-(n+1)) .. b_0 */ + /* note that the right hand side done by the mask */ + res = (d << cnt) & 0xffff; + /* now the high stuff which rotated around + into the positions B_cnt-2 .. B_0 */ + /* B_(n-2) .. B_0 <- b_15 .. b_(16-(n-1)) */ + /* shift it downward, 15-(n-2) = 17-n positions. + and mask off the result before or'ing in. + */ + mask = (1<<(cnt-1)) - 1; + res |= (d >> (17-cnt)) & mask; + /* if the carry flag was set, or it in. */ + if (ACCESS_FLAG(m, F_CF)) /* carry flag is set */ + { + /* B_(n-1) <- cf */ + res |= 1 << (cnt-1); + } + /* set the new carry flag, based on the variable "cf" */ + CONDITIONAL_SET_FLAG(cf, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. + Note that we're forming a 2 bit word here to index + into the table. The expression cf+(res>>14)&0x2 + represents the two bit word b_15 CF. + */ + /* parenthesized following expression... */ + CONDITIONAL_SET_FLAG(cnt==1&&xor_0x3_tab[cf+((res>>14)&0x2)], + m, F_OF); + } + return res & 0xffff; +} + +uint8 rcr_byte(PC_ENV *m, uint8 d, uint8 s) +{ + uint8 res, cnt; + uint8 mask, cf, ocf = 0; + /* rotate right through carry */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + have + CF B_7 B_6 B_5 B_4 B_3 B_2 B_1 B_0 + The new rotate is done mod 9, and given this, + for a rotation of n bits (mod 9) the new carry flag is + then located n bits from the LSB. The low part is + then shifted up cnt bits, and the high part is or'd + in. Using CAPS for new values, and lowercase for the + original values, this can be expressed as: + IF n > 0 + 1) CF <- b_(n-1) + 2) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) + 3) B_(8-n) <- cf + 4) B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) + I think this is correct. + */ + res = d; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 9)) /* not a typo, do nada if cnt==0 */ + { + /* extract the new CARRY FLAG. */ + /* CF <- b_(n-1) */ + if (cnt == 1) + { + cf = d & 0x1; + /* note hackery here. Access_flag(..) evaluates to either + 0 if flag not set + non-zero if flag is set. + doing access_flag(..) != 0 casts that into either + 0..1 in any representation of the flags register + (i.e. packed bit array or unpacked.) + */ + ocf = ACCESS_FLAG(m,F_CF) != 0; + } + else + cf = (d >> (cnt-1)) & 0x1; + /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_n */ + /* note that the right hand side done by the mask + This is effectively done by shifting the + object to the right. The result must be masked, + in case the object came in and was treated + as a negative number. Needed???*/ + mask = (1<<(8-cnt))-1; + res = (d >> cnt) & mask; + /* now the high stuff which rotated around + into the positions B_cnt-2 .. B_0 */ + /* B_(7) .. B_(8-(n-1)) <- b_(n-2) .. b_(0) */ + /* shift it downward, 7-(n-2) = 9-n positions. + and mask off the result before or'ing in. + */ + res |= (d << (9-cnt)); + /* if the carry flag was set, or it in. */ + if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ + { + /* B_(8-n) <- cf */ + res |= 1 << (8 - cnt); + } + /* set the new carry flag, based on the variable "cf" */ + CONDITIONAL_SET_FLAG(cf, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + /* parenthesized... */ + if (cnt == 1) + { /* [JCE] Explicit braces to stop gcc -Wall moaning */ + CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>6)&0x2)], + m, F_OF); + } + } + return res; +} + +uint16 rcr_word(PC_ENV *m, uint16 d, uint16 s) +{ + uint16 res, cnt; + uint16 mask, cf, ocf = 0; + /* rotate right through carry */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + have + CF B_15 ... B_0 + The new rotate is done mod 17, and given this, + for a rotation of n bits (mod 17) the new carry flag is + then located n bits from the LSB. The low part is + then shifted up cnt bits, and the high part is or'd + in. Using CAPS for new values, and lowercase for the + original values, this can be expressed as: + IF n > 0 + 1) CF <- b_(n-1) + 2) B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) + 3) B_(16-n) <- cf + 4) B_(15) .. B_(16-(n-1)) <- b_(n-2) .. b_(0) + I think this is correct. + */ + res = d; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 17)) /* not a typo, do nada if cnt==0 */ + { + /* extract the new CARRY FLAG. */ + /* CF <- b_(n-1) */ + if (cnt==1) + { + cf = d & 0x1; + /* see note above on teh byte version */ + ocf = ACCESS_FLAG(m,F_CF) != 0; + } + else + cf = (d >> (cnt-1)) & 0x1; + /* B_(16-(n+1)) .. B_(0) <- b_(15) .. b_n */ + /* note that the right hand side done by the mask + This is effectively done by shifting the + object to the right. The result must be masked, + in case the object came in and was treated + as a negative number. Needed???*/ + mask = (1<<(16-cnt))-1; + res = (d >> cnt) & mask; + /* now the high stuff which rotated around + into the positions B_cnt-2 .. B_0 */ + /* B_(15) .. B_(16-(n-1)) <- b_(n-2) .. b_(0) */ + /* shift it downward, 15-(n-2) = 17-n positions. + and mask off the result before or'ing in. + */ + res |= (d << (17-cnt)); + /* if the carry flag was set, or it in. */ + if (ACCESS_FLAG(m,F_CF)) /* carry flag is set */ + { + /* B_(16-n) <- cf */ + res |= 1 << (16 - cnt); + } + /* set the new carry flag, based on the variable "cf" */ + CONDITIONAL_SET_FLAG(cf, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + if (cnt==1) + { /* [JCE] Explicit braces to stop gcc -Wall moaning */ + CONDITIONAL_SET_FLAG(xor_0x3_tab[ocf+((d>>14)&0x2)], + m, F_OF); + } + } + return res; +} + +uint8 rol_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint32 res, cnt, mask; + /* rotate left */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + have + CF B_7 ... B_0 + The new rotate is done mod 8. + Much simpler than the "rcl" or "rcr" operations. + IF n > 0 + 1) B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) + 2) B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) + I think this is correct. + */ + res =d; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 8)) /* not a typo, do nada if cnt==0 */ + { + /* B_(7) .. B_(n) <- b_(8-(n+1)) .. b_(0) */ + res = (d << cnt); + /* B_(n-1) .. B_(0) <- b_(7) .. b_(8-n) */ + mask = (1 << cnt) - 1; + res |= (d >> (8-cnt)) & mask; + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res&0x1, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + CONDITIONAL_SET_FLAG(cnt==1 && + xor_0x3_tab[(res&0x1)+((res>>6)&0x2)], + m, F_OF); + } + return res&0xff; +} + +uint16 rol_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res, cnt, mask; + /* rotate left */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + have + CF B_15 ... B_0 + The new rotate is done mod 8. + Much simpler than the "rcl" or "rcr" operations. + IF n > 0 + 1) B_(15) .. B_(n) <- b_(16-(n+1)) .. b_(0) + 2) B_(n-1) .. B_(0) <- b_(16) .. b_(16-n) + I think this is correct. + */ + res = d; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 16)) /* not a typo, do nada if cnt==0 */ + { + /* B_(16) .. B_(n) <- b_(16-(n+1)) .. b_(0) */ + res = (d << cnt); + /* B_(n-1) .. B_(0) <- b_(15) .. b_(16-n) */ + mask = (1 << cnt) - 1; + res |= (d >> (16-cnt)) & mask; + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res&0x1, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + CONDITIONAL_SET_FLAG(cnt==1 && + xor_0x3_tab[(res&0x1)+((res>>14)&0x2)], + m, F_OF); + } + return res&0xffff; +} + +uint8 ror_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint32 res, cnt, mask; + /* rotate right */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + have + B_7 ... B_0 + The rotate is done mod 8. + IF n > 0 + 1) B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) + 2) B_(7) .. B_(8-n) <- b_(n-1) .. b_(0) + */ + res = d; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 8)) /* not a typo, do nada if cnt==0 */ + { + /* B_(7) .. B_(8-n) <- b_(n-1) .. b_(0)*/ + res = (d << (8-cnt)); + /* B_(8-(n+1)) .. B_(0) <- b_(7) .. b_(n) */ + mask = (1 << (8-cnt)) - 1; + res |= (d >> (cnt)) & mask; + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res&0x80, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of the two most significant bits. Blecck. */ + CONDITIONAL_SET_FLAG(cnt==1 && + xor_0x3_tab[(res>>6)&0x3], + m, F_OF); + } + return res&0xff; +} + +uint16 ror_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res, cnt, mask; + /* rotate right */ + /* + s is the rotate distance. It varies from 0 - 8. + d is the byte object rotated. + have + B_15 ... B_0 + The rotate is done mod 16. + IF n > 0 + 1) B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) + 2) B_(15) .. B_(16-n) <- b_(n-1) .. b_(0) + I think this is correct. + */ + res =d ; + /* [JCE] Extra brackets to stop gcc -Wall moaning */ + if ((cnt = s % 16)) /* not a typo, do nada if cnt==0 */ + { + /* B_(15) .. B_(16-n) <- b_(n-1) .. b_(0)*/ + res = (d << (16-cnt)); + /* B_(16-(n+1)) .. B_(0) <- b_(15) .. b_(n) */ + mask = (1 << (16-cnt)) - 1; + res |= (d >> (cnt)) & mask; + /* set the new carry flag, Note that it is the low order + bit of the result!!! */ + CONDITIONAL_SET_FLAG(res&0x8000, m, F_CF); + /* OVERFLOW is set *IFF* cnt==1, then it is the + xor of CF and the most significant bit. Blecck. */ + CONDITIONAL_SET_FLAG(cnt==1 && + xor_0x3_tab[(res>>14)&0x3], + m, F_OF); + } + return res & 0xffff; +} + +uint8 shl_byte(PC_ENV *m, uint8 d, uint8 s) +{ + uint32 cnt,res,cf; + if (s < 8) + { + cnt = s % 8; + /* last bit shifted out goes into carry flag */ + if (cnt>0) + { + res = d << cnt; + cf = d & (1<<(8-cnt)); + CONDITIONAL_SET_FLAG(cf, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + } + else + { + res = (uint8)d; + } + if (cnt == 1) + { + /* Needs simplification. */ + CONDITIONAL_SET_FLAG( + (((res&0x80)==0x80) ^ + (ACCESS_FLAG(m,F_CF) != 0)) , + /* was (m->R_FLG&F_CF)==F_CF)), */ + m, F_OF); + } + else + { + CLEAR_FLAG(m,F_OF); + } + } + else + { + res = 0; + CLEAR_FLAG(m,F_CF); + CLEAR_FLAG(m,F_OF); + CLEAR_FLAG(m,F_SF); + CLEAR_FLAG(m,F_PF); + SET_FLAG(m,F_ZF); + } + return res&0xff; +} + +uint16 shl_word(PC_ENV *m, uint16 d, uint16 s) +{ + uint32 cnt,res,cf; + if (s < 16) + { + cnt = s % 16; + if (cnt > 0) + { + res = d << cnt; + /* last bit shifted out goes into carry flag */ + cf = d & (1<<(16-cnt)); + CONDITIONAL_SET_FLAG(cf, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + } + else + { + res = (uint16)d; + } + if (cnt == 1) + { + /* Needs simplification. */ + CONDITIONAL_SET_FLAG( + (((res&0x8000)==0x8000) ^ + (ACCESS_FLAG(m,F_CF) != 0)), + /*((m&F_CF)==F_CF)),*/ + m, F_OF); + } + else + { + CLEAR_FLAG(m,F_OF); + } + } + else + { + res = 0; + CLEAR_FLAG(m,F_CF); + CLEAR_FLAG(m,F_OF); + SET_FLAG(m,F_ZF); + CLEAR_FLAG(m,F_SF); + CLEAR_FLAG(m,F_PF); + } + return res&0xffff; +} + +uint8 shr_byte(PC_ENV *m, uint8 d, uint8 s) +{ + uint32 cnt,res,cf,mask; + if (s < 8) + { + cnt = s % 8; + if (cnt > 0) + { + mask = (1<<(8-cnt))-1; + cf = d & (1<<(cnt-1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + } + else + { + res = (uint8) d; + } + if (cnt == 1) + { + CONDITIONAL_SET_FLAG( + xor_0x3_tab[(res>>6)&0x3], m, F_OF); + } + else + { + CLEAR_FLAG(m,F_OF); + } + } + else + { + res = 0; + CLEAR_FLAG(m,F_CF); + CLEAR_FLAG(m,F_OF); + SET_FLAG(m,F_ZF); + CLEAR_FLAG(m,F_SF); + CLEAR_FLAG(m,F_PF); + } + return res&0xff; +} + +uint16 shr_word(PC_ENV *m, uint16 d, uint16 s) +{ + uint32 cnt,res,cf,mask; + res = d; + if (s < 16) + { + cnt = s % 16; + if (cnt > 0) + { + mask = (1<<(16-cnt))-1; + cf = d & (1<<(cnt-1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, m, F_CF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + } + else + { + res = d; + } + if (cnt == 1) + { + CONDITIONAL_SET_FLAG( + xor_0x3_tab[(res>>14)&0x3], m, F_OF); + } + else + { + CLEAR_FLAG(m,F_OF); + } + } + else + { + res = 0; + CLEAR_FLAG(m,F_CF); + CLEAR_FLAG(m,F_OF); + SET_FLAG(m,F_ZF); + CLEAR_FLAG(m,F_SF); + CLEAR_FLAG(m,F_PF); + } + return res&0xffff; +} + +/* XXXX ??? flags may be wrong??? */ +uint8 sar_byte(PC_ENV *m, uint8 d, uint8 s) +{ + uint32 cnt,res,cf,mask,sf; + res = d; + sf = d & 0x80; + cnt = s % 8; + if(cnt > 0 && cnt < 8) + { + mask = (1<<(8-cnt))-1; + cf = d & (1<<(cnt-1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, m, F_CF); + if (sf) + { + res |= ~mask; + } + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + CONDITIONAL_SET_FLAG(res & 0x80, m, F_SF); + } + else if (cnt >= 8) + { + if (sf) + { + res = 0xff; + SET_FLAG(m,F_CF); + CLEAR_FLAG(m,F_ZF); + SET_FLAG(m, F_SF); + SET_FLAG(m, F_PF); + } + else + { + res = 0; + CLEAR_FLAG(m,F_CF); + SET_FLAG(m,F_ZF); + CLEAR_FLAG(m, F_SF); + CLEAR_FLAG(m, F_PF); + } + } + return res&0xff; +} + +uint16 sar_word(PC_ENV *m, uint16 d, uint16 s) +{ + uint32 cnt, res, cf, mask, sf; + sf = d & 0x8000; + cnt = s % 16; + res = d; + if (cnt > 0 && cnt < 16) + { + mask = (1<<(16-cnt))-1; + cf = d & (1<<(cnt-1)); + res = (d >> cnt) & mask; + CONDITIONAL_SET_FLAG(cf, m, F_CF); + if (sf) + { + res |= ~mask; + } + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(res & 0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + } + else if (cnt >= 16) + { + if (sf) + { + res = 0xffff; + SET_FLAG(m,F_CF); + CLEAR_FLAG(m,F_ZF); + SET_FLAG(m, F_SF); + SET_FLAG(m, F_PF); + } + else + { + res = 0; + CLEAR_FLAG(m,F_CF); + SET_FLAG(m,F_ZF); + CLEAR_FLAG(m, F_SF); + CLEAR_FLAG(m, F_PF); + } + } + return res&0xffff; +} + +uint8 sbb_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + if (ACCESS_FLAG(m,F_CF) ) + res = d - s - 1; + else + res = d - s; + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res & 0xff; +} + +uint16 sbb_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + if (ACCESS_FLAG(m,F_CF)) + res = d - s - 1; + else + res = d - s; + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res & 0xffff; +} + +uint8 sub_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + res = d - s; + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + CONDITIONAL_SET_FLAG(bc&0x80,m, F_CF); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>6)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res & 0xff; +} + +uint16 sub_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res; /* all operands in native machine order */ + register uint32 bc; + res = d - s; + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG((res&0xffff)==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* calculate the borrow chain. See note at top */ + bc= (res&(~d|s))|(~d&s); + CONDITIONAL_SET_FLAG(bc&0x8000,m, F_CF); + CONDITIONAL_SET_FLAG(xor_0x3_tab[(bc>>14)&0x3], m, F_OF); + CONDITIONAL_SET_FLAG(bc&0x8, m, F_AF); + return res & 0xffff; +} + +void test_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint32 res; /* all operands in native machine order */ + res = d & s; + CLEAR_FLAG(m, F_OF); + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* AF == dont care*/ + CLEAR_FLAG(m, F_CF); +} + +void test_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint32 res; /* all operands in native machine order */ + res = d & s; + CLEAR_FLAG(m, F_OF); + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + /* AF == dont care*/ + CLEAR_FLAG(m, F_CF); +} + +uint8 xor_byte(PC_ENV *m, uint8 d, uint8 s) +{ + register uint8 res; /* all operands in native machine order */ + res = d ^ s; + CLEAR_FLAG(m, F_OF); + CONDITIONAL_SET_FLAG(res&0x80, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res], m, F_PF); + CLEAR_FLAG(m, F_CF); + return res; +} + +uint16 xor_word(PC_ENV *m, uint16 d, uint16 s) +{ + register uint16 res; /* all operands in native machine order */ + res = d ^ s; + /* set the carry flag to be bit 8 */ + CLEAR_FLAG(m, F_OF); + CONDITIONAL_SET_FLAG(res&0x8000, m, F_SF); + CONDITIONAL_SET_FLAG(res==0, m, F_ZF); + CONDITIONAL_SET_FLAG(parity_tab[res&0xff], m, F_PF); + CLEAR_FLAG(m, F_CF); + return res; +} + +void imul_byte(PC_ENV *m, uint8 s) +{ + int16 res = (int8)m->R_AL * (int8)s; + m->R_AX = res; + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(res&0x8000,m,F_SF); + CONDITIONAL_SET_FLAG(res==0,m,F_ZF); + if (m->R_AH == 0 || m->R_AH == 0xff) + { + CLEAR_FLAG(m, F_CF); + CLEAR_FLAG(m, F_OF); + } + else + { + SET_FLAG(m, F_CF); + SET_FLAG(m, F_OF); + } +} + +void imul_word(PC_ENV *m, uint16 s) +{ + int32 res = (int16)m->R_AX * (int16)s; + m->R_AX = res & 0xffff; + m->R_DX = (res >> 16) & 0xffff; + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(res&0x80000000,m,F_SF); + CONDITIONAL_SET_FLAG(res==0,m,F_ZF); + if (m->R_DX == 0 || m->R_DX == 0xffff) + { + CLEAR_FLAG(m, F_CF); + CLEAR_FLAG(m, F_OF); + } + else + { + SET_FLAG(m, F_CF); + SET_FLAG(m, F_OF); + } +} + +void mul_byte(PC_ENV *m, uint8 s) +{ + uint16 res = m->R_AL * s; + m->R_AX = res; + /* Undef --- Can't hurt */ + CLEAR_FLAG(m,F_SF); + CONDITIONAL_SET_FLAG(res==0,m,F_ZF); + if (m->R_AH == 0) + { + CLEAR_FLAG(m, F_CF); + CLEAR_FLAG(m, F_OF); + } + else + { + SET_FLAG(m, F_CF); + SET_FLAG(m, F_OF); + } +} + +void mul_word(PC_ENV *m, uint16 s) +{ + uint32 res = m->R_AX * s; + /* Undef --- Can't hurt */ + CLEAR_FLAG(m,F_SF); + CONDITIONAL_SET_FLAG(res==0,m,F_ZF); + m->R_AX = res & 0xffff; + m->R_DX = (res >> 16) & 0xffff; + if (m->R_DX == 0) + { + CLEAR_FLAG(m, F_CF); + CLEAR_FLAG(m, F_OF); + } + else + { + SET_FLAG(m, F_CF); + SET_FLAG(m, F_OF); + } +} + +void idiv_byte(PC_ENV *m, uint8 s) +{ + int32 dvd,div,mod; + dvd = (int16)m->R_AX; + if (s == 0) + { + i86_intr_raise(m,0); + return; + } + div = dvd / (int8)s; + mod = dvd % (int8)s; + if (abs(div) > 0x7f) + { + i86_intr_raise(m,0); + return; + } + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(div&0x80,m,F_SF); + CONDITIONAL_SET_FLAG(div==0,m,F_ZF); + m->R_AL = (int8)div; + m->R_AH = (int8)mod; +} + +void idiv_word(PC_ENV *m, uint16 s) +{ + int32 dvd,dvs,div,mod; + dvd = m->R_DX; + dvd = (dvd << 16) | m->R_AX; + if (s == 0) + { + i86_intr_raise(m,0); + return; + } + dvs = (int16)s; + div = dvd / dvs; + mod = dvd % dvs; + if (abs(div) > 0x7fff) + { + i86_intr_raise(m,0); + return; + } + /* Undef --- Can't hurt */ + CONDITIONAL_SET_FLAG(div&0x8000,m,F_SF); + CONDITIONAL_SET_FLAG(div==0,m,F_ZF); +/* debug_printf(m, "\n%d/%d=%d,%d\n",dvd,dvs,div,mod); */ + m->R_AX = div; + m->R_DX = mod; +} + +void div_byte(PC_ENV *m, uint8 s) +{ + uint32 dvd,dvs,div,mod; + dvs = s; + dvd = m->R_AX; + if (s == 0) + { + i86_intr_raise(m,0); + return; + } + div = dvd / dvs; + mod = dvd % dvs; + if (abs(div) > 0xff) + { + i86_intr_raise(m,0); + return; + } + /* Undef --- Can't hurt */ + CLEAR_FLAG(m,F_SF); + CONDITIONAL_SET_FLAG(div==0,m,F_ZF); + m->R_AL = (uint8)div; + m->R_AH = (uint8)mod; +} + +void div_word(PC_ENV *m, uint16 s) +{ + uint32 dvd,dvs,div,mod; + dvd = m->R_DX; + dvd = (dvd << 16) | m->R_AX; + dvs = s; + if (dvs == 0) + { + i86_intr_raise(m,0); + return; + } + div = dvd / dvs; + mod = dvd % dvs; +/* printf("dvd=%x dvs=%x -> div=%x mod=%x\n",dvd, dvs,div, mod);*/ + if (abs(div) > 0xffff) + { + i86_intr_raise(m,0); + return; + } + /* Undef --- Can't hurt */ + CLEAR_FLAG(m,F_SF); + CONDITIONAL_SET_FLAG(div==0,m,F_ZF); + m->R_AX = div; + m->R_DX = mod; +} diff --git a/AltairZ80/insns.h b/AltairZ80/insns.h new file mode 100644 index 0000000..f63c0d4 --- /dev/null +++ b/AltairZ80/insns.h @@ -0,0 +1,670 @@ +/* insns.h header file for insns.c + * $Id: insns.h,v 1.1 2004/02/11 19:01:38 perrin Exp $ + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + */ + +#ifndef NASM_INSNS_H +#define NASM_INSNS_H + +/* This file is auto-generated from insns.dat by insns.pl - don't edit it */ + +/* This file in included by nasm.h */ + +/* Instruction names */ +enum { + I_AAA, + I_AAD, + I_AAM, + I_AAS, + I_ADC, + I_ADD, + I_ADDPD, + I_ADDPS, + I_ADDSD, + I_ADDSS, + I_ADDSUBPD, + I_ADDSUBPS, + I_AND, + I_ANDNPD, + I_ANDNPS, + I_ANDPD, + I_ANDPS, + I_ARPL, + I_BOUND, + I_BSF, + I_BSR, + I_BSWAP, + I_BT, + I_BTC, + I_BTR, + I_BTS, + I_CALL, + I_CBW, + I_CDQ, + I_CLC, + I_CLD, + I_CLFLUSH, + I_CLI, + I_CLTS, + I_CMC, + I_CMP, + I_CMPEQPD, + I_CMPEQPS, + I_CMPEQSD, + I_CMPEQSS, + I_CMPLEPD, + I_CMPLEPS, + I_CMPLESD, + I_CMPLESS, + I_CMPLTPD, + I_CMPLTPS, + I_CMPLTSD, + I_CMPLTSS, + I_CMPNEQPD, + I_CMPNEQPS, + I_CMPNEQSD, + I_CMPNEQSS, + I_CMPNLEPD, + I_CMPNLEPS, + I_CMPNLESD, + I_CMPNLESS, + I_CMPNLTPD, + I_CMPNLTPS, + I_CMPNLTSD, + I_CMPNLTSS, + I_CMPORDPD, + I_CMPORDPS, + I_CMPORDSD, + I_CMPORDSS, + I_CMPPD, + I_CMPPS, + I_CMPSB, + I_CMPSD, + I_CMPSS, + I_CMPSW, + I_CMPUNORDPD, + I_CMPUNORDPS, + I_CMPUNORDSD, + I_CMPUNORDSS, + I_CMPXCHG, + I_CMPXCHG486, + I_CMPXCHG8B, + I_COMISD, + I_COMISS, + I_CPUID, + I_CVTDQ2PD, + I_CVTDQ2PS, + I_CVTPD2DQ, + I_CVTPD2PI, + I_CVTPD2PS, + I_CVTPI2PD, + I_CVTPI2PS, + I_CVTPS2DQ, + I_CVTPS2PD, + I_CVTPS2PI, + I_CVTSD2SI, + I_CVTSD2SS, + I_CVTSI2SD, + I_CVTSI2SS, + I_CVTSS2SD, + I_CVTSS2SI, + I_CVTTPD2DQ, + I_CVTTPD2PI, + I_CVTTPS2DQ, + I_CVTTPS2PI, + I_CVTTSD2SI, + I_CVTTSS2SI, + I_CWD, + I_CWDE, + I_DAA, + I_DAS, + I_DB, + I_DD, + I_DEC, + I_DIV, + I_DIVPD, + I_DIVPS, + I_DIVSD, + I_DIVSS, + I_DQ, + I_DT, + I_DW, + I_EMMS, + I_ENTER, + I_EQU, + I_F2XM1, + I_FABS, + I_FADD, + I_FADDP, + I_FBLD, + I_FBSTP, + I_FCHS, + I_FCLEX, + I_FCMOVB, + I_FCMOVBE, + I_FCMOVE, + I_FCMOVNB, + I_FCMOVNBE, + I_FCMOVNE, + I_FCMOVNU, + I_FCMOVU, + I_FCOM, + I_FCOMI, + I_FCOMIP, + I_FCOMP, + I_FCOMPP, + I_FCOS, + I_FDECSTP, + I_FDISI, + I_FDIV, + I_FDIVP, + I_FDIVR, + I_FDIVRP, + I_FEMMS, + I_FENI, + I_FFREE, + I_FFREEP, + I_FIADD, + I_FICOM, + I_FICOMP, + I_FIDIV, + I_FIDIVR, + I_FILD, + I_FIMUL, + I_FINCSTP, + I_FINIT, + I_FIST, + I_FISTP, + I_FISTTP, + I_FISUB, + I_FISUBR, + I_FLD, + I_FLD1, + I_FLDCW, + I_FLDENV, + I_FLDL2E, + I_FLDL2T, + I_FLDLG2, + I_FLDLN2, + I_FLDPI, + I_FLDZ, + I_FMUL, + I_FMULP, + I_FNCLEX, + I_FNDISI, + I_FNENI, + I_FNINIT, + I_FNOP, + I_FNSAVE, + I_FNSTCW, + I_FNSTENV, + I_FNSTSW, + I_FPATAN, + I_FPREM, + I_FPREM1, + I_FPTAN, + I_FRNDINT, + I_FRSTOR, + I_FSAVE, + I_FSCALE, + I_FSETPM, + I_FSIN, + I_FSINCOS, + I_FSQRT, + I_FST, + I_FSTCW, + I_FSTENV, + I_FSTP, + I_FSTSW, + I_FSUB, + I_FSUBP, + I_FSUBR, + I_FSUBRP, + I_FTST, + I_FUCOM, + I_FUCOMI, + I_FUCOMIP, + I_FUCOMP, + I_FUCOMPP, + I_FWAIT, + I_FXAM, + I_FXCH, + I_FXRSTOR, + I_FXSAVE, + I_FXTRACT, + I_FYL2X, + I_FYL2XP1, + I_HADDPD, + I_HADDPS, + I_HLT, + I_HSUBPD, + I_HSUBPS, + I_IBTS, + I_ICEBP, + I_IDIV, + I_IMUL, + I_IN, + I_INC, + I_INCBIN, + I_INSB, + I_INSD, + I_INSW, + I_INT, + I_INT01, + I_INT03, + I_INT1, + I_INT3, + I_INTO, + I_INVD, + I_INVLPG, + I_IRET, + I_IRETD, + I_IRETW, + I_JCXZ, + I_JECXZ, + I_JMP, + I_JMPE, + I_LAHF, + I_LAR, + I_LDDQU, + I_LDMXCSR, + I_LDS, + I_LEA, + I_LEAVE, + I_LES, + I_LFENCE, + I_LFS, + I_LGDT, + I_LGS, + I_LIDT, + I_LLDT, + I_LMSW, + I_LOADALL, + I_LOADALL286, + I_LODSB, + I_LODSD, + I_LODSW, + I_LOOP, + I_LOOPE, + I_LOOPNE, + I_LOOPNZ, + I_LOOPZ, + I_LSL, + I_LSS, + I_LTR, + I_MASKMOVDQU, + I_MASKMOVQ, + I_MAXPD, + I_MAXPS, + I_MAXSD, + I_MAXSS, + I_MFENCE, + I_MINPD, + I_MINPS, + I_MINSD, + I_MINSS, + I_MONITOR, + I_MOV, + I_MOVAPD, + I_MOVAPS, + I_MOVD, + I_MOVDDUP, + I_MOVDQ2Q, + I_MOVDQA, + I_MOVDQU, + I_MOVHLPS, + I_MOVHPD, + I_MOVHPS, + I_MOVLHPS, + I_MOVLPD, + I_MOVLPS, + I_MOVMSKPD, + I_MOVMSKPS, + I_MOVNTDQ, + I_MOVNTI, + I_MOVNTPD, + I_MOVNTPS, + I_MOVNTQ, + I_MOVQ, + I_MOVQ2DQ, + I_MOVSB, + I_MOVSD, + I_MOVSHDUP, + I_MOVSLDUP, + I_MOVSS, + I_MOVSW, + I_MOVSX, + I_MOVUPD, + I_MOVUPS, + I_MOVZX, + I_MUL, + I_MULPD, + I_MULPS, + I_MULSD, + I_MULSS, + I_MWAIT, + I_NEG, + I_NOP, + I_NOT, + I_OR, + I_ORPD, + I_ORPS, + I_OUT, + I_OUTSB, + I_OUTSD, + I_OUTSW, + I_PACKSSDW, + I_PACKSSWB, + I_PACKUSWB, + I_PADDB, + I_PADDD, + I_PADDQ, + I_PADDSB, + I_PADDSIW, + I_PADDSW, + I_PADDUSB, + I_PADDUSW, + I_PADDW, + I_PAND, + I_PANDN, + I_PAUSE, + I_PAVEB, + I_PAVGB, + I_PAVGUSB, + I_PAVGW, + I_PCMPEQB, + I_PCMPEQD, + I_PCMPEQW, + I_PCMPGTB, + I_PCMPGTD, + I_PCMPGTW, + I_PDISTIB, + I_PEXTRW, + I_PF2ID, + I_PF2IW, + I_PFACC, + I_PFADD, + I_PFCMPEQ, + I_PFCMPGE, + I_PFCMPGT, + I_PFMAX, + I_PFMIN, + I_PFMUL, + I_PFNACC, + I_PFPNACC, + I_PFRCP, + I_PFRCPIT1, + I_PFRCPIT2, + I_PFRSQIT1, + I_PFRSQRT, + I_PFSUB, + I_PFSUBR, + I_PI2FD, + I_PI2FW, + I_PINSRW, + I_PMACHRIW, + I_PMADDWD, + I_PMAGW, + I_PMAXSW, + I_PMAXUB, + I_PMINSW, + I_PMINUB, + I_PMOVMSKB, + I_PMULHRIW, + I_PMULHRWA, + I_PMULHRWC, + I_PMULHUW, + I_PMULHW, + I_PMULLW, + I_PMULUDQ, + I_PMVGEZB, + I_PMVLZB, + I_PMVNZB, + I_PMVZB, + I_POP, + I_POPA, + I_POPAD, + I_POPAW, + I_POPF, + I_POPFD, + I_POPFW, + I_POR, + I_PREFETCH, + I_PREFETCHNTA, + I_PREFETCHT0, + I_PREFETCHT1, + I_PREFETCHT2, + I_PREFETCHW, + I_PSADBW, + I_PSHUFD, + I_PSHUFHW, + I_PSHUFLW, + I_PSHUFW, + I_PSLLD, + I_PSLLDQ, + I_PSLLQ, + I_PSLLW, + I_PSRAD, + I_PSRAW, + I_PSRLD, + I_PSRLDQ, + I_PSRLQ, + I_PSRLW, + I_PSUBB, + I_PSUBD, + I_PSUBQ, + I_PSUBSB, + I_PSUBSIW, + I_PSUBSW, + I_PSUBUSB, + I_PSUBUSW, + I_PSUBW, + I_PSWAPD, + I_PUNPCKHBW, + I_PUNPCKHDQ, + I_PUNPCKHQDQ, + I_PUNPCKHWD, + I_PUNPCKLBW, + I_PUNPCKLDQ, + I_PUNPCKLQDQ, + I_PUNPCKLWD, + I_PUSH, + I_PUSHA, + I_PUSHAD, + I_PUSHAW, + I_PUSHF, + I_PUSHFD, + I_PUSHFW, + I_PXOR, + I_RCL, + I_RCPPS, + I_RCPSS, + I_RCR, + I_RDMSR, + I_RDPMC, + I_RDSHR, + I_RDTSC, + I_RESB, + I_RESD, + I_RESQ, + I_REST, + I_RESW, + I_RET, + I_RETF, + I_RETN, + I_ROL, + I_ROR, + I_RSDC, + I_RSLDT, + I_RSM, + I_RSQRTPS, + I_RSQRTSS, + I_RSTS, + I_SAHF, + I_SAL, + I_SALC, + I_SAR, + I_SBB, + I_SCASB, + I_SCASD, + I_SCASW, + I_SFENCE, + I_SGDT, + I_SHL, + I_SHLD, + I_SHR, + I_SHRD, + I_SHUFPD, + I_SHUFPS, + I_SIDT, + I_SLDT, + I_SMI, + I_SMINT, + I_SMINTOLD, + I_SMSW, + I_SQRTPD, + I_SQRTPS, + I_SQRTSD, + I_SQRTSS, + I_STC, + I_STD, + I_STI, + I_STMXCSR, + I_STOSB, + I_STOSD, + I_STOSW, + I_STR, + I_SUB, + I_SUBPD, + I_SUBPS, + I_SUBSD, + I_SUBSS, + I_SVDC, + I_SVLDT, + I_SVTS, + I_SYSCALL, + I_SYSENTER, + I_SYSEXIT, + I_SYSRET, + I_TEST, + I_UCOMISD, + I_UCOMISS, + I_UD0, + I_UD1, + I_UD2, + I_UMOV, + I_UNPCKHPD, + I_UNPCKHPS, + I_UNPCKLPD, + I_UNPCKLPS, + I_VERR, + I_VERW, + I_WAIT, + I_WBINVD, + I_WRMSR, + I_WRSHR, + I_XADD, + I_XBTS, + I_XCHG, + I_XLAT, + I_XLATB, + I_XOR, + I_XORPD, + I_XORPS, + I_XSTORE, + I_CMOVcc, + I_Jcc, + I_SETcc +}; + +#define MAX_INSLEN 11 + +/* max length of any instruction, register name etc. */ +#if MAX_INSLEN > 9 /* MAX_INSLEN defined in insnsi.h */ +#define MAX_KEYWORD MAX_INSLEN +#else +#define MAX_KEYWORD 9 +#endif + +struct itemplate { + int opcode; /* the token, passed from "parser.c" */ + int operands; /* number of operands */ + long opd[3]; /* bit flags for operand types */ + const char *code; /* the code it assembles to */ + unsigned long flags; /* some flags */ +}; + +/* + * this define is used to signify the end of an itemplate + */ +#define ITEMPLATE_END {-1,-1,{-1,-1,-1},NULL,0} + +/* + * Instruction template flags. These specify which processor + * targets the instruction is eligible for, whether it is + * privileged or undocumented, and also specify extra error + * checking on the matching of the instruction. + * + * IF_SM stands for Size Match: any operand whose size is not + * explicitly specified by the template is `really' intended to be + * the same size as the first size-specified operand. + * Non-specification is tolerated in the input instruction, but + * _wrong_ specification is not. + * + * IF_SM2 invokes Size Match on only the first _two_ operands, for + * three-operand instructions such as SHLD: it implies that the + * first two operands must match in size, but that the third is + * required to be _unspecified_. + * + * IF_SB invokes Size Byte: operands with unspecified size in the + * template are really bytes, and so no non-byte specification in + * the input instruction will be tolerated. IF_SW similarly invokes + * Size Word, and IF_SD invokes Size Doubleword. + * + * (The default state if neither IF_SM nor IF_SM2 is specified is + * that any operand with unspecified size in the template is + * required to have unspecified size in the instruction too...) + */ + +#define IF_SM 0x00000001UL /* size match */ +#define IF_SM2 0x00000002UL /* size match first two operands */ +#define IF_SB 0x00000004UL /* unsized operands can't be non-byte */ +#define IF_SW 0x00000008UL /* unsized operands can't be non-word */ +#define IF_SD 0x00000010UL /* unsized operands can't be nondword */ +#define IF_AR0 0x00000020UL /* SB, SW, SD applies to argument 0 */ +#define IF_AR1 0x00000040UL /* SB, SW, SD applies to argument 1 */ +#define IF_AR2 0x00000060UL /* SB, SW, SD applies to argument 2 */ +#define IF_ARMASK 0x00000060UL /* mask for unsized argument spec */ +#define IF_PRIV 0x00000100UL /* it's a privileged instruction */ +#define IF_SMM 0x00000200UL /* it's only valid in SMM */ +#define IF_PROT 0x00000400UL /* it's protected mode only */ +#define IF_UNDOC 0x00001000UL /* it's an undocumented instruction */ +#define IF_FPU 0x00002000UL /* it's an FPU instruction */ +#define IF_MMX 0x00004000UL /* it's an MMX instruction */ +#define IF_3DNOW 0x00008000UL /* it's a 3DNow! instruction */ +#define IF_SSE 0x00010000UL /* it's a SSE (KNI, MMX2) instruction */ +#define IF_SSE2 0x00020000UL /* it's a SSE2 instruction */ +#define IF_SSE3 0x00040000UL /* it's a SSE3 (PNI) instruction */ +#define IF_PMASK 0xFF000000UL /* the mask for processor types */ +#define IF_PLEVEL 0x0F000000UL /* the mask for processor instr. level */ + /* also the highest possible processor */ +#define IF_PFMASK 0xF001FF00UL /* the mask for disassembly "prefer" */ +#define IF_8086 0x00000000UL /* 8086 instruction */ +#define IF_186 0x01000000UL /* 186+ instruction */ +#define IF_286 0x02000000UL /* 286+ instruction */ +#define IF_386 0x03000000UL /* 386+ instruction */ +#define IF_486 0x04000000UL /* 486+ instruction */ +#define IF_PENT 0x05000000UL /* Pentium instruction */ +#define IF_P6 0x06000000UL /* P6 instruction */ +#define IF_KATMAI 0x07000000UL /* Katmai instructions */ +#define IF_WILLAMETTE 0x08000000UL /* Willamette instructions */ +#define IF_PRESCOTT 0x09000000UL /* Prescott instructions */ +#define IF_IA64 0x0F000000UL /* IA64 instructions */ +#define IF_CYRIX 0x10000000UL /* Cyrix-specific instruction */ +#define IF_AMD 0x20000000UL /* AMD-specific instruction */ + +#endif diff --git a/AltairZ80/insnsa.c b/AltairZ80/insnsa.c new file mode 100644 index 0000000..ba4d70d --- /dev/null +++ b/AltairZ80/insnsa.c @@ -0,0 +1,4443 @@ +/* This file auto-generated from insns.dat by insns.pl - don't edit it */ + +#include "nasm.h" +#include "insns.h" + +static struct itemplate instrux_AAA[] = { + {I_AAA, 0, {0,0,0}, "\1\x37", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_AAD[] = { + {I_AAD, 0, {0,0,0}, "\2\xD5\x0A", IF_8086}, + {I_AAD, 1, {IMMEDIATE,0,0}, "\1\xD5\24", IF_8086|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_AAM[] = { + {I_AAM, 0, {0,0,0}, "\2\xD4\x0A", IF_8086}, + {I_AAM, 1, {IMMEDIATE,0,0}, "\1\xD4\24", IF_8086|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_AAS[] = { + {I_AAS, 0, {0,0,0}, "\1\x3F", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADC[] = { + {I_ADC, 2, {MEMORY,REG8,0}, "\300\1\x10\101", IF_8086|IF_SM}, + {I_ADC, 2, {REG8,REG8,0}, "\1\x10\101", IF_8086}, + {I_ADC, 2, {MEMORY,REG16,0}, "\320\300\1\x11\101", IF_8086|IF_SM}, + {I_ADC, 2, {REG16,REG16,0}, "\320\1\x11\101", IF_8086}, + {I_ADC, 2, {MEMORY,REG32,0}, "\321\300\1\x11\101", IF_386|IF_SM}, + {I_ADC, 2, {REG32,REG32,0}, "\321\1\x11\101", IF_386}, + {I_ADC, 2, {REG8,MEMORY,0}, "\301\1\x12\110", IF_8086|IF_SM}, + {I_ADC, 2, {REG8,REG8,0}, "\1\x12\110", IF_8086}, + {I_ADC, 2, {REG16,MEMORY,0}, "\320\301\1\x13\110", IF_8086|IF_SM}, + {I_ADC, 2, {REG16,REG16,0}, "\320\1\x13\110", IF_8086}, + {I_ADC, 2, {REG32,MEMORY,0}, "\321\301\1\x13\110", IF_386|IF_SM}, + {I_ADC, 2, {REG32,REG32,0}, "\321\1\x13\110", IF_386}, + {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\202\15", IF_8086}, + {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\202\15", IF_386}, + {I_ADC, 2, {REG_AL,IMMEDIATE,0}, "\1\x14\21", IF_8086|IF_SM}, + {I_ADC, 2, {REG_AX,SBYTE,0}, "\320\1\x83\202\15", IF_8086|IF_SM}, + {I_ADC, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x15\31", IF_8086|IF_SM}, + {I_ADC, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\202\15", IF_386|IF_SM}, + {I_ADC, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x15\41", IF_386|IF_SM}, + {I_ADC, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, + {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, + {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, + {I_ADC, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, + {I_ADC, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, + {I_ADC, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADD[] = { + {I_ADD, 2, {MEMORY,REG8,0}, "\300\17\101", IF_8086|IF_SM}, + {I_ADD, 2, {REG8,REG8,0}, "\17\101", IF_8086}, + {I_ADD, 2, {MEMORY,REG16,0}, "\320\300\1\x01\101", IF_8086|IF_SM}, + {I_ADD, 2, {REG16,REG16,0}, "\320\1\x01\101", IF_8086}, + {I_ADD, 2, {MEMORY,REG32,0}, "\321\300\1\x01\101", IF_386|IF_SM}, + {I_ADD, 2, {REG32,REG32,0}, "\321\1\x01\101", IF_386}, + {I_ADD, 2, {REG8,MEMORY,0}, "\301\1\x02\110", IF_8086|IF_SM}, + {I_ADD, 2, {REG8,REG8,0}, "\1\x02\110", IF_8086}, + {I_ADD, 2, {REG16,MEMORY,0}, "\320\301\1\x03\110", IF_8086|IF_SM}, + {I_ADD, 2, {REG16,REG16,0}, "\320\1\x03\110", IF_8086}, + {I_ADD, 2, {REG32,MEMORY,0}, "\321\301\1\x03\110", IF_386|IF_SM}, + {I_ADD, 2, {REG32,REG32,0}, "\321\1\x03\110", IF_386}, + {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\200\15", IF_8086}, + {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\200\15", IF_386}, + {I_ADD, 2, {REG_AL,IMMEDIATE,0}, "\1\x04\21", IF_8086|IF_SM}, + {I_ADD, 2, {REG_AX,SBYTE,0}, "\320\1\x83\200\15", IF_8086|IF_SM}, + {I_ADD, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x05\31", IF_8086|IF_SM}, + {I_ADD, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\200\15", IF_386|IF_SM}, + {I_ADD, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x05\41", IF_386|IF_SM}, + {I_ADD, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, + {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, + {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, + {I_ADD, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, + {I_ADD, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, + {I_ADD, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADDPD[] = { + {I_ADDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, + {I_ADDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADDPS[] = { + {I_ADDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + {I_ADDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADDSD[] = { + {I_ADDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, + {I_ADDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADDSS[] = { + {I_ADDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + {I_ADDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADDSUBPD[] = { + {I_ADDSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_ADDSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ADDSUBPS[] = { + {I_ADDSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_ADDSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_AND[] = { + {I_AND, 2, {MEMORY,REG8,0}, "\300\1\x20\101", IF_8086|IF_SM}, + {I_AND, 2, {REG8,REG8,0}, "\1\x20\101", IF_8086}, + {I_AND, 2, {MEMORY,REG16,0}, "\320\300\1\x21\101", IF_8086|IF_SM}, + {I_AND, 2, {REG16,REG16,0}, "\320\1\x21\101", IF_8086}, + {I_AND, 2, {MEMORY,REG32,0}, "\321\300\1\x21\101", IF_386|IF_SM}, + {I_AND, 2, {REG32,REG32,0}, "\321\1\x21\101", IF_386}, + {I_AND, 2, {REG8,MEMORY,0}, "\301\1\x22\110", IF_8086|IF_SM}, + {I_AND, 2, {REG8,REG8,0}, "\1\x22\110", IF_8086}, + {I_AND, 2, {REG16,MEMORY,0}, "\320\301\1\x23\110", IF_8086|IF_SM}, + {I_AND, 2, {REG16,REG16,0}, "\320\1\x23\110", IF_8086}, + {I_AND, 2, {REG32,MEMORY,0}, "\321\301\1\x23\110", IF_386|IF_SM}, + {I_AND, 2, {REG32,REG32,0}, "\321\1\x23\110", IF_386}, + {I_AND, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\204\15", IF_8086}, + {I_AND, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\204\15", IF_386}, + {I_AND, 2, {REG_AL,IMMEDIATE,0}, "\1\x24\21", IF_8086|IF_SM}, + {I_AND, 2, {REG_AX,SBYTE,0}, "\320\1\x83\204\15", IF_8086|IF_SM}, + {I_AND, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x25\31", IF_8086|IF_SM}, + {I_AND, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\204\15", IF_386|IF_SM}, + {I_AND, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x25\41", IF_386|IF_SM}, + {I_AND, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, + {I_AND, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, + {I_AND, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, + {I_AND, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, + {I_AND, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, + {I_AND, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ANDNPD[] = { + {I_ANDNPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2}, + {I_ANDNPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ANDNPS[] = { + {I_ANDNPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_KATMAI|IF_SSE}, + {I_ANDNPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x55\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ANDPD[] = { + {I_ANDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2}, + {I_ANDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ANDPS[] = { + {I_ANDPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_KATMAI|IF_SSE}, + {I_ANDPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x54\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ARPL[] = { + {I_ARPL, 2, {MEMORY,REG16,0}, "\300\1\x63\101", IF_286|IF_PROT|IF_SM}, + {I_ARPL, 2, {REG16,REG16,0}, "\1\x63\101", IF_286|IF_PROT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BOUND[] = { + {I_BOUND, 2, {REG16,MEMORY,0}, "\320\301\1\x62\110", IF_186}, + {I_BOUND, 2, {REG32,MEMORY,0}, "\321\301\1\x62\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BSF[] = { + {I_BSF, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBC\110", IF_386|IF_SM}, + {I_BSF, 2, {REG16,REG16,0}, "\320\2\x0F\xBC\110", IF_386}, + {I_BSF, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBC\110", IF_386|IF_SM}, + {I_BSF, 2, {REG32,REG32,0}, "\321\2\x0F\xBC\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BSR[] = { + {I_BSR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBD\110", IF_386|IF_SM}, + {I_BSR, 2, {REG16,REG16,0}, "\320\2\x0F\xBD\110", IF_386}, + {I_BSR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBD\110", IF_386|IF_SM}, + {I_BSR, 2, {REG32,REG32,0}, "\321\2\x0F\xBD\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BSWAP[] = { + {I_BSWAP, 1, {REG32,0,0}, "\321\1\x0F\10\xC8", IF_486}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BT[] = { + {I_BT, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA3\101", IF_386|IF_SM}, + {I_BT, 2, {REG16,REG16,0}, "\320\2\x0F\xA3\101", IF_386}, + {I_BT, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA3\101", IF_386|IF_SM}, + {I_BT, 2, {REG32,REG32,0}, "\321\2\x0F\xA3\101", IF_386}, + {I_BT, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\204\25", IF_386|IF_SB}, + {I_BT, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\204\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BTC[] = { + {I_BTC, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xBB\101", IF_386|IF_SM}, + {I_BTC, 2, {REG16,REG16,0}, "\320\2\x0F\xBB\101", IF_386}, + {I_BTC, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xBB\101", IF_386|IF_SM}, + {I_BTC, 2, {REG32,REG32,0}, "\321\2\x0F\xBB\101", IF_386}, + {I_BTC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\207\25", IF_386|IF_SB}, + {I_BTC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\207\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BTR[] = { + {I_BTR, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB3\101", IF_386|IF_SM}, + {I_BTR, 2, {REG16,REG16,0}, "\320\2\x0F\xB3\101", IF_386}, + {I_BTR, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB3\101", IF_386|IF_SM}, + {I_BTR, 2, {REG32,REG32,0}, "\321\2\x0F\xB3\101", IF_386}, + {I_BTR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\206\25", IF_386|IF_SB}, + {I_BTR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\206\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_BTS[] = { + {I_BTS, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xAB\101", IF_386|IF_SM}, + {I_BTS, 2, {REG16,REG16,0}, "\320\2\x0F\xAB\101", IF_386}, + {I_BTS, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xAB\101", IF_386|IF_SM}, + {I_BTS, 2, {REG32,REG32,0}, "\321\2\x0F\xAB\101", IF_386}, + {I_BTS, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\205\25", IF_386|IF_SB}, + {I_BTS, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\205\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CALL[] = { + {I_CALL, 1, {IMMEDIATE,0,0}, "\322\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|FAR,0,0}, "\322\1\x9A\34\37", IF_8086}, + {I_CALL, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|BITS16|FAR,0,0}, "\320\1\x9A\34\37", IF_8086}, + {I_CALL, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE8\64", IF_386}, + {I_CALL, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\xE8\64", IF_386}, + {I_CALL, 1, {IMMEDIATE|BITS32|FAR,0,0}, "\321\1\x9A\34\37", IF_386}, + {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\x9A\35\30", IF_8086}, + {I_CALL, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\x9A\31\30", IF_8086}, + {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\x9A\31\30", IF_8086}, + {I_CALL, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\x9A\41\30", IF_386}, + {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\x9A\41\30", IF_386}, + {I_CALL, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\203", IF_8086}, + {I_CALL, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\203", IF_8086}, + {I_CALL, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\203", IF_386}, + {I_CALL, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\202", IF_386}, + {I_CALL, 1, {REG16,0,0}, "\320\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {REG32,0,0}, "\321\300\1\xFF\202", IF_386}, + {I_CALL, 1, {MEMORY,0,0}, "\322\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\202", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CBW[] = { + {I_CBW, 0, {0,0,0}, "\320\1\x98", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CDQ[] = { + {I_CDQ, 0, {0,0,0}, "\321\1\x99", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CLC[] = { + {I_CLC, 0, {0,0,0}, "\1\xF8", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CLD[] = { + {I_CLD, 0, {0,0,0}, "\1\xFC", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CLFLUSH[] = { + {I_CLFLUSH, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\207", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CLI[] = { + {I_CLI, 0, {0,0,0}, "\1\xFA", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CLTS[] = { + {I_CLTS, 0, {0,0,0}, "\2\x0F\x06", IF_286|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMC[] = { + {I_CMC, 0, {0,0,0}, "\1\xF5", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMP[] = { + {I_CMP, 2, {MEMORY,REG8,0}, "\300\1\x38\101", IF_8086|IF_SM}, + {I_CMP, 2, {REG8,REG8,0}, "\1\x38\101", IF_8086}, + {I_CMP, 2, {MEMORY,REG16,0}, "\320\300\1\x39\101", IF_8086|IF_SM}, + {I_CMP, 2, {REG16,REG16,0}, "\320\1\x39\101", IF_8086}, + {I_CMP, 2, {MEMORY,REG32,0}, "\321\300\1\x39\101", IF_386|IF_SM}, + {I_CMP, 2, {REG32,REG32,0}, "\321\1\x39\101", IF_386}, + {I_CMP, 2, {REG8,MEMORY,0}, "\301\1\x3A\110", IF_8086|IF_SM}, + {I_CMP, 2, {REG8,REG8,0}, "\1\x3A\110", IF_8086}, + {I_CMP, 2, {REG16,MEMORY,0}, "\320\301\1\x3B\110", IF_8086|IF_SM}, + {I_CMP, 2, {REG16,REG16,0}, "\320\1\x3B\110", IF_8086}, + {I_CMP, 2, {REG32,MEMORY,0}, "\321\301\1\x3B\110", IF_386|IF_SM}, + {I_CMP, 2, {REG32,REG32,0}, "\321\1\x3B\110", IF_386}, + {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\207\15", IF_8086}, + {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\207\15", IF_386}, + {I_CMP, 2, {REG_AL,IMMEDIATE,0}, "\1\x3C\21", IF_8086|IF_SM}, + {I_CMP, 2, {REG_AX,SBYTE,0}, "\320\1\x83\207\15", IF_8086|IF_SM}, + {I_CMP, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x3D\31", IF_8086|IF_SM}, + {I_CMP, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\207\15", IF_386|IF_SM}, + {I_CMP, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x3D\41", IF_386|IF_SM}, + {I_CMP, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, + {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, + {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, + {I_CMP, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, + {I_CMP, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, + {I_CMP, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPEQPD[] = { + {I_CMPEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPEQPS[] = { + {I_CMPEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + {I_CMPEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPEQSD[] = { + {I_CMPEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, + {I_CMPEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPEQSS[] = { + {I_CMPEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + {I_CMPEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLEPD[] = { + {I_CMPLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLEPS[] = { + {I_CMPLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + {I_CMPLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLESD[] = { + {I_CMPLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLESS[] = { + {I_CMPLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + {I_CMPLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLTPD[] = { + {I_CMPLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLTPS[] = { + {I_CMPLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + {I_CMPLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLTSD[] = { + {I_CMPLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPLTSS[] = { + {I_CMPLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + {I_CMPLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNEQPD[] = { + {I_CMPNEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPNEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNEQPS[] = { + {I_CMPNEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + {I_CMPNEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNEQSD[] = { + {I_CMPNEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNEQSS[] = { + {I_CMPNEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + {I_CMPNEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLEPD[] = { + {I_CMPNLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPNLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLEPS[] = { + {I_CMPNLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + {I_CMPNLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLESD[] = { + {I_CMPNLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLESS[] = { + {I_CMPNLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + {I_CMPNLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLTPD[] = { + {I_CMPNLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPNLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLTPS[] = { + {I_CMPNLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + {I_CMPNLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLTSD[] = { + {I_CMPNLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPNLTSS[] = { + {I_CMPNLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + {I_CMPNLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPORDPD[] = { + {I_CMPORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPORDPS[] = { + {I_CMPORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + {I_CMPORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPORDSD[] = { + {I_CMPORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, + {I_CMPORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPORDSS[] = { + {I_CMPORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + {I_CMPORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPPD[] = { + {I_CMPPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_CMPPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPPS[] = { + {I_CMPPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_CMPPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPSB[] = { + {I_CMPSB, 0, {0,0,0}, "\332\1\xA6", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPSD[] = { + {I_CMPSD, 0, {0,0,0}, "\332\321\1\xA7", IF_386}, + {I_CMPSD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_CMPSD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPSS[] = { + {I_CMPSS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_CMPSS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPSW[] = { + {I_CMPSW, 0, {0,0,0}, "\332\320\1\xA7", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPUNORDPD[] = { + {I_CMPUNORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPUNORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPUNORDPS[] = { + {I_CMPUNORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + {I_CMPUNORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPUNORDSD[] = { + {I_CMPUNORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, + {I_CMPUNORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPUNORDSS[] = { + {I_CMPUNORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + {I_CMPUNORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPXCHG[] = { + {I_CMPXCHG, 2, {MEMORY,REG8,0}, "\300\2\x0F\xB0\101", IF_PENT|IF_SM}, + {I_CMPXCHG, 2, {REG8,REG8,0}, "\2\x0F\xB0\101", IF_PENT}, + {I_CMPXCHG, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB1\101", IF_PENT|IF_SM}, + {I_CMPXCHG, 2, {REG16,REG16,0}, "\320\2\x0F\xB1\101", IF_PENT}, + {I_CMPXCHG, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB1\101", IF_PENT|IF_SM}, + {I_CMPXCHG, 2, {REG32,REG32,0}, "\321\2\x0F\xB1\101", IF_PENT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPXCHG486[] = { + {I_CMPXCHG486, 2, {MEMORY,REG8,0}, "\300\2\x0F\xA6\101", IF_486|IF_SM|IF_UNDOC}, + {I_CMPXCHG486, 2, {REG8,REG8,0}, "\2\x0F\xA6\101", IF_486|IF_UNDOC}, + {I_CMPXCHG486, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, + {I_CMPXCHG486, 2, {REG16,REG16,0}, "\320\2\x0F\xA7\101", IF_486|IF_UNDOC}, + {I_CMPXCHG486, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, + {I_CMPXCHG486, 2, {REG32,REG32,0}, "\321\2\x0F\xA7\101", IF_486|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMPXCHG8B[] = { + {I_CMPXCHG8B, 1, {MEMORY,0,0}, "\300\2\x0F\xC7\201", IF_PENT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_COMISD[] = { + {I_COMISD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, + {I_COMISD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_COMISS[] = { + {I_COMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, + {I_COMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CPUID[] = { + {I_CPUID, 0, {0,0,0}, "\2\x0F\xA2", IF_PENT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTDQ2PD[] = { + {I_CVTDQ2PD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTDQ2PD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTDQ2PS[] = { + {I_CVTDQ2PS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTDQ2PS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPD2DQ[] = { + {I_CVTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPD2PI[] = { + {I_CVTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPD2PS[] = { + {I_CVTPD2PS, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPD2PS, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPI2PD[] = { + {I_CVTPI2PD, 2, {XMMREG,MMXREG,0}, "\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPI2PD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPI2PS[] = { + {I_CVTPI2PS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTPI2PS, 2, {XMMREG,MMXREG,0}, "\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPS2DQ[] = { + {I_CVTPS2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPS2PD[] = { + {I_CVTPS2PD, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPS2PD, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTPS2PI[] = { + {I_CVTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTSD2SI[] = { + {I_CVTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTSD2SS[] = { + {I_CVTSD2SS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSD2SS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTSI2SD[] = { + {I_CVTSI2SD, 2, {XMMREG,REG32,0}, "\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSI2SD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTSI2SS[] = { + {I_CVTSI2SS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_SD|IF_AR1}, + {I_CVTSI2SS, 2, {XMMREG,REG32,0}, "\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTSS2SD[] = { + {I_CVTSS2SD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSS2SD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTSS2SI[] = { + {I_CVTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, + {I_CVTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTTPD2DQ[] = { + {I_CVTTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTTPD2PI[] = { + {I_CVTTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTTPS2DQ[] = { + {I_CVTTPS2DQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTTPS2PI[] = { + {I_CVTTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTTSD2SI[] = { + {I_CVTTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CVTTSS2SI[] = { + {I_CVTTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, + {I_CVTTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CWD[] = { + {I_CWD, 0, {0,0,0}, "\320\1\x99", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CWDE[] = { + {I_CWDE, 0, {0,0,0}, "\321\1\x98", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DAA[] = { + {I_DAA, 0, {0,0,0}, "\1\x27", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DAS[] = { + {I_DAS, 0, {0,0,0}, "\1\x2F", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DB[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_DD[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_DEC[] = { + {I_DEC, 1, {REG16,0,0}, "\320\10\x48", IF_8086}, + {I_DEC, 1, {REG32,0,0}, "\321\10\x48", IF_386}, + {I_DEC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\201", IF_8086}, + {I_DEC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\201", IF_8086}, + {I_DEC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\201", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DIV[] = { + {I_DIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\206", IF_8086}, + {I_DIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\206", IF_8086}, + {I_DIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\206", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DIVPD[] = { + {I_DIVPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, + {I_DIVPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DIVPS[] = { + {I_DIVPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + {I_DIVPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DIVSD[] = { + {I_DIVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, + {I_DIVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DIVSS[] = { + {I_DIVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + {I_DIVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_DQ[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_DT[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_DW[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_EMMS[] = { + {I_EMMS, 0, {0,0,0}, "\2\x0F\x77", IF_PENT|IF_MMX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ENTER[] = { + {I_ENTER, 2, {IMMEDIATE,IMMEDIATE,0}, "\1\xC8\30\25", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_EQU[] = { + {I_EQU, 1, {IMMEDIATE,0,0}, "\0", IF_8086}, + {I_EQU, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\0", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_F2XM1[] = { + {I_F2XM1, 0, {0,0,0}, "\2\xD9\xF0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FABS[] = { + {I_FABS, 0, {0,0,0}, "\2\xD9\xE1", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FADD[] = { + {I_FADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\200", IF_8086|IF_FPU}, + {I_FADD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\200", IF_8086|IF_FPU}, + {I_FADD, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, + {I_FADD, 1, {FPUREG,0,0}, "\1\xD8\10\xC0", IF_8086|IF_FPU}, + {I_FADD, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, + {I_FADD, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FADDP[] = { + {I_FADDP, 1, {FPUREG,0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, + {I_FADDP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FBLD[] = { + {I_FBLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, + {I_FBLD, 1, {MEMORY,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FBSTP[] = { + {I_FBSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, + {I_FBSTP, 1, {MEMORY,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCHS[] = { + {I_FCHS, 0, {0,0,0}, "\2\xD9\xE0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCLEX[] = { + {I_FCLEX, 0, {0,0,0}, "\3\x9B\xDB\xE2", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVB[] = { + {I_FCMOVB, 1, {FPUREG,0,0}, "\1\xDA\10\xC0", IF_P6|IF_FPU}, + {I_FCMOVB, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC0", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVBE[] = { + {I_FCMOVBE, 1, {FPUREG,0,0}, "\1\xDA\10\xD0", IF_P6|IF_FPU}, + {I_FCMOVBE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD0", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVE[] = { + {I_FCMOVE, 1, {FPUREG,0,0}, "\1\xDA\10\xC8", IF_P6|IF_FPU}, + {I_FCMOVE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC8", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVNB[] = { + {I_FCMOVNB, 1, {FPUREG,0,0}, "\1\xDB\10\xC0", IF_P6|IF_FPU}, + {I_FCMOVNB, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC0", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVNBE[] = { + {I_FCMOVNBE, 1, {FPUREG,0,0}, "\1\xDB\10\xD0", IF_P6|IF_FPU}, + {I_FCMOVNBE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD0", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVNE[] = { + {I_FCMOVNE, 1, {FPUREG,0,0}, "\1\xDB\10\xC8", IF_P6|IF_FPU}, + {I_FCMOVNE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC8", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVNU[] = { + {I_FCMOVNU, 1, {FPUREG,0,0}, "\1\xDB\10\xD8", IF_P6|IF_FPU}, + {I_FCMOVNU, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD8", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCMOVU[] = { + {I_FCMOVU, 1, {FPUREG,0,0}, "\1\xDA\10\xD8", IF_P6|IF_FPU}, + {I_FCMOVU, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD8", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCOM[] = { + {I_FCOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\202", IF_8086|IF_FPU}, + {I_FCOM, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\202", IF_8086|IF_FPU}, + {I_FCOM, 1, {FPUREG,0,0}, "\1\xD8\10\xD0", IF_8086|IF_FPU}, + {I_FCOM, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCOMI[] = { + {I_FCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xF0", IF_P6|IF_FPU}, + {I_FCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xF0", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCOMIP[] = { + {I_FCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xF0", IF_P6|IF_FPU}, + {I_FCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xF0", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCOMP[] = { + {I_FCOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\203", IF_8086|IF_FPU}, + {I_FCOMP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\203", IF_8086|IF_FPU}, + {I_FCOMP, 1, {FPUREG,0,0}, "\1\xD8\10\xD8", IF_8086|IF_FPU}, + {I_FCOMP, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCOMPP[] = { + {I_FCOMPP, 0, {0,0,0}, "\2\xDE\xD9", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FCOS[] = { + {I_FCOS, 0, {0,0,0}, "\2\xD9\xFF", IF_386|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FDECSTP[] = { + {I_FDECSTP, 0, {0,0,0}, "\2\xD9\xF6", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FDISI[] = { + {I_FDISI, 0, {0,0,0}, "\3\x9B\xDB\xE1", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FDIV[] = { + {I_FDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\206", IF_8086|IF_FPU}, + {I_FDIV, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\206", IF_8086|IF_FPU}, + {I_FDIV, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, + {I_FDIV, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, + {I_FDIV, 1, {FPUREG,0,0}, "\1\xD8\10\xF0", IF_8086|IF_FPU}, + {I_FDIV, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FDIVP[] = { + {I_FDIVP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, + {I_FDIVP, 1, {FPUREG,0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FDIVR[] = { + {I_FDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\207", IF_8086|IF_FPU}, + {I_FDIVR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\207", IF_8086|IF_FPU}, + {I_FDIVR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, + {I_FDIVR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, + {I_FDIVR, 1, {FPUREG,0,0}, "\1\xD8\10\xF8", IF_8086|IF_FPU}, + {I_FDIVR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FDIVRP[] = { + {I_FDIVRP, 1, {FPUREG,0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, + {I_FDIVRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FEMMS[] = { + {I_FEMMS, 0, {0,0,0}, "\2\x0F\x0E", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FENI[] = { + {I_FENI, 0, {0,0,0}, "\3\x9B\xDB\xE0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FFREE[] = { + {I_FFREE, 1, {FPUREG,0,0}, "\1\xDD\10\xC0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FFREEP[] = { + {I_FFREEP, 1, {FPUREG,0,0}, "\1\xDF\10\xC0", IF_286|IF_FPU|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FIADD[] = { + {I_FIADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\200", IF_8086|IF_FPU}, + {I_FIADD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\200", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FICOM[] = { + {I_FICOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\202", IF_8086|IF_FPU}, + {I_FICOM, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\202", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FICOMP[] = { + {I_FICOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\203", IF_8086|IF_FPU}, + {I_FICOMP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\203", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FIDIV[] = { + {I_FIDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\206", IF_8086|IF_FPU}, + {I_FIDIV, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\206", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FIDIVR[] = { + {I_FIDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\207", IF_8086|IF_FPU}, + {I_FIDIVR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\207", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FILD[] = { + {I_FILD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\200", IF_8086|IF_FPU}, + {I_FILD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\200", IF_8086|IF_FPU}, + {I_FILD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\205", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FIMUL[] = { + {I_FIMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\201", IF_8086|IF_FPU}, + {I_FIMUL, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\201", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FINCSTP[] = { + {I_FINCSTP, 0, {0,0,0}, "\2\xD9\xF7", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FINIT[] = { + {I_FINIT, 0, {0,0,0}, "\3\x9B\xDB\xE3", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FIST[] = { + {I_FIST, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\202", IF_8086|IF_FPU}, + {I_FIST, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\202", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FISTP[] = { + {I_FISTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\203", IF_8086|IF_FPU}, + {I_FISTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\203", IF_8086|IF_FPU}, + {I_FISTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\207", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FISTTP[] = { + {I_FISTTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDD\201", IF_PRESCOTT|IF_FPU}, + {I_FISTTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDB\201", IF_PRESCOTT|IF_FPU}, + {I_FISTTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\201", IF_PRESCOTT|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FISUB[] = { + {I_FISUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\204", IF_8086|IF_FPU}, + {I_FISUB, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\204", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FISUBR[] = { + {I_FISUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\205", IF_8086|IF_FPU}, + {I_FISUBR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\205", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLD[] = { + {I_FLD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\200", IF_8086|IF_FPU}, + {I_FLD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\200", IF_8086|IF_FPU}, + {I_FLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\205", IF_8086|IF_FPU}, + {I_FLD, 1, {FPUREG,0,0}, "\1\xD9\10\xC0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLD1[] = { + {I_FLD1, 0, {0,0,0}, "\2\xD9\xE8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDCW[] = { + {I_FLDCW, 1, {MEMORY,0,0}, "\300\1\xD9\205", IF_8086|IF_FPU|IF_SW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDENV[] = { + {I_FLDENV, 1, {MEMORY,0,0}, "\300\1\xD9\204", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDL2E[] = { + {I_FLDL2E, 0, {0,0,0}, "\2\xD9\xEA", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDL2T[] = { + {I_FLDL2T, 0, {0,0,0}, "\2\xD9\xE9", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDLG2[] = { + {I_FLDLG2, 0, {0,0,0}, "\2\xD9\xEC", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDLN2[] = { + {I_FLDLN2, 0, {0,0,0}, "\2\xD9\xED", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDPI[] = { + {I_FLDPI, 0, {0,0,0}, "\2\xD9\xEB", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FLDZ[] = { + {I_FLDZ, 0, {0,0,0}, "\2\xD9\xEE", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FMUL[] = { + {I_FMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\201", IF_8086|IF_FPU}, + {I_FMUL, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\201", IF_8086|IF_FPU}, + {I_FMUL, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, + {I_FMUL, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, + {I_FMUL, 1, {FPUREG,0,0}, "\1\xD8\10\xC8", IF_8086|IF_FPU}, + {I_FMUL, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FMULP[] = { + {I_FMULP, 1, {FPUREG,0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, + {I_FMULP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNCLEX[] = { + {I_FNCLEX, 0, {0,0,0}, "\2\xDB\xE2", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNDISI[] = { + {I_FNDISI, 0, {0,0,0}, "\2\xDB\xE1", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNENI[] = { + {I_FNENI, 0, {0,0,0}, "\2\xDB\xE0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNINIT[] = { + {I_FNINIT, 0, {0,0,0}, "\2\xDB\xE3", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNOP[] = { + {I_FNOP, 0, {0,0,0}, "\2\xD9\xD0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNSAVE[] = { + {I_FNSAVE, 1, {MEMORY,0,0}, "\300\1\xDD\206", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNSTCW[] = { + {I_FNSTCW, 1, {MEMORY,0,0}, "\300\1\xD9\207", IF_8086|IF_FPU|IF_SW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNSTENV[] = { + {I_FNSTENV, 1, {MEMORY,0,0}, "\300\1\xD9\206", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FNSTSW[] = { + {I_FNSTSW, 1, {MEMORY,0,0}, "\300\1\xDD\207", IF_8086|IF_FPU|IF_SW}, + {I_FNSTSW, 1, {REG_AX,0,0}, "\2\xDF\xE0", IF_286|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FPATAN[] = { + {I_FPATAN, 0, {0,0,0}, "\2\xD9\xF3", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FPREM[] = { + {I_FPREM, 0, {0,0,0}, "\2\xD9\xF8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FPREM1[] = { + {I_FPREM1, 0, {0,0,0}, "\2\xD9\xF5", IF_386|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FPTAN[] = { + {I_FPTAN, 0, {0,0,0}, "\2\xD9\xF2", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FRNDINT[] = { + {I_FRNDINT, 0, {0,0,0}, "\2\xD9\xFC", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FRSTOR[] = { + {I_FRSTOR, 1, {MEMORY,0,0}, "\300\1\xDD\204", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSAVE[] = { + {I_FSAVE, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\206", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSCALE[] = { + {I_FSCALE, 0, {0,0,0}, "\2\xD9\xFD", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSETPM[] = { + {I_FSETPM, 0, {0,0,0}, "\2\xDB\xE4", IF_286|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSIN[] = { + {I_FSIN, 0, {0,0,0}, "\2\xD9\xFE", IF_386|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSINCOS[] = { + {I_FSINCOS, 0, {0,0,0}, "\2\xD9\xFB", IF_386|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSQRT[] = { + {I_FSQRT, 0, {0,0,0}, "\2\xD9\xFA", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FST[] = { + {I_FST, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\202", IF_8086|IF_FPU}, + {I_FST, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\202", IF_8086|IF_FPU}, + {I_FST, 1, {FPUREG,0,0}, "\1\xDD\10\xD0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSTCW[] = { + {I_FSTCW, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\207", IF_8086|IF_FPU|IF_SW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSTENV[] = { + {I_FSTENV, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\206", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSTP[] = { + {I_FSTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\203", IF_8086|IF_FPU}, + {I_FSTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\203", IF_8086|IF_FPU}, + {I_FSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\207", IF_8086|IF_FPU}, + {I_FSTP, 1, {FPUREG,0,0}, "\1\xDD\10\xD8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSTSW[] = { + {I_FSTSW, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\207", IF_8086|IF_FPU|IF_SW}, + {I_FSTSW, 1, {REG_AX,0,0}, "\3\x9B\xDF\xE0", IF_286|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSUB[] = { + {I_FSUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\204", IF_8086|IF_FPU}, + {I_FSUB, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\204", IF_8086|IF_FPU}, + {I_FSUB, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, + {I_FSUB, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, + {I_FSUB, 1, {FPUREG,0,0}, "\1\xD8\10\xE0", IF_8086|IF_FPU}, + {I_FSUB, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSUBP[] = { + {I_FSUBP, 1, {FPUREG,0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, + {I_FSUBP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSUBR[] = { + {I_FSUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\205", IF_8086|IF_FPU}, + {I_FSUBR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\205", IF_8086|IF_FPU}, + {I_FSUBR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, + {I_FSUBR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, + {I_FSUBR, 1, {FPUREG,0,0}, "\1\xD8\10\xE8", IF_8086|IF_FPU}, + {I_FSUBR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FSUBRP[] = { + {I_FSUBRP, 1, {FPUREG,0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, + {I_FSUBRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FTST[] = { + {I_FTST, 0, {0,0,0}, "\2\xD9\xE4", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FUCOM[] = { + {I_FUCOM, 1, {FPUREG,0,0}, "\1\xDD\10\xE0", IF_386|IF_FPU}, + {I_FUCOM, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE0", IF_386|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FUCOMI[] = { + {I_FUCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xE8", IF_P6|IF_FPU}, + {I_FUCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xE8", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FUCOMIP[] = { + {I_FUCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xE8", IF_P6|IF_FPU}, + {I_FUCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xE8", IF_P6|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FUCOMP[] = { + {I_FUCOMP, 1, {FPUREG,0,0}, "\1\xDD\10\xE8", IF_386|IF_FPU}, + {I_FUCOMP, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE8", IF_386|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FUCOMPP[] = { + {I_FUCOMPP, 0, {0,0,0}, "\2\xDA\xE9", IF_386|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FWAIT[] = { + {I_FWAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FXAM[] = { + {I_FXAM, 0, {0,0,0}, "\2\xD9\xE5", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FXCH[] = { + {I_FXCH, 0, {0,0,0}, "\2\xD9\xC9", IF_8086|IF_FPU}, + {I_FXCH, 1, {FPUREG,0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, + {I_FXCH, 2, {FPUREG,FPU0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, + {I_FXCH, 2, {FPU0,FPUREG,0}, "\1\xD9\11\xC8", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FXRSTOR[] = { + {I_FXRSTOR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\201", IF_P6|IF_SSE|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FXSAVE[] = { + {I_FXSAVE, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\200", IF_P6|IF_SSE|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FXTRACT[] = { + {I_FXTRACT, 0, {0,0,0}, "\2\xD9\xF4", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FYL2X[] = { + {I_FYL2X, 0, {0,0,0}, "\2\xD9\xF1", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_FYL2XP1[] = { + {I_FYL2XP1, 0, {0,0,0}, "\2\xD9\xF9", IF_8086|IF_FPU}, + ITEMPLATE_END +}; + +static struct itemplate instrux_HADDPD[] = { + {I_HADDPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HADDPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_HADDPS[] = { + {I_HADDPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HADDPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_HLT[] = { + {I_HLT, 0, {0,0,0}, "\1\xF4", IF_8086|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_HSUBPD[] = { + {I_HSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_HSUBPS[] = { + {I_HSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_IBTS[] = { + {I_IBTS, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA7\101", IF_386|IF_SW|IF_UNDOC}, + {I_IBTS, 2, {REG16,REG16,0}, "\320\2\x0F\xA7\101", IF_386|IF_UNDOC}, + {I_IBTS, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA7\101", IF_386|IF_SD|IF_UNDOC}, + {I_IBTS, 2, {REG32,REG32,0}, "\321\2\x0F\xA7\101", IF_386|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ICEBP[] = { + {I_ICEBP, 0, {0,0,0}, "\1\xF1", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_IDIV[] = { + {I_IDIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\207", IF_8086}, + {I_IDIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\207", IF_8086}, + {I_IDIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\207", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_IMUL[] = { + {I_IMUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\205", IF_8086}, + {I_IMUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\205", IF_8086}, + {I_IMUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\205", IF_386}, + {I_IMUL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xAF\110", IF_386|IF_SM}, + {I_IMUL, 2, {REG16,REG16,0}, "\320\2\x0F\xAF\110", IF_386}, + {I_IMUL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xAF\110", IF_386|IF_SM}, + {I_IMUL, 2, {REG32,REG32,0}, "\321\2\x0F\xAF\110", IF_386}, + {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS8}, "\320\301\1\x6B\110\16", IF_186|IF_SM}, + {I_IMUL, 3, {REG16,MEMORY,SBYTE}, "\320\301\1\x6B\110\16", IF_186|IF_SM}, + {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS16}, "\320\301\1\x69\110\32", IF_186|IF_SM}, + {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE}, "\320\301\135\1\x69\110\132", IF_186|IF_SM}, + {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS8}, "\320\1\x6B\110\16", IF_186}, + {I_IMUL, 3, {REG16,REG16,SBYTE}, "\320\1\x6B\110\16", IF_186|IF_SM}, + {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS16}, "\320\1\x69\110\32", IF_186}, + {I_IMUL, 3, {REG16,REG16,IMMEDIATE}, "\320\135\1\x69\110\132", IF_186|IF_SM}, + {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS8}, "\321\301\1\x6B\110\16", IF_386|IF_SM}, + {I_IMUL, 3, {REG32,MEMORY,SBYTE}, "\321\301\1\x6B\110\16", IF_386|IF_SM}, + {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS32}, "\321\301\1\x69\110\42", IF_386|IF_SM}, + {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE}, "\321\301\145\1\x69\110\142", IF_386|IF_SM}, + {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS8}, "\321\1\x6B\110\16", IF_386}, + {I_IMUL, 3, {REG32,REG32,SBYTE}, "\321\1\x6B\110\16", IF_386|IF_SM}, + {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS32}, "\321\1\x69\110\42", IF_386}, + {I_IMUL, 3, {REG32,REG32,IMMEDIATE}, "\321\145\1\x69\110\142", IF_386|IF_SM}, + {I_IMUL, 2, {REG16,IMMEDIATE|BITS8,0}, "\320\1\x6B\100\15", IF_186}, + {I_IMUL, 2, {REG16,SBYTE,0}, "\320\1\x6B\100\15", IF_186|IF_SM}, + {I_IMUL, 2, {REG16,IMMEDIATE|BITS16,0}, "\320\1\x69\100\31", IF_186}, + {I_IMUL, 2, {REG16,IMMEDIATE,0}, "\320\134\1\x69\100\131", IF_186|IF_SM}, + {I_IMUL, 2, {REG32,IMMEDIATE|BITS8,0}, "\321\1\x6B\100\15", IF_386}, + {I_IMUL, 2, {REG32,SBYTE,0}, "\321\1\x6B\100\15", IF_386|IF_SM}, + {I_IMUL, 2, {REG32,IMMEDIATE|BITS32,0}, "\321\1\x69\100\41", IF_386}, + {I_IMUL, 2, {REG32,IMMEDIATE,0}, "\321\144\1\x69\100\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_IN[] = { + {I_IN, 2, {REG_AL,IMMEDIATE,0}, "\1\xE4\25", IF_8086|IF_SB}, + {I_IN, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xE5\25", IF_8086|IF_SB}, + {I_IN, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xE5\25", IF_386|IF_SB}, + {I_IN, 2, {REG_AL,REG_DX,0}, "\1\xEC", IF_8086}, + {I_IN, 2, {REG_AX,REG_DX,0}, "\320\1\xED", IF_8086}, + {I_IN, 2, {REG_EAX,REG_DX,0}, "\321\1\xED", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INC[] = { + {I_INC, 1, {REG16,0,0}, "\320\10\x40", IF_8086}, + {I_INC, 1, {REG32,0,0}, "\321\10\x40", IF_386}, + {I_INC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\200", IF_8086}, + {I_INC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\200", IF_8086}, + {I_INC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\200", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INCBIN[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_INSB[] = { + {I_INSB, 0, {0,0,0}, "\1\x6C", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INSD[] = { + {I_INSD, 0, {0,0,0}, "\321\1\x6D", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INSW[] = { + {I_INSW, 0, {0,0,0}, "\320\1\x6D", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INT[] = { + {I_INT, 1, {IMMEDIATE,0,0}, "\1\xCD\24", IF_8086|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INT01[] = { + {I_INT01, 0, {0,0,0}, "\1\xF1", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INT03[] = { + {I_INT03, 0, {0,0,0}, "\1\xCC", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INT1[] = { + {I_INT1, 0, {0,0,0}, "\1\xF1", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INT3[] = { + {I_INT3, 0, {0,0,0}, "\1\xCC", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INTO[] = { + {I_INTO, 0, {0,0,0}, "\1\xCE", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INVD[] = { + {I_INVD, 0, {0,0,0}, "\2\x0F\x08", IF_486|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_INVLPG[] = { + {I_INVLPG, 1, {MEMORY,0,0}, "\300\2\x0F\x01\207", IF_486|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_IRET[] = { + {I_IRET, 0, {0,0,0}, "\322\1\xCF", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_IRETD[] = { + {I_IRETD, 0, {0,0,0}, "\321\1\xCF", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_IRETW[] = { + {I_IRETW, 0, {0,0,0}, "\320\1\xCF", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_JCXZ[] = { + {I_JCXZ, 1, {IMMEDIATE,0,0}, "\310\1\xE3\50", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_JECXZ[] = { + {I_JECXZ, 1, {IMMEDIATE,0,0}, "\311\1\xE3\50", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_JMP[] = { + {I_JMP, 1, {IMMEDIATE|SHORT,0,0}, "\1\xEB\50", IF_8086}, + {I_JMP, 1, {IMMEDIATE,0,0}, "\371\1\xEB\50", IF_8086}, + {I_JMP, 1, {IMMEDIATE,0,0}, "\322\1\xE9\64", IF_8086}, + {I_JMP, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\xE9\64", IF_8086}, + {I_JMP, 1, {IMMEDIATE|FAR,0,0}, "\322\1\xEA\34\37", IF_8086}, + {I_JMP, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE9\64", IF_8086}, + {I_JMP, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\xE9\64", IF_8086}, + {I_JMP, 1, {IMMEDIATE|BITS16|FAR,0,0}, "\320\1\xEA\34\37", IF_8086}, + {I_JMP, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE9\64", IF_386}, + {I_JMP, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\xE9\64", IF_386}, + {I_JMP, 1, {IMMEDIATE|BITS32|FAR,0,0}, "\321\1\xEA\34\37", IF_386}, + {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\xEA\35\30", IF_8086}, + {I_JMP, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\xEA\31\30", IF_8086}, + {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\xEA\31\30", IF_8086}, + {I_JMP, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\xEA\41\30", IF_386}, + {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\xEA\41\30", IF_386}, + {I_JMP, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\205", IF_8086}, + {I_JMP, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\205", IF_8086}, + {I_JMP, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\205", IF_386}, + {I_JMP, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\204", IF_386}, + {I_JMP, 1, {REG16,0,0}, "\320\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {REG32,0,0}, "\321\300\1\xFF\204", IF_386}, + {I_JMP, 1, {MEMORY,0,0}, "\322\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\204", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_JMPE[] = { + {I_JMPE, 1, {IMMEDIATE,0,0}, "\322\2\x0F\xB8\64", IF_IA64}, + {I_JMPE, 1, {IMMEDIATE|BITS16,0,0}, "\320\2\x0F\xB8\64", IF_IA64}, + {I_JMPE, 1, {IMMEDIATE|BITS32,0,0}, "\321\2\x0F\xB8\64", IF_IA64}, + {I_JMPE, 1, {REGMEM|BITS16,0,0}, "\320\2\x0F\x00\206", IF_IA64}, + {I_JMPE, 1, {REGMEM|BITS32,0,0}, "\321\2\x0F\x00\206", IF_IA64}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LAHF[] = { + {I_LAHF, 0, {0,0,0}, "\1\x9F", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LAR[] = { + {I_LAR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x02\110", IF_286|IF_PROT|IF_SM}, + {I_LAR, 2, {REG16,REG16,0}, "\320\2\x0F\x02\110", IF_286|IF_PROT}, + {I_LAR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x02\110", IF_386|IF_PROT|IF_SM}, + {I_LAR, 2, {REG32,REG32,0}, "\321\2\x0F\x02\110", IF_386|IF_PROT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LDDQU[] = { + {I_LDDQU, 2, {XMMREG,MEMORY,0}, "\3\xF2\x0F\xF0\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LDMXCSR[] = { + {I_LDMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\202", IF_KATMAI|IF_SSE|IF_SD}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LDS[] = { + {I_LDS, 2, {REG16,MEMORY,0}, "\320\301\1\xC5\110", IF_8086}, + {I_LDS, 2, {REG32,MEMORY,0}, "\321\301\1\xC5\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LEA[] = { + {I_LEA, 2, {REG16,MEMORY,0}, "\320\301\1\x8D\110", IF_8086}, + {I_LEA, 2, {REG32,MEMORY,0}, "\321\301\1\x8D\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LEAVE[] = { + {I_LEAVE, 0, {0,0,0}, "\1\xC9", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LES[] = { + {I_LES, 2, {REG16,MEMORY,0}, "\320\301\1\xC4\110", IF_8086}, + {I_LES, 2, {REG32,MEMORY,0}, "\321\301\1\xC4\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LFENCE[] = { + {I_LFENCE, 0, {0,0,0}, "\3\x0F\xAE\xE8", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LFS[] = { + {I_LFS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB4\110", IF_386}, + {I_LFS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB4\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LGDT[] = { + {I_LGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\202", IF_286|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LGS[] = { + {I_LGS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB5\110", IF_386}, + {I_LGS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB5\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LIDT[] = { + {I_LIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\203", IF_286|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LLDT[] = { + {I_LLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, + {I_LLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, + {I_LLDT, 1, {REG16,0,0}, "\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LMSW[] = { + {I_LMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, + {I_LMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, + {I_LMSW, 1, {REG16,0,0}, "\2\x0F\x01\206", IF_286|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LOADALL[] = { + {I_LOADALL, 0, {0,0,0}, "\2\x0F\x07", IF_386|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LOADALL286[] = { + {I_LOADALL286, 0, {0,0,0}, "\2\x0F\x05", IF_286|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LODSB[] = { + {I_LODSB, 0, {0,0,0}, "\1\xAC", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LODSD[] = { + {I_LODSD, 0, {0,0,0}, "\321\1\xAD", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LODSW[] = { + {I_LODSW, 0, {0,0,0}, "\320\1\xAD", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LOOP[] = { + {I_LOOP, 1, {IMMEDIATE,0,0}, "\312\1\xE2\50", IF_8086}, + {I_LOOP, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE2\50", IF_8086}, + {I_LOOP, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE2\50", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LOOPE[] = { + {I_LOOPE, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, + {I_LOOPE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, + {I_LOOPE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LOOPNE[] = { + {I_LOOPNE, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, + {I_LOOPNE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, + {I_LOOPNE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LOOPNZ[] = { + {I_LOOPNZ, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, + {I_LOOPNZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, + {I_LOOPNZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LOOPZ[] = { + {I_LOOPZ, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, + {I_LOOPZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, + {I_LOOPZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LSL[] = { + {I_LSL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x03\110", IF_286|IF_PROT|IF_SM}, + {I_LSL, 2, {REG16,REG16,0}, "\320\2\x0F\x03\110", IF_286|IF_PROT}, + {I_LSL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x03\110", IF_386|IF_PROT|IF_SM}, + {I_LSL, 2, {REG32,REG32,0}, "\321\2\x0F\x03\110", IF_386|IF_PROT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LSS[] = { + {I_LSS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB2\110", IF_386}, + {I_LSS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB2\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_LTR[] = { + {I_LTR, 1, {MEMORY,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, + {I_LTR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, + {I_LTR, 1, {REG16,0,0}, "\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MASKMOVDQU[] = { + {I_MASKMOVDQU, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF7\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MASKMOVQ[] = { + {I_MASKMOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF7\110", IF_KATMAI|IF_MMX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MAXPD[] = { + {I_MAXPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MAXPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MAXPS[] = { + {I_MAXPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + {I_MAXPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MAXSD[] = { + {I_MAXSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MAXSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MAXSS[] = { + {I_MAXSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + {I_MAXSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MFENCE[] = { + {I_MFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF0", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MINPD[] = { + {I_MINPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, + {I_MINPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MINPS[] = { + {I_MINPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + {I_MINPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MINSD[] = { + {I_MINSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, + {I_MINSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MINSS[] = { + {I_MINSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + {I_MINSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MONITOR[] = { + {I_MONITOR, 0, {0,0,0}, "\3\x0F\x01\xC8", IF_PRESCOTT}, + {I_MONITOR, 3, {REG_EAX,REG_ECX,REG_EDX}, "\3\x0F\x01\xC8", IF_PRESCOTT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOV[] = { + {I_MOV, 2, {MEMORY,REG_SREG,0}, "\300\1\x8C\101", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,REG_SREG,0}, "\320\1\x8C\101", IF_8086}, + {I_MOV, 2, {REG32,REG_SREG,0}, "\321\1\x8C\101", IF_386}, + {I_MOV, 2, {REG_SREG,MEMORY,0}, "\301\1\x8E\110", IF_8086|IF_SM}, + {I_MOV, 2, {REG_SREG,REG16,0}, "\1\x8E\110", IF_8086}, + {I_MOV, 2, {REG_SREG,REG32,0}, "\1\x8E\110", IF_386}, + {I_MOV, 2, {REG_AL,MEM_OFFS,0}, "\301\1\xA0\45", IF_8086|IF_SM}, + {I_MOV, 2, {REG_AX,MEM_OFFS,0}, "\301\320\1\xA1\45", IF_8086|IF_SM}, + {I_MOV, 2, {REG_EAX,MEM_OFFS,0}, "\301\321\1\xA1\45", IF_386|IF_SM}, + {I_MOV, 2, {MEM_OFFS,REG_AL,0}, "\300\1\xA2\44", IF_8086|IF_SM}, + {I_MOV, 2, {MEM_OFFS,REG_AX,0}, "\300\320\1\xA3\44", IF_8086|IF_SM}, + {I_MOV, 2, {MEM_OFFS,REG_EAX,0}, "\300\321\1\xA3\44", IF_386|IF_SM}, + {I_MOV, 2, {REG32,REG_CREG,0}, "\2\x0F\x20\101", IF_386|IF_PRIV}, + {I_MOV, 2, {REG32,REG_DREG,0}, "\2\x0F\x21\101", IF_386|IF_PRIV}, + {I_MOV, 2, {REG32,REG_TREG,0}, "\2\x0F\x24\101", IF_386|IF_PRIV}, + {I_MOV, 2, {REG_CREG,REG32,0}, "\2\x0F\x22\110", IF_386|IF_PRIV}, + {I_MOV, 2, {REG_DREG,REG32,0}, "\2\x0F\x23\110", IF_386|IF_PRIV}, + {I_MOV, 2, {REG_TREG,REG32,0}, "\2\x0F\x26\110", IF_386|IF_PRIV}, + {I_MOV, 2, {MEMORY,REG8,0}, "\300\1\x88\101", IF_8086|IF_SM}, + {I_MOV, 2, {REG8,REG8,0}, "\1\x88\101", IF_8086}, + {I_MOV, 2, {MEMORY,REG16,0}, "\320\300\1\x89\101", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,REG16,0}, "\320\1\x89\101", IF_8086}, + {I_MOV, 2, {MEMORY,REG32,0}, "\321\300\1\x89\101", IF_386|IF_SM}, + {I_MOV, 2, {REG32,REG32,0}, "\321\1\x89\101", IF_386}, + {I_MOV, 2, {REG8,MEMORY,0}, "\301\1\x8A\110", IF_8086|IF_SM}, + {I_MOV, 2, {REG8,REG8,0}, "\1\x8A\110", IF_8086}, + {I_MOV, 2, {REG16,MEMORY,0}, "\320\301\1\x8B\110", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,REG16,0}, "\320\1\x8B\110", IF_8086}, + {I_MOV, 2, {REG32,MEMORY,0}, "\321\301\1\x8B\110", IF_386|IF_SM}, + {I_MOV, 2, {REG32,REG32,0}, "\321\1\x8B\110", IF_386}, + {I_MOV, 2, {REG8,IMMEDIATE,0}, "\10\xB0\21", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,IMMEDIATE,0}, "\320\10\xB8\31", IF_8086|IF_SM}, + {I_MOV, 2, {REG32,IMMEDIATE,0}, "\321\10\xB8\41", IF_386|IF_SM}, + {I_MOV, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, + {I_MOV, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, + {I_MOV, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, + {I_MOV, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, + {I_MOV, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, + {I_MOV, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVAPD[] = { + {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x29\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVAPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x29\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVAPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVAPS[] = { + {I_MOVAPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x28\110", IF_KATMAI|IF_SSE}, + {I_MOVAPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x29\101", IF_KATMAI|IF_SSE}, + {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x28\110", IF_KATMAI|IF_SSE}, + {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x29\101", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVD[] = { + {I_MOVD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6E\110", IF_PENT|IF_MMX|IF_SD}, + {I_MOVD, 2, {MMXREG,REG32,0}, "\2\x0F\x6E\110", IF_PENT|IF_MMX}, + {I_MOVD, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7E\101", IF_PENT|IF_MMX|IF_SD}, + {I_MOVD, 2, {REG32,MMXREG,0}, "\2\x0F\x7E\101", IF_PENT|IF_MMX}, + {I_MOVD, 2, {XMMREG,REG32,0}, "\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVDDUP[] = { + {I_MOVDDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVDDUP, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVDQ2Q[] = { + {I_MOVDQ2Q, 2, {MMXREG,XMMREG,0}, "\3\xF2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVDQA[] = { + {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVDQA, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQA, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVDQU[] = { + {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVDQU, 2, {MEMORY,XMMREG,0}, "\333\300\2\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQU, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVHLPS[] = { + {I_MOVHLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x12\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVHPD[] = { + {I_MOVHPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x17\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x16\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVHPS[] = { + {I_MOVHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x16\110", IF_KATMAI|IF_SSE}, + {I_MOVHPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x17\101", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVLHPS[] = { + {I_MOVLHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x16\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVLPD[] = { + {I_MOVLPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x13\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x12\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVLPS[] = { + {I_MOVLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x12\110", IF_KATMAI|IF_SSE}, + {I_MOVLPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x13\101", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVMSKPD[] = { + {I_MOVMSKPD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x50\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVMSKPS[] = { + {I_MOVMSKPS, 2, {REG32,XMMREG,0}, "\2\x0F\x50\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVNTDQ[] = { + {I_MOVNTDQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xE7\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVNTI[] = { + {I_MOVNTI, 2, {MEMORY,REG32,0}, "\300\2\x0F\xC3\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVNTPD[] = { + {I_MOVNTPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x2B\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVNTPS[] = { + {I_MOVNTPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x2B\101", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVNTQ[] = { + {I_MOVNTQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\xE7\101", IF_KATMAI|IF_MMX|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVQ[] = { + {I_MOVQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6F\110", IF_PENT|IF_MMX|IF_SM}, + {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6F\110", IF_PENT|IF_MMX}, + {I_MOVQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7F\101", IF_PENT|IF_MMX|IF_SM}, + {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x7F\101", IF_PENT|IF_MMX}, + {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xD6\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVQ2DQ[] = { + {I_MOVQ2DQ, 2, {XMMREG,MMXREG,0}, "\333\2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVSB[] = { + {I_MOVSB, 0, {0,0,0}, "\1\xA4", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVSD[] = { + {I_MOVSD, 0, {0,0,0}, "\321\1\xA5", IF_386}, + {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVSD, 2, {MEMORY,XMMREG,0}, "\300\3\xF2\x0F\x11\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVSHDUP[] = { + {I_MOVSHDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVSHDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVSLDUP[] = { + {I_MOVSLDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVSLDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVSS[] = { + {I_MOVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVSS, 2, {MEMORY,XMMREG,0}, "\300\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVSW[] = { + {I_MOVSW, 0, {0,0,0}, "\320\1\xA5", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVSX[] = { + {I_MOVSX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBE\110", IF_386|IF_SB}, + {I_MOVSX, 2, {REG16,REG8,0}, "\320\2\x0F\xBE\110", IF_386}, + {I_MOVSX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xBE\110", IF_386}, + {I_MOVSX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xBF\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVUPD[] = { + {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVUPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x11\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVUPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVUPS[] = { + {I_MOVUPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVUPS, 2, {MEMORY,XMMREG,0}, "\300\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MOVZX[] = { + {I_MOVZX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB6\110", IF_386|IF_SB}, + {I_MOVZX, 2, {REG16,REG8,0}, "\320\2\x0F\xB6\110", IF_386}, + {I_MOVZX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xB6\110", IF_386}, + {I_MOVZX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xB7\110", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MUL[] = { + {I_MUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\204", IF_8086}, + {I_MUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\204", IF_8086}, + {I_MUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\204", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MULPD[] = { + {I_MULPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, + {I_MULPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MULPS[] = { + {I_MULPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + {I_MULPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MULSD[] = { + {I_MULSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, + {I_MULSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MULSS[] = { + {I_MULSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + {I_MULSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_MWAIT[] = { + {I_MWAIT, 0, {0,0,0}, "\3\x0F\x01\xC9", IF_PRESCOTT}, + {I_MWAIT, 2, {REG_EAX,REG_ECX,0}, "\3\x0F\x01\xC9", IF_PRESCOTT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_NEG[] = { + {I_NEG, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\203", IF_8086}, + {I_NEG, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\203", IF_8086}, + {I_NEG, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\203", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_NOP[] = { + {I_NOP, 0, {0,0,0}, "\1\x90", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_NOT[] = { + {I_NOT, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\202", IF_8086}, + {I_NOT, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\202", IF_8086}, + {I_NOT, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\202", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_OR[] = { + {I_OR, 2, {MEMORY,REG8,0}, "\300\1\x08\101", IF_8086|IF_SM}, + {I_OR, 2, {REG8,REG8,0}, "\1\x08\101", IF_8086}, + {I_OR, 2, {MEMORY,REG16,0}, "\320\300\1\x09\101", IF_8086|IF_SM}, + {I_OR, 2, {REG16,REG16,0}, "\320\1\x09\101", IF_8086}, + {I_OR, 2, {MEMORY,REG32,0}, "\321\300\1\x09\101", IF_386|IF_SM}, + {I_OR, 2, {REG32,REG32,0}, "\321\1\x09\101", IF_386}, + {I_OR, 2, {REG8,MEMORY,0}, "\301\1\x0A\110", IF_8086|IF_SM}, + {I_OR, 2, {REG8,REG8,0}, "\1\x0A\110", IF_8086}, + {I_OR, 2, {REG16,MEMORY,0}, "\320\301\1\x0B\110", IF_8086|IF_SM}, + {I_OR, 2, {REG16,REG16,0}, "\320\1\x0B\110", IF_8086}, + {I_OR, 2, {REG32,MEMORY,0}, "\321\301\1\x0B\110", IF_386|IF_SM}, + {I_OR, 2, {REG32,REG32,0}, "\321\1\x0B\110", IF_386}, + {I_OR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\201\15", IF_8086}, + {I_OR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\201\15", IF_386}, + {I_OR, 2, {REG_AL,IMMEDIATE,0}, "\1\x0C\21", IF_8086|IF_SM}, + {I_OR, 2, {REG_AX,SBYTE,0}, "\320\1\x83\201\15", IF_8086|IF_SM}, + {I_OR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x0D\31", IF_8086|IF_SM}, + {I_OR, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\201\15", IF_386|IF_SM}, + {I_OR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x0D\41", IF_386|IF_SM}, + {I_OR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, + {I_OR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, + {I_OR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, + {I_OR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, + {I_OR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, + {I_OR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ORPD[] = { + {I_ORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_ORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ORPS[] = { + {I_ORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x56\110", IF_KATMAI|IF_SSE}, + {I_ORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x56\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_OUT[] = { + {I_OUT, 2, {IMMEDIATE,REG_AL,0}, "\1\xE6\24", IF_8086|IF_SB}, + {I_OUT, 2, {IMMEDIATE,REG_AX,0}, "\320\1\xE7\24", IF_8086|IF_SB}, + {I_OUT, 2, {IMMEDIATE,REG_EAX,0}, "\321\1\xE7\24", IF_386|IF_SB}, + {I_OUT, 2, {REG_DX,REG_AL,0}, "\1\xEE", IF_8086}, + {I_OUT, 2, {REG_DX,REG_AX,0}, "\320\1\xEF", IF_8086}, + {I_OUT, 2, {REG_DX,REG_EAX,0}, "\321\1\xEF", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_OUTSB[] = { + {I_OUTSB, 0, {0,0,0}, "\1\x6E", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_OUTSD[] = { + {I_OUTSD, 0, {0,0,0}, "\321\1\x6F", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_OUTSW[] = { + {I_OUTSW, 0, {0,0,0}, "\320\1\x6F", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PACKSSDW[] = { + {I_PACKSSDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6B\110", IF_PENT|IF_MMX|IF_SM}, + {I_PACKSSDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6B\110", IF_PENT|IF_MMX}, + {I_PACKSSDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2}, + {I_PACKSSDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PACKSSWB[] = { + {I_PACKSSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x63\110", IF_PENT|IF_MMX|IF_SM}, + {I_PACKSSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x63\110", IF_PENT|IF_MMX}, + {I_PACKSSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2}, + {I_PACKSSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PACKUSWB[] = { + {I_PACKUSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x67\110", IF_PENT|IF_MMX|IF_SM}, + {I_PACKUSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x67\110", IF_PENT|IF_MMX}, + {I_PACKUSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2}, + {I_PACKUSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDB[] = { + {I_PADDB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFC\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFC\110", IF_PENT|IF_MMX}, + {I_PADDB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDD[] = { + {I_PADDD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFE\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFE\110", IF_PENT|IF_MMX}, + {I_PADDD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDQ[] = { + {I_PADDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDSB[] = { + {I_PADDSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEC\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEC\110", IF_PENT|IF_MMX}, + {I_PADDSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDSIW[] = { + {I_PADDSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x51\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PADDSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x51\110", IF_PENT|IF_MMX|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDSW[] = { + {I_PADDSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xED\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xED\110", IF_PENT|IF_MMX}, + {I_PADDSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDUSB[] = { + {I_PADDUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDC\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDC\110", IF_PENT|IF_MMX}, + {I_PADDUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDUSW[] = { + {I_PADDUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDD\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDD\110", IF_PENT|IF_MMX}, + {I_PADDUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PADDW[] = { + {I_PADDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFD\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFD\110", IF_PENT|IF_MMX}, + {I_PADDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PAND[] = { + {I_PAND, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDB\110", IF_PENT|IF_MMX|IF_SM}, + {I_PAND, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDB\110", IF_PENT|IF_MMX}, + {I_PAND, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2}, + {I_PAND, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PANDN[] = { + {I_PANDN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDF\110", IF_PENT|IF_MMX|IF_SM}, + {I_PANDN, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDF\110", IF_PENT|IF_MMX}, + {I_PANDN, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2}, + {I_PANDN, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PAUSE[] = { + {I_PAUSE, 0, {0,0,0}, "\333\1\x90", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PAVEB[] = { + {I_PAVEB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x50\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PAVEB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x50\110", IF_PENT|IF_MMX|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PAVGB[] = { + {I_PAVGB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE0\110", IF_KATMAI|IF_MMX}, + {I_PAVGB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE0\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PAVGB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2}, + {I_PAVGB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PAVGUSB[] = { + {I_PAVGUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW|IF_SM}, + {I_PAVGUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PAVGW[] = { + {I_PAVGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE3\110", IF_KATMAI|IF_MMX}, + {I_PAVGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE3\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PAVGW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2}, + {I_PAVGW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PCMPEQB[] = { + {I_PCMPEQB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x74\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPEQB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x74\110", IF_PENT|IF_MMX}, + {I_PCMPEQB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPEQB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PCMPEQD[] = { + {I_PCMPEQD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x76\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPEQD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x76\110", IF_PENT|IF_MMX}, + {I_PCMPEQD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPEQD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PCMPEQW[] = { + {I_PCMPEQW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x75\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPEQW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x75\110", IF_PENT|IF_MMX}, + {I_PCMPEQW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPEQW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PCMPGTB[] = { + {I_PCMPGTB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x64\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPGTB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x64\110", IF_PENT|IF_MMX}, + {I_PCMPGTB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPGTB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PCMPGTD[] = { + {I_PCMPGTD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x66\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPGTD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x66\110", IF_PENT|IF_MMX}, + {I_PCMPGTD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPGTD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PCMPGTW[] = { + {I_PCMPGTW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x65\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPGTW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x65\110", IF_PENT|IF_MMX}, + {I_PCMPGTW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPGTW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PDISTIB[] = { + {I_PDISTIB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PEXTRW[] = { + {I_PEXTRW, 3, {REG32,MMXREG,IMMEDIATE}, "\2\x0F\xC5\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PEXTRW, 3, {REG32,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC5\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PF2ID[] = { + {I_PF2ID, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW|IF_SM}, + {I_PF2ID, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PF2IW[] = { + {I_PF2IW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW|IF_SM}, + {I_PF2IW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFACC[] = { + {I_PFACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFADD[] = { + {I_PFADD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFADD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFCMPEQ[] = { + {I_PFCMPEQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFCMPEQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFCMPGE[] = { + {I_PFCMPGE, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFCMPGE, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFCMPGT[] = { + {I_PFCMPGT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFCMPGT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFMAX[] = { + {I_PFMAX, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFMAX, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFMIN[] = { + {I_PFMIN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFMIN, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFMUL[] = { + {I_PFMUL, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFMUL, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFNACC[] = { + {I_PFNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFPNACC[] = { + {I_PFPNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFPNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFRCP[] = { + {I_PFRCP, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRCP, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFRCPIT1[] = { + {I_PFRCPIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRCPIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFRCPIT2[] = { + {I_PFRCPIT2, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRCPIT2, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFRSQIT1[] = { + {I_PFRSQIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRSQIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFRSQRT[] = { + {I_PFRSQRT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRSQRT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFSUB[] = { + {I_PFSUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFSUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PFSUBR[] = { + {I_PFSUBR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFSUBR, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PI2FD[] = { + {I_PI2FD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW|IF_SM}, + {I_PI2FD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PI2FW[] = { + {I_PI2FW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW|IF_SM}, + {I_PI2FW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PINSRW[] = { + {I_PINSRW, 3, {MMXREG,REG16,IMMEDIATE}, "\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PINSRW, 3, {MMXREG,REG32,IMMEDIATE}, "\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PINSRW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PINSRW, 3, {MMXREG,MEMORY|BITS16,IMMEDIATE}, "\301\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PINSRW, 3, {XMMREG,REG16,IMMEDIATE}, "\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PINSRW, 3, {XMMREG,REG32,IMMEDIATE}, "\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PINSRW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PINSRW, 3, {XMMREG,MEMORY|BITS16,IMMEDIATE}, "\301\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMACHRIW[] = { + {I_PMACHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5E\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMADDWD[] = { + {I_PMADDWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF5\110", IF_PENT|IF_MMX|IF_SM}, + {I_PMADDWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF5\110", IF_PENT|IF_MMX}, + {I_PMADDWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMADDWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMAGW[] = { + {I_PMAGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x52\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMAGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x52\110", IF_PENT|IF_MMX|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMAXSW[] = { + {I_PMAXSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEE\110", IF_KATMAI|IF_MMX}, + {I_PMAXSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEE\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMAXSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMAXSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMAXUB[] = { + {I_PMAXUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDE\110", IF_KATMAI|IF_MMX}, + {I_PMAXUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDE\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMAXUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMAXUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMINSW[] = { + {I_PMINSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEA\110", IF_KATMAI|IF_MMX}, + {I_PMINSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEA\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMINSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMINSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMINUB[] = { + {I_PMINUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDA\110", IF_KATMAI|IF_MMX}, + {I_PMINUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDA\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMINUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMINUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMOVMSKB[] = { + {I_PMOVMSKB, 2, {REG32,MMXREG,0}, "\2\x0F\xD7\110", IF_KATMAI|IF_MMX}, + {I_PMOVMSKB, 2, {REG32,XMMREG,0}, "\3\x66\x0F\xD7\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMULHRIW[] = { + {I_PMULHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMULHRIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMULHRWA[] = { + {I_PMULHRWA, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW|IF_SM}, + {I_PMULHRWA, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMULHRWC[] = { + {I_PMULHRWC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMULHRWC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x59\110", IF_PENT|IF_MMX|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMULHUW[] = { + {I_PMULHUW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE4\110", IF_KATMAI|IF_MMX}, + {I_PMULHUW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE4\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMULHUW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULHUW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMULHW[] = { + {I_PMULHW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE5\110", IF_PENT|IF_MMX|IF_SM}, + {I_PMULHW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE5\110", IF_PENT|IF_MMX}, + {I_PMULHW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMULHW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMULLW[] = { + {I_PMULLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD5\110", IF_PENT|IF_MMX|IF_SM}, + {I_PMULLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD5\110", IF_PENT|IF_MMX}, + {I_PMULLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMULLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMULUDQ[] = { + {I_PMULUDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULUDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMULUDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULUDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMVGEZB[] = { + {I_PMVGEZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5C\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMVLZB[] = { + {I_PMVLZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMVNZB[] = { + {I_PMVNZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PMVZB[] = { + {I_PMVZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x58\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POP[] = { + {I_POP, 1, {REG16,0,0}, "\320\10\x58", IF_8086}, + {I_POP, 1, {REG32,0,0}, "\321\10\x58", IF_386}, + {I_POP, 1, {REGMEM|BITS16,0,0}, "\320\300\1\x8F\200", IF_8086}, + {I_POP, 1, {REGMEM|BITS32,0,0}, "\321\300\1\x8F\200", IF_386}, + {I_POP, 1, {REG_CS,0,0}, "\1\x0F", IF_8086|IF_UNDOC}, + {I_POP, 1, {REG_DESS,0,0}, "\4", IF_8086}, + {I_POP, 1, {REG_FSGS,0,0}, "\1\x0F\5", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POPA[] = { + {I_POPA, 0, {0,0,0}, "\322\1\x61", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POPAD[] = { + {I_POPAD, 0, {0,0,0}, "\321\1\x61", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POPAW[] = { + {I_POPAW, 0, {0,0,0}, "\320\1\x61", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POPF[] = { + {I_POPF, 0, {0,0,0}, "\322\1\x9D", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POPFD[] = { + {I_POPFD, 0, {0,0,0}, "\321\1\x9D", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POPFW[] = { + {I_POPFW, 0, {0,0,0}, "\320\1\x9D", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_POR[] = { + {I_POR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEB\110", IF_PENT|IF_MMX|IF_SM}, + {I_POR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEB\110", IF_PENT|IF_MMX}, + {I_POR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_POR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PREFETCH[] = { + {I_PREFETCH, 1, {MEMORY,0,0}, "\2\x0F\x0D\200", IF_PENT|IF_3DNOW|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PREFETCHNTA[] = { + {I_PREFETCHNTA, 1, {MEMORY,0,0}, "\300\2\x0F\x18\200", IF_KATMAI}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PREFETCHT0[] = { + {I_PREFETCHT0, 1, {MEMORY,0,0}, "\300\2\x0F\x18\201", IF_KATMAI}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PREFETCHT1[] = { + {I_PREFETCHT1, 1, {MEMORY,0,0}, "\300\2\x0F\x18\202", IF_KATMAI}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PREFETCHT2[] = { + {I_PREFETCHT2, 1, {MEMORY,0,0}, "\300\2\x0F\x18\203", IF_KATMAI}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PREFETCHW[] = { + {I_PREFETCHW, 1, {MEMORY,0,0}, "\2\x0F\x0D\201", IF_PENT|IF_3DNOW|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSADBW[] = { + {I_PSADBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF6\110", IF_KATMAI|IF_MMX}, + {I_PSADBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF6\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PSADBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSADBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSHUFD[] = { + {I_PSHUFD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PSHUFD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSHUFHW[] = { + {I_PSHUFHW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PSHUFHW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSHUFLW[] = { + {I_PSHUFLW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PSHUFLW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSHUFW[] = { + {I_PSHUFW, 3, {MMXREG,MMXREG,IMMEDIATE}, "\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PSHUFW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SM2|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSLLD[] = { + {I_PSLLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF2\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSLLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF2\110", IF_PENT|IF_MMX}, + {I_PSLLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\206\25", IF_PENT|IF_MMX}, + {I_PSLLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSLLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSLLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSLLDQ[] = { + {I_PSLLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\207\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSLLQ[] = { + {I_PSLLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF3\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSLLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF3\110", IF_PENT|IF_MMX}, + {I_PSLLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\206\25", IF_PENT|IF_MMX}, + {I_PSLLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSLLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSLLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSLLW[] = { + {I_PSLLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF1\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSLLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF1\110", IF_PENT|IF_MMX}, + {I_PSLLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\206\25", IF_PENT|IF_MMX}, + {I_PSLLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSLLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSLLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSRAD[] = { + {I_PSRAD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE2\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRAD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE2\110", IF_PENT|IF_MMX}, + {I_PSRAD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\204\25", IF_PENT|IF_MMX}, + {I_PSRAD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRAD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRAD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSRAW[] = { + {I_PSRAW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE1\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRAW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE1\110", IF_PENT|IF_MMX}, + {I_PSRAW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\204\25", IF_PENT|IF_MMX}, + {I_PSRAW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRAW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRAW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSRLD[] = { + {I_PSRLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD2\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD2\110", IF_PENT|IF_MMX}, + {I_PSRLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\202\25", IF_PENT|IF_MMX}, + {I_PSRLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSRLDQ[] = { + {I_PSRLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\203\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSRLQ[] = { + {I_PSRLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD3\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD3\110", IF_PENT|IF_MMX}, + {I_PSRLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\202\25", IF_PENT|IF_MMX}, + {I_PSRLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSRLW[] = { + {I_PSRLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD1\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD1\110", IF_PENT|IF_MMX}, + {I_PSRLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\202\25", IF_PENT|IF_MMX}, + {I_PSRLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBB[] = { + {I_PSUBB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF8\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF8\110", IF_PENT|IF_MMX}, + {I_PSUBB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBD[] = { + {I_PSUBD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFA\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFA\110", IF_PENT|IF_MMX}, + {I_PSUBD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBQ[] = { + {I_PSUBQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBSB[] = { + {I_PSUBSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE8\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE8\110", IF_PENT|IF_MMX}, + {I_PSUBSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBSIW[] = { + {I_PSUBSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PSUBSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x55\110", IF_PENT|IF_MMX|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBSW[] = { + {I_PSUBSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE9\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE9\110", IF_PENT|IF_MMX}, + {I_PSUBSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBUSB[] = { + {I_PSUBUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD8\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD8\110", IF_PENT|IF_MMX}, + {I_PSUBUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBUSW[] = { + {I_PSUBUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD9\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD9\110", IF_PENT|IF_MMX}, + {I_PSUBUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSUBW[] = { + {I_PSUBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF9\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF9\110", IF_PENT|IF_MMX}, + {I_PSUBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PSWAPD[] = { + {I_PSWAPD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW|IF_SM}, + {I_PSWAPD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKHBW[] = { + {I_PUNPCKHBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x68\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKHBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x68\110", IF_PENT|IF_MMX}, + {I_PUNPCKHBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKHBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKHDQ[] = { + {I_PUNPCKHDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6A\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKHDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6A\110", IF_PENT|IF_MMX}, + {I_PUNPCKHDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKHDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKHQDQ[] = { + {I_PUNPCKHQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKHQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKHWD[] = { + {I_PUNPCKHWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x69\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKHWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x69\110", IF_PENT|IF_MMX}, + {I_PUNPCKHWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKHWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKLBW[] = { + {I_PUNPCKLBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x60\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKLBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x60\110", IF_PENT|IF_MMX}, + {I_PUNPCKLBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKLBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKLDQ[] = { + {I_PUNPCKLDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x62\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKLDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x62\110", IF_PENT|IF_MMX}, + {I_PUNPCKLDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKLDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKLQDQ[] = { + {I_PUNPCKLQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKLQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUNPCKLWD[] = { + {I_PUNPCKLWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x61\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKLWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x61\110", IF_PENT|IF_MMX}, + {I_PUNPCKLWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKLWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUSH[] = { + {I_PUSH, 1, {REG16,0,0}, "\320\10\x50", IF_8086}, + {I_PUSH, 1, {REG32,0,0}, "\321\10\x50", IF_386}, + {I_PUSH, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\206", IF_8086}, + {I_PUSH, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\206", IF_386}, + {I_PUSH, 1, {REG_CS,0,0}, "\6", IF_8086}, + {I_PUSH, 1, {REG_DESS,0,0}, "\6", IF_8086}, + {I_PUSH, 1, {REG_FSGS,0,0}, "\1\x0F\7", IF_386}, + {I_PUSH, 1, {IMMEDIATE|BITS8,0,0}, "\1\x6A\14", IF_186}, + {I_PUSH, 1, {SBYTE,0,0}, "\1\x6A\14", IF_186}, + {I_PUSH, 1, {IMMEDIATE|BITS16,0,0}, "\320\133\1\x68\130", IF_186}, + {I_PUSH, 1, {IMMEDIATE|BITS32,0,0}, "\321\143\1\x68\140", IF_386}, + {I_PUSH, 1, {IMMEDIATE,0,0}, "\1\x68\34", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUSHA[] = { + {I_PUSHA, 0, {0,0,0}, "\322\1\x60", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUSHAD[] = { + {I_PUSHAD, 0, {0,0,0}, "\321\1\x60", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUSHAW[] = { + {I_PUSHAW, 0, {0,0,0}, "\320\1\x60", IF_186}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUSHF[] = { + {I_PUSHF, 0, {0,0,0}, "\322\1\x9C", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUSHFD[] = { + {I_PUSHFD, 0, {0,0,0}, "\321\1\x9C", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PUSHFW[] = { + {I_PUSHFW, 0, {0,0,0}, "\320\1\x9C", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_PXOR[] = { + {I_PXOR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEF\110", IF_PENT|IF_MMX|IF_SM}, + {I_PXOR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEF\110", IF_PENT|IF_MMX}, + {I_PXOR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PXOR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RCL[] = { + {I_RCL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\202\25", IF_186|IF_SB}, + {I_RCL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\202\25", IF_186|IF_SB}, + {I_RCL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\202", IF_386}, + {I_RCL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\202", IF_386}, + {I_RCL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\202\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RCPPS[] = { + {I_RCPPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + {I_RCPPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RCPSS[] = { + {I_RCPSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + {I_RCPSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RCR[] = { + {I_RCR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\203\25", IF_186|IF_SB}, + {I_RCR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\203\25", IF_186|IF_SB}, + {I_RCR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\203", IF_386}, + {I_RCR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\203", IF_386}, + {I_RCR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\203\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RDMSR[] = { + {I_RDMSR, 0, {0,0,0}, "\2\x0F\x32", IF_PENT|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RDPMC[] = { + {I_RDPMC, 0, {0,0,0}, "\2\x0F\x33", IF_P6}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RDSHR[] = { + {I_RDSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x36\200", IF_P6|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RDTSC[] = { + {I_RDTSC, 0, {0,0,0}, "\2\x0F\x31", IF_PENT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RESB[] = { + {I_RESB, 1, {IMMEDIATE,0,0}, "\340", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RESD[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_RESQ[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_REST[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_RESW[] = { + ITEMPLATE_END +}; + +static struct itemplate instrux_RET[] = { + {I_RET, 0, {0,0,0}, "\1\xC3", IF_8086}, + {I_RET, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RETF[] = { + {I_RETF, 0, {0,0,0}, "\1\xCB", IF_8086}, + {I_RETF, 1, {IMMEDIATE,0,0}, "\1\xCA\30", IF_8086|IF_SW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RETN[] = { + {I_RETN, 0, {0,0,0}, "\1\xC3", IF_8086}, + {I_RETN, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ROL[] = { + {I_ROL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\200\25", IF_186|IF_SB}, + {I_ROL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\200\25", IF_186|IF_SB}, + {I_ROL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\200", IF_386}, + {I_ROL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\200", IF_386}, + {I_ROL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\200\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_ROR[] = { + {I_ROR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\201\25", IF_186|IF_SB}, + {I_ROR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\201\25", IF_186|IF_SB}, + {I_ROR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\201", IF_386}, + {I_ROR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\201", IF_386}, + {I_ROR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\201\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RSDC[] = { + {I_RSDC, 2, {REG_SREG,MEMORY|BITS80,0}, "\301\2\x0F\x79\110", IF_486|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RSLDT[] = { + {I_RSLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7B\200", IF_486|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RSM[] = { + {I_RSM, 0, {0,0,0}, "\2\x0F\xAA", IF_PENT|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RSQRTPS[] = { + {I_RSQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + {I_RSQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RSQRTSS[] = { + {I_RSQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + {I_RSQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_RSTS[] = { + {I_RSTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7D\200", IF_486|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SAHF[] = { + {I_SAHF, 0, {0,0,0}, "\1\x9E", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SAL[] = { + {I_SAL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\204", IF_8086}, + {I_SAL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\204", IF_8086}, + {I_SAL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\204\25", IF_186|IF_SB}, + {I_SAL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\204", IF_8086}, + {I_SAL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\204", IF_8086}, + {I_SAL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\204\25", IF_186|IF_SB}, + {I_SAL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\204", IF_386}, + {I_SAL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\204", IF_386}, + {I_SAL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\204\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SALC[] = { + {I_SALC, 0, {0,0,0}, "\1\xD6", IF_8086|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SAR[] = { + {I_SAR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\207\25", IF_186|IF_SB}, + {I_SAR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\207\25", IF_186|IF_SB}, + {I_SAR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\207", IF_386}, + {I_SAR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\207", IF_386}, + {I_SAR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\207\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SBB[] = { + {I_SBB, 2, {MEMORY,REG8,0}, "\300\1\x18\101", IF_8086|IF_SM}, + {I_SBB, 2, {REG8,REG8,0}, "\1\x18\101", IF_8086}, + {I_SBB, 2, {MEMORY,REG16,0}, "\320\300\1\x19\101", IF_8086|IF_SM}, + {I_SBB, 2, {REG16,REG16,0}, "\320\1\x19\101", IF_8086}, + {I_SBB, 2, {MEMORY,REG32,0}, "\321\300\1\x19\101", IF_386|IF_SM}, + {I_SBB, 2, {REG32,REG32,0}, "\321\1\x19\101", IF_386}, + {I_SBB, 2, {REG8,MEMORY,0}, "\301\1\x1A\110", IF_8086|IF_SM}, + {I_SBB, 2, {REG8,REG8,0}, "\1\x1A\110", IF_8086}, + {I_SBB, 2, {REG16,MEMORY,0}, "\320\301\1\x1B\110", IF_8086|IF_SM}, + {I_SBB, 2, {REG16,REG16,0}, "\320\1\x1B\110", IF_8086}, + {I_SBB, 2, {REG32,MEMORY,0}, "\321\301\1\x1B\110", IF_386|IF_SM}, + {I_SBB, 2, {REG32,REG32,0}, "\321\1\x1B\110", IF_386}, + {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\203\15", IF_8086}, + {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\203\15", IF_386}, + {I_SBB, 2, {REG_AL,IMMEDIATE,0}, "\1\x1C\21", IF_8086|IF_SM}, + {I_SBB, 2, {REG_AX,SBYTE,0}, "\320\1\x83\203\15", IF_8086|IF_SM}, + {I_SBB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x1D\31", IF_8086|IF_SM}, + {I_SBB, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\203\15", IF_386|IF_SM}, + {I_SBB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x1D\41", IF_386|IF_SM}, + {I_SBB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, + {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, + {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, + {I_SBB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, + {I_SBB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, + {I_SBB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SCASB[] = { + {I_SCASB, 0, {0,0,0}, "\332\1\xAE", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SCASD[] = { + {I_SCASD, 0, {0,0,0}, "\332\321\1\xAF", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SCASW[] = { + {I_SCASW, 0, {0,0,0}, "\332\320\1\xAF", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SFENCE[] = { + {I_SFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF8", IF_KATMAI}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SGDT[] = { + {I_SGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\200", IF_286}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SHL[] = { + {I_SHL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\204\25", IF_186|IF_SB}, + {I_SHL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\204\25", IF_186|IF_SB}, + {I_SHL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\204", IF_386}, + {I_SHL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\204", IF_386}, + {I_SHL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\204\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SHLD[] = { + {I_SHLD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xA5\101", IF_386|IF_SM}, + {I_SHLD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xA5\101", IF_386}, + {I_SHLD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xA5\101", IF_386|IF_SM}, + {I_SHLD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xA5\101", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SHR[] = { + {I_SHR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\205\25", IF_186|IF_SB}, + {I_SHR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\205\25", IF_186|IF_SB}, + {I_SHR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\205", IF_386}, + {I_SHR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\205", IF_386}, + {I_SHR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\205\25", IF_386|IF_SB}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SHRD[] = { + {I_SHRD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xAD\101", IF_386|IF_SM}, + {I_SHRD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xAD\101", IF_386}, + {I_SHRD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xAD\101", IF_386|IF_SM}, + {I_SHRD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xAD\101", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SHUFPD[] = { + {I_SHUFPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_SHUFPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SHUFPS[] = { + {I_SHUFPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_SHUFPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SIDT[] = { + {I_SIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\201", IF_286}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SLDT[] = { + {I_SLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\200", IF_286}, + {I_SLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\200", IF_286}, + {I_SLDT, 1, {REG16,0,0}, "\320\1\x0F\17\200", IF_286}, + {I_SLDT, 1, {REG32,0,0}, "\321\1\x0F\17\200", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SMI[] = { + {I_SMI, 0, {0,0,0}, "\1\xF1", IF_386|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SMINT[] = { + {I_SMINT, 0, {0,0,0}, "\2\x0F\x38", IF_P6|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SMINTOLD[] = { + {I_SMINTOLD, 0, {0,0,0}, "\2\x0F\x7E", IF_486|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SMSW[] = { + {I_SMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\204", IF_286}, + {I_SMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\204", IF_286}, + {I_SMSW, 1, {REG16,0,0}, "\320\2\x0F\x01\204", IF_286}, + {I_SMSW, 1, {REG32,0,0}, "\321\2\x0F\x01\204", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SQRTPD[] = { + {I_SQRTPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, + {I_SQRTPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SQRTPS[] = { + {I_SQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + {I_SQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SQRTSD[] = { + {I_SQRTSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, + {I_SQRTSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SQRTSS[] = { + {I_SQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + {I_SQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STC[] = { + {I_STC, 0, {0,0,0}, "\1\xF9", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STD[] = { + {I_STD, 0, {0,0,0}, "\1\xFD", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STI[] = { + {I_STI, 0, {0,0,0}, "\1\xFB", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STMXCSR[] = { + {I_STMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\203", IF_KATMAI|IF_SSE|IF_SD}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STOSB[] = { + {I_STOSB, 0, {0,0,0}, "\1\xAA", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STOSD[] = { + {I_STOSD, 0, {0,0,0}, "\321\1\xAB", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STOSW[] = { + {I_STOSW, 0, {0,0,0}, "\320\1\xAB", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_STR[] = { + {I_STR, 1, {MEMORY,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, + {I_STR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, + {I_STR, 1, {REG16,0,0}, "\320\1\x0F\17\201", IF_286|IF_PROT}, + {I_STR, 1, {REG32,0,0}, "\321\1\x0F\17\201", IF_386|IF_PROT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SUB[] = { + {I_SUB, 2, {MEMORY,REG8,0}, "\300\1\x28\101", IF_8086|IF_SM}, + {I_SUB, 2, {REG8,REG8,0}, "\1\x28\101", IF_8086}, + {I_SUB, 2, {MEMORY,REG16,0}, "\320\300\1\x29\101", IF_8086|IF_SM}, + {I_SUB, 2, {REG16,REG16,0}, "\320\1\x29\101", IF_8086}, + {I_SUB, 2, {MEMORY,REG32,0}, "\321\300\1\x29\101", IF_386|IF_SM}, + {I_SUB, 2, {REG32,REG32,0}, "\321\1\x29\101", IF_386}, + {I_SUB, 2, {REG8,MEMORY,0}, "\301\1\x2A\110", IF_8086|IF_SM}, + {I_SUB, 2, {REG8,REG8,0}, "\1\x2A\110", IF_8086}, + {I_SUB, 2, {REG16,MEMORY,0}, "\320\301\1\x2B\110", IF_8086|IF_SM}, + {I_SUB, 2, {REG16,REG16,0}, "\320\1\x2B\110", IF_8086}, + {I_SUB, 2, {REG32,MEMORY,0}, "\321\301\1\x2B\110", IF_386|IF_SM}, + {I_SUB, 2, {REG32,REG32,0}, "\321\1\x2B\110", IF_386}, + {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\205\15", IF_8086}, + {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\205\15", IF_386}, + {I_SUB, 2, {REG_AL,IMMEDIATE,0}, "\1\x2C\21", IF_8086|IF_SM}, + {I_SUB, 2, {REG_AX,SBYTE,0}, "\320\1\x83\205\15", IF_8086|IF_SM}, + {I_SUB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x2D\31", IF_8086|IF_SM}, + {I_SUB, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\205\15", IF_386|IF_SM}, + {I_SUB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x2D\41", IF_386|IF_SM}, + {I_SUB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, + {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, + {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, + {I_SUB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, + {I_SUB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, + {I_SUB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SUBPD[] = { + {I_SUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, + {I_SUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SUBPS[] = { + {I_SUBPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + {I_SUBPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SUBSD[] = { + {I_SUBSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, + {I_SUBSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SUBSS[] = { + {I_SUBSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + {I_SUBSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SVDC[] = { + {I_SVDC, 2, {MEMORY|BITS80,REG_SREG,0}, "\300\2\x0F\x78\101", IF_486|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SVLDT[] = { + {I_SVLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7A\200", IF_486|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SVTS[] = { + {I_SVTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7C\200", IF_486|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SYSCALL[] = { + {I_SYSCALL, 0, {0,0,0}, "\2\x0F\x05", IF_P6|IF_AMD}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SYSENTER[] = { + {I_SYSENTER, 0, {0,0,0}, "\2\x0F\x34", IF_P6}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SYSEXIT[] = { + {I_SYSEXIT, 0, {0,0,0}, "\2\x0F\x35", IF_P6|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SYSRET[] = { + {I_SYSRET, 0, {0,0,0}, "\2\x0F\x07", IF_P6|IF_PRIV|IF_AMD}, + ITEMPLATE_END +}; + +static struct itemplate instrux_TEST[] = { + {I_TEST, 2, {MEMORY,REG8,0}, "\300\1\x84\101", IF_8086|IF_SM}, + {I_TEST, 2, {REG8,REG8,0}, "\1\x84\101", IF_8086}, + {I_TEST, 2, {MEMORY,REG16,0}, "\320\300\1\x85\101", IF_8086|IF_SM}, + {I_TEST, 2, {REG16,REG16,0}, "\320\1\x85\101", IF_8086}, + {I_TEST, 2, {MEMORY,REG32,0}, "\321\300\1\x85\101", IF_386|IF_SM}, + {I_TEST, 2, {REG32,REG32,0}, "\321\1\x85\101", IF_386}, + {I_TEST, 2, {REG8,MEMORY,0}, "\301\1\x84\110", IF_8086|IF_SM}, + {I_TEST, 2, {REG16,MEMORY,0}, "\320\301\1\x85\110", IF_8086|IF_SM}, + {I_TEST, 2, {REG32,MEMORY,0}, "\321\301\1\x85\110", IF_386|IF_SM}, + {I_TEST, 2, {REG_AL,IMMEDIATE,0}, "\1\xA8\21", IF_8086|IF_SM}, + {I_TEST, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xA9\31", IF_8086|IF_SM}, + {I_TEST, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xA9\41", IF_386|IF_SM}, + {I_TEST, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, + {I_TEST, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, + {I_TEST, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, + {I_TEST, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, + {I_TEST, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, + {I_TEST, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UCOMISD[] = { + {I_UCOMISD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, + {I_UCOMISD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UCOMISS[] = { + {I_UCOMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, + {I_UCOMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UD0[] = { + {I_UD0, 0, {0,0,0}, "\2\x0F\xFF", IF_286|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UD1[] = { + {I_UD1, 0, {0,0,0}, "\2\x0F\xB9", IF_286|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UD2[] = { + {I_UD2, 0, {0,0,0}, "\2\x0F\x0B", IF_286}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UMOV[] = { + {I_UMOV, 2, {MEMORY,REG8,0}, "\300\2\x0F\x10\101", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x10\101", IF_386|IF_UNDOC}, + {I_UMOV, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x11\101", IF_386|IF_UNDOC}, + {I_UMOV, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x11\101", IF_386|IF_UNDOC}, + {I_UMOV, 2, {REG8,MEMORY,0}, "\301\2\x0F\x12\110", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x12\110", IF_386|IF_UNDOC}, + {I_UMOV, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x13\110", IF_386|IF_UNDOC}, + {I_UMOV, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x13\110", IF_386|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UNPCKHPD[] = { + {I_UNPCKHPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2}, + {I_UNPCKHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UNPCKHPS[] = { + {I_UNPCKHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x15\110", IF_KATMAI|IF_SSE}, + {I_UNPCKHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x15\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UNPCKLPD[] = { + {I_UNPCKLPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2}, + {I_UNPCKLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_UNPCKLPS[] = { + {I_UNPCKLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x14\110", IF_KATMAI|IF_SSE}, + {I_UNPCKLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x14\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_VERR[] = { + {I_VERR, 1, {MEMORY,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, + {I_VERR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, + {I_VERR, 1, {REG16,0,0}, "\1\x0F\17\204", IF_286|IF_PROT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_VERW[] = { + {I_VERW, 1, {MEMORY,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, + {I_VERW, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, + {I_VERW, 1, {REG16,0,0}, "\1\x0F\17\205", IF_286|IF_PROT}, + ITEMPLATE_END +}; + +static struct itemplate instrux_WAIT[] = { + {I_WAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_WBINVD[] = { + {I_WBINVD, 0, {0,0,0}, "\2\x0F\x09", IF_486|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_WRMSR[] = { + {I_WRMSR, 0, {0,0,0}, "\2\x0F\x30", IF_PENT|IF_PRIV}, + ITEMPLATE_END +}; + +static struct itemplate instrux_WRSHR[] = { + {I_WRSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x37\200", IF_P6|IF_CYRIX|IF_SMM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XADD[] = { + {I_XADD, 2, {MEMORY,REG8,0}, "\300\2\x0F\xC0\101", IF_486|IF_SM}, + {I_XADD, 2, {REG8,REG8,0}, "\2\x0F\xC0\101", IF_486}, + {I_XADD, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xC1\101", IF_486|IF_SM}, + {I_XADD, 2, {REG16,REG16,0}, "\320\2\x0F\xC1\101", IF_486}, + {I_XADD, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xC1\101", IF_486|IF_SM}, + {I_XADD, 2, {REG32,REG32,0}, "\321\2\x0F\xC1\101", IF_486}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XBTS[] = { + {I_XBTS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xA6\110", IF_386|IF_SW|IF_UNDOC}, + {I_XBTS, 2, {REG16,REG16,0}, "\320\2\x0F\xA6\110", IF_386|IF_UNDOC}, + {I_XBTS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xA6\110", IF_386|IF_SD|IF_UNDOC}, + {I_XBTS, 2, {REG32,REG32,0}, "\321\2\x0F\xA6\110", IF_386|IF_UNDOC}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XCHG[] = { + {I_XCHG, 2, {REG_AX,REG16,0}, "\320\11\x90", IF_8086}, + {I_XCHG, 2, {REG_EAX,REG32,0}, "\321\11\x90", IF_386}, + {I_XCHG, 2, {REG16,REG_AX,0}, "\320\10\x90", IF_8086}, + {I_XCHG, 2, {REG32,REG_EAX,0}, "\321\10\x90", IF_386}, + {I_XCHG, 2, {REG8,MEMORY,0}, "\301\1\x86\110", IF_8086|IF_SM}, + {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\110", IF_8086}, + {I_XCHG, 2, {REG16,MEMORY,0}, "\320\301\1\x87\110", IF_8086|IF_SM}, + {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\110", IF_8086}, + {I_XCHG, 2, {REG32,MEMORY,0}, "\321\301\1\x87\110", IF_386|IF_SM}, + {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\110", IF_386}, + {I_XCHG, 2, {MEMORY,REG8,0}, "\300\1\x86\101", IF_8086|IF_SM}, + {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\101", IF_8086}, + {I_XCHG, 2, {MEMORY,REG16,0}, "\320\300\1\x87\101", IF_8086|IF_SM}, + {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\101", IF_8086}, + {I_XCHG, 2, {MEMORY,REG32,0}, "\321\300\1\x87\101", IF_386|IF_SM}, + {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\101", IF_386}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XLAT[] = { + {I_XLAT, 0, {0,0,0}, "\1\xD7", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XLATB[] = { + {I_XLATB, 0, {0,0,0}, "\1\xD7", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XOR[] = { + {I_XOR, 2, {MEMORY,REG8,0}, "\300\1\x30\101", IF_8086|IF_SM}, + {I_XOR, 2, {REG8,REG8,0}, "\1\x30\101", IF_8086}, + {I_XOR, 2, {MEMORY,REG16,0}, "\320\300\1\x31\101", IF_8086|IF_SM}, + {I_XOR, 2, {REG16,REG16,0}, "\320\1\x31\101", IF_8086}, + {I_XOR, 2, {MEMORY,REG32,0}, "\321\300\1\x31\101", IF_386|IF_SM}, + {I_XOR, 2, {REG32,REG32,0}, "\321\1\x31\101", IF_386}, + {I_XOR, 2, {REG8,MEMORY,0}, "\301\1\x32\110", IF_8086|IF_SM}, + {I_XOR, 2, {REG8,REG8,0}, "\1\x32\110", IF_8086}, + {I_XOR, 2, {REG16,MEMORY,0}, "\320\301\1\x33\110", IF_8086|IF_SM}, + {I_XOR, 2, {REG16,REG16,0}, "\320\1\x33\110", IF_8086}, + {I_XOR, 2, {REG32,MEMORY,0}, "\321\301\1\x33\110", IF_386|IF_SM}, + {I_XOR, 2, {REG32,REG32,0}, "\321\1\x33\110", IF_386}, + {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\206\15", IF_8086}, + {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\206\15", IF_386}, + {I_XOR, 2, {REG_AL,IMMEDIATE,0}, "\1\x34\21", IF_8086|IF_SM}, + {I_XOR, 2, {REG_AX,SBYTE,0}, "\320\1\x83\206\15", IF_8086|IF_SM}, + {I_XOR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x35\31", IF_8086|IF_SM}, + {I_XOR, 2, {REG_EAX,SBYTE,0}, "\321\1\x83\206\15", IF_386|IF_SM}, + {I_XOR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x35\41", IF_386|IF_SM}, + {I_XOR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, + {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, + {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, + {I_XOR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, + {I_XOR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, + {I_XOR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XORPD[] = { + {I_XORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2}, + {I_XORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XORPS[] = { + {I_XORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x57\110", IF_KATMAI|IF_SSE}, + {I_XORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x57\110", IF_KATMAI|IF_SSE}, + ITEMPLATE_END +}; + +static struct itemplate instrux_XSTORE[] = { + {I_XSTORE, 0, {0,0,0}, "\3\x0F\xA7\xC0", IF_P6|IF_CYRIX}, + ITEMPLATE_END +}; + +static struct itemplate instrux_CMOVcc[] = { + {I_CMOVcc, 2, {REG16,MEMORY,0}, "\320\301\1\x0F\330\x40\110", IF_P6|IF_SM}, + {I_CMOVcc, 2, {REG16,REG16,0}, "\320\1\x0F\330\x40\110", IF_P6}, + {I_CMOVcc, 2, {REG32,MEMORY,0}, "\321\301\1\x0F\330\x40\110", IF_P6|IF_SM}, + {I_CMOVcc, 2, {REG32,REG32,0}, "\321\1\x0F\330\x40\110", IF_P6}, + ITEMPLATE_END +}; + +static struct itemplate instrux_Jcc[] = { + {I_Jcc, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\x0F\330\x80\64", IF_386}, + {I_Jcc, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\x0F\330\x80\64", IF_386}, + {I_Jcc, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\x0F\330\x80\64", IF_386}, + {I_Jcc, 1, {IMMEDIATE|SHORT,0,0}, "\330\x70\50", IF_8086}, + {I_Jcc, 1, {IMMEDIATE,0,0}, "\370\330\x70\50", IF_8086}, + {I_Jcc, 1, {IMMEDIATE,0,0}, "\1\x0F\330\x80\64", IF_386}, + {I_Jcc, 1, {IMMEDIATE,0,0}, "\330\x71\373\1\xE9\64", IF_8086}, + {I_Jcc, 1, {IMMEDIATE,0,0}, "\330\x70\50", IF_8086}, + ITEMPLATE_END +}; + +static struct itemplate instrux_SETcc[] = { + {I_SETcc, 1, {MEMORY,0,0}, "\300\1\x0F\330\x90\200", IF_386|IF_SB}, + {I_SETcc, 1, {REG8,0,0}, "\300\1\x0F\330\x90\200", IF_386}, + ITEMPLATE_END +}; + +struct itemplate *nasm_instructions[] = { + instrux_AAA, + instrux_AAD, + instrux_AAM, + instrux_AAS, + instrux_ADC, + instrux_ADD, + instrux_ADDPD, + instrux_ADDPS, + instrux_ADDSD, + instrux_ADDSS, + instrux_ADDSUBPD, + instrux_ADDSUBPS, + instrux_AND, + instrux_ANDNPD, + instrux_ANDNPS, + instrux_ANDPD, + instrux_ANDPS, + instrux_ARPL, + instrux_BOUND, + instrux_BSF, + instrux_BSR, + instrux_BSWAP, + instrux_BT, + instrux_BTC, + instrux_BTR, + instrux_BTS, + instrux_CALL, + instrux_CBW, + instrux_CDQ, + instrux_CLC, + instrux_CLD, + instrux_CLFLUSH, + instrux_CLI, + instrux_CLTS, + instrux_CMC, + instrux_CMP, + instrux_CMPEQPD, + instrux_CMPEQPS, + instrux_CMPEQSD, + instrux_CMPEQSS, + instrux_CMPLEPD, + instrux_CMPLEPS, + instrux_CMPLESD, + instrux_CMPLESS, + instrux_CMPLTPD, + instrux_CMPLTPS, + instrux_CMPLTSD, + instrux_CMPLTSS, + instrux_CMPNEQPD, + instrux_CMPNEQPS, + instrux_CMPNEQSD, + instrux_CMPNEQSS, + instrux_CMPNLEPD, + instrux_CMPNLEPS, + instrux_CMPNLESD, + instrux_CMPNLESS, + instrux_CMPNLTPD, + instrux_CMPNLTPS, + instrux_CMPNLTSD, + instrux_CMPNLTSS, + instrux_CMPORDPD, + instrux_CMPORDPS, + instrux_CMPORDSD, + instrux_CMPORDSS, + instrux_CMPPD, + instrux_CMPPS, + instrux_CMPSB, + instrux_CMPSD, + instrux_CMPSS, + instrux_CMPSW, + instrux_CMPUNORDPD, + instrux_CMPUNORDPS, + instrux_CMPUNORDSD, + instrux_CMPUNORDSS, + instrux_CMPXCHG, + instrux_CMPXCHG486, + instrux_CMPXCHG8B, + instrux_COMISD, + instrux_COMISS, + instrux_CPUID, + instrux_CVTDQ2PD, + instrux_CVTDQ2PS, + instrux_CVTPD2DQ, + instrux_CVTPD2PI, + instrux_CVTPD2PS, + instrux_CVTPI2PD, + instrux_CVTPI2PS, + instrux_CVTPS2DQ, + instrux_CVTPS2PD, + instrux_CVTPS2PI, + instrux_CVTSD2SI, + instrux_CVTSD2SS, + instrux_CVTSI2SD, + instrux_CVTSI2SS, + instrux_CVTSS2SD, + instrux_CVTSS2SI, + instrux_CVTTPD2DQ, + instrux_CVTTPD2PI, + instrux_CVTTPS2DQ, + instrux_CVTTPS2PI, + instrux_CVTTSD2SI, + instrux_CVTTSS2SI, + instrux_CWD, + instrux_CWDE, + instrux_DAA, + instrux_DAS, + instrux_DB, + instrux_DD, + instrux_DEC, + instrux_DIV, + instrux_DIVPD, + instrux_DIVPS, + instrux_DIVSD, + instrux_DIVSS, + instrux_DQ, + instrux_DT, + instrux_DW, + instrux_EMMS, + instrux_ENTER, + instrux_EQU, + instrux_F2XM1, + instrux_FABS, + instrux_FADD, + instrux_FADDP, + instrux_FBLD, + instrux_FBSTP, + instrux_FCHS, + instrux_FCLEX, + instrux_FCMOVB, + instrux_FCMOVBE, + instrux_FCMOVE, + instrux_FCMOVNB, + instrux_FCMOVNBE, + instrux_FCMOVNE, + instrux_FCMOVNU, + instrux_FCMOVU, + instrux_FCOM, + instrux_FCOMI, + instrux_FCOMIP, + instrux_FCOMP, + instrux_FCOMPP, + instrux_FCOS, + instrux_FDECSTP, + instrux_FDISI, + instrux_FDIV, + instrux_FDIVP, + instrux_FDIVR, + instrux_FDIVRP, + instrux_FEMMS, + instrux_FENI, + instrux_FFREE, + instrux_FFREEP, + instrux_FIADD, + instrux_FICOM, + instrux_FICOMP, + instrux_FIDIV, + instrux_FIDIVR, + instrux_FILD, + instrux_FIMUL, + instrux_FINCSTP, + instrux_FINIT, + instrux_FIST, + instrux_FISTP, + instrux_FISTTP, + instrux_FISUB, + instrux_FISUBR, + instrux_FLD, + instrux_FLD1, + instrux_FLDCW, + instrux_FLDENV, + instrux_FLDL2E, + instrux_FLDL2T, + instrux_FLDLG2, + instrux_FLDLN2, + instrux_FLDPI, + instrux_FLDZ, + instrux_FMUL, + instrux_FMULP, + instrux_FNCLEX, + instrux_FNDISI, + instrux_FNENI, + instrux_FNINIT, + instrux_FNOP, + instrux_FNSAVE, + instrux_FNSTCW, + instrux_FNSTENV, + instrux_FNSTSW, + instrux_FPATAN, + instrux_FPREM, + instrux_FPREM1, + instrux_FPTAN, + instrux_FRNDINT, + instrux_FRSTOR, + instrux_FSAVE, + instrux_FSCALE, + instrux_FSETPM, + instrux_FSIN, + instrux_FSINCOS, + instrux_FSQRT, + instrux_FST, + instrux_FSTCW, + instrux_FSTENV, + instrux_FSTP, + instrux_FSTSW, + instrux_FSUB, + instrux_FSUBP, + instrux_FSUBR, + instrux_FSUBRP, + instrux_FTST, + instrux_FUCOM, + instrux_FUCOMI, + instrux_FUCOMIP, + instrux_FUCOMP, + instrux_FUCOMPP, + instrux_FWAIT, + instrux_FXAM, + instrux_FXCH, + instrux_FXRSTOR, + instrux_FXSAVE, + instrux_FXTRACT, + instrux_FYL2X, + instrux_FYL2XP1, + instrux_HADDPD, + instrux_HADDPS, + instrux_HLT, + instrux_HSUBPD, + instrux_HSUBPS, + instrux_IBTS, + instrux_ICEBP, + instrux_IDIV, + instrux_IMUL, + instrux_IN, + instrux_INC, + instrux_INCBIN, + instrux_INSB, + instrux_INSD, + instrux_INSW, + instrux_INT, + instrux_INT01, + instrux_INT03, + instrux_INT1, + instrux_INT3, + instrux_INTO, + instrux_INVD, + instrux_INVLPG, + instrux_IRET, + instrux_IRETD, + instrux_IRETW, + instrux_JCXZ, + instrux_JECXZ, + instrux_JMP, + instrux_JMPE, + instrux_LAHF, + instrux_LAR, + instrux_LDDQU, + instrux_LDMXCSR, + instrux_LDS, + instrux_LEA, + instrux_LEAVE, + instrux_LES, + instrux_LFENCE, + instrux_LFS, + instrux_LGDT, + instrux_LGS, + instrux_LIDT, + instrux_LLDT, + instrux_LMSW, + instrux_LOADALL, + instrux_LOADALL286, + instrux_LODSB, + instrux_LODSD, + instrux_LODSW, + instrux_LOOP, + instrux_LOOPE, + instrux_LOOPNE, + instrux_LOOPNZ, + instrux_LOOPZ, + instrux_LSL, + instrux_LSS, + instrux_LTR, + instrux_MASKMOVDQU, + instrux_MASKMOVQ, + instrux_MAXPD, + instrux_MAXPS, + instrux_MAXSD, + instrux_MAXSS, + instrux_MFENCE, + instrux_MINPD, + instrux_MINPS, + instrux_MINSD, + instrux_MINSS, + instrux_MONITOR, + instrux_MOV, + instrux_MOVAPD, + instrux_MOVAPS, + instrux_MOVD, + instrux_MOVDDUP, + instrux_MOVDQ2Q, + instrux_MOVDQA, + instrux_MOVDQU, + instrux_MOVHLPS, + instrux_MOVHPD, + instrux_MOVHPS, + instrux_MOVLHPS, + instrux_MOVLPD, + instrux_MOVLPS, + instrux_MOVMSKPD, + instrux_MOVMSKPS, + instrux_MOVNTDQ, + instrux_MOVNTI, + instrux_MOVNTPD, + instrux_MOVNTPS, + instrux_MOVNTQ, + instrux_MOVQ, + instrux_MOVQ2DQ, + instrux_MOVSB, + instrux_MOVSD, + instrux_MOVSHDUP, + instrux_MOVSLDUP, + instrux_MOVSS, + instrux_MOVSW, + instrux_MOVSX, + instrux_MOVUPD, + instrux_MOVUPS, + instrux_MOVZX, + instrux_MUL, + instrux_MULPD, + instrux_MULPS, + instrux_MULSD, + instrux_MULSS, + instrux_MWAIT, + instrux_NEG, + instrux_NOP, + instrux_NOT, + instrux_OR, + instrux_ORPD, + instrux_ORPS, + instrux_OUT, + instrux_OUTSB, + instrux_OUTSD, + instrux_OUTSW, + instrux_PACKSSDW, + instrux_PACKSSWB, + instrux_PACKUSWB, + instrux_PADDB, + instrux_PADDD, + instrux_PADDQ, + instrux_PADDSB, + instrux_PADDSIW, + instrux_PADDSW, + instrux_PADDUSB, + instrux_PADDUSW, + instrux_PADDW, + instrux_PAND, + instrux_PANDN, + instrux_PAUSE, + instrux_PAVEB, + instrux_PAVGB, + instrux_PAVGUSB, + instrux_PAVGW, + instrux_PCMPEQB, + instrux_PCMPEQD, + instrux_PCMPEQW, + instrux_PCMPGTB, + instrux_PCMPGTD, + instrux_PCMPGTW, + instrux_PDISTIB, + instrux_PEXTRW, + instrux_PF2ID, + instrux_PF2IW, + instrux_PFACC, + instrux_PFADD, + instrux_PFCMPEQ, + instrux_PFCMPGE, + instrux_PFCMPGT, + instrux_PFMAX, + instrux_PFMIN, + instrux_PFMUL, + instrux_PFNACC, + instrux_PFPNACC, + instrux_PFRCP, + instrux_PFRCPIT1, + instrux_PFRCPIT2, + instrux_PFRSQIT1, + instrux_PFRSQRT, + instrux_PFSUB, + instrux_PFSUBR, + instrux_PI2FD, + instrux_PI2FW, + instrux_PINSRW, + instrux_PMACHRIW, + instrux_PMADDWD, + instrux_PMAGW, + instrux_PMAXSW, + instrux_PMAXUB, + instrux_PMINSW, + instrux_PMINUB, + instrux_PMOVMSKB, + instrux_PMULHRIW, + instrux_PMULHRWA, + instrux_PMULHRWC, + instrux_PMULHUW, + instrux_PMULHW, + instrux_PMULLW, + instrux_PMULUDQ, + instrux_PMVGEZB, + instrux_PMVLZB, + instrux_PMVNZB, + instrux_PMVZB, + instrux_POP, + instrux_POPA, + instrux_POPAD, + instrux_POPAW, + instrux_POPF, + instrux_POPFD, + instrux_POPFW, + instrux_POR, + instrux_PREFETCH, + instrux_PREFETCHNTA, + instrux_PREFETCHT0, + instrux_PREFETCHT1, + instrux_PREFETCHT2, + instrux_PREFETCHW, + instrux_PSADBW, + instrux_PSHUFD, + instrux_PSHUFHW, + instrux_PSHUFLW, + instrux_PSHUFW, + instrux_PSLLD, + instrux_PSLLDQ, + instrux_PSLLQ, + instrux_PSLLW, + instrux_PSRAD, + instrux_PSRAW, + instrux_PSRLD, + instrux_PSRLDQ, + instrux_PSRLQ, + instrux_PSRLW, + instrux_PSUBB, + instrux_PSUBD, + instrux_PSUBQ, + instrux_PSUBSB, + instrux_PSUBSIW, + instrux_PSUBSW, + instrux_PSUBUSB, + instrux_PSUBUSW, + instrux_PSUBW, + instrux_PSWAPD, + instrux_PUNPCKHBW, + instrux_PUNPCKHDQ, + instrux_PUNPCKHQDQ, + instrux_PUNPCKHWD, + instrux_PUNPCKLBW, + instrux_PUNPCKLDQ, + instrux_PUNPCKLQDQ, + instrux_PUNPCKLWD, + instrux_PUSH, + instrux_PUSHA, + instrux_PUSHAD, + instrux_PUSHAW, + instrux_PUSHF, + instrux_PUSHFD, + instrux_PUSHFW, + instrux_PXOR, + instrux_RCL, + instrux_RCPPS, + instrux_RCPSS, + instrux_RCR, + instrux_RDMSR, + instrux_RDPMC, + instrux_RDSHR, + instrux_RDTSC, + instrux_RESB, + instrux_RESD, + instrux_RESQ, + instrux_REST, + instrux_RESW, + instrux_RET, + instrux_RETF, + instrux_RETN, + instrux_ROL, + instrux_ROR, + instrux_RSDC, + instrux_RSLDT, + instrux_RSM, + instrux_RSQRTPS, + instrux_RSQRTSS, + instrux_RSTS, + instrux_SAHF, + instrux_SAL, + instrux_SALC, + instrux_SAR, + instrux_SBB, + instrux_SCASB, + instrux_SCASD, + instrux_SCASW, + instrux_SFENCE, + instrux_SGDT, + instrux_SHL, + instrux_SHLD, + instrux_SHR, + instrux_SHRD, + instrux_SHUFPD, + instrux_SHUFPS, + instrux_SIDT, + instrux_SLDT, + instrux_SMI, + instrux_SMINT, + instrux_SMINTOLD, + instrux_SMSW, + instrux_SQRTPD, + instrux_SQRTPS, + instrux_SQRTSD, + instrux_SQRTSS, + instrux_STC, + instrux_STD, + instrux_STI, + instrux_STMXCSR, + instrux_STOSB, + instrux_STOSD, + instrux_STOSW, + instrux_STR, + instrux_SUB, + instrux_SUBPD, + instrux_SUBPS, + instrux_SUBSD, + instrux_SUBSS, + instrux_SVDC, + instrux_SVLDT, + instrux_SVTS, + instrux_SYSCALL, + instrux_SYSENTER, + instrux_SYSEXIT, + instrux_SYSRET, + instrux_TEST, + instrux_UCOMISD, + instrux_UCOMISS, + instrux_UD0, + instrux_UD1, + instrux_UD2, + instrux_UMOV, + instrux_UNPCKHPD, + instrux_UNPCKHPS, + instrux_UNPCKLPD, + instrux_UNPCKLPS, + instrux_VERR, + instrux_VERW, + instrux_WAIT, + instrux_WBINVD, + instrux_WRMSR, + instrux_WRSHR, + instrux_XADD, + instrux_XBTS, + instrux_XCHG, + instrux_XLAT, + instrux_XLATB, + instrux_XOR, + instrux_XORPD, + instrux_XORPS, + instrux_XSTORE, + instrux_CMOVcc, + instrux_Jcc, + instrux_SETcc, +}; diff --git a/AltairZ80/insnsd.c b/AltairZ80/insnsd.c new file mode 100644 index 0000000..0f10cdb --- /dev/null +++ b/AltairZ80/insnsd.c @@ -0,0 +1,4516 @@ +/* This file auto-generated from insns.dat by insns.pl - don't edit it */ + +#include "nasm.h" +#include "insns.h" + +static struct itemplate instrux[] = { + {I_AAA, 0, {0,0,0}, "\1\x37", IF_8086}, + {I_AAD, 0, {0,0,0}, "\2\xD5\x0A", IF_8086}, + {I_AAD, 1, {IMMEDIATE,0,0}, "\1\xD5\24", IF_8086|IF_SB}, + {I_AAM, 0, {0,0,0}, "\2\xD4\x0A", IF_8086}, + {I_AAM, 1, {IMMEDIATE,0,0}, "\1\xD4\24", IF_8086|IF_SB}, + {I_AAS, 0, {0,0,0}, "\1\x3F", IF_8086}, + {I_ADC, 2, {MEMORY,REG8,0}, "\300\1\x10\101", IF_8086|IF_SM}, + {I_ADC, 2, {REG8,REG8,0}, "\1\x10\101", IF_8086}, + {I_ADC, 2, {MEMORY,REG16,0}, "\320\300\1\x11\101", IF_8086|IF_SM}, + {I_ADC, 2, {REG16,REG16,0}, "\320\1\x11\101", IF_8086}, + {I_ADC, 2, {MEMORY,REG32,0}, "\321\300\1\x11\101", IF_386|IF_SM}, + {I_ADC, 2, {REG32,REG32,0}, "\321\1\x11\101", IF_386}, + {I_ADC, 2, {REG8,MEMORY,0}, "\301\1\x12\110", IF_8086|IF_SM}, + {I_ADC, 2, {REG8,REG8,0}, "\1\x12\110", IF_8086}, + {I_ADC, 2, {REG16,MEMORY,0}, "\320\301\1\x13\110", IF_8086|IF_SM}, + {I_ADC, 2, {REG16,REG16,0}, "\320\1\x13\110", IF_8086}, + {I_ADC, 2, {REG32,MEMORY,0}, "\321\301\1\x13\110", IF_386|IF_SM}, + {I_ADC, 2, {REG32,REG32,0}, "\321\1\x13\110", IF_386}, + {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\202\15", IF_8086}, + {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\202\15", IF_386}, + {I_ADC, 2, {REG_AL,IMMEDIATE,0}, "\1\x14\21", IF_8086|IF_SM}, + {I_ADC, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x15\31", IF_8086|IF_SM}, + {I_ADC, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x15\41", IF_386|IF_SM}, + {I_ADC, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, + {I_ADC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, + {I_ADC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, + {I_ADC, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\202\21", IF_8086|IF_SM}, + {I_ADC, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\202\131", IF_8086|IF_SM}, + {I_ADC, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\202\141", IF_386|IF_SM}, + {I_ADD, 2, {MEMORY,REG8,0}, "\300\17\101", IF_8086|IF_SM}, + {I_ADD, 2, {REG8,REG8,0}, "\17\101", IF_8086}, + {I_ADD, 2, {MEMORY,REG16,0}, "\320\300\1\x01\101", IF_8086|IF_SM}, + {I_ADD, 2, {REG16,REG16,0}, "\320\1\x01\101", IF_8086}, + {I_ADD, 2, {MEMORY,REG32,0}, "\321\300\1\x01\101", IF_386|IF_SM}, + {I_ADD, 2, {REG32,REG32,0}, "\321\1\x01\101", IF_386}, + {I_ADD, 2, {REG8,MEMORY,0}, "\301\1\x02\110", IF_8086|IF_SM}, + {I_ADD, 2, {REG8,REG8,0}, "\1\x02\110", IF_8086}, + {I_ADD, 2, {REG16,MEMORY,0}, "\320\301\1\x03\110", IF_8086|IF_SM}, + {I_ADD, 2, {REG16,REG16,0}, "\320\1\x03\110", IF_8086}, + {I_ADD, 2, {REG32,MEMORY,0}, "\321\301\1\x03\110", IF_386|IF_SM}, + {I_ADD, 2, {REG32,REG32,0}, "\321\1\x03\110", IF_386}, + {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\200\15", IF_8086}, + {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\200\15", IF_386}, + {I_ADD, 2, {REG_AL,IMMEDIATE,0}, "\1\x04\21", IF_8086|IF_SM}, + {I_ADD, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x05\31", IF_8086|IF_SM}, + {I_ADD, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x05\41", IF_386|IF_SM}, + {I_ADD, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, + {I_ADD, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, + {I_ADD, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, + {I_ADD, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\200\21", IF_8086|IF_SM}, + {I_ADD, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\200\131", IF_8086|IF_SM}, + {I_ADD, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\200\141", IF_386|IF_SM}, + {I_AND, 2, {MEMORY,REG8,0}, "\300\1\x20\101", IF_8086|IF_SM}, + {I_AND, 2, {REG8,REG8,0}, "\1\x20\101", IF_8086}, + {I_AND, 2, {MEMORY,REG16,0}, "\320\300\1\x21\101", IF_8086|IF_SM}, + {I_AND, 2, {REG16,REG16,0}, "\320\1\x21\101", IF_8086}, + {I_AND, 2, {MEMORY,REG32,0}, "\321\300\1\x21\101", IF_386|IF_SM}, + {I_AND, 2, {REG32,REG32,0}, "\321\1\x21\101", IF_386}, + {I_AND, 2, {REG8,MEMORY,0}, "\301\1\x22\110", IF_8086|IF_SM}, + {I_AND, 2, {REG8,REG8,0}, "\1\x22\110", IF_8086}, + {I_AND, 2, {REG16,MEMORY,0}, "\320\301\1\x23\110", IF_8086|IF_SM}, + {I_AND, 2, {REG16,REG16,0}, "\320\1\x23\110", IF_8086}, + {I_AND, 2, {REG32,MEMORY,0}, "\321\301\1\x23\110", IF_386|IF_SM}, + {I_AND, 2, {REG32,REG32,0}, "\321\1\x23\110", IF_386}, + {I_AND, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\204\15", IF_8086}, + {I_AND, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\204\15", IF_386}, + {I_AND, 2, {REG_AL,IMMEDIATE,0}, "\1\x24\21", IF_8086|IF_SM}, + {I_AND, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x25\31", IF_8086|IF_SM}, + {I_AND, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x25\41", IF_386|IF_SM}, + {I_AND, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, + {I_AND, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, + {I_AND, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, + {I_AND, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\204\21", IF_8086|IF_SM}, + {I_AND, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\204\131", IF_8086|IF_SM}, + {I_AND, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\204\141", IF_386|IF_SM}, + {I_ARPL, 2, {MEMORY,REG16,0}, "\300\1\x63\101", IF_286|IF_PROT|IF_SM}, + {I_ARPL, 2, {REG16,REG16,0}, "\1\x63\101", IF_286|IF_PROT}, + {I_BOUND, 2, {REG16,MEMORY,0}, "\320\301\1\x62\110", IF_186}, + {I_BOUND, 2, {REG32,MEMORY,0}, "\321\301\1\x62\110", IF_386}, + {I_BSF, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBC\110", IF_386|IF_SM}, + {I_BSF, 2, {REG16,REG16,0}, "\320\2\x0F\xBC\110", IF_386}, + {I_BSF, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBC\110", IF_386|IF_SM}, + {I_BSF, 2, {REG32,REG32,0}, "\321\2\x0F\xBC\110", IF_386}, + {I_BSR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBD\110", IF_386|IF_SM}, + {I_BSR, 2, {REG16,REG16,0}, "\320\2\x0F\xBD\110", IF_386}, + {I_BSR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xBD\110", IF_386|IF_SM}, + {I_BSR, 2, {REG32,REG32,0}, "\321\2\x0F\xBD\110", IF_386}, + {I_BSWAP, 1, {REG32,0,0}, "\321\1\x0F\10\xC8", IF_486}, + {I_BT, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA3\101", IF_386|IF_SM}, + {I_BT, 2, {REG16,REG16,0}, "\320\2\x0F\xA3\101", IF_386}, + {I_BT, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA3\101", IF_386|IF_SM}, + {I_BT, 2, {REG32,REG32,0}, "\321\2\x0F\xA3\101", IF_386}, + {I_BT, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\204\25", IF_386|IF_SB}, + {I_BT, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\204\25", IF_386|IF_SB}, + {I_BTC, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xBB\101", IF_386|IF_SM}, + {I_BTC, 2, {REG16,REG16,0}, "\320\2\x0F\xBB\101", IF_386}, + {I_BTC, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xBB\101", IF_386|IF_SM}, + {I_BTC, 2, {REG32,REG32,0}, "\321\2\x0F\xBB\101", IF_386}, + {I_BTC, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\207\25", IF_386|IF_SB}, + {I_BTC, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\207\25", IF_386|IF_SB}, + {I_BTR, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB3\101", IF_386|IF_SM}, + {I_BTR, 2, {REG16,REG16,0}, "\320\2\x0F\xB3\101", IF_386}, + {I_BTR, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB3\101", IF_386|IF_SM}, + {I_BTR, 2, {REG32,REG32,0}, "\321\2\x0F\xB3\101", IF_386}, + {I_BTR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\206\25", IF_386|IF_SB}, + {I_BTR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\206\25", IF_386|IF_SB}, + {I_BTS, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xAB\101", IF_386|IF_SM}, + {I_BTS, 2, {REG16,REG16,0}, "\320\2\x0F\xAB\101", IF_386}, + {I_BTS, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xAB\101", IF_386|IF_SM}, + {I_BTS, 2, {REG32,REG32,0}, "\321\2\x0F\xAB\101", IF_386}, + {I_BTS, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\2\x0F\xBA\205\25", IF_386|IF_SB}, + {I_BTS, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\2\x0F\xBA\205\25", IF_386|IF_SB}, + {I_CALL, 1, {IMMEDIATE,0,0}, "\322\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\xE8\64", IF_8086}, + {I_CALL, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE8\64", IF_386}, + {I_CALL, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\xE8\64", IF_386}, + {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\x9A\35\30", IF_8086}, + {I_CALL, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\x9A\31\30", IF_8086}, + {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\x9A\31\30", IF_8086}, + {I_CALL, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\x9A\41\30", IF_386}, + {I_CALL, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\x9A\41\30", IF_386}, + {I_CALL, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\203", IF_8086}, + {I_CALL, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\203", IF_8086}, + {I_CALL, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\203", IF_386}, + {I_CALL, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\202", IF_386}, + {I_CALL, 1, {REG16,0,0}, "\320\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {REG32,0,0}, "\321\300\1\xFF\202", IF_386}, + {I_CALL, 1, {MEMORY,0,0}, "\322\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\202", IF_8086}, + {I_CALL, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\202", IF_386}, + {I_CBW, 0, {0,0,0}, "\320\1\x98", IF_8086}, + {I_CDQ, 0, {0,0,0}, "\321\1\x99", IF_386}, + {I_CLC, 0, {0,0,0}, "\1\xF8", IF_8086}, + {I_CLD, 0, {0,0,0}, "\1\xFC", IF_8086}, + {I_CLI, 0, {0,0,0}, "\1\xFA", IF_8086}, + {I_CLTS, 0, {0,0,0}, "\2\x0F\x06", IF_286|IF_PRIV}, + {I_CMC, 0, {0,0,0}, "\1\xF5", IF_8086}, + {I_CMP, 2, {MEMORY,REG8,0}, "\300\1\x38\101", IF_8086|IF_SM}, + {I_CMP, 2, {REG8,REG8,0}, "\1\x38\101", IF_8086}, + {I_CMP, 2, {MEMORY,REG16,0}, "\320\300\1\x39\101", IF_8086|IF_SM}, + {I_CMP, 2, {REG16,REG16,0}, "\320\1\x39\101", IF_8086}, + {I_CMP, 2, {MEMORY,REG32,0}, "\321\300\1\x39\101", IF_386|IF_SM}, + {I_CMP, 2, {REG32,REG32,0}, "\321\1\x39\101", IF_386}, + {I_CMP, 2, {REG8,MEMORY,0}, "\301\1\x3A\110", IF_8086|IF_SM}, + {I_CMP, 2, {REG8,REG8,0}, "\1\x3A\110", IF_8086}, + {I_CMP, 2, {REG16,MEMORY,0}, "\320\301\1\x3B\110", IF_8086|IF_SM}, + {I_CMP, 2, {REG16,REG16,0}, "\320\1\x3B\110", IF_8086}, + {I_CMP, 2, {REG32,MEMORY,0}, "\321\301\1\x3B\110", IF_386|IF_SM}, + {I_CMP, 2, {REG32,REG32,0}, "\321\1\x3B\110", IF_386}, + {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\207\15", IF_8086}, + {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\207\15", IF_386}, + {I_CMP, 2, {REG_AL,IMMEDIATE,0}, "\1\x3C\21", IF_8086|IF_SM}, + {I_CMP, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x3D\31", IF_8086|IF_SM}, + {I_CMP, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x3D\41", IF_386|IF_SM}, + {I_CMP, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, + {I_CMP, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, + {I_CMP, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, + {I_CMP, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\207\21", IF_8086|IF_SM}, + {I_CMP, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\207\131", IF_8086|IF_SM}, + {I_CMP, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\207\141", IF_386|IF_SM}, + {I_CMPSB, 0, {0,0,0}, "\332\1\xA6", IF_8086}, + {I_CMPSD, 0, {0,0,0}, "\332\321\1\xA7", IF_386}, + {I_CMPSW, 0, {0,0,0}, "\332\320\1\xA7", IF_8086}, + {I_CMPXCHG, 2, {MEMORY,REG8,0}, "\300\2\x0F\xB0\101", IF_PENT|IF_SM}, + {I_CMPXCHG, 2, {REG8,REG8,0}, "\2\x0F\xB0\101", IF_PENT}, + {I_CMPXCHG, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xB1\101", IF_PENT|IF_SM}, + {I_CMPXCHG, 2, {REG16,REG16,0}, "\320\2\x0F\xB1\101", IF_PENT}, + {I_CMPXCHG, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xB1\101", IF_PENT|IF_SM}, + {I_CMPXCHG, 2, {REG32,REG32,0}, "\321\2\x0F\xB1\101", IF_PENT}, + {I_CMPXCHG486, 2, {MEMORY,REG8,0}, "\300\2\x0F\xA6\101", IF_486|IF_SM|IF_UNDOC}, + {I_CMPXCHG486, 2, {REG8,REG8,0}, "\2\x0F\xA6\101", IF_486|IF_UNDOC}, + {I_CMPXCHG486, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, + {I_CMPXCHG486, 2, {REG16,REG16,0}, "\320\2\x0F\xA7\101", IF_486|IF_UNDOC}, + {I_CMPXCHG486, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xA7\101", IF_486|IF_SM|IF_UNDOC}, + {I_CMPXCHG486, 2, {REG32,REG32,0}, "\321\2\x0F\xA7\101", IF_486|IF_UNDOC}, + {I_CMPXCHG8B, 1, {MEMORY,0,0}, "\300\2\x0F\xC7\201", IF_PENT}, + {I_CPUID, 0, {0,0,0}, "\2\x0F\xA2", IF_PENT}, + {I_CWD, 0, {0,0,0}, "\320\1\x99", IF_8086}, + {I_CWDE, 0, {0,0,0}, "\321\1\x98", IF_386}, + {I_DAA, 0, {0,0,0}, "\1\x27", IF_8086}, + {I_DAS, 0, {0,0,0}, "\1\x2F", IF_8086}, + {I_DEC, 1, {REG16,0,0}, "\320\10\x48", IF_8086}, + {I_DEC, 1, {REG32,0,0}, "\321\10\x48", IF_386}, + {I_DEC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\201", IF_8086}, + {I_DEC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\201", IF_8086}, + {I_DEC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\201", IF_386}, + {I_DIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\206", IF_8086}, + {I_DIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\206", IF_8086}, + {I_DIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\206", IF_386}, + {I_EMMS, 0, {0,0,0}, "\2\x0F\x77", IF_PENT|IF_MMX}, + {I_ENTER, 2, {IMMEDIATE,IMMEDIATE,0}, "\1\xC8\30\25", IF_186}, + {I_EQU, 1, {IMMEDIATE,0,0}, "\0", IF_8086}, + {I_EQU, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\0", IF_8086}, + {I_F2XM1, 0, {0,0,0}, "\2\xD9\xF0", IF_8086|IF_FPU}, + {I_FABS, 0, {0,0,0}, "\2\xD9\xE1", IF_8086|IF_FPU}, + {I_FADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\200", IF_8086|IF_FPU}, + {I_FADD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\200", IF_8086|IF_FPU}, + {I_FADD, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, + {I_FADD, 1, {FPUREG,0,0}, "\1\xD8\10\xC0", IF_8086|IF_FPU}, + {I_FADD, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC0", IF_8086|IF_FPU}, + {I_FADD, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC0", IF_8086|IF_FPU}, + {I_FADDP, 1, {FPUREG,0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, + {I_FADDP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC0", IF_8086|IF_FPU}, + {I_FBLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, + {I_FBLD, 1, {MEMORY,0,0}, "\300\1\xDF\204", IF_8086|IF_FPU}, + {I_FBSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, + {I_FBSTP, 1, {MEMORY,0,0}, "\300\1\xDF\206", IF_8086|IF_FPU}, + {I_FCHS, 0, {0,0,0}, "\2\xD9\xE0", IF_8086|IF_FPU}, + {I_FCLEX, 0, {0,0,0}, "\3\x9B\xDB\xE2", IF_8086|IF_FPU}, + {I_FCMOVB, 1, {FPUREG,0,0}, "\1\xDA\10\xC0", IF_P6|IF_FPU}, + {I_FCMOVB, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC0", IF_P6|IF_FPU}, + {I_FCMOVBE, 1, {FPUREG,0,0}, "\1\xDA\10\xD0", IF_P6|IF_FPU}, + {I_FCMOVBE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD0", IF_P6|IF_FPU}, + {I_FCMOVE, 1, {FPUREG,0,0}, "\1\xDA\10\xC8", IF_P6|IF_FPU}, + {I_FCMOVE, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xC8", IF_P6|IF_FPU}, + {I_FCMOVNB, 1, {FPUREG,0,0}, "\1\xDB\10\xC0", IF_P6|IF_FPU}, + {I_FCMOVNB, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC0", IF_P6|IF_FPU}, + {I_FCMOVNBE, 1, {FPUREG,0,0}, "\1\xDB\10\xD0", IF_P6|IF_FPU}, + {I_FCMOVNBE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD0", IF_P6|IF_FPU}, + {I_FCMOVNE, 1, {FPUREG,0,0}, "\1\xDB\10\xC8", IF_P6|IF_FPU}, + {I_FCMOVNE, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xC8", IF_P6|IF_FPU}, + {I_FCMOVNU, 1, {FPUREG,0,0}, "\1\xDB\10\xD8", IF_P6|IF_FPU}, + {I_FCMOVNU, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xD8", IF_P6|IF_FPU}, + {I_FCMOVU, 1, {FPUREG,0,0}, "\1\xDA\10\xD8", IF_P6|IF_FPU}, + {I_FCMOVU, 2, {FPU0,FPUREG,0}, "\1\xDA\11\xD8", IF_P6|IF_FPU}, + {I_FCOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\202", IF_8086|IF_FPU}, + {I_FCOM, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\202", IF_8086|IF_FPU}, + {I_FCOM, 1, {FPUREG,0,0}, "\1\xD8\10\xD0", IF_8086|IF_FPU}, + {I_FCOM, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD0", IF_8086|IF_FPU}, + {I_FCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xF0", IF_P6|IF_FPU}, + {I_FCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xF0", IF_P6|IF_FPU}, + {I_FCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xF0", IF_P6|IF_FPU}, + {I_FCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xF0", IF_P6|IF_FPU}, + {I_FCOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\203", IF_8086|IF_FPU}, + {I_FCOMP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\203", IF_8086|IF_FPU}, + {I_FCOMP, 1, {FPUREG,0,0}, "\1\xD8\10\xD8", IF_8086|IF_FPU}, + {I_FCOMP, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xD8", IF_8086|IF_FPU}, + {I_FCOMPP, 0, {0,0,0}, "\2\xDE\xD9", IF_8086|IF_FPU}, + {I_FCOS, 0, {0,0,0}, "\2\xD9\xFF", IF_386|IF_FPU}, + {I_FDECSTP, 0, {0,0,0}, "\2\xD9\xF6", IF_8086|IF_FPU}, + {I_FDISI, 0, {0,0,0}, "\3\x9B\xDB\xE1", IF_8086|IF_FPU}, + {I_FDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\206", IF_8086|IF_FPU}, + {I_FDIV, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\206", IF_8086|IF_FPU}, + {I_FDIV, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, + {I_FDIV, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF8", IF_8086|IF_FPU}, + {I_FDIV, 1, {FPUREG,0,0}, "\1\xD8\10\xF0", IF_8086|IF_FPU}, + {I_FDIV, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF0", IF_8086|IF_FPU}, + {I_FDIVP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, + {I_FDIVP, 1, {FPUREG,0,0}, "\1\xDE\10\xF8", IF_8086|IF_FPU}, + {I_FDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\207", IF_8086|IF_FPU}, + {I_FDIVR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\207", IF_8086|IF_FPU}, + {I_FDIVR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, + {I_FDIVR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xF0", IF_8086|IF_FPU}, + {I_FDIVR, 1, {FPUREG,0,0}, "\1\xD8\10\xF8", IF_8086|IF_FPU}, + {I_FDIVR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xF8", IF_8086|IF_FPU}, + {I_FDIVRP, 1, {FPUREG,0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, + {I_FDIVRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xF0", IF_8086|IF_FPU}, + {I_FEMMS, 0, {0,0,0}, "\2\x0F\x0E", IF_PENT|IF_3DNOW}, + {I_FENI, 0, {0,0,0}, "\3\x9B\xDB\xE0", IF_8086|IF_FPU}, + {I_FFREE, 1, {FPUREG,0,0}, "\1\xDD\10\xC0", IF_8086|IF_FPU}, + {I_FFREEP, 1, {FPUREG,0,0}, "\1\xDF\10\xC0", IF_286|IF_FPU|IF_UNDOC}, + {I_FIADD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\200", IF_8086|IF_FPU}, + {I_FIADD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\200", IF_8086|IF_FPU}, + {I_FICOM, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\202", IF_8086|IF_FPU}, + {I_FICOM, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\202", IF_8086|IF_FPU}, + {I_FICOMP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\203", IF_8086|IF_FPU}, + {I_FICOMP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\203", IF_8086|IF_FPU}, + {I_FIDIV, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\206", IF_8086|IF_FPU}, + {I_FIDIV, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\206", IF_8086|IF_FPU}, + {I_FIDIVR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\207", IF_8086|IF_FPU}, + {I_FIDIVR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\207", IF_8086|IF_FPU}, + {I_FILD, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\200", IF_8086|IF_FPU}, + {I_FILD, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\200", IF_8086|IF_FPU}, + {I_FILD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\205", IF_8086|IF_FPU}, + {I_FIMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\201", IF_8086|IF_FPU}, + {I_FIMUL, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\201", IF_8086|IF_FPU}, + {I_FINCSTP, 0, {0,0,0}, "\2\xD9\xF7", IF_8086|IF_FPU}, + {I_FINIT, 0, {0,0,0}, "\3\x9B\xDB\xE3", IF_8086|IF_FPU}, + {I_FIST, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\202", IF_8086|IF_FPU}, + {I_FIST, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\202", IF_8086|IF_FPU}, + {I_FISTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDB\203", IF_8086|IF_FPU}, + {I_FISTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDF\203", IF_8086|IF_FPU}, + {I_FISTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\207", IF_8086|IF_FPU}, + {I_FISTTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xDD\201", IF_PRESCOTT|IF_FPU}, + {I_FISTTP, 1, {MEMORY|BITS16,0,0}, "\300\1\xDB\201", IF_PRESCOTT|IF_FPU}, + {I_FISTTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDF\201", IF_PRESCOTT|IF_FPU}, + {I_FISUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\204", IF_8086|IF_FPU}, + {I_FISUB, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\204", IF_8086|IF_FPU}, + {I_FISUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xDA\205", IF_8086|IF_FPU}, + {I_FISUBR, 1, {MEMORY|BITS16,0,0}, "\300\1\xDE\205", IF_8086|IF_FPU}, + {I_FLD, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\200", IF_8086|IF_FPU}, + {I_FLD, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\200", IF_8086|IF_FPU}, + {I_FLD, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\205", IF_8086|IF_FPU}, + {I_FLD, 1, {FPUREG,0,0}, "\1\xD9\10\xC0", IF_8086|IF_FPU}, + {I_FLD1, 0, {0,0,0}, "\2\xD9\xE8", IF_8086|IF_FPU}, + {I_FLDCW, 1, {MEMORY,0,0}, "\300\1\xD9\205", IF_8086|IF_FPU|IF_SW}, + {I_FLDENV, 1, {MEMORY,0,0}, "\300\1\xD9\204", IF_8086|IF_FPU}, + {I_FLDL2E, 0, {0,0,0}, "\2\xD9\xEA", IF_8086|IF_FPU}, + {I_FLDL2T, 0, {0,0,0}, "\2\xD9\xE9", IF_8086|IF_FPU}, + {I_FLDLG2, 0, {0,0,0}, "\2\xD9\xEC", IF_8086|IF_FPU}, + {I_FLDLN2, 0, {0,0,0}, "\2\xD9\xED", IF_8086|IF_FPU}, + {I_FLDPI, 0, {0,0,0}, "\2\xD9\xEB", IF_8086|IF_FPU}, + {I_FLDZ, 0, {0,0,0}, "\2\xD9\xEE", IF_8086|IF_FPU}, + {I_FMUL, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\201", IF_8086|IF_FPU}, + {I_FMUL, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\201", IF_8086|IF_FPU}, + {I_FMUL, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, + {I_FMUL, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xC8", IF_8086|IF_FPU}, + {I_FMUL, 1, {FPUREG,0,0}, "\1\xD8\10\xC8", IF_8086|IF_FPU}, + {I_FMUL, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xC8", IF_8086|IF_FPU}, + {I_FMULP, 1, {FPUREG,0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, + {I_FMULP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xC8", IF_8086|IF_FPU}, + {I_FNCLEX, 0, {0,0,0}, "\2\xDB\xE2", IF_8086|IF_FPU}, + {I_FNDISI, 0, {0,0,0}, "\2\xDB\xE1", IF_8086|IF_FPU}, + {I_FNENI, 0, {0,0,0}, "\2\xDB\xE0", IF_8086|IF_FPU}, + {I_FNINIT, 0, {0,0,0}, "\2\xDB\xE3", IF_8086|IF_FPU}, + {I_FNOP, 0, {0,0,0}, "\2\xD9\xD0", IF_8086|IF_FPU}, + {I_FNSAVE, 1, {MEMORY,0,0}, "\300\1\xDD\206", IF_8086|IF_FPU}, + {I_FNSTCW, 1, {MEMORY,0,0}, "\300\1\xD9\207", IF_8086|IF_FPU|IF_SW}, + {I_FNSTENV, 1, {MEMORY,0,0}, "\300\1\xD9\206", IF_8086|IF_FPU}, + {I_FNSTSW, 1, {MEMORY,0,0}, "\300\1\xDD\207", IF_8086|IF_FPU|IF_SW}, + {I_FNSTSW, 1, {REG_AX,0,0}, "\2\xDF\xE0", IF_286|IF_FPU}, + {I_FPATAN, 0, {0,0,0}, "\2\xD9\xF3", IF_8086|IF_FPU}, + {I_FPREM, 0, {0,0,0}, "\2\xD9\xF8", IF_8086|IF_FPU}, + {I_FPREM1, 0, {0,0,0}, "\2\xD9\xF5", IF_386|IF_FPU}, + {I_FPTAN, 0, {0,0,0}, "\2\xD9\xF2", IF_8086|IF_FPU}, + {I_FRNDINT, 0, {0,0,0}, "\2\xD9\xFC", IF_8086|IF_FPU}, + {I_FRSTOR, 1, {MEMORY,0,0}, "\300\1\xDD\204", IF_8086|IF_FPU}, + {I_FSAVE, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\206", IF_8086|IF_FPU}, + {I_FSCALE, 0, {0,0,0}, "\2\xD9\xFD", IF_8086|IF_FPU}, + {I_FSETPM, 0, {0,0,0}, "\2\xDB\xE4", IF_286|IF_FPU}, + {I_FSIN, 0, {0,0,0}, "\2\xD9\xFE", IF_386|IF_FPU}, + {I_FSINCOS, 0, {0,0,0}, "\2\xD9\xFB", IF_386|IF_FPU}, + {I_FSQRT, 0, {0,0,0}, "\2\xD9\xFA", IF_8086|IF_FPU}, + {I_FST, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\202", IF_8086|IF_FPU}, + {I_FST, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\202", IF_8086|IF_FPU}, + {I_FST, 1, {FPUREG,0,0}, "\1\xDD\10\xD0", IF_8086|IF_FPU}, + {I_FSTCW, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\207", IF_8086|IF_FPU|IF_SW}, + {I_FSTENV, 1, {MEMORY,0,0}, "\300\2\x9B\xD9\206", IF_8086|IF_FPU}, + {I_FSTP, 1, {MEMORY|BITS32,0,0}, "\300\1\xD9\203", IF_8086|IF_FPU}, + {I_FSTP, 1, {MEMORY|BITS64,0,0}, "\300\1\xDD\203", IF_8086|IF_FPU}, + {I_FSTP, 1, {MEMORY|BITS80,0,0}, "\300\1\xDB\207", IF_8086|IF_FPU}, + {I_FSTP, 1, {FPUREG,0,0}, "\1\xDD\10\xD8", IF_8086|IF_FPU}, + {I_FSTSW, 1, {MEMORY,0,0}, "\300\2\x9B\xDD\207", IF_8086|IF_FPU|IF_SW}, + {I_FSTSW, 1, {REG_AX,0,0}, "\3\x9B\xDF\xE0", IF_286|IF_FPU}, + {I_FSUB, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\204", IF_8086|IF_FPU}, + {I_FSUB, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\204", IF_8086|IF_FPU}, + {I_FSUB, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, + {I_FSUB, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE8", IF_8086|IF_FPU}, + {I_FSUB, 1, {FPUREG,0,0}, "\1\xD8\10\xE0", IF_8086|IF_FPU}, + {I_FSUB, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE0", IF_8086|IF_FPU}, + {I_FSUBP, 1, {FPUREG,0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, + {I_FSUBP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE8", IF_8086|IF_FPU}, + {I_FSUBR, 1, {MEMORY|BITS32,0,0}, "\300\1\xD8\205", IF_8086|IF_FPU}, + {I_FSUBR, 1, {MEMORY|BITS64,0,0}, "\300\1\xDC\205", IF_8086|IF_FPU}, + {I_FSUBR, 1, {FPUREG|TO,0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, + {I_FSUBR, 2, {FPUREG,FPU0,0}, "\1\xDC\10\xE0", IF_8086|IF_FPU}, + {I_FSUBR, 1, {FPUREG,0,0}, "\1\xD8\10\xE8", IF_8086|IF_FPU}, + {I_FSUBR, 2, {FPU0,FPUREG,0}, "\1\xD8\11\xE8", IF_8086|IF_FPU}, + {I_FSUBRP, 1, {FPUREG,0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, + {I_FSUBRP, 2, {FPUREG,FPU0,0}, "\1\xDE\10\xE0", IF_8086|IF_FPU}, + {I_FTST, 0, {0,0,0}, "\2\xD9\xE4", IF_8086|IF_FPU}, + {I_FUCOM, 1, {FPUREG,0,0}, "\1\xDD\10\xE0", IF_386|IF_FPU}, + {I_FUCOM, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE0", IF_386|IF_FPU}, + {I_FUCOMI, 1, {FPUREG,0,0}, "\1\xDB\10\xE8", IF_P6|IF_FPU}, + {I_FUCOMI, 2, {FPU0,FPUREG,0}, "\1\xDB\11\xE8", IF_P6|IF_FPU}, + {I_FUCOMIP, 1, {FPUREG,0,0}, "\1\xDF\10\xE8", IF_P6|IF_FPU}, + {I_FUCOMIP, 2, {FPU0,FPUREG,0}, "\1\xDF\11\xE8", IF_P6|IF_FPU}, + {I_FUCOMP, 1, {FPUREG,0,0}, "\1\xDD\10\xE8", IF_386|IF_FPU}, + {I_FUCOMP, 2, {FPU0,FPUREG,0}, "\1\xDD\11\xE8", IF_386|IF_FPU}, + {I_FUCOMPP, 0, {0,0,0}, "\2\xDA\xE9", IF_386|IF_FPU}, + {I_FXAM, 0, {0,0,0}, "\2\xD9\xE5", IF_8086|IF_FPU}, + {I_FXCH, 0, {0,0,0}, "\2\xD9\xC9", IF_8086|IF_FPU}, + {I_FXCH, 1, {FPUREG,0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, + {I_FXCH, 2, {FPUREG,FPU0,0}, "\1\xD9\10\xC8", IF_8086|IF_FPU}, + {I_FXCH, 2, {FPU0,FPUREG,0}, "\1\xD9\11\xC8", IF_8086|IF_FPU}, + {I_FXTRACT, 0, {0,0,0}, "\2\xD9\xF4", IF_8086|IF_FPU}, + {I_FYL2X, 0, {0,0,0}, "\2\xD9\xF1", IF_8086|IF_FPU}, + {I_FYL2XP1, 0, {0,0,0}, "\2\xD9\xF9", IF_8086|IF_FPU}, + {I_HLT, 0, {0,0,0}, "\1\xF4", IF_8086|IF_PRIV}, + {I_IDIV, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\207", IF_8086}, + {I_IDIV, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\207", IF_8086}, + {I_IDIV, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\207", IF_386}, + {I_IMUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\205", IF_8086}, + {I_IMUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\205", IF_8086}, + {I_IMUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\205", IF_386}, + {I_IMUL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xAF\110", IF_386|IF_SM}, + {I_IMUL, 2, {REG16,REG16,0}, "\320\2\x0F\xAF\110", IF_386}, + {I_IMUL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xAF\110", IF_386|IF_SM}, + {I_IMUL, 2, {REG32,REG32,0}, "\321\2\x0F\xAF\110", IF_386}, + {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS8}, "\320\301\1\x6B\110\16", IF_186|IF_SM}, + {I_IMUL, 3, {REG16,MEMORY,IMMEDIATE|BITS16}, "\320\301\1\x69\110\32", IF_186|IF_SM}, + {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS8}, "\320\1\x6B\110\16", IF_186}, + {I_IMUL, 3, {REG16,REG16,IMMEDIATE|BITS16}, "\320\1\x69\110\32", IF_186}, + {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS8}, "\321\301\1\x6B\110\16", IF_386|IF_SM}, + {I_IMUL, 3, {REG32,MEMORY,IMMEDIATE|BITS32}, "\321\301\1\x69\110\42", IF_386|IF_SM}, + {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS8}, "\321\1\x6B\110\16", IF_386}, + {I_IMUL, 3, {REG32,REG32,IMMEDIATE|BITS32}, "\321\1\x69\110\42", IF_386}, + {I_IMUL, 2, {REG16,IMMEDIATE|BITS8,0}, "\320\1\x6B\100\15", IF_186}, + {I_IMUL, 2, {REG16,IMMEDIATE|BITS16,0}, "\320\1\x69\100\31", IF_186}, + {I_IMUL, 2, {REG32,IMMEDIATE|BITS8,0}, "\321\1\x6B\100\15", IF_386}, + {I_IMUL, 2, {REG32,IMMEDIATE|BITS32,0}, "\321\1\x69\100\41", IF_386}, + {I_IN, 2, {REG_AL,IMMEDIATE,0}, "\1\xE4\25", IF_8086|IF_SB}, + {I_IN, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xE5\25", IF_8086|IF_SB}, + {I_IN, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xE5\25", IF_386|IF_SB}, + {I_IN, 2, {REG_AL,REG_DX,0}, "\1\xEC", IF_8086}, + {I_IN, 2, {REG_AX,REG_DX,0}, "\320\1\xED", IF_8086}, + {I_IN, 2, {REG_EAX,REG_DX,0}, "\321\1\xED", IF_386}, + {I_INC, 1, {REG16,0,0}, "\320\10\x40", IF_8086}, + {I_INC, 1, {REG32,0,0}, "\321\10\x40", IF_386}, + {I_INC, 1, {REGMEM|BITS8,0,0}, "\300\1\xFE\200", IF_8086}, + {I_INC, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\200", IF_8086}, + {I_INC, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\200", IF_386}, + {I_INSB, 0, {0,0,0}, "\1\x6C", IF_186}, + {I_INSD, 0, {0,0,0}, "\321\1\x6D", IF_386}, + {I_INSW, 0, {0,0,0}, "\320\1\x6D", IF_186}, + {I_INT, 1, {IMMEDIATE,0,0}, "\1\xCD\24", IF_8086|IF_SB}, + {I_INT1, 0, {0,0,0}, "\1\xF1", IF_386}, + {I_INT3, 0, {0,0,0}, "\1\xCC", IF_8086}, + {I_INTO, 0, {0,0,0}, "\1\xCE", IF_8086}, + {I_INVD, 0, {0,0,0}, "\2\x0F\x08", IF_486|IF_PRIV}, + {I_INVLPG, 1, {MEMORY,0,0}, "\300\2\x0F\x01\207", IF_486|IF_PRIV}, + {I_IRET, 0, {0,0,0}, "\322\1\xCF", IF_8086}, + {I_IRETD, 0, {0,0,0}, "\321\1\xCF", IF_386}, + {I_IRETW, 0, {0,0,0}, "\320\1\xCF", IF_8086}, + {I_JCXZ, 1, {IMMEDIATE,0,0}, "\310\1\xE3\50", IF_8086}, + {I_JECXZ, 1, {IMMEDIATE,0,0}, "\311\1\xE3\50", IF_386}, + {I_JMP, 1, {IMMEDIATE|SHORT,0,0}, "\1\xEB\50", IF_8086}, + {I_JMP, 1, {IMMEDIATE,0,0}, "\322\1\xE9\64", IF_8086}, + {I_JMP, 1, {IMMEDIATE|BITS16,0,0}, "\320\1\xE9\64", IF_8086}, + {I_JMP, 1, {IMMEDIATE|BITS32,0,0}, "\321\1\xE9\64", IF_386}, + {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE,0}, "\322\1\xEA\35\30", IF_8086}, + {I_JMP, 2, {IMMEDIATE|BITS16|COLON,IMMEDIATE,0}, "\320\1\xEA\31\30", IF_8086}, + {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS16,0}, "\320\1\xEA\31\30", IF_8086}, + {I_JMP, 2, {IMMEDIATE|BITS32|COLON,IMMEDIATE,0}, "\321\1\xEA\41\30", IF_386}, + {I_JMP, 2, {IMMEDIATE|COLON,IMMEDIATE|BITS32,0}, "\321\1\xEA\41\30", IF_386}, + {I_JMP, 1, {MEMORY|FAR,0,0}, "\322\300\1\xFF\205", IF_8086}, + {I_JMP, 1, {MEMORY|BITS16|FAR,0,0}, "\320\300\1\xFF\205", IF_8086}, + {I_JMP, 1, {MEMORY|BITS32|FAR,0,0}, "\321\300\1\xFF\205", IF_386}, + {I_JMP, 1, {MEMORY|NEAR,0,0}, "\322\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS16|NEAR,0,0}, "\320\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS32|NEAR,0,0}, "\321\300\1\xFF\204", IF_386}, + {I_JMP, 1, {REG16,0,0}, "\320\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {REG32,0,0}, "\321\300\1\xFF\204", IF_386}, + {I_JMP, 1, {MEMORY,0,0}, "\322\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS16,0,0}, "\320\300\1\xFF\204", IF_8086}, + {I_JMP, 1, {MEMORY|BITS32,0,0}, "\321\300\1\xFF\204", IF_386}, + {I_JMPE, 1, {IMMEDIATE,0,0}, "\322\2\x0F\xB8\64", IF_IA64}, + {I_JMPE, 1, {IMMEDIATE|BITS16,0,0}, "\320\2\x0F\xB8\64", IF_IA64}, + {I_JMPE, 1, {IMMEDIATE|BITS32,0,0}, "\321\2\x0F\xB8\64", IF_IA64}, + {I_JMPE, 1, {REGMEM|BITS16,0,0}, "\320\2\x0F\x00\206", IF_IA64}, + {I_JMPE, 1, {REGMEM|BITS32,0,0}, "\321\2\x0F\x00\206", IF_IA64}, + {I_LAHF, 0, {0,0,0}, "\1\x9F", IF_8086}, + {I_LAR, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x02\110", IF_286|IF_PROT|IF_SM}, + {I_LAR, 2, {REG16,REG16,0}, "\320\2\x0F\x02\110", IF_286|IF_PROT}, + {I_LAR, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x02\110", IF_386|IF_PROT|IF_SM}, + {I_LAR, 2, {REG32,REG32,0}, "\321\2\x0F\x02\110", IF_386|IF_PROT}, + {I_LDS, 2, {REG16,MEMORY,0}, "\320\301\1\xC5\110", IF_8086}, + {I_LDS, 2, {REG32,MEMORY,0}, "\321\301\1\xC5\110", IF_386}, + {I_LEA, 2, {REG16,MEMORY,0}, "\320\301\1\x8D\110", IF_8086}, + {I_LEA, 2, {REG32,MEMORY,0}, "\321\301\1\x8D\110", IF_386}, + {I_LEAVE, 0, {0,0,0}, "\1\xC9", IF_186}, + {I_LES, 2, {REG16,MEMORY,0}, "\320\301\1\xC4\110", IF_8086}, + {I_LES, 2, {REG32,MEMORY,0}, "\321\301\1\xC4\110", IF_386}, + {I_LFS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB4\110", IF_386}, + {I_LFS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB4\110", IF_386}, + {I_LGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\202", IF_286|IF_PRIV}, + {I_LGS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB5\110", IF_386}, + {I_LGS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB5\110", IF_386}, + {I_LIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\203", IF_286|IF_PRIV}, + {I_LLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, + {I_LLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, + {I_LLDT, 1, {REG16,0,0}, "\1\x0F\17\202", IF_286|IF_PROT|IF_PRIV}, + {I_LMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, + {I_LMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\206", IF_286|IF_PRIV}, + {I_LMSW, 1, {REG16,0,0}, "\2\x0F\x01\206", IF_286|IF_PRIV}, + {I_LOADALL, 0, {0,0,0}, "\2\x0F\x07", IF_386|IF_UNDOC}, + {I_LOADALL286, 0, {0,0,0}, "\2\x0F\x05", IF_286|IF_UNDOC}, + {I_LODSB, 0, {0,0,0}, "\1\xAC", IF_8086}, + {I_LODSD, 0, {0,0,0}, "\321\1\xAD", IF_386}, + {I_LODSW, 0, {0,0,0}, "\320\1\xAD", IF_8086}, + {I_LOOP, 1, {IMMEDIATE,0,0}, "\312\1\xE2\50", IF_8086}, + {I_LOOP, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE2\50", IF_8086}, + {I_LOOP, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE2\50", IF_386}, + {I_LOOPE, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, + {I_LOOPE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, + {I_LOOPE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, + {I_LOOPNE, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, + {I_LOOPNE, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, + {I_LOOPNE, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, + {I_LOOPNZ, 1, {IMMEDIATE,0,0}, "\312\1\xE0\50", IF_8086}, + {I_LOOPNZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE0\50", IF_8086}, + {I_LOOPNZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE0\50", IF_386}, + {I_LOOPZ, 1, {IMMEDIATE,0,0}, "\312\1\xE1\50", IF_8086}, + {I_LOOPZ, 2, {IMMEDIATE,REG_CX,0}, "\310\1\xE1\50", IF_8086}, + {I_LOOPZ, 2, {IMMEDIATE,REG_ECX,0}, "\311\1\xE1\50", IF_386}, + {I_LSL, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x03\110", IF_286|IF_PROT|IF_SM}, + {I_LSL, 2, {REG16,REG16,0}, "\320\2\x0F\x03\110", IF_286|IF_PROT}, + {I_LSL, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x03\110", IF_386|IF_PROT|IF_SM}, + {I_LSL, 2, {REG32,REG32,0}, "\321\2\x0F\x03\110", IF_386|IF_PROT}, + {I_LSS, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB2\110", IF_386}, + {I_LSS, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\xB2\110", IF_386}, + {I_LTR, 1, {MEMORY,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, + {I_LTR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, + {I_LTR, 1, {REG16,0,0}, "\1\x0F\17\203", IF_286|IF_PROT|IF_PRIV}, + {I_MONITOR, 0, {0,0,0}, "\3\x0F\x01\xC8", IF_PRESCOTT}, + {I_MOV, 2, {MEMORY,REG_SREG,0}, "\300\1\x8C\101", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,REG_SREG,0}, "\320\1\x8C\101", IF_8086}, + {I_MOV, 2, {REG32,REG_SREG,0}, "\321\1\x8C\101", IF_386}, + {I_MOV, 2, {REG_SREG,MEMORY,0}, "\301\1\x8E\110", IF_8086|IF_SM}, + {I_MOV, 2, {REG_SREG,REG16,0}, "\1\x8E\110", IF_8086}, + {I_MOV, 2, {REG_SREG,REG32,0}, "\1\x8E\110", IF_386}, + {I_MOV, 2, {REG_AL,MEM_OFFS,0}, "\301\1\xA0\45", IF_8086|IF_SM}, + {I_MOV, 2, {REG_AX,MEM_OFFS,0}, "\301\320\1\xA1\45", IF_8086|IF_SM}, + {I_MOV, 2, {REG_EAX,MEM_OFFS,0}, "\301\321\1\xA1\45", IF_386|IF_SM}, + {I_MOV, 2, {MEM_OFFS,REG_AL,0}, "\300\1\xA2\44", IF_8086|IF_SM}, + {I_MOV, 2, {MEM_OFFS,REG_AX,0}, "\300\320\1\xA3\44", IF_8086|IF_SM}, + {I_MOV, 2, {MEM_OFFS,REG_EAX,0}, "\300\321\1\xA3\44", IF_386|IF_SM}, + {I_MOV, 2, {REG32,REG_CREG,0}, "\2\x0F\x20\101", IF_386|IF_PRIV}, + {I_MOV, 2, {REG32,REG_DREG,0}, "\2\x0F\x21\101", IF_386|IF_PRIV}, + {I_MOV, 2, {REG32,REG_TREG,0}, "\2\x0F\x24\101", IF_386|IF_PRIV}, + {I_MOV, 2, {REG_CREG,REG32,0}, "\2\x0F\x22\110", IF_386|IF_PRIV}, + {I_MOV, 2, {REG_DREG,REG32,0}, "\2\x0F\x23\110", IF_386|IF_PRIV}, + {I_MOV, 2, {REG_TREG,REG32,0}, "\2\x0F\x26\110", IF_386|IF_PRIV}, + {I_MOV, 2, {MEMORY,REG8,0}, "\300\1\x88\101", IF_8086|IF_SM}, + {I_MOV, 2, {REG8,REG8,0}, "\1\x88\101", IF_8086}, + {I_MOV, 2, {MEMORY,REG16,0}, "\320\300\1\x89\101", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,REG16,0}, "\320\1\x89\101", IF_8086}, + {I_MOV, 2, {MEMORY,REG32,0}, "\321\300\1\x89\101", IF_386|IF_SM}, + {I_MOV, 2, {REG32,REG32,0}, "\321\1\x89\101", IF_386}, + {I_MOV, 2, {REG8,MEMORY,0}, "\301\1\x8A\110", IF_8086|IF_SM}, + {I_MOV, 2, {REG8,REG8,0}, "\1\x8A\110", IF_8086}, + {I_MOV, 2, {REG16,MEMORY,0}, "\320\301\1\x8B\110", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,REG16,0}, "\320\1\x8B\110", IF_8086}, + {I_MOV, 2, {REG32,MEMORY,0}, "\321\301\1\x8B\110", IF_386|IF_SM}, + {I_MOV, 2, {REG32,REG32,0}, "\321\1\x8B\110", IF_386}, + {I_MOV, 2, {REG8,IMMEDIATE,0}, "\10\xB0\21", IF_8086|IF_SM}, + {I_MOV, 2, {REG16,IMMEDIATE,0}, "\320\10\xB8\31", IF_8086|IF_SM}, + {I_MOV, 2, {REG32,IMMEDIATE,0}, "\321\10\xB8\41", IF_386|IF_SM}, + {I_MOV, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, + {I_MOV, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, + {I_MOV, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, + {I_MOV, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xC6\200\21", IF_8086|IF_SM}, + {I_MOV, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xC7\200\31", IF_8086|IF_SM}, + {I_MOV, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xC7\200\41", IF_386|IF_SM}, + {I_MOVD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6E\110", IF_PENT|IF_MMX|IF_SD}, + {I_MOVD, 2, {MMXREG,REG32,0}, "\2\x0F\x6E\110", IF_PENT|IF_MMX}, + {I_MOVD, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7E\101", IF_PENT|IF_MMX|IF_SD}, + {I_MOVD, 2, {REG32,MMXREG,0}, "\2\x0F\x7E\101", IF_PENT|IF_MMX}, + {I_MOVQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6F\110", IF_PENT|IF_MMX|IF_SM}, + {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6F\110", IF_PENT|IF_MMX}, + {I_MOVQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\x7F\101", IF_PENT|IF_MMX|IF_SM}, + {I_MOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x7F\101", IF_PENT|IF_MMX}, + {I_MOVSB, 0, {0,0,0}, "\1\xA4", IF_8086}, + {I_MOVSD, 0, {0,0,0}, "\321\1\xA5", IF_386}, + {I_MOVSW, 0, {0,0,0}, "\320\1\xA5", IF_8086}, + {I_MOVSX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xBE\110", IF_386|IF_SB}, + {I_MOVSX, 2, {REG16,REG8,0}, "\320\2\x0F\xBE\110", IF_386}, + {I_MOVSX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xBE\110", IF_386}, + {I_MOVSX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xBF\110", IF_386}, + {I_MOVZX, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\xB6\110", IF_386|IF_SB}, + {I_MOVZX, 2, {REG16,REG8,0}, "\320\2\x0F\xB6\110", IF_386}, + {I_MOVZX, 2, {REG32,REGMEM|BITS8,0}, "\321\301\2\x0F\xB6\110", IF_386}, + {I_MOVZX, 2, {REG32,REGMEM|BITS16,0}, "\321\301\2\x0F\xB7\110", IF_386}, + {I_MUL, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\204", IF_8086}, + {I_MUL, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\204", IF_8086}, + {I_MUL, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\204", IF_386}, + {I_MWAIT, 0, {0,0,0}, "\3\x0F\x01\xC9", IF_PRESCOTT}, + {I_NEG, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\203", IF_8086}, + {I_NEG, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\203", IF_8086}, + {I_NEG, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\203", IF_386}, + {I_NOP, 0, {0,0,0}, "\1\x90", IF_8086}, + {I_NOT, 1, {REGMEM|BITS8,0,0}, "\300\1\xF6\202", IF_8086}, + {I_NOT, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xF7\202", IF_8086}, + {I_NOT, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xF7\202", IF_386}, + {I_OR, 2, {MEMORY,REG8,0}, "\300\1\x08\101", IF_8086|IF_SM}, + {I_OR, 2, {REG8,REG8,0}, "\1\x08\101", IF_8086}, + {I_OR, 2, {MEMORY,REG16,0}, "\320\300\1\x09\101", IF_8086|IF_SM}, + {I_OR, 2, {REG16,REG16,0}, "\320\1\x09\101", IF_8086}, + {I_OR, 2, {MEMORY,REG32,0}, "\321\300\1\x09\101", IF_386|IF_SM}, + {I_OR, 2, {REG32,REG32,0}, "\321\1\x09\101", IF_386}, + {I_OR, 2, {REG8,MEMORY,0}, "\301\1\x0A\110", IF_8086|IF_SM}, + {I_OR, 2, {REG8,REG8,0}, "\1\x0A\110", IF_8086}, + {I_OR, 2, {REG16,MEMORY,0}, "\320\301\1\x0B\110", IF_8086|IF_SM}, + {I_OR, 2, {REG16,REG16,0}, "\320\1\x0B\110", IF_8086}, + {I_OR, 2, {REG32,MEMORY,0}, "\321\301\1\x0B\110", IF_386|IF_SM}, + {I_OR, 2, {REG32,REG32,0}, "\321\1\x0B\110", IF_386}, + {I_OR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\201\15", IF_8086}, + {I_OR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\201\15", IF_386}, + {I_OR, 2, {REG_AL,IMMEDIATE,0}, "\1\x0C\21", IF_8086|IF_SM}, + {I_OR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x0D\31", IF_8086|IF_SM}, + {I_OR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x0D\41", IF_386|IF_SM}, + {I_OR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, + {I_OR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, + {I_OR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, + {I_OR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\201\21", IF_8086|IF_SM}, + {I_OR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\201\131", IF_8086|IF_SM}, + {I_OR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\201\141", IF_386|IF_SM}, + {I_OUT, 2, {IMMEDIATE,REG_AL,0}, "\1\xE6\24", IF_8086|IF_SB}, + {I_OUT, 2, {IMMEDIATE,REG_AX,0}, "\320\1\xE7\24", IF_8086|IF_SB}, + {I_OUT, 2, {IMMEDIATE,REG_EAX,0}, "\321\1\xE7\24", IF_386|IF_SB}, + {I_OUT, 2, {REG_DX,REG_AL,0}, "\1\xEE", IF_8086}, + {I_OUT, 2, {REG_DX,REG_AX,0}, "\320\1\xEF", IF_8086}, + {I_OUT, 2, {REG_DX,REG_EAX,0}, "\321\1\xEF", IF_386}, + {I_OUTSB, 0, {0,0,0}, "\1\x6E", IF_186}, + {I_OUTSD, 0, {0,0,0}, "\321\1\x6F", IF_386}, + {I_OUTSW, 0, {0,0,0}, "\320\1\x6F", IF_186}, + {I_PACKSSDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6B\110", IF_PENT|IF_MMX|IF_SM}, + {I_PACKSSDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6B\110", IF_PENT|IF_MMX}, + {I_PACKSSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x63\110", IF_PENT|IF_MMX|IF_SM}, + {I_PACKSSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x63\110", IF_PENT|IF_MMX}, + {I_PACKUSWB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x67\110", IF_PENT|IF_MMX|IF_SM}, + {I_PACKUSWB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x67\110", IF_PENT|IF_MMX}, + {I_PADDB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFC\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFC\110", IF_PENT|IF_MMX}, + {I_PADDD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFE\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFE\110", IF_PENT|IF_MMX}, + {I_PADDSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEC\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEC\110", IF_PENT|IF_MMX}, + {I_PADDSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x51\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PADDSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x51\110", IF_PENT|IF_MMX|IF_CYRIX}, + {I_PADDSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xED\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xED\110", IF_PENT|IF_MMX}, + {I_PADDUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDC\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDC\110", IF_PENT|IF_MMX}, + {I_PADDUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDD\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDD\110", IF_PENT|IF_MMX}, + {I_PADDW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFD\110", IF_PENT|IF_MMX|IF_SM}, + {I_PADDW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFD\110", IF_PENT|IF_MMX}, + {I_PAND, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDB\110", IF_PENT|IF_MMX|IF_SM}, + {I_PAND, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDB\110", IF_PENT|IF_MMX}, + {I_PANDN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDF\110", IF_PENT|IF_MMX|IF_SM}, + {I_PANDN, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDF\110", IF_PENT|IF_MMX}, + {I_PAVEB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x50\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PAVEB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x50\110", IF_PENT|IF_MMX|IF_CYRIX}, + {I_PAVGUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW|IF_SM}, + {I_PAVGUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBF", IF_PENT|IF_3DNOW}, + {I_PCMPEQB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x74\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPEQB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x74\110", IF_PENT|IF_MMX}, + {I_PCMPEQD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x76\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPEQD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x76\110", IF_PENT|IF_MMX}, + {I_PCMPEQW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x75\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPEQW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x75\110", IF_PENT|IF_MMX}, + {I_PCMPGTB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x64\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPGTB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x64\110", IF_PENT|IF_MMX}, + {I_PCMPGTD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x66\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPGTD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x66\110", IF_PENT|IF_MMX}, + {I_PCMPGTW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x65\110", IF_PENT|IF_MMX|IF_SM}, + {I_PCMPGTW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x65\110", IF_PENT|IF_MMX}, + {I_PDISTIB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PF2ID, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW|IF_SM}, + {I_PF2ID, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1D", IF_PENT|IF_3DNOW}, + {I_PFACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAE", IF_PENT|IF_3DNOW}, + {I_PFADD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFADD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9E", IF_PENT|IF_3DNOW}, + {I_PFCMPEQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFCMPEQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB0", IF_PENT|IF_3DNOW}, + {I_PFCMPGE, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFCMPGE, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x90", IF_PENT|IF_3DNOW}, + {I_PFCMPGT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFCMPGT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA0", IF_PENT|IF_3DNOW}, + {I_PFMAX, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFMAX, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA4", IF_PENT|IF_3DNOW}, + {I_PFMIN, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFMIN, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x94", IF_PENT|IF_3DNOW}, + {I_PFMUL, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFMUL, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB4", IF_PENT|IF_3DNOW}, + {I_PFRCP, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRCP, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x96", IF_PENT|IF_3DNOW}, + {I_PFRCPIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRCPIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA6", IF_PENT|IF_3DNOW}, + {I_PFRCPIT2, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRCPIT2, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xB6", IF_PENT|IF_3DNOW}, + {I_PFRSQIT1, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRSQIT1, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xA7", IF_PENT|IF_3DNOW}, + {I_PFRSQRT, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFRSQRT, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x97", IF_PENT|IF_3DNOW}, + {I_PFSUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFSUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x9A", IF_PENT|IF_3DNOW}, + {I_PFSUBR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFSUBR, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xAA", IF_PENT|IF_3DNOW}, + {I_PI2FD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW|IF_SM}, + {I_PI2FD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0D", IF_PENT|IF_3DNOW}, + {I_PMACHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5E\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMADDWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF5\110", IF_PENT|IF_MMX|IF_SM}, + {I_PMADDWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF5\110", IF_PENT|IF_MMX}, + {I_PMAGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x52\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMAGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x52\110", IF_PENT|IF_MMX|IF_CYRIX}, + {I_PMULHRIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMULHRIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x5D\110", IF_PENT|IF_MMX|IF_CYRIX}, + {I_PMULHRWA, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW|IF_SM}, + {I_PMULHRWA, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\1\xB7", IF_PENT|IF_3DNOW}, + {I_PMULHRWC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMULHRWC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x59\110", IF_PENT|IF_MMX|IF_CYRIX}, + {I_PMULHW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE5\110", IF_PENT|IF_MMX|IF_SM}, + {I_PMULHW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE5\110", IF_PENT|IF_MMX}, + {I_PMULLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD5\110", IF_PENT|IF_MMX|IF_SM}, + {I_PMULLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD5\110", IF_PENT|IF_MMX}, + {I_PMVGEZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5C\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMVLZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMVNZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PMVZB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x58\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_POP, 1, {REG16,0,0}, "\320\10\x58", IF_8086}, + {I_POP, 1, {REG32,0,0}, "\321\10\x58", IF_386}, + {I_POP, 1, {REGMEM|BITS16,0,0}, "\320\300\1\x8F\200", IF_8086}, + {I_POP, 1, {REGMEM|BITS32,0,0}, "\321\300\1\x8F\200", IF_386}, + {I_POP, 1, {REG_DESS,0,0}, "\4", IF_8086}, + {I_POP, 1, {REG_FSGS,0,0}, "\1\x0F\5", IF_386}, + {I_POPA, 0, {0,0,0}, "\322\1\x61", IF_186}, + {I_POPAD, 0, {0,0,0}, "\321\1\x61", IF_386}, + {I_POPAW, 0, {0,0,0}, "\320\1\x61", IF_186}, + {I_POPF, 0, {0,0,0}, "\322\1\x9D", IF_8086}, + {I_POPFD, 0, {0,0,0}, "\321\1\x9D", IF_386}, + {I_POPFW, 0, {0,0,0}, "\320\1\x9D", IF_8086}, + {I_POR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEB\110", IF_PENT|IF_MMX|IF_SM}, + {I_POR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEB\110", IF_PENT|IF_MMX}, + {I_PREFETCH, 1, {MEMORY,0,0}, "\2\x0F\x0D\200", IF_PENT|IF_3DNOW|IF_SM}, + {I_PREFETCHW, 1, {MEMORY,0,0}, "\2\x0F\x0D\201", IF_PENT|IF_3DNOW|IF_SM}, + {I_PSLLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF2\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSLLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF2\110", IF_PENT|IF_MMX}, + {I_PSLLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\206\25", IF_PENT|IF_MMX}, + {I_PSLLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF3\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSLLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF3\110", IF_PENT|IF_MMX}, + {I_PSLLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\206\25", IF_PENT|IF_MMX}, + {I_PSLLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF1\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSLLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF1\110", IF_PENT|IF_MMX}, + {I_PSLLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\206\25", IF_PENT|IF_MMX}, + {I_PSRAD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE2\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRAD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE2\110", IF_PENT|IF_MMX}, + {I_PSRAD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\204\25", IF_PENT|IF_MMX}, + {I_PSRAW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE1\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRAW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE1\110", IF_PENT|IF_MMX}, + {I_PSRAW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\204\25", IF_PENT|IF_MMX}, + {I_PSRLD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD2\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRLD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD2\110", IF_PENT|IF_MMX}, + {I_PSRLD, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x72\202\25", IF_PENT|IF_MMX}, + {I_PSRLQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD3\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRLQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD3\110", IF_PENT|IF_MMX}, + {I_PSRLQ, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x73\202\25", IF_PENT|IF_MMX}, + {I_PSRLW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD1\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSRLW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD1\110", IF_PENT|IF_MMX}, + {I_PSRLW, 2, {MMXREG,IMMEDIATE,0}, "\2\x0F\x71\202\25", IF_PENT|IF_MMX}, + {I_PSUBB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF8\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF8\110", IF_PENT|IF_MMX}, + {I_PSUBD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFA\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBD, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFA\110", IF_PENT|IF_MMX}, + {I_PSUBSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE8\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE8\110", IF_PENT|IF_MMX}, + {I_PSUBSIW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_PENT|IF_MMX|IF_SM|IF_CYRIX}, + {I_PSUBSIW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x55\110", IF_PENT|IF_MMX|IF_CYRIX}, + {I_PSUBSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE9\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE9\110", IF_PENT|IF_MMX}, + {I_PSUBUSB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD8\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBUSB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD8\110", IF_PENT|IF_MMX}, + {I_PSUBUSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD9\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBUSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD9\110", IF_PENT|IF_MMX}, + {I_PSUBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF9\110", IF_PENT|IF_MMX|IF_SM}, + {I_PSUBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF9\110", IF_PENT|IF_MMX}, + {I_PUNPCKHBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x68\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKHBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x68\110", IF_PENT|IF_MMX}, + {I_PUNPCKHDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x6A\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKHDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x6A\110", IF_PENT|IF_MMX}, + {I_PUNPCKHWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x69\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKHWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x69\110", IF_PENT|IF_MMX}, + {I_PUNPCKLBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x60\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKLBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x60\110", IF_PENT|IF_MMX}, + {I_PUNPCKLDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x62\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKLDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\x62\110", IF_PENT|IF_MMX}, + {I_PUNPCKLWD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x61\110", IF_PENT|IF_MMX|IF_SM}, + {I_PUNPCKLWD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x61\110", IF_PENT|IF_MMX}, + {I_PUSH, 1, {REG16,0,0}, "\320\10\x50", IF_8086}, + {I_PUSH, 1, {REG32,0,0}, "\321\10\x50", IF_386}, + {I_PUSH, 1, {REGMEM|BITS16,0,0}, "\320\300\1\xFF\206", IF_8086}, + {I_PUSH, 1, {REGMEM|BITS32,0,0}, "\321\300\1\xFF\206", IF_386}, + {I_PUSH, 1, {REG_CS,0,0}, "\6", IF_8086}, + {I_PUSH, 1, {REG_DESS,0,0}, "\6", IF_8086}, + {I_PUSH, 1, {REG_FSGS,0,0}, "\1\x0F\7", IF_386}, + {I_PUSH, 1, {IMMEDIATE|BITS8,0,0}, "\1\x6A\14", IF_186}, + {I_PUSH, 1, {IMMEDIATE|BITS16,0,0}, "\320\133\1\x68\130", IF_186}, + {I_PUSH, 1, {IMMEDIATE|BITS32,0,0}, "\321\143\1\x68\140", IF_386}, + {I_PUSH, 1, {IMMEDIATE,0,0}, "\1\x68\34", IF_186}, + {I_PUSHA, 0, {0,0,0}, "\322\1\x60", IF_186}, + {I_PUSHAD, 0, {0,0,0}, "\321\1\x60", IF_386}, + {I_PUSHAW, 0, {0,0,0}, "\320\1\x60", IF_186}, + {I_PUSHF, 0, {0,0,0}, "\322\1\x9C", IF_8086}, + {I_PUSHFD, 0, {0,0,0}, "\321\1\x9C", IF_386}, + {I_PUSHFW, 0, {0,0,0}, "\320\1\x9C", IF_8086}, + {I_PXOR, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEF\110", IF_PENT|IF_MMX|IF_SM}, + {I_PXOR, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEF\110", IF_PENT|IF_MMX}, + {I_RCL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\202\25", IF_186|IF_SB}, + {I_RCL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\202", IF_8086}, + {I_RCL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\202\25", IF_186|IF_SB}, + {I_RCL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\202", IF_386}, + {I_RCL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\202", IF_386}, + {I_RCL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\202\25", IF_386|IF_SB}, + {I_RCR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\203\25", IF_186|IF_SB}, + {I_RCR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\203", IF_8086}, + {I_RCR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\203\25", IF_186|IF_SB}, + {I_RCR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\203", IF_386}, + {I_RCR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\203", IF_386}, + {I_RCR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\203\25", IF_386|IF_SB}, + {I_RDSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x36\200", IF_P6|IF_CYRIX|IF_SMM}, + {I_RDMSR, 0, {0,0,0}, "\2\x0F\x32", IF_PENT|IF_PRIV}, + {I_RDPMC, 0, {0,0,0}, "\2\x0F\x33", IF_P6}, + {I_RDTSC, 0, {0,0,0}, "\2\x0F\x31", IF_PENT}, + {I_RESB, 1, {IMMEDIATE,0,0}, "\340", IF_8086}, + {I_RET, 0, {0,0,0}, "\1\xC3", IF_8086}, + {I_RET, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, + {I_RETF, 0, {0,0,0}, "\1\xCB", IF_8086}, + {I_RETF, 1, {IMMEDIATE,0,0}, "\1\xCA\30", IF_8086|IF_SW}, + {I_RETN, 0, {0,0,0}, "\1\xC3", IF_8086}, + {I_RETN, 1, {IMMEDIATE,0,0}, "\1\xC2\30", IF_8086|IF_SW}, + {I_ROL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\200\25", IF_186|IF_SB}, + {I_ROL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\200", IF_8086}, + {I_ROL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\200\25", IF_186|IF_SB}, + {I_ROL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\200", IF_386}, + {I_ROL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\200", IF_386}, + {I_ROL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\200\25", IF_386|IF_SB}, + {I_ROR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\201\25", IF_186|IF_SB}, + {I_ROR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\201", IF_8086}, + {I_ROR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\201\25", IF_186|IF_SB}, + {I_ROR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\201", IF_386}, + {I_ROR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\201", IF_386}, + {I_ROR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\201\25", IF_386|IF_SB}, + {I_RSDC, 2, {REG_SREG,MEMORY|BITS80,0}, "\301\2\x0F\x79\110", IF_486|IF_CYRIX|IF_SMM}, + {I_RSLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7B\200", IF_486|IF_CYRIX|IF_SMM}, + {I_RSM, 0, {0,0,0}, "\2\x0F\xAA", IF_PENT|IF_SMM}, + {I_RSTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7D\200", IF_486|IF_CYRIX|IF_SMM}, + {I_SAHF, 0, {0,0,0}, "\1\x9E", IF_8086}, + {I_SALC, 0, {0,0,0}, "\1\xD6", IF_8086|IF_UNDOC}, + {I_SAR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\207\25", IF_186|IF_SB}, + {I_SAR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\207", IF_8086}, + {I_SAR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\207\25", IF_186|IF_SB}, + {I_SAR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\207", IF_386}, + {I_SAR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\207", IF_386}, + {I_SAR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\207\25", IF_386|IF_SB}, + {I_SBB, 2, {MEMORY,REG8,0}, "\300\1\x18\101", IF_8086|IF_SM}, + {I_SBB, 2, {REG8,REG8,0}, "\1\x18\101", IF_8086}, + {I_SBB, 2, {MEMORY,REG16,0}, "\320\300\1\x19\101", IF_8086|IF_SM}, + {I_SBB, 2, {REG16,REG16,0}, "\320\1\x19\101", IF_8086}, + {I_SBB, 2, {MEMORY,REG32,0}, "\321\300\1\x19\101", IF_386|IF_SM}, + {I_SBB, 2, {REG32,REG32,0}, "\321\1\x19\101", IF_386}, + {I_SBB, 2, {REG8,MEMORY,0}, "\301\1\x1A\110", IF_8086|IF_SM}, + {I_SBB, 2, {REG8,REG8,0}, "\1\x1A\110", IF_8086}, + {I_SBB, 2, {REG16,MEMORY,0}, "\320\301\1\x1B\110", IF_8086|IF_SM}, + {I_SBB, 2, {REG16,REG16,0}, "\320\1\x1B\110", IF_8086}, + {I_SBB, 2, {REG32,MEMORY,0}, "\321\301\1\x1B\110", IF_386|IF_SM}, + {I_SBB, 2, {REG32,REG32,0}, "\321\1\x1B\110", IF_386}, + {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\203\15", IF_8086}, + {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\203\15", IF_386}, + {I_SBB, 2, {REG_AL,IMMEDIATE,0}, "\1\x1C\21", IF_8086|IF_SM}, + {I_SBB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x1D\31", IF_8086|IF_SM}, + {I_SBB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x1D\41", IF_386|IF_SM}, + {I_SBB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, + {I_SBB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, + {I_SBB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, + {I_SBB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\203\21", IF_8086|IF_SM}, + {I_SBB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\203\131", IF_8086|IF_SM}, + {I_SBB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\203\141", IF_386|IF_SM}, + {I_SCASB, 0, {0,0,0}, "\332\1\xAE", IF_8086}, + {I_SCASD, 0, {0,0,0}, "\332\321\1\xAF", IF_386}, + {I_SCASW, 0, {0,0,0}, "\332\320\1\xAF", IF_8086}, + {I_SGDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\200", IF_286}, + {I_SHL, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\204\25", IF_186|IF_SB}, + {I_SHL, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\204", IF_8086}, + {I_SHL, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\204\25", IF_186|IF_SB}, + {I_SHL, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\204", IF_386}, + {I_SHL, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\204", IF_386}, + {I_SHL, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\204\25", IF_386|IF_SB}, + {I_SHLD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xA4\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHLD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xA5\101", IF_386|IF_SM}, + {I_SHLD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xA5\101", IF_386}, + {I_SHLD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xA5\101", IF_386|IF_SM}, + {I_SHLD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xA5\101", IF_386}, + {I_SHR, 2, {REGMEM|BITS8,UNITY,0}, "\300\1\xD0\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS8,REG_CL,0}, "\300\1\xD2\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xC0\205\25", IF_186|IF_SB}, + {I_SHR, 2, {REGMEM|BITS16,UNITY,0}, "\320\300\1\xD1\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS16,REG_CL,0}, "\320\300\1\xD3\205", IF_8086}, + {I_SHR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xC1\205\25", IF_186|IF_SB}, + {I_SHR, 2, {REGMEM|BITS32,UNITY,0}, "\321\300\1\xD1\205", IF_386}, + {I_SHR, 2, {REGMEM|BITS32,REG_CL,0}, "\321\300\1\xD3\205", IF_386}, + {I_SHR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xC1\205\25", IF_386|IF_SB}, + {I_SHRD, 3, {MEMORY,REG16,IMMEDIATE}, "\300\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {REG16,REG16,IMMEDIATE}, "\320\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {MEMORY,REG32,IMMEDIATE}, "\300\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {REG32,REG32,IMMEDIATE}, "\321\2\x0F\xAC\101\26", IF_386|IF_SM2|IF_SB|IF_AR2}, + {I_SHRD, 3, {MEMORY,REG16,REG_CL}, "\300\320\2\x0F\xAD\101", IF_386|IF_SM}, + {I_SHRD, 3, {REG16,REG16,REG_CL}, "\320\2\x0F\xAD\101", IF_386}, + {I_SHRD, 3, {MEMORY,REG32,REG_CL}, "\300\321\2\x0F\xAD\101", IF_386|IF_SM}, + {I_SHRD, 3, {REG32,REG32,REG_CL}, "\321\2\x0F\xAD\101", IF_386}, + {I_SIDT, 1, {MEMORY,0,0}, "\300\2\x0F\x01\201", IF_286}, + {I_SLDT, 1, {MEMORY,0,0}, "\300\1\x0F\17\200", IF_286}, + {I_SLDT, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\200", IF_286}, + {I_SLDT, 1, {REG16,0,0}, "\320\1\x0F\17\200", IF_286}, + {I_SLDT, 1, {REG32,0,0}, "\321\1\x0F\17\200", IF_386}, + {I_SMI, 0, {0,0,0}, "\1\xF1", IF_386|IF_UNDOC}, + {I_SMINT, 0, {0,0,0}, "\2\x0F\x38", IF_P6|IF_CYRIX}, + {I_SMSW, 1, {MEMORY,0,0}, "\300\2\x0F\x01\204", IF_286}, + {I_SMSW, 1, {MEMORY|BITS16,0,0}, "\300\2\x0F\x01\204", IF_286}, + {I_SMSW, 1, {REG16,0,0}, "\320\2\x0F\x01\204", IF_286}, + {I_SMSW, 1, {REG32,0,0}, "\321\2\x0F\x01\204", IF_386}, + {I_STC, 0, {0,0,0}, "\1\xF9", IF_8086}, + {I_STD, 0, {0,0,0}, "\1\xFD", IF_8086}, + {I_STI, 0, {0,0,0}, "\1\xFB", IF_8086}, + {I_STOSB, 0, {0,0,0}, "\1\xAA", IF_8086}, + {I_STOSD, 0, {0,0,0}, "\321\1\xAB", IF_386}, + {I_STOSW, 0, {0,0,0}, "\320\1\xAB", IF_8086}, + {I_STR, 1, {MEMORY,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, + {I_STR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\201", IF_286|IF_PROT}, + {I_STR, 1, {REG16,0,0}, "\320\1\x0F\17\201", IF_286|IF_PROT}, + {I_STR, 1, {REG32,0,0}, "\321\1\x0F\17\201", IF_386|IF_PROT}, + {I_SUB, 2, {MEMORY,REG8,0}, "\300\1\x28\101", IF_8086|IF_SM}, + {I_SUB, 2, {REG8,REG8,0}, "\1\x28\101", IF_8086}, + {I_SUB, 2, {MEMORY,REG16,0}, "\320\300\1\x29\101", IF_8086|IF_SM}, + {I_SUB, 2, {REG16,REG16,0}, "\320\1\x29\101", IF_8086}, + {I_SUB, 2, {MEMORY,REG32,0}, "\321\300\1\x29\101", IF_386|IF_SM}, + {I_SUB, 2, {REG32,REG32,0}, "\321\1\x29\101", IF_386}, + {I_SUB, 2, {REG8,MEMORY,0}, "\301\1\x2A\110", IF_8086|IF_SM}, + {I_SUB, 2, {REG8,REG8,0}, "\1\x2A\110", IF_8086}, + {I_SUB, 2, {REG16,MEMORY,0}, "\320\301\1\x2B\110", IF_8086|IF_SM}, + {I_SUB, 2, {REG16,REG16,0}, "\320\1\x2B\110", IF_8086}, + {I_SUB, 2, {REG32,MEMORY,0}, "\321\301\1\x2B\110", IF_386|IF_SM}, + {I_SUB, 2, {REG32,REG32,0}, "\321\1\x2B\110", IF_386}, + {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\205\15", IF_8086}, + {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\205\15", IF_386}, + {I_SUB, 2, {REG_AL,IMMEDIATE,0}, "\1\x2C\21", IF_8086|IF_SM}, + {I_SUB, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x2D\31", IF_8086|IF_SM}, + {I_SUB, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x2D\41", IF_386|IF_SM}, + {I_SUB, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, + {I_SUB, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, + {I_SUB, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, + {I_SUB, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\205\21", IF_8086|IF_SM}, + {I_SUB, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\205\131", IF_8086|IF_SM}, + {I_SUB, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\205\141", IF_386|IF_SM}, + {I_SVDC, 2, {MEMORY|BITS80,REG_SREG,0}, "\300\2\x0F\x78\101", IF_486|IF_CYRIX|IF_SMM}, + {I_SVLDT, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7A\200", IF_486|IF_CYRIX|IF_SMM}, + {I_SVTS, 1, {MEMORY|BITS80,0,0}, "\300\2\x0F\x7C\200", IF_486|IF_CYRIX|IF_SMM}, + {I_SYSCALL, 0, {0,0,0}, "\2\x0F\x05", IF_P6|IF_AMD}, + {I_SYSENTER, 0, {0,0,0}, "\2\x0F\x34", IF_P6}, + {I_SYSEXIT, 0, {0,0,0}, "\2\x0F\x35", IF_P6|IF_PRIV}, + {I_SYSRET, 0, {0,0,0}, "\2\x0F\x07", IF_P6|IF_PRIV|IF_AMD}, + {I_TEST, 2, {MEMORY,REG8,0}, "\300\1\x84\101", IF_8086|IF_SM}, + {I_TEST, 2, {REG8,REG8,0}, "\1\x84\101", IF_8086}, + {I_TEST, 2, {MEMORY,REG16,0}, "\320\300\1\x85\101", IF_8086|IF_SM}, + {I_TEST, 2, {REG16,REG16,0}, "\320\1\x85\101", IF_8086}, + {I_TEST, 2, {MEMORY,REG32,0}, "\321\300\1\x85\101", IF_386|IF_SM}, + {I_TEST, 2, {REG32,REG32,0}, "\321\1\x85\101", IF_386}, + {I_TEST, 2, {REG8,MEMORY,0}, "\301\1\x84\110", IF_8086|IF_SM}, + {I_TEST, 2, {REG16,MEMORY,0}, "\320\301\1\x85\110", IF_8086|IF_SM}, + {I_TEST, 2, {REG32,MEMORY,0}, "\321\301\1\x85\110", IF_386|IF_SM}, + {I_TEST, 2, {REG_AL,IMMEDIATE,0}, "\1\xA8\21", IF_8086|IF_SM}, + {I_TEST, 2, {REG_AX,IMMEDIATE,0}, "\320\1\xA9\31", IF_8086|IF_SM}, + {I_TEST, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\xA9\41", IF_386|IF_SM}, + {I_TEST, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, + {I_TEST, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, + {I_TEST, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, + {I_TEST, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\xF6\200\21", IF_8086|IF_SM}, + {I_TEST, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\1\xF7\200\31", IF_8086|IF_SM}, + {I_TEST, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\1\xF7\200\41", IF_386|IF_SM}, + {I_UD0, 0, {0,0,0}, "\2\x0F\xFF", IF_286|IF_UNDOC}, + {I_UD1, 0, {0,0,0}, "\2\x0F\xB9", IF_286|IF_UNDOC}, + {I_UD2, 0, {0,0,0}, "\2\x0F\x0B", IF_286}, + {I_UMOV, 2, {MEMORY,REG8,0}, "\300\2\x0F\x10\101", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x10\101", IF_386|IF_UNDOC}, + {I_UMOV, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x11\101", IF_386|IF_UNDOC}, + {I_UMOV, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\x11\101", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x11\101", IF_386|IF_UNDOC}, + {I_UMOV, 2, {REG8,MEMORY,0}, "\301\2\x0F\x12\110", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG8,REG8,0}, "\2\x0F\x12\110", IF_386|IF_UNDOC}, + {I_UMOV, 2, {REG16,MEMORY,0}, "\320\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG16,REG16,0}, "\320\2\x0F\x13\110", IF_386|IF_UNDOC}, + {I_UMOV, 2, {REG32,MEMORY,0}, "\321\301\2\x0F\x13\110", IF_386|IF_UNDOC|IF_SM}, + {I_UMOV, 2, {REG32,REG32,0}, "\321\2\x0F\x13\110", IF_386|IF_UNDOC}, + {I_VERR, 1, {MEMORY,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, + {I_VERR, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\204", IF_286|IF_PROT}, + {I_VERR, 1, {REG16,0,0}, "\1\x0F\17\204", IF_286|IF_PROT}, + {I_VERW, 1, {MEMORY,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, + {I_VERW, 1, {MEMORY|BITS16,0,0}, "\300\1\x0F\17\205", IF_286|IF_PROT}, + {I_VERW, 1, {REG16,0,0}, "\1\x0F\17\205", IF_286|IF_PROT}, + {I_WAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, + {I_FWAIT, 0, {0,0,0}, "\1\x9B", IF_8086}, + {I_WBINVD, 0, {0,0,0}, "\2\x0F\x09", IF_486|IF_PRIV}, + {I_WRSHR, 1, {REGMEM|BITS32,0,0}, "\321\300\2\x0F\x37\200", IF_P6|IF_CYRIX|IF_SMM}, + {I_WRMSR, 0, {0,0,0}, "\2\x0F\x30", IF_PENT|IF_PRIV}, + {I_XADD, 2, {MEMORY,REG8,0}, "\300\2\x0F\xC0\101", IF_486|IF_SM}, + {I_XADD, 2, {REG8,REG8,0}, "\2\x0F\xC0\101", IF_486}, + {I_XADD, 2, {MEMORY,REG16,0}, "\320\300\2\x0F\xC1\101", IF_486|IF_SM}, + {I_XADD, 2, {REG16,REG16,0}, "\320\2\x0F\xC1\101", IF_486}, + {I_XADD, 2, {MEMORY,REG32,0}, "\321\300\2\x0F\xC1\101", IF_486|IF_SM}, + {I_XADD, 2, {REG32,REG32,0}, "\321\2\x0F\xC1\101", IF_486}, + {I_XCHG, 2, {REG_AX,REG16,0}, "\320\11\x90", IF_8086}, + {I_XCHG, 2, {REG_EAX,REG32,0}, "\321\11\x90", IF_386}, + {I_XCHG, 2, {REG16,REG_AX,0}, "\320\10\x90", IF_8086}, + {I_XCHG, 2, {REG32,REG_EAX,0}, "\321\10\x90", IF_386}, + {I_XCHG, 2, {REG8,MEMORY,0}, "\301\1\x86\110", IF_8086|IF_SM}, + {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\110", IF_8086}, + {I_XCHG, 2, {REG16,MEMORY,0}, "\320\301\1\x87\110", IF_8086|IF_SM}, + {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\110", IF_8086}, + {I_XCHG, 2, {REG32,MEMORY,0}, "\321\301\1\x87\110", IF_386|IF_SM}, + {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\110", IF_386}, + {I_XCHG, 2, {MEMORY,REG8,0}, "\300\1\x86\101", IF_8086|IF_SM}, + {I_XCHG, 2, {REG8,REG8,0}, "\1\x86\101", IF_8086}, + {I_XCHG, 2, {MEMORY,REG16,0}, "\320\300\1\x87\101", IF_8086|IF_SM}, + {I_XCHG, 2, {REG16,REG16,0}, "\320\1\x87\101", IF_8086}, + {I_XCHG, 2, {MEMORY,REG32,0}, "\321\300\1\x87\101", IF_386|IF_SM}, + {I_XCHG, 2, {REG32,REG32,0}, "\321\1\x87\101", IF_386}, + {I_XLATB, 0, {0,0,0}, "\1\xD7", IF_8086}, + {I_XLAT, 0, {0,0,0}, "\1\xD7", IF_8086}, + {I_XOR, 2, {MEMORY,REG8,0}, "\300\1\x30\101", IF_8086|IF_SM}, + {I_XOR, 2, {REG8,REG8,0}, "\1\x30\101", IF_8086}, + {I_XOR, 2, {MEMORY,REG16,0}, "\320\300\1\x31\101", IF_8086|IF_SM}, + {I_XOR, 2, {REG16,REG16,0}, "\320\1\x31\101", IF_8086}, + {I_XOR, 2, {MEMORY,REG32,0}, "\321\300\1\x31\101", IF_386|IF_SM}, + {I_XOR, 2, {REG32,REG32,0}, "\321\1\x31\101", IF_386}, + {I_XOR, 2, {REG8,MEMORY,0}, "\301\1\x32\110", IF_8086|IF_SM}, + {I_XOR, 2, {REG8,REG8,0}, "\1\x32\110", IF_8086}, + {I_XOR, 2, {REG16,MEMORY,0}, "\320\301\1\x33\110", IF_8086|IF_SM}, + {I_XOR, 2, {REG16,REG16,0}, "\320\1\x33\110", IF_8086}, + {I_XOR, 2, {REG32,MEMORY,0}, "\321\301\1\x33\110", IF_386|IF_SM}, + {I_XOR, 2, {REG32,REG32,0}, "\321\1\x33\110", IF_386}, + {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE|BITS8,0}, "\320\300\1\x83\206\15", IF_8086}, + {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE|BITS8,0}, "\321\300\1\x83\206\15", IF_386}, + {I_XOR, 2, {REG_AL,IMMEDIATE,0}, "\1\x34\21", IF_8086|IF_SM}, + {I_XOR, 2, {REG_AX,IMMEDIATE,0}, "\320\1\x35\31", IF_8086|IF_SM}, + {I_XOR, 2, {REG_EAX,IMMEDIATE,0}, "\321\1\x35\41", IF_386|IF_SM}, + {I_XOR, 2, {REGMEM|BITS8,IMMEDIATE,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, + {I_XOR, 2, {REGMEM|BITS16,IMMEDIATE,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, + {I_XOR, 2, {REGMEM|BITS32,IMMEDIATE,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, + {I_XOR, 2, {MEMORY,IMMEDIATE|BITS8,0}, "\300\1\x80\206\21", IF_8086|IF_SM}, + {I_XOR, 2, {MEMORY,IMMEDIATE|BITS16,0}, "\320\300\134\1\x81\206\131", IF_8086|IF_SM}, + {I_XOR, 2, {MEMORY,IMMEDIATE|BITS32,0}, "\321\300\144\1\x81\206\141", IF_386|IF_SM}, + {I_XSTORE, 0, {0,0,0}, "\3\x0F\xA7\xC0", IF_P6|IF_CYRIX}, + {I_CMOVcc, 2, {REG16,MEMORY,0}, "\320\301\1\x0F\330\x40\110", IF_P6|IF_SM}, + {I_CMOVcc, 2, {REG16,REG16,0}, "\320\1\x0F\330\x40\110", IF_P6}, + {I_CMOVcc, 2, {REG32,MEMORY,0}, "\321\301\1\x0F\330\x40\110", IF_P6|IF_SM}, + {I_CMOVcc, 2, {REG32,REG32,0}, "\321\1\x0F\330\x40\110", IF_P6}, + {I_Jcc, 1, {IMMEDIATE|NEAR,0,0}, "\322\1\x0F\330\x80\64", IF_386}, + {I_Jcc, 1, {IMMEDIATE|BITS16|NEAR,0,0}, "\320\1\x0F\330\x80\64", IF_386}, + {I_Jcc, 1, {IMMEDIATE|BITS32|NEAR,0,0}, "\321\1\x0F\330\x80\64", IF_386}, + {I_Jcc, 1, {IMMEDIATE,0,0}, "\330\x70\50", IF_8086}, + {I_SETcc, 1, {MEMORY,0,0}, "\300\1\x0F\330\x90\200", IF_386|IF_SB}, + {I_SETcc, 1, {REG8,0,0}, "\300\1\x0F\330\x90\200", IF_386}, + {I_ADDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + {I_ADDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + {I_ADDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + {I_ADDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x58\110", IF_KATMAI|IF_SSE}, + {I_ANDNPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x55\110", IF_KATMAI|IF_SSE}, + {I_ANDNPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x55\110", IF_KATMAI|IF_SSE}, + {I_ANDPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x54\110", IF_KATMAI|IF_SSE}, + {I_ANDPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x54\110", IF_KATMAI|IF_SSE}, + {I_CMPEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + {I_CMPEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + {I_CMPEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + {I_CMPEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x00", IF_KATMAI|IF_SSE}, + {I_CMPLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + {I_CMPLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + {I_CMPLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + {I_CMPLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x02", IF_KATMAI|IF_SSE}, + {I_CMPLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + {I_CMPLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + {I_CMPLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + {I_CMPLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x01", IF_KATMAI|IF_SSE}, + {I_CMPNEQPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + {I_CMPNEQPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + {I_CMPNEQSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + {I_CMPNEQSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x04", IF_KATMAI|IF_SSE}, + {I_CMPNLEPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + {I_CMPNLEPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + {I_CMPNLESS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + {I_CMPNLESS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x06", IF_KATMAI|IF_SSE}, + {I_CMPNLTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + {I_CMPNLTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + {I_CMPNLTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + {I_CMPNLTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x05", IF_KATMAI|IF_SSE}, + {I_CMPORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + {I_CMPORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + {I_CMPORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + {I_CMPORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x07", IF_KATMAI|IF_SSE}, + {I_CMPUNORDPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + {I_CMPUNORDPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + {I_CMPUNORDSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + {I_CMPUNORDSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xC2\110\1\x03", IF_KATMAI|IF_SSE}, + {I_CMPPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_CMPPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_CMPSS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_CMPSS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\xC2\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_COMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, + {I_COMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2F\110", IF_KATMAI|IF_SSE}, + {I_CVTPI2PS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTPI2PS, 2, {XMMREG,MMXREG,0}, "\331\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2D\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTSI2SS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE|IF_SD|IF_AR1}, + {I_CVTSI2SS, 2, {XMMREG,REG32,0}, "\333\2\x0F\x2A\110", IF_KATMAI|IF_SSE}, + {I_CVTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, + {I_CVTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2D\110", IF_KATMAI|IF_SSE}, + {I_CVTTPS2PI, 2, {MMXREG,MEMORY,0}, "\301\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTTPS2PI, 2, {MMXREG,XMMREG,0}, "\331\2\x0F\x2C\110", IF_KATMAI|IF_SSE|IF_MMX}, + {I_CVTTSS2SI, 2, {REG32,MEMORY,0}, "\301\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, + {I_CVTTSS2SI, 2, {REG32,XMMREG,0}, "\333\2\x0F\x2C\110", IF_KATMAI|IF_SSE}, + {I_DIVPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + {I_DIVPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + {I_DIVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + {I_DIVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5E\110", IF_KATMAI|IF_SSE}, + {I_LDMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\202", IF_KATMAI|IF_SSE|IF_SD}, + {I_MAXPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + {I_MAXPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + {I_MAXSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + {I_MAXSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5F\110", IF_KATMAI|IF_SSE}, + {I_MINPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + {I_MINPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + {I_MINSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + {I_MINSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5D\110", IF_KATMAI|IF_SSE}, + {I_MOVAPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x28\110", IF_KATMAI|IF_SSE}, + {I_MOVAPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x29\101", IF_KATMAI|IF_SSE}, + {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x28\110", IF_KATMAI|IF_SSE}, + {I_MOVAPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x29\101", IF_KATMAI|IF_SSE}, + {I_MOVHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x16\110", IF_KATMAI|IF_SSE}, + {I_MOVHPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x17\101", IF_KATMAI|IF_SSE}, + {I_MOVLHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x16\110", IF_KATMAI|IF_SSE}, + {I_MOVLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x12\110", IF_KATMAI|IF_SSE}, + {I_MOVLPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x13\101", IF_KATMAI|IF_SSE}, + {I_MOVHLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x12\110", IF_KATMAI|IF_SSE}, + {I_MOVMSKPS, 2, {REG32,XMMREG,0}, "\2\x0F\x50\110", IF_KATMAI|IF_SSE}, + {I_MOVNTPS, 2, {MEMORY,XMMREG,0}, "\300\2\x0F\x2B\101", IF_KATMAI|IF_SSE}, + {I_MOVSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVSS, 2, {MEMORY,XMMREG,0}, "\300\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + {I_MOVUPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVUPS, 2, {MEMORY,XMMREG,0}, "\300\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x10\110", IF_KATMAI|IF_SSE}, + {I_MOVUPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x11\101", IF_KATMAI|IF_SSE}, + {I_MULPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + {I_MULPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + {I_MULSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + {I_MULSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x59\110", IF_KATMAI|IF_SSE}, + {I_ORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x56\110", IF_KATMAI|IF_SSE}, + {I_ORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x56\110", IF_KATMAI|IF_SSE}, + {I_RCPPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + {I_RCPPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + {I_RCPSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + {I_RCPSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x53\110", IF_KATMAI|IF_SSE}, + {I_RSQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + {I_RSQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + {I_RSQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + {I_RSQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x52\110", IF_KATMAI|IF_SSE}, + {I_SHUFPS, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_SHUFPS, 3, {XMMREG,XMMREG,IMMEDIATE}, "\2\x0F\xC6\110\26", IF_KATMAI|IF_SSE|IF_SB|IF_AR2}, + {I_SQRTPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + {I_SQRTPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + {I_SQRTSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + {I_SQRTSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x51\110", IF_KATMAI|IF_SSE}, + {I_STMXCSR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\203", IF_KATMAI|IF_SSE|IF_SD}, + {I_SUBPS, 2, {XMMREG,MEMORY,0}, "\301\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + {I_SUBPS, 2, {XMMREG,XMMREG,0}, "\331\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + {I_SUBSS, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + {I_SUBSS, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5C\110", IF_KATMAI|IF_SSE}, + {I_UCOMISS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, + {I_UCOMISS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x2E\110", IF_KATMAI|IF_SSE}, + {I_UNPCKHPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x15\110", IF_KATMAI|IF_SSE}, + {I_UNPCKHPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x15\110", IF_KATMAI|IF_SSE}, + {I_UNPCKLPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x14\110", IF_KATMAI|IF_SSE}, + {I_UNPCKLPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x14\110", IF_KATMAI|IF_SSE}, + {I_XORPS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x57\110", IF_KATMAI|IF_SSE}, + {I_XORPS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x57\110", IF_KATMAI|IF_SSE}, + {I_FXRSTOR, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\201", IF_P6|IF_SSE|IF_FPU}, + {I_FXSAVE, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\200", IF_P6|IF_SSE|IF_FPU}, + {I_PREFETCHNTA, 1, {MEMORY,0,0}, "\300\2\x0F\x18\200", IF_KATMAI}, + {I_PREFETCHT0, 1, {MEMORY,0,0}, "\300\2\x0F\x18\201", IF_KATMAI}, + {I_PREFETCHT1, 1, {MEMORY,0,0}, "\300\2\x0F\x18\202", IF_KATMAI}, + {I_PREFETCHT2, 1, {MEMORY,0,0}, "\300\2\x0F\x18\203", IF_KATMAI}, + {I_SFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF8", IF_KATMAI}, + {I_MASKMOVQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF7\110", IF_KATMAI|IF_MMX}, + {I_MOVNTQ, 2, {MEMORY,MMXREG,0}, "\300\2\x0F\xE7\101", IF_KATMAI|IF_MMX|IF_SM}, + {I_PAVGB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE0\110", IF_KATMAI|IF_MMX}, + {I_PAVGB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE0\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PAVGW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE3\110", IF_KATMAI|IF_MMX}, + {I_PAVGW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE3\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PEXTRW, 3, {REG32,MMXREG,IMMEDIATE}, "\2\x0F\xC5\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PINSRW, 3, {MMXREG,REG16,IMMEDIATE}, "\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PINSRW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\xC4\110\26", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PMAXSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEE\110", IF_KATMAI|IF_MMX}, + {I_PMAXSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEE\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMAXUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDE\110", IF_KATMAI|IF_MMX}, + {I_PMAXUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDE\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMINSW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xEA\110", IF_KATMAI|IF_MMX}, + {I_PMINSW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xEA\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMINUB, 2, {MMXREG,MMXREG,0}, "\2\x0F\xDA\110", IF_KATMAI|IF_MMX}, + {I_PMINUB, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xDA\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PMOVMSKB, 2, {REG32,MMXREG,0}, "\2\x0F\xD7\110", IF_KATMAI|IF_MMX}, + {I_PMULHUW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xE4\110", IF_KATMAI|IF_MMX}, + {I_PMULHUW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xE4\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PSADBW, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF6\110", IF_KATMAI|IF_MMX}, + {I_PSADBW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF6\110", IF_KATMAI|IF_MMX|IF_SM}, + {I_PSHUFW, 3, {MMXREG,MMXREG,IMMEDIATE}, "\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SB|IF_AR2}, + {I_PSHUFW, 3, {MMXREG,MEMORY,IMMEDIATE}, "\301\2\x0F\x70\110\22", IF_KATMAI|IF_MMX|IF_SM2|IF_SB|IF_AR2}, + {I_PF2IW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW|IF_SM}, + {I_PF2IW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x1C", IF_PENT|IF_3DNOW}, + {I_PFNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8A", IF_PENT|IF_3DNOW}, + {I_PFPNACC, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW|IF_SM}, + {I_PFPNACC, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x8E", IF_PENT|IF_3DNOW}, + {I_PI2FW, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW|IF_SM}, + {I_PI2FW, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\x0C", IF_PENT|IF_3DNOW}, + {I_PSWAPD, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW|IF_SM}, + {I_PSWAPD, 2, {MMXREG,MMXREG,0}, "\2\x0F\x0F\110\01\xBB", IF_PENT|IF_3DNOW}, + {I_MASKMOVDQU, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF7\110", IF_WILLAMETTE|IF_SSE2}, + {I_CLFLUSH, 1, {MEMORY,0,0}, "\300\2\x0F\xAE\207", IF_WILLAMETTE|IF_SSE2}, + {I_MOVNTDQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xE7\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVNTI, 2, {MEMORY,REG32,0}, "\300\2\x0F\xC3\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVNTPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x2B\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PAUSE, 0, {0,0,0}, "\333\1\x90", IF_WILLAMETTE|IF_SSE2}, + {I_LFENCE, 0, {0,0,0}, "\3\x0F\xAE\xE8", IF_WILLAMETTE|IF_SSE2}, + {I_MFENCE, 0, {0,0,0}, "\3\x0F\xAE\xF0", IF_WILLAMETTE|IF_SSE2}, + {I_MOVD, 2, {XMMREG,REG32,0}, "\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7E\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6E\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVDQA, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQA, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQA, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVDQU, 2, {MEMORY,XMMREG,0}, "\333\300\2\x0F\x7F\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQU, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x6F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVDQU, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVDQ2Q, 2, {MMXREG,XMMREG,0}, "\3\xF2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\xD6\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x7E\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVQ2DQ, 2, {XMMREG,MMXREG,0}, "\333\2\x0F\xD6\110", IF_WILLAMETTE|IF_SSE2}, + {I_PACKSSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2}, + {I_PACKSSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x63\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PACKSSDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2}, + {I_PACKSSDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PACKUSWB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2}, + {I_PACKUSWB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x67\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEC\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xED\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDC\110", IF_WILLAMETTE|IF_SSE2}, + {I_PADDUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PADDUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDD\110", IF_WILLAMETTE|IF_SSE2}, + {I_PAND, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2}, + {I_PAND, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PANDN, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2}, + {I_PANDN, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PAVGB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2}, + {I_PAVGB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE0\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PAVGW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2}, + {I_PAVGW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PCMPEQB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPEQB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x74\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PCMPEQW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPEQW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x75\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PCMPEQD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPEQD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x76\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PCMPGTB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPGTB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x64\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PCMPGTW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPGTW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x65\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PCMPGTD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2}, + {I_PCMPGTD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x66\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PEXTRW, 3, {REG32,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC5\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PINSRW, 3, {XMMREG,REG16,IMMEDIATE}, "\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PINSRW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC4\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PMADDWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMADDWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF5\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMAXSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMAXSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMAXUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMAXUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDE\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMINSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMINSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMINUB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMINUB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xDA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMOVMSKB, 2, {REG32,XMMREG,0}, "\3\x66\x0F\xD7\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULHUW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULHUW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMULHW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMULHW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE5\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMULLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD5\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULUDQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULUDQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PMULUDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2}, + {I_PMULUDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF4\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_POR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_POR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEB\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSADBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSADBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSHUFD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PSHUFD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + {I_PSHUFHW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PSHUFHW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\333\2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + {I_PSHUFLW, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_PSHUFLW, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\xF2\x0F\x70\110\22", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + {I_PSLLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\207\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSLLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSLLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF1\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSLLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSLLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSLLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF2\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSLLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSLLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSLLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF3\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSLLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\206\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSRAW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRAW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE1\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRAW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSRAD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRAD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE2\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRAD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\204\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSRLDQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\203\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSRLW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRLW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD1\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRLW, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x71\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSRLD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRLD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD2\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRLD, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x72\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSRLQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSRLQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD3\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSRLQ, 2, {XMMREG,IMMEDIATE,0}, "\3\x66\x0F\x73\202\25", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR1}, + {I_PSUBB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF8\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xF9\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFA\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBQ, 2, {MMXREG,MMXREG,0}, "\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBQ, 2, {MMXREG,MEMORY,0}, "\301\2\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xFB\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE8\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE9\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBUSB, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBUSB, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD8\110", IF_WILLAMETTE|IF_SSE2}, + {I_PSUBUSW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PSUBUSW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD9\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKHBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKHBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x68\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKHWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKHWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x69\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKHDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKHDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6A\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKHQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKHQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKLBW, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKLBW, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x60\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKLWD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKLWD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x61\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKLDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PUNPCKLDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x62\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKLQDQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2}, + {I_PUNPCKLQDQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x6C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PXOR, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_PXOR, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xEF\110", IF_WILLAMETTE|IF_SSE2}, + {I_ADDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, + {I_ADDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x58\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_ADDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, + {I_ADDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\x58\110", IF_WILLAMETTE|IF_SSE2}, + {I_ANDNPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2}, + {I_ANDNPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x55\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_ANDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2}, + {I_ANDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x54\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, + {I_CMPEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, + {I_CMPEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x00", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x02", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, + {I_CMPLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x01", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNEQPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPNEQPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNEQSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNEQSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x04", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLEPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPNLEPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLESD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLESD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x06", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLTPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPNLTPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLTSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, + {I_CMPNLTSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x05", IF_WILLAMETTE|IF_SSE2}, + {I_CMPORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, + {I_CMPORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, + {I_CMPORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x07", IF_WILLAMETTE|IF_SSE2}, + {I_CMPUNORDPD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CMPUNORDPD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, + {I_CMPUNORDSD, 2, {XMMREG,MEMORY,0}, "\301\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, + {I_CMPUNORDSD, 2, {XMMREG,XMMREG,0}, "\331\3\xF2\x0F\xC2\110\1\x03", IF_WILLAMETTE|IF_SSE2}, + {I_CMPPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_CMPPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\x66\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM2|IF_SB|IF_AR2}, + {I_CMPSD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_CMPSD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\331\3\xF2\x0F\xC2\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_COMISD, 2, {XMMREG,XMMREG,0}, "\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, + {I_COMISD, 2, {XMMREG,MEMORY,0}, "\301\331\3\x66\x0F\x2F\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTDQ2PD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTDQ2PD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTDQ2PS, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTDQ2PS, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CVTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CVTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPD2PS, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPD2PS, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CVTPI2PD, 2, {XMMREG,MMXREG,0}, "\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPI2PD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPS2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CVTPS2PD, 2, {XMMREG,XMMREG,0}, "\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTPS2PD, 2, {XMMREG,MEMORY,0}, "\301\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2D\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSD2SS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSD2SS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSI2SD, 2, {XMMREG,REG32,0}, "\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSI2SD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x2A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSS2SD, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTSS2SD, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5A\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPD2PI, 2, {MMXREG,XMMREG,0}, "\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPD2PI, 2, {MMXREG,MEMORY,0}, "\301\3\x66\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPD2DQ, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPD2DQ, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xE6\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CVTTPS2DQ, 2, {XMMREG,XMMREG,0}, "\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTPS2DQ, 2, {XMMREG,MEMORY,0}, "\301\333\2\x0F\x5B\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_CVTTSD2SI, 2, {REG32,XMMREG,0}, "\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + {I_CVTTSD2SI, 2, {REG32,MEMORY,0}, "\301\3\xF2\x0F\x2C\110", IF_WILLAMETTE|IF_SSE2}, + {I_DIVPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, + {I_DIVPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_DIVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, + {I_DIVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5E\110", IF_WILLAMETTE|IF_SSE2}, + {I_MAXPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MAXPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MAXSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MAXSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5F\110", IF_WILLAMETTE|IF_SSE2}, + {I_MINPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, + {I_MINPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MINSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, + {I_MINSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5D\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVAPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x29\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVAPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x29\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVAPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x28\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVHPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x17\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x16\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVLPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x13\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x12\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVMSKPD, 2, {REG32,XMMREG,0}, "\3\x66\x0F\x50\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVSD, 2, {MEMORY,XMMREG,0}, "\300\3\xF2\x0F\x11\101", IF_WILLAMETTE|IF_SSE2}, + {I_MOVSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVUPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x11\110", IF_WILLAMETTE|IF_SSE2}, + {I_MOVUPD, 2, {MEMORY,XMMREG,0}, "\300\3\x66\x0F\x11\101", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MOVUPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x10\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MULPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, + {I_MULPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x59\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_MULSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, + {I_MULSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x59\110", IF_WILLAMETTE|IF_SSE2}, + {I_ORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_ORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x56\110", IF_WILLAMETTE|IF_SSE2}, + {I_SHUFPD, 3, {XMMREG,XMMREG,IMMEDIATE}, "\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SB|IF_AR2}, + {I_SHUFPD, 3, {XMMREG,MEMORY,IMMEDIATE}, "\301\3\x66\x0F\xC6\110\26", IF_WILLAMETTE|IF_SSE2|IF_SM|IF_SB|IF_AR2}, + {I_SQRTPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, + {I_SQRTPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x51\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_SQRTSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, + {I_SQRTSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x51\110", IF_WILLAMETTE|IF_SSE2}, + {I_SUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, + {I_SUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_SUBSD, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, + {I_SUBSD, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x5C\110", IF_WILLAMETTE|IF_SSE2}, + {I_UCOMISD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, + {I_UCOMISD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x2E\110", IF_WILLAMETTE|IF_SSE2}, + {I_UNPCKHPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2}, + {I_UNPCKHPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x15\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_UNPCKLPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2}, + {I_UNPCKLPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x14\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_XORPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2}, + {I_XORPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x57\110", IF_WILLAMETTE|IF_SSE2|IF_SM}, + {I_ADDSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_ADDSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, + {I_ADDSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_ADDSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\xD0\110", IF_PRESCOTT|IF_SSE3}, + {I_HADDPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HADDPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, + {I_HADDPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HADDPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7C\110", IF_PRESCOTT|IF_SSE3}, + {I_HSUBPD, 2, {XMMREG,MEMORY,0}, "\301\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HSUBPD, 2, {XMMREG,XMMREG,0}, "\3\x66\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, + {I_HSUBPS, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3|IF_SM}, + {I_HSUBPS, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x7D\110", IF_PRESCOTT|IF_SSE3}, + {I_LDDQU, 2, {XMMREG,MEMORY,0}, "\3\xF2\x0F\xF0\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVDDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVDDUP, 2, {XMMREG,XMMREG,0}, "\3\xF2\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVSHDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVSHDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x16\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVSLDUP, 2, {XMMREG,MEMORY,0}, "\301\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + {I_MOVSLDUP, 2, {XMMREG,XMMREG,0}, "\3\xF3\x0F\x12\110", IF_PRESCOTT|IF_SSE3}, + ITEMPLATE_END +}; + +static struct itemplate *itable_00[] = { + instrux + 29, + instrux + 30, + NULL +}; + +static struct itemplate *itable_01[] = { + instrux + 31, + instrux + 32, + instrux + 33, + instrux + 34, + NULL +}; + +static struct itemplate *itable_02[] = { + instrux + 35, + instrux + 36, + NULL +}; + +static struct itemplate *itable_03[] = { + instrux + 37, + instrux + 38, + instrux + 39, + instrux + 40, + NULL +}; + +static struct itemplate *itable_04[] = { + instrux + 43, + NULL +}; + +static struct itemplate *itable_05[] = { + instrux + 44, + instrux + 45, + NULL +}; + +static struct itemplate *itable_06[] = { + instrux + 778, + instrux + 779, + NULL +}; + +static struct itemplate *itable_07[] = { + instrux + 710, + NULL +}; + +static struct itemplate *itable_08[] = { + instrux + 578, + instrux + 579, + NULL +}; + +static struct itemplate *itable_09[] = { + instrux + 580, + instrux + 581, + instrux + 582, + instrux + 583, + NULL +}; + +static struct itemplate *itable_0A[] = { + instrux + 584, + instrux + 585, + NULL +}; + +static struct itemplate *itable_0B[] = { + instrux + 586, + instrux + 587, + instrux + 588, + instrux + 589, + NULL +}; + +static struct itemplate *itable_0C[] = { + instrux + 592, + NULL +}; + +static struct itemplate *itable_0D[] = { + instrux + 593, + instrux + 594, + NULL +}; + +static struct itemplate *itable_0E[] = { + instrux + 778, + instrux + 779, + NULL +}; + +static struct itemplate *itable_0F[] = { + instrux + 79, + instrux + 80, + instrux + 81, + instrux + 82, + instrux + 83, + instrux + 84, + instrux + 85, + instrux + 86, + instrux + 87, + instrux + 88, + instrux + 89, + instrux + 90, + instrux + 91, + instrux + 92, + instrux + 93, + instrux + 94, + instrux + 95, + instrux + 96, + instrux + 97, + instrux + 98, + instrux + 99, + instrux + 100, + instrux + 101, + instrux + 102, + instrux + 103, + instrux + 104, + instrux + 105, + instrux + 106, + instrux + 107, + instrux + 108, + instrux + 109, + instrux + 110, + instrux + 111, + instrux + 139, + instrux + 167, + instrux + 168, + instrux + 169, + instrux + 170, + instrux + 171, + instrux + 172, + instrux + 173, + instrux + 174, + instrux + 175, + instrux + 176, + instrux + 177, + instrux + 178, + instrux + 179, + instrux + 180, + instrux + 193, + instrux + 261, + instrux + 389, + instrux + 390, + instrux + 391, + instrux + 392, + instrux + 423, + instrux + 424, + instrux + 450, + instrux + 451, + instrux + 452, + instrux + 453, + instrux + 454, + instrux + 456, + instrux + 457, + instrux + 458, + instrux + 459, + instrux + 467, + instrux + 468, + instrux + 469, + instrux + 470, + instrux + 471, + instrux + 472, + instrux + 473, + instrux + 474, + instrux + 475, + instrux + 476, + instrux + 477, + instrux + 478, + instrux + 479, + instrux + 480, + instrux + 499, + instrux + 500, + instrux + 501, + instrux + 502, + instrux + 503, + instrux + 504, + instrux + 505, + instrux + 506, + instrux + 507, + instrux + 508, + instrux + 521, + instrux + 522, + instrux + 523, + instrux + 524, + instrux + 525, + instrux + 526, + instrux + 548, + instrux + 549, + instrux + 550, + instrux + 551, + instrux + 552, + instrux + 553, + instrux + 554, + instrux + 555, + instrux + 559, + instrux + 560, + instrux + 561, + instrux + 562, + instrux + 563, + instrux + 564, + instrux + 565, + instrux + 566, + instrux + 570, + instrux + 610, + instrux + 611, + instrux + 612, + instrux + 613, + instrux + 614, + instrux + 615, + instrux + 616, + instrux + 617, + instrux + 618, + instrux + 619, + instrux + 620, + instrux + 621, + instrux + 622, + instrux + 623, + instrux + 624, + instrux + 625, + instrux + 626, + instrux + 627, + instrux + 628, + instrux + 629, + instrux + 630, + instrux + 631, + instrux + 632, + instrux + 633, + instrux + 634, + instrux + 635, + instrux + 636, + instrux + 637, + instrux + 638, + instrux + 639, + instrux + 640, + instrux + 641, + instrux + 642, + instrux + 643, + instrux + 644, + instrux + 645, + instrux + 646, + instrux + 647, + instrux + 648, + instrux + 649, + instrux + 650, + instrux + 651, + instrux + 652, + instrux + 653, + instrux + 654, + instrux + 655, + instrux + 656, + instrux + 657, + instrux + 658, + instrux + 659, + instrux + 660, + instrux + 661, + instrux + 662, + instrux + 663, + instrux + 664, + instrux + 665, + instrux + 666, + instrux + 667, + instrux + 668, + instrux + 669, + instrux + 670, + instrux + 671, + instrux + 672, + instrux + 673, + instrux + 674, + instrux + 675, + instrux + 676, + instrux + 677, + instrux + 678, + instrux + 679, + instrux + 680, + instrux + 681, + instrux + 682, + instrux + 683, + instrux + 684, + instrux + 685, + instrux + 686, + instrux + 687, + instrux + 688, + instrux + 689, + instrux + 690, + instrux + 691, + instrux + 692, + instrux + 693, + instrux + 694, + instrux + 695, + instrux + 696, + instrux + 697, + instrux + 698, + instrux + 699, + instrux + 700, + instrux + 701, + instrux + 702, + instrux + 703, + instrux + 704, + instrux + 705, + instrux + 711, + instrux + 718, + instrux + 719, + instrux + 720, + instrux + 721, + instrux + 722, + instrux + 723, + instrux + 724, + instrux + 725, + instrux + 726, + instrux + 727, + instrux + 728, + instrux + 729, + instrux + 730, + instrux + 731, + instrux + 732, + instrux + 733, + instrux + 734, + instrux + 735, + instrux + 736, + instrux + 737, + instrux + 738, + instrux + 739, + instrux + 740, + instrux + 741, + instrux + 742, + instrux + 743, + instrux + 744, + instrux + 745, + instrux + 746, + instrux + 747, + instrux + 748, + instrux + 749, + instrux + 750, + instrux + 751, + instrux + 752, + instrux + 753, + instrux + 754, + instrux + 755, + instrux + 756, + instrux + 757, + instrux + 758, + instrux + 759, + instrux + 760, + instrux + 761, + instrux + 762, + instrux + 763, + instrux + 764, + instrux + 765, + instrux + 766, + instrux + 767, + instrux + 768, + instrux + 769, + instrux + 770, + instrux + 771, + instrux + 772, + instrux + 773, + instrux + 780, + instrux + 791, + instrux + 792, + instrux + 811, + instrux + 812, + instrux + 813, + instrux + 814, + instrux + 840, + instrux + 841, + instrux + 842, + instrux + 843, + instrux + 881, + instrux + 891, + instrux + 892, + instrux + 893, + instrux + 894, + instrux + 895, + instrux + 896, + instrux + 897, + instrux + 898, + instrux + 908, + instrux + 909, + instrux + 910, + instrux + 911, + instrux + 912, + instrux + 913, + instrux + 914, + instrux + 915, + instrux + 916, + instrux + 917, + instrux + 918, + instrux + 919, + instrux + 920, + instrux + 922, + instrux + 923, + instrux + 924, + instrux + 925, + instrux + 926, + instrux + 933, + instrux + 934, + instrux + 935, + instrux + 936, + instrux + 960, + instrux + 961, + instrux + 962, + instrux + 963, + instrux + 964, + instrux + 965, + instrux + 966, + instrux + 985, + instrux + 986, + instrux + 987, + instrux + 988, + instrux + 989, + instrux + 990, + instrux + 991, + instrux + 992, + instrux + 993, + instrux + 994, + instrux + 995, + instrux + 996, + instrux + 997, + instrux + 998, + instrux + 999, + instrux + 1000, + instrux + 1001, + instrux + 1002, + instrux + 1003, + instrux + 1004, + instrux + 1005, + instrux + 1008, + instrux + 1009, + instrux + 1010, + instrux + 1011, + instrux + 1012, + instrux + 1013, + instrux + 1014, + instrux + 1015, + instrux + 1016, + instrux + 1058, + instrux + 1059, + instrux + 1060, + instrux + 1061, + instrux + 1062, + instrux + 1063, + instrux + 1064, + instrux + 1065, + instrux + 1067, + instrux + 1068, + instrux + 1069, + instrux + 1070, + instrux + 1071, + instrux + 1072, + instrux + 1073, + instrux + 1074, + instrux + 1075, + instrux + 1076, + instrux + 1077, + instrux + 1078, + instrux + 1079, + instrux + 1080, + instrux + 1081, + instrux + 1082, + instrux + 1083, + instrux + 1084, + instrux + 1085, + instrux + 1086, + instrux + 1087, + instrux + 1088, + instrux + 1089, + instrux + 1090, + instrux + 1091, + instrux + 1092, + instrux + 1093, + instrux + 1094, + instrux + 1095, + instrux + 1096, + instrux + 1097, + instrux + 1098, + instrux + 1099, + instrux + 1100, + instrux + 1101, + instrux + 1102, + instrux + 1103, + instrux + 1104, + instrux + 1105, + instrux + 1106, + instrux + 1107, + instrux + 1108, + instrux + 1109, + instrux + 1110, + instrux + 1111, + instrux + 1112, + instrux + 1113, + instrux + 1114, + instrux + 1115, + instrux + 1116, + instrux + 1117, + instrux + 1118, + instrux + 1119, + instrux + 1120, + instrux + 1121, + instrux + 1122, + instrux + 1123, + instrux + 1124, + instrux + 1125, + instrux + 1126, + instrux + 1127, + instrux + 1128, + instrux + 1129, + instrux + 1130, + instrux + 1131, + instrux + 1132, + instrux + 1133, + instrux + 1134, + instrux + 1135, + instrux + 1136, + instrux + 1137, + instrux + 1138, + instrux + 1139, + instrux + 1140, + instrux + 1141, + instrux + 1142, + instrux + 1143, + instrux + 1144, + instrux + 1145, + instrux + 1146, + instrux + 1147, + instrux + 1148, + instrux + 1149, + instrux + 1150, + instrux + 1151, + instrux + 1152, + instrux + 1153, + instrux + 1154, + instrux + 1155, + instrux + 1156, + instrux + 1157, + instrux + 1158, + instrux + 1159, + instrux + 1160, + instrux + 1161, + instrux + 1162, + instrux + 1163, + instrux + 1164, + instrux + 1165, + instrux + 1166, + instrux + 1167, + instrux + 1168, + instrux + 1169, + instrux + 1170, + instrux + 1171, + instrux + 1172, + instrux + 1173, + instrux + 1174, + instrux + 1175, + instrux + 1176, + instrux + 1177, + instrux + 1178, + instrux + 1179, + instrux + 1180, + instrux + 1181, + instrux + 1182, + instrux + 1183, + instrux + 1184, + instrux + 1185, + instrux + 1186, + instrux + 1187, + instrux + 1188, + instrux + 1189, + instrux + 1190, + instrux + 1191, + instrux + 1192, + instrux + 1193, + instrux + 1194, + instrux + 1195, + instrux + 1196, + instrux + 1197, + instrux + 1198, + instrux + 1199, + instrux + 1200, + instrux + 1201, + instrux + 1202, + instrux + 1203, + instrux + 1204, + instrux + 1205, + instrux + 1206, + instrux + 1207, + instrux + 1208, + instrux + 1209, + instrux + 1210, + instrux + 1211, + instrux + 1212, + instrux + 1213, + instrux + 1214, + instrux + 1215, + instrux + 1216, + instrux + 1217, + instrux + 1218, + instrux + 1219, + instrux + 1220, + instrux + 1221, + instrux + 1222, + instrux + 1223, + instrux + 1224, + instrux + 1225, + instrux + 1226, + instrux + 1227, + instrux + 1228, + instrux + 1229, + instrux + 1230, + instrux + 1231, + instrux + 1232, + instrux + 1233, + instrux + 1235, + instrux + 1237, + instrux + 1240, + instrux + 1241, + instrux + 1250, + instrux + 1251, + instrux + 1252, + instrux + 1253, + instrux + 1255, + instrux + 1258, + instrux + 1259, + instrux + 1272, + instrux + 1273, + instrux + 1324, + instrux + 1325, + instrux + 1334, + instrux + 1335, + instrux + 1370, + instrux + 1371, + instrux + 1446, + instrux + 1447, + instrux + 1448, + instrux + 1449, + instrux + 1460, + instrux + 1461, + instrux + 1468, + instrux + 1469, + instrux + 1474, + instrux + 1475, + NULL +}; + +static struct itemplate *itable_10[] = { + instrux + 6, + instrux + 7, + NULL +}; + +static struct itemplate *itable_11[] = { + instrux + 8, + instrux + 9, + instrux + 10, + instrux + 11, + NULL +}; + +static struct itemplate *itable_12[] = { + instrux + 12, + instrux + 13, + NULL +}; + +static struct itemplate *itable_13[] = { + instrux + 14, + instrux + 15, + instrux + 16, + instrux + 17, + NULL +}; + +static struct itemplate *itable_14[] = { + instrux + 20, + NULL +}; + +static struct itemplate *itable_15[] = { + instrux + 21, + instrux + 22, + NULL +}; + +static struct itemplate *itable_16[] = { + instrux + 778, + instrux + 779, + NULL +}; + +static struct itemplate *itable_17[] = { + instrux + 710, + NULL +}; + +static struct itemplate *itable_18[] = { + instrux + 855, + instrux + 856, + NULL +}; + +static struct itemplate *itable_19[] = { + instrux + 857, + instrux + 858, + instrux + 859, + instrux + 860, + NULL +}; + +static struct itemplate *itable_1A[] = { + instrux + 861, + instrux + 862, + NULL +}; + +static struct itemplate *itable_1B[] = { + instrux + 863, + instrux + 864, + instrux + 865, + instrux + 866, + NULL +}; + +static struct itemplate *itable_1C[] = { + instrux + 869, + NULL +}; + +static struct itemplate *itable_1D[] = { + instrux + 870, + instrux + 871, + NULL +}; + +static struct itemplate *itable_1E[] = { + instrux + 778, + instrux + 779, + NULL +}; + +static struct itemplate *itable_1F[] = { + instrux + 710, + NULL +}; + +static struct itemplate *itable_20[] = { + instrux + 52, + instrux + 53, + NULL +}; + +static struct itemplate *itable_21[] = { + instrux + 54, + instrux + 55, + instrux + 56, + instrux + 57, + NULL +}; + +static struct itemplate *itable_22[] = { + instrux + 58, + instrux + 59, + NULL +}; + +static struct itemplate *itable_23[] = { + instrux + 60, + instrux + 61, + instrux + 62, + instrux + 63, + NULL +}; + +static struct itemplate *itable_24[] = { + instrux + 66, + NULL +}; + +static struct itemplate *itable_25[] = { + instrux + 67, + instrux + 68, + NULL +}; + +static struct itemplate *itable_26[] = { + NULL +}; + +static struct itemplate *itable_27[] = { + instrux + 183, + NULL +}; + +static struct itemplate *itable_28[] = { + instrux + 937, + instrux + 938, + NULL +}; + +static struct itemplate *itable_29[] = { + instrux + 939, + instrux + 940, + instrux + 941, + instrux + 942, + NULL +}; + +static struct itemplate *itable_2A[] = { + instrux + 943, + instrux + 944, + NULL +}; + +static struct itemplate *itable_2B[] = { + instrux + 945, + instrux + 946, + instrux + 947, + instrux + 948, + NULL +}; + +static struct itemplate *itable_2C[] = { + instrux + 951, + NULL +}; + +static struct itemplate *itable_2D[] = { + instrux + 952, + instrux + 953, + NULL +}; + +static struct itemplate *itable_2E[] = { + NULL +}; + +static struct itemplate *itable_2F[] = { + instrux + 184, + NULL +}; + +static struct itemplate *itable_30[] = { + instrux + 1035, + instrux + 1036, + NULL +}; + +static struct itemplate *itable_31[] = { + instrux + 1037, + instrux + 1038, + instrux + 1039, + instrux + 1040, + NULL +}; + +static struct itemplate *itable_32[] = { + instrux + 1041, + instrux + 1042, + NULL +}; + +static struct itemplate *itable_33[] = { + instrux + 1043, + instrux + 1044, + instrux + 1045, + instrux + 1046, + NULL +}; + +static struct itemplate *itable_34[] = { + instrux + 1049, + NULL +}; + +static struct itemplate *itable_35[] = { + instrux + 1050, + instrux + 1051, + NULL +}; + +static struct itemplate *itable_36[] = { + NULL +}; + +static struct itemplate *itable_37[] = { + instrux + 0, + NULL +}; + +static struct itemplate *itable_38[] = { + instrux + 141, + instrux + 142, + NULL +}; + +static struct itemplate *itable_39[] = { + instrux + 143, + instrux + 144, + instrux + 145, + instrux + 146, + NULL +}; + +static struct itemplate *itable_3A[] = { + instrux + 147, + instrux + 148, + NULL +}; + +static struct itemplate *itable_3B[] = { + instrux + 149, + instrux + 150, + instrux + 151, + instrux + 152, + NULL +}; + +static struct itemplate *itable_3C[] = { + instrux + 155, + NULL +}; + +static struct itemplate *itable_3D[] = { + instrux + 156, + instrux + 157, + NULL +}; + +static struct itemplate *itable_3E[] = { + NULL +}; + +static struct itemplate *itable_3F[] = { + instrux + 5, + NULL +}; + +static struct itemplate *itable_40[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_41[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_42[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_43[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_44[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_45[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_46[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_47[] = { + instrux + 411, + instrux + 412, + NULL +}; + +static struct itemplate *itable_48[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_49[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_4A[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_4B[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_4C[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_4D[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_4E[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_4F[] = { + instrux + 185, + instrux + 186, + NULL +}; + +static struct itemplate *itable_50[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_51[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_52[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_53[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_54[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_55[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_56[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_57[] = { + instrux + 774, + instrux + 775, + NULL +}; + +static struct itemplate *itable_58[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_59[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_5A[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_5B[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_5C[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_5D[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_5E[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_5F[] = { + instrux + 706, + instrux + 707, + NULL +}; + +static struct itemplate *itable_60[] = { + instrux + 785, + instrux + 786, + instrux + 787, + NULL +}; + +static struct itemplate *itable_61[] = { + instrux + 712, + instrux + 713, + instrux + 714, + NULL +}; + +static struct itemplate *itable_62[] = { + instrux + 77, + instrux + 78, + NULL +}; + +static struct itemplate *itable_63[] = { + instrux + 75, + instrux + 76, + NULL +}; + +static struct itemplate *itable_64[] = { + NULL +}; + +static struct itemplate *itable_65[] = { + NULL +}; + +static struct itemplate *itable_66[] = { + instrux + 1234, + instrux + 1236, + instrux + 1238, + instrux + 1242, + instrux + 1243, + instrux + 1244, + instrux + 1245, + instrux + 1246, + instrux + 1247, + instrux + 1248, + instrux + 1249, + instrux + 1256, + instrux + 1257, + instrux + 1260, + instrux + 1261, + instrux + 1262, + instrux + 1263, + instrux + 1264, + instrux + 1265, + instrux + 1266, + instrux + 1267, + instrux + 1268, + instrux + 1269, + instrux + 1270, + instrux + 1271, + instrux + 1274, + instrux + 1275, + instrux + 1276, + instrux + 1277, + instrux + 1278, + instrux + 1279, + instrux + 1280, + instrux + 1281, + instrux + 1282, + instrux + 1283, + instrux + 1284, + instrux + 1285, + instrux + 1286, + instrux + 1287, + instrux + 1288, + instrux + 1289, + instrux + 1290, + instrux + 1291, + instrux + 1292, + instrux + 1293, + instrux + 1294, + instrux + 1295, + instrux + 1296, + instrux + 1297, + instrux + 1298, + instrux + 1299, + instrux + 1300, + instrux + 1301, + instrux + 1302, + instrux + 1303, + instrux + 1304, + instrux + 1305, + instrux + 1306, + instrux + 1307, + instrux + 1308, + instrux + 1309, + instrux + 1310, + instrux + 1311, + instrux + 1312, + instrux + 1313, + instrux + 1314, + instrux + 1315, + instrux + 1316, + instrux + 1317, + instrux + 1318, + instrux + 1319, + instrux + 1320, + instrux + 1321, + instrux + 1322, + instrux + 1323, + instrux + 1326, + instrux + 1327, + instrux + 1328, + instrux + 1329, + instrux + 1330, + instrux + 1331, + instrux + 1332, + instrux + 1333, + instrux + 1338, + instrux + 1339, + instrux + 1340, + instrux + 1341, + instrux + 1342, + instrux + 1343, + instrux + 1344, + instrux + 1345, + instrux + 1346, + instrux + 1347, + instrux + 1348, + instrux + 1349, + instrux + 1350, + instrux + 1351, + instrux + 1352, + instrux + 1353, + instrux + 1354, + instrux + 1355, + instrux + 1356, + instrux + 1357, + instrux + 1358, + instrux + 1359, + instrux + 1360, + instrux + 1361, + instrux + 1362, + instrux + 1363, + instrux + 1364, + instrux + 1365, + instrux + 1366, + instrux + 1367, + instrux + 1368, + instrux + 1369, + instrux + 1372, + instrux + 1373, + instrux + 1374, + instrux + 1375, + instrux + 1376, + instrux + 1377, + instrux + 1378, + instrux + 1379, + instrux + 1380, + instrux + 1381, + instrux + 1382, + instrux + 1383, + instrux + 1384, + instrux + 1385, + instrux + 1386, + instrux + 1387, + instrux + 1388, + instrux + 1389, + instrux + 1390, + instrux + 1391, + instrux + 1392, + instrux + 1393, + instrux + 1394, + instrux + 1395, + instrux + 1396, + instrux + 1397, + instrux + 1398, + instrux + 1399, + instrux + 1400, + instrux + 1401, + instrux + 1404, + instrux + 1405, + instrux + 1406, + instrux + 1407, + instrux + 1408, + instrux + 1409, + instrux + 1412, + instrux + 1413, + instrux + 1416, + instrux + 1417, + instrux + 1420, + instrux + 1421, + instrux + 1424, + instrux + 1425, + instrux + 1428, + instrux + 1429, + instrux + 1432, + instrux + 1433, + instrux + 1436, + instrux + 1437, + instrux + 1440, + instrux + 1441, + instrux + 1444, + instrux + 1445, + instrux + 1452, + instrux + 1453, + instrux + 1454, + instrux + 1455, + instrux + 1456, + instrux + 1457, + instrux + 1458, + instrux + 1459, + instrux + 1470, + instrux + 1471, + instrux + 1472, + instrux + 1473, + instrux + 1478, + instrux + 1479, + instrux + 1482, + instrux + 1483, + instrux + 1486, + instrux + 1487, + instrux + 1490, + instrux + 1491, + instrux + 1492, + instrux + 1493, + instrux + 1494, + instrux + 1495, + instrux + 1496, + instrux + 1497, + instrux + 1498, + instrux + 1503, + instrux + 1504, + instrux + 1505, + instrux + 1506, + instrux + 1507, + instrux + 1508, + instrux + 1511, + instrux + 1512, + instrux + 1513, + instrux + 1514, + instrux + 1515, + instrux + 1516, + instrux + 1519, + instrux + 1520, + instrux + 1523, + instrux + 1524, + instrux + 1525, + instrux + 1526, + instrux + 1527, + instrux + 1528, + instrux + 1529, + instrux + 1530, + instrux + 1531, + instrux + 1532, + instrux + 1535, + instrux + 1536, + instrux + 1539, + instrux + 1540, + NULL +}; + +static struct itemplate *itable_67[] = { + NULL +}; + +static struct itemplate *itable_68[] = { + instrux + 782, + instrux + 783, + instrux + 784, + NULL +}; + +static struct itemplate *itable_69[] = { + instrux + 394, + instrux + 396, + instrux + 398, + instrux + 400, + instrux + 402, + instrux + 404, + NULL +}; + +static struct itemplate *itable_6A[] = { + instrux + 781, + NULL +}; + +static struct itemplate *itable_6B[] = { + instrux + 393, + instrux + 395, + instrux + 397, + instrux + 399, + instrux + 401, + instrux + 403, + NULL +}; + +static struct itemplate *itable_6C[] = { + instrux + 416, + NULL +}; + +static struct itemplate *itable_6D[] = { + instrux + 417, + instrux + 418, + NULL +}; + +static struct itemplate *itable_6E[] = { + instrux + 607, + NULL +}; + +static struct itemplate *itable_6F[] = { + instrux + 608, + instrux + 609, + NULL +}; + +static struct itemplate *itable_70[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_71[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_72[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_73[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_74[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_75[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_76[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_77[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_78[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_79[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_7A[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_7B[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_7C[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_7D[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_7E[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_7F[] = { + instrux + 1066, + NULL +}; + +static struct itemplate *itable_80[] = { + instrux + 23, + instrux + 26, + instrux + 46, + instrux + 49, + instrux + 69, + instrux + 72, + instrux + 158, + instrux + 161, + instrux + 595, + instrux + 598, + instrux + 872, + instrux + 875, + instrux + 954, + instrux + 957, + instrux + 1052, + instrux + 1055, + NULL +}; + +static struct itemplate *itable_81[] = { + instrux + 24, + instrux + 25, + instrux + 27, + instrux + 28, + instrux + 47, + instrux + 48, + instrux + 50, + instrux + 51, + instrux + 70, + instrux + 71, + instrux + 73, + instrux + 74, + instrux + 159, + instrux + 160, + instrux + 162, + instrux + 163, + instrux + 596, + instrux + 597, + instrux + 599, + instrux + 600, + instrux + 873, + instrux + 874, + instrux + 876, + instrux + 877, + instrux + 955, + instrux + 956, + instrux + 958, + instrux + 959, + instrux + 1053, + instrux + 1054, + instrux + 1056, + instrux + 1057, + NULL +}; + +static struct itemplate *itable_82[] = { + NULL +}; + +static struct itemplate *itable_83[] = { + instrux + 18, + instrux + 19, + instrux + 41, + instrux + 42, + instrux + 64, + instrux + 65, + instrux + 153, + instrux + 154, + instrux + 590, + instrux + 591, + instrux + 867, + instrux + 868, + instrux + 949, + instrux + 950, + instrux + 1047, + instrux + 1048, + NULL +}; + +static struct itemplate *itable_84[] = { + instrux + 967, + instrux + 968, + instrux + 973, + NULL +}; + +static struct itemplate *itable_85[] = { + instrux + 969, + instrux + 970, + instrux + 971, + instrux + 972, + instrux + 974, + instrux + 975, + NULL +}; + +static struct itemplate *itable_86[] = { + instrux + 1021, + instrux + 1022, + instrux + 1027, + instrux + 1028, + NULL +}; + +static struct itemplate *itable_87[] = { + instrux + 1023, + instrux + 1024, + instrux + 1025, + instrux + 1026, + instrux + 1029, + instrux + 1030, + instrux + 1031, + instrux + 1032, + NULL +}; + +static struct itemplate *itable_88[] = { + instrux + 527, + instrux + 528, + NULL +}; + +static struct itemplate *itable_89[] = { + instrux + 529, + instrux + 530, + instrux + 531, + instrux + 532, + NULL +}; + +static struct itemplate *itable_8A[] = { + instrux + 533, + instrux + 534, + NULL +}; + +static struct itemplate *itable_8B[] = { + instrux + 535, + instrux + 536, + instrux + 537, + instrux + 538, + NULL +}; + +static struct itemplate *itable_8C[] = { + instrux + 509, + instrux + 510, + instrux + 511, + NULL +}; + +static struct itemplate *itable_8D[] = { + instrux + 462, + instrux + 463, + NULL +}; + +static struct itemplate *itable_8E[] = { + instrux + 512, + instrux + 513, + instrux + 514, + NULL +}; + +static struct itemplate *itable_8F[] = { + instrux + 708, + instrux + 709, + NULL +}; + +static struct itemplate *itable_90[] = { + instrux + 574, + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + instrux + 1239, + NULL +}; + +static struct itemplate *itable_91[] = { + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + NULL +}; + +static struct itemplate *itable_92[] = { + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + NULL +}; + +static struct itemplate *itable_93[] = { + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + NULL +}; + +static struct itemplate *itable_94[] = { + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + NULL +}; + +static struct itemplate *itable_95[] = { + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + NULL +}; + +static struct itemplate *itable_96[] = { + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + NULL +}; + +static struct itemplate *itable_97[] = { + instrux + 1017, + instrux + 1018, + instrux + 1019, + instrux + 1020, + NULL +}; + +static struct itemplate *itable_98[] = { + instrux + 134, + instrux + 182, + NULL +}; + +static struct itemplate *itable_99[] = { + instrux + 135, + instrux + 181, + NULL +}; + +static struct itemplate *itable_9A[] = { + instrux + 118, + instrux + 119, + instrux + 120, + instrux + 121, + instrux + 122, + NULL +}; + +static struct itemplate *itable_9B[] = { + instrux + 212, + instrux + 244, + instrux + 262, + instrux + 281, + instrux + 331, + instrux + 340, + instrux + 341, + instrux + 346, + instrux + 347, + instrux + 1006, + instrux + 1007, + NULL +}; + +static struct itemplate *itable_9C[] = { + instrux + 788, + instrux + 789, + instrux + 790, + NULL +}; + +static struct itemplate *itable_9D[] = { + instrux + 715, + instrux + 716, + instrux + 717, + NULL +}; + +static struct itemplate *itable_9E[] = { + instrux + 844, + NULL +}; + +static struct itemplate *itable_9F[] = { + instrux + 455, + NULL +}; + +static struct itemplate *itable_A0[] = { + instrux + 515, + NULL +}; + +static struct itemplate *itable_A1[] = { + instrux + 516, + instrux + 517, + NULL +}; + +static struct itemplate *itable_A2[] = { + instrux + 518, + NULL +}; + +static struct itemplate *itable_A3[] = { + instrux + 519, + instrux + 520, + NULL +}; + +static struct itemplate *itable_A4[] = { + instrux + 556, + NULL +}; + +static struct itemplate *itable_A5[] = { + instrux + 557, + instrux + 558, + NULL +}; + +static struct itemplate *itable_A6[] = { + instrux + 164, + NULL +}; + +static struct itemplate *itable_A7[] = { + instrux + 165, + instrux + 166, + NULL +}; + +static struct itemplate *itable_A8[] = { + instrux + 976, + NULL +}; + +static struct itemplate *itable_A9[] = { + instrux + 977, + instrux + 978, + NULL +}; + +static struct itemplate *itable_AA[] = { + instrux + 930, + NULL +}; + +static struct itemplate *itable_AB[] = { + instrux + 931, + instrux + 932, + NULL +}; + +static struct itemplate *itable_AC[] = { + instrux + 481, + NULL +}; + +static struct itemplate *itable_AD[] = { + instrux + 482, + instrux + 483, + NULL +}; + +static struct itemplate *itable_AE[] = { + instrux + 878, + NULL +}; + +static struct itemplate *itable_AF[] = { + instrux + 879, + instrux + 880, + NULL +}; + +static struct itemplate *itable_B0[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B1[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B2[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B3[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B4[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B5[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B6[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B7[] = { + instrux + 539, + NULL +}; + +static struct itemplate *itable_B8[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_B9[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_BA[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_BB[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_BC[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_BD[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_BE[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_BF[] = { + instrux + 540, + instrux + 541, + NULL +}; + +static struct itemplate *itable_C0[] = { + instrux + 795, + instrux + 804, + instrux + 824, + instrux + 833, + instrux + 848, + instrux + 884, + instrux + 901, + NULL +}; + +static struct itemplate *itable_C1[] = { + instrux + 798, + instrux + 801, + instrux + 807, + instrux + 810, + instrux + 827, + instrux + 830, + instrux + 836, + instrux + 839, + instrux + 851, + instrux + 854, + instrux + 887, + instrux + 890, + instrux + 904, + instrux + 907, + NULL +}; + +static struct itemplate *itable_C2[] = { + instrux + 817, + instrux + 821, + NULL +}; + +static struct itemplate *itable_C3[] = { + instrux + 816, + instrux + 820, + NULL +}; + +static struct itemplate *itable_C4[] = { + instrux + 465, + instrux + 466, + NULL +}; + +static struct itemplate *itable_C5[] = { + instrux + 460, + instrux + 461, + NULL +}; + +static struct itemplate *itable_C6[] = { + instrux + 542, + instrux + 545, + NULL +}; + +static struct itemplate *itable_C7[] = { + instrux + 543, + instrux + 544, + instrux + 546, + instrux + 547, + NULL +}; + +static struct itemplate *itable_C8[] = { + instrux + 194, + NULL +}; + +static struct itemplate *itable_C9[] = { + instrux + 464, + NULL +}; + +static struct itemplate *itable_CA[] = { + instrux + 819, + NULL +}; + +static struct itemplate *itable_CB[] = { + instrux + 818, + NULL +}; + +static struct itemplate *itable_CC[] = { + instrux + 421, + NULL +}; + +static struct itemplate *itable_CD[] = { + instrux + 419, + NULL +}; + +static struct itemplate *itable_CE[] = { + instrux + 422, + NULL +}; + +static struct itemplate *itable_CF[] = { + instrux + 425, + instrux + 426, + instrux + 427, + NULL +}; + +static struct itemplate *itable_D0[] = { + instrux + 793, + instrux + 802, + instrux + 822, + instrux + 831, + instrux + 846, + instrux + 882, + instrux + 899, + NULL +}; + +static struct itemplate *itable_D1[] = { + instrux + 796, + instrux + 799, + instrux + 805, + instrux + 808, + instrux + 825, + instrux + 828, + instrux + 834, + instrux + 837, + instrux + 849, + instrux + 852, + instrux + 885, + instrux + 888, + instrux + 902, + instrux + 905, + NULL +}; + +static struct itemplate *itable_D2[] = { + instrux + 794, + instrux + 803, + instrux + 823, + instrux + 832, + instrux + 847, + instrux + 883, + instrux + 900, + NULL +}; + +static struct itemplate *itable_D3[] = { + instrux + 797, + instrux + 800, + instrux + 806, + instrux + 809, + instrux + 826, + instrux + 829, + instrux + 835, + instrux + 838, + instrux + 850, + instrux + 853, + instrux + 886, + instrux + 889, + instrux + 903, + instrux + 906, + NULL +}; + +static struct itemplate *itable_D4[] = { + instrux + 3, + instrux + 4, + NULL +}; + +static struct itemplate *itable_D5[] = { + instrux + 1, + instrux + 2, + NULL +}; + +static struct itemplate *itable_D6[] = { + instrux + 845, + NULL +}; + +static struct itemplate *itable_D7[] = { + instrux + 1033, + instrux + 1034, + NULL +}; + +static struct itemplate *itable_D8[] = { + instrux + 199, + instrux + 202, + instrux + 204, + instrux + 229, + instrux + 231, + instrux + 232, + instrux + 237, + instrux + 239, + instrux + 240, + instrux + 245, + instrux + 249, + instrux + 250, + instrux + 253, + instrux + 257, + instrux + 258, + instrux + 307, + instrux + 311, + instrux + 312, + instrux + 348, + instrux + 352, + instrux + 353, + instrux + 356, + instrux + 360, + instrux + 361, + NULL +}; + +static struct itemplate *itable_D9[] = { + instrux + 197, + instrux + 198, + instrux + 211, + instrux + 242, + instrux + 243, + instrux + 280, + instrux + 294, + instrux + 297, + instrux + 298, + instrux + 299, + instrux + 300, + instrux + 301, + instrux + 302, + instrux + 303, + instrux + 304, + instrux + 305, + instrux + 306, + instrux + 319, + instrux + 321, + instrux + 322, + instrux + 325, + instrux + 326, + instrux + 327, + instrux + 328, + instrux + 329, + instrux + 332, + instrux + 334, + instrux + 335, + instrux + 336, + instrux + 337, + instrux + 342, + instrux + 364, + instrux + 374, + instrux + 375, + instrux + 376, + instrux + 377, + instrux + 378, + instrux + 379, + instrux + 380, + instrux + 381, + NULL +}; + +static struct itemplate *itable_DA[] = { + instrux + 213, + instrux + 214, + instrux + 215, + instrux + 216, + instrux + 217, + instrux + 218, + instrux + 227, + instrux + 228, + instrux + 265, + instrux + 267, + instrux + 269, + instrux + 271, + instrux + 273, + instrux + 278, + instrux + 290, + instrux + 292, + instrux + 373, + NULL +}; + +static struct itemplate *itable_DB[] = { + instrux + 219, + instrux + 220, + instrux + 221, + instrux + 222, + instrux + 223, + instrux + 224, + instrux + 225, + instrux + 226, + instrux + 233, + instrux + 234, + instrux + 275, + instrux + 282, + instrux + 284, + instrux + 288, + instrux + 296, + instrux + 315, + instrux + 316, + instrux + 317, + instrux + 318, + instrux + 333, + instrux + 344, + instrux + 367, + instrux + 368, + NULL +}; + +static struct itemplate *itable_DC[] = { + instrux + 200, + instrux + 201, + instrux + 203, + instrux + 230, + instrux + 238, + instrux + 246, + instrux + 247, + instrux + 248, + instrux + 254, + instrux + 255, + instrux + 256, + instrux + 308, + instrux + 309, + instrux + 310, + instrux + 349, + instrux + 350, + instrux + 351, + instrux + 357, + instrux + 358, + instrux + 359, + NULL +}; + +static struct itemplate *itable_DD[] = { + instrux + 263, + instrux + 287, + instrux + 295, + instrux + 320, + instrux + 323, + instrux + 330, + instrux + 338, + instrux + 339, + instrux + 343, + instrux + 345, + instrux + 365, + instrux + 366, + instrux + 371, + instrux + 372, + NULL +}; + +static struct itemplate *itable_DE[] = { + instrux + 205, + instrux + 206, + instrux + 241, + instrux + 251, + instrux + 252, + instrux + 259, + instrux + 260, + instrux + 266, + instrux + 268, + instrux + 270, + instrux + 272, + instrux + 274, + instrux + 279, + instrux + 291, + instrux + 293, + instrux + 313, + instrux + 314, + instrux + 354, + instrux + 355, + instrux + 362, + instrux + 363, + NULL +}; + +static struct itemplate *itable_DF[] = { + instrux + 207, + instrux + 208, + instrux + 209, + instrux + 210, + instrux + 235, + instrux + 236, + instrux + 264, + instrux + 276, + instrux + 277, + instrux + 283, + instrux + 285, + instrux + 286, + instrux + 289, + instrux + 324, + instrux + 369, + instrux + 370, + NULL +}; + +static struct itemplate *itable_E0[] = { + instrux + 490, + instrux + 491, + instrux + 492, + instrux + 493, + instrux + 494, + instrux + 495, + NULL +}; + +static struct itemplate *itable_E1[] = { + instrux + 487, + instrux + 488, + instrux + 489, + instrux + 496, + instrux + 497, + instrux + 498, + NULL +}; + +static struct itemplate *itable_E2[] = { + instrux + 484, + instrux + 485, + instrux + 486, + NULL +}; + +static struct itemplate *itable_E3[] = { + instrux + 428, + instrux + 429, + NULL +}; + +static struct itemplate *itable_E4[] = { + instrux + 405, + NULL +}; + +static struct itemplate *itable_E5[] = { + instrux + 406, + instrux + 407, + NULL +}; + +static struct itemplate *itable_E6[] = { + instrux + 601, + NULL +}; + +static struct itemplate *itable_E7[] = { + instrux + 602, + instrux + 603, + NULL +}; + +static struct itemplate *itable_E8[] = { + instrux + 112, + instrux + 113, + instrux + 114, + instrux + 115, + instrux + 116, + instrux + 117, + NULL +}; + +static struct itemplate *itable_E9[] = { + instrux + 431, + instrux + 432, + instrux + 433, + NULL +}; + +static struct itemplate *itable_EA[] = { + instrux + 434, + instrux + 435, + instrux + 436, + instrux + 437, + instrux + 438, + NULL +}; + +static struct itemplate *itable_EB[] = { + instrux + 430, + NULL +}; + +static struct itemplate *itable_EC[] = { + instrux + 408, + NULL +}; + +static struct itemplate *itable_ED[] = { + instrux + 409, + instrux + 410, + NULL +}; + +static struct itemplate *itable_EE[] = { + instrux + 604, + NULL +}; + +static struct itemplate *itable_EF[] = { + instrux + 605, + instrux + 606, + NULL +}; + +static struct itemplate *itable_F0[] = { + NULL +}; + +static struct itemplate *itable_F1[] = { + instrux + 420, + instrux + 921, + NULL +}; + +static struct itemplate *itable_F2[] = { + instrux + 1254, + instrux + 1336, + instrux + 1337, + instrux + 1402, + instrux + 1403, + instrux + 1410, + instrux + 1411, + instrux + 1414, + instrux + 1415, + instrux + 1418, + instrux + 1419, + instrux + 1422, + instrux + 1423, + instrux + 1426, + instrux + 1427, + instrux + 1430, + instrux + 1431, + instrux + 1434, + instrux + 1435, + instrux + 1438, + instrux + 1439, + instrux + 1442, + instrux + 1443, + instrux + 1450, + instrux + 1451, + instrux + 1462, + instrux + 1463, + instrux + 1464, + instrux + 1465, + instrux + 1466, + instrux + 1467, + instrux + 1476, + instrux + 1477, + instrux + 1480, + instrux + 1481, + instrux + 1484, + instrux + 1485, + instrux + 1488, + instrux + 1489, + instrux + 1499, + instrux + 1500, + instrux + 1501, + instrux + 1502, + instrux + 1509, + instrux + 1510, + instrux + 1517, + instrux + 1518, + instrux + 1521, + instrux + 1522, + instrux + 1533, + instrux + 1534, + instrux + 1537, + instrux + 1538, + instrux + 1541, + instrux + 1542, + instrux + 1543, + instrux + 1544, + instrux + 1545, + NULL +}; + +static struct itemplate *itable_F3[] = { + instrux + 1546, + instrux + 1547, + instrux + 1548, + instrux + 1549, + NULL +}; + +static struct itemplate *itable_F4[] = { + instrux + 382, + NULL +}; + +static struct itemplate *itable_F5[] = { + instrux + 140, + NULL +}; + +static struct itemplate *itable_F6[] = { + instrux + 190, + instrux + 383, + instrux + 386, + instrux + 567, + instrux + 571, + instrux + 575, + instrux + 979, + instrux + 982, + NULL +}; + +static struct itemplate *itable_F7[] = { + instrux + 191, + instrux + 192, + instrux + 384, + instrux + 385, + instrux + 387, + instrux + 388, + instrux + 568, + instrux + 569, + instrux + 572, + instrux + 573, + instrux + 576, + instrux + 577, + instrux + 980, + instrux + 981, + instrux + 983, + instrux + 984, + NULL +}; + +static struct itemplate *itable_F8[] = { + instrux + 136, + NULL +}; + +static struct itemplate *itable_F9[] = { + instrux + 927, + NULL +}; + +static struct itemplate *itable_FA[] = { + instrux + 138, + NULL +}; + +static struct itemplate *itable_FB[] = { + instrux + 929, + NULL +}; + +static struct itemplate *itable_FC[] = { + instrux + 137, + NULL +}; + +static struct itemplate *itable_FD[] = { + instrux + 928, + NULL +}; + +static struct itemplate *itable_FE[] = { + instrux + 187, + instrux + 413, + NULL +}; + +static struct itemplate *itable_FF[] = { + instrux + 123, + instrux + 124, + instrux + 125, + instrux + 126, + instrux + 127, + instrux + 128, + instrux + 129, + instrux + 130, + instrux + 131, + instrux + 132, + instrux + 133, + instrux + 188, + instrux + 189, + instrux + 414, + instrux + 415, + instrux + 439, + instrux + 440, + instrux + 441, + instrux + 442, + instrux + 443, + instrux + 444, + instrux + 445, + instrux + 446, + instrux + 447, + instrux + 448, + instrux + 449, + instrux + 776, + instrux + 777, + NULL +}; + +struct itemplate **itable[] = { + itable_00, + itable_01, + itable_02, + itable_03, + itable_04, + itable_05, + itable_06, + itable_07, + itable_08, + itable_09, + itable_0A, + itable_0B, + itable_0C, + itable_0D, + itable_0E, + itable_0F, + itable_10, + itable_11, + itable_12, + itable_13, + itable_14, + itable_15, + itable_16, + itable_17, + itable_18, + itable_19, + itable_1A, + itable_1B, + itable_1C, + itable_1D, + itable_1E, + itable_1F, + itable_20, + itable_21, + itable_22, + itable_23, + itable_24, + itable_25, + itable_26, + itable_27, + itable_28, + itable_29, + itable_2A, + itable_2B, + itable_2C, + itable_2D, + itable_2E, + itable_2F, + itable_30, + itable_31, + itable_32, + itable_33, + itable_34, + itable_35, + itable_36, + itable_37, + itable_38, + itable_39, + itable_3A, + itable_3B, + itable_3C, + itable_3D, + itable_3E, + itable_3F, + itable_40, + itable_41, + itable_42, + itable_43, + itable_44, + itable_45, + itable_46, + itable_47, + itable_48, + itable_49, + itable_4A, + itable_4B, + itable_4C, + itable_4D, + itable_4E, + itable_4F, + itable_50, + itable_51, + itable_52, + itable_53, + itable_54, + itable_55, + itable_56, + itable_57, + itable_58, + itable_59, + itable_5A, + itable_5B, + itable_5C, + itable_5D, + itable_5E, + itable_5F, + itable_60, + itable_61, + itable_62, + itable_63, + itable_64, + itable_65, + itable_66, + itable_67, + itable_68, + itable_69, + itable_6A, + itable_6B, + itable_6C, + itable_6D, + itable_6E, + itable_6F, + itable_70, + itable_71, + itable_72, + itable_73, + itable_74, + itable_75, + itable_76, + itable_77, + itable_78, + itable_79, + itable_7A, + itable_7B, + itable_7C, + itable_7D, + itable_7E, + itable_7F, + itable_80, + itable_81, + itable_82, + itable_83, + itable_84, + itable_85, + itable_86, + itable_87, + itable_88, + itable_89, + itable_8A, + itable_8B, + itable_8C, + itable_8D, + itable_8E, + itable_8F, + itable_90, + itable_91, + itable_92, + itable_93, + itable_94, + itable_95, + itable_96, + itable_97, + itable_98, + itable_99, + itable_9A, + itable_9B, + itable_9C, + itable_9D, + itable_9E, + itable_9F, + itable_A0, + itable_A1, + itable_A2, + itable_A3, + itable_A4, + itable_A5, + itable_A6, + itable_A7, + itable_A8, + itable_A9, + itable_AA, + itable_AB, + itable_AC, + itable_AD, + itable_AE, + itable_AF, + itable_B0, + itable_B1, + itable_B2, + itable_B3, + itable_B4, + itable_B5, + itable_B6, + itable_B7, + itable_B8, + itable_B9, + itable_BA, + itable_BB, + itable_BC, + itable_BD, + itable_BE, + itable_BF, + itable_C0, + itable_C1, + itable_C2, + itable_C3, + itable_C4, + itable_C5, + itable_C6, + itable_C7, + itable_C8, + itable_C9, + itable_CA, + itable_CB, + itable_CC, + itable_CD, + itable_CE, + itable_CF, + itable_D0, + itable_D1, + itable_D2, + itable_D3, + itable_D4, + itable_D5, + itable_D6, + itable_D7, + itable_D8, + itable_D9, + itable_DA, + itable_DB, + itable_DC, + itable_DD, + itable_DE, + itable_DF, + itable_E0, + itable_E1, + itable_E2, + itable_E3, + itable_E4, + itable_E5, + itable_E6, + itable_E7, + itable_E8, + itable_E9, + itable_EA, + itable_EB, + itable_EC, + itable_ED, + itable_EE, + itable_EF, + itable_F0, + itable_F1, + itable_F2, + itable_F3, + itable_F4, + itable_F5, + itable_F6, + itable_F7, + itable_F8, + itable_F9, + itable_FA, + itable_FB, + itable_FC, + itable_FD, + itable_FE, + itable_FF, +}; diff --git a/AltairZ80/mfdc.c b/AltairZ80/mfdc.c new file mode 100644 index 0000000..6d2379f --- /dev/null +++ b/AltairZ80/mfdc.c @@ -0,0 +1,682 @@ +/************************************************************************* + * * + * $Id: mfdc.c 1773 2008-01-11 05:46:19Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Micropolis FD Control module for SIMH. * + * See the "Vector Using MDOS Revision 8.4" manual at: * + * www.hartetechnologies.com/manuals in the Vector Graphic section * + * for details of the on-disk sector format and programming information. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ +#define USE_VGI /* Use 275-byte VGI-format sectors (includes all metadata) */ + +#include "altairz80_defs.h" +#include "sim_imd.h" + +#if defined (_WIN32) +#include +#endif + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define ORDERS_MSG 0x02 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 + +extern uint32 PCX; +extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern int32 find_unit_index(UNIT *uptr); + +static void MFDC_Command(uint8 cData); + +#define MFDC_MAX_DRIVES 4 +#define JUMPER_W9 1 /* Not Installed (0) = 2MHz, Installed (1) = 4MHz. */ +#define JUMPER_W10 0 + +#define MFDC_SECTOR_LEN 275 + +typedef union { + struct { + uint8 sync; + uint8 header[2]; + uint8 unused[10]; + uint8 data[256]; + uint8 checksum; + uint8 ecc[4]; + uint8 ecc_valid; /* Not used for Micropolis FDC, but is used by FDHD. */ + } u; + uint8 raw[MFDC_SECTOR_LEN]; + +} SECTOR_FORMAT; + +typedef struct { + UNIT *uptr; + DISK_INFO *imd; + uint8 track; + uint8 wp; /* Disk write protected */ + uint8 ready; /* Drive is ready */ + uint8 sector; /* Current Sector number */ + uint32 sector_wait_count; +} MFDC_DRIVE_INFO; + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint8 xfr_flag; /* Indicates controller is ready to send/receive data */ + uint8 sel_drive; /* Currently selected drive */ + uint8 selected; /* 1 if drive is selected */ + uint8 track0; /* Set it selected drive is on track 0 */ + uint8 head; /* Currently selected head */ + uint8 wr_latch; /* Write enable latch */ + uint8 int_enable; /* Interrupt Enable */ + uint32 datacount; /* Number of data bytes transferred from controller for current sector */ + uint8 read_in_progress; /* TRUE if a read is in progress */ + MFDC_DRIVE_INFO drive[MFDC_MAX_DRIVES]; +} MFDC_INFO; + +static MFDC_INFO mfdc_info_data = { { 0xF800, 1024, 0, 0 } }; +static MFDC_INFO *mfdc_info = &mfdc_info_data; + +static SECTOR_FORMAT sdata; + +#define UNIT_V_MFDC_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_MFDC_WLK (1 << UNIT_V_MFDC_WLK) +#define UNIT_V_MFDC_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_MFDC_VERBOSE (1 << UNIT_V_MFDC_VERBOSE) +#define MFDC_CAPACITY (77*16*MFDC_SECTOR_LEN) /* Default Micropolis Disk Capacity */ +#define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ +#define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ +#define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ + +static t_stat mfdc_reset(DEVICE *mfdc_dev); +static t_stat mfdc_attach(UNIT *uptr, char *cptr); +static t_stat mfdc_detach(UNIT *uptr); +static uint8 MFDC_Read(const uint32 Addr); +static uint8 MFDC_Write(const uint32 Addr, uint8 cData); + +static int32 mdskdev(const int32 Addr, const int32 rw, const int32 data); + +static int32 trace_level = 0; + +static UNIT mfdc_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MFDC_CAPACITY) } +}; + +static REG mfdc_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { NULL } +}; + +static MTAB mfdc_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, + { UNIT_MFDC_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_MFDC_WLK, UNIT_MFDC_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_MFDC_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_MFDC_VERBOSE, UNIT_MFDC_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE mfdc_dev = { + "MDSK", mfdc_unit, mfdc_reg, mfdc_mod, + MFDC_MAX_DRIVES, 10, 31, 1, MFDC_MAX_DRIVES, MFDC_MAX_DRIVES, + NULL, NULL, &mfdc_reset, + NULL, &mfdc_attach, &mfdc_detach, + &mfdc_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Micropolis FD Control Boot ROM + * This ROM code is runtime-relocatable. See Appendix F of the "Vector Using MDOS Revision 8.4" + * manual at www.hartetechnologies.com/manuals in the Vector Graphic section. + */ +static uint8 mfdc_rom[256] = { + 0xF3, 0x21, 0xA2, 0x00, 0xF9, 0x36, 0xC9, 0xCD, 0xA2, 0x00, 0xEB, 0x2A, 0xA0, 0x00, 0x2E, 0x00, /* 0x00 */ + 0xE5, 0x01, 0x1D, 0x00, 0x09, 0xE5, 0xE1, 0x0E, 0x1A, 0x09, 0x06, 0xBD, 0xEB, 0x3B, 0x3B, 0x1A, /* 0x10 */ + 0x77, 0xBE, 0xC0, 0x23, 0x13, 0x05, 0xC0, 0xE1, 0x2A, 0xA0, 0x00, 0x11, 0x00, 0x02, 0x19, 0x22, /* 0x20 */ + 0xA2, 0x00, 0x36, 0xA0, 0xC3, 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2A, 0xA2, 0x00, 0x7E, 0xE6, /* 0x30 */ + 0x80, 0xCA, 0xA9, 0x00, 0x7E, 0xE6, 0x0F, 0xA8, 0xC2, 0xA9, 0x00, 0x23, 0xB6, 0xF2, 0xB7, 0x00, /* 0x40 */ + 0x23, 0x7E, 0xAF, 0xEB, 0x06, 0x00, 0x00, 0x00, 0x1A, 0x77, 0x23, 0x88, 0x47, 0x1A, 0x77, 0x23, /* 0x50 */ + 0x88, 0x47, 0x0D, 0xC2, 0xC3, 0x00, 0x1A, 0xB8, 0xC9, 0x2A, 0xA2, 0x00, 0x36, 0x20, 0x23, 0x7E, /* 0x60 */ + 0x2B, 0xE6, 0x24, 0xEE, 0x20, 0xC2, 0xD4, 0x00, 0x0E, 0x5E, 0xCD, 0x49, 0x01, 0x23, 0x7E, 0x2B, /* 0x70 */ + 0xE6, 0x24, 0xEE, 0x20, 0xC2, 0xD4, 0x00, 0x23, 0x7E, 0xE6, 0x08, 0x2B, 0xCA, 0x07, 0x01, 0x06, /* 0x80 */ + 0x08, 0x36, 0x61, 0x0E, 0x0F, 0xCD, 0x49, 0x01, 0x05, 0xC2, 0xFC, 0x00, 0x23, 0x7E, 0xE6, 0x08, /* 0x90 */ + 0x2B, 0xC2, 0x19, 0x01, 0x36, 0x60, 0x0E, 0x0F, 0xCD, 0x49, 0x01, 0xC3, 0x07, 0x01, 0x21, 0x5F, /* 0xA0 */ + 0x01, 0xCD, 0x37, 0x01, 0xC2, 0xD4, 0x00, 0x2A, 0x69, 0x02, 0x22, 0xA4, 0x00, 0xCD, 0x37, 0x01, /* 0xB0 */ + 0xC2, 0xD4, 0x00, 0x2A, 0xA4, 0x00, 0x11, 0x0C, 0x00, 0x19, 0xD1, 0xE9, 0xE5, 0xEB, 0x01, 0x86, /* 0xC0 */ + 0x00, 0xCD, 0xA6, 0x00, 0xE1, 0xC2, 0x37, 0x01, 0xE5, 0x7E, 0x23, 0xB6, 0xE1, 0xC9, 0x7E, 0xE6, /* 0xD0 */ + 0x20, 0x79, 0xC2, 0x51, 0x01, 0x07, 0x4F, 0x3E, 0xFF, 0xD6, 0x01, 0xB7, 0xC2, 0x54, 0x01, 0x0D, /* 0xE0 */ + 0xC2, 0x52, 0x01, 0xC9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC3, 0xA6, 0x00 /* 0xF0 */ +}; + +/* Reset routine */ +t_stat mfdc_reset(DEVICE *dptr) +{ + uint8 i; + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { + sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &mdskdev, TRUE); + } else { + /* Connect MFDC at base address */ + for(i = 0; i < MFDC_MAX_DRIVES; i++) { + mfdc_info->drive[i].uptr = &mfdc_dev.units[i]; + } + if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &mdskdev, FALSE) != 0) { + printf("%s: error mapping resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); + dptr->flags |= DEV_DIS; + return SCPE_ARG; + } + } + return SCPE_OK; +} + +/* Attach routine */ +t_stat mfdc_attach(UNIT *uptr, char *cptr) +{ + char header[4]; + t_stat r; + unsigned int i = 0; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + if(sim_fsize(uptr->fileref) != 0) { + uptr->capac = sim_fsize(uptr->fileref); + } else { + uptr->capac = MFDC_CAPACITY; + } + + i = find_unit_index(uptr); + + /* Default for new file is DSK */ + uptr->u3 = IMAGE_TYPE_DSK; + + if(uptr->capac > 0) { + fgets(header, 4, uptr->fileref); + if(!strcmp(header, "IMD")) { + uptr->u3 = IMAGE_TYPE_IMD; + } else if(!strcmp(header, "CPT")) { + printf("CPT images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_CPT; + mfdc_detach(uptr); + return SCPE_OPENERR; + } else { + uptr->u3 = IMAGE_TYPE_DSK; + } + } + + if (uptr->flags & UNIT_MFDC_VERBOSE) + printf("MDSK%d, attached to '%s', type=%s, len=%d\n", i, cptr, + uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + + if(uptr->u3 == IMAGE_TYPE_IMD) { + if(uptr->capac < 318000) { + printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); + mfdc_detach(uptr); + return SCPE_OPENERR; + } + + if (uptr->flags & UNIT_MFDC_VERBOSE) + printf("--------------------------------------------------------\n"); + mfdc_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_MFDC_VERBOSE)); + if (uptr->flags & UNIT_MFDC_VERBOSE) + printf("\n"); + } else { + mfdc_info->drive[i].imd = NULL; + } + + return SCPE_OK; +} + + +/* Detach routine */ +t_stat mfdc_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + for(i = 0; i < MFDC_MAX_DRIVES; i++) { + if(mfdc_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + if (i >= MFDC_MAX_DRIVES) return SCPE_ARG; + + DBG_PRINT(("Detach MFDC%d\n", i)); + diskClose(mfdc_info->drive[i].imd); + + r = detach_unit(uptr); /* detach unit */ + if ( r != SCPE_OK) + return r; + + return SCPE_OK; +} + + + +static uint8 cy; +static uint8 adc(uint8 sum, uint8 a1) +{ + uint32 total; + + total = sum + a1 + cy; + + if(total > 0xFF) { + cy = 1; + } else { + cy = 0; + } + + return(total & 0xFF); +} + +/* Main Entry Point for Memory-Mapped I/O to the Micropolis FD Control Board + * + * The controller is typically located at 0xF800 in the Memory Map, and occupies + * 1K of address space. Accesses are broken down as follows: + * + * 0xF800-0xF8FF: Bootstrap ROM + * 0xF900-0xF9FF: Nothing (reads 0xFF) + * 0xFA00-0xFBFF: Controller registers: there are four registers, which are shadowed + * throughout this 512-byte range. + * + * The controller can be relocated on any 1K boundary in the memory map, and since the + * boot ROM code is runtime relocatable, it moves with the controller registers. + */ +static int32 mdskdev(const int32 Addr, const int32 rw, const int32 data) +{ + switch(Addr & 0x300) { + case 0x000: /* Boot ROM */ + if(rw == 0) { /* Read boot ROM */ + return(mfdc_rom[Addr & 0xFF]); + } else { + printf("MFDC: Attempt to write to boot ROM." NLP); + return (-1); + } + break; + case 0x100: /* Nothing */ + return(0xFF); + break; + case 0x200: + case 0x300: /* Controller Registers */ + if(rw == 0) { /* Read Register */ + return(MFDC_Read(Addr)); + } else { /* Write Register */ + return(MFDC_Write(Addr, data)); + } + break; + } + + return(-1); +} + + +static uint8 MFDC_Read(const uint32 Addr) +{ + uint8 cData; + MFDC_DRIVE_INFO *pDrive; + + cData = 0x00; + + pDrive = &mfdc_info->drive[mfdc_info->sel_drive]; + + switch(Addr & 0x3) { + case 0: + if(mfdc_info->read_in_progress == FALSE) { + pDrive->sector_wait_count++; + if(pDrive->sector_wait_count > 10) { + pDrive->sector++; + pDrive->sector &= 0x0F; /* Max of 16 sectors */ + mfdc_info->wr_latch = 0; /* on new sector, disable the write latch */ + DBG_PRINT(("Head over sector %d" NLP, pDrive->sector)); + pDrive->sector_wait_count = 0; + } + } + + cData = (pDrive->sector) & 0xF; /* [3:0] current sector */ + cData |= (JUMPER_W10 << 4); + cData |= ((~JUMPER_W9) & 1) << 5; + cData |= (0 << 6); /* Sector Interrupt Flag, reset by RESET command or Interrupt Disable */ + cData |= (1 << 7); /* Sector Flag */ + mfdc_info->xfr_flag = 1; /* Drive has data */ + mfdc_info->datacount = 0; + TRACE_PRINT(STATUS_MSG, ("MFDC: " ADDRESS_FORMAT " RD Sector Register = 0x%02x" NLP, PCX, cData)); + break; + case 1: + cData = (mfdc_info->sel_drive & 0x3); /* [1:0] selected drive */ + cData |= (!mfdc_info->selected << 2); /* [2] drive is selected */ + cData |= (pDrive->track == 0) ? 0x08 : 0; /* [3] TK0 */ + pDrive->wp = ((pDrive->uptr)->flags & UNIT_MFDC_WLK) ? 1 : 0; + cData |= (pDrive->wp << 4); /* [4] Write Protect */ + cData |= (pDrive->ready << 5); /* [5] Drive Ready */ + cData |= (0 << 6); /* [6] PINTE from S-100 Bus */ + cData |= (mfdc_info->xfr_flag << 7); /* [7] Transfer Flag */ + + TRACE_PRINT(STATUS_MSG, ("MFDC: " ADDRESS_FORMAT " RD Status = 0x%02x" NLP, PCX, cData)); + break; + case 2: + case 3: + if(mfdc_info->datacount == 0) { + unsigned int i, checksum; + unsigned long sec_offset; + unsigned int flags; + unsigned int readlen; + + /* Clear out unused portion of sector. */ + memset(&sdata.u.unused[0], 0x00, 10); + + sdata.u.sync = 0xFF; + sdata.u.header[0] = pDrive->track; + sdata.u.header[1] = pDrive->sector; + + TRACE_PRINT(RD_DATA_MSG, ("MFDC: " ADDRESS_FORMAT " RD Data T:%d S:[%d]" NLP, + PCX, + pDrive->track, + pDrive->sector)); + +#ifdef USE_VGI + sec_offset = (pDrive->track * MFDC_SECTOR_LEN * 16) + \ + (pDrive->sector * MFDC_SECTOR_LEN); +#else + sec_offset = (pDrive->track * 4096) + \ + (pDrive->sector * 256); +#endif /* USE_VGI */ + + if (!(pDrive->uptr->flags & UNIT_ATT)) { + if (pDrive->uptr->flags & UNIT_MFDC_VERBOSE) + printf("MFDC: " ADDRESS_FORMAT " MDSK%i not attached." NLP, PCX, + mfdc_info->sel_drive); + return 0x00; + } + + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } +/* printf("%s: Read: imd=%p" NLP, __FUNCTION__, pDrive->imd); */ + sectRead(pDrive->imd, + pDrive->track, + mfdc_info->head, + pDrive->sector, + sdata.u.data, + 256, + &flags, + &readlen); + break; + case IMAGE_TYPE_DSK: + if(pDrive->uptr->fileref == NULL) { + printf(".fileref is NULL!" NLP); + } else { + fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); +#ifdef USE_VGI + fread(sdata.raw, MFDC_SECTOR_LEN, 1, (pDrive->uptr)->fileref); +#else + fread(sdata.u.data, 256, 1, (pDrive->uptr)->fileref); +#endif /* USE_VGI */ + } + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" NLP, __FUNCTION__); + break; + } + +/* printf("%d/%d @%04x Len=%04x" NLP, sdata.u.header[0], sdata.u.header[1], sdata.u.header[9]<<8|sdata.u.header[8], sdata.u.header[11]<<8|sdata.u.header[10]); */ + + adc(0,0); /* clear Carry bit */ + checksum = 0; + + /* Checksum everything except the sync byte */ + for(i=1;i<269;i++) { + checksum = adc(checksum, sdata.raw[i]); + } + + sdata.u.checksum = checksum & 0xFF; +/* DBG_PRINT(("Checksum=%x" NLP, sdata.u.checksum)); */ + mfdc_info->read_in_progress = TRUE; + } + + cData = sdata.raw[mfdc_info->datacount]; + + mfdc_info->datacount++; + if(mfdc_info->datacount == 270) { + TRACE_PRINT(RD_DATA_MSG, ("MFDC: " ADDRESS_FORMAT " Read sector [%d] complete" NLP, + PCX, pDrive->sector)); + mfdc_info->read_in_progress = FALSE; + } + +/* DBG_PRINT(("MFDC: " ADDRESS_FORMAT " RD Data Sector %d[%03d]: 0x%02x" NLP, PCX, pDrive->sector, mfdc_info->datacount, cData)); */ + break; + } + + return (cData); +} + +static uint8 MFDC_Write(const uint32 Addr, uint8 cData) +{ + unsigned int sec_offset; + unsigned int flags = 0; + unsigned int writelen; + MFDC_DRIVE_INFO *pDrive; + + pDrive = &mfdc_info->drive[mfdc_info->sel_drive]; + + switch(Addr & 0x3) { + case 0: + case 1: + MFDC_Command(cData); + break; + case 2: + case 3: +/* DBG_PRINT(("MFDC: " ADDRESS_FORMAT " WR Data" NLP, PCX)); */ + if(mfdc_info->wr_latch == 0) { + printf("MFDC: " ADDRESS_FORMAT " Error, attempt to write data when write latch is not set." NLP, PCX); + } else { +#ifdef USE_VGI + sec_offset = (pDrive->track * MFDC_SECTOR_LEN * 16) + \ + (pDrive->sector * MFDC_SECTOR_LEN); + + sdata.raw[mfdc_info->datacount] = cData; +#else + int data_index = mfdc_info->datacount - 13; + + sec_offset = (pDrive->track * 4096) + \ + (pDrive->sector * 256); + + if((data_index >= 0) && (data_index < 256)) { + DBG_PRINT(("writing data [%03d]=%02x" NLP, data_index, cData)); + + sdata.u.data[data_index] = cData; + + } + +#endif /* USE_VGI */ + + mfdc_info->datacount ++; + + if(mfdc_info->datacount == 270) { + TRACE_PRINT(WR_DATA_MSG, ("MFDC: " ADDRESS_FORMAT " WR Data T:%d S:[%d]" NLP, + PCX, + pDrive->track, + pDrive->sector)); + + if (!(pDrive->uptr->flags & UNIT_ATT)) { + if (pDrive->uptr->flags & UNIT_MFDC_VERBOSE) + printf("MFDC: " ADDRESS_FORMAT " MDSK%i not attached." NLP, PCX, + mfdc_info->sel_drive); + return 0x00; + } + + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } + sectWrite(pDrive->imd, + pDrive->track, + mfdc_info->head, + pDrive->sector, + sdata.u.data, + 256, + &flags, + &writelen); + break; + case IMAGE_TYPE_DSK: + if(pDrive->uptr->fileref == NULL) { + printf(".fileref is NULL!" NLP); + } else { + fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); +#ifdef USE_VGI + fwrite(sdata.raw, MFDC_SECTOR_LEN, 1, (pDrive->uptr)->fileref); +#else + fwrite(sdata.u.data, 256, 1, (pDrive->uptr)->fileref); +#endif /* USE_VGI */ + } + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" NLP, __FUNCTION__); + break; + } + } + } + break; + } + + cData = 0x00; + + return (cData); +} + +#define MFDC_CMD_NOP 0 +#define MFDC_CMD_SELECT 1 +#define MFDC_CMD_INTR 2 +#define MFDC_CMD_STEP 3 +#define MFDC_CMD_SET_WRITE 4 +#define MFDC_CMD_RESET 5 + +static void MFDC_Command(uint8 cData) +{ + uint8 cCommand; + uint8 cModifier; + MFDC_DRIVE_INFO *pDrive; + + pDrive = &mfdc_info->drive[mfdc_info->sel_drive]; + + + cCommand = cData >> 5; + cModifier = cData & 0x1F; + + switch(cCommand) { + case MFDC_CMD_NOP: + TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " No Op." NLP, PCX)); + break; + case MFDC_CMD_SELECT: + mfdc_info->sel_drive = cModifier & 3; + mfdc_info->head = (cModifier & 0x10) >> 4; + mfdc_info->selected = TRUE; + + if(pDrive->uptr->fileref != NULL) { + pDrive->ready = 1; + } else { + pDrive->ready = 0; + } + + TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Select Drive: %d, Head: %s" NLP, + PCX, mfdc_info->sel_drive, (mfdc_info->head) ? "Upper" : "Lower")); + break; + case MFDC_CMD_INTR: + mfdc_info->int_enable = cModifier & 1; /* 0=int disable, 1=enable */ + TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Interrupts %s." NLP, + PCX, mfdc_info->int_enable ? "Enabled" : "Disabled")); + break; + case MFDC_CMD_STEP: + if(cModifier & 1) { /* Step IN */ + pDrive->track++; + } + else { /* Step OUT */ + if(pDrive->track != 0) { + pDrive->track--; + } + } + + TRACE_PRINT(SEEK_MSG, ("MFDC: " ADDRESS_FORMAT " Step %s, Track=%d." NLP, + PCX, (cModifier & 1) ? "IN" : "OUT", pDrive->track)); + + break; + case MFDC_CMD_SET_WRITE: + TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Set WRITE." NLP, PCX)); + mfdc_info->wr_latch = 1; /* Allow writes for the current sector */ + mfdc_info->datacount = 0; /* reset the byte counter */ + break; + case MFDC_CMD_RESET: + TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Reset Controller." NLP, PCX)); + mfdc_info->selected = 0; /* de-select the drive */ + mfdc_info->wr_latch = 0; /* Disable the write latch */ + mfdc_info->datacount = 0; /* reset the byte counter */ + break; + default: + TRACE_PRINT(CMD_MSG, ("MFDC: " ADDRESS_FORMAT " Unsupported command." NLP, PCX)); + break; + } +} diff --git a/AltairZ80/mfdc.h b/AltairZ80/mfdc.h new file mode 100644 index 0000000..991e1c8 --- /dev/null +++ b/AltairZ80/mfdc.h @@ -0,0 +1,46 @@ +/************************************************************************* + * * + * $Id: mfdc.h 1694 2007-12-14 05:23:11Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Micropolis FDC module for SIMH definitions * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +extern uint8 MFDC_Read(const uint32 Addr); +extern uint8 MFDC_Write(const uint32 Addr, uint8 cData); + + + diff --git a/AltairZ80/n8vem.c b/AltairZ80/n8vem.c new file mode 100644 index 0000000..f9cdf07 --- /dev/null +++ b/AltairZ80/n8vem.c @@ -0,0 +1,486 @@ +/************************************************************************* + * * + * $Id: n8vem.c 1902 2008-05-11 02:40:41Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * N8VEM Single-Board Computer I/O module for SIMH. * + * http://groups.google.com/group/n8vem/web/n8vem-single-board-computer-home-page * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/* #define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define PIO_MSG 0x01 +#define UART_MSG 0x02 +#define RTC_MSG 0x04 +#define MPCL_MSG 0x08 +#define ROM_MSG 0x10 +#define TRACE_MSG 0x80 + +#define N8VEM_MAX_DRIVES 2 + +#define UNIT_V_N8VEM_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_N8VEM_VERBOSE (1 << UNIT_V_N8VEM_VERBOSE) + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint8 *ram; + uint8 *rom; + uint8 rom_attached; + uint8 uart_scr; + uint8 uart_lcr; + uint8 mpcl_ram; + uint8 mpcl_rom; +} N8VEM_INFO; + +static N8VEM_INFO n8vem_info_data = { { 0x0, 0x8000, 0x60, 32 } }; +static N8VEM_INFO *n8vem_info = &n8vem_info_data; + +extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern uint32 PCX; +extern REG *sim_PC; +extern int32 find_unit_index (UNIT *uptr); + +static t_stat n8vem_reset(DEVICE *n8vem_dev); +static t_stat n8vem_boot(int32 unitno, DEVICE *dptr); +static t_stat n8vem_attach(UNIT *uptr, char *cptr); +static t_stat n8vem_detach(UNIT *uptr); + +static uint8 N8VEM_Read(const uint32 Addr); +static uint8 N8VEM_Write(const uint32 Addr, uint8 cData); + +static int32 n8vemdev(const int32 port, const int32 io, const int32 data); +static int32 n8vem_mem(const int32 port, const int32 io, const int32 data); + +static int32 trace_level = 0x00; /* Disable all tracing by default */ +static int32 save_rom = 0x00; /* When set to 1, saves ROM back to file on disk at detach time */ +static int32 save_ram = 0x00; /* When set to 1, saves RAM back to file on disk at detach time */ +static int32 n8vem_pio1a = 0x00; /* 8255 PIO1A IN Port */ +static int32 n8vem_pio1b = 0x00; /* 8255 PIO1B OUT Port */ +static int32 n8vem_pio1c = 0x00; /* 8255 PIO1C IN Port */ +static int32 n8vem_pio1ctrl = 0x00; /* 8255 PIO1 Control Port */ + +#define N8VEM_ROM_SIZE (1024 * 1024) +#define N8VEM_RAM_SIZE (512 * 1024) + +#define N8VEM_RAM_SELECT (1 << 7) +#define N8VEM_RAM_MASK 0x0F +#define N8VEM_ROM_MASK 0x1F +#define N8VEM_ADDR_MASK 0x7FFF + +static UNIT n8vem_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, N8VEM_ROM_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, N8VEM_RAM_SIZE) } +}; + +static REG n8vem_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { HRDATA (SAVEROM, save_rom, 1), }, + { HRDATA (SAVERAM, save_ram, 1), }, + { HRDATA (PIO1A, n8vem_pio1a, 8), }, + { HRDATA (PIO1B, n8vem_pio1b, 8), }, + { HRDATA (PIO1C, n8vem_pio1c, 8), }, + { HRDATA (PIO1CTRL, n8vem_pio1ctrl, 8), }, + { NULL } +}; + +static MTAB n8vem_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + /* quiet, no warning messages */ + { UNIT_N8VEM_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_N8VEM_VERBOSE, UNIT_N8VEM_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE n8vem_dev = { + "N8VEM", n8vem_unit, n8vem_reg, n8vem_mod, + N8VEM_MAX_DRIVES, 10, 31, 1, N8VEM_MAX_DRIVES, N8VEM_MAX_DRIVES, + NULL, NULL, &n8vem_reset, + &n8vem_boot, &n8vem_attach, &n8vem_detach, + &n8vem_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat n8vem_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + TRACE_PRINT(TRACE_MSG, ("N8VEM: Reset." NLP)); + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &n8vemdev, TRUE); + sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &n8vem_mem, TRUE); + free(n8vem_info->ram); + free(n8vem_info->rom); + } else { + /* Connect N8VEM at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &n8vemdev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + /* Connect N8VEM Memory (512K RAM, 1MB FLASH) */ + if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &n8vem_mem, FALSE) != 0) { + printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); + return SCPE_ARG; + } + + n8vem_info->ram = calloc(1, (N8VEM_RAM_SIZE)); + n8vem_info->rom = calloc(1, (N8VEM_ROM_SIZE)); + + /* Clear the RAM and ROM mapping registers */ + n8vem_info->mpcl_ram = 0; + n8vem_info->mpcl_rom = 0; + } + return SCPE_OK; +} + +static t_stat n8vem_boot(int32 unitno, DEVICE *dptr) +{ + TRACE_PRINT(TRACE_MSG, ("N8VEM: Boot." NLP)); + + /* Clear the RAM and ROM mapping registers */ + n8vem_info->mpcl_ram = 0; + n8vem_info->mpcl_rom = 0; + + /* Set the PC to 0, and go. */ + *((int32 *) sim_PC->loc) = 0; + return SCPE_OK; +} + +/* Attach routine */ +static t_stat n8vem_attach(UNIT *uptr, char *cptr) +{ + t_stat r; + int32 i = 0; + + i = find_unit_index(uptr); + + if (i == -1) { + return (SCPE_IERR); + } + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + + TRACE_PRINT(TRACE_MSG, ("N8VEM: Attach %s." NLP, i == 0 ? "ROM" : "RAM")); + + if(i == 0) { /* Attaching ROM */ + n8vem_info->rom_attached = TRUE; + + /* Erase ROM */ + memset(n8vem_info->rom, 0xFF, N8VEM_ROM_SIZE); + + if(uptr->capac > 0) { + /* Only read in enough of the file to fill the ROM. */ + if(uptr->capac > N8VEM_ROM_SIZE) uptr->capac = N8VEM_ROM_SIZE; + + printf("Reading %d bytes into ROM.\n", uptr->capac); + fread((void *)(n8vem_info->rom), uptr->capac, 1, uptr->fileref); + } + } else { /* attaching RAM */ + /* Erase RAM */ + memset(n8vem_info->ram, 0x00, N8VEM_RAM_SIZE); + + if(uptr->capac > 0) { + /* Only read in enough of the file to fill the RAM. */ + if(uptr->capac > N8VEM_RAM_SIZE) uptr->capac = N8VEM_RAM_SIZE; + + printf("Reading %d bytes into RAM.\n", uptr->capac); + fread((void *)(n8vem_info->ram), uptr->capac, 1, uptr->fileref); + } + } + return r; +} + +/* Detach routine */ +static t_stat n8vem_detach(UNIT *uptr) +{ + t_stat r; + int32 i = 0; + + i = find_unit_index(uptr); + + if (i == -1) { + return (SCPE_IERR); + } + + TRACE_PRINT(TRACE_MSG, ("N8VEM: Detach %s." NLP, i == 0 ? "ROM" : "RAM")); + + /* rewind to the beginning of the file. */ + fseek(uptr->fileref, 0, SEEK_SET); + + if(i == 0) { /* ROM */ + /* Save the ROM back to disk if SAVEROM is set. */ + if(save_rom == 1) { + printf("Writing %d bytes into ROM image.\n", N8VEM_ROM_SIZE); + fwrite((void *)(n8vem_info->rom), N8VEM_ROM_SIZE, 1, uptr->fileref); + } + } else { /* RAM */ + /* Save the RAM back to disk if SAVERAM is set. */ + if(save_ram == 1) { + printf("Writing %d bytes into RAM image.\n", N8VEM_RAM_SIZE); + fwrite((void *)(n8vem_info->ram), N8VEM_RAM_SIZE, 1, uptr->fileref); + } + } + r = detach_unit(uptr); /* detach unit */ + + return r; +} + +/* RAM MEMORY PAGE CONFIGURATION LATCH CONTROL PORT ( IO_Y3 ) INFORMATION + * + * 7 6 5 4 3 2 1 0 ONLY APPLICABLE TO THE LOWER MEMORY PAGE $0000-$7FFF + * ^ ^ ^ ^ ^ ^ ^ ^ + * : : : : : : : :--0 = A15 RAM ADDRESS LINE DEFAULT IS 0 + * : : : : : : :----0 = A16 RAM ADDRESS LINE DEFAULT IS 0 + * : : : : : :------0 = A17 RAM ADDRESS LINE DEFAULT IS 0 + * : : : : :--------0 = A18 RAM ADDRESS LINE DEFAULT IS 0 + * : : : :-----------0 = + * : : :-------------0 = + * : :---------------0 = + * :-----------------0 = + * + * ROM MEMORY PAGE CONFIGURATION LATCH CONTROL PORT ( IO_Y3+$04 ) INFORMATION + * + * 7 6 5 4 3 2 1 0 ONLY APPLICABLE TO THE LOWER MEMORY PAGE $0000-$7FFF + * ^ ^ ^ ^ ^ ^ ^ ^ + * : : : : : : : :--0 = A15 ROM ADDRESS LINE DEFAULT IS 0 + * : : : : : : :----0 = A16 ROM ADDRESS LINE DEFAULT IS 0 + * : : : : : :------0 = A17 ROM ADDRESS LINE DEFAULT IS 0 + * : : : : :--------0 = A18 ROM ADDRESS LINE DEFAULT IS 0 + * : : : :-----------0 = A19 ROM ONLY ADDRESS LINE DEFAULT IS 0 + * : : :-------------0 = + * : :---------------0 = + * :-----------------0 = ROM SELECT (0=ROM, 1=RAM) DEFAULT IS 0 + */ + static int32 n8vem_mem(const int32 Addr, const int32 write, const int32 data) +{ +/* DBG_PRINT(("N8VEM: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ + if(write) { + if(n8vem_info->mpcl_rom & N8VEM_RAM_SELECT) + { + n8vem_info->ram[((n8vem_info->mpcl_ram & N8VEM_RAM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)] = data; + } else { + if(save_rom == 1) { + n8vem_info->rom[((n8vem_info->mpcl_rom & N8VEM_ROM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)] = data; + } else { + TRACE_PRINT(ROM_MSG, ("N8VEM: " ADDRESS_FORMAT " WR ROM[0x%05x]: Cannot write to ROM." NLP, PCX, ((n8vem_info->mpcl_rom & N8VEM_ROM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK))); + } + } + return 0; + } else { + if(n8vem_info->mpcl_rom & N8VEM_RAM_SELECT) + { + return n8vem_info->ram[((n8vem_info->mpcl_ram & N8VEM_RAM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)]; + } else { + return n8vem_info->rom[((n8vem_info->mpcl_rom & N8VEM_ROM_MASK) << 15) | (Addr & N8VEM_ADDR_MASK)]; + } + } +} + +static int32 n8vemdev(const int32 port, const int32 io, const int32 data) +{ +/* DBG_PRINT(("N8VEM: IO %s, Port %02x\n", io ? "WR" : "RD", port)); */ + if(io) { + N8VEM_Write(port, data); + return 0; + } else { + return(N8VEM_Read(port)); + } +} + +#define N8VEM_PIO1A 0x00 /* (INPUT) IN 1-8 */ +#define N8VEM_PIO1B 0x01 /* (OUTPUT) OUT TO LEDS */ +#define N8VEM_PIO1C 0x02 /* (INPUT) */ +#define N8VEM_PIO1CONT 0x03 /* CONTROL BYTE PIO 82C55 */ + +#define N8VEM_UART_DATA 0x08 +#define N8VEM_UART_RSR 0x09 +#define N8VEM_UART_INTR 0x0A +#define N8VEM_UART_LCR 0x0B +#define N8VEM_UART_MCR 0x0C +#define N8VEM_UART_LSR 0x0D +#define N8VEM_UART_MSR 0x0E +#define N8VEM_UART_SCR 0x0F + +#define N8VEM_MPCL_RAM 0x18 /* RAM Address control port */ +#define N8VEM_MPCL_RAM1 0x19 /* RAM Address control port */ +#define N8VEM_MPCL_RAM2 0x1A /* RAM Address control port */ +#define N8VEM_MPCL_RAM3 0x1B /* RAM Address control port */ +#define N8VEM_MPCL_ROM 0x1C /* ROM Address control port */ +#define N8VEM_MPCL_ROM1 0x1D /* ROM Address control port */ +#define N8VEM_MPCL_ROM2 0x1E /* ROM Address control port */ +#define N8VEM_MPCL_ROM3 0x1F /* ROM Address control port */ + +extern int32 sio0d(const int32 port, const int32 io, const int32 data); +extern int32 sio0s(const int32 port, const int32 io, const int32 data); + +static uint8 N8VEM_Read(const uint32 Addr) +{ + uint8 cData = 0xFF; + + switch(Addr & 0x1F) { + case N8VEM_PIO1A: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1A" NLP, PCX)); + cData = n8vem_pio1a; + break; + case N8VEM_PIO1B: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1B" NLP, PCX)); + cData = n8vem_pio1b; + break; + case N8VEM_PIO1C: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1C" NLP, PCX)); + cData = n8vem_pio1c; + break; + case N8VEM_PIO1CONT: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: PIO1CTRL" NLP, PCX)); + cData = n8vem_pio1ctrl; + break; + case N8VEM_UART_LCR: + cData = n8vem_info->uart_lcr; + break; + case N8VEM_UART_DATA: + case N8VEM_UART_RSR: + case N8VEM_UART_LSR: + case N8VEM_UART_INTR: + case N8VEM_UART_MCR: + case N8VEM_UART_MSR: + TRACE_PRINT(UART_MSG, ("N8VEM: " ADDRESS_FORMAT " RD[%02x]: UART not Implemented." NLP, PCX, Addr)); + break; + case N8VEM_UART_SCR: /* 16550 Scratchpad, implemented so software can detect UART is present */ + cData = n8vem_info->uart_scr; + break; + case N8VEM_MPCL_RAM: + case N8VEM_MPCL_RAM1: + case N8VEM_MPCL_RAM2: + case N8VEM_MPCL_RAM3: + TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: MPCL_RAM not Implemented." NLP, PCX)); + break; + case N8VEM_MPCL_ROM: + case N8VEM_MPCL_ROM1: + case N8VEM_MPCL_ROM2: + case N8VEM_MPCL_ROM3: + TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " RD: MPCL_ROM not Implemented." NLP, PCX)); + break; + default: + TRACE_PRINT(TRACE_MSG, ("N8VEM: " ADDRESS_FORMAT " RD[%02x]: not Implemented." NLP, PCX, Addr)); + break; + } + + return (cData); + +} + +static uint8 N8VEM_Write(const uint32 Addr, uint8 cData) +{ + + switch(Addr & 0x1F) { + case N8VEM_PIO1A: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1A=0x%02x" NLP, PCX, cData)); + n8vem_pio1a = cData; + break; + case N8VEM_PIO1B: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1B=0x%02x" NLP, PCX, cData)); + n8vem_pio1b = cData; + break; + case N8VEM_PIO1C: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1C=0x%02x" NLP, PCX, cData)); + n8vem_pio1c = cData; + break; + case N8VEM_PIO1CONT: + TRACE_PRINT(PIO_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: PIO1_CTRL=0x%02x" NLP, PCX, cData)); + n8vem_pio1ctrl = cData; + break; + case N8VEM_UART_LCR: + TRACE_PRINT(UART_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: UART LCR=%02x." NLP, PCX, cData)); + n8vem_info->uart_lcr = cData; + break; + case N8VEM_UART_DATA: + case N8VEM_UART_RSR: + case N8VEM_UART_INTR: + case N8VEM_UART_MCR: + case N8VEM_UART_LSR: + case N8VEM_UART_MSR: + TRACE_PRINT(UART_MSG, ("N8VEM: " ADDRESS_FORMAT " WR[%02x]: UART not Implemented." NLP, PCX, Addr)); + break; + case N8VEM_UART_SCR: /* 16550 Scratchpad, implemented so software can detect UART is present */ + n8vem_info->uart_scr = cData; + break; + case N8VEM_MPCL_RAM: + case N8VEM_MPCL_RAM1: + case N8VEM_MPCL_RAM2: + case N8VEM_MPCL_RAM3: + TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: MPCL_RAM=0x%02x" NLP, PCX, cData)); + n8vem_info->mpcl_ram = cData; + break; + case N8VEM_MPCL_ROM: + case N8VEM_MPCL_ROM1: + case N8VEM_MPCL_ROM2: + case N8VEM_MPCL_ROM3: + TRACE_PRINT(MPCL_MSG, ("N8VEM: " ADDRESS_FORMAT " WR: MPCL_ROM=0x%02x" NLP, PCX, cData)); + n8vem_info->mpcl_rom = cData; + break; + default: + TRACE_PRINT(TRACE_MSG, ("N8VEM: " ADDRESS_FORMAT " WR[0x%02x]=0x%02x: not Implemented." NLP, PCX, Addr, cData)); + break; + } + + return(0); +} + diff --git a/AltairZ80/nasm.h b/AltairZ80/nasm.h new file mode 100644 index 0000000..87c2b38 --- /dev/null +++ b/AltairZ80/nasm.h @@ -0,0 +1,941 @@ +/* nasm.h main header file for the Netwide Assembler: inter-module interface + * + * The Netwide Assembler is copyright (C) 1996 Simon Tatham and + * Julian Hall. All rights reserved. The software is + * redistributable under the licence given in the file "Licence" + * distributed in the NASM archive. + * + * initial version: 27/iii/95 by Simon Tatham + */ + +#ifndef NASM_NASM_H +#define NASM_NASM_H + +#include +#define NASM_VERSION_H +#define NASM_MAJOR_VER 0 +#define NASM_MINOR_VER 98 +#define NASM_SUBMINOR_VER 38 +#define NASM_PATCHLEVEL_VER 0 +#define NASM_VERSION_ID 0x00622600 +#define NASM_VER "0.98.38" + +#ifndef NULL +#define NULL 0 +#endif + +#ifndef FALSE +#define FALSE 0 /* comes in handy */ +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#define NO_SEG -1L /* null segment value */ +#define SEG_ABS 0x40000000L /* mask for far-absolute segments */ + +#ifndef FILENAME_MAX +#define FILENAME_MAX 256 +#endif + +#ifndef PREFIX_MAX +#define PREFIX_MAX 10 +#endif + +#ifndef POSTFIX_MAX +#define POSTFIX_MAX 10 +#endif + +#define IDLEN_MAX 4096 + +/* + * Name pollution problems: on Digital UNIX pulls in some + * strange hardware header file which sees fit to define R_SP. We + * undefine it here so as not to break the enum below. + */ +#ifdef R_SP +#undef R_SP +#endif + +/* + * We must declare the existence of this structure type up here, + * since we have to reference it before we define it... + */ +struct ofmt; + +/* + * ------------------------- + * Error reporting functions + * ------------------------- + */ + +/* + * An error reporting function should look like this. + */ +typedef void (*efunc) (int severity, const char *fmt, ...); + +/* + * These are the error severity codes which get passed as the first + * argument to an efunc. + */ + +#define ERR_DEBUG 0x00000008 /* put out debugging message */ +#define ERR_WARNING 0x00000000 /* warn only: no further action */ +#define ERR_NONFATAL 0x00000001 /* terminate assembly after phase */ +#define ERR_FATAL 0x00000002 /* instantly fatal: exit with error */ +#define ERR_PANIC 0x00000003 /* internal error: panic instantly + * and dump core for reference */ +#define ERR_MASK 0x0000000F /* mask off the above codes */ +#define ERR_NOFILE 0x00000010 /* don't give source file name/line */ +#define ERR_USAGE 0x00000020 /* print a usage message */ +#define ERR_PASS1 0x00000040 /* only print this error on pass one */ + +/* + * These codes define specific types of suppressible warning. + */ + +#define ERR_WARN_MASK 0x0000FF00 /* the mask for this feature */ +#define ERR_WARN_SHR 8 /* how far to shift right */ + +#define ERR_WARN_MNP 0x00000100 /* macro-num-parameters warning */ +#define ERR_WARN_MSR 0x00000200 /* macro self-reference */ +#define ERR_WARN_OL 0x00000300 /* orphan label (no colon, and + * alone on line) */ +#define ERR_WARN_NOV 0x00000400 /* numeric overflow */ +#define ERR_WARN_GNUELF 0x00000500 /* using GNU ELF extensions */ +#define ERR_WARN_MAX 5 /* the highest numbered one */ + +/* + * ----------------------- + * Other function typedefs + * ----------------------- + */ + +/* + * A label-lookup function should look like this. + */ +typedef int (*lfunc) (char *label, long *segment, long *offset); + +/* + * And a label-definition function like this. The boolean parameter + * `is_norm' states whether the label is a `normal' label (which + * should affect the local-label system), or something odder like + * an EQU or a segment-base symbol, which shouldn't. + */ +typedef void (*ldfunc) (char *label, long segment, long offset, char *special, + int is_norm, int isextrn, struct ofmt *ofmt, + efunc error); + +/* + * List-file generators should look like this: + */ +typedef struct { + /* + * Called to initialise the listing file generator. Before this + * is called, the other routines will silently do nothing when + * called. The `char *' parameter is the file name to write the + * listing to. + */ + void (*init) (char *, efunc); + + /* + * Called to clear stuff up and close the listing file. + */ + void (*cleanup) (void); + + /* + * Called to output binary data. Parameters are: the offset; + * the data; the data type. Data types are similar to the + * output-format interface, only OUT_ADDRESS will _always_ be + * displayed as if it's relocatable, so ensure that any non- + * relocatable address has been converted to OUT_RAWDATA by + * then. Note that OUT_RAWDATA+0 is a valid data type, and is a + * dummy call used to give the listing generator an offset to + * work with when doing things like uplevel(LIST_TIMES) or + * uplevel(LIST_INCBIN). + */ + void (*output) (long, const void *, unsigned long); + + /* + * Called to send a text line to the listing generator. The + * `int' parameter is LIST_READ or LIST_MACRO depending on + * whether the line came directly from an input file or is the + * result of a multi-line macro expansion. + */ + void (*line) (int, char *); + + /* + * Called to change one of the various levelled mechanisms in + * the listing generator. LIST_INCLUDE and LIST_MACRO can be + * used to increase the nesting level of include files and + * macro expansions; LIST_TIMES and LIST_INCBIN switch on the + * two binary-output-suppression mechanisms for large-scale + * pseudo-instructions. + * + * LIST_MACRO_NOLIST is synonymous with LIST_MACRO except that + * it indicates the beginning of the expansion of a `nolist' + * macro, so anything under that level won't be expanded unless + * it includes another file. + */ + void (*uplevel) (int); + + /* + * Reverse the effects of uplevel. + */ + void (*downlevel) (int); +} ListGen; + +/* + * The expression evaluator must be passed a scanner function; a + * standard scanner is provided as part of nasmlib.c. The + * preprocessor will use a different one. Scanners, and the + * token-value structures they return, look like this. + * + * The return value from the scanner is always a copy of the + * `t_type' field in the structure. + */ +struct tokenval { + int t_type; + long t_integer, t_inttwo; + char *t_charptr; +}; +typedef int (*scanner) (void *private_data, struct tokenval *tv); + +/* + * Token types returned by the scanner, in addition to ordinary + * ASCII character values, and zero for end-of-string. + */ +enum { /* token types, other than chars */ + TOKEN_INVALID = -1, /* a placeholder value */ + TOKEN_EOS = 0, /* end of string */ + TOKEN_EQ = '=', TOKEN_GT = '>', TOKEN_LT = '<', /* aliases */ + TOKEN_ID = 256, TOKEN_NUM, TOKEN_REG, TOKEN_INSN, /* major token types */ + TOKEN_ERRNUM, /* numeric constant with error in */ + TOKEN_HERE, TOKEN_BASE, /* $ and $$ */ + TOKEN_SPECIAL, /* BYTE, WORD, DWORD, FAR, NEAR, etc */ + TOKEN_PREFIX, /* A32, O16, LOCK, REPNZ, TIMES, etc */ + TOKEN_SHL, TOKEN_SHR, /* << and >> */ + TOKEN_SDIV, TOKEN_SMOD, /* // and %% */ + TOKEN_GE, TOKEN_LE, TOKEN_NE, /* >=, <= and <> (!= is same as <>) */ + TOKEN_DBL_AND, TOKEN_DBL_OR, TOKEN_DBL_XOR, /* &&, || and ^^ */ + TOKEN_SEG, TOKEN_WRT, /* SEG and WRT */ + TOKEN_FLOAT /* floating-point constant */ +}; + +typedef struct { + long segment; + long offset; + int known; +} loc_t; + +/* + * Expression-evaluator datatype. Expressions, within the + * evaluator, are stored as an array of these beasts, terminated by + * a record with type==0. Mostly, it's a vector type: each type + * denotes some kind of a component, and the value denotes the + * multiple of that component present in the expression. The + * exception is the WRT type, whose `value' field denotes the + * segment to which the expression is relative. These segments will + * be segment-base types, i.e. either odd segment values or SEG_ABS + * types. So it is still valid to assume that anything with a + * `value' field of zero is insignificant. + */ +typedef struct { + long type; /* a register, or EXPR_xxx */ + long value; /* must be >= 32 bits */ +} expr; + +/* + * The evaluator can also return hints about which of two registers + * used in an expression should be the base register. See also the + * `operand' structure. + */ +struct eval_hints { + int base; + int type; +}; + +/* + * The actual expression evaluator function looks like this. When + * called, it expects the first token of its expression to already + * be in `*tv'; if it is not, set tv->t_type to TOKEN_INVALID and + * it will start by calling the scanner. + * + * If a forward reference happens during evaluation, the evaluator + * must set `*fwref' to TRUE if `fwref' is non-NULL. + * + * `critical' is non-zero if the expression may not contain forward + * references. The evaluator will report its own error if this + * occurs; if `critical' is 1, the error will be "symbol not + * defined before use", whereas if `critical' is 2, the error will + * be "symbol undefined". + * + * If `critical' has bit 8 set (in addition to its main value: 0x101 + * and 0x102 correspond to 1 and 2) then an extended expression + * syntax is recognised, in which relational operators such as =, < + * and >= are accepted, as well as low-precedence logical operators + * &&, ^^ and ||. + * + * If `hints' is non-NULL, it gets filled in with some hints as to + * the base register in complex effective addresses. + */ +#define CRITICAL 0x100 +typedef expr *(*evalfunc) (scanner sc, void *scprivate, struct tokenval *tv, + int *fwref, int critical, efunc error, + struct eval_hints *hints); + +/* + * Special values for expr->type. ASSUMPTION MADE HERE: the number + * of distinct register names (i.e. possible "type" fields for an + * expr structure) does not exceed 124 (EXPR_REG_START through + * EXPR_REG_END). + */ +#define EXPR_REG_START 1 +#define EXPR_REG_END 124 +#define EXPR_UNKNOWN 125L /* for forward references */ +#define EXPR_SIMPLE 126L +#define EXPR_WRT 127L +#define EXPR_SEGBASE 128L + +/* + * Preprocessors ought to look like this: + */ +typedef struct { + /* + * Called at the start of a pass; given a file name, the number + * of the pass, an error reporting function, an evaluator + * function, and a listing generator to talk to. + */ + void (*reset) (char *, int, efunc, evalfunc, ListGen *); + + /* + * Called to fetch a line of preprocessed source. The line + * returned has been malloc'ed, and so should be freed after + * use. + */ + char *(*getline) (void); + + /* + * Called at the end of a pass. + */ + void (*cleanup) (int); +} Preproc; + +/* + * ---------------------------------------------------------------- + * Some lexical properties of the NASM source language, included + * here because they are shared between the parser and preprocessor + * ---------------------------------------------------------------- + */ + +/* + * isidstart matches any character that may start an identifier, and isidchar + * matches any character that may appear at places other than the start of an + * identifier. E.g. a period may only appear at the start of an identifier + * (for local labels), whereas a number may appear anywhere *but* at the + * start. + */ + +#define isidstart(c) ( isalpha(c) || (c)=='_' || (c)=='.' || (c)=='?' \ + || (c)=='@' ) +#define isidchar(c) ( isidstart(c) || isdigit(c) || (c)=='$' || (c)=='#' \ + || (c)=='~' ) + +/* Ditto for numeric constants. */ + +#define isnumstart(c) ( isdigit(c) || (c)=='$' ) +#define isnumchar(c) ( isalnum(c) ) + +/* This returns the numeric value of a given 'digit'. */ + +#define numvalue(c) ((c)>='a' ? (c)-'a'+10 : (c)>='A' ? (c)-'A'+10 : (c)-'0') + +/* + * Data-type flags that get passed to listing-file routines. + */ +enum { + LIST_READ, LIST_MACRO, LIST_MACRO_NOLIST, LIST_INCLUDE, + LIST_INCBIN, LIST_TIMES +}; + +/* + * ----------------------------------------------------------- + * Format of the `insn' structure returned from `parser.c' and + * passed into `assemble.c' + * ----------------------------------------------------------- + */ + +/* + * Here we define the operand types. These are implemented as bit + * masks, since some are subsets of others; e.g. AX in a MOV + * instruction is a special operand type, whereas AX in other + * contexts is just another 16-bit register. (Also, consider CL in + * shift instructions, DX in OUT, etc.) + */ + +/* size, and other attributes, of the operand */ +#define BITS8 0x00000001L +#define BITS16 0x00000002L +#define BITS32 0x00000004L +#define BITS64 0x00000008L /* FPU only */ +#define BITS80 0x00000010L /* FPU only */ +#define FAR 0x00000020L /* grotty: this means 16:16 or */ + /* 16:32, like in CALL/JMP */ +#define NEAR 0x00000040L +#define SHORT 0x00000080L /* and this means what it says :) */ + +#define SIZE_MASK 0x000000FFL /* all the size attributes */ +#define NON_SIZE (~SIZE_MASK) + +#define TO 0x00000100L /* reverse effect in FADD, FSUB &c */ +#define COLON 0x00000200L /* operand is followed by a colon */ +#define STRICT 0x00000400L /* do not optimize this operand */ + +/* type of operand: memory reference, register, etc. */ +#define MEMORY 0x00204000L +#define REGISTER 0x00001000L /* register number in 'basereg' */ +#define IMMEDIATE 0x00002000L + +#define REGMEM 0x00200000L /* for r/m, ie EA, operands */ +#define REGNORM 0x00201000L /* 'normal' reg, qualifies as EA */ +#define REG8 0x00201001L +#define REG16 0x00201002L +#define REG32 0x00201004L +#define MMXREG 0x00201008L /* MMX registers */ +#define XMMREG 0x00201010L /* XMM Katmai reg */ +#define FPUREG 0x01000000L /* floating point stack registers */ +#define FPU0 0x01000800L /* FPU stack register zero */ + +/* special register operands: these may be treated differently */ +#define REG_SMASK 0x00070000L /* a mask for the following */ +#define REG_ACCUM 0x00211000L /* accumulator: AL, AX or EAX */ +#define REG_AL 0x00211001L /* REG_ACCUM | BITSxx */ +#define REG_AX 0x00211002L /* ditto */ +#define REG_EAX 0x00211004L /* and again */ +#define REG_COUNT 0x00221000L /* counter: CL, CX or ECX */ +#define REG_CL 0x00221001L /* REG_COUNT | BITSxx */ +#define REG_CX 0x00221002L /* ditto */ +#define REG_ECX 0x00221004L /* another one */ +#define REG_DL 0x00241001L +#define REG_DX 0x00241002L +#define REG_EDX 0x00241004L +#define REG_SREG 0x00081002L /* any segment register */ +#define REG_CS 0x01081002L /* CS */ +#define REG_DESS 0x02081002L /* DS, ES, SS (non-CS 86 registers) */ +#define REG_FSGS 0x04081002L /* FS, GS (386 extended registers) */ +#define REG_SEG67 0x08081002L /* Non-implemented segment registers */ +#define REG_CDT 0x00101004L /* CRn, DRn and TRn */ +#define REG_CREG 0x08101004L /* CRn */ +#define REG_DREG 0x10101004L /* DRn */ +#define REG_TREG 0x20101004L /* TRn */ + +/* special type of EA */ +#define MEM_OFFS 0x00604000L /* simple [address] offset */ + +/* special type of immediate operand */ +#define ONENESS 0x00800000L /* so UNITY == IMMEDIATE | ONENESS */ +#define UNITY 0x00802000L /* for shift/rotate instructions */ +#define BYTENESS 0x40000000L /* so SBYTE == IMMEDIATE | BYTENESS */ +#define SBYTE 0x40002000L /* for op r16/32,immediate instrs. */ + +/* Register names automatically generated from regs.dat */ +/* automatically generated from ./regs.dat - do not edit */ +enum reg_enum { + R_AH = EXPR_REG_START, + R_AL, + R_AX, + R_BH, + R_BL, + R_BP, + R_BX, + R_CH, + R_CL, + R_CR0, + R_CR1, + R_CR2, + R_CR3, + R_CR4, + R_CR5, + R_CR6, + R_CR7, + R_CS, + R_CX, + R_DH, + R_DI, + R_DL, + R_DR0, + R_DR1, + R_DR2, + R_DR3, + R_DR4, + R_DR5, + R_DR6, + R_DR7, + R_DS, + R_DX, + R_EAX, + R_EBP, + R_EBX, + R_ECX, + R_EDI, + R_EDX, + R_ES, + R_ESI, + R_ESP, + R_FS, + R_GS, + R_MM0, + R_MM1, + R_MM2, + R_MM3, + R_MM4, + R_MM5, + R_MM6, + R_MM7, + R_SEGR6, + R_SEGR7, + R_SI, + R_SP, + R_SS, + R_ST0, + R_ST1, + R_ST2, + R_ST3, + R_ST4, + R_ST5, + R_ST6, + R_ST7, + R_TR0, + R_TR1, + R_TR2, + R_TR3, + R_TR4, + R_TR5, + R_TR6, + R_TR7, + R_XMM0, + R_XMM1, + R_XMM2, + R_XMM3, + R_XMM4, + R_XMM5, + R_XMM6, + R_XMM7, + REG_ENUM_LIMIT +}; + +enum { /* condition code names */ + C_A, C_AE, C_B, C_BE, C_C, C_E, C_G, C_GE, C_L, C_LE, C_NA, C_NAE, + C_NB, C_NBE, C_NC, C_NE, C_NG, C_NGE, C_NL, C_NLE, C_NO, C_NP, + C_NS, C_NZ, C_O, C_P, C_PE, C_PO, C_S, C_Z +}; + +/* + * Note that because segment registers may be used as instruction + * prefixes, we must ensure the enumerations for prefixes and + * register names do not overlap. + */ +enum { /* instruction prefixes */ + PREFIX_ENUM_START = REG_ENUM_LIMIT, + P_A16 = PREFIX_ENUM_START, P_A32, P_LOCK, P_O16, P_O32, P_REP, P_REPE, + P_REPNE, P_REPNZ, P_REPZ, P_TIMES +}; + +enum { /* extended operand types */ + EOT_NOTHING, EOT_DB_STRING, EOT_DB_NUMBER +}; + +enum { /* special EA flags */ + EAF_BYTEOFFS = 1, /* force offset part to byte size */ + EAF_WORDOFFS = 2, /* force offset part to [d]word size */ + EAF_TIMESTWO = 4 /* really do EAX*2 not EAX+EAX */ +}; + +enum { /* values for `hinttype' */ + EAH_NOHINT = 0, /* no hint at all - our discretion */ + EAH_MAKEBASE = 1, /* try to make given reg the base */ + EAH_NOTBASE = 2 /* try _not_ to make reg the base */ +}; + +typedef struct { /* operand to an instruction */ + long type; /* type of operand */ + int addr_size; /* 0 means default; 16; 32 */ + int basereg, indexreg, scale; /* registers and scale involved */ + int hintbase, hinttype; /* hint as to real base register */ + long segment; /* immediate segment, if needed */ + long offset; /* any immediate number */ + long wrt; /* segment base it's relative to */ + int eaflags; /* special EA flags */ + int opflags; /* see OPFLAG_* defines below */ +} operand; + +#define OPFLAG_FORWARD 1 /* operand is a forward reference */ +#define OPFLAG_EXTERN 2 /* operand is an external reference */ + +typedef struct extop { /* extended operand */ + struct extop *next; /* linked list */ + long type; /* defined above */ + char *stringval; /* if it's a string, then here it is */ + int stringlen; /* ... and here's how long it is */ + long segment; /* if it's a number/address, then... */ + long offset; /* ... it's given here ... */ + long wrt; /* ... and here */ +} extop; + +#define MAXPREFIX 4 + +typedef struct { /* an instruction itself */ + char *label; /* the label defined, or NULL */ + int prefixes[MAXPREFIX]; /* instruction prefixes, if any */ + int nprefix; /* number of entries in above */ + int opcode; /* the opcode - not just the string */ + int condition; /* the condition code, if Jcc/SETcc */ + int operands; /* how many operands? 0-3 + * (more if db et al) */ + operand oprs[3]; /* the operands, defined as above */ + extop *eops; /* extended operands */ + int eops_float; /* true if DD and floating */ + long times; /* repeat count (TIMES prefix) */ + int forw_ref; /* is there a forward reference? */ +} insn; + +enum geninfo { GI_SWITCH }; +/* + * ------------------------------------------------------------ + * The data structure defining an output format driver, and the + * interfaces to the functions therein. + * ------------------------------------------------------------ + */ + +struct ofmt { + /* + * This is a short (one-liner) description of the type of + * output generated by the driver. + */ + const char *fullname; + + /* + * This is a single keyword used to select the driver. + */ + const char *shortname; + + /* + * this is reserved for out module specific help. + * It is set to NULL in all the out modules but is not implemented + * in the main program + */ + const char *helpstring; + + /* + * this is a pointer to the first element of the debug information + */ + struct dfmt **debug_formats; + + /* + * and a pointer to the element that is being used + * note: this is set to the default at compile time and changed if the + * -F option is selected. If developing a set of new debug formats for + * an output format, be sure to set this to whatever default you want + * + */ + struct dfmt *current_dfmt; + + /* + * This, if non-NULL, is a NULL-terminated list of `char *'s + * pointing to extra standard macros supplied by the object + * format (e.g. a sensible initial default value of __SECT__, + * and user-level equivalents for any format-specific + * directives). + */ + const char **stdmac; + + /* + * This procedure is called at the start of an output session. + * It tells the output format what file it will be writing to, + * what routine to report errors through, and how to interface + * to the label manager and expression evaluator if necessary. + * It also gives it a chance to do other initialisation. + */ + void (*init) (FILE *fp, efunc error, ldfunc ldef, evalfunc eval); + + /* + * This procedure is called to pass generic information to the + * object file. The first parameter gives the information type + * (currently only command line switches) + * and the second parameter gives the value. This function returns + * 1 if recognized, 0 if unrecognized + */ + int (*setinfo)(enum geninfo type, char **string); + + /* + * This procedure is called by assemble() to write actual + * generated code or data to the object file. Typically it + * doesn't have to actually _write_ it, just store it for + * later. + * + * The `type' argument specifies the type of output data, and + * usually the size as well: its contents are described below. + */ + void (*output) (long segto, const void *data, unsigned long type, + long segment, long wrt); + + /* + * This procedure is called once for every symbol defined in + * the module being assembled. It gives the name and value of + * the symbol, in NASM's terms, and indicates whether it has + * been declared to be global. Note that the parameter "name", + * when passed, will point to a piece of static storage + * allocated inside the label manager - it's safe to keep using + * that pointer, because the label manager doesn't clean up + * until after the output driver has. + * + * Values of `is_global' are: 0 means the symbol is local; 1 + * means the symbol is global; 2 means the symbol is common (in + * which case `offset' holds the _size_ of the variable). + * Anything else is available for the output driver to use + * internally. + * + * This routine explicitly _is_ allowed to call the label + * manager to define further symbols, if it wants to, even + * though it's been called _from_ the label manager. That much + * re-entrancy is guaranteed in the label manager. However, the + * label manager will in turn call this routine, so it should + * be prepared to be re-entrant itself. + * + * The `special' parameter contains special information passed + * through from the command that defined the label: it may have + * been an EXTERN, a COMMON or a GLOBAL. The distinction should + * be obvious to the output format from the other parameters. + */ + void (*symdef) (char *name, long segment, long offset, int is_global, + char *special); + + /* + * This procedure is called when the source code requests a + * segment change. It should return the corresponding segment + * _number_ for the name, or NO_SEG if the name is not a valid + * segment name. + * + * It may also be called with NULL, in which case it is to + * return the _default_ section number for starting assembly in. + * + * It is allowed to modify the string it is given a pointer to. + * + * It is also allowed to specify a default instruction size for + * the segment, by setting `*bits' to 16 or 32. Or, if it + * doesn't wish to define a default, it can leave `bits' alone. + */ + long (*section) (char *name, int pass, int *bits); + + /* + * This procedure is called to modify the segment base values + * returned from the SEG operator. It is given a segment base + * value (i.e. a segment value with the low bit set), and is + * required to produce in return a segment value which may be + * different. It can map segment bases to absolute numbers by + * means of returning SEG_ABS types. + * + * It should return NO_SEG if the segment base cannot be + * determined; the evaluator (which calls this routine) is + * responsible for throwing an error condition if that occurs + * in pass two or in a critical expression. + */ + long (*segbase) (long segment); + + /* + * This procedure is called to allow the output driver to + * process its own specific directives. When called, it has the + * directive word in `directive' and the parameter string in + * `value'. It is called in both assembly passes, and `pass' + * will be either 1 or 2. + * + * This procedure should return zero if it does not _recognise_ + * the directive, so that the main program can report an error. + * If it recognises the directive but then has its own errors, + * it should report them itself and then return non-zero. It + * should also return non-zero if it correctly processes the + * directive. + */ + int (*directive) (char *directive, char *value, int pass); + + /* + * This procedure is called before anything else - even before + * the "init" routine - and is passed the name of the input + * file from which this output file is being generated. It + * should return its preferred name for the output file in + * `outname', if outname[0] is not '\0', and do nothing to + * `outname' otherwise. Since it is called before the driver is + * properly initialised, it has to be passed its error handler + * separately. + * + * This procedure may also take its own copy of the input file + * name for use in writing the output file: it is _guaranteed_ + * that it will be called before the "init" routine. + * + * The parameter `outname' points to an area of storage + * guaranteed to be at least FILENAME_MAX in size. + */ + void (*filename) (char *inname, char *outname, efunc error); + + /* + * This procedure is called after assembly finishes, to allow + * the output driver to clean itself up and free its memory. + * Typically, it will also be the point at which the object + * file actually gets _written_. + * + * One thing the cleanup routine should always do is to close + * the output file pointer. + */ + void (*cleanup) (int debuginfo); +}; + +/* + * values for the `type' parameter to an output function. Each one + * must have the actual number of _bytes_ added to it. + * + * Exceptions are OUT_RELxADR, which denote an x-byte relocation + * which will be a relative jump. For this we need to know the + * distance in bytes from the start of the relocated record until + * the end of the containing instruction. _This_ is what is stored + * in the size part of the parameter, in this case. + * + * Also OUT_RESERVE denotes reservation of N bytes of BSS space, + * and the contents of the "data" parameter is irrelevant. + * + * The "data" parameter for the output function points to a "long", + * containing the address in question, unless the type is + * OUT_RAWDATA, in which case it points to an "unsigned char" + * array. + */ +#define OUT_RAWDATA 0x00000000UL +#define OUT_ADDRESS 0x10000000UL +#define OUT_REL2ADR 0x20000000UL +#define OUT_REL4ADR 0x30000000UL +#define OUT_RESERVE 0x40000000UL +#define OUT_TYPMASK 0xF0000000UL +#define OUT_SIZMASK 0x0FFFFFFFUL + +/* + * ------------------------------------------------------------ + * The data structure defining a debug format driver, and the + * interfaces to the functions therein. + * ------------------------------------------------------------ + */ + +struct dfmt { + + /* + * This is a short (one-liner) description of the type of + * output generated by the driver. + */ + const char *fullname; + + /* + * This is a single keyword used to select the driver. + */ + const char *shortname; + + + /* + * init - called initially to set up local pointer to object format, + * void pointer to implementation defined data, file pointer (which + * probably won't be used, but who knows?), and error function. + */ + void (*init) (struct ofmt * of, void * id, FILE * fp, efunc error); + + /* + * linenum - called any time there is output with a change of + * line number or file. + */ + void (*linenum) (const char * filename, long linenumber, long segto); + + /* + * debug_deflabel - called whenever a label is defined. Parameters + * are the same as to 'symdef()' in the output format. This function + * would be called before the output format version. + */ + + void (*debug_deflabel) (char * name, long segment, long offset, + int is_global, char * special); + /* + * debug_directive - called whenever a DEBUG directive other than 'LINE' + * is encountered. 'directive' contains the first parameter to the + * DEBUG directive, and params contains the rest. For example, + * 'DEBUG VAR _somevar:int' would translate to a call to this + * function with 'directive' equal to "VAR" and 'params' equal to + * "_somevar:int". + */ + void (*debug_directive) (const char * directive, const char * params); + + /* + * typevalue - called whenever the assembler wishes to register a type + * for the last defined label. This routine MUST detect if a type was + * already registered and not re-register it. + */ + void (*debug_typevalue) (long type); + + /* + * debug_output - called whenever output is required + * 'type' is the type of info required, and this is format-specific + */ + void (*debug_output) (int type, void *param); + + /* + * cleanup - called after processing of file is complete + */ + void (*cleanup) (void); + +}; +/* + * The type definition macros + * for debugging + * + * low 3 bits: reserved + * next 5 bits: type + * next 24 bits: number of elements for arrays (0 for labels) + */ + +#define TY_UNKNOWN 0x00 +#define TY_LABEL 0x08 +#define TY_BYTE 0x10 +#define TY_WORD 0x18 +#define TY_DWORD 0x20 +#define TY_FLOAT 0x28 +#define TY_QWORD 0x30 +#define TY_TBYTE 0x38 +#define TY_COMMON 0xE0 +#define TY_SEG 0xE8 +#define TY_EXTERN 0xF0 +#define TY_EQU 0xF8 + +#define TYM_TYPE(x) ((x) & 0xF8) +#define TYM_ELEMENTS(x) (((x) & 0xFFFFFF00) >> 8) + +#define TYS_ELEMENTS(x) ((x) << 8) +/* + * ----- + * Other + * ----- + */ + +/* + * This is a useful #define which I keep meaning to use more often: + * the number of elements of a statically defined array. + */ + +#define elements(x) ( sizeof(x) / sizeof(*(x)) ) + +extern int tasm_compatible_mode; + +/* + * This declaration passes the "pass" number to all other modules + * "pass0" assumes the values: 0, 0, ..., 0, 1, 2 + * where 0 = optimizing pass + * 1 = pass 1 + * 2 = pass 2 + */ + +extern int pass0; /* this is globally known */ +extern int optimizing; + +#endif diff --git a/AltairZ80/s100_64fdc.c b/AltairZ80/s100_64fdc.c new file mode 100644 index 0000000..c9d1104 --- /dev/null +++ b/AltairZ80/s100_64fdc.c @@ -0,0 +1,1541 @@ +/************************************************************************* + * * + * $Id: s100_64fdc.c 1907 2008-05-21 07:04:17Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Cromemco 4FDC/16FDC/64FDC Floppy Controller module for SIMH. * + * This module is a wrapper around the wd179x FDC module, and adds the * + * Cromemco-specific registers as well as the Cromemco RDOS Boot ROM. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ +#define DBG_MSG +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#include "sim_defs.h" /* simulator definitions */ +#include "wd179x.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 +#define DRIVE_MSG 0x40 +#define VERBOSE_MSG 0x80 + +#define CROMFDC_MAX_DRIVES 4 +#define CROMFDC_ROM_SIZE (8 * 1024) +#define CROMFDC_ADDR_MASK (CROMFDC_ROM_SIZE - 1) + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint32 dma_addr; /* DMA Transfer Address */ + uint8 rom_disabled; /* TRUE if ROM has been disabled */ + uint8 motor_on; + uint8 autowait; + uint8 rtc; +} CROMFDC_INFO; + +extern WD179X_INFO_PUB *wd179x_info; + +static CROMFDC_INFO cromfdc_info_data = { { 0xC000, CROMFDC_ROM_SIZE, 0x3, 2 } }; +static CROMFDC_INFO *cromfdc_info = &cromfdc_info_data; + +extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +t_stat cromfdc_svc (UNIT *uptr); + +extern REG *sim_PC; +extern uint32 PCX; /* external view of PC */ + +#define UNIT_V_CROMFDC_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_CROMFDC_WLK (1 << UNIT_V_CROMFDC_WLK) +#define UNIT_V_CROMFDC_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_CROMFDC_VERBOSE (1 << UNIT_V_CROMFDC_VERBOSE) +#define UNIT_V_CROMFDC_ROM (UNIT_V_UF + 2) /* boot ROM enabled */ +#define UNIT_CROMFDC_ROM (1 << UNIT_V_CROMFDC_ROM) +#define CROMFDC_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ + +#define MOTOR_TO_LIMIT 128 + +static t_stat cromfdc_reset(DEVICE *cromfdc_dev); +static t_stat cromfdc_boot(int32 unitno, DEVICE *dptr); +static t_stat cromfdc_attach(UNIT *uptr, char *cptr); +static t_stat cromfdc_detach(UNIT *uptr); + +static int32 cromfdc_ext(const int32 port, const int32 io, const int32 data); +static int32 cromfdc_timer(const int32 port, const int32 io, const int32 data); +static int32 cromfdc_control(const int32 port, const int32 io, const int32 data); +static int32 cromfdc_banksel(const int32 port, const int32 io, const int32 data); +static int32 cromfdcrom(const int32 port, const int32 io, const int32 data); + +static int32 dipswitch = 0; /* 5-position DIP switch on 64FDC card */ +static int32 trace_level = 0; /* Disable all tracing by default */ +static int32 bootstrap = 0; /* 0 for RDOS 2.52, 1 for RDOS 3.12. */ +static int32 crofdc_type = 64; /* controller type, either 4, 16, or 64. */ +static int32 crofdc_boot = 1; /* BOOT jumper setting, default is auto-boot */ +static int32 crofdc_inh_init = 0; /* Inhibit Init (Format) switch, default is not inhibited */ + +/* Disk Control/Flags Register, 0x34 (IN) */ +#define CROMFDC_FLAG_DRQ (1 << 7) /* DRQ (All controllers) */ +#define CROMFDC_FLAG_BOOT (1 << 6) /* boot# jumper (active low) (All controllers) */ +#define CROMFDC_FLAG_SEL_REQ (1 << 5) /* Head Load (4FDC, 16FDC) / Select Request (64FDC) */ +#define CROMFDC_FLAG_INH_INIT (1 << 4) /* Unassigned (4FDC) / Inhibit_Init# (16FDC, 64FDC) */ +#define CROMFDC_FLAG_MTRON (1 << 3) /* Unassigned (4FDC) / Motor On (16FDC, 64FDC) */ +#define CROMFDC_FLAG_MTO (1 << 2) /* Unassigned (4FDC) / Motor Timeout (16FDC, 64FDC) */ +#define CROMFDC_FLAG_ATO (1 << 1) /* Unassigned (4FDC) / Autowait Timeout (16FDC, 64FDC) */ +#define CROMFDC_FLAG_EOJ (1 << 0) /* End of Job (INTRQ) (All Controllers) (16FDC, 64FDC) */ + +/* Disk Control/Flags Register, 0x34 (OUT) */ +#define CROMFDC_CTRL_AUTOWAIT (1 << 7) /* Auto Wait Enable (All controllers) */ +#define CROMFDC_CTRL_DDENS (1 << 6) /* Unassigned (4FDC) / Double Density (16FDC, 64FDC) */ +#define CROMFDC_CTRL_MTRON (1 << 5) /* Motor On (All controllers) */ +#define CROMFDC_CTRL_MAXI (1 << 4) /* Maxi (8") (All controllers) */ +#define CROMFDC_CTRL_DS4 (1 << 3) /* Drive Select 4 (All controllers) */ +#define CROMFDC_CTRL_DS3 (1 << 2) /* Drive Select 3 (All controllers) */ +#define CROMFDC_CTRL_DS2 (1 << 1) /* Drive Select 2 (All controllers) */ +#define CROMFDC_CTRL_DS1 (1 << 0) /* Drive Select 1 (All controllers) */ + +/* 64FDC Auxiliary Disk Command, 0x04 (OUT) */ +#define CROMFDC_AUX_RESERVED0 (1 << 0) /* Unused (All Controllers) */ +#define CROMFDC_AUX_CMD_SIDE (1 << 1) /* 16FDC, 64FDC: Side Select* Low=Side 1, High=Side 0. */ +#define CROMFDC_AUX_CTRL_OUT (1 << 2) /* Control Out* (All Controllers) */ +#define CROMFDC_AUX_RESTORE (1 << 3) /* 4FDC, 16FDC Restore* / 64FDC Unused */ +#define CROMFDC_AUX_FAST_SEEK (1 << 4) /* 4FDC, 16FDC Fast Seek* / 64FDC Unused */ +#define CROMFDC_AUX_SEL_OVERRIDE (1 << 5) /* 4FDC Eject Right* / 16FDC, 64FDC Drive Select Override */ +#define CROMFDC_AUX_EJECT (1 << 6) /* 4FDC Eject Left* / 16FDC Eject*, 64FDC Unused */ +#define CROMFDC_AUX_RESERVED7 (1 << 7) /* Unused (All Controllers) */ + + + +/* The CROMFDC does not really have RAM associated with it, but for ease of integration with the + * SIMH/AltairZ80 Resource Mapping Scheme, rather than Map and Unmap the ROM, simply implement our + * own RAM that can be swapped in when the CROMFDC Boot ROM is disabled. + */ +static uint8 cromfdcram[CROMFDC_ROM_SIZE]; + +static UNIT cromfdc_unit[] = { + { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE + UNIT_CROMFDC_ROM, CROMFDC_CAPACITY), 1024 }, + { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, CROMFDC_CAPACITY) }, + { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, CROMFDC_CAPACITY) }, + { UDATA (&cromfdc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, CROMFDC_CAPACITY) } +}; + +static REG cromfdc_reg[] = { + { HRDATA (DIPSW, dipswitch, 16), }, + { HRDATA (TRACELEVEL, trace_level, 16), }, + { DRDATA (BOOTSTRAP, bootstrap, 10), }, + { DRDATA (FDCTYPE, crofdc_type, 10), }, + { DRDATA (BOOT, crofdc_boot, 10), }, + { DRDATA (INHINIT, crofdc_inh_init, 10), }, + { NULL } +}; + +static MTAB cromfdc_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_CROMFDC_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_CROMFDC_WLK, UNIT_CROMFDC_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_CROMFDC_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_CROMFDC_VERBOSE, UNIT_CROMFDC_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { UNIT_CROMFDC_ROM, 0, "NOROM", "NOROM", NULL }, + { UNIT_CROMFDC_ROM, UNIT_CROMFDC_ROM, "ROM", "ROM", NULL }, + { 0 } +}; + +DEVICE cromfdc_dev = { + "CROMFDC", cromfdc_unit, cromfdc_reg, cromfdc_mod, + CROMFDC_MAX_DRIVES, 10, 31, 1, CROMFDC_MAX_DRIVES, CROMFDC_MAX_DRIVES, + NULL, NULL, &cromfdc_reset, + &cromfdc_boot, &cromfdc_attach, &cromfdc_detach, + &cromfdc_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* This is the CROMFDC RDOS-II ROM. + * The CROMFDC has a single 8K ROM; however ths simulation includes + * two different versions of RDOS: + * RDOS 2.52 and RDOS 3.12 + * RDOS 2.52 is the default, but RDOS 3.12 can be + * selected with 'd cromfdc bootstrap ' at the SIMH SCP Prompt. + */ +static uint8 cromfdc_rom[2][CROMFDC_ROM_SIZE] = { +{ /* RDOS 2.52 */ + 0xF3, 0x18, 0x3C, 0xC3, 0x30, 0xC0, 0xC3, 0x04, 0xC5, 0xC3, 0x37, 0xC0, 0xC3, 0x3B, 0xC0, 0xC3, + 0x9E, 0xCB, 0xC3, 0x37, 0xCD, 0xC3, 0xD4, 0xC4, 0xC3, 0x52, 0xCD, 0xC3, 0xAB, 0xC5, 0xC3, 0x4B, + 0xC2, 0xC3, 0x6C, 0xCC, 0xC3, 0x76, 0xCC, 0xC3, 0xEA, 0xCB, 0xC3, 0xFE, 0xC7, 0xC3, 0x97, 0xCC, + 0x32, 0x77, 0x00, 0x78, 0xC3, 0x87, 0xC3, 0x32, 0x75, 0x00, 0xC9, 0x32, 0x76, 0x00, 0xC9, 0xAF, + 0xD3, 0x03, 0x47, 0xD9, 0x2F, 0xD3, 0x04, 0x3E, 0xD0, 0xD3, 0x30, 0x21, 0x2E, 0x00, 0xF9, 0x25, + 0x20, 0xFD, 0x74, 0x2C, 0x20, 0xFC, 0x24, 0x22, 0x62, 0x00, 0x22, 0x64, 0x00, 0xCD, 0x4B, 0xC2, + 0xDB, 0x34, 0xE6, 0x40, 0xC2, 0x39, 0xC1, 0xDB, 0x04, 0x2F, 0xE6, 0x03, 0x32, 0x77, 0x00, 0xCD, + 0x06, 0xC8, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, + 0x6F, 0x6F, 0x74, 0x2C, 0x20, 0x45, 0x53, 0x43, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x62, 0x6F, 0x72, + 0x74, 0x8D, 0xCD, 0xA8, 0xCF, 0x57, 0xD3, 0x34, 0x06, 0x64, 0xCD, 0x28, 0xC1, 0x21, 0x64, 0x00, + 0xCD, 0xF1, 0xCF, 0x10, 0xF5, 0x32, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x32, 0x78, 0x00, 0x3C, 0x32, + 0x76, 0x00, 0xCD, 0xF7, 0xC1, 0x32, 0x7E, 0x00, 0x21, 0x80, 0x00, 0x22, 0x7B, 0x00, 0x22, 0x79, + 0x00, 0x3E, 0x42, 0x32, 0x70, 0x00, 0x06, 0x02, 0xC5, 0xCD, 0x37, 0xCD, 0xD4, 0xCB, 0xCD, 0xD4, + 0x52, 0xCD, 0xC1, 0x30, 0x13, 0xCD, 0x28, 0xC1, 0x10, 0xEE, 0xCD, 0x06, 0xC8, 0x0D, 0x55, 0x6E, + 0x61, 0x62, 0x6C, 0x65, 0x20, 0xF4, 0x18, 0x2D, 0x3A, 0x80, 0x00, 0xFE, 0x40, 0x28, 0x04, 0xFE, + 0xE5, 0x20, 0x07, 0xCD, 0x06, 0xC8, 0x0D, 0xCE, 0x18, 0x1B, 0xCD, 0x28, 0xC1, 0xCD, 0xA8, 0xCF, + 0x57, 0x3A, 0x77, 0x00, 0x08, 0xCD, 0x06, 0xC8, 0x0D, 0x53, 0x74, 0x61, 0x6E, 0x64, 0x62, 0x79, + 0x8D, 0x37, 0xC3, 0x80, 0x00, 0xCD, 0x06, 0xC8, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x8D, 0x3A, + 0x71, 0x00, 0xB7, 0xC4, 0x59, 0xC3, 0x18, 0x09, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, 0xCC, 0xFE, 0x1B, + 0xC0, 0x3E, 0xD0, 0xD3, 0x30, 0x3E, 0x7C, 0xD3, 0x04, 0xCD, 0x06, 0xC8, 0x0D, 0x43, 0x72, 0x6F, + 0x6D, 0x65, 0x6D, 0x63, 0x6F, 0x20, 0x52, 0x44, 0x4F, 0x53, 0x20, 0x30, 0x32, 0x2E, 0x35, 0x32, + 0x8D, 0xAF, 0x32, 0x6C, 0x00, 0x3A, 0x7E, 0x00, 0x47, 0xCD, 0xE2, 0xC1, 0x31, 0x2E, 0x00, 0xCD, + 0x06, 0xC8, 0xBB, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xA7, 0x28, 0xE5, 0xCD, 0xC1, 0xC2, 0x28, + 0xE4, 0x78, 0xFE, 0x17, 0x30, 0x47, 0x87, 0x21, 0x8B, 0xC1, 0xCD, 0x86, 0xC1, 0x7E, 0x23, 0x66, + 0x6F, 0xCD, 0xC7, 0xC1, 0x18, 0xCF, 0x85, 0x6F, 0xD0, 0x24, 0xC9, 0xFD, 0xCC, 0xC8, 0xC1, 0xBD, + 0xC1, 0x2B, 0xC7, 0x2A, 0xC6, 0xBD, 0xC1, 0xC4, 0xC1, 0xBD, 0xC1, 0x45, 0xC2, 0xBD, 0xC1, 0xBD, + 0xC1, 0x39, 0xC4, 0x45, 0xC6, 0xBD, 0xC1, 0x36, 0xC6, 0xBD, 0xC1, 0xC2, 0xC6, 0x16, 0xC5, 0x7A, + 0xC6, 0x41, 0xC9, 0xBD, 0xC1, 0x52, 0xC6, 0x1A, 0xC5, 0xAF, 0x32, 0x6C, 0x00, 0xCD, 0x06, 0xC8, + 0x3F, 0x8D, 0x18, 0x91, 0xCD, 0x53, 0xC8, 0xE9, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x67, 0xC0, 0xFE, + 0x45, 0xD2, 0x2A, 0xC4, 0xD6, 0x41, 0xDA, 0x2A, 0xC4, 0xF5, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC3, + 0x6C, 0xC0, 0x3A, 0x6C, 0x00, 0xB7, 0xC8, 0xCD, 0xFE, 0xC7, 0xCB, 0x58, 0xC0, 0xCB, 0x78, 0x3E, + 0x3B, 0xC4, 0xFE, 0xC7, 0xC3, 0xFE, 0xC7, 0x7A, 0xD3, 0x34, 0x3E, 0xDF, 0xD3, 0x04, 0x3E, 0xD4, + 0xD3, 0x30, 0xDB, 0x34, 0x0F, 0x30, 0xFB, 0xDB, 0x30, 0x01, 0x00, 0x02, 0xCD, 0x3B, 0xC2, 0x3E, + 0xD4, 0xD3, 0x30, 0xCD, 0x34, 0xC2, 0x28, 0xDF, 0xDB, 0x34, 0x0F, 0x30, 0xF6, 0xDB, 0x30, 0x10, + 0xEE, 0xAF, 0xD3, 0x03, 0x79, 0xFE, 0x5A, 0x30, 0x03, 0x87, 0x87, 0x87, 0xFE, 0xB7, 0x3E, 0x80, + 0xD0, 0x3E, 0x04, 0xC9, 0xDB, 0x03, 0xFE, 0xC7, 0xC0, 0x0C, 0xC8, 0x3E, 0x01, 0xD3, 0x03, 0x3E, + 0xFA, 0xD3, 0x05, 0x18, 0xEF, 0xCD, 0x27, 0xC8, 0xCD, 0x97, 0xCC, 0xDB, 0x04, 0xE6, 0x08, 0x28, + 0x5D, 0x3E, 0x0A, 0xD3, 0x02, 0x21, 0xD0, 0x07, 0xCD, 0xF1, 0xCF, 0x3E, 0x08, 0xD3, 0x02, 0x16, + 0x64, 0x15, 0x28, 0xED, 0x21, 0xB9, 0xC2, 0x0E, 0x00, 0x3E, 0x19, 0x06, 0x09, 0xD3, 0x02, 0xED, + 0xA3, 0x28, 0xEE, 0xCD, 0x98, 0xC2, 0xCD, 0x98, 0xC2, 0x38, 0xE9, 0xFE, 0x0D, 0x3E, 0x09, 0x20, + 0xEC, 0x3E, 0x0D, 0xD3, 0x01, 0x21, 0xA0, 0x0F, 0xCD, 0xF1, 0xCF, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, + 0xCC, 0xFE, 0x0D, 0x28, 0xEC, 0xC3, 0x97, 0xCC, 0xD5, 0x11, 0xA0, 0x8C, 0xCD, 0x76, 0xCC, 0x28, + 0x05, 0xCD, 0x6C, 0xCC, 0x18, 0x06, 0x1B, 0x7A, 0xB3, 0x20, 0xF1, 0x37, 0xD1, 0xC9, 0x3E, 0x09, + 0xD3, 0x02, 0x3E, 0x84, 0xD3, 0x00, 0xC3, 0x97, 0xCC, 0x90, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, + 0x01, 0xD6, 0x41, 0xDA, 0xBD, 0xC1, 0x47, 0x13, 0x1A, 0xFE, 0x3B, 0xC0, 0x78, 0xFE, 0x04, 0xD2, + 0x2A, 0xC4, 0xC6, 0x41, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x0C, + 0x20, 0x0B, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x04, 0x20, 0x03, 0x06, 0x80, 0x13, 0xC5, 0xCD, 0xB7, + 0xC3, 0xC1, 0x70, 0x30, 0x06, 0x3A, 0x6D, 0x00, 0xB0, 0x77, 0x37, 0xF5, 0xCD, 0x84, 0xC3, 0x32, + 0x78, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x32, 0x76, 0x00, 0xCD, 0x04, 0xC6, 0x3E, 0x48, 0x32, 0x70, + 0x00, 0xCD, 0x37, 0xCD, 0xF5, 0xCD, 0x1E, 0xC6, 0xF1, 0xDA, 0x0D, 0xC4, 0xF1, 0xDA, 0xB5, 0xC3, + 0xCD, 0x04, 0xC6, 0x21, 0x00, 0x01, 0x22, 0x7B, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x52, 0xCD, 0xF5, + 0xCD, 0x1E, 0xC6, 0xF1, 0x30, 0x2D, 0x3A, 0x71, 0x00, 0xB7, 0xF2, 0x45, 0xC3, 0x21, 0x64, 0x00, + 0xCD, 0xF1, 0xCF, 0x18, 0xDB, 0xCD, 0x06, 0xC8, 0x43, 0x61, 0x6E, 0x27, 0x74, 0x20, 0x72, 0x65, + 0x61, 0x64, 0x20, 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x8D, 0x1E, 0x01, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, + 0xC4, 0xAF, 0xC9, 0x01, 0x10, 0x00, 0x11, 0x7A, 0x01, 0xCD, 0xEE, 0xC3, 0x0E, 0x01, 0xCD, 0xEE, + 0xC3, 0x3A, 0x78, 0x01, 0xFE, 0x43, 0x20, 0x02, 0xCB, 0xC8, 0x3A, 0x7E, 0x00, 0xE6, 0xCC, 0xB0, + 0x2A, 0x6A, 0x00, 0x77, 0xCD, 0xF6, 0xC3, 0x32, 0x7E, 0x00, 0xCB, 0x47, 0x11, 0x0A, 0x10, 0x21, + 0x00, 0x02, 0x20, 0x0D, 0xCB, 0x4F, 0x11, 0x05, 0x08, 0x20, 0x06, 0x11, 0x12, 0x1A, 0x21, 0x80, + 0x00, 0x22, 0x79, 0x00, 0xCB, 0x7F, 0x3E, 0x4C, 0x42, 0x28, 0x03, 0x3E, 0x27, 0x43, 0x32, 0x6F, + 0x00, 0x78, 0x32, 0x6E, 0x00, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0x01, 0x10, 0x00, 0xCD, + 0xDE, 0xC3, 0x0E, 0x01, 0xCD, 0xDE, 0xC3, 0xCD, 0x14, 0xC8, 0xFE, 0x43, 0x20, 0x06, 0xCB, 0xC8, + 0x13, 0xCD, 0x14, 0xC8, 0xB7, 0xC2, 0xB9, 0xC1, 0x78, 0x32, 0x6D, 0x00, 0x37, 0xC9, 0xCD, 0x14, + 0xC8, 0x13, 0xFE, 0x53, 0xC8, 0xFE, 0x44, 0xC2, 0xB9, 0xC1, 0x78, 0xB1, 0x47, 0xC9, 0x1A, 0x13, + 0x13, 0xFE, 0x44, 0xC0, 0x18, 0xF4, 0x3A, 0x6C, 0x00, 0xD6, 0x41, 0x32, 0x77, 0x00, 0x21, 0x66, + 0x00, 0xCD, 0x86, 0xC1, 0x22, 0x6A, 0x00, 0x7E, 0xB7, 0xC9, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, 0xC4, + 0xC3, 0x55, 0xC1, 0xCD, 0x06, 0xC8, 0x20, 0x45, 0x72, 0x72, 0xAD, 0x3A, 0x70, 0x00, 0xCD, 0xFE, + 0xC7, 0x3A, 0x71, 0x00, 0xCD, 0xDB, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x41, 0x2D, 0x44, + 0x20, 0x6F, 0x6E, 0x6C, 0x79, 0x8D, 0xC3, 0x55, 0xC1, 0xCD, 0x27, 0xC8, 0x3A, 0x77, 0x00, 0xF5, + 0x3A, 0x6C, 0x00, 0xF5, 0x3E, 0x41, 0xF5, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x2A, 0x6A, 0x00, + 0x46, 0xC4, 0x63, 0xC4, 0xF1, 0x3C, 0xFE, 0x45, 0x38, 0xEC, 0xF1, 0x32, 0x6C, 0x00, 0xF1, 0x32, + 0x77, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x20, 0xA0, 0xCD, 0xE2, 0xC1, 0xCD, 0x06, 0xC8, 0x3B, 0xA0, + 0xCB, 0x58, 0xC4, 0xFC, 0xC7, 0xCB, 0x78, 0xCC, 0xFC, 0xC7, 0xCB, 0x60, 0xCD, 0x98, 0xC4, 0xCD, + 0xFC, 0xC7, 0xCB, 0x40, 0xCD, 0x98, 0xC4, 0xCB, 0x48, 0x28, 0x0A, 0xCD, 0x06, 0xC8, 0x20, 0x43, + 0x72, 0x6F, 0x6D, 0x69, 0xF8, 0xC3, 0x97, 0xCC, 0x3E, 0x44, 0x20, 0x02, 0x3E, 0x53, 0xC3, 0xFE, + 0xC7, 0x47, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, 0xBD, 0xC1, 0x78, 0xFE, 0x53, 0x28, 0x2C, 0xCD, 0xB0, + 0xC8, 0xDA, 0xBD, 0xC1, 0xE5, 0xCD, 0xE1, 0xC4, 0x3E, 0x53, 0x32, 0x70, 0x00, 0xE1, 0xAF, 0xB4, + 0xC2, 0xBD, 0xC1, 0x3A, 0x6F, 0x00, 0xBD, 0x38, 0xE8, 0x7D, 0x32, 0x75, 0x00, 0xCD, 0xD4, 0xC4, + 0xD0, 0xC3, 0x0D, 0xC4, 0xCD, 0xF8, 0xC5, 0xC3, 0xCB, 0xCD, 0x13, 0xCD, 0xE1, 0xC4, 0xC3, 0xCB, + 0xCD, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xCD, 0xAD, 0xC8, 0xD8, 0xAF, 0xB4, 0xC2, 0xBD, 0xC1, + 0x7D, 0xB7, 0x20, 0x04, 0x32, 0x78, 0x00, 0xC9, 0x3D, 0x20, 0xF2, 0xCD, 0xF6, 0xC3, 0xCB, 0x67, + 0x28, 0x06, 0x3E, 0x01, 0x32, 0x78, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x53, 0x2E, 0x53, 0x69, 0x64, + 0x65, 0x64, 0x8D, 0xC3, 0x55, 0xC1, 0x06, 0x52, 0x18, 0x02, 0x06, 0x57, 0x1A, 0xFE, 0x44, 0xC2, + 0xBD, 0xC1, 0x78, 0x32, 0x70, 0x00, 0xCD, 0xBB, 0xC5, 0xE5, 0xFD, 0xE1, 0x60, 0x69, 0xD5, 0xE5, + 0x7B, 0x32, 0x76, 0x00, 0xFD, 0x22, 0x7B, 0x00, 0xCD, 0xD4, 0xC4, 0x3A, 0x70, 0x00, 0xFE, 0x57, + 0x37, 0x3F, 0xCC, 0xAB, 0xC5, 0x38, 0x08, 0x3A, 0x70, 0x00, 0xFE, 0x52, 0xCC, 0x52, 0xCD, 0xE1, + 0xD1, 0xDA, 0x0A, 0xC4, 0xED, 0x4B, 0x79, 0x00, 0xFD, 0x09, 0x03, 0xED, 0x42, 0x23, 0xDA, 0xF2, + 0xC5, 0x1C, 0x3A, 0x6E, 0x00, 0xBB, 0x30, 0xC6, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x47, + 0x3A, 0x6F, 0x00, 0xB8, 0x38, 0x08, 0x78, 0x32, 0x75, 0x00, 0x1E, 0x01, 0x18, 0xB0, 0x1D, 0xCD, + 0xF2, 0xC5, 0xCD, 0x06, 0xC8, 0x4E, 0x65, 0x78, 0x74, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, + 0x3A, 0xA0, 0xFD, 0xE5, 0xE1, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x0D, 0x45, 0x6E, 0x64, 0x20, + 0x6F, 0x66, 0x20, 0x44, 0x69, 0x73, 0x6B, 0x8D, 0xC3, 0x55, 0xC1, 0x21, 0x7E, 0x00, 0x46, 0xC5, + 0xCB, 0xCE, 0xCD, 0x5F, 0xCD, 0xC1, 0x78, 0x32, 0x7E, 0x00, 0xC9, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, + 0xBD, 0xC1, 0x13, 0xCD, 0x2F, 0xC8, 0xAF, 0xB2, 0xC2, 0xBD, 0xC1, 0xB3, 0xCA, 0xBD, 0xC1, 0xC5, + 0xD5, 0xE5, 0xCD, 0xF8, 0xC5, 0xE1, 0xD1, 0xC1, 0x3A, 0x6E, 0x00, 0xBB, 0x38, 0xEA, 0xDB, 0x31, + 0x57, 0xEB, 0xCD, 0xFC, 0xC7, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, 0xFC, 0xC7, 0x3A, 0x78, 0x00, 0xC3, + 0xF0, 0xC7, 0xCD, 0xDE, 0xC5, 0xC3, 0x97, 0xCC, 0x3A, 0x75, 0x00, 0xB7, 0x20, 0x20, 0x3A, 0x78, + 0x00, 0xB7, 0x20, 0x1A, 0x3A, 0x7E, 0x00, 0xCB, 0x6F, 0xC0, 0xCB, 0x4F, 0x20, 0x03, 0xCB, 0x47, + 0xC8, 0x32, 0x7F, 0x00, 0xCB, 0xEF, 0xCB, 0x87, 0x32, 0x7E, 0x00, 0xC3, 0x9B, 0xC3, 0x3A, 0x7E, + 0x00, 0xCB, 0x6F, 0xC8, 0x3A, 0x7F, 0x00, 0xC3, 0x87, 0xC3, 0xCD, 0x53, 0xC8, 0x4D, 0xED, 0x78, + 0xCD, 0xE7, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0xAD, 0xC8, 0xE5, 0xCD, 0x1B, 0xC8, 0xCD, 0x53, 0xC8, + 0xD1, 0x4D, 0xED, 0x59, 0xC9, 0xCD, 0x2F, 0xC8, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xE1, 0xD1, 0xC1, + 0x18, 0x03, 0xCD, 0x2F, 0xC8, 0x1A, 0xBE, 0x28, 0x19, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, + 0xCD, 0xFC, 0xC7, 0x1A, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0xEB, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, + 0x97, 0xCC, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xDC, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, 0xC2, + 0xA1, 0xC4, 0x13, 0x2A, 0x64, 0x00, 0xCD, 0x5B, 0xC8, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, + 0xCD, 0xFC, 0xC7, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xFE, 0x2E, 0xC8, 0xFE, 0x2D, 0x20, 0x03, + 0x2B, 0x18, 0x1A, 0xCD, 0xF1, 0xC6, 0x30, 0x07, 0xCD, 0x06, 0xC8, 0x3F, 0x8D, 0x18, 0xDA, 0xAF, + 0x80, 0x20, 0x03, 0x23, 0x18, 0x07, 0x48, 0x06, 0x00, 0xEB, 0xED, 0xB0, 0xEB, 0x22, 0x64, 0x00, + 0x18, 0xC7, 0xCD, 0x65, 0xC8, 0xC5, 0xCD, 0xF1, 0xC6, 0xD1, 0xDA, 0xBD, 0xC1, 0xAF, 0x80, 0xCA, + 0xBD, 0xC1, 0xC5, 0xD5, 0xE5, 0x11, 0x2E, 0x00, 0x1A, 0xBE, 0x20, 0x04, 0x13, 0x23, 0x10, 0xF8, + 0xE1, 0xE5, 0x06, 0x10, 0xCC, 0x60, 0xC7, 0xE1, 0xD1, 0xC1, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xE2, + 0xC9, 0xE5, 0x06, 0x00, 0x21, 0x2E, 0x00, 0xCD, 0x14, 0xC8, 0xB7, 0x28, 0x29, 0xFE, 0x2C, 0x13, + 0x28, 0xF5, 0x4F, 0xFE, 0x27, 0x28, 0x12, 0xFE, 0x22, 0x28, 0x0E, 0x1B, 0xE5, 0xCD, 0xAD, 0xC8, + 0x7D, 0xE1, 0x38, 0x12, 0x77, 0x23, 0x04, 0x18, 0xDE, 0x1A, 0x13, 0xB7, 0x28, 0x08, 0xB9, 0x28, + 0xD6, 0x77, 0x23, 0x04, 0x18, 0xF3, 0x11, 0x2E, 0x00, 0xE1, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, + 0x20, 0x01, 0x13, 0x01, 0x80, 0x00, 0x2A, 0x62, 0x00, 0xCD, 0x4E, 0xC8, 0x1E, 0x10, 0xAF, 0xB0, + 0x20, 0x0A, 0x3E, 0x0F, 0xB9, 0x38, 0x05, 0xAF, 0xB1, 0x28, 0x01, 0x59, 0xC5, 0x43, 0xCD, 0x60, + 0xC7, 0x22, 0x62, 0x00, 0xC1, 0x79, 0x93, 0x4F, 0x30, 0x01, 0x05, 0x78, 0xB1, 0x20, 0xDD, 0xC9, + 0xCD, 0xE2, 0xC7, 0xC5, 0xE5, 0xD5, 0x0E, 0x00, 0x1E, 0x04, 0x3E, 0x03, 0xA1, 0xCC, 0xA0, 0xC7, + 0xCD, 0xA0, 0xC7, 0x7E, 0xCD, 0xE7, 0xC7, 0x1C, 0x1C, 0x23, 0x0C, 0x10, 0xED, 0xCD, 0xA0, 0xC7, + 0x3E, 0x3A, 0xBB, 0x20, 0xF8, 0xD1, 0xE1, 0xC1, 0x7E, 0x23, 0xCD, 0x92, 0xC7, 0x10, 0xF9, 0xC3, + 0x97, 0xCC, 0xE6, 0x7F, 0xFE, 0x7F, 0x28, 0x04, 0xFE, 0x20, 0x30, 0x62, 0x3E, 0x2E, 0x18, 0x5E, + 0x1C, 0x18, 0x59, 0x3E, 0x31, 0x11, 0x2E, 0x00, 0x12, 0xD5, 0xCD, 0xEA, 0xCB, 0xE3, 0x23, 0x46, + 0x04, 0x23, 0xE5, 0x05, 0x28, 0x17, 0x7E, 0xCD, 0xD2, 0xC7, 0x77, 0x23, 0xFE, 0x22, 0x28, 0x04, + 0xFE, 0x27, 0x20, 0xEF, 0x05, 0x28, 0x06, 0xBE, 0x23, 0x20, 0xF9, 0x18, 0xE6, 0x36, 0x00, 0xD1, + 0xE1, 0xC9, 0xFE, 0x61, 0xD8, 0xFE, 0x7B, 0xD0, 0xD6, 0x20, 0xC9, 0xF5, 0xCD, 0xFC, 0xC7, 0xF1, + 0x18, 0x05, 0x7C, 0xCD, 0xE7, 0xC7, 0x7D, 0xF5, 0x1F, 0x1F, 0x1F, 0x1F, 0xCD, 0xF0, 0xC7, 0xF1, + 0xE6, 0x0F, 0xFE, 0x0A, 0x38, 0x02, 0xC6, 0x07, 0xC6, 0x30, 0x18, 0x02, 0x3E, 0x20, 0xF5, 0xE6, + 0x7F, 0xCD, 0x99, 0xCC, 0xF1, 0xC9, 0xE3, 0x7E, 0x23, 0xB7, 0x28, 0x06, 0xCD, 0xFE, 0xC7, 0xF2, + 0x07, 0xC8, 0xE3, 0xC9, 0x1A, 0xFE, 0x20, 0xC0, 0x13, 0x18, 0xF9, 0xCD, 0x14, 0xC8, 0xFE, 0x2C, + 0xC0, 0x13, 0xCD, 0x14, 0xC8, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0xC3, 0xBD, 0xC1, 0x21, + 0x80, 0x00, 0x44, 0x4D, 0xCD, 0x68, 0xC8, 0xE5, 0xC5, 0xCD, 0x1B, 0xC8, 0xCD, 0xAD, 0xC8, 0xDA, + 0xBD, 0xC1, 0xCD, 0x27, 0xC8, 0xEB, 0xC1, 0xE1, 0xC9, 0xCD, 0x65, 0xC8, 0x18, 0xD9, 0xCD, 0x68, + 0xC8, 0x18, 0xD4, 0xCD, 0xAD, 0xC8, 0xDA, 0xBD, 0xC1, 0x18, 0xCC, 0xE5, 0xCD, 0xAD, 0xC8, 0x38, + 0x01, 0xE3, 0xE1, 0x18, 0xC2, 0x37, 0x18, 0x01, 0xB7, 0x08, 0xC5, 0xE5, 0xCD, 0xAD, 0xC8, 0x30, + 0x08, 0x08, 0xDA, 0xBD, 0xC1, 0x08, 0xE1, 0x18, 0x01, 0xF1, 0xCD, 0x87, 0xC8, 0x30, 0x06, 0x08, + 0xC1, 0xD0, 0xC3, 0xBD, 0xC1, 0xF1, 0xC9, 0xCD, 0x1B, 0xC8, 0x1A, 0xFE, 0x53, 0x20, 0x01, 0x13, + 0xE5, 0xF5, 0xCD, 0xAD, 0xC8, 0x38, 0x0F, 0x44, 0x4D, 0xF1, 0xE1, 0x28, 0x07, 0x79, 0x95, 0x4F, + 0x78, 0x9C, 0x47, 0x03, 0xB7, 0xC9, 0xF1, 0xE1, 0xCA, 0xBD, 0xC1, 0x37, 0xC9, 0xCD, 0x14, 0xC8, + 0xCD, 0xE0, 0xC8, 0xD8, 0xD5, 0x13, 0xCD, 0xE0, 0xC8, 0x30, 0xFA, 0xD1, 0xFE, 0x2E, 0x28, 0x05, + 0xCD, 0xCA, 0xC8, 0xA7, 0xC9, 0xCD, 0xF4, 0xC8, 0xA7, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0xE0, 0xC8, + 0x38, 0x09, 0x29, 0x29, 0x29, 0x29, 0x85, 0x6F, 0x13, 0x18, 0xF2, 0xFE, 0x48, 0xC0, 0x13, 0xC9, + 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x38, 0x09, 0xFE, 0x41, 0xD8, 0xFE, 0x47, 0x3F, 0xD8, 0xD6, + 0x07, 0xD6, 0x30, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0x10, 0xC9, 0x13, 0x38, 0x0D, 0xC5, 0x29, 0x44, + 0x4D, 0x29, 0x29, 0x09, 0xC1, 0xCD, 0x86, 0xC1, 0x18, 0xED, 0xFE, 0x2E, 0xC8, 0xC3, 0xBD, 0xC1, + 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x3F, 0xD8, 0xD6, 0x30, 0xC9, 0x01, 0x18, 0x00, 0x11, 0x00, + 0x01, 0x21, 0x29, 0xC9, 0xED, 0xB0, 0xC3, 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x00, 0x10, 0x21, + 0x00, 0xC0, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0x3E, 0x01, 0xD3, 0x40, 0xD1, 0xE1, 0xC1, 0xED, 0xB0, + 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x5A, 0xF5, 0x20, 0x01, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC4, 0x1B, + 0xC9, 0xCD, 0x06, 0xC8, 0x0D, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0xBA, 0x01, 0x00, 0x10, 0xCD, + 0xFC, 0xC7, 0x79, 0xCD, 0xF0, 0xC7, 0x0C, 0x10, 0xF6, 0xCD, 0x97, 0xCC, 0x06, 0x07, 0xCD, 0xFC, + 0xC7, 0x10, 0xFB, 0x60, 0x68, 0xCD, 0xFC, 0xC7, 0xE5, 0x11, 0x00, 0x10, 0x7C, 0xFE, 0xC9, 0x20, + 0x09, 0x7D, 0xFE, 0x8D, 0x38, 0x04, 0xFE, 0x9C, 0x38, 0x12, 0x46, 0x3E, 0x55, 0x77, 0xBE, 0x20, + 0x05, 0x2F, 0x77, 0xBE, 0x28, 0x05, 0x70, 0x3E, 0x58, 0x18, 0x09, 0x70, 0x23, 0x1B, 0x7A, 0xB3, + 0x20, 0xDA, 0x3E, 0x5E, 0xCD, 0xFE, 0xC7, 0xE1, 0x3E, 0x10, 0x84, 0x67, 0x20, 0xC7, 0xCD, 0x06, + 0xC8, 0x0D, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x28, + 0x65, 0x67, 0x2C, 0x20, 0x41, 0x3B, 0x20, 0x6F, 0x72, 0x20, 0x41, 0x3B, 0x3B, 0x20, 0x6F, 0x72, + 0x20, 0x41, 0x3B, 0x3B, 0x3B, 0x29, 0xA0, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x97, + 0xCC, 0xCD, 0xC1, 0xC2, 0xC2, 0xB9, 0xC1, 0xCD, 0x06, 0xC8, 0x53, 0x65, 0x65, 0x6B, 0x20, 0x74, + 0x65, 0x73, 0x74, 0x73, 0x3A, 0x8D, 0x06, 0x15, 0x21, 0x20, 0xCA, 0xDB, 0x31, 0x32, 0x7D, 0x00, + 0x7E, 0xC5, 0xE5, 0xFE, 0xFF, 0x20, 0x2E, 0xCD, 0x06, 0xC8, 0x20, 0x52, 0x65, 0x73, 0x74, 0x6F, + 0x72, 0x65, 0xBA, 0xCD, 0x04, 0xC6, 0xCD, 0x37, 0xCD, 0x08, 0xCD, 0x1E, 0xC6, 0x08, 0x18, 0x2C, + 0x01, 0x02, 0x03, 0x04, 0x05, 0xFE, 0x06, 0x07, 0x08, 0x09, 0x00, 0xFE, 0x27, 0x00, 0x15, 0x00, + 0x01, 0xFE, 0xFF, 0xFE, 0x27, 0xFE, 0xFE, 0x20, 0x06, 0xCD, 0x97, 0xCC, 0xB7, 0x18, 0x12, 0x32, + 0x75, 0x00, 0xCD, 0xDB, 0xC7, 0xCD, 0x06, 0xC8, 0xBA, 0xCD, 0xD4, 0xC4, 0xF5, 0xCD, 0x7A, 0xCB, + 0xF1, 0xE1, 0xC1, 0xDA, 0x97, 0xCC, 0x23, 0x10, 0xA2, 0xCD, 0x06, 0xC8, 0x0D, 0x52, 0x65, 0x61, + 0x64, 0x2F, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x65, 0x73, 0x74, 0x73, 0x8D, 0xCD, 0x94, + 0xCB, 0xCD, 0xB0, 0xCB, 0xDA, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, + 0x74, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x41, 0x59, 0x20, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, + 0x20, 0x64, 0x61, 0x74, 0x61, 0x0D, 0x45, 0x53, 0x43, 0x3D, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x20, + 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x3D, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x65, 0x64, 0xA0, 0xCD, + 0xA3, 0xC7, 0x21, 0x00, 0x09, 0xCD, 0xA2, 0xCB, 0x01, 0x00, 0x02, 0x75, 0x23, 0x0B, 0x78, 0xB1, + 0x20, 0xF9, 0xCD, 0xC1, 0xCB, 0x30, 0x6E, 0xCD, 0x06, 0xC8, 0x54, 0x65, 0x73, 0x74, 0x20, 0x66, + 0x61, 0x69, 0x6C, 0x65, 0x64, 0x21, 0x0D, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x20, 0x61, 0xF4, 0x1E, 0x01, 0xCD, 0xDE, 0xC5, 0xCD, 0x06, 0xC8, 0x20, 0x6D, 0x61, 0x79, 0x20, + 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6F, + 0x79, 0x65, 0x64, 0x0D, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x20, 0x69, 0x73, 0x20, + 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x30, 0x44, 0x30, 0x30, 0xF3, + 0x2A, 0x79, 0x00, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x20, 0x69, 0x6E, 0x20, 0x6D, 0x65, 0x6D, + 0x6F, 0x72, 0x79, 0x8D, 0xC9, 0x21, 0x00, 0x0B, 0xCD, 0xA2, 0xCB, 0xCD, 0xB0, 0xCB, 0x38, 0x87, + 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x61, + 0x72, 0x65, 0xA0, 0xED, 0x4B, 0x79, 0x00, 0x11, 0x00, 0x09, 0x21, 0x00, 0x0B, 0x1A, 0xBE, 0x20, + 0x09, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF5, 0x18, 0x01, 0x37, 0xCD, 0xD1, 0xCB, 0xCD, 0x94, + 0xCB, 0xCD, 0xC1, 0xCB, 0xDA, 0xC7, 0xCA, 0xC3, 0x97, 0xCC, 0x38, 0x06, 0xCD, 0x06, 0xC8, 0x4F, + 0xCB, 0xC9, 0xCD, 0x06, 0xC8, 0x65, 0x72, 0x72, 0x6F, 0x72, 0xA0, 0x3A, 0x71, 0x00, 0xCD, 0xE7, + 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x44, 0x61, 0x74, 0xE1, 0x21, 0x00, 0x0D, 0x22, 0x7B, + 0x00, 0xC9, 0x22, 0x7B, 0x00, 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0xEE, 0xC9, + 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x72, 0x65, 0x61, 0x64, 0xA0, 0xCD, 0x52, 0xCD, 0x18, + 0x10, 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0xA0, 0xCD, 0xAB, + 0xC5, 0xF5, 0xCD, 0x7A, 0xCB, 0xCD, 0x97, 0xCC, 0xF1, 0xC9, 0x3E, 0x27, 0x32, 0x75, 0x00, 0x32, + 0x7D, 0x00, 0x3E, 0x01, 0x32, 0x76, 0x00, 0xC3, 0xCB, 0xCD, 0xC5, 0xE5, 0x06, 0x00, 0x21, 0x02, + 0x00, 0x19, 0xCD, 0x64, 0xCC, 0xFE, 0x10, 0xCC, 0xBB, 0xCC, 0x28, 0xF6, 0xFE, 0x1B, 0xCA, 0x8E, + 0xCC, 0xFE, 0x08, 0x28, 0x04, 0xFE, 0x7F, 0x20, 0x11, 0xAF, 0xB0, 0x28, 0xE5, 0x2B, 0x05, 0xCD, + 0x4B, 0xCC, 0x7E, 0xFE, 0x20, 0xDC, 0x4B, 0xCC, 0x18, 0xD8, 0xFE, 0x0D, 0xCC, 0x45, 0xCC, 0x28, + 0x1B, 0xFE, 0x15, 0x20, 0x08, 0xCD, 0x45, 0xCC, 0xCD, 0x97, 0xCC, 0x18, 0xBF, 0x4F, 0x1A, 0x3D, + 0xB8, 0x28, 0xBF, 0x79, 0xCD, 0x45, 0xCC, 0x77, 0x23, 0x04, 0x18, 0xB6, 0x36, 0x00, 0x78, 0x13, + 0x12, 0x1B, 0xE1, 0xC1, 0xC9, 0xF5, 0xCD, 0x52, 0xCC, 0xF1, 0xC9, 0xCD, 0x06, 0xC8, 0x08, 0x20, + 0x88, 0xC9, 0xFE, 0x20, 0x30, 0x43, 0xFE, 0x0D, 0x28, 0x3F, 0xF5, 0xCD, 0x06, 0xC8, 0xDE, 0xF1, + 0xC6, 0x40, 0x18, 0x35, 0xCD, 0x23, 0xCD, 0xCD, 0x76, 0xCC, 0x28, 0xF8, 0xCD, 0x76, 0xCC, 0x28, + 0xFB, 0xDB, 0x01, 0xE6, 0x7F, 0xC9, 0xDB, 0x00, 0xE6, 0x40, 0xC9, 0xCD, 0x76, 0xCC, 0xC8, 0xCD, + 0x6C, 0xCC, 0xFE, 0x13, 0xCC, 0x6C, 0xCC, 0xFE, 0x0D, 0x28, 0x03, 0xFE, 0x1B, 0xC0, 0x31, 0x2E, + 0x00, 0xCD, 0x97, 0xCC, 0xC3, 0x55, 0xC1, 0x3E, 0x0D, 0xF5, 0xD9, 0xCB, 0x78, 0xD9, 0xC4, 0xCB, + 0xCC, 0xCD, 0x23, 0xCD, 0xDB, 0x00, 0xE6, 0x80, 0x28, 0xFA, 0xF1, 0xD3, 0x01, 0xFE, 0x0D, 0xC0, + 0x3E, 0x0A, 0xCD, 0x99, 0xCC, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0xC0, 0xF5, 0xD9, 0x3E, 0x80, 0xA8, + 0x47, 0xCB, 0x78, 0xD9, 0xC4, 0xC9, 0xCC, 0xF1, 0xC9, 0x3E, 0x11, 0xF5, 0xE5, 0x21, 0x08, 0x00, + 0xCD, 0xF1, 0xCF, 0xE1, 0xF1, 0xF5, 0xFE, 0x11, 0x28, 0x13, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0x20, + 0x05, 0xCD, 0xBB, 0xCC, 0xF1, 0xC9, 0xDB, 0x54, 0x2F, 0xE6, 0x20, 0x28, 0xED, 0xF1, 0xCB, 0xFF, + 0xD3, 0x54, 0xCB, 0xBF, 0xD3, 0x54, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xC9, 0xCD, 0x14, 0xC8, + 0xFE, 0x4F, 0xC2, 0xBD, 0xC1, 0x13, 0x1A, 0xFE, 0x4E, 0x28, 0x0E, 0xFE, 0x46, 0xC2, 0xBD, 0xC1, + 0xD9, 0xCB, 0xA8, 0xD9, 0x3E, 0xFF, 0xD3, 0x04, 0xC9, 0xD9, 0xCB, 0xE8, 0xD9, 0x3E, 0x01, 0x32, + 0x72, 0x00, 0xC9, 0xD9, 0xCB, 0x68, 0xD9, 0xC8, 0xC5, 0xCD, 0xA8, 0xCF, 0xC1, 0xD3, 0x34, 0xCD, + 0xD6, 0xCF, 0xEE, 0xA0, 0xD3, 0x04, 0xC9, 0x16, 0x02, 0xD5, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0xD5, + 0x3E, 0x0A, 0x32, 0x75, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0x15, 0x20, 0xE9, + 0x18, 0x18, 0x16, 0x0A, 0xD5, 0xCD, 0x58, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x18, 0x0B, 0x16, + 0x04, 0xD5, 0xCD, 0x9F, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x3A, 0x71, 0x00, 0x4F, 0x37, 0xC9, + 0x97, 0x32, 0x75, 0x00, 0x32, 0x78, 0x00, 0xCD, 0x55, 0xCF, 0xD3, 0x34, 0xCD, 0x25, 0xCE, 0x38, + 0x0E, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x3B, 0x1F, 0x30, 0xF7, 0xC3, 0x3E, 0xCE, 0x3E, + 0xC4, 0xD3, 0x30, 0xCD, 0xD6, 0xCF, 0xE6, 0x57, 0xD3, 0x04, 0xCD, 0xE4, 0xCF, 0xDB, 0x04, 0xE6, + 0x40, 0x20, 0x16, 0xCD, 0xCF, 0xCF, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x18, 0x1F, 0x30, 0xF7, 0x3E, + 0xD0, 0xD3, 0x30, 0x97, 0xD3, 0x31, 0xC3, 0x50, 0xCE, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x05, 0x1F, + 0x30, 0xDB, 0x18, 0xCB, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0x97, 0xCD, 0x55, 0xCF, 0xD3, + 0x34, 0x3A, 0x75, 0x00, 0xD3, 0x33, 0x4F, 0x3A, 0x76, 0x00, 0xD3, 0x32, 0x3A, 0x7D, 0x00, 0xD3, + 0x31, 0x91, 0xCA, 0xCF, 0xCF, 0xCD, 0x25, 0xCE, 0x38, 0x0F, 0xF6, 0x10, 0xD3, 0x30, 0xDB, 0x34, + 0xCB, 0x57, 0x20, 0xD0, 0x1F, 0x30, 0xF7, 0x18, 0x45, 0xCD, 0xD6, 0xCF, 0xE6, 0x4F, 0xD3, 0x04, + 0x3E, 0x18, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xBA, 0x1F, 0x30, 0xF7, 0xDB, 0x30, 0x2E, + 0x32, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0x2D, 0x20, 0xF7, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, + 0xCD, 0xCF, 0xCF, 0x18, 0x2B, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x0A, 0xCB, 0x5F, 0x3E, 0x0E, + 0x28, 0x02, 0x3E, 0x0C, 0xA7, 0xC9, 0xCB, 0x5F, 0x3E, 0x0F, 0x28, 0xF8, 0x37, 0xC9, 0xCD, 0xCF, + 0xCF, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x98, 0x37, 0xC0, + 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xA7, 0xC9, 0xCD, 0x8F, 0xCE, 0xD3, 0x30, 0xDB, 0x34, 0x1F, + 0x38, 0x16, 0xED, 0xA2, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0E, 0xED, 0xA2, 0xC2, 0x5D, 0xCE, 0xDB, + 0x34, 0xCB, 0x4F, 0x20, 0x10, 0x1F, 0x30, 0xF7, 0xCD, 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, + 0xE6, 0x9C, 0xC8, 0x18, 0x08, 0xCD, 0xCF, 0xCF, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0xCD, + 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0x88, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xC9, 0xCD, + 0xE4, 0xCF, 0xCD, 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0xA8, 0x5F, 0x7A, 0xD3, 0x34, + 0x7B, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x12, 0xED, 0xA3, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0A, + 0xED, 0xA3, 0xC2, 0xB3, 0xCE, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xCD, 0xCF, 0xCF, 0xCD, 0xE4, 0xCF, + 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0xFC, 0x37, 0xC0, 0xA7, 0x3A, 0x7E, 0x00, 0xCB, 0x4F, 0xC8, + 0xCD, 0x00, 0xCF, 0x38, 0x0A, 0xDB, 0x34, 0x1F, 0x38, 0x04, 0xDB, 0x33, 0x18, 0xF7, 0x1C, 0xCD, + 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x9C, 0x37, 0xC0, 0x7B, 0xA7, 0xC8, 0x37, 0xC9, + 0xCD, 0x8F, 0xCE, 0xED, 0x4B, 0x79, 0x00, 0xCB, 0x38, 0xCB, 0x19, 0xCB, 0x38, 0xCB, 0x19, 0x41, + 0x1E, 0x00, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, + 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, + 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0x10, 0xDA, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xC9, + 0xDB, 0x33, 0x3E, 0x80, 0xCD, 0x55, 0xCF, 0x2A, 0x79, 0x00, 0xCB, 0x1C, 0xCB, 0x1D, 0x45, 0x0E, + 0x33, 0x2A, 0x7B, 0x00, 0xC9, 0x4F, 0xDB, 0x34, 0xE6, 0x04, 0x28, 0x04, 0x97, 0x32, 0x72, 0x00, + 0xCD, 0xA8, 0xCF, 0xD3, 0x34, 0xF5, 0xE5, 0xCD, 0xD6, 0xCF, 0xE6, 0x5F, 0xD3, 0x04, 0x3A, 0x72, + 0x00, 0xA7, 0x28, 0x21, 0x21, 0x90, 0x01, 0x3A, 0x73, 0x00, 0xB8, 0x20, 0x1B, 0x3A, 0x74, 0x00, + 0x67, 0x3A, 0x78, 0x00, 0x32, 0x74, 0x00, 0xBC, 0x20, 0x06, 0xDB, 0x34, 0xE6, 0x20, 0x20, 0x0B, + 0xCD, 0xE4, 0xCF, 0x18, 0x06, 0x21, 0x20, 0x4E, 0xCD, 0xF1, 0xCF, 0xE1, 0x78, 0x32, 0x73, 0x00, + 0x3E, 0x01, 0x32, 0x72, 0x00, 0xF1, 0xB1, 0xC9, 0x3A, 0x77, 0x00, 0x47, 0x04, 0x97, 0x37, 0x17, + 0x10, 0xFD, 0x47, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x28, 0x02, 0xCB, 0xE0, 0xCB, 0x47, 0x28, 0x02, + 0xCB, 0xF0, 0x78, 0xF6, 0x20, 0xC9, 0xDB, 0x34, 0x2F, 0xE6, 0x20, 0xC8, 0x3E, 0x04, 0xC9, 0xCD, + 0xD6, 0xCF, 0xD3, 0x04, 0xAF, 0xC9, 0xC5, 0x06, 0x7F, 0x3A, 0x78, 0x00, 0xA7, 0x28, 0x02, 0x06, + 0x7D, 0x78, 0xC1, 0xC9, 0x21, 0x08, 0x00, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x03, 0x21, 0x0C, + 0x00, 0xC5, 0x2B, 0x06, 0x1C, 0x10, 0xFE, 0x00, 0x00, 0x7D, 0xB4, 0x20, 0xF5, 0xC1, 0xC9, 0xFF, + 0xF3, 0x18, 0x3C, 0xC3, 0x30, 0xC0, 0xC3, 0x04, 0xC5, 0xC3, 0x37, 0xC0, 0xC3, 0x3B, 0xC0, 0xC3, + 0x9E, 0xCB, 0xC3, 0x37, 0xCD, 0xC3, 0xD4, 0xC4, 0xC3, 0x52, 0xCD, 0xC3, 0xAB, 0xC5, 0xC3, 0x4B, + 0xC2, 0xC3, 0x6C, 0xCC, 0xC3, 0x76, 0xCC, 0xC3, 0xEA, 0xCB, 0xC3, 0xFE, 0xC7, 0xC3, 0x97, 0xCC, + 0x32, 0x77, 0x00, 0x78, 0xC3, 0x87, 0xC3, 0x32, 0x75, 0x00, 0xC9, 0x32, 0x76, 0x00, 0xC9, 0xAF, + 0xD3, 0x03, 0x47, 0xD9, 0x2F, 0xD3, 0x04, 0x3E, 0xD0, 0xD3, 0x30, 0x21, 0x2E, 0x00, 0xF9, 0x25, + 0x20, 0xFD, 0x74, 0x2C, 0x20, 0xFC, 0x24, 0x22, 0x62, 0x00, 0x22, 0x64, 0x00, 0xCD, 0x4B, 0xC2, + 0xDB, 0x34, 0xE6, 0x40, 0xC2, 0x39, 0xC1, 0xDB, 0x04, 0x2F, 0xE6, 0x03, 0x32, 0x77, 0x00, 0xCD, + 0x06, 0xC8, 0x50, 0x72, 0x65, 0x70, 0x61, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, + 0x6F, 0x6F, 0x74, 0x2C, 0x20, 0x45, 0x53, 0x43, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x62, 0x6F, 0x72, + 0x74, 0x8D, 0xCD, 0xA8, 0xCF, 0x57, 0xD3, 0x34, 0x06, 0x64, 0xCD, 0x28, 0xC1, 0x21, 0x64, 0x00, + 0xCD, 0xF1, 0xCF, 0x10, 0xF5, 0x32, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x32, 0x78, 0x00, 0x3C, 0x32, + 0x76, 0x00, 0xCD, 0xF7, 0xC1, 0x32, 0x7E, 0x00, 0x21, 0x80, 0x00, 0x22, 0x7B, 0x00, 0x22, 0x79, + 0x00, 0x3E, 0x42, 0x32, 0x70, 0x00, 0x06, 0x02, 0xC5, 0xCD, 0x37, 0xCD, 0xD4, 0xCB, 0xCD, 0xD4, + 0x52, 0xCD, 0xC1, 0x30, 0x13, 0xCD, 0x28, 0xC1, 0x10, 0xEE, 0xCD, 0x06, 0xC8, 0x0D, 0x55, 0x6E, + 0x61, 0x62, 0x6C, 0x65, 0x20, 0xF4, 0x18, 0x2D, 0x3A, 0x80, 0x00, 0xFE, 0x40, 0x28, 0x04, 0xFE, + 0xE5, 0x20, 0x07, 0xCD, 0x06, 0xC8, 0x0D, 0xCE, 0x18, 0x1B, 0xCD, 0x28, 0xC1, 0xCD, 0xA8, 0xCF, + 0x57, 0x3A, 0x77, 0x00, 0x08, 0xCD, 0x06, 0xC8, 0x0D, 0x53, 0x74, 0x61, 0x6E, 0x64, 0x62, 0x79, + 0x8D, 0x37, 0xC3, 0x80, 0x00, 0xCD, 0x06, 0xC8, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x8D, 0x3A, + 0x71, 0x00, 0xB7, 0xC4, 0x59, 0xC3, 0x18, 0x09, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, 0xCC, 0xFE, 0x1B, + 0xC0, 0x3E, 0xD0, 0xD3, 0x30, 0x3E, 0x7C, 0xD3, 0x04, 0xCD, 0x06, 0xC8, 0x0D, 0x43, 0x72, 0x6F, + 0x6D, 0x65, 0x6D, 0x63, 0x6F, 0x20, 0x52, 0x44, 0x4F, 0x53, 0x20, 0x30, 0x32, 0x2E, 0x35, 0x32, + 0x8D, 0xAF, 0x32, 0x6C, 0x00, 0x3A, 0x7E, 0x00, 0x47, 0xCD, 0xE2, 0xC1, 0x31, 0x2E, 0x00, 0xCD, + 0x06, 0xC8, 0xBB, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xA7, 0x28, 0xE5, 0xCD, 0xC1, 0xC2, 0x28, + 0xE4, 0x78, 0xFE, 0x17, 0x30, 0x47, 0x87, 0x21, 0x8B, 0xC1, 0xCD, 0x86, 0xC1, 0x7E, 0x23, 0x66, + 0x6F, 0xCD, 0xC7, 0xC1, 0x18, 0xCF, 0x85, 0x6F, 0xD0, 0x24, 0xC9, 0xFD, 0xCC, 0xC8, 0xC1, 0xBD, + 0xC1, 0x2B, 0xC7, 0x2A, 0xC6, 0xBD, 0xC1, 0xC4, 0xC1, 0xBD, 0xC1, 0x45, 0xC2, 0xBD, 0xC1, 0xBD, + 0xC1, 0x39, 0xC4, 0x45, 0xC6, 0xBD, 0xC1, 0x36, 0xC6, 0xBD, 0xC1, 0xC2, 0xC6, 0x16, 0xC5, 0x7A, + 0xC6, 0x41, 0xC9, 0xBD, 0xC1, 0x52, 0xC6, 0x1A, 0xC5, 0xAF, 0x32, 0x6C, 0x00, 0xCD, 0x06, 0xC8, + 0x3F, 0x8D, 0x18, 0x91, 0xCD, 0x53, 0xC8, 0xE9, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x67, 0xC0, 0xFE, + 0x45, 0xD2, 0x2A, 0xC4, 0xD6, 0x41, 0xDA, 0x2A, 0xC4, 0xF5, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC3, + 0x6C, 0xC0, 0x3A, 0x6C, 0x00, 0xB7, 0xC8, 0xCD, 0xFE, 0xC7, 0xCB, 0x58, 0xC0, 0xCB, 0x78, 0x3E, + 0x3B, 0xC4, 0xFE, 0xC7, 0xC3, 0xFE, 0xC7, 0x7A, 0xD3, 0x34, 0x3E, 0xDF, 0xD3, 0x04, 0x3E, 0xD4, + 0xD3, 0x30, 0xDB, 0x34, 0x0F, 0x30, 0xFB, 0xDB, 0x30, 0x01, 0x00, 0x02, 0xCD, 0x3B, 0xC2, 0x3E, + 0xD4, 0xD3, 0x30, 0xCD, 0x34, 0xC2, 0x28, 0xDF, 0xDB, 0x34, 0x0F, 0x30, 0xF6, 0xDB, 0x30, 0x10, + 0xEE, 0xAF, 0xD3, 0x03, 0x79, 0xFE, 0x5A, 0x30, 0x03, 0x87, 0x87, 0x87, 0xFE, 0xB7, 0x3E, 0x80, + 0xD0, 0x3E, 0x04, 0xC9, 0xDB, 0x03, 0xFE, 0xC7, 0xC0, 0x0C, 0xC8, 0x3E, 0x01, 0xD3, 0x03, 0x3E, + 0xFA, 0xD3, 0x05, 0x18, 0xEF, 0xCD, 0x27, 0xC8, 0xCD, 0x97, 0xCC, 0xDB, 0x04, 0xE6, 0x08, 0x28, + 0x5D, 0x3E, 0x0A, 0xD3, 0x02, 0x21, 0xD0, 0x07, 0xCD, 0xF1, 0xCF, 0x3E, 0x08, 0xD3, 0x02, 0x16, + 0x64, 0x15, 0x28, 0xED, 0x21, 0xB9, 0xC2, 0x0E, 0x00, 0x3E, 0x19, 0x06, 0x09, 0xD3, 0x02, 0xED, + 0xA3, 0x28, 0xEE, 0xCD, 0x98, 0xC2, 0xCD, 0x98, 0xC2, 0x38, 0xE9, 0xFE, 0x0D, 0x3E, 0x09, 0x20, + 0xEC, 0x3E, 0x0D, 0xD3, 0x01, 0x21, 0xA0, 0x0F, 0xCD, 0xF1, 0xCF, 0xCD, 0x76, 0xCC, 0xC4, 0x6C, + 0xCC, 0xFE, 0x0D, 0x28, 0xEC, 0xC3, 0x97, 0xCC, 0xD5, 0x11, 0xA0, 0x8C, 0xCD, 0x76, 0xCC, 0x28, + 0x05, 0xCD, 0x6C, 0xCC, 0x18, 0x06, 0x1B, 0x7A, 0xB3, 0x20, 0xF1, 0x37, 0xD1, 0xC9, 0x3E, 0x09, + 0xD3, 0x02, 0x3E, 0x84, 0xD3, 0x00, 0xC3, 0x97, 0xCC, 0x90, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, + 0x01, 0xD6, 0x41, 0xDA, 0xBD, 0xC1, 0x47, 0x13, 0x1A, 0xFE, 0x3B, 0xC0, 0x78, 0xFE, 0x04, 0xD2, + 0x2A, 0xC4, 0xC6, 0x41, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x0C, + 0x20, 0x0B, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x04, 0x20, 0x03, 0x06, 0x80, 0x13, 0xC5, 0xCD, 0xB7, + 0xC3, 0xC1, 0x70, 0x30, 0x06, 0x3A, 0x6D, 0x00, 0xB0, 0x77, 0x37, 0xF5, 0xCD, 0x84, 0xC3, 0x32, + 0x78, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x32, 0x76, 0x00, 0xCD, 0x04, 0xC6, 0x3E, 0x48, 0x32, 0x70, + 0x00, 0xCD, 0x37, 0xCD, 0xF5, 0xCD, 0x1E, 0xC6, 0xF1, 0xDA, 0x0D, 0xC4, 0xF1, 0xDA, 0xB5, 0xC3, + 0xCD, 0x04, 0xC6, 0x21, 0x00, 0x01, 0x22, 0x7B, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x52, 0xCD, 0xF5, + 0xCD, 0x1E, 0xC6, 0xF1, 0x30, 0x2D, 0x3A, 0x71, 0x00, 0xB7, 0xF2, 0x45, 0xC3, 0x21, 0x64, 0x00, + 0xCD, 0xF1, 0xCF, 0x18, 0xDB, 0xCD, 0x06, 0xC8, 0x43, 0x61, 0x6E, 0x27, 0x74, 0x20, 0x72, 0x65, + 0x61, 0x64, 0x20, 0x4C, 0x61, 0x62, 0x65, 0x6C, 0x8D, 0x1E, 0x01, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, + 0xC4, 0xAF, 0xC9, 0x01, 0x10, 0x00, 0x11, 0x7A, 0x01, 0xCD, 0xEE, 0xC3, 0x0E, 0x01, 0xCD, 0xEE, + 0xC3, 0x3A, 0x78, 0x01, 0xFE, 0x43, 0x20, 0x02, 0xCB, 0xC8, 0x3A, 0x7E, 0x00, 0xE6, 0xCC, 0xB0, + 0x2A, 0x6A, 0x00, 0x77, 0xCD, 0xF6, 0xC3, 0x32, 0x7E, 0x00, 0xCB, 0x47, 0x11, 0x0A, 0x10, 0x21, + 0x00, 0x02, 0x20, 0x0D, 0xCB, 0x4F, 0x11, 0x05, 0x08, 0x20, 0x06, 0x11, 0x12, 0x1A, 0x21, 0x80, + 0x00, 0x22, 0x79, 0x00, 0xCB, 0x7F, 0x3E, 0x4C, 0x42, 0x28, 0x03, 0x3E, 0x27, 0x43, 0x32, 0x6F, + 0x00, 0x78, 0x32, 0x6E, 0x00, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0x01, 0x10, 0x00, 0xCD, + 0xDE, 0xC3, 0x0E, 0x01, 0xCD, 0xDE, 0xC3, 0xCD, 0x14, 0xC8, 0xFE, 0x43, 0x20, 0x06, 0xCB, 0xC8, + 0x13, 0xCD, 0x14, 0xC8, 0xB7, 0xC2, 0xB9, 0xC1, 0x78, 0x32, 0x6D, 0x00, 0x37, 0xC9, 0xCD, 0x14, + 0xC8, 0x13, 0xFE, 0x53, 0xC8, 0xFE, 0x44, 0xC2, 0xB9, 0xC1, 0x78, 0xB1, 0x47, 0xC9, 0x1A, 0x13, + 0x13, 0xFE, 0x44, 0xC0, 0x18, 0xF4, 0x3A, 0x6C, 0x00, 0xD6, 0x41, 0x32, 0x77, 0x00, 0x21, 0x66, + 0x00, 0xCD, 0x86, 0xC1, 0x22, 0x6A, 0x00, 0x7E, 0xB7, 0xC9, 0xCD, 0xF2, 0xC5, 0xCD, 0x13, 0xC4, + 0xC3, 0x55, 0xC1, 0xCD, 0x06, 0xC8, 0x20, 0x45, 0x72, 0x72, 0xAD, 0x3A, 0x70, 0x00, 0xCD, 0xFE, + 0xC7, 0x3A, 0x71, 0x00, 0xCD, 0xDB, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x41, 0x2D, 0x44, + 0x20, 0x6F, 0x6E, 0x6C, 0x79, 0x8D, 0xC3, 0x55, 0xC1, 0xCD, 0x27, 0xC8, 0x3A, 0x77, 0x00, 0xF5, + 0x3A, 0x6C, 0x00, 0xF5, 0x3E, 0x41, 0xF5, 0x32, 0x6C, 0x00, 0xCD, 0xF6, 0xC3, 0x2A, 0x6A, 0x00, + 0x46, 0xC4, 0x63, 0xC4, 0xF1, 0x3C, 0xFE, 0x45, 0x38, 0xEC, 0xF1, 0x32, 0x6C, 0x00, 0xF1, 0x32, + 0x77, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x20, 0xA0, 0xCD, 0xE2, 0xC1, 0xCD, 0x06, 0xC8, 0x3B, 0xA0, + 0xCB, 0x58, 0xC4, 0xFC, 0xC7, 0xCB, 0x78, 0xCC, 0xFC, 0xC7, 0xCB, 0x60, 0xCD, 0x98, 0xC4, 0xCD, + 0xFC, 0xC7, 0xCB, 0x40, 0xCD, 0x98, 0xC4, 0xCB, 0x48, 0x28, 0x0A, 0xCD, 0x06, 0xC8, 0x20, 0x43, + 0x72, 0x6F, 0x6D, 0x69, 0xF8, 0xC3, 0x97, 0xCC, 0x3E, 0x44, 0x20, 0x02, 0x3E, 0x53, 0xC3, 0xFE, + 0xC7, 0x47, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, 0xBD, 0xC1, 0x78, 0xFE, 0x53, 0x28, 0x2C, 0xCD, 0xB0, + 0xC8, 0xDA, 0xBD, 0xC1, 0xE5, 0xCD, 0xE1, 0xC4, 0x3E, 0x53, 0x32, 0x70, 0x00, 0xE1, 0xAF, 0xB4, + 0xC2, 0xBD, 0xC1, 0x3A, 0x6F, 0x00, 0xBD, 0x38, 0xE8, 0x7D, 0x32, 0x75, 0x00, 0xCD, 0xD4, 0xC4, + 0xD0, 0xC3, 0x0D, 0xC4, 0xCD, 0xF8, 0xC5, 0xC3, 0xCB, 0xCD, 0x13, 0xCD, 0xE1, 0xC4, 0xC3, 0xCB, + 0xCD, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xCD, 0xAD, 0xC8, 0xD8, 0xAF, 0xB4, 0xC2, 0xBD, 0xC1, + 0x7D, 0xB7, 0x20, 0x04, 0x32, 0x78, 0x00, 0xC9, 0x3D, 0x20, 0xF2, 0xCD, 0xF6, 0xC3, 0xCB, 0x67, + 0x28, 0x06, 0x3E, 0x01, 0x32, 0x78, 0x00, 0xC9, 0xCD, 0x06, 0xC8, 0x53, 0x2E, 0x53, 0x69, 0x64, + 0x65, 0x64, 0x8D, 0xC3, 0x55, 0xC1, 0x06, 0x52, 0x18, 0x02, 0x06, 0x57, 0x1A, 0xFE, 0x44, 0xC2, + 0xBD, 0xC1, 0x78, 0x32, 0x70, 0x00, 0xCD, 0xBB, 0xC5, 0xE5, 0xFD, 0xE1, 0x60, 0x69, 0xD5, 0xE5, + 0x7B, 0x32, 0x76, 0x00, 0xFD, 0x22, 0x7B, 0x00, 0xCD, 0xD4, 0xC4, 0x3A, 0x70, 0x00, 0xFE, 0x57, + 0x37, 0x3F, 0xCC, 0xAB, 0xC5, 0x38, 0x08, 0x3A, 0x70, 0x00, 0xFE, 0x52, 0xCC, 0x52, 0xCD, 0xE1, + 0xD1, 0xDA, 0x0A, 0xC4, 0xED, 0x4B, 0x79, 0x00, 0xFD, 0x09, 0x03, 0xED, 0x42, 0x23, 0xDA, 0xF2, + 0xC5, 0x1C, 0x3A, 0x6E, 0x00, 0xBB, 0x30, 0xC6, 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0x3C, 0x47, + 0x3A, 0x6F, 0x00, 0xB8, 0x38, 0x08, 0x78, 0x32, 0x75, 0x00, 0x1E, 0x01, 0x18, 0xB0, 0x1D, 0xCD, + 0xF2, 0xC5, 0xCD, 0x06, 0xC8, 0x4E, 0x65, 0x78, 0x74, 0x20, 0x6D, 0x65, 0x6D, 0x6F, 0x72, 0x79, + 0x3A, 0xA0, 0xFD, 0xE5, 0xE1, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x0D, 0x45, 0x6E, 0x64, 0x20, + 0x6F, 0x66, 0x20, 0x44, 0x69, 0x73, 0x6B, 0x8D, 0xC3, 0x55, 0xC1, 0x21, 0x7E, 0x00, 0x46, 0xC5, + 0xCB, 0xCE, 0xCD, 0x5F, 0xCD, 0xC1, 0x78, 0x32, 0x7E, 0x00, 0xC9, 0x3A, 0x6C, 0x00, 0xB7, 0xCA, + 0xBD, 0xC1, 0x13, 0xCD, 0x2F, 0xC8, 0xAF, 0xB2, 0xC2, 0xBD, 0xC1, 0xB3, 0xCA, 0xBD, 0xC1, 0xC5, + 0xD5, 0xE5, 0xCD, 0xF8, 0xC5, 0xE1, 0xD1, 0xC1, 0x3A, 0x6E, 0x00, 0xBB, 0x38, 0xEA, 0xDB, 0x31, + 0x57, 0xEB, 0xCD, 0xFC, 0xC7, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, 0xFC, 0xC7, 0x3A, 0x78, 0x00, 0xC3, + 0xF0, 0xC7, 0xCD, 0xDE, 0xC5, 0xC3, 0x97, 0xCC, 0x3A, 0x75, 0x00, 0xB7, 0x20, 0x20, 0x3A, 0x78, + 0x00, 0xB7, 0x20, 0x1A, 0x3A, 0x7E, 0x00, 0xCB, 0x6F, 0xC0, 0xCB, 0x4F, 0x20, 0x03, 0xCB, 0x47, + 0xC8, 0x32, 0x7F, 0x00, 0xCB, 0xEF, 0xCB, 0x87, 0x32, 0x7E, 0x00, 0xC3, 0x9B, 0xC3, 0x3A, 0x7E, + 0x00, 0xCB, 0x6F, 0xC8, 0x3A, 0x7F, 0x00, 0xC3, 0x87, 0xC3, 0xCD, 0x53, 0xC8, 0x4D, 0xED, 0x78, + 0xCD, 0xE7, 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0xAD, 0xC8, 0xE5, 0xCD, 0x1B, 0xC8, 0xCD, 0x53, 0xC8, + 0xD1, 0x4D, 0xED, 0x59, 0xC9, 0xCD, 0x2F, 0xC8, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xE1, 0xD1, 0xC1, + 0x18, 0x03, 0xCD, 0x2F, 0xC8, 0x1A, 0xBE, 0x28, 0x19, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, + 0xCD, 0xFC, 0xC7, 0x1A, 0xCD, 0xDB, 0xC7, 0xCD, 0xFC, 0xC7, 0xEB, 0xCD, 0xE2, 0xC7, 0xEB, 0xCD, + 0x97, 0xCC, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xDC, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, 0xC2, + 0xA1, 0xC4, 0x13, 0x2A, 0x64, 0x00, 0xCD, 0x5B, 0xC8, 0xCD, 0xE2, 0xC7, 0x7E, 0xCD, 0xDB, 0xC7, + 0xCD, 0xFC, 0xC7, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xFE, 0x2E, 0xC8, 0xFE, 0x2D, 0x20, 0x03, + 0x2B, 0x18, 0x1A, 0xCD, 0xF1, 0xC6, 0x30, 0x07, 0xCD, 0x06, 0xC8, 0x3F, 0x8D, 0x18, 0xDA, 0xAF, + 0x80, 0x20, 0x03, 0x23, 0x18, 0x07, 0x48, 0x06, 0x00, 0xEB, 0xED, 0xB0, 0xEB, 0x22, 0x64, 0x00, + 0x18, 0xC7, 0xCD, 0x65, 0xC8, 0xC5, 0xCD, 0xF1, 0xC6, 0xD1, 0xDA, 0xBD, 0xC1, 0xAF, 0x80, 0xCA, + 0xBD, 0xC1, 0xC5, 0xD5, 0xE5, 0x11, 0x2E, 0x00, 0x1A, 0xBE, 0x20, 0x04, 0x13, 0x23, 0x10, 0xF8, + 0xE1, 0xE5, 0x06, 0x10, 0xCC, 0x60, 0xC7, 0xE1, 0xD1, 0xC1, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xE2, + 0xC9, 0xE5, 0x06, 0x00, 0x21, 0x2E, 0x00, 0xCD, 0x14, 0xC8, 0xB7, 0x28, 0x29, 0xFE, 0x2C, 0x13, + 0x28, 0xF5, 0x4F, 0xFE, 0x27, 0x28, 0x12, 0xFE, 0x22, 0x28, 0x0E, 0x1B, 0xE5, 0xCD, 0xAD, 0xC8, + 0x7D, 0xE1, 0x38, 0x12, 0x77, 0x23, 0x04, 0x18, 0xDE, 0x1A, 0x13, 0xB7, 0x28, 0x08, 0xB9, 0x28, + 0xD6, 0x77, 0x23, 0x04, 0x18, 0xF3, 0x11, 0x2E, 0x00, 0xE1, 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x4D, + 0x20, 0x01, 0x13, 0x01, 0x80, 0x00, 0x2A, 0x62, 0x00, 0xCD, 0x4E, 0xC8, 0x1E, 0x10, 0xAF, 0xB0, + 0x20, 0x0A, 0x3E, 0x0F, 0xB9, 0x38, 0x05, 0xAF, 0xB1, 0x28, 0x01, 0x59, 0xC5, 0x43, 0xCD, 0x60, + 0xC7, 0x22, 0x62, 0x00, 0xC1, 0x79, 0x93, 0x4F, 0x30, 0x01, 0x05, 0x78, 0xB1, 0x20, 0xDD, 0xC9, + 0xCD, 0xE2, 0xC7, 0xC5, 0xE5, 0xD5, 0x0E, 0x00, 0x1E, 0x04, 0x3E, 0x03, 0xA1, 0xCC, 0xA0, 0xC7, + 0xCD, 0xA0, 0xC7, 0x7E, 0xCD, 0xE7, 0xC7, 0x1C, 0x1C, 0x23, 0x0C, 0x10, 0xED, 0xCD, 0xA0, 0xC7, + 0x3E, 0x3A, 0xBB, 0x20, 0xF8, 0xD1, 0xE1, 0xC1, 0x7E, 0x23, 0xCD, 0x92, 0xC7, 0x10, 0xF9, 0xC3, + 0x97, 0xCC, 0xE6, 0x7F, 0xFE, 0x7F, 0x28, 0x04, 0xFE, 0x20, 0x30, 0x62, 0x3E, 0x2E, 0x18, 0x5E, + 0x1C, 0x18, 0x59, 0x3E, 0x31, 0x11, 0x2E, 0x00, 0x12, 0xD5, 0xCD, 0xEA, 0xCB, 0xE3, 0x23, 0x46, + 0x04, 0x23, 0xE5, 0x05, 0x28, 0x17, 0x7E, 0xCD, 0xD2, 0xC7, 0x77, 0x23, 0xFE, 0x22, 0x28, 0x04, + 0xFE, 0x27, 0x20, 0xEF, 0x05, 0x28, 0x06, 0xBE, 0x23, 0x20, 0xF9, 0x18, 0xE6, 0x36, 0x00, 0xD1, + 0xE1, 0xC9, 0xFE, 0x61, 0xD8, 0xFE, 0x7B, 0xD0, 0xD6, 0x20, 0xC9, 0xF5, 0xCD, 0xFC, 0xC7, 0xF1, + 0x18, 0x05, 0x7C, 0xCD, 0xE7, 0xC7, 0x7D, 0xF5, 0x1F, 0x1F, 0x1F, 0x1F, 0xCD, 0xF0, 0xC7, 0xF1, + 0xE6, 0x0F, 0xFE, 0x0A, 0x38, 0x02, 0xC6, 0x07, 0xC6, 0x30, 0x18, 0x02, 0x3E, 0x20, 0xF5, 0xE6, + 0x7F, 0xCD, 0x99, 0xCC, 0xF1, 0xC9, 0xE3, 0x7E, 0x23, 0xB7, 0x28, 0x06, 0xCD, 0xFE, 0xC7, 0xF2, + 0x07, 0xC8, 0xE3, 0xC9, 0x1A, 0xFE, 0x20, 0xC0, 0x13, 0x18, 0xF9, 0xCD, 0x14, 0xC8, 0xFE, 0x2C, + 0xC0, 0x13, 0xCD, 0x14, 0xC8, 0xAF, 0xC9, 0xCD, 0x14, 0xC8, 0xB7, 0xC8, 0xC3, 0xBD, 0xC1, 0x21, + 0x80, 0x00, 0x44, 0x4D, 0xCD, 0x68, 0xC8, 0xE5, 0xC5, 0xCD, 0x1B, 0xC8, 0xCD, 0xAD, 0xC8, 0xDA, + 0xBD, 0xC1, 0xCD, 0x27, 0xC8, 0xEB, 0xC1, 0xE1, 0xC9, 0xCD, 0x65, 0xC8, 0x18, 0xD9, 0xCD, 0x68, + 0xC8, 0x18, 0xD4, 0xCD, 0xAD, 0xC8, 0xDA, 0xBD, 0xC1, 0x18, 0xCC, 0xE5, 0xCD, 0xAD, 0xC8, 0x38, + 0x01, 0xE3, 0xE1, 0x18, 0xC2, 0x37, 0x18, 0x01, 0xB7, 0x08, 0xC5, 0xE5, 0xCD, 0xAD, 0xC8, 0x30, + 0x08, 0x08, 0xDA, 0xBD, 0xC1, 0x08, 0xE1, 0x18, 0x01, 0xF1, 0xCD, 0x87, 0xC8, 0x30, 0x06, 0x08, + 0xC1, 0xD0, 0xC3, 0xBD, 0xC1, 0xF1, 0xC9, 0xCD, 0x1B, 0xC8, 0x1A, 0xFE, 0x53, 0x20, 0x01, 0x13, + 0xE5, 0xF5, 0xCD, 0xAD, 0xC8, 0x38, 0x0F, 0x44, 0x4D, 0xF1, 0xE1, 0x28, 0x07, 0x79, 0x95, 0x4F, + 0x78, 0x9C, 0x47, 0x03, 0xB7, 0xC9, 0xF1, 0xE1, 0xCA, 0xBD, 0xC1, 0x37, 0xC9, 0xCD, 0x14, 0xC8, + 0xCD, 0xE0, 0xC8, 0xD8, 0xD5, 0x13, 0xCD, 0xE0, 0xC8, 0x30, 0xFA, 0xD1, 0xFE, 0x2E, 0x28, 0x05, + 0xCD, 0xCA, 0xC8, 0xA7, 0xC9, 0xCD, 0xF4, 0xC8, 0xA7, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0xE0, 0xC8, + 0x38, 0x09, 0x29, 0x29, 0x29, 0x29, 0x85, 0x6F, 0x13, 0x18, 0xF2, 0xFE, 0x48, 0xC0, 0x13, 0xC9, + 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x38, 0x09, 0xFE, 0x41, 0xD8, 0xFE, 0x47, 0x3F, 0xD8, 0xD6, + 0x07, 0xD6, 0x30, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0x10, 0xC9, 0x13, 0x38, 0x0D, 0xC5, 0x29, 0x44, + 0x4D, 0x29, 0x29, 0x09, 0xC1, 0xCD, 0x86, 0xC1, 0x18, 0xED, 0xFE, 0x2E, 0xC8, 0xC3, 0xBD, 0xC1, + 0x1A, 0xFE, 0x30, 0xD8, 0xFE, 0x3A, 0x3F, 0xD8, 0xD6, 0x30, 0xC9, 0x01, 0x18, 0x00, 0x11, 0x00, + 0x01, 0x21, 0x29, 0xC9, 0xED, 0xB0, 0xC3, 0x00, 0x01, 0x01, 0x00, 0x10, 0x11, 0x00, 0x10, 0x21, + 0x00, 0xC0, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0x3E, 0x01, 0xD3, 0x40, 0xD1, 0xE1, 0xC1, 0xED, 0xB0, + 0xC9, 0xCD, 0x14, 0xC8, 0xFE, 0x5A, 0xF5, 0x20, 0x01, 0x13, 0xCD, 0x27, 0xC8, 0xF1, 0xC4, 0x1B, + 0xC9, 0xCD, 0x06, 0xC8, 0x0D, 0x4D, 0x65, 0x6D, 0x6F, 0x72, 0x79, 0xBA, 0x01, 0x00, 0x10, 0xCD, + 0xFC, 0xC7, 0x79, 0xCD, 0xF0, 0xC7, 0x0C, 0x10, 0xF6, 0xCD, 0x97, 0xCC, 0x06, 0x07, 0xCD, 0xFC, + 0xC7, 0x10, 0xFB, 0x60, 0x68, 0xCD, 0xFC, 0xC7, 0xE5, 0x11, 0x00, 0x10, 0x7C, 0xFE, 0xC9, 0x20, + 0x09, 0x7D, 0xFE, 0x8D, 0x38, 0x04, 0xFE, 0x9C, 0x38, 0x12, 0x46, 0x3E, 0x55, 0x77, 0xBE, 0x20, + 0x05, 0x2F, 0x77, 0xBE, 0x28, 0x05, 0x70, 0x3E, 0x58, 0x18, 0x09, 0x70, 0x23, 0x1B, 0x7A, 0xB3, + 0x20, 0xDA, 0x3E, 0x5E, 0xCD, 0xFE, 0xC7, 0xE1, 0x3E, 0x10, 0x84, 0x67, 0x20, 0xC7, 0xCD, 0x06, + 0xC8, 0x0D, 0x53, 0x70, 0x65, 0x63, 0x69, 0x66, 0x79, 0x20, 0x64, 0x69, 0x73, 0x6B, 0x20, 0x28, + 0x65, 0x67, 0x2C, 0x20, 0x41, 0x3B, 0x20, 0x6F, 0x72, 0x20, 0x41, 0x3B, 0x3B, 0x20, 0x6F, 0x72, + 0x20, 0x41, 0x3B, 0x3B, 0x3B, 0x29, 0xA0, 0xCD, 0xA3, 0xC7, 0xCD, 0x14, 0xC8, 0xB7, 0xCA, 0x97, + 0xCC, 0xCD, 0xC1, 0xC2, 0xC2, 0xB9, 0xC1, 0xCD, 0x06, 0xC8, 0x53, 0x65, 0x65, 0x6B, 0x20, 0x74, + 0x65, 0x73, 0x74, 0x73, 0x3A, 0x8D, 0x06, 0x15, 0x21, 0x20, 0xCA, 0xDB, 0x31, 0x32, 0x7D, 0x00, + 0x7E, 0xC5, 0xE5, 0xFE, 0xFF, 0x20, 0x2E, 0xCD, 0x06, 0xC8, 0x20, 0x52, 0x65, 0x73, 0x74, 0x6F, + 0x72, 0x65, 0xBA, 0xCD, 0x04, 0xC6, 0xCD, 0x37, 0xCD, 0x08, 0xCD, 0x1E, 0xC6, 0x08, 0x18, 0x2C, + 0x01, 0x02, 0x03, 0x04, 0x05, 0xFE, 0x06, 0x07, 0x08, 0x09, 0x00, 0xFE, 0x27, 0x00, 0x15, 0x00, + 0x01, 0xFE, 0xFF, 0xFE, 0x27, 0xFE, 0xFE, 0x20, 0x06, 0xCD, 0x97, 0xCC, 0xB7, 0x18, 0x12, 0x32, + 0x75, 0x00, 0xCD, 0xDB, 0xC7, 0xCD, 0x06, 0xC8, 0xBA, 0xCD, 0xD4, 0xC4, 0xF5, 0xCD, 0x7A, 0xCB, + 0xF1, 0xE1, 0xC1, 0xDA, 0x97, 0xCC, 0x23, 0x10, 0xA2, 0xCD, 0x06, 0xC8, 0x0D, 0x52, 0x65, 0x61, + 0x64, 0x2F, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, 0x74, 0x65, 0x73, 0x74, 0x73, 0x8D, 0xCD, 0x94, + 0xCB, 0xCD, 0xB0, 0xCB, 0xDA, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x57, 0x72, 0x69, 0x74, 0x65, 0x20, + 0x74, 0x65, 0x73, 0x74, 0x20, 0x4D, 0x41, 0x59, 0x20, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, + 0x20, 0x64, 0x61, 0x74, 0x61, 0x0D, 0x45, 0x53, 0x43, 0x3D, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x20, + 0x52, 0x45, 0x54, 0x55, 0x52, 0x4E, 0x3D, 0x70, 0x72, 0x6F, 0x63, 0x65, 0x65, 0x64, 0xA0, 0xCD, + 0xA3, 0xC7, 0x21, 0x00, 0x09, 0xCD, 0xA2, 0xCB, 0x01, 0x00, 0x02, 0x75, 0x23, 0x0B, 0x78, 0xB1, + 0x20, 0xF9, 0xCD, 0xC1, 0xCB, 0x30, 0x6E, 0xCD, 0x06, 0xC8, 0x54, 0x65, 0x73, 0x74, 0x20, 0x66, + 0x61, 0x69, 0x6C, 0x65, 0x64, 0x21, 0x0D, 0x44, 0x69, 0x73, 0x6B, 0x20, 0x64, 0x61, 0x74, 0x61, + 0x20, 0x61, 0xF4, 0x1E, 0x01, 0xCD, 0xDE, 0xC5, 0xCD, 0x06, 0xC8, 0x20, 0x6D, 0x61, 0x79, 0x20, + 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x64, 0x65, 0x73, 0x74, 0x72, 0x6F, + 0x79, 0x65, 0x64, 0x0D, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, 0x6C, 0x20, 0x69, 0x73, 0x20, + 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, 0x30, 0x44, 0x30, 0x30, 0xF3, + 0x2A, 0x79, 0x00, 0xCD, 0xE2, 0xC7, 0xCD, 0x06, 0xC8, 0x20, 0x69, 0x6E, 0x20, 0x6D, 0x65, 0x6D, + 0x6F, 0x72, 0x79, 0x8D, 0xC9, 0x21, 0x00, 0x0B, 0xCD, 0xA2, 0xCB, 0xCD, 0xB0, 0xCB, 0x38, 0x87, + 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0x6E, 0x20, 0x63, 0x6F, 0x6D, 0x70, 0x61, + 0x72, 0x65, 0xA0, 0xED, 0x4B, 0x79, 0x00, 0x11, 0x00, 0x09, 0x21, 0x00, 0x0B, 0x1A, 0xBE, 0x20, + 0x09, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF5, 0x18, 0x01, 0x37, 0xCD, 0xD1, 0xCB, 0xCD, 0x94, + 0xCB, 0xCD, 0xC1, 0xCB, 0xDA, 0xC7, 0xCA, 0xC3, 0x97, 0xCC, 0x38, 0x06, 0xCD, 0x06, 0xC8, 0x4F, + 0xCB, 0xC9, 0xCD, 0x06, 0xC8, 0x65, 0x72, 0x72, 0x6F, 0x72, 0xA0, 0x3A, 0x71, 0x00, 0xCD, 0xE7, + 0xC7, 0xC3, 0x97, 0xCC, 0xCD, 0x06, 0xC8, 0x44, 0x61, 0x74, 0xE1, 0x21, 0x00, 0x0D, 0x22, 0x7B, + 0x00, 0xC9, 0x22, 0x7B, 0x00, 0xCD, 0x06, 0xC8, 0x50, 0x61, 0x74, 0x74, 0x65, 0x72, 0xEE, 0xC9, + 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x72, 0x65, 0x61, 0x64, 0xA0, 0xCD, 0x52, 0xCD, 0x18, + 0x10, 0xCD, 0xDA, 0xCB, 0xCD, 0x06, 0xC8, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65, 0xA0, 0xCD, 0xAB, + 0xC5, 0xF5, 0xCD, 0x7A, 0xCB, 0xCD, 0x97, 0xCC, 0xF1, 0xC9, 0x3E, 0x27, 0x32, 0x75, 0x00, 0x32, + 0x7D, 0x00, 0x3E, 0x01, 0x32, 0x76, 0x00, 0xC3, 0xCB, 0xCD, 0xC5, 0xE5, 0x06, 0x00, 0x21, 0x02, + 0x00, 0x19, 0xCD, 0x64, 0xCC, 0xFE, 0x10, 0xCC, 0xBB, 0xCC, 0x28, 0xF6, 0xFE, 0x1B, 0xCA, 0x8E, + 0xCC, 0xFE, 0x08, 0x28, 0x04, 0xFE, 0x7F, 0x20, 0x11, 0xAF, 0xB0, 0x28, 0xE5, 0x2B, 0x05, 0xCD, + 0x4B, 0xCC, 0x7E, 0xFE, 0x20, 0xDC, 0x4B, 0xCC, 0x18, 0xD8, 0xFE, 0x0D, 0xCC, 0x45, 0xCC, 0x28, + 0x1B, 0xFE, 0x15, 0x20, 0x08, 0xCD, 0x45, 0xCC, 0xCD, 0x97, 0xCC, 0x18, 0xBF, 0x4F, 0x1A, 0x3D, + 0xB8, 0x28, 0xBF, 0x79, 0xCD, 0x45, 0xCC, 0x77, 0x23, 0x04, 0x18, 0xB6, 0x36, 0x00, 0x78, 0x13, + 0x12, 0x1B, 0xE1, 0xC1, 0xC9, 0xF5, 0xCD, 0x52, 0xCC, 0xF1, 0xC9, 0xCD, 0x06, 0xC8, 0x08, 0x20, + 0x88, 0xC9, 0xFE, 0x20, 0x30, 0x43, 0xFE, 0x0D, 0x28, 0x3F, 0xF5, 0xCD, 0x06, 0xC8, 0xDE, 0xF1, + 0xC6, 0x40, 0x18, 0x35, 0xCD, 0x23, 0xCD, 0xCD, 0x76, 0xCC, 0x28, 0xF8, 0xCD, 0x76, 0xCC, 0x28, + 0xFB, 0xDB, 0x01, 0xE6, 0x7F, 0xC9, 0xDB, 0x00, 0xE6, 0x40, 0xC9, 0xCD, 0x76, 0xCC, 0xC8, 0xCD, + 0x6C, 0xCC, 0xFE, 0x13, 0xCC, 0x6C, 0xCC, 0xFE, 0x0D, 0x28, 0x03, 0xFE, 0x1B, 0xC0, 0x31, 0x2E, + 0x00, 0xCD, 0x97, 0xCC, 0xC3, 0x55, 0xC1, 0x3E, 0x0D, 0xF5, 0xD9, 0xCB, 0x78, 0xD9, 0xC4, 0xCB, + 0xCC, 0xCD, 0x23, 0xCD, 0xDB, 0x00, 0xE6, 0x80, 0x28, 0xFA, 0xF1, 0xD3, 0x01, 0xFE, 0x0D, 0xC0, + 0x3E, 0x0A, 0xCD, 0x99, 0xCC, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0xC0, 0xF5, 0xD9, 0x3E, 0x80, 0xA8, + 0x47, 0xCB, 0x78, 0xD9, 0xC4, 0xC9, 0xCC, 0xF1, 0xC9, 0x3E, 0x11, 0xF5, 0xE5, 0x21, 0x08, 0x00, + 0xCD, 0xF1, 0xCF, 0xE1, 0xF1, 0xF5, 0xFE, 0x11, 0x28, 0x13, 0xCD, 0x7B, 0xCC, 0xFE, 0x10, 0x20, + 0x05, 0xCD, 0xBB, 0xCC, 0xF1, 0xC9, 0xDB, 0x54, 0x2F, 0xE6, 0x20, 0x28, 0xED, 0xF1, 0xCB, 0xFF, + 0xD3, 0x54, 0xCB, 0xBF, 0xD3, 0x54, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xC9, 0xCD, 0x14, 0xC8, + 0xFE, 0x4F, 0xC2, 0xBD, 0xC1, 0x13, 0x1A, 0xFE, 0x4E, 0x28, 0x0E, 0xFE, 0x46, 0xC2, 0xBD, 0xC1, + 0xD9, 0xCB, 0xA8, 0xD9, 0x3E, 0xFF, 0xD3, 0x04, 0xC9, 0xD9, 0xCB, 0xE8, 0xD9, 0x3E, 0x01, 0x32, + 0x72, 0x00, 0xC9, 0xD9, 0xCB, 0x68, 0xD9, 0xC8, 0xC5, 0xCD, 0xA8, 0xCF, 0xC1, 0xD3, 0x34, 0xCD, + 0xD6, 0xCF, 0xEE, 0xA0, 0xD3, 0x04, 0xC9, 0x16, 0x02, 0xD5, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0xD5, + 0x3E, 0x0A, 0x32, 0x75, 0x00, 0xCD, 0xCB, 0xCD, 0xCD, 0x70, 0xCD, 0xD1, 0xD0, 0x15, 0x20, 0xE9, + 0x18, 0x18, 0x16, 0x0A, 0xD5, 0xCD, 0x58, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x18, 0x0B, 0x16, + 0x04, 0xD5, 0xCD, 0x9F, 0xCE, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x3A, 0x71, 0x00, 0x4F, 0x37, 0xC9, + 0x97, 0x32, 0x75, 0x00, 0x32, 0x78, 0x00, 0xCD, 0x55, 0xCF, 0xD3, 0x34, 0xCD, 0x25, 0xCE, 0x38, + 0x0E, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x3B, 0x1F, 0x30, 0xF7, 0xC3, 0x3E, 0xCE, 0x3E, + 0xC4, 0xD3, 0x30, 0xCD, 0xD6, 0xCF, 0xE6, 0x57, 0xD3, 0x04, 0xCD, 0xE4, 0xCF, 0xDB, 0x04, 0xE6, + 0x40, 0x20, 0x16, 0xCD, 0xCF, 0xCF, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x18, 0x1F, 0x30, 0xF7, 0x3E, + 0xD0, 0xD3, 0x30, 0x97, 0xD3, 0x31, 0xC3, 0x50, 0xCE, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x05, 0x1F, + 0x30, 0xDB, 0x18, 0xCB, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0x97, 0xCD, 0x55, 0xCF, 0xD3, + 0x34, 0x3A, 0x75, 0x00, 0xD3, 0x33, 0x4F, 0x3A, 0x76, 0x00, 0xD3, 0x32, 0x3A, 0x7D, 0x00, 0xD3, + 0x31, 0x91, 0xCA, 0xCF, 0xCF, 0xCD, 0x25, 0xCE, 0x38, 0x0F, 0xF6, 0x10, 0xD3, 0x30, 0xDB, 0x34, + 0xCB, 0x57, 0x20, 0xD0, 0x1F, 0x30, 0xF7, 0x18, 0x45, 0xCD, 0xD6, 0xCF, 0xE6, 0x4F, 0xD3, 0x04, + 0x3E, 0x18, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xBA, 0x1F, 0x30, 0xF7, 0xDB, 0x30, 0x2E, + 0x32, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0x2D, 0x20, 0xF7, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, + 0xCD, 0xCF, 0xCF, 0x18, 0x2B, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x0A, 0xCB, 0x5F, 0x3E, 0x0E, + 0x28, 0x02, 0x3E, 0x0C, 0xA7, 0xC9, 0xCB, 0x5F, 0x3E, 0x0F, 0x28, 0xF8, 0x37, 0xC9, 0xCD, 0xCF, + 0xCF, 0x21, 0x64, 0x00, 0xCD, 0xF1, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x98, 0x37, 0xC0, + 0x3A, 0x75, 0x00, 0x32, 0x7D, 0x00, 0xA7, 0xC9, 0xCD, 0x8F, 0xCE, 0xD3, 0x30, 0xDB, 0x34, 0x1F, + 0x38, 0x16, 0xED, 0xA2, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0E, 0xED, 0xA2, 0xC2, 0x5D, 0xCE, 0xDB, + 0x34, 0xCB, 0x4F, 0x20, 0x10, 0x1F, 0x30, 0xF7, 0xCD, 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, + 0xE6, 0x9C, 0xC8, 0x18, 0x08, 0xCD, 0xCF, 0xCF, 0x3E, 0x80, 0x32, 0x71, 0x00, 0x37, 0xC9, 0xCD, + 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0x88, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xC9, 0xCD, + 0xE4, 0xCF, 0xCD, 0x40, 0xCF, 0x57, 0xF3, 0xCD, 0xC6, 0xCF, 0xC6, 0xA8, 0x5F, 0x7A, 0xD3, 0x34, + 0x7B, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x12, 0xED, 0xA3, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0A, + 0xED, 0xA3, 0xC2, 0xB3, 0xCE, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xCD, 0xCF, 0xCF, 0xCD, 0xE4, 0xCF, + 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0xFC, 0x37, 0xC0, 0xA7, 0x3A, 0x7E, 0x00, 0xCB, 0x4F, 0xC8, + 0xCD, 0x00, 0xCF, 0x38, 0x0A, 0xDB, 0x34, 0x1F, 0x38, 0x04, 0xDB, 0x33, 0x18, 0xF7, 0x1C, 0xCD, + 0xCF, 0xCF, 0xDB, 0x30, 0x32, 0x71, 0x00, 0xE6, 0x9C, 0x37, 0xC0, 0x7B, 0xA7, 0xC8, 0x37, 0xC9, + 0xCD, 0x8F, 0xCE, 0xED, 0x4B, 0x79, 0x00, 0xCB, 0x38, 0xCB, 0x19, 0xCB, 0x38, 0xCB, 0x19, 0x41, + 0x1E, 0x00, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, + 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, + 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0x10, 0xDA, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xC9, + 0xDB, 0x33, 0x3E, 0x80, 0xCD, 0x55, 0xCF, 0x2A, 0x79, 0x00, 0xCB, 0x1C, 0xCB, 0x1D, 0x45, 0x0E, + 0x33, 0x2A, 0x7B, 0x00, 0xC9, 0x4F, 0xDB, 0x34, 0xE6, 0x04, 0x28, 0x04, 0x97, 0x32, 0x72, 0x00, + 0xCD, 0xA8, 0xCF, 0xD3, 0x34, 0xF5, 0xE5, 0xCD, 0xD6, 0xCF, 0xE6, 0x5F, 0xD3, 0x04, 0x3A, 0x72, + 0x00, 0xA7, 0x28, 0x21, 0x21, 0x90, 0x01, 0x3A, 0x73, 0x00, 0xB8, 0x20, 0x1B, 0x3A, 0x74, 0x00, + 0x67, 0x3A, 0x78, 0x00, 0x32, 0x74, 0x00, 0xBC, 0x20, 0x06, 0xDB, 0x34, 0xE6, 0x20, 0x20, 0x0B, + 0xCD, 0xE4, 0xCF, 0x18, 0x06, 0x21, 0x20, 0x4E, 0xCD, 0xF1, 0xCF, 0xE1, 0x78, 0x32, 0x73, 0x00, + 0x3E, 0x01, 0x32, 0x72, 0x00, 0xF1, 0xB1, 0xC9, 0x3A, 0x77, 0x00, 0x47, 0x04, 0x97, 0x37, 0x17, + 0x10, 0xFD, 0x47, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x28, 0x02, 0xCB, 0xE0, 0xCB, 0x47, 0x28, 0x02, + 0xCB, 0xF0, 0x78, 0xF6, 0x20, 0xC9, 0xDB, 0x34, 0x2F, 0xE6, 0x20, 0xC8, 0x3E, 0x04, 0xC9, 0xCD, + 0xD6, 0xCF, 0xD3, 0x04, 0xAF, 0xC9, 0xC5, 0x06, 0x7F, 0x3A, 0x78, 0x00, 0xA7, 0x28, 0x02, 0x06, + 0x7D, 0x78, 0xC1, 0xC9, 0x21, 0x08, 0x00, 0x3A, 0x7E, 0x00, 0xCB, 0x57, 0x20, 0x03, 0x21, 0x0C, + 0x00, 0xC5, 0x2B, 0x06, 0x1C, 0x10, 0xFE, 0x00, 0x00, 0x7D, 0xB4, 0x20, 0xF5, 0xC1, 0xC9, 0xFF +}, +{ /* RDOS 3.12 */ + 0xF3, 0x18, 0x36, 0xC3, 0x43, 0xC3, 0xC3, 0x75, 0xC3, 0xC3, 0x79, 0xC3, 0xC3, 0x7D, 0xC3, 0xC3, + 0x06, 0xCA, 0xC3, 0x88, 0xC3, 0xC3, 0xAF, 0xC3, 0xC3, 0xC0, 0xC3, 0xC3, 0xD0, 0xC3, 0xC3, 0x81, + 0xC3, 0xC3, 0xAA, 0xCF, 0xC3, 0x02, 0xD5, 0xC3, 0xF8, 0xD4, 0xC3, 0xA1, 0xD4, 0xC3, 0x1F, 0xD4, + 0xC3, 0xE4, 0xD4, 0xC3, 0xF2, 0xD4, 0xC3, 0x80, 0xD4, 0xAF, 0x47, 0xD3, 0x03, 0xD3, 0xE1, 0x2F, + 0xD3, 0x04, 0x3E, 0xD0, 0xD3, 0x30, 0x0E, 0x00, 0xD9, 0x21, 0x03, 0x00, 0x25, 0x20, 0xFD, 0x74, + 0x2C, 0xF2, 0x4F, 0xC0, 0x31, 0xF0, 0x7F, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0x3E, 0x20, 0x32, 0x10, + 0x00, 0x22, 0x05, 0x00, 0x22, 0x03, 0x00, 0xCD, 0xAA, 0xCF, 0xDB, 0x34, 0xE6, 0x40, 0xCC, 0x74, + 0xC4, 0xCD, 0xF2, 0xD4, 0x43, 0x72, 0x6F, 0x6D, 0x65, 0x6D, 0x63, 0x6F, 0x20, 0x52, 0x44, 0x4F, + 0x53, 0x20, 0x30, 0x33, 0x2E, 0x31, 0x32, 0x8D, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0x31, 0xF0, 0x7F, + 0xCD, 0x58, 0xD3, 0x21, 0x15, 0x00, 0x3A, 0x07, 0x00, 0xCD, 0xCB, 0xC0, 0xCD, 0x1A, 0xD4, 0xCD, + 0x45, 0xD3, 0xB7, 0x28, 0xE3, 0xD6, 0x41, 0xFE, 0x1A, 0x30, 0x4C, 0x21, 0x8D, 0xC0, 0xE5, 0x47, + 0x13, 0xCD, 0x45, 0xD3, 0xFE, 0x3B, 0x78, 0x28, 0x76, 0x87, 0x21, 0x5D, 0xD5, 0xCD, 0x84, 0xD2, + 0x7E, 0x23, 0x66, 0x6F, 0xE5, 0x21, 0x80, 0x00, 0xC3, 0x45, 0xD3, 0x28, 0x10, 0xFE, 0xFF, 0x28, + 0x15, 0xCD, 0xF2, 0xD4, 0x53, 0xD4, 0xCD, 0xFE, 0xD2, 0x3E, 0x3A, 0x18, 0x17, 0xFE, 0xFF, 0x28, + 0x05, 0xC6, 0x41, 0xCD, 0xA1, 0xD4, 0x3E, 0x3B, 0x28, 0x0A, 0xCB, 0x5E, 0xCC, 0xA1, 0xD4, 0xCB, + 0x56, 0xCC, 0xA1, 0xD4, 0xC3, 0xA1, 0xD4, 0xCD, 0xF2, 0xD4, 0x3F, 0x8D, 0x18, 0x8F, 0xCD, 0x27, + 0xC1, 0x1B, 0x1F, 0x41, 0x2D, 0xC4, 0x18, 0x0D, 0xCD, 0x27, 0xC1, 0x1B, 0x1F, 0x53, 0x54, 0x30, + 0x2D, 0x53, 0x54, 0x33, 0xC6, 0xCD, 0xF2, 0xD4, 0x20, 0x4F, 0x6E, 0x6C, 0x79, 0x8D, 0x18, 0xDC, + 0xCD, 0x27, 0xC1, 0x01, 0x07, 0xA0, 0xC9, 0xE3, 0xF5, 0xCD, 0xA9, 0xCC, 0xF1, 0xE3, 0xC9, 0xFE, + 0x04, 0x30, 0xCB, 0xF5, 0x13, 0x1A, 0xFE, 0x3B, 0x06, 0x0C, 0x20, 0x0B, 0x13, 0x1A, 0xFE, 0x3B, + 0x06, 0x04, 0x20, 0x03, 0x13, 0xAF, 0x47, 0xCD, 0x66, 0xC2, 0xD1, 0xF5, 0xAF, 0x32, 0x08, 0x00, + 0x7A, 0x32, 0x16, 0x00, 0x32, 0x07, 0x00, 0xF1, 0x21, 0x15, 0x00, 0x70, 0x38, 0x02, 0xB0, 0x77, + 0xF5, 0xCD, 0xAF, 0xC1, 0xCD, 0xB5, 0xC2, 0x3E, 0x48, 0x32, 0x09, 0x00, 0xCD, 0x88, 0xC3, 0xDA, + 0xE6, 0xCB, 0xF1, 0xD0, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0xCD, + 0x60, 0xD6, 0xCD, 0xD2, 0xD5, 0x30, 0x1F, 0x3A, 0x21, 0x00, 0xB7, 0xF2, 0x96, 0xC1, 0x21, 0x64, + 0x00, 0xCD, 0x7C, 0xD8, 0x18, 0xE3, 0xCD, 0xE6, 0xCB, 0xCD, 0x20, 0xC1, 0xCD, 0xF2, 0xD4, 0x4C, + 0x61, 0x62, 0x65, 0x6C, 0x8D, 0xC9, 0x21, 0xF8, 0x00, 0xCD, 0x9A, 0xC2, 0xCD, 0xB5, 0xC2, 0xAF, + 0x32, 0x08, 0x00, 0x21, 0x27, 0x00, 0x3A, 0x16, 0x00, 0xCD, 0x84, 0xD2, 0x3A, 0x15, 0x00, 0xCB, + 0xFF, 0x77, 0xC9, 0x13, 0xCD, 0xB2, 0xD3, 0xFE, 0x3A, 0xC2, 0xF7, 0xC0, 0x45, 0x7C, 0xB7, 0xC2, + 0xF7, 0xC0, 0x13, 0xCD, 0xE9, 0xD2, 0x78, 0xF5, 0xFE, 0x40, 0xD2, 0x08, 0xC1, 0x32, 0x16, 0x00, + 0x32, 0x07, 0x00, 0x3E, 0x80, 0x32, 0x08, 0x00, 0x3E, 0xD8, 0x32, 0x09, 0x00, 0xCD, 0xA3, 0xC3, + 0xCD, 0x8A, 0xD8, 0xDA, 0x8D, 0xC0, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0x21, 0x00, 0x02, 0x22, + 0x1D, 0x00, 0xCD, 0x37, 0xD9, 0xF1, 0x38, 0x0F, 0xCD, 0x51, 0xDA, 0xDC, 0xF4, 0xCB, 0xCC, 0x3D, + 0xC2, 0xCD, 0x51, 0xDA, 0x11, 0xF8, 0x00, 0xCD, 0xEF, 0xC2, 0xCB, 0x77, 0xC8, 0x21, 0x1A, 0x00, + 0x34, 0xCD, 0x7D, 0xD9, 0xCD, 0x51, 0xDA, 0xD0, 0xCD, 0xF4, 0xCB, 0xCD, 0x20, 0xC1, 0xCD, 0xF2, + 0xD4, 0x53, 0x75, 0x70, 0x65, 0x72, 0x42, 0x6C, 0x6F, 0x63, 0x6B, 0x8D, 0xC9, 0x3E, 0xC8, 0x32, + 0x09, 0x00, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0x21, 0xE5, 0xE5, 0x22, 0xF8, 0x00, 0xCD, 0x88, + 0xC3, 0xDA, 0xF4, 0xCB, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0xCD, 0x7D, 0xD9, 0xCD, 0x51, 0xDA, 0xD0, + 0xCD, 0xF4, 0xCB, 0xC3, 0x99, 0xC1, 0xE5, 0x0E, 0x00, 0x21, 0x10, 0x44, 0xCD, 0x85, 0xC2, 0x38, + 0x0F, 0x2E, 0x01, 0xCD, 0x85, 0xC2, 0xDA, 0xF7, 0xC0, 0x21, 0x02, 0x58, 0xCD, 0x8F, 0xC2, 0xB7, + 0x79, 0xE1, 0xC3, 0xE9, 0xD2, 0xCD, 0x8F, 0xC2, 0xD0, 0xD6, 0x53, 0x28, 0x0B, 0x37, 0xC9, 0xCD, + 0x45, 0xD3, 0xBC, 0x37, 0xC0, 0x79, 0xB5, 0x4F, 0x13, 0xC9, 0x01, 0x02, 0x43, 0xCD, 0xA8, 0xC2, + 0x01, 0x10, 0x44, 0xCD, 0xA8, 0xC2, 0x0E, 0x01, 0x7E, 0x23, 0x23, 0xB8, 0xC0, 0x3A, 0x15, 0x00, + 0xB1, 0x32, 0x15, 0x00, 0xC9, 0x3A, 0x15, 0x00, 0x47, 0xCB, 0x40, 0x11, 0x10, 0x0A, 0x21, 0x00, + 0x02, 0x20, 0x0D, 0xCB, 0x48, 0x11, 0x08, 0x05, 0x20, 0x06, 0x11, 0x1A, 0x12, 0x21, 0x80, 0x00, + 0x22, 0x1D, 0x00, 0xCB, 0x50, 0x7B, 0x1E, 0x4C, 0x20, 0x03, 0x7A, 0x1E, 0x27, 0x32, 0x11, 0x00, + 0xAF, 0x57, 0xED, 0x53, 0x12, 0x00, 0xCB, 0x60, 0x28, 0x01, 0x3C, 0x32, 0x14, 0x00, 0xC9, 0x3E, + 0x80, 0x21, 0x08, 0x00, 0x77, 0x38, 0x35, 0x1A, 0xFE, 0x43, 0x20, 0x02, 0xCB, 0xF6, 0x13, 0x1A, + 0xFE, 0x53, 0x20, 0x28, 0x13, 0x1A, 0xFE, 0x54, 0x20, 0x22, 0x21, 0xEE, 0xFF, 0x19, 0xC5, 0x56, + 0x23, 0x5E, 0x23, 0xD5, 0x56, 0x23, 0x5E, 0x23, 0x4E, 0x06, 0x00, 0xEB, 0xCD, 0xBF, 0xD2, 0x7B, + 0xB2, 0x28, 0x01, 0x23, 0xD1, 0x19, 0x79, 0x3D, 0x2B, 0xC1, 0x18, 0x05, 0x3E, 0x05, 0x21, 0x31, + 0x01, 0x32, 0x14, 0x00, 0x22, 0x12, 0x00, 0x3E, 0x13, 0x32, 0x11, 0x00, 0x21, 0x00, 0x02, 0x22, + 0x1D, 0x00, 0xC9, 0x32, 0x07, 0x00, 0xFE, 0x40, 0x30, 0x08, 0x47, 0x37, 0xCD, 0xD6, 0xC1, 0xC3, + 0x57, 0xDA, 0xD6, 0x41, 0xFE, 0x04, 0x38, 0x07, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0x37, 0xC9, 0x32, + 0x07, 0x00, 0x32, 0x16, 0x00, 0x78, 0x32, 0x15, 0x00, 0xD9, 0xCB, 0xA8, 0xD9, 0xDB, 0x31, 0x32, + 0x1F, 0x00, 0xC3, 0xAC, 0xC1, 0x32, 0x17, 0x00, 0xC9, 0x22, 0x18, 0x00, 0xC9, 0x32, 0x1A, 0x00, + 0xC9, 0xCD, 0x51, 0xDA, 0x47, 0xC3, 0x57, 0xDA, 0xCD, 0xA3, 0xC3, 0x20, 0x06, 0xCD, 0x3B, 0xDA, + 0xC3, 0x51, 0xDA, 0xD9, 0xCB, 0xA8, 0xD9, 0xCD, 0x12, 0xC4, 0xCD, 0x91, 0xD5, 0xF5, 0xCD, 0x30, + 0xC4, 0xF1, 0xC9, 0xAF, 0x67, 0x6F, 0x32, 0x17, 0x00, 0x22, 0x18, 0x00, 0xC3, 0xE6, 0xCE, 0xCD, + 0x58, 0xD3, 0x20, 0x06, 0xCD, 0x08, 0xC4, 0xC3, 0x60, 0xD6, 0xCD, 0x17, 0xDA, 0xC3, 0x51, 0xDA, + 0xCD, 0xEA, 0xC3, 0xD8, 0xF5, 0xC4, 0x7D, 0xD9, 0xF1, 0x20, 0x18, 0xCD, 0xD2, 0xD5, 0x18, 0x0E, + 0xCD, 0xEA, 0xC3, 0xD8, 0xF5, 0xC4, 0x04, 0xDA, 0xF1, 0x20, 0x08, 0xCD, 0xF8, 0xC3, 0xED, 0x4B, + 0x1D, 0x00, 0xC9, 0xED, 0x4B, 0x1D, 0x00, 0xC3, 0x51, 0xDA, 0xCD, 0xAF, 0xC3, 0x3A, 0x08, 0x00, + 0xCB, 0x7F, 0x20, 0x01, 0xD8, 0xE6, 0x80, 0xC9, 0x21, 0x15, 0x00, 0x46, 0xC5, 0xCB, 0xCE, 0xCD, + 0xE7, 0xD5, 0xC1, 0x78, 0x32, 0x15, 0x00, 0xC9, 0x3A, 0x18, 0x00, 0x47, 0x3A, 0x17, 0x00, 0xB0, + 0x20, 0x1E, 0xD9, 0xCB, 0x68, 0xD9, 0xC0, 0x3A, 0x15, 0x00, 0xCB, 0x4F, 0x20, 0x03, 0xCB, 0x47, + 0xC8, 0x32, 0x26, 0x00, 0xE6, 0xFC, 0x32, 0x15, 0x00, 0xD9, 0xCB, 0xE8, 0xD9, 0xC3, 0xB5, 0xC2, + 0xD9, 0xCB, 0x68, 0xCB, 0xA8, 0xD9, 0xC8, 0x3A, 0x26, 0x00, 0x32, 0x15, 0x00, 0xC3, 0xB5, 0xC2, + 0xB7, 0x28, 0x31, 0x13, 0xFE, 0x53, 0x20, 0x20, 0x1A, 0xCD, 0x2C, 0xD3, 0xFE, 0x54, 0xC2, 0x08, + 0xC1, 0x13, 0xCD, 0xB2, 0xD3, 0xB7, 0xC2, 0x08, 0xC1, 0x7C, 0xA7, 0xC2, 0x08, 0xC1, 0x7D, 0xFE, + 0x40, 0xD2, 0x08, 0xC1, 0xF6, 0x80, 0x18, 0x2F, 0xCD, 0xE9, 0xD2, 0xD6, 0x41, 0xFE, 0x04, 0xD2, + 0xFE, 0xC0, 0x18, 0x23, 0xDB, 0x04, 0x2F, 0xC5, 0xE6, 0x17, 0x47, 0xE6, 0x03, 0xCB, 0x50, 0x28, + 0x02, 0xCB, 0xFF, 0xCB, 0x60, 0x20, 0x0F, 0xCB, 0xEF, 0xCB, 0x48, 0x20, 0x09, 0xCB, 0xFF, 0x3D, + 0xCB, 0x40, 0x28, 0x02, 0xC6, 0x1F, 0xC1, 0xF5, 0xCB, 0x7F, 0x28, 0x05, 0x3E, 0x01, 0xCD, 0xB3, + 0xCB, 0xDB, 0x04, 0x2F, 0xE6, 0x08, 0xCC, 0x00, 0xCB, 0xCC, 0x80, 0xD4, 0xCD, 0xF2, 0xD4, 0x50, + 0x72, 0x65, 0x70, 0x61, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x74, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, + 0x2C, 0x20, 0x45, 0x53, 0x43, 0x20, 0x74, 0x6F, 0x20, 0x61, 0x62, 0x6F, 0x72, 0x74, 0x8D, 0xF1, + 0xCB, 0x7F, 0xF5, 0xF5, 0xCB, 0xBF, 0x32, 0x16, 0x00, 0x3E, 0x80, 0x20, 0x01, 0xAF, 0x32, 0x08, + 0x00, 0x3E, 0xFF, 0x32, 0x07, 0x00, 0xCD, 0xA3, 0xC3, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0xF1, + 0x20, 0x65, 0xCB, 0x6F, 0x28, 0x23, 0xCD, 0xF2, 0xD4, 0x53, 0x77, 0x69, 0x74, 0x63, 0x68, 0x20, + 0x73, 0x65, 0x74, 0x74, 0x69, 0x6E, 0x67, 0x73, 0x20, 0x6E, 0x6F, 0x74, 0x20, 0x61, 0x73, 0x73, + 0x69, 0x67, 0x6E, 0x65, 0x64, 0x8D, 0xC3, 0x71, 0xC0, 0xCD, 0x33, 0xD8, 0x57, 0xD3, 0x34, 0xCD, + 0x12, 0xC6, 0xCD, 0x2E, 0xC6, 0x32, 0x15, 0x00, 0x21, 0x80, 0x00, 0x22, 0x1D, 0x00, 0x06, 0x02, + 0xC5, 0xCD, 0x23, 0xC6, 0x3E, 0xC8, 0x32, 0x09, 0x00, 0xCD, 0x91, 0xD5, 0xD4, 0x60, 0xD6, 0x3E, + 0xD2, 0x32, 0x09, 0x00, 0xD4, 0xD2, 0xD5, 0xC1, 0x30, 0x53, 0xCD, 0x23, 0xC6, 0x10, 0xE1, 0xCD, + 0x80, 0xD4, 0xCD, 0xE3, 0xCB, 0x18, 0x5E, 0x3E, 0xC8, 0x32, 0x09, 0x00, 0x21, 0x00, 0x28, 0x22, + 0x1D, 0x00, 0xCD, 0x8A, 0xD8, 0x38, 0x48, 0x01, 0x00, 0x00, 0xC5, 0xCD, 0x23, 0xC6, 0xCD, 0x37, + 0xD9, 0xC1, 0xCD, 0x51, 0xDA, 0x28, 0x0B, 0xFE, 0x07, 0x20, 0x34, 0x0B, 0x78, 0xB1, 0x20, 0xEA, + 0x18, 0x2D, 0xCD, 0x3B, 0xDA, 0xCD, 0x51, 0xDA, 0x20, 0x25, 0xCD, 0x23, 0xC6, 0x3E, 0xD2, 0x32, + 0x09, 0x00, 0xCD, 0x6B, 0xD9, 0xCD, 0x51, 0xDA, 0x20, 0x15, 0xCD, 0x12, 0xC6, 0x3A, 0x80, 0x00, + 0xFE, 0x40, 0x28, 0x04, 0xFE, 0xE5, 0x20, 0x31, 0xCD, 0xF2, 0xD4, 0x0D, 0xCE, 0x18, 0x12, 0xCD, + 0x80, 0xD4, 0xCD, 0xF1, 0xCB, 0xCD, 0xF2, 0xD4, 0x0D, 0x55, 0x6E, 0x61, 0x62, 0x6C, 0x65, 0x20, + 0xF4, 0xCD, 0xF2, 0xD4, 0x6F, 0x20, 0x62, 0x6F, 0x6F, 0x74, 0x8D, 0x3E, 0xD0, 0xD3, 0x30, 0x3E, + 0xFF, 0xD3, 0x04, 0xCD, 0x80, 0xD4, 0xC3, 0x71, 0xC0, 0xCD, 0x58, 0xD3, 0xCC, 0x33, 0xD8, 0x57, + 0xCD, 0x23, 0xC6, 0xF1, 0x08, 0xCD, 0xF2, 0xD4, 0x0D, 0x53, 0x74, 0x61, 0x6E, 0x64, 0x62, 0x79, + 0x8D, 0xCD, 0x58, 0xD3, 0x37, 0xCA, 0x80, 0x00, 0x21, 0x80, 0x04, 0x11, 0x00, 0x00, 0x01, 0x00, + 0x22, 0xED, 0xB0, 0x08, 0xE6, 0x7F, 0x32, 0x07, 0x04, 0x3E, 0x06, 0x32, 0x06, 0x04, 0x3E, 0xFF, + 0xD3, 0xFF, 0x01, 0xE8, 0x03, 0xCD, 0x23, 0xC6, 0x21, 0x0A, 0x00, 0xCD, 0x7C, 0xD8, 0x0B, 0x78, + 0xB1, 0x20, 0xF2, 0xCD, 0xF8, 0xD4, 0xC4, 0x0A, 0xD5, 0xFE, 0x1B, 0xC0, 0x18, 0x9D, 0x7A, 0xD3, + 0x34, 0x3E, 0xDF, 0xD3, 0x04, 0x3E, 0xD4, 0xD3, 0x30, 0xDB, 0x34, 0x0F, 0x30, 0xFB, 0xDB, 0x30, + 0x01, 0x00, 0x02, 0xCD, 0x72, 0xC6, 0x3E, 0xD4, 0xD3, 0x30, 0xCD, 0x6B, 0xC6, 0x28, 0xDF, 0xDB, + 0x34, 0x0F, 0x30, 0xF6, 0xDB, 0x30, 0x10, 0xEE, 0xAF, 0xD3, 0x03, 0x79, 0xFE, 0x5A, 0x30, 0x03, + 0x87, 0x87, 0x87, 0xFE, 0xB7, 0x3E, 0x80, 0xD0, 0x3E, 0x04, 0xC9, 0xDB, 0x03, 0xFE, 0xC7, 0xC0, + 0x0C, 0xC8, 0x3E, 0x01, 0xD3, 0x03, 0x3E, 0xFA, 0xD3, 0x05, 0x18, 0xEF, 0x13, 0xFE, 0x41, 0xCA, + 0x4B, 0xC7, 0x06, 0x52, 0x18, 0x03, 0x13, 0x06, 0x57, 0xFE, 0x44, 0xC2, 0xF7, 0xC0, 0xCD, 0x5F, + 0xD3, 0x78, 0x32, 0x09, 0x00, 0xCD, 0x84, 0xD3, 0xAF, 0xB2, 0xC2, 0xF7, 0xC0, 0xB0, 0xB1, 0xCA, + 0xF7, 0xC0, 0xCD, 0x58, 0xD3, 0x20, 0x05, 0xAF, 0xB3, 0xCA, 0xF7, 0xC0, 0x22, 0x1B, 0x00, 0x0B, + 0xC5, 0xD5, 0xD4, 0x08, 0xC4, 0xD1, 0xFD, 0xE1, 0x3A, 0x11, 0x00, 0xBB, 0xDA, 0xF7, 0xC0, 0x7B, + 0x32, 0x1A, 0x00, 0xCD, 0x28, 0xCE, 0xCD, 0x9F, 0xD4, 0xFD, 0xE5, 0x3A, 0x09, 0x00, 0xFE, 0x52, + 0x28, 0x0A, 0xFE, 0x57, 0xC2, 0xF7, 0xC0, 0xCD, 0xD0, 0xC3, 0x18, 0x03, 0xCD, 0xC0, 0xC3, 0x30, + 0x09, 0xCD, 0x58, 0xD3, 0xCA, 0xE3, 0xCB, 0xC3, 0xF1, 0xCB, 0x2A, 0x1B, 0x00, 0x09, 0x22, 0x1B, + 0x00, 0xE1, 0xB7, 0xED, 0x42, 0xDA, 0x22, 0xCE, 0xE5, 0xFD, 0xE1, 0x21, 0x1A, 0x00, 0x34, 0x3A, + 0x11, 0x00, 0xBE, 0x30, 0xC4, 0xCD, 0xE6, 0xCE, 0x2A, 0x18, 0x00, 0x7D, 0x32, 0x1F, 0x00, 0xE5, + 0x23, 0x22, 0x18, 0x00, 0xED, 0x4B, 0x12, 0x00, 0x37, 0xED, 0x42, 0xE1, 0x38, 0xAB, 0x22, 0x18, + 0x00, 0x3A, 0x11, 0x00, 0x32, 0x1A, 0x00, 0xCD, 0x22, 0xCE, 0xCD, 0xF2, 0xD4, 0x4E, 0x65, 0x78, + 0x74, 0x20, 0x3D, 0xA0, 0x2A, 0x1B, 0x00, 0xCD, 0xF9, 0xD2, 0xCD, 0xF2, 0xD4, 0x0D, 0x45, 0x6E, + 0x64, 0x20, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x8D, 0xC9, 0xCD, 0xE9, 0xD2, 0xCD, 0x5F, + 0xD3, 0xCD, 0xF1, 0xCE, 0x3E, 0xD2, 0x32, 0x09, 0x00, 0xCD, 0xE6, 0xCE, 0x3E, 0x0D, 0xCD, 0x18, + 0xD5, 0xCD, 0x13, 0xCE, 0x21, 0x80, 0x00, 0x22, 0x1B, 0x00, 0xCD, 0xC0, 0xC3, 0x38, 0x0A, 0xCD, + 0x58, 0xD3, 0x28, 0x1B, 0xCD, 0x57, 0xDA, 0x28, 0x16, 0x3A, 0x1A, 0x00, 0xCD, 0x9C, 0xD4, 0xCD, + 0x58, 0xD3, 0x20, 0x05, 0xCD, 0xE6, 0xCB, 0x18, 0x03, 0xCD, 0xF4, 0xCB, 0xCD, 0x13, 0xCE, 0xCD, + 0xC1, 0xD4, 0xCD, 0x58, 0xD3, 0x20, 0x05, 0xCD, 0x9B, 0xCE, 0x20, 0xC8, 0xCD, 0x67, 0xCE, 0x20, + 0xBB, 0xC3, 0x80, 0xD4, 0xFE, 0x54, 0xCA, 0xC3, 0xC1, 0xFE, 0x4D, 0xCA, 0x86, 0xD0, 0xF5, 0xCD, + 0x5F, 0xD3, 0xF1, 0xFE, 0x53, 0x20, 0x0A, 0x13, 0xED, 0x4B, 0x18, 0x00, 0xCD, 0x72, 0xD3, 0x18, + 0x16, 0xCD, 0xB2, 0xD3, 0x38, 0x1A, 0xE5, 0xED, 0x4B, 0x12, 0x00, 0x03, 0xED, 0x42, 0xC1, 0xD2, + 0xF7, 0xC0, 0xCD, 0x79, 0xD3, 0x38, 0x10, 0xAF, 0xB4, 0xC2, 0xF7, 0xC0, 0x3A, 0x14, 0x00, 0xBD, + 0xDA, 0xF7, 0xC0, 0x7D, 0x32, 0x17, 0x00, 0xED, 0x43, 0x18, 0x00, 0x3E, 0xD3, 0x32, 0x09, 0x00, + 0xCD, 0xAF, 0xC3, 0xD0, 0xCD, 0x58, 0xD3, 0xCA, 0xE6, 0xCB, 0xC3, 0xF4, 0xCB, 0xFE, 0x4F, 0xC2, + 0xF7, 0xC0, 0x13, 0xCD, 0x45, 0xD3, 0xFE, 0x4E, 0x28, 0x0D, 0xFE, 0x46, 0xC2, 0xF7, 0xC0, 0xD9, + 0xCB, 0xB0, 0xD9, 0x3E, 0xFF, 0x18, 0x1F, 0xD9, 0xCB, 0xF0, 0xD9, 0x3E, 0x01, 0x32, 0x23, 0x00, + 0xC9, 0xD9, 0xCB, 0x70, 0xD9, 0xC8, 0xCD, 0x58, 0xD3, 0xD8, 0xC5, 0xCD, 0x33, 0xD8, 0xC1, 0xD3, + 0x34, 0xCD, 0x61, 0xD8, 0xEE, 0xA0, 0xD3, 0x04, 0xC9, 0xFE, 0x58, 0x32, 0x2C, 0x00, 0x06, 0x81, + 0x28, 0x16, 0xD6, 0x5A, 0x06, 0x01, 0x20, 0x11, 0xCD, 0x50, 0xCA, 0xC2, 0xF7, 0xC0, 0x47, 0x3A, + 0x2B, 0x00, 0x20, 0x04, 0x3D, 0x32, 0x2C, 0x00, 0x13, 0xCD, 0xE9, 0xD2, 0x78, 0xB7, 0xF5, 0xC4, + 0xB3, 0xCB, 0xAF, 0xCD, 0xFD, 0xCA, 0xF1, 0xFC, 0x8F, 0xCB, 0xCD, 0x54, 0xCA, 0xCD, 0x80, 0xD4, + 0xCD, 0x78, 0xC9, 0xDA, 0x80, 0xD4, 0xCD, 0x27, 0xC1, 0x07, 0x2F, 0x08, 0x00, 0xF3, 0xCD, 0x80, + 0xD4, 0xCD, 0xFE, 0xC9, 0xCD, 0x19, 0xCA, 0xDA, 0x80, 0xD4, 0xCD, 0x27, 0xC1, 0x08, 0x00, 0x20, + 0x4D, 0x41, 0x59, 0x20, 0x44, 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, 0x20, 0x98, 0xCD, 0xF2, 0xD4, + 0x0D, 0x43, 0x52, 0x3D, 0x50, 0x72, 0x6F, 0x63, 0x65, 0x65, 0x64, 0x20, 0x45, 0x53, 0x43, 0x3D, + 0x41, 0x62, 0x6F, 0x72, 0x74, 0xA0, 0xCD, 0x1A, 0xD4, 0x21, 0x00, 0x06, 0xCD, 0x0A, 0xCA, 0x01, + 0x00, 0x02, 0x75, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF9, 0xCD, 0x27, 0xCA, 0x30, 0x60, 0xCD, 0xF2, + 0xD4, 0x54, 0x65, 0x73, 0x74, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x21, 0x0D, 0x44, 0x69, + 0x73, 0x6B, 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x61, 0xF4, 0xCD, 0x4C, 0xCE, 0xCD, 0xF2, 0xD4, + 0x20, 0x6D, 0x61, 0x79, 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x62, 0x65, 0x65, 0x6E, 0x20, 0x44, + 0x45, 0x53, 0x54, 0x52, 0x4F, 0x59, 0x45, 0x44, 0x0D, 0x4F, 0x72, 0x69, 0x67, 0x69, 0x6E, 0x61, + 0x6C, 0x20, 0x69, 0x73, 0x20, 0x6C, 0x6F, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, 0x74, 0x20, + 0x34, 0x30, 0x30, 0x48, 0xF3, 0x2A, 0x1D, 0x00, 0xCD, 0x15, 0xD3, 0xC3, 0x80, 0xD4, 0x21, 0x00, + 0x08, 0xCD, 0x0A, 0xCA, 0xCD, 0x19, 0xCA, 0x38, 0x95, 0xCD, 0x0A, 0xCA, 0xCD, 0x27, 0xC1, 0x43, + 0x6F, 0x6D, 0x70, 0x61, 0x72, 0xE5, 0xED, 0x4B, 0x1D, 0x00, 0x11, 0x00, 0x06, 0x21, 0x00, 0x08, + 0x1A, 0xBE, 0x20, 0x09, 0x13, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xF5, 0x18, 0x0D, 0xCD, 0xF2, 0xD4, + 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x8D, 0x18, 0x03, 0xCD, 0x33, 0xCA, 0xCD, 0xFE, 0xC9, + 0xCD, 0x27, 0xCA, 0xDA, 0xCE, 0xC8, 0x18, 0xB3, 0xCD, 0x27, 0xC1, 0x06, 0x00, 0xF3, 0x06, 0x17, + 0x21, 0xD4, 0xC9, 0xDB, 0x31, 0x32, 0x1F, 0x00, 0x7E, 0x5F, 0x16, 0x00, 0xC5, 0xE5, 0xFE, 0xFF, + 0x20, 0x06, 0xCD, 0x80, 0xD4, 0xB7, 0x18, 0x35, 0xFE, 0xFE, 0x20, 0x13, 0xCD, 0x27, 0xC1, 0x20, + 0x19, 0x73, 0x74, 0x14, 0xE5, 0x3E, 0xC8, 0x32, 0x09, 0x00, 0xCD, 0x88, 0xC3, 0x18, 0x19, 0xFE, + 0xFD, 0x20, 0x04, 0xED, 0x5B, 0x12, 0x00, 0xED, 0x53, 0x18, 0x00, 0xEB, 0xCD, 0x15, 0xD3, 0xEB, + 0x3E, 0xD3, 0x32, 0x09, 0x00, 0xCD, 0xAF, 0xC3, 0xF5, 0xCD, 0xEB, 0xC9, 0xF1, 0xE1, 0xC1, 0xD8, + 0x23, 0x10, 0xB0, 0xC9, 0xFF, 0x01, 0x02, 0x03, 0x04, 0x05, 0xFF, 0x06, 0x07, 0x08, 0x09, 0x00, + 0xFF, 0xFD, 0x10, 0x20, 0x00, 0x01, 0xFF, 0xFE, 0xFF, 0xFD, 0xFF, 0x30, 0x09, 0xCD, 0x58, 0xD3, + 0xCA, 0xE6, 0xCB, 0xC3, 0xF4, 0xCB, 0xCD, 0xF2, 0xD4, 0x20, 0x4F, 0x4B, 0xA0, 0xC9, 0xCD, 0x27, + 0xC1, 0x20, 0x98, 0x21, 0x00, 0x04, 0x22, 0x1B, 0x00, 0xC9, 0x22, 0x1B, 0x00, 0xCD, 0x27, 0xC1, + 0x20, 0x50, 0x61, 0x74, 0x74, 0x16, 0x6E, 0xA0, 0xC9, 0x3E, 0xD2, 0xCD, 0x3C, 0xCA, 0xCD, 0x27, + 0xC1, 0x87, 0xCD, 0xC0, 0xC3, 0x18, 0x0C, 0x3E, 0xD7, 0xCD, 0x3C, 0xCA, 0xCD, 0x27, 0xC1, 0x88, + 0xCD, 0xD0, 0xC3, 0xF5, 0xCD, 0xEB, 0xC9, 0xF1, 0xD2, 0x80, 0xD4, 0xC9, 0x32, 0x09, 0x00, 0x3A, + 0x12, 0x00, 0x32, 0x18, 0x00, 0xDB, 0x31, 0x32, 0x1F, 0x00, 0x3E, 0x01, 0x32, 0x1A, 0x00, 0xC9, + 0xDB, 0x44, 0x3C, 0xC9, 0xCD, 0xF2, 0xD4, 0x0D, 0x54, 0x79, 0x70, 0x65, 0x20, 0x28, 0x46, 0x2C, + 0x20, 0x48, 0x29, 0xA0, 0xCD, 0xE9, 0xCA, 0xFE, 0x48, 0x20, 0x26, 0xCD, 0xF2, 0xD4, 0x55, 0x6E, + 0x69, 0x74, 0x20, 0x28, 0x30, 0x2D, 0x33, 0x46, 0x29, 0xA0, 0xCD, 0x1A, 0xD4, 0xCD, 0xB2, 0xD3, + 0xB7, 0xC2, 0x08, 0xC1, 0x7C, 0xA7, 0xC2, 0x08, 0xC1, 0x7D, 0x47, 0xCD, 0xD6, 0xC1, 0xC3, 0x51, + 0xDA, 0xFE, 0x46, 0xC2, 0xF7, 0xC0, 0xCD, 0x27, 0xC1, 0x0F, 0x28, 0x41, 0x2D, 0x44, 0x29, 0xA0, + 0xCD, 0xE9, 0xCA, 0xD6, 0x41, 0xFE, 0x04, 0xD2, 0xFE, 0xC0, 0x57, 0xCD, 0xF2, 0xD4, 0x53, 0x69, + 0x7A, 0x65, 0x20, 0x28, 0x4C, 0x2C, 0x20, 0x53, 0x29, 0xA0, 0xCD, 0xE9, 0xCA, 0xFE, 0x4C, 0x06, + 0x04, 0x28, 0x05, 0xD6, 0x53, 0x20, 0xCC, 0x47, 0xCD, 0xF2, 0xD4, 0x53, 0x70, 0x65, 0x65, 0x64, + 0x20, 0x28, 0x46, 0x2C, 0x20, 0x53, 0x29, 0xA0, 0xCD, 0xE9, 0xCA, 0xFE, 0x53, 0x28, 0x06, 0xFE, + 0x46, 0x20, 0xB0, 0xCB, 0xD8, 0x37, 0xC3, 0x4B, 0xC1, 0xCD, 0x02, 0xD5, 0xCD, 0xD1, 0xD4, 0xCD, + 0x2C, 0xD3, 0xFE, 0x0D, 0xCA, 0xD8, 0xD4, 0xCD, 0x8C, 0xD4, 0xC3, 0x80, 0xD4, 0xCD, 0x80, 0xD4, + 0xCD, 0xF2, 0xD4, 0x42, 0x61, 0x6E, 0x6B, 0xA0, 0x47, 0xCD, 0x07, 0xD3, 0xCD, 0x51, 0xD3, 0x78, + 0xCD, 0x89, 0xD2, 0x08, 0x01, 0x00, 0x10, 0xAF, 0xC4, 0x9F, 0xD4, 0x79, 0xCD, 0x07, 0xD3, 0x0C, + 0x10, 0xF6, 0xCD, 0x80, 0xD4, 0x06, 0x08, 0xCD, 0x9F, 0xD4, 0x10, 0xFB, 0x60, 0x68, 0xCD, 0x9F, + 0xD4, 0xE5, 0x01, 0x00, 0x10, 0xE5, 0xE5, 0x21, 0x00, 0x00, 0x11, 0x62, 0xCB, 0xA7, 0xED, 0x52, + 0xEB, 0xE1, 0x19, 0xE1, 0x30, 0x11, 0xE5, 0xE5, 0x21, 0x00, 0x00, 0x11, 0x71, 0xCB, 0xA7, 0xED, + 0x52, 0xEB, 0xE1, 0x19, 0xE1, 0x30, 0x1A, 0x08, 0xFE, 0x01, 0x28, 0x02, 0xD3, 0x40, 0x08, 0x56, + 0x3E, 0x55, 0x77, 0xBE, 0x20, 0x05, 0x2F, 0x77, 0xBE, 0x28, 0x05, 0x72, 0x16, 0x58, 0x18, 0x09, + 0x72, 0x23, 0x0B, 0x78, 0xB1, 0x20, 0xBE, 0x16, 0x5E, 0x08, 0x47, 0x08, 0x3E, 0x01, 0xB8, 0x28, + 0x02, 0xD3, 0x40, 0x7A, 0xCD, 0xA1, 0xD4, 0xE1, 0x3E, 0x10, 0x84, 0x67, 0x20, 0xA0, 0xC9, 0x21, + 0xB2, 0xCB, 0x36, 0xAA, 0x01, 0x01, 0x06, 0x79, 0xCD, 0x89, 0xD2, 0xD3, 0x40, 0x3E, 0x55, 0xBE, + 0x3E, 0x01, 0xD3, 0x40, 0x79, 0xC5, 0xE5, 0xCC, 0xFD, 0xCA, 0xE1, 0xC1, 0x0C, 0x10, 0xE8, 0x36, + 0x55, 0xC9, 0x55, 0x01, 0x22, 0x00, 0x11, 0x5E, 0x00, 0x21, 0xC1, 0xCB, 0xED, 0xB0, 0xC3, 0x5E, + 0x00, 0x01, 0x00, 0x20, 0x11, 0x00, 0x01, 0x21, 0x00, 0xC0, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xD1, + 0xE1, 0xC1, 0xD3, 0x40, 0xED, 0xB0, 0x3E, 0x55, 0x32, 0xB2, 0xCB, 0x32, 0x2B, 0x00, 0x3E, 0x01, + 0xD3, 0x40, 0xC9, 0xCD, 0x22, 0xCE, 0x3A, 0x21, 0x00, 0xCD, 0x0E, 0xCC, 0xCD, 0x1F, 0xCC, 0x18, + 0x12, 0xCD, 0x22, 0xCE, 0x3A, 0x44, 0x00, 0xCD, 0x0E, 0xCC, 0x3A, 0x45, 0x00, 0xCD, 0xFE, 0xD2, + 0xCD, 0x7C, 0xCC, 0xCD, 0x80, 0xD4, 0x3A, 0x09, 0x00, 0xB7, 0xF8, 0xC3, 0x8D, 0xC0, 0xF5, 0xCD, + 0x27, 0xC1, 0x20, 0x02, 0xAD, 0x3A, 0x09, 0x00, 0xCD, 0xA1, 0xD4, 0xF1, 0xC3, 0xF3, 0xD2, 0x2E, + 0xD9, 0x3A, 0x09, 0x00, 0xE6, 0x7F, 0xFE, 0x48, 0x28, 0x0F, 0xFE, 0x53, 0x28, 0x0B, 0x2E, 0x9F, + 0xFE, 0x52, 0x28, 0x05, 0x2E, 0xFF, 0xFE, 0x57, 0xC0, 0xCD, 0x51, 0xD3, 0x3A, 0x21, 0x00, 0xA5, + 0x21, 0x59, 0xCC, 0x17, 0xD4, 0x35, 0xD3, 0x30, 0x0C, 0xF5, 0xCD, 0xA9, 0xCC, 0xF1, 0xB7, 0xC8, + 0xCD, 0xF2, 0xD4, 0x2C, 0xA0, 0xB7, 0xC8, 0x18, 0xEA, 0x0F, 0x10, 0x07, 0xF9, 0x08, 0x97, 0x08, + 0x20, 0x9A, 0x19, 0x63, 0x14, 0x64, 0x20, 0x10, 0x46, 0x6F, 0x75, 0x6E, 0xE4, 0x0C, 0x82, 0x18, + 0x4C, 0x6F, 0x73, 0xF4, 0x18, 0x19, 0x71, 0x75, 0x65, 0x73, 0xF4, 0x9E, 0xCD, 0x51, 0xD3, 0x3A, + 0x44, 0x00, 0xF5, 0x21, 0xC8, 0xCC, 0xCD, 0x9F, 0xCC, 0xE1, 0x3A, 0x45, 0x00, 0x28, 0x0D, 0x6F, + 0x3C, 0xC8, 0x84, 0xFE, 0x10, 0xC8, 0x7D, 0xCD, 0xF2, 0xD4, 0x3B, 0xA0, 0x21, 0x46, 0xCD, 0x3C, + 0xC8, 0x3D, 0x28, 0x05, 0xCD, 0x35, 0xD3, 0x18, 0xF8, 0x7E, 0xE6, 0x7F, 0xFE, 0x20, 0xD4, 0xA1, + 0xD4, 0x30, 0x0F, 0xE5, 0x21, 0x90, 0xCD, 0x3D, 0xF4, 0x35, 0xD3, 0xF2, 0xB7, 0xCC, 0xCD, 0xA9, + 0xCC, 0xE1, 0x7E, 0x23, 0xB7, 0xF8, 0x18, 0xE1, 0x01, 0x06, 0x20, 0x26, 0x20, 0x07, 0x20, 0x8E, + 0x01, 0x06, 0x11, 0x84, 0x03, 0x09, 0x0A, 0x86, 0x01, 0x06, 0x20, 0x43, 0x14, 0x72, 0x12, 0x20, + 0x54, 0x72, 0x61, 0x63, 0xEB, 0x01, 0x07, 0x20, 0x8E, 0x01, 0x05, 0x11, 0x84, 0x03, 0x09, 0x0B, + 0x85, 0x0F, 0x10, 0x07, 0xF9, 0x01, 0x08, 0x11, 0x03, 0x82, 0x01, 0x0D, 0x0B, 0x88, 0x01, 0x07, + 0x11, 0x03, 0x82, 0x01, 0x07, 0x11, 0x0C, 0x82, 0x43, 0x61, 0x6E, 0x10, 0x4C, 0x6F, 0x63, 0x61, + 0x74, 0x65, 0x20, 0x53, 0x12, 0x94, 0x0F, 0x08, 0x97, 0x01, 0x53, 0x65, 0x6C, 0x12, 0x20, 0x9B, + 0x01, 0x53, 0x65, 0x6C, 0x12, 0x20, 0x48, 0x65, 0x61, 0xE4, 0x49, 0x6E, 0x64, 0x65, 0x78, 0x20, + 0x50, 0x75, 0x6C, 0x73, 0x65, 0x20, 0x84, 0x06, 0x20, 0x52, 0x61, 0x6E, 0x67, 0x65, 0x20, 0x82, + 0x1D, 0x42, 0x75, 0x66, 0x66, 0x96, 0x1D, 0x13, 0x66, 0x72, 0x6F, 0x6D, 0x20, 0x9B, 0x0F, 0x1E, + 0x11, 0x13, 0x4C, 0x6F, 0xF7, 0x04, 0x20, 0x09, 0x0A, 0x85, 0x03, 0x19, 0x70, 0x14, 0x74, 0x95, + 0x01, 0x07, 0x11, 0x0C, 0x82, 0x0E, 0x20, 0x44, 0x6F, 0x65, 0x1F, 0x10, 0x43, 0x6F, 0x6D, 0x70, + 0x61, 0x72, 0xE5, 0x01, 0x0D, 0x0B, 0x88, 0x18, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x6D, 0x65, 0x6E, + 0x74, 0x20, 0x82, 0x0E, 0x20, 0x41, 0x6C, 0x69, 0x67, 0x6E, 0x6D, 0x65, 0x6E, 0x74, 0x20, 0x82, + 0x20, 0x54, 0x65, 0x73, 0xF4, 0x46, 0x61, 0x69, 0x6C, 0x15, 0x20, 0x74, 0x6F, 0xA0, 0x45, 0x72, + 0x72, 0x94, 0x1A, 0xA0, 0x54, 0x69, 0x6D, 0x65, 0x6F, 0x75, 0xF4, 0x19, 0x5A, 0x16, 0xEF, 0x53, + 0x65, 0x65, 0xEB, 0x19, 0x61, 0xE4, 0x57, 0x1C, 0x74, 0xE5, 0x4F, 0x63, 0x63, 0x75, 0x72, 0x72, + 0x95, 0x20, 0x44, 0x75, 0x1C, 0x6E, 0x67, 0xA0, 0x20, 0x41, 0x66, 0x74, 0x16, 0xA0, 0x43, 0x52, + 0x43, 0xA0, 0x56, 0x16, 0x69, 0x66, 0xF9, 0x48, 0x65, 0x61, 0x64, 0x96, 0x1B, 0xA0, 0x6E, 0x6F, + 0x74, 0xA0, 0x2C, 0xA0, 0x65, 0x63, 0xF4, 0x41, 0x43, 0x4B, 0xA0, 0x6F, 0xF2, 0x65, 0xE4, 0x65, + 0xF2, 0x20, 0x50, 0x72, 0x6F, 0x74, 0x12, 0x95, 0x44, 0x61, 0x74, 0x61, 0xA0, 0x52, 0xE5, 0x46, + 0x61, 0x75, 0x6C, 0xF4, 0x44, 0x1C, 0x76, 0xE5, 0x72, 0xE9, 0x4E, 0x6F, 0xA0, 0x42, 0x75, 0x73, + 0xF9, 0x73, 0xA0, 0x21, 0x1A, 0x00, 0x7E, 0xF5, 0x36, 0xFF, 0xCD, 0x28, 0xCE, 0xF1, 0x32, 0x1A, + 0x00, 0xC9, 0xCD, 0x28, 0xCE, 0xC3, 0x80, 0xD4, 0xCD, 0x58, 0xD3, 0x28, 0x1F, 0xCD, 0x9F, 0xD4, + 0x3A, 0x17, 0x00, 0xCD, 0x07, 0xD3, 0xCD, 0x9F, 0xD4, 0x2A, 0x18, 0x00, 0x7C, 0xCD, 0x07, 0xD3, + 0x7D, 0xCD, 0xFE, 0xD2, 0x3A, 0x1A, 0x00, 0xB7, 0xF8, 0xC3, 0xF3, 0xD2, 0xED, 0x5B, 0x1A, 0x00, + 0x3A, 0x18, 0x00, 0x57, 0xCD, 0x9F, 0xD4, 0x3A, 0x17, 0x00, 0xCD, 0x07, 0xD3, 0xCD, 0x9F, 0xD4, + 0x7A, 0xCD, 0xFE, 0xD2, 0x7B, 0x18, 0xE0, 0x21, 0x17, 0x00, 0x3A, 0x0C, 0x00, 0xFE, 0xFF, 0x46, + 0x38, 0x09, 0x34, 0x3A, 0x14, 0x00, 0xBE, 0x3C, 0xD0, 0x36, 0x00, 0x2A, 0x18, 0x00, 0x7D, 0x32, + 0x1F, 0x00, 0xE5, 0xED, 0x5B, 0x0A, 0x00, 0xB7, 0xED, 0x52, 0xE1, 0x28, 0x09, 0x23, 0x38, 0x02, + 0x2B, 0x2B, 0x22, 0x18, 0x00, 0xC9, 0x78, 0x32, 0x17, 0x00, 0xC9, 0x3A, 0x11, 0x00, 0x3C, 0x47, + 0x21, 0x1A, 0x00, 0xCD, 0x58, 0xD3, 0x28, 0x09, 0x3E, 0x01, 0x86, 0x77, 0xB8, 0xD8, 0x90, 0x77, + 0xC9, 0x3A, 0x15, 0x00, 0xE6, 0x07, 0xE5, 0x21, 0xDE, 0xCE, 0xCD, 0x84, 0xD2, 0x7E, 0xE1, 0xF5, + 0x86, 0xB8, 0x38, 0x02, 0x05, 0x90, 0x77, 0x47, 0xF1, 0xFE, 0x04, 0x28, 0x06, 0xFE, 0x06, 0x28, + 0x02, 0x05, 0xC9, 0x3E, 0x02, 0x05, 0x28, 0x03, 0x05, 0xC0, 0x3D, 0x77, 0x3D, 0xC9, 0x05, 0x04, + 0x02, 0x04, 0x06, 0x0B, 0x03, 0x06, 0x3A, 0x08, 0x00, 0x07, 0x2F, 0xE6, 0x01, 0x32, 0x1A, 0x00, + 0xC9, 0xCD, 0xF2, 0xD4, 0x53, 0x74, 0x61, 0x72, 0xF4, 0x21, 0x00, 0x00, 0xCD, 0x73, 0xCF, 0x38, + 0xF0, 0x22, 0x18, 0x00, 0xCD, 0xF2, 0xD4, 0x45, 0x6E, 0xE4, 0x2A, 0x12, 0x00, 0xCD, 0x73, 0xCF, + 0x38, 0xF2, 0x22, 0x0A, 0x00, 0xAF, 0x32, 0x17, 0x00, 0x32, 0x0C, 0x00, 0x3A, 0x14, 0x00, 0xB7, + 0xC8, 0xCD, 0xF2, 0xD4, 0x53, 0x75, 0x72, 0x66, 0x61, 0x63, 0x65, 0x20, 0xA8, 0x47, 0x04, 0x3E, + 0x00, 0xF5, 0xFE, 0x10, 0x38, 0x05, 0xCD, 0xFE, 0xD2, 0x18, 0x03, 0xCD, 0x07, 0xD3, 0xCD, 0xF2, + 0xD4, 0xAF, 0xF1, 0x3C, 0x10, 0xEB, 0xCD, 0xF2, 0xD4, 0x41, 0x6C, 0x6C, 0x29, 0x20, 0x5B, 0x41, + 0x6C, 0x6C, 0x5D, 0xA0, 0xCD, 0x1A, 0xD4, 0x21, 0xFF, 0x00, 0xCD, 0x79, 0xD3, 0xAF, 0xB4, 0x20, + 0xB4, 0x7D, 0x32, 0x0C, 0x00, 0xFE, 0xFF, 0xC8, 0x3A, 0x14, 0x00, 0xBD, 0x38, 0xA7, 0x7D, 0x32, + 0x17, 0x00, 0xC9, 0xCD, 0x27, 0xC1, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x79, 0x6C, 0x69, 0x6E, 0x64, + 0x16, 0x20, 0x28, 0x30, 0xAD, 0xE5, 0x2A, 0x12, 0x00, 0xCD, 0x15, 0xD3, 0xE1, 0xCD, 0xF2, 0xD4, + 0x29, 0x20, 0xDB, 0xCD, 0x15, 0xD3, 0xCD, 0xF2, 0xD4, 0x5D, 0xA0, 0xCD, 0x1A, 0xD4, 0xCD, 0x79, + 0xD3, 0xEB, 0x2A, 0x12, 0x00, 0xB7, 0xED, 0x52, 0xEB, 0xC9, 0xDB, 0x04, 0xE6, 0x08, 0x20, 0x14, + 0x3E, 0x09, 0xD3, 0x02, 0x3E, 0x84, 0xD3, 0x00, 0xD9, 0x0E, 0x00, 0xD9, 0x18, 0x3B, 0xCD, 0x33, + 0xD0, 0xCD, 0x22, 0xD0, 0x3E, 0x0A, 0xCD, 0x06, 0xD0, 0x21, 0xD0, 0x07, 0xCD, 0x7C, 0xD8, 0x3E, + 0x08, 0xCD, 0x06, 0xD0, 0x16, 0x64, 0x15, 0x28, 0xEB, 0x21, 0x2B, 0xD0, 0x3E, 0x19, 0x06, 0x09, + 0xCD, 0x06, 0xD0, 0xD9, 0x79, 0xD9, 0x4F, 0xED, 0xA3, 0x28, 0xEB, 0xCD, 0x0F, 0xD0, 0xCD, 0x0F, + 0xD0, 0x28, 0xE6, 0xFE, 0x0D, 0x3E, 0x09, 0x20, 0xE7, 0x3E, 0xFF, 0xCD, 0x18, 0xD5, 0x3E, 0x0D, + 0xCD, 0x18, 0xD5, 0xC3, 0x80, 0xD4, 0xD9, 0x0C, 0x0C, 0xED, 0x79, 0x0D, 0x0D, 0xD9, 0xC9, 0xD5, + 0x11, 0xA0, 0x8C, 0xCD, 0xF8, 0xD4, 0xC4, 0x0A, 0xD5, 0x20, 0x05, 0x1B, 0x7A, 0xB3, 0x20, 0xF3, + 0xD1, 0xC9, 0x06, 0x08, 0xAF, 0xCD, 0x18, 0xD5, 0x10, 0xFA, 0xC9, 0x90, 0xC0, 0xA0, 0x90, 0x88, + 0x84, 0x82, 0x01, 0xCD, 0x79, 0xD3, 0xD8, 0x7D, 0xFE, 0x08, 0xD2, 0xF7, 0xC0, 0x21, 0x4F, 0xD0, + 0xCD, 0x84, 0xD2, 0x4E, 0xED, 0x78, 0x3C, 0xCA, 0xF7, 0xC0, 0x79, 0xD9, 0x4F, 0xD9, 0xC9, 0x00, + 0x20, 0x50, 0x60, 0x70, 0x80, 0x90, 0xF0, 0xCD, 0x84, 0xD3, 0xC5, 0xD5, 0xE5, 0xED, 0xB0, 0xE1, + 0xD1, 0xC1, 0x18, 0x03, 0xCD, 0x84, 0xD3, 0x1A, 0xBE, 0x28, 0x13, 0xCD, 0xF9, 0xD2, 0x7E, 0xCD, + 0x9C, 0xD4, 0x1A, 0xCD, 0x9C, 0xD4, 0xEB, 0xCD, 0xF9, 0xD2, 0xEB, 0xCD, 0x80, 0xD4, 0x13, 0x23, + 0x0B, 0x78, 0xB1, 0x20, 0xE2, 0xC9, 0x13, 0x2A, 0x05, 0x00, 0xCD, 0x79, 0xD3, 0xCD, 0xF9, 0xD2, + 0x7E, 0xCD, 0x9C, 0xD4, 0xCD, 0x1A, 0xD4, 0xCD, 0xFA, 0xD0, 0x30, 0x07, 0xCD, 0xF2, 0xD4, 0x3F, + 0x8D, 0x18, 0xEA, 0xF5, 0xAF, 0x80, 0x20, 0x07, 0xF1, 0xB7, 0x20, 0x0B, 0x23, 0x18, 0x08, 0x48, + 0x06, 0x00, 0xEB, 0xED, 0xB0, 0xEB, 0xF1, 0x22, 0x05, 0x00, 0xFE, 0x2E, 0xC8, 0xFE, 0x2D, 0x20, + 0xCC, 0x2B, 0x22, 0x05, 0x00, 0x18, 0xC6, 0xCD, 0x90, 0xD3, 0x38, 0x05, 0xC5, 0xCD, 0xFA, 0xD0, + 0xD1, 0xDA, 0xF7, 0xC0, 0xC2, 0xF7, 0xC0, 0x80, 0xCA, 0xF7, 0xC0, 0xC5, 0xD5, 0xE5, 0x11, 0x5E, + 0x00, 0x1A, 0xBE, 0x20, 0x04, 0x13, 0x23, 0x10, 0xF8, 0xE1, 0xE5, 0x06, 0x10, 0xCC, 0x6B, 0xD1, + 0xE1, 0xD1, 0xC1, 0x23, 0x1B, 0x7A, 0xB3, 0x20, 0xE2, 0xC9, 0xE5, 0x06, 0x00, 0x21, 0x5E, 0x00, + 0xCD, 0x3E, 0xD3, 0xB7, 0x28, 0x2D, 0xFE, 0x2D, 0x28, 0x29, 0xFE, 0x2E, 0x28, 0x25, 0x13, 0x4F, + 0xFE, 0x27, 0x28, 0x12, 0xFE, 0x22, 0x28, 0x0E, 0x1B, 0xE5, 0xCD, 0xB2, 0xD3, 0x7D, 0xE1, 0x38, + 0x13, 0x77, 0x23, 0x04, 0x18, 0xDA, 0x1A, 0x13, 0xB7, 0x28, 0x08, 0xB9, 0x28, 0xD2, 0x77, 0x23, + 0x04, 0x18, 0xF3, 0xB7, 0x11, 0x5E, 0x00, 0xE1, 0xC9, 0xFE, 0x4D, 0x20, 0x01, 0x13, 0x01, 0x80, + 0x00, 0x2A, 0x03, 0x00, 0xCD, 0x7F, 0xD3, 0x1E, 0x10, 0xAF, 0xB0, 0x20, 0x0A, 0x3E, 0x0F, 0xB9, + 0x38, 0x05, 0xAF, 0xB1, 0x28, 0x01, 0x59, 0xC5, 0x43, 0xCD, 0x6B, 0xD1, 0x22, 0x03, 0x00, 0xC1, + 0x79, 0x93, 0x4F, 0x30, 0x01, 0x05, 0x78, 0xB1, 0x20, 0xDD, 0xC9, 0xCD, 0xF9, 0xD2, 0xC5, 0xE5, + 0xD5, 0x0E, 0x00, 0x1E, 0x04, 0x3E, 0x03, 0xA1, 0xCC, 0xAD, 0xD1, 0xCD, 0xAD, 0xD1, 0x7E, 0xCD, + 0xFE, 0xD2, 0x1C, 0x1C, 0x23, 0x0C, 0x10, 0xED, 0xCD, 0xAD, 0xD1, 0x3E, 0x3A, 0xBB, 0x20, 0xF8, + 0xD1, 0xE1, 0xC1, 0x7E, 0x23, 0xCD, 0x9D, 0xD1, 0x10, 0xF9, 0xC3, 0x80, 0xD4, 0xE6, 0x7F, 0xFE, + 0x7F, 0x28, 0x05, 0xFE, 0x20, 0xD2, 0xA1, 0xD4, 0x3E, 0x2E, 0xC3, 0xA1, 0xD4, 0x1C, 0xC3, 0x9F, + 0xD4, 0xCD, 0x90, 0xD3, 0x38, 0x04, 0xC5, 0xCD, 0xFA, 0xD0, 0xDA, 0xF7, 0xC0, 0xC2, 0xF7, 0xC0, + 0x80, 0xCA, 0xF7, 0xC0, 0xC1, 0xE5, 0xEB, 0xED, 0xA0, 0xE2, 0xD3, 0xD1, 0x3D, 0x20, 0xF8, 0xE1, + 0xED, 0xB0, 0xC9, 0xE1, 0xC9, 0xCD, 0x72, 0xD3, 0xE9, 0xCD, 0x72, 0xD3, 0x4D, 0xED, 0x78, 0xCD, + 0xFE, 0xD2, 0xC3, 0x80, 0xD4, 0xCD, 0xB2, 0xD3, 0x45, 0xCD, 0x72, 0xD3, 0x4D, 0xED, 0x41, 0xC9, + 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xCD, 0x45, 0xD3, 0xA7, 0x28, 0x74, 0xE5, 0xFE, 0x2B, 0x28, + 0x2D, 0xFE, 0x2D, 0x28, 0x36, 0xFE, 0x2A, 0x28, 0x41, 0xFE, 0x2F, 0x28, 0x50, 0xCD, 0xB2, 0xD3, + 0xDA, 0xF7, 0xC0, 0xCD, 0x45, 0xD3, 0xA7, 0xC2, 0xF7, 0xC0, 0xEB, 0xE1, 0xE5, 0xD5, 0x19, 0xCD, + 0x76, 0xD2, 0xCD, 0xF2, 0xD4, 0x2C, 0xA0, 0xD1, 0xE1, 0xA7, 0xED, 0x52, 0x18, 0x42, 0x13, 0xCD, + 0xB2, 0xD3, 0xDA, 0xF7, 0xC0, 0xEB, 0xE3, 0x19, 0xD1, 0x18, 0xBB, 0x13, 0xCD, 0xB2, 0xD3, 0xDA, + 0xF7, 0xC0, 0xEB, 0xE3, 0xA7, 0xED, 0x52, 0xD1, 0x18, 0xAC, 0x13, 0xCD, 0xB2, 0xD3, 0xDA, 0xF7, + 0xC0, 0xEB, 0xE3, 0xC5, 0x42, 0x4B, 0xCD, 0xA6, 0xD2, 0xC1, 0xD1, 0x18, 0x99, 0x13, 0xCD, 0xB2, + 0xD3, 0xDA, 0xF7, 0xC0, 0xEB, 0xE3, 0xC5, 0x42, 0x4B, 0xCD, 0xBF, 0xD2, 0xC1, 0xD1, 0x18, 0x86, + 0xCD, 0x76, 0xD2, 0xC3, 0x80, 0xD4, 0xCD, 0xF9, 0xD2, 0xCD, 0x9F, 0xD4, 0xCD, 0x96, 0xD2, 0x3E, + 0x2E, 0xC3, 0xA1, 0xD4, 0x85, 0x6F, 0xD0, 0x24, 0xC9, 0xC5, 0x47, 0x3E, 0x01, 0x05, 0xFA, 0x94, + 0xD2, 0x07, 0x30, 0xF9, 0xC1, 0xC9, 0x01, 0x0A, 0x00, 0xCD, 0xBF, 0xD2, 0x7B, 0xF5, 0x7C, 0xB5, + 0xC4, 0x96, 0xD2, 0xF1, 0x18, 0x61, 0x11, 0x00, 0x00, 0xEB, 0x3E, 0x10, 0xB7, 0xCB, 0x43, 0x28, + 0x01, 0x09, 0xCB, 0x1C, 0xCB, 0x1D, 0xCB, 0x1A, 0xCB, 0x1B, 0x3D, 0x20, 0xEF, 0xEB, 0xC9, 0x11, + 0x00, 0x00, 0x7A, 0xB8, 0x20, 0x02, 0x7B, 0xB9, 0x3F, 0xD8, 0xEB, 0x3E, 0x10, 0x37, 0xCB, 0x13, + 0xCB, 0x12, 0xCB, 0x15, 0xCB, 0x14, 0x30, 0x05, 0xB7, 0xED, 0x42, 0x18, 0x06, 0xED, 0x42, 0x30, + 0x02, 0x1D, 0x09, 0x3D, 0x20, 0xE7, 0xEB, 0xB7, 0xC9, 0xF5, 0xCD, 0x45, 0xD3, 0xB7, 0xC2, 0xF7, + 0xC0, 0xF1, 0xC9, 0xCD, 0xF2, 0xD4, 0xA0, 0x18, 0x05, 0x7C, 0xCD, 0xFE, 0xD2, 0x7D, 0xF5, 0x0F, + 0x0F, 0x0F, 0x0F, 0xCD, 0x07, 0xD3, 0xF1, 0xE6, 0x0F, 0xC6, 0x30, 0xFE, 0x3A, 0xDA, 0xA1, 0xD4, + 0xC6, 0x07, 0xC3, 0xA1, 0xD4, 0x7C, 0xB7, 0x28, 0x0C, 0xCD, 0x07, 0xD3, 0x7D, 0xCD, 0xFE, 0xD2, + 0xCD, 0xF2, 0xD4, 0xC8, 0xC9, 0x7D, 0xFE, 0x0A, 0x30, 0xF3, 0x18, 0xDB, 0xFE, 0x61, 0xD8, 0xFE, + 0x7B, 0xD0, 0xD6, 0x20, 0xC9, 0xF5, 0x7E, 0x23, 0x3D, 0xF2, 0x36, 0xD3, 0xF1, 0xC9, 0xCD, 0x45, + 0xD3, 0xFE, 0x2C, 0xC0, 0x13, 0x1A, 0xFE, 0x20, 0x28, 0x04, 0xFE, 0x09, 0x20, 0xDE, 0x13, 0x18, + 0xF4, 0xCD, 0xF2, 0xD4, 0x20, 0x3E, 0xA0, 0xC9, 0x3A, 0x08, 0x00, 0xE6, 0x80, 0x07, 0xC9, 0x3A, + 0x07, 0x00, 0xFE, 0xFF, 0xC0, 0xCD, 0x27, 0xC1, 0x1D, 0x0F, 0x53, 0x65, 0x6C, 0x12, 0x95, 0xC3, + 0xDE, 0xD4, 0xCD, 0x79, 0xD3, 0xD0, 0xC3, 0xF7, 0xC0, 0xCD, 0xB2, 0xD3, 0xC3, 0xE9, 0xD2, 0xCD, + 0x90, 0xD3, 0x18, 0xF8, 0xCD, 0x90, 0xD3, 0x38, 0xED, 0xE5, 0xCD, 0x72, 0xD3, 0xEB, 0xE1, 0xC9, + 0xCD, 0xB2, 0xD3, 0xCD, 0x3E, 0xD3, 0xFE, 0x53, 0xE5, 0x20, 0x0A, 0x13, 0xCD, 0xB2, 0xD3, 0x38, + 0xD5, 0x44, 0x4D, 0xE1, 0xC9, 0xCD, 0xB2, 0xD3, 0x38, 0xF9, 0xC1, 0xC5, 0xED, 0x42, 0x23, 0xB7, + 0x18, 0xEF, 0xCD, 0x3E, 0xD3, 0xD5, 0xE5, 0xCD, 0xEE, 0xD3, 0xFE, 0x2E, 0xE1, 0xD1, 0x28, 0x0D, + 0xCD, 0x09, 0xD4, 0xD8, 0xCD, 0xEE, 0xD3, 0xFE, 0x48, 0x28, 0x09, 0xB7, 0xC9, 0xCD, 0xFE, 0xD3, + 0xD8, 0xCD, 0xD9, 0xD3, 0xCD, 0x4E, 0xD3, 0xB7, 0xC9, 0x21, 0x00, 0x00, 0xCD, 0xFE, 0xD3, 0xD8, + 0x13, 0xC5, 0x44, 0x4D, 0x29, 0x29, 0x09, 0x29, 0xC1, 0xCD, 0x84, 0xD2, 0x18, 0xEE, 0x21, 0x00, + 0x00, 0xCD, 0x09, 0xD4, 0xD8, 0x13, 0x29, 0x29, 0x29, 0x29, 0x85, 0x6F, 0x18, 0xF3, 0x1A, 0xFE, + 0x30, 0xD8, 0xFE, 0x3A, 0x3F, 0xD8, 0xD6, 0x30, 0xC9, 0xCD, 0xFE, 0xD3, 0xD0, 0xCD, 0x2C, 0xD3, + 0xFE, 0x41, 0xD8, 0xFE, 0x47, 0x3F, 0xD8, 0xD6, 0x37, 0xC9, 0x11, 0x5E, 0x00, 0x3E, 0x24, 0xC5, + 0xE5, 0x4F, 0x06, 0x00, 0x62, 0x6B, 0xCD, 0x02, 0xD5, 0xCD, 0xC7, 0xD4, 0xFE, 0x10, 0x28, 0xF6, + 0xFE, 0x05, 0xCC, 0x80, 0xD4, 0x28, 0xEF, 0xFE, 0x0D, 0x28, 0x3F, 0xFE, 0x08, 0x28, 0x04, 0xFE, + 0x7F, 0x20, 0x10, 0x05, 0xFA, 0x22, 0xD4, 0x2B, 0xCD, 0x85, 0xD4, 0x7E, 0xFE, 0x20, 0xDC, 0x85, + 0xD4, 0x18, 0xD3, 0xFE, 0x16, 0x20, 0x10, 0x05, 0xFA, 0x22, 0xD4, 0x2B, 0xCD, 0x85, 0xD4, 0x7E, + 0xFE, 0x20, 0xDC, 0x85, 0xD4, 0x18, 0xF0, 0xF5, 0xCD, 0x8C, 0xD4, 0xF1, 0xFE, 0x15, 0xCC, 0x80, + 0xD4, 0x28, 0xAF, 0x77, 0x23, 0x04, 0x79, 0xB8, 0x20, 0xAC, 0x36, 0x00, 0x78, 0xE1, 0xC1, 0xB7, + 0xCD, 0xF2, 0xD4, 0x8D, 0xC9, 0xCD, 0xF2, 0xD4, 0x08, 0x20, 0x88, 0xC9, 0xFE, 0x20, 0x30, 0x11, + 0xFE, 0x0D, 0x28, 0x0D, 0xC6, 0x40, 0xCD, 0xF2, 0xD4, 0xDE, 0x18, 0x05, 0xCD, 0xF3, 0xD2, 0x3E, + 0x20, 0xF5, 0xE6, 0x7F, 0xD9, 0xCB, 0x78, 0xD9, 0xC4, 0x29, 0xD5, 0xD9, 0xCB, 0x60, 0xD9, 0xCC, + 0x18, 0xD5, 0xFE, 0x0D, 0x3E, 0x0A, 0xCC, 0xA1, 0xD4, 0xCC, 0xC1, 0xD4, 0xCD, 0x21, 0xC8, 0xF1, + 0xC9, 0xCD, 0xF8, 0xD4, 0xC4, 0x0A, 0xD5, 0xFE, 0x10, 0xCC, 0x4D, 0xD5, 0xFE, 0x13, 0xCC, 0x02, + 0xD5, 0xFE, 0x03, 0x28, 0x03, 0xFE, 0x1B, 0xC0, 0x31, 0xF0, 0x7F, 0xCD, 0x8C, 0xD4, 0xCD, 0x80, + 0xD4, 0xC3, 0x8D, 0xC0, 0xF5, 0x7E, 0x23, 0xB7, 0x28, 0x06, 0xCD, 0xA1, 0xD4, 0xF2, 0xE5, 0xD4, + 0xF1, 0xC9, 0xE3, 0xCD, 0xE4, 0xD4, 0xE3, 0xC9, 0xD9, 0xED, 0x78, 0xD9, 0xE6, 0x40, 0xC8, 0x3E, + 0xFF, 0xC9, 0xCD, 0x21, 0xC8, 0xCD, 0xF8, 0xD4, 0x28, 0xF8, 0xCD, 0xF8, 0xD4, 0x28, 0xFB, 0xD9, + 0x0C, 0xED, 0x78, 0x0D, 0xD9, 0xE6, 0x7F, 0xC9, 0xF5, 0xD9, 0xED, 0x78, 0xE6, 0x80, 0x28, 0xFA, + 0x0C, 0xF1, 0xED, 0x79, 0xF5, 0x0D, 0xD9, 0xF1, 0xC9, 0xF5, 0xC5, 0x06, 0xF7, 0x10, 0xFE, 0xC1, + 0xCD, 0xC1, 0xD4, 0xFE, 0x10, 0x28, 0x24, 0xDB, 0x54, 0xE6, 0x20, 0x20, 0xF3, 0xF1, 0xCB, 0xFF, + 0xD3, 0x54, 0xCB, 0xBF, 0xD3, 0x54, 0xCB, 0xFF, 0xD3, 0x54, 0xCB, 0xBF, 0xC9, 0xF5, 0xD9, 0x3E, + 0x80, 0xA8, 0x47, 0xD9, 0xCB, 0x7F, 0x3E, 0x11, 0xC4, 0x3E, 0xD5, 0xF1, 0xC9, 0xFD, 0xC7, 0x40, + 0xC4, 0xF7, 0xC0, 0x39, 0xD1, 0xD9, 0xD1, 0xF7, 0xC0, 0xD5, 0xD1, 0xF0, 0xD1, 0xBE, 0xCF, 0xF7, + 0xC0, 0xF7, 0xC0, 0xF7, 0xC0, 0x57, 0xD0, 0xF7, 0xC0, 0xE5, 0xD1, 0xF7, 0xC0, 0xC7, 0xD0, 0x7C, + 0xC6, 0xA4, 0xC7, 0x39, 0xC8, 0xF7, 0xC0, 0x64, 0xD0, 0x86, 0xC6, 0xF7, 0xC0, 0xF7, 0xC0, 0xB1, + 0xD1, 0x16, 0x02, 0xD5, 0xCD, 0x02, 0xD6, 0xD1, 0xD0, 0xD5, 0x3E, 0x0A, 0x32, 0x18, 0x00, 0xCD, + 0x60, 0xD6, 0xCD, 0x02, 0xD6, 0xD1, 0xD0, 0x15, 0x20, 0xE9, 0x18, 0x50, 0xD5, 0x3A, 0x17, 0x00, + 0xF5, 0x3A, 0x18, 0x00, 0xF5, 0x3A, 0x15, 0x00, 0xF5, 0xCB, 0x87, 0x32, 0x15, 0x00, 0xCD, 0x02, + 0xD6, 0xF1, 0x32, 0x15, 0x00, 0xF1, 0x32, 0x18, 0x00, 0xF1, 0x32, 0x17, 0x00, 0xCD, 0x60, 0xD6, + 0xD1, 0xC9, 0x1E, 0x02, 0x16, 0x0A, 0xD5, 0xCD, 0xE5, 0xD6, 0xD1, 0xD0, 0x15, 0x20, 0xF7, 0x1D, + 0x28, 0x1A, 0xCD, 0xAC, 0xD5, 0x18, 0xED, 0x1E, 0x02, 0x16, 0x04, 0xD5, 0xCD, 0x2B, 0xD7, 0xD1, + 0xD0, 0x15, 0x20, 0xF7, 0x1D, 0x28, 0x05, 0xCD, 0xAC, 0xD5, 0x18, 0xED, 0x3A, 0x21, 0x00, 0x4F, + 0x37, 0xC9, 0x97, 0x32, 0x18, 0x00, 0x32, 0x17, 0x00, 0x32, 0x1F, 0x00, 0xCD, 0xE0, 0xD7, 0xD3, + 0x34, 0xCD, 0xBA, 0xD6, 0x38, 0x0E, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0x3B, 0x1F, 0x30, + 0xF7, 0xC3, 0xCB, 0xD6, 0x3E, 0xC4, 0xD3, 0x30, 0xCD, 0x61, 0xD8, 0xE6, 0x57, 0xD3, 0x04, 0xCD, + 0x6F, 0xD8, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0x16, 0xCD, 0x5A, 0xD8, 0xDB, 0x34, 0xCB, 0x57, 0x20, + 0x18, 0x1F, 0x30, 0xF7, 0x3E, 0xD0, 0xD3, 0x30, 0x97, 0xD3, 0x31, 0xC3, 0xDD, 0xD6, 0xDB, 0x34, + 0xCB, 0x57, 0x20, 0x05, 0x1F, 0x30, 0xDB, 0x18, 0xCB, 0x3E, 0x80, 0x32, 0x21, 0x00, 0x37, 0xC9, + 0x97, 0xCD, 0xE0, 0xD7, 0xD3, 0x34, 0x3A, 0x18, 0x00, 0xD3, 0x33, 0x4F, 0x3A, 0x1A, 0x00, 0xD3, + 0x32, 0x3A, 0x1F, 0x00, 0xD3, 0x31, 0x91, 0xCA, 0x5A, 0xD8, 0xCD, 0xBA, 0xD6, 0x38, 0x0F, 0xF6, + 0x10, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xD0, 0x1F, 0x30, 0xF7, 0x18, 0x3D, 0xCD, 0x61, + 0xD8, 0xE6, 0x4F, 0xD3, 0x04, 0x3E, 0x18, 0xD3, 0x30, 0xDB, 0x34, 0xCB, 0x57, 0x20, 0xBA, 0x1F, + 0x30, 0xF7, 0xDB, 0x30, 0x2E, 0x32, 0xDB, 0x04, 0xE6, 0x40, 0x20, 0xFA, 0x2D, 0x20, 0xF7, 0xDB, + 0x04, 0xE6, 0x40, 0x20, 0xFA, 0xCD, 0x5A, 0xD8, 0x18, 0x23, 0x3A, 0x15, 0x00, 0xCB, 0x5F, 0x3E, + 0x0E, 0x28, 0x06, 0xE6, 0x04, 0x37, 0xC0, 0x3E, 0x0C, 0xA7, 0xC9, 0xCD, 0x5A, 0xD8, 0x21, 0x64, + 0x00, 0xCD, 0x7C, 0xD8, 0xDB, 0x30, 0x32, 0x21, 0x00, 0xE6, 0x98, 0x37, 0xC0, 0x3A, 0x18, 0x00, + 0x32, 0x1F, 0x00, 0xA7, 0xC9, 0xCD, 0x1C, 0xD7, 0xD3, 0x30, 0xDB, 0x34, 0x1F, 0x38, 0x16, 0xED, + 0xA2, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0E, 0xED, 0xA2, 0xC2, 0xEA, 0xD6, 0xDB, 0x34, 0xCB, 0x4F, + 0x20, 0x10, 0x1F, 0x30, 0xF7, 0xCD, 0x5A, 0xD8, 0xDB, 0x30, 0x32, 0x21, 0x00, 0xE6, 0x9C, 0xC8, + 0x18, 0x08, 0xCD, 0x5A, 0xD8, 0x3E, 0x80, 0x32, 0x21, 0x00, 0x37, 0xC9, 0xCD, 0xCB, 0xD7, 0x57, + 0xCD, 0x51, 0xD8, 0xC6, 0x88, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xC9, 0xCD, 0x6F, 0xD8, 0xCD, 0xCB, + 0xD7, 0x57, 0xCD, 0x51, 0xD8, 0xC6, 0xA8, 0x5F, 0x7A, 0xD3, 0x34, 0x7B, 0xD3, 0x30, 0xDB, 0x34, + 0x1F, 0x38, 0x12, 0xED, 0xA3, 0x04, 0xDB, 0x34, 0x1F, 0x38, 0x0A, 0xED, 0xA3, 0xC2, 0x3E, 0xD7, + 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xCD, 0x5A, 0xD8, 0xCD, 0x6F, 0xD8, 0xDB, 0x30, 0x32, 0x21, 0x00, + 0xE6, 0xFC, 0x37, 0xC0, 0xA7, 0x3A, 0x15, 0x00, 0xCB, 0x4F, 0xC8, 0xCD, 0x8B, 0xD7, 0x38, 0x0A, + 0xDB, 0x34, 0x1F, 0x38, 0x04, 0xDB, 0x33, 0x18, 0xF7, 0x1C, 0xCD, 0x5A, 0xD8, 0xDB, 0x30, 0x32, + 0x21, 0x00, 0xE6, 0x9C, 0x37, 0xC0, 0x7B, 0xA7, 0xC8, 0x37, 0xC9, 0xCD, 0x1C, 0xD7, 0xED, 0x4B, + 0x1D, 0x00, 0xCB, 0x38, 0xCB, 0x19, 0xCB, 0x38, 0xCB, 0x19, 0x41, 0x1E, 0x00, 0xD3, 0x30, 0xDB, + 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, + 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, 0xAE, 0xC0, 0x23, 0xDB, 0x34, 0x1F, 0xD8, 0xDB, 0x33, + 0xAE, 0xC0, 0x23, 0x10, 0xDA, 0xDB, 0x34, 0x1F, 0x30, 0xFB, 0xC9, 0xDB, 0x33, 0x3E, 0x80, 0xCD, + 0xE0, 0xD7, 0x2A, 0x1D, 0x00, 0xCB, 0x1C, 0xCB, 0x1D, 0x45, 0x0E, 0x33, 0x2A, 0x1B, 0x00, 0xC9, + 0x4F, 0xDB, 0x34, 0xE6, 0x04, 0x28, 0x04, 0x97, 0x32, 0x23, 0x00, 0xCD, 0x33, 0xD8, 0xD3, 0x34, + 0xF5, 0xE5, 0xCD, 0x61, 0xD8, 0xE6, 0x5F, 0xD3, 0x04, 0x3A, 0x23, 0x00, 0xA7, 0x28, 0x21, 0x21, + 0x90, 0x01, 0x3A, 0x24, 0x00, 0xB8, 0x20, 0x1B, 0x3A, 0x25, 0x00, 0x67, 0x3A, 0x17, 0x00, 0x32, + 0x25, 0x00, 0xBC, 0x20, 0x06, 0xDB, 0x34, 0xE6, 0x20, 0x20, 0x0B, 0xCD, 0x6F, 0xD8, 0x18, 0x06, + 0x21, 0x20, 0x4E, 0xCD, 0x7C, 0xD8, 0xE1, 0x78, 0x32, 0x24, 0x00, 0x3E, 0x01, 0x32, 0x23, 0x00, + 0xF1, 0xB1, 0xC9, 0x3A, 0x16, 0x00, 0x47, 0x04, 0x97, 0x37, 0x17, 0x10, 0xFD, 0x47, 0x3A, 0x15, + 0x00, 0xCB, 0x57, 0x28, 0x02, 0xCB, 0xE0, 0xCB, 0x47, 0x28, 0x02, 0xCB, 0xF0, 0x78, 0xF6, 0x20, + 0xC9, 0xDB, 0x34, 0x2F, 0xE6, 0x20, 0xC8, 0x3E, 0x04, 0xC9, 0xCD, 0x61, 0xD8, 0xD3, 0x04, 0xAF, + 0xC9, 0xC5, 0x06, 0x7F, 0x3A, 0x17, 0x00, 0xA7, 0x28, 0x02, 0x06, 0x7D, 0x78, 0xC1, 0xC9, 0x21, + 0x08, 0x00, 0x3A, 0x15, 0x00, 0xCB, 0x57, 0x20, 0x03, 0x21, 0x0C, 0x00, 0xC5, 0x2B, 0x06, 0x1C, + 0x10, 0xFE, 0x00, 0x00, 0x7D, 0xB4, 0x20, 0xF5, 0xC1, 0xC9, 0x3A, 0x2D, 0x00, 0xA7, 0x28, 0x05, + 0xFE, 0x01, 0xC8, 0x18, 0x4F, 0x3C, 0x32, 0x2D, 0x00, 0xDB, 0xF8, 0xA7, 0x28, 0x04, 0xFE, 0x01, + 0x20, 0x42, 0x21, 0x29, 0xD9, 0x11, 0x2E, 0x00, 0x01, 0x0E, 0x00, 0xED, 0xB0, 0x21, 0x2E, 0x00, + 0x7C, 0x32, 0x32, 0x00, 0x7D, 0x32, 0x34, 0x00, 0x06, 0x07, 0x0E, 0x00, 0x21, 0x2E, 0x00, 0x7E, + 0x23, 0xD3, 0xF8, 0x81, 0x4F, 0xCD, 0x10, 0xD9, 0x38, 0x1A, 0x10, 0xF3, 0x79, 0xD3, 0xF8, 0x21, + 0x2E, 0x00, 0x11, 0x2F, 0x00, 0x01, 0x2F, 0x00, 0x36, 0x00, 0xED, 0xB0, 0xDB, 0xF8, 0xA7, 0xC8, + 0xFE, 0x01, 0x20, 0xF8, 0xCD, 0xF2, 0xD4, 0x43, 0x61, 0x6E, 0x6E, 0x6F, 0x74, 0x20, 0x69, 0x6E, + 0x69, 0x74, 0x69, 0x61, 0x6C, 0x69, 0x7A, 0x65, 0x20, 0x53, 0x54, 0x44, 0x43, 0x20, 0x63, 0x6F, + 0x6E, 0x74, 0x72, 0x6F, 0x6C, 0x6C, 0x65, 0x72, 0x8D, 0x3E, 0xFF, 0x32, 0x2D, 0x00, 0x37, 0xC9, + 0xD5, 0xC5, 0x4E, 0x23, 0x06, 0x0A, 0x11, 0x00, 0x00, 0xDB, 0xF8, 0xB9, 0x28, 0x08, 0x1B, 0x7A, + 0xB3, 0x20, 0xF6, 0x10, 0xF1, 0x37, 0xC1, 0xD1, 0xC9, 0x11, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0xCD, 0x5D, 0xDA, 0x3E, 0xFF, 0x32, 0x0D, 0x00, 0x3E, + 0x12, 0xF5, 0x7E, 0xE6, 0x60, 0x77, 0xF1, 0xD3, 0xF8, 0xC5, 0xD5, 0x06, 0x1E, 0x11, 0x00, 0x00, + 0x7E, 0xCB, 0x7F, 0x20, 0x13, 0x1B, 0x7A, 0xB3, 0x20, 0xF6, 0x10, 0xF1, 0xAF, 0x32, 0x45, 0x00, + 0x3E, 0x07, 0x32, 0x44, 0x00, 0x3E, 0x01, 0x77, 0xD1, 0xC1, 0xC9, 0xCD, 0x5D, 0xDA, 0x3A, 0x16, + 0x00, 0x32, 0x2F, 0x00, 0x3E, 0x81, 0x32, 0x2E, 0x00, 0x3E, 0x19, 0x18, 0xC4, 0xCD, 0xA5, 0xD9, + 0x3E, 0xFF, 0x32, 0x0D, 0x00, 0x32, 0x2E, 0x00, 0x20, 0x11, 0xE5, 0xD5, 0xC5, 0x21, 0x4B, 0x00, + 0x11, 0x0D, 0x00, 0x01, 0x03, 0x00, 0xED, 0xB0, 0xC1, 0xD1, 0xE1, 0x3E, 0x17, 0xCD, 0x41, 0xD9, + 0xED, 0x53, 0x44, 0x00, 0xC9, 0xCD, 0x5D, 0xDA, 0xE5, 0x11, 0x0D, 0x00, 0x21, 0x47, 0x00, 0x06, + 0x03, 0x1A, 0xBE, 0x20, 0x04, 0x23, 0x13, 0x10, 0xF8, 0xE1, 0x11, 0xFF, 0xFF, 0xC8, 0x0E, 0xFF, + 0x06, 0x0A, 0x3E, 0x15, 0xCD, 0x41, 0xD9, 0xED, 0x5B, 0x44, 0x00, 0xE6, 0x02, 0xC0, 0x3E, 0xFF, + 0xBA, 0x20, 0x02, 0xBB, 0xC8, 0x4A, 0x10, 0xEA, 0xBB, 0xC0, 0x79, 0xFE, 0x05, 0x20, 0x04, 0x3E, + 0x03, 0x18, 0x1A, 0xFE, 0x04, 0x20, 0x04, 0x3E, 0x0B, 0x18, 0x12, 0xFE, 0x07, 0x38, 0x04, 0x3E, + 0x0C, 0x18, 0x0A, 0xFE, 0x06, 0x20, 0x04, 0x3E, 0x09, 0x18, 0x02, 0x3E, 0x07, 0x32, 0x44, 0x00, + 0x5F, 0x51, 0xA7, 0xC9, 0xCD, 0xA5, 0xD9, 0xC0, 0x3E, 0x80, 0x32, 0x2E, 0x00, 0x3E, 0x17, 0xCD, + 0x41, 0xD9, 0x3E, 0x16, 0xC3, 0x41, 0xD9, 0xCD, 0x5D, 0xDA, 0x3E, 0xFF, 0x32, 0x0D, 0x00, 0x3E, + 0x13, 0xCD, 0x41, 0xD9, 0xED, 0x5B, 0x44, 0x00, 0x3E, 0xFF, 0xBB, 0xC0, 0xBA, 0x20, 0x06, 0x06, + 0x0A, 0xCD, 0xC2, 0xD9, 0xC8, 0x3E, 0x00, 0x32, 0x44, 0x00, 0xC9, 0xCD, 0x5D, 0xDA, 0x3E, 0x14, + 0xCD, 0x41, 0xD9, 0x3A, 0x44, 0x00, 0xFE, 0xFF, 0xC0, 0x32, 0x2E, 0x00, 0x3E, 0x1B, 0xC3, 0x41, + 0xD9, 0x3A, 0x44, 0x00, 0xFE, 0xFF, 0xC9, 0x3A, 0x45, 0x00, 0xFE, 0xFF, 0xC9, 0x21, 0x2E, 0x00, + 0x11, 0x2F, 0x00, 0x01, 0x2F, 0x00, 0x36, 0x00, 0xED, 0xB0, 0xDD, 0xE5, 0xDD, 0x21, 0x2E, 0x00, + 0x3A, 0x16, 0x00, 0xF6, 0x1F, 0xDD, 0x77, 0x01, 0x3A, 0x17, 0x00, 0xDD, 0x77, 0x19, 0x2A, 0x18, + 0x00, 0xDD, 0x74, 0x1A, 0xDD, 0x75, 0x1B, 0x3A, 0x1A, 0x00, 0xDD, 0x77, 0x18, 0x2A, 0x1B, 0x00, + 0xDD, 0x74, 0x0A, 0xDD, 0x75, 0x0B, 0x2A, 0x1D, 0x00, 0xDD, 0x74, 0x06, 0xDD, 0x75, 0x07, 0x3A, + 0x10, 0x00, 0xE6, 0x60, 0xDD, 0x77, 0x15, 0xDD, 0xE1, 0x21, 0x43, 0x00, 0xC9, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +} +}; + + + +/* returns TRUE iff there exists a disk with VERBOSE */ +static int32 cromfdc_hasProperty(uint32 property) { + int32 i; + for (i = 0; i < CROMFDC_MAX_DRIVES; i++) + if (cromfdc_dev.units[i].flags & property) return TRUE; + return FALSE; +} + +static uint8 motor_timeout = 0; + +/* Unit service routine */ +t_stat cromfdc_svc (UNIT *uptr) +{ + + if(cromfdc_info->motor_on == 1) { + motor_timeout ++; + if(motor_timeout == MOTOR_TO_LIMIT) { + cromfdc_info->motor_on = 0; + TRACE_PRINT(DRIVE_MSG, ("CROMFDC: Motor OFF" NLP)) + } + } + + cromfdc_info->rtc ++; + + sim_activate (cromfdc_unit, cromfdc_unit->wait); /* requeue! */ + + return SCPE_OK; +} + + +/* Reset routine */ +static t_stat cromfdc_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ + if (cromfdc_hasProperty(UNIT_CROMFDC_ROM)) { + sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &cromfdcrom, TRUE); + } + /* Unmap I/O Ports (0x3-4,0x5-9,0x34,0x40 */ + sim_map_resource(0x03, 2, RESOURCE_TYPE_IO, &cromfdc_ext, TRUE); + sim_map_resource(0x05, 5, RESOURCE_TYPE_IO, &cromfdc_timer, TRUE); + sim_map_resource(0x34, 1, RESOURCE_TYPE_IO, &cromfdc_control, TRUE); +/* sim_map_resource(0x40, 1, RESOURCE_TYPE_IO, &cromfdc_banksel, TRUE);*/ + } else { + /* Connect CROMFDC ROM at base address */ + if (cromfdc_hasProperty(UNIT_CROMFDC_ROM)) { + TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: ROM Enabled." NLP)) + if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &cromfdcrom, FALSE) != 0) { + printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } else { + TRACE_PRINT(VERBOSE_MSG, ("CROMFDC: ROM Disabled." NLP)) + } + /* Connect CROMFDC Interrupt, and Aux Disk Registers */ + if(sim_map_resource(0x03, 0x02, RESOURCE_TYPE_IO, &cromfdc_ext, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + + /* Connect CROMFDC Timer Registers */ + if(sim_map_resource(0x05, 0x05, RESOURCE_TYPE_IO, &cromfdc_timer, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + + /* Connect CROMFDC Disk Flags and Control Register */ + if(sim_map_resource(0x34, 0x01, RESOURCE_TYPE_IO, &cromfdc_control, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + + /* Connect CROMFDC Bank Select Register */ +/* if(sim_map_resource(0x40, 0x1, RESOURCE_TYPE_IO, &cromfdc_banksel, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } +*/ + } + + cromfdc_info->rom_disabled = FALSE; + sim_activate (cromfdc_unit, cromfdc_unit->wait); /* requeue! */ + return SCPE_OK; +} + +static t_stat cromfdc_boot(int32 unitno, DEVICE *dptr) +{ + if((crofdc_type != 4) && (crofdc_type != 16) && (crofdc_type != 64)) { + printf("Invalid fdc_type: %d, must be 4, 16, or 64." NLP, crofdc_type); + return SCPE_ARG; + } + + DBG_PRINT(("Booting %dFDC Controller, bootstrap=%d" NLP, crofdc_type, bootstrap)); + + /* Re-enable the ROM in case it was disabled */ + cromfdc_info->rom_disabled = FALSE; + + /* Set the PC to 0, and go. */ + *((int32 *) sim_PC->loc) = 0xC000; + return SCPE_OK; +} + +/* Attach routine */ +static t_stat cromfdc_attach(UNIT *uptr, char *cptr) +{ + t_stat r; + r = wd179x_attach(uptr, cptr); + + return r; +} + +/* Detach routine */ +static t_stat cromfdc_detach(UNIT *uptr) +{ + t_stat r; + + r = wd179x_detach(uptr); + + return r; +} + +static int32 cromfdcrom(const int32 Addr, const int32 write, const int32 data) +{ +/* DBG_PRINT(("CROMFDC: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ + if(write) { + cromfdcram[Addr & CROMFDC_ADDR_MASK] = data; + return 0; + } else { + if(cromfdc_info->rom_disabled == FALSE) { + return(cromfdc_rom[bootstrap][Addr & CROMFDC_ADDR_MASK]); + } else { + return(cromfdcram[Addr & CROMFDC_ADDR_MASK]); + } + } +} + +/* Disk Control/Flags Register, 0x34 */ +static int32 cromfdc_control(const int32 port, const int32 io, const int32 data) +{ + int32 result = 0; + if(io) { /* I/O Write */ + switch(data & 0x0F) { + case 0: + wd179x_info->sel_drive = 0xFF; + break; + case CROMFDC_CTRL_DS1: + wd179x_info->sel_drive = 0; + break; + case CROMFDC_CTRL_DS2: + wd179x_info->sel_drive = 1; + break; + case CROMFDC_CTRL_DS3: + wd179x_info->sel_drive = 2; + break; + case CROMFDC_CTRL_DS4: + wd179x_info->sel_drive = 3; + break; + default: + TRACE_PRINT(STATUS_MSG, + ("CROMFDC: " ADDRESS_FORMAT " WR CTRL = 0x%02x: Invalid drive selected." NLP, PCX, data & 0xFF)); + break; + } + if(data & CROMFDC_CTRL_MAXI) { + wd179x_info->drivetype = 8; + } else { + wd179x_info->drivetype = 5; + } + + if(data & CROMFDC_CTRL_MTRON) { + cromfdc_info->motor_on = 1; + motor_timeout = 0; + } + + if(data & CROMFDC_CTRL_DDENS) { + if(crofdc_type == 4) { /* 4FDC */ + TRACE_PRINT(DRIVE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " WR CTRL: Cannot set double density on 4FDC" NLP, PCX)); + } else { + wd179x_info->ddens = 1; + } + } else { + wd179x_info->ddens = 0; + } + if(data & CROMFDC_CTRL_AUTOWAIT) { + cromfdc_info->autowait = 1; + } else { + cromfdc_info->autowait = 0; + } + + TRACE_PRINT(DRIVE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " WR CTRL: sel_drive=%d, drivetype=%d, motor=%d, dens=%d, aw=%d" NLP, PCX, + wd179x_info->sel_drive, wd179x_info->drivetype, cromfdc_info->motor_on, wd179x_info->ddens, cromfdc_info->autowait)); + } else { /* I/O Read */ + result = (crofdc_boot) ? 0 : CROMFDC_FLAG_BOOT; + result |= (wd179x_info->intrq) ? CROMFDC_FLAG_EOJ : 0; + result |= (wd179x_info->drq) ? CROMFDC_FLAG_DRQ : 0; + result |= (motor_timeout < MOTOR_TO_LIMIT) ? CROMFDC_FLAG_SEL_REQ : 0; + if(crofdc_type > 4) { /* 16, 64FDC */ + result |= (cromfdc_info->motor_on) ? CROMFDC_FLAG_MTRON : 0; + result |= (motor_timeout == MOTOR_TO_LIMIT) ? CROMFDC_FLAG_MTO : 0; + result |= (crofdc_inh_init) ? 0 : CROMFDC_FLAG_INH_INIT; + } + TRACE_PRINT(VERBOSE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " Read DISK FLAGS, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) + } + + return result; +} + +/* 64FDC Interrupt and Auxiliary Disk Status */ +/* 0x03-04 */ +static int32 cromfdc_ext(const int32 port, const int32 io, const int32 data) +{ + int32 result; + if(io) { /* I/O Write */ + if(port == 0x4) { + if((data & CROMFDC_AUX_CMD_SIDE) == 0) { + if(crofdc_type == 4) { /* 4FDC */ + TRACE_PRINT(DRIVE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " WR CTRL: Cannot set side 1 on 4FDC" NLP, PCX)); + } else { + wd179x_info->fdc_head = 1; + } + } else { + wd179x_info->fdc_head = 0; + } +#if 0 /* hharte - nothing implemented for these */ + if((data & CROMFDC_AUX_EJECT) == 0) { + printf("CROMFDC: Eject\n"); + } + if((data & CROMFDC_AUX_SEL_OVERRIDE) == 0) { + printf("CROMFDC: Sel Override\n"); + } + if((data & CROMFDC_AUX_CTRL_OUT) == 0) { + printf("CROMFDC: Ctrl Out\n"); + } +#endif /* 0 */ + if((data & CROMFDC_AUX_RESTORE) == 0) { + wd179x_external_restore(); + } + } else if (port == 0x3) { /* Interrupt Address */ + } else { + } + + TRACE_PRINT(DRIVE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " AUX OUT, Port 0x%02x Data 0x%02x" NLP, PCX, port, data)) + result = 0; + } else { /* I/O Read */ + if(port == 0x4) { + result = dipswitch & 0x1F; + result |= 0x00; /* Bit 6 is Seek in Progress for Persci drives. */ + result |= (cromfdc_info->rtc & 1) ? 0x80 : 0; + } else if (port == 0x3) { /* Interrupt Address */ + result = 0xD7; /* end of job */ + } else { + result = 0xFF; + } + TRACE_PRINT(VERBOSE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " AUX IN, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) + } + return result; +} + +/* 64FDC Timer registers */ +static int32 cromfdc_timer(const int32 port, const int32 io, const int32 data) +{ + static int32 result = 0; + if(io) { + TRACE_PRINT(VERBOSE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " TIMER OUT, Port 0x%02x Data 0x%02x" NLP, PCX, port, data)) + result = 0; + } else { + result++; + TRACE_PRINT(VERBOSE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " TIMER IN, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) + } + return result; +} + +/* 64FDC Bank Select (Write Disables boot ROM) */ +static int32 cromfdc_banksel(const int32 port, const int32 io, const int32 data) +{ + int32 result; + if(io) { + TRACE_PRINT(VERBOSE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " BANKSEL OUT, Port 0x%02x Data 0x%02x" NLP, PCX, port, data)) + /* Unmap Boot ROM */ + cromfdc_info->rom_disabled = TRUE; + result = 0; + } else { + result = 0xFF; + TRACE_PRINT(VERBOSE_MSG, + ("CROMFDC: " ADDRESS_FORMAT " BANKSEL IN, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) + } + return result; +} diff --git a/AltairZ80/s100_disk1a.c b/AltairZ80/s100_disk1a.c new file mode 100644 index 0000000..3b607ef --- /dev/null +++ b/AltairZ80/s100_disk1a.c @@ -0,0 +1,853 @@ +/************************************************************************* + * * + * $Id: s100_disk1a.c 1771 2008-01-09 07:10:46Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * CompuPro DISK1A Floppy Controller module for SIMH. * + * This module is a wrapper around the i8272 FDC module, and adds the * + * CompuPro-specific registers as well as the CompuPro Boot ROM. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#include "sim_defs.h" /* simulator definitions */ +#include "i8272.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 +#define VERBOSE_MSG 0x80 + +#define DISK1A_MAX_DRIVES 4 + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint32 dma_addr; /* DMA Transfer Address */ + uint8 rom_disabled; /* TRUE if ROM has been disabled */ +} DISK1A_INFO; + +static DISK1A_INFO disk1a_info_data = { { 0x0, 512, 0xC0, 4 } }; +static DISK1A_INFO *disk1a_info = &disk1a_info_data; + +extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +extern REG *sim_PC; +extern uint32 PCX; /* external view of PC */ + +#define UNIT_V_DISK1A_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_DISK1A_WLK (1 << UNIT_V_DISK1A_WLK) +#define UNIT_V_DISK1A_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_DISK1A_VERBOSE (1 << UNIT_V_DISK1A_VERBOSE) +#define UNIT_V_DISK1A_ROM (UNIT_V_UF + 2) /* boot ROM enabled */ +#define UNIT_DISK1A_ROM (1 << UNIT_V_DISK1A_ROM) +#define DISK1A_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ + +static t_stat disk1a_reset(DEVICE *disk1a_dev); +static t_stat disk1a_boot(int32 unitno, DEVICE *dptr); +static t_stat disk1a_attach(UNIT *uptr, char *cptr); +static t_stat disk1a_detach(UNIT *uptr); + +static int32 disk1adev(const int32 port, const int32 io, const int32 data); +static int32 disk1arom(const int32 port, const int32 io, const int32 data); + +static uint8 DISK1A_Read(const uint32 Addr); +static uint8 DISK1A_Write(const uint32 Addr, uint8 cData); + +static int32 trace_level = 0; /* Disable all tracing by default */ +static int32 bootstrap = 0; + +/* The DISK1A does not really have RAM associated with it, but for ease of integration with the + * SIMH/AltairZ80 Resource Mapping Scheme, rather than Map and Unmap the ROM, simply implement our + * own RAM that can be swapped in when the DISK1A Boot ROM is disabled. + */ +static uint8 disk1aram[512]; + +static UNIT disk1a_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK1A_CAPACITY) } +}; + +static REG disk1a_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { DRDATA (BOOTSTRAP, bootstrap, 10), }, + { NULL } +}; + +static MTAB disk1a_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_DISK1A_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_DISK1A_WLK, UNIT_DISK1A_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_DISK1A_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_DISK1A_VERBOSE, UNIT_DISK1A_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { UNIT_DISK1A_ROM, 0, "NOROM", "NOROM", NULL }, + { UNIT_DISK1A_ROM, UNIT_DISK1A_ROM, "ROM", "ROM", NULL }, + { 0 } +}; + +DEVICE disk1a_dev = { + "DISK1A", disk1a_unit, disk1a_reg, disk1a_mod, + DISK1A_MAX_DRIVES, 10, 31, 1, DISK1A_MAX_DRIVES, DISK1A_MAX_DRIVES, + NULL, NULL, &disk1a_reset, + &disk1a_boot, &disk1a_attach, &disk1a_detach, + &disk1a_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* This is the DISK1A Boot ROM. + * It consists of an 8K ROM, broken down into 16 bootstraps of + * 512-bytes each. See the DISK1A Manual for details of each + * bootstrap. Bootstrap 0 is the default, but others can be + * selected with 'd disk1a bootstrap ' at the SIMH SCP Prompt. + */ +static uint8 disk1a_rom[16][512] = { +{ 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x3E, 0xFF, 0xD3, 0xC3, 0x01, 0x00, 0x40, 0xE3, 0xE3, /* 0 */ + 0x0B, 0x78, 0xB1, 0xC2, 0x0E, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, + 0x7D, 0xB4, 0xC2, 0x1C, 0x00, 0x11, 0x5D, 0x00, 0x3E, 0x85, 0x12, 0x13, 0x3E, 0x81, 0x12, 0x13, + 0x3E, 0x00, 0x12, 0x11, 0x50, 0x00, 0x12, 0x13, 0x13, 0x12, 0xC3, 0x49, 0x80, 0x01, 0x00, 0xC0, + 0xE3, 0xE3, 0x0B, 0x78, 0xB1, 0xC2, 0x40, 0x80, 0xC9, 0x3E, 0xFE, 0xD3, 0xC3, 0x11, 0x70, 0x81, + 0x06, 0x03, 0xDB, 0xC0, 0xB7, 0xF2, 0x52, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x52, 0x80, + 0x06, 0x02, 0xDB, 0xC0, 0xB7, 0xF2, 0x62, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x62, 0x80, + 0xDB, 0xC2, 0xB7, 0xF2, 0x70, 0x80, 0x3E, 0x08, 0xD3, 0xC1, 0xCD, 0x3D, 0x80, 0xDB, 0xC0, 0xB7, + 0xF2, 0x7D, 0x80, 0xDB, 0xC1, 0xEE, 0x20, 0x4F, 0xDB, 0xC0, 0xB7, 0xF2, 0x88, 0x80, 0xDB, 0xC1, + 0xB1, 0xC2, 0xDD, 0x80, 0x06, 0x03, 0x1A, 0xD3, 0xC2, 0x13, 0x05, 0xC2, 0x96, 0x80, 0x06, 0x09, + 0xDB, 0xC0, 0xB7, 0xF2, 0xA0, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0xA0, 0x80, 0xDB, 0xC2, + 0xB7, 0xF2, 0xAE, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0xB4, 0x80, 0xDB, 0xC1, 0xD6, 0x40, 0x67, 0xDB, + 0xC0, 0xB7, 0xF2, 0xBF, 0x80, 0xDB, 0xC1, 0xEE, 0x80, 0x6F, 0x06, 0x05, 0xDB, 0xC0, 0xB7, 0xF2, + 0xCC, 0x80, 0xDB, 0xC1, 0x05, 0xC2, 0xCC, 0x80, 0x7D, 0xB4, 0xCA, 0x53, 0x81, 0x3E, 0x01, 0xD3, + 0x90, 0xAF, 0x32, 0x86, 0x81, 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x13, + 0x81, 0xAF, 0x32, 0x86, 0x81, 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x13, + 0x81, 0xC3, 0x49, 0x80, 0xAF, 0x12, 0xD3, 0x90, 0x1A, 0xB7, 0xCA, 0x08, 0x81, 0x07, 0xF8, 0xC1, + 0xC3, 0x49, 0x80, 0x11, 0x96, 0x81, 0xCD, 0x04, 0x81, 0x11, 0xA6, 0x81, 0xCD, 0x04, 0x81, 0x11, + 0xB6, 0x81, 0x2A, 0xBF, 0x81, 0xCD, 0x04, 0x81, 0x11, 0x81, 0x81, 0x06, 0x04, 0x1A, 0xBE, 0xC2, + 0x49, 0x80, 0x23, 0x13, 0x05, 0xC2, 0x2D, 0x81, 0x11, 0xC6, 0x81, 0xCD, 0x04, 0x81, 0x11, 0xD6, + 0x81, 0xCD, 0x04, 0x81, 0x11, 0xE6, 0x81, 0x2A, 0xEF, 0x81, 0xCD, 0x04, 0x81, 0x7E, 0xFE, 0xE5, + 0xCA, 0x49, 0x80, 0xDB, 0xC2, 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, 0xC3, 0x00, 0x01, 0x52, + 0x52, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x8F, 0x24, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, + 0x80, 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x95, 0x81, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xA5, 0x81, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xB5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0xC5, 0x81, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x01, 0x00, 0xD5, 0x81, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0xE5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x50, 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A }, + +{ 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x01, 0x00, 0x40, 0xE3, 0xE3, 0x0B, 0x78, 0xB1, 0xC2, /* 1 */ + 0x0A, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, 0x7D, 0xB4, 0xC2, 0x18, + 0x00, 0x11, 0x5D, 0x00, 0x3E, 0xD2, 0x12, 0x13, 0x3E, 0x80, 0x12, 0x13, 0x3E, 0x00, 0x12, 0x11, + 0x50, 0x00, 0x12, 0x13, 0x13, 0x12, 0xC3, 0x39, 0x80, 0x3E, 0xFE, 0xD3, 0xC3, 0x3E, 0x01, 0xD3, + 0x90, 0xAF, 0x32, 0xD3, 0x80, 0xD3, 0x90, 0x01, 0x00, 0x14, 0x0B, 0x78, 0xB1, 0xC2, 0x4A, 0x80, + 0x3A, 0xD3, 0x80, 0xB7, 0xFA, 0x7F, 0x80, 0xAF, 0x32, 0xD3, 0x80, 0xD3, 0x90, 0x01, 0x00, 0x14, + 0x0B, 0x78, 0xB1, 0xC2, 0x60, 0x80, 0x3A, 0xD3, 0x80, 0xB7, 0xFA, 0x7F, 0x80, 0xC3, 0x39, 0x80, + 0xAF, 0x12, 0xD3, 0x90, 0x1A, 0xB7, 0xCA, 0x74, 0x80, 0x07, 0xF8, 0xC1, 0xC3, 0x39, 0x80, 0x11, + 0xE3, 0x80, 0xCD, 0x70, 0x80, 0x11, 0xF3, 0x80, 0xCD, 0x70, 0x80, 0x11, 0x03, 0x81, 0x2A, 0x0C, + 0x81, 0xCD, 0x70, 0x80, 0x11, 0xCE, 0x80, 0x06, 0x04, 0x1A, 0xBE, 0xC2, 0x39, 0x80, 0x23, 0x13, + 0x05, 0xC2, 0x99, 0x80, 0x11, 0x13, 0x81, 0xCD, 0x70, 0x80, 0x11, 0x23, 0x81, 0xCD, 0x70, 0x80, + 0x11, 0x33, 0x81, 0x2A, 0x3C, 0x81, 0xCD, 0x70, 0x80, 0x7E, 0xFE, 0xE5, 0xCA, 0x39, 0x80, 0xDB, + 0xC2, 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, 0xC3, 0x00, 0x01, 0x52, 0x52, 0x32, 0x43, 0x6F, + 0x6D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE2, + 0x80, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF2, + 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x12, + 0x81, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x01, 0x00, 0x22, + 0x81, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x32, + 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x00, 0x50, + 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x3E, 0xFF, 0xD3, 0xC3, 0x01, 0x00, 0x40, 0xE3, 0xE3, /* 2 */ + 0x0B, 0x78, 0xB1, 0xC2, 0x0E, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, + 0x7D, 0xB4, 0xC2, 0x1C, 0x00, 0xC3, 0x34, 0x80, 0x01, 0x00, 0xC0, 0xE3, 0xE3, 0x0B, 0x78, 0xB1, + 0xC2, 0x2B, 0x80, 0xC9, 0x11, 0x00, 0x81, 0x3E, 0xFE, 0xD3, 0xC3, 0x1A, 0xD3, 0xC0, 0xCD, 0x28, + 0x80, 0x13, 0x06, 0x03, 0xDB, 0xC0, 0xB7, 0xF2, 0x44, 0x80, 0x1A, 0xD3, 0xC1, 0x13, 0x05, 0xC2, + 0x44, 0x80, 0xCD, 0x28, 0x80, 0x06, 0x02, 0xDB, 0xC0, 0xB7, 0xF2, 0x57, 0x80, 0x1A, 0xD3, 0xC1, + 0x13, 0x05, 0xC2, 0x57, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0x65, 0x80, 0x3E, 0x08, 0xD3, 0xC1, 0xCD, + 0x28, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0x72, 0x80, 0xDB, 0xC1, 0xEE, 0x20, 0x4F, 0xDB, 0xC0, 0xB7, + 0xF2, 0x7D, 0x80, 0xDB, 0xC1, 0xB1, 0xE6, 0xFC, 0xC2, 0xD6, 0x80, 0x06, 0x03, 0x1A, 0xD3, 0xC2, + 0x13, 0x05, 0xC2, 0x8D, 0x80, 0x06, 0x09, 0xDB, 0xC0, 0xB7, 0xF2, 0x97, 0x80, 0x1A, 0xD3, 0xC1, + 0x13, 0x05, 0xC2, 0x97, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0xA5, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0xAB, + 0x80, 0xDB, 0xC1, 0xD6, 0x40, 0xE6, 0xFC, 0x67, 0xDB, 0xC0, 0xB7, 0xF2, 0xB8, 0x80, 0xDB, 0xC1, + 0xEE, 0x80, 0x6F, 0x06, 0x05, 0xDB, 0xC0, 0xB7, 0xF2, 0xC5, 0x80, 0xDB, 0xC1, 0x05, 0xC2, 0xC5, + 0x80, 0x7D, 0xB4, 0xCA, 0xE7, 0x80, 0x7B, 0xFE, 0x18, 0xCA, 0x34, 0x80, 0xFE, 0x24, 0xCA, 0x34, + 0x80, 0x11, 0x12, 0x81, 0xC3, 0x37, 0x80, 0xDB, 0xC2, 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, + 0xC3, 0x00, 0x01, 0x52, 0x52, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x8F, 0x24, 0x07, 0x00, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, + 0x07, 0x80, 0x28, 0x03, 0xDF, 0x1E, 0x07, 0x02, 0x00, 0x01, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, + 0x03, 0x05, 0x35, 0xFF, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, + 0x72, 0x79, 0x2E, 0x20, 0x20, 0x43, 0x6F, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, + 0x39, 0x38, 0x33, 0x20, 0x62, 0x79, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, 0x52, + 0x65, 0x73, 0x65, 0x61, 0x72, 0x63, 0x68, 0x2E, 0x00, 0x24, 0x4C, 0x53, 0x54, 0x3A, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x00, 0xC4, 0x09, 0x00, 0x00, 0xFF, 0xFF, 0xC4, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x3C, 0x3E, 0x2E, 0x2C, 0x3D, 0x3A, 0x7C, 0x5B, 0x5D, 0x2A, 0x0A, 0x0D, 0x00, 0x00, 0x00, 0x00, + 0x63, 0x68, 0x20, 0x73, 0x75, 0x70, 0x70, 0x6F, 0x72, 0x74, 0x73, 0x20, 0x6E, 0x61, 0x6D, 0x65, + 0x20, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x0A, 0x00, 0x43, 0x6F, 0x75, 0x6C, 0x64, 0x20, + 0x6E, 0x6F, 0x74, 0x20, 0x6C, 0x6F, 0x67, 0x6F, 0x6E, 0x20, 0x74, 0x6F, 0x20, 0x74, 0x68, 0x65 }, + +{ 0x00, 0x00, 0x00, 0x00, 0x31, 0x00, 0x80, 0x3E, 0xFF, 0xD3, 0xC3, 0x01, 0x00, 0x40, 0xE3, 0xE3, /* 3 */ + 0x0B, 0x78, 0xB1, 0xC2, 0x0E, 0x00, 0x21, 0x00, 0x02, 0x11, 0x00, 0x82, 0x7E, 0x12, 0x1B, 0x2B, + 0x7D, 0xB4, 0xC2, 0x1C, 0x00, 0x11, 0x5D, 0x00, 0x3E, 0x85, 0x12, 0x13, 0x3E, 0x81, 0x12, 0x13, + 0x3E, 0x00, 0x12, 0x11, 0x50, 0x00, 0x12, 0x13, 0x13, 0x12, 0xC3, 0x49, 0x80, 0x01, 0x00, 0x80, + 0xE3, 0xE3, 0x0B, 0x78, 0xB1, 0xC2, 0x40, 0x80, 0xC9, 0x3E, 0xFE, 0xD3, 0xC3, 0x3E, 0x28, 0xD3, + 0xC0, 0xCD, 0x3D, 0x80, 0x11, 0x70, 0x81, 0x06, 0x03, 0xDB, 0xC0, 0xB7, 0xF2, 0x59, 0x80, 0x1A, + 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x59, 0x80, 0x06, 0x02, 0xDB, 0xC0, 0xB7, 0xF2, 0x69, 0x80, 0x1A, + 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0x69, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0x77, 0x80, 0x3E, 0x08, 0xD3, + 0xC1, 0xCD, 0x3D, 0x80, 0xDB, 0xC0, 0xB7, 0xF2, 0x84, 0x80, 0xDB, 0xC1, 0xEE, 0x20, 0x4F, 0xDB, + 0xC0, 0xB7, 0xF2, 0x8F, 0x80, 0xDB, 0xC1, 0xB1, 0xE6, 0xFC, 0xC2, 0xE8, 0x80, 0x06, 0x03, 0x1A, + 0xD3, 0xC2, 0x13, 0x05, 0xC2, 0x9F, 0x80, 0x06, 0x09, 0xDB, 0xC0, 0xB7, 0xF2, 0xA9, 0x80, 0x1A, + 0xD3, 0xC1, 0x13, 0x05, 0xC2, 0xA9, 0x80, 0xDB, 0xC2, 0xB7, 0xF2, 0xB7, 0x80, 0xDB, 0xC0, 0xB7, + 0xF2, 0xBD, 0x80, 0xDB, 0xC1, 0xD6, 0x40, 0xE6, 0xFC, 0x67, 0xDB, 0xC0, 0xB7, 0xF2, 0xCA, 0x80, + 0xDB, 0xC1, 0xEE, 0x80, 0x6F, 0x06, 0x05, 0xDB, 0xC0, 0xB7, 0xF2, 0xD7, 0x80, 0xDB, 0xC1, 0x05, + 0xC2, 0xD7, 0x80, 0x7D, 0xB4, 0xCA, 0x5E, 0x81, 0x3E, 0x01, 0xD3, 0x90, 0xAF, 0x32, 0x86, 0x81, + 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x1E, 0x81, 0xAF, 0x32, 0x86, 0x81, + 0xD3, 0x90, 0xCD, 0x3D, 0x80, 0x3A, 0x86, 0x81, 0xB7, 0xFA, 0x1E, 0x81, 0xC3, 0x49, 0x80, 0xAF, + 0x12, 0xD3, 0x90, 0x1A, 0xB7, 0xCA, 0x13, 0x81, 0x07, 0xF8, 0xC1, 0xC3, 0x49, 0x80, 0x11, 0x96, + 0x81, 0xCD, 0x0F, 0x81, 0x11, 0xA6, 0x81, 0xCD, 0x0F, 0x81, 0x11, 0xB6, 0x81, 0x2A, 0xBF, 0x81, + 0xCD, 0x0F, 0x81, 0x11, 0x81, 0x81, 0x06, 0x04, 0x1A, 0xBE, 0xC2, 0x49, 0x80, 0x23, 0x13, 0x05, + 0xC2, 0x38, 0x81, 0x11, 0xC6, 0x81, 0xCD, 0x0F, 0x81, 0x11, 0xD6, 0x81, 0xCD, 0x0F, 0x81, 0x11, + 0xE6, 0x81, 0x2A, 0xEF, 0x81, 0xCD, 0x0F, 0x81, 0x7E, 0xFE, 0xE5, 0xCA, 0x49, 0x80, 0xDB, 0xC2, + 0x0F, 0x0F, 0xE6, 0x01, 0xF6, 0x02, 0x4F, 0xC3, 0x00, 0x01, 0x52, 0x52, 0x34, 0x00, 0x00, 0x00, + 0x03, 0xDF, 0x1E, 0x07, 0x02, 0x00, 0x01, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, + 0xFF, 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x95, 0x81, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xA5, 0x81, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xB5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, + 0x01, 0x00, 0xC5, 0x81, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x01, 0x00, 0xD5, 0x81, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0xE5, 0x81, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, + 0x01, 0x00, 0x50, 0x00, 0x00, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A, 0x1A }, + +{ 0xDB, 0xFD, 0x90, 0x90, 0xB0, 0xFF, 0xE6, 0xC3, 0xB9, 0x00, 0xD0, 0x50, 0x58, 0xE2, 0xFC, 0x33, /* 4 */ + 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x04, 0x00, 0xBA, 0x90, 0x00, 0xBE, 0x00, 0x00, + 0x8B, 0xFE, 0xB9, 0x00, 0x01, 0xF3, 0xA5, 0xEB, 0x0F, 0xFC, 0x33, 0xC0, 0xE4, 0xC0, 0x0A, 0xC0, + 0x79, 0xFA, 0xAC, 0xE6, 0xC1, 0xE2, 0xF5, 0xC3, 0xB0, 0xFE, 0xE6, 0xC3, 0xB9, 0x03, 0x00, 0xBE, + 0xC6, 0x00, 0xE8, 0xE4, 0xFF, 0xB9, 0x02, 0x00, 0xE8, 0xDE, 0xFF, 0xEB, 0x13, 0x52, 0x52, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, + 0xE4, 0xC2, 0x0A, 0xC0, 0x79, 0xFA, 0xB0, 0x08, 0xE6, 0xC1, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, + 0xFC, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x20, 0x8A, 0xE0, 0xE4, 0xC0, 0x0A, + 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x0A, 0xC4, 0x74, 0x03, 0xE9, 0x84, 0x00, 0xB9, 0x03, 0x00, 0xAC, + 0xE6, 0xC2, 0xE2, 0xFB, 0xB9, 0x09, 0x00, 0xE8, 0x8F, 0xFF, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, + 0xE4, 0xC1, 0x2C, 0x40, 0x8A, 0xD8, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x80, + 0x8A, 0xF8, 0xB9, 0x05, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0xE2, 0xF6, 0x0A, + 0xFB, 0x74, 0x24, 0xE9, 0x4A, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x00, 0x02, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0x00, 0x02, 0xAC, + 0x3C, 0xE5, 0x75, 0x03, 0xE9, 0x51, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x00, 0x01, 0xB9, 0x00, 0x12, + 0xF3, 0xA5, 0x33, 0xC0, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, + 0x43, 0x6F, 0x6D, 0x70, 0x33, 0xC0, 0xEE, 0x0A, 0x44, 0x01, 0x74, 0xFB, 0x79, 0x01, 0xC3, 0x58, + 0xBE, 0x50, 0x00, 0xB0, 0x01, 0xEE, 0xB0, 0x00, 0x88, 0x44, 0x01, 0xEE, 0xB9, 0x00, 0xF0, 0x50, + 0x58, 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x13, 0x88, 0x44, 0x01, 0xEF, 0xB9, 0x00, 0xF0, 0x50, + 0x58, 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x03, 0xE9, 0xFD, 0xFE, 0xC7, 0x04, 0x05, 0x00, 0xE8, + 0xC2, 0xFF, 0xC7, 0x44, 0x04, 0x08, 0x01, 0xC7, 0x04, 0x02, 0x00, 0xE8, 0xB6, 0xFF, 0xC7, 0x44, + 0x04, 0x00, 0x00, 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x02, 0x00, 0xC6, 0x44, 0x03, + 0x01, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x9C, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x00, 0x01, 0xB9, 0x04, + 0x00, 0xF3, 0xA6, 0x75, 0xC3, 0xBE, 0x50, 0x00, 0xC7, 0x44, 0x0A, 0x10, 0x02, 0xC7, 0x04, 0x03, + 0x00, 0xE8, 0x80, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x06, 0xC7, 0x04, 0x04, 0x00, 0xE8, 0x74, 0xFF, + 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x09, 0x00, 0xC6, 0x44, 0x03, 0x01, 0x33, 0xC0, + 0x89, 0x44, 0x04, 0x89, 0x44, 0x06, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x57, 0xFF, 0xE9, 0x2C, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0x04, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, + +{ 0xDB, 0xFD, 0x33, 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x1D, 0x00, 0xBA, 0x90, 0x00, + 0xBE, 0x00, 0x00, 0x8B, 0xFE, 0xFC, 0xB9, 0x00, 0x01, 0xF3, 0xA5, 0xEB, 0x18, 0x43, 0x6F, 0x6D, + 0x70, 0xB9, 0x00, 0xD0, 0x50, 0x58, 0xE2, 0xFC, 0xC3, 0x33, 0xC0, 0xEE, 0x0A, 0x44, 0x01, 0x74, + 0xFB, 0x79, 0x01, 0xC3, 0x58, 0xB0, 0xFE, 0xE6, 0xC3, 0xBE, 0x50, 0x00, 0xB0, 0x01, 0xEE, 0xEB, + 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, + 0xB0, 0x00, 0x88, 0x44, 0x01, 0xEE, 0xE8, 0xB8, 0xFF, 0x0A, 0x44, 0x01, 0x75, 0x0C, 0x88, 0x44, + 0x01, 0xEE, 0xE8, 0xAC, 0xFF, 0x0A, 0x44, 0x01, 0x74, 0xBB, 0xC7, 0x04, 0x05, 0x00, 0xE8, 0xA8, + 0xFF, 0xC7, 0x44, 0x04, 0x08, 0x01, 0xC7, 0x04, 0x02, 0x00, 0xE8, 0x9C, 0xFF, 0xC7, 0x44, 0x04, + 0x00, 0x00, 0xC7, 0x44, 0x0A, 0x00, 0x01, 0xC7, 0x44, 0x08, 0x02, 0x00, 0xC6, 0x44, 0x03, 0x01, + 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x82, 0xFF, 0xBE, 0x00, 0x01, 0xBF, 0x1D, 0x00, 0xB9, 0x02, 0x00, + 0xF3, 0xA7, 0x75, 0x81, 0xBE, 0x50, 0x00, 0xC7, 0x44, 0x0A, 0x10, 0x01, 0xC7, 0x04, 0x03, 0x00, + 0xE8, 0x66, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x05, 0xC7, 0x04, 0x04, 0x00, 0xE8, 0x5A, 0xFF, 0xC7, + 0x44, 0x0A, 0x00, 0x01, 0xC7, 0x44, 0x08, 0x09, 0x00, 0xC6, 0x44, 0x03, 0x01, 0x33, 0xC0, 0x89, + 0x44, 0x04, 0x89, 0x44, 0x06, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x3D, 0xFF, 0x33, 0xC0, 0xEB, 0x04, + 0x00, 0x00, 0x00, 0x00, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, + +{ 0xDB, 0xFD, 0x90, 0x90, 0xB0, 0xFF, 0xE6, 0xC3, 0xB9, 0x00, 0xD0, 0x50, 0x58, 0xE2, 0xFC, 0x33, + 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x04, 0x00, 0xBE, 0x00, 0x00, 0x8B, 0xFE, 0xB9, + 0x80, 0x00, 0xF3, 0xA5, 0xEB, 0x17, 0xFC, 0x33, 0xC0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xAC, + 0xE6, 0xC1, 0xE2, 0xF5, 0xC3, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, 0xFC, 0xC3, 0xBE, 0xCA, 0x00, + 0xB0, 0xFE, 0xE6, 0xC3, 0xFC, 0xAC, 0xE6, 0xC0, 0xE8, 0xEA, 0xFF, 0xB9, 0x03, 0x00, 0xE8, 0xD5, + 0xFF, 0xB9, 0x02, 0x00, 0xE8, 0xCF, 0xFF, 0xE4, 0xC2, 0x0A, 0xC0, 0x79, 0xFA, 0xB0, 0x08, 0xE6, + 0xC1, 0xE8, 0xD1, 0xFF, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x20, 0x8A, 0xE0, + 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x0A, 0xC4, 0x24, 0xFC, 0x74, 0x03, 0xE9, 0x39, + 0x00, 0xB9, 0x03, 0x00, 0xAC, 0xE6, 0xC2, 0xE2, 0xFB, 0xB9, 0x09, 0x00, 0xE8, 0x97, 0xFF, 0xE4, + 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x2C, 0x40, 0x24, 0xFC, 0x8A, 0xD8, 0xE4, 0xC0, 0x0A, + 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x80, 0x8A, 0xF8, 0xB9, 0x05, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, + 0x79, 0xFA, 0xE4, 0xC1, 0xE2, 0xF6, 0x0A, 0xFB, 0x74, 0x38, 0x8B, 0xC6, 0x3D, 0xE2, 0x00, 0x7D, + 0x06, 0xBE, 0xDC, 0x00, 0xE9, 0x79, 0xFF, 0xE9, 0x73, 0xFF, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, + 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x28, 0x03, 0xDF, 0x1E, + 0x07, 0x02, 0x00, 0x01, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x35, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x33, 0xC0, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x52, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0x04, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, +{ 0xDB, 0xFD, 0x90, 0x90, 0xB0, 0xFF, 0xE6, 0xC3, 0xB9, 0x00, 0xA0, 0x50, 0x58, 0xE2, 0xFC, 0x33, + 0xC0, 0x8E, 0xC0, 0x8E, 0xD8, 0x8E, 0xD0, 0xBC, 0x04, 0x00, 0xBA, 0x90, 0x00, 0xBE, 0x00, 0x00, + 0x8B, 0xFE, 0xB9, 0x00, 0x01, 0xF3, 0xA5, 0xB0, 0xFE, 0xE6, 0xC3, 0xB0, 0x28, 0xE6, 0xC0, 0xB9, + 0x00, 0xA0, 0x50, 0x58, 0xE2, 0xFC, 0xB9, 0x03, 0x00, 0xBE, 0xCA, 0x00, 0xE8, 0xC1, 0x00, 0xB9, + 0x02, 0x00, 0xE8, 0xBB, 0x00, 0xEB, 0x19, 0x52, 0x52, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, + 0xE4, 0xC2, 0x0A, 0xC0, 0x79, 0xFA, 0xB0, 0x08, 0xE6, 0xC1, 0xB9, 0x00, 0xF0, 0x50, 0x58, 0xE2, + 0xFC, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x34, 0x20, 0x8A, 0xE0, 0xE4, 0xC0, 0x0A, + 0xC0, 0x79, 0xFA, 0xE4, 0xC1, 0x0A, 0xC4, 0x24, 0xFC, 0x74, 0x03, 0xE9, 0x91, 0x00, 0xB9, 0x03, + 0x00, 0xAC, 0xE6, 0xC2, 0xE2, 0xFB, 0xB9, 0x09, 0x00, 0xE8, 0x64, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, + 0x79, 0xFA, 0xE4, 0xC1, 0x2C, 0x40, 0x24, 0xFC, 0x8A, 0xD8, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, + 0xE4, 0xC1, 0x34, 0x80, 0x8A, 0xF8, 0xB9, 0x05, 0x00, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xE4, + 0xC1, 0xE2, 0xF6, 0x0A, 0xFB, 0x74, 0x20, 0xE9, 0x55, 0x00, 0x03, 0xDF, 0x1E, 0x07, 0x02, 0x00, + 0x02, 0x00, 0x46, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0xBE, 0x00, 0x02, 0xAC, + 0x3C, 0xE5, 0x75, 0x03, 0xE9, 0x40, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x00, 0x01, 0xB9, 0x00, 0x12, + 0xF3, 0xA5, 0x33, 0xC0, 0xE4, 0xC2, 0xB1, 0x02, 0xD3, 0xD8, 0x24, 0x01, 0x0C, 0x02, 0x8B, 0xC8, + 0xFC, 0x33, 0xC0, 0xE4, 0xC0, 0x0A, 0xC0, 0x79, 0xFA, 0xAC, 0xE6, 0xC1, 0xE2, 0xF5, 0xC3, 0x43, + 0x6F, 0x6D, 0x70, 0x33, 0xC0, 0xEE, 0x0A, 0x44, 0x01, 0x74, 0xFB, 0x79, 0x01, 0xC3, 0x58, 0xBE, + 0x50, 0x00, 0xB0, 0x01, 0xEE, 0xB0, 0x00, 0x88, 0x44, 0x01, 0xEE, 0xB9, 0x00, 0xF0, 0x50, 0x58, + 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x13, 0x88, 0x44, 0x01, 0xEE, 0xB9, 0x00, 0xF0, 0x50, 0x58, + 0xE2, 0xFC, 0x0A, 0x44, 0x01, 0x75, 0x03, 0xE9, 0xDD, 0xFE, 0xC7, 0x04, 0x05, 0x00, 0xE8, 0xC2, + 0xFF, 0xC7, 0x44, 0x04, 0x08, 0x01, 0xC7, 0x04, 0x02, 0x00, 0xE8, 0xB6, 0xFF, 0xC7, 0x44, 0x04, + 0x00, 0x00, 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x02, 0x00, 0xC6, 0x44, 0x03, 0x01, + 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x9C, 0xFF, 0xBE, 0x00, 0x02, 0xBF, 0x0F, 0x01, 0xB9, 0x04, 0x00, + 0xA6, 0x74, 0x03, 0xE9, 0xA1, 0xFE, 0xE2, 0xF8, 0xBE, 0x50, 0x00, 0xC7, 0x44, 0x0A, 0x10, 0x02, + 0xC7, 0x04, 0x03, 0x00, 0xE8, 0x7C, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x06, 0xC7, 0x04, 0x04, 0x00, + 0xE8, 0x70, 0xFF, 0xC7, 0x44, 0x0A, 0x00, 0x02, 0xC7, 0x44, 0x08, 0x09, 0x00, 0xC6, 0x44, 0x03, + 0x01, 0x33, 0xC0, 0x89, 0x44, 0x04, 0x89, 0x44, 0x06, 0xC7, 0x04, 0x08, 0x00, 0xE8, 0x53, 0xFF, + 0xE9, 0x19, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0x04, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x00, 0x00, 0x00 }, + +{ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x43, 0xF8, + 0x00, 0x00, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x24, 0xD9, 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, + 0x00, 0x00, 0x80, 0x2C, 0x42, 0x39, 0x00, 0xFF, 0x00, 0xC3, 0x4E, 0xD2, 0x49, 0xF9, 0x00, 0xFF, + 0x00, 0xC0, 0x4B, 0xEC, 0x00, 0x01, 0x4D, 0xEC, 0x00, 0x02, 0x43, 0xEC, 0x00, 0x02, 0x19, 0x7C, + 0x00, 0x80, 0x00, 0x03, 0x76, 0x07, 0x20, 0x7C, 0x00, 0x00, 0x81, 0xCC, 0x42, 0x14, 0x42, 0x16, + 0x42, 0x16, 0x42, 0x16, 0x70, 0x04, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xC8, 0xFF, 0xF8, + 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x74, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x84, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x94, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0xA4, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0xB4, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, + 0x00, 0x50, 0x80, 0x00, 0x07, 0x16, 0x67, 0xFC, 0x1A, 0xBC, 0x00, 0x08, 0x3C, 0x3C, 0x00, 0x03, + 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF8, 0x51, 0xCE, 0xFF, 0xF2, + 0x07, 0x14, 0x67, 0xFC, 0x14, 0x15, 0x94, 0x3C, 0x00, 0x20, 0x07, 0x14, 0x67, 0xFC, 0x84, 0x15, + 0x66, 0x00, 0x00, 0x48, 0x74, 0x08, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xCA, 0xFF, 0xF8, + 0x07, 0x16, 0x67, 0xFC, 0x07, 0x14, 0x67, 0xFC, 0x10, 0x15, 0x90, 0x3C, 0x00, 0x40, 0x07, 0x14, + 0x67, 0xFC, 0xD0, 0x15, 0x90, 0x3C, 0x00, 0x80, 0x74, 0x04, 0x07, 0x14, 0x67, 0xFC, 0x1E, 0x15, + 0x51, 0xCA, 0xFF, 0xF8, 0x4A, 0x00, 0x66, 0x00, 0x00, 0x12, 0x1E, 0x11, 0xCE, 0xBC, 0x00, 0x00, + 0x00, 0x04, 0xE4, 0x07, 0x54, 0x07, 0x4E, 0xF8, 0x00, 0x00, 0x4D, 0xF9, 0x00, 0xFF, 0x00, 0x90, + 0x2A, 0x7C, 0x00, 0x00, 0x80, 0x64, 0x21, 0xFC, 0x00, 0x64, 0x80, 0x00, 0x00, 0x5C, 0x42, 0x2D, + 0x00, 0x01, 0x1C, 0xBC, 0x00, 0x01, 0x42, 0x16, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, + 0x51, 0xC8, 0xFF, 0xF8, 0x4A, 0x2D, 0x00, 0x01, 0x6C, 0x00, 0xFE, 0xC2, 0x42, 0x2D, 0x00, 0x11, + 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x11, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xB2, 0x42, 0x2D, 0x00, 0x21, + 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x21, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xA2, 0x0C, 0xB8, 0x43, 0x6F, + 0x6D, 0x70, 0x00, 0x00, 0x66, 0x00, 0xFE, 0x96, 0x42, 0x2D, 0x00, 0x31, 0x42, 0x16, 0x4A, 0x2D, + 0x00, 0x31, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x86, 0x42, 0x2D, 0x00, 0x41, 0x42, 0x16, 0x4A, 0x2D, + 0x00, 0x41, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x76, 0x42, 0x2D, 0x00, 0x51, 0x42, 0x16, 0x4A, 0x2D, + 0x00, 0x51, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x66, 0x60, 0x00, 0xFF, 0x60, 0x03, 0x8F, 0x46, 0x07, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43, 0xF9, 0x00, 0xFF, 0x00, 0x90, 0x20, 0x3C, + 0x00, 0x00, 0x00, 0x7F, 0x45, 0xF8, 0x00, 0x00, 0x26, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x26, 0xDA, + 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x34, 0x41, 0xF9, 0x00, 0xFF, 0x00, 0xC3, + 0x42, 0x10, 0x4E, 0xD2, 0x2A, 0x7C, 0x00, 0x00, 0x80, 0x66, 0x21, 0xFC, 0x00, 0x66, 0x80, 0x00, + 0x00, 0x5C, 0x42, 0x2D, 0x00, 0x01, 0x12, 0xBC, 0x00, 0x01, 0x42, 0x11, 0x70, 0xFF, 0x4E, 0x71, + 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF4, 0x4A, 0x2D, 0x00, 0x01, + 0x6C, 0xD2, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x86, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x96, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0xA6, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0xB6, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x80, 0x00, 0x42, 0x2D, 0x00, 0x11, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x11, + 0x67, 0xFA, 0x6E, 0x00, 0xFF, 0x60, 0x42, 0x2D, 0x00, 0x21, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x21, + 0x67, 0xFA, 0x6E, 0x00, 0xFF, 0x50, 0x20, 0x38, 0x00, 0x00, 0xB0, 0xBC, 0x43, 0x6F, 0x6D, 0x70, + 0x66, 0x00, 0xFF, 0x42, 0x42, 0x2D, 0x00, 0x31, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x31, 0x67, 0xFA, + 0x6E, 0x00, 0xFF, 0x32, 0x42, 0x2D, 0x00, 0x41, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x41, 0x67, 0xFA, + 0x6E, 0x00, 0xFF, 0x22, 0x42, 0x2D, 0x00, 0x51, 0x42, 0x11, 0x4A, 0x2D, 0x00, 0x51, 0x67, 0xFA, + 0x6E, 0x00, 0xFF, 0x12, 0x53, 0x88, 0x1E, 0x10, 0xCE, 0xBC, 0x00, 0x00, 0x00, 0x04, 0xE4, 0x07, + 0x54, 0x07, 0x4E, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x41, 0xF8, + 0x00, 0x00, 0x22, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x22, 0xD8, 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, + 0x00, 0x00, 0x80, 0x2C, 0x42, 0x39, 0x00, 0xFF, 0x00, 0xC3, 0x4E, 0xD2, 0x41, 0xF9, 0x00, 0xFF, + 0x00, 0xC0, 0x43, 0xE8, 0x00, 0x01, 0x45, 0xE8, 0x00, 0x02, 0x47, 0xE8, 0x00, 0x03, 0x17, 0x7C, + 0x00, 0x80, 0x00, 0x00, 0x70, 0x07, 0x42, 0x10, 0x28, 0x7C, 0x00, 0x00, 0x81, 0x42, 0x42, 0x12, + 0x42, 0x12, 0x42, 0x12, 0x72, 0x02, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, + 0x72, 0xFF, 0x4E, 0x71, 0x51, 0xC9, 0xFF, 0xFC, 0x72, 0x01, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, + 0x51, 0xC9, 0xFF, 0xF8, 0x01, 0x12, 0x67, 0xFC, 0x12, 0xBC, 0x00, 0x08, 0x72, 0xFF, 0x4E, 0x71, + 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC9, 0xFF, 0xF4, 0x01, 0x10, 0x67, 0xFC, + 0x12, 0x11, 0x92, 0x3C, 0x00, 0x20, 0x01, 0x10, 0x67, 0xFC, 0x82, 0x11, 0x67, 0x00, 0x00, 0x5C, + 0xD9, 0xFC, 0x00, 0x00, 0x00, 0x09, 0x10, 0xBC, 0x00, 0x28, 0x72, 0x02, 0x01, 0x10, 0x67, 0xFC, + 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x72, 0xFF, 0x4E, 0x71, 0x51, 0xC9, 0xFF, 0xFC, 0x72, 0x01, + 0x01, 0x10, 0x67, 0xFC, 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x01, 0x12, 0x67, 0xFC, 0x12, 0xBC, + 0x00, 0x08, 0x72, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC9, + 0xFF, 0xF4, 0x01, 0x10, 0x67, 0xFC, 0x12, 0x11, 0x92, 0x3C, 0x00, 0x20, 0x01, 0x10, 0x67, 0xFC, + 0x82, 0x11, 0xC2, 0x3C, 0x00, 0xFC, 0x66, 0x00, 0xFF, 0x4E, 0x72, 0x08, 0x01, 0x10, 0x67, 0xFC, + 0x12, 0x9C, 0x51, 0xC9, 0xFF, 0xF8, 0x01, 0x12, 0x67, 0xFC, 0x01, 0x10, 0x67, 0xFC, 0x14, 0x11, + 0x94, 0x3C, 0x00, 0x40, 0x01, 0x10, 0x67, 0xFC, 0xD4, 0x11, 0x94, 0x3C, 0x00, 0x80, 0x72, 0x04, + 0x01, 0x10, 0x67, 0xFC, 0x1E, 0x11, 0x51, 0xC9, 0xFF, 0xF8, 0xC4, 0x3C, 0x00, 0xF8, 0x66, 0x00, + 0xFF, 0x16, 0x1E, 0x12, 0xCE, 0xBC, 0x00, 0x00, 0x00, 0x04, 0xE4, 0x07, 0x54, 0x07, 0x4E, 0xF8, + 0x00, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, + 0x03, 0xEF, 0x1E, 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x08, 0x20, 0x3C, 0x00, 0x00, 0x00, 0x7F, 0x43, 0xF8, + 0x00, 0x00, 0x24, 0x7C, 0x00, 0x00, 0x80, 0x00, 0x24, 0xD9, 0x51, 0xC8, 0xFF, 0xFC, 0x24, 0x7C, + 0x00, 0x00, 0x80, 0x2C, 0x42, 0x39, 0x00, 0xFF, 0x00, 0xC3, 0x4E, 0xD2, 0x49, 0xF9, 0x00, 0xFF, + 0x00, 0xC0, 0x4B, 0xEC, 0x00, 0x01, 0x4D, 0xEC, 0x00, 0x02, 0x43, 0xEC, 0x00, 0x02, 0x19, 0x7C, + 0x00, 0x80, 0x00, 0x03, 0x76, 0x07, 0x18, 0xBC, 0x00, 0x28, 0x20, 0x7C, 0x00, 0x00, 0x81, 0xCE, + 0x42, 0x16, 0x42, 0x16, 0x42, 0x16, 0x70, 0x04, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xC8, + 0xFF, 0xF8, 0x60, 0x00, 0x00, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x76, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x86, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x96, 0x80, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x00, 0xA6, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0xB6, 0x80, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x07, 0x16, 0x67, 0xFC, 0x1A, 0xBC, 0x00, 0x08, 0x3C, 0x3C, + 0x00, 0x03, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF8, 0x51, 0xCE, + 0xFF, 0xF2, 0x07, 0x14, 0x67, 0xFC, 0x14, 0x15, 0x94, 0x3C, 0x00, 0x22, 0x07, 0x14, 0x67, 0xFC, + 0x84, 0x15, 0x66, 0x00, 0x00, 0x48, 0x74, 0x08, 0x07, 0x14, 0x67, 0xFC, 0x1A, 0x98, 0x51, 0xCA, + 0xFF, 0xF8, 0x07, 0x16, 0x67, 0xFC, 0x07, 0x14, 0x67, 0xFC, 0x10, 0x15, 0x90, 0x3C, 0x00, 0x42, + 0x07, 0x14, 0x67, 0xFC, 0xD0, 0x15, 0x90, 0x3C, 0x00, 0x80, 0x74, 0x03, 0x07, 0x14, 0x67, 0xFC, + 0x1E, 0x15, 0x51, 0xCA, 0xFF, 0xF8, 0x4A, 0x00, 0x66, 0x00, 0x00, 0x12, 0x1E, 0x11, 0xCE, 0xBC, + 0x00, 0x00, 0x00, 0x04, 0xE4, 0x07, 0x54, 0x07, 0x4E, 0xF8, 0x00, 0x00, 0x4D, 0xF9, 0x00, 0xFF, + 0x00, 0x90, 0x2A, 0x7C, 0x00, 0x00, 0x80, 0x66, 0x21, 0xFC, 0x00, 0x66, 0x80, 0x00, 0x00, 0x5C, + 0x42, 0x2D, 0x00, 0x01, 0x1C, 0xBC, 0x00, 0x01, 0x42, 0x16, 0x70, 0xFF, 0x4E, 0x71, 0x4E, 0x71, + 0x4E, 0x71, 0x51, 0xC8, 0xFF, 0xF8, 0x4A, 0x2D, 0x00, 0x01, 0x6C, 0x00, 0xFE, 0xC0, 0x42, 0x2D, + 0x00, 0x11, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x11, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xB0, 0x42, 0x2D, + 0x00, 0x21, 0x42, 0x16, 0x4A, 0x2D, 0x00, 0x21, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0xA0, 0x0C, 0xB8, + 0x43, 0x6F, 0x6D, 0x70, 0x00, 0x00, 0x66, 0x00, 0xFE, 0x94, 0x42, 0x2D, 0x00, 0x31, 0x42, 0x16, + 0x4A, 0x2D, 0x00, 0x31, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x84, 0x42, 0x2D, 0x00, 0x41, 0x42, 0x16, + 0x4A, 0x2D, 0x00, 0x41, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x74, 0x42, 0x2D, 0x00, 0x51, 0x42, 0x16, + 0x4A, 0x2D, 0x00, 0x51, 0x67, 0xFA, 0x6E, 0x00, 0xFE, 0x64, 0x60, 0x00, 0xFF, 0x60, 0x03, 0xEF, + 0x1E, 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, 0x0E, 0x03, 0x00, 0x27, 0xA9, 0xC0, + 0xFE, 0x00, 0xC0, 0x67, 0x61, 0x01, 0xA7, 0x61, 0x02, 0x67, 0x60, 0x03, 0x54, 0xA2, 0x80, 0x00, + 0xDC, 0x1B, 0x5C, 0x60, 0x00, 0xE7, 0xA9, 0xC0, 0x00, 0x01, 0xCD, 0x5C, 0x70, 0x00, 0x5C, 0x71, + 0x00, 0x5C, 0x70, 0x00, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, + 0x38, 0xCC, 0x17, 0x75, 0xEA, 0xC0, 0x00, 0x00, 0x7C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0xA0, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xB0, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, + 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, 0xA2, 0xA2, 0xA2, 0xCD, 0x17, + 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, 0x20, 0x34, 0x1B, 0x00, 0x9A, + 0x7D, 0x98, 0x68, 0x00, 0x1C, 0x10, 0x1A, 0xC0, 0x00, 0x00, 0x56, 0x94, 0xA0, 0x09, 0x34, 0x1B, + 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, + 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x14, 0x68, 0x00, 0x20, 0xA0, 0x40, 0x34, 0x1B, 0x00, 0x9A, + 0x7D, 0x00, 0x68, 0x00, 0x20, 0xA0, 0x80, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0xD4, 0x69, + 0x00, 0xCC, 0x17, 0x78, 0x1C, 0x00, 0x1A, 0xC0, 0x00, 0x00, 0x16, 0xCE, 0xD8, 0x71, 0x00, 0xE8, + 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, 0xAA, 0x82, 0x00, 0xA7, 0xA9, 0xC0, 0xFE, + 0x00, 0x90, 0x5C, 0xA8, 0x80, 0x61, 0xDC, 0x70, 0x00, 0x5C, 0x70, 0x00, 0xDD, 0x07, 0xA2, 0xA2, + 0xA2, 0xCD, 0x07, 0x7D, 0x9C, 0xAF, 0x80, 0x61, 0x1A, 0xBE, 0xB5, 0x5C, 0xA8, 0x80, 0x71, 0x5C, + 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x71, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x71, 0x1A, 0xBE, 0xA1, 0x5C, + 0xA8, 0x80, 0x81, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x81, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x81, + 0x1A, 0xBE, 0x8D, 0x47, 0xA5, 0x70, 0x6D, 0x6F, 0x43, 0x82, 0x00, 0x1A, 0xBE, 0x82, 0x5C, 0xA8, + 0x80, 0x91, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x91, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x91, 0x1A, + 0xBE, 0x6E, 0x5C, 0xA8, 0x80, 0xA1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xA1, 0x0A, 0x7C, 0x9C, + 0xAF, 0x80, 0xA1, 0x1A, 0xBE, 0x5A, 0x5C, 0xA8, 0x80, 0xB1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, + 0xB1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xB1, 0x1A, 0xBE, 0x46, 0xEA, 0xBF, 0x61, 0x03, 0x8F, 0x1E, + 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0xA7, 0xA9, 0xC0, 0xFE, 0x00, 0x90, 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, + 0x0E, 0x03, 0x00, 0x67, 0xA8, 0xC0, 0xFE, 0x00, 0xC3, 0x5C, 0x48, 0x00, 0x5C, 0xA8, 0xC0, 0x00, + 0x00, 0x61, 0xDC, 0x70, 0x00, 0x5C, 0x70, 0x00, 0xDD, 0x07, 0xA2, 0xA2, 0xA2, 0xCD, 0x07, 0x7D, + 0x9C, 0xAF, 0xC0, 0x00, 0x00, 0x61, 0x1A, 0x66, 0xEA, 0xC0, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0xA0, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xB0, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, + 0x5C, 0xA8, 0x80, 0x71, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x71, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, + 0x71, 0x1A, 0xBF, 0x4B, 0x5C, 0xA8, 0x80, 0x81, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x81, 0x0A, + 0x7C, 0x9C, 0xAF, 0x80, 0x81, 0x1A, 0xBF, 0x37, 0x47, 0xA5, 0x70, 0x6D, 0x6F, 0x43, 0x82, 0x00, + 0x1A, 0xBF, 0x2C, 0x5C, 0xA8, 0x80, 0x91, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x91, 0x0A, 0x7C, + 0x9C, 0xAF, 0x80, 0x91, 0x1A, 0xBF, 0x18, 0x5C, 0xA8, 0x80, 0xA1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, + 0x80, 0xA1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xA1, 0x1A, 0xBF, 0x04, 0x5C, 0xA8, 0x80, 0xB1, 0x5C, + 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xB1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xB1, 0x1A, 0xBE, 0xF0, 0x8F, + 0x0F, 0xCE, 0xD8, 0x49, 0x00, 0xE8, 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, 0xAA, + 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, 0x0E, 0x03, 0x00, 0x27, 0xA9, 0xC0, + 0xFE, 0x00, 0xC0, 0x67, 0x61, 0x01, 0xA7, 0x61, 0x02, 0x67, 0x60, 0x03, 0x54, 0xA2, 0x80, 0x00, + 0xDC, 0x1B, 0x5C, 0x60, 0x00, 0xE7, 0xA9, 0xC0, 0x00, 0x01, 0x33, 0x5C, 0x70, 0x00, 0x5C, 0x71, + 0x00, 0x5C, 0x70, 0x00, 0xDC, 0x11, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, + 0x38, 0xCC, 0x17, 0x75, 0xDC, 0x07, 0xA2, 0xA2, 0xCC, 0x07, 0x7E, 0x5C, 0x11, 0x34, 0x1B, 0x00, + 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, + 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, 0xA2, 0xA2, 0xA2, 0xCD, 0x17, 0x7D, 0x34, 0x1B, 0x00, 0x9A, + 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, 0x20, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x98, 0x68, 0x00, 0x1C, + 0x10, 0x0A, 0xC0, 0x00, 0x00, 0x60, 0xC1, 0xA1, 0x00, 0x09, 0x14, 0xA3, 0x28, 0x00, 0xDC, 0x11, + 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xDC, 0x07, + 0xA2, 0xA2, 0xCC, 0x07, 0x7E, 0x5C, 0x11, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, + 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, + 0xA2, 0xA2, 0xA2, 0xCD, 0x17, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, + 0x20, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x98, 0x68, 0x00, 0xA8, 0xA0, 0xFC, 0x1C, 0x10, 0x1A, 0xBF, + 0x44, 0x94, 0xA0, 0x09, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, + 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x14, 0x68, 0x00, 0x20, + 0xA0, 0x40, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x00, 0x68, 0x00, 0x20, 0xA0, 0x80, 0xDC, 0x12, 0x34, + 0x1B, 0x00, 0x9A, 0x7D, 0xD4, 0x69, 0x00, 0xCC, 0x17, 0x78, 0x28, 0xA0, 0xF8, 0x1C, 0x00, 0x1A, + 0xBF, 0x03, 0xCE, 0xD8, 0x71, 0x00, 0xE8, 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, + 0xAA, 0x82, 0x00, 0x03, 0x8F, 0x46, 0x07, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1A, 0x07, + 0x80, 0x03, 0xEF, 0x1E, 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + +{ 0x17, 0xA0, 0x00, 0x00, 0x00, 0x80, 0x5F, 0x08, 0x5F, 0x10, 0x0E, 0x03, 0x00, 0x27, 0xA9, 0xC0, + 0xFE, 0x00, 0xC0, 0x67, 0x61, 0x01, 0xA7, 0x61, 0x02, 0x67, 0x60, 0x03, 0x54, 0xA2, 0x80, 0x00, + 0xDC, 0x1B, 0x14, 0xA3, 0x28, 0x00, 0xE7, 0xA9, 0xC0, 0x00, 0x01, 0xCD, 0x5C, 0x70, 0x00, 0x5C, + 0x71, 0x00, 0x5C, 0x70, 0x00, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, + 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xEA, 0xC0, 0x00, 0x00, 0x7B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02, 0x00, 0x90, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x02, 0x00, 0xA0, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xB0, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x02, 0x00, 0x50, 0x00, 0x00, + 0xB4, 0x1B, 0x00, 0x9A, 0x7D, 0x54, 0xA3, 0x08, 0x00, 0xDD, 0x17, 0xA2, 0xA2, 0xA2, 0xCD, 0x17, + 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x94, 0x68, 0x00, 0xA0, 0xA0, 0x22, 0x34, 0x1B, 0x00, 0x9A, + 0x7D, 0x98, 0x68, 0x00, 0x1C, 0x10, 0x1A, 0xC0, 0x00, 0x00, 0x56, 0x94, 0xA0, 0x09, 0x34, 0x1B, + 0x00, 0x9A, 0x7D, 0x54, 0x7B, 0x00, 0x00, 0x8C, 0x38, 0xCC, 0x17, 0x75, 0xB4, 0x1B, 0x00, 0x9A, + 0x7D, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0x14, 0x68, 0x00, 0x20, 0xA0, 0x42, 0x34, 0x1B, 0x00, 0x9A, + 0x7D, 0x00, 0x68, 0x00, 0x20, 0xA0, 0x80, 0xDC, 0x12, 0x34, 0x1B, 0x00, 0x9A, 0x7D, 0xD4, 0x69, + 0x00, 0xCC, 0x17, 0x78, 0x1C, 0x00, 0x1A, 0xC0, 0x00, 0x00, 0x16, 0xCE, 0xD8, 0x71, 0x00, 0xE8, + 0xA1, 0x04, 0x4E, 0xC4, 0xA1, 0xFE, 0x0C, 0x39, 0x7F, 0xAA, 0x82, 0x00, 0xA7, 0xA9, 0xC0, 0xFE, + 0x00, 0x90, 0x5C, 0xA8, 0x80, 0x61, 0xDC, 0x70, 0x00, 0x5C, 0x70, 0x00, 0xDD, 0x07, 0xA2, 0xA2, + 0xA2, 0xCD, 0x07, 0x7D, 0x9C, 0xAF, 0x80, 0x61, 0x1A, 0xBE, 0xB5, 0x5C, 0xA8, 0x80, 0x71, 0x5C, + 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x71, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x71, 0x1A, 0xBE, 0xA1, 0x5C, + 0xA8, 0x80, 0x81, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x81, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x81, + 0x1A, 0xBE, 0x8D, 0x47, 0xA5, 0x70, 0x6D, 0x6F, 0x43, 0x82, 0x00, 0x1A, 0xBE, 0x82, 0x5C, 0xA8, + 0x80, 0x91, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0x91, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0x91, 0x1A, + 0xBE, 0x6E, 0x5C, 0xA8, 0x80, 0xA1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, 0xA1, 0x0A, 0x7C, 0x9C, + 0xAF, 0x80, 0xA1, 0x1A, 0xBE, 0x5A, 0x5C, 0xA8, 0x80, 0xB1, 0x5C, 0x70, 0x00, 0x1C, 0xA8, 0x80, + 0xB1, 0x0A, 0x7C, 0x9C, 0xAF, 0x80, 0xB1, 0x1A, 0xBE, 0x46, 0xEA, 0xBF, 0x61, 0x03, 0xEF, 0x1E, + 0x07, 0x02, 0xC6, 0x02, 0x00, 0x00, 0x01, 0x03, 0x05, 0x20, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } +}; + + +/* returns TRUE iff there exists a disk with VERBOSE */ +static int32 disk1a_hasProperty(uint32 property) { + int32 i; + for (i = 0; i < DISK1A_MAX_DRIVES; i++) + if (disk1a_dev.units[i].flags & property) return TRUE; + return FALSE; +} + +/* Reset routine */ +static t_stat disk1a_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ + if (disk1a_hasProperty(UNIT_DISK1A_ROM)) + sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &disk1arom, TRUE); + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk1adev, TRUE); + } else { + /* Connect DISK1A ROM at base address */ + if (disk1a_hasProperty(UNIT_DISK1A_ROM)) + if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &disk1arom, FALSE) != 0) { + printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + + /* Connect DISK1A at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk1adev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + return SCPE_OK; +} + +static t_stat disk1a_boot(int32 unitno, DEVICE *dptr) +{ + DBG_PRINT(("Booting DISK1A Controller, bootstrap=%d" NLP, bootstrap)); + + /* Re-enable the ROM in case it was disabled */ + disk1a_info->rom_disabled = FALSE; + + /* Set the PC to 0, and go. */ + *((int32 *) sim_PC->loc) = 0; + return SCPE_OK; +} + +/* Attach routine */ +static t_stat disk1a_attach(UNIT *uptr, char *cptr) +{ + t_stat r; + r = i8272_attach(uptr, cptr); + + return r; +} + +/* Detach routine */ +static t_stat disk1a_detach(UNIT *uptr) +{ + t_stat r; + + r = i8272_detach(uptr); + + return r; +} + +static int32 disk1arom(const int32 Addr, const int32 write, const int32 data) +{ +/* DBG_PRINT(("DISK1A: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ + if(write) { + disk1aram[Addr & 0x1FF] = data; + return 0; + } else { + if(disk1a_info->rom_disabled == FALSE) { + return(disk1a_rom[bootstrap][Addr & 0x1FF]); + } else { + return(disk1aram[Addr & 0x1FF]); + } + } +} + +static int32 disk1adev(const int32 port, const int32 io, const int32 data) +{ + int32 result; + if(io) { + TRACE_PRINT(VERBOSE_MSG, + ("DISK1A: " ADDRESS_FORMAT " OUT, Port 0x%02x Data 0x%02x" NLP, PCX, port, data)) + DISK1A_Write(port, data); + result = 0; + } else { + result = DISK1A_Read(port); + TRACE_PRINT(VERBOSE_MSG, + ("DISK1A: " ADDRESS_FORMAT " IN, Port 0x%02x Result 0x%02x" NLP, PCX, port, result)) + } + return result; +} + +#define DISK1A_DRIVE_STATUS 2 /* R=Drive Status Register / W=DMA Address Register */ +#define DISK1A_MOTOR 3 /* R=Unused / W=Motor Control Register */ +#define BOOT_PROM_DISABLE 0x01 +#define FLOPPY_MOTORS 0xF0 + +static uint8 DISK1A_Read(const uint32 Addr) +{ + uint8 cData; + + cData = 0x00; + + switch(Addr & 0x3) { + case I8272_FDC_MSR: + case I8272_FDC_DATA: + cData = I8272_Read(Addr); + break; + case DISK1A_DRIVE_STATUS: + cData = 0x81; /* Ready */ + TRACE_PRINT(STATUS_MSG, + ("DISK1A: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData)) + break; + case DISK1A_MOTOR: + TRACE_PRINT(VERBOSE_MSG, + ("DISK1A: " ADDRESS_FORMAT " Error, can't read from MOTOR register." NLP, PCX)) + cData = 0xFF; /* Return High-Z data */ + break; + } + + return (cData); +} + +static uint8 DISK1A_Write(const uint32 Addr, uint8 cData) +{ + uint8 result = 0; + + switch(Addr & 0x3) { + case I8272_FDC_MSR: + case I8272_FDC_DATA: + result = I8272_Write(Addr, cData); + break; + + case DISK1A_DRIVE_STATUS: /* DMA Address */ + disk1a_info->dma_addr <<= 8; + disk1a_info->dma_addr &= 0x00FFFF00; + disk1a_info->dma_addr |= cData; + TRACE_PRINT(RD_DATA_MSG, ("DISK1A: " ADDRESS_FORMAT " DMA Address=%06x" NLP, + PCX, disk1a_info->dma_addr)) + I8272_Set_DMA(disk1a_info->dma_addr); + break; + case DISK1A_MOTOR: + TRACE_PRINT(CMD_MSG, + ("DISK1A: " ADDRESS_FORMAT " write Motor Reg=0x%02x" NLP, PCX, cData)) + + if((cData & BOOT_PROM_DISABLE) == 0) { + TRACE_PRINT(CMD_MSG, + ("DISK1A: " ADDRESS_FORMAT " Boot ROM disabled" NLP, PCX)) + + /* Unmap Boot ROM */ + disk1a_info->rom_disabled = TRUE; + } + + TRACE_PRINT(CMD_MSG, ("DISK1A: " ADDRESS_FORMAT " Motors = %x" NLP, + PCX, (cData & FLOPPY_MOTORS) >> 4)) + break; + } + + return (result); +} + diff --git a/AltairZ80/s100_disk2.c b/AltairZ80/s100_disk2.c new file mode 100644 index 0000000..25d6a7b --- /dev/null +++ b/AltairZ80/s100_disk2.c @@ -0,0 +1,574 @@ +/************************************************************************* + * * + * $Id: s100_disk2.c 1771 2008-01-09 07:10:46Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * CompuPro DISK2 Hard Disk Controller module for SIMH. * + * This module must be used in conjunction with the CompuPro Selector * + * Channel Module for proper operation. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#include "sim_imd.h" + +#define SEEK_MSG 0x01 +#define BUG_MSG 0x02 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 +#define VERBOSE_MSG 0x80 + +#define DISK2_MAX_DRIVES 4 + +typedef union { + uint8 raw[2051]; + struct { + uint8 header[3]; + uint8 data[2048]; + } u; +} SECTOR_FORMAT; + +static SECTOR_FORMAT sdata; + +typedef struct { + UNIT *uptr; + DISK_INFO *imd; + uint16 ntracks; /* number of tracks */ + uint8 nheads; /* number of heads */ + uint8 nsectors; /* number of sectors/track */ + uint32 sectsize; /* sector size, not including pre/postamble */ + uint16 track; /* Current Track */ + uint8 ready; /* Is drive ready? */ +} DISK2_DRIVE_INFO; + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint8 sel_drive; /* Currently selected drive */ + uint8 head_sel; /* Head select (signals to drive itself) */ + uint8 head; /* Head set by write to the HEAD register */ + uint8 cyl; /* Cyl that the current operation is targetting */ + uint8 sector; /* Sector the current READ/WRITE operation is targetting */ + uint8 hdr_sector; /* Current sector for WRITE_HEADER */ + uint8 ctl_attn; + uint8 ctl_run; + uint8 ctl_op; + uint8 ctl_fault_clr; + uint8 ctl_us; + uint8 timeout; + uint8 crc_error; + uint8 overrun; + uint8 seek_complete; + uint8 write_fault; + DISK2_DRIVE_INFO drive[DISK2_MAX_DRIVES]; +} DISK2_INFO; + +static DISK2_INFO disk2_info_data = { { 0x0, 0, 0xC8, 2 } }; +static DISK2_INFO *disk2_info = &disk2_info_data; + +extern uint32 PCX; +extern REG *sim_PC; +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern int32 selchan_dma(uint8 *buf, uint32 len); +extern int32 find_unit_index(UNIT *uptr); + +/* These are needed for DMA. PIO Mode has not been implemented yet. */ +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); +extern uint8 GetBYTEWrapper(const uint32 Addr); + +#define UNIT_V_DISK2_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_DISK2_WLK (1 << UNIT_V_DISK2_WLK) +#define UNIT_V_DISK2_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_DISK2_VERBOSE (1 << UNIT_V_DISK2_VERBOSE) +#define DISK2_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ +#define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ +#define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ +#define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ + +static t_stat disk2_reset(DEVICE *disk2_dev); +static t_stat disk2_attach(UNIT *uptr, char *cptr); +static t_stat disk2_detach(UNIT *uptr); + +static int32 disk2dev(const int32 port, const int32 io, const int32 data); + +static uint8 DISK2_Read(const uint32 Addr); +static uint8 DISK2_Write(const uint32 Addr, uint8 cData); + +static int32 trace_level = 0; /* Disable all tracing by default */ + +static UNIT disk2_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, DISK2_CAPACITY) } +}; + +static REG disk2_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { NULL } +}; + +static MTAB disk2_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_DISK2_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_DISK2_WLK, UNIT_DISK2_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_DISK2_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_DISK2_VERBOSE, UNIT_DISK2_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE disk2_dev = { + "DISK2", disk2_unit, disk2_reg, disk2_mod, + DISK2_MAX_DRIVES, 10, 31, 1, DISK2_MAX_DRIVES, DISK2_MAX_DRIVES, + NULL, NULL, &disk2_reset, + NULL, &disk2_attach, &disk2_detach, + &disk2_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat disk2_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, TRUE); + } else { + /* Connect DISK2 at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &disk2dev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + return SCPE_OK; +} + + +/* Attach routine */ +static t_stat disk2_attach(UNIT *uptr, char *cptr) +{ + t_stat r = SCPE_OK; + DISK2_DRIVE_INFO *pDrive; + char header[4]; + unsigned int i = 0; + + i = find_unit_index(uptr); + if (i == -1) { + return (SCPE_IERR); + } + pDrive = &disk2_info->drive[i]; + + pDrive->ready = 1; + disk2_info->write_fault = 1; + pDrive->track = 5; + pDrive->ntracks = 243; + pDrive->nheads = 8; + pDrive->nsectors = 11; + pDrive->sectsize = 1024; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + if(sim_fsize(uptr->fileref) != 0) { + uptr->capac = sim_fsize(uptr->fileref); + } else { + uptr->capac = (pDrive->ntracks * pDrive->nsectors * pDrive->nheads * pDrive->sectsize); + } + + pDrive->uptr = uptr; + + /* Default for new file is DSK */ + uptr->u3 = IMAGE_TYPE_DSK; + + if(uptr->capac > 0) { + fgets(header, 4, uptr->fileref); + if(!strcmp(header, "IMD")) { + uptr->u3 = IMAGE_TYPE_IMD; + } else if(!strcmp(header, "CPT")) { + printf("CPT images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_CPT; + disk2_detach(uptr); + return SCPE_OPENERR; + } else { + uptr->u3 = IMAGE_TYPE_DSK; + } + } + + if (uptr->flags & UNIT_DISK2_VERBOSE) + printf("DISK2%d, attached to '%s', type=%s, len=%d\n", i, cptr, + uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + + if(uptr->u3 == IMAGE_TYPE_IMD) { + if(uptr->capac < 318000) { + printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); + disk2_detach(uptr); + return SCPE_OPENERR; + } + + if (uptr->flags & UNIT_DISK2_VERBOSE) + printf("--------------------------------------------------------\n"); + disk2_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_DISK2_VERBOSE)); + if (uptr->flags & UNIT_DISK2_VERBOSE) printf("\n"); + } else { + disk2_info->drive[i].imd = NULL; + } + + return SCPE_OK; +} + + +/* Detach routine */ +t_stat disk2_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + i = find_unit_index(uptr); + + if (i == -1) { + return (SCPE_IERR); + } + + if (uptr->flags & UNIT_DISK2_VERBOSE) + printf("Detach DISK2%d\n", i); + + r = detach_unit(uptr); /* detach unit */ + if ( r != SCPE_OK) + return r; + + return SCPE_OK; +} + + +static int32 disk2dev(const int32 port, const int32 io, const int32 data) +{ +/* TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); */ + if(io) { + DISK2_Write(port, data); + return 0; + } else { + return(DISK2_Read(port)); + } +} + +#define DISK2_CSR 0 /* R=DISK2 Status / W=DISK2 Control Register */ +#define DISK2_DATA 1 /* R=Step Pulse / W=Write Data Register */ + +static uint8 DISK2_Read(const uint32 Addr) +{ + uint8 cData; + DISK2_DRIVE_INFO *pDrive; + + pDrive = &disk2_info->drive[disk2_info->sel_drive]; + cData = 0x00; + + switch(Addr & 0x1) { + case DISK2_CSR: + cData = (disk2_info->ctl_attn) << 7; + cData |= (disk2_info->timeout) << 6; + cData |= (disk2_info->crc_error) << 5; + cData |= (disk2_info->overrun) << 4; + cData |= (pDrive->ready == 0) ? 0x08 : 0x00; + cData |= (disk2_info->seek_complete == 0) ? 0x04 : 0x00; + cData |= (disk2_info->write_fault) << 1; + cData |= ((pDrive->track != 0) || (disk2_info->seek_complete == 0)) ? 0x01 : 0x00; + TRACE_PRINT(STATUS_MSG, ("DISK2: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData)); + + disk2_info->seek_complete = 1; + break; + case DISK2_DATA: + if(disk2_info->ctl_op & 0x04) { + if(pDrive->track < pDrive->ntracks) { + pDrive->track ++; + } + } else { + if(pDrive->track > 0) { + pDrive->track --; + } + } + TRACE_PRINT(SEEK_MSG, ("DISK2: " ADDRESS_FORMAT " Step %s, Track=%d" NLP, + PCX, disk2_info->ctl_op & 0x04 ? "IN" : "OUT", pDrive->track)); + disk2_info->seek_complete = 0; + cData = 0xFF; /* Return High-Z data */ + break; + } + + return (cData); +} + +#define DISK2_OP_DRIVE 0x00 +#define DISK2_OP_CYL 0x01 +#define DISK2_OP_HEAD 0x02 +#define DISK2_OP_SECTOR 0x03 + +#define DISK2_CMD_NULL 0x00 +#define DISK2_CMD_READ_DATA 0x01 +#define DISK2_CMD_WRITE_DATA 0x02 +#define DISK2_CMD_WRITE_HEADER 0x03 +#define DISK2_CMD_READ_HEADER 0x04 + +static uint8 DISK2_Write(const uint32 Addr, uint8 cData) +{ + uint32 track_offset; + uint8 result = 0; + uint8 i; + long file_offset; + DISK2_DRIVE_INFO *pDrive; + + pDrive = &disk2_info->drive[disk2_info->sel_drive]; + + switch(Addr & 0x1) { + case DISK2_CSR: /* Write CTL register */ + disk2_info->ctl_attn = (cData & 0x80) >> 7; + disk2_info->ctl_run = (cData & 0x40) >> 6; + disk2_info->ctl_op = (cData & 0x38) >> 3; + disk2_info->ctl_fault_clr = (cData & 0x04) >> 2; + if(disk2_info->ctl_fault_clr == 1) { + disk2_info->timeout = 0; + } + disk2_info->ctl_us = (cData & 0x03); + TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " ATTN*=%d, RUN=%d, OP=%d, FAULT_CLR=%d, US=%d" NLP, + PCX, + disk2_info->ctl_attn, + disk2_info->ctl_run, + disk2_info->ctl_op, + disk2_info->ctl_fault_clr, + disk2_info->ctl_us)); + + /* FIXME: seek_complete = 1 is needed by CP/M, but why? Also, maybe related, + * there appears to be a bug in the seeking logic. For some reason, the + * pDrive->track does not equal the disk2_info->cyl, when doing READ_DATA and + * WRITE_DATA commands. For this reason, disk2_info->cyl is used instead of + * pDrive->track for these commands. For READ_HEADER and WRITE_HEADER, + * pDrive->track is used, because the DISK2 format program (DISK2.COM) does not + * issue DISK2_OP_CYL. The root cause of this anomaly needs to be determined, + * because it is surely a bug in the logic somewhere. + */ + /* pDrive->track may be different from disk2_info->cyl when a program such as DISK2.COM + moves the position of the track without informing the CP/M BIOS which stores the + current track for each drive. This appears to be an application program bug. + */ + disk2_info->seek_complete = 1; + + if(disk2_info->ctl_run == 1) { + disk2_info->timeout = 0; + track_offset = disk2_info->cyl * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3); + + switch(disk2_info->ctl_op) { + case DISK2_CMD_NULL: + TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " NULL Command" NLP, PCX)); + break; + case DISK2_CMD_READ_DATA: + TRACE_PRINT(RD_DATA_MSG, ("DISK2: " ADDRESS_FORMAT " READ_DATA: (C:%d/H:%d/S:%d)" NLP, + PCX, + disk2_info->cyl, + disk2_info->head, + disk2_info->sector)); + if(disk2_info->head_sel != disk2_info->head) { + printf("DISK2: " ADDRESS_FORMAT " READ_DATA: head_sel != head" NLP, PCX); + } + /* See FIXME above... that might be why this does not work properly... */ + if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */ + TRACE_PRINT(BUG_MSG, ("DISK2: " ADDRESS_FORMAT " READ_DATA: cyl=%d, track=%d" NLP, + PCX, disk2_info->cyl, pDrive->track)); + pDrive->track = disk2_info->cyl; /* update track */ + } + fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET); + for(i=0;insectors;i++) { + /* Read sector */ + fread(sdata.raw, (pDrive->sectsize + 3), 1, (pDrive->uptr)->fileref); + if(sdata.u.header[2] == disk2_info->sector) { + if(sdata.u.header[0] != disk2_info->cyl) { /*pDrive->track) { */ + printf("DISK2: " ADDRESS_FORMAT " READ_DATA Incorrect header: track" NLP, PCX); + disk2_info->timeout = 1; + } + if(sdata.u.header[1] != disk2_info->head) { + printf("DISK2: " ADDRESS_FORMAT " READ_DATA Incorrect header: head" NLP, PCX); + disk2_info->timeout = 1; + } + + selchan_dma(sdata.u.data, pDrive->sectsize); + break; + } + if(i == pDrive->nsectors) { + printf("DISK2: " ADDRESS_FORMAT " Sector not found" NLP, PCX); + disk2_info->timeout = 1; + } + } + + break; + case DISK2_CMD_WRITE_DATA: + TRACE_PRINT(WR_DATA_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_DATA: (C:%d/H:%d/S:%d)" NLP, + PCX, + disk2_info->cyl, + disk2_info->head, + disk2_info->sector)); + if(disk2_info->head_sel != disk2_info->head) { + printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA: head_sel != head" NLP, PCX); + } + if(disk2_info->cyl != pDrive->track) { /* problem, should not happen, see above */ + TRACE_PRINT(BUG_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_DATA = 0x%02x, cyl=%d, track=%d" NLP, + PCX, cData, disk2_info->cyl, pDrive->track)); + pDrive->track = disk2_info->cyl; /* update track */ + } + + fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET); + for(i=0;insectors;i++) { + /* Read sector */ + file_offset = ftell((pDrive->uptr)->fileref); + fread(sdata.raw, 3, 1, (pDrive->uptr)->fileref); + if(sdata.u.header[2] == disk2_info->sector) { + if(sdata.u.header[0] != disk2_info->cyl) { + printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA Incorrect header: track" NLP, PCX); + disk2_info->timeout = 1; + } + if(sdata.u.header[1] != disk2_info->head) { + printf("DISK2: " ADDRESS_FORMAT " WRITE_DATA Incorrect header: head" NLP, PCX); + disk2_info->timeout = 1; + } + + selchan_dma(sdata.u.data, pDrive->sectsize); + fseek((pDrive->uptr)->fileref, file_offset+3, SEEK_SET); + fwrite(sdata.u.data, (pDrive->sectsize), 1, (pDrive->uptr)->fileref); + break; + } + fread(sdata.raw, pDrive->sectsize, 1, (pDrive->uptr)->fileref); + if(i == pDrive->nsectors) { + printf("DISK2: " ADDRESS_FORMAT " Sector not found" NLP, PCX); + disk2_info->timeout = 1; + } + } + break; + case DISK2_CMD_WRITE_HEADER: + track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3); + TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " WRITE_HEADER Command: track=%d (%d), Head=%d, Sector=%d" NLP, + PCX, + pDrive->track, + disk2_info->cyl, + disk2_info->head_sel, + disk2_info->hdr_sector)); + + i = disk2_info->hdr_sector; + selchan_dma(sdata.raw, 3); + fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * (pDrive->sectsize + 3) * pDrive->nsectors) + (i * (pDrive->sectsize + 3)), SEEK_SET); + fwrite(sdata.raw, 3, 1, (pDrive->uptr)->fileref); + + disk2_info->hdr_sector++; + if(disk2_info->hdr_sector >= pDrive->nsectors) { + disk2_info->hdr_sector = 0; + disk2_info->timeout = 1; + } + break; + case DISK2_CMD_READ_HEADER: + track_offset = pDrive->track * pDrive->nheads * pDrive->nsectors * (pDrive->sectsize + 3); + TRACE_PRINT(CMD_MSG, ("DISK2: " ADDRESS_FORMAT " READ_HEADER Command" NLP, PCX)); + fseek((pDrive->uptr)->fileref, track_offset + (disk2_info->head_sel * pDrive->nsectors * (pDrive->sectsize + 3)), SEEK_SET); + fread(sdata.raw, 3, 1, (pDrive->uptr)->fileref); + selchan_dma(sdata.raw, 3); + break; + default: + printf("DISK2: " ADDRESS_FORMAT " Unknown CMD=%d" NLP, PCX, disk2_info->ctl_op); + break; + } + + disk2_info->ctl_attn = 0; + } + + break; + case DISK2_DATA: + switch(disk2_info->ctl_op) { + case DISK2_OP_DRIVE: + switch(cData >> 4) { + case 0x01: + disk2_info->sel_drive = 0; + break; + case 0x02: + disk2_info->sel_drive = 1; + break; + case 0x04: + disk2_info->sel_drive = 2; + break; + case 0x08: + disk2_info->sel_drive = 3; + break; + default: + printf("DISK2: " ADDRESS_FORMAT " Error, invalid drive select=0x%x" NLP, PCX, cData >> 4); + break; + } + + disk2_info->head_sel = cData & 0x0F; + + TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [DRIVE]=%d, Head=%d" NLP, + PCX, disk2_info->sel_drive, disk2_info->head)); + break; + case DISK2_OP_CYL: + disk2_info->cyl = cData; + TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [CYL] = %02x" NLP, + PCX, cData)); + break; + case DISK2_OP_HEAD: + disk2_info->head = cData; + TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write DATA [HEAD] = %02x" NLP, + PCX, cData)); + break; + case DISK2_OP_SECTOR: + disk2_info->sector = cData; + TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write Register [SECTOR] = %02x" NLP, + PCX, cData)); + break; + default: + TRACE_PRINT(VERBOSE_MSG, ("DISK2: " ADDRESS_FORMAT " Write Register unknown op [%d] = %02x" NLP, + PCX, disk2_info->ctl_op, cData)); + break; + } + } + + return (result); +} + diff --git a/AltairZ80/s100_fif.c b/AltairZ80/s100_fif.c new file mode 100644 index 0000000..62ddfa8 --- /dev/null +++ b/AltairZ80/s100_fif.c @@ -0,0 +1,467 @@ +/* $Id: s100_fif.c 1773 2008-01-11 05:46:19Z hharte $ + + IMSAI FIF Disk Controller by Ernie Price + + Based on altairz80_dsk.c, Copyright (c) 2002-2008, Peter Schorn + + Plug-n-Play added by Howard M. Harte + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + PETER SCHORN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Peter Schorn shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Peter Schorn. + +*/ + +#include "altairz80_defs.h" + +#define UNIT_V_DSK_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_DSK_WLK (1 << UNIT_V_DSK_WLK) +#define UNIT_V_DSK_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_DSK_VERBOSE (1 << UNIT_V_DSK_VERBOSE) +#define DSK_SECTSIZE 137 /* size of sector */ +#define DSK_SECT 32 /* sectors per track */ +#define MAX_TRACKS 254 /* number of tracks, original Altair has 77 tracks only */ +#define DSK_TRACSIZE (DSK_SECTSIZE * DSK_SECT) +#define MAX_DSK_SIZE (DSK_TRACSIZE * MAX_TRACKS) + +static t_stat fif_reset(DEVICE *dptr); +static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc); +static int32 fif_io(const int32 port, const int32 io, const int32 data); + +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern uint8 GetBYTEWrapper(const uint32 Addr); +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); + +extern uint32 PCX; +extern char messageBuffer[]; +extern void printMessage(void); + +/* global data on status */ + +/* currently selected drive (values are 0 .. NUM_OF_DSK) + current_disk < NUM_OF_DSK implies that the corresponding disk is attached to a file */ +static int32 current_disk = NUM_OF_DSK; +static int32 warnLevelDSK = 3; +static int32 warnLock [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static int32 warnAttached [NUM_OF_DSK] = {0, 0, 0, 0, 0, 0, 0, 0}; +static int32 warnDSK11 = 0; + +/* 88DSK Standard I/O Data Structures */ + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ +} FIF_INFO; + +FIF_INFO fif_info_data = { { 0x0000, 0, 0xFD, 1 } }; +FIF_INFO *fif_info = &fif_info_data; + +static UNIT fif_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MAX_DSK_SIZE) } +}; + +static REG fif_reg[] = { + { DRDATA (DISK, current_disk, 4) }, + { DRDATA (DSKWL, warnLevelDSK, 32) }, + { BRDATA (WARNLOCK, warnLock, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { BRDATA (WARNATTACHED, warnAttached, 10, 32, NUM_OF_DSK), REG_CIRC + REG_RO }, + { DRDATA (WARNDSK11, warnDSK11, 4), REG_RO }, + { NULL } +}; + +static MTAB fif_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_DSK_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_DSK_WLK, UNIT_DSK_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_DSK_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_DSK_VERBOSE, UNIT_DSK_VERBOSE, "VERBOSE", "VERBOSE", &fif_set_verbose }, + { 0 } +}; + +DEVICE fif_dev = { + "FIF", fif_unit, fif_reg, fif_mod, + 8, 10, 31, 1, 8, 8, + NULL, NULL, &fif_reset, + NULL, NULL, NULL, + &fif_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +static void resetDSKWarningFlags(void) { + int32 i; + for (i = 0; i < NUM_OF_DSK; i++) { + warnLock[i] = 0; + warnAttached[i] = 0; + } + warnDSK11 = 0; +} + +static t_stat fif_set_verbose(UNIT *uptr, int32 value, char *cptr, void *desc) { + resetDSKWarningFlags(); + return SCPE_OK; +} + +/* returns TRUE iff there exists a disk with VERBOSE */ +static int32 hasVerbose(void) { + int32 i; + for (i = 0; i < NUM_OF_DSK; i++) { + if (((fif_dev.units + i) -> flags) & UNIT_DSK_VERBOSE) { + return TRUE; + } + } + return FALSE; +} + +/* service routines to handle simulator functions */ + +/* Reset routine */ +static t_stat fif_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + resetDSKWarningFlags(); + current_disk = NUM_OF_DSK; + + if(dptr->flags & DEV_DIS) { + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, TRUE); + } else { + /* Connect HDSK at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &fif_io, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); + dptr->flags |= DEV_DIS; + return SCPE_ARG; + } + } + return SCPE_OK; +} + +typedef struct desc_t +{ + uint8 + cmd_unit, /* (cmd << 4) | unit : 1 = A: */ + result, /* result: 0 == busy, 1 = normal completion, */ + nn, /* number of secs ? */ + track, /* track */ + sector, /* sector */ + addr_l, /* low (transfer address) */ + addr_h; /* high (transfer address) */ +} desc_t; + +static desc_t mydesc; + +enum {NONE, WRITE_SEC, READ_SEC, FMT_TRACK}; + +#define SEC_SZ 128 +#define SPT 26 +#define UMASK 0xf + +static uint8 blanksec[SEC_SZ]; +/* 0 1 2 3 4 5 6 7 8 9 a b c d e f */ +static const uint8 utrans[] = {0,1,2,0,3,0,0,0,4,0,0,0,0,0,0,0}; + +/************************************************** + + Translate an IMSAI FIF disk request into an access into the harddrive file + +*/ +static int DoDiskOperation(desc_t *dsc, uint8 val) +{ + int32 current_disk_flags; + int kt, + addr; + FILE *cpx; + UNIT *uptr; + +#if 0 + printf("%02x %02x %02x %02x %02x %02x %02x %02x \n", + val, + dsc->cmd_unit, + dsc->result, + dsc->nn, + dsc->track, + dsc->sector, + dsc->addr_l, + dsc->addr_h); +#endif + + current_disk = (utrans[dsc->cmd_unit & UMASK]) - 1; /* 0 <= current_disk < NUM_OF_DSK */ + if (current_disk >= NUM_OF_DSK) { + if (hasVerbose() && (warnDSK11 < warnLevelDSK)) { + warnDSK11++; +/*03*/ MESSAGE_2("Attempt disk io on illegal disk %d - ignored.", current_disk + 1); + } + return 0; /* no drive selected - can do nothing */ + } + current_disk_flags = (fif_dev.units + current_disk) -> flags; + if ((current_disk_flags & UNIT_ATT) == 0) { /* nothing attached? */ + if ( (current_disk_flags & UNIT_DSK_VERBOSE) && (warnAttached[current_disk] < warnLevelDSK) ) { + warnAttached[current_disk]++; +/*02*/MESSAGE_2("Attempt to select unattached DSK%d - ignored.", current_disk); + } + current_disk = NUM_OF_DSK; + return 2; + } + + uptr = fif_dev.units + current_disk; + cpx = uptr->fileref; + + /* decode request: */ + switch (dsc->cmd_unit >> 4) { + case FMT_TRACK: + /*printf("%c", dsc->track % 10 ? '*' : '0' + + dsc->track / 10); */ + /*Sleep(250); */ + memset(blanksec, 0, SEC_SZ); + addr = dsc->track * SPT; + fseek(cpx, addr * SEC_SZ, SEEK_SET); + + /* write a track worth of sectors */ + for (kt=0; kt < SPT; kt++) { + fwrite(blanksec, 1, sizeof(blanksec), cpx); + } + break; + + case READ_SEC: + addr = (dsc->track * SPT) + dsc->sector - 1; + fseek(cpx, addr * SEC_SZ, SEEK_SET); + fread(blanksec, 1, SEC_SZ, cpx); + addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */ + for (kt = 0; kt < SEC_SZ; kt++) { + PutBYTEWrapper(addr++, blanksec[kt]); + } + break; + + case WRITE_SEC: + addr = (dsc->track * SPT) + dsc->sector - 1; + fseek(cpx, addr * SEC_SZ, SEEK_SET); + addr = dsc->addr_l + (dsc->addr_h << 8); /* no assumption on endianness */ + for (kt = 0; kt < SEC_SZ; kt++) { + blanksec[kt] = GetBYTEWrapper(addr++); + } + fwrite(blanksec, 1, SEC_SZ, cpx); + break; + + default: + ; + } + return 1; +} + +/********************************************************************** + + Copy the disk descriptor from target RAM + +*/ +static void getdesc(uint16 addr) { + int32 x; + uint8 *p1 = (uint8*)&mydesc; + + for (x = 0; x < sizeof(mydesc); x++) { + *p1++ = GetBYTEWrapper(addr++); + } +} + +/********************************************************************** + + handle the IMSAI FIF floppy controller + +*/ +static int32 fif_io(const int32 port, const int32 io, const int32 data) { + + static int32 fdstate = 0; /* chan 0xfd state */ + static int32 desc; + static uint16 fdAdr[16]; /* disk descriptor address in 8080/z80 RAM */ + + /* cmd | desc# */ + /* cmd == 0x00 do operation */ + /* cmd == 0x10 next 2 transfers are desc address */ + /* desc# is one of 16 0x0 - 0xf */ + + if (!io) { + return 0; + } + + switch (fdstate) { + case 0: + desc = data & 0xf; + if ((data & 0x10) != 0) { /* prefix 0x10 */ + fdstate++; /* means desc address is next 2 out (fd),a */ + } + else { /* do what descriptor says */ + getdesc(fdAdr[desc]); + PutBYTEWrapper(fdAdr[desc] + 1, + (uint8)DoDiskOperation(&mydesc, (uint8)data)); + } + break; + + case 1: + /*printf("D1 %02x %02x\n", desc, data); */ + fdAdr[desc] = data; /* LSB of descriptor address */ + fdstate++; + break; + + case 2: + /*printf("D2 %02x %02x\n", desc, data); */ + fdAdr[desc] |= data << 8; /* MSB of descriptor address */ + fdstate = 0; + break; + } + return 0; +} + +#define ERNIES_FTP 0 +#if ERNIES_FTP + +#define WRK_BUF_SZ 150 +#define FCB_SIZE 32 +#define NAME_LTH 8 +#define EXT_LTH 3 + + +/************************************************** +*/ +static void xfero(int32 addr, char *src, int32 lth) +{ + while (lth--) { + PutBYTEWrapper(addr++, *src++); + } +} + +/************************************************** +*/ +static void xferi(int32 addr, char *dst, int32 lth) +{ + while (lth--) { + *dst++ = GetBYTEWrapper(addr++); + } +} + +#if !defined (_WIN32) +static void strupr(char *fn) { /* psco added */ + while (*fn) { + if (('a' <= *fn) && (*fn <= 'z')) *fn -= 'a' - 'A'; + fn++; + } +} +#endif + +/************************************************** +*/ +static void initfcb(char *fcb, char *fn, int32 flg) +{ + char *p1 = fcb; + + if (flg) + { + strupr(fn); + } + memset (fcb, 0 , FCB_SIZE); + memset (fcb + 1, ' ', NAME_LTH + EXT_LTH); + p1++; + while (*fn && (*fn != '.')) + { + *p1++ = *fn++; + } + if (*fn == '.') + { + fn++; + } + p1 = fcb + NAME_LTH + 1; + while (*fn && (*fn != '.')) + { + *p1++ = *fn++; + } +} + +/************************************************** + + FTP interface - most of the work is done here + The IMDOS/CPM application only does minimal work + +*/ + +char message[WRK_BUF_SZ]; +char temp [WRK_BUF_SZ]; +FILE * myfile; + +uint8 FTP(int32 BC, int32 DE) +{ + char *p1, *p2; + int32 retval; + + xferi(DE, temp, SEC_SZ); + p1 = temp; + switch (BC & 0x7f) + { + case 0: + memcpy(message, p1 + 2, *(p1 + 1)); + *(message + *(p1 + 1)) = 0; + p2 = strtok(message, " \t"); + if (!strcmp(p2, "get")) + { + p2 = strtok(NULL, " \t"); + if (myfile = fopen(p2, "rb")) + { + initfcb(temp, p2, 1); + xfero(DE + 2, temp, 32); + retval = 0; + break; + } + } + if (!strcmp(p2, "era")) + { + p2 = strtok(NULL, " \t"); + initfcb(temp, p2, 0); + xfero(DE + 2, temp, 32); + retval = 1; + break; + } + retval = 0xff; + break; + + case 20: + memset(temp, 0x1a, SEC_SZ); + retval = fread(temp, 1, SEC_SZ, myfile) ? 0 : 1; + xfero( DE, temp, SEC_SZ); + if (retval) + { + fclose(myfile); + } + break; + } + return retval; +} + +#endif /* ERNIES_FTP */ + +/* end of the source */ + + + diff --git a/AltairZ80/s100_mdriveh.c b/AltairZ80/s100_mdriveh.c new file mode 100644 index 0000000..8f29e43 --- /dev/null +++ b/AltairZ80/s100_mdriveh.c @@ -0,0 +1,260 @@ +/************************************************************************* + * * + * $Id: s100_mdriveh.c 1773 2008-01-11 05:46:19Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * CompuPro M-DRIVE/H Controller module for SIMH. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define VERBOSE_MSG 0x80 + +#define MDRIVEH_MAX_DRIVES 8 + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint32 dma_addr; /* DMA Transfer Address */ + UNIT uptr[MDRIVEH_MAX_DRIVES]; + uint8 *storage[MDRIVEH_MAX_DRIVES]; +} MDRIVEH_INFO; + +static MDRIVEH_INFO mdriveh_info_data = { { 0x0, 0, 0xC6, 2 } }; +static MDRIVEH_INFO *mdriveh_info = &mdriveh_info_data; + +extern uint32 PCX; +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +extern REG *sim_PC; + +#define UNIT_V_MDRIVEH_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_MDRIVEH_WLK (1 << UNIT_V_MDRIVEH_WLK) +#define UNIT_V_MDRIVEH_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_MDRIVEH_VERBOSE (1 << UNIT_V_MDRIVEH_VERBOSE) +#define MDRIVEH_CAPACITY (512 * 1000) /* Default M-DRIVE/H Capacity */ +#define MDRIVEH_NONE 0 + +static t_stat mdriveh_reset(DEVICE *mdriveh_dev); +static int32 mdrivehdev(const int32 port, const int32 io, const int32 data); +static uint8 MDRIVEH_Read(const uint32 Addr); +static uint8 MDRIVEH_Write(const uint32 Addr, uint8 cData); + +static int32 trace_level = 0; /* Disable all tracing by default */ + +static UNIT mdriveh_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ROABLE, MDRIVEH_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_DIS + UNIT_ROABLE, MDRIVEH_CAPACITY) } +}; + +static REG mdriveh_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { NULL } +}; + +static MTAB mdriveh_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_MDRIVEH_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_MDRIVEH_WLK, UNIT_MDRIVEH_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_MDRIVEH_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_MDRIVEH_VERBOSE, UNIT_MDRIVEH_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE mdriveh_dev = { + "MDRIVEH", mdriveh_unit, mdriveh_reg, mdriveh_mod, + MDRIVEH_MAX_DRIVES, 10, 31, 1, MDRIVEH_MAX_DRIVES, MDRIVEH_MAX_DRIVES, + NULL, NULL, &mdriveh_reset, + NULL, NULL, NULL, + &mdriveh_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + + +/* Reset routine */ +static t_stat mdriveh_reset(DEVICE *dptr) +{ + uint8 i; + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect ROM and I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &mdrivehdev, TRUE); + } else { + /* Connect MDRIVEH at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &mdrivehdev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + + for(i=0; iuptr[i] = dptr->units[i]; + if((dptr->flags & DEV_DIS) || (dptr->units[i].flags & UNIT_DIS)) { + if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) + printf("MDRIVEH: Unit %d disabled", i); + if(mdriveh_info->storage[i] != NULL) { + if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) + printf(", freed 0x%p\n", mdriveh_info->storage[i]); + free(mdriveh_info->storage[i]); + mdriveh_info->storage[i] = NULL; + } else if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) { + printf(".\n"); + } + } else { + if(mdriveh_info->storage[i] == NULL) { + mdriveh_info->storage[i] = calloc(1, 524288); + } + if (dptr->units[i].flags & UNIT_MDRIVEH_VERBOSE) + printf("MDRIVEH: Unit %d enabled, 512K at 0x%p\n", i, mdriveh_info->storage[i]); + } + } + + return SCPE_OK; +} + +static int32 mdrivehdev(const int32 port, const int32 io, const int32 data) +{ + DBG_PRINT(("MDRIVEH: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); + if(io) { + MDRIVEH_Write(port, data); + return 0; + } else { + return(MDRIVEH_Read(port)); + } +} + +#define MDRIVEH_DATA 0 /* R=Drive Status Register / W=DMA Address Register */ +#define MDRIVEH_ADDR 1 /* R=Unused / W=Motor Control Register */ + +static uint8 MDRIVEH_Read(const uint32 Addr) +{ + uint8 cData; + uint8 unit; + uint32 offset; + + cData = 0xFF; /* default is High-Z Data */ + + switch(Addr & 0x1) { + case MDRIVEH_ADDR: + TRACE_PRINT(VERBOSE_MSG, ("MDRIVEH: " ADDRESS_FORMAT " RD Addr = 0x%02x" NLP, + PCX, cData)); + break; + case MDRIVEH_DATA: + unit = (mdriveh_info->dma_addr & 0x380000) >> 19; + offset = mdriveh_info->dma_addr & 0x7FFFF; + + if(mdriveh_info->storage[unit] != NULL) { + cData = mdriveh_info->storage[unit][offset]; + } + + TRACE_PRINT(RD_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " RD Data [%x:%05x] = 0x%02x" NLP, + PCX, unit, offset, cData)); + + /* Increment M-DRIVE/H Data Address */ + mdriveh_info->dma_addr++; + mdriveh_info->dma_addr &= 0x3FFFFF; + break; + } + + return (cData); +} + +static uint8 MDRIVEH_Write(const uint32 Addr, uint8 cData) +{ + uint8 result = 0; + uint8 unit; + uint32 offset; + + switch(Addr & 0x1) { + case MDRIVEH_ADDR: + mdriveh_info->dma_addr <<= 8; + mdriveh_info->dma_addr &= 0x003FFF00; + mdriveh_info->dma_addr |= cData; + TRACE_PRINT(SEEK_MSG, ("MDRIVEH: " ADDRESS_FORMAT " DMA Address=%06x" NLP, + PCX, mdriveh_info->dma_addr)); + break; + case MDRIVEH_DATA: + unit = (mdriveh_info->dma_addr & 0x380000) >> 19; + offset = mdriveh_info->dma_addr & 0x7FFFF; + + if(mdriveh_info->storage[unit] != NULL) { + if(mdriveh_info->uptr[unit].flags & UNIT_MDRIVEH_WLK) { + TRACE_PRINT(WR_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = Unit Write Locked" NLP, + PCX, unit, offset)); + } else { + TRACE_PRINT(WR_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = 0x%02x" NLP, + PCX, unit, offset, cData)); + mdriveh_info->storage[unit][offset] = cData; + } + } else { + TRACE_PRINT(WR_DATA_MSG, ("MDRIVEH: " ADDRESS_FORMAT " WR Data [%x:%05x] = Unit OFFLINE" NLP, + PCX, unit, offset)); + } + + /* Increment M-DRIVE/H Data Address */ + mdriveh_info->dma_addr++; + mdriveh_info->dma_addr &= 0x3FFFFF; + break; + } + + return (result); +} + diff --git a/AltairZ80/s100_mdsad.c b/AltairZ80/s100_mdsad.c new file mode 100644 index 0000000..db33491 --- /dev/null +++ b/AltairZ80/s100_mdsad.c @@ -0,0 +1,802 @@ +/************************************************************************* + * * + * $Id: s100_mdsad.c 1773 2008-01-11 05:46:19Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Northstar MDS-AD Disk Controller module for SIMH * + * Only Double-Density is supported for now. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG*/ +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#include "sim_imd.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define ORDERS_MSG 0x02 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 +#define RD_DATA_DETAIL_MSG 0x40 +#define WR_DATA_DETAIL_MSG 0x80 + +extern uint32 PCX; +extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern REG *sim_PC; + +#define MDSAD_MAX_DRIVES 4 +#define MDSAD_SECTOR_LEN 512 +#define MDSAD_SECTORS_PER_TRACK 10 +#define MDSAD_TRACKS 35 +#define MDSAD_RAW_LEN (32 + 2 + MDSAD_SECTOR_LEN + 1) + +typedef union { + struct { + uint8 zeros[32]; + uint8 sync[2]; + uint8 data[MDSAD_SECTOR_LEN]; + uint8 checksum; + } u; + uint8 raw[MDSAD_RAW_LEN]; + +} SECTOR_FORMAT; + +typedef struct { + UNIT *uptr; + DISK_INFO *imd; + uint8 track; + uint8 wp; /* Disk write protected */ + uint8 sector; /* Current Sector number */ + uint32 sector_wait_count; +} MDSAD_DRIVE_INFO; + +typedef struct { + uint8 dd; /* Controls density on write DD=1 for double density and DD=0 for single density. */ + uint8 ss; /* Specifies the side of a double-sided diskette. The bottom side (and only side of a single-sided diskette) is selected when SS=0. The second (top) side is selected when SS=1. */ + uint8 dp; /* has shared use. During stepping operations, DP=O specifies a step out and DP=1 specifies a step in. During write operations, write procompensation is invoked if and only if DP=1. */ + uint8 st; /* controls the level of the head step signal to the disk drives. */ + uint8 ds; /* is the drive select field, encoded as follows: */ + /* 0=no drive selected + * 1=drive 1 selected + * 2=drive 2 selected + * 4=drive 3 selected + * 8=drive 4 selected + */ +} ORDERS; + +typedef struct { + uint8 sf; /* Sector Flag: set when sector hole detected, reset by software. */ + uint8 ix; /* Index Detect: true if index hole detected during previous sector. */ + uint8 dd; /* Double Density Indicator: true if data being read is encoded in double density. */ + uint8 mo; /* Motor On: true while motor(s) are on. */ +} COM_STATUS; + +typedef struct { + uint8 wi; /* Window: true during 96-microsecond window at beginning of sector. */ + uint8 re; /* Read Enable: true while phase-locked loop is enabled. */ + uint8 sp; /* Spare: reserved for future use. */ + uint8 bd; /* Body: set when sync character is detected. */ +} A_STATUS; + +typedef struct { + uint8 wr; /* Write: true during valid write operation. */ + uint8 sp; /* Spare: reserved for future use. */ + uint8 wp; /* Write Protect: true while the diskette installed in the selected drive is write protected. */ + uint8 t0; /* Track 0: true if selected drive is at track zero. */ +} B_STATUS; + +typedef struct { + uint8 sc; /* Sector Counter: indicates the current sector position. */ +} C_STATUS; + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + + ORDERS orders; + COM_STATUS com_status; + A_STATUS a_status; + B_STATUS b_status; + C_STATUS c_status; + + uint8 int_enable; /* Interrupt Enable */ + uint32 datacount; /* Number of data bytes transferred from controller for current sector */ + MDSAD_DRIVE_INFO drive[MDSAD_MAX_DRIVES]; +} MDSAD_INFO; + +static MDSAD_INFO mdsad_info_data = { { 0xE800, 1024, 0, 0 } }; +static MDSAD_INFO *mdsad_info = &mdsad_info_data; + +static SECTOR_FORMAT sdata; + +#define UNIT_V_MDSAD_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_MDSAD_WLK (1 << UNIT_V_MDSAD_WLK) +#define UNIT_V_MDSAD_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_MDSAD_VERBOSE (1 << UNIT_V_MDSAD_VERBOSE) +#define MDSAD_CAPACITY (70*10*MDSAD_SECTOR_LEN) /* Default North Star Disk Capacity */ +#define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ +#define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ +#define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ + +/* MDS-AD Controller Subcases */ +#define MDSAD_READ_ROM 0 +#define MDSAD_WRITE_DATA 1 +#define MDSAD_CTLR_ORDERS 2 +#define MDSAD_CTLR_COMMAND 3 + +/* MDS-AD Controller Commands */ +#define MDSAD_CMD_NOP 0 +#define MDSAD_CMD_RESET_SF 1 +#define MDSAD_CMD_INTR_DIS 2 +#define MDSAD_CMD_INTR_ARM 3 +#define MDSAD_CMD_SET_BODY 4 +#define MDSAD_CMD_MOTORS_ON 5 +#define MDSAD_CMD_BEGIN_WR 6 +#define MDSAD_CMD_RESET 7 + +/* MDS-AD Data returned on DI bus */ +#define MDSAD_A_STATUS 1 +#define MDSAD_B_STATUS 2 +#define MDSAD_C_STATUS 3 +#define MDSAD_READ_DATA 4 + +/* MDS-AD status byte masks */ +/* A-Status */ +#define MDSAD_A_SF 0x80 +#define MDSAD_A_IX 0x40 +#define MDSAD_A_DD 0x20 +#define MDSAD_A_MO 0x10 +#define MDSAD_A_WI 0x08 +#define MDSAD_A_RE 0x04 +#define MDSAD_A_SP 0x02 +#define MDSAD_A_BD 0x01 + +/* B-Status */ +#define MDSAD_B_SF 0x80 +#define MDSAD_B_IX 0x40 +#define MDSAD_B_DD 0x20 +#define MDSAD_B_MO 0x10 +#define MDSAD_B_WR 0x08 +#define MDSAD_B_SP 0x04 +#define MDSAD_B_WP 0x02 +#define MDSAD_B_T0 0x01 + +/* C-Status */ +#define MDSAD_C_SF 0x80 +#define MDSAD_C_IX 0x40 +#define MDSAD_C_DD 0x20 +#define MDSAD_C_MO 0x10 +#define MDSAD_C_SC 0x0f + +/* Local function prototypes */ +static t_stat mdsad_reset(DEVICE *mdsad_dev); +static t_stat mdsad_attach(UNIT *uptr, char *cptr); +static t_stat mdsad_detach(UNIT *uptr); +static t_stat mdsad_boot(int32 unitno, DEVICE *dptr); +static uint8 MDSAD_Read(const uint32 Addr); + +static int32 mdsaddev(const int32 Addr, const int32 rw, const int32 data); + +static int32 trace_level = 0; + +static UNIT mdsad_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, MDSAD_CAPACITY) } +}; + +static REG mdsad_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { NULL } +}; + +static MTAB mdsad_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, + { UNIT_MDSAD_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_MDSAD_WLK, UNIT_MDSAD_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_MDSAD_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_MDSAD_VERBOSE, UNIT_MDSAD_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE mdsad_dev = { + "MDSAD", mdsad_unit, mdsad_reg, mdsad_mod, + MDSAD_MAX_DRIVES, 10, 31, 1, MDSAD_MAX_DRIVES, MDSAD_MAX_DRIVES, + NULL, NULL, &mdsad_reset, + &mdsad_boot, &mdsad_attach, &mdsad_detach, + &mdsad_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +t_stat mdsad_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { + sim_map_resource(pnp->mem_base, pnp->mem_size, + RESOURCE_TYPE_MEMORY, &mdsaddev, TRUE); + } else { + /* Connect MDSAD at base address */ + if(sim_map_resource(pnp->mem_base, pnp->mem_size, + RESOURCE_TYPE_MEMORY, &mdsaddev, FALSE) != 0) { + printf("%s: error mapping resource at 0x%04x\n", + __FUNCTION__, pnp->mem_base); + dptr->flags |= DEV_DIS; + return SCPE_ARG; + } + } + return SCPE_OK; +} + +/* Attach routine */ +t_stat mdsad_attach(UNIT *uptr, char *cptr) +{ + char header[4]; + t_stat r; + unsigned int i = 0; + + r = attach_unit(uptr, cptr); /* attach unit */ + if(r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + if(sim_fsize(uptr->fileref) != 0) { + uptr->capac = sim_fsize(uptr->fileref); + } else { + uptr->capac = MDSAD_CAPACITY; + } + + for(i = 0; i < MDSAD_MAX_DRIVES; i++) { + mdsad_info->drive[i].uptr = &mdsad_dev.units[i]; + } + + for(i = 0; i < MDSAD_MAX_DRIVES; i++) { + if(mdsad_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + /* Default for new file is DSK */ + uptr->u3 = IMAGE_TYPE_DSK; + + if(uptr->capac > 0) { + fgets(header, 4, uptr->fileref); + if(!strcmp(header, "IMD")) { + uptr->u3 = IMAGE_TYPE_IMD; + } else if(!strcmp(header, "CPT")) { + printf("CPT images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_CPT; + mdsad_detach(uptr); + return SCPE_OPENERR; + } else { + uptr->u3 = IMAGE_TYPE_DSK; + } + } + + if (uptr->flags & UNIT_MDSAD_VERBOSE) + printf("MDSAD%d, attached to '%s', type=%s, len=%d\n", i, cptr, + uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + + if(uptr->u3 == IMAGE_TYPE_IMD) { + if(uptr->capac < 318000) { + printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); + mdsad_detach(uptr); + return SCPE_OPENERR; + } + + if (uptr->flags & UNIT_MDSAD_VERBOSE) + printf("--------------------------------------------------------\n"); + mdsad_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_MDSAD_VERBOSE)); + if (uptr->flags & UNIT_MDSAD_VERBOSE) + printf("\n"); + } else { + mdsad_info->drive[i].imd = NULL; + } + + return SCPE_OK; +} + + +/* Detach routine */ +t_stat mdsad_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + for(i = 0; i < MDSAD_MAX_DRIVES; i++) { + if(mdsad_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + if (i >= MDSAD_MAX_DRIVES) return SCPE_ARG; + + DBG_PRINT(("Detach MDSAD%d\n", i)); + diskClose(mdsad_info->drive[i].imd); + + r = detach_unit(uptr); /* detach unit */ + if(r != SCPE_OK) + return r; + + mdsad_dev.units[i].fileref = NULL; /* psco check if ok */ + return SCPE_OK; +} + +static t_stat mdsad_boot(int32 unitno, DEVICE *dptr) +{ + + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + DBG_PRINT(("Booting MDSAD Controller at 0x%04x, unit %d" NLP, + pnp->mem_base+1+(unitno&3), unitno & 3)); + + /* Unit 3 can't be booted yet. This involves modifying the A register. */ + *((int32 *) sim_PC->loc) = pnp->mem_base+1+(unitno&3); + return SCPE_OK; +} + +static int32 mdsaddev(const int32 Addr, const int32 rw, const int32 data) +{ + if(rw == 0) { /* Read */ + return(MDSAD_Read(Addr)); + } else { /* Write */ + DBG_PRINT(("MDSAD: write attempt at 0x%04x ignored." NLP, Addr)); + return (-1); + } +} + +/* This ROM image is taken from the Solace Emulator, which uses */ +/* a ROM from a "Micro Complex Phase Lock II" dual- */ +/* density controller card. It is supposedly compatible with the */ +/* Northstar-designed dual density floppy controller. It has the */ +/* interesting property that by jumping to base_addr+0 (or +1) and */ +/* it boots from floppy 0; jump to base_addr+2 you boot from floppy 1; */ +/* jump to base_addr+3 and you boot from floppy 2. You can boot from */ +/* floppy 3 by loading A with 08H and jumping to base_addr+7. */ +static uint8 mdsad_rom[] = { + 0x44, 0x01, 0x01, 0x01, 0x82, 0x84, 0x78, 0xE6, 0x07, 0x4F, 0x00, 0x31, 0x30, 0x00, 0x21, 0x29, /* 0x00 */ + 0x00, 0xE5, 0x21, 0x2C, 0xC2, 0xE5, 0x21, 0x77, 0x13, 0xE5, 0x21, 0xC9, 0x1A, 0xE5, 0xCD, 0x28, /* 0x10 */ + 0x00, 0x21, 0x30, 0x00, 0x5B, 0x52, 0x44, 0x54, 0x5D, 0x3A, 0x27, 0x00, 0x57, 0xC3, 0x29, 0x00, /* 0x20 */ + 0x14, 0x14, 0x1E, 0x15, 0x1A, 0x26, 0x30, 0xCD, 0xD9, 0x00, 0x42, 0x05, 0x0A, 0xCD, 0xD4, 0x00, /* 0x30 */ + 0x2E, 0x0D, 0x2D, 0xCA, 0x43, 0x00, 0xCD, 0xD7, 0x00, 0x1A, 0xE6, 0x40, 0xCA, 0x42, 0x00, 0x3E, /* 0x40 */ + 0x0A, 0xF5, 0xCD, 0xC1, 0x00, 0x1E, 0x20, 0x1A, 0xE6, 0x01, 0xC2, 0x63, 0x00, 0xCD, 0xC5, 0x00, /* 0x50 */ + 0xC3, 0x55, 0x00, 0x2E, 0x04, 0xCD, 0xE7, 0x00, 0x1E, 0x10, 0x1A, 0xE6, 0x04, 0xCA, 0x68, 0x00, /* 0x60 */ + 0x3E, 0x09, 0x3D, 0xC2, 0x72, 0x00, 0x1A, 0xE6, 0x20, 0xC2, 0x84, 0x00, 0xCD, 0xC1, 0x00, 0x2E, /* 0x70 */ + 0x08, 0xCD, 0xE7, 0x00, 0x06, 0xA3, 0x1E, 0x10, 0x05, 0xCA, 0xF4, 0x00, 0x1A, 0x0F, 0xD2, 0x88, /* 0x80 */ + 0x00, 0x1E, 0x40, 0x1A, 0x67, 0x2E, 0x00, 0x36, 0x59, 0x07, 0x47, 0x23, 0x1A, 0x77, 0xA8, 0x07, /* 0x90 */ + 0x47, 0x2C, 0xC2, 0x9C, 0x00, 0x24, 0x1A, 0x77, 0xA8, 0x07, 0x47, 0x2C, 0xC2, 0xA6, 0x00, 0x1A, /* 0xA0 */ + 0xA8, 0xC2, 0xF4, 0x00, 0x25, 0x2E, 0x03, 0x71, 0x2D, 0x36, 0x59, 0xC2, 0xB8, 0x00, 0x2E, 0x0A, /* 0xB0 */ + 0xE9, 0x3E, 0x20, 0x81, 0x4F, 0x0A, 0x3E, 0x10, 0x81, 0x4F, 0x0A, 0x3E, 0xF0, 0x81, 0x4F, 0x0A, /* 0xC0 */ + 0x79, 0xE6, 0x0F, 0x4F, 0xCD, 0xD7, 0x00, 0x26, 0x01, 0x1E, 0x11, 0x1A, 0x1D, 0x1A, 0xB7, 0xF2, /* 0xD0 */ + 0xDD, 0x00, 0x25, 0xC2, 0xD9, 0x00, 0xC9, 0xCD, 0xD7, 0x00, 0x1E, 0x35, 0x1A, 0xE6, 0x0F, 0xBD, /* 0xE0 */ + 0xC2, 0xE7, 0x00, 0xC9, 0xF1, 0x3D, 0xF5, 0xC2, 0x55, 0x00, 0xC3, 0xFA, 0x00, 0x52, 0x44, 0x54 /* 0xF0 */ +}; + +static void showdata(int32 isRead) { + int32 i; + printf("MDSAD: " ADDRESS_FORMAT " %s Sector =" NLP "\t", PCX, isRead ? "Read" : "Write"); + for(i=0; i < MDSAD_SECTOR_LEN; i++) { + printf("%02X ", sdata.u.data[i]); + if(((i+1) & 0xf) == 0) printf(NLP "\t"); + } + printf(NLP); +} + +static int checksum; +static uint32 sec_offset; + +static uint8 MDSAD_Read(const uint32 Addr) +{ + uint8 cData; + uint8 ds; + MDSAD_DRIVE_INFO *pDrive; + + cData = 0x00; + + pDrive = &mdsad_info->drive[mdsad_info->orders.ds]; + + switch( (Addr & 0x300) >> 8 ) { + case MDSAD_READ_ROM: + cData = mdsad_rom[Addr & 0xFF]; + break; + case MDSAD_WRITE_DATA: + { + unsigned int flags = 0; + unsigned int writelen; + + if(mdsad_info->datacount == 0) { + TRACE_PRINT(WR_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT + " WRITE Start: Drive: %d, Track=%d, Head=%d, Sector=%d" NLP, + PCX, + mdsad_info->orders.ds, + pDrive->track, + mdsad_info->orders.ss, + pDrive->sector)); + + sec_offset = (pDrive->track * (MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK)) + + (mdsad_info->orders.ss * ((MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK) * MDSAD_TRACKS)) + + (pDrive->sector * MDSAD_SECTOR_LEN); + + } + + DBG_PRINT(("MDSAD: " ADDRESS_FORMAT + " WRITE-DATA[offset:%06x+%03x]=%02x" NLP, + PCX, sec_offset, mdsad_info->datacount, Addr & 0xFF)); + mdsad_info->datacount++; + if(mdsad_info->datacount < MDSAD_RAW_LEN) + sdata.raw[mdsad_info->datacount] = Addr & 0xFF; + + if(mdsad_info->datacount == (MDSAD_RAW_LEN - 1)) { + TRACE_PRINT(WR_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT + " Write Complete" NLP, PCX)); + + if ((pDrive->uptr == NULL) || (pDrive->uptr->fileref == NULL)) { + TRACE_PRINT(WR_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT + " Drive: %d not attached - write ignored." NLP, + PCX, mdsad_info->orders.ds)); + return 0x00; + } + if(trace_level & WR_DATA_DETAIL_MSG) showdata(FALSE); + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } + sectWrite(pDrive->imd, + pDrive->track, + mdsad_info->orders.ss, + pDrive->sector, + sdata.u.data, + MDSAD_SECTOR_LEN, + &flags, + &writelen); + break; + case IMAGE_TYPE_DSK: + if(pDrive->uptr->fileref == NULL) { + printf(".fileref is NULL!" NLP); + } else { + fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); + fwrite(sdata.u.data, MDSAD_SECTOR_LEN, 1, + (pDrive->uptr)->fileref); + } + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" NLP, __FUNCTION__); + break; + } + } + break; + } + case MDSAD_CTLR_ORDERS: + mdsad_info->orders.dd = (Addr & 0x80) >> 7; + mdsad_info->orders.ss = (Addr & 0x40) >> 6; + mdsad_info->orders.dp = (Addr & 0x20) >> 5; + mdsad_info->orders.st = (Addr & 0x10) >> 4; + mdsad_info->orders.ds = (Addr & 0x0F); + + ds = mdsad_info->orders.ds; + switch(mdsad_info->orders.ds) { + case 0: + case 1: + mdsad_info->orders.ds = 0; + break; + case 2: + mdsad_info->orders.ds = 1; + break; + case 4: + mdsad_info->orders.ds = 2; + break; + case 8: + mdsad_info->orders.ds = 3; + break; + } + + TRACE_PRINT(ORDERS_MSG, ("MDSAD: " ADDRESS_FORMAT + " Controller Orders: Drive=%x[%x], DD=%d, SS=%d, DP=%d, ST=%d" NLP, + PCX, + mdsad_info->orders.ds, ds, + mdsad_info->orders.dd, + mdsad_info->orders.ss, + mdsad_info->orders.dp, + mdsad_info->orders.st)); + + /* use latest selected drive */ + pDrive = &mdsad_info->drive[mdsad_info->orders.ds]; + + if(mdsad_info->orders.st == 1) { + if(mdsad_info->orders.dp == 0) { + TRACE_PRINT(SEEK_MSG, ("MDSAD: " ADDRESS_FORMAT + " Step out: Track=%d%s" NLP, PCX, pDrive->track, + pDrive->track == 0 ? "[Warn: already at 0]" : "")); + if(pDrive->track > 0) /* anything to do? */ + pDrive->track--; + } else { + TRACE_PRINT(SEEK_MSG, ("MDSAD: " ADDRESS_FORMAT + " Step in: Track=%d%s" NLP, PCX, pDrive->track, + pDrive->track == (MDSAD_TRACKS - 1) ? + "[Warn: already at highest track]" : "")); + if(pDrive->track < (MDSAD_TRACKS - 1)) /* anything to do? */ + pDrive->track++; + } + } + /* always update t0 */ + mdsad_info->b_status.t0 = (pDrive->track == 0); + break; + case MDSAD_CTLR_COMMAND: +/* TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT " DM=%x" NLP, PCX, (Addr & 0xF0) >> 4)); */ + switch(Addr & 0x0F) { + case MDSAD_CMD_MOTORS_ON: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " CMD=Motors On" NLP, PCX)); + mdsad_info->com_status.mo = 1; /* Turn motors on */ + break; + + case MDSAD_CMD_NOP: + pDrive->sector_wait_count++; + switch(pDrive->sector_wait_count) { + case 10: + { + mdsad_info->com_status.sf = 1; + mdsad_info->a_status.wi = 0; + mdsad_info->a_status.re = 0; + mdsad_info->a_status.bd = 0; + pDrive->sector_wait_count = 0; + pDrive->sector++; + if(pDrive->sector >= MDSAD_SECTORS_PER_TRACK) { + pDrive->sector = 0; + mdsad_info->com_status.ix = 1; + } else { + mdsad_info->com_status.ix = 0; + } + break; + } + case 2: + mdsad_info->a_status.wi = 1; + break; + case 3: + mdsad_info->a_status.re = 1; + mdsad_info->a_status.bd = 1; + break; + default: + break; + } + break; + case MDSAD_CMD_RESET_SF: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " CMD=Reset Sector Flag" NLP, PCX)); + mdsad_info->com_status.sf = 0; + mdsad_info->datacount = 0; + break; + case MDSAD_CMD_INTR_DIS: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " CMD=Disarm Interrupt" NLP, PCX)); + mdsad_info->int_enable = 0; + break; + case MDSAD_CMD_INTR_ARM: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " CMD=Arm Interrupt" NLP, PCX)); + mdsad_info->int_enable = 1; + break; + case MDSAD_CMD_SET_BODY: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " CMD=Set Body (Diagnostic)" NLP, PCX)); + break; + case MDSAD_CMD_BEGIN_WR: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " CMD=Begin Write" NLP, PCX)); + break; + case MDSAD_CMD_RESET: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " CMD=Reset Controller" NLP, PCX)); + mdsad_info->com_status.mo = 0; /* Turn motors off */ + break; + default: + TRACE_PRINT(CMD_MSG, ("MDSAD: " ADDRESS_FORMAT + " Unsupported CMD=0x%x" NLP, PCX, Addr & 0x0F)); + break; + } + + /* Always Double-Density for now... */ + mdsad_info->com_status.dd = 1; + + cData = (mdsad_info->com_status.sf & 1) << 7; + cData |= (mdsad_info->com_status.ix & 1) << 6; + cData |= (mdsad_info->com_status.dd & 1) << 5; + cData |= (mdsad_info->com_status.mo & 1) << 4; + + mdsad_info->c_status.sc = pDrive->sector; + + switch( (Addr & 0xF0) >> 4) { + case MDSAD_A_STATUS: /* A-STATUS */ + cData |= (mdsad_info->a_status.wi & 1) << 3; + cData |= (mdsad_info->a_status.re & 1) << 2; + cData |= (mdsad_info->a_status.sp & 1) << 1; + cData |= (mdsad_info->a_status.bd & 1); + TRACE_PRINT(STATUS_MSG, ("MDSAD: " ADDRESS_FORMAT + " A-Status = <%s %s %s %s %s %s %s %s>" NLP, PCX, + cData & MDSAD_A_SF ? "SF" : " ", + cData & MDSAD_A_IX ? "IX" : " ", + cData & MDSAD_A_DD ? "DD" : " ", + cData & MDSAD_A_MO ? "MO" : " ", + cData & MDSAD_A_WI ? "WI" : " ", + cData & MDSAD_A_RE ? "RE" : " ", + cData & MDSAD_A_SP ? "SP" : " ", + cData & MDSAD_A_BD ? "BD" : " ")); + break; + case MDSAD_B_STATUS: /* B-STATUS */ + cData |= (mdsad_info->b_status.wr & 1) << 3; + cData |= (mdsad_info->b_status.sp & 1) << 2; + cData |= (mdsad_info->b_status.wp & 1) << 1; + cData |= (mdsad_info->b_status.t0 & 1); + TRACE_PRINT(STATUS_MSG, ("MDSAD: " ADDRESS_FORMAT + " B-Status = <%s %s %s %s %s %s %s %s>" NLP, PCX, + cData & MDSAD_B_SF ? "SF" : " ", + cData & MDSAD_B_IX ? "IX" : " ", + cData & MDSAD_B_DD ? "DD" : " ", + cData & MDSAD_B_MO ? "MO" : " ", + cData & MDSAD_B_WR ? "WR" : " ", + cData & MDSAD_B_SP ? "SP" : " ", + cData & MDSAD_B_WP ? "WP" : " ", + cData & MDSAD_B_T0 ? "T0" : " ")); + break; + case MDSAD_C_STATUS: /* C-STATUS */ + cData |= (mdsad_info->c_status.sc & 0xF); + TRACE_PRINT(STATUS_MSG, ("MDSAD: " ADDRESS_FORMAT + " C-Status = <%s %s %s %s %i>" NLP, PCX, + cData & MDSAD_C_SF ? "SF" : " ", + cData & MDSAD_C_IX ? "IX" : " ", + cData & MDSAD_C_DD ? "DD" : " ", + cData & MDSAD_C_MO ? "MO" : " ", cData & MDSAD_C_SC)); + break; + case MDSAD_READ_DATA: /* READ DATA */ + { + unsigned int flags; + unsigned int readlen; + + if(mdsad_info->datacount == 0) { + TRACE_PRINT(RD_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT + " READ Start: Drive: %d, Track=%d, Head=%d, Sector=%d" NLP, + PCX, + mdsad_info->orders.ds, + pDrive->track, + mdsad_info->orders.ss, + pDrive->sector)); + + checksum = 0; + + sec_offset = (pDrive->track * (MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK)) + + (mdsad_info->orders.ss * ((MDSAD_SECTOR_LEN * MDSAD_SECTORS_PER_TRACK) * MDSAD_TRACKS)) + + (pDrive->sector * MDSAD_SECTOR_LEN); + + if ((pDrive->uptr == NULL) || + (pDrive->uptr->fileref == NULL)) { + TRACE_PRINT(RD_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT + " Drive: %d not attached - read ignored." NLP, + PCX, mdsad_info->orders.ds)); + return 0xe5; + } + + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } +/* DBG_PRINT(("%s: Read: imd=%p" NLP, __FUNCTION__, mdsad_info->drive[mdsad_info->sel_drive].imd)); */ + sectRead(pDrive->imd, + pDrive->track, + mdsad_info->orders.ss, + pDrive->sector, + sdata.u.data, + MDSAD_SECTOR_LEN, + &flags, + &readlen); + break; + case IMAGE_TYPE_DSK: + if(pDrive->uptr->fileref == NULL) { + printf(".fileref is NULL!" NLP); + } else { + fseek((pDrive->uptr)->fileref, + sec_offset, SEEK_SET); + fread(&sdata.u.data[0], MDSAD_SECTOR_LEN, + 1, (pDrive->uptr)->fileref); + } + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" + NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" + NLP, __FUNCTION__); + break; + } + if(trace_level & RD_DATA_DETAIL_MSG) showdata(TRUE); + } + + if(mdsad_info->datacount < 0x200) { + cData = sdata.u.data[mdsad_info->datacount]; + + /* Exclusive OR */ + checksum ^= cData; + /* Rotate Left Circular */ + checksum = ((checksum << 1) | ((checksum & 0x80) != 0)) & 0xff; + + DBG_PRINT(("MDSAD: " ADDRESS_FORMAT + " READ-DATA[offset:%06x+%03x]=%02x" NLP, + PCX, sec_offset, mdsad_info->datacount, cData)); + } else { /* checksum */ + cData = checksum; + TRACE_PRINT(RD_DATA_MSG, ("MDSAD: " ADDRESS_FORMAT + " READ-DATA: Checksum is: 0x%02x" NLP, + PCX, cData)); + } + + mdsad_info->datacount++; + break; + } + default: + DBG_PRINT(("MDSAD: " ADDRESS_FORMAT + " Invalid DM=%x" NLP, PCX, Addr & 0xF)); + break; + } + + break; + } + return (cData); +} diff --git a/AltairZ80/s100_scp300f.c b/AltairZ80/s100_scp300f.c new file mode 100644 index 0000000..e6b7d0b --- /dev/null +++ b/AltairZ80/s100_scp300f.c @@ -0,0 +1,425 @@ +/************************************************************************* + * * + * $Id: s100_scp300f.c 1902 2008-05-11 02:40:41Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Seattle Computer Products SCP300F Support Board module for SIMH. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/* #define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define PIO_MSG 0x01 +#define UART_MSG 0x02 +#define RTC_MSG 0x04 +#define MPCL_MSG 0x08 +#define ROM_MSG 0x10 +#define TRACE_MSG 0x80 + +#define SCP300F_MAX_DRIVES 1 +#define SCP300F_ROM_SIZE (2048) +#define SCP300F_ADDR_MASK (SCP300F_ROM_SIZE - 1) + +#define UNIT_V_SCP300F_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_SCP300F_VERBOSE (1 << UNIT_V_SCP300F_VERBOSE) + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint8 *ram; + uint8 *rom; + uint8 rom_enabled; +} SCP300F_INFO; + +static SCP300F_INFO scp300f_info_data = { { 0xFF800, SCP300F_ROM_SIZE, 0xF0, 16 } }; +static SCP300F_INFO *scp300f_info = &scp300f_info_data; + +extern t_stat set_membase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_membase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern uint32 PCX; +extern REG *sim_PC; +extern int32 find_unit_index (UNIT *uptr); + +static t_stat scp300f_reset(DEVICE *scp300f_dev); + +static uint8 SCP300F_Read(const uint32 Addr); +static uint8 SCP300F_Write(const uint32 Addr, uint8 cData); + +static int32 scp300fdev(const int32 port, const int32 io, const int32 data); +static int32 scp300f_mem(const int32 port, const int32 io, const int32 data); + +static int32 trace_level = 0x00; /* Disable all tracing by default */ +static int32 scp300f_sr = 0x00; /* Sense Switch Register */ + +static UNIT scp300f_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE, 0) } +}; + +static REG scp300f_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { HRDATA (SR, scp300f_sr, 8), }, + { NULL } +}; + +static MTAB scp300f_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MEMBASE", "MEMBASE", &set_membase, &show_membase, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + /* quiet, no warning messages */ + { UNIT_SCP300F_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_SCP300F_VERBOSE, UNIT_SCP300F_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE scp300f_dev = { + "SCP300F", scp300f_unit, scp300f_reg, scp300f_mod, + SCP300F_MAX_DRIVES, 10, 31, 1, SCP300F_MAX_DRIVES, SCP300F_MAX_DRIVES, + NULL, NULL, &scp300f_reset, + NULL, NULL, NULL, + &scp300f_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat scp300f_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + TRACE_PRINT(TRACE_MSG, ("SCP300F: Reset." NLP)); + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &scp300fdev, TRUE); + sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &scp300f_mem, TRUE); + } else { + /* Connect SCP300F at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &scp300fdev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + /* Connect SCP300F Memory (512K RAM, 1MB FLASH) */ + if(sim_map_resource(pnp->mem_base, pnp->mem_size, RESOURCE_TYPE_MEMORY, &scp300f_mem, FALSE) != 0) { + printf("%s: error mapping MEM resource at 0x%04x\n", __FUNCTION__, pnp->mem_base); + return SCPE_ARG; + } + + /* Re-enable ROM */ + scp300f_info->rom_enabled = 1; + } + return SCPE_OK; +} + + +static uint8 scp300f_ram[SCP300F_ROM_SIZE]; + +/* ; Seattle Computer Products 8086 Monitor version 1.5 3-19-82. + * ; by Tim Paterson + * ; This software is not copyrighted. + * + * This was assembled from source (MON.ASM) using 86DOS ASM.COM running under Windows XP. + */ +static uint8 scp300f_rom[SCP300F_ROM_SIZE] = { + 0xFC, 0x33, 0xC0, 0x8E, 0xD0, 0x8E, 0xD8, 0x8E, 0xC0, 0xBF, 0x9C, 0x01, 0xB9, 0x0E, 0x00, 0xF3, + 0xAB, 0x80, 0x0E, 0xB7, 0x01, 0x02, 0xB1, 0x04, 0xB0, 0x40, 0xBF, 0xAC, 0x01, 0xF3, 0xAB, 0xC6, + 0x06, 0xA5, 0x01, 0x0C, 0xBC, 0x9C, 0x01, 0xB0, 0x17, 0xE6, 0xF5, 0xB0, 0xF3, 0xE6, 0xF4, 0xB8, + 0x84, 0x05, 0xE7, 0xF4, 0xBE, 0x33, 0x07, 0xBA, 0xF0, 0x00, 0x2E, 0xAC, 0x8A, 0xC8, 0xE3, 0x05, + 0x2E, 0xAC, 0xEE, 0xE2, 0xFB, 0x42, 0x80, 0xFA, 0xF8, 0x75, 0xEF, 0xE8, 0x19, 0x00, 0xBE, 0xF5, + 0x07, 0xB8, 0x23, 0xE8, 0xE7, 0xF4, 0xB0, 0x0D, 0xE6, 0xF5, 0x2E, 0xAD, 0xE6, 0xF4, 0x8A, 0xC4, + 0xE6, 0xF4, 0xE8, 0x02, 0x00, 0xEB, 0xF3, 0xE8, 0x98, 0x00, 0xE8, 0x95, 0x00, 0x3C, 0x0D, 0x74, + 0x01, 0xC3, 0xBF, 0x18, 0x01, 0xC6, 0x05, 0x0D, 0xE4, 0xFF, 0xA8, 0x01, 0x74, 0x03, 0xE9, 0xF5, + 0x06, 0xBE, 0x51, 0x07, 0xE8, 0x8B, 0x00, 0xFC, 0x33, 0xC0, 0x8E, 0xD8, 0x8E, 0xC0, 0xBC, 0x9C, + 0x01, 0xC7, 0x06, 0x64, 0x00, 0xBB, 0x06, 0x8C, 0x0E, 0x66, 0x00, 0xB0, 0x3E, 0xE8, 0xC8, 0x00, + 0xE8, 0x1E, 0x00, 0xE8, 0x7F, 0x00, 0x74, 0xDF, 0x8A, 0x05, 0x2C, 0x42, 0x72, 0x10, 0x3C, 0x13, + 0x73, 0x0C, 0x47, 0xD0, 0xE0, 0x98, 0x93, 0x2E, 0xFF, 0x97, 0x7D, 0x01, 0xEB, 0xC9, 0xE9, 0xA8, + 0x02, 0xBF, 0x18, 0x01, 0x33, 0xC9, 0xE8, 0x39, 0x00, 0x3C, 0x20, 0x72, 0x1B, 0x3C, 0x7F, 0x74, + 0x0E, 0xE8, 0x94, 0x00, 0x3C, 0x40, 0x74, 0x25, 0xAA, 0x41, 0x83, 0xF9, 0x50, 0x76, 0xE7, 0xE3, + 0xE5, 0x4F, 0x49, 0xE8, 0x29, 0x00, 0xEB, 0xDE, 0x3C, 0x08, 0x74, 0xF3, 0x3C, 0x0D, 0x75, 0xD6, + 0xAA, 0xBF, 0x18, 0x01, 0xB0, 0x0D, 0xE8, 0x6F, 0x00, 0xB0, 0x0A, 0xEB, 0x6B, 0xE8, 0xF4, 0xFF, + 0xEB, 0x85, 0xFA, 0xE4, 0xF7, 0xA8, 0x02, 0x74, 0xF9, 0xE4, 0xF6, 0x24, 0x7F, 0xFB, 0xC3, 0xBE, + 0x73, 0x07, 0x2E, 0xAC, 0xE8, 0x51, 0x00, 0xD0, 0xE0, 0x73, 0xF7, 0xC3, 0xE8, 0x06, 0x00, 0x82, + 0x3D, 0x2C, 0x75, 0x0A, 0x47, 0xB0, 0x20, 0x51, 0xB1, 0xFF, 0xF3, 0xAE, 0x4F, 0x59, 0x82, 0x3D, + 0x0D, 0xC3, 0x8C, 0xDA, 0xB4, 0x00, 0xE8, 0x78, 0x00, 0x03, 0xD6, 0xEB, 0x09, 0x8C, 0xC2, 0xB4, + 0x00, 0xE8, 0x6D, 0x00, 0x03, 0xD7, 0x82, 0xD4, 0x00, 0xE8, 0x12, 0x00, 0x8A, 0xC6, 0xE8, 0x02, + 0x00, 0x8A, 0xC2, 0x8A, 0xE0, 0x51, 0xB1, 0x04, 0xD2, 0xE8, 0x59, 0xE8, 0x02, 0x00, 0x8A, 0xC4, + 0x24, 0x0F, 0x04, 0x90, 0x27, 0x14, 0x40, 0x27, 0x50, 0xE4, 0xF7, 0x24, 0x01, 0x74, 0xFA, 0x58, + 0xE6, 0xF6, 0xC3, 0xB0, 0x20, 0xEB, 0xF1, 0xE8, 0xF9, 0xFF, 0xE2, 0xFB, 0xC3, 0x76, 0x07, 0x68, + 0x03, 0x0D, 0x02, 0x88, 0x03, 0x97, 0x02, 0x6A, 0x06, 0x68, 0x03, 0x4C, 0x06, 0x68, 0x03, 0x68, + 0x03, 0x68, 0x03, 0x6A, 0x02, 0x68, 0x03, 0x59, 0x06, 0x68, 0x03, 0x68, 0x03, 0x2F, 0x04, 0xBA, + 0x02, 0x6A, 0x05, 0x8A, 0xC2, 0x24, 0x0F, 0xE8, 0x07, 0x00, 0x8A, 0xD0, 0x8A, 0xC6, 0x32, 0xF6, + 0xC3, 0xD1, 0xE2, 0xD0, 0xD4, 0xD1, 0xE2, 0xD0, 0xD4, 0xD1, 0xE2, 0xD0, 0xD4, 0xD1, 0xE2, 0xD0, + 0xD4, 0xC3, 0xB9, 0x05, 0x00, 0xE8, 0x22, 0x01, 0x50, 0x52, 0xE8, 0x4F, 0xFF, 0x82, 0x3D, 0x4C, + 0x74, 0x1C, 0xBA, 0x80, 0x00, 0xE8, 0x30, 0x01, 0x72, 0x1B, 0xB9, 0x05, 0x00, 0xE8, 0x0A, 0x01, + 0x8B, 0xCA, 0x5A, 0x5B, 0x2B, 0xCA, 0x1A, 0xE7, 0x75, 0x1D, 0x93, 0x41, 0xEB, 0x0B, 0x47, 0xB9, + 0x04, 0x00, 0xE8, 0xF5, 0x00, 0x8B, 0xCA, 0x5A, 0x58, 0x8B, 0xDA, 0x81, 0xE3, 0x0F, 0x00, 0xE3, + 0x04, 0x03, 0xD9, 0x73, 0x9E, 0x74, 0x9C, 0xB8, 0x52, 0x47, 0xE9, 0x1F, 0x03, 0xE8, 0xB2, 0xFF, + 0x50, 0xE8, 0x4E, 0x01, 0x1F, 0x8B, 0xF2, 0xE8, 0x18, 0xFF, 0x56, 0xE8, 0x55, 0xFF, 0xAC, 0xE8, + 0x31, 0xFF, 0x5A, 0x49, 0x74, 0x17, 0x8B, 0xC6, 0xA8, 0x0F, 0x74, 0x0C, 0x52, 0xA8, 0x07, 0x75, + 0xEA, 0xB0, 0x2D, 0xE8, 0x32, 0xFF, 0xEB, 0xE6, 0xE8, 0x02, 0x00, 0xEB, 0xDA, 0x51, 0x8B, 0xC6, + 0x8B, 0xF2, 0x2B, 0xC2, 0x8B, 0xD8, 0xD1, 0xE0, 0x03, 0xC3, 0xB9, 0x33, 0x00, 0x2B, 0xC8, 0xE8, + 0x25, 0xFF, 0x8B, 0xCB, 0xAC, 0x24, 0x7F, 0x3C, 0x7F, 0x74, 0x04, 0x3C, 0x20, 0x73, 0x02, 0xB0, + 0x2E, 0xE8, 0x04, 0xFF, 0xE2, 0xEE, 0x59, 0xE9, 0x8A, 0xFE, 0xE8, 0x55, 0xFF, 0x51, 0x50, 0x8B, + 0xF2, 0xB9, 0x05, 0x00, 0xE8, 0x73, 0x00, 0xE8, 0xE8, 0x00, 0xE8, 0x26, 0xFF, 0x8B, 0xFA, 0x5B, + 0x8E, 0xDB, 0x8E, 0xC0, 0x59, 0x3B, 0xFE, 0x1B, 0xC3, 0x72, 0x07, 0x49, 0x03, 0xF1, 0x03, 0xF9, + 0xFD, 0x41, 0xA4, 0x49, 0xF3, 0xA4, 0xC3, 0xE8, 0x28, 0xFF, 0x51, 0x50, 0x52, 0xE8, 0xB4, 0x00, + 0x5F, 0x07, 0x59, 0x3B, 0xD9, 0xBE, 0x18, 0x01, 0xE3, 0x02, 0x73, 0xE6, 0x2B, 0xCB, 0x87, 0xD9, + 0x57, 0xF3, 0xA4, 0x5E, 0x8B, 0xCB, 0x06, 0x1F, 0xEB, 0xD8, 0xE8, 0x05, 0xFF, 0x51, 0x50, 0x52, + 0xE8, 0x91, 0x00, 0x4B, 0x5F, 0x07, 0x59, 0x2B, 0xCB, 0xBE, 0x18, 0x01, 0xAC, 0xAE, 0xE0, 0xFD, + 0x75, 0xC4, 0x53, 0x87, 0xCB, 0x57, 0xF3, 0xA6, 0x8B, 0xCB, 0x5F, 0x5B, 0x75, 0x08, 0x4F, 0xE8, + 0x5B, 0xFE, 0x47, 0xE8, 0x0E, 0xFE, 0xE3, 0xAE, 0xEB, 0xDF, 0xE8, 0x2F, 0xFE, 0x33, 0xD2, 0x8A, + 0xE6, 0xE8, 0x14, 0x00, 0x72, 0x73, 0x8A, 0xD0, 0x47, 0x49, 0xE8, 0x0B, 0x00, 0x72, 0x97, 0xE3, + 0x68, 0xE8, 0xAD, 0xFE, 0x0A, 0xD0, 0xEB, 0xF0, 0x8A, 0x05, 0x2C, 0x30, 0x72, 0x88, 0x3C, 0x0A, + 0xF5, 0x73, 0x83, 0x2C, 0x07, 0x3C, 0x0A, 0x72, 0x03, 0x3C, 0x10, 0xF5, 0xC3, 0xE8, 0xFC, 0xFD, + 0xE8, 0xE5, 0xFF, 0x72, 0x0B, 0xB9, 0x02, 0x00, 0xE8, 0xBF, 0xFF, 0x88, 0x17, 0x43, 0xF8, 0xC3, + 0x8A, 0x05, 0x3C, 0x27, 0x74, 0x06, 0x3C, 0x22, 0x74, 0x02, 0xF9, 0xC3, 0x8A, 0xE0, 0x47, 0x8A, + 0x05, 0x47, 0x3C, 0x0D, 0x74, 0x23, 0x3A, 0xC4, 0x75, 0x05, 0x3A, 0x25, 0x75, 0xE0, 0x47, 0x88, + 0x07, 0x43, 0xEB, 0xEB, 0xBB, 0x18, 0x01, 0xE8, 0xC3, 0xFF, 0x73, 0xFB, 0x81, 0xEB, 0x18, 0x01, + 0x74, 0x07, 0xE8, 0xC0, 0xFD, 0x75, 0x02, 0xC3, 0x4F, 0x81, 0xEF, 0x17, 0x01, 0x8B, 0xCF, 0xE8, + 0x05, 0xFE, 0xBE, 0x6A, 0x07, 0xE8, 0x9A, 0xFD, 0xE9, 0x0C, 0xFD, 0xE8, 0xD6, 0xFF, 0x5F, 0x07, + 0xBE, 0x18, 0x01, 0x8B, 0xCB, 0xF3, 0xA4, 0xC3, 0xB9, 0x05, 0x00, 0xE8, 0x5C, 0xFF, 0xE8, 0x12, + 0xFE, 0x82, 0xEC, 0x08, 0x80, 0xC6, 0x80, 0x50, 0x52, 0xE8, 0x89, 0xFD, 0x75, 0xDD, 0x5F, 0x07, + 0xE8, 0x9A, 0xFD, 0xE8, 0xCD, 0xFD, 0x26, 0x8A, 0x05, 0xE8, 0xA7, 0xFD, 0xB0, 0x2E, 0xE8, 0xB7, + 0xFD, 0xB9, 0x02, 0x00, 0xBA, 0x00, 0x00, 0xE8, 0x48, 0xFD, 0x8A, 0xE0, 0xE8, 0x4B, 0xFF, 0x86, + 0xE0, 0x72, 0x0C, 0xE8, 0xA2, 0xFD, 0x8A, 0xF2, 0x8A, 0xD4, 0xE2, 0xEB, 0xE8, 0x33, 0xFD, 0x3C, + 0x08, 0x74, 0x19, 0x3C, 0x7F, 0x74, 0x15, 0x3C, 0x2D, 0x74, 0x4D, 0x3C, 0x0D, 0x74, 0x2F, 0x3C, + 0x20, 0x74, 0x31, 0xB0, 0x07, 0xE8, 0x80, 0xFD, 0xE3, 0xE2, 0xEB, 0xCB, 0x82, 0xF9, 0x02, 0x74, + 0xC6, 0xFE, 0xC1, 0x8A, 0xD6, 0x8A, 0xF5, 0xE8, 0x15, 0xFD, 0xEB, 0xBB, 0x82, 0xF9, 0x02, 0x74, + 0x0B, 0x51, 0xB1, 0x04, 0xD2, 0xE6, 0x59, 0x0A, 0xD6, 0x26, 0x88, 0x15, 0x47, 0xC3, 0xE8, 0xEB, + 0xFF, 0xE9, 0xE0, 0xFC, 0xE8, 0xE5, 0xFF, 0x41, 0x41, 0xE8, 0x5B, 0xFD, 0x8B, 0xC7, 0x24, 0x07, + 0x75, 0x84, 0xE8, 0xCF, 0xFC, 0xE9, 0x78, 0xFF, 0xE8, 0xD1, 0xFF, 0x4F, 0x4F, 0xEB, 0xF3, 0xE8, + 0xEA, 0xFC, 0x74, 0x62, 0x8A, 0x15, 0x47, 0x8A, 0x35, 0x82, 0xFE, 0x0D, 0x74, 0x76, 0x47, 0xE8, + 0x20, 0xFF, 0x82, 0xFE, 0x20, 0x74, 0x6D, 0xBF, 0xD7, 0x06, 0x92, 0x0E, 0x07, 0xB9, 0x0E, 0x00, + 0xF2, 0xAF, 0x75, 0x3C, 0x0B, 0xC9, 0x75, 0x06, 0x4F, 0x4F, 0x2E, 0x8B, 0x45, 0xFE, 0xE8, 0x07, + 0xFD, 0x8A, 0xC4, 0xE8, 0x02, 0xFD, 0xE8, 0x0A, 0xFD, 0x1E, 0x07, 0x8D, 0x9D, 0xC3, 0xFA, 0x8B, + 0x17, 0xE8, 0xD8, 0xFC, 0xE8, 0x7D, 0xFC, 0xB0, 0x3A, 0xE8, 0xEC, 0xFC, 0xE8, 0x42, 0xFC, 0xE8, + 0xA3, 0xFC, 0x74, 0x0B, 0xB9, 0x04, 0x00, 0xE8, 0x63, 0xFE, 0xE8, 0xD5, 0xFE, 0x89, 0x17, 0xC3, + 0xB8, 0x42, 0x52, 0xE9, 0x96, 0x00, 0xBE, 0xD7, 0x06, 0xBB, 0x9C, 0x01, 0xB9, 0x08, 0x00, 0xE8, + 0x65, 0x00, 0xE8, 0x4F, 0xFC, 0xB9, 0x05, 0x00, 0xE8, 0x5C, 0x00, 0xE8, 0xC5, 0xFC, 0xE8, 0x93, + 0x00, 0xE9, 0x40, 0xFC, 0x82, 0xFA, 0x46, 0x75, 0xD7, 0xE8, 0x88, 0x00, 0xB0, 0x2D, 0xE8, 0xA7, + 0xFC, 0xE8, 0xFD, 0xFB, 0xE8, 0x5E, 0xFC, 0x33, 0xDB, 0x8B, 0x16, 0xB6, 0x01, 0x8B, 0xF7, 0xAD, + 0x3C, 0x0D, 0x74, 0x66, 0x82, 0xFC, 0x0D, 0x74, 0x66, 0xBF, 0xF3, 0x06, 0xB9, 0x20, 0x00, 0x0E, + 0x07, 0xF2, 0xAF, 0x75, 0x5A, 0x8A, 0xE9, 0x80, 0xE1, 0x0F, 0xB8, 0x01, 0x00, 0xD3, 0xC0, 0x85, + 0xC3, 0x75, 0x33, 0x0B, 0xD8, 0x0B, 0xD0, 0xF6, 0xC5, 0x10, 0x75, 0x02, 0x33, 0xD0, 0x8B, 0xFE, + 0x1E, 0x07, 0xE8, 0x17, 0xFC, 0xEB, 0xC6, 0x2E, 0xAD, 0xE8, 0x5C, 0xFC, 0x8A, 0xC4, 0xE8, 0x57, + 0xFC, 0xB0, 0x3D, 0xE8, 0x52, 0xFC, 0x8B, 0x17, 0x43, 0x43, 0xE8, 0x2F, 0xFC, 0xE8, 0x53, 0xFC, + 0xE8, 0x50, 0xFC, 0xE2, 0xE2, 0xC3, 0xB8, 0x44, 0x46, 0xE8, 0x0E, 0x00, 0xE8, 0x39, 0xFC, 0x8A, + 0xC4, 0xE8, 0x34, 0xFC, 0xBE, 0x6B, 0x07, 0xE9, 0x3B, 0xFE, 0x89, 0x16, 0xB6, 0x01, 0xC3, 0xB8, + 0x42, 0x46, 0xEB, 0xE5, 0xBE, 0xF3, 0x06, 0xB9, 0x10, 0x00, 0x8B, 0x16, 0xB6, 0x01, 0x2E, 0xAD, + 0xD1, 0xE2, 0x72, 0x04, 0x2E, 0x8B, 0x44, 0x1E, 0x0B, 0xC0, 0x74, 0x0B, 0xE8, 0x09, 0xFC, 0x8A, + 0xC4, 0xE8, 0x04, 0xFC, 0xE8, 0x0C, 0xFC, 0xE2, 0xE5, 0xC3, 0xE8, 0xAF, 0xFB, 0xE8, 0x98, 0xFD, + 0xBA, 0x01, 0x00, 0x72, 0x06, 0xB9, 0x04, 0x00, 0xE8, 0x6F, 0xFD, 0x89, 0x16, 0x02, 0x01, 0xE8, + 0xE0, 0xFD, 0xC7, 0x06, 0x00, 0x01, 0x00, 0x00, 0x80, 0x0E, 0xB7, 0x01, 0x01, 0xC7, 0x06, 0x0C, + 0x00, 0xD1, 0x05, 0x8C, 0x0E, 0x0E, 0x00, 0xC7, 0x06, 0x04, 0x00, 0xD8, 0x05, 0x8C, 0x0E, 0x06, + 0x00, 0xFA, 0xC7, 0x06, 0x64, 0x00, 0xD8, 0x05, 0x8C, 0x0E, 0x66, 0x00, 0xBC, 0x9C, 0x01, 0x58, + 0x5B, 0x59, 0x5A, 0x5D, 0x5D, 0x5E, 0x5F, 0x07, 0x07, 0x17, 0x8B, 0x26, 0xA4, 0x01, 0xFF, 0x36, + 0xB6, 0x01, 0xFF, 0x36, 0xB2, 0x01, 0xFF, 0x36, 0xB4, 0x01, 0x8E, 0x1E, 0xAC, 0x01, 0xCF, 0xEB, + 0xB1, 0x87, 0xEC, 0xFF, 0x4E, 0x00, 0x87, 0xEC, 0x2E, 0x89, 0x26, 0xA4, 0x09, 0x2E, 0x8C, 0x16, + 0xB0, 0x09, 0x33, 0xE4, 0x8E, 0xD4, 0xBC, 0xB0, 0x01, 0x06, 0x1E, 0x57, 0x56, 0x55, 0x4C, 0x4C, + 0x52, 0x51, 0x53, 0x50, 0x16, 0x1F, 0x8B, 0x26, 0xA4, 0x01, 0x8E, 0x16, 0xB0, 0x01, 0x8F, 0x06, + 0xB4, 0x01, 0x8F, 0x06, 0xB2, 0x01, 0x58, 0x80, 0xE4, 0xFE, 0xA3, 0xB6, 0x01, 0x89, 0x26, 0xA4, + 0x01, 0x1E, 0x07, 0x1E, 0x17, 0xBC, 0x9C, 0x01, 0xC7, 0x06, 0x64, 0x00, 0xBB, 0x06, 0xB0, 0x20, + 0xE6, 0xF2, 0xFB, 0xFC, 0xE8, 0xCD, 0xFA, 0xE8, 0x6C, 0xFE, 0xFF, 0x0E, 0x02, 0x01, 0x75, 0x9F, + 0xBE, 0x04, 0x01, 0x8B, 0x0E, 0x00, 0x01, 0xE3, 0x10, 0x8B, 0x54, 0x14, 0xAD, 0x50, 0xE8, 0x62, + 0xFB, 0x8E, 0xC0, 0x8B, 0xFA, 0x58, 0xAA, 0xE2, 0xF0, 0xE9, 0x3B, 0xFA, 0xB9, 0x04, 0x00, 0xE8, + 0x98, 0xFC, 0xEC, 0xE8, 0xFD, 0xFA, 0xE9, 0x9B, 0xFA, 0xB9, 0x04, 0x00, 0xE8, 0x8B, 0xFC, 0x52, + 0xB9, 0x02, 0x00, 0xE8, 0x84, 0xFC, 0x92, 0x5A, 0xEE, 0xC3, 0xBB, 0x18, 0x01, 0x33, 0xF6, 0xE8, + 0xAA, 0xFA, 0x74, 0x19, 0xB9, 0x05, 0x00, 0xE8, 0x70, 0xFC, 0x89, 0x17, 0x88, 0x67, 0xED, 0x43, + 0x43, 0x46, 0x83, 0xFE, 0x0B, 0x75, 0xE8, 0xB8, 0x42, 0x50, 0xE9, 0x9F, 0xFE, 0x89, 0x36, 0x00, + 0x01, 0xE8, 0xCE, 0xFC, 0x8B, 0xCE, 0xE3, 0x1A, 0xBE, 0x04, 0x01, 0x8B, 0x54, 0x14, 0xAD, 0xE8, + 0x01, 0xFB, 0x8E, 0xD8, 0x8B, 0xFA, 0x8A, 0x05, 0xC6, 0x05, 0xCC, 0x06, 0x1F, 0x88, 0x44, 0xFE, + 0xE2, 0xE9, 0xC7, 0x06, 0x02, 0x01, 0x01, 0x00, 0xE9, 0xD2, 0xFE, 0x50, 0xB0, 0x20, 0xE6, 0xF2, + 0xE4, 0xF6, 0x24, 0x7F, 0x3C, 0x13, 0x75, 0x03, 0xE8, 0x37, 0xFA, 0x3C, 0x03, 0x74, 0x02, 0x58, + 0xCF, 0xE8, 0x20, 0xFA, 0xE9, 0xB0, 0xF9, 0x41, 0x58, 0x42, 0x58, 0x43, 0x58, 0x44, 0x58, 0x53, + 0x50, 0x42, 0x50, 0x53, 0x49, 0x44, 0x49, 0x44, 0x53, 0x45, 0x53, 0x53, 0x53, 0x43, 0x53, 0x49, + 0x50, 0x50, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x56, 0x44, 0x4E, 0x45, + 0x49, 0x00, 0x00, 0x4E, 0x47, 0x5A, 0x52, 0x00, 0x00, 0x41, 0x43, 0x00, 0x00, 0x50, 0x45, 0x00, + 0x00, 0x43, 0x59, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x56, 0x55, 0x50, 0x44, + 0x49, 0x00, 0x00, 0x50, 0x4C, 0x4E, 0x5A, 0x00, 0x00, 0x4E, 0x41, 0x00, 0x00, 0x50, 0x4F, 0x00, + 0x00, 0x4E, 0x43, 0x01, 0x19, 0x04, 0x10, 0x02, 0x0F, 0xFD, 0x01, 0x19, 0x04, 0x18, 0x01, 0x0B, + 0xFD, 0x06, 0x63, 0x0B, 0x07, 0x00, 0x06, 0x00, 0x02, 0x70, 0x05, 0x00, 0x04, 0xB7, 0x77, 0xCE, + 0x37, 0x0D, 0x0A, 0x0A, 0x53, 0x43, 0x50, 0x20, 0x38, 0x30, 0x38, 0x36, 0x20, 0x4D, 0x6F, 0x6E, + 0x69, 0x74, 0x6F, 0x72, 0x20, 0x31, 0x2E, 0x35, 0x0D, 0x8A, 0x5E, 0x20, 0x45, 0x72, 0x72, 0x6F, + 0x72, 0x0D, 0x8A, 0x08, 0x20, 0x88, 0x57, 0xB0, 0x01, 0xE6, 0x02, 0xB0, 0x84, 0xE6, 0x00, 0xB0, + 0x7F, 0xE6, 0x04, 0xB6, 0x21, 0xB0, 0xD0, 0xE6, 0x30, 0xD4, 0x0A, 0xD4, 0x0A, 0xD4, 0x0A, 0xD4, + 0x0A, 0x80, 0xF6, 0x10, 0x8A, 0xC6, 0xE6, 0x34, 0xBF, 0x00, 0x02, 0xB0, 0x0F, 0xE6, 0x30, 0xE4, + 0x34, 0xD0, 0xC8, 0x73, 0xFA, 0xE4, 0x30, 0x24, 0x98, 0x75, 0xDA, 0xB0, 0x01, 0xE6, 0x32, 0x8A, + 0xC6, 0x0C, 0x80, 0xE6, 0x34, 0xB2, 0x33, 0xB0, 0x8C, 0xE6, 0x30, 0xEB, 0x02, 0xEC, 0xAA, 0xE4, + 0x34, 0xD0, 0xC8, 0x73, 0xF8, 0xE4, 0x30, 0x24, 0x9C, 0x75, 0xBA, 0xC7, 0x06, 0xB2, 0x01, 0x00, + 0x00, 0xC7, 0x06, 0xB4, 0x01, 0x00, 0x02, 0x5F, 0xE9, 0x8F, 0xFE, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xEA, 0x00, 0x00, 0x80, 0xFF, 0x0D, 0x00, 0x68, 0x00, 0xA0, 0x01, 0x40, 0x03, 0x78, 0x04, 0x00 +}; + + static int32 scp300f_mem(const int32 Addr, const int32 write, const int32 data) +{ +/* DBG_PRINT(("SCP300F: ROM %s, Addr %04x" NLP, write ? "WR" : "RD", Addr)); */ + if(write) { + if(scp300f_info->rom_enabled) + { + TRACE_PRINT(ROM_MSG, ("SCP300F: " ADDRESS_FORMAT " WR ROM[0x%05x]: Cannot write to ROM." NLP, PCX, Addr)); + } else { + } + return 0; + } else { + if(scp300f_info->rom_enabled) + { + return scp300f_rom[Addr & SCP300F_ADDR_MASK]; + } else { + return scp300f_ram[Addr & SCP300F_ADDR_MASK]; + } + } +} + +static int32 scp300fdev(const int32 port, const int32 io, const int32 data) +{ +/* DBG_PRINT(("SCP300F: IO %s, Port %02x\n", io ? "WR" : "RD", port)); */ + if(io) { + SCP300F_Write(port, data); + return 0; + } else { + return(SCP300F_Read(port)); + } +} + +#define SCP300F_MPIC_0 0x00 /* Master PIC */ +#define SCP300F_MPIC_1 0x01 /* Master PIC */ +#define SCP300F_SPIC_0 0x02 /* Slave PIC */ +#define SCP300F_SPIC_1 0x03 /* Slave PIC */ +#define SCP300F_9513_DATA 0x04 /* 9513 counter/timer Data Port */ +#define SCP300F_9513_STATUS 0x05 /* 9513 counter/timer Status/Control Port */ +#define SCP300F_UART_DATA 0x06 /* UART Data Register */ +#define SCP300F_UART_STATUS 0x07 /* UART Status */ + +#define SCP300F_PIO_DATA 0x0C /* PIO Data */ +#define SCP300F_PIO_STATUS 0x0D /* PIO Status */ +#define SCP300F_EPROM_DIS 0x0E /* EPROM Disable */ +#define SCP300F_SENSE_SW 0x0F /* Sense Switch */ + +extern int32 sio0d(const int32 port, const int32 io, const int32 data); +extern int32 sio0s(const int32 port, const int32 io, const int32 data); + +static uint8 SCP300F_Read(const uint32 Addr) +{ + uint8 cData = 0xFF; + + switch(Addr & 0xF) { + case SCP300F_MPIC_0: + case SCP300F_MPIC_1: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Master 8259 DATA RD[%02x]: not implemented." NLP, PCX, Addr)); + break; + case SCP300F_SPIC_0: + case SCP300F_SPIC_1: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Slave 8259 DATA RD[%02x]: not implemented." NLP, PCX, Addr)); + break; + case SCP300F_9513_DATA: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 DATA RD[%02x]: not implemented." NLP, PCX, Addr)); + break; + case SCP300F_9513_STATUS: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 STAT RD[%02x]: not implemented." NLP, PCX, Addr)); + break; + case SCP300F_UART_DATA: /* UART is handled by the 2SIO, if this gets called, then the 2SIO was not */ + case SCP300F_UART_STATUS: /* configured properly. */ + TRACE_PRINT(TRACE_MSG, ("SCP300F: " ADDRESS_FORMAT " RD[%02x]: UART not configured properly." NLP, PCX, Addr)); + break; + case SCP300F_PIO_DATA: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " PIO DATA RD[%02x]: not implemented." NLP, PCX, Addr)); + break; + case SCP300F_PIO_STATUS: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " PIO STATUS RD[%02x]: not implemented." NLP, PCX, Addr)); + break; + case SCP300F_SENSE_SW: /* Sense Switch */ + cData = scp300f_sr; + break; + default: + TRACE_PRINT(TRACE_MSG, ("SCP300F: " ADDRESS_FORMAT " RD[%02x]: not Implemented." NLP, PCX, Addr)); + break; + } + + return (cData); + +} + +static uint8 SCP300F_Write(const uint32 Addr, uint8 cData) +{ + + switch(Addr & 0xF) { + case SCP300F_MPIC_0: + case SCP300F_MPIC_1: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Master 8259 DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); + break; + case SCP300F_SPIC_0: + case SCP300F_SPIC_1: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " Slave 8259 DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); + break; + case SCP300F_9513_DATA: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); + break; + case SCP300F_9513_STATUS: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " 9513 STAT WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); + break; + case SCP300F_UART_DATA: /* UART is handled by the 2SIO, if this gets called, then the 2SIO was not */ + case SCP300F_UART_STATUS: /* configured properly. */ + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[%02x]: UART not configured properly." NLP, PCX, Addr)); + break; + case SCP300F_PIO_DATA: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " PIO DATA WR[%02x]=%02x: not implemented." NLP, PCX, Addr, cData)); + break; + case SCP300F_PIO_STATUS: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[%02x]: Cannot write to PIO STATUS." NLP, PCX, Addr)); + break; + case SCP300F_SENSE_SW: + TRACE_PRINT(UART_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[%02x]: Cannot write to SR." NLP, PCX, Addr)); + break; + default: + TRACE_PRINT(TRACE_MSG, ("SCP300F: " ADDRESS_FORMAT " WR[0x%02x]=0x%02x: not Implemented." NLP, PCX, Addr, cData)); + break; + } + + return(0); +} + diff --git a/AltairZ80/s100_selchan.c b/AltairZ80/s100_selchan.c new file mode 100644 index 0000000..09c508a --- /dev/null +++ b/AltairZ80/s100_selchan.c @@ -0,0 +1,208 @@ +/************************************************************************* + * * + * $Id: s100_selchan.c 1771 2008-01-09 07:10:46Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * CompuPro Selector Channel module for SIMH. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define TRACE_MSG 0x01 +#define DMA_MSG 0x02 + +#define SELCHAN_MAX_DRIVES 1 + +#define UNIT_V_SELCHAN_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_SELCHAN_VERBOSE (1 << UNIT_V_SELCHAN_VERBOSE) + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint32 selchan; /* Selector Channel Register */ + uint32 dma_addr; /* DMA Transfer Address */ + uint32 dma_mode; /* DMA Mode register */ + uint8 reg_cnt; /* Counter for selchan register */ +} SELCHAN_INFO; + +static SELCHAN_INFO selchan_info_data = { { 0x0, 0, 0xF0, 1 } }; +static SELCHAN_INFO *selchan_info = &selchan_info_data; +int32 selchan_dma(uint8 *buf, uint32 len); + +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern uint32 PCX; +extern REG *sim_PC; + +/* These are needed for DMA. PIO Mode has not been implemented yet. */ +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); +extern uint8 GetBYTEWrapper(const uint32 Addr); + +static t_stat selchan_reset(DEVICE *selchan_dev); + +static int32 selchandev(const int32 port, const int32 io, const int32 data); + +static int32 trace_level = 0; /* Disable all tracing by default */ + +static UNIT selchan_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ROABLE, 0) } +}; + +static REG selchan_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { NULL } +}; + +static MTAB selchan_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + /* quiet, no warning messages */ + { UNIT_SELCHAN_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_SELCHAN_VERBOSE, UNIT_SELCHAN_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE selchan_dev = { + "SELCHAN", selchan_unit, selchan_reg, selchan_mod, + SELCHAN_MAX_DRIVES, 10, 31, 1, SELCHAN_MAX_DRIVES, SELCHAN_MAX_DRIVES, + NULL, NULL, &selchan_reset, + NULL, NULL, NULL, + &selchan_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat selchan_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &selchandev, TRUE); + } else { + /* Connect SELCHAN at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &selchandev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + return SCPE_OK; +} + +#define SELCHAN_MODE_WRITE 0x80 /* Selector Channel Memory or I/O Write */ +#define SELCHAN_MODE_IO 0x40 /* Set if I/O Access, otherwise memory */ +#define SELCHAN_MODE_CNT_UP 0x20 /* Set = DMA Address Count Up, otherwise down. (Mem only */ +#define SELCHAN_MODE_WAIT 0x10 /* Insert one wait state. */ +#define SELCHAN_MODE_DMA_MASK 0x0F /* Mask for DMA Priority field */ + +static int32 selchandev(const int32 port, const int32 io, const int32 data) +{ + DBG_PRINT(("SELCHAN: IO %s, Port %02x" NLP, io ? "WR" : "RD", port)); + if(io) { + selchan_info->selchan <<= 8; + selchan_info->selchan &= 0xFFFFFF00; + selchan_info->selchan |= data; + + selchan_info->dma_addr = (selchan_info->selchan & 0xFFFFF00) >> 8; + selchan_info->dma_mode = (selchan_info->selchan & 0xFF); + + selchan_info->reg_cnt ++; + + if(selchan_info->reg_cnt == 4) { + TRACE_PRINT(TRACE_MSG, ("SELCHAN: " ADDRESS_FORMAT " DMA=0x%06x, Mode=0x%02x (%s, %s, %s)" NLP, + PCX, + selchan_info->dma_addr, + selchan_info->dma_mode, + selchan_info->dma_mode & SELCHAN_MODE_WRITE ? "WR" : "RD", + selchan_info->dma_mode & SELCHAN_MODE_IO ? "I/O" : "MEM", + selchan_info->dma_mode & SELCHAN_MODE_IO ? "FIX" : selchan_info->dma_mode & SELCHAN_MODE_CNT_UP ? "INC" : "DEC")); + } + + return 0; + } else { + TRACE_PRINT(TRACE_MSG, ("SELCHAN: " ADDRESS_FORMAT " Reset" NLP, PCX)); + selchan_info->reg_cnt = 0; + return(0xFF); + } +} + +int32 selchan_dma(uint8 *buf, uint32 len) +{ + uint32 i; + + if(selchan_info->reg_cnt != 4) { + printf("SELCHAN: " ADDRESS_FORMAT " Programming error: selector channel disabled." NLP, + PCX); + return (-1); + } + + if(selchan_info->dma_mode & SELCHAN_MODE_IO) + { + printf("SELCHAN: " ADDRESS_FORMAT " I/O Not supported" NLP, PCX); + return (-1); + } else { + TRACE_PRINT(DMA_MSG, ("SELCHAN: " ADDRESS_FORMAT " DMA %s Transfer, len=%d" NLP, + PCX, + (selchan_info->dma_mode & SELCHAN_MODE_WRITE) ? "WR" : "RD", len)); + for(i=0;idma_mode & SELCHAN_MODE_WRITE) { + PutBYTEWrapper(selchan_info->dma_addr + i, buf[i]); + } else { + buf[i] = GetBYTEWrapper(selchan_info->dma_addr + i); + } + } + + if(selchan_info->dma_mode & SELCHAN_MODE_CNT_UP) { + selchan_info->dma_addr += i; + } else { + selchan_info->dma_addr -= i; + } + } + + return(0); +} diff --git a/AltairZ80/s100_ss1.c b/AltairZ80/s100_ss1.c new file mode 100644 index 0000000..39b9387 --- /dev/null +++ b/AltairZ80/s100_ss1.c @@ -0,0 +1,246 @@ +/************************************************************************* + * * + * $Id: s100_ss1.c 1773 2008-01-11 05:46:19Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * CompuPro System Support 1 module for SIMH. * + * Note this does not include the Boot ROM on the System Support 1 Card * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define TRACE_MSG 0x01 +#define DMA_MSG 0x02 + +#define SS1_MAX_DRIVES 1 + +#define UNIT_V_SS1_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_SS1_VERBOSE (1 << UNIT_V_SS1_VERBOSE) + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ +} SS1_INFO; + +static SS1_INFO ss1_info_data = { { 0x0, 0, 0x50, 12 } }; +/* static SS1_INFO *ss1_info = &ss1_info_data;*/ + +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); +extern uint32 PCX; +extern REG *sim_PC; + +/* These are needed for DMA. PIO Mode has not been implemented yet. */ +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); +extern uint8 GetBYTEWrapper(const uint32 Addr); + +static t_stat ss1_reset(DEVICE *ss1_dev); +static uint8 SS1_Read(const uint32 Addr); +static uint8 SS1_Write(const uint32 Addr, uint8 cData); + + +static int32 ss1dev(const int32 port, const int32 io, const int32 data); + +static int32 trace_level = 0x00; /* Disable all tracing by default */ + +static UNIT ss1_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ROABLE, 0) } +}; + +static REG ss1_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { NULL } +}; + +static MTAB ss1_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + /* quiet, no warning messages */ + { UNIT_SS1_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_SS1_VERBOSE, UNIT_SS1_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE ss1_dev = { + "SS1", ss1_unit, ss1_reg, ss1_mod, + SS1_MAX_DRIVES, 10, 31, 1, SS1_MAX_DRIVES, SS1_MAX_DRIVES, + NULL, NULL, &ss1_reset, + NULL, NULL, NULL, + &ss1_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat ss1_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ss1dev, TRUE); + } else { + /* Connect SS1 at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &ss1dev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + return SCPE_OK; +} + +static int32 ss1dev(const int32 port, const int32 io, const int32 data) +{ + DBG_PRINT(("SS1: IO %s, Port %02x\n", io ? "WR" : "RD", port)); + if(io) { + SS1_Write(port, data); + return 0; + } else { + return(SS1_Read(port)); + } +} + +#define SS1_M8259_L 0x00 +#define SS1_M8259_H 0x01 +#define SS1_S8259_L 0x02 +#define SS1_S8259_H 0x03 +#define SS1_8253_TC0 0x04 +#define SS1_8253_TC1 0x05 +#define SS1_8253_TC2 0x06 +#define SS1_8253_CTL 0x07 +#define SS1_9511A_DATA 0x08 +#define SS1_9511A_CMD 0x09 +#define SS1_RTC_CMD 0x0A +#define SS1_RTC_DATA 0x0B +#define SS1_UART_DATA 0x0C +#define SS1_UART_STAT 0x0D +#define SS1_UART_MODE 0x0E +#define SS1_UART_CMD 0x0F + +extern int32 sio0d(const int32 port, const int32 io, const int32 data); +extern int32 sio0s(const int32 port, const int32 io, const int32 data); + +static uint8 SS1_Read(const uint32 Addr) +{ + uint8 cData = 0x00; + + switch(Addr & 0x0F) { + case SS1_M8259_L: + case SS1_M8259_H: + case SS1_S8259_L: + case SS1_S8259_H: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " RD: Interrupt Controller not Implemented." NLP, PCX)); + break; + case SS1_8253_TC0: + case SS1_8253_TC1: + case SS1_8253_TC2: + case SS1_8253_CTL: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " RD: Timer not Implemented." NLP, PCX)); + break; + case SS1_9511A_DATA: + case SS1_9511A_CMD: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " RD: Math Coprocessor not Implemented." NLP, PCX)); + break; + case SS1_RTC_CMD: + case SS1_RTC_DATA: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " RD: RTC not Implemented." NLP, PCX)); + break; + case SS1_UART_DATA: + cData = sio0d(Addr, 0, 0); + break; + case SS1_UART_STAT: + cData = sio0s(Addr, 0, 0); + break; + case SS1_UART_MODE: + case SS1_UART_CMD: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " RD: UART not Implemented." NLP, PCX)); + break; + } + + return (cData); + +} + +static uint8 SS1_Write(const uint32 Addr, uint8 cData) +{ + + switch(Addr & 0x0F) { + case SS1_M8259_L: + case SS1_M8259_H: + case SS1_S8259_L: + case SS1_S8259_H: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: Interrupt Controller not Implemented." NLP, PCX)); + break; + case SS1_8253_TC0: + case SS1_8253_TC1: + case SS1_8253_TC2: + case SS1_8253_CTL: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: Timer not Implemented." NLP, PCX)); + break; + case SS1_9511A_DATA: + case SS1_9511A_CMD: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: Math Coprocessor not Implemented." NLP, PCX)); + break; + case SS1_RTC_CMD: + case SS1_RTC_DATA: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: RTC not Implemented." NLP, PCX)); + break; + case SS1_UART_DATA: + sio0d(Addr, 1, cData); + break; + case SS1_UART_STAT: + sio0s(Addr, 1, cData); + break; + case SS1_UART_MODE: + case SS1_UART_CMD: + TRACE_PRINT(TRACE_MSG, ("SS1: " ADDRESS_FORMAT " WR: UART not Implemented." NLP, PCX)); + break; + } + + return(0); +} + diff --git a/AltairZ80/sim_imd.c b/AltairZ80/sim_imd.c new file mode 100644 index 0000000..37a7a5b --- /dev/null +++ b/AltairZ80/sim_imd.c @@ -0,0 +1,365 @@ +/************************************************************************* + * * + * $Id: sim_imd.c 1904 2008-05-21 06:57:57Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * ImageDisk Disk Image File access module for SIMH, definitions. * + * see : * + * for details on the ImageDisk format and other utilities. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +#include "altairz80_defs.h" +#include "sim_imd.h" + +/* #define DBG_MSG */ + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + + +DISK_INFO * diskOpen(FILE *fileref, int isVerbose) +{ + char cBuf[256]; + char sectorMap[256]; + char sectorHeadMap[256]; + char sectorCylMap[256]; + unsigned int sectorSize, sectRecordType; + unsigned int i; + unsigned char start_sect; + DISK_INFO *myDisk = NULL; + + unsigned int TotalSectorCount = 0; + IMD_HEADER imd; + + myDisk = (DISK_INFO *)malloc(sizeof(DISK_INFO)); + + myDisk->file = fileref; + + /* rewind to the beginning of the file. */ + fseek(myDisk->file, 0, SEEK_SET); + + do { + cBuf[0] = fgetc(myDisk->file); + if((cBuf[0] != 0x1a) && isVerbose) putchar(cBuf[0]); + } + while (cBuf[0] != 0x1a); + + myDisk->nsides = 1; + myDisk->ntracks = 0; + myDisk->flags = 0; /* Make sure all flags are clear. */ + + do { + DBG_PRINT(("start of track %d at file offset %d\n", myDisk->ntracks, ftell(myDisk->file))); + + fread(&imd, 1, 5, myDisk->file); + if(feof(myDisk->file)) break; + + sectorSize = (1 << imd.sectsize) * 128; + + DBG_PRINT(("Track %d:\n", myDisk->ntracks)); + DBG_PRINT(("\tMode=%d, Cyl=%d, Head=%d, #sectors=%d, sectsize=%d (%d bytes)\n", imd.mode, imd.cyl, imd.head, imd.nsects, imd.sectsize, sectorSize)); + + if((imd.head + 1) > myDisk->nsides) { + myDisk->nsides = imd.head + 1; + } + + myDisk->track[imd.cyl][imd.head].mode = imd.mode; + myDisk->track[imd.cyl][imd.head].nsects = imd.nsects; + myDisk->track[imd.cyl][imd.head].sectsize = sectorSize; + + fread(sectorMap, 1, imd.nsects, myDisk->file); + myDisk->track[imd.cyl][imd.head].start_sector = imd.nsects; + DBG_PRINT(("\tSector Map: ")); + for(i=0;itrack[imd.cyl][imd.head].start_sector) { + myDisk->track[imd.cyl][imd.head].start_sector = sectorMap[i]; + } + } + DBG_PRINT((", Start Sector=%d", myDisk->track[imd.cyl][imd.head].start_sector)); + + if(imd.head & IMD_FLAG_SECT_HEAD_MAP) { + fread(sectorHeadMap, 1, imd.nsects, myDisk->file); + DBG_PRINT(("\tSector Head Map: ")); + for(i=0;ifile); + DBG_PRINT(("\tSector Cyl Map: ")); + for(i=0;ifile))); + + /* Build the table with location 0 being the start sector. */ + start_sect = myDisk->track[imd.cyl][imd.head].start_sector; + + /* Now read each sector */ + for(i=0;ifile); + switch(sectRecordType) { + case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */ + myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = 0xBADBAD; + break; + case SECT_RECORD_NORM: /* Normal Data */ + case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ + case SECT_RECORD_NORM_ERR: /* Normal Data with read error */ + case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ +/* DBG_PRINT(("Uncompressed Data\n")); */ + myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file); + fseek(myDisk->file, sectorSize, SEEK_CUR); + break; + case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */ + case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ + case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */ + case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ + myDisk->track[imd.cyl][imd.head].sectorOffsetMap[sectorMap[i]-start_sect] = ftell(myDisk->file); + myDisk->flags |= FD_FLAG_WRITELOCK; /* Write-protect the disk if any sectors are compressed. */ +#ifdef VERBOSE_DEBUG + DBG_PRINT(("Compressed Data = 0x%02x\n", fgetc(myDisk->file))); +#else + fgetc(myDisk->file); +#endif + break; + default: + printf("ERROR: unrecognized sector record type %d\n", sectRecordType); + break; + } + DBG_PRINT(("\n")); + } + + myDisk->ntracks++; + } + while (!feof(myDisk->file)); + + DBG_PRINT(("Processed %d sectors\n", TotalSectorCount)); + +#ifdef VERBOSE_DEBUG + for(i=0;intracks;i++) { + DBG_PRINT(("Track %02d: ", i)); + for(j=0;jtrack[i][0].sectorOffsetMap[j])); + } + DBG_PRINT(("\n")); + } +#endif + if(myDisk->flags & FD_FLAG_WRITELOCK) { + printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress.\n"); + } + + return myDisk; +} + +unsigned int diskClose(DISK_INFO *myDisk) +{ + unsigned int retval = 0; + + if(myDisk == NULL) { + return (-1); + } + + free(myDisk); + myDisk = NULL; + + return retval; +} + +unsigned int imdGetSides(DISK_INFO *myDisk) +{ + if(myDisk != NULL) { + return(myDisk->nsides); + } + + return (0); +} + +unsigned int imdIsWriteLocked(DISK_INFO *myDisk) +{ + if(myDisk != NULL) { + return((myDisk->flags & FD_FLAG_WRITELOCK) ? 1 : 0); + } + + return (0); +} + +/* Check that the given track/sector exists on the disk */ +int sectSeek(DISK_INFO *myDisk, unsigned int Cyl, unsigned int Head) +{ + if(myDisk->track[Cyl][Head].nsects == 0) { + DBG_PRINT(("%s: invalid track/head" NLP, __FUNCTION__)); + return(-1); + } + + return(0); +} + + +int sectRead(DISK_INFO *myDisk, unsigned int Cyl, unsigned int Head, unsigned int Sector, unsigned char *buf, unsigned int buflen, unsigned int *flags, unsigned int *readlen) +{ + unsigned int sectorFileOffset; + unsigned char sectRecordType; + unsigned char start_sect; + *readlen = 0; + *flags = 0; + + if(Sector > myDisk->track[Cyl][Head].nsects) { + DBG_PRINT(("%s: invalid sector" NLP, __FUNCTION__)); + return(-1); + } + + if(buflen < myDisk->track[Cyl][Head].sectsize) { + printf("%s: Reading C:%d/H:%d/S:%d, len=%d: user buffer too short, need %d" NLP, __FUNCTION__, Cyl, Head, Sector, buflen, myDisk->track[Cyl][Head].sectsize); + return(-1); + } + + start_sect = myDisk->track[Cyl][Head].start_sector; + + sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; + + DBG_PRINT(("Reading C:%d/H:%d/S:%d, len=%d, offset=0x%08x" NLP, Cyl, Head, Sector, buflen, sectorFileOffset)); + + fseek(myDisk->file, sectorFileOffset-1, 0); + + sectRecordType = fgetc(myDisk->file); + switch(sectRecordType) { + case SECT_RECORD_UNAVAILABLE: /* Data could not be read from the original media */ + *flags |= IMD_DISK_IO_ERROR_GENERAL; + break; + case SECT_RECORD_NORM_ERR: /* Normal Data with read error */ + case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ + *flags |= IMD_DISK_IO_ERROR_CRC; + case SECT_RECORD_NORM: /* Normal Data */ + case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ + +/* DBG_PRINT(("Uncompressed Data" NLP)); */ + fread(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file); + *readlen = myDisk->track[Cyl][Head].sectsize; + break; + case SECT_RECORD_NORM_COMP_ERR: /* Compressed Normal Data */ + case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ + *flags |= IMD_DISK_IO_ERROR_CRC; + case SECT_RECORD_NORM_COMP: /* Compressed Normal Data */ + case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ +/* DBG_PRINT(("Compressed Data" NLP)); */ + memset(buf, fgetc(myDisk->file), myDisk->track[Cyl][Head].sectsize); + *readlen = myDisk->track[Cyl][Head].sectsize; + *flags |= IMD_DISK_IO_COMPRESSED; + break; + default: + printf("ERROR: unrecognized sector record type %d" NLP, sectRecordType); + break; + } + + /* Set flags for deleted address mark. */ + switch(sectRecordType) { + case SECT_RECORD_NORM_DAM: /* Normal Data with deleted address mark */ + case SECT_RECORD_NORM_DAM_ERR: /* Normal Data with deleted address mark with read error */ + case SECT_RECORD_NORM_DAM_COMP: /* Compressed Normal Data with deleted address mark */ + case SECT_RECORD_NORM_DAM_COMP_ERR: /* Compressed Normal Data with deleted address mark */ + *flags |= IMD_DISK_IO_DELETED_ADDR_MARK; + default: + break; + } + + return(0); +} + + +int sectWrite(DISK_INFO *myDisk, unsigned int Cyl, unsigned int Head, unsigned int Sector, unsigned char *buf, unsigned int buflen, unsigned int *flags, unsigned int *writelen) +{ + unsigned int sectorFileOffset; + unsigned char sectRecordType; + unsigned char start_sect; + *writelen = 0; + *flags = 0; + + DBG_PRINT(("Writing C:%d/H:%d/S:%d, len=%d" NLP, Cyl, Head, Sector, buflen)); + + if(myDisk->flags & FD_FLAG_WRITELOCK) { + printf("Disk write-protected because the image contains compressed sectors. Use IMDU to uncompress." NLP); + } + + if(Sector > myDisk->track[Cyl][Head].nsects) { + printf("%s: invalid sector [sector %i > # of sectors %i]" NLP, + __FUNCTION__, Sector, myDisk->track[Cyl][Head].nsects); + return(-1); + } + + if(buflen < myDisk->track[Cyl][Head].sectsize) { + printf("%s: user buffer too short [buflen %i < sectsize %i]" NLP, + __FUNCTION__, buflen, myDisk->track[Cyl][Head].sectsize); + return(-1); + } + + start_sect = myDisk->track[Cyl][Head].start_sector; + + sectorFileOffset = myDisk->track[Cyl][Head].sectorOffsetMap[Sector-start_sect]; + + fseek(myDisk->file, sectorFileOffset-1, 0); + + if (*flags & IMD_DISK_IO_ERROR_GENERAL) { + sectRecordType = SECT_RECORD_UNAVAILABLE; + } else if (*flags & IMD_DISK_IO_ERROR_CRC) { + if (*flags & IMD_DISK_IO_DELETED_ADDR_MARK) + sectRecordType = SECT_RECORD_NORM_DAM_ERR; + else + sectRecordType = SECT_RECORD_NORM_ERR; + } else { + sectRecordType = SECT_RECORD_NORM; + } + + fputc(sectRecordType, myDisk->file); + fwrite(buf, 1, myDisk->track[Cyl][Head].sectsize, myDisk->file); + *writelen = myDisk->track[Cyl][Head].sectsize; + + return(0); +} diff --git a/AltairZ80/sim_imd.h b/AltairZ80/sim_imd.h new file mode 100644 index 0000000..eb70ef1 --- /dev/null +++ b/AltairZ80/sim_imd.h @@ -0,0 +1,110 @@ +/************************************************************************* + * * + * $Id: sim_imd.h 1904 2008-05-21 06:57:57Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * ImageDisk Disk Image File access module for SIMH, definitions. * + * see : * + * for details on the ImageDisk format and other utilities. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +typedef struct { + unsigned char mode; + unsigned char cyl; + unsigned char head; + unsigned char nsects; + unsigned char sectsize; +} IMD_HEADER; + + +#define IMD_FLAG_SECT_HEAD_MAP (1 << 6) +#define IMD_FLAG_SECT_CYL_MAP (1 << 7) + +#define SECT_RECORD_UNAVAILABLE 0 /* Data could not be read from the original media */ +#define SECT_RECORD_NORM 1 /* Normal Data */ +#define SECT_RECORD_NORM_COMP 2 /* Compressed Normal Data */ +#define SECT_RECORD_NORM_DAM 3 /* Normal Data with deleted address mark */ +#define SECT_RECORD_NORM_DAM_COMP 4 /* Compressed Normal Data with deleted address mark */ +#define SECT_RECORD_NORM_ERR 5 /* Normal Data */ +#define SECT_RECORD_NORM_COMP_ERR 6 /* Compressed Normal Data */ +#define SECT_RECORD_NORM_DAM_ERR 7 /* Normal Data with deleted address mark */ +#define SECT_RECORD_NORM_DAM_COMP_ERR 8 /* Compressed Normal Data with deleted address mark */ + +#define MAX_CYL 80 +#define MAX_HEAD 2 +#define MAX_SPT 26 + +#define FD_FLAG_WRITELOCK 1 + +#define IMD_DISK_IO_ERROR_GENERAL (1 << 0) /* General data error. */ +#define IMD_DISK_IO_ERROR_CRC (1 << 1) /* Data read/written, but got a CRC error. */ +#define IMD_DISK_IO_DELETED_ADDR_MARK (1 << 2) /* Sector had a deleted address mark */ +#define IMD_DISK_IO_COMPRESSED (1 << 3) /* Sector is compressed in the IMD file (Read Only) */ + +#define IMD_MODE_500K_FM 0 +#define IMD_MODE_300K_FM 1 +#define IMD_MODE_250K_FM 2 +#define IMD_MODE_500K_MFM 3 +#define IMD_MODE_300K_MFM 4 +#define IMD_MODE_250K_MFM 5 + +#define IMD_MODE_FM(x) (x <= IMD_MODE_250K_FM) +#define IMD_MODE_MFM(x) (x >= IMD_MODE_500K_MFM) + +typedef struct { + unsigned char mode; + unsigned char nsects; + unsigned int sectsize; + unsigned int sectorOffsetMap[MAX_SPT]; + unsigned char start_sector; +} TRACK_INFO; + +typedef struct { + FILE *file; + unsigned int ntracks; + unsigned char nsides; + unsigned char flags; + TRACK_INFO track[MAX_CYL][MAX_HEAD]; +} DISK_INFO; + +extern DISK_INFO *diskOpen(FILE *fileref, int isVerbose); /*char *filename); */ +extern unsigned int diskClose(DISK_INFO *myDisk); +extern unsigned int imdGetSides(DISK_INFO *myDisk); +extern unsigned int imdIsWriteLocked(DISK_INFO *myDisk); + +extern int sectSeek(DISK_INFO *myDisk, unsigned int Cyl, unsigned int Head); +extern int sectRead(DISK_INFO *myDisk, unsigned int Cyl, unsigned int Head, unsigned int Sector, unsigned char *buf, unsigned int buflen, unsigned int *flags, unsigned int *readlen); +extern int sectWrite(DISK_INFO *myDisk, unsigned int Cyl, unsigned int Head, unsigned int Sector, unsigned char *buf, unsigned int buflen, unsigned int *flags, unsigned int *writelen); diff --git a/AltairZ80/vfdhd.c b/AltairZ80/vfdhd.c new file mode 100644 index 0000000..8475153 --- /dev/null +++ b/AltairZ80/vfdhd.c @@ -0,0 +1,650 @@ +/************************************************************************* + * * + * $Id: vfdhd.c 1773 2008-01-11 05:46:19Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Micropolis FDC module for SIMH * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ +#define USE_VGI /* Use 275-byte VGI-format sectors (includes all metadata) */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#include "sim_imd.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define ORDERS_MSG 0x02 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 + +static void VFDHD_Command(void); + +#define VFDHD_MAX_DRIVES 4 + +#define VFDHD_SECTOR_LEN 275 + +typedef union { + struct { + uint8 preamble[40]; /* Hard disk uses 30 bytes of preamble, floppy uses 40. */ + uint8 sync; + uint8 header[2]; + uint8 unused[10]; + uint8 data[256]; + uint8 checksum; + uint8 ecc[4]; + uint8 ecc_valid; /* 0xAA indicates ECC is being used. */ + uint8 postamble[128]; + } u; + uint8 raw[VFDHD_SECTOR_LEN]; + +} SECTOR_FORMAT; + +typedef struct { + UNIT *uptr; + DISK_INFO *imd; + uint16 ntracks; /* number of tracks */ + uint8 nheads; /* number of heads */ + uint8 nspt; /* number of sectors per track */ + uint8 npre_len; /* preamble length */ + uint32 sectsize; /* sector size, not including pre/postamble */ + uint16 track; + uint8 wp; /* Disk write protected */ + uint8 ready; /* Drive is ready */ + uint8 write_fault; + uint8 seek_complete; + uint8 sync_lost; + uint32 sector_wait_count; +} VFDHD_DRIVE_INFO; + +typedef struct { + PNP_INFO pnp; /* Plug and Play */ + uint8 xfr_flag; /* Indicates controller is ready to send/receive data */ + uint8 sel_drive; /* Currently selected drive */ + uint8 selected; /* 1 if drive is selected */ + uint8 track0; /* Set it selected drive is on track 0 */ + uint8 head; /* Currently selected head */ + uint8 wr_latch; /* Write enable latch */ + uint8 int_enable; /* Interrupt Enable */ + uint32 datacount; /* Number of data bytes transferred from controller for current sector */ + uint8 step; + uint8 direction; + uint8 rwc; + uint8 sector; + uint8 read; + uint8 ecc_enable; + uint8 precomp; + + uint8 floppy_sel; + uint8 controller_busy; + uint8 motor_on; + uint8 hdsk_type; + VFDHD_DRIVE_INFO drive[VFDHD_MAX_DRIVES]; +} VFDHD_INFO; + +static VFDHD_INFO vfdhd_info_data = { { 0x0, 0, 0xC0, 4 } }; +static VFDHD_INFO *vfdhd_info = &vfdhd_info_data; + +static SECTOR_FORMAT sdata; +extern uint32 PCX; +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +#define UNIT_V_VFDHD_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_VFDHD_WLK (1 << UNIT_V_VFDHD_WLK) +#define UNIT_V_VFDHD_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_VFDHD_VERBOSE (1 << UNIT_V_VFDHD_VERBOSE) +#define VFDHD_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ +#define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ +#define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ +#define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ + +static t_stat vfdhd_reset(DEVICE *vfdhd_dev); +static t_stat vfdhd_attach(UNIT *uptr, char *cptr); +static t_stat vfdhd_detach(UNIT *uptr); + +static int32 vfdhddev(const int32 port, const int32 io, const int32 data); + +static uint8 VFDHD_Read(const uint32 Addr); +static uint8 VFDHD_Write(const uint32 Addr, uint8 cData); + +static int32 trace_level = FALSE; +static int32 hdSize = 5; + +static UNIT vfdhd_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) }, + { UDATA (NULL, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, VFDHD_CAPACITY) } +}; + +static REG vfdhd_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { DRDATA (HDSIZE, hdSize, 10), }, + { NULL } +}; + +static MTAB vfdhd_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_VFDHD_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_VFDHD_WLK, UNIT_VFDHD_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_VFDHD_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_VFDHD_VERBOSE, UNIT_VFDHD_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE vfdhd_dev = { + "VFDHD", vfdhd_unit, vfdhd_reg, vfdhd_mod, + VFDHD_MAX_DRIVES, 10, 31, 1, VFDHD_MAX_DRIVES, VFDHD_MAX_DRIVES, + NULL, NULL, &vfdhd_reset, + NULL, &vfdhd_attach, &vfdhd_detach, + &vfdhd_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Reset routine */ +static t_stat vfdhd_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &vfdhddev, TRUE); + } else { + /* Connect MFDC at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &vfdhddev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + return SCPE_OK; +} + + +/* Attach routine */ +static t_stat vfdhd_attach(UNIT *uptr, char *cptr) +{ + char header[4]; + t_stat r; + unsigned int i = 0; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + + for(i = 0; i < VFDHD_MAX_DRIVES; i++) { + vfdhd_info->drive[i].uptr = &vfdhd_dev.units[i]; + } + + for(i = 0; i < VFDHD_MAX_DRIVES; i++) { + if(vfdhd_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + if(uptr->capac > 0) { + fgets(header, 4, uptr->fileref); + if(!strcmp(header, "IMD")) { + uptr->u3 = IMAGE_TYPE_IMD; + } else if(!strcmp(header, "CPT")) { + printf("CPT images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_CPT; + vfdhd_detach(uptr); + return SCPE_OPENERR; + } else { + uptr->u3 = IMAGE_TYPE_DSK; + } + } else { + /* creating file, must be DSK format. */ + uptr->u3 = IMAGE_TYPE_DSK; + } + + if (uptr->flags & UNIT_VFDHD_VERBOSE) + printf("VFDHD%d: attached to '%s', type=%s, len=%d\n", i, cptr, + uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + + if(uptr->u3 == IMAGE_TYPE_IMD) { + if(uptr->capac < 318000) { + printf("Cannot create IMD files with SIMH.\nCopy an existing file and format it with CP/M.\n"); + vfdhd_detach(uptr); + return SCPE_OPENERR; + } + + if (uptr->flags & UNIT_VFDHD_VERBOSE) + printf("--------------------------------------------------------\n"); + vfdhd_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_VFDHD_VERBOSE)); + printf("\n"); + } else { + vfdhd_info->drive[i].imd = NULL; + } + + if(i>0) { /* Floppy Disk, Unit 1-3 */ + vfdhd_info->drive[i].ntracks = 77; /* number of tracks */ + vfdhd_info->drive[i].nheads = 2; /* number of heads */ + vfdhd_info->drive[i].nspt = 16; /* number of sectors per track */ + vfdhd_info->drive[i].npre_len = 40; /* preamble length */ + vfdhd_info->drive[i].sectsize = VFDHD_SECTOR_LEN; /* sector size, not including pre/postamble */ + } else { /* Hard Disk, Unit 0 */ + if(hdSize == 10) { + vfdhd_info->drive[i].ntracks = 153; /* number of tracks */ + vfdhd_info->drive[i].nheads = 6; /* number of heads */ + vfdhd_info->hdsk_type = 1; + printf("10MB\n"); + } else if (hdSize == 5) { + vfdhd_info->drive[i].ntracks = 153; /* number of tracks */ + vfdhd_info->drive[i].nheads = 4; /* number of heads */ + vfdhd_info->hdsk_type = 0; + printf("5MB\n"); + } else { + vfdhd_info->drive[i].ntracks = 512; /* number of tracks */ + vfdhd_info->drive[i].nheads = 8; /* number of heads */ + vfdhd_info->hdsk_type = 1; + printf("32MB\n"); + } + + vfdhd_info->drive[i].nheads = 4; /* number of heads */ + vfdhd_info->drive[i].nspt = 32; /* number of sectors per track */ + vfdhd_info->drive[i].npre_len = 30; /* preamble length */ + vfdhd_info->drive[i].sectsize = VFDHD_SECTOR_LEN; /* sector size, not including pre/postamble */ + vfdhd_info->drive[i].ready = 1; + vfdhd_info->drive[i].seek_complete = 1; + vfdhd_info->drive[i].sync_lost = 1; /* Active LOW */ + } + + vfdhd_info->motor_on = 1; + return SCPE_OK; +} + + +/* Detach routine */ +static t_stat vfdhd_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + for(i = 0; i < VFDHD_MAX_DRIVES; i++) { + if(vfdhd_dev.units[i].fileref == uptr->fileref) { + break; + } + } + + DBG_PRINT(("Detach VFDHD%d\n", i)); + diskClose(vfdhd_info->drive[i].imd); + + r = detach_unit(uptr); /* detach unit */ + if ( r != SCPE_OK) + return r; + + return SCPE_OK; +} + + +static uint8 cy; +static uint8 adc(uint8 sum, uint8 a1) +{ + uint32 total; + + total = sum + a1 + cy; + + if(total > 0xFF) { + cy = 1; + } else { + cy = 0; + } + + return(total & 0xFF); +} + +static int32 vfdhddev(const int32 port, const int32 io, const int32 data) +{ + DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " IO %s, Port %02x" NLP, PCX, io ? "WR" : "RD", port)); + if(io) { + VFDHD_Write(port, data); + return 0; + } else { + return(VFDHD_Read(port)); + } +} + +#define FDHD_CTRL_STATUS0 0 /* R=Status Port 0, W=Control Port 0 */ +#define FDHD_CTRL_STATUS1 1 /* R=Status Port 1, W=Control Port 0 */ +#define FDHD_DATA 2 /* R/W=Data Port */ +#define FDHD_RESET_START 3 /* R=RESET, W=START */ + +static uint8 VFDHD_Read(const uint32 Addr) +{ + uint8 cData; + VFDHD_DRIVE_INFO *pDrive; + + pDrive = &vfdhd_info->drive[vfdhd_info->sel_drive]; + + cData = 0x00; + + switch(Addr & 0x3) { + case FDHD_CTRL_STATUS0: + cData = (pDrive->wp & 1); /* [0] Write Protect (FD) */ + cData |= (pDrive->ready & 1) << 1; /* [1] Drive ready (HD) */ + cData |= (pDrive->track == 0) ? 0x04 : 0; /* [2] TK0 (FD/HD) */ + cData |= (pDrive->write_fault & 1) << 3; /* [3] Write Fault (HD) */ + cData |= (pDrive->seek_complete & 1) << 4; /* [4] Seek Complete (HD) */ + cData |= (pDrive->sync_lost & 1) << 5; /* [5] Loss of Sync (HD) */ + cData |= 0xC0; /* [7:6] Reserved (pulled up) */ + TRACE_PRINT(STATUS_MSG, ("VFDHD: " ADDRESS_FORMAT " RD S0 = 0x%02x" NLP, PCX, cData)); + break; + case FDHD_CTRL_STATUS1: + vfdhd_info->floppy_sel = (vfdhd_info->sel_drive == 0) ? 0 : 1; + cData = (vfdhd_info->floppy_sel & 0x1); /* [0] Floppy Selected */ + cData |= (vfdhd_info->controller_busy & 0x1) << 1; /* [1] Controller busy */ + cData |= (vfdhd_info->motor_on & 0x1) << 2; /* [2] Motor On (FD) */ + cData |= (vfdhd_info->hdsk_type & 0x1) << 3; /* [3] Hard Disk Type (0=5MB, 1=10MB) */ + cData |= 0xF0; /* [7:4] Reserved (pulled up) */ + if(vfdhd_info->sel_drive == 0) { +/* cData &= 0xF0; */ + } + + vfdhd_info->controller_busy = 0; + + TRACE_PRINT(STATUS_MSG, ("VFDHD: " ADDRESS_FORMAT " RD S1 = 0x%02x" NLP, PCX, cData)); + break; + case FDHD_DATA: +/* DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " RD Data" NLP, PCX)); */ + cData = sdata.raw[vfdhd_info->datacount+40]; + + vfdhd_info->datacount++; + +/* DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " RD Data Sector %d[%03d]: 0x%02x" NLP, PCX, pDrive->sector, vfdhd_info->datacount, cData)); */ + break; + case FDHD_RESET_START: /* Reset */ + TRACE_PRINT(CMD_MSG, ("VFDHD: " ADDRESS_FORMAT " Reset" NLP, PCX)); + vfdhd_info->datacount = 0; + cData = 0xFF; /* Return High-Z data */ + break; + } + + return (cData); +} + +static uint8 VFDHD_Write(const uint32 Addr, uint8 cData) +{ + VFDHD_DRIVE_INFO *pDrive; + + pDrive = &vfdhd_info->drive[vfdhd_info->sel_drive]; + + switch(Addr & 0x3) { + case FDHD_CTRL_STATUS0: + vfdhd_info->sel_drive = cData & 3; + vfdhd_info->head = (cData >> 2) & 0x7; + vfdhd_info->step = (cData >> 5) & 1; + vfdhd_info->direction = (cData >> 6) & 1; + vfdhd_info->rwc = (cData >> 7) & 1; + + TRACE_PRINT(WR_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " WR C0=%02x: sel_drive=%d, head=%d, step=%d, dir=%d, rwc=%d" NLP, + PCX, + cData, + vfdhd_info->sel_drive, + vfdhd_info->head, + vfdhd_info->step, + vfdhd_info->direction, + vfdhd_info->rwc)); + + if(vfdhd_info->step == 1) { + if(vfdhd_info->direction == 1) { /* Step IN */ + pDrive->track++; + } else { /* Step OUT */ + if(pDrive->track != 0) { + pDrive->track--; + } + } + TRACE_PRINT(SEEK_MSG, ("VFDHD: " ADDRESS_FORMAT " Drive %d on track %d" NLP, + PCX, vfdhd_info->sel_drive, pDrive->track)); + } + + break; + case FDHD_CTRL_STATUS1: + vfdhd_info->sector = (cData & 0x1f); + vfdhd_info->read = (cData >> 5) & 1; + vfdhd_info->ecc_enable = (cData >> 6) & 1; + vfdhd_info->precomp = (cData >> 7) & 1; + if(cData == 0xFF) { + TRACE_PRINT(SEEK_MSG, ("VFDHD: " ADDRESS_FORMAT " Home Disk %d" NLP, + PCX, vfdhd_info->sel_drive)); + pDrive->track = 0; + } + DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " WR C1=%02x: sector=%d, read=%d, ecc_en=%d, precomp=%d" NLP, + PCX, + cData, + vfdhd_info->sector, + vfdhd_info->read, + vfdhd_info->ecc_enable, + vfdhd_info->precomp)); + break; + case FDHD_DATA: /* Data Port */ + DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " WR Data" NLP, PCX)); +#ifdef USE_VGI + if(vfdhd_info->sel_drive > 0) { /* Floppy */ + sdata.raw[vfdhd_info->datacount] = cData; + } else { /* Hard */ + sdata.raw[vfdhd_info->datacount+10] = cData; + } +#else + sdata.u.data[vfdhd_info->datacount-13] = cData; +#endif /* USE_VGI */ + + vfdhd_info->datacount ++; + + break; + case FDHD_RESET_START: + TRACE_PRINT(CMD_MSG, ("VFDHD: " ADDRESS_FORMAT " Start Command" NLP, PCX)); + VFDHD_Command(); + break; + } + + cData = 0x00; + + return (cData); +} + +static void VFDHD_Command(void) +{ + VFDHD_DRIVE_INFO *pDrive; + + uint32 bytesPerTrack; + uint32 bytesPerHead; + + uint32 sec_offset; + uint32 flags; + + pDrive = &(vfdhd_info->drive[vfdhd_info->sel_drive]); + + bytesPerTrack = pDrive->sectsize * pDrive->nspt; + bytesPerHead = bytesPerTrack * pDrive->ntracks; + + sec_offset = (pDrive->track * bytesPerTrack) + \ + (vfdhd_info->head * bytesPerHead) + \ + (vfdhd_info->sector * pDrive->sectsize); + + vfdhd_info->controller_busy = 1; + + if(vfdhd_info->read == 1) { /* Perform a Read operation */ + unsigned int i, checksum; + unsigned int readlen; + + TRACE_PRINT(RD_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " RD: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP, + PCX, + vfdhd_info->sel_drive, + pDrive->track, + vfdhd_info->head, + vfdhd_info->sector)); + + /* Clear out unused portion of sector. */ + memset(&sdata.u.unused[0], 0x00, 10); + + sdata.u.sync = 0xFF; + sdata.u.header[0] = pDrive->track & 0xFF; + sdata.u.header[1] = vfdhd_info->sector; + + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } + printf("%s: Read: imd=%p" NLP, __FUNCTION__, pDrive->imd); + sectRead(pDrive->imd, + pDrive->track, + vfdhd_info->head, + vfdhd_info->sector, + sdata.u.data, + 256, + &flags, + &readlen); + + adc(0,0); /* clear Carry bit */ + checksum = 0; + + /* Checksum everything except the sync byte */ + for(i=1;i<269;i++) { + checksum = adc(checksum, sdata.raw[i]); + } + + sdata.u.checksum = checksum & 0xFF; + sdata.u.ecc_valid = 0xAA; + break; + case IMAGE_TYPE_DSK: + if(pDrive->uptr->fileref == NULL) { + printf(".fileref is NULL!" NLP); + } else { + fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); + fread(&sdata.u.sync, 274, /*VFDHD_SECTOR_LEN,*/ 1, (pDrive->uptr)->fileref); + + memset(&sdata.u.preamble, 0, 40); + memset(&sdata.u.ecc, 0, 5); /* Clear out the ECC and ECC Valid bytes */ + sdata.u.ecc_valid = 0xAA; + for(vfdhd_info->datacount = 0; sdata.raw[vfdhd_info->datacount] == 0x00; vfdhd_info->datacount++) { + } + + DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " READ: Sync found at offset %d" NLP, PCX, vfdhd_info->datacount)); + + } + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" NLP, __FUNCTION__); + break; + } + + } else { /* Perform a Write operation */ + unsigned int writelen; + + TRACE_PRINT(WR_DATA_MSG, ("VFDHD: " ADDRESS_FORMAT " WR: Drive=%d, Track=%d, Head=%d, Sector=%d" NLP, + PCX, + vfdhd_info->sel_drive, + pDrive->track, + vfdhd_info->head, + vfdhd_info->sector)); + +#ifdef USE_VGI +#else + int data_index = vfdhd_info->datacount - 13; + + sec_offset = (pDrive->track * 4096) + \ + (vfdhd_info->head * 315392) + \ + (vfdhd_info->sector * 256); +#endif /* USE_VGI */ + + switch((pDrive->uptr)->u3) + { + case IMAGE_TYPE_IMD: + if(pDrive->imd == NULL) { + printf(".imd is NULL!" NLP); + } + sectWrite(pDrive->imd, + pDrive->track, + vfdhd_info->head, + vfdhd_info->sector, + sdata.u.data, + 256, + &flags, + &writelen); + break; + case IMAGE_TYPE_DSK: + if(pDrive->uptr->fileref == NULL) { + printf(".fileref is NULL!" NLP); + } else { + DBG_PRINT(("VFDHD: " ADDRESS_FORMAT " WR drive=%d, track=%d, head=%d, sector=%d" NLP, + PCX, + vfdhd_info->sel_drive, + pDrive->track, + vfdhd_info->head, + vfdhd_info->sector)); + fseek((pDrive->uptr)->fileref, sec_offset, SEEK_SET); +#ifdef USE_VGI + fwrite(&sdata.u.sync, VFDHD_SECTOR_LEN, 1, (pDrive->uptr)->fileref); +#else + fwrite(sdata.u.data, 256, 1, (pDrive->uptr)->fileref); +#endif /* USE_VGI */ + } + break; + case IMAGE_TYPE_CPT: + printf("%s: CPT Format not supported" NLP, __FUNCTION__); + break; + default: + printf("%s: Unknown image Format" NLP, __FUNCTION__); + break; + } + } +} diff --git a/AltairZ80/vfdhd.h b/AltairZ80/vfdhd.h new file mode 100644 index 0000000..7b5052c --- /dev/null +++ b/AltairZ80/vfdhd.h @@ -0,0 +1,43 @@ +/************************************************************************* + * * + * $Id: vfdhd.h 1694 2007-12-14 05:23:11Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Micropolis FDC module for SIMH definitions * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +extern int32 mfdhd_dev(const int32 port, const int32 io, const int32 data); + diff --git a/AltairZ80/wd179x.c b/AltairZ80/wd179x.c new file mode 100644 index 0000000..f739446 --- /dev/null +++ b/AltairZ80/wd179x.c @@ -0,0 +1,864 @@ +/************************************************************************* + * * + * $Id: wd179x.c 1907 2008-05-21 07:04:17Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Generic WD179X Disk Controller module for SIMH. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +/*#define DBG_MSG */ + +#include "altairz80_defs.h" + +#if defined (_WIN32) +#include +#endif + +#include "sim_imd.h" +#include "wd179x.h" + +#ifdef DBG_MSG +#define DBG_PRINT(args) printf args +#else +#define DBG_PRINT(args) +#endif + +#define SEEK_MSG 0x01 +#define CMD_MSG 0x04 +#define RD_DATA_MSG 0x08 +#define WR_DATA_MSG 0x10 +#define STATUS_MSG 0x20 +#define VERBOSE_MSG 0x80 + +#define WD179X_MAX_DRIVES 4 +#define WD179X_SECTOR_LEN 8192 + +#define CMD_PHASE 0 +#define EXEC_PHASE 1 +#define DATA_PHASE 2 + +/* Status Bits for Type I Commands */ +#define WD179X_STAT_NOT_READY (1 << 7) +#define WD179X_STAT_WPROT (1 << 6) +#define WD179X_STAT_HLD (1 << 5) +#define WD179X_STAT_SEEK_ERROR (1 << 4) +#define WD179X_STAT_CRC_ERROR (1 << 3) +#define WD179X_STAT_TRACK0 (1 << 2) +#define WD179X_STAT_INDEX (1 << 1) +#define WD179X_STAT_BUSY (1 << 0) + +/* Status Bits for Type II, III Commands */ +#define WD179X_STAT_REC_TYPE (1 << 5) +#define WD179X_STAT_NOT_FOUND (1 << 4) +#define WD179X_STAT_LOST_DATA (1 << 2) +#define WD179X_STAT_DRQ (1 << 1) + + +typedef union { + uint8 raw[WD179X_SECTOR_LEN]; +} SECTOR_FORMAT; + +typedef struct { + UNIT *uptr; + DISK_INFO *imd; + uint8 ntracks; /* number of tracks */ + uint8 nheads; /* number of heads */ + uint32 sectsize; /* sector size, not including pre/postamble */ + uint8 track; /* Current Track */ + uint8 ready; /* Is drive ready? */ +} WD179X_DRIVE_INFO; + +typedef struct { + PNP_INFO pnp; /* Plug-n-Play Information */ + uint8 intrq; /* WD179X Interrupt Request Output (EOJ) */ + uint8 hld; /* WD179X Head Load Output */ + uint8 drq; /* WD179X DMA Request Output */ + uint8 ddens; /* WD179X Double-Density Input */ + uint8 fdc_head; /* H Head Number */ + uint8 sel_drive; /* Currently selected drive */ + uint8 drivetype; /* 8 or 5 depending on disk type. */ + uint8 fdc_status; /* WD179X Status Register */ + uint8 verify; /* WD179X Type 1 command Verify flag */ + uint8 fdc_data; /* WD179X Data Register */ + uint8 fdc_read; /* TRUE when reading */ + uint8 fdc_write; /* TRUE when writing */ + uint8 fdc_read_addr; /* TRUE when READ ADDRESS command is in progress */ + uint8 fdc_multiple; /* TRUE for multi-sector read/write */ + uint16 fdc_datacount; /* Read or Write data remaining transfer length */ + uint16 fdc_dataindex; /* index of current byte in sector data */ + uint8 index_pulse_wait; /* TRUE if waiting for interrupt on next index pulse. */ + uint8 fdc_sector; /* R Record (Sector) */ + uint8 fdc_sec_len; /* N Sector Length */ + int8 step_dir; + WD179X_DRIVE_INFO drive[WD179X_MAX_DRIVES]; +} WD179X_INFO; + +static SECTOR_FORMAT sdata; +extern uint32 PCX; +extern t_stat set_iobase(UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_iobase(FILE *st, UNIT *uptr, int32 val, void *desc); +extern uint32 sim_map_resource(uint32 baseaddr, uint32 size, uint32 resource_type, + int32 (*routine)(const int32, const int32, const int32), uint8 unmap); + +t_stat wd179x_svc (UNIT *uptr); + +/* These are needed for DMA. PIO Mode has not been implemented yet. */ +extern void PutBYTEWrapper(const uint32 Addr, const uint32 Value); +extern uint8 GetBYTEWrapper(const uint32 Addr); +static uint8 Do1793Command(uint8 cCommand); + +#define UNIT_V_WD179X_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WD179X_WLK (1 << UNIT_V_WD179X_WLK) +#define UNIT_V_WD179X_VERBOSE (UNIT_V_UF + 1) /* verbose mode, i.e. show error messages */ +#define UNIT_WD179X_VERBOSE (1 << UNIT_V_WD179X_VERBOSE) +#define WD179X_CAPACITY (77*2*16*256) /* Default Micropolis Disk Capacity */ +#define WD179X_CAPACITY_SSSD (77*1*26*128) /* Single-sided Single Density IBM Diskette1 */ +#define IMAGE_TYPE_DSK 1 /* Flat binary "DSK" image file. */ +#define IMAGE_TYPE_IMD 2 /* ImageDisk "IMD" image file. */ +#define IMAGE_TYPE_CPT 3 /* CP/M Transfer "CPT" image file. */ + +/* WD179X Commands */ +#define WD179X_RESTORE 0x00 /* Type I */ +#define WD179X_SEEK 0x10 /* Type I */ +#define WD179X_STEP 0x20 /* Type I */ +#define WD179X_STEP_U 0x30 /* Type I */ +#define WD179X_STEP_IN 0x40 /* Type I */ +#define WD179X_STEP_IN_U 0x50 /* Type I */ +#define WD179X_STEP_OUT 0x60 /* Type I */ +#define WD179X_STEP_OUT_U 0x70 /* Type I */ +#define WD179X_READ_REC 0x80 /* Type II */ +#define WD179X_READ_RECS 0x90 /* Type II */ +#define WD179X_WRITE_REC 0xA0 /* Type II */ +#define WD179X_WRITE_RECS 0xB0 /* Type II */ +#define WD179X_READ_ADDR 0xC0 /* Type III */ +#define WD179X_FORCE_INTR 0xD0 /* Type IV */ +#define WD179X_READ_TRACK 0xE0 /* Type III */ +#define WD179X_WRITE_TRACK 0xF0 /* Type III */ + +static int32 trace_level = 0xff; /* Disable all tracing by default. */ +static int32 bootstrap = 0; + +static int32 wd179xdev(const int32 port, const int32 io, const int32 data); +static t_stat wd179x_reset(DEVICE *dptr); +int32 find_unit_index (UNIT *uptr); + +WD179X_INFO wd179x_info_data = { { 0x0, 0, 0x30, 4 } }; +WD179X_INFO *wd179x_info = &wd179x_info_data; + +static UNIT wd179x_unit[] = { + { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }, + { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }, + { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 }, + { UDATA (&wd179x_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_DISABLE + UNIT_ROABLE, WD179X_CAPACITY), 58200 } +}; + +static REG wd179x_reg[] = { + { HRDATA (TRACELEVEL, trace_level, 16), }, + { DRDATA (BOOTSTRAP, bootstrap, 10), }, + { NULL } +}; + +static MTAB wd179x_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "IOBASE", "IOBASE", &set_iobase, &show_iobase, NULL }, + { UNIT_WD179X_WLK, 0, "WRTENB", "WRTENB", NULL }, + { UNIT_WD179X_WLK, UNIT_WD179X_WLK, "WRTLCK", "WRTLCK", NULL }, + /* quiet, no warning messages */ + { UNIT_WD179X_VERBOSE, 0, "QUIET", "QUIET", NULL }, + /* verbose, show warning messages */ + { UNIT_WD179X_VERBOSE, UNIT_WD179X_VERBOSE, "VERBOSE", "VERBOSE", NULL }, + { 0 } +}; + +DEVICE wd179x_dev = { + "WD179X", wd179x_unit, wd179x_reg, wd179x_mod, + WD179X_MAX_DRIVES, 10, 31, 1, WD179X_MAX_DRIVES, WD179X_MAX_DRIVES, + NULL, NULL, &wd179x_reset, + NULL, &wd179x_attach, &wd179x_detach, + &wd179x_info_data, (DEV_DISABLE | DEV_DIS), 0, + NULL, NULL, NULL +}; + +/* Unit service routine */ +/* Used to generate INDEX pulses in response to a FORCE_INTR command */ +t_stat wd179x_svc (UNIT *uptr) +{ + + if(wd179x_info->index_pulse_wait == TRUE) { + wd179x_info->index_pulse_wait = FALSE; + wd179x_info->intrq = 1; + } + + return SCPE_OK; +} + + +/* Reset routine */ +static t_stat wd179x_reset(DEVICE *dptr) +{ + PNP_INFO *pnp = (PNP_INFO *)dptr->ctxt; + + if(dptr->flags & DEV_DIS) { /* Disconnect I/O Ports */ + sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, TRUE); + } else { + /* Connect I/O Ports at base address */ + if(sim_map_resource(pnp->io_base, pnp->io_size, RESOURCE_TYPE_IO, &wd179xdev, FALSE) != 0) { + printf("%s: error mapping I/O resource at 0x%04x\n", __FUNCTION__, pnp->io_base); + return SCPE_ARG; + } + } + return SCPE_OK; +} + +extern int32 find_unit_index (UNIT *uptr); + +void wd179x_external_restore(void) +{ + WD179X_DRIVE_INFO *pDrive; + pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; + + if(pDrive->uptr == NULL) { + TRACE_PRINT(SEEK_MSG, + ("WD179X: " ADDRESS_FORMAT " No drive selected, cannot restore." NLP, PCX)) + return; + } + + TRACE_PRINT(SEEK_MSG, + ("WD179X[%d]: " ADDRESS_FORMAT " External Restore drive to track 0" NLP, wd179x_info->sel_drive, PCX)) + + pDrive->track = 0; + +} + +/* Attach routine */ +t_stat wd179x_attach(UNIT *uptr, char *cptr) +{ + char header[4]; + t_stat r; + int32 i = 0; + + r = attach_unit(uptr, cptr); /* attach unit */ + if ( r != SCPE_OK) /* error? */ + return r; + + /* Determine length of this disk */ + uptr->capac = sim_fsize(uptr->fileref); + + i = find_unit_index(uptr); + + if (i == -1) { + return (SCPE_IERR); + } + + DBG_PRINT(("Attach WD179X%d\n", i)); + wd179x_info->drive[i].uptr = uptr; + + /* Default to drive not ready */ + wd179x_info->drive[i].ready = 0; + + if(uptr->capac > 0) { + fgets(header, 4, uptr->fileref); + if(!strcmp(header, "IMD")) { + uptr->u3 = IMAGE_TYPE_IMD; + } else if(!strcmp(header, "CPT")) { + printf("CPT images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_CPT; + wd179x_detach(uptr); + return SCPE_OPENERR; + } else { + printf("DSK images not yet supported\n"); + uptr->u3 = IMAGE_TYPE_DSK; + wd179x_detach(uptr); + return SCPE_OPENERR; + } + } else { + /* creating file, must be DSK format. */ + printf("Cannot create images, must start with a WD179X IMD image.\n"); + uptr->u3 = IMAGE_TYPE_DSK; + wd179x_detach(uptr); + return SCPE_OPENERR; + } + + if (uptr->flags & UNIT_WD179X_VERBOSE) + printf("WD179X%d: attached to '%s', type=%s, len=%d\n", i, cptr, + uptr->u3 == IMAGE_TYPE_IMD ? "IMD" : uptr->u3 == IMAGE_TYPE_CPT ? "CPT" : "DSK", + uptr->capac); + + if(uptr->u3 == IMAGE_TYPE_IMD) { + if(uptr->capac < WD179X_CAPACITY_SSSD) { /*was 318000 but changed to allow 8inch SSSD disks*/ + printf("IMD file too small for use with SIMH.\nCopy an existing file and format it with CP/M.\n"); + } + + if (uptr->flags & UNIT_WD179X_VERBOSE) + printf("--------------------------------------------------------\n"); + wd179x_info->drive[i].imd = diskOpen((uptr->fileref), (uptr->flags & UNIT_WD179X_VERBOSE)); + wd179x_info->drive[i].ready = 1; + if (uptr->flags & UNIT_WD179X_VERBOSE) + printf("\n"); + } else { + wd179x_info->drive[i].imd = NULL; + } + + wd179x_info->fdc_sec_len = 0; /* 128 byte sectors, fixme */ + wd179x_info->sel_drive = 0; + + return SCPE_OK; +} + + +/* Detach routine */ +t_stat wd179x_detach(UNIT *uptr) +{ + t_stat r; + int8 i; + + i = find_unit_index(uptr); + + if (i == -1) { + return (SCPE_IERR); + } + + DBG_PRINT(("Detach WD179X%d\n", i)); + diskClose(wd179x_info->drive[i].imd); + wd179x_info->drive[i].ready = 0; + + r = detach_unit(uptr); /* detach unit */ + if ( r != SCPE_OK) + return r; + + return SCPE_OK; +} + + +static int32 wd179xdev(const int32 port, const int32 io, const int32 data) +{ + DBG_PRINT(("WD179X: " ADDRESS_FORMAT " %s, Port 0x%02x Data 0x%02x" NLP, + PCX, io ? "OUT" : " IN", port, data)); + if(io) { + WD179X_Write(port, data); + return 0; + } else { + return(WD179X_Read(port)); + } +} + +static uint8 floorlog2(unsigned int n) +{ + /* Compute log2(n) */ + uint8 r = 0; + if(n >= 1<<16) { n >>=16; r += 16; } + if(n >= 1<< 8) { n >>= 8; r += 8; } + if(n >= 1<< 4) { n >>= 4; r += 4; } + if(n >= 1<< 2) { n >>= 2; r += 2; } + if(n >= 1<< 1) { r += 1; } + return ((n == 0) ? (0xFF) : r); /* 0xFF is error return value */ +} + +uint8 WD179X_Read(const uint32 Addr) +{ + uint8 cData; + WD179X_DRIVE_INFO *pDrive; + unsigned int flags; + unsigned int readlen; + int status; + + pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; + + if(pDrive->uptr == NULL) { + return 0xFF; + } + + cData = 0x00; + + switch(Addr & 0x3) { + case WD179X_STATUS: + cData = (pDrive->ready == 0) ? WD179X_STAT_NOT_READY : 0; + cData |= wd179x_info->fdc_status; /* Status Register */ + TRACE_PRINT(STATUS_MSG, + ("WD179X: " ADDRESS_FORMAT " RD STATUS = 0x%02x" NLP, PCX, cData)) + wd179x_info->intrq = 0; + break; + case WD179X_TRACK: + cData = pDrive->track; + TRACE_PRINT(STATUS_MSG, + ("WD179X: " ADDRESS_FORMAT " RD TRACK = 0x%02x" NLP, PCX, cData)) + break; + case WD179X_SECTOR: + cData = wd179x_info->fdc_sector; + TRACE_PRINT(STATUS_MSG, + ("WD179X: " ADDRESS_FORMAT " RD SECT = 0x%02x" NLP, PCX, cData)) + break; + case WD179X_DATA: + cData = 0xFF; /* Return High-Z data */ + if(wd179x_info->fdc_read == TRUE) { + if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) { + cData = sdata.raw[wd179x_info->fdc_dataindex]; + if(wd179x_info->fdc_read_addr == TRUE) { + TRACE_PRINT(STATUS_MSG, + ("WD179X[%d]: " ADDRESS_FORMAT " READ_ADDR[%d] = 0x%02x" NLP, wd179x_info->sel_drive, PCX, wd179x_info->fdc_dataindex, cData)) + } + + wd179x_info->fdc_dataindex++; + if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) { + if(wd179x_info->fdc_multiple == FALSE) { + wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ + wd179x_info->drq = 0; + wd179x_info->intrq = 1; + wd179x_info->fdc_read = FALSE; + wd179x_info->fdc_read_addr = FALSE; + } else { + + /* Compute Sector Size */ + wd179x_info->fdc_sec_len = floorlog2( + pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; + if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/ + printf("Invalid sector size!\n"); + } + + wd179x_info->fdc_sector ++; + TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " MULTI_READ_REC, T:%d/S:%d/N:%d, %s, len=%d" NLP, + wd179x_info->sel_drive, + PCX, + pDrive->track, + wd179x_info->fdc_head, + wd179x_info->fdc_sector, + wd179x_info->ddens ? "DD" : "SD", + 128 << wd179x_info->fdc_sec_len)); + + status = sectRead(pDrive->imd, + pDrive->track, + wd179x_info->fdc_head, + wd179x_info->fdc_sector, + sdata.raw, + 128 << wd179x_info->fdc_sec_len, + &flags, + &readlen); + + if(status != -1) { + wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */ + wd179x_info->drq = 1; + wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; + wd179x_info->fdc_dataindex = 0; + wd179x_info->fdc_read = TRUE; + wd179x_info->fdc_read_addr = FALSE; + } else { + wd179x_info->fdc_status = 0; /* Clear DRQ, BUSY */ + wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; + wd179x_info->drq = 0; + wd179x_info->intrq = 1; + wd179x_info->fdc_read = FALSE; + wd179x_info->fdc_read_addr = FALSE; + } + } + } + } + } + break; + } + + return (cData); +} + +/* + * Command processing happens in three stages: + * 1. Flags and initial conditions are set up based on the Type of the command. + * 2. The execution phase takes place. + * 3. Status is updated based on the Type and outcome of the command execution. + * + * See the WD179x-02 Datasheet available on www.hartetechnologies.com/manuals/ + * + */ +static uint8 Do1793Command(uint8 cCommand) +{ + uint8 result = 0; + WD179X_DRIVE_INFO *pDrive; + unsigned int flags; + unsigned int readlen; + + pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; + + if(pDrive->uptr == NULL) { + return 0xFF; + } + + if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { + if((cCommand & 0xF0) != WD179X_FORCE_INTR) { + printf("WD179X[%d]: ERROR: Command 0x%02x ignored because controller is BUSY\n", wd179x_info->sel_drive, cCommand); + } + return 0xFF; + } + + /* Extract Type-specific command flags, and set initial conditions */ + switch(cCommand & 0xF0) { + /* Type I Commands */ + case WD179X_RESTORE: + case WD179X_SEEK: + case WD179X_STEP: + case WD179X_STEP_U: + case WD179X_STEP_IN: + case WD179X_STEP_IN_U: + case WD179X_STEP_OUT: + case WD179X_STEP_OUT_U: + wd179x_info->fdc_status |= WD179X_STAT_BUSY; /* Set BUSY */ + wd179x_info->fdc_status &= ~(WD179X_STAT_CRC_ERROR | WD179X_STAT_SEEK_ERROR | WD179X_STAT_DRQ); + wd179x_info->intrq = 0; + wd179x_info->hld = cCommand && 0x08; + wd179x_info->verify = cCommand & 0x04; + break; + /* Type II Commands */ + case WD179X_READ_REC: + case WD179X_READ_RECS: + case WD179X_WRITE_REC: + case WD179X_WRITE_RECS: + wd179x_info->fdc_status = WD179X_STAT_BUSY; /* Set BUSY, clear all others */ + wd179x_info->intrq = 0; + wd179x_info->hld = 1; /* Load the head immediately, E Flag not checked. */ + break; + /* Type III Commands */ + case WD179X_READ_ADDR: + case WD179X_READ_TRACK: + case WD179X_WRITE_TRACK: + /* Type IV Commands */ + case WD179X_FORCE_INTR: + default: + break; + } + + switch(cCommand & 0xF0) { + /* Type I Commands */ + case WD179X_RESTORE: + TRACE_PRINT(CMD_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=RESTORE" NLP, wd179x_info->sel_drive, PCX)); + pDrive->track = 0; + wd179x_info->intrq = 1; + break; + case WD179X_SEEK: + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=SEEK, track=%d, new=%d" NLP, wd179x_info->sel_drive, PCX, pDrive->track, wd179x_info->fdc_data)); + pDrive->track = wd179x_info->fdc_data; + break; + case WD179X_STEP: + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP" NLP, wd179x_info->sel_drive, PCX)); + break; + case WD179X_STEP_U: + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_U dir=%d" NLP, wd179x_info->sel_drive, PCX, wd179x_info->step_dir)); + if(wd179x_info->step_dir == 1) { + if(pDrive->track < 255) pDrive->track++; + } else if (wd179x_info->step_dir == -1) { + if(pDrive->track > 0) pDrive->track--; + } else { + printf("WD179X[%d]: Error, undefined direction for STEP\n", wd179x_info->sel_drive); + } + break; + case WD179X_STEP_IN: + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN" NLP, wd179x_info->sel_drive, PCX)); + break; + case WD179X_STEP_IN_U: + if(pDrive->track < 255) pDrive->track++; + wd179x_info->step_dir = 1; + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_IN_U, Track=%d" NLP, + wd179x_info->sel_drive, PCX, pDrive->track)); + break; + case WD179X_STEP_OUT: + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT" NLP, + wd179x_info->sel_drive, PCX)); + break; + case WD179X_STEP_OUT_U: + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=STEP_OUT_U" NLP, + wd179x_info->sel_drive, PCX)); + if(pDrive->track > 0) pDrive->track--; + wd179x_info->step_dir = -1; + break; + /* Type II Commands */ + case WD179X_READ_REC: + case WD179X_READ_RECS: + /* Compute Sector Size */ + wd179x_info->fdc_sec_len = floorlog2( + pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; + if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/ + printf("Invalid sector size!\n"); + } + + wd179x_info->fdc_multiple = (cCommand & 0x10) ? TRUE : FALSE; + TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_REC, T:%d/S:%d/N:%d, %s, %s len=%d" NLP, + wd179x_info->sel_drive, + PCX, pDrive->track, + wd179x_info->fdc_head, + wd179x_info->fdc_sector, + wd179x_info->fdc_multiple ? "Multiple" : "Single", + wd179x_info->ddens ? "DD" : "SD", + 128 << wd179x_info->fdc_sec_len)); + + sectRead(pDrive->imd, + pDrive->track, + wd179x_info->fdc_head, + wd179x_info->fdc_sector, + sdata.raw, + 128 << wd179x_info->fdc_sec_len, + &flags, + &readlen); + + if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) { + printf("Sector not found\n"); + wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; /* Sector not found */ + wd179x_info->intrq = 1; + } else { + wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ + wd179x_info->drq = 1; + wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; + wd179x_info->fdc_dataindex = 0; + wd179x_info->fdc_write = FALSE; + wd179x_info->fdc_read = TRUE; + wd179x_info->fdc_read_addr = FALSE; + } + break; + case WD179X_WRITE_RECS: + printf("-->> Error: WRITE_RECS not implemented." NLP); + case WD179X_WRITE_REC: + /* Compute Sector Size */ + wd179x_info->fdc_sec_len = floorlog2( + pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; + if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/ + printf("Invalid sector size!\n"); + } + + TRACE_PRINT(WR_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_REC, T:%d/S:%d/N:%d, %s." NLP, + wd179x_info->sel_drive, + PCX, + pDrive->track, + cCommand&&0x08, + wd179x_info->fdc_sector, + (cCommand & 0x10) ? "Multiple" : "Single")); + wd179x_info->fdc_status |= (WD179X_STAT_DRQ); /* Set DRQ */ + wd179x_info->drq = 1; + wd179x_info->fdc_datacount = 128 << wd179x_info->fdc_sec_len; + wd179x_info->fdc_dataindex = 0; + wd179x_info->fdc_write = TRUE; + wd179x_info->fdc_read = FALSE; + wd179x_info->fdc_read_addr = FALSE; + + sdata.raw[wd179x_info->fdc_dataindex] = wd179x_info->fdc_data; + break; + /* Type III Commands */ + case WD179X_READ_ADDR: + TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_ADDR, T:%d/S:%d, %s" NLP, + wd179x_info->sel_drive, + PCX, + pDrive->track, + wd179x_info->fdc_head, + wd179x_info->ddens ? "DD" : "SD")); + + /* Compute Sector Size */ + wd179x_info->fdc_sec_len = floorlog2( + pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].sectsize) - 7; + if(wd179x_info->fdc_sec_len == 0xF8) { /*Error calculating N*/ + printf("Invalid sector size!\n"); + } + + if(IMD_MODE_MFM(pDrive->imd->track[pDrive->track][wd179x_info->fdc_head].mode) != (wd179x_info->ddens)) { + wd179x_info->fdc_status = WD179X_STAT_NOT_FOUND; /* Sector not found */ + wd179x_info->intrq = 1; + } else { + wd179x_info->fdc_status = (WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Set DRQ, BUSY */ + wd179x_info->drq = 1; + wd179x_info->fdc_datacount = 6; + wd179x_info->fdc_dataindex = 0; + wd179x_info->fdc_read = TRUE; + wd179x_info->fdc_read_addr = TRUE; + + sdata.raw[0] = pDrive->track; + sdata.raw[1] = wd179x_info->fdc_head; + sdata.raw[2] = wd179x_info->fdc_sector; + sdata.raw[3] = wd179x_info->fdc_sec_len; + sdata.raw[4] = 0xAA; /* CRC1 */ + sdata.raw[5] = 0x55; /* CRC2 */ + + wd179x_info->fdc_sector = pDrive->track; + wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ + wd179x_info->intrq = 1; + } + break; + case WD179X_READ_TRACK: + TRACE_PRINT(RD_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=READ_TRACK" NLP, wd179x_info->sel_drive, PCX)); + printf("-->> Error: READ_TRACK not implemented." NLP); + break; + case WD179X_WRITE_TRACK: + TRACE_PRINT(WR_DATA_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=WRITE_TRACK" NLP, wd179x_info->sel_drive, PCX)); + printf("-->> Error: WRITE_TRACK not implemented." NLP); + break; + /* Type IV Commands */ + case WD179X_FORCE_INTR: + TRACE_PRINT(CMD_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " CMD=FORCE_INTR" NLP, wd179x_info->sel_drive, PCX)); + if((cCommand & 0x0F) == 0) { /* I0-I3 == 0, no intr, but clear BUSY and terminate command */ + wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ + wd179x_info->drq = 0; + wd179x_info->fdc_write = FALSE; + wd179x_info->fdc_read = FALSE; + wd179x_info->fdc_read_addr = FALSE; + wd179x_info->fdc_datacount = 0; + wd179x_info->fdc_dataindex = 0; + } else { + if(wd179x_info->fdc_status & WD179X_STAT_BUSY) { /* Force Interrupt when command is pending */ + } else { /* Command not pending, clear status */ + wd179x_info->fdc_status = 0; + } + + if(cCommand & 0x04) { + wd179x_info->index_pulse_wait = TRUE; + sim_activate (wd179x_unit, wd179x_info->drivetype == 8 ? 48500 : 58200); /* Generate INDEX pulse */ + } else { + wd179x_info->intrq = 1; + } + wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ + } + break; + default: + printf("WD179X[%d]: Unknown WD179X command 0x%02x.\n", wd179x_info->sel_drive, cCommand); + break; + } + + /* Post processing of Type-specific command */ + switch(cCommand & 0xF0) { + /* Type I Commands */ + case WD179X_RESTORE: + case WD179X_SEEK: + case WD179X_STEP: + case WD179X_STEP_U: + case WD179X_STEP_IN: + case WD179X_STEP_IN_U: + case WD179X_STEP_OUT: + case WD179X_STEP_OUT_U: + if(wd179x_info->verify) { /* Verify the selected track/head is ok. */ + TRACE_PRINT(SEEK_MSG, ("WD179X[%d]: " ADDRESS_FORMAT " Verify ", wd179x_info->sel_drive, PCX)); + if(sectSeek(pDrive->imd, pDrive->track, wd179x_info->fdc_head) != 0) { + TRACE_PRINT(SEEK_MSG, ("FAILED" NLP)); + wd179x_info->fdc_status |= WD179X_STAT_NOT_FOUND; + } else { + TRACE_PRINT(SEEK_MSG, ("Ok" NLP)); + } + } + + if(pDrive->track == 0) { + wd179x_info->fdc_status |= WD179X_STAT_TRACK0; + } else { + wd179x_info->fdc_status &= ~(WD179X_STAT_TRACK0); + } + + wd179x_info->fdc_status &= ~(WD179X_STAT_BUSY); /* Clear BUSY */ + wd179x_info->intrq = 1; + break; + /* Type II Commands */ + case WD179X_READ_REC: + case WD179X_READ_RECS: + case WD179X_WRITE_REC: + case WD179X_WRITE_RECS: + /* Type III Commands */ + case WD179X_READ_ADDR: + case WD179X_READ_TRACK: + case WD179X_WRITE_TRACK: + /* Type IV Commands */ + case WD179X_FORCE_INTR: + default: + break; + } + + + return result; +} + +uint8 WD179X_Write(const uint32 Addr, uint8 cData) +{ + WD179X_DRIVE_INFO *pDrive; + unsigned int flags; + unsigned int writelen; + + pDrive = &wd179x_info->drive[wd179x_info->sel_drive]; + + if(pDrive->uptr == NULL) { + return 0xFF; + } + + switch(Addr & 0x3) { + case WD179X_STATUS: + TRACE_PRINT(STATUS_MSG, + ("WD179X: " ADDRESS_FORMAT " WR CMD = 0x%02x" NLP, PCX, cData)) + wd179x_info->fdc_read = FALSE; + wd179x_info->fdc_write = FALSE; + wd179x_info->fdc_datacount = 0; + wd179x_info->fdc_dataindex = 0; + + Do1793Command(cData); + break; + case WD179X_TRACK: + TRACE_PRINT(STATUS_MSG, + ("WD179X: " ADDRESS_FORMAT " WR TRACK = 0x%02x" NLP, PCX, cData)) + pDrive->track = cData; + break; + case WD179X_SECTOR: /* Sector Register */ + TRACE_PRINT(STATUS_MSG, + ("WD179X: " ADDRESS_FORMAT " WR SECT = 0x%02x" NLP, PCX, cData)) + wd179x_info->fdc_sector = cData; + break; + case WD179X_DATA: + TRACE_PRINT(STATUS_MSG, + ("WD179X: " ADDRESS_FORMAT " WR DATA = 0x%02x" NLP, PCX, cData)) + if(wd179x_info->fdc_write == TRUE) { + if(wd179x_info->fdc_dataindex < wd179x_info->fdc_datacount) { + sdata.raw[wd179x_info->fdc_dataindex] = cData; + + wd179x_info->fdc_dataindex++; + if(wd179x_info->fdc_dataindex == wd179x_info->fdc_datacount) { + wd179x_info->fdc_status &= ~(WD179X_STAT_DRQ | WD179X_STAT_BUSY); /* Clear DRQ, BUSY */ + wd179x_info->drq = 0; + wd179x_info->intrq = 1; + + sectWrite(pDrive->imd, + pDrive->track, + wd179x_info->fdc_head, + wd179x_info->fdc_sector, + sdata.raw, + 128 << wd179x_info->fdc_sec_len, + &flags, + &writelen); + + wd179x_info->fdc_write = FALSE; + } + } + } + wd179x_info->fdc_data = cData; + break; + } + + return 0; +} + diff --git a/AltairZ80/wd179x.h b/AltairZ80/wd179x.h new file mode 100644 index 0000000..1ac1f19 --- /dev/null +++ b/AltairZ80/wd179x.h @@ -0,0 +1,67 @@ +/************************************************************************* + * * + * $Id: wd179x.h 1907 2008-05-21 07:04:17Z hharte $ * + * * + * Copyright (c) 2007-2008 Howard M. Harte. * + * http://www.hartetec.com * + * * + * Permission is hereby granted, free of charge, to any person obtaining * + * a copy of this software and associated documentation files (the * + * "Software"), to deal in the Software without restriction, including * + * without limitation the rights to use, copy, modify, merge, publish, * + * distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to * + * the following conditions: * + * * + * The above copyright notice and this permission notice shall be * + * included in all copies or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * + * NONINFRINGEMENT. IN NO EVENT SHALL HOWARD M. HARTE BE LIABLE FOR ANY * + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + * * + * Except as contained in this notice, the name of Howard M. Harte shall * + * not be used in advertising or otherwise to promote the sale, use or * + * other dealings in this Software without prior written authorization * + * Howard M. Harte. * + * * + * SIMH Interface based on altairz80_hdsk.c, by Peter Schorn. * + * * + * Module Description: * + * Generic Intel 8272 Disk Controller module for SIMH. * + * * + * Environment: * + * User mode only * + * * + *************************************************************************/ + +extern t_stat wd179x_attach(UNIT *uptr, char *cptr); +extern t_stat wd179x_detach(UNIT *uptr); +extern uint8 WD179X_Set_DMA(const uint32 dma_addr); +extern uint8 WD179X_Read(const uint32 Addr); +extern uint8 WD179X_Write(const uint32 Addr, uint8 cData); + +extern void wd179x_external_restore(void); + +#define WD179X_FDC_MSR 0 /* R=FDC Main Status Register, W=Drive Select Register */ +#define WD179X_FDC_DATA 1 /* R/W FDC Data Register */ + +#define WD179X_STATUS 0 +#define WD179X_TRACK 1 +#define WD179X_SECTOR 2 +#define WD179X_DATA 3 + +typedef struct { + PNP_INFO pnp; /* Plug-n-Play Information */ + uint8 intrq; /* WD179X Interrupt Request Output (EOJ) */ + uint8 hld; /* WD179X Head Load Output */ + uint8 drq; /* WD179X DMA Request Output */ + uint8 ddens; /* WD179X Double-Density Input */ + uint8 fdc_head; /* H Head Number */ + uint8 sel_drive; /* Currently selected drive */ + uint8 drivetype; /* 8 or 5 depending on disk type. */ +} WD179X_INFO_PUB; diff --git a/GRI/gri_cpu.c b/GRI/gri_cpu.c new file mode 100644 index 0000000..8f86b9e --- /dev/null +++ b/GRI/gri_cpu.c @@ -0,0 +1,1075 @@ +/* gri_cpu.c: GRI-909 CPU simulator + + Copyright (c) 2001-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu GRI-909/GRI-99 CPU + + 14-Jan-08 RMS Added GRI-99 support + 28-Apr-07 RMS Removed clock initialization + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 18-Jul-04 RMS Fixed missing ao_update calls in AX, AY write + 17-Jul-04 RMS Revised MSR, EAO based on additional documentation + 14-Mar-03 RMS Fixed bug in SC queue tracking + + The system state for the GRI-909/GRI-99 is: + + AX<15:0> arithmetic input + AY<15:0> arithmetic input + BSW<15:0> byte swapper + BPK<15:0> byte packer + GR[0:5]<15:0> extended general registers + MSR<15:0> machine status register + TRP<15:0> trap register (subroutine return) + SC<14:0> sequence counter + XR<15:0> index register (GRI-99 only) + + The GRI-909 has, nominally, just one instruction format: move. + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | source | op | destination | move + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <6:9> operation + + xx1x complement + 01xx add 1 + 10xx rotate left 1 + 11xx rotate right 1 + + In fact, certain of the source and destination operators have side + effects, yielding four additional instruction formats: function out, + skip on function, memory reference, and conditional jump. + + The function out format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0 1 0| pulse | destination | function out + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The skip on function format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | source | skip |rv| 0 0 0 0 1 0| skip function + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The memory reference format is (src and/or dst = 006): + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | source | op | mode| destination | memory ref + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | address or immediate | + +-----------------------------------------------+ + + <6:9> operation + + xx0x direct, ea = M[SC+1] + xx1x immediate, ea = SC+1 + xxx1 indirect, M[ea] = M[ea]+1, then ea = M[ea] + 01xx add 1 + 10xx rotate left 1 + 11xx rotate right 1 + + The conditional jump format is (src != 006): + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | source | cond|rv|df| 0 0 0 0 1 1| cond jump + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | jump address | + +-----------------------------------------------+ + + <6:9> operation + + xxx0 direct, ea = M[SC+1] + xxx1 indirect, ea = M[SC+1], M[ea] = M[ea]+1, + then ea = M[ea] + xx1x reverse conditional sense + x1xx jump if src == 0 + 1xxx jump if src < 0 + + This routine is the instruction decode routine for the GRI-909. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unknown source or destination and STOP_OPR flag set + I/O error in I/O simulator + + 2. Interrupts. The interrupt structure is kept in two parallel variables: + + dev_done device done flags + ISR interrupt status register (enables) + + In addition, there is a master interrupt enable, and a one cycle + interrupt defer, both kept in dev_done. + + 3. Non-existent memory. On the GRI-909, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + gri_defs.h add interrupt request definition + gri_cpu.c add dev_tab table entry + gri_sys.c add sim_devices table entry +*/ + +#include "gri_defs.h" + +#define SCQ_SIZE 64 /* must be 2**n */ +#define SCQ_MASK (SCQ_SIZE - 1) +#define SCQ_ENTRY scq[scq_p = (scq_p - 1) & SCQ_MASK] = SC +#define UNIT_V_AO (UNIT_V_UF + 0) /* AO */ +#define UNIT_AO (1u << UNIT_V_AO) +#define UNIT_V_EAO (UNIT_V_UF + 1) /* EAO */ +#define UNIT_EAO (1u << UNIT_V_EAO) +#define UNIT_V_GPR (UNIT_V_UF + 2) /* GPR */ +#define UNIT_GPR (1u << UNIT_V_GPR) +#define UNIT_V_BSWPK (UNIT_V_UF + 3) /* BSW-BPK */ +#define UNIT_BSWPK (1u << UNIT_V_BSWPK) +#define UNIT_V_GRI99 (UNIT_V_UF + 4) /* GRI-99 */ +#define UNIT_GRI99 (1u << UNIT_V_GRI99) +#define UNIT_V_MSIZE (UNIT_V_UF + 5) /* dummy mask */ +#define UNIT_MSIZE (1u << UNIT_V_MSIZE) + +#define IDX_ADD(x) ((((cpu_unit.flags & UNIT_GRI99) && ((x) & INDEX))? ((x) + XR): (x)) & AMASK) + +uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ +uint32 SC; /* sequence cntr */ +uint32 AX, AY, AO; /* arithmetic unit */ +uint32 IR; /* instr reg */ +uint32 MA; /* memory addr */ +uint32 TRP; /* subr return */ +uint32 MSR; /* machine status */ +uint32 ISR; /* interrupt status */ +uint32 BSW, BPK; /* byte swap, pack */ +uint32 GR[6]; /* extended general regs */ +uint32 SWR; /* switch reg */ +uint32 DR; /* display register */ +uint32 XR; /* index register */ +uint32 thwh = 0; /* thumbwheel */ +uint32 dev_done = 0; /* device flags */ +uint32 bkp = 0; /* bkpt pending */ +uint32 stop_opr = 1; /* stop ill operator */ +int16 scq[SCQ_SIZE] = { 0 }; /* PC queue */ +int32 scq_p = 0; /* PC queue ptr */ +REG *scq_r = NULL; /* PC queue reg ptr */ + +extern int32 sim_interval; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat bus_op (uint32 src, uint32 op, uint32 dst); + +/* Dispatch tables for source, dest, function out, skip on function */ + +uint32 no_rd (uint32 src); +t_stat no_wr (uint32 dst, uint32 val); +t_stat no_fo (uint32 op); +uint32 no_sf (uint32 op); +uint32 zero_rd (uint32 src); +t_stat zero_wr (uint32 dst, uint32 val); +t_stat zero_fo (uint32 op); +uint32 zero_sf (uint32 op); +uint32 ir_rd (uint32 op); +t_stat ir_fo (uint32 op); +uint32 trp_rd (uint32 src); +t_stat trp_wr (uint32 dst, uint32 val); +uint32 atrp_rd (uint32 src); +t_stat atrp_wr (uint32 dst, uint32 val); +uint32 isr_rd (uint32 src); +t_stat isr_wr (uint32 dst, uint32 val); +t_stat isr_fo (uint32 op); +uint32 isr_sf (uint32 op); +uint32 ma_rd (uint32 src); +uint32 mem_rd (uint32 src); +t_stat mem_wr (uint32 dst, uint32 val); +uint32 sc_rd (uint32 src); +t_stat sc_wr (uint32 dst, uint32 val); +uint32 swr_rd (uint32 src); +uint32 ax_rd (uint32 src); +t_stat ax_wr (uint32 dst, uint32 val); +uint32 ay_rd (uint32 src); +t_stat ay_wr (uint32 dst, uint32 val); +uint32 ao_rd (uint32 src); +t_stat ao_fo (uint32 op); +uint32 ao_sf (uint32 op); +uint32 ao_update (void); +t_stat eao_fo (uint32 op); +uint32 msr_rd (uint32 src); +t_stat msr_wr (uint32 dst, uint32 val); +uint32 bsw_rd (uint32 src); +t_stat bsw_wr (uint32 dst, uint32 val); +uint32 bpk_rd (uint32 src); +t_stat bpk_wr (uint32 dst, uint32 val); +uint32 gr_rd (uint32 src); +t_stat gr_wr (uint32 dst, uint32 val); +uint32 xr_rd (uint32 src); +t_stat xr_wr (uint32 dst, uint32 val); + +extern t_stat rtc_fo (uint32 op); +extern uint32 rtc_sf (uint32 op); +extern uint32 hsrp_rd (uint32 src); +extern t_stat hsrp_wr (uint32 dst, uint32 val); +extern t_stat hsrp_fo (uint32 op); +extern uint32 hsrp_sf (uint32 op); +extern uint32 tty_rd (uint32 src); +extern t_stat tty_wr (uint32 dst, uint32 val); +extern t_stat tty_fo (uint32 op); +extern uint32 tty_sf (uint32 op); + +struct gdev dev_tab[64] = { + { &zero_rd, &zero_wr, &zero_fo, &zero_sf }, /* 00: zero */ + { &ir_rd, &zero_wr, &ir_fo, &zero_sf }, /* ir */ + { &no_rd, &no_wr, &no_fo, &no_sf }, /* fo/sf */ + { &trp_rd, &trp_wr, &zero_fo, &zero_sf }, /* trp */ + { &isr_rd, &isr_wr, &isr_fo, &isr_sf }, /* isr */ + { &ma_rd, &no_wr, &no_fo, &no_sf }, /* ma */ + { &mem_rd, &mem_wr, &zero_fo, &zero_sf }, /* memory */ + { &sc_rd, &sc_wr, &zero_fo, &zero_sf }, /* sc */ + { &swr_rd, &no_wr, &no_fo, &no_sf }, /* swr */ + { &ax_rd, &ax_wr, &zero_fo, &zero_sf }, /* ax */ + { &ay_rd, &ay_wr, &zero_fo, &zero_sf }, /* ay */ + { &ao_rd, &zero_wr, &ao_fo, &ao_sf }, /* ao */ + { &zero_rd, &zero_wr, &eao_fo, &zero_sf }, /* eao */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &msr_rd, &msr_wr, &zero_fo, &zero_sf }, /* msr */ + { &no_rd, &no_wr, &no_fo, &no_sf }, /* 20 */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &xr_rd, &xr_wr, &no_fo, &no_sf }, /* xr */ + { &atrp_rd, &atrp_wr, &no_fo, &no_sf }, + { &bsw_rd, &bsw_wr, &no_fo, &no_sf }, /* bsw */ + { &bpk_rd, &bpk_wr, &no_fo, &no_sf }, /* bpk */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* 30: gr1 */ + { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr2 */ + { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr3 */ + { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr4 */ + { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr5 */ + { &gr_rd, &gr_wr, &zero_fo, &zero_sf }, /* gr6 */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, /* 40 */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, /* 50 */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, /* 60 */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, /* 70 */ + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &no_rd, &no_wr, &no_fo, &no_sf }, + { &zero_rd, &zero_wr, &rtc_fo, &rtc_sf }, /* rtc */ + { &hsrp_rd, &hsrp_wr, &hsrp_fo, &hsrp_sf }, /* hsrp */ + { &tty_rd, &tty_wr, &tty_fo, &tty_sf } /* tty */ + }; + +static const int32 vec_map[16] = { + VEC_TTO, VEC_TTI, VEC_HSP, VEC_HSR, + -1, -1, -1, -1, + -1, -1, -1, VEC_RTC, + -1, -1, -1, -1 + }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_AO+UNIT_EAO+UNIT_GPR, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (SC, SC, 15) }, + { ORDATA (AX, AX, 16) }, + { ORDATA (AY, AY, 16) }, + { ORDATA (AO, AO, 16), REG_RO }, + { ORDATA (TRP, TRP, 16) }, + { ORDATA (MSR, MSR, 16) }, + { ORDATA (ISR, ISR, 16) }, + { ORDATA (BSW, BSW, 16) }, + { ORDATA (BPK, BPK, 16) }, + { ORDATA (GR1, GR[0], 16) }, + { ORDATA (GR2, GR[1], 16) }, + { ORDATA (GR3, GR[2], 16) }, + { ORDATA (GR4, GR[3], 16) }, + { ORDATA (GR5, GR[4], 16) }, + { ORDATA (GR6, GR[5], 16) }, + { ORDATA (XR, XR, 16) }, + { FLDATA (BOV, MSR, MSR_V_BOV) }, + { FLDATA (L, MSR, MSR_V_L) }, + { GRDATA (FOA, MSR, 8, 2, MSR_V_FOA) }, + { FLDATA (SOV, MSR, MSR_V_SOV) }, + { FLDATA (AOV, MSR, MSR_V_AOV) }, + { ORDATA (IR, IR, 16), REG_RO }, + { ORDATA (MA, MA, 16), REG_RO }, + { ORDATA (SWR, SWR, 16) }, + { ORDATA (DR, DR, 16) }, + { ORDATA (THW, thwh, 6) }, + { ORDATA (IREQ, dev_done, INT_V_NODEF) }, + { FLDATA (ION, dev_done, INT_V_ON) }, + { FLDATA (INODEF, dev_done, INT_V_NODEF) }, + { FLDATA (BKP, bkp, 0) }, + { BRDATA (SCQ, scq, 8, 15, SCQ_SIZE), REG_RO + REG_CIRC }, + { ORDATA (SCQP, scq_p, 6), REG_HRO }, + { FLDATA (STOP_OPR, stop_opr, 0) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_GRI99, UNIT_GRI99, "GRI99", "GRI99", NULL }, + { UNIT_GRI99, 0, "GRI909", "GRI909", NULL }, + { UNIT_AO, UNIT_AO, "AO", "AO", NULL }, + { UNIT_AO, 0, "no AO", "NOAO", NULL }, + { UNIT_EAO, UNIT_EAO, "EAO", "EAO", NULL }, + { UNIT_EAO, 0, "no EAO", "NOEAO", NULL }, + { UNIT_GPR, UNIT_GPR, "GPR", "GPR", NULL }, + { UNIT_GPR, 0, "no GPR", "NOGPR", NULL }, + { UNIT_BSWPK, UNIT_BSWPK, "BSW-BPK", "BSW-BPK", NULL }, + { UNIT_BSWPK, 0, "no BSW-BPK", "NOBSW-BPK", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL + }; + +t_stat sim_instr (void) +{ +uint32 src, dst, op, t, jmp; +t_stat reason; + +/* Restore register state */ + +SC = SC & AMASK; /* load local PC */ +reason = 0; +ao_update (); /* update AO */ + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + + if (bkp) { /* breakpoint? */ + bkp = 0; /* clear request */ + dev_done = dev_done & ~INT_ON; /* int off */ + M[VEC_BKP] = SC; /* save SC */ + SC = VEC_BKP + 1; /* new SC */ + } + + else if ((dev_done & (INT_PENDING | ISR)) > (INT_PENDING)) { /* intr? */ + int32 i, vec; + t = dev_done & ISR; /* find hi pri */ + for (i = 15; i >= 0; i--) { + if ((t >> i) & 1) break; + } + if ((i < 0) || ((vec = vec_map[i]) < 0)) { /* undefined? */ + reason = STOP_ILLINT; /* stop */ + break; + } + dev_done = dev_done & ~INT_ON; /* int off */ + M[vec] = SC; /* save SC */ + SC = vec + 1; /* new SC */ + continue; + } + + if (sim_brk_summ && sim_brk_test (SC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + MA = SC; /* set mem addr */ + IR = M[MA]; /* fetch instr */ + dev_done = dev_done | INT_NODEF; /* clr ion defer */ + sim_interval = sim_interval - 1; + +/* Decode instruction types */ + + src = I_GETSRC (IR); /* src unit */ + dst = I_GETDST (IR); /* dst unit */ + op = I_GETOP (IR); /* bus op */ + + if (src == U_FSK) { /* func out? */ + reason = dev_tab[dst].FO (op); /* send function */ + SC = (SC + 1) & AMASK; /* incr SC */ + } + + else if (dst == U_FSK) { /* skip func? */ + t = dev_tab[src].SF (op & ~1); /* issue SF */ + reason = t >> SF_V_REASON; + if ((t ^ op) & 1) SC = SC + 2; /* skip? */ + SC = (SC + 1) & AMASK; /* incr SC */ + } + + else if ((src != U_MEM) && (dst == U_TRP)) { /* cond jump */ + t = dev_tab[src].Src (src); /* get source */ + switch (op >> 1) { /* case on jump */ + + case 00: /* never */ + jmp = 0; + break; + + case 01: /* always */ + jmp = 1; + break; + + case 02: /* src == 0 */ + jmp = (t == 0); + break; + + case 03: /* src != 0 */ + jmp = (t != 0); + break; + + case 04: /* src < 0 */ + jmp = (t >= SIGN); + break; + + case 05: /* src >= 0 */ + jmp = (t < SIGN); + break; + + case 06: /* src <= 0 */ + jmp = (t == 0) || (t & SIGN); + break; + + case 07: /* src > 0 */ + jmp = (t != 0) && !(t & SIGN); + break; + } + + if (jmp) { /* jump taken? */ + SCQ_ENTRY; /* save SC */ + SC = (SC + 1) & AMASK; /* incr SC once */ + MA = M[SC]; /* get jump addr */ + MA = IDX_ADD (MA); /* index? */ + if (op & TRP_DEF) { /* defer? */ + t = (M[MA] + 1) & DMASK; /* autoinc */ + if (MEM_ADDR_OK (MA)) M[MA] = t; + MA = IDX_ADD (t); /* index? */ + } + TRP = SC; /* save SC */ + SC = MA; /* load new SC */ + } + else SC = (SC + 2) & AMASK; /* incr SC twice */ + } + + else if ((src != U_MEM) && (dst != U_MEM)) { /* reg-reg? */ + reason = bus_op (src, op, dst); /* xmt and modify */ + SC = (SC + 1) & AMASK; /* incr SC */ + } + +/* Memory reference. The second SC increment occurs after the first + execution cycle. For direct, defer, and immediate defer, this is + after the first memory read and before the bus transfer; but for + immediate, it is after the bus transfer. +*/ + + else { /* memory reference */ + SC = (SC + 1) & AMASK; /* incr SC */ + switch (op & MEM_MOD) { /* case on addr mode */ + + case MEM_DIR: /* direct */ + MA = M[SC]; /* get address */ + MA = IDX_ADD (MA); /* index? */ + SC = (SC + 1) & AMASK; /* incr SC again */ + reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ + break; + + case MEM_DEF: /* defer */ + MA = M[SC]; /* get ind addr */ + MA = IDX_ADD (MA); /* index? */ + SC = (SC + 1) & AMASK; /* incr SC again */ + t = (M[MA] + 1) & DMASK; /* autoinc */ + if (MEM_ADDR_OK (MA)) M[MA] = t; + MA = IDX_ADD (t); /* index? */ + reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ + break; + + case MEM_IMM: /* immediate */ + MA = SC; /* eff addr */ + reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ + SC = (SC + 1) & AMASK; /* incr SC again */ + break; + + case MEM_IDF: /* immediate defer */ + MA = SC; /* get ind addr */ + t = (M[MA] + 1) & DMASK; /* autoinc */ + if (MEM_ADDR_OK (MA)) M[MA] = t; + MA = IDX_ADD (t); /* index? */ + SC = (SC + 1) & AMASK; /* incr SC again */ + reason = bus_op (src, op & BUS_FNC, dst); /* xmt and modify */ + break; + } /* end switch */ + } /* end mem ref */ + } /* end while */ + +/* Simulation halted */ + +ao_update (); /* update AO */ +scq_r->qptr = scq_p; /* update sc q ptr */ +return reason; +} + +/* Bus operations */ + +t_stat bus_op (uint32 src, uint32 op, uint32 dst) +{ +uint32 t, old_t; + +t = dev_tab[src].Src (src); /* get src */ +if (op & BUS_COM) t = t ^ DMASK; /* complement? */ +switch (op & BUS_FNC) { /* case op */ + + case BUS_P1: /* plus 1 */ + t = t + 1; /* do add */ + if (t & CBIT) MSR = MSR | MSR_BOV; /* set cry out */ + else MSR = MSR & ~MSR_BOV; + break; + + case BUS_L1: /* left 1 */ + t = (t << 1) | ((MSR & MSR_L)? 1: 0); /* rotate */ + if (t & CBIT) MSR = MSR | MSR_L; /* set link out */ + else MSR = MSR & ~MSR_L; + break; + + case BUS_R1: /* right 1 */ + old_t = t; + t = (t >> 1) | ((MSR & MSR_L)? SIGN: 0); /* rotate */ + if (old_t & 1) MSR = MSR | MSR_L; /* set link out */ + else MSR = MSR & ~MSR_L; + break; + } /* end case op */ + +if (dst == thwh) DR = t & DMASK; /* display dst? */ +return dev_tab[dst].Dst (dst, t & DMASK); /* store dst */ +} + +/* Non-existent device */ + +uint32 no_rd (uint32 src) +{ +return 0; +} + +t_stat no_wr (uint32 dst, uint32 dat) +{ +return stop_opr; +} + +t_stat no_fo (uint32 fnc) +{ +return stop_opr; +} + +uint32 no_sf (uint32 fnc) +{ +return (stop_opr << SF_V_REASON); +} + +/* Zero device */ + +uint32 zero_rd (uint32 src) +{ +return 0; +} + +t_stat zero_wr (uint32 dst, uint32 val) +{ +return SCPE_OK; +} + +t_stat zero_fo (uint32 op) +{ +switch (op & 3) { /* FOM link */ + + case 1: /* CLL */ + MSR = MSR & ~MSR_L; + break; + + case 2: /* STL */ + MSR = MSR | MSR_L; + break; + + case 3: /* CML */ + MSR = MSR ^ MSR_L; + break; + } + +if (op & 4) return STOP_HALT; /* HALT */ +return SCPE_OK; +} + +uint32 zero_sf (uint32 op) +{ +if ((op & 010) || /* power always ok */ + ((op & 4) && (MSR & MSR_L)) || /* link set? */ + ((op & 2) && (MSR & MSR_BOV))) return 1; /* BOV set? */ +return 0; +} + +/* Instruction register (01) */ + +uint32 ir_rd (uint32 src) +{ +return IR; +} + +t_stat ir_fo (uint32 op) +{ +if (op & 2) bkp = 1; +return SCPE_OK; +} + +/* Trap register (03) */ + +uint32 trp_rd (uint32 src) +{ +return TRP; +} + +t_stat trp_wr (uint32 dst, uint32 val) +{ +TRP = val; +return SCPE_OK; +} + +/* Interrupt status register (04) */ + +uint32 isr_rd (uint32 src) +{ +return ISR; +} + +t_stat isr_wr (uint32 dst, uint32 dat) +{ +ISR = dat; +return SCPE_OK; +} + +t_stat isr_fo (uint32 op) +{ +if (op & ISR_ON) dev_done = (dev_done | INT_ON) & ~INT_NODEF; +if (op & ISR_OFF) dev_done = dev_done & ~INT_ON; +return SCPE_OK; +} + +uint32 isr_sf (uint32 op) +{ +return 0; +} + +/* Memory address (05) */ + +uint32 ma_rd (uint32 src) +{ +return MA; +} + +/* Memory (06) */ + +uint32 mem_rd (uint32 src) +{ +return M[MA]; +} + +t_stat mem_wr (uint32 dst, uint32 dat) +{ + +if (MEM_ADDR_OK (MA)) M[MA] = dat; +return SCPE_OK; +} + +/* Sequence counter (07) */ + +uint32 sc_rd (uint32 src) +{ +return SC; +} + +t_stat sc_wr (uint32 dst, uint32 dat) +{ +SCQ_ENTRY; +SC = IDX_ADD (dat); +return SCPE_OK; +} + +/* Switch register (10) */ + +uint32 swr_rd (uint32 src) +{ +return SWR; +} + +/* Machine status register (17) */ + +uint32 msr_rd (uint32 src) +{ +return MSR & MSR_RW; +} + +t_stat msr_wr (uint32 src, uint32 dat) +{ +MSR = dat & MSR_RW; /* new MSR */ +ao_update (); /* update SOV,AOV */ +return SCPE_OK; +} + +/* Arithmetic operator (11:13) */ + +uint32 ao_update (void) +{ +uint32 af = MSR_GET_FOA (MSR); + +switch (af) { + + case AO_ADD: + AO = (AX + AY) & DMASK; /* add */ + break; + + case AO_AND: + AO = AX & AY; /* and */ + break; + + case AO_XOR: /* xor */ + AO = AX ^ AY; + break; + + case AO_IOR: + AO = AX | AY; /* or */ + break; + } + +if ((AX + AY) & CBIT) MSR = MSR | MSR_AOV; /* always calc AOV */ +else MSR = MSR & ~MSR_AOV; +if (SIGN & ((AX ^ (AX + AY)) & (~AX ^ AY))) /* always calc SOV */ + MSR = MSR | MSR_SOV; +else MSR = MSR & ~MSR_SOV; +return AO; +} + +uint32 ax_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_AO) return AX; +else return 0; +} + +t_stat ax_wr (uint32 dst, uint32 dat) +{ +if (cpu_unit.flags & UNIT_AO) { + AX = dat; + ao_update (); + return SCPE_OK; + } +return stop_opr; +} + +uint32 ay_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_AO) return AY; +else return 0; +} + +t_stat ay_wr (uint32 dst, uint32 dat) +{ +if (cpu_unit.flags & UNIT_AO) { + AY = dat; + ao_update (); + return SCPE_OK; + } +return stop_opr; +} + +uint32 ao_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_AO) return ao_update (); +else return 0; +} + +t_stat ao_fo (uint32 op) +{ +if (cpu_unit.flags & UNIT_AO) { + uint32 t = OP_GET_FOA (op); /* get func */ + MSR = MSR_PUT_FOA (MSR, t); /* store in MSR */ + ao_update (); /* update AOV */ + return SCPE_OK; + } +return stop_opr; +} + +uint32 ao_sf (uint32 op) +{ +if (!(cpu_unit.flags & UNIT_AO)) /* not installed? */ + return (stop_opr << SF_V_REASON); +if (((op & 2) && (MSR & MSR_AOV)) || /* arith carry? */ + ((op & 4) && (MSR & MSR_SOV))) return 1; /* arith overflow? */ +return 0; +} + +/* Extended arithmetic operator (14) */ + +t_stat eao_fo (uint32 op) +{ +uint32 t; + +if (!(cpu_unit.flags & UNIT_EAO)) /* EAO installed? */ + return stop_opr; +switch (op) { + + case EAO_MUL: /* mul? */ + t = AX * AY; /* AX * AY */ + AX = (t >> 16) & DMASK; /* to AX'GR1 */ + GR[0] = t & DMASK; + break; + + case EAO_DIV: /* div? */ + if (AY && (AX < AY)) { + t = (AX << 16) | GR[0]; /* AX'GR1 / AY */ + GR[0] = t / AY; /* quo to GR1 */ + AX = t % AY; /* rem to AX */ + MSR = MSR & ~MSR_L; /* clear link */ + } + else MSR = MSR | MSR_L; /* set link */ + break; + + case EAO_ARS: /* arith right? */ + t = 0; /* shift limiter */ + if (AX & SIGN) MSR = MSR | MSR_L; /* L = sign */ + else MSR = MSR & ~MSR_L; + do { /* shift one bit */ + AY = ((AY >> 1) | (AX << 15)) & DMASK; + AX = (AX & SIGN) | (AX >> 1); + GR[0] = (GR[0] + 1) & DMASK; + } + while (GR[0] && (++t < 32)); /* until cnt or limit */ + break; + + case EAO_NORM: /* norm? */ + if ((AX | AY) != 0) { /* can normalize? */ + while ((AX & SIGN) != ((AX << 1) & SIGN)) { /* until AX15 != AX14 */ + AX = ((AX << 1) | (AY >> 15)) & DMASK; + AY = (AY << 1) & DMASK; + GR[0] = (GR[0] + 1) & DMASK; + } + } + break; + } + +// MSR = MSR_PUT_FOA (MSR, AO_ADD); /* AO fnc is add */ +ao_update (); +return SCPE_OK; +} + +/* Index register (GRI-99) (22) */ + +uint32 xr_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_GRI99) return XR; +else return 0; +} + +t_stat xr_wr (uint32 dst, uint32 val) +{ +if (cpu_unit.flags & UNIT_GRI99) { + XR = val; + return SCPE_OK; + } +return stop_opr; +} + +/* Alternate trap (GRI-99) (23) */ + +uint32 atrp_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_GRI99) return TRP; +else return 0; +} + +t_stat atrp_wr (uint32 dst, uint32 val) +{ +if (cpu_unit.flags & UNIT_GRI99) { + TRP = val; + return SCPE_OK; + } +return stop_opr; +} + +/* Byte swapper (24) */ + +uint32 bsw_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_BSWPK) return BSW; +else return 0; +} + +t_stat bsw_wr (uint32 dst, uint32 val) +{ +if (cpu_unit.flags & UNIT_BSWPK) { + BSW = ((val >> 8) & 0377) | ((val & 0377) << 8); + return SCPE_OK; + } +return stop_opr; +} + +/* Byte packer (25) */ + +uint32 bpk_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_BSWPK) return BPK; +else return 0; +} + +t_stat bpk_wr (uint32 dst, uint32 val) +{ +if (cpu_unit.flags & UNIT_BSWPK) { + BPK = ((BPK & 0377) << 8) | (val & 0377); + return SCPE_OK; + } +return stop_opr; +} + +/* General registers (30:35) */ + +uint32 gr_rd (uint32 src) +{ +if (cpu_unit.flags & UNIT_GPR) return GR[src - U_GR]; +else return 0; +} + +t_stat gr_wr (uint32 dst, uint32 dat) +{ +if (cpu_unit.flags & UNIT_GPR) { + GR[dst - U_GR] = dat; + return SCPE_OK; + } +return stop_opr; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int32 i; + +AX = AY = AO = 0; +XR = 0; +TRP = 0; +ISR = 0; +MSR = 0; +MA = IR = 0; +BSW = BPK = 0; +for (i = 0; i < 6; i++) GR[i] = 0; +dev_done = dev_done & ~INT_PENDING; +scq_r = find_reg ("SCQ", NULL, dptr); +if (scq_r) scq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & DMASK; +return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} diff --git a/GRI/gri_defs.h b/GRI/gri_defs.h new file mode 100644 index 0000000..35eff8f --- /dev/null +++ b/GRI/gri_defs.h @@ -0,0 +1,246 @@ +/* gri_defs.h: GRI-909 simulator definitions + + Copyright (c) 2001-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 12-Jan-08 RMS Added GRI-99 support + 25-Apr-03 RMS Revised for extended file support + 19-Sep-02 RMS Fixed declarations in gdev structure + + There are several discrepancies between the original GRI-909 Reference + Manual of 1969 and the only surviving code sample, the MIT Crystal Growing + System of 1972. These discrepancies were clarified by later documentation: + + 1. Ref Manual documents two GR's at codes 26-27; MITCS documents six GR's + at 30-35. Answer: 6 GR's, 26-27 were used for character compares. + 2. Ref Manual documents only unsigned overflow (carry) for arithmetic + operator; MITCS uses both unsigned overflow (AOV) and signed overflow + (SOV). Answer: signed and unsigned. + 3. Ref Manual documents a ROM-subroutine multiply operator and mentions + but does not document a "fast multiply"; MITCS uses an extended + arithmetic operator with multiply, divide, and shift. Answer: EAO + is a package of ROM subroutines with just four functions: multiply, + divide, arithmetic right shift, and normalize. + 4. Is SOV testable even if the FOA is not ADD? Answer: AOV and SOV are + calculated regardless of the function. + 5. How does the EAO handle divide overflow? Answer: set link. +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_DEV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_ILLINT 4 /* illegal intr */ + +/* Memory */ + +#define MAXMEMSIZE 32768 /* max memory size */ +#define AMASK 077777 /* logical addr mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* Architectural constants */ + +#define SIGN 0100000 /* sign */ +#define INDEX 0100000 /* indexed (GRI-99) */ +#define DMASK 0177777 /* data mask */ +#define CBIT (DMASK + 1) /* carry bit */ + +/* Instruction format */ + +#define I_M_SRC 077 /* source */ +#define I_V_SRC 10 +#define I_GETSRC(x) (((x) >> I_V_SRC) & I_M_SRC) +#define I_M_OP 017 /* operator */ +#define I_V_OP 6 +#define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) +#define I_M_DST 077 /* destination */ +#define I_V_DST 0 +#define I_GETDST(x) (((x) >> I_V_DST) & I_M_DST) +#define SF_V_REASON 1 /* SF reason */ + +/* IO return */ + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Operators */ + +#define U_ZERO 000 /* zero */ +#define U_IR 001 /* instruction reg */ +#define U_FSK 002 /* func out/skip */ +#define U_TRP 003 /* trap */ +#define U_ISR 004 /* intr status */ +#define U_MA 005 /* mem addr */ +#define U_MEM 006 /* mem data */ +#define U_SC 007 /* seq counter */ +#define U_SWR 010 /* switch register */ +#define U_AX 011 /* arith in 1 */ +#define U_AY 012 /* arith in 2 */ +#define U_AO 013 /* arith out */ +#define U_EAO 014 /* ext arith */ +#define U_MSR 017 /* machine status */ +#define U_XR 022 /* GRI-99: idx reg */ +#define U_GTRP 023 /* GRI-99: alt trap */ +#define U_BSW 024 /* byte swap */ +#define U_BPK 025 /* byte pack */ +#define U_BCP1 026 /* byte compare 1 */ +#define U_BCP2 027 /* byte compare 2 */ +#define U_GR 030 /* hex general regs */ +#define U_CDR 055 /* card reader */ +#define U_CADR 057 +#define U_DWC 066 /* disk */ +#define U_DCA 067 +#define U_DISK 070 +#define U_LPR 071 /* line printer */ +#define U_CAS 074 /* casette */ +#define U_RTC 075 /* clock */ +#define U_HS 076 /* paper tape */ +#define U_TTY 077 /* console */ + +struct gdev { + uint32 (*Src)(uint32); /* source */ + t_stat (*Dst)(uint32, uint32); /* dest */ + t_stat (*FO)(uint32); /* func out */ + uint32 (*SF)(uint32); /* skip func */ +}; + +/* Trap (jump) */ + +#define TRP_DIR 00 /* direct */ +#define TRP_DEF 01 /* defer */ + +/* Interrupt status */ + +#define ISR_OFF 01 /* int off */ +#define ISR_ON 02 /* int on */ + +/* Bus modifiers */ + +#define BUS_COM 002 /* complement */ +#define BUS_FNC 014 /* function mask */ +#define BUS_P1 004 /* + 1 */ +#define BUS_L1 010 /* rotate left */ +#define BUS_R1 014 /* rotate right */ + +/* Memory address modes */ + +#define MEM_MOD 03 +#define MEM_DIR 00 /* direct */ +#define MEM_DEF 01 /* defer */ +#define MEM_IMM 02 /* immediate */ +#define MEM_IDF 03 /* immediate defer */ + +/* Arithmetic unit */ + +#define FO_V_FOA 8 /* arith func */ +#define FO_M_FOA 03 +#define OP_GET_FOA(x) (((x) >> (FO_V_FOA - I_V_OP)) & FO_M_FOA) +#define AO_ADD 00 /* add */ +#define AO_AND 01 /* and */ +#define AO_XOR 02 /* xor */ +#define AO_IOR 03 /* or */ +#define EAO_MUL 01 /* multiply */ +#define EAO_DIV 02 /* divide */ +#define EAO_ARS 03 /* arith rshft */ +#define EAO_NORM 04 /* normalize */ + +/* Machine status */ + +#define MSR_V_BOV 15 /* bus carry */ +#define MSR_V_L 14 /* bus link */ +#define MSR_V_FOA 8 /* arith func */ +#define MSR_M_FOA 03 +#define MSR_V_SOV 1 /* arith ovflo */ +#define MSR_V_AOV 0 /* arith carry */ +#define MSR_BOV (1u << MSR_V_BOV) +#define MSR_L (1u << MSR_V_L) +#define MSR_FOA (MSR_M_FOA << MSR_V_FOA) +#define MSR_SOV (1u << MSR_V_SOV) +#define MSR_AOV (1u << MSR_V_AOV) +#define MSR_GET_FOA(x) (((x) >> MSR_V_FOA) & MSR_M_FOA) +#define MSR_PUT_FOA(x,n) (((x) & ~(MSR_M_FOA << MSR_V_FOA)) | \ + (((n) & MSR_M_FOA) << MSR_V_FOA)) +#define MSR_RW (MSR_BOV|MSR_L|MSR_FOA|MSR_SOV|MSR_AOV) + +/* Real time clock */ + +#define RTC_OFF 001 /* off */ +#define RTC_ON 002 /* clock on */ +#define RTC_OV 010 /* clock flag */ +#define RTC_CTR 0103 /* counter */ + +/* Terminal */ + +#define TTY_ORDY 002 /* output flag */ +#define TTY_IRDY 010 /* input flag */ + +/* Paper tape */ + +#define PT_STRT 001 /* start reader */ +#define PT_ORDY 002 /* output flag */ +#define PT_IRDY 010 /* input flag */ + +/* Interrupt masks (ISR) */ + +#define INT_V_TTO 0 /* console out */ +#define INT_V_TTI 1 /* console in */ +#define INT_V_HSP 2 /* paper tape punch */ +#define INT_V_HSR 3 /* paper tape reader */ +#define INT_V_LPR 5 /* line printer */ +#define INT_V_CDR 7 /* card reader */ +#define INT_V_CASW 9 /* casette */ +#define INT_V_CASR 10 +#define INT_V_RTC 11 /* clock */ +#define INT_V_DISK 14 /* disk */ +#define INT_V_NODEF 16 /* nodefer */ +#define INT_V_ON 17 /* enable */ +#define INT_TTO (1u << INT_V_TTO) +#define INT_TTI (1u << INT_V_TTI) +#define INT_HSP (1u << INT_V_HSP) +#define INT_HSR (1u << INT_V_HSR) +#define INT_LPR (1u << INT_V_LPR) +#define INT_CDR (1u << INT_V_CDR) +#define INT_CASW (1u << INT_V_CAS1) +#define INT_CASR (1u << INT_V_CAS2) +#define INT_RTC (1u << INT_V_RTC) +#define INT_DISK (1u << INT_V_DISK) +#define INT_NODEF (1u << INT_V_NODEF) +#define INT_ON (1u << INT_V_ON) +#define INT_PENDING (INT_ON | INT_NODEF) + +/* Vectors */ + +#define VEC_BKP 0000 /* breakpoint */ +#define VEC_TTO 0011 /* console out */ +#define VEC_TTI 0014 /* console in */ +#define VEC_HSP 0017 /* paper tape punch */ +#define VEC_HSR 0022 /* paper tape reader */ +#define VEC_LPR 0033 /* line printer */ +#define VEC_CDR 0033 /* card reader */ +#define VEC_CASW 0044 /* casette */ +#define VEC_CASR 0047 +#define VEC_DISK 0055 /* disk */ +#define VEC_RTC 0100 /* clock */ diff --git a/GRI/gri_stddev.c b/GRI/gri_stddev.c new file mode 100644 index 0000000..0d77316 --- /dev/null +++ b/GRI/gri_stddev.c @@ -0,0 +1,409 @@ +/* gri_stddev.c: GRI-909 standard devices + + Copyright (c) 2001-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti S42-001 terminal input + tto S42-002 terminal output + hsr S42-004 high speed reader + hsp S42-006 high speed punch + rtc real time clock + + 31-May-08 RMS Fixed declarations (found by Peter Schorn) + 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode + 22-Nov-05 RMS Revised for new terminal processing routines + 29-Dec-03 RMS Added support for console backpressure + 25-Apr-03 RMS Revised for extended file support + 22-Dec-02 RMS Added break support + 01-Nov-02 RMS Added 7b/8B support to terminal +*/ + +#include "gri_defs.h" +#include + +uint32 hsr_stopioe = 1, hsp_stopioe = 1; + +extern uint16 M[]; +extern uint32 dev_done, ISR; + +t_stat tti_svc (UNIT *uhsr); +t_stat tto_svc (UNIT *uhsr); +t_stat tti_reset (DEVICE *dhsr); +t_stat tto_reset (DEVICE *dhsr); +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat hsr_svc (UNIT *uhsr); +t_stat hsp_svc (UNIT *uhsr); +t_stat hsr_reset (DEVICE *dhsr); +t_stat hsp_reset (DEVICE *dhsr); +t_stat rtc_svc (UNIT *uhsr); +t_stat rtc_reset (DEVICE *dhsr); +int32 rtc_tps = 1000; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list + tti_mod TTI modifiers list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, TT_MODE_KSR, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 8) }, + { FLDATA (IRDY, dev_done, INT_V_TTI) }, + { FLDATA (IENB, ISR, INT_V_TTI) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 8) }, + { FLDATA (ORDY, dev_done, INT_V_TTO) }, + { FLDATA (IENB, ISR, INT_V_TTO) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL + }; + +/* HSR data structures + + hsr_dev HSR device descriptor + hsr_unit HSR unit descriptor + hsr_reg HSR register list + hsr_mod HSR modifiers list +*/ + +UNIT hsr_unit = { + UDATA (&hsr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), SERIAL_IN_WAIT + }; + +REG hsr_reg[] = { + { ORDATA (BUF, hsr_unit.buf, 8) }, + { FLDATA (IRDY, dev_done, INT_V_HSR) }, + { FLDATA (IENB, ISR, INT_V_HSR) }, + { DRDATA (POS, hsr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, hsr_unit.wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, hsr_stopioe, 0) }, + { NULL } + }; + +DEVICE hsr_dev = { + "HSR", &hsr_unit, hsr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &hsr_reset, + NULL, NULL, NULL + }; + +/* HSP data structures + + hsp_dev HSP device descriptor + hsp_unit HSP unit descriptor + hsp_reg HSP register list +*/ + +UNIT hsp_unit = { + UDATA (&hsp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG hsp_reg[] = { + { ORDATA (BUF, hsp_unit.buf, 8) }, + { FLDATA (ORDY, dev_done, INT_V_HSP) }, + { FLDATA (IENB, ISR, INT_V_HSP) }, + { DRDATA (POS, hsp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, hsp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, hsp_stopioe, 0) }, + { NULL } + }; + +DEVICE hsp_dev = { + "HSP", &hsp_unit, hsp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &hsp_reset, + NULL, NULL, NULL + }; + +/* RTC data structures + + rtc_dev RTC device descriptor + rtc_unit RTC unit descriptor + rtc_reg RTC register list +*/ + +UNIT rtc_unit = { UDATA (&rtc_svc, 0, 0), 16000 }; + +REG rtc_reg[] = { + { FLDATA (RDY, dev_done, INT_V_RTC) }, + { FLDATA (IENB, ISR, INT_V_RTC) }, + { DRDATA (TIME, rtc_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, rtc_tps, 8), REG_NZ + PV_LEFT + REG_HIDDEN }, + { NULL } + }; + +DEVICE rtc_dev = { + "RTC", &rtc_unit, rtc_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &rtc_reset, + NULL, NULL, NULL + }; + +/* Console terminal function processors */ + +uint32 tty_rd (int32 src, int32 ea) +{ +return tti_unit.buf; /* return data */ +} + +t_stat tty_wr (uint32 dst, uint32 val) +{ +tto_unit.buf = val & 0377; /* save char */ +dev_done = dev_done & ~INT_TTO; /* clear ready */ +sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ +return SCPE_OK; +} + +t_stat tty_fo (uint32 op) +{ +if (op & TTY_IRDY) dev_done = dev_done & ~INT_TTI; +if (op & TTY_ORDY) dev_done = dev_done & ~INT_TTO; +return SCPE_OK; +} + +uint32 tty_sf (uint32 op) +{ +if (((op & TTY_IRDY) && (dev_done & INT_TTI)) || + ((op & TTY_ORDY) && (dev_done & INT_TTO))) return 1; +return 0; +} + +/* Service routines */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c; + +sim_activate (uptr, uptr->wait); /* continue poll */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +if (c & SCPE_BREAK) uptr->buf = 0; /* break? */ +else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); +dev_done = dev_done | INT_TTI; /* set ready */ +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); +if (c >= 0) { + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } + } +dev_done = dev_done | INT_TTO; /* set ready */ +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +/* Reset routines */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; /* clear buffer */ +dev_done = dev_done & ~INT_TTI; /* clear ready */ +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; /* clear buffer */ +dev_done = dev_done | INT_TTO; /* set ready */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; +tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; +return SCPE_OK; +} + +/* High speed paper tape function processors */ + +uint32 hsrp_rd (int32 src, int32 ea) +{ +return hsr_unit.buf; /* return data */ +} + +t_stat hsrp_wr (uint32 dst, uint32 val) +{ +hsp_unit.buf = val & 0377; /* save char */ +dev_done = dev_done & ~INT_HSP; /* clear ready */ +sim_activate (&hsp_unit, hsp_unit.wait); /* activate unit */ +return SCPE_OK; +} + +t_stat hsrp_fo (uint32 op) +{ +if (op & PT_IRDY) dev_done = dev_done & ~INT_HSR; +if (op & PT_ORDY) dev_done = dev_done & ~INT_HSP; +if (op & PT_STRT) sim_activate (&hsr_unit, hsr_unit.wait); +return SCPE_OK; +} + +uint32 hsrp_sf (uint32 op) +{ +if (((op & PT_IRDY) && (dev_done & INT_HSR)) || + ((op & PT_ORDY) && (dev_done & INT_HSP))) return 1; +return 0; +} + +t_stat hsr_svc (UNIT *uptr) +{ +int32 temp; + +if ((hsr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (hsr_stopioe, SCPE_UNATT); +if ((temp = getc (hsr_unit.fileref)) == EOF) { /* read char */ + if (feof (hsr_unit.fileref)) { /* err or eof? */ + if (hsr_stopioe) printf ("HSR end of file\n"); + else return SCPE_OK; + } + else perror ("HSR I/O error"); + clearerr (hsr_unit.fileref); + return SCPE_IOERR; + } +dev_done = dev_done | INT_HSR; /* set ready */ +hsr_unit.buf = temp & 0377; /* save char */ +hsr_unit.pos = hsr_unit.pos + 1; +return SCPE_OK; +} + +t_stat hsp_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_HSP; /* set ready */ +if ((hsp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (hsp_stopioe, SCPE_UNATT); +if (putc (hsp_unit.buf, hsp_unit.fileref) == EOF) { /* write char */ + perror ("HSP I/O error"); /* error? */ + clearerr (hsp_unit.fileref); + return SCPE_IOERR; + } +hsp_unit.pos = hsp_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routines */ + +t_stat hsr_reset (DEVICE *dptr) +{ +hsr_unit.buf = 0; /* clear buffer */ +dev_done = dev_done & ~INT_HSR; /* clear ready */ +sim_cancel (&hsr_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat hsp_reset (DEVICE *dptr) +{ +hsp_unit.buf = 0; /* clear buffer */ +dev_done = dev_done | INT_HSP; /* set ready */ +sim_cancel (&hsp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Clock function processors */ + +t_stat rtc_fo (int32 op) +{ +if (op & RTC_OFF) sim_cancel (&rtc_unit); /* clock off? */ +if ((op & RTC_ON) && !sim_is_active (&rtc_unit)) /* clock on? */ + sim_activate (&rtc_unit, sim_rtc_init (rtc_unit.wait)); +if (op & RTC_OV) dev_done = dev_done & ~INT_RTC; /* clr ovflo? */ +return SCPE_OK; +} + +uint32 rtc_sf (int32 op) +{ +if ((op & RTC_OV) && (dev_done & INT_RTC)) return 1; +return 0; +} + +t_stat rtc_svc (UNIT *uptr) +{ +M[RTC_CTR] = (M[RTC_CTR] + 1) & DMASK; /* incr counter */ +if (M[RTC_CTR] == 0) dev_done = dev_done | INT_RTC; /* ovflo? set ready */ +sim_activate (&rtc_unit, sim_rtc_calb (rtc_tps)); /* reactivate */ +return SCPE_OK; +} + +t_stat rtc_reset (DEVICE *dptr) +{ +dev_done = dev_done & ~INT_RTC; /* clear ready */ +sim_cancel (&rtc_unit); /* stop clock */ +return SCPE_OK; +} diff --git a/GRI/gri_sys.c b/GRI/gri_sys.c new file mode 100644 index 0000000..afceb2e --- /dev/null +++ b/GRI/gri_sys.c @@ -0,0 +1,660 @@ +/* gri_sys.c: GRI-909 simulator interface + + Copyright (c) 2001-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 14-Jan-08 RMS Added GRI-99 support + 18-Oct-02 RMS Fixed bug in symbolic decode (found by Hans Pufal) +*/ + +#include "gri_defs.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE tti_dev, tto_dev; +extern DEVICE hsr_dev, hsp_dev; +extern DEVICE rtc_dev; +extern REG cpu_reg[]; +extern uint16 M[]; +extern int32 sim_switches; + +void fprint_addr (FILE *of, uint32 val, uint32 mod, uint32 dst); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "GRI-909"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 2; + +DEVICE *sim_devices[] = { + &cpu_dev, + &tti_dev, + &tto_dev, + &hsr_dev, + &hsp_dev, + &rtc_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented unit", + "HALT instruction", + "Breakpoint", + "Invalid interrupt request" + }; + +/* Binary loader + + Bootstrap loader format consists of blocks separated by zeroes. Each + word in the block has three frames: a control frame (ignored) and two + data frames. The user must specify the load address. Switch -c means + continue and load all blocks until end of tape. +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 c; +uint32 org; +t_stat r; +char gbuf[CBUFSIZE]; + +if (*cptr != 0) { /* more input? */ + cptr = get_glyph (cptr, gbuf, 0); /* get origin */ + org = get_uint (gbuf, 8, AMASK, &r); + if (r != SCPE_OK) return r; + if (*cptr != 0) return SCPE_ARG; /* no more */ + } +else org = 0200; /* default 200 */ + +for (;;) { /* until EOF */ + while ((c = getc (fileref)) == 0) ; /* skip starting 0's */ + if (c == EOF) break; /* EOF? done */ + for ( ; c != 0; ) { /* loop until ctl = 0 */ + /* ign ctrl frame */ + if ((c = getc (fileref)) == EOF) /* get high byte */ + return SCPE_FMT; /* EOF is error */ + if (!MEM_ADDR_OK (org)) return SCPE_NXM; + M[org] = ((c & 0377) << 8); /* store high */ + if ((c = getc (fileref)) == EOF) /* get low byte */ + return SCPE_FMT; /* EOF is error */ + M[org] = M[org] | (c & 0377); /* store low */ + org = org + 1; /* incr origin */ + if ((c = getc (fileref)) == EOF) /* get ctrl frame */ + return SCPE_OK; /* EOF is ok */ + } /* end block for */ + if (!(sim_switches & SWMASK ('C'))) return SCPE_OK; + } /* end tape for */ +return SCPE_OK; +} + +/* Symbol tables */ + +#define F_V_FL 16 /* class flag */ +#define F_M_FL 017 +#define F_V_FO 000 /* function out */ +#define F_V_FOI 001 /* FO, impl reg */ +#define F_V_SF 002 /* skip function */ +#define F_V_SFI 003 /* SF, impl reg */ +#define F_V_RR 004 /* reg reg */ +#define F_V_ZR 005 /* zero reg */ +#define F_V_RS 006 /* reg self */ +#define F_V_JC 010 /* jump cond */ +#define F_V_JU 011 /* jump uncond */ +#define F_V_RM 012 /* reg mem */ +#define F_V_ZM 013 /* zero mem */ +#define F_V_MR 014 /* mem reg */ +#define F_V_MS 015 /* mem self */ +#define F_2WD 010 /* 2 words */ + +#define F_FO (F_V_FO << F_V_FL) +#define F_FOI (F_V_FOI << F_V_FL) +#define F_SF (F_V_SF << F_V_FL) +#define F_SFI (F_V_SFI << F_V_FL) +#define F_RR (F_V_RR << F_V_FL) +#define F_ZR (F_V_ZR << F_V_FL) +#define F_RS (F_V_RS << F_V_FL) +#define F_JC (F_V_JC << F_V_FL) +#define F_JU (F_V_JU << F_V_FL) +#define F_RM (F_V_RM << F_V_FL) +#define F_ZM (F_V_ZM << F_V_FL) +#define F_MR (F_V_MR << F_V_FL) +#define F_MS (F_V_MS << F_V_FL) + +struct fnc_op { + uint32 inst; /* instr prot */ + uint32 imask; /* instr mask */ + uint32 oper; /* operator */ + uint32 omask; /* oper mask */ + }; + +static const int32 masks[] = { + 0176000, 0176077, 0000077, 0176077, + 0000300, 0176300, 0000300, 0177777, + 0000077, 0177777, 0000377, 0176377, + 0176300, 0176377 + }; + +/* Instruction mnemonics + + Order is critical, as some instructions are more precise versions of + others. For example, JU must precede JC, otherwise, JU will be decoded + as JC 0,ETZ,dst. There are some ambiguities, eg, what is 02-xxxx-06? + Priority is as follows: + + FO (02-xxxx-rr) + SF (rr-xxxx-02) + MR (06-xxxx-rr) + RM (rr-xxxx-06) + JC (rr-xxxx-03) + RR +*/ + +static const char *opcode[] = { + "FOM", "FOA", "FOI", "FO", /* FOx before FO */ + "SFM", "SFA", "SFI", "SF", /* SFx before SF */ + "ZM", "ZMD", "ZMI", "ZMID", /* ZM before RM */ + "MS", "MSD", "MSI", "MSID", + "RM", "RMD", "RMI", "RMID", + "MR", "MRD", "MRI", "MRID", + "JO", "JOD", "JN", "JND", /* JU before JC */ + "JU", "JUD", "JC", "JCD", + "ZR", "ZRC", "RR", "RRC", /* ZR before RR */ + "RS", "RSC", + NULL + }; + +static const uint32 opc_val[] = { + 0004000+F_FOI, 0004013+F_FOI, 0004004+F_FOI, 0004000+F_FO, + 0000002+F_SFI, 0026002+F_SFI, 0010002+F_SFI, 0000002+F_SF, + 0000006+F_ZM, 0000106+F_ZM, 0000206+F_ZM, 0000306+F_ZM, + 0014006+F_MS, 0014106+F_MS, 0014206+F_MS, 0014306+F_MS, + 0000006+F_RM, 0000106+F_RM, 0000206+F_RM, 0000306+F_RM, + 0014000+F_MR, 0014100+F_MR, 0014200+F_MR, 0014300+F_MR, + 0037003+F_JU, 0037103+F_JU, 0037203+F_JU, 0037303+F_JU, + 0000403+F_JU, 0000503+F_JU, 0000003+F_JC, 0000103+F_JC, + 0000000+F_ZR, 0000200+F_ZR, 0000000+F_RR, 0000200+F_RR, + 0000000+F_RS, 0000200+F_RS + }; + +/* Unit mnemonics. All 64 units are decoded, most just to octal integers */ + +static const char *unsrc[64] = { + "0", "IR", "2", "TRP", "ISR", "MA", "MB", "SC", /* 00 - 07 */ + "SWR", "AX", "AY", "AO", "14", "15", "16", "MSR", /* 10 - 17 */ + "20", "21", "XR", "ATRP", "BSW", "BPK", "BCPA", "BCPB",/* 20 - 27 */ + "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "36", "37", /* 30 - 37 */ + "40", "41", "42", "43", "44", "45", "46", "47", + "50", "51", "52", "53", "54", "CDR", "56", "CADR", + "60", "61", "62", "63", "64", "65", "DWC", "DCA", + "DISK", "LPR", "72", "73", "CAS", "RTC", "HSR", "TTI" /* 70 - 77 */ + }; + +static const char *undst[64] = { + "0", "IR", "2", "TRP", "ISR", "5", "MB", "SC", /* 00 - 07 */ + "SWR", "AX", "AY", "13", "EAO", "15", "16", "MSR", /* 10 - 17 */ + "20", "21", "XR", "ATRP", "BSW", "BPK", "BCPA", "BCPB",/* 20 - 27 */ + "GR1", "GR2", "GR3", "GR4", "GR5", "GR6", "36", "37", /* 30 - 37 */ + "40", "41", "42", "43", "44", "45", "46", "47", + "50", "51", "52", "53", "54", "CDR", "56", "CADR", + "60", "61", "62", "63", "64", "65", "DWC", "DCA", + "DISK", "LPR", "72", "73", "CAS", "RTC", "HSP", "TTO" /* 70 - 77 */ + }; + +/* Operators */ + +static const char *opname[4] = { + NULL, "P1", "L1", "R1" + }; + +/* Conditions */ + +static const char *cdname[8] = { + "NEVER", "ALWAYS", "ETZ", "NEZ", "LTZ", "GEZ", "LEZ", "GTZ" + }; + +/* Function out/sense function */ + +static const char *fname[] = { + "NOT", /* any SF */ + "POK", "LNK", "BOV", /* SFM */ + "SOV", "AOV", /* SFA */ + "IRDY", "ORDY", /* any SF */ + "CLL", "STL", "CML", "HLT", /* FOM */ + "ICF", "ICO", /* FOI */ + "ADD", "AND", "XOR", "OR", /* FOA */ + "INP", "IRDY", "ORDY", "STRT", /* any FO */ + NULL + }; + +static const struct fnc_op fop[] = { + { 0000002, 0000077, 001, 001 }, /* NOT */ + { 0000002, 0176077, 010, 010 }, /* POK */ + { 0000002, 0176077, 004, 004 }, /* LNK */ + { 0000002, 0176077, 002, 002 }, /* BOV */ + { 0026002, 0176077, 004, 004 }, /* SOV */ + { 0026002, 0176077, 002, 002 }, /* AOV */ + { 0000002, 0000077, 010, 010 }, /* IRDY */ + { 0000002, 0000077, 002, 002 }, /* ORDY */ + { 0004000, 0176077, 001, 003 }, /* CLL */ + { 0004000, 0176077, 002, 003 }, /* STL */ + { 0004000, 0176077, 003, 003 }, /* CML */ + { 0004000, 0176077, 004, 004 }, /* HLT */ + { 0004004, 0176077, 001, 001 }, /* ICF */ + { 0004004, 0176077, 002, 002 }, /* ICO */ + { 0004013, 0176077, 000, 014 }, /* ADD */ + { 0004013, 0176077, 004, 014 }, /* AND */ + { 0004013, 0176077, 010, 014 }, /* XOR */ + { 0004013, 0176077, 014, 014 }, /* OR */ + { 0004000, 0176000, 011, 011 }, /* INP */ + { 0004000, 0176000, 010, 010 }, /* IRDY */ + { 0004000, 0176000, 002, 002 }, /* ORDY */ + { 0004000, 0176000, 001, 001 } /* STRT */ + }; + +/* Print opcode field for FO, SF */ + +void fprint_op (FILE *of, uint32 inst, uint32 op) +{ +int32 i, nfirst; + +for (i = nfirst = 0; fname[i] != NULL; i++) { + if (((inst & fop[i].imask) == fop[i].inst) && + ((op & fop[i].omask) == fop[i].oper)) { + op = op & ~fop[i].omask; + if (nfirst) fputc (' ', of); + nfirst = 1; + fprintf (of, "%s", fname[i]); + } + } +if (op) fprintf (of, " %o", op); +return; +} + +/* Print address field with potential indexing */ + +void fprint_addr (FILE *of, uint32 val, uint32 mode, uint32 dst) +{ +if ((val & INDEX) && + ((dst == U_SC) || (mode != MEM_IMM))) + fprintf (of, "#%o", val & AMASK); +else fprintf (of, "%o", val); +return; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, j; +uint32 inst, src, dst, op, bop; + +inst = val[0]; +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* characters? */ + fprintf (of, FMTASC ((inst >> 8) & 0177)); + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +inst = val[0]; +src = I_GETSRC (inst); /* get fields */ +op = I_GETOP (inst); +dst = I_GETDST (inst); +bop = op >> 2; /* bus op */ +for (i = 0; opcode[i] != NULL; i++) { /* loop thru ops */ + j = (opc_val[i] >> F_V_FL) & F_M_FL; /* get class */ + if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + + case F_V_FO: /* func out */ + fprintf (of, "%s ", opcode[i]); + fprint_op (of, inst, op); + fprintf (of, ",%s", undst[dst]); + break; + + case F_V_FOI: /* func out impl */ + fprintf (of, "%s ", opcode[i]); + fprint_op (of, inst, op); + break; + + case F_V_SF: /* skip func */ + fprintf (of, "%s %s,", opcode[i], unsrc[src]); + fprint_op (of, inst, op); + break; + + case F_V_SFI: /* skip func impl */ + fprintf (of, "%s ", opcode[i]); + fprint_op (of, inst, op); + break; + + case F_V_RR: /* reg reg */ + if (strcmp (unsrc[src], undst[dst]) == 0) { + if (bop) fprintf (of, "%s %s,%s", opcode[i + 2], + unsrc[src], opname[bop]); + else fprintf (of, "%s %s", opcode[i + 2], unsrc[src]); + } + else { + if (bop) fprintf (of, "%s %s,%s,%s", opcode[i], + unsrc[src], opname[bop], undst[dst]); + else fprintf (of, "%s %s,%s", opcode[i], + unsrc[src], undst[dst]); + } + break; + + case F_V_ZR: /* zero reg */ + if (bop) fprintf (of, "%s %s,%s", opcode[i], + opname[bop], undst[dst]); + else fprintf (of, "%s %s", opcode[i], undst[dst]); + break; + + case F_V_JC: /* jump cond */ + fprintf (of, "%s %s,%s,", + opcode[i], unsrc[src], cdname[op >> 1]); + fprint_addr (of, val[1], 0, U_SC); + break; + + case F_V_JU: /* jump uncond */ + fprintf (of, "%s ", opcode[i]); + fprint_addr (of, val[1], 0, U_SC); + break; + + case F_V_RM: /* reg mem */ + if (bop) fprintf (of, "%s %s,%s,", + opcode[i], unsrc[src], opname[bop]); + else fprintf (of, "%s %s,", opcode[i], unsrc[src]); + fprint_addr (of, val[1], op & MEM_MOD, dst); + break; + + case F_V_ZM: /* zero mem */ + if (bop) fprintf (of, "%s %s,", opcode[i], opname[bop]); + else fprintf (of, "%s ", opcode[i]); + fprint_addr (of, val[1], op & MEM_MOD, dst); + break; + + case F_V_MR: /* mem reg */ + fprintf (of, "%s ", opcode[i]); + fprint_addr (of, val[1], op & MEM_MOD, dst); + if (bop) fprintf (of, ",%s,%s", opname[bop], undst[dst]); + else fprintf (of, ",%s", undst[dst]); + break; + + case F_V_MS: /* mem self */ + fprintf (of, "%s ", opcode[i]); + fprint_addr (of, val[1], op & MEM_MOD, dst); + if (bop) fprintf (of, ",%s", opname[bop]); + break; + } /* end case */ + + return (j >= F_2WD)? -1: SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Field parse routines + + get_fnc get function field + get_ma get memory address + get_sd get source or dest + get_op get optional bus operator +*/ + +char *get_fnc (char *cptr, t_value *val) +{ +char gbuf[CBUFSIZE]; +int32 i; +t_value d; +t_stat r; +uint32 inst = val[0]; +uint32 fncv = 0, fncm = 0; + +while (*cptr) { + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + d = get_uint (gbuf, 8, 017, &r); /* octal? */ + if (r == SCPE_OK) { /* ok? */ + if (d & fncm) return NULL; /* already filled? */ + fncv = fncv | d; /* save */ + fncm = fncm | d; /* field filled */ + } + else { /* symbol? */ + for (i = 0; fname[i] != NULL; i++) { /* search table */ + if ((strcmp (gbuf, fname[i]) == 0) && /* match for inst? */ + ((inst & fop[i].imask) == fop[i].inst)) { + if (fop[i].oper & fncm) return NULL; /* already filled? */ + fncm = fncm | fop[i].omask; + fncv = fncv | fop[i].oper; + break; + } + } + if (fname[i] == NULL) return NULL; + } /* end else */ + } /* end while */ +val[0] = val[0] | (fncv << I_V_OP); /* store fnc */ +return cptr; +} + +char *get_ma (char *cptr, t_value *val, char term) +{ +char gbuf[CBUFSIZE]; +t_value d; +t_stat r; + +cptr = get_glyph (cptr, gbuf, term); /* get glyph */ +if (gbuf[0] == '#') /* indexed? */ + d = get_uint (gbuf + 1, 8, AMASK, &r) | INDEX; /* [0, 77777] */ +else d = get_uint (gbuf, 8, DMASK, &r); /* [0,177777] */ +if (r != SCPE_OK) return NULL; +val[1] = d; /* second wd */ +return cptr; +} + +char *get_sd (char *cptr, t_value *val, char term, t_bool src) +{ +char gbuf[CBUFSIZE]; +int32 d; +t_stat r; + +cptr = get_glyph (cptr, gbuf, term); /* get glyph */ +for (d = 0; d < 64; d++) { /* symbol match? */ + if ((strcmp (gbuf, unsrc[d]) == 0) || + (strcmp (gbuf, undst[d]) == 0)) break; + } +if (d >= 64) { /* no, [0,63]? */ + d = get_uint (gbuf, 8, 077, &r); + if (r != SCPE_OK) return NULL; + } +val[0] = val[0] | (d << (src? I_V_SRC: I_V_DST)); /* or to inst */ +return cptr; +} + +char *get_op (char *cptr, t_value *val, char term) +{ +char gbuf[CBUFSIZE], *tptr; +int32 i; + +tptr = get_glyph (cptr, gbuf, term); /* get glyph */ +for (i = 1; i < 4; i++) { /* symbol match? */ + if (strcmp (gbuf, opname[i]) == 0) { + val[0] = val[0] | (i << (I_V_OP + 2)); /* or to inst */ + return tptr; + } + } +return cptr; /* original ptr */ +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 i, j, k; +char *tptr, gbuf[CBUFSIZE]; + +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0] & 0177; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* char string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 0177) << 8) | + ((t_value) cptr[1] & 0177); + return SCPE_OK; + } + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & DMASK; /* get value */ +j = (opc_val[i] >> F_V_FL) & F_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case F_V_FO: /* func out */ + tptr = strchr (cptr, ','); /* find dst */ + if (!tptr) return SCPE_ARG; /* none? */ + *tptr = 0; /* split fields */ + cptr = get_fnc (cptr, val); /* fo # */ + if (!cptr) return SCPE_ARG; + cptr = get_sd (tptr + 1, val, 0, FALSE); /* dst */ + break; + + case F_V_FOI: /* func out impl */ + cptr = get_fnc (cptr, val); /* fo # */ + break; + + case F_V_SF: /* skip func */ + cptr = get_sd (cptr, val, ',', TRUE); /* src */ + if (!cptr) return SCPE_ARG; + + case F_V_SFI: /* skip func impl */ + cptr = get_fnc (cptr, val); /* fo # */ + break; + + case F_V_RR: /* reg-reg */ + cptr = get_sd (cptr, val, ',', TRUE); /* src */ + if (!cptr) return SCPE_ARG; + cptr = get_op (cptr, val, ','); /* op */ + if (!cptr) return SCPE_ARG; + cptr = get_sd (cptr, val, 0, FALSE); /* dst */ + break; + + case F_V_ZR: /* zero-reg */ + cptr = get_op (cptr, val, ','); /* op */ + if (!cptr) return SCPE_ARG; + cptr = get_sd (cptr, val, 0, FALSE); /* dst */ + break; + + case F_V_RS: /* reg self */ + cptr = get_sd (cptr, val, ',', TRUE); /* src */ + if (!cptr) return SCPE_ARG; + val[0] = val[0] | I_GETSRC (val[0]); /* duplicate */ + cptr = get_op (cptr, val, 0); /* op */ + break; + + case F_V_JC: /* jump cond */ + cptr = get_sd (cptr, val, ',', TRUE); /* src */ + if (!cptr) return SCPE_ARG; + cptr = get_glyph (cptr, gbuf, ','); /* cond */ + for (k = 0; k < 8; k++) { /* symbol? */ + if (strcmp (gbuf, cdname[k]) == 0) break; + } + if (k >= 8) return SCPE_ARG; + val[0] = val[0] | (k << (I_V_OP + 1)); /* or to inst */ + + case F_V_JU: /* jump uncond */ + cptr = get_ma (cptr, val, 0); /* addr */ + break; + + case F_V_RM: /* reg mem */ + cptr = get_sd (cptr, val, ',', TRUE); /* src */ + if (!cptr) return SCPE_ARG; + case F_V_ZM: /* zero mem */ + cptr = get_op (cptr, val, ','); /* op */ + if (!cptr) return SCPE_ARG; + cptr = get_ma (cptr, val, 0); /* addr */ + break; + + case F_V_MR: /* mem reg */ + cptr = get_ma (cptr, val, ','); /* addr */ + if (!cptr) return SCPE_ARG; + cptr = get_op (cptr, val, ','); /* op */ + if (!cptr) return SCPE_ARG; + cptr = get_sd (cptr, val, 0, FALSE); /* dst */ + break; + + case F_V_MS: /* mem self */ + cptr = get_ma (cptr, val, ','); /* addr */ + if (!cptr) return SCPE_ARG; + cptr = get_op (cptr, val, 0); /* op */ + break; + } /* end case */ + +if (!cptr || (*cptr != 0)) return SCPE_ARG; /* junk at end? */ +return (j >= F_2WD)? -1: SCPE_OK; +} diff --git a/H316/h316_cpu.c b/H316/h316_cpu.c new file mode 100644 index 0000000..07778cb --- /dev/null +++ b/H316/h316_cpu.c @@ -0,0 +1,1489 @@ +/* h316_cpu.c: Honeywell 316/516 CPU simulator + + Copyright (c) 1999-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu H316/H516 CPU + + 28-Apr-07 RMS Removed clock initialization + 03-Apr-06 RMS Fixed bugs in LLL, LRL (from Theo Engel) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 15-Feb-05 RMS Added start button interrupt + 01-Dec-04 RMS Fixed bug in DIV + 06-Nov-04 RMS Added =n to SHOW HISTORY + 04-Jan-04 RMS Removed unnecessary compare + 31-Dec-03 RMS Fixed bug in cpu_set_hist + 24-Oct-03 RMS Added DMA/DMC support, instruction history + 30-Dec-01 RMS Added old PC queue + 03-Nov-01 RMS Fixed NOHSA modifier + 30-Nov-01 RMS Added extended SET/SHOW support + + The register state for the Honeywell 316/516 CPU is: + + AR<1:16> A register + BR<1:16> B register + XR<1:16> X register + PC<1:16> P register (program counter) + Y<1:16> memory address register + MB<1:16> memory data register + C overflow flag + EXT extend mode flag + DP double precision mode flag + SC<1:5> shift count + SR[1:4]<0> sense switches 1-4 + + The Honeywell 316/516 has six instruction formats: memory reference, + I/O, control, shift, skip, and operate. + + The memory reference format is: + + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in|xr| op |sc| offset | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <13:10> mnemonic action + + 0000 (other) see control, shift, skip, operate instructions + 0001 JMP P = MA + 0010 LDA A = M[MA] + 0011 ANA A = A & M[MA] + 0100 STA M[MA] = A + 0101 ERA A = A ^ M[MA] + 0110 ADD A = A + M[MA] + 0111 SUB A = A - M[MA] + 1000 JST M[MA] = P, P = MA + 1 + 1001 CAS skip if A == M[MA], double skip if A < M[MA] + 1010 IRS M[MA] = M[MA] + 1, skip if M[MA] == 0 + 1011 IMA A <=> M[MA] + 1100 (I/O) see I/O instructions + 1101 LDX/STX X = M[MA] (xr = 1), M[MA] = x (xr = 0) + 1110 MPY multiply + 1111 DIV divide + + In non-extend mode, memory reference instructions can access an address + space of 16K words. Multiple levels of indirection are supported, and + each indirect word supplies its own indirect and index bits. + + <1,2,7> mode action + + 0,0,0 sector zero direct MA = IR<8:0> + 0,0,1 current direct MA = P<13:9>'IR<8:0> + 0,1,0 sector zero indexed MA = IR<8:0> + X + 0,1,1 current direct MA = P<13:9>'IR<8:0> + X + 1,0,0 sector zero indirect MA = M[IR<8:0>] + 1,0,1 current indirect MA = M[P<13:9>'IR<8:0>] + 1,1,0 sector zero indirect indexed MA = M[IR<8:0> + X] + 1,1,1 current indirect indexed MA = M[MA = P<13:9>'IR<8:0> + X] + + In extend mode, memory reference instructions can access an address + space of 32K words. Multiple levels of indirection are supported, but + only post-indexing, based on the original instruction word index flag, + is allowed. + + The control format is: + + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0 0 0| opcode | control + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The shift format is: + + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 1 0 0 0 0|dr|sz|type | shift count | shift + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | \-+-/ + | | | + | | +--------------------- type + | +------------------------- long/A only + +---------------------------- right/left + + The skip format is: + + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 0 0 0 0 0|rv|po|pe|ev|ze|s1|s2|s3|s4|cz| skip + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | | | + | | | | | | | | | +- skip if C = 0 + | | | | | | | | +---- skip if ssw 4 = 0 + | | | | | | | +------- skip if ssw 3 = 0 + | | | | | | +---------- skip if ssw 2 = 0 + | | | | | +------------- skip if ssw 1 = 0 + | | | | +---------------- skip if A == 0 + | | | +------------------- skip if A<0> == 0 + | | +---------------------- skip if mem par err + | +------------------------- skip if A<15> = 0 + +---------------------------- reverse skip sense + + The operate format is: + + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 0 0 0 0| opcode | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The I/O format is: + + 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | 1 1 0 0| function | device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction controls the specified device. + Depending on the opcode, the instruction may set or clear + the device flag, start or stop I/O, or read or write data. + + This routine is the instruction decode routine for the Honeywell + 316/516. It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + infinite indirection loop + unimplemented instruction and stop_inst flag set + unknown I/O device and stop_dev flag set + I/O error in I/O simulator + + 2. Interrupts. Interrupts are maintained by two parallel variables: + + dev_int device interrupt flags + dev_enb device interrupt enable flags + + In addition, dev_int contains the interrupt enable and interrupt no + defer flags. If interrupt enable and interrupt no defer are set, and + at least one interrupt request is pending, then an interrupt occurs. + The order of flags in these variables corresponds to the order + in the SMK instruction. + + 3. Non-existent memory. On the H316/516, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + h316_defs.h add interrupt request definition + h316_cpu.c add device dispatch table entry + h316_sys.c add sim_devices table entry +*/ + +#include "h316_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC +#define PCQ_TOP pcq[pcq_p] +#define m7 0001000 /* for generics */ +#define m8 0000400 +#define m9 0000200 +#define m10 0000100 +#define m11 0000040 +#define m12 0000020 +#define m13 0000010 +#define m14 0000004 +#define m15 0000002 +#define m16 0000001 + +#define HIST_PC 0x40000000 +#define HIST_C 0x20000000 +#define HIST_EA 0x10000000 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + int32 pc; + int32 ir; + int32 ar; + int32 br; + int32 xr; + int32 ea; + int32 opnd; + } InstHistory; + +uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 saved_AR = 0; /* A register */ +int32 saved_BR = 0; /* B register */ +int32 saved_XR = 0; /* X register */ +int32 PC = 0; /* P register */ +int32 C = 0; /* C register */ +int32 ext = 0; /* extend mode */ +int32 pme = 0; /* prev mode extend */ +int32 extoff_pending = 0; /* extend off pending */ +int32 dp = 0; /* double mode */ +int32 sc = 0; /* shift count */ +int32 ss[4]; /* sense switches */ +int32 dev_int = 0; /* dev ready */ +int32 dev_enb = 0; /* dev enable */ +int32 ind_max = 8; /* iadr nest limit */ +int32 stop_inst = 1; /* stop on ill inst */ +int32 stop_dev = 2; /* stop on ill dev */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +uint32 dma_nch = DMA_MAX; /* number of chan */ +uint32 dma_ad[DMA_MAX] = { 0 }; /* DMA addresses */ +uint32 dma_wc[DMA_MAX] = { 0 }; /* DMA word count */ +uint32 dma_eor[DMA_MAX] = { 0 }; /* DMA end of range */ +uint32 chan_req = 0; /* channel requests */ +uint32 chan_map[DMA_MAX + DMC_MAX] = { 0 }; /* chan->dev map */ +int32 (*iotab[DEV_MAX])(int32 inst, int32 fnc, int32 dat, int32 dev) = { NULL }; +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ + +extern int32 sim_int_char; +extern int32 sim_interval; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern FILE *sim_log; +extern DEVICE *sim_devices[]; + +t_bool devtab_init (void); +int32 dmaio (int32 inst, int32 fnc, int32 dat, int32 dev); +int32 undio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_show_dma (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_nchan (FILE *st, UNIT *uptr, int32 val, void *desc); + +extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +DIB cpu_dib = { DMA, IOBUS, 1, &dmaio }; + +UNIT cpu_unit = { + UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_EXT+UNIT_HSA+UNIT_DMC, MAXMEMSIZE) + }; + +REG cpu_reg[] = { + { ORDATA (P, PC, 15) }, + { ORDATA (A, saved_AR, 16) }, + { ORDATA (B, saved_BR, 16) }, + { ORDATA (X, XR, 16) }, + { ORDATA (SC, sc, 16) }, + { FLDATA (C, C, 0) }, + { FLDATA (EXT, ext, 0) }, + { FLDATA (PME, pme, 0) }, + { FLDATA (EXT_OFF, extoff_pending, 0) }, + { FLDATA (DP, dp, 0) }, + { FLDATA (SS1, ss[0], 0) }, + { FLDATA (SS2, ss[1], 0) }, + { FLDATA (SS3, ss[2], 0) }, + { FLDATA (SS4, ss[3], 0) }, + { FLDATA (ION, dev_int, INT_V_ON) }, + { FLDATA (INODEF, dev_int, INT_V_NODEF) }, + { FLDATA (START, dev_int, INT_V_START) }, + { ORDATA (DEVINT, dev_int, 16), REG_RO }, + { ORDATA (DEVENB, dev_enb, 16), REG_RO }, + { ORDATA (CHREQ, chan_req, DMA_MAX + DMC_MAX) }, + { BRDATA (DMAAD, dma_ad, 8, 16, DMA_MAX) }, + { BRDATA (DMAWC, dma_wc, 8, 16, DMA_MAX) }, + { BRDATA (DMAEOR, dma_eor, 8, 1, DMA_MAX) }, + { ORDATA (DMANCH, dma_nch, 3), REG_HRO }, + { FLDATA (MPERDY, dev_int, INT_V_MPE) }, + { FLDATA (MPEENB, dev_enb, INT_V_MPE) }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (STOP_DEV, stop_dev, 1) }, + { DRDATA (INDMAX, ind_max, 8), REG_NZ + PV_LEFT }, + { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO + REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_EXT, 0, "no extend", "NOEXTEND", &cpu_set_noext }, + { UNIT_EXT, UNIT_EXT, "extend", "EXTEND", NULL }, + { UNIT_HSA, 0, "no HSA", "NOHSA", NULL }, + { UNIT_HSA, UNIT_HSA, "HSA", "HSA", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { MTAB_XTD | MTAB_VDV, 0, "channels", "CHANNELS", + &cpu_set_nchan, &cpu_show_nchan, NULL }, + { UNIT_DMC, 0, "no DMC", "NODMC", NULL }, + { UNIT_DMC, UNIT_DMC, "DMC", "DMC", NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DMA1", NULL, + NULL, &cpu_show_dma, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "DMA2", NULL, + NULL, &cpu_show_dma, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 2, "DMA3", NULL, + NULL, &cpu_show_dma, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 3, "DMA4", NULL, + NULL, &cpu_show_dma, NULL }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + &cpu_dib, 0 + }; + +t_stat sim_instr (void) +{ +int32 AR, BR, MB, Y, t1, t2, t3, skip, dev; +uint32 ut; +t_stat reason; +t_stat Ea (int32 inst, int32 *addr); +void Write (int32 addr, int32 val); +int32 Add16 (int32 val1, int32 val2); +int32 Add31 (int32 val1, int32 val2); +int32 Operate (int32 MB, int32 AR); + +#define Read(ad) M[(ad)] +#define GETDBL_S(h,l) (((h) << 15) | ((l) & MMASK)) +#define GETDBL_U(h,l) (((h) << 16) | (l)) +#define PUTDBL_S(x) AR = ((x) >> 15) & DMASK; \ + BR = (BR & SIGN) | ((x) & MMASK) +#define PUTDBL_U(x) AR = ((x) >> 16) & DMASK; \ + BR = (x) & DMASK +#define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): ((x) & DMASK)) +#define NEWA(c,n) (ext? (((c) & ~X_AMASK) | ((n) & X_AMASK)): \ + (((c) & ~NX_AMASK) | ((n) & NX_AMASK))) + +/* Restore register state */ + +if (devtab_init ()) return SCPE_STOP; /* init tables */ +AR = saved_AR & DMASK; /* restore reg */ +BR = saved_BR & DMASK; +XR = saved_XR & DMASK; +PC = PC & ((cpu_unit.flags & UNIT_EXT)? X_AMASK: NX_AMASK); /* mask PC */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + +/* Channel breaks (DMA and DMC) */ + +if (chan_req) { /* channel request? */ + int32 i, t, ch, dev, st, end, ad, dmcad; + t_stat r; + for (i = 0, ch = chan_req; ch != 0; i++, ch = ch >> 1) { + if (ch & 1) { /* req on chan i? */ + dev = chan_map[i]; /* get dev for chan */ + if (iotab[dev] == &undio) return SCPE_IERR; + chan_req = chan_req & ~(1 << i); /* clear req */ + if (Q_DMA (i)) st = dma_ad[i]; /* DMA? */ + else { /* DMC */ + dmcad = DMC_BASE + ((i - DMC_V_DMC1) << 1); + st = Read (dmcad); /* DMC ctrl word */ + } + ad = st & X_AMASK; /* get curr addr */ + if (st & DMA_IN) { /* input? */ + t = iotab[dev] (ioINA, 0, 0, dev); /* input word */ + if ((t & IOT_SKIP) == 0) return STOP_DMAER; + if ((r = t >> IOT_V_REASON) != 0) return r; + Write (ad, t & DMASK); /* write to mem */ + } + else { /* no, output */ + t = iotab[dev] (ioOTA, 0, Read (ad), dev); /* output word */ + if ((t & IOT_SKIP) == 0) return STOP_DMAER; + if (r = (t >> IOT_V_REASON)) return r; + } + if (Q_DMA (i)) { /* DMA? */ + dma_ad[i] = (dma_ad[i] & DMA_IN) | ((ad + 1) & X_AMASK); + dma_wc[i] = (dma_wc[i] + 1) & 077777; /* update wc */ + if (dma_wc[i] == 0) { /* done? */ + dma_eor[i] = 1; /* set end of range */ + t = iotab[dev] (ioEND, 0, 0, dev); /* send end range */ + if ((r = t >> IOT_V_REASON) != 0) return r; + } + } + else { /* DMC */ + st = (st & DMA_IN) | ((ad + 1) & X_AMASK); + Write (dmcad, st); /* update start */ + end = Read (dmcad + 1); /* get end */ + if (((ad ^ end) & X_AMASK) == 0) { /* start == end? */ + t = iotab[dev] (ioEND, 0, 0, dev); /* send end range */ + if ((r = t >> IOT_V_REASON) != 0) return r; + } /* end if end range */ + } /* end else DMC */ + } /* end if chan i */ + } /* end for */ + } /* end if chan_req */ + + +/* Interrupts */ + +if ((dev_int & (INT_PEND|INT_NMI|dev_enb)) > INT_PEND) {/* int req? */ + pme = ext; /* save extend */ + if (cpu_unit.flags & UNIT_EXT) ext = 1; /* ext opt? extend on */ + dev_int = dev_int & ~INT_ON; /* intr off */ + MB = 0120000 | M_INT; /* inst = JST* 63 */ + } + +/* Instruction fetch */ + +else { + if (sim_brk_summ && + sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + Y = PC; /* set mem addr */ + MB = Read (Y); /* fetch instr */ + PC = NEWA (Y, Y + 1); /* incr PC */ + dev_int = dev_int | INT_NODEF; + } + +dev_int = dev_int & ~INT_START; /* clr start button int */ +sim_interval = sim_interval - 1; +if (hst_lnt) { /* instr hist? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) hst_p = 0; + hst[hst_p].pc = Y | HIST_PC | (C? HIST_C: 0); /* fill slots */ + hst[hst_p].ir = MB; + hst[hst_p].ar = AR; + hst[hst_p].br = BR; + hst[hst_p].xr = XR; + } + +/* Memory reference instructions */ + +switch (I_GETOP (MB)) { /* case on <1:6> */ + + case 001: case 021: case 041: case 061: /* JMP */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + PCQ_ENTRY; /* save PC */ + PC = NEWA (PC, Y); /* set new PC */ + if (extoff_pending) ext = extoff_pending = 0; /* cond ext off */ + break; + + case 002: case 022: case 042: case 062: /* LDA */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + if (dp) { /* double prec? */ + AR = Read (Y & ~1); /* get doubleword */ + BR = Read (Y | 1); + sc = 0; + } + else AR = Read (Y); /* no, get word */ + break; + + case 003: case 023: case 043: case 063: /* ANA */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + AR = AR & Read (Y); + break; + + case 004: case 024: case 044: case 064: /* STA */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + Write (Y, AR); /* store A */ + if (dp) { /* double prec? */ + Write (Y | 1, BR); /* store B */ + sc = 0; + } + break; + + case 005: case 025: case 045: case 065: /* ERA */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + AR = AR ^ Read (Y); + break; + + case 006: case 026: case 046: case 066: /* ADD */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + if (dp) { /* double prec? */ + t1 = GETDBL_S (AR, BR); /* get A'B */ + t2 = GETDBL_S (Read (Y & ~1), Read (Y | 1)); + t1 = Add31 (t1, t2); /* 31b add */ + PUTDBL_S (t1); + sc = 0; + } + else AR = Add16 (AR, Read (Y)); /* no, 16b add */ + break; + + case 007: case 027: case 047: case 067: /* SUB */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + if (dp) { /* double prec? */ + t1 = GETDBL_S (AR, BR); /* get A'B */ + t2 = GETDBL_S (Read (Y & ~1), Read (Y | 1)); + t1 = Add31 (t1, -t2); /* 31b sub */ + PUTDBL_S (t1); + sc = 0; + } + else AR = Add16 (AR, (-Read (Y)) & DMASK); /* no, 16b sub */ + break; + + case 010: case 030: case 050: case 070: /* JST */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + MB = NEWA (Read (Y), PC); /* merge old PC */ + Write (Y, MB); + PCQ_ENTRY; + PC = NEWA (PC, Y + 1); /* set new PC */ + break; + + case 011: case 031: case 051: case 071: /* CAS */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + MB = Read (Y); + if (AR == MB) PC = NEWA (PC, PC + 1); + else if (SEXT (AR) < SEXT (MB)) PC = NEWA (PC, PC + 2); + break; + + case 012: case 032: case 052: case 072: /* IRS */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + MB = (Read (Y) + 1) & DMASK; /* incr, rewrite */ + Write (Y, MB); + if (MB == 0) PC = NEWA (PC, PC + 1); /* skip if zero */ + break; + + case 013: case 033: case 053: case 073: /* IMA */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + MB = Read (Y); + Write (Y, AR); /* A to mem */ + AR = MB; /* mem to A */ + break; + + case 015: case 055: /* STX */ + if (reason = Ea (MB & ~IDX, &Y)) break; /* eff addr */ + Write (Y, XR); /* store XR */ + break; + + case 035: case 075: /* LDX */ + if (reason = Ea (MB & ~IDX, &Y)) break; /* eff addr */ + XR = Read (Y); /* load XR */ + break; + + case 016: case 036: case 056: case 076: /* MPY */ + if (cpu_unit.flags & UNIT_HSA) { /* installed? */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + t1 = SEXT (AR) * SEXT (Read (Y)); + PUTDBL_S (t1); + sc = 0; + } + else reason = stop_inst; + break; + + case 017: case 037: case 057: case 077: /* DIV */ + if (cpu_unit.flags & UNIT_HSA) { /* installed? */ + if (reason = Ea (MB, &Y)) break; /* eff addr */ + t2 = SEXT (Read (Y)); /* divr */ + if (t2) { /* divr != 0? */ + t1 = GETDBL_S (SEXT (AR), BR); /* get A'B signed */ + BR = (t1 % t2) & DMASK; /* remainder */ + t1 = t1 / t2; /* quotient */ + AR = t1 & DMASK; + if ((t1 > MMASK) || (t1 < (-SIGN))) C = 1; + else C = 0; + sc = 0; + } + else C = 1; + } + else reason = stop_inst; + break; + +/* I/O instructions */ + + case 014: /* OCP */ + dev = MB & DEVMASK; + t2 = iotab[dev] (ioOCP, I_GETFNC (MB), AR, dev); + reason = t2 >> IOT_V_REASON; + break; + + case 034: /* SKS */ + dev = MB & DEVMASK; + t2 = iotab[dev] (ioSKS, I_GETFNC (MB), AR, dev); + reason = t2 >> IOT_V_REASON; + if (t2 & IOT_SKIP) PC = NEWA (PC, PC + 1); /* skip? */ + break; + + case 054: /* INA */ + dev = MB & DEVMASK; + if (MB & INCLRA) AR = 0; + t2 = iotab[dev] (ioINA, I_GETFNC (MB & ~INCLRA), AR, dev); + reason = t2 >> IOT_V_REASON; + if (t2 & IOT_SKIP) PC = NEWA (PC, PC + 1); /* skip? */ + AR = t2 & DMASK; /* data */ + break; + + case 074: /* OTA */ + dev = MB & DEVMASK; + t2 = iotab[dev] (ioOTA, I_GETFNC (MB), AR, dev); + reason = t2 >> IOT_V_REASON; + if (t2 & IOT_SKIP) PC = NEWA (PC, PC + 1); /* skip? */ + break; + +/* Control */ + + case 000: + if ((MB & 1) == 0) { /* HLT */ + if ((reason = sim_process_event ()) != SCPE_OK) break; + reason = STOP_HALT; + break; + } + if (MB & m14) { /* SGL, DBL */ + if (cpu_unit.flags & UNIT_HSA) dp = (MB & m15)? 1: 0; + else reason = stop_inst; + } + if (MB & m13) { /* DXA, EXA */ + if (!(cpu_unit.flags & UNIT_EXT)) reason = stop_inst; + else if (MB & m15) { /* EXA */ + ext = 1; + extoff_pending = 0; /* DXA */ + } + else extoff_pending = 1; + } + if (MB & m12) CLR_INT (INT_MPE); /* RMP */ + if (MB & m11) { /* SCA, INK */ + if (MB & m15) /* INK */ + AR = (C << 15) | (dp << 14) | (pme << 13) | (sc & 037); + else if (cpu_unit.flags & UNIT_HSA) /* SCA */ + AR = sc & 037; + else reason = stop_inst; + } + else if (MB & m10) { /* NRM */ + if (cpu_unit.flags & UNIT_HSA) { + for (sc = 0; + (sc <= 32) && ((AR & SIGN) != ((AR << 1) & SIGN)); + sc++) { + AR = (AR & SIGN) | ((AR << 1) & MMASK) | + ((BR >> 14) & 1); + BR = (BR & SIGN) | ((BR << 1) & MMASK); + } + sc = sc & 037; + } + else reason = stop_inst; + } + else if (MB & m9) { /* IAB */ + sc = BR; + BR = AR; + AR = sc; + } + if (MB & m8) /* ENB */ + dev_int = (dev_int | INT_ON) & ~INT_NODEF; + if (MB & m7) /* INH */ + dev_int = dev_int & ~INT_ON; + break; + +/* Shift + + Shifts are microcoded as follows: + + op<7> = right/left + op<8> = long/short + op<9> = shift/rotate (rotate bits "or" into new position) + op<10> = logical/arithmetic + + If !op<7> && op<10> (right arithmetic), A<1> propagates rightward + If op<7> && op<10> (left arithmetic), C is set if A<1> changes state + If !op<8> && op<10> (long arithmetic), B<1> is skipped + + This microcoding "explains" how the 4 undefined opcodes actually work + 003 = long arith rotate right, skip B<1>, propagate A<1>, + bits rotated out "or" into A<1> + 007 = short arith rotate right, propagate A<1>, + bits rotated out "or" into A<1> + 013 = long arith rotate left, skip B<1>, C = overflow + 017 = short arith rotate left, C = overflow +*/ + + case 020: + C = 0; /* clear C */ + sc = 0; /* clear sc */ + if ((t1 = (-MB) & SHFMASK) == 0) break; /* shift count */ + switch (I_GETFNC (MB)) { /* case shift fnc */ + + case 000: /* LRL */ + if (t1 > 32) ut = 0; /* >32? all 0 */ + else { + ut = GETDBL_U (AR, BR); /* get A'B */ + C = (ut >> (t1 - 1)) & 1; /* C = last out */ + if (t1 == 32) ut = 0; /* =32? all 0 */ + else ut = ut >> t1; /* log right */ + } + PUTDBL_U (ut); /* store A,B */ + break; + + case 001: /* LRS */ + if (t1 > 31) t1 = 31; /* limit to 31 */ + t2 = GETDBL_S (SEXT (AR), BR); /* get A'B signed */ + C = (t2 >> (t1 - 1)) & 1; /* C = last out */ + t2 = t2 >> t1; /* arith right */ + PUTDBL_S (t2); /* store A,B */ + break; + + case 002: /* LRR */ + t2 = t1 % 32; /* mod 32 */ + ut = GETDBL_U (AR, BR); /* get A'B */ + ut = (ut >> t2) | (ut << (32 - t2)); /* rot right */ + C = (ut >> 31) & 1; /* C = A<1> */ + PUTDBL_U (ut); /* store A,B */ + break; + + case 003: /* "long right arot" */ + if (reason = stop_inst) break; /* stop on undef? */ + for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ + C = BR & 1; /* C = last out */ + BR = (BR & SIGN) | ((AR & 1) << 14) | + ((BR & MMASK) >> 1); + AR = ((AR & SIGN) | (C << 15)) | (AR >> 1); + } + break; + + case 004: /* LGR */ + if (t1 > 16) AR = 0; /* > 16? all 0 */ + else { + C = (AR >> (t1 - 1)) & 1; /* C = last out */ + AR = (AR >> t1) & DMASK; /* log right */ + } + break; + + case 005: /* ARS */ + if (t1 > 16) t1 = 16; /* limit to 16 */ + C = ((SEXT (AR)) >> (t1 - 1)) & 1; /* C = last out */ + AR = ((SEXT (AR)) >> t1) & DMASK; /* arith right */ + break; + + case 006: /* ARR */ + t2 = t1 % 16; /* mod 16 */ + AR = ((AR >> t2) | (AR << (16 - t2))) & DMASK; + C = (AR >> 15) & 1; /* C = A<1> */ + break; + + case 007: /* "short right arot" */ + if (reason = stop_inst) break; /* stop on undef? */ + for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ + C = AR & 1; /* C = last out */ + AR = ((AR & SIGN) | (C << 15)) | (AR >> 1); + } + break; + + case 010: /* LLL */ + if (t1 > 32) ut = 0; /* > 32? all 0 */ + else { + ut = GETDBL_U (AR, BR); /* get A'B */ + C = (ut >> (32 - t1)) & 1; /* C = last out */ + if (t1 == 32) ut = 0; /* =32? all 0 */ + else ut = ut << t1; /* log left */ + } + PUTDBL_U (ut); /* store A,B */ + break; + + case 011: /* LLS */ + if (t1 > 31) t1 = 31; /* limit to 31 */ + t2 = GETDBL_S (SEXT (AR), BR); /* get A'B */ + t3 = t2 << t1; /* "arith" left */ + PUTDBL_S (t3); /* store A'B */ + if ((t2 >> (31 - t1)) != /* shf out = sgn? */ + ((AR & SIGN)? -1: 0)) C = 1; + break; + + case 012: /* LLR */ + t2 = t1 % 32; /* mod 32 */ + ut = GETDBL_U (AR, BR); /* get A'B */ + ut = (ut << t2) | (ut >> (32 - t2)); /* rot left */ + C = ut & 1; /* C = B<16> */ + PUTDBL_U (ut); /* store A,B */ + break; + + case 013: /* "long left arot" */ + if (reason = stop_inst) break; /* stop on undef? */ + for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ + AR = (AR << 1) | ((BR >> 14) & 1); + BR = (BR & SIGN) | ((BR << 1) & MMASK) | + ((AR >> 16) & 1); + if ((AR & SIGN) != ((AR >> 1) & SIGN)) C = 1; + AR = AR & DMASK; + } + break; + + case 014: /* LGL */ + if (t1 > 16) AR = 0; /* > 16? all 0 */ + else { + C = (AR >> (16 - t1)) & 1; /* C = last out */ + AR = (AR << t1) & DMASK; /* log left */ + } + break; + + case 015: /* ALS */ + if (t1 > 16) t1 = 16; /* limit to 16 */ + t2 = SEXT (AR); /* save AR */ + AR = (AR << t1) & DMASK; /* "arith" left */ + if ((t2 >> (16 - t1)) != /* shf out + sgn */ + ((AR & SIGN)? -1: 0)) C = 1; + break; + + case 016: /* ALR */ + t2 = t1 % 16; /* mod 16 */ + AR = ((AR << t2) | (AR >> (16 - t2))) & DMASK; + C = AR & 1; /* C = A<16> */ + break; + + case 017: /* "short left arot" */ + if (reason = stop_inst) break; /* stop on undef? */ + for (t2 = 0; t2 < t1; t2++) { /* bit by bit */ + if ((AR & SIGN) != ((AR << 1) & SIGN)) C = 1; + AR = ((AR << 1) | (AR >> 15)) & DMASK; + } + break; /* end case fnc */ + } + break; + +/* Skip */ + + case 040: + skip = 0; + if (((MB & 000001) && C) || /* SSC */ + ((MB & 000002) && ss[3]) || /* SS4 */ + ((MB & 000004) && ss[2]) || /* SS3 */ + ((MB & 000010) && ss[1]) || /* SS2 */ + ((MB & 000020) && ss[0]) || /* SS1 */ + ((MB & 000040) && AR) || /* SNZ */ + ((MB & 000100) && (AR & 1)) || /* SLN */ + ((MB & 000200) && (TST_INTREQ (INT_MPE))) || /* SPS */ + ((MB & 000400) && (AR & SIGN))) skip = 1; /* SMI */ + if ((MB & 001000) == 0) skip = skip ^ 1; /* reverse? */ + PC = NEWA (PC, PC + skip); + break; + +/* Operate */ + + case 060: + if (MB == 0140024) AR = AR ^ SIGN; /* CHS */ + else if (MB == 0140040) AR = 0; /* CRA */ + else if (MB == 0140100) AR = AR & ~SIGN; /* SSP */ + else if (MB == 0140200) C = 0; /* RCB */ + else if (MB == 0140320) { /* CSA */ + C = (AR & SIGN) >> 15; + AR = AR & ~SIGN; + } + else if (MB == 0140401) AR = AR ^ DMASK; /* CMA */ + else if (MB == 0140407) { /* TCA */ + AR = (-AR) & DMASK; + sc = 0; + } + else if (MB == 0140500) AR = AR | SIGN; /* SSM */ + else if (MB == 0140600) C = 1; /* SCB */ + else if (MB == 0141044) AR = AR & 0177400; /* CAR */ + else if (MB == 0141050) AR = AR & 0377; /* CAL */ + else if (MB == 0141140) AR = AR >> 8; /* ICL */ + else if (MB == 0141206) AR = Add16 (AR, 1); /* AOA */ + else if (MB == 0141216) AR = Add16 (AR, C); /* ACA */ + else if (MB == 0141240) AR = (AR << 8) & DMASK; /* ICR */ + else if (MB == 0141340) /* ICA */ + AR = ((AR << 8) | (AR >> 8)) & DMASK; + else if (reason = stop_inst) break; + else AR = Operate (MB, AR); /* undefined */ + break; + } /* end case op */ + } /* end while */ + +saved_AR = AR & DMASK; +saved_BR = BR & DMASK; +saved_XR = XR & DMASK; +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Effective address + + The effective address calculation consists of three phases: + - base address calculation: 0/pagenumber'displacement + - (extend): indirect address resolution + (non-extend): pre-indexing + - (extend): post-indexing + (non-extend): indirect address/post-indexing resolution + + In extend mode, address calculations are carried out to 16b + and masked to 15b at exit. In non-extend mode, address bits + <1:2> are preserved by the NEWA macro; address bit <1> is + masked at exit. +*/ + +t_stat Ea (int32 IR, int32 *addr) +{ +int32 i; +int32 Y = IR & (IA | DISP); /* ind + disp */ + +if (IR & SC) Y = ((PC - 1) & PAGENO) | Y; /* cur sec? + pageno */ +if (ext) { /* extend mode? */ + for (i = 0; (i < ind_max) && (Y & IA); i++) { /* resolve ind addr */ + Y = Read (Y & X_AMASK); /* get ind addr */ + } + if (IR & IDX) Y = Y + XR; /* post-index */ + } /* end if ext */ +else { /* non-extend */ + Y = NEWA (PC, Y + ((IR & IDX)? XR: 0)); /* pre-index */ + for (i = 0; (i < ind_max) && (IR & IA); i++) { /* resolve ind addr */ + IR = Read (Y & X_AMASK); /* get ind addr */ + Y = NEWA (Y, IR + ((IR & IDX)? XR: 0)); /* post-index */ + } + } /* end else */ +*addr = Y = Y & X_AMASK; /* return addr */ +if (hst_lnt) { /* history? */ + hst[hst_p].pc = hst[hst_p].pc | HIST_EA; + hst[hst_p].ea = Y; + hst[hst_p].opnd = Read (Y); + } +if (i >= ind_max) return STOP_IND; /* too many ind? */ +return SCPE_OK; +} + +/* Write memory */ + +void Write (int32 addr, int32 val) +{ +if (((addr == 0) || (addr >= 020)) && MEM_ADDR_OK (addr)) + M[addr] = val; +return; +} + +/* Add */ + +int32 Add16 (int32 v1, int32 v2) +{ +int32 r = v1 + v2; + +if (((v1 ^ ~v2) & (v1 ^ r)) & SIGN) C = 1; +else C = 0; +return (r & DMASK); +} + +int32 Add31 (int32 v1, int32 v2) +{ +int32 r = v1 + v2; + +if (((v1 ^ ~v2) & (v1 ^ r)) & DP_SIGN) C = 1; +else C = 0; +return r; +} + +/* Unimplemented I/O device */ + +int32 undio (int32 op, int32 fnc, int32 val, int32 dev) +{ +return ((stop_dev << IOT_V_REASON) | val); +} + +/* DMA control */ + +int32 dmaio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +int32 ch = (fnc - 1) & 03; + +switch (inst) { /* case on opcode */ + + case ioOCP: /* OCP */ + if ((fnc >= 001) && (fnc <= 004)) { /* load addr ctr */ + dma_ad[ch] = dat; + dma_wc[ch] = 0; + dma_eor[ch] = 0; + } + else if ((fnc >= 011) && (fnc <= 014)) /* load range ctr */ + dma_wc[ch] = (dma_wc[ch] | dat) & 077777; + else return IOBADFNC (dat); /* undefined */ + break; + + case ioINA: /* INA */ + if ((fnc >= 011) && (fnc <= 014)) { + if (dma_eor[ch]) return dat; /* end range? nop */ + return IOSKIP (0100000 | dma_wc[ch]); /* return range */ + } + else return IOBADFNC (dat); + } + +return dat; +} + +/* Undefined operate instruction. This code is reached when the + opcode does not correspond to a standard operate instruction. + It simulates the behavior of the actual logic. + + An operate instruction executes in 4 or 6 phases. A 'normal' + instruction takes 4 phases: + + t1 t1 + t2/tlate t2/t2 extended into t3 + t3/tlate t3 + t4 t4 + + A '1.5 cycle' instruction takes 6 phases: + + t1 t1 + t2/tlate t2/t2 extended into t3 + t3/tlate t3 + t2/tlate 'special' t2/t2 extended into t3 + t3/tlate t3 + t4 t4 + + The key signals, by phase, are the following + + tlate EASTL enable A to sum leg 1 (else 0) + (((m12+m16)x!azzzz)+(m9+m11+azzzz) + EASBM enable 0 to sum leg 2 (else 177777) + (m9+m11+azzzz) + JAMKN jam carry network to 0 = force XOR + ((m12+m16)x!azzzz) + EIKI7 force carry into adder + ((m15x(C+!m13))x!JAMKN) + + t3 CLDTR set D to 177777 (always) + ESDTS enable adder sum to D (always) + SETAZ enable repeat cycle = set azzzz + (m8xm15) + + if azzzz { + t2 CLATR clear A register (due to azzzz) + EDAHS enable D high to A high register (due to azzzz) + EDALS enable D low to A low register (due to azzzz) + + tlate, t3 as above + } + + t4 CLATR clear A register + (m11+m15+m16) + CLA1R clear A1 register + (m10+m14) + EDAHS enable D high to A high register + ((m11xm14)+m15+m16) + EDALS enable D low to A low register + ((m11xm13)+m15+m16) + ETAHS enable D transposed to A high register + (m9xm11) + ETALS enable D transposed to A low register + (m10xm11) + EDA1R enable D1 to A1 register + ((m8xm10)+m14) + CBITL clear C, conditionally set C from adder output + (m9x!m11) + CBITG conditionally set C if D1 + (m10xm12xD1) + CBITE unconditionally set C + (m8xm9) +*/ + +int32 Operate (int32 MB, int32 AR) +{ +int32 D, jamkn, eiki7, easbm, eastl, setaz; +int32 clatr, cla1r, edahs, edals, etahs, etals, eda1r; +int32 cbitl, cbitg, cbite; +int32 aleg, bleg, ARx; + +/* Phase tlate */ + +ARx = AR; /* default */ +jamkn = (MB & (m12+m16)) != 0; /* m12+m16 */ +easbm = (MB & (m9+m11)) != 0; /* m9+m11 */ +eastl = jamkn || easbm; /* m9+m11+m12+m16 */ +setaz = (MB & (m8+m15)) == (m8+m15); /* m8xm15*/ +eiki7 = (MB & m15) && (C || !(MB & m13)); /* cin */ +aleg = eastl? AR: 0; /* a input */ +bleg = easbm? 0: DMASK; /* b input */ +if (jamkn) D = aleg ^ bleg; /* jammin? xor */ +else D = (aleg + bleg + eiki7) & DMASK; /* else add */ + +/* Possible repeat at end of tlate - special t2, repeat tlate */ + +if (setaz) { + ARx = D; /* forced: t2 */ + aleg = ARx; /* forced: tlate */ + bleg = 0; /* forced */ + jamkn = 0; /* forced */ + D = (aleg + bleg + eiki7) & DMASK; /* forced add */ + sc = 0; /* ends repeat */ + } + +/* Phase t4 */ + +clatr = (MB & (m11+m15+m16)) != 0; /* m11+m15+m16 */ +cla1r = (MB & (m10+m14)) != 0; /* m10+m14 */ +edahs = ((MB & (m11+m14)) == (m11+m14)) || /* (m11xm14)+m15+m16 */ + (MB & (m15+m16)); +edals = ((MB & (m11+m13)) == (m11+m13)) || /* (m11xm13)+m15+m16 */ + (MB & (m15+m16)); +etahs = (MB & (m9+m11)) == (m9+m11); /* m9xm11 */ +etals = (MB & (m10+m11)) == (m10+m11); /* m10xm11 */ +eda1r = ((MB & (m8+m10)) == (m8+m10)) || (MB & m14); /* (m8xm10)+m14 */ +cbitl = (MB & (m9+m11)) == m9; /* m9x!m11 */ +cbite = (MB & (m8+m9)) == (m8+m9); /* m8xm9 */ +cbitg = (MB & (m10+m12)) == (m10+m12); /* m10xm12 */ + +if (clatr) ARx = 0; /* clear A */ +if (cla1r) ARx = ARx & ~SIGN; /* clear A1 */ +if (edahs) ARx = ARx | (D & 0177400); /* D hi to A hi */ +if (edals) ARx = ARx | (D & 0000377); /* D lo to A lo */ +if (etahs) ARx = ARx | ((D << 8) & 0177400); /* D lo to A hi */ +if (etals) ARx = ARx | ((D >> 8) & 0000377); /* D hi to A lo */ +if (eda1r) ARx = ARx | (D & SIGN); /* D1 to A1 */ +if (cbitl) { /* ovflo to C */ + +/* Overflow calculation. Cases: + + aleg bleg cin overflow + + 0 x x can't overflow + A 0 0 can't overflow + A -1 1 can't overflow + A 0 1 overflow if 77777->100000 + A -1 0 overflow if 100000->77777 +*/ + + if (!jamkn && + ((bleg && !eiki7 && (D == 0077777)) || + (!bleg && eiki7 && (D == 0100000)))) C = 1; + else C = 0; + } +if (cbite || (cbitg && (D & SIGN))) C = 1; /* C = 1 */ +return ARx; +} + +/* Reset routines */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int32 i; + +saved_AR = saved_BR = saved_XR = 0; +C = 0; +dp = 0; +ext = pme = extoff_pending = 0; +dev_int = dev_int & ~(INT_PEND|INT_NMI); +dev_enb = 0; +for (i = 0; i < DMA_MAX; i++) dma_ad[i] = dma_wc[i] = dma_eor[i] = 0; +chan_req = 0; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 d; + +if (addr >= MEMSIZE) return SCPE_NXM; +if (addr == 0) d = saved_XR; +else d = M[addr]; +if (vptr != NULL) *vptr = d & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (addr == 0) saved_XR = val & DMASK; +else M[addr] = val & DMASK; +return SCPE_OK; +} + +/* Option processors */ + +t_stat cpu_set_noext (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (MEMSIZE > (NX_AMASK + 1)) return SCPE_ARG; +return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0) || + (((cpu_unit.flags & UNIT_EXT) == 0) && (val > (NX_AMASK + 1)))) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +t_stat cpu_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i, newmax; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newmax = get_uint (cptr, 10, DMA_MAX, &r); /* get new max */ +if ((r != SCPE_OK) || (newmax == dma_nch)) return r; /* err or no chg? */ +dma_nch = newmax; /* set new max */ +for (i = newmax; i < DMA_MAX; i++) { /* reset chan */ + dma_ad[i] = dma_wc[i] = dma_eor[i] = 0; + chan_req = chan_req & ~(1 << i); + } +return SCPE_OK; +} + +/* Show DMA channels */ + +t_stat cpu_show_nchan (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (dma_nch) fprintf (st, "DMA channels = %d", dma_nch); +else fprintf (st, "no DMA channels"); +return SCPE_OK; +} + +/* Show channel state */ + +t_stat cpu_show_dma (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if ((val < 0) || (val >= DMA_MAX)) return SCPE_IERR; +fputs ((dma_ad[val] & DMA_IN)? "Input": "Output", st); +fprintf (st, ", addr = %06o, count = %06o, ", dma_ad[val] & X_AMASK, dma_wc[val]); +fprintf (st, "end of range %s\n", (dma_eor[val]? "set": "clear")); +return SCPE_OK; +} + +/* Set I/O device to IOBUS / DMA channel / DMC channel */ + +t_stat io_set_iobus (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (val || cptr || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +dibp->chan = 0; +return SCPE_OK; +} + +t_stat io_set_dma (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newc; +t_stat r; + +if ((cptr == NULL) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +if (dma_nch == 0) return SCPE_NOFNC; +newc = get_uint (cptr, 10, DMA_MAX, &r); /* get new */ +if ((r != SCPE_OK) || (newc == 0) || (newc > dma_nch)) return SCPE_ARG; +dibp->chan = (newc - DMA_MIN) + DMA_V_DMA1 + 1; /* store */ +return SCPE_OK; +} + +t_stat io_set_dmc (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newc; +t_stat r; + +if ((cptr == NULL) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +if (!(cpu_unit.flags & UNIT_DMC)) return SCPE_NOFNC; +newc = get_uint (cptr, 10, DMC_MAX, &r); /* get new */ +if ((r != SCPE_OK) || (newc == 0)) return SCPE_ARG; +dibp->chan = (newc - DMC_MIN) + DMC_V_DMC1 + 1; /* store */ +return SCPE_OK; +} + +/* Show channel configuration */ + +t_stat io_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +if (dibp->chan == 0) fprintf (st, "IO bus"); +else if (dibp->chan < (DMC_V_DMC1 + 1)) + fprintf (st, "DMA channel %d", dibp->chan); +else fprintf (st, "DMC channel %d", dibp->chan - DMC_V_DMC1); +return SCPE_OK; +} + +/* Set up I/O dispatch and channel maps */ + +t_bool devtab_init (void) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, j, dno, chan; + +for (i = 0; i < DEV_MAX; i++) iotab[i] = NULL; +for (i = 0; i < (DMA_MAX + DMC_MAX); i++) chan_map[i] = 0; +for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if ((dibp == NULL) || (dptr->flags & DEV_DIS)) continue; /* exist, enabled? */ + dno = dibp->dev; /* device number */ + for (j = 0; j < dibp->num; j++) { /* repeat for slots */ + if (iotab[dno + j]) { /* conflict? */ + printf ("%s device number conflict, devno = %02o\n", + sim_dname (dptr), dno + j); + if (sim_log) fprintf (sim_log, + "%s device number conflict, devno = %02o\n", + sim_dname (dptr), dno + j); + return TRUE; + } + iotab[dno + j] = dibp->io; /* set I/O routine */ + } /* end for */ + if (dibp->chan) { /* DMA/DMC? */ + chan = dibp->chan - 1; + if ((chan < DMC_V_DMC1) && (chan >= dma_nch)) { + printf ("%s configured for DMA channel %d\n", + sim_dname (dptr), chan + 1); + if (sim_log) fprintf (sim_log, + "%s configured for DMA channel %d\n", + sim_dname (dptr), chan + 1); + return TRUE; + } + if ((chan >= DMC_V_DMC1) && !(cpu_unit.flags & UNIT_DMC)) { + printf ("%s configured for DMC, option disabled\n", + sim_dname (dptr)); + if (sim_log) fprintf (sim_log, + "%s configured for DMC, option disabled\n", + sim_dname (dptr)); + return TRUE; + } + if (chan_map[chan]) { /* channel conflict? */ + printf ("%s DMA/DMC channel conflict, devno = %02o\n", + sim_dname (dptr), dno); + if (sim_log) fprintf (sim_log, + "%s DMA/DMC channel conflict, devno = %02o\n", + sim_dname (dptr), dno); + return TRUE; + } + chan_map[chan] = dno; /* channel back map */ + } + } /* end for */ +for (i = 0; i < DEV_MAX; i++) { /* fill in blanks */ + if (iotab[i] == NULL) iotab[i] = &undio; + } +return FALSE; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 cr, k, di, op, lnt; +char *cptr = (char *) desc; +t_value sim_eval; +t_stat r; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); +static uint8 has_opnd[16] = { + 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 + }; + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC C A B X ea IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + cr = (h->pc & HIST_C)? 1: 0; /* carry */ + fprintf (st, "%05o %o %06o %06o %06o ", + h->pc & X_AMASK, cr, h->ar, h->br, h->xr); + if (h->pc & HIST_EA) fprintf (st, "%05o ", h->ea); + else fprintf (st, " "); + sim_eval = h->ir; + if ((fprint_sym (st, h->pc & X_AMASK, &sim_eval, + &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %06o", h->ir); + op = I_GETOP (h->ir) & 017; /* base op */ + if (has_opnd[op]) fprintf (st, " [%06o]", h->opnd); + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/H316/h316_defs.h b/H316/h316_defs.h new file mode 100644 index 0000000..510ee6a --- /dev/null +++ b/H316/h316_defs.h @@ -0,0 +1,205 @@ +/* h316_defs.h: Honeywell 316/516 simulator definitions + + Copyright (c) 1999-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-Feb-05 RMS Added start button interrupt + 01-Dec-04 RMS Added double precision constants + 24-Oct-03 RMS Added DMA/DMC support + 25-Apr-03 RMS Revised for extended file support +*/ + +#ifndef _H316_DEFS_H_ +#define _H316_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_IODV 2 /* must be 2 */ +#define STOP_HALT 3 /* HALT */ +#define STOP_IBKPT 4 /* breakpoint */ +#define STOP_IND 5 /* indirect loop */ +#define STOP_DMAER 6 /* DMA error */ +#define STOP_MTWRP 7 /* MT write protected */ +#define STOP_DPOVR 8 /* DP write overrun */ +#define STOP_DPFMT 9 /* DP invalid format */ + +/* Memory */ + +#define MAXMEMSIZE 32768 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define X_AMASK (MAXMEMSIZE - 1) /* ext address mask */ +#define NX_AMASK ((MAXMEMSIZE / 2) - 1) /* nx address mask */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* Architectural constants */ + +#define SIGN 0100000 /* sign */ +#define DP_SIGN 010000000000 +#define DMASK 0177777 /* data mask */ +#define MMASK (DMASK & ~SIGN) /* magnitude mask */ +#define XR M[0] +#define M_CLK 061 /* clock location */ +#define M_RSTINT 062 /* restrict int */ +#define M_INT 063 /* int location */ + +/* CPU options */ + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ +#define UNIT_V_EXT (UNIT_V_UF + 1) /* extended mem */ +#define UNIT_V_HSA (UNIT_V_UF + 2) /* high speed arith */ +#define UNIT_V_DMC (UNIT_V_UF + 3) /* DMC */ +#define UNIT_MSIZE (1u << UNIT_V_MSIZE) +#define UNIT_EXT (1u << UNIT_V_EXT) +#define UNIT_HSA (1u << UNIT_V_HSA) +#define UNIT_DMC (1u << UNIT_V_DMC) + +/* Instruction format */ + +#define I_M_OP 077 /* opcode */ +#define I_V_OP 10 +#define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) +#define I_M_FNC 017 /* function */ +#define I_V_FNC 6 +#define I_GETFNC(x) (((x) >> I_V_FNC) & I_M_FNC) +#define IA 0100000 /* indirect address */ +#define IDX 0040000 /* indexed */ +#define SC 0001000 /* sector */ +#define DISP 0000777 /* page displacement */ +#define PAGENO 0077000 /* page number */ +#define INCLRA (010 << I_V_FNC) /* INA clear A */ +#define DEVMASK 0000077 /* device mask */ +#define SHFMASK 0000077 /* shift mask */ + +/* I/O opcodes */ + +#define ioOCP 0 /* output control */ +#define ioSKS 1 /* skip if set */ +#define ioINA 2 /* input to A */ +#define ioOTA 3 /* output from A */ +#define ioEND 4 /* channel end */ + +/* Device information block */ + +struct h316_dib { + uint32 dev; /* device number */ + uint32 chan; /* dma/dmc channel */ + uint32 num; /* number of slots */ + int32 (*io) (int32 inst, int32 fnc, int32 dat, int32 dev); }; + +typedef struct h316_dib DIB; + +/* DMA/DMC channel numbers */ + +#define IOBUS 0 /* IO bus */ +#define DMA_MIN 1 /* 4 DMA channels */ +#define DMA_MAX 4 +#define DMC_MIN 1 /* 16 DMC channels */ +#define DMC_MAX 16 + +#define DMA1 (DMA_MIN) +#define DMC1 (DMA_MAX+DMC_MIN) + +/* DMA/DMC bit assignments in channel request word */ + +#define DMA_V_DMA1 0 /* DMA channels */ +#define DMC_V_DMC1 4 /* DMC channels */ +#define SET_CH_REQ(x) chan_req = chan_req | (1 << (x)) +#define Q_DMA(x) (((x) >= 0) && ((x) < DMC_V_DMC1)) + +/* DMA/DMC definitions */ + +#define DMA_IN 0100000 /* input flag */ +#define DMC_BASE 020 /* DMC memory base */ + +/* I/O device codes */ + +#define PTR 001 /* paper tape reader */ +#define PTP 002 /* paper tape punch */ +#define LPT 003 /* line printer */ +#define TTY 004 /* console */ +#define CDR 005 /* card reader */ +#define MT 010 /* mag tape data */ +#define CLK_KEYS 020 /* clock/keys (CPU) */ +#define FHD 022 /* fixed head disk */ +#define DMA 024 /* DMA control */ +#define DP 025 /* moving head disk */ +#define DEV_MAX 64 + +/* Interrupt flags, definitions correspond to SMK bits */ + +#define INT_V_CLK 0 /* clock */ +#define INT_V_MPE 1 /* parity error */ +#define INT_V_LPT 2 /* line printer */ +#define INT_V_CDR 4 /* card reader */ +#define INT_V_TTY 5 /* teletype */ +#define INT_V_PTP 6 /* paper tape punch */ +#define INT_V_PTR 7 /* paper tape reader */ +#define INT_V_FHD 8 /* fixed head disk */ +#define INT_V_DP 12 /* moving head disk */ +#define INT_V_MT 15 /* mag tape */ +#define INT_V_START 16 /* start button */ +#define INT_V_NODEF 17 /* int not deferred */ +#define INT_V_ON 18 /* int on */ + +/* I/O macros */ + +#define IOT_V_REASON 17 +#define IOT_V_SKIP 16 +#define IOT_SKIP (1u << IOT_V_SKIP) +#define IORETURN(f,v) (((f)? (v): SCPE_OK) << IOT_V_REASON) +#define IOBADFNC(x) (((stop_inst) << IOT_V_REASON) | (x)) +#define IOSKIP(x) (IOT_SKIP | (x)) + +#define INT_CLK (1u << INT_V_CLK) +#define INT_MPE (1u << INT_V_MPE) +#define INT_LPT (1u << INT_V_LPT) +#define INT_CDR (1u << INT_V_CDR) +#define INT_TTY (1u << INT_V_TTY) +#define INT_PTP (1u << INT_V_PTP) +#define INT_PTR (1u << INT_V_PTR) +#define INT_FHD (1u << INT_V_FHD) +#define INT_DP (1u << INT_V_DP) +#define INT_MT (1u << INT_V_MT) +#define INT_START (1u << INT_V_START) +#define INT_NODEF (1u << INT_V_NODEF) +#define INT_ON (1u << INT_V_ON) +#define INT_NMI (INT_START) +#define INT_PEND (INT_ON | INT_NODEF) + +#define SET_INT(x) dev_int = dev_int | (x) +#define CLR_INT(x) dev_int = dev_int & ~(x) +#define TST_INT(x) ((dev_int & (x)) != 0) +#define CLR_ENB(x) dev_enb = dev_enb & ~(x) +#define TST_INTREQ(x) ((dev_int & dev_enb & (x)) != 0) + +/* Prototypes */ + +t_stat io_set_iobus (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat io_set_dma (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat io_set_dmc (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat io_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc); + +#endif diff --git a/H316/h316_dp.c b/H316/h316_dp.c new file mode 100644 index 0000000..f90ceeb --- /dev/null +++ b/H316/h316_dp.c @@ -0,0 +1,1044 @@ +/* h316_dp.c: Honeywell 4623, 4651, 4720 disk simulator + + Copyright (c) 2003-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dp 4623 disk subsystem + 4651 disk subsystem + 4720 disk subsystem + + 04-Sep-05 RMS Fixed missing return (found by Peter Schorn) + 15-Jul-05 RMS Fixed bug in attach routine + 01-Dec-04 RMS Fixed bug in skip on !seeking + + The Honeywell disks have the unique characteristic of supporting variable + formatting, on a per track basis. To accomodate this, each track is + simulated as 2048 words, divided into records. (2048 words accomodates + the largest record of 1891 + 8 overhead words.) A record is structured + as follows: + + word 0 record length n (0 = end of track) + word 1 record address (16b, uninterpreted by the simulator) + word 2 record extension (0 to 4 words of permitted 'overwrite') + word 3 first data word + : + word 3+n-1 last data word + word 3+n checksum word + word 4+n first extension word + : + word 7+n fourth extension word + word 8+n start of next record + + Formatting is done in two ways. The SET DPn FORMAT=k command formats + unit n with k records per track, each record having the maximum allowable + record size and a standard record address; or with k words per record. + Alternately, the simulator allows programmatic formating. When a track + is formated, the program supplies record parameters as follows: + + word 0 record address + words 1-n data words + word n+1 gap size in bits + + To make this work, the simulator tracks the consumption of bits in the + track, against the track capacity in bits. Bit consumption is: + + 16.5 * 16 for overhead (including address and checksum) + n * 16 for data + 'gap' for gap, which must be at least 5% of the record length +*/ + +#include "h316_defs.h" +#include + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define FNC u3 /* saved function */ +#define CYL u4 /* actual cylinder */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ +#define DP_TRKLEN 2048 /* track length, words */ +#define DP_NUMDRV 8 /* max # drives */ +#define DP_NUMTYP 3 /* # controller types */ + +/* Record format */ + +#define REC_LNT 0 /* length (unextended) */ +#define REC_ADDR 1 /* address */ +#define REC_EXT 2 /* extension (0-4) */ +#define REC_DATA 3 /* start of data */ +#define REC_OVHD 8 /* overhead words */ +#define REC_MAXEXT 4 /* maximum extension */ +#define REC_OVHD_WRDS 16.5 /* 16.5 words */ +#define REC_OVHD_BITS ((16 * 16) + 8) + +/* Status word, ^ = dynamic */ + +#define STA_BUSY 0100000 /* busy */ +#define STA_RDY 0040000 /* ready */ +#define STA_ADRER 0020000 /* address error */ +#define STA_FMTER 0010000 /* format error */ +#define STA_HNLER 0004000 /* heads not loaded (NI) */ +#define STA_OFLER 0002000 /* offline */ +#define STA_SEKER 0001000 /* seek error */ +#define STA_MBZ 0000700 +#define STA_WPRER 0000040 /* write prot error */ +#define STA_UNSER 0000020 /* unsafe */ +#define STA_CSMER 0000010 /* checksum error */ +#define STA_DTRER 0000004 /* transfer rate error */ +#define STA_ANYER 0000002 /* any error^ */ +#define STA_EOR 0000001 /* end of record */ +#define STA_ALLERR (STA_ADRER|STA_FMTER|STA_HNLER|STA_OFLER|STA_SEKER|\ + STA_WPRER|STA_UNSER|STA_DTRER) + +/* Functions */ + +#define FNC_SK0 0000 /* recalibrate */ +#define FNC_SEEK 0001 /* seek */ +#define FNC_RCA 0002 /* read current */ +#define FNC_UNL 0004 /* unload */ +#define FNC_FMT 0005 /* format */ +#define FNC_RW 0006 /* read/write */ +#define FNC_STOP 0010 /* stop format */ +#define FNC_RDS 0011 /* read status */ +#define FNC_DMA 0013 /* DMA/DMC */ +#define FNC_AKI 0014 /* acknowledge intr */ +#define FNC_IOBUS 0017 /* IO bus */ +#define FNC_2ND 0020 /* second state */ +#define FNC_3RD 0040 /* third state */ +#define FNC_4TH 0060 /* fourth state */ +#define FNC_5TH 0100 /* fifth state */ + +/* Command word 1 */ + +#define CW1_RW 0100000 /* read/write */ +#define CW1_DIR 0000400 /* seek direction */ +#define CW1_V_UNIT 11 /* unit select */ +#define CW1_V_HEAD 6 /* head select */ +#define CW1_V_OFFS 0 /* seek offset */ +#define CW1_GETUNIT(x) (((x) >> CW1_V_UNIT) & dp_tab[dp_ctype].umsk) +#define CW1_GETHEAD(x) (((x) >> CW1_V_HEAD) & dp_tab[dp_ctype].hmsk) +#define CW1_GETOFFS(x) (((x) >> CW1_V_OFFS) & dp_tab[dp_ctype].cmsk) + +/* OTA states */ + +#define OTA_NOP 0 /* normal */ +#define OTA_CW1 1 /* expecting CW1 */ +#define OTA_CW2 2 /* expecting CW2 */ + +/* Transfer state */ + +#define XIP_UMSK 007 /* unit mask */ +#define XIP_SCHED 010 /* scheduled */ +#define XIP_WRT 020 /* write */ +#define XIP_FMT 040 /* format */ + +/* The H316/516 disk emulator supports three disk controllers: + + controller units cylinders surfaces data words per track + + 4651 4 203 2 1908.25 + 4623 8 203 10 1816.5 + 4720 8 203 20 1908.25 + + Disk types may not be intermixed on the same controller. +*/ + +#define TYPE_4651 0 +#define UNIT_4651 4 +#define CYL_4651 203 +#define SURF_4651 2 +#define WRDS_4651 1908.25 +#define UMSK_4651 0003 +#define HMSK_4651 0001 +#define CMSK_4651 0377 +#define CAP_4651 (CYL_4651*SURF_4651*DP_TRKLEN) + +#define TYPE_4623 1 +#define UNIT_4623 8 +#define CYL_4623 203 +#define SURF_4623 10 +#define WRDS_4623 1816.5 +#define UMSK_4623 0007 +#define HMSK_4623 0017 +#define CMSK_4623 0377 +#define CAP_4623 (CYL_4623*SURF_4623*DP_TRKLEN) + +#define TYPE_4720 2 +#define UNIT_4720 8 +#define CYL_4720 203 +#define SURF_4720 20 +#define WRDS_4720 1908.25 +#define UMSK_4720 0007 +#define HMSK_4720 0037 +#define CMSK_4720 0377 +#define CAP_4720 (CYL_4720*SURF_4720*DP_TRKLEN) + +struct drvtyp { + char *name; + uint32 numu; + uint32 cyl; + uint32 surf; + uint32 cap; + uint32 umsk; + uint32 hmsk; + uint32 cmsk; + float wrds; + }; + +#define DP_DRV(d) \ + #d, \ + UNIT_##d, CYL_##d, SURF_##d, CAP_##d, \ + UMSK_##d, HMSK_##d, CMSK_##d, WRDS_##d + +static struct drvtyp dp_tab[] = { + { DP_DRV (4651) }, + { DP_DRV (4623) }, + { DP_DRV (4720) } + }; + +extern int32 dev_int, dev_enb, chan_req; +extern int32 stop_inst; +extern uint32 dma_ad[DMA_MAX]; +extern int32 sim_switches; + +uint32 dp_cw1 = 0; /* cmd word 1 */ +uint32 dp_cw2 = 0; /* cmd word 2 */ +uint32 dp_fnc = 0; /* saved function */ +uint32 dp_buf = 0; /* buffer */ +uint32 dp_otas = 0; /* state */ +uint32 dp_sta = 0; /* status */ +uint32 dp_defint = 0; /* deferred seek int */ +uint32 dp_ctype = TYPE_4651; /* controller type */ +uint32 dp_dma = 0; /* DMA/DMC */ +uint32 dp_eor = 0; /* end of range */ +uint32 dp_xip = 0; /* transfer in prog */ +uint32 dp_csum = 0; /* parity checksum */ +uint32 dp_rptr = 0; /* start of record */ +uint32 dp_wptr = 0; /* word ptr in record */ +uint32 dp_bctr = 0; /* format bit cntr */ +uint32 dp_gap = 0; /* format gap size */ +uint32 dp_stopioe = 1; /* stop on error */ +int32 dp_stime = 1000; /* seek per cylinder */ +int32 dp_xtime = 10; /* xfer per word */ +int32 dp_btime = 30; /* busy time */ +uint16 dpxb[DP_TRKLEN]; /* track buffer */ + +int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat dp_svc (UNIT *uptr); +t_stat dp_reset (DEVICE *dptr); +t_stat dp_attach (UNIT *uptr, char *cptr); +t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dp_go (uint32 dma); +t_stat dp_go1 (uint32 dat); +t_stat dp_go2 (uint32 dat); +t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd); +t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 cyl, uint32 hd); +t_bool dp_findrec (uint32 addr); +t_stat dp_wrwd (UNIT *uptr, uint32 dat); +t_stat dp_wrdone (UNIT *uptr, uint32 flg); +t_stat dp_done (uint32 req, uint32 f); +t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* DP data structures + + dp_dev DP device descriptor + dp_unit DP unit list + dp_reg DP register list + dp_mod DP modifier list +*/ + +DIB dp_dib = { DP, DMC1, 1, &dpio }; + +UNIT dp_unit[] = { + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, CAP_4651) } + }; + +REG dp_reg[] = { + { ORDATA (STA, dp_sta, 16) }, + { ORDATA (BUF, dp_buf, 16) }, + { ORDATA (FNC, dp_fnc, 4) }, + { ORDATA (CW1, dp_cw1, 16) }, + { ORDATA (CW2, dp_cw2, 16) }, + { ORDATA (CSUM, dp_csum, 16) }, + { FLDATA (BUSY, dp_sta, 15) }, + { FLDATA (RDY, dp_sta, 14) }, + { FLDATA (EOR, dp_eor, 0) }, + { FLDATA (DEFINT, dp_defint, 0) }, + { FLDATA (INTREQ, dev_int, INT_V_DP) }, + { FLDATA (ENABLE, dev_enb, INT_V_DP) }, + { BRDATA (TBUF, dpxb, 8, 16, DP_TRKLEN) }, + { ORDATA (RPTR, dp_rptr, 11), REG_RO }, + { ORDATA (WPTR, dp_wptr, 11), REG_RO }, + { ORDATA (BCTR, dp_bctr, 15), REG_RO }, + { ORDATA (GAP, dp_gap, 16), REG_RO }, + { DRDATA (STIME, dp_stime, 24), REG_NZ + PV_LEFT }, + { DRDATA (XTIME, dp_xtime, 24), REG_NZ + PV_LEFT }, + { DRDATA (BTIME, dp_btime, 24), REG_NZ + PV_LEFT }, + { FLDATA (CTYPE, dp_ctype, 0), REG_HRO }, + { URDATA (UCYL, dp_unit[0].CYL, 10, 8, 0, + DP_NUMDRV, PV_LEFT | REG_HRO) }, + { URDATA (UFNC, dp_unit[0].FNC, 8, 7, 0, + DP_NUMDRV, REG_HRO) }, + { URDATA (CAPAC, dp_unit[0].capac, 10, T_ADDR_W, 0, + DP_NUMDRV, PV_LEFT | REG_HRO) }, + { ORDATA (OTAS, dp_otas, 2), REG_HRO }, + { ORDATA (XIP, dp_xip, 6), REG_HRO }, + { ORDATA (CHAN, dp_dib.chan, 5), REG_HRO }, + { FLDATA (STOP_IOE, dp_stopioe, 0) }, + { NULL } + }; + +MTAB dp_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VDV, TYPE_4623, NULL, "4623", + &dp_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TYPE_4651, NULL, "4651", + &dp_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TYPE_4720, NULL, "4720", + &dp_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, + NULL, &dp_showtype, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC", + &io_set_dmc, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA", + &io_set_dma, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, + NULL, &io_show_chan, NULL }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "FORMAT", "FORMAT", + &dp_setformat, &dp_showformat, NULL }, + { 0 } + }; + +DEVICE dp_dev = { + "DP", dp_unit, dp_reg, dp_mod, + DP_NUMDRV, 8, 24, 1, 8, 16, + NULL, NULL, &dp_reset, + NULL, &dp_attach, NULL, + &dp_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 dpio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ +int32 u; +UNIT *uptr; + +switch (inst) { /* case on opcode */ + + case ioOCP: /* OCP */ + switch (fnc) { /* case on function */ + + case FNC_SK0: case FNC_SEEK: case FNC_RCA: /* data transfer */ + case FNC_UNL: case FNC_FMT: case FNC_RW: + dp_go (fnc); /* if !busy, start */ + break; + + case FNC_STOP: /* stop transfer */ + if (dp_xip) { /* transfer in prog? */ + uptr = dp_dev.units + (dp_xip & XIP_UMSK); /* get unit */ + sim_cancel (uptr); /* stop operation */ + if (dp_xip & (XIP_WRT|XIP_FMT)) /* write or format? */ + dp_wrdone (uptr, /* write track */ + ((dp_xip & XIP_FMT) && /* check fmt state */ + (uptr->FNC != (FNC_FMT|FNC_2ND)))? + STA_DTRER: 0); + else dp_done (1, dp_csum? STA_CSMER: 0);/* no, just clr busy */ + dp_xip = 0; /* clear flag */ + } + dp_otas = OTA_NOP; /* clear state */ + dp_sta = dp_sta & ~STA_BUSY; /* clear busy */ + break; + + case FNC_RDS: /* read status */ + if (dp_sta & STA_BUSY) return dat; /* ignore if busy */ + dp_sta = (dp_sta | STA_RDY) & ~(STA_MBZ | STA_ANYER); + if (dp_sta & STA_ALLERR) dp_sta = dp_sta | STA_ANYER; + dp_buf = dp_sta; + if (dp_dma && Q_DMA (ch)) SET_CH_REQ (ch); /* DMA? set chan req */ + break; + + case FNC_DMA: /* set DMA/DMC */ + dp_dma = 1; + break; + + case FNC_IOBUS: /* set IO bus */ + dp_dma = 0; + break; + + case FNC_AKI: /* ack intr */ + CLR_INT (INT_DP); + break; + + default: /* undefined */ + return IOBADFNC (dat); + } + break; + + case ioINA: /* INA */ + if (fnc) return IOBADFNC (dat); /* fnc 0 only */ + if (dp_sta & STA_RDY) { /* ready? */ + dp_sta = dp_sta & ~STA_RDY; /* clear ready */ + return IOSKIP (dat | dp_buf); /* ret buf, skip */ + } + break; + + case ioOTA: /* OTA */ + if (fnc) return IOBADFNC (dat); /* fnc 0 only */ + if (dp_sta & STA_RDY) { /* ready? */ + dp_sta = dp_sta & ~STA_RDY; /* clear ready */ + dp_buf = dat; /* store buf */ + if (dp_otas == OTA_CW1) dp_go1 (dat); /* expecting CW1? */ + else if (dp_otas == OTA_CW2) dp_go2 (dat); /* expecting CW2? */ + return IOSKIP (dat); + } + break; + + case ioSKS: /* SKS */ + u = 7; /* assume unit 7 */ + switch (fnc) { + + case 000: /* ready */ + if (dp_sta & STA_RDY) return IOSKIP (dat); + break; + + case 001: /* !interrupting */ + if (!TST_INTREQ (INT_DP)) return IOSKIP (dat); + break; + + case 002: /* operational */ + if (!(dp_sta & (STA_BUSY | STA_ALLERR))) return IOSKIP (dat); + break; + + case 003: /* !error */ + if (!(dp_sta & STA_ALLERR)) return IOSKIP (dat); + break; + + case 004: /* !busy */ + if (!(dp_sta & STA_BUSY)) return IOSKIP (dat); + break; + + case 011: case 012: case 013: /* !not seeking 0-6 */ + case 014: case 015: case 016: case 017: + u = fnc - 011; + case 007: /* !not seeking 7 */ + if (!sim_is_active (&dp_unit[u]) || /* quiescent? */ + (dp_unit[u].FNC != (FNC_SEEK | FNC_2ND))) + return IOSKIP (dat); /* seeking sets late */ + break; + } + break; + + case ioEND: /* end of range */ + dp_eor = 1; /* transfer done */ + break; + } + +return dat; +} + +/* Start new operation - recal, seek, read address, format, read/write */ + +t_stat dp_go (uint32 fnc) +{ +int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ + +if (dp_sta & STA_BUSY) return SCPE_OK; /* ignore if busy */ +dp_fnc = fnc; /* save function */ +dp_xip = 0; /* transfer not started */ +dp_eor = 0; /* not end of range */ +dp_csum = 0; /* init checksum */ +dp_otas = OTA_CW1; /* expect CW1 */ +dp_sta = (dp_sta | STA_BUSY | STA_RDY) & ~(STA_ALLERR | STA_EOR); +if (dp_dma && Q_DMA (ch)) { /* DMA and DMA channel? */ + SET_CH_REQ (ch); /* set channel request */ + dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */ + } +return SCPE_OK; +} + +/* Process command word 1 - recal, seek, read address, format, read/write */ + +t_stat dp_go1 (uint32 dat) +{ +int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ +uint32 u = CW1_GETUNIT (dat); +UNIT *uptr = dp_dev.units + u; + +dp_cw1 = dat; /* store CW1 */ +dp_otas = OTA_NOP; /* assume no CW2 */ +uptr->FNC = dp_fnc; +if (sim_is_active (uptr)) /* still seeking? */ + return dp_done (1, STA_UNSER); /* unsafe */ +if (!(uptr->flags & UNIT_ATT)) /* not attached? */ + return dp_done (1, STA_OFLER); /* offline */ + +switch (dp_fnc) { /* case on function */ + + case FNC_SEEK: /* seek */ + case FNC_SK0: /* recalibrate */ + case FNC_UNL: /* unload */ + sim_activate (uptr, dp_btime); /* quick timeout */ + break; + + case FNC_FMT: /* format */ + if (uptr->flags & UNIT_WPRT) /* write protect? */ + return dp_done (1, STA_WPRER); /* stop now */ + case FNC_RCA: /* read current addr */ + dp_xip = u | XIP_SCHED; /* operation started */ + sim_activate (uptr, dp_xtime * 10); /* rotation timeout */ + break; + + case FNC_RW: /* read/write */ + dp_otas = OTA_CW2; /* expect CW2 */ + dp_sta = dp_sta | STA_RDY; /* set ready */ + if (dp_dma && Q_DMA (ch)) SET_CH_REQ (ch); /* DMA? set chan request */ + break; + } + +return SCPE_OK; +} + +/* Process command word 2 - read/write only */ + +t_stat dp_go2 (uint32 dat) +{ +uint32 u = CW1_GETUNIT (dp_cw1); +UNIT *uptr = dp_dev.units + u; + +dp_cw2 = dat; /* store CW2 */ +dp_otas = OTA_NOP; /* normal state */ +sim_activate (uptr, dp_xtime * 10); /* rotation timeout */ +dp_xip = u | XIP_SCHED; /* operation started */ +return SCPE_OK; +} + +/* Unit service */ + +t_stat dp_svc (UNIT *uptr) +{ +int32 dcyl = 0; /* assume recalibrate */ +int32 ch = dp_dib.chan - 1; /* DMA/DMC chan */ +uint32 h = CW1_GETHEAD (dp_cw1); /* head */ +int32 st; +uint32 i, offs, lnt, ming, tpos; +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) { /* not attached? */ + dp_done (1, STA_OFLER); /* offline */ + return IORETURN (dp_stopioe, SCPE_UNATT); + } + +switch (uptr->FNC) { /* case on function */ + + case FNC_SEEK: /* seek, need cyl */ + offs = CW1_GETOFFS (dp_cw1); /* get offset */ + if (dp_cw1 & CW1_DIR) dcyl = uptr->CYL - offs; /* get desired cyl */ + else dcyl = uptr->CYL + offs; + if ((offs == 0) || (dcyl < 0) || + (dcyl >= (int32) dp_tab[dp_ctype].cyl)) + return dp_done (1, STA_SEKER); /* bad seek? */ + + case FNC_SK0: /* recalibrate */ + dp_sta = dp_sta & ~STA_BUSY; /* clear busy */ + uptr->FNC = FNC_SEEK | FNC_2ND; /* next state */ + st = (abs (dcyl - uptr->CYL)) * dp_stime; /* schedule seek */ + if (st == 0) st = dp_stime; + uptr->CYL = dcyl; /* put on cylinder */ + sim_activate (uptr, st); + return SCPE_OK; + + case FNC_SEEK | FNC_2ND: /* seek, 2nd state */ + if (dp_sta & STA_BUSY) dp_defint = 1; /* busy? queue intr */ + else SET_INT (INT_DP); /* no, req intr */ + return SCPE_OK; + + case FNC_UNL: /* unload */ + detach_unit (uptr); /* detach unit */ + return dp_done (0, 0); /* clear busy, no intr */ + + case FNC_RCA: /* read current addr */ + if (h >= dp_tab[dp_ctype].surf) /* invalid head? */ + return dp_done (1, STA_ADRER); /* error */ + if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */ + return r; + dp_rptr = 0; /* init rec ptr */ + if (dpxb[dp_rptr + REC_LNT] == 0) /* unformated? */ + return dp_done (1, STA_ADRER); /* error */ + tpos = (uint32) (fmod (sim_gtime () / (double) dp_xtime, DP_TRKLEN)); + do { /* scan down track */ + dp_buf = dpxb[dp_rptr + REC_ADDR]; /* get rec addr */ + dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD; + } while ((dp_rptr < tpos) && (dpxb[dp_rptr + REC_LNT] != 0)); + if (dp_dma) { /* DMA/DMC? */ + if (Q_DMA (ch)) /* DMA? */ + dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ + SET_CH_REQ (ch); /* request chan */ + } + return dp_done (1, STA_RDY); /* clr busy, set rdy */ + +/* Formating takes place in five states: + + init - clear track buffer, start at first record + address - store address word + data - store data word(s) until end of range + pause - wait for gap word or stop command + gap - validate gap word, advance to next record + + Note that formating is stopped externally by an OCP command; the + track buffer is flushed at that point. If the stop does not occur + in the proper state (gap word received), a format error occurs. +*/ + + case FNC_FMT: /* format */ + for (i = 0; i < DP_TRKLEN; i++) dpxb[i] = 0; /* clear track */ + dp_xip = dp_xip | XIP_FMT; /* format in progress */ + dp_rptr = 0; /* init record ptr */ + dp_gap = 0; /* no gap before first */ + dp_bctr = (uint32) (16.0 * dp_tab[dp_ctype].wrds); /* init bit cntr */ + uptr->FNC = uptr->FNC | FNC_2ND; /* address state */ + break; /* set up next word */ + + case FNC_FMT | FNC_2ND: /* format, address word */ + dp_wptr = 0; /* clear word ptr */ + if (dp_bctr < (dp_gap + REC_OVHD_BITS + 16)) /* room for gap, record? */ + return dp_wrdone (uptr, STA_FMTER); /* no, format error */ + dp_bctr = dp_bctr - dp_gap - REC_OVHD_BITS; /* charge for gap, ovhd */ + dpxb[dp_rptr + REC_ADDR] = dp_buf; /* store address */ + uptr->FNC = FNC_FMT | FNC_3RD; /* data state */ + if (dp_eor) { /* record done? */ + dp_eor = 0; /* clear for restart */ + if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */ + } + break; /* set up next word */ + + case FNC_FMT | FNC_3RD: /* format, data word */ + if (dp_sta & STA_RDY) /* timing failure? */ + return dp_wrdone (uptr, STA_DTRER); /* write trk, err */ + else { /* no, have word */ + if (dp_bctr < 16) /* room for it? */ + return dp_wrdone (uptr, STA_FMTER); /* no, error */ + dp_bctr = dp_bctr - 16; /* charge for word */ + dp_csum = dp_csum ^ dp_buf; /* update checksum */ + dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_buf;/* store word */ + dpxb[dp_rptr + REC_LNT]++; /* incr rec lnt */ + dp_wptr++; /* incr word ptr */ + } + if (dp_eor) { /* record done? */ + dp_eor = 0; /* clear for restart */ + if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */ + dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* store checksum */ + uptr->FNC = uptr->FNC | FNC_4TH; /* pause state */ + sim_activate (uptr, 5 * dp_xtime); /* schedule pause */ + return SCPE_OK; /* don't request word */ + } + break; /* set up next word */ + + case FNC_FMT | FNC_4TH: /* format, pause */ + uptr->FNC = FNC_FMT | FNC_5TH; /* gap state */ + break; /* request word */ + + case FNC_FMT | FNC_5TH: /* format, gap word */ + ming = ((16 * dp_wptr) + REC_OVHD_BITS) / 20; /* min 5% gap */ + if (dp_buf < ming) /* too small? */ + return dp_wrdone (uptr, STA_FMTER); /* yes, format error */ + dp_rptr = dp_rptr + dp_wptr + REC_OVHD; /* next record */ + uptr->FNC = FNC_FMT | FNC_2ND; /* address state */ + if (dp_eor) { /* record done? */ + dp_eor = 0; /* clear for restart */ + if (dp_dma) SET_INT (INT_DP); /* DMA/DMC? intr */ + } + dp_gap = dp_buf; /* save gap */ + dp_csum = 0; /* clear checksum */ + break; /* set up next word */ + +/* Read and write take place in two states: + + init - read track into buffer, find record, validate parameters + data - (read) fetch data from buffer, stop on end of range + - (write) write data into buffer, flush on end of range +*/ + + case FNC_RW: /* read/write */ + if (h >= dp_tab[dp_ctype].surf) /* invalid head? */ + return dp_done (1, STA_ADRER); /* error */ + if (r = dp_rdtrk (uptr, dpxb, uptr->CYL, h)) /* get track; error? */ + return r; + if (!dp_findrec (dp_cw2)) /* find rec; error? */ + return dp_done (1, STA_ADRER); /* address error */ + if ((dpxb[dp_rptr + REC_LNT] >= (DP_TRKLEN - dp_rptr - REC_OVHD)) || + (dpxb[dp_rptr + REC_EXT] >= REC_MAXEXT)) { /* bad lnt or ext? */ + dp_done (1, STA_UNSER); /* stop simulation */ + return STOP_DPFMT; /* bad format */ + } + uptr->FNC = uptr->FNC | FNC_2ND; /* next state */ + if (dp_cw1 & CW1_RW) { /* write? */ + if (uptr->flags & UNIT_WPRT) /* write protect? */ + return dp_done (1, STA_WPRER); /* error */ + dp_xip = dp_xip | XIP_WRT; /* write in progress */ + dp_sta = dp_sta | STA_RDY; /* set ready */ + if (dp_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */ + } + else if (Q_DMA (ch)) /* read; DMA? */ + dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ + sim_activate (uptr, dp_xtime); /* schedule word */ + dp_wptr = 0; /* init word pointer */ + return SCPE_OK; + + case FNC_RW | FNC_2ND: /* read/write, word */ + if (dp_cw1 & CW1_RW) { /* write? */ + if (dp_sta & STA_RDY) /* timing failure? */ + return dp_wrdone (uptr, STA_DTRER); /* yes, error */ + if (r = dp_wrwd (uptr, dp_buf)) return r; /* wr word, error? */ + if (dp_eor) { /* transfer done? */ + dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; + return dp_wrdone (uptr, 0); /* clear busy, intr req */ + } + } + else { /* read? */ + lnt = dpxb[dp_rptr + REC_LNT] + dpxb[dp_rptr + REC_EXT]; + dp_buf = dpxb[dp_rptr + REC_DATA + dp_wptr];/* current word */ + dp_csum = dp_csum ^ dp_buf; /* xor to csum */ + if ((dp_wptr > lnt) || dp_eor) /* transfer done? */ + return dp_done (1, + (dp_csum? STA_CSMER: 0) | + ((dp_wptr >= lnt)? STA_EOR: 0)); + if (dp_sta & STA_RDY) /* data buf full? */ + return dp_done (1, STA_DTRER); /* no, underrun */ + dp_wptr++; /* next word */ + } + break; + + default: + return SCPE_IERR; + } /* end case */ + +dp_sta = dp_sta | STA_RDY; /* set ready */ +if (dp_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */ +sim_activate (uptr, dp_xtime); /* schedule word */ +return SCPE_OK; +} + +/* Read track */ + +t_stat dp_rdtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h) +{ +uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN; +int32 l; + +fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); +l = fxread (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref); +for ( ; l < DP_TRKLEN; l++) buf[l] = 0; +if (ferror (uptr->fileref)) { + perror ("DP I/O error"); + clearerr (uptr->fileref); + dp_done (1, STA_UNSER); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Write track */ + +t_stat dp_wrtrk (UNIT *uptr, uint16 *buf, uint32 c, uint32 h) +{ +uint32 da = ((c * dp_tab[dp_ctype].surf) + h) * DP_TRKLEN; + +fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); +fxwrite (buf, sizeof (uint16), DP_TRKLEN, uptr->fileref); +if (ferror (uptr->fileref)) { + perror ("DP I/O error"); + clearerr (uptr->fileref); + dp_done (1, STA_UNSER); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Find record; true if found, false if not found */ + +t_bool dp_findrec (uint32 addr) +{ +dp_rptr = 0; + +do { + if (dpxb[dp_rptr + REC_LNT] == 0) return FALSE; + if (dpxb[dp_rptr + REC_LNT] >= DP_TRKLEN) return TRUE; + if (dpxb[dp_rptr + REC_ADDR] == addr) return TRUE; + dp_rptr = dp_rptr + dpxb[dp_rptr + REC_LNT] + REC_OVHD; + } while (dp_rptr < DP_TRKLEN); +return FALSE; +} + +/* Write next word to track buffer; return TRUE if ok, FALSE if next record trashed */ + +t_stat dp_wrwd (UNIT *uptr, uint32 dat) +{ +uint32 lnt = dpxb[dp_rptr + REC_LNT]; +t_stat r; + +dp_csum = dp_csum ^ dat; +if (dp_wptr < lnt) { + dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat; + return SCPE_OK; + } +if (dp_wptr < (lnt + REC_MAXEXT)) { + dpxb[dp_rptr + REC_EXT]++; + dpxb[dp_rptr + REC_DATA + dp_wptr++] = dat; + return SCPE_OK; + } +dpxb[dp_rptr + REC_DATA + dp_wptr] = dp_csum; /* write csum */ +dpxb[dp_rptr + lnt + REC_OVHD] = 0; /* zap rest of track */ +if (r = dp_wrdone (uptr, STA_UNSER)) return r; /* dump track */ +return STOP_DPOVR; +} + +/* Write done, dump track, clear busy */ + +t_stat dp_wrdone (UNIT *uptr, uint32 flg) +{ +dp_done (1, flg); +return dp_wrtrk (uptr, dpxb, uptr->CYL, CW1_GETHEAD (dp_cw1)); +} + +/* Clear busy, set errors, request interrupt if required */ + +t_stat dp_done (uint32 req, uint32 flg) +{ +dp_xip = 0; /* clear xfr in prog */ +dp_sta = (dp_sta | flg) & ~(STA_BUSY | STA_MBZ); /* clear busy */ +if (req || dp_defint) SET_INT (INT_DP); /* if req, set intr */ +dp_defint = 0; /* clr def intr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat dp_reset (DEVICE *dptr) +{ +int32 i; + +dp_fnc = 0; +dp_cw1 = 0; +dp_cw2 = 0; +dp_sta = 0; +dp_buf = 0; +dp_xip = 0; +dp_eor = 0; +dp_dma = 0; +dp_csum = 0; +dp_rptr = 0; +dp_wptr = 0; +dp_bctr = 0; +dp_gap = 0; +dp_defint = 0; +for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */ + sim_cancel (&dp_unit[i]); /* cancel activity */ + dp_unit[i].FNC = 0; /* clear function */ + dp_unit[i].CYL = 0; + } +CLR_INT (INT_DP); /* clear int, enb */ +CLR_ENB (INT_DP); +return SCPE_OK; +} + +/* Attach routine, test formating */ + +t_stat dp_attach (UNIT *uptr, char *cptr) +{ +t_stat r; +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +return dp_showformat (stdout, uptr, 0, NULL); +} + +/* Set controller type */ + +t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i; + +if ((val < 0) || (val >= DP_NUMTYP) || (cptr != NULL)) return SCPE_ARG; +for (i = 0; i < DP_NUMDRV; i++) { + if (dp_unit[i].flags & UNIT_ATT) return SCPE_ALATT; + } +for (i = 0; i < DP_NUMDRV; i++) + dp_unit[i].capac = dp_tab[val].cap; +dp_ctype = val; +return SCPE_OK; +} + +/* Show controller type */ + +t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (dp_ctype >= DP_NUMTYP) return SCPE_IERR; +fprintf (st, "%s", dp_tab[dp_ctype].name); +return SCPE_OK; +} + +/* Set drive format + + There is no standard format for record addresses. This routine + provides two schemes: + + -S sequential addressing (starting from 0) + default geometric addressing (8b: cylinder, 5b: head, 3b: sector) + + This routine also supports formatting by record count or word count: + + -R argument is records per track + default argument is words per record + + The relationship between words per record (W), bits per track (B), + and records per track (R), is as follows: + + W = (B / (R + ((R - 1) / 20))) - 16.5 + + where (R - 1) / 20 is the "5% gap" and 16.5 is the overhead, in words, + per record. +*/ + +t_stat dp_setformat (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 h, c, cntr, rptr; +int32 i, nr, nw, inp; +uint16 tbuf[DP_TRKLEN]; +float finp; +t_stat r; + +if (uptr == NULL) return SCPE_IERR; +if (cptr == NULL) return SCPE_ARG; +if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT; +inp = (int32) get_uint (cptr, 10, 2048, &r); +if (r != SCPE_OK) return r; +if (inp == 0) return SCPE_ARG; +finp = (float) inp; +if (sim_switches & SWMASK ('R')) { /* format records? */ + nr = inp; + nw = (int32) ((dp_tab[dp_ctype].wrds / (finp + ((finp - 1.0) / 20.0))) - REC_OVHD_WRDS); + if (nw <= 0) return SCPE_ARG; + } +else { + nw = inp; /* format words */ + nr = (int32) ((((20.0 * dp_tab[dp_ctype].wrds) / (finp + REC_OVHD_WRDS)) + 1.0) / 21.0); + if (nr <= 0) return SCPE_ARG; + } +printf ("Proposed format: records/track = %d, record size = %d\n", nr, nw); +if (!get_yn ("Formatting will destroy all data on this disk; proceed? [N]", FALSE)) + return SCPE_OK; +for (c = cntr = 0; c < dp_tab[dp_ctype].cyl; c++) { + for (h = 0; h < dp_tab[dp_ctype].surf; h++) { + for (i = 0; i < DP_TRKLEN; i++) tbuf[i] = 0; + rptr = 0; + for (i = 0; i < nr; i++) { + tbuf[rptr + REC_LNT] = nw & DMASK; + if (sim_switches & SWMASK ('S')) + tbuf[rptr + REC_ADDR] = cntr++; + else tbuf[rptr + REC_ADDR] = (c << 8) + (h << 3) + i; + rptr = rptr + nw + REC_OVHD; + } + if (r = dp_wrtrk (uptr, tbuf, c, h)) return r; + } + } +printf ("Formatting complete\n"); +return SCPE_OK; +} + +/* Show format */ + +t_stat dp_showformat (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 c, h, rptr, rlnt, sec; +uint32 minrec = DP_TRKLEN; +uint32 maxrec = 0; +uint32 minsec = DP_TRKLEN; +uint32 maxsec = 0; +uint16 tbuf[DP_TRKLEN]; +t_stat r; + +if (uptr == NULL) return SCPE_IERR; +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; +for (c = 0; c < dp_tab[dp_ctype].cyl; c++) { + for (h = 0; h < dp_tab[dp_ctype].surf; h++) { + if (r = dp_rdtrk (uptr, tbuf, c, h)) return r; + rptr = 0; + rlnt = tbuf[rptr + REC_LNT]; + if (rlnt == 0) { + if (c || h) fprintf (st, + "Unformatted track, cyl = %d, head = %d\n", c, h); + else fprintf (st, "Disk is unformatted\n"); + return SCPE_OK; + } + for (sec = 0; rlnt != 0; sec++) { + if ((rptr + rlnt + REC_OVHD) >= DP_TRKLEN) { + fprintf (st, "Invalid record length %d, cyl = %d, head = %d, sect = %d\n", + rlnt, c, h, sec); + return SCPE_OK; + } + if (tbuf[rptr + REC_EXT] >= REC_MAXEXT) { + fprintf (st, "Invalid record extension %d, cyl = %d, head = %d, sect = %d\n", + tbuf[rptr + REC_EXT], c, h, sec); + return SCPE_OK; + } + if (rlnt > maxrec) maxrec = rlnt; + if (rlnt < minrec) minrec = rlnt; + rptr = rptr + rlnt + REC_OVHD; + rlnt = tbuf[rptr + REC_LNT]; + } + if (sec > maxsec) maxsec = sec; + if (sec < minsec) minsec = sec; + } + } +if ((minrec == maxrec) && (minsec == maxsec)) fprintf (st, + "Valid fixed format, records/track = %d, record size = %d\n", + minsec, minrec); +else if (minrec == maxrec) fprintf (st, + "Valid variable format, records/track = %d-%d, record size = %d\n", + minsec, maxsec, minrec); +else if (minsec == maxsec) fprintf (st, + "Valid variable format, records/track = %d, record sizes = %d-%d\n", + minsec, minrec, maxrec); +else fprintf (st, + "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n", + minsec, maxsec, minrec, maxrec); +return SCPE_OK; +} diff --git a/H316/h316_fhd.c b/H316/h316_fhd.c new file mode 100644 index 0000000..f27061a --- /dev/null +++ b/H316/h316_fhd.c @@ -0,0 +1,455 @@ +/* h316_fhd.c: H316/516 fixed head simulator + + Copyright (c) 2003-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + fhd 516-4400 fixed head disk + + 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) + 04-Jan-04 RMS Changed sim_fsize calling sequence + + These head-per-track devices are buffered in memory, to minimize overhead. +*/ + +#include "h316_defs.h" +#include + +/* Constants */ + +#define FH_NUMWD 1536 /* words/track */ +#define FH_NUMTK 64 /* tracks/surface */ +#define FH_WDPSF (FH_NUMWD * FH_NUMTK) /* words/surface */ +#define FH_NUMSF 16 /* surfaces/ctlr */ +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_SF (UNIT_V_UF + 1) /* #surfaces - 1 */ +#define UNIT_M_SF 017 +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_SF (UNIT_M_SF << UNIT_V_SF) +#define UNIT_GETSF(x) ((((x) >> UNIT_V_SF) & UNIT_M_SF) + 1) + +/* Command word 1 */ + +#define CW1_RW 0100000 /* read vs write */ +#define CW1_V_SF 10 /* surface */ +#define CW1_M_SF 017 +#define CW1_GETSF(x) (((x) >> CW1_V_SF) & CW1_M_SF) +#define CW1_V_TK 4 /* track */ +#define CW1_M_TK 077 +#define CW1_GETTK(x) (((x) >> CW1_V_TK) & CW1_M_TK) + +/* Command word 2 */ + +#define CW2_V_CA 0 /* character addr */ +#define CW2_M_CA 07777 +#define CW2_GETCA(x) (((x) >> CW2_V_CA) & CW2_M_CA) + +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) FH_NUMWD))) + +/* OTA states */ + +#define OTA_NOP 0 /* normal */ +#define OTA_CW1 1 /* expecting CW1 */ +#define OTA_CW2 2 /* expecting CW2 */ + +extern int32 dev_int, dev_enb, chan_req; +extern int32 stop_inst; +extern uint32 dma_ad[DMA_MAX]; + +uint32 fhd_cw1 = 0; /* cmd word 1 */ +uint32 fhd_cw2 = 0; /* cmd word 2 */ +uint32 fhd_buf = 0; /* buffer */ +uint32 fhd_otas = 0; /* state */ +uint32 fhd_busy = 0; /* busy */ +uint32 fhd_rdy = 0; /* word ready */ +uint32 fhd_dte = 0; /* data err */ +uint32 fhd_ace = 0; /* access error */ +uint32 fhd_dma = 0; /* DMA/DMC */ +uint32 fhd_eor = 0; /* end of range */ +uint32 fhd_csum = 0; /* parity checksum */ +uint32 fhd_stopioe = 1; /* stop on error */ +int32 fhd_time = 10; /* time per word */ + +int32 fhdio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat fhd_svc (UNIT *uptr); +t_stat fhd_reset (DEVICE *dptr); +t_stat fhd_attach (UNIT *uptr, char *cptr); +t_stat fhd_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +void fhd_go (uint32 dma); +void fhd_go1 (uint32 dat); +void fhd_go2 (uint32 dat); +t_bool fhd_getc (UNIT *uptr, uint32 *ch); +t_bool fhd_putc (UNIT *uptr, uint32 ch); +t_bool fhd_bad_wa (uint32 wa); +uint32 fhd_csword (uint32 cs, uint32 ch); + +/* FHD data structures + + fhd_dev device descriptor + fhd_unit unit descriptor + fhd_mod unit modifiers + fhd_reg register list +*/ + +DIB fhd_dib = { FHD, IOBUS, 1, &fhdio }; + +UNIT fhd_unit = { + UDATA (&fhd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + FH_WDPSF) + }; + +REG fhd_reg[] = { + { ORDATA (CW1, fhd_cw1, 16) }, + { ORDATA (CW2, fhd_cw2, 16) }, + { ORDATA (BUF, fhd_buf, 16) }, + { FLDATA (BUSY, fhd_busy, 0) }, + { FLDATA (RDY, fhd_rdy, 0) }, + { FLDATA (DTE, fhd_dte, 0) }, + { FLDATA (ACE, fhd_ace, 0) }, + { FLDATA (EOR, fhd_eor, 0) }, + { FLDATA (DMA, fhd_dma, 0) }, + { FLDATA (CSUM, fhd_csum, 7) }, + { FLDATA (INTREQ, dev_int, INT_V_MT) }, + { FLDATA (ENABLE, dev_enb, INT_V_MT) }, + { DRDATA (TIME, fhd_time, 31), REG_NZ + PV_LEFT }, + { ORDATA (OTAS, fhd_otas, 2), REG_HRO }, + { ORDATA (CHAN, fhd_dib.chan, 5), REG_HRO }, + { FLDATA (STOP_IOE, fhd_stopioe, 0) }, + { NULL } + }; + +MTAB fhd_mod[] = { + { UNIT_SF, (0 << UNIT_V_SF), NULL, "1S", &fhd_set_size }, + { UNIT_SF, (1 << UNIT_V_SF), NULL, "2S", &fhd_set_size }, + { UNIT_SF, (2 << UNIT_V_SF), NULL, "3S", &fhd_set_size }, + { UNIT_SF, (3 << UNIT_V_SF), NULL, "4S", &fhd_set_size }, + { UNIT_SF, (4 << UNIT_V_SF), NULL, "5S", &fhd_set_size }, + { UNIT_SF, (5 << UNIT_V_SF), NULL, "6S", &fhd_set_size }, + { UNIT_SF, (6 << UNIT_V_SF), NULL, "7S", &fhd_set_size }, + { UNIT_SF, (7 << UNIT_V_SF), NULL, "8S", &fhd_set_size }, + { UNIT_SF, (8 << UNIT_V_SF), NULL, "9S", &fhd_set_size }, + { UNIT_SF, (9 << UNIT_V_SF), NULL, "10S", &fhd_set_size }, + { UNIT_SF, (10 << UNIT_V_SF), NULL, "11S", &fhd_set_size }, + { UNIT_SF, (11 << UNIT_V_SF), NULL, "12S", &fhd_set_size }, + { UNIT_SF, (12 << UNIT_V_SF), NULL, "13S", &fhd_set_size }, + { UNIT_SF, (13 << UNIT_V_SF), NULL, "14S", &fhd_set_size }, + { UNIT_SF, (14 << UNIT_V_SF), NULL, "15S", &fhd_set_size }, + { UNIT_SF, (15 << UNIT_V_SF), NULL, "16S", &fhd_set_size }, + { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "IOBUS", + &io_set_iobus, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC", + &io_set_dmc, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA", + &io_set_dma, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, + NULL, &io_show_chan, NULL }, + { 0 } + }; + +DEVICE fhd_dev = { + "FHD", &fhd_unit, fhd_reg, fhd_mod, + 1, 8, 22, 1, 8, 16, + NULL, NULL, &fhd_reset, + NULL, &fhd_attach, NULL, + &fhd_dib, DEV_DISABLE + }; + +/* IO routines */ + +int32 fhdio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +switch (inst) { /* case on opcode */ + + case ioOCP: /* control */ + if (fnc == 04) { /* terminate output? */ + fhd_eor = 1; /* stop */ + CLR_INT (INT_FHD); /* clear int req */ + } + else if (fnc == 003) fhd_go (1); /* start, DMA */ + else if (fnc == 007) fhd_go (0); /* start, IO bus */ + else return IOBADFNC (dat); + break; + + case ioOTA: /* output */ + if (fnc) return IOBADFNC (dat); /* only fnc 0 */ + if (fhd_rdy) { /* ready? */ + fhd_buf = dat; /* store data */ + if (fhd_otas == OTA_CW1) fhd_go1 (dat); /* expecting CW1? */ + else if (fhd_otas == OTA_CW2) fhd_go2 (dat);/* expecting CW2? */ + else fhd_rdy = 0; /* normal, clr ready */ + return IOSKIP (dat); + } + break; + + case ioINA: /* input */ + if (fnc) return IOBADFNC (dat); /* only fnc 0 */ + if (fhd_rdy) { /* ready? */ + fhd_rdy = 0; /* clear ready */ + return IOSKIP (dat | fhd_buf); /* return data */ + } + break; + + case ioSKS: /* sense */ + if (((fnc == 000) && fhd_rdy) || /* 0 = skip if ready */ + ((fnc == 001) && !fhd_busy) || /* 1 = skip if !busy */ + ((fnc == 002) && !fhd_dte) || /* 2 = skip if !data err */ + ((fnc == 003) && !fhd_ace) || /* 3 = skip if !access err */ + ((fnc == 004) && !TST_INTREQ (INT_FHD))) /* 4 = skip if !interrupt */ + return IOSKIP (dat); + break; + + case ioEND: + fhd_eor = 1; + break; + } + +return dat; +} + +/* Start new operation */ + +void fhd_go (uint32 dma) +{ +int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */ + +if (fhd_busy) return; /* ignore if busy */ +fhd_busy = 1; /* ctlr is busy */ +fhd_eor = 0; /* transfer not done */ +fhd_csum = 0; /* init checksum */ +fhd_dte = 0; /* clear errors */ +fhd_ace = 0; +if (ch >= 0) fhd_dma = dma; /* DMA allowed? */ +else fhd_dma = 0; /* no, force IO bus */ +fhd_otas = OTA_CW1; /* expect CW1 */ +fhd_rdy = 1; /* set ready */ +if (fhd_dma && Q_DMA (ch)) { /* DMA and DMA channel? */ + SET_CH_REQ (ch); /* set channel request */ + dma_ad[ch] = dma_ad[ch] & ~DMA_IN; /* force output */ + } +return; +} + +/* Process command word 1 */ + +void fhd_go1 (uint32 dat) +{ +int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */ + +fhd_cw1 = dat; /* store CW1 */ +fhd_otas = OTA_CW2; /* expect CW2 */ +fhd_rdy = 1; /* set ready */ +if (fhd_dma && Q_DMA (ch)) SET_CH_REQ (ch); /* DMA? set chan request */ +return; +} + +/* Process command word 2 - initiate seek */ + +void fhd_go2 (uint32 dat) +{ +int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan */ +uint32 sf = CW1_GETSF (fhd_cw1); /* surface */ +int32 t, wa; + +fhd_cw2 = dat; /* store CW2 */ +fhd_otas = OTA_NOP; /* next state */ +wa = CW2_GETCA (fhd_cw2) >> 1; /* word addr */ +if ((wa >= FH_NUMWD) || /* if bad char addr */ + ((fhd_unit.flags & UNIT_ATT) == 0) || /* or unattached */ + (sf >= UNIT_GETSF (fhd_unit.flags))) { /* or bad surface */ + fhd_ace = 1; /* access error */ + fhd_busy = 0; /* abort operation */ + SET_INT (INT_FHD); + return; + } +if (fhd_cw1 & CW1_RW) { /* write? */ + fhd_rdy = 1; /* set ready */ + if (fhd_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */ + } +else { + fhd_rdy = 0; /* read, clear ready */ + if (fhd_dma && (ch < DMC_V_DMC1)) /* read and DMA chan? */ + dma_ad[ch] = dma_ad[ch] | DMA_IN; /* force input */ + } +t = wa - GET_POS (fhd_time); /* delta to new loc */ +if (t < 0) t = t + FH_NUMWD; /* wrap around? */ +sim_activate (&fhd_unit, t * fhd_time); /* schedule op */ +return; +} + +/* Unit service */ + +t_stat fhd_svc (UNIT *uptr) +{ +int32 ch = fhd_dib.chan - 1; /* DMA/DMC chan (-1 if IO bus) */ +uint32 c1, c2; + +if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + fhd_ace = 1; /* access error */ + fhd_busy = 0; /* abort operation */ + SET_INT (INT_FHD); + return IORETURN (fhd_stopioe, SCPE_UNATT); + } + +if (fhd_eor || fhd_rdy) { /* done or ready set? */ + if (fhd_rdy) fhd_dte = 1; /* if ready set, data err */ + if (fhd_cw1 & CW1_RW) { /* write? */ + if (!fhd_rdy) { /* buffer full? */ + fhd_putc (uptr, fhd_buf >> 8); /* store last word */ + fhd_putc (uptr, fhd_buf); + } + fhd_putc (uptr, fhd_csum); /* store csum */ + } + else { /* read */ + fhd_getc (uptr, &c1); /* get csum */ + if (fhd_csum) fhd_dte = 1; /* if csum != 0, err */ + } + fhd_busy = 0; /* operation complete */ + SET_INT (INT_FHD); + return SCPE_OK; + } + +if (fhd_cw1 & CW1_RW) { /* write? */ + if (fhd_putc (uptr, fhd_buf >> 8)) return SCPE_OK; + if (fhd_putc (uptr, fhd_buf)) return SCPE_OK; + } +else { + if (fhd_getc (uptr, &c1)) return SCPE_OK; /* read */ + if (fhd_getc (uptr, &c2)) return SCPE_OK; + fhd_buf = (c1 << 8) | c2; + } +sim_activate (uptr, fhd_time); /* next word */ +fhd_rdy = 1; /* set ready */ +if (fhd_dma) SET_CH_REQ (ch); /* if DMA/DMC, req chan */ +return SCPE_OK; +} + +/* Read character from disk */ + +t_bool fhd_getc (UNIT *uptr, uint32 *ch) +{ +uint32 sf = CW1_GETSF (fhd_cw1); /* surface */ +uint32 tk = CW1_GETTK (fhd_cw1); /* track */ +uint32 ca = CW2_GETCA (fhd_cw2); /* char addr */ +uint32 wa = ca >> 1; /* word addr */ +uint32 ba = (((sf * FH_NUMTK) + tk) * FH_NUMWD) + wa; /* buffer offset */ +uint16 *fbuf = uptr->filebuf; /* buffer base */ +uint32 wd; + +if (fhd_bad_wa (wa)) return TRUE; /* addr bad? */ +fhd_cw2 = fhd_cw2 + 1; /* incr char addr */ +if (ca & 1) wd = fbuf[ba] & 0377; /* select char */ +else wd = (fbuf[ba] >> 8) & 0377; +fhd_csum = fhd_csword (fhd_csum, wd); /* put in csum */ +*ch = wd; /* return */ +return FALSE; +} + +/* Write character to disk */ + +t_bool fhd_putc (UNIT *uptr, uint32 ch) +{ +uint32 sf = CW1_GETSF (fhd_cw1); /* surface */ +uint32 tk = CW1_GETTK (fhd_cw1); /* track */ +uint32 ca = CW2_GETCA (fhd_cw2); /* char addr */ +uint32 wa = ca >> 1; /* word addr */ +uint32 ba = (((sf * FH_NUMTK) + tk) * FH_NUMWD) + wa; /* buffer offset */ +uint16 *fbuf = uptr->filebuf; /* buffer base */ + +ch = ch & 0377; /* mask char */ +if (fhd_bad_wa (wa)) return TRUE; /* addr bad? */ +fhd_cw2 = fhd_cw2 + 1; /* incr char addr */ +if (ca & 1) fbuf[ba] = (fbuf[ba] & ~0377) | ch; /* odd? low char */ +else fbuf[ba] = (fbuf[ba] & 0377) | (ch << 8); /* even, hi char */ +fhd_csum = fhd_csword (fhd_csum, ch); /* put in csum */ +if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; /* update hwmark */ +return FALSE; +} + +/* Check word address */ + +t_bool fhd_bad_wa (uint32 wa) +{ +if (wa >= FH_NUMWD) { /* bad address? */ + fhd_ace = 1; /* access error */ + fhd_busy = 0; /* abort operation */ + SET_INT (INT_FHD); + return TRUE; + } +return FALSE; +} + +/* Add character to checksum (parity) */ + +uint32 fhd_csword (uint32 cs, uint32 ch) +{ +while (ch) { /* count bits */ + ch = ch & ~(ch & (-(int32) ch)); + cs = cs ^ 0200; /* invert cs for each 1 */ + } +return cs; +} + +/* Reset routine */ + +t_stat fhd_reset (DEVICE *dptr) +{ +fhd_busy = 0; /* reset state */ +fhd_rdy = 0; +fhd_ace = 0; +fhd_dte = 0; +fhd_eor = 0; +fhd_otas = OTA_NOP; +fhd_cw1 = fhd_cw2 = fhd_buf = 0; +CLR_INT (INT_FHD); /* clear int, enb */ +CLR_ENB (INT_FHD); +sim_cancel (&fhd_unit); /* cancel operation */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat fhd_attach (UNIT *uptr, char *cptr) +{ +uint32 sz, sf; +uint32 ds_bytes = FH_WDPSF * sizeof (int16); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + sf = (sz + ds_bytes - 1) / ds_bytes; + if (sf >= FH_NUMSF) sf = FH_NUMSF - 1; + uptr->flags = (uptr->flags & ~UNIT_SF) | + (sf << UNIT_V_SF); + } +uptr->capac = UNIT_GETSF (uptr->flags) * FH_WDPSF; +return attach_unit (uptr, cptr); +} + +/* Set size routine */ + +t_stat fhd_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val < 0) return SCPE_IERR; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = UNIT_GETSF (val) * FH_WDPSF; +return SCPE_OK; +} diff --git a/H316/h316_lp.c b/H316/h316_lp.c new file mode 100644 index 0000000..48c9526 --- /dev/null +++ b/H316/h316_lp.c @@ -0,0 +1,338 @@ +/* h316_lp.c: Honeywell 316/516 line printer + + Copyright (c) 1999-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt line printer + + 09-Jun-07 RMS Fixed lost last print line (from Theo Engel) + 19-Jan-06 RMS Added UNIT_TEXT flag + 03-Apr-06 RMS Fixed bug in blanks backscanning (from Theo Engel) + 01-Dec-04 RMS Fixed bug in DMA/DMC support + 24-Oct-03 RMS Added DMA/DMC support + 25-Apr-03 RMS Revised for extended file support + 30-May-02 RMS Widened POS to 32b + + The Series 16 line printer is an unbuffered Analex shuttle printer. + Because it was unbuffered, the CPU had to scan out an entire line's + worth of characters (60 words) for every character on the print drum + (64 characters). Because it was a shuttle printer, the entire + process must be repeated first for the odd columns and then for the + even columns. After scanning the odd columns, the printer carriage + shuttled right by one column; after scanning the even columns, the + carriage shuttled left. This halved the number of hammers required, + reducing cost but increasing mechanical complexity. + + The real printer is very timing dependent. If the CPU misses a + scan, then the wrong characters are printed. If the printer protocol + is violated, then results are unpredictable. The simulated printer + is much more forgiving. Rather than simulating the fixed drum and + hammer timing of the real printer, the simulator is driven by the + program's OTA instructions. If the program misses a time slot, the + simulator will still print the "correct" result. A timing based + simulation would be very hard to do in the absense of accurate + instruction timing. + + Printer state is maintained in a set of position and state variables: + + lpt_wdpos word count within a line scan (0-59) + lpt_drpos drum position (0-63) + lpt_crpos carriage position (0-1) + lpt_svcst service state (shuttle, paper advance) + lpt_svcch channel for paper advance (0 = no adv) + lpt_rdy transfer ready flag + lpt_prdn printing done flag + lpt_dma use DMA/DMC + lpt_eor DMA/DMC end of range +*/ + +#include "h316_defs.h" + +#define LPT_WIDTH 120 /* width */ +#define LPT_SCAN (LPT_WIDTH / 2) /* words/scan */ +#define LPT_DRUM 64 /* drum rows */ +#define LPT_SVCSH 01 /* shuttle */ +#define LPT_SVCPA 02 /* paper advance */ + +extern int32 dev_int, dev_enb; +extern int32 stop_inst; +extern uint32 chan_req; + +int32 lpt_wdpos = 0; /* word position */ +int32 lpt_drpos = 0; /* drum position */ +int32 lpt_crpos = 0; /* carriage position */ +int32 lpt_svcst = 0; /* service state */ +int32 lpt_svcch = 0; /* service channel */ +int32 lpt_rdy = 0; /* transfer flag */ +int32 lpt_prdn = 1; /* printing done */ +int32 lpt_dma = 0; /* use DMA/DMC */ +int32 lpt_eor = 0; /* DMA/DMC end range */ +char lpt_buf[LPT_WIDTH + 1] = { 0 }; /* line buffer */ +int32 lpt_xtime = 5; /* transfer time */ +int32 lpt_etime = 50; /* end of scan time */ +int32 lpt_ptime = 5000; /* paper adv time */ +int32 lpt_stopioe = 0; /* stop on error */ + +int32 lptio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_mod LPT modifiers + lpt_reg LPT register list +*/ + +DIB lpt_dib = { LPT, IOBUS, 1, &lptio }; + +UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }; + +REG lpt_reg[] = { + { DRDATA (WDPOS, lpt_wdpos, 6) }, + { DRDATA (DRPOS, lpt_drpos, 6) }, + { FLDATA (CRPOS, lpt_crpos, 0) }, + { FLDATA (RDY, lpt_rdy, 0) }, + { FLDATA (EOR, lpt_eor, 0) }, + { FLDATA (DMA, lpt_dma, 0) }, + { FLDATA (PRDN, lpt_prdn, 0) }, + { FLDATA (INTREQ, dev_int, INT_V_LPT) }, + { FLDATA (ENABLE, dev_enb, INT_V_LPT) }, + { ORDATA (SVCST, lpt_svcst, 2) }, + { ORDATA (SVCCH, lpt_svcch, 2) }, + { BRDATA (BUF, lpt_buf, 8, 8, 120) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (XTIME, lpt_xtime, 24), PV_LEFT }, + { DRDATA (ETIME, lpt_etime, 24), PV_LEFT }, + { DRDATA (PTIME, lpt_ptime, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { NULL } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL, + &lpt_dib, DEV_DISABLE + }; + +/* IO routine */ + +int32 lptio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */ +int32 chr; + +switch (inst) { /* case on opcode */ + + case ioOCP: /* OCP */ + switch (fnc) { /* case on fnc */ + + case 000: case 002: case 004: /* paper adv */ + lpt_svcst = lpt_svcst | LPT_SVCPA; /* set state */ + lpt_svcch = fnc >> 1; /* save channel */ + sim_activate (&lpt_unit, lpt_ptime); + CLR_INT (INT_LPT); /* clear int */ + break; + + case 003: /* init scan DMA/DMC */ + lpt_prdn = 0; /* clear pr done */ + lpt_wdpos = 0; /* init scan pos */ + lpt_eor = 0; + if (ch >= 0) lpt_dma = 1; /* try for DMA/DMC */ + else lpt_dma = 0; + if (!sim_is_active (&lpt_unit)) { + lpt_rdy = 1; + if (lpt_dma) SET_CH_REQ (ch); + } + CLR_INT (INT_LPT); /* clear int */ + break; + + case 007: /* init scan IO bus */ + lpt_prdn = 0; /* clear pr done */ + lpt_wdpos = 0; /* init scan pos */ + lpt_eor = 0; + lpt_dma = 0; /* IO bus */ + if (!sim_is_active (&lpt_unit)) lpt_rdy = 1; + CLR_INT (INT_LPT); /* clear int */ + break; + + default: + return IOBADFNC (dat); + } + break; + + case ioSKS: /* SKS */ + switch (fnc) { /* case on fnc */ + + case 000: /* if xfer rdy */ + if (lpt_rdy) return IOSKIP (dat); + break; + + case 002: /* if !alarm */ + if (lpt_unit.flags & UNIT_ATT) return IOSKIP (dat); + break; + + case 003: /* if odd col */ + if (lpt_crpos) return IOSKIP (dat); + break; + + case 004: /* if !interrupt */ + if (!TST_INTREQ (INT_LPT)) return IOSKIP (dat); + break; + + case 011: /* if line printed */ + if (lpt_prdn) return IOSKIP (dat); + break; + + case 012: /* if !shuttling */ + if (!(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat); + break; + + case 013: + if (lpt_prdn && !(lpt_svcst & LPT_SVCSH)) return IOSKIP (dat); + break; + + case 014: /* if !advancing */ + if (!(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat); + break; + + case 015: + if (lpt_prdn && !(lpt_svcst & LPT_SVCPA)) return IOSKIP (dat); + break; + + case 016: + if (!(lpt_svcst & (LPT_SVCSH | LPT_SVCPA))) return IOSKIP (dat); + break; + + case 017: + if (lpt_prdn && !(lpt_svcst & (LPT_SVCSH | LPT_SVCPA))) + return IOSKIP (dat); + break; + + default: + return IOBADFNC (dat); + } + break; + + case ioOTA: /* OTA */ + if (fnc) return IOBADFNC (dat); /* only fnc 0 */ + if (lpt_rdy) { /* xfer ready? */ + lpt_rdy = 0; /* clear xfer */ + chr = (dat >> (lpt_crpos? 0: 8)) & 077; /* get 6b char */ + if (chr == lpt_drpos) { /* match drum pos? */ + if (chr < 040) chr = chr | 0100; + lpt_buf[2 * lpt_wdpos + lpt_crpos] = chr; + } + lpt_wdpos++; /* adv scan pos */ + if (lpt_wdpos >= LPT_SCAN) { /* end of scan? */ + lpt_wdpos = 0; /* reset scan pos */ + lpt_drpos++; /* adv drum pos */ + if (lpt_drpos >= LPT_DRUM) { /* end of drum? */ + lpt_drpos = 0; /* reset drum pos */ + lpt_crpos = lpt_crpos ^ 1; /* shuttle */ + lpt_svcst = lpt_svcst | LPT_SVCSH; + sim_activate (&lpt_unit, lpt_ptime); + } /* end if shuttle */ + else sim_activate (&lpt_unit, lpt_etime); + } /* end if endscan */ + else sim_activate (&lpt_unit, lpt_xtime); + return IOSKIP (dat); /* skip return */ + } + break; + + case ioEND: /* end DMA/DMC */ + lpt_eor = 1; /* set end range */ + break; + } /* end case op */ + +return dat; +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 i; +int32 ch = lpt_dib.chan - 1; /* DMA/DMC chan */ +static const char *lpt_cc[] = { + "\r", + "\n", + "\n\f", + "\n" + }; + +if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); +if (lpt_dma) { /* DMA/DMC? */ + if (lpt_eor) SET_INT (INT_LPT); /* end range? intr */ + else { + lpt_rdy = 1; /* set ready */ + SET_CH_REQ (ch); /* get more data */ + } + } +else lpt_rdy = 1; /* IO, continue scan */ +if (lpt_dma && lpt_eor) SET_INT (INT_LPT); /* end of range? */ +if (lpt_svcst & LPT_SVCSH) { /* shuttling? */ + SET_INT (INT_LPT); /* interrupt */ + if (lpt_crpos == 0) { /* done shuttling? */ + for (i = LPT_WIDTH - 1; i >= 0; i--) { /* backscan for blanks */ + if (lpt_buf[i] != ' ') break; + } + lpt_buf[i + 1] = 0; + fputs (lpt_buf, uptr->fileref); /* output buf */ + uptr->pos = ftell (uptr->fileref); /* update pos */ + for (i = 0; i < LPT_WIDTH; i++) lpt_buf[i] = ' '; /* clear buf */ + lpt_prdn = 1; /* print done */ + } + } +if (lpt_svcst & LPT_SVCPA) { /* paper advance */ + SET_INT (INT_LPT); /* interrupt */ + fputs (lpt_cc[lpt_svcch & 03], uptr->fileref); /* output eol */ + uptr->pos = ftell (uptr->fileref); /* update pos */ + } +lpt_svcst = 0; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +int32 i; + +lpt_wdpos = lpt_drpos = lpt_crpos = 0; /* clear positions */ +lpt_svcst = lpt_svcch = 0; /* idle state */ +lpt_rdy = 0; /* not rdy to xfer */ +lpt_prdn = 1; /* printing done */ +lpt_eor = 0; +lpt_dma = 0; +for (i = 0; i < LPT_WIDTH; i++) lpt_buf[i] = ' '; /* clear buffer */ +lpt_buf[LPT_WIDTH] = 0; +CLR_INT (INT_LPT); /* clear int, enb */ +CLR_ENB (INT_LPT); +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/H316/h316_mt.c b/H316/h316_mt.c new file mode 100644 index 0000000..46ac60c --- /dev/null +++ b/H316/h316_mt.c @@ -0,0 +1,586 @@ +/* h316_mt.c: H316/516 magnetic tape simulator + + Copyright (c) 2003-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt 516-4100 seven track magnetic tape + + 09-Jun-07 RMS Fixed bug in write without stop (from Theo Engel) + 16-Feb-06 RMS Added tape capacity checking + 26-Aug-05 RMS Revised to use API for write lock check + 08-Feb-05 RMS Fixed error reporting from OCP (found by Philipp Hachtmann) + 01-Dec-04 RMS Fixed bug in DMA/DMC support + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "h316_defs.h" +#include "sim_tape.h" + +#define MT_NUMDR 4 /* number of drives */ +#define DB_N_SIZE 16 /* max data buf */ +#define DBSIZE (1 << DB_N_SIZE) /* max data cmd */ +#define FNC u3 /* function */ +#define UST u4 /* unit status */ + +/* Function codes */ + +#define FNC_RBCD2 000 +#define FNC_RBIN2 001 +#define FNC_RBIN3 002 +#define FNC_DMANM 003 +#define FNC_WBCD2 004 +#define FNC_WBIN2 005 +#define FNC_WEOF 006 +#define FNC_IOBUS 007 +#define FNC_WBIN3 010 +#define FNC_FSR 011 +#define FNC_FSF 012 +#define FNC_DMAAU 013 +#define FNC_REW 014 +#define FNC_BSR 015 +#define FNC_BSF 016 +#define FNC_STOPW 017 +#define FNC_2ND 020 /* second state */ +#define FNC_NOP (FNC_STOPW|FNC_2ND) +#define FNC_EOM 040 /* end of motion */ + +/* Status - unit.UST */ + +#define STA_BOT 0000002 /* beg of tape */ +#define STA_EOT 0000001 /* end of tape */ + +extern int32 dev_int, dev_enb, chan_req; +extern int32 stop_inst; + +uint32 mt_buf = 0; /* data buffer */ +uint32 mt_usel = 0; /* unit select */ +uint32 mt_busy = 0; /* ctlr busy */ +uint32 mt_mdirq = 0; /* motion done int req */ +uint32 mt_rdy = 0; /* transfer ready (int) */ +uint32 mt_err = 0; /* error */ +uint32 mt_eof = 0; /* end of file */ +uint32 mt_eor = 0; /* transfer done */ +uint32 mt_dma = 0; /* DMA/DMC */ +uint32 mt_xtime = 16; /* transfer time */ +uint32 mt_ctime = 3000; /* start/stop time */ +uint32 mt_stopioe = 1; /* stop on I/O error */ +uint8 mtxb[DBSIZE] = { 0 }; /* data buffer */ +t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */ + +int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev); +void mt_updint (uint32 rdy, uint32 mdone); +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_detach (UNIT *uptr); +t_stat mt_map_err (UNIT *uptr, t_stat st); +void mt_wrwd (UNIT *uptr, uint32 dat); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +DIB mt_dib = { MT, IOBUS, MT_NUMDR, &mtio }; + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) } + }; + +REG mt_reg[] = { + { ORDATA (BUF, mt_buf, 16) }, + { ORDATA (USEL, mt_usel, 2) }, + { FLDATA (BUSY, mt_busy, 0) }, + { FLDATA (RDY, mt_rdy, 0) }, + { FLDATA (ERR, mt_err, 0) }, + { FLDATA (EOF, mt_eof, 0) }, + { FLDATA (EOR, mt_eor, 0) }, + { FLDATA (MDIRQ, mt_mdirq, 0) }, + { FLDATA (DMA, mt_dma, 0) }, + { FLDATA (INTREQ, dev_int, INT_V_MT) }, + { FLDATA (ENABLE, dev_enb, INT_V_MT) }, + { BRDATA (DBUF, mtxb, 8, 8, DBSIZE) }, + { DRDATA (BPTR, mt_ptr, DB_N_SIZE + 1) }, + { DRDATA (BMAX, mt_max, DB_N_SIZE + 1) }, + { DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT }, + { DRDATA (XTIME, mt_xtime, 24), REG_NZ + PV_LEFT }, + { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, MT_NUMDR, PV_LEFT) }, + { URDATA (FNC, mt_unit[0].FNC, 8, 8, 0, MT_NUMDR, REG_HRO) }, + { URDATA (UST, mt_unit[0].UST, 8, 2, 0, MT_NUMDR, REG_HRO) }, + { ORDATA (CHAN, mt_dib.chan, 5), REG_HRO }, + { FLDATA (STOP_IOE, mt_stopioe, 0) }, + { NULL } + }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "IOBUS", + &io_set_iobus, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DMC", + &io_set_dmc, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DMA", + &io_set_dma, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, + NULL, &io_show_chan, NULL }, + { 0 } + }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &mt_detach, + &mt_dib, DEV_DISABLE + }; + +/* IO routine */ + +int32 mtio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +uint32 i, u = dev & 03; +UNIT *uptr = mt_dev.units + u; +static uint8 wrt_fnc[16] = { /* >0 = wr, 1 = chan op */ + 0, 0, 0, 0, 1, 1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0 + }; + +switch (inst) { /* case on opcode */ + + case ioOCP: + mt_updint (mt_rdy, 0); /* clear motion intr */ + mt_eof = 0; /* clear eof */ + switch (fnc) { /* case on function */ + + case FNC_DMANM: /* set DMA/DMC */ + case FNC_DMAAU: + mt_usel = u; /* save unit select */ + if (mt_dib.chan) mt_dma = 1; /* set DMA if configured */ + else mt_dma = 0; + break; + + case FNC_IOBUS: /* set IOBUS */ + mt_usel = u; /* save unit select */ + mt_dma = 0; + break; + + case FNC_STOPW: /* stop write */ + mt_usel = u; /* save unit select */ + mt_updint (0, mt_mdirq); /* clear ready */ + if (wrt_fnc[uptr->FNC & 017] == 1) /* writing? */ + mt_eor = 1; /* set transfer done */ + break; + + default: /* motion command */ + if (mt_busy) return dat; /* nop if ctlr busy */ + mt_eor = 0; /* clr transfer done */ + mt_err = 0; /* clr error */ + mt_usel = u; /* save unit select */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return (((mt_stopioe? SCPE_UNATT: SCPE_OK) << IOT_V_REASON) | dat); + if (sim_is_active (uptr)) return dat; /* nop if busy */ + if (wrt_fnc[fnc] && sim_tape_wrp (uptr)) + return ((STOP_MTWRP << IOT_V_REASON) | dat); + uptr->FNC = fnc; + uptr->UST = 0; + mt_busy = 1; + for (i = 0; i < MT_NUMDR; i++) /* clear all EOT flags */ + mt_unit[i].UST = mt_unit[i].UST & ~STA_EOT; + sim_activate (uptr, mt_ctime); /* schedule */ + break; + } + break; + + case ioINA: /* INA */ + if (fnc) return IOBADFNC (dat); /* fnc 0 only */ + if (mt_rdy) { /* ready? */ + mt_rdy = 0; /* clear ready */ + return IOSKIP (dat | mt_buf); /* ret buf, skip */ + } + break; + + case ioOTA: /* OTA */ + if (fnc) return IOBADFNC (dat); /* fnc 0 only */ + if (mt_rdy) { /* ready? */ + mt_rdy = 0; /* clear ready */ + mt_buf = dat; /* store buf */ + return IOSKIP (dat); /* skip */ + } + break; + + case ioSKS: + uptr = mt_dev.units + mt_usel; /* use saved unit sel */ + switch (fnc) { + + case 000: /* ready */ + if (mt_rdy) return IOSKIP (dat); + break; + + case 001: /* !busy */ + if (!mt_busy) return IOSKIP (dat); + break; + + case 002: /* !error */ + if (!mt_err) return IOSKIP (dat); + break; + + case 003: /* !BOT */ + if (!(uptr->UST & STA_BOT)) return IOSKIP (dat); + break; + + case 004: /* !interrupting */ + if (!TST_INTREQ (INT_MT)) return IOSKIP (dat); + break; + + case 005: /* !EOT */ + if (!(uptr->UST & STA_EOT)) return IOSKIP (dat); + break; + + case 006: /* !EOF */ + if (!mt_eof) return IOSKIP (dat); + break; + + case 007: /* !write prot */ + if (!sim_tape_wrp (uptr)) return IOSKIP (dat); + break; + + case 011: /* operational */ + if ((uptr->flags & UNIT_ATT) && + ((uptr->FNC & 017) != FNC_REW)) return IOSKIP (dat); + break; + + case 012: /* skip if !chan 2 */ + return IOSKIP (dat); + + case 013: /* skip if !auto */ + return IOSKIP (dat); + + case 014: /* !rewinding */ + uptr = mt_dev.units + (dev & 03); /* use specified unit */ + if ((uptr->FNC & 017) != FNC_REW) return IOSKIP (dat); + break; + } + break; + + case ioEND: /* end of range */ + mt_eor = 1; /* transfer done */ + break; + } + +return dat; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt + + Can't be write locked, can only write lock detached unit +*/ + +t_stat mt_svc (UNIT *uptr) +{ +int32 ch = mt_dib.chan - 1; /* DMA/DMC ch */ +uint32 i, c1, c2, c3; +t_mtrlnt tbc; +t_bool passed_eot; +t_stat st, r = SCPE_OK; + +if ((uptr->flags & UNIT_ATT) == 0) { /* offline? */ + mt_err = 1; + mt_busy = 0; + mt_updint (0, 1); /* cmd done */ + return IORETURN (mt_stopioe, SCPE_UNATT); + } + +passed_eot = sim_tape_eot (uptr); /* passed EOT? */ +switch (uptr->FNC) { /* case on function */ + + case FNC_REW: /* rewind (initial) */ + mt_busy = 0; /* ctlr not busy */ + uptr->FNC = uptr->FNC | FNC_2ND; + sim_activate (uptr, mt_ctime); + return SCPE_OK; /* continue */ + + case FNC_REW | FNC_2ND: /* rewind done */ + uptr->pos = 0; /* reposition file */ + uptr->UST = STA_BOT; /* set BOT */ + uptr->FNC = FNC_NOP; /* nop function */ + for (i = 0; i < MT_NUMDR; i++) { /* last rewind? */ + if ((mt_unit[i].FNC & 017) == FNC_REW) return SCPE_OK; + } + mt_updint (mt_rdy, 1); /* yes, motion done */ + return SCPE_OK; + + case FNC_WEOF: /* write file mark */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* sched end motion */ + + case FNC_FSR: /* space fwd rec */ + if (st = sim_tape_sprecf (uptr, &tbc)) /* space fwd, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* sched end motion */ + + case FNC_BSR: /* space rev rec */ + if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* sched end motion */ + + case FNC_FSF: /* space fwd file */ + while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; + r = mt_map_err (uptr, st); /* map error */ + break; /* sched end motion */ + + case FNC_BSF: /* space rev file */ + while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; + r = mt_map_err (uptr, st); /* map error */ + break; /* sched end motion */ + + case FNC_EOM: /* end of motion */ + uptr->FNC = FNC_NOP; /* nop function */ + mt_busy = 0; /* not busy */ + mt_updint (mt_rdy, 1); /* end of motion */ + return SCPE_OK; /* done! */ + + case FNC_RBCD2: case FNC_RBIN2: case FNC_RBIN3: /* read first */ + mt_ptr = 0; /* clr buf ptr */ + st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */ + if (st != MTSE_OK) { /* error? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* sched end motion */ + } + uptr->FNC = uptr->FNC | FNC_2ND; /* next state */ + sim_activate (uptr, mt_xtime); /* sched xfer */ + return SCPE_OK; + + case FNC_RBCD2 | FNC_2ND: /* read, word */ + case FNC_RBIN2 | FNC_2ND: + case FNC_RBIN3 | FNC_2ND: + if (mt_ptr >= mt_max) break; /* record done? */ + c1 = mtxb[mt_ptr++] & 077; /* get 2 chars */ + c2 = mtxb[mt_ptr++] & 077; + if (uptr->FNC == (FNC_RBCD2 | FNC_2ND)) { /* BCD? */ + if (c1 == 012) c1 = 0; /* change 12 to 0 */ + if (c2 == 012) c2 = 0; + } + if (uptr->FNC == (FNC_RBIN3 | FNC_2ND)) { /* read 3? */ + if (mt_ptr >= mt_max) break; /* lose wd if not enuf */ + c3 = mtxb[mt_ptr++] & 017; /* get 3rd char */ + } + else c3 = 0; + sim_activate (uptr, mt_xtime); /* no, sched word */ + if (mt_eor) return SCPE_OK; /* xfer done? */ + mt_buf = (c1 << 10) | (c2 << 4) | c3; /* pack chars */ + if (mt_rdy) mt_err = 1; /* buf full? err */ + mt_updint (1, mt_mdirq); /* set ready */ + if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */ + return SCPE_OK; /* continue */ + + case FNC_WBCD2: case FNC_WBIN2: case FNC_WBIN3: /* write first */ + mt_ptr = 0; /* clear buf ptr */ + mt_updint (1, mt_mdirq); /* set ready */ + if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */ + uptr->FNC = uptr->FNC | FNC_2ND; /* next state */ + sim_activate (uptr, mt_xtime); /* sched xfer */ + return SCPE_OK; /* continue */ + + case FNC_WBCD2 | FNC_2ND: /* write, word */ + case FNC_WBIN2 | FNC_2ND: + case FNC_WBIN3 | FNC_2ND: + if (mt_eor || mt_rdy) { /* done or no data? */ + if (!mt_rdy) mt_wrwd (uptr, mt_buf); /* write last word */ + else mt_rdy = 0; /* rdy must be clr */ + if (mt_ptr) { /* any data? */ + if (st = sim_tape_wrrecf (uptr, mtxb, mt_ptr)) /* write, err? */ + r = mt_map_err (uptr, st); /* map error */ + } + break; /* sched end motion */ + } + mt_wrwd (uptr, mt_buf); /* write word */ + sim_activate (uptr, mt_xtime); /* no, sched word */ + mt_updint (1, mt_mdirq); /* set ready */ + if (mt_dma) SET_CH_REQ (ch); /* DMC/DMA? req chan */ + return SCPE_OK; /* continue */ + + default: /* unknown */ + break; + } + +/* End of command, process error or schedule end of motion */ + +if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ + uptr->UST = uptr->UST | STA_EOT; +if (r != SCPE_OK) { + uptr->FNC = FNC_NOP; /* nop function */ + mt_busy = 0; /* not busy */ + mt_updint (mt_rdy, 1); /* end of motion */ + return r; + } +uptr->FNC = FNC_EOM; /* sched end motion */ +sim_activate (uptr, mt_ctime); +return SCPE_OK; +} + +/* Write word to buffer */ + +void mt_wrwd (UNIT *uptr, uint32 dat) +{ +uint32 c1, c2; + +c1 = (dat >> 10) & 077; /* get 2 chars */ +c2 = (dat >> 4) & 077; +if (uptr->FNC == (FNC_WBCD2 | FNC_2ND)) { /* BCD? */ + if (c1 == 0) c1 = 012; /* change 0 to 12 */ + if (c2 == 0) c2 = 012; + } +if (mt_ptr < DBSIZE) mtxb[mt_ptr++] = c1; /* store 2 char */ +if (mt_ptr < DBSIZE) mtxb[mt_ptr++] = c2; +if ((uptr->FNC == (FNC_WBIN3 | FNC_2ND)) && /* write 3? */ + (mt_ptr < DBSIZE)) mtxb[mt_ptr++] = mt_buf & 017; +return; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* unattached */ + mt_err = 1; /* reject */ + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_TMK: /* end of file */ + mt_eof = 1; /* eof */ + break; + + case MTSE_INVRL: /* invalid rec lnt */ + mt_err = 1; + return SCPE_MTRLNT; + + case MTSE_IOERR: /* IO error */ + mt_err = 1; /* error */ + if (mt_stopioe) return SCPE_IOERR; + break; + + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + mt_err = 1; /* error */ + break; + + case MTSE_BOT: /* reverse into BOT */ + uptr->UST = STA_BOT; /* set status */ + break; + + case MTSE_WRP: /* write protect */ + mt_err = 1; /* error */ + return STOP_MTWRP; + } + +return SCPE_OK; +} + +/* Update interrupts */ + +void mt_updint (uint32 rdy, uint32 mdirq) +{ +mt_rdy = rdy; /* store new ready */ +mt_mdirq = mdirq; /* store new motion irq */ +if ((mt_rdy && !mt_dma) || mt_mdirq) SET_INT (INT_MT); /* update int request */ +else CLR_INT (INT_MT); +return; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +mt_buf = 0; /* clear state */ +mt_usel = 0; +mt_mdirq = 0; +mt_eor = 0; +mt_busy = 0; +mt_rdy = 0; +mt_eof = 0; +mt_err = 0; +mt_dma = 0; +CLR_INT (INT_MT); /* clear int, enb */ +CLR_ENB (INT_MT); +for (i = 0; i < MT_NUMDR; i++) { /* loop thru units */ + uptr = mt_dev.units + i; + sim_tape_reset (uptr); /* reset tape */ + sim_cancel (uptr); /* cancel op */ + uptr->UST = uptr->pos? 0: STA_BOT; /* update status */ + uptr->FNC = FNC_NOP; + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* update status */ +uptr->UST = STA_BOT; +return r; +} + +/* Detach routine */ + +t_stat mt_detach (UNIT* uptr) +{ +uptr->UST = 0; /* update status */ +uptr->FNC = FNC_NOP; /* nop function */ +return sim_tape_detach (uptr); /* detach unit */ +} diff --git a/H316/h316_stddev.c b/H316/h316_stddev.c new file mode 100644 index 0000000..4565277 --- /dev/null +++ b/H316/h316_stddev.c @@ -0,0 +1,865 @@ +/* h316_stddev.c: Honeywell 316/516 standard devices + + Copyright (c) 1999-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr 316/516-50 paper tape reader + ptp 316/516-52 paper tape punch + tty 316/516-33 teleprinter + clk/options 316/516-12 real time clocks/internal options + + 09-Jun-07 RMS Fixed bug in clock increment (found by Theo Engel) + 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode + 03-Apr-06 RMS Fixed bugs in punch state handling (from Theo Engel) + 22-Nov-05 RMS Revised for new terminal processing routines + 05-Feb-05 RMS Fixed bug in OCP '0001 (found by Philipp Hachtmann) + 31-Jan-05 RMS Fixed bug in TTY print (found by Philipp Hachtmann) + 01-Dec-04 RMS Fixed problem in SKS '104 (reported by Philipp Hachtmann) + Fixed bug in SKS '504 + Added PTR detach routine, stops motion + Added PTR/PTP ASCII file support + Added TTR/TTP support + 24-Oct-03 RMS Added DMA/DMC support + 25-Apr-03 RMS Revised for extended file support + 01-Mar-03 RMS Added SET/SHOW CLK FREQ support + 22-Dec-02 RMS Added break support + 01-Nov-02 RMS Added 7b/8b support to terminal + 30-May-02 RMS Widened POS to 32b + 03-Nov-01 RMS Implemented upper case for console output + 29-Nov-01 RMS Added read only unit support + 07-Sep-01 RMS Moved function prototypes + + The ASR-33/35 reader/punch logic, and the ASCII file support for all paper tape + devices, logic is taken, with grateful thanks, from Adrian Wise's H316 emulator. + + Teletype reader transitions: + + - SET TTY2 START puts the reader in RUN + - XOFF from keyboard/reader stops the reader after 1-2 more characters are read + - XON from program starts the reader + - Detach, SET TTY2 STOP, or end of file stops the reader + + Teletype punch transitions: + + - SET TTY3 START puts the punch in RUN + - XOFF from program stops the punch after 1 more character is punched + - TAPE from program starts the punch after 1 character delay + - Detach or SET TTY3 STOP stops the punch +*/ + +#include "h316_defs.h" +#include + +#define UNIT_V_ASC (TTUF_V_UF + 0) /* ASCII */ +#define UNIT_V_UASC (TTUF_V_UF + 1) /* Unix ASCII */ +#define UNIT_ASC (1 << UNIT_V_ASC) +#define UNIT_UASC (1 << UNIT_V_UASC) +#define STA u3 /* state bits */ +#define LF_PEND 01 /* lf pending */ +#define RUNNING 02 /* tape running */ + +#define XON 0021 +#define TAPE 0022 +#define XOFF 0023 +#define RUBOUT 0377 + +extern uint16 M[]; +extern int32 PC; +extern int32 stop_inst; +extern int32 C, dp, ext, extoff_pending, sc; +extern int32 dev_int, dev_enb; +extern int32 sim_switches; +extern UNIT cpu_unit; + +uint32 ptr_motion = 0; /* read motion */ +uint32 ptr_stopioe = 0; /* stop on error */ +uint32 ptp_stopioe = 0; +uint32 ptp_power = 0; /* punch power, time */ +int32 ptp_ptime; +uint32 ttr_stopioe = 0; +uint32 tty_mode = 0; /* input (0), output (1) */ +uint32 tty_buf = 0; /* tty buffer */ +uint32 ttr_xoff_read = 0; +uint32 ttp_tape_rcvd = 0; +uint32 ttp_xoff_rcvd = 0; +int32 clk_tps = 60; /* ticks per second */ + +int32 ptrio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); +int32 ptpio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); +int32 ttyio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tty_reset (DEVICE *dptr); +t_stat ttio_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ttrp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ttrp_set_start_stop (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 clkio (int32 inst, int32 fnc, int32 dat, int32 dev); +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat pt_attach (UNIT *uptr, char *cptr); +t_stat pt_detach (UNIT *uptr); +t_stat tto_write (int32 c); +t_stat ttp_write (int32 c); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_mod PTR modifiers + ptr_reg PTR register list +*/ + +DIB ptr_dib = { PTR, IOBUS, 1, &ptrio }; + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { FLDATA (READY, dev_int, INT_V_PTR) }, + { FLDATA (ENABLE, dev_enb, INT_V_PTR) }, + { FLDATA (MOTION, ptr_motion, 0) }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { ORDATA (RSTATE, ptr_unit.STA, 2), REG_HIDDEN }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } + }; + +MTAB pt_mod[] = { + { UNIT_ATT+UNIT_ASC+UNIT_UASC, UNIT_ATT+UNIT_ASC, "ASCII", NULL }, + { UNIT_ATT+UNIT_ASC+UNIT_UASC, UNIT_ATT+UNIT_ASC+UNIT_UASC, "Unix ASCII", NULL }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, pt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, &pt_attach, &pt_detach, + &ptr_dib, 0 + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_mod PTP modifiers + ptp_reg PTP register list +*/ + +DIB ptp_dib = { PTP, IOBUS, 1, &ptpio }; + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (READY, dev_int, INT_V_PTP) }, + { FLDATA (ENABLE, dev_enb, INT_V_PTP) }, + { FLDATA (POWER, ptp_power, 0) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { ORDATA (PSTATE, ptp_unit.STA, 2), REG_HIDDEN }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { DRDATA (PWRTIME, ptp_ptime, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, pt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, &pt_attach, NULL, + &ptp_dib, 0 + }; + +/* TTY data structures + + tty_dev TTY device descriptor + tty_unit TTY unit descriptor + tty_reg TTY register list + tty_mod TTy modifiers list +*/ + +#define TTI 0 +#define TTO 1 +#define TTR 2 +#define TTP 3 + +DIB tty_dib = { TTY, IOBUS, 1, &ttyio }; + +UNIT tty_unit[] = { + { UDATA (&tti_svc, TT_MODE_KSR, 0), KBD_POLL_WAIT }, + { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } + }; + +REG tty_reg[] = { + { ORDATA (BUF, tty_buf, 8) }, + { FLDATA (MODE, tty_mode, 0) }, + { FLDATA (READY, dev_int, INT_V_TTY) }, + { FLDATA (ENABLE, dev_enb, INT_V_TTY) }, + { DRDATA (KPOS, tty_unit[TTI].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (KTIME, tty_unit[TTI].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPOS, tty_unit[TTO].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TTIME, tty_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, + { ORDATA (RXOFF, ttr_xoff_read, 2), REG_HIDDEN }, + { ORDATA (RSTATE, tty_unit[TTR].STA, 2), REG_HIDDEN }, + { DRDATA (RPOS, tty_unit[TTR].pos, T_ADDR_W), PV_LEFT }, + { ORDATA (PTAPE, ttp_tape_rcvd, 2), REG_HIDDEN }, + { ORDATA (PXOFF, ttp_xoff_rcvd, 2), REG_HIDDEN }, + { ORDATA (PSTATE, tty_unit[TTP].STA, 2), REG_HIDDEN }, + { DRDATA (PPOS, tty_unit[TTP].pos, T_ADDR_W), PV_LEFT }, + { FLDATA (STOP_IOE, ttr_stopioe, 0) }, + { NULL } + }; + +MTAB tty_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &ttio_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &ttio_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &ttio_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &ttio_set_mode }, + { UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, UNIT_ATTABLE, NULL, "BINARY", + &ttrp_set_mode }, + { UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, UNIT_ATTABLE+UNIT_ASC, "ASCII", "ASCII", + &ttrp_set_mode }, + { UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, UNIT_ATTABLE+UNIT_ASC+UNIT_UASC, "Unix ASCII", "UASCII", + &ttrp_set_mode }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, NULL, "START", &ttrp_set_start_stop }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, NULL, "STOP", &ttrp_set_start_stop }, + { 0 } + }; + +DEVICE tty_dev = { + "TTY", tty_unit, tty_reg, tty_mod, + 4, 10, 31, 1, 8, 8, + NULL, NULL, &tty_reset, + NULL, &pt_attach, &pt_detach, + &tty_dib, 0 + }; + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_mod CLK modifiers + clk_reg CLK register list +*/ + +DIB clk_dib = { CLK_KEYS, IOBUS, 1, &clkio }; + +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; + +REG clk_reg[] = { + { FLDATA (READY, dev_int, INT_V_CLK) }, + { FLDATA (ENABLE, dev_enb, INT_V_CLK) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &clk_show_freq, NULL }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, 0 + }; + +/* Paper tape reader: IO routine */ + +int32 ptrio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +switch (inst) { /* case on opcode */ + + case ioOCP: /* OCP */ + if (fnc & 016) return IOBADFNC (dat); /* only fnc 0,1 */ + ptr_motion = fnc ^ 1; + if (fnc) sim_cancel (&ptr_unit); /* fnc 1? stop */ + else sim_activate (&ptr_unit, ptr_unit.wait); /* fnc 0? start */ + break; + + case ioSKS: /* SKS */ + if (fnc & 013) return IOBADFNC (dat); /* only fnc 0,4 */ + if (((fnc == 000) && TST_INT (INT_PTR)) || /* fnc 0? skip rdy */ + ((fnc == 004) && !TST_INTREQ (INT_PTR))) /* fnc 4? skip !int */ + return IOSKIP (dat); + break; + + case ioINA: /* INA */ + if (fnc) return IOBADFNC (dat); /* only fnc 0 */ + if (TST_INT (INT_PTR)) { /* ready? */ + CLR_INT (INT_PTR); /* clear ready */ + if (ptr_motion) /* if motion, restart */ + sim_activate (&ptr_unit, ptr_unit.wait); + return IOSKIP (ptr_unit.buf | dat); /* ret buf, skip */ + } + break; + } /* end case op */ + +return dat; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 c; + +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if (uptr->STA & LF_PEND) { /* lf pending? */ + uptr->STA &= ~LF_PEND; /* clear flag */ + c = 0212; /* insert LF */ + } +else { + if ((c = getc (uptr->fileref)) == EOF) { /* read byte */ + if (feof (uptr->fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + if ((uptr->flags & UNIT_UASC) && (c == '\n')) { /* Unix newline? */ + c = 0215; /* insert CR */ + uptr->STA |= LF_PEND; /* lf pending */ + } + else if ((uptr->flags & UNIT_ASC) && (c != 0)) /* ASCII? */ + c = c | 0200; + uptr->pos = ftell (uptr->fileref); /* update pos */ + } +SET_INT (INT_PTR); /* set ready flag */ +uptr->buf = c & 0377; /* get byte */ +return SCPE_OK; +} + +/* Paper tape attach routine - set or clear ASC/UASC flags if specified */ + +t_stat pt_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOFNC; +if (r = attach_unit (uptr, cptr)) return r; +if (sim_switches & SWMASK ('A')) /* -a? ASCII */ + uptr->flags |= UNIT_ASC; +else if (sim_switches & SWMASK ('U')) /* -u? Unix ASCII */ + uptr->flags |= (UNIT_ASC|UNIT_UASC); +else if (sim_switches & SWMASK ('B')) /* -b? binary */ + uptr->flags &= ~(UNIT_ASC|UNIT_UASC); +uptr->STA = 0; +return r; +} + +/* Detach routine - stop motion if not restore */ + +t_stat pt_detach (UNIT *uptr) +{ +if (!(sim_switches & SIM_SW_REST)) sim_cancel (uptr); /* stop motion */ +uptr->STA = 0; +return detach_unit (uptr); +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +CLR_INT (INT_PTR); /* clear ready, enb */ +CLR_ENB (INT_PTR); +ptr_unit.buf = 0; /* clear buffer */ +ptr_unit.STA = 0; +ptr_motion = 0; /* unit stopped */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Paper tape reader bootstrap routine */ + +#define PBOOT_START 1 +#define PBOOT_SIZE (sizeof (pboot) / sizeof (int32)) + +static const int32 pboot[] = { + 0010057, /* STA 57 */ + 0030001, /* OCP 1 */ + 0131001, /* READ, INA 1001 */ + 0002003, /* JMP READ */ + 0101040, /* SNZ */ + 0002003, /* JMP READ */ + 0010000, /* STA 0 */ + 0131001, /* READ1, INA 1001 */ + 0002010, /* JMP READ1 */ + 0041470, /* LGL 8 */ + 0130001, /* READ2, INA 1 */ + 0002013, /* JMP READ2 */ + 0110000, /* STA* 0 */ + 0024000, /* IRS 0 */ + 0100040 /* SZE */ + }; + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < PBOOT_SIZE; i++) /* copy bootstrap */ + M[PBOOT_START + i] = pboot[i]; +PC = PBOOT_START; +return SCPE_OK; +} + +/* Paper tape punch: IO routine */ + +int32 ptpio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +switch (inst) { /* case on opcode */ + + case ioOCP: /* OCP */ + if (fnc & 016) return IOBADFNC (dat); /* only fnc 0,1 */ + if (fnc) { /* fnc 1? pwr off */ + CLR_INT (INT_PTP); /* not ready */ + ptp_power = 0; /* turn off power */ + sim_cancel (&ptp_unit); /* stop punch */ + } + else if (ptp_power == 0) /* fnc 0? start */ + sim_activate (&ptp_unit, ptp_ptime); + break; + + case ioSKS: /* SKS */ + if ((fnc & 012) || (fnc == 005)) /* only 0, 1, 4 */ + return IOBADFNC (dat); + if (((fnc == 000) && TST_INT (INT_PTP)) || /* fnc 0? skip rdy */ + ((fnc == 001) && /* fnc 1? skip ptp on */ + (ptp_power || sim_is_active (&ptp_unit))) || + ((fnc == 004) && !TST_INTREQ (INT_PTP))) /* fnc 4? skip !int */ + return IOSKIP (dat); + break; + + case ioOTA: /* OTA */ + if (fnc) return IOBADFNC (dat); /* only fnc 0 */ + if (TST_INT (INT_PTP)) { /* if ptp ready */ + CLR_INT (INT_PTP); /* clear ready */ + ptp_unit.buf = dat & 0377; /* store byte */ + sim_activate (&ptp_unit, ptp_unit.wait); + return IOSKIP (dat); /* skip return */ + } + break; + } + +return dat; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +int32 c; + +SET_INT (INT_PTP); /* set flag */ +if (ptp_power == 0) { /* power on? */ + ptp_power = 1; /* ptp is ready */ + return SCPE_OK; + } +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (uptr->flags & UNIT_ASC) { /* ASCII? */ + c = uptr->buf & 0177; /* mask to 7b */ + if ((uptr->flags & UNIT_UASC) && (c == 015)) /* cr? drop if Unix */ + return SCPE_OK; + else if (c == 012) c = '\n'; /* lf? cvt to nl */ + } +else c = uptr->buf & 0377; /* no, binary */ +if (putc (c, uptr->fileref) == EOF) { /* output byte */ + perror ("PTP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +uptr->pos = ftell (uptr->fileref); /* update pos */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +CLR_INT (INT_PTP); /* clear ready, enb */ +CLR_ENB (INT_PTP); +ptp_power = 0; /* power off */ +ptp_unit.buf = 0; /* clear buffer */ +ptp_unit.STA = 0; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Terminal: IO routine */ + +int32 ttyio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +switch (inst) { /* case on opcode */ + + case ioOCP: /* OCP */ + if (fnc & 016) return IOBADFNC (dat); /* only fnc 0,1 */ + if (fnc && (tty_mode == 0)) { /* input to output? */ + if (!sim_is_active (&tty_unit[TTO])) /* set ready */ + SET_INT (INT_TTY); + tty_mode = 1; /* mode is output */ + } + else if ((fnc == 0) && tty_mode) { /* output to input? */ + CLR_INT (INT_TTY); /* clear ready */ + tty_mode = 0; /* mode is input */ + } + break; + + case ioSKS: /* SKS */ + if (fnc & 012) return IOBADFNC (dat); /* fnc 0,1,4,5 */ + if (((fnc == 000) && TST_INT (INT_TTY)) || /* fnc 0? skip rdy */ + ((fnc == 001) && /* fnc 1? skip !busy */ + (!tty_mode || !sim_is_active (&tty_unit[TTO]))) || + ((fnc == 004) && !TST_INTREQ (INT_TTY)) || /* fnc 4? skip !int */ + ((fnc == 005) && (tty_mode || /* fnc 5? skip !xoff */ + ((tty_buf & 0177) != XOFF)))) /* input & XOFF char */ + return IOSKIP (dat); + break; + + case ioINA: /* INA */ + if (fnc & 005) return IOBADFNC (dat); /* only 0,2 */ + if (TST_INT (INT_TTY)) { /* ready? */ + if (tty_mode == 0) CLR_INT (INT_TTY); /* inp? clear rdy */ + return IOSKIP (dat | + (tty_buf & ((fnc & 002)? 077: 0377))); + } + break; + + case ioOTA: + if (fnc & 015) return IOBADFNC (dat); /* only 0,2 */ + if (TST_INT (INT_TTY)) { /* ready? */ + tty_buf = dat & 0377; /* store char */ + if (fnc & 002) { /* binary mode? */ + tty_buf = tty_buf | 0100; /* set ch 7 */ + if (tty_buf & 040) tty_buf = tty_buf & 0277; + } + if (tty_mode) { + sim_activate (&tty_unit[TTO], tty_unit[TTO].wait); + CLR_INT (INT_TTY); + } + return IOSKIP (dat); + } + break; + } /* end case op */ + +return dat; +} + +/* Input service - keyboard and reader */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 out, c; +UNIT *ruptr = &tty_unit[TTR]; + +sim_activate (uptr, uptr->wait); /* continue poll */ +if ((c = sim_poll_kbd ()) >= SCPE_KFLAG) { /* character? */ + out = c & 0177; /* mask echo to 7b */ + if (c & SCPE_BREAK) c = 0; /* break? */ + else c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); + uptr->pos = uptr->pos + 1; + } +else if (c != SCPE_OK) return c; /* error? */ +else if ((ruptr->flags & UNIT_ATT) && /* TTR attached */ + (ruptr->STA & RUNNING)) { /* and running? */ + if (ruptr->STA & LF_PEND) { /* lf pending? */ + c = 0212; /* char is lf */ + ruptr->STA &= ~LF_PEND; /* clear flag */ + } + else { /* normal read */ + if ((c = getc (ruptr->fileref)) == EOF) { /* read byte */ + if (feof (ruptr->fileref)) { /* EOF? */ + ruptr->STA &= ~RUNNING; /* stop reader */ + if (ttr_stopioe) printf ("TTR end of file\n"); + else return SCPE_OK; + } + else perror ("TTR I/O error"); + clearerr (ruptr->fileref); + return SCPE_IOERR; + } + if ((ruptr->flags & UNIT_UASC) && (c == '\n')) { + c = 0215; /* Unix ASCII NL? */ + ruptr->STA |= LF_PEND; /* LF pending */ + } + else if ((ruptr->flags & UNIT_ASC) && (c != 0)) + c = c | 0200; /* ASCII nz? cvt */ + ruptr->pos = ftell (ruptr->fileref); + } + if (ttr_xoff_read != 0) { /* reader stopping? */ + if (c == RUBOUT) ttr_xoff_read = 0; /* rubout? stop */ + else ttr_xoff_read--; /* else decr state */ + if (ttr_xoff_read == 0) /* delay done? */ + ruptr->STA &= ~RUNNING; /* stop reader */ + } + else if ((c & 0177) == XOFF) ttr_xoff_read = 2; /* XOFF read? */ + out = c; /* echo char */ + } +else return SCPE_OK; /* no char */ +if (tty_mode == 0) { /* input mode? */ + tty_buf = c & 0377; /* put char in buf */ + SET_INT (INT_TTY); /* set flag */ + } +tto_write (out); /* echo to printer */ +return ttp_write (out); /* and punch */ +} + +/* Output service - printer and punch */ + +t_stat tto_svc (UNIT *uptr) +{ +uint32 c7b; +UNIT *ruptr = &tty_unit[TTR]; +UNIT *puptr = &tty_unit[TTP]; +t_stat r; + +c7b = tty_buf & 0177; +if (ttp_tape_rcvd != 0) { /* prev = tape? */ + ttp_tape_rcvd--; /* decrement state */ + if ((ttp_tape_rcvd == 0) && (puptr->flags & UNIT_ATT)) + puptr->STA |= RUNNING; /* start after delay */ + } +else if (c7b == TAPE) ttp_tape_rcvd = 2; /* char = TAPE? */ +if (ttp_xoff_rcvd != 0) { /* prev = XOFF? */ + ttp_xoff_rcvd--; /* decrement state */ + if (ttp_xoff_rcvd == 0) puptr->STA &= ~RUNNING; /* stop after delay */ + } +else if (c7b == XOFF) ttp_xoff_rcvd = 2; /* char = XOFF? */ +if ((c7b == XON) && (ruptr->flags & UNIT_ATT)) { /* char = XON? */ + ruptr->STA |= RUNNING; /* start reader */ + ttr_xoff_read = 0; /* cancel stop */ + } +if ((r = tto_write (tty_buf)) != SCPE_OK) { /* print; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } +if ((r = ttp_write (tty_buf)) != SCPE_OK) return r; /* punch; error? */ +SET_INT (INT_TTY); /* set done flag */ +return SCPE_OK; +} + +/* Output to printer */ + +t_stat tto_write (int32 c) +{ +UNIT *tuptr = &tty_unit[TTO]; + +c = sim_tt_outcvt (c, TT_GET_MODE (tuptr->flags) | TTUF_KSR); +tuptr->pos = tuptr->pos + 1; +if (c >= 0) return sim_putchar_s (c); +else return SCPE_OK; +} + +/* Output to punch */ + +t_stat ttp_write (int32 c) +{ +uint32 p, c7b; +UNIT *puptr = &tty_unit[TTP]; + +if ((puptr->flags & UNIT_ATT) && /* TTP attached */ + (puptr->STA & RUNNING)) { /* and running? */ + c7b = c & 0177; + if (!(puptr->flags & UNIT_UASC) || (c7b != 015)) { + if (puptr->flags & UNIT_ASC) { /* ASCII? */ + if (c7b == 012) p = '\n'; /* cvt LF */ + else p = c7b; /* else 7b */ + } + else p = c; /* untouched */ + if (putc (p, puptr->fileref) == EOF) { /* output byte */ + perror ("TTP I/O error"); + clearerr (puptr->fileref); + return SCPE_IOERR; + } + puptr->pos = ftell (puptr->fileref); /* update pos */ + } + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tty_reset (DEVICE *dptr) +{ +CLR_INT (INT_TTY); /* clear ready, enb */ +CLR_ENB (INT_TTY); +tty_mode = 0; /* mode = input */ +tty_buf = 0; +ttr_xoff_read = 0; /* clr TTR, TTP flags */ +ttp_tape_rcvd = 0; +ttp_xoff_rcvd = 0; +tty_unit[TTR].STA = 0; +tty_unit[TTP].STA = 0; +sim_activate (&tty_unit[TTI], tty_unit[TTI].wait); /* activate poll */ +sim_cancel (&tty_unit[TTO]); /* cancel output */ +return SCPE_OK; +} + +/* Set keyboard/printer mode - make sure flags agree */ + +t_stat ttio_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATTABLE) return SCPE_NOFNC; /* not TTR, TTP */ +tty_unit[TTO].flags = (tty_unit[TTO].flags & ~TT_MODE) | val; +if (val == TT_MODE_7P) val = TT_MODE_7B; +tty_unit[TTI].flags = (tty_unit[TTI].flags & ~TT_MODE) | val; +return SCPE_OK; +} + +/* Set reader/punch mode */ + +t_stat ttrp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOFNC; /* TTR, TTP only */ +if (!(val & UNIT_UASC)) uptr->STA &= ~LF_PEND; +return SCPE_OK; +} + +/* Set reader/punch start/stop */ + +t_stat ttrp_set_start_stop (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOFNC; /* TTR, TTP only */ +if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT; /* must be attached */ +if (val) uptr->STA |= RUNNING; /* start? set running */ +else uptr->STA &= ~RUNNING; /* stop? clr running */ +if (uptr->flags & UNIT_ROABLE) ttr_xoff_read = 0; /* TTR? cancel stop */ +else ttp_tape_rcvd = ttp_xoff_rcvd = 0; /* TTP? cancel all */ +return SCPE_OK; +} + +/* Clock/options: IO routine */ + +int32 clkio (int32 inst, int32 fnc, int32 dat, int32 dev) +{ +switch (inst) { /* case on opcode */ + + case ioOCP: /* OCP */ + if (fnc & 015) return IOBADFNC (dat); /* only fnc 0,2 */ + CLR_INT (INT_CLK); /* reset ready */ + if (fnc) sim_cancel (&clk_unit); /* fnc = 2? stop */ + else { /* fnc = 0? */ + if (!sim_is_active (&clk_unit)) + sim_activate (&clk_unit, /* activate */ + sim_rtc_init (clk_unit.wait)); /* init calibr */ + } + break; + + case ioSKS: /* SKS */ + if (fnc == 000) { /* clock skip !int */ + if (!TST_INTREQ (INT_CLK)) return IOSKIP (dat); + } + else if ((fnc & 007) == 002) { /* mem parity? */ + if (((fnc == 002) && !TST_INT (INT_MPE)) || + ((fnc == 012) && TST_INT (INT_MPE))) + return IOSKIP (dat); + } + else return IOBADFNC (dat); /* invalid fnc */ + break; + + case ioOTA: /* OTA */ + if (fnc == 000) dev_enb = dat; /* SMK */ + else if (fnc == 010) { /* OTK */ + C = (dat >> 15) & 1; /* set C */ + if (cpu_unit.flags & UNIT_HSA) /* HSA included? */ + dp = (dat >> 14) & 1; /* set dp */ + if (cpu_unit.flags & UNIT_EXT) { /* ext opt? */ + if (dat & 020000) { /* ext set? */ + ext = 1; /* yes, set */ + extoff_pending = 0; + } + else extoff_pending = 1; /* no, clr later */ + } + sc = dat & 037; /* set sc */ + } + else return IOBADFNC (dat); + break; + } + +return dat; +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ + +M[M_CLK] = (M[M_CLK] + 1) & DMASK; /* increment mem ctr */ +if (M[M_CLK] == 0) SET_INT (INT_CLK); /* = 0? set flag */ +sim_activate (&clk_unit, sim_rtc_calb (clk_tps)); /* reactivate */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +CLR_INT (INT_CLK); /* clear ready, enb */ +CLR_ENB (INT_CLK); +sim_cancel (&clk_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Set frequency */ + +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val != 50) && (val != 60)) return SCPE_IERR; +clk_tps = val; +return SCPE_OK; +} + +/* Show frequency */ + +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, (clk_tps == 50)? "50Hz": "60Hz"); +return SCPE_OK; +} diff --git a/H316/h316_sys.c b/H316/h316_sys.c new file mode 100644 index 0000000..9995a0b --- /dev/null +++ b/H316/h316_sys.c @@ -0,0 +1,393 @@ +/* h316_sys.c: Honeywell 316/516 simulator interface + + Copyright (c) 1999-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 01-Dec-04 RMS Fixed fprint_opr calling sequence + 24-Oct-03 RMS Added DMA/DMC support + 17-Sep-01 RMS Removed multiconsole support +*/ + +#include "h316_defs.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE tty_dev; +extern DEVICE lpt_dev; +extern DEVICE clk_dev; +extern DEVICE dp_dev; +extern DEVICE fhd_dev; +extern DEVICE mt_dev; +extern REG cpu_reg[]; +extern uint16 M[]; +extern int32 sim_switches; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "H316"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &ptr_dev, + &ptp_dev, + &tty_dev, + &lpt_dev, + &clk_dev, + &dp_dev, + &fhd_dev, + &mt_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "Unimplemented I/O device", + "HALT instruction", + "Breakpoint", + "Indirect address loop", + "DMA error", + "MT write protected", + "DP write overrun, track destroyed", + "DP track format invalid" + }; + +/* Binary loader + + Tbs. +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +return SCPE_FMT; +} + +/* Symbol tables */ + +#define I_V_FL 16 /* flag start */ +#define I_M_FL 07 /* flag mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_MRF 1 /* mem ref */ +#define I_V_MRX 2 /* mem ref, no idx */ +#define I_V_IOT 3 /* I/O */ +#define I_V_SHF 4 /* shift */ +#define I_V_SK0 5 /* skip 0 */ +#define I_V_SK1 6 /* skip 1 */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_MRX (I_V_MRX << I_V_FL) +#define I_IOT (I_V_IOT << I_V_FL) +#define I_SHF (I_V_SHF << I_V_FL) +#define I_SK0 (I_V_SK0 << I_V_FL) +#define I_SK1 (I_V_SK1 << I_V_FL) + +static const int32 masks[] = { + 0177777, 0136000, 0176000, 0176000, + 0177700, 0177000, 0177000 + }; + +static const char *opcode[] = { + "HLT", "SGL", "DBL", + "DXA", "EXA", "RMP", + "SCA", "INK", "NRM", + "IAB", "ENB", "INH", "ERM", + "CHS", "CRA", "SSP", + "RCB", "CSA", "CMA", + "TCA", "SSM", "SCB", + "CAR", "CAL", "ICL", + "AOA", "ACA", "ICR", "ICA", + "NOP", "SKP", "SSR", "SSS", + "JMP", "JMP*", + "LDA", "LDA*", "ANA", "ANA*", + "STA", "STA*", "ERA", "ERA*", + "ADD", "ADD*", "SUB", "SUB*", + "JST", "JST*", "CAS", "CAS*", + "IRS", "IRS*", "IMA", "IMA*", + "MPY", "MPY*", "DIV", "DIV*", + "STX", "STX*", "LDX", "LDX*", + "LRL", "LRS", "LRR", + "LGR", "ARS", "ARR", + "LLL", "LLS", "LLR", + "LGL", "ALS", "ALR", + "OCP", "SKS", "INA", "OTA", + "SMK", + "SPL", "SPN", "SLZ", /* encode only */ + "SZE", "SR1", "SR2", + "SR3", "SR4", "SRC", + "SMI", "SPS", "SLN", + "SNZ", "SS1", "SS2", + "SS3", "SS4", "SSC", + NULL, NULL, /* decode only */ + NULL + }; + +static const int32 opc_val[] = { + 0000000+I_NPN, 0000005+I_NPN, 0000007+I_NPN, + 0000011+I_NPN, 0000013+I_NPN, 0000021+I_NPN, + 0000041+I_NPN, 0000043+I_NPN, 0000101+I_NPN, + 0000201+I_NPN, 0000401+I_NPN, 0001001+I_NPN, 0001401+I_NPN, + 0140024+I_NPN, 0140040+I_NPN, 0140100+I_NPN, + 0140200+I_NPN, 0140320+I_NPN, 0140401+I_NPN, + 0140407+I_NPN, 0140500+I_NPN, 0140600+I_NPN, + 0141044+I_NPN, 0141050+I_NPN, 0141140+I_NPN, + 0141206+I_NPN, 0141216+I_NPN, 0141240+I_NPN, 0141340+I_NPN, + 0101000+I_NPN, 0100000+I_NPN, 0100036+I_NPN, 0101036+I_NPN, + 0002000+I_MRF, 0102000+I_MRF, + 0004000+I_MRF, 0104000+I_MRF, 0006000+I_MRF, 0106000+I_MRF, + 0010000+I_MRF, 0110000+I_MRF, 0012000+I_MRF, 0112000+I_MRF, + 0014000+I_MRF, 0114000+I_MRF, 0016000+I_MRF, 0116000+I_MRF, + 0020000+I_MRF, 0120000+I_MRF, 0022000+I_MRF, 0122000+I_MRF, + 0024000+I_MRF, 0124000+I_MRF, 0026000+I_MRF, 0126000+I_MRF, + 0034000+I_MRF, 0134000+I_MRF, 0036000+I_MRF, 0136000+I_MRF, + 0032000+I_MRX, 0132000+I_MRX, 0072000+I_MRX, 0172000+I_MRX, + 0040000+I_SHF, 0040100+I_SHF, 0040200+I_SHF, + 0040400+I_SHF, 0040500+I_SHF, 0040600+I_SHF, + 0041000+I_SHF, 0041100+I_SHF, 0041200+I_SHF, + 0041400+I_SHF, 0041500+I_SHF, 0041600+I_SHF, + 0030000+I_IOT, 0070000+I_IOT, 0130000+I_IOT, 0170000+I_IOT, + 0170000+I_IOT, + 0100400+I_SK0, 0100200+I_SK0, 0100100+I_SK0, /* encode only */ + 0100040+I_SK0, 0100020+I_SK0, 0100010+I_SK0, + 0100004+I_SK0, 0100002+I_SK0, 0100001+I_SK0, + 0101400+I_SK1, 0101200+I_SK1, 0101100+I_SK1, + 0101040+I_SK1, 0101020+I_SK1, 0101010+I_SK1, + 0101004+I_SK1, 0101002+I_SK1, 0101001+I_SK1, + 0100000+I_SK0, 0101000+I_SK1, /* decode only */ + -1 + }; + +/* Operate decode + + Inputs: + *of = output stream + inst = mask bits + class = instruction class code + sp = space needed? + Outputs: + status = space needed +*/ + +void fprint_opr (FILE *of, int32 inst, int32 class) +{ +int32 i, j, sp; + +for (i = sp = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; + } + } +return; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, inst, disp; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +inst = val[0]; +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* characters? */ + fprintf (of, FMTASC ((inst >> 8) & 0177)); + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_MRF: case I_V_MRX: /* mem ref */ + disp = inst & DISP; /* displacement */ + fprintf (of, "%s ", opcode[i]); /* opcode */ + if (inst & SC) { /* current sector? */ + if (cflag) fprintf (of, "%-o", (addr & PAGENO) | disp); + else fprintf (of, "C %-o", disp); + } + else fprintf (of, "%-o", disp); /* sector zero */ + if ((j == I_V_MRF) && (inst & IDX)) fprintf (of, ",1"); + break; + + case I_V_IOT: /* I/O */ + disp = inst & 01777; /* pulse+dev */ + fprintf (of, "%s %o", opcode[i], disp); + break; + + case I_V_SHF: /* shift */ + disp = -inst & SHFMASK; /* shift count */ + fprintf (of, "%s %o", opcode[i], disp); + break; + + case I_V_SK0: case I_V_SK1: /* skips */ + fprint_opr (of, inst & 0777, j); /* print skips */ + break; + } /* end case */ + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0] & 0177; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* char string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 0177) << 8) | + ((t_value) cptr[1] & 0177); + return SCPE_OK; + } + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & DMASK; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_NPN: /* no operand */ + break; + + case I_V_IOT: /* IOT */ + cptr = get_glyph (cptr, gbuf, 0); /* get pulse+dev */ + d = get_uint (gbuf, 8, 01777, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + break; + + case I_V_SHF: /* shift */ + cptr = get_glyph (cptr, gbuf, 0); /* get shift count */ + d = get_uint (gbuf, 8, SHFMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (-d & SHFMASK); /* store 2's comp */ + break; + + case I_V_MRF: case I_V_MRX: /* mem ref */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + if (k = (strcmp (gbuf, "C") == 0)) { /* C specified? */ + val[0] = val[0] | SC; + cptr = get_glyph (cptr, gbuf, 0); + } + else if (k = (strcmp (gbuf, "Z") == 0)) { /* Z specified? */ + cptr = get_glyph (cptr, gbuf, ','); + } + d = get_uint (gbuf, 8, X_AMASK, &r); /* construe as addr */ + if (r != SCPE_OK) return SCPE_ARG; + if (d <= DISP) val[0] = val[0] | d; /* fits? */ + else if (cflag && !k && (((addr ^ d) & PAGENO) == 0)) + val[0] = val[0] | (d & DISP) | SC; + else return SCPE_ARG; + if ((j == I_V_MRX) || (*cptr == 0)) break; /* indexed? */ + cptr = get_glyph (cptr, gbuf, 0); + d = get_uint (gbuf, 8, 1, &r); /* get tag */ + if (r != SCPE_OK) return SCPE_ARG; + if (d) val[0] = val[0] | IDX; /* or in index */ + break; + + case I_V_SK0: case I_V_SK1: /* skips */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + k = opc_val[i] & DMASK; + if ((opcode[i] == NULL) || (((k ^ val[0]) & 0177000) != 0)) + return SCPE_ARG; + val[0] = val[0] | k; + } + break; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/HP2100/hp2100_baci.c b/HP2100/hp2100_baci.c new file mode 100644 index 0000000..13a7fd5 --- /dev/null +++ b/HP2100/hp2100_baci.c @@ -0,0 +1,1585 @@ +/* hp2100_baci.c: HP 12966A buffered asynchronous communications interface simulator + + Copyright (c) 2007-2008, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + BACI 12966A BACI card + + 13-Jun-08 JDB Cleaned up debug reporting for sim_activate calls + 16-Apr-08 JDB Separated terminal I/O and Telnet poll for idle compatibility + 07-Dec-07 JDB Created BACI device + + References: + - HP 12966A Buffered Asynchronous Data Communications Interface Installation + and Reference Manual (12966-90001, Jul-1982) + - Western Digital Communications Products Handbook (Jun-1984) + + + The 12966A BACI card supplanted the 12531C Teletype and 12880A CRT interfaces + as the primary terminal connection for HP 1000 systems. The main advantage + of this card over the others was its 128-character FIFO memory. While this + allowed more efficient I/O than its interrupt-per-character predecessors, the + most significant advantage was that block input from the 264x-series of CRT + terminals was supported. The 264x were the first HP-supported terminals to + provide local editing and character storage, as well as mass storage via dual + DC-100 minicartridge drives. This support meant that input from the terminal + could come in bursts at the full baud rate, which would overrun the older + cards that needed a small intercharacter handling time. Also, the older + cards placed a substantial load on the CPU in high-baud-rate output + applications. Indeed, block output under RTE on a 1000 M-Series with a + 12880A CRT card would saturate the CPU at about 5700 baud. + + For a while, the BACI and the earlier cards were both supported as the system + console interface, and RTE primary systems were generated with drivers for + both cards. The boot-time I/O reconfigurator would detect the presence of + the BACI card and would dynamically select the correct driver (DVR05 vs. + DVR00). However, the 12880A card faded quickly as the 264x and later 262x + terminals gained in popularity, and support for the 12880A was dropped in + favor of the BACI. This meant that later RTE primary systems could only be + run on CPUs containing a BACI card. + + The simulation supports terminal and diagnostic modes. The latter simulates + the installation of the 12966-60003 diagnostic loopback connector on the + card. + + Fifteen programmable baud rates were supported by the BACI. We simulate + these "realistic" rates by scheduling I/O service based on the appropriate + number of 1000 E-Series instructions for the rate selected. We also provide + an "external rate" that is equivalent to 9600 baud, as most terminals were + set to their maximum speeds. + + We support the 12966A connected to an HP terminal emulator via Telnet. + Internally, we model the BACI as a terminal multiplexer with one line. The + simulation is complicated by the half-duplex nature of the card (there is + only one FIFO, used selectively either for transmission or reception) and the + double-buffered UART (a Western Digital TR1863A), which has holding registers + as well as a shift registers for transmission and reception. We model both + sets of device registers. + + During an output operation, the first character output to the card passes + through the FIFO and into the transmitter holding register. Subsequent + characters remain in the FIFO. If the FIFO is then turned around by a mode + switch from transmission to reception, the second character output becomes + the first character input to the CPU, as the first character output remains + in the THR. Also, the FIFO counter reflects the combined state of the FIFO + and the THR: it is incremented by a "shift in" to the FIFO and decremented by + the "transmit complete" signal from the UART. This has two implications: + + 1. If the FIFO is turned around before the character in the THR is + transmitted, the counter will not decrement when transmission is + complete, so the FIFO will show as "empty" when the counter reads "1". + + 2. The FIFO counter will indicate "half full" and "full" one character + before the FIFO itself reaches those stages. + + The diagnostic hood connects the UART clock to a spare output register. This + allows the diagnostic to supply programmed clock pulses to the UART. The + serial transmit and receive lines from the UART are also available to the + diagnostic. Functional operation is checked by supplying or testing serial + data while clocking the UART sixteen times for each bit. This meant that we + had to model the UART shift registers for faithful hardware simulation. + + The simulation provides both the "realistic timing" described above, as well + as an "optimized (fast) timing" option. Optimization makes three + improvements: + + 1. On output, characters in the FIFO are emptied into the Telnet buffer as a + block, rather than one character per service call, and on input, all of + the characters available in the Telnet buffer are loaded into the FIFO as + a block. + + 2. The ENQ/ACK handshake is done locally, without involving the Telnet + client. + + 3. Input occurring during an output operation is delayed until the second or + third consecutive ENQ/ACK handshake. + + During development, it was noted that a comparatively long time elapsed + (approximately 30 milliseconds on a 3 GHz system) between the transmission of + an ENQ and the reception of the ACK. As the RTE BACI driver, DVR05, does + three ENQ/ACKs at the end of each line, plus an additional ENQ/ACK every 33 + characters within a line, maximum throughput was about ten lines per second. + The source of this delay is not understood but apparently lies within the + terminal emulator, as it was observed with two emulators from two different + companies. Absorbing the ENQ and generating the ACK locally provided a + dramatic improvement in output speed. + + However, as a result, RTE break-mode became effectively impossible, i.e., + striking a key during output no longer produced the break-mode prompt. This + was traced to the RTE driver. DVR05 only checks for an input character + during ENQ/ACK processing, and then only during the second and third + end-of-line handshakes. When the ENQ/ACKs were eliminated, break-mode also + disappeared. + + The workaround is to save a character received during output and supply it + during the second or third consecutive handshake. This ensures that + break-mode is recognized. Because the driver tries to "cheat" the card by + selecting receive mode before the ENQ has actually been transmitted (in order + to save an interrupt), the FIFO counter becomes "off by one" and is reset + with a master clear at the end of each handshake. This would normally clear + the UART receiving register, thereby losing the deferred character. We work + around this by skipping the register clear in "fast timing" mode. +*/ + +#include + +#include "hp2100_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + + +/* Program limits */ + +#define FIFO_SIZE 128 /* read/write buffer size */ + + +/* Character constants */ + +#define ENQ '\005' +#define ACK '\006' + + +/* Unit flags */ + +#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */ +#define UNIT_V_FASTTIME (UNIT_V_UF + 1) /* fast timing mode */ +#define UNIT_V_CAPSLOCK (UNIT_V_UF + 2) /* caps lock mode */ + +#define UNIT_DIAG (1 << UNIT_V_DIAG) +#define UNIT_FASTTIME (1 << UNIT_V_FASTTIME) +#define UNIT_CAPSLOCK (1 << UNIT_V_CAPSLOCK) + + +/* Debug flags */ + +#define DEB_CMDS (1 << 0) /* commands and status */ +#define DEB_CPU (1 << 1) /* CPU I/O */ +#define DEB_BUF (1 << 2) /* buffer gets and puts */ +#define DEB_XFER (1 << 3) /* character reads and writes */ + + +/* Bit flags */ + +#define OUT_MR 0100000 /* common master reset */ + +#define OUT_ENCM 0000040 /* ID1: enable character mode */ +#define OUT_ENCB 0000020 /* ID1: enable CB */ +#define OUT_ENCC 0000010 /* ID1: enable CC */ +#define OUT_ENCE 0000004 /* ID1: enable CE */ +#define OUT_ENCF 0000002 /* ID1: enable CF */ +#define OUT_ENSXX 0000001 /* ID1: enable SBB/SCF */ + +#define OUT_DIAG 0000040 /* ID2: diagnostic output */ +#define OUT_REFCB 0000020 /* ID2: reference CB */ +#define OUT_REFCC 0000010 /* ID2: reference CC */ +#define OUT_REFCE 0000004 /* ID2: reference CE */ +#define OUT_REFCF 0000002 /* ID2: reference CF */ +#define OUT_REFSXX 0000001 /* ID2: reference SBB/SCF */ + +#define OUT_STBITS 0000040 /* ID3: number of stop bits */ +#define OUT_ECHO 0000020 /* ID3: enable echo */ +#define OUT_PARITY 0000010 /* ID3: enable parity */ +#define OUT_PAREVEN 0000004 /* ID3: even parity or odd */ + +#define OUT_XMIT 0000400 /* ID4: transmit or receive */ +#define OUT_CA 0000200 /* ID4: CA on */ +#define OUT_CD 0000100 /* ID4: CD on */ +#define OUT_SXX 0000040 /* ID4: SBA/SCA on */ +#define OUT_DCPC 0000020 /* ID4: DCPC on */ + +#define OUT_CSC 0000040 /* ID5: clear special char interrupt */ +#define OUT_CBH 0000020 /* ID5: clear buffer half-full interrupt */ +#define OUT_CBF 0000010 /* ID5: clear buffer full interrupt */ +#define OUT_CBE 0000004 /* ID5: clear buffer empty interrupt */ +#define OUT_CBRK 0000002 /* ID5: clear break interrupt */ +#define OUT_COVR 0000001 /* ID5: clear overrun/parity interrupt */ + +#define OUT_SPFLAG 0000400 /* ID6: special character */ + +#define OUT_IRQCLR (OUT_CBH | OUT_CBF | OUT_CBE | OUT_CBRK | OUT_COVR) + + +#define IN_VALID 0100000 /* received data: character valid */ +#define IN_SPFLAG 0040000 /* received data: is special character */ + +#define IN_DEVINT 0100000 /* status: device interrupt */ +#define IN_SPCHAR 0040000 /* status: special char has been recd */ +#define IN_SPARE 0010000 /* status: spare receiver state */ +#define IN_TEST 0004000 /* status: unprocessed serial data line */ +#define IN_BUFHALF 0001000 /* status: buffer is half full */ +#define IN_BUFFULL 0000400 /* status: buffer is full */ +#define IN_BUFEMPTY 0000200 /* status: buffer is empty */ +#define IN_BREAK 0000100 /* status: break detected */ +#define IN_OVRUNPE 0000040 /* status: overrun or parity error */ +#define IN_CB 0000020 /* status: CB is on */ +#define IN_CC 0000010 /* status: CC is on */ +#define IN_CE 0000004 /* status: CE is on */ +#define IN_CF 0000002 /* status: CF is on */ +#define IN_SXX 0000001 /* status: SBB/SCF is on */ + +#define IN_MODEM (IN_CB | IN_CC | IN_CE | IN_CF | IN_SXX) +#define IN_LOOPBACK (IN_DEVINT | IN_SPARE | IN_TEST | IN_MODEM) +#define IN_STDIRQ (IN_DEVINT | IN_SPCHAR | IN_BREAK | IN_OVRUNPE) +#define IN_FIFOIRQ (IN_BUFEMPTY | IN_BUFHALF | IN_BUFFULL) + + +/* Packed starting bit numbers */ + +#define OUT_V_ID 12 /* common output word ID */ +#define OUT_V_DATA 0 /* ID 0: output data character */ +#define OUT_V_CHARSIZE 0 /* ID 3: character size */ +#define OUT_V_BAUDRATE 0 /* ID 4: baud rate */ +#define OUT_V_SPCHAR 0 /* ID 6: special character */ + +#define IN_V_CHARCNT 8 /* data: char count in buffer */ +#define IN_V_DATA 0 /* data: input character */ +#define IN_V_IRQCLR 5 /* status: interrupt status clear */ + + +/* Packed bit widths */ + +#define OUT_W_ID 3 +#define OUT_W_DATA 8 +#define OUT_W_CHARSIZE 2 +#define OUT_W_BAUDRATE 4 +#define OUT_W_SPCHAR 8 + +#define IN_W_CHARCNT 6 +#define IN_W_DATA 8 + +/* Packed bit masks */ + +#define OUT_M_ID ((1 << OUT_W_ID) - 1) +#define OUT_M_DATA ((1 << OUT_W_DATA) - 1) +#define OUT_M_CHARSIZE ((1 << OUT_W_CHARSIZE) - 1) +#define OUT_M_BAUDRATE ((1 << OUT_W_BAUDRATE) - 1) +#define OUT_M_SPCHAR ((1 << OUT_W_SPCHAR) - 1) + +#define IN_M_CHARCNT ((1 << IN_W_CHARCNT) - 1) +#define IN_M_DATA ((1 << IN_W_DATA) - 1) + +/* Packed field masks */ + +#define OUT_ID (OUT_M_ID << OUT_V_ID) +#define OUT_DATA (OUT_M_DATA << OUT_V_DATA) +#define OUT_CHARSIZE (OUT_M_CHARSIZE << OUT_V_CHARSIZE) +#define OUT_BAUDRATE (OUT_M_BAUDRATE << OUT_V_BAUDRATE) +#define OUT_SPCHAR (OUT_M_SPCHAR << OUT_V_SPCHAR) + +#define IN_CHARCNT (IN_M_CHARCNT << IN_V_CHARCNT) +#define IN_DATA (IN_M_DATA << IN_V_DATA) + + +/* Command helpers */ + +#define TO_CHARCNT(c) (((c) << IN_V_CHARCNT) & IN_CHARCNT) + +#define GET_ID(i) (((i) & OUT_ID) >> OUT_V_ID) +#define GET_BAUDRATE(b) (((b) & OUT_BAUDRATE) >> OUT_V_BAUDRATE) + +#define IO_MODE (baci_icw & OUT_XMIT) +#define XMIT OUT_XMIT +#define RECV 0 + +#define CLEAR_HR 0 /* UART holding register clear value */ +#define CLEAR_R -1 /* UART register clear value */ + + +/* Unit references */ + +#define baci_term baci_unit[0] /* terminal I/O unit */ +#define baci_poll baci_unit[1] /* Telnet polling unit */ + + +/* External variables */ + +extern uint32 PC; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; +extern FILE *sim_deb; + + +/* BACI state variables */ + +uint16 baci_ibuf = 0; /* status/data in */ +uint16 baci_obuf = 0; /* command/data out */ +uint16 baci_status = 0; /* current status */ + +uint16 baci_edsiw = 0; /* enable device status word */ +uint16 baci_dsrw = 0; /* device status reference word */ +uint16 baci_cfcw = 0; /* character frame control word */ +uint16 baci_icw = 0; /* interface control word */ +uint16 baci_isrw = 0; /* interrupt status reset word */ + +uint32 baci_fput = 0; /* FIFO buffer add index */ +uint32 baci_fget = 0; /* FIFO buffer remove index */ +uint32 baci_fcount = 0; /* FIFO buffer counter */ +uint32 baci_bcount = 0; /* break counter */ + +uint8 baci_fifo [FIFO_SIZE]; /* read/write buffer FIFO */ +uint8 baci_spchar [256]; /* special character RAM */ + +uint16 baci_uart_thr = CLEAR_HR; /* UART transmitter holding register */ +uint16 baci_uart_rhr = CLEAR_HR; /* UART receiver holding register */ + int32 baci_uart_tr = CLEAR_R; /* UART transmitter register */ + int32 baci_uart_rr = CLEAR_R; /* UART receiver register */ +uint32 baci_uart_clk = 0; /* UART transmit/receive clock */ + +t_bool baci_enq_seen = FALSE; /* ENQ seen flag */ +uint32 baci_enq_cntr = 0; /* ENQ seen counter */ + + +/* Terminal multiplexer library interface */ + +TMLN baci_ldsc = { 0 }; /* line descriptor */ +TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */ + + +/* BACI local routines */ + +static int32 service_time (uint32 control_word); +static const char *fmt_char (uint8 ch); +static void update_status (void); +static void master_reset (uint32 selcode); + +static uint32 fifo_get (void); +static void fifo_put (uint8 ch); +static void clock_uart (void); + +/* BACI global routines */ + + int32 baci_io (int32 inst, int32 IR, int32 dat); +t_stat baci_term_svc (UNIT *uptr); +t_stat baci_poll_svc (UNIT *uptr); +t_stat baci_reset (DEVICE *dptr); +t_stat baci_attach (UNIT *uptr, char *cptr); +t_stat baci_detach (UNIT *uptr); +t_stat baci_show (FILE *st, UNIT *uptr, int32 val, void *desc); + + +/* BACI data structures + + baci_dib BACI device information block + baci_dev BACI device descriptor + baci_unit BACI unit list + baci_reg BACI register list + baci_mod BACI modifier list + baci_deb BACI debug list + + Two units are used: one to handle character I/O via the Telnet library, and + another to poll for connections and input. The character I/O service routine + runs only when there are characters to read or write. It operates at the + approximate baud rate of the terminal (in CPU instructions per second) in + order to be compatible with the OS drivers. The Telnet poll must run + continuously, but it can operate much more slowly, as the only requirement is + that it must not present a perceptible lag to human input. To be compatible + with CPU idling, it is co-scheduled with the master poll timer, which uses a + ten millisecond period. +*/ + +DIB baci_dib = { BACI, 0, 0, 0, 0, 0, &baci_io }; + +DEVICE baci_dev; + +UNIT baci_unit[] = + { { UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */ + { UDATA (&baci_poll_svc, UNIT_DIS, POLL_WAIT) } }; /* Telnet poll unit */ + +REG baci_reg[] = { + { ORDATA (IBUF, baci_ibuf, 16) }, + { ORDATA (OBUF, baci_obuf, 16) }, + { ORDATA (STATUS, baci_status, 16) }, + + { ORDATA (EDSIW, baci_edsiw, 16) }, + { ORDATA (DSRW, baci_dsrw, 16) }, + { ORDATA (CFCW, baci_cfcw, 16) }, + { ORDATA (ICW, baci_icw, 16) }, + { ORDATA (ISRW, baci_isrw, 16) }, + + { DRDATA (FIFOPUT, baci_fput, 8) }, + { DRDATA (FIFOGET, baci_fget, 8) }, + { DRDATA (FIFOCNTR, baci_fcount, 8) }, + { DRDATA (BRKCNTR, baci_bcount, 16) }, + + { BRDATA (FIFO, baci_fifo, 8, 8, FIFO_SIZE) }, + { BRDATA (SPCHAR, baci_spchar, 8, 1, 256) }, + + { ORDATA (UARTTHR, baci_uart_thr, 16) }, + { ORDATA (UARTTR, baci_uart_tr, 16), REG_NZ }, + { ORDATA (UARTRHR, baci_uart_rhr, 16) }, + { ORDATA (UARTRR, baci_uart_rr, 16), REG_NZ }, + { DRDATA (UARTCLK, baci_uart_clk, 16) }, + + { DRDATA (CTIME, baci_term.wait, 19), REG_RO }, + + { FLDATA (ENQFLAG, baci_enq_seen, 0), REG_HRO }, + { DRDATA (ENQCNTR, baci_enq_cntr, 16), REG_HRO }, + + { FLDATA (LKO, baci_dib.cmd, 0) }, + { FLDATA (CTL, baci_dib.ctl, 0) }, + { FLDATA (FLG, baci_dib.flg, 0) }, + { FLDATA (FBF, baci_dib.fbf, 0) }, + { FLDATA (SRQ, baci_dib.srq, 0) }, + { ORDATA (DEVNO, baci_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB baci_mod[] = { + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL, NULL, NULL }, + { UNIT_DIAG, 0, "terminal mode", "TERMINAL", NULL, NULL, NULL }, + + { UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL }, + { UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL }, + + { UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL }, + { UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL }, + + { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &baci_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &baci_desc }, + + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTION", NULL, NULL, &baci_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &baci_show, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &baci_desc }, + + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &baci_dev }, + { 0 } + }; + +DEBTAB baci_deb[] = { + { "CMDS", DEB_CMDS }, + { "CPU", DEB_CPU }, + { "BUF", DEB_BUF }, + { "XFER", DEB_XFER }, + { NULL, 0 } + }; + +DEVICE baci_dev = { + "BACI", /* device name */ + baci_unit, /* unit array */ + baci_reg, /* register array */ + baci_mod, /* modifier array */ + 2, /* number of units */ + 10, /* address radix */ + 31, /* address width */ + 1, /* address increment */ + 8, /* data radix */ + 8, /* data width */ + &tmxr_ex, /* examine routine */ + &tmxr_dep, /* deposit routine */ + &baci_reset, /* reset routine */ + NULL, /* boot routine */ + &baci_attach, /* attach routine */ + &baci_detach, /* detach routine */ + &baci_dib, /* device information block */ + DEV_NET | DEV_DEBUG | DEV_DISABLE, /* device flags */ + 0, /* debug control flags */ + baci_deb, /* debug flag name table */ + NULL, /* memory size change routine */ + NULL }; /* logical device name */ + + + +/* I/O instruction processor. + + The BACI processes seven types of output words and supplies two types of + input words. Output word type is identified by an ID code in bits 14-12. + Input word type is determined by the state of the control flip-flop. + + The card has the usual control, flag buffer, flag, and SRQ flip-flops. + However, they have the following unusual characteristics: + + - STC is not required to transfer a character. + - Flag is not set after character transfer completes. + - FLAG and SRQ are decoupled. + + An interrupt lockout flip-flop is used to prevent the generation of multiple + interrupts until the cause of the first interrupt is identified and cleared. + CMD is used to model the lockout flip-flop. +*/ + +int32 baci_io (int32 inst, int32 IR, int32 dat) +{ +uint8 ch; +uint32 mask; +uint32 dev = IR & I_DEVMASK; + +switch (inst) { /* dispatch by instruction */ + + case ioFLG: /* set or clear flag */ + if ((IR & I_HC) == 0) { /* STF? */ + setFSR (dev); /* set flag, flag buffer, and SRQ */ + setCMD (dev); /* set lockout */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: [STF] Flag, SRQ, and lockout set\n", sim_deb); + } + break; /* CLF handled by H/C below */ + + + case ioSFC: /* skip if flag clear */ + if (FLG (dev) == 0) /* flag clear? */ + PC = (PC + 1) & VAMASK; /* skip next instruction */ + break; + + + case ioSFS: /* skip if flag set */ + if (FLG (dev) != 0) /* flag set? */ + PC = (PC + 1) & VAMASK; /* skip next instruction */ + break; + + + case ioCRS: /* control reset */ + master_reset (dev); /* issue master reset */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: [CRS] Master reset\n", sim_deb); + break; + + + case ioOTX: /* output word */ + if (DEBUG_PRI (baci_dev, DEB_CPU)) + fprintf (sim_deb, ">>BACI cpu: [OTx] Command = %06o\n", dat); + + baci_obuf = dat; + + if (baci_obuf & OUT_MR) { /* master reset? */ + master_reset (dev); /* do before processing */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: [OTx] Master reset\n", sim_deb); + } + + switch (GET_ID (baci_obuf)) { /* isolate ID code */ + + case 0: /* transmit data */ + if (IO_MODE == XMIT) { /* transmitting? */ + ch = baci_obuf & OUT_DATA; /* mask to character */ + fifo_put (ch); /* queue character */ + + if (baci_term.flags & UNIT_ATT) { /* attached to network? */ + if (DEBUG_PRI (baci_dev, DEB_CMDS) && /* debugging? */ + (sim_is_active (&baci_term) == 0)) /* service stopped? */ + fprintf (sim_deb, ">>BACI cmds: [OTx] Terminal service scheduled, " + "time = %d\n", baci_term.wait); + + if (baci_fcount == 1) /* first char to xmit? */ + sim_activate_abs (&baci_term, /* start service with full char time */ + baci_term.wait); + else + sim_activate (&baci_term, /* start service if not running */ + baci_term.wait); + } + } + break; + + case 1: /* enable device status interrupt */ + baci_edsiw = baci_obuf; /* load new enable word */ + update_status (); /* may have enabled an interrupt */ + break; + + case 2: /* device status reference */ + if ((baci_term.flags & UNIT_DIAG) && /* diagnostic mode? */ + (baci_dsrw & OUT_DIAG) && /* and last DIAG was high? */ + !(baci_obuf & OUT_DIAG) && /* and new DIAG is low? */ + !(baci_icw & OUT_BAUDRATE)) /* and clock is external? */ + clock_uart (); /* pulse UART clock */ + + baci_dsrw = baci_obuf; /* load new reference word */ + update_status (); /* clocking UART may interrupt */ + break; + + case 3: /* character frame control */ + baci_cfcw = baci_obuf; /* load new frame word */ + break; + + case 4: /* interface control */ + if ((baci_icw ^ baci_obuf) & OUT_BAUDRATE) { /* baud rate change? */ + baci_term.wait = service_time (baci_obuf); /* set service time to match rate */ + + if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */ + if (baci_obuf & OUT_BAUDRATE) { /* internal baud rate requested? */ + sim_activate (&baci_term, /* activate I/O service */ + baci_term.wait); + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fprintf (sim_deb, ">>BACI cmds: [OTx] Terminal service scheduled, " + "time = %d\n", baci_term.wait); + } + + else { /* external rate */ + sim_cancel (&baci_term); /* stop I/O service */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: [OTx] Terminal service stopped\n", sim_deb); + } + } + + baci_icw = baci_obuf; /* load new reference word */ + update_status (); /* loopback may change status */ + break; + + case 5: /* interrupt status reset */ + baci_isrw = baci_obuf; /* load new reset word */ + + mask = (baci_isrw & OUT_IRQCLR) << /* form reset mask */ + IN_V_IRQCLR; /* for common irqs */ + + if (baci_isrw & OUT_CSC) /* add special char mask bit */ + mask = mask | IN_SPCHAR; /* if requested */ + + baci_status = baci_status & ~mask; /* clear specified status bits */ + break; + + case 6: /* special character */ + baci_spchar [baci_obuf & OUT_SPCHAR] = /* set special character entry */ + ((baci_obuf & OUT_SPFLAG) != 0); + break; + } + + break; + + + case ioLIX: /* load word */ + dat = 0; + + case ioMIX: /* merge word */ + if (CTL (dev)) { /* control set? */ + baci_ibuf = TO_CHARCNT (baci_fcount); /* get FIFO count */ + + if (IO_MODE == RECV) /* receiving? */ + baci_ibuf = baci_ibuf | fifo_get (); /* add char and validity flag */ + + dat = dat | baci_ibuf; /* return received data */ + + if (DEBUG_PRI (baci_dev, DEB_CPU)) + fprintf (sim_deb, ">>BACI cpu: [LIx] Received data = %06o\n", dat); + } + + else { /* control clear? */ + dat = dat | baci_status; /* return status */ + + if (DEBUG_PRI (baci_dev, DEB_CPU)) + fprintf (sim_deb, ">>BACI cpu: [LIx] Status = %06o\n", dat); + } + break; + + + case ioCTL: /* control set/clear */ + if (IR & I_CTL) { /* CLC */ + clrCTL (dev); /* clear control */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: [CLC] Control cleared\n", sim_deb); + } + + else { /* STC */ + setCTL (dev); /* set control */ + clrCMD (dev); /* clear lockout */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: [STC] Control set and lockout cleared\n", sim_deb); + + update_status (); /* clearing lockout might interrupt */ + } + break; + + + default: /* all others */ + break; /* do nothing */ + } + + +if (IR & I_HC) { /* H/C option */ + clrFSR (dev); /* clear flag and SRQ */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fprintf (sim_deb, ">>BACI cmds: [%s] Flag and SRQ cleared\n", + (inst == ioFLG ? "CLF" : "x,C")); + + update_status (); /* FLG might set when SRQ clears */ + } + +return dat; +} + + +/* BACI terminal service. + + The terminal service routine is used to transmit and receive characters. + + In terminal mode, it is started when a character is ready for output or when + the Telnet poll routine determines that there are characters ready for input + and stopped when there are no more characters to output or input. When the + terminal is quiescent, this routine does not run. + + In diagnostic mode, it is started whenever an internal baud rate is set and + stopped when the external clock is requested. In this mode, the routine will + be called without an attached socket, so character I/O will be skipped. + + Because there is only one FIFO, the card is half-duplex and must be + configured for transmit or receive mode. The UART, though, is double- + buffered, so it may transmit and receive simultaneously. We implement both + the UART shift and holding registers for each mode. + + If a character is received by the UART while the card is in transmit mode, it + will remain in the receiver holding register (RHR). When the mode is + reversed, the RHR contents will be unloaded into the FIFO. Conversely, + transmit mode enables the output of the FIFO to be unloaded into the + transmitter holding register (THR). Characters received or transmitted pass + through the receiver register (RR) or transmitter register (TR), + respectively. They are not strictly necessary in terminal (Telnet) + transactions but are critical to diagnostic operations. + + The UART signals an overrun if a complete character is received while the RHR + still contains the previous character. The BACI does not use this signal, + though; an overrun is only indicated if the FIFO is full, and another + character is received. + + In "fast timing" mode, we defer the recognition of a received character until + the card is put into receive mode for the second or third consecutive ENQ/ACK + handshake. This improves RTE break-mode recognition. "Realistic timing" + mode behaves as the hardware does: a character present in the RHR is unloaded + into the FIFO as soon as receive mode is set. + + Fast timing mode also enables internal ENQ/ACK handshaking. We allow one + character time for the RTE driver to turn the card around, as otherwise the + ACK may not be seen by the driver. Also, the local ACK is supplied after any + received characters, as the driver detects operator attention only when the + first character after an ENQ is not an ACK. + + Finally, fast timing enables buffer combining. For output, all characters + present in the FIFO are unloaded into the Telnet buffer before initiating a + packet send. For input, all characters present in the Telnet buffer are + loaded into the FIFO. This reduces network traffic and decreases simulator + overhead (there is only one service routine entry per block, rather than one + per character). + + In fast output mode, it is imperative that not less than 1500 instructions + elapse between the first character load to the FIFO and the initiation of + transmission. The RTE driver must have enough time to output the maximum + number of contiguous characters (33) and reset the interrupt status flags + before the service routine is entered. Because all of the characters are + transmitted as a block, the FIFO empty flag will be set by the service + routine. If the driver has not yet exited at that time, the buffer-empty + interrupt will be cleared when the interrupt status reset is done. The + symptom will be a 3.8-second pause in output until the driver times out. + + To avoid this, the OTx output character handler does an absolute schedule for + the first character to ensure that a full character time is used. +*/ + +t_stat baci_term_svc (UNIT *uptr) +{ +uint32 data_bits, data_mask; +const t_bool fast_timing = (baci_term.flags & UNIT_FASTTIME) != 0; +const t_bool is_attached = (baci_term.flags & UNIT_ATT) != 0; +t_stat status = SCPE_OK; +t_bool xmit_loop = TRUE; +t_bool recv_loop = TRUE; + + +/* Transmission */ + +while (xmit_loop && (baci_uart_thr & IN_VALID)) { /* valid character in UART? */ + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + baci_uart_tr = baci_uart_thr & data_mask; /* mask data into transmitter register */ + + if ((baci_uart_tr == ENQ) && fast_timing) { /* char is ENQ and fast timing? */ + baci_enq_seen = TRUE; /* set flag instead of transmitting */ + baci_enq_cntr = baci_enq_cntr + 1; /* bump ENQ counter */ + recv_loop = FALSE; /* skip recv to allow time before ACK */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: Character ENQ absorbed internally, " + "ENQ count = %d\n", baci_enq_cntr); + } + + else { /* character is not an ENQ */ + baci_enq_cntr = 0; /* reset ENQ counter */ + + if (is_attached) { /* attached to network? */ + status = tmxr_putc_ln (&baci_ldsc, /* transmit the character */ + baci_uart_tr); + + if ((status == SCPE_OK) && /* transmitted OK? */ + DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: Character %s " + "transmitted from UART\n", fmt_char (baci_uart_tr)); + } + } + + if (status == SCPE_OK) { /* transmitted OK? */ + baci_uart_tr = CLEAR_R; /* clear transmitter register */ + + if (IO_MODE == XMIT) { /* transmit mode? */ + baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ + baci_uart_thr = fifo_get (); /* get next char into UART */ + update_status (); /* update FIFO status */ + } + + else /* receive mode */ + baci_uart_thr = CLEAR_HR; /* clear holding register */ + + xmit_loop = fast_timing && !baci_enq_seen; /* loop if fast mode and char not ENQ */ + } + + else + xmit_loop = FALSE; + } + + +/* Deferred reception */ + +if (recv_loop && /* ok to process? */ + baci_uart_rhr && (IO_MODE == RECV) && /* and deferred char in RHR in recv mode? */ + (!baci_enq_seen || (baci_enq_cntr >= 2))) { /* and either no ENQ or at least 2nd ENQ? */ + + baci_uart_rhr = baci_uart_rhr & ~IN_VALID; /* clear valid bit */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: Deferred character %s processed\n", + fmt_char ((uint8) baci_uart_rhr)); + + fifo_put ((uint8) baci_uart_rhr); /* move deferred character to FIFO */ + baci_uart_rhr = CLEAR_HR; /* clear RHR */ + update_status (); /* update FIFO status */ + } + + +/* Reception */ + +while (recv_loop && /* OK to process? */ + (baci_uart_rr = tmxr_getc_ln (&baci_ldsc))) { /* and new character available? */ + + if (baci_uart_rr & SCPE_BREAK) /* break detected? */ + baci_status = baci_status | IN_BREAK; /* set break status */ + + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + baci_uart_rhr = baci_uart_rr & data_mask; /* mask data into holding register */ + baci_uart_rr = CLEAR_R; /* clear receiver register */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: Character %s received by UART\n", + fmt_char ((uint8) baci_uart_rhr)); + + if (baci_term.flags & UNIT_CAPSLOCK) /* caps lock mode? */ + baci_uart_rhr = toupper (baci_uart_rhr); /* convert to upper case if lower */ + + if (baci_cfcw & OUT_ECHO) /* echo wanted? */ + tmxr_putc_ln (&baci_ldsc, baci_uart_rhr); /* send it back */ + + if ((IO_MODE == RECV) && !baci_enq_seen) { /* receive mode and not ENQ/ACK? */ + fifo_put ((uint8) baci_uart_rhr); /* put data in FIFO */ + baci_uart_rhr = CLEAR_HR; /* clear RHR */ + update_status (); /* update FIFO status (may set flag) */ + + recv_loop = fast_timing && !FLG (baci_dib.devno); /* loop if fast mode and no IRQ */ + } + + else { /* xmit or ENQ/ACK, leave char in RHR */ + baci_uart_rhr = baci_uart_rhr | IN_VALID; /* set character valid bit */ + recv_loop = FALSE; /* terminate loop */ + } + } + + +/* Housekeeping */ + +if (recv_loop && baci_enq_seen) { /* OK to process and ENQ seen? */ + baci_enq_seen = FALSE; /* reset flag */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fputs (">>BACI xfer: Character ACK generated internally\n", sim_deb); + + fifo_put (ACK); /* fake ACK from terminal */ + update_status (); /* update FIFO status */ + } + +if (is_attached) /* attached to network? */ + tmxr_poll_tx (&baci_desc); /* output any accumulated chars */ + +if ((baci_uart_thr & IN_VALID) || baci_enq_seen || /* more to transmit? */ + tmxr_rqln (&baci_ldsc)) /* or more to receive? */ + sim_activate (uptr, uptr->wait); /* reschedule service */ +else + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: Terminal service stopped\n", sim_deb); + +return status; +} + + +/* BACI Telnet poll service. + + This service routine is used to poll for Telnet connections and incoming + characters. If characters are available, the terminal I/O service routine is + scheduled. It starts when the socket is attached and stops when the socket + is detached. +*/ + +t_stat baci_poll_svc (UNIT *uptr) +{ +if (baci_term.flags & UNIT_ATT) { /* attached to network? */ + if (tmxr_poll_conn (&baci_desc) >= 0) /* new connection established? */ + baci_ldsc.rcve = 1; /* enable line to receive */ + + tmxr_poll_rx (&baci_desc); /* poll for input */ + + if (tmxr_rqln (&baci_ldsc)) /* chars available? */ + sim_activate (&baci_term, baci_term.wait); /* activate I/O service */ + } + +uptr->wait = sync_poll (SERVICE); /* synchronize poll */ +sim_activate (uptr, uptr->wait); /* continue polling */ + +return SCPE_OK; +} + + +/* Simulator reset routine */ + +t_stat baci_reset (DEVICE *dptr) +{ +master_reset (0); /* do master reset */ + +baci_dib.ctl = 0; /* clear control */ +baci_dib.flg = 1; /* set flag */ +baci_dib.fbf = 1; /* set flag buffer */ +baci_dib.srq = 1; /* set SRQ */ +baci_dib.cmd = 1; /* set lockout */ + +baci_ibuf = 0; /* clear input buffer */ +baci_obuf = 0; /* clear output buffer */ + +baci_enq_seen = FALSE; /* reset ENQ seen flag */ +baci_enq_cntr = 0; /* clear ENQ counter */ + +baci_term.wait = service_time (baci_icw); /* set terminal I/O time */ + +if (baci_term.flags & UNIT_ATT) { /* device attached? */ + baci_poll.wait = sync_poll (INITIAL); /* synchronize poll */ + sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll */ + } +else + sim_cancel (&baci_poll); /* else stop Telnet poll */ + +return SCPE_OK; +} + + +/* Attach controller */ + +t_stat baci_attach (UNIT *uptr, char *cptr) +{ +t_stat status = SCPE_OK; + +status = tmxr_attach (&baci_desc, uptr, cptr); /* attach to socket */ + +if (status == SCPE_OK) { + baci_poll.wait = sync_poll (INITIAL); /* synchronize poll */ + sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll */ + } +return status; +} + +/* Detach controller */ + +t_stat baci_detach (UNIT *uptr) +{ +t_stat status; + +status = tmxr_detach (&baci_desc, uptr); /* detach socket */ +baci_ldsc.rcve = 0; /* disable line reception */ +sim_cancel (&baci_poll); /* stop Telnet poll */ +return status; +} + +/* Show connection status and statistics */ + +t_stat baci_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (baci_ldsc.conn) + if (val) + tmxr_fconns (st, &baci_ldsc, -1); + else + tmxr_fstats (st, &baci_ldsc, -1); +else + fprintf (st, "terminal disconnected\n"); + +return SCPE_OK; +} + + +/* Local routines */ + + +/* Master reset. + + This is the programmed card master reset, not the simulator reset routine. + It is called from the simulator reset routine with "selcode" = 0; the latter + routine will take care of setting the card flip-flops appropriately. + + Master reset normally clears the UART registers. However, if we are in "fast + timing" mode, the receiver holding register may hold a deferred character. + In this case, we do not clear the RHR, unless we are called from the + simulator reset routine. + + The HP BACI manual states that master reset "Clears Service Request (SRQ)." + An examination of the schematic, though, shows that it sets SRQ instead. +*/ + +static void master_reset (uint32 selcode) +{ +baci_fput = baci_fget = 0; /* clear FIFO indexes */ +baci_fcount = 0; /* clear FIFO counter */ +memset (baci_fifo, 0, sizeof (baci_fifo)); /* clear FIFO data */ + +baci_uart_thr = CLEAR_HR; /* clear transmitter holding register */ + +if (!(baci_term.flags & UNIT_FASTTIME) || !selcode) /* real time mode or power-on init? */ + baci_uart_rhr = CLEAR_HR; /* clear receiver holding register */ + +baci_uart_tr = CLEAR_R; /* clear transmitter register */ +baci_uart_rr = CLEAR_R; /* clear receiver register */ + +baci_uart_clk = 0; /* clear UART clock */ +baci_bcount = 0; /* clear break counter */ + +if (selcode) { + clrCTL (selcode); /* clear control */ + setFLG (selcode); /* set flag and flag buffer */ + setSRQ (selcode); /* set SRQ */ + setCMD (selcode); /* set lockout flip-flop */ + } + +baci_edsiw = 0; /* clear interrupt enables */ +baci_dsrw = 0; /* clear status reference */ +baci_cfcw = baci_cfcw & ~OUT_ECHO; /* clear echo flag */ +baci_icw = baci_icw & OUT_BAUDRATE; /* clear interface control */ + +if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */ + baci_status = baci_status & ~IN_MODEM | IN_SPARE; /* clear loopback status, set BA */ + +return; +} + + +/* Update status. + + In diagnostic mode, several of the modem output lines are looped back to the + input lines. Also, CD is tied to BB (received data), which is presented on + the TEST status bit via an inversion. Echo mode couples BB to BA + (transmitted data), which is presented on the SPARE status bit. + + If a modem line interrupt condition is present and enabled, the DEVINT status + bit is set. Other potential "standard" interrupt sources are the special + character, break detected, and overrun/parity error bits. If DCPC transfers + are not selected, then the FIFO interrupts (buffer empty, half-full, and + full) and the "data ready" condition (i.e., receive and character modes + enabled and FIFO not empty) also produces an interrupt request. + + An interrupt request will set the card flag unless either the lockout or SRQ + flip-flops are set. SRQ will set if DCPC mode is enabled and there is room + (transmit mode) or data (receive mode) in the FIFO. +*/ + +static void update_status (void) +{ +if (baci_term.flags & UNIT_DIAG) { /* diagnostic mode? */ + baci_status = baci_status & ~IN_LOOPBACK; /* prepare loopback flags */ + + if (baci_icw & OUT_SXX) /* SCA to SCF and CF */ + baci_status = baci_status | IN_SXX | IN_CF; + if ((baci_icw & OUT_CA) && (baci_fcount < 128)) /* CA to CC and CE */ + baci_status = baci_status | IN_CC | IN_CE; + if (baci_icw & OUT_CD) /* CD to CB */ + baci_status = baci_status | IN_CB; + else { + baci_status = baci_status | IN_TEST; /* BB is inversion of CD */ + if (baci_cfcw & OUT_ECHO) + baci_status = baci_status | IN_SPARE; /* BB couples to BA with echo */ + } + + if (!(baci_cfcw & OUT_ECHO) && (baci_uart_tr & 1)) /* no echo and UART TR set? */ + baci_status = baci_status | IN_SPARE; /* BA to SPARE */ + } + +if (baci_edsiw & (baci_status ^ baci_dsrw) & IN_MODEM) /* device interrupt? */ + baci_status = baci_status | IN_DEVINT; /* set flag */ + +if ((baci_status & IN_STDIRQ) || /* standard interrupt? */ + !(baci_icw & OUT_DCPC) && /* or under program control */ + (baci_status & IN_FIFOIRQ) || /* and FIFO interrupt? */ + (IO_MODE == RECV) && /* or receiving */ + (baci_edsiw & OUT_ENCM) && /* and char mode */ + (baci_fget != baci_fput)) { /* and FIFO not empty? */ + + if (CMD (baci_dib.devno)) { /* interrupt lockout? */ + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: Lockout prevents flag set", sim_deb); + } + + else if (SRQ (baci_dib.devno)) { /* SRQ? */ + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: SRQ prevents flag set", sim_deb); + } + + else { + setFLG (baci_dib.devno); /* set device flag and flag buffer */ + setCMD (baci_dib.devno); /* set lockout */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: Flag and lockout set", sim_deb); + } + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fprintf (sim_deb, ", status = %06o\n", baci_status); + } + +if ((baci_icw & OUT_DCPC) && /* DCPC enabled? */ + ((IO_MODE == XMIT) && (baci_fcount < 128) || /* and xmit and room in FIFO */ + (IO_MODE == RECV) && (baci_fcount > 0))) { /* or recv and data in FIFO? */ + + if (CMD (baci_dib.devno)) { /* interrupt lockout? */ + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: Lockout prevents SRQ set", sim_deb); + } + + else { + setSRQ (baci_dib.devno); /* set SRQ */ + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fputs (">>BACI cmds: SRQ set", sim_deb); + } + + if (DEBUG_PRI (baci_dev, DEB_CMDS)) + fprintf (sim_deb, ", status = %06o\n", baci_status); + } + +return; +} + + +/* Calculate service time from baud rate. + + Service times are based on 1580 instructions per second, which is the 1000 + E-Series execution speed. The "external clock" rate uses the 9600 baud rate, + as most real terminals were set to their maximum rate. + + Note that the RTE driver has a race condition that will trip if the service + time is less than 1500 instructions. Therefore, these times cannot be + shortened arbitrarily. +*/ + +static int32 service_time (uint32 control_word) +{ +static const int32 ticks [] = { 1646, 316000, 210667, 143636, 117472, 105333, 52667, 26333, + 17556, 13667, 8778, 6583, 4389, 3292, 2194, 1646 }; + +return ticks [GET_BAUDRATE (control_word)]; /* return service time for indicated rate */ +} + + +/* Format a character into a printable string. + + Control characters are translated to readable strings. Printable characters + retain their original form but are enclosed in single quotes. Characters + outside of the ASCII range are represented as escaped octal values. +*/ + +static const char *fmt_char (uint8 ch) +{ +static const char *const ctl[] = { "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", "BEL", + "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" }; +static char rep[5]; + +if (ch <= '\037') /* ASCII control character? */ + return ctl [ch]; /* return string representation */ + +else if (ch == '\177') /* ASCII delete? */ + return "DEL"; /* return string representation */ + +else if (ch > '\177') { /* beyond printable range? */ + sprintf (rep, "\\%03o", ch); /* format value */ + return rep; /* return escaped octal code */ + } + +else { /* printable character */ + rep[0] = '\''; /* form string */ + rep[1] = ch; /* containing character */ + rep[2] = '\''; + rep[3] = '\0'; + return rep; /* return quoted character */ + } +} + + +/* FIFO manipulation routines. + + The BACI is a half-duplex device that has a single 128-byte FIFO that is used + for both transmitting and receiving. Whether the FIFO is connected to the + input or output of the UART is determined by the XMIT bit in word 4. A + separate 8-bit FIFO up/down counter is used to track the number of bytes + available. FIFO operations are complicated slightly by the UART, which is + double-buffered. + + The FIFO is modeled as a circular 128-byte array. Separate get and put + indexes track the current data extent. A FIFO character counter is used to + derive empty, half-full, and full status indications, and counts greater than + 128 are possible. + + In the transmit mode, an OTA/B with word type 0 generates SI (shift in) to + load the FIFO and increment the FIFO counter. When the UART is ready for a + character, THRE (UART transmitter holding register empty) and OR (FIFO output + ready) generate THRL (transmitter holding register load) and SO (FIFO shift + out) to unload the FIFO into the UART. When transmission of the character + over the serial line is complete, TRE (UART transmitter register empty) + decrements the FIFO counter. + + In the receive mode, the UART sets DR (data received) when has obtained a + character, which generates SI (FIFO shift in) to load the FIFO and increment + the FIFO counter. This also clocks PE (UART parity error) and IR (FIFO input + ready) into the overrun/parity error flip-flop. An LIA/B with control set + and with OR (FIFO output ready) set, indicating valid data is available, + generates SO (FIFO shift out) to unload the FIFO and decrement the FIFO + counter. + + Presuming an empty FIFO and UART, double-buffering in the transmit mode means + that the first byte deposited into the FIFO is removed and loaded into the + UART transmitter holding register. Even though the FIFO is actually empty, + the FIFO counter remains at 1, because FIFO decrement does not occur until + the UART actually transmits the data byte. The intended mode of operation is + to wait until the buffer-empty interrupt occurs, which will happen when the + final character is transmitted from the UART, before switching the BACI into + receive mode. The counter will match the FIFO contents properly, i.e., will + be zero, when the UART transmission completes. + + However, during diagnostic operation, FIFO testing will take this "extra" + count into consideration. For example, after a master reset, if ten bytes + are written to the FIFO in transmit mode, the first byte will pass through to + the UART transmitter holding register, and the next nine bytes will fill the + FIFO. The first byte read in receive mode will be byte 2, not byte 1; the + latter remains in the UART. After the ninth byte is read, OR (FIFO output + ready) will drop, resetting the valid data flip-flop and inhibiting any + further FIFO counter decrement pulses. The counter will remain at 1 until + another master reset is done. + + The same situation occurs in the RTE driver during ENQ/ACK handshakes. The + driver sets the card to transmit mode, sends an ENQ, waits for a short time + for the character to "bubble through" the FIFO and into the UART transmitter + holding register, and then switches the card to receive mode to await the + interrupt from the reception of the ACK. This is done to avoid the overhead + of the interrupt after the ENQ is transmitted. However, switching the card + into receive mode before the ENQ is actually transmitted means that the FIFO + counter will not decrement when that occurs, leaving the counter in an "off + by one" configuration. To remedy this, the driver does a master reset after + the ACK is received. + + Therefore, for proper operation, we must simulate both the UART + double-buffering and the decoupling of the FIFO and FIFO character counter. +*/ + + +/* Get a character from the FIFO. + + In receive mode, getting a character from the FIFO decrements the character + counter concurrently. In transmit mode, the counter must not be decremented + until the character is actually sent; in this latter case, the caller is + responsible for decrementing. Attempting to get a character when the FIFO is + empty returns the last valid data and does not alter the FIFO indexes. + + Because the FIFO counter may indicate more characters than are actually in + the FIFO, the count is not an accurate indicator of FIFO fill status. We + account for this by examining the get and put indexes. If these are equal, + then the FIFO is either empty or exactly full. We differentiate by examining + the FIFO counter and seeing if it is >= 128, indicating an (over)full + condition. If it is < 128, then the FIFO is empty, even if the counter is + not 0. +*/ + +static uint32 fifo_get (void) +{ +uint32 data; + +data = baci_fifo [baci_fget]; /* get character */ + +if ((baci_fget != baci_fput) || (baci_fcount >= 128)) { /* FIFO occupied? */ + if (IO_MODE == RECV) /* receive mode? */ + baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ + + if (DEBUG_PRI (baci_dev, DEB_BUF)) + fprintf (sim_deb, ">>BACI buf: Character %s get from FIFO [%d], " + "character counter = %d\n", fmt_char (data), baci_fget, baci_fcount); + + baci_fget = (baci_fget + 1) % FIFO_SIZE; /* bump index modulo array size */ + + if (baci_spchar [data]) /* is it a special character? */ + data = data | IN_SPFLAG; /* set flag */ + + data = data | IN_VALID; /* set valid flag in return */ + } + +else /* FIFO empty */ + if (DEBUG_PRI (baci_dev, DEB_BUF)) + fprintf (sim_deb, ">>BACI buf: Attempted get on empty FIFO, " + "character count = %d\n", baci_fcount); + +if (baci_fcount == 0) /* count now zero? */ + baci_status = baci_status | IN_BUFEMPTY; /* set buffer empty flag */ + +update_status (); /* update FIFO status */ + +return data; /* return character */ +} + + +/* Put a character into the FIFO. + + In transmit mode, available characters are unloaded from the FIFO into the + UART transmitter holding register as soon as the THR is empty. That is, + given an empty FIFO and THR, a stored character will pass through the FIFO + and into the THR immediately. Otherwise, the character will remain in the + FIFO. In either case, the FIFO character counter is incremented. + + In receive mode, characters are only unloaded from the FIFO explicitly, so + stores always load the FIFO and increment the counter. +*/ + +static void fifo_put (uint8 ch) +{ +uint32 index = 0; +t_bool pass_thru; + +pass_thru = (IO_MODE == XMIT) && /* pass thru if XMIT and THR empty */ + !(baci_uart_thr & IN_VALID); + +if (pass_thru) /* pass char thru to UART */ + baci_uart_thr = ch | IN_VALID; /* and set valid character flag */ + +else { /* RECV or THR occupied */ + index = baci_fput; /* save current index */ + baci_fifo [baci_fput] = ch; /* put char in FIFO */ + baci_fput = (baci_fput + 1) % FIFO_SIZE; /* bump index modulo array size */ + } + +baci_fcount = baci_fcount + 1; /* increment occupancy counter */ + +if (DEBUG_PRI (baci_dev, DEB_BUF)) + if (pass_thru) + fprintf (sim_deb, ">>BACI buf: Character %s put to UART transmitter holding register, " + "character counter = 1\n", fmt_char (ch)); + else + fprintf (sim_deb, ">>BACI buf: Character %s put to FIFO [%d], " + "character counter = %d\n", fmt_char (ch), index, baci_fcount); + +if ((IO_MODE == RECV) && (baci_spchar [ch])) /* receive mode and special character? */ + baci_status = baci_status | IN_SPCHAR; /* set special char seen flag */ + +if (baci_fcount == 64) /* FIFO half full? */ + baci_status = baci_status | IN_BUFHALF; + +else if (baci_fcount == 128) /* FIFO completely full? */ + baci_status = baci_status | IN_BUFFULL; + +else if (baci_fcount > 128) /* FIFO overrun? */ + baci_status = baci_status | IN_OVRUNPE; + +update_status (); /* update FIFO status */ + +return; +} + + +/* Clock the UART. + + In the diagnostic mode, the DIAG output is connected to the EXT CLK input. + If the baud rate of the Interface Control Word is set to "external clock," + then raising and lowering the DIAG output will pulse the UART transmitter and + receiver clock lines, initiating transmission or reception of serial data. + Sixteen pulses are needed to shift one bit through the UART. + + The diagnostic hood ties CD to BB (received data), so bits presented to CD + via the Interface Control Word can be clocked into the UART receiver register + (RR). Similarly, the UART transmitter register (TR) shifts data onto BA + (transmitted data), and the hood ties BA to SPARE, so transmitted bits are + presented to the SPARE bit in the status word. + + "baci_uart_clk" contains the number of clock pulses remaining for the current + character transfer. Calling this routine with "baci_uart_clk" = 0 initiates + a transfer. The value will be a multiple of 16 and will account for the + start bit, the data bits, the optional parity bit, and the stop bits. The + transfer terminates when the count reaches zero (or eight, if 1.5 stop bits + is selected during transmission). + + Every sixteen pulses when the lower four bits of the clock count are zero, + the transmitter or receiver register will be shifted to present or receive a + new serial bit. The registers are initialized to all ones for proper + handling of the stop bits. + + A break counter is maintained and incremented whenever a space (0) condition + is seen on the serial line. After 160 clock times (10 bits) of continuous + zero data, the "break seen" status is set. + + This routine is not used in terminal mode. +*/ + +static void clock_uart (void) +{ +uint32 uart_bits, data_bits, data_mask, parity, bit_low, i; + +if (baci_uart_clk > 0) { /* transfer in progress? */ + bit_low = (baci_icw & OUT_CD); /* get current receive bit */ + + if ((baci_uart_clk & 017) == 0) /* end of a bit? */ + if (IO_MODE == XMIT) /* transmit? */ + baci_uart_tr = baci_uart_tr >> 1; /* shift new bit onto line */ + else /* receive? */ + baci_uart_rr = (baci_uart_rr >> 1) & /* shift new bit in */ + (bit_low ? ~SIGN : -1); /* (inverted sense) */ + + if (bit_low) { /* another low bit? */ + baci_bcount = baci_bcount + 1; /* update break counter */ + + if (baci_bcount == 160) { /* break held long enough? */ + baci_status = baci_status | IN_BREAK; /* set break flag */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fputs (">>BACI xfer: Break detected\n", sim_deb); + } + } + + else /* high bit? */ + baci_bcount = 0; /* reset break counter */ + + baci_uart_clk = baci_uart_clk - 1; /* decrement clocks remaining */ + + if ((IO_MODE == XMIT) && /* transmit mode? */ + ((baci_uart_clk == 0) || /* and end of character? */ + (baci_uart_clk == 8) && /* or last stop bit */ + (baci_cfcw & OUT_STBITS) && /* and extra stop bit requested */ + ((baci_cfcw & OUT_CHARSIZE) == 0))) { /* and 1.5 stop bits used? */ + + baci_uart_clk = 0; /* clear clock count */ + + baci_fcount = baci_fcount - 1; /* decrement occupancy counter */ + baci_uart_thr = fifo_get (); /* get next char into THR */ + update_status (); /* update FIFO status */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: UART transmitter empty, " + "holding register = %06o\n", baci_uart_thr); + } + + else if ((IO_MODE == RECV) && /* receive mode? */ + (baci_uart_clk == 0)) { /* and end of character? */ + + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + + uart_bits = data_bits + /* calculate UART bits as data bits */ + ((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */ + ((baci_cfcw & OUT_STBITS) != 0); /* plus extra stop bit if used */ + + baci_uart_rhr = baci_uart_rr >> (16 - uart_bits); /* position data to right align */ + baci_uart_rr = CLEAR_R; /* clear receiver register */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: UART receiver = %06o (%s)\n", + baci_uart_rhr, fmt_char (baci_uart_rhr & data_mask)); + + fifo_put (baci_uart_rhr & data_mask); /* put data in FIFO */ + update_status (); /* update FIFO status */ + + if (baci_cfcw & OUT_PARITY) { /* parity present? */ + data_mask = data_mask << 1 | 1; /* widen mask to encompass parity */ + uart_bits = baci_uart_rhr & data_mask; /* get data plus parity */ + + parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */ + + for (i = 0; i < data_bits + 1; i++) { /* calc parity of data + parity bit */ + parity = parity ^ uart_bits; /* parity calculated in LSB */ + uart_bits = uart_bits >> 1; + } + + if (parity & 1) { /* parity error? */ + baci_status = baci_status | IN_OVRUNPE; /* report it */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fputs (">>BACI xfer: Parity error detected\n", sim_deb); + } + } + } + } + +if ((baci_uart_clk == 0) && /* start of transfer? */ + ((IO_MODE == RECV) || /* and receive mode */ + (baci_uart_thr & IN_VALID))) { /* or character ready to transmit? */ + + data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */ + + uart_bits = data_bits + /* calculate UART bits as data bits */ + ((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */ + 2 + ((baci_cfcw & OUT_STBITS) != 0); /* plus start/stop and extra stop if used */ + + baci_uart_clk = 16 * uart_bits; /* calculate clocks pulses expected */ + + if (IO_MODE == XMIT) { /* transmit mode? */ + data_mask = (1 << data_bits) - 1; /* generate mask for data bits */ + baci_uart_tr = baci_uart_thr & data_mask; /* mask data into holding register */ + + if (baci_cfcw & OUT_PARITY) { /* add parity to this transmission? */ + uart_bits = baci_uart_tr; /* copy data bits */ + parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */ + + for (i = 0; i < data_bits; i++) { /* calculate parity of data */ + parity = parity ^ uart_bits; /* parity calculated in LSB */ + uart_bits = uart_bits >> 1; + } + + data_mask = data_mask << 1 | 1; /* extend mask for the parity bit */ + baci_uart_tr = baci_uart_tr | /* include parity in transmission register */ + (parity & 1) << data_bits; /* (mask to parity bit and position it) */ + } + + baci_uart_tr = (~data_mask | baci_uart_tr) << 2 | 1; /* form serial data stream */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: UART transmitter = %06o (%s), " + "clock count = %d\n", baci_uart_tr & DMASK, + fmt_char (baci_uart_thr & data_mask), baci_uart_clk); + } + + else { + baci_uart_rr = CLEAR_R; /* clear receiver register */ + + if (DEBUG_PRI (baci_dev, DEB_XFER)) + fprintf (sim_deb, ">>BACI xfer: UART receiver empty, " + "clock count = %d\n", baci_uart_clk); + } + } + +return; +} diff --git a/HP2100/hp2100_cpu.c b/HP2100/hp2100_cpu.c new file mode 100644 index 0000000..9c13904 --- /dev/null +++ b/HP2100/hp2100_cpu.c @@ -0,0 +1,2781 @@ +/* hp2100_cpu.c: HP 21xx CPU simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + CPU 2114C/2115A/2116C/2100A/1000-M/E/F central processing unit + MP 12581A/12892B memory protect + DMA0,DMA1 12607B/12578A/12895A direct memory access controller + DCPC0,DCPC1 12897B dual channel port controller + + 30-Apr-08 JDB Enabled SIGNAL instructions, SIG debug flag + 24-Apr-08 JDB Fixed single stepping through interrupts + 20-Apr-08 JDB Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags + 03-Dec-07 JDB Memory ex/dep and bkpt type default to current map mode + 26-Nov-07 JDB Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA + 15-Nov-07 JDB Corrected MP W5 (JSB) jumper action, SET/SHOW reversal, + mp_mevff clear on interrupt with I/O instruction in trap cell + 04-Nov-07 JDB Removed DBI support from 1000-M (was temporary for RTE-6/VM) + 28-Apr-07 RMS Removed clock initialization + 02-Mar-07 JDB EDT passes input flag and DMA channel in dat parameter + 11-Jan-07 JDB Added 12578A DMA byte packing + 28-Dec-06 JDB CLC 0 now sends CRS instead of CLC to devices + 26-Dec-06 JDB Fixed improper IRQ deferral for 21xx CPUs + Fixed improper interrupt servicing in resolve + 21-Dec-06 JDB Added 21xx loader enable/disable support + 16-Dec-06 JDB Added 2114 and 2115 CPU options. + Added support for 12607B (2114) and 12578A (2115/6) DMA + 01-Dec-06 JDB Added 1000-F CPU option (requires HAVE_INT64) + SHOW CPU displays 1000-M/E instead of 21MX-M/E + 16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c + 12-Oct-06 JDB Fixed INDMAX off-by-one error in resolve + 26-Sep-06 JDB Added iotrap parameter to UIG dispatchers for RTE microcode + 12-Sep-06 JDB iogrp returns NOTE_IOG to recalc interrupts + resolve returns NOTE_INDINT to service held-off interrupt + 16-Aug-06 JDB Added support for future microcode options, future F-Series + 09-Aug-06 JDB Added double integer microcode, 1000-M/E synonyms + Enhanced CPU option validity checking + Added DCPC as a synonym for DMA for 21MX simulations + 26-Dec-05 JDB Improved reporting in dev_conflict + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 21-Jan-05 JDB Reorganized CPU option flags + 15-Jan-05 RMS Split out EAU and MAC instructions + 26-Dec-04 RMS DMA reset doesn't clear alternate CTL flop (from Dave Bryan) + DMA reset shouldn't clear control words (from Dave Bryan) + Alternate CTL flop not visible as register (from Dave Bryan) + Fixed CBS, SBS, TBS to perform virtual reads + Separated A/B from M[0/1] for DMA IO (from Dave Bryan) + Fixed bug in JPY (from Dave Bryan) + 25-Dec-04 JDB Added SET CPU 21MX-M, 21MX-E (21MX defaults to MX-E) + TIMER/EXECUTE/DIAG instructions disabled for 21MX-M + T-register reflects changes in M-register when halted + 25-Sep-04 JDB Moved MP into its own device; added MP option jumpers + Modified DMA to allow disabling + Modified SET CPU 2100/2116 to truncate memory > 32K + Added -F switch to SET CPU to force memory truncation + Fixed S-register behavior on 2116 + Fixed LIx/MIx behavior for DMA on 2116 and 2100 + Fixed LIx/MIx behavior for empty I/O card slots + Modified WRU to be REG_HRO + Added BRK and DEL to save console settings + Fixed use of "unsigned int16" in cpu_reset + Modified memory size routine to return SCPE_INCOMP if + memory size truncation declined + 20-Jul-04 RMS Fixed bug in breakpoint test (reported by Dave Bryan) + Back up PC on instruction errors (from Dave Bryan) + 14-May-04 RMS Fixed bugs and added features from Dave Bryan + - SBT increments B after store + - DMS console map must check dms_enb + - SFS x,C and SFC x,C work + - MP violation clears automatically on interrupt + - SFS/SFC 5 is not gated by protection enabled + - DMS enable does not disable mem prot checks + - DMS status inconsistent at simulator halt + - Examine/deposit are checking wrong addresses + - Physical addresses are 20b not 15b + - Revised DMS to use memory rather than internal format + - Added instruction printout to HALT message + - Added M and T internal registers + - Added N, S, and U breakpoints + Revised IBL facility to conform to microcode + Added DMA EDT I/O pseudo-opcode + Separated DMA SRQ (service request) from FLG + 12-Mar-03 RMS Added logical name support + 02-Feb-03 RMS Fixed last cycle bug in DMA output (found by Mike Gemeny) + 22-Nov-02 RMS Added 21MX IOP support + 24-Oct-02 RMS Fixed bugs in IOP and extended instructions + Fixed bugs in memory protection and DMS + Added clock calibration + 25-Sep-02 RMS Fixed bug in DMS decode (found by Robert Alan Byer) + 26-Jul-02 RMS Restructured extended instructions, added IOP support + 22-Mar-02 RMS Changed to allocate memory array dynamically + 11-Mar-02 RMS Cleaned up setjmp/auto variable interaction + 17-Feb-02 RMS Added DMS support + Fixed bugs in extended instructions + 03-Feb-02 RMS Added terminal multiplexor support + Changed PCQ macro to use unmodified PC + Fixed flop restore logic (found by Bill McDermith) + Fixed SZx,SLx,RSS bug (found by Bill McDermith) + Added floating point support + 16-Jan-02 RMS Added additional device support + 07-Jan-02 RMS Fixed DMA register tables (found by Bill McDermith) + 07-Dec-01 RMS Revised to use breakpoint package + 03-Dec-01 RMS Added extended SET/SHOW support + 10-Aug-01 RMS Removed register in declarations + 26-Nov-00 RMS Fixed bug in dual device number routine + 21-Nov-00 RMS Fixed bug in reset routine + 15-Oct-00 RMS Added dynamic device number support + + References: + - 2100A Computer Reference Manual (02100-90001, Dec-1971) + - Model 2100A Computer Installation and Maintenance Manual + (02100-90002, Aug-1972) + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - 12607A Direct Memory Access Operating and Service Manual + (12607-90002, Jan-1970) + - 12578A/12578A-01 Direct Memory Access Operating and Service Manual + (12578-9001, Mar-1972) + - 12892B Memory Protect Installation Manual (12892-90007, Jun-1978) + + The register state for the HP 2116 CPU is: + + AR<15:0> A register - addressable as location 0 + BR<15:0> B register - addressable as location 1 + PC<14:0> P register (program counter) + SR<15:0> S register + MR<14:0> M register - memory address + TR<15:0> T register - memory data + E extend flag (carry out) + O overflow flag + + The 2100 adds memory protection logic: + + mp_fence<14:0> memory fence register + mp_viol<15:0> memory protection violation register (F register) + + The 21MX adds a pair of index registers and memory expansion logic: + + XR<15:0> X register + YR<15:0> Y register + dms_sr<15:0> dynamic memory system status register + dms_vr<15:0> dynamic memory system violation register + + The original HP 2116 has four instruction formats: memory reference, + shift, alter/skip, and I/O. The HP 2100 added extended memory reference + and extended arithmetic. The HP21MX added extended byte, bit, and word + instructions as well as extended memory. + + The memory reference format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| op |cp| offset | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <14:11> mnemonic action + + 0010 AND A = A & M[MA] + 0011 JSB M[MA] = P, P = MA + 1 + 0100 XOR A = A ^ M[MA] + 0101 JMP P = MA + 0110 IOR A = A | M[MA] + 0111 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 1000 ADA A = A + M[MA] + 1001 ADB B = B + M[MA] + 1010 CPA skip if A != M[MA] + 1011 CPB skip if B != M[MA] + 1100 LDA A = M[MA] + 1101 LDB B = M[MA] + 1110 STA M[MA] = A + 1111 STB M[MA] = B + + <15,10> mode action + + 0,0 page zero direct MA = IR<9:0> + 0,1 current page direct MA = PC<14:0>'IR,9:0> + 1,0 page zero indirect MA = M[IR<9:0>] + 1,1 current page indirect MA = M[PC<14:10>'IR<9:0>] + + Memory reference instructions can access an address space of 32K words. + An instruction can directly reference the first 1024 words of memory + (called page zero), as well as 1024 words of the current page; it can + indirectly access all 32K. + + The shift format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0|ab| 0|s1| op1 |ce|s2|sl| op2 | shift + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | \---+---/ | | | \---+---/ + | | | | | | | + | | | | | | +---- shift 2 opcode + | | | | | +---------- skip if low bit == 0 + | | | | +------------- shift 2 enable + | | | +---------------- clear Extend + | | +---------------------- shift 1 opcode + | +---------------------------- shift 1 enable + +---------------------------------- A/B select + + The alter/skip format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0|ab| 1|regop| e op|se|ss|sl|in|sz|rs| alter/skip + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | \-+-/ \-+-/ | | | | | | + | | | | | | | | +- reverse skip sense + | | | | | | | +---- skip if register == 0 + | | | | | | +------- increment register + | | | | | +---------- skip if low bit == 0 + | | | | +------------- skip if sign bit == 0 + | | | +---------------- skip if Extend == 0 + | | +--------------------- clr/com/set Extend + | +--------------------------- clr/com/set register + +---------------------------------- A/B select + + The I/O transfer format is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 0 0 0|ab| 1|hc| opcode | device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | \---+---/\-------+-------/ + | | | | + | | | +--------- device select + | | +---------------------- opcode + | +---------------------------- hold/clear flag + +---------------------------------- A/B select + + The IO transfer instruction controls the specified device. + Depending on the opcode, the instruction may set or clear + the device flag, start or stop I/O, or read or write data. + + The 2100 added an extended memory reference instruction; + the 21MX added extended arithmetic, operate, byte, word, + and bit instructions. Note that the HP 21xx is, despite + the right-to-left bit numbering, a big endian system. + Bits <15:8> are byte 0, and bits <7:0> are byte 1. + + + The extended memory reference format (HP 2100) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0|op| 0| opcode | extended mem ref + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended arithmetic format (HP 2100) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0 0 0|dr| 0 0| opcode |shift count| extended arithmetic + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended operate format (HP 21MX) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0|op| 0| 1 1 1 1 1| opcode | extended operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended byte and word format (HP 21MX) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0 1 0 1 1 1 1 1 1| opcode | extended byte/word + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0| + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The extended bit operate format (HP 21MX) is: + + 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 0 0 0 1 0 1 1 1 1 1 1 1| opcode | extended bit operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| operand address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + This routine is the instruction decode routine for the HP 2100. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + infinite indirection loop + unimplemented instruction and stop_inst flag set + unknown I/O device and stop_dev flag set + I/O error in I/O simulator + + 2. Interrupts. I/O devices are modelled as five parallel arrays: + + device commands as bit array dev_cmd[2][31..0] + device flags as bit array dev_flg[2][31..0] + device flag buffers as bit array dev_fbf[2][31..0] + device controls as bit array dev_ctl[2][31..0] + device service requests as bit array dev_srq[3][31..0] + + The HP 2100 interrupt structure is based on flag, flag buffer, + and control. If a device flag is set, the flag buffer is set, + the control bit is set, and the device is the highest priority + on the interrupt chain, it requests an interrupt. When the + interrupt is acknowledged, the flag buffer is cleared, preventing + further interrupt requests from that device. The combination of + flag and control set blocks interrupts from lower priority devices. + + Command plays no direct role in interrupts. The command flop + tells whether a device is active. It is set by STC and cleared + by CLC; it is also cleared when the device flag is set. Simple + devices don't need to track command separately from control. + + Service requests are used to trigger the DMA service logic. + + 3. Non-existent memory. On the HP 2100, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against memory size. + + On the 21xx machines, doing SET CPU LOADERDISABLE decreases available + memory size by 64 words. + + 4. Adding I/O devices. These modules must be modified: + + hp2100_defs.h add interrupt request definition + hp2100_sys.c add sim_devices table entry + + 5. Instruction interruptibility. The simulator is fast enough, compared + to the run-time of the longest instructions, for interruptibility not + to matter. But the HP diagnostics explicitly test interruptibility in + EIS and DMS instructions, and long indirect address chains. Accordingly, + the simulator does "just enough" to pass these tests. In particular, if + an interrupt is pending but deferred at the beginning of an interruptible + instruction, the interrupt is taken at the appropriate point; but there + is no testing for new interrupts during execution (that is, the event + timer is not called). + + 6. Interrupt deferral. At instruction fetch time, a pending interrupt + request will be deferred if the previous instruction was a JMP indirect, + JSB indirect, STC, CLC, STF, CLF, or was executing from an interrupt trap + cell. In addition, the following instructions will cause deferral on the + 1000 series: SFS, SFC, JRS, DJP, DJS, SJP, SJS, UJP, and UJS. + + On the HP 1000, the request is always deferred until after the current + instruction completes. On the 21xx, the request is deferred unless the + current instruction is an MRG instruction other than JMP or JMP,I or + JSB,I. Note that for the 21xx, SFS and SFC are not included in the + deferral criteria. +*/ + +#include "hp2100_defs.h" +#include +#include "hp2100_cpu.h" + +/* Memory protect constants */ + +#define UNIT_V_MP_JSB (UNIT_V_UF + 0) /* MP jumper W5 */ +#define UNIT_V_MP_INT (UNIT_V_UF + 1) /* MP jumper W6 */ +#define UNIT_V_MP_SEL1 (UNIT_V_UF + 2) /* MP jumper W7 */ +#define UNIT_MP_JSB (1 << UNIT_V_MP_JSB) /* 1 = W5 is out */ +#define UNIT_MP_INT (1 << UNIT_V_MP_INT) /* 1 = W6 is out */ +#define UNIT_MP_SEL1 (1 << UNIT_V_MP_SEL1) /* 1 = W7 is out */ + +#define ABORT(val) longjmp (save_env, (val)) + +#define DMAR0 1 +#define DMAR1 2 + +#define ALL_BKPTS (SWMASK('E')|SWMASK('N')|SWMASK('S')|SWMASK('U')) +#define ALL_MAPMODES (SWMASK('S')|SWMASK('U')|SWMASK('P')|SWMASK('Q')) + +uint16 *M = NULL; /* memory */ +uint32 saved_AR = 0; /* A register */ +uint32 saved_BR = 0; /* B register */ +uint16 ABREG[2]; /* during execution */ +uint32 PC = 0; /* P register */ +uint32 SR = 0; /* S register */ +uint32 MR = 0; /* M register */ +uint32 saved_MR = 0; /* between executions */ +uint32 TR = 0; /* T register */ +uint32 XR = 0; /* X register */ +uint32 YR = 0; /* Y register */ +uint32 E = 0; /* E register */ +uint32 O = 0; /* O register */ +uint32 dev_cmd[2] = { 0 }; /* device command */ +uint32 dev_ctl[2] = { 0 }; /* device control */ +uint32 dev_flg[2] = { 0 }; /* device flags */ +uint32 dev_fbf[2] = { 0 }; /* device flag bufs */ +uint32 dev_srq[2] = { 0 }; /* device svc reqs */ +struct DMA dmac[2] = { { 0 }, { 0 } }; /* DMA channels */ +uint32 ion = 0; /* interrupt enable */ +uint32 ion_defer = 0; /* interrupt defer */ +uint32 intaddr = 0; /* interrupt addr */ +uint32 mp_fence = 0; /* mem prot fence */ +uint32 mp_viol = 0; /* mem prot viol reg */ +uint32 mp_mevff = 0; /* mem exp (dms) viol */ +uint32 mp_evrff = 1; /* update mp_viol */ +uint32 err_PC = 0; /* error PC */ +uint32 dms_enb = 0; /* dms enable */ +uint32 dms_ump = 0; /* dms user map */ +uint32 dms_sr = 0; /* dms status reg */ +uint32 dms_vr = 0; /* dms violation reg */ +uint16 dms_map[MAP_NUM * MAP_LNT] = { 0 }; /* dms maps */ +uint32 iop_sp = 0; /* iop stack */ +uint32 ind_max = 16; /* iadr nest limit */ +uint32 stop_inst = 1; /* stop on ill inst */ +uint32 stop_dev = 0; /* stop on ill dev */ +uint32 fwanxm = 0; /* first word addr of nx mem */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +uint32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +jmp_buf save_env; /* abort handler */ + +/* Table of CPU features by model. + + Fields: + - typ: standard features plus typically configured options. + - opt: complete list of optional features. + - maxmem: maximum configurable memory in 16-bit words. + + Features in the "typical" list are enabled when the CPU model is selected. + If a feature appears in the "typical" list but NOT in the "optional" list, + then it is standard equipment and cannot be disabled. If a feature appears + in the "optional" list, then it may be enabled or disabled as desired by the + user. +*/ + +struct FEATURE_TABLE { /* CPU model feature table: */ + uint32 typ; /* - typical features */ + uint32 opt; /* - optional features */ + uint32 maxmem; /* - maximum memory */ + }; + +static struct FEATURE_TABLE cpu_features[] = { /* features in UNIT_xxxx order*/ + { UNIT_DMA | UNIT_MP, /* UNIT_2116 */ + UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_EAU, + 32768 }, + { UNIT_DMA, /* UNIT_2115 */ + UNIT_PFAIL | UNIT_DMA | UNIT_EAU, + 8192 }, + { UNIT_DMA, /* UNIT_2114 */ + UNIT_PFAIL | UNIT_DMA, + 16384 }, + { 0, 0, 0 }, + { UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* UNIT_2100 */ + UNIT_DMA | UNIT_FP | UNIT_IOP | UNIT_FFP, + 32768 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { 0, 0, 0 }, + { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_M */ + UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS | + UNIT_IOP | UNIT_FFP | UNIT_DS, + 1048576 }, + { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_E */ + UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS | + UNIT_IOP | UNIT_FFP | UNIT_DBI | UNIT_DS | UNIT_EMA_VMA, + 1048576 }, + { UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | /* UNIT_1000_F */ + UNIT_FFP | UNIT_DBI | UNIT_DMS, + UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_VIS | + UNIT_IOP | UNIT_DS | UNIT_SIGNAL | UNIT_EMA_VMA, + 1048576 } + }; + +extern int32 sim_interval; +extern int32 sim_int_char; +extern int32 sim_brk_char; +extern int32 sim_del_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern FILE *sim_log; +extern DEVICE *sim_devices[]; +extern int32 sim_switches; +extern char halt_msg[]; + +t_stat Ea (uint32 IR, uint32 *addr, uint32 irq); +uint16 ReadIO (uint32 addr, uint32 map); +uint16 ReadPW (uint32 addr); +uint16 ReadTAB (uint32 addr); +void WriteIO (uint32 addr, uint32 dat, uint32 map); +void WritePW (uint32 addr, uint32 dat); +uint32 dms (uint32 va, uint32 map, uint32 prot); +uint32 dms_io (uint32 va, uint32 map); +uint32 shift (uint32 inval, uint32 flag, uint32 oper); +void dma_cycle (uint32 chan, uint32 map); +uint32 calc_dma (void); +uint32 calc_int (void); +uint32 calc_defer (void); +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_boot (int32 unitno, DEVICE *dptr); +t_stat mp_reset (DEVICE *dptr); +t_stat dma0_reset (DEVICE *dptr); +t_stat dma1_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc); +t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc); +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc); +t_stat cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc); +t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc); +t_bool dev_conflict (void); +void hp_post_cmd (t_bool from_scp); + +extern t_stat cpu_eau (uint32 IR, uint32 intrq); +extern t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap); +extern t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap); +extern void (*sim_vm_post) (t_bool from_scp); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list + cpu_deb CPU debug flags +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 0) }; + +REG cpu_reg[] = { + { ORDATA (P, PC, 15) }, + { ORDATA (A, saved_AR, 16) }, + { ORDATA (B, saved_BR, 16) }, + { ORDATA (M, MR, 15) }, + { ORDATA (T, TR, 16), REG_RO }, + { ORDATA (X, XR, 16) }, + { ORDATA (Y, YR, 16) }, + { ORDATA (S, SR, 16) }, + { FLDATA (E, E, 0) }, + { FLDATA (O, O, 0) }, + { FLDATA (ION, ion, 0) }, + { FLDATA (ION_DEFER, ion_defer, 0) }, + { ORDATA (CIR, intaddr, 6) }, + { FLDATA (DMSENB, dms_enb, 0) }, + { FLDATA (DMSCUR, dms_ump, VA_N_PAG) }, + { ORDATA (DMSSR, dms_sr, 16) }, + { ORDATA (DMSVR, dms_vr, 16) }, + { BRDATA (DMSMAP, dms_map, 8, 16, MAP_NUM * MAP_LNT) }, + { ORDATA (IOPSP, iop_sp, 16) }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (STOP_DEV, stop_dev, 1) }, + { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, + { ORDATA (FWANXM, fwanxm, 32), REG_HRO }, + { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { ORDATA (WRU, sim_int_char, 8), REG_HRO }, + { ORDATA (BRK, sim_brk_char, 8), REG_HRO }, + { ORDATA (DEL, sim_del_char, 8), REG_HRO }, + { ORDATA (HCMD, dev_cmd[0], 32), REG_HRO }, + { ORDATA (LCMD, dev_cmd[1], 32), REG_HRO }, + { ORDATA (HCTL, dev_ctl[0], 32), REG_HRO }, + { ORDATA (LCTL, dev_ctl[1], 32), REG_HRO }, + { ORDATA (HFLG, dev_flg[0], 32), REG_HRO }, + { ORDATA (LFLG, dev_flg[1], 32), REG_HRO }, + { ORDATA (HFBF, dev_fbf[0], 32), REG_HRO }, + { ORDATA (LFBF, dev_fbf[1], 32), REG_HRO }, + { ORDATA (HSRQ, dev_srq[0], 32), REG_HRO }, + { ORDATA (LSRQ, dev_srq[1], 32), REG_HRO }, + { NULL } + }; + +/* CPU modifier table. + + The 21MX monikers are deprecated in favor of the 1000 designations. See the + "HP 1000 Series Naming History" on the back inside cover of the Technical + Reference Handbook. */ + +MTAB cpu_mod[] = { + { UNIT_MODEL_MASK, UNIT_2116, "", "2116", &cpu_set_model, &cpu_show_model, "2116" }, + { UNIT_MODEL_MASK, UNIT_2115, "", "2115", &cpu_set_model, &cpu_show_model, "2115" }, + { UNIT_MODEL_MASK, UNIT_2114, "", "2114", &cpu_set_model, &cpu_show_model, "2114" }, + { UNIT_MODEL_MASK, UNIT_2100, "", "2100", &cpu_set_model, &cpu_show_model, "2100" }, + { UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &cpu_set_model, &cpu_show_model, "1000-E" }, + { UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &cpu_set_model, &cpu_show_model, "1000-E" }, + { UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &cpu_set_model, &cpu_show_model, "1000-M" }, + { UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &cpu_set_model, &cpu_show_model, "1000-M" }, + +#if defined (HAVE_INT64) + { UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &cpu_set_model, &cpu_show_model, "1000-F" }, +#endif + + { MTAB_XTD | MTAB_VDV, 1, NULL, "LOADERENABLE", &cpu_set_ldr, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "LOADERDISABLE", &cpu_set_ldr, NULL, NULL }, + + { UNIT_EAU, UNIT_EAU, "EAU", "EAU", &cpu_set_opt, NULL, NULL }, + { UNIT_EAU, 0, "no EAU", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_EAU, NULL, "NOEAU", &cpu_clr_opt, NULL, NULL }, + + { UNIT_FP, UNIT_FP, "FP", "FP", &cpu_set_opt, NULL, NULL }, + { UNIT_FP, 0, "no FP", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_FP, NULL, "NOFP", &cpu_clr_opt, NULL, NULL }, + + { UNIT_IOP, UNIT_IOP, "IOP", "IOP", &cpu_set_opt, NULL, NULL }, + { UNIT_IOP, 0, "no IOP", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_IOP, NULL, "NOIOP", &cpu_clr_opt, NULL, NULL }, + + { UNIT_DMS, UNIT_DMS, "DMS", "DMS", &cpu_set_opt, NULL, NULL }, + { UNIT_DMS, 0, "no DMS", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_DMS, NULL, "NODMS", &cpu_clr_opt, NULL, NULL }, + + { UNIT_FFP, UNIT_FFP, "FFP", "FFP", &cpu_set_opt, NULL, NULL }, + { UNIT_FFP, 0, "no FFP", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_FFP, NULL, "NOFFP", &cpu_clr_opt, NULL, NULL }, + + { UNIT_DBI, UNIT_DBI, "DBI", "DBI", &cpu_set_opt, NULL, NULL }, + { UNIT_DBI, 0, "no DBI", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_DBI, NULL, "NODBI", &cpu_clr_opt, NULL, NULL }, + + { UNIT_EMA_VMA, UNIT_EMA, "EMA", "EMA", &cpu_set_opt, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_EMA, NULL, "NOEMA", &cpu_clr_opt, NULL, NULL }, + + { UNIT_EMA_VMA, UNIT_VMAOS, "VMA", "VMA", &cpu_set_opt, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_VMAOS, NULL, "NOVMA", &cpu_clr_opt, NULL, NULL }, + + { UNIT_EMA_VMA, 0, "no EMA/VMA", NULL, &cpu_set_opt, NULL, NULL }, + +#if defined (HAVE_INT64) + { UNIT_VIS, UNIT_VIS, "VIS", "VIS", &cpu_set_opt, NULL, NULL }, + { UNIT_VIS, 0, "no VIS", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_VIS, NULL, "NOVIS", &cpu_clr_opt, NULL, NULL }, + + { UNIT_SIGNAL, UNIT_SIGNAL,"SIGNAL", "SIGNAL", &cpu_set_opt, NULL, NULL }, + { UNIT_SIGNAL, 0, "no SIGNAL", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_SIGNAL, NULL, "NOSIGNAL", &cpu_clr_opt, NULL, NULL }, +#endif + +/* Future microcode support. + { UNIT_DS, UNIT_DS, "DS", "DS", &cpu_set_opt, NULL, NULL }, + { UNIT_DS, 0, "no DS", NULL, NULL, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, UNIT_DS, NULL, "NODS", &cpu_clr_opt, NULL, NULL }, +*/ + + { MTAB_XTD | MTAB_VDV, 4096, NULL, "4K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 8192, NULL, "8K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 12288, NULL, "12K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 16384, NULL, "16K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 24576, NULL, "24K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 32768, NULL, "32K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 65536, NULL, "64K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 131072, NULL, "128K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 262144, NULL, "256K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 524288, NULL, "512K", &cpu_set_size, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 1048576, NULL, "1024K", &cpu_set_size, NULL, NULL }, + { 0 } + }; + +DEBTAB cpu_deb[] = { + { "OS", DEB_OS }, + { "OSTBG", DEB_OSTBG }, + { "VMA", DEB_VMA }, + { "EMA", DEB_EMA }, + { "VIS", DEB_VIS }, + { "SIG", DEB_SIG }, + { NULL, 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, PA_N_SIZE, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + &cpu_boot, NULL, NULL, + NULL, DEV_DEBUG, + 0, cpu_deb, NULL, NULL + }; + +/* Memory protect data structures + + mp_dev MP device descriptor + mp_unit MP unit descriptor + mp_reg MP register list + mp_mod MP modifiers list +*/ + +UNIT mp_unit = { UDATA (NULL, UNIT_MP_SEL1, 0) }; /* default is JSB in, INT in, SEL1 out */ + +REG mp_reg[] = { + { FLDATA (CTL, dev_ctl[PRO/32], INT_V (PRO)) }, + { FLDATA (FLG, dev_flg[PRO/32], INT_V (PRO)) }, + { FLDATA (FBF, dev_fbf[PRO/32], INT_V (PRO)) }, + { ORDATA (FR, mp_fence, 15) }, + { ORDATA (VR, mp_viol, 16) }, + { FLDATA (MEV, mp_mevff, 0) }, + { FLDATA (EVR, mp_evrff, 0) }, + { NULL } + }; + +MTAB mp_mod[] = { + { UNIT_MP_JSB, UNIT_MP_JSB, "JSB (W5) out", "JSBOUT", NULL }, + { UNIT_MP_JSB, 0, "JSB (W5) in", "JSBIN", NULL }, + { UNIT_MP_INT, UNIT_MP_INT, "INT (W6) out", "INTOUT", NULL }, + { UNIT_MP_INT, 0, "INT (W6) in", "INTIN", NULL }, + { UNIT_MP_SEL1, UNIT_MP_SEL1, "SEL1 (W7) out", "SEL1OUT", NULL }, + { UNIT_MP_SEL1, 0, "SEL1 (W7) in", "SEL1IN", NULL }, + { 0 } + }; + +DEVICE mp_dev = { + "MP", &mp_unit, mp_reg, mp_mod, + 1, 8, 1, 1, 8, 16, + NULL, NULL, &mp_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }; + +/* DMA controller data structures + + dmax_dev DMAx device descriptor + dmax_reg DMAx register list +*/ + +UNIT dma0_unit = { UDATA (NULL, 0, 0) }; + +REG dma0_reg[] = { + { FLDATA (CMD, dev_cmd[DMA0/32], INT_V (DMA0)) }, + { FLDATA (CTL, dev_ctl[DMA0/32], INT_V (DMA0)) }, + { FLDATA (FLG, dev_flg[DMA0/32], INT_V (DMA0)) }, + { FLDATA (FBF, dev_fbf[DMA0/32], INT_V (DMA0)) }, + { FLDATA (CTLALT, dev_ctl[DMALT0/32], INT_V (DMALT0)) }, + { ORDATA (CW1, dmac[0].cw1, 16) }, + { ORDATA (CW2, dmac[0].cw2, 16) }, + { ORDATA (CW3, dmac[0].cw3, 16) }, + { DRDATA (LATENCY, dmac[0].latency, 8) }, + { FLDATA (BYTE, dmac[0].packer, 31) }, + { ORDATA (PACKER, dmac[0].packer, 8) }, + { NULL } + }; + +DEVICE dma0_dev = { + "DMA0", &dma0_unit, dma0_reg, NULL, + 1, 8, 1, 1, 8, 16, + NULL, NULL, &dma0_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE + }; + +UNIT dma1_unit = { UDATA (NULL, 0, 0) }; + +REG dma1_reg[] = { + { FLDATA (CMD, dev_cmd[DMA1/32], INT_V (DMA1)) }, + { FLDATA (CTL, dev_ctl[DMA1/32], INT_V (DMA1)) }, + { FLDATA (FLG, dev_flg[DMA1/32], INT_V (DMA1)) }, + { FLDATA (FBF, dev_fbf[DMA1/32], INT_V (DMA1)) }, + { FLDATA (CTLALT, dev_ctl[DMALT1/32], INT_V (DMALT1)) }, + { ORDATA (CW1, dmac[1].cw1, 16) }, + { ORDATA (CW2, dmac[1].cw2, 16) }, + { ORDATA (CW3, dmac[1].cw3, 16) }, + { DRDATA (LATENCY, dmac[1].latency, 8) }, + { FLDATA (BYTE, dmac[1].packer, 31) }, + { ORDATA (PACKER, dmac[1].packer, 8) }, + { NULL } + }; + +DEVICE dma1_dev = { + "DMA1", &dma1_unit, dma1_reg, NULL, + 1, 8, 1, 1, 8, 16, + NULL, NULL, &dma1_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE + }; + +/* Interrupt defer table (1000 version) */ + +static int32 defer_tab[] = { 0, 1, 1, 1, 0, 0, 0, 1 }; + +/* Device dispatch table */ + +uint32 devdisp (uint32 devno, uint32 inst, uint32 IR, uint32 outdat); +int32 cpuio (int32 op, int32 IR, int32 outdat); +int32 ovfio (int32 op, int32 IR, int32 outdat); +int32 pwrio (int32 op, int32 IR, int32 outdat); +int32 proio (int32 op, int32 IR, int32 outdat); +int32 dmsio (int32 op, int32 IR, int32 outdat); +int32 dmpio (int32 op, int32 IR, int32 outdat); +int32 nulio (int32 op, int32 IR, int32 outdat); + +int32 (*dtab[64])() = { + &cpuio, &ovfio, &dmsio, &dmsio, &pwrio, &proio, &dmpio, &dmpio, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }; + +t_stat sim_instr (void) +{ +uint32 intrq, dmarq; /* set after setjmp */ +uint32 iotrap = 0; /* set after setjmp */ +t_stat reason; /* set after setjmp */ +int32 i, dev; /* temp */ +DEVICE *dptr; /* temp */ +DIB *dibp; /* temp */ +int abortval; + +/* Restore register state */ + +if (dev_conflict ()) return SCPE_STOP; /* check consistency */ +AR = saved_AR & DMASK; /* restore reg */ +BR = saved_BR & DMASK; +err_PC = PC = PC & VAMASK; /* load local PC */ +reason = 0; + +/* Restore I/O state */ + +if (mp_dev.flags & DEV_DIS) dtab[PRO] = NULL; +else dtab[PRO] = &proio; /* set up MP dispatch */ +if (dma0_dev.flags & DEV_DIS) dtab[DMA0] = dtab[DMALT0] = NULL; +else { + dtab[DMA0] = &dmpio; /* set up DMA0 dispatch */ + dtab[DMALT0] = &dmsio; + } +if (dma1_dev.flags & DEV_DIS) dtab[DMA1] = dtab[DMALT1] = NULL; +else { + dtab[DMA1] = &dmpio; /* set up DMA1 dispatch */ + dtab[DMALT1] = &dmsio; + } + +for (i = VARDEV; i <= I_DEVMASK; i++) dtab[i] = NULL; /* clr disp table */ +dev_cmd[0] = dev_cmd[0] & M_FXDEV; /* clear dynamic info */ +dev_ctl[0] = dev_ctl[0] & M_FXDEV; +dev_flg[0] = dev_flg[0] & M_FXDEV; +dev_fbf[0] = dev_fbf[0] & M_FXDEV; +dev_srq[0] = dev_srq[1] = 0; /* init svc requests */ +dev_cmd[1] = dev_ctl[1] = dev_flg[1] = dev_fbf[1] = 0; +for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* exist, enabled? */ + dev = dibp->devno; /* get dev # */ + if (dibp->cmd) { setCMD (dev); } /* restore cmd */ + if (dibp->ctl) { setCTL (dev); } /* restore ctl */ + if (dibp->flg) { setFLG (dev); } /* restore flg */ + clrFBF (dev); /* also sets fbf */ + if (dibp->fbf) { setFBF (dev); } /* restore fbf */ + if (dibp->srq) { setSRQ (dev); } /* restore srq */ + dtab[dev] = dibp->iot; /* set I/O dispatch */ + } + } + +/* Configure interrupt deferral table */ + +if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* 21xx series? */ + defer_tab[ioSFC] = defer_tab[ioSFS] = 0; /* SFC/S doesn't defer */ +else /* 1000 series */ + defer_tab[ioSFC] = defer_tab[ioSFS] = 1; /* SFC/S does defer */ + +/* Abort handling + + If an abort occurs in memory protection, the relocation routine + executes a longjmp to this area OUTSIDE the main simulation loop. + Memory protection errors are the only sources of aborts in the + HP 2100. All referenced variables must be globals, and all sim_instr + scoped automatics must be set after the setjmp. +*/ + +abortval = setjmp (save_env); /* set abort hdlr */ +if (abortval != 0) { /* mem mgt abort? */ + setFLG (PRO); /* req interrupt */ + mp_evrff = 0; /* block mp_viol upd */ + } +dmarq = calc_dma (); /* recalc DMA masks */ +intrq = calc_int (); /* recalc interrupts */ + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + uint32 IR, MA, absel, v1, t, skip; + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + dmarq = calc_dma (); /* recalc DMA reqs */ + intrq = calc_int (); /* recalc interrupts */ + } + + if (dmarq) { + if (dmarq & DMAR0) dma_cycle (0, PAMAP); /* DMA1 cycle? */ + if (dmarq & DMAR1) dma_cycle (1, PBMAP); /* DMA2 cycle? */ + dmarq = calc_dma (); /* recalc DMA reqs */ + intrq = calc_int (); /* recalc interrupts */ + } + + if (intrq && ion_defer) /* interrupt pending but deferred? */ + ion_defer = calc_defer (); /* confirm deferral */ + +/* (From Dave Bryan) + Unlike most other I/O devices, the MP flag flip-flop is cleared + automatically when the interrupt is acknowledged and not by a programmed + instruction (CLF and STF affect the parity error enable FF instead). + Section 4.4.3 "Memory Protect and I/O Interrupt Generation" of the "HP 1000 + M/E/F-Series Computers Engineering and Reference Documentation" (HP + 92851-90001) says: + + "When IAK occurs and IRQ5 is asserted, the FLAGBFF is cleared, FLAGFF + clocked off at next T2, and IRQ5 will no longer occur." +*/ + + if (intrq && ((intrq <= PRO) || !ion_defer)) { /* interrupt request? */ + iotrap = 1; /* I/O trap cell instr */ + clrFBF (intrq); /* clear flag buffer */ + if (intrq == PRO) clrFLG (PRO); /* MP flag follows fbuf */ + intaddr = intrq; /* save int addr */ + if (dms_enb) dms_sr = dms_sr | MST_ENBI; /* dms enabled? */ + else dms_sr = dms_sr & ~MST_ENBI; + if (dms_ump) { /* user map? */ + dms_sr = dms_sr | MST_UMPI; + dms_ump = SMAP; /* switch to system */ + } + else dms_sr = dms_sr & ~MST_UMPI; + IR = ReadW (intrq); /* get dispatch instr */ + ion_defer = 1; /* defer interrupts */ + intrq = 0; /* clear request */ + if (((IR & I_NMRMASK) != I_IO) || /* if not I/O or */ + (I_GETIOOP (IR) == ioHLT)) /* if halt, */ + clrCTL (PRO); /* protection off */ + else /* I/O instr leaves MP on */ + mp_mevff = 0; /* but clears MEV flip-flop */ + } + + else { /* normal instruction */ + iotrap = 0; + err_PC = PC; /* save PC for error */ + if (sim_brk_summ && /* any breakpoints? */ + sim_brk_test (PC, SWMASK ('E') | /* unconditional or */ + (dms_enb? (dms_ump? SWMASK ('U'): SWMASK ('S')): + SWMASK ('N')))) { /* or right type for DMS? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + if (mp_evrff) mp_viol = PC; /* if ok, upd mp_viol */ + IR = ReadW (PC); /* fetch instr */ + PC = (PC + 1) & VAMASK; + ion_defer = 0; + } + + sim_interval = sim_interval - 1; /* count instruction */ + +/* Instruction decode. The 21MX does a 256-way decode on IR<15:8> + + 15 14 13 12 11 10 09 08 instruction + + x <-!= 0-> x x x x memory reference + 0 0 0 0 x 0 x x shift + 0 0 0 0 x 0 x x alter-skip + 1 0 0 0 x 1 x x IO + 1 0 0 0 0 0 x 0 extended arithmetic + 1 0 0 0 0 0 0 1 divide (decoded as 100400) + 1 0 0 0 1 0 0 0 double load (decoded as 104000) + 1 0 0 0 1 0 0 1 double store (decoded as 104400) + 1 0 0 0 1 0 1 0 extended instr group 0 (A/B must be set) + 1 0 0 0 x 0 1 1 extended instr group 1 (A/B ignored) */ + + absel = (IR & I_AB)? 1: 0; /* get A/B select */ + switch ((IR >> 8) & 0377) { /* decode IR<15:8> */ + +/* Memory reference instructions */ + + case 0020:case 0021:case 0022:case 0023: + case 0024:case 0025:case 0026:case 0027: + case 0220:case 0221:case 0222:case 0223: + case 0224:case 0225:case 0226:case 0227: + if (reason = Ea (IR, &MA, intrq)) break; /* AND */ + AR = AR & ReadW (MA); + break; + +/* JSB is a little tricky. It is possible to generate both an MP and a DM + violation simultaneously. Consider a JSB to a location under the MP fence + and on a write-protected page. This situation must be reported as a DM + violation, because it has priority (SFS 5 and SFC 5 check only the MEVFF, + which sets independently of the MP fence violation). + + Under simulation, this means that DM violations must be checked, and the + MEVFF must be set, before an MP abort is taken. This is done for JSB by the + WriteW call to store the return PC. However, WriteW only checks for fence + violations above location 2, as normally JSBs to locations 0 and 1 (i.e., the + A and B register) are allowed. However, if the W5 (JSB) jumper is out, then + JSB 0 and JSB 1 are MP violations as well and must be caught. We do this + with an explicit check before calling WriteW. + + This would seem to violate the above requirement for DM checks before MP + checks. However, a DM abort cannot occur on a write to 0/1, even if logical + page 0 is write-protected, because writes to 0/1 do not attempt to access + memory; they are intercepted and affect the A/B registers instead (micro- + order TAB is used in the Store field), so no MEV signal is generated. +*/ + + case 0230:case 0231:case 0232:case 0233: + case 0234:case 0235:case 0236:case 0237: + ion_defer = 1; /* defer if JSB,I */ + + case 0030:case 0031:case 0032:case 0033: + case 0034:case 0035:case 0036:case 0037: + if (reason = Ea (IR, &MA, intrq)) break; /* JSB */ + + if ((mp_unit.flags & UNIT_MP_JSB) && /* if W5 (JSB) out */ + CTL (PRO) && (MA <= 1)) /* and MP on and JSB 0 or JSB 1 */ + ABORT (ABORT_PRO); /* MP violation */ + + WriteW (MA, PC); /* store PC */ + PCQ_ENTRY; + PC = (MA + 1) & VAMASK; /* jump */ + break; + + case 0040:case 0041:case 0042:case 0043: + case 0044:case 0045:case 0046:case 0047: + case 0240:case 0241:case 0242:case 0243: + case 0244:case 0245:case 0246:case 0247: + if (reason = Ea (IR, &MA, intrq)) break; /* XOR */ + AR = AR ^ ReadW (MA); + break; + + case 0250:case 0251:case 0252:case 0253: + case 0254:case 0255:case 0256:case 0257: + ion_defer = 1; /* defer if JMP,I */ + + case 0050:case 0051:case 0052:case 0053: + case 0054:case 0055:case 0056:case 0057: + if (reason = Ea (IR, &MA, intrq)) break; /* JMP */ + mp_dms_jmp (MA); /* validate jump addr */ + PCQ_ENTRY; + PC = MA; /* jump */ + break; + + case 0060:case 0061:case 0062:case 0063: + case 0064:case 0065:case 0066:case 0067: + case 0260:case 0261:case 0262:case 0263: + case 0264:case 0265:case 0266:case 0267: + if (reason = Ea (IR, &MA, intrq)) break; /* IOR */ + AR = AR | ReadW (MA); + break; + + case 0070:case 0071:case 0072:case 0073: + case 0074:case 0075:case 0076:case 0077: + case 0270:case 0271:case 0272:case 0273: + case 0274:case 0275:case 0276:case 0277: + if (reason = Ea (IR, &MA, intrq)) break; /* ISZ */ + t = (ReadW (MA) + 1) & DMASK; + WriteW (MA, t); + if (t == 0) PC = (PC + 1) & VAMASK; + break; + + case 0100:case 0101:case 0102:case 0103: + case 0104:case 0105:case 0106:case 0107: + case 0300:case 0301:case 0302:case 0303: + case 0304:case 0305:case 0306:case 0307: + if (reason = Ea (IR, &MA, intrq)) break; /* ADA */ + v1 = ReadW (MA); + t = AR + v1; + if (t > DMASK) E = 1; + if (((~AR ^ v1) & (AR ^ t)) & SIGN) O = 1; + AR = t & DMASK; + break; + + case 0110:case 0111:case 0112:case 0113: + case 0114:case 0115:case 0116:case 0117: + case 0310:case 0311:case 0312:case 0313: + case 0314:case 0315:case 0316:case 0317: + if (reason = Ea (IR, &MA, intrq)) break; /* ADB */ + v1 = ReadW (MA); + t = BR + v1; + if (t > DMASK) E = 1; + if (((~BR ^ v1) & (BR ^ t)) & SIGN) O = 1; + BR = t & DMASK; + break; + + case 0120:case 0121:case 0122:case 0123: + case 0124:case 0125:case 0126:case 0127: + case 0320:case 0321:case 0322:case 0323: + case 0324:case 0325:case 0326:case 0327: + if (reason = Ea (IR, &MA, intrq)) break; /* CPA */ + if (AR != ReadW (MA)) PC = (PC + 1) & VAMASK; + break; + + case 0130:case 0131:case 0132:case 0133: + case 0134:case 0135:case 0136:case 0137: + case 0330:case 0331:case 0332:case 0333: + case 0334:case 0335:case 0336:case 0337: + if (reason = Ea (IR, &MA, intrq)) break; /* CPB */ + if (BR != ReadW (MA)) PC = (PC + 1) & VAMASK; + break; + + case 0140:case 0141:case 0142:case 0143: + case 0144:case 0145:case 0146:case 0147: + case 0340:case 0341:case 0342:case 0343: + case 0344:case 0345:case 0346:case 0347: + if (reason = Ea (IR, &MA, intrq)) break; /* LDA */ + AR = ReadW (MA); + break; + + case 0150:case 0151:case 0152:case 0153: + case 0154:case 0155:case 0156:case 0157: + case 0350:case 0351:case 0352:case 0353: + case 0354:case 0355:case 0356:case 0357: + if (reason = Ea (IR, &MA, intrq)) break; /* LDB */ + BR = ReadW (MA); + break; + + case 0160:case 0161:case 0162:case 0163: + case 0164:case 0165:case 0166:case 0167: + case 0360:case 0361:case 0362:case 0363: + case 0364:case 0365:case 0366:case 0367: + if (reason = Ea (IR, &MA, intrq)) break; /* STA */ + WriteW (MA, AR); + break; + + case 0170:case 0171:case 0172:case 0173: + case 0174:case 0175:case 0176:case 0177: + case 0370:case 0371:case 0372:case 0373: + case 0374:case 0375:case 0376:case 0377: + if (reason = Ea (IR, &MA, intrq)) break; /* STB */ + WriteW (MA, BR); + break; + +/* Alter/skip instructions */ + + case 0004:case 0005:case 0006:case 0007: + case 0014:case 0015:case 0016:case 0017: + skip = 0; /* no skip */ + if (IR & 000400) t = 0; /* CLx */ + else t = ABREG[absel]; + if (IR & 001000) t = t ^ DMASK; /* CMx */ + if (IR & 000001) { /* RSS? */ + if ((IR & 000040) && (E != 0)) skip = 1; /* SEZ,RSS */ + if (IR & 000100) E = 0; /* CLE */ + if (IR & 000200) E = E ^ 1; /* CME */ + if (((IR & 000030) == 000030) && /* SSx,SLx,RSS */ + ((t & 0100001) == 0100001)) skip = 1; + if (((IR & 000030) == 000020) && /* SSx,RSS */ + ((t & SIGN) != 0)) skip = 1; + if (((IR & 000030) == 000010) && /* SLx,RSS */ + ((t & 1) != 0)) skip = 1; + if (IR & 000004) { /* INx */ + t = (t + 1) & DMASK; + if (t == 0) E = 1; + if (t == SIGN) O = 1; + } + if ((IR & 000002) && (t != 0)) skip = 1; /* SZx,RSS */ + if ((IR & 000072) == 0) skip = 1; /* RSS */ + } /* end if RSS */ + else { + if ((IR & 000040) && (E == 0)) skip = 1; /* SEZ */ + if (IR & 000100) E = 0; /* CLE */ + if (IR & 000200) E = E ^ 1; /* CME */ + if ((IR & 000020) && /* SSx */ + ((t & SIGN) == 0)) skip = 1; + if ((IR & 000010) && /* SLx */ + ((t & 1) == 0)) skip = 1; + if (IR & 000004) { /* INx */ + t = (t + 1) & DMASK; + if (t == 0) E = 1; + if (t == SIGN) O = 1; + } + if ((IR & 000002) && (t == 0)) skip = 1; /* SZx */ + } /* end if ~RSS */ + ABREG[absel] = t; /* store result */ + PC = (PC + skip) & VAMASK; /* add in skip */ + break; /* end if alter/skip */ + +/* Shift instructions */ + + case 0000:case 0001:case 0002:case 0003: + case 0010:case 0011:case 0012:case 0013: + t = shift (ABREG[absel], IR & 01000, IR >> 6); /* do first shift */ + if (IR & 000040) E = 0; /* CLE */ + if ((IR & 000010) && ((t & 1) == 0)) /* SLx */ + PC = (PC + 1) & VAMASK; + ABREG[absel] = shift (t, IR & 00020, IR); /* do second shift */ + break; /* end if shift */ + +/* I/O instructions */ + + case 0204:case 0205:case 0206:case 0207: + case 0214:case 0215:case 0216:case 0217: + reason = iogrp (IR, iotrap); /* execute instr */ + break; /* end if I/O */ + +/* Extended arithmetic */ + + case 0200: /* EAU group 0 */ + case 0201: /* divide */ + case 0202: /* EAU group 2 */ + case 0210: /* DLD */ + case 0211: /* DST */ + reason = cpu_eau (IR, intrq); /* extended arith */ + break; + +/* Extended instructions */ + + case 0212: /* UIG 0 extension */ + reason = cpu_uig_0 (IR, intrq, iotrap); /* extended opcode */ + break; + + case 0203: /* UIG 1 extension */ + case 0213: + reason = cpu_uig_1 (IR, intrq, iotrap); /* extended opcode */ + break; + } /* end case IR */ + + if (reason == NOTE_IOG) { /* I/O instr exec? */ + dmarq = calc_dma (); /* recalc DMA masks */ + intrq = calc_int (); /* recalc interrupts */ + reason = 0; /* continue */ + } + + else if (reason == NOTE_INDINT) { /* intr pend during indir? */ + PC = err_PC; /* back out of inst */ + reason = 0; /* continue */ + } + } /* end while */ + +/* Simulation halted */ + +saved_AR = AR & DMASK; +saved_BR = BR & DMASK; +if (iotrap && (reason == STOP_HALT)) MR = intaddr; /* HLT in trap cell? */ +else MR = (PC - 1) & VAMASK; /* no, M = P - 1 */ +TR = ReadTAB (MR); /* last word fetched */ +saved_MR = MR; /* save for T cmd update */ +if ((reason == STOP_RSRV) || (reason == STOP_IODV) || /* instr error? */ + (reason == STOP_IND)) PC = err_PC; /* back up PC */ +dms_upd_sr (); /* update dms_sr */ +if (reason == STOP_HALT) /* programmed halt? */ + cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (ignore errors) */ +for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp) { /* exist? */ + dev = dibp->devno; + dibp->cmd = CMD (dev); + dibp->ctl = CTL (dev); + dibp->flg = FLG (dev); + dibp->fbf = FBF (dev); + dibp->srq = SRQ (dev); + } + } +pcq_r->qptr = pcq_p; /* update pc q ptr */ +if (dms_enb) /* default breakpoint type */ + if (dms_ump) sim_brk_dflt = SWMASK ('U'); /* to current map mode */ + else sim_brk_dflt = SWMASK ('S'); +else sim_brk_dflt = SWMASK ('N'); +return reason; +} + +/* Resolve indirect addresses. + + An indirect chain is followed until a direct address is obtained. Under + simulation, a maximum number of indirect levels are allowed (typically 16), + after which the instruction will be aborted. + + If the memory protect feature is present, an indirect counter is used that + allows a pending interrupt to be serviced if more than three levels of + indirection are encountered. If MP jumper W6 ("INT") is out and MP is + enabled, then pending interrupts are serviced immediately. When employing + the indirect counter, the hardware clears a pending interrupt deferral after + the third indirection and aborts the instruction after the fourth. +*/ + +t_stat resolve (uint32 MA, uint32 *addr, uint32 irq) +{ +uint32 i; +t_bool pending = (irq && !(mp_unit.flags & DEV_DIS)); +t_bool int_enable = ((mp_unit.flags & UNIT_MP_INT) && CTL(PRO)); + +for (i = 0; (i < ind_max) && (MA & I_IA); i++) { /* resolve multilevel */ + if (pending) { /* interrupt pending and MP enabled? */ + if ((i == 2) || int_enable) /* 3rd level indirect or INT out? */ + ion_defer = 0; /* reenable interrrupts */ + if ((i > 2) || int_enable) /* 4th or higher or INT out? */ + return NOTE_INDINT; /* break out now */ + } + MA = ReadW (MA & VAMASK); /* follow address chain */ + } +if (MA & I_IA) return STOP_IND; /* indirect loop? */ +*addr = MA; +return SCPE_OK; +} + +/* Get effective address from IR */ + +t_stat Ea (uint32 IR, uint32 *addr, uint32 irq) +{ +uint32 MA; + +MA = IR & (I_IA | I_DISP); /* ind + disp */ +if (IR & I_CP) MA = ((PC - 1) & I_PAGENO) | MA; /* current page? */ +return resolve (MA, addr, irq); /* resolve indirects */ +} + +/* Shift micro operation */ + +uint32 shift (uint32 t, uint32 flag, uint32 op) +{ +uint32 oldE; + +op = op & 07; /* get shift op */ +if (flag) { /* enabled? */ + switch (op) { /* case on operation */ + + case 00: /* signed left shift */ + return ((t & SIGN) | ((t << 1) & 077777)); + + case 01: /* signed right shift */ + return ((t & SIGN) | (t >> 1)); + + case 02: /* rotate left */ + return (((t << 1) | (t >> 15)) & DMASK); + + case 03: /* rotate right */ + return (((t >> 1) | (t << 15)) & DMASK); + + case 04: /* left shift, 0 sign */ + return ((t << 1) & 077777); + + case 05: /* ext right rotate */ + oldE = E; + E = t & 1; + return ((t >> 1) | (oldE << 15)); + + case 06: /* ext left rotate */ + oldE = E; + E = (t >> 15) & 1; + return (((t << 1) | oldE) & DMASK); + + case 07: /* rotate left four */ + return (((t << 4) | (t >> 12)) & DMASK); + } /* end case */ + } /* end if */ +if (op == 05) E = t & 1; /* disabled ext rgt rot */ +if (op == 06) E = (t >> 15) & 1; /* disabled ext lft rot */ +return t; /* input unchanged */ +} + +/* I/O instruction decode. + + If memory protect is enabled, and the instruction is not in a trap cell, then + HLT instructions are illegal and will cause a memory protect violation. If + jumper W7 (SEL1) is in, then all other I/O instructions are legal; if W7 is + out, then only I/O instructions to select code 1 are legal. + + We return NOTE_IOG for normal status instead of SCPE_OK to request that + interrupts be recalculated at the end of the instruction (execution of the + I/O group instructions can change the interrupt priority chain). +*/ + +t_stat iogrp (uint32 ir, uint32 iotrap) +{ +uint32 dev, sop, iodata, iostat, ab; + +ab = (ir & I_AB)? 1: 0; /* get A/B select */ +dev = ir & I_DEVMASK; /* get device */ +sop = I_GETIOOP (ir); /* get subopcode */ +if (!iotrap && CTL (PRO) && /* protected? */ + ((sop == ioHLT) || /* halt or !ovf? */ + ((dev != OVF) && (mp_unit.flags & UNIT_MP_SEL1)))) { /* sel code OK? */ + if (sop == ioLIX) ABREG[ab] = 0; /* A/B writes anyway */ + ABORT (ABORT_PRO); + } +iodata = devdisp (dev, sop, ir, ABREG[ab]); /* process I/O */ +ion_defer = defer_tab[sop]; /* set defer */ +if ((sop == ioMIX) || (sop == ioLIX)) /* store ret data */ + ABREG[ab] = iodata & DMASK; +if (sop == ioHLT) { /* halt? */ + int32 len = strlen (halt_msg); /* find end msg */ + sprintf (&halt_msg[len - 6], "%06o", ir); /* add the halt */ + return STOP_HALT; + } +iostat = iodata >> IOT_V_REASON; +if (iostat == SCPE_OK) return NOTE_IOG; /* normal status */ +else return iostat; /* abnormal status */ +} + +/* Device dispatch */ + +uint32 devdisp (uint32 devno, uint32 inst, uint32 IR, uint32 dat) +{ +if (dtab[devno]) return dtab[devno] (inst, IR, dat); +else return nulio (inst, IR, dat); +} + +/* Calculate DMA requests */ + +uint32 calc_dma (void) +{ +uint32 r = 0; + +if (CMD (DMA0) && SRQ (dmac[0].cw1 & I_DEVMASK)) /* check DMA0 cycle */ + r = r | DMAR0; +if (CMD (DMA1) && SRQ (dmac[1].cw1 & I_DEVMASK)) /* check DMA1 cycle */ + r = r | DMAR1; +return r; +} + +/* Determine whether a pending interrupt deferral should be inhibited. + + Execution of certain instructions generally causes a pending interrupt to be + deferred until the succeeding instruction completes. However, the interrupt + deferral rules differ on the 21xx vs. the 1000. + + The 1000 always defers until the completion of the instruction following a + deferring instruction. The 21xx defers unless the following instruction is + an MRG instruction other than JMP or JMP,I or JSB,I. If it is, then the + deferral is inhibited, i.e., the pending interrupt will be serviced. + + See the "Set Phase Logic Flowchart," transition from phase 1A to phase 1B, + and the "Theory of Operation," "Control Section Detailed Theory," "Phase + Control Logic," "Phase 1B" paragraph in the Model 2100A Computer Installation + and Maintenance Manual for details. +*/ + +uint32 calc_defer (void) +{ +uint16 IR; + +if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx series? */ + IR = ReadW (PC); /* prefetch next instr */ + + if (((IR & I_MRG & ~I_AB) != 0000000) && /* is MRG instruction? */ + ((IR & I_MRG_I) != I_JSB_I) && /* but not JSB,I? */ + ((IR & I_MRG) != I_JMP)) /* and not JMP or JMP,I? */ + return 0; /* yes, so inhibit deferral */ + else + return 1; /* no, so allow deferral */ + } +else + return 1; /* 1000 always allows deferral */ +} + +/* Calculate interrupt requests + + This routine takes into account all the relevant state of the + interrupt system: ion, dev_flg, dev_fbf, and dev_ctl. + + 1. dev_flg & dev_ctl determines the end of the priority grant. + The break in the chain will occur at the first device for + which dev_flg & dev_ctl is true. This is determined by + AND'ing the set bits with their 2's complement; only the low + order (highest priority) bit will differ. 1 less than + that, or'd with the single set bit itself, is the mask of + possible interrupting devices. If ION is clear, only devices + 4 and 5 are eligible to interrupt. + 2. dev_flg & dev_ctl & dev_fbf determines the outstanding + interrupt requests. All three bits must be on for a device + to request an interrupt. This is the masked under the + result from #1 to determine the highest priority interrupt, + if any. +*/ + +uint32 calc_int (void) +{ +int32 j, lomask, mask[2], req[2]; + +lomask = dev_flg[0] & dev_ctl[0] & ~M_NXDEV; /* start chain calc */ +req[0] = lomask & dev_fbf[0]; /* calc requests */ +lomask = lomask & (-lomask); /* chain & -chain */ +mask[0] = lomask | (lomask - 1); /* enabled devices */ +req[0] = req[0] & mask[0]; /* highest request */ +if (ion) { /* ion? */ + if (lomask == 0) { /* no break in chn? */ + mask[1] = dev_flg[1] & dev_ctl[1]; /* do all devices */ + req[1] = mask[1] & dev_fbf[1]; + mask[1] = mask[1] & (-mask[1]); + mask[1] = mask[1] | (mask[1] - 1); + req[1] = req[1] & mask[1]; + } + else req[1] = 0; + } +else { + req[0] = req[0] & (INT_M (PWR) | INT_M (PRO)); + req[1] = 0; + } +if (req[0]) { /* if low request */ + for (j = 0; j < 32; j++) { /* find dev # */ + if (req[0] & INT_M (j)) return j; + } + } +if (req[1]) { /* if hi request */ + for (j = 0; j < 32; j++) { /* find dev # */ + if (req[1] & INT_M (j)) return (32 + j); + } + } +return 0; +} + +/* Memory access routines */ + +uint8 ReadB (uint32 va) +{ +int32 pa; + +if (dms_enb) pa = dms (va >> 1, dms_ump, RD); +else pa = va >> 1; +if (va & 1) return (ReadPW (pa) & 0377); +else return ((ReadPW (pa) >> 8) & 0377); +} + +uint8 ReadBA (uint32 va) +{ +uint32 pa; + +if (dms_enb) pa = dms (va >> 1, dms_ump ^ MAP_LNT, RD); +else pa = va >> 1; +if (va & 1) return (ReadPW (pa) & 0377); +else return ((ReadPW (pa) >> 8) & 0377); +} + +uint16 ReadW (uint32 va) +{ +uint32 pa; + +if (dms_enb) pa = dms (va, dms_ump, RD); +else pa = va; +return ReadPW (pa); +} + +uint16 ReadWA (uint32 va) +{ +uint32 pa; + +if (dms_enb) pa = dms (va, dms_ump ^ MAP_LNT, RD); +else pa = va; +return ReadPW (pa); +} + +uint16 ReadIO (uint32 va, uint32 map) +{ +uint32 pa; + +if (dms_enb) pa = dms_io (va, map); +else pa = va; +return M[pa]; +} + +uint16 ReadPW (uint32 pa) +{ +if (pa <= 1) return ABREG[pa]; +return M[pa]; +} + +uint16 ReadTAB (uint32 addr) +{ +if (addr == 0) return saved_AR; +else if (addr == 1) return saved_BR; +else return ReadIO (addr, dms_ump); +} + +/* Memory protection test for writes + + From Dave Bryan: The problem is that memory writes aren't being checked for + an MP violation if DMS is enabled, i.e., if DMS is enabled, and the page is + writable, then whether the target is below the MP fence is not checked. [The + simulator must] do MP check on all writes after DMS translation and violation + checks are done (so, to pass, the page must be writable AND the target must + be above the MP fence). +*/ + +#define MP_TEST(x) (CTL (PRO) && ((x) > 1) && ((x) < mp_fence)) + +void WriteB (uint32 va, uint32 dat) +{ +uint32 pa, t; + +if (dms_enb) pa = dms (va >> 1, dms_ump, WR); +else pa = va >> 1; +if (MP_TEST (va >> 1)) ABORT (ABORT_PRO); +if (MEM_ADDR_OK (pa)) { + t = ReadPW (pa); + if (va & 1) t = (t & 0177400) | (dat & 0377); + else t = (t & 0377) | ((dat & 0377) << 8); + WritePW (pa, t); + } +return; +} + +void WriteBA (uint32 va, uint32 dat) +{ +uint32 pa, t; + +if (dms_enb) { + dms_viol (va >> 1, MVI_WPR); /* viol if prot */ + pa = dms (va >> 1, dms_ump ^ MAP_LNT, WR); + } +else pa = va >> 1; +if (MP_TEST (va >> 1)) ABORT (ABORT_PRO); +if (MEM_ADDR_OK (pa)) { + t = ReadPW (pa); + if (va & 1) t = (t & 0177400) | (dat & 0377); + else t = (t & 0377) | ((dat & 0377) << 8); + WritePW (pa, t); + } +return; +} + +void WriteW (uint32 va, uint32 dat) +{ +uint32 pa; + +if (dms_enb) pa = dms (va, dms_ump, WR); +else pa = va; +if (MP_TEST (va)) ABORT (ABORT_PRO); +if (MEM_ADDR_OK (pa)) WritePW (pa, dat); +return; +} + +void WriteWA (uint32 va, uint32 dat) +{ +int32 pa; + +if (dms_enb) { + dms_viol (va, MVI_WPR); /* viol if prot */ + pa = dms (va, dms_ump ^ MAP_LNT, WR); + } +else pa = va; +if (MP_TEST (va)) ABORT (ABORT_PRO); +if (MEM_ADDR_OK (pa)) WritePW (pa, dat); +return; +} + +void WriteIO (uint32 va, uint32 dat, uint32 map) +{ +uint32 pa; + +if (dms_enb) pa = dms_io (va, map); +else pa = va; +if (MEM_ADDR_OK (pa)) M[pa] = dat & DMASK; +return; +} + +void WritePW (uint32 pa, uint32 dat) +{ +if (pa <= 1) ABREG[pa] = dat & DMASK; +else M[pa] = dat & DMASK; +return; +} + +/* DMS relocation for CPU access */ + +uint32 dms (uint32 va, uint32 map, uint32 prot) +{ +uint32 pgn, mpr; + +if (va <= 1) return va; /* A, B */ +pgn = VA_GETPAG (va); /* get page num */ +if (pgn == 0) { /* base page? */ + uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */ + if ((dms_sr & MST_FLT)? /* check unmapped */ + (va >= dms_fence): /* 1B10: >= fence */ + (va < dms_fence)) { /* 0B10: < fence */ + if (prot == WR) dms_viol (va, MVI_BPG); /* if W, viol */ + return va; /* no mapping */ + } + } +mpr = dms_map[map + pgn]; /* get map reg */ +if (mpr & prot) dms_viol (va, prot); /* prot violation? */ +return (MAP_GETPAG (mpr) | VA_GETOFF (va)); +} + +/* DMS relocation for IO access */ + +uint32 dms_io (uint32 va, uint32 map) +{ +uint32 pgn, mpr; + +if (va <= 1) return va; /* A, B */ +pgn = VA_GETPAG (va); /* get page num */ +if (pgn == 0) { /* base page? */ + uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */ + if ((dms_sr & MST_FLT)? /* check unmapped */ + (va >= dms_fence): /* 1B10: >= fence */ + (va < dms_fence)) { /* 0B10: < fence */ + return va; /* no mapping */ + } + } +mpr = dms_map[map + pgn]; /* get map reg */ +return (MAP_GETPAG (mpr) | VA_GETOFF (va)); +} + +/* DMS relocation for console access */ + +uint32 dms_cons (uint32 va, int32 sw) +{ +uint32 map_sel; + +if ((dms_enb == 0) || /* DMS off? */ + (sw & (SWMASK ('N') | SIM_SW_REST))) /* no mapping rqst or save/rest? */ + return va; /* use physical address */ +else if (sw & SWMASK ('S')) map_sel = SMAP; +else if (sw & SWMASK ('U')) map_sel = UMAP; +else if (sw & SWMASK ('P')) map_sel = PAMAP; +else if (sw & SWMASK ('Q')) map_sel = PBMAP; +else map_sel = dms_ump; /* dflt to log addr, cur map */ +if (va >= VASIZE) return MEMSIZE; /* virtual, must be 15b */ +else if (dms_enb) return dms_io (va, map_sel); /* DMS on? go thru map */ +else return va; /* else return virtual */ +} + +/* Mem protect and DMS validation for jumps */ + +void mp_dms_jmp (uint32 va) +{ +uint32 pgn = VA_GETPAG (va); /* get page num */ + +if ((pgn == 0) && (va > 1)) { /* base page? */ + uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */ + if ((dms_sr & MST_FLT)? /* check unmapped */ + (va >= dms_fence): /* 1B10: >= fence */ + (va < dms_fence)) { /* 0B10: < fence */ + dms_viol (va, MVI_BPG); /* if W, viol */ + return; /* PRO not set */ + } + } +if (CTL (PRO) && (va < mp_fence)) ABORT (ABORT_PRO); /* base page MPR */ +return; +} + +/* DMS read and write maps */ + +uint16 dms_rmap (uint32 mapi) +{ +mapi = mapi & MAP_MASK; +return (dms_map[mapi] & ~MAP_MBZ); +} + +void dms_wmap (uint32 mapi, uint32 dat) +{ +mapi = mapi & MAP_MASK; +dms_map[mapi] = (uint16) (dat & ~MAP_MBZ); +return; +} + +/* DMS violation */ + +void dms_viol (uint32 va, uint32 st) +{ +dms_vr = st | VA_GETPAG (va) | + ((st & (MVI_RPR | MVI_WPR))? MVI_MEB: 0) | /* set MEB */ + (dms_enb? MVI_MEM: 0) | /* set MEM */ + (dms_ump? MVI_UMP: 0); /* set UMAP */ +if (CTL (PRO)) { /* protected? */ + mp_mevff = 1; /* signal dms */ + ABORT (ABORT_PRO); /* abort */ + } +return; +} + +/* DMS update status */ + +uint32 dms_upd_sr (void) +{ +dms_sr = dms_sr & ~(MST_ENB | MST_UMP | MST_PRO); +if (dms_enb) dms_sr = dms_sr | MST_ENB; +if (dms_ump) dms_sr = dms_sr | MST_UMP; +if (CTL (PRO)) dms_sr = dms_sr | MST_PRO; +return dms_sr; +} + +/* Device 0 (CPU) I/O routine + + NOTE: LIx/MIx reads floating I/O bus (0 on all machines). + + NOTE: CLC 0 issues CRS to all devices, not CLC. While most cards react + identically to CRS and CLC, some do not, e.g., the 12566B when used as an + I/O diagnostic target. PRESET also issues CRS (with POPIO). + + From Dave Bryan: RTE uses the undocumented instruction "SFS 0,C" to both test + and turn off the interrupt system. This is confirmed in the "RTE-6/VM + Technical Specifications" manual (HP 92084-90015), section 2.3.1 "Process + the Interrupt", subsection "A.1 $CIC": + + "Test to see if the interrupt system is on or off. This is done with the + SFS 0,C instruction. In either case, turn it off (the ,C does it)." + + ...and in section 5.8, "Parity Error Detection": + + "Because parity error interrupts can occur even when the interrupt system + is off, the code at $CIC must be able to save the complete system status. + The major hole in being able to save the complete state is in saving the + interrupt system state. In order to do this in both the 21MX and the 21XE + the instruction 103300 was used to both test the interrupt system and + turn it off." +*/ + +int32 cpuio (int32 inst, int32 IR, int32 dat) +{ +int i; + +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag */ + ion = (IR & I_HC)? 0: 1; /* interrupts off/on */ + return dat; + + case ioSFC: /* skip flag clear */ + if (!ion) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (ion) PC = (PC + 1) & VAMASK; + break; + + case ioLIX: /* load */ + dat = 0; /* returns 0 */ + break; + + case ioCTL: /* control */ + if (IR & I_CTL) /* CLC 0 sends CRS */ + for (i = 0; i <= I_DEVMASK; i++) /* to all devices */ + devdisp (i, ioCRS, I_CTL + i, 0); /* IR -> "CLC i" for convenience */ + break; + + default: + break; + } + +if (IR & I_HC) ion = 0; /* HC option */ +return dat; +} + +/* Device 1 (overflow/S-register) I/O routine + + NOTE: The S register is read-only on the 2115/2116. It is read/write on + the 2114, 2100, and 1000. +*/ + +int32 ovfio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag */ + O = (IR & I_HC)? 0: 1; /* clear/set overflow */ + return dat; + + case ioSFC: /* skip flag clear */ + if (!O) PC = (PC + 1) & VAMASK; + break; /* can clear flag */ + + case ioSFS: /* skip flag set */ + if (O) PC = (PC + 1) & VAMASK; + break; /* can clear flag */ + + case ioMIX: /* merge */ + dat = dat | SR; + break; + + case ioLIX: /* load */ + dat = SR; + break; + + case ioOTX: /* output */ + if ((UNIT_CPU_MODEL != UNIT_2116) && + (UNIT_CPU_MODEL != UNIT_2115)) + SR = dat; + break; + + default: + break; + } + +if (IR & I_HC) O = 0; /* HC option */ +return dat; +} + +/* Device 4 (power fail) I/O routine */ + +int32 pwrio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ + + case ioMIX: /* merge */ + dat = dat | intaddr; + break; + + case ioLIX: /* load */ + dat = intaddr; + break; + + default: + break; + } + +return dat; +} + +/* Device 5 (memory protect) I/O routine + + From Dave Bryan: Examination of the schematics for the MP card in the + engineering documentation shows that the SFS and SFC I/O backplane signals + gate the output of the MEVFF onto the SKF line unconditionally. + + The MEVFF records memory expansion (a.k.a. dynamic mapping) violations. It + is set when an DM violation is encountered. It is cleared on POPIO, STC 5, + and -HLT * IOGSP * INTPT. The latter occurs when an interrupt causes + execution of a non-halt I/O instruction in the interrupt trap cell. +*/ + +int32 proio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ + + case ioSFC: /* skip flag clear */ + if (!mp_mevff) PC = (PC + 1) & VAMASK; /* skip if mem prot */ + break; + + case ioSFS: /* skip flag set */ + if (mp_mevff) PC = (PC + 1) & VAMASK; /* skip if DMS */ + break; + + case ioMIX: /* merge */ + dat = dat | mp_viol; + break; + + case ioLIX: /* load */ + dat = mp_viol; + break; + + case ioOTX: /* output */ + mp_fence = dat & VAMASK; + if (cpu_unit.flags & UNIT_2100) iop_sp = mp_fence; + break; + + case ioCRS: /* control reset */ + case ioCTL: /* control clear/set */ + if ((IR & I_CTL) == 0) { /* STC */ + setCTL (PRO); + dms_vr = 0; + mp_evrff = 1; /* allow mp_viol upd */ + mp_mevff = 0; /* clear DMS flag */ + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFLG (PRO); } /* HC option */ +return dat; +} + +/* Devices 2,3 (secondary DMA) I/O routine. + + Implements control word 2 (memory address) and control word 3 (word count). + + The 12607B (2114) supports 14-bit addresses and 13-bit word counts. + The 12578A (2115/6) supports 15-bit addresses and 14-bit word counts. + The 12895A (2100) and 12897B (1000) support 15-bit addresses and 16-bit word + counts. + + Note: because the I/O bus floats to zero on 211x computers, LIA/MIA (word + count) returns zeros in the unused bit locations, even though the word count + is a negative value. +*/ + +int32 dmsio (int32 inst, int32 IR, int32 dat) +{ +int32 ch; + +ch = IR & 1; /* get channel num */ +switch (inst) { /* case on opcode */ + + case ioLIX: /* load remaining word count */ + dat = 0; + + case ioMIX: /* merge */ + if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */ + dat = dat | (dmac[ch].cw3 & 0017777); /* only 13-bit count */ + else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 2115/2116? */ + dat = dat | (dmac[ch].cw3 & 0037777); /* only 14-bit count */ + else + dat = dat | dmac[ch].cw3; /* rest use full value */ + break; + + case ioOTX: /* output */ + if (CTL (DMALT0 + ch)) /* word count selected? */ + dmac[ch].cw3 = dat; /* save count */ + else /* memory address selected */ + if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */ + dmac[ch].cw2 = dat & 0137777; /* 14-bit address */ + else + dmac[ch].cw2 = dat; /* full address stored */ + break; + + case ioCRS: /* control reset */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { clrCTL (DMALT0 + ch); } /* CLC */ + else { setCTL (DMALT0 + ch); } /* STC */ + break; + + default: + break; + } + +return dat; +} + +/* Devices 6,7 (primary DMA) I/O routine + + Implements control word 1 (device address) and DMA control. + + The 12607B (2114) stores only bits 2-0 of the select code and interprets them + as select codes 10-16 (SRQ17 is not decoded). The 12578A (2115/6), 12895A + (2100), and 12897B (1000) support the full 10-77 range of select codes. + + The 12578A supports byte-sized transfers by setting bit 14. Bit 14 is + ignored by all other DMA cards, which support word transfers only. + + NOTE: LIx/MIx reads floating S-bus (1 on 21MX, 0 on 211x/2100). + + NOTE: CRS clears control and command flip-flops, whereas CLC clears only + control. +*/ + +int32 dmpio (int32 inst, int32 IR, int32 dat) +{ +int32 ch; + +ch = IR & 1; /* get channel number */ + +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag */ + if ((IR & I_HC) == 0) { /* set->abort */ + setFLG (DMA0 + ch); /* set flag */ + clrCMD (DMA0 + ch); /* clr cmd */ + } + break; + + case ioSFC: /* skip flag clear */ + if (FLG (DMA0 + ch) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (DMA0 + ch) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioLIX: /* load */ + dat = 0; + + case ioMIX: /* merge */ + if (UNIT_CPU_TYPE == UNIT_TYPE_1000) + dat = DMASK; + break; + + case ioOTX: /* output */ + if (UNIT_CPU_MODEL == UNIT_2114) /* 12607? */ + dmac[ch].cw1 = (dat & 0137707) | 010; /* mask SC, convert to 10-17 */ + else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 12578? */ + dmac[ch].cw1 = dat; /* store full select code, flags */ + else /* 12895, 12897 */ + dmac[ch].cw1 = dat & ~DMA1_PB; /* clip byte-packing flag */ + break; + + case ioCRS: /* control reset */ + clrCMD (DMA0 + ch); /* clear command flip-flop */ + + case ioCTL: /* control */ + if (IR & I_CTL) { clrCTL (DMA0 + ch); } /* CLC: cmd unchgd */ + else { /* STC */ + if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* slow DMA card? */ + dmac[ch].latency = 1; /* needs startup latency */ + else + dmac[ch].latency = 0; /* DCPC starts immediately */ + + dmac[ch].packer = 0; /* clear packing register */ + setCTL (DMA0 + ch); /* set ctl, cmd */ + setCMD (DMA0 + ch); + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFLG (DMA0 + ch); } /* HC option */ +return dat; +} + +/* DMA cycle routine + + The 12578A card supports byte-packing. If bit 14 in control word 1 is set, + each transfer will involve one read/write from memory and two output/input + operations in order to transfer sequential bytes to/from the device. + + The last cycle (word count reaches 0) logic is quite tricky. + Input cases: + - CLC requested: issue CLC + Output cases: + - neither STC nor CLC requested: issue CLF + - STC requested but not CLC: issue STC,C + - CLC requested but not STC: issue CLC,C + - STC and CLC both requested: issue STC,C and CLC,C, in that order + Either: issue EDT (pass DMA channel number and I/O flag) +*/ + +void dma_cycle (uint32 ch, uint32 map) +{ +int32 temp, dev, MA; +int32 inp = dmac[ch].cw2 & DMA2_OI; /* input flag */ +int32 byt = dmac[ch].cw1 & DMA1_PB; /* pack bytes flag */ + +if (dmac[ch].latency) { /* start-up latency? */ + dmac[ch].latency = dmac[ch].latency - 1; /* decrease it */ + return; /* that's all this cycle */ + } + +dev = dmac[ch].cw1 & I_DEVMASK; /* get device */ +MA = dmac[ch].cw2 & VAMASK; /* get mem addr */ + +if (inp) { /* input cycle? */ + temp = devdisp (dev, ioLIX, dev, 0); /* do LIA dev */ + + if (byt) { /* byte packing? */ + if (dmac[ch].packer & DMA_OE) { /* second byte? */ + temp = (dmac[ch].packer << 8) | /* merge stored byte */ + (temp & DMASK8); + WriteIO (MA, temp, map); /* store word data */ + } + else /* first byte */ + dmac[ch].packer = (temp & DMASK8); /* save it */ + + dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */ + } + else /* no byte packing */ + WriteIO (MA, temp, map); /* store word data */ + } +else { /* output cycle */ + if (byt) { /* byte packing? */ + if (dmac[ch].packer & DMA_OE) /* second byte? */ + temp = dmac[ch].packer & DMASK8; /* retrieve it */ + + else { /* first byte */ + dmac[ch].packer = ReadIO (MA, map); /* read word data */ + temp = (dmac[ch].packer >> 8) & DMASK8; /* get high byte */ + } + + dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */ + } + else /* no byte packing */ + temp = ReadIO (MA, map); /* read word data */ + + devdisp (dev, ioOTX, dev, temp); /* do OTA dev */ + } + +if ((dmac[ch].packer & DMA_OE) == 0) { /* new byte or no packing? */ + dmac[ch].cw2 = (dmac[ch].cw2 & DMA2_OI) | /* increment address */ + ((dmac[ch].cw2 + 1) & VAMASK); + dmac[ch].cw3 = (dmac[ch].cw3 + 1) & DMASK; /* increment word count */ + } + +if (dmac[ch].cw3) { /* more to do? */ + if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */ + devdisp (dev, ioCTL, I_HC + dev, 0); /* do STC,C dev */ + else devdisp (dev, ioFLG, I_HC + dev, 0); /* else CLF dev */ + } +else { + if (inp) { /* last cycle, input? */ + if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */ + devdisp (dev, ioCTL, I_CTL + dev, 0); /* yes */ + } /* end input */ + else { /* output */ + if ((dmac[ch].cw1 & (DMA1_STC | DMA1_CLC)) == 0) + devdisp (dev, ioFLG, I_HC + dev, 0); /* clear flag */ + if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */ + devdisp (dev, ioCTL, I_HC + dev, 0); /* do STC,C dev */ + if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */ + devdisp (dev, ioCTL, I_HC + I_CTL + dev, 0); /* yes */ + } /* end output */ + setFLG (DMA0 + ch); /* set DMA flg */ + clrCMD (DMA0 + ch); /* clr DMA cmd */ + devdisp (dev, ioEDT, dev, inp | ch); /* do EDT */ + } +return; +} + +/* Unimplemented device routine + + NOTE: For SC < 10, LIx/MIx reads floating S-bus (-1 on 21MX, 0 on 211x/2100). + For SC >= 10, LIx/MIx reads floating I/O bus (0 on all machines). +*/ + +int32 nulio (int32 inst, int32 IR, int32 dat) +{ +int32 devd; + +devd = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioSFC: /* skip flag clear */ + PC = (PC + 1) & VAMASK; + break; + + case ioLIX: /* load */ + dat = 0; + + case ioMIX: /* merge */ + if ((devd < VARDEV) && (UNIT_CPU_TYPE == UNIT_TYPE_1000)) + dat = DMASK; + break; + + default: + break; + } + +return (stop_dev << IOT_V_REASON) | dat; +} + +/* Reset routines */ + +t_stat cpu_reset (DEVICE *dptr) +{ +E = 0; +O = 0; +ion = ion_defer = 0; +clrCMD (PWR); +clrCTL (PWR); +clrFLG (PWR); +clrFBF (PWR); +dev_srq[0] = dev_srq[0] & ~M_FXDEV; +dms_enb = dms_ump = 0; /* init DMS */ +dms_sr = 0; +dms_vr = 0; +pcq_r = find_reg ("PCQ", NULL, dptr); +sim_brk_types = ALL_BKPTS; +sim_brk_dflt = SWMASK ('N'); /* type is nomap as DMS is off */ + +if (M == NULL) { /* initial call? */ + M = calloc (PASIZE, sizeof (uint16)); /* alloc mem */ + + if (M == NULL) /* alloc fail? */ + return SCPE_MEM; + else { /* do one-time init */ + MEMSIZE = 32768; /* set initial memory size */ + cpu_set_model (NULL, UNIT_2116, NULL, NULL); /* set initial CPU model */ + SR = 001000; /* select PTR boot ROM at SC 10 */ + cpu_boot (0, NULL); /* install loader for 2116 */ + cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (was enabled) */ + SR = 0; /* clear S */ + sim_vm_post = &hp_post_cmd; /* set cmd post proc */ + } +} + +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +return SCPE_OK; +} + +t_stat mp_reset (DEVICE *dptr) +{ +clrCTL (PRO); +clrFLG (PRO); +clrFBF (PRO); +mp_fence = 0; /* init mprot */ +mp_viol = 0; +mp_mevff = 0; +mp_evrff = 1; +return SCPE_OK; +} + +t_stat dma0_reset (DEVICE *tptr) +{ +if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */ + hp_enbdis_pair (&dma0_dev, &dma1_dev); /* make pair cons */ +clrCMD (DMA0); +clrCTL (DMA0); +setFLG (DMA0); +clrSRQ (DMA0); +clrCTL (DMALT0); +dmac[0].latency = dmac[0].packer = 0; +if (sim_switches & SWMASK ('P')) /* power up? */ + dmac[0].cw1 = dmac[0].cw2 = dmac[0].cw3 = 0; +return SCPE_OK; +} + +t_stat dma1_reset (DEVICE *tptr) +{ +if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */ + hp_enbdis_pair (&dma1_dev, &dma0_dev); /* make pair cons */ +clrCMD (DMA1); +clrCTL (DMA1); +setFLG (DMA1); +clrSRQ (DMA1); +clrCTL (DMALT1); +dmac[1].latency = dmac[1].packer = 0; +if (sim_switches & SWMASK ('P')) /* power up? */ + dmac[1].cw1 = dmac[1].cw2 = dmac[1].cw3 = 0; +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 d; + +if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */ + return SCPE_NOFNC; /* command not allowed */ +addr = dms_cons (addr, sw); +if (addr >= MEMSIZE) return SCPE_NXM; +if (!(sw & SIM_SW_REST) && (addr == 0)) d = saved_AR; +else if (!(sw & SIM_SW_REST) && (addr == 1)) d = saved_BR; +else d = M[addr]; +if (vptr != NULL) *vptr = d & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */ + return SCPE_NOFNC; /* command not allowed */ +addr = dms_cons (addr, sw); +if (addr >= MEMSIZE) return SCPE_NXM; +if (!(sw & SIM_SW_REST) && (addr == 0)) saved_AR = val & DMASK; +else if (!(sw & SIM_SW_REST) && (addr == 1)) saved_BR = val & DMASK; +else M[addr] = val & DMASK; +return SCPE_OK; +} + +/* Set device number */ + +t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc) +{ +DEVICE *dptr = (DEVICE *) desc; +DIB *dibp; +int32 i, newdev; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if ((desc == NULL) || (num > 1)) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newdev = get_uint (cptr, 8, I_DEVMASK - num, &r); +if (r != SCPE_OK) return r; +if (newdev < VARDEV) return SCPE_ARG; +for (i = 0; i <= num; i++, dibp++) dibp->devno = newdev + i; +return SCPE_OK; +} + +/* Show device number */ + +t_stat hp_showdev (FILE *st, UNIT *uptr, int32 num, void *desc) +{ +DEVICE *dptr = (DEVICE *) desc; +DIB *dibp; +int32 i; + +if ((desc == NULL) || (num > 1)) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +fprintf (st, "devno=%o", dibp->devno); +for (i = 1; i <= num; i++) fprintf (st, "/%o", dibp->devno + i); +return SCPE_OK; +} + +/* Make a pair of devices consistent */ + +void hp_enbdis_pair (DEVICE *ccp, DEVICE *dcp) +{ +if (ccp->flags & DEV_DIS) dcp->flags = dcp->flags | DEV_DIS; +else dcp->flags = dcp->flags & ~DEV_DIS; +return; +} + +/* VM command post-processor + + Update T register to contents of memory addressed by M register + if M register has changed. */ + +void hp_post_cmd (t_bool from_scp) +{ +if (MR != saved_MR) { /* M changed since last update? */ + saved_MR = MR; + TR = ReadTAB (MR); /* sync T with new M */ + } +return; +} + +/* Test for device conflict */ + +t_bool dev_conflict (void) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, j, k; +t_bool is_conflict = FALSE; +uint32 conflicts[I_DEVMASK + 1] = { 0 }; + +for (i = 0; dptr = sim_devices[i]; i++) { + dibp = (DIB *) dptr->ctxt; + if (dibp && !(dptr->flags & DEV_DIS)) + if (++conflicts[dibp->devno] > 1) + is_conflict = TRUE; + } + +if (is_conflict) { + sim_ttcmd(); + for (i = 0; i <= I_DEVMASK; i++) { + if (conflicts[i] > 1) { + k = conflicts[i]; + printf ("Select code %o conflict:", i); + if (sim_log) fprintf (sim_log, "Select code %o conflict:", i); + for (j = 0; dptr = sim_devices[j]; j++) { + dibp = (DIB *) dptr->ctxt; + if (dibp && !(dptr->flags & DEV_DIS) && (i == dibp->devno)) { + if (k < conflicts[i]) { + printf (" and"); + if (sim_log) fputs (" and", sim_log); + } + printf (" %s", sim_dname (dptr)); + if (sim_log) fprintf (sim_log, " %s", sim_dname (dptr)); + k = k - 1; + if (k == 0) { + putchar ('\n'); + if (sim_log) fputc ('\n', sim_log); + break; + } + } + } + } + } + } +return is_conflict; +} + +/* Change CPU memory size. + + On a 21xx, move the current loader to the top of the new memory size. Then + clear "non-existent memory" so that reads return zero, per spec. + + Validation: + - New size <= maximum size for current CPU. + - New size a positive multiple of 4K (progamming error if not). + - If new size < old size, truncation accepted. +*/ + +t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; +uint32 model = CPU_MODEL_INDEX; /* current CPU model index */ +uint32 old_size = MEMSIZE; /* current memory size */ + +if ((uint32) new_size > cpu_features[model].maxmem) + return SCPE_NOFNC; /* mem size unsupported */ + +if ((new_size <= 0) || (new_size > PASIZE) || ((new_size & 07777) != 0)) + return SCPE_NXM; /* invalid size (prog err) */ + +if (!(sim_switches & SWMASK ('F'))) { /* force truncation? */ + for (i = new_size; i < MEMSIZE; i++) mc = mc | M[i]; + if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_INCOMP; + } + +if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx CPU? */ + cpu_set_ldr (uptr, FALSE, NULL, NULL); /* save loader to shadow RAM */ + MEMSIZE = new_size; /* set new memory size */ + fwanxm = MEMSIZE - IBL_LNT; /* reserve memory for loader */ + } +else /* loader unsupported */ + fwanxm = MEMSIZE = new_size; /* set new memory size */ + +for (i = fwanxm; i < old_size; i++) M[i] = 0; /* zero non-existent memory */ +return SCPE_OK; +} + +/* Change CPU models. + + For convenience, MP and DMA are typically enabled if available; they may be + disabled subsequently if desired. Note that the 2114 supports only one DMA + channel (channel 0). All other models support two channels. + + Validation: + - Sets standard equipment and convenience features. + - Changes DMA device name to DCPC if 1000 is selected. + - Enforces maximum memory allowed (doesn't change otherwise). + - Disables loader on 21xx machines. +*/ + +t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc) +{ +uint32 old_family = UNIT_CPU_FAMILY; /* current CPU type */ +uint32 new_family = new_model & UNIT_FAMILY_MASK; /* new CPU family */ +uint32 new_index = new_model >> UNIT_V_CPU; /* new CPU model index */ +uint32 new_memsize; +t_stat result; + +cpu_unit.flags = cpu_unit.flags & ~UNIT_OPTS | /* set typical features */ + cpu_features[new_index].typ & UNIT_OPTS; /* mask pseudo-opts */ + + +if (cpu_features[new_index].typ & UNIT_MP) /* MP in typ config? */ + mp_dev.flags = mp_dev.flags & ~DEV_DIS; /* enable it */ +else + mp_dev.flags = mp_dev.flags | DEV_DIS; /* disable it */ + +if (cpu_features[new_index].opt & UNIT_MP) /* MP an option? */ + mp_dev.flags = mp_dev.flags | DEV_DISABLE; /* make it alterable */ +else + mp_dev.flags = mp_dev.flags & ~DEV_DISABLE; /* make it unalterable */ + + +if (cpu_features[new_index].typ & UNIT_DMA) { /* DMA in typ config? */ + dma0_dev.flags = dma0_dev.flags & ~DEV_DIS; /* enable DMA channel 0 */ + + if (new_model == UNIT_2114) /* 2114 has only one channel */ + dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */ + else /* all others have two channels */ + dma1_dev.flags = dma1_dev.flags & ~DEV_DIS; /* enable it */ + } +else { + dma0_dev.flags = dma0_dev.flags | DEV_DIS; /* disable channel 0 */ + dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */ + } + +if (cpu_features[new_index].opt & UNIT_DMA) { /* DMA an option? */ + dma0_dev.flags = dma0_dev.flags | DEV_DISABLE; /* make it alterable */ + + if (new_model == UNIT_2114) /* 2114 has only one channel */ + dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */ + else /* all others have two channels */ + dma1_dev.flags = dma1_dev.flags | DEV_DISABLE; /* make it alterable */ + } +else { + dma0_dev.flags = dma0_dev.flags & ~DEV_DISABLE; /* make it unalterable */ + dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */ + } + + +if ((old_family == UNIT_FAMILY_1000) && /* if current family is 1000 */ + (new_family == UNIT_FAMILY_21XX)) { /* and new family is 21xx */ + deassign_device (&dma0_dev); /* delete DCPC names */ + deassign_device (&dma1_dev); + } +else if ((old_family == UNIT_FAMILY_21XX) && /* if current family is 21xx */ + (new_family == UNIT_FAMILY_1000)) { /* and new family is 1000 */ + assign_device (&dma0_dev, "DCPC0"); /* change DMA device name */ + assign_device (&dma1_dev, "DCPC1"); /* to DCPC for familiarity */ + } + +if ((MEMSIZE == 0) || /* current mem size not set? */ + (MEMSIZE > cpu_features[new_index].maxmem)) /* current mem size too large? */ + new_memsize = cpu_features[new_index].maxmem; /* set it to max supported */ +else + new_memsize = MEMSIZE; /* or leave it unchanged */ + +result = cpu_set_size (uptr, new_memsize, NULL, NULL); /* set memory size */ + +if (result == SCPE_OK) /* memory change OK? */ + if (new_family == UNIT_FAMILY_21XX) /* 21xx CPU? */ + fwanxm = MEMSIZE - IBL_LNT; /* reserve memory for loader */ + else + fwanxm = MEMSIZE; /* loader reserved only for 21xx */ + +return result; +} + +/* Display the CPU model and optional loader status. + + Loader status is displayed for 21xx models and suppressed for 1000 models. +*/ + +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fputs ((char *) desc, st); /* write model name */ + +if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* valid only for 21xx */ + if (fwanxm < MEMSIZE) /* loader area non-existent? */ + fputs (", loader disabled", st); /* yes, so access disabled */ + else + fputs (", loader enabled", st); /* no, so access enabled */ +return SCPE_OK; +} + +/* Set a CPU option. + + Validation: + - Checks that the current CPU model supports the option selected. + - If CPU is 2100, ensures that FP/FFP and IOP are mutually exclusive. + - If CPU is 2100, ensures that FP is enabled if FFP enabled + (FP is required for FFP installation). +*/ + +t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc) +{ +uint32 model = CPU_MODEL_INDEX; /* current CPU model index */ + +if ((cpu_features[model].opt & option) == 0) /* option supported? */ + return SCPE_NOFNC; /* no */ + +if (UNIT_CPU_TYPE == UNIT_TYPE_2100) { + if ((option == UNIT_FP) || (option == UNIT_FFP)) /* 2100 IOP and FP/FFP options */ + uptr->flags = uptr->flags & ~UNIT_IOP; /* are mutually exclusive */ + else if (option == UNIT_IOP) + uptr->flags = uptr->flags & ~(UNIT_FP | UNIT_FFP); + + if (option == UNIT_FFP) /* 2100 FFP option requires FP */ + uptr->flags = uptr->flags | UNIT_FP; + } + +return SCPE_OK; +} + +/* Clear a CPU option. + + Validation: + - Checks that the current CPU model supports the option selected. + - Clears flag from unit structure (we are processing MTAB_XTD entries). + - If CPU is 2100, ensures that FFP is disabled if FP disabled + (FP is required for FFP installation). +*/ + +t_bool cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc) +{ +uint32 model = CPU_MODEL_INDEX; /* current CPU model index */ + +if ((cpu_features[model].opt & option) == 0) /* option supported? */ + return SCPE_NOFNC; /* no */ + +uptr->flags = uptr->flags & ~option; /* disable option */ + +if ((UNIT_CPU_TYPE == UNIT_TYPE_2100) && /* disabling 2100 FP? */ + (option == UNIT_FP)) + uptr->flags = uptr->flags & ~UNIT_FFP; /* yes, so disable FFP too */ + +return SCPE_OK; +} + +/* 21xx loader enable/disable function. + + The 21xx CPUs store their initial binary loaders in the last 64 words of + available memory. This memory is protected by a LOADER ENABLE switch on the + front panel. When the switch is off (disabled), main memory effectively ends + 64 locations earlier, i.e., the loader area is treated as non-existent. + Because these are core machines, the loader is retained when system power is + off. + + 1000 CPUs do not have a protected loader feature. Instead, loaders are + stored in PROMs and are copied into main memory for execution by the IBL + switch. + + Under simulation, we keep both a total configured memory size (MEMSIZE) and a + current configured memory size (fwanxm = "first word address of non-existent + memory). When the two are equal, the loader is enabled. When the current + size is less than the total size, the loader is disabled. + + Disabling the loader copies the last 64 words to a shadow array, zeros the + corresponding memory, and decreases the last word of addressable memory by + 64. Enabling the loader reverses this process. + + Disabling may be done manually by user command or automatically when a halt + instruction is executed. Enabling occurs only by user command. This differs + slightly from actual machine operation, which additionally disables the + loader when a manual halt is performed. We do not do this to allow + breakpoints within and single-stepping through the loaders. +*/ + +t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc) +{ +static uint16 loader[IBL_LNT]; +int32 i; +t_bool is_enabled = (fwanxm == MEMSIZE); + +if ((UNIT_CPU_FAMILY != UNIT_FAMILY_21XX) || /* valid only for 21xx */ + (MEMSIZE == 0)) /* and for initialized memory */ + return SCPE_NOFNC; + +if (is_enabled && (enable == 0)) { /* disable loader? */ + fwanxm = MEMSIZE - IBL_LNT; /* decrease available memory */ + for (i = 0; i < IBL_LNT; i++) { /* copy loader */ + loader[i] = M[fwanxm + i]; /* from memory */ + M[fwanxm + i] = 0; /* and zero location */ + } + } + +else if ((!is_enabled) && (enable == 1)) { /* enable loader? */ + for (i = 0; i < IBL_LNT; i++) /* copy loader */ + M[fwanxm + i] = loader[i]; /* to memory */ + fwanxm = MEMSIZE; /* increase available memory */ + } + +return SCPE_OK; +} + +/* IBL routine (CPU boot) */ + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ +extern const uint16 ptr_rom[IBL_LNT], dq_rom[IBL_LNT]; +extern const uint16 ms_rom[IBL_LNT], ds_rom[IBL_LNT]; +int32 dev = (SR >> IBL_V_DEV) & I_DEVMASK; +int32 sel = (SR >> IBL_V_SEL) & IBL_M_SEL; + +if (dev < 010) return SCPE_NOFNC; +switch (sel) { + + case 0: /* PTR boot */ + ibl_copy (ptr_rom, dev); + break; + + case 1: /* DP/DQ boot */ + ibl_copy (dq_rom, dev); + break; + + case 2: /* MS boot */ + ibl_copy (ms_rom, dev); + break; + + case 3: /* DS boot */ + ibl_copy (ds_rom,dev); + break; + } + +return SCPE_OK; +} + +/* IBL boot ROM copy + + - Use memory size to set the initial PC and base of the boot area + - Copy boot ROM to memory, updating I/O instructions + - Place 2's complement of boot base in last location + + Notes: + - SR settings are done by the caller + - Boot ROM's must be assembled with a device code of 10 (10 and 11 for + devices requiring two codes) +*/ + +t_stat ibl_copy (const uint16 pboot[IBL_LNT], int32 dev) +{ +int32 i; +uint16 wd; + +cpu_set_ldr (NULL, TRUE, NULL, NULL); /* enable loader (ignore errors) */ + +if (dev < 010) return SCPE_ARG; /* valid device? */ +PC = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */ +for (i = 0; i < IBL_LNT; i++) { /* copy bootstrap */ + wd = pboot[i]; /* get word */ + if (((wd & I_NMRMASK) == I_IO) && /* IO instruction? */ + ((wd & I_DEVMASK) >= 010) && /* dev >= 10? */ + (I_GETIOOP (wd) != ioHLT)) /* not a HALT? */ + M[PC + i] = (wd + (dev - 010)) & DMASK; /* change dev code */ + else M[PC + i] = wd; /* leave unchanged */ + } +M[PC + IBL_DPC] = (M[PC + IBL_DPC] + (dev - 010)) & DMASK; /* patch DMA ctrl */ +M[PC + IBL_END] = (~PC + 1) & DMASK; /* fill in start of boot */ +return SCPE_OK; +} diff --git a/HP2100/hp2100_cpu.h b/HP2100/hp2100_cpu.h new file mode 100644 index 0000000..60eaa55 --- /dev/null +++ b/HP2100/hp2100_cpu.h @@ -0,0 +1,232 @@ +/* hp2100_cpu.h: HP 2100 CPU definitions + + Copyright (c) 2005-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 24-Apr-08 JDB Added calc_defer() prototype + 20-Apr-08 JDB Added DEB_VIS and DEB_SIG debug flags + 26-Nov-07 JDB Added extern sim_deb, cpu_dev, DEB flags for debug printouts + 05-Nov-07 JDB Added extern intaddr, mp_viol, mp_mevff, calc_int, dev_ctl, + ReadIO, WriteIO for RTE-6/VM microcode support + 16-Dec-06 JDB Added UNIT_2115 and UNIT_2114 + 16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c + 26-Sep-06 JDB Added CPU externs for microcode simulators + 16-Aug-06 JDB Added UNIT_EMA for future RTE-4 EMA microcode + Added UNIT_VMA for future RTE-6 VMA and OS microcode + Added UNIT_1000_F for future F-Series support + 09-Aug-06 JDB Added UNIT_DBI for double integer microcode + 21-Jan-05 JDB Reorganized CPU option flags + 14-Jan-05 RMS Cloned from hp2100_cpu.c + + CPU models are broken down into family, type, and series to facilitate option + validation. Bit 3 encodes the family, bit 2 encodes the type, and bits 1:0 + encode the series within the type. +*/ + +#ifndef _HP2100_CPU_H_ +#define _HP2100_CPU_H_ 0 + +/* CPU model definition flags */ + +#define CPU_V_SERIES 0 +#define CPU_V_TYPE 2 +#define CPU_V_FAMILY 3 + +#define FAMILY_21XX (0 << CPU_V_FAMILY) +#define FAMILY_1000 (1 << CPU_V_FAMILY) + +#define TYPE_211X (0 << CPU_V_TYPE) /* 2114, 2115, 2116 */ +#define TYPE_2100 (1 << CPU_V_TYPE) /* 2100A, 2100S */ +#define TYPE_1000MEF (0 << CPU_V_TYPE) /* 1000-M, 1000-E, 1000-F */ +#define TYPE_1000AL (1 << CPU_V_TYPE) /* 1000-L, A600, A700, A900, A990 */ + +#define SERIES_16 (0 << CPU_V_SERIES) /* 211X */ +#define SERIES_15 (1 << CPU_V_SERIES) /* 211X */ +#define SERIES_14 (2 << CPU_V_SERIES) /* 211X */ +#define SERIES_00 (0 << CPU_V_SERIES) /* 2100 */ +#define SERIES_M (0 << CPU_V_SERIES) /* 1000 */ +#define SERIES_E (1 << CPU_V_SERIES) /* 1000 */ +#define SERIES_F (2 << CPU_V_SERIES) /* 1000 */ + +/* CPU unit flags */ + +#define UNIT_M_CPU 017 /* CPU model mask [3:0] */ +#define UNIT_M_TYPE 014 /* CPU type mask [3:2] */ +#define UNIT_M_FAMILY 010 /* CPU family mask [3:3] */ + +#define UNIT_V_CPU (UNIT_V_UF + 0) /* CPU model bits 0-3 */ +#define UNIT_V_EAU (UNIT_V_UF + 4) /* EAU installed */ +#define UNIT_V_FP (UNIT_V_UF + 5) /* FP installed */ +#define UNIT_V_IOP (UNIT_V_UF + 6) /* IOP installed */ +#define UNIT_V_DMS (UNIT_V_UF + 7) /* DMS installed */ +#define UNIT_V_FFP (UNIT_V_UF + 8) /* FFP installed */ +#define UNIT_V_DBI (UNIT_V_UF + 9) /* DBI installed */ +#define UNIT_V_EMA (UNIT_V_UF + 10) /* RTE-4 EMA installed */ +#define UNIT_V_VMAOS (UNIT_V_UF + 11) /* RTE-6 VMA/OS installed */ +#define UNIT_V_VIS (UNIT_V_UF + 12) /* VIS installed */ +#define UNIT_V_SIGNAL (UNIT_V_UF + 13) /* SIGNAL/1000 installed */ +/* Future microcode expansion; reuse flags bottom-up if needed */ +#define UNIT_V_DS (UNIT_V_UF + 14) /* DS installed */ + +/* Unit models */ + +#define UNIT_MODEL_MASK (UNIT_M_CPU << UNIT_V_CPU) + +#define UNIT_2116 ((FAMILY_21XX | TYPE_211X | SERIES_16) << UNIT_V_CPU) +#define UNIT_2115 ((FAMILY_21XX | TYPE_211X | SERIES_15) << UNIT_V_CPU) +#define UNIT_2114 ((FAMILY_21XX | TYPE_211X | SERIES_14) << UNIT_V_CPU) +#define UNIT_2100 ((FAMILY_21XX | TYPE_2100 | SERIES_00) << UNIT_V_CPU) +#define UNIT_1000_M ((FAMILY_1000 | TYPE_1000MEF | SERIES_M) << UNIT_V_CPU) +#define UNIT_1000_E ((FAMILY_1000 | TYPE_1000MEF | SERIES_E) << UNIT_V_CPU) +#define UNIT_1000_F ((FAMILY_1000 | TYPE_1000MEF | SERIES_F) << UNIT_V_CPU) + +/* Unit types */ + +#define UNIT_TYPE_MASK (UNIT_M_TYPE << UNIT_V_CPU) + +#define UNIT_TYPE_211X ((FAMILY_21XX | TYPE_211X) << UNIT_V_CPU) +#define UNIT_TYPE_2100 ((FAMILY_21XX | TYPE_2100) << UNIT_V_CPU) +#define UNIT_TYPE_1000 ((FAMILY_1000 | TYPE_1000MEF) << UNIT_V_CPU) + +/* Unit families */ + +#define UNIT_FAMILY_MASK (UNIT_M_FAMILY << UNIT_V_CPU) + +#define UNIT_FAMILY_21XX (FAMILY_21XX << UNIT_V_CPU) +#define UNIT_FAMILY_1000 (FAMILY_1000 << UNIT_V_CPU) + +/* Unit accessors */ + +#define UNIT_CPU_MODEL (cpu_unit.flags & UNIT_MODEL_MASK) +#define UNIT_CPU_TYPE (cpu_unit.flags & UNIT_TYPE_MASK) +#define UNIT_CPU_FAMILY (cpu_unit.flags & UNIT_FAMILY_MASK) + +#define CPU_MODEL_INDEX (UNIT_CPU_MODEL >> UNIT_V_CPU) + +/* Unit features */ + +#define UNIT_EAU (1 << UNIT_V_EAU) +#define UNIT_FP (1 << UNIT_V_FP) +#define UNIT_IOP (1 << UNIT_V_IOP) +#define UNIT_DMS (1 << UNIT_V_DMS) +#define UNIT_FFP (1 << UNIT_V_FFP) +#define UNIT_DBI (1 << UNIT_V_DBI) +#define UNIT_EMA (1 << UNIT_V_EMA) +#define UNIT_VMAOS (1 << UNIT_V_VMAOS) +#define UNIT_VIS (1 << UNIT_V_VIS) +#define UNIT_DS (1 << UNIT_V_DS) +#define UNIT_SIGNAL (1 << UNIT_V_SIGNAL) + +#define UNIT_EMA_VMA (UNIT_EMA | UNIT_VMAOS) + +#define UNIT_OPTS (UNIT_EAU | UNIT_FP | UNIT_IOP | \ + UNIT_DMS | UNIT_FFP | UNIT_DBI | \ + UNIT_EMA | UNIT_VMAOS | \ + UNIT_VIS | UNIT_DS | UNIT_SIGNAL) + +/* "Pseudo-option" flags used only for option testing; never set into UNIT structure. */ + +#define UNIT_V_PFAIL (UNIT_V_UF - 1) /* Power fail installed */ +#define UNIT_V_DMA (UNIT_V_UF - 2) /* DMA installed */ +#define UNIT_V_MP (UNIT_V_UF - 3) /* Memory protect installed */ + +#define UNIT_PFAIL (1 << UNIT_V_PFAIL) +#define UNIT_DMA (1 << UNIT_V_DMA) +#define UNIT_MP (1 << UNIT_V_MP) + +#define UNIT_NONE 0 /* no options */ + +/* Debug flags */ + +#define DEB_OS (1 << 0) /* RTE-6/VM OS firmware non-TBG processing */ +#define DEB_OSTBG (1 << 1) /* RTE-6/VM OS firmware TBG processing */ +#define DEB_VMA (1 << 2) /* RTE-6/VM VMA firmware instructions */ +#define DEB_EMA (1 << 3) /* RTE-6/VM EMA firmware instructions */ +#define DEB_VIS (1 << 4) /* E/F-Series VIS firmware instructions */ +#define DEB_SIG (1 << 5) /* F-Series SIGNAL/1000 firmware instructions */ + +/* PC queue. */ + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = err_PC + +/* simulator state */ + +extern FILE *sim_deb; + +/* CPU registers */ + +extern uint16 ABREG[2]; /* A/B regs (use AR/BR) */ +extern uint32 PC; /* P register */ +extern uint32 SR; /* S register */ +extern uint32 MR; /* M register */ +extern uint32 TR; /* T register */ +extern uint32 XR; /* X register */ +extern uint32 YR; /* Y register */ +extern uint32 E; /* E register */ +extern uint32 O; /* O register */ +extern uint32 dev_ctl[2]; /* device control */ + +/* CPU state */ + +extern uint32 err_PC; +extern uint32 dms_enb; +extern uint32 dms_ump; +extern uint32 dms_sr; +extern uint32 dms_vr; +extern uint32 mp_fence; +extern uint32 mp_viol; +extern uint32 mp_mevff; +extern uint32 iop_sp; +extern uint32 ion_defer; +extern uint32 intaddr; +extern uint16 pcq[PCQ_SIZE]; +extern uint32 pcq_p; +extern uint32 stop_inst; +extern UNIT cpu_unit; +extern DEVICE cpu_dev; + +/* CPU functions */ + +t_stat resolve (uint32 MA, uint32 *addr, uint32 irq); +uint8 ReadB (uint32 addr); +uint8 ReadBA (uint32 addr); +uint16 ReadW (uint32 addr); +uint16 ReadWA (uint32 addr); +uint16 ReadIO (uint32 addr, uint32 map); +void WriteB (uint32 addr, uint32 dat); +void WriteBA (uint32 addr, uint32 dat); +void WriteW (uint32 addr, uint32 dat); +void WriteWA (uint32 addr, uint32 dat); +void WriteIO (uint32 addr, uint32 dat, uint32 map); +t_stat iogrp (uint32 ir, uint32 iotrap); +uint32 calc_int (void); +uint32 calc_defer (void); +void mp_dms_jmp (uint32 va); +uint16 dms_rmap (uint32 mapi); +void dms_wmap (uint32 mapi, uint32 dat); +void dms_viol (uint32 va, uint32 st); +uint32 dms_upd_sr (void); + +#endif diff --git a/HP2100/hp2100_cpu0.c b/HP2100/hp2100_cpu0.c new file mode 100644 index 0000000..ad1dc33 --- /dev/null +++ b/HP2100/hp2100_cpu0.c @@ -0,0 +1,137 @@ +/* hp2100_cpu0.c: HP 1000 unimplemented instruction set stubs + + Copyright (c) 2006-2008, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + CPU0 Unimplemented firmware option instructions + + 26-Feb-08 HV Removed and implemented "cpu_vis" and "cpu_signal" + 22-Nov-07 JDB Removed and implemented "cpu_rte_ema" + 12-Nov-07 JDB Removed and implemented "cpu_rte_vma" and "cpu_rte_os" + 01-Dec-06 JDB Removed and implemented "cpu_sis". + 26-Sep-06 JDB Created + + This file contains template simulations for the firmware options that have + not yet been implemented. When a given firmware option is implemented, it + should be moved out of this file and into another (or its own, depending on + complexity). + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + + +t_stat cpu_ds (uint32 IR, uint32 intrq); /* Distributed System */ + + +/* Distributed System + + Distributed System firmware was provided with the HP 91740A DS/1000 product + for use with the HP 12771A (12665A) Serial Interface and 12773A Modem + Interface system interconnection kits. Firmware permitted high-speed + transfers with minimum impact to the processor. The advent of the + "intelligent" 12794A and 12825A HDLC cards, the 12793A and 12834A Bisync + cards, and the 91750A DS-1000/IV software obviated the need for CPU firmware, + as essentially the firmware was moved onto the I/O cards. + + Primary documentation for the DS instructions has not been located. However, + examination of the DS/1000 sources reveals that two instruction were used by + the DVA65 Serial Interface driver (91740-18071) and placed in the trap cells + of the communications interfaces. Presumably they handled interrupts from + the cards. + + Implementation of the DS instructions will also require simulation of the + 12665A Hardwired Serial Data Interface Card and the 12620A RTE Privileged + Interrupt Fence. These are required for DS/1000. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A 91740A 91740B 91740B + + The routines are mapped to instruction codes as follows: + + Instr. 1000-M 1000-E/F Description + ------ ------ -------- ---------------------------------------------- + 105520 105300 "Open loop" (trap cell handler) + 105521 105301 "Closed loop" (trap cell handler) + 105522 105302 [unknown] + [test] 105524 105304 [self test] + + Notes: + + 1. The E/F-Series opcodes were moved from 105340-357 to 105300-317 at + revision 1813. + + 2. DS/1000 ROM data are available from Bitsavers. + + Additional references (documents unavailable): + - HP 91740A M-Series Distributed System (DS/1000) Firmware Installation + Manual (91740-90007). + - HP 91740B Distributed System (DS/1000) Firmware Installation Manual + (91740-90009). +*/ + +static const OP_PAT op_ds[16] = { + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */ + }; + +t_stat cpu_ds (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +uint32 entry; + +if ((cpu_unit.flags & UNIT_DS) == 0) /* DS option installed? */ + return stop_inst; + +entry = IR & 017; /* mask to entry point */ + +if (op_ds[entry] != OP_N) + if (reason = cpu_ops (op_ds[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<3:0> */ + + default: /* others undefined */ + reason = stop_inst; + } + +return reason; +} diff --git a/HP2100/hp2100_cpu1.c b/HP2100/hp2100_cpu1.c new file mode 100644 index 0000000..e4eb4d6 --- /dev/null +++ b/HP2100/hp2100_cpu1.c @@ -0,0 +1,870 @@ +/* hp2100_cpu1.c: HP 2100/1000 EAU simulator and UIG dispatcher + + Copyright (c) 2005-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + CPU1 Extended arithmetic and optional microcode dispatchers + + 20-Apr-08 JDB Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64 + 28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts + 17-Nov-07 JDB Enabled DIAG as NOP on 1000 F-Series + 04-Jan-07 JDB Added special DBI dispatcher for non-INT64 diagnostic + 29-Dec-06 JDB Allows RRR as NOP if 2114 (diag config test) + 01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64 + 16-Oct-06 JDB Generalized operands for F-Series FP types + 26-Sep-06 JDB Split hp2100_cpu1.c into multiple modules to simplify extensions + Added iotrap parameter to UIG dispatchers for RTE microcode + 22-Feb-05 JDB Removed EXECUTE instruction (is NOP in actual microcode) + 21-Jan-05 JDB Reorganized CPU option and operand processing flags + Split code along microcode modules + 15-Jan-05 RMS Cloned from hp2100_cpu.c + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. + + This source file contains the Extended Arithmetic Unit simulator and the User + Instruction Group (a.k.a. "Macro") dispatcher for the 2100 and 1000 (21MX) + CPUs. The UIG simulators reside in separate source files, due to the large + number of firmware options available for the 1000 machines. Unit flags + indicate which options are present in the current system. + + This module also provides generalized instruction operand processing. + + The microcode address space of the 2100 encompassed four modules of 256 words + each. The 1000 M-series expanded that to sixteen modules, and the 1000 + E/F-series expanded that still further to sixty-four modules. Each CPU had + its own microinstruction set, although the micromachines of the various 1000 + models were similar internally. + + Regarding option instruction sets, there was some commonality across CPU + types. EAU instructions were identical across all models, and the floating + point set was the same on the 2100 and 1000. Other options implemented + proper instruction supersets (e.g., the Fast FORTRAN Processor from 2100 to + 1000-M to 1000-E to 1000-F) or functional equivalence with differing code + points (the 2000 I/O Processor from 2100 to 1000 and extended-precision + floating-point instructions from 1000-E to 1000-F). + + The 2100 decoded the EAU and UIG sets separately in hardware and supported + only the UIG 0 code points. Bits 7-4 of a UIG instruction decoded one of + sixteen entry points in the lowest-numbered module after module 0. Those + entry points could be used directly (as for the floating-point instructions), + or additional decoding based on bits 3-0 could be implemented. + + The 1000 generalized the instruction decoding to a series of microcoded + jumps, based on the bits in the instruction. Bits 15-8 indicated the group + of the current instruction: EAU (200, 201, 202, 210, and 211), UIG 0 (212), + or UIG 1 (203 and 213). UIG 0, UIG 1, and some EAU instructions were decoded + further by selecting one of sixteen modules within the group via bits 7-4. + Finally, each UIG module decoded up to sixteen instruction entry points via + bits 3-0. Jump tables for all firmware options were contained in the base + set, so modules needed only to be concerned with decoding their individual + entry points within the module. + + While the 2100 and 1000 hardware decoded these instruction sets differently, + the decoding mechanism of the simulation follows that of the 1000 E/F-series. + Where needed, CPU type- or model-specific behavior is simulated. + + The design of the 1000 microinstruction set was such that executing an + instruction for which no microcode was present (e.g., executing a FFP + instruction when the FFP firmware was not installed) resulted in a NOP. + Under simulation, such execution causes an undefined instruction stop. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + +#if defined (HAVE_INT64) /* int64 support available */ +extern t_stat cpu_fpp (uint32 IR, uint32 intrq); /* Floating Point Processor */ +extern t_stat cpu_sis (uint32 IR, uint32 intrq); /* Scientific Instruction Set */ +extern t_stat cpu_vis (uint32 IR, uint32 intrq); /* Vector Instruction Set */ +extern t_stat cpu_signal (uint32 IR, uint32 intrq); /* SIGNAL/1000 */ +#else /* int64 support unavailable */ +extern t_stat cpu_fp (uint32 IR, uint32 intrq); /* Firmware Floating Point */ +#endif /* end of int64 support */ + +extern t_stat cpu_ffp (uint32 IR, uint32 intrq); /* Fast FORTRAN Processor */ +extern t_stat cpu_ds (uint32 IR, uint32 intrq); /* Distributed Systems */ +extern t_stat cpu_dbi (uint32 IR, uint32 intrq); /* Double integer */ +extern t_stat cpu_rte_vma (uint32 IR, uint32 intrq); /* RTE-4/6 EMA/VMA */ +extern t_stat cpu_rte_os (uint32 IR, uint32 intrq, uint32 iotrap); /* RTE-6 OS */ +extern t_stat cpu_iop (uint32 IR, uint32 intrq); /* 2000 I/O Processor */ +extern t_stat cpu_dms (uint32 IR, uint32 intrq); /* Dynamic mapping system */ +extern t_stat cpu_eig (uint32 IR, uint32 intrq); /* Extended instruction group */ + +/* EAU + + The Extended Arithmetic Unit (EAU) adds ten instructions with double-word + operands, including multiply, divide, shifts, and rotates. Option + implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A 12579A 12579A std std std std + + The instruction codes are mapped to routines as follows: + + Instr. Bits + Code 15-8 7-4 2116 2100 1000-M 1000-E 1000-F Note + ------ ---- --- ------ ------ ------ ------ ------ --------------------- + 100000 200 00 [diag] [diag] [self test] + 100020 200 01 ASL ASL ASL ASL ASL Bits 3-0 encode shift + 100040 200 02 LSL LSL LSL LSL LSL Bits 3-0 encode shift + 100060 200 03 TIMER TIMER [deterministic delay] + 100100 200 04 RRL RRL RRL RRL RRL Bits 3-0 encode shift + 100200 200 10 MPY MPY MPY MPY MPY + 100400 201 xx DIV DIV DIV DIV DIV + 101020 202 01 ASR ASR ASR ASR ASR Bits 3-0 encode shift + 101040 202 02 LSR LSR LSR LSR LSR Bits 3-0 encode shift + 101100 202 04 RRR RRR RRR RRR RRR Bits 3-0 encode shift + 104200 210 xx DLD DLD DLD DLD DLD + 104400 211 xx DST DST DST DST DST + + The remaining codes for bits 7-4 are undefined and will cause a simulator + stop if enabled. On a real 1000-M, all undefined instructions in the 200 + group decode as MPY, and all in the 202 group decode as NOP. On a real + 1000-E, instruction patterns 200/05 through 200/07 and 202/03 decode as NOP; + all others cause erroneous execution. + + EAU instruction decoding on the 1000 M-series is convoluted. The JEAU + microorder maps IR bits 11, 9-7 and 5-4 to bits 2-0 of the microcode jump + address. The map is detailed on page IC-84 of the ERD. + + The 1000 E/F-series add two undocumented instructions to the 200 group: TIMER + and DIAG. These are described in the ERD on page IA 5-5, paragraph 5-7. The + M-series executes these as MPY and RRL, respectively. A third instruction, + EXECUTE (100120), is also described but was never implemented, and the + E/F-series microcode execute a NOP for this instruction code. + + Notes: + + 1. Under simulation, TIMER, DIAG, and EXECUTE cause undefined instruction + stops if the CPU is set to 21xx. DIAG and EXECUTE also cause stops on + the 1000-M. TIMER does not, because it is used by several HP programs + to differentiate between M- and E/F-series machines. + + 2. DIAG is not implemented under simulation. On the E/F, it performs a + destructive test of all installed memory. Because of this, it is only + functional if the machine is halted, i.e., if the instruction is + executed with the INSTR STEP button. If it is executed in a program, + the result is NOP. + + 3. RRR is permitted and executed as NOP if the CPU is a 2114, as the + presence of the EAU is tested by the diagnostic configurator to + differentiate between 2114 and 2100/1000 CPUs. +*/ + +t_stat cpu_eau (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +uint32 rs, qs, sc, v1, v2, t; +int32 sop1, sop2; + +if ((cpu_unit.flags & UNIT_EAU) == 0) /* option installed? */ + if ((UNIT_CPU_MODEL == UNIT_2114) && (IR == 0101100)) /* 2114 and RRR 16? */ + return SCPE_OK; /* allowed as NOP */ + else + return stop_inst; /* fail */ + +switch ((IR >> 8) & 0377) { /* decode IR<15:8> */ + + case 0200: /* EAU group 0 */ + switch ((IR >> 4) & 017) { /* decode IR<7:4> */ + + case 000: /* DIAG 100000 */ + if ((UNIT_CPU_MODEL != UNIT_1000_E) && /* must be 1000 E-series */ + (UNIT_CPU_MODEL != UNIT_1000_F)) /* or 1000 F-series */ + return stop_inst; /* trap if not */ + break; /* DIAG is NOP unless halted */ + + case 001: /* ASL 100020-100037 */ + sc = (IR & 017)? (IR & 017): 16; /* get sc */ + O = 0; /* clear ovflo */ + while (sc-- != 0) { /* bit by bit */ + t = BR << 1; /* shift B */ + BR = (BR & SIGN) | (t & 077777) | (AR >> 15); + AR = (AR << 1) & DMASK; + if ((BR ^ t) & SIGN) O = 1; + } + break; + + case 002: /* LSL 100040-100057 */ + sc = (IR & 017)? (IR & 017): 16; /* get sc */ + BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; + AR = (AR << sc) & DMASK; /* BR'AR lsh left */ + break; + + case 003: /* TIMER 100060 */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + if (UNIT_CPU_MODEL == UNIT_1000_M) /* 1000 M-series? */ + goto MPY; /* decode as MPY */ + BR = (BR + 1) & DMASK; /* increment B */ + if (BR) PC = err_PC; /* if !=0, repeat */ + break; + + case 004: /* RRL 100100-100117 */ + sc = (IR & 017)? (IR & 017): 16; /* get sc */ + t = BR; /* BR'AR rot left */ + BR = ((BR << sc) | (AR >> (16 - sc))) & DMASK; + AR = ((AR << sc) | (t >> (16 - sc))) & DMASK; + break; + + case 010: /* MPY 100200 (OP_K) */ + MPY: + if (reason = cpu_ops (OP_K, op, intrq)) /* get operand */ + break; + sop1 = SEXT (AR); /* sext AR */ + sop2 = SEXT (op[0].word); /* sext mem */ + sop1 = sop1 * sop2; /* signed mpy */ + BR = (sop1 >> 16) & DMASK; /* to BR'AR */ + AR = sop1 & DMASK; + O = 0; /* no overflow */ + break; + + default: /* others undefined */ + return stop_inst; + } + + break; + + case 0201: /* DIV 100400 (OP_K) */ + if (reason = cpu_ops (OP_K, op, intrq)) /* get operand */ + break; + if (rs = qs = BR & SIGN) { /* save divd sign, neg? */ + AR = (~AR + 1) & DMASK; /* make B'A pos */ + BR = (~BR + (AR == 0)) & DMASK; /* make divd pos */ + } + v2 = op[0].word; /* divr = mem */ + if (v2 & SIGN) { /* neg? */ + v2 = (~v2 + 1) & DMASK; /* make divr pos */ + qs = qs ^ SIGN; /* sign of quotient */ + } + if (BR >= v2) O = 1; /* divide work? */ + else { /* maybe... */ + O = 0; /* assume ok */ + v1 = (BR << 16) | AR; /* 32b divd */ + AR = (v1 / v2) & DMASK; /* quotient */ + BR = (v1 % v2) & DMASK; /* remainder */ + if (AR) { /* quotient > 0? */ + if (qs) AR = (~AR + 1) & DMASK; /* apply quo sign */ + if ((AR ^ qs) & SIGN) O = 1; /* still wrong? ovflo */ + } + if (rs) BR = (~BR + 1) & DMASK; /* apply rem sign */ + } + break; + + case 0202: /* EAU group 2 */ + switch ((IR >> 4) & 017) { /* decode IR<7:4> */ + + case 001: /* ASR 101020-101037 */ + sc = (IR & 017)? (IR & 017): 16; /* get sc */ + AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; + BR = (SEXT (BR) >> sc) & DMASK; /* BR'AR ash right */ + O = 0; + break; + + case 002: /* LSR 101040-101057 */ + sc = (IR & 017)? (IR & 017): 16; /* get sc */ + AR = ((BR << (16 - sc)) | (AR >> sc)) & DMASK; + BR = BR >> sc; /* BR'AR log right */ + break; + + case 004: /* RRR 101100-101117 */ + sc = (IR & 017)? (IR & 017): 16; /* get sc */ + t = AR; /* BR'AR rot right */ + AR = ((AR >> sc) | (BR << (16 - sc))) & DMASK; + BR = ((BR >> sc) | (t << (16 - sc))) & DMASK; + break; + + default: /* others undefined */ + return stop_inst; + } + + break; + + case 0210: /* DLD 104200 (OP_D) */ + if (reason = cpu_ops (OP_D, op, intrq)) /* get operand */ + break; + AR = (op[0].dword >> 16) & DMASK; /* load AR */ + BR = op[0].dword & DMASK; /* load BR */ + break; + + case 0211: /* DST 104400 (OP_A) */ + if (reason = cpu_ops (OP_A, op, intrq)) /* get operand */ + break; + WriteW (op[0].word, AR); /* store AR */ + WriteW ((op[0].word + 1) & VAMASK, BR); /* store BR */ + break; + + default: /* should never get here */ + return SCPE_IERR; /* bad call from cpu_instr */ + } + +return reason; +} + +/* UIG 0 + + The first User Instruction Group (UIG) encodes firmware options for the 2100 + and 1000. Instruction codes 105000-105377 are assigned to microcode options + as follows: + + Instructions Option Name 2100 1000-M 1000-E 1000-F + ------------- -------------------------- ------ ------ ------ ------ + 105000-105362 2000 I/O Processor opt - - - + 105000-105137 Floating Point opt std std std + 105200-105237 Fast FORTRAN Processor opt opt opt std + 105240-105257 RTE-IVA/B Extended Memory - - opt opt + 105240-105257 RTE-6/VM Virtual Memory - - opt opt + 105300-105317 Distributed System - - opt opt + 105320-105337 Double Integer - - opt - + 105320-105337 Scientific Instruction Set - - - std + 105340-105357 RTE-6/VM Operating System - - opt opt + + Because the 2100 IOP microcode uses the same instruction range as the 2100 FP + and FFP options, it cannot coexist with them. To simplify simulation, the + 2100 IOP instructions are remapped to the equivalent 1000 instructions and + dispatched to the UIG 1 module. + + Note that if the 2100 IOP is installed, the only valid UIG instructions are + IOP instructions, as the IOP used the full 2100 microcode addressing space. + + The F-Series moved the three-word extended real instructions from the FFP + range to the base floating-point range and added four-word double real and + two-word double integer instructions. The double integer instructions + occupied some of the vacated extended real instruction codes in the FFP, with + the rest assigned to the floating-point range. Consequently, many + instruction codes for the F-Series are different from the E-Series. + + Notes: + + 1. Product 93585A, available from the "Specials" group, added double + integer microcode to the E-Series. The instruction codes were different + from those in the F-Series to avoid conflicting with the E-Series FFP. + HP manual number 93585-90007 documents the double integer instructions, + but no copy of this manual has been found. The Macro/1000 manual + (92059-090001) lists E-Series double integer instructions as occupying + the code points of the F-Series Scientific Instruction Set. + + 2. To run the double-integer instructions diagnostic in the absence of + 64-bit integer support (and therefore of F-Series simulation), a special + DBI dispatcher may be enabled by defining ENABLE_DIAG during + compilation. This dispatcher will remap the F-Series DBI instructions + to the E-Series codes, so that the F-Series diagnostic may be run. + Because several of the F-Series DBI instruction codes replace M/E-Series + FFP codes, this dispatcher will only operate if FFP is disabled. + + Note that enabling the dispatcher will produce non-standard FP behavior. + For example, any code in the range 105000-105017 normally would execute + a FAD instruction. With the dispatcher enabled, 105014 would execute a + .DAD, while the other codes would execute a FAD. Therefore, ENABLE_DIAG + should only be used to run the diagnostic and is not intended for + general use. +*/ + +t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap) +{ +if ((cpu_unit.flags & UNIT_IOP) && /* I/O Processor? */ + (UNIT_CPU_TYPE == UNIT_TYPE_2100)) /* 2100 CPU? */ + return cpu_iop (IR, intrq); /* dispatch to IOP */ + + +#if !defined (HAVE_INT64) && defined (ENABLE_DIAG) /* DBI diagnostic dispatcher wanted */ + +if ((cpu_unit.flags & UNIT_FFP) == 0) + switch (IR & 0377) { + case 0014: /* .DAD 105014 */ + return cpu_dbi (0105321, intrq); + + case 0034: /* .DSB 105034 */ + return cpu_dbi (0105327, intrq); + + case 0054: /* .DMP 105054 */ + return cpu_dbi (0105322, intrq); + + case 0074: /* .DDI 105074 */ + return cpu_dbi (0105325, intrq); + + case 0114: /* .DSBR 105114 */ + return cpu_dbi (0105334, intrq); + + case 0134: /* .DDIR 105134 */ + return cpu_dbi (0105326, intrq); + + case 0203: /* .DNG 105203 */ + return cpu_dbi (0105323, intrq); + + case 0204: /* .DCO 105204 */ + return cpu_dbi (0105324, intrq); + + case 0210: /* .DIN 105210 */ + return cpu_dbi (0105330, intrq); + + case 0211: /* .DDE 105211 */ + return cpu_dbi (0105331, intrq); + + case 0212: /* .DIS 105212 */ + return cpu_dbi (0105332, intrq); + + case 0213: /* .DDS 105213 */ + return cpu_dbi (0105333, intrq); + } /* otherwise, continue */ + +#endif /* end of DBI dispatcher */ + + +switch ((IR >> 4) & 017) { /* decode IR<7:4> */ + + case 000: /* 105000-105017 */ + case 001: /* 105020-105037 */ + case 002: /* 105040-105057 */ + case 003: /* 105060-105077 */ + case 004: /* 105100-105117 */ + case 005: /* 105120-105137 */ +#if defined (HAVE_INT64) /* int64 support available */ + return cpu_fpp (IR, intrq); /* Floating Point Processor */ +#else /* int64 support unavailable */ + return cpu_fp (IR, intrq); /* Firmware Floating Point */ +#endif /* end of int64 support */ + + case 010: /* 105200-105217 */ + case 011: /* 105220-105237 */ + return cpu_ffp (IR, intrq); /* Fast FORTRAN Processor */ + + case 012: /* 105240-105257 */ + return cpu_rte_vma (IR, intrq); /* RTE-4/6 EMA/VMA */ + + case 014: /* 105300-105317 */ + return cpu_ds (IR, intrq); /* Distributed System */ + + case 015: /* 105320-105337 */ +#if defined (HAVE_INT64) /* int64 support available */ + if (UNIT_CPU_MODEL == UNIT_1000_F) /* F-series? */ + return cpu_sis (IR, intrq); /* Scientific Instruction */ + else /* M/E-series */ +#endif /* end of int64 support */ + return cpu_dbi (IR, intrq); /* Double integer */ + + case 016: /* 105340-105357 */ + return cpu_rte_os (IR, intrq, iotrap); /* RTE-6 OS */ + } + +return stop_inst; /* others undefined */ +} + +/* UIG 1 + + The second User Instruction Group (UIG) encodes firmware options for the + 1000. Instruction codes 101400-101777 and 105400-105777 are assigned to + microcode options as follows ("x" is "1" or "5" below): + + Instructions Option Name 1000-M 1000-E 1000-F + ------------- ---------------------------- ------ ------ ------ + 10x400-10x437 2000 IOP opt opt - + 10x460-10x477 2000 IOP opt opt - + 10x460-10x477 Vector Instruction Set - - opt + 105520-105537 Distributed System opt - - + 105600-105617 SIGNAL/1000 Instruction Set - - opt + 10x700-10x737 Dynamic Mapping System opt opt std + 10x740-10x777 Extended Instruction Group std std std + + Only 1000 systems execute these instructions. +*/ + +t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap) +{ +if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* 1000 execution? */ + return stop_inst; /* no, so trap */ + +switch ((IR >> 4) & 017) { /* decode IR<7:4> */ + + case 000: /* 105400-105417 */ + case 001: /* 105420-105437 */ + return cpu_iop (IR, intrq); /* 2000 I/O Processor */ + + case 003: /* 105460-105477 */ +#if defined (HAVE_INT64) /* int64 support available */ + if (UNIT_CPU_MODEL == UNIT_1000_F) /* F-series? */ + return cpu_vis (IR, intrq); /* Vector Instruction Set */ + else /* M/E-series */ +#endif /* end of int64 support */ + return cpu_iop (IR, intrq); /* 2000 I/O Processor */ + + case 005: /* 105520-105537 */ + IR = IR ^ 0000620; /* remap to 105300-105317 */ + return cpu_ds (IR, intrq); /* Distributed System */ + +#if defined (HAVE_INT64) /* int64 support available */ + case 010: /* 105600-105617 */ + return cpu_signal (IR, intrq); /* SIGNAL/1000 Instructions */ +#endif /* end of int64 support */ + + case 014: /* 105700-105717 */ + case 015: /* 105720-105737 */ + return cpu_dms (IR, intrq); /* Dynamic Mapping System */ + + case 016: /* 105740-105737 */ + case 017: /* 105760-105777 */ + return cpu_eig (IR, intrq); /* Extended Instruction Group */ + } + +return stop_inst; /* others undefined */ +} + + +/* Read a multiple-precision operand value. */ + +OP ReadOp (uint32 va, OPSIZE precision) +{ +OP operand; +uint32 i; + +if (precision == in_s) + operand.word = ReadW (va); /* read single integer */ + +else if (precision == in_d) + operand.dword = ReadW (va) << 16 | /* read double integer */ + ReadW ((va + 1) & VAMASK); /* merge high and low words */ + +else + for (i = 0; i < (uint32) precision; i++) { /* read fp 2 to 5 words */ + operand.fpk[i] = ReadW (va); + va = (va + 1) & VAMASK; + } +return operand; +} + +/* Write a multiple-precision operand value. */ + +void WriteOp (uint32 va, OP operand, OPSIZE precision) +{ +uint32 i; + +if (precision == in_s) + WriteW (va, operand.word); /* write single integer */ + +else if (precision == in_d) { + WriteW (va, (operand.dword >> 16) & DMASK); /* write double integer */ + WriteW ((va + 1) & VAMASK, operand.dword & DMASK); /* high word, then low word */ + } + +else + for (i = 0; i < (uint32) precision; i++) { /* write fp 2 to 5 words */ + WriteW (va, operand.fpk[i]); + va = (va + 1) & VAMASK; + } +return; +} + + +/* Get instruction operands. + + Operands for a given instruction are specifed by an "operand pattern" + consisting of flags indicating the types and storage methods. The pattern + directs how each operand is to be retrieved and whether the operand value or + address is returned in the operand array. + + Typically, a microcode simulation handler will define an OP_PAT array, with + each element containing an operand pattern corresponding to the simulated + instruction. Operand patterns are defined in the header file accompanying + this source file. After calling this function with the appropriate operand + pattern and a pointer to an array of OPs, operands are decoded and stored + sequentially in the array. + + The following operand encodings are defined: + + Code Operand Description Example Return + ------ ---------------------------------------- ----------- ------------ + OP_NUL No operand present [inst] None + + OP_IAR Integer constant in A register LDA I Value of I + [inst] + ... + I DEC 0 + + OP_DAB Double integer constant in A/B registers DLD J Value of J + [inst] + ... + J DEC 0,0 + + OP_FAB 2-word FP constant in A/B registers DLD F Value of F + [inst] + ... + F DEC 0.0 + + OP_CON Inline 1-word constant [inst] Value of C + C DEC 0 + ... + + OP_VAR Inline 1-word variable [inst] Address of V + V BSS 1 + ... + + OP_ADR Inline address [inst] Address of A + DEF A + ... + A EQU * + + OP_ADK Address of integer constant [inst] Value of K + DEF K + ... + K DEC 0 + + OP_ADD Address of double integer constant [inst] Value of D + DEF D + ... + D DEC 0,0 + + OP_ADF Address of 2-word FP constant [inst] Value of F + DEF F + ... + F DEC 0.0 + + OP_ADX Address of 3-word FP constant [inst] Value of X + DEF X + ... + X DEX 0.0 + + OP_ADT Address of 4-word FP constant [inst] Value of T + DEF T + ... + T DEY 0.0 + + OP_ADE Address of 5-word FP constant [inst] Value of E + DEF E + ... + E DEC 0,0,0,0,0 + + Address operands, i.e., those having a DEF to the operand, will be resolved + to direct addresses. If an interrupt is pending and more than three levels + of indirection are used, the routine returns without completing operand + retrieval (the instruction will be retried after interrupt servicing). + Addresses are always resolved in the current DMS map. + + An operand pattern consists of one or more operand encodings, corresponding + to the operands required by a given instruction. Values are returned in + sequence to the operand array. +*/ + +t_stat cpu_ops (OP_PAT pattern, OPS op, uint32 irq) +{ +t_stat reason = SCPE_OK; +OP_PAT flags; +uint32 i, MA; + +for (i = 0; i < OP_N_F; i++) { + flags = pattern & OP_M_FLAGS; /* get operand pattern */ + + if (flags >= OP_ADR) /* address operand? */ + if (reason = resolve (ReadW (PC), &MA, irq)) /* resolve indirects */ + return reason; + + switch (flags) { + case OP_NUL: /* null operand */ + return reason; /* no more, so quit */ + + case OP_IAR: /* int in A */ + (*op++).word = AR; /* get one-word value */ + break; + + case OP_JAB: /* dbl-int in A/B */ + (*op++).dword = (AR << 16) | BR; /* get two-word value */ + break; + + case OP_FAB: /* 2-word FP in A/B */ + (*op).fpk[0] = AR; /* get high FP word */ + (*op++).fpk[1] = BR; /* get low FP word */ + break; + + case OP_CON: /* inline constant operand */ + *op++ = ReadOp (PC, in_s); /* get value */ + break; + + case OP_VAR: /* inline variable operand */ + (*op++).word = PC; /* get pointer to variable */ + break; + + case OP_ADR: /* inline address operand */ + (*op++).word = MA; /* get address */ + break; + + case OP_ADK: /* address of int constant */ + *op++ = ReadOp (MA, in_s); /* get value */ + break; + + case OP_ADD: /* address of dbl-int constant */ + *op++ = ReadOp (MA, in_d); /* get value */ + break; + + case OP_ADF: /* address of 2-word FP const */ + *op++ = ReadOp (MA, fp_f); /* get value */ + break; + + case OP_ADX: /* address of 3-word FP const */ + *op++ = ReadOp (MA, fp_x); /* get value */ + break; + + case OP_ADT: /* address of 4-word FP const */ + *op++ = ReadOp (MA, fp_t); /* get value */ + break; + + case OP_ADE: /* address of 5-word FP const */ + *op++ = ReadOp (MA, fp_e); /* get value */ + break; + + default: + return SCPE_IERR; /* not implemented */ + } + + if (flags >= OP_CON) /* operand after instruction? */ + PC = (PC + 1) & VAMASK; /* yes, so bump to next */ + pattern = pattern >> OP_N_FLAGS; /* move next pattern into place */ + } +return reason; +} + + +/* Print operands to the debug device. + + The values of an operand array are printed to the debug device. The types of + the operands are specified by an operand pattern. Typically, the operand + pattern is the same one that was used to fill the array originally. +*/ + +void fprint_ops (OP_PAT pattern, OPS op) +{ +OP_PAT flags; +uint32 i; + +for (i = 0; i < OP_N_F; i++) { + flags = pattern & OP_M_FLAGS; /* get operand pattern */ + + switch (flags) { + case OP_NUL: /* null operand */ + return; /* no more, so quit */ + + case OP_IAR: /* int in A */ + case OP_CON: /* inline constant operand */ + case OP_VAR: /* inline variable operand */ + case OP_ADR: /* inline address operand */ + case OP_ADK: /* address of int constant */ + fprintf (sim_deb, + ", op[%d] = %06o", + i, op[i].word); + break; + + case OP_JAB: /* dbl-int in A/B */ + case OP_ADD: /* address of dbl-int constant */ + fprintf (sim_deb, + ", op[%d] = %011o", + i, op[i].dword); + break; + + case OP_FAB: /* 2-word FP in A/B */ + case OP_ADF: /* address of 2-word FP const */ + fprintf (sim_deb, + ", op[%d] = (%06o, %06o)", + i, op[i].fpk[0], op[i].fpk[1]); + break; + + case OP_ADX: /* address of 3-word FP const */ + fprintf (sim_deb, + ", op[%d] = (%06o, %06o, %06o)", + i, op[i].fpk[0], op[i].fpk[1], + op[i].fpk[2]); + break; + + case OP_ADT: /* address of 4-word FP const */ + fprintf (sim_deb, + ", op[%d] = (%06o, %06o, %06o, %06o)", + i, op[i].fpk[0], op[i].fpk[1], + op[i].fpk[2], op[i].fpk[3]); + break; + + case OP_ADE: /* address of 5-word FP const */ + fprintf (sim_deb, + ", op[%d] = (%06o, %06o, %06o, %06o, %06o)", + i, op[i].fpk[0], op[i].fpk[1], + op[i].fpk[2], op[i].fpk[3], op[i].fpk[4]); + break; + + default: + fprintf (sim_deb, "UNKNOWN OPERAND TYPE"); /* not implemented */ + } + + pattern = pattern >> OP_N_FLAGS; /* move next pattern into place */ + } +} + + +/* Print CPU registers to the debug device. + + One or more CPU registers may be printed to the debug output device, which + must be valid before calling. +*/ + +void fprint_regs (char *caption, uint32 regs, uint32 base) +{ +static uint32 ARX, BRX, PRL; /* static so addresses are constant */ + +static const char *reg_names[] = { "CIR", "A", "B", "E", "X", "Y", "O", "P", "return" }; +static const uint32 *reg_ptrs[] = { &intaddr, &ARX, &BRX, &E, &XR, &YR, &O, &PC, &PRL }; +static const char *formats[] = { "%02o", "%06o", "%06o", "%01o", "%06o", "%06o", "%01o", "%06o", "P+%d" }; + +static char format[20] = " %s = "; /* base format string */ +static const int eos = 6; /* length of base format string */ + +uint32 i; +t_bool first = TRUE; /* first-time through flag */ + +ARX = AR; /* copy 16-bit value to static variable */ +BRX = BR; /* copy 16-bit value to static variable */ +PRL = PC - base; /* compute value in static variable */ + +for (i = 0; i < REG_COUNT; i++) { + if (regs & 1) { /* register requested? */ + if (first) /* first time? */ + fputs (caption, sim_deb); /* print caption */ + else + fputc (',', sim_deb); /* print separator */ + + strcpy (&format[eos], formats[i]); /* copy format specifier */ + fprintf (sim_deb, format, reg_names[i], *reg_ptrs[i]); + + first = FALSE; + } + + regs = regs >> 1; /* align next register flag */ + } +return; +} diff --git a/HP2100/hp2100_cpu1.h b/HP2100/hp2100_cpu1.h new file mode 100644 index 0000000..e563443 --- /dev/null +++ b/HP2100/hp2100_cpu1.h @@ -0,0 +1,282 @@ +/* hp2100_cpu1.h: HP 2100/1000 firmware dispatcher definitions + + Copyright (c) 2006-2008, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + 30-Apr-08 JDB Corrected OP_AFF to OP_AAFF for SIGNAL/1000 + Removed unused operand patterns + 23-Feb-08 HV Added more OP_* for SIGNAL/1000 and VIS + 28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts + 19-Oct-07 JDB Revised OP_KKKAKK operand profile to OP_CCCACC for $LOC + 16-Oct-06 JDB Generalized operands for F-Series FP types + 26-Sep-06 JDB Split from hp2100_cpu1.c +*/ + +#ifndef _HP2100_CPU1_H_ +#define _HP2100_CPU1_H_ + + +/* Register print encoding. */ + +#define REG_COUNT 9 /* count of print flags */ + +#define REG_CIR (1 << 0) /* print central interrupt register */ +#define REG_A (1 << 1) /* print A register */ +#define REG_B (1 << 2) /* print B register */ +#define REG_E (1 << 3) /* print E register */ +#define REG_X (1 << 4) /* print X register */ +#define REG_Y (1 << 5) /* print Y register */ +#define REG_O (1 << 6) /* print O register */ +#define REG_P (1 << 7) /* print P register */ +#define REG_P_REL (1 << 8) /* print P register as relative */ + + +/* Operand processing encoding. */ + +/* Base operand types. Note that all address encodings must be grouped together + after OP_ADR. */ + +#define OP_NUL 0 /* no operand */ +#define OP_IAR 1 /* 1-word int in A reg */ +#define OP_JAB 2 /* 2-word int in A/B regs */ +#define OP_FAB 3 /* 2-word FP const in A/B regs */ +#define OP_CON 4 /* inline 1-word constant */ +#define OP_VAR 5 /* inline 1-word variable */ + +#define OP_ADR 6 /* inline address */ +#define OP_ADK 7 /* addr of 1-word int const */ +#define OP_ADD 8 /* addr of 2-word int const */ +#define OP_ADF 9 /* addr of 2-word FP const */ +#define OP_ADX 10 /* addr of 3-word FP const */ +#define OP_ADT 11 /* addr of 4-word FP const */ +#define OP_ADE 12 /* addr of 5-word FP const */ + +#define OP_N_FLAGS 4 /* number of bits needed for flags */ +#define OP_M_FLAGS ((1 << OP_N_FLAGS) - 1) /* mask for flag bits */ + +#define OP_N_F (8 * sizeof (uint32) / OP_N_FLAGS) /* max number of op fields */ + +#define OP_V_F1 (0 * OP_N_FLAGS) /* 1st operand field */ +#define OP_V_F2 (1 * OP_N_FLAGS) /* 2nd operand field */ +#define OP_V_F3 (2 * OP_N_FLAGS) /* 3rd operand field */ +#define OP_V_F4 (3 * OP_N_FLAGS) /* 4th operand field */ +#define OP_V_F5 (4 * OP_N_FLAGS) /* 5th operand field */ +#define OP_V_F6 (5 * OP_N_FLAGS) /* 6th operand field */ +#define OP_V_F7 (6 * OP_N_FLAGS) /* 7th operand field */ +#define OP_V_F8 (7 * OP_N_FLAGS) /* 8th operand field */ + +/* Operand processing patterns. */ + +#define OP_N (OP_NUL << OP_V_F1) +#define OP_I (OP_IAR << OP_V_F1) +#define OP_J (OP_JAB << OP_V_F1) +#define OP_R (OP_FAB << OP_V_F1) +#define OP_C (OP_CON << OP_V_F1) +#define OP_V (OP_VAR << OP_V_F1) +#define OP_A (OP_ADR << OP_V_F1) +#define OP_K (OP_ADK << OP_V_F1) +#define OP_D (OP_ADD << OP_V_F1) +#define OP_X (OP_ADX << OP_V_F1) +#define OP_T (OP_ADT << OP_V_F1) +#define OP_E (OP_ADE << OP_V_F1) + +#define OP_IA ((OP_IAR << OP_V_F1) | (OP_ADR << OP_V_F2)) +#define OP_JA ((OP_JAB << OP_V_F1) | (OP_ADR << OP_V_F2)) +#define OP_JD ((OP_JAB << OP_V_F1) | (OP_ADD << OP_V_F2)) +#define OP_RC ((OP_FAB << OP_V_F1) | (OP_CON << OP_V_F2)) +#define OP_RK ((OP_FAB << OP_V_F1) | (OP_ADK << OP_V_F2)) +#define OP_RF ((OP_FAB << OP_V_F1) | (OP_ADF << OP_V_F2)) +#define OP_CV ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2)) +#define OP_AC ((OP_ADR << OP_V_F1) | (OP_CON << OP_V_F2)) +#define OP_AA ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2)) +#define OP_AK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2)) +#define OP_AX ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2)) +#define OP_AT ((OP_ADR << OP_V_F1) | (OP_ADT << OP_V_F2)) +#define OP_KV ((OP_ADK << OP_V_F1) | (OP_VAR << OP_V_F2)) +#define OP_KA ((OP_ADK << OP_V_F1) | (OP_ADR << OP_V_F2)) +#define OP_KK ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2)) + +#define OP_IIF ((OP_IAR << OP_V_F1) | (OP_IAR << OP_V_F2) | \ + (OP_ADF << OP_V_F3)) + +#define OP_IAT ((OP_IAR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADT << OP_V_F3)) + +#define OP_CVA ((OP_CON << OP_V_F1) | (OP_VAR << OP_V_F2) | \ + (OP_ADR << OP_V_F3)) + +#define OP_AAA ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADR << OP_V_F3)) + +#define OP_AAF ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADF << OP_V_F3)) + +#define OP_AAX ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADX << OP_V_F3)) + +#define OP_AAT ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADT << OP_V_F3)) + +#define OP_AKA ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ + (OP_ADR << OP_V_F3)) + +#define OP_AKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ + (OP_ADK << OP_V_F3)) + +#define OP_AXX ((OP_ADR << OP_V_F1) | (OP_ADX << OP_V_F2) | \ + (OP_ADX << OP_V_F3)) + +#define OP_ATT ((OP_ADR << OP_V_F1) | (OP_ADT << OP_V_F2) | \ + (OP_ADT << OP_V_F3)) + +#define OP_AEE ((OP_ADR << OP_V_F1) | (OP_ADE << OP_V_F2) | \ + (OP_ADE << OP_V_F3)) + +#define OP_AAXX ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADX << OP_V_F3) | (OP_ADX << OP_V_F4)) + +#define OP_AAFF ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADF << OP_V_F3) | (OP_ADF << OP_V_F4)) + +#define OP_AAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADK << OP_V_F3) | (OP_ADK << OP_V_F4)) + +#define OP_KKKK ((OP_ADK << OP_V_F1) | (OP_ADK << OP_V_F2) | \ + (OP_ADK << OP_V_F3) | (OP_ADK << OP_V_F4)) + +#define OP_AAAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \ + (OP_ADK << OP_V_F5)) + +#define OP_AKAKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ + (OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \ + (OP_ADK << OP_V_F5)) + +#define OP_AAACCC ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADR << OP_V_F3) | (OP_CON << OP_V_F4) | \ + (OP_CON << OP_V_F5) | (OP_CON << OP_V_F6)) + +#define OP_AAFFKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADF << OP_V_F3) | (OP_ADF << OP_V_F4) | \ + (OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6)) + +#define OP_AAKAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADK << OP_V_F3) | (OP_ADR << OP_V_F4) | \ + (OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6)) + +#define OP_CATAKK ((OP_CON << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADT << OP_V_F3) | (OP_ADR << OP_V_F4) | \ + (OP_ADK << OP_V_F5) | (OP_ADK << OP_V_F6)) + +#define OP_CCCACC ((OP_CON << OP_V_F1) | (OP_CON << OP_V_F2) | \ + (OP_CON << OP_V_F3) | (OP_ADR << OP_V_F4) | \ + (OP_CON << OP_V_F5) | (OP_CON << OP_V_F6)) + +#define OP_AAAFFKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADR << OP_V_F3) | (OP_ADF << OP_V_F4) | \ + (OP_ADF << OP_V_F5) | (OP_ADK << OP_V_F6) | \ + (OP_ADK << OP_V_F7)) + +#define OP_AKAKAKK ((OP_ADR << OP_V_F1) | (OP_ADK << OP_V_F2) | \ + (OP_ADR << OP_V_F3) | (OP_ADK << OP_V_F4) | \ + (OP_ADR << OP_V_F5) | (OP_ADK << OP_V_F6) | \ + (OP_ADK << OP_V_F7)) + +#define OP_AAKAKAKK ((OP_ADR << OP_V_F1) | (OP_ADR << OP_V_F2) | \ + (OP_ADK << OP_V_F3) | (OP_ADR << OP_V_F4) | \ + (OP_ADK << OP_V_F5) | (OP_ADR << OP_V_F6) | \ + (OP_ADK << OP_V_F7) | (OP_ADK << OP_V_F8)) + +#define OP_CCACACCA ((OP_CON << OP_V_F1) | (OP_CON << OP_V_F2) | \ + (OP_ADR << OP_V_F3) | (OP_CON << OP_V_F4) | \ + (OP_ADR << OP_V_F5) | (OP_CON << OP_V_F6) | \ + (OP_CON << OP_V_F7) | (OP_ADR << OP_V_F8)) + + +/* Operand precisions (compatible with F-Series FPP): + + - S = 1-word integer + - D = 2-word integer + - F = 2-word single-precision floating-point + - X = 3-word extended-precision floating-point + - T = 4-word double-precision floating-point + - E = 5-word expanded-exponent floating-point + - A = null operand (operand is in FPP accumulator) + + 5-word floating-point numbers are supported by the F-Series Floating-Point + Processor hardware, but the instruction codes are not documented. + + Note that ordering is important, as we depend on the "fp" type codes to + reflect the number of words needed. +*/ + +typedef enum { in_s, in_d, fp_f, fp_x, fp_t, fp_e, fp_a } OPSIZE; + + +/* Conversion from operand size to word count. */ + +#define TO_COUNT(s) ((s == fp_a) ? 0 : (uint32) (s + (s < fp_f))) + + +/* HP in-memory representation of a packed floating-point number. + Actual value will use two, three, four, or five words, as needed. */ + +typedef uint16 FPK[5]; + + +/* Operand processing types. + + NOTE: Microsoft VC++ 6.0 does not support the C99 standard, so we cannot + initialize unions by arbitrary variant ("designated initializers"). + Therefore, we follow the C90 form of initializing via the first named + variant. The FPK variant must appear first in the OP structure, as we define + a number of FPK constants in other modules. +*/ + +typedef union { /* general operand */ + FPK fpk; /* floating-point value */ + uint16 word; /* 16-bit integer */ + uint32 dword; /* 32-bit integer */ + } OP; + +typedef OP OPS[OP_N_F]; /* operand array */ + +typedef uint32 OP_PAT; /* operand pattern */ + + +/* Microcode dispatcher functions. */ + +t_stat cpu_eau (uint32 IR, uint32 intrq); /* EAU group simulator */ +t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap); /* UIG group 0 dispatcher */ +t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap); /* UIG group 1 dispatcher */ + +/* Microcode helper functions. */ + +OP ReadOp (uint32 va, OPSIZE precision); /* generalized operand read */ +void WriteOp (uint32 va, OP operand, OPSIZE precision); /* generalized operand write */ +t_stat cpu_ops (OP_PAT pattern, OPS op, uint32 irq); /* operand processor */ + +void fprint_ops (OP_PAT pattern, OPS op); /* debug print operands */ +void fprint_regs (char *caption, uint32 regs, uint32 base); /* debug print CPU registers */ + +#endif diff --git a/HP2100/hp2100_cpu2.c b/HP2100/hp2100_cpu2.c new file mode 100644 index 0000000..d9ba58d --- /dev/null +++ b/HP2100/hp2100_cpu2.c @@ -0,0 +1,1125 @@ +/* hp2100_cpu2.c: HP 2100/1000 FP/DMS/EIG/IOP instructions + + Copyright (c) 2005-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + CPU2 Floating-point, dynamic mapping, extended, and I/O processor + instructions + + 19-Dec-06 JDB DMS self-test now executes as NOP on 1000-M + 01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64 + 26-Sep-06 JDB Moved from hp2100_cpu1.c to simplify extensions + 22-Feb-05 JDB Fixed missing MPCK on JRS target + 21-Jan-05 JDB Reorganized CPU option and operand processing flags + Split code along microcode modules + 15-Jan-05 RMS Cloned from hp2100_cpu.c + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + +#if !defined (HAVE_INT64) /* int64 support unavailable */ + +#include "hp2100_fp.h" + +t_stat cpu_fp (uint32 IR, uint32 intrq); /* Firmware Floating Point */ + +#endif /* int64 support unavailable */ + +t_stat cpu_dms (uint32 IR, uint32 intrq); /* Dynamic mapping system */ +t_stat cpu_eig (uint32 IR, uint32 intrq); /* Extended instruction group */ +t_stat cpu_iop (uint32 IR, uint32 intrq); /* 2000 I/O Processor */ + + +#if !defined (HAVE_INT64) /* int64 support unavailable */ + +/* Single-Precision Floating Point Instructions + + The 2100 and 1000 CPUs share the single-precision (two word) floating-point + instruction codes. Floating-point firmware was an option on the 2100 and was + standard on the 1000-M and E. The 1000-F had a standard hardware Floating + Point Processor that executed these six instructions and added extended- and + double-precision floating- point instructions, as well as double-integer + instructions (the FPP is simulated separately). + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A 12901A std std N/A + + The instruction codes for the 2100 and 1000-M/E systems are mapped to + routines as follows: + + Instr. 2100/1000-M/E Description + ------ ------------- ----------------------------------- + 105000 FAD Single real add + 105020 FSB Single real subtract + 105040 FMP Single real multiply + 105060 FDV Single real divide + 105100 FIX Single integer to single real fix + 105120 FLT Single real to single integer float + + Bits 3-0 are not decoded by these instructions, so FAD (e.g.) would be + executed by any instruction in the range 105000-105017. + + Implementation note: rather than have two simulators that each executes the + single-precision FP instruction set, we compile conditionally, based on the + availability of 64-bit integer support in the host compiler. 64-bit integers + are required for the FPP, so if they are available, then the FPP is used to + handle the six single-precision instructions for the 2100 and M/E-Series, and + this function is omitted. If support is unavailable, this function is used + instead. + + Implementation note: the operands to FAD, etc. are floating-point values, so + OP_F would normally be used. However, the firmware FP support routines want + floating-point operands as 32-bit integer values, so OP_D is used to achieve + this. +*/ + +static const OP_PAT op_fp[8] = { + OP_D, OP_D, OP_D, OP_D, /* FAD FSB FMP FDV */ + OP_N, OP_N, OP_N, OP_N /* FIX FLT --- --- */ + }; + +t_stat cpu_fp (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +uint32 entry; + +if ((cpu_unit.flags & UNIT_FP) == 0) /* FP option installed? */ + return stop_inst; + +entry = (IR >> 4) & 017; /* mask to entry point */ + +if (op_fp[entry] != OP_N) + if (reason = cpu_ops (op_fp[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<7:4> */ + + case 000: /* FAD 105000 (OP_D) */ + O = f_as (op[0].dword, 0); /* add, upd ovflo */ + break; + + case 001: /* FSB 105020 (OP_D) */ + O = f_as (op[0].dword, 1); /* sub, upd ovflo */ + break; + + case 002: /* FMP 105040 (OP_D) */ + O = f_mul (op[0].dword); /* mul, upd ovflo */ + break; + + case 003: /* FDV 105060 (OP_D) */ + O = f_div (op[0].dword); /* div, upd ovflo */ + break; + + case 004: /* FIX 105100 (OP_N) */ + O = f_fix (); /* fix, upd ovflo */ + break; + + case 005: /* FLT 105120 (OP_N) */ + O = f_flt (); /* float, upd ovflo */ + break; + + default: /* should be impossible */ + return SCPE_IERR; + } + +return reason; +} + +#endif /* int64 support unavailable */ + + +/* Dynamic Mapping System + + The 1000 Dynamic Mapping System (DMS) consisted of the 12731A Memory + Expansion Module (MEM) card and 38 instructions to expand the basic 32K + logical address space to a 1024K physical space. The MEM provided four maps + of 32 mapping registers each: a system map, a user map, and two DCPC maps. + DMS worked in conjunction with memory protect to provide a "protected mode" + in which memory read and write violations could be trapped, and that + inhibited "privileged" instruction execution that attempted to alter the + memory mapping. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A 12976B 13307B std + + The instruction codes are mapped to routines as follows: + + Instr. 1000-M 1000-E/F Instr. 1000-M 1000-E/F + ------ ------ -------- ------ ------ -------- + 10x700 [xmm] [xmm] 10x720 XMM XMM + 10x701 [nop] [test] 10x721 XMS XMS + 10x702 MBI MBI 10x722 XM* XM* + 10x703 MBF MBF 10x723 [nop] [nop] + 10x704 MBW MBW 10x724 XL* XL* + 10x705 MWI MWI 10x725 XS* XS* + 10x706 MWF MWF 10x726 XC* XC* + 10x707 MWW MWW 10x727 LF* LF* + 10x710 SY* SY* 10x730 RS* RS* + + 10x711 US* US* 10x731 RV* RV* + 10x712 PA* PA* 10x732 DJP DJP + 10x713 PB* PB* 10x733 DJS DJS + 10x714 SSM SSM 10x734 SJP SJP + 10x715 JRS JRS 10x735 SJS SJS + 10x716 [nop] [nop] 10x736 UJP UJP + 10x717 [nop] [nop] 10x737 UJS UJS + + Instructions that use IR bit 9 to select the A or B register are designated + with a * above (e.g., 101710 is SYA, and 105710 is SYB). For those that do + not use this feature, either the 101xxx or 105xxx code will execute the + corresponding instruction, although the 105xxx form is the documented + instruction code. + + Notes: + + 1. Instruction code 10x700 will execute the XMM instruction, although + 10x720 is the documented instruction value. + + 2. Instruction code 10x701 will complement the A or B register, as + indicated, on 1000-E and F-Series machines. This instruction is a NOP + on M-Series machines. + + 3. The DMS privilege violation rules are: + - load map and CTL5 set (XMM, XMS, XM*, SY*, US*, PA*, PB*) + - load state or fence and UMAP set (JRS, DJP, DJS, SJP, SJS, UJP, UJS, LF*) + + 4. The 1000 manual is incorrect in stating that M*I, M*W, XS* are + privileged. +*/ + +static const OP_PAT op_dms[32] = { + OP_N, OP_N, OP_N, OP_N, /* [xmm] [test] MBI MBF */ + OP_N, OP_N, OP_N, OP_N, /* MBW MWI MWF MWW */ + OP_N, OP_N, OP_N, OP_N, /* SYA/B USA/B PAA/B PBA/B */ + OP_A, OP_KA, OP_N, OP_N, /* SSM JRS nop nop */ + OP_N, OP_N, OP_N, OP_N, /* XMM XMS XMA/B nop */ + OP_A, OP_A, OP_A, OP_N, /* XLA/B XSA/B XCA/B LFA/B */ + OP_N, OP_N, OP_A, OP_A, /* RSA/B RVA/B DJP DJS */ + OP_A, OP_A, OP_A, OP_A /* SJP SJS UJP UJS */ + }; + +t_stat cpu_dms (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +uint32 entry, absel; +uint32 i, t, mapi, mapj; + +if ((cpu_unit.flags & UNIT_DMS) == 0) /* DMS option installed? */ + return stop_inst; + +absel = (IR & I_AB)? 1: 0; /* get A/B select */ +entry = IR & 037; /* mask to entry point */ + +if (op_dms[entry] != OP_N) + if (reason = cpu_ops (op_dms[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<3:0> */ + +/* DMS module 1 */ + + case 000: /* [undefined] 105700 (OP_N) */ + goto XMM; /* decodes as XMM */ + + case 001: /* [self test] 105701 (OP_N) */ + if (UNIT_CPU_MODEL != UNIT_1000_M) /* executes as NOP on 1000-M */ + ABREG[absel] = ~ABREG[absel]; /* CMA or CMB */ + break; + + case 002: /* MBI 105702 (OP_N) */ + AR = AR & ~1; /* force A, B even */ + BR = BR & ~1; + while (XR != 0) { /* loop */ + t = ReadB (AR); /* read curr */ + WriteBA (BR, t); /* write alt */ + AR = (AR + 1) & DMASK; /* incr ptrs */ + BR = (BR + 1) & DMASK; + XR = (XR - 1) & DMASK; + if (XR && intrq && !(AR & 1)) { /* more, int, even? */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 003: /* MBF 105703 (OP_N) */ + AR = AR & ~1; /* force A, B even */ + BR = BR & ~1; + while (XR != 0) { /* loop */ + t = ReadBA (AR); /* read alt */ + WriteB (BR, t); /* write curr */ + AR = (AR + 1) & DMASK; /* incr ptrs */ + BR = (BR + 1) & DMASK; + XR = (XR - 1) & DMASK; + if (XR && intrq && !(AR & 1)) { /* more, int, even? */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 004: /* MBW 105704 (OP_N) */ + AR = AR & ~1; /* force A, B even */ + BR = BR & ~1; + while (XR != 0) { /* loop */ + t = ReadBA (AR); /* read alt */ + WriteBA (BR, t); /* write alt */ + AR = (AR + 1) & DMASK; /* incr ptrs */ + BR = (BR + 1) & DMASK; + XR = (XR - 1) & DMASK; + if (XR && intrq && !(AR & 1)) { /* more, int, even? */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 005: /* MWI 105705 (OP_N) */ + while (XR != 0) { /* loop */ + t = ReadW (AR & VAMASK); /* read curr */ + WriteWA (BR & VAMASK, t); /* write alt */ + AR = (AR + 1) & DMASK; /* incr ptrs */ + BR = (BR + 1) & DMASK; + XR = (XR - 1) & DMASK; + if (XR && intrq) { /* more and intr? */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 006: /* MWF 105706 (OP_N) */ + while (XR != 0) { /* loop */ + t = ReadWA (AR & VAMASK); /* read alt */ + WriteW (BR & VAMASK, t); /* write curr */ + AR = (AR + 1) & DMASK; /* incr ptrs */ + BR = (BR + 1) & DMASK; + XR = (XR - 1) & DMASK; + if (XR && intrq) { /* more and intr? */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 007: /* MWW 105707 (OP_N) */ + while (XR != 0) { /* loop */ + t = ReadWA (AR & VAMASK); /* read alt */ + WriteWA (BR & VAMASK, t); /* write alt */ + AR = (AR + 1) & DMASK; /* incr ptrs */ + BR = (BR + 1) & DMASK; + XR = (XR - 1) & DMASK; + if (XR && intrq) { /* more and intr? */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 010: /* SYA, SYB 10x710 (OP_N) */ + case 011: /* USA, USB 10x711 (OP_N) */ + case 012: /* PAA, PAB 10x712 (OP_N) */ + case 013: /* PBA, PBB 10x713 (OP_N) */ + mapi = (IR & 03) << VA_N_PAG; /* map base */ + if (ABREG[absel] & SIGN) { /* store? */ + for (i = 0; i < MAP_LNT; i++) { + t = dms_rmap (mapi + i); /* map to memory */ + WriteW ((ABREG[absel] + i) & VAMASK, t); + } + } + else { /* load */ + dms_viol (err_PC, MVI_PRV); /* priv if PRO */ + for (i = 0; i < MAP_LNT; i++) { + t = ReadW ((ABREG[absel] + i) & VAMASK); + dms_wmap (mapi + i, t); /* mem to map */ + } + } + ABREG[absel] = (ABREG[absel] + MAP_LNT) & DMASK; + break; + + case 014: /* SSM 105714 (OP_A) */ + WriteW (op[0].word, dms_upd_sr ()); /* store stat */ + break; + + case 015: /* JRS 105715 (OP_KA) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + dms_enb = 0; /* assume off */ + dms_ump = SMAP; + if (op[0].word & 0100000) { /* set enable? */ + dms_enb = 1; + if (op[0].word & 0040000) dms_ump = UMAP; /* set/clr usr */ + } + mp_dms_jmp (op[1].word); /* mpck jmp target */ + PCQ_ENTRY; /* save old PC */ + PC = op[1].word; /* jump */ + ion_defer = 1; /* defer intr */ + break; + +/* DMS module 2 */ + + case 020: /* XMM 105720 (OP_N) */ + XMM: + if (XR == 0) break; /* nop? */ + while (XR != 0) { /* loop */ + if (XR & SIGN) { /* store? */ + t = dms_rmap (AR); /* map to mem */ + WriteW (BR & VAMASK, t); + XR = (XR + 1) & DMASK; + } + else { /* load */ + dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + t = ReadW (BR & VAMASK); /* mem to map */ + dms_wmap (AR, t); + XR = (XR - 1) & DMASK; + } + AR = (AR + 1) & DMASK; + BR = (BR + 1) & DMASK; + if (intrq && ((XR & 017) == 017)) { /* intr, grp of 16? */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 021: /* XMS 105721 (OP_N) */ + if ((XR & SIGN) || (XR == 0)) break; /* nop? */ + dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + while (XR != 0) { + dms_wmap (AR, BR); /* AR to map */ + XR = (XR - 1) & DMASK; + AR = (AR + 1) & DMASK; + BR = (BR + 1) & DMASK; + if (intrq && ((XR & 017) == 017)) { /* intr, grp of 16? */ + PC = err_PC; + break; + } + } + break; + + case 022: /* XMA, XMB 10x722 (OP_N) */ + dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + if (ABREG[absel] & 0100000) mapi = UMAP; + else mapi = SMAP; + if (ABREG[absel] & 0000001) mapj = PBMAP; + else mapj = PAMAP; + for (i = 0; i < MAP_LNT; i++) { + t = dms_rmap (mapi + i); /* read map */ + dms_wmap (mapj + i, t); /* write map */ + } + break; + + case 024: /* XLA, XLB 10x724 (OP_A) */ + ABREG[absel] = ReadWA (op[0].word); /* load alt */ + break; + + case 025: /* XSA, XSB 10x725 (OP_A) */ + WriteWA (op[0].word, ABREG[absel]); /* store alt */ + break; + + case 026: /* XCA, XCB 10x726 (OP_A) */ + if (ABREG[absel] != ReadWA (op[0].word)) /* compare alt */ + PC = (PC + 1) & VAMASK; + break; + + case 027: /* LFA, LFB 10x727 (OP_N) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + dms_sr = (dms_sr & ~(MST_FLT | MST_FENCE)) | + (ABREG[absel] & (MST_FLT | MST_FENCE)); + break; + + case 030: /* RSA, RSB 10x730 (OP_N) */ + ABREG[absel] = dms_upd_sr (); /* save stat */ + break; + + case 031: /* RVA, RVB 10x731 (OP_N) */ + ABREG[absel] = dms_vr; /* save viol */ + break; + + case 032: /* DJP 105732 (OP_A) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + mp_dms_jmp (op[0].word); /* validate jump addr */ + PCQ_ENTRY; /* save curr PC */ + PC = op[0].word; /* new PC */ + dms_enb = 0; /* disable map */ + dms_ump = SMAP; + ion_defer = 1; + break; + + case 033: /* DJS 105733 (OP_A) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + WriteW (op[0].word, PC); /* store ret addr */ + PCQ_ENTRY; /* save curr PC */ + PC = (op[0].word + 1) & VAMASK; /* new PC */ + dms_enb = 0; /* disable map */ + dms_ump = SMAP; + ion_defer = 1; /* defer intr */ + break; + + case 034: /* SJP 105734 (OP_A) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + mp_dms_jmp (op[0].word); /* validate jump addr */ + PCQ_ENTRY; /* save curr PC */ + PC = op[0].word; /* jump */ + dms_enb = 1; /* enable system */ + dms_ump = SMAP; + ion_defer = 1; /* defer intr */ + break; + + case 035: /* SJS 105735 (OP_A) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + t = PC; /* save retn addr */ + PCQ_ENTRY; /* save curr PC */ + PC = (op[0].word + 1) & VAMASK; /* new PC */ + dms_enb = 1; /* enable system */ + dms_ump = SMAP; + WriteW (op[0].word, t); /* store ret addr */ + ion_defer = 1; /* defer intr */ + break; + + case 036: /* UJP 105736 (OP_A) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + mp_dms_jmp (op[0].word); /* validate jump addr */ + PCQ_ENTRY; /* save curr PC */ + PC = op[0].word; /* jump */ + dms_enb = 1; /* enable user */ + dms_ump = UMAP; + ion_defer = 1; /* defer intr */ + break; + + case 037: /* UJS 105737 (OP_A) */ + if (dms_ump) dms_viol (err_PC, MVI_PRV); /* priv viol if prot */ + t = PC; /* save retn addr */ + PCQ_ENTRY; /* save curr PC */ + PC = (op[0].word + 1) & VAMASK; /* new PC */ + dms_enb = 1; /* enable user */ + dms_ump = UMAP; + WriteW (op[0].word, t); /* store ret addr */ + ion_defer = 1; /* defer intr */ + break; + + default: /* others NOP */ + break; + } + +return reason; +} + + +/* Extended Instruction Group + + The Extended Instruction Group (EIG) adds 32 index and 10 bit/byte/word + manipulation instructions to the 1000 base set. These instructions + use the new X and Y index registers that were added to the 1000. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A std std std + + The instruction codes are mapped to routines as follows: + + Instr. 1000-M/E/F Instr. 1000-M/E/F + ------ ---------- ------ ---------- + 10x740 S*X 10x760 ISX + 10x741 C*X 10x761 DSX + 10x742 L*X 10x762 JLY + 10x743 STX 10x763 LBT + 10x744 CX* 10x764 SBT + 10x745 LDX 10x765 MBT + 10x746 ADX 10x766 CBT + 10x747 X*X 10x767 SFB + + 10x750 S*Y 10x770 ISY + 10x751 C*Y 10x771 DSY + 10x752 L*Y 10x772 JPY + 10x753 STY 10x773 SBS + 10x754 CY* 10x774 CBS + 10x755 LDY 10x775 TBS + 10x756 ADY 10x776 CMW + 10x757 X*Y 10x777 MVW + + Instructions that use IR bit 9 to select the A or B register are designated + with a * above (e.g., 101740 is SAX, and 105740 is SBX). For those that do + not use this feature, either the 101xxx or 105xxx code will execute the + corresponding instruction, although the 105xxx form is the documented + instruction code. + + Notes: + + 1. The LBT, SBT, MBT, and MVW instructions are used as part of the 2100 IOP + implementation. When so called, the MBT and MVW instructions have the + additional restriction that the count must be positive. +*/ + +static const OP_PAT op_eig[32] = { + OP_A, OP_N, OP_A, OP_A, /* S*X C*X L*X STX */ + OP_N, OP_K, OP_K, OP_N, /* CX* LDX ADX X*X */ + OP_A, OP_N, OP_A, OP_A, /* S*Y C*Y L*Y STY */ + OP_N, OP_K, OP_K, OP_N, /* CY* LDY ADY X*Y */ + OP_N, OP_N, OP_A, OP_N, /* ISX DSX JLY LBT */ + OP_N, OP_KV, OP_KV, OP_N, /* SBT MBT CBT SFB */ + OP_N, OP_N, OP_C, OP_KA, /* ISY DSY JPY SBS */ + OP_KA, OP_KK, OP_KV, OP_KV /* CBS TBS CMW MVW */ + }; + +t_stat cpu_eig (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +uint32 entry, absel; +uint32 t, v1, v2, wc; +int32 sop1, sop2; + +absel = (IR & I_AB)? 1: 0; /* get A/B select */ +entry = IR & 037; /* mask to entry point */ + +if (op_eig[entry] != OP_N) + if (reason = cpu_ops (op_eig[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<4:0> */ + +/* EIG module 1 */ + + case 000: /* SAX, SBX 10x740 (OP_A) */ + op[0].word = (op[0].word + XR) & VAMASK; /* indexed addr */ + WriteW (op[0].word, ABREG[absel]); /* store */ + break; + + case 001: /* CAX, CBX 10x741 (OP_N) */ + XR = ABREG[absel]; /* copy to XR */ + break; + + case 002: /* LAX, LBX 10x742 (OP_A) */ + op[0].word = (op[0].word + XR) & VAMASK; /* indexed addr */ + ABREG[absel] = ReadW (op[0].word); /* load */ + break; + + case 003: /* STX 105743 (OP_A) */ + WriteW (op[0].word, XR); /* store XR */ + break; + + case 004: /* CXA, CXB 10x744 (OP_N) */ + ABREG[absel] = XR; /* copy from XR */ + break; + + case 005: /* LDX 105745 (OP_K)*/ + XR = op[0].word; /* load XR */ + break; + + case 006: /* ADX 105746 (OP_K) */ + t = XR + op[0].word; /* add to XR */ + if (t > DMASK) E = 1; /* set E, O */ + if (((~XR ^ op[0].word) & (XR ^ t)) & SIGN) O = 1; + XR = t & DMASK; + break; + + case 007: /* XAX, XBX 10x747 (OP_N) */ + t = XR; /* exchange XR */ + XR = ABREG[absel]; + ABREG[absel] = t; + break; + + case 010: /* SAY, SBY 10x750 (OP_A) */ + op[0].word = (op[0].word + YR) & VAMASK; /* indexed addr */ + WriteW (op[0].word, ABREG[absel]); /* store */ + break; + + case 011: /* CAY, CBY 10x751 (OP_N) */ + YR = ABREG[absel]; /* copy to YR */ + break; + + case 012: /* LAY, LBY 10x752 (OP_A) */ + op[0].word = (op[0].word + YR) & VAMASK; /* indexed addr */ + ABREG[absel] = ReadW (op[0].word); /* load */ + break; + + case 013: /* STY 105753 (OP_A) */ + WriteW (op[0].word, YR); /* store YR */ + break; + + case 014: /* CYA, CYB 10x754 (OP_N) */ + ABREG[absel] = YR; /* copy from YR */ + break; + + case 015: /* LDY 105755 (OP_K) */ + YR = op[0].word; /* load YR */ + break; + + case 016: /* ADY 105756 (OP_K) */ + t = YR + op[0].word; /* add to YR */ + if (t > DMASK) E = 1; /* set E, O */ + if (((~YR ^ op[0].word) & (YR ^ t)) & SIGN) O = 1; + YR = t & DMASK; + break; + + case 017: /* XAY, XBY 10x757 (OP_N) */ + t = YR; /* exchange YR */ + YR = ABREG[absel]; + ABREG[absel] = t; + break; + +/* EIG module 2 */ + + case 020: /* ISX 105760 (OP_N) */ + XR = (XR + 1) & DMASK; /* incr XR */ + if (XR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ + break; + + case 021: /* DSX 105761 (OP_N) */ + XR = (XR - 1) & DMASK; /* decr XR */ + if (XR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ + break; + + case 022: /* JLY 105762 (OP_A) */ + mp_dms_jmp (op[0].word); /* validate jump addr */ + PCQ_ENTRY; + YR = PC; /* ret addr to YR */ + PC = op[0].word; /* jump */ + break; + + case 023: /* LBT 105763 (OP_N) */ + AR = ReadB (BR); /* load byte */ + BR = (BR + 1) & DMASK; /* incr ptr */ + break; + + case 024: /* SBT 105764 (OP_N) */ + WriteB (BR, AR); /* store byte */ + BR = (BR + 1) & DMASK; /* incr ptr */ + break; + + case 025: /* MBT 105765 (OP_KV) */ + wc = ReadW (op[1].word); /* get continuation count */ + if (wc == 0) wc = op[0].word; /* none? get initiation count */ + if ((wc & SIGN) && + (UNIT_CPU_TYPE == UNIT_TYPE_2100)) + break; /* < 0 is NOP for 2100 IOP */ + while (wc != 0) { /* while count */ + WriteW (op[1].word, wc); /* for MP abort */ + t = ReadB (AR); /* move byte */ + WriteB (BR, t); + AR = (AR + 1) & DMASK; /* incr src */ + BR = (BR + 1) & DMASK; /* incr dst */ + wc = (wc - 1) & DMASK; /* decr cnt */ + if (intrq && wc) { /* intr, more to do? */ + PC = err_PC; /* back up PC */ + break; + } + } + WriteW (op[1].word, wc); /* clean up inline */ + break; + + case 026: /* CBT 105766 (OP_KV) */ + wc = ReadW (op[1].word); /* get continuation count */ + if (wc == 0) wc = op[0].word; /* none? get initiation count */ + while (wc != 0) { /* while count */ + WriteW (op[1].word, wc); /* for MP abort */ + v1 = ReadB (AR); /* get src1 */ + v2 = ReadB (BR); /* get src2 */ + if (v1 != v2) { /* compare */ + PC = (PC + 1 + (v1 > v2)) & VAMASK; + BR = (BR + wc) & DMASK; /* update BR */ + wc = 0; /* clr interim */ + break; + } + AR = (AR + 1) & DMASK; /* incr src1 */ + BR = (BR + 1) & DMASK; /* incr src2 */ + wc = (wc - 1) & DMASK; /* decr cnt */ + if (intrq && wc) { /* intr, more to do? */ + PC = err_PC; /* back up PC */ + break; + } + } + WriteW (op[1].word, wc); /* clean up inline */ + break; + + case 027: /* SFB 105767 (OP_N) */ + v1 = AR & 0377; /* test byte */ + v2 = (AR >> 8) & 0377; /* term byte */ + for (;;) { /* scan */ + t = ReadB (BR); /* read byte */ + if (t == v1) break; /* test match? */ + BR = (BR + 1) & DMASK; + if (t == v2) { /* term match? */ + PC = (PC + 1) & VAMASK; + break; + } + if (intrq) { /* int pending? */ + PC = err_PC; /* back up PC */ + break; + } + } + break; + + case 030: /* ISY 105770 (OP_N) */ + YR = (YR + 1) & DMASK; /* incr YR */ + if (YR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ + break; + + case 031: /* DSY 105771 (OP_N) */ + YR = (YR - 1) & DMASK; /* decr YR */ + if (YR == 0) PC = (PC + 1) & VAMASK; /* skip if zero */ + break; + + case 032: /* JPY 105772 (OP_C) */ + op[0].word = (op[0].word + YR) & VAMASK; /* index, no indir */ + mp_dms_jmp (op[0].word); /* validate jump addr */ + PCQ_ENTRY; + PC = op[0].word; /* jump */ + break; + + case 033: /* SBS 105773 (OP_KA) */ + WriteW (op[1].word, /* set bits */ + ReadW (op[1].word) | op[0].word); + break; + + case 034: /* CBS 105774 (OP_KA) */ + WriteW (op[1].word, /* clear bits */ + ReadW (op[1].word) & ~op[0].word); + break; + + case 035: /* TBS 105775 (OP_KK) */ + if ((op[1].word & op[0].word) != op[0].word) /* test bits */ + PC = (PC + 1) & VAMASK; + break; + + case 036: /* CMW 105776 (OP_KV) */ + wc = ReadW (op[1].word); /* get continuation count */ + if (wc == 0) wc = op[0].word; /* none? get initiation count */ + while (wc != 0) { /* while count */ + WriteW (op[1].word, wc); /* for abort */ + v1 = ReadW (AR & VAMASK); /* first op */ + v2 = ReadW (BR & VAMASK); /* second op */ + sop1 = (int32) SEXT (v1); /* signed */ + sop2 = (int32) SEXT (v2); + if (sop1 != sop2) { /* compare */ + PC = (PC + 1 + (sop1 > sop2)) & VAMASK; + BR = (BR + wc) & DMASK; /* update BR */ + wc = 0; /* clr interim */ + break; + } + AR = (AR + 1) & DMASK; /* incr src1 */ + BR = (BR + 1) & DMASK; /* incr src2 */ + wc = (wc - 1) & DMASK; /* decr cnt */ + if (intrq && wc) { /* intr, more to do? */ + PC = err_PC; /* back up PC */ + break; + } + } + WriteW (op[1].word, wc); /* clean up inline */ + break; + + case 037: /* MVW 105777 (OP_KV) */ + wc = ReadW (op[1].word); /* get continuation count */ + if (wc == 0) wc = op[0].word; /* none? get initiation count */ + if ((wc & SIGN) && + (UNIT_CPU_TYPE == UNIT_TYPE_2100)) + break; /* < 0 is NOP for 2100 IOP */ + while (wc != 0) { /* while count */ + WriteW (op[1].word, wc); /* for abort */ + t = ReadW (AR & VAMASK); /* move word */ + WriteW (BR & VAMASK, t); + AR = (AR + 1) & DMASK; /* incr src */ + BR = (BR + 1) & DMASK; /* incr dst */ + wc = (wc - 1) & DMASK; /* decr cnt */ + if (intrq && wc) { /* intr, more to do? */ + PC = err_PC; /* back up PC */ + break; + } + } + WriteW (op[1].word, wc); /* clean up inline */ + break; + + } + +return reason; +} + + +/* 2000 I/O Processor + + The IOP accelerates certain operations of the HP 2000 Time-Share BASIC system + I/O processor. Most 2000 systems were delivered with 2100 CPUs, although IOP + microcode was developed for the 1000-M and 1000-E. As the I/O processors + were specific to the 2000 system, general compatibility with other CPU + microcode options was unnecessary, and indeed no other options were possible + for the 2100. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A 13206A 13207A 22702A N/A + + The routines are mapped to instruction codes as follows: + + Instr. 2100 1000-M/E Description + ------ ---------- ---------- -------------------------------------------- + SAI 105060-117 101400-037 Store A indexed by B (+/- offset in IR<4:0>) + LAI 105020-057 105400-037 Load A indexed by B (+/- offset in IR<4:0>) + CRC 105150 105460 Generate CRC + REST 105340 105461 Restore registers from stack + READF 105220 105462 Read F register (stack pointer) + INS -- 105463 Initialize F register (stack pointer) + ENQ 105240 105464 Enqueue + PENQ 105257 105465 Priority enqueue + DEQ 105260 105466 Dequeue + TRSLT 105160 105467 Translate character + ILIST 105000 105470 Indirect address list (similar to $SETP) + PRFEI 105222 105471 Power fail exit with I/O + PRFEX 105223 105472 Power fail exit + PRFIO 105221 105473 Power fail I/O + SAVE 105362 105474 Save registers to stack + + MBYTE 105120 105765 Move bytes (MBT) + MWORD 105200 105777 Move words (MVW) + SBYTE 105300 105764 Store byte (SBT) + LBYTE 105320 105763 Load byte (LBT) + + The INS instruction was not required in the 2100 implementation because the + stack pointer was actually the memory protect fence register and so could be + loaded directly with an OTA/B 05. Also, the 1000 implementation did not + offer the MBYTE, MWORD, SBYTE, and LBYTE instructions because the equivalent + instructions from the standard Extended Instruction Group were used instead. + Note that the 2100 MBYTE and MWORD instructions operate slightly differently + from the 1000 MBT and MVW instructions. Specifically, the move count is + signed on the 2100 and unsigned on the 1000. A negative count on the 2100 + results in a NOP. + + The simulator remaps the 2100 instructions to the 1000 codes. The four EIG equivalents + are dispatched to the EIG simulator. The rest are handled here. Note that the MBT and + MVW instructions operate slightly differently on the 2100; they are + + Additional reference: + - HP 2000 Computer System Sources and Listings Documentation + (22687-90020, undated), section 3, pages 2-74 through 2-91. +*/ + +static const OP_PAT op_iop[16] = { + OP_V, OP_N, OP_N, OP_N, /* CRC RESTR READF INS */ + OP_N, OP_N, OP_N, OP_V, /* ENQ PENQ DEQ TRSLT */ + OP_AC, OP_CVA, OP_A, OP_CV, /* ILIST PRFEI PRFEX PRFIO */ + OP_N, OP_N, OP_N, OP_N /* SAVE --- --- --- */ + }; + +t_stat cpu_iop (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +uint32 entry; +uint32 hp, tp, i, t, wc, MA; + +if ((cpu_unit.flags & UNIT_IOP) == 0) /* IOP option installed? */ + return stop_inst; + +if (UNIT_CPU_TYPE == UNIT_TYPE_2100) { /* 2100 IOP? */ + if ((IR >= 0105020) && (IR <= 0105057)) /* remap LAI */ + IR = 0105400 | (IR - 0105020); + else if ((IR >= 0105060) && (IR <= 0105117)) /* remap SAI */ + IR = 0101400 | (IR - 0105060); + else { + switch (IR) { /* remap others */ + case 0105000: IR = 0105470; break; /* ILIST */ + case 0105120: return cpu_eig (0105765, intrq); /* MBYTE (maps to MBT) */ + case 0105150: IR = 0105460; break; /* CRC */ + case 0105160: IR = 0105467; break; /* TRSLT */ + case 0105200: return cpu_eig (0105777, intrq); /* MWORD (maps to MVW) */ + case 0105220: IR = 0105462; break; /* READF */ + case 0105221: IR = 0105473; break; /* PRFIO */ + case 0105222: IR = 0105471; break; /* PRFEI */ + case 0105223: IR = 0105472; break; /* PRFEX */ + case 0105240: IR = 0105464; break; /* ENQ */ + case 0105257: IR = 0105465; break; /* PENQ */ + case 0105260: IR = 0105466; break; /* DEQ */ + case 0105300: return cpu_eig (0105764, intrq); /* SBYTE (maps to SBT) */ + case 0105320: return cpu_eig (0105763, intrq); /* LBYTE (maps to LBT) */ + case 0105340: IR = 0105461; break; /* REST */ + case 0105362: IR = 0105474; break; /* SAVE */ + + default: /* all others invalid */ + return stop_inst; + } + } + } + +entry = IR & 077; /* mask to entry point */ + +if (entry <= 037) { /* LAI/SAI 10x400-437 */ + MA = ((entry - 020) + BR) & VAMASK; /* +/- offset */ + if (IR & I_AB) AR = ReadW (MA); /* AB = 1 -> LAI */ + else WriteW (MA, AR); /* AB = 0 -> SAI */ + return reason; + } +else if (entry <= 057) /* IR = 10x440-457? */ + return stop_inst; /* not part of IOP */ + +entry = entry - 060; /* offset 10x460-477 */ + +if (op_iop[entry] != OP_N) + if (reason = cpu_ops (op_iop[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<5:0> */ + + case 000: /* CRC 105460 (OP_V) */ + t = ReadW (op[0].word) ^ (AR & 0377); /* xor prev CRC and char */ + for (i = 0; i < 8; i++) { /* apply polynomial */ + t = (t >> 1) | ((t & 1) << 15); /* rotate right */ + if (t & SIGN) t = t ^ 020001; /* old t<0>? xor */ + } + WriteW (op[0].word, t); /* rewrite CRC */ + break; + + case 001: /* RESTR 105461 (OP_N) */ + iop_sp = (iop_sp - 1) & VAMASK; /* decr stack ptr */ + t = ReadW (iop_sp); /* get E and O */ + O = ((t >> 1) ^ 1) & 1; /* restore O */ + E = t & 1; /* restore E */ + iop_sp = (iop_sp - 1) & VAMASK; /* decr sp */ + BR = ReadW (iop_sp); /* restore B */ + iop_sp = (iop_sp - 1) & VAMASK; /* decr sp */ + AR = ReadW (iop_sp); /* restore A */ + if (UNIT_CPU_MODEL == UNIT_2100) + mp_fence = iop_sp; /* 2100 keeps sp in MP FR */ + break; + + case 002: /* READF 105462 (OP_N) */ + AR = iop_sp; /* copy stk ptr */ + break; + + case 003: /* INS 105463 (OP_N) */ + iop_sp = AR; /* init stk ptr */ + break; + + case 004: /* ENQ 105464 (OP_N) */ + hp = ReadW (AR & VAMASK); /* addr of head */ + tp = ReadW ((AR + 1) & VAMASK); /* addr of tail */ + WriteW ((BR - 1) & VAMASK, 0); /* entry link */ + WriteW ((tp - 1) & VAMASK, BR); /* tail link */ + WriteW ((AR + 1) & VAMASK, BR); /* queue tail */ + if (hp != 0) PC = (PC + 1) & VAMASK; /* q not empty? skip */ + break; + + case 005: /* PENQ 105465 (OP_N) */ + hp = ReadW (AR & VAMASK); /* addr of head */ + WriteW ((BR - 1) & VAMASK, hp); /* becomes entry link */ + WriteW (AR & VAMASK, BR); /* queue head */ + if (hp == 0) /* q empty? */ + WriteW ((AR + 1) & VAMASK, BR); /* queue tail */ + else PC = (PC + 1) & VAMASK; /* skip */ + break; + + case 006: /* DEQ 105466 (OP_N) */ + BR = ReadW (AR & VAMASK); /* addr of head */ + if (BR) { /* queue not empty? */ + hp = ReadW ((BR - 1) & VAMASK); /* read hd entry link */ + WriteW (AR & VAMASK, hp); /* becomes queue head */ + if (hp == 0) /* q now empty? */ + WriteW ((AR + 1) & VAMASK, (AR + 1) & DMASK); + PC = (PC + 1) & VAMASK; /* skip */ + } + break; + + case 007: /* TRSLT 105467 (OP_V) */ + wc = ReadW (op[0].word); /* get count */ + if (wc & SIGN) break; /* cnt < 0? */ + while (wc != 0) { /* loop */ + MA = (AR + AR + ReadB (BR)) & VAMASK; + t = ReadB (MA); /* xlate */ + WriteB (BR, t); /* store char */ + BR = (BR + 1) & DMASK; /* incr ptr */ + wc = (wc - 1) & DMASK; /* decr cnt */ + if (wc && intrq) { /* more and intr? */ + WriteW (op[0].word, wc); /* save count */ + PC = err_PC; /* stop for now */ + break; + } + } + break; + + case 010: /* ILIST 105470 (OP_AC) */ + do { /* for count */ + WriteW (op[0].word, AR); /* write AR to mem */ + AR = (AR + 1) & DMASK; /* incr AR */ + op[0].word = (op[0].word + 1) & VAMASK; /* incr MA */ + op[1].word = (op[1].word - 1) & DMASK; /* decr count */ + } + while (op[1].word != 0); + break; + + case 011: /* PRFEI 105471 (OP_CVA) */ + WriteW (op[1].word, 1); /* set flag */ + reason = iogrp (op[0].word, 0); /* execute I/O instr */ + op[0].word = op[2].word; /* set rtn and fall through */ + + case 012: /* PRFEX 105472 (OP_A) */ + PCQ_ENTRY; + PC = ReadW (op[0].word) & VAMASK; /* jump indirect */ + WriteW (op[0].word, 0); /* clear exit */ + break; + + case 013: /* PRFIO 105473 (OP_CV) */ + WriteW (op[1].word, 1); /* set flag */ + reason = iogrp (op[0].word, 0); /* execute instr */ + break; + + case 014: /* SAVE 105474 (OP_N) */ + WriteW (iop_sp, AR); /* save A */ + iop_sp = (iop_sp + 1) & VAMASK; /* incr stack ptr */ + WriteW (iop_sp, BR); /* save B */ + iop_sp = (iop_sp + 1) & VAMASK; /* incr stack ptr */ + t = ((O ^ 1) << 1) | E; /* merge E and O */ + WriteW (iop_sp, t); /* save E and O */ + iop_sp = (iop_sp + 1) & VAMASK; /* incr stack ptr */ + if (UNIT_CPU_TYPE == UNIT_TYPE_2100) + mp_fence = iop_sp; /* 2100 keeps sp in MP FR */ + break; + + default: /* instruction undefined */ + return stop_inst; + } + +return reason; +} diff --git a/HP2100/hp2100_cpu3.c b/HP2100/hp2100_cpu3.c new file mode 100644 index 0000000..9f6585a --- /dev/null +++ b/HP2100/hp2100_cpu3.c @@ -0,0 +1,820 @@ +/* hp2100_cpu3.c: HP 2100/1000 FFP/DBI instructions + + Copyright (c) 2005-2008, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + CPU3 Fast FORTRAN and Double Integer instructions + + 27-Feb-08 JDB Added DBI self-test instruction + 23-Oct-07 JDB Fixed unsigned-divide bug in .DDI + 17-Oct-07 JDB Fixed unsigned-multiply bug in .DMP + 16-Oct-06 JDB Calls FPP for extended-precision math + 12-Oct-06 JDB Altered DBLE, DDINT for F-Series FFP compatibility + 26-Sep-06 JDB Moved from hp2100_cpu1.c to simplify extensions + 09-Aug-06 JDB Added double-integer instruction set + 18-Feb-05 JDB Add 2100/21MX Fast FORTRAN Processor instructions + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + +#if defined (HAVE_INT64) /* int64 support available */ +#include "hp2100_fp1.h" +#else /* int64 support unavailable */ +#include "hp2100_fp.h" +#endif /* end of int64 support */ + + +t_stat cpu_ffp (uint32 IR, uint32 intrq); /* Fast FORTRAN Processor */ +t_stat cpu_dbi (uint32 IR, uint32 intrq); /* Double-Integer instructions */ + + +/* Fast FORTRAN Processor. + + The Fast FORTRAN Processor (FFP) is a set of FORTRAN language accelerators + and extended-precision (three-word) floating point routines. Although the + FFP is an option for the 2100 and later CPUs, each implements the FFP in a + slightly different form. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A 12907A 12977B 13306B std + + The instruction codes are mapped to routines as follows: + + Instr. 2100 1000-M 1000-E 1000-F Instr. 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ ------ ------ ------ + 105200 -- [nop] [nop] [test] 105220 .XFER .XFER .XFER .XFER + 105201 DBLE DBLE DBLE DBLE 105221 .GOTO .GOTO .GOTO .GOTO + 105202 SNGL SNGL SNGL SNGL 105222 ..MAP ..MAP ..MAP ..MAP + 105203 .XMPY .XMPY .XMPY .DNG 105223 .ENTR .ENTR .ENTR .ENTR + 105204 .XDIV .XDIV .XDIV .DCO 105224 .ENTP .ENTP .ENTP .ENTP + 105205 .DFER .DFER .DFER .DFER 105225 -- .PWR2 .PWR2 .PWR2 + 105206 -- .XPAK .XPAK .XPAK 105226 -- .FLUN .FLUN .FLUN + 105207 -- XADD XADD .BLE 105227 $SETP $SETP $SETP $SETP + + 105210 -- XSUB XSUB .DIN 105230 -- .PACK .PACK .PACK + 105211 -- XMPY XMPY .DDE 105231 -- -- .CFER .CFER + 105212 -- XDIV XDIV .DIS 105232 -- -- -- ..FCM + 105213 .XADD .XADD .XADD .DDS 105233 -- -- -- ..TCM + 105214 .XSUB .XSUB .XSUB .NGL 105234 -- -- -- -- + 105215 -- .XCOM .XCOM .XCOM 105235 -- -- -- -- + 105216 -- ..DCM ..DCM ..DCM 105236 -- -- -- -- + 105217 -- DDINT DDINT DDINT 105237 -- -- -- -- + + The F-Series maps different instructions to several of the standard FFP + opcodes. We first look for these and dispatch them appropriately before + falling into the handler for the common instructions. + + The math functions use the F-Series FPP for implementation. The FPP requires + that the host compiler support 64-bit integers. Therefore, if 64-bit + integers are not available, the math instructions of the FFP are disabled. + We allow this partial implementation as an aid in running systems generated + for the FFP. Most system programs did not use the math instructions, but + almost all use .ENTR. Supporting the latter even on systems that do not + support the former still allows such systems to boot. + + Notes: + + 1. The "$SETP" instruction is sometimes listed as ".SETP" in the + documentation. + + 2. Extended-precision arithmetic routines (e.g., .XMPY) exist on the + 1000-F, but they are assigned instruction codes in the single-precision + floating-point module range. They are replaced by several double + integer instructions, which we dispatch to the double integer handler. + + 3. The software implementation of ..MAP supports 1-, 2-, or 3-dimensional + arrays, designated by setting A = -1, 0, and +1, respectively. The + firmware implementation supports only 2- and 3-dimensional access. + + 4. The documentation for ..MAP for the 2100 FFP shows A = 0 or -1 for two + or three dimensions, respectively, but the 1000 FFP shows A = 0 or +1. + The firmware actually only checks the LSB of A. + + 5. The .DFER and .XFER implementations for the 2100 FFP return X+4 and Y+4 + in the A and B registers, whereas the 1000 FFP returns X+3 and Y+3. + + 6. The .XFER implementation for the 2100 FFP returns to P+2, whereas the + 1000 implementation returns to P+1. + + 7. The firmware implementations of DBLE, .BLE, and DDINT clear the overflow + flag. The software implementations do not change overflow. + + 8. The M/E-Series FFP arithmetic instructions (.XADD, etc.) return negative + infinity on negative overflow and positive infinity on positive + overflow. The equivalent F-Series instructions return positive infinity + on both. + + Additional references: + - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981) + - Implementing the HP 2100 Fast FORTRAN Processor (12907-90010, Nov-1974) +*/ + +static const OP_PAT op_ffp_f[32] = { /* patterns for F-Series only */ + OP_N, OP_AAF, OP_AX, OP_N, /* [tst] DBLE SNGL .DNG */ + OP_N, OP_AA, OP_A, OP_AAF, /* .DCO .DFER .XPAK .BLE */ + OP_N, OP_N, OP_N, OP_N, /* .DIN .DDE .DIS .DDS */ + OP_AT, OP_A, OP_A, OP_AAX, /* .NGL .XCOM ..DCM DDINT */ + OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */ + OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */ + OP_RC, OP_AA, OP_R, OP_A, /* .PACK .CFER ..FCM ..TCM */ + OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */ + }; + +static const OP_PAT op_ffp_e[32] = { /* patterns for 2100/M/E-Series */ + OP_N, OP_AAF, OP_AX, OP_AXX, /* [nop] DBLE SNGL .XMPY */ + OP_AXX, OP_AA, OP_A, OP_AAXX, /* .XDIV .DFER .XPAK XADD */ + OP_AAXX, OP_AAXX, OP_AAXX, OP_AXX, /* XSUB XMPY XDIV .XADD */ + OP_AXX, OP_A, OP_A, OP_AAX, /* .XSUB .XCOM ..DCM DDINT */ + OP_N, OP_AK, OP_KKKK, OP_A, /* .XFER .GOTO ..MAP .ENTR */ + OP_A, OP_RK, OP_R, OP_K, /* .ENTP .PWR2 .FLUN $SETP */ + OP_RC, OP_AA, OP_N, OP_N, /* .PACK .CFER --- --- */ + OP_N, OP_N, OP_N, OP_N /* --- --- --- --- */ + }; + +t_stat cpu_ffp (uint32 IR, uint32 intrq) +{ +OP fpop; +OPS op, op2; +uint32 entry; +uint32 j, sa, sb, sc, da, dc, ra, MA; +int32 expon; +t_stat reason = SCPE_OK; + +#if defined (HAVE_INT64) /* int64 support available */ + +int32 i; + +#endif /* end of int64 support */ + +if ((cpu_unit.flags & UNIT_FFP) == 0) /* FFP option installed? */ + return stop_inst; + +entry = IR & 037; /* mask to entry point */ + +if (UNIT_CPU_MODEL != UNIT_1000_F) { /* 2100/M/E-Series? */ + if (op_ffp_e[entry] != OP_N) + if (reason = cpu_ops (op_ffp_e[entry], op, intrq)) /* get instruction operands */ + return reason; + } + +#if defined (HAVE_INT64) /* int64 support available */ + +else { /* F-Series */ + if (op_ffp_f[entry] != OP_N) + if (reason = cpu_ops (op_ffp_f[entry], op, intrq)) /* get instruction operands */ + return reason; + + switch (entry) { /* decode IR<4:0> */ + + case 000: /* [tst] 105200 (OP_N) */ + XR = 4; /* firmware revision */ + SR = 0102077; /* test passed code */ + AR = 0; /* test clears A/B */ + BR = 0; + PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/DBI */ + return reason; + + case 003: /* .DNG 105203 (OP_N) */ + return cpu_dbi (0105323, intrq); /* remap to double int handler */ + + case 004: /* .DCO 105204 (OP_N) */ + return cpu_dbi (0105324, intrq); /* remap to double int handler */ + + case 007: /* .BLE 105207 (OP_AAF) */ + O = fp_cvt (&op[2], fp_f, fp_t); /* convert value and clear overflow */ + WriteOp (op[1].word, op[2], fp_t); /* write double-precision value */ + return reason; + + case 010: /* .DIN 105210 (OP_N) */ + return cpu_dbi (0105330, intrq); /* remap to double int handler */ + + case 011: /* .DDE 105211 (OP_N) */ + return cpu_dbi (0105331, intrq); /* remap to double int handler */ + + case 012: /* .DIS 105212 (OP_N) */ + return cpu_dbi (0105332, intrq); /* remap to double int handler */ + + case 013: /* .DDS 105213 (OP_N) */ + return cpu_dbi (0105333, intrq); /* remap to double int handler */ + + case 014: /* .NGL 105214 (OP_AT) */ + O = fp_cvt (&op[1], fp_t, fp_f); /* convert value */ + AR = op[1].fpk[0]; /* move MSB to A */ + BR = op[1].fpk[1]; /* move LSB to B */ + return reason; + + case 032: /* ..FCM 105232 (OP_R) */ + O = fp_pcom (&op[0], fp_f); /* complement value */ + AR = op[0].fpk[0]; /* return result */ + BR = op[0].fpk[1]; /* to A/B registers */ + return reason; + + case 033: /* ..TCM 105233 (OP_A) */ + fpop = ReadOp (op[0].word, fp_t); /* read 4-word value */ + O = fp_pcom (&fpop, fp_t); /* complement it */ + WriteOp (op[0].word, fpop, fp_t); /* write 4-word value */ + return reason; + } /* fall thru if not special to F */ + } + +#endif /* end of int64 support */ + +switch (entry) { /* decode IR<4:0> */ + +/* FFP module 1 */ + + case 000: /* [nop] 105200 (OP_N) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 M/E-series */ + return stop_inst; /* trap if not */ + break; + +#if defined (HAVE_INT64) /* int64 support available */ + + case 001: /* DBLE 105201 (OP_AAF) */ + O = fp_cvt (&op[2], fp_f, fp_x); /* convert value and clear overflow */ + WriteOp (op[1].word, op[2], fp_x); /* write extended-precision value */ + break; + + case 002: /* SNGL 105202 (OP_AX) */ + O = fp_cvt (&op[1], fp_x, fp_f); /* convert value */ + AR = op[1].fpk[0]; /* move MSB to A */ + BR = op[1].fpk[1]; /* move LSB to B */ + break; + + case 003: /* .XMPY 105203 (OP_AXX) */ + i = 0; /* params start at op[0] */ + goto XMPY; /* process as XMPY */ + + case 004: /* .XDIV 105204 (OP_AXX) */ + i = 0; /* params start at op[0] */ + goto XDIV; /* process as XDIV */ + +#endif /* end of int64 support */ + + case 005: /* .DFER 105205 (OP_AA) */ + BR = op[0].word; /* get destination address */ + AR = op[1].word; /* get source address */ + goto XFER; /* do transfer */ + +#if defined (HAVE_INT64) /* int64 support available */ + + case 006: /* .XPAK 105206 (OP_A) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + + if (intrq) { /* interrupt pending? */ + PC = err_PC; /* restart instruction */ + break; + } + + fpop = ReadOp (op[0].word, fp_x); /* read unpacked */ + O = fp_nrpack (&fpop, fpop, (int16) AR, fp_x); /* nrm/rnd/pack mantissa, exponent */ + WriteOp (op[0].word, fpop, fp_x); /* write result */ + break; + + case 007: /* XADD 105207 (OP_AAXX) */ + i = 1; /* params start at op[1] */ + XADD: /* enter here from .XADD */ + if (intrq) { /* interrupt pending? */ + PC = err_PC; /* restart instruction */ + break; + } + + O = fp_exec (001, &fpop, op[i + 1], op[i + 2]); /* three-word add */ + WriteOp (op[i].word, fpop, fp_x); /* write sum */ + break; + + case 010: /* XSUB 105210 (OP_AAXX) */ + i = 1; /* params start at op[1] */ + XSUB: /* enter here from .XSUB */ + if (intrq) { /* interrupt pending? */ + PC = err_PC; /* restart instruction */ + break; + } + + O = fp_exec (021, &fpop, op[i + 1], op[i + 2]); /* three-word subtract */ + WriteOp (op[i].word, fpop, fp_x); /* write difference */ + break; + + case 011: /* XMPY 105211 (OP_AAXX) */ + i = 1; /* params start at op[1] */ + XMPY: /* enter here from .XMPY */ + if (intrq) { /* interrupt pending? */ + PC = err_PC; /* restart instruction */ + break; + } + + O = fp_exec (041, &fpop, op[i + 1], op[i + 2]); /* three-word multiply */ + WriteOp (op[i].word, fpop, fp_x); /* write product */ + break; + + case 012: /* XDIV 105212 (OP_AAXX) */ + i = 1; /* params start at op[1] */ + XDIV: /* enter here from .XDIV */ + if (intrq) { /* interrupt pending? */ + PC = err_PC; /* restart instruction */ + break; + } + + O = fp_exec (061, &fpop, op[i + 1], op[i + 2]); /* three-word divide */ + WriteOp (op[i].word, fpop, fp_x); /* write quotient */ + break; + + case 013: /* .XADD 105213 (OP_AXX) */ + i = 0; /* params start at op[0] */ + goto XADD; /* process as XADD */ + + case 014: /* .XSUB 105214 (OP_AXX) */ + i = 0; /* params start at op[0] */ + goto XSUB; /* process as XSUB */ + + case 015: /* .XCOM 105215 (OP_A) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + + fpop = ReadOp (op[0].word, fp_x); /* read unpacked */ + AR = fp_ucom (&fpop, fp_x); /* complement and rtn exp adj */ + WriteOp (op[0].word, fpop, fp_x); /* write result */ + break; + + case 016: /* ..DCM 105216 (OP_A) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + + if (intrq) { /* interrupt pending? */ + PC = err_PC; /* restart instruction */ + break; + } + + fpop = ReadOp (op[0].word, fp_x); /* read operand */ + O = fp_pcom (&fpop, fp_x); /* complement (can't ovf neg) */ + WriteOp (op[0].word, fpop, fp_x); /* write result */ + break; + + case 017: /* DDINT 105217 (OP_AAX) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + + if (intrq) { /* interrupt pending? */ + PC = err_PC; /* restart instruction */ + break; + } + + O = fp_trun (&fpop, op[2], fp_x); /* truncate operand (can't ovf) */ + WriteOp (op[1].word, fpop, fp_x); /* write result */ + break; + +#endif /* end of int64 support */ + +/* FFP module 2 */ + + case 020: /* .XFER 105220 (OP_N) */ + if (UNIT_CPU_TYPE == UNIT_TYPE_2100) + PC = (PC + 1) & VAMASK; /* 2100 .XFER returns to P+2 */ + XFER: /* enter here from .DFER */ + sc = 3; /* set count for 3-wd xfer */ + goto CFER; /* do transfer */ + + case 021: /* .GOTO 105221 (OP_AK) */ + if ((int16) op[1].word < 1) /* index < 1? */ + op[1].word = 1; /* reset min */ + + sa = PC + op[1].word - 1; /* point to jump target */ + if (sa >= op[0].word) /* must be <= last target */ + sa = op[0].word - 1; + + da = ReadW (sa); /* get jump target */ + if (reason = resolve (da, &MA, intrq)) { /* resolve indirects */ + PC = err_PC; /* irq restarts instruction */ + break; + } + + mp_dms_jmp (MA); /* validate jump addr */ + PCQ_ENTRY; /* record last PC */ + PC = MA; /* jump */ + BR = op[0].word; /* (for 2100 FFP compat) */ + break; + + case 022: /* ..MAP 105222 (OP_KKKK) */ + op[1].word = op[1].word - 1; /* decrement 1st subscr */ + + if ((AR & 1) == 0) /* 2-dim access? */ + op[1].word = op[1].word + /* compute element offset */ + (op[2].word - 1) * op[3].word; + else { /* 3-dim access */ + if (reason = cpu_ops (OP_KK, op2, intrq)) { /* get 1st, 2nd ranges */ + PC = err_PC; /* irq restarts instruction */ + break; + } + op[1].word = op[1].word + /* offset */ + ((op[3].word - 1) * op2[1].word + + op[2].word - 1) * op2[0].word; + } + + AR = (op[0].word + op[1].word * BR) & DMASK; /* return element address */ + break; + + case 023: /* .ENTR 105223 (OP_A) */ + MA = PC - 3; /* get addr of entry point */ + ENTR: /* enter here from .ENTP */ + da = op[0].word; /* get addr of 1st formal */ + dc = MA - da; /* get count of formals */ + sa = ReadW (MA); /* get addr of return point */ + ra = ReadW (sa++); /* get rtn, ptr to 1st actual */ + WriteW (MA, ra); /* stuff rtn into caller's ent */ + sc = ra - sa; /* get count of actuals */ + if (sc > dc) /* use min (actuals, formals) */ + sc = dc; + + for (j = 0; j < sc; j++) { + MA = ReadW (sa++); /* get addr of actual */ + if (reason = resolve (MA, &MA, intrq)) { /* resolve indirect */ + PC = err_PC; /* irq restarts instruction */ + break; + } + WriteW (da++, MA); /* put addr into formal */ + } + + AR = ra; /* return address */ + BR = da; /* addr of 1st unused formal */ + break; + + case 024: /* .ENTP 105224 (OP_A) */ + MA = PC - 5; /* get addr of entry point */ + goto ENTR; + + case 025: /* .PWR2 105225 (OP_RK) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + + fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */ + expon = expon + (int16) (op[1].word); /* multiply by 2**n */ + fp_pack (&fpop, fpop, expon, fp_f); /* repack value */ + AR = fpop.fpk[0]; /* return result */ + BR = fpop.fpk[1]; /* to A/B registers */ + break; + + case 026: /* .FLUN 105226 (OP_R) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + + fp_unpack (&fpop, &expon, op[0], fp_f); /* unpack value */ + AR = (int16) expon; /* return expon to A */ + BR = fpop.fpk[1]; /* and low mant to B */ + break; + + case 027: /* $SETP 105227 (OP_K) */ + j = sa = AR; /* save initial value */ + sb = BR; /* save initial address */ + AR = 0; /* AR will return = 0 */ + BR = BR & VAMASK; /* addr must be direct */ + + do { + WriteW (BR, j); /* write value to address */ + j = (j + 1) & DMASK; /* incr value */ + BR = (BR + 1) & VAMASK; /* incr address */ + op[0].word = op[0].word - 1; /* decr count */ + if (op[0].word && intrq) { /* more and intr? */ + AR = sa; /* restore A */ + BR = sb; /* restore B */ + PC = err_PC; /* restart instruction */ + break; + } + } + while (op[0].word != 0); /* loop until count exhausted */ + break; + + case 030: /* .PACK 105230 (OP_RC) */ + if (UNIT_CPU_TYPE != UNIT_TYPE_1000) /* must be 1000 */ + return stop_inst; /* trap if not */ + + O = fp_nrpack (&fpop, op[0], /* nrm/rnd/pack value */ + (int16) (op[1].word), fp_f); + AR = fpop.fpk[0]; /* return result */ + BR = fpop.fpk[1]; /* to A/B registers */ + break; + + case 031: /* .CFER 105231 (OP_AA) */ + if ((UNIT_CPU_MODEL != UNIT_1000_E) && /* must be 1000 E-series */ + (UNIT_CPU_MODEL != UNIT_1000_F)) /* or 1000 F-series */ + return stop_inst; /* trap if not */ + + BR = op[0].word; /* get destination address */ + AR = op[1].word; /* get source address */ + sc = 4; /* set for 4-wd xfer */ + CFER: /* enter here from .XFER */ + for (j = 0; j < sc; j++) { /* xfer loop */ + WriteW (BR, ReadW (AR)); /* transfer word */ + AR = (AR + 1) & VAMASK; /* bump source addr */ + BR = (BR + 1) & VAMASK; /* bump destination addr */ + } + + E = 0; /* routine clears E */ + + if (UNIT_CPU_TYPE == UNIT_TYPE_2100) { /* 2100 (and .DFER/.XFER)? */ + AR = (AR + 1) & VAMASK; /* 2100 FFP returns X+4, Y+4 */ + BR = (BR + 1) & VAMASK; + } + break; + + default: /* others undefined */ + reason = stop_inst; + } + +return reason; +} + + +/* Double-Integer Instructions. + + The double-integer instructions were added to the HP instruction set at + revision 1920 of the 1000-F. They were immediately adopted in a number of HP + software products, most notably the RTE file management package (FMP) + routines. As these routines are used in nearly every RTE program, F-Series + programs were almost always a few hundred bytes smaller than their M- and + E-Series counterparts. This became significant as RTE continued to grow in + size, and some customer programs ran out of address space on E-Series + machines. + + While HP never added double-integer instructions to the standard E-Series, a + product from the HP "specials group," HP 93585A, provided microcoded + replacements for the E-Series. This could provide just enough address-space + savings to allow programs to load in E-Series systems, in addition to + accelerating these common operations. + + There was no equivalent M-Series microcode, due to the limited micromachine + address space on that system. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A 93585A std + + The routines are mapped to instruction codes as follows: + + Instr. 1000-E 1000-F Description + ------ ------ ------ ----------------------------------------- + [test] 105320 -- [self test] + .DAD 105321 105014 Double integer add + .DMP 105322 105054 Double integer multiply + .DNG 105323 105203 Double integer negate + .DCO 105324 105204 Double integer compare + .DDI 105325 105074 Double integer divide + .DDIR 105326 105134 Double integer divide (reversed) + .DSB 105327 105034 Double integer subtract + .DIN 105330 105210 Double integer increment + .DDE 105331 105211 Double integer decrement + .DIS 105332 105212 Double integer increment and skip if zero + .DDS 105333 105213 Double integer decrement and skip if zero + .DSBR 105334 105114 Double integer subtraction (reversed) + + On the F-Series, the double-integer instruction codes are split among the + floating-point processor and the Fast FORTRAN Processor ranges. They are + dispatched from those respective simulators for processing here. + + Notes: + + 1. Opcodes 105335-105337 are NOPs in the microcode. They generate + unimplemented instructions stops under simulation. + + 2. This is an implementation of Revision 2 of the microcode, which was + released as ROM part numbers 93585-80003, 93585-80005, and 93585-80001 + (Revision 1 substituted -80002 for -80005). + + 3. The F-Series firmware executes .DMP and .DDI/.DDIR by floating the + 32-bit double integer to a 48-bit extended-precision number, calling the + FPP to execute the extended-precision multiply/divide, and then fixing + the product to a 32-bit double integer. We simulate these directly with + 64- or 32-bit integer arithmetic. + + Additional references: + - 93585A Microcode Source (93585-18002 Rev. 2005) + - 93585A Double Integer Instructions Installation and Reference Manual + (93585-90007) +*/ + +static const OP_PAT op_dbi[16] = { + OP_N, OP_JD, OP_JD, OP_J, /* [test] .DAD .DMP .DNG */ + OP_JD, OP_JD, OP_JD, OP_JD, /* .DCO .DDI .DDIR .DSB */ + OP_J, OP_J, OP_A, OP_A, /* .DIN .DDE .DIS .DDS */ + OP_JD, OP_N, OP_N, OP_N /* .DSBR --- --- --- */ + }; + +t_stat cpu_dbi (uint32 IR, uint32 intrq) +{ +OP din; +OPS op; +uint32 entry, t; +t_stat reason = SCPE_OK; + +if ((cpu_unit.flags & UNIT_DBI) == 0) /* DBI option installed? */ + return stop_inst; + +entry = IR & 017; /* mask to entry point */ + +if (op_dbi[entry] != OP_N) + if (reason = cpu_ops (op_dbi[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<3:0> */ + + case 000: /* [test] 105320 (OP_N) */ + XR = 2; /* set revision */ + BR = 0377; /* side effect of microcode */ + SR = 0102077; /* set "pass" code */ + PC = (PC + 1) & VAMASK; /* return to P+1 */ + t = (AR << 16) | BR; /* set t for return */ + break; + + case 001: /* .DAD 105321 (OP_JD) */ + t = op[0].dword + op[1].dword; /* add values */ + E = E | (t < op[0].dword); /* carry if result smaller */ + O = (((~op[0].dword ^ op[1].dword) & /* overflow if sign wrong */ + (op[0].dword ^ t) & SIGN32) != 0); + break; + + case 002: /* .DMP 105322 (OP_JD) */ + { + +#if defined (HAVE_INT64) /* int64 support available */ + + t_int64 t64; + + t64 = (t_int64) INT32 (op[0].dword) * /* multiply signed values */ + (t_int64) INT32 (op[1].dword); + O = ((t64 < -(t_int64) 0x80000000) || /* overflow if out of range */ + (t64 > (t_int64) 0x7FFFFFFF)); + if (O) + t = ~SIGN32; /* if overflow, rtn max pos */ + else + t = (uint32) (t64 & DMASK32); /* else lower 32 bits of result */ + +#else /* int64 support unavailable */ + + uint32 sign, xu, yu, rh, rl; + + sign = ((int32) op[0].dword < 0) ^ /* save sign of result */ + ((int32) op[1].dword < 0); + + xu = (uint32) abs ((int32) op[0].dword); /* make operands pos */ + yu = (uint32) abs ((int32) op[1].dword); + + if ((xu & 0xFFFF0000) == 0 && /* 16 x 16 multiply? */ + (yu & 0xFFFF0000) == 0) { + t = xu * yu; /* do it */ + O = 0; /* can't overflow */ + } + + else if ((xu & 0xFFFF0000) != 0 && /* 32 x 32 multiply? */ + (yu & 0xFFFF0000) != 0) + O = 1; /* always overflows */ + + else { /* 16 x 32 or 32 x 16 */ + rl = (xu & 0xFFFF) * (yu & 0xFFFF); /* form 1st partial product */ + + if ((xu & 0xFFFF0000) == 0) + rh = xu * (yu >> 16) + (rl >> 16); /* 16 x 32 2nd partial */ + else + rh = (xu >> 16) * yu + (rl >> 16); /* 32 x 16 2nd partial */ + + O = (rh > 0x7FFF + sign); /* check for out of range */ + if (O == 0) + t = (rh << 16) | (rl & 0xFFFF); /* combine partials */ + } + + if (O) + t = ~SIGN32; /* if overflow, rtn max pos */ + else if (sign) + t = ~t + 1; /* if result neg, 2s compl */ + +#endif /* end of int64 support */ + + } + break; + + case 003: /* .DNG 105323 (OP_J) */ + t = ~op[0].dword + 1; /* negate value */ + O = (op[0].dword == SIGN32); /* overflow if max neg */ + if (op[0].dword == 0) /* borrow if result zero */ + E = 1; + break; + + case 004: /* .DCO 105324 (OP_JD) */ + t = op[0].dword; /* copy for later store */ + if ((int32) op[0].dword < (int32) op[1].dword) + PC = (PC + 1) & VAMASK; /* < rtns to P+2 */ + else if ((int32) op[0].dword > (int32) op[1].dword) + PC = (PC + 2) & VAMASK; /* > rtns to P+3 */ + break; /* = rtns to P+1 */ + + case 005: /* .DDI 105325 (OP_JD) */ + DDI: + O = ((op[1].dword == 0) || /* overflow if div 0 */ + ((op[0].dword == SIGN32) && /* or max neg div -1 */ + ((int32) op[1].dword == -1))); + if (O) + t = ~SIGN32; /* rtn max pos for ovf */ + else + t = (uint32) (INT32 (op[0].dword) / /* else return quotient */ + INT32 (op[1].dword)); + break; + + case 006: /* .DDIR 105326 (OP_JD) */ + t = op[0].dword; /* swap operands */ + op[0].dword = op[1].dword; + op[1].dword = t; + goto DDI; /* continue at .DDI */ + + case 007: /* .DSB 105327 (OP_JD) */ + DSB: + t = op[0].dword - op[1].dword; /* subtract values */ + E = E | (op[0].dword < op[1].dword); /* borrow if minu < subtr */ + O = (((op[0].dword ^ op[1].dword) & /* overflow if sign wrong */ + (op[0].dword ^ t) & SIGN32) != 0); + break; + + case 010: /* .DIN 105330 (OP_J) */ + t = op[0].dword + 1; /* increment value */ + O = (t == SIGN32); /* overflow if sign flipped */ + if (t == 0) + E = 1; /* carry if result zero */ + break; + + case 011: /* .DDE 105331 (OP_J) */ + t = op[0].dword - 1; /* decrement value */ + O = (t == ~SIGN32); /* overflow if sign flipped */ + if ((int32) t == -1) + E = 1; /* borrow if result -1 */ + break; + + case 012: /* .DIS 105332 (OP_A) */ + din = ReadOp (op[0].word, in_d); /* get value */ + t = din.dword = din.dword + 1; /* increment value */ + WriteOp (op[0].word, din, in_d); /* store it back */ + if (t == 0) + PC = (PC + 1) & VAMASK; /* skip if result zero */ + break; + + case 013: /* .DDS 105333 (OP_A) */ + din = ReadOp (op[0].word, in_d); /* get value */ + t = din.dword = din.dword - 1; /* decrement value */ + WriteOp (op[0].word, din, in_d); /* write it back */ + if (t == 0) + PC = (PC + 1) & VAMASK; /* skip if result zero */ + break; + + case 014: /* .DSBR 105334 (OP_JD) */ + t = op[0].dword; /* swap operands */ + op[0].dword = op[1].dword; + op[1].dword = t; + goto DSB; /* continue at .DSB */ + + default: /* others undefined */ + t = (AR << 16) | BR; /* set t for NOP */ + reason = stop_inst; + } + +if (reason == SCPE_OK) { /* if return OK */ + AR = (t >> 16) & DMASK; /* break result */ + BR = t & DMASK; /* into A and B */ + } + +return reason; +} diff --git a/HP2100/hp2100_cpu4.c b/HP2100/hp2100_cpu4.c new file mode 100644 index 0000000..126da35 --- /dev/null +++ b/HP2100/hp2100_cpu4.c @@ -0,0 +1,1129 @@ +/* hp2100_cpu4.c: HP 1000 FPP/SIS + + Copyright (c) 2006-2008, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + CPU4 Floating Point Processor and Scientific Instruction Set + + 18-Mar-08 JDB Fixed B register return bug in /CMRT + 01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64 + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + +#if defined (HAVE_INT64) /* int64 support available */ + +#include "hp2100_fp1.h" + + +t_stat cpu_fpp (uint32 IR, uint32 intrq); /* Floating Point Processor */ +t_stat cpu_sis (uint32 IR, uint32 intrq); /* Scientific Instruction Set */ + +extern t_stat cpu_dbi (uint32 IR, uint32 intrq); /* Double-Integer instructions */ + + +/* Floating-Point Processor. + + The 1000 F-Series replaces the six 2100/1000-M/E single-precision firmware + floating-point instructions with a hardware floating-point processor (FPP). + The FPP executes single-, extended-, and double-precision floating-point + instructions, as well as double-integer instructions. All of the + floating-point instructions, as well as the single- and double-integer fix + and float instructions, are handled here. Pure double-integer instructions + are dispatched to the double-integer handler for simulation. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A N/A std + + For the F-Series, the instruction codes are mapped to routines as follows: + + Instr. 1000-F Description + ------ ------ ------------------------------------- + 105000 FAD Single real add + 105001 .XADD Extended real add + 105002 .TADD Double real add + 105003 [EAD] [5-word add] + 105004 [tst] [Floating Point Processor self test] + 105005 [xpd] [Expand exponent] + 105006 [rst] [Floating Point Processor reset] + 105007 [stk] [Process stack of operands] + 105010 [chk] [FPP addressing check] + 105014 .DAD Double integer add + 105020 FSB Single real subtract + 105021 .XSUB Extended real subtract + 105022 .TSUB Double real subtract + 105023 [ESB] [5-word subtract] + 105034 .DSB Double integer subtract + 105040 FMP Single real multiply + 105041 .XMPY Extended real multiply + 105042 .TMPY Double real multiply + 105043 [EMP] [5-word multiply] + 105054 .DMP Double integer multiply + 105060 FDV Single real divide + 105061 .XDIV Extended real divide + 105062 .TDIV Double real divide + 105063 [EDV] [5-word divide] + 105074 .DDI Double integer divide + 105100 FIX Single real to integer fix + 105101 .XFXS Extended real to integer fix (.DINT) + 105102 .TXFS Double real to integer fix (.TINT) + 105103 [EFS] [5-word FIXS] + 105104 .FIXD Real to double integer fix + 105105 .XFXD Extended real to double integer fix + 105106 .TFXD Double real to double integer fix + 105107 [EFD] [5-word FIXD] + 105114 .DSBR Double integer subtraction (reversed) + 105120 FLT Integer to single real float + 105121 .XFTS Integer to extended real float (.IDBL) + 105122 .TFTS Integer to double real float (.ITBL) + 105123 [ELS] [5-word FLTS] + 105124 .FLTD Double integer to real float + 105125 .XFTD Double integer to extended real float + 105126 .TFTD Double integer to double real float + 105127 [ELD] [5-word FLTD] + 105134 .DDIR Double integer divide (reversed) + + Implementation note: rather than have two simulators that each executes the + single-precision FP instruction set, we compile conditionally, based on the + availability of 64-bit integer support in the host compiler. 64-bit integers + are required for the FPP, so if they are available, then we handle the + single-precision instructions for the 2100 and M/E-Series here, and the + firmware simulation is omitted. If support is unavailable, then the firmware + function is used instead. + + Notes: + + 1. Single-precision arithmetic instructions (.FAD, etc.) and extended- and + double-precision F-Series FPP arithmetic instructions (.XADD, .TADD, + etc.) return positive infinity on both positive and negative overflow. + The equivalent extended-precision M/E-Series FFP instructions return + negative infinity on negative overflow and positive infinity on positive + overflow. + + 2. The items in brackets above are undocumented instructions that are used + by the 12740 FPP-SIS-FFP diagnostic only. + + 3. The five-word arithmetic instructions (e.g., 105003) use an expanded + operand format that dedicates a separate word to the exponent. See the + implementation notes in the hardware floating-point processor simulation + for details. + + 4. The "self test" instruction (105004) returned to P+1 for early F-Series + units without double-integer support. Units incorporating such support + returned to P+2. + + 5. The "expand exponent" instruction (105005) is used as a "prefix" + instruction to enable a 10-bit exponent range. It is placed immediately + before a 5-word arithmetic instruction sequence, e.g., immediately + preceding an EAD instruction sequence. The arithmetic instruction + executes normally, except that under/overflow is not indicated unless + the exponent exceeds the 10-bit range, instead of the normal 8-bit + range. If overflow is indicated, the exponent is still set to +128. + + Note that as 2-, 3-, and 4-word packed numbers only have room for 8-bit + exponents, the Expand Exponent instruction serves no useful purpose in + conjunction with instructions associated with these precisions. If + used, the resulting values may be in error, as overflow from the 8-bit + exponents will not be indicated. + + 6. The "FPP reset" instruction (105006) is provided to reset a hung box, + e.g., in cases where an improper number of parameters is supplied. The + hardware resets its internal state machine in response to this + instruction. Under simulation, the instruction has no effect, as the + simulated FPP cannot hang. + + 7. The "process stack" instruction (105007) executes a series of FPP + instruction sets in sequence. Each set consists of a single FPP + instruction and associated operands that specifies the operation, + followed by a "result" instruction and operand. The result instruction + is optional and is only used to specify the result precision; the + instruction itself is not executed. If the result instruction is NOP, + then the result precision is that of the executed FPP instruction. If + the result operand is null, then the result is kept in the internal FPP + accumulator for later use. + + The calling sequence is as follows: + + STK Process stack instruction + DEF ERRTN Address of error return + DEF SET1 Address of first instruction set + DEF SET2 Address of second instruction set + . + . + . + ERRTN EQU * Return here if execution in error + OKRTN EQU * Return here if execution OK + + Instruction sets are specified as follows (e.g.): + + SET1 .TADD Operation instruction (NOP to terminate series) + DEC 4 Number of words in first operand (or 0 if accum) + DEF OP1 Address of first operand + DEC 4 Number of words in second operand (or 0 if accum) + DEF OP2 Address of second operand + .XADD Result precision conversion instruction (or NOP) + DEC 3 Number of words to store (or 0 if no store) + DEF RSLT Address of buffer to hold value + + The primary use of the "process stack" instruction is to enable chained + operations employing the FPP accumulator for intermediate results and to + enable expanded exponent usage across multiple instructions. + + 8. The "addressing check" instruction sets bit 0 of the L register to 1, + copies the X register value to the FPP, and then reads the FPP and + stores the result in the Y register. Setting the L register bit 0 to 1 + normally deselects the FPP, so that the value in Y is 177777. However, + the FPP box has a strap that inverts the selection logic, even though + the box will not work with the base-set firmware if this is done. The + "addressing check" instruction is provided to test whether the strap is + in the alternate location. Under simulation, the return value is always + 177777, indicating that the strap is correctly set. + + Additional references: + - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981) + - FPP-SIS-FFP Diagnostic Source (12740-18001, Rev. 1926) +*/ + +static const OP_PAT op_fpp[96] = { + OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FAD .XADD .TADD .EADD */ + OP_N, OP_C, OP_N, OP_A, /* [tst] [xpd] [rst] [stk] */ + OP_N, OP_N, OP_N, OP_N, /* [chk] --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* .DAD --- --- --- */ + OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FSB .XSUB .TSUB .ESUB */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* .DSB --- --- --- */ + OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FMP .XMPY .TMPY .EMPY */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* .DMP --- --- --- */ + OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FDV .XDIV .TDIV .EDIV */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* .DDI --- --- --- */ + OP_R, OP_X, OP_T, OP_E, /* FIX .XFXS .TFXS .EFXS */ + OP_R, OP_X, OP_T, OP_E, /* .FIXD .XFXD .TFXD .EFXD */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* .DSBR --- --- --- */ + OP_I, OP_IA, OP_IA, OP_IA, /* FLT .XFTS .TFTS .EFTS */ + OP_J, OP_JA, OP_JA, OP_JA, /* .FLTD .XFTD .TFTD .EFTD */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N /* .DDIR --- --- --- */ + }; + +t_stat cpu_fpp (uint32 IR, uint32 intrq) +{ +OP fpop; +OPS op; +OPSIZE op1_prec, op2_prec, rslt_prec, cvt_prec; +uint16 opcode, rtn_addr, stk_ptr; +uint32 entry; +t_stat reason = SCPE_OK; + +if ((cpu_unit.flags & UNIT_FP) == 0) /* FP option installed? */ + return stop_inst; + +if (UNIT_CPU_MODEL == UNIT_1000_F) /* F-Series? */ + opcode = (uint16) (IR & 0377); /* yes, use full opcode */ +else + opcode = (uint16) (IR & 0160); /* no, use 6 SP FP opcodes */ + +entry = opcode & 0177; /* map to <6:0> */ + +if (op_fpp[entry] != OP_N) + if (reason = cpu_ops (op_fpp[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<6:0> */ + case 0000: /* FAD 105000 (OP_RF) */ + case 0020: /* FSB 105020 (OP_RF) */ + case 0040: /* FMP 105040 (OP_RF) */ + case 0060: /* FDV 105060 (OP_RF) */ + O = fp_exec (opcode, &fpop, op[0], op[1]); /* execute operation */ + AR = fpop.fpk[0]; /* return result to A/B */ + BR = fpop.fpk[1]; + break; + + case 0001: /* .XADD 105001 (OP_AXX) */ + case 0002: /* .TADD 105002 (OP_ATT) */ + case 0003: /* .EADD 105003 (OP_AEE) */ + + case 0021: /* .XSUB 105021 (OP_AXX) */ + case 0022: /* .TSUB 105022 (OP_ATT) */ + case 0023: /* .ESUB 105023 (OP_AEE) */ + + case 0041: /* .XMPY 105041 (OP_AXX) */ + case 0042: /* .TMPY 105042 (OP_ATT) */ + case 0043: /* .EMPY 105043 (OP_AEE) */ + + case 0061: /* .XDIV 105061 (OP_AXX) */ + case 0062: /* .TDIV 105062 (OP_ATT) */ + case 0063: /* .EDIV 105063 (OP_AEE) */ + O = fp_exec (opcode, &fpop, op[1], op[2]); /* execute operation */ + fp_prec (opcode, NULL, NULL, &rslt_prec); /* determine result precision */ + WriteOp (op[0].word, fpop, rslt_prec); /* write result */ + break; + + case 0004: /* [tst] 105004 (OP_N) */ + XR = 3; /* firmware revision */ + SR = 0102077; /* test passed code */ + PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/DBI */ + break; + + case 0005: /* [xpd] 105005 (OP_C) */ + return cpu_fpp (op[0].word | 0200, intrq); /* set bit 7, execute instr */ + + case 0006: /* [rst] 105006 (OP_N) */ + break; /* do nothing for FPP reset */ + + case 0007: /* [stk] 105007 (OP_A) */ + O = 0; /* clear overflow */ + stk_ptr = PC; /* save ptr to next buf */ + rtn_addr = op[0].word; /* save return address */ + + while (TRUE) { + PC = ReadW (stk_ptr) & VAMASK; /* point at next instruction set */ + stk_ptr = (stk_ptr + 1) & VAMASK; + + reason = cpu_ops (OP_CCACACCA, op, intrq); /* get instruction set */ + + if (reason) { + PC = err_PC; /* irq restarts */ + break; + } + + if (op[0].word == 0) { /* opcode = NOP? */ + PC = (rtn_addr + 1) & VAMASK; /* bump to good return */ + break; /* done */ + } + + fp_prec ((uint16) (op[0].word & 0377), /* determine operand precisions */ + &op1_prec, &op2_prec, &rslt_prec); + + if (TO_COUNT(op1_prec) != op[1].word) { /* first operand precisions agree? */ + PC = rtn_addr; /* no, so take error return */ + break; + } + + else if (op1_prec != fp_a) /* operand in accumulator? */ + op[1] = ReadOp (op[2].word, op1_prec); /* no, so get operand 1 */ + + if (TO_COUNT(op2_prec) != op[3].word) { /* second operand precisions agree? */ + PC = rtn_addr; /* no, so take error return */ + break; + } + + else if (op2_prec != fp_a) /* operand in accumulator? */ + op[2] = ReadOp (op[4].word, op2_prec); /* no, so get operand 2 */ + + O = O | /* execute instruction */ + fp_exec ((uint16) (op[0].word & 0377), /* and accumulate overflow */ + &fpop, op[1], op[2]); + + if (op[5].word) { /* precision conversion? */ + fp_prec ((uint16) (op[5].word & 0377), /* determine conversion precision */ + NULL, NULL, &cvt_prec); + + fpop = fp_accum (NULL, cvt_prec); /* convert result */ + } + else /* no conversion specified */ + cvt_prec = rslt_prec; /* so use original precision */ + + if (op[6].word) /* store result? */ + WriteOp (op[7].word, fpop, cvt_prec); /* yes, so write it */ + } + + break; + + case 0010: /* [chk] 105010 (OP_N) */ + YR = 0177777; /* -1 if selection strap OK */ + break; + + case 0014: /* .DAD 105014 (OP_N) */ + return cpu_dbi (0105321, intrq); /* remap to double int handler */ + + case 0034: /* .DSB 105034 (OP_N) */ + return cpu_dbi (0105327, intrq); /* remap to double int handler */ + + case 0054: /* .DMP 105054 (OP_N) */ + return cpu_dbi (0105322, intrq); /* remap to double int handler */ + + case 0074: /* .DDI 105074 (OP_N) */ + return cpu_dbi (0105325, intrq); /* remap to double int handler */ + + case 0100: /* FIX 105100 (OP_R) */ + case 0101: /* .XFXS 105101 (OP_X) */ + case 0102: /* .TFXS 105102 (OP_T) */ + case 0103: /* .EFXS 105103 (OP_E) */ + O = fp_exec (opcode, &fpop, op[0], NOP); /* fix to integer */ + AR = fpop.fpk[0]; /* save result */ + break; + + case 0104: /* .FIXD 105104 (OP_R) */ + case 0105: /* .XFXD 105105 (OP_X) */ + case 0106: /* .TFXD 105106 (OP_T) */ + case 0107: /* .EFXD 105107 (OP_E) */ + O = fp_exec (opcode, &fpop, op[0], NOP); /* fix to integer */ + AR = (fpop.dword >> 16) & DMASK; /* save result */ + BR = fpop.dword & DMASK; /* in A and B */ + break; + + case 0114: /* .DSBR 105114 (OP_N) */ + return cpu_dbi (0105334, intrq); /* remap to double int handler */ + + case 0120: /* FLT 105120 (OP_I) */ + case 0124: /* .FLTD 105124 (OP_J) */ + O = fp_exec (opcode, &fpop, op[0], NOP); /* float to single */ + AR = fpop.fpk[0]; /* save result */ + BR = fpop.fpk[1]; /* into A/B */ + break; + + case 0121: /* .XFTS 105121 (OP_IA) */ + case 0122: /* .TFTS 105122 (OP_IA) */ + case 0123: /* .EFTS 105123 (OP_IA) */ + case 0125: /* .XFTD 105125 (OP_JA) */ + case 0126: /* .TFTD 105126 (OP_JA) */ + case 0127: /* .EFTD 105127 (OP_JA) */ + O = fp_exec (opcode, &fpop, op[0], NOP); /* float integer */ + fp_prec (opcode, NULL, NULL, &rslt_prec); /* determine result precision */ + WriteOp (op[1].word, fpop, rslt_prec); /* write result */ + break; + + case 0134: /* .DDIR 105134 (OP_N) */ + return cpu_dbi (0105326, intrq); /* remap to double int handler */ + + default: /* others undefined */ + reason = stop_inst; + } + +return reason; +} + + +/* Scientific Instruction Set. + + The SIS adds single-precision trigonometric and logarithmic, and + double-precision polynomial evaluation instructions to the 1000-F instruction + set. The SIS is standard on the 1000-F. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A N/A std + + The routines are mapped to instruction codes as follows: + + Instr. 1000-F Description + ------ ------ ---------------------------------------------- + TAN 105320 Tangent + SQRT 105321 Square root + ALOG 105322 Natural logarithm + ATAN 105323 Arc tangent + COS 105324 Cosine + SIN 105325 Sine + EXP 105326 E to the power X + ALOGT 105327 Common logarithm + TANH 105330 Hyperbolic tangent + DPOLY 105331 Double-precision polynomial evaluation + /CMRT 105332 Double-precision common range reduction + /ATLG 105333 Compute (1-x)/(1+x) for .ATAN and .LOG + .FPWR 105334 Single-precision exponentiation + .TPWR 105335 Double-precision exponentiation + [tst] 105337 [self test] + + The SIS simulation follows the F-Series SIS microcode, which, in turn, + follows the algebraic approximations given in the Relocatable Library manual + descriptions of the equivalent software routines. + + Notes: + + 1. The word following the DPOLY instruction contains up to three flag bits + to indicate one of several polynomial forms to evaluate. The comments + in the DPOLY software library routine source interchange the actions of + the bit 14 and bit 0 flags. The DPOLY description in the Technical + Reference Handbook is correct. + + 2. Several instructions (e.g., DPOLY) are documented as leaving undefined + values in the A, B, X, Y, E, or O registers. Simulation does not + attempt to reproduce the same values as would be obtained with the + hardware. + + 3. The SIS uses the hardware FPP of the F-Series. FPP malfunctions are + detected by the SIS firmware and are indicated by a memory-protect + violation and setting the overflow flag. Under simulation, + malfunctions cannot occur. + + 4. We use OP_IIT for the .FPWR operand pattern. The "II" is redundant, but + it aligns the operands with the OP_IAT of .TPWR, so the code may be + shared. + + Additional references: + - DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981) + - HP 1000 E-Series and F-Series Computer Microprogramming Reference Manual + (02109-90004, Apr-1980). +*/ + + +/* Common single-precision range reduction for SIN, COS, TAN, and EXP. + + This routine is called by the SIN, COS, TAN, and EXP handlers to reduce the + range of the argument. Reduction is performed in extended-precision. We + calculate: + + multiple = (nearest even integer to argument * multiplier) + argument = argument * multiplier - multiple +*/ + +static uint32 reduce (OP *argument, int32 *multiple, OP multiplier) +{ +OP product, count; +uint32 overflow; + +fp_cvt (argument, fp_f, fp_x); /* convert to extended precision */ +fp_exec (0041, &product, *argument, multiplier); /* product = argument * multiplier */ +overflow = fp_exec (0111, &count, NOP, NOP); /* count = FIX (acc) */ + +if ((int16) count.word >= 0) /* nearest even integer */ + count.word = count.word + 1; +count.word = count.word & ~1; +*multiple = (int16) count.word; + +if (overflow == 0) { /* in range? */ + fp_exec (0121, ACCUM, count, NOP); /* acc = FLT (count) */ + overflow = fp_exec (0025, ACCUM, product, NOP); /* acc = product - acc */ + *argument = fp_accum (NULL, fp_f); /* trim to single-precision */ + } +return overflow; +} + + +/* SIS dispatcher. */ + +static const OP_PAT op_sis[16] = { + OP_R, OP_R, OP_R, OP_R, /* TAN SQRT ALOG ATAN */ + OP_R, OP_R, OP_R, OP_R, /* COS SIN EXP ALOGT */ + OP_R, OP_CATAKK, OP_AAT, OP_A, /* TANH DPOLY /CMRT /ATLG */ + OP_IIF, OP_IAT, OP_N, OP_N /* .FPWR .TPWR --- [tst] */ + }; + +t_stat cpu_sis (uint32 IR, uint32 intrq) +{ +OPS op; +OP arg, coeff, pwr, product, count, result; +int16 f, p; +int32 multiple, power, exponent, rsltexp; +uint32 entry, i; +t_bool flag, sign; +t_stat reason = SCPE_OK; + +static const OP tan_c4 = { { 0137763, 0051006 } }; /* DEC -4.0030956 */ +static const OP tan_c3 = { { 0130007, 0051026 } }; /* DEC -1279.5424 */ +static const OP tan_c2 = { { 0040564, 0012761 } }; /* DEC 0.0019974806 */ +static const OP tan_c1 = { { 0045472, 0001375 } }; /* DEC 0.14692695 */ + +static const OP alog_c3 = { { 0065010, 0063002 } }; /* DEC 1.6567626301 */ +static const OP alog_c2 = { { 0125606, 0044404 } }; /* DEC -2.6398577035 */ +static const OP alog_c1 = { { 0051260, 0037402 } }; /* DEC 1.2920070987 */ + +static const OP atan_c4 = { { 0040257, 0154404 } }; /* DEC 2.0214656 */ +static const OP atan_c3 = { { 0132062, 0133406 } }; /* DEC -4.7376165 */ +static const OP atan_c2 = { { 0047407, 0173775 } }; /* DEC 0.154357652 */ +static const OP atan_c1 = { { 0053447, 0014002 } }; /* DEC 1.3617611 */ + +static const OP sin_c4 = { { 0132233, 0040745 } }; /* DEC -0.000035950439 */ +static const OP sin_c3 = { { 0050627, 0122361 } }; /* DEC 0.002490001 */ +static const OP sin_c2 = { { 0126521, 0011373 } }; /* DEC -0.0807454325 */ +static const OP sin_c1 = { { 0062207, 0166400 } }; /* DEC 0.78539816 */ + +static const OP cos_c4 = { { 0126072, 0002753 } }; /* DEC -0.00031957 */ +static const OP cos_c3 = { { 0040355, 0007767 } }; /* DEC 0.015851077 */ +static const OP cos_c2 = { { 0130413, 0011377 } }; /* DEC -0.30842483 */ +static const OP cos_c1 = { { 0040000, 0000002 } }; /* DEC 1.0 */ + +static const OP sqrt_a2 = { { 0045612, 0067400 } }; /* DEC 0.5901621 */ +static const OP sqrt_b2 = { { 0065324, 0126377 } }; /* DEC 0.4173076 */ +static const OP sqrt_a1 = { { 0065324, 0126400 } }; /* DEC 0.8346152 */ +static const OP sqrt_b1 = { { 0045612, 0067400 } }; /* DEC 0.5901621 */ + +static const OP exp_c2 = { { 0073000, 0070771 } }; /* DEC 0.05761803 */ +static const OP exp_c1 = { { 0056125, 0041406 } }; /* DEC 5.7708162 */ + +static const OP tanh_c3 = { { 0050045, 0022004 } }; /* DEC 2.5045337 */ +static const OP tanh_c2 = { { 0041347, 0101404 } }; /* DEC 2.0907609 */ +static const OP tanh_c1 = { { 0052226, 0047375 } }; /* DEC 0.16520923 */ + +static const OP minus_1 = { { 0100000, 0000000 } }; /* DEC -1.0 */ +static const OP plus_1 = { { 0040000, 0000002 } }; /* DEC +1.0 */ +static const OP plus_half = { { 0040000, 0000000 } }; /* DEC +0.5 */ +static const OP ln_2 = { { 0054271, 0006000 } }; /* DEC 0.6931471806 (ln 2.0) */ +static const OP log_e = { { 0067455, 0166377 } }; /* DEC 0.43429228 (log e) */ +static const OP pi_over_4 = { { 0062207, 0166400 } }; /* Pi / 4.0 */ +static const OP pi_over_2 = { { 0062207, 0166402 } }; /* Pi / 2.0 */ + +static const OP four_over_pi = { { 0050574, 0140667, 0023402 } }; /* 4.0 / Pi */ +static const OP two_over_ln2 = { { 0056125, 0016624, 0127404 } }; /* 2.0 / ln(2.0) */ + +static const OP t_one = { { 0040000, 0000000, 0000000, 0000002 } }; /* DEY 1.0 */ + + +if (UNIT_CPU_MODEL != UNIT_1000_F) /* F-Series? */ + return stop_inst; + +entry = IR & 017; /* mask to entry point */ + +if (op_sis[entry] != OP_N) + if (reason = cpu_ops (op_sis[entry], op, intrq)) /* get instruction operands */ + return reason; + +switch (entry) { /* decode IR<3:0> */ + + case 000: /* TAN 105320 (OP_R) */ + O = reduce (&op[0], &multiple, four_over_pi); /* reduce range */ + + if (O) { /* out of range? */ + op[0].fpk[0] = '0' << 8 | '9'; /* return '09' */ + op[0].fpk[1] = 'O' << 8 | 'R'; /* return 'OR' */ + break; /* error return is P+1 */ + } + + fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg ^ 2 */ + fp_exec (0010, ACCUM, NOP, tan_c4); /* acc = acc + C4 */ + fp_exec (0064, ACCUM, tan_c3, NOP); /* acc = C3 / acc */ + fp_exec (0010, ACCUM, NOP, op[1]); /* acc = acc + op1 */ + fp_exec (0050, ACCUM, NOP, tan_c2); /* acc = acc * C2 */ + fp_exec (0010, ACCUM, NOP, tan_c1); /* acc = acc + C1 */ + fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */ + + if (multiple & 0002) /* multiple * 2 odd? */ + fp_exec (0064, &op[0], minus_1, NOP); /* res = -1.0 / acc */ + + PC = (PC + 1) & VAMASK; /* normal return is P+2 */ + break; + + + case 001: /* SQRT 105321 (OP_R) */ + O = 0; /* clear overflow */ + + if (op[0].fpk[0] == 0) { /* arg = 0? */ + PC = (PC + 1) & VAMASK; /* normal return is P+2 */ + break; + } + + else if ((int16) op[0].fpk[0] < 0) { /* sqrt of neg? */ + op[0].fpk[0] = '0' << 8 | '3'; /* return '03' */ + op[0].fpk[1] = 'U' << 8 | 'N'; /* return 'UN' */ + O = 1; /* set overflow */ + break; /* error return is P+1 */ + } + + fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ + + if (exponent & 1) { /* exponent odd? */ + fp_exec (0040, ACCUM, op[1], sqrt_a1); /* acc = op1 * A1 */ + fp_exec (0010, &op[2], NOP, sqrt_b1); /* op2 = acc + B1 */ + op[1].fpk[1] = op[1].fpk[1] + 2; /* op1 = op1 * 2.0 */ + } + else { /* exponent even */ + fp_exec (0040, ACCUM, op[1], sqrt_a2); /* acc = op1 * A2 */ + fp_exec (0010, &op[2], NOP, sqrt_b2); /* op2 = acc + B2 */ + } + + fp_exec (0064, ACCUM, op[1], NOP); /* acc = op1 / acc */ + fp_exec (0010, &op[2], NOP, op[2]); /* op2 = acc + op2 */ + + op[1].fpk[1] = op[1].fpk[1] + 4; /* op1 = op1 * 4.0 */ + + fp_exec (0064, ACCUM, op[1], NOP); /* acc = op1 / acc */ + fp_exec (0010, &op[0], NOP, op[2]); /* res = acc + op2 */ + + power = (exponent >> 1) - 2; + + if (op[0].fpk[0]) { /* calc x * 2**n */ + fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ + exponent = exponent + power; /* multiply by 2**n */ + + if ((exponent > 0177) || /* exponent overflow? */ + (exponent < -0200)) { /* or underflow? */ + O = 1; /* rtn unscaled val, set ovf */ + break; /* error return is P+1 */ + } + + else + fp_pack (&op[0], op[1], exponent, fp_f);/* repack result */ + } + + PC = (PC + 1) & VAMASK; /* normal return is P+2 */ + break; + + + case 002: /* ALOG 105322 (OP_R) */ + case 007: /* ALOGT 105327 (OP_R) */ + O = 0; /* clear overflow */ + + if ((int16) op[0].fpk[0] <= 0) { /* log of neg or zero? */ + op[0].fpk[0] = '0' << 8 | '2'; /* return '02' */ + op[0].fpk[1] = 'U' << 8 | 'N'; /* return 'UN' */ + O = 1; /* set overflow */ + break; /* error return is P+1 */ + } + + fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ + + if (op[0].fpk[0] < 0055000) { /* out of range? */ + exponent = exponent - 1; /* drop exponent */ + op[1].fpk[1] = op[1].fpk[1] | 2; /* set "exponent" to 1 */ + } + + op[2].fpk[0] = exponent; + fp_exec (0120, &op[3], op[2], NOP); /* op3 = FLT(exponent) */ + + fp_exec (0020, &op[4], op[1], plus_1); /* op4 = op1 - 1.0 */ + fp_exec (0000, ACCUM, op[1], plus_1); /* acc = op1 + 1.0 */ + fp_exec (0064, &op[5], op[4], NOP); /* op5 = op4 / acc */ + + fp_exec (0054, ACCUM, NOP, NOP); /* acc = acc * acc */ + fp_exec (0030, ACCUM, NOP, alog_c3); /* acc = acc - c3 */ + fp_exec (0064, ACCUM, alog_c2, NOP); /* acc = c2 / acc */ + fp_exec (0010, ACCUM, NOP, alog_c1); /* acc = acc + c1 */ + fp_exec (0050, ACCUM, NOP, op[5]); /* acc = acc * op5 */ + fp_exec (0010, ACCUM, NOP, op[3]); /* acc = acc + op3 */ + fp_exec (0050, &op[0], NOP, ln_2); /* res = acc * ln2 */ + + if (entry == 007) /* ALOGT? */ + fp_exec (0050, &op[0], NOP, log_e); /* res = acc * log(e) */ + + PC = (PC + 1) & VAMASK; /* normal return is P+2 */ + break; + + + case 003: /* ATAN 105323 (OP_R) */ + O = 0; /* clear overflow */ + + if (op[0].fpk[0] == 0) /* argument zero? */ + break; /* result zero */ + + flag = (op[0].fpk[1] & 1); /* get exponent sign */ + sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */ + + if (flag == 0) { /* exp pos? (abs >= 0.5)? */ + if (sign) /* argument negative? */ + fp_pcom (&op[0], fp_f); /* make positive */ + + if (op[0].fpk[1] & 0374) { /* arg >= 2? */ + fp_exec(0060, &op[0], plus_1, op[0]); /* arg = 1.0 / arg */ + op[2] = pi_over_2; /* constant = pi / 2.0 */ + } + else { + fp_exec (0020, &op[1], plus_1, op[0]); /* op1 = 1.0 - arg */ + fp_exec (0000, ACCUM, plus_1, op[0]); /* acc = 1.0 + arg */ + fp_exec (0064, &op[0], op[1], NOP); /* arg = op1 / acc */ + op[2] = pi_over_4; /* constant = pi / 4.0 */ + } + } + + fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg * arg */ + fp_exec (0010, ACCUM, NOP, atan_c4); /* acc = acc + C4 */ + fp_exec (0064, ACCUM, atan_c3, NOP); /* acc = C3 / acc */ + fp_exec (0010, ACCUM, NOP, op[1]); /* acc = acc + op1 */ + fp_exec (0050, ACCUM, NOP, atan_c2); /* acc = acc * C2 */ + fp_exec (0010, ACCUM, NOP, atan_c1); /* acc = acc + C1 */ + fp_exec (0064, &op[0], op[0], NOP); /* res = arg / acc */ + + if (flag == 0) { /* exp pos? (abs >= 0.5)? */ + fp_exec (0030, &op[0], NOP, op[2]); /* res = acc - pi / n */ + + if (sign == 0) /* argument positive? */ + fp_pcom (&op[0], fp_f); /* make negative */ + } + + break; + + + case 004: /* COS 105324 (OP_R) */ + case 005: /* SIN 105325 (OP_R) */ + O = reduce (&op[0], &multiple, four_over_pi); /* reduce range */ + + if (O) { /* out of range? */ + op[0].fpk[0] = '0' << 8 | '5'; /* return '05' */ + op[0].fpk[1] = 'O' << 8 | 'R'; /* return 'OR' */ + break; /* error return is P+1 */ + } + + multiple = multiple / 2 + (entry == 004); /* add one for cosine */ + flag = (multiple & 1); /* decide on series */ + + fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg ^ 2 */ + + if (flag) { + fp_exec (0050, ACCUM, NOP, cos_c4); /* acc = acc * c4 */ + fp_exec (0010, ACCUM, NOP, cos_c3); /* acc = acc + c3 */ + fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ + fp_exec (0010, ACCUM, NOP, cos_c2); /* acc = acc + c2 */ + fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ + fp_exec (0010, &op[0], NOP, cos_c1); /* res = acc + c1 */ + } + + else { + fp_exec (0050, ACCUM, NOP, sin_c4); /* acc = acc * c4 */ + fp_exec (0010, ACCUM, NOP, sin_c3); /* acc = acc + c3 */ + fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ + fp_exec (0010, ACCUM, NOP, sin_c2); /* acc = acc + c2 */ + fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */ + fp_exec (0010, ACCUM, NOP, sin_c1); /* acc = acc + c1 */ + fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */ + } + + if (multiple & 0002) /* multiple * 2 odd? */ + fp_pcom (&op[0], fp_f); /* make negative */ + + PC = (PC + 1) & VAMASK; /* normal return is P+2 */ + break; + + + case 006: /* EXP 105326 (OP_R) */ + sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */ + + O = reduce (&op[0], &multiple, two_over_ln2); /* reduce range */ + multiple = multiple / 2; /* get true multiple */ + + if ((sign == 0) && (O | (multiple > 128))) { /* pos and ovf or out of range? */ + op[0].fpk[0] = '0' << 8 | '7'; /* return '07' */ + op[0].fpk[1] = 'O' << 8 | 'F'; /* return 'OF' */ + O = 1; /* set overflow */ + break; /* error return is P+1 */ + } + + else if (sign && (multiple < -128)) { /* neg and out of range? */ + op[0].fpk[0] = 0; /* result is zero */ + op[0].fpk[1] = 0; + O = 0; /* clear for underflow */ + PC = (PC + 1) & VAMASK; /* normal return is P+2 */ + break; + } + + fp_exec (0040, ACCUM, op[0], op[0]); /* acc = arg ^ 2 */ + fp_exec (0050, ACCUM, NOP, exp_c2); /* acc = acc * c2 */ + fp_exec (0030, ACCUM, NOP, op[0]); /* acc = acc - op0 */ + fp_exec (0010, ACCUM, NOP, exp_c1); /* acc = acc + c1 */ + fp_exec (0064, ACCUM, op[0], NOP); /* acc = op0 / acc */ + fp_exec (0010, &op[0], NOP, plus_half); /* res = acc + 0.5 */ + + power = multiple + 1; + + if (op[0].fpk[0]) { /* calc x * 2**n */ + fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */ + exponent = exponent + power; /* multiply by 2**n */ + + if ((exponent > 0177) || /* exponent overflow? */ + (exponent < -0200)) { /* or underflow? */ + if (sign == 0) { /* arg positive? */ + op[0].fpk[0] = '0' << 8 | '7'; /* return '07' */ + op[0].fpk[1] = 'O' << 8 | 'F'; /* return 'OF' */ + O = 1; /* set overflow */ + } + else { + op[0].fpk[0] = 0; /* result is zero */ + op[0].fpk[1] = 0; + O = 0; /* clear for underflow */ + } + break; /* error return is P+1 */ + } + + else { + fp_pack (&op[0], op[1], exponent, fp_f);/* repack value */ + O = 0; + } + } + + PC = (PC + 1) & VAMASK; /* normal return is P+2 */ + break; + + + case 010: /* TANH 105330 (OP_R) */ + O = 0; + sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */ + + if (op[0].fpk[1] & 1) { /* abs (arg) < 0.5? */ + fp_exec (0040, ACCUM, op[0], op[0]); /* acc = arg ^ 2 */ + fp_exec (0010, ACCUM, NOP, tanh_c3); /* acc = acc + c3 */ + fp_exec (0064, ACCUM, tanh_c2, NOP); /* acc = c2 / acc */ + fp_exec (0010, ACCUM, NOP, tanh_c1); /* acc = acc + c1 */ + fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */ + } + + else if (op[0].fpk[1] & 0370) /* abs (arg) >= 8.0? */ + if (sign) /* arg negative? */ + op[0] = minus_1; /* result = -1.0 */ + else /* arg positive */ + op[0] = plus_1; /* result = +1.0 */ + + else { /* 0.5 <= abs (arg) < 8.0 */ + BR = BR + 2; /* arg = arg * 2.0 */ + cpu_sis (0105326, intrq); /* calc exp (arg) */ + PC = (PC - 1) & VAMASK; /* correct P (always good rtn) */ + + op[0].fpk[0] = AR; /* save value */ + op[0].fpk[1] = BR; + + fp_exec (0020, &op[1], op[0], plus_1); /* op1 = op0 - 1.0 */ + fp_exec (0000, ACCUM, op[0], plus_1); /* acc = op0 + 1.0 */ + fp_exec (0064, &op[0], op[1], NOP); /* res = op1 / acc */ + } + + break; + + + case 011: /* DPOLY 105331 (OP_CATAKK) */ + O = 0; /* clear overflow */ + AR = op[0].word; /* get flag word */ + + if ((int16) AR >= 0) { /* flags present? */ + AR = 1; /* no, so set default */ + arg = op[2]; /* arg = X */ + } + + else /* bit 15 set */ + fp_exec (0042, &arg, op[2], op[2]); /* arg = X ^ 2 */ + + coeff = ReadOp (op[3].word, fp_t); /* get first coefficient */ + op[3].word = (op[3].word + 4) & VAMASK; /* point at next */ + fp_accum (&coeff, fp_t); /* acc = coeff */ + + for (i = 0; i < op[4].word; i++) { /* compute numerator */ + fp_exec (0052, ACCUM, NOP, arg); /* acc = P[m] * arg */ + coeff = ReadOp (op[3].word, fp_t); /* get next coefficient */ + op[3].word = (op[3].word + 4) & VAMASK; /* point at next */ + fp_exec (0012, ACCUM, NOP, coeff); /* acc = acc + P[m-1] */ + } + + if (AR & 1) /* bit 0 set? */ + op[6] = fp_accum (NULL, fp_t); /* save numerator */ + else + fp_exec (0046, &op[6], op[2], NOP); /* acc = X * acc */ + + + if (op[5].word) { /* n > 0 ? */ + fp_accum (&t_one, fp_t); /* acc = 1.0 */ + + for (i = 0; i < op[5].word; i++) { /* compute denominator */ + fp_exec (0052, ACCUM, NOP, arg); /* acc = P[m] * arg */ + coeff = ReadOp (op[3].word, fp_t); /* get next coefficient */ + op[3].word = (op[3].word + 4) & VAMASK; /* point at next */ + fp_exec (0012, ACCUM, NOP, coeff); /* acc = acc + P[m-1] */ + } + + if (AR & 0040000) /* bit 14 set? */ + fp_exec (0032, ACCUM, NOP, op[6]); /* acc = den - num */ + + fp_exec (0066, &op[6], op[6], NOP); /* op6 = num / den */ + } + + WriteOp (op[1].word, op[6], fp_t); /* write result */ + + if (O) /* overflow? */ + op[0].fpk[0] = 0; /* microcode rtns with A = 0 */ + break; + + + case 012: /* /CMRT 105332 (OP_AAT) */ + O = 0; + f = (int16) AR; /* save flags */ + + coeff = ReadOp (op[1].word, fp_t); /* get coefficient (C) */ + + fp_unpack (NULL, &exponent, op[2], fp_t); /* unpack exponent */ + + if ((f == -1) || (exponent < 4)) { /* TANH or abs (arg) < 16.0? */ + + /* result = x * c - n */ + + fp_exec (0042, &product, op[2], coeff); /* product = arg * C */ + O = fp_exec (0112, &count, NOP, NOP); /* count = FIX (acc) */ + + if ((int16) count.word >= 0) /* nearest even integer */ + count.word = count.word + 1; + BR = count.word = count.word & ~1; /* save LSBs of N */ + + O = O | fp_exec (0122, ACCUM, count, NOP); /* acc = FLT (count) */ + + if (O) { /* out of range? */ + op[0].fpk[0] = 0; /* microcode rtns with A = 0 */ + break; /* error return is P+1 */ + } + + fp_exec (0026, &result, product, NOP); /* acc = product - acc */ + fp_unpack (NULL, &rsltexp, result, fp_t); /* unpack exponent */ + + /* determine if cancellation matters */ + + if ((f < 0) || (f == 2) || (f == 6) || /* EXP, TANH, or COS? */ + (exponent - rsltexp < 5)) { /* bits lost < 5? */ + WriteOp (op[0].word, result, fp_t); /* write result */ + PC = (PC + 1) & VAMASK; /* P+2 return for good result */ + op[0].fpk[1] = BR; /* return LSBs of N in B */ + break; /* all done! */ + } + } + + /* result = (xu * cu - n) + (x - xu) * c + xu * cl */ + + if (exponent >= (8 + 16 * (f >= 0))) { /* exp >= 8 (EXP,TANH)? */ + op[0].fpk[0] = 0; /* or 24 (SIN/COS/TAN)? */ + break; /* range error return is P+1 */ + } + + op[3].fpk[0] = coeff.fpk[0]; /* form upper bits of C (CU) */ + op[3].fpk[1] = coeff.fpk[1] & 0177770; + op[3].fpk[2] = 0; + op[3].fpk[3] = coeff.fpk[3] & 0000377; + + op[4].fpk[0] = op[2].fpk[0]; /* form upper bits of X (XU) */ + op[4].fpk[1] = op[2].fpk[1] & 0177770; + op[4].fpk[2] = 0; + op[4].fpk[3] = op[2].fpk[3] & 0000377; + + fp_exec (0042, &op[5], op[3], op[4]); /* op5 = cu * xu */ + + fp_exec (0116, &op[6], NOP, NOP); /* op6 = fix (acc) (2wd) */ + + if ((int32) op[6].dword >= 0) /* nearest even integer */ + op[6].dword = op[6].dword + 1; + op[6].dword = op[6].dword & ~1; + BR = op[6].dword & DMASK; /* save LSBs of N */ + + O = fp_exec (0126, ACCUM, op[6], NOP); /* acc = flt (op6) */ + + if (O) { /* overflow? */ + op[0].fpk[0] = 0; /* microcode rtns with A = 0 */ + break; /* range error return is P+1 */ + } + + fp_exec (0026, &op[7], op[5], NOP); /* op7 = cu * xu - n */ + + fp_exec (0022, ACCUM, op[2], op[4]); /* acc = x - xu */ + fp_exec (0052, ACCUM, NOP, coeff); /* acc = (x - xu) * c */ + fp_exec (0012, &op[5], NOP, op[7]); /* op5 = acc + (cu * xu - n) */ + + op[1].word = (op[1].word + 4) & VAMASK; /* point at second coefficient */ + coeff = ReadOp (op[1].word, fp_t); /* get coefficient (CL) */ + + fp_exec (0042, ACCUM, op[4], coeff); /* acc = xu * cl */ + fp_exec (0012, &result, NOP, op[5]); /* result = acc + (x - xu) * c + (cu * xu - n) */ + + WriteOp (op[0].word, result, fp_t); /* write result */ + PC = (PC + 1) & VAMASK; /* P+2 return for good result */ + op[0].fpk[1] = BR; /* return LSBs of N in B */ + break; + + + case 013: /* /ATLG 105333 (OP_A) */ + arg = ReadOp (op[0].word, fp_t); /* get argument */ + + fp_exec (0022, &op[1], t_one, arg); /* op1 = 1.0 - arg */ + fp_exec (0002, ACCUM, t_one, arg); /* acc = 1.0 + arg */ + fp_exec (0066, &op[1], op[1], NOP); /* res = op1 / acc */ + + WriteOp (op[0].word, op[1], fp_t); /* write result */ + break; + + + case 014: /* .FPWR 105334 (OP_IIF) */ + p = 0; /* set to single-precision */ + goto NPWR; + + case 015: /* .TPWR 105335 (OP_IAT) */ + p = 2; /* set to double-precision */ + + NPWR: + if (op[2].fpk[0]) { /* non-zero base? */ + fp_exec (0120, &pwr, op[0], NOP); /* float power */ + + sign = ((int16) pwr.fpk[0] < 0); /* save sign of power */ + i = (pwr.fpk[0] << 2) & DMASK; /* clear it */ + + fp_unpack (NULL, &exponent, pwr, fp_f); /* unpack exponent */ + + if (sign == 0) + exponent = exponent - 1; + + O = 0; /* clear overflow */ + fp_accum (&op[2], (fp_f + p)); /* acc = arg */ + + while (exponent-- > 0) { + O = O | fp_exec ((uint16) (0054 | p), /* square acc */ + ACCUM, NOP, NOP); + + if (i & SIGN) + O = O | fp_exec ((uint16) (0050 | p), /* acc = acc * arg */ + ACCUM, NOP, op[2]); + i = i << 1; + } + + op[2] = fp_accum (NULL, (fp_f + p)); /* get accum */ + + if (op[2].fpk[0] == 0) /* result zero? */ + O = 1; /* underflow */ + } + + if (entry == 014) /* .FPWR ? */ + op[0] = op[2]; /* copy result */ + else /* .TPWR */ + WriteOp (op[1].word, op[2], fp_t); /* write result */ + + break; + + + case 017: /* [tst] 105337 (OP_N) */ + XR = 4; /* firmware revision */ + SR = 0102077; /* test passed code */ + PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/DPOLY */ + return reason; + + + default: /* others undefined */ + return stop_inst; + } + +AR = op[0].fpk[0]; /* save result */ +BR = op[0].fpk[1]; /* into A/B */ +return reason; +} + +#endif /* end of int64 support */ diff --git a/HP2100/hp2100_cpu5.c b/HP2100/hp2100_cpu5.c new file mode 100644 index 0000000..ea9ce3b --- /dev/null +++ b/HP2100/hp2100_cpu5.c @@ -0,0 +1,1426 @@ +/* hp2100_cpu5.c: HP 1000 RTE-6/VM VMA and RTE-IV EMA instructions + + Copyright (c) 2006, J. David Bryan + Copyright (c) 2007-2008, Holger Veit + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the authors shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the authors. + + CPU5 RTE-6/VM and RTE-IV firmware option instructions + + 01-May-08 HV Fixed mapping bug in "cpu_ema_emap" + 21-Apr-08 JDB Added EMA support from Holger + 25-Nov-07 JDB Added TF fix from Holger + 07-Nov-07 HV VMACK diagnostic tests 1...32 passed + 19-Oct-07 JDB Corrected $LOC operand profile to OP_CCCACC + 03-Oct-07 HV Moved RTE-6/VM instrs from hp2100_cpu0.c + 26-Sep-06 JDB Created + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. +*/ + +#include +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + + +t_stat cpu_rte_vma (uint32 IR, uint32 intrq); /* RTE-6 VMA */ +t_stat cpu_rte_ema (uint32 IR, uint32 intrq); /* RTE-IV EMA */ + + +/* RTE-6/VM Virtual Memory Area Instructions + + RTE-6/VM (product number 92084A) introduced Virtual Memory Area (VMA) + instructions -- a superset of the RTE-IV EMA instructions. Different + microcode was supplied with the operating system that replaced the microcode + used with RTE-IV. Microcode was limited to the E/F-Series, and the M-Series + used software equivalents. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A 92084A 92084A + + The routines are mapped to instruction codes as follows: + + Instr. 1000-E/F Description + ------ -------- ---------------------------------------------- + .PMAP 105240 Map VMA page into map register + $LOC 105241 Load on call + [test] 105242 [self test] + .SWP 105243 [Swap A and B registers] + .STAS 105244 [STA B; LDA SP] + .LDAS 105245 [LDA SP] + .MYAD 105246 [NOP in microcode] + .UMPY 105247 [Unsigned multiply and add] + + .IMAP 105250 Integer element resolve address and map + .IMAR 105251 Integer element resolve address + .JMAP 105252 Double integer element resolve address and map + .JMAR 105253 Double integer element resolve address + .LPXR 105254 Map pointer in P+1 plus offset in P+2 + .LPX 105255 Map pointer in A/B plus offset in P+1 + .LBPR 105256 Map pointer in P+1 + .LBP 105257 Map pointer in A/B registers + + Notes: + + 1. The opcodes 105243-247 are undocumented and do not appear to be used in + any HP software. + + 2. The opcode list in the CE Handbook incorrectly shows 105246 as ".MYAD - + multiply 2 signed integers." The microcode listing shows that this + instruction was deleted, and the opcode is now a NOP. + + 3. RTE-IV EMA and RTE-6 VMA instructions shared the same address space, so + a given machine could run one or the other, but not both. + + Additional references: + - RTE-6/VM VMA/EMA Microcode Source (92084-18828, revision 3). + - RTE-6/VM Technical Specifications (92084-90015, Apr-1983). + - M/E/F-Series Computer Systems CE Handbook (5950-3767, Jul-1984). +*/ + +static const OP_PAT op_vma[16] = { + OP_N, OP_CCCACC, OP_N, OP_N, /* .PMAP $LOC [test] .SWAP */ + OP_N, OP_N, OP_N, OP_K, /* .STAS .LDAS .MYAD .UMPY */ + OP_A, OP_A, OP_A, OP_A, /* .IMAP .IMAR .JMAP .JMAR */ + OP_AA, OP_A, OP_A, OP_N /* .LPXR .LPX .LBPR .LBP */ + }; + +/* some addresses in page0 of RTE-6/VM */ +static const uint32 idx = 0001645; +static const uint32 xmata = 0001646; +static const uint32 xi = 0001647; +static const uint32 xeqt = 0001717; +static const uint32 vswp = 0001776; +static const uint32 umaps = 0003740; +static const uint32 page30 = 0074000; +static const uint32 page31 = 0076000; +static const uint32 ptemiss = 0176000; + +/* frequent constants in paging */ +#define SUITMASK 0176000 +#define NILPAGE 0176000 +#define PAGEIDX 0001777 +#define MSEGMASK 0076000 +#define RWPROT 0141777 + +/* from scp.c */ +extern int32 sim_step; +extern FILE* sim_log; + +/* MP abort handler */ +extern jmp_buf save_env; +#define ABORT(val) longjmp (save_env, (val)) + +/* microcode version of resolve(): allows a much higher # of indirection levels. Used for + instance for LBP microcode diagnostics which will check > 100 levels. + */ +#define VMA_INDMAX 200 + +static t_stat vma_resolve (uint32 MA, uint32 *addr, t_bool debug) +{ +uint32 i; +uint32 faultma = MA; + +for (i = 0; (i < VMA_INDMAX) && (MA & I_IA); i++) { /* resolve multilevel */ + MA = ReadW (MA & VAMASK); /* follow address chain */ + } + +if (MA & I_IA) { + if (debug) + fprintf(sim_deb,">>CPU VMA: vma_resolve indirect loop addr=%06o\n",faultma); + return STOP_IND; /* indirect loop */ + } + +*addr = MA; +return SCPE_OK; +} + +/* $LOC + ASSEMBLER CALLING SEQUENCE: + + $MTHK NOP RETURN ADDRESS OF CALL (REDONE AFTER THIS ROUTINE) + JSB $LOC + .DTAB OCT LGPG# LOGICAL PAGE # AT WHICH THE NODE TO + * BE MAPPED IN BELONGS (0-31) + OCT RELPG RELATIVE PAGE OFFSET FROM BEGINING + * OF PARTITION OF WHERE THAT NODE RESIDES. + * (0 - 1023) + OCT RELBP RELATIVE PAGE OFFSET FROM BEGINING OF + * PARTITION OF WHERE BASE PAGE RESIDES + * (0 - 1023) + CNODE DEF .CNOD THIS IS THE ADDRESS OF CURRENT PATH # WORD + .ORD OCT XXXXX THIS NODE'S LEAF # (IE PATH #) + .NOD# OCT XXXXX THIS NODE'S ORDINAL # +*/ + +static t_stat cpu_vma_loc(OPS op,uint32 intrq,t_bool debug) +{ +uint32 eqt,mls,pnod,lstpg,fstpg,rotsz,lgpg,relpg,relbp,matloc,ptnpg,physpg,cnt,pgs,umapr; + +eqt = ReadIO(xeqt,UMAP); /* get ID segment */ +mls = ReadIO(eqt+33,SMAP); /* get word33 of alternate map */ +if ((mls & 0x8000) == 0) { /* this is not an MLS prog! */ + PC = err_PC; + if (debug) + fprintf(sim_deb,">>CPU VMA: cpu_vma_loc at P=%06o: not an MLS program\n", PC); + if (CTL (PRO)) ABORT (ABORT_PRO); /* allow an MP abort */ + return STOP_HALT; /* FATAL error! */ + } + +pnod = mls & 01777; /* get #pages of mem res nodes */ +if (pnod == 0) { /* no pages? FATAL! */ + PC = err_PC; + if (debug) + fprintf(sim_deb,">>CPU VMA: cpu_vma_loc at P=%06o: no mem resident pages\n", PC); + if (CTL (PRO)) ABORT (ABORT_PRO); /* allow an MP abort */ + return STOP_HALT; + } + +lstpg = (ReadIO(eqt+29,SMAP) >> 10) - 1; /* last page# of code */ +fstpg = ReadIO(eqt+23,SMAP) >> 10; /* index to 1st addr + mem nodes */ +rotsz = fstpg - (ReadIO(eqt+22,SMAP) >> 10); /* #pages in root */ +lgpg = op[0].word; + +/* lets do some consistency checks, CPU halt if they fail */ +if (lstpg < lgpg || lgpg < fstpg) { /* assert LSTPG >= LGPG# >= FSTPG */ + PC = err_PC; + if (debug) + fprintf(sim_deb, + ">>CPU VMA: $LOC at P=%06o: failed check LSTPG >= LGPG# >= FSTPG\n",PC); + if (CTL (PRO)) ABORT (ABORT_PRO); /* allow an MP abort */ + return STOP_HALT; + } + +relpg = op[1].word; +if (pnod < relpg || relpg < (rotsz+1)) { /* assert #PNOD >= RELPG >= ROTSZ+1 */ + PC = err_PC; + if (debug) + fprintf(sim_deb, + ">>CPU VMA: $LOC at %06o: failed check #PNOD >= RELPG >= ROTSZ+1\n",PC); + if (CTL (PRO)) ABORT (ABORT_PRO); /* allow an MP abort */ + return STOP_HALT; + } + +relbp = op[2].word; +if (relbp != 0) /* assert RELBP == 0 OR */ + if (pnod < relbp || relbp < (rotsz+1)) { /* #PNOD >= RELBP >= ROTSZ+1 */ + PC = err_PC; + if (debug) + fprintf(sim_deb, + ">>CPU VMA: $LOC at P=%06o: failed check: #PNOD >= RELBP >= ROTSZ+1\n",PC); + if (CTL (PRO)) ABORT (ABORT_PRO); /* allow an MP abort */ + return STOP_HALT; + } + +cnt = lstpg - lgpg + 1; /* #pages to map */ +pgs = pnod - relpg + 1; /* #pages from start node to end of code */ +if (pgs < cnt) cnt = pgs; /* ensure minimum, so not to map into EMA */ + +matloc = ReadIO(xmata,UMAP); /* get MAT $LOC address */ +ptnpg = ReadIO(matloc+3,SMAP) & 01777; /* index to start phys pg */ +physpg = ptnpg + relpg; /* phys pg # of node */ +umapr = 32 + lgpg; /* map register to start */ + +/* do an XMS with AR=umapr,BR=physpg,XR=cnt */ +if (debug) + fprintf(sim_deb, + ">>CPU VMA: $LOC map %d pgs from phys=%06o to mapr=%d\n", + cnt,physpg,umapr); +while (cnt != 0) { + dms_wmap (umapr, physpg); /* map pages of new overlay segment */ + cnt = (cnt - 1) & DMASK; + umapr = (umapr + 1) & DMASK; + physpg = (physpg + 1) & DMASK; + } + +dms_wmap(32,relbp+ptnpg); /* map base page again */ +WriteW(op[3].word,op[4].word); /* path# we are going to */ + +PC = (PC - 8) & DMASK; /* adjust PC to return address */ + /* word before the $LOC microinstr. */ +PC = (ReadW(PC) - 1) & DMASK; /* but the call has to be rerun, */ + /* so must skip back to the original call */ + /* which will now lead to the real routine */ +if (debug) + fprintf(sim_deb,">>CPU VMA: $LOC done: path#=%06o, P=%06o\n",op[4].word,PC); +return SCPE_OK; +} + +/* map pte into last page + return FALSE if page fault, nil flag in PTE or suit mismatch + return TRUE if suit match, physpg = physical page + or page=0 -> last+1 page +*/ +static t_bool cpu_vma_ptevl(uint32 pagid,uint32* physpg) +{ +uint32 suit; +uint32 pteidx = pagid & 0001777; /* build index */ +uint32 reqst = pagid & SUITMASK; /* required suit */ +uint32 pteval = ReadW(page31 | pteidx); /* get PTE entry */ +*physpg = pteval & 0001777; /* store physical page number */ +suit = pteval & SUITMASK; /* suit number seen */ +if (pteval == NILPAGE) return FALSE; /* NIL value in PTE */ +return suit == reqst || !*physpg; /* good page or last+1 */ +} + +/* handle page fault */ +static t_stat cpu_vma_fault(uint32 x,uint32 y,int32 mapr, + uint32 ptepg,uint32 ptr,uint32 faultpc, t_bool debug) +{ +uint32 pre = ReadIO(xi,UMAP); /* get program preamble */ +uint32 ema = ReadIO(pre+2,UMAP); /* get address of $EMA$/$VMA$ */ +WriteIO(ema,faultpc,UMAP); /* write addr of fault instr */ +XR = x; /* X = faulting page */ +YR = y; /* Y = faulting address for page */ + +if (mapr>0) + dms_wmap(mapr+UMAP,ptepg); /* map PTE into specified user dmsmap */ + +/* do a safety check: first instr of $EMA$/$VMA$ must be a DST instr */ +if (ReadIO(ema+1,UMAP) != 0104400) { + if (debug) + fprintf(sim_deb, ">>CPU VMA: pg fault: no EMA/VMA user code present\n"); + if (CTL (PRO)) ABORT (ABORT_PRO); /* allow an MP abort */ + return STOP_HALT; /* FATAL: no EMA/VMA! */ + } + +PC = (ema+1) & VAMASK; /* restart $EMA$ user code, */ + /* will return to fault instruction */ + +AR = (ptr >> 16) & DMASK; /* restore A, B */ +BR = ptr & DMASK; +E = 0; /* enforce E = 0 */ +if (debug) + fprintf(sim_deb, + ">>CPU VMA: Call pg fault OS exit, AR=%06o BR=%06o P=%06o\n", + AR, BR, PC); +return SCPE_OK; +} + +/* map in PTE into last page, return false, if page fault */ +static t_bool cpu_vma_mapte(uint32* ptepg) +{ +uint32 idext,idext2; +uint32 dispatch = ReadIO(vswp,UMAP) & 01777; /* get fresh dispatch flag */ +t_bool swapflag = TRUE; + +if (dispatch == 0) { /* not yet set */ + idext = ReadIO(idx,UMAP); /* go into IDsegment extent */ + if (idext != 0) { /* is ema/vma program? */ + dispatch = ReadWA(idext+1) & 01777; /* get 1st ema page: new vswp */ + WriteIO(vswp,dispatch,UMAP); /* move into $VSWP */ + idext2 = ReadWA(idext+2); /* get swap bit */ + swapflag = (idext2 & 020000) != 0; /* bit 13 = swap bit */ + } + } + +if (dispatch) { /* some page is defined */ + dms_wmap(31 + UMAP,dispatch); /* map $VSWP to register 31 */ + *ptepg = dispatch; /* return PTEPG# for later */ + } + +return swapflag; /* true for swap bit set */ +} + +/* .LBP + ASSEMBLER CALLING SEQUENCE: + + DLD PONTR TRANSLATE 32 BIT POINTER TO 15 + JSB .LBP BIT POINTER. + + + 32 bit pointer: + ----------AR------------ -----BR----- + 15 14....10 9....4 3...0 15.10 9....0 + L<----------------------------------- L=1 local reference bit + XXXXXXXX<------------------------- 5 bit unused + PPPPPP PPPPP PPPPP<------ 16 bit PAGEID + SSSSSS<------------------ SUIT# within PAGEID + PPPPP PPPPP<------ 10 bit PAGEID index into PTE + OOOOOO 10 bit OFFSET +*/ + +static t_stat cpu_vma_lbp(uint32 ptr,uint32 aoffset,uint32 faultpc,uint32 intrq,t_bool debug) +{ +uint32 pagid,offset,ptrl,pgidx,ptepg; +uint16 p30,p31,suit; +t_stat reason = SCPE_OK; +uint32 faultab = ptr; /* remember A,B for page fault */ +ptr += aoffset; /* add the offset e.g. for .LPX */ + +if (debug) + fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: ptr=%o/%o\n", + (ptr>>16) & DMASK,ptr & DMASK); + +O = 0; /* clear overflow */ +if (ptr & 0x80000000) { /* is it a local reference? */ + ptrl = ptr & VAMASK; + if ((ptr&I_IA) && (reason = vma_resolve (ReadW (ptrl), &ptrl, debug))) + return reason; /* yes, resolve indirect ref */ + BR = ptrl & VAMASK; /* address is local */ + AR = (ptr >> 16) & DMASK; + if (debug) + fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: local ref AR=%06o BR=%06o\n",AR,BR); + return SCPE_OK; + } + +pagid = (ptr >> 10) & DMASK; /* extract page id (16 bit idx, incl suit*/ +offset = ptr & 01777; /* and offset */ +suit = pagid & SUITMASK; /* suit of page */ +pgidx = pagid & PAGEIDX; /* index into PTE */ + +if (!cpu_vma_mapte(&ptepg)) /* map in PTE */ + return cpu_vma_fault(65535,ptemiss,-1,ptepg,faultab,faultpc, debug); /* oops, must init PTE */ + +/* ok, we have the PTE mapped to page31 */ +/* the microcode tries to reads two consecutive data pages into page30 and page31 */ + +/* read the 1st page value from PTE */ +p30 = ReadW(page31 | pgidx) ^ suit; +if (!p30) /* matched suit for 1st page */ + return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug); + +/* suit switch situation: 1st page is in last idx of PTE, then following page + * must be in idx 0 of PTE */ +if (pgidx==01777) { /* suit switch situation */ + pgidx = 0; /* select correct idx 0 */ + suit = pagid+1; /* suit needs increment */ + if (suit==0) { /* is it page 65536? */ + offset += 02000; /* adjust to 2nd page */ + suit = NILPAGE; + pgidx = 01777; + } +} else + pgidx++; /* select next page */ + +p31 = ReadW(page31 | pgidx) ^ suit; +if (!p31) { /* matched suit for 2nd page */ + dms_wmap(31+UMAP,p30); + if (p30 & SUITMASK) + return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug); + if (!(p31 ^ NILPAGE)) /* suit is 63: fault */ + return cpu_vma_fault(pagid+1,page31,31,ptepg,faultab,faultpc,debug); + + offset += 02000; /* adjust offset to last user map because */ + /* the address requested page 76xxx */ + } +else { + dms_wmap(30+UMAP,p30); + if (p30 & SUITMASK) + return cpu_vma_fault(pagid,page30,30,ptepg,faultab,faultpc,debug); + dms_wmap(31+UMAP,p31); + if (p31 & SUITMASK) + return cpu_vma_fault(pagid+1,page31,31,ptepg,faultab,faultpc,debug); + } + +AR = pagid; /* return pagid in A */ +BR = page30+offset; /* mapped address in B */ +if (debug) + fprintf(sim_deb,">>CPU VMA: cpu_vma_lbp: map done AR=%06o BR=%o6o\n",AR,BR); +return SCPE_OK; +} + +/* .PMAP + ASSEMBLER CALLING SEQUENCE: + + LDA UMAPR (MSEG - 31) + LDB PAGID (0-65535) + JSB .PMAP GO MAP IT IN + A-REG = REASON, NOTE 1 + > SEE NOTE 2> + + NOTE 1 : IF BIT 15 OF A-REG SET, THEN ALL NORMAL BRANCHES TO THE + $EMA$/$VMA$ CODE WILL BE CHANGED TO P+1 EXIT. THE A-REG + WILL BE THE REASON THE MAPPING WAS NOT SUCCESSFUL IF BIT 15 + OF THE A-REG WAS NOT SET. + THIS WAS DONE SO THAT A ROUTINE ($VMA$) CAN DO A MAPPING + WITHOUT THE POSSIBILITY OF BEING RE-CURRED. IT IS USED + BY $VMA$ AND PSTVM IN THE PRIVLEDGED MODE. + NOTE 2: E-REG WILL = 1 IF THE LAST+1 PAGE IS REQUESTED AND + MAPPED READ/WRITE PROTECTED ON A GOOD P+2 RETURN. +*/ +static t_stat cpu_vma_pmap(uint32 umapr,uint32 pagid, t_bool debug) +{ +uint32 physpg, ptr, pgpte; +uint32 mapnm = umapr & 0x7fff; /* strip off bit 15 */ + +if (debug) + fprintf(sim_deb, ">>CPU VMA: .PMAP AR=%06o(umapr) BR=%06o(pagid)\n",umapr,pagid); + +if (mapnm > 31) { /* check for invalid map register */ + AR = 80; /* error: corrupt EMA/VMA system */ + if (debug) + fprintf(sim_deb, ">>CPU VMA: .PMAP invalid mapr: AR=80, exit P+1\n"); + return SCPE_OK; /* return exit PC+1 */ + } + +ptr = (umapr << 16) | (pagid & DMASK); /* build the ptr argument for vma_fault */ +if (!cpu_vma_mapte(&pgpte)) { /* map the PTE */ + if (umapr & 0x8000) { + XR = 65535; + YR = ptemiss; + if (debug) + fprintf(sim_deb, + ">>CPU VMA: .PMAP pg fault&bit15: XR=%06o YR=%06o, exit P+1\n", + XR, YR); + return SCPE_OK; /* use PC+1 error exit */ + } + return cpu_vma_fault(65535,ptemiss,-1,pgpte,ptr,PC-1,debug); /* oops: fix PTE */ + } + +/* PTE is successfully mapped to page31 and dmsmap[63] */ +if (!cpu_vma_ptevl(pagid,&physpg)) { + if (umapr & 0x8000) { + XR = pagid; + YR = page31; + if (debug) + fprintf(sim_deb, + ">>CPU VMA: .PMAP pg map&bit15: XR=%06o YR=%06o, exit P+1\n", + XR, YR); + return SCPE_OK; /* use PC+1 error exit*/ + } + return cpu_vma_fault(pagid,page31,31,pgpte,ptr,PC-1,debug); /* page not present */ + } + +E = 1; +if (physpg == 0) /* last+1 page ? */ + physpg = RWPROT; /* yes, use page 1023 RW/Protected */ +else E = 0; /* normal page to map */ + +dms_wmap(mapnm+UMAP,physpg); /* map page to user page reg */ +if (mapnm != 31) /* unless already unmapped, */ + dms_wmap(31+UMAP,RWPROT); /* unmap PTE */ + +AR = (umapr + 1) & DMASK; /* increment mapr for next call */ +BR = (pagid + 1) & DMASK; /* increment pagid for next call */ +O = 0; /* clear overflow */ +PC = (PC + 1) & VAMASK; /* normal PC+2 return */ +if (debug) + fprintf(sim_deb,">>CPU VMA: .PMAP map done: AR=%06o BR=%o6o exit P+2\n",AR,BR); +return SCPE_OK; +} + +/* array calc helper for .imar, .jmar, .imap, .jmap + ij=in_s: 16 bit descriptors + ij=in_d: 32 bit descriptors + + This helper expects mainly the following arguments: + dtbl: pointer to an array descriptor table + atbl: pointer to the table of actual subscripts + + where subscript table is the following: + atbl-> DEF last_subscript,I (point to single or double integer) + ... + DEF first subscript,I (point to single or double integer) + + where Descriptor_table is the following table: + dtbl-> DEC #dimensions + DEC/DIN next-to-last dimension (single or double integer) + ... + DEC/DIN first dimension (single or double integer) + DEC elementsize in words + DEC high,low offset from start of EMA to element(0,0...0) + + Note that subscripts are counting from 0 +*/ +static t_stat cpu_vma_ijmar(OPSIZE ij,uint32 dtbl,uint32 atbl,uint32* dimret, + uint32 intrq,t_bool debug) +{ +t_stat reason = SCPE_OK; +uint32 ndim,MA,i,ws; +int32 accu,ax,dx; +OP din; +int opsz = ij==in_d ? 2 : 1; + +ndim = ReadW(dtbl++); /* get #dimensions itself */ +if (debug) { + fprintf(sim_deb, ">>CPU VMA array calc #dim=%d, size=%d\n",ndim,opsz); + fprintf(sim_deb, ">>CPU VMA: array actual subscripts ("); + for (i=0; i0) fputc(',',sim_deb); + fprintf(sim_deb,"%d",ij==in_d?INT32(din.dword) : INT16(din.word)); + } + + fprintf(sim_deb,")\n>>CPU VMA: array descriptor table ("); + if (ndim) { + for (i=0; i0) fputc(',',sim_deb); + fprintf(sim_deb,"%d",ij==in_d?INT32(din.dword) : INT16(din.word)); + } + i = dtbl+1+(ndim-1)*opsz; + ws = ReadW(i-1); + } + else { + i = dtbl; + ws = 1; + } + fprintf(sim_deb,")\n>>CPU VMA: array elemsz=%d base=%o/%o\n", + ws,ReadW(i),ReadW(i+1)); + } + +if (dimret) *dimret = ndim; /* return dimensions */ +if (ndim == 0) { /* no dimensions: */ + AR = ReadW(dtbl++); /* return the array base itself */ + BR = ReadW(dtbl); + if (debug) + fprintf(sim_deb,">>CPU VMA: #dim=0, AR=%06o, BR=%06o\n",AR,BR); + return SCPE_OK; + } + +/* calculate + * (...(An*Dn-1)+An-1)*Dn-2)+An-2....)+A2)*D1)+A1)*#words + Array base + * Depending on ij, Ax and Dx can be 16 or 32 bit + */ +accu = 0; +while (ndim-- > 0) { + MA = ReadW(atbl++); /* get addr of subscript */ + if ((reason = resolve (MA, &MA, intrq))) /* and resolve it */ + return reason; + din = ReadOp(MA,ij); /* get actual subscript value */ + ax = ij==in_d ? INT32(din.dword) : INT16(din.word); + accu += ax; /* add to accu */ + + if (ndim==0) ij = in_s; /* #words is single */ + din = ReadOp(dtbl,ij); /* get dimension from descriptor table */ + if (ij==in_d) { + dx = INT32(din.dword); /* either get double or single dimension */ + dtbl += 2; + } else { + dx = INT16(din.word); + dtbl++; + } + accu *= dx; /* multiply */ + } + +din = ReadOp(dtbl,in_d); /* add base address */ +accu += din.dword; + +AR = (accu >> 16) & DMASK; /* transfer to AB */ +BR = accu & DMASK; +if (debug) + fprintf(sim_deb,">>CPU VMA: resulting virt addr=%o (AR=%06o, BR=%06o)\n",accu,AR,BR); +return reason; +} + +/* + * This is the main handler for the RTE6/VMA microcodes */ +t_stat cpu_rte_vma (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +OP_PAT pattern; +uint32 entry,t32,ndim; +uint32 dtbl,atbl; /* descriptor table ptr, actual args ptr */ +OP dop0,dop1; +uint32 pcsave = (PC+1) & VAMASK; /* save PC to check for redo in imap/jmap */ +t_bool debug = DEBUG_PRI (cpu_dev, DEB_VMA); + +if ((cpu_unit.flags & UNIT_VMAOS) == 0) /* VMA/OS option installed? */ + return cpu_rte_ema (IR, intrq); /* try EMA */ + +entry = IR & 017; /* mask to entry point */ +pattern = op_vma[entry]; /* get operand pattern */ + +if (pattern != OP_N) + if (reason = cpu_ops (pattern, op, intrq)) /* get instruction operands */ + return reason; + +if (debug) { /* debugging? */ + fprintf (sim_deb, ">>CPU VMA: IR = %06o (", IR); /* print preamble and IR */ + fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ + NULL, SWMASK('M')); + fprintf (sim_deb, "), P = %06o, XEQT = %06o", /* print location and program ID */ + err_PC, ReadW (xeqt)); + + fprint_ops (pattern, op); /* print operands */ + fputc ('\n', sim_deb); /* terminate line */ + } + +switch (entry) { /* decode IR<3:0> */ + + case 000: /* .PMAP 105240 (OP_N) */ + reason = cpu_vma_pmap(AR,BR,debug); /* map pages */ + break; + + case 001: /* $LOC 105241 (OP_CCCACC) */ + reason = cpu_vma_loc(op,intrq,debug); /* handle the coroutine switch */ + break; + + case 002: /* [test] 105242 (OP_N) */ + XR = 3; /* refer to src code 92084-18828 rev 3 */ + SR = 0102077; /* HLT 77 instruction */ + YR = 1; /* ROMs correctly installed */ + PC = (PC+1) & VAMASK; /* skip instr if VMA/EMA ROM installed */ + break; + + case 003: /* [swap] 105243 (OP_N) */ + t32 = AR; /* swap A and B registers */ + AR = BR; + BR = t32; + break; + + case 004: /* [---] 105244 (OP_N) */ + reason = stop_inst; /* fragment of dead code */ + break; /* in microrom */ + + case 005: /* [---] 105245 (OP_N) */ + reason = stop_inst; /* fragment of dead code */ + break; /* in microrom */ + + case 006: /* [nop] 105246 (OP_N) */ + break; /* do nothing */ + + case 007: /* [umpy] 105247 (OP_K) */ + t32 = AR * op[0].word; /* get multiplier */ + t32 += BR; /* add B */ + AR = (t32 >> 16) & DMASK; /* move result back to AB */ + BR = t32 & DMASK; + O = 0; /* instr clears OV */ + break; + + case 010: /* .IMAP 105250 (OP_A) */ + dtbl = op[0].word; + atbl = PC; + if ((reason = cpu_vma_ijmar(in_s,dtbl,atbl,&ndim,intrq,debug))) /* calc the virt address to AB */ + return reason; + t32 = (AR << 16) | (BR & DMASK); + if ((reason = cpu_vma_lbp(t32,0,PC-2,intrq,debug))) + return reason; + if (PC==pcsave) + PC = (PC+ndim) & VAMASK; /* adjust PC: skip ndim subscript words */ + break; + + case 011: /* .IMAR 105251 (OP_A) */ + dtbl = ReadW(op[0].word); + atbl = (op[0].word+1) & VAMASK; + reason = cpu_vma_ijmar(in_s,dtbl,atbl,0,intrq,debug); /* calc the virt address to AB */ + break; + + case 012: /* .JMAP 105252 (OP_A) */ + dtbl = op[0].word; + atbl = PC; + if ((reason = cpu_vma_ijmar(in_d,dtbl,atbl,&ndim,intrq,debug))) /* calc the virtual address to AB */ + return reason; + t32 = (AR << 16) | (BR & DMASK); + if ((reason = cpu_vma_lbp(t32,0,PC-2,intrq,debug))) + return reason; + if (PC==pcsave) + PC = (PC + ndim) & VAMASK; /* adjust PC: skip ndim subscript dword ptr */ + break; + + case 013: /* .JMAR 105253 (OP_A) */ + dtbl = ReadW(op[0].word); + atbl = (op[0].word+1) & VAMASK; + reason = cpu_vma_ijmar(in_d,dtbl,atbl,0,intrq,debug); /* calc the virt address to AB */ + break; + + case 014: /* .LPXR 105254 (OP_AA) */ + dop0 = ReadOp(op[0].word,in_d); /* get pointer from arg */ + dop1 = ReadOp(op[1].word,in_d); + t32 = dop0.dword + dop1.dword; /* add offset to it */ + reason = cpu_vma_lbp(t32,0,PC-3,intrq,debug); + break; + + case 015: /* .LPX 105255 (OP_A) */ + t32 = (AR << 16) | (BR & DMASK); /* pointer in AB */ + dop0 = ReadOp(op[0].word,in_d); + reason = cpu_vma_lbp(t32,dop0.dword,PC-2,intrq,debug); + break; + + case 016: /* .LBPR 105256 (OP_A) */ + dop0 = ReadOp(op[0].word,in_d); /* get the pointer */ + reason = cpu_vma_lbp(dop0.dword,0,PC-2,intrq,debug); + break; + + case 017: /* .LBP 105257 (OP_N) */ + t32 = (AR << 16) | (BR & DMASK); + reason = cpu_vma_lbp(t32,0,PC-1,intrq,debug); + break; + } + +return reason; +} + + +/* RTE-IV Extended Memory Area Instructions + + The RTE-IV operating system (HP product number 92067A) introduced the + Extended Memory Area (EMA) instructions. EMA provided a mappable data area + up to one megaword in size. These three instructions accelerated data + accesses to variables stored in EMA partitions. Support was limited to + E/F-Series machines; M-Series machines used software equivalents. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A 92067A 92067A + + The routines are mapped to instruction codes as follows: + + Instr. 1000-E/F Description + ------ -------- ---------------------------------------------- + .EMIO 105240 EMA I/O + MMAP 105241 Map physical to logical memory + [test] 105242 [self test] + .EMAP 105257 Resolve array element address + + Notes: + + 1. RTE-IV EMA and RTE-6 VMA instructions share the same address space, so a + given machine can run one or the other, but not both. + + Additional references: + - RTE-IVB Programmer's Reference Manual (92068-90004, Dec-1983). + - RTE-IVB Technical Specifications (92068-90013, Jan-1980). +*/ + +static const OP_PAT op_ema[16] = { + OP_AKA, OP_AKK, OP_N, OP_N, /* .EMIO MMAP [test] --- */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */ + OP_N, OP_N, OP_N, OP_AAA /* --- --- --- .EMAP */ + }; + +/* calculate the 32 bit EMA subscript for an array */ +static t_bool cpu_ema_resolve(uint32 dtbl,uint32 atbl,uint32* sum) +{ +int32 sub, act, low, sz; +uint32 MA, base; + +int32 ndim = ReadW(dtbl++); /* # dimensions */ +if (ndim < 0) return FALSE; /* invalid? */ + +*sum = 0; /* accu for index calc */ +while (ndim > 0) { + MA = ReadW(atbl++); /* get address of A(N) */ + resolve (MA, &MA, 0); + act = ReadW(MA); /* A(N) */ + low = ReadW(dtbl++); /* -L(N) */ + sub = SEXT(act) + SEXT(low); /* subscript */ + if (sub & 0xffff8000) return FALSE; /* overflow? */ + *sum += sub; /* accumulate */ + sz = ReadW(dtbl++); + sz = SEXT(sz); + if (sz < 0) return FALSE; + *sum *= sz; + if (*sum > (512*1024)) return FALSE; /* overflow? */ + ndim--; +} +base = (ReadW(dtbl+1)<<16) | (ReadW(dtbl) & 0xffff); /* base of array in EMA */ +if (base & 0x8000000) return FALSE; +*sum += base; /* calculate address into EMA */ +if (*sum & 0xf8000000) return FALSE; /* overflow? */ +return TRUE; +} + +/* implementation of VIS RTE-IVB EMA support + * .ERES microcode routine, resolves only EMA addresses + * Call: + * .OCT 101474B + * DEF RTN error return (rtn), good return is rtn+1 + * DEF DUMMY dummy argument for compatibility with .EMAP + * DEF TABLE[,I] array declaration (dtbl) + * DEF A(N)[,I] actual subscripts (atbl) + * DEF A(N-1)[,I] + * ... + * DEF A(2)[,I] + * DEF A(1)[,I] + * RTN EQU * error return A="20", B="EM" + * RTN+1 EQU *+1 good return B=logical address + * + * TABLE DEC # # dimensions + * DEC -L(N) + * DEC D(N-1) + * DEC -L(N-1) lower bound (n-1)st dim + * DEC D(N-2) (n-2)st dim + * ... + * DEC D(1) 1st dim + * DEC -L(1) lower bound 1st dim + * DEC # # words/element + * OFFSET 1 EMA Low + * OFFSET 2 EMA High + */ +t_stat cpu_ema_eres(uint32 *rtn,uint32 dtbl,uint32 atbl,t_bool debug) +{ +uint32 sum; +if (cpu_ema_resolve(dtbl,atbl,&sum)) { /* calculate subscript */ + AR = sum & 0xffff; + BR = sum >> 16; + if (!(BR & SIGN)) { /* no overflow? */ + (*rtn)++; /* return via good exit */ + return SCPE_OK; + } +} +AR = 0x3230; /* error condition: */ +BR = 0x454d; /* AR = '20', BR = 'EM' */ +return SCPE_OK; /* return via unmodified rtn */ +} + +/* implementation of VIS RTE-IVB EMA support + * .ESEG microcode routine + * Call: + * LDA FIRST first map to set + * LDB N # of maps to set + * .OCT 101475B/105475B + * DEF RTN ptr to return + * DEF TABLE map table + * RTN EQU * error return A="21", B="EM" + * RTN+1 EQU *+1 good return B=logical address + * + * load maps FIRST to FIRST+N from TABLE, with FIRST = FIRST + LOG_START MSEG + * update map table in base page. Set LOG_START MSEG=0 if opcode==105475 + */ +t_stat cpu_ema_eseg(uint32* rtn, uint32 IR, uint32 tbl, t_bool debug) +{ +uint32 xidex,eqt,idext0,idext1; +uint32 msegsz,phys,msegn,last,emasz,pg0,pg1,pg,i,lp; + +if ((BR & SIGN) || BR==0) goto em21; /* #maps not positive? */ +xidex = ReadIO(idx,UMAP); /* read ID extension */ +if (xidex==0) goto em21; +idext0 = ReadWA(xidex+0); /* get 1st word idext */ +msegsz = idext0 & 037; /* S7 MSEG size */ +WriteIO(xidex+0, idext0 | 0100000, SMAP); /* enforce nonstd MSEG */ +idext1 = ReadWA(xidex+1); /* get 2nd word idext */ +phys = idext1 & 01777; /* S5 phys start of EMA */ +msegn = (idext1 >> 11) & 037; /* S9 get logical start MSEG# */ +if (IR & 04000) { /* opcode == 105475? (.VPRG) */ + msegn = 0; /* log start = 0 */ + msegsz = 32; /* size = full range */ +} +last = AR-1 + BR; /* last page */ +if (last > msegsz) goto em21; /* too many? error */ +eqt = ReadIO(xeqt,UMAP); +emasz = (ReadWA(eqt+28) & 01777) - 1; /* S6 EMA size in pages */ + +/* locations 1740...1777 of user base page contain the map entries we need. + * They are normally hidden by BP fence, therefore they have to be accessed by + * another fence-less map register. uCode uses #1 temporarily */ +pg0 = dms_rmap(UMAP+0); /* read map #0 */ +pg1 = dms_rmap(UMAP+1); /* save map #1 */ +dms_wmap(UMAP+1,pg0); /* copy #0 into reg #1 */ +lp = AR + msegn; /* first */ +for (i=0; i emasz) pg |= 0140000; /* write protect if outside */ + pg += phys; /* adjust into EMA page range */ + WriteIO(umaps+lp+i, pg, UMAP); /* copy pg to user map */ +//printf("MAP val %oB to reg %d (addr=%oB)\n",pg,lp+i,umaps+lp+i); + dms_wmap(UMAP+lp+i, pg); /* set DMS reg */ +} +dms_wmap(UMAP+1,pg1); /* restore map #1 */ +O = 0; /* clear overflow */ +(*rtn)++; /* return via good exit */ +return SCPE_OK; + +em21: +AR = 0x3231; /* error condition: */ +BR = 0x454d; /* AR = '21', BR = 'EM' */ +return SCPE_OK; /* return via unmodified rtn */ +} + +/* implementation of VIS RTE-IVB EMA support + * .VSET microcode routine + * Call: + * .OCT 101476B + * DEF RTN return address + * DEF VIN input vector + * DEF VOUT output vector + * DEF MAPS + * OCT #SCALARS + * OCT #VECTORS + * OCT K 1024/(#words/element) + * RTN EQU * error return (B,A) = "VI22" + * RTN+1 EQU *+1 hard return, A = K/IMAX + * RTN+2 EQU *+2 easy return, A = 0, B = 2* #VCTRS + */ +t_stat cpu_ema_vset(uint32* rtn, OPS op, t_bool debug) +{ +uint32 vin = op[0].word; /* S1 */ +uint32 vout = op[1].word; /* S2 */ +uint32 maps = op[2].word; /* S3 */ +uint32 scalars = op[3].word; /* S4 */ +uint32 vectors = op[4].word; /* S5 */ +uint32 k = op[5].word; /* S6 */ +uint32 imax = 0; /* imax S11*/ +uint32 xidex,idext1,mseg,phys, addr, i, MA; +t_bool negflag = FALSE; + +for (i=0; i> 1) & MSEGMASK; /* S9 get logical start MSEG */ +phys = idext1 & 01777; /* phys start of EMA */ + +for (i=0; i> 10) & 0xffff; /* get page */ + WriteW(maps++, addr); /* save page# */ + WriteW(maps++, addr+1); /* save next page# as well */ + MA = ReadW(vin++); /* get index into Y */ + resolve(MA, &MA, 0); + YR = ReadW(MA); /* get index value */ + WriteW(vout++, MA); /* copy address of index */ + if (YR & SIGN) { /* index is negative */ + negflag = TRUE; /* mark a negative index (HARD) */ + YR = (~YR + 1) & DMASK; /* make index positive */ + } + if (imax < YR) imax = YR; /* set maximum index */ + mseg += 04000; /* incr mseg address by 2 more pages */ +} +MA = ReadW(vin); /* get N index into Y */ +resolve(MA, &MA, 0); +YR = ReadW(MA); +WriteW(vout++, MA); vin++; /* copy address of N */ + +if (imax==0) goto easy; /* easy case */ +AR = k / imax; AR++; /* calculate K/IMAX */ +if (negflag) goto hard; /* had a negative index? */ +if (YR > AR) goto hard; + +easy: +(*rtn)++; /* return via exit 2 */ +AR = 0; + +hard: +(*rtn)++; /* return via exit 1 */ +BR = 2 * op[4].word; /* B = 2* vectors */ +return SCPE_OK; + +vi22: /* error condition */ + AR=0x3232; /* AR = '22' */ + BR=0x5649; /* BR = 'VI' */ + return SCPE_OK; /* return via unmodified e->rtn */ +} + +typedef struct ema4 { + uint32 mseg; /* logical start of MSEG */ + uint32 msegsz; /* size of std mseg in pgs */ + uint32 pgoff; /* pg # in EMA containing element */ + uint32 offs; /* offset into page of element */ + uint32 msoff; /* total offset to element in MSEG */ + uint32 emasz; /* size of ema in pgs */ + uint32 msegno; /* # of std mseg */ + uint32 ipgs; /* # of pgs to start of MSEG */ + uint32 npgs; /* # of pgs needed */ + uint32 spmseg; /* first phys pg of MSEG */ +} EMA4; + +static t_bool cpu_ema_emas(uint32 dtbl,uint32 atbl,EMA4* e) +{ +uint32 xidex, eqt; +uint32 sum, msegsz,pgoff,offs,emasz,msegno,msoff,ipgs; + +if (!cpu_ema_resolve(dtbl,atbl,&sum)) return FALSE; /* calculate 32 bit index */ + +xidex = ReadIO(idx,UMAP); /* read ID extension */ +msegsz = ReadWA(xidex+0) & 037; /* S5 # pgs for std MSEG */ +pgoff = sum >> 10; /* S2 page containing element */ +offs = sum & 01777; /* S6 offset in page to element */ +if (pgoff > 1023) return FALSE; /* overflow? */ +eqt = ReadIO(xeqt,UMAP); +emasz = ReadWA(eqt+28) & 01777; /* S EMA size in pages */ +if (pgoff > emasz) return FALSE; /* outside EMA? */ +msegno = pgoff / msegsz; /* S4 # of MSEG */ +msoff = pgoff % msegsz; /* offset within MSEG in pgs */ +ipgs = pgoff - msoff; /* S7 # pgs to start of MSEG */ +msoff = msoff << 10; /* offset within MSEG in words */ +msoff += offs; /* S1 offset to element in words */ + +e->msegsz = msegsz; /* return calculated data */ +e->pgoff = pgoff; +e->offs = offs; +e->emasz = emasz; +e->msegno = msegno; +e->ipgs = ipgs; +e->msoff = msoff; +return TRUE; +} + +static t_bool cpu_ema_mmap01(EMA4* e) +{ +uint32 xidex,idext0, pg, pg0, pg1, i; + +uint32 base = e->mseg >> 10; /* get the # of first MSEG DMS reg */ +xidex = ReadIO(idx,UMAP); /* get ID extension */ +idext0 = ReadWA(xidex+1); + +if (e->npgs==0) return FALSE; /* no pages to map? */ +if ((e->npgs+1+e->ipgs) <= e->emasz) e->npgs++; /* actually map npgs+1 pgs */ + +/* locations 1740...1777 of user base page contain the map entries we need. + * They are normally hidden by BP fence, therefore they have to be accessed by + * another fence-less map register. uCode uses #1, macro code uses $DVCT (==2) + */ +pg0 = dms_rmap(UMAP+0); /* read base page map# */ +pg1 = dms_rmap(UMAP+1); /* save map# 1 */ +dms_wmap(UMAP+1,pg0); /* map #0 into reg #1 */ +for (i=0; (base+i)<32; i++) { + pg = inpgs ? e->spmseg : 0140000; /* write protect if outside */ + WriteIO(umaps+base+i, pg, UMAP); /* copy pg to user map */ +//printf("MAP val %d to reg %d (addr=%o)\n",pg,base+i,umaps+base+i); + dms_wmap(UMAP+base+i, pg); /* set DMS reg */ + e->spmseg++; +} +dms_wmap(UMAP+1,pg1); /* restore map #1 */ + +xidex = ReadIO(idx,UMAP); /* get ID extension */ +idext0 = ReadWA(xidex+0); +if (e->msegno == 0xffff) /* non std mseg */ + idext0 |= 0x8000; /* set nonstd marker */ +else + idext0 = (idext0 & 037) | (e->msegno<<5); /* set new current mseg# */ +WriteIO(xidex, idext0, SMAP); /* save back value */ +AR = 0; /* was successful */ +return TRUE; +} + +static t_bool cpu_ema_mmap02(EMA4* e) +{ +uint32 xidex, eqt, idext1; +uint32 mseg,phys,spmseg,emasz,msegsz,msegno; + +xidex = ReadIO(idx,UMAP); /* get ID extension */ +msegsz = ReadWA(xidex+0) & 037; /* P size of std MSEG */ +idext1 = ReadWA(xidex+1); +mseg = (idext1 >> 1) & MSEGMASK; /* S9 get logical start MSEG */ +phys = idext1 & 01777; /* S phys start of EMA */ +spmseg = phys + e->ipgs; /* S7 phys pg# of MSEG */ +msegno = e->ipgs / msegsz; +if ((e->ipgs % msegsz) != 0) /* non std MSEG? */ + msegno = 0xffff; /* S4 yes, set marker */ +if (e->npgs > msegsz) return FALSE; /* map more pages than MSEG sz? */ +eqt = ReadIO(xeqt,UMAP); +emasz = ReadWA(eqt+28) & 01777; /* B EMA size in pages */ +if ((e->ipgs+e->npgs) > emasz) return FALSE; /* outside EMA? */ +if ((e->ipgs+msegsz) > emasz) /* if MSEG overlaps end of EMA */ + e->npgs = emasz - e->ipgs; /* only map until end of EMA */ + +e->emasz = emasz; /* copy arguments */ +e->msegsz = msegsz; +e->msegno = msegno; +e->spmseg = spmseg; +e->mseg = mseg; +return cpu_ema_mmap01(e); +} + +static t_stat cpu_ema_mmap(uint32 ipage,uint32 npgs, t_bool debug) +{ +uint32 xidex; +EMA4 ema4, *e = &ema4; + +e->ipgs = ipage; /* S6 set the arguments */ +e->npgs = npgs; /* S5 */ + +AR = 0; +xidex = ReadIO(idx,UMAP); +if ((ipage & SIGN) || /* negative page displacement? */ + (npgs & SIGN) || /* negative # of pages? */ + xidex == 0 || /* no EMA? */ + !cpu_ema_mmap02(e)) /* mapping failed? */ + AR = 0177777; /* return with error */ +return SCPE_OK; /* leave */ +} + +static t_bool cpu_ema_emat(EMA4* e) +{ +uint32 xidex,idext0; +uint32 curmseg,phys,msnum,lastpgs; + +xidex = ReadIO(idx,UMAP); /* read ID extension */ +idext0 = ReadWA(xidex+0); /* get current segment */ +curmseg = idext0 >> 5; +if ((idext0 & 0100000) || /* was nonstd MSEG? */ + curmseg != e->msegno) { /* or different MSEG last time? */ + phys = ReadWA(xidex+1) & 01777; /* physical start pg of EMA */ + e->spmseg = phys + e->ipgs; /* physical start pg of MSEG */ + msnum = e->emasz / e->msegsz; /* find last MSEG# */ + lastpgs = e->emasz % e->msegsz; /* #pgs in last MSEG */ + if (lastpgs==0) msnum--; /* adjust # of last MSEG */ + e->npgs = msnum==e->msegno ? lastpgs : e->msegsz; /* for last MSEG, only map available pgs */ + if (!cpu_ema_mmap01(e)) return FALSE; /* map npgs pages at ipgs */ +} +BR = e->mseg + e->msoff; /* return address of element */ +return TRUE; /* and everything done */ +} + +/* .EMIO microcode routine, resolves element addr for EMA array + * and maps the appropriate map segment + * + * Call: + * OCT 105250B + * DEF RTN error return (rtn), good return is rtn+1 + * DEF BUFLEN length of buffer in words (bufl) + * DEF TABLE[,I] array declaration (dtbl) + * DEF A(N)[,I] actual subscripts (atbl) + * DEF A(N-1)[,I] + * ... + * DEF A(2)[,I] + * DEF A(1)[,I] + * RTN EQU * error return A="15", B="EM" + * RTN+1 EQU *+1 good return B=logical address + * + * TABLE DEC # # dimensions + * DEC -L(N) + * DEC D(N-1) + * DEC -L(N-1) lower bound (n-1)st dim + * DEC D(N-2) (n-2)st dim + * ... + * DEC D(1) 1st dim + * DEC -L(1) lower bound 1st dim + * DEC # # words/element + * OFFSET 1 EMA Low + * OFFSET 2 EMA High + */ +static t_stat cpu_ema_emio(uint32* rtn,uint32 bufl,uint32 dtbl,uint32 atbl,t_bool debug) +{ +uint32 xidex, idext1; +uint32 mseg, bufpgs, npgs; +EMA4 ema4, *e = &ema4; + +xidex = ReadIO(idx,UMAP); /* read ID extension */ +if (bufl & SIGN || /* buffer length negative? */ + xidex==0) goto em16; /* no EMA declared? */ + +idext1 = ReadWA(xidex+1); /* |logstrt mseg|d|physstrt ema| */ +mseg = (idext1 >> 1) & MSEGMASK; /* get logical start MSEG */ +if (!cpu_ema_emas(dtbl,atbl,e)) goto em16; /* resolve address */ +bufpgs = (bufl + e->offs) >> 10; /* # of pgs reqd for buffer */ +if ((bufl + e->offs) & 01777) bufpgs++; /* S11 add 1 if not at pg boundary */ +if ((bufpgs + e->pgoff) > e->emasz) goto em16; /* exceeds EMA limit? */ +npgs = (e->msoff + bufl) >> 10; /* # of pgs reqd for MSEG */ +if ((e->msoff + bufl) & 01777) npgs++; /* add 1 if not at pg boundary */ +if (npgs < e->msegsz) { + e->mseg = mseg; /* logical stat of MSEG */ + if (!cpu_ema_emat(e)) goto em16; /* do a std mapping */ +} else { + BR = mseg + e->offs; /* logical start of buffer */ + e->npgs = bufpgs; /* S5 # pgs required */ + e->ipgs = e->pgoff; /* S6 page offset to reqd pg */ + if (!cpu_ema_mmap02(e)) goto em16; /* do nonstd mapping */ +} +(*rtn)++; /* return via good exit */ +return SCPE_OK; + +em16: /* error condition */ +AR=0x3136; /* AR = '16' */ +BR=0x454d; /* BR = 'EM' */ +return SCPE_OK; /* return via unmodified rtn */ +} + +/* .EMAP microcode routine, resolves both EMA/non-EMA calls + * Call: + * OCT 105257B + * DEF RTN error return (rtn), good return is rtn+1 + * DEF ARRAY[,I] array base (abase) + * DEF TABLE[,I] array declaration (dtbl) + * DEF A(N)[,I] actual subscripts (atbl) + * DEF A(N-1)[,I] + * ... + * DEF A(2)[,I] + * DEF A(1)[,I] + * RTN EQU * error return A="15", B="EM" + * RTN+1 EQU *+1 good return B=logical address + * + * TABLE DEC # # dimensions + * DEC -L(N) + * DEC D(N-1) + * DEC -L(N-1) lower bound (n-1)st dim + * DEC D(N-2) (n-2)st dim + * ... + * DEC D(1) 1st dim + * DEC -L(1) lower bound 1st dim + * DEC # # words/element + * OFFSET 1 EMA Low + * OFFSET 2 EMA High + */ +static t_stat cpu_ema_emap(uint32* rtn,uint32 abase,uint32 dtbl,uint32 atbl,t_bool debug) +{ +uint32 xidex, eqt, idext0, idext1; +int32 sub, act, low, ndim, sz; +uint32 offs, pgoff, emasz, phys, msgn, mseg, sum, MA, pg0, pg1; + +xidex = ReadIO(idx,UMAP); /* read ID Extension */ +if (xidex) { /* is EMA declared? */ + idext1 = ReadWA(xidex+1); /* get word 1 of idext */ + mseg = (idext1 >> 1) & MSEGMASK; /* get logical start MSEG */ + if (abase >= mseg) { /* EMA reference? */ + if (!cpu_ema_resolve(dtbl,atbl,&sum)) /* calculate subscript */ + goto em15; + offs = sum & 01777; /* address offset within page */ + pgoff = sum >> 10; /* ema offset in pages */ + if (pgoff > 1023) goto em15; /* overflow? */ + eqt = ReadIO(xeqt,UMAP); + emasz = ReadWA(eqt+28) & 01777; /* EMA size in pages */ + phys = idext1 & 01777; /* physical start pg of EMA */ + if (pgoff > emasz) goto em15; /* outside EMA range? */ + + msgn = mseg >> 10; /* get # of 1st MSEG reg */ + phys += pgoff; + + pg0 = dms_rmap(UMAP+0); /* read base page map# */ + pg1 = dms_rmap(UMAP+1); /* save map# 1 */ + dms_wmap(UMAP+1,pg0); /* map #0 into reg #1 */ + + WriteIO(umaps+msgn, phys, UMAP); /* store 1st mapped pg in user map */ + dms_wmap(UMAP+msgn, phys); /* and set the map register */ + phys = (pgoff+1)==emasz ? 0140000 : phys+1; /* protect 2nd map if end of EMA */ + WriteIO(umaps+msgn+1, phys, UMAP); /* store 2nd mapped pg in user map */ + dms_wmap(UMAP+msgn+1, phys); /* and set the map register */ + + dms_wmap(UMAP+1,pg1); /* restore map #1 */ + + idext0 = ReadWA(xidex+0) | 0100000; /* set NS flag in id extension */ + WriteIO(xidex+0, idext0, SMAP); /* save back value */ + AR = 0; /* was successful */ + BR = mseg + offs; /* calculate log address */ + (*rtn)++; /* return via good exit */ + return SCPE_OK; + } +} /* not EMA reference */ +ndim = ReadW(dtbl++); +if (ndim<0) goto em15; /* negative ´dimensions */ +sum = 0; /* accu for index calc */ +while (ndim > 0) { + MA = ReadW(atbl++); /* get address of A(N) */ + resolve (MA, &MA, 0); + act = ReadW(MA); /* A(N) */ + low = ReadW(dtbl++); /* -L(N) */ + sub = SEXT(act) + SEXT(low); /* subscript */ + if (sub & 0xffff8000) goto em15; /* overflow? */ + sum += sub; /* accumulate */ + sz = ReadW(dtbl++); + sz = SEXT(sz); + if (sz < 0) goto em15; + sum *= sz; /* and multiply with sz of dimension */ + if (sum & 0xffff8000) goto em15; /* overflow? */ + ndim--; +} +BR = abase + sum; /* add displacement */ +(*rtn)++; /* return via good exit */ +return SCPE_OK; + +em15: /* error condition */ + AR=0x3135; /* AR = '15' */ + BR=0x454d; /* BR = 'EM' */ + return SCPE_OK; /* return via unmodified e->rtn */ +} + +t_stat cpu_rte_ema (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +OP_PAT pattern; +uint32 entry, rtn; +t_bool debug = DEBUG_PRI (cpu_dev, DEB_EMA); + +if ((cpu_unit.flags & UNIT_EMA) == 0) /* EMA option installed? */ + return stop_inst; + +entry = IR & 017; /* mask to entry point */ +pattern = op_ema[entry]; /* get operand pattern */ + +if (pattern != OP_N) + if (reason = cpu_ops (pattern, op, intrq)) /* get instruction operands */ + return reason; + +if (debug) { /* debugging? */ + fprintf (sim_deb, ">>CPU EMA: PC = %06o, IR = %06o (", err_PC,IR); /* print preamble and IR */ + fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ + NULL, SWMASK('M')); + fputc (')', sim_deb); + + fprint_ops (pattern, op); /* print operands */ + fputc ('\n', sim_deb); /* terminate line */ + } + +switch (entry) { /* decode IR<3:0> */ + case 000: /* .EMIO 105240 (OP_A) */ + rtn = op[0].word; + reason = cpu_ema_emio(&rtn, op[1].word, + op[2].word, PC, debug); /* handle the EMIO instruction */ + PC = rtn; + if (debug) + fprintf (sim_deb, ">>CPU EMA: return .EMIO: AR = %06o, BR = %06o, rtn=%s\n", + AR, BR, PC==op[0].word?"error":"good"); + break; + + case 001: /* .MMAP 105241 (OP_AKK) */ + reason = cpu_ema_mmap(op[1].word, + op[2].word, debug); /* handle the MMAP instruction */ + if (debug) + fprintf (sim_deb, ">>CPU EMA: return .MMAP: AR = %06o\n",AR); + break; + + case 002: /* [test] 105242 (OP_N) */ + /* effectively, this code just returns without error: + * real microcode will set S register to 102077B when in single step mode */ + if (sim_step==1) { + if (debug) + fprintf(sim_deb, ">>CPU EMA: EMA option 92067 correctly installed: S=102077\n"); + SR = 0102077; + } + break; + + case 017: /* .EMAP 105247 (OP_A) */ + rtn = op[0].word; /* error return */ + reason = cpu_ema_emap(&rtn, op[1].word, + op[2].word, PC, debug); /* handle the EMAP instruction */ + PC = rtn; + if (debug) { + fprintf (sim_deb, ">>CPU EMA: return .EMAP: AR = %06o, BR = %06o, rtn=%s\n", + AR, BR, PC==op[0].word?"error":"good"); + } + break; + + default: /* others undefined */ + reason = stop_inst; + } + +return reason; +} diff --git a/HP2100/hp2100_cpu6.c b/HP2100/hp2100_cpu6.c new file mode 100644 index 0000000..88827ea --- /dev/null +++ b/HP2100/hp2100_cpu6.c @@ -0,0 +1,811 @@ +/* hp2100_cpu6.c: HP 1000 RTE-6/VM OS instructions + + Copyright (c) 2006-2007, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + CPU6 RTE-6/VM OS instructions + + 27-Nov-07 JDB Implemented OS instructions + 26-Sep-06 JDB Created + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. +*/ + + +#include +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + + +/* external variables */ + +extern jmp_buf save_env; /* MP abort handler */ + + +/* Offsets to data and addresses within RTE. */ + +static const uint32 xi = 0001647; /* XI address */ +static const uint32 intba = 0001654; /* INTBA address */ +static const uint32 intlg = 0001655; /* INTLG address */ +static const uint32 eqt1 = 0001660; /* EQT1 address */ +static const uint32 eqt11 = 0001672; /* EQT11 address */ +static const uint32 pvcn = 0001712; /* PVCN address */ +static const uint32 xsusp = 0001730; /* XSUSP address */ +static const uint32 dummy = 0001737; /* DUMMY address */ +static const uint32 mptfl = 0001770; /* MPTFL address */ +static const uint32 eqt12 = 0001771; /* EQT12 address */ +static const uint32 eqt15 = 0001774; /* EQT15 address */ +static const uint32 vctr = 0002000; /* VCTR address */ + +static const uint32 CLC_0 = 0004700; /* CLC 0 instruction */ +static const uint32 STC_0 = 0000700; /* STC 0 instruction */ +static const uint32 CLF_0 = 0001100; /* CLF 0 instruction */ +static const uint32 STF_0 = 0000100; /* STF 0 instruction */ +static const uint32 SFS_0_C = 0003300; /* SFS 0,C instruction */ + +enum vctr_offsets { dms_offset = 0, /* DMS status */ + int_offset, /* interrupt system status */ + sc_offset, /* select code */ + clck_offset, /* TBG IRQ handler */ + cic4_offset, /* illegal IRQ handler */ + cic2_offset, /* device IRQ handler */ + sked_offset, /* prog sched IRQ handler */ + rqst_offset, /* EXEC request handler */ + cic_offset, /* IRQ location */ + perr_offset, /* parity error IRQ handler */ + mper_offset, /* memory protect IRQ handler */ + lxnd_offset }; /* $LIBR return */ + + +/* RTE-6/VM Operating System Instructions + + The OS instructions were added to acccelerate certain time-consuming + operations of the RTE-6/VM operating system, HP product number 92084A. + Microcode was available for the E- and F-Series; the M-Series used software + equivalents. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A 92084A 92084A + + The routines are mapped to instruction codes as follows: + + Instr. 1000-E/F Description + ------ -------- ---------------------------------------------- + $LIBR 105340 Enter privileged/reentrant library routine + $LIBX 105341 Exit privileged/reentrant library routine + .TICK 105342 TBG tick interrupt handler + .TNAM 105343 Find ID segment that matches name + .STIO 105344 Configure I/O instructions + .FNW 105345 Find word with user increment + .IRT 105346 Interrupt return processing + .LLS 105347 Linked list search + + .SIP 105350 Skip if interrupt pending + .YLD 105351 .SIP completion return point + .CPM 105352 Compare words LT/EQ/GT + .ETEQ 105353 Set up EQT pointers in base page + .ENTN 105354 Transfer parameter addresses (utility) + $OTST * 105355 OS firmware self test + .ENTC 105356 Transfer parameter addresses (priv/reent) + .DSPI 105357 Set display indicator + + Opcodes 105354-105357 are "dual use" instructions that take different + actions, depending on whether they are executed from a trap cell during an + interrupt. When executed from a trap cell, they have these actions: + + Instr. 1000-E/F Description + ------ -------- ---------------------------------------------- + $DCPC * 105354 DCPC channel interrupt processing + $MPV * 105355 MP/DMS/PE interrupt processing + $DEV * 105356 Standard device interrupt processing + $TBG * 105357 TBG interrupt processing + + * These mnemonics are recognized by symbolic examine/deposit but are not + official HP mnemonics. + + Notes: + + 1. The microcode differentiates between interrupt processing and normal + execution of the "dual use" instructions by testing the CPU flag. + Interrupt vectoring sets the flag; a normal instruction fetch clears it. + Under simulation, interrupt vectoring is indicated by the value of the + "iotrap" parameter (0 = normal instruction, 1 = trap cell instruction). + + 2. The operand patterns for .ENTN and .ENTC normally would be coded as + "OP_A", as each takes a single address as a parameter. However, because + they might also be executed from a trap cell, we cannot assume that P+1 + is an address, or we might cause a DM abort when trying to resolve + indirects. Therefore, "OP_A" handling is done within each routine, once + the type of use is determined. + + 3. The microcode for .ENTC, .ENTN, .FNW, .LLS, .TICK, and .TNAM explicitly + checks for interrupts during instruction execution. In addition, the + .STIO, .CPM, and .LLS instructions implicitly check for interrupts + during parameter indirect resolution. Because the simulator calculates + interrupt requests only between instructions, this behavior is not + simulated. + + 4. The microcode executes certain I/O instructions (e.g., CLF 0) by + building the instruction in the IR and executing an IOG micro-order. We + simulate this behavior by calling the "iogrp" handler with the + appropriate instruction, rather than manipulating the I/O system + directly, so that we will remain unaffected by any future changes to the + underlying I/O simulation structure. + + 5. The $OTST and .DSPI microcode uses features (reading the RPL switches + and boot loader ROM data, loading the display register) that are not + simulated. The remaining functions of the $OTST instruction are + provided. The .DSPI instruction is a NOP or unimplemented instruction + stop. + + 6. The microcode detects a privileged system and takes some additional + actions if one is found. We provide simulations of these actions. + However, at the current time, the simulator does not provide a + privileged interrupt fence card, so this code is untested. + + 7. Because of the volume of calls to the OS firmware, debug printouts + attempt to write only one line per instruction invocation. This means + that calling and returned register values are printed separately, with a + newline added at the end of execution. However, many instructions can + MP or DM abort, either intentionally or due to improper use. That would + leave debug lines without the required trailing newlines. + + There are two ways to address this: either we could replace the CPU's + setjmp buffer with one that points to a routine that adds the missing + newline, or we can add a semaphore that is tested on entry to see if it + is already set, implying a longjmp occurred, and then add the newline if + so. The latter would add the newline when the MP trap cell instruction + was entered or when the next user-level instruction was executed. + However, the merged-line problem would still exist if some other module + generated intervening debug printouts. So we do the former. This does + mean that this routine must be changed if the MP abort mechanism is + changed. + + 8. The $LIBX instruction is executed to complete either a privileged or + reentrant execution. In the former case, the privileged nest counter + ($PVCN) is decremented. In the latter, $PVCN decrement is attempted but + the write will trap with an MP violation, as reentrant routines execute + with the interrupt system on. RTE will then complete the release of + memory allocated for the original $LIBR call. + + Additional references: + - RTE-6/VM OS Microcode Source (92084-18831, revision 8). + - RTE-6/VM Technical Specifications (92084-90015, Apr-1983). +*/ + + +/* Save the CPU registers. + + The CPU registers are saved in the current ID segment in preparation for + interrupt handling. Although the RTE base page has separate pointers for the + P, A, B, and E/O registers, they are always contiguous, and the microcode + simply increments the P-register pointer (XSUSP) to store the remaining + values. + + This routine is called from the trap cell interrupt handlers and from the + $LIBX processor. In the latter case, the privileged system interrupt + handling is not required, so it is bypassed. In either case, the current map + will be the system map when we are called. +*/ + +static t_stat cpu_save_regs (uint32 iotrap) +{ +uint16 save_area, priv_fence; +t_stat reason = SCPE_OK; + +save_area = ReadW (xsusp); /* addr of PABEO save area */ + +WriteW (save_area + 0, PC); /* save P */ +WriteW (save_area + 1, AR); /* save A */ +WriteW (save_area + 2, BR); /* save B */ +WriteW (save_area + 3, (E << 15) & SIGN | O & 1); /* save E and O */ + +save_area = ReadW (xi); /* addr of XY save area */ +WriteWA (save_area + 0, XR); /* save X (in user map) */ +WriteWA (save_area + 1, YR); /* save Y (in user map) */ + +if (iotrap) { /* do priv setup only if IRQ */ + priv_fence = ReadW (dummy); /* get priv fence select code */ + + if (priv_fence) { /* privileged system? */ + reason = iogrp (STC_0 + priv_fence, iotrap); /* STC SC on priv fence */ + reason = iogrp (CLC_0 + DMA0, iotrap); /* CLC 6 to inh IRQ on DCPC 0 */ + reason = iogrp (CLC_0 + DMA1, iotrap); /* CLC 7 to inh IRQ on DCPC 1 */ + reason = iogrp (STF_0, iotrap); /* turn interrupt system back on */ + } + } + +return reason; +} + + +/* Save the machine state at interrupt. + + This routine is called from each of the trap cell instructions. Its purpose + is to save the complete state of the machine in preparation for interrupt + handling. + + For the MP/DMS/PE interrupt, the interrupting device must not be cleared and + the CPU registers must not be saved until it is established that the + interrupt is not caused by a parity error. Parity errors cannot be + inhibited, so the interrupt may have occurred while in RTE. Saving the + registers would overwrite the user's registers that were saved at RTE entry. + + Note that the trap cell instructions are dual-use and invoke this routine + only when they are executed during interrupts. Therefore, the current map + will always be the system map when we are called. +*/ + +static t_stat cpu_save_state (uint32 iotrap) +{ +uint16 vectors; +uint32 saved_PC, int_sys_off; +t_stat reason; + +saved_PC = PC; /* save current PC */ +reason = iogrp (SFS_0_C, iotrap); /* turn interrupt system off */ +int_sys_off = (PC == saved_PC); /* set flag if already off */ +PC = saved_PC; /* restore PC in case it bumped */ + +vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ + +WriteW (vectors + dms_offset, dms_upd_sr ()); /* save DMS status (SSM) */ +WriteW (vectors + int_offset, int_sys_off); /* save int status */ +WriteW (vectors + sc_offset, intaddr); /* save select code */ + +WriteW (mptfl, 1); /* show MP is off */ + +if (intaddr != 5) { /* only if not MP interrupt */ + reason = iogrp (CLF_0 + intaddr, iotrap); /* issue CLF to device */ + cpu_save_regs (iotrap); /* save CPU registers */ + } + +return reason; +} + + +/* Get the interrupt table entry corresponding to a select code. + + Return the word in the RTE interrupt table that corresponds to the + interrupting select code. Return 0 if the select code is beyond the end of + the table. +*/ + +uint32 cpu_get_intbl (uint32 select_code) +{ +uint16 interrupt_table; /* interrupt table (starts with SC 06) */ +uint16 table_length; /* length of interrupt table */ + +interrupt_table = ReadW (intba); /* get int table address */ +table_length = ReadW (intlg); /* get int table length */ + +if (select_code - 6 > table_length) /* SC beyond end of table? */ + return 0; /* return 0 for illegal interrupt */ +else + return ReadW (interrupt_table + select_code - 6); /* else return table entry */ +} + + +/* RTE-6/VM OS instruction dispatcher. + + Debugging printouts are provided with the OS and OSTBG debug flags. The OS + flag enables tracing for all instructions except for the three-instruction + sequence executed for the time-base generator interrupt ($TBG, .TICK, and + .IRT). The OSTBG flag enables tracing for just the TBG sequence. The flags + are separate, as the TBG generates 100 interrupts per second. Use caution + when specifying the OSTBG flag, as the debug output file will grow rapidly. + Note that the OS flag enables the .IRT instruction trace for all cases except + a TBG interrupt. + + The firmware self-test instruction (105355) is always allowed, regardless of + the UNIT_VMAOS setting. This is because RTE-6/VM will always test for the + presence of OS and VMA firmware on E/F-Series machines. If the firmware is + not present, then these instructions are NOPs and return to P+1. RTE will + then HLT 21. This means that RTE-6/VM will not run on an E/F-Series machine + without the OS and VMA firmware. + + Howwever, RTE allows the firmware instructions to be disabled for debugging + purposes. If the firmware is present and returns to P+2 but sets the X + register to 0, then RTE will use software equivalents. We enable this + condition when the OS firmware is enabled (SET CPU VMA), the OS debug flag is + set (SET CPU DEBUG=OS), but debug output has been disabled (SET CONSOLE + NODEBUG). That is: + + OS Debug + Firmware Flag Output Tracing Self-Test Instruction + ======== ===== ====== ======= ===================== + disabled x x off NOP + enabled clear x off X = revision code + enabled set off off X = 0 + enabled set on on X = revision code +*/ + +static const OP_PAT op_os[16] = { + OP_A, OP_A, OP_N, OP_N, /* $LIBR $LIBX .TICK .TNAM */ + OP_A, OP_K, OP_A, OP_KK, /* .STIO .FNW .IRT .LLS */ + OP_N, OP_C, OP_KK, OP_N, /* .SIP .YLD .CPM .ETEQ */ + OP_N, OP_N, OP_N, OP_N /* .ENTN $OTST .ENTC .DSPI */ + }; + +t_stat cpu_rte_os (uint32 IR, uint32 intrq, uint32 iotrap) +{ +t_stat reason = SCPE_OK; +OPS op; +OP_PAT pattern; +uint32 entry, count, cp, sa, da, i, ma; +uint16 vectors, save_area, priv_fence, eoreg, eqt, key; +char test[6], target[6]; +jmp_buf mp_handler; +int abortval; +t_bool debug_print; +static t_bool tbg_tick = FALSE; /* set if processing TBG interrupt */ + +if ((IR != 0105355) && /* allow self-test with OS disabled */ + (cpu_unit.flags & UNIT_VMAOS) == 0) /* VMA/OS option installed? */ + return stop_inst; + +entry = IR & 017; /* mask to entry point */ +pattern = op_os[entry]; /* get operand pattern */ + +if (pattern != OP_N) + if (reason = cpu_ops (pattern, op, intrq)) /* get instruction operands */ + return reason; + +tbg_tick = tbg_tick || (IR == 0105357) && iotrap; /* set TBG interrupting flag */ + +debug_print = (DEBUG_PRI (cpu_dev, DEB_OS) && !tbg_tick) || + (DEBUG_PRI (cpu_dev, DEB_OSTBG) && tbg_tick); + +if (debug_print) { + fprintf (sim_deb, ">>CPU OS: IR = %06o (", IR); /* print preamble and IR */ + fprint_sym (sim_deb, (iotrap ? intaddr : err_PC), /* print instruction mnemonic */ + (t_value *) &IR, NULL, SWMASK('M')); + fputc (')', sim_deb); + + fprint_ops (pattern, op); /* print operands */ + + memcpy (mp_handler, save_env, sizeof (jmp_buf)); /* save original MP handler */ + abortval = setjmp (save_env); /* set new MP abort handler */ + + if (abortval != 0) { /* MP abort? */ + fputs ("...MP abort\n", sim_deb); /* report it and terminate line */ + memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */ + longjmp (save_env, abortval); /* transfer to MP handler */ + } + } + +switch (entry) { /* decode IR<3:0> */ + + case 000: /* $LIBR 105340 (OP_A) */ + if ((op[0].word != 0) || /* reentrant call? */ + (CTL (PRO) && (ReadW (dummy) != 0))) { /* or priv call + MP on + priv sys? */ + if (dms_ump) { /* called from user map? */ + dms_viol (err_PC, MVI_PRV); /* privilege violation */ + } + dms_ump = SMAP; /* set system map */ + + vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ + PC = ReadW (vectors + mper_offset); /* vector to $MPER for processing */ + } + + else { /* privileged call */ + if (CTL (PRO)) { /* memory protect on? */ + clrCTL (PRO); /* turn it off */ + reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */ + WriteW (mptfl, 1); /* show MP is off */ + save_area = ReadW (xsusp); /* get addr of P save area */ + + if (dms_ump) /* user map current? */ + WriteWA (save_area, (PC - 2) & VAMASK); /* set point of suspension */ + else /* system map current */ + WriteW (save_area, (PC - 2) & VAMASK); /* set point of suspension */ + } + + WriteW (pvcn, (ReadW (pvcn) + 1) & DMASK); /* increment priv nest counter */ + } + break; + + case 001: /* $LIBX 105341 (OP_A) */ + PC = ReadW (op[0].word); /* set P to return point */ + count = (ReadW (pvcn) - 1) & DMASK; /* decrement priv nest counter */ + WriteW (pvcn, count); /* write it back */ + + if (count == 0) { /* end of priv mode? */ + dms_ump = SMAP; /* set system map */ + reason = cpu_save_regs (iotrap); /* save registers */ + vectors = ReadW (vctr); /* get address of vectors */ + PC = ReadW (vectors + lxnd_offset); /* vector to $LXND for processing */ + } + break; + + case 002: /* .TICK 105342 (OP_N) */ + if (debug_print) /* debugging? */ + fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */ + + do { + eqt = (ReadW (AR) + 1) & DMASK; /* bump timeout from EQT15 */ + + if (eqt != 1) { /* was timeout active? */ + WriteW (AR, eqt); /* yes, write it back */ + + if (eqt == 0) /* did timeout expire? */ + break; /* P+0 return for timeout */ + } + + AR = (AR + 15) & DMASK; /* point at next EQT15 */ + BR = (BR - 1) & DMASK; /* decrement count of EQTs */ + } while ((BR > 0) && (eqt != 0)); /* loop until timeout or done */ + + if (BR == 0) /* which termination condition? */ + PC = (PC + 1) & VAMASK; /* P+1 return for no timeout */ + + if (debug_print) /* debugging? */ + fprint_regs ("; result:", /* print return registers */ + REG_A | REG_B | REG_P_REL, + err_PC + 1); + break; + + case 003: /* .TNAM 105343 (OP_N) */ + if (debug_print) /* debugging? */ + fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */ + + E = 1; /* preset flag for not found */ + cp = (BR << 1) & DMASK; /* form char addr (B is direct) */ + + for (i = 0; i < 5; i++) { /* copy target name */ + target[i] = (char) ReadB (cp); /* name is only 5 chars */ + cp = (cp + 1) & DMASK; + } + + if ((target[0] == '\0') && (target[1] == '\0')) /* if name is null, */ + break; /* return immed to P+0 */ + + key = ReadW (AR); /* get first keyword addr */ + + while (key != 0) { /* end of keywords? */ + cp = ((key + 12) << 1) & DMASK; /* form char addr of name */ + + for (i = 0; i < 6; i++) { /* copy test name */ + test[i] = (char) ReadB (cp); /* name is only 5 chars */ + cp = (cp + 1) & DMASK; /* but copy 6 to get flags */ + } + + if (strncmp (target, test, 5) == 0) { /* names match? */ + AR = (key + 15) & DMASK; /* A = addr of IDSEG [15] */ + BR = key; /* B = addr of IDSEG [0] */ + E = (uint32) ((test[5] >> 4) & 1); /* E = short ID segment bit */ + PC = (PC + 1) & VAMASK; /* P+1 for found return */ + break; + } + + AR = (AR + 1) & DMASK; /* bump to next keyword */ + key = ReadW (AR); /* get next keyword */ + }; + + if (debug_print) /* debugging? */ + fprint_regs ("; result:", /* print return registers */ + REG_A | REG_B | REG_E | REG_P_REL, + err_PC + 1); + break; + + case 004: /* .STIO 105344 (OP_A) */ + count = op[0].word - PC; /* get count of operands */ + + if (debug_print) /* debugging? */ + fprintf (sim_deb, /* print registers on entry */ + ", A = %06o, count = %d", AR, count); + + for (i = 0; i < count; i++) { + ma = ReadW (PC); /* get operand address */ + + if (reason = resolve (ma, &ma, intrq)) { /* resolve indirect */ + PC = err_PC; /* IRQ restarts instruction */ + break; + } + + WriteW (ma, ReadW (ma) & ~I_DEVMASK | AR); /* set SC into instruction */ + PC = (PC + 1) & VAMASK; /* bump to next */ + } + break; + + case 005: /* .FNW 105345 (OP_K) */ + if (debug_print) /* debugging? */ + fprint_regs (",", REG_A | REG_B | REG_X, 0); /* print entry registers */ + + while (XR != 0) { /* all comparisons done? */ + key = ReadW (BR); /* read a buffer word */ + + if (key == AR) { /* does it match? */ + PC = (PC + 1) & VAMASK; /* P+1 found return */ + break; + } + + BR = (BR + op[0].word) & DMASK; /* increment buffer ptr */ + XR = (XR - 1) & DMASK; /* decrement remaining count */ + } + /* P+0 not found return */ + if (debug_print) /* debugging? */ + fprint_regs ("; result:", /* print return registers */ + REG_A | REG_B | REG_X | REG_P_REL, + err_PC + 2); + break; + + case 006: /* .IRT 105346 (OP_A) */ + save_area = ReadW (xsusp); /* addr of PABEO save area */ + + WriteW (op[0].word, ReadW (save_area + 0)); /* restore P to DEF RTN */ + + AR = ReadW (save_area + 1); /* restore A */ + BR = ReadW (save_area + 2); /* restore B */ + + eoreg = ReadW (save_area + 3); /* get combined E and O */ + E = (eoreg >> 15) & 1; /* restore E */ + O = eoreg & 1; /* restore O */ + + save_area = ReadW (xi); /* addr of XY save area */ + XR = ReadWA (save_area + 0); /* restore X (from user map) */ + YR = ReadWA (save_area + 1); /* restore Y (from user map) */ + + reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */ + WriteW (mptfl, 0); /* show MP is on */ + + priv_fence = ReadW (dummy); /* get priv fence select code */ + + if (priv_fence) { /* privileged system? */ + reason = iogrp (CLC_0 + priv_fence, iotrap); /* CLC SC on priv fence */ + reason = iogrp (STF_0 + priv_fence, iotrap); /* STF SC on priv fence */ + + if (cpu_get_intbl (DMA0) & SIGN) /* DCPC 0 active? */ + reason = iogrp (STC_0 + DMA0, iotrap); /* STC 6 to enable IRQ on DCPC 0 */ + + if (cpu_get_intbl (DMA1) & SIGN) /* DCPC 1 active? */ + reason = iogrp (STC_0 + DMA1, iotrap); /* STC 7 to enable IRQ on DCPC 1 */ + } + + tbg_tick = 0; /* .IRT terminates TBG servicing */ + break; + + case 007: /* .LLS 105347 (OP_KK) */ + if (debug_print) /* debugging? */ + fprint_regs (",", REG_A | REG_B | REG_E, 0); /* print entry registers */ + + AR = AR & ~SIGN; /* clear sign bit of A */ + + while ((AR != 0) && ((AR & SIGN) == 0)) { /* end of list or bad list? */ + key = ReadW ((AR + op[1].word) & VAMASK); /* get key value */ + + if ((E == 0) && (key == op[0].word) || /* for E = 0, key = arg? */ + (E != 0) && (key > op[0].word)) /* for E = 1, key > arg? */ + break; /* search is done */ + + BR = AR; /* B = last link */ + AR = ReadW (AR); /* A = next link */ + } + + if (AR == 0) /* exhausted list? */ + PC = (PC + 1) & VAMASK; /* P+1 arg not found */ + else if ((AR & SIGN) == 0) /* good link? */ + PC = (PC + 2) & VAMASK; /* P+2 arg found */ + /* P+0 bad link */ + if (debug_print) /* debugging? */ + fprint_regs ("; result:", /* print return registers */ + REG_A | REG_B | REG_P_REL, + err_PC + 3); + break; + + case 010: /* .SIP 105350 (OP_N) */ + reason = iogrp (STF_0, iotrap); /* turn interrupt system on */ + intrq = calc_int (); /* check for interrupt requests */ + reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */ + + if (intrq) /* was interrupt pending? */ + PC = (PC + 1) & VAMASK; /* P+1 return for pending IRQ */ + /* P+0 return for no pending IRQ */ + if (debug_print) /* debugging? */ + fprintf (sim_deb, /* print return registers */ + "CIR = %02o, return = P+%d", + intrq, PC - (err_PC + 1)); + break; + + case 011: /* .YLD 105351 (OP_C) */ + PC = op[0].word; /* pick up point of resumption */ + reason = iogrp (STF_0, iotrap); /* turn interrupt system on */ + ion_defer = 0; /* kill defer so irq occurs immed */ + break; + + case 012: /* .CPM 105352 (OP_KK) */ + if (INT16 (op[0].word) > INT16 (op[1].word)) + PC = (PC + 2) & VAMASK; /* P+2 arg1 > arg2 */ + else if (INT16 (op[0].word) < INT16 (op[1].word)) + PC = (PC + 1) & VAMASK; /* P+1 arg1 < arg2 */ + /* P+0 arg1 = arg2 */ + if (debug_print) /* debugging? */ + fprint_regs (",", REG_P_REL, err_PC + 3); /* print return registers */ + break; + + case 013: /* .ETEQ 105353 (OP_N) */ + eqt = ReadW (eqt1); /* get addr of EQT1 */ + + if (AR != eqt) { /* already set up? */ + for (eqt = eqt1; eqt <= eqt11; eqt++) /* init EQT1-EQT11 */ + WriteW (eqt & VAMASK, (AR++ & DMASK)); + for (eqt = eqt12; eqt <= eqt15; eqt++) /* init EQT12-EQT15 */ + WriteW (eqt & VAMASK, (AR++ & DMASK)); /* (not contig with EQT1-11) */ + } + + if (debug_print) /* debugging? */ + fprintf (sim_deb, /* print return registers */ + ", A = %06o, EQT1 = %06o", AR, eqt); + break; + + case 014: /* .ENTN/$DCPC 105354 (OP_N) */ + if (iotrap) { /* in trap cell? */ + reason = cpu_save_state (iotrap); /* DMA interrupt */ + AR = cpu_get_intbl (intaddr) & ~SIGN; /* get intbl value and strip sign */ + goto DEVINT; /* vector by intbl value */ + } + + else { /* .ENTN instruction */ + ma = (PC - 2) & VAMASK; /* get addr of entry point */ + + ENTX: /* enter here from .ENTC */ + reason = cpu_ops (OP_A, op, intrq); /* get instruction operand */ + da = op[0].word; /* get addr of 1st formal */ + count = ma - da; /* get count of formals */ + sa = ReadW (ma); /* get addr of 1st actual */ + WriteW (ma, (sa + count) & VAMASK); /* adjust return point to skip actuals */ + + if (debug_print) /* debugging? */ + fprintf (sim_deb, /* print entry registers */ + ", op [0] = %06o, pcount = %d", + da, count); + + for (i = 0; i < count; i++) { /* parameter loop */ + ma = ReadW (sa); /* get addr of actual */ + sa = (sa + 1) & VAMASK; /* increment address */ + + if (reason = resolve (ma, &ma, intrq)) { /* resolve indirect */ + PC = err_PC; /* irq restarts instruction */ + break; + } + + WriteW (da, ma); /* put addr into formal */ + da = (da + 1) & VAMASK; /* increment address */ + } + + if (entry == 016) /* call was .ENTC? */ + AR = sa; /* set A to return address */ + } + break; + + case 015: /* $OTST/$MPV 105355 (OP_N) */ + if (iotrap) { /* in trap cell? */ + reason = cpu_save_state (iotrap); /* MP/DMS/PE interrupt */ + vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ + + if (mp_viol & SIGN) { /* parity error? */ + WriteW (vectors + cic_offset, PC); /* save point of suspension in $CIC */ + PC = ReadW (vectors + perr_offset); /* vector to $PERR for processing */ + } + + else { /* MP/DMS violation */ + cpu_save_regs (iotrap); /* save CPU registers */ + PC = ReadW (vectors + rqst_offset); /* vector to $RQST for processing */ + } + + if (debug_print) { /* debugging? */ + fprint_regs (",", REG_CIR, 0); /* print interrupt source */ + /* and cause */ + if (mp_viol & SIGN) + fputs (", parity error", sim_deb); + else if (mp_mevff) + fputs (", DM violation", sim_deb); + else + fputs (", MP violation", sim_deb); + } + } + + else { /* self-test instruction */ + if (cpu_unit.flags & UNIT_VMAOS) { /* VMA/OS option installed? */ + YR = 0000000; /* RPL switch (not implemented) */ + AR = 0000000; /* LDR [B] (not implemented) */ + SR = 0102077; /* test passed code */ + PC = (PC + 1) & VAMASK; /* P+1 return for firmware OK */ + + if ((cpu_dev.dctrl & DEB_OS) && /* OS debug flag set, */ + (sim_deb == NULL)) /* but debugging disabled? */ + XR = 0; /* rev = 0 means RTE won't use ucode */ + else + XR = 010; /* firmware revision 10B = 8 */ + } + + if (debug_print) /* debugging? */ + fprint_regs (",", REG_X | REG_P_REL, /* print return registers */ + err_PC + 1); + } + + break; /* self-test is NOP if no firmware */ + + case 016: /* .ENTC/$DEV 105356 (OP_N) */ + if (iotrap) { /* in trap cell? */ + reason = cpu_save_state (iotrap); /* device interrupt */ + AR = cpu_get_intbl (intaddr); /* get interrupt table value */ + + DEVINT: + vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ + + if (INT16 (AR) < 0) /* negative (program ID)? */ + PC = ReadW (vectors + sked_offset); /* vector to $SKED for processing */ + else if (AR > 0) /* positive (EQT address)? */ + PC = ReadW (vectors + cic2_offset); /* vector to $CIC2 for processing */ + else /* zero (illegal interrupt) */ + PC = ReadW (vectors + cic4_offset); /* vector to $CIC4 for processing */ + + if (debug_print) /* debugging? */ + fprintf (sim_deb, /* print return registers */ + ", CIR = %02o, INTBL = %06o", + intaddr, AR); + } + + else { /* .ENTC instruction */ + ma = (PC - 4) & VAMASK; /* get addr of entry point */ + goto ENTX; /* continue with common processing */ + } + break; + + case 017: /* .DSPI/$TBG 105357 (OP_N) */ + if (iotrap) { /* in trap cell? */ + reason = cpu_save_state (iotrap); /* TBG interrupt */ + vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ + PC = ReadW (vectors + clck_offset); /* vector to $CLCK for processing */ + + if (debug_print) /* debugging? */ + fprint_regs (",", REG_CIR, 0); /* print interrupt source */ + } + + else /* .DSPI instruction */ + reason = stop_inst; /* not implemented yet */ + + break; + } + +if (debug_print) { /* debugging? */ + fputc ('\n', sim_deb); /* terminate line */ + memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */ + } + +return reason; +} diff --git a/HP2100/hp2100_cpu7.c b/HP2100/hp2100_cpu7.c new file mode 100644 index 0000000..5b0e3d0 --- /dev/null +++ b/HP2100/hp2100_cpu7.c @@ -0,0 +1,945 @@ +/* hp2100_cpu7.c: HP 1000 VIS and SIGNAL/1000 microcode + + Copyright (c) 2006, J. David Bryan + Copyright (c) 2008, Holger Veit + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the authors shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the authors. + + CPU7 Vector Instruction Set and SIGNAL firmware + + 30-Apr-08 JDB Updated SIGNAL code from Holger + 24-Apr-08 HV Implemented SIGNAL + 20-Apr-08 JDB Updated comments + 26-Feb-08 HV Implemented VIS + + Primary references: + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - Macro/1000 Reference Manual (92059-90001, Dec-1992) + + Additional references are listed with the associated firmware + implementations, as are the HP option model numbers pertaining to the + applicable CPUs. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" + +#if defined (HAVE_INT64) /* int64 support available */ + +#include "hp2100_fp1.h" + + +t_stat cpu_vis (uint32 IR, uint32 intrq); /* Vector Instruction Set */ +t_stat cpu_signal (uint32 IR, uint32 intrq); /* SIGNAL/1000 Instructions */ + +static const OP zero = { { 0, 0, 0, 0, 0 } }; /* DEC 0.0D0 */ + + +/* Vector Instruction Set + + The VIS provides instructions that operate on one-dimensional arrays of + floating-point values. Both single- and double-precision operations are + supported. VIS uses the F-Series floating-point processor to handle the + floating-point math. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A N/A 12824A + + The routines are mapped to instruction codes as follows: + + Single-Precision Double-Precision + Instr. Opcode Subcod Instr. Opcode Subcod Description + ------ ------ ------ ------ ------ ------ ----------------------------- + VADD 101460 000000 DVADD 105460 004002 Vector add + VSUB 101460 000020 DVSUB 105460 004022 Vector subtract + VMPY 101460 000040 DVMPY 105460 004042 Vector multiply + VDIV 101460 000060 DVDIV 105460 004062 Vector divide + VSAD 101460 000400 DVSAD 105460 004402 Scalar-vector add + VSSB 101460 000420 DVSSB 105460 004422 Scalar-vector subtract + VSMY 101460 000440 DVSMY 105460 004442 Scalar-vector multiply + VSDV 101460 000460 DVSDV 105460 004462 Scalar-vector divide + VPIV 101461 0xxxxx DVPIV 105461 0xxxxx Vector pivot + VABS 101462 0xxxxx DVABS 105462 0xxxxx Vector absolute value + VSUM 101463 0xxxxx DVSUM 105463 0xxxxx Vector sum + VNRM 101464 0xxxxx DVNRM 105464 0xxxxx Vector norm + VDOT 101465 0xxxxx DVDOT 105465 0xxxxx Vector dot product + VMAX 101466 0xxxxx DVMAX 105466 0xxxxx Vector maximum value + VMAB 101467 0xxxxx DVMAB 105467 0xxxxx Vector maximum absolute value + VMIN 101470 0xxxxx DVMIN 105470 0xxxxx Vector minimum value + VMIB 101471 0xxxxx DVMIB 105471 0xxxxx Vector minimum absolute value + VMOV 101472 0xxxxx DVMOV 105472 0xxxxx Vector move + VSWP 101473 0xxxxx DVSWP 105473 0xxxxx Vector swap + .ERES 101474 -- -- -- -- Resolve array element address + .ESEG 101475 -- -- -- -- Load MSEG maps + .VSET 101476 -- -- -- -- Vector setup + [test] -- -- -- 105477 -- [self test] + + Instructions use IR bit 11 to select single- or double-precision format. The + double-precision instruction names begin with "D" (e.g., DVADD vs. VADD). + Most VIS instructions are two words in length, with a sub-opcode immediately + following the primary opcode. + + Notes: + + 1. The .VECT (101460) and .DVCT (105460) opcodes preface a single- or + double-precision arithmetic operation that is determined by the + sub-opcode value. The remainder of the dual-precision sub-opcode values + are "don't care," except for requiring a zero in bit 15. + + 2. The VIS uses the hardware FPP of the F-Series. FPP malfunctions are + detected by the VIS firmware and are indicated by a memory-protect + violation and setting the overflow flag. Under simulation, + malfunctions cannot occur. + + Additional references: + - 12824A Vector Instruction Set User's Manual (12824-90001, Jun-1979). + - VIS Microcode Source (12824-18059, revision 3). +*/ + +/* implemented in hp2100_cpu5.c (RTE-IV EMA functions) */ +extern t_stat cpu_ema_eres(uint32* rtn,uint32 dtbl,uint32 atbl, t_bool debug); +extern t_stat cpu_ema_eseg(uint32* rtn,uint32 ir,uint32 tbl, t_bool debug); +extern t_stat cpu_ema_vset(uint32* rtn,OPS op, t_bool debug); + +static const OP_PAT op_vis[16] = { + OP_N, OP_AAKAKAKK,OP_AKAKK, OP_AAKK, /* .VECT VPIV VABS VSUM */ + OP_AAKK, OP_AAKAKK, OP_AAKK, OP_AAKK, /* VNRM VDOT VMAX VMAB */ + OP_AAKK, OP_AAKK, OP_AKAKK, OP_AKAKK, /* VMIN VMIB VMOV VSWP */ + OP_AA, OP_A, OP_AAACCC,OP_N /* .ERES .ESEG .VSET [test] */ + }; + +static const t_bool op_ftnret[16] = { + FALSE, TRUE, TRUE, TRUE, + TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, TRUE, TRUE, + FALSE, TRUE, TRUE, FALSE, + }; + + +/* handle the scalar/vector base ops */ +static void vis_svop(uint32 subcode, OPS op, OPSIZE opsize) +{ +OP v1,v2; +int16 delta = opsize==fp_f ? 2 : 4; +OP s = ReadOp(op[0].word,opsize); +uint32 v1addr = op[1].word; +int16 ix1 = INT16(op[2].word) * delta; +uint32 v2addr = op[3].word; +int16 ix2 = INT16(op[4].word) * delta; +int16 i, n = INT16(op[5].word); +uint32 fpuop = (subcode & 060) | (opsize==fp_f ? 0 : 2); + +if (n <= 0) return; +for (i=0; ifpk[0] & 0100000) + +static void vis_abs(OP* in, uint32 opsize) +{ +uint32 sign = GET_MSIGN(in); /* get sign */ +if (sign) (void)fp_pcom(in, opsize); /* if negative, make positive */ +} + +static void vis_minmax(OPS op,OPSIZE opsize,t_bool domax,t_bool doabs) +{ +OP v1,vmxmn,res; +int16 delta = opsize==fp_f ? 2 : 4; +uint32 mxmnaddr = op[0].word; +uint32 v1addr = op[1].word; +int16 ix1 = INT16(op[2].word) * delta; +int16 n = INT16(op[3].word); +int16 i,mxmn,sign; +int32 subop = 020 | (opsize==fp_f ? 0 : 2); + +if (n <= 0) return; +mxmn = 0; /* index of maxmin element */ +vmxmn = ReadOp(v1addr,opsize); /* initialize with first element */ +if (doabs) vis_abs(&vmxmn,opsize); /* ABS(v[1]) if requested */ + +for (i = 0; ifpk[0] = in.fpk[0]; +out->fpk[1] = (in.fpk[1] & 0177400) | (in.fpk[3] & 0377); +} + +static void vis_vsmnm(OPS op,OPSIZE opsize,t_bool doabs) +{ +uint32 fpuop; +OP v1,sumnrm = zero; +int16 delta = opsize==fp_f ? 2 : 4; +uint32 saddr = op[0].word; +uint32 v1addr = op[1].word; +int16 ix1 = INT16(op[2].word) * delta; +int16 i,n = INT16(op[3].word); + +if (n <= 0) return; +/* calculates sumnrm = sumnrm + DBLE(v1[i]) resp DBLE(ABS(v1[i])) for incrementing i */ +for (i=0; i>CPU VIS: IR = %06o/%06o (", /* print preamble and IR */ + IR, subcode); + fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ + NULL, SWMASK('M')); + fprintf (sim_deb, "), P = %06o", err_PC); /* print location */ + fprint_ops (pattern, op); /* print operands */ + fputc ('\n', sim_deb); /* terminate line */ + } + +switch (entry) { /* decode IR<3:0> */ + case 000: /* .VECT (OP_special) */ + if (subcode & 0400) + vis_svop(subcode,op,opsize); /* scalar/vector op */ + else + vis_vvop(subcode,op,opsize); /* vector/vector op */ + break; + case 001: /* VPIV (OP_(A)AAKAKAKK) */ + vis_vpiv(op,opsize); + break; + case 002: /* VABS (OP_(A)AKAKK) */ + vis_vabs(op,opsize); + break; + case 003: /* VSUM (OP_(A)AAKK) */ + vis_vsmnm(op,opsize,FALSE); + break; + case 004: /* VNRM (OP_(A)AAKK) */ + vis_vsmnm(op,opsize,TRUE); + break; + case 005: /* VDOT (OP_(A)AAKAKK) */ + vis_vdot(op,opsize); + break; + case 006: /* VMAX (OP_(A)AAKK) */ + vis_minmax(op,opsize,TRUE,FALSE); + break; + case 007: /* VMAB (OP_(A)AAKK) */ + vis_minmax(op,opsize,TRUE,TRUE); + break; + case 010: /* VMIN (OP_(A)AAKK) */ + vis_minmax(op,opsize,FALSE,FALSE); + break; + case 011: /* VMIB (OP_(A)AAKK) */ + vis_minmax(op,opsize,FALSE,TRUE); + break; + case 012: /* VMOV (OP_(A)AKAKK) */ + vis_movswp(op,opsize,FALSE); + break; + case 013: /* VSWP (OP_(A)AKAKK) */ + vis_movswp(op,opsize,TRUE); + break; + case 014: /* .ERES (OP_(A)AA) */ + reason = cpu_ema_eres(&rtn,op[2].word,PC,debug); /* handle the ERES instruction */ + PC = rtn; + if (debug) + fprintf (sim_deb, + ">>CPU VIS: return .ERES: AR = %06o, BR = %06o, rtn=%s\n", + AR, BR, PC==op[0].word ? "error" : "good"); + break; + + case 015: /* .ESEG (OP_(A)A) */ + reason = cpu_ema_eseg(&rtn,IR,op[0].word,debug); /* handle the ESEG instruction */ + PC = rtn; + if (debug) + fprintf (sim_deb, + ">>CPU VIS: return .ESEG: AR = %06o , BR = %06o, rtn=%s\n", + AR, BR, rtn==rtn1 ? "error" : "good"); + break; + + case 016: /* .VSET (OP_(A)AAACCC) */ + reason = cpu_ema_vset(&rtn,op,debug); + PC = rtn; + if (debug) + fprintf (sim_deb, ">>CPU VIS: return .VSET: AR = %06o BR = %06o, rtn=%s\n", + AR, BR, + rtn==rtn1 ? "error" : (rtn==(rtn1+1) ? "hard" : "easy") ); + break; + + case 017: /* [test] (OP_N) */ + XR = 3; /* firmware revision */ + SR = 0102077; /* test passed code */ + PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/VIS */ + break; + default: /* others undefined */ + reason = stop_inst; + } + +return reason; +} + + +/* SIGNAL/1000 Instructions + + The SIGNAL/1000 instructions provide fast Fourier transforms and complex + arithmetic. They utilize the F-Series floating-point processor and the + Vector Instruction Set. + + Option implementation by CPU was as follows: + + 2114 2115 2116 2100 1000-M 1000-E 1000-F + ------ ------ ------ ------ ------ ------ ------ + N/A N/A N/A N/A N/A N/A 92835A + + The routines are mapped to instruction codes as follows: + + Instr. 1000-F Description + ------ ------ ---------------------------------------------- + BITRV 105600 Bit reversal + BTRFY 105601 Butterfly algorithm + UNSCR 105602 Unscramble for phasor MPY + PRSCR 105603 Unscramble for phasor MPY + BITR1 105604 Swap two elements in array (alternate format) + BTRF1 105605 Butterfly algorithm (alternate format) + .CADD 105606 Complex number addition + .CSUB 105607 Complex number subtraction + .CMPY 105610 Complex number multiplication + .CDIV 105611 Complex number division + CONJG 105612 Complex conjugate + ..CCM 105613 Complex complement + AIMAG 105614 Return imaginary part + CMPLX 105615 Form complex number + [nop] 105616 [no operation] + [test] 105617 [self test] + + Notes: + + 1. SIGNAL/1000 ROM data are available from Bitsavers. + + Additional references (documents unavailable): + - HP Signal/1000 User Reference and Installation Manual (92835-90002). + - SIGNAL/1000 Microcode Source (92835-18075, revision 2). +*/ + +#define RE(x) (x+0) +#define IM(x) (x+2) + +static const OP_PAT op_signal[16] = { + OP_AAKK, OP_AAFFKK, OP_AAFFKK,OP_AAFFKK, /* BITRV BTRFY UNSCR PRSCR */ + OP_AAAKK, OP_AAAFFKK,OP_AAA, OP_AAA, /* BITR1 BTRF1 .CADD .CSUB */ + OP_AAA, OP_AAA, OP_AAA, OP_A, /* .CMPY .CDIV CONJG ..CCM */ + OP_AA, OP_AAFF, OP_N, OP_N /* AIMAG CMPLX --- [test]*/ + }; + +/* complex addition helper */ +static void sig_caddsub(uint32 addsub,OPS op) +{ +OP a,b,c,d,p1,p2; + +a = ReadOp(RE(op[1].word), fp_f); /* read 1st op */ +b = ReadOp(IM(op[1].word), fp_f); +c = ReadOp(RE(op[2].word), fp_f); /* read 2nd op */ +d = ReadOp(IM(op[2].word), fp_f); +(void)fp_exec(addsub,&p1, a, c); /* add real */ +(void)fp_exec(addsub,&p2, b, d); /* add imag */ +WriteOp(RE(op[0].word), p1, fp_f); /* write result */ +WriteOp(IM(op[0].word), p2, fp_f); /* write result */ +} + +/* butterfly operation helper */ +static void sig_btrfy(uint32 re,uint32 im,OP wr,OP wi,uint32 k, uint32 n2) +{ +/* + * v(k)-------->o-->o----> v(k) + * \ / + * x + * / \ + * v(k+N/2)---->o-->o----> v(k+N/2) + * Wn -1 + * + */ + +OP p1,p2,p3,p4; +OP v1r = ReadOp(re+k, fp_f); /* read v1 */ +OP v1i = ReadOp(im+k, fp_f); +OP v2r = ReadOp(re+k+n2, fp_f); /* read v2 */ +OP v2i = ReadOp(im+k+n2, fp_f); + +/* (p1,p2) := cmul(w,v2) */ +(void)fp_exec(040, &p1, wr, v2r); /* S7,8 p1 := wr*v2r */ +(void)fp_exec(040, ACCUM, wi, v2i); /* ACCUM := wi*v2i */ +(void)fp_exec(024, &p1, p1, NOP); /* S7,S8 p1 := wr*v2r-wi*v2i ==real(w*v2) */ +(void)fp_exec(040, &p2, wi, v2r); /* S9,10 p2 := wi*v2r */ +(void)fp_exec(040, ACCUM, wr, v2i); /* ACCUM := wr*v2i */ +(void)fp_exec(004, &p2, p2, NOP); /* S9,10 p2 := wi*v2r+wr*v2i ==imag(w*v2) */ +/* v2 := v1 - (p1,p2) */ +(void)fp_exec(020, &p3, v1r, p1); /* v2r := v1r-real(w*v2) */ +(void)fp_exec(020, &p4, v1i, p2); /* v2i := v1i-imag(w*v2) */ +WriteOp(re+k+n2, p3, fp_f); /* write v2r */ +WriteOp(im+k+n2, p4, fp_f); /* write v2i */ +/* v1 := v1 + (p1,p2) */ +(void)fp_exec(0, &p3, v1r, p1); /* v1r := v1r+real(w*v2) */ +(void)fp_exec(0, &p4, v1i, p2); /* v1i := v1i+imag(w*v2) */ +WriteOp(re+k, p3, fp_f); /* write v1r */ +WriteOp(im+k, p4, fp_f); /* write v1i */ +O = 0; +} + +/* helper for bit reversal + * idx is 0-based already */ +static void sig_bitrev(uint32 re,uint32 im, uint32 idx, uint32 log2n, int sz) +{ +uint32 i, org=idx, rev = 0; +OP v1r,v1i,v2r,v2i; + +for (i=0; i>= 1; +} + +if (rev < idx) return; /* avoid swapping same pair twice in loop */ + +idx *= sz; /* adjust for element size */ +rev *= sz; /* (REAL*4 vs COMPLEX*8) */ + +v1r = ReadOp(re+idx, fp_f); /* read 1st element */ +v1i = ReadOp(im+idx, fp_f); +v2r = ReadOp(re+rev, fp_f); /* read 2nd element */ +v2i = ReadOp(im+rev, fp_f); +WriteOp(re+idx, v2r, fp_f); /* swap elements */ +WriteOp(im+idx, v2i, fp_f); +WriteOp(re+rev, v1r, fp_f); +WriteOp(im+rev, v1i, fp_f); +} + +/* helper for PRSCR/UNSCR */ +static OP sig_scadd(uint32 oper,t_bool addh, OP a, OP b) +{ +OP r; +static const OP plus_half = { { 0040000, 0000000 } }; /* DEC +0.5 */ + +(void)fp_exec(oper,&r,a,b); /* calculate r := a +/- b */ +if (addh) (void)fp_exec(044,&r,plus_half,NOP); /* if addh set, multiply by 0.5 */ +return r; +} + +/* complex multiply helper */ +static void sig_cmul(OP *r, OP *i, OP a, OP b, OP c, OP d) +{ +OP p; +(void)fp_exec(040, &p , a, c); /* p := ac */ +(void)fp_exec(040, ACCUM, b, d); /* ACCUM := bd */ +(void)fp_exec(024, r, p , NOP); /* real := ac-bd */ +(void)fp_exec(040, &p, a, d); /* p := ad */ +(void)fp_exec(040, ACCUM, b, c); /* ACCUM := bc */ +(void)fp_exec(004, i, p, NOP); /* imag := ad+bc */ +} + +t_stat cpu_signal (uint32 IR, uint32 intrq) +{ +t_stat reason = SCPE_OK; +OPS op; +OP a,b,c,d,p1,p2,p3,p4,m1,m2,wr,wi; +uint32 entry, v, idx1, idx2; +int32 exc, exd; + +t_bool debug = DEBUG_PRI (cpu_dev, DEB_SIG); +if ((cpu_unit.flags & UNIT_SIGNAL) == 0) /* SIGNAL option installed? */ + return stop_inst; + +entry = IR & 017; /* mask to entry point */ + +if (op_signal[entry] != OP_N) + if (reason = cpu_ops (op_signal[entry], op, intrq)) /* get instruction operands */ + return reason; + +if (debug) { /* debugging? */ + fprintf (sim_deb, ">>CPU SIG: IR = %06o (", IR); /* print preamble and IR */ + fprint_sym (sim_deb, err_PC, (t_value *) &IR, /* print instruction mnemonic */ + NULL, SWMASK('M')); + fprintf (sim_deb, "), P = %06o", err_PC); /* print location */ + fprint_ops (op_signal[entry], op); /* print operands */ + fputc ('\n', sim_deb); /* terminate line */ + } + +switch (entry) { /* decode IR<3:0> */ + case 000: /* BITRV (OP_AAKK) */ + /* BITRV + * bit reversal for FFT + * JSB BITRV + * DEF ret(,I) return address + * DEF vect,I base address of array + * DEF idx,I index bitmap to be reversed (one-based) + * DEF nbits,I number of bits of index + * + * Given a complex*8 vector of nbits (power of 2), this calculates: + * swap( vect[idx], vect[rev(idx)]) where rev(i) is the bitreversed value of i */ + sig_bitrev(op[1].word, op[1].word+2, op[2].word-1, op[3].word, 4); + PC = op[0].word & VAMASK; + break; + + case 001: /* BTRFY (OP_AAFFKK) */ + /* BTRFY - butterfly operation + * JSB BTRFY + * DEF ret(,I) return address + * DEF vect(,I) complex*8 vector + * DEF wr,I real part of W + * DEF wi,I imag part of W + * DEF node,I index of 1st op (1 based) + * DEF lmax,I offset to 2nd op (0 based) */ + sig_btrfy(op[1].word, op[1].word+2, + op[2], op[3], + 2*(op[4].word-1), 2*op[5].word); + PC = op[0].word & VAMASK; + break; + + case 002: /* UNSCR (OP_AAFFKK) */ + /* UNSCR unscramble for phasor MPY + * JSB UNSCR + * DEF ret(,I) + * DEF vector,I + * DEF WR + * DEF WI + * DEF idx1,I + * DEF idx2,I */ + v = op[1].word; + idx1 = 2 * (op[4].word - 1); + idx2 = 2 * (op[5].word - 1); + wr = op[2]; /* read WR */ + wi = op[3]; /* read WI */ + p1 = ReadOp(RE(v + idx1), fp_f); /* S1 VR[idx1] */ + p2 = ReadOp(RE(v + idx2), fp_f); /* S2 VR[idx2] */ + p3 = ReadOp(IM(v + idx1), fp_f); /* S9 VI[idx1] */ + p4 = ReadOp(IM(v + idx2), fp_f); /* S10 VI[idx2] */ + c = sig_scadd(000, TRUE, p3, p4); /* S5,6 0.5*(p3+p4) */ + d = sig_scadd(020, TRUE, p2, p1); /* S7,8 0.5*(p2-p1) */ + sig_cmul(&m1, &m2, wr, wi, c, d); /* (WR,WI) * (c,d) */ + c = sig_scadd(000, TRUE, p1, p2); /* 0.5*(p1+p2) */ + d = sig_scadd(020, TRUE, p3, p4); /* 0.5*(p3-p4) */ + (void)fp_exec(000, &p1, c, m1); /* VR[idx1] := 0.5*(p1+p2) + real(W*(c,d)) */ + WriteOp(RE(v + idx1), p1, fp_f); + (void)fp_exec(000, &p2, d, m2); /* VI[idx1] := 0.5*(p3-p4) + imag(W*(c,d)) */ + WriteOp(IM(v + idx1), p2, fp_f); + (void)fp_exec(020, &p1, c, m1); /* VR[idx2] := 0.5*(p1+p2) - imag(W*(c,d)) */ + WriteOp(RE(v + idx2), p1, fp_f); + (void)fp_exec(020, &p2, d, m2); /* VI[idx2] := 0.5*(p3-p4) - imag(W*(c,d)) */ + WriteOp(IM(v + idx2), p2, fp_f); + PC = op[0].word & VAMASK; + break; + + case 003: /* PRSCR (OP_AAFFKK) */ + /* PRSCR unscramble for phasor MPY + * JSB PRSCR + * DEF ret(,I) + * DEF vector,I + * DEF WR + * DEF WI + * DEF idx1,I + * DEF idx2,I */ + v = op[1].word; + idx1 = 2 * (op[4].word - 1); + idx2 = 2 * (op[5].word - 1); + wr = op[2]; /* read WR */ + wi = op[3]; /* read WI */ + p1 = ReadOp(RE(v + idx1), fp_f); /* VR[idx1] */ + p2 = ReadOp(RE(v + idx2), fp_f); /* VR[idx2] */ + p3 = ReadOp(IM(v + idx1), fp_f); /* VI[idx1] */ + p4 = ReadOp(IM(v + idx2), fp_f); /* VI[idx2] */ + c = sig_scadd(020, FALSE, p1, p2); /* p1-p2 */ + d = sig_scadd(000, FALSE, p3, p4); /* p3+p4 */ + sig_cmul(&m1,&m2, wr, wi, c, d); /* (WR,WI) * (c,d) */ + c = sig_scadd(000, FALSE, p1, p2); /* p1+p2 */ + d = sig_scadd(020, FALSE, p3,p4); /* p3-p4 */ + (void)fp_exec(020, &p1, c, m2); /* VR[idx1] := (p1-p2) - imag(W*(c,d)) */ + WriteOp(RE(v + idx1), p1, fp_f); + (void)fp_exec(000, &p2, d, m1); /* VI[idx1] := (p3-p4) + real(W*(c,d)) */ + WriteOp(IM(v + idx1), p2, fp_f); + (void)fp_exec(000, &p1, c, m2); /* VR[idx2] := (p1+p2) + imag(W*(c,d)) */ + WriteOp(RE(v + idx2), p1, fp_f); + (void)fp_exec(020, &p2, m1, d); /* VI[idx2] := imag(W*(c,d)) - (p3-p4) */ + WriteOp(IM(v + idx2), p2, fp_f); + PC = op[0].word & VAMASK; + break; + + case 004: /* BITR1 (OP_AAAKK) */ + /* BITR1 + * bit reversal for FFT, alternative version + * JSB BITR1 + * DEF ret(,I) return address if already swapped + * DEF revect,I base address of real vect + * DEF imvect,I base address of imag vect + * DEF idx,I index bitmap to be reversed (one-based) + * DEF nbits,I number of bits of index + * + * Given a complex*8 vector of nbits (power of 2), this calculates: + * swap( vect[idx], vect[rev(idx)]) where rev(i) is the bitreversed value of i + * + * difference to BITRV is that BITRV uses complex*8, and BITR1 uses separate real*4 + * vectors for Real and Imag parts */ + sig_bitrev(op[1].word, op[2].word, op[3].word-1, op[4].word, 2); + PC = op[0].word & VAMASK; + break; + + + case 005: /* BTRF1 (OP_AAAFFKK) */ + /* BTRF1 - butterfly operation with real*4 vectors + * JSB BTRF1 + * DEF ret(,I) return address + * DEF rvect,I real part of vector + * DEF ivect,I imag part of vector + * DEF wr,I real part of W + * DEF wi,I imag part of W + * DEF node,I index (1 based) + * DEF lmax,I index (0 based) */ + sig_btrfy(op[1].word, op[2].word, + op[3], op[4], + op[5].word-1, op[6].word); + PC = op[0].word & VAMASK; + break; + + case 006: /* .CADD (OP_AAA) */ + /* .CADD Complex addition + * JSB .CADD + * DEF result,I + * DEF oprd1,I + * DEF oprd2,I + * complex addition is: (a+bi) + (c+di) => (a+c) + (b+d)i */ + sig_caddsub(000,op); + break; + + case 007: /* .CSUB (OP_AAA) */ + /* .CSUB Complex subtraction + * JSB .CSUB + * DEF result,I + * DEF oprd1,I + * DEF oprd2,I + * complex subtraction is: (a+bi) - (c+di) => (a - c) + (b - d)i */ + sig_caddsub(020,op); + break; + + case 010: /* .CMUL (OP_AAA) */ + /* .CMPY Complex multiplication + * call: + * JSB .CMPY + * DEF result,I + * DEF oprd1,I + * DEF oprd2,I + * complex multiply is: (a+bi)*(c+di) => (ac-bd) + (ad+bc)i */ + a = ReadOp(RE(op[1].word), fp_f); /* read 1st op */ + b = ReadOp(IM(op[1].word), fp_f); + c = ReadOp(RE(op[2].word), fp_f); /* read 2nd op */ + d = ReadOp(IM(op[2].word), fp_f); + sig_cmul(&p1, &p2, a, b, c, d); + WriteOp(RE(op[0].word), p1, fp_f); /* write real result */ + WriteOp(IM(op[0].word), p2, fp_f); /* write imag result */ + break; + + case 011: /* .CDIV (OP_AAA) */ + /* .CDIV Complex division + * call: + * JSB .CDIV + * DEF result,I + * DEF oprd1,I + * DEF oprd2,I + * complex division is: (a+bi)/(c+di) => ((ac+bd) + (bc-ad)i)/(c^2+d^2) */ + a = ReadOp(RE(op[1].word), fp_f); /* read 1st op */ + b = ReadOp(IM(op[1].word), fp_f); + c = ReadOp(RE(op[2].word), fp_f); /* read 2nd op */ + d = ReadOp(IM(op[2].word), fp_f); + (void)fp_unpack (NULL, &exc, c, fp_f); /* get exponents */ + (void)fp_unpack (NULL, &exd, d, fp_f); + if (exc < exd) { /* ensure c/d < 1 */ + p1 = a; a = c; c = p1; /* swap dividend and divisor */ + p1 = b; b = d; d = p1; + } + (void)fp_exec(060, &p1, d, c); /* p1,accu := d/c */ + (void)fp_exec(044, ACCUM, d, NOP); /* ACCUM := dd/c */ + (void)fp_exec(004, &p2, c, NOP); /* p2 := c + dd/c */ + (void)fp_exec(040, ACCUM, b, p1); /* ACCUM := bd/c */ + (void)fp_exec(004, ACCUM, a, NOP); /* ACCUM := a + bd/c */ + (void)fp_exec(070, &p3, NOP, p2); /* p3 := (a+bd/c)/(c+dd/c) == (ac+bd)/(cc+dd) */ + WriteOp(RE(op[0].word), p3, fp_f); /* Write real result */ + (void)fp_exec(040, ACCUM, a, p1); /* ACCUM := ad/c */ + (void)fp_exec(030, ACCUM, NOP, b); /* ACCUM := ad/c - b */ + if (exd < exc) { /* was not swapped? */ + (void)fp_exec(024, ACCUM, zero, NOP); /* ACCUM := -ACCUM */ + } + (void)fp_exec(070, &p3, NOP, p2); /* p3 := (b-ad/c)/(c+dd/c) == (bc-ad)/cc+dd) */ + WriteOp(IM(op[0].word), p3, fp_f); /* Write imag result */ + break; + + case 012: /* CONJG (OP_AAA) */ + /* CONJG build A-Bi from A+Bi + * call: + * JSB CONJG + * DEF RTN + * DEF res,I result + * DEF arg,I input argument */ + a = ReadOp(RE(op[2].word), fp_f); /* read real */ + b = ReadOp(IM(op[2].word), fp_f); /* read imag */ + (void)fp_pcom(&b, fp_f); /* negate imag */ + WriteOp(RE(op[1].word), a, fp_f); /* write real */ + WriteOp(IM(op[1].word), b, fp_f); /* write imag */ + break; + + case 013: /* ..CCM (OP_A) */ + /* ..CCM complement complex + * call + * JSB ..CCM + * DEF arg + * build (-RE,-IM) + */ + v = op[0].word; + a = ReadOp(RE(v), fp_f); /* read real */ + b = ReadOp(IM(v), fp_f); /* read imag */ + (void)fp_pcom(&a, fp_f); /* negate real */ + (void)fp_pcom(&b, fp_f); /* negate imag */ + WriteOp(RE(v), a, fp_f); /* write real */ + WriteOp(IM(v), b, fp_f); /* write imag */ + break; + + case 014: /* AIMAG (OP_AA) */ + /* AIMAG return the imaginary part in AB + * JSB AIMAG + * DEF *+2 + * DEF cplx(,I) + * returns: AB imaginary part of complex number */ + a = ReadOp(IM(op[1].word), fp_f); /* read imag */ + AR = a.fpk[0]; /* move MSB to A */ + BR = a.fpk[1]; /* move LSB to B */ + break; + + case 015: /* CMPLX (OP_AFF) */ + /* CMPLX form a complex number + * JSB CMPLX + * DEF *+4 + * DEF result,I complex number + * DEF repart,I real value + * DEF impart,I imaginary value */ + WriteOp(RE(op[1].word), op[2], fp_f); /* write real part */ + WriteOp(IM(op[1].word), op[3], fp_f); /* write imag part */ + break; + + case 017: /* [slftst] (OP_N) */ + XR = 2; /* firmware revision */ + SR = 0102077; /* test passed code */ + PC = (PC + 1) & VAMASK; /* P+2 return for firmware w/SIGNAL1000 */ + break; + + case 016: /* invalid */ + default: /* others undefined */ + reason = stop_inst; + } + +return reason; +} + +#endif /* end of int64 support */ diff --git a/HP2100/hp2100_defs.h b/HP2100/hp2100_defs.h new file mode 100644 index 0000000..f5b935f --- /dev/null +++ b/HP2100/hp2100_defs.h @@ -0,0 +1,343 @@ +/* hp2100_defs.h: HP 2100 simulator definitions + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 24-Apr-08 JDB Added I_MRG_I, I_JSB, I_JSB_I, and I_JMP instruction masks + 14-Apr-08 JDB Changed TMR_MUX to TMR_POLL for idle support + Added POLLMODE, sync_poll() declaration + Added I_MRG, I_ISZ, I_IOG, I_STF, and I_SFS instruction masks + 07-Dec-07 JDB Added BACI device + 10-Nov-07 JDB Added 16/32-bit unsigned-to-signed conversions + 11-Jan-07 JDB Added 12578A DMA byte packing to DMA structure + 28-Dec-06 JDB Added CRS backplane signal as I/O pseudo-opcode + Added DMASK32 32-bit mask value + 21-Dec-06 JDB Changed MEM_ADDR_OK for 21xx loader support + 12-Sep-06 JDB Define NOTE_IOG to recalc interrupts after instr exec + Rename STOP_INDINT to NOTE_INDINT (not a stop condition) + 30-Dec-04 JDB Added IBL_DS_HEAD head number mask + 19-Nov-04 JDB Added STOP_OFFLINE, STOP_PWROFF stop codes + 25-Apr-04 RMS Added additional IBL definitions + Added DMA EDT I/O pseudo-opcode + 25-Apr-03 RMS Revised for extended file support + 24-Oct-02 RMS Added indirect address interrupt + 08-Feb-02 RMS Added DMS definitions + 01-Feb-02 RMS Added terminal multiplexor support + 16-Jan-02 RMS Added additional device support + 30-Nov-01 RMS Added extended SET/SHOW support + 15-Oct-00 RMS Added dynamic device numbers + 14-Apr-99 RMS Changed t_addr to unsigned + + The author gratefully acknowledges the help of Jeff Moffat in answering + questions about the HP2100; and of Dave Bryan in adding features and + correcting errors throughout the simulator. +*/ +#ifndef _HP2100_DEFS_H_ +#define _HP2100_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop and notification codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_IODV 2 /* must be 2 */ +#define STOP_HALT 3 /* HALT */ +#define STOP_IBKPT 4 /* breakpoint */ +#define STOP_IND 5 /* indirect loop */ +#define NOTE_INDINT 6 /* indirect intr */ +#define STOP_NOCONN 7 /* no connection */ +#define STOP_OFFLINE 8 /* device offline */ +#define STOP_PWROFF 9 /* device powered off */ +#define NOTE_IOG 10 /* I/O instr executed */ + +#define ABORT_PRO 1 /* protection abort */ + +/* Memory */ + +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < fwanxm) +#define VA_N_SIZE 15 /* virtual addr size */ +#define VASIZE (1 << VA_N_SIZE) +#define VAMASK 077777 /* virt addr mask */ +#define PA_N_SIZE 20 /* phys addr size */ +#define PASIZE (1 << PA_N_SIZE) +#define PAMASK (PASIZE - 1) /* phys addr mask */ + +/* Architectural constants */ + +#define SIGN32 020000000000 /* 32b sign */ +#define DMASK32 037777777777 /* 32b data mask/maximum value */ +#define DMAX32 017777777777 /* 32b maximum signed value */ +#define SIGN 0100000 /* 16b sign */ +#define DMASK 0177777 /* 16b data mask/maximum value */ +#define DMAX 0077777 /* 16b maximum signed value */ +#define DMASK8 0377 /* 8b data mask/maximum value */ +#define AR ABREG[0] /* A = reg 0 */ +#define BR ABREG[1] /* B = reg 1 */ + +/* Portable conversions (sign-extension, unsigned-to-signed) */ + +#define SEXT(x) ((int32) (((x) & SIGN)? ((x) | ~DMASK): ((x) & DMASK))) + +#define INT16(u) ((u) > DMAX ? (-(int16) (DMASK - (u)) - 1) : (int16) (u)) +#define INT32(u) ((u) > DMAX32 ? (-(int32) (DMASK32 - (u)) - 1) : (int32) (u)) + +/* Memory reference instructions */ + +#define I_IA 0100000 /* indirect address */ +#define I_AB 0004000 /* A/B select */ +#define I_CP 0002000 /* current page */ +#define I_DISP 0001777 /* page displacement */ +#define I_PAGENO 0076000 /* page number */ + +/* Other instructions */ + +#define I_NMRMASK 0172000 /* non-mrf opcode */ +#define I_SRG 0000000 /* shift */ +#define I_ASKP 0002000 /* alter/skip */ +#define I_EXTD 0100000 /* extend */ +#define I_IO 0102000 /* I/O */ +#define I_CTL 0004000 /* CTL on/off */ +#define I_HC 0001000 /* hold/clear */ +#define I_DEVMASK 0000077 /* device mask */ +#define I_GETIOOP(x) (((x) >> 6) & 07) /* I/O sub op */ + +/* Instruction masks */ + +#define I_MRG 0074000 /* MRG instructions */ +#define I_MRG_I (I_MRG | I_IA) /* MRG indirect instruction group */ +#define I_JSB 0014000 /* JSB instruction */ +#define I_JSB_I (I_JSB | I_IA) /* JSB,I instruction */ +#define I_JMP 0024000 /* JMP instruction */ +#define I_ISZ 0034000 /* ISZ instruction */ + +#define I_IOG 0107700 /* I/O group instruction */ +#define I_SFS 0102300 /* SFS instruction */ +#define I_STF 0102100 /* STF instruction */ + +/* DMA channels */ + +#define DMA_OE 020000000000 /* byte packing odd/even flag */ +#define DMA1_STC 0100000 /* DMA - issue STC */ +#define DMA1_PB 0040000 /* DMA - pack bytes */ +#define DMA1_CLC 0020000 /* DMA - issue CLC */ +#define DMA2_OI 0100000 /* DMA - output/input */ + +struct DMA { /* DMA channel */ + uint32 cw1; /* device select */ + uint32 cw2; /* direction, address */ + uint32 cw3; /* word count */ + uint32 latency; /* 1st cycle delay */ + uint32 packer; /* byte-packer holding reg */ + }; + +/* Memory management */ + +#define VA_N_OFF 10 /* offset width */ +#define VA_M_OFF ((1 << VA_N_OFF) - 1) /* offset mask */ +#define VA_GETOFF(x) ((x) & VA_M_OFF) +#define VA_N_PAG (VA_N_SIZE - VA_N_OFF) /* page width */ +#define VA_V_PAG (VA_N_OFF) +#define VA_M_PAG ((1 << VA_N_PAG) - 1) +#define VA_GETPAG(x) (((x) >> VA_V_PAG) & VA_M_PAG) + +/* Maps */ + +#define MAP_NUM 4 /* num maps */ +#define MAP_LNT (1 << VA_N_PAG) /* map length */ +#define MAP_MASK ((MAP_NUM * MAP_LNT) - 1) +#define SMAP 0 /* system map */ +#define UMAP (SMAP + MAP_LNT) /* user map */ +#define PAMAP (UMAP + MAP_LNT) /* port A map */ +#define PBMAP (PAMAP + MAP_LNT) /* port B map */ + +/* DMS map entries */ + +#define MAP_V_RPR 15 /* read prot */ +#define MAP_V_WPR 14 /* write prot */ +#define RD (1 << MAP_V_RPR) +#define WR (1 << MAP_V_WPR) +#define MAP_MBZ 0036000 /* must be zero */ +#define MAP_N_PAG (PA_N_SIZE - VA_N_OFF) /* page width */ +#define MAP_V_PAG (VA_N_OFF) +#define MAP_M_PAG ((1 << MAP_N_PAG) - 1) +#define MAP_GETPAG(x) (((x) & MAP_M_PAG) << MAP_V_PAG) + +/* Map status register */ + +#define MST_ENBI 0100000 /* mem enb @ int */ +#define MST_UMPI 0040000 /* usr map @ int */ +#define MST_ENB 0020000 /* mem enb */ +#define MST_UMP 0010000 /* usr map */ +#define MST_PRO 0004000 /* protection */ +#define MST_FLT 0002000 /* fence comp */ +#define MST_FENCE 0001777 /* base page fence */ + +/* Map violation register */ + +#define MVI_V_RPR 15 /* must be same as */ +#define MVI_V_WPR 14 /* MAP_V_xPR */ +#define MVI_RPR (1 << MVI_V_RPR) /* rd viol */ +#define MVI_WPR (1 << MVI_V_WPR) /* wr viol */ +#define MVI_BPG 0020000 /* base page viol */ +#define MVI_PRV 0010000 /* priv viol */ +#define MVI_MEB 0000200 /* me bus enb @ viol */ +#define MVI_MEM 0000100 /* mem enb @ viol */ +#define MVI_UMP 0000040 /* usr map @ viol */ +#define MVI_PAG 0000037 /* pag sel */ + +/* Timers */ + +#define TMR_CLK 0 /* clock */ +#define TMR_POLL 1 /* input polling */ + +#define POLL_RATE 100 /* poll 100 times per second */ +#define POLL_WAIT 15800 /* initial poll ~ 10 msec. */ + +typedef enum { INITIAL, SERVICE } POLLMODE; /* poll synchronization modes */ + +/* I/O sub-opcodes */ + +#define ioHLT 0 /* halt */ +#define ioFLG 1 /* set/clear flag */ +#define ioSFC 2 /* skip on flag clear */ +#define ioSFS 3 /* skip on flag set */ +#define ioMIX 4 /* merge into A/B */ +#define ioLIX 5 /* load into A/B */ +#define ioOTX 6 /* output from A/B */ +#define ioCTL 7 /* set/clear control */ +#define ioEDT 8 /* DMA: end data transfer */ +#define ioCRS 9 /* control reset ("CLC 0") */ + +/* I/O devices - fixed assignments */ + +#define CPU 000 /* interrupt control */ +#define OVF 001 /* overflow */ +#define DMALT0 002 /* DMA 0 alternate */ +#define DMALT1 003 /* DMA 1 alternate */ +#define PWR 004 /* power fail */ +#define PRO 005 /* parity/mem protect */ +#define DMA0 006 /* DMA channel 0 */ +#define DMA1 007 /* DMA channel 1 */ +#define VARDEV (DMA1 + 1) /* start of var assign */ +#define M_NXDEV (INT_M (CPU) | INT_M (OVF) | \ + INT_M (DMALT0) | INT_M (DMALT1)) +#define M_FXDEV (M_NXDEV | INT_M (PWR) | INT_M (PRO) | \ + INT_M (DMA0) | INT_M (DMA1)) + +/* I/O devices - variable assignment defaults */ + +#define PTR 010 /* 12597A-002 paper tape reader */ +#define TTY 011 /* 12531C teleprinter */ +#define PTP 012 /* 12597A-005 paper tape punch */ +#define CLK 013 /* 12539C time-base generator */ +#define LPS 014 /* 12653A line printer */ +#define LPT 015 /* 12845A line printer */ +#define MTD 020 /* 12559A data */ +#define MTC 021 /* 12559A control */ +#define DPD 022 /* 12557A data */ +#define DPC 023 /* 12557A control */ +#define DQD 024 /* 12565A data */ +#define DQC 025 /* 12565A control */ +#define DRD 026 /* 12610A data */ +#define DRC 027 /* 12610A control */ +#define MSD 030 /* 13181A data */ +#define MSC 031 /* 13181A control */ +#define IPLI 032 /* 12566B link in */ +#define IPLO 033 /* 12566B link out */ +#define DS 034 /* 13037A control */ +#define BACI 035 /* 12966A Buffered Async Comm Interface */ +#define MUXL 040 /* 12920A lower data */ +#define MUXU 041 /* 12920A upper data */ +#define MUXC 042 /* 12920A control */ + +/* IBL assignments */ + +#define IBL_V_SEL 14 /* ROM select */ +#define IBL_M_SEL 03 +#define IBL_PTR 0000000 /* PTR */ +#define IBL_DP 0040000 /* disk: DP */ +#define IBL_DQ 0060000 /* disk: DQ */ +#define IBL_MS 0100000 /* option 0: MS */ +#define IBL_DS 0140000 /* option 1: DS */ +#define IBL_MAN 0010000 /* RPL/man boot */ +#define IBL_V_DEV 6 /* dev in <11:6> */ +#define IBL_OPT 0000070 /* options in <5:3> */ +#define IBL_DP_REM 0000001 /* DP removable */ +#define IBL_DS_HEAD 0000003 /* DS head number */ +#define IBL_LNT 64 /* boot length */ +#define IBL_MASK (IBL_LNT - 1) /* boot length mask */ +#define IBL_DPC (IBL_LNT - 2) /* DMA ctrl word */ +#define IBL_END (IBL_LNT - 1) /* last location */ + +/* Dynamic device information table */ + +typedef struct { + uint32 devno; /* device number */ + uint32 cmd; /* saved command */ + uint32 ctl; /* saved control */ + uint32 flg; /* saved flag */ + uint32 fbf; /* saved flag buf */ + uint32 srq; /* saved svc req */ + int32 (*iot)(int32 op, int32 ir, int32 dat); /* I/O routine */ + } DIB; + +/* I/O macros */ + +#define INT_V(x) ((x) & 037) /* device bit pos */ +#define INT_M(x) (1u << INT_V (x)) /* device bit mask */ +#define setCMD(D) dev_cmd[(D)/32] = dev_cmd[(D)/32] | INT_M ((D)) +#define clrCMD(D) dev_cmd[(D)/32] = dev_cmd[(D)/32] & ~INT_M (D) +#define setCTL(D) dev_ctl[(D)/32] = dev_ctl[(D)/32] | INT_M ((D)) +#define clrCTL(D) dev_ctl[(D)/32] = dev_ctl[(D)/32] & ~INT_M (D) +#define setFBF(D) dev_fbf[(D)/32] = dev_fbf[(D)/32] | INT_M (D) +#define clrFBF(D) dev_fbf[(D)/32] = dev_fbf[(D)/32] & ~INT_M (D) +#define setFLG(D) dev_flg[(D)/32] = dev_flg[(D)/32] | INT_M (D); \ + setFBF(D) +#define clrFLG(D) dev_flg[(D)/32] = dev_flg[(D)/32] & ~INT_M (D); \ + clrFBF(D) +#define setFSR(D) dev_flg[(D)/32] = dev_flg[(D)/32] | INT_M (D); \ + setFBF(D); setSRQ(D) +#define clrFSR(D) dev_flg[(D)/32] = dev_flg[(D)/32] & ~INT_M (D); \ + clrFBF(D); clrSRQ(D) +#define setSRQ(D) dev_srq[(D)/32] = dev_srq[(D)/32] | INT_M ((D)) +#define clrSRQ(D) dev_srq[(D)/32] = dev_srq[(D)/32] & ~INT_M (D) +#define CMD(D) ((dev_cmd[(D)/32] >> INT_V (D)) & 1) +#define CTL(D) ((dev_ctl[(D)/32] >> INT_V (D)) & 1) +#define FLG(D) ((dev_flg[(D)/32] >> INT_V (D)) & 1) +#define FBF(D) ((dev_fbf[(D)/32] >> INT_V (D)) & 1) +#define SRQ(D) ((dev_srq[(D)/32] >> INT_V (D)) & 1) + +#define IOT_V_REASON 16 +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Function prototypes */ + +int32 sync_poll (POLLMODE poll_mode); +t_stat ibl_copy (const uint16 pboot[IBL_LNT], int32 dev); +t_stat hp_setdev (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat hp_showdev (FILE *st, UNIT *uptr, int32 val, void *desc); +void hp_enbdis_pair (DEVICE *ccp, DEVICE *dcp); +t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); + +#endif diff --git a/HP2100/hp2100_diag.txt b/HP2100/hp2100_diag.txt new file mode 100644 index 0000000..27db354 --- /dev/null +++ b/HP2100/hp2100_diag.txt @@ -0,0 +1,3371 @@ + SIMH/HP 21XX DIAGNOSTICS PERFORMANCE + ==================================== + Last update: 2008-05-10 + + +The HP 24396 diagnostic suite has been run against the SIMH HP 21xx simulation. +Diagnostic programs were obtained from two magnetic tapes, HP 24396-13601 Rev. +1713 and Rev. 2326, plus a few standalone paper tapes. For each diagnostic, the +recommended standard tests were selected, plus any available optional tests that +broadened the test coverage. + +Except where noted in the individual diagnostic reports, the test system +configuration is the default SIMH configuration with these alterations: + + * All I/O devices are enabled. + * The CPU is configured as a 1000-E with 128KW of memory. + +Detailed diagnostic configuration, operation, and results are given after the +summary table. These may be used to duplicate the diagnostic results. + + +The results of the diagnostic runs are summarized below: + + Date SIMH + DSN Diagnostic Name Code Vers. Result +------ --------------------------------------- ---- ----- ------------- +000200 Diagnostic Configurator Pretest 1627 3.2-3 Passed + +101100 Memory Reference Instruction Group 1624 3.2-3 Passed +101001 Alter-Skip Instruction Group 1431 3.2-3 Passed +101002 Shift-Rotate Instruction Group 1431 3.2-3 Passed +102200 Core Memory (2100/16/15/14) 1624 3.3-0 Passed +102104 Semiconductor Memory (21MX) 1644 3.2-3 Passed + +101004 EAU Instruction Group 1431 3.2-3 Passed +101207 Floating Point Instruction Group 1551 3.2-3 Passed +102001 Memory Protect 1431 3.7-0 Passed +102002 Memory Parity Check 1431 - No simulation +102305 Memory Protect/Parity Error 1705 3.3-0 Partial + +101206 Power Fail/Auto Restart 1635 - No simulation +141203 I/O Instruction Group - I/O Extender 2326 3.2-3 Passed +143300 General Purpose Register 1813 3.2-3 Passed +101105 Direct Memory Access (2114/15/16) 1502 3.7-0 Passed +101220 Direct Memory Access (2100/21MX) 1705 3.2-3 Passed + +101011 Extended Instruction Group (Index) 1432 3.2-3 Passed +101112 Extended Instruction Group (Word, Byte) 1728 3.2-3 Passed +101110 2100 Fast FORTRAN Package 1632 3.4-0 Partial +101213 M/E-Series Fast FORTRAN Package 1 1822 3.4-0 Passed +101114 M/E-Series Fast FORTRAN Package 2 1632 3.4-0 Passed +101121 F-Series FPP/SIS/FFP 1926 3.7-0 Passed +101016 2000/Access Comm Processor for 2100 1526 3.2-3 Partial + +102103 Memory Expansion Unit 1830 3.2-3 Passed +102103 Semiconductor Memory Microcoded 21MX 1644 - No simulation +103301 Time Base Generator 1830 3.2-3 Passed +103115 12936 Privileged Interrupt 1643 - No simulation +103105 12908/12978 WCS 256 Word 1502 - No simulation +103023 13197 WCS 1024 Word 1640 - No simulation +103207 12889 Hardwired Serial Interface 1717 - No simulation +103122 59310 Interface Bus Interface 1728 - No simulation + +103003 12587 Asynchronous Data Set Interface 1552 - No simulation +103110 12920 Asynchronous Multiplexer (Data) 1805 3.7-1 Passed +103011 12920 Asynchronous Multiplexer (Cntl) 1444 3.7-1 Passed +103012 12621 Synchronous Data Set (Receive) 1532 - No simulation +103013 12622 Synchronous Data Set (Send) 1532 - No simulation +103116 12967 Synchronous Interface 1438 - No simulation +103017 12966 Asynchronous Data Set 1519 3.8-0 Passed +103121 12968 Asynchronous Comm. Interface 1602 - No simulation +103024 12821 ICD Disc Interface 1928 - No simulation + +104000 2600 Keyboard Display Terminal 1615 - No simulation +104003 Teleprinter 1509 3.2-3 Partial +144105 2762A/B Terminal (Terminet) 1546 - No simulation +104007 2615 Video Terminal 1347 - No simulation +104011 2640 Interactive Terminal 1502 - No simulation +104012 2644 Mini Data Station (non CTU) 1542 - No simulation +104013 2644 Mini Data Station (CTU Only) 1542 - No simulation +104017 92900 Terminal Subsystem (3070, 40280) 1643 - No simulation + +105000 2610/14 Line Printer 1451 - No simulation +105101 2767 Line Printer 1611 3.3-0 Passed +105102 2607 Line Printer 1446 3.3-0 Passed +145103 2613/17/18 Line Printer 1633 - No simulation +105104 9866 Line Printer 1541 - No simulation +105106 2631 Printer 1913 - No simulation +105107 2635 Printing Terminal 1913 - No simulation +105105 2608 Line Printer 2026 - No simulation + +111001 Disc File (2883) 1451 3.3-0 Partial +111104 12732 Flexible Disc Subsystem 1708 - No simulation +151302 7900/01 Cartridge Disc 1805 3.2-3 Partial +151403 7905/06/20/25 Disc 1805 3.3-1 Partial +104117 92900 Terminal Subsystem 1814 - No simulation + +112200 9-Track Magnetic Tape (7970, 13181/3) 2040 3.2-3 Partial +112102 7/9-Track Magnetic Tape (13184 Interf.) 1629 - No simulation +010000 Diagnostic Cross Link 1627 - No simulation +011000 7900/05/20 Disc Initialization 1627 - No simulation +146200 Paper Tape Reader/Punch 1725 3.2-3 Passed +107000 Digital Plotter Interface (CALCOMP) 1540 - No simulation +113100 2892 Card Reader 1537 - No simulation +113001 2894 Card Reader Punch 1728 - No simulation +113003 7261 Card Reader 1546 - No simulation +103006 12909B PROM Writer 1420 - No simulation + + +The following stand-alone diagnostics were run for devices not supported by the +24396 suite: + + Date SIMH +Part Number DSN Diagnostic Name Code Vers. Result +----------- ------ ------------------------------------ ---- ----- ---------- +13207-16001 101217 2000/Access Comm Processor for 21MX 1728 3.2-3 Passed +20433-????? -- HP 3030 Magnetic Tape Subsystem -- - Not tested +22682-16017 177777 HP 2100 Fixed Head Disc/Drum (277x) 1612 3.3-0 Passed +24197-60001 -- 12875 Processor Interconnect Cable B 3.7-1 Passed +24203-60001 -- HP2100A Cartridge Disc Memory (2871) A 3.3-0 Partial + + +The following online diagnostics were run for devices not supported by the +offline diagnostics: + + Date Host Date SIMH +Part Number Diagnostic Name Code Op. Sys. Code Vers. Result +----------- ------------------------------- ---- -------- ---- ----- ---------- +92067-16013 Extended Memory Area Firmware 1805 RTE-IVB 5010 3.8-0 Passed +92084-16423 Virtual Memory Area Firmware 2121 RTE-6/VM 6200 3.8-0 Passed +12824-16002 Vector Instruction Set Firmware 2026 RTE-IVB 5010 3.8-0 Passed +12829-16006 Vector Instruction Set Firmware 2226 RTE-6/VM 6200 3.8-0 Passed +92835-16006 SIGNAL/1000 Firmware Diagnostic 2040 RTE-6/VM 6200 3.8-0 Passed + + +The "SIMH Version" is the version number of the earliest SIMH system that was +tested with the given diagnostic. Earlier versions may or may not work +properly. + +The "Result" column indicates the level of success in passing the given +diagnostic: + + Term Meaning + ------------- --------------------------------------------------------------- + Passed All of the standard tests relevant to the hardware model passed + without error. Optional "utility" tests, where present, were + not run unless they broadened the test coverage. + + Partial One or more of the standard tests relevant to the hardware + model were either excluded or failed as expected, due to known + limitations in the simulation, e.g., the lack of "defective + cylinder" flags in a disc simulation. + + Failed One or more of the standard tests relevant to the hardware + model failed unexpectedly. + + Not tested The diagnostic has not been run with the device simulation. + + No simulation A simulation of the given device does not exist. + +See the "Test Notes" associated with each diagnostic report below for details on +subsets, limitations, or errors encountered. + + + +24396 DIAGNOSTIC SUITE DETAILED EXECUTION AND RESULTS +===================================================== + +Each execution note below presumes that the target diagnostic has been loaded. +For all runs other than the diagnostic configurator pretest, the configurator +was used in automatic mode to load the target diagnostic via its Diagnostic +Serial Number (DSN), as follows: + + sim> attach -r MSC0 24396-13601_Rev-2326.abin.tape + sim> deposit S 000000 + sim> boot MSC0 + + HALT instruction 102077 + + sim> deposit A [DSN] + sim> deposit B 000000 + sim> deposit S 113011 + sim> reset + sim> go 100 + +For the pretest, only the first three commands above were used to load the +diagnostic configurator. + + + +-------------------------------------------- +DSN 000200 - Diagnostic Configurator Pretest +-------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu.c) + +CONFIGURATION: sim> deposit S 000011 + sim> reset + sim> go 2 + +TEST REPORT: HALT instruction 102077 + +TEST RESULT: Passed. + + + +----------------------------------------------- +DSN 101100 - Memory Reference Instruction Group +----------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu.c) + +CONFIGURATION: sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: HALT instruction 102077 + +TEST RESULT: Passed. + + + +------------------------------------ +DSN 101001 - Alter-Skip Instructions +------------------------------------ + +TESTED DEVICE: CPU (hp2100_cpu.c) + +CONFIGURATION: sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: HALT instruction 102077 + +TEST RESULT: Passed. + + + +-------------------------------------- +DSN 101002 - Shift-Rotate Instructions +-------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu.c) + +CONFIGURATION: sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: HALT instruction 102077 + +TEST RESULT: Passed. + + + +---------------------------------------- +DSN 102200 - Core Memory (2100/16/15/14) +---------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu.c) + +CONFIGURATION: sim> set CPU 2100 + sim> set CPU 32K + + sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------- +DSN 102104 - Semiconductor Memory +--------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu.c) + +CONFIGURATION: sim> deposit S 001000 + sim> reset + sim> go 100 + + HALT instruction 102075 + + sim> deposit A 054777 + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: HALT instruction 102077 + +TEST RESULT: Passed. + +TEST NOTES: The standard tests 00-10, plus optional tests 13, 14, and 16 are + executed. + + + +---------------------------------- +DSN 101004 - EAU Instruction Group +---------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu1.c) + +CONFIGURATION: sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: 2100 SERIES EAU DIAGNOSTIC + END OF PASS 1 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------------------- +DSN 101207 - Floating Point Instruction Group +--------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu2.c) + +CONFIGURATION: sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: 2100-21MX FLOATING POINT DIAGNOSTIC + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------- +DSN 102001 - Memory Protect +--------------------------- + +TESTED DEVICE: MP (hp2100_cpu.c) + +CONFIGURATION: sim> set CPU 2100 + sim> set CPU 32K + + sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: HP 2100 SERIES MEMORY PROTECT DIAGNOSTIC + H07. PRESS PRESET (EXT/INT), RUN + + HALT instruction 102007 + + sim> reset + sim> go + + H13. PRESS HALT, PRESET(INT), RUN + IN LESS THAN 15 SEC. + + [CTRL+E] + Simulation stopped + + sim> reset + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +---------------------------------------- +DSN 102305 - Memory Protect/Parity Error +---------------------------------------- + +TESTED DEVICE: MP (hp2100_cpu.c) + +CONFIGURATION: sim> set LPS diag + sim> deposit S 140014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 001000 + sim> reset + sim> go + + MEMORY PROTECT-PARITY ERROR DIAGNOSTIC + + HALT instruction 102075 + + sim> deposit A 035777 + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: H061 POWER DOWN COMPUTER + INSTALL JUMPERS PER TABLE 3-5 IN MOD + POWER UP COMPUTER + + HALT instruction 102061 + + sim> set MP jsbout,intout,sel1in + sim> go + + H314 PRESS HALT,PRESET AND RUN WITHIN 30 SECONDS + + [CTRL+E] + Simulation stopped + + sim> reset + sim> go + + PASS 000001 + + H062 POWER DOWN COMPUTER + SET JUMPERS TO INITIAL SETTINGS + PER TABLE 3-1 IN MOD + POWER UP COMPUTER + + HALT instruction 102062 + + sim> set MP jsbin,intin,sel1out + sim> go + + HALT instruction 102077 + +TEST RESULT: Partially passed. + +TEST NOTES: Test 10 is not executed. This test verifies parity error + detection. This feature is not simulated. + + + +---------------------------------- +DSN 141103 - I/O Instruction Group +---------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu.c) + +CONFIGURATION: sim> set LPS diag + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: I-O INSTRUCTION GROUP & CHANNEL OR + EXTENDER DIAGNOSTIC DSN 141103 + H033 SET S-REG TO 125252, PRESS RUN + + HALT instruction 102033 + + sim> deposit S 125252 + sim> go + + H033 SET S-REG TO 052525, PRESS RUN + + HALT instruction 102033 + + sim> deposit S 052525 + sim> go + + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +------------------------------------- +DSN 143300 - General Purpose Register +------------------------------------- + +TESTED DEVICE: LPS (hp2100_lps.c) + +CONFIGURATION: sim> set LPS diag + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: GENERAL PURPOSE REGISTER DIAGNOSTIC, DSN 143300 + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BASIC I-O COMPLETED + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +---------------------------------------------- +DSN 101105 - Direct Memory Access (2114/15/16) +---------------------------------------------- + +TESTED DEVICE: DMA0/DMA1 (hp2100_cpu.c) + +CONFIGURATION: sim> set CPU 2116 + sim> set CPU 16K + sim> set LPS diag + + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 040000 + sim> reset + sim> go + +TEST REPORT: H0. START DMA DIAGNOSTIC + + HALT instruction 102027 + + sim> reset + sim> go + + H77. END DIAGNOSTIC + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------------------- +DSN 101220 - Direct Memory Access (2100/21MX) +--------------------------------------------- + +TESTED DEVICE: DCPC0/DCPC1 (hp2100_cpu.c) + +CONFIGURATION: sim> set LPS diag + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: DMA-DCPC DIAGNOSTIC + + H324 PRESS PRESET AND RUN + + HALT instruction 107024 + + sim> reset + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +----------------------------------------------- +DSN 101011 - Extended Instruction Group (Index) +----------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu2.c) + +CONFIGURATION: sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: EIG (INDEX) DIAGNOSTIC + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------------------------------- +DSN 101112 - Extended Instruction Group (Word, Byte, Bit) +--------------------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu2.c) + +CONFIGURATION: sim> set LPS diag + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: EIG (WORD,BYTE,BIT) DIAGNOSTIC DSN 101112 + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +-------------------------------------- +DSN 101110 - 2100 Fast FORTRAN Package +-------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu3.c) + +CONFIGURATION: sim> set CPU 2100 + sim> set CPU 32K + sim> set CPU FFP + + sim> deposit S 000013 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: START 2100A-S FFP DIAGNOSTIC + H030 .GOTO TEST + H050 .ENTR TEST + H060 .ENTP TEST + H100 .SETP TEST + H110 ..MAP TEST + H120 SNGL TEST + H130 DBLE TEST + H140 .XADD TEST + + TEST 07 + E142 NOT INTERRUPTIBLE + + HALT instruction 106042 + + sim> go + + H150 .XSUB TEST + H160 .XMPY TEST + + TEST 11 + E162 NOT INTERRUPTIBLE + + HALT instruction 106062 + + sim> go + + H200 .XDIV TEST + H210 .DFER TEST + H220 .XFER TEST + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Partially passed. + +TEST NOTES: Tests 07 and 11 test the interruptibility of the .XADD and .XMPY + instructions. These features are not simulated. + + + +---------------------------------------------- +DSN 101213 - M/E-Series Fast FORTRAN Package 1 +---------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu3.c) + +CONFIGURATION: sim> set CPU FFP + sim> set LPS diag + + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: START 21MX FFP DIAGNOSTIC 1 + H110 ..MAP TEST + H120 SNGL TEST + H130 DBLE TEST + H210 .DFER TEST + H220 .XFER TEST + H230 PWR2 TEST + H240 .PACK TEST + H250 FLUN TEST + H260 .XPAK TEST + H300 .XCOM TEST + H310 ..DCM TEST + H320 DDINT TEST + H330 .CFER TEST + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +---------------------------------------------- +DSN 101114 - M/E-Series Fast FORTRAN Package 2 +---------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu3.c) + +CONFIGURATION: sim> set CPU FFP + sim> set LPS diag + + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: START 21MX FFP DIAGNOSTIC 2 + H030 .GOTO TEST + H050 .ENTR TEST + H060 .ENTP TEST + H100 .SETP TEST + H115 XADD TEST + H125 XSUB TEST + H135 XMPY TEST + H140 .XADD TEST + H150 .XSUB TEST + H160 .XMPY TEST + H200 .XDIV TEST + H215 XDIV TEST + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------- +DSN 101121 - F-Series FPP/SIS/FFP +--------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu3.c) + +CONFIGURATION: sim> set CPU 1000-F + sim> set LPS diag + + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: FPP-SIS-FFP DIAGNOSTIC DSN 101121 + BEGIN BASIC CONTROL TEST + OVFL TEST + CONF TEST + BASE RETN TEST + SIS1 RETN TEST + SIS2 RETN TEST + SIS3 RETN TEST + FPP1 RETN TEST + FFP2 RETN TEST + FFP3 RETN TEST + END BASIC CONTROL TEST + LONG PASSES + FIXS TEST + FIXD TEST + FLTS TEST + FLTD TEST + ADD TEST + SUB TEST + MPY TEST + DIV TEST + ACC TEST + SIS1 TEST + SIS2 TEST + SIS3 TEST + FFP1 TEST + FFP2 TEST + FFP3 TEST + PASS 00001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +------------------------------------------------ +DSN 101016 - 2000/Access Comm Processor for 2100 +------------------------------------------------ + +TESTED DEVICE: CPU (hp2100_cpu2.c) + +CONFIGURATION: sim> set CPU 2100 + sim> set CPU 32K + sim> set CPU IOP + + sim> deposit S 000013 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: 2100 2000-ACCESS COMM. PROC. FIRMWARE DIAGNOSTIC + H030 CRC TEST + H040 ENQ, DEQ AND PENQ TESTS + H060 IAL TEST + H110 READF, SAVE AND RESTR TESTS + H120 LAI AND SAI TESTS + H130 PFREX TEST + H140 PFREI TEST + H150 PFRIO TEST + H160 STORE-LOAD BYTE, TRSLT + AND BYTE MOVE TEST + + TEST 10 + E165 TRSLT NOT INTERRUPTIBLE + + HALT instruction 106065 + + sim> go + + H230 WORD MOVE TEST + + TEST 11 + E234 WORD MOVE NOT INTERRUPTIBLE + + HALT instruction 103034 + + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Partially passed. + +TEST NOTES: Tests 10 and 11 test the interruptibility of the TRSLT and MWORD + instructions. These features are not simulated. + + + +---------------------------------- +DSN 102103 - Memory Expansion Unit +---------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu2.c) + +CONFIGURATION: sim> set LPS diag + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 001000 + sim> reset + sim> go + + MEMORY EXPANSION MODULE DIAGNOSTIC, DSN = 102103 + + HALT instruction 102075 + + sim> deposit A 177777 + sim> deposit B 000037 + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: H115 PRESS HALT-PRESET-RUN IN LESS THAN 10 SECONDS + + [CTRL+E] + Simulation stopped + + sim> reset + sim> go + + H117 PRESET TEST COMPLETE + H327 00128K OF CONTIGUOUS MEMORY INSTALLED + H024 PRESS PRESET, RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + +TEST NOTES: The standard tests 00-22 plus optional tests 23 and 24 are + executed. + + Test 25 (Register Crusher Test) is not executed. This test is + designed specifically for the RAM chips present on the hardware + and isn't relevant to simulation. + + Test 23 cannot be run with more than 256K of memory, or the + diagnostic will be corrupted. There is a fixed-size table in + revision 1830 that overflows if memory size is greater than + 256K. + + + +-------------------------------- +DSN 103301 - Time Base Generator +-------------------------------- + +TESTED DEVICE: CLK (hp2100_stddev.c) + +CONFIGURATION: sim> set CLK diag + sim> deposit S 100013 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: TBG DIAGNOSTIC, DSN = 103301 + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H030 TEST 01 IN PROGRESS + H030 TEST 02 IN PROGRESS + H030 TEST 03 IN PROGRESS + H030 TEST 04 IN PROGRESS + H030 TEST 05 IN PROGRESS + H030 TEST 06 IN PROGRESS + H030 TEST 07 IN PROGRESS + H030 TEST 10 IN PROGRESS + H030 TEST 11 IN PROGRESS + H030 TEST 12 IN PROGRESS + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------------------------- +DSN 103110 - 12920A Asynchronous Multiplexer (Data) +--------------------------------------------------- + +TESTED DEVICE: MUX, MUXL (hp2100_mux.c) + +CONFIGURATION: sim> set MUX DIAG + sim> deposit S 004040 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: ASYNC MULTIPLEXER DATA BOARD DIAGNOSTIC DSN 103110 + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------------------------- +DSN 103011 - 12920A Asynchronous Multiplexer (Cntl) +--------------------------------------------------- + +TESTED DEVICE: MUXM (hp2100_mux.c) + +CONFIGURATION: sim> set MUX DIAG + sim> deposit S 004042 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: ASYNC MULTIPLEXER CONTROL BOARD DIAGNOSTIC + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +---------------------------------------- +DSN 103017 - 12966 Asynchronous Data Set +---------------------------------------- + +TESTED DEVICE: BACI (hp2100_baci.c) + +CONFIGURATION: sim> set BACI realtime + sim> set BACI diag + sim> deposit S 000035 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: BUFFERED ASYNC COMM INTFC DIAG + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + PASS 000001 + +TEST RESULT: Passed. + + + +------------------------ +DSN 104003 - Teleprinter +------------------------ + +TESTED DEVICE: TTY (hp2100_stddev.c) + +CONFIGURATION: sim> deposit S 000011 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 001000 + sim> reset + sim> go + + START TTY DIAGNOSTIC + + HALT instruction 102075 + + sim> deposit A 000373 + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H030 TURN TTY PUNCH ON + PRESS RUN + + HALT instruction 102030 + + sim> attach TTY2 scratch.2752.punch + sim> go + + H045 TURN TTY PUNCH OFF + PRESS RUN + + HALT instruction 102045 + + sim> detach TTY2 + sim> deposit S 100000 + sim> go + + HALT instruction 102076 + + sim> go + + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_ + + HALT instruction 102076 + + sim> set console WRU=003 + sim> go + + INPUT THE FOLLOWING: + 1 2 3 4 5 6 7 8 9 0 : - + + Q W E R T Y U I O P + + A S D F G H J K L ; + + Z X C V B N M , . / + + SHIFT+ + ! " # $ % & ' ( ) * = + + _ @ + ^ < > ? + + CNTRL+ + WRU TAPE NTAP XOFF EOT RU BELL TAB VT FORM + + + RBOT CR LF + + + HALT instruction 102076 + + sim> set console WRU=005 + sim> go + + INPUT ANY KEY + T H I S 040 I S 040 A 040 + T E S T + + [CTRL+E] + Simulation stopped + + sim> deposit S 000002 + sim> go + + [CTRL+E] + Simulation stopped + + sim> deposit S 000000 + sim> go + + H044 INPUT TERMINATED + + ECHO MODE ANY INPUT IS ECHOED + THIS IS A TEST + + [CTRL+E] + Simulation stopped + + sim> deposit S 000002 + sim> go + + [CTRL+E] + Simulation stopped + + sim> deposit S 100000 + sim> go + + H044 INPUT TERMINATED + + HALT instruction 102076 + + sim> deposit TTY TTIME 158000 + sim> deposit S 000000 + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Partially passed. + +TEST NOTES: Test 2 is not executed. This test uses the teleprinter paper + tape reader. This feature is not simulated. + + Test 7 is the oscillator tolerance test, so the TTY TTIME is set + for realistic timing. + + + +------------------------------ +DSN 105101 - 2767 Line Printer +------------------------------ + +TESTED DEVICE: LPS (hp2100_lps.c) + +CONFIGURATION: sim> set LPS realtime + sim> attach LPS scratch.2767.printer + sim> deposit S 000014 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: 2767 L.P. DIAGNOSTIC + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H035 TURN OFF L.P. POWER + + HALT instruction 102035 + + sim> set LPS poweroff + sim> go + + H036 TURN ON L.P. POWER + + HALT instruction 102036 + + sim> set LPS poweron + sim> go + + H033 PUT L.P. ON-LINE + + HALT instruction 102033 + + sim> set LPS online + sim> go + + H034 MASTER CLEAR L.P. + + HALT instruction 102034 + + sim> set LPS offline + sim> go + + H033 PUT L.P. ON-LINE + + HALT instruction 102033 + + sim> set LPS online + sim> go + + H040 PUT L.P. OFF-LINE. TOGGLE TOP-OF-FORM SWITCH + + HALT instruction 102040 + + sim> set LPS offline + sim> go + + H033 PUT L.P. ON-LINE + + HALT instruction 102033 + + sim> set LPS online + sim> go + + H041 PUT L.P. OFF-LINE. TOGGLE PAPER-STEP 5 TIMES + + HALT instruction 102041 + + sim> set LPS offline + sim> go + + H033 PUT L.P. ON-LINE + + HALT instruction 102033 + + sim> set LPS online + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + +TEST NOTES: The simulation provides no manual Master Clear, Top of Form, or + Paper Step functions, so these are merely presumed above. + + + +------------------------------ +DSN 105102 - 2607 Line Printer +------------------------------ + +TESTED DEVICE: LPT (hp2100_lpt.c) + +CONFIGURATION: sim> attach LPT scratch.2607.printer + sim> deposit S 100015 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 001000 + sim> reset + sim> go + + 2607 LINE PRINTER DIAGNOSTIC + + HALT instruction 102075 + + sim> deposit A 000377 + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H040 PWR OFF LP,PRESS RUN + + HALT instruction 102040 + + sim> set LPT poweroff + sim> go + + H041 PWR ON LP,READY LP,PRESS RUN + + HALT instruction 102041 + + sim> set LPT poweron + sim> go + + H042 PRINT SW OFF,PRESS RUN + + HALT instruction 102042 + + sim> set LPT offline + sim> go + + H043 PRINT SW ON,PRESS RUN + + HALT instruction 102043 + + sim> set LPT online + sim> go + + H044 OPEN PLATEN,PRESS RUN + + HALT instruction 102044 + + sim> set LPT offline + sim> go + + H045 CLOSE PLATEN,PRESS RUN + + HALT instruction 102045 + + sim> set LPT online + sim> go + + H046 REMOVE PAPER FROM LP,PRESS RUN + + HALT instruction 102046 + + sim> detach LPT + sim> go + + H047 RESTORE PAPER IN LP, READY LP,PRESS RUN + + HALT instruction 102047 + + sim> attach LPT scratch.2607.printer + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + +TEST NOTES: The standard tests 00-07 are executed. Test 08 (operator + design) is selected as a standard test in this diagnostic only + and so is excluded manually. + + + +----------------------------------------------------- +DSN 111001 - HP2100A Disc File (2883) (multiple unit) +----------------------------------------------------- + +TESTED DEVICE: DQ (hp2100_dq.c) + +CONFIGURATION: sim> attach DQC0 scratch.U0.2883.disc + sim> attach DQC1 scratch.U1.2883.disc + sim> reset + sim> go 100 + + H0 HP 2100 SERIES DISC FILE(2883) DIAGNOSTIC + + H72 ENTER SELECT CODES,DMA CHANNEL IN SWITCH REGISTER,PRESS RUN + + HALT instruction 107001 + + sim> deposit S 002411 + sim> go + + H1 ENTER PROGRAM OPTIONS IN SWITCH REGISTER,PRESS RUN + + HALT instruction 107077 + + sim> deposit S 000400 + sim> go + +TEST REPORT: H65 PASS 0001 + H65 PASS 0002 + + [CTRL+E] + Simulation stopped + +TEST RESULT: Passed. + +TEST NOTES: Two passes are required to test all head/unit combinations. + + + +-------------------------------------------------------- +DSN 111001 - HP2100A Disc File (2883) (user interaction) +-------------------------------------------------------- + +TESTED DEVICE: DQ (hp2100_dq.c) + +CONFIGURATION: sim> attach DQC0 scratch.U0.2883.disc + sim> reset + sim> go 100 + + H0 HP 2100 SERIES DISC FILE(2883) DIAGNOSTIC + + H72 ENTER SELECT CODES,DMA CHANNEL IN SWITCH REGISTER,PRESS RUN + + HALT instruction 107001 + + sim> deposit S 002411 + sim> go + + H1 ENTER PROGRAM OPTIONS IN SWITCH REGISTER,PRESS RUN + + HALT instruction 107077 + + sim> deposit S 000142 + sim> go + +TEST REPORT: H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> go + + H37 READ ADDRESS IN S0 + E47 DATA WORD 0000 IS 000000 SHOULD BE 100000 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0002 UNIT 00 + + HALT instruction 102001 + + sim> go + + H37 READ ADDRESS IN S0 + E47 DATA WORD 0000 IS 000000 SHOULD BE 100001 + H51 CYL 0001 HEAD 01 SECTOR 00 WORD COUNT 0002 UNIT 00 + + HALT instruction 102001 + + sim> go + + H33 WRITE DEFECTIVE TRACK IN S0 + E64 STATUS IS 000000 SHOULD BE 000031 + H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H41 READ DEFECTIVE TRACK IN S0 + E64 STATUS IS 000000 SHOULD BE 000031 + H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H67 CLEAR FORMAT SWITCH ON UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> go + + H33 WRITE DEFECTIVE TRACK IN S0 + E64 STATUS IS 000000 SHOULD BE 000031 + H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H41 READ DEFECTIVE TRACK IN S0 + E64 STATUS IS 000000 SHOULD BE 000031 + H51 CYL 0000 HEAD 01 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H42 WRITE PROTECTED TRACK IN S0 + E64 STATUS IS 000000 SHOULD BE 000011 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H36 WRITE ADDRESS IN S0 + E64 STATUS IS 000000 SHOULD BE 000011 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0046 UNIT 00 + + HALT instruction 102001 + + sim> go + + H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> go + + H67 CLEAR FORMAT SWITCH ON UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> go + + H70 DISABLE UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> set DQC0 unloaded + sim> go + + H40 ENABLE UNIT 0 + + [CTRL+E] + Simulation stopped + + sim> set DQC0 loaded + sim> go + + H71 PRESS PRESET THEN PRESS RUN + + HALT instruction 102002 + + sim> deposit S 010140 + sim> reset + sim> go + + H74 SHORT PASS + H65 PASS 0001 + + HALT instruction 102077 + +TEST RESULT: Partially passed. + +TEST NOTES: Step 0 tests the FORMAT OVERRIDE switch, the use of the flagged + track bit to indicate a protected or defective track, and the + ability to write a sector address field that differs from the + sector location to indicate track sparing. These features are + not simulated. + + + +---------------------------------------------------------- +DSN 151302 - 7900/01 Cartridge Disc Memory (multiple unit) +---------------------------------------------------------- + +TESTED DEVICE: DP (hp2100_dp.c) + +CONFIGURATION: sim> attach DPC0 scratch.U0.7900.disc + sim> attach DPC1 scratch.U1.7900.disc + sim> attach DPC2 scratch.U2.7900.disc + sim> attach DPC3 scratch.U3.7900.disc + sim> deposit S 000022 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000004 + sim> reset + sim> go + + H0 7900/7901 CARTRIDGE DISC MEMORY DIAGNOSTIC + H24 CYLINDER TABLE + 000,001,002,004,008,016,032,064,128,202 + H25 WISH TO CHANGE? + NO + + H27 PATTERN TABLE + 000000 177777 125252 052525 007417 + 170360 162745 163346 155555 022222 + H25 WISH TO CHANGE? + NO + + H62 TYPE A FOR HEADS 0,1;B FOR 2,3;C FOR ALTERNATELY 0,1 THEN 2,3 + C + + H23 00020 ERRORS/PASS ALLOWED + H25 WISH TO CHANGE? + NO + + H37 UNIT TABLE/ 01 DRIVE(S); 0 + H25 WISH TO CHANGE? + YES + + H34 ENTER UNIT NUMBERS(0-3)SEPARATED BY COMMAS + 0,1,2,3 + + H37 UNIT TABLE/ 04 DRIVE(S); 0 1 2 3 + H25 WISH TO CHANGE? + + [CTRL+E] + Simulation stopped + + sim> deposit S 000000 + sim> go + + NO + +TEST REPORT: H65 LONG PASS 0001,HEADS 0/1,UNIT 00, 0000 ERRORS + H65 LONG PASS 0002,HEADS 0/1,UNIT 01, 0000 ERRORS + H65 LONG PASS 0003,HEADS 0/1,UNIT 02, 0000 ERRORS + H65 LONG PASS 0004,HEADS 0/1,UNIT 03, 0000 ERRORS,MULTI-DRIVE + H65 LONG PASS 0005,HEADS 2/3,UNIT 00, 0000 ERRORS + H65 LONG PASS 0006,HEADS 2/3,UNIT 01, 0000 ERRORS + H65 LONG PASS 0007,HEADS 2/3,UNIT 02, 0000 ERRORS + H65 LONG PASS 0008,HEADS 2/3,UNIT 03, 0000 ERRORS,MULTI-DRIVE + + [CTRL+E] + Simulation stopped + +TEST RESULT: Passed. + +TEST NOTES: Eight passes are required to test all head/unit combinations. + + + +------------------------------------------------------------- +DSN 151302 - 7900/01 Cartridge Disc Memory (user interaction) +------------------------------------------------------------- + +TESTED DEVICE: DP (hp2100_dp.c) + +CONFIGURATION: sim> attach DPC0 scratch.U0.7900.disc + sim> deposit S 000022 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000160 + sim> reset + sim> go + +TEST REPORT: H0 7900/7901 CARTRIDGE DISC MEMORY DIAGNOSTIC + H66 SET OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H46 READ IN STEP 04 + E64 STATUS IS 000000 SHOULD BE 000010 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H22 CYCLIC CHECK IN STEP 04 + E64 STATUS IS 000000 SHOULD BE 000010 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0001 UNIT 00 + + HALT instruction 102001 + + sim> go + + H67 CLEAR OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H46 READ IN STEP 07 + E64 STATUS IS 000000 SHOULD BE 000031 + H51 CYL 0001 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H45 WRITE IN STEP 08 + E64 STATUS IS 000000 SHOULD BE 000011 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H36 INITIALIZE DATA IN STEP 09 + E64 STATUS IS 000000 SHOULD BE 000011 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 6144 UNIT 00 + + HALT instruction 102001 + + sim> go + + H66 SET OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H67 CLEAR OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H70 UNLOAD UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> set DPC0 unloaded + sim> go + + H40 PROTECT U/D THEN READY UNIT 0 + + [CTRL+E] + Simulation stopped + + sim> set DPC0 locked + sim> set DPC0 loaded + sim> go + + H41 CLEAR U/D PROTECT,LOAD,PUSH RUN + + HALT instruction 102002 + + sim> set DPC0 writeenabled + sim> go + + H71 PRESS PRESET(S) THEN PRESS RUN + + HALT instruction 102002 + + sim> reset + sim> go + + H65 SHORT PASS 0001,HEADS 0/1,UNIT 00, 0005 ERRORS + + [CTRL+E] + Simulation stopped + +TEST RESULT: Partially passed. + +TEST NOTES: Steps 4, 7, 8, and 9 test the defective and protected cylinder + bits and the FORMAT switch. These features are not simulated. + + + +----------------------------------------------- +DSN 151403 - 7905/06/20/25 Disc (multiple unit) +----------------------------------------------- + +TESTED DEVICE: DS (hp2100_ds.c) + +CONFIGURATION: sim> set DS0 7905 + sim> set DS1 7906 + sim> set DS2 7920 + sim> set DS3 7925 + sim> set DS4 7905 + sim> set DS5 7906 + sim> set DS6 7920 + sim> set DS7 7925 + sim> attach DS0 scratch.U0.7905.disc + sim> attach DS1 scratch.U1.7906.disc + sim> attach DS2 scratch.U2.7920.disc + sim> attach DS3 scratch.U3.7925.disc + sim> attach DS4 scratch.U4.7905.disc + sim> attach DS5 scratch.U5.7906.disc + sim> attach DS6 scratch.U6.7920.disc + sim> attach DS7 scratch.U7.7925.disc + sim> deposit S 000034 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000004 + sim> reset + sim> go + + H0 79XX/13037 DISC MEMORY DIAGNOSTIC + H37 UNIT TABLE: 01 DRIVE(S); 0 + H25 WISH TO CHANGE? + YES + + H34 ENTER UNIT NUMBERS(0-7)SEPARATED BY COMMAS + 0,1,2,3,4,5,6,7 + + H37 UNIT TABLE: 08 DRIVE(S); 0 1 2 3 4 5 6 7 + H25 WISH TO CHANGE? + NO + + ENTER:(U)NIT,(?) ERRS,(H)EAD,(O)UTPUT,(P)ATT,(S)OFT,(C)YL,(M)CPU,(E)XIT + H + + H62 HEAD TABLE; UNIT 0 7905A , 02 HEAD(S) 0 1 + H62 HEAD TABLE; UNIT 1 7906A , 02 HEAD(S) 0 1 + H62 HEAD TABLE; UNIT 2 7920A , 05 HEAD(S) 0 1 2 3 4 + H62 HEAD TABLE; UNIT 3 7925A , 09 HEAD(S) 0 1 2 3 4 5 6 7 8 + H62 HEAD TABLE; UNIT 4 7905A , 02 HEAD(S) 0 1 + H62 HEAD TABLE; UNIT 5 7906A , 02 HEAD(S) 0 1 + H62 HEAD TABLE; UNIT 6 7920A , 05 HEAD(S) 0 1 2 3 4 + H62 HEAD TABLE; UNIT 7 7925A , 09 HEAD(S) 0 1 2 3 4 5 6 7 8 + H25 WISH TO CHANGE? + YES + + H132 TYPE UNITS YOU WISH TO CHANGE SEPERATED BY COMMAS + 0,1,4,5 + + H62 HEAD TABLE; UNIT 0 7905A , 02 HEAD(S) 0 1 + H106 ENTER HEADS SEPARATED BY COMMAS + 0,1,2 + + H62 HEAD TABLE; UNIT 0 7905A , 03 HEAD(S) 0 1 2 + H25 WISH TO CHANGE? + NO + + H62 HEAD TABLE; UNIT 1 7906A , 02 HEAD(S) 0 1 + H106 ENTER HEADS SEPARATED BY COMMAS + 0,1,2,3 + + H62 HEAD TABLE; UNIT 1 7906A , 04 HEAD(S) 0 1 2 3 + H25 WISH TO CHANGE? + NO + + H62 HEAD TABLE; UNIT 4 7905A , 02 HEAD(S) 0 1 + H106 ENTER HEADS SEPARATED BY COMMAS + 0,1,2 + + H62 HEAD TABLE; UNIT 4 7905A , 03 HEAD(S) 0 1 2 + H25 WISH TO CHANGE? + NO + + H62 HEAD TABLE; UNIT 5 7906A , 02 HEAD(S) 0 1 + H106 ENTER HEADS SEPARATED BY COMMAS + 0,1,2,3 + + H62 HEAD TABLE; UNIT 5 7906A , 04 HEAD(S) 0 1 2 3 + H25 WISH TO CHANGE? + NO + + ENTER:(U)NIT,(?) ERRS,(H)EAD,(O)UTPUT,(P)ATT,(S)OFT,(C)YL,(M)CPU,(E)XIT + E + +TEST REPORT: H121 WARNING-FORMAT SWITCH OFF + H65 LONG PASS 0001,HEAD 012 ,UNIT 0,0000 ERRORS-0000 SOFT + H65 LONG PASS 0002,HEAD 0123 ,UNIT 1,0000 ERRORS-0000 SOFT + H65 LONG PASS 0003,HEAD 01234 ,UNIT 2,0000 ERRORS-0000 SOFT + H65 LONG PASS 0004,HEAD 012345678,UNIT 3,0000 ERRORS-0000 SOFT + H65 LONG PASS 0005,HEAD 012 ,UNIT 4,0000 ERRORS-0000 SOFT + H65 LONG PASS 0006,HEAD 0123 ,UNIT 5,0000 ERRORS-0000 SOFT + H65 LONG PASS 0007,HEAD 01234 ,UNIT 6,0000 ERRORS-0000 SOFT + H65 LONG PASS 0008,HEAD 012345678,UNIT 7,0000 ERRORS-0000 SOFT,MULTI-UNIT + + [CTRL+E] + Simulation stopped + +TEST RESULT: Passed. + +TEST NOTES: Eight passes are required to test all head/unit combinations. + + + +-------------------------------------------------- +DSN 151403 - 7905/06/20/25 Disc (user interaction) +-------------------------------------------------- + +TESTED DEVICE: DS (hp2100_ds.c) + +CONFIGURATION: sim> set DS0 7905 + sim> attach DS0 scratch.U0.7905.disc + sim> deposit S 000034 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000120 + sim> reset + sim> go + + H0 79XX/13037 DISC MEMORY DIAGNOSTIC + H37 UNIT TABLE: 01 DRIVE(S); 0 + H25 WISH TO CHANGE? + NO + +TEST REPORT: H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> set DS0 format + sim> go + + H46 READ IN STEP 04 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 1 0 00000 XXXX XXXX / 0 000010 0 0 0 1 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" + START 0000/00/00-LAST 0000/00/01 WORD COUNT 00128,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H22 VERIFY IN STEP 04 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 1 0 00000 XXXX XXXX / 0 000010 0 0 0 1 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" + START 0000/00/00-LAST 0001/00/00 WORD COUNT 00048,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H67 CLEAR FORMAT SWITCH ON UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> set DS0 noformat + sim> go + + H46 READ IN STEP 07 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 0 0 0 0 0 0 + SHOULD BE 0 0 1 10001 XXXX XXXX / 0 000010 0 0 0 0 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "DEFECTIVE TRK " + START 0001/00/00-LAST 0001/00/01 WORD COUNT 00128,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H45 WRITE IN STEP 08 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 0 0 0 0 0 0 + SHOULD BE 0 1 0 10110 XXXX XXXX / 0 000010 0 0 0 0 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "WRT PROTEC TRK" + START 0000/00/00-LAST 0000/00/01 WORD COUNT 00128,OLD CYL 0001,UNIT 00 + + HALT instruction 102001 + + sim> go + + H66 SET FORMAT SWITCH ON UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> set DS0 format + sim> go + + H45 WRITE IN STEP 10 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 1 0 00000 XXXX XXXX / 0 000010 0 0 0 1 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" + START 0000/00/00-LAST 0000/00/08 WORD COUNT 01024,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H70 UNLOAD UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> set DS0 unloaded + sim> go + + H107 READY UNIT 0 + + [CTRL+E] + Simulation stopped + + sim> set DS0 loaded + sim> go + + H142 PROTECT U/D,PUSH RUN + + HALT instruction 102002 + + sim> set DS0 locked + sim> go + + H143 CLEAR U/D PROTECT,PUSH RUN + + HALT instruction 102002 + + sim> set DS0 writeenabled + sim> go + + H110 PRESS PRESET(S),PRESS RUN + + HALT instruction 102002 + + sim> reset + sim> go + + H46 READ IN STEP 38 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 0 0 00111 0000 0000 / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "CYL CMP ERROR " + START 0000/00/01-LAST 0000/00/03 WORD COUNT 00138,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H46 READ IN STEP 39 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 0 0 01001 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "HD/SEC CMP ERR" + START 0000/00/01-LAST 0000/00/03 WORD COUNT 00138,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H46 READ IN STEP 40 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 0 0 01001 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "HD/SEC CMP ERR" + START 0000/00/01-LAST 0000/00/03 WORD COUNT 00138,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H46 READ IN STEP 41 + E47 DATA WORD 0065 IS 075126 SHOULD BE 030400 + E47 DATA WORD 0066 IS 000762 SHOULD BE 030400 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 0 0 01111 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "POSS CORR DATA" + START 0000/00/00-LAST 0000/00/03 WORD COUNT 00128,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H46 READ IN STEP 42 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 0 0 01000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "UNCOR DATA ERR" + START 0000/00/00-LAST 0000/00/03 WORD COUNT 00276,OLD CYL 0000,UNIT 00 + + HALT instruction 102001 + + sim> go + + H22 VERIFY IN STEP 43 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 0 0 1 10001 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "DEFECTIVE TRK " + START 0016/00/00-LAST 0017/00/00 WORD COUNT 00048,OLD CYL 0128,UNIT 00 + + HALT instruction 102001 + + sim> go + + H22 VERIFY IN STEP 43 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 1 0 0 10000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" SHOULD BE "SPR TRK ACCESS" + START 0128/01/00-LAST 0129/01/00 WORD COUNT 00048,OLD CYL 0016,UNIT 00 + + HALT instruction 102001 + + sim> go + + H45 WRITE IN STEP 43 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 1 0 0 00000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" + START 0016/00/33-LAST 0016/00/34 WORD COUNT 00128,OLD CYL 0128,UNIT 00 + + HALT instruction 102001 + + sim> go + + H46 READ IN STEP 43 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 1 0 0 00000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" + START 0016/00/33-LAST 0016/00/34 WORD COUNT 00128,OLD CYL 0016,UNIT 00 + + HALT instruction 102001 + + sim> go + + H46 READ IN STEP 43 + E47 DATA WORD 0000 IS 156164 SHOULD BE 144300 + E47 DATA WORD 0001 IS 023302 SHOULD BE 117306 + E47 DATA WORD 0002 IS 114642 SHOULD BE 045322 + H135 S P D TSTAT XXXX UNIT / E DRTYPE X A P F DF FS SC NR B + E64 STATUS IS 0 0 0 00000 0000 0000 / 0 000010 0 0 0 1 0 0 0 0 0 + SHOULD BE 1 0 0 00000 XXXX XXXX / 0 000010 0 0 0 X 0 0 0 0 0 + H137 TERMINATION STATUS IS "NORMAL COMPLET" + START 0016/00/33-LAST 0016/00/34 WORD COUNT 00128,OLD CYL 0016,UNIT 00 + + HALT instruction 102001 + + sim> go + + H65 SHORT PASS 0001,HEAD 01 ,UNIT 0,0015 ERRORS-0015 SOFT + + [CTRL+E] + Simulation stopped + +TEST RESULT: Partially passed. + +TEST NOTES: Steps 4, 8, and 10 test the protected cylinder bit. Step 7 + tests the defective cylinder bit. Steps 38, 39, and 40 test the + Write Full Sector command. Steps 41 and 42 test error + correction. Step 43 tests the spare cylinder bit and track + sparing. These features are not simulated. + + + +------------------------------------------------- +DSN 112200 - 9-Track Magnetic Tape (7970B, 13181) +------------------------------------------------- + +DEVICE: MS (hp2100_ms.c) + +CONFIGURATION: sim> detach MSC0 + sim> set MSC 13181A + sim> set MSC realtime + sim> attach MSC0 scratch.U0.7970.tape + sim> attach MSC1 scratch.U1.7970.tape + sim> attach MSC2 scratch.U2.7970.tape + sim> attach MSC3 scratch.U3.7970.tape + sim> deposit S 102030 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000217 + sim> reset + sim> go + +TEST REPORT: 7970-13181 DIAG. + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H025 FOR DATA CH + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H025 FOR CMND CH + H154 UNIT 000000 + H034 UNIT ON-LINE + H155 STATUS IS 0 000 000 001 000 000 + H154 UNIT 000001 + H034 UNIT ON-LINE + H155 STATUS IS 0 000 000 001 000 000 + H154 UNIT 000002 + H034 UNIT ON-LINE + H155 STATUS IS 0 000 000 001 000 000 + H154 UNIT 000003 + H034 UNIT ON-LINE + H155 STATUS IS 0 000 000 001 000 000 + + H154 UNIT 000000 + H102 RECORD 000117 + H054 COMMAND 000061 + H155 STATUS IS 0 000 000 000 000 000 + H155 AND SHOULD BE 0 000 000 000 000 000 + + TEST 23 + E135 LRCC ERROR + + HALT instruction 106035 + + sim> go + + H154 UNIT 000000 + H102 RECORD 000117 + H054 COMMAND 000061 + H155 STATUS IS 0 000 000 000 000 000 + H155 AND SHOULD BE 0 000 000 000 000 000 + E141 CRCC ERROR + + HALT instruction 106041 + + sim> go + + H126 EXCHANGE REELS + + HALT instruction 106026 + + sim> attach MSC0 scratch.U3.7970.tape + sim> attach MSC1 scratch.U2.7970.tape + sim> attach MSC2 scratch.U1.7970.tape + sim> attach MSC3 scratch.U0.7970.tape + sim> go + + H127 SET SW 13 TO LOOP + + HALT instruction 106027 + + sim> go + + H130 REMOVE WRITE RING + + HALT instruction 106030 + + sim> set MSC0 locked + sim> go + + H131 REPLACE WRITE RING + + HALT instruction 106031 + + sim> set MSC0 writeenabled + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC0 online + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC1 online + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC2 online + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC3 online + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Partially passed. + +TEST NOTES: Test 23 verifies the LRCC and CRCC values obtained from the + interface. These features are not simulated. (Setting bit 7 of + the S register during configuration eliminates most LRCC/CRCC + checks but does not inhibit test 23.) + + If test 34 is selected manually, E065 WRITE ERROR will occur. + This is due to the implementation of the tape simulation + library. Test 34 writes data in a single record until a data + error or EOT occurs (conceivably 20+ megabytes for the largest + reel size at 800 bpi). Because the tape simulation library + writes complete records, the 7970 simulator must use a data + buffer to accumulate the entire record before calling + "sim_tape_wrrecf" to write the record. The simulator uses a + data buffer of 32768 words. When the buffer is full, + parity-error status is returned to the program. + + + +------------------------------------------------- +DSN 112200 - 9-Track Magnetic Tape (7970E, 13183) +------------------------------------------------- + +DEVICE: MS (hp2100_ms.c) + +CONFIGURATION: sim> detach MSC0 + sim> set MSC 13183A + sim> set MSC realtime + sim> attach MSC0 scratch.U0.7970.tape + sim> attach MSC1 scratch.U1.7970.tape + sim> attach MSC2 scratch.U2.7970.tape + sim> attach MSC3 scratch.U3.7970.tape + sim> deposit S 104030 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000017 + sim> reset + sim> go + +TEST REPORT: 7970-13183 DIAG. + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H025 FOR DATA CH + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H025 FOR CMND CH + H154 UNIT 000000 + H034 UNIT ON-LINE + H155 STATUS IS 1 000 000 001 000 000 + H154 UNIT 000001 + H034 UNIT ON-LINE + H155 STATUS IS 1 010 000 001 000 000 + H154 UNIT 000002 + H034 UNIT ON-LINE + H155 STATUS IS 1 100 000 001 000 000 + H154 UNIT 000003 + H034 UNIT ON-LINE + H155 STATUS IS 1 110 000 001 000 000 + + H126 EXCHANGE REELS + + HALT instruction 106026 + + sim> attach MSC0 scratch.U3.7970.tape + sim> attach MSC1 scratch.U2.7970.tape + sim> attach MSC2 scratch.U1.7970.tape + sim> attach MSC3 scratch.U0.7970.tape + sim> go + + H127 SET SW 13 TO LOOP + + HALT instruction 106027 + + sim> go + + H130 REMOVE WRITE RING + + HALT instruction 106030 + + sim> set MSC0 locked + sim> go + + H131 REPLACE WRITE RING + + HALT instruction 106031 + + sim> set MSC0 writeenabled + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC0 online + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC1 online + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC2 online + sim> go + + H137 PUT TAPE UNIT ON-LINE + + HALT instruction 106037 + + sim> set MSC3 online + sim> go + + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +------------------------------------ +DSN 146200 - Paper Tape Reader/Punch +------------------------------------ + +TESTED DEVICE: PTR and PTP (hp2100_stddev.c) + +CONFIGURATION: sim> deposit S 001012 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 001000 + sim> reset + sim> go + + PAPER TAPE READER AND PUNCH DIAGNOSTIC DSN 146200 + + HALT instruction 102075 + + sim> deposit A 000200 + sim> reset + sim> go + + H060 TO MAKE LOOP, PUNCH ON AND RUN + + HALT instruction 102060 + + sim> attach PTP loop.2895.punch + sim> go + + PASS 000001 + + HALT instruction 102077 + + sim> detach PTP + sim> deposit S 001000 + sim> reset + sim> go 2000 + + PAPER TAPE READER AND PUNCH DIAGNOSTIC DSN 146200 + + HALT instruction 102075 + + sim> deposit A 003177 + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: H050 BI-O ON PUNCH + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H055 BI-O ON READER + H024 PRESS PRESET (EXT&INT),RUN + + HALT instruction 102024 + + sim> reset + sim> go + + H025 BI-O COMP + H051 ALL CHARTR COMBINATIONS, PUNCH ONLY + TURN PUNCH ON, PRESS RUN + + HALT instruction 102051 + + sim> attach PTP scratch.2895.punch + sim> go + + H052 ALL CHARTR COMBINATIONS, VERIFY + TEAR TAPE AT PUNCH, PLACE IN READER, PRESS RUN + + HALT instruction 102052 + + sim> detach PTP + sim> attach PTR scratch.2895.punch + sim> go + + H054 PLACE LOOP IN READER-PRESS RUN + TO START READ, SET BIT0 TO 1 + TO EXIT TEST, SET BIT0 TO 0 + + HALT instruction 102054 + + sim> set PTR diag + sim> attach PTR loop.2895.punch + sim> deposit S 000001 + sim> go + + [CTRL+E] + Simulation stopped + + sim> deposit S 000000 + sim> go + + H054 PLACE LOOP IN READER-PRESS RUN + TO START READ, SET BIT0 TO 1 + TO EXIT TEST, SET BIT0 TO 0 + + HALT instruction 102054 + + sim> deposit S 000001 + sim> go + + [CTRL+E] + Simulation stopped + + sim> deposit PTR TIME 100 + sim> deposit PTP TIME 200 + sim> deposit S 000000 + sim> go + + H056 TURN PUNCH ON, PRESS RUN. PUNCH ROUTINE + WILL START. LOAD THE TAPE BEING PUNCHED + INTO THE READER. + TO START READ, SET BIT0 TO 1 + TO EXIT, SET BIT0 TO 0 + + HALT instruction 102056 + + sim> set PTR reader + sim> attach PTR scratch.2895.punch + sim> attach PTP scratch.2895.punch + sim> go + + [CTRL+E] + Simulation stopped + + sim> deposit S 000001 + sim> go + + [CTRL+E] + Simulation stopped + + sim> deposit S 000000 + sim> go + + H057 TO COMPLETE, TEAR TAPE, PRESS RUN + + HALT instruction 102057 + + sim> go + + H063 READER SPEED TEST. PLACE LOOP IN READER + BIT 5=0 FOR 2748-58, BIT 5=1 FOR 2737. PRESS RUN. + + HALT instruction 102063 + + sim> set PTR diag + sim> attach PTR loop.2895.punch + sim> deposit PTR TIME 3150 + sim> go + + H066 TEST 11 COMPLETE + H100 PUNCH SPEED TEST. + BIT 6=0 FOR 2895 OR BIT 6=1 FOR 2753-PRESS RUN + + HALT instruction 106000 + + sim> deposit PTP TIME 20790 + sim> go + + H103 TEST 12 COMPLETE + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + +TEST NOTES: Test 07 is executed to punch a tape loop that is used in tests + 04, 05, and 11. Then the default tests 00-06, plus tests 11 and + 12, are executed. + + Test 06 punches and reads the same tape concurrently (the tape + coming out of the punch is then fed into the reader). Under + simulation, it is necessary to delay starting the read until the + punch buffer has been flushed to the disc. Also, this test + depends on the reader being at least twice as fast as the punch, + so the PTR/PTP TIME registers are adjusted accordingly. + + Test 11 and test 12 are speed tests, so the PTR and PTP TIMEs + are set for realistic timing. + + + + +STAND-ALONE DIAGNOSTIC DETAILED EXECUTION AND RESULTS +===================================================== + +Each execution note below presumes that the target diagnostic has been loaded. +For all runs, the diagnostic configurator was used in automatic mode to load the +target diagnostic from a paper tape image, as follows: + + sim> attach -r MSC0 24396-13601_Rev-2326.abin.tape + sim> deposit S 000000 + sim> boot MSC0 + + HALT instruction 102077 + + sim> attach PTR [paper-tape-image-file] + sim> deposit S 001011 + sim> reset + sim> go 100 + + + +------------------------------------------------ +DSN 101217 - 2000/Access Comm Processor for 21MX +------------------------------------------------ + +TESTED DEVICE: CPU (hp2100_cpu2.c) + +BINARY TAPE: 13207-16001 Rev. 1728 + +CONFIGURATION: sim> set CPU IOP + + sim> deposit S 000013 + sim> reset + sim> go 100 + + HALT instruction 102074 + + sim> deposit S 000000 + sim> reset + sim> go + +TEST REPORT: 21MX 2000 COMPUTER SYSTEM COMM. PROC. FIRMWARE DIAGNOSTIC + H030 CRC TEST + H040 ENQ, DEQ AND PENQ TESTS + H060 IAL TEST + H110 INS,READF, SAVE AND RESTR TESTS + H120 LAI AND SAI TESTS + H130 PFREX TEST + H140 PFREI TEST + H150 PFRIO TEST + PASS 000001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +-------------------------------------------- +DSN (none) - HP 3030 Magnetic Tape Subsystem +-------------------------------------------- + +TESTED DEVICE: MT (hp2100_mt.c) + +BINARY TAPE: None available. + +CONFIGURATION: (none) + +TEST REPORT: (none) + +TEST RESULT: Not tested. + +TEST NOTES: The limited documentation available for this unit suggests that + the diagnostic is HP product number 20433, but no copy of this + diagnostic has been found. + + + +----------------------------------------------------------- +DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2770) +----------------------------------------------------------- + +TESTED DEVICE: DR (hp2100_dr.c) + +BINARY TAPE: 22682-16017 Rev. 1612 + +CONFIGURATION: sim> reset + sim> go 100 + + H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC + ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN + + HALT instruction 107001 + + sim> set DRC 180K + sim> set DRC trackprot=8 + sim> attach DRC0 scratch.U0.2770.disc + sim> deposit S 002611 + sim> go + + H1 CONFIGURATION COMPLETE + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, + H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN + + HALT instruction 107077 + + sim> deposit S 010000 + sim> go + +TEST REPORT: H12 DEVICE HAS 90 SECTORS + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN + + HALT instruction 102002 + + sim> set DRC protected + sim> go + + H14 DEVICE HAS 0032 TRACKS,THE FOLLOWING ARE PROTECTED: + H63 0000 TO 0007 + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H36 PASS 0001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +--------------------------------------------------------------- +DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2771-001) +--------------------------------------------------------------- + +TESTED DEVICE: DR (hp2100_dr.c) + +BINARY TAPE: 22682-16017 Rev. 1612 + +CONFIGURATION: sim> reset + sim> go 100 + + H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC + ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN + + HALT instruction 107001 + + sim> set DRC 720K + sim> set DRC trackprot=32 + sim> attach DRC0 scratch.U0.2771.disc + sim> deposit S 002611 + sim> go + + H1 CONFIGURATION COMPLETE + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, + H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN + + HALT instruction 107077 + + sim> deposit S 010000 + sim> go + +TEST REPORT: H12 DEVICE HAS 90 SECTORS + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN + + HALT instruction 102002 + + sim> set DRC protected + sim> go + + H14 DEVICE HAS 0128 TRACKS,THE FOLLOWING ARE PROTECTED: + H63 0000 TO 0031 + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H36 PASS 0001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +----------------------------------------------------------- +DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2773) +----------------------------------------------------------- + +TESTED DEVICE: DR (hp2100_dr.c) + +BINARY TAPE: 22682-16017 Rev. 1612 + +CONFIGURATION: sim> reset + sim> go 100 + + H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC + ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN + + HALT instruction 107001 + + sim> set DRC 384K + sim> set DRC trackprot=16 + sim> attach DRC0 scratch.U0.2773.disc + sim> deposit S 002611 + sim> go + + H1 CONFIGURATION COMPLETE + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, + H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN + + HALT instruction 107077 + + sim> deposit S 010000 + sim> go + +TEST REPORT: H12 DEVICE HAS 32 SECTORS + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN + + HALT instruction 102002 + + sim> set DRC protected + sim> go + + H14 DEVICE HAS 0192 TRACKS,THE FOLLOWING ARE PROTECTED: + H63 0000 TO 0015 + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H36 PASS 0001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +----------------------------------------------------------- +DSN 177777 - HP 2100 Fixed Head Disc/Drum Diagnostic (2775) +----------------------------------------------------------- + +TESTED DEVICE: DR (hp2100_dr.c) + +BINARY TAPE: 22682-16017 Rev. 1612 + +CONFIGURATION: sim> reset + sim> go 100 + + H0 2100 SERIES FIXED HEAD DISC/DRUM DIAGNOSTIC + ENTER SELECT CODES, CHANNELS IN SWITCH REGISTER,PUSH RUN + + HALT instruction 107001 + + sim> set DRC 1536K + sim> set DRC trackprot=64 + sim> attach DRC0 scratch.U0.2775.disc + sim> deposit S 002611 + sim> go + + H1 CONFIGURATION COMPLETE + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, + H70 ENTER PROGRAM OPTIONS IN SWITCH REGISTER, PUSH RUN + + HALT instruction 107077 + + sim> deposit S 010000 + sim> go + +TEST REPORT: H12 DEVICE HAS 32 SECTORS + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H10 SET TRACK PROTECT SWITCH TO PROTECTED,PRESS RUN + + HALT instruction 102002 + + sim> set DRC protected + sim> go + + H14 DEVICE HAS 0768 TRACKS,THE FOLLOWING ARE PROTECTED: + H63 0000 TO 0063 + H11 SET TRACK PROTECT SWITCH TO NOT PROTECTED, PRESS RUN + + HALT instruction 102002 + + sim> set DRC unprotected + sim> go + + H36 PASS 0001 + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +----------------------------------------------- +DSN (none) - 12875 Processor Interconnect Cable +----------------------------------------------- + +TESTED DEVICE: IPLI, IPLO (hp2100_ipl.c) + +BINARY TAPE: 24197-60001 Rev. B + +CONFIGURATION: sim> set IPLI DIAG + sim> set IPLO DIAG + sim> deposit S 003332 + sim> reset + sim> go 2 + + HALT instruction 107076 + + sim> deposit S 010000 + sim> reset + sim> go + + HALT instruction 107077 + + sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: H14. START 12875 CABLE DIAGNOSTIC + H77. END 12875 CABLE DIAGNOSTIC + + HALT instruction 102077 + +TEST RESULT: Passed. + + + +----------------------------------------------------------------- +DSN (none) - HP2100A Cartridge Disc Memory (2871) (multiple unit) +----------------------------------------------------------------- + +TESTED DEVICE: DP (hp2100_dp.c) + +BINARY TAPE: 24203-60001 Rev. A + +CONFIGURATION: sim> set DPC 12557A + sim> attach DPC0 scratch.U0.2871.disc + sim> attach DPC1 scratch.U1.2871.disc + sim> attach DPC2 scratch.U2.2871.disc + sim> attach DPC3 scratch.U3.2871.disc + sim> deposit S 002211 + sim> reset + sim> go 2 + + HALT instruction 107077 + + sim> deposit S 000400 + sim> reset + sim> go 100 + + H0 HP2100A CARTRIDGE DISC MEMORY DIAGNOSTIC + H34 ENTER UNIT NUMBERS(0-3)SEPARATED BY COMMAS + 0,1,2,3 + + H33 RESET SWITCH 8 + + HALT instruction 102002 + + sim> deposit S 000004 + sim> go + + H24 CYLINDER TABLE + 000,001,002,004,008,016,032,064,128,202 + H25 WISH TO ALTER TABLE? + NO + + H27 PATTERN TABLE + 000000 177777 125252 052525 007417 + 170360 162745 163346 155555 022222 + H25 WISH TO ALTER TABLE? + NO + + H62 TYPE A FOR HEADS 0,1;B FOR 2,3;C FOR ALTERNATELY 0,1 THEN 2,3 + C + + H32 RESET SWITCH 2 + + HALT instruction 102002 + + sim> deposit S 000000 + sim> reset + sim> go 100 + +TEST REPORT: H0 HP2100A CARTRIDGE DISC MEMORY DIAGNOSTIC + H65 PASS 0001 + H65 PASS 0002 + H65 PASS 0003 + H65 PASS 0004 + + [CTRL+E] + Simulation stopped + +TEST RESULT: Passed. + +TEST NOTES: Four passes are required to test all head/unit combinations. + + + +-------------------------------------------------------------------- +DSN (none) - HP2100A Cartridge Disc Memory (2871) (user interaction) +-------------------------------------------------------------------- + +TESTED DEVICE: DP (hp2100_dp.c) + +BINARY TAPE: 24203-60001 Rev. A + +CONFIGURATION: sim> set DPC 12557A + sim> attach DPC0 scratch.U0.2871.disc + sim> deposit S 002211 + sim> reset + sim> go 2 + + HALT instruction 107077 + + sim> deposit S 010020 + sim> reset + sim> go 100 + +TEST REPORT: H0 HP2100A CARTRIDGE DISC MEMORY DIAGNOSTIC + H66 SET OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H37 READ AFTER WRITE ADDRESS IN S0 + E64 STATUS IS 000000 SHOULD BE 000010 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H22 CYCLIC CHECK IN S0 + E64 STATUS IS 000000 SHOULD BE 000010 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H67 CLEAR OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H41 READ DEFECTIVE TRACK IN S0 + E64 STATUS IS 000000 SHOULD BE 000031 + H51 CYL 0001 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H42 WRITE PROTECTED TRACK IN S0 + E64 STATUS IS 000000 SHOULD BE 000011 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 0128 UNIT 00 + + HALT instruction 102001 + + sim> go + + H36 WRITE ADDRESS IN S0 + E64 STATUS IS 000000 SHOULD BE 000011 + H51 CYL 0000 HEAD 00 SECTOR 00 WORD COUNT 3072 UNIT 00 + + HALT instruction 102001 + + sim> go + + H66 SET OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H67 CLEAR OVERRIDE SWITCH,PUSH RUN + + HALT instruction 102002 + + sim> go + + H70 UNLOCK UNIT 0,PUSH RUN + + HALT instruction 102002 + + sim> set DPC0 unloaded + sim> go + + H40 READY UNIT 0 + + [CTRL+E] + Simulation stopped + + sim> set DPC0 loaded + sim> go + + H71 PRESS PRESET THEN PRESS RUN + + HALT instruction 102002 + + sim> deposit S 000140 + sim> reset + sim> go + + H65 PASS 0001 + +TEST RESULT: Partially passed. + +TEST NOTES: Step 0 tests the the defective and protected cylinder bits and + the FORMAT OVERRIDE switch. These features are not simulated. + + + + +ONLINE DIAGNOSTIC DETAILED EXECUTION AND RESULTS +================================================ + +Online diagnostics were run under the control of the indicated operating +systems. Unless otherwise noted, the programs were loaded with the default +configuration specified by the associated linker command file or the operating +system. + + + +------------------------------------------------ +#EMA - Extended Memory Array Firmware Diagnostic +------------------------------------------------ + +TESTED DEVICE: CPU (hp2100_cpu5.c) + +BINARY FILE: 92067-16013 Rev. 1805 + +HOST SYSTEM: RTE-IVB Rev. 5010 + +CONFIGURATION: sim> set CPU EMA + sim> go + +TEST REPORT: EMA ON-LINE DIAGNOSTIC SUCCESSFUL COMPLETION + +TEST RESULT: Passed. + + + +------------------------------------------------ +VMACK - Virtual Memory Array Firmware Diagnostic +------------------------------------------------ + +TESTED DEVICE: CPU (hp2100_cpu5.c) + +BINARY FILE: 92084-16423 Rev. 2121 + +HOST SYSTEM: RTE-6/VM Rev. 6200 + +CONFIGURATION: sim> set CPU 1000-F + sim> set CPU VMA + sim> go + +TEST REPORT: VMACK - VMA FIRMWARE DIAGNOSTIC, FIRMWARE REV# 003 + VMACK - .IMAR NO ERRORS DETECTED PASS# 1 + VMACK - .JMAR NO ERRORS DETECTED PASS# 1 + VMACK - .LBP NO ERRORS DETECTED PASS# 1 + VMACK - .LBPR NO ERRORS DETECTED PASS# 1 + VMACK - .LPX NO ERRORS DETECTED PASS# 1 + VMACK - .LPXR NO ERRORS DETECTED PASS# 1 + VMACK - .PMAP NO ERRORS DETECTED PASS# 1 + VMACK - .IMAP NO ERRORS DETECTED PASS# 1 + VMACK - .JMAP NO ERRORS DETECTED PASS# 1 + +TEST RESULT: Passed. + + + +-------------------------------------------------- +VISOD - Vector Instruction Set Firmware Diagnostic +-------------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu7.c) + +BINARY FILE: 12824-16002 Rev. 2026 + +HOST SYSTEM: RTE-IVB Rev. 5010 + +CONFIGURATION: sim> set CPU 1000-F + sim> set CPU VIS + sim> go + +TEST REPORT: VIS ON-LINE DIAGNOSTIC SUCCESSFUL COMPLETION + +TEST RESULT: Passed. + + + +-------------------------------------------------- +VISOD - Vector Instruction Set Firmware Diagnostic +-------------------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu7.c) + +BINARY FILE: 12829-16006 Rev. 2226 + +HOST SYSTEM: RTE-6/VM Rev. 6200 + +CONFIGURATION: sim> set CPU 1000-F + sim> set CPU VIS + sim> go + +TEST REPORT: VIS ON-LINE DIAGNOSTIC SUCCESSFUL COMPLETION + +TEST RESULT: Passed. + + + +--------------------------------------- +SDIAG - SIGNAL/1000 Firmware Diagnostic +--------------------------------------- + +TESTED DEVICE: CPU (hp2100_cpu7.c) + +BINARY FILE: 92835-16006 Rev. 2040 + +HOST SYSTEM: RTE-6/VM Rev. 6200 + +CONFIGURATION: sim> set CPU 1000-F + sim> set CPU VIS + sim> set CPU SIGNAL + sim> go + +TEST REPORT: SIGNAL/1000 FIRMWARE DIAGNOSTIC + + SIGNAL/1000 FIRMWARE DIAGNOSTIC SUCCESSFUL COMPLETION + +TEST RESULT: Passed. diff --git a/HP2100/hp2100_dp.c b/HP2100/hp2100_dp.c new file mode 100644 index 0000000..17c5c21 --- /dev/null +++ b/HP2100/hp2100_dp.c @@ -0,0 +1,974 @@ +/* hp2100_dp.c: HP 2100 12557A/13210A disk simulator + + Copyright (c) 1993-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dp 12557A 2871 disk subsystem + 13210A 7900 disk subsystem + + 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified) + 01-Mar-05 JDB Added SET UNLOAD/LOAD + 07-Oct-04 JDB Fixed enable/disable from either device + Fixed ANY ERROR status for 12557A interface + Fixed unattached drive status for 12557A interface + Status cmd without prior STC DC now completes (12557A) + OTA/OTB CC on 13210A interface also does CLC CC + Fixed RAR model + Fixed seek check on 13210 if sector out of range + 20-Aug-04 JDB Fixes from Dave Bryan + - Check status on unattached drive set busy and not ready + - Check status tests wrong unit for write protect status + - Drive on line sets ATN, will set FLG if polling + 15-Aug-04 RMS Controller resumes polling for ATN interrupts after + read status (found by Dave Bryan) + 22-Jul-04 RMS Controller sets ATN for all commands except + read status (found by Dave Bryan) + 21-Apr-04 RMS Fixed typo in boot loader (found by Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Fixed SR setting in IBL + Fixed interpretation of SR<0> + Revised IBL loader + Implemented DMA SRQ (follows FLG) + 25-Apr-03 RMS Revised for extended file support + Fixed bug(s) in boot (found by Terry Newton) + 10-Nov-02 RMS Added BOOT command, fixed numerous bugs + 15-Jan-02 RMS Fixed INIT handling (found by Bill McDermith) + 10-Jan-02 RMS Fixed f(x)write call (found by Bill McDermith) + 03-Dec-01 RMS Changed DEVNO to use extended SET/SHOW + 24-Nov-01 RMS Changed STA to be an array + 07-Sep-01 RMS Moved function prototypes + 29-Nov-00 RMS Made variable names unique + 21-Nov-00 RMS Fixed flag, buffer power up state + + The simulator uses a number of state variables: + + dpc_busy set to drive number + 1 when the controller is busy + of the unit in use + dpd_xfer set to 1 if the data channel is executing a data transfer + dpd_wval set to 1 by OTx if either !dpc_busy or dpd_xfer + dpc_poll set to 1 if attention polling is enabled + + dpc_busy and dpd_xfer are set together at the start of a read, write, refine, + or init. When data transfers are complete (CLC DC), dpd_xfer is cleared, but the + operation is not necessarily over. When the operation is complete, dpc_busy + is cleared and the command channel flag is set. + + dpc_busy && !dpd_xfer && STC DC (controller is busy, data channel transfer has + been terminated by CLC DC, but a word has been placed in the data channel buffer) + indicates data overrun. + + dpd_wval is used in write operations to fill out the sector buffer with 0's + if only a partial sector has been transferred. + + dpc_poll indicates whether seek completion polling can occur. It is cleared + by reset and CLC CC and set by issuance of a seek or completion of check status. + + The controller's "Record Address Register" (RAR) contains the CHS address of + the last Seek or Address Record command executed. The RAR is shared among + all drives on the controller. In addition, each drive has an internal + position register that contains the last cylinder position transferred to the + drive during Seek command execution (data operations always start with the + RAR head and sector position). + + In a real drive, the address field of the sector under the head is read and + compared to the RAR. When they match, the target sector is under the head + and is ready for reading or writing. If a match doesn't occur, an Address + Error is indicated. In the simulator, the address field is obtained from the + drive's current position register during a read, i.e., the "on-disc" address + field is assumed to match the current position. + + References: + - 7900A Disc Drive Operating and Service Manual (07900-90002, Feb-1975) + - 13210A Disc Drive Interface Kit Operating and Service Manual + (13210-90003, Nov-1974) + - 12557A Cartridge Disc Interface Kit Operating and Service Manual + (12557-90001, Sep-1970) + + The following implemented behaviors have been inferred from secondary sources + (diagnostics, operating system drivers, etc.), due to absent or contradictory + authoritative information; future correction may be needed: + + 1. Status bit 15 (ATTENTION) does not set bit 0 (ANY ERROR) on the 12557A. + 2. Omitting STC DC before Status Check does not set DC flag but does poll. +*/ + +#include "hp2100_defs.h" + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) +#define FNC u3 /* saved function */ +#define DRV u4 /* drive number (DC) */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ + +#define DP_N_NUMWD 7 +#define DP_NUMWD (1 << DP_N_NUMWD) /* words/sector */ +#define DP_NUMSC2 12 /* sectors/srf 12557 */ +#define DP_NUMSC3 24 /* sectors/srf 13210 */ +#define DP_NUMSC (dp_ctype? DP_NUMSC3: DP_NUMSC2) +#define DP_NUMSF 4 /* surfaces/cylinder */ +#define DP_NUMCY 203 /* cylinders/disk */ +#define DP_SIZE2 (DP_NUMSF * DP_NUMCY * DP_NUMSC2 * DP_NUMWD) +#define DP_SIZE3 (DP_NUMSF * DP_NUMCY * DP_NUMSC3 * DP_NUMWD) +#define DP_NUMDRV 4 /* # drives */ + +/* Command word */ + +#define CW_V_FNC 12 /* function */ +#define CW_M_FNC 017 +#define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC) +#define FNC_STA 000 /* status check */ +#define FNC_WD 001 /* write */ +#define FNC_RD 002 /* read */ +#define FNC_SEEK 003 /* seek */ +#define FNC_REF 005 /* refine */ +#define FNC_CHK 006 /* check */ +#define FNC_INIT 011 /* init */ +#define FNC_AR 013 /* address */ +#define FNC_SEEK1 020 /* fake - seek1 */ +#define FNC_SEEK2 021 /* fake - seek2 */ +#define FNC_SEEK3 022 /* fake - seek3 */ +#define FNC_CHK1 023 /* fake - check1 */ +#define FNC_AR1 024 /* fake - arec1 */ +#define CW_V_DRV 0 /* drive */ +#define CW_M_DRV 03 +#define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV) + +/* Disk address words */ + +#define DA_V_CYL 0 /* cylinder */ +#define DA_M_CYL 0377 +#define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) +#define DA_V_HD 8 /* head */ +#define DA_M_HD 03 +#define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD) +#define DA_V_SC 0 /* sector */ +#define DA_M_SC2 017 +#define DA_M_SC3 037 +#define DA_M_SC (dp_ctype? DA_M_SC3: DA_M_SC2) +#define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC) +#define DA_CKMASK2 037 /* check mask */ +#define DA_CKMASK3 077 +#define DA_CKMASK (dp_ctype? DA_CKMASK3: DA_CKMASK2) + +/* Status in dpc_sta[drv], (u) = unused in 13210, (d) = dynamic */ + +#define STA_ATN 0100000 /* attention (u) */ +#define STA_1ST 0040000 /* first status */ +#define STA_OVR 0020000 /* overrun */ +#define STA_RWU 0010000 /* rw unsafe NI (u) */ +#define STA_ACU 0004000 /* access unsafe NI */ +#define STA_HUNT 0002000 /* hunting NI (12557) */ +#define STA_PROT 0002000 /* protected (13210) */ +#define STA_SKI 0001000 /* incomplete NI (u) */ +#define STA_SKE 0000400 /* seek error */ +/* 0000200 /* unused */ +#define STA_NRDY 0000100 /* not ready (d) */ +#define STA_EOC 0000040 /* end of cylinder */ +#define STA_AER 0000020 /* addr error */ +#define STA_FLG 0000010 /* flagged */ +#define STA_BSY 0000004 /* seeking */ +#define STA_DTE 0000002 /* data error */ +#define STA_ERR 0000001 /* any error (d) */ + +#define STA_ERSET2 (STA_1ST | STA_OVR | STA_RWU | STA_ACU | \ + STA_SKI | STA_SKE | STA_NRDY | \ + STA_EOC | STA_AER | STA_DTE) /* 12557A error set */ +#define STA_ERSET3 (STA_ATN | STA_1ST | STA_OVR | STA_RWU | STA_ACU | \ + STA_SKI | STA_SKE | STA_NRDY | STA_EOC | STA_AER | \ + STA_FLG | STA_BSY | STA_DTE) /* 13210A error set */ +#define STA_ANYERR (dp_ctype ? STA_ERSET3 : STA_ERSET2) + +#define STA_UNLOADED (dp_ctype ? (STA_NRDY | STA_BSY) : STA_NRDY) +#define STA_MBZ13 (STA_ATN | STA_RWU | STA_SKI) /* zero in 13210 */ + +extern uint32 PC, SR; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; +extern int32 sim_switches; + +int32 dp_ctype = 1; /* ctrl type */ +int32 dpc_busy = 0; /* cch unit */ +int32 dpc_poll = 0; /* cch poll enable */ +int32 dpc_cnt = 0; /* check count */ +int32 dpc_eoc = 0; /* end of cyl */ +int32 dpc_stime = 100; /* seek time */ +int32 dpc_ctime = 100; /* command time */ +int32 dpc_xtime = 5; /* xfer time */ +int32 dpc_dtime = 2; /* dch time */ +int32 dpd_obuf = 0, dpd_ibuf = 0; /* dch buffers */ +int32 dpc_obuf = 0; /* cch buffers */ +int32 dpd_xfer = 0; /* xfer in prog */ +int32 dpd_wval = 0; /* write data valid */ +int32 dp_ptr = 0; /* buffer ptr */ +uint8 dpc_rarc = 0; /* RAR cylinder */ +uint8 dpc_rarh = 0; /* RAR head */ +uint8 dpc_rars = 0; /* RAR sector */ +uint8 dpc_ucyl[DP_NUMDRV] = { 0 }; /* unit cylinder */ +uint16 dpc_sta[DP_NUMDRV] = { 0 }; /* status regs */ +uint16 dpxb[DP_NUMWD]; /* sector buffer */ + +DEVICE dpd_dev, dpc_dev; +int32 dpdio (int32 inst, int32 IR, int32 dat); +int32 dpcio (int32 inst, int32 IR, int32 dat); +t_stat dpc_svc (UNIT *uptr); +t_stat dpd_svc (UNIT *uptr); +t_stat dpc_reset (DEVICE *dptr); +t_stat dpc_attach (UNIT *uptr, char *cptr); +t_stat dpc_detach (UNIT* uptr); +t_stat dpc_boot (int32 unitno, DEVICE *dptr); +void dp_god (int32 fnc, int32 drv, int32 time); +void dp_goc (int32 fnc, int32 drv, int32 time); +t_stat dpc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* DPD data structures + + dpd_dev DPD device descriptor + dpd_unit DPD unit list + dpd_reg DPD register list +*/ + +DIB dp_dib[] = { + { DPD, 0, 0, 0, 0, 0, &dpdio }, + { DPC, 0, 0, 0, 0, 0, &dpcio } + }; + +#define dpd_dib dp_dib[0] +#define dpc_dib dp_dib[1] + +UNIT dpd_unit = { UDATA (&dpd_svc, 0, 0) }; + +REG dpd_reg[] = { + { ORDATA (IBUF, dpd_ibuf, 16) }, + { ORDATA (OBUF, dpd_obuf, 16) }, + { BRDATA (DBUF, dpxb, 8, 16, DP_NUMWD) }, + { DRDATA (BPTR, dp_ptr, DP_N_NUMWD) }, + { FLDATA (CMD, dpd_dib.cmd, 0) }, + { FLDATA (CTL, dpd_dib.ctl, 0) }, + { FLDATA (FLG, dpd_dib.flg, 0) }, + { FLDATA (FBF, dpd_dib.fbf, 0) }, + { FLDATA (SRQ, dpd_dib.srq, 0) }, + { FLDATA (XFER, dpd_xfer, 0) }, + { FLDATA (WVAL, dpd_wval, 0) }, + { ORDATA (DEVNO, dpd_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB dpd_mod[] = { + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &dpd_dev }, + { 0 } + }; + +DEVICE dpd_dev = { + "DPD", &dpd_unit, dpd_reg, dpd_mod, + 1, 10, DP_N_NUMWD, 1, 8, 16, + NULL, NULL, &dpc_reset, + NULL, NULL, NULL, + &dpd_dib, DEV_DISABLE + }; + +/* DPC data structures + + dpc_dev DPC device descriptor + dpc_unit DPC unit list + dpc_reg DPC register list + dpc_mod DPC modifier list +*/ + +UNIT dpc_unit[] = { + { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) }, + { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) }, + { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) }, + { UDATA (&dpc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, DP_SIZE3) } + }; + +REG dpc_reg[] = { + { ORDATA (OBUF, dpc_obuf, 16) }, + { ORDATA (BUSY, dpc_busy, 4), REG_RO }, + { ORDATA (CNT, dpc_cnt, 5) }, + { FLDATA (CMD, dpc_dib.cmd, 0) }, + { FLDATA (CTL, dpc_dib.ctl, 0) }, + { FLDATA (FLG, dpc_dib.flg, 0) }, + { FLDATA (FBF, dpc_dib.fbf, 0) }, + { FLDATA (SRQ, dpc_dib.srq, 0) }, + { FLDATA (EOC, dpc_eoc, 0) }, + { FLDATA (POLL, dpc_poll, 0) }, + { DRDATA (RARC, dpc_rarc, 8), PV_RZRO }, + { DRDATA (RARH, dpc_rarh, 2), PV_RZRO }, + { DRDATA (RARS, dpc_rars, 5), PV_RZRO }, + { BRDATA (CYL, dpc_ucyl, 10, 8, DP_NUMDRV), PV_RZRO }, + { BRDATA (STA, dpc_sta, 8, 16, DP_NUMDRV) }, + { DRDATA (CTIME, dpc_ctime, 24), PV_LEFT }, + { DRDATA (DTIME, dpc_dtime, 24), PV_LEFT }, + { DRDATA (STIME, dpc_stime, 24), PV_LEFT }, + { DRDATA (XTIME, dpc_xtime, 24), REG_NZ | PV_LEFT }, + { FLDATA (CTYPE, dp_ctype, 0), REG_HRO }, + { URDATA (UFNC, dpc_unit[0].FNC, 8, 8, 0, + DP_NUMDRV, REG_HRO) }, + { URDATA (CAPAC, dpc_unit[0].capac, 10, T_ADDR_W, 0, + DP_NUMDRV, PV_LEFT | REG_HRO) }, + { ORDATA (DEVNO, dpc_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB dpc_mod[] = { + { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", dpc_load_unload }, + { UNIT_UNLOAD, 0, "heads loaded", "LOADED", dpc_load_unload }, + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "13210A", + &dp_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "12557A", + &dp_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, + NULL, &dp_showtype, NULL }, + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &dpd_dev }, + { 0 } + }; + +DEVICE dpc_dev = { + "DPC", dpc_unit, dpc_reg, dpc_mod, + DP_NUMDRV, 8, 24, 1, 8, 16, + NULL, NULL, &dpc_reset, + &dpc_boot, &dpc_attach, &dpc_detach, + &dpc_dib, DEV_DISABLE + }; + +/* IO instructions */ + +int32 dpdio (int32 inst, int32 IR, int32 dat) +{ +int32 devd; + +devd = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devd); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devd) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devd) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + dpd_obuf = dat; + if (!dpc_busy || dpd_xfer) dpd_wval = 1; /* if !overrun, valid */ + break; + + case ioMIX: /* merge */ + dat = dat | dpd_ibuf; + break; + + case ioLIX: /* load */ + dat = dpd_ibuf; + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCTL (devd); /* clr ctl, cmd */ + clrCMD (devd); + dpd_xfer = 0; /* clr xfer */ + } + else { /* STC */ + if (!dp_ctype) setCTL (devd); /* 12557: set ctl */ + setCMD (devd); /* set cmd */ + if (dpc_busy && !dpd_xfer) /* overrun? */ + dpc_sta[dpc_busy - 1] |= STA_OVR; + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devd); } /* H/C option */ +return dat; +} + +int32 dpcio (int32 inst, int32 IR, int32 dat) +{ +int32 i, devc, fnc, drv; +int32 devd = dpd_dib.devno; + +devc = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devc); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devc) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devc) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioLIX: /* load */ + dat = 0; + case ioMIX: /* merge */ + for (i = 0; i < DP_NUMDRV; i++) + if (dpc_sta[i] & STA_ATN) dat = dat | (1 << i); + break; + + case ioOTX: /* output */ + dpc_obuf = dat; + if (!dp_ctype) break; + IR = IR | I_CTL; /* 13210 OTx causes CLC */ + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC? */ + clrCTL (devc); /* clr cmd, ctl */ + clrCMD (devc); /* cancel non-seek */ + if (dpc_busy) sim_cancel (&dpc_unit[dpc_busy - 1]); + sim_cancel (&dpd_unit); /* cancel dch */ + dpd_xfer = 0; /* clr dch xfer */ + dpc_busy = 0; /* clr cch busy */ + dpc_poll = 0; /* clr cch poll */ + } + else { /* STC */ + setCTL (devc); /* set ctl */ + if (!CMD (devc)) { /* is cmd clr? */ + setCMD (devc); /* set cmd */ + drv = CW_GETDRV (dpc_obuf); /* get fnc, drv */ + fnc = CW_GETFNC (dpc_obuf); /* from cmd word */ + switch (fnc) { /* case on fnc */ + + case FNC_SEEK: /* seek */ + dpc_poll = 1; /* enable polling */ + dp_god (fnc, drv, dpc_dtime); /* sched dch xfr */ + break; + + case FNC_STA: /* rd sta */ + if (dp_ctype) { clrFSR (devd); } /* 13210? clr dch flag */ + case FNC_CHK: /* check */ + case FNC_AR: /* addr rec */ + dp_god (fnc, drv, dpc_dtime); /* sched dch xfr */ + break; + + case FNC_RD: case FNC_WD: /* read, write */ + case FNC_REF: case FNC_INIT: /* refine, init */ + dp_goc (fnc, drv, dpc_ctime); /* sched drive */ + break; + } /* end case */ + } /* end if */ + } /* end else */ + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devc); } /* H/C option */ +return dat; +} + +/* Start data channel operation */ + +void dp_god (int32 fnc, int32 drv, int32 time) +{ +dpd_unit.DRV = drv; /* save unit */ +dpd_unit.FNC = fnc; /* save function */ +sim_activate (&dpd_unit, time); +return; +} + +/* Start controller operation */ + +void dp_goc (int32 fnc, int32 drv, int32 time) +{ +int32 t; + +if (t = sim_is_active (&dpc_unit[drv])) { /* still seeking? */ + sim_cancel (&dpc_unit[drv]); /* stop seek */ + dpc_sta[drv] = dpc_sta[drv] & ~STA_BSY; /* clear busy */ + time = time + t; /* include seek time */ + } +dp_ptr = 0; /* init buf ptr */ +dpc_eoc = 0; /* clear end cyl */ +dpc_busy = drv + 1; /* set busy */ +dpd_xfer = 1; /* xfer in prog */ +dpc_unit[drv].FNC = fnc; /* save function */ +dpc_sta[drv] = dpc_sta[drv] & ~STA_ATN; /* clear ATN */ +sim_activate (&dpc_unit[drv], time); /* activate unit */ +return; +} + +/* Data channel unit service + + This routine handles the data channel transfers. It also handles + data transfers that are blocked by seek in progress. + + uptr->DRV = target drive + uptr->FNC = target function + + Seek substates + seek - transfer cylinder + seek1 - transfer head/surface + Address record + ar - transfer cylinder + ar1 - transfer head/surface, finish operation + Status check - transfer status, finish operation + Check data + chk - transfer sector count +*/ + +t_stat dpd_svc (UNIT *uptr) +{ +int32 i, drv, devc, devd, st; + +drv = uptr->DRV; /* get drive no */ +devc = dpc_dib.devno; /* get cch devno */ +devd = dpd_dib.devno; /* get dch devno */ +switch (uptr->FNC) { /* case function */ + + case FNC_AR: /* arec, need cyl */ + case FNC_SEEK: /* seek, need cyl */ + if (CMD (devd)) { /* dch active? */ + dpc_rarc = DA_GETCYL (dpd_obuf); /* set RAR from cyl word */ + dpd_wval = 0; /* clr data valid */ + setFSR (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + if (uptr->FNC == FNC_AR) uptr->FNC = FNC_AR1; + else uptr->FNC = FNC_SEEK1; /* advance state */ + } + sim_activate (uptr, dpc_xtime); /* no, wait more */ + break; + + case FNC_AR1: /* arec, need hd/sec */ + case FNC_SEEK1: /* seek, need hd/sec */ + if (CMD (devd)) { /* dch active? */ + dpc_rarh = DA_GETHD (dpd_obuf); /* set RAR from head */ + dpc_rars = DA_GETSC (dpd_obuf); /* set RAR from sector */ + dpd_wval = 0; /* clr data valid */ + setFSR (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + if (uptr->FNC == FNC_AR1) { + setFSR (devc); /* set cch flg */ + clrCMD (devc); /* clr cch cmd */ + dpc_sta[drv] = dpc_sta[drv] | STA_ATN; /* set drv attn */ + break; /* done if Address Record */ + } + if (sim_is_active (&dpc_unit[drv])) { /* if busy, */ + dpc_sta[drv] = dpc_sta[drv] | STA_SKE; /* seek check */ + break; /* allow prev seek to cmpl */ + } + if ((dpc_rarc >= DP_NUMCY) || /* invalid cyl? */ + (dp_ctype && (dpc_rars >= DP_NUMSC3))) { /* invalid sector? (13210A) */ + dpc_sta[drv] = dpc_sta[drv] | STA_SKE; /* seek check */ + sim_activate (&dpc_unit[drv], 1); /* schedule drive no-wait */ + dpc_unit[drv].FNC = FNC_SEEK3; /* do immed compl w/poll */ + break; + } + st = abs (dpc_rarc - dpc_ucyl[drv]) * dpc_stime; + if (st == 0) st = dpc_stime; /* min time */ + dpc_ucyl[drv] = dpc_rarc; /* transfer RAR */ + sim_activate (&dpc_unit[drv], st); /* schedule drive */ + dpc_sta[drv] = (dpc_sta[drv] | STA_BSY) & + ~(STA_SKE | STA_SKI | STA_HUNT); + dpc_unit[drv].FNC = FNC_SEEK2; /* set operation */ + } + else sim_activate (uptr, dpc_xtime); /* no, wait more */ + break; + + case FNC_STA: /* read status */ + if (CMD (devd) || dp_ctype) { /* dch act or 13210? */ + if ((dpc_unit[drv].flags & UNIT_UNLOAD) == 0) { /* drive up? */ + dpd_ibuf = dpc_sta[drv] & ~STA_ERR; /* clear err */ + if (dp_ctype) dpd_ibuf = /* 13210? */ + (dpd_ibuf & ~(STA_MBZ13 | STA_PROT)) | + (dpc_unit[drv].flags & UNIT_WPRT? STA_PROT: 0); + } + else dpd_ibuf = STA_UNLOADED; /* not ready */ + if (dpd_ibuf & STA_ANYERR) /* errors? set flg */ + dpd_ibuf = dpd_ibuf | STA_ERR; + setFSR (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + clrCMD (devc); /* clr cch cmd */ + } + dpc_sta[drv] = dpc_sta[drv] & /* clr sta flags */ + ~(STA_ATN | STA_1ST | STA_OVR | + STA_RWU | STA_ACU | STA_EOC | + STA_AER | STA_FLG | STA_DTE); + dpc_poll = 1; /* enable polling */ + for (i = 0; i < DP_NUMDRV; i++) { /* loop thru drives */ + if (dpc_sta[i] & STA_ATN) { /* any ATN set? */ + setFSR (devc); /* set cch flg */ + break; + } + } + break; + + case FNC_CHK: /* check, need cnt */ + if (CMD (devd)) { /* dch active? */ + dpc_cnt = dpd_obuf & DA_CKMASK; /* get count */ + dpd_wval = 0; /* clr data valid */ + dp_goc (FNC_CHK1, drv, dpc_xtime); /* sched drv */ + } + else sim_activate (uptr, dpc_xtime); /* wait more */ + break; + + default: + return SCPE_IERR; + } + +return SCPE_OK; +} + +/* Drive unit service + + This routine handles the data transfers. + + Seek substates + seek2 - done + Refine sector - erase sector, finish operation + Check data + chk1 - finish operation + Read + Write +*/ + +#define GETDA(x,y,z) \ + (((((x) * DP_NUMSF) + (y)) * DP_NUMSC) + (z)) * DP_NUMWD + +t_stat dpc_svc (UNIT *uptr) +{ +int32 da, drv, devc, devd, err; + +err = 0; /* assume no err */ +drv = uptr - dpc_dev.units; /* get drive no */ +devc = dpc_dib.devno; /* get cch devno */ +devd = dpd_dib.devno; /* get dch devno */ +if (uptr->flags & UNIT_UNLOAD) { /* drive down? */ + setFSR (devc); /* set cch flg */ + clrCMD (devc); /* clr cch cmd */ + dpc_sta[drv] = 0; /* clr status */ + dpc_busy = 0; /* ctlr is free */ + dpc_poll = 0; /* polling disabled */ + dpd_xfer = 0; + dpd_wval = 0; + return SCPE_OK; + } +switch (uptr->FNC) { /* case function */ + + case FNC_SEEK2: /* positioning done */ + dpc_sta[drv] = (dpc_sta[drv] | STA_ATN) & ~STA_BSY; /* fall into cmpl */ + case FNC_SEEK3: /* seek complete */ + if (dpc_poll) { /* polling enabled? */ + setFSR (devc); /* set cch flg */ + clrCMD (devc); /* clear cmd */ + } + return SCPE_OK; + + case FNC_REF: /* refine sector */ + break; /* just a NOP */ + + case FNC_RD: /* read */ + case FNC_CHK1: /* check */ + if (dp_ptr == 0) { /* new sector? */ + if (!CMD (devd) && (uptr->FNC != FNC_CHK1)) break; + if (dpc_rarc != dpc_ucyl[drv]) /* RAR cyl miscompare? */ + dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* set flag, read */ + if (dpc_rars >= DP_NUMSC) { /* bad sector? */ + dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* set flag, stop */ + break; + } + if (dpc_eoc) { /* end of cyl? */ + dpc_sta[drv] = dpc_sta[drv] | STA_EOC; + break; + } + da = GETDA (dpc_rarc, dpc_rarh, dpc_rars); /* calc disk addr */ + dpc_rars = (dpc_rars + 1) % DP_NUMSC; /* incr sector */ + if (dpc_rars == 0) { /* wrap? */ + dpc_rarh = dpc_rarh ^ 1; /* incr head */ + dpc_eoc = ((dpc_rarh & 1) == 0); /* calc eoc */ + } + if (err = fseek (uptr->fileref, da * sizeof (int16), + SEEK_SET)) break; + fxread (dpxb, sizeof (int16), DP_NUMWD, uptr->fileref); + if (err = ferror (uptr->fileref)) break; + } + dpd_ibuf = dpxb[dp_ptr++]; /* get word */ + if (dp_ptr >= DP_NUMWD) { /* end of sector? */ + if (uptr->FNC == FNC_CHK1) { /* check? */ + dpc_cnt = (dpc_cnt - 1) & DA_CKMASK; /* decr count */ + if (dpc_cnt == 0) break; /* stop at zero */ + } + dp_ptr = 0; /* wrap buf ptr */ + } + if (CMD (devd) && dpd_xfer) { /* dch on, xfer? */ + setFSR (devd); /* set flag */ + } + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dpc_xtime); /* sched next word */ + return SCPE_OK; + + case FNC_INIT: /* init */ + case FNC_WD: /* write */ + if (dp_ptr == 0) { /* start sector? */ + if (!CMD (devd) && !dpd_wval) break; /* xfer done? */ + if (uptr->flags & UNIT_WPRT) { /* wr prot? */ + dpc_sta[drv] = dpc_sta[drv] | STA_FLG; /* set status */ + break; /* done */ + } + if ((dpc_rarc != dpc_ucyl[drv]) || /* RAR cyl miscompare? */ + (dpc_rars >= DP_NUMSC)) { /* bad sector? */ + dpc_sta[drv] = dpc_sta[drv] | STA_AER; /* address error */ + break; + } + if (dpc_eoc) { /* end of cyl? */ + dpc_sta[drv] = dpc_sta[drv] | STA_EOC; /* set status */ + break; /* done */ + } + } + dpxb[dp_ptr++] = dpd_wval? dpd_obuf: 0; /* store word/fill */ + dpd_wval = 0; /* clr data valid */ + if (dp_ptr >= DP_NUMWD) { /* buffer full? */ + da = GETDA (dpc_rarc, dpc_rarh, dpc_rars); /* calc disk addr */ + dpc_rars = (dpc_rars + 1) % DP_NUMSC; /* incr sector */ + if (dpc_rars == 0) { /* wrap? */ + dpc_rarh = dpc_rarh ^ 1; /* incr head */ + dpc_eoc = ((dpc_rarh & 1) == 0); /* calc eoc */ + } + if (err = fseek (uptr->fileref, da * sizeof (int16), + SEEK_SET)) break; + fxwrite (dpxb, sizeof (int16), DP_NUMWD, uptr->fileref); + if (err = ferror (uptr->fileref)) break; /* error? */ + dp_ptr = 0; /* next sector */ + } + if (CMD (devd) && dpd_xfer) { /* dch on, xfer? */ + setFSR (devd); /* set flag */ + } + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dpc_xtime); /* sched next word */ + return SCPE_OK; + + default: + return SCPE_IERR; + } /* end case fnc */ + +dpc_sta[drv] = dpc_sta[drv] | STA_ATN; /* set ATN */ +setFSR (devc); /* set cch flg */ +clrCMD (devc); /* clr cch cmd */ +dpc_busy = 0; /* ctlr is free */ +dpd_xfer = dpd_wval = 0; +if (err != 0) { /* error? */ + perror ("DP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat dpc_reset (DEVICE *dptr) +{ +int32 drv; + +hp_enbdis_pair (dptr, /* make pair cons */ + (dptr == &dpd_dev)? &dpc_dev: &dpd_dev); +dpd_ibuf = dpd_obuf = 0; /* clear buffers */ +dpc_busy = dpc_obuf = 0; +dpc_eoc = 0; +dpc_poll = 0; +dpd_xfer = dpd_wval = 0; +dp_ptr = 0; +dpc_rarc = dpc_rarh = dpc_rars = 0; /* clear RAR */ +dpc_dib.cmd = dpd_dib.cmd = 0; /* clear cmd */ +dpc_dib.ctl = dpd_dib.ctl = 0; /* clear ctl */ +dpc_dib.fbf = dpd_dib.fbf = 1; /* set fbf */ +dpc_dib.flg = dpd_dib.flg = 1; /* set flg */ +dpc_dib.srq = dpd_dib.flg = 1; /* srq follows flg */ +sim_cancel (&dpd_unit); /* cancel dch */ +for (drv = 0; drv < DP_NUMDRV; drv++) { /* loop thru drives */ + sim_cancel (&dpc_unit[drv]); /* cancel activity */ + dpc_unit[drv].FNC = 0; /* clear function */ + dpc_ucyl[drv] = 0; /* clear drive pos */ + if (dpc_unit[drv].flags & UNIT_ATT) + dpc_sta[drv] = dpc_sta[drv] & STA_1ST; /* first seek status */ + else dpc_sta[drv] = 0; /* clear status */ + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat dpc_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); /* attach unit */ +if (r == SCPE_OK) dpc_load_unload (uptr, 0, NULL, NULL);/* if OK, load heads */ +return r; +} + +/* Detach routine */ + +t_stat dpc_detach (UNIT* uptr) +{ +dpc_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads */ +return detach_unit (uptr); /* detach unit */ +} + +/* Load and unload heads */ + +t_stat dpc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +uint32 drv; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to load */ + +if (value == UNIT_UNLOAD) /* unload heads? */ + uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */ +else { /* load heads */ + uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */ + drv = uptr - dpc_dev.units; /* get drive no */ + dpc_sta[drv] = dpc_sta[drv] | STA_ATN | STA_1ST; /* update status */ + if (dpc_poll) { /* polling enabled? */ + dpc_dib.fbf = 1; /* set fbf */ + dpc_dib.flg = 1; /* set flg */ + dpc_dib.srq = 1; /* srq follows flg */ + } + } +return SCPE_OK; +} + +/* Set controller type */ + +t_stat dp_settype (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i; + +if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; +for (i = 0; i < DP_NUMDRV; i++) { + if (dpc_unit[i].flags & UNIT_ATT) return SCPE_ALATT; + } +for (i = 0; i < DP_NUMDRV; i++) + dpc_unit[i].capac = (val? DP_SIZE3: DP_SIZE2); +dp_ctype = val; +return SCPE_OK; +} + +/* Show controller type */ + +t_stat dp_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (dp_ctype) fprintf (st, "13210A"); +else fprintf (st, "12557A"); +return SCPE_OK; +} + +/* 7900/7901 bootstrap routine (HP 12992F ROM) */ + +const uint16 dp_rom[IBL_LNT] = { + 0106710, /*ST CLC DC ; clr dch */ + 0106711, /* CLC CC ; clr cch */ + 0017757, /* JSB STAT ; get status */ + 0067746, /*SK LDB SKCMD ; seek cmd */ + 0106610, /* OTB DC ; cyl # */ + 0103710, /* STC DC,C ; to dch */ + 0106611, /* OTB CC ; seek cmd */ + 0103711, /* STC CC,C ; to cch */ + 0102310, /* SFS DC ; addr wd ok? */ + 0027710, /* JMP *-1 ; no, wait */ + 0006400, /* CLB */ + 0102501, /* LIA 1 ; read switches */ + 0002011, /* SLA,RSS ; <0> set? */ + 0047747, /* ADB BIT9 ; head 2 = removable */ + 0106610, /* OTB DC ; head/sector */ + 0103710, /* STC DC,C ; to dch */ + 0102311, /* SFS CC ; seek done? */ + 0027720, /* JMP *-1 ; no, wait */ + 0017757, /* JSB STAT ; get status */ + 0067776, /* LDB DMACW ; DMA control */ + 0106606, /* OTB 6 */ + 0067750, /* LDB ADDR1 ; memory addr */ + 0106602, /* OTB 2 */ + 0102702, /* STC 2 ; flip DMA ctrl */ + 0067752, /* LDB CNT ; word count */ + 0106602, /* OTB 2 */ + 0063745, /* LDB RDCMD ; read cmd */ + 0102611, /* OTA CC ; to cch */ + 0103710, /* STC DC,C ; start dch */ + 0103706, /* STC 6,C ; start DMA */ + 0103711, /* STC CC,C ; start cch */ + 0102311, /* SFS CC ; done? */ + 0027737, /* JMP *-1 ; no, wait */ + 0017757, /* JSB STAT ; get status */ + 0027775, /* JMP XT ; done */ + 0037766, /*FSMSK 037766 ; status mask */ + 0004000, /*STMSK 004000 ; unsafe mask */ + 0020000, /*RDCMD 020000 ; read cmd */ + 0030000, /*SKCMD 030000 ; seek cmd */ + 0001000, /*BIT9 001000 ; head 2 select */ + 0102011, /*ADDR1 102011 */ + 0102055, /*ADDR2 102055 */ + 0164000, /*CNT -6144. */ + 0, 0, 0, 0, /* unused */ + 0000000, /*STAT 0 */ + 0002400, /* CLA ; status request */ + 0102611, /* OTC CC ; to cch */ + 0103711, /* STC CC,C ; start cch */ + 0102310, /* SFS DC ; done? */ + 0027763, /* JMP *-1 */ + 0102510, /* LIA DC ; get status */ + 0013743, /* AND FSMSK ; mask 15,14,3,0 */ + 0002003, /* SZA,RSS ; drive ready? */ + 0127757, /* JMP STAT,I ; yes */ + 0013744, /* AND STMSK ; fault? */ + 0002002, /* SZA */ + 0102030, /* HLT 30 ; yes */ + 0027700, /* JMP ST ; no, retry */ + 0117751, /*XT JSB ADDR2,I ; start program */ + 0120010, /*DMACW 120000+DC */ + 0000000 /* -ST */ + }; + +t_stat dpc_boot (int32 unitno, DEVICE *dptr) +{ +int32 dev; + +if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ +dev = dpd_dib.devno; /* get data chan dev */ +if (ibl_copy (dp_rom, dev)) return SCPE_IERR; /* copy boot to memory */ +SR = (SR & IBL_OPT) | IBL_DP | (dev << IBL_V_DEV); /* set SR */ +if (sim_switches & SWMASK ('R')) SR = SR | IBL_DP_REM; /* boot from removable? */ +return SCPE_OK; +} diff --git a/HP2100/hp2100_dq.c b/HP2100/hp2100_dq.c new file mode 100644 index 0000000..50227a7 --- /dev/null +++ b/HP2100/hp2100_dq.c @@ -0,0 +1,849 @@ +/* hp2100_dq.c: HP 2100 12565A disk simulator + + Copyright (c) 1993-2006, Bill McDermith + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + dq 12565A 2883 disk system + + 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified) + 01-Mar-05 JDB Added SET UNLOAD/LOAD + 07-Oct-04 JDB Fixed enable/disable from either device + Shortened xtime from 5 to 3 (drive avg 156KW/second) + Fixed not ready/any error status + Fixed RAR model + 21-Apr-04 RMS Fixed typo in boot loader (found by Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Fixed SR setting in IBL + Revised IBL loader + Implemented DMA SRQ (follows FLG) + 25-Apr-03 RMS Fixed bug in status check + 10-Nov-02 RMS Added boot command, rebuilt like 12559/13210 + 09-Jan-02 WOM Copied dp driver and mods for 2883 + + Differences between 12559/13210 and 12565 controllers + - 12565 stops transfers on address miscompares; 12559/13210 only stops writes + - 12565 does not set error on positioner busy + - 12565 does not set positioner busy if already on cylinder + - 12565 does not need eoc logic, it will hit an invalid head number + + The controller's "Record Address Register" (RAR) contains the CHS address of + the last Position or Load Address command executed. The RAR is shared among + all drives on the controller. In addition, each drive has an internal + position register that contains the last cylinder and head position + transferred to the drive during Position command execution (sector operations + always start with the RAR sector position). + + In a real drive, the address field of the sector under the head is read and + compared to the RAR. When they match, the target sector is under the head + and is ready for reading or writing. If a match doesn't occur, an Address + Error is indicated. In the simulator, the address field is obtained from the + drive's current position register during a read, i.e., the "on-disc" address + field is assumed to match the current position. + + Reference: + - 12565A Disc Interface Kit Operating and Service Manual (12565-90003, Aug-1973) + + The following implemented behaviors have been inferred from secondary sources + (diagnostics, operating system drivers, etc.), due to absent or contradictory + authoritative information; future correction may be needed: + + 1. Read Address command starts at the sector number in the RAR. +*/ + +#include "hp2100_defs.h" + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) +#define FNC u3 /* saved function */ +#define DRV u4 /* drive number (DC) */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ + +#define DQ_N_NUMWD 7 +#define DQ_NUMWD (1 << DQ_N_NUMWD) /* words/sector */ +#define DQ_NUMSC 23 /* sectors/track */ +#define DQ_NUMSF 20 /* tracks/cylinder */ +#define DQ_NUMCY 203 /* cylinders/disk */ +#define DQ_SIZE (DQ_NUMSF * DQ_NUMCY * DQ_NUMSC * DQ_NUMWD) +#define DQ_NUMDRV 2 /* # drives */ + +/* Command word */ + +#define CW_V_FNC 12 /* function */ +#define CW_M_FNC 017 +#define CW_GETFNC(x) (((x) >> CW_V_FNC) & CW_M_FNC) +/* 000 /* unused */ +#define FNC_STA 001 /* status check */ +#define FNC_RCL 002 /* recalibrate */ +#define FNC_SEEK 003 /* seek */ +#define FNC_RD 004 /* read */ +#define FNC_WD 005 /* write */ +#define FNC_RA 006 /* read address */ +#define FNC_WA 007 /* write address */ +#define FNC_CHK 010 /* check */ +#define FNC_LA 013 /* load address */ +#define FNC_AS 014 /* address skip */ + +#define FNC_SEEK1 020 /* fake - seek1 */ +#define FNC_SEEK2 021 /* fake - seek2 */ +#define FNC_SEEK3 022 /* fake - seek3 */ +#define FNC_CHK1 023 /* fake - check1 */ +#define FNC_LA1 024 /* fake - ldaddr1 */ + +#define CW_V_DRV 0 /* drive */ +#define CW_M_DRV 01 +#define CW_GETDRV(x) (((x) >> CW_V_DRV) & CW_M_DRV) + +/* Disk address words */ + +#define DA_V_CYL 0 /* cylinder */ +#define DA_M_CYL 0377 +#define DA_GETCYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) +#define DA_V_HD 8 /* head */ +#define DA_M_HD 037 +#define DA_GETHD(x) (((x) >> DA_V_HD) & DA_M_HD) +#define DA_V_SC 0 /* sector */ +#define DA_M_SC 037 +#define DA_GETSC(x) (((x) >> DA_V_SC) & DA_M_SC) +#define DA_CKMASK 0777 /* check mask */ + +/* Status in dqc_sta[drv] - (d) = dynamic */ + +#define STA_DID 0000200 /* drive ID (d) */ +#define STA_NRDY 0000100 /* not ready (d) */ +#define STA_EOC 0000040 /* end of cylinder */ +#define STA_AER 0000020 /* addr error */ +#define STA_FLG 0000010 /* flagged */ +#define STA_BSY 0000004 /* seeking */ +#define STA_DTE 0000002 /* data error */ +#define STA_ERR 0000001 /* any error */ +#define STA_ANYERR (STA_NRDY | STA_EOC | STA_AER | STA_FLG | STA_DTE) + +extern uint32 PC, SR; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; + +int32 dqc_busy = 0; /* cch xfer */ +int32 dqc_cnt = 0; /* check count */ +int32 dqc_stime = 100; /* seek time */ +int32 dqc_ctime = 100; /* command time */ +int32 dqc_xtime = 3; /* xfer time */ +int32 dqc_dtime = 2; /* dch time */ +int32 dqd_obuf = 0, dqd_ibuf = 0; /* dch buffers */ +int32 dqc_obuf = 0; /* cch buffers */ +int32 dqd_xfer = 0; /* xfer in prog */ +int32 dqd_wval = 0; /* write data valid */ +int32 dq_ptr = 0; /* buffer ptr */ +uint8 dqc_rarc = 0; /* RAR cylinder */ +uint8 dqc_rarh = 0; /* RAR head */ +uint8 dqc_rars = 0; /* RAR sector */ +uint8 dqc_ucyl[DQ_NUMDRV] = { 0 }; /* unit cylinder */ +uint8 dqc_uhed[DQ_NUMDRV] = { 0 }; /* unit head */ +uint16 dqc_sta[DQ_NUMDRV] = { 0 }; /* unit status */ +uint16 dqxb[DQ_NUMWD]; /* sector buffer */ + +DEVICE dqd_dev, dqc_dev; +int32 dqdio (int32 inst, int32 IR, int32 dat); +int32 dqcio (int32 inst, int32 IR, int32 dat); +t_stat dqc_svc (UNIT *uptr); +t_stat dqd_svc (UNIT *uptr); +t_stat dqc_reset (DEVICE *dptr); +t_stat dqc_attach (UNIT *uptr, char *cptr); +t_stat dqc_detach (UNIT* uptr); +t_stat dqc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat dqc_boot (int32 unitno, DEVICE *dptr); +void dq_god (int32 fnc, int32 drv, int32 time); +void dq_goc (int32 fnc, int32 drv, int32 time); + +/* DQD data structures + + dqd_dev DQD device descriptor + dqd_unit DQD unit list + dqd_reg DQD register list +*/ + +DIB dq_dib[] = { + { DQD, 0, 0, 0, 0, 0, &dqdio }, + { DQC, 0, 0, 0, 0, 0, &dqcio } + }; + +#define dqd_dib dq_dib[0] +#define dqc_dib dq_dib[1] + +UNIT dqd_unit = { UDATA (&dqd_svc, 0, 0) }; + +REG dqd_reg[] = { + { ORDATA (IBUF, dqd_ibuf, 16) }, + { ORDATA (OBUF, dqd_obuf, 16) }, + { BRDATA (DBUF, dqxb, 8, 16, DQ_NUMWD) }, + { DRDATA (BPTR, dq_ptr, DQ_N_NUMWD) }, + { FLDATA (CMD, dqd_dib.cmd, 0) }, + { FLDATA (CTL, dqd_dib.ctl, 0) }, + { FLDATA (FLG, dqd_dib.flg, 0) }, + { FLDATA (FBF, dqd_dib.fbf, 0) }, + { FLDATA (SRQ, dqd_dib.srq, 0) }, + { FLDATA (XFER, dqd_xfer, 0) }, + { FLDATA (WVAL, dqd_wval, 0) }, + { ORDATA (DEVNO, dqd_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB dqd_mod[] = { + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &dqd_dev }, + { 0 } + }; + +DEVICE dqd_dev = { + "DQD", &dqd_unit, dqd_reg, dqd_mod, + 1, 10, DQ_N_NUMWD, 1, 8, 16, + NULL, NULL, &dqc_reset, + NULL, NULL, NULL, + &dqd_dib, DEV_DISABLE + }; + +/* DQC data structures + + dqc_dev DQC device descriptor + dqc_unit DQC unit list + dqc_reg DQC register list + dqc_mod DQC modifier list +*/ + +UNIT dqc_unit[] = { + { UDATA (&dqc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, DQ_SIZE) }, + { UDATA (&dqc_svc, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, DQ_SIZE) } + }; + +REG dqc_reg[] = { + { ORDATA (OBUF, dqc_obuf, 16) }, + { ORDATA (BUSY, dqc_busy, 2), REG_RO }, + { ORDATA (CNT, dqc_cnt, 9) }, + { FLDATA (CMD, dqc_dib.cmd, 0) }, + { FLDATA (CTL, dqc_dib.ctl, 0) }, + { FLDATA (FLG, dqc_dib.flg, 0) }, + { FLDATA (FBF, dqc_dib.fbf, 0) }, + { FLDATA (SRQ, dqc_dib.srq, 0) }, + { DRDATA (RARC, dqc_rarc, 8), PV_RZRO }, + { DRDATA (RARH, dqc_rarh, 5), PV_RZRO }, + { DRDATA (RARS, dqc_rars, 5), PV_RZRO }, + { BRDATA (CYL, dqc_ucyl, 10, 8, DQ_NUMDRV), PV_RZRO }, + { BRDATA (HED, dqc_uhed, 10, 5, DQ_NUMDRV), PV_RZRO }, + { BRDATA (STA, dqc_sta, 8, 16, DQ_NUMDRV) }, + { DRDATA (CTIME, dqc_ctime, 24), PV_LEFT }, + { DRDATA (DTIME, dqc_dtime, 24), PV_LEFT }, + { DRDATA (STIME, dqc_stime, 24), PV_LEFT }, + { DRDATA (XTIME, dqc_xtime, 24), REG_NZ + PV_LEFT }, + { URDATA (UFNC, dqc_unit[0].FNC, 8, 8, 0, + DQ_NUMDRV, REG_HRO) }, + { ORDATA (DEVNO, dqc_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB dqc_mod[] = { + { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", dqc_load_unload }, + { UNIT_UNLOAD, 0, "heads loaded", "LOADED", dqc_load_unload }, + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &dqd_dev }, + { 0 } + }; + +DEVICE dqc_dev = { + "DQC", dqc_unit, dqc_reg, dqc_mod, + DQ_NUMDRV, 8, 24, 1, 8, 16, + NULL, NULL, &dqc_reset, + &dqc_boot, &dqc_attach, &dqc_detach, + &dqc_dib, DEV_DISABLE + }; + +/* IO instructions */ + +int32 dqdio (int32 inst, int32 IR, int32 dat) +{ +int32 devd; + +devd = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devd); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devd) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devd) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + dqd_obuf = dat; + if (!dqc_busy || dqd_xfer) dqd_wval = 1; /* if !overrun, valid */ + break; + + case ioMIX: /* merge */ + dat = dat | dqd_ibuf; + break; + + case ioLIX: /* load */ + dat = dqd_ibuf; + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCTL (devd); /* clr ctl, cmd */ + clrCMD (devd); + dqd_xfer = 0; /* clr xfer */ + } + else { /* STC */ + setCTL (devd); /* set ctl, cmd */ + setCMD (devd); + if (dqc_busy && !dqd_xfer) /* overrun? */ + dqc_sta[dqc_busy - 1] |= STA_DTE; + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devd); } /* H/C option */ +return dat; +} + +int32 dqcio (int32 inst, int32 IR, int32 dat) +{ +int32 devc, fnc, drv; + +devc = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devc); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devc) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devc) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + dqc_obuf = dat; + break; + + case ioLIX: /* load */ + dat = 0; + case ioMIX: /* merge */ + break; /* no data */ + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC? */ + clrCMD (devc); /* clr cmd, ctl */ + clrCTL (devc); /* cancel non-seek */ + if (dqc_busy) sim_cancel (&dqc_unit[dqc_busy - 1]); + sim_cancel (&dqd_unit); /* cancel dch */ + dqd_xfer = 0; /* clr dch xfer */ + dqc_busy = 0; /* clr busy */ + } + else { /* STC */ + setCTL (devc); /* set ctl */ + if (!CMD (devc)) { /* cmd clr? */ + setCMD (devc); /* set cmd, ctl */ + drv = CW_GETDRV (dqc_obuf); /* get fnc, drv */ + fnc = CW_GETFNC (dqc_obuf); /* from cmd word */ + switch (fnc) { /* case on fnc */ + case FNC_SEEK: case FNC_RCL: /* seek, recal */ + case FNC_CHK: /* check */ + dqc_sta[drv] = 0; /* clear status */ + case FNC_STA: case FNC_LA: /* rd sta, load addr */ + dq_god (fnc, drv, dqc_dtime); /* sched dch xfer */ + break; + case FNC_RD: case FNC_WD: /* read, write */ + case FNC_RA: case FNC_WA: /* rd addr, wr addr */ + case FNC_AS: /* address skip */ + dq_goc (fnc, drv, dqc_ctime); /* sched drive */ + break; + } /* end case */ + } /* end if !CMD */ + } /* end else */ + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devc); } /* H/C option */ +return dat; +} + +/* Start data channel operation */ + +void dq_god (int32 fnc, int32 drv, int32 time) +{ +dqd_unit.DRV = drv; /* save unit */ +dqd_unit.FNC = fnc; /* save function */ +sim_activate (&dqd_unit, time); +return; +} + +/* Start controller operation */ + +void dq_goc (int32 fnc, int32 drv, int32 time) +{ +int32 t; + +if (t = sim_is_active (&dqc_unit[drv])) { /* still seeking? */ + sim_cancel (&dqc_unit[drv]); /* cancel */ + time = time + t; /* include seek time */ + } +dqc_sta[drv] = 0; /* clear status */ +dq_ptr = 0; /* init buf ptr */ +dqc_busy = drv + 1; /* set busy */ +dqd_xfer = 1; /* xfer in prog */ +dqc_unit[drv].FNC = fnc; /* save function */ +sim_activate (&dqc_unit[drv], time); /* activate unit */ +return; +} + +/* Data channel unit service + + This routine handles the data channel transfers. It also handles + data transfers that are blocked by seek in progress. + + uptr->DRV = target drive + uptr->FNC = target function + + Seek substates + seek - transfer cylinder + seek1 - transfer head/surface, sched drive + Recalibrate substates + rcl - clear cyl/head/surface, sched drive + Load address + la - transfer cylinder + la1 - transfer head/surface, finish operation + Status check - transfer status, finish operation + Check data + chk - transfer sector count, sched drive +*/ + +t_stat dqd_svc (UNIT *uptr) +{ +int32 drv, devc, devd, st; + +drv = uptr->DRV; /* get drive no */ +devc = dqc_dib.devno; /* get cch devno */ +devd = dqd_dib.devno; /* get dch devno */ +switch (uptr->FNC) { /* case function */ + + case FNC_LA: /* arec, need cyl */ + case FNC_SEEK: /* seek, need cyl */ + if (CMD (devd)) { /* dch active? */ + dqc_rarc = DA_GETCYL (dqd_obuf); /* set RAR from cyl word */ + dqd_wval = 0; /* clr data valid */ + setFSR (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + if (uptr->FNC == FNC_LA) uptr->FNC = FNC_LA1; + else uptr->FNC = FNC_SEEK1; /* advance state */ + } + sim_activate (uptr, dqc_xtime); /* no, wait more */ + break; + + case FNC_LA1: /* arec, need hd/sec */ + case FNC_SEEK1: /* seek, need hd/sec */ + if (CMD (devd)) { /* dch active? */ + dqc_rarh = DA_GETHD (dqd_obuf); /* set RAR from head */ + dqc_rars = DA_GETSC (dqd_obuf); /* set RAR from sector */ + dqd_wval = 0; /* clr data valid */ + setFSR (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + if (uptr->FNC == FNC_LA1) { + setFSR (devc); /* set cch flg */ + clrCMD (devc); /* clr cch cmd */ + break; /* done if Load Address */ + } + if (sim_is_active (&dqc_unit[drv])) break; /* if busy, seek check */ + st = abs (dqc_rarc - dqc_ucyl[drv]) * dqc_stime; + if (st == 0) st = dqc_xtime; /* if on cyl, min time */ + else dqc_sta[drv] = dqc_sta[drv] | STA_BSY; /* set busy */ + dqc_ucyl[drv] = dqc_rarc; /* transfer RAR */ + dqc_uhed[drv] = dqc_rarh; + sim_activate (&dqc_unit[drv], st); /* schedule op */ + dqc_unit[drv].FNC = FNC_SEEK2; /* advance state */ + } + else sim_activate (uptr, dqc_xtime); /* no, wait more */ + break; + + case FNC_RCL: /* recalibrate */ + dqc_rarc = dqc_rarh = dqc_rars = 0; /* clear RAR */ + if (sim_is_active (&dqc_unit[drv])) break; /* ignore if busy */ + st = dqc_ucyl[drv] * dqc_stime; /* calc diff */ + if (st == 0) st = dqc_xtime; /* if on cyl, min time */ + else dqc_sta[drv] = dqc_sta[drv] | STA_BSY; /* set busy */ + sim_activate (&dqc_unit[drv], st); /* schedule drive */ + dqc_ucyl[drv] = dqc_uhed[drv] = 0; /* clear drive pos */ + dqc_unit[drv].FNC = FNC_SEEK2; /* advance state */ + break; + + case FNC_STA: /* read status */ + if (CMD (devd)) { /* dch active? */ + if ((dqc_unit[drv].flags & UNIT_UNLOAD) == 0) /* drive up? */ + dqd_ibuf = dqc_sta[drv] & ~STA_DID; + else dqd_ibuf = STA_NRDY; + if (dqd_ibuf & STA_ANYERR) /* errors? set flg */ + dqd_ibuf = dqd_ibuf | STA_ERR; + if (drv) dqd_ibuf = dqd_ibuf | STA_DID; + setFSR (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + clrCMD (devc); /* clr cch cmd */ + dqc_sta[drv] = dqc_sta[drv] & ~STA_ANYERR; /* clr sta flags */ + } + else sim_activate (uptr, dqc_xtime); /* wait more */ + break; + + case FNC_CHK: /* check, need cnt */ + if (CMD (devd)) { /* dch active? */ + dqc_cnt = dqd_obuf & DA_CKMASK; /* get count */ + dqd_wval = 0; /* clr data valid */ + dq_goc (FNC_CHK1, drv, dqc_ctime); /* sched drv */ + } + else sim_activate (uptr, dqc_xtime); /* wait more */ + break; + + default: + return SCPE_IERR; + } + +return SCPE_OK; +} + +/* Drive unit service + + This routine handles the data transfers. + + Seek substates + seek2 - done + Recalibrate substate + rcl1 - done + Check data substates + chk1 - finish operation + Read + Read address + Address skip (read without header check) + Write + Write address +*/ + +#define GETDA(x,y,z) \ + (((((x) * DQ_NUMSF) + (y)) * DQ_NUMSC) + (z)) * DQ_NUMWD + +t_stat dqc_svc (UNIT *uptr) +{ +int32 da, drv, devc, devd, err; + +err = 0; /* assume no err */ +drv = uptr - dqc_dev.units; /* get drive no */ +devc = dqc_dib.devno; /* get cch devno */ +devd = dqd_dib.devno; /* get dch devno */ +if (uptr->flags & UNIT_UNLOAD) { /* drive down? */ + setFSR (devc); /* set cch flg */ + clrCMD (devc); /* clr cch cmd */ + dqc_sta[drv] = 0; /* clr status */ + dqc_busy = 0; /* ctlr is free */ + dqd_xfer = dqd_wval = 0; + return SCPE_OK; + } +switch (uptr->FNC) { /* case function */ + + case FNC_SEEK2: /* seek done */ + if (dqc_ucyl[drv] >= DQ_NUMCY) { /* out of range? */ + dqc_sta[drv] = dqc_sta[drv] | STA_BSY | STA_ERR; /* seek check */ + dqc_ucyl[drv] = 0; /* seek to cyl 0 */ + } + else dqc_sta[drv] = dqc_sta[drv] & ~STA_BSY; /* drive not busy */ + case FNC_SEEK3: + if (dqc_busy || FLG (devc)) { /* ctrl busy? */ + uptr->FNC = FNC_SEEK3; /* next state */ + sim_activate (uptr, dqc_xtime); /* ctrl busy? wait */ + } + else { + setFSR (devc); /* set cch flg */ + clrCMD (devc); /* clr cch cmd */ + } + return SCPE_OK; + + case FNC_RA: /* read addr */ + if (!CMD (devd)) break; /* dch clr? done */ + if (dq_ptr == 0) dqd_ibuf = dqc_ucyl[drv]; /* 1st word? */ + else if (dq_ptr == 1) { /* second word? */ + dqd_ibuf = (dqc_uhed[drv] << DA_V_HD) | /* use drive head */ + (dqc_rars << DA_V_SC); /* and RAR sector */ + dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */ + } + else break; + dq_ptr = dq_ptr + 1; + setFSR (devd); /* set dch flg */ + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dqc_xtime); /* sched next word */ + return SCPE_OK; + + case FNC_AS: /* address skip */ + case FNC_RD: /* read */ + case FNC_CHK1: /* check */ + if (dq_ptr == 0) { /* new sector? */ + if (!CMD (devd) && (uptr->FNC != FNC_CHK1)) break; + if ((dqc_rarc != dqc_ucyl[drv]) || /* RAR cyl miscompare? */ + (dqc_rarh != dqc_uhed[drv]) || /* RAR head miscompare? */ + (dqc_rars >= DQ_NUMSC)) { /* bad sector? */ + dqc_sta[drv] = dqc_sta[drv] | STA_AER; /* no record found err */ + break; + } + if (dqc_rarh >= DQ_NUMSF) { /* bad head? */ + dqc_sta[drv] = dqc_sta[drv] | STA_EOC; /* end of cyl err */ + break; + } + da = GETDA (dqc_rarc, dqc_rarh, dqc_rars); /* calc disk addr */ + dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */ + if (dqc_rars == 0) /* wrap? incr head */ + dqc_uhed[drv] = dqc_rarh = dqc_rarh + 1; + if (err = fseek (uptr->fileref, da * sizeof (int16), + SEEK_SET)) break; + fxread (dqxb, sizeof (int16), DQ_NUMWD, uptr->fileref); + if (err = ferror (uptr->fileref)) break; + } + dqd_ibuf = dqxb[dq_ptr++]; /* get word */ + if (dq_ptr >= DQ_NUMWD) { /* end of sector? */ + if (uptr->FNC == FNC_CHK1) { /* check? */ + dqc_cnt = (dqc_cnt - 1) & DA_CKMASK; /* decr count */ + if (dqc_cnt == 0) break; /* if zero, done */ + } + dq_ptr = 0; /* wrap buf ptr */ + } + if (CMD (devd) && dqd_xfer) { /* dch on, xfer? */ + setFSR (devd); /* set flag */ + } + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dqc_xtime); /* sched next word */ + return SCPE_OK; + + case FNC_WA: /* write address */ + case FNC_WD: /* write */ + if (dq_ptr == 0) { /* sector start? */ + if (!CMD (devd) && !dqd_wval) break; /* xfer done? */ + if (uptr->flags & UNIT_WPRT) { /* write protect? */ + dqc_sta[drv] = dqc_sta[drv] | STA_FLG; + break; /* done */ + } + if ((dqc_rarc != dqc_ucyl[drv]) || /* RAR cyl miscompare? */ + (dqc_rarh != dqc_uhed[drv]) || /* RAR head miscompare? */ + (dqc_rars >= DQ_NUMSC)) { /* bad sector? */ + dqc_sta[drv] = dqc_sta[drv] | STA_AER; /* no record found err */ + break; + } + if (dqc_rarh >= DQ_NUMSF) { /* bad head? */ + dqc_sta[drv] = dqc_sta[drv] | STA_EOC; /* end of cyl err */ + break; + } + } + dqxb[dq_ptr++] = dqd_wval? dqd_obuf: 0; /* store word/fill */ + dqd_wval = 0; /* clr data valid */ + if (dq_ptr >= DQ_NUMWD) { /* buffer full? */ + da = GETDA (dqc_rarc, dqc_rarh, dqc_rars); /* calc disk addr */ + dqc_rars = (dqc_rars + 1) % DQ_NUMSC; /* incr sector */ + if (dqc_rars == 0) /* wrap? incr head */ + dqc_uhed[drv] = dqc_rarh = dqc_rarh + 1; + if (err = fseek (uptr->fileref, da * sizeof (int16), + SEEK_SET)) return TRUE; + fxwrite (dqxb, sizeof (int16), DQ_NUMWD, uptr->fileref); + if (err = ferror (uptr->fileref)) break; + dq_ptr = 0; + } + if (CMD (devd) && dqd_xfer) { /* dch on, xfer? */ + setFSR (devd); /* set flag */ + } + clrCMD (devd); /* clr dch cmd */ + sim_activate (uptr, dqc_xtime); /* sched next word */ + return SCPE_OK; + + default: + return SCPE_IERR; + } /* end case fnc */ + +setFSR (devc); /* set cch flg */ +clrCMD (devc); /* clr cch cmd */ +dqc_busy = 0; /* ctlr is free */ +dqd_xfer = dqd_wval = 0; +if (err != 0) { /* error? */ + perror ("DQ I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat dqc_reset (DEVICE *dptr) +{ +int32 drv; + +hp_enbdis_pair (dptr, /* make pair cons */ + (dptr == &dqd_dev)? &dqc_dev: &dqd_dev); +dqd_ibuf = dqd_obuf = 0; /* clear buffers */ +dqc_busy = dqc_obuf = 0; +dqd_xfer = dqd_wval = 0; +dq_ptr = 0; +dqc_rarc = dqc_rarh = dqc_rars = 0; /* clear RAR */ +dqc_dib.cmd = dqd_dib.cmd = 0; /* clear cmd */ +dqc_dib.ctl = dqd_dib.ctl = 0; /* clear ctl */ +dqc_dib.fbf = dqd_dib.fbf = 1; /* set fbf */ +dqc_dib.flg = dqd_dib.flg = 1; /* set flg */ +dqc_dib.srq = dqd_dib.srq = 1; /* srq follows flg */ +sim_cancel (&dqd_unit); /* cancel dch */ +for (drv = 0; drv < DQ_NUMDRV; drv++) { /* loop thru drives */ + sim_cancel (&dqc_unit[drv]); /* cancel activity */ + dqc_unit[drv].FNC = 0; /* clear function */ + dqc_ucyl[drv] = dqc_uhed[drv] = 0; /* clear drive pos */ + dqc_sta[drv] = 0; /* clear status */ + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat dqc_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = attach_unit (uptr, cptr); /* attach unit */ +if (r == SCPE_OK) dqc_load_unload (uptr, 0, NULL, NULL);/* if OK, load heads */ +return r; +} + +/* Detach routine */ + +t_stat dqc_detach (UNIT* uptr) +{ +dqc_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads */ +return detach_unit (uptr); /* detach unit */ +} + +/* Load and unload heads */ + +t_stat dqc_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to load */ +if (value == UNIT_UNLOAD) /* unload heads? */ + uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */ +else uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */ +return SCPE_OK; +} + +/* 7900/7901/2883/2884 bootstrap routine (HP 12992A ROM) */ + +const uint16 dq_rom[IBL_LNT] = { + 0102501, /*ST LIA 1 ; get switches */ + 0106501, /* LIB 1 */ + 0013765, /* AND D7 ; isolate hd */ + 0005750, /* BLF,CLE,SLB */ + 0027741, /* JMP RD */ + 0005335, /* RBR,SLB,ERB ; <13>->E, set = 2883 */ + 0027717, /* JMP IS */ + 0102611, /*LP OTA CC ; do 7900 status to */ + 0103711, /* STC CC,C ; clear first seek */ + 0102310, /* SFS DC */ + 0027711, /* JMP *-1 */ + 0002004, /* INA ; get next drive */ + 0053765, /* CPA D7 ; all cleared? */ + 0002001, /* RSS */ + 0027707, /* JMP LP */ + 0067761, /*IS LDB SEEKC ; get seek comnd */ + 0106610, /* OTB DC ; issue cyl addr (0) */ + 0103710, /* STC DC,C ; to dch */ + 0106611, /* OTB CC ; seek cmd */ + 0103711, /* STC CC,C ; to cch */ + 0102310, /* SFS DC ; addr wd ok? */ + 0027724, /* JMP *-1 ; no, wait */ + 0006400, /* CLB */ + 0102501, /* LIA 1 ; get switches */ + 0002051, /* SEZ,SLA,RSS ; subchan = 1 or ISS */ + 0047770, /* ADB BIT9 ; head 2 */ + 0106610, /* OTB DC ; head/sector */ + 0103710, /* STC DC,C ; to dch */ + 0102311, /* SFS CC ; seek done? */ + 0027734, /* JMP *-1 ; no, wait */ + 0063731, /* LDA ISSRD ; get read read */ + 0002341, /* SEZ,CCE,RSS ; iss disc? */ + 0001100, /* ARS ; no, make 7900 read */ + 0067776, /*RD LDB DMACW ; DMA control */ + 0106606, /* OTB 6 */ + 0067762, /* LDB ADDR1 ; memory addr */ + 0077741, /* STB RD ; make non re-executable */ + 0106602, /* OTB 2 */ + 0102702, /* STC 2 ; flip DMA ctrl */ + 0067764, /* LDB COUNT ; word count */ + 0106602, /* OTB 2 */ + 0002041, /* SEZ,RSS */ + 0027766, /* JMP NW */ + 0102611, /* OTA CC ; to cch */ + 0103710, /* STC DC,C ; start dch */ + 0103706, /* STC 6,C ; start DMA */ + 0103711, /* STC CC,C ; start cch */ + 0037773, /* ISZ SK */ + 0027773, /* JMP SK */ + 0030000, /*SEEKC 030000 */ + 0102011, /*ADDR1 102011 */ + 0102055, /*ADDR2 102055 */ + 0164000, /*COUNT -6144. */ + 0000007, /*D7 7 */ + 0106710, /*NW CLC DC ; set 'next wd is cmd' flag */ + 0001720, /* ALF,ALF ; move to head number loc */ + 0001000, /*BIT9 ALS */ + 0103610, /* OTA DC,C ; output cold load cmd */ + 0103706, /* STC 6,C ; start DMA */ + 0102310, /* SFS DC ; done? */ + 0027773, /* JMP *-1 ; no, wait */ + 0117763, /*XT JSB ADDR2,I ; start program */ + 0120010, /*DMACW 120000+DC */ + 0000000 /* -ST */ + }; + +t_stat dqc_boot (int32 unitno, DEVICE *dptr) +{ +int32 dev; + +if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ +dev = dqd_dib.devno; /* get data chan dev */ +if (ibl_copy (dq_rom, dev)) return SCPE_IERR; /* copy boot to memory */ +SR = (SR & IBL_OPT) | IBL_DQ | (dev << IBL_V_DEV); /* set SR */ +return SCPE_OK; +} diff --git a/HP2100/hp2100_dr.c b/HP2100/hp2100_dr.c new file mode 100644 index 0000000..2796e8c --- /dev/null +++ b/HP2100/hp2100_dr.c @@ -0,0 +1,646 @@ +/* hp2100_dr.c: HP 2100 12606B/12610B fixed head disk/drum simulator + + Copyright (c) 1993-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dr 12606B 2770/2771 fixed head disk + 12610B 2773/2774/2775 drum + + 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified) + 07-Oct-04 JDB Fixed enable/disable from either device + Fixed sector return in status word + Provided protected tracks and "Writing Enabled" status bit + Fixed DMA last word write, incomplete sector fill value + Added "parity error" status return on writes for 12606 + Added track origin test for 12606 + Added SCP test for 12606 + Fixed 12610 SFC operation + Added "Sector Flag" status bit + Added "Read Inhibit" status bit for 12606 + Fixed current-sector determination + Added PROTECTED, UNPROTECTED, TRACKPROT modifiers + 26-Aug-04 RMS Fixed CLC to stop operation (from Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Revised boot rom to use IBL algorithm + Implemented DMA SRQ (follows FLG) + 27-Jul-03 RMS Fixed drum sizes + Fixed variable capacity interaction with SAVE/RESTORE + 10-Nov-02 RMS Added BOOT command + + These head-per-track devices are buffered in memory, to minimize overhead. + + The drum data channel does not have a command flip-flop. Its control + flip-flop is not wired into the interrupt chain; accordingly, the + simulator uses command rather than control for the data channel. Its + flag does not respond to SFS, SFC, or STF. + + The drum control channel does not have any of the traditional flip-flops. + + The 12606 interface implements two diagnostic tests. An SFS CC instruction + will skip if the disk has passed the track origin (sector 0) since the last + CLF CC instruction, and an SFC CC instruction will skip if the Sector Clock + Phase (SCP) flip-flop is clear, indicating that the current sector is + accessible. The 12610 interface does not support these tests; the SKF signal + is not driven, so neither SFC CC nor SFS CC will skip. + + The interface implements a track protect mechanism via a switch and a set of + on-card diodes. The switch sets the protected/unprotected status, and the + particular diodes installed indicate the range of tracks (a power of 2) that + are read-only in the protected mode. + + Somewhat unusually, writing to a protected track completes normally, but the + data isn't actually written, as the write current is inhibited. There is no + "failure" status indication. Instead, a program must note the lack of + "Writing Enabled" status before the write is attempted. + + Specifications (2770/2771): + - 90 sectors per logical track + - 45 sectors per revolution + - 64 words per sector + - 2880 words per revolution + - 3450 RPM = 17.4 ms/revolution + - data timing = 6.0 us/word, 375 us/sector + - inst timing = 4 inst/word, 11520 inst/revolution + + Specifications 2773/2774/2775: + - 32 sectors per logical track + - 32 sectors per revolution + - 64 words per sector + - 2048 words per revolution + - 3450 RPM = 17.4 ms/revolution + - data timing = 8.5 us/word, 550 us/sector + - inst timing = 6 inst/word, 12288 inst/revolution + + References: + - 12606B Disc Memory Interface Kit Operating and Service Manual + (12606-90012, Mar-1970) + - 12610B Drum Memory Interface Kit Operating and Service Manual + (12610-9001, Feb-1970) +*/ + +#include "hp2100_defs.h" +#include + +/* Constants */ + +#define DR_NUMWD 64 /* words/sector */ +#define DR_FNUMSC 90 /* fhd sec/track */ +#define DR_DNUMSC 32 /* drum sec/track */ +#define DR_NUMSC ((drc_unit.flags & UNIT_DR)? DR_DNUMSC: DR_FNUMSC) +#define DR_SIZE (512 * DR_DNUMSC * DR_NUMWD) /* initial size */ +#define DR_FTIME 4 /* fhd per-word time */ +#define DR_DTIME 6 /* drum per-word time */ +#define DR_OVRHEAD 5 /* overhead words at track start */ +#define UNIT_V_PROT (UNIT_V_UF + 0) /* track protect */ +#define UNIT_V_SZ (UNIT_V_UF + 1) /* disk vs drum */ +#define UNIT_M_SZ 017 /* size */ +#define UNIT_PROT (1 << UNIT_V_PROT) +#define UNIT_SZ (UNIT_M_SZ << UNIT_V_SZ) +#define UNIT_DR (1 << UNIT_V_SZ) /* low order bit */ +#define SZ_180K 000 /* disks */ +#define SZ_360K 002 +#define SZ_720K 004 +#define SZ_1024K 001 /* drums: default size */ +#define SZ_1536K 003 +#define SZ_384K 005 +#define SZ_512K 007 +#define SZ_640K 011 +#define SZ_768K 013 +#define SZ_896K 015 +#define DR_GETSZ(x) (((x) >> UNIT_V_SZ) & UNIT_M_SZ) + +/* Command word */ + +#define CW_WR 0100000 /* write vs read */ +#define CW_V_FTRK 7 /* fhd track */ +#define CW_M_FTRK 0177 +#define CW_V_DTRK 5 /* drum track */ +#define CW_M_DTRK 01777 +#define MAX_TRK (((drc_unit.flags & UNIT_DR)? CW_M_DTRK: CW_M_FTRK) + 1) +#define CW_GETTRK(x) ((drc_unit.flags & UNIT_DR)? \ + (((x) >> CW_V_DTRK) & CW_M_DTRK): \ + (((x) >> CW_V_FTRK) & CW_M_FTRK)) +#define CW_PUTTRK(x) ((drc_unit.flags & UNIT_DR)? \ + (((x) & CW_M_DTRK) << CW_V_DTRK): \ + (((x) & CW_M_FTRK) << CW_V_FTRK)) +#define CW_V_FSEC 0 /* fhd sector */ +#define CW_M_FSEC 0177 +#define CW_V_DSEC 0 /* drum sector */ +#define CW_M_DSEC 037 +#define CW_GETSEC(x) ((drc_unit.flags & UNIT_DR)? \ + (((x) >> CW_V_DSEC) & CW_M_DSEC): \ + (((x) >> CW_V_FSEC) & CW_M_FSEC)) +#define CW_PUTSEC(x) ((drc_unit.flags & UNIT_DR)? \ + (((x) & CW_M_DSEC) << CW_V_DSEC): \ + (((x) & CW_M_FSEC) << CW_V_FSEC)) + +/* Status register, ^ = dynamic */ + +#define DRS_V_NS 8 /* ^next sector */ +#define DRS_M_NS 0177 +#define DRS_SEC 0100000 /* ^sector flag */ +#define DRS_RDY 0000200 /* ^ready */ +#define DRS_RIF 0000100 /* ^read inhibit */ +#define DRS_SAC 0000040 /* sector coincidence */ +#define DRS_ABO 0000010 /* abort */ +#define DRS_WEN 0000004 /* ^write enabled */ +#define DRS_PER 0000002 /* parity error */ +#define DRS_BSY 0000001 /* ^busy */ + +#define CALC_SCP(x) (((int32) fmod ((x) / (double) dr_time, \ + (double) (DR_NUMWD))) >= (DR_NUMWD - 3)) + +extern UNIT cpu_unit; +extern uint16 *M; +extern uint32 PC; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; + +int32 drc_cw = 0; /* fnc, addr */ +int32 drc_sta = 0; /* status */ +int32 drc_run = 0; /* run flip-flop */ +int32 drd_ibuf = 0; /* input buffer */ +int32 drd_obuf = 0; /* output buffer */ +int32 drd_ptr = 0; /* sector pointer */ +int32 drc_pcount = 1; /* number of prot tracks */ +int32 dr_stopioe = 1; /* stop on error */ +int32 dr_time = DR_DTIME; /* time per word */ + +static int32 sz_tab[16] = { + 184320, 1048576, 368640, 1572864, 737280, 393216, 0, 524288, + 0, 655360, 0, 786432, 0, 917504, 0, 0 }; + +DEVICE drd_dev, drc_dev; +int32 drdio (int32 inst, int32 IR, int32 dat); +int32 drcio (int32 inst, int32 IR, int32 dat); +t_stat drc_svc (UNIT *uptr); +t_stat drc_reset (DEVICE *dptr); +t_stat drc_attach (UNIT *uptr, char *cptr); +t_stat drc_boot (int32 unitno, DEVICE *dptr); +int32 dr_incda (int32 trk, int32 sec, int32 ptr); +int32 dr_seccntr (double simtime); +t_stat dr_set_prot (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dr_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* DRD data structures + + drd_dev device descriptor + drd_unit unit descriptor + drd_reg register list +*/ + +DIB dr_dib[] = { + { DRD, 0, 0, 0, 0, 0, &drdio }, + { DRC, 0, 0, 0, 0, 0, &drcio } + }; + +#define drd_dib dr_dib[0] +#define drc_dib dr_dib[1] + +UNIT drd_unit[] = { + { UDATA (NULL, 0, 0) }, + { UDATA (NULL, UNIT_DIS, 0) } + }; + +#define TMR_ORG 0 /* origin timer */ +#define TMR_INH 1 /* inhibit timer */ + +REG drd_reg[] = { + { ORDATA (IBUF, drd_ibuf, 16) }, + { ORDATA (OBUF, drd_obuf, 16) }, + { FLDATA (CMD, drd_dib.cmd, 0) }, + { FLDATA (CTL, drd_dib.ctl, 0) }, + { FLDATA (FLG, drd_dib.flg, 0) }, + { FLDATA (FBF, drd_dib.fbf, 0) }, + { FLDATA (SRQ, drd_dib.srq, 0) }, + { ORDATA (BPTR, drd_ptr, 6) }, + { ORDATA (DEVNO, drd_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB drd_mod[] = { + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &drd_dev }, + { 0 } + }; + +DEVICE drd_dev = { + "DRD", drd_unit, drd_reg, drd_mod, + 2, 0, 0, 0, 0, 0, + NULL, NULL, &drc_reset, + NULL, NULL, NULL, + &drd_dib, DEV_DISABLE + }; + +/* DRC data structures + + drc_dev device descriptor + drc_unit unit descriptor + drc_mod unit modifiers + drc_reg register list +*/ + +UNIT drc_unit = { + UDATA (&drc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DR+UNIT_BINK, DR_SIZE) + }; + +REG drc_reg[] = { + { DRDATA (PCNT, drc_pcount, 10), REG_HIDDEN | PV_LEFT }, + { ORDATA (CW, drc_cw, 16) }, + { ORDATA (STA, drc_sta, 16) }, + { FLDATA (RUN, drc_run, 0) }, + { FLDATA (CMD, drc_dib.cmd, 0) }, + { FLDATA (CTL, drc_dib.ctl, 0) }, + { FLDATA (FLG, drc_dib.flg, 0) }, + { FLDATA (FBF, drc_dib.fbf, 0) }, + { FLDATA (SRQ, drc_dib.srq, 0) }, + { DRDATA (TIME, dr_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, dr_stopioe, 0) }, + { ORDATA (DEVNO, drc_dib.devno, 6), REG_HRO }, + { DRDATA (CAPAC, drc_unit.capac, 24), REG_HRO }, + { NULL } + }; + +MTAB drc_mod[] = { + { UNIT_DR, 0, "disk", NULL, NULL }, + { UNIT_DR, UNIT_DR, "drum", NULL, NULL }, + { UNIT_SZ, (SZ_180K << UNIT_V_SZ), NULL, "180K", &dr_set_size }, + { UNIT_SZ, (SZ_360K << UNIT_V_SZ), NULL, "360K", &dr_set_size }, + { UNIT_SZ, (SZ_720K << UNIT_V_SZ), NULL, "720K", &dr_set_size }, + { UNIT_SZ, (SZ_384K << UNIT_V_SZ), NULL, "384K", &dr_set_size }, + { UNIT_SZ, (SZ_512K << UNIT_V_SZ), NULL, "512K", &dr_set_size }, + { UNIT_SZ, (SZ_640K << UNIT_V_SZ), NULL, "640K", &dr_set_size }, + { UNIT_SZ, (SZ_768K << UNIT_V_SZ), NULL, "768K", &dr_set_size }, + { UNIT_SZ, (SZ_896K << UNIT_V_SZ), NULL, "896K", &dr_set_size }, + { UNIT_SZ, (SZ_1024K << UNIT_V_SZ), NULL, "1024K", &dr_set_size }, + { UNIT_SZ, (SZ_1536K << UNIT_V_SZ), NULL, "1536K", &dr_set_size }, + { UNIT_PROT, UNIT_PROT, "protected", "PROTECTED", NULL }, + { UNIT_PROT, 0, "unprotected", "UNPROTECTED", NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "tracks protected", "TRACKPROT", + &dr_set_prot, NULL, &drc_reg[0] }, + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &drd_dev }, + { 0 } + }; + +DEVICE drc_dev = { + "DRC", &drc_unit, drc_reg, drc_mod, + 1, 8, 21, 1, 8, 16, + NULL, NULL, &drc_reset, + &drc_boot, &drc_attach, NULL, + &drc_dib, DEV_DISABLE + }; + +/* IO instructions */ + +int32 drdio (int32 inst, int32 IR, int32 dat) +{ +int32 devd, t; + +devd = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioOTX: /* output */ + drd_obuf = dat; + break; + + case ioMIX: /* merge */ + dat = dat | drd_ibuf; + break; + + case ioLIX: /* load */ + dat = drd_ibuf; + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_AB) { /* CLC */ + clrCMD (devd); /* clr "ctl" */ + clrFSR (devd); /* clr flg */ + if (!drc_run) sim_cancel (&drc_unit); /* cancel curr op */ + drc_sta = drc_sta & ~DRS_SAC; /* clear SAC flag */ + } + else if (!CMD (devd)) { /* STC, not set? */ + setCMD (devd); /* set "ctl" */ + if (drc_cw & CW_WR) { setFSR (devd); } /* prime DMA */ + drc_sta = 0; /* clr status */ + drd_ptr = 0; /* clear sec ptr */ + sim_cancel (&drc_unit); /* cancel curr op */ + t = CW_GETSEC (drc_cw) - dr_seccntr (sim_gtime()); + if (t <= 0) t = t + DR_NUMSC; + sim_activate (&drc_unit, t * DR_NUMWD * dr_time); + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devd); } /* H/C option */ +return dat; +} + +int32 drcio (int32 inst, int32 IR, int32 dat) +{ +int32 sec; + +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) && !(drc_unit.flags & UNIT_DR)) { /* CLF disk */ + sec = dr_seccntr (sim_gtime ()); /* current sector */ + sim_cancel (&drd_unit[TMR_ORG]); /* sched origin tmr */ + sim_activate (&drd_unit[TMR_ORG], + (DR_FNUMSC - sec) * DR_NUMWD * dr_time); + } + break; + + case ioSFC: /* skip flag clear */ + if (drc_unit.flags & UNIT_DR) break; /* 12610 never skips */ + if (!(CALC_SCP (sim_gtime()))) /* nearing end of sector? */ + PC = (PC + 1) & VAMASK; /* skip if SCP clear */ + break; + + case ioSFS: /* skip flag set */ + if (drc_unit.flags & UNIT_DR) break; /* 12610 never skips */ + if (!sim_is_active (&drd_unit[TMR_ORG])) /* passed origin? */ + PC = (PC + 1) & VAMASK; /* skip if origin seen */ + break; + + case ioOTX: /* output */ + if (!(drc_unit.flags & UNIT_DR)) { /* disk? */ + sim_cancel (&drd_unit[TMR_INH]); /* schedule inhibit timer */ + sim_activate (&drd_unit[TMR_INH], DR_FTIME * DR_NUMWD); + } + drc_cw = dat; /* get control word */ + break; + + case ioLIX: /* load */ + dat = 0; + case ioMIX: /* merge */ + dat = dat | drc_sta; /* static bits */ + if (!(drc_unit.flags & UNIT_PROT) || /* not protected? */ + (CW_GETTRK(drc_cw) >= drc_pcount)) /* or not in range? */ + dat = dat | DRS_WEN; /* set wrt enb status */ + if (drc_unit.flags & UNIT_ATT) { /* attached? */ + dat = dat | (dr_seccntr (sim_gtime()) << DRS_V_NS) | DRS_RDY; + if (sim_is_active (&drc_unit)) /* op in progress? */ + dat = dat | DRS_BSY; + if (CALC_SCP (sim_gtime())) /* SCP ff set? */ + dat = dat | DRS_SEC; /* set sector flag */ + if (sim_is_active (&drd_unit[TMR_INH]) && /* inhibit timer on? */ + !(drc_cw & CW_WR)) + dat = dat | DRS_RIF; /* set read inh flag */ + } + break; + + default: + break; + } + +return dat; +} + +/* Unit service */ + +t_stat drc_svc (UNIT *uptr) +{ +int32 devd, trk, sec; +uint32 da; +uint16 *bptr = (uint16 *) uptr->filebuf; + +if ((uptr->flags & UNIT_ATT) == 0) { + drc_sta = DRS_ABO; + return IORETURN (dr_stopioe, SCPE_UNATT); + } + +devd = drd_dib.devno; /* get dch devno */ +trk = CW_GETTRK (drc_cw); +sec = CW_GETSEC (drc_cw); +da = ((trk * DR_NUMSC) + sec) * DR_NUMWD; +drc_sta = drc_sta | DRS_SAC; +drc_run = 1; /* set run ff */ + +if (drc_cw & CW_WR) { /* write? */ + if ((da < uptr->capac) && (sec < DR_NUMSC)) { + bptr[da + drd_ptr] = drd_obuf; + if (((uint32) (da + drd_ptr)) >= uptr->hwmark) + uptr->hwmark = da + drd_ptr + 1; + } + drd_ptr = dr_incda (trk, sec, drd_ptr); /* inc disk addr */ + if (CMD (devd)) { /* dch active? */ + setFSR (devd); /* set dch flg */ + sim_activate (uptr, dr_time); /* sched next word */ + } + else { /* done */ + if (drd_ptr) /* need to fill? */ + for ( ; drd_ptr < DR_NUMWD; drd_ptr++) + bptr[da + drd_ptr] = drd_obuf; /* fill with last word */ + if (!(drc_unit.flags & UNIT_DR)) /* disk? */ + drc_sta = drc_sta | DRS_PER; /* parity bit sets on write */ + drc_run = 0; /* clear run ff */ + } + } /* end write */ +else { /* read */ + if (CMD (devd)) { /* dch active? */ + if ((da >= uptr->capac) || (sec >= DR_NUMSC)) drd_ibuf = 0; + else drd_ibuf = bptr[da + drd_ptr]; + drd_ptr = dr_incda (trk, sec, drd_ptr); + setFSR (devd); /* set dch flg */ + sim_activate (uptr, dr_time); /* sched next word */ + } + else drc_run = 0; /* clear run ff */ + } +return SCPE_OK; +} + +/* Increment current disk address */ + +int32 dr_incda (int32 trk, int32 sec, int32 ptr) +{ +ptr = ptr + 1; /* inc pointer */ +if (ptr >= DR_NUMWD) { /* end sector? */ + ptr = 0; /* new sector */ + sec = sec + 1; /* adv sector */ + if (sec >= DR_NUMSC) { /* end track? */ + sec = 0; /* new track */ + trk = trk + 1; /* adv track */ + if (trk >= MAX_TRK) trk = 0; /* wraps at max */ + } + drc_cw = (drc_cw & CW_WR) | CW_PUTTRK (trk) | CW_PUTSEC (sec); + } +return ptr; +} + +/* Read the sector counter + + The hardware sector counter contains the number of the next sector that will + pass under the heads (so it is one ahead of the current sector). For the + duration of the last sector of the track, the sector counter contains 90 for + the 12606 and 0 for the 12610. The sector counter resets to 0 at track + origin and increments at the start of the first sector. Therefore, the + counter value ranges from 0-90 for the 12606 and 0-31 for the 12610. The 0 + state is quite short in the 12606 and long in the 12610, relative to the + other sector counter states. + + The simulated sector counter is calculated from the simulation time, based on + the time per word and the number of words per track. +*/ + +int32 dr_seccntr (double simtime) +{ +int32 curword; + +curword = (int32) fmod (simtime / (double) dr_time, + (double) (DR_NUMWD * DR_NUMSC + DR_OVRHEAD)); +if (curword <= DR_OVRHEAD) return 0; +else return ((curword - DR_OVRHEAD) / DR_NUMWD + + ((drc_unit.flags & UNIT_DR)? 0: 1)); +} + +/* Reset routine */ + +t_stat drc_reset (DEVICE *dptr) +{ +hp_enbdis_pair (dptr, /* make pair cons */ + (dptr == &drd_dev)? &drc_dev: &drd_dev); +drc_sta = drc_cw = drd_ptr = 0; +drc_dib.cmd = drd_dib.cmd = 0; /* clear cmd */ +drc_dib.ctl = drd_dib.ctl = 0; /* clear ctl */ +drc_dib.fbf = drd_dib.fbf = 0; /* clear fbf */ +drc_dib.flg = drd_dib.flg = 0; /* clear flg */ +drc_dib.srq = drd_dib.srq = 0; /* srq follows flg */ +sim_cancel (&drc_unit); +sim_cancel (&drd_unit[TMR_ORG]); +sim_cancel (&drd_unit[TMR_INH]); +return SCPE_OK; +} + +/* Attach routine */ + +t_stat drc_attach (UNIT *uptr, char *cptr) +{ +int32 sz = sz_tab[DR_GETSZ (uptr->flags)]; + +if (sz == 0) return SCPE_IERR; +uptr->capac = sz; +return attach_unit (uptr, cptr); +} + +/* Set protected track count */ + +t_stat dr_set_prot (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 count; +t_stat status; + +if (cptr == NULL) return SCPE_ARG; +count = (int32) get_uint (cptr, 10, 768, &status); +if (status != SCPE_OK) return status; +else switch (count) { + case 1: + case 2: + case 4: + case 8: + case 16: + case 32: + case 64: + case 128: + drc_pcount = count; + break; + case 256: + case 512: + case 768: + if (drc_unit.flags & UNIT_DR) drc_pcount = count; + else return SCPE_ARG; + break; + default: + return SCPE_ARG; + } +return SCPE_OK; +} + +/* Set size routine */ + +t_stat dr_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 sz; +int32 szindex; + +if (val < 0) return SCPE_IERR; +if ((sz = sz_tab[szindex = DR_GETSZ (val)]) == 0) return SCPE_IERR; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = sz; +if (szindex & UNIT_DR) dr_time = DR_DTIME; /* drum */ +else { + dr_time = DR_FTIME; /* disk */ + if (drc_pcount > 128) drc_pcount = 128; /* max prot track count */ + } +return SCPE_OK; +} + +/* Fixed head disk/drum bootstrap routine (disc subset of disc/paper tape loader) */ + +#define BOOT_BASE 056 +#define BOOT_START 060 + +static const uint16 dr_rom[IBL_LNT - BOOT_BASE] = { + 0020010, /*DMA 20000+DC */ + 0000000, /* 0 */ + 0107700, /* CLC 0,C */ + 0063756, /* LDA DMA ; DMA ctrl */ + 0102606, /* OTA 6 */ + 0002700, /* CLA,CCE */ + 0102611, /* OTA CC ; trk = sec = 0 */ + 0001500, /* ERA ; A = 100000 */ + 0102602, /* OTA 2 ; DMA in, addr */ + 0063777, /* LDA M64 */ + 0102702, /* STC 2 */ + 0102602, /* OTA 2 ; DMA wc = -64 */ + 0103706, /* STC 6,C ; start DMA */ + 0067776, /* LDB JSF ; get JMP . */ + 0074077, /* STB 77 ; in base page */ + 0102710, /* STC DC ; start disc */ + 0024077, /*JSF JMP 77 ; go wait */ + 0177700 /*M64 -100 */ + }; + +t_stat drc_boot (int32 unitno, DEVICE *dptr) +{ +int32 i, dev, ad; +uint16 wd; + +if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ +dev = drd_dib.devno; /* get data chan dev */ +ad = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */ +for (i = BOOT_BASE; i < IBL_LNT; i++) { /* copy bootstrap */ + wd = dr_rom[i - BOOT_BASE]; /* get word */ + if (((wd & I_NMRMASK) == I_IO) && /* IO instruction? */ + ((wd & I_DEVMASK) >= 010) && /* dev >= 10? */ + (I_GETIOOP (wd) != ioHLT)) /* not a HALT? */ + M[ad + i] = (wd + (dev - 010)) & DMASK; + else M[ad + i] = wd; + } +PC = ad + BOOT_START; +return SCPE_OK; +} diff --git a/HP2100/hp2100_ds.c b/HP2100/hp2100_ds.c new file mode 100644 index 0000000..08d3814 --- /dev/null +++ b/HP2100/hp2100_ds.c @@ -0,0 +1,1599 @@ +/* hp2100_ds.c: HP 2100 13037 disk controller simulator + + Copyright (c) 2004-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ds 13037 disk controller + + 31-Dec-07 JDB Corrected and verified ioCRS action + 20-Dec-07 JDB Corrected DPTR register definition from FLDATA to DRDATA + 28-Dec-06 JDB Added ioCRS state to I/O decoders + 03-Aug-06 JDB Fixed REQUEST STATUS command to clear status-1 + Removed redundant attached test in "ds_detach" + 18-Mar-05 RMS Added attached test to detach routine + 01-Mar-05 JDB Added SET UNLOAD/LOAD + + States of the controller: the controller uP runs all the time, but most of + the time it is waiting for an event. The simulator only 'runs' the controller + when there's an event to process: change in CPU interface state, change in + disk state, or timeout. The controller has three states: + + - Idle. No operations other than seek or recalibrate are in progress, and + the CPU interface is disconnected. The controller responds both to new + commands and to drive attention interrupts. + - Wait. No operations other than seek or recalibrate are in progress, but + the CPU interface is connected. The controller responds to new commands + but not to drive attention interrupts. + - Busy. The controller is processing a command. The controller does not + respond to new commands or to drive attention interrupts. + + The controller busy state is loosely related to the testable (visible) busy + flop. If the visible busy flop is set, the controller is in the busy state; + but the controller can also be busy (processing an invalid opcode or invalid + unit) while visible busy is clear. + + Omissions: the following features are not implemented: + + - Drive hold. Since this is a single CPU implementation, the drives are + always available to the CPU. + - Spare, defective, protected. The disk files carry only data. + - Formatting. The disk files carry only data. + - ECC. Data errors are always uncorrectable. + + Reference: + - 13037 Disc Controller Technical Information Package (13037-90902, Aug-1980) +*/ + +#include "hp2100_defs.h" +#include + +#define DS_NUMDR 8 /* max drives */ +#define DS_DRMASK (DS_NUMDR - 1) +#define DS_NUMWD 128 /* data words/sec */ +#define DS_NUMWDF 138 /* total words/sec */ +#define DS_FSYNC 0 /* sector offsets */ +#define DS_FCYL 1 +#define DS_FHS 2 +#define DS_FDATA 3 +#define DS_FIFO_SIZE 16 /* fifo size */ +#define DS_FIFO_EMPTY (ds_fifo_cnt == 0) +#define ds_ctrl ds_unit[DS_NUMDR] /* ctrl thread */ +#define ds_timer ds_unit[DS_NUMDR + 1] /* timeout thread */ +#define GET_CURSEC(x,d) ((int32) fmod (sim_gtime() / ((double) (x)), \ + ((double) (drv_tab[d].sc)))) + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_UNLOAD (UNIT_V_UF + 1) /* heads unloaded */ +#define UNIT_V_DTYPE (UNIT_V_UF + 2) /* disk type */ +#define UNIT_M_DTYPE 3 +#define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ +#define UNIT_V_FMT (UNIT_V_UF + 5) /* format enabled */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_FMT (1 << UNIT_V_FMT) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_UNLOAD (1 << UNIT_V_UNLOAD) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define UNIT_WPR (UNIT_WLK | UNIT_RO) /* write prot */ + +/* Parameters in the unit descriptor */ + +#define FNC u3 /* function */ +#define CYL u4 /* current cylinder */ +#define STA u5 /* status */ + +/* Arguments to subroutines */ + +#define CLR_BUSY 0 /* clear visible busy */ +#define SET_BUSY 1 /* set visible busy */ + +/* Command word - <12:8> are opcode, <7:0> are opcode dependent + + cold load read <7:6> = head + <5:0> = sector + set file mask <7:4> = retry count + <3:0> = file mask (auto-seek options) + commands with units <7> = hold flag + <4:0> = unit number */ + +#define DSC_V_OP 8 /* opcode */ +#define DSC_M_OP 037 +#define DSC_COLD 000 /* cold load read */ +#define DSC_RECAL 001 /* recalibrate */ +#define DSC_SEEK 002 /* seek */ +#define DSC_RSTA 003 /* request status */ +#define DSC_RSA 004 /* request sector addr */ +#define DSC_READ 005 /* read */ +#define DSC_RFULL 006 /* read full */ +#define DSC_VFY 007 /* verify */ +#define DSC_WRITE 010 /* write */ +#define DSC_WFULL 011 /* write full */ +#define DSC_CLEAR 012 /* clear */ +#define DSC_INIT 013 /* initialize */ +#define DSC_AREC 014 /* address record */ +#define DSC_RSYN 015 /* request syndrome */ +#define DSC_ROFF 016 /* read with offset */ +#define DSC_SFM 017 /* set file mask */ +#define DSC_RNOVFY 022 /* read no verify */ +#define DSC_WTIO 023 /* write TIO */ +#define DSC_RDA 024 /* request disk addr */ +#define DSC_END 025 /* end */ +#define DSC_WAKE 026 /* wakeup */ +#define DSC_ATN 035 /* pseudo: ATN */ +#define DSC_BADU 036 /* pseudo: bad unit */ +#define DSC_BADF 037 /* pseudo: bad opcode */ +#define DSC_NEXT 0040 /* state increment */ +#define DSC_2ND 0040 /* subcommand states */ +#define DSC_3RD 0100 +#define DSC_4TH 0140 +#define DSC_V_CHD 6 /* cold load head */ +#define DSC_M_CHD 03 +#define DSC_V_CSC 0 /* cold load sector */ +#define DSC_M_CSC 077 +#define DSC_V_RTY 4 /* retry count */ +#define DSC_M_RTY 017 +#define DSC_V_DECR 3 /* seek decrement */ +#define DSC_V_SPEN 2 /* enable sparing */ +#define DSC_V_CYLM 1 /* cylinder mode */ +#define DSC_V_AUTO 0 /* auto seek */ +#define DSC_V_HOLD 7 /* hold flag */ +#define DSC_V_UNIT 0 /* unit */ +#define DSC_M_UNIT 017 +#define DSC_V_SPAR 15 /* INIT spare */ +#define DSC_V_PROT 14 /* INIT protected */ +#define DSC_V_DFCT 13 /* INIT defective */ + +#define DSC_HOLD (1u << DSC_V_HOLD) +#define DSC_DECR (1u << DSC_V_DECR) +#define DSC_SPEN (1u << DSC_V_SPEN) +#define DSC_CYLM (1u << DSC_V_CYLM) +#define DSC_AUTO (1u << DSC_V_AUTO) +#define DSC_FMASK ((DSC_M_RTY << DSC_V_RTY)|DSC_DECR|\ + DSC_SPEN|DSC_CYLM|DSC_AUTO) +#define DSC_GETOP(x) (((x) >> DSC_V_OP) & DSC_M_OP) +#define DSC_GETUNIT(x) (((x) >> DSC_V_UNIT) & DSC_M_UNIT) +#define DSC_GETCHD(x) (((x) >> DSC_V_CHD) & DSC_M_CHD) +#define DSC_GETCSC(x) (((x) >> DSC_V_CSC) & DSC_M_CSC) +#define DSC_SPAR (1u << DSC_V_SPAR) +#define DSC_PROT (1u << DSC_V_PROT) +#define DSC_DFCT (1u << DSC_V_DFCT) + +/* Command flags */ + +#define CMF_UNDF 001 /* undefined */ +#define CMF_CLREC 002 /* clear eoc flag */ +#define CMF_CLRS 004 /* clear status */ +#define CMF_UIDLE 010 /* requires unit no */ + +/* Cylinder words - 16b */ + +/* Head/sector word */ + +#define DSHS_V_HD 8 /* head */ +#define DSHS_M_HD 037 +#define DSHS_V_SC 0 /* sector */ +#define DSHS_M_SC 0377 +#define DSHS_HD (DSHS_M_HD << DSHS_V_HD) +#define DSHS_SC (DSHS_M_SC << DSHS_V_SC) +#define DSHS_GETHD(x) (((x) >> DSHS_V_HD) & DSHS_M_HD) +#define DSHS_GETSC(x) (((x) >> DSHS_V_SC) & DSHS_M_SC) + +/* Status 1 */ + +#define DS1_V_SPAR 15 /* spare - na */ +#define DS1_V_PROT 14 /* protected - na */ +#define DS1_V_DFCT 13 /* defective - na */ +#define DS1_V_STAT 8 /* status */ +#define DS1_OK (000 << DS1_V_STAT) /* normal */ +#define DS1_ILLOP (001 << DS1_V_STAT) /* illegal opcode */ +#define DS1_AVAIL (002 << DS1_V_STAT) /* available */ +#define DS1_CYLCE (007 << DS1_V_STAT) /* cyl compare err */ +#define DS1_UNCOR (010 << DS1_V_STAT) /* uncor data err */ +#define DS1_HSCE (011 << DS1_V_STAT) /* h/s compare err */ +#define DS1_IOPE (012 << DS1_V_STAT) /* IO oper err - na */ +#define DS1_EOCYL (014 << DS1_V_STAT) /* end cylinder */ +#define DS1_OVRUN (016 << DS1_V_STAT) /* overrun */ +#define DS1_CORDE (017 << DS1_V_STAT) /* correctible - na */ +#define DS1_ILLST (020 << DS1_V_STAT) /* illegal spare - na */ +#define DS1_DEFTK (021 << DS1_V_STAT) /* defective trk - na */ +#define DS1_ACCER (022 << DS1_V_STAT) /* access not rdy - na */ +#define DS1_S2ERR (023 << DS1_V_STAT) /* status 2 error */ +#define DS1_TKPER (026 << DS1_V_STAT) /* protected trk - na */ +#define DS1_UNAVL (027 << DS1_V_STAT) /* illegal unit */ +#define DS1_ATN (037 << DS1_V_STAT) /* attention */ +#define DS1_V_UNIT 0 +#define DS1_SPAR (1u << DS1_V_SPAR) +#define DS1_PROT (1u << DS1_V_PROT) +#define DS1_DFCT (1u << DS1_V_DFCT) + +/* Status 2, ^ = kept in unit status, * = dynamic */ + +#define DS2_ERR 0100000 /* *error */ +#define DS2_V_ID 9 /* drive type */ +#define DS2_ATN 0000200 /* ^attention */ +#define DS2_RO 0000100 /* *read only */ +#define DS2_FRM 0000040 /* *format */ +#define DS2_FLT 0000020 /* fault - na */ +#define DS2_FS 0000010 /* ^first status */ +#define DS2_SC 0000004 /* ^seek error */ +#define DS2_NR 0000002 /* *not ready */ +#define DS2_BS 0000001 /* *busy */ +#define DS2_ALLERR (DS2_FLT|DS2_SC|DS2_NR|DS2_BS) + +/* Controller state */ + +#define DS_IDLE 0 /* idle */ +#define DS_WAIT 1 /* command wait */ +#define DS_BUSY 2 /* busy */ + +/* This controller supports four different disk drive types: + + type #sectors/ #surfaces/ #cylinders/ + surface cylinder drive + + 7905 48 3 411 =15MB + 7906 48 4 411 =20MB + 7920 48 5 823 =50MB + 7925 64 9 823 =120MB + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. + + The 7905 and 7906 have fixed and removable platters. Consequently, + they are almost always accessed with cylinders limited to each + platter. The 7920 and 7925 have multiple-platter packs, and so are + almost always accessed with cylinders that span all surfaces. + + Disk image files are arranged as a linear set of tracks. To improve + locality, tracks on the 7905 and 7906 images are grouped per-platter, + i.e., all tracks on heads 0 and 1, followed by all tracks on head 2 + (and, for the 7906, head 3), whereas tracks on the 7920 and 7925 are + sequential by cylinder and head number. + + This variable-access geometry is accomplished by defining a "heads + per cylinder" value for the fixed and removable sections of each + drive that indicates the number of heads that should be grouped for + locality. The removable values are set to 2 on the 7905 and 7906, + indicating that those drives typically use cylinders of two surfaces. + They are set to the number of surfaces per drive for the 7920 and + 7925, as those typically use cylinders encompassing the entire + spindle. +*/ + +#define GET_DA(x,y,z,t) \ + (((((y) < drv_tab[t].rh)? \ + (x) * drv_tab[t].rh + (y): \ + drv_tab[t].cyl * drv_tab[t].rh + \ + ((x) * drv_tab[t].fh + (y) - drv_tab[t].rh)) * \ + drv_tab[t].sc + (z)) * DS_NUMWD) + +#define D7905_DTYPE 0 +#define D7905_SECT 48 +#define D7905_SURF 3 +#define D7905_RH 2 +#define D7905_FH (D7905_SURF - D7905_RH) +#define D7905_CYL 411 +#define D7905_ID (2 << DS2_V_ID) +#define D7905_SIZE (D7905_SECT * D7905_SURF * D7905_CYL * DS_NUMWD) + +#define D7906_DTYPE 1 +#define D7906_SECT 48 +#define D7906_SURF 4 +#define D7906_RH 2 +#define D7906_FH (D7906_SURF - D7906_RH) +#define D7906_CYL 411 +#define D7906_ID (0 << DS2_V_ID) +#define D7906_SIZE (D7906_SECT * D7906_SURF * D7906_CYL * DS_NUMWD) + +#define D7920_DTYPE 2 +#define D7920_SECT 48 +#define D7920_SURF 5 +#define D7920_RH D7920_SURF +#define D7920_FH (D7920_SURF - D7920_RH) +#define D7920_CYL 823 +#define D7920_ID (1 << DS2_V_ID) +#define D7920_SIZE (D7920_SECT * D7920_SURF * D7920_CYL * DS_NUMWD) + +#define D7925_DTYPE 3 +#define D7925_SECT 64 +#define D7925_SURF 9 +#define D7925_RH D7925_SURF +#define D7925_FH (D7925_SURF - D7925_RH) +#define D7925_CYL 823 +#define D7925_ID (3 << DS2_V_ID) +#define D7925_SIZE (D7925_SECT * D7925_SURF * D7925_CYL * DS_NUMWD) + +struct drvtyp { + uint32 sc; /* sectors */ + uint32 hd; /* surfaces */ + uint32 cyl; /* cylinders */ + uint32 size; /* #blocks */ + uint32 id; /* device type */ + uint32 rh; /* removable surfaces */ + uint32 fh; /* fixed surfaces */ + }; + +static struct drvtyp drv_tab[] = { + { D7905_SECT, D7905_SURF, D7905_CYL, D7905_SIZE, D7905_ID, D7905_RH, D7905_FH }, + { D7906_SECT, D7906_SURF, D7906_CYL, D7906_SIZE, D7906_ID, D7906_RH, D7906_FH }, + { D7920_SECT, D7920_SURF, D7920_CYL, D7920_SIZE, D7920_ID, D7920_RH, D7920_FH }, + { D7925_SECT, D7925_SURF, D7925_CYL, D7925_SIZE, D7925_ID, D7925_RH, D7925_FH }, + { 0 } + }; + +extern uint32 PC, SR; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; +extern int32 sim_switches; + +uint32 ds_fifo[DS_FIFO_SIZE] = { 0 }; /* fifo */ +uint32 ds_fifo_ip = 0; /* insertion ptr */ +uint32 ds_fifo_rp = 0; /* removal ptr */ +uint32 ds_fifo_cnt = 0; /* count */ +uint32 ds_cmd = 0; /* command word */ +uint32 ds_sr1 = 0; /* status word 1 */ +uint32 ds_busy = 0; /* busy flag */ +uint32 ds_eoc = 0; /* end of cylinder */ +uint32 ds_eod = 0; /* end of data */ +uint32 ds_fmask = 0; /* file mask */ +uint32 ds_cmdf = 0; /* command follows */ +uint32 ds_cmdp = 0; /* command present */ +uint32 ds_cyl = 0; /* disk address: cyl */ +uint32 ds_hs = 0; /* disk address: hs */ +uint32 ds_vctr = 0; /* verify counter */ +uint32 ds_state = 0; /* controller state */ +uint32 ds_lastatn = 0; /* last atn intr */ +int32 ds_stime = 100; /* seek time */ +int32 ds_rtime = 100; /* inter-sector time */ +int32 ds_ctime = 3; /* command time */ +int32 ds_dtime = 1; /* dch time */ +int32 ds_tmo = 2749200; /* timeout = 1.74 sec */ +uint32 ds_ptr = 0; /* buffer ptr */ +uint16 dsxb[DS_NUMWDF]; /* sector buffer */ + +static const uint32 ds_opflags[32] = { /* flags for ops */ + CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* cold read */ + CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* recalibrate */ + CMF_CLREC|CMF_CLRS|CMF_UIDLE, /* seek */ + 0, /* read status */ + CMF_CLRS, /* read sector */ + CMF_CLRS|CMF_UIDLE, /* read */ + CMF_CLRS|CMF_UIDLE, /* read full */ + CMF_CLRS|CMF_UIDLE, /* verify */ + CMF_CLRS|CMF_UIDLE, /* write */ + CMF_CLRS|CMF_UIDLE, /* write full */ + CMF_CLRS, /* clear */ + CMF_CLRS|CMF_UIDLE, /* init */ + CMF_CLREC|CMF_CLRS, /* addr record */ + 0, /* read syndrome */ + CMF_CLRS|CMF_UIDLE, /* read offset */ + CMF_CLRS, /* set file mask */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_CLRS|CMF_UIDLE, /* read no verify */ + CMF_CLRS, /* write TIO */ + CMF_CLRS, /* read disk addr */ + CMF_CLRS, /* end */ + CMF_CLRS, /* wake */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS, /* undefined */ + CMF_UNDF|CMF_CLRS /* undefined */ + }; + +DEVICE ds_dev; +int32 dsio (int32 inst, int32 IR, int32 dat); +t_stat ds_svc_c (UNIT *uptr); +t_stat ds_svc_u (UNIT *uptr); +t_stat ds_svc_t (UNIT *uptr); +t_stat ds_reset (DEVICE *dptr); +t_stat ds_attach (UNIT *uptr, char *cptr); +t_stat ds_detach (UNIT *uptr); +t_stat ds_boot (int32 unitno, DEVICE *dptr); +t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +void ds_poll (void); +void ds_docmd (uint32 cmd); +void ds_doatn (void); +uint32 ds_updds2 (UNIT *uptr); +void ds_cmd_done (t_bool sf, uint32 sr1); +void ds_wait_for_cpu (UNIT *uptr, uint32 newst); +void ds_set_idle (void); +void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy); +void ds_reqad (uint16 *cyl, uint16 *hs); +void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst); +t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy); +void ds_next_sec (UNIT *uptr); +void ds_next_cyl (UNIT *uptr); +t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy); +void ds_start_wr (UNIT *uptr, t_bool vfy); +void ds_cont_rd (UNIT *uptr, uint32 bsize); +t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize); +void ds_end_rw (UNIT *uptr, uint32 newst); +t_stat ds_set_uncorr (UNIT *uptr); +t_stat ds_reset_cmn (DEVICE *dptr); +void ds_sched_atn (UNIT *uptr); +uint32 ds_fifo_read (void); +void ds_fifo_write (uint32 dat); +void ds_fifo_reset (void); + +/* DS data structures + + ds_dev DS device descriptor + ds_unit DS unit list + ds_reg DS register list + ds_mod DS modifier list +*/ + +DIB ds_dib = { DS, 0, 0, 0, 0, 0, &dsio }; + +UNIT ds_unit[] = { + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_u, UNIT_FIX | UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_UNLOAD, D7905_SIZE) }, + { UDATA (&ds_svc_c, UNIT_DIS, 0) }, + { UDATA (&ds_svc_t, UNIT_DIS, 0) } + }; + +REG ds_reg[] = { + { ORDATA (CMD, ds_cmd, 16) }, + { BRDATA (FIFO, ds_fifo, 8, 16, DS_FIFO_SIZE) }, + { ORDATA (SR1, ds_sr1, 16) }, + { ORDATA (VCTR, ds_vctr, 16) }, + { ORDATA (FMASK, ds_fmask, 8) }, + { ORDATA (CYL, ds_cyl, 16) }, + { ORDATA (HS, ds_hs, 16) }, + { ORDATA (STATE, ds_state, 2), REG_RO }, + { ORDATA (LASTA, ds_lastatn, 3) }, + { DRDATA (FIP, ds_fifo_ip, 4) }, + { DRDATA (FRP, ds_fifo_rp, 4) }, + { DRDATA (FCNT, ds_fifo_cnt, 5) }, + { FLDATA (CMD, ds_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, ds_dib.ctl, 0) }, + { FLDATA (FLG, ds_dib.flg, 0) }, + { FLDATA (FBF, ds_dib.fbf, 0) }, + { FLDATA (SRQ, ds_dib.srq, 0) }, + { FLDATA (BUSY, ds_busy, 0) }, + { FLDATA (CMDF, ds_cmdf, 0) }, + { FLDATA (CMDP, ds_cmdp, 0) }, + { FLDATA (EOC, ds_eoc, 0) }, + { FLDATA (EOD, ds_eod, 0) }, + { BRDATA (DBUF, dsxb, 8, 16, DS_NUMWDF) }, + { DRDATA (DPTR, ds_ptr, 8) }, + { DRDATA (CTIME, ds_ctime, 24), PV_LEFT + REG_NZ }, + { DRDATA (DTIME, ds_dtime, 24), PV_LEFT + REG_NZ }, + { DRDATA (STIME, ds_stime, 24), PV_LEFT + REG_NZ }, + { DRDATA (RTIME, ds_rtime, 24), PV_LEFT + REG_NZ }, + { DRDATA (TIMEOUT, ds_tmo, 31), PV_LEFT + REG_NZ }, + { URDATA (UCYL, ds_unit[0].CYL, 10, 10, 0, + DS_NUMDR + 1, PV_LEFT | REG_HRO) }, + { URDATA (UFNC, ds_unit[0].FNC, 8, 8, 0, + DS_NUMDR + 1, REG_HRO) }, + { URDATA (USTA, ds_unit[0].STA, 8, 16, 0, + DS_NUMDR + 1, REG_HRO) }, + { URDATA (CAPAC, ds_unit[0].capac, 10, T_ADDR_W, 0, + DS_NUMDR, PV_LEFT | REG_HRO) }, + { ORDATA (DEVNO, ds_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB ds_mod[] = { + { UNIT_UNLOAD, UNIT_UNLOAD, "heads unloaded", "UNLOADED", ds_load_unload }, + { UNIT_UNLOAD, 0, "heads loaded", "LOADED", ds_load_unload }, + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_FMT, 0, "format disabled", "NOFORMAT", NULL }, + { UNIT_FMT, UNIT_FMT, "format enabled", "FORMAT", NULL }, + { (UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "7905", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "7906", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "7920", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "7925", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7905_DTYPE << UNIT_V_DTYPE), + "7905", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7906_DTYPE << UNIT_V_DTYPE), + "7906", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7920_DTYPE << UNIT_V_DTYPE), + "7920", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (D7925_DTYPE << UNIT_V_DTYPE), + "7925", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (D7905_DTYPE << UNIT_V_DTYPE), + NULL, "7905", &ds_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (D7906_DTYPE << UNIT_V_DTYPE), + NULL, "7906", &ds_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (D7920_DTYPE << UNIT_V_DTYPE), + NULL, "7920", &ds_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (D7925_DTYPE << UNIT_V_DTYPE), + NULL, "7925", &ds_set_size }, + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &ds_dev }, + { 0 } + }; + +DEVICE ds_dev = { + "DS", ds_unit, ds_reg, ds_mod, + DS_NUMDR + 2, 8, 27, 1, 8, 16, + NULL, NULL, &ds_reset, + &ds_boot, &ds_attach, &ds_detach, + &ds_dib, DEV_DISABLE + }; + +/* IO instructions */ + +int32 dsio (int32 inst, int32 IR, int32 dat) +{ +uint32 dev = IR & I_DEVMASK; + +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFLG (dev); } /* STF */ + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFC: /* skip flag clear */ + if (ds_busy == 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + if (ds_cmdf) { /* expecting command? */ + ds_cmd = dat; /* save command */ + ds_cmdf = 0; + ds_cmdp = 1; /* command present */ + } + else ds_fifo_write (dat); /* put in fifo */ + break; + + case ioMIX: /* merge */ + dat = dat | ds_fifo_read (); + break; + + case ioLIX: /* load */ + dat = ds_fifo_read (); + break; + + case ioCRS: /* control reset */ + clrCTL (dev); /* clear control */ + ds_cmdf = 0; /* not expecting command */ + ds_cmdp = 0; /* and none pending */ + ds_reset_cmn (&ds_dev); /* reset ctrl */ + break; + + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCTL (dev); /* clear control */ + ds_cmdf = 1; /* expecting command */ + ds_cmdp = 0; /* none pending */ + ds_fifo_reset (); /* clear fifo */ + } + else { /* STC */ + setCTL (dev); /* set ctl */ + } + break; + + case ioEDT: /* end of transfer */ + ds_eod = 1; /* flag end transfer */ + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +ds_poll (); /* run the controller */ +return dat; +} + +/* Run the controller polling loop, based on ds_state: + + IDLE commands and ATN interrupts + WAIT commands only + BUSY nothing +*/ + +void ds_poll (void) +{ +int32 dev = ds_dib.devno; + +if ((ds_state != DS_BUSY) && ds_cmdp) ds_docmd (ds_cmd);/* cmd pending? */ +if ((ds_state == DS_IDLE) && CTL (dev)) ds_doatn (); /* idle? check ATN */ +return; +} + +/* Process a command - ctrl state is either IDLE or WAIT. + + - A drive may be processing a seek or recalibrate + - The controller unit is idle + - If the command can be processed, ds_state is set to BUSY, and + the interface command buffer is cleared + - If the command cannot be processed, ds_state is set to WAIT, + and the command is retained in the interface command buffer */ + +void ds_docmd (uint32 cmd) +{ +uint32 op, f, dtyp, unum; + +op = DSC_GETOP (cmd); /* operation */ +f = ds_opflags[op]; /* flags */ +if (op == DSC_COLD) unum = 0; /* boot force unit 0 */ +else unum = DSC_GETUNIT (cmd); /* get unit */ +if ((f & CMF_UIDLE) && (unum < DS_NUMDR) && /* idle required */ + sim_is_active (&ds_unit[unum])) { /* but unit busy? */ + ds_state = DS_WAIT; /* wait */ + return; + } +ds_cmdp = 0; /* flush command */ +ds_state = DS_BUSY; /* ctrl is busy */ +if (f & CMF_CLRS) ds_sr1 = 0; /* clear status */ +if (f & CMF_CLREC) ds_eoc = 0; /* clear end cyl */ +if (f & CMF_UNDF) { /* illegal op? */ + ds_sched_ctrl_op (DSC_BADF, 0, CLR_BUSY); /* sched, clr busy */ + return; + } +switch (op) { + +/* Drive commands */ + + case DSC_COLD: /* cold load read */ + ds_fmask = DSC_SPEN; /* sparing enabled */ + ds_cyl = 0; /* cylinder 0 */ + ds_hs = (DSC_GETCHD (ds_cmd) << DSHS_V_HD) | /* reformat hd/sec */ + (DSC_GETCSC (ds_cmd) << DSHS_V_SC); + case DSC_RECAL: /* recalibrate */ + case DSC_SEEK: /* seek */ + case DSC_READ: /* read */ + case DSC_RFULL: /* read full */ + case DSC_ROFF: /* read offset */ + case DSC_RNOVFY: /* read no verify */ + case DSC_VFY: /* verify */ + case DSC_WRITE: /* write */ + case DSC_WFULL: /* write full */ + case DSC_INIT: /* init */ + ds_sr1 = unum; /* init status */ + if (unum >= DS_NUMDR) { /* invalid unit? */ + ds_sched_ctrl_op (DSC_BADU, unum, CLR_BUSY);/* sched, not busy */ + return; + } + if (op == DSC_INIT) ds_sr1 |= /* init? */ + ((cmd & DSC_SPAR)? DS1_SPAR: 0) | /* copy SPD to stat1 */ + ((cmd & DSC_PROT)? DS1_PROT: 0) | + ((cmd & DSC_DFCT)? DS1_DFCT: 0); + ds_unit[unum].FNC = op; /* save op */ + ds_unit[unum].STA &= ~DS2_ATN; /* clear ATN */ + sim_cancel (&ds_unit[unum]); /* cancel current */ + sim_activate (&ds_unit[unum], ds_ctime); /* schedule unit */ + ds_busy = 1; /* set visible busy */ + break; + +/* Read status commands */ + + case DSC_RSTA: /* read status */ + dsxb[1] = ds_sr1; /* return SR1 */ + ds_sr1 = 0; /* clear SR1 */ + if (unum < DS_NUMDR) { /* return SR2 */ + dsxb[0] = ds_updds2 (&ds_unit[unum]); + ds_unit[unum].STA &= ~DS2_FS; /* clear 1st */ + } + else dsxb[0] = DS2_ERR|DS2_NR; + ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */ + break; + + case DSC_RSA: /* read sector address */ + dtyp = GET_DTYPE (ds_unit[unum].flags); /* get unit type */ + dsxb[0] = GET_CURSEC (ds_dtime * DS_NUMWD, dtyp); /* rot position */ + ds_sched_ctrl_op (DSC_RSTA, 1, SET_BUSY); /* sched 1 wd, busy */ + break; + + case DSC_RDA: /* read disk address */ + ds_reqad (&dsxb[1], &dsxb[0]); /* return disk address */ + ds_sched_ctrl_op (DSC_RSTA, 2, SET_BUSY); /* sched 2 wds, busy */ + break; + + case DSC_RSYN: /* read syndrome */ + dsxb[6] = ds_sr1; /* return SR1 */ + ds_reqad (&dsxb[5], &dsxb[4]); /* return disk address */ + dsxb[3] = dsxb[2] = dsxb[1] = dsxb[0] = 0; /* syndrome is 0 */ + ds_sched_ctrl_op (DSC_RSTA, 7, SET_BUSY); /* sched 7 wds, busy */ + break; + +/* Other controller commands */ + + case DSC_SFM: /* set file mask */ + case DSC_CLEAR: /* clear */ + case DSC_AREC: /* address record */ + case DSC_WAKE: /* wakeup */ + case DSC_WTIO: /* write TIO */ + ds_sched_ctrl_op (op, 0, SET_BUSY); /* schedule, busy */ + break; + + case DSC_END: /* end */ + ds_set_idle (); /* idle ctrl */ + break; + } + +return; +} + +/* Check for attention */ + +void ds_doatn (void) +{ +uint32 i, dev; + +dev = ds_dib.devno; /* device num */ +for (i = 0; i < DS_NUMDR; i++) { /* intr disabled? */ + ds_lastatn = (ds_lastatn + 1) & DS_DRMASK; /* loop through units */ + if (ds_unit[ds_lastatn].STA & DS2_ATN) { /* ATN set? */ + ds_unit[ds_lastatn].STA &= ~DS2_ATN; /* clear ATN */ + setFLG (dev); /* request interrupt */ + ds_sr1 = DS1_ATN | ds_lastatn; /* set up status 1 */ + ds_state = DS_WAIT; /* block atn intrs */ + return; + } + } +return; +} + +/* Controller service + + The argument for the function, if any, is stored in uptr->CYL */ + +t_stat ds_svc_c (UNIT *uptr) +{ +uint32 op, dev; + +op = uptr->FNC; +dev = ds_dib.devno; +switch (op) { + + case DSC_AREC: /* address record */ + ds_wait_for_cpu (uptr, DSC_AREC|DSC_2ND); /* set flag, new state */ + break; + case DSC_AREC | DSC_2ND: /* poll done */ + if (!DS_FIFO_EMPTY) { /* OTA ds? */ + ds_cyl = ds_fifo_read (); /* save cylinder */ + ds_wait_for_cpu (uptr, DSC_AREC|DSC_3RD); /* set flag, new state */ + } + else sim_activate (uptr, ds_ctime); /* no, continue poll */ + break; + case DSC_AREC | DSC_3RD: /* poll done */ + if (!DS_FIFO_EMPTY) { /* OTA ds? */ + ds_hs = ds_fifo_read (); /* save head/sector */ + ds_cmd_done (0, DS1_OK); /* op done, no flag */ + } + else sim_activate (uptr, ds_ctime); /* no, continue poll */ + break; + + case DSC_RSTA: /* rd stat (all forms) */ + if (DS_FIFO_EMPTY) { /* fifo empty? */ + uptr->CYL--; + ds_fifo_write (dsxb[uptr->CYL]); /* store next status */ + ds_wait_for_cpu (uptr, DSC_RSTA | + (uptr->CYL? 0: DSC_2ND)); /* set flag, new state */ + } + else sim_activate (uptr, ds_ctime); /* no, continue poll */ + break; + case DSC_RSTA | DSC_2ND: /* poll done */ + if (DS_FIFO_EMPTY) ds_cmd_done (0, DS1_OK); /* op done? no flag */ + else sim_activate (uptr, ds_ctime); /* no, continue poll */ + break; + + case DSC_CLEAR: /* clear */ + ds_reset_cmn (&ds_dev); /* reset ctrl */ + clrCTL (dev); /* clear CTL, SRQ */ + clrSRQ (dev); + ds_cmd_done (1, DS1_OK); /* op done, set flag */ + break; + + case DSC_SFM: /* set file mask */ + ds_fmask = ds_cmd & DSC_FMASK; + ds_cmd_done (1, DS1_OK); /* op done, set flag */ + break; + + case DSC_WTIO: /* write I/O */ + ds_cmd_done (0, DS1_OK); /* op done, no flag */ + break; + + case DSC_WAKE: /* wakeup */ + ds_cmd_done (1, DS1_AVAIL); /* op done, set flag */ + break; + + case DSC_BADU: /* invalid unit */ + if (uptr->CYL > 10) ds_cmd_done (1, DS1_UNAVL); /* [11,16]? bad unit */ + else ds_cmd_done (1, DS1_S2ERR); /* else unit not ready */ + break; + + case DSC_BADF: /* invalid operation */ + ds_cmd_done (1, DS1_ILLOP); /* op done, set flag */ + break; + + default: + return SCPE_IERR; + } + +ds_poll (); /* run the controller */ +return SCPE_OK; +} + +/* Timeout service */ + +t_stat ds_svc_t (UNIT *uptr) +{ +int32 i; + +for (i = 0; i < (DS_NUMDR + 1); i++) /* cancel all ops */ + sim_cancel (&ds_unit[i]); +ds_set_idle (); /* idle the controller */ +ds_fmask = 0; /* clear file mask */ +ds_poll (); /* run the controller */ +return SCPE_OK; +} + +/* Unit service */ + +t_stat ds_svc_u (UNIT *uptr) +{ +uint32 op, dev, dtyp; +t_stat r; + +op = uptr->FNC; +dev = ds_dib.devno; +dtyp = GET_DTYPE (uptr->flags); +switch (op) { /* case on function */ + +/* Seek and recalibrate */ + + case DSC_RECAL: /* recalibrate */ + if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */ + ds_start_seek (uptr, 0, DSC_RECAL|DSC_2ND); /* set up seek */ + ds_set_idle (); /* ctrl is idle */ + } + else ds_cmd_done (1, DS1_S2ERR); /* not ready error */ + break; + case DSC_RECAL | DSC_2ND: /* recal complete */ + uptr->STA = uptr->STA | DS2_ATN; /* set attention */ + break; + + case DSC_SEEK: /* seek */ + ds_wait_for_cpu (uptr, DSC_SEEK|DSC_2ND); /* set flag, new state */ + break; + case DSC_SEEK | DSC_2ND: /* waiting for word 1 */ + if (!DS_FIFO_EMPTY) { /* OTA ds? */ + ds_cyl = ds_fifo_read (); /* save cylinder */ + ds_wait_for_cpu (uptr, DSC_SEEK|DSC_3RD); /* set flag, new state */ + } + else sim_activate (uptr, ds_ctime); /* no, continue poll */ + break; + case DSC_SEEK | DSC_3RD: /* waiting for word 2 */ + if (!DS_FIFO_EMPTY) { /* OTA ds? */ + ds_hs = ds_fifo_read (); /* save head/sector */ + if ((uptr->flags & UNIT_UNLOAD) == 0) { /* drive up? */ + ds_start_seek (uptr, ds_cyl, DSC_SEEK|DSC_4TH); /* set up seek */ + ds_set_idle (); /* ctrl is idle */ + } + else ds_cmd_done (1, DS1_S2ERR); /* else not ready error */ + } + else sim_activate (uptr, ds_ctime); /* continue poll */ + break; + case DSC_SEEK | DSC_4TH: /* seek complete */ + uptr->STA = uptr->STA | DS2_ATN; /* set attention */ + break; + +/* Read variants */ + + case DSC_ROFF: /* read with offset */ + ds_wait_for_cpu (uptr, DSC_ROFF|DSC_2ND); /* set flag, new state */ + break; + case DSC_ROFF | DSC_2ND: /* poll done */ + if (!DS_FIFO_EMPTY) { /* OTA ds? new state */ + ds_fifo_read (); /* drain fifo */ + uptr->FNC = DSC_READ; + setFLG (dev); /* handshake */ + } + sim_activate (uptr, ds_ctime); /* schedule unit */ + break; + + case DSC_COLD: /* cold load read */ + if ((uptr->flags & UNIT_UNLOAD) == 0) /* drive up? */ + ds_start_seek (uptr, 0, DSC_READ); /* set up seek */ + else ds_cmd_done (1, DS1_S2ERR); /* no, not ready error */ + break; + + case DSC_READ: /* read */ + if (r = ds_start_rd (uptr, 0, 1)) return r; /* new sector; error? */ + break; + case DSC_READ | DSC_2ND: /* word transfer */ + ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */ + break; + case DSC_READ | DSC_3RD: /* end of sector */ + ds_end_rw (uptr, DSC_READ); /* see if more to do */ + break; + + case DSC_RNOVFY: /* read, no verify */ + if (r = ds_start_rd (uptr, 0, 0)) return r; /* new sector; error? */ + break; + case DSC_RNOVFY | DSC_2ND: /* word transfer */ + ds_cont_rd (uptr, DS_NUMWD); /* xfr wd, check end */ + break; + case DSC_RNOVFY | DSC_3RD: /* end of sector */ + ds_end_rw (uptr, DSC_RNOVFY); /* see if more to do */ + break; + + case DSC_RFULL: /* read full */ + dsxb[DS_FSYNC] = 0100376; /* fill in header */ + dsxb[DS_FCYL] = uptr->CYL; + dsxb[DS_FHS] = ds_hs; /* before h/s update */ + if (r = ds_start_rd (uptr, DS_FDATA, 0)) /* new sector; error? */ + return r; + break; + case DSC_RFULL | DSC_2ND: /* word transfer */ + ds_cont_rd (uptr, DS_NUMWDF); /* xfr wd, check end */ + break; + case DSC_RFULL | DSC_3RD: /* end of sector */ + ds_end_rw (uptr, DSC_RFULL); /* see if more to do */ + break; + + case DSC_VFY: /* verify */ + ds_wait_for_cpu (uptr, DSC_VFY|DSC_2ND); /* set flag, new state */ + break; + case DSC_VFY | DSC_2ND: /* poll done */ + if (!DS_FIFO_EMPTY) { /* OTA ds? */ + ds_vctr = ds_fifo_read (); /* save count */ + uptr->FNC = DSC_VFY | DSC_3RD; /* next state */ + sim_activate (uptr, ds_rtime); /* delay for transfer */ + } + else sim_activate (uptr, ds_ctime); /* no, continue poll */ + break; + case DSC_VFY | DSC_3RD: /* start sector */ + if (ds_start_rw (uptr, ds_dtime * DS_NUMWD, 1)) break; + /* new sector; error? */ + ds_next_sec (uptr); /* increment hd, sc */ + break; + case DSC_VFY | DSC_4TH: /* end sector */ + ds_vctr = (ds_vctr - 1) & DMASK; /* decrement count */ + if (ds_vctr) ds_end_rw (uptr, DSC_VFY|DSC_3RD); /* more to do? */ + else ds_cmd_done (1, DS1_OK); /* no, set done */ + break; + +/* Write variants */ + + case DSC_WRITE: /* write */ + ds_start_wr (uptr, 1); /* new sector */ + break; + case DSC_WRITE | DSC_2ND: + if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */ + return r; /* error? */ + break; + case DSC_WRITE | DSC_3RD: /* end sector */ + ds_end_rw (uptr, DSC_WRITE); /* see if more to do */ + break; + + case DSC_INIT: /* init */ + ds_start_wr (uptr, 0); /* new sector */ + break; + case DSC_INIT | DSC_2ND: + if (r = ds_cont_wr (uptr, 0, DS_NUMWD)) /* write word */ + return r; /* error? */ + break; + case DSC_INIT | DSC_3RD: /* end sector */ + ds_end_rw (uptr, DSC_INIT); /* see if more to do */ + break; + + case DSC_WFULL: /* write full */ + ds_start_wr (uptr, 0); /* new sector */ + break; + case DSC_WFULL | DSC_2ND: + if (r = ds_cont_wr (uptr, DS_FDATA, DS_NUMWDF)) /* write word */ + return r; /* error */ + break; + case DSC_WFULL | DSC_3RD: + ds_end_rw (uptr, DSC_WFULL); /* see if more to do */ + break; + + default: + break; + } + +ds_poll (); +return SCPE_OK; +} + +/* Schedule timed wait for CPU response + + - Set flag to get CPU attention + - Set specified unit to 'newstate' and schedule + - Schedule timeout */ + +void ds_wait_for_cpu (UNIT *uptr, uint32 newst) +{ +uint32 dev = ds_dib.devno; + +setFLG (dev); /* set flag */ +uptr->FNC = newst; /* new state */ +sim_activate (uptr, ds_ctime); /* activate unit */ +sim_cancel (&ds_timer); /* activate timeout */ +sim_activate (&ds_timer, ds_tmo); +return; +} + +/* Set idle state + + - Controller is set to idle state + - Visible busy is cleared + - Timeout is cancelled */ + +void ds_set_idle (void) +{ +ds_busy = 0; /* busy clear */ +ds_state = DS_IDLE; /* ctrl idle */ +sim_cancel (&ds_timer); /* no timeout */ +return; +} + +/* Set wait state + + - Set flag if required + - Set controller to wait state + - Clear visible busy + - Schedule timeout */ + +void ds_cmd_done (t_bool sf, uint32 sr1) +{ +uint32 dev = ds_dib.devno; + +if (sf) { setFLG (dev); } /* set host flag */ +ds_busy = 0; /* clear visible busy */ +ds_sr1 = ds_sr1 | sr1; /* final status */ +ds_state = DS_WAIT; /* ctrl waiting */ +sim_cancel (&ds_timer); /* activate timeout */ +sim_activate (&ds_timer, ds_tmo); +return; +} + +/* Return drive status (status word 2) */ + +uint32 ds_updds2 (UNIT *uptr) +{ +uint32 sta; +uint32 dtyp = GET_DTYPE (uptr->flags); + +sta = drv_tab[dtyp].id | /* form status */ + uptr->STA | /* static bits */ + ((uptr->flags & UNIT_WPR)? DS2_RO: 0) | /* dynamic bits */ + ((uptr->flags & UNIT_FMT)? DS2_FRM: 0) | + ((uptr->flags & UNIT_UNLOAD)? DS2_NR | DS2_BS: 0) | + (sim_is_active (uptr)? DS2_BS: 0); +if (sta & DS2_ALLERR) sta = sta | DS2_ERR; /* set error */ +return sta; +} + +/* Schedule controller operation */ + +void ds_sched_ctrl_op (uint32 op, uint32 arg, uint32 busy) +{ +ds_ctrl.FNC = op; /* save op */ +ds_ctrl.CYL = arg; /* save argument */ +ds_busy = busy; /* set visible busy */ +sim_activate (&ds_ctrl, ds_ctime); /* schedule */ +sim_cancel (&ds_timer); /* activate timeout */ +sim_activate (&ds_timer, ds_tmo); +return; +} + +/* Request address - if pending eoc, report cylinder + 1 */ + +void ds_reqad (uint16 *cyl, uint16 *hs) +{ +*cyl = ds_cyl + (ds_eoc? 1: 0); +*hs = ds_hs; +return; +} + +/* Start seek - schedule whether in bounds or out of bounds */ + +void ds_start_seek (UNIT *uptr, uint32 cyl, uint32 newst) +{ +int32 t; +uint32 hd, sc; +uint32 dtyp = GET_DTYPE (uptr->flags); + +uptr->FNC = newst; /* set new state */ +if (cyl >= drv_tab[dtyp].cyl) { /* out of bounds? */ + t = 0; /* don't change cyl */ + uptr->STA = uptr->STA | DS2_SC; /* set seek check */ + } +else { + t = abs (uptr->CYL - cyl); /* delta cylinders */ + uptr->CYL = cyl; /* put on cylinder */ + hd = DSHS_GETHD (ds_hs); /* invalid head or sec? */ + sc = DSHS_GETSC (ds_hs); + if ((hd >= drv_tab[dtyp].hd) || + (sc >= drv_tab[dtyp].sc)) + uptr->STA = uptr->STA | DS2_SC; /* set seek check */ + else uptr->STA = uptr->STA & ~DS2_SC; /* clear seek check */ + } +sim_activate (uptr, ds_stime * (t + 1)); /* schedule */ +return; +} + +/* Start next sector for read or write + + - If error, set command done, return TRUE, nothing is scheduled + - If implicit seek, return TRUE, implicit seek is scheduled, but + state is not changed - we will return here when seek is done + - Otherwise, advance state, set position in file, schedule next state */ + +t_bool ds_start_rw (UNIT *uptr, int32 tm, t_bool vfy) +{ +uint32 da, hd, sc; +uint32 dtyp = GET_DTYPE (uptr->flags); + +ds_eod = 0; /* init eod */ +ds_ptr = 0; /* init buffer ptr */ +if (uptr->flags & UNIT_UNLOAD) { /* drive down? */ + ds_cmd_done (1, DS1_S2ERR); + return TRUE; + } +if (ds_eoc) { /* at end of cylinder? */ + ds_next_cyl (uptr); /* auto seek to next */ + return TRUE; /* or error */ + } +if (vfy && ((uint32) uptr->CYL != ds_cyl)) { /* on wrong cylinder? */ + if (ds_cyl >= drv_tab[dtyp].cyl) /* seeking to bad? */ + ds_cmd_done (1, DS1_CYLCE); /* lose */ + else ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek right cyl */ + return TRUE; + } +hd = DSHS_GETHD (ds_hs); +sc = DSHS_GETSC (ds_hs); +if ((uint32) uptr->CYL >= drv_tab[dtyp].cyl) { /* valid cylinder? */ + uptr->STA = uptr->STA | DS2_SC; /* set seek check */ + ds_cmd_done (1, DS1_S2ERR); /* error */ + return TRUE; + } +if ((hd >= drv_tab[dtyp].hd) || /* valid head, sector? */ + (sc >= drv_tab[dtyp].sc)) { + ds_cmd_done (1, DS1_HSCE); /* no, error */ + return TRUE; + } +da = GET_DA (uptr->CYL, hd, sc, dtyp); /* position in file */ +sim_fseek (uptr->fileref, da * sizeof (uint16), SEEK_SET); /* set file pos */ +uptr->FNC += DSC_NEXT; /* next state */ +sim_activate (uptr, tm); /* activate unit */ +return FALSE; +} + +/* Start next sector for read + + - Do common start for read and write + - If error, return, command has been terminated, nothing scheduled + - If implicit seek, return, seek scheduled + - If no error or seek, state has been advanced and unit scheduled + - Read sector + - If read error, terminate command and return, nothing scheduled + - If no error, advance head/sector, next state scheduled */ + +t_stat ds_start_rd (UNIT *uptr, uint32 off, t_bool vfy) +{ +uint32 t; + +if (ds_start_rw (uptr, ds_rtime, vfy)) return SCPE_OK; /* new sec; err or seek? */ +t = sim_fread (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref); +for (t = t + off ; t < DS_NUMWDF; t++) dsxb[t] = 0; /* fill sector */ +if (ferror (uptr->fileref)) /* error? */ + return ds_set_uncorr (uptr); /* say uncorrectable */ +ds_next_sec (uptr); /* increment hd, sc */ +return SCPE_OK; +} + +/* Start next sector for write + + - Do common start for read and write + - If error, return, command has been terminated, nothing scheduled + - If implicit seek, return, seek scheduled + - If no error or seek, state has been advanced and unit scheduled + - Clear buffer + - Set service request */ + +void ds_start_wr (UNIT *uptr, t_bool vfy) +{ +uint32 i; +uint32 dev = ds_dib.devno; + +if ((uptr->flags & UNIT_WPR) || /* write protected? */ + (!vfy && ((uptr->flags & UNIT_FMT) == 0))) { /* format, not enbl? */ + ds_cmd_done (1, DS1_S2ERR); /* error */ + return; + } +if (ds_start_rw (uptr, ds_rtime, vfy)) return; /* new sec; err or seek? */ +for (i = 0; i < DS_NUMWDF; i++) dsxb[i] = 0; /* clear buffer */ +setSRQ (dev); /* request word */ +return; +} + +/* Advance to next sector (but not next cylinder) */ + +void ds_next_sec (UNIT *uptr) +{ +uint32 dtyp = GET_DTYPE (uptr->flags); + +ds_hs = ds_hs + 1; /* increment sector */ +if (DSHS_GETSC (ds_hs) < drv_tab[dtyp].sc) return; /* end of track? */ +ds_hs = ds_hs & ~DSHS_SC; /* yes, wrap sector */ +if (ds_fmask & DSC_CYLM) { /* cylinder mode? */ + ds_hs = ds_hs + (1 << DSHS_V_HD); /* increment head */ + if (DSHS_GETHD (ds_hs) < drv_tab[dtyp].hd) return; /* end of cyl? */ + ds_hs = ds_hs & ~DSHS_HD; /* 0 head */ + } +ds_eoc = 1; /* flag end cylinder */ +return; +} + +/* Advance to next cylinder + + - If autoseek enabled, seek to cylinder +/- 1 + - Otherwise, done with end of cylinder error */ + +void ds_next_cyl (UNIT *uptr) +{ +if (ds_fmask & DSC_AUTO) { /* auto seek allowed? */ + if (ds_fmask & DSC_DECR) ds_cyl = (ds_cyl - 1) & DMASK; + else ds_cyl = (ds_cyl + 1) & DMASK; + ds_eoc = 0; /* clear end cylinder */ + ds_start_seek (uptr, ds_cyl, uptr->FNC); /* seek, same state */ + } +else ds_cmd_done (1, DS1_EOCYL); /* no, end of cyl err */ +return; +} + +/* Transfer word for read + + - If end of data, terminate command, nothing scheduled + - Otherwise, transfer word, advance state if last word, schedule */ + +void ds_cont_rd (UNIT *uptr, uint32 bsize) +{ +uint32 dev = ds_dib.devno; + +if (ds_eod) ds_cmd_done (1, DS1_OK); /* DMA end? done */ +else if (SRQ (dev)) { /* overrun? */ + ds_cmd_done (1, DS1_OVRUN); /* set done */ + return; + } +else { ds_fifo_write (dsxb[ds_ptr++]); /* next word */ + setSRQ (dev); /* request service */ + if (ds_ptr >= bsize) uptr->FNC += DSC_NEXT; /* sec done? next state */ + sim_activate (uptr, ds_dtime); /* schedule */ + } +return; +} + +/* Transfer word for write + + - Copy word from fifo to buffer + - If end of data, write buffer, terminate command, nothing scheduled + - If end of sector, write buffer, next state, schedule + - Otherwises, set service request, schedule */ + +t_stat ds_cont_wr (UNIT *uptr, uint32 off, uint32 bsize) +{ +uint32 i, dat; +uint32 dev = ds_dib.devno; + +if (SRQ (dev)) { /* overrun? */ + ds_cmd_done (1, DS1_OVRUN); /* set done */ + return SCPE_OK; + } +dsxb[ds_ptr++] = dat = ds_fifo_read (); /* next word */ +if (ds_eod || (ds_ptr >= bsize)) { /* xfr or sector done? */ + for (i = ds_ptr; i < bsize; i++) dsxb[i] = dat; /* fill sector */ + sim_fwrite (dsxb + off, sizeof (uint16), DS_NUMWD, uptr->fileref); + if (ferror (uptr->fileref)) /* error on write? */ + return ds_set_uncorr (uptr); /* uncorrectable */ + ds_next_sec (uptr); /* increment hd, sc */ + if (ds_eod) { /* end data? */ + ds_cmd_done (1, DS1_OK); /* set done */ + return SCPE_OK; + } + else uptr->FNC += DSC_NEXT; /* no, next state */ + } +else { setSRQ (dev); } /* request next word */ +sim_activate (uptr, ds_dtime); /* schedule */ +return SCPE_OK; +} + +/* End sector for read or write + + - If end of data, terminate command, nothing scheduled + - If end of cylinder, schedule next cylinder + - Else schedule start of next sector */ + +void ds_end_rw (UNIT *uptr, uint32 newst) +{ +uptr->FNC = newst; /* new state */ +if (ds_eod) ds_cmd_done (1, DS1_OK); /* done? */ +else if (ds_eoc) ds_next_cyl (uptr); /* end cyl? seek */ +else sim_activate (uptr, ds_rtime); /* normal transfer */ +return; +} + +/* Report uncorrectable data error */ + +t_stat ds_set_uncorr (UNIT *uptr) +{ +sim_cancel (uptr); /* cancel any operation */ +ds_cmd_done (1, DS1_UNCOR); /* done with error */ +perror ("DS I/O error"); /* visible error */ +clearerr (uptr->fileref); +ds_poll (); /* force poll */ +return SCPE_IOERR; +} + +/* Fifo read */ + +uint32 ds_fifo_read (void) +{ +uint32 dat; + +if (ds_fifo_cnt == 0) return ds_fifo[ds_fifo_rp]; +dat = ds_fifo[ds_fifo_rp++]; +if (ds_fifo_rp >= DS_FIFO_SIZE) ds_fifo_rp = 0; +ds_fifo_cnt--; +return dat; +} + +void ds_fifo_write (uint32 dat) +{ +ds_fifo[ds_fifo_ip++] = dat; +if (ds_fifo_ip >= DS_FIFO_SIZE) ds_fifo_ip = 0; +if (ds_fifo_cnt < DS_FIFO_SIZE) ds_fifo_cnt++; +return; +} + +void ds_fifo_reset (void) +{ +uint32 i; + +ds_fifo_ip = ds_fifo_rp = ds_fifo_cnt = 0; +for (i = 0; i < DS_FIFO_SIZE; i++) ds_fifo[i] = 0; +return; +} + +/* Reset routine */ + +t_stat ds_reset_cmn (DEVICE *dptr) +{ +int32 i; + +ds_cmd = 0; /* clear command */ +ds_cmdf = ds_cmdp = 0; /* clear commands flops */ +ds_fifo_reset (); /* clear fifo */ +ds_eoc = ds_eod = 0; +ds_busy = 0; +ds_state = DS_IDLE; /* ctrl idle */ +ds_lastatn = 0; +ds_fmask = 0; +ds_ptr = 0; +ds_cyl = ds_hs = 0; +ds_vctr = 0; +for (i = 0; i < DS_NUMDR; i++) { /* loop thru drives */ + sim_cancel (&ds_unit[i]); /* cancel activity */ + ds_unit[i].FNC = 0; /* clear function */ + ds_unit[i].CYL = 0; + ds_unit[i].STA = 0; + } +sim_cancel (&ds_ctrl); +sim_cancel (&ds_timer); +return SCPE_OK; +} + +t_stat ds_reset (DEVICE *dptr) +{ +ds_dib.cmd = 0; /* clear cmd */ +ds_dib.ctl = 0; /* clear ctl */ +ds_dib.fbf = 1; /* set fbf */ +ds_dib.flg = 1; /* set flg */ +ds_dib.srq = 0; /* clear srq */ +return ds_reset_cmn (dptr); /* do common reset */ +} + +/* Device attach */ + +t_stat ds_attach (UNIT *uptr, char *cptr) +{ +uint32 i, p; +t_stat r; + +uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +ds_load_unload (uptr, 0, NULL, NULL); /* if OK, load heads */ +ds_sched_atn (uptr); /* schedule attention */ +if (((uptr->flags & UNIT_AUTO) == 0) || /* static size? */ + ((p = sim_fsize (uptr->fileref)) == 0)) return SCPE_OK; /* new file? */ +for (i = 0; drv_tab[i].sc != 0; i++) { /* find best fit */ + if (p <= (drv_tab[i].size * sizeof (uint16))) { + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr->capac = drv_tab[i].size; + return SCPE_OK; + } + } +return SCPE_OK; +} + +/* Device detach */ + +t_stat ds_detach (UNIT *uptr) +{ +ds_load_unload (uptr, UNIT_UNLOAD, NULL, NULL); /* unload heads if attached */ +return detach_unit (uptr); +} + +/* Load and unload heads */ + +t_stat ds_load_unload (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* must be attached to [un]load */ +if (value == UNIT_UNLOAD) { /* unload heads? */ + uptr->flags = uptr->flags | UNIT_UNLOAD; /* indicate unload */ + uptr->STA = DS2_ATN; /* update drive status */ + ds_sched_atn (uptr); /* schedule attention */ + } +else { /* load heads */ + uptr->flags = uptr->flags & ~UNIT_UNLOAD; /* indicate load */ + uptr->STA = DS2_ATN | DS2_FS; /* update drive status */ + } +return SCPE_OK; +} + +/* Schedule attention interrupt if CTL set, not restore, and controller idle */ + +void ds_sched_atn (UNIT *uptr) +{ +int32 i; + +if (!ds_dib.ctl || (sim_switches & SIM_SW_REST)) return; +for (i = 0; i < (DS_NUMDR + 1); i++) { /* check units, ctrl */ + if (sim_is_active (ds_dev.units + i)) return; + } +uptr->FNC = DSC_ATN; /* pseudo operation */ +sim_activate (uptr, 1); /* do immediately */ +return; +} + +/* Set size command validation routine */ + +t_stat ds_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = drv_tab[GET_DTYPE (val)].size; +return SCPE_OK; +} + +/* 13037 bootstrap routine (HP 12992B ROM) */ + +const uint16 ds_rom[IBL_LNT] = { + 0017727, /* STRT JSB STAT ; get status */ + 0002021, /* SSA,RSS ; is drive ready? */ + 0027742, /* JMP DMA ; yes, set up DMA */ + 0013714, /* AND B20 ; no, check status bits */ + 0002002, /* SZA ; faulty or hard down? */ + 0102030, /* HLT 30B ; HALT 30B */ + 0027700, /* JMP STRT ; try again */ + 0102011, /* ADR1 OCT 102011 */ + 0102055, /* ADR2 OCT 102055 */ + 0164000, /* CNT DEC -6144 */ + 0000007, /* D7 OCT 7 */ + 0001400, /* STCM OCT 1400 */ + 0000020, /* B20 OCT 20 */ + 0017400, /* STMS OCT 17400 */ + 0000000, /* 9 NOP's */ + 0000000, + 0000000, + 0000000, + 0000000, + 0000000, + 0000000, + 0000000, + 0000000, + 0000000, /* STAT NOP ; status check routine */ + 0107710, /* CLC DC,C ; set command mode */ + 0063713, /* LDA STCM ; get status command */ + 0102610, /* OTA DC ; output status command */ + 0102310, /* SFS DC ; wait for stat#1 word */ + 0027733, /* JMP *-1 */ + 0107510, /* LIB DC,C ; B-reg - status#1 word */ + 0102310, /* SFS DC ; wait for stat#2 word */ + 0027736, /* JMP *-1 */ + 0103510, /* LIA DC,C ; A-reg - status#2 word */ + 0127727, /* JMP STAT,I ; return */ + 0067776, /* DMA LDB DMAC ; get DMA control word */ + 0106606, /* OTB 6 ; output DMA ctrl word */ + 0067707, /* LDB ADR1 ; get memory address */ + 0106702, /* CLC 2 ; set memory addr mode */ + 0106602, /* OTB 2 ; output mem addr to DMA */ + 0102702, /* STC 2 ; set word count mode */ + 0067711, /* LDB CNT ; get word count */ + 0106602, /* OTB 2 ; output word cnt to DMA */ + 0106710, /* CLC CLC DC ; set command follows */ + 0102501, /* LIA 1 ; load switches */ + 0106501, /* LIB 1 ; register settings */ + 0013712, /* AND D7 ; isolate head number */ + 0005750, /* BLF,CLE,SLB ; bit 12 = 0? */ + 0027762, /* JMP *+3 ; no, manual boot */ + 0002002, /* SZA ; yes, RPL, head# = 0? */ + 0001000, /* ALS ; no, head# = 1 --> 2 */ + 0001720, /* ALF,ALS ; form cold load */ + 0001000, /* ALS ; command word */ + 0103706, /* STC 6,C ; activate DMA */ + 0103610, /* OTA DC,C ; output cold load cmd */ + 0102310, /* SFS DC ; is cold load done? */ + 0027766, /* JMP *-1 ; no, wait */ + 0017727, /* JSB STAT ; yes, get status */ + 0060001, /* LDA 1 ; get status word #1 */ + 0013715, /* AND STMS ; isolate status bits */ + 0002002, /* SZA ; is transfer ok? */ + 0027700, /* JMP STRT ; no, try again */ + 0117710, /* JSB ADR2,I ; yes, start program */ + 0000010, /* DMAC ABS DC ; DMA command word */ + 0170100, /* ABS -STRT */ + }; + +t_stat ds_boot (int32 unitno, DEVICE *dptr) +{ +int32 dev; + +if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ +dev = ds_dib.devno; /* get data chan dev */ +if (ibl_copy (ds_rom, dev)) return SCPE_IERR; /* copy boot to memory */ +SR = (SR & (IBL_OPT | IBL_DS_HEAD)) | IBL_DS | IBL_MAN | (dev << IBL_V_DEV); +return SCPE_OK; +} diff --git a/HP2100/hp2100_fp.c b/HP2100/hp2100_fp.c new file mode 100644 index 0000000..7e431ba --- /dev/null +++ b/HP2100/hp2100_fp.c @@ -0,0 +1,408 @@ +/* hp2100_fp.c: HP 2100 floating point instructions + + Copyright (c) 2002-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 21-Jan-08 JDB Corrected fp_unpack mantissa high-word return + (from Mark Pizzolato) + 01-Dec-06 JDB Reworked FFP helpers for 1000-F support, deleted f_pwr2 + 22-Jul-05 RMS Fixed compiler warning in Solaris (from Doug Glyn) + 25-Feb-05 JDB Added FFP helpers f_pack, f_unpack, f_pwr2 + 11-Feb-05 JDB Fixed missing negative overflow renorm in StoreFP + 26-Dec-04 RMS Separated A/B from M[0/1] for DMA IO (from Dave Bryan) + 15-Jul-03 RMS Fixed signed/unsigned warning + 21-Oct-02 RMS Recoded for compatibility with 21MX microcode algorithms + + + Implementation note: The 2100/1000-M/E Fast FORTRAN Processor (FFP) and 1000 + F-Series Floating Point Processor (FPP) simulations require that the host + compiler support 64-bit integers and the HAVE_INT64 symbol be defined during + compilation. If this symbol is defined, two-word floating-point operations + are handled in the FPP code, and this module is not used. If it is not + defined, then FFP and FPP operations are not available, and this module + provides the floating-point support. + + + The HP2100 uses a unique binary floating point format: + + 15 14 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |S | fraction high | : A + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | fraction low | exponent |XS| : A + 1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + 15 8 7 1 0 + + where S = 0 for plus fraction, 1 for minus fraction + fraction = s.bbbbb..., 24 binary digits + exponent = 2**+/-n + XS = 0 for plus exponent, 1 for minus exponent + + Numbers can be normalized or unnormalized but are always normalized + when loaded. + + Unpacked floating point numbers are stored in structure ufp + + exp = exponent, 2's complement + h'l = fraction, 2's comp, left justified + + This routine tries to reproduce the algorithms of the 2100/21MX + microcode in order to achieve 'bug-for-bug' compatibility. In + particular, + + - The FIX code produces various results in B. + - The fraction multiply code uses 16b x 16b multiplies to produce + a 31b result. It always loses the low order bit of the product. + - The fraction divide code is an approximation that may produce + an error of 1 LSB. + - Signs are tracked implicitly as part of the fraction. Unnormalized + inputs may cause the packup code to produce the wrong sign. + - "Unclean" zeros (zero fraction, non-zero exponent) are processed + like normal operands. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu1.h" +#include "hp2100_fp.h" + +#if !defined (HAVE_INT64) /* int64 support unavailable */ + +struct ufp { /* unpacked fp */ + int32 exp; /* exp */ + uint32 fr; /* frac */ + }; + +#define FP_V_SIGN 31 /* sign */ +#define FP_M_SIGN 01 +#define FP_V_FR 8 /* fraction */ +#define FP_M_FR 077777777 +#define FP_V_EXP 1 /* exponent */ +#define FP_M_EXP 0177 +#define FP_V_EXPS 0 /* exp sign */ +#define FP_M_EXPS 01 +#define FP_SIGN (FP_M_SIGN << FP_V_SIGN) +#define FP_FR (FP_M_FR << FP_V_FR) +#define FP_EXP (FP_M_EXP << FP_V_EXP) +#define FP_EXPS (FP_M_EXPS << FP_V_EXPS) +#define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN) +#define FP_GETEXP(x) (((x) >> FP_V_EXP) & FP_M_EXP) +#define FP_GETEXPS(x) (((x) >> FP_V_EXPS) & FP_M_EXPS) + +#define FP_NORM (1 << (FP_V_SIGN - 1)) /* normalized */ +#define FP_LOW (1 << FP_V_FR) +#define FP_RNDP (1 << (FP_V_FR - 1)) /* round for plus */ +#define FP_RNDM (FP_RNDP - 1) /* round for minus */ + +#define FPAB ((((uint32) AR) << 16) | ((uint32) BR)) + +/* Fraction shift; 0 < shift < 32 */ + +#define FR_ARS(v,s) (((v) >> (s)) | (((v) & FP_SIGN)? \ + (((uint32) DMASK32) << (32 - (s))): 0)) & DMASK32 + +#define FR_NEG(v) ((~(v) + 1) & DMASK32) + +extern uint16 ABREG[2]; + +uint32 UnpackFP (struct ufp *fop, uint32 opnd); +void NegFP (struct ufp *fop); +void NormFP (struct ufp *fop); +uint32 PackFP (struct ufp *fop); +uint32 StoreFP (struct ufp *fop); + +/* Floating to integer conversion */ + +uint32 f_fix (void) +{ +struct ufp fop; +uint32 res = 0; + +UnpackFP (&fop, FPAB); /* unpack op */ +if (fop.exp < 0) { /* exp < 0? */ + AR = 0; /* result = 0 */ + return 0; /* B unchanged */ + } +if (fop.exp > 15) { /* exp > 15? */ + BR = AR; /* B has high bits */ + AR = 077777; /* result = 77777 */ + return 1; /* overflow */ + } +if (fop.exp < 15) { /* if not aligned */ + res = FR_ARS (fop.fr, 15 - fop.exp); /* shift right */ + AR = (res >> 16) & DMASK; /* AR gets result */ + } +BR = AR; +if ((AR & SIGN) && ((fop.fr | res) & DMASK)) /* any low bits lost? */ + AR = (AR + 1) & DMASK; /* round up */ +return 0; +} + +/* Integer to floating conversion */ + +uint32 f_flt (void) +{ +struct ufp res = { 15, 0 }; /* +, 2**15 */ + +res.fr = ((uint32) AR) << 16; /* left justify */ +StoreFP (&res); /* store result */ +return 0; /* clr overflow */ +} + +/* Floating point add/subtract */ + +uint32 f_as (uint32 opnd, t_bool sub) +{ +struct ufp fop1, fop2, t; +int32 ediff; + +UnpackFP (&fop1, FPAB); /* unpack A-B */ +UnpackFP (&fop2, opnd); /* get op */ +if (sub) { /* subtract? */ + fop2.fr = FR_NEG (fop2.fr); /* negate frac */ + if (fop2.fr == ((uint32) FP_SIGN)) { /* -1/2? */ + fop2.fr = fop2.fr >> 1; /* special case */ + fop2.exp = fop2.exp + 1; + } + } +if (fop1.fr == 0) fop1 = fop2; /* op1 = 0? res = op2 */ +else if (fop2.fr != 0) { /* op2 = 0? no add */ + if (fop1.exp < fop2.exp) { /* |op1| < |op2|? */ + t = fop2; /* swap operands */ + fop2 = fop1; + fop1 = t; + } + ediff = fop1.exp - fop2.exp; /* get exp diff */ + if (ediff <= 24) { + if (ediff) fop2.fr = FR_ARS (fop2.fr, ediff); /* denorm, signed */ + if ((fop1.fr ^ fop2.fr) & FP_SIGN) /* unlike signs? */ + fop1.fr = fop1.fr + fop2.fr; /* eff subtract */ + else { /* like signs */ + fop1.fr = fop1.fr + fop2.fr; /* eff add */ + if (fop2.fr & FP_SIGN) { /* both -? */ + if ((fop1.fr & FP_SIGN) == 0) { /* overflow? */ + fop1.fr = FP_SIGN | (fop1.fr >> 1); /* renormalize */ + fop1.exp = fop1.exp + 1; /* incr exp */ + } + } + else if (fop1.fr & FP_SIGN) { /* both +, cry out? */ + fop1.fr = fop1.fr >> 1; /* renormalize */ + fop1.exp = fop1.exp + 1; /* incr exp */ + } + } /* end else like */ + } /* end if ediff */ + } /* end if fop2 */ +return StoreFP (&fop1); /* store result */ +} + +/* Floating point multiply - passes diagnostic */ + +uint32 f_mul (uint32 opnd) +{ +struct ufp fop1, fop2; +struct ufp res = { 0, 0 }; +int32 shi1, shi2, t1, t2, t3, t4, t5; + +UnpackFP (&fop1, FPAB); /* unpack A-B */ +UnpackFP (&fop2, opnd); /* unpack op */ +if (fop1.fr && fop2.fr) { /* if both != 0 */ + res.exp = fop1.exp + fop2.exp + 1; /* exp = sum */ + shi1 = SEXT (fop1.fr >> 16); /* mpy hi */ + shi2 = SEXT (fop2.fr >> 16); /* mpc hi */ + t1 = shi2 * ((int32) ((fop1.fr >> 1) & 077600)); /* mpc hi * (mpy lo/2) */ + t2 = shi1 * ((int32) ((fop2.fr >> 1) & 077600)); /* mpc lo * (mpy hi/2) */ + t3 = t1 + t2; /* cross product */ + t4 = (shi1 * shi2) & ~1; /* mpy hi * mpc hi */ + t5 = (SEXT (t3 >> 16)) << 1; /* add in cross */ + res.fr = (t4 + t5) & DMASK32; /* bit<0> is lost */ + } +return StoreFP (&res); /* store */ +} + +/* Floating point divide - reverse engineered from diagnostic */ + +uint32 divx (uint32 ba, uint32 dvr, uint32 *rem) +{ +int32 sdvd = 0, sdvr = 0; +uint32 q, r; + +if (ba & FP_SIGN) sdvd = 1; /* 32b/16b signed dvd */ +if (dvr & SIGN) sdvr = 1; /* use old-fashioned */ +if (sdvd) ba = (~ba + 1) & DMASK32; /* unsigned divides, */ +if (sdvr) dvr = (~dvr + 1) & DMASK; /* as results may ovflo */ +q = ba / dvr; +r = ba % dvr; +if (sdvd ^ sdvr) q = (~q + 1) & DMASK; +if (sdvd) r = (~r + 1) & DMASK; +if (rem) *rem = r; +return q; +} + +uint32 f_div (uint32 opnd) +{ +struct ufp fop1, fop2; +struct ufp quo = { 0, 0 }; +uint32 ba, q0, q1, q2, dvrh; + +UnpackFP (&fop1, FPAB); /* unpack A-B */ +UnpackFP (&fop2, opnd); /* unpack op */ +dvrh = (fop2.fr >> 16) & DMASK; /* high divisor */ +if (dvrh == 0) { /* div by zero? */ + AR = 0077777; /* return most pos */ + BR = 0177776; + return 1; + } +if (fop1.fr) { /* dvd != 0? */ + quo.exp = fop1.exp - fop2.exp + 1; /* exp = diff */ + ba = FR_ARS (fop1.fr, 2); /* prevent ovflo */ + q0 = divx (ba, dvrh, &ba); /* Q0 = dvd / dvrh */ + ba = (ba & ~1) << 16; /* remainder */ + ba = FR_ARS (ba, 1); /* prevent ovflo */ + q1 = divx (ba, dvrh, NULL); /* Q1 = rem / dvrh */ + ba = (fop2.fr & 0xFF00) << 13; /* dvrl / 8 */ + q2 = divx (ba, dvrh, NULL); /* dvrl / dvrh */ + ba = -(SEXT (q2)) * (SEXT (q0)); /* -Q0 * Q2 */ + ba = (ba >> 16) & 0xFFFF; /* save ms half */ + if (q1 & SIGN) quo.fr = quo.fr - 0x00010000; /* Q1 < 0? -1 */ + if (ba & SIGN) quo.fr = quo.fr - 0x00010000; /* -Q0*Q2 < 0? */ + quo.fr = quo.fr + ((ba << 2) & 0xFFFF) + q1; /* rest prod, add Q1 */ + quo.fr = quo.fr << 1; /* shift result */ + quo.fr = quo.fr + (q0 << 16); /* add Q0 */ + } /* end if fop1.h */ +return StoreFP (&quo); /* store result */ +} + +/* Utility routines */ + +/* Unpack operand */ + +uint32 UnpackFP (struct ufp *fop, uint32 opnd) +{ +fop->fr = opnd & FP_FR; /* get frac */ +fop->exp = FP_GETEXP (opnd); /* get exp */ +if (FP_GETEXPS (opnd)) fop->exp = fop->exp | ~FP_M_EXP; /* < 0? sext */ +return FP_GETSIGN (opnd); /* return sign */ +} + +/* Normalize unpacked floating point number */ + +void NormFP (struct ufp *fop) +{ +if (fop->fr) { /* any fraction? */ + uint32 test = (fop->fr >> 1) & FP_NORM; + while ((fop->fr & FP_NORM) == test) { /* until norm */ + fop->exp = fop->exp - 1; + fop->fr = (fop->fr << 1); + } + } +else fop->exp = 0; /* clean 0 */ +return; +} + +/* Pack fp number */ + +uint32 PackFP (struct ufp *fop) +{ +return (fop->fr & FP_FR) | /* merge frac */ + ((fop->exp & FP_M_EXP) << FP_V_EXP) | /* and exp */ + ((fop->exp < 0)? (1 << FP_V_EXPS): 0); /* add exp sign */ +} + +/* Round fp number, store, generate overflow */ + +uint32 StoreFP (struct ufp *fop) +{ +uint32 sign, svfr, hi, ov = 0; + +NormFP (fop); /* normalize */ +svfr = fop->fr; /* save fraction */ +sign = FP_GETSIGN (fop->fr); /* save sign */ +fop->fr = (fop->fr + (sign? FP_RNDM: FP_RNDP)) & FP_FR; /* round */ +if ((fop->fr ^ svfr) & FP_SIGN) { /* sign change? */ + fop->fr = fop->fr >> 1; /* renormalize */ + fop->exp = fop->exp + 1; + } +else NormFP (fop); /* check for norm */ +if (fop->fr == 0) hi = 0; /* result 0? */ +else if (fop->exp < -(FP_M_EXP + 1)) { /* underflow? */ + hi = 0; /* store clean 0 */ + ov = 1; + } +else if (fop->exp > FP_M_EXP) { /* overflow? */ + hi = 0x7FFFFFFE; /* all 1's */ + ov = 1; + } +else hi = PackFP (fop); /* pack mant and exp */ +AR = (hi >> 16) & DMASK; +BR = hi & DMASK; +return ov; +} + + +/* Single-precision Fast FORTRAN Processor helpers. */ + +/* Pack mantissa and exponent and return fp value. */ + +uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) +{ +struct ufp fop; +uint32 val; + +fop.fr = ((uint32) mantissa.fpk[0] << 16) | mantissa.fpk[1]; +fop.exp = exponent; +val = PackFP (&fop); +result->fpk[0] = (int16) (val >> 16); +result->fpk[1] = (int16) val; +return 0; +} + +/* Normalize, round, and pack mantissa and exponent and return fp value. */ + +uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) +{ +struct ufp fop; +uint32 ovf; + +fop.fr = ((uint32) mantissa.fpk[0] << 16) | mantissa.fpk[1]; +fop.exp = exponent; +ovf = StoreFP (&fop); +result->fpk[0] = AR; +result->fpk[1] = BR; +return ovf; +} + +/* Unpack fp number in into mantissa and exponent. */ + +uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision) +{ +struct ufp fop; +uint32 operand; + +operand = ((uint32) packed.fpk[0] << 16) | packed.fpk[1]; +UnpackFP (&fop, operand); +mantissa->fpk[0] = (uint16) (fop.fr >> 16); +mantissa->fpk[1] = (uint16) fop.fr; +*exponent = fop.exp; +return 0; +} + +#endif /* int64 support unavailable */ diff --git a/HP2100/hp2100_fp.h b/HP2100/hp2100_fp.h new file mode 100644 index 0000000..4809da4 --- /dev/null +++ b/HP2100/hp2100_fp.h @@ -0,0 +1,47 @@ +/* hp2100_fp.h: HP 2100/21MX floating point definitions + + Copyright (c) 2002-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 01-Dec-06 JDB Reworked FFP helpers for 1000-F support, deleted f_pwr2 + 26-Sep-06 JDB Moved from hp2100_fp.c to simplify extensions +*/ + +#ifndef _HP2100_FP_H_ +#define _HP2100_FP_H_ + +/* Firmware floating-point routines */ + +uint32 f_as (uint32 op, t_bool sub); /* FAD/FSB */ +uint32 f_mul (uint32 op); /* FMP */ +uint32 f_div (uint32 op); /* FDV */ +uint32 f_fix (void); /* FIX */ +uint32 f_flt (void); /* FLT */ + +/* Firmware FFP helpers */ + +uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); +uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); +uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision); + +#endif diff --git a/HP2100/hp2100_fp1.c b/HP2100/hp2100_fp1.c new file mode 100644 index 0000000..21793fd --- /dev/null +++ b/HP2100/hp2100_fp1.c @@ -0,0 +1,1431 @@ +/* hp2100_fp1.c: HP 1000 multiple-precision floating point routines + + Copyright (c) 2005-2008, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be used + in advertising or otherwise to promote the sale, use or other dealings in + this Software without prior written authorization from the author. + + 10-May-08 JDB Fixed uninitialized return in fp_accum when setting + 19-Mar-08 JDB Reworked "complement" to avoid inlining bug in gcc-4.x + 01-Dec-06 JDB Reworked into generalized multiple-precision ops for FPP + 12-Oct-06 JDB Altered x_trun for F-Series FFP compatibility + Added F-Series ..TCM FFP helpers + + Primary references: + - HP 1000 M/E/F-Series Computers Engineering and Reference Documentation + (92851-90001, Mar-1981) + - HP 1000 M/E/F-Series Computers Technical Reference Handbook + (5955-0282, Mar-1980) + - DOS/RTE Relocatable Library Reference Manual + (24998-90001, Oct-1981) + + + This module implements multiple-precision floating-point operations to + support the 1000 F-Series hardware Floating Point Processor. It employs + 64-bit integer arithmetic for speed and simplicity of implementation. The + host compiler must support 64-bit integers, and the HAVE_INT64 symbol must be + defined during compilation. If this symbol is not defined, then FPP support + is not available. + + HP 2100/1000 computers used a proprietary floating-point format. The 2100 + had optional firmware that provided two-word floating-point add, subtract, + multiply, and divide, as well as single-integer fix and float. The 1000-M/E + provided the same two-word firmware operations as standard equipment. + Three-word extended-precision instructions for the 2100 and 1000-M/E were + provided by the optional Fast FORTRAN Processor firmware. + + The 1000-F substituted a hardware floating point processor for the firmware + in previous machines. In addition to the two- and three-word formats, the + F-Series introduced a four-word double-precision format. A five-word format + that provided extra range in the exponent by unpacking it from the mantissa + was also provided, although this capability was not documented in the user + manual. In addition, the FPP improved the accuracy of floating-point + calculations, as the firmware versions sacrificed a few bits of precision to + gain speed. Consequently, operations on the F-Series may return results that + differ slightly from the same operations on the M/E-Series or the 2100. + + F-Series units after date code 1920 also provided two-word double-integer + instructions in firmware, as well as double-integer fix and float operations. + + The original 32-bit floating-point format is as follows: + + 15 14 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |MS| mantissa high | : M + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa low | exponent |XS| : M + 1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + 15 8 7 1 0 + + Both 23-bit mantissa and 7-bit exponent are in twos-complement form. The + exponent sign bit has been rotated into the LSB of the second word. + + The extended-precision floating-point format is a 48-bit extension of the + 32-bit format used for single precision. A packed extended-precision value + consists of a 39-bit mantissa and a 7-bit exponent. The format is as + follows: + + 15 14 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |MS| mantissa high | : M + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa middle | : M + 1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa low | exponent |XS| : M + 2 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + 15 8 7 1 0 + + The double-precision floating-point format is similar to the 48-bit + extended-precision format, although with a 55-bit mantissa: + + 15 14 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |MS| mantissa high | : M + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa middle high | : M + 1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa middle low | : M + 2 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa low | exponent |XS| : M + 3 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + 15 8 7 1 0 + + The FPP also supports a special five-word expanded-exponent format: + + 15 14 0 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |MS| mantissa high | : M + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa middle high | : M + 1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa middle low | : M + 2 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | mantissa low | : M + 3 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | exponent |XS| : M + 4 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + 15 8 7 1 0 + + The exponent is a full 16-bit twos-complement value, but the allowed range is + only 10 bits, i.e., -512 to +511. + + In a normalized value, the sign and MSB of the mantissa differ. Zero is + represented by all words = 0. + + Internally, unpacked floating-point values are contained in a structure + having a signed 64-bit mantissa and a signed 32-bit exponent. Mantissas are + left-justified with the unused bits masked to zero. Exponents are + right-justified. The precision is indicated by the value of a structure + field. + + HP terminology for the three-word floating-point format is confused. Some + documents refer to it as "double precision," while others use "extended + precision." The instruction mnemonics begin with "X" (e.g., .XADD), + suggesting the extended-precision term. + + HP apparently intended that the four-word double-precision format would be + called "triple-precision," as the instruction mnemonics begin with "T" (e.g., + ".TADD" for the four-word add instruction). The source files for the + software simulations of these instructions for the M/E-Series also explicitly + refer to "triple precision math." However, the engineering documentation and + the F-Series reference manual both use the double-precision term. + + This module adopts the single/extended/double terminology and uses the + initial letters of the instructions (F/X/T) to indicate the precision used. + + The FPP hardware consisted of two circuit boards that interfaced to the main + CPU via the Microprogammable Processor Port (MPP) that had been introduced + with the 1000 E-Series. One board contained argument registers and ALUs, + split into separate mantissa and exponent parts. The other contained a state + machine sequencer. FPP results were copied automatically to the argument + registers in addition to being available over the MPP, so that chained + operations could be executed from these "accumulators" without reloading. + + The FPP operated independently of the CPU. An opcode, specifying one of the + six operations (add, subtract, multiply, divide, fix, or float) was sent to + the FPP, and a start command was given. Operands of appropriate precision + were then supplied to the FPP. Once the operands were received, the FPP + would execute and set a flag when the operation was complete. The result + would then be retrieved from the FPP. The floating-point instruction + firmware in the CPU initiated the desired FPP operation and handled operand + reads from and result writes to main memory. + + Under simulation, "fp_exec" provides the six arithmetic operations analogous + to FPP execution. The remainder of the functions are helpers that were + provided by firmware in the 1000-F but that can reuse code needed to simulate + the FPP hardware. As with the hardware, "fp_exec" retains the last result + in an internal accumulator that may be referenced in subsequent operations. + + NOTE: this module also provides the floating-point support for the firmware + single-precision 1000-M/E base set and extended-precision FFP instructions. + Because the firmware and hardware implementations returned slightly different + results, particularly with respect to round-off, conditional checks are + implemented in the arithmetic routines. In some cases, entirely different + algorithms are used to ensure fidelity with the real machines. Functionally, + this means that the 2100/1000-M/E and 1000-F floating-point diagnostics are + not interchangeable, and failures are to be expected if a diagnostic is run + on the wrong machine. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include "hp2100_cpu1.h" +#include "hp2100_fp1.h" + + +#if defined (HAVE_INT64) /* we need int64 support */ + +/* Field widths. */ + +#define IN_W_SIGN 1 +#define IN_W_SMAGN 15 +#define IN_W_DMAGN 31 + +#define FP_W_MSIGN 1 +#define FP_W_FMANT 23 +#define FP_W_XMANT 39 +#define FP_W_TMANT 55 +#define FP_W_EMANT 55 +#define FP_W_EXPANDEXP 9 +#define FP_W_EXP 7 +#define FP_W_ESIGN 1 + +/* Starting bit numbers. */ + +#define IN_V_SIGN (64 - IN_W_SIGN) +#define IN_V_SNUM (64 - IN_W_SIGN - IN_W_SMAGN) +#define IN_V_DNUM (64 - IN_W_SIGN - IN_W_DMAGN) + +#define FP_V_FNUM (64 - FP_W_MSIGN - FP_W_FMANT - FP_W_EXP - FP_W_ESIGN) +#define FP_V_XNUM (64 - FP_W_MSIGN - FP_W_XMANT - FP_W_EXP - FP_W_ESIGN) +#define FP_V_TNUM (64 - FP_W_MSIGN - FP_W_TMANT - FP_W_EXP - FP_W_ESIGN) +#define FP_V_ENUM (64 - FP_W_MSIGN - FP_W_EMANT - FP_W_EXP - FP_W_ESIGN) + +#define FP_V_MSIGN (64 - FP_W_MSIGN) +#define FP_V_FMANT (64 - FP_W_MSIGN - FP_W_FMANT) +#define FP_V_XMANT (64 - FP_W_MSIGN - FP_W_XMANT) +#define FP_V_TMANT (64 - FP_W_MSIGN - FP_W_TMANT) +#define FP_V_EMANT (64 - FP_W_MSIGN - FP_W_EMANT) +#define FP_V_EXP 1 +#define FP_V_ESIGN 0 + +/* Right-aligned field masks. */ + +#define IN_M_SIGN (((t_uint64) 1 << IN_W_SIGN) - 1) +#define IN_M_SMAGN (((t_uint64) 1 << IN_W_SMAGN) - 1) +#define IN_M_DMAGN (((t_uint64) 1 << IN_W_DMAGN) - 1) + +#define FP_M_MSIGN (((t_uint64) 1 << FP_W_MSIGN) - 1) +#define FP_M_FMANT (((t_uint64) 1 << FP_W_FMANT) - 1) +#define FP_M_XMANT (((t_uint64) 1 << FP_W_XMANT) - 1) +#define FP_M_TMANT (((t_uint64) 1 << FP_W_TMANT) - 1) +#define FP_M_EMANT (((t_uint64) 1 << FP_W_EMANT) - 1) + +#define FP_M_EXPANDEXP ((1 << FP_W_EXPANDEXP) - 1) +#define FP_M_EXP ((1 << FP_W_EXP) - 1) +#define FP_M_ESIGN ((1 << FP_W_ESIGN) - 1) + +/* In-place field masks. */ + +#define IN_SIGN (IN_M_SIGN << IN_V_SIGN) +#define IN_SMAGN (IN_M_SMAGN << IN_V_SNUM) +#define IN_DMAGN (IN_M_DMAGN << IN_V_DNUM) + +#define FP_MSIGN (FP_M_MSIGN << FP_V_MSIGN) +#define FP_FMANT (FP_M_FMANT << FP_V_FMANT) +#define FP_XMANT (FP_M_XMANT << FP_V_XMANT) +#define FP_TMANT (FP_M_TMANT << FP_V_TMANT) +#define FP_EMANT (FP_M_EMANT << FP_V_EMANT) +#define FP_EXP (FP_M_EXP << FP_V_EXP) +#define FP_ESIGN (FP_M_ESIGN << FP_V_ESIGN) + +/* In-place record masks. */ + +#define IN_SSMAGN (IN_SIGN | IN_SMAGN) +#define IN_SDMAGN (IN_SIGN | IN_DMAGN) + +#define FP_SFMANT (FP_MSIGN | FP_FMANT) +#define FP_SXMANT (FP_MSIGN | FP_XMANT) +#define FP_STMANT (FP_MSIGN | FP_TMANT) +#define FP_SEMANT (FP_MSIGN | FP_EMANT) +#define FP_SEXP (FP_ESIGN | FP_EXP) + +/* Minima and maxima. */ + +#define FP_ONEHALF ((t_int64) 1 << (FP_V_MSIGN - 1)) /* mantissa = 0.5 */ +#define FP_MAXPMANT ((t_int64) FP_EMANT) /* maximum pos mantissa */ +#define FP_MAXNMANT ((t_int64) FP_MSIGN) /* maximum neg mantissa */ +#define FP_MAXPEXP (FP_M_EXPANDEXP) /* maximum pos expanded exponent */ +#define FP_MAXNEXP (-(FP_MAXPEXP + 1)) /* maximum neg expanded exponent */ + +/* Floating-point helpers. */ + +#define DENORM(x) ((((x) ^ (x) << 1) & FP_MSIGN) == 0) + +#define TO_EXP(e) (int8) ((e >> FP_V_EXP & FP_M_EXP) | \ + (e & FP_M_ESIGN ? ~FP_M_EXP : 0)) + +/* Property constants. */ + +static const t_int64 p_half_lsb[6] = { ((t_int64) 1 << IN_V_SNUM) - 1, /* different than FP! */ + ((t_int64) 1 << IN_V_DNUM) - 1, /* different than FP! */ + (t_int64) 1 << (FP_V_FMANT - 1), + (t_int64) 1 << (FP_V_XMANT - 1), + (t_int64) 1 << (FP_V_TMANT - 1), + (t_int64) 1 << (FP_V_EMANT - 1) }; + +static const t_int64 n_half_lsb[6] = { 0, + 0, + ((t_int64) 1 << (FP_V_FMANT - 1)) - 1, + ((t_int64) 1 << (FP_V_XMANT - 1)) - 1, + ((t_int64) 1 << (FP_V_TMANT - 1)) - 1, + ((t_int64) 1 << (FP_V_EMANT - 1)) - 1 }; + +static const uint32 op_start[6] = { IN_V_SNUM, + IN_V_DNUM, + FP_V_FMANT, + FP_V_XMANT, + FP_V_TMANT, + FP_V_EMANT }; + +static const t_int64 mant_mask[6] = { IN_SSMAGN, + IN_SDMAGN, + FP_SFMANT, + FP_SXMANT, + FP_STMANT, + FP_SEMANT }; + +static const uint32 op_bits[6] = { IN_W_SMAGN, + IN_W_DMAGN, + FP_W_FMANT + FP_W_MSIGN, + FP_W_XMANT + FP_W_MSIGN, + FP_W_TMANT + FP_W_MSIGN, + FP_W_EMANT + FP_W_MSIGN }; + +static const t_int64 op_mask[6] = { ~(t_int64) 0 << IN_V_SNUM, + ~(t_int64) 0 << IN_V_DNUM, + ~(t_int64) 0 << FP_V_FNUM, + ~(t_int64) 0 << FP_V_XNUM, + ~(t_int64) 0 << FP_V_TNUM, + ~(t_int64) 0 << FP_V_ENUM }; + +static const uint32 int_p_max[2] = { IN_M_SMAGN, + IN_M_DMAGN }; + + +/* Internal unpacked floating-point representation. */ + +typedef struct { + t_int64 mantissa; + int32 exponent; + OPSIZE precision; + } FPU; + + + +/* Low-level helper routines. */ + + +/* Arithmetic shift right for mantissa only. + + Returns TRUE if any one-bits are shifted out (for F-series only). +*/ + +static t_bool asr (FPU *operand, int32 shift) +{ +t_uint64 mask; +t_bool bits_lost; + +if (UNIT_CPU_MODEL == UNIT_1000_F) { /* F-series? */ + mask = ((t_uint64) 1 << shift) - 1; /* mask for lost bits */ + bits_lost = ((operand->mantissa & mask) != 0); /* flag if any lost */ + } +else + bits_lost = FALSE; + +operand->mantissa = operand->mantissa >> shift; /* mantissa is int, so ASR */ +return bits_lost; +} + + +/* Logical shift right for mantissa and exponent. + + Shifts mantissa and corrects exponent for mantissa overflow. + Returns TRUE if any one-bits are shifted out (for F-series only). +*/ + +static t_bool lsrx (FPU *operand, int32 shift) +{ +t_uint64 mask; +t_bool bits_lost; + +if (UNIT_CPU_MODEL == UNIT_1000_F) { /* F-series? */ + mask = ((t_uint64) 1 << shift) - 1; /* mask for lost bits */ + bits_lost = ((operand->mantissa & mask) != 0); /* flag if any lost */ + } +else + bits_lost = FALSE; + +operand->mantissa = (t_uint64) operand->mantissa >> shift; /* uint, so LSR */ +operand->exponent = operand->exponent + shift; /* correct exponent */ +return bits_lost; +} + + +/* Unpack an operand into a long integer. + + Returns a left-aligned integer or mantissa. Does not mask to precision; this + should be done subsequently if desired. +*/ + +static t_int64 unpack_int (OP packed, OPSIZE precision) +{ +uint32 i; +t_uint64 unpacked = 0; + +if (precision == in_s) + unpacked = (t_uint64) packed.word << 48; /* unpack single integer */ + +else if (precision == in_d) + unpacked = (t_uint64) packed.dword << 32; /* unpack double integer */ + +else { + if (precision == fp_e) /* five word operand? */ + precision = fp_t; /* only four mantissa words */ + + for (i = 0; i < 4; i++) /* unpack fp 2 to 4 words */ + if (i < TO_COUNT (precision)) + unpacked = unpacked << 16 | packed.fpk[i]; + else + unpacked = unpacked << 16; + } + +return (t_int64) unpacked; +} + + +/* Unpack a packed operand. + + The packed value is split into separate mantissa and exponent variables. The + multiple words of the mantissa are concatenated into a single 64-bit signed + value, and the exponent is shifted with recovery of the sign. +*/ + +static FPU unpack (OP packed, OPSIZE precision) +{ +FPU unpacked; + +unpacked.precision = precision; /* set value's precision */ + +unpacked.mantissa = /* unpack and mask mantissa */ + unpack_int (packed, precision) & mant_mask[precision]; + +switch (precision) { + + case fp_f: + case fp_x: + case fp_t: + unpacked.exponent = /* unpack exponent from correct word */ + TO_EXP (packed.fpk[(uint32) precision - 1]); + break; + + case fp_e: + unpacked.exponent = /* unpack expanded exponent */ + (int16) (packed.fpk[4] >> FP_V_EXP | /* rotate sign into place */ + (packed.fpk[4] & 1 ? SIGN : 0)); + break; + + case fp_a: /* no action for value in accum */ + case in_s: /* integers don't use exponent */ + case in_d: /* integers don't use exponent */ + default: + unpacked.exponent = 0; + break; + } + +return unpacked; +} + + +/* Pack a long integer into an operand. */ + +static OP pack_int (t_int64 unpacked, OPSIZE precision) +{ +int32 i; +OP packed; + +if (precision == in_s) + packed.word = (uint16) (unpacked >> 48) & DMASK; /* pack single integer */ + +else if (precision == in_d) + packed.dword = (uint32) (unpacked >> 32) & DMASK32; /* pack double integer */ + +else { + if (precision == fp_e) /* five word operand? */ + precision = fp_t; /* only four mantissa words */ + + for (i = 3; i >= 0; i--) { /* pack fp 2 to 4 words */ + packed.fpk[i] = (uint16) unpacked & DMASK; + unpacked = unpacked >> 16; + } + } + +return packed; +} + + +/* Pack an unpacked floating-point number. + + The 64-bit mantissa is split into the appropriate number of 16-bit words. + The exponent is rotated to incorporate the sign bit and merged into the + appropriate word. +*/ + +static OP pack (FPU unpacked) +{ +OP packed; +uint8 exp; + +packed = pack_int (unpacked.mantissa, unpacked.precision); /* pack mantissa */ + +exp = ((uint8) unpacked.exponent << FP_V_EXP) | /* rotate exponent */ + ((unpacked.exponent < 0) << FP_V_ESIGN); + +switch (unpacked.precision) { /* merge exponent into correct word */ + + case in_s: /* no action for integers */ + case in_d: + break; + + case fp_f: /* merge into last word */ + case fp_x: + case fp_t: + packed.fpk[(uint32) unpacked.precision - 1] = + (packed.fpk[(uint32) unpacked.precision - 1] & ~FP_SEXP) | exp; + break; + + case fp_e: /* place in separate word */ + packed.fpk[4] = ((uint16) unpacked.exponent << FP_V_EXP) | + ((unpacked.exponent < 0) << FP_V_ESIGN); + break; + + case fp_a: /* no action for value in accum */ + break; + } + +return packed; +} + + +/* Normalize an unpacked floating-point number. + + Floating-point numbers are in normal form if the sign bit and the MSB of the + mantissa differ. Unnormalized numbers are shifted as needed with appropriate + exponent modification. +*/ + +static void normalize (FPU *unpacked) +{ + +if (unpacked->mantissa) /* non-zero? */ + while (DENORM (unpacked->mantissa)) { /* normal form? */ + unpacked->exponent = unpacked->exponent - 1; /* no, so left shift */ + unpacked->mantissa = unpacked->mantissa << 1; + } +else + unpacked->exponent = 0; /* clean for zero */ +return; +} + + +/* Round an unpacked floating-point number and check for overflow. + + An unpacked floating-point number is rounded by adding one-half of the LSB + value, maintaining symmetry around zero. If rounding resulted in a mantissa + overflow, the result logically is shifted to the right with an appropriate + exponent modification. Finally, the result is checked for exponent underflow + or overflow, and the appropriate approximation (zero or infinity) is + returned. + + Rounding in hardware involves a special mantissa extension register that + holds three "guard" bits and one "sticky" bit. These represent the value of + bits right-shifted out the mantissa register. Under simulation, we track + such right-shifts and utilize the lower eight bits of the 64-bit mantissa + value to simulate the extension register. + + Overflow depends on whether the FPP expanded-exponent form is being used + (this expands the exponent range by two bits). If overflow is detected, the + value representing infinity is dependent on whether the operation is on + behalf of the Fast FORTRAN Processor. The F-Series FPP returns positive + infinity on both positive and negative overflow for all precisions. The 2100 + and M/E-Series FFPs return negative infinity on negative overflow of + extended-precision values. Single-precision overflows on these machines + always return positive infinity. + + The number to be rounded must be normalized upon entry. +*/ + +static uint32 roundovf (FPU *unpacked, t_bool expand) +{ +uint32 overflow; +t_bool sign; + +sign = (unpacked->mantissa < 0); /* save mantissa sign */ + +if (sign) /* round and mask the number */ + unpacked->mantissa = + (unpacked->mantissa + n_half_lsb[unpacked->precision]) & + mant_mask[unpacked->precision]; +else + unpacked->mantissa = + (unpacked->mantissa + p_half_lsb[unpacked->precision]) & + mant_mask[unpacked->precision]; + +if (sign != (unpacked->mantissa < 0)) /* mantissa overflow? */ + lsrx (unpacked, 1); /* correct by shifting */ +else + normalize (unpacked); /* renorm may be needed */ + +if (unpacked->mantissa == 0) { /* result zero? */ + unpacked->mantissa = 0; /* return zero */ + unpacked->exponent = 0; + overflow = 0; /* with overflow clear */ + } +else if (unpacked->exponent < /* result underflow? */ + (FP_MAXNEXP >> (expand ? 0 : 2))) { + unpacked->mantissa = 0; /* return zero */ + unpacked->exponent = 0; + overflow = 1; /* and set overflow */ + } +else if (unpacked->exponent > /* result overflow? */ + (FP_MAXPEXP >> (expand ? 0 : 2))) { + if (sign && /* negative value? */ + (unpacked->precision == fp_x) && /* extended precision? */ + (UNIT_CPU_MODEL != UNIT_1000_F)) { /* not F-series? */ + unpacked->mantissa = FP_MAXNMANT; /* return negative infinity */ + unpacked->exponent = FP_MAXPEXP & FP_M_EXP; + } + else { + unpacked->mantissa = FP_MAXPMANT; /* return positive infinity */ + unpacked->exponent = FP_MAXPEXP & FP_M_EXP; + } + overflow = 1; /* and set overflow */ + } +else + overflow = 0; /* value is in range */ + +return overflow; +} + + +/* Normalize, round, and pack an unpacked floating-point number. */ + +static uint32 nrpack (OP *packed, FPU unpacked, t_bool expand) +{ +uint32 overflow; + +normalize (&unpacked); /* normalize for rounding */ +overflow = roundovf (&unpacked, expand); /* round and check for overflow */ +*packed = pack (unpacked); /* pack result */ + +return overflow; +} + + + +/* Low-level arithmetic routines. */ + + +/* Complement an unpacked number. */ + +static void complement (FPU *result) +{ +if (result->mantissa == FP_MAXNMANT) { /* maximum negative? */ + result->mantissa = FP_ONEHALF; /* complement of -1.0 * 2 ^ n */ + result->exponent = result->exponent + 1; /* is 0.5 * 2 ^ (n + 1) */ + } +else + result->mantissa = -result->mantissa; /* negate mantissa */ +return; +} + + +/* Add two unpacked numbers. + + The mantissas are first aligned if necessary by scaling the smaller of the + two operands. If the magnitude of the difference between the exponents is + greater than the number of significant bits, then the smaller number has been + scaled to zero (swamped), and so the sum is simply the larger operand. + Otherwise, the sum is computed and checked for overflow, which has occured if + the signs of the operands are the same but differ from that of the result. + Scaling and renormalization is perfomed if overflow occurred. +*/ + +static void add (FPU *sum, FPU augend, FPU addend) +{ +int32 magn; +t_bool bits_lost; + +if (augend.mantissa == 0) + *sum = addend; /* X + 0 = X */ + +else if (addend.mantissa == 0) + *sum = augend; /* 0 + X = X */ + +else { + magn = augend.exponent - addend.exponent; /* difference exponents */ + + if (magn > 0) { /* addend smaller? */ + *sum = augend; /* preset augend */ + bits_lost = asr (&addend, magn); /* align addend */ + } + else { /* augend smaller? */ + *sum = addend; /* preset addend */ + magn = -magn; /* make difference positive */ + bits_lost = asr (&augend, magn); /* align augend */ + } + + if (magn <= (int32) op_bits[augend.precision]) { /* value swamped? */ + sum->mantissa = /* no, add mantissas */ + addend.mantissa + augend.mantissa; + + if (((addend.mantissa < 0) == (augend.mantissa < 0)) && /* mantissa overflow? */ + ((addend.mantissa < 0) != (sum->mantissa < 0))) { + bits_lost = bits_lost | lsrx (sum, 1); /* restore value */ + sum->mantissa = /* restore sign */ + sum-> mantissa | (addend.mantissa & FP_MSIGN); + } + + if (bits_lost) /* any bits lost? */ + sum->mantissa = sum->mantissa | 1; /* include one for rounding */ + } + } +return; +} + + +/* Multiply two unpacked numbers. + + The single-precision firmware (FMP) operates differently from the firmware + extended-precision (.XMPY) and the hardware multiplies of any precision. + Firmware implementations form 16-bit x 16-bit = 32-bit partial products and + sum them to form the result. The hardware uses a series of shifts and adds. + This means that firmware FMP and hardware FMP return slightly different + values, as may be seen by attempting to run the firmware FMP diagnostic on + the FPP. + + The FMP microcode calls a signed multiply routine to calculate three partial + products (all but LSB * LSB). Because the LSBs are unsigned, i.e., all bits + significant, the two MSB * LSB products are calculated using LSB/2. The + unsigned right-shift ensures a positive LSB with no significant bits lost, + because the lower eight bits are unused (they held the vacated exponent). In + order to sum the partial products, the LSB of the result of MSB * MSB is also + right-shifted before addition. Note, though, that this loses a significant + bit. After summation, the result is left-shifted to correct for the original + right shifts. + + The .XMPY microcode negates both operands as necessary to produce positive + values and then forms six of the nine 16-bit x 16-bit = 32-bit unsigned + multiplications required for a full 96-bit product. Given a 48-bit + multiplicand "a1a2a3" and a 48-bit multiplier "b1b2b3", the firmware performs + these calculations to develop a 48-bit product: + + a1 a2 a3 + +-------+-------+-------+ + b1 b2 b3 + +-------+-------+-------+ + _________________________ + + a1 * b3 [p1] + +-------+-------+ + a2 * b2 [p2] + +-------+-------+ + a1 * b2 [p3] + +-------+-------+ + a3 * b1 [p4] + +-------+-------+ + a2 * b1 [p5] + +-------+-------+ + a1 * b1 [p6] + +-------+-------+ + _________________________________ + + product + +-------+-------+-------+ + + The least-significant words of partial products [p1], [p2], and [p4] are used + only to develop a carry bit into the 48-bit sum. The product is complemented + as necessary to restore the sign. + + The basic FPP hardware algorithm scans the multiplier and adds a shifted copy + of the multiplicand whenever a one-bit is detected. To avoid successive adds + when a string of ones is encountered (because adds are more expensive than + shifts), the hardware instead adds the multiplicand shifted by N+1+P and + subtracts the multiplicand shifted by P to obtain the equivalent value with a + maximum of two operations. + + Instead of implementing either the .XMPY firmware algorithm or the hardware + shift-and-add algorithm directly, it is more efficient under simulation to + use 32 x 32 = 64-bit multiplications, thereby reducing the number required + from six to four (64-bit "c1c2" x 64-bit "d1d2"): + + ah al + +-------+-------+ + bh bl + +-------+-------+ + _________________ + + al * bl [ll] + +-------+-------+ + ah * bl [hl] + +-------+-------+ + al * bh [lh] + +-------+-------+ + ah * bh [hh] + +-------+-------+ + _________________________________ + + product + +-------+-------+ + + However, the FMP algorithm is implemented directly from the microcode to + preserve the fidelity of the simulation, i.e., to lose the same amount + of precision. +*/ + +static void multiply (FPU *product, FPU multiplicand, FPU multiplier) +{ +uint32 ah, al, bh, bl, sign = 0; +t_uint64 hh, hl, lh, ll, carry; +int16 ch, cl, dh, dl; +t_bool firmware; + +product->precision = multiplicand.precision; /* set precision */ + +if ((multiplicand.mantissa == 0) || /* 0 * X = 0 */ + (multiplier.mantissa == 0)) /* X * 0 = 0 */ + product->mantissa = product->exponent = 0; + +else { + firmware = (UNIT_CPU_MODEL != UNIT_1000_F); /* set firmware flag */ + + if (!firmware || (product->precision != fp_f)) { /* hardware? */ + if (multiplicand.mantissa < 0) { /* negative? */ + complement (&multiplicand); /* complement operand */ + sign = ~sign; /* track sign */ + } + if (multiplier.mantissa < 0) { /* negative? */ + complement (&multiplier); /* complement operand */ + sign = ~sign; /* track sign */ + } + } + + product->exponent = /* compute exponent */ + multiplicand.exponent + multiplier.exponent + 1; + + ah = (uint32) (multiplicand.mantissa >> 32); /* split multiplicand */ + al = (uint32) (multiplicand.mantissa & DMASK32); /* into high and low parts */ + bh = (uint32) (multiplier.mantissa >> 32); /* split multiplier */ + bl = (uint32) (multiplier.mantissa & DMASK32); /* into high and low parts */ + + if (firmware && (product->precision == fp_f)) { /* single-precision firmware? */ + ch = (int16) (ah >> 16) & DMASK; /* split 32-bit multiplicand */ + cl = (int16) (ah & 0xfffe); /* into high and low parts */ + dh = (int16) (bh >> 16) & DMASK; /* split 32-bit multiplier */ + dl = (int16) (bh & 0xfffe); /* into high and low parts */ + + hh = (t_uint64) (((int32) ch * dh) & ~1); /* form cross products */ + hl = (t_uint64) (((t_int64) ch * (t_int64) (uint16) dl + + (t_int64) dh * (t_int64) (uint16) cl) & + 0xfffffffffffe0000); + + product->mantissa = (t_uint64) (((t_int64) hh << 32) + /* sum partials */ + ((t_int64) hl << 16)); + } + + else { + hh = ((t_uint64) ah * bh); /* form four cross products */ + hl = ((t_uint64) ah * bl); /* using 32 x 32 = */ + lh = ((t_uint64) al * bh); /* 64-bit multiplies */ + ll = ((t_uint64) al * bl); + + carry = ((ll >> 32) + (uint32) hl + (uint32) lh) >> 32; /* form carry */ + + product->mantissa = hh + (hl >> 32) + (lh >> 32) + carry; /* sum partials */ + + if (sign) /* negate if required */ + complement (product); + } + } +return; +} + + +/* Divide two unpacked numbers. + + As with multiply, the single-precision firmware (FDV) operates differently + from the firmware extended-precision (.XDIV) and the hardware divisions of + any precision. Firmware implementations utilize a "divide and correct" + algorithm, wherein the quotient is estimated and then corrected by comparing + the dividend to the product of the quotient and the divisor. The hardware + uses a series of shifts and subtracts. This means that firmware FDV and + hardware FDV once again return slightly different values. + + Under simulation, the classic divide-and-correct method is employed, using + 64-bit / 32-bit = 32-bit divisions. This involves dividing the 64-bit + dividend "a1a2a3a4" by the first 32-bit digit "b1b2" of the 64-bit divisor + "b1b2b3b4". The resulting 32-bit quotient is ... + + The microcoded single-precision division avoids overflows by right-shifting + some values, which leads to a loss of precision in the LSBs. We duplicate + the firmware algorithm here to preserve the fidelity of the simulation. +*/ + +static void divide (FPU *quotient, FPU dividend, FPU divisor) +{ +uint32 sign = 0; +t_int64 bh, bl, r1, r0, p1, p0; +t_uint64 q, q1, q0; +t_bool firmware; +int32 ah, div, cp; +int16 dh, dl, pq1, pq2, cq; + +quotient->precision = dividend.precision; /* set precision */ + +if (divisor.mantissa == 0) { /* division by zero? */ + if (dividend.mantissa < 0) + quotient->mantissa = FP_MSIGN; /* return minus infinity */ + else + quotient->mantissa = ~FP_MSIGN; /* or plus infinity */ + quotient->exponent = FP_MAXPEXP + 1; + } + +else if (dividend.mantissa == 0) /* dividend zero? */ + quotient->mantissa = quotient->exponent = 0; /* yes; result is zero */ + +else { + firmware = (UNIT_CPU_MODEL != UNIT_1000_F); /* set firmware flag */ + + if (!firmware || (quotient->precision != fp_f)) { /* hardware or FFP? */ + if (dividend.mantissa < 0) { /* negative? */ + complement (÷nd); /* complement operand */ + sign = ~sign; /* track sign */ + } + if (divisor.mantissa < 0) { /* negative? */ + complement (&divisor); /* complement operand */ + sign = ~sign; /* track sign */ + } + } + + quotient->exponent = /* division subtracts exponents */ + dividend.exponent - divisor.exponent; + + bh = divisor.mantissa >> 32; /* split divisor */ + bl = divisor.mantissa & DMASK32; /* into high and low parts */ + + if (firmware && (quotient->precision == fp_f)) { /* single-precision firmware? */ + quotient->exponent = quotient->exponent + 1; /* fix exponent */ + + ah = (int32) (dividend.mantissa >> 32); /* split dividend */ + dh = (int16) (bh >> 16); /* split divisor again */ + dl = (int16) bh; + + div = ah >> 2; /* ASR 2 to prevent overflow */ + + pq1 = div / dh; /* form first partial quotient */ + div = ((div % dh) & ~1) << 15; /* ASR 1, move rem to upper */ + pq2 = div / dh; /* form second partial quotient */ + + div = (uint16) dl << 13; /* move divisor LSB to upper, LSR 3 */ + cq = div / dh; /* form correction quotient */ + cp = -cq * pq1; /* and correction product */ + + cp = (((cp >> 14) & ~3) + (int32) pq2) << 1; /* add corr prod and 2nd partial quo */ + quotient->mantissa = /* add 1st partial quo and align */ + (t_uint64) (((int32) pq1 << 16) + cp) << 32; + } + + else { /* hardware or FFP */ + q1 = (t_uint64) (dividend.mantissa / bh); /* form 1st trial quotient */ + r1 = dividend.mantissa % bh; /* and remainder */ + p1 = (r1 << 24) - (bl >> 8) * q1; /* calculate correction */ + + while (p1 < 0) { /* correction needed? */ + q1 = q1 - 1; /* trial quotient too large */ + p1 = p1 + (divisor.mantissa >> 8); /* increase remainder */ + } + + q0 = (t_uint64) ((p1 << 8) / bh); /* form 2nd trial quotient */ + r0 = (p1 << 8) % bh; /* and remainder */ + p0 = (r0 << 24) - (bl >> 8) * q0; /* calculate correction */ + + while (p0 < 0) { /* correction needed? */ + q0 = q0 - 1; /* trial quotient too large */ + p0 = p0 + (divisor.mantissa >> 8); /* increase remainder */ + } + + q = (q1 << 32) + q0; /* sum quotient digits */ + + if (q1 & 0xffffffff00000000) { /* did we lose MSB? */ + q = (q >> 1) | 0x8000000000000000; /* shift right and replace bit */ + quotient->exponent = quotient->exponent + 1;/* bump exponent for shift */ + } + + if (q & 0x8000000000000000) /* lose normalization? */ + q = q >> 1; /* correct */ + + quotient->mantissa = (t_int64) q; + } + + if (sign) + complement (quotient); /* negate if required */ + } +return; +} + + +/* Fix an unpacked number. + + A floating-point value is converted to an integer. The desired precision of + the result (single or double integer) must be set before calling. + + Values less than 0.5 (i.e., with negative exponents) underflow to zero. If + the value exceeds the specified integer range, the maximum integer value is + returned and overflow is set. Otherwise, the floating-point value is + right-shifted to zero the exponent. The result is then rounded. +*/ + +static uint32 fix (FPU *result, FPU operand) +{ +uint32 overflow; +t_bool bits_lost; + +if (operand.exponent < 0) { /* value < 0.5? */ + result->mantissa = 0; /* result rounds to zero */ + overflow = 0; /* clear for underflow */ + } + +else if (operand.exponent > /* value > integer size? */ + (int32) op_bits[result->precision]) { + result->mantissa = /* return max int value */ + (t_uint64) int_p_max[result->precision] << + op_start[result->precision]; + overflow = 1; /* and set overflow */ + } + +else { /* value in range */ + bits_lost = asr (&operand, /* shift to zero exponent */ + op_bits[result->precision] - operand.exponent); + + if (operand.mantissa < 0) { /* value negative? */ + if (bits_lost) /* bits lost? */ + operand.mantissa = operand.mantissa | 1; /* include one for rounding */ + + operand.mantissa = operand.mantissa + /* round result */ + p_half_lsb[result->precision]; + } + + result->mantissa = operand.mantissa & /* mask to precision */ + op_mask[result->precision]; + overflow = 0; + } + +result->exponent = 0; /* tidy up for integer value */ +return overflow; +} + + +/* Float an integer to an unpacked number. + + An integer is converted to a floating-point value. The desired precision of + the result must be set before calling. + + Conversion is simply a matter of copying the integer value, setting an + exponent that reflects the right-aligned position of the bits, and + normalizing. +*/ + +static void ffloat (FPU *result, FPU operand) +{ +result->mantissa = operand.mantissa; /* set value */ +result->exponent = op_bits[operand.precision]; /* set exponent */ +normalize (result); /* normalize */ +return; +} + + + +/* High-level floating-point routines. */ + + +/* Determine operand precisions. + + The precisions of the operands and result are determined by decoding an + operation opcode and returned to the caller. Pass NULL for both of the + operands if only the result precision is wanted. Pass NULL for the result if + only the operand precisions are wanted. +*/ + +void fp_prec (uint16 opcode, OPSIZE *operand_l, OPSIZE *operand_r, OPSIZE *result) +{ +OPSIZE fp_size, int_size; + +fp_size = (OPSIZE) ((opcode & 0003) + 2); /* fp_f, fp_x, fp_t, fp_e */ +int_size = (OPSIZE) ((opcode & 0004) >> 2); /* in_s, in_d */ + +if (operand_l && operand_r) { /* want operand precisions? */ + switch (opcode & 0120) { /* mask out opcode bit 5 */ + case 0000: /* add/mpy */ + case 0020: /* sub/div */ + *operand_l = fp_size; /* assume first op is fp */ + + if (opcode & 0004) /* operand internal? */ + *operand_r = fp_a; /* second op is accum */ + else + *operand_r = fp_size; /* second op is fp */ + break; + + case 0100: /* fix/accum as integer */ + *operand_l = fp_size; /* first op is fp */ + *operand_r = fp_a; /* second op is always null */ + break; + + case 0120: /* flt/accum as float */ + *operand_l = int_size; /* first op is integer */ + *operand_r = fp_a; /* second op is always null */ + break; + } + + if (opcode & 0010) /* operand internal? */ + *operand_l = fp_a; /* first op is accum */ + } + +if (result) /* want result precision? */ + if ((opcode & 0120) == 0100) /* fix? */ + *result = int_size; /* result is integer */ + else /* all others */ + *result = fp_size; /* result is fp */ + +return; +} + + +/* Floating Point Processor executor. + + The executor simulates the MPP interface between the CPU and the FPP. The + operation to be performed is specified by the supplied opcode, which conforms + to the FPP hardware interface, as follows: + + Bits Value Action + ---- ----- ---------------------------------------------- + 7 0 Exponent range is standard (+/-127) + 1 Exponent range is expanded (+/-511) + + 6-4 000 Add + 001 Subtract + 010 Multiply + 011 Divide + 100 Fix + 101 Float + 110 (diagnostic) + 111 (diagnostic) + + 3 0 Left operand is supplied + 1 Left operand in accumulator + + 2 0 Right operand is supplied (ADD/SUB/MPY/DIV) + Single integer operation (FIX/FLT) + 1 Right operand in accumulator (ADD/SUB/MPY/DIV) + Double integer operation (FIX/FLT) + + 1-0 00 2-word operation + 01 3-word operation + 10 4-word operation + 11 5-word operation + + If the opcode specifies that the left (or right) operand is in the + accumulator, then the value supplied for that parameter is not used. All + results are automatically left in the accumulator. If the result is not + needed externally, then NULL may be passed for the result parameter. + + To support accumulator set/get operations under simulation, the opcode is + expanded to include a special mode, indicated by bit 15 = 1. In this mode, + if the result parameter is NULL, then the accumulator is set from the value + passed as operand_l. If the result parameter is not null, then the + accumulator value is returned as the result, and operand_l is ignored. The + precision of the operation is performed as specified by the OPSIZE value + passed in bits 2-0 of the opcode. + + The function returns 1 if the operation overflows and 0 if not. +*/ + +uint32 fp_exec (uint16 opcode, OP *result, OP operand_l, OP operand_r) +{ +static FPU accumulator; +FPU uoperand_l, uoperand_r; +OPSIZE op_l_prec, op_r_prec, rslt_prec; +uint32 overflow; + +if (opcode & SIGN) { /* accumulator mode? */ + rslt_prec = (OPSIZE) (opcode & 0017); /* get operation precision */ + + if (result) { /* get accumulator? */ + op_l_prec = accumulator.precision; /* save accum prec temp */ + accumulator.precision = rslt_prec; /* set desired precision */ + *result = pack (accumulator); /* pack accumulator */ + accumulator.precision = op_l_prec; /* restore correct prec */ + } + else /* set accumulator */ + accumulator = unpack (operand_l, rslt_prec); /* unpack from operand */ + + return 0; /* no overflow from accum ops */ + } + +fp_prec (opcode, &op_l_prec, &op_r_prec, &rslt_prec); /* calc precs from opcode */ + +if (op_l_prec == fp_a) /* left operand in accum? */ + uoperand_l = accumulator; /* copy it */ +else /* operand supplied */ + uoperand_l = unpack (operand_l, op_l_prec); /* unpack from parameter */ + +if (op_r_prec == fp_a) /* right operand in accum? */ + uoperand_r = accumulator; /* copy it */ +else /* operand supplied */ + uoperand_r = unpack (operand_r, op_r_prec); /* unpack from parameter */ + + +switch (opcode & 0160) { /* dispatch operation */ + + case 0000: /* add */ + add (&accumulator, uoperand_l, uoperand_r); + break; + + case 0020: /* subtract */ + complement (&uoperand_r); + add (&accumulator, uoperand_l, uoperand_r); + break; + + case 0040: /* multiply */ + multiply (&accumulator, uoperand_l, uoperand_r); + break; + + case 0060: /* divide */ + divide (&accumulator, uoperand_l, uoperand_r); + break; + + case 0100: /* fix */ + accumulator.precision = rslt_prec; + overflow = fix (&accumulator, uoperand_l); + + if (result) /* result wanted? */ + *result = pack_int (accumulator.mantissa, /* pack integer */ + rslt_prec); + return overflow; + + case 0120: /* float */ + accumulator.precision = rslt_prec; + ffloat (&accumulator, uoperand_l); + + if (result) /* result wanted? */ + *result = pack (accumulator); /* pack FP (FLT does not round) */ + return 0; + + case 0140: /* (diagnostic) */ + case 0160: /* (diagnostic) */ + return 0; + } + +if (UNIT_CPU_MODEL != UNIT_1000_F) /* firmware implementation? */ + accumulator.mantissa = accumulator.mantissa & /* mask to precision */ + op_mask[accumulator.precision]; + +normalize (&accumulator); /* normalize */ +overflow = roundovf (&accumulator, opcode & 0200); /* round and check for overflow */ + +if (result) /* result wanted? */ + *result = pack (accumulator); /* pack result */ + +return overflow; +} + + +/* Set or get accumulator at desired precision. + + This function provides access to the FPP accumulator. In hardware, the + accumulator may be read at a given precision by sending the FPP an opcode + encoded with the desired precision and then reading words from the FPP + /without/ initiating the operation, i.e., without starting the processor. + + Under simulation, pass this function a NULL operand and the desired + precision to read the accumulator. Pass a pointer to an operand and the + desired precision to set the accumulator; the return value in this case is + not defined. +*/ + +OP fp_accum (const OP *operand, OPSIZE precision) +{ +OP result = NOP; +uint16 opcode = (uint16) precision | SIGN; /* add special mode bit */ + +if (operand) + fp_exec (opcode, NULL, *operand, NOP); /* set accum */ +else + fp_exec (opcode, &result, NOP, NOP); /* get accum */ +return result; +} + + +/* Pack an unpacked floating-point number. + + An unpacked mantissa is passed as a "packed" number with an unused exponent. + The mantissa and separately-passed exponent are packed into the in-memory + floating-point format. Note that all bits are significant in the mantissa + (no masking is done). +*/ + +uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) +{ +FPU unpacked; + +unpacked.mantissa = unpack_int (mantissa, precision); /* unpack mantissa */ +unpacked.exponent = exponent; /* set exponent */ +unpacked.precision = precision; /* set precision */ +*result = pack (unpacked); /* pack them */ +return 0; +} + + +/* Normalize, round, and pack an unpacked floating-point number. + + An unpacked mantissa is passed as a "packed" number with an unused exponent. + The mantissa and separately-passed exponent are normalized, rounded, and + packed into the in-memory floating-point format. Note that all bits are + significant in the mantissa (no masking is done). +*/ + +uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision) +{ +FPU unpacked; + +unpacked.mantissa = unpack_int (mantissa, precision); /* unpack mantissa */ +unpacked.exponent = exponent; /* set exponent */ +unpacked.precision = precision; /* set precision */ +return nrpack (result, unpacked, FALSE); /* norm/rnd/pack them */ +} + + +/* Unpack a packed floating-point number. + + A floating-point number, packed into the in-memory format, is unpacked into + separate mantissa and exponent values. The unpacked mantissa is returned in + a "packed" structure with an exponent of zero. Mantissa or exponent may be + null if that part isn't wanted. +*/ + +uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision) + +{ +FPU unpacked; + +unpacked = unpack (packed, precision); /* unpack mantissa and exponent */ + +if (exponent) /* exponent wanted? */ + *exponent = unpacked.exponent; /* return exponent */ + +if (mantissa) /* mantissa wanted? */ + *mantissa = pack_int (unpacked.mantissa, fp_t); /* return full-size mantissa */ +return 0; +} + + + /* Complement an unpacked mantissa. + + An unpacked mantissa is passed as a "packed" number with a zero exponent. + The exponent increment, i.e., either zero or one, depending on whether a + renormalization was required, is returned. Note that all bits are + significant in the mantissa. +*/ + +uint32 fp_ucom (OP *mantissa, OPSIZE precision) +{ +FPU unpacked; + +unpacked.mantissa = unpack_int (*mantissa, precision); /* unpack mantissa */ +unpacked.exponent = 0; /* clear undefined exponent */ +unpacked.precision = precision; /* set precision */ +complement (&unpacked); /* negate it */ +*mantissa = pack_int (unpacked.mantissa, precision); /* replace mantissa */ +return (uint32) unpacked.exponent; /* return exponent increment */ +} + + +/* Complement a floating-point number. */ + +uint32 fp_pcom (OP *packed, OPSIZE precision) +{ +FPU unpacked; + +unpacked = unpack (*packed, precision); /* unpack the number */ +complement (&unpacked); /* negate it */ +return nrpack (packed, unpacked, FALSE); /* and norm/rnd/pack */ +} + + +/* Truncate a floating-point number. */ + +uint32 fp_trun (OP *result, OP source, OPSIZE precision) +{ +t_bool bits_lost; +FPU unpacked; +FPU one = { FP_ONEHALF, 1, 0 }; /* 0.5 * 2 ** 1 = 1.0 */ +OP zero = { { 0, 0, 0, 0, 0 } }; /* 0.0 */ +t_uint64 mask = mant_mask[precision] & ~FP_MSIGN; + +unpacked = unpack (source, precision); +if (unpacked.exponent < 0) /* number < 0.5? */ + *result = zero; /* return 0 */ +else if (unpacked.exponent >= (int32) op_bits[precision]) /* no fractional bits? */ + *result = source; /* already integer */ +else { + mask = (mask >> unpacked.exponent) & mask; /* mask fractional bits */ + bits_lost = ((unpacked.mantissa & mask) != 0); /* flag if bits lost */ + unpacked.mantissa = unpacked.mantissa & ~mask; /* mask off fraction */ + if ((unpacked.mantissa < 0) && bits_lost) /* negative? */ + add (&unpacked, unpacked, one); /* truncate toward zero */ + nrpack (result, unpacked, FALSE); /* (overflow cannot occur) */ + } +return 0; /* clear overflow on return */ +} + + +/* Convert a floating-point number from one precision to another. */ + +uint32 fp_cvt (OP *result, OPSIZE source_precision, OPSIZE dest_precision) +{ +FPU unpacked; + +unpacked = unpack (*result, source_precision); +unpacked.precision = dest_precision; +return nrpack (result, unpacked, FALSE); /* norm/rnd/pack */ +} + + +#endif /* end of int64 support */ diff --git a/HP2100/hp2100_fp1.h b/HP2100/hp2100_fp1.h new file mode 100644 index 0000000..4649c09 --- /dev/null +++ b/HP2100/hp2100_fp1.h @@ -0,0 +1,53 @@ +/* hp2100_fp1.h: HP 2100/1000 multiple-precision floating point definitions + + Copyright (c) 2005-2006, J. David Bryan + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be used + in advertising or otherwise to promote the sale, use or other dealings in + this Software without prior written authorization from the author. + + 16-Oct-06 JDB Generalized FP calling sequences for F-Series + 12-Oct-06 JDB Altered x_trun for F-Series FFP compatibility +*/ + +#ifndef _HP2100_FP1_H_ +#define _HP2100_FP1_H_ 0 + + +/* Special operands. */ + +#define ACCUM NULL /* result not returned */ +static const OP NOP = { { 0, 0, 0, 0, 0 } }; /* unneeded operand */ + + +/* Generalized floating-point handlers. */ + +void fp_prec (uint16 opcode, OPSIZE *operand_l, OPSIZE *operand_r, OPSIZE *result); +uint32 fp_exec (uint16 opcode, OP *result, OP operand_l, OP operand_r); +OP fp_accum (const OP *operand, OPSIZE precision); +uint32 fp_pack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); +uint32 fp_nrpack (OP *result, OP mantissa, int32 exponent, OPSIZE precision); +uint32 fp_unpack (OP *mantissa, int32 *exponent, OP packed, OPSIZE precision); +uint32 fp_ucom (OP *mantissa, OPSIZE precision); +uint32 fp_pcom (OP *packed, OPSIZE precision); +uint32 fp_trun (OP *result, OP source, OPSIZE precision); +uint32 fp_cvt (OP *result, OPSIZE source_precision, OPSIZE dest_precision); + +#endif diff --git a/HP2100/hp2100_ipl.c b/HP2100/hp2100_ipl.c new file mode 100644 index 0000000..b36e4cd --- /dev/null +++ b/HP2100/hp2100_ipl.c @@ -0,0 +1,651 @@ +/* hp2100_ipl.c: HP 2000 interprocessor link simulator + + Copyright (c) 2002-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ipli, iplo 12875A interprocessor link + + 01-Mar-07 JDB IPLI EDT delays DMA completion interrupt for TSB + Added debug printouts + 28-Dec-06 JDB Added ioCRS state to I/O decoders + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Oct-04 JDB Fixed enable/disable from either device + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Implemented DMA SRQ (follows FLG) + 21-Dec-03 RMS Adjusted ipl_ptime for TSB (from Mike Gemeny) + 09-May-03 RMS Added network device flag + 31-Jan-03 RMS Links are full duplex (found by Mike Gemeny) + + The 12875A Processor Interconnect Kit consists four 12566A Microcircuit + Interface cards. Two are used in each processor. One card in each system is + used to initiate transmissions to the other, and the second card is used to + receive transmissions from the other. Each pair of cards forms a + bidirectional link, as the sixteen data lines are cross-connected, so that + data sent and status returned are supported. In each processor, data is sent + on the lower priority card and received on the higher priority card. Two + sets of cards are used to support simultaneous transmission in both + directions. + + Reference: + - 12875A Processor Interconnect Kit Operating and Service Manual + (12875-90002, Jan-1974) +*/ + +#include "hp2100_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */ +#define UNIT_V_ACTV (UNIT_V_UF + 1) /* making connection */ +#define UNIT_V_ESTB (UNIT_V_UF + 2) /* connection established */ +#define UNIT_V_HOLD (UNIT_V_UF + 3) /* character holding */ +#define UNIT_DIAG (1 << UNIT_V_DIAG) +#define UNIT_ACTV (1 << UNIT_V_ACTV) +#define UNIT_ESTB (1 << UNIT_V_ESTB) +#define UNIT_HOLD (1 << UNIT_V_HOLD) +#define IBUF buf /* input buffer */ +#define OBUF wait /* output buffer */ +#define DSOCKET u3 /* data socket */ +#define LSOCKET u4 /* listening socket */ + +/* Debug flags */ + +#define DEB_CMDS (1 << 0) /* Command initiation and completion */ +#define DEB_CPU (1 << 1) /* CPU I/O */ +#define DEB_XFER (1 << 2) /* Socket receive and transmit */ + +extern uint32 PC; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; +extern FILE *sim_log; +extern FILE *sim_deb; +int32 ipl_edtdelay = 1; /* EDT delay (msec) */ +int32 ipl_ptime = 31; /* polling interval */ +int32 ipl_stopioe = 0; /* stop on error */ +int32 ipl_hold[2] = { 0 }; /* holding character */ + +DEVICE ipli_dev, iplo_dev; +int32 ipliio (int32 inst, int32 IR, int32 dat); +int32 iploio (int32 inst, int32 IR, int32 dat); +int32 iplio (UNIT *uptr, int32 inst, int32 IR, int32 dat); +t_stat ipl_svc (UNIT *uptr); +t_stat ipl_reset (DEVICE *dptr); +t_stat ipl_attach (UNIT *uptr, char *cptr); +t_stat ipl_detach (UNIT *uptr); +t_stat ipl_boot (int32 unitno, DEVICE *dptr); +t_stat ipl_dscln (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ipl_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc); +t_bool ipl_check_conn (UNIT *uptr); + +/* Debug flags table */ + +DEBTAB ipl_deb[] = { + { "CMDS", DEB_CMDS }, + { "CPU", DEB_CPU }, + { "XFER", DEB_XFER }, + { NULL, 0 } }; + +/* IPLI data structures + + ipli_dev IPLI device descriptor + ipli_unit IPLI unit descriptor + ipli_reg IPLI register list +*/ + +DIB ipl_dib[] = { + { IPLI, 0, 0, 0, 0, 0, &ipliio }, + { IPLO, 0, 0, 0, 0, 0, &iploio } + }; + +#define ipli_dib ipl_dib[0] +#define iplo_dib ipl_dib[1] + +UNIT ipl_unit[] = { + { UDATA (&ipl_svc, UNIT_ATTABLE, 0) }, + { UDATA (&ipl_svc, UNIT_ATTABLE, 0) } + }; + +#define ipli_unit ipl_unit[0] +#define iplo_unit ipl_unit[1] + +REG ipli_reg[] = { + { ORDATA (IBUF, ipli_unit.IBUF, 16) }, + { ORDATA (OBUF, ipli_unit.OBUF, 16) }, + { FLDATA (CMD, ipli_dib.cmd, 0) }, + { FLDATA (CTL, ipli_dib.ctl, 0) }, + { FLDATA (FLG, ipli_dib.flg, 0) }, + { FLDATA (FBF, ipli_dib.fbf, 0) }, + { FLDATA (SRQ, ipli_dib.srq, 0) }, + { ORDATA (HOLD, ipl_hold[0], 8) }, + { DRDATA (TIME, ipl_ptime, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ipl_stopioe, 0) }, + { ORDATA (DEVNO, ipli_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB ipl_mod[] = { + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", &ipl_setdiag }, + { UNIT_DIAG, 0, "link mode", "LINK", &ipl_setdiag }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", + &ipl_dscln, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &ipli_dev }, + { 0 } + }; + +DEVICE ipli_dev = { + "IPLI", &ipli_unit, ipli_reg, ipl_mod, + 1, 10, 31, 1, 16, 16, + &tmxr_ex, &tmxr_dep, &ipl_reset, + &ipl_boot, &ipl_attach, &ipl_detach, + &ipli_dib, DEV_NET | DEV_DISABLE | DEV_DIS | DEV_DEBUG, + 0, ipl_deb, NULL, NULL + }; + +/* IPLO data structures + + iplo_dev IPLO device descriptor + iplo_unit IPLO unit descriptor + iplo_reg IPLO register list +*/ + +REG iplo_reg[] = { + { ORDATA (IBUF, iplo_unit.IBUF, 16) }, + { ORDATA (OBUF, iplo_unit.OBUF, 16) }, + { FLDATA (CMD, iplo_dib.cmd, 0) }, + { FLDATA (CTL, iplo_dib.ctl, 0) }, + { FLDATA (FLG, iplo_dib.flg, 0) }, + { FLDATA (FBF, iplo_dib.fbf, 0) }, + { FLDATA (SRQ, iplo_dib.srq, 0) }, + { ORDATA (HOLD, ipl_hold[1], 8) }, + { DRDATA (TIME, ipl_ptime, 24), PV_LEFT }, + { ORDATA (DEVNO, iplo_dib.devno, 6), REG_HRO }, + { NULL } + }; + +DEVICE iplo_dev = { + "IPLO", &iplo_unit, iplo_reg, ipl_mod, + 1, 10, 31, 1, 16, 16, + &tmxr_ex, &tmxr_dep, &ipl_reset, + &ipl_boot, &ipl_attach, &ipl_detach, + &iplo_dib, DEV_NET | DEV_DISABLE | DEV_DIS | DEV_DEBUG, + 0, ipl_deb, NULL, NULL + }; + +/* I/O instructions */ + +int32 ipliio (int32 inst, int32 IR, int32 dat) +{ +return iplio (&ipli_unit, inst, IR, dat); +} + +int32 iploio (int32 inst, int32 IR, int32 dat) +{ +return iplio (&iplo_unit, inst, IR, dat); +} + +/* I/O handler for the IPLI and IPLO devices. + + Implementation note: 2000 Access has a race condition that manifests itself + by an apparently normal boot and operational system console but no PLEASE LOG + IN response to terminals connected to the multiplexer. The frequency of + occurrence is higher on multiprocessor host systems, where the SP and IOP + instances may execute concurrently. + + The cause is this code in the SP disc loader source (2883.asm, 7900.asm, + 790X.asm, 79X3.asm, and 79XX.asm): + + LDA SDVTR REQUEST + JSB IOPMA,I DEVICE TABLE + [...] + STC DMAHS,C TURN ON DMA + SFS DMAHS WAIT FOR + JMP *-1 DEVICE TABLE + STC CH2,C SET CORRECT + CLC CH2 FLAG DIRECTION + + The STC/CLC normally would cause a second "request device table" command to + be recognized by the IOP, except that the IOP DMA setup routine "DMAXF" (in + D61.asm) has specified an end-of-block CLC that holds off the IPL interrupt, + and the completion interrupt routine "DMACP" ends with a STC,C that clears + the IPL flag. + + In hardware, the two CPUs are essentially interlocked by the DMA transfer, + and DMA completion interrupts occur almost simultaneously. Therefore, the + STC/CLC in the SP is guaranteed to occur before the STC,C in the IOP. Under + simulation, and especially on multiprocessor hosts, that guarantee does not + hold. If the STC/CLC occurs after the STC,C, then the IOP starts a second + device table DMA transfer, which the SP is not expecting. The IOP never + processes the subsequent "start timesharing" command, and the muxtiplexer is + non-reponsive. + + We employ a workaround that decreases the incidence of the problem: DMA + output completion interrupts are delayed to allow the other SIMH instance a + chance to process its own DMA completion. We do this by processing the EDT + (End Data Transfer) I/O backplane signal and "sleep"ing for a short time if + the transfer was an output transfer ("dat" contains the DMA channel number + and direction flag for EDT signals). +*/ + +int32 iplio (UNIT *uptr, int32 inst, int32 IR, int32 dat) +{ +uint32 u, dev, odev; +int32 sta; +char msg[2], uc; +DEVICE *dbdev; /* device ptr for debug */ +static const char *iotype[] = { "Status", "Command" }; + +uc = (uptr == &ipli_unit) ? 'I' : 'O'; /* identify unit for debug */ +dbdev = (uptr == &ipli_unit) ? &ipli_dev : &iplo_dev; /* identify device for debug */ + +u = (uptr - ipl_unit); /* get unit number */ +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + uptr->OBUF = dat; + + if (DEBUG_PRJ (dbdev, DEB_CPU)) + fprintf (sim_deb, ">>IPL%c OTx: %s = %06o\n", uc, iotype[u], dat); + break; + + case ioLIX: /* load */ + dat = 0; + + case ioMIX: /* merge */ + dat = dat | uptr->IBUF; /* get return data */ + + if (DEBUG_PRJ (dbdev, DEB_CPU)) + fprintf (sim_deb, ">>IPL%c LIx: %s = %06o\n", uc, iotype[u ^ 1], dat); + break; + + case ioCRS: /* control reset */ + clrCMD (dev); /* clear ctl, cmd */ + clrCTL (dev); + break; + + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCMD (dev); /* clear ctl, cmd */ + clrCTL (dev); + + if (DEBUG_PRJ (dbdev, DEB_CMDS)) + fprintf (sim_deb, ">>IPL%c CLC: Command cleared\n", uc); + } + + else { /* STC */ + setCMD (dev); /* set ctl, cmd */ + setCTL (dev); + + if (DEBUG_PRJ (dbdev, DEB_CMDS)) + fprintf (sim_deb, ">>IPL%c STC: Command set\n", uc); + + if (uptr->flags & UNIT_ATT) { /* attached? */ + if ((uptr->flags & UNIT_ESTB) == 0) { /* established? */ + if (!ipl_check_conn (uptr)) /* not established? */ + return STOP_NOCONN; /* lose */ + uptr->flags = uptr->flags | UNIT_ESTB; + } + msg[0] = (uptr->OBUF >> 8) & 0377; + msg[1] = uptr->OBUF & 0377; + sta = sim_write_sock (uptr->DSOCKET, msg, 2); + + if (DEBUG_PRJ (dbdev, DEB_XFER)) + fprintf (sim_deb, + ">>IPL%c STC: Socket write = %06o, status = %d\n", + uc, uptr->OBUF, sta); + + if (sta == SOCKET_ERROR) { + printf ("IPL: socket write error\n"); + return SCPE_IOERR; + } + sim_os_sleep (0); + } + else if (uptr->flags & UNIT_DIAG) { /* diagnostic mode? */ + u = (uptr - ipl_unit) ^ 1; /* find other device */ + ipl_unit[u].IBUF = uptr->OBUF; /* output to other */ + odev = ipl_dib[u].devno; /* other device no */ + setFSR (odev); /* set other flag */ + } + else return SCPE_UNATT; /* lose */ + } + break; + + case ioEDT: /* End of DMA data transfer */ + if ((dat & DMA2_OI) == 0) { /* output transfer? */ + if (DEBUG_PRJ (dbdev, DEB_CMDS)) + fprintf (sim_deb, + ">>IPL%c EDT: Delaying DMA completion interrupt for %d msec\n", + uc, ipl_edtdelay); + sim_os_ms_sleep (ipl_edtdelay); /* delay completion */ + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +return dat; +} + +/* Unit service - poll for input */ + +t_stat ipl_svc (UNIT *uptr) +{ +int32 u, nb, dev; +char msg[2], uc; +DEVICE *dbdev; /* device ptr for debug */ + +u = uptr - ipl_unit; /* get link number */ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* not attached? */ +sim_activate (uptr, ipl_ptime); /* reactivate */ +if ((uptr->flags & UNIT_ESTB) == 0) { /* not established? */ + if (!ipl_check_conn (uptr)) return SCPE_OK; /* check for conn */ + uptr->flags = uptr->flags | UNIT_ESTB; + } +nb = sim_read_sock (uptr->DSOCKET, msg, ((uptr->flags & UNIT_HOLD)? 1: 2)); +if (nb < 0) { /* connection closed? */ + printf ("IPL: socket read error\n"); + return SCPE_IOERR; + } +if (nb == 0) return SCPE_OK; /* no data? */ +if (uptr->flags & UNIT_HOLD) { /* holdover byte? */ + uptr->IBUF = (ipl_hold[u] << 8) | (((int32) msg[0]) & 0377); + uptr->flags = uptr->flags & ~UNIT_HOLD; + } +else if (nb == 1) { + ipl_hold[u] = ((int32) msg[0]) & 0377; + uptr->flags = uptr->flags | UNIT_HOLD; + } +else uptr->IBUF = ((((int32) msg[0]) & 0377) << 8) | + (((int32) msg[1]) & 0377); +dev = ipl_dib[u].devno; /* get device number */ +clrCMD (dev); /* clr cmd, set flag */ +setFSR (dev); + +uc = (uptr == &ipli_unit) ? 'I' : 'O'; /* identify unit for debug */ +dbdev = (uptr == &ipli_unit) ? &ipli_dev : &iplo_dev; /* identify device for debug */ + +if (DEBUG_PRJ (dbdev, DEB_XFER)) + fprintf (sim_deb, ">>IPL%c svc: Socket read = %06o, status = %d\n", + uc, uptr->IBUF, nb); + +return SCPE_OK; +} + +t_bool ipl_check_conn (UNIT *uptr) +{ +SOCKET sock; + +if (uptr->flags & UNIT_ESTB) return TRUE; /* established? */ +if (uptr->flags & UNIT_ACTV) { /* active connect? */ + if (sim_check_conn (uptr->DSOCKET, 0) <= 0) return FALSE; + } +else { + sock = sim_accept_conn (uptr->LSOCKET, NULL); /* poll connect */ + if (sock == INVALID_SOCKET) return FALSE; /* got a live one? */ + uptr->DSOCKET = sock; /* save data socket */ + } +uptr->flags = uptr->flags | UNIT_ESTB; /* conn established */ +return TRUE; +} + +/* Reset routine */ + +t_stat ipl_reset (DEVICE *dptr) +{ +DIB *dibp = (DIB *) dptr->ctxt; +UNIT *uptr = dptr->units; + +hp_enbdis_pair (dptr, /* make pair cons */ + (dptr == &ipli_dev)? &iplo_dev: &ipli_dev); +dibp->cmd = dibp->ctl = 0; /* clear cmd, ctl */ +dibp->flg = dibp->fbf = dibp->srq = 1; /* set flg, fbf, srq */ +uptr->IBUF = uptr->OBUF = 0; /* clr buffers */ +if (uptr->flags & UNIT_ATT) sim_activate (uptr, ipl_ptime); +else sim_cancel (uptr); /* deactivate unit */ +uptr->flags = uptr->flags & ~UNIT_HOLD; +return SCPE_OK; +} + +/* Attach routine + + attach -l - listen for connection on port + attach -c - connect to ip address and port +*/ + +t_stat ipl_attach (UNIT *uptr, char *cptr) +{ +extern int32 sim_switches; +SOCKET newsock; +uint32 i, t, ipa, ipp, oldf; +char *tptr; +t_stat r; + +r = get_ipaddr (cptr, &ipa, &ipp); +if ((r != SCPE_OK) || (ipp == 0)) return SCPE_ARG; +oldf = uptr->flags; +if (oldf & UNIT_ATT) ipl_detach (uptr); +if ((sim_switches & SWMASK ('C')) || + ((sim_switches & SIM_SW_REST) && (oldf & UNIT_ACTV))) { + if (ipa == 0) ipa = 0x7F000001; + newsock = sim_connect_sock (ipa, ipp); + if (newsock == INVALID_SOCKET) return SCPE_IOERR; + printf ("Connecting to IP address %d.%d.%d.%d, port %d\n", + (ipa >> 24) & 0xff, (ipa >> 16) & 0xff, + (ipa >> 8) & 0xff, ipa & 0xff, ipp); + if (sim_log) fprintf (sim_log, + "Connecting to IP address %d.%d.%d.%d, port %d\n", + (ipa >> 24) & 0xff, (ipa >> 16) & 0xff, + (ipa >> 8) & 0xff, ipa & 0xff, ipp); + uptr->flags = uptr->flags | UNIT_ACTV; + uptr->LSOCKET = 0; + uptr->DSOCKET = newsock; + } +else { + if (ipa != 0) return SCPE_ARG; + newsock = sim_master_sock (ipp); + if (newsock == INVALID_SOCKET) return SCPE_IOERR; + printf ("Listening on port %d\n", ipp); + if (sim_log) fprintf (sim_log, "Listening on port %d\n", ipp); + uptr->flags = uptr->flags & ~UNIT_ACTV; + uptr->LSOCKET = newsock; + uptr->DSOCKET = 0; + } +uptr->IBUF = uptr->OBUF = 0; +uptr->flags = (uptr->flags | UNIT_ATT) & ~(UNIT_ESTB | UNIT_HOLD); +tptr = (char *) malloc (strlen (cptr) + 1); /* get string buf */ +if (tptr == NULL) { /* no memory? */ + ipl_detach (uptr); /* close sockets */ + return SCPE_MEM; + } +strcpy (tptr, cptr); /* copy ipaddr:port */ +uptr->filename = tptr; /* save */ +sim_activate (uptr, ipl_ptime); /* activate poll */ +if (sim_switches & SWMASK ('W')) { /* wait? */ + for (i = 0; i < 30; i++) { /* check for 30 sec */ + if (t = ipl_check_conn (uptr)) break; /* established? */ + if ((i % 10) == 0) /* status every 10 sec */ + printf ("Waiting for connnection\n"); + sim_os_sleep (1); /* sleep 1 sec */ + } + if (t) printf ("Connection established\n"); + } +return SCPE_OK; +} + +/* Detach routine */ + +t_stat ipl_detach (UNIT *uptr) +{ +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if (uptr->flags & UNIT_ACTV) sim_close_sock (uptr->DSOCKET, 1); +else { + if (uptr->flags & UNIT_ESTB) /* if established, */ + sim_close_sock (uptr->DSOCKET, 0); /* close data socket */ + sim_close_sock (uptr->LSOCKET, 1); /* closen listen socket */ + } +free (uptr->filename); /* free string */ +uptr->filename = NULL; +uptr->LSOCKET = 0; +uptr->DSOCKET = 0; +uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_ACTV | UNIT_ESTB); +sim_cancel (uptr); /* don't poll */ +return SCPE_OK; +} + +/* Disconnect routine */ + +t_stat ipl_dscln (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if (((uptr->flags & UNIT_ATT) == 0) || (uptr->flags & UNIT_ACTV) || + ((uptr->flags & UNIT_ESTB) == 0)) return SCPE_NOFNC; +sim_close_sock (uptr->DSOCKET, 0); +uptr->DSOCKET = 0; +uptr->flags = uptr->flags & ~UNIT_ESTB; +return SCPE_OK; +} + +/* Diagnostic/normal mode routine */ + +t_stat ipl_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val) { + ipli_unit.flags = ipli_unit.flags | UNIT_DIAG; + iplo_unit.flags = iplo_unit.flags | UNIT_DIAG; + } +else { + ipli_unit.flags = ipli_unit.flags & ~UNIT_DIAG; + iplo_unit.flags = iplo_unit.flags & ~UNIT_DIAG; + } +return SCPE_OK; +} + +/* Interprocessor link bootstrap routine (HP Access Manual) */ + +#define MAX_BASE 073 +#define IPL_PNTR 074 +#define PTR_PNTR 075 +#define IPL_DEVA 076 +#define PTR_DEVA 077 + +static const uint32 pboot[IBL_LNT] = { + 0163774, /*BBL LDA ICK,I ; IPL sel code */ + 0027751, /* JMP CFG ; go configure */ + 0107700, /*ST CLC 0,C ; intr off */ + 0002702, /* CLA,CCE,SZA ; skip in */ + 0063772, /*CN LDA M26 ; feed frame */ + 0002307, /*EOC CCE,INA,SZA,RSS ; end of file? */ + 0027760, /* JMP EOT ; yes */ + 0017736, /* JSB READ ; get #char */ + 0007307, /* CMB,CCE,INB,SZB,RSS ; 2's comp; null? */ + 0027705, /* JMP EOC ; read next */ + 0077770, /* STB WC ; word in rec */ + 0017736, /* JSB READ ; get feed frame */ + 0017736, /* JSB READ ; get address */ + 0074000, /* STB 0 ; init csum */ + 0077771, /* STB AD ; save addr */ + 0067771, /*CK LDB AD ; check addr */ + 0047773, /* ADB MAXAD ; below loader */ + 0002040, /* SEZ ; E =0 => OK */ + 0102055, /* HLT 55 */ + 0017736, /* JSB READ ; get word */ + 0040001, /* ADA 1 ; cont checksum */ + 0177771, /* STB AD,I ; store word */ + 0037771, /* ISZ AD */ + 0000040, /* CLE ; force wd read */ + 0037770, /* ISZ WC ; block done? */ + 0027717, /* JMP CK ; no */ + 0017736, /* JSB READ ; get checksum */ + 0054000, /* CPB 0 ; ok? */ + 0027704, /* JMP CN ; next block */ + 0102011, /* HLT 11 ; bad csum */ + 0000000, /*RD 0 */ + 0006600, /* CLB,CME ; E reg byte ptr */ + 0103700, /*IO1 STC RDR,C ; start reader */ + 0102300, /*IO2 SFS RDR ; wait */ + 0027741, /* JMP *-1 */ + 0106400, /*IO3 MIB RDR ; get byte */ + 0002041, /* SEZ,RSS ; E set? */ + 0127736, /* JMP RD,I ; no, done */ + 0005767, /* BLF,CLE,BLF ; shift byte */ + 0027740, /* JMP IO1 ; again */ + 0163775, /* LDA PTR,I ; get ptr code */ + 0043765, /*CFG ADA SFS ; config IO */ + 0073741, /* STA IO2 */ + 0043766, /* ADA STC */ + 0073740, /* STA IO1 */ + 0043767, /* ADA MIB */ + 0073743, /* STA IO3 */ + 0027702, /* JMP ST */ + 0063777, /*EOT LDA PSC ; put select codes */ + 0067776, /* LDB ISC ; where xloader wants */ + 0102077, /* HLT 77 */ + 0027702, /* JMP ST */ + 0000000, /* NOP */ + 0102300, /*SFS SFS 0 */ + 0001400, /*STC 1400 */ + 0002500, /*MIB 2500 */ + 0000000, /*WC 0 */ + 0000000, /*AD 0 */ + 0177746, /*M26 -26 */ + 0000000, /*MAX -BBL */ + 0007776, /*ICK ISC */ + 0007777, /*PTR IPT */ + 0000000, /*ISC 0 */ + 0000000 /*IPT 0 */ + }; + +t_stat ipl_boot (int32 unitno, DEVICE *dptr) +{ +int32 i, devi, devp; +extern DIB ptr_dib; +extern UNIT cpu_unit; +extern uint32 SR; +extern uint16 *M; + +devi = ipli_dib.devno; /* get device no */ +devp = ptr_dib.devno; +PC = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */ +SR = (devi << IBL_V_DEV) | devp; /* set SR */ +for (i = 0; i < IBL_LNT; i++) M[PC + i] = pboot[i]; /* copy bootstrap */ +M[PC + MAX_BASE] = (~PC + 1) & DMASK; /* fix ups */ +M[PC + IPL_PNTR] = M[PC + IPL_PNTR] | PC; +M[PC + PTR_PNTR] = M[PC + PTR_PNTR] | PC; +M[PC + IPL_DEVA] = devi; +M[PC + PTR_DEVA] = devp; +return SCPE_OK; +} diff --git a/HP2100/hp2100_lps.c b/HP2100/hp2100_lps.c new file mode 100644 index 0000000..9f26a28 --- /dev/null +++ b/HP2100/hp2100_lps.c @@ -0,0 +1,528 @@ +/* hp2100_lps.c: HP 2100 12653A/2767 line printer simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lps 12653A 2767 line printer + 12566B microcircuit interface with loopback diagnostic connector + + 10-May-07 RMS Added UNIT_TEXT flag + 11-Jan-07 JDB CLC cancels I/O event if DIAG (jumper W9 in "A" pos) + Added ioCRS state to I/O decoders + 19-Nov-04 JDB Added restart when set online, etc. + Fixed col count for non-printing chars + 01-Oct-04 JDB Added SET OFFLINE/ONLINE, POWEROFF/POWERON + Fixed status returns for error conditions + Fixed handling of non-printing characters + Fixed handling of characters after column 80 + Improved timing model accuracy for RTE + Added fast/realistic timing + Added debug printouts + 03-Jun-04 RMS Fixed timing (found by Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Implemented DMA SRQ (follows FLG) + 25-Apr-03 RMS Revised for extended file support + 24-Oct-02 RMS Added microcircuit test features + 30-May-02 RMS Widened POS to 32b + 03-Dec-01 RMS Changed DEVNO to use extended SET/SHOW + 07-Sep-01 RMS Moved function prototypes + 21-Nov-00 RMS Fixed flag, fbf power up state + Added command flop + 15-Oct-00 RMS Added variable device number support + + This module simulates two different devices. In "diagnostic mode," it + simulates a 12566B microcircuit interface card with a loopback connector and + the jumpers set as required for execution of the General Purpose Register + diagnostic. In non-diagnostic mode, it simulates a 12653A line printer + interface card and a 2767 line printer. + + The 12566B interface with the loopback connector ties the device command + output to the device flag input. Setting control therefore causes device + flag to set almost immediately. Device command is active only during that + interim. Under simulation, the loopback occurs within the STC handler, and + CMD is never set. + + The 2767 impact printer has a rotating drum with 80 columns of 64 raised + characters. ASCII codes 32 through 95 (SPACE through "_") form the print + repertoire. The printer responds to the control characters FF, LF, and CR. + + The 80 columns are divided into four zones of 20 characters each that are + addressed sequentially. Received characters are buffered in a 20-character + memory. When the 20th printable character is received, the current zone is + printed, and the memory is reset. In the absence of print command + characters, a zone print operation will commence after each group of 20 + printable characters is transmitted to the printer. + + The print command characters have these actions: + + * CR -- print the characters in the current zone, reset to zone 1, and clear + the buffer memory. + * LF -- same as CR, plus advances the paper one line. + * FF -- same as CR, plus advances the paper to the top of the next form. + + The 2767 provides two status bits via the interface: + + bit 15 -- printer not ready + bit 0 -- printer busy + + The expected status returns are: + + 100001 -- power off or cable disconnected + 100001 -- initial power on, then changes to 000001 within sixty + seconds of initial power on + 000001 -- power on, paper unloaded or printer offline or not idle + 000000 -- power on, paper loaded and printer online and idle + + These simulator commands provide the listed printer states: + + SET LPS POWEROFF --> power off or cable disconnected + SET LPS POWERON --> power on + SET LPS OFFLINE --> printer offline + SET LPS ONLINE --> printer online + ATT LPS --> paper loaded + DET LPS --> paper out + + References: + - 2767A Line Printer Operating and Service Manual (02767-90002, Oct-1973) + - 12566B, 12566B-001, 12566B-002, 12566B-003 Microcircuit Interface Kits + Operating and Service Manual (12566-90015, Apr-1976) + + The following implemented behaviors have been inferred from secondary sources + (diagnostics, operating system drivers, etc.), due to absent or contradictory + authoritative information; future correction may be needed: + + 1. Paper out sets BUSY instead of NOT READY. + 2. Print operation in progress sets BUSY instead of NOT READY. + 3. Characters not in the print repertoire are replaced with blanks. + 4. The 81st and succeeding characters overprint the current line. +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" + +#define LPS_ZONECNT 20 /* zone char count */ +#define LPS_PAGECNT 80 /* page char count */ +#define LPS_PAGELNT 60 /* page line length */ +#define LPS_FORMLNT 66 /* form line length */ + +/* Printer power states */ + +#define LPS_ON 0 /* power is on */ +#define LPS_OFF 1 /* power is off */ +#define LPS_TURNING_ON 2 /* power is turning on */ + +#define LPS_BUSY 0000001 /* busy status */ +#define LPS_NRDY 0100000 /* not ready status */ +#define LPS_PWROFF LPS_BUSY | LPS_NRDY /* power-off status */ + +#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */ +#define UNIT_V_POWEROFF (UNIT_V_UF + 1) /* unit powered off */ +#define UNIT_V_OFFLINE (UNIT_V_UF + 2) /* unit offline */ +#define UNIT_DIAG (1 << UNIT_V_DIAG) +#define UNIT_POWEROFF (1 << UNIT_V_POWEROFF) +#define UNIT_OFFLINE (1 << UNIT_V_OFFLINE) + +extern uint32 PC; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; +extern FILE *sim_deb; + +int32 lps_ccnt = 0; /* character count */ +int32 lps_lcnt = 0; /* line count */ +int32 lps_stopioe = 0; /* stop on error */ +int32 lps_sta = 0; /* printer status */ +int32 lps_timing = 1; /* timing type */ +uint32 lps_power = LPS_ON; /* power state */ + +/* Hardware timing: + (based on 1580 instr/msec) instr msec calc msec + ------------------------ + - character transfer time : ctime = 2 2 us + - per-zone printing time : ptime = 55300 35 40 + - per-line paper slew time : stime = 17380 11 13 + - power-on ready delay time : rtime = 158000 100 + + NOTE: the printer acknowledges before the print motion has stopped to allow + for continuous slew, so the set times are a bit less than the calculated + operation time from the manual. + + NOTE: the 2767 diagnostic checks completion times, so the realistic timing + must be used. Because simulator timing is in instructions, and because the + diagnostic uses the TIMER instruction (~1580 executions per millisecond) when + running on a 1000-E/F but a software timing loop (~400-600 executions per + millisecond) when running on anything else, realistic timings are decreased by + three-fourths when not executing on an E/F. +*/ + +int32 lps_ctime = 0; /* char xfer time */ +int32 lps_ptime = 0; /* zone printing time */ +int32 lps_stime = 0; /* paper slew time */ +int32 lps_rtime = 0; /* power-on ready time */ + +typedef int32 TIMESET[4]; /* set of controller times */ + +int32 *const lps_timers[] = { &lps_ctime, &lps_ptime, &lps_stime, &lps_rtime }; + +const TIMESET lps_times[2] = { + { 2, 55300, 17380, 158000 }, /* REALTIME */ + { 2, 1000, 1000, 1000 } /* FASTTIME */ + }; + +DEVICE lps_dev; +int32 lpsio (int32 inst, int32 IR, int32 dat); +t_stat lps_svc (UNIT *uptr); +t_stat lps_reset (DEVICE *dptr); +t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat lps_attach (UNIT *uptr, char *cptr); +t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* LPS data structures + + lps_dev LPS device descriptor + lps_unit LPS unit descriptor + lps_reg LPS register list +*/ + +DIB lps_dib = { LPS, 0, 0, 0, 0, 0, &lpsio }; + +UNIT lps_unit = { + UDATA (&lps_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_DISABLE+UNIT_TEXT, 0) + }; + +REG lps_reg[] = { + { ORDATA (BUF, lps_unit.buf, 16) }, + { ORDATA (STA, lps_sta, 16) }, + { ORDATA (POWER, lps_power, 2), REG_RO }, + { FLDATA (CMD, lps_dib.cmd, 0) }, + { FLDATA (CTL, lps_dib.ctl, 0) }, + { FLDATA (FLG, lps_dib.flg, 0) }, + { FLDATA (FBF, lps_dib.fbf, 0) }, + { FLDATA (SRQ, lps_dib.srq, 0) }, + { DRDATA (CCNT, lps_ccnt, 7), PV_LEFT }, + { DRDATA (LCNT, lps_lcnt, 7), PV_LEFT }, + { DRDATA (POS, lps_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (CTIME, lps_ctime, 24), PV_LEFT }, + { DRDATA (PTIME, lps_ptime, 24), PV_LEFT }, + { DRDATA (STIME, lps_stime, 24), PV_LEFT }, + { DRDATA (RTIME, lps_rtime, 24), PV_LEFT }, + { FLDATA (TIMING, lps_timing, 0), REG_HRO }, + { FLDATA (STOP_IOE, lps_stopioe, 0) }, + { ORDATA (DEVNO, lps_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB lps_mod[] = { + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, + { UNIT_DIAG, 0, "printer mode", "PRINTER", NULL }, + { UNIT_POWEROFF, UNIT_POWEROFF, "power off", "POWEROFF", lps_poweroff }, + { UNIT_POWEROFF, 0, "power on", "POWERON", lps_poweron }, + { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL }, + { UNIT_OFFLINE, 0, "online", "ONLINE", lps_restart }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "REALTIME", + &lps_set_timing, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "FASTTIME", + &lps_set_timing, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TIMING", NULL, + NULL, &lps_show_timing, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &lps_dev }, + { 0 } + }; + +DEVICE lps_dev = { + "LPS", &lps_unit, lps_reg, lps_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lps_reset, + NULL, &lps_attach, NULL, + &lps_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG + }; + +/* IO instructions */ + +int32 lpsio (int32 inst, int32 IR, int32 dat) +{ +int32 dev, sched; + +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + if (DEBUG_PRS (lps_dev)) + fprintf (sim_deb, ">>LPS OTx: Character %06o output\n", dat); + lps_unit.buf = dat; + break; + + case ioLIX: /* load */ + dat = 0; /* default sta = 0 */ + case ioMIX: /* merge */ + if ((lps_unit.flags & UNIT_DIAG) == 0) { /* real lpt? */ + if (lps_power == LPS_ON) { /* power on? */ + if (((lps_unit.flags & UNIT_ATT) == 0) || /* paper out? */ + (lps_unit.flags & UNIT_OFFLINE) || /* offline? */ + sim_is_active (&lps_unit)) lps_sta = LPS_BUSY; + else lps_sta = 0; + } + else lps_sta = LPS_PWROFF; + } + dat = dat | lps_sta; /* diag, rtn status */ + if (DEBUG_PRS (lps_dev)) + fprintf (sim_deb, ">>LPS LIx: Status %06o returned\n", dat); + break; + + case ioCRS: /* control reset */ + clrCTL (dev); /* clear control */ + clrCMD (dev); /* clear command */ + sim_cancel (&lps_unit); /* deactivate unit */ + break; + + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCTL (dev); /* clear control */ + if (lps_unit.flags & UNIT_DIAG) { /* diagnostic mode? */ + clrCMD (dev); /* clear command (jumper W9-A) */ + if (IR & I_HC) /* clear flag too? */ + sim_cancel (&lps_unit); /* prevent FLG/SRQ */ + } + } + else { /* STC */ + setCTL (dev); /* set ctl */ + setCMD (dev); /* set cmd */ + if (lps_unit.flags & UNIT_DIAG) { /* diagnostic? */ + lps_sta = lps_unit.buf; /* loop back data */ + sim_activate (&lps_unit, 2); /* schedule flag */ + } + else { /* real lpt, sched */ + if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, + ">>LPS STC: Character %06o scheduled for line %d, column %d, ", + lps_unit.buf, lps_lcnt + 1, lps_ccnt + 1); + if ((lps_unit.buf != '\f') && + (lps_unit.buf != '\n') && + (lps_unit.buf != '\r')) { /* normal char */ + lps_ccnt = lps_ccnt + 1; /* incr char counter */ + if (lps_ccnt % LPS_ZONECNT == 0) /* end of zone? */ + sched = lps_ptime; /* print zone */ + else sched = lps_ctime; /* xfer char */ + } + else { /* print cmd */ + if (lps_ccnt % LPS_ZONECNT == 0) /* last zone printed? */ + sched = lps_ctime; /* yes, so just char time */ + else sched = lps_ptime; /* no, so print needed */ + lps_ccnt = 0; /* reset char counter */ + if (lps_unit.buf == '\n') { /* line advance */ + lps_lcnt = (lps_lcnt + 1) % LPS_PAGELNT; + if (lps_lcnt > 0) sched = sched + lps_stime; + else sched = sched + /* allow for perf skip */ + lps_stime * (LPS_FORMLNT - LPS_PAGELNT); + } + else if (lps_unit.buf == '\f') { /* form advance */ + sched = sched + lps_stime * (LPS_FORMLNT - lps_lcnt); + lps_lcnt = 0; + } + } + sim_activate (&lps_unit, sched); + if (DEBUG_PRS (lps_dev)) + fprintf (sim_deb, "time = %d\n", sched); + } + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +return dat; +} + +/* Unit service */ + +t_stat lps_svc (UNIT *uptr) +{ +int32 dev; +int32 c = uptr->buf & 0177; + +if (lps_power == LPS_TURNING_ON) { /* printer warmed up? */ + lps_power = LPS_ON; /* change state */ + lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */ + if (DEBUG_PRS (lps_dev)) + fputs (">>LPS svc: Power state is ON\n", sim_deb); + return SCPE_OK; /* done */ + } +dev = lps_dib.devno; /* get dev no */ +if (uptr->flags & UNIT_DIAG) { /* diagnostic? */ + clrCMD (dev); /* clear cmd */ + setFSR (dev); /* set flag, fbf */ + return SCPE_OK; /* done */ + } +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lps_stopioe, SCPE_UNATT); +else if (uptr->flags & UNIT_OFFLINE) /* offline? */ + return IORETURN (lps_stopioe, STOP_OFFLINE); +else if (uptr->flags & UNIT_POWEROFF) /* powered off? */ + return IORETURN (lps_stopioe, STOP_PWROFF); +clrCMD (dev); /* clear cmd */ +setFSR (dev); /* set flag, fbf */ +if (((c < ' ') || (c > '_')) && /* non-printing char? */ + (c != '\f') && (c != '\n') && (c != '\r')) { + if (DEBUG_PRS (lps_dev)) + fprintf (sim_deb, ">>LPS svc: Character %06o erased\n", c); + c = ' '; /* replace with blank */ + } +if (lps_ccnt > LPS_PAGECNT) { /* 81st character? */ + fputc ('\r', uptr->fileref); /* return to line start */ + uptr->pos = uptr->pos + 1; /* update pos */ + lps_ccnt = 1; /* reset char counter */ + if (DEBUG_PRS (lps_dev)) + fputs (">>LPS svc: Line wraparound to column 1\n", sim_deb); + } +fputc (c, uptr->fileref); /* "print" char */ +uptr->pos = uptr->pos + 1; /* update pos */ +if (DEBUG_PRS (lps_dev)) + fprintf (sim_deb, ">>LPS svc: Character %06o printed\n", c); +if ((lps_lcnt == 0) && (c == '\n')) { /* LF did TOF? */ + fputc ('\f', uptr->fileref); /* do perf skip */ + uptr->pos = uptr->pos + 1; /* update pos */ + if (DEBUG_PRS (lps_dev)) + fputs (">>LPS svc: Perforation skip to TOF\n", sim_deb); + } +if (ferror (uptr->fileref)) { + perror ("LPS I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine - called from SCP, flags in DIB */ + +t_stat lps_reset (DEVICE *dptr) +{ +lps_dib.cmd = lps_dib.ctl = 0; /* clear cmd, ctl */ +lps_dib.flg = lps_dib.fbf = lps_dib.srq = 1; /* set flg, fbf, srq */ +lps_sta = lps_unit.buf = 0; +lps_power = LPS_ON; /* power is on */ +sim_cancel (&lps_unit); /* deactivate unit */ +lps_set_timing (NULL, lps_timing, NULL, NULL); /* init timing set */ +return SCPE_OK; +} + +/* Restart I/O routine + + If I/O is started via STC, and the printer is powered off, offline, + or out of paper, the CTL and CMD flip-flops will set, a service event + will be scheduled, and the service routine will be entered. If + STOP_IOE is not set, the I/O operation will "hang" at that point + until the printer is powered on, set online, or paper is supplied + (attached). + + If a pending operation is "hung" when this routine is called, it is + restarted, which clears CTL and sets FBF and FLG, completing the + original I/O request. + */ + +t_stat lps_restart (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if (lps_dib.cmd && lps_dib.ctl && !sim_is_active (uptr)) + sim_activate (uptr, 0); /* reschedule I/O */ +return SCPE_OK; +} + +/* Printer power off */ + +t_stat lps_poweroff (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +lps_power = LPS_OFF; /* change state */ +if (DEBUG_PRS (lps_dev)) fputs (">>LPS set: Power state is OFF\n", sim_deb); +return SCPE_OK; +} + +/* Printer power on */ + +t_stat lps_poweron (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if (lps_unit.flags & UNIT_DIAG) { /* diag mode? */ + lps_power = LPS_ON; /* no delay */ + if (DEBUG_PRS (lps_dev)) + fputs (">>LPS set: Power state is ON\n", sim_deb); + } +else { + lps_power = LPS_TURNING_ON; /* change state */ + lps_unit.flags |= UNIT_OFFLINE; /* set offline */ + sim_activate (&lps_unit, lps_rtime); /* schedule ready */ + if (DEBUG_PRS (lps_dev)) fprintf (sim_deb, + ">>LPS set: Power state is TURNING ON, scheduled time = %d\n", + lps_rtime ); + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lps_attach (UNIT *uptr, char *cptr) +{ +lps_ccnt = lps_lcnt = 0; /* top of form */ +lps_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */ +return attach_unit (uptr, cptr); +} + +/* Set printer timing + + Realistic timing is factored, depending on CPU model, to account for the + timing method employed by the diagnostic. */ + +t_stat lps_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i, factor = 1; + +lps_timing = (val != 0); /* determine choice */ +if ((lps_timing == 0) && /* calc speed factor */ + (UNIT_CPU_MODEL != UNIT_1000_E) && + (UNIT_CPU_MODEL != UNIT_1000_F)) + factor = 4; +for (i = 0; i < (sizeof (lps_timers) / sizeof (lps_timers[0])); i++) + *lps_timers[i] = lps_times[lps_timing][i] / factor; /* assign times */ +return SCPE_OK; +} + +/* Show printer timing */ + +t_stat lps_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (lps_timing) fputs ("fast timing", st); +else fputs ("realistic timing", st); +return SCPE_OK; +} diff --git a/HP2100/hp2100_lpt.c b/HP2100/hp2100_lpt.c new file mode 100644 index 0000000..41f43cf --- /dev/null +++ b/HP2100/hp2100_lpt.c @@ -0,0 +1,299 @@ +/* hp2100_lpt.c: HP 2100 12845B line printer simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt 12845B 2607 line printer + + 22-Jan-07 RMS Added UNIT_TEXT flag + 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified) + 19-Nov-04 JDB Added restart when set online, etc. + 29-Sep-04 JDB Added SET OFFLINE/ONLINE, POWEROFF/POWERON + Fixed status returns for error conditions + Fixed TOF handling so form remains on line 0 + 03-Jun-04 RMS Fixed timing (found by Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Implemented DMA SRQ (follows FLG) + 25-Apr-03 RMS Revised for extended file support + 24-Oct-02 RMS Cloned from 12653A + + The 2607 provides three status bits via the interface: + + bit 15 -- printer ready (online) + bit 14 -- paper out + bit 0 -- printer idle + + The expected status returns are: + + 140001 -- power off or cable disconnected + 100001 -- power on, paper loaded, printer ready + 100000 -- power on, paper loaded, printer busy + 040000 -- power on, paper out (at bottom-of-form) + 000000 -- power on, paper out (not at BOF) / print button up / platen open + + Manual Note: "2-33. PAPER OUT SIGNAL. [...] The signal is asserted only + when the format tape in the line printer has reached the bottom of form." + + These simulator commands provide the listed printer states: + + SET LPT POWEROFF --> power off or cable disconnected + SET LPT POWERON --> power on + SET LPT OFFLINE --> print button up + SET LPT ONLINE --> print button down + ATT LPT --> paper loaded + DET LPT --> paper out + + Reference: + - 12845A Line Printer Operating and Service Manual (12845-90001, Aug-1972) +*/ + +#include "hp2100_defs.h" + +#define LPT_PAGELNT 60 /* page length */ + +#define LPT_NBSY 0000001 /* not busy */ +#define LPT_PAPO 0040000 /* paper out */ +#define LPT_RDY 0100000 /* ready */ +#define LPT_PWROFF LPT_RDY | LPT_PAPO | LPT_NBSY /* power-off status */ + +#define LPT_CTL 0100000 /* control output */ +#define LPT_CHAN 0000100 /* skip to chan */ +#define LPT_SKIPM 0000077 /* line count mask */ +#define LPT_CHANM 0000007 /* channel mask */ + +#define UNIT_V_POWEROFF (UNIT_V_UF + 0) /* unit powered off */ +#define UNIT_V_OFFLINE (UNIT_V_UF + 1) /* unit offline */ +#define UNIT_POWEROFF (1 << UNIT_V_POWEROFF) +#define UNIT_OFFLINE (1 << UNIT_V_OFFLINE) + +extern uint32 PC; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; + +int32 lpt_ctime = 4; /* char time */ +int32 lpt_ptime = 10000; /* print time */ +int32 lpt_stopioe = 0; /* stop on error */ +int32 lpt_lcnt = 0; /* line count */ +static int32 lpt_cct[8] = { + 1, 1, 1, 2, 3, LPT_PAGELNT/2, LPT_PAGELNT/4, LPT_PAGELNT/6 + }; + +DEVICE lpt_dev; +int32 lptio (int32 inst, int32 IR, int32 dat); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_restart (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat lpt_attach (UNIT *uptr, char *cptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { LPT, 0, 0, 0, 0, 0, &lptio }; + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_DISABLE+UNIT_TEXT, 0) + }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 7) }, + { FLDATA (CMD, lpt_dib.cmd, 0) }, + { FLDATA (CTL, lpt_dib.ctl, 0) }, + { FLDATA (FLG, lpt_dib.flg, 0) }, + { FLDATA (FBF, lpt_dib.fbf, 0) }, + { FLDATA (SRQ, lpt_dib.srq, 0) }, + { DRDATA (LCNT, lpt_lcnt, 7) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (CTIME, lpt_ctime, 31), PV_LEFT }, + { DRDATA (PTIME, lpt_ptime, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { ORDATA (DEVNO, lpt_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB lpt_mod[] = { + { UNIT_POWEROFF, UNIT_POWEROFF, "power off", "POWEROFF", NULL }, + { UNIT_POWEROFF, 0, "power on", "POWERON", lpt_restart }, + { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL }, + { UNIT_OFFLINE, 0, "online", "ONLINE", lpt_restart }, + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &lpt_dev }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, NULL, + &lpt_dib, DEV_DISABLE + }; + +/* IO instructions */ + +int32 lptio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + lpt_unit.buf = dat & (LPT_CTL | 0177); + break; + + case ioLIX: /* load */ + dat = 0; /* default sta = 0 */ + case ioMIX: /* merge */ + if (lpt_unit.flags & UNIT_POWEROFF) /* power off? */ + dat = dat | LPT_PWROFF; + else if (!(lpt_unit.flags & UNIT_OFFLINE)) { /* online? */ + if (lpt_unit.flags & UNIT_ATT) { /* paper loaded? */ + dat = dat | LPT_RDY; + if (!sim_is_active (&lpt_unit)) /* printer busy? */ + dat = dat | LPT_NBSY; + } + else if (lpt_lcnt == LPT_PAGELNT - 1) /* paper out, at BOF? */ + dat = dat | LPT_PAPO; + } + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCMD (dev); /* clear ctl, cmd */ + clrCTL (dev); + } + else { /* STC */ + setCMD (dev); /* set ctl, cmd */ + setCTL (dev); + sim_activate (&lpt_unit, /* schedule op */ + (lpt_unit.buf & LPT_CTL)? lpt_ptime: lpt_ctime); + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +return dat; +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 i, skip, chan, dev; + +dev = lpt_dib.devno; /* get dev no */ +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); +else if (uptr->flags & UNIT_OFFLINE) /* offline? */ + return IORETURN (lpt_stopioe, STOP_OFFLINE); +else if (uptr->flags & UNIT_POWEROFF) /* powered off? */ + return IORETURN (lpt_stopioe, STOP_PWROFF); +clrCMD (dev); /* clear cmd */ +setFSR (dev); /* set flag, fbf */ +if (uptr->buf & LPT_CTL) { /* control word? */ + if (uptr->buf & LPT_CHAN) { + chan = uptr->buf & LPT_CHANM; + if (chan == 0) { /* top of form? */ + fputc ('\f', uptr->fileref); /* ffeed */ + lpt_lcnt = 0; /* reset line cnt */ + skip = 0; + } + else if (chan == 1) skip = LPT_PAGELNT - lpt_lcnt - 1; + else skip = lpt_cct[chan] - (lpt_lcnt % lpt_cct[chan]); + } + else { + skip = uptr->buf & LPT_SKIPM; + if (skip == 0) fputc ('\r', uptr->fileref); + } + for (i = 0; i < skip; i++) fputc ('\n', uptr->fileref); + lpt_lcnt = (lpt_lcnt + skip) % LPT_PAGELNT; + } +else fputc (uptr->buf & 0177, uptr->fileref); /* no, just add char */ +if (ferror (uptr->fileref)) { + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +lpt_unit.pos = ftell (uptr->fileref); /* update pos */ +return SCPE_OK; +} + +/* Reset routine - called from SCP, flags in DIB */ + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_dib.cmd = lpt_dib.ctl = 0; /* clear cmd, ctl */ +lpt_dib.flg = lpt_dib.fbf = lpt_dib.srq = 1; /* set flg, fbf, srq */ +lpt_unit.buf = 0; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Restart I/O routine + + If I/O is started via STC, and the printer is powered off, offline, + or out of paper, the CTL and CMD flip-flops will set, a service event + will be scheduled, and the service routine will be entered. If + STOP_IOE is not set, the I/O operation will "hang" at that point + until the printer is powered on, set online, or paper is supplied + (attached). + + If a pending operation is "hung" when this routine is called, it is + restarted, which clears CTL and sets FBF and FLG, completing the + original I/O request. + */ + +t_stat lpt_restart (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if (lpt_dib.cmd && lpt_dib.ctl && !sim_is_active (uptr)) + sim_activate (uptr, 0); /* reschedule I/O */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +lpt_lcnt = 0; /* top of form */ +lpt_restart (uptr, 0, NULL, NULL); /* restart I/O if hung */ +return attach_unit (uptr, cptr); +} diff --git a/HP2100/hp2100_ms.c b/HP2100/hp2100_ms.c new file mode 100644 index 0000000..f94d9be --- /dev/null +++ b/HP2100/hp2100_ms.c @@ -0,0 +1,1126 @@ +/* hp2100_ms.c: HP 2100 13181A/13183A magnetic tape simulator + + Copyright (c) 1993-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ms 13181A 7970B 800bpi nine track magnetic tape + 13183A 7970E 1600bpi nine track magnetic tape + + 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified) + 18-Sep-06 JDB Fixed 2nd CLR after WC causing another write + Improve debug reporting, add debug flags + 14-Sep-06 JDB Removed local BOT flag, now uses sim_tape_bot + 30-Aug-06 JDB Added erase gap support, improved tape lib err reporting + 07-Jul-06 JDB Added CAPACITY as alternate for REEL + Fixed EOT test for unlimited reel size + 16-Feb-06 RMS Revised for new EOT test + 22-Jul-05 RMS Fixed compiler warning on Solaris (from Doug Glyn) + 01-Mar-05 JDB Added SET OFFLINE; rewind/offline now does not detach + 07-Oct-04 JDB Fixed enable/disable from either device + 14-Aug-04 JDB Fixed many functional and timing problems (from Dave Bryan) + - fixed erroneous execution of rejected command + - fixed erroneous execution of select-only command + - fixed erroneous execution of clear command + - fixed odd byte handling for read + - fixed spurious odd byte status on 13183A EOF + - modified handling of end of medium + - added detailed timing, with fast and realistic modes + - added reel sizes to simulate end of tape + - added debug printouts + 06-Jul-04 RMS Fixed spurious timing error after CLC (found by Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Fixed SR setting in IBL + Revised IBL loader + Implemented DMA SRQ (follows FLG) + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised for magtape library + 18-Oct-02 RMS Added BOOT command, added 13183A support + 30-Sep-02 RMS Revamped error handling + 29-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length test + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. + + References: + - 13181B Digital Magnetic Tape Unit Interface Kit Operating and Service Manual + (13181-90901, Nov-1982) + - 13183B Digital Magnetic Tape Unit Interface Kit Operating and Service Manual + (13183-90901, Nov-1983) +*/ + +#include "hp2100_defs.h" +#include "sim_tape.h" + +#define UNIT_V_OFFLINE (MTUF_V_UF + 0) /* unit offline */ +#define UNIT_OFFLINE (1 << UNIT_V_OFFLINE) + +#define MS_NUMDR 4 /* number of drives */ +#define DB_N_SIZE 16 /* max data buf */ +#define DBSIZE (1 << DB_N_SIZE) /* max data cmd */ +#define FNC u3 /* function */ +#define UST u4 /* unit status */ +#define REEL u5 /* tape reel size */ + +#define BPI_13181 800 /* 800 bpi for 13181 cntlr */ +#define BPI_13183 1600 /* 1600 bpi for 13183 cntlr */ +#define GAP_13181 48 /* gap is 4.8 inches for 13181 cntlr */ +#define GAP_13183 30 /* gap is 3.0 inches for 13183 cntlr */ +#define TCAP (300 * 12 * 800) /* 300 ft capacity at 800 bpi */ + +/* Debug flags */ + +#define DEB_CMDS (1 << 0) /* command init and compl */ +#define DEB_CPU (1 << 1) /* CPU I/O */ +#define DEB_RWS (1 << 2) /* tape reads, writes, status */ + +/* Command - msc_fnc */ + +#define FNC_CLR 00110 /* clear */ +#define FNC_GAP 00015 /* write gap */ +#define FNC_GFM 00215 /* gap+file mark */ +#define FNC_RC 00023 /* read */ +#define FNC_WC 00031 /* write */ +#define FNC_FSR 00003 /* forward space */ +#define FNC_BSR 00041 /* backward space */ +#define FNC_FSF 00203 /* forward file */ +#define FNC_BSF 00241 /* backward file */ +#define FNC_REW 00101 /* rewind */ +#define FNC_RWS 00105 /* rewind and offline */ +#define FNC_WFM 00211 /* write file mark */ +#define FNC_RFF 00223 /* read file fwd (diag) */ +#define FNC_RRR 00061 /* read record rev (diag) */ +#define FNC_CMPL 00400 /* completion state */ +#define FNC_V_SEL 9 /* select */ +#define FNC_M_SEL 017 +#define FNC_GETSEL(x) (((x) >> FNC_V_SEL) & FNC_M_SEL) + +#define FNF_MOT 00001 /* motion */ +#define FNF_OFL 00004 +#define FNF_WRT 00010 /* write */ +#define FNF_REV 00040 /* reverse */ +#define FNF_RWD 00100 /* rewind */ +#define FNF_CHS 00400 /* change select */ + +#define FNC_SEL ((FNC_M_SEL << FNC_V_SEL) | FNF_CHS) + +/* Status - stored in msc_sta, unit.UST (u), or dynamic (d) */ + +#define STA_PE 0100000 /* 1600 bpi (d) */ +#define STA_V_SEL 13 /* unit sel (d) */ +#define STA_M_SEL 03 +#define STA_SEL (STA_M_SEL << STA_V_SEL) +#define STA_ODD 0004000 /* odd bytes */ +#define STA_REW 0002000 /* rewinding (u) */ +#define STA_TBSY 0001000 /* transport busy (d) */ +#define STA_BUSY 0000400 /* ctrl busy */ +#define STA_EOF 0000200 /* end of file */ +#define STA_BOT 0000100 /* beg of tape (d) */ +#define STA_EOT 0000040 /* end of tape (d) */ +#define STA_TIM 0000020 /* timing error */ +#define STA_REJ 0000010 /* programming error */ +#define STA_WLK 0000004 /* write locked (d) */ +#define STA_PAR 0000002 /* parity error */ +#define STA_LOCAL 0000001 /* local (d) */ +#define STA_DYN (STA_PE | STA_SEL | STA_TBSY | STA_BOT | \ + STA_EOT | STA_WLK | STA_LOCAL) + +extern uint32 PC, SR; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; +extern int32 sim_switches; +extern FILE *sim_deb; + +int32 ms_ctype = 0; /* ctrl type */ +int32 ms_timing = 1; /* timing type */ +int32 msc_sta = 0; /* status */ +int32 msc_buf = 0; /* buffer */ +int32 msc_usl = 0; /* unit select */ +int32 msc_1st = 0; /* first service */ +int32 msc_stopioe = 1; /* stop on error */ +int32 msd_buf = 0; /* data buffer */ +uint8 msxb[DBSIZE] = { 0 }; /* data buffer */ +t_mtrlnt ms_ptr = 0, ms_max = 0; /* buffer ptrs */ + +/* Hardware timing at 45 IPS 13181 13183 + (based on 1580 instr/msec) instr msec SCP instr msec SCP + -------------------- -------------------- + - BOT start delay : btime = 161512 102.22 184 252800 160.00 288 + - motion cmd start delay : ctime = 14044 8.89 16 17556 11.11 20 + - GAP traversal time : gtime = 175553 111.11 200 105333 66.67 120 + - IRG traversal time : itime = 24885 15.75 - 27387 17.33 - + - rewind initiation time : rtime = 878 0.56 1 878 0.56 1 + - data xfer time / word : xtime = 88 55.56us - 44 27.78us - + + NOTE: The 13181-60001 Rev. 1629 tape diagnostic fails test 17B subtest 6 with + "E116 BYTE TIME SHORT" if the correct data transfer time is used for + 13181A interface. Set "xtime" to 115 (instructions) to pass that + diagnostic. Rev. 2040 of the tape diagnostic fixes this problem and + passes with the correct data transfer time. +*/ + +int32 msc_btime = 0; /* BOT start delay */ +int32 msc_ctime = 0; /* motion cmd start delay */ +int32 msc_gtime = 0; /* GAP traversal time */ +int32 msc_itime = 0; /* IRG traversal time */ +int32 msc_rtime = 0; /* rewind initiation time */ +int32 msc_xtime = 0; /* data xfer time / word */ + +typedef int32 TIMESET[6]; /* set of controller times */ + +int32 *const timers[] = { &msc_btime, &msc_ctime, &msc_gtime, + &msc_itime, &msc_rtime, &msc_xtime }; + +const TIMESET msc_times[3] = { + { 161512, 14044, 175553, 24885, 878, 88 }, /* 13181A */ + { 252800, 17556, 105333, 27387, 878, 44 }, /* 13183A */ + { 1, 1000, 1, 1, 100, 10 } /* FAST */ + }; + +DEVICE msd_dev, msc_dev; +int32 msdio (int32 inst, int32 IR, int32 dat); +int32 mscio (int32 inst, int32 IR, int32 dat); +t_stat msc_svc (UNIT *uptr); +t_stat msc_reset (DEVICE *dptr); +t_stat msc_attach (UNIT *uptr, char *cptr); +t_stat msc_detach (UNIT *uptr); +t_stat msc_online (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat msc_boot (int32 unitno, DEVICE *dptr); +t_stat ms_write_gap (UNIT *uptr); +t_stat ms_map_err (UNIT *uptr, t_stat st); +t_stat ms_settype (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ms_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat ms_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ms_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat ms_set_reelsize (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ms_show_reelsize (FILE *st, UNIT *uptr, int32 val, void *desc); +void ms_config_timing (void); +char *ms_cmd_name (uint32 cmd); + +/* MSD data structures + + msd_dev MSD device descriptor + msd_unit MSD unit list + msd_reg MSD register list +*/ + +DIB ms_dib[] = { + { MSD, 0, 0, 0, 0, 0, &msdio }, + { MSC, 0, 0, 0, 0, 0, &mscio } + }; + +#define msd_dib ms_dib[0] +#define msc_dib ms_dib[1] + +UNIT msd_unit = { UDATA (NULL, 0, 0) }; + +REG msd_reg[] = { + { ORDATA (BUF, msd_buf, 16) }, + { FLDATA (CMD, msd_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, msd_dib.ctl, 0) }, + { FLDATA (FLG, msd_dib.flg, 0) }, + { FLDATA (FBF, msd_dib.fbf, 0) }, + { FLDATA (SRQ, msd_dib.srq, 0) }, + { BRDATA (DBUF, msxb, 8, 8, DBSIZE) }, + { DRDATA (BPTR, ms_ptr, DB_N_SIZE + 1) }, + { DRDATA (BMAX, ms_max, DB_N_SIZE + 1) }, + { ORDATA (DEVNO, msd_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB msd_mod[] = { + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &msd_dev }, + { 0 } + }; + +DEVICE msd_dev = { + "MSD", &msd_unit, msd_reg, msd_mod, + 1, 10, DB_N_SIZE, 1, 8, 8, + NULL, NULL, &msc_reset, + NULL, NULL, NULL, + &msd_dib, DEV_DISABLE + }; + +/* MSC data structures + + msc_dev MSC device descriptor + msc_unit MSC unit list + msc_reg MSC register list + msc_mod MSC modifier list + msc_deb MSC debug flags +*/ + +UNIT msc_unit[] = { + { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_OFFLINE, 0) }, + { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_OFFLINE, 0) }, + { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_OFFLINE, 0) }, + { UDATA (&msc_svc, UNIT_ATTABLE | UNIT_ROABLE | + UNIT_DISABLE | UNIT_OFFLINE, 0) } + }; + +REG msc_reg[] = { + { ORDATA (STA, msc_sta, 12) }, + { ORDATA (BUF, msc_buf, 16) }, + { ORDATA (USEL, msc_usl, 2) }, + { FLDATA (FSVC, msc_1st, 0) }, + { FLDATA (CMD, msc_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, msc_dib.ctl, 0) }, + { FLDATA (FLG, msc_dib.flg, 0) }, + { FLDATA (FBF, msc_dib.fbf, 0) }, + { FLDATA (SRQ, msc_dib.srq, 0) }, + { URDATA (POS, msc_unit[0].pos, 10, T_ADDR_W, 0, MS_NUMDR, PV_LEFT) }, + { URDATA (FNC, msc_unit[0].FNC, 8, 8, 0, MS_NUMDR, REG_HRO) }, + { URDATA (UST, msc_unit[0].UST, 8, 12, 0, MS_NUMDR, REG_HRO) }, + { URDATA (REEL, msc_unit[0].REEL, 10, 2, 0, MS_NUMDR, REG_HRO) }, + { DRDATA (BTIME, msc_btime, 24), REG_NZ + PV_LEFT }, + { DRDATA (CTIME, msc_ctime, 24), REG_NZ + PV_LEFT }, + { DRDATA (GTIME, msc_gtime, 24), REG_NZ + PV_LEFT }, + { DRDATA (ITIME, msc_itime, 24), REG_NZ + PV_LEFT }, + { DRDATA (RTIME, msc_rtime, 24), REG_NZ + PV_LEFT }, + { DRDATA (XTIME, msc_xtime, 24), REG_NZ + PV_LEFT }, + { FLDATA (TIMING, ms_timing, 0), REG_HRO }, + { FLDATA (STOP_IOE, msc_stopioe, 0) }, + { FLDATA (CTYPE, ms_ctype, 0), REG_HRO }, + { ORDATA (DEVNO, msc_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB msc_mod[] = { + { UNIT_OFFLINE, UNIT_OFFLINE, "offline", "OFFLINE", NULL }, + { UNIT_OFFLINE, 0, "online", "ONLINE", msc_online }, + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &ms_set_reelsize, &ms_show_reelsize, NULL }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 1, "REEL", "REEL", + &ms_set_reelsize, &ms_show_reelsize, NULL }, + { MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "13181A", + &ms_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "13183A", + &ms_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, + NULL, &ms_showtype, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "REALTIME", + &ms_set_timing, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "FASTTIME", + &ms_set_timing, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TIMING", NULL, + NULL, &ms_show_timing, NULL }, + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &msd_dev }, + { 0 } + }; + +DEBTAB msc_deb[] = { + { "CMDS", DEB_CMDS }, + { "CPU", DEB_CPU }, + { "RWS", DEB_RWS }, + { NULL, 0 } + }; + +DEVICE msc_dev = { + "MSC", msc_unit, msc_reg, msc_mod, + MS_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &msc_reset, + &msc_boot, &msc_attach, &msc_detach, + &msc_dib, DEV_DISABLE | DEV_DEBUG, + 0, msc_deb, NULL, NULL + }; + +/* IO instructions */ + +int32 msdio (int32 inst, int32 IR, int32 dat) +{ +int32 devd; + +devd = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devd); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devd) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devd) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + msd_buf = dat; /* store data */ + break; + + case ioMIX: /* merge */ + dat = dat | msd_buf; + break; + + case ioLIX: /* load */ + dat = msd_buf; + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCTL (devd); /* clr ctl, cmd */ + clrCMD (devd); + } + else { /* STC */ + setCTL (devd); /* set ctl, cmd */ + setCMD (devd); + } + break; + + case ioEDT: /* DMA end */ + clrFSR (devd); /* same as CLF */ + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devd); } /* H/C option */ +return dat; +} + +int32 mscio (int32 inst, int32 IR, int32 dat) +{ +int32 i, devc, devd, sched_time; +t_stat st; +UNIT *uptr = msc_dev.units + msc_usl; +static const uint8 map_sel[16] = { + 0, 0, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3 + }; + +devc = IR & I_DEVMASK; /* get device no */ +devd = devc - 1; +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devc); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devc) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devc) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + if (DEBUG_PRI (msc_dev, DEB_CPU)) + fprintf (sim_deb, ">>MSC OTx: Command = %06o\n", dat); + msc_buf = dat; + msc_sta = msc_sta & ~STA_REJ; /* clear reject */ + if ((dat & 0377) == FNC_CLR) break; /* clear always ok */ + if (msc_sta & STA_BUSY) { /* busy? reject */ + msc_sta = msc_sta | STA_REJ; /* dont chg select */ + break; + } + if (dat & FNF_CHS) { /* select change */ + msc_usl = map_sel[FNC_GETSEL (dat)]; /* is immediate */ + uptr = msc_dev.units + msc_usl; + if (DEBUG_PRI (msc_dev, DEB_CMDS)) + fprintf (sim_deb, ">>MSC OTx: Unit %d selected\n", msc_usl); + } + if (((dat & FNF_MOT) && sim_is_active (uptr)) || + ((dat & FNF_REV) && sim_tape_bot (uptr)) || + ((dat & FNF_WRT) && sim_tape_wrp (uptr))) + msc_sta = msc_sta | STA_REJ; /* reject? */ + break; + + case ioLIX: /* load */ + dat = 0; + case ioMIX: /* merge */ + dat = dat | (msc_sta & ~STA_DYN); /* get card status */ + if ((uptr->flags & UNIT_OFFLINE) == 0) { /* online? */ + dat = dat | uptr->UST; /* add unit status */ + if (sim_tape_bot (uptr)) /* BOT? */ + dat = dat | STA_BOT; + if (sim_is_active (uptr) && /* TBSY unless RWD at BOT */ + !((uptr->FNC & FNF_RWD) && sim_tape_bot (uptr))) + dat = dat | STA_TBSY; + if (sim_tape_wrp (uptr)) /* write prot? */ + dat = dat | STA_WLK; + if (sim_tape_eot (uptr)) /* EOT? */ + dat = dat | STA_EOT; + } + else dat = dat | STA_TBSY | STA_LOCAL; + if (ms_ctype) dat = dat | STA_PE | /* 13183A? */ + (msc_usl << STA_V_SEL); + if (DEBUG_PRI (msc_dev, DEB_CPU)) + fprintf (sim_deb, ">>MSC LIx: Status = %06o\n", dat); + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { clrCTL (devc); } /* CLC */ + else if (!(msc_sta & STA_REJ)) { /* STC, last cmd rejected? */ + if ((msc_buf & 0377) == FNC_CLR) { /* clear? */ + for (i = 0; i < MS_NUMDR; i++) { /* look for write in progr */ + if (sim_is_active (&msc_unit[i]) && /* unit active? */ + (msc_unit[i].FNC == FNC_WC) && /* last cmd write? */ + (ms_ptr > 0)) { /* partial buffer? */ + if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC STC: Unit %d wrote %d word partial record\n", + i, ms_ptr / 2); + if (st = sim_tape_wrrecf (uptr, msxb, ms_ptr | MTR_ERF)) + ms_map_err (uptr, st); /* discard any error */ + ms_ptr = 0; /* clear partial */ + } + if ((msc_unit[i].UST & STA_REW) == 0) + sim_cancel (&msc_unit[i]); /* stop if not rew */ + } + setCTL (devc); /* set CTL for STC */ + setFSR (devc); /* set FLG for completion */ + msc_sta = msc_1st = 0; /* clr ctlr status */ + if (DEBUG_PRI (msc_dev, DEB_CMDS)) + fputs (">>MSC STC: Controller cleared\n", sim_deb); + return SCPE_OK; + } + uptr->FNC = msc_buf & 0377; /* save function */ + if (uptr->FNC & FNF_RWD) { /* rewind? */ + if (!sim_tape_bot (uptr)) /* not at BOT? */ + uptr->UST = STA_REW; /* set rewinding */ + sched_time = msc_rtime; /* set response time */ + } + else { + if (sim_tape_bot (uptr)) /* at BOT? */ + sched_time = msc_btime; /* use BOT start time */ + else if ((uptr->FNC == FNC_GAP) || (uptr->FNC == FNC_GFM)) + sched_time = msc_gtime; /* use gap traversal time */ + else sched_time = 0; + if (uptr->FNC != FNC_GAP) + sched_time += msc_ctime; /* add base command time */ + } + if (msc_buf & ~FNC_SEL) { /* NOP for unit sel alone */ + sim_activate (uptr, sched_time); /* else schedule op */ + if (DEBUG_PRI (msc_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MSC STC: Unit %d command %03o (%s) scheduled, " + "pos = %d, time = %d\n", + msc_usl, uptr->FNC, ms_cmd_name (uptr->FNC), + uptr->pos, sched_time); + } + else if (DEBUG_PRI (msc_dev, DEB_CMDS)) + fputs (">>MSC STC: Unit select (NOP)\n", sim_deb); + msc_sta = STA_BUSY; /* ctrl is busy */ + msc_1st = 1; + setCTL (devc); /* go */ + } + break; + + case ioEDT: /* DMA end */ + clrFSR (devc); /* same as CLF */ + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devc); } /* H/C option */ +return dat; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt. + + In addition to decreasing the timing intervals, the FASTTIME option enables + two additional optimizations: WFM for GFM substitution, and BOT gap + elimination. If FASTTIME is selected, gap and file mark (GFM) commands are + processed as WFM (write file mark) commands. That is, the preceding GAP is + not performed. Also, the initial gap that normally precedes the first data + record or EOF mark at the beginning of the tape is omitted. These omissions + result in smaller tape image files. If REALTIME is selected, the gaps are + included. Note that the gaps (and realistic timing) are necessary to pass + the 7970 diagnostics. +*/ + +t_stat msc_svc (UNIT *uptr) +{ +int32 devc, devd, unum; +t_mtrlnt tbc; +t_stat st, r = SCPE_OK; + +devc = msc_dib.devno; /* get device nos */ +devd = msd_dib.devno; +unum = uptr - msc_dev.units; /* get unit number */ + +if ((uptr->FNC != FNC_RWS) && (uptr->flags & UNIT_OFFLINE)) { /* offline? */ + msc_sta = (msc_sta | STA_REJ) & ~STA_BUSY; /* reject */ + setFSR (devc); /* set cch flg */ + return IORETURN (msc_stopioe, SCPE_UNATT); + } + +switch (uptr->FNC) { /* case on function */ + + case FNC_RWS: /* rewind offline */ + sim_tape_rewind (uptr); /* rewind tape */ + uptr->flags = uptr->flags | UNIT_OFFLINE; /* set offline */ + uptr->UST = 0; /* clear REW status */ + break; /* we're done */ + + case FNC_REW: /* rewind */ + if (uptr->UST & STA_REW) { /* rewind in prog? */ + uptr->FNC |= FNC_CMPL; /* set compl state */ + sim_activate (uptr, msc_ctime); /* sched completion */ + } + break; /* anyway, ctrl done */ + + case FNC_REW | FNC_CMPL: /* complete rewind */ + sim_tape_rewind (uptr); /* rewind tape */ + uptr->UST = 0; /* clear REW status */ + return SCPE_OK; /* drive is free */ + + case FNC_GFM: /* gap + file mark */ + if (ms_timing == 1) /* fast timing? */ + goto DO_WFM; /* do plain file mark */ + /* else fall into GAP */ + case FNC_GAP: /* erase gap */ + if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d wrote gap\n", + unum); + if ((r = ms_write_gap (uptr)) || /* write tape gap; error? */ + (uptr->FNC != FNC_GFM)) /* not GFM? */ + break; /* bail out now */ + /* else drop into WFM */ + case FNC_WFM: /* write file mark */ + if ((ms_timing == 0) && sim_tape_bot (uptr)) { /* realistic timing + BOT? */ + if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d wrote initial gap\n", + unum); + if (st = ms_write_gap (uptr)) { /* write initial gap; error? */ + r = ms_map_err (uptr, st); /* map error */ + break; /* terminate operation */ + } + } + DO_WFM: + if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d wrote file mark\n", + unum); + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = ms_map_err (uptr, st); /* map error */ + msc_sta = STA_EOF; /* set EOF status */ + break; + + case FNC_FSR: /* space forward */ + if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ + r = ms_map_err (uptr, st); /* map error */ + if (tbc & 1) msc_sta = msc_sta | STA_ODD; + else msc_sta = msc_sta & ~STA_ODD; + break; + + case FNC_BSR: /* space reverse */ + if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ + r = ms_map_err (uptr, st); /* map error */ + if (tbc & 1) msc_sta = msc_sta | STA_ODD; + else msc_sta = msc_sta & ~STA_ODD; + break; + + case FNC_FSF: /* space fwd file */ + while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) { + if (sim_tape_eot (uptr)) break; /* EOT stops */ + } + r = ms_map_err (uptr, st); /* map error */ + break; + + case FNC_BSF: /* space rev file */ + while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; + r = ms_map_err (uptr, st); /* map error */ + break; + + case FNC_RFF: /* diagnostic read */ + case FNC_RC: /* read */ + if (msc_1st) { /* first svc? */ + msc_1st = ms_ptr = ms_max = 0; /* clr 1st flop */ + st = sim_tape_rdrecf (uptr, msxb, &ms_max, DBSIZE); /* read rec */ + if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d read %d word record\n", + unum, ms_max / 2); + if (st == MTSE_RECE) msc_sta = msc_sta | STA_PAR; /* rec in err? */ + else if (st != MTSE_OK) { /* other error? */ + r = ms_map_err (uptr, st); /* map error */ + if (r == SCPE_OK) { /* recoverable? */ + sim_activate (uptr, msc_itime); /* sched IRG */ + uptr->FNC |= FNC_CMPL; /* set completion */ + return SCPE_OK; + } + break; /* err, done */ + } + if (ms_ctype) msc_sta = msc_sta | STA_ODD; /* set ODD for 13183A */ + } + if (CTL (devd) && (ms_ptr < ms_max)) { /* DCH on, more data? */ + if (FLG (devd)) msc_sta = msc_sta | STA_TIM | STA_PAR; + msd_buf = ((uint16) msxb[ms_ptr] << 8) | + ((ms_ptr + 1 == ms_max) ? 0 : msxb[ms_ptr + 1]); + ms_ptr = ms_ptr + 2; + setFSR (devd); /* set dch flg */ + sim_activate (uptr, msc_xtime); /* re-activate */ + return SCPE_OK; + } + if (ms_max & 1) msc_sta = msc_sta | STA_ODD; /* set ODD by rec len */ + else msc_sta = msc_sta & ~STA_ODD; + sim_activate (uptr, msc_itime); /* sched IRG */ + if (uptr->FNC == FNC_RFF) msc_1st = 1; /* diagnostic? */ + else uptr->FNC |= FNC_CMPL; /* set completion */ + return SCPE_OK; + + case FNC_RFF | FNC_CMPL: /* diagnostic read completion */ + case FNC_RC | FNC_CMPL: /* read completion */ + break; + + case FNC_WC: /* write */ + if (msc_1st) { /* first service? */ + msc_1st = ms_ptr = 0; /* no data xfer on first svc */ + if ((ms_timing == 0) && sim_tape_bot (uptr)) { /* realistic timing + BOT? */ + if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d wrote initial gap\n", + unum); + if (st = ms_write_gap (uptr)) { /* write initial gap; error? */ + r = ms_map_err (uptr, st); /* map error */ + break; /* terminate operation */ + } + } + } + else { /* not 1st, next char */ + if (ms_ptr < DBSIZE) { /* room in buffer? */ + msxb[ms_ptr] = msd_buf >> 8; /* store 2 char */ + msxb[ms_ptr + 1] = msd_buf & 0377; + ms_ptr = ms_ptr + 2; + } + else msc_sta = msc_sta | STA_PAR; + } + if (CTL (devd)) { /* xfer flop set? */ + setFSR (devd); /* set dch flag */ + sim_activate (uptr, msc_xtime); /* re-activate */ + return SCPE_OK; + } + if (ms_ptr) { /* any data? write */ + if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d wrote %d word record\n", + unum, ms_ptr / 2); + if (st = sim_tape_wrrecf (uptr, msxb, ms_ptr)) { /* write, err? */ + r = ms_map_err (uptr, st); /* map error */ + break; + } + } + sim_activate (uptr, msc_itime); /* sched IRG */ + uptr->FNC |= FNC_CMPL; /* set completion */ + return SCPE_OK; + + case FNC_WC | FNC_CMPL: /* write completion */ + break; + + case FNC_RRR: /* not supported */ + default: /* unknown command */ + if (DEBUG_PRI (msc_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d command %03o is unknown (NOP)\n", + unum, uptr->FNC); + break; + } + +setFSR (devc); /* set cch flg */ +msc_sta = msc_sta & ~STA_BUSY; /* update status */ +if (DEBUG_PRI (msc_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MSC svc: Unit %d command %03o (%s) complete\n", + unum, uptr->FNC & 0377, ms_cmd_name (uptr->FNC)); +return r; +} + +/* Write an erase gap */ + +t_stat ms_write_gap (UNIT *uptr) +{ +t_stat st; +uint32 gap_len = ms_ctype ? GAP_13183 : GAP_13181; /* establish gap length */ +uint32 tape_bpi = ms_ctype ? BPI_13183 : BPI_13181; /* establish nominal bpi */ + +if (st = sim_tape_wrgap (uptr, gap_len, tape_bpi)) /* write gap */ + return ms_map_err (uptr, st); /* map error if failure */ +else + return SCPE_OK; +} + +/* Map tape error status */ + +t_stat ms_map_err (UNIT *uptr, t_stat st) +{ +int32 unum = uptr - msc_dev.units; /* get unit number */ + +if (DEBUG_PRI (msc_dev, DEB_RWS)) + fprintf (sim_deb, + ">>MSC err: Unit %d tape library status = %d\n", + unum, st); + +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + msc_sta = msc_sta | STA_REJ; /* reject cmd */ + return SCPE_FMT; /* format error */ + + case MTSE_UNATT: /* unattached */ + msc_detach (uptr); /* resync status (ignore rtn) */ + msc_sta = msc_sta | STA_REJ; /* reject cmd */ + return SCPE_UNATT; /* unit unattached */ + + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_EOM: /* end of medium */ + case MTSE_TMK: /* end of file */ + msc_sta = msc_sta | STA_EOF | (ms_ctype ? 0 : STA_ODD); + break; /* EOF also sets ODD for 13181A */ + + case MTSE_INVRL: /* invalid rec lnt */ + msc_sta = msc_sta | STA_PAR; + return SCPE_MTRLNT; + + case MTSE_IOERR: /* IO error */ + msc_sta = msc_sta | STA_PAR; /* error */ + if (msc_stopioe) return SCPE_IOERR; + break; + + case MTSE_RECE: /* record in error */ + msc_sta = msc_sta | STA_PAR; /* error */ + break; + + case MTSE_WRP: /* write protect */ + msc_sta = msc_sta | STA_REJ; /* reject */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat msc_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +hp_enbdis_pair (dptr, /* make pair cons */ + (dptr == &msd_dev)? &msc_dev: &msd_dev); +msc_buf = msd_buf = 0; +msc_sta = msc_usl = 0; +msc_1st = 0; +msc_dib.cmd = msd_dib.cmd = 0; /* clear cmd */ +msc_dib.ctl = msd_dib.ctl = 0; /* clear ctl */ +msc_dib.flg = msd_dib.flg = 1; /* set flg */ +msc_dib.fbf = msd_dib.fbf = 1; /* set fbf */ +msc_dib.srq = msd_dib.srq = 1; /* srq follows flg */ +for (i = 0; i < MS_NUMDR; i++) { + uptr = msc_dev.units + i; + sim_tape_reset (uptr); + sim_cancel (uptr); + uptr->UST = 0; + } +ms_config_timing (); +return SCPE_OK; +} + +/* Attach routine */ + +t_stat msc_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); /* attach unit */ +if (r == SCPE_OK) + uptr->flags = uptr->flags & ~UNIT_OFFLINE; /* set online */ +return r; +} + +/* Detach routine */ + +t_stat msc_detach (UNIT* uptr) +{ +uptr->UST = 0; /* clear status */ +uptr->flags = uptr->flags | UNIT_OFFLINE; /* set offline */ +return sim_tape_detach (uptr); /* detach unit */ +} + +/* Online routine */ + +t_stat msc_online (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_OK; +else return SCPE_UNATT; +} + +/* Configure timing */ + +void ms_config_timing (void) +{ +uint32 i, tset; + +tset = (ms_timing << 1) | (ms_timing? 0 : ms_ctype); /* select timing set */ +for (i = 0; i < (sizeof (timers) / sizeof (timers[0])); i++) + *timers[i] = msc_times[tset][i]; /* assign times */ +} + +/* Set controller timing */ + +t_stat ms_set_timing (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; +ms_timing = val; +ms_config_timing (); +return SCPE_OK; +} + +/* Show controller timing */ + +t_stat ms_show_timing (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (ms_timing) fputs ("fast timing", st); +else fputs ("realistic timing", st); +return SCPE_OK; +} + +/* Set controller type */ + +t_stat ms_settype (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i; + +if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; +for (i = 0; i < MS_NUMDR; i++) { + if (msc_unit[i].flags & UNIT_ATT) return SCPE_ALATT; + } +ms_ctype = val; +ms_config_timing (); /* update for new type */ +return SCPE_OK; +} + +/* Show controller type */ + +t_stat ms_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (ms_ctype) fprintf (st, "13183A"); +else fprintf (st, "13181A"); +return SCPE_OK; +} + +/* Set unit reel size + + val = 0 -> SET MSCn CAPACITY=n + val = 1 -> SET MSCn REEL=n */ + +t_stat ms_set_reelsize (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 reel; +t_stat status; + +if (val == 0) { + status = sim_tape_set_capac (uptr, val, cptr, desc); + if (status == SCPE_OK) uptr->REEL = 0; + return status; + } + +if (cptr == NULL) return SCPE_ARG; +reel = (int32) get_uint (cptr, 10, 2400, &status); +if (status != SCPE_OK) return status; +else switch (reel) { + + case 0: + uptr->REEL = 0; /* type 0 = unlimited/custom */ + break; + + case 600: + uptr->REEL = 1; /* type 1 = 600 foot */ + break; + + case 1200: + uptr->REEL = 2; /* type 2 = 1200 foot */ + break; + + case 2400: + uptr->REEL = 3; /* type 3 = 2400 foot */ + break; + + default: + return SCPE_ARG; + } + +uptr->capac = uptr->REEL? (TCAP << uptr->REEL) << ms_ctype: 0; +return SCPE_OK; +} + +/* Show unit reel size + + val = 0 -> SHOW MSC or SHOW MSCn or SHOW MSCn CAPACITY + val = 1 -> SHOW MSCn REEL */ + +t_stat ms_show_reelsize (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +t_stat status = SCPE_OK; + +if (uptr->REEL == 0) status = sim_tape_show_capac (st, uptr, val, desc); +else fprintf (st, "%4d foot reel", 300 << uptr->REEL); +if (val == 1) fputc ('\n', st); /* MTAB_NMO omits \n */ +return status; +} + +/* Translate command to mnemonic for debug logging + + The command names and descriptions are taken from the 13181 interface + manual. */ + +char *ms_cmd_name (uint32 cmd) +{ + +switch (cmd & 0377) { + case FNC_WC: return "WCC"; /* Write command */ + case FNC_WFM: return "WFM"; /* Write file mark */ + case FNC_RC: return "RRF"; /* Read record forward */ + case FNC_FSR: return "FSR"; /* Forward space record */ + case FNC_FSF: return "FSF"; /* Forward space file */ + case FNC_GAP: return "GAP"; /* Write gap */ + case FNC_BSR: return "BSR"; /* Backspace record */ + case FNC_BSF: return "BSF"; /* Backspace file */ + case FNC_REW: return "REW"; /* Rewind */ + case FNC_RWS: return "RWO"; /* Rewind off-line */ + case FNC_CLR: return "CLR"; /* Clear controller */ + case FNC_GFM: return "GFM"; /* Gap file mark */ + case FNC_RFF: return "RFF"; /* Read forward until file mark (diag) */ + case FNC_RRR: return "RRR"; /* Read record in reverse (diag) */ + + default: return "???"; /* Unknown command */ + } +} + +/* 7970B/7970E bootstrap routine (HP 12992D ROM) */ + +const uint16 ms_rom[IBL_LNT] = { + 0106501, /*ST LIB 1 ; read sw */ + 0006011, /* SLB,RSS ; bit 0 set? */ + 0027714, /* JMP RD ; no read */ + 0003004, /* CMA,INA ; A is ctr */ + 0073775, /* STA WC ; save */ + 0067772, /* LDA SL0RW ; sel 0, rew */ + 0017762, /*FF JSB CMD ; do cmd */ + 0102311, /* SFS CC ; done? */ + 0027707, /* JMP *-1 ; wait */ + 0067774, /* LDB FFC ; get file fwd */ + 0037775, /* ISZ WC ; done files? */ + 0027706, /* JMP FF ; no */ + 0067773, /*RD LDB RDCMD ; read cmd */ + 0017762, /* JSB CMD ; do cmd */ + 0103710, /* STC DC,C ; start dch */ + 0102211, /* SFC CC ; read done? */ + 0027752, /* JMP STAT ; no, get stat */ + 0102310, /* SFS DC ; any data? */ + 0027717, /* JMP *-3 ; wait */ + 0107510, /* LIB DC,C ; get rec cnt */ + 0005727, /* BLF,BLF ; move to lower */ + 0007000, /* CMB ; make neg */ + 0077775, /* STA WC ; save */ + 0102211, /* SFC CC ; read done? */ + 0027752, /* JMP STAT ; no, get stat */ + 0102310, /* SFS DC ; any data? */ + 0027727, /* JMP *-3 ; wait */ + 0107510, /* LIB DC,C ; get load addr */ + 0074000, /* STB 0 ; start csum */ + 0077762, /* STA CMD ; save address */ + 0027742, /* JMP *+4 */ + 0177762, /*NW STB CMD,I ; store data */ + 0040001, /* ADA 1 ; add to csum */ + 0037762, /* ISZ CMD ; adv addr ptr */ + 0102310, /* SFS DC ; any data? */ + 0027742, /* JMP *-1 ; wait */ + 0107510, /* LIB DC,C ; get word */ + 0037775, /* ISZ WC ; done? */ + 0027737, /* JMP NW ; no */ + 0054000, /* CPB 0 ; csum ok? */ + 0027717, /* JMP RD+3 ; yes, cont */ + 0102011, /* HLT 11 ; no, halt */ + 0102511, /*ST LIA CC ; get status */ + 0001727, /* ALF,ALF ; get eof bit */ + 0002020, /* SSA ; set? */ + 0102077, /* HLT 77 ; done */ + 0001727, /* ALF,ALF ; put status back */ + 0001310, /* RAR,SLA ; read ok? */ + 0102000, /* HLT 0 ; no */ + 0027714, /* JMP RD ; read next */ + 0000000, /*CMD 0 */ + 0106611, /* OTB CC ; output cmd */ + 0102511, /* LIA CC ; check for reject */ + 0001323, /* RAR,RAR */ + 0001310, /* RAR,SLA */ + 0027763, /* JMP CMD+1 ; try again */ + 0103711, /* STC CC,C ; start command */ + 0127762, /* JMP CMD,I ; exit */ + 0001501, /*SL0RW 001501 ; select 0, rewind */ + 0001423, /*RDCMD 001423 ; read record */ + 0000203, /*FFC 000203 ; space forward file */ + 0000000, /*WC 000000 */ + 0000000, + 0000000 + }; + +t_stat msc_boot (int32 unitno, DEVICE *dptr) +{ +int32 dev; +extern uint32 saved_AR; + +if (unitno != 0) return SCPE_NOFNC; /* only unit 0 */ +dev = msd_dib.devno; /* get data chan dev */ +if (ibl_copy (ms_rom, dev)) return SCPE_IERR; /* copy boot to memory */ +SR = (SR & IBL_OPT) | IBL_MS | (dev << IBL_V_DEV); /* set SR */ +if ((sim_switches & SWMASK ('S')) && saved_AR) SR = SR | 1; /* skip? */ +return SCPE_OK; +} + diff --git a/HP2100/hp2100_mt.c b/HP2100/hp2100_mt.c new file mode 100644 index 0000000..61f379e --- /dev/null +++ b/HP2100/hp2100_mt.c @@ -0,0 +1,547 @@ +/* hp2100_mt.c: HP 2100 12559A magnetic tape simulator + + Copyright (c) 1993-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt 12559A 3030 nine track magnetic tape + + 28-Dec-06 JDB Added ioCRS state to I/O decoders (action unverified) + 07-Oct-04 JDB Allow enable/disable from either device + 14-Aug-04 RMS Modified handling of end of medium (suggested by Dave Bryan) + 06-Jul-04 RMS Fixed spurious timing error after CLC (found by Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Implemented DMA SRQ (follows FLG) + 21-Dec-03 RMS Adjusted msc_ctime for TSB (from Mike Gemeny) + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised for magtape library + 30-Sep-02 RMS Revamped error handling + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length test + 20-Jan-02 RMS Fixed bug on last character write + 03-Dec-01 RMS Added read only unit, extended SET/SHOW support + 07-Sep-01 RMS Moved function prototypes + 30-Nov-00 RMS Made variable names unique + 04-Oct-98 RMS V2.4 magtape format + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. + + Unusually among HP peripherals, the 12559 does not have a command flop, + and its flag and flag buffer power up as clear rather than set. +*/ + +#include "hp2100_defs.h" +#include "sim_tape.h" + +#define DB_V_SIZE 16 /* max data buf */ +#define DBSIZE (1 << DB_V_SIZE) /* max data cmd */ + +/* Command - mtc_fnc */ + +#define FNC_CLR 0300 /* clear */ +#define FNC_WC 0031 /* write */ +#define FNC_RC 0023 /* read */ +#define FNC_GAP 0011 /* write gap */ +#define FNC_FSR 0003 /* forward space */ +#define FNC_BSR 0041 /* backward space */ +#define FNC_REW 0201 /* rewind */ +#define FNC_RWS 0101 /* rewind and offline */ +#define FNC_WFM 0035 /* write file mark */ + +/* Status - stored in mtc_sta, (d) = dynamic */ + +#define STA_LOCAL 0400 /* local (d) */ +#define STA_EOF 0200 /* end of file */ +#define STA_BOT 0100 /* beginning of tape */ +#define STA_EOT 0040 /* end of tape */ +#define STA_TIM 0020 /* timing error */ +#define STA_REJ 0010 /* programming error */ +#define STA_WLK 0004 /* write locked (d) */ +#define STA_PAR 0002 /* parity error */ +#define STA_BUSY 0001 /* busy (d) */ + +extern uint32 PC; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; + +int32 mtc_fnc = 0; /* function */ +int32 mtc_sta = 0; /* status register */ +int32 mtc_dtf = 0; /* data xfer flop */ +int32 mtc_1st = 0; /* first svc flop */ +int32 mtc_ctime = 40; /* command wait */ +int32 mtc_gtime = 1000; /* gap stop time */ +int32 mtc_xtime = 15; /* data xfer time */ +int32 mtc_stopioe = 1; /* stop on error */ +uint8 mtxb[DBSIZE] = { 0 }; /* data buffer */ +t_mtrlnt mt_ptr = 0, mt_max = 0; /* buffer ptrs */ +static const int32 mtc_cmd[] = { + FNC_WC, FNC_RC, FNC_GAP, FNC_FSR, FNC_BSR, FNC_REW, FNC_RWS, FNC_WFM }; + +DEVICE mtd_dev, mtc_dev; +int32 mtdio (int32 inst, int32 IR, int32 dat); +int32 mtcio (int32 inst, int32 IR, int32 dat); +t_stat mtc_svc (UNIT *uptr); +t_stat mtd_reset (DEVICE *dptr); +t_stat mtc_reset (DEVICE *dptr); +t_stat mtc_attach (UNIT *uptr, char *cptr); +t_stat mtc_detach (UNIT *uptr); +t_stat mt_map_err (UNIT *uptr, t_stat st); + +/* MTD data structures + + mtd_dev MTD device descriptor + mtd_unit MTD unit list + mtd_reg MTD register list +*/ + +DIB mt_dib[] = { + { MTD, 0, 0, 0, 0, 0, &mtdio }, + { MTC, 0, 0, 0, 0, 0, &mtcio } + }; + +#define mtd_dib mt_dib[0] +#define mtc_dib mt_dib[1] + +UNIT mtd_unit = { UDATA (NULL, 0, 0) }; + +REG mtd_reg[] = { + { FLDATA (CMD, mtd_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, mtd_dib.ctl, 0), REG_HRO }, + { FLDATA (FLG, mtd_dib.flg, 0) }, + { FLDATA (FBF, mtd_dib.fbf, 0) }, + { FLDATA (SRQ, mtd_dib.srq, 0) }, + { BRDATA (DBUF, mtxb, 8, 8, DBSIZE) }, + { DRDATA (BPTR, mt_ptr, DB_V_SIZE + 1) }, + { DRDATA (BMAX, mt_max, DB_V_SIZE + 1) }, + { ORDATA (DEVNO, mtd_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB mtd_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &mtd_dev }, + { 0 } + }; + +DEVICE mtd_dev = { + "MTD", &mtd_unit, mtd_reg, mtd_mod, + 1, 10, 16, 1, 8, 8, + NULL, NULL, &mtd_reset, + NULL, NULL, NULL, + &mtd_dib, DEV_DISABLE | DEV_DIS + }; + +/* MTC data structures + + mtc_dev MTC device descriptor + mtc_unit MTC unit list + mtc_reg MTC register list + mtc_mod MTC modifier list +*/ + +UNIT mtc_unit = { UDATA (&mtc_svc, UNIT_ATTABLE + UNIT_ROABLE, 0) }; + +REG mtc_reg[] = { + { ORDATA (FNC, mtc_fnc, 8) }, + { ORDATA (STA, mtc_sta, 9) }, + { ORDATA (BUF, mtc_unit.buf, 8) }, + { FLDATA (CMD, mtc_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, mtc_dib.ctl, 0) }, + { FLDATA (FLG, mtc_dib.flg, 0) }, + { FLDATA (FBF, mtc_dib.fbf, 0) }, + { FLDATA (SRQ, mtc_dib.srq, 0) }, + { FLDATA (DTF, mtc_dtf, 0) }, + { FLDATA (FSVC, mtc_1st, 0) }, + { DRDATA (POS, mtc_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (CTIME, mtc_ctime, 24), REG_NZ + PV_LEFT }, + { DRDATA (GTIME, mtc_gtime, 24), REG_NZ + PV_LEFT }, + { DRDATA (XTIME, mtc_xtime, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, mtc_stopioe, 0) }, + { ORDATA (DEVNO, mtc_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB mtc_mod[] = { + { MTAB_XTD | MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &mtd_dev }, + { 0 } + }; + +DEVICE mtc_dev = { + "MTC", &mtc_unit, mtc_reg, mtc_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mtc_reset, + NULL, &mtc_attach, &mtc_detach, + &mtc_dib, DEV_DISABLE | DEV_DIS + }; + +/* IO instructions */ + +int32 mtdio (int32 inst, int32 IR, int32 dat) +{ +int32 devd; + +devd = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devd); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devd) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devd) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + mtc_unit.buf = dat & 0377; /* store data */ + break; + + case ioMIX: /* merge */ + dat = dat | mtc_unit.buf; + break; + + case ioLIX: /* load */ + dat = mtc_unit.buf; + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) mtc_dtf = 0; /* CLC: clr xfer flop */ + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devd); } /* H/C option */ +return dat; +} + +int32 mtcio (int32 inst, int32 IR, int32 dat) +{ +uint32 i; +int32 devc, devd, valid; +t_stat st; + +devc = IR & I_DEVMASK; /* get device no */ +devd = devc - 1; +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (devc); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (devc) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (devc) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + dat = dat & 0377; + mtc_sta = mtc_sta & ~STA_REJ; /* clear reject */ + if (dat == FNC_CLR) { /* clear? */ + if (sim_is_active (&mtc_unit) && /* write in prog? */ + (mtc_fnc == FNC_WC) && (mt_ptr > 0)) { /* yes, bad rec */ + if (st = sim_tape_wrrecf (&mtc_unit, mtxb, mt_ptr | MTR_ERF)) + mt_map_err (&mtc_unit, st); + } + if (((mtc_fnc == FNC_REW) || (mtc_fnc == FNC_RWS)) && + sim_is_active (&mtc_unit)) sim_cancel (&mtc_unit); + mtc_1st = mtc_dtf = 0; + mtc_sta = mtc_sta & STA_BOT; + clrCTL (devc); /* init device */ + clrFSR (devc); + clrCTL (devd); + clrFSR (devd); + return SCPE_OK; + } + for (i = valid = 0; i < sizeof (mtc_cmd); i++) /* is fnc valid? */ + if (dat == mtc_cmd[i]) valid = 1; + if (!valid || sim_is_active (&mtc_unit) || /* is cmd valid? */ + ((mtc_sta & STA_BOT) && (dat == FNC_BSR)) || + (sim_tape_wrp (&mtc_unit) && + ((dat == FNC_WC) || (dat == FNC_GAP) || (dat == FNC_WFM)))) + mtc_sta = mtc_sta | STA_REJ; + else { + sim_activate (&mtc_unit, mtc_ctime); /* start tape */ + mtc_fnc = dat; /* save function */ + mtc_sta = STA_BUSY; /* unit busy */ + mt_ptr = 0; /* init buffer ptr */ + clrFSR (devc); /* clear flags */ + clrFSR (devd); + mtc_1st = 1; /* set 1st flop */ + mtc_dtf = 1; /* set xfer flop */ + } + break; + + case ioLIX: /* load */ + dat = 0; + case ioMIX: /* merge */ + dat = dat | (mtc_sta & ~(STA_LOCAL | STA_WLK | STA_BUSY)); + if (mtc_unit.flags & UNIT_ATT) { /* construct status */ + if (sim_is_active (&mtc_unit)) dat = dat | STA_BUSY; + if (sim_tape_wrp (&mtc_unit)) dat = dat | STA_WLK; + } + else dat = dat | STA_BUSY | STA_LOCAL; + break; + + case ioCRS: /* control reset (action unverif) */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { clrCTL (devc); } /* CLC */ + else { setCTL (devc); } /* STC */ + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (devc); } /* H/C option */ +return dat; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt + + Can't be write locked, can only write lock detached unit +*/ + +t_stat mtc_svc (UNIT *uptr) +{ +int32 devc, devd; +t_mtrlnt tbc; +t_stat st, r = SCPE_OK; + +devc = mtc_dib.devno; /* get device nos */ +devd = mtd_dib.devno; +if ((mtc_unit.flags & UNIT_ATT) == 0) { /* offline? */ + mtc_sta = STA_LOCAL | STA_REJ; /* rejected */ + setFSR (devc); /* set cch flg */ + return IORETURN (mtc_stopioe, SCPE_UNATT); + } + +switch (mtc_fnc) { /* case on function */ + + case FNC_REW: /* rewind */ + sim_tape_rewind (uptr); /* BOT */ + mtc_sta = STA_BOT; /* update status */ + break; + + case FNC_RWS: /* rewind and offline */ + sim_tape_rewind (uptr); /* clear position */ + return sim_tape_detach (uptr); /* don't set cch flg */ + + case FNC_WFM: /* write file mark */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = mt_map_err (uptr, st); /* map error */ + mtc_sta = STA_EOF; /* set EOF status */ + break; + + case FNC_GAP: /* erase gap */ + break; + + case FNC_FSR: /* space forward */ + if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; + + case FNC_BSR: /* space reverse */ + if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; + + case FNC_RC: /* read */ + if (mtc_1st) { /* first svc? */ + mtc_1st = mt_ptr = 0; /* clr 1st flop */ + st = sim_tape_rdrecf (uptr, mtxb, &mt_max, DBSIZE); /* read rec */ + if (st == MTSE_RECE) mtc_sta = mtc_sta | STA_PAR; /* rec in err? */ + else if (st != MTSE_OK) { /* other error? */ + r = mt_map_err (uptr, st); /* map error */ + if (r == SCPE_OK) { /* recoverable? */ + sim_activate (uptr, mtc_gtime); /* sched IRG */ + mtc_fnc = 0; /* NOP func */ + return SCPE_OK; + } + break; /* non-recov, done */ + } + if (mt_max < 12) { /* record too short? */ + mtc_sta = mtc_sta | STA_PAR; /* set flag */ + break; + } + } + if (mtc_dtf && (mt_ptr < mt_max)) { /* more chars? */ + if (FLG (devd)) mtc_sta = mtc_sta | STA_TIM; + mtc_unit.buf = mtxb[mt_ptr++]; /* fetch next */ + setFSR (devd); /* set dch flg */ + sim_activate (uptr, mtc_xtime); /* re-activate */ + return SCPE_OK; + } + sim_activate (uptr, mtc_gtime); /* schedule gap */ + mtc_fnc = 0; /* nop */ + return SCPE_OK; + + case FNC_WC: /* write */ + if (mtc_1st) mtc_1st = 0; /* no xfr on first */ + else { + if (mt_ptr < DBSIZE) { /* room in buffer? */ + mtxb[mt_ptr++] = mtc_unit.buf; + mtc_sta = mtc_sta & ~STA_BOT; /* clear BOT */ + } + else mtc_sta = mtc_sta | STA_PAR; + } + if (mtc_dtf) { /* xfer flop set? */ + setFSR (devd); /* set dch flag */ + sim_activate (uptr, mtc_xtime); /* re-activate */ + return SCPE_OK; + } + if (mt_ptr) { /* write buffer */ + if (st = sim_tape_wrrecf (uptr, mtxb, mt_ptr)) { /* write, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* done */ + } + } + sim_activate (uptr, mtc_gtime); /* schedule gap */ + mtc_fnc = 0; /* nop */ + return SCPE_OK; + + default: /* unknown */ + break; + } + +setFSR (devc); /* set cch flg */ +mtc_sta = mtc_sta & ~STA_BUSY; /* not busy */ +return r; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* unattached */ + mtc_sta = mtc_sta | STA_REJ; /* reject */ + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_EOM: /* end of medium */ + case MTSE_TMK: /* end of file */ + mtc_sta = mtc_sta | STA_EOF; /* eof */ + break; + + case MTSE_IOERR: /* IO error */ + mtc_sta = mtc_sta | STA_PAR; /* error */ + if (mtc_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + mtc_sta = mtc_sta | STA_PAR; + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + mtc_sta = mtc_sta | STA_PAR; /* error */ + break; + + case MTSE_BOT: /* reverse into BOT */ + mtc_sta = mtc_sta | STA_BOT; /* set status */ + break; + + case MTSE_WRP: /* write protect */ + mtc_sta = mtc_sta | STA_REJ; /* reject */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mtd_reset (DEVICE *dptr) +{ +hp_enbdis_pair (&mtd_dev, &mtc_dev); /* make pair cons */ +return mtc_reset (dptr); /* do common reset */ +} + +t_stat mtc_reset (DEVICE *dptr) +{ +hp_enbdis_pair (&mtc_dev, &mtd_dev); /* make pair cons */ +mtc_fnc = 0; +mtc_1st = mtc_dtf = 0; +mtc_dib.cmd = mtd_dib.cmd = 0; /* clear cmd */ +mtc_dib.ctl = mtd_dib.ctl = 0; /* clear ctl */ +mtc_dib.flg = mtd_dib.flg = 0; /* clear flg */ +mtc_dib.fbf = mtd_dib.fbf = 0; /* clear fbf */ +mtc_dib.srq = mtd_dib.srq = 0; /* srq follows flg */ +sim_cancel (&mtc_unit); /* cancel activity */ +sim_tape_reset (&mtc_unit); +if (mtc_unit.flags & UNIT_ATT) mtc_sta = + (sim_tape_bot (&mtc_unit)? STA_BOT: 0) | + (sim_tape_wrp (&mtc_unit)? STA_WLK: 0); +else mtc_sta = STA_LOCAL | STA_BUSY; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mtc_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* update status */ +mtc_sta = STA_BOT; +return r; +} + +/* Detach routine */ + +t_stat mtc_detach (UNIT* uptr) +{ +mtc_sta = 0; /* update status */ +return sim_tape_detach (uptr); /* detach unit */ +} diff --git a/HP2100/hp2100_mux.c b/HP2100/hp2100_mux.c new file mode 100644 index 0000000..2ec6c53 --- /dev/null +++ b/HP2100/hp2100_mux.c @@ -0,0 +1,1089 @@ +/* hp2100_mux.c: HP 2100 12920A terminal multiplexor simulator + + Copyright (c) 2002-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mux,muxl,muxc 12920A terminal multiplexor + + 16-Apr-08 JDB Sync mux poll with console poll for idle compatibility + 06-Mar-07 JDB Corrected "mux_sta" size from 16 to 21 elements + Fixed "muxc_reset" to clear lines 16-20 + 26-Feb-07 JDB Added debug printouts + Fixed control card OTx to set current channel number + Fixed to set "muxl_ibuf" in response to a transmit interrupt + Changed "mux_xbuf", "mux_rbuf" declarations from 8 to 16 bits + Fixed to set "mux_rchp" when a line break is received + Fixed incorrect "odd_par" table values + Reversed test in "RCV_PAR" to return "LIL_PAR" on odd parity + Fixed mux reset (ioCRS) to clear port parameters + Fixed to use PUT_DCH instead of PUT_CCH for data channel status + 10-Feb-07 JDB Added DIAG/TERM modifiers to implement diagnostic mode + 28-Dec-06 JDB Added ioCRS state to I/O decoders + 02-Jun-06 JDB Fixed compiler warning for mux_ldsc init + 22-Nov-05 RMS Revised for new terminal processing routines + 29-Jun-05 RMS Added SET MUXLn DISCONNECT + 07-Oct-04 JDB Allow enable/disable from any device + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Implemented DMA SRQ (follows FLG) + 05-Jan-04 RMS Revised for tmxr library changes + 21-Dec-03 RMS Added invalid character screening for TSB (from Mike Gemeny) + 09-May-03 RMS Added network device flag + 01-Nov-02 RMS Added 7B/8B support + 22-Aug-02 RMS Updated for changes to sim_tmxr + + The 12920A consists of three separate devices + + mux scanner (upper data card) + muxl lines (lower data card) + muxm modem control (control card) + + The lower data card has no CMD flop; the control card has no CMD flop. + The upper data card has none of the usual flops. + + Reference: + - 12920A Asynchronous Multiplexer Interface Kits Operating and Service + Manual (12920-90001, Oct-1972) +*/ + +#include "hp2100_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define MUX_LINES 16 /* user lines */ +#define MUX_ILINES 5 /* diag rcv only */ +#define UNIT_V_MDM (TTUF_V_UF + 0) /* modem control */ +#define UNIT_V_DIAG (TTUF_V_UF + 1) /* loopback diagnostic */ +#define UNIT_MDM (1 << UNIT_V_MDM) +#define UNIT_DIAG (1 << UNIT_V_DIAG) +#define MUXL_WAIT 500 + +/* Channel number (OTA upper, LIA lower or upper) */ + +#define MUX_V_CHAN 10 /* channel num */ +#define MUX_M_CHAN 037 +#define MUX_CHAN(x) (((x) >> MUX_V_CHAN) & MUX_M_CHAN) + +/* OTA, lower = parameters or data */ + +#define OTL_P 0100000 /* parameter */ +#define OTL_TX 0040000 /* transmit */ +#define OTL_ENB 0020000 /* enable */ +#define OTL_TPAR 0010000 /* xmt parity */ +#define OTL_ECHO 0010000 /* rcv echo */ +#define OTL_DIAG 0004000 /* diagnose */ +#define OTL_SYNC 0004000 /* sync */ +#define OTL_V_LNT 8 /* char length */ +#define OTL_M_LNT 07 +#define OTL_LNT(x) (((x) >> OTL_V_LNT) & OTL_M_LNT) +#define OTL_V_BAUD 0 /* baud rate */ +#define OTL_M_BAUD 0377 +#define OTL_BAUD(x) (((x) >> OTL_V_BAUD) & OTL_M_BAUD) +#define OTL_CHAR 03777 /* char mask */ +#define OTL_PAR 0200 /* char parity */ + +/* LIA, lower = received data */ + +#define LIL_PAR 0100000 /* parity */ +#define PUT_DCH(x) (((x) & MUX_M_CHAN) << MUX_V_CHAN) +#define LIL_CHAR 01777 /* character */ + +/* LIA, upper = status */ + +#define LIU_SEEK 0100000 /* seeking NI */ +#define LIU_DG 0000010 /* diagnose */ +#define LIU_BRK 0000004 /* break */ +#define LIU_LOST 0000002 /* char lost */ +#define LIU_TR 0000001 /* trans/rcv */ + +/* OTA, control */ + +#define OTC_SCAN 0100000 /* scan */ +#define OTC_UPD 0040000 /* update */ +#define OTC_V_CHAN 10 /* channel */ +#define OTC_M_CHAN 017 +#define OTC_CHAN(x) (((x) >> OTC_V_CHAN) & OTC_M_CHAN) +#define OTC_EC2 0000200 /* enable Cn upd */ +#define OTC_EC1 0000100 +#define OTC_C2 0000040 /* Cn flops */ +#define OTC_C1 0000020 +#define OTC_V_C 4 /* S1 to C1 */ +#define OTC_ES2 0000010 /* enb comparison */ +#define OTC_ES1 0000004 +#define OTC_V_ES 2 +#define OTC_SS2 0000002 /* SSn flops */ +#define OTC_SS1 0000001 +#define OTC_RW (OTC_ES2|OTC_ES1|OTC_SS2|OTC_SS1) +#define RTS OCT_C2 /* C2 = rts */ +#define DTR OTC_C1 /* C1 = dtr */ + +/* LIA, control */ + +#define LIC_MBO 0140000 /* always set */ +#define LIC_V_CHAN 10 /* channel */ +#define LIC_M_CHAN 017 +#define PUT_CCH(x) (((x) & OTC_M_CHAN) << OTC_V_CHAN) +#define LIC_I2 0001000 /* change flags */ +#define LIC_I1 0000400 +#define LIC_S2 0000002 /* Sn flops */ +#define LIC_S1 0000001 +#define LIC_V_I 8 /* S1 to I1 */ +#define CDET LIC_S2 /* S2 = cdet */ +#define DSR LIC_S1 /* S1 = dsr */ + +#define LIC_TSTI(ch) (((muxc_lia[ch] ^ muxc_ota[ch]) & \ + ((muxc_ota[ch] & (OTC_ES2|OTC_ES1)) >> OTC_V_ES)) \ + << LIC_V_I) + +/* Debug flags */ + +#define DEB_CMDS (1 << 0) /* Command initiation and completion */ +#define DEB_CPU (1 << 1) /* CPU I/O */ +#define DEB_XFER (1 << 2) /* Socket receive and transmit */ + +extern uint32 PC; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; +extern FILE *sim_deb; + +uint16 mux_sta[MUX_LINES + MUX_ILINES]; /* line status */ +uint16 mux_rpar[MUX_LINES + MUX_ILINES]; /* rcv param */ +uint16 mux_xpar[MUX_LINES]; /* xmt param */ +uint16 mux_rbuf[MUX_LINES + MUX_ILINES]; /* rcv buf */ +uint16 mux_xbuf[MUX_LINES]; /* xmt buf */ +uint8 mux_rchp[MUX_LINES + MUX_ILINES]; /* rcv chr pend */ +uint8 mux_xdon[MUX_LINES]; /* xmt done */ +uint8 muxc_ota[MUX_LINES]; /* ctrl: Cn,ESn,SSn */ +uint8 muxc_lia[MUX_LINES]; /* ctrl: Sn */ +uint32 muxl_ibuf = 0; /* low in: rcv data */ +uint32 muxl_obuf = 0; /* low out: param */ +uint32 muxu_ibuf = 0; /* upr in: status */ +uint32 muxu_obuf = 0; /* upr out: chan */ +uint32 muxc_chan = 0; /* ctrl chan */ +uint32 muxc_scan = 0; /* ctrl scan */ + +TMLN mux_ldsc[MUX_LINES] = { { 0 } }; /* line descriptors */ +TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc }; /* mux descriptor */ + +DEVICE muxl_dev, muxu_dev, muxc_dev; +int32 muxlio (int32 inst, int32 IR, int32 dat); +int32 muxuio (int32 inst, int32 IR, int32 dat); +int32 muxcio (int32 inst, int32 IR, int32 dat); +t_stat muxi_svc (UNIT *uptr); +t_stat muxo_svc (UNIT *uptr); +t_stat muxc_reset (DEVICE *dptr); +t_stat mux_attach (UNIT *uptr, char *cptr); +t_stat mux_detach (UNIT *uptr); +t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat mux_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat mux_show (FILE *st, UNIT *uptr, int32 val, void *desc); +void mux_data_int (void); +void mux_ctrl_int (void); +void mux_diag (int32 c); + +static uint8 odd_par[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 000-017 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 020-037 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 040-067 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 060-077 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 100-117 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 120-137 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 140-157 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 160-177 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 200-217 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 220-237 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 240-267 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 260-277 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 300-317 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 320-337 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 340-357 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 /* 360-377 */ + }; + +#define RCV_PAR(x) (odd_par[(x) & 0377] ? 0 : LIL_PAR) +#define XMT_PAR(x) (odd_par[(x) & 0377] ? 0 : OTL_PAR) + +/* Debug flags table */ + +DEBTAB mux_deb[] = { + { "CMDS", DEB_CMDS }, + { "CPU", DEB_CPU }, + { "XFER", DEB_XFER }, + { NULL, 0 } }; + +DIB mux_dib[] = { + { MUXL, 0, 0, 0, 0, 0, &muxlio }, + { MUXU, 0, 0, 0, 0, 0, &muxuio } + }; + +#define muxl_dib mux_dib[0] +#define muxu_dib mux_dib[1] + +/* MUX data structures + + muxu_dev MUX device descriptor + muxu_unit MUX unit descriptor + muxu_reg MUX register list + muxu_mod MUX modifiers list +*/ + +UNIT muxu_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), POLL_WAIT }; + +REG muxu_reg[] = { + { ORDATA (IBUF, muxu_ibuf, 16) }, + { ORDATA (OBUF, muxu_obuf, 16) }, + { FLDATA (CMD, muxu_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, muxu_dib.ctl, 0), REG_HRO }, + { FLDATA (FLG, muxu_dib.flg, 0), REG_HRO }, + { FLDATA (FBF, muxu_dib.fbf, 0), REG_HRO }, + { FLDATA (SRQ, muxu_dib.srq, 0), REG_HRO }, + { ORDATA (DEVNO, muxu_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB muxu_mod[] = { + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", &mux_setdiag }, + { UNIT_DIAG, 0, "terminal mode", "TERM", &mux_setdiag }, + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &mux_summ }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &mux_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &mux_show, NULL }, + { MTAB_XTD|MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &muxl_dev }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &mux_desc }, + { 0 } + }; + +DEVICE muxu_dev = { + "MUX", &muxu_unit, muxu_reg, muxu_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &muxc_reset, + NULL, &mux_attach, &mux_detach, + &muxu_dib, DEV_NET | DEV_DISABLE | DEV_DEBUG, + 0, mux_deb, NULL, NULL + }; + +/* MUXL data structures + + muxl_dev MUXL device descriptor + muxl_unit MUXL unit descriptor + muxl_reg MUXL register list + muxl_mod MUXL modifiers list +*/ + +UNIT muxl_unit[] = { + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT } + }; + +MTAB muxl_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { UNIT_MDM, 0, "no dataset", "NODATASET", NULL }, + { UNIT_MDM, UNIT_MDM, "dataset", "DATASET", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &mux_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &mux_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &mux_desc }, + { MTAB_XTD|MTAB_VDV, 1, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &muxl_dev }, + { 0 } + }; + +REG muxl_reg[] = { + { FLDATA (CMD, muxl_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, muxl_dib.ctl, 0) }, + { FLDATA (FLG, muxl_dib.flg, 0) }, + { FLDATA (FBF, muxl_dib.fbf, 0) }, + { FLDATA (SRQ, muxl_dib.srq, 0) }, + { BRDATA (STA, mux_sta, 8, 16, MUX_LINES + MUX_ILINES) }, + { BRDATA (RPAR, mux_rpar, 8, 16, MUX_LINES + MUX_ILINES) }, + { BRDATA (XPAR, mux_xpar, 8, 16, MUX_LINES) }, + { BRDATA (RBUF, mux_rbuf, 8, 16, MUX_LINES + MUX_ILINES) }, + { BRDATA (XBUF, mux_xbuf, 8, 16, MUX_LINES) }, + { BRDATA (RCHP, mux_rchp, 8, 1, MUX_LINES + MUX_ILINES) }, + { BRDATA (XDON, mux_xdon, 8, 1, MUX_LINES) }, + { URDATA (TIME, muxl_unit[0].wait, 10, 24, 0, + MUX_LINES, REG_NZ + PV_LEFT) }, + { ORDATA (DEVNO, muxl_dib.devno, 6), REG_HRO }, + { NULL } + }; + +DEVICE muxl_dev = { + "MUXL", muxl_unit, muxl_reg, muxl_mod, + MUX_LINES, 10, 31, 1, 8, 8, + NULL, NULL, &muxc_reset, + NULL, NULL, NULL, + &muxl_dib, DEV_DISABLE + }; + +/* MUXM data structures + + muxc_dev MUXM device descriptor + muxc_unit MUXM unit descriptor + muxc_reg MUXM register list + muxc_mod MUXM modifiers list +*/ + +DIB muxc_dib = { MUXC, 0, 0, 0, 0, 0, &muxcio }; + +UNIT muxc_unit = { UDATA (NULL, 0, 0) }; + +REG muxc_reg[] = { + { FLDATA (CMD, muxc_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, muxc_dib.ctl, 0) }, + { FLDATA (FLG, muxc_dib.flg, 0) }, + { FLDATA (FBF, muxc_dib.fbf, 0) }, + { FLDATA (SRQ, muxc_dib.srq, 0) }, + { FLDATA (SCAN, muxc_scan, 0) }, + { ORDATA (CHAN, muxc_chan, 4) }, + { BRDATA (DSO, muxc_ota, 8, 6, MUX_LINES) }, + { BRDATA (DSI, muxc_lia, 8, 2, MUX_LINES) }, + { ORDATA (DEVNO, muxc_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB muxc_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &muxc_dev }, + { 0 } + }; + +DEVICE muxc_dev = { + "MUXM", &muxc_unit, muxc_reg, muxc_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &muxc_reset, + NULL, NULL, NULL, + &muxc_dib, DEV_DISABLE + }; + +/* I/O instructions: data cards + + Implementation note: the operating manual says that "at least 100 + milliseconds of CLC 0s must be programmed" by systems employing the + multiplexer to ensure that the multiplexer resets. In practice, such systems + issue 128K CLC 0 instructions. As we provide debug logging of multiplexer + resets, a CRS counter is used to ensure that only one debug line is printed + in response to these 128K CRS invocations. +*/ + +int32 muxlio (int32 inst, int32 IR, int32 dat) +{ +int32 dev, ln; +t_bool is_crs; +static uint32 crs_count = 0; /* cntr for crs repeat */ + +dev = IR & I_DEVMASK; /* get device no */ +is_crs = FALSE; + +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + muxl_obuf = dat; /* store data */ + + if (DEBUG_PRI (muxu_dev, DEB_CPU)) + if (dat & OTL_P) + fprintf (sim_deb, ">>MUXl OTx: Parameter = %06o\n", dat); + else + fprintf (sim_deb, ">>MUXl OTx: Data = %06o\n", dat); + break; + + case ioLIX: /* load */ + dat = 0; + + case ioMIX: /* merge */ + dat = dat | muxl_ibuf; + + if (DEBUG_PRI (muxu_dev, DEB_CPU)) + fprintf (sim_deb, ">>MUXl LIx: Data = %06o\n", dat); + break; + + case ioCRS: /* control reset */ + is_crs = TRUE; + + if (crs_count) /* already reset? */ + break; /* skip redundant clear */ + + for (ln = 0; ln < MUX_LINES; ln++) { /* clear transmit info */ + mux_xbuf[ln] = mux_xpar[ln] = 0; + muxc_ota[ln] = muxc_lia[ln] = mux_xdon[ln] = 0; + } + + for (ln = 0; ln < (MUX_LINES + MUX_ILINES); ln++) { + mux_rbuf[ln] = mux_rpar[ln] = 0; /* clear receive info */ + mux_sta[ln] = mux_rchp[ln] = 0; + } /* fall into CLC SC */ + + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCTL (dev); + + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, ">>MUXl CLC: Data interrupt inhibited\n"); + } + else { /* STC */ + setCTL (dev); /* set ctl */ + ln = MUX_CHAN (muxu_obuf); /* get chan # */ + + if (muxl_obuf & OTL_TX) { /* transmit? */ + if (ln < MUX_LINES) { /* line valid? */ + if (muxl_obuf & OTL_P) { /* parameter? */ + mux_xpar[ln] = muxl_obuf; /* store param value */ + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MUXl STC: Transmit channel %d parameter %06o stored\n", + ln, muxl_obuf); + } + + else { /* data */ + if (mux_xpar[ln] & OTL_TPAR) /* parity requested? */ + muxl_obuf = /* add parity bit */ + muxl_obuf & ~OTL_PAR | + XMT_PAR(muxl_obuf); + mux_xbuf[ln] = muxl_obuf; /* load buffer */ + + if (sim_is_active (&muxl_unit[ln])) { /* still working? */ + mux_sta[ln] = mux_sta[ln] | LIU_LOST; /* char lost */ + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MUXl STC: Transmit channel %d data overrun\n", ln); + } + else { + if (muxu_unit.flags & UNIT_DIAG) /* loopback? */ + mux_ldsc[ln].conn = 1; /* connect this line */ + sim_activate (&muxl_unit[ln], muxl_unit[ln].wait); + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MUXl STC: Transmit channel %d data %06o scheduled\n", + ln, muxl_obuf); + } + } + } + else if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* line invalid */ + fprintf (sim_deb, ">>MUXl STC: Transmit channel %d invalid\n", ln); + } + + else /* receive */ + if (ln < (MUX_LINES + MUX_ILINES)) { /* line valid? */ + if (muxl_obuf & OTL_P) { /* parameter? */ + mux_rpar[ln] = muxl_obuf; /* store param value */ + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MUXl STC: Receive channel %d parameter %06o stored\n", + ln, muxl_obuf); + } + + else if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* data (invalid action) */ + fprintf (sim_deb, + ">>MUXl STC: Receive channel %d parameter %06o invalid action\n", + ln, muxl_obuf); + } + + else if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* line invalid */ + fprintf (sim_deb, ">>MUXl STC: Receive channel %d invalid\n", ln); + } /* end STC */ + break; + + default: + break; + } + +if (is_crs) /* control reset? */ + crs_count = crs_count + 1; /* increment count */ + +else if (crs_count) { /* something else */ + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) /* report reset count */ + fprintf (sim_deb, + ">>MUXl CRS: Multiplexer reset %d times\n", crs_count); + crs_count = 0; /* clear counter */ + } + +if (IR & I_HC) { /* H/C option */ + clrFSR (dev); /* clear flag */ + mux_data_int (); /* look for new int */ + } +return dat; +} + +int32 muxuio (int32 inst, int32 IR, int32 dat) +{ +switch (inst) { /* case on opcode */ + + case ioOTX: /* output */ + muxu_obuf = dat; /* store data */ + + if (DEBUG_PRI (muxu_dev, DEB_CPU)) + fprintf (sim_deb, ">>MUXu OTx: Data channel = %d\n", MUX_CHAN(dat)); + break; + + case ioLIX: /* load */ + dat = 0; + + case ioMIX: /* merge */ + dat = dat | muxu_ibuf; + + if (DEBUG_PRI (muxu_dev, DEB_CPU)) + fprintf (sim_deb, ">>MUXu LIx: Status = %06o, channel = %d\n", dat, MUX_CHAN(dat)); + break; + + default: + break; + } + +return dat; +} + +/* I/O instructions: control card + + In diagnostic mode, the control signals C1 and C2 are looped back to status + signals S1 and S2. Changing the control signals may cause an interrupt, so a + test is performed after OTx processing. +*/ + +int32 muxcio (int32 inst, int32 IR, int32 dat) +{ +int32 dev, ln, t, old; + +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioOTX: /* output */ + ln = muxc_chan = OTC_CHAN (dat); /* set channel */ + + if (dat & OTC_SCAN) muxc_scan = 1; /* set scan flag */ + else muxc_scan = 0; + + if (dat & OTC_UPD) { /* update? */ + old = muxc_ota[ln]; /* save prior val */ + muxc_ota[ln] = /* save ESn,SSn */ + (muxc_ota[ln] & ~OTC_RW) | (dat & OTC_RW); + + if (dat & OTC_EC2) /* if EC2, upd C2 */ + muxc_ota[ln] = + (muxc_ota[ln] & ~OTC_C2) | (dat & OTC_C2); + + if (dat & OTC_EC1) /* if EC1, upd C1 */ + muxc_ota[ln] = + (muxc_ota[ln] & ~OTC_C1) | (dat & OTC_C1); + + if (muxu_unit.flags & UNIT_DIAG) /* loopback? */ + muxc_lia[ln ^ 1] = /* set S1, S2 to C1, C2 */ + (muxc_lia[ln ^ 1] & ~(LIC_S2 | LIC_S1)) | + (muxc_ota[ln] & (OTC_C1 | OTC_C2)) >> OTC_V_C; + + else if ((muxl_unit[ln].flags & UNIT_MDM) && /* modem ctrl? */ + (old & DTR) && /* DTR drop? */ + !(muxc_ota[ln] & DTR)) { + tmxr_linemsg (&mux_ldsc[ln], "\r\nLine hangup\r\n"); + tmxr_reset_ln (&mux_ldsc[ln]); /* reset line */ + muxc_lia[ln] = 0; /* dataset off */ + } + } /* end update */ + + if (DEBUG_PRI (muxu_dev, DEB_CPU)) + fprintf (sim_deb, ">>MUXc OTx: Parameter = %06o, channel = %d\n", + dat, ln); + + if ((muxu_unit.flags & UNIT_DIAG) && /* loopback? */ + (!FLG(muxc_dib.devno))) /* flag clear? */ + mux_ctrl_int (); /* status chg may interrupt */ + break; + + case ioLIX: /* load */ + dat = 0; + + case ioMIX: /* merge */ + t = LIC_MBO | PUT_CCH (muxc_chan) | /* mbo, chan num */ + LIC_TSTI (muxc_chan) | /* I2, I1 */ + (muxc_ota[muxc_chan] & (OTC_ES2 | OTC_ES1)) | /* ES2, ES1 */ + (muxc_lia[muxc_chan] & (LIC_S2 | LIC_S1)); /* S2, S1 */ + dat = dat | t; /* return status */ + + if (DEBUG_PRI (muxu_dev, DEB_CPU)) + fprintf (sim_deb, ">>MUXc LIx: Status = %06o, channel = %d\n", + dat, muxc_chan); + + muxc_chan = (muxc_chan + 1) & LIC_M_CHAN; /* incr channel */ + break; + + case ioCRS: /* control reset */ + case ioCTL: /* ctrl clear/set */ + if (IR & I_CTL) { clrCTL (dev); } /* CLC */ + else { setCTL (dev); } /* STC */ + break; + + default: + break; + } + +if (IR & I_HC) { /* H/C option */ + clrFSR (dev); /* clear flag */ + mux_ctrl_int (); /* look for new int */ + } +return dat; +} + +/* Unit service - receive side + + Poll for new connections + Poll all active lines for input +*/ + +t_stat muxi_svc (UNIT *uptr) +{ +int32 ln, c; +t_bool loopback; + +loopback = ((muxu_unit.flags & UNIT_DIAG) != 0); /* diagnostic mode? */ + +if (!loopback) { /* terminal mode? */ + if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ + muxu_unit.wait = sync_poll (SERVICE); /* synchronize poll */ + sim_activate (uptr, muxu_unit.wait); /* continue poll */ + ln = tmxr_poll_conn (&mux_desc); /* look for connect */ + if (ln >= 0) { /* got one? */ + if ((muxl_unit[ln].flags & UNIT_MDM) && /* modem ctrl? */ + (muxc_ota[ln] & DTR)) /* DTR? */ + muxc_lia[ln] = muxc_lia[ln] | CDET; /* set cdet */ + muxc_lia[ln] = muxc_lia[ln] | DSR; /* set dsr */ + mux_ldsc[ln].rcve = 1; /* rcv enabled */ + } + tmxr_poll_rx (&mux_desc); /* poll for input */ +} + +for (ln = 0; ln < MUX_LINES; ln++) { /* loop thru lines */ + if (mux_ldsc[ln].conn) { /* connected? */ + if (loopback) { /* diagnostic mode? */ + c = mux_xbuf[ln ^ 1] & OTL_CHAR; /* get char from xmit line */ + if (c == 0) /* all char bits = 0? */ + c = c | SCPE_BREAK; /* set break flag */ + mux_ldsc[ln].conn = 0; /* clear connection */ + } + + else + c = tmxr_getc_ln (&mux_ldsc[ln]); /* get char from Telnet */ + + if (c) { /* valid char? */ + if (c & SCPE_BREAK) { /* break? */ + mux_sta[ln] = mux_sta[ln] | LIU_BRK; + mux_rbuf[ln] = 0; /* no char */ + } + else { /* normal */ + if (mux_rchp[ln]) /* char already pending? */ + mux_sta[ln] = mux_sta[ln] | LIU_LOST; + + if (!loopback) { /* terminal mode? */ + c = sim_tt_inpcvt (c, TT_GET_MODE (muxl_unit[ln].flags)); + if (mux_rpar[ln] & OTL_ECHO) { /* echo? */ + TMLN *lp = &mux_ldsc[ln]; /* get line */ + tmxr_putc_ln (lp, c); /* output char */ + tmxr_poll_tx (&mux_desc); /* poll xmt */ + } + } + mux_rbuf[ln] = c; /* save char */ + } + + mux_rchp[ln] = 1; /* char pending */ + + if (DEBUG_PRI (muxu_dev, DEB_XFER)) + fprintf (sim_deb, ">>MUXi svc: Line %d character %06o received\n", + ln, c); + + if (mux_rpar[ln] & OTL_DIAG) mux_diag (c); /* rcv diag? */ + } /* end if char */ + } /* end if connected */ + + else /* not connected */ + if (!loopback) /* terminal mode? */ + muxc_lia[ln] = 0; /* line disconnected */ + } /* end for */ +if (!FLG (muxl_dib.devno)) mux_data_int (); /* scan for data int */ +if (!FLG (muxc_dib.devno)) mux_ctrl_int (); /* scan modem */ +return SCPE_OK; +} + +/* Unit service - transmit side */ + +t_stat muxo_svc (UNIT *uptr) +{ +int32 c, fc, ln, altln; +t_bool loopback; + +ln = uptr - muxl_unit; /* line # */ +altln = ln ^ 1; /* alt. line for diag mode */ + +fc = mux_xbuf[ln] & OTL_CHAR; /* full character data */ +c = fc & 0377; /* Telnet character data */ + +loopback = ((muxu_unit.flags & UNIT_DIAG) != 0); /* diagnostic mode? */ + +if (mux_ldsc[ln].conn) { /* connected? */ + if (mux_ldsc[ln].xmte) { /* xmt enabled? */ + if (loopback) /* diagnostic mode? */ + mux_ldsc[ln].conn = 0; /* clear connection */ + + if ((mux_xbuf[ln] & OTL_SYNC) == 0) { /* start bit 0? */ + TMLN *lp = &mux_ldsc[ln]; /* get line */ + c = sim_tt_outcvt (c, TT_GET_MODE (muxl_unit[ln].flags)); + + if (mux_xpar[ln] & OTL_DIAG) /* xmt diagnose? */ + mux_diag (fc); /* before munge */ + + if (loopback) { /* diagnostic mode? */ + mux_ldsc[altln].conn = 1; /* set recv connection */ + sim_activate (&muxu_unit, 1); /* schedule receive */ + } + + else { /* no loopback */ + if (c >= 0) /* valid? */ + tmxr_putc_ln (lp, c); /* output char */ + tmxr_poll_tx (&mux_desc); /* poll xmt */ + } + } + + mux_xdon[ln] = 1; /* set for xmit irq */ + + if (DEBUG_PRI (muxu_dev, DEB_XFER) && (loopback | (c >= 0))) + fprintf (sim_deb, ">>MUXo svc: Line %d character %06o sent\n", + ln, (loopback ? fc : c)); + } + + else { /* buf full */ + tmxr_poll_tx (&mux_desc); /* poll xmt */ + sim_activate (uptr, muxl_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } + +if (!FLG (muxl_dib.devno)) mux_data_int (); /* scan for int */ +return SCPE_OK; +} + +/* Look for data interrupt */ + +void mux_data_int (void) +{ +int32 i; + +for (i = 0; i < MUX_LINES; i++) { /* rcv lines */ + if ((mux_rpar[i] & OTL_ENB) && mux_rchp[i]) { /* enabled, char? */ + muxl_ibuf = PUT_DCH (i) | /* lo buf = char */ + mux_rbuf[i] & LIL_CHAR | + RCV_PAR (mux_rbuf[i]); + muxu_ibuf = PUT_DCH (i) | mux_sta[i]; /* hi buf = stat */ + mux_rchp[i] = 0; /* clr char, stat */ + mux_sta[i] = 0; + + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, ">>MUXd irq: Receive channel %d interrupt requested\n", i); + + setFSR (muxl_dib.devno); /* interrupt */ + return; + } + } +for (i = 0; i < MUX_LINES; i++) { /* xmt lines */ + if ((mux_xpar[i] & OTL_ENB) && mux_xdon[i]) { /* enabled, done? */ + muxl_ibuf = PUT_DCH (i) | /* lo buf = last rcv char */ + mux_rbuf[i] & LIL_CHAR | + RCV_PAR (mux_rbuf[i]); + muxu_ibuf = PUT_DCH (i) | mux_sta[i] | LIU_TR; /* hi buf = stat */ + mux_xdon[i] = 0; /* clr done, stat */ + mux_sta[i] = 0; + + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, ">>MUXd irq: Transmit channel %d interrupt requested\n", i); + + setFSR (muxl_dib.devno); /* interrupt */ + return; + } + } +for (i = MUX_LINES; i < (MUX_LINES + MUX_ILINES); i++) { /* diag lines */ + if ((mux_rpar[i] & OTL_ENB) && mux_rchp[i]) { /* enabled, char? */ + muxl_ibuf = PUT_DCH (i) | /* lo buf = char */ + mux_rbuf[i] & LIL_CHAR | + RCV_PAR (mux_rbuf[i]); + muxu_ibuf = PUT_DCH (i) | mux_sta[i] | LIU_DG; /* hi buf = stat */ + mux_rchp[i] = 0; /* clr char, stat */ + mux_sta[i] = 0; + + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, ">>MUXd irq: Receive channel %d interrupt requested\n", i); + + setFSR (muxl_dib.devno); + return; + } + } +return; +} + +/* Look for control interrupt + + If either of the incoming status bits does not match the stored status, and + the corresponding mismatch is enabled, a control interrupt request is + generated. Depending on the scan flag, we check either all 16 lines or just + the current line. If an interrupt is requested, the channel counter + indicates the interrupting channel. +*/ + +void mux_ctrl_int (void) +{ +int32 i, line_count; + +line_count = (muxc_scan ? MUX_LINES : 1); /* check one or all lines */ + +for (i = 0; i < line_count; i++) { + if (muxc_scan) /* scanning? */ + muxc_chan = (muxc_chan + 1) & LIC_M_CHAN; /* step channel */ + if (LIC_TSTI (muxc_chan)) { /* status change? */ + + if (DEBUG_PRI (muxu_dev, DEB_CMDS)) + fprintf (sim_deb, + ">>MUXc irq: Control channel %d interrupt requested (poll = %d)\n", + muxc_chan, i + 1); + + setFSR (muxc_dib.devno); /* set flag */ + break; + } + } +return; +} + +/* Set diagnostic lines for given character */ + +void mux_diag (int32 c) +{ +int32 i; + +for (i = MUX_LINES; i < (MUX_LINES + MUX_ILINES); i++) { + if (c & SCPE_BREAK) { /* break? */ + mux_sta[i] = mux_sta[i] | LIU_BRK; + mux_rbuf[i] = 0; /* no char */ + } + else { + if (mux_rchp[i]) mux_sta[i] = mux_sta[i] | LIU_LOST; + mux_rchp[i] = 1; + mux_rbuf[i] = c; + } + } +return; +} + +/* Reset an individual line */ + +void mux_reset_ln (int32 i) +{ +mux_rbuf[i] = mux_xbuf[i] = 0; /* clear state */ +mux_rpar[i] = mux_xpar[i] = 0; +mux_rchp[i] = mux_xdon[i] = 0; +mux_sta[i] = 0; +muxc_ota[i] = muxc_lia[i] = 0; /* clear modem */ +if (mux_ldsc[i].conn && /* connected? */ + ((muxu_unit.flags & UNIT_DIAG) == 0)) /* term mode? */ + muxc_lia[i] = muxc_lia[i] | DSR | /* cdet, dsr */ + (muxl_unit[i].flags & UNIT_MDM? CDET: 0); +sim_cancel (&muxl_unit[i]); +return; +} + +/* Reset routine */ + +t_stat muxc_reset (DEVICE *dptr) +{ +int32 i; + +if (dptr == &muxc_dev) { /* make all consistent */ + hp_enbdis_pair (dptr, &muxl_dev); + hp_enbdis_pair (dptr, &muxu_dev); + } +else if (dptr == &muxl_dev) { + hp_enbdis_pair (dptr, &muxc_dev); + hp_enbdis_pair (dptr, &muxu_dev); + } +else { + hp_enbdis_pair (dptr, &muxc_dev); + hp_enbdis_pair (dptr, &muxl_dev); + } +muxl_dib.cmd = muxl_dib.ctl = 0; /* init lower */ +muxl_dib.flg = muxl_dib.fbf = muxl_dib.srq = 1; +muxu_dib.cmd = muxu_dib.ctl = 0; /* upper not */ +muxu_dib.flg = muxu_dib.fbf = muxu_dib.srq = 0; /* implemented */ +muxc_dib.cmd = muxc_dib.ctl = 0; /* init ctrl */ +muxc_dib.flg = muxc_dib.fbf = muxc_dib.srq = 1; +muxc_chan = muxc_scan = 0; /* init modem scan */ +if (muxu_unit.flags & UNIT_ATT) { /* master att? */ + if (!sim_is_active (&muxu_unit)) { + muxu_unit.wait = sync_poll (INITIAL); /* synchronize poll */ + sim_activate (&muxu_unit, muxu_unit.wait); /* activate */ + } + } +else sim_cancel (&muxu_unit); /* else stop */ +for (i = 0; i < MUX_LINES; i++) mux_reset_ln (i); /* reset lines 0-15 */ +for (i = MUX_LINES; i < (MUX_LINES + MUX_ILINES); i++) /* reset lines 16-20 */ + mux_rbuf[i] = mux_rpar[i] = mux_sta[i] = mux_rchp[i] = 0; +return SCPE_OK; +} + +/* Attach master unit */ + +t_stat mux_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */ + return SCPE_NOFNC; /* command not allowed */ + +r = tmxr_attach (&mux_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +muxu_unit.wait = sync_poll (INITIAL); /* synchronize poll */ +sim_activate (uptr, muxu_unit.wait); /* start poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat mux_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&mux_desc, uptr); /* detach */ +for (i = 0; i < MUX_LINES; i++) mux_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Diagnostic/normal mode routine + + Diagnostic testing wants to exercise as much of the regular simulation code + as possible to ensure good test coverage. Normally, input polling and output + transmission only occurs on connected lines. In diagnostic mode, line + connection flags are set selectively to enable processing on the lines under + test. The alternative to this would require duplicating the send/receive + code; the diagnostic would then test the copy but not the actual code used + for normal character transfers, which is undesirable. + + Therefore, to enable diagnostic mode, we must force a disconnect of the + master socket and any connected Telnet lines, which clears the connection + flags on all lines. Then we set the "transmission enabled" flags on all + lines to enable output character processing for the diagnostic. (Normally, + all of the flags are set when the multiplexer is first attached. Until then, + the enable flags default to "not enabled," so we enable them explicitly + here.) +*/ + +t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 ln; + +if (val) { /* set diag? */ + mux_detach (uptr); /* detach lines */ + for (ln = 0; ln < MUX_LINES; ln++) /* enable transmission */ + mux_ldsc[ln].xmte = 1; /* on all lines */ + } +else { /* set term */ + for (ln = 0; ln < MUX_LINES; ln++) /* clear connections */ + mux_ldsc[ln].conn = 0; /* on all lines */ + } +return SCPE_OK; +} + +/* Show summary processor */ + +t_stat mux_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */ + return SCPE_NOFNC; /* command not allowed */ + +for (i = t = 0; i < MUX_LINES; i++) t = t + (mux_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat mux_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */ + return SCPE_NOFNC; /* command not allowed */ + +for (i = t = 0; i < MUX_LINES; i++) t = t + (mux_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < MUX_LINES; i++) { + if (mux_ldsc[i].conn) { + if (val) tmxr_fconns (st, &mux_ldsc[i], i); + else tmxr_fstats (st, &mux_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} diff --git a/HP2100/hp2100_stddev.c b/HP2100/hp2100_stddev.c new file mode 100644 index 0000000..8923943 --- /dev/null +++ b/HP2100/hp2100_stddev.c @@ -0,0 +1,1015 @@ +/* hp2100_stddev.c: HP2100 standard devices simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr 12597A-002 paper tape reader interface + ptp 12597A-005 paper tape punch interface + tty 12531C buffered teleprinter interface + clk 12539C time base generator + + 25-Apr-08 JDB Changed TTY output wait from 100 to 200 for MSU BASIC + 18-Apr-08 JDB Removed redundant control char handling definitions + 14-Apr-08 JDB Changed TTY console poll to 10 msec. real time + Synchronized CLK with TTY if set for 10 msec. + Added UNIT_IDLE to TTY and CLK + 09-Jan-08 JDB Fixed PTR trailing null counter for tape re-read + 31-Dec-07 JDB Added IPTICK register to CLK to display CPU instr/tick + Corrected and verified ioCRS actions + 28-Dec-06 JDB Added ioCRS state to I/O decoders + 22-Nov-05 RMS Revised for new terminal processing routines + 13-Sep-04 JDB Added paper tape loop mode, DIAG/READER modifiers to PTR + Added PV_LEFT to PTR TRLLIM register + Modified CLK to permit disable + 15-Aug-04 RMS Added tab to control char set (from Dave Bryan) + 14-Jul-04 RMS Generalized handling of control char echoing + (from Dave Bryan) + 26-Apr-04 RMS Fixed SFS x,C and SFC x,C + Fixed SR setting in IBL + Fixed input behavior during typeout for RTE-IV + Suppressed nulls on TTY output for RTE-IV + Implemented DMA SRQ (follows FLG) + 29-Mar-03 RMS Added support for console backpressure + 25-Apr-03 RMS Added extended file support + 22-Dec-02 RMS Added break support + 01-Nov-02 RMS Revised BOOT command for IBL ROMs + Fixed bug in TTY reset, TTY starts in input mode + Fixed bug in TTY mode OTA, stores data as well + Fixed clock to add calibration, proper start/stop + Added UC option to TTY output + 30-May-02 RMS Widened POS to 32b + 22-Mar-02 RMS Revised for dynamically allocated memory + 03-Nov-01 RMS Changed DEVNO to use extended SET/SHOW + 29-Nov-01 RMS Added read only unit support + 24-Nov-01 RMS Changed TIME to an array + 07-Sep-01 RMS Moved function prototypes + 21-Nov-00 RMS Fixed flag, buffer power up state + Added status input for ptp, tty + 15-Oct-00 RMS Added dynamic device number support + + The reader and punch, like most HP devices, have a command flop. The + teleprinter and clock do not. + + Reader diagnostic mode simulates a tape loop by rewinding the tape image file + upon EOF. Normal mode EOF action is to supply TRLLIM nulls and then either + return SCPE_IOERR or SCPE_OK without setting the device flag. + + To support CPU idling, the teleprinter interface (which doubles as the + simulator console) polls for input using a calibrated timer with a ten + millisecond period. Other polled-keyboard input devices (multiplexers and + the BACI card) synchronize with the console poll to ensure maximum available + idle time. The console poll is guaranteed to run, as the TTY device cannot + be disabled. + + The clock (time base generator) autocalibrates. If the CLK is set to a ten + millisecond period (e.g., as under RTE), it is synchronized to the console + poll. Otherwise (e.g., as under DOS or TSB, which use 100 millisecond + periods), it runs asynchronously. If the specified clock frequency is below + 10Hz, the clock service routine runs at 10Hz and counts down a repeat counter + before generating an interrupt. Autocalibration will not work if the clock + is running at 1Hz or less. + + Clock diagnostic mode corresponds to inserting jumper W2 on the 12539C. + This turns off autocalibration and divides the longest time intervals down + by 10**3. The clk_time values were chosen to allow the diagnostic to + pass its clock calibration test. + + References: + - 2748B Tape Reader Operating and Service Manual (02748-90041, Oct-1977) + - 12597A 8-Bit Duplex Register Interface Kit Operating and Service Manual + (12597-9002, Sep-1974) + - 12531C Buffered Teleprinter Interface Kit Operating and Service Manual + (12531-90033, Nov-1972) + - 12539C Time Base Generator Interface Kit Operating and Service Manual + (12539-90008, Jan-1975) +*/ + +#include "hp2100_defs.h" + +#define TTY_OUT_WAIT 200 /* TTY output wait */ + +#define UNIT_V_DIAG (TTUF_V_UF + 0) /* diag mode */ +#define UNIT_V_AUTOLF (TTUF_V_UF + 1) /* auto linefeed */ +#define UNIT_DIAG (1 << UNIT_V_DIAG) +#define UNIT_AUTOLF (1 << UNIT_V_AUTOLF) + +#define PTP_LOW 0000040 /* low tape */ +#define TM_MODE 0100000 /* mode change */ +#define TM_KBD 0040000 /* enable keyboard */ +#define TM_PRI 0020000 /* enable printer */ +#define TM_PUN 0010000 /* enable punch */ +#define TP_BUSY 0100000 /* busy */ + +#define CLK_V_ERROR 4 /* clock overrun */ +#define CLK_ERROR (1 << CLK_V_ERROR) + +extern uint32 PC, SR; +extern uint32 dev_cmd[2], dev_ctl[2], dev_flg[2], dev_fbf[2], dev_srq[2]; + +int32 ptr_stopioe = 0; /* stop on error */ +int32 ptr_trlcnt = 0; /* trailer counter */ +int32 ptr_trllim = 40; /* trailer to add */ +int32 ptp_stopioe = 0; +int32 ttp_stopioe = 0; +int32 tty_buf = 0; /* tty buffer */ +int32 tty_mode = 0; /* tty mode */ +int32 tty_shin = 0377; /* tty shift in */ +int32 tty_lf = 0; /* lf flag */ +int32 clk_select = 0; /* clock time select */ +int32 clk_error = 0; /* clock error */ +int32 clk_ctr = 0; /* clock counter */ +int32 clk_time[8] = { /* clock intervals */ + 155, 1550, 15500, 155000, 155000, 155000, 155000, 155000 + }; +int32 clk_tps[8] = { /* clock tps */ + 10000, 1000, 100, 10, 10, 10, 10, 10 + }; +int32 clk_rpt[8] = { /* number of repeats */ + 1, 1, 1, 1, 10, 100, 1000, 10000 + }; +uint32 clk_tick = 0; /* instructions per tick */ + +DEVICE ptr_dev, ptp_dev, tty_dev, clk_dev; +int32 ptrio (int32 inst, int32 IR, int32 dat); +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_attach (UNIT *uptr, char *cptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); +int32 ptpio (int32 inst, int32 IR, int32 dat); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); +int32 ttyio (int32 inst, int32 IR, int32 dat); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tty_reset (DEVICE *dptr); +t_stat tty_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tty_set_alf (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 clkio (int32 inst, int32 IR, int32 dat); +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +int32 clk_delay (int32 flg); +t_stat tto_out (int32 c); +t_stat ttp_out (int32 c); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_mod PTR modifiers + ptr_reg PTR register list +*/ + +DIB ptr_dib = { PTR, 0, 0, 0, 0, 0, &ptrio }; + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { FLDATA (CMD, ptr_dib.cmd, 0) }, + { FLDATA (CTL, ptr_dib.ctl, 0) }, + { FLDATA (FLG, ptr_dib.flg, 0) }, + { FLDATA (FBF, ptr_dib.fbf, 0) }, + { FLDATA (SRQ, ptr_dib.srq, 0) }, + { DRDATA (TRLCTR, ptr_trlcnt, 8), REG_HRO }, + { DRDATA (TRLLIM, ptr_trllim, 8), PV_LEFT }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { ORDATA (DEVNO, ptr_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB ptr_mod[] = { + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, + { UNIT_DIAG, 0, "reader mode", "READER", NULL }, + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &ptr_dev }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, &ptr_attach, NULL, + &ptr_dib, DEV_DISABLE + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_mod PTP modifiers + ptp_reg PTP register list +*/ + +DIB ptp_dib = { PTP, 0, 0, 0, 0, 0, &ptpio }; + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (CMD, ptp_dib.cmd, 0) }, + { FLDATA (CTL, ptp_dib.ctl, 0) }, + { FLDATA (FLG, ptp_dib.flg, 0) }, + { FLDATA (FBF, ptp_dib.fbf, 0) }, + { FLDATA (SRQ, ptp_dib.srq, 0) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { ORDATA (DEVNO, ptp_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB ptp_mod[] = { + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &ptp_dev }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + &ptp_dib, DEV_DISABLE + }; + +/* TTY data structures + + tty_dev TTY device descriptor + tty_unit TTY unit descriptor + tty_reg TTY register list + tty_mod TTy modifiers list +*/ + +#define TTI 0 +#define TTO 1 +#define TTP 2 + +DIB tty_dib = { TTY, 0, 0, 0, 0, 0, &ttyio }; + +UNIT tty_unit[] = { + { UDATA (&tti_svc, UNIT_IDLE | TT_MODE_UC, 0), POLL_WAIT }, + { UDATA (&tto_svc, TT_MODE_UC, 0), TTY_OUT_WAIT }, + { UDATA (&tto_svc, UNIT_SEQ | UNIT_ATTABLE | TT_MODE_8B, 0), SERIAL_OUT_WAIT } + }; + +REG tty_reg[] = { + { ORDATA (BUF, tty_buf, 8) }, + { ORDATA (MODE, tty_mode, 16) }, + { ORDATA (SHIN, tty_shin, 8), REG_HRO }, + { FLDATA (CMD, tty_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, tty_dib.ctl, 0) }, + { FLDATA (FLG, tty_dib.flg, 0) }, + { FLDATA (FBF, tty_dib.fbf, 0) }, + { FLDATA (SRQ, tty_dib.srq, 0) }, + { FLDATA (KLFP, tty_lf, 0), REG_HRO }, + { DRDATA (KPOS, tty_unit[TTI].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (KTIME, tty_unit[TTI].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPOS, tty_unit[TTO].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TTIME, tty_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (PPOS, tty_unit[TTP].pos, T_ADDR_W), PV_LEFT }, + { FLDATA (STOP_IOE, ttp_stopioe, 0) }, + { ORDATA (DEVNO, tty_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB tty_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_opt }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_opt }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_opt }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_opt }, + { UNIT_AUTOLF, UNIT_AUTOLF, "autolf", "AUTOLF", &tty_set_alf }, + { UNIT_AUTOLF, 0 , NULL, "NOAUTOLF", &tty_set_alf }, + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &tty_dev }, + { 0 } + }; + +DEVICE tty_dev = { + "TTY", tty_unit, tty_reg, tty_mod, + 3, 10, 31, 1, 8, 8, + NULL, NULL, &tty_reset, + NULL, NULL, NULL, + &tty_dib, 0 + }; + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_mod CLK modifiers + clk_reg CLK register list +*/ + +DIB clk_dib = { CLK, 0, 0, 0, 0, 0, &clkio }; + +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0) }; + +REG clk_reg[] = { + { ORDATA (SEL, clk_select, 3) }, + { DRDATA (CTR, clk_ctr, 14) }, + { FLDATA (CMD, clk_dib.cmd, 0), REG_HRO }, + { FLDATA (CTL, clk_dib.ctl, 0) }, + { FLDATA (FLG, clk_dib.flg, 0) }, + { FLDATA (FBF, clk_dib.fbf, 0) }, + { FLDATA (SRQ, clk_dib.srq, 0) }, + { FLDATA (ERR, clk_error, CLK_V_ERROR) }, + { BRDATA (TIME, clk_time, 10, 24, 8) }, + { DRDATA (IPTICK, clk_tick, 24), PV_RSPC | REG_RO }, + { ORDATA (DEVNO, clk_dib.devno, 6), REG_HRO }, + { NULL } + }; + +MTAB clk_mod[] = { + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, + { UNIT_DIAG, 0, "calibrated", "CALIBRATED", NULL }, + { MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", + &hp_setdev, &hp_showdev, &clk_dev }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, DEV_DISABLE + }; + +/* Paper tape reader IO instructions */ + +int32 ptrio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioMIX: /* merge */ + dat = dat | ptr_unit.buf; + break; + + case ioLIX: /* load */ + dat = ptr_unit.buf; + break; + + case ioCRS: /* control reset */ + /* action same as CLC */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCMD (dev); /* clear cmd, ctl */ + clrCTL (dev); + } + else { /* STC */ + setCMD (dev); /* set cmd, ctl */ + setCTL (dev); + sim_activate (&ptr_unit, ptr_unit.wait); + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +return dat; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 dev, temp; + +dev = ptr_dib.devno; /* get device no */ +clrCMD (dev); /* clear cmd */ +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +while ((temp = getc (ptr_unit.fileref)) == EOF) { /* read byte, error? */ + if (feof (ptr_unit.fileref)) { /* end of file? */ + if ((ptr_unit.flags & UNIT_DIAG) && (ptr_unit.pos > 0)) { + rewind (ptr_unit.fileref); /* rewind if loop mode */ + ptr_unit.pos = 0; + } + else { + if (ptr_trlcnt >= ptr_trllim) { /* added all trailer? */ + if (ptr_stopioe) { /* stop on error? */ + printf ("PTR end of file\n"); + return SCPE_IOERR; + } + else return SCPE_OK; /* no, just hang */ + } + ptr_trlcnt++; /* count trailer */ + temp = 0; /* read a zero */ + break; + } + } + else { /* no, real error */ + perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } + } +setFSR (dev); /* set flag */ +ptr_unit.buf = temp & 0377; /* put byte in buf */ +ptr_unit.pos = ftell (ptr_unit.fileref); + +if (temp) /* character non-null? */ + ptr_trlcnt = 0; /* clear trailing null counter */ + +return SCPE_OK; +} + +/* Attach routine - clear the trailer counter */ + +t_stat ptr_attach (UNIT *uptr, char *cptr) +{ +ptr_trlcnt = 0; +return attach_unit (uptr, cptr); +} + +/* Reset routine - called from SCP, flags in DIB's */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_dib.cmd = ptr_dib.ctl = 0; /* clear cmd, ctl */ +ptr_dib.flg = ptr_dib.fbf = ptr_dib.srq = 1; /* set flg, fbf, srq */ +ptr_unit.buf = 0; +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Paper tape reader bootstrap routine (HP 12992K ROM) */ + +const uint16 ptr_rom[IBL_LNT] = { + 0107700, /*ST CLC 0,C ; intr off */ + 0002401, /* CLA,RSS ; skip in */ + 0063756, /*CN LDA M11 ; feed frame */ + 0006700, /* CLB,CCE ; set E to rd byte */ + 0017742, /* JSB READ ; get #char */ + 0007306, /* CMB,CCE,INB,SZB ; 2's comp */ + 0027713, /* JMP *+5 ; non-zero byte */ + 0002006, /* INA,SZA ; feed frame ctr */ + 0027703, /* JMP *-3 */ + 0102077, /* HLT 77B ; stop */ + 0027700, /* JMP ST ; next */ + 0077754, /* STA WC ; word in rec */ + 0017742, /* JSB READ ; get feed frame */ + 0017742, /* JSB READ ; get address */ + 0074000, /* STB 0 ; init csum */ + 0077755, /* STB AD ; save addr */ + 0067755, /*CK LDB AD ; check addr */ + 0047777, /* ADB MAXAD ; below loader */ + 0002040, /* SEZ ; E =0 => OK */ + 0027740, /* JMP H55 */ + 0017742, /* JSB READ ; get word */ + 0040001, /* ADA 1 ; cont checksum */ + 0177755, /* STA AD,I ; store word */ + 0037755, /* ISZ AD */ + 0000040, /* CLE ; force wd read */ + 0037754, /* ISZ WC ; block done? */ + 0027720, /* JMP CK ; no */ + 0017742, /* JSB READ ; get checksum */ + 0054000, /* CPB 0 ; ok? */ + 0027702, /* JMP CN ; next block */ + 0102011, /* HLT 11 ; bad csum */ + 0027700, /* JMP ST ; next */ + 0102055, /*H55 HALT 55 ; bad address */ + 0027700, /* JMP ST ; next */ + 0000000, /*RD 0 */ + 0006600, /* CLB,CME ; E reg byte ptr */ + 0103710, /* STC RDR,C ; start reader */ + 0102310, /* SFS RDR ; wait */ + 0027745, /* JMP *-1 */ + 0106410, /* MIB RDR ; get byte */ + 0002041, /* SEZ,RSS ; E set? */ + 0127742, /* JMP RD,I ; no, done */ + 0005767, /* BLF,CLE,BLF ; shift byte */ + 0027744, /* JMP RD+2 ; again */ + 0000000, /*WC 000000 ; word count */ + 0000000, /*AD 000000 ; address */ + 0177765, /*M11 -11 ; feed count */ + 0, 0, 0, 0, 0, 0, 0, 0, /* unused */ + 0, 0, 0, 0, 0, 0, 0, /* unused */ + 0000000 /*MAXAD -ST ; max addr */ + }; + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +int32 dev; + +dev = ptr_dib.devno; /* get device no */ +if (ibl_copy (ptr_rom, dev)) return SCPE_IERR; /* copy boot to memory */ +SR = (SR & IBL_OPT) | IBL_PTR | (dev << IBL_V_DEV); /* set SR */ +return SCPE_OK; +} + +/* Paper tape punch IO instructions */ + +int32 ptpio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioLIX: /* load */ + dat = 0; + case ioMIX: /* merge */ + if ((ptp_unit.flags & UNIT_ATT) == 0) + dat = dat | PTP_LOW; /* out of tape? */ + break; + + case ioOTX: /* output */ + ptp_unit.buf = dat; + break; + + case ioCRS: /* control reset */ + /* action same as CLC */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCMD (dev); /* clear cmd, ctl */ + clrCTL (dev); + } + else { /* STC */ + setCMD (dev); /* set cmd, ctl */ + setCTL (dev); + sim_activate (&ptp_unit, ptp_unit.wait); + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +return dat; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +int32 dev; + +dev = ptp_dib.devno; /* get device no */ +clrCMD (dev); /* clear cmd */ +setFSR (dev); /* set flag */ +if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { /* output byte */ + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +ptp_unit.pos = ftell (ptp_unit.fileref); /* update position */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_dib.cmd = ptp_dib.ctl = 0; /* clear cmd, ctl */ +ptp_dib.flg = ptp_dib.fbf = ptp_dib.srq = 1; /* set flg, fbf, srq */ +ptp_unit.buf = 0; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Terminal IO instructions */ + +int32 ttyio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioLIX: /* load */ + dat = 0; + case ioMIX: /* merge */ + dat = dat | tty_buf; + if (!(tty_mode & TM_KBD) && sim_is_active (&tty_unit[TTO])) + dat = dat | TP_BUSY; + break; + + case ioOTX: /* output */ + if (dat & TM_MODE) tty_mode = dat & (TM_KBD|TM_PRI|TM_PUN); + tty_buf = dat & 0377; + break; + + case ioCRS: /* control reset */ + clrCTL (dev); /* clear control */ + setFSR (dev); /* set flag */ + tty_mode = TM_KBD; /* set tty, clear print/punch */ + tty_buf = 0; /* clear buffer */ + tty_shin = 0377; /* input inactive */ + tty_lf = 0; /* no lf pending */ + break; + + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { clrCTL (dev); } /* CLC */ + else { /* STC */ + setCTL (dev); + if (!(tty_mode & TM_KBD)) /* output? */ + sim_activate (&tty_unit[TTO], tty_unit[TTO].wait); + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +return dat; +} + +/* TTY input service routine. + + The console input poll routine is scheduled with a ten millisecond period + using a calibrated timer, which is the source of event timing for all of the + keyboard polling routines. Synchronizing other keyboard polls with the + console poll ensures maximum idle time. + + Several HP operating systems require a CR and LF sequence for line + termination. This is awkward on a PC, as there is no LF key (CTRL+J is + needed instead). We provide an AUTOLF mode to add a LF automatically to each + CR input. When this mode is set, entering CR will set a flag, which will + cause a LF to be supplied automatically at the next input poll. + + The 12531C teleprinter interface and the later 12880A CRT interface provide a + clever mechanism to detect a keypress during output. This is used by DOS and + RTE to allow the user to interrupt lengthy output operations to enter system + commands. + + Referring to the 12531C schematic, the terminal input enters on pin X + ("DATA FROM EIA COMPATIBLE DEVICE"). The signal passes through four + transistor inversions (Q8, Q1, Q2, and Q3) to appear on pin 12 of NAND gate + U104C. If the flag flip-flop is not set, the terminal input passes to the + (inverted) output of U104C and thence to the D input of the first of the + flip-flops forming the data register. + + In the idle condition (no key pressed), the terminal input line is marking + (voltage negative), so in passing through a total of five inversions, a + logic one is presented at the serial input of the data register. During an + output operation, the register is parallel loaded and serially shifted, + sending the output data through the register to the device and -- this is + the crux -- filling the register with logic ones from U104C. + + At the end of the output operation, the card flag is set, an interrupt + occurs, and the RTE driver is entered. The driver then does an LIA SC to + read the contents of the data register. If no key has been pressed during + the output operation, the register will read as all ones (octal 377). If, + however, any key was struck, at least one zero bit will be present. If the + register value doesn't equal 377, the driver sets the system "operator + attention" flag, which will cause DOS or RTE to output an asterisk prompt and + initiate a terminal read when the current output line is completed. +*/ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c, dev; + +uptr->wait = sim_rtcn_calb (POLL_RATE, TMR_POLL); /* calibrate poll timer */ +sim_activate (uptr, uptr->wait); /* continue poll */ + +dev = tty_dib.devno; /* get device no */ +tty_shin = 0377; /* assume inactive */ +if (tty_lf) { /* auto lf pending? */ + c = 012; /* force lf */ + tty_lf = 0; + } +else { + if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ + if (c & SCPE_BREAK) c = 0; /* break? */ + else c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); + tty_lf = ((c & 0177) == 015) && (uptr->flags & UNIT_AUTOLF); + } +if (tty_mode & TM_KBD) { /* keyboard enabled? */ + tty_buf = c; /* put char in buf */ + uptr->pos = uptr->pos + 1; + setFSR (dev); /* set flag */ + if (c) { + tto_out (c); /* echo? */ + return ttp_out (c); /* punch? */ + } + } +else tty_shin = c; /* no, char shifts in */ +return SCPE_OK; +} + +/* TTY output service routine */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c, dev; +t_stat r; + +c = tty_buf; /* get char */ +tty_buf = tty_shin; /* shift in */ +tty_shin = 0377; /* line inactive */ +if ((r = tto_out (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* retry */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } +dev = tty_dib.devno; /* get device no */ +setFSR (dev); /* set done flag */ +return ttp_out (c); /* punch if enabled */ +} + +t_stat tto_out (int32 c) +{ +t_stat r; + +if (tty_mode & TM_PRI) { /* printing? */ + c = sim_tt_outcvt (c, TT_GET_MODE (tty_unit[TTO].flags)); + if (c >= 0) { /* valid? */ + if (r = sim_putchar_s (c)) return r; /* output char */ + tty_unit[TTO].pos = tty_unit[TTO].pos + 1; + } + } +return SCPE_OK; +} + +t_stat ttp_out (int32 c) +{ +if (tty_mode & TM_PUN) { /* punching? */ + if ((tty_unit[TTP].flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ttp_stopioe, SCPE_UNATT); + if (putc (c, tty_unit[TTP].fileref) == EOF) { /* output char */ + perror ("TTP I/O error"); + clearerr (tty_unit[TTP].fileref); + return SCPE_IOERR; + } + tty_unit[TTP].pos = ftell (tty_unit[TTP].fileref); + } +return SCPE_OK; +} + +/* TTY reset routine */ + +t_stat tty_reset (DEVICE *dptr) +{ +tty_dib.cmd = tty_dib.ctl = 0; /* clear cmd, ctl */ +tty_dib.flg = tty_dib.fbf = tty_dib.srq = 1; /* set flg, fbf, srq */ +tty_mode = TM_KBD; /* enable input */ +tty_buf = 0; +tty_shin = 0377; /* input inactive */ +tty_lf = 0; /* no lf pending */ +tty_unit[TTI].wait = POLL_WAIT; /* reset initial poll */ +sim_rtcn_init (tty_unit[TTI].wait, TMR_POLL); /* init poll timer */ +sim_activate (&tty_unit[TTI], tty_unit[TTI].wait); /* activate poll */ +sim_cancel (&tty_unit[TTO]); /* cancel output */ +return SCPE_OK; +} + +t_stat tty_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 u = uptr - tty_dev.units; + +if (u > TTO) return SCPE_NOFNC; +if ((u == TTI) && (val == TT_MODE_7P)) + val = TT_MODE_7B; +tty_unit[u].flags = (tty_unit[u].flags & ~TT_MODE) | val; +return SCPE_OK; +} + +t_stat tty_set_alf (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 u = uptr - tty_dev.units; + +if (u != TTI) return SCPE_NOFNC; +return SCPE_OK; +} + +/* Synchronize polling. + + Return an event time corresponding either with the amount of time remaining + in the current poll (mode = INITIAL) or the amount of time in a full poll + period (mode = SERVICE). If the former call is made when the device service + routine is started, then making the latter call during unit service will + ensure that the polls remain synchronized. + */ + +int32 sync_poll (POLLMODE poll_mode) +{ +int32 poll_time; + + if (poll_mode == INITIAL) { + poll_time = sim_is_active (&tty_unit[TTI]); + + if (poll_time) + return poll_time; + else + return POLL_WAIT; + } + else + return tty_unit[TTI].wait; +} + + +/* Clock I/O instructions. + + The time base generator (CLK) provides periodic interrupts from 100 + microseconds to 1000 seconds. The CLK uses a calibrated timer to provide the + time base. For periods ranging from 1 to 1000 seconds, a 100 millisecond + timer is used, and 10 to 10000 ticks are counted before setting the device + flag to indicate that the period has expired. + + If the period is set to ten milliseconds, the console poll timer is used + instead of an independent timer. This is to maximize the idle period. + + In diagnostic mode, the clock period is set to the expected number of CPU + instructions, rather than wall-clock time, so that the diagnostic executes as + expected. +*/ + +int32 clkio (int32 inst, int32 IR, int32 dat) +{ +int32 dev; + +dev = IR & I_DEVMASK; /* get device no */ +switch (inst) { /* case on opcode */ + + case ioFLG: /* flag clear/set */ + if ((IR & I_HC) == 0) { setFSR (dev); } /* STF */ + break; + + case ioSFC: /* skip flag clear */ + if (FLG (dev) == 0) PC = (PC + 1) & VAMASK; + break; + + case ioSFS: /* skip flag set */ + if (FLG (dev) != 0) PC = (PC + 1) & VAMASK; + break; + + case ioMIX: /* merge */ + dat = dat | clk_error; + break; + + case ioLIX: /* load */ + dat = clk_error; + break; + + case ioOTX: /* output */ + clk_select = dat & 07; /* save select */ + sim_cancel (&clk_unit); /* stop the clock */ + clrCTL (dev); /* clear control */ + break; + + case ioCRS: /* control reset */ + /* action same as CLC */ + case ioCTL: /* control clear/set */ + if (IR & I_CTL) { /* CLC */ + clrCTL (dev); /* turn off clock */ + sim_cancel (&clk_unit); /* deactivate unit */ + } + else { /* STC */ + setCTL (dev); /* set CTL */ + + if (clk_unit.flags & UNIT_DIAG) /* diag mode? */ + clk_unit.flags = clk_unit.flags & ~UNIT_IDLE; /* not calibrated */ + else + clk_unit.flags = clk_unit.flags | UNIT_IDLE; /* is calibrated */ + + if (!sim_is_active (&clk_unit)) { /* clock running? */ + clk_tick = clk_delay (0); /* get tick count */ + + if ((clk_unit.flags & UNIT_DIAG) == 0) /* calibrated? */ + if (clk_select == 2) /* 10 msec. interval? */ + clk_tick = sync_poll (INITIAL); /* sync poll */ + else + sim_rtcn_init (clk_tick, TMR_CLK); /* initialize timer */ + + sim_activate (&clk_unit, clk_tick); /* start clock */ + clk_ctr = clk_delay (1); /* set repeat ctr */ + } + clk_error = 0; /* clear error */ + } + break; + + default: + break; + } + +if (IR & I_HC) { clrFSR (dev); } /* H/C option */ +return dat; +} + +/* CLK unit service. + + As with the I/O handler, if the time base period is set to ten milliseconds, + the console poll timer is used instead of an independent timer. +*/ + +t_stat clk_svc (UNIT *uptr) +{ +int32 dev; + +dev = clk_dib.devno; /* get device no */ +if (!CTL (dev)) return SCPE_OK; /* CTL off? done */ + +if (clk_unit.flags & UNIT_DIAG) /* diag mode? */ + clk_tick = clk_delay (0); /* get fixed delay */ +else if (clk_select == 2) /* 10 msec period? */ + clk_tick = sync_poll (SERVICE); /* sync poll */ +else + clk_tick = sim_rtcn_calb (clk_tps[clk_select], TMR_CLK); /* calibrate delay */ + +sim_activate (uptr, clk_tick); /* reactivate */ +clk_ctr = clk_ctr - 1; /* decrement counter */ +if (clk_ctr <= 0) { /* end of interval? */ + if (FLG (dev)) clk_error = CLK_ERROR; /* overrun? error */ + else { setFSR (dev); } /* else set flag */ + clk_ctr = clk_delay (1); /* reset counter */ + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +clk_dib.cmd = clk_dib.ctl = 0; /* clear cmd, ctl */ +clk_dib.flg = clk_dib.fbf = clk_dib.srq = 1; /* set flg, fbf, srq */ +clk_error = 0; /* clear error */ +clk_select = 0; /* clear select */ +clk_ctr = 0; /* clear counter */ +sim_cancel (&clk_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Clock delay routine */ + +int32 clk_delay (int32 flg) +{ +int32 sel = clk_select; + +if ((clk_unit.flags & UNIT_DIAG) && (sel >= 4)) sel = sel - 3; +if (flg) return clk_rpt[sel]; +else return clk_time[sel]; +} diff --git a/HP2100/hp2100_sys.c b/HP2100/hp2100_sys.c new file mode 100644 index 0000000..b4abeac --- /dev/null +++ b/HP2100/hp2100_sys.c @@ -0,0 +1,720 @@ +/* hp2100_sys.c: HP 2100 simulator interface + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 24-Apr-08 JDB Changed fprint_sym to handle step with irq pending + 07-Dec-07 JDB Added BACI device + 27-Nov-07 JDB Added RTE OS/VMA/EMA mnemonics + 21-Dec-06 JDB Added "fwanxm" external for sim_load check + 19-Nov-04 JDB Added STOP_OFFLINE, STOP_PWROFF messages + 25-Sep-04 JDB Added memory protect device + Fixed display of CCA/CCB/CCE instructions + 01-Jun-04 RMS Added latent 13037 support + 19-Apr-04 RMS Recognize SFS x,C and SFC x,C + 22-Mar-02 RMS Revised for dynamically allocated memory + 14-Feb-02 RMS Added DMS instructions + 04-Feb-02 RMS Fixed bugs in alter/skip display and parsing + 01-Feb-02 RMS Added terminal multiplexor support + 16-Jan-02 RMS Added additional device support + 17-Sep-01 RMS Removed multiconsole support + 27-May-01 RMS Added multiconsole support + 14-Mar-01 RMS Revised load/dump interface (again) + 30-Oct-00 RMS Added examine to file support + 15-Oct-00 RMS Added dynamic device number support + 27-Oct-98 RMS V2.4 load interface +*/ + +#include "hp2100_defs.h" +#include "hp2100_cpu.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE mp_dev; +extern DEVICE dma0_dev, dma1_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tty_dev, clk_dev; +extern DEVICE lps_dev, lpt_dev; +extern DEVICE baci_dev; +extern DEVICE mtd_dev, mtc_dev; +extern DEVICE msd_dev, msc_dev; +extern DEVICE dpd_dev, dpc_dev; +extern DEVICE dqd_dev, dqc_dev; +extern DEVICE drd_dev, drc_dev; +extern DEVICE ds_dev; +extern DEVICE muxl_dev, muxu_dev, muxc_dev; +extern DEVICE ipli_dev, iplo_dev; +extern REG cpu_reg[]; +extern uint16 *M; +extern uint32 fwanxm; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "HP 2100"; + +char halt_msg[] = "HALT instruction xxxxxx"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 3; + +DEVICE *sim_devices[] = { + &cpu_dev, + &mp_dev, + &dma0_dev, + &dma1_dev, + &ptr_dev, + &ptp_dev, + &tty_dev, + &clk_dev, + &lps_dev, + &lpt_dev, + &baci_dev, + &dpd_dev, &dpc_dev, + &dqd_dev, &dqc_dev, + &drd_dev, &drc_dev, + &ds_dev, + &mtd_dev, &mtc_dev, + &msd_dev, &msc_dev, + &muxl_dev, &muxu_dev, &muxc_dev, + &ipli_dev, &iplo_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "Non-existent I/O device", + halt_msg, + "Breakpoint", + "Indirect address loop", + "Indirect address interrupt (should not happen!)", + "No connection on interprocessor link", + "Device/unit offline", + "Device/unit powered off" + }; + +/* Binary loader + + The binary loader consists of blocks preceded and trailed by zero frames. + A block consists of 16b words (punched big endian), as follows: + + count'xxx + origin + word 0 + : + word count-1 + checksum + + The checksum includes the origin but not the count. +*/ + +int32 fgetw (FILE *fileref) +{ +int c1, c2; + +if ((c1 = fgetc (fileref)) == EOF) return -1; +if ((c2 = fgetc (fileref)) == EOF) return -1; +return ((c1 & 0377) << 8) | (c2 & 0377); +} + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 origin, csum, zerocnt, count, word, i; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +for (zerocnt = 1;; zerocnt = -10) { /* block loop */ + for (;; zerocnt++) { /* skip 0's */ + if ((count = fgetc (fileref)) == EOF) return SCPE_OK; + else if (count) break; + else if (zerocnt == 0) return SCPE_OK; + } + if (fgetc (fileref) == EOF) return SCPE_FMT; + if ((origin = fgetw (fileref)) < 0) return SCPE_FMT; + csum = origin; /* seed checksum */ + for (i = 0; i < count; i++) { /* get data words */ + if ((word = fgetw (fileref)) < 0) return SCPE_FMT; + if (MEM_ADDR_OK (origin)) M[origin] = word; + origin = origin + 1; + csum = csum + word; + } + if ((word = fgetw (fileref)) < 0) return SCPE_FMT; + if ((word ^ csum) & DMASK) return SCPE_CSUM; + } +} + +/* Symbol tables */ + +#define I_V_FL 16 /* flag start */ +#define I_M_FL 017 /* flag mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_NPC 1 /* no operand + C */ +#define I_V_MRF 2 /* mem ref */ +#define I_V_ASH 3 /* alter/skip, shift */ +#define I_V_ESH 4 /* extended shift */ +#define I_V_EMR 5 /* extended mem ref */ +#define I_V_IO1 6 /* I/O + HC */ +#define I_V_IO2 7 /* I/O only */ +#define I_V_EGZ 010 /* ext grp, 1 op + 0 */ +#define I_V_EG2 011 /* ext grp, 2 op */ +#define I_V_ALT 012 /* alternate use instr */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_NPC (I_V_NPC << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_ASH (I_V_ASH << I_V_FL) +#define I_ESH (I_V_ESH << I_V_FL) +#define I_EMR (I_V_EMR << I_V_FL) +#define I_IO1 (I_V_IO1 << I_V_FL) +#define I_IO2 (I_V_IO2 << I_V_FL) +#define I_EGZ (I_V_EGZ << I_V_FL) +#define I_EG2 (I_V_EG2 << I_V_FL) +#define I_ALT (I_V_ALT << I_V_FL) + +static const int32 masks[] = { + 0177777, 0176777, 0074000, 0170000, + 0177760, 0177777, 0176700, 0177700, + 0177777, 0177777, 0177777 + }; + +static const char *opcode[] = { + +/* These mnemonics are used by debug printouts, so put them first. */ + + "$LIBR", "$LIBX", ".TICK", ".TNAM", /* RTE-6/VM OS firmware */ + ".STIO", ".FNW", ".IRT", ".LLS", + ".SIP", ".YLD", ".CPM", ".ETEQ", + ".ENTN", "$OTST", ".ENTC", ".DSPI", + "$DCPC", "$MPV", "$DEV", "$TBG", /* alternates for dual-use */ + + ".PMAP", "$LOC", "$VTST",/* --- */ /* RTE-6/VM VMA firmware */ +/* --- --- --- --- */ + ".IMAP", ".IMAR", ".JMAP", ".JMAR", + ".LPXR", ".LPX", ".LBPR", ".LBP", + + ".EMIO", "MMAP", "$ETST",/* --- */ /* RTE-IV EMA firmware */ +/* --- --- --- --- */ +/* --- --- --- --- */ +/* --- --- --- */ ".EMAP", + +/* Regular mnemonics. */ + + "NOP", "NOP", "AND", "JSB", + "XOR", "JMP", "IOR", "ISZ", + "ADA", "ADB" ,"CPA", "CPB", + "LDA", "LDB", "STA", "STB", + "DIAG", "ASL", "LSL", "TIMER", + "RRL", "ASR", "LSR", "RRR", + "MPY", "DIV", "DLD", "DST", + "FAD", "FSB", "FMP", "FDV", + "FIX", "FLT", + "STO", "CLO", "SOC", "SOS", + "HLT", "STF", "CLF", + "SFC", "SFS", "MIA", "MIB", + "LIA", "LIB", "OTA", "OTB", + "STC", "CLC", + "SYA", "USA", "PAA", "PBA", + "XMA", + "XLA", "XSA", "XCA", "LFA", + "RSA", "RVA", + "MBI", "MBF", + "MBW", "MWI", "MWF", "MWW", + "SYB", "USB", "PAB", "PBB", + "SSM", "JRS", + "XMM", "XMS", "XMB", + "XLB", "XSB", "XCB", "LFB", + "RSB", "RVB", "DJP", "DJS", + "SJP", "SJS", "UJP", "UJS", + "SAX", "SBX", "CAX", "CBX", + "LAX", "LBX", "STX", + "CXA", "CXB", "LDX", + "ADX", "XAX", "XBX", + "SAY", "SBY", "CAY", "CBY", + "LAY", "LBY", "STY", + "CYA", "CYB", "LDY", + "ADY", "XAY", "XBY", + "ISX", "DSX", "JLY", "LBT", + "SBT", "MBT", "CBT", "SBT", + "ISY", "DSY", "JPY", "SBS", + "CBS", "TBS", "CMW", "MVW", + NULL, /* decode only */ + NULL + }; + +static const int32 opc_val[] = { + 0105340+I_NPN, 0105341+I_NPN, 0105342+I_NPN, 0105343+I_NPN, /* RTE-6/VM OS */ + 0105344+I_NPN, 0105345+I_NPN, 0105346+I_NPN, 0105347+I_NPN, + 0105350+I_NPN, 0105351+I_NPN, 0105352+I_NPN, 0105353+I_NPN, + 0105354+I_ALT, 0105355+I_ALT, 0105356+I_ALT, 0105357+I_ALT, + 0105354+I_NPN, 0105355+I_NPN, 0105356+I_NPN, 0105357+I_NPN, /* alternates */ + + 0105240+I_ALT, 0105241+I_ALT, 0105242+I_ALT, /* --- */ /* RTE-6/VM VMA */ +/* --- --- --- --- */ + 0105250+I_NPN, 0105251+I_NPN, 0105252+I_NPN, 0105253+I_NPN, + 0105254+I_NPN, 0105255+I_NPN, 0105256+I_NPN, 0105257+I_ALT, + + 0105240+I_NPN, 0105241+I_NPN, 0105242+I_NPN, /* RTE-IV EMA */ +/* --- --- --- --- */ +/* --- --- --- --- */ +/* --- --- --- */ 0105257+I_NPN, + + 0000000+I_NPN, 0002000+I_NPN, 0010000+I_MRF, 0014000+I_MRF, + 0020000+I_MRF, 0024000+I_MRF, 0030000+I_MRF, 0034000+I_MRF, + 0040000+I_MRF, 0044000+I_MRF, 0050000+I_MRF, 0054000+I_MRF, + 0060000+I_MRF, 0064000+I_MRF, 0070000+I_MRF, 0074000+I_MRF, + 0100000+I_NPN, 0100020+I_ESH, 0100040+I_ESH, 0100060+I_NPN, + 0100100+I_ESH, 0101020+I_ESH, 0101040+I_ESH, 0101100+I_ESH, + 0100200+I_EMR, 0100400+I_EMR, 0104200+I_EMR, 0104400+I_EMR, + 0105000+I_EMR, 0105020+I_EMR, 0105040+I_EMR, 0105060+I_EMR, + 0105100+I_NPN, 0105120+I_NPN, + 0102101+I_NPN, 0103101+I_NPN, 0102201+I_NPC, 0102301+I_NPC, + 0102000+I_IO1, 0102100+I_IO2, 0103100+I_IO2, + 0102200+I_IO1, 0102300+I_IO1, 0102400+I_IO1, 0106400+I_IO1, + 0102500+I_IO1, 0106500+I_IO1, 0102600+I_IO1, 0106600+I_IO1, + 0102700+I_IO1, 0106700+I_IO1, + 0101710+I_NPN, 0101711+I_NPN, 0101712+I_NPN, 0101713+I_NPN, + 0101722+I_NPN, + 0101724+I_EMR, 0101725+I_EMR, 0101726+I_EMR, 0101727+I_NPN, + 0101730+I_NPN, 0101731+I_NPN, + 0105702+I_NPN, 0105703+I_NPN, + 0105704+I_NPN, 0105705+I_NPN, 0105706+I_NPN, 0105707+I_NPN, + 0105710+I_NPN, 0105711+I_NPN, 0105712+I_NPN, 0105713+I_NPN, + 0105714+I_EMR, 0105715+I_EG2, + 0105720+I_NPN, 0105721+I_NPN, 0105722+I_NPN, + 0105724+I_EMR, 0105725+I_EMR, 0105726+I_EMR, 0105727+I_NPN, + 0105730+I_NPN, 0105731+I_NPN, 0105732+I_EMR, 0105733+I_EMR, + 0105734+I_EMR, 0105735+I_EMR, 0105736+I_EMR, 0105737+I_EMR, + 0101740+I_EMR, 0105740+I_EMR, 0101741+I_NPN, 0105741+I_NPN, + 0101742+I_EMR, 0105742+I_EMR, 0105743+I_EMR, + 0101744+I_NPN, 0105744+I_NPN, 0105745+I_EMR, + 0105746+I_EMR, 0101747+I_NPN, 0105747+I_NPN, + 0101750+I_EMR, 0105750+I_EMR, 0101751+I_NPN, 0105751+I_NPN, + 0101752+I_EMR, 0105752+I_EMR, 0105753+I_EMR, + 0101754+I_NPN, 0105754+I_NPN, 0105755+I_EMR, + 0105756+I_EMR, 0101757+I_NPN, 0105757+I_NPN, + 0105760+I_NPN, 0105761+I_NPN, 0105762+I_EMR, 0105763+I_NPN, + 0105764+I_NPN, 0105765+I_EGZ, 0105766+I_EGZ, 0105767+I_NPN, + 0105770+I_NPN, 0105771+I_NPN, 0105772+I_EMR, 0105773+I_EG2, + 0105774+I_EG2, 0105775+I_EG2, 0105776+I_EGZ, 0105777+I_EGZ, + 0000000+I_ASH, /* decode only */ + -1 + }; + +/* Decode tables for shift and alter/skip groups */ + +static const char *stab[] = { + "ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF", + "BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF", + "CLA", "CMA", "CCA", "CLB", "CMB", "CCB", + "SEZ", "CLE", "CLE", "CME", "CCE", + "SSA", "SSB", "SLA", "SLB", + "ALS", "ARS", "RAL", "RAR", "ALR", "ERA", "ELA", "ALF", + "BLS", "BRS", "RBL", "RBR", "BLR", "ERB", "ELB", "BLF", + "INA", "INB", "SZA", "SZB", "RSS", + NULL + }; + +static const int32 mtab[] = { + 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, + 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, 0007700, + 0007400, 0007400, 0007400, 0007400, 0007400, 0007400, + 0002040, 0002040, 0002300, 0002300, 0002300, + 0006020, 0006020, 0004010, 0004010, + 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, + 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, 0006027, + 0006004, 0006004, 0006002, 0006002, 0002001, + 0 + }; + +static const int32 vtab[] = { + 0001000, 0001100, 0001200, 0001300, 0001400, 0001500, 0001600, 0001700, + 0005000, 0005100, 0005200, 0005300, 0005400, 0005500, 0005600, 0005700, + 0002400, 0003000, 0003400, 0006400, 0007000, 0007400, + 0002040, 0000040, 0002100, 0002200, 0002300, + 0002020, 0006020, 0000010, 0004010, + 0000020, 0000021, 0000022, 0000023, 0000024, 0000025, 0000026, 0000027, + 0004020, 0004021, 0004022, 0004023, 0004024, 0004025, 0004026, 0004027, + 0002004, 0006004, 0002002, 0006002, 0002001, + -1 + }; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, cm, i, j, inst, disp; +uint32 irq; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +inst = val[0]; +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* characters? */ + fprintf (of, FMTASC ((inst >> 8) & 0177)); + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* If we are being called as a result of a VM stop to display the next + instruction to be executed, check to see if an interrupt is pending and not + deferred. If so, then display the interrupt source and the trap cell + instruction as the instruction to be executed, rather than the instruction at + the current PC. +*/ + +if (sw & SIM_SW_STOP) { /* simulator stop? */ + irq = calc_int (); /* check interrupt */ + + if (irq && (!ion_defer || !calc_defer())) { /* pending interrupt and not deferred? */ + inst = val[0] = ReadIO (irq, SMAP); /* load trap cell instruction */ + val[1] = ReadIO (irq + 1, SMAP); /* might be multi-word */ + val[2] = ReadIO (irq + 2, SMAP); /* although it's unlikely */ + fprintf (of, "IAK %2o: ", irq); /* report acknowledged interrupt */ + } + } + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ + switch (j) { /* case on class */ + + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_NPC: /* no operands + C */ + fprintf (of, "%s", opcode[i]); + if (inst & I_HC) fprintf (of, " C"); + break; + + case I_V_MRF: /* mem ref */ + disp = inst & I_DISP; /* displacement */ + fprintf (of, "%s ", opcode[i]); /* opcode */ + if (inst & I_CP) { /* current page? */ + if (cflag) fprintf (of, "%-o", (addr & I_PAGENO) | disp); + else fprintf (of, "C %-o", disp); + } + else fprintf (of, "%-o", disp); /* page zero */ + if (inst & I_IA) fprintf (of, ",I"); + break; + + case I_V_ASH: /* shift, alter-skip */ + cm = FALSE; + for (i = 0; mtab[i] != 0; i++) { + if ((inst & mtab[i]) == vtab[i]) { + inst = inst & ~(vtab[i] & 01777); + if (cm) fprintf (of, ","); + cm = TRUE; + fprintf (of, "%s", stab[i]); + } + } + if (!cm) return SCPE_ARG; /* nothing decoded? */ + break; + + case I_V_ESH: /* extended shift */ + disp = inst & 017; /* shift count */ + if (disp == 0) disp = 16; + fprintf (of, "%s %d", opcode[i], disp); + break; + + case I_V_EMR: /* extended mem ref */ + fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK); + if (val[1] & I_IA) fprintf (of, ",I"); + return -1; /* extra word */ + + case I_V_IO1: /* IOT with H/C */ + fprintf (of, "%s %-o", opcode[i], inst & I_DEVMASK); + if (inst & I_HC) fprintf (of, ",C"); + break; + + case I_V_IO2: /* IOT */ + fprintf (of, "%s %-o", opcode[i], inst & I_DEVMASK); + break; + + case I_V_EGZ: /* ext grp 1 op + 0 */ + fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK); + if (val[1] & I_IA) fprintf (of, ",I"); + return -2; /* extra words */ + + case I_V_EG2: /* ext grp 2 op */ + fprintf (of, "%s %-o", opcode[i], val[1] & VAMASK); + if (val[1] & I_IA) fprintf (of, ",I"); + fprintf (of, " %-o", val[2] & VAMASK); + if (val[2] & I_IA) fprintf (of, ",I"); + return -2; /* extra words */ + + case I_V_ALT: /* alternate use instr */ + if ((inst >= 0105354) && + (inst <= 0105357) && /* RTE-6/VM OS range? */ + (addr >= 2) && + (addr <= 077)) /* in trap cell? */ + continue; /* use alternate mnemonic */ + + else if ((inst >= 0105240) && /* RTE-6/VM VMA range? */ + (inst <= 0105257) && + (cpu_unit.flags & UNIT_EMA)) /* EMA enabled? */ + continue; /* use EMA mnemonics */ + + else + fprintf (of, "%s", opcode[i]); /* print opcode */ + break; + } + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get address with indirection + + Inputs: + *cptr = pointer to input string + Outputs: + val = address + -1 if error +*/ + +int32 get_addr (char *cptr) +{ +int32 d; +t_stat r; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, ','); /* get next field */ +d = get_uint (gbuf, 8, VAMASK, &r); /* construe as addr */ +if (r != SCPE_OK) return -1; +if (*cptr != 0) { /* more? */ + cptr = get_glyph (cptr, gbuf, 0); /* look for indirect */ + if (*cptr != 0) return -1; /* should be done */ + if (strcmp (gbuf, "I")) return -1; /* I? */ + d = d | I_IA; + } +return d; +} + +/* Symbolic input + + Inputs: + *iptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *iptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k, clef, tbits; +t_stat r, ret; +char *cptr, gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*iptr)) iptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*iptr == '\'') && iptr++)) { /* ASCII char? */ + if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) iptr[0] & 0177; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*iptr == '"') && iptr++)) { /* char string? */ + if (iptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) iptr[0] & 0177) << 8) | + ((t_value) iptr[1] & 0177); + return SCPE_OK; + } + +ret = SCPE_OK; +cptr = get_glyph (iptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i]) { /* found opcode? */ + val[0] = opc_val[i] & DMASK; /* get value */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + switch (j) { /* case on class */ + + case I_V_NPN: /* no operand */ + break; + + case I_V_NPC: /* no operand + C */ + if (*cptr != 0) { + cptr = get_glyph (cptr, gbuf, 0); + if (strcmp (gbuf, "C")) return SCPE_ARG; + val[0] = val[0] | I_HC; + } + break; + + case I_V_MRF: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if (k = (strcmp (gbuf, "C") == 0)) { /* C specified? */ + val[0] = val[0] | I_CP; + cptr = get_glyph (cptr, gbuf, 0); + } + else if (k = (strcmp (gbuf, "Z") == 0)) { /* Z specified? */ + cptr = get_glyph (cptr, gbuf, ','); + } + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + if ((d & VAMASK) <= I_DISP) val[0] = val[0] | d; + else if (cflag && !k && (((addr ^ d) & I_PAGENO) == 0)) + val[0] = val[0] | (d & (I_IA | I_DISP)) | I_CP; + else return SCPE_ARG; + break; + + case I_V_ESH: /* extended shift */ + cptr = get_glyph (cptr, gbuf, 0); + d = get_uint (gbuf, 10, 16, &r); + if ((r != SCPE_OK) || (d == 0)) return SCPE_ARG; + val[0] = val[0] | (d & 017); + break; + + case I_V_EMR: /* extended mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + val[1] = d; + ret = -1; + break; + + case I_V_IO1: /* IOT + optional C */ + cptr = get_glyph (cptr, gbuf, ','); /* get device */ + d = get_uint (gbuf, 8, I_DEVMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + if (*cptr != 0) { + cptr = get_glyph (cptr, gbuf, 0); + if (strcmp (gbuf, "C")) return SCPE_ARG; + val[0] = val[0] | I_HC; + } + break; + + case I_V_IO2: /* IOT */ + cptr = get_glyph (cptr, gbuf, 0); /* get device */ + d = get_uint (gbuf, 8, I_DEVMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + break; + + case I_V_EGZ: /* ext grp 1 op + 0 */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + val[1] = d; + val[2] = 0; + ret = -2; + break; + + case I_V_EG2: /* ext grp 2 op */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((d = get_addr (gbuf)) < 0) return SCPE_ARG; + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((k = get_addr (gbuf)) < 0) return SCPE_ARG; + val[1] = d; + val[2] = k; + ret = -2; + break; + } /* end case */ + + if (*cptr != 0) return SCPE_ARG; /* junk at end? */ + return ret; + } /* end if opcode */ + +/* Shift or alter-skip + + Each opcode is matched by a mask, specifiying the bits affected, and + the value, specifying the value. As opcodes are processed, the mask + values are used to specify which fields have already been filled in. + + The mask has two subfields, the type bits (A/B and A/S), and the field + bits. The type bits, once specified by any instruction, must be + consistent in all other instructions. The mask bits assure that no + field is filled in twice. + + Two special cases: + + 1. The dual shift field in shift requires checking how much of the + target word has been filled in before assigning the shift value. + To implement this, shifts are listed twice is the decode table. + If the current subopcode is a shift in the first part of the table + (entries 0..15), and CLE has been seen or the first shift field is + filled in, the code forces a mismatch. The glyph will match in + the second part of the table. + + 2. CLE processing must be deferred until the instruction can be + classified as shift or alter-skip, since it has two different + bit values in the two classes. To implement this, CLE seen is + recorded as a flag and processed after all other subopcodes. +*/ + +clef = FALSE; +tbits = 0; +val[0] = 0; +for (cptr = get_glyph (iptr, gbuf, ','); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, ',')) { /* loop thru glyphs */ + if (strcmp (gbuf, "CLE") == 0) { /* CLE? */ + if (clef) return SCPE_ARG; /* already seen? */ + clef = TRUE; /* set flag */ + continue; + } + for (i = 0; stab[i] != NULL; i++) { /* find subopcode */ + if ((strcmp (gbuf, stab[i]) == 0) && + ((i >= 16) || (!clef && ((val[0] & 001710) == 0)))) break; + } + if (stab[i] == NULL) return SCPE_ARG; + if (tbits & mtab[i] & (I_AB | I_ASKP) & (vtab[i] ^ val[0])) + return SCPE_ARG; + if (tbits & mtab[i] & ~(I_AB | I_ASKP)) return SCPE_ARG; + tbits = tbits | mtab[i]; /* fill type+mask */ + val[0] = val[0] | vtab[i]; /* fill value */ + } +if (clef) { /* CLE seen? */ + if (val[0] & I_ASKP) { /* alter-skip? */ + if (tbits & 0100) return SCPE_ARG; /* already filled in? */ + else val[0] = val[0] | 0100; + } + else val[0] = val[0] | 040; /* fill in shift */ + } +return ret; +} diff --git a/I1401/i1401_cd.c b/I1401/i1401_cd.c new file mode 100644 index 0000000..34c8075 --- /dev/null +++ b/I1401/i1401_cd.c @@ -0,0 +1,387 @@ +/* i1401_cd.c: IBM 1402 card reader/punch + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cdr card reader + cdp card punch + stack stackers (5 units) + 0 normal + 1 1 + 2 2/8 + 3 unused + 4 4 + + Cards are represented as ASCII text streams terminated by newlines. + This allows cards to be created and edited as normal files. + + 28-Jun-07 RMS Added support for SS overlap modifiers + 19-Jan-07 RMS Added UNIT_TEXT flag + 20-Sep-05 RMS Revised for new code tables, compatible colbinary treatment + 30-Aug-05 RMS Fixed read, punch to ignore modifier on 1,4 char inst + (reported by Van Snyder) + 14-Nov-04 WVS Added column binary support + 25-Apr-03 RMS Revised for extended file support + 30-May-02 RMS Widened POS to 32b + 30-Jan-02 RMS New zero footprint card bootstrap from Van Snyder + 29-Nov-01 RMS Added read only unit support + 13-Apr-01 RMS Revised for register arrays +*/ + +#include "i1401_defs.h" +#include + +#define UNIT_V_PCH (UNIT_V_UF + 0) /* output conv */ +#define UNIT_PCH (1 << UNIT_V_PCH) + +extern uint8 M[]; +extern int32 ind[64], ssa, iochk; +extern int32 conv_old; + +int32 s1sel, s2sel, s4sel, s8sel; +char rbuf[2 * CBUFSIZE]; /* > CDR_WIDTH */ +t_stat cdr_svc (UNIT *uptr); +t_stat cdr_boot (int32 unitno, DEVICE *dptr); +t_stat cdr_attach (UNIT *uptr, char *cptr); +t_stat cd_reset (DEVICE *dptr); +int32 bcd2asc (int32 c, UNIT *uptr); +char colbin_to_bcd (uint32 cb); + +/* Card reader data structures + + cdr_dev CDR descriptor + cdr_unit CDR unit descriptor + cdr_reg CDR register list +*/ + +UNIT cdr_unit = { + UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0), 100 + }; + +REG cdr_reg[] = { + { FLDATA (LAST, ind[IN_LST], 0) }, + { FLDATA (ERR, ind[IN_READ], 0) }, + { FLDATA (S1, s1sel, 0) }, + { FLDATA (S2, s2sel, 0) }, + { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, cdr_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, rbuf, 8, 8, CDR_WIDTH) }, + { NULL } + }; + +DEVICE cdr_dev = { + "CDR", &cdr_unit, cdr_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + &cdr_boot, &cdr_attach, NULL + }; + +/* CDP data structures + + cdp_dev CDP device descriptor + cdp_unit CDP unit descriptor + cdp_reg CDP register list +*/ + +UNIT cdp_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) + }; + +REG cdp_reg[] = { + { FLDATA (ERR, ind[IN_PNCH], 0) }, + { FLDATA (S4, s4sel, 0) }, + { FLDATA (S8, s8sel, 0) }, + { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, + { NULL } + }; + +MTAB cdp_mod[] = { + { UNIT_PCH, 0, "business set", "BUSINESS" }, + { UNIT_PCH, UNIT_PCH, "Fortran set", "FORTRAN" }, + { 0 } + }; + +DEVICE cdp_dev = { + "CDP", &cdp_unit, cdp_reg, cdp_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL + }; + +/* Stacker data structures + + stack_dev STACK device descriptor + stack_unit STACK unit descriptors + stack_reg STACK register list +*/ + +UNIT stack_unit[] = { + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) }, + { UDATA (NULL, UNIT_DIS, 0) }, /* unused */ + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) } + }; + +REG stack_reg[] = { + { DRDATA (POS0, stack_unit[0].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (POS1, stack_unit[1].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (POS28, stack_unit[2].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (POS4, stack_unit[4].pos, T_ADDR_W), PV_LEFT }, + { NULL } + }; + +DEVICE stack_dev = { + "STKR", stack_unit, stack_reg, cdp_mod, + 5, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL + }; + +/* Card read routine + + Modifiers have been checked by the caller + C modifier is recognized (column binary is implemented) +*/ + +t_stat read_card (int32 ilnt, int32 mod) +{ +int32 i, cbn, c1, c2; +t_stat r; + +if (sim_is_active (&cdr_unit)) { /* busy? */ + sim_cancel (&cdr_unit); /* cancel */ + if (r = cdr_svc (&cdr_unit)) return r; /* process */ + } +if ((cdr_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +ind[IN_READ] = ind[IN_LST] = s1sel = s2sel = 0; /* default stacker */ +cbn = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_C); /* col binary? */ +for (i = 0; i < 2 * CBUFSIZE; i++) rbuf[i] = 0; /* clear extended buf */ +fgets (rbuf, (cbn)? 2 * CBUFSIZE: CBUFSIZE, /* rd bin/char card */ + cdr_unit.fileref); +if (feof (cdr_unit.fileref)) return STOP_NOCD; /* eof? */ +if (ferror (cdr_unit.fileref)) { /* error? */ + ind[IN_READ] = 1; + perror ("Card reader I/O error"); + clearerr (cdr_unit.fileref); + if (iochk) return SCPE_IOERR; + return SCPE_OK; + } +cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ +if (ssa) { /* if last cd on */ + getc (cdr_unit.fileref); /* see if more */ + if (feof (cdr_unit.fileref)) ind[IN_LST] = 1; /* eof? set flag */ + fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); + } +if (cbn) { /* column binary */ + for (i = 0; i < CDR_WIDTH; i++) { + if (conv_old) { + c1 = ascii2bcd (rbuf[i]); + c2 = ascii2bcd (rbuf[CDR_WIDTH + i]); + } + else { + c1 = ascii2bcd (rbuf[2 * i]); + c2 = ascii2bcd (rbuf[(2 * i) + 1]); + } + M[CD_CBUF1 + i] = (M[CD_CBUF1 + i] & WM) | c1; + M[CD_CBUF2 + i] = (M[CD_CBUF2 + i] & WM) | c2; + M[CDR_BUF + i] = colbin_to_bcd ((c1 << 6) | c2); + } /* end for i */ + } /* end if col bin */ +else { /* normal read */ + for (i = 0; i < CDR_WIDTH; i++) { /* cvt to BCD */ + rbuf[i] = ascii2bcd (rbuf[i]); + M[CDR_BUF + i] = (M[CDR_BUF + i] & WM) | rbuf[i]; + } + } +M[CDR_BUF - 1] = 060; /* mem mark */ +sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ +return SCPE_OK; +} + +/* Card reader service. If a stacker select is active, copy to the + selected stacker. Otherwise, copy to the normal stacker. If the + unit is unattached, simply exit. +*/ + +t_stat cdr_svc (UNIT *uptr) +{ +int32 i; + +if (s1sel) uptr = &stack_unit[1]; /* stacker 1? */ +else if (s2sel) uptr = &stack_unit[2]; /* stacker 2? */ +else uptr = &stack_unit[0]; /* then default */ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +for (i = 0; i < CDR_WIDTH; i++) + rbuf[i] = bcd2ascii (rbuf[i], uptr->flags & UNIT_PCH); +for (i = CDR_WIDTH - 1; (i >= 0) && (rbuf[i] == ' '); i--) rbuf[i] = 0; +rbuf[CDR_WIDTH] = 0; /* null at end */ +fputs (rbuf, uptr->fileref); /* write card */ +fputc ('\n', uptr->fileref); /* plus new line */ +uptr->pos = ftell (uptr->fileref); /* update position */ +if (ferror (uptr->fileref)) { /* error? */ + perror ("Card stacker I/O error"); + clearerr (uptr->fileref); + if (iochk) return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Card punch routine + + Modifiers have been checked by the caller + C modifier is recognized (column binary is implemented) +*/ + +t_stat punch_card (int32 ilnt, int32 mod) +{ +int32 i, cbn, c1, c2; +static char pbuf[(2 * CDP_WIDTH) + 1]; /* + null */ +t_bool use_h; +UNIT *uptr; + +if (s8sel) uptr = &stack_unit[2]; /* stack 8? */ +else if (s4sel) uptr = &stack_unit[4]; /* stack 4? */ +else uptr = &cdp_unit; /* normal output */ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +use_h = uptr->flags & UNIT_PCH; +ind[IN_PNCH] = s4sel = s8sel = 0; /* clear flags */ +cbn = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_C); /* col binary? */ + +M[CDP_BUF - 1] = 012; /* set prev loc */ +if (cbn) { /* column binary */ + for (i = 0; i < CDP_WIDTH; i++) { + c1 = bcd2ascii (M[CD_CBUF1 + i] & CHAR, use_h); + c2 = bcd2ascii (M[CD_CBUF2 + i] & CHAR, use_h); + if (conv_old) { + pbuf[i] = c1; + pbuf[i + CDP_WIDTH] = c2; + } + else { + pbuf[2 * i] = c1; + pbuf[(2 * i) + 1] = c2; + } + } + for (i = 2 * CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) + pbuf[i] = 0; + pbuf[2 * CDP_WIDTH] = 0; /* trailing null */ + } +else { /* normal */ + for (i = 0; i < CDP_WIDTH; i++) + pbuf[i] = bcd2ascii (M[CDP_BUF + i] & CHAR, use_h); + for (i = CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) + pbuf[i] = 0; + pbuf[CDP_WIDTH] = 0; /* trailing null */ + } +fputs (pbuf, uptr->fileref); /* output card */ +fputc ('\n', uptr->fileref); /* plus new line */ +uptr->pos = ftell (uptr->fileref); /* update position */ +if (ferror (uptr->fileref)) { /* error? */ + perror ("Card punch I/O error"); + clearerr (uptr->fileref); + if (iochk) return SCPE_IOERR; + ind[IN_PNCH] = 1; + } +return SCPE_OK; +} + +/* Select stack routine + + Modifiers have been checked by the caller + Modifiers are 1, 2, 4, 8 for the respective stack, + or $, ., square for overlap control (ignored). +*/ + +t_stat select_stack (int32 ilnt, int32 mod) +{ +if (mod == BCD_ONE) s1sel = 1; +else if (mod == BCD_TWO) s2sel = 1; +else if (mod == BCD_FOUR) s4sel = 1; +else if (mod == BCD_EIGHT) s8sel = 1; +return SCPE_OK; +} + +/* Card reader/punch reset */ + +t_stat cd_reset (DEVICE *dptr) +{ +ind[IN_LST] = ind[IN_READ] = ind[IN_PNCH] = 0; /* clear indicators */ +s1sel = s2sel = s4sel = s8sel = 0; /* clear stacker sel */ +sim_cancel (&cdr_unit); /* clear reader event */ +return SCPE_OK; +} + +/* Card reader attach */ + +t_stat cdr_attach (UNIT *uptr, char *cptr) +{ +ind[IN_LST] = ind[IN_READ] = 0; /* clear last card */ +return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ + +#define BOOT_START 0 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (unsigned char)) + +static const unsigned char boot_rom[] = { + OP_R + WM, OP_NOP + WM /* R, NOP */ + }; + +t_stat cdr_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_IS; + +for (i = 0; i < CDR_WIDTH; i++) M[CDR_BUF + i] = 0; /* clear buffer */ +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_IS = BOOT_START; +return SCPE_OK; +} + +/* Column binary to BCD + + This is based on documentation in the IBM 1620 manual and may not be + accurate for the 7094. Each row (12,11,0,1..9) is interpreted as a bit + pattern, and the appropriate bits are set. (Double punches inclusive + OR, eg, 1,8,9 is 9.) On the 1620, double punch errors are detected; + since the 7094 only reads column binary, double punches are ignored. + + Bit order, left to right, is 12, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. + The for loop works right to left, so the table is reversed. */ + +static const char row_val[12] = { + 011, 010, 007, 006, 005, 004, + 003, 002, 001, 020, 040, 060 + }; + +char colbin_to_bcd (uint32 cb) +{ +uint32 i; +char bcd; + +for (i = 0, bcd = 0; i < 12; i++) { /* 'sum' rows */ + if (cb & (1 << i)) bcd |= row_val[i]; + } +return bcd; +} diff --git a/I1401/i1401_cpu.c b/I1401/i1401_cpu.c new file mode 100644 index 0000000..851a300 --- /dev/null +++ b/I1401/i1401_cpu.c @@ -0,0 +1,1808 @@ +/* i1401_cpu.c: IBM 1401 CPU simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 07-Jul-07 RMS Removed restriction on load-mode binary tape + 28-Jun-07 RMS Added support for SS overlap modifiers + 22-May-06 RMS Fixed format error in CPU history (found by Peter Schorn) + 06-Mar-06 RMS Fixed bug in divide (found by Van Snyder) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 01-Sep-05 RMS Removed error stops in MCE + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 02-Jun-05 RMS Fixed SSB-SSG clearing on RESET + (reported by Ralph Reinke) + 14-Nov-04 WVS Added column binary support, debug support + 06-Nov-04 RMS Added instruction history + 12-Jul-03 RMS Moved ASCII/BCD tables to included file + Revised fetch to model hardware + Removed length checking in fetch phase + 16-Mar-03 RMS Fixed mnemonic, instruction lengths, and reverse + scan length check bug for MCS + Fixed MCE bug, BS off by 1 if zero suppress + Fixed chaining bug, D lost if return to SCP + Fixed H branch, branch occurs after continue + Added check for invalid 8 character MCW, LCA + 03-Jun-03 RMS Added 1311 support + 22-May-02 RMS Added multiply and divide + 30-Dec-01 RMS Added old PC queue + 30-Nov-01 RMS Added extended SET/SHOW support + 10-Aug-01 RMS Removed register in declarations + 07-Dec-00 RMS Fixed bugs found by Charles Owen + -- 4,7 char NOPs are legal + -- 1 char B is chained BCE + -- MCE moves whole char after first + 14-Apr-99 RMS Changed t_addr to unsigned + + The register state for the IBM 1401 is: + + IS I storage address register (PC) + AS A storage address register (address of first operand) + BS B storage address register (address of second operand) + ind[0:63] indicators + SSA sense switch A + IOCHK I/O check + PRCHK process check + + The IBM 1401 is a variable instruction length, decimal data system. + Memory consists of 4000, 8000, 12000, or 16000 BCD characters, each + containing six bits of data and a word mark. There are no general + registers; all instructions are memory to memory, using explicit + addresses or an address pointer from a prior instruction. + + BCD numeric data consists of the low four bits of a character (DIGIT), + encoded as X, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, X, X, X, X, X. The high + two bits (ZONE) encode the sign of the data as +, +, -, +. Character + data uses all six bits of a character. Numeric and character fields are + delimited by a word mark. Fields are typically processed in descending + address order (low-order data to high-order data). + + The 1401 encodes a decimal address, and an index register number, in + three characters: + + character zone digit + addr + 0 <1:0> of thousands hundreds + addr + 1 index register # tens + addr + 2 <3:2> of thousands ones + + Normally the digit values 0, 11, 12, 13, 14, 15 are illegal in addresses. + However, in indexing, digits are passed through the adder, and illegal + values are normalized to legal counterparts. + + The 1401 has six instruction formats: + + op A and B addresses, if any, from AS and BS + op d A and B addresses, if any, from AS and BS + op aaa B address, if any, from BS + op aaa d B address, if any, from BS + op aaa bbb + op aaa bbb d + + where aaa is the A address, bbb is the B address, and d is a modifier. + The opcode has word mark set; all other characters have word mark clear. + + This routine is the instruction decode routine for the IBM 1401. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + illegal addresses or instruction formats + I/O error in I/O simulator + + 2. Interrupts. The 1401 has no interrupt structure. + + 3. Non-existent memory. On the 1401, references to non-existent + memory halt the processor. + + 4. Adding I/O devices. These modules must be modified: + + i1401_cpu.c add device dispatching code to iodisp + i1401_sys.c add sim_devices table entry +*/ + +#include "i1401_defs.h" +#include "i1401_dat.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = saved_IS + +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + uint16 is; + uint16 ilnt; + uint8 inst[MAX_L]; + } InstHistory; + +/* These macros validate addresses. If an addresses error is detected, + they return an error status to the caller. These macros should only + be used in a routine that returns a t_stat value. +*/ + +#define MM(x) x = x - 1; \ + if (x < 0) { \ + x = BA + MAXMEMSIZE - 1; \ + reason = STOP_WRAP; \ + break; \ + } + +#define PP(x) x = x + 1; \ + if (ADDR_ERR (x)) { \ + x = BA + (x % MAXMEMSIZE); \ + reason = STOP_WRAP; \ + break; \ + } + +#define BRANCH if (ADDR_ERR (AS)) { \ + reason = STOP_INVBR; \ + break; \ + } \ + if (cpu_unit.flags & XSA) BS = IS; \ + else BS = BA + 0; \ + PCQ_ENTRY; \ + IS = AS; + +uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */ +int32 saved_IS = 0; /* saved IS */ +int32 AS = 0; /* AS */ +int32 BS = 0; /* BS */ +int32 D = 0; /* modifier */ +int32 as_err = 0, bs_err = 0; /* error flags */ +int32 hb_pend = 0; /* halt br pending */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 ind[64] = { 0 }; /* indicators */ +int32 ssa = 1; /* sense switch A */ +int32 prchk = 0; /* process check stop */ +int32 iochk = 0; /* I/O check stop */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ +t_bool conv_old = 0; /* old conversions */ + +extern int32 sim_int_char; +extern int32 sim_emax; +extern t_value *sim_eval; +extern FILE *sim_deb; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_conv (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_conv (FILE *st, UNIT *uptr, int32 val, void *desc); +int32 store_addr_h (int32 addr); +int32 store_addr_t (int32 addr); +int32 store_addr_u (int32 addr); +int32 div_add (int32 ap, int32 bp, int32 aend); +int32 div_sub (int32 ap, int32 bp, int32 aend); +void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp); +t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr); +t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod); + +extern t_stat read_card (int32 ilnt, int32 mod); +extern t_stat punch_card (int32 ilnt, int32 mod); +extern t_stat select_stack (int32 mod); +extern t_stat carriage_control (int32 mod); +extern t_stat write_line (int32 ilnt, int32 mod); +extern t_stat inq_io (int32 flag, int32 mod); +extern t_stat mt_io (int32 unit, int32 flag, int32 mod); +extern t_stat dp_io (int32 fnc, int32 flag, int32 mod); +extern t_stat mt_func (int32 unit, int32 mod); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { + UDATA (NULL, UNIT_FIX + UNIT_BCD + STDOPT, MAXMEMSIZE) + }; + +REG cpu_reg[] = { + { DRDATA (IS, saved_IS, 14), PV_LEFT }, + { DRDATA (AS, AS, 14), PV_LEFT }, + { DRDATA (BS, BS, 14), PV_LEFT }, + { FLDATA (ASERR, as_err, 0) }, + { FLDATA (BSERR, bs_err, 0) }, + { ORDATA (D, D, 7) }, + { FLDATA (SSA, ssa, 0) }, + { FLDATA (SSB, ind[IN_SSB], 0) }, + { FLDATA (SSC, ind[IN_SSC], 0) }, + { FLDATA (SSD, ind[IN_SSD], 0) }, + { FLDATA (SSE, ind[IN_SSE], 0) }, + { FLDATA (SSF, ind[IN_SSF], 0) }, + { FLDATA (SSG, ind[IN_SSG], 0) }, + { FLDATA (EQU, ind[IN_EQU], 0) }, + { FLDATA (UNEQ, ind[IN_UNQ], 0) }, + { FLDATA (HIGH, ind[IN_HGH], 0) }, + { FLDATA (LOW, ind[IN_LOW], 0) }, + { FLDATA (OVF, ind[IN_OVF], 0) }, + { FLDATA (IOCHK, iochk, 0) }, + { FLDATA (PRCHK, prchk, 0) }, + { FLDATA (HBPEND, hb_pend, 0) }, + { BRDATA (ISQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC }, + { DRDATA (ISQP, pcq_p, 6), REG_HRO }, + { ORDATA (WRU, sim_int_char, 8) }, + { FLDATA (CONVOLD, conv_old, 0), REG_HIDDEN }, + { NULL } + }; + +MTAB cpu_mod[] = { + { XSA, XSA, "XSA", "XSA", NULL }, + { XSA, 0, "no XSA", "NOXSA", NULL }, + { HLE, HLE, "HLE", "HLE", NULL }, + { HLE, 0, "no HLE", "NOHLE", NULL }, + { BBE, BBE, "BBE", "BBE", NULL }, + { BBE, 0, "no BBE", "NOBBE", NULL }, + { MA, MA, "MA", 0, NULL }, + { MA, 0, "no MA", 0, NULL }, + { MR, MR, "MR", "MR", NULL }, + { MR, 0, "no MR", "NOMR", NULL }, + { EPE, EPE, "EPE", "EPE", NULL }, + { EPE, 0, "no EPE", "NOEPE", NULL }, + { MDV, MDV, "MDV", "MDV", NULL }, + { MDV, 0, "no MDV", "NOMDV", NULL }, + { UNIT_MSIZE, 4000, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8000, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12000, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16000, NULL, "16K", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "CONVERSIONS", "NEWCONVERSIONS", + &cpu_set_conv, &cpu_show_conv }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, NULL, "OLDCONVERSIONS", + &cpu_set_conv, NULL }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 14, 1, 8, 7, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, DEV_DEBUG + }; + +/* Tables */ + +/* Opcode table - length, dispatch, and option flags. This table is + used by the symbolic input routine to validate instruction lengths */ + +const int32 op_table[64] = { + 0, /* 00: illegal */ + L1 | L2 | L4 | L5, /* read */ + L1 | L2 | L4 | L5, /* write */ + L1 | L2 | L4 | L5, /* write and read */ + L1 | L2 | L4 | L5, /* punch */ + L1 | L4, /* read and punch */ + L1 | L2 | L4 | L5, /* write and read */ + L1 | L2 | L4 | L5, /* write, read, punch */ + L1, /* 10: read feed */ + L1, /* punch feed */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ | MA, /* modify address */ + L1 | L4 | L7 | AREQ | BREQ | MDV, /* multiply */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* 20: illegal */ + L1 | L4 | L7 | BREQ | NOWM, /* clear storage */ + L1 | L4 | L7 | AREQ | BREQ, /* subtract */ + 0, /* illegal */ + L5 | IO, /* magtape */ + L1 | L8 | BREQ, /* branch wm or zone */ + L1 | L8 | BREQ | BBE, /* branch if bit eq */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* 30: move zones */ + L1 | L4 | L7 | AREQ | BREQ, /* move supress zero */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ | NOWM, /* set word mark */ + L1 | L4 | L7 | AREQ | BREQ | MDV, /* divide */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* 40: illegal */ + 0, /* illegal */ + L2 | L5, /* select stacker */ + L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* load */ + L1 | L4 | L7 | L8 | BREQ | MLS | IO, /* move */ + HNOP | L1 | L2 | L4 | L5 | L7 | L8, /* nop */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ | MR, /* move to record */ + L1 | L4 | AREQ | MLS, /* 50: store A addr */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* zero and subtract */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* illegal */ + 0, /* 60: illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* add */ + L1 | L4 | L5 | L8, /* branch */ + L1 | L4 | L7 | AREQ | BREQ, /* compare */ + L1 | L4 | L7 | AREQ | BREQ, /* move numeric */ + L1 | L4 | L7 | AREQ | BREQ, /* move char edit */ + L2 | L5, /* carriage control */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | MLS, /* 70: store B addr */ + 0, /* illegal */ + L1 | L4 | L7 | AREQ | BREQ, /* zero and add */ + HNOP | L1 | L2 | L4 | L5 | L7 | L8, /* halt */ + L1 | L4 | L7 | AREQ | BREQ, /* clear word mark */ + 0, /* illegal */ + 0, /* illegal */ + 0 /* illegal */ + }; + +const int32 len_table[9] = { 0, L1, L2, 0, L4, L5, 0, L7, L8 }; + +/* Address character conversion tables. Illegal characters are marked by + the flag BA but also contain the post-adder value for indexing */ + +const int32 hun_table[64] = { + BA+000, 100, 200, 300, 400, 500, 600, 700, + 800, 900, 000, BA+300, BA+400, BA+500, BA+600, BA+700, + BA+1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, + 1800, 1900, 1000, BA+1300, BA+1400, BA+1500, BA+1600, BA+1700, + BA+2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, + 2800, 2900, 2000, BA+2300, BA+2400, BA+2500, BA+2600, BA+2700, + BA+3000, 3100, 3200, 3300, 3400, 3500, 3600, 3700, + 3800, 3900, 3000, BA+3300, BA+3400, BA+3500, BA+3600, BA+3700 + }; + +const int32 ten_table[64] = { + BA+00, 10, 20, 30, 40, 50, 60, 70, + 80, 90, 00, BA+30, BA+40, BA+50, BA+60, BA+70, + X1+00, X1+10, X1+20, X1+30, X1+40, X1+50, X1+60, X1+70, + X1+80, X1+90, X1+00, X1+30, X1+40, X1+50, X1+60, X1+70, + X2+00, X2+10, X2+20, X2+30, X2+40, X2+50, X2+60, X2+70, + X2+80, X2+90, X2+00, X2+30, X2+40, X2+50, X2+60, X2+70, + X3+00, X3+10, X3+20, X3+30, X3+40, X3+50, X3+60, X3+70, + X3+80, X3+90, X3+00, X3+30, X3+40, X3+50, X3+60, X3+70 + }; + +const int32 one_table[64] = { + BA+0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 0, BA+3, BA+4, BA+5, BA+6, BA+7, + BA+4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, + 4008, 4009, 4000, BA+4003, BA+4004, BA+4005, BA+4006, BA+4007, + BA+8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, + 8008, 8009, 8000, BA+8003, BA+8004, BA+8005, BA+8006, BA+8007, + BA+12000, 12001, 12002, 12003, 12004, 12005, 12006, 12007, + 12008, 12009, 12000, BA+12003, BA+12004, BA+12005, BA+12006, BA+12007 + }; + +const int32 bin_to_bcd[16] = { + 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + }; + +const int32 bcd_to_bin[16] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 3, 4, 5, 6, 7 + }; + +/* Indicator resets - a 1 marks an indicator that resets when tested */ + +static const int32 ind_table[64] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 00 - 07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 10 - 17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 1, 1, 0, 1, 0, 0, 0, /* 30 - 37 */ + 0, 0, 1, 0, 0, 0, 0, 0, /* 40 - 47 */ + 0, 0, 1, 0, 1, 0, 0, 0, /* 50 - 57 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 60 - 67 */ + 0, 0, 1, 0, 0, 0, 0, 0 /* 70 - 77 */ + }; + +/* Character collation table for compare with HLE option */ + +static const int32 col_table[64] = { + 000, 067, 070, 071, 072, 073, 074, 075, + 076, 077, 066, 024, 025, 026, 027, 030, + 023, 015, 056, 057, 060, 061, 062, 063, + 064, 065, 055, 016, 017, 020, 021, 022, + 014, 044, 045, 046, 047, 050, 051, 052, + 053, 054, 043, 007, 010, 011, 012, 013, + 006, 032, 033, 034, 035, 036, 037, 040, + 041, 042, 031, 001, 002, 003, 004, 005 + }; + +/* Summing table for two decimal digits, converted back to BCD + Also used for multiplying two decimal digits, converted back to BCD, + with carry forward +*/ + +static const int32 sum_table[100] = { + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE, + BCD_ZERO, BCD_ONE, BCD_TWO, BCD_THREE, BCD_FOUR, + BCD_FIVE, BCD_SIX, BCD_SEVEN, BCD_EIGHT, BCD_NINE + }; + +static const int32 cry_table[100] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 + }; + +/* Legal modifier tables */ + +static const int32 r_mod[] = { BCD_C, -1 }; +static const int32 p_mod[] = { BCD_C, -1 }; +static const int32 w_mod[] = { BCD_S, BCD_SQUARE, -1 }; +static const int32 ss_mod[] = { BCD_ONE, BCD_TWO, BCD_FOUR, BCD_EIGHT, + BCD_DOLLAR, BCD_DECIMAL, BCD_SQUARE, -1 }; +static const int32 mtf_mod[] = { BCD_B, BCD_E, BCD_M, BCD_R, BCD_U, -1 }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +int32 IS, ilnt, flags; +int32 op, xa, t, wm, ioind, dev, unit; +int32 a, b, i, k, asave, bsave; +int32 carry, lowprd, sign, ps; +int32 quo, ahigh, qs; +int32 qzero, qawm, qbody, qsign, qdollar, qaster, qdecimal; +t_stat reason, r1, r2; + +/* Restore saved state */ + +IS = saved_IS; +if (as_err) AS = AS | BA; /* flag bad addresses */ +if (bs_err) BS = BS | BA; +as_err = bs_err = 0; /* reset error flags */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + if (hb_pend) { /* halt br pending? */ + hb_pend = 0; /* clear flag */ + BRANCH; /* execute branch */ + } + + saved_IS = IS; /* commit prev instr */ + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + + if (sim_brk_summ && sim_brk_test (IS, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + sim_interval = sim_interval - 1; + +/* Instruction fetch - 1401 fetch works as follows: + + - Each character fetched enters the B register. This register is not + visible; the variable t represents the B register. + - Except for the first and last cycles, each character fetched enters + the A register. This register is not visible; the variable D represents + the A register, because this is the instruction modifier for 2, 5, and 8 + character instructions. + - At the start of the second cycle (first address character), the A-address + register and, for most instructions, the B-address register, are cleared + to blanks. The simulator represents addresses in binary and creates the + effect of blanks (address is bad) if less than three A-address characters + are found. Further, the simulator accumulates only the A-address, and + replicates it to the B-address at the appropriate point. + - At the start of the fifth cycle (fourth address character), the B-address + register is cleared to blanks. Again, the simulator creates the effect of + blanks (address is bad) if less than three B-address characters are found. + + The 1401 does not explicitly check for valid instruction lengths. Most 2, + 3, 5, 6 character instructions will be invalid because the A-address or + B-address (or both) are invalid. +*/ + + if ((M[IS] & WM) == 0) { /* I-Op: WM under op? */ + reason = STOP_NOWM; /* no, error */ + break; + } + op = M[IS] & CHAR; /* get opcode */ + flags = op_table[op]; /* get op flags */ + if ((flags == 0) || (flags & ALLOPT & ~cpu_unit.flags)) { + reason = STOP_NXI; /* illegal inst? */ + break; + } + if (op == OP_SAR) BS = AS; /* SAR? save ASTAR */ + PP (IS); + + if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* I-1: WM? 1 char inst */ + D = ioind = t; /* could be D char, % */ + AS = hun_table[t]; /* could be A addr */ + PP (IS); /* if %xy, BA is set */ + + if ((t = M[IS]) & WM) { /* I-2: WM? 2 char inst */ + AS = AS | BA; /* ASTAR bad */ + if (!(flags & MLS)) BS = AS; + goto CHECK_LENGTH; + } + D = dev = t; /* could be D char, dev */ + AS = AS + ten_table[t]; /* build A addr */ + PP (IS); + + if ((t = M[IS]) & WM) { /* I-3: WM? 3 char inst */ + AS = AS | BA; /* ASTAR bad */ + if (!(flags & MLS)) BS = AS; + goto CHECK_LENGTH; + } + D = unit = t; /* could be D char, unit */ + if (unit == BCD_ZERO) unit = 0; /* convert unit to binary */ + AS = AS + one_table[t]; /* finish A addr */ + xa = (AS >> V_INDEX) & M_INDEX; /* get index reg */ + if (xa && (ioind != BCD_PERCNT) && (cpu_unit.flags & XSA)) { /* indexed? */ + AS = AS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] + + one_table[M[xa + 2] & CHAR]; + AS = (AS & INDEXMASK) % MAXMEMSIZE; + } + if (!(flags & MLS)) BS = AS; /* not MLS? B = A */ + PP (IS); + + if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* I-4: WM? 4 char inst */ + if ((op == OP_B) && (t == BCD_BLANK)) /* BR + space? */ + goto CHECK_LENGTH; + D = t; /* could be D char */ + BS = hun_table[t]; /* could be B addr */ + PP (IS); + + if ((t = M[IS]) & WM) { /* I-5: WM? 5 char inst */ + BS = BS | BA; /* BSTAR bad */ + goto CHECK_LENGTH; + } + D = t; /* could be D char */ + BS = BS + ten_table[t]; /* build B addr */ + PP (IS); + + if ((t = M[IS]) & WM) { /* I-6: WM? 6 char inst */ + BS = BS | BA; /* BSTAR bad */ + goto CHECK_LENGTH; + } + D = t; /* could be D char */ + BS = BS + one_table[t]; /* finish B addr */ + xa = (BS >> V_INDEX) & M_INDEX; /* get index reg */ + if (xa && (cpu_unit.flags & XSA)) { /* indexed? */ + BS = BS + hun_table[M[xa] & CHAR] + ten_table[M[xa + 1] & CHAR] + + one_table[M[xa + 2] & CHAR]; + BS = (BS & INDEXMASK) % MAXMEMSIZE; + } + PP (IS); + + if (flags & NOWM) goto CHECK_LENGTH; /* I-7: SWM? done */ + if ((t = M[IS]) & WM) goto CHECK_LENGTH; /* WM? 7 char inst */ + D = t; /* last char is D */ + while (((t = M[IS]) & WM) == 0) { /* I-8: repeats until WM */ + D = t; /* last char is D */ + PP (IS); + } + if (reason) break; /* addr err on last? */ + +CHECK_LENGTH: + if ((flags & BREQ) && ADDR_ERR (BS)) { /* valid B? */ + reason = STOP_INVB; + break; + } + if ((flags & AREQ) && ADDR_ERR (AS)) { /* valid A? */ + reason = STOP_INVA; + break; + } + ilnt = IS - saved_IS; /* get lnt */ + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) hst_p = 0; + hst[hst_p].is = saved_IS; /* save IS */ + hst[hst_p].ilnt = ilnt; + for (i = 0; (i < MAX_L) && (i < ilnt); i++) + hst[hst_p].inst[i] = M[saved_IS + i]; + } + if (DEBUG_PRS (cpu_dev)) { + fprint_val (sim_deb, saved_IS, 10, 5, PV_RSPC); + fprintf (sim_deb, ": " ); + for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; + for (i = 0, k = saved_IS; i < sim_emax; i++, k++) { + if (cpu_ex (&sim_eval[i], k, &cpu_unit, 0) != SCPE_OK) break; + } + fprint_sym (sim_deb, saved_IS, sim_eval, &cpu_unit, SWMASK('M')); + fprintf (sim_deb, "\n" ); + } + switch (op) { /* case on opcode */ + +/* Move/load character instructions A check B check + + MCW copy A to B, preserving B WM, here fetch + until either A or B WM + LCA copy A to B, overwriting B WM, here fetch + until A WM + + Instruction lengths: + + 1 chained A and B + 2,3 invalid A-address + 4 chained B address + 5,6 invalid B-address + 7 normal + 8+ normal + modifier +*/ + + case OP_MCW: /* move char */ + if ((ilnt >= 4) && (ioind == BCD_PERCNT)) { /* I/O form? */ + reason = iodisp (dev, unit, MD_NORM, D); /* dispatch I/O */ + break; + } + if (ADDR_ERR (AS)) { /* check A addr */ + reason = STOP_INVA; + break; + } + do { + wm = M[AS] | M[BS]; + M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */ + MM (AS); /* decr pointers */ + MM (BS); + } while ((wm & WM) == 0); /* stop on A,B WM */ + break; + + case OP_LCA: /* load char */ + if ((ilnt >= 4) && (ioind == BCD_PERCNT)) { /* I/O form? */ + reason = iodisp (dev, unit, MD_WM, D); + break; + } + if (ADDR_ERR (AS)) { /* check A addr */ + reason = STOP_INVA; + break; + } + do { + wm = M[BS] = M[AS]; /* move char + wmark */ + MM (AS); /* decr pointers */ + MM (BS); + } while ((wm & WM) == 0); /* stop on A WM */ + break; + +/* Other move instructions A check B check + + MCM copy A to B, preserving B WM, fetch fetch + until record or group mark + MCS copy A to B, clearing B WM, until A WM; fetch fetch + reverse scan and suppress leading zeroes + MN copy A char digit to B char digit, fetch fetch + preserving B zone and WM + MZ copy A char zone to B char zone, fetch fetch + preserving B digit and WM + + Instruction lengths: + + 1 chained + 2,3 invalid A-address + 4 self (B-address = A-address) + 5,6 invalid B-address + 7 normal + 8+ normal + ignored modifier +*/ + + case OP_MCM: /* move to rec/group */ + do { + t = M[AS]; + M[BS] = (M[BS] & WM) | (M[AS] & CHAR); /* move char */ + PP (AS); /* incr pointers */ + PP (BS); + } while (((t & CHAR) != BCD_RECMRK) && (t != (BCD_GRPMRK + WM))); + break; + + case OP_MCS: /* move suppress zero */ + bsave = BS; /* save B start */ + qzero = 1; /* set suppress */ + do { + wm = M[AS]; + M[BS] = M[AS] & ((BS != bsave)? CHAR: DIGIT);/* copy char */ + MM (AS); MM (BS); /* decr pointers */ + } while ((wm & WM) == 0); /* stop on A WM */ + if (reason) break; /* addr err? stop */ + do { + PP (BS); /* adv B */ + t = M[BS]; /* get B, cant be WM */ + if ((t == BCD_ZERO) || (t == BCD_COMMA)) { + if (qzero) M[BS] = 0; + } + else if ((t == BCD_BLANK) || (t == BCD_MINUS)) ; + else if (((t == BCD_DECIMAL) && (cpu_unit.flags & EPE)) || + (t <= BCD_NINE)) qzero = 0; + else qzero = 1; + } while (BS < bsave); + PP (BS); /* BS end is B+1 */ + break; + + case OP_MN: /* move numeric */ + M[BS] = (M[BS] & ~DIGIT) | (M[AS] & DIGIT); /* move digit */ + MM (AS); /* decr pointers */ + MM (BS); + break; + + case OP_MZ: /* move zone */ + M[BS] = (M[BS] & ~ZONE) | (M[AS] & ZONE); /* move high bits */ + MM (AS); /* decr pointers */ + MM (BS); + break; + +/* Branch instruction A check B check + + Instruction lengths: + + 1 branch if B char equals d, chained if branch here + 2,3 invalid B-address if branch here + 4 unconditional branch if branch + 5 branch if indicator[d] is set if branch + 6 invalid B-address if branch here + 7 branch if B char equals d, if branch here + d is last character of B-address + 8 branch if B char equals d if branch here +*/ + + case OP_B: /* branch */ + if (ilnt == 4) { BRANCH; } /* uncond branch? */ + else if (ilnt == 5) { /* branch on ind? */ + if (ind[D]) { BRANCH; } /* test indicator */ + if (ind_table[D]) ind[D] = 0; /* reset if needed */ + } + else { /* branch char eq */ + if (ADDR_ERR (BS)) { /* validate B addr */ + reason = STOP_INVB; + break; + } + if ((M[BS] & CHAR) == D) { BRANCH; } /* char equal? */ + else { MM (BS); } + } + break; + +/* Other branch instructions A check B check + + BWZ branch if (d<0>: B char WM) if branch fetch + (d<1>: B char zone = d zone) + BBE branch if B char & d non-zero if branch fetch + + Instruction lengths: + 1 chained + 2,3 invalid A-address and B-address + 4 self (B-address = A-address, d = last character of A-address) + 5,6 invalid B-address + 7 normal, d = last character of B-address + 8+ normal +*/ + + case OP_BWZ: /* branch wm or zone */ + if (((D & 1) && (M[BS] & WM)) || /* d1? test wm */ + ((D & 2) && ((M[BS] & ZONE) == (D & ZONE)))) /* d2? test zone */ + { BRANCH; } + else { MM (BS); } /* decr pointer */ + break; + + case OP_BBE: /* branch if bit eq */ + if (M[BS] & D & CHAR) { BRANCH; } /* any bits set? */ + else { MM (BS); } /* decr pointer */ + break; + +/* Arithmetic instructions A check B check + + ZA move A to B, normalizing A sign, fetch fetch + preserving B WM, until B WM + ZS move A to B, complementing A sign, fetch fetch + preserving B WM, until B WM + A add A to B fetch fetch + S subtract A from B fetch fetch + C compare A to B fetch fetch + + Instruction lengths: + + 1 chained + 2,3 invalid A-address + 4 self (B-address = A-address) + 5,6 invalid B-address + 7 normal + 8+ normal + ignored modifier +*/ + + case OP_ZA: case OP_ZS: /* zero and add/sub */ + a = i = 0; /* clear flags */ + do { + if (a & WM) wm = M[BS] = (M[BS] & WM) | BCD_ZERO; + else { + a = M[AS]; /* get A char */ + t = (a & CHAR)? bin_to_bcd[a & DIGIT]: 0; + wm = M[BS] = (M[BS] & WM) | t; /* move digit */ + MM (AS); + } + if (i == 0) i = M[BS] = M[BS] | + ((((a & ZONE) == BBIT) ^ (op == OP_ZS))? BBIT: ZONE); + MM (BS); + } while ((wm & WM) == 0); /* stop on B WM */ + break; + + case OP_A: case OP_S: /* add/sub */ + bsave = BS; /* save sign pos */ + a = M[AS]; /* get A digit/sign */ + b = M[BS]; /* get B digit/sign */ + MM (AS); + qsign = ((a & ZONE) == BBIT) ^ ((b & ZONE) == BBIT) ^ (op == OP_S); + t = bcd_to_bin[a & DIGIT]; /* get A binary */ + t = bcd_to_bin[b & DIGIT] + (qsign? 10 - t: t); /* sum A + B */ + carry = (t >= 10); /* get carry */ + b = (b & ~DIGIT) | sum_table[t]; /* get result */ + if (qsign && ((b & BBIT) == 0)) b = b | ZONE; /* normalize sign */ + M[BS] = b; /* store result */ + MM (BS); + if (b & WM) { /* b wm? done */ + if (qsign && (carry == 0)) M[bsave] = /* compl, no carry? */ + WM + ((b & ZONE) ^ ABIT) + sum_table[10 - t]; + break; + } + do { + if (a & WM) a = WM; /* A WM? char = 0 */ + else { + a = M[AS]; /* else get A */ + MM (AS); + } + b = M[BS]; /* get B */ + t = bcd_to_bin[a & DIGIT]; /* get A binary */ + t = bcd_to_bin[b & DIGIT] + (qsign? 9 - t: t) + carry; + carry = (t >= 10); /* get carry */ + if ((b & WM) && (qsign == 0)) { /* last, no recomp? */ + M[BS] = WM + sum_table[t] + /* zone add */ + (((a & ZONE) + b + (carry? ABIT: 0)) & ZONE); + ind[IN_OVF] = carry; /* ovflo if carry */ + } + else M[BS] = (b & WM) + sum_table[t]; /* normal add */ + MM (BS); + } while ((b & WM) == 0); /* stop on B WM */ + if (reason) break; /* address err? */ + if (qsign && (carry == 0)) { /* recompl, no carry? */ + M[bsave] = M[bsave] ^ ABIT; /* XOR sign */ + for (carry = 1; bsave != BS; --bsave) { /* rescan */ + t = 9 - bcd_to_bin[M[bsave] & DIGIT] + carry; + carry = (t >= 10); + M[bsave] = (M[bsave] & ~DIGIT) | sum_table[t]; + } + } + break; + + case OP_C: /* compare */ + if (ilnt != 1) { /* if not chained */ + ind[IN_EQU] = 1; /* clear indicators */ + ind[IN_UNQ] = ind[IN_HGH] = ind[IN_LOW] = 0; + } + do { + a = M[AS]; /* get characters */ + b = M[BS]; + wm = a | b; /* get word marks */ + if ((a & CHAR) != (b & CHAR)) { /* unequal? */ + ind[IN_EQU] = 0; /* set indicators */ + ind[IN_UNQ] = 1; + ind[IN_HGH] = col_table[b & CHAR] > col_table [a & CHAR]; + ind[IN_LOW] = ind[IN_HGH] ^ 1; + } + MM (AS); MM (BS); /* decr pointers */ + } while ((wm & WM) == 0); /* stop on A, B WM */ + if ((a & WM) && !(b & WM)) { /* short A field? */ + ind[IN_EQU] = ind[IN_LOW] = 0; + ind[IN_UNQ] = ind[IN_HGH] = 1; + } + if (!(cpu_unit.flags & HLE)) /* no HLE? */ + ind[IN_EQU] = ind[IN_LOW] = ind[IN_HGH] = 0; + break; + +/* I/O instructions A check B check + + R read a card if branch + W write to line printer if branch + WR write and read if branch + P punch a card if branch + RP read and punch if branch + WP : write and punch if branch + WRP write read and punch if branch + RF read feed (nop) + PF punch feed (nop) + SS select stacker if branch + CC carriage control if branch + + Instruction lengths: + + 1 normal + 2,3 normal, with modifier + 4 branch; modifier, if any, is last character of branch address + 5 branch + modifier + 6+ normal, with modifier +*/ + + case OP_R: /* read */ + if (reason = iomod (ilnt, D, r_mod)) break; /* valid modifier? */ + reason = read_card (ilnt, D); /* read card */ + BS = CDR_BUF + CDR_WIDTH; + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + break; + + case OP_W: /* write */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + BS = LPT_BUF + LPT_WIDTH; + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + break; + + case OP_P: /* punch */ + if (reason = iomod (ilnt, D, p_mod)) break; /* valid modifier? */ + reason = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + break; + + case OP_WR: /* write and read */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + r1 = read_card (ilnt, D); /* read card */ + BS = CDR_BUF + CDR_WIDTH; + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = r1; /* merge errors */ + break; + + case OP_WP: /* write and punch */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + r1 = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = r1; /* merge errors */ + break; + + case OP_RP: /* read and punch */ + if (reason = iomod (ilnt, D, NULL)) break; /* valid modifier? */ + reason = read_card (ilnt, D); /* read card */ + r1 = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = r1; /* merge errors */ + break; + + case OP_WRP: /* write, read, punch */ + if (reason = iomod (ilnt, D, w_mod)) break; /* valid modifier? */ + reason = write_line (ilnt, D); /* print line */ + r1 = read_card (ilnt, D); /* read card */ + r2 = punch_card (ilnt, D); /* punch card */ + BS = CDP_BUF + CDP_WIDTH; + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + if (reason == SCPE_OK) reason = (r1 == SCPE_OK)? r2: r1; + break; + + case OP_SS: /* select stacker */ + if (reason = iomod (ilnt, D, ss_mod)) break; /* valid modifier? */ + if (reason = select_stack (D)) break; /* sel stack, error? */ + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + break; + + case OP_CC: /* carriage control */ + if (reason = carriage_control (D)) break; /* car ctrl, error? */ + if ((ilnt == 4) || (ilnt == 5)) { BRANCH; } /* check for branch */ + break; + +/* MTF - magtape functions - must be at least 4 characters + + Instruction lengths: + + 1-3 invalid I/O address + 4 normal, d-character is unit + 5 normal + 6+ normal, d-character is last character +*/ + + case OP_MTF: /* magtape function */ + if (ilnt < 4) reason = STOP_INVL; /* too short? */ + else if (ioind != BCD_PERCNT) reason = STOP_INVA; + else if (reason = iomod (ilnt, D, mtf_mod)) break; /* valid modifier? */ + reason = mt_func (unit, D); /* mt func, error? */ + break; /* can't branch */ + + case OP_RF: case OP_PF: /* read, punch feed */ + break; /* nop's */ + +/* Move character and edit + + Control flags + qsign sign of A field (0 = +, 1 = minus) + qawm A field WM seen and processed + qzero zero suppression enabled + qbody in body (copying A field characters) + qdollar EPE only; $ seen in body + qaster EPE only; * seen in body + qdecimal EPE only; . seen on first rescan + + MCE operates in one to three scans, the first of which has three phases + + 1 right to left qbody = 0, qawm = 0 => right status + qbody = 1, qawm = 0 => body + qbody = 0, qawm = 1 => left status + 2 left to right + 3 right to left, extended print end only + + The first A field character is masked to its digit part, all others + are copied intact + + Instruction lengths: + + 1 chained + 2,3 invalid A-address + 4 self (B-address = A-address) + 5,6 invalid B-address + 7 normal + 8+ normal + ignored modifier +*/ + + case OP_MCE: /* edit */ + a = M[AS]; /* get A char */ + b = M[BS]; /* get B char */ + t = a & DIGIT; /* get A digit */ + MM (AS); + qsign = ((a & ZONE) == BBIT); /* get A field sign */ + qawm = qzero = qbody = 0; /* clear other flags */ + qdollar = qaster = qdecimal = 0; /* clear EPE flags */ + +/* Edit pass 1 - from right to left, under B field control + + * in status or !epe, skip B; else, set qaster, repl with A + $ in status or !epe, skip B; else, set qdollar, repl with A + 0 in right status or body, if !qzero, set A WM; set qzero, repl with A + else, if !qzero, skip B; else, if (!B WM) set B WM + blank in right status or body, repl with A; else, skip B + C,R,- in status, blank B; else, skip B + , in status, blank B, else, skip B + & blank B +*/ + + do { + b = M[BS]; /* get B char */ + M[BS] = M[BS] & ~WM; /* clr WM */ + switch (b & CHAR) { /* case on B char */ + + case BCD_ASTER: /* * */ + if (!qbody || qdollar || !(cpu_unit.flags & EPE)) break; + qaster = 1; /* flag */ + goto A_CYCLE; /* take A cycle */ + + case BCD_DOLLAR: /* $ */ + if (!qbody || qaster || !(cpu_unit.flags & EPE)) break; + qdollar = 1; /* flag */ + goto A_CYCLE; /* take A cycle */ + + case BCD_ZERO: /* 0 */ + if (qawm) { /* left status? */ + if (!qzero) M[BS] = M[BS] | WM; /* first? set WM */ + qzero = 1; /* flag suppress */ + break; + } + if (!qzero) t = t | WM; /* body, first? WM */ + qzero = 1; /* flag suppress */ + goto A_CYCLE; /* take A cycle */ + + case BCD_BLANK: /* blank */ + if (qawm) break; /* left status? */ + A_CYCLE: + M[BS] = t; /* copy char */ + if (a & WM) { /* end of A field? */ + qbody = 0; /* end body */ + qawm = 1; /* start left status */ + } + else { + qbody = 1; /* in body */ + a = M[AS]; /* next A */ + MM (AS); + t = a & CHAR; /* use A char */ + } + break; + + case BCD_C: case BCD_R: case BCD_MINUS: /* C, R, - */ + if (!qsign && !qbody) M[BS] = BCD_BLANK; /* + & status? blank */ + break; + + case BCD_COMMA: /* , */ + if (!qbody) M[BS] = BCD_BLANK; /* status? blank */ + break; + + case BCD_AMPER: /* & */ + M[BS] = BCD_BLANK; /* blank */ + break; + } /* end switch */ + + MM (BS); /* decr B pointer */ + } while ((b & WM) == 0); /* stop on B WM */ + + if (reason) break; /* address err? */ + if (!qzero) break; /* rescan? */ + +/* Edit pass 2 - from left to right, suppressing zeroes */ + + do { + b = M[++BS]; /* get B char */ + switch (b & CHAR) { /* case on B char */ + + case BCD_ONE: case BCD_TWO: case BCD_THREE: + case BCD_FOUR: case BCD_FIVE: case BCD_SIX: + case BCD_SEVEN: case BCD_EIGHT: case BCD_NINE: + qzero = 0; /* turn off supr */ + break; + + case BCD_ZERO: case BCD_COMMA: /* 0 or , */ + if (qzero && !qdecimal) /* if supr, blank */ + M[BS] = qaster? BCD_ASTER: BCD_BLANK; + break; + + case BCD_BLANK: /* blank */ + if (qaster) M[BS] = BCD_ASTER; /* if EPE *, repl */ + break; + + case BCD_DECIMAL: /* . */ + if (qzero && (cpu_unit.flags & EPE)) /* flag for EPE */ + qdecimal = 1; + break; + + case BCD_PERCNT: case BCD_WM: case BCD_BS: + case BCD_TS: case BCD_MINUS: + break; /* ignore */ + + default: /* other */ + qzero = 1; /* restart supr */ + break; + } /* end case */ + } while ((b & WM) == 0); + + M[BS] = M[BS] & ~WM; /* clear B WM */ + if (!qdollar && !(qdecimal && qzero)) { /* rescan again? */ + BS++; /* BS = addr WM + 1 */ + break; + } + if (qdecimal && qzero) qdollar = 0; /* no digits? clr $ */ + +/* Edit pass 3 (extended print only) - from right to left */ + + for (;; ) { /* until chars */ + b = M[BS]; /* get B char */ + if ((b == BCD_BLANK) && qdollar) { /* blank & flt $? */ + M[BS] = BCD_DOLLAR; /* insert $ */ + break; /* exit for */ + } + if (b == BCD_DECIMAL) { /* decimal? */ + M[BS] = qaster? BCD_ASTER: BCD_BLANK; + break; /* exit for */ + } + if ((b == BCD_ZERO) && !qdollar) /* 0 & ~flt $ */ + M[BS] = qaster? BCD_ASTER: BCD_BLANK; + BS--; + } /* end for */ + break; /* done at last! */ + +/* Multiply. Comments from the PDP-10 based simulator by Len Fehskens. + + Multiply, with variable length operands, is necessarily done the same + way you do it with paper and pencil, except that partial products are + added into the incomplete final product as they are computed, rather + than at the end. The 1401 multiplier format allows the product to + be developed in place, without scratch storage. + + The A field contains the multiplicand, length LD. The B field must be + LD + 1 + length of multiplier. Locate the low order multiplier digit, + and at the same time zero out the product field. Then compute the sign + of the result. + + Instruction lengths: + + 1 chained + 2,3 invalid A-address + 4 self (B-address = A-address) + 5,6 invalid B-address + 7 normal + 8+ normal + ignored modifier +*/ + + case OP_MUL: + asave = AS; /* save AS, BS */ + bsave = lowprd = BS; + do { + a = M[AS]; /* get mpcd char */ + M[BS] = BCD_ZERO; /* zero prod */ + MM (AS); /* decr pointers */ + MM (BS); + } while ((a & WM) == 0); /* until A WM */ + if (reason) break; /* address err? */ + M[BS] = BCD_ZERO; /* zero hi prod */ + MM (BS); /* addr low mpyr */ + sign = ((M[asave] & ZONE) == BBIT) ^ ((M[BS] & ZONE) == BBIT); + +/* Outer loop on multiplier (BS) and product digits (ps), + inner loop on multiplicand digits (AS). + AS and ps cannot produce an address error. +*/ + + do { + ps = bsave; /* ptr to prod */ + AS = asave; /* ptr to mpcd */ + carry = 0; /* init carry */ + b = M[BS]; /* get mpyr char */ + do { + a = M[AS]; /* get mpcd char */ + t = (bcd_to_bin[a & DIGIT] * /* mpyr * mpcd */ + bcd_to_bin[b & DIGIT]) + /* + c + partial prod */ + carry + bcd_to_bin[M[ps] & DIGIT]; + carry = cry_table[t]; + M[ps] = (M[ps] & WM) | sum_table[t]; + MM (AS); + ps--; + } while ((a & WM) == 0); /* until mpcd done */ + M[BS] = (M[BS] & WM) | BCD_ZERO; /* zero mpyr just used */ + t = bcd_to_bin[M[ps] & DIGIT] + carry; /* add carry to prod */ + M[ps] = (M[ps] & WM) | sum_table[t]; /* store */ + bsave--; /* adv prod ptr */ + MM (BS); /* adv mpyr ptr */ + } while ((b & WM) == 0); /* until mpyr done */ + M[lowprd] = M[lowprd] | ZONE; /* assume + */ + if (sign) M[lowprd] = M[lowprd] & ~ABIT; /* if minus, B only */ + break; + +/* Divide. Comments from the PDP-10 based simulator by Len Fehskens. + + Divide is done, like multiply, pretty much the same way you do it with + pencil and paper; successive subtraction of the divisor from a substring + of the dividend while counting up the corresponding quotient digit. + + Let LS be the length of the divisor, LD the length of the dividend: + - AS points to the low order divisor digit. + - BS points to the high order dividend digit. + - The low order dividend digit is identified by sign (zone) bits. + - To the left of the dividend is a zero field of length LS + 1. + The low quotient is at low dividend - LS - 1. As BS points to the + high dividend, the low dividend is at BS + LD - 1, so the low + quotient is at BS + LD - LS - 2. The longest possible quotient is + LD - LS + 1, so the first possible non-zero quotient bit will be + found as BS - 2. + + This pointer calculation assumes that the divisor has no leading zeroes. + For each leading zero, the start of the quotient will be one position + further left. + + Start by locating the high order non-zero digit of the divisor. This + also tests for a divide by zero. + + Instruction lengths: + + 1 chained + 2,3 invalid A-address + 4 self (B-address = A-address) + 5,6 invalid B-address + 7 normal + 8+ normal + ignored modifier +*/ + + case OP_DIV: + asave = AS; + ahigh = -1; + do { + a = M[AS]; /* get dvr char */ + if ((a & CHAR) != BCD_ZERO) ahigh = AS; /* mark non-zero */ + MM (AS); + } + while ((a & WM) == 0); + if (reason) break; /* address err? */ + if (ahigh < 0) { /* div by zero? */ + ind[IN_OVF] = 1; /* set ovf indic */ + qs = bsave = BS; /* quo, dividend */ + do { + b = M[bsave]; /* find end divd */ + PP (bsave); /* marked by zone */ + } while ((b & ZONE) == 0); + if (reason) break; /* address err? */ + if (ADDR_ERR (qs)) { /* address err? */ + reason = STOP_WRAP; /* address wrap? */ + break; + } + div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */ + BS = (BS - 2) - (asave - (AS + 1)); /* final bs */ + break; + } + bsave = BS + (asave - ahigh); /* end subdivd */ + qs = (BS - 2) - (ahigh - (AS + 1)); /* quo start */ + +/* Divide loop - done with subroutines to keep the code clean. + In the loop, + + asave = low order divisor + bsave = low order subdividend + qs = current quotient digit +*/ + + do { + quo = 0; /* clear quo digit */ + if (ADDR_ERR (qs) || ADDR_ERR (bsave)) { + reason = STOP_WRAP; /* address wrap? */ + break; + } + b = M[bsave]; /* save low divd */ + do { + t = div_sub (asave, bsave, ahigh); /* subtract */ + quo++; /* incr quo digit */ + } while (t == 0); /* until borrow */ + div_add (asave, bsave, ahigh); /* restore */ + quo--; + M[qs] = (M[qs] & WM) | sum_table[quo]; /* store quo digit */ + bsave++; /* adv divd, quo */ + qs++; + } while ((b & ZONE) == 0); /* until B sign */ + if (reason) break; /* address err? */ + +/* At this point, + + AS = high order divisor - 1 + asave = unit position of divisor + b = unit character of dividend + bsave = unit position of remainder + 1 + qs = unit position of quotient + 1 +*/ + + div_sign (M[asave], b, qs - 1, bsave - 1); /* set signs */ + BS = qs - 2; /* BS = quo 10's pos */ + break; + +/* Word mark instructions A check B check + + SWM set WM on A char and B char fetch fetch + CWM clear WM on A char and B char fetch fetch + + Instruction lengths: + + 1 chained + 2,3 invalid A-address + 4 one operand (B-address = A-address) + 5,6 invalid B-address + 7 two operands (SWM cannot be longer than 7) + 8+ two operands + ignored modifier +*/ + + case OP_SWM: /* set word mark */ + M[BS] = M[BS] | WM; /* set A field mark */ + M[AS] = M[AS] | WM; /* set B field mark */ + MM (AS); /* decr pointers */ + MM (BS); + break; + + case OP_CWM: /* clear word mark */ + M[BS] = M[BS] & ~WM; /* clear A field mark */ + M[AS] = M[AS] & ~WM; /* clear B field mark */ + MM (AS); /* decr pointers */ + MM (BS); + break; + +/* Clear storage instruction A check B check + + CS clear from B down to nearest hundreds if branch fetch + address + + Instruction lengths: + + 1 chained + 2,3 invalid A-address and B-address + 4 one operand (B-address = A-address) + 5,6 invalid B-address + 7 branch + 8+ one operand, branch ignored +*/ + + case OP_CS: /* clear storage */ + t = (BS / 100) * 100; /* lower bound */ + while (BS >= t) M[BS--] = 0; /* clear region */ + if (BS < 0) BS = BS + MEMSIZE; /* wrap if needed */ + if (ilnt == 7) { BRANCH; } /* branch variant? */ + break; + +/* Modify address instruction A check B check + + MA add A addr and B addr, store at B addr fetch fetch + + Instruction lengths: + 1 chained + 2,3 invalid A-address and B-address + 4 self (B-address = A-address) + 5,6 invalid B-address + 7 normal + 8+ normal + ignored modifier +*/ + + case OP_MA: /* modify address */ + a = one_table[M[AS] & CHAR]; MM (AS); /* get A address */ + a = a + ten_table[M[AS] & CHAR]; MM (AS); + a = a + hun_table[M[AS] & CHAR]; MM (AS); + b = one_table[M[BS] & CHAR]; MM (BS); /* get B address */ + b = b + ten_table[M[BS] & CHAR]; MM (BS); + b = b + hun_table[M[BS] & CHAR]; MM (BS); + t = ((a + b) & INDEXMASK) % MAXMEMSIZE; /* compute sum */ + M[BS + 3] = (M[BS + 3] & WM) | store_addr_u (t); + M[BS + 2] = (M[BS + 2] & (WM + ZONE)) | store_addr_t (t); + M[BS + 1] = (M[BS + 1] & WM) | store_addr_h (t); + if (((a % 4000) + (b % 4000)) >= 4000) BS = BS + 2; /* carry? */ + break; + +/* Store address instructions A-check B-check + + SAR store A* at A addr fetch + SBR store B* at A addr fetch + + Instruction lengths: + 1 chained + 2,3 invalid A-address + 4 normal + 5+ B-address overwritten from instruction; + invalid address ignored +*/ + + case OP_SAR: case OP_SBR: /* store A, B reg */ + M[AS] = (M[AS] & WM) | store_addr_u (BS); + MM (AS); + M[AS] = (M[AS] & WM) | store_addr_t (BS); + MM (AS); + M[AS] = (M[AS] & WM) | store_addr_h (BS); + MM (AS); + break; + +/* NOP - no validity checking, all instructions length ok */ + + case OP_NOP: /* nop */ + break; + +/* HALT - unless length = 4 (branch), no validity checking; all lengths ok */ + + case OP_H: /* halt */ + if (ilnt == 4) hb_pend = 1; /* set pending branch */ + reason = STOP_HALT; /* stop simulator */ + saved_IS = IS; /* commit instruction */ + break; + + default: + reason = STOP_NXI; /* unimplemented */ + break; + } /* end switch */ + } /* end while */ + +/* Simulation halted */ + +as_err = ADDR_ERR (AS); /* get addr err flags */ +bs_err = ADDR_ERR (BS); +AS = AS & ADDRMASK; /* clean addresses */ +BS = BS & ADDRMASK; +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} /* end sim_instr */ + +/* store addr_x - convert address to BCD character in x position + + Inputs: + addr = address to convert + Outputs: + char = converted address character +*/ + +int32 store_addr_h (int32 addr) +{ +int32 thous; + +thous = (addr / 1000) & 03; +return bin_to_bcd[(addr % 1000) / 100] | (thous << V_ZONE); +} + +int32 store_addr_t (int32 addr) +{ +return bin_to_bcd[(addr % 100) / 10]; +} + +int32 store_addr_u (int32 addr) +{ +int32 thous; + +thous = (addr / 1000) & 014; +return bin_to_bcd[addr % 10] | (thous << (V_ZONE - 2)); +} + +/* div_add - add string for divide */ + +int32 div_add (int32 ap, int32 bp, int32 aend) +{ +int32 a, b, c, r; + +c = 0; /* init carry */ +do { + a = M[ap]; /* get operands */ + b = M[bp]; + r = bcd_to_bin[b & DIGIT] + /* sum digits + c */ + bcd_to_bin[a & DIGIT] + c; + c = (r >= 10); /* set carry out */ + M[bp] = sum_table[r]; /* store result */ + ap--; + bp--; + } while (ap >= aend); +return c; +} + +/* div_sub - substract string for divide */ + +int32 div_sub (int32 ap, int32 bp, int32 aend) +{ +int32 a, b, c, r; + +c = 0; /* init borrow */ +do { + a = M[ap]; /* get operands */ + b = M[bp]; + r = bcd_to_bin[b & DIGIT] - /* a - b - borrow */ + bcd_to_bin[a & DIGIT] - c; + c = (r < 0); /* set borrow out */ + M[bp] = sum_table[r + 10]; /* store result */ + ap--; + bp--; + } while (ap >= aend); +b = M[bp] & CHAR; /* borrow position */ +if (b && (b != BCD_ZERO)) { /* non-zero? */ + r = bcd_to_bin[b & DIGIT] - c; /* subtract borrow */ + M[bp] = sum_table[r]; /* store result */ + return 0; /* subtract worked */ + } +return c; /* return borrow */ +} + +/* div_sign - set signs for divide */ + +void div_sign (int32 dvrc, int32 dvdc, int32 qp, int32 rp) +{ +int32 sign = dvrc & ZONE; /* divisor sign */ + +M[rp] = M[rp] | ZONE; /* assume rem pos */ +if (sign == BBIT) M[rp] = M[rp] & ~ABIT; /* if dvr -, rem - */ +M[qp] = M[qp] | ZONE; /* assume quo + */ +if (((dvdc & ZONE) == BBIT) ^ (sign == BBIT)) /* dvr,dvd diff? */ + M[qp] = M[qp] & ~ABIT; /* make quo - */ +return; +} + +/* iomod - check on I/O modifiers + + Inputs: + ilnt = instruction length + mod = modifier character + tptr = pointer to table of modifiers, end is -1 + Output: + status = SCPE_OK if ok, STOP_INVM if invalid +*/ + +t_stat iomod (int32 ilnt, int32 mod, const int32 *tptr) +{ +if ((ilnt != 2) && (ilnt != 5) && (ilnt < 8)) return SCPE_OK; +if (tptr == NULL) return STOP_INVM; +do { + if (mod == *tptr++) return SCPE_OK; + } while (*tptr >= 0); +return STOP_INVM; +} + +/* iodisp - dispatch load or move to I/O routine + + Inputs: + dev = device number + unit = unit number + flag = move (MD_NORM) vs load (MD_WM) + mod = modifier +*/ + +t_stat iodisp (int32 dev, int32 unit, int32 flag, int32 mod) +{ +if (dev == IO_INQ) return inq_io (flag, mod); /* inq terminal? */ +if (dev == IO_DP) return dp_io (unit, flag, mod); /* disk pack? */ +if (dev == IO_MT) return mt_io (unit, flag, mod); /* magtape? */ +if (dev == IO_MTB) /* binary magtape? */ + return mt_io (unit, flag | MD_BIN, mod); +return STOP_NXD; /* not implemented */ +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < 64; i++) { /* clr indicators */ + if ((i < IN_SSB) || (i > IN_SSG)) ind[i] = 0; /* except SSB-SSG */ + } +ind[IN_UNC] = 1; /* ind[0] always on */ +AS = 0; /* clear AS */ +BS = 0; /* clear BS * +as_err = 1; +bs_err = 1;/ +D = 0; /* clear D */ +hb_pend = 0; /* no halt br */ +pcq_r = find_reg ("ISQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & (WM + CHAR); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & (WM + CHAR); +return SCPE_OK; +} + +/* Memory size change */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +if (MEMSIZE > 4000) cpu_unit.flags = cpu_unit.flags | MA; +else cpu_unit.flags = cpu_unit.flags & ~MA; +return SCPE_OK; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].ilnt = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, k, di, lnt; +char *cptr = (char *) desc; +t_value sim_eval[MAX_L + 1]; +t_stat r; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "IS IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->ilnt) { /* instruction? */ + fprintf (st, "%05d ", h->is); + for (i = 0; i < h->ilnt; i++) + sim_eval[i] = h->inst[i]; + sim_eval[h->ilnt] = WM; + if ((fprint_sym (st, h->is, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) { + fprintf (st, "(undefined)"); + for (i = 0; i < h->ilnt; i++) + fprintf (st, " %02o", h->inst[i]); + } + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} + +/* Set conversions */ + +t_stat cpu_set_conv (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +conv_old = val; +return SCPE_OK; +} + +/* Show conversions */ + +t_stat cpu_show_conv (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (conv_old) fputs ("Old (pre-3.5-1) conversions\n", st); +else fputs ("New conversions\n", st); +return SCPE_OK; +} diff --git a/I1401/i1401_dat.h b/I1401/i1401_dat.h new file mode 100644 index 0000000..53d8501 --- /dev/null +++ b/I1401/i1401_dat.h @@ -0,0 +1,145 @@ +/* i1401_dat.h: IBM 1401 character conversion tables + + Copyright (c) 1993-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 20-Sep-05 RMS Updated for compatibility with Paul Pierce conventions +*/ + +/* Old tables */ +/* ASCII to BCD conversion */ + +const char ascii_to_bcd_old[128] = { + 000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */ + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 052, 077, 013, 053, 034, 060, 032, /* 040 - 077 */ + 017, 074, 054, 037, 033, 040, 073, 021, + 012, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 015, 056, 076, 035, 016, 072, + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 075, 036, 055, 020, 057, + 000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 000, 000, 000, 000, 000 + }; + +/* BCD to ASCII conversion - also the "full" print chain */ + +char bcd_to_ascii_old[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '#', '@', ':', '>', '(', + '^', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '\'', ',', '%', '=', '\\', '+', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '"' + }; + +/* New tables */ +/* ASCII to BCD conversion */ + +const char ascii_to_bcd[128] = { + 000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */ + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 052, 037, 013, 053, 034, 060, 014, /* 040 - 077 */ + 034, 074, 054, 060, 033, 040, 073, 021, + 012, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 015, 056, 076, 013, 016, 072, + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 075, 036, 055, 020, 057, + 000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 017, 032, 077, 035, 000 + }; + +/* BCD to ASCII conversion */ + +const char bcd_to_ascii_a[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '#', '@', ':', '>', '{', + '^', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '|', ',', '%', '~', '\\', '"', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '}' + }; + +const char bcd_to_ascii_h[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ':', '>', '{', + '^', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '|', ',', '(', '~', '\\', '"', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '}' + }; + +/* BCD to ASCII 48 character print chains */ + +const char bcd_to_pca[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '#', '@', ' ', ' ', ' ', + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', ' ', ',', '%', ' ', ' ', ' ', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '&', '.', ')', ' ', ' ', ' ' + }; + +const char bcd_to_pch[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '0', '=', '\'', ' ', ' ', ' ', + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', ' ', ',', '(', ' ', ' ', ' ', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '&', '.', ')', ' ', ' ', ' ' + }; + +/* BCD to column binary conversion */ + +const uint32 bcd_to_colbin[64] = { + 00000, 00400, 00200, 00100, 00040, 00020, 00010, 00004, + 00002, 00001, 00202, 00102, 00042, 00022, 00012, 00006, + 01000, 01400, 01200, 01100, 01040, 01020, 01010, 01004, + 01002, 01001, 01202, 01102, 01042, 01022, 01012, 01006, + 02000, 02400, 02200, 02100, 02040, 02020, 02010, 02004, + 02002, 02001, 02202, 02102, 02042, 02022, 02012, 02006, + 04000, 04400, 04200, 04100, 04040, 04020, 04010, 04004, + 04002, 04001, 04202, 04102, 04042, 04022, 04012, 04006 + }; diff --git a/I1401/i1401_defs.h b/I1401/i1401_defs.h new file mode 100644 index 0000000..7e6b627 --- /dev/null +++ b/I1401/i1401_defs.h @@ -0,0 +1,293 @@ +/* i1401_defs.h: IBM 1401 simulator definitions + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 28-Jun-07 RMS Defined character code for tape mark + 14-Nov-04 RMS Added column binary support + 27-Oct-04 RMS Added maximum instruction length + 16-Mar-03 RMS Fixed mnemonic for MCS + 03-Jun-02 RMS Added 1311 support + 14-Apr-99 RMS Converted t_addr to unsigned + + This simulator is based on the 1401 simulator written by Len Fehskens + with assistance from Sarah Lee Harris and Bob Supnik. This one's for + you, Len. I am grateful to Paul Pierce and Charles Owen for their help + in answering questions, gathering source material, and debugging. +*/ + +#ifndef _I1401_DEFS_H_ +#define _I1401_DEFS_H_ 0 + +#include "sim_defs.h" + +/* Simulator stop codes */ + +#define STOP_NXI 1 /* unimpl instr */ +#define STOP_NXM 2 /* non-exist mem */ +#define STOP_NXD 3 /* non-exist dev */ +#define STOP_NOWM 4 /* no WM under op */ +#define STOP_INVA 5 /* invalid A addr */ +#define STOP_INVB 6 /* invalid B addr */ +#define STOP_INVL 7 /* invalid length */ +#define STOP_INVM 8 /* invalid modifier */ +#define STOP_INVBR 9 /* invalid branch */ +#define STOP_IBKPT 10 /* breakpoint */ +#define STOP_HALT 11 /* halt */ +#define STOP_INVMTU 12 /* invalid MT unit */ +#define STOP_MTZ 13 /* MT zero lnt rec */ +#define STOP_MTL 14 /* MT write lock */ +#define STOP_CCT 15 /* inv CCT channel */ +#define STOP_NOCD 16 /* no cards left */ +#define STOP_WRAP 17 /* AS, BS mem wrap */ +#define STOP_IOC 18 /* I/O check */ +#define STOP_INVDSC 19 /* invalid disk sector */ +#define STOP_INVDCN 20 /* invalid disk count */ +#define STOP_INVDSK 21 /* invalid disk unit */ +#define STOP_INVDFN 22 /* invalid disk func */ +#define STOP_INVDLN 23 /* invalid disk reclen */ +#define STOP_WRADIS 24 /* write address dis */ +#define STOP_WRCHKE 25 /* write check error */ +#define STOP_INVDAD 26 /* invalid disk addr */ +#define STOP_INVDCY 27 /* invalid direct seek */ + +/* Memory and devices */ + +#define MAXMEMSIZE 16000 /* max memory */ +#define MEMSIZE (cpu_unit.capac) /* current memory */ +#define CDR_BUF 1 /* card rdr buffer */ +#define CDR_WIDTH 80 /* card rdr width */ +#define CDP_BUF 101 /* card punch buffer */ +#define CDP_WIDTH 80 /* card punch width */ +#define CD_CBUF1 401 /* r/p col bin buf 12-3 */ +#define CD_CBUF2 501 /* r/p col bin buf 4-9 */ +#define LPT_BUF 201 /* line print buffer */ +#define LPT_WIDTH 132 /* line print width */ +#define CCT_LNT 132 /* car ctrl length */ +#define INQ_WIDTH 80 /* inq term width */ +#define ADDR_ERR(x) (((uint32) (x)) >= MEMSIZE) + +/* Binary address format + + <14:0> address, with index added in + <23:16> index register memory address + <25:24> address error bits +*/ + +#define ADDRMASK 037777 /* addr mask */ +#define INDEXMASK 077777 /* addr + index mask */ +#define V_INDEX 16 +#define M_INDEX 0177 +#define V_ADDRERR 24 +#define BA (1 << V_ADDRERR) /* bad addr digit */ +#define X1 (87 << V_INDEX) /* index reg 1 */ +#define X2 (92 << V_INDEX) /* index reg 2 */ +#define X3 (97 << V_INDEX) /* index reg 3 */ + +/* CPU instruction control flags. The flag definitions must be harmonized + with the UNIT flag definitions used by the simulator. */ + +/* Lengths */ + +#define L1 0001 /* 1: op */ +#define L2 0002 /* 2: op d */ +#define L4 0004 /* 4: op aaa */ +#define L5 0010 /* 5: op aaa d */ +#define L7 0020 /* 7: op aaa bbb */ +#define L8 0040 /* 8: op aaa bbb d */ +#define MAX_L 8 /* max length */ + +/* CPU options, stored in cpu_unit.flags */ + +#define MDV (1 << (UNIT_V_UF + 0)) /* multiply/divide */ +#define MR (1 << (UNIT_V_UF + 1)) /* move record */ +#define XSA (1 << (UNIT_V_UF + 2)) /* index, store addr */ +#define EPE (1 << (UNIT_V_UF + 3)) /* expanded edit */ +#define MA (1 << (UNIT_V_UF + 4)) /* modify address */ +#define BBE (1 << (UNIT_V_UF + 5)) /* br bit equal */ +#define HLE (1 << (UNIT_V_UF + 6)) /* high/low/equal */ +#define UNIT_MSIZE (1 << (UNIT_V_UF + 7)) /* fake flag */ +#define ALLOPT (MDV + MR + XSA + EPE + MA + BBE + HLE) +#define STDOPT (MDV + MR + XSA + EPE + MA + BBE + HLE) + +/* Fetch control */ + +#define AREQ (1 << (UNIT_V_UF + 8)) /* validate A */ +#define BREQ (1 << (UNIT_V_UF + 9)) /* validate B */ +#define MLS (1 << (UNIT_V_UF + 10)) /* move load store */ +#define NOWM (1 << (UNIT_V_UF + 11)) /* no WM at end */ +#define HNOP (1 << (UNIT_V_UF + 12)) /* halt or nop */ +#define IO (1 << (UNIT_V_UF + 13)) /* IO */ +#define UNIT_BCD (1 << (UNIT_V_UF + 14)) /* BCD strings */ + +#if (UNIT_V_UF < 6) || ((UNIT_V_UF + 14) > 31) + Definition error: flags overlap +#endif + +/* BCD memory character format */ + +#define WM 0100 /* word mark */ +#define ZONE 0060 /* zone */ +#define BBIT 0040 /* 1 in valid sign */ +#define ABIT 0020 /* sign (1 = +) */ +#define DIGIT 0017 /* digit */ +#define CHAR 0077 /* character */ + +#define V_WM 6 +#define V_ZONE 4 +#define V_DIGIT 0 + +/* Interesting BCD characters */ + +#define BCD_BLANK 000 +#define BCD_ONE 001 +#define BCD_TWO 002 +#define BCD_THREE 003 +#define BCD_FOUR 004 +#define BCD_FIVE 005 +#define BCD_SIX 006 +#define BCD_SEVEN 007 +#define BCD_EIGHT 010 +#define BCD_NINE 011 +#define BCD_ZERO 012 +#define BCD_TAPMRK 017 +#define BCD_ALT 020 +#define BCD_S 022 +#define BCD_U 024 +#define BCD_W 026 +#define BCD_RECMRK 032 +#define BCD_COMMA 033 +#define BCD_PERCNT 034 +#define BCD_WM 035 +#define BCD_BS 036 +#define BCD_TS 037 +#define BCD_MINUS 040 +#define BCD_M 044 +#define BCD_R 051 +#define BCD_DOLLAR 053 +#define BCD_ASTER 054 +#define BCD_AMPER 060 +#define BCD_A 061 +#define BCD_B 062 +#define BCD_C 063 +#define BCD_E 065 +#define BCD_DECIMAL 073 +#define BCD_SQUARE 074 +#define BCD_GRPMRK 077 + +/* Opcodes */ + +#define OP_R 001 /* read */ +#define OP_W 002 /* write */ +#define OP_WR 003 /* write and read */ +#define OP_P 004 /* punch */ +#define OP_RP 005 /* read and punch */ +#define OP_WP 006 /* write and punch */ +#define OP_WRP 007 /* write read punch */ +#define OP_RF 010 /* reader feed */ +#define OP_PF 011 /* punch feed */ +#define OP_MA 013 /* modify address */ +#define OP_MUL 014 /* multiply */ +#define OP_CS 021 /* clear storage */ +#define OP_S 022 /* subtract */ +#define OP_MTF 024 /* magtape function */ +#define OP_BWZ 025 /* branch wm or zone */ +#define OP_BBE 026 /* branch bit equal */ +#define OP_MZ 030 /* move zone */ +#define OP_MCS 031 /* move suppr zeroes */ +#define OP_SWM 033 /* set word mark */ +#define OP_DIV 034 /* divide */ +#define OP_SS 042 /* select stacker */ +#define OP_LCA 043 /* load characters */ +#define OP_MCW 044 /* move characters */ +#define OP_NOP 045 /* no op */ +#define OP_MCM 047 /* move to rec/grp mk */ +#define OP_SAR 050 /* store A register */ +#define OP_ZS 052 /* zero and subtract */ +#define OP_A 061 /* add */ +#define OP_B 062 /* branch */ +#define OP_C 063 /* compare */ +#define OP_MN 064 /* move numeric */ +#define OP_MCE 065 /* move char and edit */ +#define OP_CC 066 /* carriage control */ +#define OP_SBR 070 /* store B register */ +#define OP_ZA 072 /* zero and add */ +#define OP_H 073 /* halt */ +#define OP_CWM 074 /* clear word mark */ + +/* I/O addresses */ + +#define IO_INQ 023 /* inquiry terminal */ +#define IO_MT 024 /* magtape */ +#define IO_MTB 062 /* binary magtape */ +#define IO_DP 066 /* 1311 diskpack */ + +/* I/O modes */ + +#define MD_NORM 0 /* normal (move) */ +#define MD_WM 1 /* word mark (load) */ +#define MD_BIN 2 /* binary */ + +/* Indicator characters */ + +#define IN_UNC 000 /* unconditional */ +#define IN_CC9 011 /* carr ctrl chan 9 */ +#define IN_CC12 014 /* carr ctrl chan 12 */ +#define IN_UNQ 021 /* unequal */ +#define IN_EQU 022 /* equal */ +#define IN_LOW 023 /* low */ +#define IN_HGH 024 /* high */ +#define IN_DPW 025 /* parity/compare check */ +#define IN_LNG 026 /* wrong lnt record */ +#define IN_UNA 027 /* unequal addr cmp */ +#define IN_DSK 030 /* disk error */ +#define IN_OVF 031 /* overflow */ +#define IN_LPT 032 /* printer error */ +#define IN_PRO 034 /* process check */ +#define IN_DBY 036 /* disk busy */ +#define IN_END 042 /* end indicator */ +#define IN_TAP 043 /* tape error */ +#define IN_ACC 045 /* access error */ +#define IN_BSY 047 /* printer busy */ +#define IN_INR 050 /* inquiry request */ +#define IN_PCB 051 /* printer carr busy */ +#define IN_PNCH 052 /* punch error */ +#define IN_INC 054 /* inquiry clear */ +#define IN_LST 061 /* last card */ +#define IN_SSB 062 /* sense switch B */ +#define IN_SSC 063 /* sense switch C */ +#define IN_SSD 064 /* sense switch D */ +#define IN_SSE 065 /* sense switch E */ +#define IN_SSF 066 /* sense switch F */ +#define IN_SSG 067 /* sense switch G */ +#define IN_READ 072 /* reader error */ + +#define CRETIOE(f,c) return ((f)? (c): SCPE_OK) + +/* Function prototypes */ + +int32 bcd2ascii (int32 c, t_bool use_h); +int32 ascii2bcd (int32 c); + + +#endif diff --git a/I1401/i1401_dp.c b/I1401/i1401_dp.c new file mode 100644 index 0000000..cb80dd8 --- /dev/null +++ b/I1401/i1401_dp.c @@ -0,0 +1,594 @@ +/* i1401_dp.c: IBM 1311 disk simulator + + Copyright (c) 2002-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dp 1311 disk pack + + 18-Oct-02 RMS Fixed bug in address comparison logic + 19-Sep-02 RMS Minor edit for consistency with 1620 + 15-Jun-02 RMS Reworked address comparison logic + + The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track. + Each sector contains 106 characters of information: + + 6c sector address + 100c sector data + + By default, a sector's address field will be '000000', which is illegal. + This is interpreted to mean the implied sector number that would be in + place if the disk pack had been formatted with sequential sector numbers. + + The sector data can be 100 characters without word marks, or 90 characters + with word marks. Load mode transfers 90 characters per sector with + word marks, move mode transfers 100 characters per sector without word + marks. No attempt is made to catch incompatible writes (eg, load mode + write followed by move mode read). +*/ + +#include "i1401_defs.h" + +#define DP_NUMDR 5 /* #drives */ +#define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */ +#define UNIT_WAE (1 << UNIT_V_WAE) + +/* Disk format */ + +#define DP_ADDR 6 /* address */ +#define DP_DATA 100 /* data */ +#define DP_NUMCH (DP_ADDR + DP_DATA) + +#define DP_NUMSC 20 /* #sectors */ +#define DP_NUMSF 10 /* #surfaces */ +#define DP_NUMCY 100 /* #cylinders */ +#define DP_TOTSC (DP_NUMCY*DP_NUMSF*DP_NUMSC) +#define DP_SIZE (DP_TOTSC*DP_NUMCH) + +/* Disk control field */ + +#define DCF_DRV 0 /* drive select */ +#define DCF_SEC 1 /* sector addr */ +#define DCF_SEC_LEN 6 +#define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */ +#define DCF_CNT_LEN 3 +#define DCF_LEN (DCF_CNT + DCF_CNT_LEN) +#define DCF_DIR 1 /* direct seek */ +#define DCF_DIR_LEN 4 +#define DCF_DIR_FL (DCF_DIR + DCF_DIR_LEN) /* direct seek flag */ +#define DCF_DSEEK 0xB + +/* Functions */ + +#define FNC_SEEK 0 /* seek */ +#define FNC_CHECK 3 /* check */ +#define FNC_READ 1 /* read sectors */ +#define FNC_RSCO 5 /* read sec cnt overlay */ +#define FNC_RTRK 6 /* read track */ +#define FNC_WOFF 10 /* offset for write */ +#define FNC_WRITE 11 /* write sectors */ +#define FNC_WRSCO 15 /* write sec cnt overlay */ +#define FNC_WRTRK 16 /* write track */ + +#define CYL u3 /* current cylinder */ + +extern uint8 M[]; /* memory */ +extern int32 ind[64]; +extern int32 AS, BS, iochk; +extern int32 bcd_to_bin[16]; +extern int32 bin_to_bcd[16]; +extern UNIT cpu_unit; + +int32 dp_lastf = 0; /* prior function */ +int32 dp_time = 0; /* seek time */ + +t_stat dp_reset (DEVICE *dptr); +t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 wchk); +t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 wchk); +t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg); +t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg); +int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf); +t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf); +t_bool dp_zeroad (uint8 *ap); +t_bool dp_cmp_ad (uint8 *ap, int32 dcf); +int32 dp_trkop (int32 drv, int32 sec); +int32 dp_cvt_bcd (int32 ad, int32 len); +void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg); +int32 dp_get_cnt (int32 dcf); +void dp_fill (UNIT *uptr, uint32 da, int32 cnt); + +/* DP data structures + + dp_dev DSK device descriptor + dp_unit DSK unit list + dp_reg DSK register list + dp_mod DSK modifier list +*/ + +UNIT dp_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) } + }; + +REG dp_reg[] = { + { FLDATA (ACC, ind[IN_ACC], 0) }, + { FLDATA (PWC, ind[IN_DPW], 0) }, + { FLDATA (WLR, ind[IN_LNG], 0) }, + { FLDATA (UNA, ind[IN_UNA], 0) }, + { FLDATA (ERR, ind[IN_DSK], 0) }, + { FLDATA (BSY, ind[IN_DBY], 0) }, + { DRDATA (LASTF, dp_lastf, 3) }, + { DRDATA (TIME, dp_time, 24), PV_LEFT }, + { URDATA (CYL, dp_unit[0].CYL, 10, 8, 0, + DP_NUMDR, PV_LEFT + REG_RO) }, + { NULL } + }; + +MTAB dp_mod[] = { + { UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL }, + { UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL }, + { 0 } + }; + +DEVICE dp_dev = { + "DP", dp_unit, dp_reg, dp_mod, + DP_NUMDR, 10, 21, 1, 8, 7, + NULL, NULL, &dp_reset, + NULL, NULL, NULL + }; + +/* Disk IO routine + + Inputs: + fnc = function character + flg = load vs move mode + mod = modifier character + Outputs: + status = status +*/ + +t_stat dp_io (int32 fnc, int32 flg, int32 mod) +{ +int32 dcf, drv, sec, psec, cnt, qwc, qzr, diff; +UNIT *uptr; +t_stat r; + +dcf = BS; /* save DCF addr */ +qwc = 0; /* not wcheck */ +ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */ +ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0; +if (sim_is_active (&dp_unit[0])) { /* ctlr busy? */ + ind[IN_DBY] = ind[IN_DSK] = 1; /* set indicators */ + return SCPE_OK; /* done */ + } + +AS = dcf + 6; /* AS for most ops */ +BS = dcf + DCF_CNT - 1; /* minimum DCF */ +if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */ +if (M[dcf] & BBIT) drv = M[dcf + DCF_SEC + 1] & 0xE; /* impl sel? cyl 8-4-2 */ +else drv = M[dcf] & DIGIT; /* get drive sel */ +if ((drv == 0) || (drv & 1) || (drv > BCD_ZERO)) /* bad drive #? */ + return STOP_INVDSK; +drv = bcd_to_bin[drv] >> 1; /* convert */ +uptr = dp_dev.units + drv; /* get unit ptr */ +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + ind[IN_DSK] = ind[IN_ACC] = 1; /* no, error */ + CRETIOE (iochk, SCPE_UNATT); + } + +if ((fnc == FNC_SEEK) && /* seek and */ + (M[dcf + DCF_DIR_FL] & DCF_DSEEK) == DCF_DSEEK) { /* direct flag? */ + diff = dp_cvt_bcd (dcf + DCF_DIR, DCF_DIR_LEN); /* cvt diff */ + if (diff < 0) return STOP_INVDSC; /* error? */ + diff = diff >> 1; /* diff is *2 */ + if ((M[dcf + DCF_DIR + DCF_DIR_LEN - 1] & ZONE) == BBIT) + diff = -diff; /* get sign */ + uptr->CYL = uptr->CYL + diff; /* bound seek */ + if (uptr->CYL < 0) uptr->CYL = 0; + else if (uptr->CYL >= DP_NUMCY) { /* too big? */ + uptr->CYL = 0; /* system hangs */ + return STOP_INVDCY; + } + sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */ + return SCPE_OK; /* done! */ + } + +sec = dp_cvt_bcd (dcf + DCF_SEC, DCF_SEC_LEN); /* cvt sector */ +if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */ + return STOP_INVDSC; +if (fnc == FNC_SEEK) { /* seek? */ + uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */ + DP_NUMCY; + sim_activate (&dp_unit[0], dp_time); /* set ctlr busy */ + return SCPE_OK; /* done! */ + } + +BS = dcf + DCF_LEN; /* full DCF */ +if (ADDR_ERR (BS)) return STOP_WRAP; /* DCF in memory? */ +cnt = dp_get_cnt (dcf); /* get count */ +if (cnt < 0) return STOP_INVDCN; /* bad count? */ + +if (fnc >= FNC_WOFF) return STOP_INVDFN; /* invalid func */ +if (mod == BCD_W) { /* write? */ + if (fnc == FNC_CHECK) { /* write check? */ + qwc = 1; /* special read */ + fnc = dp_lastf; /* use last func */ + } + else { + dp_lastf = fnc; /* save func */ + fnc = fnc + FNC_WOFF; /* change to write */ + } + } +else if (mod == BCD_R) dp_lastf = fnc; /* read? save func */ +else return STOP_INVM; /* other? error */ + +switch (fnc) { /* case on function */ + + case FNC_RSCO: /* read sec cnt ov */ + BS = dcf + DCF_CNT; /* set count back */ + /* fall thru */ + case FNC_READ: /* read */ + psec = dp_fndsec (uptr, sec, dcf); /* find sector */ + if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */ + for (;;) { /* loop */ + qzr = (--cnt == 0); /* set zero latch */ + dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */ + if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read sector */ + break; + cnt = dp_get_cnt (dcf); /* get new count */ + if (cnt < 0) return STOP_INVDCN; /* bad count? */ + if (qzr) break; /* zero latch? done */ + sec++; psec++; /* next sector */ + dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */ + if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */ + } + break; /* done, clean up */ + + case FNC_RTRK: /* read track */ + AS = dcf + 9; /* special AS */ + psec = dp_trkop (drv, sec); /* start of track */ + for (;;) { /* loop */ + qzr = (--cnt == 0); /* set zero latch */ + dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */ + if (r = dp_rdadr (uptr, psec, flg, qwc)) /* read addr */ + break; /* error? */ + if (r = dp_rdsec (uptr, psec, flg, qwc)) /* read data */ + break; /* error? */ + cnt = dp_get_cnt (dcf); /* get new count */ + if (cnt < 0) return STOP_INVDCN; /* bad count? */ + if (qzr) break; /* zero latch? done */ + psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); + } + break; /* done, clean up */ + + case FNC_WRSCO: /* write sec cnt ov */ + BS = dcf + DCF_CNT; /* set count back */ + /* fall through */ + case FNC_WRITE: /* read */ + psec = dp_fndsec (uptr, sec, dcf); /* find sector */ + if (psec < 0) CRETIOE (iochk, STOP_INVDAD); /* addr cmp error? */ + for (;;) { /* loop */ + qzr = (--cnt == 0); /* set zero latch */ + dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* rewr cnt */ + if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */ + if (qzr) break; /* zero latch? done */ + sec++; psec++; /* next sector */ + dp_cvt_bin (dcf + DCF_SEC, DCF_SEC_LEN, sec, flg); /* rewr sec */ + if (r = dp_nexsec (uptr, psec, dcf)) break; /* find next */ + } + break; /* done, clean up */ + + case FNC_WRTRK: /* write track */ + if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */ + return STOP_WRADIS; + AS = dcf + 9; /* special AS */ + psec = dp_trkop (drv, sec); /* start of track */ + for (;;) { /* loop */ + qzr = (--cnt == 0); /* set zero latch */ + dp_cvt_bin (dcf + DCF_CNT, DCF_CNT_LEN, cnt, MD_WM); /* redo count */ + if (r = dp_wradr (uptr, psec, flg)) break; /* write addr */ + if (r = dp_wrsec (uptr, psec, flg)) break; /* write data */ + if (qzr) break; /* zero latch? done */ + psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); + } + break; /* done, clean up */ + + default: /* unknown */ + return STOP_INVDFN; + } + +if (r == SCPE_OK) { /* normal so far? */ + BS++; /* advance BS */ + if (ADDR_ERR (BS)) return STOP_WRAP; /* address error? */ + if (M[BS - 1] != (WM + BCD_GRPMRK)) { /* GM + WM at end? */ + ind[IN_LNG] = ind[IN_DSK] = 1; /* no, error */ + r = STOP_INVDLN; + } + } +CRETIOE (iochk || !ind[IN_DSK], r); /* return status */ +} + +/* Read or compare address with memory */ + +t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 flg, int32 qwc) +{ +int32 i; +uint8 ac; +int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ +t_bool zad = dp_zeroad (ap); /* zero address */ +static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */ + 100000, 10000, 1000, 100, 10, 1 + } ; + +for (i = 0; i < DP_ADDR; i++) { /* copy address */ + if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ + ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ + return STOP_INVDLN; + } + if (zad) { /* addr zero? */ + ac = sec / dec_tab[i]; /* get addr digit */ + sec = sec % dec_tab[i]; /* get remainder */ + ac = bcd_to_bin[ac]; /* cvt to BCD */ + } + else ac = *ap; /* addr char */ + if (qwc) { /* wr chk? skip if zad */ + if (!zad && (flg? (M[BS] != ac): /* L? cmp with WM */ + ((M[BS] & CHAR) != (ac & CHAR)))) { /* M? cmp w/o WM */ + ind[IN_DPW] = ind[IN_DSK] = 1; + return STOP_WRCHKE; + } + } + else if (flg) M[BS] = ac & CHAR; /* load mode */ + else M[BS] = (M[BS] & WM) | (ac & CHAR); /* move mode */ + ap++; BS++; /* adv ptrs */ + if (ADDR_ERR (BS)) return STOP_WRAP; + } +return SCPE_OK; +} + +/* Read or compare data with memory */ + +t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 flg, int32 qwc) +{ +int32 i, lim; +int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */ + +lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */ +for (i = 0; i < lim; i++) { /* copy data */ + if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ + ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ + return STOP_INVDLN; + } + if (qwc) { /* write check? */ + if (flg? (M[BS] != *ap): /* load mode cmp */ + ((M[BS] & CHAR) != (*ap & CHAR))) { /* move mode cmp */ + ind[IN_DPW] = ind[IN_DSK] = 1; /* error */ + return STOP_WRCHKE; + } + } + else if (flg) M[BS] = *ap & (WM | CHAR); /* load mode */ + else M[BS] = (M[BS] & WM) | (*ap & CHAR); /* word mode */ + ap++; BS++; /* adv ptrs */ + if (ADDR_ERR (BS)) return STOP_WRAP; + } +return SCPE_OK; +} + +/* Write address to disk */ + +t_stat dp_wradr (UNIT *uptr, int32 sec, int32 flg) +{ +int32 i; +uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ + +for (i = 0; i < DP_ADDR; i++) { /* copy address */ + if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ + dp_fill (uptr, da, DP_NUMCH - i); /* fill, set err */ + ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ + return STOP_INVDLN; + } + if (flg) *ap = M[BS] & (WM | CHAR); /* L? copy WM */ + else *ap = M[BS] & CHAR; /* M? strip WM */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + da++; ap++; BS++; /* adv ptrs */ + if (ADDR_ERR (BS)) return STOP_WRAP; + } +return SCPE_OK; +} + +/* Write data to disk */ + +t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 flg) +{ +int32 i, lim; +uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ + +lim = flg? (DP_DATA - 10): DP_DATA; /* load vs move */ +for (i = 0; i < lim; i++) { /* copy data */ + if (M[BS] == (WM | BCD_GRPMRK)) { /* premature GWM? */ + dp_fill (uptr, da, DP_DATA - i); /* fill, set err */ + ind[IN_LNG] = ind[IN_DSK] = 1; /* error */ + return STOP_INVDLN; + } + if (flg) *ap = M[BS] & (WM | CHAR); /* load, copy WM */ + else *ap = M[BS] & CHAR; /* move, strip WM */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + da++; ap++; BS++; /* adv ptrs */ + if (ADDR_ERR (BS)) return STOP_WRAP; + } +return SCPE_OK; +} + +/* Find sector */ + +int32 dp_fndsec (UNIT *uptr, int32 sec, int32 dcf) +{ +int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ +int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk; +int32 da = psec * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ +int32 i; + +if (dp_zeroad (ap)) return psec; /* addr zero? ok */ +if (dp_cmp_ad (ap, dcf)) return psec; /* addr comp? ok */ +psec = psec - (psec % DP_NUMSC); /* sector 0 */ +for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */ + da = psec * DP_NUMCH; /* char number */ + ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */ + if (dp_zeroad (ap)) continue; /* no implicit match */ + if (dp_cmp_ad (ap, dcf)) return psec; /* match? */ + } +ind[IN_UNA] = ind[IN_DSK] = 1; /* no match */ +return -1; +} + +/* Find next sector - must be sequential, cannot cross cylinder boundary */ + +t_stat dp_nexsec (UNIT *uptr, int32 psec, int32 dcf) +{ +int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ +int32 da = psec * DP_NUMCH; /* word number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ + +if (ctrk) { /* not trk zero? */ + if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */ + if (dp_cmp_ad (ap, dcf)) return SCPE_OK; /* addr comp? ok */ + } +ind[IN_UNA] = ind[IN_DSK] = 1; /* no, error */ +return STOP_INVDAD; +} + +/* Test for zero address */ + +t_bool dp_zeroad (uint8 *ap) +{ +int32 i; + +for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ + if (*ap & CHAR) return FALSE; /* nonzero? lose */ + } +return TRUE; /* all zeroes */ +} + +/* Compare disk address to memory sector address - always omit word marks */ + +t_bool dp_cmp_ad (uint8 *ap, int32 dcf) +{ +int32 i; +uint8 c; + +for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ + c = M[dcf + DCF_SEC + i]; /* sector addr char */ + if ((c & CHAR) != (*ap & CHAR)) /* cmp w/o WM */ + return FALSE; + } +return TRUE; /* compare ok */ +} + +/* Track operation setup */ + +int32 dp_trkop (int32 drv, int32 sec) +{ +int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF; + +return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) + + (ctrk * DP_NUMSC)); +} + +/* Convert DCF BCD field to binary */ + +int32 dp_cvt_bcd (int32 ad, int32 len) +{ +uint8 c; +int32 r; + +for (r = 0; len > 0; len--) { /* loop thru char */ + c = M[ad] & DIGIT; /* get digit */ + if ((c == 0) || (c > BCD_ZERO)) return -1; /* invalid? */ + r = (r * 10) + bcd_to_bin[c]; /* cvt to bin */ + ad++; /* next digit */ + } +return r; +} + +/* Convert binary to DCF BCD field */ + +void dp_cvt_bin (int32 ad, int32 len, int32 val, int32 flg) +{ +int32 r; + +for ( ; len > 0; len--) { /* loop thru char */ + r = val % 10; /* get digit */ + if (flg) M[ad + len - 1] = bin_to_bcd[r]; /* load mode? */ + else M[ad + len - 1] = (M[ad + len - 1] & WM) | bin_to_bcd[r]; + val = val / 10; + } +return; +} + +/* Get and validate count */ + +int32 dp_get_cnt (int32 dcf) +{ +int32 cnt = dp_cvt_bcd (dcf + DCF_CNT, DCF_CNT_LEN); /* get new count */ +if (cnt < 0) return -1; /* bad count? */ +if (cnt == 0) return 1000; /* 0 => 1000 */ +return cnt; +} + +/* Fill sector buffer with blanks */ + +void dp_fill (UNIT *uptr, uint32 da, int32 cnt) +{ +while (cnt-- > 0) { /* fill with blanks */ + *(((uint8 *) uptr->filebuf) + da) = BCD_BLANK; + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + da++; + } +return; +} + +/* Reset routine */ + +t_stat dp_reset (DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < DP_NUMDR; i++) dp_unit[i].CYL = 0; /* reset cylinder */ +dp_lastf = 0; /* clear state */ +ind[IN_DPW] = ind[IN_LNG] = ind[IN_UNA] = 0; /* clr indicators */ +ind[IN_DSK] = ind[IN_ACC] = ind[IN_DBY] = 0; +sim_cancel (&dp_unit[0]); /* cancel timer */ +return SCPE_OK; +} diff --git a/I1401/i1401_iq.c b/I1401/i1401_iq.c new file mode 100644 index 0000000..93be315 --- /dev/null +++ b/I1401/i1401_iq.c @@ -0,0 +1,184 @@ +/* i1401_iq.c: IBM 1407 inquiry terminal + + Copyright (c) 1993-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + inq 1407 inquiry terminal + + 20-Sep-05 RMS Revised for new code tables + 22-Dec-02 RMS Added break support + 07-Sep-01 RMS Moved function prototypes + 14-Apr-99 RMS Changed t_addr to unsigned +*/ + +#include "i1401_defs.h" +#include + +#define UNIT_V_PCH (UNIT_V_UF + 0) /* output conv */ +#define UNIT_PCH (1 << UNIT_V_PCH) + +extern volatile int32 stop_cpu; +extern uint8 M[]; +extern int32 BS, iochk, ind[64]; +extern UNIT cpu_unit; +extern t_bool conv_old; + +int32 inq_char = 033; /* request inq */ +t_stat inq_svc (UNIT *uptr); +t_stat inq_reset (DEVICE *dptr); + +void puts_tty (char *cptr); + +/* INQ data structures + + inq_dev INQ device descriptor + inq_unit INQ unit descriptor + inq_reg INQ register list +*/ + +UNIT inq_unit = { UDATA (&inq_svc, 0, 0), KBD_POLL_WAIT }; + +REG inq_reg[] = { + { ORDATA (INQC, inq_char, 7) }, + { FLDATA (INR, ind[IN_INR], 0) }, + { FLDATA (INC, ind[IN_INC], 0) }, + { DRDATA (TIME, inq_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB inq_mod[] = { + { UNIT_PCH, 0, "business set", "BUSINESS" }, + { UNIT_PCH, UNIT_PCH, "Fortran set", "FORTRAN" }, + { 0 } + }; + +DEVICE inq_dev = { + "INQ", &inq_unit, inq_reg, inq_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &inq_reset, + NULL, NULL, NULL + }; + +/* Terminal I/O + + Modifiers have not been checked; legal modifiers are R and W +*/ + +t_stat inq_io (int32 flag, int32 mod) +{ +int32 i, t, wm_seen = 0; +t_bool use_h = inq_unit.flags & UNIT_PCH; + +ind[IN_INC] = 0; /* clear inq clear */ +switch (mod) { /* case on mod */ + + case BCD_R: /* input */ +/* if (ind[IN_INR] == 0) return SCPE_OK; /* return if no req */ + ind[IN_INR] = 0; /* clear req */ + puts_tty ("[Enter]\r\n"); /* prompt */ + for (i = 0; M[BS] != (BCD_GRPMRK + WM); i++) { /* until GM + WM */ + while (((t = sim_poll_kbd ()) == SCPE_OK) || + (t & SCPE_BREAK)) { + if (stop_cpu) return SCPE_STOP; /* interrupt? */ + } + if (t < SCPE_KFLAG) return t; /* if not char, err */ + t = t & 0177; + if ((t == '\r') || (t == '\n')) break; + if (t == inq_char) { /* cancel? */ + ind[IN_INC] = 1; /* set indicator */ + puts_tty ("\r\n[Canceled]\r\n"); + return SCPE_OK; + } + if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); + sim_putchar (t); /* echo */ + if (flag == MD_WM) { /* word mark mode? */ + if ((t == '~') && (wm_seen == 0)) wm_seen = WM; + else { + M[BS] = wm_seen | ascii2bcd (t); + wm_seen = 0; + } + } + else M[BS] = (M[BS] & WM) | ascii2bcd (t); + if (!wm_seen) BS++; + if (ADDR_ERR (BS)) { + BS = BA | (BS % MAXMEMSIZE); + return STOP_NXM; + } + } + puts_tty ("\r\n"); + M[BS++] = BCD_GRPMRK + WM; + break; + + case BCD_W: /* output */ + for (i = 0; (t = M[BS++]) != (BCD_GRPMRK + WM); i++) { + if ((flag == MD_WM) && (t & WM)) { + if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); + if (conv_old) sim_putchar ('~'); + else sim_putchar ('`'); + } + if (i && ((i % INQ_WIDTH) == 0)) puts_tty ("\r\n"); + sim_putchar (bcd2ascii (t & CHAR, use_h)); + if (ADDR_ERR (BS)) { + BS = BA | (BS % MAXMEMSIZE); + return STOP_NXM; + } + } + puts_tty ("\r\n"); + break; + + default: /* invalid mod */ + return STOP_INVM; + } + +return SCPE_OK; +} + +/* Unit service - polls for WRU or inquiry request */ + +t_stat inq_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&inq_unit, inq_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +if ((temp & 0177) == inq_char) ind[IN_INR] = 1; /* set indicator */ +return SCPE_OK; +} + +/* Output multiple characters */ + +void puts_tty (char *cptr) +{ +if (cptr == NULL) return; +while (*cptr != 0) sim_putchar (*cptr++); +return; +} + +/* Reset routine */ + +t_stat inq_reset (DEVICE *dptr) +{ +ind[IN_INR] = ind[IN_INC] = 0; /* clear indicators */ +sim_activate (&inq_unit, inq_unit.wait); /* activate poll */ +return SCPE_OK; +} diff --git a/I1401/i1401_lp.c b/I1401/i1401_lp.c new file mode 100644 index 0000000..5bd4b5d --- /dev/null +++ b/I1401/i1401_lp.c @@ -0,0 +1,245 @@ +/* i1401_lp.c: IBM 1403 line printer simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt 1403 line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + 07-Mar-05 RMS Fixed bug in write_line (reported by Van Snyder) + 25-Apr-03 RMS Revised for extended file support + 30-May-02 RMS Widened POS to 32b + 13-Apr-01 RMS Revised for register arrays +*/ + +#include "i1401_defs.h" + +extern uint8 M[]; +extern char bcd_to_ascii_old[64]; +extern char bcd_to_ascii_a[64], bcd_to_ascii_h[64]; +extern char bcd_to_pca[64], bcd_to_pch[64]; +extern int32 iochk, ind[64]; +extern t_bool conv_old; + +int32 cct[CCT_LNT] = { 03 }; +int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0; + +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat space (int32 lines, int32 lflag); + +char *pch_table_old[4] = { + bcd_to_ascii_old, bcd_to_pca, bcd_to_pch, bcd_to_ascii_old + }; +char *pch_table[4] = { + bcd_to_ascii_a, bcd_to_pca, bcd_to_pch, bcd_to_ascii_h + }; + +#define UNIT_V_FT (UNIT_V_UF + 0) +#define UNIT_V_48 (UNIT_V_UF + 1) +#define UNIT_FT (1 << UNIT_V_FT) +#define UNIT_48 (1 << UNIT_V_48) +#define GET_PCHAIN(x) (((x) >> UNIT_V_FT) & (UNIT_FT|UNIT_48)) +#define CHP(ch,val) ((val) & (1 << (ch))) + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) + }; + +REG lpt_reg[] = { + { FLDATA (ERR, ind[IN_LPT], 0) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, + { DRDATA (LINES, lines, 8), PV_LEFT }, + { DRDATA (CCTP, cctptr, 8), PV_LEFT }, + { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, + { NULL } + }; + +MTAB lpt_mod[] = { + { UNIT_48, UNIT_48, "48 character chain", "48" }, + { UNIT_48, 0, "64 character chain", "64" }, + { UNIT_FT, UNIT_FT, "Fortran set", "FORTRAN" }, + { UNIT_FT, 0, "business set", "BUSINESS" }, + { UNIT_FT|UNIT_48, 0, NULL, "PCF" }, /* obsolete */ + { UNIT_FT|UNIT_48, UNIT_48, NULL, "PCA" }, + { UNIT_FT|UNIT_48, UNIT_FT|UNIT_48, NULL, "PCH" }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, NULL + }; + +/* Print routine + + Modifiers have been checked by the caller + SQUARE = word mark mode + S = suppress automatic newline +*/ + +t_stat write_line (int32 ilnt, int32 mod) +{ +int32 i, t, wm, sup; +char *bcd2asc; +static char lbuf[LPT_WIDTH + 1]; /* + null */ + +if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +wm = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_SQUARE); +sup = ((ilnt == 2) || (ilnt == 5)) && (mod == BCD_S); +ind[IN_LPT] = 0; /* clear error */ +if (conv_old) /* get print chain */ + bcd2asc = pch_table_old[GET_PCHAIN (lpt_unit.flags)]; +else bcd2asc = pch_table[GET_PCHAIN (lpt_unit.flags)]; +for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */ + t = M[LPT_BUF + i]; + if (wm) lbuf[i] = (t & WM)? '1': ' '; /* wmarks -> 1 or sp */ + else lbuf[i] = bcd2asc[t & CHAR]; /* normal */ + } +lbuf[LPT_WIDTH] = 0; /* trailing null */ +for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0; +fputs (lbuf, lpt_unit.fileref); /* write line */ +if (lines) space (lines, lflag); /* cc action? do it */ +else if (sup == 0) space (1, FALSE); /* default? 1 line */ +else { + fputc ('\r', lpt_unit.fileref); /* sup -> overprint */ + lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ + } +lines = lflag = 0; /* clear cc action */ +if (ferror (lpt_unit.fileref)) { /* error? */ + ind[IN_LPT] = 1; + perror ("Line printer I/O error"); + clearerr (lpt_unit.fileref); + if (iochk) return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Carriage control routine + + The modifier has not been checked, its format is + <5:4> = 00, skip to channel now + = 01, space lines after + = 10, space lines now + = 11, skip to channel after + <3:0> = number of lines or channel number +*/ + +t_stat carriage_control (int32 mod) +{ +int32 i, action; + +action = (mod & ZONE) >> V_ZONE; /* get mod type */ +mod = mod & DIGIT; /* isolate value */ + +switch (action) { + + case 0: /* to channel now */ + if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) + return space (i, TRUE); + } + return STOP_CCT; /* runaway channel */ + + case 1: /* space after */ + if (mod <= 3) { + lines = mod; /* save # lines */ + lflag = FALSE; /* flag spacing */ + ind[IN_CC9] = ind[IN_CC12] = 0; + } + return SCPE_OK; + + case 2: /* space now */ + if (mod <= 3) return space (mod, FALSE); + return SCPE_OK; + + case 3: /* to channel after */ + if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */ + ind[IN_CC9] = ind[IN_CC12] = 0; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) { + lines = i; /* save # lines */ + lflag = TRUE; /* flag skipping */ + return SCPE_OK; + } + } + return STOP_CCT; /* runaway channel */ + } + +return SCPE_OK; +} + +/* Space routine - space or skip n lines + + Inputs: + count = number of lines to space or skip + sflag = skip (TRUE) or space (FALSE) +*/ + +t_stat space (int32 count, int32 sflag) +{ +int32 i; + +if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; +cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */ +if (sflag && CHP (0, cct[cctptr])) /* skip, top of form? */ + fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ +else { + for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref); + } +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +ind[IN_CC9] = CHP (9, cct[cctptr]) != 0; /* set indicators */ +ind[IN_CC12] = CHP (12, cct[cctptr]) != 0; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = lflag = 0; /* no cc action */ +ind[IN_LPT] = 0; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = 0; /* no cc action */ +ind[IN_LPT] = 0; +return attach_unit (uptr, cptr); +} diff --git a/I1401/i1401_mt.c b/I1401/i1401_mt.c new file mode 100644 index 0000000..65edf46 --- /dev/null +++ b/I1401/i1401_mt.c @@ -0,0 +1,408 @@ +/* i1401_mt.c: IBM 1401 magnetic tape simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt 7-track magtape + + 07-Jul-07 RMS Removed restriction on load-mode binary tape + 28-Jun-07 RMS Revised read tape mark behavior based on real hardware + (found by Van Snyder) + 16-Feb-06 RMS Added tape capacity checking + 15-Sep-05 RMS Yet another fix to load read group mark plus word mark + Added debug printouts (from Van Snyder) + 26-Aug-05 RMS Revised to use API for write lock check + 16-Aug-03 RMS End-of-record on load read works like move read + (verified on real 1401) + Added diagnostic read (space forward) + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 15-Mar-03 RMS Fixed end-of-record on load read yet again + 28-Feb-03 RMS Modified for magtape library + 31-Oct-02 RMS Added error record handling + 10-Oct-02 RMS Fixed end-of-record on load read writes WM plus GM + 30-Sep-02 RMS Revamped error handling + 28-Aug-02 RMS Added end of medium support + 12-Jun-02 RMS End-of-record on move read preserves old WM under GM + (found by Van Snyder) + 03-Jun-02 RMS Modified for 1311 support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added protection against bad record lengths + 30-Jan-02 RMS New zero footprint tape bootstrap from Van Snyder + 20-Jan-02 RMS Changed write enabled modifier + 29-Nov-01 RMS Added read only unit support + 18-Apr-01 RMS Changed to rewind tape before boot + 07-Dec-00 RMS Widened display width from 6 to 8 bits to see record lnt + CEO Added tape bootstrap + 14-Apr-99 RMS Changed t_addr to unsigned + 04-Oct-98 RMS V2.4 magtape format + + Magnetic tapes are represented as a series of variable 16b records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "i1401_defs.h" +#include "sim_tape.h" + +#define MT_NUMDR 7 /* #drives */ +#define MT_MAXFR (MAXMEMSIZE * 2) /* max transfer */ + +uint8 dbuf[MT_MAXFR]; /* tape buffer */ + +extern uint8 M[]; /* memory */ +extern int32 ind[64]; +extern int32 BS, iochk; +extern UNIT cpu_unit; +extern FILE *sim_deb; + +t_stat mt_reset (DEVICE *dptr); +t_stat mt_boot (int32 unitno, DEVICE *dptr); +t_stat mt_map_status (t_stat st); +UNIT *get_unit (int32 unit); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +UNIT mt_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, /* doesn't exist */ + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + + UNIT_ROABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + + UNIT_ROABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + + UNIT_ROABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + + UNIT_ROABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + + UNIT_ROABLE + UNIT_BCD, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_ATTABLE + + UNIT_ROABLE + UNIT_BCD, 0) } + }; + +REG mt_reg[] = { + { FLDATA (END, ind[IN_END], 0) }, + { FLDATA (ERR, ind[IN_TAP], 0) }, + { DRDATA (POS1, mt_unit[1].pos, T_ADDR_W), PV_LEFT + REG_RO }, + { DRDATA (POS2, mt_unit[2].pos, T_ADDR_W), PV_LEFT + REG_RO }, + { DRDATA (POS3, mt_unit[3].pos, T_ADDR_W), PV_LEFT + REG_RO }, + { DRDATA (POS4, mt_unit[4].pos, T_ADDR_W), PV_LEFT + REG_RO }, + { DRDATA (POS5, mt_unit[5].pos, T_ADDR_W), PV_LEFT + REG_RO }, + { DRDATA (POS6, mt_unit[6].pos, T_ADDR_W), PV_LEFT + REG_RO }, + { NULL } + }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { 0 } + }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + &mt_boot, &sim_tape_attach, &sim_tape_detach, + NULL, DEV_DEBUG + }; + +/* Function routine + + Inputs: + unit = unit character + mod = modifier character + Outputs: + status = status +*/ + +t_stat mt_func (int32 unit, int32 mod) +{ +t_mtrlnt tbc; +UNIT *uptr; +t_stat st; + +if ((uptr = get_unit (unit)) == NULL) return STOP_INVMTU; /* valid unit? */ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ +switch (mod) { /* case on modifier */ + + case BCD_A: /* diagnostic read */ + if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, + ">>MT%d: diagnostic read\n", unit); + ind[IN_END] = 0; /* clear end of file */ + st = sim_tape_sprecf (uptr, &tbc); /* space fwd */ + break; + + case BCD_B: /* backspace */ + if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, + ">>MT%d: backspace\n", unit); + ind[IN_END] = 0; /* clear end of file */ + st = sim_tape_sprecr (uptr, &tbc); /* space rev */ + break; /* end case */ + + case BCD_E: /* erase = nop */ + if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, + ">>MT%d: erase\n", unit); + if (sim_tape_wrp (uptr)) return STOP_MTL; + return SCPE_OK; + + case BCD_M: /* write tapemark */ + if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, + ">>MT%d: write tape mark\n", unit); + st = sim_tape_wrtmk (uptr); /* write tmk */ + break; + + case BCD_R: /* rewind */ + if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, + ">>MT%d: rewind\n", unit); + sim_tape_rewind (uptr); /* update position */ + return SCPE_OK; + + case BCD_U: /* unload */ + if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, + ">>MT%d: rewind and unload\n", unit); + sim_tape_rewind (uptr); /* update position */ + return detach_unit (uptr); /* detach */ + + default: + return STOP_INVM; + } + +return mt_map_status (st); +} + +/* Read and write routines + + Inputs: + unit = unit character + flag = normal, word mark, or binary mode + mod = modifier character + Outputs: + status = status + + Fine point: after a read, the system writes a group mark just + beyond the end of the record. However, first it checks for a + GM + WM; if present, the GM + WM is not changed. Otherwise, + an MCW read sets a GM, preserving the current WM; while an LCA + read sets a GM and clears the WM. +*/ + +t_stat mt_io (int32 unit, int32 flag, int32 mod) +{ +int32 t, wm_seen; +t_mtrlnt i, tbc; +t_stat st; +t_bool passed_eot; +UNIT *uptr; + +if ((uptr = get_unit (unit)) == NULL) return STOP_INVMTU; /* valid unit? */ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ + +switch (mod) { + + case BCD_R: /* read */ + if (DEBUG_PRS (mt_dev)) + fprintf (sim_deb, ">>MT%d: read from %d", unit, BS); + ind[IN_TAP] = ind[IN_END] = 0; /* clear error */ + wm_seen = 0; /* no word mk seen */ + st = sim_tape_rdrecf (uptr, dbuf, &tbc, MT_MAXFR); /* read rec */ + if (st == MTSE_RECE) ind[IN_TAP] = 1; /* rec in error? */ + else if (st == MTSE_TMK) { /* tape mark? */ + ind[IN_END] = 1; /* set indicator */ + tbc = 1; /* one char read */ + dbuf[0] = BCD_TAPMRK; /* BCD tapemark */ + } + else if (st != MTSE_OK) { /* stop on error */ + if (DEBUG_PRS (mt_dev)) + fprintf (sim_deb, ", stopped by status = %d\n", st); + break; + } + for (i = 0; i < tbc; i++) { /* loop thru buf */ + if (M[BS] == (BCD_GRPMRK + WM)) { /* GWM in memory? */ + if (DEBUG_PRS (mt_dev)) + fprintf (sim_deb, " to %d, stopped by GMWM\n", BS); + BS++; /* incr BS */ + if (ADDR_ERR (BS)) { /* test for wrap */ + BS = BA | (BS % MAXMEMSIZE); + return STOP_WRAP; + } + return SCPE_OK; /* done */ + } + t = dbuf[i]; /* get char */ + if (!(flag & MD_BIN) && (t == BCD_ALT)) /* BCD mode alt blank? */ + t = BCD_BLANK; /* real blank */ + if (flag & MD_WM) { /* word mk mode? */ + if ((t == BCD_WM) && (wm_seen == 0)) /* WM char, none prev? */ + wm_seen = WM; /* set flag */ + else { + M[BS] = wm_seen | (t & CHAR); /* char + wm seen */ + wm_seen = 0; /* clear flag */ + } + } + else M[BS] = (M[BS] & WM) | (t & CHAR); /* preserve mem WM */ + if (!wm_seen) BS++; + if (ADDR_ERR (BS)) { /* check next BS */ + BS = BA | (BS % MAXMEMSIZE); + return STOP_WRAP; + } + } + if (M[BS] != (BCD_GRPMRK + WM)) { /* not GM+WM at end? */ + if (flag & MD_WM) M[BS] = BCD_GRPMRK; /* LCA: clear WM */ + else M[BS] = (M[BS] & WM) | BCD_GRPMRK; /* MCW: save WM */ + } + if (DEBUG_PRS (mt_dev)) + fprintf (sim_deb, " to %d, stopped by EOR\n", BS); + BS++; /* adv BS */ + if (ADDR_ERR (BS)) { /* check final BS */ + BS = BA | (BS % MAXMEMSIZE); + return STOP_WRAP; + } + break; + + case BCD_W: + if (sim_tape_wrp (uptr)) return STOP_MTL; /* locked? */ + if (M[BS] == (BCD_GRPMRK + WM)) return STOP_MTZ;/* eor? */ + if (DEBUG_PRS (mt_dev)) + fprintf (sim_deb, ">>MT%d: write from %d", unit, BS); + ind[IN_TAP] = ind[IN_END] = 0; /* clear error */ + for (tbc = 0; (t = M[BS++]) != (BCD_GRPMRK + WM); ) { + if ((t & WM) && (flag & MD_WM)) /* WM in wm mode? */ + dbuf[tbc++] = BCD_WM; + if (((t & CHAR) == BCD_BLANK) && !(flag & MD_BIN)) + dbuf[tbc++] = BCD_ALT; + else dbuf[tbc++] = t & CHAR; + if (ADDR_ERR (BS)) { /* check next BS */ + BS = BA | (BS % MAXMEMSIZE); + return STOP_WRAP; + } + } + if (DEBUG_PRS (mt_dev)) fprintf (sim_deb, " to %d\n", BS - 1); + passed_eot = sim_tape_eot (uptr); /* passed EOT? */ + st = sim_tape_wrrecf (uptr, dbuf, tbc); /* write record */ + if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ + ind[IN_END] = 1; + if (ADDR_ERR (BS)) { /* check final BS */ + BS = BA | (BS % MAXMEMSIZE); + return STOP_WRAP; + } + break; + + default: + return STOP_INVM; + } + +return mt_map_status (st); +} + +/* Get unit pointer from unit number */ + +UNIT *get_unit (int32 unit) +{ +if ((unit <= 0) || (unit >= MT_NUMDR)) return NULL; +return mt_dev.units + unit; +} + +/* Map tape status */ + +t_stat mt_map_status (t_stat st) +{ +switch (st) { + + case MTSE_OK: /* no error */ + case MTSE_BOT: /* reverse into BOT */ + break; + + case MTSE_FMT: /* illegal fmt */ + return SCPE_IERR; + + case MTSE_UNATT: /* not attached */ + return SCPE_UNATT; + + case MTSE_INVRL: /* invalid rec lnt */ + return SCPE_MTRLNT; + + case MTSE_TMK: /* end of file */ + ind[IN_END] = 1; /* set end mark */ + break; + + case MTSE_IOERR: /* IO error */ + ind[IN_TAP] = 1; /* set error */ + if (iochk) return SCPE_IOERR; + break; + + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + ind[IN_TAP] = 1; /* set error */ + break; + + case MTSE_WRP: /* write protect */ + return STOP_MTL; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +for (i = 0; i < MT_NUMDR; i++) { /* clear pos flag */ + if (uptr = get_unit (i)) MT_CLR_PNU (uptr); + } +ind[IN_END] = ind[IN_TAP] = 0; /* clear indicators */ +return SCPE_OK; +} + +/* Bootstrap routine */ + +t_stat mt_boot (int32 unitno, DEVICE *dptr) +{ +extern int32 saved_IS; + +sim_tape_rewind (&mt_unit[unitno]); /* force rewind */ +BS = 1; /* set BS = 001 */ +mt_io (unitno, MD_WM, BCD_R); /* LDA %U1 001 R */ +saved_IS = 1; +return SCPE_OK; +} diff --git a/I1401/i1401_sys.c b/I1401/i1401_sys.c new file mode 100644 index 0000000..cd4c19d --- /dev/null +++ b/I1401/i1401_sys.c @@ -0,0 +1,412 @@ +/* i1401_sys.c: IBM 1401 simulator interface + + Copyright (c) 1993-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 20-Sep-05 RMS Revised for new code tables + 04-Jan-05 WVS Added address argument support + 14-Nov-04 WVS Added data printout support + 16-Mar-03 RMS Fixed mnemonic for MCS + 03-Jun-02 RMS Added 1311 support + 18-May-02 RMS Added -D feature from Van Snyder + 26-Jan-02 RMS Fixed H, NOP with no trailing wm (found by Van Snyder) + 17-Sep-01 RMS Removed multiconsole support + 13-Jul-01 RMS Fixed bug in symbolic output (found by Peter Schorn) + 27-May-01 RMS Added multiconsole support + 14-Mar-01 RMS Revised load/dump interface (again) + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface +*/ + +#include "i1401_defs.h" +#include + +#define LINE_LNT 80 +extern DEVICE cpu_dev, inq_dev, lpt_dev; +extern DEVICE cdr_dev, cdp_dev, stack_dev; +extern DEVICE dp_dev, mt_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint8 M[]; +extern char ascii_to_bcd_old[128], ascii_to_bcd[128]; +extern char bcd_to_ascii_old[64], bcd_to_ascii_a[64], bcd_to_ascii_h[64]; +extern char *get_glyph (char *cptr, char *gbuf, char term); +extern int32 store_addr_h (int32 addr); +extern int32 store_addr_t (int32 addr); +extern int32 store_addr_u (int32 addr); +extern t_bool conv_old; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 1401"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = LINE_LNT; + +DEVICE *sim_devices[] = { + &cpu_dev, + &inq_dev, + &cdr_dev, + &cdp_dev, + &stack_dev, + &lpt_dev, + &mt_dev, + &dp_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "Non-existent memory", + "Non-existent device", + "No WM at instruction start", + "Invalid A address", + "Invalid B address", + "Invalid instruction length", + "Invalid modifer", + "Invalid branch address", + "Breakpoint", + "HALT instruction", + "Invalid MT unit number", + "Invalid MT record length", + "Write to locked MT unit", + "Skip to unpunched CCT channel", + "Card reader empty", + "Address register wrap", + "I/O check", + "Invalid disk sector address", + "Invalid disk sector count", + "Invalid disk unit", + "Invalid disk function", + "Invalid disk record length", + "Write track while disabled", + "Write check error", + "Disk address miscompare", + "Direct seek cylinder exceeds maximum" + }; + +/* Binary loader -- load carriage control tape + + A carriage control tape consists of entries of the form + + (repeat count) column number,column number,column number,... + + The CCT entries are stored in cct[0:lnt-1], cctlnt contains the + number of entries +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; +t_stat r; +extern int32 cctlnt, cctptr, cct[CCT_LNT]; +char cbuf[CBUFSIZE], gbuf[CBUFSIZE]; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +ptr = 0; +for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ + mask = 0; + if (*cptr == '(') { /* repeat count? */ + cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ + rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */ + if (r != SCPE_OK) return SCPE_FMT; + } + else rpt = 1; + while (*cptr != 0) { /* get col no's */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + col = get_uint (gbuf, 10, 12, &r); /* column number */ + if (r != SCPE_OK) return SCPE_FMT; + mask = mask | (1 << col); /* set bit */ + } + for ( ; rpt > 0; rpt--) { /* store vals */ + if (ptr >= CCT_LNT) return SCPE_FMT; + cctbuf[ptr++] = mask; + } + } +if (ptr == 0) return SCPE_FMT; +cctlnt = ptr; +cctptr = 0; +for (rpt = 0; rpt < cctlnt; rpt++) cct[rpt] = cctbuf[rpt]; +return SCPE_OK; +} + +/* Symbol table */ + +const char *opcode[64] = { + NULL, "R", "W", "WR", "P", "RP", "WP", "WRP", + "SRF", "SPF", NULL, "MA", "MUL", NULL, NULL, NULL, + NULL, "CS", "S", NULL, "MTF", "BWZ", "BBE", NULL, + "MZ", "MCS", NULL, "SWM", "DIV", NULL, NULL, NULL, + NULL, NULL, "SS", "LCA", "MCW", "NOP", NULL, "MCM", + "SAR", NULL, "ZS", NULL, NULL, NULL, NULL, NULL, + NULL, "A", "B", "C", "MN", "MCE", "CC", NULL, + "SBR", NULL, "ZA", "H", "CWM", NULL, NULL, NULL + }; + +/* Print an address from three characters */ + +void fprint_addr (FILE *of, t_value *dig) +{ +int32 addr, xa; +extern int32 hun_table[64], ten_table[64], one_table[64]; + +addr = hun_table[dig[0] & CHAR] + ten_table[dig[1]] + one_table[dig[2]]; +xa = (addr >> V_INDEX) & M_INDEX; +if (xa) fprintf (of, " %d,%d", addr & ADDRMASK, ((xa - (X1 >> V_INDEX)) / 5) + 1); +else if (addr >= MAXMEMSIZE) fprintf (of, " %d*", addr & ADDRMASK); +else fprintf (of, " %d", addr); +return; +} + +/* Print unknown opcode as data */ + +t_stat dcw (FILE *of, int32 op, t_value *val, int32 sw) +{ +int32 i; +t_bool use_h = sw & SWMASK ('F'); + +fprintf (of, "DCW @%c", bcd2ascii (op, use_h)); /* assume it's data */ +for (i = 1; i < sim_emax; i++) { + if (val[i] & WM) break; + fprintf (of, "%c", bcd2ascii (val[i], use_h)); + } +fprintf (of, "@"); +return -(i - 1); /* return # chars */ +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current address + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 op, flags, ilnt, i, t; +int32 wmch = conv_old? '~': '`'; +t_bool use_h = sw & SWMASK ('F'); +extern int32 op_table[64], len_table[9]; + +if (sw & SWMASK ('C')) { /* character? */ + t = val[0]; + if (uptr->flags & UNIT_BCD) { + if (t & WM) fputc (wmch, of); + fputc (bcd2ascii (t & CHAR, use_h), of); + } + else fprintf (of, FMTASC (t & 0177)); + return SCPE_OK; + } +if ((uptr != NULL) && (uptr != &cpu_unit)) /* CPU? */ + return SCPE_ARG; +if (sw & SWMASK ('D')) { /* dump? */ + for (i = 0; i < 50; i++) + fprintf (of, "%c", bcd2ascii (val[i] & CHAR, use_h)) ; + fprintf (of, "\n\t"); + for (i = 0; i < 50; i++) + fprintf (of, (val[i] & WM)? "1": " ") ; + return -(i - 1); + } +if (sw & SWMASK ('S')) { /* string? */ + i = 0; + do { + t = val[i++]; + if (t & WM) fputc (wmch, of); + fputc (bcd2ascii (t & CHAR, use_h), of); + } while ((i < LINE_LNT) && ((val[i] & WM) == 0)); + return -(i - 1); + } +if ((sw & SWMASK ('M')) == 0) return SCPE_ARG; + +if ((val[0] & WM) == 0) return STOP_NOWM; /* WM under op? */ +op = val[0]& CHAR; /* isolate op */ +if (opcode[op] == NULL) return dcw (of, op, val, sw); /* invalid op */ +flags = op_table[op]; /* get flags */ +for (ilnt = 1; ilnt < sim_emax; ilnt++) { /* find inst lnt */ + if (val[ilnt] & WM) break; + } +if ((flags & (NOWM | HNOP)) && (ilnt > 7)) ilnt = 7; /* cs, swm, h, nop? */ +else if ((op == OP_B) && (ilnt > 4) && (val[4] == BCD_BLANK)) ilnt = 4; +else if ((ilnt > 8) && (op != OP_NOP)) ilnt = 8; /* cap length */ +if (ilnt == 3) { /* lnt = 3? */ + fprintf (of, "DSA"); /* assume DSA */ + fprint_addr (of, val); /* print addr */ + return -(ilnt - 1); + } +if ((((flags & len_table[ilnt]) == 0) && /* invalid lnt, */ + (op != OP_NOP)) || /* not nop? */ + (opcode[op] == NULL)) /* or undef? */ + return dcw (of, op, val, sw); +fprintf (of, "%s",opcode[op]); /* print opcode */ +if (ilnt > 2) { /* A address? */ + if (((flags & IO) || (op == OP_NOP)) && (val[1] == BCD_PERCNT)) + fprintf (of, " %%%c%c", bcd2ascii (val[2], use_h), + bcd2ascii (val[3], sw)); + else fprint_addr (of, &val[1]); + } +if (ilnt > 5) fprint_addr (of, &val[4]); /* B address? */ +if ((ilnt == 2) || (ilnt == 5) || (ilnt == 8)) /* d character? */ + fprintf (of, " '%c", bcd2ascii (val[ilnt - 1], use_h)); +return -(ilnt - 1); /* return # chars */ +} + +/* get_addr - get address + index pair */ + +t_stat get_addr (char *cptr, t_value *val) +{ +int32 addr, index; +t_stat r; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, ','); /* get address */ +addr = get_uint (gbuf, 10, MAXMEMSIZE, &r); +if (r != SCPE_OK) return SCPE_ARG; +if (*cptr != 0) { /* more? */ + cptr = get_glyph (cptr, gbuf, ' '); + index = get_uint (gbuf, 10, 3, &r); + if ((r != SCPE_OK) || (index == 0)) return SCPE_ARG; + } +else index = 0; +if (*cptr != 0) return SCPE_ARG; +val[0] = store_addr_h (addr); +val[1] = store_addr_t (addr) | (index << V_ZONE); +val[2] = store_addr_u (addr); +return SCPE_OK; +} + +/* get_io - get I/O address */ + +t_stat get_io (char *cptr, t_value *val) +{ +if ((cptr[0] != '%') || (cptr[3] != 0) || !isalnum (cptr[1]) || + !isalnum (cptr[2])) return SCPE_ARG; +val[0] = BCD_PERCNT; +val[1] = ascii2bcd (cptr[1]); +val[2] = ascii2bcd (cptr[2]); +return SCPE_OK; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 i, op, ilnt, t, cflag, wm_seen; +int32 wmch = conv_old? '~': '`'; +extern int32 op_table[64], len_table[9]; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('C')) || (sw & SWMASK ('S')) || (*cptr == wmch) || + ((*cptr == '\'') && cptr++) || ((*cptr == '"') && cptr++)) { + wm_seen = 0; + for (i = 0; (i < sim_emax) && (*cptr != 0); ) { + t = *cptr++; /* get character */ + if (cflag && (wm_seen == 0) && (t == wmch)) wm_seen = WM; + else if (uptr->flags & UNIT_BCD) { + if (t < 040) return SCPE_ARG; + val[i++] = ascii2bcd (t) | wm_seen; + wm_seen = 0; + } + else val[i++] = t; + } + if ((i == 0) || wm_seen) return SCPE_ARG; + return -(i - 1); + } + +if (cflag == 0) return SCPE_ARG; /* CPU only */ +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (op = 0; op < 64; op++) { /* look it up */ + if (opcode[op] && strcmp (gbuf, opcode[op]) == 0) break; + } +if (op >= 64) return SCPE_ARG; /* successful? */ +val[0] = op | WM; /* store opcode */ +cptr = get_glyph (cptr, gbuf, 0); /* get addr or d */ +if (((op_table[op] && IO) && (get_io (gbuf, &val[1]) == SCPE_OK)) || + (get_addr (gbuf, &val[1]) == SCPE_OK)) { + cptr = get_glyph (cptr, gbuf, 0); /* get addr or d */ + if (get_addr (gbuf, &val[4]) == SCPE_OK) { + cptr = get_glyph (cptr, gbuf, ','); /* get d */ + ilnt = 7; /* a and b addresses */ + } + else ilnt = 4; /* a address */ + } +else ilnt = 1; /* no addresses */ +if ((gbuf[0] == '\'') || (gbuf[0] == '"')) { /* d character? */ + t = gbuf[1]; + if ((gbuf[2] != 0) || (*cptr != 0) || (t < 040)) + return SCPE_ARG; /* end and legal? */ + val[ilnt] = ascii2bcd (t); /* save D char */ + ilnt = ilnt + 1; + } +else if (gbuf[0] != 0) return SCPE_ARG; /* not done? */ +if ((op_table[op] & len_table[ilnt]) == 0) return STOP_INVL; +return -(ilnt - 1); +} + +/* Convert BCD to ASCII */ + +int32 bcd2ascii (int32 c, t_bool use_h) +{ +if (conv_old) return bcd_to_ascii_old[c]; +else if (use_h) return bcd_to_ascii_h[c]; +else return bcd_to_ascii_a[c]; +} + +/* Convert ASCII to BCD */ + +int32 ascii2bcd (int32 c) +{ +if (conv_old) return ascii_to_bcd_old[c]; +else return ascii_to_bcd[c]; +} diff --git a/I1620/i1620_cd.c b/I1620/i1620_cd.c new file mode 100644 index 0000000..df19931 --- /dev/null +++ b/I1620/i1620_cd.c @@ -0,0 +1,441 @@ +/* i1620_cd.c: IBM 1622 card reader/punch + + Copyright (c) 2002-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cdr 1622 card reader + cdp 1622 card punch + + 19-Jan-07 RMS Set UNIT_TEXT flag + 13-Jul-06 RMS Fixed card reader fgets call (from Tom McBride) + Fixed card reader boot sequence (from Tom McBride) + 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility + 25-Apr-03 RMS Revised for extended file support + + Cards are represented as ASCII text streams terminated by newlines. + This allows cards to be created and edited as normal files. +*/ + +#include "i1620_defs.h" + +#define CD_LEN 80 + +extern uint8 M[MAXMEMSIZE]; +extern uint8 ind[NUM_IND]; +extern UNIT cpu_unit; +extern int32 io_stop; + +char cdr_buf[CD_LEN + 2]; +char cdp_buf[CD_LEN + 2]; + +t_stat cdr_reset (DEVICE *dptr); +t_stat cdr_attach (UNIT *uptr, char *cptr); +t_stat cdr_boot (int32 unitno, DEVICE *dptr); +t_stat cdr_read (void); +t_stat cdp_reset (DEVICE *dptr); +t_stat cdp_write (uint32 len); +t_stat cdp_num (uint32 pa, uint32 ndig, t_bool dump); + +/* Card reader data structures + + cdr_dev CDR descriptor + cdr_unit CDR unit descriptor + cdr_reg CDR register list +*/ + +UNIT cdr_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0) + }; + +REG cdr_reg[] = { + { FLDATA (LAST, ind[IN_LAST], 0) }, + { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, + { NULL } + }; + +DEVICE cdr_dev = { + "CDR", &cdr_unit, cdr_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cdr_reset, + &cdr_boot, &cdr_attach, NULL + }; + +/* CDP data structures + + cdp_dev CDP device descriptor + cdp_unit CDP unit descriptor + cdp_reg CDP register list +*/ + +UNIT cdp_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) + }; + +REG cdp_reg[] = { + { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, + { NULL } + }; + +DEVICE cdp_dev = { + "CDP", &cdp_unit, cdp_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cdp_reset, + NULL, NULL, NULL + }; + +/* Data tables. The card reader presents unusual problems. + - Unique codes needed for 11-2-8 (uses !) and 12-7-8 (uses ") . + - Can punch both 11 (-) and 11-0 (uses ]). + On input, the nul and nl generated by C are converted to + spaces; tabs and line feeds are also converted to spaces. + +/* Card reader (ASCII) to numeric (one digit) */ + +const char cdr_to_num[128] = { + 0x00, -1, -1, -1, -1, -1, -1, -1, /* 00 */ + -1, 0x00, 0x00, -1, -1, 0x00, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x1A, 0x0F, 0x0B, 0x1B, 0x0C, 0x00, 0x0C, /* !"#$%&' */ + 0x0C, 0x0C, 0x1C, 0x00, 0x0B, 0x10, 0x1B, 0x01, /* ()*+,-./ */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */ + 0x08, 0x09, 0x00, 0x1E, 0x1E, 0x0B, 0x0E, 0x1A, /* 89:;<=>? */ + 0x0C, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* @ABCDEFG */ + 0x08, 0x09, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* HIJKLMNO */ + 0x17, 0x18, 0x19, 0x02, 0x03, 0x04, 0x05, 0x06, /* PQRSTUVW */ + 0x07, 0x08, 0x09, 0x00, 0x0E, 0x10, 0x0A, 0x1F, /* XYZ[\]^_ */ + -1, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* `abcdefg */ + 0x08, 0x09, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, /* hijklmno */ + 0x17, 0x18, 0x19, 0x02, 0x03, 0x04, 0x05, 0x06, /* pqrstuvw */ + 0x07, 0x08, 0x09, 0x0F, 0x0A, 0x1F, 0x00, -1 /* xyz{|}~ */ + }; + +/* Numeric (flag + digit) to card punch (ASCII) */ + +const char num_to_cdp[32] = { + '0', '1', '2', '3', '4', '5', '6', '7', /* 0 */ + '8', '9', '|', ',', ' ', '"', ' ', '"', + ']', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* F + 0 */ + 'Q', 'R', '!', '$', -1, -1, -1, '"' + }; + +/* Card reader (ASCII) to alphameric (two digits) + + 11-2-8 (!) reads as 5A + 11-7-8 (_) reads as 5F + 12-2-8 (?) reads inconsistently (here 02) + 12-6-8 (<) reads inconsistently (here 5E) + 12-7-8 (}) reads as 5F +*/ + +const char cdr_to_alp[128] = { + 0x00, -1, -1, -1, -1, -1, -1, -1, /* 00 */ + -1, 0x00, 0x00, -1, -1, 0x00, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x5A, 0x0F, 0x33, 0x13, 0x24, 0x10, 0x34, /* !"#$%&' */ + 0x24, 0x04, 0x14, 0x10, 0x23, 0x20, 0x03, 0x21, /* ()*+,-./ */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 01234567 */ + 0x78, 0x79, 0x70, 0x5E, 0x5E, 0x33, 0x0E, 0x02, /* 89:;<=>? */ + 0x34, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */ + 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* HIJKLMNO */ + 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* PQRSTUVW */ + 0x67, 0x68, 0x69, 0x40, 0x0E, 0x50, 0x0A, 0x5F, /* XYZ[\]^_ */ + 0x50, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */ + 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* hijklmno */ + 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* pqrstuvw */ + 0x67, 0x68, 0x69, 0x0F, 0x0A, 0x5F, 0x60, -1 /* xyz{|}~ */ + }; + +/* Alphameric (two digits) to card punch (ASCII). Oddities: + + 02 -> 12-2-8 (?), symmetric + 07 -> 12-7-8 (}), reads as 5F + 12 -> 11-2-8 (!), reads as 5A + 15 -> 11,0 (`), reads as 50 + 22 -> 0-2-8 (|), reads as 0A + 32 -> 2-8 (^), reads as 0A + 5B -> 11-3-8 (=), reads as 13 + 6A -> 0-2-8 (|), reads as 0A + 6B -> 0-3-8 (,), reads as 23 + AA -> 0-2-8 (|), reads as 0A + + There is no way to punch 0-5-8 (~), 0-6-8 (\), + 11-5-8 (]), 11-6-8 (;), 11-7-8 (_), + 12-5-8 ([), or 12-6-8 (<) +*/ + +const char alp_to_cdp[256] = { + ' ', -1, '?', '.', ')', -1, -1, '}', /* 00 */ + -1, -1, '\'', -1, -1, -1, -1, '"', + '+', -1, '!', '$', '*', ']', -1, -1, /* 10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, '^', '=', '@', ':', ' ', -1, /* 30 */ + -1, -1, '|', -1, -1, -1, -1, '"', + -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */ + 'H', 'I', -1, -1, -1, -1, -1, -1, + '_', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */ + 'Q', 'R', '?', '=', -1, -1, -1, '}', + -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */ + 'Y', 'Z', '|', ',', -1, -1, -1, -1, + '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */ + '8', '9', -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ + -1, -1, '|', -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ + -1, -1, -1, -1, -1, -1, -1, -1 + }; + +/* Card reader IO routine + + - Hard errors stop the operation and halt the system. + - Invalid characters place a blank in memory and set RDCHK. + If IO stop is set, the system halts at the end of the operation. +*/ + +t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +int32 i; +int8 cdc; +t_stat r, sta; + +sta = SCPE_OK; /* assume ok */ +switch (op) { /* case on op */ + + case OP_RN: /* read numeric */ + r = cdr_read (); /* fill reader buf */ + if (r != SCPE_OK) return r; /* error? */ + for (i = 0; i < CD_LEN; i++) { /* transfer to mem */ + cdc = cdr_to_num[cdr_buf[i]]; /* translate */ + if (cdc < 0) { /* invalid? */ + ind[IN_RDCHK] = 1; /* set read check */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + cdc = 0; + } + M[pa] = cdc; /* store digit */ + PP (pa); /* incr mem addr */ + } + break; + + case OP_RA: /* read alphameric */ + r = cdr_read (); /* fill reader buf */ + if (r != SCPE_OK) return r; /* error? */ + for (i = 0; i < CD_LEN; i++) { /* transfer to mem */ + cdc = cdr_to_alp[cdr_buf[i]]; /* translate */ + if (cdc < 0) { /* invalid? */ + ind[IN_RDCHK] = 1; /* set read check */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + cdc = 0; + }; + M[pa] = (M[pa] & FLAG) | (cdc & DIGIT); /* store 2 digits */ + M[pa - 1] = (M[pa - 1] & FLAG) | ((cdc >> 4) & DIGIT); + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + break; + + default: /* invalid function */ + return STOP_INVFNC; + } + +return sta; +} + +/* Fill card reader buffer - all errors are hard errors */ + +t_stat cdr_read (void) +{ +int32 i; + +ind[IN_LAST] = 0; /* clear last card */ +if ((cdr_unit.flags & UNIT_ATT) == 0) { /* attached? */ + ind[IN_RDCHK] = 1; /* no, error */ + return SCPE_UNATT; + } + +for (i = 0; i < CD_LEN + 2; i++) cdr_buf[i] = ' '; /* clear buffer */ +fgets (cdr_buf, CD_LEN + 2, cdr_unit.fileref); /* read card */ +if (feof (cdr_unit.fileref)) return STOP_NOCD; /* eof? */ +if (ferror (cdr_unit.fileref)) { /* error? */ + ind[IN_RDCHK] = 1; /* set read check */ + perror ("CDR I/O error"); + clearerr (cdr_unit.fileref); + return SCPE_IOERR; + } +cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ +getc (cdr_unit.fileref); /* see if more */ +if (feof (cdr_unit.fileref)) ind[IN_LAST] = 1; /* eof? set last */ +fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); /* "backspace" */ +return SCPE_OK; +} + +/* Card reader attach */ + +t_stat cdr_attach (UNIT *uptr, char *cptr) +{ +ind[IN_LAST] = 0; /* clear last card */ +return attach_unit (uptr, cptr); +} + +/* Card reader reset */ + +t_stat cdr_reset (DEVICE *dptr) +{ +ind[IN_LAST] = 0; /* clear last card */ +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 0 + +t_stat cdr_boot (int32 unitno, DEVICE *dptr) +{ +t_stat r; +int32 old_io_stop; +extern int32 saved_PC; + +old_io_stop = io_stop; +io_stop = 1; +r = cdr (OP_RN, 0, 0, 0); /* read card @ 0 */ +io_stop = old_io_stop; +if (r != SCPE_OK) return r; /* error? */ +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* Card punch IO routine + + - Hard errors stop the operation and halt the system. + - Invalid characters stop the operation and set WRCHK. + If IO stop is set, the system halts. +*/ + +t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +int32 i; +int8 cdc; +uint8 z, d; + +switch (op) { /* decode op */ + + case OP_DN: + return cdp_num (pa, 20000 - (pa % 20000), TRUE); /* dump numeric */ + + case OP_WN: + return cdp_num (pa, CD_LEN, FALSE); /* write numeric */ + + case OP_WA: + for (i = 0; i < CD_LEN; i++) { /* one card */ + d = M[pa] & DIGIT; /* get digit pair */ + z = M[pa - 1] & DIGIT; + cdc = alp_to_cdp[(z << 4) | d]; /* translate */ + if (cdc < 0) { /* bad char? */ + ind[IN_WRCHK] = 1; /* set write check */ + CRETIOE (io_stop, STOP_INVCHR); + } + cdp_buf[i] = cdc; /* store in buf */ + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + return cdp_write (CD_LEN); /* punch buffer */ + + default: /* invalid function */ + break; + } + +return STOP_INVFNC; +} + +/* Punch card numeric */ + +t_stat cdp_num (uint32 pa, uint32 ndig, t_bool dump) +{ +int32 i, ncd, len; +uint8 d; +int8 cdc; +t_stat r; + +ncd = ndig / CD_LEN; /* number of cards */ +while (ncd-- >= 0) { /* until done */ + len = (ncd >= 0)? CD_LEN: (ndig % CD_LEN); /* card length */ + if (len == 0) break; + for (i = 0; i < len; i++) { /* one card */ + d = M[pa] & (FLAG | DIGIT); /* get char */ + if (dump && (d == FLAG)) cdc = '-'; /* dump? F+0 is diff */ + else cdc = num_to_cdp[d]; /* translate */ + if (cdc < 0) { /* bad char? */ + ind[IN_WRCHK] = 1; /* set write check */ + CRETIOE (io_stop, STOP_INVCHR); /* stop */ + } + cdp_buf[i] = cdc; /* store in buf */ + PP (pa); /* incr mem addr */ + } + r = cdp_write (len); /* punch card */ + if (r != SCPE_OK) return r; /* error? */ + } +return SCPE_OK; +} + +/* Write punch card buffer - all errors are hard errors */ + +t_stat cdp_write (uint32 len) +{ +if ((cdp_unit.flags & UNIT_ATT) == 0) { /* attached? */ + ind[IN_WRCHK] = 1; /* no, error */ + return SCPE_UNATT; + } + +while ((len > 0) && (cdp_buf[len - 1] == ' ')) --len; /* trim spaces */ +cdp_buf[len] = '\n'; /* newline, null */ +cdp_buf[len + 1] = 0; + +fputs (cdp_buf, cdp_unit.fileref); /* write card */ +cdp_unit.pos = ftell (cdp_unit.fileref); /* count char */ +if (ferror (cdp_unit.fileref)) { /* error? */ + ind[IN_WRCHK] = 1; + perror ("CDP I/O error"); + clearerr (cdp_unit.fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset card punch */ + +t_stat cdp_reset (DEVICE *dptr) +{ +return SCPE_OK; +} diff --git a/I1620/i1620_cpu.c b/I1620/i1620_cpu.c new file mode 100644 index 0000000..eba47e6 --- /dev/null +++ b/I1620/i1620_cpu.c @@ -0,0 +1,2059 @@ +/* i1620_cpu.c: IBM 1620 CPU simulator + + Copyright (c) 2002-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This CPU module incorporates code and comments from the 1620 simulator by + Geoff Kuenning, with his permission. + + 28-May-06 RMS Fixed bug in cpu history (found by Peter Schorn) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Nov-04 RMS Added instruction history + 26-Mar-04 RMS Fixed warnings with -std=c99 + 02-Nov-03 RMS Fixed bug in branch digit (found by Dave Babcock) + 21-Aug-03 RMS Fixed bug in immediate index add (found by Michael Short) + 25-Apr-03 RMS Changed t_addr to uint32 throughout + 18-Oct-02 RMS Fixed bugs in invalid result testing (found by Hans Pufal) + + The simulated register state for the IBM 1620 is: + + 1620 sim comment + + IR1 [PC] program counter + IR2 instruction register 2 (subroutine return address) + OR1 [QAR] Q address + OR2 [PAR] P address + PR1 manual save address + ind[0:99] indicators + + Additional internal registers OR3, PR2, and PR3 are not simulated. + + The IBM 1620 is a fixed instruction length, variable data length, decimal + data system. Memory consists of 20000 - 60000 BCD digits, each containing + four bits of data and a flag. There are no general registers; all + instructions are memory to memory. + + The 1620 uses a fixed, 12 digit instruction format: + + oo ppppp qqqqq + + where + + oo = opcode + ppppp = P (usually destination) address + qqqqq = Q (usually source) address + + Immediate instructions use the qqqqq field as the second operand. + + The 1620 Model 1 uses table lookups for add and multiply; for that reason, + it was nicknamed CADET (Can't Add, Doesn't Even Try). The Model 2 does + adds in hardware and uses the add table memory for index registers. + + This routine is the instruction decode routine for the IBM 1620. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + illegal addresses or instruction formats + I/O error in I/O simulator + + 2. Interrupts. The 1620 has no interrupt structure. + + 3. Non-existent memory. On the 1620, all memory references + are modulo the memory size. + + 4. Adding I/O devices. These modules must be modified: + + i1620_cpu.c add iodisp table entry + i1620_sys.c add sim_devices table entry +*/ + +#include "i1620_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = saved_PC + +#define HIST_PC 0x40000000 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + uint16 vld; + uint16 pc; + uint8 inst[INST_LEN]; + } InstHistory; + +uint8 M[MAXMEMSIZE] = { 0 }; /* main memory */ +uint32 saved_PC = 0; /* saved PC */ +uint32 IR2 = 1; /* inst reg 2 */ +uint32 PAR = 0; /* P address */ +uint32 QAR = 0; /* Q address */ +uint32 PR1 = 1; /* proc reg 1 */ +uint32 iae = 1; /* ind addr enb */ +uint32 idxe = 0; /* index enable */ +uint32 idxb = 0; /* index band */ +uint32 io_stop = 1; /* I/O stop */ +uint32 ar_stop = 1; /* arith stop */ +int32 ind_max = 16; /* iadr nest limit */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ +uint8 ind[NUM_IND] = { 0 }; /* indicators */ + +extern int32 sim_int_char; +extern int32 sim_interval; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern FILE *sim_log; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); + +int32 get_2d (uint32 ad); +t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *addr); +t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val); +t_stat get_idx (uint32 aidx); +t_stat xmt_field (uint32 d, uint32 s, uint32 skp); +t_stat xmt_record (uint32 d, uint32 s, t_bool cpy); +t_stat xmt_index (uint32 d, uint32 s); +t_stat xmt_divd (uint32 d, uint32 s); +t_stat xmt_tns (uint32 d, uint32 s); +t_stat xmt_tnf (uint32 d, uint32 s); +t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta); +uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry); +t_stat mul_field (uint32 mpc, uint32 mpy); +t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last); +t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez); +t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max, uint32 *quod, uint32 *quop); +t_stat oct_to_dec (uint32 tbl, uint32 s); +t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez); +t_stat or_field (uint32 d, uint32 s); +t_stat and_field (uint32 d, uint32 s); +t_stat xor_field (uint32 d, uint32 s); +t_stat com_field (uint32 d, uint32 s); +void upd_ind (void); + +extern t_stat tty (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat ptp (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat ptr (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat cdp (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat cdr (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat btp (uint32 op, uint32 pa, uint32 f0, uint32 f1); +extern t_stat btr (uint32 op, uint32 pa, uint32 f0, uint32 f1); + +extern t_stat fp_add (uint32 d, uint32 s, t_bool sub); +extern t_stat fp_mul (uint32 d, uint32 s); +extern t_stat fp_div (uint32 d, uint32 s); +extern t_stat fp_fsl (uint32 d, uint32 s); +extern t_stat fp_fsr (uint32 d, uint32 s); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BCD+MI_STD, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { DRDATA (PC, saved_PC, 16), PV_LEFT }, + { DRDATA (IR2, IR2, 16), PV_LEFT }, + { DRDATA (PR1, PR1, 16), PV_LEFT }, + { DRDATA (PAR, PAR, 16), PV_LEFT + REG_RO }, + { DRDATA (QAR, QAR, 16), PV_LEFT + REG_RO }, + { FLDATA (SW1, ind[IN_SW1], 0) }, + { FLDATA (SW2, ind[IN_SW2], 0) }, + { FLDATA (SW3, ind[IN_SW3], 0) }, + { FLDATA (SW4, ind[IN_SW4], 0) }, + { FLDATA (HP, ind[IN_HP], 0) }, + { FLDATA (EZ, ind[IN_EZ], 0) }, + { FLDATA (OVF, ind[IN_OVF], 0) }, + { FLDATA (EXPCHK, ind[IN_EXPCHK], 0) }, + { FLDATA (RDCHK, ind[IN_RDCHK], 0) }, + { FLDATA (WRCHK, ind[IN_WRCHK], 0) }, + { FLDATA (ARSTOP, ar_stop, 0) }, + { FLDATA (IOSTOP, io_stop, 0) }, + { BRDATA (IND, ind, 10, 1, NUM_IND) }, + { FLDATA (IAE, iae, 0) }, + { FLDATA (IDXE, idxe, 0) }, + { FLDATA (IDXB, idxb, 0) }, + { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, + { BRDATA (PCQ, pcq, 10, 14, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { IF_IA, IF_IA, "IA", "IA", &cpu_set_opt1 }, + { IF_IA, 0, "no IA", "NOIA", &cpu_set_opt1 }, + { IF_EDT, IF_EDT, "EDT", "EDT", &cpu_set_opt1 }, + { IF_EDT, 0, "no EDT", "NOEDT", &cpu_set_opt1 }, + { IF_DIV, IF_DIV, "DIV", "DIV", &cpu_set_opt1 }, + { IF_DIV, 0, "no DIV", "NODIV", &cpu_set_opt1 }, + { IF_FP, IF_FP, "FP", "FP", NULL }, + { IF_FP, 0, "no FP", "NOFP", NULL }, + { IF_BIN, IF_BIN, "BIN", "BIN", &cpu_set_opt2 }, + { IF_BIN, 0, "no BIN", "NOBIN", &cpu_set_opt2 }, + { IF_IDX, IF_IDX, "IDX", "IDX", &cpu_set_opt2 }, + { IF_IDX, 0, "no IDX", "NOIDX", &cpu_set_opt2 }, + { IF_MII, IF_MII, "Model 2", "MOD2", &cpu_set_model }, + { IF_MII, 0, "Model 1", "MOD1", &cpu_set_model }, + { UNIT_MSIZE, 20000, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 40000, NULL, "40K", &cpu_set_size }, + { UNIT_MSIZE, 60000, NULL, "60K", &cpu_set_size }, + { UNIT_MSIZE, 0, NULL, "SAVE", &cpu_set_save }, + { UNIT_MSIZE, 0, NULL, "TABLE", &cpu_set_table }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 18, 1, 16, 5, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL + }; + +/* Instruction table */ + +const int32 op_table[100] = { + 0, /* 0 */ + IF_FP + IF_VPA + IF_VQA, /* FADD */ + IF_FP + IF_VPA + IF_VQA, /* FSUB */ + IF_FP + IF_VPA + IF_VQA, /* FMUL */ + 0, + IF_FP + IF_VPA + IF_VQA, /* FSL */ + IF_FP + IF_MII + IF_VPA + IF_VQA, /* TFL */ + IF_FP + IF_MII + IF_VPA + IF_VQA, /* BTFL */ + IF_FP + IF_VPA + IF_VQA, /* FSR */ + IF_FP + IF_VPA + IF_VQA, /* FDV */ + IF_MII + IF_VPA + IF_IMM, /* 10: BTAM */ + IF_VPA + IF_IMM, /* AM */ + IF_VPA + IF_IMM, /* SM */ + IF_VPA + IF_IMM, /* MM */ + IF_VPA + IF_IMM, /* CM */ + IF_VPA + IF_IMM, /* TDM */ + IF_VPA + IF_IMM, /* TFM */ + IF_VPA + IF_IMM, /* BTM */ + IF_DIV + IF_VPA + IF_IMM, /* LDM */ + IF_DIV + IF_VPA + IF_IMM, /* DM */ + IF_MII + IF_VPA + IF_VQA, /* 20: BTA */ + IF_VPA + IF_VQA, /* A */ + IF_VPA + IF_VQA, /* S */ + IF_VPA + IF_VQA, /* M */ + IF_VPA + IF_VQA, /* C */ + IF_VPA + IF_VQA, /* TD */ + IF_VPA + IF_VQA, /* TF */ + IF_VPA + IF_VQA, /* BT */ + IF_DIV + IF_VPA + IF_VQA, /* LD */ + IF_DIV + IF_VPA + IF_VQA, /* D */ + IF_MII + IF_VPA + IF_VQA, /* 30: TRNM */ + IF_VPA + IF_VQA, /* TR */ + IF_VPA, /* SF */ + IF_VPA, /* CF */ + IF_VPA, /* K */ + IF_VPA, /* DN */ + IF_VPA, /* RN */ + IF_VPA, /* RA */ + IF_VPA, /* WN */ + IF_VPA, /* WA */ + 0, /* 40 */ + 0, /* NOP */ + 0, /* BB */ + IF_VPA + IF_VQA, /* BD */ + IF_VPA + IF_VQA, /* BNF */ + IF_VPA + IF_VQA, /* BNR */ + IF_VPA, /* BI */ + IF_VPA, /* BNI */ + 0, /* H */ + IF_VPA, /* B */ + 0, /* 50 */ + 0, + 0, + 0, + 0, + IF_VPA + IF_VQA, /* BNG - disk sys */ + 0, + 0, + 0, + 0, + IF_MII + IF_VPA, /* 60: BS */ + IF_IDX + IF_VPA + IF_NQX, /* BX */ + IF_IDX + IF_VPA + IF_IMM, /* BXM */ + IF_IDX + IF_VPA + IF_NQX, /* BCX */ + IF_IDX + IF_VPA + IF_IMM, /* BCXM */ + IF_IDX + IF_VPA + IF_NQX, /* BLX */ + IF_IDX + IF_VPA + IF_IMM, /* BLXM */ + IF_IDX + IF_VPA + IF_NQX, /* BSX */ + 0, + 0, + IF_IDX + IF_VPA + IF_VQA, /* 70: MA */ + IF_EDT + IF_VPA + IF_VQA, /* MF */ + IF_EDT + IF_VPA + IF_VQA, /* MF */ + IF_EDT + IF_VPA + IF_VQA, /* TNF */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, /* 80 */ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + IF_BIN + IF_VPA + IF_4QA, /* 90: BBT */ + IF_BIN + IF_VPA + IF_4QA, /* BMK */ + IF_BIN + IF_VPA + IF_VQA, /* ORF */ + IF_BIN + IF_VPA + IF_VQA, /* ANDF */ + IF_BIN + IF_VPA + IF_VQA, /* CPLF */ + IF_BIN + IF_VPA + IF_VQA, /* EORF */ + IF_BIN + IF_VPA + IF_VQA, /* OTD */ + IF_BIN + IF_VPA + IF_VQA, /* DTO */ + 0, + 0 + }; + +/* IO dispatch table */ + +t_stat (*iodisp[NUM_IO])(uint32 op, uint32 pa, uint32 f0, uint32 f1) = { + NULL, &tty, &ptp, &ptr, &cdp, /* 00 - 09 */ + &cdr, NULL, &dp, NULL, &lpt, + NULL, NULL, NULL, NULL, NULL, /* 10 - 19 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, /* 20 - 29 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, &btp, &btr, NULL, /* 30 - 39 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, /* 40 - 49 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, /* 50 - 59 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, /* 60 - 69 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, /* 70 - 79 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, /* 80 - 89 */ + NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, /* 90 - 99 */ + NULL, NULL, NULL, NULL, NULL + }; + +/* Indicator table: -1 = illegal, +1 = resets when tested */ + +const int32 ind_table[NUM_IND] = { + -1, 0, 0, 0, 0, -1, 1, 1, -1, 1, /* 00 - 09 */ + -1, 0, 0, 0, 1, 1, 1, 1, -1, 0, /* 10 - 19 */ + -1, -1, -1, -1, -1, 0, -1, -1, -1, -1, /* 20 - 29 */ + 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, /* 30 - 39 */ + -1, -1, 1, -1, -1, -1, -1, -1, -1, -1, /* 40 - 49 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 - 59 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 - 69 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 - 79 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 - 89 */ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 /* 90 - 99 */ + }; + +/* Add table for 1620 Model 1 */ + +const uint8 std_add_table[ADD_TABLE_LEN] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, + 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, + 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, + 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, + 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 + }; + +/* Add table for 1620 Model 2 ("hardware add") */ + +const uint8 sum_table[20] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19 + }; + +/* Multiply table */ + +const uint8 std_mul_table[MUL_TABLE_LEN] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 0, 2, 0, 3, 0, 4, 0, + 0, 0, 2, 0, 4, 0, 6, 0, 8, 0, + 0, 0, 3, 0, 6, 0, 9, 0, 2, 1, + 0, 0, 4, 0, 8, 0, 2, 1, 6, 1, + 0, 0, 5, 0, 0, 1, 5, 1, 0, 2, + 0, 0, 6, 0, 2, 1, 8, 1, 4, 2, + 0, 0, 7, 0, 4, 1, 1, 2, 8, 2, + 0, 0, 8, 0, 6, 1, 4, 2, 2, 3, + 0, 0, 9, 0, 8, 1, 7, 2, 6, 3, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 5, 0, 6, 0, 7, 0, 8, 0, 9, 0, + 0, 1, 2, 1, 4, 1, 6, 1, 8, 1, + 5, 1, 8, 1, 1, 2, 4, 2, 7, 2, + 0, 2, 4, 2, 8, 2, 2, 3, 6, 3, + 5, 2, 0, 3, 5, 3, 0, 4, 5, 4, + 0, 3, 6, 3, 2, 4, 8, 4, 4, 5, + 5, 3, 2, 4, 9, 4, 6, 5, 3, 6, + 0, 4, 8, 4, 6, 5, 4, 6, 2, 7, + 5, 4, 4, 5, 3, 6, 2, 7, 1, 8 + }; + +#define BRANCH(x) PCQ_ENTRY; PC = (x) +#define GET_IDXADDR(x) ((idxb? IDX_B: IDX_A) + ((x) * ADDR_LEN) + (ADDR_LEN - 1)) + +t_stat sim_instr (void) +{ +uint32 PC, pla, qla, f0, f1; +int32 i, t, idx, flags, sta, dev, op; +t_stat reason; + +/* Restore saved state */ + +PC = saved_PC; +if ((cpu_unit.flags & IF_IA) == 0) iae = 0; +if ((cpu_unit.flags & IF_IDX) == 0) idxe = idxb = 0; +upd_ind (); /* update indicators */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + saved_PC = PC; /* commit prev instr */ + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + sim_interval = sim_interval - 1; + +/* Instruction fetch and address decode */ + + if (PC & 1) { /* PC odd? */ + reason = STOP_INVIAD; /* stop */ + break; + } + + op = get_2d (PC); /* get opcode */ + if (op < 0) { /* invalid? */ + reason = STOP_INVINS; + break; + } + flags = op_table[op]; /* get op, flags */ + if ((flags & ALLOPT) && /* need option? */ + !(flags & ALLOPT & cpu_unit.flags)) { /* any set? */ + reason = STOP_INVINS; /* no, error */ + break; + } + + pla = ADDR_A (PC, I_PL); /* P last addr */ + qla = ADDR_A (PC, I_QL); /* Q last addr */ + if (flags & IF_VPA) { /* need P? */ + reason = get_addr (pla, 5, TRUE, &PAR); /* get P addr */ + if (reason != SCPE_OK) break; /* stop if error */ + } + if (flags & (IF_VQA | IF_4QA | IF_NQX)) { /* need Q? */ + reason = get_addr (qla, /* get Q addr */ + ((flags & IF_4QA)? 4: 5), /* 4 or 5 digits */ + ((flags & IF_NQX)? FALSE: TRUE), /* not or indexed */ + &QAR); + if (reason != SCPE_OK) { /* stop if invalid */ + reason = reason + (STOP_INVQDG - STOP_INVPDG); + break; + } + } + else if (flags & IF_IMM) QAR = qla; /* immediate? */ + + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) hst_p = 0; + hst[hst_p].vld = 1; + hst[hst_p].pc = PC; + for (i = 0; i < INST_LEN; i++) + hst[hst_p].inst[i] = M[(PC + i) % MEMSIZE]; + } + + PC = PC + INST_LEN; /* advance PC */ + switch (op) { /* case on op */ + +/* Transmit digit - P,Q are valid */ + + case OP_TD: + case OP_TDM: + M[PAR] = M[QAR] & (FLAG | DIGIT); /* move dig, flag */ + break; + +/* Transmit field - P,Q are valid */ + + case OP_TF: + case OP_TFM: + reason = xmt_field (PAR, QAR, 1); /* xmit field */ + break; + +/* Transmit record - P,Q are valid */ + + case OP_TR: + reason = xmt_record (PAR, QAR, TRUE); /* xmit record */ + break; + +/* Transmit record no record mark - P,Q are valid */ + + case OP_TRNM: + reason = xmt_record (PAR, QAR, FALSE); /* xmit record but */ + break; /* not rec mark */ + +/* Set flag - P is valid */ + + case OP_SF: + M[PAR] = M[PAR] | FLAG; /* set flag on P */ + break; + +/* Clear flag - P is valid */ + + case OP_CF: + M[PAR] = M[PAR] & ~FLAG; /* clear flag on P */ + break; + +/* Branch - P is valid */ + + case OP_B: + BRANCH (PAR); /* branch to P */ + break; + +/* Branch and transmit - P,Q are valid */ + + case OP_BT: + case OP_BTM: + reason = xmt_field (ADDR_S (PAR, 1), QAR, 1); /* xmit field to P-1 */ + IR2 = PC; /* save PC */ + BRANCH (PAR); /* branch to P */ + break; + +/* Branch and transmit floating - P,Q are valid */ + + case OP_BTFL: + reason = xmt_field (ADDR_S (PAR, 1), QAR, 3); /* skip 3 flags */ + IR2 = PC; /* save PC */ + BRANCH (PAR); /* branch to P */ + break; + +/* Branch and transmit address - P,Q are valid */ + + case OP_BTA: + case OP_BTAM: + reason = xmt_field (ADDR_S (PAR, 1), QAR, 4); /* skip 4 flags */ + IR2 = PC; /* save PC */ + BRANCH (PAR); /* branch to P */ + break; + +/* Branch back */ + + case OP_BB: + if (PR1 != 1) { /* PR1 valid? */ + BRANCH (PR1); /* return to PR1 */ + PR1 = 1; /* invalidate */ + } + else if (IR2 != 1) { /* IR2 valid? */ + BRANCH (IR2); /* return to IR2 */ + IR2 = 1; /* invalidate */ + } + else reason = STOP_INVRTN; /* MAR check */ + break; + +/* Branch on digit (not zero) - P,Q are valid */ + + case OP_BD: + if ((M[QAR] & DIGIT) != 0) { /* digit != 0? */ + BRANCH (PAR); /* branch */ + } + break; + +/* Branch no flag - P,Q are valid */ + + case OP_BNF: + if ((M[QAR] & FLAG) == 0) { /* flag == 0? */ + BRANCH (PAR); /* branch */ + } + break; + +/* Branch no record mark (8-2 not set) - P,Q are valid */ + + case OP_BNR: + if ((M[QAR] & REC_MARK) != REC_MARK) { /* not rec mark? */ + BRANCH (PAR); /* branch */ + } + break; + +/* Branch no group mark - P,Q are valid */ + + case OP_BNG: + if ((M[QAR] & DIGIT) != GRP_MARK) { /* not grp mark? */ + BRANCH (PAR); /* branch */ + } + break; + +/* Branch (no) indicator - P is valid */ + + case OP_BI: + case OP_BNI: + upd_ind (); /* update indicators */ + t = get_2d (ADDR_A (saved_PC, I_BR)); /* get ind number */ + if ((t < 0) || (ind_table[t] < 0)) { /* not valid? */ + reason = STOP_INVIND; /* stop */ + break; + } + if ((ind[t] != 0) ^ (op == OP_BNI)) { /* ind value correct? */ + BRANCH (PAR); /* branch */ + } + if (ind_table[t] > 0) ind[t] = 0; /* reset if needed */ + break; + +/* Add/subtract/compare - P,Q are valid */ + + case OP_A: + case OP_AM: + reason = add_field (PAR, QAR, FALSE, TRUE, 0, &sta); /* add, store */ + if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */ + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + break; + + case OP_S: + case OP_SM: + reason = add_field (PAR, QAR, TRUE, TRUE, 0, &sta); /* sub, store */ + if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */ + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + break; + + case OP_C: + case OP_CM: + reason = add_field (PAR, QAR, TRUE, FALSE, 0, &sta); /* sub, nostore */ + if (sta == ADD_CARRY) ind[IN_OVF] = 1; /* cout => ovflo */ + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + break; + +/* Multiply - P,Q are valid */ + + case OP_M: + case OP_MM: + reason = mul_field (PAR, QAR); /* multiply */ + break; + +/* IO instructions - P is valid */ + + case OP_RA: + case OP_WA: + if ((PAR & 1) == 0) { /* P even? */ + reason = STOP_INVEAD; /* stop */ + break; + } + case OP_K: + case OP_DN: + case OP_RN: + case OP_WN: + dev = get_2d (ADDR_A (saved_PC, I_IO)); /* get IO dev */ + f0 = M[ADDR_A (saved_PC, I_CTL)] & DIGIT; /* get function */ + f1 = M[ADDR_A (saved_PC, I_CTL + 1)] & DIGIT; + if ((dev < 0) || (iodisp[dev] == NULL)) /* undefined dev? */ + reason = STOP_INVIO; /* stop */ + else reason = iodisp[dev] (op, PAR, f0, f1); /* call device */ + break; + +/* Divide special feature instructions */ + + case OP_LD: + case OP_LDM: + for (i = 0; i < PROD_AREA_LEN; i++) /* clear prod area */ + M[PROD_AREA + i] = 0; + t = M[QAR] & FLAG; /* save Q sign */ + reason = xmt_divd (PAR, QAR); /* xmit dividend */ + M[PROD_AREA + PROD_AREA_LEN - 1] |= t; /* set sign */ + break; + +/* Divide - P,Q are valid */ + + case OP_D: + case OP_DM: + reason = div_field (PAR, QAR, &t); /* divide */ + ind[IN_EZ] = t; /* set indicator */ + if ((reason == STOP_OVERFL) && !ar_stop) /* ovflo stop? */ + reason = SCPE_OK; /* no */ + break; + +/* Edit special feature instructions */ + +/* Move flag - P,Q are valid */ + + case OP_MF: + M[PAR] = (M[PAR] & ~FLAG) | (M[QAR] & FLAG); /* copy Q flag */ + M[QAR] = M[QAR] & ~FLAG; /* clr Q flag */ + break; + +/* Transmit numeric strip - P,Q are valid, P is source */ + + case OP_TNS: + if ((PAR & 1) == 0) { /* P must be odd */ + reason = STOP_INVEAD; + break; + } + reason = xmt_tns (QAR, PAR); /* xmit and strip */ + break; + +/* Transmit numeric fill - P,Q are valid */ + + case OP_TNF: + if ((PAR & 1) == 0) { /* P must be odd */ + reason = STOP_INVEAD; + break; + } + reason = xmt_tnf (PAR, QAR); /* xmit and strip */ + break; + +/* Index special feature instructions */ + +/* Move address - P,Q are valid */ + + case OP_MA: + for (i = 0; i < ADDR_LEN; i++) { /* move 5 digits */ + M[PAR] = (M[PAR] & FLAG) | (M[QAR] & DIGIT); + MM (PAR); MM (QAR); + } + break; + +/* Branch load index - P,Q are valid, Q not indexed */ + + case OP_BLX: + case OP_BLXM: + idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ + if (idx < 0) { /* disabled? */ + reason = STOP_INVIDX; /* stop */ + break; + } + xmt_index (GET_IDXADDR (idx), QAR); /* copy Q to idx */ + BRANCH (PAR); /* branch to P */ + break; + +/* Branch store index - P,Q are valid, Q not indexed */ + + case OP_BSX: + idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ + if (idx < 0) { /* disabled? */ + reason = STOP_INVIDX; /* stop */ + break; + } + xmt_index (QAR, GET_IDXADDR (idx)); /* copy idx to Q */ + BRANCH (PAR); /* branch to P */ + break; + +/* Branch and modify index - P,Q are valid, Q not indexed */ + + case OP_BX: + idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ + if (idx < 0) { /* disabled? */ + reason = STOP_INVIDX; /* stop */ + break; + } + reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta); + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + BRANCH (PAR); /* branch to P */ + break; + + case OP_BXM: + idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ + if (idx < 0) { /* disabled? */ + reason = STOP_INVIDX; /* stop */ + break; + } + reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta); + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + BRANCH (PAR); /* branch to P */ + break; + +/* Branch conditionally and modify index - P,Q are valid, Q not indexed */ + + case OP_BCX: + idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ + if (idx < 0) { /* disabled? */ + reason = STOP_INVIDX; /* stop */ + break; + } + reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 0, &sta); + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */ + BRANCH (PAR); /* branch */ + } + break; + + case OP_BCXM: + idx = get_idx (ADDR_A (saved_PC, I_QL - 1)); /* get index */ + if (idx < 0) { /* disabled? */ + reason = STOP_INVIDX; /* stop */ + break; + } + reason = add_field (GET_IDXADDR (idx), QAR, FALSE, TRUE, 3, &sta); + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + if ((ind[IN_EZ] == 0) && (sta == ADD_NOCRY)) { /* ~z, ~c, ~sign chg? */ + BRANCH (PAR); /* branch */ + } + break; + +/* Branch and select - P is valid */ + + case OP_BS: + t = M[ADDR_A (saved_PC, I_SEL)] & DIGIT; /* get select */ + switch (t) { /* case on select */ + case 0: + idxe = idxb = 0; /* indexing off */ + break; + case 1: + idxe = 1; idxb = 0; /* index band A */ + break; + case 2: + idxe = idxb = 1; /* index band B */ + break; + case 8: + iae = 0; /* indirect off */ + break; + case 9: + iae = 1; /* indirect on */ + break; + default: + reason = STOP_INVSEL; /* undefined */ + break; + } + BRANCH (PAR); + break; + +/* Binary special feature instructions */ + +/* Branch on bit - P,Q are valid, Q is 4d address */ + + case OP_BBT: + t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */ + if (t & M[QAR] & DIGIT) { /* match to mem? */ + BRANCH (PAR); /* branch */ + } + break; + +/* Branch on mask - P,Q are valid, Q is 4d address */ + + case OP_BMK: + t = M[ADDR_A (saved_PC, I_Q)]; /* get Q0 digit */ + if (((t ^ M[QAR]) & /* match to mem? */ + ((t & FLAG)? (FLAG + DIGIT): DIGIT)) == 0) { + BRANCH (PAR); /* branch */ + } + break; + +/* Or - P,Q are valid */ + + case OP_ORF: + reason = or_field (PAR, QAR); /* OR fields */ + break; + +/* AND - P,Q are valid */ + + case OP_ANDF: + reason = and_field (PAR, QAR); /* AND fields */ + break; + +/* Exclusive or - P,Q are valid */ + + case OP_EORF: + reason = xor_field (PAR, QAR); /* XOR fields */ + break; + +/* Complement - P,Q are valid */ + + case OP_CPLF: + reason = com_field (PAR, QAR); /* COM field */ + break; + +/* Octal to decimal - P,Q are valid */ + + case OP_OTD: + reason = oct_to_dec (PAR, QAR); /* convert */ + break; + +/* Decimal to octal - P,Q are valid */ + + case OP_DTO: + reason = dec_to_oct (PAR, QAR, &t); /* convert */ + ind[IN_EZ] = t; /* set indicator */ + if (ar_stop && ind[IN_OVF]) reason = STOP_OVERFL; + break; + +/* Floating point special feature instructions */ + + case OP_FADD: + reason = fp_add (PAR, QAR, FALSE); /* add */ + if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; + break; + + case OP_FSUB: + reason = fp_add (PAR, QAR, TRUE); /* subtract */ + if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; + break; + + case OP_FMUL: + reason = fp_mul (PAR, QAR); /* multiply */ + if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; + break; + + case OP_FDIV: + reason = fp_div (PAR, QAR); /* divide */ + if (ar_stop && ind[IN_OVF]) reason = STOP_FPDVZ; + if (ar_stop && ind[IN_EXPCHK]) reason = STOP_EXPCHK; + break; + + case OP_FSL: + reason = fp_fsl (PAR, QAR); /* shift left */ + break; + + case OP_FSR: + reason = fp_fsr (PAR, QAR); /* shift right */ + break; + +/* Halt */ + + case OP_H: + saved_PC = PC; /* commit inst */ + reason = STOP_HALT; /* stop */ + break; + +/* NOP */ + + case OP_NOP: + break; + +/* Invalid instruction code */ + + default: + reason = STOP_INVINS; /* stop */ + break; + } /* end switch */ + } /* end while */ + +/* Simulation halted */ + +pcq_r->qptr = pcq_p; /* update pc q ptr */ +upd_ind (); +return reason; +} + +/* Utility routines */ + +/* Get 2 digit field + + Inputs: + ad = address of high digit + Outputs: + val = field converted to binary + -1 if bad digit +*/ + +int32 get_2d (uint32 ad) +{ +int32 d, d1; + +d = M[ad] & DIGIT; /* get 1st digit */ +d1 = M[ADDR_A (ad, 1)] & DIGIT; /* get 2nd digit */ +if (BAD_DIGIT (d) || BAD_DIGIT (d1)) return -1; /* bad? error */ +return ((d * 10) + d1); /* cvt to binary */ +} + +/* Get address routine + + Inputs: + alast = address of low digit + lnt = length + indexok = TRUE if indexing allowed + &addr = pointer to address output + Output: + return = error status (in terms of P address) + addr = address converted to binary + + Notes: + - If indexing produces a negative result, the effective address is + the 10's complement of the result + - An address that exceeds memory produces a MAR check stop +*/ + +t_stat get_addr (uint32 alast, int32 lnt, t_bool indexok, uint32 *reta) +{ +uint8 indir; +int32 cnt, idx, idxa, idxv, addr; + +if (iae) indir = FLAG; /* init indirect */ +else indir = 0; + +cnt = 0; /* count depth */ +do { + indir = indir & M[alast]; /* get indirect */ + if (cvt_addr (alast, lnt, FALSE, &addr)) /* cvt addr to bin */ + return STOP_INVPDG; /* bad? */ + idx = get_idx (ADDR_S (alast, 1)); /* get index reg num */ + if (indexok && (idx > 0)) { /* indexable? */ + idxa = GET_IDXADDR (idx); /* get idx reg addr */ + if (cvt_addr (idxa, ADDR_LEN, TRUE, &idxv)) /* cvt idx reg */ + return STOP_INVPDG; + addr = addr + idxv; /* add in index */ + if (addr < 0) addr = addr + 100000; /* -? 10's comp */ + } + if (addr >= (int32) MEMSIZE) return STOP_INVPAD; /* invalid addr? */ + alast = addr; /* new address */ + lnt = ADDR_LEN; /* std len */ + } while (indir && (cnt++ < ind_max)); +if (cnt > ind_max) return STOP_INVPIA; /* indir too deep? */ +*reta = addr; /* return address */ +return SCPE_OK; +} + +/* Convert address to binary + + Inputs: + alast = address of low digit + lnt = length + signok = TRUE if signed + val = address of output + Outputs: + status = 0 if ok, != 0 if error +*/ + +t_stat cvt_addr (uint32 alast, int32 lnt, t_bool signok, int32 *val) +{ +int32 sign = 0, addr = 0, t; + +if (signok && (M[alast] & FLAG)) sign = 1; /* signed? */ +alast = alast - lnt; /* find start */ +do { + PP (alast); /* incr mem addr */ + t = M[alast] & DIGIT; /* get digit */ + if (BAD_DIGIT (t)) return STOP_INVDIG; /* bad? error */ + addr = (addr * 10) + t; /* cvt to bin */ + } while (--lnt > 0); +if (sign) *val = -addr; /* minus? */ +else *val = addr; +return SCPE_OK; +} + +/* Get index register number + + Inputs: + aidx = address of low digit + Outputs: + index = >0 if indexed + =0 if not indexed + <0 if indexing disabled +*/ + +t_stat get_idx (uint32 aidx) +{ +int32 i, idx; + +if (idxe == 0) return -1; /* indexing off? */ +for (i = idx = 0; i < 3; i++) { /* 3 flags worth */ + if (M[aidx] & FLAG) idx = idx | (1 << i); /* test flag */ + MM (aidx); /* next digit */ + } +return idx; +} + +/* Update indicators routine */ + +void upd_ind (void) +{ +ind[IN_HPEZ] = ind[IN_HP] | ind[IN_EZ]; /* HPEZ = HP | EZ */ +ind[IN_DERR] = ind[IN_DACH] | ind[IN_DWLR] | ind[IN_DCYO]; +ind[IN_ANYCHK] = ind[IN_RDCHK] | ind[IN_WRCHK] | /* ANYCHK = all chks */ + ind[IN_MBREVEN] | ind[IN_MBRODD] | + ind[IN_PRCHK] | ind[IN_DACH]; +ind[IN_IXN] = ind[IN_IXA] = ind[IN_IXB] = 0; /* clr index indics */ +if (!idxe) ind[IN_IXN] = 1; /* off? */ +else if (!idxb) ind[IN_IXA] = 1; /* on, band A? */ +else ind[IN_IXB] = 1; /* no, band B */ +return; +} + +/* Transmit routines */ + +/* Transmit field from 's' to 'd' - ignore first 'skp' flags */ + +t_stat xmt_field (uint32 d, uint32 s, uint32 skp) +{ +uint32 cnt = 0; +uint8 t; + +do { + t = M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ + MM (d); MM (s); /* decr mem addrs */ + if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while (((t & FLAG) == 0) || (cnt <= skp)); /* until flag */ +return SCPE_OK; +} + +/* Transmit record from 's' to 'd' - copy record mark if 'cpy' = TRUE */ + +t_stat xmt_record (uint32 d, uint32 s, t_bool cpy) +{ +uint32 cnt = 0; + +while ((M[s] & REC_MARK) != REC_MARK) { /* until rec mark */ + M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ + PP (d); PP (s); /* incr mem addrs */ + if (cnt++ >= MEMSIZE) return STOP_RWRAP; /* (stop runaway) */ + } +if (cpy) M[d] = M[s] & (FLAG | DIGIT); /* copy rec mark */ +return SCPE_OK; +} + +/* Transmit index from 's' to 'd' - fixed five character field */ + +t_stat xmt_index (uint32 d, uint32 s) +{ +int32 i; + +M[d] = M[s] & (FLAG | DIGIT); /* preserve sign */ +MM (d); MM (s); /* decr mem addrs */ +for (i = 0; i < ADDR_LEN - 2; i++) { /* copy 3 digits */ + M[d] = M[s] & DIGIT; /* without flags */ + MM (d); MM (s); /* decr mem addrs */ + } +M[d] = (M[s] & DIGIT) | FLAG; /* set flag on last */ +return SCPE_OK; +} + +/* Transmit dividend from 'd' to 's' - clear flag on first digit */ + +t_stat xmt_divd (uint32 d, uint32 s) +{ +uint32 cnt = 0; + +M[d] = M[s] & DIGIT; /* first w/o flag */ +do { + MM (d); MM (s); /* decr mem addrs */ + M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ + if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while ((M[d] & FLAG) == 0); /* until src flag */ +return SCPE_OK; +} + +/* Transmit numeric strip from 's' to 'd' - s is odd */ + +t_stat xmt_tns (uint32 d, uint32 s) +{ +uint32 cnt = 0; +uint8 t, z; + +t = M[s] & DIGIT; /* get units */ +z = M[s - 1] & DIGIT; /* get zone */ +if ((z == 1) || (z == 5) || ((z == 2) && (t == 0))) /* 1x, 5x, 20? */ + M[d] = t | FLAG; /* set flag */ +else M[d] = t; /* else clear flag */ +do { + MM (d); /* decr mem addrs */ + s = ADDR_S (s, 2); + t = M[d] & FLAG; /* save dst flag */ + M[d] = M[s] & (FLAG | DIGIT); /* copy src to dst */ + if (cnt >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + cnt = cnt + 2; + } while (t == 0); /* until dst flag */ +M[d] = M[d] | FLAG; /* set flag at end */ +return SCPE_OK; +} + +/* Transmit numeric fill from 's' to 'd' - d is odd */ + +t_stat xmt_tnf (uint32 d, uint32 s) +{ +uint32 cnt = 0; +uint8 t; + +t = M[s]; /* get 1st digit */ +M[d] = t & DIGIT; /* store */ +M[d - 1] = (t & FLAG)? 5: 7; /* set sign from flag */ +do { + MM (s); /* decr mem addr */ + d = ADDR_S (d, 2); + t = M[s]; /* get src digit */ + M[d] = t & DIGIT; /* move to dst, no flag */ + M[d - 1] = 7; /* set zone */ + if (cnt >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + cnt = cnt + 2; + } while ((t & FLAG) == 0); /* until src flag */ +return SCPE_OK; +} + +/* Add routine + + Inputs: + d = destination field low (P) + s = source field low (Q) + sub = TRUE if subtracting + sto = TRUE if storing + skp = number of source field flags, beyond sign, to ignore + Output: + return = status + sta = ADD_NOCRY: no carry out, no sign change + ADD_SCHNG: sign change + ADD_CARRY: carry out + + Reference Manual: "When the sum is zero, the sign of the P field + is retained." +*/ + +t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta) +{ +uint32 cry, src, dst, res, comp, dp, dsv; +uint32 src_f = 0, cnt = 0, dst_f; + +*sta = ADD_NOCRY; /* assume no cry */ +dsv = d; /* save dst */ +comp = ((M[d] ^ M[s]) & FLAG) ^ (sub? FLAG: 0); /* set compl flag */ +cry = 0; /* clr carry */ +ind[IN_HP] = ((M[d] & FLAG) == 0); /* set sign from res */ +ind[IN_EZ] = 1; /* assume zero */ + +dst = M[d] & DIGIT; /* 1st digits */ +src = M[s] & DIGIT; +if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */ + return STOP_INVDIG; +if (comp) src = 10 - src; /* complement? */ +res = add_one_digit (dst, src, &cry); /* add */ +if (sto) M[d] = (M[d] & FLAG) | res; /* store */ +MM (d); MM (s); /* decr mem addrs */ +do { + dst = M[d] & DIGIT; /* get dst digit */ + dst_f = M[d] & FLAG; /* get dst flag */ + if (src_f) src = 0; /* src done? src = 0 */ + else { + src = M[s] & DIGIT; /* get src digit */ + if (cnt >= skp) src_f = M[s] & FLAG; /* get src flag */ + MM (s); /* decr src addr */ + } + if (BAD_DIGIT (dst) || BAD_DIGIT (src)) /* bad digit? */ + return STOP_INVDIG; + if (comp) src = 9 - src; /* complement? */ + res = add_one_digit (dst, src, &cry); /* add */ + if (sto) M[d] = dst_f | res; /* store */ + MM (d); /* decr dst addr */ + if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while (dst_f == 0); /* until dst done */ +if (!src_f) ind[IN_OVF] = 1; /* !src done? ovf */ +if (comp && !cry && !ind[IN_EZ]) { /* recomp needed? */ + ind[IN_HP] = ind[IN_HP] ^ 1; /* flip indicator */ + if (sto) { /* storing? */ + for (cry = 1, dp = dsv; dp != d; ) { /* rescan */ + dst = M[dp] & DIGIT; /* get dst digit */ + res = add_one_digit (9 - dst, 0, &cry); /* "add" */ + M[dp] = (M[dp] & FLAG) | res; /* store */ + MM (dp); /* decr dst addr */ + } + M[dsv] = M[dsv] ^ FLAG; /* compl sign */ + } + *sta = ADD_SIGNC; /* sign changed */ + return SCPE_OK; + } /* end if recomp */ +if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */ +if (!comp && cry) *sta = ADD_CARRY; /* set status */ +return SCPE_OK; +} + +/* Add one digit via table (Model 1) or "hardware" (Model 2) */ + +uint32 add_one_digit (uint32 dst, uint32 src, uint32 *cry) +{ +uint32 res; + +if (*cry) src = src + 1; /* cry in? incr src */ +if (src >= 10) { /* src > 10? */ + src = src - 10; /* src -= 10 */ + *cry = 1; /* carry out */ + } +else *cry = 0; /* else no carry */ +if (cpu_unit.flags & IF_MII) /* Model 2? */ + res = sum_table[dst + src]; /* "hardware" */ +else res = M[ADD_TABLE + (dst * 10) + src]; /* table lookup */ +if (res & FLAG) *cry = 1; /* carry out? */ +if (res & DIGIT) ind[IN_EZ] = 0; /* nz? clr ind */ +return res & DIGIT; +} + +/* Multiply routine + + Inputs: + mpc = multiplicand address + mpy = multiplier address + Outputs: + return = status + + Reference manual: "A zero product may have a negative or positive sign, + depending on the signs of the fields at the P and Q addresses." +*/ + +t_stat mul_field (uint32 mpc, uint32 mpy) +{ +int32 i; +uint32 pro; /* prod pointer */ +uint32 mpyd, mpyf; /* mpy digit, flag */ +uint32 cnt = 0; /* counter */ +uint8 sign; /* final sign */ +t_stat r; + +PR1 = 1; /* step on PR1 */ +for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */ + M[PROD_AREA + i] = 0; +sign = (M[mpc] & FLAG) ^ (M[mpy] & FLAG); /* get final sign */ +ind[IN_HP] = (sign == 0); /* set indicators */ +ind[IN_EZ] = 1; +pro = PROD_AREA + PROD_AREA_LEN - 1; /* product ptr */ + +/* Loop on multiplier (mpy) and product (pro) digits */ + +do { + mpyd = M[mpy] & DIGIT; /* multiplier digit */ + mpyf = (M[mpy] & FLAG) && (cnt != 0); /* last digit flag */ + if (BAD_DIGIT (mpyd)) return STOP_INVDIG; /* bad? */ + r = mul_one_digit (mpyd, mpc, pro, mpyf); /* prod += mpc*mpy_dig */ + if (r != SCPE_OK) return r; /* error? */ + MM (mpy); MM (pro); /* decr mpyr, prod addrs */ + if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while ((mpyf == 0) || (cnt <= 1)); /* until mpyr flag */ + +if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */ +M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set final sign */ +return SCPE_OK; +} + +/* Multiply step + + Inputs: + mpyd = multiplier digit (tested valid) + mpcp = multiplicand low address + prop = product low address + last = last iteration flag (set flag on high product) + Outputs: + prod += multiplicand * multiplier_digit + return = status + + The multiply table address is constructed as follows: + - double the multiplier digit + - use the 10's digit of the doubled result, + 1, as the 100's digit + of the table address + - use the multiplicand digit as the 10's digit of the table address + - use the unit digit of the doubled result as the unit digit of the + table address + EZ indicator is cleared if a non-zero digit is ever generated +*/ + +t_stat mul_one_digit (uint32 mpyd, uint32 mpcp, uint32 prop, uint32 last) +{ +uint32 mpta, mptb; /* mult table */ +uint32 mptd; /* mult table digit */ +uint32 mpcd, mpcf; /* mpc digit, flag */ +uint32 prwp; /* prod working ptr */ +uint32 prod; /* product digit */ +uint32 cry; /* carry */ +uint32 mpcc, cryc; /* counters */ + +mptb = MUL_TABLE + ((mpyd <= 4)? (mpyd * 2): /* set mpy table 100's, */ + (((mpyd - 5) * 2) + 100)); /* 1's digits */ + +/* Inner loop on multiplicand (mpcp) and product (prop) digits */ + +mpcc = 0; /* multiplicand ctr */ +do { + prwp = prop; /* product working ptr */ + mpcd = M[mpcp] & DIGIT; /* multiplicand digit */ + mpcf = M[mpcp] & FLAG; /* multiplicand flag */ + if (BAD_DIGIT (mpcd)) return STOP_INVDIG; /* bad? */ + mpta = mptb + (mpcd * 10); /* mpy table 10's digit */ + cry = 0; /* init carry */ + mptd = M[mpta] & DIGIT; /* mpy table digit */ + if (BAD_DIGIT (mptd)) return STOP_INVDIG; /* bad? */ + prod = M[prwp] & DIGIT; /* product digit */ + if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */ + M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */ + MM (prwp); /* decr working ptr */ + mptd = M[mpta + 1] & DIGIT; /* mpy table digit */ + if (BAD_DIGIT (mptd)) return STOP_INVDIG; /* bad? */ + prod = M[prwp] & DIGIT; /* product digit */ + if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */ + M[prwp] = add_one_digit (prod, mptd, &cry); /* add mpy tbl to prod */ + cryc = 0; /* (stop runaway) */ + while (cry) { /* propagate carry */ + MM (prwp); /* decr working ptr */ + prod = M[prwp] & DIGIT; /* product digit */ + if (BAD_DIGIT (prod)) return STOP_INVDIG; /* bad? */ + M[prwp] = add_one_digit (prod, 0, &cry); /* add cry */ + if (cryc++ > MEMSIZE) return STOP_FWRAP; + } + MM (mpcp); MM (prop); /* decr mpc, prod ptrs */ + if (mpcc++ > MEMSIZE) return STOP_FWRAP; + } while ((mpcf == 0) || (mpcc <= 1)); /* until mpcf flag */ +if (last) M[prop] = M[prop] | FLAG; /* flag high product */ +return SCPE_OK; +} + +/* Divide routine - comments from Geoff Kuenning's 1620 simulator + + The destination of the divide is given by: + + 100 - <# digits in quotient> + + Which is more easily calculated as: + + 100 - <# digits in divisor> - <# digits in dividend> + + The quotient goes into 99 minus the divisor length. The + remainder goes into 99. The load dividend instruction (above) + should have specified a P address of 99 minus the size of the + divisor. + + Note that this all implies that "dest" points to the *leftmost* + digit of the dividend. + + After the division, the assumed decimal point will be as many + positions to the left as there are digits in the divisor. In + other words, a 4-digit divisor will produce 4 (assumed) decimal + places. + + There are other ways to do these things. In particular, the + load-dividend instruction doesn't have to specify the above + formula; if it's done differently, then you don't have to get + decimal places. This is not well-explained in the books I have. + + How to divide on a 1620: + + The dividend is the field at 99: + + 90 = _1234567890 + + The divisor is somewhere else in memory: + + _03 + + The divide operation specifies the left-most digit of the + dividend as the place to begin trial subtractions: + + DM 90,3 + + The loop works as follows: + + 1. Call the left-most digit of the dividend "current_dividend". + Call the location current_dividend - + "quotient_digit". + 2. Clear the flag at current_dividend, and set one at + quotient_digit. + + 88 = _001234567890, q_d = 88, c_d = 90 + [Not actually done; divisor length controls subtract.] + 3. Subtract the divisor from the field at current-dividend, + using normal 1620 rules, except that signs are ignored. + Continue these subtractions until either 10 subtractions + have been done, or you get a negative result: + + 88 = _00_2234567890, q_d = 88, c_d = 90 + 4. If 10 subtractions have been done, set the overflow + indicator and abort. Otherwise, add the divisor back to + correct for the oversubtraction: + + 88 = _001234567890, q_d = 88, c_d = 90 + 5. Store the (net) number of subtractions in quotient_digit: + + 88 = _001234567890, q_d = 88, c_d = 90 + 6. If this is not the first pass, clear the flag at + quotient_digit. Increment quotient_digit and + current_dividend, and set a flag at the new + quotient_digit: + + 88 = _0_01234567890, q_d = 89, c_d = 91 + [If first pass, set a flag at quotient digit.] + 7. If current_dividend is not 100, repeat steps 3 through 7. + 8. Set flags at 99 and quotient_digit - 1 according to the + rules of algebra: the quotient's sign is the exclusive-or + of the signs of the divisor and dividend, and the + remainder has the sign of the dividend: + + 10 / 3 = 3 remainder 1 + 10 / -3 = -3 remainder 1 + -10 / 3 = -3 remainder -1 + -10 / -3 = 3 remainder -1 + + This preserves the relationship dd = q * dv + r. + + Our example continues as follows for steps 3 through 7: + + 3. 88 = _0_00_334567890, q_d = 89, c_d = 91 + 4. 88 = _0_00034567890 + 5. 88 = _0_40034567890 + 6. 88 = _04_0034567890, q_d = 90, c_d = 92 + 3. 88 = _04_00_34567890 + 4. 88 = _04_0004567890 + 5. 88 = _04_1004567890 + 6. 88 = _041_004567890, q_d = 91, c_d = 93 + 3. 88 = _041_00_2567890 + 4. 88 = _041_001567890 + 5. 88 = _041_101567890 + 6. 88 = _0411_01567890, q_d = 92, c_d = 94 + 3. 88 = _0411_00_367890 + 4. 88 = _0411_00067890 + 5. 88 = _0411_50067890 + 6. 88 = _04115_0067890, q_d = 93, c_d = 95 + 3. 88 = _04115_00_37890 + 4. 88 = _04115_0007890 + 5. 88 = _04115_2007890 + 6. 88 = _041152_007890, q_d = 94, c_d = 96 + 3. 88 = _041152_00_2890 + 4. 88 = _041152_001890 + 5. 88 = _041152_201890 + 6. 88 = _0411522_01890, q_d = 95, c_d = 97 + 3. 88 = _0411522_00_390 + 4. 88 = _0411522_00090 + 5. 88 = _0411522_60090 + 6. 88 = _04115226_0090, q_d = 96, c_d = 98 + 3. 88 = _04115226_00_30 + 4. 88 = _04115226_0000 + 5. 88 = _04115226_3000 + 6. 88 = _041152263_000, q_d = 97, c_d = 99 + 3. 88 = _041152263_00_3 + 4. 88 = _041152263_000 + 5. 88 = _041152263_000 + 6. 88 = _0411522630_00, q_d = 98, c_d = 100 + + In the actual code below, we elide several of these steps in + various ways for convenience and efficiency. + + Note that the EZ indicator is NOT valid for divide, because it + is cleared by any non-zero result in an intermediate add. The + code maintains its own EZ indicator for the quotient. +*/ + +t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez) +{ +uint32 quop, quod, quos; /* quo ptr, dig, sign */ +uint32 dvds; /* dvd sign */ +t_bool first = TRUE; /* first pass */ +t_stat r; + +dvds = (M[PROD_AREA + PROD_AREA_LEN - 1]) & FLAG; /* dividend sign */ +quos = dvds ^ (M[dvr] & FLAG); /* quotient sign */ +ind[IN_HP] = (quos == 0); /* set indicators */ +*ez = 1; + +/* Loop on current dividend, high order digit at dvd */ + +do { + r = div_one_digit (dvd, dvr, 10, &quod, &quop); /* dev quo digit */ + if (r != SCPE_OK) return r; /* error? */ + +/* Store quotient digit and advance current dividend pointer */ + + if (first) { /* first pass? */ + if (quod >= 10) { /* overflow? */ + ind[IN_OVF] = 1; /* set indicator */ + return STOP_OVERFL; /* stop */ + } + M[quop] = FLAG | quod; /* set flag on quo */ + first = FALSE; + } + else M[quop] = quod; /* store quo digit */ + if (quod) *ez = 0; /* if nz, clr ind */ + PP (dvd); /* incr dvd ptr */ + } while (dvd != (PROD_AREA + PROD_AREA_LEN)); /* until end prod */ + +/* Division done. Set signs of quo, rem, set flag on high order remainder */ + +if (*ez) ind[IN_HP] = 0; /* res = 0? clr HP */ +M[PROD_AREA + PROD_AREA_LEN - 1] |= dvds; /* remainder sign */ +M[quop] = M[quop] | quos; /* quotient sign */ +PP (quop); /* high remainder */ +M[quop] = M[quop] | FLAG; /* set flag */ +return SCPE_OK; +} + +/* Divide step + + Inputs: + dvd = current dividend address (high digit) + dvr = divisor address (low digit) + max = max number of iterations before overflow + &quod = address to store quotient digit + &quop = address to store quotient pointer (can be NULL) + Outputs: + return = status + + Divide step calculates a quotient digit by repeatedly subtracting the + divisor from the current dividend. The divisor's length controls the + subtraction; dividend flags are ignored. +*/ + +t_stat div_one_digit (uint32 dvd, uint32 dvr, uint32 max, + uint32 *quod, uint32 *quop) +{ +uint32 dvrp, dvrd, dvrf; /* dvr ptr, dig, flag */ +uint32 dvdp, dvdd; /* dvd ptr, dig */ +uint32 qd, cry; /* quo dig, carry */ +uint32 cnt; + +for (qd = 0; qd < max; qd++) { /* devel quo dig */ + dvrp = dvr; /* divisor ptr */ + dvdp = dvd; /* dividend ptr */ + cnt = 0; + cry = 1; /* carry in = 1 */ + do { /* sub dvr fm dvd */ + dvdd = M[dvdp] & DIGIT; /* dividend digit */ + if (BAD_DIGIT (dvdd)) return STOP_INVDIG; /* bad? */ + dvrd = M[dvrp] & DIGIT; /* divisor digit */ + dvrf = M[dvrp] & FLAG; /* divisor flag */ + if (BAD_DIGIT (dvrd)) return STOP_INVDIG; /* bad? */ + M[dvdp] = add_one_digit (dvdd, 9 - dvrd, &cry); /* sub */ + MM (dvdp); MM (dvrp); /* decr ptrs */ + if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */ + if (!cry) { /* !cry = borrow */ + dvdd = M[dvdp] & DIGIT; /* borrow digit */ + if (BAD_DIGIT (dvdd)) return STOP_INVDIG; /* bad? */ + M[dvdp] = add_one_digit (dvdd, 9, &cry); /* sub */ + } + if (!cry) break; /* !cry = negative */ + } + +/* Add back the divisor to correct for the negative result */ + +dvrp = dvr; /* divisor ptr */ +dvdp = dvd; /* dividend ptr */ +cnt = 0; +cry = 0; /* carry in = 0 */ +do { + dvdd = M[dvdp] & DIGIT; /* dividend digit */ + dvrd = M[dvrp] & DIGIT; /* divisor digit */ + dvrf = M[dvrp] & FLAG; /* divisor flag */ + M[dvdp] = add_one_digit (dvdd, dvrd, &cry); /* add */ + MM (dvdp); MM (dvrp); cnt++; /* decr ptrs */ + } while ((dvrf == 0) || (cnt <= 1)); /* until dvr flag */ +if (cry) { /* carry out? */ + dvdd = M[dvdp] & DIGIT; /* borrow digit */ + M[dvdp] = add_one_digit (dvdd, 0, &cry); /* add */ + } +if (quop != NULL) *quop = dvdp; /* set quo addr */ +*quod = qd; /* set quo digit */ +return SCPE_OK; +} + +/* Logical operation routines (and, or, xor, complement) + + Inputs: + d = destination address + s = source address + Output: + return = status + + Destination flags are preserved; EZ reflects the result. + COM does not obey normal field length restrictions. +*/ + +t_stat or_field (uint32 d, uint32 s) +{ +uint32 cnt = 0; +int32 t; + +ind[IN_EZ] = 1; /* assume result zero */ +do { + t = M[s]; /* get src */ + M[d] = (M[d] & FLAG) | ((M[d] | t) & 07); /* OR src to dst */ + if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */ + MM (d); MM (s); /* decr pointers */ + if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */ +return SCPE_OK; +} + +t_stat and_field (uint32 d, uint32 s) +{ +uint32 cnt = 0; +int32 t; + +ind[IN_EZ] = 1; /* assume result zero */ +do { + t = M[s]; /* get src */ + M[d] = (M[d] & FLAG) | ((M[d] & t) & 07); /* AND src to dst */ + if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */ + MM (d); MM (s); /* decr pointers */ + if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */ +return SCPE_OK; +} + +t_stat xor_field (uint32 d, uint32 s) +{ +uint32 cnt = 0; +int32 t; + +ind[IN_EZ] = 1; /* assume result zero */ +do { + t = M[s]; /* get src */ + M[d] = (M[d] & FLAG) | ((M[d] ^ t) & 07); /* XOR src to dst */ + if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */ + MM (d); MM (s); /* decr pointers */ + if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while (((t & FLAG) == 0) || (cnt <= 1)); /* until src flag */ +return SCPE_OK; +} + +t_stat com_field (uint32 d, uint32 s) +{ +uint32 cnt = 0; +int32 t; + +ind[IN_EZ] = 1; /* assume result zero */ +do { + t = M[s]; /* get src */ + M[d] = (t & FLAG) | ((t ^ 07) & 07); /* comp src to dst */ + if (M[d] & DIGIT) ind[IN_EZ] = 0; /* nz dig? clr ind */ + MM (d); MM (s); /* decr pointers */ + if (cnt++ >= MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while ((t & FLAG) == 0); /* until src flag */ +return SCPE_OK; +} + +/* Octal to decimal + + Inputs: + tbl = conversion table address (low digit) + s = source address + Outputs: + product area = converted source + result = status + + OTD is a cousin of multiply. The octal digits in the source are + multiplied by successive values in the conversion table, and the + results are accumulated in the product area. Although the manual + does not say, this code assumes that EZ and HP are affected. + */ + +t_stat oct_to_dec (uint32 tbl, uint32 s) +{ +uint32 cnt = 0, tblc; +uint32 i, sd, sf, tf, sign; +t_stat r; + +for (i = 0; i < PROD_AREA_LEN; i++) /* clr prod area */ + M[PROD_AREA + i] = 0; +sign = M[s] & FLAG; /* save sign */ +ind[IN_EZ] = 1; /* set indicators */ +ind[IN_HP] = (sign == 0); +do { + sd = M[s] & DIGIT; /* src digit */ + sf = M[s] & FLAG; /* src flag */ + r = mul_one_digit (sd, tbl, PROD_AREA + PROD_AREA_LEN - 1, sf); + if (r != SCPE_OK) return r; /* err? */ + MM (s); /* decr src addr */ + MM (tbl); /* skip 1st tbl dig */ + tblc = 0; /* count */ + do { + tf = M[tbl] & FLAG; /* get next */ + MM (tbl); /* decr ptr */ + if (tblc++ > MEMSIZE) return STOP_FWRAP; + } while (tf == 0); /* until flag */ + if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while (sf == 0); +if (ind[IN_EZ]) ind[IN_HP] = 0; /* res = 0? clr HP */ +M[PROD_AREA + PROD_AREA_LEN - 1] |= sign; /* set sign */ +return SCPE_OK; +} + +/* Decimal to octal + + Inputs: + d = destination address + tbl = conversion table address (low digit of highest power) + &ez = address of soft EZ indicator + product area = field to convert + Outputs: + return = status + + DTO is a cousin to divide. The number in the product area is repeatedly + divided by successive values in the conversion table, and the quotient + digits are stored in the destination. Although the manual does not say, + this code assumes that EZ and HP are affected. + */ + +t_stat dec_to_oct (uint32 d, uint32 tbl, int32 *ez) +{ +uint32 sign, octd, t; +t_bool first = TRUE; +uint32 ctr = 0; +t_stat r; + +sign = M[PROD_AREA + PROD_AREA_LEN - 1] & FLAG; /* input sign */ +*ez = 1; /* set indicators */ +ind[IN_HP] = (sign == 0); +for ( ;; ) { + r = div_one_digit (PROD_AREA + PROD_AREA_LEN - 1, /* divide */ + tbl, 8, &octd, NULL); + if (r != SCPE_OK) return r; /* error? */ + if (first) { /* first pass? */ + if (octd >= 8) { /* overflow? */ + ind[IN_OVF] = 1; /* set indicator */ + return SCPE_OK; /* stop */ + } + M[d] = FLAG | octd; /* set flag on quo */ + first = FALSE; + } + else M[d] = octd; /* store quo digit */ + if (octd) *ez = 0; /* if nz, clr ind */ + PP (tbl); /* incr tbl addr */ + if ((M[tbl] & REC_MARK) == REC_MARK) break; /* record mark? */ + PP (tbl); /* skip flag */ + if ((M[tbl] & REC_MARK) == REC_MARK) break; /* record mark? */ + do { /* look for F, rec mk */ + PP (tbl); + t = M[tbl]; + } while (((t & FLAG) == 0) && ((t & REC_MARK) != REC_MARK)); + MM (tbl); /* step back one */ + PP (d); /* incr quo addr */ + if (ctr++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } +if (*ez) ind[IN_HP] = 0; /* res = 0? clr HP */ +M[d] = M[d] | sign; /* set result sign */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int32 i; +static t_bool one_time = TRUE; + +PR1 = IR2 = 1; /* invalidate PR1,IR2 */ +ind[0] = 0; +for (i = IN_SW4 + 1; i < NUM_IND; i++) ind[i] = 0; /* init indicators */ +if (cpu_unit.flags & IF_IA) iae = 1; /* indirect enabled? */ +else iae = 0; +idxe = idxb = 0; /* indexing off */ +pcq_r = find_reg ("PCQ", NULL, dptr); /* init old PC queue */ +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init breakpoints */ +upd_ind (); /* update indicators */ +if (one_time) cpu_set_table (&cpu_unit, 1, NULL, NULL); /* set default tables */ +one_time = FALSE; +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & (FLAG | DIGIT); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & (FLAG | DIGIT); +return SCPE_OK; +} + +/* Memory size change */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val % 1000) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* Model change */ + +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val) cpu_unit.flags = (cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MII_OPT)) | + IF_DIV | IF_IA | IF_EDT; +else cpu_unit.flags = cpu_unit.flags & (UNIT_SCP | UNIT_BCD | MI_OPT); +return SCPE_OK; +} + +/* Set/clear Model 1 option */ + +t_stat cpu_set_opt1 (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cpu_unit.flags & IF_MII) { + printf ("Feature is standard on 1620 Model 2\n"); + if (sim_log) fprintf (sim_log, "Feature is standard on 1620 Model 2\n"); + return SCPE_NOFNC; + } +return SCPE_OK; +} + +/* Set/clear Model 2 option */ + +t_stat cpu_set_opt2 (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (!(cpu_unit.flags & IF_MII)) { + printf ("Feature is not available on 1620 Model 1\n"); + if (sim_log) fprintf (sim_log, "Feature is not available on 1620 Model 1\n"); + return SCPE_NOFNC; + } +return SCPE_OK; +} + +/* Front panel save */ + +t_stat cpu_set_save (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (saved_PC & 1) return SCPE_NOFNC; +PR1 = saved_PC; +return SCPE_OK; +} + +/* Set standard add/multiply tables */ + +t_stat cpu_set_table (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i; + +for (i = 0; i < MUL_TABLE_LEN; i++) /* set mul table */ + M[MUL_TABLE + i] = std_mul_table[i]; +if (((cpu_unit.flags & IF_MII) == 0) || val) { /* set add table */ + for (i = 0; i < ADD_TABLE_LEN; i++) + M[ADD_TABLE + i] = std_add_table[i]; + } +return SCPE_OK; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].vld = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, k, di, lnt; +char *cptr = (char *) desc; +t_value sim_eval[INST_LEN]; +t_stat r; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->vld) { /* instruction? */ + fprintf (st, "%05d ", h->pc); + for (i = 0; i < INST_LEN; i++) + sim_eval[i] = h->inst[i]; + if ((fprint_sym (st, h->pc, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) { + fprintf (st, "(undefined)"); + for (i = 0; i < INST_LEN; i++) + fprintf (st, "%02X", h->inst[i]); + } + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/I1620/i1620_defs.h b/I1620/i1620_defs.h new file mode 100644 index 0000000..6f32af3 --- /dev/null +++ b/I1620/i1620_defs.h @@ -0,0 +1,226 @@ +/* i1620_defs.h: IBM 1620 simulator definitions + + Copyright (c) 2002-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This simulator is based on the 1620 simulator written by Geoff Kuenning. + I am grateful to Al Kossow, the Computer History Museum, and the IBM Corporate + Archives for their help in gathering documentation about the IBM 1620. + + 18-Oct-02 RMS Fixed bug in ADDR_S macro (found by Hans Pufal) +*/ + +#ifndef _I1620_DEFS_H_ +#define _I1620_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_HALT 1 /* HALT */ +#define STOP_IBKPT 2 /* breakpoint */ +#define STOP_INVINS 3 /* invalid instruction */ +#define STOP_INVDIG 4 /* invalid digit */ +#define STOP_INVCHR 5 /* invalid char */ +#define STOP_INVIND 6 /* invalid indicator */ +#define STOP_INVPDG 7 /* invalid P addr digit */ +#define STOP_INVPAD 8 /* invalid P addr */ +#define STOP_INVPIA 9 /* invalid P indir addr */ +#define STOP_INVQDG 10 /* invalid Q addr digits */ +#define STOP_INVQAD 11 /* invalid Q addr */ +#define STOP_INVQIA 12 /* invalid Q indir addr */ +#define STOP_INVIO 13 /* invalid IO address */ +#define STOP_INVRTN 14 /* invalid return */ +#define STOP_INVFNC 15 /* invalid function */ +#define STOP_INVIAD 16 /* invalid instr addr */ +#define STOP_INVSEL 17 /* invalid select */ +#define STOP_INVIDX 18 /* invalid index instr */ +#define STOP_INVEAD 19 /* invalid even addr */ +#define STOP_INVDCF 20 /* invalid DCF addr */ +#define STOP_INVDRV 21 /* invalid disk drive */ +#define STOP_INVDSC 22 /* invalid disk sector */ +#define STOP_INVDCN 23 /* invalid disk count */ +#define STOP_INVDBA 24 /* invalid disk buf addr */ +#define STOP_DACERR 25 /* disk addr comp err */ +#define STOP_DWCERR 26 /* disk wr check err */ +#define STOP_CYOERR 27 /* cylinder ovflo err */ +#define STOP_WRLERR 28 /* wrong rec lnt err */ +#define STOP_CCT 29 /* runaway CCT */ +#define STOP_FWRAP 30 /* field wrap */ +#define STOP_RWRAP 31 /* record wrap */ +#define STOP_NOCD 32 /* no card in reader */ +#define STOP_OVERFL 33 /* overflow */ +#define STOP_EXPCHK 34 /* exponent error */ +#define STOP_WRADIS 35 /* write addr disabled */ +#define STOP_FPLNT 36 /* invalid fp length */ +#define STOP_FPUNL 37 /* fp lengths unequal */ +#define STOP_FPMF 38 /* no flag on exp */ +#define STOP_FPDVZ 39 /* divide by zero */ + +/* Memory */ + +#define MAXMEMSIZE 60000 /* max mem size */ +#define MEMSIZE (cpu_unit.capac) /* act memory size */ + +/* Processor parameters */ + +#define INST_LEN 12 /* inst length */ +#define ADDR_LEN 5 /* addr length */ +#define MUL_TABLE 100 /* multiply table */ +#define MUL_TABLE_LEN 200 +#define ADD_TABLE 300 /* add table */ +#define ADD_TABLE_LEN 100 +#define IDX_A 300 /* index A base */ +#define IDX_B 340 /* index B base */ +#define PROD_AREA 80 /* product area */ +#define PROD_AREA_LEN 20 /* product area */ +#define PROD_AREA_END (PROD_AREA + PROD_AREA_LEN) + +/* Branch indicator codes */ + +#define NUM_IND 100 /* number of indicators */ + +#define IN_SW1 1 /* sense switch 1 */ +#define IN_SW2 2 /* sense switch 2 */ +#define IN_SW3 3 /* sense switch 3 */ +#define IN_SW4 4 /* sense switch 4 */ +#define IN_RDCHK 6 /* read check (I/O error) */ +#define IN_WRCHK 7 /* write check (I/O error) */ +#define IN_LAST 9 /* last card was just read */ +#define IN_HP 11 /* high or positive result */ +#define IN_EZ 12 /* equal or zero result */ +#define IN_HPEZ 13 /* high/positive or equal/zero */ +#define IN_OVF 14 /* overflow */ +#define IN_EXPCHK 15 /* floating exponent check */ +#define IN_MBREVEN 16 /* even parity check */ +#define IN_MBRODD 17 /* odd parity check */ +#define IN_ANYCHK 19 /* any of read, write, even/odd */ +#define IN_PRCHK 25 /* printer check */ +#define IN_IXN 30 /* IX neither */ +#define IN_IXA 31 /* IX A band */ +#define IN_IXB 32 /* IX B band */ +#define IN_PRCH9 33 /* printer chan 9 */ +#define IN_PRCH12 34 /* printer chan 12 */ +#define IN_PRBSY 35 /* printer busy */ +#define IN_DACH 36 /* disk addr/data check */ +#define IN_DWLR 37 /* disk rec length */ +#define IN_DCYO 38 /* disk cyl overflow */ +#define IN_DERR 39 /* disk any error */ + +/* I/O channel codes */ + +#define NUM_IO 100 /* number of IO chan */ + +#define IO_TTY 1 /* console typewriter */ +#define IO_PTP 2 /* paper-tape punch */ +#define IO_PTR 3 /* paper-tape reader */ +#define IO_CDP 4 /* card punch */ +#define IO_CDR 5 /* card reader */ +#define IO_DSK 7 /* disk */ +#define IO_LPT 9 /* line printer */ +#define IO_BTP 32 /* binary ptp */ +#define IO_BTR 33 /* binary ptr */ + +#define LPT_WIDTH 120 /* line print width */ +#define CCT_LNT 132 /* car ctrl length */ + +#define CRETIOE(f,c) return ((f)? (c): SCPE_OK) + +/* Memory representation: flag + BCD digit per byte */ + +#define FLAG 0x10 +#define DIGIT 0x0F +#define REC_MARK 0xA +#define NUM_BLANK 0xC +#define GRP_MARK 0xF +#define BAD_DIGIT(x) ((x) > 9) + +/* Instruction format */ + +#define I_OP 0 /* opcode */ +#define I_P 2 /* P start */ +#define I_PL 6 /* P end */ +#define I_Q 7 /* Q start */ +#define I_QL 11 /* Q end */ +#define I_IO 8 /* IO select */ +#define I_BR 8 /* indicator select */ +#define I_CTL 10 /* control select */ +#define I_SEL 11 /* BS select */ + +#define ADDR_A(x,a) ((((x) + (a)) >= MEMSIZE)? ((x) + (a) - MEMSIZE): ((x) + (a))) +#define ADDR_S(x,a) (((x) < (a))? ((x) - (a) + MEMSIZE): ((x) - (a))) +#define PP(x) x = ADDR_A(x,1) +#define MM(x) x = ADDR_S(x,1) + +/* CPU options, stored in cpu_unit.flags */ +/* Decoding flags must be part of the same definition set */ + +#define UNIT_SCP ((1 << UNIT_V_UF) - 1) /* mask of SCP flags */ +#define IF_MII (1 << (UNIT_V_UF + 0)) /* model 2 */ +#define IF_DIV (1 << (UNIT_V_UF + 1)) /* automatic divide */ +#define IF_IA (1 << (UNIT_V_UF + 2)) /* indirect addressing */ +#define IF_EDT (1 << (UNIT_V_UF + 3)) /* edit */ +#define IF_FP (1 << (UNIT_V_UF + 4)) /* floating point */ +#define IF_BIN (1 << (UNIT_V_UF + 5)) /* binary */ +#define IF_IDX (1 << (UNIT_V_UF + 6)) /* indexing */ +#define IF_VPA (1 << (UNIT_V_UF + 7)) /* valid P addr */ +#define IF_VQA (1 << (UNIT_V_UF + 8)) /* valid Q addr */ +#define IF_4QA (1 << (UNIT_V_UF + 9)) /* 4 char Q addr */ +#define IF_NQX (1 << (UNIT_V_UF + 10)) /* no Q indexing */ +#define IF_IMM (1 << (UNIT_V_UF + 11)) /* immediate */ +#define UNIT_BCD (1 << (UNIT_V_UF + 12)) /* BCD coded */ +#define UNIT_MSIZE (1 << (UNIT_V_UF + 13)) /* fake flag */ +#define ALLOPT (IF_DIV + IF_IA + IF_EDT + IF_FP + IF_BIN + IF_IDX) +#define MI_OPT (IF_DIV + IF_IA + IF_EDT + IF_FP) +#define MI_STD (IF_DIV + IF_IA + IF_EDT) +#define MII_OPT (ALLOPT) +#define MII_STD (IF_DIV + IF_IA + IF_EDT + IF_BIN + IF_IDX) + +/* Add status codes */ + +#define ADD_NOCRY 0 /* no carry out */ +#define ADD_CARRY 1 /* carry out */ +#define ADD_SIGNC 2 /* sign change */ + +/* Opcodes */ + +enum opcodes { + OP_FADD = 1, OP_FSUB, OP_FMUL, /* 00 - 09 */ + OP_FSL = 5, OP_TFL, OP_BTFL, OP_FSR, OP_FDIV, + OP_BTAM = 10, OP_AM, OP_SM, OP_MM, OP_CM, /* 10 - 19 */ + OP_TDM, OP_TFM, OP_BTM, OP_LDM, OP_DM, + OP_BTA = 20, OP_A, OP_S, OP_M, OP_C, /* 20 - 29 */ + OP_TD, OP_TF, OP_BT, OP_LD, OP_D, + OP_TRNM = 30, OP_TR, OP_SF, OP_CF, OP_K, /* 30 - 39 */ + OP_DN, OP_RN, OP_RA, OP_WN, OP_WA, + OP_NOP = 41, OP_BB, OP_BD, OP_BNF, /* 40 - 49 */ + OP_BNR, OP_BI, OP_BNI, OP_H, OP_B, + OP_BNG = 55, + OP_BS = 60, OP_BX, OP_BXM, OP_BCX, OP_BCXM, /* 60 - 69 */ + OP_BLX, OP_BLXM, OP_BSX, + OP_MA = 70, OP_MF, OP_TNS, OP_TNF, /* 70 - 79 */ + /* 80 - 89 */ + OP_BBT = 90, OP_BMK, OP_ORF, OP_ANDF, OP_CPLF, /* 90 - 99 */ + OP_EORF, OP_OTD, OP_DTO }; + +#endif diff --git a/I1620/i1620_dp.c b/I1620/i1620_dp.c new file mode 100644 index 0000000..a99e90b --- /dev/null +++ b/I1620/i1620_dp.c @@ -0,0 +1,495 @@ +/* i1620_dp.c: IBM 1311 disk simulator + + Copyright (c) 2002-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dp 1311 disk pack + + The 1311 disk pack has 100 cylinders, 10 tracks/cylinder, 20 sectors/track. + Each sector contains 105 characters of information: + + 5c sector address + 100c sector data + + By default, a sector's address field will be '00000', which is interpreted + to mean the implied sector number that would be in place if the disk pack + had been formatted with sequential sector numbers. + + 18-Oct-02 RMS Fixed bug in error testing (found by Hans Pufal) +*/ + +#include "i1620_defs.h" + +#define DP_NUMDR 4 /* #drives */ +#define UNIT_V_WAE (UNIT_V_UF + 0) /* write addr enab */ +#define UNIT_WAE (1 << UNIT_V_WAE) + +/* Disk format */ + +#define DP_ADDR 5 /* address */ +#define DP_DATA 100 /* data */ +#define DP_NUMCH (DP_ADDR + DP_DATA) + +#define DP_NUMSC 20 /* #sectors */ +#define DP_NUMSF 10 /* #surfaces */ +#define DP_NUMCY 100 /* #cylinders */ +#define DP_TOTSC (DP_NUMCY * DP_NUMSF * DP_NUMSC) +#define DP_SIZE (DP_TOTSC * DP_NUMCH) + +/* Disk control field */ + +#define DCF_DRV 0 /* drive select */ +#define DCF_SEC 1 /* sector addr */ +#define DCF_SEC_LEN 5 +#define DCF_CNT (DCF_SEC + DCF_SEC_LEN) /* sector count */ +#define DCF_CNT_LEN 3 +#define DCF_ADR (DCF_CNT + DCF_CNT_LEN) /* buffer address */ +#define DCF_ADR_LEN 5 +#define DCF_LEN (DCF_ADR + DCF_ADR_LEN) + +/* Functions */ + +#define FNC_SEEK 1 /* seek */ +#define FNC_SEC 0 /* sectors */ +#define FNC_WCH 1 /* write check */ +#define FNC_NRL 2 /* no rec lnt chk */ +#define FNC_TRK 4 /* tracks */ +#define FNC_WRI 8 /* write offset */ + +#define CYL u3 /* current cylinder */ + +extern uint8 M[MAXMEMSIZE]; /* memory */ +extern uint8 ind[NUM_IND]; +extern UNIT cpu_unit; + +int32 dp_stop = 1; /* disk err stop */ +uint32 dp_ba = 0; /* buffer addr */ + +t_stat dp_reset (DEVICE *dptr); +t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc); +t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc); +t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr); +t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr); +int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd); +t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd); +t_bool dp_zeroad (uint8 *ap); +int32 dp_cvt_ad (uint8 *ap); +int32 dp_trkop (int32 drv, int32 sec); +int32 dp_cvt_bcd (uint32 ad, int32 len); +void dp_fill (UNIT *uptr, uint32 da, int32 cnt); +t_stat dp_tstgm (uint32 c, int32 qnr); + +/* DP data structures + + dp_dev DP device descriptor + dp_unit DP unit list + dp_reg DP register list + dp_mod DP modifier list +*/ + +UNIT dp_unit[] = { + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) }, + { UDATA (NULL, UNIT_FIX + UNIT_DISABLE + UNIT_ATTABLE + + UNIT_BUFABLE + UNIT_MUSTBUF + UNIT_BCD, DP_SIZE) } + }; + +REG dp_reg[] = { + { FLDATA (ADCHK, ind[IN_DACH], 0) }, + { FLDATA (WLRC, ind[IN_DWLR], 0) }, + { FLDATA (CYLO, ind[IN_DCYO], 0) }, + { FLDATA (ERR, ind[IN_DERR], 0) }, + { FLDATA (DPSTOP, dp_stop, 0) }, + { URDATA (CYL, dp_unit[0].CYL, 10, 8, 0, + DP_NUMDR, PV_LEFT + REG_RO) }, + { NULL } + }; + +MTAB dp_mod[] = { + { UNIT_WAE, 0, "write address disabled", "ADDROFF", NULL }, + { UNIT_WAE, UNIT_WAE, "write address enabled", "ADDRON", NULL }, + { 0 } + }; + +DEVICE dp_dev = { + "DP", dp_unit, dp_reg, dp_mod, + DP_NUMDR, 10, 21, 1, 16, 5, + NULL, NULL, &dp_reset, + NULL, NULL, NULL + }; + +/* Disk IO routine */ + +t_stat dp (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +int32 drv, sa, sec, psec, cnt, qwc, qnr, t; +UNIT *uptr; +t_stat r; + +if (pa & 1) return STOP_INVDCF; /* dcf must be even */ +ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */ +ind[IN_DERR] = ind[IN_DCYO] = 0; +sa = ADDR_A (pa, DCF_SEC); /* ptr to sector */ +if (((dp_unit[0].flags & UNIT_DIS) == 0) && /* only drive 0? */ + (dp_unit[1].flags & UNIT_DIS) && + (dp_unit[2].flags & UNIT_DIS) && + (dp_unit[3].flags & UNIT_DIS)) drv = 0; /* ignore drv select */ +else drv = (((M[pa] & 1)? M[pa]: M[sa]) & 0xE) >> 1; /* drive # */ +if (drv >= DP_NUMDR) return STOP_INVDRV; /* invalid? */ +uptr = dp_dev.units + drv; /* get unit ptr */ +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + ind[IN_DERR] = 1; /* no, error */ + CRETIOE (dp_stop, SCPE_UNATT); + } + +sec = dp_cvt_bcd (sa, DCF_SEC_LEN); /* cvt sector */ +if ((sec < 0) || (sec >= (DP_NUMDR * DP_TOTSC))) /* bad sector? */ + return STOP_INVDSC; +if (op == OP_K) { /* seek? */ + if (f1 != FNC_SEEK) return STOP_INVFNC; /* really? */ + uptr->CYL = (sec / (DP_NUMSF * DP_NUMSC)) % /* set cyl # */ + DP_NUMCY; + return SCPE_OK; /* done! */ + } + +cnt = dp_cvt_bcd (ADDR_A (pa, DCF_CNT), DCF_CNT_LEN); /* get count */ +t = dp_cvt_bcd (ADDR_A (pa, DCF_ADR), DCF_ADR_LEN); /* get address */ +if ((t < 0) || (t & 1)) return STOP_INVDBA; /* bad address? */ +dp_ba = t; /* save addr */ + +if (f1 >= FNC_WRI) return STOP_INVFNC; /* invalid func? */ +if (op == OP_RN) qwc = f1 & FNC_WCH; /* read? set wch */ +else if (op == OP_WN) { /* write? */ + if (op & FNC_WCH) return STOP_INVFNC; /* cant check */ + f1 = f1 + FNC_WRI; /* offset fnc */ + } +else return STOP_INVFNC; /* not R or W */ +qnr = f1 & FNC_NRL; /* no rec check? */ + +switch (f1 & ~(FNC_WCH | FNC_NRL)) { /* case on function */ + + case FNC_SEC: /* read sectors */ + if (cnt <= 0) return STOP_INVDCN; /* bad count? */ + psec = dp_fndsec (uptr, sec, TRUE); /* find sector */ + if (psec < 0) CRETIOE (dp_stop, STOP_DACERR); /* error? */ + do { /* loop on count */ + if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read sector */ + break; + sec++; psec++; /* next sector */ + } while ((--cnt > 0) && + ((r = dp_nexsec (uptr, sec, psec, TRUE)) == SCPE_OK)); + break; /* done, clean up */ + + case FNC_TRK: /* read track */ + psec = dp_trkop (drv, sec); /* start of track */ + for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */ + if (r = dp_rdadr (uptr, psec, qnr, qwc)) /* read addr */ + break; /* error? */ + if (r = dp_rdsec (uptr, psec, qnr, qwc)) /* read data */ + break; /* error? */ + psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); + } + break; /* done, clean up */ + + case FNC_SEC + FNC_WRI: /* write */ + if (cnt <= 0) return STOP_INVDCN; /* bad count? */ + psec = dp_fndsec (uptr, sec, FALSE); /* find sector */ + if (psec < 0) CRETIOE (dp_stop, STOP_DACERR); /* error? */ + do { /* loop on count */ + if (r = dp_tstgm (M[dp_ba], qnr)) break; /* start with gm? */ + if (r = dp_wrsec (uptr, psec, qnr)) break; /* write data */ + sec++; psec++; /* next sector */ + } while ((--cnt > 0) && + ((r = dp_nexsec (uptr, sec, psec, FALSE)) == SCPE_OK)); + break; /* done, clean up */ + + case FNC_TRK + FNC_WRI: /* write track */ + if ((uptr->flags & UNIT_WAE) == 0) /* enabled? */ + return STOP_WRADIS; + psec = dp_trkop (drv, sec); /* start of track */ + for (cnt = 0; cnt < DP_NUMSC; cnt++) { /* full track */ + if (r = dp_tstgm (M[dp_ba], qnr)) break; /* start with gm? */ + if (r = dp_wradr (uptr, psec, qnr)) break; /* write addr */ + if (r = dp_wrsec (uptr, psec, qnr)) break; /* write data */ + psec = dp_trkop (drv, sec) + ((psec + 1) % DP_NUMSC); + } + break; /* done, clean up */ + + default: /* unknown */ + return STOP_INVFNC; + } + +if ((r == SCPE_OK) && !qnr) { /* eor check? */ + if ((M[dp_ba] & DIGIT) != GRP_MARK) { /* GM at end? */ + ind[IN_DWLR] = ind[IN_DERR] = 1; /* no, error */ + r = STOP_WRLERR; + } + } +if ((r != SCPE_OK) && /* error? */ + (dp_stop || !ind[IN_DERR])) return r; /* iochk or stop? */ +return SCPE_OK; /* continue */ +} + +/* Read or compare address with memory */ + +t_stat dp_rdadr (UNIT *uptr, int32 sec, int32 qnr, int32 qwc) +{ +int32 i; +uint8 ad; +int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ +t_bool zad = dp_zeroad (ap); /* zero address */ +static const int32 dec_tab[DP_ADDR] = { /* powers of 10 */ + 10000, 1000, 100, 10, 1 + } ; + +for (i = 0; i < DP_ADDR; i++) { /* copy/check addr */ + if (zad) { /* addr zero? */ + ad = sec / dec_tab[i]; /* get addr digit */ + sec = sec % dec_tab[i]; /* get remainder */ + } + else ad = *ap; /* addr digit */ + if (qwc) { /* write check? */ + if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */ + return STOP_WRLERR; /* yes, error */ + if (!zad && (M[dp_ba] != ad)) { /* digits equal? */ + ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */ + return STOP_DWCERR; + } + } + else M[dp_ba] = ad & (FLAG | DIGIT); /* store digit */ + if (dp_tstgm (*ap, qnr)) return STOP_WRLERR; /* grp mrk on disk? */ + ap++; PP (dp_ba); /* adv ptrs */ + } +return SCPE_OK; +} + +/* Read or compare data with memory */ + +t_stat dp_rdsec (UNIT *uptr, int32 sec, int32 qnr, int32 qwc) +{ +int32 i; +int32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da + DP_ADDR; /* buf ptr */ + +for (i = 0; i < DP_DATA; i++) { /* copy data */ + if (qwc) { /* write check? */ + if (dp_tstgm (M[dp_ba], qnr)) /* grp mrk in mem? */ + return STOP_WRLERR; /* yes, error */ + if (M[dp_ba] != *ap) { /* dig+flags equal? */ + ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */ + return STOP_DWCERR; + } + } + else M[dp_ba] = *ap & (FLAG | DIGIT); /* flag + digit */ + if (dp_tstgm (*ap, qnr)) return STOP_WRLERR; /* grp mrk on disk? */ + ap++; PP (dp_ba); /* adv ptrs */ + } +return SCPE_OK; +} + +/* Write address to disk */ + +t_stat dp_wradr (UNIT *uptr, int32 sec, int32 qnr) +{ +int32 i; +uint32 da = (sec % DP_TOTSC) * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ + +for (i = 0; i < DP_ADDR; i++) { /* copy address */ + *ap = M[dp_ba] & (FLAG | DIGIT); /* flag + digit */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */ + dp_fill (uptr, da + 1, DP_NUMCH - i - 1); /* fill addr+data */ + return STOP_WRLERR; /* error */ + } + da++; ap++; PP (dp_ba); /* adv ptrs */ + } +return SCPE_OK; +} + +/* Write data to disk */ + +t_stat dp_wrsec (UNIT *uptr, int32 sec, int32 qnr) +{ +int32 i; +uint32 da = ((sec % DP_TOTSC) * DP_NUMCH) + DP_ADDR; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ + +for (i = 0; i < DP_DATA; i++) { /* copy data */ + *ap = M[dp_ba] & (FLAG | DIGIT); /* get character */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + if (dp_tstgm (*ap, qnr)) { /* grp mrk fm mem? */ + dp_fill (uptr, da + 1, DP_DATA - i - 1); /* fill data */ + return STOP_WRLERR; /* error */ + } + da++; ap++; PP (dp_ba); /* adv ptrs */ + } +return SCPE_OK; +} + +/* Find sector */ + +int32 dp_fndsec (UNIT *uptr, int32 sec, t_bool rd) +{ +int32 ctrk = sec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ +int32 psec = ((uptr->CYL) * (DP_NUMSF * DP_NUMSC)) + ctrk; +int32 da = psec * DP_NUMCH; /* char number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ +int32 dskad, i; + +if (dp_zeroad (ap)) return psec; /* addr zero? ok */ +dskad = dp_cvt_ad (ap); /* cvt addr */ +if (dskad == sec) { /* match? */ + if (rd || ((*ap & FLAG) == 0)) return psec; /* read or !wprot? */ + ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */ + return -1; + } +psec = psec - (psec % DP_NUMSC); /* sector 0 */ +for (i = 0; i < DP_NUMSC; i++, psec++) { /* check track */ + da = psec * DP_NUMCH; /* char number */ + ap = ((uint8 *) uptr->filebuf) + da; /* word pointer */ + if (dp_zeroad (ap)) continue; /* no implicit match */ + dskad = dp_cvt_ad (ap); /* cvt addr */ + if (dskad == sec) { /* match? */ + if (rd || ((*ap & FLAG) == 0)) return psec; /* read or !wprot? */ + ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */ + return -1; + } + } +ind[IN_DACH] = ind[IN_DERR] = 1; /* no match */ +return -1; +} + +/* Find next sector - must be sequential, cannot cross cylinder boundary */ + +t_stat dp_nexsec (UNIT *uptr, int32 sec, int32 psec, t_bool rd) +{ +int32 ctrk = psec % (DP_NUMSF * DP_NUMSC); /* curr trk-sec */ +int32 da = psec * DP_NUMCH; /* word number */ +uint8 *ap = ((uint8 *) uptr->filebuf) + da; /* buf ptr */ +int32 dskad; + +if (ctrk) { /* not trk zero? */ + if (dp_zeroad (ap)) return SCPE_OK; /* addr zero? ok */ + dskad = dp_cvt_ad (ap); /* cvt addr */ + if ((dskad == sec) && /* match? */ + (rd || ((*ap & FLAG) == 0))) return SCPE_OK; /* read or !wprot? */ + ind[IN_DACH] = ind[IN_DERR] = 1; /* no, error */ + return STOP_DACERR; + } +ind[IN_DCYO] = ind[IN_DERR] = 1; /* cyl overflow */ +return STOP_CYOERR; +} + +/* Test for zero address */ + +t_bool dp_zeroad (uint8 *ap) +{ +int32 i; + +for (i = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ + if (*ap & DIGIT) return FALSE; /* nonzero? lose */ + } +return TRUE; /* all zeroes */ +} + +/* Test for group mark when enabled */ + +t_stat dp_tstgm (uint32 c, int32 qnr) +{ +if (!qnr && ((c & DIGIT) == GRP_MARK)) { /* premature GM? */ + ind[IN_DWLR] = ind[IN_DERR] = 1; /* error */ + return STOP_WRLERR; + } +return SCPE_OK; +} + +/* Convert disk address to binary - invalid char force bad address */ + +int32 dp_cvt_ad (uint8 *ap) +{ +int32 i, r; +uint8 c; + +for (i = r = 0; i < DP_ADDR; i++, ap++) { /* loop thru addr */ + c = *ap & DIGIT; /* get digit */ + if (BAD_DIGIT (c)) return -1; /* bad digit? */ + r = (r * 10) + c; /* bcd to binary */ + } +return r; +} + +/* Track operation setup */ + +int32 dp_trkop (int32 drv, int32 sec) +{ +int32 ctrk = (sec / DP_NUMSC) % DP_NUMSF; + +return ((drv * DP_TOTSC) + (dp_unit[drv].CYL * DP_NUMSF * DP_NUMSC) + + (ctrk * DP_NUMSC)); +} + +/* Convert DCF BCD field to binary */ + +int32 dp_cvt_bcd (uint32 ad, int32 len) +{ +uint8 c; +int32 r; + +for (r = 0; len > 0; len--) { /* loop thru char */ + c = M[ad] & DIGIT; /* get digit */ + if (BAD_DIGIT (c)) return -1; /* invalid? */ + r = (r * 10) + c; /* cvt to bin */ + PP (ad); /* next digit */ + } +return r; +} + +/* Fill sector buffer with zero */ + +void dp_fill (UNIT *uptr, uint32 da, int32 cnt) +{ +while (cnt-- > 0) { /* fill with zeroes*/ + *(((uint8 *) uptr->filebuf) + da) = 0; + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + da++; + } +return; +} + +/* Reset routine */ + +t_stat dp_reset (DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < DP_NUMDR; i++) dp_unit[i].CYL = 0; /* reset cylinder */ +ind[IN_DACH] = ind[IN_DWLR] = 0; /* clr indicators */ +ind[IN_DERR] = ind[IN_DCYO] = 0; +return SCPE_OK; +} diff --git a/I1620/i1620_fp.c b/I1620/i1620_fp.c new file mode 100644 index 0000000..6ce4c4b --- /dev/null +++ b/I1620/i1620_fp.c @@ -0,0 +1,419 @@ +/* i1620_fp.c: IBM 1620 floating point simulator + + Copyright (c) 2002-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + The IBM 1620 uses a variable length floating point format, with a fixed + two digit decimal exponent and a variable length decimal mantissa: + + _ S_S + M.......MEE + + where S represents flag bits if the mantissa or exponent are negative. + + 31-May-2008 RMS Fixed add_field call (found by Peter Schorn) +*/ + +#include "i1620_defs.h" + +#define FP_LMAX 100 /* max fp mant lnt */ +#define FP_EMAX 99 /* max fp exponent */ + +/* Unpacked floating point operand */ + +typedef struct { + int32 sign; /* 0 => +, 1 => - */ + int32 exp; /* binary exponent */ + uint32 lnt; /* mantissa length */ + uint32 addr; /* mantissa addr */ + uint32 zero; /* 0 => nz, 1 => zero */ + } FPA; + +extern uint8 M[MAXMEMSIZE]; /* main memory */ +extern uint8 ind[NUM_IND]; /* indicators */ +extern UNIT cpu_unit; + +t_stat fp_scan_mant (uint32 ad, uint32 *lnt, uint32 *zro); +t_stat fp_zero (FPA *fp); + +extern t_stat xmt_field (uint32 d, uint32 s, uint32 skp); +extern t_stat add_field (uint32 d, uint32 s, t_bool sub, t_bool sto, uint32 skp, int32 *sta); +extern t_stat mul_field (uint32 d, uint32 s); +extern t_stat xmt_divd (uint32 d, uint32 s); +extern t_stat div_field (uint32 dvd, uint32 dvr, int32 *ez); + +/* Unpack and validate a floating point argument */ + +t_stat fp_unpack (uint32 ad, FPA *fp) +{ +uint8 d0, d1, esign; + +esign = M[ad] & FLAG; /* get exp sign */ +d0 = M[ad] & DIGIT; /* get exp lo digit */ +MM (ad); +if ((M[ad] & FLAG) == 0) return STOP_FPMF; /* no flag on hi exp? */ +d1 = M[ad] & DIGIT; /* get exp hi digit */ +MM (ad); +fp->addr = ad; /* save mant addr */ +if (BAD_DIGIT (d1) || BAD_DIGIT (d0)) return STOP_INVDIG; /* exp bad dig? */ +fp->exp = ((d1 * 10) + d0) * (esign? -1: 1); /* convert exponent */ +fp->sign = (M[ad] & FLAG)? 1: 0; /* get mantissa sign */ +return fp_scan_mant (fp->addr, &(fp->lnt), &(fp->zero)); +} + +/* Unpack and validate source and destination arguments */ + +t_stat fp_unpack_two (uint32 dad, uint32 sad, FPA *dfp, FPA *sfp) +{ +t_stat r; + +if ((r = fp_unpack (dad, dfp)) != SCPE_OK) return r; /* unpack dst */ +if ((r = fp_unpack (sad, sfp)) != SCPE_OK) return r; /* unpack src */ +if (sfp->lnt != dfp->lnt) return STOP_FPUNL; /* lnts must be equal */ +return SCPE_OK; +} + +/* Pack floating point result */ + +t_stat fp_pack (FPA *fp) +{ +int32 e; +uint32 i, mad; + +e = (fp->exp >= 0)? fp->exp: -fp->exp; /* get |exp| */ +if (e > FP_EMAX) { /* too big? */ + ind[IN_EXPCHK] = 1; /* set indicator */ + if (fp->exp < 0) return fp_zero (fp); /* underflow? */ + mad = fp->addr; + for (i = 0; i < fp->lnt; i++) { /* mant = 99...99 */ + M[mad] = (M[mad] & FLAG) | 9; + MM (mad); + } + e = FP_EMAX; /* cap at max */ + } +M[ADDR_A (fp->addr, 1)] = (e / 10) | FLAG; /* high exp digit */ +M[ADDR_A (fp->addr, 2)] = (e % 10) | /* low exp digit */ + ((fp->exp < 0)? FLAG: 0); +return SCPE_OK; +} + +/* Shift mantissa right n positions */ + +void fp_rsh (FPA *fp, uint32 n) +{ +uint32 i, sad, dad; + +if (n == 0) return; /* zero? done */ +sad = ADDR_S (fp->addr, n); /* src = addr - n */ +dad = fp->addr; /* dst = n */ +for (i = 0; i < fp->lnt; i++) { /* move digits */ + if (i >= (fp->lnt - n)) M[dad] = M[dad] & FLAG; + else M[dad] = (M[dad] & FLAG) | (M[sad] & DIGIT); + MM (dad); + MM (sad); + } +return; +} + +/* Shift mantissa left 1 position */ + +void fp_lsh_1 (FPA *fp) +{ +uint32 i, mad, nxt; + +mad = ADDR_S (fp->addr, fp->lnt - 1); /* hi order digit */ +for (i = 0; i < (fp->lnt - 1); i++) { /* move lnt-1 digits */ + nxt = ADDR_A (mad, 1); + M[mad] = (M[mad] & FLAG) | (M[nxt] & DIGIT); + mad = nxt; + } +M[mad] = M[mad] & FLAG; /* clear last digit */ +return; +} + +/* Clear floating point number */ + +t_stat fp_zero (FPA *fp) +{ +uint32 i, mad = fp->addr; + +for (i = 0; i < fp->lnt; i++) { /* clear mantissa */ + M[mad] = (i? M[mad] & FLAG: 0); /* clear sign bit */ + MM (mad); + } +M[ADDR_A (fp->addr, 1)] = FLAG + 9; /* exp = -99 */ +M[ADDR_A (fp->addr, 2)] = FLAG + 9; /* exp = -99 */ +ind[IN_EZ] = 1; /* result = 0 */ +ind[IN_HP] = 0; +return SCPE_OK; +} + +/* Scan floating point mantissa for length and (optionally) zero */ + +t_stat fp_scan_mant (uint32 ad, uint32 *lnt, uint32 *zro) +{ +uint8 d, l, z; + +z = 1; /* assume zero */ +for (l = 1; l <= FP_LMAX; l++) { /* scan to get length */ + d = M[ad] & DIGIT; /* get mant digit */ + if (d) z = 0; /* non-zero? */ + if ((l != 1) && (M[ad] & FLAG)) { /* flag past first dig? */ + *lnt = l; /* set returns */ + if (zro) *zro = z; + return SCPE_OK; + } + MM (ad); + } +return STOP_FPLNT; /* too long */ +} + +/* Copy floating point mantissa */ + +void fp_copy_mant (uint32 d, uint32 s, uint32 l) +{ +uint32 i; + +if (ind[IN_HP]) M[d] = M[d] & ~FLAG; /* clr/set sign */ +else M[d] = M[d] | FLAG; +for (i = 0; i < l; i++) { /* copy src */ + M[d] = (M[d] & FLAG) | (M[s] & DIGIT); /* preserve flags */ + MM (d); + MM (s); + } +return; +} + +/* Compare floating point mantissa */ + +int32 fp_comp_mant (uint32 d, uint32 s, uint32 l) +{ +uint8 i, dd, sd; + +d = ADDR_S (d, l - 1); /* start of mantissa */ +s = ADDR_S (s, l - 1); +for (i = 0; i < l; i++) { /* compare dst:src */ + dd = M[d] & DIGIT; /* get dst digit */ + sd = M[s] & DIGIT; /* get src digit */ + if (dd > sd) return 1; /* >? done */ + if (dd < sd) return -1; /* = ((int32) dfp.lnt))) { /* src = 0, or too small? */ + if (dfp.zero) return fp_zero (&dfp); /* res = dst, zero? */ + ind[IN_EZ] = 0; /* res nz, set EZ, HP */ + ind[IN_HP] = (dfp.sign == 0); + return SCPE_OK; + } +if (dfp.zero || (dif <= -((int32) dfp.lnt))) { /* dst = 0, or too small? */ + if (sfp.zero) return fp_zero (&dfp); /* res = src, zero? */ + r = xmt_field (d, s, 3); /* copy src to dst */ + ind[IN_EZ] = 0; /* res nz, set EZ, HP */ + ind[IN_HP] = (dfp.sign == 0); + return r; + } + +if (dif > 0) { /* dst exp > src exp? */ + sad = sfp.addr; /* save src in save area */ + for (i = 0; i < sfp.lnt; i++) { + sav_src[i] = M[sad]; + MM (sad); + } + fp_rsh (&sfp, dif); /* denormalize src */ + } +else if (dif < 0) { /* dst exp < src exp? */ + dfp.exp = sfp.exp; /* res exp = src exp */ + fp_rsh (&dfp, -dif); /* denormalize dst */ + } +r = add_field (dfp.addr, sfp.addr, sub, TRUE, 0, &sta); /* add mant, set EZ, HP */ +if (dif > 0) { /* src denormalized? */ + sad = sfp.addr; /* restore src from */ + for (i = 0; i < sfp.lnt; i++) { /* save area */ + M[sad] = sav_src[i]; + MM (sad); + } + } +if (r != SCPE_OK) return r; /* add error? */ + +hi = ADDR_S (dfp.addr, dfp.lnt - 1); /* addr of hi digit */ +if (sta == ADD_CARRY) { /* carry out? */ + fp_rsh (&dfp, 1); /* shift mantissa */ + M[hi] = FLAG + 1; /* high order 1 */ + dfp.exp = dfp.exp + 1; + ind[IN_EZ] = 0; /* not zero */ + ind[IN_HP] = (dfp.sign == 0); /* set HP */ + } +else if (ind[IN_EZ]) return fp_zero (&dfp); /* result zero? */ +else { + while ((M[hi] & DIGIT) == 0) { /* until normalized */ + fp_lsh_1 (&dfp); /* left shift */ + dfp.exp = dfp.exp - 1; /* decr exponent */ + } + } + +return fp_pack (&dfp); /* pack and exit */ +} + +/* Floating point multiply */ + +t_stat fp_mul (uint32 d, uint32 s) +{ +FPA sfp, dfp; +uint32 pad; +t_stat r; + +r = fp_unpack_two (d, s, &dfp, &sfp); /* unpack operands */ +if (r != SCPE_OK) return r; /* error? */ +if (sfp.zero || dfp.zero) return fp_zero (&dfp); /* either zero? */ + +r = mul_field (dfp.addr, sfp.addr); /* mul, set EZ, HP */ +if (r != SCPE_OK) return r; +if (M[ADDR_S (PROD_AREA_END, 2 * dfp.lnt)] & DIGIT) { /* hi prod dig set? */ + pad = ADDR_S (PROD_AREA_END - 1, dfp.lnt); /* no normalization */ + dfp.exp = dfp.exp + sfp.exp; /* res exp = sum */ + } +else { + pad = ADDR_S (PROD_AREA_END, dfp.lnt); /* 'normalize' 1 */ + dfp.exp = dfp.exp + sfp.exp - 1; /* res exp = sum - 1 */ + } +fp_copy_mant (dfp.addr, pad, dfp.lnt); /* copy prod to mant */ + +return fp_pack (&dfp); /* pack and exit */ +} + +/* Floating point divide */ + +t_stat fp_div (uint32 d, uint32 s) +{ +FPA sfp, dfp; +uint32 i, pad, a100ml, a99ml; +int32 ez; +t_stat r; + +r = fp_unpack_two (d, s, &dfp, &sfp); /* unpack operands */ +if (r != SCPE_OK) return r; /* error? */ +if (sfp.zero) { /* divide by zero? */ + ind[IN_OVF] = 1; /* dead jim */ + return SCPE_OK; + } +if (dfp.zero) return fp_zero (&dfp); /* divide into zero? */ + +for (i = 0; i < PROD_AREA_LEN; i++) /* clear prod area */ + M[PROD_AREA + i] = 0; +a100ml = ADDR_S (PROD_AREA_END, dfp.lnt); /* 100 - lnt */ +a99ml = ADDR_S (PROD_AREA_END - 1, dfp.lnt); /* 99 - lnt */ +if (fp_comp_mant (dfp.addr, sfp.addr, dfp.lnt) >= 0) { /* |Mdst| >= |Msrc|? */ + pad = a100ml; + dfp.exp = dfp.exp - sfp.exp + 1; /* res exp = diff + 1 */ + } +else { + pad = a99ml; + dfp.exp = dfp.exp - sfp.exp; /* res exp = diff */ + } +r = xmt_divd (pad, dfp.addr); /* xmt dividend */ +if (r != SCPE_OK) return r; /* error? */ +r = div_field (a100ml, sfp.addr, &ez); /* divide fractions */ +if (r != SCPE_OK) return r; /* error? */ +if (ez) return fp_zero (&dfp); /* result zero? */ + +ind[IN_HP] = ((dfp.sign ^ sfp.sign) == 0); /* set res sign */ +ind[IN_EZ] = 0; /* not zero */ +fp_copy_mant (dfp.addr, a99ml, dfp.lnt); /* copy result */ + +return fp_pack (&dfp); +} + +/* Floating shift right */ + +t_stat fp_fsr (uint32 d, uint32 s) +{ +uint32 cnt; +uint8 t; + +if (d == s) return SCPE_OK; /* no move? */ + +cnt = 0; +M[d] = (M[d] & FLAG) | (M[s] & DIGIT); /* move 1st wo flag */ +do { + MM (d); /* decr ptrs */ + MM (s); + t = M[d] = M[s] & (FLAG | DIGIT); /* copy others */ + if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while ((t & FLAG) == 0); /* until src flag */ + +cnt = 0; +do { + MM (d); /* decr pointer */ + t = M[d]; /* save old val */ + M[d] = 0; /* zero field */ + if (cnt++ > MEMSIZE) return STOP_FWRAP; /* (stop runaway) */ + } while ((t & FLAG) == 0); /* until dst flag */ +return SCPE_OK; +} + +/* Floating shift left - note that dst is addr of high order digit */ + +t_stat fp_fsl (uint32 d, uint32 s) +{ +uint32 i, lnt; +uint8 sign; +t_stat r; + +if (d == s) return SCPE_OK; +sign = M[s] & FLAG; /* get src sign */ +r = fp_scan_mant (s, &lnt, NULL); /* get src length */ +if (r != SCPE_OK) return r; /* error? */ +s = ADDR_S (s, lnt - 1); /* hi order src */ +M[d] = M[s] & (FLAG | DIGIT); /* move 1st w flag */ +M[s] = M[s] & ~FLAG; /* clr flag from src */ +for (i = 1; i < lnt; i++) { /* move src to dst */ + PP (d); /* incr ptrs */ + PP (s); + M[d] = M[s] & DIGIT; /* move just digit */ + } +PP (d); /* incr pointer */ +while ((M[d] & FLAG) == 0) { /* until flag */ + M[d] = 0; /* clear field */ + PP (d); + } +if (sign) M[d] = FLAG; /* -? zero under sign */ +return SCPE_OK; +} diff --git a/I1620/i1620_lp.c b/I1620/i1620_lp.c new file mode 100644 index 0000000..daa2152 --- /dev/null +++ b/I1620/i1620_lp.c @@ -0,0 +1,347 @@ +/* i1620_lp.c: IBM 1443 line printer simulator + + Copyright (c) 2002-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt 1443 line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility + 29-Dec-03 RMS Fixed bug in scheduling + 25-Apr-03 RMS Revised for extended file support +*/ + +#include "i1620_defs.h" + +#define LPT_BSIZE 197 /* buffer size */ + +#define K_IMM 0x10 /* control now */ +#define K_LIN 0x20 /* spc lines */ +#define K_CH10 0x40 /* chan 10 */ +#define K_LCNT 0x03 /* line count */ +#define K_CHAN 0x0F /* channel */ + +extern uint8 M[MAXMEMSIZE]; +extern uint8 ind[NUM_IND]; +extern UNIT cpu_unit; +extern uint32 io_stop; + +uint32 cct[CCT_LNT] = { 03 }; /* car ctrl tape */ +int32 cct_lnt = 66, cct_ptr = 0; /* cct len, ptr */ +int32 lpt_bptr = 0; /* lpt buf ptr */ +char lpt_buf[LPT_BSIZE + 1]; /* lpt buf */ +int32 lpt_savctrl = 0; /* saved spc ctrl */ + +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +void lpt_buf_init (void); +t_stat lpt_num (uint32 pa, uint32 len, uint32 f1); +t_stat lpt_print (void); +t_stat lpt_space (int32 lines, int32 lflag); + +#define CHP(ch,val) ((val) & (1 << (ch))) + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 50) + }; + +REG lpt_reg[] = { + { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE + 1) }, + { DRDATA (BPTR, lpt_bptr, 8) }, + { HRDATA (PCTL, lpt_savctrl, 8) }, + { FLDATA (PRCHK, ind[IN_PRCHK], 0) }, + { FLDATA (PRCH9, ind[IN_PRCH9], 0) }, + { FLDATA (PRCH12, ind[IN_PRCH12], 0) }, + { FLDATA (PRBSY, ind[IN_PRBSY], 0) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, + { DRDATA (CCTP, cct_ptr, 8), PV_LEFT }, + { DRDATA (CCTL, cct_lnt, 8), REG_RO + PV_LEFT }, + { NULL } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, NULL + }; + +/* Data tables */ + +/* Numeric (flag plus digit) to lineprinter (ASCII) */ + +const char num_to_lpt[32] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '|', ' ', '@', ':', ' ', 'G', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'W', ' ', '*', ' ', -1, 'X' + }; + +/* Alphameric (digit pair) to lineprinter (ASCII) */ + +const char alp_to_lpt[256] = { + ' ', -1, '?', '.', ')', -1, -1, -1, /* 00 */ + -1, -1, -1, -1, -1, -1, -1, -1, + '+', -1, '!', '$', '*', ' ', -1, -1, /* 10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, '0', '=', '@', ':', -1, -1, /* 30 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */ + 'H', 'I', -1, -1, -1, -1, -1, -1, + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */ + 'Q', 'R', -1, -1, -1, -1, -1, -1, + -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */ + 'Y', 'Z', -1, -1, -1, -1, -1, -1, + '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */ + '8', '9', -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ + -1, -1, -1, -1, -1, -1, -1, -1 + }; + +/* Line printer IO routine + + - Hard errors halt the system. + - Invalid characters print a blank, set the WRCHK and PRCHK + flags, and halt the system if IO stop is set. +*/ + +t_stat lpt (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +int8 lpc; +uint8 z, d; +t_stat r, sta; + +sta = SCPE_OK; +sim_cancel (&lpt_unit); /* "stall" until */ +ind[IN_PRBSY] = 0; /* printer free */ + +switch (op) { /* decode op */ + + case OP_K: /* control */ + lpt_savctrl = (f0 << 4) | f1; /* form ctrl */ + if (lpt_savctrl & K_IMM) return lpt_print (); /* immediate? */ + break; + + case OP_DN: + return lpt_num (pa, 20000 - (pa % 20000), f1); /* dump numeric */ + + case OP_WN: + return lpt_num (pa, 0, f1); /* write numeric */ + + case OP_WA: + for ( ; lpt_bptr < LPT_BSIZE; lpt_bptr++) { /* only fill buf */ + d = M[pa] & DIGIT; /* get digit */ + z = M[pa - 1] & DIGIT; /* get zone */ + if ((d & REC_MARK) == REC_MARK) break; /* 8-2 char? */ + lpc = alp_to_lpt[(z << 4) | d]; /* translate pair */ + if (lpc < 0) { /* bad char? */ + ind[IN_WRCHK] = ind[IN_PRCHK] = 1; /* wr chk */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + } + lpt_buf[lpt_bptr] = lpc & 0x7F; /* fill buffer */ + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + if ((f1 & 1) == 0) { ; /* print now? */ + r = lpt_print (); /* print line */ + if (r != SCPE_OK) return r; + } + return sta; + + default: /* invalid function */ + return STOP_INVFNC; + } + +return SCPE_OK; +} + +/* Print numeric */ + +t_stat lpt_num (uint32 pa, uint32 len, uint32 f1) +{ +uint32 end; +uint8 d; +int8 lpc; +t_stat r, sta; + +sta = SCPE_OK; +end = pa + len; +for ( ; lpt_bptr < LPT_BSIZE; lpt_bptr++) { /* only fill buf */ + d = M[pa]; /* get digit */ + if (len? (pa >= end): /* end reached? */ + ((d & REC_MARK) == REC_MARK)) break; + lpc = num_to_lpt[d]; /* translate */ + if (lpc < 0) { /* bad char? */ + ind[IN_WRCHK] = ind[IN_PRCHK] = 1; /* wr chk */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + } + lpt_buf[lpt_bptr++] = lpc & 0x7F; /* fill buffer */ + PP (pa); /* incr mem addr */ + } +if ((f1 & 1) == 0) { /* print now? */ + r = lpt_print (); /* print line */ + if (r != SCPE_OK) return r; + } +return sta; +} + +/* Print and space */ + +t_stat lpt_print (void) +{ +int32 i, chan, ctrl = lpt_savctrl; + +if ((lpt_unit.flags & UNIT_ATT) == 0) { /* not attached? */ + ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */ + return SCPE_UNATT; + } + +ind[IN_PRBSY] = 1; /* print busy */ +sim_activate (&lpt_unit, lpt_unit.wait); /* start timer */ + +for (i = LPT_WIDTH; i <= LPT_BSIZE; i++) /* clear unprintable */ + lpt_buf[i] = ' '; +while ((lpt_bptr > 0) && (lpt_buf[lpt_bptr - 1] == ' ')) + lpt_buf[--lpt_bptr] = 0; /* trim buffer */ +if (lpt_bptr) { /* any line? */ + fputs (lpt_buf, lpt_unit.fileref); /* print */ + lpt_unit.pos = ftell (lpt_unit.fileref); /* update pos */ + lpt_buf_init (); /* reinit buf */ + if (ferror (lpt_unit.fileref)) { /* error? */ + ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; + } + } + +lpt_savctrl = 0x61; /* reset ctrl */ +if ((ctrl & K_LIN) == ((ctrl & K_IMM)? 0: K_LIN)) /* space lines? */ + return lpt_space (ctrl & K_LCNT, FALSE); +chan = lpt_savctrl & K_CHAN; /* basic chan */ +if (lpt_savctrl & K_CH10) { /* chan 10-12? */ + if (chan == 0) chan = 10; + else if (chan == 3) chan = 11; + else if (chan == 4) chan = 12; + else chan = 0; + } +if ((chan == 0) || (chan > 12)) return STOP_INVFNC; +for (i = 1; i < cct_lnt + 1; i++) { /* sweep thru cct */ + if (CHP (chan, cct[(cct_ptr + i) % cct_lnt])) + return lpt_space (i, TRUE); + } +return STOP_CCT; /* runaway channel */ +} + +/* Space routine - space or skip n lines + + Inputs: + count = number of lines to space or skip + sflag = skip (TRUE) or space (FALSE) +*/ + +t_stat lpt_space (int32 count, int32 sflag) +{ +int32 i; + +cct_ptr = (cct_ptr + count) % cct_lnt; /* adv cct, mod lnt */ +if (sflag && CHP (0, cct[cct_ptr])) /* skip, top of form? */ + fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ +else { + for (i = 0; i < count; i++) /* count lines */ + fputc ('\n', lpt_unit.fileref); + } +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +ind[IN_PRCH9] = CHP (9, cct[cct_ptr]) != 0; /* set indicators */ +ind[IN_PRCH12] = CHP (12, cct[cct_ptr]) != 0; +if (ferror (lpt_unit.fileref)) { /* error? */ + ind[IN_PRCHK] = ind[IN_WRCHK] = 1; /* wr, pri check */ + perror ("LPT I/O error"); + clearerr (lpt_unit.fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Unit service - clear printer busy */ + +t_stat lpt_svc (UNIT *uptr) +{ +ind[IN_PRBSY] = 0; +return SCPE_OK; +} + +/* Initialize lpt buffer */ + +void lpt_buf_init (void) +{ +int32 i; + +lpt_bptr = 0; +for (i = 0; i < LPT_WIDTH + 1; i++) lpt_buf[i] = 0; +return; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_buf_init (); /* clear buffer */ +cct_ptr = 0; /* clear cct ptr */ +lpt_savctrl = 0x61; /* clear cct action */ +ind[IN_PRCHK] = ind[IN_PRBSY] = 0; /* clear indicators */ +ind[IN_PRCH9] = ind[IN_PRCH12] = 0; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +lpt_reset (&lpt_dev); +return attach_unit (uptr, cptr); +} diff --git a/I1620/i1620_pt.c b/I1620/i1620_pt.c new file mode 100644 index 0000000..b564f40 --- /dev/null +++ b/I1620/i1620_pt.c @@ -0,0 +1,489 @@ +/* i1620_pt.c: IBM 1621/1624 paper tape reader/punch simulator + + Copyright (c) 2002-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr 1621 paper tape reader + ptp 1624 paper tape punch + + 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility + 25-Apr-03 RMS Revised for extended file support +*/ + +#include "i1620_defs.h" + +#define PT_EL 0x80 /* end record */ +#define PT_X 0x40 /* X */ +#define PT_O 0x20 /* O */ +#define PT_C 0x10 /* C */ +#define PT_FD 0x7F /* deleted */ + +extern uint8 M[MAXMEMSIZE]; +extern uint8 ind[NUM_IND]; +extern UNIT cpu_unit; +extern uint32 io_stop; + +t_stat ptr_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); +t_stat ptr_read (uint8 *c, t_bool ignfeed); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptp_write (uint32 c); +t_stat ptp_num (uint32 pa, uint32 len); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0) + }; + +REG ptr_reg[] = { + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { NULL } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) + }; + +REG ptp_reg[] = { + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { NULL } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL + }; + +/* Data tables */ + +/* Paper tape reader odd parity chart: 1 = bad, 0 = ok */ + +const int8 bad_par[128] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 00 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 10 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 20 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 30 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, /* 40 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 50 */ + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, /* 60 */ + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 /* 70 */ + }; + +/* Paper tape read (7b) to numeric (one digit) */ + +const int8 ptr_to_num[128] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* - */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* C */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* O */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* OC */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* X */ + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x10, 0x1E, 0x1F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, /* XC */ + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x10, 0x1E, 0x1F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* XO */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* XOC */ + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x00, 0x0E, 0x0F + }; + +/* Paper tape read (7b) to alphameric (two digits) + Codes XO82, 82, XO842, 842 do not have consistent translations +*/ + +const int8 ptr_to_alp[128] = { + 0x00, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* - */ + 0x78, 0x79, -1, 0x33, 0x34, 0x70, -1, 0x0F, + 0x00, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* C */ + 0x78, 0x79, -1, 0x33, 0x34, 0x70, -1, 0x0F, + 0x70, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* O */ + 0x68, 0x69, 0x0A, 0x23, 0x24, 0x60, 0x0E, 0x0F, + 0x70, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, /* OC */ + 0x68, 0x69, 0x0A, 0x23, 0x24, 0x60, 0x0E, 0x0F, + 0x20, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* X */ + 0x58, 0x59, 0x5A, 0x13, 0x14, 0x50, 0x5E, 0x5F, + 0x20, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* XC */ + 0x58, 0x59, 0x5A, 0x13, 0x14, 0x50, 0x5E, 0x5F, + 0x10, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* XO */ + 0x48, 0x49, -1, 0x03, 0x04, 0x40, -1, 0x7F, + 0x10, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* XOC */ + 0x48, 0x49, -1, 0x03, 0x04, 0x40, -1, 0x7F + }; + +/* Numeric (flag + digit) to paper tape punch */ + +const int8 num_to_ptp[32] = { + 0x20, 0x01, 0x02, 0x13, 0x04, 0x15, 0x16, 0x07, /* 0 */ + 0x08, 0x19, 0x2A, 0x3B, 0x1C, 0x0D, 0x3E, 0x3F, + 0x40, 0x51, 0x52, 0x43, 0x54, 0x45, 0x46, 0x57, /* F + 0 */ + 0x58, 0x49, 0x4A, 0x5B, 0x4C, 0x5D, 0x5E, 0x4F + }; + +/* Alphameric (two digits) to paper tape punch */ + +const int8 alp_to_ptp[256] = { + 0x10, -1, 0x7A, 0x6B, 0x7C, -1, -1, 0x7F, /* 00 */ + -1, -1, 0x2A, -1, -1, -1, -1, 0x1F, + 0x70, -1, 0x4A, 0x5B, 0x4C, -1, -1, -1, /* 10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + 0x40, 0x31, 0x2A, 0x3B, 0x2C, -1, -1, -1, /* 20 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 0x1A, 0x0B, 0x1C, 0x0D, 0x0E, -1, /* 30 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 0x61, 0x62, 0x73, 0x64, 0x75, 0x76, 0x67, /* 40 */ + 0x68, 0x79, -1, -1, -1, -1, -1, -1, + 0x40, 0x51, 0x52, 0x43, 0x54, 0x45, 0x46, 0x57, /* 50 */ + 0x58, 0x49, 0x4A, -1, -1, -1, -1, 0x4F, + -1, 0x31, 0x32, 0x23, 0x34, 0x25, 0x26, 0x37, /* 60 */ + 0x38, 0x29, -1, -1, -1, -1, -1, -1, + 0x20, 0x01, 0x02, 0x13, 0x04, 0x15, 0x16, 0x07, /* 70 */ + 0x08, 0x19, 0x7A, -1, -1, -1, -1, 0x7F, + -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ + -1, -1, -1, -1, -1, -1, -1, -1 + }; + +/* Paper tape reader IO routine + + - Hard errors halt the operation and the system. + - Parity errors place an invalid character in memory and set + RDCHK, but the read continues until end of record. If IO + stop is set, the system then halts. +*/ + +t_stat ptr (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +uint32 i; +int8 mc; +uint8 ptc; +t_stat r, sta; + +sta = SCPE_OK; +switch (op) { /* case on op */ + + case OP_RN: /* read numeric */ + for (i = 0; i < MEMSIZE; i++) { /* (stop runaway) */ + r = ptr_read (&ptc, TRUE); /* read frame */ + if (r != SCPE_OK) return r; /* error? */ + if (ptc & PT_EL) { /* end record? */ + M[pa] = REC_MARK; /* store rec mark */ + return sta; /* done */ + } + if (bad_par[ptc]) { /* bad parity? */ + ind[IN_RDCHK] = 1; /* set read check */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + M[pa] = 0; /* store zero */ + } + else M[pa] = ptr_to_num[ptc]; /* translate, store */ + PP (pa); /* incr mem addr */ + } + break; + + case OP_RA: /* read alphameric */ + for (i = 0; i < MEMSIZE; i = i + 2) { /* (stop runaway) */ + r = ptr_read (&ptc, TRUE); /* read frame */ + if (r != SCPE_OK) return r; /* error? */ + if (ptc & PT_EL) { /* end record? */ + M[pa] = REC_MARK; /* store rec mark */ + M[pa - 1] = 0; + return sta; /* done */ + } + mc = ptr_to_alp[ptc]; /* translate */ + if (bad_par[ptc] || (mc < 0)) { /* bad par or char? */ + ind[IN_RDCHK] = 1; /* set read check */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + mc = 0; /* store blank */ + } + M[pa] = (M[pa] & FLAG) | (mc & DIGIT); /* store 2 digits */ + M[pa - 1] = (M[pa - 1] & FLAG) | ((mc >> 4) & DIGIT); + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + break; + + default: /* invalid function */ + return STOP_INVFNC; + } + +return STOP_RWRAP; +} + +/* Binary paper tape reader IO routine - see above for error handling */ + +t_stat btr (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +uint32 i; +uint8 ptc; +t_stat r, sta; + +if ((cpu_unit.flags & IF_BIN) == 0) return STOP_INVIO; + +sta = SCPE_OK; +switch (op) { /* case on op */ + + case OP_RA: /* read alphameric */ + for (i = 0; i < MEMSIZE; i = i + 2) { /* (stop runaway) */ + r = ptr_read (&ptc, FALSE); /* read frame */ + if (r != SCPE_OK) return r; /* error? */ + if (ptc & PT_EL) { /* end record? */ + M[pa] = REC_MARK; /* store rec mark */ + M[pa - 1] = 0; + return sta; /* done */ + } + if (bad_par[ptc]) { /* bad parity? */ + ind[IN_RDCHK] = 1; /* set read check */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + } + M[pa] = (M[pa] & FLAG) | (ptc & 07); /* store 2 digits */ + M[pa - 1] = (M[pa - 1] & FLAG) | + (((ptc >> 5) & 06) | ((ptc >> 3) & 1)); + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + break; + + default: /* invalid function */ + return STOP_INVFNC; + } + +return STOP_RWRAP; +} + +/* Read ptr frame - all errors are 'hard' errors and halt the system */ + +t_stat ptr_read (uint8 *c, t_bool ignfeed) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */ + ind[IN_RDCHK] = 1; /* no, error */ + return SCPE_UNATT; + } + +do { + if ((temp = getc (ptr_unit.fileref)) == EOF) { /* read char */ + ind[IN_RDCHK] = 1; /* err, rd chk */ + if (feof (ptr_unit.fileref)) + printf ("PTR end of file\n"); + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } + *c = temp & 0377; /* save char */ + ptr_unit.pos = ptr_unit.pos + 1; /* incr file addr */ + } while (ignfeed && (*c == PT_FD)); /* until not feed */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +return SCPE_OK; +} + +/* Bootstrap routine */ + +const static uint8 boot_rom[] = { + 4, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* NOP */ + 3, 6, 0, 0, 0, 3, 1, 0, 0, 3, 0, 0, /* RNPT 31 */ + 2, 5, 0, 0, 0, 7, 1, 0, 0, 0, 0, 0, /* TD 71,loc */ + 3, 6, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, /* RNPT loc1 */ + 2, 6, 0, 0, 0, 6, 6, 0, 0, 0, 3, 5, /* TF 66,35 */ + 1, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* TDM loc2,loc3 */ + 4, 9, 0, 0, 0, 1, 2, 0, 0, 0, 0, 0 /* BR 12 */ + }; + +#define BOOT_START 0 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* Paper tape punch IO routine + + - Hard errors halt the operation and the system. + - Parity errors stop the operation and set WRCHK. + If IO stop is set, the system then halts. +*/ + +t_stat ptp (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +uint32 i; +int8 ptc; +uint8 z, d; +t_stat r; + +switch (op) { /* decode op */ + + case OP_DN: + return ptp_num (pa, 20000 - (pa % 20000)); /* dump numeric */ + + case OP_WN: + return ptp_num (pa, 0); /* punch numeric */ + + case OP_WA: + for (i = 0; i < MEMSIZE; i = i + 2) { /* stop runaway */ + d = M[pa] & DIGIT; /* get digit */ + z = M[pa - 1] & DIGIT; /* get zone */ + if ((d & REC_MARK) == REC_MARK) /* 8-2 char? */ + return ptp_write (PT_EL); /* end record */ + ptc = alp_to_ptp[(z << 4) | d]; /* translate pair */ + if (ptc < 0) { /* bad char? */ + ind[IN_WRCHK] = 1; /* write check */ + CRETIOE (io_stop, STOP_INVCHR); + } + r = ptp_write (ptc); /* write char */ + if (r != SCPE_OK) return r; /* error? */ + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + break; + + default: /* invalid function */ + return STOP_INVFNC; + } + +return STOP_RWRAP; +} + +/* Binary paper tape punch IO routine - see above for error handling */ + +t_stat btp (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +uint32 i; +uint8 ptc, z, d; +t_stat r; + +if ((cpu_unit.flags & IF_BIN) == 0) return STOP_INVIO; + +switch (op) { /* decode op */ + + case OP_WA: + for (i = 0; i < MEMSIZE; i = i + 2) { /* stop runaway */ + d = M[pa] & DIGIT; /* get digit */ + z = M[pa - 1] & DIGIT; /* get zone */ + if ((d & REC_MARK) == REC_MARK) /* 8-2 char? */ + return ptp_write (PT_EL); /* end record */ + ptc = ((z & 06) << 5) | ((z & 01) << 3) | (d & 07); + if (bad_par[ptc]) ptc = ptc | PT_C; /* set parity */ + r = ptp_write (ptc); /* write char */ + if (r != SCPE_OK) return r; /* error? */ + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + break; + + default: /* invalid function */ + return STOP_INVFNC; + } + +return STOP_RWRAP; +} + +/* Punch tape numeric - cannot generate parity errors */ + +t_stat ptp_num (uint32 pa, uint32 len) +{ +t_stat r; +uint8 d; +uint32 i, end; + +end = pa + len; +for (i = 0; i < MEMSIZE; i++) { /* stop runaway */ + d = M[pa] & (FLAG | DIGIT); /* get char */ + if (len? (pa >= end): /* dump: end reached? */ + ((d & REC_MARK) == REC_MARK)) /* write: rec mark? */ + return ptp_write (PT_EL); /* end record */ + r = ptp_write (num_to_ptp[d]); /* write */ + if (r != SCPE_OK) return r; /* error? */ + PP (pa); /* incr mem addr */ + } +return STOP_RWRAP; +} + +/* Write ptp frame - all errors are hard errors */ + +t_stat ptp_write (uint32 c) +{ +if ((ptp_unit.flags & UNIT_ATT) == 0) { /* attached? */ + ind[IN_WRCHK] = 1; /* no, error */ + return SCPE_UNATT; + } +if (putc (c, ptp_unit.fileref) == EOF) { /* write char */ + ind[IN_WRCHK] = 1; /* error? */ + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +ptp_unit.pos = ptp_unit.pos + 1; /* count char */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +return SCPE_OK; +} diff --git a/I1620/i1620_sys.c b/I1620/i1620_sys.c new file mode 100644 index 0000000..3c62370 --- /dev/null +++ b/I1620/i1620_sys.c @@ -0,0 +1,527 @@ +/* i1620_sys.c: IBM 1620 simulator interface + + Copyright (c) 2002-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include "i1620_defs.h" +#include + +#define LINE_LNT 50 + +extern DEVICE cpu_dev, tty_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE lpt_dev; +extern DEVICE cdr_dev, cdp_dev; +extern DEVICE dp_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint8 M[MAXMEMSIZE]; +extern char cdr_to_alp[128], alp_to_cdp[256]; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 1620"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = LINE_LNT; + +DEVICE *sim_devices[] = { + &cpu_dev, + &tty_dev, + &ptr_dev, + &ptp_dev, + &cdr_dev, + &cdp_dev, + &lpt_dev, + &dp_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "Invalid instruction", + "Invalid digit", + "Invalid character", + "Invalid indicator", + "Invalid digit in P address", + "Invalid P address", + "P address exceeds indirect address limit", + "Invalid digit in Q address", + "Invalid Q address", + "Q address exceeds indirect address limit", + "Invalid IO device", + "Invalid return register", + "Invalid IO function", + "Instruction address must be even", + "Invalid select code", + "Index instruction with no band selected", + "P address must be odd", + "DCF address must be even", + "Invalid disk drive", + "Invalid disk sector address", + "Invalid disk sector count", + "Invalid disk buffer address", + "Disk address compare error", + "Disk write check error", + "Disk cylinder overflow error", + "Disk wrong length record error", + "Invalid CCT", + "Field exceeds memory", + "Record exceeds memory", + "No card in reader", + "Overflow check", + "Exponent check", + "Write address function disabled", + "Floating point mantissa too long", + "Floating point mantissa lengths unequal", + "Floating point exponent flag missing", + "Floating point divide by zero" + }; + +/* Binary loader -- load carriage control tape + + A carriage control tape consists of entries of the form + + (repeat count) column number,column number,column number,... + + The CCT entries are stored in cct[0:lnt-1], cctlnt contains the + number of entries +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; +t_stat r; +extern int32 cct_lnt, cct_ptr, cct[CCT_LNT]; +char cbuf[CBUFSIZE], gbuf[CBUFSIZE]; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +ptr = 0; +for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ + mask = 0; + if (*cptr == '(') { /* repeat count? */ + cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ + rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */ + if (r != SCPE_OK) return SCPE_FMT; + } + else rpt = 1; + while (*cptr != 0) { /* get col no's */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + col = get_uint (gbuf, 10, 12, &r); /* column number */ + if (r != SCPE_OK) return SCPE_FMT; + mask = mask | (1 << col); /* set bit */ + } + for ( ; rpt > 0; rpt--) { /* store vals */ + if (ptr >= CCT_LNT) return SCPE_FMT; + cctbuf[ptr++] = mask; + } + } +if (ptr == 0) return SCPE_FMT; +cct_lnt = ptr; +cct_ptr = 0; +for (rpt = 0; rpt < cct_lnt; rpt++) cct[rpt] = cctbuf[rpt]; +return SCPE_OK; +} + +/* Symbol table */ + +struct opc { + char *str; /* mnemonic */ + uint32 opv; /* opcode & flags */ + uint32 qv; /* q field */ + }; + +#define I_V_FL 16 /* flags */ +#define I_M_QX 0x01 /* Q indexable */ +#define I_M_QM 0x02 /* Q immediate */ +#define I_M_QNP 0x00 /* Q no print */ +#define I_M_QCP 0x04 /* Q cond print */ +#define I_M_QP 0x08 /* Q print */ +#define I_M_PCP 0x00 /* P cond print */ +#define I_M_PP 0x10 /* P print */ +#define I_GETQF(x) (((x) >> I_V_FL) & 0x03) +#define I_GETQP(x) (((x) >> I_V_FL) & 0x0C) +#define I_GETPP(x) (((x) >> I_V_FL) & 0x10) + +#define I_2 ((I_M_PP | I_M_QP | I_M_QX) << I_V_FL) +#define I_2M ((I_M_PP | I_M_QP | I_M_QM) << I_V_FL) +#define I_2X ((I_M_PP | I_M_QP | I_M_QX | I_M_QM) << I_V_FL) +#define I_2S ((I_M_PP | I_M_QP) << I_V_FL) +#define I_1 ((I_M_PP | I_M_QCP) << I_V_FL) +#define I_1E ((I_M_PP | I_M_QNP) << I_V_FL) +#define I_0 ((I_M_PCP | I_M_QCP) << I_V_FL) +#define I_0E ((I_M_PCP | I_M_QNP) << I_V_FL) + +struct opc opcode[] = { + { "RNTY", 36+I_1E, 100 }, { "RATY", 37+I_1E, 100 }, + { "WNTY", 38+I_1E, 100 }, { "WATY", 39+I_1E, 100 }, + { "DNTY", 35+I_1E, 100 }, { "SPTY", 34+I_0E, 101 }, + { "RCTY", 34+I_0E, 102 }, { "BKTY", 34+I_0E, 103 }, + { "IXTY", 34+I_0E, 104 }, { "TBTY", 34+I_0E, 108 }, + { "RNPT", 36+I_1E, 300 }, { "RAPT", 37+I_1E, 300 }, + { "WNPT", 38+I_1E, 200 }, { "WAPT", 39+I_1E, 200 }, + { "DNPT", 35+I_1E, 200 }, + { "RNCD", 36+I_1E, 500 }, { "RACD", 37+I_1E, 500 }, + { "WNCD", 38+I_1E, 400 }, { "WACD", 39+I_1E, 400 }, + { "DNCD", 35+I_1E, 400 }, + { "PRN", 38+I_1E, 900 }, { "PRNS", 38+I_1E, 901 }, + { "PRA", 39+I_1E, 900 }, { "PRAS", 39+I_1E, 901 }, + { "PRD", 35+I_1E, 900 }, { "PRDS", 35+I_1E, 901 }, + { "SK", 34+I_1E, 701 }, + { "RDGN", 36+I_1E, 700 }, { "CDGN", 36+I_1E, 701 }, + { "RDN", 36+I_1E, 702 }, { "CDN", 36+I_1E, 703 }, + { "RTGN", 36+I_1E, 704 }, { "CTGN", 36+I_1E, 705 }, + { "RTN", 36+I_1E, 706 }, { "CTN", 36+I_1E, 707 }, + { "WDGN", 38+I_1E, 700 }, { "WDN", 38+I_1E, 702 }, + { "WTGN", 38+I_1E, 704 }, { "WTN", 38+I_1E, 706 }, + { "RBPT", 37+I_1E, 3300 }, { "WBPT", 39+I_1E, 3200 }, + { "BC1", 46+I_1E, 100 }, { "BNC1", 47+I_1E, 100 }, + { "BC2", 46+I_1E, 200 }, { "BNC2", 47+I_1E, 200 }, + { "BC3", 46+I_1E, 300 }, { "BNC3", 47+I_1E, 300 }, + { "BC4", 46+I_1E, 400 }, { "BNC4", 47+I_1E, 400 }, + { "BLC", 46+I_1E, 900 }, { "BNLC", 47+I_1E, 900 }, + { "BH", 46+I_1E, 1100 }, { "BNH", 47+I_1E, 1100 }, + { "BP", 46+I_1E, 1100 }, { "BNP", 47+I_1E, 1100 }, + { "BE", 46+I_1E, 1200 }, { "BNE", 47+I_1E, 1200 }, + { "BZ", 46+I_1E, 1200 }, { "BNZ", 47+I_1E, 1200 }, + { "BNL", 46+I_1E, 1300 }, { "BL", 47+I_1E, 1300 }, + { "BNN", 46+I_1E, 1300 }, { "BN", 47+I_1E, 1300 }, + { "BV", 46+I_1E, 1400 }, { "BNV", 47+I_1E, 1400 }, + { "BXV", 46+I_1E, 1500 }, { "BNXV", 47+I_1E, 1500 }, + { "BA", 46+I_1E, 1900 }, { "BNA", 47+I_1E, 1900 }, + { "BNBS", 46+I_1E, 3000 }, { "BEBS", 47+I_1E, 3000 }, + { "BBAS", 46+I_1E, 3100 }, { "BANS", 47+I_1E, 3100 }, + { "BBBS", 46+I_1E, 3200 }, { "BBNS", 47+I_1E, 3200 }, + { "BCH9", 46+I_1E, 3300 }, + { "BCOV", 46+I_1E, 3400 }, + { "BSNX", 60+I_1E, 0 }, { "BSBA", 60+I_1E, 1 }, + { "BSBB", 60+I_1E, 2 }, + { "BSNI", 60+I_1E, 8 }, { "BSIA", 60+I_1E, 9 }, + + { "FADD", 1+I_2, 0 }, { "FSUB", 2+I_2, 0 }, + { "FMUL", 3+I_2, 0 }, { "FSL", 5+I_2, 0 }, + { "TFL", 6+I_2, 0 }, { "BTFL", 7+I_2, 0 }, + { "FSR", 8+I_2, 0 }, { "FDIV", 9+I_2, 0 }, + { "BTAM", 10+I_2M, 0 }, { "AM", 11+I_2M, 0 }, + { "SM", 12+I_2M, 0 }, { "MM", 13+I_2M, 0 }, + { "CM", 14+I_2M, 0 }, { "TDM", 15+I_2S, 0 }, + { "TFM", 16+I_2M, 0 }, { "BTM", 17+I_2M, 0 }, + { "LDM", 18+I_2M, 0 }, { "DM", 19+I_2M, 0 }, + { "BTA", 20+I_2, 0 }, { "A", 21+I_2, 0 }, + { "S", 22+I_2, 0 }, { "M", 23+I_2, 0 }, + { "C", 24+I_2, 0 }, { "TD", 25+I_2, 0 }, + { "TF", 26+I_2, 0 }, { "BT", 27+I_2, 0 }, + { "LD", 28+I_2, 0 }, { "D", 29+I_2, 0 }, + { "TRNM", 30+I_2, 0 }, { "TR", 31+I_2, 0 }, + { "SF", 32+I_1, 0 }, { "CF", 33+I_1, 0 }, + { "K", 34+I_2S, 0 }, { "DN", 35+I_2S, 0 }, + { "RN", 36+I_2S, 0 }, { "RA", 37+I_2S, 0 }, + { "WN", 38+I_2S, 0 }, { "WA", 39+I_2S, 0 }, + { "NOP", 41+I_0, 0 }, { "BB", 42+I_0, 0 }, + { "BD", 43+I_2, 0 }, { "BNF", 44+I_2, 0 }, + { "BNR", 45+I_2, 0 }, { "BI", 46+I_2S, 0 }, + { "BNI", 47+I_2S, 0 }, { "H", 48+I_0, 0 }, + { "B", 49+I_1, 0 }, { "BNG", 55+I_2, 0 }, + { "BS", 60+I_2S, 0 }, { "BX", 61+I_2, 0 }, + { "BXM", 62+I_2X, 0 }, { "BCX", 63+I_2, 0 }, + { "BCXM", 64+I_2X, 0 }, { "BLX", 65+I_2, 0 }, + { "BLXM", 66+I_2X, 0 }, { "BSX", 67+I_2, 0 }, + { "MA", 70+I_2, 0 }, { "MF", 71+I_2, 0 }, + { "TNS", 72+I_2, 0 }, { "TNF", 73+I_2, 0 }, + { "BBT", 90+I_2, 0 }, { "BMK", 91+I_2, 0 }, + { "ORF", 92+I_2, 0 }, { "ANDF", 93+I_2, 0 }, + { "CPFL", 94+I_2, 0 }, { "EORF", 95+I_2, 0 }, + { "OTD", 96+I_2, 0 }, { "DTO", 97+I_2, 0 }, + { NULL, 0, 0 } + }; + +/* Print an address from five characters */ + +void fprint_addr (FILE *of, int32 spc, t_value *dig, t_bool flg) +{ +int32 i, idx; + +fputc (spc, of); /* spacer */ +if (dig[ADDR_LEN - 1] & FLAG) { /* signed? */ + fputc ('-', of); /* print minus */ + dig[ADDR_LEN - 1] = dig[ADDR_LEN - 1] & ~FLAG; + } +for (i = 0; i < ADDR_LEN; i++) /* print digits */ + fprintf (of, "%X", dig[i] & DIGIT); +if ((cpu_unit.flags & IF_IDX) && flg) { /* indexing? */ + for (i = idx = 0; i < ADDR_LEN - 2; i++) { /* get index reg */ + if (dig[ADDR_LEN - 2 - i] & FLAG) + idx = idx | (1 << i); + dig[ADDR_LEN - 2 - i] = dig[ADDR_LEN - 2 - i] & ~FLAG; + } + if (idx) fprintf (of, "(%d)", idx); /* print */ + } +return; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current address + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 pmp, qmp, i, c, d, any; +uint32 op, qv, opfl; + +if (uptr == NULL) uptr = &cpu_unit; +if (sw & SWMASK ('C')) { /* character? */ + if (uptr->flags & UNIT_BCD) { + if (addr & 1) return SCPE_ARG; /* must be even */ + c = ((val[0] & DIGIT) << 4) | (val[1] & DIGIT); + if (alp_to_cdp[c] > 0) + fprintf (of, "%c", alp_to_cdp[c]); + else fprintf (of, "<%02x>", c); + return -1; + } + else fprintf (of, FMTASC (val[0] & 0177)); + return SCPE_OK; + } +if ((uptr->flags & UNIT_BCD) == 0) return SCPE_ARG; /* CPU or disk? */ +if (sw & SWMASK ('D')) { /* dump? */ + for (i = d = 0; i < LINE_LNT; i++) d = d | val[i]; + if (d & FLAG) { /* any flags? */ + for (i = 0; i < LINE_LNT; i++) /* print flags */ + fprintf (of, (val[i] & FLAG)? "_": " "); + fprintf (of, "\n\t"); + } + for (i = 0; i < LINE_LNT; i++) /* print digits */ + fprintf (of, "%X", val[i] & DIGIT) ; + return -(i - 1); + } +if (sw & SWMASK ('S')) { /* string? */ + if (addr & 1) return SCPE_ARG; /* must be even */ + for (i = 0; i < LINE_LNT; i = i + 2) { + c = ((val[i] & DIGIT) << 4) | (val[i + 1] & DIGIT); + if (alp_to_cdp[c] < 0) break; + fprintf (of, "%c", alp_to_cdp[c]); + } + if (i == 0) { + fprintf (of, "<%02X>", c); + return -1; + } + return -(i - 1); + } +if ((sw & SWMASK ('M')) == 0) return SCPE_ARG; + +if (addr & 1) return SCPE_ARG; /* must be even */ +op = ((val[0] & DIGIT) * 10) + (val[1] & DIGIT); /* get opcode */ +for (i = qv = pmp = qmp = 0; i < ADDR_LEN; i++) { /* test addr */ + if (val[I_P + i]) pmp = 1; + if (val[I_Q + i]) qmp = 1; + qv = (qv * 10) + (val[I_Q + i] & DIGIT); + } +if ((val[0] | val[1]) & FLAG) pmp = qmp = 1; /* flags force */ +for (i = 0; opcode[i].str != NULL; i++) { /* find opcode */ + opfl = opcode[i].opv & 0xFF0000; + if ((op == (opcode[i].opv & 0xFF)) && + ((qv == opcode[i].qv) || + ((opfl != I_1E) && (opfl != I_0E)))) break; + } +if (opcode[i].str == NULL) return SCPE_ARG; +if (I_GETQP (opfl) == I_M_QNP) qmp = 0; /* Q no print? */ + +fprintf (of, opcode[i].str); /* print opcode */ +if (I_GETPP (opfl) == I_M_PP) /* P required? */ + fprint_addr (of, ' ', &val[I_P], I_M_QX); +else if ((I_GETPP (opfl) == I_M_PCP) && (pmp || qmp)) /* P opt & needed? */ + fprint_addr (of, ' ', &val[I_P], 0); +if (I_GETQP (opfl) == I_M_QP) { /* Q required? */ + fprint_addr (of, ',', &val[I_Q], I_GETQF (opfl)); + if (I_GETQF (opfl) & I_M_QM) /* immediate? */ + val[I_Q] = val[I_Q] & ~FLAG; /* clr hi Q flag */ + } +else if ((I_GETQP (opfl) == I_M_QCP) && (pmp || qmp)) /* Q opt & needed? */ + fprint_addr (of, ',', &val[I_Q], 0); +for (i = any = 0; i < INST_LEN; i++) { /* print rem flags */ + if (val[i] & FLAG) { + if (!any) fputc (',', of); + any = 1; + fprintf (of, "%d", i); + } + } +return -(INST_LEN - 1); +} + +/* parse_addr - get sign + address + index */ + +t_stat parse_addr (char *cptr, t_value *val, int32 flg) +{ +int32 i, sign = 0, addr, index; +static int32 idx_tst[ADDR_LEN] = { 0, 4, 2, 1, 0 }; +char *tptr; + +if (*cptr == '+') cptr++; /* +? skip */ +else if (*cptr == '-') { /* -? skip, flag */ + sign = 1; + cptr++; + } +errno = 0; /* get address */ +addr = strtoul (cptr, &tptr, 16); +if (errno || (cptr == tptr) || (addr > 0xFFFFF)) /* err or too big? */ + return SCPE_ARG; +if ((cpu_unit.flags & IF_IDX) && (flg & I_M_QX) && /* index allowed? */ + (*tptr == '(')) { /* index specified */ + errno = 0; + index = strtoul (cptr = tptr + 1, &tptr, 10); /* get index */ + if (errno || (cptr == tptr) || (index > 7)) /* err or too big? */ + return SCPE_ARG; + if (*tptr++ != ')') return SCPE_ARG; + } +else index = 0; +if (*tptr != 0) return SCPE_ARG; /* all done? */ +for (i = ADDR_LEN - 1; i >= 0; i--) { /* cvt addr to dig */ + val[i] = (addr & 0xF) | ((index & idx_tst[i])? FLAG: 0); + addr = addr >> 4; + } +if (sign) val[ADDR_LEN - 1] = val[ADDR_LEN - 1] | FLAG; /* set sign */ +if (flg & I_M_QM) val[0] = val[0] | FLAG; /* set immediate */ +return SCPE_OK; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 i, qv, opfl, last; +char t, la, *fptr, gbuf[CBUFSIZE]; + +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('C')) || ((*cptr == '\'') && cptr++)) { /* character? */ + if ((t = *cptr & 0x7F) == 0) return SCPE_ARG; /* get char */ + if (uptr->flags & UNIT_BCD) { /* BCD? */ + if (addr & 1) return SCPE_ARG; + t = cdr_to_alp[t]; /* convert */ + if (t < 0) return SCPE_ARG; /* invalid? */ + val[0] = (t >> 4) & DIGIT; /* store */ + val[1] = t & DIGIT; + return -1; + } + else val[0] = t; /* store ASCII */ + return SCPE_OK; + } + +if ((uptr->flags & UNIT_BCD) == 0) return SCPE_ARG; /* CPU or disk? */ +if ((sw & SWMASK ('S')) || ((*cptr == '"') && cptr++)) { /* string? */ + if (addr & 1) return SCPE_ARG; /* must be even */ + for (i = 0; (i < sim_emax) && (*cptr != 0); i = i + 2) { + t = *cptr++ & 0x7F; /* get character */ + t = cdr_to_alp[t]; /* convert */ + if (t < 0) return SCPE_ARG; /* invalid? */ + val[i] = (t >> 4) & DIGIT; /* store */ + val[i + 1] = t & DIGIT; + } + if (i == 0) return SCPE_ARG; /* final check */ + return -(i - 1); + } + +if (addr & 1) return SCPE_ARG; /* even addr? */ +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; opcode[i].str != NULL; i++) { /* look it up */ + if (strcmp (gbuf, opcode[i].str) == 0) break; + } +if (opcode[i].str == NULL) return SCPE_ARG; /* successful? */ +opfl = opcode[i].opv & 0xFF0000; /* get flags */ +val[0] = (opcode[i].opv & 0xFF) / 10; /* store opcode */ +val[1] = (opcode[i].opv & 0xFF) % 10; +qv = opcode[i].qv; +for (i = ADDR_LEN - 1; i >= 0; i--) { /* set P,Q fields */ + val[I_P + i] = 0; + val[I_Q + i] = qv % 10; + qv = qv /10; + } + +cptr = get_glyph (cptr, gbuf, ','); /* get P field */ +if (gbuf[0]) { /* any? */ + if (parse_addr (gbuf, &val[I_P], (I_GETPP (opfl)? + I_M_QX: 0))) return SCPE_ARG; + } +else if (I_GETPP (opfl) == I_M_PP) return SCPE_ARG; + +if (I_GETQP (opfl) != I_M_QNP) { /* Q field allowed? */ + cptr = get_glyph (cptr, gbuf, ','); /* get Q field */ + if (gbuf[0]) { /* any? */ + if (parse_addr (gbuf, &val[I_Q], I_GETQF (opfl))) + return SCPE_ARG; + } + else if (I_GETQP (opfl) == I_M_QP) return SCPE_ARG; + } + +cptr = get_glyph (cptr, fptr = gbuf, ' '); /* get flag field */ +last = -1; /* none yet */ +while (t = *fptr++) { /* loop through */ + if ((t < '0') || (t > '9')) return SCPE_ARG; /* must be digit */ + t = t - '0'; /* convert */ + if (t == 1) { /* ambiguous? */ + la = *fptr++; /* get next */ + if (la == '0') t = 10; /* 10? */ + else if ((la == '1') && (*fptr == 0)) t = 11; /* 11 & end field? */ + else --fptr; /* dont lookahead */ + } + if (t <= last) return SCPE_ARG; /* in order? */ + val[t] = val[t] | FLAG; /* set flag */ + last = t; /* continue */ + } + +if (*cptr != 0) return SCPE_ARG; +return -(INST_LEN - 1); +} diff --git a/I1620/i1620_tty.c b/I1620/i1620_tty.c new file mode 100644 index 0000000..aacbbd0 --- /dev/null +++ b/I1620/i1620_tty.c @@ -0,0 +1,375 @@ +/* i1620_tty.c: IBM 1620 typewriter + + Copyright (c) 2002-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tty console typewriter + + 21-Sep-05 RMS Revised translation tables for 7094/1401 compatibility + 22-Dec-02 RMS Added break test +*/ + +#include "i1620_defs.h" + +#define TTO_COLMAX 80 + +int32 tto_col = 0; + +extern uint8 M[MAXMEMSIZE]; +extern uint8 ind[NUM_IND]; +extern UNIT cpu_unit; +extern uint32 io_stop; + +void tti_unlock (void); +t_stat tti_rnum (int8 *c); +t_stat tti_ralp (int8 *c); +t_stat tti_read (int8 *c); +t_stat tto_num (uint32 pa, uint32 len); +t_stat tto_write (uint32 c); +t_stat tty_svc (UNIT *uptr); +t_stat tty_reset (DEVICE *dptr); + +/* TTY data structures + + tty_dev TTY device descriptor + tty_unit TTY unit descriptor + tty_reg TTY register list +*/ + +UNIT tty_unit = { UDATA (&tty_svc, 0, 0), KBD_POLL_WAIT }; + +REG tty_reg[] = { + { DRDATA (COL, tto_col, 7) }, + { DRDATA (TIME, tty_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +DEVICE tty_dev = { + "TTY", &tty_unit, tty_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &tty_reset, + NULL, NULL, NULL + }; + +/* Data tables */ + +/* Keyboard to numeric */ + +const char *tti_to_num = "0123456789|=@:;}"; + +/* Keyboard to alphameric (digit pair) - translates LC to UC */ + +const int8 tti_to_alp[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + 0x00, 0x02, -1, 0x33, 0x13, 0x24, 0x10, 0x34, /* !"#$%&' */ + 0x24, 0x04, 0x14, 0x10, 0x23, 0x20, 0x03, 0x21, /* ()*+,-./ */ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, /* 01234567 */ + 0x78, 0x79, -1, -1, -1, 0x33, -1, -1, /* 89:;<=>? */ + 0x34, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* @ABCDEFG */ + 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* HIJKLMNO */ + 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* PQRSTUVW */ + 0x67, 0x68, 0x69, -1, -1, -1, -1, -1, /* XYZ[\]^_ */ + -1, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* `abcdefg */ + 0x48, 0x49, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, /* hijklmno */ + 0x57, 0x58, 0x59, 0x62, 0x63, 0x64, 0x65, 0x66, /* pqrstuvw */ + 0x67, 0x68, 0x69, -1, -1, 0x0F, -1, -1 /* xyz{|}~ */ + }; + +/* Numeric (digit) to typewriter */ + +const char num_to_tto[16] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '|', '=', '@', ':', ';', '}' + }; + +/* Alphameric (digit pair) to typewriter */ + +const char alp_to_tto[256] = { + ' ', -1, '?', '.', ')', -1, -1, -1, /* 00 */ + -1, -1, -1, -1, -1, -1, -1, -1, + '+', -1, '!', '$', '*', ' ', -1, -1, /* 10 */ + -1, -1, -1, -1, -1, -1, -1, -1, + '-', '/', '|', ',', '(', -1, -1, -1, /* 20 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, '0', '=', '@', ':', -1, -1, /* 30 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, 'A', 'B', 'C', 'D', 'E', 'F', 'G', /* 40 */ + 'H', 'I', -1, -1, -1, -1, -1, -1, + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', /* 50 */ + 'Q', 'R', -1, -1, -1, -1, -1, -1, + -1, '/', 'S', 'T', 'U', 'V', 'W', 'X', /* 60 */ + 'Y', 'Z', -1, -1, -1, -1, -1, -1, + '0', '1', '2', '3', '4', '5', '6', '7', /* 70 */ + '8', '9', -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* A0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* B0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* C0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* D0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* E0 */ + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, /* F0 */ + -1, -1, -1, -1, -1, -1, -1, -1 + }; + +/* Terminal IO + + - On input, parity errors cannot occur. + - On input, release-start does NOT cause a record mark to be stored. + - On output, invalid characters type an invalid character and set WRCHK. + If IO stop is set, the system halts at the end of the operation. +*/ + +t_stat tty (uint32 op, uint32 pa, uint32 f0, uint32 f1) +{ +t_addr i; +uint8 d; +int8 ttc; +t_stat r, sta; + +sta = SCPE_OK; +switch (op) { /* case on op */ + + case OP_K: /* control */ + switch (f1) { /* case on control */ + case 1: /* space */ + tto_write (' '); + break; + case 2: /* return */ + tto_write ('\r'); + break; + case 3: /* backspace */ + if ((cpu_unit.flags & IF_MII) == 0) return STOP_INVFNC; + tto_write ('\b'); + break; + case 4: /* index */ + if ((cpu_unit.flags & IF_MII) == 0) return STOP_INVFNC; + tto_write ('\n'); + break; + case 8: /* tab */ + tto_write ('\t'); + break; + default: + return STOP_INVFNC; + } + return SCPE_OK; + + case OP_RN: /* read numeric */ + tti_unlock (); /* unlock keyboard */ + for (i = 0; i < MEMSIZE; i++) { /* (stop runaway) */ + r = tti_rnum (&ttc); /* read char */ + if (r != SCPE_OK) return r; /* error? */ + if (ttc == 0x7F) return SCPE_OK; /* end record? */ + M[pa] = ttc & (FLAG | DIGIT); /* store char */ + PP (pa); /* incr mem addr */ + } + break; + + case OP_RA: /* read alphameric */ + tti_unlock (); + for (i = 0; i < MEMSIZE; i = i + 2) { /* (stop runaway) */ + r = tti_ralp (&ttc); /* read char */ + if (r != SCPE_OK) return r; /* error? */ + if (ttc == 0x7F) return SCPE_OK; /* end record? */ + M[pa] = (M[pa] & FLAG) | (ttc & DIGIT); /* store 2 digits */ + M[pa - 1] = (M[pa - 1] & FLAG) | ((ttc >> 4) & DIGIT); + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + break; + + case OP_DN: + return tto_num (pa, 20000 - (pa % 20000)); /* dump numeric */ + + case OP_WN: + return tto_num (pa, 0); /* type numeric */ + + case OP_WA: + for (i = 0; i < MEMSIZE; i = i + 2) { /* stop runaway */ + d = M[pa] & DIGIT; /* get digit */ + if ((d & 0xA) == REC_MARK) return sta; /* 8-2 char? done */ + d = ((M[pa - 1] & DIGIT) << 4) | d; /* get digit pair */ + ttc = alp_to_tto[d]; /* translate */ + if (ttc < 0) { /* bad char? */ + ind[IN_WRCHK] = 1; /* set write check */ + if (io_stop) sta = STOP_INVCHR; /* set return status */ + } + tto_write (ttc & 0x7F); /* write */ + pa = ADDR_A (pa, 2); /* incr mem addr */ + } + break; + + default: /* invalid function */ + return STOP_INVFNC; + } + +return STOP_RWRAP; +} + +/* Read numerically - cannot generate parity errors */ + +t_stat tti_rnum (int8 *c) +{ +int8 raw, flg = 0; +char *cp; +t_stat r; + +*c = -1; /* no char yet */ +do { + r = tti_read (&raw); /* get char */ + if (r != SCPE_OK) return r; /* error? */ + if (raw == '\r') *c = 0x7F; /* return? mark */ + else if ((raw == '~') || (raw == '`')) flg = FLAG; /* flag? mark */ + else if (cp = strchr (tti_to_num, raw)) /* legal? */ + *c = ((int8) (cp - tti_to_num)) | flg; /* assemble char */ + else raw = 007; /* beep! */ + tto_write (raw); /* echo */ + } while (*c == -1); +return SCPE_OK; +} + +/* Read alphamerically - cannot generate parity errors */ + +t_stat tti_ralp (int8 *c) +{ +int8 raw; +t_stat r; + +*c = -1; /* no char yet */ +do { + r = tti_read (&raw); /* get char */ + if (r != SCPE_OK) return r; /* error? */ + if (raw == '\r') *c = 0x7F; /* return? mark */ + else if (tti_to_alp[raw] >= 0) /* legal char? */ + *c = tti_to_alp[raw]; /* xlate */ + else raw = 007; /* beep! */ + tto_write (raw); /* echo */ + } while (*c == -1); +return SCPE_OK; +} + +/* Read from keyboard */ + +t_stat tti_read (int8 *c) +{ +int32 t; + +do { + t = sim_poll_kbd (); /* get character */ + } while ((t == SCPE_OK) || (t & SCPE_BREAK)); /* ignore break */ +if (t < SCPE_KFLAG) return t; /* error? */ +*c = t & 0177; /* store character */ +return SCPE_OK; +} + +/* Write numerically - cannot generate parity errors */ + +t_stat tto_num (uint32 pa, uint32 len) +{ +t_stat r; +uint8 d; +uint32 i, end; + +end = pa + len; +for (i = 0; i < MEMSIZE; i++) { /* (stop runaway) */ + d = M[pa]; /* get char */ + if (len? (pa >= end): /* dump: end reached? */ + ((d & REC_MARK) == REC_MARK)) /* write: rec mark? */ + return SCPE_OK; /* end operation */ + if (d & FLAG) tto_write ('`'); /* flag? */ + r = tto_write (num_to_tto[d & DIGIT]); /* write */ + if (r != SCPE_OK) return r; /* error? */ + PP (pa); /* incr mem addr */ + } +return STOP_RWRAP; +} + +/* Write, maintaining position */ + +t_stat tto_write (uint32 c) +{ +int32 rpt; + +if (c == '\t') { /* tab? */ + rpt = 8 - (tto_col % 8); /* distance to next */ + tto_col = tto_col + rpt; /* tab over */ + while (rpt-- > 0) sim_putchar (' '); /* use spaces */ + return SCPE_OK; + } +if (c == '\r') { /* return? */ + sim_putchar ('\r'); /* crlf */ + sim_putchar ('\n'); + tto_col = 0; /* clear colcnt */ + return SCPE_OK; + } +if ((c == '\n') || (c == 007)) { /* non-spacing? */ + sim_putchar (c); + return SCPE_OK; + } +if (c == '\b') tto_col = tto_col? tto_col - 1: 0; /* backspace? */ +else tto_col++; /* normal */ +if (tto_col > TTO_COLMAX) { /* line wrap? */ + sim_putchar ('\r'); + sim_putchar ('\n'); + tto_col = 0; + } +sim_putchar (c); +return SCPE_OK; +} + +/* Unit service - polls for WRU */ + +t_stat tty_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tty_unit, tty_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tty_reset (DEVICE *dptr) +{ +sim_activate (&tty_unit, tty_unit.wait); /* activate poll */ +tto_col = 0; +return SCPE_OK; +} + +/* TTI unlock - signals that we are ready for keyboard input */ + +void tti_unlock (void) +{ +tto_write ('>'); +return; +} diff --git a/I7094/i7094_binloader.c b/I7094/i7094_binloader.c new file mode 100644 index 0000000..6089937 --- /dev/null +++ b/I7094/i7094_binloader.c @@ -0,0 +1,214 @@ +/* i7094_binloader.c: IBM 7094 simulator interface + + Copyright (c) 2006, David G. Pitts + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. +*/ +/*********************************************************************** +* +* binloader.h - IBM 7090 emulator binary loader header. +* +* Changes: +* 10/20/03 DGP Original. +* 12/28/04 DGP Changed for new object formats. +* +***********************************************************************/ + +#define IBSYSSYM '$' /* Marks end of object file */ +#define WORDPERREC 5 /* Object words per record */ +#define LOADADDR 0200 /* Default load address */ +#define OBJRECLEN 80 /* Object record length */ +#define CHARWORD 12 /* Characters per word */ + +/* +** Object tags +*/ + +#define IDT_TAG '0' /* 0SSSSSS0LLLLL */ +#define ABSENTRY_TAG '1' /* 10000000AAAAA */ +#define RELENTRY_TAG '2' /* 20000000RRRRR */ +#define ABSEXTRN_TAG '3' /* 3SSSSSS0AAAAA */ +#define RELEXTRN_TAG '4' /* 4SSSSSS0RRRRR */ +#define ABSGLOBAL_TAG '5' /* 5SSSSSS0AAAAA */ +#define RELGLOBAL_TAG '6' /* 6SSSSSS0RRRRR */ +#define ABSORG_TAG '7' /* 70000000AAAAA */ +#define RELORG_TAG '8' /* 80000000RRRRR */ +#define ABSDATA_TAG '9' /* 9AAAAAAAAAAAA */ +#define RELADDR_TAG 'A' /* AAAAAAAARRRRR */ +#define RELDECR_TAG 'B' /* BARRRRRAAAAAA */ +#define RELBOTH_TAG 'C' /* CARRRRRARRRRR */ +#define BSS_TAG 'D' /* D0000000PPPPP */ +#define ABSXFER_TAG 'E' /* E0000000RRRRR */ +#define RELXFER_TAG 'F' /* F0000000RRRRR */ +#define EVEN_TAG 'G' /* G0000000RRRRR */ +#define FAPCOMMON_TAG 'H' /* H0000000AAAAA */ + +/* Where: + * SSSSSS - Symbol + * LLLLLL - Length of module + * AAAAAA - Absolute field + * RRRRRR - Relocatable field + * PPPPPP - PC offset field +*/ + +/*********************************************************************** +* +* binloader.c - IBM 7090 emulator binary loader routines for ASM7090 +* and LNK7090 object files. +* +* Changes: +* 10/20/03 DGP Original. +* 12/28/04 DGP Changed for new object formats. +* 02/14/05 DGP Detect IBSYSSYM for EOF. +* 06/09/06 DGP Make simh callable. +* +***********************************************************************/ + +#include "i7094_defs.h" + +extern t_uint64 *M; +extern uint32 PC; + +t_stat +binloader (FILE *fd, char *file, int loadpt) +{ +#ifdef DEBUGLOADER + FILE *lfd; +#endif + int transfer = FALSE; + int loadaddr = LOADADDR; + int curraddr = LOADADDR; + char inbuf[OBJRECLEN+2]; + +#ifdef DEBUGLOADER + lfd = fopen ("load.log", "w"); + fprintf (lfd, "binloader: file = '%s', loadpt = %d\n", file, loadpt); +#endif + + if (loadpt > 0) + { + loadaddr = loadpt; + curraddr = loadpt; + } + + while (fgets (inbuf, sizeof(inbuf), fd)) + { + char *op = inbuf; + int i; + + if (*op == IBSYSSYM) /* End of object marker */ + break; + + for (i = 0; i < WORDPERREC; i++) + { + char otag; + char item[16]; + t_uint64 ldata; + + otag = *op++; + if (otag == ' ') break; + strncpy (item, op, CHARWORD); + item[CHARWORD] = '\0'; +#ifdef WIN32 + sscanf (item, "%I64o", &ldata); +#else + sscanf (item, "%llo", &ldata); +#endif + +#ifdef DEBUGLOADER + fprintf (lfd, "loadaddr = %05o, curraddr = %05o\n", + loadaddr, curraddr); + fprintf (lfd, " otag = %c, item = %s\n", otag, item); + fprintf (lfd, " ldata = %12.12o\n", ldata); +#endif + + switch (otag) + { + case IDT_TAG: + break; + + case ABSORG_TAG: + curraddr = loadaddr = (int32) ldata & AMASK; + break; + + case RELORG_TAG: + curraddr = (int32) (ldata + loadaddr) & AMASK; + break; + + case BSS_TAG: + curraddr = (int32) (curraddr + ldata) & AMASK; + break; + + case RELBOTH_TAG: + ldata = ldata + loadaddr + (loadaddr << INST_V_DEC); + goto STORE; + + case RELDECR_TAG: + ldata = ldata + (loadaddr << INST_V_DEC); + goto STORE; + + case RELADDR_TAG: + ldata = ldata + loadaddr; + + case ABSDATA_TAG: + STORE: +#ifdef DEBUGLOADER + fprintf (lfd, " M[%05o] = %12.12o\n", curraddr, ldata); +#endif + M[curraddr] = ldata & DMASK; + curraddr++; + break; + + case ABSXFER_TAG: + transfer = TRUE; + case ABSENTRY_TAG: + PC = (uint32) ldata & AMASK; +#ifdef DEBUGLOADER + fprintf (lfd, " PC = %05o\n", PC); +#endif + if (transfer) goto GOSTART; + break; + + case RELXFER_TAG: + transfer = TRUE; + case RELENTRY_TAG: + ldata = (ldata + loadaddr) & AMASK; + PC = (uint32) ldata & AMASK; +#ifdef DEBUGLOADER + fprintf (lfd, " PC = %05o\n", PC); +#endif + if (transfer) goto GOSTART; + break; + + default: ; + } + op += CHARWORD; + } + } + +GOSTART: +#ifdef DEBUGLOADER + fclose (lfd); +#endif + + return SCPE_OK; +} diff --git a/I7094/i7094_bug_history.txt b/I7094/i7094_bug_history.txt new file mode 100644 index 0000000..e8fb15b --- /dev/null +++ b/I7094/i7094_bug_history.txt @@ -0,0 +1,72 @@ +Bugs Found and Fixed During Simulator Debug + +1. CPU: MPY tested sign of AC instead of sign of MQ. +2. CPU: VLM, VDP, VDH need alternate opcode decode points for large counts. +3. CPU: STL not in decode table. +4. CPU: Partial stores lacked initial read in decode table. +5. CPU: PXD, PCD needed (t_uint64) cast. +6. CPU: SXD, SCD needed (t_uint64) cast. +7. CPU: SBM at wrong case offset. +8. CPU: All transfers used IR<21:35> instead of calculated effective address. +9. CPU: HPR, TRA need alternate negative opcodes. +10. CPU: STT missing final write to memory. +11. IO: Channel output process model incorrect, extensive revision required. +12. IO: Channel connect test should not test channel state, only connection. +13. IO: Channel opcode with nostore doesn't increment channel address. +14. CDR: Card reader missing activate at end of state 2. +15. SYS: Zero operand instructions printed with trailing space. +16. CPU: Convert class count bit field start definition incorrect. +17. CPU: CAQ cut and paste error from CVR. +18. CPU: Shift count is 8b wide, not 9b. +19. SYS: Mnemonic is LDQ not LMQ. +20. SYS: RQL opcode incorrect in symbolic decode table. +21. CPU: Multi-tag mode stores OR'd value of tags on any index read except + normal effective address. +22. CPU: Floating point trap does not write location 0 if trap suppressed. +23. SYS: TRA instructions should have symbolic class MXN not MXR. +24. CPU: Floating add instructions test for zero result only if normalization enabled. +25. CPU: Floating add with unlike signs and equal magnitudes, result sign is sign + of SR rather than sign of AC. +26. CPU: Floating multiply does not test spill prior to normalization step. +27. CPU: Channel activity proceeds under HALT. +28. MT: Any read error should stop the channel and the tape controller. +29. MT: EOR write flag cleared before testing. +39. IO: EOR write flag set after data sent to device. +40. IO: EOR write flag incorrect set on IOCP, IOCT, IOSP, IOST. +41. MT: End of file errors not masked on backspace operations. +42. CPU: LCHx, RCHx miscoded in decode table. +43. IO: Only 7607 error completions (IOxT without new command) set trap. +44. CPU: 7067 channel trap flags misdefined with extraneous decrement shift. +45. CPU: pcq array misdeclared as uint32 instead of uint16. +46. IO: SDC and SCD tested chan_flags instead of chan_dev.flags. +47. SYS: 7907 opcodes defined incorrectly. +48. SYS: TCH not decoding bit<19>. +49. IO: CTL not clearing EOR after device end. +50. DSK: Format code not incrementing track in writing all tracks in cylinder. +51. DSK: THA mode overwrites record structure of first record. +52. DSK: Write doesn't set channel request for initial word. +53. DSK: Saved record number was command digits 3..8 instead of 5..10. +54. CLK: Compute 60th's from location 5, so Chrono clock is in sync with interval clock. +55. CPU: Enable Chronolog clock if CTSS. +56. CPU: Read/write protection error not setting protection trap. +57. CPU: SCHx not setting protection trap in user mode. +58: CPU: Protection trap overwriting wrong location. +59. CPU: Stop message reporting physical, not virtual, PC. +60. IO: Test for "request another cycle" in channel processor was inverted. +61. IO: Valid bit handling incorrect across multiple transfers. +62. IO: BSR doesn't set EOF indicator. +63. IO: 7607 channel modeled incorrectly, could stall. +64. IO: All 7607 "effective NOP" conditions must be tested when a new command is + decoded (wc == 0 for IOCx and IOSx, EOR set for IOSx and IORx). + + + + + + + + + + + + diff --git a/I7094/i7094_cd.c b/I7094/i7094_cd.c new file mode 100644 index 0000000..4029c49 --- /dev/null +++ b/I7094/i7094_cd.c @@ -0,0 +1,499 @@ +/* i7094_cd.c: IBM 711/721 card reader/punch + + Copyright (c) 2003-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cdr 711 card reader + cdp 721 card punch + + 19-Jan-07 RMS Added UNIT_TEXT + 13-Jul-06 RMS Fixed problem with 80 column full cards + + Cards are represented as ASCII text streams terminated by newlines. + This allows cards to be created and edited as normal files. Two + formats are supported: + + column binary each character represents 6b of a 12b column + text each character represents all 12b of a column + + Internally, the 7094 works only with column binary and is limited + to 72 columns of data. Each row of the card is represented by 72b + of data (two 36b words). A complete card image consists of 12 rows + (24 36b words). +*/ + +#include "i7094_defs.h" + +#define CD_BINLNT 24 /* bin buf length */ +#define CD_CHRLNT 80 /* char buf length */ + +#define CDS_INIT 0 /* card in motion */ +#define CDS_DATA 1 /* data transfer */ +#define CDS_END 2 /* card complete */ + +#define UNIT_V_CBN (UNIT_V_UF + 0) /* column binary file */ +#define UNIT_V_PCA (UNIT_V_UF + 1) /* A vs H punch flag */ +#define UNIT_CBN (1 << UNIT_V_CBN) +#define UNIT_PCA (1 << UNIT_V_PCA) + +uint32 cdr_sta = 0; /* state */ +uint32 cdr_bptr = 0; /* buffer ptr */ +uint32 cdr_tstart = 27500; /* timing */ +uint32 cdr_tstop = 27500; +uint32 cdr_tleft = 150; +uint32 cdr_tright = 4000; +t_uint64 cdr_bbuf[CD_BINLNT]; /* col binary buf */ + +uint32 cdp_sta = 0; /* state */ +uint32 cdp_bptr = 0; /* buffer ptr */ +uint32 cdp_tstart = 35000; /* timing */ +uint32 cdp_tstop = 35000; +uint32 cdp_tleft = 150; +uint32 cdp_tright = 15500; +t_uint64 cdp_chob = 0; +uint32 cdp_chob_v = 0; +t_uint64 cdp_bbuf[CD_BINLNT]; /* col binary buf */ + +t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat cdr_reset (DEVICE *dptr); +t_stat cdr_svc (UNIT *uptr); +t_stat cdr_boot (int32 unitno, DEVICE *dptr); +t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_stat cdp_reset (DEVICE *dptr); +t_stat cdp_svc (UNIT *uptr); +t_stat cdp_card_end (UNIT *uptr); +t_stat cd_attach (UNIT *uptr, char *cptr); +t_stat cd_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +char colbin_to_bcd (uint32 cb); + +extern uint32 sim_switches; +extern uint32 PC; +extern uint32 ind_ioc; +extern char bcd_to_ascii_a[64]; +extern char bcd_to_ascii_h[64]; +extern uint32 bcd_to_colbin[64]; +extern char ascii_to_bcd[128]; +extern t_uint64 bit_masks[36]; +extern uint32 col_masks[12]; + +/* Card reader data structures + + cdr_dev CDR descriptor + cdr_unit CDR unit descriptor + cdr_reg CDR register list +*/ + +DIB cdr_dib = { &cdr_chsel, NULL }; + +UNIT cdr_unit = { + UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_TEXT, 0) + }; + +REG cdr_reg[] = { + { ORDATA (STATE, cdr_sta, 2) }, + { DRDATA (BPTR, cdr_bptr, 5), PV_LEFT }, + { BRDATA (BUF, cdr_bbuf, 8, 36, CD_BINLNT) }, + { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TSTART, cdr_tstart, 24), PV_LEFT + REG_NZ }, + { DRDATA (TSTOP, cdr_tstop, 24), PV_LEFT + REG_NZ }, + { DRDATA (TLEFT, cdr_tleft, 24), PV_LEFT + REG_NZ }, + { DRDATA (TRIGHT, cdr_tright, 24), PV_LEFT + REG_NZ }, + { NULL } }; + +MTAB cdr_mod[] = { + { UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode }, + { UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode }, + { 0 } + }; + +DEVICE cdr_dev = { + "CDR", &cdr_unit, cdr_reg, cdr_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cdr_reset, + &cdr_boot, &cd_attach, NULL, + &cdr_dib, DEV_DISABLE + }; + +/* CDP data structures + + cdp_dev CDP device descriptor + cdp_unit CDP unit descriptor + cdp_reg CDP register list +*/ + +DIB cdp_dib = { &cdp_chsel, &cdp_chwr }; + +UNIT cdp_unit = { + UDATA (&cdp_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) + }; + +REG cdp_reg[] = { + { ORDATA (STATE, cdp_sta, 2) }, + { ORDATA (CHOB, cdp_chob, 36) }, + { FLDATA (CHOBV, cdp_chob_v, 0) }, + { DRDATA (BPTR, cdp_bptr, 5), PV_LEFT }, + { BRDATA (BUF, cdp_bbuf, 8, 36, CD_BINLNT) }, + { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TSTART, cdp_tstart, 24), PV_LEFT + REG_NZ }, + { DRDATA (TSTOP, cdp_tstop, 24), PV_LEFT + REG_NZ }, + { DRDATA (TLEFT, cdp_tleft, 24), PV_LEFT + REG_NZ }, + { DRDATA (TRIGHT, cdp_tright, 24), PV_LEFT + REG_NZ }, + { NULL } + }; + +MTAB cdp_mod[] = { + { UNIT_CBN, UNIT_CBN, "column binary", "BINARY", &cd_set_mode }, + { UNIT_CBN, UNIT_CBN, "text", "TEXT", &cd_set_mode }, + { UNIT_PCA, UNIT_PCA, "business set", "BUSINESS", NULL }, + { UNIT_PCA, 0, "Fortran set", "FORTRAN", NULL }, + { 0 } + }; + +DEVICE cdp_dev = { + "CDP", &cdp_unit, cdp_reg, cdp_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cdp_reset, + NULL, &cd_attach, NULL, + &cdp_dib, DEV_DISABLE + }; + +/* Card reader select */ + +t_stat cdr_chsel (uint32 ch, uint32 sel, uint32 unit) +{ +if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */ + +switch (sel) { /* case on data sel */ + + case CHSL_RDS: /* read */ + if ((cdr_unit.flags & UNIT_ATT) == 0) /* not attached? */ + return SCPE_UNATT; + if (sim_is_active (&cdr_unit)) /* busy? */ + return ERR_STALL; + cdr_sta = CDS_INIT; /* initial state */ + sim_activate (&cdr_unit, cdp_tstart); /* start reader */ + break; + + default: /* other */ + return STOP_ILLIOP; /* not allowed */ + } + +return SCPE_OK; +} + +/* Unit timeout */ + +t_stat cdr_svc (UNIT *uptr) +{ +uint32 i, col, row, bufw, colbin; +char cdr_cbuf[(2 * CD_CHRLNT) + 2]; +t_uint64 dat = 0; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* not attached? */ +switch (cdr_sta) { /* case on state */ + + case CDS_INIT: /* initial state */ + for (i = 0; i < CD_BINLNT; i++) /* clear bin buf */ + cdr_bbuf[i] = 0; + for (i = 0; i < ((2 * CD_CHRLNT) + 2); i++) /* clear char buf */ + cdr_cbuf[i] = ' '; + cdr_sta = CDS_DATA; /* data state */ + cdr_bptr = 0; /* init buf ptr */ + fgets (cdr_cbuf, (uptr->flags & UNIT_CBN)? (2 * CD_CHRLNT) + 2: CD_CHRLNT + 2, + uptr->fileref); /* read card */ + if (feof (uptr->fileref)) /* eof? */ + return ch6_err_disc (CH_A, U_CDR, CHF_EOF); /* set EOF, disc */ + if (ferror (uptr->fileref)) { /* error? */ + perror ("CDR I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; /* stop */ + } + uptr->pos = ftell (uptr->fileref); /* update position */ + for (i = 0; i < (2 * CD_CHRLNT); i++) /* convert to BCD */ + cdr_cbuf[i] = ascii_to_bcd[cdr_cbuf[i] & 0177] & 077; + for (col = 0; col < 72; col++) { /* process 72 columns */ + if (uptr->flags & UNIT_CBN) /* column binary? */ + colbin = (((uint32) cdr_cbuf[2 * col]) << 6) | + ((uint32) cdr_cbuf[(2 * col) + 1]); /* 2 chars -> col bin */ + else colbin = bcd_to_colbin[cdr_cbuf[col]]; /* cvt to col binary */ + dat = bit_masks[35 - (col % 36)]; /* mask for column */ + for (row = 0; row < 12; row++) { /* rows 9..0, 11, 12 */ + bufw = (row * 2) + (col / 36); /* index to buffer */ + if (colbin & col_masks[row]) /* row bit set? */ + cdr_bbuf[bufw] |= dat; + } + } + + case CDS_DATA: /* data state */ + dat = cdr_bbuf[cdr_bptr++]; /* get next word */ + if (cdr_bptr >= CD_BINLNT) { /* last word? */ + cdr_sta = CDS_END; /* end state */ + ch6_req_rd (CH_A, U_CDR, dat, CH6DF_EOR); /* req chan, dat, EOR */ + sim_activate (uptr, cdr_tstop); + } + else { + ch6_req_rd (CH_A, U_CDR, dat, 0); /* req chan, dat */ + sim_activate (uptr, (cdr_bptr & 1)? cdr_tleft: cdr_tright); + } + break; + + case CDS_END: /* end state */ + if (ch6_qconn (CH_A, U_CDR)) { /* if cdr still conn */ + cdr_sta = CDS_INIT; /* return to init */ + sim_activate (uptr, 1); /* next card */ + } + break; + } + +return SCPE_OK; +} + +/* Card reader reset */ + +t_stat cdr_reset (DEVICE *dptr) +{ +uint32 i; + +for (i = 0; i < CD_BINLNT; i++) cdr_bbuf[i] = 0; /* clear buffer */ +cdr_sta = 0; /* clear state */ +cdr_bptr = 0; /* clear buf ptr */ +sim_cancel (&cdr_unit); /* stop reader */ +return SCPE_OK; +} + +/* Card reader bootstrap */ + +#define BOOT_START 01000 +#define BOOT_SIZE (sizeof (boot_rom) / sizeof (t_uint64)) + +static const t_uint64 boot_rom[] = { + 00762000001000 + U_CDR, /* RDSA CDR */ + 00544000000000 + BOOT_START + 4, /* LCHA *+3 */ + 00544000000000, /* LCHA 0 */ + 00021000000001, /* TTR 1 */ + 05000030000000, /* IOCT 3,,0 */ + }; + +t_stat cdr_boot (int32 unitno, DEVICE *dptr) +{ +uint32 i; +extern t_uint64 *M; + +for (i = 0; i < BOOT_SIZE; i++) + WriteP (BOOT_START + i, boot_rom[i]); +PC = BOOT_START; +return SCPE_OK; +} + +/* Reader/punch attach */ + +t_stat cd_attach (UNIT *uptr, char *cptr) +{ +t_stat r = attach_unit (uptr, cptr); + +if (r != SCPE_OK) return r; /* attach */ +if (sim_switches & SWMASK ('T')) /* text? */ + uptr->flags = uptr->flags & ~UNIT_CBN; +else if (sim_switches & SWMASK ('C')) /* column binary? */ + uptr->flags = uptr->flags | UNIT_CBN; +else if (match_ext (cptr, "TXT")) /* .txt? */ + uptr->flags = uptr->flags & ~UNIT_CBN; +else if (match_ext (cptr, "CBN")) /* .cbn? */ + uptr->flags = uptr->flags | UNIT_CBN; +return SCPE_OK; +} + +/* Reader/punch set mode - valid only if not attached */ + +t_stat cd_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +return (uptr->flags & UNIT_ATT)? SCPE_NOFNC: SCPE_OK; +} + +/* Card punch select */ + +t_stat cdp_chsel (uint32 ch, uint32 sel, uint32 unit) +{ +if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */ + +switch (sel) { /* case on cmd */ + + case CHSL_WRS: /* write */ + if ((cdp_unit.flags & UNIT_ATT) == 0) /* not attached? */ + return SCPE_UNATT; + if (sim_is_active (&cdp_unit)) /* busy? */ + return ERR_STALL; + cdp_sta = CDS_INIT; /* initial state */ + sim_activate (&cdp_unit, cdp_tstart); /* start punch */ + break; + + default: /* other */ + return STOP_ILLIOP; /* not allowed */ + } +return SCPE_OK; +} + +/* Channel write routine - write word to buffer, write card when full */ + +t_stat cdp_chwr (uint32 ch, t_uint64 val, uint32 eorfl) +{ +cdp_chob = val & DMASK; /* store data */ +cdp_chob_v = 1; /* buffer valid */ +if (cdp_sta == CDS_DATA) { + cdp_bbuf[cdp_bptr++] = cdp_chob; /* store data */ + if ((cdp_bptr >= CD_BINLNT) || eorfl) { /* end card or end rec? */ + ch6_set_flags (CH_A, U_CDP, CHF_EOR); /* set eor */ + return cdp_card_end (&cdp_unit); /* write card */ + } + return SCPE_OK; + } +return SCPE_IERR; +} + +/* Unit timeout */ + +t_stat cdp_svc (UNIT *uptr) +{ +uint32 i; + +switch (cdp_sta) { /* case on state */ + + case CDS_INIT: /* initial state */ + for (i = 0; i < CD_BINLNT; i++) /* clear bin buffer */ + cdp_bbuf[i] = 0; + cdp_sta = CDS_DATA; /* data state */ + cdp_bptr = 0; /* init pointer */ + ch6_req_wr (CH_A, U_CDP); /* request channel */ + cdp_chob = 0; /* clr, inval buffer */ + cdp_chob_v = 0; + sim_activate (uptr, cdp_tleft); /* go again */ + break; + + case CDS_DATA: /* data state */ + if (!ch6_qconn (CH_A, U_CDP)) /* chan disconnect? */ + return cdp_card_end (uptr); /* write card */ + if (cdp_chob_v) cdp_chob_v = 0; /* valid? clear */ + else ind_ioc = 1; /* no, io check */ + ch6_req_wr (CH_A, U_CDP); /* req channel */ + sim_activate (uptr, (cdp_bptr & 1)? cdp_tleft: cdp_tright); + break; + + case CDS_END: /* end state */ + if (ch6_qconn (CH_A, U_CDP)) { /* if cdp still conn */ + cdp_sta = CDS_INIT; /* return to init */ + sim_activate (uptr, 1); /* next card */ + } + break; + } + +return SCPE_OK; +} + +/* Card end - write card image to file, transition to end state */ + +t_stat cdp_card_end (UNIT *uptr) +{ +uint32 i, col, row, bufw, colbin; +char *pch, bcd, cdp_cbuf[(2 * CD_CHRLNT) + 2]; +t_uint64 dat; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; /* not attached? */ +if (uptr->flags & UNIT_PCA) pch = bcd_to_ascii_a; +else pch = bcd_to_ascii_h; +for (col = 0; col < ((2 * CD_CHRLNT) + 1); col++) + cdp_cbuf[col] = ' '; /* clear char buf */ +for (col = 0; col < 72; col++) { /* process 72 columns */ + colbin = 0; + dat = bit_masks[35 - (col % 36)]; /* mask for column */ + for (row = 0; row < 12; row++) { /* proc 12 rows */ + bufw = (row * 2) + (col / 36); /* index to buffer */ + if (cdp_bbuf[bufw] & dat) colbin |= col_masks[row]; + } + if (cdp_unit.flags & UNIT_CBN) { /* column binary? */ + cdp_cbuf[2 * col] = pch[(colbin >> 6) & 077]; + cdp_cbuf[(2 * col) + 1] = pch[colbin & 077]; + } + else { /* text */ + bcd = colbin_to_bcd (colbin); /* column bin -> BCD */ + cdp_cbuf[col] = pch[bcd]; /* -> ASCII */ + } + } +for (i = ((2 * CD_CHRLNT) + 1); (i > 0) && + (cdp_cbuf[i - 1] == ' '); --i) ; /* trim spaces */ +cdp_cbuf[i++] = '\n'; /* append nl */ +cdp_cbuf[i++] = 0; /* append nul */ +fputs (cdp_cbuf, uptr->fileref); /* write card */ +uptr->pos = ftell (uptr->fileref); /* update position */ +if (ferror (uptr->fileref)) { /* error? */ + perror ("CDP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +cdp_sta = CDS_END; /* end state */ +sim_cancel (uptr); /* cancel current */ +sim_activate (uptr, cdp_tstop); /* long timer */ +return SCPE_OK; +} + +/* Card punch reset */ + +t_stat cdp_reset (DEVICE *dptr) +{ +uint32 i; + +for (i = 0; i < 24; i++) cdp_bbuf[i] = 0; /* clear buffer */ +cdp_sta = 0; /* clear state */ +cdp_bptr = 0; /* clear buf ptr */ +cdp_chob = 0; +cdp_chob_v = 0; +sim_cancel (&cdp_unit); /* stop punch */ +return SCPE_OK; +} + +/* Column binary to BCD + + This is based on documentation in the IBM 1620 manual and may not be + accurate for the 7094. Each row (12,11,0,1..9) is interpreted as a bit + pattern, and the appropriate bits are set. (Double punches inclusive + OR, eg, 1,8,9 is 9.) On the 1620, double punch errors are detected; + since the 7094 only reads column binary, double punches are ignored. + + Bit order, left to right, is 12, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9. + The for loop works right to left, so the table is reversed. */ + +static const char row_val[12] = { + 011, 010, 007, 006, 005, 004, + 003, 002, 001, 020, 040, 060 + }; + +char colbin_to_bcd (uint32 cb) +{ +uint32 i; +char bcd; + +for (i = 0, bcd = 0; i < 12; i++) { /* 'sum' rows */ + if (cb & (1 << i)) bcd |= row_val[i]; + } +return bcd; +} diff --git a/I7094/i7094_clk.c b/I7094/i7094_clk.c new file mode 100644 index 0000000..f931d95 --- /dev/null +++ b/I7094/i7094_clk.c @@ -0,0 +1,124 @@ +/* i7094_clk.c: IBM 7094 clock + + Copyright (c) 2003-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + clk RPQ F89349 interval timer + Chronolog calendar clock +*/ + +#include "i7094_defs.h" +#include + +uint32 chtr_clk = 0; +extern t_uint64 *M; + +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +uint8 bcd_2d (uint32 n, uint8 *b2); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit + clk_reg CLK register list +*/ + +UNIT clk_unit = { UDATA (&clk_svc, 0, 0), 16000 }; + +REG clk_reg[] = { + { FLDATA (TRAP, chtr_clk, 0) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE+DEV_DIS + }; + +/* Clock unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +t_uint64 ctr; + +if ((clk_dev.flags & DEV_DIS) == 0) { /* clock enabled? */ + ctr = ReadP (CLK_CTR); + ctr = (ctr + 1) & DMASK; /* increment */ + WriteP (CLK_CTR, ctr); + if ((ctr & MMASK) == 0) chtr_clk = 1; /* overflow? req trap */ + sim_activate (uptr, sim_rtcn_calb (CLK_TPS, TMR_CLK)); /* reactivate unit */ + } +return SCPE_OK; +} + +/* Chronolog clock */ + +uint32 chrono_rd (uint8 *buf, uint32 bufsiz) +{ +time_t curtim; +t_uint64 ctr; +struct tm *tptr; + +if (bufsiz < 12) return 0; +curtim = time (NULL); /* get time */ +tptr = localtime (&curtim); /* decompose */ +if (tptr == NULL) return 0; /* error? */ + +buf[0] = bcd_2d (tptr->tm_mon + 1, buf + 1); +buf[2] = bcd_2d (tptr->tm_mday, buf + 3); +buf[4] = bcd_2d (tptr->tm_hour, buf + 5); +buf[6] = bcd_2d (tptr->tm_min, buf + 7); +buf[8] = bcd_2d (tptr->tm_sec, buf + 9); +ctr = ReadP (CLK_CTR); +buf[10] = bcd_2d ((uint32) (ctr % 60), buf + 11); +return 12; +} + +/* Convert number (0-99) to BCD */ + +uint8 bcd_2d (uint32 n, uint8 *b2) +{ +uint8 d1, d2; + +d1 = n / 10; +d2 = n % 10; +if (d1 == 0) d1 = BCD_ZERO; +if (d2 == 0) d2 = BCD_ZERO; +if (b2 != NULL) *b2 = d2; +return d1; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +chtr_clk = 0; +if (clk_dev.flags & DEV_DIS) sim_cancel (&clk_unit); +else sim_activate (&clk_unit, sim_rtcn_init (clk_unit.wait, TMR_CLK)); +return SCPE_OK; +} diff --git a/I7094/i7094_com.c b/I7094/i7094_com.c new file mode 100644 index 0000000..ea4e872 --- /dev/null +++ b/I7094/i7094_com.c @@ -0,0 +1,1149 @@ +/* i7094_com.c: IBM 7094 7750 communications interface simulator + + Copyright (c) 2005-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + com 7750 controller + coml 7750 lines + + This module implements an abstract simulator for the IBM 7750 communications + computer as used by the CTSS system. The 7750 supports up to 112 lines; + the simulator supports 33. The 7750 can handle both high-speed lines, in + 6b and 12b mode, and normal terminals, in 12b mode only; the simulator + supports only terminals. The 7750 can handle many different kinds of + terminals; the simulator supports only a limited subset. + + Input is asynchronous. The 7750 sets ATN1 to signal availability of input. + When the 7094 issues a CTLRN, the 7750 gathers available input characters + into a message. The message has a 12b sequence number, followed by 12b line + number/character pairs, followed by end-of-medium (03777). Input characters + can either be control characters (bit 02000 set) or data characters. Data + characters are 1's complemented and are 8b wide: 7 data bits and 1 parity + bit (which may be 0). + + Output is synchronous. When the 7094 issues a CTLWN, the 7750 interprets + the channel output as a message. The message has a 12b line number, followed + by a 12b character count, followed by characters, followed by end-of-medium. + If bit 02000 of the line number is set, the characters are 12b wide. If + bit 01000 is set, the message is a control message. 12b characters consist + of 7 data bits, 1 parity bit, and 1 start bit. Data characters are 1's + complemented. Data character 03777 is special and causes the 7750 to + repeat the previous bit for the number of bit times specified in the next + character. This is used to generate delays for positioning characters. + + The 7750 supports flow control for output. To help the 7094 account for + usage of 7750 buffer memory, the 7750 sends 'character output completion' + messages for every 'n' characters output on a line, where n <= 31. + + Note that the simulator console is mapped in as line n+1. +*/ + +#include "i7094_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define COM_MLINES 32 /* mux lines */ +#define COM_TLINES (COM_MLINES + 1) /* total lines */ +#define COM_BUFSIZ 120 /* max chan transfer */ +#define COM_PKTSIZ 16384 /* character buffer */ + +#define UNIT_V_2741 (TTUF_V_UF + 0) /* 2741 - ni */ +#define UNIT_V_K35 (TTUF_V_UF + 1) /* KSR-35 */ +#define UNIT_2741 (1 << UNIT_V_2741) +#define UNIT_K35 (1 << UNIT_V_K35) + +#define CONN u3 /* line is connected */ +#define NEEDID u4 /* need to send ID */ + +#define COM_INIT_POLL 8000 /* polling interval */ +#define COMC_WAIT 2 /* channel delay time */ +#define COML_WAIT 1000 /* char delay time */ +#define COM_LBASE 4 /* start of lines */ + +/* Input threads */ + +#define COM_PLU 0 /* multiplexor poll */ +#define COM_CIU 1 /* console input */ +#define COM_CHU 2 /* channel transfer */ +#define COM_SNS 3 /* sense transfer */ + +/* Communications input */ + +#define COMI_VALIDL 02000 /* valid line flag */ +#define COMI_PARITY 00200 /* parity bit */ +#define COMI_DIALUP 02001 /* dialup */ +#define COMI_ENDID 02002 /* end ID */ +#define COMI_INTR 02003 /* interrupt */ +#define COMI_QUIT 02004 /* quit */ +#define COMI_HANGUP 02005 /* hangup */ +#define COMI_EOM 03777 /* end of medium */ +#define COMI_COMP(x) ((uint16) (03000 + ((x) & COMI_CMAX))) +#define COMI_K35 1 /* KSR-35 ID */ +#define COMI_K37 7 /* KSR-37 ID */ +#define COMI_2741 8 /* 2741 ID */ +#define COMI_CMAX 31 /* max chars returned */ +#define COMI_BMAX 50 /* buffer max, words */ +#define COMI_12BMAX ((3 * COMI_BMAX) - 1) /* last 12b char */ + +/* Communications output */ + +#define COMO_LIN12B 0200000000000 /* line is 12b */ +#define COMO_LINCTL 0100000000000 /* control msg */ +#define COMO_GETLN(x) (((uint32) ((x) >> 24)) & 0777) +#define COMO_CTLRST 0000077770000 /* control reset */ +#define COMO_BITRPT 03777 /* bit repeat */ +#define COMO_EOM12B 07777 /* end of medium */ +#define COMO_BMAX 94 /* buffer max, words */ +#define COMO_12BMAX ((3 * COMO_BMAX) - 1) + +/* Status word (60b) */ + +#define COMS_PCHK 004000000000000000000 /* prog check */ +#define COMS_DCHK 002000000000000000000 /* data check */ +#define COMS_EXCC 001000000000000000000 /* exc cond */ +#define COMS_MLNT 000040000000000000000 /* message length check */ +#define COMS_CHNH 000020000000000000000 /* channel hold */ +#define COMS_CHNQ 000010000000000000000 /* channel queue full */ +#define COMS_ITMO 000000100000000000000 /* interface timeout */ +#define COMS_DATR 000000004000000000000 /* data message ready */ +#define COMS_INBF 000000002000000000000 /* input buffer free */ +#define COMS_SVCR 000000001000000000000 /* service message ready */ +#define COMS_PALL 000000000000000000000 +#define COMS_DALL 000000000000000000000 +#define COMS_EALL 000000000000000000000 +#define COMS_DYN 000000007000000000000 + +/* Report variables */ + +#define COMR_FQ 1 /* free queue */ +#define COMR_IQ 2 /* input queue */ +#define COMR_OQ 4 /* output queue */ + +/* List heads and entries */ + +typedef struct { + uint16 head; + uint16 tail; + } LISTHD; + +typedef struct { + uint16 next; + uint16 data; + } LISTENT; + +/* The 7750 character buffer is maintained as linked lists. The lists are: + + free free list + inpq input queue + outq[ln] output queue for line ln + + The input queue has two entries for each character; the first is the + line number, the second the character. The output queues have only + one entry for each character. + + Links are done as subscripts in array com_pkt. This allows the list + headers and the queues themselves to be saved and restored. */ + +uint32 com_ch = CH_E; /* saved channel */ +uint32 com_enab = 0; /* 7750 enabled */ +uint32 com_msgn = 0; /* next input msg num */ +uint32 com_sta = 0; /* 7750 state */ +uint32 com_stop = 0; /* channel stop */ +uint32 com_quit = 0; /* quit code */ +uint32 com_intr = 0; /* interrupt code */ +uint32 com_bptr = 0; /* buffer pointer */ +uint32 com_blim = 0; /* buffer count */ +uint32 com_tps = 50; /* polls/second */ +uint32 com_not_ret[COM_TLINES] = { 0 }; /* chars not returned */ +t_uint64 com_sns = 0; /* sense word */ +t_uint64 com_chob = 0; /* chan output buf */ +uint32 com_chob_v = 0; /* valid flag */ +t_uint64 com_buf[COM_BUFSIZ]; /* channel buffer */ +LISTHD com_free; /* free list */ +LISTHD com_inpq; /* input queue */ +LISTHD com_outq[COM_TLINES]; /* output queue */ +LISTENT com_pkt[COM_PKTSIZ]; /* character packets */ +TMLN com_ldsc[COM_MLINES] = { 0 }; /* line descriptors */ +TMXR com_desc = { COM_MLINES, 0, 0, com_ldsc }; /* mux descriptor */ + +/* Even parity truth table */ + +static const uint8 com_epar[128] = { + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1 + }; + +extern uint32 ch_req; + +t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat com_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_stat comi_svc (UNIT *uptr); +t_stat comc_svc (UNIT *uptr); +t_stat como_svc (UNIT *uptr); +t_stat coms_svc (UNIT *uptr); +t_stat comti_svc (UNIT *uptr); +t_stat comto_svc (UNIT *uptr); +t_stat com_reset (DEVICE *dptr); +t_stat com_attach (UNIT *uptr, char *cptr); +t_stat com_detach (UNIT *uptr); +t_stat com_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc); +void com_reset_ln (uint32 i); +uint16 com_gethd_free (LISTHD *lh); +uint16 com_gethd (LISTHD *lh); +t_bool com_new_puttl (LISTHD *lh, uint16 val); +void com_puttl (LISTHD *lh, uint16 ent); +t_bool com_inp_msg (uint32 ln, uint16 msg); +void com_skip_outc (uint32 ln); +t_stat com_test_atn (uint32 ch); +t_uint64 com_getob (uint32 ch); +t_bool com_qdone (uint32 ch); +void com_end (uint32 ch, uint32 fl, uint32 st); +t_stat com_send_id (uint32 ln); +t_stat com_send_ccmp (uint32 ln); +t_stat com_queue_in (uint32 ln, uint32 ch); +uint32 com_queue_out (uint32 ln, uint32 *c1); +void com_set_sns (t_uint64 stat); + +/* COM data structures + + com_dev COM device descriptor + com_unit COM unit descriptor + com_reg COM register list + com_mod COM modifiers list +*/ + +DIB com_dib = { &com_chsel, &com_chwr }; + +UNIT com_unit[] = { + { UDATA (&comi_svc, UNIT_ATTABLE, 0), COM_INIT_POLL }, + { UDATA (&comti_svc, UNIT_DIS, 0), KBD_POLL_WAIT }, + { UDATA (&comc_svc, UNIT_DIS, 0), COMC_WAIT }, + { UDATA (&coms_svc, UNIT_DIS, 0), COMC_WAIT } + }; + +REG com_reg[] = { + { FLDATA (ENABLE, com_enab, 0) }, + { ORDATA (STATE, com_sta, 6) }, + { ORDATA (MSGNUM, com_msgn, 12) }, + { ORDATA (SNS, com_sns, 60) }, + { ORDATA (CHOB, com_chob, 36) }, + { FLDATA (CHOBV, com_chob_v, 0) }, + { FLDATA (STOP, com_stop, 0) }, + { BRDATA (BUF, com_buf, 8, 36, COM_BUFSIZ) }, + { DRDATA (BPTR, com_bptr, 7), REG_RO }, + { DRDATA (BLIM, com_blim, 7), REG_RO }, + { BRDATA (NRET, com_not_ret, 10, 32, COM_TLINES), REG_RO + PV_LEFT }, + { BRDATA (FREEQ, &com_free, 10, 16, 2) }, + { BRDATA (INPQ, &com_inpq, 10, 16, 2) }, + { BRDATA (OUTQ, com_outq, 10, 16, 2 * COM_TLINES) }, + { BRDATA (PKTB, com_pkt, 10, 16, 2 * COM_PKTSIZ) }, + { DRDATA (TTIME, com_unit[COM_CIU].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (WTIME, com_unit[COM_CHU].wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (CHAN, com_ch, 3), REG_HRO }, + { NULL } + }; + +MTAB com_mod[] = { + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &com_summ }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &com_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &com_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_FQ, "FREEQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_IQ, "INQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, COMR_OQ, "OUTQ", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, -1, "ALL", NULL, + NULL, &com_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "OUTQ", NULL, + NULL, &com_show_outq, 0 }, + { 0 } + }; + +DEVICE com_dev = { + "COM", com_unit, com_reg, com_mod, + 3, 10, 31, 1, 16, 8, + &tmxr_ex, &tmxr_dep, &com_reset, + NULL, &com_attach, &com_detach, + &com_dib, DEV_NET | DEV_DIS + }; + +/* COMLL data structures + + coml_dev COML device descriptor + coml_unit COML unit descriptor + coml_reg COML register list + coml_mod COML modifiers list +*/ + +UNIT coml_unit[] = { + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&como_svc, 0, 0), COML_WAIT }, + { UDATA (&comto_svc, 0, 0), COML_WAIT }, + }; + +MTAB coml_mod[] = { + { UNIT_K35+UNIT_2741, 0 , "KSR-37", "KSR-37", NULL }, + { UNIT_K35+UNIT_2741, UNIT_K35 , "KSR-35", "KSR-35", NULL }, +// { UNIT_K35+UNIT_2741, UNIT_2741, "2741", "2741", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &com_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &com_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &com_desc }, + { 0 } + }; + +REG coml_reg[] = { + { URDATA (TIME, coml_unit[0].wait, 10, 24, 0, + COM_TLINES, REG_NZ + PV_LEFT) }, + { NULL } + }; + +DEVICE coml_dev = { + "COML", coml_unit, coml_reg, coml_mod, + COM_TLINES, 10, 31, 1, 16, 8, + NULL, NULL, &com_reset, + NULL, NULL, NULL, + NULL, DEV_DIS + }; + +/* COM: channel select */ + +t_stat com_chsel (uint32 ch, uint32 sel, uint32 unit) +{ +com_ch = ch; /* save channel */ +if (sim_is_active (&com_unit[COM_CHU]) || /* not idle? */ + sim_is_active (&com_unit[COM_SNS])) { + com_end (ch, CHINT_SEQC, 0); /* end, seq check */ + return SCPE_OK; + } + +switch (sel) { /* case on select */ + + case CHSL_RDS: /* read */ + case CHSL_WRS: /* write */ + com_sns = 0; /* clear status */ + sim_activate (&com_unit[COM_CHU], com_unit[COM_CHU].wait); + break; + + case CHSL_SNS: /* sense */ + sim_activate (&com_unit[COM_SNS], com_unit[COM_SNS].wait); + break; + + case CHSL_CTL: /* control */ + default: /* other */ + return STOP_ILLIOP; + } + +com_stop = 0; /* clear stop */ +com_sta = sel; /* set initial state */ +return SCPE_OK; +} + +/* Channel write, from 7909 channel program */ + +t_stat com_chwr (uint32 ch, t_uint64 val, uint32 stopf) +{ +if (stopf) com_stop = 1; +else { + com_chob = val; /* store data */ + com_chob_v = 1; /* set valid */ + } +return SCPE_OK; +} + +/* Unit service - SNS */ + +t_stat coms_svc (UNIT *uptr) +{ +t_uint64 dat; + +switch (com_sta) { /* case on state */ + + case CHSL_SNS: /* prepare data */ + com_sns &= ~COMS_DYN; /* clear dynamic flags */ + if (com_free.head) com_set_sns (COMS_INBF); /* free space? */ + if (com_inpq.head) com_set_sns (COMS_DATR); /* pending input? */ + com_buf[0] = (com_sns >> 24) & DMASK; /* buffer is 2 words */ + com_buf[1] = (com_sns << 12) & DMASK; + com_bptr = 0; + com_blim = 2; + com_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */ + break; + + case CHSL_SNS|CHSL_2ND: /* second state */ + if (com_bptr >= com_blim) { /* end of buffer? */ + ch9_set_end (com_ch, 0); /* set end */ + ch_req |= REQ_CH (com_ch); /* request channel */ + com_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */ + sim_activate (uptr, 10 * uptr->wait); /* longer wait */ + return SCPE_OK; + } + dat = com_buf[com_bptr++]; /* get word */ + if (!com_stop) ch9_req_rd (com_ch, dat); /* send wd to chan */ + break; + + case CHSL_SNS|CHSL_3RD: /* 3rd state */ + if (com_qdone (com_ch)) return SCPE_OK; /* done? exit */ + com_sta = CHSL_SNS; /* repeat sequence */ + break; + } + +sim_activate (uptr, uptr->wait); /* sched next */ +return SCPE_OK; +} + +/* Unit service - channel program */ + +t_stat comc_svc (UNIT *uptr) +{ +uint32 i, j, k, ccnt, ln, uln, ent; +uint16 chr; +t_uint64 dat; + +switch (com_sta) { /* case on state */ + + case CHSL_RDS: /* read start */ + for (i = 0; i < COM_BUFSIZ; i++) com_buf[i] = 0; /* clear chan buf */ + com_buf[0] = com_msgn; /* 1st char is msg num */ + com_msgn = (com_msgn + 1) & 03777; /* incr msg num */ + for (i = 1, j = 0; i < COMI_12BMAX; i++) { /* fill buffer */ + ent = com_gethd_free (&com_inpq); /* get next entry */ + if (ent == 0) break; /* q empty, done */ + if ((i % 3) == 0) j++; /* next word? */ + com_buf[j] = (com_buf[j] << 12) | /* pack data */ + ((t_uint64) (com_pkt[ent].data & 07777)); + } + for (k = i % 3; k < 3; k++) { /* fill with EOM */ + if (k == 0) j++; /* next word? */ + com_buf[j] = (com_buf[j] << 12) | COMI_EOM; + } + com_bptr = 0; /* init buf ptr */ + com_blim = j + 1; /* save buf size */ + com_sta = CHSL_RDS|CHSL_2ND; /* next state */ + break; + + case CHSL_RDS|CHSL_2ND: /* read xmit word */ + if (com_bptr >= com_blim) /* transfer done? */ + com_end (com_ch, 0, CHSL_RDS|CHSL_3RD); /* end, next state */ + else { /* more to do */ + dat = com_buf[com_bptr++]; /* get word */ + if (!com_stop) ch9_req_rd (com_ch, dat); /* give to channel */ + } + break; + + case CHSL_RDS|CHSL_3RD: /* read end */ + if (com_qdone (com_ch)) /* done? */ + return com_test_atn (com_ch); /* test atn, exit */ + com_sta = CHSL_RDS; /* repeat sequence */ + break; + + case CHSL_WRS: /* write start */ + for (i = 0; i < COM_BUFSIZ; i++) com_buf[i] = 0; /* clear chan buf */ + com_bptr = 0; /* init buf ptr */ + com_sta = CHSL_WRS|CHSL_2ND; /* next state */ + ch_req |= REQ_CH (com_ch); /* request channel */ + com_chob = 0; /* clr, inval buf */ + com_chob_v = 0; + break; + + case CHSL_WRS|CHSL_2ND: /* write first word */ + dat = com_getob (com_ch); /* get word? */ + if (dat == 0777777777777) { /* turn on? */ + com_enab = 1; /* enable 7750 */ + com_msgn = 0; /* init message # */ + com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ + } + else if (dat & COMO_LINCTL) { /* control message? */ + ln = COMO_GETLN (dat); /* line number */ + if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ + return STOP_INVLIN; + if (dat & COMO_CTLRST) return STOP_INVMSG; /* char must be 0 */ + if (ln >= COM_LBASE) com_reset_ln (ln - COM_LBASE); + com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ + } + else { /* data message */ + ccnt = (((uint32) dat >> 12) & 07777) + 1; /* char count plus EOM */ + if (dat & COMO_LIN12B) ccnt = ccnt << 1; /* 12b? double */ + com_blim = (ccnt + 6 + 5) / 6; /* buffer limit */ + if ((com_blim == 1) || (com_blim >= COMO_BMAX)) + return STOP_INVMSG; + com_buf[com_bptr++] = dat; /* store word */ + com_sta = CHSL_WRS|CHSL_3RD; /* next state */ + ch_req |= REQ_CH (com_ch); /* request channel */ + } + break; + + case CHSL_WRS|CHSL_3RD: /* other words */ + dat = com_getob (com_ch); /* get word */ + com_buf[com_bptr++] = dat; /* store word */ + if (com_bptr >= com_blim) { /* transfer done? */ + ln = COMO_GETLN (com_buf[0]); /* line number */ + if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */ + return STOP_INVLIN; + if ((com_buf[0] & COMO_LIN12B) && /* 12b message? */ + (ln >= COM_LBASE)) { + uln = ln - COM_LBASE; /* unit number */ + for (i = 2, j = 0; i < COMO_12BMAX; i++) { /* unpack 12b char */ + if ((i % 3) == 0) j++; + chr = (uint16) (com_buf[j] >> ((2 - (i % 3)) * 12)) & 07777; + if (chr == COMO_EOM12B) break; /* EOM? */ + if (!com_new_puttl (&com_outq[uln], chr)) + return STOP_NOOFREE; /* append to outq */ + } + sim_activate (&coml_unit[uln], coml_unit[uln].wait); + } + com_end (com_ch, 0, CHSL_WRS|CHSL_4TH); /* end, last state */ + } + else if (!com_stop) ch_req |= REQ_CH (com_ch); /* request channel */ + break; + + case CHSL_WRS|CHSL_4TH: /* buffer done */ + if (com_qdone (com_ch)) /* done? */ + return com_test_atn (com_ch); /* test atn, exit */ + com_sta = CHSL_WRS; /* repeat sequence */ + break; + + default: + return SCPE_IERR; + } + +sim_activate (uptr, uptr->wait); +return SCPE_OK; +} + +/* Unit service - console receive - always running, even if device is not */ + +t_stat comti_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +sim_activate (uptr, uptr->wait); /* continue poll */ +c = sim_poll_kbd (); /* get character */ +if (c && !(c & (SCPE_BREAK|SCPE_KFLAG))) return c; /* error? */ +if (!com_enab || (c & SCPE_BREAK)) return SCPE_OK; /* !enab, break? done */ +if (coml_unit[COM_MLINES].NEEDID) /* ID needed? */ + return com_send_id (COM_MLINES); +if ((c & SCPE_KFLAG) && ((c = c & 0177) != 0)) { /* char input? */ + if (r = com_queue_in (COM_MLINES, c)) return r; + if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) sim_putchar (c); + if (c == '\r') sim_putchar ('\n'); + } +return com_test_atn (com_ch); /* set ATN if input */ +} + +/* Unit service - receive side + + Poll all active lines for input + Poll for new connections */ + +t_stat comi_svc (UNIT *uptr) +{ +int32 c, ln, t; +t_stat r; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +t = sim_rtcn_calb (com_tps, TMR_COM); /* calibrate */ +sim_activate (uptr, t); /* continue poll */ +if (!com_enab) return SCPE_OK; /* not enabled? exit */ +ln = tmxr_poll_conn (&com_desc); /* look for connect */ +if (ln >= 0) { /* got one? */ + com_ldsc[ln].rcve = 1; /* rcv enabled */ + coml_unit[ln].CONN = 1; /* flag connected */ + coml_unit[ln].NEEDID = 1; /* need ID */ + } +tmxr_poll_rx (&com_desc); /* poll for input */ +for (ln = 0; ln < COM_MLINES; ln++) { /* loop thru mux */ + if (com_ldsc[ln].conn) { /* connected? */ + if (coml_unit[ln].NEEDID) return com_send_id (ln); + c = tmxr_getc_ln (&com_ldsc[ln]); /* get char */ + if (c) { /* any char? */ + c = c & 0177; /* mask to 7b */ + if (r = com_queue_in (ln, c)) return r; /* queue char, err? */ + if (com_ldsc[ln].xmte) { /* output enabled? */ + if (sim_tt_outcvt (c, TT_MODE_7P) >= 0) /* echo char */ + tmxr_putc_ln (&com_ldsc[ln], c); + if (c == '\r') /* add LF after CR */ + tmxr_putc_ln (&com_ldsc[ln], '\n'); + tmxr_poll_tx (&com_desc); /* poll xmt */ + } /* end if enabled */ + } /* end if char */ + } /* end if conn */ + else if (coml_unit[ln].CONN) { /* not conn, was conn? */ + coml_unit[ln].CONN = 0; /* clear connected */ + coml_unit[ln].NEEDID = 0; /* clear need id */ + if (!com_inp_msg (ln, COMI_HANGUP)) /* hangup message */ + return STOP_NOIFREE; + } + } /* end for */ +return com_test_atn (com_ch); /* set ATN if input */ +} + +/* Unit service - console transmit */ + +t_stat comto_svc (UNIT *uptr) +{ +uint32 c, c1; + +if (com_outq[COM_MLINES].head == 0) /* no more characters? */ + return com_send_ccmp (COM_MLINES); /* free any remaining */ +c = com_queue_out (COM_MLINES, &c1); /* get character, cvt */ +if (c) sim_putchar (c); /* printable? output */ +if (c1) sim_putchar (c1); /* second char? output */ +sim_activate (uptr, uptr->wait); /* next char */ +if (com_not_ret[COM_MLINES] >= COMI_CMAX) /* completion needed? */ + return com_send_ccmp (COM_MLINES); /* generate msg */ +return SCPE_OK; +} + +/* Unit service - transmit side */ + +t_stat como_svc (UNIT *uptr) +{ +uint32 c, c1; +int32 ln = uptr - coml_unit; /* line # */ + +if (com_outq[ln].head == 0) /* no more characters? */ + return com_send_ccmp (ln); /* free any remaining */ +if (com_ldsc[ln].conn) { /* connected? */ + if (com_ldsc[ln].xmte) { /* output enabled? */ + c = com_queue_out (ln, &c1); /* get character, cvt */ + if (c) tmxr_putc_ln (&com_ldsc[ln], c); /* printable? output */ + if (c1) tmxr_putc_ln (&com_ldsc[ln], c1); /* print second */ + } /* end if */ + tmxr_poll_tx (&com_desc); /* poll xmt */ + sim_activate (uptr, uptr->wait); /* next char */ + if (com_not_ret[ln] >= COMI_CMAX) /* completion needed? */ + return com_send_ccmp (ln); /* generate msg */ + } /* end if conn */ +return SCPE_OK; +} + +/* Send ID sequence on input */ + +t_stat com_send_id (uint32 ln) +{ +com_inp_msg (ln, COMI_DIALUP); /* input message: */ +if (coml_unit[ln].flags & UNIT_K35) /* dialup, ID, endID */ + com_inp_msg (ln, COMI_K35); +else com_inp_msg (ln, COMI_K37); +com_inp_msg (ln, 0); +com_inp_msg (ln, 0); +com_inp_msg (ln, 0); +com_inp_msg (ln, 0); +com_inp_msg (ln, (uint16) (ln + COM_LBASE)); +if (!com_inp_msg (ln, COMI_ENDID)) /* make sure there */ + return STOP_NOIFREE; /* was room for msg */ +coml_unit[ln].NEEDID = 0; +return SCPE_OK; +} + +/* Translate and queue input character */ + +t_stat com_queue_in (uint32 ln, uint32 c) +{ +uint16 out; + +if (c == com_intr) out = COMI_INTR; +else if (c == com_quit) out = COMI_QUIT; +else { + if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */ + if (islower (c)) c = toupper (c); /* convert LC to UC */ + } + else c |= (com_epar[c]? COMI_PARITY: 0); /* add even parity */ + out = (~c) & 0377; /* 1's complement */ + } +if (!com_inp_msg (ln, out)) return STOP_NOIFREE; /* input message */ +return SCPE_OK; +} + +/* Retrieve and translate output character */ + +uint32 com_queue_out (uint32 ln, uint32 *c1) +{ +uint32 c, ent, raw; + +*c1 = 0; /* assume non-printing */ +ent = com_gethd_free (&com_outq[ln]); /* get character */ +if (ent == 0) return 0; /* nothing? */ +raw = com_pkt[ent].data; /* get 12b character */ +com_not_ret[ln]++; +if (raw == COMO_BITRPT) { /* insert delay? */ + com_skip_outc (ln); + return 0; + } +c = (~raw >> 1) & 0177; /* remove start, parity */ +if (c >= 040) { /* printable? */ + if (c == 0177) return 0; /* DEL? ignore */ + if ((coml_unit[ln].flags & UNIT_K35) && islower (c)) /* KSR-35 LC? */ + c = toupper (c); /* cvt to UC */ + return c; + } +switch (c) { + + case '\t': case '\f': case '\b': case '\a': /* valid ctrls */ + return c; + + case '\r': /* carriage return? */ + if (coml_unit[ln].flags & UNIT_K35) /* KSR-35? */ + *c1 = '\n'; /* lf after cr */ + return c; + + case '\n': /* line feed? */ + if (!(coml_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ + *c1 = '\n'; /* lf after cr */ + return '\r'; + } + return c; /* print lf */ + + case 022: /* DC2 */ + if (!(com_unit[ln].flags & UNIT_K35)) { /* KSR-37? */ + com_skip_outc (ln); /* skip next */ + return '\n'; /* print lf */ + } + break; + + case 023: /* DC3 */ + if (!(com_unit[ln].flags & UNIT_K35)) /* KSR-37? */ + com_skip_outc (ln); /* skip next */ + break; + } + +return 0; /* ignore others */ +} + +/* Generate completion message, if needed */ + +t_stat com_send_ccmp (uint32 ln) +{ +uint32 t; + +if (t = com_not_ret[ln]) { /* chars not returned? */ + if (t > COMI_CMAX) t = COMI_CMAX; /* limit to max */ + com_not_ret[ln] -= t; /* keep count */ + if (!com_inp_msg (ln, COMI_COMP (t))) /* gen completion msg */ + return STOP_NOIFREE; + } +return SCPE_OK; +} + +/* Skip next char in output queue */ + +void com_skip_outc (uint32 ln) +{ +if (com_gethd_free (&com_outq[ln])) com_not_ret[ln]++; /* count it */ +return; +} + +/* Read and validate output buffer */ + +t_uint64 com_getob (uint32 ch) +{ +if (com_chob_v) com_chob_v = 0; /* valid? clear */ +else if (!com_stop) { /* not stopped? */ + ch9_set_ioc (com_ch); /* IO check */ + com_set_sns (COMS_ITMO); /* set sense bit */ + } +return com_chob; +} + +/* Set attention if input pending */ + +t_stat com_test_atn (uint32 ch) +{ +if (com_inpq.head) ch9_set_atn (ch); +return SCPE_OK; +} + +/* Test for done */ + +t_bool com_qdone (uint32 ch) +{ +if (com_stop || !ch9_qconn (ch)) { /* stop or err disc? */ + com_sta = 0; /* ctrl is idle */ + return TRUE; + } +return FALSE; +} + +/* Channel end */ + +void com_end (uint32 ch, uint32 fl, uint32 st) +{ +ch9_set_end (ch, fl); /* set end */ +ch_req |= REQ_CH (ch); +com_sta = st; /* next state */ +return; +} + +/* List routines - remove from head and free */ + +uint16 com_gethd_free (LISTHD *lh) +{ +uint16 ent; + +if ((ent = com_gethd (lh)) != 0) + com_puttl (&com_free, ent); +return ent; +} + +/* Get free entry and insert at tail */ + +t_bool com_new_puttl (LISTHD *lh, uint16 val) +{ +uint16 ent; + +if ((ent = com_gethd (&com_free)) != 0) { + com_pkt[ent].data = val; + com_puttl (lh, ent); + return TRUE; + } +return FALSE; +} + +/* Remove from head */ + +uint16 com_gethd (LISTHD *lh) +{ +uint16 ent; + +if ((ent = lh->head) != 0) { + lh->head = com_pkt[ent].next; + if (lh->head == 0) lh->tail = 0; + } +else lh->tail = 0; +return ent; +} + +/* Insert at tail */ + +void com_puttl (LISTHD *lh, uint16 ent) +{ +if (lh->tail == 0) lh->head = ent; +else com_pkt[lh->tail].next = ent; +com_pkt[ent].next = 0; +lh->tail = ent; +return; +} + +/* Insert line and message into input queue */ + +t_bool com_inp_msg (uint32 ln, uint16 msg) +{ +uint16 ent1, ent2; + +if ((ent1 = com_gethd (&com_free)) != 0) { /* pkt avail? */ + if ((ent2 = com_gethd (&com_free)) != 0) { /* 2nd pkt avail? */ + com_pkt[ent1].data = (ln + COM_LBASE) | COMI_VALIDL; /* 1st pkt = line# */ + com_pkt[ent2].data = msg; /* 2nd pkt = char */ + com_puttl (&com_inpq, ent1); /* queue pkts */ + com_puttl (&com_inpq, ent2); + return TRUE; + } + com_puttl (&com_free, ent1); /* free 1st */ + } +return FALSE; /* failed */ +} + +/* Set flag in sense */ + +void com_set_sns (t_uint64 stat) +{ +com_sns |= stat; +com_sns &= ~(COMS_PCHK|COMS_DCHK|COMS_EXCC); +if (com_sns & COMS_PALL) com_sns |= COMS_PCHK; +if (com_sns & COMS_DALL) com_sns |= COMS_DCHK; +if (com_sns & COMS_EALL) com_sns |= COMS_EXCC; +return; +} + +/* Reset routine */ + +t_stat com_reset (DEVICE *dptr) +{ +uint32 i; + +if (dptr->flags & DEV_DIS) { /* disabled? */ + com_dev.flags = com_dev.flags | DEV_DIS; /* disable lines */ + coml_dev.flags = coml_dev.flags | DEV_DIS; + } +else { + com_dev.flags = com_dev.flags & ~DEV_DIS; /* enable lines */ + coml_dev.flags = coml_dev.flags & ~DEV_DIS; + } +sim_activate (&com_unit[COM_CIU], com_unit[COM_CIU].wait); /* console */ +sim_cancel (&com_unit[COM_PLU]); +sim_cancel (&com_unit[COM_CHU]); +if (com_unit[COM_PLU].flags & UNIT_ATT) { /* master att? */ + int32 t = sim_rtcn_init (com_unit[COM_PLU].wait, TMR_COM); + sim_activate (&com_unit[COM_PLU], t); + } +com_enab = 0; +com_sns = 0; +com_msgn = 0; +com_sta = 0; +com_chob = 0; +com_chob_v = 0; +com_stop = 0; +com_bptr = 0; +com_blim = 0; +for (i = 0; i < COM_BUFSIZ; i++) com_buf[i] = 0; +com_inpq.head = 0; /* init queues */ +com_inpq.tail = 0; +for (i = 0; i < COM_TLINES; i++) { + com_outq[i].head = 0; + com_outq[i].tail = 0; + com_reset_ln (i); + } +com_pkt[0].next = 0; /* init free list */ +for (i = 1; i < COM_PKTSIZ; i++) { + com_pkt[i].next = i + 1; + com_pkt[i].data = 0; + } +com_pkt[COM_PKTSIZ - 1].next = 0; /* end of free list */ +com_free.head = 1; +com_free.tail = COM_PKTSIZ - 1; +coml_unit[COM_MLINES].CONN = 1; /* console always conn */ +coml_unit[COM_MLINES].NEEDID = 1; +return SCPE_OK; +} + +/* Attach master unit */ + +t_stat com_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&com_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +sim_rtcn_init (uptr->wait, TMR_COM); +sim_activate (uptr, 100); /* quick poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat com_detach (UNIT *uptr) +{ +uint32 i; +t_stat r; + +r = tmxr_detach (&com_desc, uptr); /* detach */ +for (i = 0; i < COM_MLINES; i++) com_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat com_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i, t; + +for (i = t = 0; i < COM_MLINES; i++) t = t + (com_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat com_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i, t; + +for (i = t = 0; i < COM_MLINES; i++) t = t + (com_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < COM_MLINES; i++) { + if (com_ldsc[i].conn) { + if (val) tmxr_fconns (st, &com_ldsc[i], i); + else tmxr_fstats (st, &com_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Reset an individual line */ + +void com_reset_ln (uint32 ln) +{ +while (com_gethd_free (&com_outq[ln])) ; +com_not_ret[ln] = 0; +sim_cancel (&coml_unit[ln]); +if ((ln < COM_MLINES) && (com_ldsc[ln].conn == 0)) coml_unit[ln].CONN = 0; +return; +} + +/* Special show commands */ + +uint32 com_show_qsumm (FILE *st, LISTHD *lh, char *name) +{ +uint32 i, next; + +next = lh->head; +for (i = 0; i < COM_PKTSIZ; i++) { + if (next == 0) { + if (i == 0) fprintf (st, "%s is empty\n", name); + else if (i == 1) fprintf (st, "%s has 1 entry\n", name); + else fprintf (st, "%s had %d entries\n", name, i); + return i; + } + next = com_pkt[next].next; + } +fprintf (st, "%s is corrupt\n", name); +return 0; +} + +void com_show_char (FILE *st, uint32 ch) +{ +uint32 c; + +fprintf (st, "%03o", ch); +c = (~ch) & 0177; +if (((ch & 07400) == 0) && (c >= 040) && (c != 0177)) fprintf (st, "[%c]", c); +return; +} + +t_stat com_show_freeq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +com_show_qsumm (st, &com_free, "Free queue"); +return SCPE_OK; +} + +t_stat com_show_inq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 entc, ln, i, next; + +if (entc = com_show_qsumm (st, &com_inpq, "Input queue")) { + for (i = 0, next = com_inpq.head; next != 0; + i++, next = com_pkt[next].next) { + if ((i % 4) == 0) fprintf (st, "%d:\t", i); + ln = com_pkt[next].data; + next = com_pkt[next].next; + if (next == 0) { + fprintf (st, "Line number without data\n"); + return SCPE_OK; + } + fprintf (st, "%d/", ln); + com_show_char (st, com_pkt[next].data); + fputc ((((i % 4) == 3)? '\n': '\t'), st); + } + if (i % 4) fputc ('\n', st); + } +return SCPE_OK; +} + +t_stat com_show_outq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 entc, ln, i, next; +char name[20]; + +ln = uptr - com_dev.units; +sprintf (name, "Output queue %d", ln); +if (entc = com_show_qsumm (st, &com_outq[ln], name)) { + for (i = 0, next = com_outq[ln].head; next != 0; + i++, next = com_pkt[next].next) { + if ((i % 8) == 0) fprintf (st, "%d:\t", i); + com_show_char (st, com_pkt[next].data >> 1); + fputc ((((i % 8) == 7)? '\n': '\t'), st); + } + if (i % 8) fputc ('\n', st); + } +return SCPE_OK; +} + +t_stat com_show_aoutq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i; + +for (i = 0; i < COM_TLINES; i++) + com_show_outq (st, com_dev.units + i, 1, desc); +return SCPE_OK; +} + +t_stat com_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (!com_enab) fprintf (st, "Controller is not initialized\n"); +if (val & COMR_FQ) com_show_freeq (st, uptr, 1, desc); +if (val & COMR_IQ) com_show_inq (st, uptr, 1, desc); +if (val & COMR_OQ) com_show_aoutq (st, uptr, 1, desc); +return SCPE_OK; +} diff --git a/I7094/i7094_cpu.c b/I7094/i7094_cpu.c new file mode 100644 index 0000000..e5a4987 --- /dev/null +++ b/I7094/i7094_cpu.c @@ -0,0 +1,2253 @@ +/* i7094_cpu.c: IBM 7094 CPU simulator + + Copyright (c) 2003-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu 7094 central processor + + 28-Apr-07 RMS Removed clock initialization + 29-Oct-06 RMS Added additional expanded core instructions + 17-Oct-06 RMS Fixed the fix in halt IO wait loop + 16-Jun-06 RMS Fixed bug in halt IO wait loop + + The register state for the 7094 is: + + AC accumulator + MQ multiplier-quotient register + SI storage indicators + KEYS<0:35> front panel keys (switches) + IC<0:14> instruction counter (called PC here) + XR<0:14>[8] index registers (XR[0] is always 0) + SSW<0:5> sense switches + SLT<0:3> sense lights + OVF AC overflow + MQO MQ overflow + DVC divide check + IOC I/O check + TTRAP transfer trap mode + CTRAP copy trap mode (for 709 compatibility) + FTRAP floating trap mode (off is 704 compatibility) + STRAP select trap mode + STORN storage nullifcation mode + MULTI multi-tag mode (7090 compatibility) + + CTSS required a set of special features: memory extension (to 65K), + protection, and relocation. Additional state: + + USER user mode + INST_BASE instruction memory select (A vs B core) + DATA_BASE data memory select (A vs B core) + IND_RELOC<0:6> relocation value (block number) + IND_START<0:6> start address block + IND_LIMIT<0:6> limit address block + + The 7094 had five instruction formats: memory reference, + memory reference with count, convert, decrement, and immediate. + + 00000000011 11 1111 112 222222222333333 + S12345678901 23 4567 890 123456789012345 + +------------+--+----+---+---------------+ + | opcode |ND|0000|tag| address | memory reference + +------------+--+----+---+---------------+ + + 00000000011 111111 112 222222222333333 + S12345678901 234567 890 123456789012345 + +------------+------+---+---------------+ + | opcode | count|tag| address | memory reference + +------------+------+---+---------------+ with count + + 000000000 11111111 11 2 222222222333333 + S123456789 01234567 89 0 123456789012345 + +----------+--------+--+-+---------------+ + | opcode | count |00|X| address | convert + +----------+--------+--+-+---------------+ + + 00 000000011111111 112 222222222333333 + S12 345678901234567 890 123456789012345 + +---+---------------+---+---------------+ + |opc| decrement |tag| address | decrement + +---+---------------+---+---------------+ + + 00000000011 111111 112222222222333333 + S12345678901 234567 890123456789012345 + +------------+------+------------------+ + | opcode |000000| immediate | immediate + +------------+------+------------------+ + + This routine is the instruction decode routine for the 7094. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until a stop condition occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + illegal instruction + illegal I/O operation for device + illegal I/O operation for channel + breakpoint encountered + nested XEC's exceeding limit + divide check + I/O error in I/O simulator + + 2. Data channel traps. The 7094 is a channel-based system. + Channels can generate traps for errors and status conditions. + Channel trap state: + + ch_flags[0..7] flags for channels A..H + chtr_enab channel trap enables + chtr_inht channel trap inhibit due to trap (cleared by RCT) + chtr_inhi channel trap inhibit due to XEC, ENAB, RCT, RDS, + or WDS (cleared after one instruction) + + Channel traps are summarized in variable chtr_pend. + + 3. Arithmetic. The 7094 uses signed magnitude arithmetic for + integer and floating point calculations, and 2's complement + arithmetic for indexing calculations. + + 4. Adding I/O devices. These modules must be modified: + + i7094_defs.h add device definitions + i7094_io.c add device address mapping + i7094_sys.c add sim_devices table entry +*/ + +#include "i7094_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = (PC | inst_base) + +#define HIST_MIN 64 +#define HIST_MAX (2 << 18) +#define HIST_CH_C 1 /* include channel */ +#define HIST_CH_I 2 /* include IO */ + +#define HALT_IO_LIMIT ((2 << 18) + 1) /* max wait to stop */ + +t_uint64 *M = NULL; /* memory */ +t_uint64 AC = 0; /* AC */ +t_uint64 MQ = 0; /* MQ */ +t_uint64 SI = 0; /* indicators */ +t_uint64 KEYS = 0; /* storage keys */ +uint32 PC = 0; /* PC (IC) */ +uint32 oldPC = 0; /* prior PC */ +uint32 XR[8] = { 0 }; /* index registers */ +uint32 SSW = 0; /* sense switches */ +uint32 SLT = 0; /* sense lights */ +uint32 ch_req = 0; /* channel requests */ +uint32 chtr_pend = 0; /* chan trap pending */ +uint32 chtr_inht = 0; /* chan trap inhibit trap */ +uint32 chtr_inhi = 0; /* chan trap inhibit inst */ +uint32 chtr_enab = 0; /* chan trap enables */ +uint32 mode_ttrap = 0; /* transfer trap mode */ +uint32 mode_ctrap = 0; /* copy trap mode */ +uint32 mode_strap = 0; /* select trap mode */ +uint32 mode_ftrap = 0; /* floating trap mode */ +uint32 mode_storn = 0; /* storage nullification */ +uint32 mode_multi = 0; /* multi-index mode */ +uint32 ind_ovf = 0; /* overflow */ +uint32 ind_mqo = 0; /* MQ overflow */ +uint32 ind_dvc = 0; /* divide check */ +uint32 ind_ioc = 0; /* IO check */ +uint32 cpu_model = I_9X|I_94; /* CPU type */ +uint32 mode_user = 0; /* (CTSS) user mode */ +uint32 ind_reloc = 0; /* (CTSS) relocation */ +uint32 ind_start = 0; /* (CTSS) prot start */ +uint32 ind_limit = 0; /* (CTSS) prot limit */ +uint32 inst_base = 0; /* (CTSS) inst A/B sel */ +uint32 data_base = 0; /* (CTSS) data A/B sel */ +uint32 xec_max = 16; /* XEC chain limit */ +uint32 ht_pend = 0; /* HTR pending */ +uint32 ht_addr = 0; /* HTR address */ +uint32 stop_illop = 1; /* stop on ill op */ +uint32 cpu_astop = 0; /* address stop */ +static uint32 eamask = AMASK; /* (dynamic) addr mask */ + +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +uint32 hst_ch = 0; /* channel history */ +InstHistory *hst = NULL; /* instruction history */ + +extern uint32 ch_sta[NUM_CHAN]; +extern uint32 ch_flags[NUM_CHAN]; +extern DEVICE mt_dev[NUM_CHAN]; +extern DEVICE ch_dev[NUM_CHAN]; +extern FILE *sim_deb; +extern int32 sim_int_char; +extern int32 sim_interval; +extern int32 sim_switches; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +/* Forward and external declarations */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_bool ReadI (uint32 va, t_uint64 *dat); +t_bool Read (uint32 va, t_uint64 *dat); +t_bool Write (uint32 va, t_uint64 dat); +void WriteTA (uint32 pa, uint32 addr); +void WriteTAD (uint32 pa, uint32 addr, uint32 decr); +void TrapXfr (uint32 newpc); +t_bool fp_trap (uint32 spill); +t_bool prot_trap (uint32 decr); +t_bool sel_trap (uint32 va); +t_bool cpy_trap (uint32 va); +uint32 get_xri (uint32 tag); +uint32 get_xrx (uint32 tag); +void put_xr (uint32 tag, uint32 dat); +t_stat cpu_fprint_one_inst (FILE *st, uint32 pc, uint32 rpt, uint32 ea, + t_uint64 ir, t_uint64 ac, t_uint64 mq, t_uint64 si, t_uint64 opnd); + +extern uint32 chtr_eval (uint32 *decr); +extern void op_add (t_uint64 sr); +extern void op_mpy (t_uint64 ac, t_uint64 sr, uint32 sc); +extern t_bool op_div (t_uint64 sr, uint32 sc); +extern uint32 op_fad (t_uint64 sr, t_bool norm); +extern uint32 op_fmp (t_uint64 sr, t_bool norm); +extern uint32 op_fdv (t_uint64); +extern uint32 op_dfad (t_uint64 shi, t_uint64 slo, t_bool norm); +extern uint32 op_dfmp (t_uint64 shi, t_uint64 slo, t_bool norm); +extern uint32 op_dfdv (t_uint64 shi, t_uint64 slo); +extern void op_als (uint32 ea); +extern void op_ars (uint32 ea); +extern void op_lls (uint32 ea); +extern void op_lrs (uint32 ea); +extern void op_lgl (uint32 ea); +extern void op_lgr (uint32 ea); +extern t_stat op_pse (uint32 ea); +extern t_stat op_mse (uint32 ea); +extern t_stat ch_op_ds (uint32 ch, uint32 ds, uint32 unit); +extern t_stat ch_op_nds (uint32 ch, uint32 ds, uint32 unit); +extern t_stat ch_op_start (uint32 ch, uint32 clc, t_bool reset); +extern t_stat ch_op_store (uint32 ch, t_uint64 *dat); +extern t_stat ch_op_store_diag (uint32 ch, t_uint64 *dat); +extern t_stat ch_proc (uint32 ch); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK, STDMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, PC, ASIZE) }, + { ORDATA (AC, AC, 38) }, + { ORDATA (MQ, MQ, 36) }, + { ORDATA (SI, SI, 36) }, + { ORDATA (KEYS, KEYS, 36) }, + { ORDATA (XR1, XR[1], 15) }, + { ORDATA (XR2, XR[2], 15) }, + { ORDATA (XR3, XR[3], 15) }, + { ORDATA (XR4, XR[4], 15) }, + { ORDATA (XR5, XR[5], 15) }, + { ORDATA (XR6, XR[6], 15) }, + { ORDATA (XR7, XR[7], 15) }, + { FLDATA (SS1, SSW, 5) }, + { FLDATA (SS2, SSW, 4) }, + { FLDATA (SS3, SSW, 3) }, + { FLDATA (SS4, SSW, 2) }, + { FLDATA (SS5, SSW, 1) }, + { FLDATA (SS6, SSW, 0) }, + { FLDATA (SL1, SLT, 3) }, + { FLDATA (SL2, SLT, 2) }, + { FLDATA (SL3, SLT, 1) }, + { FLDATA (SL4, SLT, 0) }, + { FLDATA (OVF, ind_ovf, 0) }, + { FLDATA (MQO, ind_mqo, 0) }, + { FLDATA (DVC, ind_dvc, 0) }, + { FLDATA (IOC, ind_ioc, 0) }, + { FLDATA (TTRAP, mode_ttrap, 0) }, + { FLDATA (CTRAP, mode_ctrap, 0) }, + { FLDATA (STRAP, mode_strap, 0) }, + { FLDATA (FTRAP, mode_ftrap, 0) }, + { FLDATA (STORN, mode_storn, 0) }, + { FLDATA (MULTI, mode_multi, 0) }, + { ORDATA (CHREQ, ch_req, NUM_CHAN) }, + { FLDATA (CHTR_PEND, chtr_pend, 0) }, + { FLDATA (CHTR_INHT, chtr_inht, 0) }, + { FLDATA (CHTR_INHI, chtr_inhi, 0) }, + { ORDATA (CHTR_ENAB, chtr_enab, 30) }, + { FLDATA (USERM, mode_user, 0) }, + { FLDATA (IMEM, inst_base, BCORE_V) }, + { FLDATA (DMEM, data_base, BCORE_V) }, + { GRDATA (RELOC, ind_reloc, 8, VA_N_BLK, VA_V_BLK) }, + { GRDATA (START, ind_start, 8, VA_N_BLK, VA_V_BLK) }, + { GRDATA (LIMIT, ind_limit, 8, VA_N_BLK, VA_V_BLK) }, + { ORDATA (OLDPC, oldPC, ASIZE), REG_RO }, + { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { FLDATA (HTPEND, ht_pend, 0) }, + { ORDATA (HTADDR, ht_addr, ASIZE) }, + { DRDATA (XECMAX, xec_max, 8), PV_LEFT + REG_NZ }, + { ORDATA (WRU, sim_int_char, 8) }, + { FLDATA (STOP_ILL, stop_illop, 0) }, + { ORDATA (MODEL, cpu_model, 4), REG_HRO }, + { NULL } + }; + +MTAB cpu_mod[] = { + { MTAB_XTD | MTAB_VDV, I_9X|I_94|I_CT, "MODEL", "CTSS", + &cpu_set_model, &cpu_show_model, NULL }, + { MTAB_XTD | MTAB_VDV, I_9X|I_94, NULL, "7094", + &cpu_set_model, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, I_9X, NULL, "7090", + &cpu_set_model, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, PASIZE, 1, 8, 36, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, DEV_DEBUG + }; + +/* Instruction decode table */ + +const uint8 op_flags[1024] = { + I_XN , 0 , 0 , 0 , /* +000 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_9X , I_XN , 0 , /* +020 */ + I_XN , 0 , I_XN , I_XN , + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + I_XN|I_9X , I_9X , I_XN|I_9X , I_9X , /* +040 */ + I_9X , 0 , I_XN|I_9X , 0 , + 0 , I_9X , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN , I_XN , I_XN , I_XN , /* +060 */ + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_CT , 0 , 0 , /* +100 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN , 0 , 0 , 0 , /* +120 */ + 0 , 0 , 0 , 0 , + 0 , I_9X , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , 0 , 0 , 0 , /* +140 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , I_XN|I_9X , I_XN|I_9X , 0 , /* +160 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* +200 */ + I_XNR , I_XNR , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, I_XNR , 0 , 0 , /* +220 */ + I_XNR|I_9X, I_XNR , I_XNR|I_9X, I_XNR , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, I_XNR , 0 , 0 , /* +240 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, 0 , 0 , /* +260 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, I_XNR , I_XND|I_94, /* +300 */ + I_XNR|I_9X, I_XND|I_94, I_XNR|I_9X, I_XND|I_94, + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , I_XNR|I_9X, 0 , /* +320 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* +340 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , I_XNR , 0 , 0 , /* +360 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XNR|I_9X, I_XNR , 0 , /* +400 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +420 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_94, /* +440 */ + I_XNR|I_9X, I_XNR|I_9X, I_XNR|I_9X, 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , /* +460 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , I_XNR , 0 , /* +500 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , I_XNR , 0 , /* +520 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_R , I_R , 0 , 0 , + I_XN , I_XN , I_XN , I_XN , /* +540 */ + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , I_XNR|I_CT, 0 , /* +560 */ + I_XNR , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN , I_XN , 0 , /* +600 */ + I_XN|I_9X , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , I_XNR , I_XNR , 0 , /* +620 */ + 0 , I_XNR|I_9X, 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , + I_R , 0 , I_R|I_94 , 0 , + I_XN , I_XN , I_XN , I_XN , /* +640 */ + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +660 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , /* +700 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +720 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* +740 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , I_94 , 0 , + I_X , 0 , I_X , I_X , /* +760 */ + I_X , I_X , I_X , I_X , + I_X , I_X , I_X , 0 , + 0 , 0 , 0 , 0 , + + I_XN , 0 , 0 , 0 , /* -000 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_9X , I_XN , 0 , /* -020 */ + I_XN , 0 , I_XN , I_XN , + I_XN , I_XN , I_XN , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -040 */ + 0 , 0 , I_9X , 0 , + 0 , I_9X , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , /* -060 */ + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , I_XN|I_CT , 0 , 0 , /* -100 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + I_XN , 0 , 0 , 0 , /* -120 */ + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN|I_9X , 0 , 0 , 0 , /* -140 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , I_9X , I_9X , I_9X , + 0 , 0 , 0 , 0 , /* -160 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -200 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -220 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XND|I_94, I_XND|I_94, 0 , 0 , /* -240 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, 0 , 0 , /* -260 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XND|I_94, I_XNR , I_XND|I_94, /* -300 */ + I_XNR|I_9X, I_XND|I_94, I_XNR|I_9X, I_XND|I_94, + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* -320 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , 0 , 0 , 0 , /* -340 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -360 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -400 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -420 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -440 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -460 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR , I_XNR , 0 , 0 , /* -500 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -520 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_R , I_R , 0 , 0 , + I_XN , I_XN , I_XN , I_XN , /* -540 */ + I_XN , I_XN , I_XNR , I_XN , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -560 */ + I_XNR|I_CT, 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XN , 0 , I_XNR|I_9X, I_XN|I_94 , /* -600 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_XNR|I_9X, 0 , 0 , 0 , /* -620 */ + 0 , I_XNR , 0 , 0 , + 0 , 0 , 0 , 0 , + I_R , 0 , I_R|I_94 , 0 , + I_XN , I_XN , I_XN , I_XN , /* -640 */ + I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , I_XN|I_9X , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -660 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + I_9X , 0 , 0 , 0 , /* -700 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -720 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , /* -740 */ + 0 , 0 , 0 , 0 , + 0 , 0 , 0 , 0 , + 0 , 0 , I_94 , 0 , + I_X , I_X|I_CT , 0 , I_X , /* -760 */ + 0 , I_X , 0 , 0 , + 0 , 0 , I_X , I_X , + I_9X , 0 , 0 , 0 + }; + +/* Instruction execution routine */ + +t_stat sim_instr (void) +{ +t_stat reason = SCPE_OK; +t_uint64 IR, SR, t, t1, t2, sr1; +uint32 op, fl, tag, tagi, addr, ea; +uint32 ch, dec, xr, xec_cnt, trp; +uint32 i, j, sc, s1, s2, spill; +t_bool tracing; + +/* Restore register state */ + +ch_set_map (); /* set dispatch map */ +if (!(cpu_model & (I_94|I_CT))) mode_multi = 1; /* ~7094? MTM always on */ +eamask = mode_storn? A704_MASK: AMASK; /* set eff addr mask */ +inst_base = inst_base & ~AMASK; /* A/B sel is 1b */ +data_base = data_base & ~AMASK; +ind_reloc = ind_reloc & VA_BLK; /* canonical form */ +ind_start = ind_start & VA_BLK; +ind_limit = (ind_limit & VA_BLK) | VA_OFF; +chtr_pend = chtr_eval (NULL); /* eval chan traps */ +tracing = ((hst_lnt != 0) || DEBUG_PRS (cpu_dev)); + +if (ht_pend) { /* HTR pending? */ + oldPC = (PC - 1) & AMASK; + ht_pend = 0; /* clear flag */ + PCQ_ENTRY; + if (mode_ttrap) { /* trap? */ + WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ + TrapXfr (TRAP_TRA_PC); /* trap */ + } + else PC = ht_addr; /* branch */ + } + +/* Main instruction fetch/decode loop */ + +while (reason == SCPE_OK) { /* loop until error */ + + if (cpu_astop) { /* debug stop? */ + cpu_astop = 0; + reason = SCPE_STOP; + break; + } + + if (sim_interval <= 0) { /* intv cnt expired? */ + if (reason = sim_process_event ()) break; /* process events */ + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + + for (i = 0; ch_req && (i < NUM_CHAN); i++) { /* loop thru channels */ + if (ch_req & REQ_CH (i)) { /* channel request? */ + if (reason = ch_proc (i)) break; + } + chtr_pend = chtr_eval (NULL); + if (reason) break; /* error? */ + } + + if (chtr_pend) { /* channel trap? */ + addr = chtr_eval (&trp); /* get trap info, clr */ + chtr_inht = 1; /* inhibit traps */ + chtr_pend = 0; /* no trap pending */ + WriteTAD (addr, PC, trp); /* wr trap addr,flag */ + IR = ReadP (addr + 1); /* get trap instr */ + oldPC = PC; /* save current PC */ + } + + else { + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + if (chtr_inhi) { /* 1 cycle inhibit? */ + chtr_inhi = 0; /* clear */ + chtr_pend = chtr_eval (NULL); /* re-evaluate */ + } + oldPC = PC; /* save current PC */ + PC = (PC + 1) & eamask; /* increment PC */ + if (!ReadI (oldPC, &IR)) continue; /* get inst; trap? */ + } + + sim_interval = sim_interval - 1; + xec_cnt = 0; /* clear XEC cntr */ + + XEC: + + tag = GET_TAG (IR); /* get tag */ + addr = (uint32) IR & eamask; /* get base addr */ + +/* Decrement format instructions */ + + if (IR & INST_T_DEC) { /* decrement type? */ + op = GET_OPD (IR); /* get opcode */ + dec = GET_DEC (IR); /* get decrement */ + xr = get_xrx (tag); /* get xr, upd MTM */ + if (tracing) { /* trace or history? */ + if (hst_lnt) /* history enabled? */ + cpu_ent_hist (oldPC|HIST_PC, xr, IR, 0); + if (DEBUG_PRS (cpu_dev)) + cpu_fprint_one_inst (sim_deb, oldPC|HIST_PC, 0, xr, + IR, AC, MQ, SI, 0); + } + switch (op) { + + case 01: /* TXI */ + put_xr (tag, xr + dec); /* xr += decr */ + PCQ_ENTRY; + if (mode_ttrap) { /* trap? */ + WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ + TrapXfr (TRAP_TRA_PC); /* trap */ + } + else PC = addr; /* branch */ + break; + + case 02: /* TIX */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (xr > dec) { /* if xr > decr */ + put_xr (tag, xr - dec); /* xr -= decr */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = addr; /* branch */ + } + break; + + case 03: /* TXH */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (xr > dec) { /* if xr > decr */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = addr; /* branch */ + } + break; + + case 05: /* STR */ + WriteTA (TRAP_STD_SAV, PC); /* save inst+1 */ + PCQ_ENTRY; + PC = TRAP_STR_PC; /* branch to 2 */ + break; + + case 06: /* TNX */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (xr > dec) put_xr (tag, xr - dec); /* if xr > decr */ + else { /* xr -= decr */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = addr; /* branch */ + } + break; + + case 07: /* TXL */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (xr <= dec) { /* if xr <= decr */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = addr; /* branch */ + } + break; + } + } /* end if */ + +/* Normal format instructions */ + + else { + op = GET_OPC (IR); /* get opcode */ + fl = op_flags[op]; /* get flags */ + if (fl & I_MODEL & ~cpu_model) { /* invalid for model? */ + if (stop_illop) reason = STOP_ILLEG; /* possible stop */ + continue; + } + if (tag && (fl & I_X)) /* tag and indexable? */ + ea = (addr - get_xri (tag)) & eamask; /* do indexing */ + else ea = addr; + if (TST_IND (IR) && (fl & I_N)) { /* indirect? */ + if (!ReadI (ea, &SR)) continue; /* get ind; trap? */ + addr = (uint32) SR & eamask; /* get address */ + tagi = GET_TAG (SR); /* get tag */ + if (tagi) /* tag? */ + ea = (addr - get_xri (tagi)) & eamask; /* do indexing */ + else ea = addr; + } + if ((fl & I_R) && !Read (ea, &SR)) continue; /* read opnd; trap? */ + else if (fl & I_D) { /* double prec? */ + if ((ea & 1) && fp_trap (TRAP_F_ODD)) continue; + if (!Read (ea, &SR)) continue; /* SR gets high */ + if (!Read (ea | 1, &sr1)) continue; /* "sr1" gets low */ + } + if (tracing) { /* tracing or history? */ + if (hst_lnt) /* history enabled? */ + cpu_ent_hist (oldPC|HIST_PC, ea, IR, SR); + if (DEBUG_PRS (cpu_dev)) + cpu_fprint_one_inst (sim_deb, oldPC|HIST_PC, 0, ea, + IR, AC, MQ, SI, SR); + } + switch (op) { /* case on opcode */ + +/* Positive instructions */ + + case 00000: /* HTR */ + case 01000: /* also -HTR */ + if (prot_trap (0)) break; /* user mode? */ + ht_pend = 1; /* transfer pending */ + ht_addr = ea; /* save address */ + reason = STOP_HALT; /* halt if I/O done */ + break; + + case 00020: /* TRA */ + case 01020: /* also -TRA */ + PCQ_ENTRY; + if (mode_ttrap) { /* trap? */ + WriteTA (TRAP_STD_SAV, oldPC); /* save PC */ + TrapXfr (TRAP_TRA_PC); /* trap */ + } + else PC = ea; /* branch */ + break; + + case 00021: /* TTR */ + PCQ_ENTRY; + PC = ea; /* branch, no trap */ + break; + + case 00040: /* TLQ */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + s1 = (AC & AC_S)? 1: 0; /* get AC, MQ sign, */ + s2 = (MQ & SIGN)? 1: 0; /* magnitude */ + t1 = AC & AC_MMASK; + t2 = MQ & MMASK; /* signs differ? */ + if ((s1 != s2)? s2: /* y, br if MQ- */ + ((t1 != t2) && (s2 ^ (t1 > t2)))) { /* n, br if sgn-^AC>MQ */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 00041: /* IIA */ + SI = SI ^ (AC & DMASK); + break; + + case 00042: /* TIO */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if ((SI & AC) == (AC & DMASK)) { /* if ind on */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 00043: /* OAI */ + SI = SI | (AC & DMASK); + break; + + case 00044: /* PAI */ + SI = AC & DMASK; + break; + + case 00046: /* TIF */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if ((SI & AC) == 0) { /* if ind off */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 00051: /* IIR */ + SI = SI ^ (IR & RMASK); + break; + + case 00054: /* RFT */ + t = IR & RMASK; + if ((SI & t) == 0) PC = (PC + 1) & eamask; /* if ind off, skip */ + break; + + case 00055: /* SIR */ + SI = SI | (IR & RMASK); + break; + + case 00056: /* RNT */ + t = IR & RMASK; + if ((SI & t) == t) PC = (PC + 1) & eamask; /* if ind on, skip */ + break; + + case 00057: /* RIR */ + SI = SI & ~(IR & RMASK); + break; + + case 00074: /* TSX */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (tag) put_xr (tag, ~oldPC + 1); /* save -inst loc */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + break; + + case 00100: /* TZE */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_MMASK) == 0) { /* if AC Q,P,1-35 = 0 */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 00101: /* (CTSS) TIA */ + if (prot_trap (0)) break; /* not user mode? */ + PCQ_ENTRY; + PC = ea; + inst_base = 0; + break; + + case 00114: case 00115: case 00116: case 00117: /* CVR */ + sc = GET_CCNT (IR); + SR = ea; + while (sc) { + ea = (uint32) ((AC & 077) + SR) & eamask; + if (!Read (ea, &SR)) break; + AC = (AC & AC_S) | ((AC >> 6) & 0017777777777) | + (SR & 0770000000000); + sc--; + } + if ((sc == 0) && (IR & INST_T_CXR1)) + put_xr (1, (uint32) SR); + break; + + case 00120: /* TPL */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_S) == 0) { /* if AC + */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 00131: /* XCA */ + t = MQ; + MQ = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); + AC = (t & MMASK) | ((t & SIGN)? AC_S: 0); + break; + + case 00140: /* TOV */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (ind_ovf) { /* if overflow */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + ind_ovf = 0; /* clear overflow */ + } + break; + + case 00161: /* TQO */ + if (!mode_ftrap) { /* only in 704 mode */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (ind_mqo) { /* if MQ overflow */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + ind_mqo = 0; /* clear overflow */ + } + } + break; + + case 00162: /* TQP */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if ((MQ & SIGN) == 0) { /* if MQ + */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 00200: /* MPY */ + op_mpy (0, SR, 043); + break; + + case 00204: /* VLM */ + case 00205: /* for diagnostic */ + sc = GET_VCNT (IR); + op_mpy (0, SR, sc); + break; + + case 00220: /* DVH */ + if (op_div (SR, 043)) { + ind_dvc = 1; + if (!prot_trap (0)) reason = STOP_DIVCHK; + } + break; + + case 00221: /* DVP */ + if (op_div (SR, 043)) ind_dvc = 1; + break; + + case 00224: /* VDH */ + case 00226: /* for diagnostic */ + sc = GET_VCNT (IR); + if (op_div (SR, sc)) { + ind_dvc = 1; + if (!prot_trap (0)) reason = STOP_DIVCHK; + } + break; + + case 00225: /* VDP */ + case 00227: /* for diagnostic */ + sc = GET_VCNT (IR); + if (op_div (SR, sc)) ind_dvc = 1; + break; + + case 00240: /* FDH */ + spill = op_fdv (SR); + if (spill == TRAP_F_DVC) { + ind_dvc = 1; + if (!prot_trap (0)) reason = STOP_DIVCHK; + } + else if (spill) fp_trap (spill); + break; + + case 00241: /* FDP */ + spill = op_fdv (SR); + if (spill == TRAP_F_DVC) ind_dvc = 1; + else if (spill) fp_trap (spill); + break; + + case 00260: /* FMP */ + spill = op_fmp (SR, 1); /* MQ * SR */ + if (spill) fp_trap (spill); + break; + + case 00261: /* DFMP */ + spill = op_dfmp (SR, sr1, 1); + if (spill) fp_trap (spill); + break; + + case 00300: /* FAD */ + spill = op_fad (SR, 1); + if (spill) fp_trap (spill); + break; + + case 00301: /* DFAD */ + spill = op_dfad (SR, sr1, 1); + if (spill) fp_trap (spill); + break; + + case 00302: /* FSB */ + spill = op_fad (SR ^ SIGN, 1); + if (spill) fp_trap (spill); + break; + + case 00303: /* DFSB */ + spill = op_dfad (SR ^ SIGN, sr1, 1); + if (spill) fp_trap (spill); + break; + + case 00304: /* FAM */ + spill = op_fad (SR & ~SIGN, 1); + if (spill) fp_trap (spill); + break; + + case 00305: /* DFAM */ + spill = op_dfad (SR & ~SIGN, sr1, 1); + if (spill) fp_trap (spill); + break; + + case 00306: /* FSM */ + spill = op_fad (SR | SIGN, 1); + if (spill) fp_trap (spill); + break; + + case 00307: /* DFSM */ + spill = op_dfad (SR | SIGN, sr1, 1); + if (spill) fp_trap (spill); + break; + + case 00320: /* ANS */ + SR = AC & SR; + Write (ea, SR); + break; + + case 00322: /* ERA */ + AC = (AC ^ SR) & DMASK; /* AC S,Q cleared */ + break; + + case 00340: /* CAS */ + s1 = (AC & AC_S)? 1: 0; /* get AC, MQ signs, */ + s2 = (SR & SIGN)? 1: 0; + t1 = AC & AC_MMASK; /* magnitudes */ + t2 = SR & MMASK; + if (s1 ^ s2) { /* diff signs? */ + if (s1) PC = (PC + 2) & eamask; /* AC < mem? skip 2 */ + } + else if (t1 == t2) PC = (PC + 1) & eamask; /* equal? skip 1 */ + else if ((t1 < t2) ^ s1) /* AC < mem, AC +, or */ + PC = (PC + 2) & eamask; /* AC > mem, AC -? */ + break; + + case 00361: /* ACL */ + t = (AC + SR) & DMASK; /* AC P,1-35 + SR */ + if (t < SR) t = (t + 1) & DMASK; /* end around carry */ + AC = (AC & (AC_S | AC_Q)) | t; /* preserve AC S,Q */ + break; + + case 00400: /* ADD */ + op_add (SR); + break; + + case 00401: /* ADM */ + op_add (SR & MMASK); + break; + + case 00402: /* SUB */ + op_add (SR ^ SIGN); + break; + + case 00420: /* HPR */ + if (prot_trap (0)) break; /* user mode? */ + reason = STOP_HALT; /* halt if I/O done */ + break; + + case 00440: /* IIS */ + SI = SI ^ SR; + break; + + case 00441: /* LDI */ + SI = SR; + break; + + case 00442: /* OSI */ + SI = SI | SR; + break; + + case 00443: /* DLD */ + AC = (SR & MMASK) | ((SR & SIGN)? AC_S: 0); /* normal load */ + if (!Read (ea | 1, &SR)) break; /* second load */ + MQ = SR; + if (ea & 1) fp_trap (TRAP_F_ODD); /* trap after exec */ + break; + + case 00444: /* OFT */ + if ((SI & SR) == 0) PC = (PC + 1) & eamask; /* skip if ind off */ + break; + + case 00445: /* RIS */ + SI = SI & ~SR; + break; + + case 00446: /* ONT */ + if ((SI & SR) == SR) PC = (PC + 1) & eamask;/* skip if ind on */ + break; + + case 00460: /* LDA (704) */ + cpy_trap (PC); + break; + + case 00500: /* CLA */ + AC = (SR & MMASK) | ((SR & SIGN)? AC_S: 0); + break; + + case 00502: /* CLS */ + AC = (SR & MMASK) | ((SR & SIGN)? 0: AC_S); + break; + + case 00520: /* ZET */ + if ((SR & MMASK) == 0) PC = (PC + 1) & eamask; /* skip if M 1-35 = 0 */ + break; + + case 00522: /* XEC */ + if (xec_cnt++ >= xec_max) { /* xec chain limit? */ + reason = STOP_XEC; /* stop */ + break; + } + IR = SR; /* operand is new inst */ + chtr_inhi = 1; /* delay traps */ + chtr_pend = 0; /* no trap now */ + goto XEC; /* start over */ + + case 00534: /* LXA */ + if (tag) put_xr (tag, (uint32) SR); /* M addr -> xr */ + break; + + case 00535: /* LAC */ + if (tag) put_xr (tag, NEG ((uint32) SR)); /* -M addr -> xr */ + break; + + case 00560: /* LDQ */ + MQ = SR; + break; + + case 00562: /* (CTSS) LRI */ + if (prot_trap (0)) break; /* user mode? */ + ind_reloc = ((uint32) SR) & VA_BLK; + break; + + case 00564: /* ENB */ + if (prot_trap (0)) break; /* user mode? */ + chtr_enab = (uint32) SR; /* set enables */ + chtr_inht = 0; /* clear inhibit */ + chtr_inhi = 1; /* 1 cycle delay */ + chtr_pend = 0; /* no traps now */ + break; + + case 00600: /* STZ */ + Write (ea, 0); + break; + + case 00601: /* STO */ + SR = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); + Write (ea, SR); + break; + + case 00602: /* SLW */ + Write (ea, AC & DMASK); + break; + + case 00604: /* STI */ + Write (ea, SI); + break; + + case 00621: /* STA */ + SR = (SR & ~AMASK) | (AC & AMASK); + Write (ea, SR); + break; + + case 00622: /* STD */ + SR = (SR & ~XMASK) | (AC & XMASK); + Write (ea, SR); + break; + + case 00625: /* STT */ + SR = (SR & ~TMASK) | (AC & TMASK); + Write (ea, SR); + break; + + case 00630: /* STP */ + SR = (SR & ~PMASK) | (AC & PMASK); + Write (ea, SR); + break; + + case 00634: /* SXA */ + SR = (SR & ~AMASK) | /* xr -> M addr */ + ((t_uint64) get_xrx (tag)); + Write (ea, SR); + break; + + case 00636: /* SCA */ + SR = (SR & ~AMASK) | /* -xr -> M addr */ + ((t_uint64) (NEG (get_xrx (tag)) & AMASK)); + Write (ea, SR); + break; + + case 00700: /* CPY (704) */ + cpy_trap (PC); + break; + + case 00734: /* PAX */ + if (tag) put_xr (tag, (uint32) AC); /* AC addr -> xr */ + break; + + case 00737: /* PAC */ + if (tag) put_xr (tag, NEG ((uint32) AC)); /* -AC addr -> xr */ + break; + + case 00754: /* PXA */ + AC = get_xrx (tag); /* xr -> AC */ + break; + + case 00756: /* PCA */ + AC = NEG (get_xrx (tag)) & AMASK; /* -xr -> AC */ + break; + + case 00760: /* PSE */ + if (prot_trap (0)) break; /* user mode? */ + reason = op_pse (ea); + break; + + case 00761: /* NOP */ + break; + + case 00763: /* LLS */ + op_lls (ea); + break; + + case 00765: /* LRS */ + op_lrs (ea); + break; + + case 00767: /* ALS */ + op_als (ea); + break; + + case 00771: /* ARS */ + op_ars (ea); + break; + + case 00774: /* AXT */ + if (tag) put_xr (tag, addr); /* IR addr -> xr */ + break; + +/* Negative instructions */ + + case 01021: /* ESNT */ + mode_storn = 1; /* enter nullification */ + PCQ_ENTRY; + PC = ea; /* branch, no trap */ + break; + + case 01042: /* RIA */ + SI = SI & ~AC; + break; + + case 01046: /* PIA */ + AC = SI; + break; + + case 01051: /* IIL */ + SI = SI ^ ((IR & RMASK) << 18); + break; + + case 01054: /* LFT */ + t = (IR & RMASK) << 18; + if ((SI & t) == 0) PC = (PC + 1) & eamask; /* if ind off, skip */ + break; + + case 01055: /* SIL */ + SI = SI | ((IR & RMASK) << 18); + break; + + case 01056: /* LNT */ + t = (IR & RMASK) << 18; + if ((SI & t) == t) PC = (PC + 1) & eamask; /* if ind on, skip */ + break; + + case 01057: /* RIL */ + SI = SI & ~((IR & RMASK) << 18); + break; + + case 01100: /* TNZ */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_MMASK) != 0) { /* if AC != 0 */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 01101: /* (CTSS) TIB */ + if (prot_trap (0)) break; /* not user mode? */ + PCQ_ENTRY; + PC = ea; + mode_user = 1; + inst_base = BCORE_BASE; + break; + + case 01114: case 01115: case 01116: case 01117: /* CAQ */ + sc = GET_CCNT (IR); + SR = ea; + while (sc) { + ea = (uint32) ((MQ >> 30) + SR) & eamask; + if (!Read (ea, &SR)) break; + MQ = ((MQ << 6) & DMASK) | (MQ >> 30); + AC = (AC & AC_S) | ((AC + SR) & AC_MMASK); + sc--; + } + if ((sc == 0) && (IR & INST_T_CXR1)) + put_xr (1, (uint32) SR); + break; + + case 01120: /* TMI */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if ((AC & AC_S) != 0) { /* if AC - */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 01130: /* XCL */ + t = MQ; + MQ = AC & DMASK; + AC = t; + break; + + case 01140: /* TNO */ + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (!ind_ovf) { /* if no overflow */ + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + ind_ovf = 0; /* clear overflow */ + break; + + case 01154: case 01155: case 01156: case 01157: /* CRQ */ + sc = GET_CCNT (IR); + SR = ea; + while (sc) { + ea = (uint32) ((MQ >> 30) + SR) & eamask; + if (!Read (ea, &SR)) break; + MQ = ((MQ << 6) & DMASK) | (SR >> 30); + sc--; + } + if ((sc == 0) && (IR & INST_T_CXR1)) + put_xr (1, (uint32) SR); + break; + + case 01200: /* MPR */ + op_mpy (0, SR, 043); + if (MQ & B1) AC = (AC & AC_S) | ((AC + 1) & AC_MMASK); + break; + + case 01240: /* DFDH */ + spill = op_dfdv (SR, sr1); + if (spill == TRAP_F_DVC) { + ind_dvc = 1; + if (!prot_trap (0)) reason = STOP_DIVCHK; + } + else if (spill) fp_trap (spill); + break; + + case 01241: /* DFDP */ + spill = op_dfdv (SR, sr1); + if (spill == TRAP_F_DVC) ind_dvc = 1; + else if (spill) fp_trap (spill); + break; + + case 01260: /* UFM */ + spill = op_fmp (SR, 0); + if (spill) fp_trap (spill); + break; + + case 01261: /* DUFM */ + spill = op_dfmp (SR, sr1, 0); + if (spill) fp_trap (spill); + break; + + case 01300: /* UFA */ + spill = op_fad (SR, 0); + if (spill) fp_trap (spill); + break; + + case 01301: /* DUFA */ + spill = op_dfad (SR, sr1, 0); + if (spill) fp_trap (spill); + break; + + case 01302: /* UFS */ + spill = op_fad (SR ^ SIGN, 0); + if (spill) fp_trap (spill); + break; + + case 01303: /* DUFS */ + spill = op_dfad (SR ^ SIGN, sr1, 0); + if (spill) fp_trap (spill); + break; + + case 01304: /* UAM */ + spill = op_fad (SR & ~SIGN, 0); + if (spill) fp_trap (spill); + break; + + case 01305: /* DUAM */ + spill = op_dfad (SR & ~SIGN, sr1, 0); + if (spill) fp_trap (spill); + break; + + case 01306: /* USM */ + spill = op_fad (SR | SIGN, 0); + if (spill) fp_trap (spill); + break; + + case 01307: /* DUSM */ + spill = op_dfad (SR | SIGN, sr1, 0); + if (spill) fp_trap (spill); + break; + + case 01320: /* ANA */ + AC = AC & SR; + break; + + case 01340: /* LAS */ + t = AC & AC_MMASK; /* AC Q,P,1-35 */ + if (t < SR) PC = (PC + 2) & eamask; + else if (t == SR) PC = (PC + 1) & eamask; + break; + + case 01400: /* SBM */ + op_add (SR | SIGN); + break; + + case 01500: /* CAL */ + AC = SR; + break; + + case 01501: /* ORA */ + AC = AC | SR; + break; + + case 01520: /* NZT */ + if ((SR & MMASK) != 0) PC = (PC + 1) & eamask; ; + break; + + case 01534: /* LXD */ + if (tag) put_xr (tag, GET_DEC (SR)); /* M decr -> xr */ + break; + + case 01535: /* LDC */ + if (tag) put_xr (tag, NEG (GET_DEC (SR))); /* -M decr -> xr */ + break; + + case 01564: /* (CTSS) LPI */ + if (prot_trap (0)) break; /* user mode? */ + ind_start = ((uint32) SR) & VA_BLK; + ind_limit = (GET_DEC (SR) & VA_BLK) | VA_OFF; + break; + + case 01600: /* STQ */ + Write (ea, MQ); + break; + + case 01602: /* ORS */ + SR = SR | (AC & DMASK); + Write (ea, SR); + break; + + case 01603: /* DST */ + SR = (AC & MMASK) | ((AC & AC_S)? SIGN: 0); + if (!Write (ea, SR)) break; + Write ((ea + 1) & eamask, MQ); + break; + + case 01620: /* SLQ */ + SR = (SR & RMASK) | (MQ & LMASK); + Write (ea, SR); + break; + + case 01625: /* STL */ + SR = (SR & ~AMASK) | PC; + Write (ea, SR); + break; + + case 01634: /* SXD */ + SR = (SR & ~XMASK) | /* xr -> M decr */ + (((t_uint64) get_xrx (tag)) << INST_V_DEC); + Write (ea, SR); + break; + + case 01636: /* SCD */ + SR = (SR & ~XMASK) | /* -xr -> M decr */ + (((t_uint64) (NEG (get_xrx (tag)) & AMASK)) << INST_V_DEC); + Write (ea, SR); + break; + + case 01700: /* CAD (704) */ + cpy_trap (PC); + break; + + case 01734: /* PDX */ + if (tag) put_xr (tag, GET_DEC (AC)); /* AC decr -> xr */ + break; + + case 01737: /* PDC */ + if (tag) put_xr (tag, NEG (GET_DEC (AC))); /* -AC decr -> xr */ + break; + + case 01754: /* PXD */ + AC = ((t_uint64) get_xrx (tag)) << INST_V_DEC; + break; /* xr -> AC decr */ + + case 01756: /* PCD */ + AC = ((t_uint64) (NEG (get_xrx (tag)) & AMASK)) << INST_V_DEC; + break; /* -xr -> AC decr */ + + case 01760: /* MSE */ + if (prot_trap (0)) break; /* user mode? */ + reason = op_mse (ea); + break; + + case 01761: /* (CTSS) ext core */ + if (prot_trap (0)) break; /* user mode? */ + if (ea == 041) /* SEA? */ + data_base = 0; + else if (ea == 042) /* SEB? */ + data_base = BCORE_BASE; + else if (ea == 043) { /* IFT? */ + if (inst_base == 0) + PC = (PC + 1) & eamask; + } + else if (ea == 044) { /* EFT? */ + if (data_base == 0) + PC = (PC + 1) & eamask; + } + else if (stop_illop) + reason = STOP_ILLEG; + break; + + case 01763: /* LGL */ + op_lgl (ea); + break; + + case 01765: /* LGR */ + op_lgr (ea); + break; + + case 01773: /* RQL */ + sc = (ea & SCMASK) % 36; + if (sc) MQ = ((MQ << sc) | (MQ >> (36 - sc))) & DMASK; + break; + + case 01774: /* AXC */ + if (tag) put_xr (tag, NEG (addr)); /* -IR addr -> xr */ + break; + +/* IO instructions */ + + case 00022: case 00024: case 00026: /* TRCx */ + case 01022: case 01024: case 01026: + if (prot_trap (0)) break; /* user mode? */ + ch = ((op & 077) - 00022) | ((op >> 9) & 01); + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (!BIT_TST (chtr_enab, CHTR_V_TRC + ch) && + (ch_flags[ch] & CHF_TRC)) { + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + ch_flags[ch] = ch_flags[ch] & ~CHF_TRC; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + break; + + case 00027: case 01027: + if (prot_trap (0)) break; /* user mode? */ + ch = 6 + ((op >> 9) & 01); + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (!BIT_TST (chtr_enab, CHTR_V_TRC + ch) && + (ch_flags[ch] & CHF_TRC)) { + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + ch_flags[ch] = ch_flags[ch] & ~CHF_TRC; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + break; + + case 00030: case 00031: case 00032: case 00033: /* TEFx */ + case 01030: case 01031: case 01032: case 01033: + if (prot_trap (0)) break; /* user mode? */ + ch = ((op & 03) << 1) | ((op >> 9) & 01); + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (!BIT_TST (chtr_enab, CHTR_V_CME + ch) && + (ch_flags[ch] & CHF_EOF)) { + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOF; + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + } + break; + + case 00060: case 00061: case 00062: case 00063: /* TCOx */ + case 00064: case 00065: case 00066: case 00067: + if (prot_trap (0)) break; /* user mode? */ + ch = op & 07; + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (ch_sta[ch] != CHXS_IDLE) { + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 01060: case 01061: case 01062: case 01063: /* TCNx */ + case 01064: case 01065: case 01066: case 01067: + if (prot_trap (0)) break; /* user mode? */ + ch = op & 07; + if (mode_ttrap) WriteTA (TRAP_STD_SAV, oldPC); + if (ch_sta[ch] == CHXS_IDLE) { + PCQ_ENTRY; + if (mode_ttrap) TrapXfr (TRAP_TRA_PC); /* trap? */ + else PC = ea; /* branch */ + } + break; + + case 00540: case 00541: case 00542: case 00543: /* RCHx */ + case 01540: case 01541: case 01542: case 01543: + if (prot_trap (0)) break; /* user mode? */ + ch = ((op & 03) << 1) | ((op >> 9) & 01); + reason = ch_op_start (ch, ea, TRUE); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00544: case 00545: case 00546: case 00547: /* LCHx */ + case 01544: case 01545: case 01546: case 01547: + if (prot_trap (0)) break; /* user mode? */ + ch = ((op & 03) << 1) | ((op >> 9) & 01); + reason = ch_op_start (ch, ea, FALSE); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00640: case 00641: case 00642: case 00643: /* SCHx */ + case 01640: case 01641: case 01642: case 01643: + if (prot_trap (0)) break; /* user mode? */ + ch = ((op & 03) << 1) | ((op >> 9) & 01); + if ((reason = ch_op_store (ch, &SR)) == SCPE_OK) + Write (ea, SR); + break; + + case 00644: case 00645: case 00646: case 00647: /* SCDx */ + case 01644: case 01645: case 01646: case 01647: + ch = ((op & 03) << 1) | ((op >> 9) & 01); + if ((reason = ch_op_store_diag (ch, &SR)) == SCPE_OK) + Write (ea, SR); + break; + + case 00762: /* RDS */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_ds (ch, CHSL_RDS, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00764: /* BSR */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_BSR, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00766: /* WRS */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_ds (ch, CHSL_WRS, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00770: /* WEF */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_WEF, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00772: /* REW */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_REW, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 01764: /* BSF */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_BSF, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 01772: /* RUN */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_RUN, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + case 00776: /* SDN */ + if (prot_trap (0) || sel_trap (PC)) break; + ch = GET_U_CH (IR); + reason = ch_op_nds (ch, CHSL_SDN, GET_U_UNIT (ea)); + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + break; + + default: + if (stop_illop) reason = STOP_ILLEG; + break; + } + } /* end else */ + + if (reason) { /* reason code? */ + if (reason == ERR_STALL) { /* stall? */ + PC = oldPC; /* back up PC */ + reason = 0; + } + else if (reason == STOP_HALT) { /* halt? wait for IO */ + t_stat r; + for (i = 0; (i < HALT_IO_LIMIT) && !ch_qidle (); i++) { + sim_interval = 0; + if (r = sim_process_event ()) return r; /* process events */ + chtr_pend = chtr_eval (NULL); /* eval chan traps */ + while (ch_req) { /* until no ch req */ + for (j = 0; j < NUM_CHAN; j++) { /* loop thru channels */ + if (ch_req & REQ_CH (j)) { /* channel request? */ + if (r = ch_proc (j)) return r; + } + chtr_pend = chtr_eval (NULL); + } + } /* end while ch_req */ + } /* end for wait */ + if (chtr_pend) reason = 0; /* trap? cancel HALT */ + } /* end if HALT */ + } /* end if reason */ + } /* end while */ + +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Get index register for indexing */ + +uint32 get_xri (uint32 tag) +{ +tag = tag & INST_M_TAG; + +if (tag) { + if (mode_multi) { + uint32 r = 0; + if (tag & 1) r = r | XR[1]; + if (tag & 2) r = r | XR[2]; + if (tag & 4) r = r | XR[4]; + return r & eamask; + } + return XR[tag] & eamask; + } +return 0; +} + +/* Get index register for instruction execution + + Instructions which are 'executing directly' on index registers rewrite + the index register value. In multi-tag mode, this causes all registers + involved in the OR function to receive the OR'd value. */ + +uint32 get_xrx (uint32 tag) +{ +tag = tag & INST_M_TAG; + +if (tag) { + if (mode_multi) { + uint32 r = 0; + if (tag & 1) r = r | XR[1]; + if (tag & 2) r = r | XR[2]; + if (tag & 4) r = r | XR[4]; + put_xr (tag, r); + return r & eamask; + } + return XR[tag] & eamask; + } +return 0; +} + +/* Store index register */ + +void put_xr (uint32 tag, uint32 dat) +{ +tag = tag & INST_M_TAG; +dat = dat & eamask; + +if (tag) { + if (mode_multi) { + if (tag & 1) XR[1] = dat; + if (tag & 2) XR[2] = dat; + if (tag & 4) XR[4] = dat; + } + else XR[tag] = dat; + } +return; +} + +/* Floating point trap */ + +t_bool fp_trap (uint32 spill) +{ +if (mode_ftrap) { + WriteTAD (TRAP_STD_SAV, PC, spill); + PCQ_ENTRY; + PC = TRAP_FP_PC; + return TRUE; + } +else { + if (spill & TRAP_F_AC) ind_ovf = 1; + if (spill & TRAP_F_MQ) ind_mqo = 1; + } +return FALSE; +} + +/* (CTSS) Protection trap */ + +t_bool prot_trap (uint32 decr) +{ +if (mode_user) { + WriteTAD (TRAP_PROT_SAV, PC, decr); + PCQ_ENTRY; + PC = TRAP_PROT_PC; + return TRUE; + } +return FALSE; +} + +/* Store trap address and decrement, with A/B select flags; clear A/B, user mode */ + +void WriteTAD (uint32 pa, uint32 addr, uint32 decr) +{ +t_uint64 mem; + +if (inst_base) decr |= TRAP_F_BINST; +if (data_base) decr |= TRAP_F_BDATA; +mem = ReadP (pa) & ~(XMASK | AMASK); +mem |= (((t_uint64) (decr & AMASK)) << INST_V_DEC) | + ((t_uint64) (addr & AMASK)); +WriteP (pa, mem); +mode_ctrap = 0; +mode_strap = 0; +mode_storn = 0; +mode_user = 0; +inst_base = 0; +data_base = 0; +return; +} + +/* Copy trap */ + +t_bool cpy_trap (uint32 va) +{ +if (mode_ctrap) { + WriteTA (TRAP_704_SAV, va); + PCQ_ENTRY; + TrapXfr (TRAP_CPY_PC); + return TRUE; + } +return FALSE; +} + +/* Select trap */ + +t_bool sel_trap (uint32 va) +{ +if (mode_strap) { + WriteTA (TRAP_704_SAV, va); + PCQ_ENTRY; + TrapXfr (TRAP_SEL_PC); + return TRUE; + } +return FALSE; +} + +/* Store trap address - do not alter state yet (might be TRA) */ + +void WriteTA (uint32 pa, uint32 dat) +{ +t_uint64 mem; + +mem = ReadP (pa) & ~AMASK; +mem |= (dat & AMASK); +WriteP (pa, mem); +return; +} + +/* Set trap PC - second half of address-only trap */ + +void TrapXfr (uint32 newpc) +{ +PC = newpc; +mode_ctrap = 0; +mode_strap = 0; +mode_storn = 0; +mode_user = 0; +inst_base = 0; +data_base = 0; +return; +} + +/* Read instruction and indirect */ + +t_bool ReadI (uint32 va, t_uint64 *val) +{ +if (mode_user) { + va = (va + ind_reloc) & AMASK; + if ((va < ind_start) || (va > ind_limit)) { + prot_trap (0); + return FALSE; + } + } +*val = M[va | inst_base]; +return TRUE; +} + +/* Read */ + +t_bool Read (uint32 va, t_uint64 *val) +{ +if (mode_user) { + va = (va + ind_reloc) & AMASK; + if ((va < ind_start) || (va > ind_limit)) { + prot_trap (0); + return FALSE; + } + } +*val = M[va | data_base]; +return TRUE; +} + +/* Write */ + +t_bool Write (uint32 va, t_uint64 dat) +{ +if (mode_user) { + va = (va + ind_reloc) & AMASK; + if ((va < ind_start) || (va > ind_limit)) { + prot_trap (0); + return FALSE; + } + } +M[va | data_base] = dat; +return TRUE; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +ind_ovf = 0; +ind_mqo = 0; +ind_dvc = 0; +ind_ioc = 0; +ind_reloc = 0; +ind_start = 0; +ind_limit = 0; +mode_ttrap = 0; +mode_ctrap = 0; +mode_strap = 0; +mode_ftrap = 1; +mode_storn = 0; +if (cpu_model & (I_94|I_CT)) mode_multi = 0; +else mode_multi = 1; +mode_user = 0; +inst_base = 0; +data_base = 0; +ch_req = 0; +chtr_pend = chtr_enab = 0; +chtr_inht = chtr_inhi = 0; +ht_pend = 0; +SLT = 0; +XR[0] = 0; +if (M == NULL) M = (t_uint64 *) calloc (MAXMEMSIZE, sizeof (t_uint64)); +if (M == NULL) return SCPE_MEM; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw) +{ +if (vptr == NULL) return SCPE_ARG; +if ((sw & (SWMASK ('A') | SWMASK ('B')))? (ea > AMASK): (ea >= MEMSIZE)) return SCPE_NXM; +if ((sw & SWMASK ('B')) || + ((sw & SWMASK ('V')) && mode_user && inst_base)) ea = ea | BCORE_BASE; +*vptr = M[ea] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) +{ +if ((sw & (SWMASK ('A') | SWMASK ('B')))? (ea > AMASK): (ea >= MEMSIZE)) return SCPE_NXM; +if (sw & SWMASK ('B')) ea = ea | BCORE_BASE; +M[ea] = val & DMASK; +return SCPE_OK; +} + +/* Set model */ + +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +UNIT *chuptr = mt_dev[CHRONO_CH].units + CHRONO_UNIT; +extern DEVICE clk_dev; + +cpu_model = val; +if (val & I_CT) { + uptr->capac = MAXMEMSIZE; + detach_unit (uptr); + chuptr->flags &= ~UNIT_ATTABLE; + clk_dev.flags &= ~DEV_DIS; + } +else { + uptr->capac = STDMEMSIZE; + chuptr->flags |= UNIT_ATTABLE; + } +if (!(cpu_model & I_94)) mode_multi = 1; +return SCPE_OK; +} + +/* Show CTSS */ + +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (cpu_model & I_CT) fputs ("CTSS", st); +else if (cpu_model & I_94) fputs ("7094", st); +else fputs ("7090", st); +return SCPE_OK; +} + +/* Insert history entry */ + +static uint32 inst_io_tab[32] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0000 - 0377 */ + 0, 0, 0, 0x000000FF, 0, 0, 0, 0x45540000, /* 0400 - 0777 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 1000 - 1400 */ + 0, 0, 0, 0x000000FF, 0, 0, 0, 0 /* 1400 - 1777 */ + }; + +void cpu_ent_hist (uint32 pc, uint32 ea, t_uint64 ir, t_uint64 opnd) +{ +int32 prv_p; + +if (pc & HIST_PC) { + if ((pc == hst[hst_p].pc) && (ir == hst[hst_p].ir)) { /* repeat last? */ + hst[hst_p].rpt++; + return; + } + prv_p = hst_p? hst_p - 1: hst_lnt - 1; + if ((pc == hst[prv_p].pc) && (ir == hst[prv_p].ir)) { /* 2 line loop? */ + hst[prv_p].rpt++; + return; + } + if (hst_ch & HIST_CH_I) { /* IO only? */ + uint32 op = GET_OPC (ir); /* get opcode */ + if ((ir & INST_T_DEC) || + !(inst_io_tab[op / 32] & (1u << (op & 037)))) return; + } + } +hst_p = (hst_p + 1); /* next entry */ +if (hst_p >= hst_lnt) hst_p = 0; +hst[hst_p].pc = pc; +hst[hst_p].ir = ir; +hst[hst_p].ac = AC; +hst[hst_p].mq = MQ; +hst[hst_p].si = SI; +hst[hst_p].ea = ea; +hst[hst_p].opnd = opnd; +hst[hst_p].rpt = 0; +return; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = hst_ch = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + if (sim_switches & SWMASK ('I')) hst_ch = HIST_CH_I|HIST_CH_C; + else if (sim_switches & SWMASK ('C')) hst_ch = HIST_CH_C; + else hst_ch = 0; + } +return SCPE_OK; +} + +/* Print one instruction */ + +t_stat cpu_fprint_one_inst (FILE *st, uint32 pc, uint32 rpt, uint32 ea, + t_uint64 ir, t_uint64 ac, t_uint64 mq, t_uint64 si, t_uint64 opnd) +{ +int32 ch; +t_value sim_eval; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +sim_eval = ir; +if (pc & HIST_PC) { /* instruction? */ + fputs ("CPU ", st); + fprintf (st, "%05o ", pc & AMASK); + if (rpt == 0) fprintf (st, " "); + else if (rpt < 1000000) fprintf (st, "%6d ", rpt); + else fprintf (st, "%5dM ", rpt / 1000000); + fprint_val (st, ac, 8, 38, PV_RZRO); + fputc (' ', st); + fprint_val (st, mq, 8, 36, PV_RZRO); + fputc (' ', st); + fprint_val (st, si, 8, 36, PV_RZRO); + fputc (' ', st); + if (ir & INST_T_DEC) fprintf (st, " "); + else fprintf (st, "%05o ", ea); + if (fprint_sym (st, pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M')) > 0) { + fputs ("(undefined) ", st); + fprint_val (st, ir, 8, 36, PV_RZRO); + } + else if (!(ir & INST_T_DEC) && (op_flags[GET_OPC (ir)] & I_R)) { + fputs (" [", st); + fprint_val (st, opnd, 8, 36, PV_RZRO); + fputc (']', st); + } + fputc ('\n', st); /* end line */ + } /* end if instruction */ +else if (ch = HIST_CH (pc)) { /* channel? */ + fprintf (st, "CH%c ", 'A' + ch - 1); + fprintf (st, "%05o ", pc & AMASK); + fputs (" ", st); + fprintf (st, "%05o ", ea & AMASK); + if (fprint_sym (st, pc & AMASK, &sim_eval, &cpu_unit, + (ch_dev[ch - 1].flags & DEV_7909)? SWMASK ('N'): SWMASK ('I')) > 0) { + fputs ("(undefined) ", st); + fprint_val (st, ir, 8, 36, PV_RZRO); + } + fputc ('\n', st); /* end line */ + } /* end else channel */ +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +InstHistory *h; + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, " PC repeat AC MQ SI EA IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + cpu_fprint_one_inst (st, h->pc, h->rpt, h->ea, h->ir, h->ac, h->mq, h->si, h->opnd); + } /* end for */ +return SCPE_OK; +} diff --git a/I7094/i7094_cpu1.c b/I7094/i7094_cpu1.c new file mode 100644 index 0000000..dc6dd0b --- /dev/null +++ b/I7094/i7094_cpu1.c @@ -0,0 +1,837 @@ +/* i7094_cpu1.c: IBM 7094 CPU complex instructions + + Copyright (c) 2003-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include "i7094_defs.h" + +#define FP_HIFRAC(x) ((uint32) ((x) >> FP_N_FR) & FP_FMASK) +#define FP_LOFRAC(x) ((uint32) (x) & FP_FMASK) + +#define FP_PACK38(s,e,f) (((s)? AC_S: 0) | ((t_uint64) (f)) | \ + (((t_uint64) ((e) & FP_M_ACCH)) << FP_V_CH)) +#define FP_PACK36(s,e,f) (((s)? SIGN: 0) | ((t_uint64) (f)) | \ + (((t_uint64) ((e) & FP_M_CH)) << FP_V_CH)) + +extern t_uint64 AC, MQ, SI, KEYS; +extern uint32 PC; +extern uint32 SLT, SSW; +extern uint32 cpu_model, stop_illop; +extern uint32 ind_ovf, ind_dvc, ind_ioc, ind_mqo; +extern uint32 mode_ttrap, mode_strap, mode_ctrap, mode_ftrap; +extern uint32 mode_storn, mode_multi; +extern uint32 chtr_pend, chtr_inht, chtr_inhi; +extern uint32 ch_flags[NUM_CHAN]; + +typedef struct { /* unpacked fp */ + uint32 s; /* sign: 0 +, 1 - */ + int32 ch; /* exponent */ + t_uint64 fr; /* fraction (54b) */ + } UFP; + +uint32 op_frnd (void); +t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem); +void fp_norm (UFP *op); +void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op); +uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch); + +extern t_bool fp_trap (uint32 spill); +extern t_bool sel_trap (uint32 va); +extern t_stat ch_op_reset (uint32 ch, t_bool ch7909); + +/* Integer add + + Sherman: "As the result of an addition or subtraction, if the C(AC) is + zero, the sign of AC is unchanged." */ + +void op_add (t_uint64 op) +{ +t_uint64 mac = AC & AC_MMASK; /* get magnitudes */ +t_uint64 mop = op & MMASK; + +AC = AC & AC_S; /* isolate AC sign */ +if ((AC? 1: 0) ^ ((op & SIGN)? 1: 0)) { /* signs diff? sub */ + if (mac >= mop) AC = AC | (mac - mop); /* AC >= MQ */ + else AC = (AC ^ AC_S) | (mop - mac); /* <, sign change */ + } +else { + AC = AC | ((mac + mop) & AC_MMASK); /* signs same, add */ + if ((AC ^ mac) & AC_P) ind_ovf = 1; /* P change? overflow */ + } +return; +} + +/* Multiply */ + +void op_mpy (t_uint64 ac, t_uint64 sr, uint32 sc) +{ +uint32 sign; + +if (sc == 0) return; /* sc = 0? nop */ +sign = ((MQ & SIGN)? 1: 0) ^ ((sr & SIGN)? 1: 0); /* result sign */ +ac = ac & AC_MMASK; /* clear AC sign */ +sr = sr & MMASK; /* mpy magnitude */ +MQ = MQ & MMASK; /* MQ magnitude */ +if (sr && MQ) { /* mpy != 0? */ + while (sc--) { /* for sc */ + if (MQ & 1) ac = (ac + sr) & AC_MMASK; /* MQ35? AC += mpy */ + MQ = (MQ >> 1) | ((ac & 1) << 34); /* AC'MQ >> 1 */ + ac = ac >> 1; + } + } +else ac = MQ = 0; /* result = 0 */ +if (sign) { /* negative? */ + ac = ac | AC_S; /* insert signs */ + MQ = MQ | SIGN; + } +AC = ac; /* update AC */ +return; +} + +/* Divide */ + +t_bool op_div (t_uint64 sr, uint32 sc) +{ +uint32 signa, signm; + +if (sc == 0) return FALSE; /* sc = 0? nop */ +signa = (AC & AC_S)? 1: 0; /* get signs */ +signm = (sr & SIGN)? 1: 0; +sr = sr & MMASK; /* get dvr magn */ +if ((AC & AC_MMASK) >= sr) return TRUE; /* |AC| >= |sr|? */ +AC = AC & AC_MMASK; /* AC, MQ magn */ +MQ = MQ & MMASK; +while (sc--) { /* for sc */ + AC = ((AC << 1) & AC_MMASK) | (MQ >> 34); /* AC'MQ << 1 */ + MQ = (MQ << 1) & MMASK; + if (AC >= sr) { /* AC >= dvr? */ + AC = AC - sr; /* AC -= dvr */ + MQ = MQ | 1; /* set quo bit */ + } + } +if (signa ^ signm) MQ = MQ | SIGN; /* quo neg? */ +if (signa) AC = AC | AC_S; /* rem neg? */ +return FALSE; /* div ok */ +} + +/* Shifts */ + +void op_als (uint32 addr) +{ +uint32 sc = addr & SCMASK; + +if ((sc >= 35)? /* shift >= 35? */ + ((AC & MMASK) != 0): /* test all bits for ovf */ + (((AC & MMASK) >> (35 - sc)) != 0)) /* test only 35-sc bits */ + ind_ovf = 1; +if (sc >= 37) AC = AC & AC_S; /* sc >= 37? result 0 */ +else AC = (AC & AC_S) | ((AC << sc) & AC_MMASK); /* shift, save sign */ +return; +} + +void op_ars (uint32 addr) +{ +uint32 sc = addr & SCMASK; + +if (sc >= 37) AC = AC & AC_S; /* sc >= 37? result 0 */ +else AC = (AC & AC_S) | ((AC & AC_MMASK) >> sc); /* shift, save sign */ +return; +} + +void op_lls (uint32 addr) +{ +uint32 sc; /* get sc */ + +AC = AC & AC_MMASK; /* clear AC sign */ +for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ + AC = ((AC << 1) & AC_MMASK) | ((MQ >> 34) & 1); /* AC'MQ << 1 */ + MQ = (MQ & SIGN) | ((MQ << 1) & MMASK); /* preserve MQ sign */ + if (AC & AC_P) ind_ovf = 1; /* if P, overflow */ + } +if (MQ & SIGN) AC = AC | AC_S; /* set ACS from MQS */ +return; +} + +void op_lrs (uint32 addr) +{ +uint32 sc = addr & SCMASK; +t_uint64 mac; + +MQ = MQ & MMASK; /* get MQ magnitude */ +if (sc != 0) { + mac = AC & AC_MMASK; /* get AC magnitude, */ + AC = AC & AC_S; /* sign */ + if (sc < 35) { /* sc [1,34]? */ + MQ = ((MQ >> sc) | (mac << (35 - sc))) & MMASK; /* MQ has AC'MQ */ + AC = AC | (mac >> sc); /* AC has AC only */ + } + else if (sc < 37) { /* sc [35:36]? */ + MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ + AC = AC | (mac >> sc); /* AC has */ + } + else if (sc < 72) /* sc [37:71]? */ + MQ = (mac >> (sc - 35)) & MMASK; /* MQ has AC only */ + else MQ = 0; /* >72? MQ = 0 */ + } +if (AC & AC_S) MQ = MQ | SIGN; /* set MQS from ACS */ +return; +} + +void op_lgl (uint32 addr) +{ +uint32 sc; /* get sc */ + +for (sc = addr & SCMASK; sc != 0; sc--) { /* for SC */ + AC = (AC & AC_S) | ((AC << 1) & AC_MMASK) | /* AC'MQ << 1 */ + ((MQ >> 35) & 1); /* preserve AC sign */ + MQ = (MQ << 1) & DMASK; + if (AC & AC_P) ind_ovf = 1; /* if P, overflow */ + } +return; +} + +void op_lgr (uint32 addr) +{ +uint32 sc = addr & SCMASK; +t_uint64 mac; + +if (sc != 0) { + mac = AC & AC_MMASK; /* get AC magnitude, */ + AC = AC & AC_S; /* sign */ + if (sc < 36) { /* sc [1,35]? */ + MQ = ((MQ >> sc) | (mac << (36 - sc))) & DMASK; /* MQ has AC'MQ */ + AC = AC | (mac >> sc); /* AC has AC only */ + } + else if (sc == 36) { /* sc [36]? */ + MQ = mac & DMASK; /* MQ = AC */ + AC = AC | (mac >> 36); /* AC = AC */ + } + else if (sc < 73) /* sc [37, 72]? */ + MQ = (mac >> (sc - 36)) & DMASK; /* MQ has AC only */ + else MQ = 0; /* >72, AC,MQ = 0 */ + } +return; +} + +/* Plus sense - undefined operations are NOPs */ + +t_stat op_pse (uint32 addr) +{ +uint32 ch, spill; + +switch (addr) { + + case 00000: /* CLM */ + if (cpu_model & I_9X) AC = AC & AC_S; /* 709X only */ + break; + + case 00001: /* LBT */ + if ((AC & 1) != 0) PC = (PC + 1) & AMASK; + break; + + case 00002: /* CHS */ + AC = AC ^ AC_S; + break; + + case 00003: /* SSP */ + AC = AC & ~AC_S; + break; + + case 00004: /* ENK */ + MQ = KEYS; + break; + + case 00005: /* IOT */ + if (ind_ioc) ind_ioc = 0; + else PC = (PC + 1) & AMASK; + break; + + case 00006: /* COM */ + AC = AC ^ AC_MMASK; + break; + + case 00007: /* ETM */ + if (cpu_model & I_9X) mode_ttrap = 1; /* 709X only */ + break; + + case 00010: /* RND */ + if ((cpu_model & I_9X) && (MQ & B1)) /* 709X only, MQ1 set? */ + op_add ((t_uint64) 1); /* incr AC */ + break; + + case 00011: /* FRN */ + if (cpu_model & I_9X) { /* 709X only */ + spill = op_frnd (); + if (spill) fp_trap (spill); + } + break; + + case 00012: /* DCT */ + if (ind_dvc) ind_dvc = 0; + else PC = (PC + 1) & AMASK; + break; + + case 00014: /* RCT */ + chtr_inhi = 1; /* 1 cycle delay */ + chtr_inht = 0; /* clr inhibit trap */ + chtr_pend = 0; /* no trap now */ + break; + + case 00016: /* LMTM */ + if (cpu_model & I_94) mode_multi = 0; /* 709X only */ + break; + + case 00140: /* SLF */ + if (cpu_model & I_9X) SLT = 0; /* 709X only */ + break; + + case 00141: case 00142: case 00143: case 00144: /* SLN */ + if (cpu_model & I_9X) /* 709X only */ + SLT = SLT | (1u << (00144 - addr)); + break; + + case 00161: case 00162: case 00163: /* SWT */ + case 00164: case 00165: case 00166: + if ((SSW & (1u << (00166 - addr))) != 0) + PC = (PC + 1) & AMASK; + break; + + case 01000: case 02000: case 03000: case 04000: /* BTT */ + case 05000: case 06000: case 07000: case 10000: + if (cpu_model & I_9X) { /* 709X only */ + if (sel_trap (PC)) break; /* sel trap? */ + ch = GET_U_CH (addr); /* get channel */ + if (ch_flags[ch] & CHF_BOT) /* BOT? */ + ch_flags[ch] &= ~CHF_BOT; /* clear */ + else PC = (PC + 1) & AMASK; /* else skip */ + } + break; + + case 001350: case 002350: case 003350: case 004350: /* RICx */ + case 005350: case 006350: case 007350: case 010350: + ch = GET_U_CH (addr); /* get channel */ + return ch_op_reset (ch, 1); + + case 001352: case 002352: case 003352: case 004352: /* RDCx */ + case 005352: case 006352: case 007352: case 010352: + ch = GET_U_CH (addr); /* get channel */ + return ch_op_reset (ch, 0); + } /* end case */ + +return SCPE_OK; +} + +/* Minus sense */ + +t_stat op_mse (uint32 addr) +{ +uint32 t, ch; + +switch (addr) { + + case 00000: /* CLM */ + if (cpu_model & I_9X) AC = AC & AC_S; /* 709X only */ + break; + + case 00001: /* PBT */ + if ((AC & AC_P) != 0) PC = (PC + 1) & AMASK; + break; + + case 00002: /* EFTM */ + if (cpu_model & I_9X) { /* 709X only */ + mode_ftrap = 1; + ind_mqo = 0; /* clears MQ ovf */ + } + break; + + case 00003: /* SSM */ + if (cpu_model & I_9X) AC = AC | AC_S; /* 709X only */ + break; + + case 00004: /* LFTM */ + if (cpu_model & I_9X) mode_ftrap = 0; /* 709X only */ + break; + + case 00005: /* ESTM */ + if (cpu_model & I_9X) mode_strap = 1; /* 709X only */ + break; + + case 00006: /* ECTM */ + if (cpu_model & I_9X) mode_ctrap = 1; /* 709X only */ + break; + + case 00007: /* LTM */ + if (cpu_model & I_9X) mode_ttrap = 0; /* 709X only */ + break; + + case 00010: /* LSNM */ + if (cpu_model & I_9X) mode_storn = 0; /* 709X only */ + break; + + case 00012: /* RTT (704) */ + if (cpu_model & I_9X) sel_trap (PC); /* 709X only */ + break; + + case 00016: /* EMTM */ + mode_multi = 1; + break; + + case 00140: /* SLF */ + if (cpu_model & I_9X) SLT = 0; /* 709X only */ + break; + + case 00141: case 00142: case 00143: case 00144: /* SLT */ + if (cpu_model & I_9X) { /* 709X only */ + t = SLT & (1u << (00144 - addr)); + SLT = SLT & ~t; + if (t != 0) PC = (PC + 1) & AMASK; + } + break; + + case 00161: case 00162: case 00163: /* SWT */ + case 00164: case 00165: case 00166: + if ((cpu_model & I_9X) && /* 709X only */ + ((SSW & (1u << (00166 - addr))) != 0)) + PC = (PC + 1) & AMASK; + break; + + case 001000: case 002000: case 003000: case 004000: /* ETT */ + case 005000: case 006000: case 007000: case 010000: + if (sel_trap (PC)) break; /* sel trap? */ + ch = GET_U_CH (addr); /* get channel */ + if (ch_flags[ch] & CHF_EOT) /* EOT? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOT; /* clear */ + else PC = (PC + 1) & AMASK; /* else skip */ + break; + } + +return SCPE_OK; +} + +/* Floating add + + Notes: + - AC enter into the initial exponent comparison. If either is set, + the numbers are always swapped. AC

gets OR'd into AC during the + swap, and AC are cleared afterwards + - The early end test is actually > 077 if AC <= SR and > 100 if + AC > SR. However, any shift >= 54 will produce a zero fraction, + so the difference can be ignored */ + +uint32 op_fad (t_uint64 sr, t_bool norm) +{ +UFP op1, op2, t; +int32 mqch, diff; + +MQ = 0; /* clear MQ */ +fp_unpack (AC, 0, 1, &op1); /* unpack AC */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr */ +if (op1.ch > op2.ch) { /* AC exp > SR exp? */ + if (AC & AC_P) op1.s = 1; /* AC P or's with S */ + t = op1; /* swap operands */ + op1 = op2; + op2 = t; + op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ + } +diff = op2.ch - op1.ch; /* exp diff */ +if (diff) { /* any shift? */ + if ((diff < 0) || (diff > 077)) op1.fr = 0; /* diff > 63? */ + else op1.fr = op1.fr >> diff; /* no, denormalize */ + } +if (op1.s ^ op2.s) { /* subtract? */ + if (op1.fr >= op2.fr) { /* op1 > op2? */ + op2.fr = op1.fr - op2.fr; /* op1 - op2 */ + op2.s = op1.s; /* op2 sign is result */ + } + else op2.fr = op2.fr - op1.fr; /* else op2 - op1 */ + } +else { + op2.fr = op2.fr + op1.fr; /* op2 + op1 */ + if (op2.fr & FP_FCRY) { /* carry? */ + op2.fr = op2.fr >> 1; /* renormalize */ + op2.ch++; /* incr exp */ + } + } +if (norm) { /* normalize? */ + if (op2.fr) { /* non-zero frac? */ + fp_norm (&op2); + mqch = op2.ch - FP_N_FR; + } + else op2.ch = mqch = 0; /* else true zero */ + } +else mqch = op2.ch - FP_N_FR; +return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ +} + +/* Floating multiply */ + +uint32 op_fmp (t_uint64 sr, t_bool norm) +{ +UFP op1, op2; +int32 mqch; +uint32 f1h, f2h; + +fp_unpack (MQ, 0, 0, &op1); /* unpack MQ */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr */ +op1.s = op1.s ^ op2.s; /* result sign */ +if ((op2.ch == 0) && (op2.fr == 0)) { /* sr a normal 0? */ + AC = op1.s? AC_S: 0; /* result is 0 */ + MQ = op1.s? SIGN: 0; + return 0; + } +f1h = FP_HIFRAC (op1.fr); /* get hi fracs */ +f2h = FP_HIFRAC (op2.fr); +op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* f1h * f2h */ +op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ +if (norm) { /* normalize? */ + if (!(op1.fr & FP_FNORM)) { /* not normalized? */ + op1.fr = op1.fr << 1; /* shift frac left 1 */ + op1.ch--; /* decr exp */ + } + if (FP_HIFRAC (op1.fr)) /* hi result non-zero? */ + mqch = op1.ch - FP_N_FR; /* set MQ exp */ + else op1.ch = mqch = 0; /* clear AC, MQ exp */ + } +else mqch = op1.ch - FP_N_FR; /* set MQ exp */ +return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ +} + +/* Floating divide */ + +uint32 op_fdv (t_uint64 sr) +{ +UFP op1, op2; +int32 mqch; +uint32 spill, quos; +t_uint64 rem; + +fp_unpack (AC, 0, 1, &op1); /* unpack AC */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr */ +quos = op1.s ^ op2.s; /* quotient sign */ +if (op1.fr >= (2 * op2.fr)) { /* |AC| >= 2*|sr|? */ + MQ = quos? SIGN: 0; /* MQ = sign only */ + return TRAP_F_DVC; /* divide check */ + } +if (op1.fr == 0) { /* |AC| == 0? */ + MQ = quos? SIGN: 0; /* MQ = sign only */ + AC = 0; /* AC = +0 */ + return 0; /* done */ + } +op1.ch = op1.ch & FP_M_CH; /* remove AC */ +if (op1.fr >= op2.fr) { /* |AC| >= |sr|? */ + op1.fr = op1.fr >> 1; /* denorm AC */ + op1.ch++; + } +op1.fr = fp_fracdiv (op1.fr, op2.fr, &rem); /* fraction divide */ +op1.fr = op1.fr | (rem << FP_N_FR); /* rem'quo */ +mqch = op1.ch - op2.ch + FP_BIAS; /* quotient exp */ +op1.ch = op1.ch - FP_N_FR; /* remainder exp */ +spill = fp_pack (&op1, quos, mqch); /* pack up */ +return (spill? (spill | TRAP_F_SGL): 0); /* if spill, set SGL */ +} + +/* Double floating add + + Notes: + - AC enter into the initial exponent comparison. If either is set, + the numbers are always swapped. AC

gets OR'd into AC during the + swap, and AC are cleared afterwards + - For most cases, SI ends up with the high order part of the larger number + - The 'early end' cases (smaller number is shifted away) must be tracked + exactly for SI impacts. The early end cases are: + + (a) AC > SR, diff > 0100, and AC normalized + (b) AC <= SR, diff > 077, and SR normalized + + In case (a), SI is unchanged. In case (b), SI ends up with the SR sign + and characteristic but the MQ (!) fraction */ + +uint32 op_dfad (t_uint64 sr, t_uint64 sr1, t_bool norm) +{ +UFP op1, op2, t; +int32 mqch, diff; + +fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ +fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ +if (op1.ch > op2.ch) { /* AC exp > SR exp? */ + if (((op1.ch - op2.ch) > 0100) && (AC & B9)) ; /* early out */ + else SI = FP_PACK36 (op1.s, op1.ch, FP_HIFRAC (op1.fr)); + if (AC & AC_P) op1.s = 1; /* AC P or's with S */ + t = op1; /* swap operands */ + op1 = op2; + op2 = t; + op2.ch = op2.ch & FP_M_CH; /* clear P,Q */ + } +else { /* AC <= SR */ + if (((op2.ch - op1.ch) > 077) && (sr & B9)) /* early out */ + SI = FP_PACK36 (op2.s, op2.ch, FP_LOFRAC (MQ)); + else SI = FP_PACK36 (op2.s, op2.ch, FP_HIFRAC (op2.fr)); + } +diff = op2.ch - op1.ch; /* exp diff */ +if (diff) { /* any shift? */ + if ((diff < 0) || (diff > 077)) op1.fr = 0; /* diff > 63? */ + else op1.fr = op1.fr >> diff; /* no, denormalize */ + } +if (op1.s ^ op2.s) { /* subtract? */ + if (op1.fr >= op2.fr) { /* op1 > op2? */ + op2.fr = op1.fr - op2.fr; /* op1 - op2 */ + op2.s = op1.s; /* op2 sign is result */ + } + else op2.fr = op2.fr - op1.fr; /* op2 - op1 */ + } +else { + op2.fr = op2.fr + op1.fr; /* op2 + op1 */ + if (op2.fr & FP_FCRY) { /* carry? */ + op2.fr = op2.fr >> 1; /* renormalize */ + op2.ch++; /* incr exp */ + } + } +if (norm) { /* normalize? */ + if (op2.fr) { /* non-zero frac? */ + fp_norm (&op2); + mqch = op2.ch - FP_N_FR; + } + else op2.ch = mqch = 0; /* else true zero */ + } +else mqch = op2.ch - FP_N_FR; +return fp_pack (&op2, op2.s, mqch); /* pack AC, MQ */ +} + +/* Double floating multiply + + Notes (notation is A+B' * C+D', where ' denotes 2^-27): + - The instruction returns 0 if A and C are both zero, because B*D is never + done as part of the algorithm + - For most cases, SI ends up with B*C, with a zero sign and exponent + - For the A+B' both zero 'early end' case SI ends up with A or C, + depending on whether the operation is normalized or not */ + +uint32 op_dfmp (t_uint64 sr, t_uint64 sr1, t_bool norm) +{ +UFP op1, op2; +int32 mqch; +uint32 f1h, f2h, f1l, f2l; +t_uint64 tx; + +fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ +fp_unpack (sr, sr1, 0, &op2); /* unpack sr'sr1 */ +op1.s = op1.s ^ op2.s; /* result sign */ +f1h = FP_HIFRAC (op1.fr); /* A */ +f1l = FP_LOFRAC (op1.fr); /* B */ +f2h = FP_HIFRAC (op2.fr); /* C */ +f2l = FP_LOFRAC (op2.fr); /* D */ +if (((op1.ch == 0) && (op1.fr == 0)) || /* AC'MQ normal 0? */ + ((op2.ch == 0) && (op2.fr == 0)) || /* sr'sr1 normal 0? */ + ((f1h == 0) && (f2h == 0))) { /* both hi frac zero? */ + AC = op1.s? AC_S: 0; /* result is 0 */ + MQ = op1.s? SIGN: 0; + SI = sr; /* SI has C */ + return 0; + } +op1.ch = (op1.ch & FP_M_CH) + op2.ch - FP_BIAS; /* result exponent */ +if (op1.fr) { /* A'B != 0? */ + op1.fr = ((t_uint64) f1h) * ((t_uint64) f2h); /* A * C */ + tx = ((t_uint64) f1h) * ((t_uint64) f2l); /* A * D */ + op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ + tx = ((t_uint64) f1l) * ((t_uint64) f2h); /* B * C */ + op1.fr = op1.fr + (tx >> FP_N_FR); /* add in hi 27b */ + SI = tx >> FP_N_FR; /* SI keeps B * C */ + } +else { + if (norm) SI = sr; /* early out */ + else SI = FP_PACK36 (op2.s, op2.ch, 0); + } +if (norm) { /* normalize? */ + if (!(op1.fr & FP_FNORM)) { /* not normalized? */ + op1.fr = op1.fr << 1; /* shift frac left 1 */ + op1.ch--; /* decr exp */ + } + if (FP_HIFRAC (op1.fr)) { /* non-zero? */ + mqch = op1.ch - FP_N_FR; /* set MQ exp */ + } + else op1.ch = mqch = 0; /* clear AC, MQ exp */ + } +else mqch = op1.ch - FP_N_FR; /* set MQ exp */ +return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ +} + +/* Double floating divide + + + Notes: + - This is a Taylor series expansion (where ' denotes >> 27): + + (A+B') * (C+D')^-1 = (A+B') * C^-1 - (A+B') * D'* C^-2 +... + + to two terms, which can be rewritten as terms Q1, Q2: + + Q1 = (A+B')/C + Q2' = (R - Q1*D)'/C + + - Tracking the sign of Q2' is complicated: + + Q1 has the sign of the quotient, s_AC ^ s_SR + D has the sign of the divisor, s_SR + R has the sign of the dividend, s_AC + Q1*D sign is s_AC ^ s_SR ^ s^SR = s^AC + Therefore, R and Q1*D have the same sign, s_AC + Q2' sign is s^AC ^ s_SR, which is the sign of the quotient + + - For first divide check, SI is 0 + - For other cases, including second divide check, SI ends up with Q1 + - R-Q1*D is only calculated to the high 27b; using the full 54b + throws off the result + - The second divide must check for divd >= divr, otherwise an extra + bit of quotient would be devloped, throwing off the result + - A late ECO added full post-normalization; single precision divide + does no normalization */ + +uint32 op_dfdv (t_uint64 sr, t_uint64 sr1) +{ +UFP op1, op2; +int32 mqch; +uint32 csign, ac_s; +t_uint64 f1h, f2h, tr, tq1, tq1d, trmq1d, tq2; + +fp_unpack (AC, MQ, 1, &op1); /* unpack AC'MQ */ +fp_unpack (sr, 0, 0, &op2); /* unpack sr only */ +ac_s = op1.s; /* save AC sign */ +op1.s = op1.s ^ op2.s; /* sign of result */ +f1h = FP_HIFRAC (op1.fr); +f2h = FP_HIFRAC (op2.fr); +if (f1h >= (2 * f2h)) { /* |A| >= 2*|C|? */ + SI = 0; /* clear SI */ + return TRAP_F_DVC; /* divide check */ + } +if (f1h == 0) { /* |AC| == 0? */ + SI = MQ = op1.s? SIGN: 0; /* MQ, SI = sign only */ + AC = op1.s? AC_S: 0; /* AC = sign only */ + return 0; /* done */ + } +op1.ch = op1.ch & FP_M_CH; /* remove AC */ +if (f1h >= f2h) { /* |A| >= |C|? */ + op1.fr = op1.fr >> 1; /* denorm AC */ + op1.ch++; + } +op1.ch = op1.ch - op2.ch + FP_BIAS; /* exp of quotient */ +tq1 = fp_fracdiv (op1.fr, op2.fr, &tr); /* |A+B| / |C| */ +tr = tr << FP_N_FR; /* R << 27 */ +tq1d = (tq1 * ((t_uint64) FP_LOFRAC (sr1))) & /* Q1 * D */ + ~((t_uint64) FP_FMASK); /* top 27 bits */ +csign = (tr < tq1d); /* correction sign */ +if (csign) trmq1d = tq1d - tr; /* |R|<|Q1*D|? compl */ +else trmq1d = tr - tq1d; /* no, subtr ok */ +SI = FP_PACK36 (op1.s, op1.ch, tq1); /* SI has Q1 */ +if (trmq1d >= (2 * op2.fr)) { /* |R-Q1*D| >= 2*|C|? */ + AC = FP_PACK38 (csign ^ ac_s, 0, FP_HIFRAC (trmq1d)); /* AC has R-Q1*D */ + MQ = (csign ^ ac_s)? SIGN: 0; /* MQ = sign only */ + return TRAP_F_DVC; /* divide check */ + } +tq2 = fp_fracdiv (trmq1d, op2.fr, NULL); /* |R-Q1*D| / |C| */ +if (trmq1d >= op2.fr) tq2 &= ~((t_uint64) 1); /* can only gen 27b quo */ +op1.fr = tq1 << FP_N_FR; /* shift Q1 into place */ +if (csign) op1.fr = op1.fr - tq2; /* sub or add Q2 */ +else op1.fr = op1.fr + tq2; +fp_norm (&op1); /* normalize */ +if (op1.fr) mqch = op1.ch - FP_N_FR; /* non-zero? */ +else op1.ch = mqch = 0; /* clear AC, MQ exp */ +return fp_pack (&op1, op1.s, mqch); /* pack AC, MQ */ +} + +/* Floating round */ + +uint32 op_frnd (void) +{ +UFP op; +uint32 spill; + +spill = 0; /* no error */ +if (MQ & B9) { /* MQ9 set? */ + fp_unpack (AC, 0, 1, &op); /* unpack AC */ + op.fr = op.fr + ((t_uint64) (1 << FP_N_FR)); /* round up */ + if (op.fr & FP_FCRY) { /* carry out? */ + op.fr = op.fr >> 1; /* renormalize */ + op.ch++; /* incr exp */ + if (op.ch == (FP_M_CH + 1)) /* ovf with QP = 0? */ + spill = TRAP_F_OVF | TRAP_F_AC; + } + AC = FP_PACK38 (op.s, op.ch, FP_HIFRAC (op.fr)); /* pack AC */ + } +return spill; +} + +/* Fraction divide - 54/27'0 yielding quotient and remainder */ + +t_uint64 fp_fracdiv (t_uint64 dvd, t_uint64 dvr, t_uint64 *rem) +{ +dvr = dvr >> FP_N_FR; +if (rem) *rem = dvd % dvr; +return (dvd / dvr); +} + +/* Floating point normalize */ + +void fp_norm (UFP *op) +{ +op->fr = op->fr & FP_DFMASK; /* mask fraction */ +if (op->fr == 0) return; /* zero? */ +while ((op->fr & FP_FNORM) == 0) { /* until norm */ + op->fr = op->fr << 1; /* lsh 1 */ + op->ch--; /* decr exp */ + } +return; +} + +/* Floating point unpack */ + +void fp_unpack (t_uint64 h, t_uint64 l, t_bool q_ac, UFP *op) +{ +if (q_ac) { /* AC? */ + op->s = (h & AC_S)? 1: 0; /* get sign */ + op->ch = (uint32) ((h >> FP_V_CH) & FP_M_ACCH); /* get exp */ + } +else { + op->s = (h & SIGN)? 1: 0; /* no, mem */ + op->ch = (uint32) ((h >> FP_V_CH) & FP_M_CH); + } +op->fr = (((t_uint64) FP_LOFRAC (h)) << FP_N_FR) | /* get frac hi */ + ((t_uint64) FP_LOFRAC (l)); /* get frac lo */ +return; +} + +/* Floating point pack */ + +uint32 fp_pack (UFP *op, uint32 mqs, int32 mqch) +{ +uint32 spill; + +AC = FP_PACK38 (op->s, op->ch, FP_HIFRAC (op->fr)); /* pack AC */ +MQ = FP_PACK36 (mqs, mqch, FP_LOFRAC (op->fr)); /* pack MQ */ +if (op->ch > FP_M_CH) spill = TRAP_F_OVF | TRAP_F_AC; /* check AC exp */ +else if (op->ch < 0) spill = TRAP_F_AC; +else spill = 0; +if (mqch > FP_M_CH) spill |= (TRAP_F_OVF | TRAP_F_MQ); /* check MQ exp */ +else if (mqch < 0) spill |= TRAP_F_MQ; +return spill; +} diff --git a/I7094/i7094_dat.h b/I7094/i7094_dat.h new file mode 100644 index 0000000..4d86ecb --- /dev/null +++ b/I7094/i7094_dat.h @@ -0,0 +1,152 @@ +/* i7094_dat.h: IBM 7094 data conversion tables + + Copyright (c) 2003-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +/* Nine-code to ASCII conversion */ + +const char nine_to_ascii_a[64] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '^', '#', '@', ':', '>', '{', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '}', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '|', ',', '%', '~', '\\', '"', + }; + +const char nine_to_ascii_h[64] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '^', '=', '\'', ':', '>', '{', + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '}', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + ' ', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '|', ',', '(', '~', '\\', '"', + }; + +/* ASCII to nine-code conversion */ + +const char ascii_to_nine[128] = { + 060, 060, 060, 060, 060, 060, 060, 060, /* 000 - 037 */ + 060, 060, 060, 060, 060, 060, 060, 060, + 060, 060, 060, 060, 060, 060, 060, 060, + 060, 060, 060, 060, 060, 060, 060, 060, + 060, 052, 077, 013, 053, 074, 020, 014, /* 040 - 077 */ + 074, 034, 054, 020, 073, 040, 033, 061, + 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 015, 056, 036, 013, 016, 032, + 014, 021, 022, 023, 024, 025, 026, 027, /* 100 - 137 */ + 030, 031, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 062, 063, 064, 065, 066, + 067, 070, 071, 035, 076, 055, 012, 057, + 060, 021, 022, 023, 024, 025, 026, 027, /* 140 - 177 */ + 030, 031, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 062, 063, 064, 065, 066, + 067, 070, 071, 017, 072, 037, 075, 060 + }; + +/* ASCII to BCD conversion */ + +const char ascii_to_bcd[128] = { + 000, 000, 000, 000, 000, 000, 000, 000, /* 000 - 037 */ + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 000, 000, 000, 000, 000, 000, 000, + 000, 052, 037, 013, 053, 074, 060, 014, /* 040 - 077 */ + 034, 074, 054, 060, 033, 040, 073, 021, + 020, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 015, 056, 076, 013, 016, 072, + 014, 061, 062, 063, 064, 065, 066, 067, /* 100 - 137 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 075, 036, 055, 012, 057, + 000, 061, 062, 063, 064, 065, 066, 067, /* 140 - 177 */ + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 017, 032, 077, 035, 000 + }; + +/* BCD to ASCII conversion */ + +const char bcd_to_ascii_a[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '^', '#', '@', ':', '>', '{', + '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '|', ',', '%', '~', '\\', '"', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '}' + }; + +const char bcd_to_ascii_h[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', '^', '=', '\'', ':', '>', '{', + '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '|', ',', '(', '~', '\\', '"', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '_', + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '}' + }; + +/* BCD to ASCII 48 character print chains */ + +const char bcd_to_pca[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ' ', '#', '@', ' ', ' ', ' ', + '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', ' ', ',', '%', ' ', ' ', ' ', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '&', '.', ')', ' ', ' ', ' ' + }; + +const char bcd_to_pch[64] = { + ' ', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ' ', '=', '\'', ' ', ' ', ' ', + '0', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', ' ', ',', '(', ' ', ' ', ' ', + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '-', '$', '*', ' ', ' ', ' ', + '&', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '&', '.', ')', ' ', ' ', ' ' + }; + +/* BCD to column binary conversion */ + +const uint32 bcd_to_colbin[64] = { + 00000, 00400, 00200, 00100, 00040, 00020, 00010, 00004, + 00002, 00001, 00202, 00102, 00042, 00022, 00012, 00006, + 01000, 01400, 01200, 01100, 01040, 01020, 01010, 01004, + 01002, 01001, 01202, 01102, 01042, 01022, 01012, 01006, + 02000, 02400, 02200, 02100, 02040, 02020, 02010, 02004, + 02002, 02001, 02202, 02102, 02042, 02022, 02012, 02006, + 04000, 04400, 04200, 04100, 04040, 04020, 04010, 04004, + 04002, 04001, 04202, 04102, 04042, 04022, 04012, 04006 + }; diff --git a/I7094/i7094_defs.h b/I7094/i7094_defs.h new file mode 100644 index 0000000..8eff876 --- /dev/null +++ b/I7094/i7094_defs.h @@ -0,0 +1,474 @@ +/* i7094_defs.h: IBM 7094 simulator definitions + + Copyright (c) 2003-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This simulator incorporates prior work by Paul Pierce, Dave Pitts, and Rob + Storey. Tom Van Vleck, Stan Dunten, Jerry Saltzer, and other CTSS veterans + helped to reconstruct the CTSS hardware RPQ's. Dave Pitts gets special + thanks for patiently coaching me through IBSYS debug. */ + +#ifndef _I7094_DEFS_H_ +#define _I7094_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_HALT 1 /* halted */ +#define STOP_IBKPT 2 /* breakpoint */ +#define STOP_ILLEG 3 /* illegal instr */ +#define STOP_DIVCHK 4 /* divide check */ +#define STOP_XEC 5 /* XCT loop */ +#define STOP_ASTOP 6 /* address stop */ +#define STOP_NXCHN 7 /* nx channel */ +#define STOP_7909 8 /* ill inst to 7909 */ +#define STOP_NT7909 9 /* ill inst to !7909 */ +#define STOP_NXDEV 10 /* nx device */ +#define STOP_ILLCHI 11 /* illegal channel op */ +#define STOP_WRP 12 /* write protect */ +#define STOP_ILLIOP 13 /* illegal I/O op */ +#define STOP_INVFMT 14 /* invalid disk format */ +#define STOP_NOIFREE 15 /* 7750: no buf for inp */ +#define STOP_NOOFREE 16 /* 7750: no buf for out */ +#define STOP_INVLIN 17 /* 7750: invalid line# */ +#define STOP_INVMSG 18 /* 7750: invalid message */ +#define STOP_CHBKPT 19 /* channel breakpoint */ + +/* Simulator error codes */ + +#define ERR_STALL 40 /* stall */ +#define ERR_ENDRC 41 /* end rec */ +#define ERR_NRCF 42 /* no record found */ + +/* Instruction history - flags in left half of pc entry */ + +#define HIST_PC 0x04000000 /* CPU */ +#define HIST_V_CH 28 /* chan + 1 */ +#define HIST_M_CH 0xF +#define HIST_CH(x) (((x) >> HIST_V_CH) & HIST_M_CH) + +typedef struct { + uint32 pc; + uint32 ea; + uint32 rpt; + t_uint64 ir; + t_uint64 ac; + t_uint64 mq; + t_uint64 si; + t_uint64 opnd; + } InstHistory; + +/* Architectural constants */ + +#define A704_SIZE 14 /* addr width, 704 mode */ +#define ASIZE 15 /* inst addr width */ +#define PASIZE 16 /* phys addr width */ +#define STDMEMSIZE (1u << ASIZE) /* standard memory */ +#define MAXMEMSIZE (1u << PASIZE) /* maximum memory */ +#define A704_MASK ((1u << A704_SIZE) - 1) +#define PAMASK ((1u << PASIZE) - 1) +#define MEMSIZE (cpu_unit.capac) +#define BCORE_V (ASIZE) /* (CTSS) A/B core sel */ +#define BCORE_BASE (1u << BCORE_V) /* (CTSS) B core base */ + +/* Traps */ + +#define TRAP_STD_SAV 000000 /* trap save location */ +#define TRAP_TRA_PC 000001 /* trap PC: transfer */ +#define TRAP_STR_PC 000002 /* trap PC: STR */ +#define TRAP_FP_PC 000010 /* trap PC: flt point */ +#define TRAP_PROT_SAV 000032 /* protection trap save */ +#define TRAP_PROT_PC 000033 /* protection trap PC */ +#define TRAP_704_SAV 040000 /* 704 compat trap */ +#define TRAP_SEL_PC 040001 /* 704 trap PC: select */ +#define TRAP_CPY_PC 040002 /* 704 trap PC: copy */ + +#define TRAP_F_MQ 000001 /* MQ error */ +#define TRAP_F_AC 000002 /* AC error */ +#define TRAP_F_OVF 000004 /* overflow */ +#define TRAP_F_SGL 000010 /* single precision */ +#define TRAP_F_DVC 000020 /* fake: divide check */ +#define TRAP_F_ODD 000040 /* odd address */ +#define TRAP_F_BDATA 020000 /* (CTSS) data B core */ +#define TRAP_F_BINST 040000 /* (CTSS) inst B core */ + +/* Integer */ + +#define DMASK 0777777777777 /* data mask */ +#define SIGN 0400000000000 /* sign */ +#define MMASK 0377777777777 /* magnitude mask */ +#define LMASK 0777777000000 /* left mask */ +#define RMASK 0000000777777 /* right mask */ +#define PMASK 0700000000000 /* prefix */ +#define XMASK 0077777000000 /* decrement */ +#define TMASK 0000000700000 /* tag */ +#define AMASK 0000000077777 /* address */ +#define SCMASK 0000000000377 /* shift count mask */ +#define B1 0200000000000 /* bit 1 */ +#define B9 0000400000000 /* bit 9 */ + +/* Accumulator is actually 38b wide */ + +#define AC_S 02000000000000 /* sign */ +#define AC_Q 01000000000000 /* Q */ +#define AC_P 00400000000000 /* P */ +#define AC_MMASK 01777777777777 /* Q+P+magnitude */ + +/* Floating point */ + +#define FP_N_FR 27 /* fraction bits */ +#define FP_FMASK ((1u << FP_N_FR) - 1) +#define FP_N_DFR 54 /* double fraction bits */ +#define FP_DFMASK ((((t_uint64) 1) << FP_N_DFR) - 1) +#define FP_FNORM (((t_uint64) 1u) << (FP_N_DFR - 1)) /* normalized bit */ +#define FP_FCRY (((t_uint64) 1u) << FP_N_DFR) /* fraction carry */ +#define FP_BIAS 0200 /* exponent bias */ +#define FP_V_CH (FP_N_FR) /* exponent */ +#define FP_M_CH 0377 /* SR char mask */ +#define FP_M_ACCH 01777 /* AC char mask incl Q,P */ + +/* Instruction format */ + +#define INST_T_DEC 0300000000000 /* if nz, takes decr */ +#define INST_T_CXR1 0000000100000 /* if nz, update XR1 */ +#define INST_V_OPD 33 /* decrement opcode */ +#define INST_M_OPD 07 +#define INST_V_DEC 18 /* decrement */ +#define INST_M_DEC 077777 +#define INST_V_OPC 24 /* normal opcode */ +#define INST_M_OPC 0777 +#define INST_V_IND 22 /* indirect */ +#define INST_IND (3 << INST_V_IND) +#define INST_V_CCNT 18 /* convert count */ +#define INST_M_CCNT 0377 +#define INST_V_VCNT 18 /* vlm/vdh count */ +#define INST_M_VCNT 077 +#define INST_V_TAG 15 /* index */ +#define INST_M_TAG 07 +#define INST_V_ADDR 0 +#define INST_M_ADDR 077777 + +#define GET_OPD(x) ((uint32) (((x) >> INST_V_OPD) & INST_M_OPD)) +#define GET_DEC(x) ((uint32) (((x) >> INST_V_DEC) & INST_M_DEC)) +#define GET_OPC(x) (((uint32) (((x) >> INST_V_OPC) & INST_M_OPC)) | \ + (((x) & SIGN)? 01000: 0)) +#define TST_IND(x) (((x) & INST_IND) == INST_IND) +#define GET_CCNT(x) ((uint32) (((x) >> INST_V_CCNT) & INST_M_CCNT)) +#define GET_VCNT(x) ((uint32) (((x) >> INST_V_VCNT) & INST_M_VCNT)) +#define GET_TAG(x) ((uint32) (((x) >> INST_V_TAG) & INST_M_TAG)) + +/* Instruction decode flags */ + +#define I_4X 0x01 /* 7040, 7044 */ +#define I_9X 0x02 /* 7090, 7094, CTSS */ +#define I_94 0x04 /* 7094, CTSS */ +#define I_CT 0x08 /* CTSS */ +#define I_MODEL 0x0F /* option mask */ +#define I_X 0x10 /* indexed */ +#define I_N 0x20 /* indirect */ +#define I_R 0x40 /* read */ +#define I_D 0x80 /* double read */ + +#define I_XN (I_X|I_N) +#define I_XNR (I_X|I_N|I_R) +#define I_XND (I_X|I_N|I_D) + +/* Memory protection (CTSS) */ + +#define VA_V_OFF 0 /* offset in block */ +#define VA_N_OFF 8 /* width of offset */ +#define VA_M_OFF ((1u << VA_N_OFF) - 1) +#define VA_OFF (VA_M_OFF << VA_V_OFF) +#define VA_V_BLK (VA_N_OFF) /* block */ +#define VA_N_BLK (ASIZE - VA_N_OFF) /* width of block */ +#define VA_M_BLK ((1u << VA_N_BLK) - 1) +#define VA_BLK (VA_M_BLK << VA_V_BLK) + +/* Unsigned operations */ + +#define NEG(x) (~(x) + 1) +#define BIT_TST(w,b) (((w) >> (b)) & 1) + +/* Device information block */ + +typedef struct { + t_stat (*chsel)(uint32 ch, uint32 sel, uint32 u); + t_stat (*write)(uint32 ch, t_uint64 val, uint32 flags); + } DIB; + +/* BCD digits */ + +#define BCD_MASK 017 +#define BCD_ZERO 012 +#define BCD_ONE 001 +#define BCD_TWO 002 +#define BCD_AT 014 + +/* Channels */ + +#define NUM_CHAN 8 /* # channels */ +#define CH_A 0 /* channel A */ +#define CH_B 1 +#define CH_C 2 +#define CH_D 3 +#define CH_E 4 +#define CH_F 5 +#define CH_G 6 +#define CH_H 7 + +#define REQ_CH(x) (1u << (x)) + +/* All channel commands */ + +#define CHI_IND 0000000400000 /* ch inst indirect */ + +/* Channel selects - all channels */ + +#define CHSL_RDS 0001 /* data selects */ +#define CHSL_WRS 0002 +#define CHSL_SNS 0003 +#define CHSL_CTL 0004 +#define CHSL_FMT 0005 +#define CHSL_WEF 0010 /* non-data selects */ +#define CHSL_WBT 0011 /* 704X only */ +#define CHSL_BSR 0012 +#define CHSL_BSF 0013 +#define CHSL_REW 0014 +#define CHSL_RUN 0015 +#define CHSL_SDN 0016 +#define CHSL_2ND 0020 /* second state */ +#define CHSL_3RD 0040 /* etc */ +#define CHSL_4TH 0060 +#define CHSL_5TH 0100 +#define CHSL_NDS 0010 /* non-data sel flag */ +#define CHSL_NUM 16 + +/* Channel commands - 7607/7289 - S12'19 */ + +#define CH6I_NST 0000000200000 /* ch inst no store */ + +#define CH6_IOCD 000 +#define CH6_TCH 002 +#define CH6_IORP 004 +#define CH6_IORT 006 +#define CH6_IOCP 010 +#define CH6_IOCT 012 +#define CH6_IOSP 014 +#define CH6_IOST 016 +#define CH6_OPMASK 016 /* without nostore */ +#define TCH_LIMIT 5 /* TCH autoresolve limit */ + +/* Channel data flags - 7607 */ + +#define CH6DF_EOR 1 /* end of record */ +#define CH6DF_VLD 2 /* input valid */ + +/* Channel commands - 7909 - S123'19 */ + +#define CH9_WTR 000 +#define CH9_XMT 001 +#define CH9_TCH 004 +#define CH9_LIPT 005 +#define CH9_CTL 010 +#define CH9_CTLR 011 +#define CH9_CTLW 012 +#define CH9_SNS 013 +#define CH9_LAR 014 +#define CH9_SAR 015 +#define CH9_TWT 016 +#define CH9_CPYP 020 +#define CH9_CPYD 024 +#define CH9_TCM 025 +#define CH9_LIP 031 +#define CH9_TDC 032 +#define CH9_LCC 033 +#define CH9_SMS 034 +#define CH9_ICC 035 +#define CH9_ICCA 037 /* ignores bit <3> */ +#define CH9_OPMASK 037 + +/* Channel data flags - 7909 */ + +#define CH9DF_STOP 1 /* stop */ +#define CH9DF_VLD 2 /* input valid */ + +/* Extended parts of the command come from the decrement, stored in ch_wc */ + +#define CH9D_V_MASK 0 /* condition mask */ +#define CH9D_M_MASK 077 +#define CH9D_V_COND 12 /* condition select */ +#define CH9D_M_COND 07 +#define CH9D_MASK(x) (((x) >> CH9D_V_MASK) & CH9D_M_MASK) +#define CH9D_COND(x) (((x) >> CH9D_V_COND) & CH9D_M_COND) + +#define CH9D_NST 020000 /* no store */ +#define CH9D_B11 000100 + +/* Or from the effective address, stored in ch_ca */ + +#define CH9A_V_LCC 0 /* counter */ +#define CH9A_M_LCC 077 +#define CH9A_V_SMS 0 /* system mask */ +#define CH9A_M_SMS 0177 +#define CH9A_LCC(x) (((x) >> CH9A_V_LCC) & CH9A_M_LCC) +#define CH9A_SMS(x) (((x) >> CH9A_V_SMS) & CH9A_M_SMS) + +/* Channel states - common */ + +#define CHXS_IDLE 0 /* idle */ +#define CHXS_DSX 1 /* executing */ + +/* Channel states - 7607/7289 */ + +#define CH6S_PNDS 2 /* polling NDS */ +#define CH6S_PDS 3 /* polling DS */ +#define CH6S_NDS 4 /* nds, executing */ +#define CH6S_DSW 5 /* ds, chan wait */ + +/* Channel traps - 7909 has only CMD (== TWT) */ + +#define CHTR_V_CME 0 /* cmd/eof enable */ +#define CHTR_V_CLK 17 /* clock */ +#define CHTR_V_TRC 18 /* tape check */ +#define CHTR_V_TWT (CHTR_V_CME) +#define CHTR_CLK_SAV 006 /* clock */ +#define CHTR_CHA_SAV 012 /* start of chan block */ +#define CHTR_F_CMD 1 /* CMD flag (in decr) */ +#define CHTR_F_TRC 2 /* TRC flag (in decr) */ +#define CHTR_F_EOF 4 /* EOF flag (in decr) */ + +/* Channel interrupts - 7909 only */ + +#define CHINT_CHA_SAV 042 /* start of chan block */ + +/* Channel interrupt conditions - 7909 only */ + +#define CHINT_ADPC 001 /* adapter check */ +#define CHINT_ATN2 002 /* attention 2 - ni */ +#define CHINT_ATN1 004 /* attention 1 */ +#define CHINT_UEND 010 /* unusual end */ +#define CHINT_SEQC 020 /* sequence check */ +#define CHINT_IOC 040 /* IO check */ + +/* Channel SMS flags - 7909 only */ + +#define CHSMS_SEL2 0001 /* select 2nd - ni */ +#define CHSMS_IATN2 0002 /* inhibit atn2 - ni */ +#define CHSMS_IATN1 0004 /* inhibit atn1 */ +#define CHSMS_IUEND 0010 /* inhibit uend */ +#define CHSMS_BCD 0020 /* BCD conversion - ni */ +#define CHSMS_RBCK 0040 /* read backwards - ni */ +#define CHSMS_ENCI 0100 /* enable noncon - ni */ + +/* Channel flags (7607 in right half, 7909 in left half) */ + +#define CHF_CMD 00000000001 /* cmd done */ +#define CHF_TWT (CHF_CMD) +#define CHF_TRC 00000000002 /* tape check */ +#define CHF_EOF 00000000004 /* end of file */ +#define CHF_BOT 00000000010 /* beginning of tape */ +#define CHF_EOT 00000000020 /* end of tape */ +#define CHF_LDW 00000000040 /* LCH waiting */ +#define CHF_EOR 00000000100 /* end of record */ +#define CHF_IRQ 00001000000 /* intr request */ +#define CHF_INT 00002000000 /* intr in prog */ +#define CHF_WRS 00004000000 /* write */ +#define CHF_RDS 00010000000 /* read */ +#define CHF_PWR 00020000000 /* prepare to write */ +#define CHF_PRD 00040000000 /* prepare to read */ +#define CHF_V_COND 24 /* cond register */ +#define CHF_M_COND 077 +#define CHF_ADPC (CHINT_ADPC << CHF_V_COND) /* adapter check */ +#define CHF_ATN2 (CHINT_ATN2 << CHF_V_COND) /* attention 2 */ +#define CHF_ATN1 (CHINT_ATN1 << CHF_V_COND) /* attention 1 */ +#define CHF_UEND (CHINT_UEND << CHF_V_COND) /* unusual end */ +#define CHF_SEQC (CHINT_SEQC << CHF_V_COND) /* sequence check */ +#define CHF_IOC (CHINT_IOC << CHF_V_COND) /* IO check */ +#define CHF_V_LCC 30 /* loop ctrl counter */ +#define CHF_M_LCC 077 + +#define CHF_CLR_7909 07775000177 /* 7909 clear flags */ +#define CHF_SDC_7909 07776000000 /* 7909 SDC flags */ + +/* Channel characteristics (in dev.flags) */ + +#define DEV_7909 (1u << (DEV_V_UF + 0)) +#define DEV_7289 (1u << (DEV_V_UF + 1)) +#define DEV_CDLP (1u << (DEV_V_UF + 2)) +#define DEV_7750 (1u << (DEV_V_UF + 3)) +#define DEV_7631 (1u << (DEV_V_UF + 4)) + +/* Unit addresses - 7607/7289 only */ + +#define U_V_CH 9 /* channel number */ +#define U_M_CH 077 +#define U_V_UNIT 0 +#define U_M_UNIT 0777 +#define GET_U_CH(x) (((((uint32) (x)) >> U_V_CH) & U_M_CH) - 1) +#define GET_U_UNIT(x) ((((uint32) (x)) >> U_V_UNIT) & U_M_UNIT) + +#define U_MTBCD 0201 /* BCD tape */ +#define U_MTBIN 0221 /* binary tape */ +#define U_CDR 0321 /* card reader */ +#define U_CDP 0341 /* card punch */ +#define U_LPBCD 0361 /* BCD print */ +#define U_LPBIN 0362 /* binary print */ +#define U_DRM 0330 /* 7320A drum */ + +#define MT_NUMDR 10 + +/* CTSS Chronolog clock */ + +#define CHRONO_CH (CH_A) /* channel A */ +#define CHRONO_UNIT (7) /* unit 7 */ + +/* Interval timer */ + +#define CLK_CTR 05 /* counter */ +#define CLK_TPS 60 /* 60Hz */ +#define TMR_CLK 0 /* use timer 0 */ +#define TMR_COM 1 /* 7750 timer */ + +/* Function prototypes and macros */ + +#define ReadP(p) M[p] +#define WriteP(p,d) M[p] = d + +void cpu_ent_hist (uint32 pc, uint32 ea, t_uint64 ir, t_uint64 opnd); +t_stat ch_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat ch6_end_nds (uint32 ch); +uint32 ch6_set_flags (uint32 ch, uint32 unit, uint32 flags); +t_stat ch6_err_disc (uint32 ch, uint32 unit, uint32 flags); +t_stat ch6_req_rd (uint32 ch, uint32 unit, t_uint64 val, uint32 flags); +t_stat ch6_req_wr (uint32 ch, uint32 unit); +t_bool ch6_qconn (uint32 ch, uint32 unit); +t_stat ch9_req_rd (uint32 ch, t_uint64 val); +void ch9_set_atn (uint32 ch); +void ch9_set_ioc (uint32 ch); +void ch9_set_end (uint32 ch, uint32 ireq); +t_bool ch9_qconn (uint32 ch); +void ch_set_map (void); +t_bool ch_qidle (void); + +#endif diff --git a/I7094/i7094_drm.c b/I7094/i7094_drm.c new file mode 100644 index 0000000..798b65e --- /dev/null +++ b/I7094/i7094_drm.c @@ -0,0 +1,286 @@ +/* i7094_drm.c: 7289/7320A drum simulator + + Copyright (c) 2005-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + drm 7289/7320A "fast" drum + + Very little is known about this device; the behavior simulated here is + what is used by CTSS. + + - The drum channel/controller behaves like a hybrid of the 7607 and the 7909. + It responds to SCD (like the 7909), gets its address from the channel + program (like the 7909), but responds to IOCD/IOCP (like the 7607) and + sets channel flags (like the 7607). + - The drum channel supports at least 2 drums. The maximum is 8 or less. + Physical drums are numbered from 0. + - Each drum has a capacity of 192K 36b words. This is divided into 6 + "logical" drum of 32KW each. Each "logical" drum has 16 2048W "sectors". + Logical drums are numbered from 1. + - The drum's behavior if a sector boundary is crossed in mid-transfer is + unknown. CTSS never does this. + - The drum's behavior with record operations is unknown. CTSS only uses + IOCD and IOCP. + - The drum's error indicators are unknown. CTSS regards bits <0:2,13> of + the returned SCD data as errors, as well as the normal 7607 trap flags. + - The drum's rotational speed is unknown. + + Assumptions in this simulator: + + - Transfers may not cross a sector boundary. An attempt to do so sets + the EOF flag and causes an immediate disconnect. + - The hardware never sets end of record. + + For speed, the entire drum is buffered in memory. +*/ + +#include "i7094_defs.h" +#include + +#define DRM_NUMDR 8 /* drums/controller */ + +/* Drum geometry */ + +#define DRM_NUMWDS 2048 /* words/sector */ +#define DRM_SCMASK (DRM_NUMWDS - 1) /* sector mask */ +#define DRM_NUMSC 16 /* sectors/log drum */ +#define DRM_NUMWDL (DRM_NUMWDS * DRM_NUMSC) /* words/log drum */ +#define DRM_NUMLD 6 /* log drums/phys drum */ +#define DRM_SIZE (DRM_NUMLD * DRM_NUMWDL) /* words/phys drum */ +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DRM_NUMWDS))) + +/* Drum address from channel */ + +#define DRM_V_PHY 30 /* physical drum sel */ +#define DRM_M_PHY 07 +#define DRM_V_LOG 18 /* logical drum sel */ +#define DRM_M_LOG 07 +#define DRM_V_WDA 0 /* word address */ +#define DRM_M_WDA (DRM_NUMWDL - 1) +#define DRM_GETPHY(x) (((uint32) ((x) >> DRM_V_PHY)) & DRM_M_PHY) +#define DRM_GETLOG(x) ((((uint32) (x)) >> DRM_V_LOG) & DRM_M_LOG) +#define DRM_GETWDA(x) ((((uint32) (x)) >> DRM_V_WDA) & DRM_M_WDA) +#define DRM_GETDA(x) (((DRM_GETLOG(x) - 1) * DRM_NUMWDL) + DRM_GETWDA(x)) + +/* Drum controller states */ + +#define DRM_IDLE 0 +#define DRM_1ST 1 +#define DRM_DATA 2 +#define DRM_EOS 3 + +uint32 drm_ch = CH_G; /* drum channel */ +uint32 drm_da = 0; /* drum address */ +uint32 drm_sta = 0; /* state */ +uint32 drm_op = 0; /* operation */ +t_uint64 drm_chob = 0; /* output buf */ +uint32 drm_chob_v = 0; /* valid */ +int32 drm_time = 10; /* inter-word time */ + +extern uint32 ind_ioc; + +t_stat drm_svc (UNIT *uptr); +t_stat drm_reset (DEVICE *dptr); +t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_bool drm_da_incr (void); + +/* DRM data structures + + drm_dev DRM device descriptor + drm_unit DRM unit descriptor + drm_reg DRM register list +*/ + +DIB drm_dib = { &drm_chsel, &drm_chwr }; + +UNIT drm_unit[] = { + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) }, + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE, DRM_SIZE) }, + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) }, + { UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+ + UNIT_MUSTBUF+UNIT_DISABLE+UNIT_DIS, DRM_SIZE) } + }; + +REG drm_reg[] = { + { ORDATA (STATE, drm_sta, 2) }, + { ORDATA (DA, drm_da, 18) }, + { FLDATA (OP, drm_op, 0) }, + { ORDATA (CHOB, drm_chob, 36) }, + { FLDATA (CHOBV, drm_chob_v, 0) }, + { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, + { DRDATA (CHAN, drm_ch, 3), REG_HRO }, + { NULL } + }; + +MTAB drm_mtab[] = { + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, NULL, &ch_show_chan }, + { 0 } + }; + +DEVICE drm_dev = { + "DRM", drm_unit, drm_reg, drm_mtab, + DRM_NUMDR, 8, 18, 1, 8, 36, + NULL, NULL, &drm_reset, + NULL, NULL, NULL, + &drm_dib, DEV_DIS + }; + +/* Channel select routine */ + +t_stat drm_chsel (uint32 ch, uint32 sel, uint32 unit) +{ +drm_ch = ch; /* save channel */ +if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */ + +switch (sel) { /* case on cmd */ + + case CHSL_RDS: /* read */ + case CHSL_WRS: /* write */ + if (drm_sta != DRM_IDLE) return ERR_STALL; /* busy? */ + drm_sta = DRM_1ST; /* initial state */ + if (sel == CHSL_WRS) drm_op = 1; /* set read/write */ + else drm_op = 0; /* LCHx sends addr */ + break; /* wait for addr */ + + default: /* other */ + return STOP_ILLIOP; + } +return SCPE_OK; +} + +/* Channel write routine */ + +t_stat drm_chwr (uint32 ch, t_uint64 val, uint32 flags) +{ +uint32 u, l; +int32 cp, dp; + +if (drm_sta == DRM_1ST) { + u = DRM_GETPHY (val); /* get unit */ + l = DRM_GETLOG (val); /* get logical address */ + if ((u >= DRM_NUMDR) || /* invalid unit? */ + (drm_unit[u].flags & UNIT_DIS) || /* disabled unit? */ + (l == 0) || (l > DRM_NUMLD)) { /* invalid log drum? */ + ch6_err_disc (ch, U_DRM, CHF_TRC); /* disconnect */ + drm_sta = DRM_IDLE; + return SCPE_OK; + } + drm_da = DRM_GETDA (val); /* get drum addr */ + cp = GET_POS (drm_time); /* current pos in sec */ + dp = (drm_da & DRM_SCMASK) - cp; /* delta to desired pos */ + if (dp <= 0) dp = dp + DRM_NUMWDS; /* if neg, add rev */ + sim_activate (&drm_unit[u], dp * drm_time); /* schedule */ + if (drm_op) ch6_req_wr (ch, U_DRM); /* if write, get word */ + drm_sta = DRM_DATA; + drm_chob = 0; /* clr, inval buffer */ + drm_chob_v = 0; + } +else { + drm_chob = val & DMASK; + drm_chob_v = 1; + } +return SCPE_OK; +} + +/* Unit service - this code assumes the entire drum is buffered */ + +t_stat drm_svc (UNIT *uptr) +{ +t_uint64 *fbuf = (t_uint64 *) uptr->filebuf; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? */ + ch6_err_disc (drm_ch, U_DRM, CHF_TRC); /* set TRC, disc */ + drm_sta = DRM_IDLE; /* drum is idle */ + return SCPE_UNATT; + } +if (drm_da >= DRM_SIZE) { /* nx logical drum? */ + ch6_err_disc (drm_ch, U_DRM, CHF_EOF); /* set EOF, disc */ + drm_sta = DRM_IDLE; /* drum is idle */ + return SCPE_OK; + } + +switch (drm_sta) { /* case on state */ + + case DRM_DATA: /* data */ + if (drm_op) { /* write? */ + if (drm_chob_v) drm_chob_v = 0; /* valid? clear */ + else if (ch6_qconn (drm_ch, U_DRM)) /* no, chan conn? */ + ind_ioc = 1; /* io check */ + fbuf[drm_da] = drm_chob; /* get data */ + if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; + if (!drm_da_incr ()) ch6_req_wr (drm_ch, U_DRM); + } + else{ /* read */ + ch6_req_rd (drm_ch, U_DRM, fbuf[drm_da], 0); /* send word to channel */ + drm_da_incr (); + } + sim_activate (uptr, drm_time); /* next word */ + break; + + case DRM_EOS: /* end sector */ + if (ch6_qconn (drm_ch, U_DRM)) /* drum still conn? */ + ch6_err_disc (drm_ch, U_DRM, CHF_EOF); /* set EOF, disc */ + drm_sta = DRM_IDLE; /* drum is idle */ + break; + } /* end case */ + +return SCPE_OK; +} + +/* Increment drum address - return true, set new state if end of sector */ + +t_bool drm_da_incr (void) +{ +drm_da = drm_da + 1; +if (drm_da & DRM_SCMASK) return FALSE; +drm_sta = DRM_EOS; +return TRUE; +} + +/* Reset routine */ + +t_stat drm_reset (DEVICE *dptr) +{ +uint32 i; + +drm_da = 0; +drm_op = 0; +drm_sta = DRM_IDLE; +drm_chob = 0; +drm_chob_v = 0; +for (i = 0; i < dptr->numunits; i++) sim_cancel (dptr->units + i); +return SCPE_OK; +} diff --git a/I7094/i7094_dsk.c b/I7094/i7094_dsk.c new file mode 100644 index 0000000..d0c2f7c --- /dev/null +++ b/I7094/i7094_dsk.c @@ -0,0 +1,1172 @@ +/* i7094_dsk.c: 7631 file control (disk/drum) simulator + + Copyright (c) 2005-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dsk 7631 file control + + The 7631 is a controller for multiple serial bit stream devices such as + disks or drums. It supports the + + 1301 fixed disk + 1302 fixed disk + 2302 fixed disk + 7320 drum + + The 7631 supports variable record formatting, user-specified record numbering, + and other complex features. Each track has + + home address 1: the track number, 4 BCD digits (implicit) + home address 2: user-specified track identifier, 6 BCD chars + records 1..n: variably formatted records, each consisting of + record address: user-specified record identifier, 4 BCD digits + and 2 BCD characters + record data: 36b words + + To deal with this, the simulator provides a container of 500 (7320/1301) or + 1000 (1302/2302) words per track. Each track starts with home address 2 + and then contains a variable number of records. Each record has a two-word + header followed by data: + + word 0: record length without header + word 1: record address + word 2: start of data + word 2+n-1: end of data + word 2+n+2: start of next record + + A record length of 0 indicates end of valid data on the track. + + Orders to the 7631 are 10 BCD digits (60b), consisting of two words: + + word 0: op-op-access-module-d1-d2 + word 1: d3-d4-d5-d6-x-x + + Depending on the opcode, d1:d6 can be a track number plus home address 2, + or a record number. + + Status from the 7631 is also 10 BCD digits (60b), with 36b in the first + word, and 24b (plus 12b of zeroes) in the second. + + Because modules can have two access arms that seek independently, each + module m is represented by two units: unit m for access 0 and unit m+10 + for access 1. This requires tricky bookkeeping to be sure that the + service routine is using the 'right' unit. + + Limitations of the simulation: + + - HA2 and record address must be exactly 6 characters (one word) + - Record lengths must be exact multiples of 6 characters + - Seek timing is fixed rather than based on seek length +*/ + +/* Definitions */ + +#include "i7094_defs.h" +#include + +#define DSK_NUMDR 10 /* modules/controller */ +#define DSK_SNS (2 * DSK_NUMDR) /* dummy unit for sense */ + +/* Drive geometry */ + +#define DSK_WDSPT_7320 500 /* words/track */ +#define DSK_WDSPT_1301 500 +#define DSK_WDSPT_1302 1000 +#define DSK_WDSPT_2302 1000 +#define DSK_TRKPC_7320 400 /* tracks/cylinder */ +#define DSK_TRKPC_1301 40 +#define DSK_TRKPC_1302 40 +#define DSK_TRKPC_2302 40 +#define DSK_CYLPA_7320 1 /* cylinders/access */ +#define DSK_CYLPA_1301 250 +#define DSK_CYLPA_1302 250 +#define DSK_CYLPA_2302 250 +#define DSK_TRKPA_7320 (DSK_TRKPC_7320*DSK_CYLPA_7320) /* tracks/access */ +#define DSK_TRKPA_1301 (DSK_TRKPC_1301*DSK_CYLPA_1301) +#define DSK_TRKPA_1302 (DSK_TRKPC_1302*DSK_CYLPA_1302) +#define DSK_TRKPA_2302 (DSK_TRKPC_2302*DSK_CYLPA_2302) +#define DSK_ACCPM_7320 1 /* access/module */ +#define DSK_ACCPM_1301 1 +#define DSK_ACCPM_1302 2 +#define DSK_ACCPM_2302 2 +#define DSK_FMCPT_7320 2868 /* format chars/track */ +#define DSK_FMCPT_1301 2868 +#define DSK_FMCPT_1302 5942 +#define DSK_FMCPT_2302 5942 +#define SIZE_7320 (DSK_WDSPT_7320*DSK_TRKPA_7320*DSK_ACCPM_7320) +#define SIZE_1301 (DSK_WDSPT_1301*DSK_TRKPA_1301*DSK_ACCPM_1301) +#define SIZE_1302 (DSK_WDSPT_1302*DSK_TRKPA_1302*DSK_ACCPM_1302) +#define SIZE_2302 (DSK_WDSPT_2302*DSK_TRKPA_2302*DSK_ACCPM_2302) +#define DSK_BUFSIZ (DSK_WDSPT_2302) +#define DSK_DA(a,t,d) (((((a) * dsk_tab[d].trkpa) + (t)) * dsk_tab[d].wdspt) *\ + sizeof (t_uint64)) + +/* Unit flags */ + +#define UNIT_V_INOP0 (UNIT_V_UF + 0) /* acc 0 inoperative */ +#define UNIT_V_INOP1 (UNIT_V_UF + 1) /* acc 1 inoperative */ +#define UNIT_V_FMTE (UNIT_V_UF + 2) /* format enabled */ +#define UNIT_V_TYPE (UNIT_V_UF + 3) /* drive type */ +#define UNIT_M_TYPE 03 +#define UNIT_INOP0 (1 << UNIT_V_INOP0) +#define UNIT_INOP1 (1 << UNIT_V_INOP1) +#define UNIT_FMTE (1 << UNIT_V_FMTE) +#define UNIT_TYPE (UNIT_M_TYPE << UNIT_V_TYPE) +#define TYPE_7320 (0 << UNIT_V_TYPE) +#define TYPE_1301 (1 << UNIT_V_TYPE) +#define TYPE_1302 (2 << UNIT_V_TYPE) +#define TYPE_2302 (3 << UNIT_V_TYPE) +#define GET_DTYPE(x) (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE) +#define TRK u3 /* track */ +#define SKF u4 /* seek in progress */ + +/* Track/record structure */ + +#define THA2 0 /* home address 2 */ +#define HA2_MASK 0777700000000 /* two chars checked */ +#define T1STREC 1 /* start of records */ +#define RLNT 0 /* record length */ +#define RADDR 1 /* record address */ +#define RDATA 2 /* start of data */ +#define REC_MASK 0171717177777 /* 4 digits, 2 chars */ + +/* Command word (60b) - 10 BCD digits */ + +#define OP1 0 /* opcode */ +#define OP2 1 +#define ACC 2 /* access */ +#define MOD 3 /* module */ +#define T1 4 /* track */ +#define T2 5 +#define T3 6 +#define T4 7 + +/* Disk states */ + +#define DSK_IDLE 0 + +/* Status word (60b) */ + +#define DSKS_PCHK 004000000000000000000 /* prog check */ +#define DSKS_DCHK 002000000000000000000 /* data check */ +#define DSKS_EXCC 001000000000000000000 /* exc cond */ +#define DSKS_INVS 000200000000000000000 /* invalid seq */ +#define DSKS_INVC 000040000000000000000 /* invalid opcode */ +#define DSKS_FMTC 000020000000000000000 /* format check */ +#define DSKS_NRCF 000010000000000000000 /* no record found */ +#define DSKS_INVA 000002000000000000000 /* invalid address */ +#define DSKS_RSPC 000000400000000000000 /* response check */ +#define DSKS_CMPC 000000200000000000000 /* compare check */ +#define DSKS_PARC 000000100000000000000 /* parity check */ +#define DSKS_ACCI 000000020000000000000 /* access inoperative */ +#define DSKS_ACCN 000000004000000000000 /* access not ready */ +#define DSKS_DSKE 000000002000000000000 /* disk error */ +#define DSKS_FILE 000000001000000000000 /* file error */ +#define DSKS_6B 000000000040000000000 /* six bit mode */ +#define DSKS_ATN0 000000000002000000000 /* attention start */ +#define DSKS_PALL 000777000000000000000 +#define DSKS_DALL 000000740000000000000 +#define DSKS_EALL 000000037000000000000 +#define DSKS_ALLERR 007777777000000000000 + +/* Commands - opcode 0 */ + +#define DSKC_NOP 0x00 +#define DSKC_RLS 0x04 +#define DSKC_8B 0x08 +#define DSKC_6B 0x09 + +/* Commands - opcode 8 */ + +#define DSKC_SEEK 0x0 /* seek */ +#define DSKC_SREC 0x2 /* single record */ +#define DSKC_WFMT 0x3 /* write format */ +#define DSKC_TNOA 0x4 /* track no addr */ +#define DSKC_CYL 0x5 /* cyl no addr */ +#define DSKC_WCHK 0x6 /* write check */ +#define DSKC_ACCI 0x7 /* set acc inoperative */ +#define DSKC_TWIA 0x8 /* track with addr */ +#define DSKC_THA 0x9 /* track home addr */ + +/* CTSS record structure */ + +#define CTSS_HA2 0676767676767 /* =HXXXXXX */ +#define CTSS_RLNT 435 /* data record */ +#define CTSS_D1LNT 31 /* padding */ +#define CTSS_D2LNT 14 +#define CTSS_D3LNT 16 +#define CTSS_DLLNT 1 +#define CTSS_RA1 2 +#define CTSS_RA2 8 + +/* Data and declarations */ + +typedef struct { + char *name; + uint32 accpm; /* acc/module: 1 or 2 */ + uint32 wdspt; /* wds/track: 500 or 1000 */ + uint32 trkpc; /* trks/cyl: 1 or 40 */ + uint32 trkpa; /* trks/acc: 400 or 10000 */ + uint32 fchpt; /* format ch/track */ + uint32 size; + } DISK_TYPE; + +const DISK_TYPE dsk_tab[4] = { + { "7320", DSK_ACCPM_7320, DSK_WDSPT_7320, + DSK_TRKPC_7320, DSK_TRKPA_7320, DSK_FMCPT_7320, SIZE_7320 }, + { "1301", DSK_ACCPM_1301, DSK_WDSPT_1301, + DSK_TRKPC_1301, DSK_TRKPA_1301, DSK_FMCPT_1301, SIZE_1301 }, + { "1302", DSK_ACCPM_1302, DSK_WDSPT_1302, + DSK_TRKPC_1302, DSK_TRKPA_1302, DSK_FMCPT_1302, SIZE_1302 }, + { "2302", DSK_ACCPM_2302, DSK_WDSPT_2302, + DSK_TRKPC_2302, DSK_TRKPA_2302, DSK_FMCPT_2302, SIZE_2302 } + }; + +/* 7320/1301 format track characters */ + +uint8 fmt_thdr_7320[] = { + 4, 4, 4, /* gap 1 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */ + 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */ + }; +uint8 fmt_rhdr_7320[] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */ + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */ + 1, 1, 1, 1, 0 /* record ovhd */ + }; + +/* 1302/2302 format track characters */ + +uint8 fmt_thdr_1302[] = { + 4, 4, 4, 4, 4, 4, /* gap 1 */ + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, /* ha1 */ + 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, /* gap 2 */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 /* ha2 */ + }; +uint8 fmt_rhdr_1302[] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, /* x gap */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* record addr */ + 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, /* y gap */ + 1, 1, 1, 1, 1, 1, 1, 0 /* record ovhd */ + }; + +/* CTSS 7320/1301 track format table */ + +uint32 ctss_fmt_7320[] = { + CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0 + }; + +/* CTSS 1302/2302 track format table */ + +uint32 ctss_fmt_1302[] = { + CTSS_RLNT, CTSS_D1LNT, CTSS_D2LNT, + CTSS_RLNT, CTSS_D3LNT, CTSS_DLLNT, 0 + }; + +uint32 dsk_ch = CH_C; /* disk channel */ +uint32 dsk_acc = 0; /* access */ +uint32 dsk_mod = 0; /* module */ +uint32 dsk_sta = 0; /* disk state */ +uint32 dsk_mode = 0; /* I/O mode */ +uint32 dsk_wchk = 0; /* write check flag */ +uint32 dsk_ctime = 10; /* command time */ +uint32 dsk_stime = 1000; /* seek time */ +uint32 dsk_rtime = 100; /* rotational latency */ +uint32 dsk_wtime = 2; /* word time */ +uint32 dsk_gtime = 5; /* gap time */ +uint32 dsk_rbase = 0; /* record tracking */ +uint32 dsk_rptr = 0; +uint32 dsk_rlim = 0; +uint32 dsk_stop = 0; +uint32 dsk_fmt_cntr = 0; /* format counter */ +t_uint64 dsk_rec = 0; /* rec/home addr (36b) */ +t_uint64 dsk_sns = 0; /* sense data (60b) */ +t_uint64 dsk_cmd = 0; /* BCD command (60b) */ +t_uint64 dsk_chob = 0; /* chan output buffer */ +uint32 dsk_chob_v = 0; /* valid */ +t_uint64 dsk_buf[DSK_BUFSIZ]; /* transfer buffer */ + +extern uint32 ch_req; + +t_stat dsk_svc (UNIT *uptr); +t_stat dsk_svc_sns (UNIT *uptr); +t_stat dsk_reset (DEVICE *dptr); +t_stat dsk_attach (UNIT *uptr, char *cptr); +t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd); +t_stat dsk_uend (uint32 ch, t_uint64 stat); +t_stat dsk_recad (uint32 trk, uint32 rec, uint32 acc, uint32 mod, t_uint64 *res); +t_uint64 dsk_acc_atn (uint32 u); +t_stat dsk_init_trk (UNIT *udptr, uint32 trk); +t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp); +t_stat dsk_wr_trk (UNIT *uptr, uint32 trk); +t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc); +t_bool dsk_qdone (uint32 ch); +t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* DSK data structures + + dsk_dev DSK device descriptor + dsk_unit DSK unit descriptor + dsk_reg DSK register list +*/ + +DIB dsk_dib = { &dsk_chsel, &dsk_chwr }; + +UNIT dsk_unit[] = { + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + TYPE_7320, SIZE_7320) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_DIS+TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_DIS+TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_DIS+TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_DIS+TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_DIS+TYPE_2302, SIZE_2302) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc, UNIT_DIS, 0) }, + { UDATA (&dsk_svc_sns, UNIT_DIS, 0) } + }; + +REG dsk_reg[] = { + { ORDATA (STATE, dsk_sta, 6) }, + { ORDATA (ACCESS, dsk_acc, 1) }, + { ORDATA (MODULE, dsk_mod, 4) }, + { ORDATA (RECORD, dsk_rec, 36) }, + { ORDATA (MODE, dsk_mode, 4) }, + { ORDATA (SENSE, dsk_sns, 60) }, + { ORDATA (BCDCMD, dsk_cmd, 60) }, + { ORDATA (CHOB, dsk_chob, 36) }, + { FLDATA (CHOBV, dsk_chob_v, 0) }, + { FLDATA (STOP, dsk_stop, 0) }, + { DRDATA (FCNTR, dsk_fmt_cntr, 13) }, + { BRDATA (BUF, dsk_buf, 8, 36, DSK_BUFSIZ) }, + { DRDATA (RBASE, dsk_rbase, 10), REG_RO }, + { DRDATA (RPTR, dsk_rptr, 10), REG_RO }, + { DRDATA (RLIM, dsk_rlim, 10), REG_RO }, + { DRDATA (CHAN, dsk_ch, 3), REG_HRO }, + { DRDATA (STIME, dsk_stime, 24), REG_NZ + PV_LEFT }, + { DRDATA (RTIME, dsk_rtime, 24), REG_NZ + PV_LEFT }, + { DRDATA (WTIME, dsk_wtime, 24), REG_NZ + PV_LEFT }, + { DRDATA (GTIME, dsk_gtime, 24), REG_NZ + PV_LEFT }, + { DRDATA (CTIME, dsk_ctime, 24), REG_NZ + PV_LEFT }, + { URDATA (TRACK, dsk_unit[0].TRK, 10, 14, 0, + 2 * DSK_NUMDR, PV_LEFT) }, + { URDATA (SEEKF, dsk_unit[0].SKF, 10, 1, 0, + 2 * DSK_NUMDR, PV_LEFT | REG_HRO) }, + { URDATA (CAPAC, dsk_unit[0].capac, 10, T_ADDR_W, 0, + DSK_NUMDR, PV_LEFT | REG_HRO) }, + { NULL } + }; + +MTAB dsk_mtab[] = { + { UNIT_INOP0 + UNIT_INOP1, 0, "operational", "OPERATIONAL" }, + { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP0, "access 0 inoperative", NULL }, + { UNIT_INOP0 + UNIT_INOP1, UNIT_INOP1, "access 1 inoperative", NULL }, + { UNIT_FMTE, UNIT_FMTE, "formating enabled", "FORMAT" }, + { UNIT_FMTE, 0, "formating disabled", "NOFORMAT" }, + { UNIT_TYPE, TYPE_7320, "7320", "7320", &dsk_set_size }, + { UNIT_TYPE, TYPE_1301, "1301", "1301", &dsk_set_size }, + { UNIT_TYPE, TYPE_1302, "1302", "1302", &dsk_set_size }, + { UNIT_TYPE, TYPE_2302, "2302", "2302", &dsk_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", NULL, + NULL, &ch_show_chan, NULL }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, "FORMAT", NULL, + NULL, &dsk_show_format, NULL }, + { 0 } + }; + +DEVICE dsk_dev = { + "DSK", dsk_unit, dsk_reg, dsk_mtab, + (DSK_NUMDR * 2) + 1, 10, 24, 1, 8, 36, + NULL, NULL, &dsk_reset, + NULL, &dsk_attach, NULL, + &dsk_dib, DEV_DIS + }; + +/* Disk channel select, from 7909 channel program */ + +t_stat dsk_chsel (uint32 ch, uint32 sel, uint32 unit) +{ +uint32 u; + +dsk_ch = ch; +if (dsk_sta != DSK_IDLE) dsk_uend (ch, DSKS_INVS); /* not idle? seq check */ + +switch (sel) { + + case CHSL_CTL: /* control */ + ch_req |= REQ_CH (ch); /* request channel */ + break; + + case CHSL_SNS: /* sense */ + if (sim_is_active (&dsk_unit[DSK_SNS])) /* already sensing? */ + return dsk_uend (ch, DSKS_INVS); /* sequence error */ + sim_activate (&dsk_unit[DSK_SNS], dsk_ctime); /* set timer */ + dsk_stop = 0; + break; + + case CHSL_RDS: /* read */ + if (dsk_mode == DSKC_WFMT) /* write format? */ + return dsk_uend (ch, DSKS_INVS); /* sequence error */ + case CHSL_WRS: /* write */ + if (dsk_mode == 0) dsk_uend (ch, DSKS_INVS); /* no mode? seq check */ + if (dsk_mode == DSKC_WFMT) sel = CHSL_FMT; /* format? fake sel */ + u = (dsk_acc * DSK_NUMDR) + dsk_mod; /* access unit number */ + if (sim_is_active (&dsk_unit[u])) /* access in use? */ + return dsk_uend (ch, DSKS_ACCN); /* access not ready */ + sim_activate (&dsk_unit[u], dsk_rtime); /* rotational time */ + break; + + default: /* other */ + return STOP_ILLIOP; + } + +dsk_sta = sel; /* set new state */ +return SCPE_OK; +} + +/* Disk channel write, from 7909 channel program */ + +t_stat dsk_chwr (uint32 ch, t_uint64 val, uint32 stopf) +{ +if (stopf) dsk_stop = 1; /* stop? */ + +else { + val = val & DMASK; + switch (dsk_sta) { /* case on state */ + + case CHSL_CTL: /* control */ + dsk_cmd = val << 24; + if (val & 0100000000000) { /* need 2nd word? */ + ch_req |= REQ_CH (ch); /* req ch for 2nd */ + dsk_sta = CHSL_CTL|CHSL_2ND; /* next state */ + return SCPE_OK; + } + return dsk_new_cmd (ch, dsk_cmd); /* no, do cmd */ + + case CHSL_CTL|CHSL_2ND: /* 2nd control */ + dsk_cmd |= (val >> 12); + return dsk_new_cmd (ch, dsk_cmd); /* do cmd */ + + default: + dsk_chob = val; /* store data */ + dsk_chob_v = 1; /* set valid */ + } + } + +return SCPE_OK; +} + +/* New command - end of CTL sequence */ + +t_stat dsk_new_cmd (uint32 ch, t_uint64 cmd) +{ +uint32 i, d, a, m, u, trk, dtyp, bcd[8]; + +ch_req |= REQ_CH (ch); /* req ch for end */ +ch9_set_end (ch, 0); /* set end flag */ +dsk_sta = DSK_IDLE; /* ctrl is idle */ + +for (i = 0; i < 8; i++) { /* get chars from cmd */ + d = (uint32) (cmd >> (6 * (9 - i))) & BCD_MASK; + if (d == BCD_ZERO) d = 0; + else if (d == 0) d = BCD_ZERO; /* BCD zero cvt */ + bcd[i] = d; + } + +if (bcd[OP1] == 0) { /* cmd = 0x? */ + + switch (bcd[OP2]) { /* case on x */ + + case DSKC_NOP: /* nop */ + case DSKC_RLS: /* release */ + break; + + case DSKC_8B: /* 8b mode */ + dsk_sns &= ~DSKS_6B; + break; + + case DSKC_6B: /* 6b mode */ + dsk_sns |= DSKS_6B; + break; + + default: /* unknown */ + return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ + } /* end case op2 */ + return SCPE_OK; + } /* end if */ + +else if (bcd[OP1] == 8) { /* cmd = 8x? */ + + a = bcd[ACC]; /* get access, */ + m = bcd[MOD]; /* module */ + u = (a * DSK_NUMDR) + m; /* unit for access */ + if ((m > DSK_NUMDR) || /* invalid module? */ + (dsk_unit[m].flags & UNIT_DIS)) /* disabled module? */ + return dsk_uend (ch, DSKS_ACCI); + dtyp = GET_DTYPE (dsk_unit[m].flags); /* get drive type */ + if ((a >= dsk_tab[dtyp].accpm) || /* invalid access? */ + (dsk_unit[m].flags & (UNIT_INOP0 << a))) /* access inop? */ + return dsk_uend (ch, DSKS_ACCI); + if ((bcd[T1] > 9) || (bcd[T2] > 9) || (bcd[T3] > 9) || (bcd[T4] > 9)) + trk = dsk_tab[dtyp].trkpa + 1; /* bad track */ + else trk = (((((bcd[T1] * 10) + bcd[T2]) * 10) + bcd[T3]) * 10) + bcd[T4]; + + if (bcd[OP2] == DSKC_WCHK) { /* write check */ + if (dsk_mode == 0) /* no prior operation? */ + return dsk_uend (ch, DSKS_INVS); + bcd[OP2] = dsk_mode; /* use prior mode */ + dsk_wchk = 1; /* set write check */ + } + else dsk_wchk = 0; + dsk_sns &= ~(DSKS_ALLERR | dsk_acc_atn (u)); /* clear err, atn */ + dsk_stop = 0; /* clear stop */ + + switch (bcd[OP2]) { + + case DSKC_SEEK: /* seek */ + if ((trk >= dsk_tab[dtyp].trkpa) && /* inv track? */ + ((dtyp == TYPE_7320) || /* drum or not CE? */ + (bcd[T1] > 9) || (bcd[T2] != BCD_AT) || + (bcd[T3] > 9) || (bcd[T4] > 9))) + return dsk_uend (ch, DSKS_INVA); + if (sim_is_active (&dsk_unit[u])) /* selected acc busy? */ + return dsk_uend (ch, DSKS_ACCN); + dsk_unit[u].SKF = 1; /* set seeking flag */ + dsk_unit[u].TRK = trk; /* sel acc on cyl */ + sim_activate (&dsk_unit[u], dsk_stime); /* seek */ + dsk_mode = 0; /* clear I/O mode */ + return SCPE_OK; + + case DSKC_ACCI: /* access inoperative */ + dsk_unit[m].flags |= (UNIT_INOP0 << a); /* set correct flag */ + dsk_mode = 0; /* clear I/O mode */ + return SCPE_OK; + + case DSKC_SREC: /* single record */ + break; /* no verification */ + + case DSKC_WFMT: /* format */ + if (!(dsk_unit[m].flags & UNIT_FMTE)) /* format enabled? */ + return dsk_uend (ch, DSKS_FMTC); /* no, error */ + case DSKC_TNOA: /* track no addr */ + case DSKC_CYL: /* cyl no addr */ + case DSKC_TWIA: /* track with addr */ + case DSKC_THA: /* track home addr */ + if (trk != (uint32) dsk_unit[u].TRK) /* on track? */ + return dsk_uend (ch, DSKS_NRCF); + break; + + default: + return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ + } + + dsk_acc = a; /* save access */ + dsk_mod = m; /* save module */ + dsk_rec = cmd & DMASK; /* save rec/home addr */ + dsk_mode = bcd[OP2]; /* save mode */ + return SCPE_OK; + } + +return dsk_uend (ch, DSKS_INVC); /* invalid opcode */ +} + +/* Sense unit service */ + +t_stat dsk_svc_sns (UNIT *uptr) +{ +t_uint64 dat; + +switch (dsk_sta) { /* case on state */ + + case CHSL_SNS: /* prepare data */ + dsk_buf[0] = (dsk_sns >> 24) & DMASK; /* buffer is 2 words */ + dsk_buf[1] = (dsk_sns << 12) & DMASK; + dsk_rptr = 0; + dsk_rlim = 2; + dsk_sta = CHSL_SNS|CHSL_2ND; /* 2nd state */ + break; + + case CHSL_SNS|CHSL_2ND: /* second state */ + if (dsk_rptr >= dsk_rlim) { /* end of buffer? */ + ch9_set_end (dsk_ch, 0); /* set end */ + ch_req |= REQ_CH (dsk_ch); /* request channel */ + dsk_sta = CHSL_SNS|CHSL_3RD; /* 3rd state */ + sim_activate (uptr, dsk_ctime); /* longer wait */ + return SCPE_OK; + } + dat = dsk_buf[dsk_rptr++]; /* get word */ + if (!dsk_stop) ch9_req_rd (dsk_ch, dat); /* send wd to chan */ + break; + + case CHSL_SNS|CHSL_3RD: /* 3rd state */ + if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */ + dsk_sta = CHSL_SNS; /* repeat sequence */ + break; + } + +sim_activate (uptr, dsk_wtime); /* sched next */ +return SCPE_OK; +} + +/* Seek, read, write unit service */ + +t_stat dsk_svc (UNIT *uaptr) +{ +uint32 i, dtyp, trk; +uint8 fc, *format; +t_uint64 rdat; +UNIT *udptr; +t_stat r; + +if (uaptr->SKF) { /* seeking? */ + uint32 u = uaptr - dsk_dev.units; /* get unit */ + uaptr->SKF = 0; /* seek done */ + dsk_sns |= dsk_acc_atn (u); /* set atn bit */ + ch9_set_atn (dsk_ch); /* set atn flag */ + return SCPE_OK; + } + +udptr = dsk_dev.units + dsk_mod; /* data unit */ +if (udptr->flags & (UNIT_INOP0 << dsk_acc)) /* acc inoperative? */ + return dsk_uend (dsk_ch, DSKS_ACCI); /* error */ +if ((udptr->flags & UNIT_ATT) == 0) { /* not attached? */ + dsk_uend (dsk_ch, DSKS_ACCI); /* error */ + return SCPE_UNATT; + } + +dtyp = GET_DTYPE (udptr->flags); /* get data drive type */ +trk = uaptr->TRK; /* get access track */ + +switch (dsk_sta) { /* case on state */ + + case CHSL_RDS: /* read start */ + if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */ + return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */ + } + dsk_sta = CHSL_RDS|CHSL_2ND; /* next state */ + break; + + case CHSL_RDS|CHSL_2ND: /* read data transmit */ + if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */ + if (r != ERR_ENDRC) return r; /* error? */ + dsk_sta = CHSL_RDS|CHSL_3RD; /* next state */ + sim_activate (uaptr, dsk_gtime); /* gap time */ + return SCPE_OK; + } + rdat = dsk_buf[dsk_rptr++]; /* get word */ + if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */ + if (!dsk_stop) ch9_req_rd (dsk_ch, rdat); /* give to channel */ + break; + + case CHSL_RDS|CHSL_3RD: /* read end rec/trk */ + if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */ + dsk_sta = CHSL_RDS; /* repeat sequence */ + break; + + case CHSL_WRS: /* write start */ + if (r = dsk_init_trk (udptr, trk)) { /* read track, err? */ + return ((r == ERR_NRCF)? SCPE_OK: r); /* rec not fnd ok */ + } + ch_req |= REQ_CH (dsk_ch); /* first request */ + dsk_sta = CHSL_WRS|CHSL_2ND; /* next state */ + dsk_chob = 0; /* clr, inval buffer */ + dsk_chob_v = 0; + break; + + case CHSL_WRS|CHSL_2ND: /* write data transmit */ + if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */ + else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */ + if (dsk_wchk) { /* write check? */ + if (dsk_buf[dsk_rptr++] != dsk_chob) /* data mismatch? */ + return dsk_uend (dsk_ch, DSKS_CMPC); /* error */ + } + else dsk_buf[dsk_rptr++] = dsk_chob; /* write, store word */ + if (dsk_rptr == T1STREC) dsk_rptr++; /* if THA, skip after HA */ + if (r = dsk_xfer_done (uaptr, dtyp)) { /* transfer done? */ + if (r != ERR_ENDRC) return r; /* error? */ + dsk_sta = CHSL_WRS|CHSL_3RD; /* next state */ + sim_activate (uaptr, dsk_gtime); /* gap time */ + return SCPE_OK; + } + if (!dsk_stop) ch_req |= REQ_CH (dsk_ch); /* more to do */ + break; + + case CHSL_WRS|CHSL_3RD: /* write done */ + if (!dsk_wchk) { /* if write */ + if (r = dsk_wr_trk (udptr, trk)) return r; /* write track; err? */ + } + if (dsk_qdone (dsk_ch)) return SCPE_OK; /* done? exit */ + dsk_sta = CHSL_WRS; /* repeat sequence */ + break; + +/* Formatting takes place in five stages + + 1. Clear the track buffer, request the first word from the channel + 2. Match characters against the fixed overhead (HA1, HA2, and gaps) + 3. Match characters against the per-record overhead (RA and gaps) + 4. Count the characters defining the record length + 5. See if the next character is end or gap; if gap, return to stage 3 + + This formating routine is not exact. It checks whether the format + will fit in the container, not whether the format would fit on a + real 7320, 1301, 1302, or 2302. */ + + case CHSL_FMT: /* initialization */ + for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0;/* clear track buf */ + dsk_rbase = T1STREC; /* init record ptr */ + dsk_rptr = 0; /* init format ptr */ + dsk_fmt_cntr = 0; /* init counter */ + ch_req |= REQ_CH (dsk_ch); /* request channel */ + dsk_sta = CHSL_FMT|CHSL_2ND; /* next state */ + dsk_chob = 0; /* clr, inval buffer */ + dsk_chob_v = 0; + break; + + case CHSL_FMT|CHSL_2ND: /* match track header */ + if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) + format = fmt_thdr_7320; + else format = fmt_thdr_1302; + if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ + if (fc != format[dsk_rptr++]) /* mismatch? */ + return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ + if (format[dsk_rptr] == 0) { /* end format? */ + dsk_sta = CHSL_FMT|CHSL_3RD; /* next state */ + dsk_rptr = 0; /* reset format ptr */ + } + break; + + case CHSL_FMT|CHSL_3RD: /* match record header */ + if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) + format = fmt_rhdr_7320; + else format = fmt_rhdr_1302; + if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ + if (fc != format[dsk_rptr++]) /* mismatch? */ + return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ + if (format[dsk_rptr] == 0) { /* end format? */ + dsk_sta = CHSL_FMT|CHSL_4TH; /* next state */ + dsk_rlim = 0; /* reset record ctr */ + } + break; + + case CHSL_FMT|CHSL_4TH: /* count record size */ + if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ + if (fc == BCD_ONE) dsk_rlim++; /* more record? */ + else { + uint32 rsiz = dsk_rlim / 6; /* rec size words */ + if ((fc != BCD_TWO) || /* improper end? */ + (rsiz == 0) || /* smaller than min? */ + ((dsk_rlim % 6) != 0) || /* not multiple of 6? */ + ((dsk_rbase + rsiz + RDATA) >= dsk_tab[dtyp].wdspt)) + return dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ + dsk_buf[dsk_rbase + RLNT] = rsiz; /* record rec lnt */ + dsk_rbase = dsk_rbase + rsiz + RDATA; /* new rec start */ + dsk_sta = CHSL_FMT|CHSL_5TH; /* next state */ + } + break; + + case CHSL_FMT|CHSL_5TH: /* record or track end */ + if (!dsk_get_fmtc (dtyp, &fc)) return SCPE_OK; /* get fmt char; err? */ + if (fc == BCD_TWO) { /* back to record header? */ + dsk_rptr = 2; /* already done 2 chars */ + dsk_sta = CHSL_FMT|CHSL_3RD; /* record header state */ + } + else if (fc != BCD_ONE) dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ + else { + if (!dsk_wchk) { /* actual write? */ + trk = trk - (trk % dsk_tab[dtyp].trkpc); /* cyl start */ + for (i = 0; i < dsk_tab[dtyp].trkpc; i++) { /* do all tracks */ + if (r = dsk_wr_trk (udptr, trk + i)) /* wr track; err? */ + return r; + } + } + ch9_set_end (dsk_ch, 0); /* set end */ + ch_req |= REQ_CH (dsk_ch); /* request channel */ + dsk_sta = DSK_IDLE; /* disk is idle */ + return SCPE_OK; /* done */ + } + break; + + default: + return SCPE_IERR; + } + +sim_activate (uaptr, dsk_wtime); +return SCPE_OK; +} + +/* Initialize data transfer + + Inputs: + udptr = pointer to data unit + trk = track to read + Outputs: + dsk_buf contains track specified by trk + dsk_rbase, dsk_rptr, dsk_rlim are initialized + Errors: + SCPE_IOERR = I/O error (fatal, uend) + ERR_NRCF = no record found (HA2 or record number mismatch, uend) + STOP_INVFMT = invalid format (fatal, uend) +*/ + +t_stat dsk_init_trk (UNIT *udptr, uint32 trk) +{ +uint32 k, da, dtyp, rlnt; + +dtyp = GET_DTYPE (udptr->flags); /* get drive type */ +da = DSK_DA (dsk_acc, trk, dtyp); /* get disk address */ +sim_fseek (udptr->fileref, da, SEEK_SET); /* read track */ +k = sim_fread (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref); +if (ferror (udptr->fileref)) { /* error? */ + perror ("DSK I/O error"); + clearerr (udptr->fileref); + dsk_uend (dsk_ch, DSKS_DSKE); + return SCPE_IOERR; + } +for ( ; k < dsk_tab[dtyp].wdspt; k++) dsk_buf[k] = 0; /* zero fill */ +dsk_rbase = T1STREC; /* record base */ +rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ +dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */ +if ((rlnt == 0) || (dsk_rlim >= dsk_tab[dtyp].wdspt)) { /* invalid record? */ + dsk_uend (dsk_ch, DSKS_FMTC); + return STOP_INVFMT; + } +if (dsk_mode != DSKC_SREC) { /* not single record? */ + if (dsk_mode == DSKC_THA) dsk_rptr = 0; /* trk home addr? */ + else { + if (((dsk_rec << 24) ^ dsk_buf[THA2]) & HA2_MASK) { + dsk_uend (dsk_ch, DSKS_NRCF); /* invalid HA2 */ + return ERR_NRCF; + } + if (dsk_mode == DSKC_TWIA) /* track with addr? */ + dsk_rptr = dsk_rbase + RADDR; /* start at addr */ + else dsk_rptr = dsk_rbase + RDATA; /* else, at data */ + } + return SCPE_OK; + } +while (rlnt != 0) { /* until end track */ + dsk_rptr = dsk_rbase + RDATA; + if (((dsk_rec ^ dsk_buf[dsk_rbase + RADDR]) & REC_MASK) == 0) + return SCPE_OK; /* rec found? done */ + dsk_rbase = dsk_rlim; /* next record */ + rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ + dsk_rlim = dsk_rbase + rlnt + RDATA; /* limit */ + if (dsk_rlim >= dsk_tab[dtyp].wdspt) { /* invalid format? */ + dsk_uend (dsk_ch, DSKS_FMTC); + return STOP_INVFMT; + } + } +dsk_uend (dsk_ch, DSKS_NRCF); /* not found */ +return ERR_NRCF; +} + +/* Check end of transfer + + Inputs: + uptr = pointer to access unit + dtyp = drive type + Outputs: + ERR_ENDRC = end of record/track/cylinder, end sent, ch req if required + SCPE_OK = more to do, dsk_rbase, dsk_rptr, dsk_rlim may be updated + STOP_INVFMT = invalid format (fatal, uend sent) +*/ + +t_stat dsk_xfer_done (UNIT *uaptr, uint32 dtyp) +{ +uint32 rlnt; + +if (dsk_rptr < dsk_rlim) return SCPE_OK; /* record done? */ +if (dsk_stop || !ch9_qconn (dsk_ch) || /* stop or err disc or */ + (dsk_mode == DSKC_SREC)) { /* single record? */ + ch9_set_end (dsk_ch, 0); /* set end */ + ch_req |= REQ_CH (dsk_ch); /* request channel */ + return ERR_ENDRC; + } +dsk_rbase = dsk_rlim; /* next record */ +rlnt = (uint32) dsk_buf[dsk_rbase + RLNT]; /* length */ +dsk_rlim = dsk_rbase + rlnt + RDATA; /* end */ +if ((dsk_rbase >= dsk_tab[dtyp].wdspt) || /* invalid format? */ + (dsk_rlim >= dsk_tab[dtyp].wdspt)) { + dsk_uend (dsk_ch, DSKS_FMTC); + return STOP_INVFMT; + } +if (rlnt) { /* more on track? */ + if ((dsk_mode == DSKC_THA) || (dsk_mode == DSKC_TWIA)) + dsk_rptr = dsk_rbase + RADDR; /* start with addr */ + else dsk_rptr = dsk_rbase + RDATA; /* or data */ + return SCPE_OK; + } +if (dsk_mode == DSKC_CYL) { /* cylinder mode? */ + uaptr->TRK = (uaptr->TRK + 1) % dsk_tab[dtyp].trkpa; /* incr track */ + if (uaptr->TRK % dsk_tab[dtyp].trkpc) /* not cyl end? */ + return ERR_ENDRC; /* don't set end */ + } +ch9_set_end (dsk_ch, 0); /* set end */ +ch_req |= REQ_CH (dsk_ch); /* request channel */ +return ERR_ENDRC; +} + +/* Write track back to file */ + +t_stat dsk_wr_trk (UNIT *udptr, uint32 trk) +{ +uint32 dtyp = GET_DTYPE (udptr->flags); +uint32 da = DSK_DA (dsk_acc, trk, dtyp); + +sim_fseek (udptr->fileref, da, SEEK_SET); +sim_fwrite (dsk_buf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, udptr->fileref); +if (ferror (udptr->fileref)) { + perror ("DSK I/O error"); + clearerr (udptr->fileref); + dsk_uend (dsk_ch, DSKS_DSKE); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Synthesize right attention bit from (access * 10 + module) */ + +t_uint64 dsk_acc_atn (uint32 u) +{ +uint32 g, b; + +g = u / 4; /* bit group */ +b = u % 4; /* bit within group */ +return (DSKS_ATN0 >> ((g * 6) + (b? b + 1: 0))); +} + +/* Get next format character */ + +t_bool dsk_get_fmtc (uint32 dtyp, uint8 *fc) +{ +uint32 cc = dsk_fmt_cntr % 6; + +if (cc == 0) { /* start of word? */ + if (dsk_chob_v) dsk_chob_v = 0; /* valid? clear */ + else if (!dsk_stop) ch9_set_ioc (dsk_ch); /* no, no stop? io chk */ + } +*fc = ((uint8) (dsk_chob >> ((5 - cc) * 6))) & 077; /* get character */ +if ((cc == 5) && !dsk_stop) ch_req |= REQ_CH (dsk_ch); /* end of word? */ +if (dsk_fmt_cntr++ >= dsk_tab[dtyp].fchpt) { /* track overflow? */ + dsk_uend (dsk_ch, DSKS_FMTC); /* format check */ + return FALSE; + } +return TRUE; +} + +/* Unusual end (set status and stop) */ + +t_stat dsk_uend (uint32 ch, t_uint64 stat) +{ +dsk_sns |= stat; +dsk_sns &= ~(DSKS_PCHK|DSKS_DCHK|DSKS_EXCC); +if (dsk_sns & DSKS_PALL) dsk_sns |= DSKS_PCHK; +if (dsk_sns & DSKS_DALL) dsk_sns |= DSKS_DCHK; +if (dsk_sns & DSKS_EALL) dsk_sns |= DSKS_EXCC; +ch9_set_end (ch, CHINT_UEND); +ch_req |= REQ_CH (ch); +dsk_sta = DSK_IDLE; +return SCPE_OK; +} + +/* Test for done */ + +t_bool dsk_qdone (uint32 ch) +{ +if (dsk_stop || !ch9_qconn (ch)) { /* stop or err disc? */ + dsk_sta = DSK_IDLE; /* disk is idle */ + return TRUE; + } +return FALSE; +} + +/* Reset */ + +t_stat dsk_reset (DEVICE *dptr) +{ +uint32 i; +UNIT *uptr; + +dsk_acc = 0; +dsk_mod = 0; +dsk_rec = 0; +dsk_mode = 0; +dsk_wchk = 0; +dsk_sns = 0; +dsk_cmd = 0; +dsk_sta = DSK_IDLE; +dsk_rbase = 0; +dsk_rptr = 0; +dsk_rlim = 0; +dsk_stop = 0; +dsk_fmt_cntr = 0; +dsk_chob = 0; +dsk_chob_v = 0; +for (i = 0; i < DSK_BUFSIZ; i++) dsk_buf[i] = 0; +for (i = 0; i <= (2 * DSK_NUMDR); i++) { + uptr = dsk_dev.units + i; + sim_cancel (uptr); + uptr->TRK = 0; + uptr->SKF = 0; + } +return SCPE_OK; +} + +/* Attach routine, test formating */ + +t_stat dsk_attach (UNIT *uptr, char *cptr) +{ +uint32 dtyp = GET_DTYPE (uptr->flags); +t_stat r; + +uptr->capac = dsk_tab[dtyp].size; +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->TRK = 0; +uptr->SKF = 0; +uptr->flags &= ~(UNIT_INOP0|UNIT_INOP1); +return dsk_show_format (stdout, uptr, 0, NULL); +} + +/* Set disk size */ + +t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 dtyp = GET_DTYPE (val); +uint32 u = uptr - dsk_dev.units; +UNIT *u1; + +if (u & 1) return SCPE_ARG; +u1 = dsk_dev.units + u + 1; +if ((uptr->flags & UNIT_ATT) || (u1->flags & UNIT_ATT)) + return SCPE_ALATT; +if (val == TYPE_7320) u1->flags = (u1->flags & ~UNIT_DISABLE) | UNIT_DIS; +else { + u1->flags = (u1->flags & ~UNIT_TYPE) | val | UNIT_DISABLE; + u1->capac = dsk_tab[dtyp].size; + } +uptr->capac = dsk_tab[dtyp].size; +return SCPE_OK; +} + +/* Show format */ + +t_stat dsk_show_format (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 a, t, k, u, tlim, dtyp, da; +uint32 rptr, rlnt, rlim, rec, ctptr, *format; +uint32 minrsz = DSK_BUFSIZ; +uint32 maxrsz = 0; +uint32 minrno = DSK_BUFSIZ; +uint32 maxrno = 0; +t_bool ctss; +t_uint64 dbuf[DSK_BUFSIZ]; +DEVICE *dptr; + +if (uptr == NULL) return SCPE_IERR; +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; +dptr = find_dev_from_unit (uptr); +u = uptr - dptr->units; +if (dptr == NULL) return SCPE_IERR; + +dtyp = GET_DTYPE (uptr->flags); +if ((dtyp == TYPE_7320) || (dtyp == TYPE_1301)) + format = ctss_fmt_7320; +else format = ctss_fmt_1302; +for (a = 0, ctss = TRUE; a < dsk_tab[dtyp].accpm; a++) { + if (val) tlim = dsk_tab[dtyp].trkpa; + else tlim = 1; + for (t = 0; t < tlim; t++) { + da = DSK_DA (a, t, dtyp); /* get disk address */ + sim_fseek (uptr->fileref, da, SEEK_SET); /* read track */ + k = sim_fread (dbuf, sizeof (t_uint64), dsk_tab[dtyp].wdspt, uptr->fileref); + if (ferror (uptr->fileref)) return SCPE_IOERR; /* error? */ + for ( ; k < dsk_tab[dtyp].wdspt; k++) dbuf[k] = 0; + rptr = T1STREC; + rlnt = (uint32) dbuf[rptr + RLNT]; + if (dbuf[THA2] != CTSS_HA2) ctss = FALSE; + if (rlnt == 0) { + if (a || t) fprintf (st, + "Unformatted track, unit = %d, access = %d, track = %d\n", u, a, t); + else fprintf (st, "Unit %d is unformatted\n", u); + return SCPE_OK; + } + for (rec = 0, ctptr = 0; rlnt != 0; rec++) { + if ((format[ctptr] == 0) || format[ctptr++] != rlnt) + ctss = FALSE; + rlim = rptr + rlnt + RDATA; + if (rlim >= dsk_tab[dtyp].wdspt) { + fprintf (st, "Invalid record length %d, unit = %d, access = %d, track = %d, record = %d\n", + rlnt, u, a, t, rec); + return SCPE_OK; + } + if (rlnt > maxrsz) maxrsz = rlnt; + if (rlnt < minrsz) minrsz = rlnt; + rptr = rlim; + rlnt = (uint32) dbuf[rptr + RLNT]; + } + if (format[ctptr] != 0) ctss = FALSE; + if (rec > maxrno) maxrno = rec; + if (rec < minrno) minrno = rec; + } + } +if (val == 0) return SCPE_OK; +if (ctss) fprintf (st, "CTSS format\n"); +else if ((minrno == maxrno) && (minrsz == maxrsz)) fprintf (st, + "Valid fixed format, records/track = %d, record size = %d\n", + minrno, minrsz); +else if (minrsz == maxrsz) fprintf (st, + "Valid variable format, records/track = %d-%d, record size = %d\n", + minrno, maxrno, minrsz); +else if (minrno == maxrno) fprintf (st, + "Valid variable format, records/track = %d, record sizes = %d-%d\n", + minrno, minrsz, maxrsz); +else fprintf (st, + "Valid variable format, records/track = %d-%d, record sizes = %d-%d\n", + minrno, maxrno, minrsz, maxrsz); +return SCPE_OK; +} diff --git a/I7094/i7094_io.c b/I7094/i7094_io.c new file mode 100644 index 0000000..2bc725b --- /dev/null +++ b/I7094/i7094_io.c @@ -0,0 +1,1789 @@ +/* i7094_io.c: IBM 7094 I/O subsystem (channels) + + Copyright (c) 2003-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + chana..chanh I/O channels + + Notes on channels and CTSS. + + - CTSS B-core is supported by the addition of a 16th bit to the current + address field of the channel command. Both the channel location counter + and the channel current address register are widened to 16b. Thus, + channel programs can run in B-core, and channel transfers can access B-core. + CTSS assumes that a channel command which starts a transfer in B-core + will not access A-core; the 16th bit does not increment. + - The channel start commands (RCHx and LCHx) incorporate the A-core/B-core + select as part of effective address generation. CTSS does not relocate + RCHx and LCHx target addresses; because the relocation indicator is + always zero, it's impossible to tell whether the protection indicator + affects address generation. + - The CTSS protection RPQ does not cover channel operations. Thus, CTSS + must inspect and vet all channel programs initiated by user mode programs, + notably the background processor FMS. CTSS inspects in-progress 7607 + channel programs to make sure than either the nostore bit or the B-core + bit is set; thus, SCHx must store all 16b of the current address. +*/ + +#include "i7094_defs.h" + +#define CHAMASK ((cpu_model & I_CT)? PAMASK: AMASK) /* chan addr mask */ +#define CHAINC(x) (((x) & ~AMASK) | (((x) + 1) & AMASK)) + +typedef struct { + char *name; + uint32 flags; + } DEV_CHAR; + +uint32 ch_sta[NUM_CHAN]; /* channel state */ +uint32 ch_dso[NUM_CHAN]; /* data select op */ +uint32 ch_dsu[NUM_CHAN]; /* data select unit */ +uint32 ch_ndso[NUM_CHAN]; /* non-data select op */ +uint32 ch_ndsu[NUM_CHAN]; /* non-data select unit */ +uint32 ch_flags[NUM_CHAN]; /* flags */ +uint32 ch_clc[NUM_CHAN]; /* chan loc ctr */ +uint32 ch_op[NUM_CHAN]; /* channel op */ +uint32 ch_wc[NUM_CHAN]; /* word count */ +uint32 ch_ca[NUM_CHAN]; /* core address */ +uint32 ch_lcc[NUM_CHAN]; /* control cntr (7909) */ +uint32 ch_cnd[NUM_CHAN]; /* cond reg (7909) */ +uint32 ch_sms[NUM_CHAN]; /* cond mask reg (7909) */ +t_uint64 ch_ar[NUM_CHAN]; /* assembly register */ +uint32 ch_idf[NUM_CHAN]; /* channel input data flags */ +DEVICE *ch2dev[NUM_CHAN] = { NULL }; +uint32 ch_tpoll = 5; /* channel poll */ + +extern t_uint64 *M; +extern uint32 cpu_model, data_base; +extern uint32 hst_ch; +extern uint32 ch_req; +extern uint32 chtr_inht, chtr_inhi, chtr_enab; +extern uint32 ind_ioc; +extern uint32 chtr_clk; +extern DEVICE cdr_dev, cdp_dev; +extern DEVICE lpt_dev; +extern DEVICE mt_dev[NUM_CHAN]; +extern DEVICE drm_dev; +extern DEVICE dsk_dev; +extern DEVICE com_dev; +extern int32 sim_brk_summ; + +t_stat ch_reset (DEVICE *dptr); +t_stat ch6_svc (UNIT *uptr); +t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); +DEVICE *ch_find_dev (uint32 ch, uint32 unit); +t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta); +t_bool ch6_rd_putw (uint32 ch); +t_stat ch6_wr_getw (uint32 ch, t_bool eorz); +t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld); +t_stat ch6_ioxt (uint32 ch); +void ch6_iosp_cclr (uint32 ch); +t_stat ch9_new_cmd (uint32 ch); +t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir); +t_stat ch9_sel (uint32 ch, uint32 sel); +t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl); +t_stat ch9_rd_putw (uint32 ch); +t_stat ch9_wr_getw (uint32 ch); +void ch9_eval_int (uint32 ch, uint32 iflags); +DEVICE *ch_map_flags (uint32 ch, int32 fl); + +extern CTAB *sim_vm_cmd; +extern t_stat ch_bkpt (uint32 ch, uint32 clc); + +const uint32 col_masks[12] = { /* row 9,8,..,0,11,12 */ + 00001, 00002, 00004, + 00010, 00020, 00040, + 00100, 00200, 00400, + 01000, 02000, 04000 + }; + +const t_uint64 bit_masks[36] = { + 0000000000001, 0000000000002, 0000000000004, + 0000000000010, 0000000000020, 0000000000040, + 0000000000100, 0000000000200, 0000000000400, + 0000000001000, 0000000002000, 0000000004000, + 0000000010000, 0000000020000, 0000000040000, + 0000000100000, 0000000200000, 0000000400000, + 0000001000000, 0000002000000, 0000004000000, + 0000010000000, 0000020000000, 0000040000000, + 0000100000000, 0000200000000, 0000400000000, + 0001000000000, 0002000000000, 0004000000000, + 0010000000000, 0020000000000, 0040000000000, + 0100000000000, 0200000000000, 0400000000000 + }; + +const DEV_CHAR dev_table[] = { + { "729", 0 }, + { "TAPE", 0 }, + { "7289", DEV_7289 }, + { "DRUM", DEV_7289 }, + { "7631", DEV_7909|DEV_7631 }, + { "FILE", DEV_7909|DEV_7631 }, + { "7750", DEV_7909|DEV_7750 }, + { "COMM", DEV_7909|DEV_7750 }, + { NULL }, + }; + +const char *sel_name[] = { + "UNK", "RDS", "WRS", "SNS", "CTL", "FMT", "UNK", "UNK", + "WEF", "WBT", "BSR", "BSF", "REW", "RUN", "SDN", "UNK" + }; + +/* Channel data structures */ + +UNIT ch_unit[NUM_CHAN] = { + { UDATA (&ch6_svc, 0, 0) }, + { UDATA (&ch6_svc, 0, 0) }, + { UDATA (&ch6_svc, 0, 0) }, + { UDATA (&ch6_svc, 0, 0) }, + { UDATA (&ch6_svc, 0, 0) }, + { UDATA (&ch6_svc, 0, 0) }, + { UDATA (&ch6_svc, 0, 0) }, + { UDATA (&ch6_svc, 0, 0) } + }; + +MTAB ch_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "TYPE", NULL, + NULL, &ch_show_type, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "ENABLED", + &ch_set_enable, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DISABLED", + &ch_set_disable, NULL, NULL }, + { 0 } + }; + +REG cha_reg[] = { + { ORDATA (STA, ch_sta[CH_A], 8) }, + { ORDATA (DSC, ch_dso[CH_A], 4) }, + { ORDATA (DSU, ch_dsu[CH_A], 9) }, + { ORDATA (NDSC, ch_ndso[CH_A], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_A], 9) }, + { ORDATA (FLAGS, ch_flags[CH_A], 30) }, + { ORDATA (IDF, ch_idf[CH_A], 2) }, + { ORDATA (OP, ch_op[CH_A], 5) }, + { ORDATA (CLC, ch_clc[CH_A], 16) }, + { ORDATA (WC, ch_wc[CH_A], 15) }, + { ORDATA (CA, ch_ca[CH_A], 16) }, + { ORDATA (AR, ch_ar[CH_A], 36) }, + { ORDATA (CND, ch_cnd[CH_A], 6), REG_HRO }, + { ORDATA (LCC, ch_lcc[CH_A], 6), REG_HRO }, + { ORDATA (SMS, ch_sms[CH_A], 7), REG_HRO }, + { 0 } + }; + +REG chb_reg[] = { + { ORDATA (STATE, ch_sta[CH_B], 8) }, + { ORDATA (DSC, ch_dso[CH_B], 4) }, + { ORDATA (DSU, ch_dsu[CH_B], 9) }, + { ORDATA (NDSC, ch_ndso[CH_B], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_B], 9) }, + { ORDATA (FLAGS, ch_flags[CH_B], 30) }, + { ORDATA (IDF, ch_idf[CH_B], 2) }, + { ORDATA (OP, ch_op[CH_B], 5) }, + { ORDATA (CLC, ch_clc[CH_B], 16) }, + { ORDATA (WC, ch_wc[CH_B], 15) }, + { ORDATA (CA, ch_ca[CH_B], 16) }, + { ORDATA (AR, ch_ar[CH_B], 36) }, + { ORDATA (CND, ch_cnd[CH_B], 6) }, + { ORDATA (LCC, ch_lcc[CH_B], 6) }, + { ORDATA (SMS, ch_sms[CH_B], 7) }, + { 0 } + }; + +REG chc_reg[] = { + { ORDATA (STATE, ch_sta[CH_C], 8) }, + { ORDATA (DSC, ch_dso[CH_C], 4) }, + { ORDATA (DSU, ch_dsu[CH_C], 9) }, + { ORDATA (NDSC, ch_ndso[CH_C], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_C], 9) }, + { ORDATA (FLAGS, ch_flags[CH_C], 30) }, + { ORDATA (IDF, ch_idf[CH_C], 2) }, + { ORDATA (OP, ch_op[CH_C], 5) }, + { ORDATA (CLC, ch_clc[CH_C], 16) }, + { ORDATA (WC, ch_wc[CH_C], 15) }, + { ORDATA (CA, ch_ca[CH_C], 16) }, + { ORDATA (AR, ch_ar[CH_C], 36) }, + { ORDATA (CND, ch_cnd[CH_C], 6) }, + { ORDATA (LCC, ch_lcc[CH_C], 6) }, + { ORDATA (SMS, ch_sms[CH_C], 7) }, + { 0 } + }; + +REG chd_reg[] = { + { ORDATA (STATE, ch_sta[CH_D], 8) }, + { ORDATA (DSC, ch_dso[CH_D], 4) }, + { ORDATA (DSU, ch_dsu[CH_D], 9) }, + { ORDATA (NDSC, ch_ndso[CH_D], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_D], 9) }, + { ORDATA (FLAGS, ch_flags[CH_D], 30) }, + { ORDATA (IDF, ch_idf[CH_D], 2) }, + { ORDATA (OP, ch_op[CH_D], 5) }, + { ORDATA (CLC, ch_clc[CH_D], 16) }, + { ORDATA (WC, ch_wc[CH_D], 15) }, + { ORDATA (CA, ch_ca[CH_D], 16) }, + { ORDATA (AR, ch_ar[CH_D], 36) }, + { ORDATA (CND, ch_cnd[CH_D], 6) }, + { ORDATA (LCC, ch_lcc[CH_D], 6) }, + { ORDATA (SMS, ch_sms[CH_D], 7) }, + { 0 } + }; + +REG che_reg[] = { + { ORDATA (STATE, ch_sta[CH_E], 8) }, + { ORDATA (DSC, ch_dso[CH_E], 4) }, + { ORDATA (DSU, ch_dsu[CH_E], 9) }, + { ORDATA (NDSC, ch_ndso[CH_E], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_E], 9) }, + { ORDATA (FLAGS, ch_flags[CH_E], 30) }, + { ORDATA (IDF, ch_idf[CH_E], 2) }, + { ORDATA (OP, ch_op[CH_E], 5) }, + { ORDATA (CLC, ch_clc[CH_E], 16) }, + { ORDATA (WC, ch_wc[CH_E], 15) }, + { ORDATA (CA, ch_ca[CH_E], 16) }, + { ORDATA (AR, ch_ar[CH_E], 36) }, + { ORDATA (CND, ch_cnd[CH_E], 6) }, + { ORDATA (LCC, ch_lcc[CH_E], 6) }, + { ORDATA (SMS, ch_sms[CH_E], 7) }, + { 0 } + }; + +REG chf_reg[] = { + { ORDATA (STATE, ch_sta[CH_F], 8) }, + { ORDATA (DSC, ch_dso[CH_F], 4) }, + { ORDATA (DSU, ch_dsu[CH_F], 9) }, + { ORDATA (NDSC, ch_ndso[CH_F], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_F], 9) }, + { ORDATA (FLAGS, ch_flags[CH_F], 30) }, + { ORDATA (IDF, ch_idf[CH_F], 2) }, + { ORDATA (OP, ch_op[CH_F], 5) }, + { ORDATA (CLC, ch_clc[CH_F], 16) }, + { ORDATA (WC, ch_wc[CH_F], 15) }, + { ORDATA (CA, ch_ca[CH_F], 16) }, + { ORDATA (AR, ch_ar[CH_F], 36) }, + { ORDATA (CND, ch_cnd[CH_F], 6) }, + { ORDATA (LCC, ch_lcc[CH_F], 6) }, + { ORDATA (SMS, ch_sms[CH_F], 7) }, + { 0 } + }; + +REG chg_reg[] = { + { ORDATA (STATE, ch_sta[CH_G], 8) }, + { ORDATA (DSC, ch_dso[CH_G], 4) }, + { ORDATA (DSU, ch_dsu[CH_G], 9) }, + { ORDATA (NDSC, ch_ndso[CH_G], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_G], 9) }, + { ORDATA (FLAGS, ch_flags[CH_G], 30) }, + { ORDATA (IDF, ch_idf[CH_G], 2) }, + { ORDATA (OP, ch_op[CH_G], 5) }, + { ORDATA (CLC, ch_clc[CH_G], 16) }, + { ORDATA (WC, ch_wc[CH_G], 15) }, + { ORDATA (CA, ch_ca[CH_G], 16) }, + { ORDATA (AR, ch_ar[CH_G], 36) }, + { ORDATA (CND, ch_cnd[CH_G], 6) }, + { ORDATA (LCC, ch_lcc[CH_G], 6) }, + { ORDATA (SMS, ch_sms[CH_G], 7) }, + { 0 } + }; + +REG chh_reg[] = { + { ORDATA (STATE, ch_sta[CH_H], 8) }, + { ORDATA (DSC, ch_dso[CH_H], 4) }, + { ORDATA (DSU, ch_dsu[CH_H], 9) }, + { ORDATA (NDSC, ch_ndso[CH_H], 4) }, + { ORDATA (NDSU, ch_ndsu[CH_H],9) }, + { ORDATA (FLAGS, ch_flags[CH_H], 30) }, + { ORDATA (IDF, ch_idf[CH_H], 2) }, + { ORDATA (OP, ch_op[CH_H], 5) }, + { ORDATA (CLC, ch_clc[CH_H], 16) }, + { ORDATA (WC, ch_wc[CH_H], 15) }, + { ORDATA (CA, ch_ca[CH_H], 16) }, + { ORDATA (AR, ch_ar[CH_H], 36) }, + { ORDATA (CND, ch_cnd[CH_H], 6) }, + { ORDATA (LCC, ch_lcc[CH_H], 6) }, + { ORDATA (SMS, ch_sms[CH_H], 7) }, + { 0 } + }; + +DEVICE ch_dev[NUM_CHAN] = { + { + "CHANA", &ch_unit[CH_A], cha_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, 0 + }, + { + "CHANB", &ch_unit[CH_B], chb_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }, + { + "CHANC", &ch_unit[CH_C], chc_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }, + { + "CHAND", &ch_unit[CH_D], chd_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }, + { + "CHANE", &ch_unit[CH_E], che_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }, + { + "CHANF", &ch_unit[CH_F], chf_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }, + { + "CHANG", &ch_unit[CH_G], chg_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }, + { + "CHANH", &ch_unit[CH_H], chh_reg, ch_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &ch_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + } + }; + +/* 7607 channel overview + + Channel variables: + + ch_sta channel state + ch_dso, ch_dsu operation and unit for current data select + ch_ndso, ch_ndsu operation and unit for current non-data select + ch_clc current location counter + ch_ca memory addres + ch_wc word count + ch_op channel opcode (bits ) + ch_flags channel flags + + States of a channel + + IDLE - channel is not in operation + + RDS, WDS: -> DSW if device is idle, schedule device + device timeout drives next transition + -> stall if device is busy + repeat until device is idle + other I/O: -> NDS if device is idle, schedule device + device timeout drives next transition + -> stall if device is busy + repeat until device is idle + chan reset: -> IDLE + + PDS (PNDS) - channel is polling device to start data (non-data) select + + chan timeout: -> DSW (NDS) if device is idle + device timeout drives next transition + -> no change if device is busy, schedule channel + chan reset: -> IDLE + + DSW - channel is waiting for channel start command + + dev timeout: -> IDLE if no stacked non-data select + -> PNDS if stacked non-data select + channel timeout drives next transition + start chan: -> DSX if chan program transfers data + device timeout drives next transition + -> IDLE if channel disconnects, no stacked NDS + -> PNDS if channel disconnects, stacked NDS + channel timeout drives next transition + chan reset: -> IDLE + + DSX - channel is executing data select + + dev timeout: -> DSX if transfer not complete, reschedule device + device timeout drives next transition + -> DSW if channel command completes, CHF_LDW set + -> IDLE if transfer complete, no stacked NDS, or + if channel command completes, CHF_LDW clear + -> PNDS if channel disconnects, stacked NDS + channel timeout drives next transition + start chan: -> DSX with CHF_LDW, CPU stall + chan reset: -> IDLE + + NDS - channel is executing non-data select + + dev timeout: -> IDLE if transfer complete, no stacked DS + -> PDS if channel disconnects, stacked DS + channel timeout drives next transition + chan reset: -> IDLE + + The channel has two interfaces to a device. The select routine: + + dev_select (uint32 ch, uint32 sel, uint32 unit) + + Returns can include device errors and ERR_STALL. If ERR_STALL, the + device is busy. For I/O instructions, ERR_STALL stalls execution of + the instruction until the device is not busy. For stacked command + polls, ERR_STALL causes the poll to be repeated after a delay. + + The device write routine is used to place output data in the device + write buffer. + + Channel transfers are driven by the channel. When a device needs to + read or write data, it sets a channel request in ch_req. The channel + process transfers the data and updates channel control parameters + accordingly. Note that the channel may disconnect; in this case, the + transfer completes 'correctly' from the point of view of the device. + + The channel transfer commands (IOxT) require the channel to 'hold' + a new channel command in anticipation of the current transfer. If + the channel is currently executing (CH6S_DSX) and a channel start + is issued by the CPU, a 'start pending' flag is set and the CPU is + stalled. When the channel reaches the end of an IOxT command, it + checks the 'start pending' flag. If the flag is set, the channel + sets itself to waiting and then requeues itself for one cycle later. + The CPU tries the channel start, sees that the channel is waiting, + and issues the new channel command. + + state op device channel + + IDLE RDS,WDS start I/O ->DSW + + DSW LCHx (timed wait) ->DSX + + DSX -- timeout, req svc + (timed wait) transfer word + timeout, req svc + (timed wait) + LCHx, stalls : + timeout, EOR/EOC IOxT: ->DSW, resched + DSW LCHx (timed wait) ->DSX, etc + + 7909 channel overview + + Channel variables: + + ch_sta channel state + ch_clc current location counter + ch_ca memory addres + ch_wc word count + ch_op channel opcode (bits ) + ch_sms status mask + ch_cond interrupt conditions + ch_lcc control counter + ch_flags channel flags + + States of a channel + + IDLE - channel is not in operation + + RDCx, SDCx, interrupt -> DSX + + DSX - channel is executing data select + + TWT, WTR -> IDLE + + The 7909 is more capable than the 7607 but also simpler in some ways. + It has many more instructions, built in counters and status checking, + and interrupts. But it has only two states and no concept of records. + + The 7909 read process is driven by the device: + + channel CTLR/SNS: send select + device: schedule timeout + device timeout: device to AR, request channel + channel: AR to memory + device timeout: device to AR, request channel + channel: AR to memory + : + device timeout: set end, request channel + channel: disconnect on CPYD, send STOP + + The 7909 write process is also driven by the device: + + channel CTL/CTLW: send select + device: schedule timeout, request channel + channel: memory to output buffer + device timeout: output buffer to device, request channel + channel: memory to output buffer + device timeout: output buffer to device, request channel + : + channel: memory to output buffer + device timeout: output buffer to device, set end, request channel + channel: disconnect on CPYD, send STOP + + For both reads and writes, devices must implement an 'interblock' or + 'interrecord' state that is long enough for the channel to see the + end, disconnect, and send a stop signal. +*/ + +/* Data select - called by RDS or WDS instructions - 7607/7289 only + + - Channel is from address and has been corrected + - Channel must be an enabled 7607 + - If data select already in use, stall CPU + - If non-data select is a write end-of-file, stall CPU + - If channel is busy, stack command + - Otherwise, start IO, set channel to waiting */ + +t_stat ch_op_ds (uint32 ch, uint32 ds, uint32 unit) +{ +t_stat r; + +if (ch >= NUM_CHAN) return STOP_NXCHN; /* invalid arg? */ +if (ch_dev[ch].flags & DEV_DIS) return STOP_NXCHN; /* disabled? stop */ +if (ch_dev[ch].flags & DEV_7909) return STOP_7909; /* 7909? stop */ +if (ch_dso[ch]) return ERR_STALL; /* DS in use? */ +if (ch_ndso[ch] == CHSL_WEF) return ERR_STALL; /* NDS = WEF? */ +if (ch_sta[ch] == CHXS_IDLE) { /* chan idle? */ + r = ch6_sel (ch, ds, unit, CH6S_DSW); /* select device */ + if (r != SCPE_OK) return r; + } +ch_dso[ch] = ds; /* set command, unit */ +ch_dsu[ch] = unit; +ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_CMD); /* clear flags */ +ch_idf[ch] = 0; +return SCPE_OK; +} + +/* Non-data select - called by BSR, BSF, WEF, REW, RUN, SDS instructions - 7607 only + + - Channel is from address and has been corrected + - Channel must be an enabled 7607 + - If non-data select already in use, stall CPU + - If data select is card or printer, stall CPU + - If channel is busy, stack command + - Otherwise, start IO, set channel to waiting */ + +t_stat ch_op_nds (uint32 ch, uint32 nds, uint32 unit) +{ +DEVICE *dptr; +t_stat r; + +if (ch >= NUM_CHAN) return STOP_NXCHN; /* invalid arg? */ +if (ch_dev[ch].flags & DEV_DIS) return STOP_NXCHN; /* disabled? stop */ +if (ch_dev[ch].flags & DEV_7909) return STOP_7909; /* 7909? stop */ +if (ch_ndso[ch]) return ERR_STALL; /* NDS in use? */ +if (ch_dso[ch] && (dptr = ch_find_dev (ch, ch_dsu[ch])) /* DS, cd or lpt? */ + && (dptr->flags & DEV_CDLP)) return ERR_STALL; +if (ch_sta[ch] == CHXS_IDLE) { /* chan idle? */ + r = ch6_sel (ch, nds, unit, CH6S_NDS); /* select device */ + if (r != SCPE_OK) return r; + } +ch_ndso[ch] = nds; /* set command, unit */ +ch_ndsu[ch] = unit; +return SCPE_OK; +} + +/* End of data select - called from channel - 7607/7289 only + + - If executing, set command trap flag + - Set channel idle + - If stacked nds, set up immediate channel timeout */ + +t_stat ch6_end_ds (uint32 ch) +{ +if (ch >= NUM_CHAN) return STOP_NXCHN; /* invalid arg? */ +ch_dso[ch] = ch_dsu[ch] = 0; /* no data select */ +if (ch_ndso[ch]) { /* stacked non-data sel? */ + sim_activate (ch_dev[ch].units, 0); /* immediate poll */ + ch_sta[ch] = CH6S_PNDS; /* state = polling */ + } +else ch_sta[ch] = CHXS_IDLE; /* else state = idle */ +return SCPE_OK; +} + +/* End of non-data select - called from I/O device completion - 7607/7289 only + + - Set channel idle + - If stacked ds, set up immediate channel timeout */ + +t_stat ch6_end_nds (uint32 ch) +{ +if (ch >= NUM_CHAN) return STOP_NXCHN; /* invalid arg? */ +ch_ndso[ch] = ch_ndsu[ch] = 0; /* no non-data select */ +if (ch_dso[ch]) { /* stacked data sel? */ + sim_activate (ch_dev[ch].units, 0); /* immediate poll */ + ch_sta[ch] = CH6S_PDS; /* state = polling */ + } +else ch_sta[ch] = CHXS_IDLE; /* else state = idle */ +return SCPE_OK; +} + +/* Send select to device - 7607/7289 only */ + +t_stat ch6_sel (uint32 ch, uint32 sel, uint32 unit, uint32 sta) +{ +DEVICE *dptr; +DIB *dibp; +t_stat r; + +if (ch >= NUM_CHAN) return STOP_NXCHN; /* invalid arg? */ +dptr = ch_find_dev (ch, unit); /* find device */ +if (dptr == NULL) return STOP_NXDEV; /* invalid device? */ +dibp = (DIB *) dptr->ctxt; +r = dibp->chsel (ch, sel, unit); /* select device */ +if (r == SCPE_OK) ch_sta[ch] = sta; /* set status */ +return r; +} + +/* Channel unit service - called to start stacked command - 7607 only */ + +t_stat ch6_svc (UNIT *uptr) +{ +uint32 ch = uptr - &ch_unit[0]; /* get channel */ +t_stat r; + +if (ch >= NUM_CHAN) return SCPE_IERR; /* invalid chan? */ +switch (ch_sta[ch]) { /* case on state */ + + case CH6S_PDS: /* polling for ds */ + r = ch6_sel (ch, ch_dso[ch], ch_dsu[ch], CH6S_DSW); + break; + + case CH6S_PNDS: /* polling for nds */ + r = ch6_sel (ch, ch_ndso[ch], ch_ndsu[ch], CH6S_NDS); + break; + + default: + return SCPE_OK; + } + +if (r == ERR_STALL) { /* stalled? */ + sim_activate (uptr, ch_tpoll); /* continue poll */ + return SCPE_OK; + } +return r; +} + +/* Map channel and unit number to device - all channels */ + +DEVICE *ch_find_dev (uint32 ch, uint32 unit) +{ +if (ch >= NUM_CHAN) return NULL; /* invalid arg? */ +if (ch_dev[ch].flags & (DEV_7909|DEV_7289)) return ch2dev[ch]; +unit = unit & 0777; +if (((unit >= U_MTBCD) && (unit <= (U_MTBCD + MT_NUMDR))) || + ((unit >= U_MTBIN) && (unit <= (U_MTBIN + MT_NUMDR)))) + return ch2dev[ch]; +if (ch != 0) return NULL; +if (unit == U_CDR) return &cdr_dev; +if (unit == U_CDP) return &cdp_dev; +if ((unit == U_LPBCD) || (unit == U_LPBIN)) return &lpt_dev; +return NULL; +} + +/* Start channel - channel is from opcode + + 7607: channel should have a data select operation pending (DSW state) + 7909: channel should be idle (IDLE state) */ + +t_stat ch_op_start (uint32 ch, uint32 clc, t_bool reset) +{ +t_uint64 ir; +t_stat r; + +clc = clc | data_base; /* add A/B select */ +if (ch >= NUM_CHAN) return STOP_NXCHN; /* invalid argument? */ +if (ch_dev[ch].flags & DEV_DIS) return STOP_NXCHN; /* disabled? stop */ +if (ch_dev[ch].flags & DEV_7909) { /* 7909? */ + if (ch_sta[ch] != CHXS_IDLE) return ERR_STALL; /* must be idle */ + if (reset) { /* RDCx? */ + ch_cnd[ch] = 0; /* clear conditions */ + ch_clc[ch] = clc; /* set clc */ + } + else { /* SDCx */ + if (BIT_TST (chtr_enab, CHTR_V_TWT + ch) && /* pending trap? */ + (ch_flags[ch] & CHF_TWT)) return ERR_STALL; + ch_clc[ch] = ch_ca[ch] & CHAMASK; /* finish WTR, TWT */ + } + ch_flags[ch] &= ~CHF_CLR_7909; /* clear flags, not IP */ + ch_idf[ch] = 0; + ch_sta[ch] = CHXS_DSX; /* set state */ + return ch9_new_cmd (ch); /* start executing */ + } + /* 7607, 7289 */ +if (reset) { /* reset? */ + if (ch_sta[ch] == CHXS_DSX) ch_sta[ch] = CH6S_DSW; + ch_flags[ch] &= ~(CHF_LDW|CHF_EOR|CHF_TRC|CHF_CMD); + ch_idf[ch] = 0; + } + +switch (ch_sta[ch]) { /* case on chan state */ + + case CHXS_IDLE: /* idle */ + ind_ioc = 1; /* IO check */ + ir = ReadP (clc); /* get chan word */ + ch_clc[ch] = CHAINC (clc); /* incr chan pc */ + ch_wc[ch] = GET_DEC (ir); /* get word cnt */ + ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */ + ch_op[ch] = (GET_OPD (ir) << 1) | /* get opcode */ + ((((uint32) ir) & CH6I_NST)? 1: 0); /* plus 'no store' */ + break; + + case CH6S_PNDS: /* NDS polling */ + case CH6S_PDS: /* DS polling */ + case CH6S_NDS: /* NDS executing */ + return ERR_STALL; /* wait it out */ + + case CH6S_DSW: /* expecting command */ + ch_sta[ch] = CHXS_DSX; /* update state */ + if (ch_dev[ch].flags & DEV_7289) { /* drum channel? */ + ir = ReadP (clc); /* read addr */ + ch_clc[ch] = CHAINC (clc); /* incr chan pc */ + if (r = ch9_wr (ch, ir, 0)) return r; /* write to dev */ + } + else ch_clc[ch] = clc; /* set clc */ + return ch6_new_cmd (ch, TRUE); /* start channel */ + + case CHXS_DSX: /* executing */ + ch_flags[ch] = ch_flags[ch] | CHF_LDW; /* flag pending LCH */ + return ERR_STALL; /* stall */ + } + +return SCPE_OK; +} + +/* Store channel + + 7607/7289 stores op,ca,nostore,clc + 7909 stores clc,,ca */ + +t_stat ch_op_store (uint32 ch, t_uint64 *dat) +{ +if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS)) return STOP_NXCHN; +if (ch_dev[ch].flags & DEV_7909) + *dat = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) | + (((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_ADDR); +else *dat = (((t_uint64) ch_clc[ch] & CHAMASK) << INST_V_DEC) | + (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_ADDR) | + (((t_uint64) (ch_op[ch] & 1)) << 16) | + (((t_uint64) (ch_op[ch] & 016)) << 32); +return SCPE_OK; +} + +/* Store channel diagnostic + + 7607 is undefined + 7289 stores IOC+??? + 7909 stores 7909 lcc+flags */ + +t_stat ch_op_store_diag (uint32 ch, t_uint64 *dat) +{ +if ((ch >= NUM_CHAN) || (ch_dev[ch].flags & DEV_DIS)) return STOP_NXCHN; +if (ch_flags[ch] & DEV_7289) *dat = ind_ioc? SIGN: 0; +else if (ch_flags[ch] & DEV_7909) *dat = + (((t_uint64) (ch_lcc[ch] & CHF_M_LCC)) << CHF_V_LCC) | + (ch_flags[ch] & CHF_SDC_7909); +else *dat = 0; +return SCPE_OK; +} + +/* Reset data channel + + 7607 responds to RDC + 7909 responds to RIC */ + +t_stat ch_op_reset (uint32 ch, t_bool ch7909) +{ +DEVICE *dptr; + +if (ch >= NUM_CHAN) return STOP_NXCHN; /* invalid argument? */ +if (ch_dev[ch].flags & DEV_DIS) return SCPE_OK; /* disabled? ok */ +if (ch_dev[ch].flags & DEV_7909) { /* 7909? */ + if (!ch7909) return SCPE_OK; /* wrong reset is NOP */ + dptr = ch2dev[ch]; /* get device */ + } +else { /* 7607, 7289 */ + if (ch7909) return STOP_NT7909; /* wrong reset is err */ + dptr = ch_find_dev (ch, ch_ndsu[ch]); /* find device */ + } +ch_reset (&ch_dev[ch]); /* reset channel */ +if (dptr && dptr->reset) dptr->reset (dptr); /* reset device */ +return SCPE_OK; +} + +/* Channel process - called from main CPU loop. If the channel is unable + to get a valid command, it will reschedule itself for the next cycle. + + The read process is basically synchronous with the device timeout routine. + The device requests the channel and supplies the word to be stored in memory. + In the next time slot, the channel stores the word in memory. */ + +t_stat ch_proc (uint32 ch) +{ +t_stat r; + +if (ch >= NUM_CHAN) return SCPE_IERR; /* bad channel? */ +ch_req &= ~REQ_CH (ch); /* clear request */ +if (ch_dev[ch].flags & DEV_DIS) return SCPE_IERR; /* disabled? */ +if (ch_dev[ch].flags & DEV_7909) { /* 7909 */ + + t_uint64 sr; + uint32 csel, sc, tval, mask, ta; + t_bool xfr; + + if (ch_flags[ch] & CHF_IRQ) { /* interrupt? */ + ta = CHINT_CHA_SAV + (ch << 1); /* save location */ + if (ch_sta[ch] == CHXS_IDLE) /* waiting? */ + sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) | + ((t_uint64) ch_clc[ch] & CHAMASK); /* save CLC */ + else sr = (((t_uint64) ch_ca[ch] & CHAMASK) << INST_V_DEC) | + ((t_uint64) CHAINC (ch_clc[ch])); /* no, save CLC+1 */ + ch_sta[ch] = CHXS_DSX; /* set running */ + ch_flags[ch] = (ch_flags[ch] | CHF_INT) & /* set intr state */ + ~(CHF_IRQ|CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS); /* clr flags */ + WriteP (ta, sr); /* write ca,,clc */ + sr = ReadP (ta + 1); /* get chan cmd */ + return ch9_exec_cmd (ch, sr); /* exec cmd */ + } + + switch (ch_op[ch] & CH9_OPMASK) { /* switch on op */ + + case CH9_TWT: /* transfer of TWT */ + case CH9_WTR: /* transfer of WTR */ + case CH9_TCH: /* transfer */ + ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */ + break; + + case CH9_TDC: /* decr & transfer */ + if (ch_lcc[ch] != 0) { /* counter != 0? */ + ch_lcc[ch]--; /* decr counter */ + ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */ + } + break; + + case CH9_TCM: /* transfer on cond */ + csel = CH9D_COND (ch_wc[ch]); + mask = CH9D_MASK (ch_wc[ch]); + if (csel == 7) xfr = (mask == 0); /* C = 7? mask mbz */ + else { /* C = 0..6 */ + if (csel == 0) tval = ch_cnd[ch]; /* C = 0? test cond */ + else tval = (uint32) (ch_ar[ch] >> (6 * (6 - csel))) & 077; + if (ch_wc[ch] & CH9D_B11) + xfr = ((tval & mask) == mask); + else xfr = (tval == mask); + } + if (xfr) ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */ + break; + + case CH9_LIP: /* leave interrupt */ + ta = CHINT_CHA_SAV + (ch << 1); /* save location */ + ch_flags[ch] &= ~(CHF_INT|CHF_IRQ); /* clear intr */ + ch_cnd[ch] = 0; /* clear channel cond */ + ch_clc[ch] = (uint32) ReadP (ta) & CHAMASK; + break; + + case CH9_LIPT: /* leave intr, transfer */ + ch_flags[ch] &= ~(CHF_INT|CHF_IRQ); /* clear intr */ + ch_cnd[ch] = 0; /* clear channel cond */ + ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change CLC */ + break; + + case CH9_LAR: /* load assembly reg */ + ch_ar[ch] = ReadP (ch_ca[ch]); + break; + + case CH9_SAR: /* store assembly reg */ + WriteP (ch_ca[ch], ch_ar[ch]); + break; + + case CH9_SMS: /* load SMS reg */ + ch_sms[ch] = CH9A_SMS (ch_ca[ch]); /* from eff addr */ + if (!(ch_sms[ch] & CHSMS_IATN1) && /* atn inhbit off */ + (ch_flags[ch] & CHF_ATN1)) /* and atn pending? */ + ch9_eval_int (ch, 0); /* force int eval */ + break; + + case CH9_LCC: /* load control cntr */ + ch_lcc[ch] = CH9A_LCC (ch_ca[ch]); /* from eff addr */ + break; + + case CH9_ICC: /* insert control cntr */ + case CH9_ICCA: + csel = CH9D_COND (ch_wc[ch]); /* get C */ + if (csel == 0) ch_ar[ch] = /* C = 0? read SMS */ + (ch_ar[ch] & 0777777770000) | ((t_uint64) ch_sms[ch]); + else if (csel < 7) { /* else read cond cntr */ + sc = 6 * (6 - csel); + ch_ar[ch] = (ch_ar[ch] & ~(((t_uint64) 077) << sc)) | + (((t_uint64) ch_lcc[ch]) << sc); + } + break; + + case CH9_XMT: /* transmit */ + if (ch_wc[ch] == 0) break; + sr = ReadP (ch_clc[ch]); /* next word */ + WriteP (ch_ca[ch], sr); + ch_clc[ch] = CHAINC (ch_clc[ch]); /* incr pointers */ + ch_ca[ch] = CHAINC (ch_ca[ch]); + ch_wc[ch] = ch_wc[ch] - 1; /* decr count */ + ch_req |= REQ_CH (ch); /* go again */ + return SCPE_OK; + + case CH9_SNS: /* sense */ + if (r = ch9_sel (ch, CHSL_SNS)) return r; /* send sense to dev */ + ch_flags[ch] |= CHF_PRD; /* prepare to read */ + break; /* next command */ + + case CH9_CTL: + case CH9_CTLR: + case CH9_CTLW: /* control */ + if (((ch_wc[ch] & CH9D_NST) == 0) && /* N = 0 and */ + !(ch_flags[ch] & CHF_EOR)) { /* end not set? */ + sr = ReadP (ch_ca[ch]); + ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */ + return ch9_wr (ch, sr, 0); /* write ctrl wd */ + } + ch_flags[ch] &= ~CHF_EOR; /* clear end */ + if (ch_op[ch] == CH9_CTLR) { /* CTLR? */ + if (r = ch9_sel (ch, CHSL_RDS)) return r; /* send read sel */ + ch_flags[ch] |= CHF_PRD; /* prep to read */ + ch_idf[ch] = 0; + } + else if (ch_op[ch] == CH9_CTLW) { /* CTLW? */ + if (r = ch9_sel (ch, CHSL_WRS)) return r; /* end write sel */ + ch_flags[ch] |= CHF_PWR; /* prep to write */ + } + break; + + case CH9_CPYD: /* copy & disc */ + if ((ch_wc[ch] == 0) || (ch_flags[ch] & CHF_EOR)) { /* wc == 0 or EOR? */ + if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) { + ch_flags[ch] &= ~(CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS); + if (r = ch9_wr (ch, 0, CH9DF_STOP)) return r; /* send stop */ + } + if (ch_flags[ch] & CHF_EOR) { /* EOR? */ + ch_flags[ch] &= ~CHF_EOR; /* clear flag */ + break; /* new command */ + } + return SCPE_OK; /* wait for end */ + } + if (ch_flags[ch] & CHF_RDS) /* read? */ + return ch9_rd_putw (ch); + return ch9_wr_getw (ch); /* no, write */ + + case CH9_CPYP: /* anything to do? */ + if (ch_wc[ch] == 0) break; /* (new, wc = 0) next */ + if (ch_flags[ch] & CHF_EOR) /* end? */ + ch_flags[ch] &= ~CHF_EOR; /* ignore */ + else if (ch_flags[ch] & CHF_RDS) /* read? */ + ch9_rd_putw (ch); + else if (r = ch9_wr_getw (ch)) return r; /* no, write */ + if (ch_wc[ch] == 0) break; /* done? get next */ + return SCPE_OK; /* more to do */ + + default: + return STOP_ILLIOP; + } + + return ch9_new_cmd (ch); /* next command */ + } + +else if (ch_flags[ch] & CHF_RDS) { /* 7607 read? */ + + if (ch_sta[ch] != CHXS_DSX) return ch6_end_ds (ch); /* chan exec? no, disc */ + switch (ch_op[ch] & CH6_OPMASK) { /* switch on op */ + + case CH6_TCH: /* transfer */ + ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change clc */ + return ch6_new_cmd (ch, FALSE); /* unpack new cmd */ + + case CH6_IOCD: /* IOCD */ + if (ch_wc[ch]) { /* wc > 0? */ + if (ch6_rd_putw (ch)) return SCPE_OK; /* store; more? cont */ + } + return ch6_end_ds (ch); /* no, disconnect */ + + case CH6_IOCP: /* IOCP */ + if (ch_wc[ch]) { /* wc > 0? */ + if (ch6_rd_putw (ch)) return SCPE_OK; /* store; more? cont */ + } + return ch6_new_cmd (ch, FALSE); /* unpack new cmd */ + + case CH6_IOCT: /* IOCT */ + if (ch_wc[ch]) { /* wc > 0? */ + if (ch6_rd_putw (ch)) return SCPE_OK; /* store; more? cont */ + } + return ch6_ioxt (ch); /* unstall or disc */ + + case CH6_IOSP: /* IOSP */ + if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ + return ch6_new_cmd (ch, FALSE); /* get next cmd */ + } + if (ch_wc[ch]) { /* wc > 0? */ + if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR)) + return SCPE_OK; /* yes, store; more? */ + ch6_iosp_cclr (ch); /* cond clear eor */ + } + return ch6_new_cmd (ch, FALSE); /* next cmd */ + + case CH6_IOST: /* IOST */ + if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ + return ch6_ioxt (ch); /* get next cmd */ + } + if (ch_wc[ch]) { /* wc > 0? */ + if (ch6_rd_putw (ch) && !(ch_flags[ch] & CHF_EOR)) + return SCPE_OK; /* yes, store; more? */ + ch6_iosp_cclr (ch); /* cond clear eor */ + } + return ch6_ioxt (ch); /* unstall or disc */ + + case CH6_IORP: /* IORP */ + if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ + return ch6_new_cmd (ch, FALSE); /* get next cmd */ + } + ch6_rd_putw (ch); /* store wd; ignore wc */ + if (ch_flags[ch] & CHF_EOR) { /* EOR? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ + return ch6_new_cmd (ch, FALSE); /* get next cmd */ + } + return SCPE_OK; /* done */ + + case CH6_IORT: /* IORT */ + if (ch_flags[ch] & CHF_EOR) { /* (new) EOR set? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ + return ch6_ioxt (ch); /* get next cmd */ + } + ch6_rd_putw (ch); /* store wd; ignore wc */ + if (ch_flags[ch] & CHF_EOR) { /* EOR? */ + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear flag */ + return ch6_ioxt (ch); /* unstall or disc */ + } + return SCPE_OK; /* done */ + + default: + return SCPE_IERR; + } /* end case */ + } /* end if read */ + +else { /* 7607 write */ + + if (ch_sta[ch] != CHXS_DSX) return ch6_end_ds (ch); /* chan exec? no, disc */ + switch (ch_op[ch] & CH6_OPMASK) { /* switch on op */ + + case CH6_TCH: /* transfer */ + ch_clc[ch] = ch_ca[ch] & CHAMASK; /* change clc */ + return ch6_new_cmd (ch, FALSE); /* unpack new cmd */ + + case CH6_IOCD: /* IOCD */ + if (ch_wc[ch]) { /* wc > 0? */ + if (r = ch6_wr_getw (ch, TRUE)) return r; /* send wd to dev; err? */ + if (ch_wc[ch]) return SCPE_OK; /* more to do? */ + } + return ch6_end_ds (ch); /* disconnect */ + + case CH6_IOCP: /* IOCP */ + case CH6_IOSP: /* IOSP */ + if (ch_wc[ch]) { /* wc > 0? */ + if (r = ch6_wr_getw (ch, FALSE)) return r; /* send wd to dev; err? */ + if (ch_wc[ch]) return SCPE_OK; /* more to do? */ + } + return ch6_new_cmd (ch, FALSE); /* get next cmd */ + + case CH6_IOCT: /* IOCT */ + case CH6_IOST: /* IOST */ + if (ch_wc[ch]) { /* wc > 0? */ + if (r = ch6_wr_getw (ch, FALSE)) return r; /* send wd to dev; err? */ + if (ch_wc[ch]) return SCPE_OK; /* more to do? */ + } + return ch6_ioxt (ch); /* get next cmd */ + + case CH6_IORP: /* IORP */ + if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) { /* not EOR? (cdp, lpt) */ + if (r = ch6_wr_getw (ch, TRUE)) return r; /* send wd to dev; err? */ + if (ch_wc[ch]) return SCPE_OK; /* more to do? */ + } + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear EOR */ + return ch6_new_cmd (ch, FALSE); /* get next cmd */ + + case CH6_IORT: /* IORT */ + if (!(ch_flags[ch] & CHF_EOR) && ch_wc[ch]) { /* not EOR? (cdp, lpt) */ + if (r = ch6_wr_getw (ch, TRUE)) return r; /* send wd to dev; err? */ + if (ch_wc[ch]) return SCPE_OK; /* more to do? */ + } + ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear EOR */ + return ch6_ioxt (ch); /* unstall or disc */ + + default: + return SCPE_IERR; + } /* end switch */ + } /* end else write */ +} + +/* 7607 channel support routines */ + +/* 7607 channel input routine - put one word to memory */ + +t_bool ch6_rd_putw (uint32 ch) +{ +if (ch_idf[ch] & CH6DF_EOR) ch_flags[ch] |= CHF_EOR; /* eor from dev? */ +else ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* set/clr chan eor */ +ch_idf[ch] = 0; /* clear eor, valid */ +if (ch_wc[ch]) { /* wc > 0? */ + if ((ch_op[ch] & 1) == 0) { /* do store? */ + WriteP (ch_ca[ch], ch_ar[ch]); + ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */ + } + ch_wc[ch] = ch_wc[ch] - 1; + } +return (ch_wc[ch]? TRUE: FALSE); +} + +/* 7607 channel output routine - get one word from memory */ + +t_stat ch6_wr_getw (uint32 ch, t_bool eorz) +{ +DEVICE *dptr; +DIB *dibp; +uint32 eorfl; + +ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clr eor */ +if (ch_wc[ch]) { + ch_ar[ch] = ReadP (ch_ca[ch]); /* get word */ + ch_ca[ch] = CHAINC (ch_ca[ch]); /* incr ca */ + ch_wc[ch] = ch_wc[ch] - 1; + } +else ch_ar[ch] = 0; +if (eorz && (ch_wc[ch] == 0)) eorfl = 1; /* eor on wc = 0? */ +else eorfl = 0; +dptr = ch_find_dev (ch, ch_dsu[ch]); /* find device */ +if (dptr && /* valid device? */ + (dibp = (DIB *) dptr->ctxt) && /* with DIB? */ + dibp->write) /* and write routine? */ + return dibp->write (ch, ch_ar[ch], eorfl); +return SCPE_IERR; /* huh? */ +} + +/* 7607 channel new command - on channel load, check for disconnects + + The protocol for new commands is as follows: + - If IOCD 0,,0, disconnect immediately + - If IOCT 0,,0 or IOST 0,,0 and loaded by RCHA, disconnect immediately + - If an effective NOP (TCH, IOCx 0,,0, IOSx 0,,0), force a channel + cycle to retire the channel comand as quickly as possible. + - If an IORx and EOR is set, force a channel cycle to retire the + channel command as quickly as possible. +*/ + +t_stat ch6_new_cmd (uint32 ch, t_bool ch_ld) +{ +t_uint64 ir; +uint32 op, t; + +ir = ReadP (t = ch_clc[ch]); /* read cmd */ +ch_wc[ch] = GET_DEC (ir); /* get word cnt */ +ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */ +op = GET_OPD (ir) << 1; /* get opcode */ +ch_op[ch] = op | ((((uint32) ir) & CH6I_NST)? 1: 0); /* plus 'no store' */ +if ((ir & CHI_IND) && (ch_wc[ch] || /* indirect? */ + ((op != CH6_IOCP) && (op != CH6_IOSP)))) { /* wc >0, or !IOxP? */ + t_uint64 sr = ReadP (ch_ca[ch] & AMASK); /* read indirect */ + ch_ca[ch] = ((uint32) sr) & ((cpu_model & I_CT)? PAMASK: AMASK); + } +if (hst_ch) cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0); +ch_clc[ch] = (ch_clc[ch] + 1) & AMASK; /* incr chan pc */ + +switch (op) { /* case on opcode */ + + case CH6_IOCD: /* IOCD */ + if (ch_wc[ch] == 0) ch6_end_ds (ch); /* wc 0? end now */ + break; + + case CH6_IOST: /* IOST */ + if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */ + ch_req |= REQ_CH (ch); + case CH6_IOCT: /* IOCT */ + if (ch_wc[ch] == 0) { /* wc 0? */ + if (ch_ld) ch6_end_ds (ch); /* load? end now */ + else ch_req |= REQ_CH (ch); /* else immed ch req */ + } + break; + + case CH6_IOSP: /* IOSP */ + if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */ + ch_req |= REQ_CH (ch); + case CH6_IOCP: /* IOCP */ + if (ch_wc[ch] == 0) ch_req |= REQ_CH (ch); /* wc 0? immed ch req */ + break; + + case CH6_IORT: /* IORT */ + case CH6_IORP: /* IORP */ + if (ch_flags[ch] & CHF_EOR) /* EOR set? immed ch req */ + ch_req |= REQ_CH (ch); + break; + + case CH6_TCH: /* TCH */ + ch_req |= REQ_CH (ch); /* immed ch req */ + break; + + default: /* all others */ + break; + } /* end case */ + +if (sim_brk_summ && sim_brk_test (t, SWMASK ('E'))) + return ch_bkpt (ch, t); +return SCPE_OK; +} + +/* 7607 channel IOxT: if LCH stall, set state back to DSW; else disconnect and trap */ + +t_stat ch6_ioxt (uint32 ch) +{ +if (ch_flags[ch] & CHF_LDW) { /* LCH cmd pending? */ + ch_flags[ch] &= ~CHF_LDW; /* clr pending flag */ + ch_sta[ch] = CH6S_DSW; /* unstall pending LCH */ + } +else { + ch_flags[ch] |= CHF_CMD; /* set cmd trap flag */ + ch6_end_ds (ch); /* disconnect */ + } +return SCPE_OK; +} + +/* 7607 conditionally clear EOR on IOSx completion */ + +void ch6_iosp_cclr (uint32 ch) +{ +uint32 i, op; + +if (ch_wc[ch] == 0) { /* wc = 0? */ + uint32 ccnt = 5; /* allow 5 for CPU */ + for (i = 0; i < NUM_CHAN; i++) { /* test channels */ + if (ch_sta[ch] != CHXS_DSX) continue; /* idle? skip */ + op = ch_op[ch] & ~1; /* get op */ + ccnt++; /* 1 per active ch */ + if ((op == CH6_IOCP) || (op == CH6_IORP) || /* 1 per proceed */ + (op == CH6_IOSP)) ccnt++; + } + if (ccnt <= 11) return; /* <= 11? ok */ + } +ch_flags[ch] = ch_flags[ch] & ~CHF_EOR; /* clear eor */ +return; +} + +/* 7607 external interface routines */ + +/* Input - store word, request channel input service */ + +t_stat ch6_req_rd (uint32 ch, uint32 unit, t_uint64 val, uint32 fl) +{ +if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ + if (ch_idf[ch] & CH6DF_VLD) ind_ioc = 1; /* overrun? */ + ch_idf[ch] = CH6DF_VLD; /* set ar valid */ + if (fl) ch_idf[ch] |= CH6DF_EOR; /* set eor if requested */ + ch_req |= REQ_CH (ch); /* request chan */ + ch_flags[ch] |= CHF_RDS; + ch_ar[ch] = val & DMASK; /* save data */ + } +return SCPE_OK; +} + +/* Disconnect on error */ + +t_stat ch6_err_disc (uint32 ch, uint32 unit, uint32 fl) +{ +if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ + ch_flags[ch] |= fl; /* set flag */ + return ch6_end_ds (ch); /* disconnect */ + } +return SCPE_OK; +} + +/* Output - request channel output service */ + +t_bool ch6_req_wr (uint32 ch, uint32 unit) +{ +if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ + ch_req |= REQ_CH (ch); + ch_flags[ch] &= ~CHF_RDS; + } +return SCPE_OK; +} + +/* Set/read channel flags */ + +uint32 ch6_set_flags (uint32 ch, uint32 unit, uint32 flags) +{ +if (ch6_qconn (ch, unit)) { /* ch conn to caller? */ + ch_flags[ch] = ch_flags[ch] | flags; + return ch_flags[ch]; + } +return 0; +} + +/* Channel connected to unit? */ + +t_bool ch6_qconn (uint32 ch, uint32 unit) +{ +if ((ch < NUM_CHAN) && /* valid chan */ + (ch_dsu[ch] == unit)) return TRUE; /* for right unit? */ +return FALSE; +} + +/* 7909 channel support routines */ + +/* 7909 channel input routine - put one word to memory */ + +t_stat ch9_rd_putw (uint32 ch) +{ +ch_idf[ch] = 0; /* invalidate */ +if (ch_wc[ch]) { /* wc > 0? */ + WriteP (ch_ca[ch], ch_ar[ch]); + ch_ca[ch] = CHAINC (ch_ca[ch]); + ch_wc[ch] = ch_wc[ch] - 1; + } +return SCPE_OK; +} + +/* 7909 channel output routine - get one word from memory */ + +t_stat ch9_wr_getw (uint32 ch) +{ +if (ch_wc[ch]) { + ch_ar[ch] = ReadP (ch_ca[ch]); /* get word */ + ch_ca[ch] = CHAINC (ch_ca[ch]); + ch_wc[ch] = ch_wc[ch] - 1; + } +else ch_ar[ch] = 0; +return ch9_wr (ch, ch_ar[ch], 0); /* write to device */ +} + +/* 7909 send select to device */ + +t_stat ch9_sel (uint32 ch, uint32 sel) +{ +DEVICE *dptr = ch2dev[ch]; +DIB *dibp; + +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp && dibp->chsel) return dibp->chsel (ch, sel, 0); +return SCPE_IERR; +} + +/* 7909 send word to device */ + +t_stat ch9_wr (uint32 ch, t_uint64 dat, uint32 fl) +{ +DEVICE *dptr = ch2dev[ch]; +DIB *dibp; + +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp && dibp->write) return dibp->write (ch, dat, fl); +return SCPE_IERR; +} + +/* 7909 channel new command */ + +t_stat ch9_new_cmd (uint32 ch) +{ +t_uint64 ir; +uint32 t; +t_stat r; + +ir = ReadP (t = ch_clc[ch]); /* read cmd */ +r = ch9_exec_cmd (ch, ir); /* exec cmd */ +if (ch_sta[ch] != CHXS_IDLE) /* chan running? */ + ch_clc[ch] = CHAINC (ch_clc[ch]); /* incr chan pc */ +if ((r == SCPE_OK) && sim_brk_summ && sim_brk_test (t, SWMASK ('E'))) + return ch_bkpt (ch, t); +return r; +} + +t_stat ch9_exec_cmd (uint32 ch, t_uint64 ir) +{ +uint32 op; + +ch_wc[ch] = GET_DEC (ir); /* get word cnt */ +ch_ca[ch] = ((uint32) ir) & CHAMASK; /* get address */ +op = (GET_OPD (ir) << 2); /* get opcode */ +ch_op[ch] = op | ((((uint32) ir) & 0200000)? 1: 0) | /* plus bit<19> */ + (((op & 010) && (ch_wc[ch] & 040000))? 2: 0); /* plus bit 3 if used */ +if (ir & CHI_IND) { /* indirect? */ + t_uint64 sr = ReadP (ch_ca[ch] & CHAMASK); /* read indirect */ + ch_ca[ch] = ((uint32) sr) & CHAMASK; /* get address */ + } +if (hst_ch) + cpu_ent_hist (ch_clc[ch] | ((ch + 1) << HIST_V_CH), ch_ca[ch], ir, 0); + +switch (ch_op[ch]) { /* check initial cond */ + + case CH9_LAR: /* misc processing */ + case CH9_SAR: + case CH9_ICC: + case CH9_ICCA: + case CH9_XMT: + case CH9_LCC: + case CH9_SMS: + if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) + ch9_eval_int (ch, CHINT_SEQC); /* not during data */ + /* fall through */ + case CH9_TCM: /* jumps */ + case CH9_TCH: + case CH9_TDC: + case CH9_LIPT: + case CH9_LIP: + ch_req |= REQ_CH (ch); /* process in chan */ + break; + + case CH9_CTL: /* control */ + case CH9_CTLR: + case CH9_CTLW: + if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) + ch9_eval_int (ch, CHINT_SEQC); /* not during data */ + ch_flags[ch] &= ~CHF_EOR; + if (ch_wc[ch] & CH9D_NST) ch_req |= REQ_CH (ch); /* N set? proc in chan */ + else return ch9_sel (ch, CHSL_CTL); /* sel, dev sets ch_req! */ + break; + + case CH9_SNS: /* sense */ + if (ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) + ch9_eval_int (ch, CHINT_SEQC); + ch_flags[ch] &= ~CHF_EOR; + ch_req |= REQ_CH (ch); /* process in chan */ + break; + + case CH9_CPYD: /* data transfers */ + case CH9_CPYP: + if ((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS)) == 0) + ch9_eval_int (ch, CHINT_SEQC); /* not unless data */ + if (ch_flags[ch] & CHF_PRD) ch_flags[ch] |= CHF_RDS; + else if (ch_flags[ch] & CHF_PWR) ch_flags[ch] |= CHF_WRS; + ch_flags[ch] &= ~(CHF_EOR|CHF_PRD|CHF_PWR); + if ((ch_op[ch] == CH9_CPYP) && (ch_wc[ch] == 0)) + ch_req |= REQ_CH (ch); /* CPYP x,,0? */ + break; /* dev sets ch_req! */ + + case CH9_WTR: /* wait */ + ch_sta[ch] = CHXS_IDLE; /* stop */ + break; + + case CH9_TWT: /* trap and wait */ + ch_sta[ch] = CHXS_IDLE; /* stop */ + ch_flags[ch] |= CHF_TWT; /* set trap */ + break; + + default: + return STOP_ILLIOP; + } + +return SCPE_OK; +} + +/* 7909 external interface routines */ + +/* Input - store word, request channel input service */ + +t_stat ch9_req_rd (uint32 ch, t_uint64 val) +{ +if (ch < NUM_CHAN) { /* valid chan? */ + if (ch_idf[ch] & CH9DF_VLD) ch9_set_ioc (ch); /* prev still valid? io chk */ + ch_idf[ch] = CH9DF_VLD; /* set ar valid */ + ch_req |= REQ_CH (ch); /* request chan */ + ch_ar[ch] = val & DMASK; /* save data */ + } +return SCPE_OK; +} + +/* Set attention */ + +void ch9_set_atn (uint32 ch) +{ +if (ch < NUM_CHAN) ch9_eval_int (ch, CHINT_ATN1); +return; +} + +/* Set IO check - UEND will occur at end - not recognized in int mode */ + +void ch9_set_ioc (uint32 ch) +{ +if ((ch < NUM_CHAN) && !(ch_flags[ch] & CHF_INT)) { + ind_ioc = 1; /* IO check */ + ch_flags[ch] |= CHF_IOC; /* ch IOC for end */ + } +return; +} + +/* Set end */ + +void ch9_set_end (uint32 ch, uint32 iflags) +{ +if (ch < NUM_CHAN) { /* valid chan? */ + ch_flags[ch] |= CHF_EOR; + ch9_eval_int (ch, iflags); + } +return; +} + +/* Test connected */ + +t_bool ch9_qconn (uint32 ch) +{ +if ((ch < NUM_CHAN) && (ch_sta[ch] == CHXS_DSX)) return TRUE; +return FALSE; +} + +/* Evaluate interrupts + + - Interrupt requests set flags in the channel flags word + - If an interrupt is not in progress, interrupt requests are evaluated + - If an interrupt request is found, the interruptable flags are + transferred to the channel condition register and cleared in + the channel flags + + This provides an effective stage of buffering for interrupt requests + that are not immediately serviced */ + +void ch9_eval_int (uint32 ch, uint32 iflags) +{ +uint32 ireq; + +ch_flags[ch] |= (iflags << CHF_V_COND); /* or into chan flags */ +if ((ch_flags[ch] & CHF_INT) == 0) { /* int not in prog? */ + ireq = ((ch_flags[ch] >> CHF_V_COND) & CHF_M_COND) & + ~(((ch_sms[ch] & CHSMS_IUEND)? CHINT_UEND: 0) | + ((ch_sms[ch] & CHSMS_IATN1)? CHINT_ATN1: 0) | + ((ch_sms[ch] & CHSMS_IATN2)? CHINT_ATN2: 0) | + ((ch_flags[ch] & (CHF_PRD|CHF_PWR|CHF_RDS|CHF_WRS))? CHINT_SEQC: 0)); + if (ireq) { /* int pending? */ + ch_cnd[ch] = ireq; /* set cond reg */ + ch_flags[ch] &= ~(ireq << CHF_V_COND); /* clear chan flags */ + ch_flags[ch] |= CHF_IRQ; /* set int req */ + ch_req |= REQ_CH (ch); /* request channel */ + } + } +return; +} + +/* Test for all channels idle */ + +t_bool ch_qidle (void) +{ +uint32 i; + +for (i = 0; i < NUM_CHAN; i++) { + if (ch_sta[i] != CHXS_IDLE) return FALSE; + } +return TRUE; +} + +/* Evaluate/execute channel traps */ + +uint32 chtr_eval (uint32 *decr) +{ +uint32 i, cme; + +if (!chtr_inht && !chtr_inhi && chtr_enab) { + if (BIT_TST (chtr_enab, CHTR_V_CLK) && chtr_clk) { /* clock trap? */ + if (decr) { /* exec? */ + chtr_clk = 0; /* clr flag */ + *decr = 0; + } + return CHTR_CLK_SAV; + } + for (i = 0; i < NUM_CHAN; i++) { /* loop thru chan */ + cme = BIT_TST (chtr_enab, CHTR_V_CME + i); /* cmd/eof enab? */ + if (cme && (ch_flags[i] & CHF_CMD)) { /* cmd enab and set? */ + if (decr) { /* exec? */ + ch_flags[i] &= ~CHF_CMD; /* clr flag */ + *decr = CHTR_F_CMD; + } + return (CHTR_CHA_SAV + (i << 1)); + } + if (cme && (ch_flags[i] & CHF_EOF)) { /* eof enab and set? */ + if (decr) { /* exec? */ + ch_flags[i] &= ~CHF_EOF; /* clr flag */ + *decr = CHTR_F_EOF; + } + return (CHTR_CHA_SAV + (i << 1)); + } + if (BIT_TST (chtr_enab, CHTR_V_TRC + i) && /* trc enab? */ + (ch_flags[i] & CHF_TRC)) { /* trc flag? */ + if (decr) { /* exec? */ + ch_flags[i] &= ~CHF_TRC; /* clr flag */ + *decr = CHTR_F_TRC; + } + return (CHTR_CHA_SAV + (i << 1)); + } /* end if BIT_TST */ + } /* end for */ + } /* end if !chtr_inht */ +if (decr) *decr = 0; +return 0; +} + +/* Channel reset */ + +t_stat ch_reset (DEVICE *dptr) +{ +uint32 ch = dptr - &ch_dev[0]; /* get channel */ + +if (ch == CH_A) ch2dev[ch] = &mt_dev[0]; /* channel A fixed */ +ch_sta[ch] = 0; +ch_flags[ch] = 0; +ch_idf[ch] = 0; +ch_dso[ch] = 0; +ch_dsu[ch] = 0; +ch_ndso[ch] = 0; +ch_ndsu[ch] = 0; +ch_op[ch] = 0; +ch_clc[ch] = 0; +ch_wc[ch] = 0; +ch_ca[ch] = 0; +ch_ar[ch] = 0; +ch_sms[ch] = 0; +ch_cnd[ch] = 0; +ch_lcc[ch] = 0; +sim_cancel (&ch_unit[ch]); +return SCPE_OK; +} + +/* Show channel type */ + +t_stat ch_show_type (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; + +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +if (dptr->flags & DEV_7909) fputs ("7909", st); +else if (dptr->flags & DEV_7289) fputs ("7289", st); +else fputs ("7607", st); +return SCPE_OK; +} + +/* Enable channel, assign device */ + +t_stat ch_set_enable (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr, *dptr1; +char gbuf[CBUFSIZE]; +uint32 i, ch; + +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +ch = dptr - &ch_dev[0]; +if ((ch == 0) || !(dptr->flags & DEV_DIS)) return SCPE_ARG; +if (cptr == NULL) cptr = "TAPE"; +get_glyph (cptr, gbuf, 0); +for (i = 0; dev_table[i].name; i++) { + if (strcmp (dev_table[i].name, gbuf) == 0) { + dptr1 = ch_map_flags (ch, dev_table[i].flags); + if (!dptr1 || !(dptr1->flags & DEV_DIS)) return SCPE_ARG; + dptr->flags &= ~(DEV_DIS|DEV_7909|DEV_7289|DEV_7750|DEV_7631); + dptr->flags |= dev_table[i].flags; + dptr1->flags &= ~DEV_DIS; + ch2dev[ch] = dptr1; + return reset_all (0); + } + } +return SCPE_ARG; +} + +/* Map device flags to device pointer */ + +DEVICE *ch_map_flags (uint32 ch, int32 fl) +{ +if (fl & DEV_7289) return &drm_dev; +if (!(fl & DEV_7909)) return &mt_dev[ch]; +if (fl & DEV_7631) return &dsk_dev; +if (fl & DEV_7750) return &com_dev; +return NULL; +} + +/* Set up channel map */ + +void ch_set_map (void) +{ +uint32 i; + +for (i = 0; i < NUM_CHAN; i++) { + if (ch_dev[i].flags & DEV_DIS) ch2dev[i] = NULL; + else ch2dev[i] = ch_map_flags (i, ch_dev[i].flags); + } +return; +} + +/* Disable channel, deassign device */ + +t_stat ch_set_disable (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr, *dptr1; +UNIT *uptr1; +uint32 i, ch; + +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +ch = dptr - &ch_dev[0]; +if ((ch == 0) || (dptr->flags & DEV_DIS) || (cptr != NULL)) return SCPE_ARG; +dptr1 = ch2dev[ch]; +if (dptr1 == NULL) return SCPE_IERR; +if (dptr1->units) { + for (i = 0; i < dptr1->numunits; i++) { + uptr1 = dptr1->units + i; + if (dptr1->detach) dptr1->detach (uptr1); + else detach_unit (uptr1); + } + } +dptr->flags &= ~(DEV_7909|DEV_7289); +dptr->flags |= DEV_DIS; +dptr1->flags |= DEV_DIS; +return reset_all (0); +} + +/* Show channel that device is on (tapes, 7289, 7909 only) */ + +t_stat ch_show_chan (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +uint32 i; + +dptr = find_dev_from_unit (uptr); +if (dptr) { + for (i = 0; i < NUM_CHAN; i++) { + if (ch2dev[i] == dptr) { + fprintf (st, "channel %c", 'A' + i); + return SCPE_OK; + } + } + } +fprintf (st, "not assigned to channel"); +return SCPE_OK; +} diff --git a/I7094/i7094_lp.c b/I7094/i7094_lp.c new file mode 100644 index 0000000..f8b7c0c --- /dev/null +++ b/I7094/i7094_lp.c @@ -0,0 +1,368 @@ +/* i7094_lp.c: IBM 716 line printer simulator + + Copyright (c) 2003-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt 716 line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + + Internally, the 7094 works only with column binary and is limited to + 72 columns of data. Each row of the printed line is represented by + 72b of data (two 36b words). A complete print line consists of 12 rows + (24 36b words). + + The printer can also echo part of what it prints, namely, the digit rows + plus the 8+3 and 8+4 combinations. This was intended for verification of + check printing. Echoed data is interspersed with output data in the + following order: + + output row 9 to row 1 + echo row "8+4" + output row 0 + echo row "8+3" + output row 11 + echo row 9 + output row 12 + echo row 8 to row 1 +*/ + +#include "i7094_defs.h" + +#define UNIT_V_CONS (UNIT_V_UF + 0) /* print to console */ +#define UNIT_CONS (1u << UNIT_V_CONS) +#define UNIT_V_BZ (UNIT_V_UF + 1) +#define UNIT_V_48 (UNIT_V_UF + 2) +#define UNIT_BZ (1 << UNIT_V_BZ) +#define UNIT_48 (1 << UNIT_V_48) +#define GET_PCHAIN(x) (((x) >> UNIT_V_BZ) & (UNIT_BZ|UNIT_48)) + +#define LPT_BINLNT 24 /* bin buffer length */ +#define LPT_ECHLNT 22 /* echo buffer length */ +#define LPT_CHRLNT 80 /* char buffer length */ + +#define LPS_INIT 0 /* init state */ +#define LPS_DATA 1 /* print data state */ +#define ECS_DATA 2 /* echo data state */ +#define LPS_END 3 /* end state */ + +#define LPB_9ROW 0 /* bin buf: 9 row */ +#define LPB_8ROW 2 /* 8 row */ +#define LPB_4ROW 10 /* 4 row */ +#define LPB_3ROW 12 /* 3 row */ +#define LPB_1ROW 16 /* 1 row */ +#define LPB_12ROW 22 /* 12 row */ + +#define ECB_84ROW 0 /* echo buf: 8-4 row */ +#define ECB_83ROW 2 /* 8-3 row */ +#define ECB_9ROW 4 /* 9 row */ + +#define ECHO_F 0100 /* echo map: flag */ +#define ECHO_MASK 0037 /* mask */ + +#define CMD_BIN 1 /* cmd: bcd/bin */ +#define CMD_ECHO 2 /* cmd: wrs/rds */ + +uint32 lpt_sta = 0; /* state */ +uint32 lpt_bptr = 0; /* buffer ptr */ +uint32 lpt_cmd = 0; /* modes */ +uint32 lpt_tstart = 27500; /* timing */ +uint32 lpt_tstop = 27500; +uint32 lpt_tleft = 150; +uint32 lpt_tright = 4000; +t_uint64 lpt_chob = 0; +uint32 lpt_chob_v = 0; +t_uint64 lpt_bbuf[LPT_BINLNT]; /* binary buffer */ +t_uint64 lpt_ebuf[LPT_ECHLNT]; /* echo buffer */ + + +/* Echo ordering map */ + +static const uint8 echo_map[LPT_BINLNT + LPT_ECHLNT] = { + 0, 1, 2, 3, 4, 5, 6, 7, /* write 9 to 1 */ + 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, + 0+ECHO_F, 1+ECHO_F, /* echo 8+4 */ + 18, 19, /* write 0 */ + 2+ECHO_F, 3+ECHO_F, /* echo 8+3 */ + 20, 21, /* write 11 */ + 4+ECHO_F, 5+ECHO_F, /* echo 9 */ + 22, 23, /* write 12 */ + 6+ECHO_F, 7+ECHO_F, 8+ECHO_F, 9+ECHO_F, /* echo 8 to 1 */ + 10+ECHO_F, 11+ECHO_F, 12+ECHO_F, 13+ECHO_F, + 14+ECHO_F, 15+ECHO_F, 16+ECHO_F, 17+ECHO_F, + 18+ECHO_F, 19+ECHO_F, 20+ECHO_F, 21+ECHO_F + }; + +extern uint32 ind_ioc; +extern t_uint64 bit_masks[36]; +extern uint32 col_masks[12]; +extern char bcd_to_ascii_a[64]; +extern char bcd_to_ascii_h[64]; +extern char bcd_to_pca[64]; +extern char bcd_to_pch[64]; + +char *pch_table[4] = { + bcd_to_ascii_h, bcd_to_ascii_a, bcd_to_pch, bcd_to_pca, + }; + +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_stat lpt_end_line (UNIT *uptr); + +extern char colbin_to_bcd (uint32 colbin); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { &lpt_chsel, &lpt_chwr }; + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_CONS+UNIT_TEXT, 0) + }; + +REG lpt_reg[] = { + { ORDATA (STATE, lpt_sta, 2) }, + { ORDATA (CMD, lpt_cmd, 2) }, + { ORDATA (CHOB, lpt_chob, 36) }, + { FLDATA (CHOBV, lpt_chob_v, 0) }, + { DRDATA (BPTR, lpt_bptr, 6), PV_LEFT }, + { BRDATA (BUF, lpt_bbuf, 8, 36, LPT_BINLNT) }, + { BRDATA (EBUF, lpt_ebuf, 8, 36, LPT_ECHLNT) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TSTART, lpt_tstart, 24), PV_LEFT + REG_NZ }, + { DRDATA (TSTOP, lpt_tstop, 24), PV_LEFT + REG_NZ }, + { DRDATA (TLEFT, lpt_tleft, 24), PV_LEFT + REG_NZ }, + { DRDATA (TRIGHT, lpt_tright, 24), PV_LEFT + REG_NZ }, + { NULL } + }; + +MTAB lpt_mod[] = { + { UNIT_CONS, UNIT_CONS, "default to console", "DEFAULT" }, + { UNIT_CONS, 0 , "no default device", "NODEFAULT" }, + { UNIT_48, UNIT_48, "48 character chain", "48" }, + { UNIT_48, 0, "64 character chain", "64" }, + { UNIT_BZ, UNIT_BZ, "business set", "BUSINESS" }, + { UNIT_BZ, 0, "Fortran set", "FORTRAN" }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL, + &lpt_dib, DEV_DISABLE + }; + +/* Channel select routine */ + +t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit) +{ +if (sel & CHSL_NDS) return ch6_end_nds (ch); /* nds? nop */ + +switch (sel) { /* case on cmd */ + + case CHSL_RDS: /* read */ + case CHSL_WRS: /* write */ + if (!(lpt_unit.flags & (UNIT_ATT|UNIT_CONS))) /* not attached? */ + return SCPE_UNATT; + if (sim_is_active (&lpt_unit)) /* busy? */ + return ERR_STALL; + lpt_cmd = ((unit & 02)? CMD_BIN: 0) | /* save modes */ + ((sel == CHSL_RDS)? CMD_ECHO: 0); + lpt_sta = LPS_INIT; /* initial state */ + sim_activate (&lpt_unit, lpt_tstart); /* start reader */ + break; + + default: /* other */ + return STOP_ILLIOP; + } + +return SCPE_OK; +} + +/* Channel write routine + + - Normal mode is processed here + - Echo mode is processed in the service routine (like a read) */ + +t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 eorfl) +{ +uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */ + +lpt_chob = val & DMASK; /* store data */ +lpt_chob_v = 1; /* set valid */ +if (lpt_sta == ECS_DATA) return SCPE_OK; +if (lpt_sta == LPS_DATA) { + lpt_bbuf[lpt_bptr++] = lpt_chob; /* store data */ + if (eorfl || /* end record, or */ + ((lpt_cmd & CMD_BIN)? /* last word in buffer? */ + (lpt_bptr > (LPB_1ROW + 1)): /* (binary mode) */ + (lpt_bptr > (LPB_12ROW + 1)))) { /* (normal mode) */ + ch6_set_flags (CH_A, u, CHF_EOR); /* set eor */ + return lpt_end_line (&lpt_unit); + } + return SCPE_OK; + } +return SCPE_IERR; +} + +/* Unit timeout */ + +t_stat lpt_svc (UNIT *uptr) +{ +uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */ +uint32 i, map; + +switch (lpt_sta) { /* case on state */ + + case LPS_INIT: /* initial state */ + for (i = 0; i < LPT_BINLNT; i++) /* clear data buffer */ + lpt_bbuf[i] = 0; + for (i = 0; i < LPT_ECHLNT; i++) /* clear echo buffer */ + lpt_ebuf[i] = 0; + if (lpt_cmd & CMD_BIN) lpt_bptr = LPB_1ROW; /* set buffer ptr */ + else lpt_bptr = LPB_9ROW; + if (lpt_cmd & CMD_ECHO) lpt_sta = ECS_DATA; /* set data state */ + else lpt_sta = LPS_DATA; + ch6_req_wr (CH_A, u); /* request channel */ + lpt_chob = 0; /* clr, inval buffer */ + lpt_chob_v = 0; + sim_activate (uptr, lpt_tleft); /* go again */ + break; + + case LPS_DATA: /* print data state */ + if (!ch6_qconn (CH_A, u)) /* disconnect? */ + return lpt_end_line (uptr); /* line is done */ + if (lpt_chob_v) lpt_chob_v = 0; /* valid? clear */ + else ind_ioc = 1; /* no, io check */ + ch6_req_wr (CH_A, u); /* request chan again */ + sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright); + break; + + case ECS_DATA: /* echo data state */ + map = echo_map[lpt_bptr++]; /* map column */ + if (map == ECHO_F) { /* first echo? */ + lpt_ebuf[ECB_84ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_4ROW]; + lpt_ebuf[ECB_84ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_4ROW + 1]; + lpt_ebuf[ECB_83ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_3ROW]; + lpt_ebuf[ECB_83ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_3ROW + 1]; + for (i = 0; i < 18; i++) /* copy rows 9.. 1 */ + lpt_ebuf[ECB_9ROW + i] = lpt_bbuf[LPB_9ROW + i]; + } + if (map & ECHO_F) { /* echo cycle */ + ch6_req_rd (CH_A, u, lpt_ebuf[map & ECHO_MASK], 0); + if (lpt_bptr >= (LPT_BINLNT + LPT_ECHLNT)) + return lpt_end_line (uptr); /* done? */ + sim_activate (uptr, lpt_tleft); /* short timer */ + } + else { /* print cycle */ + if (lpt_chob_v) lpt_chob_v = 0; /* valid? clear */ + else ind_ioc = 1; /* no, io check */ + lpt_bbuf[map] = lpt_chob; /* store in buffer */ + sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright); + } + if (!(echo_map[lpt_bptr] & ECHO_F)) /* print word next? */ + ch6_req_wr (CH_A, u); /* req channel */ + break; + + case LPS_END: /* end state */ + if (ch6_qconn (CH_A, u)) { /* lpt still conn? */ + lpt_sta = LPS_INIT; /* initial state */ + sim_activate (uptr, 1); /* next line */ + } + break; + } + +return SCPE_OK; +} + +/* End line routine */ + +t_stat lpt_end_line (UNIT *uptr) +{ +uint32 i, col, row, bufw, colbin; +char *pch, bcd, lpt_cbuf[LPT_CHRLNT + 1]; +t_uint64 dat; + +pch = pch_table[GET_PCHAIN (lpt_unit.flags)]; /* get print chain */ +for (col = 0; col < (LPT_CHRLNT + 1); col++) /* clear ascii buf */ + lpt_cbuf[col] = ' '; +for (col = 0; col < 72; col++) { /* proc 72 columns */ + colbin = 0; + dat = bit_masks[35 - (col % 36)]; /* mask for column */ + for (row = 0; row < 12; row++) { /* proc 12 rows */ + bufw = (row * 2) + (col / 36); /* index to buffer */ + if (lpt_bbuf[bufw] & dat) colbin |= col_masks[row]; + } + bcd = colbin_to_bcd (colbin); /* column bin -> BCD */ + lpt_cbuf[col] = pch[bcd]; /* -> ASCII */ + } +for (i = LPT_CHRLNT; (i > 0) && + (lpt_cbuf[i - 1] == ' '); --i) ; /* trim spaces */ +lpt_cbuf[i] = 0; /* append nul */ +if (uptr->flags & UNIT_ATT) { /* file? */ + fputs (lpt_cbuf, uptr->fileref); /* write line */ + fputc ('\n', uptr->fileref); /* append nl */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + } +else if (uptr->flags & UNIT_CONS) { /* print to console? */ + for (i = 0; lpt_cbuf[i] != 0; i++) sim_putchar (lpt_cbuf[i]); + sim_putchar ('\r'); + sim_putchar ('\n'); + } +else return SCPE_UNATT; /* otherwise error */ +lpt_sta = LPS_END; /* end line state */ +sim_cancel (uptr); /* cancel current */ +sim_activate (uptr, lpt_tstop); /* long timer */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +uint32 i; + +for (i = 0; i < LPT_BINLNT; i++) lpt_bbuf[i] = 0; /* clear bin buf */ +for (i = 0; i < LPT_ECHLNT; i++) lpt_ebuf[i] = 0; /* clear echo buf */ +lpt_sta = 0; /* clear state */ +lpt_cmd = 0; /* clear modes */ +lpt_bptr = 0; /* clear buf ptr */ +lpt_chob = 0; +lpt_chob_v = 0; +sim_cancel (&lpt_unit); /* stop printer */ +return SCPE_OK; +} diff --git a/I7094/i7094_mt.c b/I7094/i7094_mt.c new file mode 100644 index 0000000..6c0d2ce --- /dev/null +++ b/I7094/i7094_mt.c @@ -0,0 +1,826 @@ +/* i7094_mt.c: IBM 7094 magnetic tape simulator + + Copyright (c) 2003-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt magtape simulator +*/ + +#include "i7094_defs.h" +#include "sim_tape.h" + +#define UST u3 /* unit state */ +#define UCH u4 /* channel number */ +#define MTUF_V_LDN (MTUF_V_UF + 0) +#define MTUF_LDN (1 << MTUF_V_LDN) +#define MT_MAXFR ((1 << 18) + 2) + +#define QCHRONO(c,u) ((cpu_model & I_CT) && \ + ((c) == CHRONO_CH) && ((u) == CHRONO_UNIT)) + +uint8 *mtxb[NUM_CHAN] = { NULL }; /* xfer buffer */ +uint32 mt_unit[NUM_CHAN]; /* unit */ +uint32 mt_bptr[NUM_CHAN]; +uint32 mt_blnt[NUM_CHAN]; +t_uint64 mt_chob[NUM_CHAN]; +uint32 mt_chob_v[NUM_CHAN]; +uint32 mt_tshort = 2; +uint32 mt_twef = 25000; /* 50 msec */ +uint32 mt_tstart = 29000; /* 58 msec */ +uint32 mt_tstop = 10000; /* 20 msec */ +uint32 mt_tword = 50; /* 125 usec */ + +static const uint8 odd_par[64] = { + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1 + }; + +static const char *tape_stat[] = { + "OK", "TMK", "UNATT", "IOERR", "INVRECLNT", + "FMT", "BOT", "EOM", "RECERR", "WRPROT" + }; + +extern uint32 PC; +extern uint32 cpu_model; +extern uint32 ind_ioc; +extern FILE *sim_deb; +extern char *sel_name[]; + +t_stat mt_chsel (uint32 ch, uint32 sel, uint32 unit); +t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 flags); +t_stat mt_rec_end (UNIT *uptr); +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_boot (int32 unitno, DEVICE *dptr); +t_stat mt_map_err (UNIT *uptr, t_stat st); + +extern uint32 chrono_rd (uint8 *buf, uint32 bufsiz); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +DIB mt_dib = { &mt_chsel, &mt_chwr }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTUF_LDN, 0, "high density", "HIGH", NULL }, + { MTUF_LDN, MTUF_LDN, "low density", "LOW", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { 0 } + }; + +UNIT mta_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mta_reg[] = { + { ORDATA (UNIT, mt_unit[0], 5) }, + { ORDATA (CHOB, mt_chob[0], 36) }, + { FLDATA (CHOBV, mt_chob_v[0], 0) }, + { DRDATA (BPTR, mt_bptr[0], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[0], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mta_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mta_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtb_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtb_reg[] = { + { ORDATA (UNIT, mt_unit[1], 5) }, + { ORDATA (CHOB, mt_chob[1], 36) }, + { FLDATA (CHOBV, mt_chob_v[1], 0) }, + { DRDATA (BPTR, mt_bptr[1], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[1], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtb_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtb_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtc_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtc_reg[] = { + { ORDATA (UNIT, mt_unit[2], 5) }, + { ORDATA (CHOB, mt_chob[2], 36) }, + { FLDATA (CHOBV, mt_chob_v[2], 0) }, + { DRDATA (BPTR, mt_bptr[2], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[2], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtc_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtc_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtd_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtd_reg[] = { + { ORDATA (UNIT, mt_unit[3], 5) }, + { ORDATA (CHOB, mt_chob[3], 36) }, + { FLDATA (CHOBV, mt_chob_v[3], 0) }, + { DRDATA (BPTR, mt_bptr[3], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[3], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtd_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtd_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mte_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mte_reg[] = { + { ORDATA (UNIT, mt_unit[4], 5) }, + { ORDATA (CHOB, mt_chob[4], 36) }, + { FLDATA (CHOBV, mt_chob_v[4], 0) }, + { DRDATA (BPTR, mt_bptr[4], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[4], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mte_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mte_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtf_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtf_reg[] = { + { ORDATA (UNIT, mt_unit[5], 5) }, + { ORDATA (CHOB, mt_chob[5], 36) }, + { FLDATA (CHOBV, mt_chob_v[5], 0) }, + { DRDATA (BPTR, mt_bptr[5], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[5], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtf_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtf_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mtg_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mtg_reg[] = { + { ORDATA (UNIT, mt_unit[6], 5) }, + { ORDATA (CHOB, mt_chob[6], 36) }, + { FLDATA (CHOBV, mt_chob_v[6], 0) }, + { DRDATA (BPTR, mt_bptr[6], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[6], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mtg_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mtg_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +UNIT mth_unit[] = { + { UDATA (NULL, UNIT_DIS, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_ROABLE+UNIT_DISABLE, 0) } + }; + +REG mth_reg[] = { + { ORDATA (UNIT, mt_unit[7], 5) }, + { ORDATA (CHOB, mt_chob[7], 36) }, + { FLDATA (CHOBV, mt_chob_v[7], 0) }, + { DRDATA (BPTR, mt_bptr[7], 16), PV_LEFT }, + { DRDATA (BLNT, mt_blnt[7], 16), PV_LEFT }, + { BRDATA (BUF, NULL, 8, 7, MT_MAXFR) }, + { DRDATA (TWEF, mt_twef, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSHORT, mt_tshort, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTART, mt_tstart, 24), REG_NZ + PV_LEFT }, + { DRDATA (TSTOP, mt_tstop, 24), REG_NZ + PV_LEFT }, + { DRDATA (TWORD, mt_tword, 24), REG_NZ + PV_LEFT }, + { URDATA (UST, mth_unit[0].UST, 8, 5, 0, MT_NUMDR + 1, 0) }, + { URDATA (POS, mth_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR + 1, PV_LEFT | REG_RO) }, + { NULL } + }; + +DEVICE mt_dev[NUM_CHAN] = { + { + "MTA", mta_unit, mta_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + &mt_boot, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DEBUG + }, + { + "MTB", mtb_unit, mtb_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTC", mtc_unit, mtc_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTD", mtd_unit, mtd_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTE", mte_unit, mte_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTF", mtf_unit, mtf_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTG", mtg_unit, mtg_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + }, + { + "MTH", mth_unit, mth_reg, mt_mod, + MT_NUMDR + 1, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &sim_tape_detach, + &mt_dib, DEV_DIS|DEV_DEBUG + } + }; + +/* Select controller + + Inputs: + ch = channel + cmd = select command + unit = unit + Outputs: + status = SCPE_OK if ok + STOP_STALL if busy + error code if error +*/ + +static const int mt_must_att[CHSL_NUM] = { + 0, 1, 1, 0, 1, 1, 0, 0, + 1, 1, 1, 1, 1, 1, 0, 0 + }; + +static const int mt_will_wrt[CHSL_NUM] = { + 0, 0, 1, 0, 0, 1, 0, 0, + 1, 1, 0, 0, 0, 0, 0, 0 + }; + +t_stat mt_chsel (uint32 ch, uint32 cmd, uint32 unit) +{ +UNIT *uptr; +uint32 u = unit & 017; + +if ((ch >= NUM_CHAN) || (cmd == 0) || (cmd >= CHSL_NUM)) + return SCPE_IERR; /* invalid arg? */ +if (mt_dev[ch].flags & DEV_DIS) return STOP_NXDEV; /* disabled? */ +if ((u == 0) || (u > MT_NUMDR)) return STOP_NXDEV; /* valid unit? */ +uptr = mt_dev[ch].units + u; /* get unit ptr */ +if (uptr->flags & UNIT_DIS) return STOP_NXDEV; /* disabled? */ +if (mt_unit[ch] || sim_is_active (uptr)) /* ctrl or unit busy? */ + return ERR_STALL; /* stall */ +if (QCHRONO (ch, u)) { /* Chronolog clock? */ + if (cmd != CHSL_RDS) return STOP_ILLIOP; /* only reads */ + sim_activate (uptr, mt_tword); /* responds quickly */ + } +else { /* real tape */ + if (!(uptr->flags & UNIT_ATT) && mt_must_att[cmd]) /* unit unatt? */ + return SCPE_UNATT; + if (sim_tape_wrp (uptr) && mt_will_wrt[cmd]) /* unit wrp && write? */ + return STOP_WRP; + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d %s, pos = %d\n", mt_dev[ch].name, u, sel_name[cmd], uptr->pos); + + switch (cmd) { /* case on cmd */ + + case CHSL_RDS: + case CHSL_WRS: + case CHSL_BSR: + case CHSL_BSF: /* rd, wr, backspace */ + sim_activate (uptr, mt_tstart); /* schedule op */ + break; + + case CHSL_WEF: /* write eof? */ + sim_activate (uptr, mt_twef); /* schedule op */ + break; + + case CHSL_RUN: + sim_activate (uptr, mt_tshort); /* schedule quick event */ + break; + case CHSL_REW: + case CHSL_SDN: /* rew, rew/unl, set det */ + sim_activate (uptr, mt_tshort); /* schedule quick event */ + break; + + default: + return SCPE_IERR; + } /* end switch */ + } /* end else */ + +uptr->UST = cmd; /* set cmd */ +mt_unit[ch] = unit & 0777; /* save unit */ +return SCPE_OK; +} + +/* Channel write routine */ + +t_stat mt_chwr (uint32 ch, t_uint64 val, uint32 eorfl) +{ +int32 k, u; +uint8 by, *xb; +UNIT *uptr; + +if (ch >= NUM_CHAN) return SCPE_IERR; /* invalid chan? */ +xb = mtxb[ch]; /* get xfer buf */ +u = mt_unit[ch] & 017; +if ((xb == NULL) || (u > MT_NUMDR)) return SCPE_IERR; /* invalid args? */ +uptr = mt_dev[ch].units + u; /* get unit */ +mt_chob[ch] = val & DMASK; /* save word from chan */ +mt_chob_v[ch] = 1; /* set valid */ + +if (uptr->UST == (CHSL_WRS|CHSL_2ND)) { /* data write? */ + for (k = 30; /* proc 6 bytes */ + (k >= 0) && (mt_bptr[ch] < MT_MAXFR); + k = k - 6) { + by = (uint8) ((val >> k) & 077); /* get byte */ + if ((mt_unit[ch] & 020) == 0) { /* BCD? */ + if (by == 0) by = BCD_ZERO; /* cvt bin 0 */ + else if (by & 020) by = by ^ 040; /* invert zones */ + if (!odd_par[by]) by = by | 0100; /* even parity */ + } + else if (odd_par[by]) by = by | 0100; /* bin, odd par */ + xb[mt_bptr[ch]++] = by; /* put in buffer */ + } + if (eorfl) return mt_rec_end (uptr); /* EOR? write rec */ + return SCPE_OK; + } +return SCPE_IERR; +} + +/* Unit timeout */ + +t_stat mt_svc (UNIT *uptr) +{ +uint32 i, u, ch = uptr->UCH; /* get channel number */ +uint8 by, *xb = mtxb[ch]; /* get xfer buffer */ +t_uint64 dat; +t_mtrlnt bc; +t_stat r; + +if (xb == NULL) return SCPE_IERR; /* valid buffer? */ +u = uptr - mt_dev[ch].units; +switch (uptr->UST) { /* case on state */ + + case CHSL_RDS: /* read start */ + if (QCHRONO (ch, mt_unit[ch] & 017)) /* Chronolog clock? */ + bc = chrono_rd (xb, MT_MAXFR); /* read clock */ + else { /* real tape */ + r = sim_tape_rdrecf (uptr, xb, &bc, MT_MAXFR); /* read record */ + if (r = mt_map_err (uptr, r)) return r; /* map status */ + if (mt_unit[ch] == 0) return SCPE_OK; /* disconnected? */ + } /* end else Chrono */ + if (!ch6_qconn (ch, mt_unit[ch])) { /* chan disconnected? */ + mt_unit[ch] = 0; /* clr ctrl busy */ + return SCPE_OK; + } + for (i = bc; i < (bc + 6); i++) xb[i] = 0; /* extra 0's */ + mt_bptr[ch] = 0; /* set ptr, lnt */ + mt_blnt[ch] = bc; + uptr->UST = CHSL_RDS|CHSL_2ND; /* next state */ + sim_activate (uptr, mt_tword); + break; + + case CHSL_RDS|CHSL_2ND: /* read word */ + for (i = 0, dat = 0; i < 6; i++) { /* proc 6 bytes */ + by = xb[mt_bptr[ch]++] & 077; /* get next byte */ + if ((mt_unit[ch] & 020) == 0) { /* BCD? */ + if (by == BCD_ZERO) by = 0; /* cvt BCD 0 */ + else if (by & 020) by = by ^ 040; /* invert zones */ + } + dat = (dat << 6) | ((t_uint64) by); + } + if (mt_bptr[ch] >= mt_blnt[ch]) { /* end of record? */ + ch6_req_rd (ch, mt_unit[ch], dat, CH6DF_EOR); + uptr->UST = CHSL_RDS|CHSL_3RD; /* next state */ + sim_activate (uptr, mt_tstop); /* long timing */ + } + else { + ch6_req_rd (ch, mt_unit[ch], dat, 0); /* send to channel */ + sim_activate (uptr, mt_tword); /* next word */ + } + break; + + case CHSL_RDS|CHSL_3RD: /* end record */ + if (ch6_qconn (ch, mt_unit[ch])) { /* ch still conn? */ + uptr->UST = CHSL_RDS; /* initial state */ + sim_activate (uptr, mt_tshort); /* sched next record */ + } + else mt_unit[ch] = 0; /* clr ctrl busy */ + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d RDS complete, pos = %d, %s\n", + mt_dev[ch].name, u, uptr->pos, mt_unit[ch]? "continuing": "disconnecting"); + return SCPE_OK; + + case CHSL_WRS: /* write start */ + if (!ch6_qconn (ch, mt_unit[ch])) { /* chan disconnected? */ + mt_unit[ch] = 0; /* clr ctrl busy */ + return SCPE_OK; /* (writes blank tape) */ + } + mt_bptr[ch] = 0; /* init buffer */ + uptr->UST = CHSL_WRS|CHSL_2ND; /* next state */ + ch6_req_wr (ch, mt_unit[ch]); /* request channel */ + mt_chob[ch] = 0; /* clr, inval buffer */ + mt_chob_v[ch] = 0; + sim_activate (uptr, mt_tword); /* wait for word */ + break; + + case CHSL_WRS|CHSL_2ND: /* write word */ + if (!ch6_qconn (ch, mt_unit[ch])) /* disconnected? */ + return mt_rec_end (uptr); /* write record */ + if (mt_chob_v[ch]) mt_chob_v[ch] = 0; /* valid? clear */ + else ind_ioc = 1; /* no, io check */ + ch6_req_wr (ch, mt_unit[ch]); /* request channel */ + sim_activate (uptr, mt_tword); /* next word */ + break; + + case CHSL_WRS|CHSL_3RD: /* write stop */ + if (ch6_qconn (ch, mt_unit[ch])) { /* chan active? */ + uptr->UST = CHSL_WRS; /* initial state */ + sim_activate (uptr, mt_tshort); /* sched next record */ + } + else mt_unit[ch] = 0; /* clr ctrl busy */ + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d WRS complete, pos = %d, %s\n", + mt_dev[ch].name, u, uptr->pos, mt_unit[ch]? "continuing": "disconnecting"); + return SCPE_OK; + + case CHSL_BSR: /* backspace rec */ + r = sim_tape_sprecr (uptr, &bc); /* space backwards */ + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d BSR complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); + if (r == MTSE_TMK) return SCPE_OK; /* allow tape mark */ + return mt_map_err (uptr, r); + + case CHSL_BSF: /* backspace file */ + while ((r = sim_tape_sprecr (uptr, &bc)) == MTSE_OK) ; + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d BSF complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); + if (r == MTSE_TMK) return SCPE_OK; /* allow tape mark */ + return mt_map_err (uptr, r); /* map others */ + + case CHSL_WEF: /* write eof */ + r = sim_tape_wrtmk (uptr); /* write tape mark */ + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d WEF complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); + return mt_map_err (uptr, r); + + case CHSL_REW: case CHSL_RUN: /* rewind, unload */ + uptr->UST = uptr->UST | CHSL_2ND; /* set 2nd state */ + sim_activate (uptr, mt_tstart); /* reactivate */ + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + return SCPE_OK; + + case CHSL_REW | CHSL_2ND: + sim_tape_rewind (uptr); + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d REW complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); + return SCPE_OK; + + case CHSL_RUN | CHSL_2ND: + sim_tape_detach (uptr); + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d RUN complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); + return SCPE_OK; + + case CHSL_SDN: + if (mt_unit[ch] & 020) /* set density flag */ + uptr->flags = uptr-> flags & ~MTUF_LDN; + else uptr->flags = uptr->flags | MTUF_LDN; + mt_unit[ch] = 0; /* clr ctrl busy */ + ch6_end_nds (ch); /* disconnect */ + if (DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d SDN complete, pos = %d\n", mt_dev[ch].name, u, uptr->pos); + return SCPE_OK; + + default: + return SCPE_IERR; + } + +return SCPE_OK; +} + +/* End record routine */ + +t_stat mt_rec_end (UNIT *uptr) +{ +uint32 ch = uptr->UCH; +uint8 *xb = mtxb[ch]; +t_stat r; + +if (mt_bptr[ch]) { /* any data? */ + if (xb == NULL) return SCPE_IERR; + r = sim_tape_wrrecf (uptr, xb, mt_bptr[ch]); /* write record */ + if (r = mt_map_err (uptr, r)) return r; /* map error */ + } +uptr->UST = CHSL_WRS|CHSL_3RD; /* next state */ +sim_cancel (uptr); /* cancel current */ +sim_activate (uptr, mt_tstop); /* long timing */ +return SCPE_OK; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +uint32 ch = uptr->UCH; +uint32 u = mt_unit[ch]; +uint32 up = uptr - mt_dev[ch].units; + +if ((st != MTSE_OK) && DEBUG_PRS (mt_dev[ch])) fprintf (sim_deb, + ">>%s%d status = %s, pos = %d\n", mt_dev[ch].name, up, tape_stat[st], uptr->pos); + +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* not attached */ + ch6_err_disc (ch, u, CHF_TRC); + mt_unit[ch] = 0; /* disconnect */ + return SCPE_IERR; + + case MTSE_IOERR: /* IO error */ + ch6_err_disc (ch, u, CHF_TRC); + mt_unit[ch] = 0; /* disconnect */ + return SCPE_IOERR; + + case MTSE_INVRL: /* invalid rec lnt */ + ch6_err_disc (ch, u, CHF_TRC); + mt_unit[ch] = 0; /* disconnect */ + return SCPE_MTRLNT; + + case MTSE_WRP: /* write protect */ + ch6_err_disc (ch, u, 0); + mt_unit[ch] = 0; /* disconnect */ + return STOP_WRP; + + case MTSE_EOM: /* end of medium */ + case MTSE_TMK: /* tape mark */ + ch6_err_disc (ch, u, CHF_EOF); + mt_unit[ch] = 0; /* disconnect */ + break; + + case MTSE_RECE: /* record in error */ + ch6_set_flags (ch, u, CHF_TRC); + break; + + case MTSE_BOT: /* reverse into BOT */ + ch6_set_flags (ch, u, CHF_BOT); + break; + + case MTSE_OK: /* no error */ + break; + } + +return SCPE_OK; +} + +/* Magtape reset */ + +t_stat mt_reset (DEVICE *dptr) +{ +uint32 ch = dptr - &mt_dev[0]; +uint32 j; +REG *rptr; +UNIT *uptr; + +if (mtxb[ch] == NULL) mtxb[ch] = (uint8 *) calloc (MT_MAXFR + 6, sizeof (uint8)); +if (mtxb[ch] == NULL) return SCPE_MEM; /* allocate buffer */ +rptr = find_reg ("BUF", NULL, dptr); /* update reg ptr */ +if (rptr == NULL) return SCPE_IERR; +rptr->loc = (void *) mtxb[ch]; +mt_unit[ch] = 0; /* clear busy */ +mt_bptr[ch] = 0; /* clear buf ptrs */ +mt_blnt[ch] = 0; +mt_chob[ch] = 0; +mt_chob_v[ch] = 0; +for (j = 1; j <= MT_NUMDR; j++) { /* for all units */ + uptr = dptr->units + j; + uptr->UST = 0; /* clear state */ + uptr->UCH = ch; + sim_cancel (uptr); /* stop activity */ + } /* end for */ +return SCPE_OK; /* done */ +} + +/* Magtape attach */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +uptr->flags = uptr->flags & ~MTUF_LDN; /* start as hi den */ +return sim_tape_attach (uptr, cptr); +} + +/* Magtape boot */ + +#define BOOT_START 01000 + +static const t_uint64 boot_rom[5] = { + 0076200000000 + U_MTBIN - 1, /* RDS MT_binary */ + 0054000000000 + BOOT_START + 4, /* RCHA *+3 */ + 0054400000000, /* LCHA 0 */ + 0002100000001, /* TTR 1 */ + 0500003000000, /* IOCT 0,,3 */ + }; + +t_stat mt_boot (int32 unitno, DEVICE *dptr) +{ +uint32 i, chan; +extern t_uint64 *M; + +chan = dptr - &mt_dev[0] + 1; +WriteP (BOOT_START, boot_rom[0] + unitno + (chan << 9)); +for (i = 1; i < 5; i++) + WriteP (BOOT_START + i, boot_rom[i]); +PC = BOOT_START; +return SCPE_OK; +} diff --git a/I7094/i7094_sys.c b/I7094/i7094_sys.c new file mode 100644 index 0000000..ae473a4 --- /dev/null +++ b/I7094/i7094_sys.c @@ -0,0 +1,727 @@ +/* i7094_sys.c: IBM 7094 simulator interface + + Copyright (c) 2003-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 29-Oct-06 RMS Added additional expanded core instructions + 08-Jun-06 RMS Added Dave Pitts' binary loader +*/ + +#include "i7094_defs.h" +#include +#include "i7094_dat.h" + +extern DEVICE cpu_dev; +extern DEVICE ch_dev[NUM_CHAN]; +extern DEVICE mt_dev[NUM_CHAN]; +extern DEVICE drm_dev; +extern DEVICE dsk_dev; +extern DEVICE com_dev, coml_dev; +extern DEVICE cdr_dev, cdp_dev; +extern DEVICE lpt_dev; +extern DEVICE clk_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; + +uint32 cvt_code_to_ascii (uint32 c, int32 sw); +uint32 cvt_ascii_to_code (uint32 c, int32 sw); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 7094"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &clk_dev, + &ch_dev[0], + &ch_dev[1], + &ch_dev[2], + &ch_dev[3], + &ch_dev[4], + &ch_dev[5], + &ch_dev[6], + &ch_dev[7], + &mt_dev[0], + &mt_dev[1], + &mt_dev[2], + &mt_dev[3], + &mt_dev[4], + &mt_dev[5], + &mt_dev[6], + &mt_dev[7], + &cdr_dev, + &cdp_dev, + &lpt_dev, + &dsk_dev, + &drm_dev, + &com_dev, + &coml_dev, + NULL + }; + +char ch_bkpt_msg[] = "Channel A breakpoint, CLC: xxxxxx"; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "Undefined instruction", + "Divide check", + "Nested XEC limit exceeded", + "Address stop", + "Non-existent channel", + "Illegal instruction for 7909 channel", + "Illegal instruction for non-7909 channel", + "Non-existent device", + "Undefined channel instruction", + "Write to protected device", + "Illegal instruction for device", + "Invalid 7631 track format", + "7750 buffer pool empty on input", + "7750 buffer pool empty on output", + "7750 invalid line number", + "7750 invalid message", + ch_bkpt_msg + }; + +/* Modify channel breakpoint message */ + +t_stat ch_bkpt (uint32 ch, uint32 clc) +{ +ch_bkpt_msg[8] = 'A' + ch; +sprintf (&ch_bkpt_msg[27], "%06o", clc); +return STOP_CHBKPT; +} + +/* Binary loader, not implemented */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +extern t_stat binloader (FILE *fd, char *file, int loadpt); + +if (flag == 0) + return binloader (fileref, cptr, 0); +return SCPE_NOFNC; +} + +/* Symbol tables */ + +#define I_V_FL 39 /* inst class */ +#define I_M_FL 017 /* class mask */ +#define I_NOP 0000000000000000 /* no operand */ +#define I_MXR 0010000000000000 /* addr(tag) */ +#define I_MXN 0020000000000000 /* *addr(tag) */ +#define I_MXV 0030000000000000 /* var mul/div */ +#define I_MXC 0040000000000000 /* convert */ +#define I_DNP 0050000000000000 /* decr, no oper */ +#define I_DEC 0060000000000000 /* decrement */ +#define I_SNS 0070000000000000 /* sense */ +#define I_IMM 0100000000000000 /* 18b immediate */ +#define I_TAG 0110000000000000 /* tag only */ +#define I_IOX 0120000000000000 /* IO channel */ +#define I_TCH 0130000000000000 /* transfer channel */ +#define I_I9N 0140000000000000 /* 7909 with nostore */ +#define I_I9S 0150000000000000 /* 7909 */ +#define IFAKE_7607 0001000000000000 /* fake op extensions */ +#define IFAKE_7909 0002000000000000 +#define DFAKE (DMASK|IFAKE_7607|IFAKE_7909) +#define I_N_NOP 000 +#define I_N_MXR 001 +#define I_N_MXN 002 +#define I_N_MXV 003 +#define I_N_MXC 004 +#define I_N_DNP 005 +#define I_N_DEC 006 +#define I_N_SNS 007 +#define I_N_IMM 010 +#define I_N_TAG 011 +#define I_N_IOX 012 +#define I_N_TCH 013 +#define I_N_I9N 014 +#define I_N_I9S 015 + +#define INST_P_XIT 0 /* exit */ +#define INST_P_SKP 1 /* do not print */ +#define INST_P_PRA 2 /* print always */ +#define INST_P_PNZ 3 /* print if nz */ +#define INST_P_PNT 4 /* print if nz, term */ + +static const t_uint64 masks[14] = { + 03777700000000, 03777700000000, + 03777700000000, 03777700000000, + 03777400000000, 03700000000000, + 03700000000000, 03777700077777, + 03777700000000, 03777700000000, + 03700000200000, 03700000200000, + 03760000200000, 03740000200000 }; + +static const uint32 fld_max[14][3] = { /* addr,tag,decr limit */ + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, INST_M_TAG, INST_M_VCNT }, + { INST_M_ADDR, INST_M_TAG, INST_M_CCNT }, + { INST_M_ADDR, INST_M_TAG, INST_M_DEC }, + { INST_M_ADDR, INST_M_TAG, INST_M_DEC }, + { 0, INST_M_TAG, 0 }, + { RMASK, 0, 0 }, + { INST_M_ADDR, INST_M_TAG, 0 }, + { INST_M_ADDR, 1, INST_M_DEC }, + { INST_M_ADDR, 1, 0 }, + { INST_M_ADDR, 1, 0 }, + { INST_M_ADDR, 1, 0 } + }; + +static const uint32 fld_fmt[14][3] = { /* addr,tag,decr print */ + { INST_P_PNT, INST_P_PNT, INST_P_XIT }, /* nop: all optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxr: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* mxn: tag optional */ + { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* mxv: tag optional */ + { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* cvt: tag optional */ + { INST_P_PNT, INST_P_PNT, INST_P_PNT }, /* dnp: all optional */ + { INST_P_PRA, INST_P_PRA, INST_P_PRA }, /* dec: print all */ + { INST_P_SKP, INST_P_PNT, INST_P_XIT }, /* sns: skip addr, tag opt */ + { INST_P_PRA, INST_P_XIT, INST_P_XIT }, /* immediate: addr only */ + { INST_P_PNZ, INST_P_PRA, INST_P_XIT }, /* tag: addr optional */ + { INST_P_PRA, INST_P_PNZ, INST_P_PRA }, /* iox: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* tch: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT }, /* i9n: tag optional */ + { INST_P_PRA, INST_P_PNT, INST_P_XIT } /* i9s: tag optional */ + }; + +static const t_uint64 ind_test[14] = { + 0, 0, INST_IND, 0, 0, 0, 0, + 0, 0, 0, CHI_IND, CHI_IND, CHI_IND, CHI_IND + }; + +static const char *opcode[] = { + "TXI", "TIX", "TXH", + "STR", "TNX", "TXL", + "HTR", "TRA", "TTR", + + "CLM", "LBT", "CHS", + "SSP", "ENK", "IOT", + "COM", "ETM", "RND", + "FRN", "DCT", "RCT", + "LMTM", "SLF", "SLN1", + "SLN2", "SLN3", "SLN4", + "SWT1", "SWT2", "SWT3", + "SWT4", "SWT5", "SWT6", + "BTTA", "BTTB", "BTTC", + "BTTD", "BTTE", "BTTF", + "BTTG", "BTTH", + "RICA", "RICB", "RICC", + "RICD", "RICE", "RICF", + "RICG", "RICH", + "RDCA", "RDCB", "RDCC", + "RDCD", "RDCE", "RDCF", + "RDCG", "RDCH", + + "TRCA", "TRCC", + "TRCE", "TRCG", + "TEFA", "TEFC", + "TEFE", "TEFG", + "TLQ", "IIA", "TIO", + "OAI", "PAI", "TIF", + "IIR", "RFT", "SIR", + "RNT", "RIR", + "TCOA", "TCOB", "TCOC", + "TCOD", "TCOE", "TCOF", + "TCOG", "TCOH", "TSX", + "TZE", "CVR", "TPL", + "XCA", "TOV", + "TQO", "TQP", + "MPY", "VLM", "VLM1", + "DVH", "DVP", + "VDH", "VDP", + "VDH2", "VDP2", + "FDH", "FDP", + "FMP", "DFMP", + "FAD", "DFAD", + "FSB", "DFSB", + "FAM", "DFAM", + "FSM", "DFSM", + "ANS", "ERA", + "CAS", "ACL", + "ADD", "ADM", + "SUB", "SBM", + "HPR", "IIS", "LDI", + "OSI", "DLD", "OFT", + "RIS", "ONT", + "CLA", "CLS", + "ZET", "XEC", + "LXA", "LAC", + "RCHA", "RCHC", + "RCHE", "RCHG", + "LCHA", "LCHC", + "LCHE", "LCHG", + "RSCA", "RSCC", + "RSCE", "RSCG", + "STCA", "STCC", + "STCE", "STCG", + "LDQ", "ENB", + "STZ", "STO", "SLW", + "STI", "STA", "STD", + "STT", "STP", + "SXA", "SCA", + "SCHA", "SCHC", + "SCHE", "SCHG", + "SCDA", "SCDC", + "SCDE", "SCDG", + "PAX", "PAC", + "PXA", "PCA", + "PSE", "NOP", "RDS", + "LLS", "BSR", "LRS", + "WRS", "ALS", "WEF", + "ARS", "REW", "AXT", + "SDN", + + "CLM", "PBT", "EFTM", + "SSM", "LFTM", "ESTM", + "ECTM", "LTM", "LSNM", + "EMTM", "SLT1", "SLT2", + "SLT3", "SLT4", + "ETTA", "ETTB", "ETTC", + "ETTD", "ETTE", "ETTF", + "ETTG", "ETTH", + + "ESNT", + "TRCB", "TRCD", + "TRCF", "TRCH", + "TEFB", "TEFD", + "TEFF", "TEFH", + "RIA", "PIA", + "IIL", "LFT", "SIL", + "LNT", "RIL", + "TCNA", "TCNB", "TCNC", + "TCND", "TCNE", "TCNF", + "TCNG", "TCNH", + "TNZ", "CVR", "TMI", + "XCL", "TNO", "CRQ", + "MPR", "DFDH", "DFDP", + "UFM", "DUFM", + "UFA", "DUFA", + "UFS", "DUFS", + "UAM", "DUAM", + "USM", "DUSM", + "ANA", "LAS", + "CAL", "ORA", "NZT", + "LXD", "LXC", + "RCHB", "RCHD", + "RCHF", "RCHH", + "LCHB", "LCHD", + "LCHF", "LCHH", + "RSCB", "RSCD", + "RSCF", "RSCH", + "STCB", "STCD", + "STCF", "STCH", + "STQ", "ORS", "DST", + "SLQ", "STL", + "SXD", "SCD", + "SCHB", "SCHD", + "SCHF", "SCHH", + "SCDB", "SCDD", + "SCDF", "SCDH", + "PDX", "PDC", + "PXD", "PCD", + "MSE", "LGL", "BSF", + "LGR", "RQL", "RUN", + "AXC", + + "TIA", "TIB", + "LRI", "LPI", + "SEA", "SEB", + "IFT", "EFT", + + "IOCD", "IOCDN", "TCH", + "IORP", "IORPN", + "IORT", "IORTN", + "IOCP", "IOCPN", + "IOCT", "IOCTN", + "IOSP", "IOSPN", + "IOST", "IOSTN", + + "WTR", "XMT", + "TCH", "LIPT", + "CTL", "CTLN", + "CTLR", "CTLRN", + "CTLW", "CTLWN", + "SNS", + "LAR", "SAR", "TWT", + "CPYP", + "CPYD", "TCM", + "LIP", "TDC", "LCC", + "SMS", "ICC", + + NULL + }; + +static const t_uint64 opc_v[] = { + 0100000000000+I_DEC, 0200000000000+I_DEC, 0300000000000+I_DEC, + 0500000000000+I_DNP, 0600000000000+I_DEC, 0700000000000+I_DEC, + 0000000000000+I_MXN, 0002000000000+I_MXN, 0002100000000+I_MXN, + + 0076000000000+I_SNS, 0076000000001+I_SNS, 0076000000002+I_SNS, + 0076000000003+I_SNS, 0076000000004+I_SNS, 0076000000005+I_SNS, + 0076000000006+I_SNS, 0076000000007+I_SNS, 0076000000010+I_SNS, + 0076000000011+I_SNS, 0076000000012+I_SNS, 0076000000014+I_SNS, + 0076000000016+I_SNS, 0076000000140+I_SNS, 0076000000141+I_SNS, + 0076000000142+I_SNS, 0076000000143+I_SNS, 0076000000144+I_SNS, + 0076000000161+I_SNS, 0076000000162+I_SNS, 0076000000163+I_SNS, + 0076000000164+I_SNS, 0076000000165+I_SNS, 0076000000166+I_SNS, + 0076000001000+I_SNS, 0076000002000+I_SNS, 0076000003000+I_SNS, + 0076000004000+I_SNS, 0076000005000+I_SNS, 0076000006000+I_SNS, + 0076000007000+I_SNS, 0076000010000+I_SNS, + 0076000001350+I_SNS, 0076000002350+I_SNS, 0076000003350+I_SNS, + 0076000004350+I_SNS, 0076000005350+I_SNS, 0076000006350+I_SNS, + 0076000007350+I_SNS, 0076000010350+I_SNS, + 0076000001352+I_SNS, 0076000002352+I_SNS, 0076000003352+I_SNS, + 0076000004352+I_SNS, 0076000005352+I_SNS, 0076000006352+I_SNS, + 0076000007352+I_SNS, 0076000010352+I_SNS, + + 0002200000000+I_MXN, 0002400000000+I_MXN, + 0002600000000+I_MXN, 0002700000000+I_MXN, + 0003000000000+I_MXN, 0003100000000+I_MXN, + 0003200000000+I_MXN, 0003300000000+I_MXN, + 0004000000000+I_MXN, 0004100000000+I_NOP, 0004200000000+I_MXR, + 0004300000000+I_NOP, 0004400000000+I_NOP, 0004600000000+I_MXR, + 0005100000000+I_IMM, 0005400000000+I_IMM, 0005500000000+I_IMM, + 0005600000000+I_IMM, 0005700000000+I_IMM, + 0006000000000+I_MXN, 0006100000000+I_MXN, 0006200000000+I_MXN, + 0006300000000+I_MXN, 0006400000000+I_MXN, 0006500000000+I_MXN, + 0006600000000+I_MXN, 0006700000000+I_MXN, 0007400000000+I_MXR, + 0010000000000+I_MXN, 0011400000000+I_MXC, 0012000000000+I_MXN, + 0013100000000+I_NOP, 0014000000000+I_MXN, + 0016100000000+I_MXN, 0016200000000+I_MXN, + 0020000000000+I_MXN, 0020400000000+I_MXV, 0020500000000+I_MXV, + 0022000000000+I_MXN, 0022100000000+I_MXN, + 0022400000000+I_MXV, 0022500000000+I_MXV, + 0022600000000+I_MXV, 0022700000000+I_MXV, + 0024000000000+I_MXN, 0024100000000+I_MXN, + 0026000000000+I_MXN, 0026100000000+I_MXN, + 0030000000000+I_MXN, 0030100000000+I_MXN, + 0030200000000+I_MXN, 0030300000000+I_MXN, + 0030400000000+I_MXN, 0030500000000+I_MXN, + 0030600000000+I_MXN, 0030700000000+I_MXN, + 0032000000000+I_MXN, 0032200000000+I_MXN, + 0034000000000+I_MXN, 0036100000000+I_MXN, + 0040000000000+I_MXN, 0040100000000+I_MXN, + 0040200000000+I_MXN, 0440000000000+I_MXN, + 0042000000000+I_NOP, 0044000000000+I_MXN, 0044100000000+I_MXN, + 0044200000000+I_MXN, 0044300000000+I_MXN, 0044400000000+I_MXN, + 0044500000000+I_MXN, 0044600000000+I_MXN, + 0050000000000+I_MXN, 0050200000000+I_MXN, + 0052000000000+I_MXN, 0052200000000+I_MXN, + 0053400000000+I_MXR, 0053500000000+I_MXR, + 0054000000000+I_MXN, 0054100000000+I_MXN, + 0054200000000+I_MXN, 0054300000000+I_MXN, + 0054400000000+I_MXN, 0054500000000+I_MXN, + 0054600000000+I_MXN, 0054700000000+I_MXN, + 0054000000000+I_MXN, 0054100000000+I_MXN, + 0054200000000+I_MXN, 0054300000000+I_MXN, + 0054400000000+I_MXN, 0054500000000+I_MXN, + 0054600000000+I_MXN, 0054700000000+I_MXN, + 0056000000000+I_MXN, 0056400000000+I_MXN, + 0060000000000+I_MXN, 0060100000000+I_MXN, 0060200000000+I_MXN, + 0060400000000+I_MXN, 0062100000000+I_MXN, 0062200000000+I_MXN, + 0062500000000+I_MXN, 0063000000000+I_MXN, + 0063400000000+I_MXR, 0063600000000+I_MXR, + 0064000000000+I_MXN, 0064000000000+I_MXN, + 0064200000000+I_MXN, 0064300000000+I_MXN, + 0064400000000+I_MXN, 0064500000000+I_MXN, + 0064600000000+I_MXN, 0064700000000+I_MXN, + 0073400000000+I_TAG, 0073700000000+I_TAG, + 0075400000000+I_TAG, 0075600000000+I_TAG, + 0076000000000+I_MXR, 0076100000000+I_NOP, 0076200000000+I_MXR, + 0076300000000+I_MXR, 0076400000000+I_MXR, 0076500000000+I_MXR, + 0076600000000+I_MXR, 0076700000000+I_MXR, 0077000000000+I_MXR, + 0077100000000+I_MXR, 0077200000000+I_MXR, 0077400000000+I_MXR, + 0077600000000+I_MXR, + + 0476000000000+I_SNS, 0476000000001+I_SNS, 0476000000002+I_SNS, + 0476000000003+I_SNS, 0476000000004+I_SNS, 0476000000005+I_SNS, + 0476000000006+I_SNS, 0476000000007+I_SNS, 0476000000010+I_SNS, + 0476000000016+I_SNS, 0476000000141+I_SNS, 0476000000142+I_SNS, + 0476000000143+I_SNS, 0476000000144+I_SNS, + 0476000001000+I_SNS, 0476000002000+I_SNS, 0476000003000+I_SNS, + 0476000004000+I_SNS, 0476000005000+I_SNS, 0476000006000+I_SNS, + 0476000007000+I_SNS, 0476000010000+I_SNS, + + 0402100000000+I_MXN, + 0402200000000+I_MXN, 0402400000000+I_MXN, + 0402600000000+I_MXN, 0402700000000+I_MXN, + 0403000000000+I_MXN, 0403100000000+I_MXN, + 0403200000000+I_MXN, 0403300000000+I_MXN, + 0404200000000+I_NOP, 0404600000000+I_NOP, + 0405100000000+I_IMM, 0405400000000+I_IMM, 0405500000000+I_IMM, + 0405600000000+I_IMM, 0405700000000+I_IMM, + 0406000000000+I_MXN, 0406100000000+I_MXN, 0406200000000+I_MXN, + 0406300000000+I_MXN, 0406400000000+I_MXN, 0406500000000+I_MXN, + 0406600000000+I_MXN, 0406700000000+I_MXN, + 0410000000000+I_MXN, 0411400000000+I_MXC, 0412000000000+I_MXN, + 0413000000000+I_NOP, 0414000000000+I_MXN, 0415400000000+I_MXC, + 0420000000000+I_MXN, 0424000000000+I_MXN, 0424100000000+I_MXN, + 0426000000000+I_MXN, 0426100000000+I_MXN, + 0430000000000+I_MXN, 0430100000000+I_MXN, + 0430200000000+I_MXN, 0430300000000+I_MXN, + 0430400000000+I_MXN, 0430500000000+I_MXN, + 0430600000000+I_MXN, 0430700000000+I_MXN, + 0432000000000+I_MXN, 0434000000000+I_MXN, + 0450000000000+I_MXN, 0450100000000+I_MXN, 0452000000000+I_MXN, + 0453400000000+I_MXR, 0453500000000+I_MXR, + 0454000000000+I_MXN, 0454100000000+I_MXN, + 0454200000000+I_MXN, 0454300000000+I_MXN, + 0454400000000+I_MXN, 0454500000000+I_MXN, + 0454600000000+I_MXN, 0454700000000+I_MXN, + 0454000000000+I_MXN, 0454100000000+I_MXN, + 0454200000000+I_MXN, 0454300000000+I_MXN, + 0454400000000+I_MXN, 0454500000000+I_MXN, + 0454600000000+I_MXN, 0454700000000+I_MXN, + 0460000000000+I_MXN, 0460200000000+I_MXN, 0460300000000+I_MXN, + 0462000000000+I_MXN, 0462500000000+I_MXN, + 0463400000000+I_MXR, 0463600000000+I_MXR, + 0464000000000+I_MXN, 0464000000000+I_MXN, + 0464200000000+I_MXN, 0464300000000+I_MXN, + 0464400000000+I_MXN, 0464500000000+I_MXN, + 0464600000000+I_MXN, 0464700000000+I_MXN, + 0473400000000+I_TAG, 0473700000000+I_TAG, + 0475400000000+I_TAG, 0475600000000+I_TAG, + 0476000000000+I_MXR, 0476300000000+I_MXR, 0476400000000+I_MXR, + 0476500000000+I_MXR, 0477300000000+I_MXR, 0477200000000+I_MXR, + 0477400000000+I_MXR, + + 0010100000000+I_MXN, 0410100000000+I_MXN, + 0056200000000+I_MXN, 0456400000000+I_MXN, + 0476100000041+I_SNS, 0476100000042+I_SNS, + 0476100000043+I_SNS, 0476100000044+I_SNS, + + 01000000000000+I_IOX, 01000000200000+I_IOX, 01100000000000+I_TCH, + 01200000000000+I_IOX, 01200000200000+I_IOX, + 01300000000000+I_IOX, 01300000200000+I_IOX, + 01400000000000+I_IOX, 01400000200000+I_IOX, + 01500000000000+I_IOX, 01500000200000+I_IOX, + 01600000000000+I_IOX, 01600000200000+I_IOX, + 01700000000000+I_IOX, 01700000200000+I_IOX, + + 02000000000000+I_TCH, 02000000200000+I_IOX, + 02100000000000+I_TCH, 02100000200000+I_TCH, + 02200000000000+I_I9N, 02220000000000+I_TCH, + 02200000200000+I_I9N, 02220000200000+I_TCH, + 02240000000000+I_I9N, 02260000000000+I_TCH, + 02240000200000+I_I9N, + 02300000000000+I_I9S, 02300000200000+I_I9S, + 02340000000000+I_I9S, + 02400000000000+I_IOX, + 02500000000000+I_IOX, 02500000200000+I_IOX, + 02600000200000+I_I9S, 02640000000000+I_I9S, 02640000200000+I_I9S, + 02700000000000+I_I9S, 02700000200000+I_IOX, + + 0 + }; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +uint32 i, j, k, l, fmt, c, fld[3]; +DEVICE *dptr; +t_uint64 inst; + +inst = val[0]; +if (uptr == NULL) uptr = &cpu_unit; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; + +if (sw & SWMASK ('C')) { /* character? */ + c = (uint32) (inst & 077); + fprintf (of, "%c", cvt_code_to_ascii (c, sw)); + return SCPE_OK; + } +if (sw & SWMASK ('S')) { /* string? */ + for (i = 36; i > 0; i = i - 6) { + c = (uint32) ((inst >> (i - 6)) & 077); + fprintf (of, "%c", cvt_code_to_ascii (c, sw)); + } + return SCPE_OK; + } +if (!(sw & (SWMASK ('M')|SWMASK ('I')|SWMASK ('N'))) || /* M, N or I? */ + (dptr->dwidth != 36)) return SCPE_ARG; + +/* Instruction decode */ + +fld[0] = ((uint32) inst & 0777777); +fld[1] = GET_TAG (inst); /* get 3 fields */ +fld[2] = GET_DEC (inst); +if (sw & SWMASK ('I')) inst |= IFAKE_7607; /* decode as 7607? */ +if (sw & SWMASK ('N')) inst |= IFAKE_7909; /* decode as 7909? */ + +for (i = 0; opc_v[i] > 0; i++) { /* loop thru ops */ + j = (int32) ((opc_v[i] >> I_V_FL) & I_M_FL); /* get class */ + if ((opc_v[i] & DFAKE) == (inst & masks[j])) { /* match? */ + if (inst & ind_test[j]) /* indirect? */ + fprintf (of, "%s*", opcode[i]); + else fprintf (of, "%s", opcode[i]); /* opcode */ + for (k = 0; k < 3; k++) fld[k] = fld[k] & fld_max[j][k]; + for (k = 0; k < 3; k++) { /* loop thru fields */ + fmt = fld_fmt[j][k]; /* get format */ + if (fmt == INST_P_XIT) return SCPE_OK; + switch (fmt) { /* case on format */ + + case INST_P_PNT: /* print nz, else term */ + for (l = k, c = 0; l < 3; l++) c |= fld[k]; + if (c == 0) return SCPE_OK; + case INST_P_PNZ: /* print non-zero */ + fputc (k? ',': ' ', of); + if (fld[k]) fprintf (of, "%-o", fld[k]); + break; + case INST_P_PRA: /* print always */ + fputc (k? ',': ' ', of); + fprintf (of, "%-o", fld[k]); + break; + case INST_P_SKP: /* skip */ + break; + } /* end switch */ + } /* end for k */ + return SCPE_OK; /* done */ + } /* end if */ + } /* end for i */ +return SCPE_ARG; +} + +/* Convert character to code to ASCII + + -b BCD + -a business-chain */ + +uint32 cvt_code_to_ascii (uint32 c, int32 sw) +{ +if (sw & SWMASK ('B')) { + if (sw & SWMASK ('A')) return bcd_to_ascii_a[c]; + else return bcd_to_ascii_h[c]; + } +else if (sw & SWMASK ('A')) return nine_to_ascii_a[c]; +else return nine_to_ascii_h[c]; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +uint32 i, j, c; +t_uint64 fld[3]; +t_bool ind; +t_stat r; +char gbuf[CBUFSIZE]; + +while (isspace (*cptr)) cptr++; +if ((sw & SWMASK ('C')) || ((*cptr == '\'') && cptr++)) { /* character? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cvt_ascii_to_code (cptr[0] & 0177, sw); + return SCPE_OK; + } +if ((sw & SWMASK ('S')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + for (i = 0; i < 6; i++) { + c = cptr[0] & 0177; + if (c) val[0] = (val[0] << 6) | ((t_value) cvt_ascii_to_code (c, sw)); + else { + val[0] = val[0] << (6 * (6 - i)); + break; + } + } + return SCPE_OK; + } + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +j = strlen (gbuf); /* get length */ +if (gbuf[j - 1] == '*') { /* indirect? */ + ind = TRUE; + gbuf[j - 1] = 0; + } +else ind = FALSE; +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +j = (uint32) ((opc_v[i] >> I_V_FL) & I_M_FL); /* get class */ +val[0] = opc_v[i] & DMASK; +if (ind) { + if (ind_test[j]) val[0] |= ind_test[j]; + else return SCPE_ARG; + } + +for (i = 0; i < 3; i++) fld[i] = 0; /* clear inputs */ +for (i = 0; (i < 3) && *cptr; i++) { /* parse inputs */ + if (i < 2) cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + else cptr = get_glyph (cptr, gbuf, 0); + if (gbuf[0]) { /* anything? */ + fld[i] = get_uint (gbuf, 8, fld_max[j][i], &r); + if ((r != SCPE_OK) || (fld_max[j][i] == 0)) return SCPE_ARG; + } + } +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ + +val[0] = val[0] | fld[0] | (fld[1] << INST_V_TAG) | (fld[2] << INST_V_DEC); +return SCPE_OK; +} + +/* Convert ASCII to character code + + -b BCD */ + +uint32 cvt_ascii_to_code (uint32 c, int32 sw) +{ +if (sw & SWMASK ('B')) return ascii_to_bcd[c]; +else return ascii_to_nine[c]; +} diff --git a/Ibm1130/1130consoleblank.bmp b/Ibm1130/1130consoleblank.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4b989529d09942d6dc50365a6dd6a62e7b5a97e0 GIT binary patch literal 381318 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 literal 0 HcmV?d00001 diff --git a/Ibm1130/1132empty.bmp b/Ibm1130/1132empty.bmp new file mode 100644 index 0000000000000000000000000000000000000000..3ccc082ca1b32ffac655f94182ead642299e8bcd GIT binary patch literal 6278 zcmeI0F^k7H#2wPLpZ<8Z&1M}?!2TPL#M`96Z zhInF6_@42LL4@_@@I~Hs@IPqVx{YY;q|D?N>nYum{X(okAu5+kvaGmpCr2Jc)Bc9Fe3G+ss{9<^W z>9)L?IL_BCGkLS8YF$*$BZErUlZU_jivK70O1h&`C(lQ?2p66JAC>(gTzCR}RQ5{? Fmn-Y;#o7P> literal 0 HcmV?d00001 diff --git a/Ibm1130/1132full.bmp b/Ibm1130/1132full.bmp new file mode 100644 index 0000000000000000000000000000000000000000..4705d1b6f88b8e9e512d24283a33e4564a4fc638 GIT binary patch literal 6278 zcmeHLu};G<5H%7?3k+doWMt$U*!hP{&W1YGV?7~{d;&8QJ4-*Q8>4x49LLvQCvPtUJ+!rL141IkM%7vj>7-#_}sSGTBL=}ZfOLulPKsNte$vE6QQ z!ums4>pT?v+z=`=&;A03j3(IYkqU8~5j*QEKKd#<>3D<`?H9rJtszy$Zb1rUnoY(K5{JQxhdJfAI z=Os?PGdxbgR)({MJ5>)8Q@$K-*i!*V!Wq-)aD8Oj#VK1yt5ot7^7m)jqlgHJ-DYbE_Z7e)b zpW}lt?BhAdr`0N^mpY6rBtPJ&j$sjVII~z;q;+5;a1tV}15Ip3;Gp3Foz3Gaw>u*b w9LLVdNpvxK*e(fcx;0F1WKi=rQGadP5?p#B;};w+P|>C6!Yja!p3Q#w0bm)?H~;_u literal 0 HcmV?d00001 diff --git a/Ibm1130/1442empty.bmp b/Ibm1130/1442empty.bmp new file mode 100644 index 0000000000000000000000000000000000000000..8b9f667b5459a0f9c06b6431fb779f3840618643 GIT binary patch literal 6278 zcmeHLy-vh13=R?!9T^!J8F>bF-l0=R=)^H*f{v_g%uKi6(nq>CC|~}XxC%XN(1DQM z#Q+3(RY*aV@}j(C26P|ke#on# zw3L^Ujch9~nTyg=UP?By&FAIbz4i*uX(=w`%Fpvha2qN;bt_)Xll5}#I!7EnBY5)7 zc$E?KDS@*}<0i(dG${_>0m{pQftB@=_6T>KEh12NB_8d#@D#ToB)ZDWpXVj*qv~^I z{&N2zO0G%gQDyLb?giNMBUG*t;JUVJUtgN=AB7Q5iVMkpDPB4WJ!H4tZKwV5aMC;n z`~D`$Q;L)3NpQjWOp|#iaaemRViT?*oeutQRUF}KcEr4-d1cPS9GDsh=Z|)uy`$qk E0EFM~IsgCw literal 0 HcmV?d00001 diff --git a/Ibm1130/1442eof.bmp b/Ibm1130/1442eof.bmp new file mode 100644 index 0000000000000000000000000000000000000000..2bf679a9ed8efc2445c0e998dc7dc8f725d51b61 GIT binary patch literal 6278 zcmeHLy-vh13=R^~b!6lbc#hk7hfW=#6UUedIt%aDRD^>eY|_#Ht%_rOt%jHvTGz^TSRHfa==$;L It95eRC%ZZIQUCw| literal 0 HcmV?d00001 diff --git a/Ibm1130/1442full.bmp b/Ibm1130/1442full.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e6ba0c5b653e49b0ac3d7eca1319e61815dafb9e GIT binary patch literal 6278 zcmeHLF>b>!3}k=;$=I<^=sU9a51u?Wo|=xDT8~}3b?%hii=UJqxF=hJB!h84Hb{$r zj75`@4kS+!Y-juQ_CTY&KtG~Ahqj`-vHw1dk4~W+5iW9Wx!tt;J2b%JaMkZeL1Mx{Md{6un&g$pMGY2$}pc9#cUR z6Ie5Y`w)*AG911GjF%Y$3;QLW5$QggMJU~cc;&>IXSf+5!8Km~JumSbvG;}bOY^%R zsV6>>ms_lXFU++-L2g qxQN4|WpeiaRu}V-;t=Hh73x-T@y$?LpOQ~7$;sLE<3Q-UI_?Ld^33u8 literal 0 HcmV?d00001 diff --git a/Ibm1130/1442middle.bmp b/Ibm1130/1442middle.bmp new file mode 100644 index 0000000000000000000000000000000000000000..af45289fadc0ae1661d2928531ce303e746622da GIT binary patch literal 6278 zcmeHLu~Ne@3^jM$wId^+z<03o51BGTCd%lkWMpMyX6o)GpTr-eFOD5cjY*@BgQF)k zb|hQxBt6@gr1uZ|TjS*!{TSgj5I8-E(ElTV1yl|YfJYzr%c(Emyf}0sjaT!uDq`W9Ei2*%FdLGhlpqlbR z*+{zblDMFn@T6qAvWT+%<1g^t%_rO4aQ<#;*tv2VF=8X Mv+Kv*2kX~yNA{WFA^-pY literal 0 HcmV?d00001 diff --git a/Ibm1130/dmsr2v12phases.h b/Ibm1130/dmsr2v12phases.h new file mode 100644 index 0000000..47fbb2d --- /dev/null +++ b/Ibm1130/dmsr2v12phases.h @@ -0,0 +1,171 @@ +0x01, "DUP - COMMON SUBROUTINES", +0x02, "DUP - CTRL RECORD PROCESSOR", +0x03, "DUP - STORE PHASE", +0x04, "DUP - *FILES, *LOCAL, *NOCAL PHASE", +0x05, "DUP - DUMP PHASE", +0x06, "DUP - DUMP LET/FLET PHASE", +0x07, "DUP - DELETE PHASE", +0x08, "DUP - DEFINE PHASE", +0x09, "DUP - EXIT PHASE", +0x0A, "DUP - CARD I/O INTERFACE", +0x0B, "DUP - KEYBOARD INPUT INTERFACE", +0x0C, "DUP - PAPER TAPE I/O INTERFACE", +0x0D, "DUP - SAVED UPCOR PHASE", +0x0E, "DUP - PRINCIPAL I/O DUMMY PHASE", +0x0F, "DUP - PRINCIPAL I/O (W/O KB) DUMMY PHASE", +0x10, "DUP - PAPER TAPE I/O DUMMY PHASE", +0x11, "DUP - MOVE DCI PROGRAMS TO UA OR FXA", +0x12, "DUP - EXIT TO MODIF DUMMY PHASE", + +0x1F, "FOR - INPUT PHASE", +0x20, "FOR - CLASSIFIERPHASE", +0x21, "FOR - CHECK ORDER/ST NO PHASE", +0x22, "FOR - COMMON SUBR OR FUNCTION PHASE", +0x23, "FOR - DIMENSION/REAL/INTEGER PHASE", +0x24, "FOR - REAL CONSTANT PHASE", +0x25, "FOR - DEFINE FILE, CALL LINK/EXIT PHASE", +0x26, "FOR - VARIABLE, STMT FUNC PHASE", +0x27, "FOR - DATA STATEMENT PHASE", +0x28, "FOR - FORMAT STATEMENT PHASE", +0x29, "FOR - SUBTRACT DECOMPOSITION PHASE", +0x2A, "FOR - ASCAN I PHASE", +0x2B, "FOR - ASCAN II PHASE", +0x2C, "FOR - DO/CONTINUE/ETC PHASE", +0x2D, "FOR - SUBSCRIPT OPTIMIZATION PHASE", +0x2E, "FOR - SCAN PHASE", +0x2F, "FOR - EXPANDER I PHASE", +0x30, "FOR - EXPANDER II PHASE", +0x31, "FOR - DATA ALLOCATION PHASE", +0x32, "FOR - COMPILATION ERROR PHASE", +0x33, "FOR - STATEMENT ALLOCATION PHASE", +0x34, "FOR - LIST STATEMENT PHASE", +0x35, "FOR - LIST SYMBOL TABLE PHASE", +0x36, "FOR - LIST CONSTANTS PHASE", +0x37, "FOR - OUTPUT I PHASE", +0x38, "FOR - OUTPUT II PHASE", +0x39, "FOR - RECOVERY/EXIT PHASE", + +0X51, "COBOL 51", +0X52, "COBOL 52", +0X53, "COBOL 53", +0X54, "COBOL 54", +0X55, "COBOL 55", +0X56, "COBOL 56", +0X57, "COBOL 57", +0X58, "COBOL 58", +0X59, "COBOL 59", +0X5A, "COBOL 5A", +0X5B, "COBOL 5B", +0X5C, "COBOL 5C", + +0X6E, "SUP PHASE 1 - MONITOR CONTROL RECORD ANALYZER", +0x6F, "SUP PHASE 2 - JOB PROCESSING", +0x70, "SUP PHASE 3 - DELETE TEMPORARY LET", +0x71, "SUP PHASE 4 - XEQ PROCESSING", +0x72, "SUP PHASE 5 - SUPV CONTROL REC PROCESSING", +0X73, "SYSTEM DUMP-CORE-TO-PRINTER", +0X74, "AUXILIARY SUPERVISOR", +0X78, "CORE LOAD BUILDER, PHASE 1", +0x79, "CORE LOAD BUILDER, PHASE 2", +0x7A, "CORE LOAD BUILDER, PHASE 3", +0x7B, "CORE LOAD BUILDER, PHASE 4", +0x7C, "CORE LOAD BUILDER, PHASE 5", +0x7D, "CORE LOAD BUILDER, PHASE 6", +0x7E, "CORE LOAD BUILDER, PHASE 7", +0x7F, "CORE LOAD BUILDER, PHASE 8", +0x80, "CORE LOAD BUILDER, PHASE 9", +0x81, "CORE LOAD BUILDER, PHASE 10", +0x82, "CORE LOAD BUILDER, PHASE 11", +0x83, "CORE LOAD BUILDER, PHASE 12", +0x84, "CORE LOAD BUILDER, PHASE 13", + +0X8C, "SYS 1403 READER", +0x8D, "SYS 1132 PRINTER", +0x8E, "SYS CONSOLE PRINTER", +0x8F, "SYS 2501/1442 READER", +0x90, "SYS 1442/1442 READER", +0x91, "SYS 1134/1055 PAPER TAPE IO", +0x92, "SYS KEYBOARD", +0x93, "SYS 2501/1442 CONVERSION", +0x94, "SYS 1134/1055 CONVERSION", +0x95, "SYS KEYBOARD CONVERSION", +0x96, "DISKZ", +0x97, "SYS DISK1", +0x98, "SYS DISKN", + +0xA0, "CIL CORE IMAGE LOADER - PHASE 1", +0xA1, "CIL CORE IMAGE LOADER - PHASE 2", + +0XB0, "RPG B0", +0XB1, "RPG B1", +0XB2, "RPG B2", +0XB3, "RPG B3", +0XB4, "RPG B4", +0XB5, "RPG B5", +0XB6, "RPG B6", +0XB7, "RPG B7", +0XB8, "RPG B8", +0XB9, "RPG B9", +0XBA, "RPG BA", +0XBB, "RPG BB", +0XBC, "RPG BC", +0XBD, "RPG BD", +0XBE, "RPG BE", +0XBF, "RPG BF", +0XC0, "RPG C0", +0XC1, "RPG C1", +0XC2, "RPG C2", +0XC3, "RPG C3", +0XC4, "RPG C4", +0XC5, "RPG C5", +0XC6, "RPG C6", +0XC7, "RPG C7", +0XC8, "RPG C8", +0XC9, "RPG C9", +0XCA, "RPG CA", +0XCB, "RPG CB", +0XCC, "RPG CC", + +0XCD, "DUP PART 2 - CTRL", +0XCE, "DUP PART 2 - MACRO UPDATE", + +0XCF, "ASM INITIALIZATION PHASE", +0xD0, "ASM CARD CONVERSION PHASE", +0xD1, "ASM DSF OUTPUT PHASE", +0xD2, "ASM INTERMEDIATE INPUT PHASE", +0xD3, "ASM END STATEMENT PHASE", +0xD4, "ASM ASSEMBLY ERROR PHASE", +0xD5, "ASM CONTROL CARDS I", +0xD6, "ASM CONTROL CARDS 2", +0xD7, "ASM DUMMY SYST SYMBOL TBL", +0xD8, "ASM SYMBOL TABLE OPTIONS PHASE", +0xD9, "ASM EXIT PHASE", +0xDA, "ASM PROG HEADER MNEMONICS PH", +0xDB, "ASM FILE STATEMENT PHASE", +0xDC, "ASM COMMON SUBROUTINES,ASCOM", +0xE4, "ASM INTERMEDIATE I/O", +0xE5, "ASM SYMBOL TABLE OVERFLOW", +0xDD, "ASM PROG CONTROL MNEMONICS PH", +0xDE, "ASM IMPERATIVE STATEMENTS PH", +0xDF, "ASM DECML,XFLC PROCESSING PH", +0xE0, "ASM DECIMAL CONVERSION PH", +0xE1, "ASM PROG LINKAGE PHASE", +0xE2, "ASM DMES PROCESSING PHASE", +0xE3, "ASM PUNCH CONVERSION PHASE", +0xE6, "ASM GRAPHIC ORDER PHASE", +0xE8, "ASM CONTROL CARDS III", +0xE9, "ASM MACRO PH 1 - SPECIAL OP AND PREPROCESSI", +0xEA, "MACRO PHASE 1A - SPECIAL PSEUDO OPS", +0xEB, "MACRO PHASE 1B - CONDITIONAL ASM PSEUDO OPS", +0xEC, "ASM MACRO PHASE 2 - MACRO DEFINITION", +0xED, "MACRO PHASE 2A - MACRO DEFINITION", +0xEE, "MACRO PHASE 2B - MACRO DEFINITION", +0xEF, "MACRO PHASE 3 - MACRO EXPANSION", +0xF0, "MACRO PHASE 3A - MACRO EXPANSION", +0xF1, "MACRO PHASE 3B - MACRO EXPANSION", +0xE7, "ASM DIVISION OPERATOR", +0xF2, "ASM CROSS-REFERENCE PART I", +0xF3, "ASM CROSS-REFERENCE PART 2A", +0xF4, "ASM CROSS-REFERENCE PART 2B", +0xF5, "ASM CROSS-REFERENCE PART 2C", +0xF6, "ASM CROSS-REFERENCE PART III", diff --git a/Ibm1130/dmsr2v12slet.h b/Ibm1130/dmsr2v12slet.h new file mode 100644 index 0000000..7d00676 --- /dev/null +++ b/Ibm1130/dmsr2v12slet.h @@ -0,0 +1,129 @@ +/* DMS R2V12 SLET without RPG, for debugging only */ + +0x0001, 0x7c50, 0x032f, 0x0008, +0x0002, 0x11de, 0x05a2, 0x000b, +0x0003, 0x21de, 0x05a2, 0x0010, +0x0004, 0x01de, 0x03c0, 0x0015, +0x0005, 0x41de, 0x0550, 0x0018, +0x0006, 0x01de, 0x03c0, 0x001d, +0x0007, 0x01de, 0x05a2, 0x0020, +0x0008, 0x01de, 0x05a2, 0x0025, +0x0009, 0x01de, 0x0500, 0x002a, +0x000a, 0x7a06, 0x00db, 0x002e, +0x000b, 0x7a06, 0x0035, 0x002f, +0x000c, 0x7a06, 0x00d8, 0x0030, +0x000d, 0x7782, 0x087c, 0x0031, +0x000e, 0x7a06, 0x0248, 0x0038, +0x000f, 0x7a06, 0x0248, 0x003a, +0x0010, 0x7a06, 0x0248, 0x003c, +0x0011, 0x01de, 0x0280, 0x003e, +0x0012, 0x0e6e, 0x0140, 0x0040, +0x001f, 0x760c, 0x09f1, 0x0041, +0x0020, 0x7a34, 0x0500, 0x0049, +0x0021, 0x7a34, 0x0280, 0x004d, +0x0022, 0x7a34, 0x03c0, 0x004f, +0x0023, 0x7a34, 0x0500, 0x0052, +0x0024, 0x7a34, 0x03c0, 0x0056, +0x0025, 0x7a34, 0x0280, 0x0059, +0x0026, 0x7a34, 0x0500, 0x005b, +0x0027, 0x7a34, 0x03f0, 0x005f, +0x0028, 0x7a34, 0x03c0, 0x0063, +0x0029, 0x7a34, 0x03c0, 0x0066, +0x002a, 0x7a34, 0x03c0, 0x0069, +0x002b, 0x7a34, 0x03c0, 0x006c, +0x002c, 0x7a34, 0x0500, 0x006f, +0x002d, 0x7a34, 0x0500, 0x0073, +0x002e, 0x7a34, 0x0500, 0x0077, +0x002f, 0x7a34, 0x0500, 0x007b, +0x0030, 0x7a34, 0x0500, 0x007f, +0x0031, 0x7a34, 0x0404, 0x0083, +0x0032, 0x7a34, 0x03c0, 0x0087, +0x0033, 0x7a34, 0x03c0, 0x008a, +0x0034, 0x7a34, 0x0280, 0x008d, +0x0035, 0x7a34, 0x03c0, 0x008f, +0x0036, 0x7a34, 0x03c0, 0x0092, +0x0037, 0x7a34, 0x0500, 0x0095, +0x0038, 0x7b96, 0x03c0, 0x0099, +0x0039, 0x766e, 0x013e, 0x009c, +0x006e, 0x04fe, 0x02fe, 0x009d, +0x006f, 0x07fe, 0x052b, 0x00a0, +0x0070, 0x07fe, 0x0280, 0x00a5, +0x0071, 0x07fe, 0x0280, 0x00a7, +0x0072, 0x07fe, 0x03ea, 0x00a9, +0x0073, 0x0506, 0x04f8, 0x00ad, +0x0074, 0x0400, 0x0189, 0x00b1, +0x0078, 0x01e0, 0x0782, 0x00b3, +0x0079, 0x05bc, 0x04dd, 0x00ba, +0x007a, 0x08b6, 0x01e8, 0x00be, +0x007b, 0x08b6, 0x01e8, 0x00c0, +0x007c, 0x08b6, 0x01e8, 0x00c2, +0x007d, 0x08b6, 0x01e8, 0x00c4, +0x007e, 0x0aa0, 0x0140, 0x00c6, +0x007f, 0x0aa0, 0x0140, 0x00c7, +0x0080, 0x0aa0, 0x0140, 0x00c8, +0x0081, 0x0aa0, 0x0140, 0x00c9, +0x0082, 0x0be2, 0x0140, 0x00ca, +0x0083, 0x08b6, 0x01e8, 0x00cb, +0x0084, 0x0aa0, 0x0140, 0x00cd, +0x008c, 0x0000, 0x0134, 0x80ceU, +0x008d, 0x0000, 0x0113, 0x00cf, +0x008e, 0x0000, 0x011f, 0x00d0, +0x008f, 0x0000, 0x009c, 0x80d1U, +0x0090, 0x0000, 0x00ab, 0x00d2, +0x0091, 0x0000, 0x016c, 0x80d3U, +0x0092, 0x0000, 0x0174, 0x00d5, +0x0093, 0x0000, 0x00b9, 0x00d7, +0x0094, 0x0000, 0x0003, 0x80d8U, +0x0095, 0x0000, 0x0003, 0x00d9, +0x0096, 0x00f0, 0x00ec, 0x00da, +0x0097, 0x00f0, 0x01a2, 0x00db, +0x0098, 0x00f0, 0x02b0, 0x00dd, +0x0099, 0x0000, 0x0113, 0x00cf, +0x009a, 0x0000, 0x00ab, 0x00d2, +0x009b, 0x0000, 0x00ab, 0x00d2, +0x009c, 0x0000, 0x00b9, 0x00d7, +0x009d, 0x0000, 0x00b9, 0x00d7, +0x00a0, 0x0000, 0x016c, 0x00e0, +0x00a1, 0x0000, 0x01c0, 0x00e2, +0x00cd, 0x11de, 0x0280, 0x00e4, +0x00ce, 0x01de, 0x11df, 0x00e6, +0x00cf, 0x01e0, 0x026b, 0x00f5, +0x00d0, 0x01e8, 0x00bb, 0x00f7, +0x00d1, 0x01e8, 0x005f, 0x00f8, +0x00d2, 0x01e8, 0x005f, 0x00f9, +0x00d3, 0x0280, 0x01d5, 0x00fa, +0x00d4, 0x0ad0, 0x0145, 0x00fc, +0x00d5, 0x0280, 0x01d6, 0x00fe, +0x00d6, 0x0280, 0x0113, 0x0100, +0x00d7, 0x0000, 0x0130, 0x0101, +0x00d8, 0x07a8, 0x0254, 0x0102, +0x00d9, 0x0280, 0x01d7, 0x0104, +0x00da, 0x0280, 0x01a0, 0x0106, +0x00db, 0x0282, 0x00a3, 0x0108, +0x00dc, 0x0458, 0x05a7, 0x0109, +0x00dd, 0x0280, 0x01d5, 0x010e, +0x00de, 0x0280, 0x01d6, 0x0110, +0x00df, 0x0280, 0x017c, 0x0112, +0x00e0, 0x0282, 0x0127, 0x0114, +0x00e1, 0x0280, 0x0196, 0x0115, +0x00e2, 0x0280, 0x01d8, 0x0117, +0x00e3, 0x0280, 0x0099, 0x0119, +0x00e4, 0x098a, 0x005f, 0x011a, +0x00e5, 0x098a, 0x0062, 0x011b, +0x00e6, 0x0eca, 0x03c1, 0x011c, +0x00e7, 0x0280, 0x00b8, 0x0120, +0x00e8, 0x0280, 0x017f, 0x0121, +0x00e9, 0x0280, 0x01d6, 0x0123, +0x00ea, 0x0280, 0x01d9, 0x0125, +0x00eb, 0x0280, 0x01d9, 0x0127, +0x00ec, 0x0280, 0x01ca, 0x0129, +0x00ed, 0x0280, 0x01c2, 0x012b, +0x00ee, 0x05dc, 0x0158, 0x012d, +0x00ef, 0x07ac, 0x0051, 0x012f, +0x00f0, 0x0280, 0x01af, 0x0130, +0x00f1, 0x12f4, 0x027f, 0x0132, +0x00f2, 0x0280, 0x01c7, 0x0134, +0x00f3, 0x07a8, 0x0052, 0x0136, +0x00f4, 0x0924, 0x005b, 0x0137, +0x00f5, 0x0886, 0x003d, 0x0138, +0x00f6, 0x0eca, 0x03b2, 0x0139, diff --git a/Ibm1130/hand.cur b/Ibm1130/hand.cur new file mode 100644 index 0000000000000000000000000000000000000000..eb0796dd30b18ae73defe98172ce1b6f3802dd91 GIT binary patch literal 326 zcmb7;K@Ng26h!A+lkQ|^Vl={%r6&-M#03;?qDScsyaGq)k_9qO!GxWO)4cZow0}Cl z28N=*tcwAARi!9IElg|H_gI>BC%HENRLReEz){=++&dtfdYtb)4s75b4R-Al_UhoT zz;;lU0C + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "ibm1130res.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +///////////////////////////////////////////////////////////////////////////// +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_CONSOLE BITMAP MOVEABLE PURE "1130consoleblank.bmp" +FULL_1442 BITMAP MOVEABLE PURE "1442full.bmp" +EOF_1442 BITMAP MOVEABLE PURE "1442eof.bmp" +EMPTY_1442 BITMAP MOVEABLE PURE "1442empty.bmp" +MIDDLE_1442 BITMAP MOVEABLE PURE "1442middle.bmp" +FULL_1132 BITMAP MOVEABLE PURE "1132full.bmp" +EMPTY_1132 BITMAP MOVEABLE PURE "1132empty.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +IDC_MYHAND CURSOR DISCARDABLE "HAND.CUR" + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Ibm1130/ibm1130_conin.h b/Ibm1130/ibm1130_conin.h new file mode 100644 index 0000000..a142e86 --- /dev/null +++ b/Ibm1130/ibm1130_conin.h @@ -0,0 +1,43 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +/* + * 03 ctrl-C => Program stop (not handled here) + * 05 ctrl-E => Simulator stop (not handled here) + * 08 ctrl-H => Backspace + * 0D ctrl-M (Enter) => EOF + * 11 ctrl-Q => Interrupt request (not handled here) + * 12 ctrl-R => "cent" (R because that's where cent is on the 1130 keyboard) + * 15 ctrl-U => Erase Field + * 7E ~ => "not" + * FF Del => Backspace again + */ + +static uint16 ascii_to_conin[] = /* ASCII to ((hollerith << 4) | special key flags) */ +{ + /* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F */ + /* 00 */ 0, 0, 0, 0, 0, 0, 0, 0,0x0004, 0, 0, 0, 0,0x0008, 0, 0, + /* 10 */ 0, 0,0x8820, 0, 0,0x0002, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 20 */ 0x0001,0x4820,0x0060,0x0420,0x4420,0x2220,0x8000,0x0120,0x8120,0x4120,0x4220,0x80a0,0x2420,0x4000,0x8420,0x3000, + /* 30 */ 0x2000,0x1000,0x0800,0x0400,0x0200,0x0100,0x0080,0x0040,0x0020,0x0010,0x0820,0x40a0,0x8220,0x00a0,0x20a0,0x2060, + /* 40 */ 0x0220,0x9000,0x8800,0x8400,0x8200,0x8100,0x8080,0x8040,0x8020,0x8010,0x5000,0x4800,0x4400,0x4200,0x4100,0x4080, + /* 50 */ 0x4040,0x4020,0x4010,0x2800,0x2400,0x2200,0x2100,0x2080,0x2040,0x2020,0x2010, 0, 0, 0, 0,0x2120, + /* 60 */ 0,0x9000,0x8800,0x8400,0x8200,0x8100,0x8080,0x8040,0x8020,0x8010,0x5000,0x4800,0x4400,0x4200,0x4100,0x4080, + /* 70 */ 0x4040,0x4020,0x4010,0x2800,0x2400,0x2200,0x2100,0x2080,0x2040,0x2020,0x2010, 0,0xB060, 0, 0,0x0004, + /* 80 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* 90 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* a0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* b0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* c0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* d0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* e0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* f0 */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,0x0004, +}; diff --git a/Ibm1130/ibm1130_conout.h b/Ibm1130/ibm1130_conout.h new file mode 100644 index 0000000..478e005 --- /dev/null +++ b/Ibm1130/ibm1130_conout.h @@ -0,0 +1,58 @@ +/* IBM1130 CONSOLE OUTPUT TO ASCII CONVERSION TABLE + * + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +#define _0_ '\0' + +#define CENT_ '\xA2' /* cent and not: standard DOS mapping */ +#define NOT_ '\xAC' +#define IGNR_ '\xFF' +#define CRLF_ '\r' + +#define COUT_IS_CTRL 0x01 /* conout characters with bit 1 set are controls: */ + +#define COUT_CTRL_BLACK 0x04 /* none or one of these bits */ +#define COUT_CTRL_RED 0x08 + +#define COUT_CTRL_LINEFEED 0x02 /* plus none or one of these bits */ +#define COUT_CTRL_BACKSPACE 0x10 +#define COUT_CTRL_SPACE 0x20 +#define COUT_CTRL_TAB 0x40 +#define COUT_CTRL_RETURN 0x80 + +#ifdef _MSC_VER +# pragma warning(disable:4245) /* enable int->char demotion warning caused by characters with high-bit set */ +#endif + +static unsigned char conout_to_ascii[] = /* console output code to ASCII */ +{ + /* 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F */ + /* 00 */ '.', IGNR_,CENT_, '\n', '@', IGNR_,'%', _0_, _0_, IGNR_,_0_, _0_, _0_, _0_, _0_, _0_, + /* 10 */ 'F', '\b', 'f', _0_, 'G', _0_, 'g', _0_, 'B', _0_, 'b', _0_, 'C', _0_, 'c', _0_, + /* 20 */ 'I', ' ', 'i', _0_, 'H', _0_, 'h', _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, + /* 30 */ 'D', _0_, 'd', _0_, 'E', _0_, 'e', _0_, _0_, _0_, _0_, _0_, 'A', _0_, 'a', _0_, + /* 40 */ '$', '\t', '!', _0_, '&', _0_, '>', _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, + /* 50 */ 'O', _0_, 'o', _0_, 'P', _0_, 'o', _0_, 'K', _0_, 'k', _0_, 'L', _0_, 'l', _0_, + /* 60 */ 'R', _0_, 'r', _0_, 'Q', _0_, 'q', _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, + /* 70 */ 'M', _0_, 'm', _0_, 'N', _0_, 'n', _0_, _0_, _0_, _0_, _0_, 'J', _0_, 'j', _0_, + /* 80 */ ',', CRLF_, ':', _0_, '-', _0_, '?', _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, + /* 90 */ 'W', _0_, 'w', _0_, 'X', _0_, 'x', _0_, 'S', _0_, 's', _0_, 'T', _0_, 't', _0_, + /* A0 */ 'Z', _0_, 'z', _0_, 'Y', _0_, 'y', _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, + /* B0 */ 'U', _0_, 'u', _0_, 'V', _0_, 'v', _0_, _0_, _0_, _0_, _0_, '/', _0_, '_', _0_, + /* C0 */ '#', _0_, '=', _0_, '0', _0_, '|', _0_, _0_, _0_, _0_, _0_, 'J', _0_, 'j', _0_, + /* D0 */ '6', _0_, ';', _0_, '7', _0_, '*', _0_, '2', _0_, '+', _0_, '3', _0_, '<', _0_, + /* E0 */ '9', _0_, '"', _0_, '8', _0_, '\'', _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, _0_, + /* F0 */ '4', _0_, NOT_, _0_, '5', _0_, ')', _0_, _0_, _0_, _0_, _0_, '1', _0_, '(', _0_, +}; + +#ifdef _MSC_VER +# pragma warning(default:4245) /* enable int->char demotion warning */ +#endif diff --git a/Ibm1130/ibm1130_cpu.c b/Ibm1130/ibm1130_cpu.c new file mode 100644 index 0000000..1db5600 --- /dev/null +++ b/Ibm1130/ibm1130_cpu.c @@ -0,0 +1,1883 @@ +/* ibm1130_cpu.c: IBM 1130 CPU simulator + + Based on the SIMH package written by Robert M Supnik + + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + + 25-Jun-01 BLK Written + 10-May-02 BLK Fixed bug in MDX instruction + 27-Mar-02 BLK Made BOSC work even in short form + 16-Aug-02 BLK Fixed bug in multiply instruction; didn't work with negative values + 18-Mar-03 BLK Fixed bug in divide instruction; didn't work with negative values + 23-Jul-03 BLK Prevented tti polling in CGI mode + 24-Nov-03 BLK Fixed carry bit error in subtract and subtract double, found by Bob Flanders + 20-Oct-04 BLK Changed "(unsigned int32)" to "(uint32)" to accomodate improved definitions of simh types + Also commented out my echo command as it's now a standard simh command + 27-Nov-05 BLK Added Arithmetic Factor Register support per Carl Claunch (GUI only) + 06-Dec-06 BLK Moved CGI stuff out of ibm1130_cpu.c + +>> To do: verify actual operands stored in ARF, need to get this from state diagrams in the schematic set + Also: determine how many bits are actually stored in the IAR in a real 1130, by forcing wraparound + and storing the IAR. + + IBM 1800 support is just beginning. Mode set is done (SET CPU 1800 or SET CPU 1130). + Index registers are handled (1800 has real registers, 1130 uses core locations 1, 2 and 3 -- + but does the 1800 make its hardware index registers appear in the address space?) + Need to add: memory protect feature, more interrupt levels, GUI mods, IO device mods, timers, watchdog. + Memory protect was interesting -- they borrowed one of the two parity bits. XIO(0) on 1800 is used for + interval timers, console data switches, console sense/program select/CE switches, interrupt mask register, + programmed interrupt, console interrupt and operations monitor (watchdog) + very interesting stuff. + + The register state for the IBM 1130 CPU is: + + IAR instruction address register + ACC accumulator + EXT accumulator extension + Oflow overflow bit + Carry carry bit + CES console entry switches + ipl current interrupt level, -1 = non interrupt + iplpending bitmap of pending interrupts + wait_state current CPU state: running or waiting + DSW console run/stop switch device status word + RUNMODE processor step/run mode (may also imply IntRun) + BREAK breakpoint address + WRU simulator-break character + IntRun Int Run flag (causes level 5 interrupt after every instruction) + ILSW0..5 interrupt level status words + XR1, 2, 3 for IBM 1800 only, index registers 1, 2, and 3 + + The SAR (storage address register) and SBR (storage buffer register) are updated + but not saved in the CPU state; they matter only to the GUI. + + Interrupt handling: interrupts occur when any device on any level has an + active interrupt. XIO commands can clear specific IRQ bits. When this + happens, we have to evaluate all devices on the same IRQ level for remaining + indicators. The flag int_req is set with a bit corresponding to the IRQ level + when any interrupt indicator is activated. + + The 1130 console has a switch that controls several run modes: SS (single processor + step), SCLK (single clock step), SINST (single instruction step), INT_RUN + (IRQ 5 after each non interrupt-handler instruction) and RUN (normal operation). + This simulator does not implement SS and SCLK. The simulator GUI console handles + SINST, so we only have to worry about INT_RUN. The console command SET CPU IntRun sets + the tmode (trace mode) flag; this causes a level 5 interrupt after each + instruction. + + The IBM 1130 instruction formats are + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | opcode | F| T | | general format + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | opcode | 0| T | DISPLACEMENT | short instruction + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | opcode | 1| T | I| MODIFIER | long instruction + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | ADDRESS | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + opcode in MSBits + + F = format. 0 = short (1 word), 1 = long (2 word) instruction + + T = Tag 00 = no index register (e.g. IAR relative) + 01 = use index register 1 (e.g. core address 1 = M[1]) + 02 = use index register 2 (e.g. core address 2 = M[2]) + 03 = use index register 3 (e.g. core address 3 = M[3]) + + DISPLACEMENT = two's complement (must be sign-extended) + + I = Indirect + + Note that IAR = instruction address+1 when instruction is being decoded. + + In normal addressing mode, effective address (EA) is computed as follows: + + F = 0 T = 0 EA = IAR + DISPLACEMENT + 0 1 IAR + DISPLACEMENT + M[1] + 0 2 IAR + DISPLACEMENT + M[2] + 0 3 IAR + DISPLACEMENT + M[3] + + F = 1 T = 0 I = 0 EA = ADDRESS + 1 1 0 ADDRESS + M[1] + 1 2 0 ADDRESS + M[2] + 1 3 0 ADDRESS + M[3] + 1 0 1 M[ADDRESS] + 1 1 1 M[ADDRESS + M[1]] + 1 2 1 M[ADDRESS + M[2]] + 1 3 1 M[ADDRESS + M[3]] + + Loads or stores are then made to/from MEM[EA]. Some instructions have special + weird addressing modes. Simulator code precomputes standard addressing for + all instructions though it's not always used. + + General notes: + + Adding I/O devices requires modifications to three modules: + + ibm1130_defs.h add interrupt request definitions + ibm1130_cpu.c add XIO command linkages + ibm1130_sys.c add to sim_devices array +*/ + +/* ------------------------------------------------------------------------ + * Definitions + * ------------------------------------------------------------------------ */ + +#include + +#include "ibm1130_defs.h" + +#define save_ibkpt (cpu_unit.u3) /* will be SAVEd */ + +#define UPDATE_BY_TIMER +#define ENABLE_BACKTRACE +/* #define USE_MY_ECHO_CMD */ /* simh now has echo command built in */ +#define ENABLE_1800_SUPPORT /* define to enable support for 1800 CPU simulation mode */ + +static void cgi_start(void); +static void cgi_stop(t_stat reason); +static int simh_status_to_stopcode (int status); + +/* hook pointers from scp.c */ +void (*sim_vm_init) (void) = &sim_init; +extern char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream); +extern void (*sim_vm_post) (t_bool from_scp); +extern CTAB *sim_vm_cmd; + +/* space to store extra simulator-specific commands */ +#define MAX_EXTRA_COMMANDS 10 +CTAB x_cmds[MAX_EXTRA_COMMANDS]; + +#ifdef _WIN32 +# define CRLF "\r\n" +#else +# define CRLF "\n" +#endif + +/* ------------------------------------------------------------------------ + * initializers for globals + * ------------------------------------------------------------------------ */ + +#define SIGN_BIT(v) ((v) & 0x8000) +#define DWSIGN_BIT(v) ((v) & 0x80000000) + +uint16 M[MAXMEMSIZE]; /* core memory, up to 32Kwords (note: don't even think about trying 64K) */ +uint16 ILSW[6] = {0,0,0,0,0,0}; /* interrupt level status words */ +uint16 XR[3] = {0,0,0}; /* IBM 1800 index registers */ +int32 IAR; /* instruction address register */ +int32 prev_IAR; /* instruction address register at start of current instruction */ +int32 SAR, SBR; /* storage address/buffer registers */ +int32 OP, TAG, CCC; /* instruction decoded pieces */ +int32 CES; /* console entry switches */ +int32 ACC, EXT; /* accumulator and extension */ +int32 ARF; /* arithmetic factor, a non-addressable internal CPU register */ +int32 RUNMODE; /* processor run/step mode */ +int32 ipl = -1; /* current interrupt level (-1 = not handling irq) */ +int32 iplpending = 0; /* interrupted IPL's */ +int32 tbit = 0; /* trace flag (causes level 5 IRQ after each instr) */ +int32 V = 0, C = 0; /* condition codes */ +int32 wait_state = 0; /* wait state (waiting for an IRQ) */ +int32 wait_lamp = TRUE; /* alternate indicator to light the wait lamp on the GUI */ +int32 int_req = 0; /* sum of interrupt request levels active */ +int32 int_lamps = 0; /* accumulated version of int_req - gives lamp persistence */ +int32 int_mask; /* current active interrupt mask (ipl sensitive) */ +int32 mem_mask; /* mask for memory address bits based on current memory size */ +int32 cpu_dsw = 0; /* CPU device status word */ +int32 ibkpt_addr = -1; /* breakpoint addr */ +int32 sim_gui = TRUE; /* enable gui */ +t_bool running = FALSE; /* TRUE if CPU is running */ +t_bool power = TRUE; /* TRUE if CPU power is on */ +t_bool cgi = FALSE; /* TRUE if we are running as a CGI program */ +t_bool cgiwritable = FALSE; /* TRUE if we can write the disk images back to the image file in CGI mode */ +t_bool is_1800 = FALSE; /* TRUE if we are simulating an IBM 1800 processor */ +t_stat reason; /* CPU execution loop control */ + +static int32 int_masks[6] = { + 0x00, 0x20, 0x30, 0x38, 0x3C, 0x3E /* IPL 0 is highest prio (sees no other interrupts) */ +}; + +/* ------------------------------------------------------------------------ + * Function declarations + * ------------------------------------------------------------------------ */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_svc (UNIT *uptr); +t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc); +t_stat cpu_set_type (UNIT *uptr, int32 value, char *cptr, void *desc); +void calc_ints (void); + +extern t_stat ts_wr (int32 data, int32 addr, int32 access); +extern t_stat detach_cmd (int flags, char *cptr); +extern UNIT cr_unit; +extern int32 sim_switches; + +#ifdef ENABLE_BACKTRACE + static void archive_backtrace(char *inst); + static void reset_backtrace (void); + static void show_backtrace (int nshow); + static t_stat backtrace_cmd (int flag, char *cptr); +#else + #define archive_backtrace(inst) + #define reset_backtrace() + #define show_backtrace(ntrace) +#endif + +#ifdef GUI_SUPPORT +# define ARFSET(v) ARF = (v) & 0xFFFF /* set Arithmetic Factor Register (used for display purposes only) */ +#else +# define ARFSET(v) /* without GUI, no need for setting ARF */ +#endif + +static void init_console_window (void); +static void destroy_console_window (void); +static t_stat view_cmd (int flag, char *cptr); +static t_stat cpu_attach (UNIT *uptr, char *cptr); +static t_bool bsctest (int32 DSPLC, t_bool reset_V); +static void exit_irq (void); +static void trace_instruction (void); + +/* ------------------------------------------------------------------------ + * CPU data structures: + * cpu_dev CPU device descriptor + * cpu_unit CPU unit descriptor + * cpu_reg CPU register list + * cpu_mod CPU modifier list + * + * The CPU is attachable; attaching a file to it write a log of instructions + * and registers + * ------------------------------------------------------------------------ */ + +#define UNIT_MSIZE (1 << (UNIT_V_UF + 7)) /* flag for memory size setting */ +#define UNIT_1800 (1 << (UNIT_V_UF + 0)) /* flag for 1800 mode */ + +UNIT cpu_unit = { UDATA (&cpu_svc, UNIT_FIX | UNIT_BINK | UNIT_ATTABLE | UNIT_SEQ, INIMEMSIZE) }; + +REG cpu_reg[] = { + { HRDATA (IAR, IAR, 32) }, + { HRDATA (ACC, ACC, 32) }, + { HRDATA (EXT, EXT, 32) }, + { FLDATA (Oflow, V, 1) }, + { FLDATA (Carry, C, 1) }, + { HRDATA (CES, CES, 32) }, + { HRDATA (ipl, ipl, 32), REG_RO }, + { HRDATA (iplpending, iplpending, 32), REG_RO }, + { HRDATA (wait_state, wait_state, 32)}, + { HRDATA (DSW, cpu_dsw, 32), REG_RO }, + { HRDATA (RUNMODE, RUNMODE, 32) }, + { HRDATA (BREAK, ibkpt_addr, 32) }, + { ORDATA (WRU, sim_int_char, 8) }, + { FLDATA (IntRun, tbit, 1) }, + + { HRDATA (ILSW0, ILSW[0], 32), REG_RO }, + { HRDATA (ILSW1, ILSW[1], 32), REG_RO }, + { HRDATA (ILSW2, ILSW[2], 32), REG_RO }, + { HRDATA (ILSW3, ILSW[3], 32), REG_RO }, + { HRDATA (ILSW4, ILSW[4], 32), REG_RO }, + { HRDATA (ILSW5, ILSW[5], 32), REG_RO }, + +#ifdef ENABLE_1800_SUPPORT + { HRDATA (IS_1800, is_1800, 32), REG_RO|REG_HIDDEN}, /* is_1800 flag is part of state, but hidden */ + { HRDATA (XR1, XR[0], 16), REG_RO|REG_HIDDEN}, /* index registers are unhidden if CPU set to 1800 mode */ + { HRDATA (XR2, XR[1], 16), REG_RO|REG_HIDDEN}, + { HRDATA (XR3, XR[2], 16), REG_RO|REG_HIDDEN}, +#endif + + { HRDATA (ARF, ARF, 32) }, + { NULL} +}; + +MTAB cpu_mod[] = { + { UNIT_MSIZE, 4096, NULL, "4KW", &cpu_set_size}, + { UNIT_MSIZE, 8192, NULL, "8KW", &cpu_set_size}, + { UNIT_MSIZE, 16384, NULL, "16KW", &cpu_set_size}, + { UNIT_MSIZE, 32768, NULL, "32KW", &cpu_set_size}, +#ifdef ENABLE_1800_SUPPORT + { UNIT_1800, 0, "1130", "1130", &cpu_set_type}, + { UNIT_1800, UNIT_1800, "1800", "1800", &cpu_set_type}, +#endif + { 0 } }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 16, 1, 16, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, cpu_attach, NULL}; /* attaching to CPU creates cpu log file */ + +/* ------------------------------------------------------------------------ + * Memory read/write -- save SAR and SBR on the way in and out + * + * (It can be helpful to set breakpoints on a = 1, 2, or 3 in these routines + * to detect attempts to read/set index registers using normal memory addessing. + * APL\1130 does this in some places, I think these are why it had to be modified + * to run on the 1800. Of course not all read/write to 1, 2 or implies an attempt + * to read/set and index register -- they could using the address in the normal way). + * ------------------------------------------------------------------------ */ + +int32 ReadW (int32 a) +{ + SAR = a; + SBR = (int32) M[(a) & mem_mask]; + return SBR; +} + +void WriteW (int32 a, int32 d) +{ + SAR = a; + SBR = d; + M[a & mem_mask] = (int16) d; +} + +/* ------------------------------------------------------------------------ + * read and write index registers. On the 1130, they're in core addresses 1, 2, 3. + * on the 1800, they're separate registers + * ------------------------------------------------------------------------ */ + +static uint16 ReadIndex (int32 tag) +{ +#ifdef ENABLE_1800_SUPPORT + if (is_1800) + return XR[tag-1]; /* 1800: fetch from register */ +#endif + + SAR = tag; /* 1130: ordinary read from memory (like ReadW) */ + SBR = (int32) M[(tag) & mem_mask]; + return SBR; +} + +static void WriteIndex (int32 tag, int32 d) +{ +#ifdef ENABLE_1800_SUPPORT + if (is_1800) { + XR[tag-1] = d; /* 1800: store in register */ + return; + } +#endif + + SAR = tag; /* 1130: ordinary write to memory (same as WriteW) */ + SBR = d; + M[tag & mem_mask] = (int16) d; +} + +/* ------------------------------------------------------------------------ + * upcase - force a string to uppercase (ASCII) + * ------------------------------------------------------------------------ */ + +char *upcase (char *str) +{ + char *s; + + for (s = str; *s; s++) { + if (*s >= 'a' && *s <= 'z') + *s -= 32; + } + + return str; +} + +/* ------------------------------------------------------------------------ + * calc_ints - set appropriate bits in int_req if any interrupts are pending on given levels + * + * int_req: + * bit 5 4 3 2 1 0 + * \ \ \ \ \ \ + * \ \ \ \ \ interrupt level 5 pending (lowest priority) + * \ . . . + * interrupt level 0 pending (highest priority) + * + * int_mask is set according to current interrupt level (ipl) + * + * 0 0 0 0 0 0 ipl = 0 (currently servicing highest priority interrupt) + * 1 0 0 0 0 0 1 + * 1 1 0 0 0 0 2 + * 1 1 1 0 0 0 3 + * 1 1 1 1 0 0 4 + * 1 1 1 1 1 0 5 (currently servicing lowest priority interrupt) + * 1 1 1 1 1 1 -1 (not servicing an interrupt) + * ------------------------------------------------------------------------ */ + +void calc_ints (void) +{ + register int i; + register int32 newbits = 0; + + GUI_BEGIN_CRITICAL_SECTION /* using critical section here so we don't mislead the GUI thread */ + + for (i = 6; --i >= 0; ) { + newbits >>= 1; + if (ILSW[i]) + newbits |= 0x20; + } + + int_req = newbits; + int_lamps |= int_req; + int_mask = (ipl < 0) ? 0xFFFF : int_masks[ipl]; /* be sure this is set correctly */ + + GUI_END_CRITICAL_SECTION +} + +/* ------------------------------------------------------------------------ + * instruction processor + * ------------------------------------------------------------------------ */ + +#define INCREMENT_IAR IAR = (IAR + 1) & mem_mask +#define DECREMENT_IAR IAR = (IAR - 1) & mem_mask + +void bail (char *msg) +{ + printf("%s\n", msg); + exit(1); +} + +static void weirdop (char *msg, int offset) +{ + printf("Weird opcode: %s at %04x\n", msg, IAR+offset); +} + +static char *xio_devs[] = { + "0?", "console", "1142card", "1134papertape", + "dsk0", "1627plot", "1132print", "switches", + "1231omr", "2501card", "comm", "b?", + "sys7", "d?", "e?", "f?", + "10?", "dsk1", "dsk2", "dsk3", + "dsk4", "dsk5", "dsk6", "dsk7+", + "18?", "2250disp", "2741attachment", "1b", + "1c?", "1d?", "1e?", "1f?" +}; + +static char *xio_funcs[] = { + "0?", "write", "read", "sense_irq", + "control", "initw", "initr", "sense" +}; + +t_stat sim_instr (void) +{ + extern int32 sim_interval; + extern UNIT *sim_clock_queue; + int32 i, eaddr, INDIR, IR, F, DSPLC, word2, oldval, newval, src, src2, dst, abit, xbit; + int32 iocc_addr, iocc_op, iocc_dev, iocc_func, iocc_mod; + char msg[50]; + int cwincount = 0, status; + static long ninstr = 0; + static char *intlabel[] = {"INT0","INT1","INT2","INT3","INT4","INT5"}; + + if (cgi) /* give CGI hook function a chance to do something */ + cgi_start(); + + if (running) /* this is definitely not reentrant */ + return -1; + + if (! power) /* this matters only to the GUI */ + return STOP_POWER_OFF; + + running = TRUE; + + mem_mask = MEMSIZE - 1; /* set other useful variables */ + calc_ints(); + + /* Main instruction fetch/decode loop */ + + reason = 0; + wait_lamp = 0; /* release lock on wait lamp */ + +#ifdef GUI_SUPPORT + update_gui(TRUE); + gui_run(TRUE); +#endif + + while (reason == 0) { + IAR &= mem_mask; + +#ifdef GUI_SUPPORT +#ifndef UPDATE_BY_TIMER +#if (UPDATE_INTERVAL > 0) + if (--cwincount <= 0) { + update_gui(FALSE); /* update console lamps only every so many instructions */ + cwincount = UPDATE_INTERVAL + (rand() % MIN(UPDATE_INTERVAL, 32)); + } +#else + update_gui(FALSE); +#endif /* ifdef UPDATE_INTERVAL */ +#endif /* ifndef UPDATE_BY_TIMER */ +#endif /* ifdef GUI_SUPPORT */ + + if (sim_interval <= 0) { /* any events timed out? */ + if (sim_clock_queue != NULL) { + if ((status = sim_process_event()) != 0) + reason = simh_status_to_stopcode(status); + + calc_ints(); + continue; + } + } + + if (int_req & int_mask) { /* any pending interrupts? */ + for (i = 0; i <= 5; i++) /* find highest pending interrupt */ + if ((int_req & int_mask) & (0x20 >> i)) + break; + + if (i >= 6) { /* nothing to do? */ + calc_ints(); /* weird. recalculate */ + continue; /* back to fetch */ + } + + GUI_BEGIN_CRITICAL_SECTION + + if (ipl >= 0) /* save previous IPL in bit stack */ + iplpending |= (0x20 >> ipl); + + ipl = i; /* set new interrupt level */ + int_mask = int_masks[i]; /* set appropriate mask */ + + GUI_END_CRITICAL_SECTION + + wait_state = 0; /* exit wait state */ + eaddr = ReadW(8+i); /* get IRQ vector */ + archive_backtrace(intlabel[i]); + WriteW(eaddr, IAR); /* save IAR */ + IAR = (eaddr+1) & mem_mask; /* go to next address */ + continue; /* now continue processing */ + } /* end if int_req */ + + if (wait_state) { /* waiting? */ + sim_interval = 0; /* run the clock out */ + + if (sim_qcount() <= (cgi ? 0 : 1)) { /* one routine queued? we're waiting for keyboard only */ + if (keyboard_is_busy()) { /* we are actually waiting for a keystroke */ + if ((status = sim_process_event()) != SCPE_OK) /* get it with wait_state still set */ + reason = simh_status_to_stopcode(status); + } + else { /* CPU is not expecting a keystroke (keyboard interrupt) */ + if (wait_state == WAIT_OP) + reason = STOP_WAIT; /* end the simulation */ + else + reason = STOP_INVALID_INSTR; + } + } + + if (gdu_active()) /* but don't stop simulator if 2250 GDU is running */ + reason = 0; + + continue; + } + + if (IAR == ibkpt_addr) { /* simulator breakpoint? */ + save_ibkpt = ibkpt_addr; /* save bkpt */ + ibkpt_addr = ibkpt_addr | ILL_ADR_FLAG; /* disable */ + sim_activate(&cpu_unit, 1); /* sched re-enable after next instruction */ + reason = STOP_IBKPT; /* stop simulation */ + cwincount = 0; + continue; + } + + ninstr++; + if (cpu_unit.flags & UNIT_ATT) + trace_instruction(); /* log CPU details if logging is enabled */ + + prev_IAR = IAR; /* save IAR before incrementing it */ + + IR = ReadW(IAR); /* fetch 1st word of instruction */ + INCREMENT_IAR; + sim_interval = sim_interval - 1; /* this constitutes one tick of the simulation clock */ + + OP = (IR >> 11) & 0x1F; /* opcode */ + F = IR & 0x0400; /* format bit: 1 = long instr */ + TAG = IR & 0x0300; /* tag bits: index reg x */ + if (TAG) + TAG >>= 8; + + /* here I compute the usual effective address on the assumption that the instruction will need it. Some don't. */ + + if (F) { /* long instruction, ASSUME it's valid (have to decrement IAR if not) */ + INDIR = IR & 0x0080; /* indirect bit */ + DSPLC = IR & 0x007F; /* displacement or modifier */ + if (DSPLC & 0x0040) + DSPLC |= ~ 0x7F; /* sign extend */ + + word2 = ReadW(IAR); /* get reference address */ + INCREMENT_IAR; /* bump the instruction address register */ + + eaddr = word2; /* assume standard addressing & compute effective address */ + if (TAG) /* if indexed */ + eaddr += ReadIndex(TAG); /* add index register value */ + if (INDIR) /* if indirect addressing */ + eaddr = ReadW(eaddr); /* pick up referenced address */ + } + else { /* short instruction, use displacement */ + INDIR = 0; /* never indirect */ + DSPLC = IR & 0x00FF; /* get displacement */ + if (DSPLC & 0x0080) + DSPLC |= ~ 0xFF; + + if (TAG) /* if indexed */ + eaddr = ReadIndex(TAG) + DSPLC; /* add index register value */ + else + eaddr = IAR + DSPLC; /* otherwise relative to IAR after fetch */ + } + + switch (OP) { /* decode instruction */ + case 0x01: /* --- XIO --- */ + iocc_addr = ReadW(eaddr); /* get IOCC packet */ + iocc_op = ReadW(eaddr|1); /* note 'or' not plus, address must be even for proper operation */ + + iocc_dev = (iocc_op >> 11) & 0x001F; + iocc_func = (iocc_op >> 8) & 0x0007; + iocc_mod = iocc_op & 0x00FF; + + if (cpu_unit.flags & UNIT_ATT) + trace_io("* XIO %s %s mod %02x addr %04x", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr); + +/* fprintf(stderr, "* XIO %s %s mod %02x addr %04x\n", xio_funcs[iocc_func], xio_devs[iocc_dev], iocc_mod, iocc_addr); */ + + ACC = 0; /* ACC is destroyed, and default XIO_SENSE_DEV result is 0 */ + + switch (iocc_func) { + case XIO_UNUSED: + sprintf(msg, "Unknown op %x on device %02x", iocc_func, iocc_dev); + xio_error(msg); + break; + + case XIO_SENSE_IRQ: /* examine current Interrupt Level Status Word */ + ACC = (ipl >= 0) ? ILSW[ipl] : 0; + break; + + default: /* perform device-specific operation */ + switch (iocc_dev) { + case 0x01: /* console keyboard and printer */ + xio_1131_console(iocc_addr, iocc_func, iocc_mod); + break; + case 0x02: /* 1142 card reader/punch */ + xio_1142_card(iocc_addr, iocc_func, iocc_mod); + break; + case 0x03: /* 1134 paper tape reader/punch */ + xio_1134_papertape(iocc_addr, iocc_func, iocc_mod); + break; + case 0x04: /* CPU disk storage */ + xio_disk(iocc_addr, iocc_func, iocc_mod, 0); + break; + case 0x05: /* 1627 plotter */ + xio_1627_plotter(iocc_addr, iocc_func, iocc_mod); + break; + case 0x06: /* 1132 Printer */ + xio_1132_printer(iocc_addr, iocc_func, iocc_mod); + break; + case 0x07: /* console switches, stop key, run mode */ + xio_1131_switches(iocc_addr, iocc_func, iocc_mod); + break; + case 0x08: /* 1231 optical mark reader */ + xio_1231_optical(iocc_addr, iocc_func, iocc_mod); + break; + case 0x09: /* 2501 card reader */ + xio_2501_card(iocc_addr, iocc_func, iocc_mod); + break; + case 0x0a: /* synchronous comm adapter */ + xio_sca(iocc_addr, iocc_func, iocc_mod); + break; + case 0x0c: /* IBM System/7 interprocessor link */ + xio_system7(iocc_addr, iocc_func, iocc_mod); + break; + case 0x11: /* 2310 Disk Storage, Drive 1, or 2311 Disk Storage Drive. Drive 1, Disk 1 */ + xio_disk(iocc_addr, iocc_func, iocc_mod, 1); + break; + case 0x12: /* 2310 Disk Storage, Drive 2, or 2311 Disk Storage Drive. Drive 1, Disk 2 */ + xio_disk(iocc_addr, iocc_func, iocc_mod, 2); + break; + case 0x13: /* 2310 Disk Storage, Drive 3, or 2311 Disk Storage Drive. Drive 1, Disk 3 */ + xio_disk(iocc_addr, iocc_func, iocc_mod, 3); + break; + case 0x14: /* 2310 Disk Storage, Drive 4, or 2311 Disk Storage Drive. Drive 1, Disk 4 */ + xio_disk(iocc_addr, iocc_func, iocc_mod, 4); + break; + case 0x15: /* 1403 Printer */ + xio_1403_printer(iocc_addr, iocc_func, iocc_mod); + break; + case 0x16: /* 2311 Disk Storage Drive. Drive 1, Disk 5 */ + xio_disk(iocc_addr, iocc_func, iocc_mod, -1); + break; + case 0x17: /* 2311 Disk Storage Drive, Drive 2, Disk 1 through 5 */ + xio_disk(iocc_addr, iocc_func, iocc_mod, -1); + break; + case 0x19: /* 2250 Display Unit */ + xio_2250_display(iocc_addr, iocc_func, iocc_mod); + break; + case 0x1a: /* 2741 Attachment (nonstandard serial interface used by APL\1130 */ + xio_t2741_terminal(iocc_addr, iocc_func, iocc_mod); + break; + default: + sprintf(msg, "unknown device %02x", iocc_dev); + xio_error(msg); + break; + } + } + + calc_ints(); /* after every XIO, reset int_mask just in case */ + break; + + case 0x02: /* --- SLA,SLT,SLC,SLCA,NOP - Shift Left family --- */ + if (F) { + weirdop("Long Left Shift", -2); + DECREMENT_IAR; + } + + CCC = ((TAG == 0) ? DSPLC : ReadIndex(TAG)) & 0x003F; + ARFSET(CCC); + if (CCC == 0) + break; /* shift of zero is a NOP */ + + switch (IR & 0x00C0) { + case 0x0040: /* SLCA */ + if (TAG) { + while (CCC > 0 && (ACC & 0x8000) == 0) { + ACC <<= 1; + CCC--; + } + C = (CCC != 0); + WriteIndex(TAG, (ReadIndex(TAG) & 0xFF00) | CCC); /* put low 6 bits back into index register and zero bits 8 and 9 */ + break; + } + /* if TAG == 0, fall through and treat like normal shift SLA */ + + case 0x0000: /* SLA */ + while (CCC > 0) { + C = (ACC & 0x8000); + ACC = (ACC << 1) & 0xFFFF; + CCC--; + } + break; + + case 0x00C0: /* SLC */ + if (TAG) { + while (CCC > 0 && (ACC & 0x8000) == 0) { + abit = (EXT & 0x8000) >> 15; + ACC = ((ACC << 1) & 0xFFFF) | abit; + EXT = (EXT << 1); + CCC--; + } + C = (CCC != 0); + WriteIndex(TAG, ReadIndex(TAG) & 0xFF00 | CCC); /* put 6 bits back into low byte of index register */ + break; + } + /* if TAG == 0, fall through and treat like normal shift SLT */ + + case 0x0080: /* SLT */ + while (CCC > 0) { + C = (ACC & 0x8000); + abit = (EXT & 0x8000) >> 15; + ACC = ((ACC << 1) & 0xFFFF) | abit; + EXT = (EXT << 1) & 0xFFFF; + CCC--; + } + break; + + default: + bail("SLA switch, can't happen"); + break; + } + break; + + case 0x03: /* --- SRA, SRT, RTE - Shift Right family --- */ + if (F) { + weirdop("Long Right Shift", -2); + DECREMENT_IAR; + } + + CCC = ((TAG == 0) ? DSPLC : ReadIndex(TAG)) & 0x3F; + ARFSET(CCC); + if (CCC == 0) + break; /* NOP */ + + switch (IR & 0x00C0) { + case 0x0000: /* SRA */ + ACC = (CCC < 16) ? ((ACC & 0xFFFF) >> CCC) : 0; + CCC = 0; + break; + + case 0x0040: /* invalid */ + wait_state = WAIT_INVALID_OP; + break; + + case 0x0080: /* SRT */ + while (CCC > 0) { + xbit = (ACC & 0x0001) << 15; + abit = (ACC & 0x8000); + ACC = (ACC >> 1) & 0x7FFF | abit; + EXT = (EXT >> 1) & 0x7FFF | xbit; + CCC--; + } + break; + + case 0x00C0: /* RTE */ + while (CCC > 0) { + abit = (EXT & 0x0001) << 15; + xbit = (ACC & 0x0001) << 15; + ACC = (ACC >> 1) & 0x7FFF | abit; + EXT = (EXT >> 1) & 0x7FFF | xbit; + CCC--; + } + break; + + default: + bail("SRA switch, can't happen"); + break; + } + break; + + case 0x04: /* --- LDS - Load Status --- */ + if (F) { /* never fetches second word? */ + weirdop("Long LDS", -2); + DECREMENT_IAR; + } + + V = (DSPLC & 1); + C = (DSPLC & 2) >> 1; + break; + + case 0x05: /* --- STS - Store Status --- */ + newval = ReadW(eaddr) & 0xFF00; + if (C) + newval |= 2; + if (V) + newval |= 1; + + WriteW(eaddr, newval); + C = V = 0; /* clear flags after storing */ + break; + + case 0x06: /* --- WAIT --- */ + wait_state = WAIT_OP; + if (F) { /* what happens if we use long format? */ + weirdop("Long WAIT", -2); + DECREMENT_IAR; /* assume it wouldn't have fetched 2nd word? */ + } + break; + + case 0x08: /* --- BSI - Branch and store IAR --- */ + if (F) { + if (bsctest(IR, F)) /* do standard BSC long format testing */ + break; /* if any condition is true, do nothing */ + } + WriteW(eaddr, IAR); /* do subroutine call */ + archive_backtrace("BSI"); /* save info in back-trace buffer */ + IAR = (eaddr + 1) & mem_mask; + break; + + case 0x09: /* --- BSC - Branch and skip on Condition --- */ + if (F) { + if (bsctest(IR, F)) /* long format; any indicator cancels branch */ + break; + + archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC"); /* save info in back-trace buffer */ + IAR = eaddr; /* no indicator means branch taken */ + } + else { /* short format: skip if any indicator hits */ + if (bsctest(IR, F)) { + archive_backtrace((DSPLC & 0x40) ? "BOSC" : "BSC"); /* save info in back-trace buffer */ + INCREMENT_IAR; + } + } +/* 27Mar02: moved this test out of the (F) condition; BOSC works even in the + * short form. The displacement field in this instruction is always the set of + * condition bits, and the interrupt clear bit doesn't collide. */ + + if (DSPLC & 0x40) { /* BOSC = exit from interrupt handler */ + exit_irq(); + cwincount = 0; + } + break; + + case 0x0c: /* --- LDX - Load Index --- */ + if (F) + eaddr = (INDIR) ? ReadW(word2) : word2; + else + eaddr = DSPLC; + + if (TAG) + WriteIndex(TAG, eaddr); + else { + archive_backtrace("LDX"); /* save info in back-trace buffer */ + IAR = eaddr; /* what happens in short form? can onlyjump to low addresses? */ + } + break; + + case 0x0d: /* --- STX - Store Index --- */ + if (F) { /* compute EA without any indexing */ + eaddr = (INDIR) ? ReadW(word2) : word2; + } + else { + eaddr = IAR + DSPLC; + } + WriteW(eaddr, TAG ? ReadIndex(TAG) : IAR); + break; + + case 0x0e: /* --- MDX - Modify Index and Skip --- */ + if (F) { /* long format: adjust memory location */ + if (TAG) { + oldval = ReadIndex(TAG); /* add word2 to index */ + newval = oldval + (INDIR ? ReadW(word2) : word2); + WriteIndex(TAG, newval); + } + else { + oldval = ReadW(word2); + DSPLC = IR & 0x00FF; /* use extended displacement (no INDIR bit, it's is part of displacement in this op) */ + if (DSPLC & 0x0080) + DSPLC |= ~ 0xFF; + newval = oldval + DSPLC; /* add modifier to @word2 */ + WriteW(word2, newval); + } + } + else { /* short format: adust IAR or index */ + if (TAG) { + oldval = ReadIndex(TAG); /* add displacement to index */ + newval = oldval + DSPLC; + WriteIndex(TAG, newval); + } + else { + oldval = IAR; /* add displacement to IAR */ + newval = IAR + DSPLC; + archive_backtrace("MDX"); + IAR = newval & mem_mask; + } + } + + if ((F || TAG) && (((newval & 0xFFFF) == 0) || ((oldval & 0x8000) != (newval & 0x8000)))) { + archive_backtrace("SKP"); + INCREMENT_IAR; /* skip if index sign change or zero */ + } + break; + + case 0x10: /* --- A - Add --- */ + /* in adds and subtracts, carry is set or cleared, overflow is set only */ + src = ReadW(eaddr); + ARFSET(src); + src2 = ACC; + ACC = (ACC + src) & 0xFFFF; + + C = ACC < src; + if (! V) + V = SIGN_BIT((~src ^ src2) & (src ^ ACC)); + break; + + case 0x11: /* --- AD - Add Double --- */ + src = ((ACC << 16) | (EXT & 0xFFFF)); + ARFSET(EXT); + src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1); + dst = src + src2; + ACC = (dst >> 16) & 0xFFFF; + EXT = dst & 0xFFFF; + + C = (uint32) dst < (uint32) src; + if (! V) + V = DWSIGN_BIT((~src ^ src2) & (src ^ dst)); + break; + + case 0x12: /* --- S - Subtract --- */ + src = ACC; + ARFSET(src); + src2 = ReadW(eaddr); + ACC = (ACC-src2) & 0xFFFF; + + C = src < src2; + if (! V) + V = SIGN_BIT((src ^ src2) & (src ^ ACC)); + break; + + case 0x13: /* --- SD - Subtract Double --- */ + src = ((ACC << 16) | (EXT & 0xFFFF)); + ARFSET(EXT); + src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1); + dst = src - src2; + ACC = (dst >> 16) & 0xFFFF; + EXT = dst & 0xFFFF; + + C = (uint32) src < (uint32) src2; + if (! V) + V = DWSIGN_BIT((src ^ src2) & (src ^ dst)); + break; + + case 0x14: /* --- M - Multiply --- */ + if ((src = ACC & 0xFFFF) & 0x8000) /* sign extend the values */ + src |= ~0xFFFF; + if ((src2 = ReadW(eaddr)) & 0x8000) + src2 |= ~0xFFFF; + + ARFSET(src2); + dst = src * src2; + ACC = (dst >> 16) & 0xFFFF; /* split the results */ + EXT = dst & 0xFFFF; + break; + + case 0x15: /* --- D - Divide --- */ + src = ((ACC << 16) | (EXT & 0xFFFF)); + if ((src2 = ReadW(eaddr)) & 0x8000) + src2 |= ~0xFFFF; /* oops: sign extend was missing, fixed 18Mar03 */ + + ARFSET(src2); + + if (src2 == 0) + V = 1; /* divide by zero just sets overflow, ACC & EXT are undefined */ + else { + ACC = (src / src2) & 0xFFFF; + EXT = (src % src2) & 0xFFFF; + } + break; + + case 0x18: /* --- LD - Load ACC --- */ + ACC = ReadW(eaddr); + break; + + case 0x19: /* --- LDD - Load Double --- */ + ACC = ReadW(eaddr); + EXT = ReadW(eaddr|1); /* notice address is |1 not +1 */ + break; + + case 0x1a: /* --- STO - Store ACC --- */ + WriteW(eaddr, ACC); + break; + + case 0x1b: /* --- STD - Store Double --- */ + WriteW(eaddr|1, EXT); + WriteW(eaddr, ACC); /* order is important: if odd addr, only ACC is stored */ + break; + + case 0x1c: /* --- AND - Logical AND --- */ + src = ReadW(eaddr); + ARFSET(src); + ACC &= src; + break; + + case 0x1d: /* --- OR - Logical OR --- */ + src = ReadW(eaddr); + ARFSET(src); + ACC |= src; + break; + + case 0x1e: /* --- EOR - Logical Excl OR --- */ + src = ReadW(eaddr); + ARFSET(src); + ACC ^= src; + break; + + case 0x16: + case 0x17: +#ifdef ENABLE_1800_SUPPORT + if (is_1800) { + if (OP == 0x16) { /* --- CMP - Compare --- */ + src = ACC; /* like subtract but result isn't stored */ + src2 = ReadW(eaddr); + dst = (ACC-src2) & 0xFFFF; + C = src < src2; + + if (dst & 0x8000) /* if ACC < operand, skip 1 instruction */ + IAR = IAR+1; + else if ((dst & 0xFFFF) == 0) /* if ACC == operand, skip 2 instructions */ + IAR = IAR+2; + } + else { /* --- DCMP - Compare Double --- */ + src = ((ACC << 16) | (EXT & 0xFFFF)); + src2 = (ReadW(eaddr) << 16) + ReadW(eaddr|1); + dst = src - src2; + C = (uint32) src < (uint32) src2; + + if (dst & 0x80000000) /* if ACC_EXT < operand, skip 1 instruction */ + IAR = IAR+1; + else if (dst == 0) /* if ACC_EXT == operand, skip 2 instructions */ + IAR = IAR+2; + } + + break; /* these are legal instructions on the 1800 */ + } +#endif + /* 1130: these are not legal instructions, fall through */ + + default: +/* all invalid instructions act like waits */ +/* case 0x00: */ +/* case 0x07: */ +/* case 0x0a: */ +/* case 0x0b: */ +/* case 0x0e: */ +/* case 0x0f: */ +/* case 0x1f: */ + wait_state = WAIT_INVALID_OP; + if (F) + DECREMENT_IAR; /* assume it wouldn't have fetched 2nd word? */ + + break; + } /* end instruction decode switch */ + + if (RUNMODE != MODE_RUN && RUNMODE != MODE_INT_RUN) + reason = STOP_WAIT; + + if (tbit && (ipl < 0)) { /* if INT_RUN mode, set IRQ5 after this instr */ + GUI_BEGIN_CRITICAL_SECTION + SETBIT(cpu_dsw, CPU_DSW_INT_RUN); + SETBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP); + int_req |= INT_REQ_5; + GUI_END_CRITICAL_SECTION + } + } /* end main loop */ + +#ifdef GUI_SUPPORT + gui_run(FALSE); +#endif + + running = FALSE; + int_lamps = 0; /* display only currently active interrupts while halted */ + + if (reason == STOP_WAIT || reason == STOP_INVALID_INSTR) { + wait_state = 0; /* on resume, don't wait */ + wait_lamp = TRUE; /* but keep the lamp lit on the GUI */ + + CLRBIT(cpu_dsw, CPU_DSW_PROGRAM_STOP); /* and on resume, reset program start bit */ + if ((cpu_dsw & CPU_DSW_PROGRAM_STOP) == 0) + CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP); + } + + if (cgi) /* give CGI hook function a chance to do something */ + cgi_stop(reason); + + return reason; +} + +/* + * simh_status_to_stopcode - convert a SCPE_xxx value from sim_process_event into a STOP_xxx code + */ + +static int simh_status_to_stopcode (int status) +{ + return (status == SCPE_BREAK) ? STOP_BREAK : + (status == SCPE_STOP) ? STOP_IMMEDIATE : + (status == SCPE_STEP) ? STOP_STEP : STOP_OTHER; +} + +/* ------------------------------------------------------------------------ + * bsctest - perform standard set of condition tests. We return TRUE if any + * of the condition bits specified in DSPLC test positive, FALSE if none are true. + * If reset_V is TRUE, we reset the oVerflow flag after testing it. + * ------------------------------------------------------------------------ */ + +static t_bool bsctest (int32 DSPLC, t_bool reset_V) +{ + if (DSPLC & 0x01) { /* Overflow off (note inverted sense) */ + if (! V) + return TRUE; + else if (reset_V) /* reset after testing */ + V = 0; + } + + if (DSPLC & 0x02) { /* Carry off (note inverted sense) */ + if (! C) + return TRUE; + } + + if (DSPLC & 0x04) /* Even */ + if ((ACC & 1) == 0) + return TRUE; + + if (DSPLC & 0x08) /* Positive */ + if ((ACC & 0x8000) == 0 && ACC != 0) + return TRUE; + + if (DSPLC & 0x10) /* Negative */ + if (ACC & 0x8000) + return TRUE; + + if (DSPLC & 0x20) /* Zero */ + if ((ACC & 0xFFFF) == 0) + return TRUE; + + return FALSE; +} + +/* ------------------------------------------------------------------------ + * exit_irq - pop interrupt stack as part of return from subroutine (BOSC) + * ------------------------------------------------------------------------ */ + +static void exit_irq (void) +{ + int i, bit; + + GUI_BEGIN_CRITICAL_SECTION + + if (ipl == 5 && tbit) { /* if we are exiting an INT_RUN interrupt, clear it for the next instruction */ + CLRBIT(cpu_dsw, CPU_DSW_INT_RUN); + if ((cpu_dsw & CPU_DSW_PROGRAM_STOP) == 0) + CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP); + } + + ipl = -1; /* default: return to main processor level */ + int_mask = 0xFFFF; + + if (iplpending) { /* restore previous interrupt status */ + for (i = 0, bit = 0x20; i < 6; i++, bit >>= 1) { + if (iplpending & bit) { + iplpending &= ~bit; + ipl = i; + int_mask = int_masks[i]; + break; + } + } + } + GUI_END_CRITICAL_SECTION + + calc_ints(); /* recompute pending interrupt mask */ +} /* because we probably cleared some ILSW bits before this instruction */ + +/* let a device halt the simulation */ + +void break_simulation (t_stat stopreason) +{ + reason = stopreason; +} + +/* ------------------------------------------------------------------------ + * SIMH required routines + * ------------------------------------------------------------------------ */ + +/* ------------------------------------------------------------------------ + * Reset routine + * ------------------------------------------------------------------------ */ + +t_stat cpu_reset (DEVICE *dptr) +{ + wait_state = 0; /* cancel wait */ + wait_lamp = TRUE; /* but keep the wait lamp lit on the GUI */ + + if (cpu_unit.flags & UNIT_ATT) { /* record reset in CPU log */ + fseek(cpu_unit.fileref, 0, SEEK_END); + fprintf(cpu_unit.fileref, "---RESET---" CRLF); + } + + GUI_BEGIN_CRITICAL_SECTION + + CLRBIT(cpu_dsw, CPU_DSW_PROGRAM_STOP|CPU_DSW_INT_RUN); + CLRBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP); + + reset_backtrace(); + + ipl = -1; + int_mask = 0xFFFF; + int_req = 0; /* hmmm, it SHOULD reset the int req, right? */ + int_lamps = 0; + iplpending = 0; + memset(ILSW, 0, sizeof(ILSW)); + + cpu_dsw = 0; /* clear int req and prot stop bits */ + tbit = 0; /* cancel INT_RUN mode */ + + C = V = 0; /* clear processor flags */ + IAR = SAR = SBR = 0; /* clear IAR and other registers */ + ACC = EXT = OP = TAG = CCC = C = V = 0; + + mem_mask = MEMSIZE - 1; /* wraparound mask */ + + GUI_END_CRITICAL_SECTION + + return cpu_svc(&cpu_unit); /* reset breakpoint */ +} + +/* ------------------------------------------------------------------------ + * Memory examine + * ------------------------------------------------------------------------ */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ + if (vptr == NULL) return SCPE_ARG; + + /* check this out -- save command hits it in weird way */ + /* I wish I remembered what I meant when I wrote that */ + if (addr < MEMSIZE) { + *vptr = M[addr] & 0xFFFF; + return SCPE_OK; + } + return SCPE_NXM; +} + +/* ------------------------------------------------------------------------ + * Memory deposit + * ------------------------------------------------------------------------ */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ + if (addr < MEMSIZE) { + M[addr] = (uint16) (val & 0xFFFF); + return SCPE_OK; + } + return SCPE_NXM; +} + +/* ------------------------------------------------------------------------ + * Breakpoint service + * ------------------------------------------------------------------------ */ + +t_stat cpu_svc (UNIT *uptr) +{ + if ((ibkpt_addr & ~ILL_ADR_FLAG) == save_ibkpt) + ibkpt_addr = save_ibkpt; + + save_ibkpt = -1; + return SCPE_OK; +} + +/* ------------------------------------------------------------------------ + * Memory allocation + * ------------------------------------------------------------------------ */ + +t_stat cpu_set_size (UNIT *uptr, int32 value, char *cptr, void *desc) +{ + t_bool used; + int32 i; + + if ((value <= 0) || (value > MAXMEMSIZE) || ((value & 0xFFF) != 0)) + return SCPE_ARG; + + for (i = value, used = FALSE; i < (int32) MEMSIZE; i++) { + if (M[i] != 0) { + used = TRUE; + break; + } + } + + if (used && ! get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; + + for (i = MEMSIZE; i < value; i++) /* clear expanded area */ + M[i] = 0; + + MEMSIZE = value; + mem_mask = MEMSIZE - 1; + + return SCPE_OK; +} + +/* processor type */ + +t_stat cpu_set_type (UNIT *uptr, int32 value, char *cptr, void *desc) +{ + REG *r; + + is_1800 = (value & UNIT_1800) != 0; /* set is_1800 mode flag */ + + for (r = cpu_reg; r->name != NULL; r++) { /* unhide or hide 1800-specific registers & state */ + if (strnicmp(r->name, "XR", 2) == 0) { + if (value & UNIT_1800) + CLRBIT(r->flags, REG_HIDDEN|REG_RO); + else + SETBIT(r->flags, REG_HIDDEN|REG_RO); + } + } + + return SCPE_OK; +} + +/* ------------------------------------------------------------------------ + * IO function for console switches + * ------------------------------------------------------------------------ */ + +void xio_1131_switches (int32 addr, int32 func, int32 modify) +{ + char msg[80]; + + switch (func) { + case XIO_READ: + WriteW(addr, CES); + break; + + case XIO_SENSE_DEV: + ACC = cpu_dsw; + break; + + default: + sprintf(msg, "Invalid console switch function %x", func); + xio_error(msg); + } +} + +/* ------------------------------------------------------------------------ + * Illegal IO operation. Not yet sure what the actual CPU does in this case + * ------------------------------------------------------------------------ */ + +void xio_error (char *msg) +{ + printf("*** XIO error at %04x: %s\n", prev_IAR, msg); + if (cgi) /* if this happens in CGI mode, probably best to halt */ + break_simulation(STOP_CRASH); +} + +/* ------------------------------------------------------------------------ + * register_cmd - add a command to the extensible command table + * ------------------------------------------------------------------------ */ + +t_stat register_cmd (char *name, t_stat (*action)(int32 flag, char *ptr), int arg, char *help) +{ + int i; + + for (i = 0; i < MAX_EXTRA_COMMANDS; i++) { /* find end of command table */ + if (x_cmds[i].action == action) + return SCPE_OK; /* command is already there, just return */ + if (x_cmds[i].name == NULL) + break; + } + + if (i >= (MAX_EXTRA_COMMANDS-1)) { /* no more room (we need room for the NULL) */ + fprintf(stderr, "The command table is full - rebuild the simulator with more free slots\n"); + return SCPE_ARG; + } + + x_cmds[i].action = action; /* add new command */ + x_cmds[i].name = name; + x_cmds[i].arg = arg; + x_cmds[i].help = help; + + i++; + x_cmds[i].action = NULL; /* move the NULL terminator */ + x_cmds[i].name = NULL; + + return SCPE_OK; +} + +#ifdef USE_MY_ECHO_CMD +/* ------------------------------------------------------------------------ + * echo_cmd - just echo the command line + * ------------------------------------------------------------------------ */ + +static t_stat echo_cmd (int flag, char *cptr) +{ + printf("%s\n", cptr); + return SCPE_OK; +} +#endif + +/* ------------------------------------------------------------------------ + * sim_init - initialize simulator upon startup of scp, before reset + * ------------------------------------------------------------------------ */ + +void sim_init (void) +{ + sim_gui = ! (sim_switches & SWMASK('G')); /* -g means no GUI */ + + sim_vm_cmd = x_cmds; /* provide list of additional commands */ + +#ifdef GUI_SUPPORT + /* set hook routines for GUI command processing */ + if (sim_gui) { + sim_vm_read = &read_cmdline; + sim_vm_post = &update_gui; + } +#endif + +#ifdef ENABLE_BACKTRACE + /* add the BACKTRACE command */ + register_cmd("BACKTRACE", &backtrace_cmd, 0, "ba{cktrace} {n} list last n branches/skips/interrupts\n"); +#endif + + register_cmd("VIEW", &view_cmd, 0, "v{iew} filename view a text file with notepad\n"); + +#ifdef USE_MY_ECHO_CMD + register_cmd("ECHO", &echo_cmd, 0, "echo args... echo arguments passed to command\n"); +#endif +} + +/* ------------------------------------------------------------------------ + * archive_backtrace - record a jump, skip, branch or whatever + * ------------------------------------------------------------------------ */ + +#ifdef ENABLE_BACKTRACE + +#define MAXARCHIVE 16 + +static struct tag_arch { + int iar; + char *inst; +} arch[MAXARCHIVE]; +int narchived = 0, archind = 0; + +static void archive_backtrace (char *inst) +{ + static int prevind; + + if (narchived < MAXARCHIVE) + narchived++; + + if (narchived > 0 && arch[prevind].iar == prev_IAR) + return; + + arch[archind].iar = prev_IAR; + arch[archind].inst = inst; + + prevind = archind; + archind = (archind+1) % MAXARCHIVE; +} + +static void reset_backtrace (void) +{ + narchived = 0; + archind = 0; +} + +void void_backtrace (int afrom, int ato) +{ + int i; + + afrom &= mem_mask; + ato &= mem_mask; + + for (i = 0; i < narchived; i++) + if (arch[i].iar >= afrom && arch[i].iar <= ato) + arch[i].inst = "OVERWRITTEN"; +} + +static void show_backtrace (int nshow) +{ + int n = narchived, i = archind; + + if (n > nshow) n = nshow; + + while (--n >= 0) { + i = (i > 0) ? (i-1) : (MAXARCHIVE-1); + printf("from %04x (%s) ", arch[i].iar, arch[i].inst); + } + + if (narchived) + putchar('\n'); +} + +static t_stat backtrace_cmd (int flag, char *cptr) +{ + int n; + + if ((n = atoi(cptr)) <= 0) + n = 6; + + show_backtrace(n); + return SCPE_OK; +} +#else + +/* stub this for the disk routine */ + +void void_backtrace (int afrom, int ato) +{ +} + +#endif + +/************************************************************************************* + * CPU log routines -- attaching a file to the CPU creates a trace of instructions and register values + * + * Syntax is WEIRD: + * + * attach cpu logfile log instructions and registers to file "logfile" + * attach -f cpu cpu.log log instructions, registers and floating point acc + * attach -m cpu mapfile logfile read addresses from "mapfile", log instructions to "logfile" + * attach -f -m cpu mapfile logfile same and log floating point stuff too + * + * mapfile if specified is a list of symbols and addresses of the form: + * symbol hexval + * + * e.g. + * FSIN 082E + * FARC 09D4 + * FMPY 09A4 + * NORM 0976 + * XMDS 095A + * START 021A + * + * These values are easily obtained from a load map created by + * XEQ L + * + * The log output is of the form + * + * IAR ACC EXT (flt) XR1 XR2 XR3 CVI FAC OPERATION + * --------------- ---- ---- -------- ---- ---- ---- --- ------------- ----------------------- + * 002a 002a 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 4c80 BSC I ,0028 + * 081d PAUSE+000d 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 7400 MDM L 00f0,0 (0) + * 0820 PAUSE+0010 1234 5381 0.14222 00b3 0236 3f7e CV 1.04720e+000 7201 MDX 2 0001 + * 0821 PAUSE+0011 1234 5381 0.14222 00b3 0237 3f7e CV 1.04720e+000 6a03 STX 2 0003 + * 0822 PAUSE+0012 1234 5381 0.14222 00b3 0237 3f7e CV 1.04720e+000 6600 LDX L2 0231 + * 0824 PAUSE+0014 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4c00 BSC L ,0237 + * 0237 START+001d 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4480 BSI I ,3fff + * 082f FSIN +0001 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4356 BSI 3 0056 + * 3fd5 ILS01+35dd 1234 5381 0.14222 00b3 0231 3f7e CV 1.04720e+000 4c00 BSC L ,08de + * + * IAR - instruction address register value, optionally including symbol and offset + * ACC - accumulator + * EXT - extension + * flt - ACC+EXT interpreted as the mantissa of a floating pt number (value 0.5 -> 1) + * XR* - index registers + * CVI - carry, overflow and interrupt indicators + * FAC - floating point accumulator (exponent at 125+XR3, mantissa at 126+XR3 and 127+XR3) + * OP - opcode value and crude disassembly + * + * flt and FAC are displayed only when the -f flag is specified in the attach command + * The label and offset and displayed only when the -m flag is specified in the attach command + * + * The register values shown are the values BEFORE the instruction is executed. + *************************************************************************************/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw); + +typedef struct tag_symentry { + struct tag_symentry *next; + int addr; + char sym[6]; +} SYMENTRY, *PSYMENTRY; + +static PSYMENTRY syms = NULL; +static t_bool new_log, log_fac; + +static t_stat cpu_attach (UNIT *uptr, char *cptr) +{ + char mapfile[200], buf[200], sym[100]; + int addr; + PSYMENTRY n, prv, s; + FILE *fd; + + remove(cptr); /* delete old log file, if present */ + new_log = TRUE; + log_fac = sim_switches & SWMASK ('F'); /* display the FAC and the ACC/EXT as fixed point. */ + + for (s = syms; s != NULL; s = n) { /* free any old map entries */ + n = s->next; + free(s); + } + syms = NULL; + + if (sim_switches & SWMASK('M')) { /* use a map file to display relative addresses */ + cptr = get_glyph(cptr, mapfile, 0); + if (! *mapfile) { + printf("/m must be followed by a filename\n"); + return SCPE_ARG; + } + if ((fd = fopen(mapfile, "r")) == NULL) { + perror(mapfile); + return SCPE_OPENERR; + } + + while (fgets(buf, sizeof(buf), fd) != NULL) { /* read symbols & addresses, link in descending address order */ + if (sscanf(buf, "%s %x", sym, &addr) != 2) + continue; + if (*buf == ';') + continue; + + for (prv = NULL, s = syms; s != NULL; prv = s, s = s->next) { + if (s->addr < addr) + break; + } + + if ((n = malloc(sizeof(SYMENTRY))) == NULL) { + printf("out of memory reading map!\n"); + break; + } + + sym[5] = '\0'; + strcpy(n->sym, sym); + upcase(n->sym); + n->addr = addr; + + if (prv == NULL) { + n->next = syms; + syms = n; + } + else { + n->next = prv->next; + prv ->next = n; + } + } + fclose(fd); + } + + return attach_unit(uptr, quotefix(cptr)); /* fix quotes in filenames & attach */ +} + +static void trace_instruction (void) +{ + t_value v[2]; + float fac; + short exp; + int addr; + PSYMENTRY s; + long mant, sign; + char facstr[20], fltstr[20]; + + if ((cpu_unit.flags & UNIT_ATT) == 0) + return; + + if (new_log) { + fseek(cpu_unit.fileref, 0, SEEK_END); + new_log = FALSE; + + fprintf(cpu_unit.fileref, " IAR%s ACC EXT %s XR1 XR2 XR3 CVI %sOPERATION" CRLF, + syms ? " " : "", log_fac ? " (flt) " : "", log_fac ? " FAC " : ""); + fprintf(cpu_unit.fileref, "----%s ---- ---- %s---- ---- ---- --- %s-----------------------" CRLF, + syms ? "-----------" : "", log_fac ? "-------- " : "", log_fac ? "------------- " : ""); + } + + if (! log_fac) + facstr[0] = fltstr[0] = '\0'; + else { + mant = ((ACC & 0xFFFF) << 16) | (EXT & 0xFFFF); + if (mant == 0x80000000) { + sign = TRUE; + fac = 1.f; + } + else { + if ((sign = mant & 0x80000000) != 0) + mant = -mant; + fac = (float) mant * ((float) 1./ (float) (unsigned long) 0x80000000); + } + sprintf(fltstr, "%c%.5f ", sign ? '-' : ' ', fac); + + if (BETWEEN(M[3], 0x300, MEMSIZE-128)) { + exp = (short) ((M[M[3]+125] & 0xFF) - 128); + mant = (M[M[3]+126] << 8) | ((M[M[3]+127] >> 8) & 0xFF); + if ((sign = (mant & 0x00800000)) != 0) + mant = (-mant) & 0x00FFFFFF; + + fac = (float) mant * ((float) 1. / (float) 0x00800000); + + if (exp > 30) { + fac *= (float) (1 << 30); + exp -= 30; + while (exp > 0) + fac *= 2; + } + else if (exp > 0) + fac *= (float) (1 << exp); + else if (exp < -30) { + fac /= (float) (1 << 30); + exp += 30; + while (exp < 0) + fac /= 2; + } + else if (exp < 0) + fac /= (float) (1 << -exp); + + sprintf(facstr, "%c%.5e ", sign ? '-' : ' ', fac); + } + else + strcpy(facstr, " "); + } + + addr = IAR & 0xFFFF; + fprintf(cpu_unit.fileref, "%04x ", addr); + + if (syms) { + for (s = syms; s != NULL; s = s->next) + if (s->addr <= addr) + break; + + if (s == NULL) + fprintf(cpu_unit.fileref, " %04x ", addr); + else + fprintf(cpu_unit.fileref, "%-5s+%04x ", s->sym, addr - s->addr); + } + + fprintf(cpu_unit.fileref, "%04x %04x %s%04x %04x %04x %c%c%c %s", + ACC & 0xFFFF, EXT & 0xFFFF, fltstr, M[1] & 0xFFFF, M[2] & 0xFFFF, M[3] & 0xFFFF, + C ? 'C' : ' ', V ? 'V' : ' ', (ipl < 0) ? ' ' : (ipl+'0'), facstr); + + v[0] = M[ IAR & mem_mask]; + v[1] = M[(IAR+1) & mem_mask]; + fprint_sym(cpu_unit.fileref, IAR & mem_mask, v, NULL, SWMASK('M')); /* disassemble instruction */ + + fputs(CRLF, cpu_unit.fileref); +} + +void trace_io (char *fmt, ...) +{ + va_list args; + + if ((cpu_unit.flags & UNIT_ATT) == 0) + return; + + va_start(args, fmt); /* get pointer to argument list */ + vfprintf(cpu_unit.fileref, fmt, args); /* write errors to cpu log file */ + va_end(args); + + fputs(CRLF, cpu_unit.fileref); +} + +void trace_both (char *fmt, ...) +{ + va_list args; + + if (cpu_unit.flags & UNIT_ATT) { + va_start(args, fmt); /* get pointer to argument list */ + vfprintf(cpu_unit.fileref, fmt, args); + va_end(args); + fputs(CRLF, cpu_unit.fileref); + } + + va_start(args, fmt); /* get pointer to argument list */ + vfprintf(stdout, fmt, args); + va_end(args); + putchar('\n'); +} + +/* debugging */ + +void debug_print (char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vprintf(fmt, args); + if (cpu_unit.flags & UNIT_ATT) + vfprintf(cpu_unit.fileref, fmt, args); + va_end(args); + + if (strchr(fmt, '\n') == NULL) { /* be sure to emit a newline */ + putchar('\n'); + if (cpu_unit.flags & UNIT_ATT) + putc('\n', cpu_unit.fileref); + } +} + +#ifdef _WIN32 +#include +#endif + +/* view_cmd - let user view and/or edit a file (e.g. a printer output file, script, or source deck) */ + +static t_stat view_cmd (int flag, char *cptr) +{ +#ifdef _WIN32 + char cmdline[256]; + + sprintf(cmdline, "notepad %s", cptr); + WinExec(cmdline, SW_SHOWNORMAL); +#endif + return SCPE_OK; +} + +/* web server version - hooks for CGI mode. These function pointer can be set by the CGI version's main() routine */ + +void (*cgi_start_hook)(void) = NULL; /* these can be defined by a CGI wrapper to do things on start and stop of simulation */ +void (*cgi_end_hook)(void) = NULL; + +static void cgi_start (void) +{ + if (cgi_start_hook != NULL) + (*cgi_start_hook)(); +} + +static void cgi_stop (t_stat reason) +{ + if (cgi_end_hook != NULL) + (*cgi_end_hook)(); +} diff --git a/Ibm1130/ibm1130_cr.c b/Ibm1130/ibm1130_cr.c new file mode 100644 index 0000000..61014b7 --- /dev/null +++ b/Ibm1130/ibm1130_cr.c @@ -0,0 +1,2636 @@ +#include "ibm1130_defs.h" +#include "ibm1130_fmt.h" + +#ifdef _WIN32 +# include /* Microsoft puts definition of mktemp into io.h rather than stdlib.h */ +#endif + +/* ibm1130_cr.c: IBM 1130 1442 Card Reader simulator + + Based on the SIMH package written by Robert M Supnik + + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + + * Update 2006-01-23 More fixes, in call to mktemp and in 2501 support, also thanks + to Carl Claunch. + + * Update 2006-01-03 Fixed bug found by Carl Claunch: feed function does not + cause an operation complete interrupt. Standard DMS routines were not + sensitive to this but DUP uses its own interrupt handler, and this + is why DUP would hang at end of deck. + + * Update 2005-05-19 Added support for 2501 reader + + * Update 2004-11-08 Merged in correct physical card reader code + + * Update 2004-11-02: Added -s to boot command: don't touch console switches. + + * Update 2004-06-05: Removed "feedcycle" from cr_reset. Reset should not touch the card reader. + + * Update 2004-04-12: Changed ascii field of CPCODE to unsigned char, caught a couple + other potential problems with signed characters used as subscript indexes. + + * Update 2003-11-25: Physical card reader support working, may not be perfect. + Changed magic filename for stdin to "(stdin)". + + * Update 2003-07-23: Added autodetect for card decks (029 vs binary), + made this the default. + + * Update 2003-06-21: Fixed bug in XIO_SENSE: op_complete and response + bits were being cleared before the DSW was saved in ACC. Somehow DMS + worked with this, but APL didn't. + + * Update 2002-02-29: Added deck-list option. + + * Update 2003-02-08: Fixed error in declaration of array list_save, pointed + out by Ray Comas. + +* ----------------------------------------------------------------------- +* USAGE NOTES +* ----------------------------------------------------------------------- + +* Attach switches: + + The ATTACH CR command accepts several command-line switches + + -q quiet mode, the simulator will not print the name of each file it opens + while processing deck files (which are discussed below). For example, + + ATTACH -q @deckfile + + -l makes the simulator convert lower case letters in text decks + to the IBM lower-case Hollerith character codes. Normally, the simulator + converts lower case input to the uppercase Hollerith character codes. + (Lowercase codes are used in APL\1130 save decks). + + -d prints a lot of simulator debugging information + + -f converts tabs in an ascii file to spaces according to Fortran column conventions + -a converts tabs in an ascii file to spaces according to 1130 Assembler column conventions + -t converts tabs in an ascii file to spaces, with tab settings every 8 columns + (See below for a discussion of tab formatting) + + -p means that filename is a COM port connected to a physical card reader using + the CARDREAD interface (see http://ibm1130.org/sim/downloads) + + The ATTACH CP command accepts the -d switch. + +* Deck lists + If you issue an attach command and specify the filename as + "@filename", the file is interpreted as a list of filenames to + be read in sequence; the effect is that the reader sees the concatenation + of all of the files listed. The simulator "reset" does NOT rewind the deck list. + + Filenames may be quoted if they contain spaces. + + The strings %1, %2, etc, if they appear, are replaced with arguments passed + on the attach command line after the name of the deckfile. These can be the + arguments to ibm1130, or to the "do" command if a "do" script is executing, if the + attach command is constructed this way: + + attach @deckfile %1 %2 %3 + + This will pass the ibm1130 or do script arguments to attach, which will make + them available in the deckfile. Then, for instance the line + + %1.for + + would be substituted accordingly. + + Blank lines and lines starting with ; # or * are ignored as comments. + + Filenames may be followed by whitespace and one or more mode options: + The mode options are: + + b forces interpration as raw binary + a forces conversion from ascii to 029 coding, tabs are left alone + af forces 029 ascii conversion, and interprets tabs in Fortran mode + aa forces 029 ascii conversion, and interprets tabs in 1130 Assembler mode + at forces 029 ascii conversion, and interprets tabs with settings every 8 spaces + + If "a" or "b" mode is not specified, the device mode setting is used. In this case, + if the mode is "auto", the simulator will select binary or 029 by inspecting each + file in turn. + + If a tab mode is not specified, tabs are left unmolested (and are treated as invalid characters) + + Example: + + attach cr @decklist + + reads filenames from file "decklist," which might contain: + + file01.for xf + file02.dat a + file03 bin b + file04 bin + + ('a' means 029, so, if you need 026 coding, specify the + device default as the correct 026 code and omit the 'a' on the text files lines). + + Literal text cards can be entered in deck files by preceding an input + line with an exclamation point. For example, + + !// JOB + !// FOR + program.for + !// XEQ + program.dat + + looks like two literal supervisor control cards, followed by the contents + of file program.for, followed by an // XEQ card, followed by the contents + of file program.dat. + + %n tokens are not replaced in literal cards. + + The line + + !BREAK + + has a special meaning: when read from a deck file, it stops the + emulator as if "IMMEDIATE STOP" was pressed. This returns control to + the command interpreter or to the current DO command script. + +* Card image format. + Card files can be ascii text or binary. There are several ASCII modes: + CODE_029, CODE_26F, etc, corresponding to different code sets. + Punch and reader modes can be set independently using + + set cr binary set cp binary * + set cr 029 set cp 029 + set cr 026f set cp 026f + set cr 026c set cp 026c + set cr auto * + + (* = default mode) + + In "auto" mode, the card reader will examine the first 160 bytes of + the deck and guess whether the card is binary or 029 text encoded. + When a deck file is used with auto mode, the simulator guesses for + each file named in the deck file. + +* Tab formatting. The attach command and deckfile entries can indicate + that tabs are to be converted to spaces, to help let you write free-form + source files. There are three tab conversion modes, which are set + with the attach command or in a decklist, as discussed earlier + + Fortran mode: + Input lines of the form + + [label]statement + + or + + [label]+continuation + + (where + is any nonalphabetic character) are rearranged in the + appropriate manner: + + 1 2 + 12345678901234567890... + ------------------------ + label statement + label+continuation + + However, you must take care that you don't end up with statement text after column 72. + + Input lines with * or C in column 1 (comments and directives) and lines without tabs + are left alone. + + (The ! escape is not used before Fortran directives as before Assembler directives) + + Assembler mode: + Input lines of the form + + [label][opcode][tag][L][argument] + + are rearranged so that the input fields are placed in the appropriate columns + + The label must start on the first character of the line. If there is no label, + the first character(s) before the opcode must be whitespace. Following the opcode, there + MUST be a tab character, followed by the format and tag. Following the format and tag + may be exactly one whitespace character, and then starts the argument. + + Input lines with * in column 1 and blank lines are turned into Assembler comments, + with the * in the Opcode field. + + Assembler directive lines at the beginning of the deck must be preceded by + ! to indicate that they are not comments. For example, + + !*LIST + * This is a comment + + Plain Tab mode: + Tabs are replaced with spaces. Tab settings are assumed to be eight characters wide, + as is standard for vi, notepad, etc. + +* CGI mode note: The command + + attach cr (stdin) + + will attach the card reader to stdin. However, this is not compatible + with the default encoding autodetect feature, so the command must be + preceded with + + set cr 029 + +* ----------------------------------------------------------------------- +* PROGRAMMING NOTES +* ----------------------------------------------------------------------- + +NOTE - there is a problem with this code. The Device Status Word (DSW) is +computed from current conditions when requested by an XIO load status +command; the value of DSW available to the simulator's examine & save +commands may NOT be accurate. This should probably be fixed. (I think there's +a way to have the expression evaluator call a routine? That would be one +way to solve the problem, the other is to keep DSW up-to-date all the time). + + The 1442 card read/punch has several cycles: + + feed cycle: moves card from hopper to read station + card from read station to punch station + card from punch station to stacker + + read or punch: operates on card at read or punch station (but not both). + + The simulator requires input cards to be read from the file attached + to the card reader unit. A feed cycle reads one line (text mode) or + 160 bytes (binary mode) from the input file to the read station buffer, + copies the read station buffer to the punch station buffer, and if + the punch unit is attached to a file, writes the punch station buffer to + the output file. + + The read and punch cycles operate on the appropriate card buffer. + + Detaching the card punch flushes the punch station buffer if necessary. + + As does the 1442, a read or punch cycle w/o a feed cycle causes a + feed cycle first. + + A feed cycle on an empty deck (reader unattaced or at EOF) clears + the appropriate buffer, so you can punch w/o attaching a deck to + the card reader. + + (Note: Carl Claunch determined by examining DUP code that a feed cycle + does not cause an operation complete interrupt). + +-- -- this may need changing depending on how things work in hardware. TBD. +|| A read cycle on an empty deck causes an error. +|| Hmmm -- what takes the place of the Start button on +-- the card reader? + + Binary format is stored using fxwrite of short ints, in this format: + + 1 1 + 2 2 0 1 2 3 4 5 6 7 8 9 + * * * * * * * * * * * * 0 0 0 0 + + MSB LSB + byte 0 [ 6] [ 7] [ 8] [ 9] 0 0 0 0 + byte 1 [12] [11] [ 0] [ 1] [ 2] [ 3] [ 4] [ 5] + + This means we can read words (little endian) and get this in memory: + + 12 11 0 1 2 3 4 5 6 7 8 9 - - - - + + which is what the 1130 sees. + + ASCII can be read in blocks of 80 characters but can be terminated by newline prematurely. + + Booting: card reader IPL loads 80 columns (1 card) into memory starting + at location 0 in a split fashion: + + ________________ _ _ _ + / + 12 | + 11 | + 0 | + 1 | + 2 | + 3 | Punched card + 4 | + 5 | + 6 | + 7 | + 8 | + 9 | + +------------------ - - - + + 12 11 0 1 2 3 4 5 6 7 8 9 <- columns of cold start card + | | | | | 0 0 0 / \ | | | | | | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15| + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | OPCODE | F| Tag | DISPLACEMENT | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The zeros mean that all IPL instructions are short form, + nonindexed. The 3 column is repeated in bits 8 and 9 so + it's a sign bit. + + Boot command on a binary deck does this. Boot on an unattached + reader loads one of the built-in boot card images. Boot with an ASCII + deck isn't allowed. +*/ + +#define READ_DELAY 35 /* see how small a number we can get away with */ +#define PUNCH_DELAY 35 +#define FEED_DELAY 25 +#define READ_2501_DELAY 500 + +/* umm, this is a weird little future project of mine. */ + +#define ENABLE_PHYSICAL_CARD_READER_SUPPORT + +extern int32 sim_switches; +extern UNIT cpu_unit; + +static t_stat cr_svc (UNIT *uptr); +static t_stat cr_reset (DEVICE *dptr); +static t_stat cr_set_code (UNIT *uptr, int32 match, char *cptr, void *desc); +static t_stat cr_attach (UNIT *uptr, char *cptr); +static int32 guess_cr_code (void); +static void feedcycle (t_bool load, t_bool punching); + +static t_stat cp_reset (DEVICE *dptr); +static t_stat cp_set_code (UNIT *uptr, int32 match, char *cptr, void *desc); +static t_stat cp_attach (UNIT *uptr, char *cptr); +static t_stat cp_detach (UNIT *uptr); + +static int16 cr_dsw = 0; /* device status word */ +static int32 cr_wait = READ_DELAY; /* read per-column wait */ +static int32 cr_wait2501 = READ_2501_DELAY; /* read card wait for 2501 reader */ +static int32 cf_wait = PUNCH_DELAY; /* punch per-column wait */ +static int32 cp_wait = FEED_DELAY; /* feed op wait */ +static int32 cr_count= 0; /* read and punch card count */ +static int32 cp_count= 0; +static int32 cr_addr = 0; /* 2501 reader transfer address */ +static int32 cr_cols = 0; /* 2501 reader column count */ + +#define UNIT_V_OPERATION (UNIT_V_UF + 0) /* operation in progress */ +#define UNIT_V_CODE (UNIT_V_UF + 2) /* three bits */ +#define UNIT_V_CR_EMPTY (UNIT_V_UF + 5) /* NOTE: THIS MUST BE SET IN ibm1130_gui.c too */ +#define UNIT_V_SCRATCH (UNIT_V_UF + 6) +#define UNIT_V_QUIET (UNIT_V_UF + 7) +#define UNIT_V_DEBUG (UNIT_V_UF + 8) +#define UNIT_V_PHYSICAL (UNIT_V_UF + 9) /* NOTE: THIS MUST BE SET IN ibm1130_gui.c too */ +#define UNIT_V_LASTPUNCH (UNIT_V_UF + 10) /* used in unit_cp only */ +#define UNIT_V_LOWERCASE (UNIT_V_UF + 10) /* used in unit_cr only */ +#define UNIT_V_ACTCODE (UNIT_V_UF + 11) /* used in unit_cr only, 3 bits */ +#define UNIT_V_2501 (UNIT_V_UF + 14) + +#define UNIT_OP (3u << UNIT_V_OPERATION) /* two bits */ +#define UNIT_CODE (7u << UNIT_V_CODE) /* three bits */ +#define UNIT_CR_EMPTY (1u << UNIT_V_CR_EMPTY) +#define UNIT_SCRATCH (1u << UNIT_V_SCRATCH) /* temp file */ +#define UNIT_QUIET (1u << UNIT_V_QUIET) +#define UNIT_DEBUG (1u << UNIT_V_DEBUG) +#define UNIT_PHYSICAL (1u << UNIT_V_PHYSICAL) +#define UNIT_LASTPUNCH (1u << UNIT_V_LASTPUNCH) +#define UNIT_LOWERCASE (1u << UNIT_V_LOWERCASE) /* permit lowercase input (needed for APL) */ +#define UNIT_ACTCODE (7u << UNIT_V_ACTCODE) +#define UNIT_2501 (1u << UNIT_V_2501) + +#define OP_IDLE (0u << UNIT_V_OPERATION) +#define OP_READING (1u << UNIT_V_OPERATION) +#define OP_PUNCHING (2u << UNIT_V_OPERATION) +#define OP_FEEDING (3u << UNIT_V_OPERATION) + +#define SET_OP(op) {cr_unit.flags &= ~UNIT_OP; cr_unit.flags |= (op);} +#define CURRENT_OP (cr_unit.flags & UNIT_OP) + +#define CODE_AUTO (0u << UNIT_V_CODE) +#define CODE_029 (1u << UNIT_V_CODE) +#define CODE_026F (2u << UNIT_V_CODE) +#define CODE_026C (3u << UNIT_V_CODE) +#define CODE_BINARY (4u << UNIT_V_CODE) + +#define GET_CODE(un) (un.flags & UNIT_CODE) +#define SET_CODE(un,cd) {un.flags &= ~UNIT_CODE; un.flags |= (cd);} + +#define ACTCODE_029 (CODE_029 << (UNIT_V_ACTCODE-UNIT_V_CODE)) /* these are used ONLY in MTAB. Elsewhere */ +#define ACTCODE_026F (CODE_026F << (UNIT_V_ACTCODE-UNIT_V_CODE)) /* we use values CODE_xxx with macros */ +#define ACTCODE_026C (CODE_026C << (UNIT_V_ACTCODE-UNIT_V_CODE)) /* GET_ACTCODE and SET_ACTCODE. */ +#define ACTCODE_BINARY (CODE_BINARY << (UNIT_V_ACTCODE-UNIT_V_CODE)) + + /* get/set macros for actual-code field, these use values like CODE_029 meant for the UNIT_CODE field */ +#define GET_ACTCODE(un) ((un.flags & UNIT_ACTCODE) >> (UNIT_V_ACTCODE-UNIT_V_CODE)) +#define SET_ACTCODE(un,cd) {un.flags &= ~UNIT_ACTCODE; un.flags |= (cd) << (UNIT_V_ACTCODE-UNIT_V_CODE);} + +#define COLUMN u4 /* column field in unit record */ + +UNIT cr_unit = { UDATA (&cr_svc, UNIT_ATTABLE|UNIT_ROABLE|UNIT_CR_EMPTY, 0) }; +UNIT cp_unit = { UDATA (NULL, UNIT_ATTABLE, 0) }; + +MTAB cr_mod[] = { + { UNIT_CODE, CODE_029, "029", "029", &cr_set_code}, + { UNIT_CODE, CODE_026F, "026F", "026F", &cr_set_code}, + { UNIT_CODE, CODE_026C, "026C", "026C", &cr_set_code}, + { UNIT_CODE, CODE_BINARY, "BINARY", "BINARY", &cr_set_code}, + { UNIT_CODE, CODE_AUTO, "AUTO", "AUTO", &cr_set_code}, + { UNIT_ACTCODE, ACTCODE_029, "(029)", NULL, NULL}, /* display-only, shows current mode */ + { UNIT_ACTCODE, ACTCODE_026F, "(026F)", NULL, NULL}, + { UNIT_ACTCODE, ACTCODE_026C, "(026C)", NULL, NULL}, + { UNIT_ACTCODE, ACTCODE_BINARY, "(BINARY)", NULL, NULL}, + { UNIT_2501, 0, "1442", "1442", NULL}, + { UNIT_2501, UNIT_2501, "2501", "2501", NULL}, + { 0 } }; + +MTAB cp_mod[] = { + { UNIT_CODE, CODE_029, "029", "029", &cp_set_code}, + { UNIT_CODE, CODE_026F, "026F", "026F", &cp_set_code}, + { UNIT_CODE, CODE_026C, "026C", "026C", &cp_set_code}, + { UNIT_CODE, CODE_BINARY, "BINARY", "BINARY", &cp_set_code}, + { 0 } }; + +REG cr_reg[] = { + { HRDATA (CRDSW, cr_dsw, 16) }, /* device status word */ + { DRDATA (CRTIME, cr_wait, 24), PV_LEFT }, /* operation wait for 1442 column read*/ + { DRDATA (2501TIME, cr_wait2501, 24), PV_LEFT }, /* operation wait for 2501 whole card read*/ + { DRDATA (CFTIME, cf_wait, 24), PV_LEFT }, /* operation wait */ + { DRDATA (CRCOUNT, cr_count, 32),PV_LEFT }, /* number of cards read since last attach cmd */ + { HRDATA (CRADDR, cr_addr, 32) }, /* 2501 reader transfer address */ + { HRDATA (CRCOLS, cr_cols, 32) }, /* 2501 reader column count */ + { NULL } }; + +REG cp_reg[] = { + { DRDATA (CPTIME, cp_wait, 24), PV_LEFT }, /* operation wait */ + { DRDATA (CPCOUNT, cp_count, 32),PV_LEFT }, /* number of cards punched since last attach cmd */ + { NULL } }; + +DEVICE cr_dev = { + "CR", &cr_unit, cr_reg, cr_mod, + 1, 16, 16, 1, 16, 16, + NULL, NULL, cr_reset, + cr_boot, cr_attach, cr_detach}; + +DEVICE cp_dev = { + "CP", &cp_unit, cp_reg, cp_mod, + 1, 16, 16, 1, 16, 16, + NULL, NULL, cp_reset, + NULL, cp_attach, cp_detach}; + +#define CR_DSW_1442_READ_RESPONSE 0x8000 /* device status word bits */ +#define CR_DSW_1442_PUNCH_RESPONSE 0x4000 +#define CR_DSW_1442_ERROR_CHECK 0x2000 +#define CR_DSW_1442_LAST_CARD 0x1000 +#define CR_DSW_1442_OP_COMPLETE 0x0800 +#define CR_DSW_1442_FEED_CHECK 0x0100 +#define CR_DSW_1442_BUSY 0x0002 +#define CR_DSW_1442_NOT_READY 0x0001 + +#define CR_DSW_2501_ERROR_CHECK 0x2000 /* DSW for 2501 reader */ +#define CR_DSW_2501_LAST_CARD 0x1000 +#define CR_DSW_2501_OP_COMPLETE 0x0800 +#define CR_DSW_2501_BUSY 0x0002 +#define CR_DSW_2501_NOT_READY 0x0001 + +typedef struct { + uint16 hollerith; + unsigned char ascii; +} CPCODE; + +static CPCODE cardcode_029[] = +{ + 0x0000, ' ', + 0x8000, '&', /* + in 026 Fortran */ + 0x4000, '-', + 0x2000, '0', + 0x1000, '1', + 0x0800, '2', + 0x0400, '3', + 0x0200, '4', + 0x0100, '5', + 0x0080, '6', + 0x0040, '7', + 0x0020, '8', + 0x0010, '9', + 0x9000, 'A', + 0x8800, 'B', + 0x8400, 'C', + 0x8200, 'D', + 0x8100, 'E', + 0x8080, 'F', + 0x8040, 'G', + 0x8020, 'H', + 0x8010, 'I', + 0x5000, 'J', + 0x4800, 'K', + 0x4400, 'L', + 0x4200, 'M', + 0x4100, 'N', + 0x4080, 'O', + 0x4040, 'P', + 0x4020, 'Q', + 0x4010, 'R', + 0x3000, '/', + 0x2800, 'S', + 0x2400, 'T', + 0x2200, 'U', + 0x2100, 'V', + 0x2080, 'W', + 0x2040, 'X', + 0x2020, 'Y', + 0x2010, 'Z', + 0x0820, ':', + 0x0420, '#', /* = in 026 Fortran */ + 0x0220, '@', /* ' in 026 Fortran */ + 0x0120, '\'', + 0x00A0, '=', + 0x0060, '"', + 0x8820, (unsigned char) '\xA2', /* cent, in MS-DOS encoding (this is in guess_cr_code as well) */ + 0x8420, '.', + 0x8220, '<', /* ) in 026 Fortran */ + 0x8120, '(', + 0x80A0, '+', + 0x8060, '|', + 0x4820, '!', + 0x4420, '$', + 0x4220, '*', + 0x4120, ')', + 0x40A0, ';', + 0x4060, (unsigned char) '\xAC', /* not, in MS-DOS encoding (this is in guess_cr_code as well) */ + 0x2420, ',', + 0x2220, '%', /* ( in 026 Fortran */ + 0x2120, '_', + 0x20A0, '>', + 0xB000, 'a', + 0xA800, 'b', + 0xA400, 'c', + 0xA200, 'd', + 0xA100, 'e', + 0xA080, 'f', + 0xA040, 'g', + 0xA020, 'h', + 0xA010, 'i', + 0xD000, 'j', + 0xC800, 'k', + 0xC400, 'l', + 0xC200, 'm', + 0xC100, 'n', + 0xC080, 'o', + 0xC040, 'p', + 0xC020, 'q', + 0xC010, 'r', + 0x6800, 's', + 0x6400, 't', + 0x6200, 'u', + 0x6100, 'v', + 0x6080, 'w', + 0x6040, 'x', + 0x6020, 'y', + 0x6010, 'z', /* these odd punch codes are used by APL: */ + 0x1010, '\001', /* no corresponding ASCII using ^A */ + 0x0810, '\002', /* SYN using ^B */ + 0x0410, '\003', /* no corresponding ASCII using ^C */ + 0x0210, '\004', /* PUNCH ON using ^D */ + 0x0110, '\005', /* READER STOP using ^E */ + 0x0090, '\006', /* UPPER CASE using ^F */ + 0x0050, '\013', /* EOT using ^K */ + 0x0030, '\016', /* no corresponding ASCII using ^N */ + 0x1030, '\017', /* no corresponding ASCII using ^O */ + 0x0830, '\020', /* no corresponding ASCII using ^P */ +}; + +static CPCODE cardcode_026F[] = /* 026 fortran */ +{ + 0x0000, ' ', + 0x8000, '+', + 0x4000, '-', + 0x2000, '0', + 0x1000, '1', + 0x0800, '2', + 0x0400, '3', + 0x0200, '4', + 0x0100, '5', + 0x0080, '6', + 0x0040, '7', + 0x0020, '8', + 0x0010, '9', + 0x9000, 'A', + 0x8800, 'B', + 0x8400, 'C', + 0x8200, 'D', + 0x8100, 'E', + 0x8080, 'F', + 0x8040, 'G', + 0x8020, 'H', + 0x8010, 'I', + 0x5000, 'J', + 0x4800, 'K', + 0x4400, 'L', + 0x4200, 'M', + 0x4100, 'N', + 0x4080, 'O', + 0x4040, 'P', + 0x4020, 'Q', + 0x4010, 'R', + 0x3000, '/', + 0x2800, 'S', + 0x2400, 'T', + 0x2200, 'U', + 0x2100, 'V', + 0x2080, 'W', + 0x2040, 'X', + 0x2020, 'Y', + 0x2010, 'Z', + 0x0420, '=', + 0x0220, '\'', + 0x8420, '.', + 0x8220, ')', + 0x4420, '$', + 0x4220, '*', + 0x2420, ',', + 0x2220, '(', +}; + +static CPCODE cardcode_026C[] = /* 026 commercial */ +{ + 0x0000, ' ', + 0x8000, '+', + 0x4000, '-', + 0x2000, '0', + 0x1000, '1', + 0x0800, '2', + 0x0400, '3', + 0x0200, '4', + 0x0100, '5', + 0x0080, '6', + 0x0040, '7', + 0x0020, '8', + 0x0010, '9', + 0x9000, 'A', + 0x8800, 'B', + 0x8400, 'C', + 0x8200, 'D', + 0x8100, 'E', + 0x8080, 'F', + 0x8040, 'G', + 0x8020, 'H', + 0x8010, 'I', + 0x5000, 'J', + 0x4800, 'K', + 0x4400, 'L', + 0x4200, 'M', + 0x4100, 'N', + 0x4080, 'O', + 0x4040, 'P', + 0x4020, 'Q', + 0x4010, 'R', + 0x3000, '/', + 0x2800, 'S', + 0x2400, 'T', + 0x2200, 'U', + 0x2100, 'V', + 0x2080, 'W', + 0x2040, 'X', + 0x2020, 'Y', + 0x2010, 'Z', + 0x0420, '=', + 0x0220, '\'', + 0x8420, '.', + 0x8220, ')', + 0x4420, '$', + 0x4220, '*', + 0x2420, ',', + 0x2220, '(', +}; + +extern int cgi; +extern void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *arg[]); + +static int16 ascii_to_card[256]; + +static CPCODE *cardcode; +static int ncardcode; +static FILE *deckfile = NULL; +static char tempfile[128]; +static int any_punched = 0; + +#define MAXARGLEN 80 /* max length of a saved attach command argument */ +#define MAXARGS 10 /* max number of arguments to save */ +static char list_save[MAXARGS][MAXARGLEN], *list_arg[MAXARGLEN]; +static int list_nargs = 0; +static char* (*tab_proc)(char*) = NULL; /* tab reformatting routine */ + +static uint16 punchstation[80]; +static uint16 readstation[80]; +static enum {STATION_EMPTY, STATION_LOADED, STATION_READ, STATION_PUNCHED} punchstate = STATION_EMPTY, readstate = STATION_EMPTY; + +static t_bool nextdeck (void); +static void checkdeck (void); + +static t_stat pcr_attach(UNIT *uptr, char *devname); +static t_stat pcr_detach(UNIT *uptr); +static t_stat pcr_svc(UNIT *uptr); +static void pcr_xio_sense(int modify); +static void pcr_xio_feedcycle(void); +static void pcr_xio_startread(void); +static void pcr_reset(void); + +/* lookup_codetable - use code flag setting to get code table pointer and length */ + +static t_bool lookup_codetable (int32 match, CPCODE **pcode, int *pncode) +{ + switch (match) { + case CODE_029: + *pcode = cardcode_029; + *pncode = sizeof(cardcode_029) / sizeof(CPCODE); + break; + + case CODE_026F: + *pcode = cardcode_026F; + *pncode = sizeof(cardcode_026F) / sizeof(CPCODE); + break; + + case CODE_026C: + *pcode = cardcode_026C; + *pncode = sizeof(cardcode_026C) / sizeof(CPCODE); + break; + + case CODE_BINARY: + *pcode = NULL; + *pncode = 0; + break; + + default: + printf("Eek! Undefined code table index"); + return FALSE; + } + return TRUE; +} + +t_stat set_active_cr_code (int match) +{ + CPCODE *code; + int i, ncode; + + SET_ACTCODE(cr_unit, match); + + if (! lookup_codetable(match, &code, &ncode)) + return SCPE_ARG; + + memset(ascii_to_card, 0, sizeof(ascii_to_card)); + + for (i = 0; i < ncode; i++) /* set ascii to card code table */ + ascii_to_card[code[i].ascii] = code[i].hollerith; + + return SCPE_OK; +} + +static t_stat cr_set_code (UNIT *uptr, int32 match, char *cptr, void *desc) +{ + if (match == CODE_AUTO) + match = guess_cr_code(); + + return set_active_cr_code(match); +} + +static int32 guess_cr_code (void) +{ + int i; + long filepos; + int32 guess; + union { + uint16 w[80]; /* one card image, viewed as 80 short words */ + char c[160]; /* same, viewed as 160 characters */ + } line; + + /* here, we can see if the attached file is binary or ascii and auto-set the + * mode. If we the file is a binary deck, we should be able to read a record of 80 short + * words, and the low 4 bits of each word must be zero. If the file was an ascii deck, + * then these low 4 bits are the low 4 bits of every other character in the first 160 + * chararacters of the file. They would all only be 0 if all of these characters were + * in the following set: {NUL ^P space 0 @ P ` p} . It seems very unlikely that + * this would happen, as even if the deck consisted of untrimmed card images and + * the first two lines were blank, the 81'st character would be a newline, and it would + * appear at one of the every-other characters seen on little-endian machines, anyway. + * So: if the code mode is AUTO, we can use this test and select either BINARY or 029. + * Might as well also check for the all-blanks and newlines case in case this is a + * big-endian machine. + */ + + + guess = CODE_029; /* assume ASCII, 029 */ + + if ((cr_unit.flags & UNIT_ATT) && (cr_unit.fileref != NULL)) { + filepos = ftell(cr_unit.fileref); /* remember current position in file */ + fseek(cr_unit.fileref, 0, SEEK_SET); /* go to first record of file */ + /* read card image; if file too short, leave guess set to 029 */ + if (fxread(line.w, sizeof(line.w[0]), 80, cr_unit.fileref) == 80) { + guess = CODE_BINARY; /* we got a card image, assume binary */ + + for (i = 0; i < 80; i++) { /* make sure low bits are zeroes, which our binary card format promises */ + if (line.w[i] & 0x000F) { + guess = CODE_029; /* low bits set, must be ascii text */ + break; + } + } + + if (guess == CODE_BINARY) { /* if we saw no low bits, it could have been all spaces. */ + guess = CODE_029; /* so now assume file is text */ + for (i = 0; i < 160; i++) { /* ensure all 160 characters are 7-bit ASCII (or not or cent) */ + /* 3.0-3, changed test for > 0x7f to & 0x80 */ + if ((strchr("\r\n\t\xA2\xAC", line.c[i]) == NULL) && ((line.c[i] < ' ') || (line.c[i] & 0x80))) { + guess = CODE_BINARY; /* oops, null or weird character, it's binary after all */ + break; + } + } + } + } + + fseek(cr_unit.fileref, filepos, SEEK_SET); /* return to original position */ + } + + return guess; +} + +static t_stat cp_set_code (UNIT *uptr, int32 match, char *cptr, void *desc) +{ + CPCODE *code; + int ncode; + + if (! lookup_codetable(match, &code, &ncode)) + return SCPE_ARG; + + cardcode = code; /* save code table for punch output */ + ncardcode = ncode; + + return SCPE_OK; +} + +t_stat load_cr_boot (int drvno, int switches) +{ + int i; + char *name, msg[80]; + t_bool expand; + uint16 word, *boot; + static uint16 dms_boot_data[] = { /* DMSV2M12, already expanded to 16 bits */ + 0xc80a, 0x18c2, 0xd008, 0xc019, 0x8007, 0xd017, 0xc033, 0x100a, + 0xd031, 0x7015, 0x000c, 0xe800, 0x0020, 0x08f8, 0x4828, 0x7035, + 0x70fa, 0x4814, 0xf026, 0x2000, 0x8800, 0x9000, 0x9800, 0xa000, + 0xb000, 0xb800, 0xb810, 0xb820, 0xb830, 0xb820, 0x3000, 0x08ea, + 0xc0eb, 0x4828, 0x70fb, 0x9027, 0x4830, 0x70f8, 0x8001, 0xd000, + 0xc0f4, 0xd0d9, 0xc01d, 0x1804, 0xe8d6, 0xd0d9, 0xc8e3, 0x18d3, + 0xd017, 0x18c4, 0xd0d8, 0x9016, 0xd815, 0x90db, 0xe8cc, 0xd0ef, + 0xc016, 0x1807, 0x0035, 0x00d0, 0xc008, 0x1803, 0xe8c4, 0xd00f, + 0x080d, 0x08c4, 0x1003, 0x4810, 0x70d9, 0x3000, 0x08df, 0x3000, + 0x7010, 0x00d1, 0x0028, 0x000a, 0x70f3, 0x0000, 0x00d0, 0xa0c0 + }; + static uint16 apl_boot_data[] = { /* APLIPL, already expanded */ + 0x7021, 0x3000, 0x7038, 0xa0c0, 0x0002, 0x4808, 0x0003, 0x0026, + 0x0001, 0x0001, 0x000c, 0x0000, 0x0000, 0x0800, 0x48f8, 0x0027, + 0x7002, 0x08f2, 0x3800, 0xe0fe, 0x18cc, 0x100e, 0x10c1, 0x4802, + 0x7007, 0x4828, 0x7005, 0x4804, 0x7001, 0x70f3, 0x08e7, 0x70e1, + 0x08ed, 0x70f1, 0xc0e0, 0x1807, 0xd0de, 0xc0df, 0x1801, 0xd0dd, + 0x800d, 0xd00c, 0xc0e3, 0x1005, 0xe80a, 0xd009, 0xc0d8, 0x1008, + 0xd0d6, 0xc0dd, 0x1008, 0x80d4, 0xd0da, 0x1000, 0xb000, 0x00f6, + 0x70e7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x9000, 0x4004, 0x40c0, 0x8001, 0x4004, 0x40c0, 0x0000, 0x0000 }; + static uint16 aplp_boot_data[] = { /* APLIPL Privileged, already expanded */ + 0x7021, 0x3000, 0x7038, 0xa0c0, 0x0002, 0x4808, 0x0003, 0x0026, + 0x0001, 0x0001, 0x000c, 0x0000, 0x0000, 0x0800, 0x48f8, 0x0027, + 0x7002, 0x08f2, 0x3800, 0xe0fe, 0x18cc, 0x100e, 0x10c1, 0x4802, + 0x7007, 0x4828, 0x7005, 0x4804, 0x7001, 0x70f3, 0x08e7, 0x70e1, + 0x08ed, 0x70f1, 0xc0e0, 0x1807, 0xd0de, 0xc0df, 0x1801, 0xd0dd, + 0x800d, 0xd00c, 0xc0e3, 0x1005, 0xe80a, 0xd009, 0xc0d8, 0x1008, + 0xd0d6, 0xc0dd, 0x1008, 0x80d4, 0xd0da, 0x1002, 0xb000, 0x00f6, + 0x70e7, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x9000, 0x4004, 0x40c0, 0x8001, 0x4004, 0x40c0, 0x4004, 0x4001 + }; + + if ((switches & SWMASK('A')) && (switches & SWMASK('P'))) { + boot = aplp_boot_data; + name = "APL\\1130 Privileged"; + expand = FALSE; + } + else if (switches & SWMASK('A')) { + boot = apl_boot_data; + name = "APL\\1130"; + expand = FALSE; + } + else { + boot = dms_boot_data; + name = "DMS V2M12"; + expand = FALSE; + } + + if (drvno >= 0 && ! (switches & SWMASK('S'))) /* if specified, set toggle switches to disk drive no */ + CES = drvno; /* so BOOT DSK1 will work correctly (DMS boot uses this) */ + /* but do not touch switches if -S was specified */ + + IAR = 0; /* clear IAR */ + + for (i = 0; i < 80; i++) { /* store the boot image to core words 0..79 */ + word = boot[i]; /* expanding the 12-bit card data to 16 bits if not already expanded */ + if (expand) + word = (word & 0xF800) | ((word & 0x0400) ? 0x00C0 : 0x0000) | ((word & 0x03F0) >> 4); + + WriteW(i, word); + } + /* quiet switch or CGI mode inhibit the boot remark */ + if (((switches & SWMASK('Q')) == 0) && ! cgi) { /* 3.0-3, parenthesized & operation, per lint check */ + sprintf(msg, "Loaded %s cold start card\n", name); + +#ifdef GUI_SUPPORT + remark_cmd(msg); +#else + printf(msg); +#endif + } + + return SCPE_OK; +} + +t_stat cr_boot (int unitno, DEVICE *dptr) +{ + t_stat rval; + int i; + + if ((rval = reset_all(0)) != SCPE_OK) + return rval; + + if (! (cr_unit.flags & UNIT_ATT)) /* no deck; load standard boot anyway */ + return load_cr_boot(-1, 0); + + if (GET_ACTCODE(cr_unit) != CODE_BINARY) { + printf("Can only boot from card reader when set to BINARY mode"); + return SCPE_IOERR; + } + + if (cr_unit.fileref == NULL) /* this will happen if no file in deck file can be opened */ + return SCPE_IOERR; + + feedcycle(TRUE, FALSE); + +/* if (fxread(buf, sizeof(buf[0]), 80, cr_unit.fileref) != 80) */ +/* return SCPE_IOERR; */ + + IAR = 0; /* Program Load sets IAR = 0 */ + + for (i = 0; i < 80; i++) /* shift 12 bits into 16 */ + WriteW(i, (readstation[i] & 0xF800) | ((readstation[i] & 0x0400) ? 0x00C0 : 0x0000) | ((readstation[i] & 0x03F0) >> 4)); + + return SCPE_OK; +} + +char card_to_ascii (uint16 hol) +{ + int i; + + for (i = 0; i < ncardcode; i++) + if (cardcode[i].hollerith == hol) + return (char) cardcode[i].ascii; + + return '?'; +} + +/* hollerith_to_ascii - provide a generic conversion for simulator debugging */ + +char hollerith_to_ascii (uint16 hol) +{ + int i; + + for (i = 0; i < ncardcode; i++) + if (cardcode_029[i].hollerith == hol) + return (char) cardcode[i].ascii; + + return ' '; +} + +/* feedcycle - move cards to next station */ + +static void feedcycle (t_bool load, t_bool punching) +{ + char buf[84], *x, *result; + int i, nread, nwrite, ch; + + /* write punched card if punch is attached to a file */ + if (cp_unit.flags & UNIT_ATT) { + if (any_punched && punchstate != STATION_EMPTY) { + if (GET_CODE(cp_unit) == CODE_BINARY) { + fxwrite(punchstation, sizeof(punchstation[0]), 80, cp_unit.fileref); + } + else { + for (i = 80; --i >= 0; ) { /* find last nonblank column */ + if (punchstation[i] != 0) + break; + } + + /* i is now index of last character to output or -1 if all blank */ + + for (nwrite = 0; nwrite <= i; nwrite++) { /* convert characters */ + buf[nwrite] = card_to_ascii(punchstation[nwrite]); + } + + /* nwrite is now number of characters to output */ + +#ifdef WIN32 + buf[nwrite++] = '\r'; /* add CR before NL for microsoft */ +#endif + buf[nwrite++] = '\n'; /* append newline */ + fxwrite(buf, sizeof(char), nwrite, cp_unit.fileref); + } + } + + cp_count++; + } + + if (! load) /* all we wanted to do was flush the punch */ + return; + + /* slide cards from reader to punch. If we know we're punching, + * generate a blank card in any case. Otherwise, it should take two feed + * cycles to get a read card from the hopper to punch station. Also when + * the reader is a 2501, we assume the 1442 is a punch only */ + + if (readstate == STATION_EMPTY || (cr_unit.flags & UNIT_2501)) { + if (punching) { + memset(punchstation, 0, sizeof(punchstation)); + punchstate = STATION_LOADED; + } + else + punchstate = STATION_EMPTY; + } + else { + memcpy(punchstation, readstation, sizeof(punchstation)); + punchstate = STATION_LOADED; + } + + /* load card into read station */ + +again: /* jump here if we've loaded a new deck after emptying the previous one */ + + if (cr_unit.flags & UNIT_ATT) { + + memset(readstation, 0, sizeof(readstation)); /* blank out the card image */ + + if (cr_unit.fileref == NULL) + nread = 0; + + else if (GET_ACTCODE(cr_unit) == CODE_BINARY) /* binary read is straightforward */ + nread = fxread(readstation, sizeof(readstation[0]), 80, cr_unit.fileref); + + else if (fgets(buf, sizeof(buf), cr_unit.fileref) == NULL) /* read up to 80 chars */ + nread = 0; /* hmm, end of file */ + + else { /* check for CRLF or newline */ + if ((x = strchr(buf, '\r')) == NULL) + x = strchr(buf, '\n'); + + if (x == NULL) { /* there were no delimiters, burn rest of line */ + while ((ch = getc(cr_unit.fileref)) != EOF) { /* get character */ + if (ch == '\n') /* newline, done */ + break; + + if (ch == '\r') { /* CR, try to take newline too */ + ch = getc(cr_unit.fileref); + if (ch != EOF && ch != '\n') /* hmm, put it back */ + ungetc(ch, cr_unit.fileref); + + break; + } + } + if ((nread = strlen(buf)) > 80) /* use the line as read, at most 80 characters */ + nread = 80; + } + else + nread = x-buf; /* reduce length of string */ + + if (! (cr_unit.flags & UNIT_LOWERCASE)) + upcase(buf); /* force uppercase */ + + if (tab_proc != NULL) { /* apply tab editing, if specified */ + buf[nread] = '\0'; /* .. be sure string is terminated */ + result = (*tab_proc)(buf); /* .. convert tabs spaces */ + nread = strlen(result); /* .. set new read length */ + } + else + result = buf; + + for (i = 0; i < nread; i++) /* convert ascii to punch code */ + readstation[i] = ascii_to_card[(unsigned char) result[i]]; + + nread = 80; /* even if line was blank consider it present */ + } + + if (nread <= 0) { /* set hopper flag accordingly */ + if (deckfile != NULL && nextdeck()) + goto again; + + if (punching) /* pretend we loaded a blank card */ + nread = 80; + } + + if (nread == 0) { + SETBIT(cr_unit.flags, UNIT_CR_EMPTY); + readstate = STATION_EMPTY; + cr_count = -1; /* nix the card counter */ + } + else { + CLRBIT(cr_unit.flags, UNIT_CR_EMPTY); + readstate = STATION_LOADED; + cr_count++; + cr_unit.pos++; + } + } +/* else */ +/* readstate = STATION_EMPTY; */ + + cr_unit.COLUMN = -1; /* neither device is currently cycling */ + cp_unit.COLUMN = -1; +} + +#ifdef NO_USE_FOR_THIS_CURRENTLY + +/* this routine should probably be hooked up to the GUI somehow */ + +/* NPRO - nonprocess runout, flushes out the reader/punch */ + +static void npro (void) +{ + if (cr_unit.flags & UNIT_ATT) + fseek(cr_unit.fileref, 0, SEEK_END); /* push reader to EOF */ + if (deckfile != NULL) + fseek(deckfile, 0, SEEK_END); /* skip to end of deck list */ + + cr_count = -1; /* nix the card counter */ + + if (punchstate == STATION_PUNCHED) + feedcycle(FALSE, FALSE); /* flush out card just punched */ + + readstate = punchstate = STATION_EMPTY; + cr_unit.COLUMN = -1; /* neither device is currently cycling */ + cp_unit.COLUMN = -1; + SETBIT(cr_unit.flags, UNIT_CR_EMPTY); /* set hopper empty */ +} + +#endif + +/* skipbl - skip leading whitespace in a string */ + +static char * skipbl (char *str) +{ + while (*str && *str <= ' ') + str++; + + return str; +} + +static char * trim (char *str) +{ + char *s, *lastnb; + + for (lastnb = str-1, s = str; *s; s++) /* point to last nonblank characteter in string */ + if (*s > ' ') + lastnb = s; + + lastnb[1] = '\0'; /* clip just after it */ + + return str; +} + +/* alltrim - remove all leading and trailing whitespace from a string */ + +static char * alltrim (char *str) +{ + char *s, *lastnb; + + if ((s = skipbl(str)) != str) /* slide down over leading whitespace */ + strcpy(str, s); + + for (lastnb = str-1, s = str; *s; s++) /* point to last nonblank characteter in string */ + if (*s > ' ') + lastnb = s; + + lastnb[1] = '\0'; /* clip just after it */ + + return str; +} + +/* checkdeck - set hopper empty status based on condition of current reader file */ + +static void checkdeck (void) +{ + t_bool empty; + + if (cr_unit.fileref == NULL) { /* there is no open file */ + empty = TRUE; + } + else { + fseek(cr_unit.fileref, 0, SEEK_END); /* seek to end of file */ + empty = ftell(cr_unit.fileref) <= 0; /* file is empty if there was nothing in it*/ + cr_count = 0; /* reset card counter */ + cr_unit.pos = 0; + fseek(cr_unit.fileref, 0, SEEK_SET); /* rewind deck */ + } + + if (empty) { + SETBIT(cr_unit.flags, UNIT_CR_EMPTY); + if (cr_unit.fileref != NULL) /* real file but it's empty, hmmm, try another */ + nextdeck(); + } + else { + CLRBIT(cr_unit.flags, UNIT_CR_EMPTY); + } +} + +/* nextdeck - attempt to load a new file from the deck list into the hopper */ + +static t_bool nextdeck (void) +{ + char buf[200], tmpbuf[200], *fname, *c, quote, *mode; + int code; + long fpos; + + cr_count = 0; /* clear read count */ + cr_unit.pos = 0; + + if (deckfile == NULL) /* we can't help */ + return FALSE; + + code = GET_CODE(cr_unit); /* default code as set */ + + if (cr_unit.fileref != NULL) { /* this pulls the rug out from under scp */ + fclose(cr_unit.fileref); /* since the attach flag is still set. be careful! */ + cr_unit.fileref = NULL; + + if (cr_unit.flags & UNIT_SCRATCH) { + remove(tempfile); + CLRBIT(cr_unit.flags, UNIT_SCRATCH); + } + } + + for (;;) { /* get a filename */ + tab_proc = NULL; /* default: no tab editing */ + + if (fgets(buf, sizeof(buf), deckfile) == NULL) + break; /* oops, no more names */ + + alltrim(buf); /* remove leading and trailing spaces */ + + if (! *buf) + continue; /* empty line */ + + if (*buf == '#' || *buf == '*' || *buf == ';') + continue; /* comment */ + + if (strnicmp(buf, "!BREAK", 6) == 0) { /* stop the simulation */ + break_simulation(STOP_DECK_BREAK); + continue; + } + + if (buf[0] == '!') { /* literal text line, make a temporary file */ + +#if defined (__GNUC__) && !defined (_WIN32) /* GCC complains about mktemp & always provides mkstemp */ + + if (*tempfile == '\0') { /* first time, open guaranteed-unique file */ + int fh; + + strcpy(tempfile, "tempXXXXXX"); /* get modifiable copy of name template */ + + if ((fh = mkstemp(tempfile)) == -1) { /* open file. Actual name is set by side effect */ + printf("Cannot create temporary deck file\n"); + break_simulation(STOP_DECK_BREAK); + return 0; + } + /* get FILE * from the file handle */ + if ((cr_unit.fileref = fdopen(fh, "w+b")) == NULL) { + printf("Cannot use temporary deck file %s\n", tempfile); + break_simulation(STOP_DECK_BREAK); + return 0; + } + } + else { /* on later opens, just reuse the old name */ + if ((cr_unit.fileref = fopen(tempfile, "w+b")) == NULL) { + printf("Cannot create temporary file %s\n", tempfile); + break_simulation(STOP_DECK_BREAK); + return 0; + } + } +#else /* ANSI standard C always provides mktemp */ + + if (*tempfile == '\0') { /* first time, construct unique name */ + strcpy(tempfile, "tempXXXXXX"); /* make a modifiable copy of the template */ + if (mktemp(tempfile) == NULL) { + printf("Cannot create temporary card file name\n"); + break_simulation(STOP_DECK_BREAK); + return 0; + } + } + /* (re)create file */ + if ((cr_unit.fileref = fopen(tempfile, "w+b")) == NULL) { + printf("Cannot create temporary file %s\n", tempfile); + break_simulation(STOP_DECK_BREAK); + return 0; + } +#endif + + SETBIT(cr_unit.flags, UNIT_SCRATCH); + + for (;;) { /* store literal cards into temporary file */ + upcase(buf+1); + fputs(buf+1, cr_unit.fileref); + putc('\n', cr_unit.fileref); + + if (cpu_unit.flags & UNIT_ATT) + trace_io("(Literal card %s\n)", buf+1); + if (! (cr_unit.flags & UNIT_QUIET)) + printf( "(Literal card %s)\n", buf+1); + + fpos = ftell(deckfile); + if (fgets(buf, sizeof(buf), deckfile) == NULL) + break; /* oops, end of file */ + if (buf[0] != '!' || strnicmp(buf, "!BREAK", 6) == 0) + break; + alltrim(buf); + } + fseek(deckfile, fpos, SEEK_SET); /* restore deck file to just before non-literal card */ + + fseek(cr_unit.fileref, 0, SEEK_SET); /* rewind scratch file for reading */ + code = CODE_029; /* assume literal cards use keycode 029 */ + break; + } + + sub_args(buf, tmpbuf, sizeof(buf), list_nargs, list_arg); /* substitute in stuff from the attach command line */ + + c = buf; /* pick filename from string */ + + while (*c && *c <= ' ') /* skip leading blanks (there could be some now after subsitution) */ + c++; + + fname = c; /* remember start */ + + if (*c == '\'' || *c == '"') { /* quoted string */ + quote = *c++; /* remember the quote type */ + fname++; /* skip the quote */ + while (*c && (*c != quote)) + c++; /* skip to end of quote */ + } + else { /* not quoted; look for terminating whitespace */ + while (*c && (*c > ' ')) + c++; + } + + if (*c) + *c++ = 0; /* term arg at space or closing quote & move to next character */ + + if (! *fname) /* blank line, no filename */ + continue; + + if ((cr_unit.fileref = fopen(fname, "rb")) == NULL) { + printf("File '%s' specified in deck file '%s' cannot be opened\n", fname, cr_unit.filename+1); + continue; + } + + mode = c = skipbl(c); /* skip to next token, which would be mode, if present */ + + switch (*c) { + case 'b': + case 'B': + code = CODE_BINARY; /* force code */ + c++; /* accept mode character by moving past it */ + break; + + case 'a': + case 'A': + code = CODE_029; + c++; + + switch (*c) { /* is ascii mode followed by another character? */ + case 'F': + case 'f': + tab_proc = EditToFortran; + c++; + break; + + case 'A': + case 'a': + tab_proc = EditToAsm; + c++; + break; + + case 't': + case 'T': + tab_proc = EditToWhitespace; + c++; + break; + } + } + + if (code == CODE_AUTO) /* otherwise if mode is auto, guess it, otherwise use default */ + code = guess_cr_code(); + + if (cpu_unit.flags & UNIT_ATT) + trace_io("(Opened %s deck %s%s)\n", (code == CODE_BINARY) ? "binary" : "text", fname, tab_proc ? (*tab_proc)(NULL) : ""); + + if (! (cr_unit.flags & UNIT_QUIET)) + printf( "(Opened %s deck %s%s)\n", (code == CODE_BINARY) ? "binary" : "text", fname, tab_proc ? (*tab_proc)(NULL) : ""); + + break; + } + + checkdeck(); + + if (code != CODE_AUTO) /* if code was determined, set it */ + set_active_cr_code(code); /* (it may be left at CODE_AUTO when deckfile is exhausted */ + + return (cr_unit.flags & UNIT_CR_EMPTY) == 0;/* return TRUE if a deck has been loaded */ +} + +static t_stat cr_reset (DEVICE *dptr) +{ + if (GET_ACTCODE(cr_unit) == CODE_AUTO) + SET_ACTCODE(cr_unit, CODE_029); /* if actual code is not yet set, select 029 for now*/ + + cr_set_code(&cr_unit, GET_ACTCODE(cr_unit), NULL, NULL); /* reset to specified code table */ + + readstate = STATION_EMPTY; + + cr_dsw = 0; + sim_cancel(&cr_unit); /* cancel any pending ops */ + calc_ints(); + + SET_OP(OP_IDLE); + + cr_unit.COLUMN = -1; /* neither device is currently cycling */ + + if (cr_unit.flags & UNIT_PHYSICAL) { + pcr_reset(); + return SCPE_OK; + } + + return SCPE_OK; +} + +static t_stat cp_reset (DEVICE *dptr) +{ + if (GET_CODE(cp_unit) == CODE_AUTO) + SET_CODE(cp_unit, CODE_BINARY); /* punch is never in auto mode; turn it to binary on startup */ + + cp_set_code(&cp_unit, GET_CODE(cp_unit), NULL, NULL); + punchstate = STATION_EMPTY; + + cp_unit.COLUMN = -1; + return SCPE_OK; +} + +t_stat cr_rewind (void) +{ + if ((cr_unit.flags & UNIT_ATT) == 0) + return SCPE_UNATT; + + if (deckfile) { + fseek(deckfile, 0, SEEK_SET); + nextdeck(); + } + else { + fseek(cr_unit.fileref, 0, SEEK_SET); + checkdeck(); + cr_set_code(&cr_unit, GET_CODE(cr_unit), NULL, NULL); + } + + cr_unit.pos = 0; + + /* there is a read pending. Pull the card in to make it go */ + if (CURRENT_OP == OP_READING || CURRENT_OP == OP_PUNCHING || CURRENT_OP == OP_FEEDING) + feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0); + + return SCPE_OK; +} + +static t_stat cr_attach (UNIT *uptr, char *cptr) +{ + t_stat rval; + t_bool use_decklist; + char *c, *arg, quote; + + cr_detach(uptr); /* detach file and possibly deck file */ + + CLRBIT(uptr->flags, UNIT_SCRATCH|UNIT_QUIET|UNIT_DEBUG|UNIT_PHYSICAL|UNIT_LOWERCASE); /* set options */ + + tab_proc = NULL; + use_decklist = FALSE; + + if (sim_switches & SWMASK('D')) SETBIT(uptr->flags, UNIT_DEBUG); + if (sim_switches & SWMASK('Q')) SETBIT(uptr->flags, UNIT_QUIET); + if (sim_switches & SWMASK('L')) SETBIT(uptr->flags, UNIT_LOWERCASE); + + if (sim_switches & SWMASK('F')) tab_proc = EditToFortran; + if (sim_switches & SWMASK('A')) tab_proc = EditToAsm; + if (sim_switches & SWMASK('T')) tab_proc = EditToWhitespace; + + /* user can specify multiple names on the CR attach command if using a deck file. The deck file + * can contain %n tokens to pickup the additional name(s). */ + + c = cptr; /* extract arguments */ + for (list_nargs = 0; list_nargs < MAXARGS; list_nargs++) { + while (*c && (*c <= ' ')) /* skip blanks */ + c++; + + if (! *c) + break; /* all done */ + + if (list_nargs == 0 && *c == '@') { /* @ might occur before a quoted name; check first */ + c++; + use_decklist = TRUE; + } + + if (*c == '\'' || *c == '"') { /* quoted string */ + quote = *c++; + arg = c; /* save start */ + while (*c && (*c != quote)) + c++; + } + else { + arg = c; /* save start */ + while (*c && (*c > ' ')) + c++; + } + + if (*c) + *c++ = 0; /* term arg at space or closing quote */ + + list_arg[list_nargs] = list_save[list_nargs]; /* set pointer to permanent storage location */ + strncpy(list_arg[list_nargs], arg, MAXARGLEN); /* store copy */ + } + + if (list_nargs <= 0) /* need at least 1 */ + return SCPE_2FARG; + + cr_count = 0; /* reset card counter */ + + cptr = list_arg[0]; /* filename is first argument */ + if (*cptr == '@') { /* @ might also occur inside a quoted name; check afterwards too */ + use_decklist = TRUE; + cptr++; + } + + else if (sim_switches & SWMASK('P')) { /* open physical card reader device */ + return pcr_attach(uptr, cptr); + } + + if (list_nargs > 1 && ! use_decklist) /* if not using deck file, there should have been only one name */ + return SCPE_2MARG; + + if (strcmp(cptr, "(stdin)") == 0 && ! use_decklist) { /* standard input */ + if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */ + uptr->filename = calloc(CBUFSIZE, sizeof(char)); + strcpy(uptr->filename, "(stdin)"); + uptr->fileref = stdin; + SETBIT(uptr->flags, UNIT_ATT); + uptr->pos = 0; + } + else if ((rval = attach_unit(uptr, cptr)) != SCPE_OK) { + return rval; + } + + if (use_decklist) { /* if we skipped the '@', store the actually-specified name */ + uptr->filename[0] = '@'; + strncpy(uptr->filename+1, cptr, CBUFSIZE-1); + + deckfile = cr_unit.fileref; /* save the deck file stream in our local variable */ + cr_unit.fileref = NULL; + nextdeck(); + } + else { + checkdeck(); + cr_set_code(&cr_unit, GET_CODE(cr_unit), NULL, NULL); + } + + /* there is a read pending. Pull the card in to make it go */ + if (CURRENT_OP == OP_READING || CURRENT_OP == OP_PUNCHING || CURRENT_OP == OP_FEEDING) + feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0); + + return SCPE_OK; +} + +t_stat cr_detach (UNIT *uptr) +{ + t_stat rval; + + cr_count = 0; /* clear read count */ + + if (cr_unit.flags & UNIT_PHYSICAL) + return pcr_detach(uptr); + + if (cr_unit.flags & UNIT_ATT && deckfile != NULL) { + if (cr_unit.fileref != NULL) /* close the active card deck */ + fclose(cr_unit.fileref); + + if (cr_unit.flags & UNIT_SCRATCH) { + remove(tempfile); + CLRBIT(cr_unit.flags, UNIT_SCRATCH); + } + + cr_unit.fileref = deckfile; /* give scp a file to close */ + } + + if (uptr->fileref == stdin) { + CLRBIT(uptr->flags, UNIT_ATT); + free(uptr->filename); + uptr->filename = NULL; + uptr->fileref = NULL; + rval = SCPE_OK; + } + else + rval = detach_unit(uptr); + + return rval; +} + +static t_stat cp_attach (UNIT *uptr, char *cptr) +{ + /* if -d is specified turn on debugging (bit is in card reader UNIT) */ + if (sim_switches & SWMASK('D')) SETBIT(cr_unit.flags, UNIT_DEBUG); + + return attach_unit(uptr, quotefix(cptr)); /* fix quotes in filenames & attach */ +} + +static t_stat cp_detach (UNIT *uptr) +{ + if (cp_unit.flags & UNIT_ATT) + if (punchstate == STATION_PUNCHED) + feedcycle(FALSE, FALSE); /* flush out card just punched */ + + any_punched = 0; /* reset punch detected */ + cp_count = 0; /* clear punch count */ + + return detach_unit(uptr); +} + +static void op_done (UNIT *u, t_bool issue_intr) +{ + if (u->flags & UNIT_DEBUG) + DEBUG_PRINT("!CR Op Complete, card %d", cr_count); + + SET_OP(OP_IDLE); + + if (u->flags & UNIT_2501) /* we use u-> not cr_unit. because PUNCH is always a 1442 */ + CLRBIT(cr_dsw, CR_DSW_2501_BUSY); + else + CLRBIT(cr_dsw, CR_DSW_1442_BUSY); /* this is trickier. 1442 cr and cp share a dsw */ + + if (issue_intr) { /* issue op-complete interrupt for read and punch ops but not feed */ + if (u->flags & UNIT_2501) { + SETBIT(cr_dsw, CR_DSW_2501_OP_COMPLETE); + SETBIT(ILSW[4], ILSW_4_2501_CARD); + } + else { + SETBIT(cr_dsw, CR_DSW_1442_OP_COMPLETE); + SETBIT(ILSW[4], ILSW_4_1442_CARD); + } + calc_ints(); + } +} + +static t_stat cr_svc (UNIT *uptr) +{ + int i; + + if (uptr->flags & UNIT_PHYSICAL) + return pcr_svc(uptr); + + switch (CURRENT_OP) { + case OP_IDLE: + break; + + case OP_FEEDING: + op_done(&cr_unit, FALSE); + break; + + case OP_READING: + if (readstate == STATION_EMPTY) { /* read active but no cards? hang */ + sim_activate(&cr_unit, cf_wait); + break; + } + + if (cr_unit.flags & UNIT_2501) { /* 2501 transfers entire card then interrupts */ + for (i = 0; i < cr_cols; i++) /* (we wait until end of delay time before transferring data) */ + M[(cr_addr + i) & mem_mask] = readstation[i]; + + readstate = STATION_READ; + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("!CR Op Complete, card %d", cr_count); + + op_done(&cr_unit, TRUE); + } + else if (++cr_unit.COLUMN < 80) { /* 1442 interrupts on each column... */ + SETBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE); + SETBIT(ILSW[0], ILSW_0_1442_CARD); + calc_ints(); + sim_activate(&cr_unit, cr_wait); + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("!CR Read Response %d : %d", cr_count, cr_unit.COLUMN+1); + } + else { /* ... then issues op-complete */ + readstate = STATION_READ; + op_done(&cr_unit, TRUE); + } + break; + + case OP_PUNCHING: + if (punchstate == STATION_EMPTY) { /* punch active but no cards? hang */ + sim_activate(&cr_unit, cf_wait); + break; + } + + if (cp_unit.flags & UNIT_LASTPUNCH) { + punchstate = STATION_PUNCHED; + op_done(&cp_unit, TRUE); + } + else if (++cp_unit.COLUMN < 80) { + SETBIT(cr_dsw, CR_DSW_1442_PUNCH_RESPONSE); + SETBIT(ILSW[0], ILSW_0_1442_CARD); + calc_ints(); + sim_activate(&cr_unit, cp_wait); + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("!CR Punch Response"); + } + else { + punchstate = STATION_PUNCHED; + op_done(&cp_unit, TRUE); + } + break; + } + + return SCPE_OK; +} + +void xio_2501_card (int32 addr, int32 func, int32 modify) +{ + char msg[80]; + int ch; + t_bool lastcard; + +/* it would be nice for simulated reader to be able to use 2501 mode -- much more + * efficient. Using the 1403 printer and 2501 reader speeds things up quite considerably. */ + + switch (func) { + case XIO_SENSE_DEV: + if (cr_unit.flags & UNIT_PHYSICAL) { + pcr_xio_sense(modify); + break; + } + +// the following part is questionable -- the 2501 might need to be more picky about setting +// the LAST_CARD bit... + + if ((cr_unit.flags & UNIT_ATT) == 0) + lastcard = TRUE; /* if nothing to read, hopper's empty */ + else if (readstate == STATION_LOADED) + lastcard = FALSE; + else if (cr_unit.fileref == NULL) + lastcard = TRUE; + else if ((ch = getc(cr_unit.fileref)) != EOF) { + ungetc(ch, cr_unit.fileref); /* put character back; hopper's not empty */ + lastcard = FALSE; + } + else if (deckfile != NULL && nextdeck()) + lastcard = FALSE; + else + lastcard = TRUE; /* there is nothing left to read for a next card */ + + CLRBIT(cr_dsw, CR_DSW_2501_LAST_CARD|CR_DSW_2501_BUSY|CR_DSW_2501_NOT_READY); + + if (lastcard) + SETBIT(cr_dsw, CR_DSW_2501_LAST_CARD|CR_DSW_2501_NOT_READY); + // don't clear it here -- modify bit must be set before last card can be cleared + + if (CURRENT_OP != OP_IDLE) + SETBIT(cr_dsw, CR_DSW_2501_BUSY|CR_DSW_2501_NOT_READY); + + ACC = cr_dsw; /* return the DSW */ + + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Sense %04x%s", cr_dsw & 0xFFFF, (modify & 1) ? " RESET" : ""); + + if (modify & 0x01) { /* reset interrupts */ +// if (! lastcard) /* (lastcard is reset only when modify bit is set) */ + CLRBIT(cr_dsw, CR_DSW_2501_LAST_CARD); + CLRBIT(cr_dsw, CR_DSW_2501_OP_COMPLETE); + CLRBIT(ILSW[4], ILSW_4_2501_CARD); + } + break; + + case XIO_INITR: + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Start read"); + + cr_unit.COLUMN = -1; + + cr_cols = M[addr & mem_mask]; /* save column count and transfer address */ + cr_addr = addr+1; + + if ((cr_cols < 0) || (cr_cols > 80)) /* this is questionable -- what would hardware do? */ + cr_cols = 80; + + if (cr_unit.flags & UNIT_PHYSICAL) { + pcr_xio_startread(); + break; + } + + if (readstate != STATION_LOADED) + feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0); + + SET_OP(OP_READING); + sim_cancel(&cr_unit); + sim_activate(&cr_unit, cr_wait2501); + break; + + default: + sprintf(msg, "Invalid 2501 XIO function %x", func); + xio_error(msg); + break; + } +} + +void xio_1142_card (int32 addr, int32 func, int32 modify) +{ + char msg[80]; + int ch; + uint16 wd; + t_bool lastcard; + + switch (func) { + case XIO_SENSE_DEV: + if (cr_unit.flags & UNIT_PHYSICAL) { + pcr_xio_sense(modify); + break; + } + +/* glunk + * have to separate out what status is 1442 is punch only and 2501 is the reader */ + + if (cp_unit.flags & UNIT_ATT) + lastcard = FALSE; /* if punch file is open, assume infinite blank cards in reader */ + else if ((cr_unit.flags & UNIT_ATT) == 0) + lastcard = TRUE; /* if nothing to read, hopper's empty */ + else if (readstate == STATION_LOADED) + lastcard = FALSE; + else if (cr_unit.fileref == NULL) + lastcard = TRUE; + else if ((ch = getc(cr_unit.fileref)) != EOF) { + ungetc(ch, cr_unit.fileref); /* put character back; hopper's not empty */ + lastcard = FALSE; + } + else if (deckfile != NULL && nextdeck()) + lastcard = FALSE; + else + lastcard = TRUE; /* there is nothing left to read for a next card */ + + CLRBIT(cr_dsw, CR_DSW_1442_LAST_CARD | CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY); + + if (lastcard) + SETBIT(cr_dsw, CR_DSW_1442_LAST_CARD); + + if (CURRENT_OP != OP_IDLE) + SETBIT(cr_dsw, CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY); + else if (readstate == STATION_EMPTY && punchstate == STATION_EMPTY && lastcard) + SETBIT(cr_dsw, CR_DSW_1442_NOT_READY); + + ACC = cr_dsw; /* return the DSW */ + + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Sense %04x%s%s", cr_dsw & 0xFFFF, (modify & 1) ? " RESET0" : "", (modify & 2) ? " RESET4" : ""); + + if (modify & 0x01) { /* reset interrupts */ + CLRBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE | CR_DSW_1442_PUNCH_RESPONSE); + CLRBIT(ILSW[0], ILSW_0_1442_CARD); + } + + if (modify & 0x02) { + CLRBIT(cr_dsw, CR_DSW_1442_OP_COMPLETE); + CLRBIT(ILSW[4], ILSW_4_1442_CARD); + } + break; + + case XIO_READ: /* get card data into word pointed to in IOCC packet */ + if (cr_unit.flags & OP_READING) { + if (cr_unit.COLUMN < 0) { + xio_error("1442: Premature read!"); + } + else if (cr_unit.COLUMN < 80) { + WriteW(addr, readstation[cr_unit.COLUMN]); + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Read %03x", (readstation[cr_unit.COLUMN] >> 4)); + } + else if (cr_unit.COLUMN == 80) { + xio_error("1442: Read past column 80!"); + cr_unit.COLUMN++; /* don't report it again */ + } + } + else { +/* don't complain: APL\1130 issues both reads and writes on every interrupt + * (probably to keep the code small). Apparently it's just ignored if corresponding + * control didn't initiate a read cycle. + * xio_error("1442: Read when not in a read cycle!"); */ + } + break; + + case XIO_WRITE: + if (cr_unit.flags & OP_PUNCHING) { + if (cp_unit.COLUMN < 0) { + xio_error("1442: Premature write!"); + } + else if (cp_unit.flags & UNIT_LASTPUNCH) { + xio_error("1442: Punch past last-punch column!"); + cp_unit.COLUMN = 81; + } + else if (cp_unit.COLUMN < 80) { + wd = (uint16) ReadW(addr); /* store one word to punch buffer */ + punchstation[cp_unit.COLUMN] = wd & 0xFFF0; + if (wd & 0x0008) /* mark this as last column to be punched */ + SETBIT(cp_unit.flags, UNIT_LASTPUNCH); + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Punch %03x%s", (wd >> 4) & 0xFFF, (wd & 8) ? " LAST" : ""); + } + else if (cp_unit.COLUMN == 80) { + xio_error("1442: Punch past column 80!"); + cp_unit.COLUMN++; /* don't report it again */ + } + } + else { +/* don't complain: APL\1130 issues both reads and writes on every interrupt + * (probably to keep the code small). Apparently it's just ignored if corresponding + * control didn't initiate a punch cycle. + * xio_error("1442: Write when not in a punch cycle!"); */ + } + break; + + case XIO_CONTROL: + switch (modify & 7) { + case 1: /* start punch */ + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Start Punch"); + if (punchstate != STATION_LOADED) + feedcycle(TRUE, TRUE); + + SET_OP(OP_PUNCHING); + cp_unit.COLUMN = -1; + + CLRBIT(cp_unit.flags, UNIT_LASTPUNCH); + + any_punched = 1; /* we've started punching, so enable writing to output deck file */ + + sim_cancel(&cr_unit); + sim_activate(&cr_unit, cp_wait); + break; + + case 2: /* feed cycle */ + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Feed"); + + if (cr_unit.flags & UNIT_PHYSICAL) { + pcr_xio_feedcycle(); + break; + } + + feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0); + + SET_OP(OP_FEEDING); + sim_cancel(&cr_unit); + sim_activate(&cr_unit, cf_wait); + break; + + case 4: /* start read */ + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Start read"); + + cr_unit.COLUMN = -1; + + if (cr_unit.flags & UNIT_PHYSICAL) { + pcr_xio_startread(); + break; + } + + if (readstate != STATION_LOADED) + feedcycle(TRUE, (cp_unit.flags & UNIT_ATT) != 0); + + SET_OP(OP_READING); + sim_cancel(&cr_unit); + sim_activate(&cr_unit, cr_wait); + break; + + case 0: + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR NOP"); + break; + + default: + sprintf(msg, "1442: Multiple operations in XIO_CONTROL: %x", modify); + xio_error(msg); + return; + } + + break; + + default: + sprintf(msg, "Invalid 1442 XIO function %x", func); + xio_error(msg); + break; + } +} + +#if ! (defined(ENABLE_PHYSICAL_CARD_READER_SUPPORT) && defined(WIN32)) + + /* stub out the physical card reader routines */ + + static t_stat pcr_attach (UNIT *uptr, char *devname) {return SCPE_ARG;} + static t_stat pcr_detach (UNIT *uptr) {return detach_unit(uptr);} + static t_stat pcr_svc (UNIT *uptr) {return SCPE_OK;} + static void pcr_xio_sense (int modify) {} + static void pcr_xio_feedcycle (void) {} + static void pcr_xio_startread (void) {} + static void pcr_reset (void) {} + +#else + +/* + * This code supports a physical card reader interface I built. Interface schematic + * and documentation can be downloaded from http://ibm1130.org/sim/downloads/cardread.zip + */ + +#include + +#define PCR_STATUS_READY 1 /* bits in interface reply byte */ +#define PCR_STATUS_ERROR 2 +#define PCR_STATUS_HEMPTY 4 +#define PCR_STATUS_EOF 8 +#define PCR_STATUS_PICKING 16 + +#define PCR_STATUS_MSEC 150 /* when idle, get status every 150 msec */ + +typedef enum { + PCR_STATE_IDLE, /* nothing expected from the interface */ + PCR_STATE_WAIT_CMD_RESPONSE, /* waiting for response from any command other than P */ + PCR_STATE_WAIT_PICK_CMD_RESPONSE, /* waiting for response from P command */ + PCR_STATE_WAIT_DATA_START, /* waiting for introduction to data from P command */ + PCR_STATE_WAIT_DATA, /* waiting for data from P command */ + PCR_STATE_WAIT_PICK_FINAL_RESPONSE, /* waiting for status byte after last of the card data */ + PCR_STATE_CLOSED +} PCR_STATE; + +static void pcr_cmd (char cmd); +static DWORD CALLBACK pcr_thread (LPVOID arg); +static BOOL pcr_handle_status_byte (int nrcvd); +static void pcr_trigger_interrupt_0(void); +static void begin_pcr_critical_section (void); +static void end_pcr_critical_section (void); +static void pcr_set_dsw_from_status (BOOL post_pick); +static t_stat pcr_open_controller (char *devname); + +static PCR_STATE pcr_state = PCR_STATE_CLOSED; /* current state of connection to physical card reader interface */ +static char pcr_status = 0; /* last status byte received from the interface */ +static int pcr_nleft; /* number of bytes still expected from pick command */ +static int pcr_nready; /* number of bytes waiting in the input buffer for simulator to read */ +static BOOL pcr_done; +static HANDLE hpcr = INVALID_HANDLE_VALUE; +static HANDLE hPickEvent = INVALID_HANDLE_VALUE; +static HANDLE hResetEvent = INVALID_HANDLE_VALUE; +static OVERLAPPED ovRd, ovWr; /* overlapped IO structures for reading from, writing to device */ +static int nwaits; /* number of timeouts waiting for response from interface */ +static char response_byte; /* buffer to receive command/status response byte from overlapped read */ +static char lastcmd = '?'; + +/* pcr_attach - perform attach function to physical card reader */ + +static t_stat pcr_attach (UNIT *uptr, char *devname) +{ + DWORD thread_id; + t_stat rval; + + pcr_state = PCR_STATE_CLOSED; + sim_cancel(uptr); + cr_unit.COLUMN = -1; /* device is not currently cycling */ + + if ((rval = pcr_open_controller(devname)) != SCPE_OK) + return rval; + + if (hPickEvent == INVALID_HANDLE_VALUE) + hPickEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + if (hResetEvent == INVALID_HANDLE_VALUE) + hResetEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + + pcr_status = PCR_STATUS_HEMPTY; /* set default status: offline, no cards */ + pcr_state = PCR_STATE_IDLE; + pcr_done = FALSE; + cr_dsw = CR_DSW_1442_LAST_CARD | CR_DSW_1442_NOT_READY; + + set_active_cr_code(CODE_BINARY); /* force binary mode */ + + if (CreateThread(NULL, 0, pcr_thread, NULL, 0, &thread_id) == NULL) { + pcr_state = PCR_STATE_CLOSED; + CloseHandle(hpcr); + hpcr = INVALID_HANDLE_VALUE; + printf("Error creating card reader thread\n"); + return SCPE_IERR; + } + + SETBIT(uptr->flags, UNIT_PHYSICAL|UNIT_ATT); /* mark device as attached */ + uptr->filename = malloc(strlen(devname)+1); + strcpy(uptr->filename, devname); + + return SCPE_OK; +} + +/* pcr_open_controller - open the USB device's virtual COM port and configure the interface */ + +static t_stat pcr_open_controller (char *devname) +{ + DCB dcb; + COMMTIMEOUTS cto; + DWORD nerr; + + if (hpcr != INVALID_HANDLE_VALUE) + return SCPE_OK; + /* open the COM port */ + hpcr = CreateFile(devname, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (hpcr == INVALID_HANDLE_VALUE) + return SCPE_OPENERR; + + memset(&dcb, 0, sizeof(dcb)); /* set communications parameters */ + + dcb.DCBlength = sizeof(DCB); + dcb.BaudRate = CBR_115200; /* for the USB virtual com port, baud rate is irrelevant */ + dcb.fBinary = 1; + dcb.fParity = 0; + dcb.fOutxCtsFlow = 0; + dcb.fOutxDsrFlow = 0; + dcb.fDtrControl = DTR_CONTROL_ENABLE; + dcb.fDsrSensitivity = FALSE; + dcb.fTXContinueOnXoff = 0; + dcb.fOutX = 0; + dcb.fInX = 0; + dcb.fErrorChar = 0; + dcb.fNull = 0; + dcb.fRtsControl = RTS_CONTROL_ENABLE; + dcb.fAbortOnError = 0; + dcb.XonLim = 0; + dcb.XoffLim = 0; + dcb.ByteSize = 8; + dcb.Parity = NOPARITY; + dcb.StopBits = ONESTOPBIT; + dcb.XonChar = 0; + dcb.XoffChar = 0; + dcb.ErrorChar = 0; + dcb.EofChar = 0; + dcb.EvtChar = 0; + + if (! SetCommState(hpcr, &dcb)) { + CloseHandle(hpcr); + hpcr = INVALID_HANDLE_VALUE; + printf("Call to SetCommState failed\n"); + return SCPE_OPENERR; + } + + cto.ReadIntervalTimeout = 100; /* stop if 100 msec elapses between two received bytes */ + cto.ReadTotalTimeoutMultiplier = 0; /* no length sensitivity */ + cto.ReadTotalTimeoutConstant = 400; /* allow 400 msec for a read (reset command can take a while) */ + + cto.WriteTotalTimeoutMultiplier = 0; + cto.WriteTotalTimeoutConstant = 200; /* allow 200 msec for a write */ + + if (! SetCommTimeouts(hpcr, &cto)) { + CloseHandle(hpcr); + hpcr = INVALID_HANDLE_VALUE; + printf("Call to SetCommTimeouts failed\n"); + return SCPE_OPENERR; + } + + PurgeComm(hpcr, PURGE_TXABORT|PURGE_RXABORT|PURGE_TXCLEAR|PURGE_RXCLEAR); + ClearCommError(hpcr, &nerr, NULL); + + return SCPE_OK; +} + +/* pcr_detach - detach physical reader from CR device */ + +static t_stat pcr_detach (UNIT *uptr) +{ + if (cr_unit.flags & UNIT_ATT) { + CloseHandle(hpcr); /* close the COM port (this will lead to the thread closing) */ + hpcr = INVALID_HANDLE_VALUE; + pcr_state = PCR_STATE_CLOSED; + + free(uptr->filename); /* release the name copy */ + uptr->filename = NULL; + } + + CLRBIT(cr_unit.flags, UNIT_PHYSICAL|UNIT_ATT); /* drop the attach and physical bits */ + return SCPE_OK; +} + +/* pcr_xio_sense - perform XIO sense function on physical card reader */ + +static void pcr_xio_sense (int modify) +{ + if (modify & 0x01) { /* reset simulated interrupts */ + CLRBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE | CR_DSW_1442_PUNCH_RESPONSE); + CLRBIT(ILSW[0], ILSW_0_1442_CARD); + } + + if (modify & 0x02) { + CLRBIT(cr_dsw, CR_DSW_1442_OP_COMPLETE); + CLRBIT(ILSW[4], ILSW_4_1442_CARD); + } + + ACC = cr_dsw; /* DSW was set in real-time, just return the DSW */ + + if (cr_unit.flags & UNIT_DEBUG) + DEBUG_PRINT("#CR Sense %04x%s%s", cr_dsw, (modify & 1) ? " RESET0" : "", (modify & 2) ? " RESET4" : ""); +} + +/* report_error - issue detailed report of Windows IO error */ + +static void report_error (char *msg, DWORD err) +{ + char *lpMessageBuffer = NULL; + + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + GetLastError(), + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* The user default language */ + (LPTSTR) &lpMessageBuffer, + 0, + NULL ); + + printf("GetOverlappedResult failed, %s, %s\n", + msg, lpMessageBuffer); + + LocalFree(lpMessageBuffer); +} + +/* pcr_thread - thread to handle card reader interface communications */ + +static DWORD CALLBACK pcr_thread (LPVOID arg) +{ + DWORD event; + long nrcvd, nread, nwritten; + HANDLE objs[4]; + BOOL pick_queued = FALSE, reset_queued = FALSE; + + nwaits = 0; + + ZeroMemory(&ovRd, sizeof(ovRd)); + ZeroMemory(&ovWr, sizeof(ovWr)); + ovRd.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* create an event for async IO reads */ + ovWr.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* create an event for async IO writes */ + + objs[0] = ovRd.hEvent; + objs[1] = ovWr.hEvent; + objs[2] = hResetEvent; + objs[3] = hPickEvent; + + while (hpcr != INVALID_HANDLE_VALUE) { + if (pcr_state == PCR_STATE_IDLE) { + if (pick_queued) { + pcr_cmd('P'); + pick_queued = FALSE; + pcr_done = FALSE; + pcr_state = PCR_STATE_WAIT_PICK_CMD_RESPONSE; + } + else if (reset_queued) { + pcr_cmd('X'); + reset_queued = FALSE; + pcr_state = PCR_STATE_WAIT_CMD_RESPONSE; + } + } + + event = WaitForMultipleObjects(4, objs, FALSE, PCR_STATUS_MSEC); + + switch (event) { + case WAIT_OBJECT_0+0: /* read complete */ + ResetEvent(ovRd.hEvent); + if (! GetOverlappedResult(hpcr, &ovRd, &nrcvd, TRUE)) + report_error("PCR_Read", GetLastError()); + else if (cr_unit.flags & UNIT_DEBUG) + printf("PCR_Read: event, %d rcvd\n", nrcvd); + break; + + case WAIT_OBJECT_0+1: /* write complete */ + nwritten = 0; + ResetEvent(ovWr.hEvent); + if (! GetOverlappedResult(hpcr, &ovWr, &nwritten, TRUE)) + report_error("PCR_Write", GetLastError()); + else if (cr_unit.flags & UNIT_DEBUG) + printf("PCR_Write: event, %d sent\n", nwritten); + continue; + + case WAIT_OBJECT_0+2: /* reset request from simulator */ + reset_queued = TRUE; + pick_queued = FALSE; + continue; + + case WAIT_OBJECT_0+3: /* pick request from simulator */ + pick_queued = TRUE; + continue; + + case WAIT_TIMEOUT: + if (pcr_state == PCR_STATE_IDLE) { + pcr_state = PCR_STATE_WAIT_CMD_RESPONSE; + ovRd.Offset = ovRd.OffsetHigh = 0; + pcr_cmd('S'); + } + else if (pcr_state == PCR_STATE_WAIT_CMD_RESPONSE && ++nwaits >= 6) { + printf("Requesting status again!\n"); + ovRd.Offset = ovRd.OffsetHigh = 0; + pcr_cmd('S'); + } + continue; + + default: + printf("Unexpected pcr_wait result %08lx", event); + continue; + } + + /* We only get here if read event occurred */ + + switch (pcr_state) { + case PCR_STATE_IDLE: /* nothing expected from the interface */ + PurgeComm(hpcr, PURGE_RXCLEAR|PURGE_RXABORT); + break; + + case PCR_STATE_WAIT_CMD_RESPONSE: /* waiting for response from any command other than P */ + if (pcr_handle_status_byte(nrcvd)) + pcr_state = PCR_STATE_IDLE; + break; + + case PCR_STATE_WAIT_PICK_CMD_RESPONSE: /* waiting for response from P command */ + if (pcr_handle_status_byte(nrcvd)) { + pcr_cmd('\0'); /* queue a response read */ + pcr_state = PCR_STATE_WAIT_DATA_START; + } + break; + + case PCR_STATE_WAIT_DATA_START: /* waiting for leadin character from P command (= or !) */ + if (nrcvd <= 0) { /* (this could take an indefinite amount of time) */ + if (cr_unit.flags & UNIT_DEBUG) + printf("PCR: NO RESP YET\n"); + + continue; /* reader is not ready */ + } + + if (cr_unit.flags & UNIT_DEBUG) /* (this could take an indefinite amount of time) */ + printf("PCR: GOT %c\n", response_byte); + + switch (response_byte) { + case '=': /* = means pick in progress, 160 bytes of data will be coming */ + pcr_state = PCR_STATE_WAIT_DATA; + ovRd.Offset = ovRd.OffsetHigh = 0; + nread = 20; /* initiate a read */ + ReadFile(hpcr, ((char *) readstation), nread, &nrcvd, &ovRd); + break; + + case '!': /* ! means pick has been canceled, status will be coming next */ + pcr_state = PCR_STATE_WAIT_CMD_RESPONSE; + pcr_cmd('\0'); /* initiate read */ + break; + + default: /* anything else is a datacomm error, or something */ + /* indicate read check or something */ +/* pcr_state = PCR_STATE_IDLE; */ + break; + } + break; + + case PCR_STATE_WAIT_DATA: /* waiting for data from P command */ + if (cr_unit.flags & UNIT_DEBUG) + printf((nrcvd <= 0) ? "PCR: NO RESP!\n" : "PCR: GOT %d BYTES\n", nrcvd); + + if (nrcvd > 0) { + pcr_nleft -= nrcvd; + + begin_pcr_critical_section(); + pcr_nready += nrcvd; + end_pcr_critical_section(); + } + + if (pcr_nleft > 0) { + ovRd.Offset = ovRd.OffsetHigh = 0; + nread = min(pcr_nleft, 20); + ReadFile(hpcr, ((char *) readstation)+160-pcr_nleft, nread, &nrcvd, &ovRd); + } + else { + pcr_state = PCR_STATE_WAIT_PICK_FINAL_RESPONSE; + pcr_cmd('\0'); /* queue read */ + } + break; + + case PCR_STATE_WAIT_PICK_FINAL_RESPONSE: /* waiting for status byte after last of the card data */ + if (pcr_handle_status_byte(nrcvd)) { + readstate = STATION_READ; + pcr_state = PCR_STATE_IDLE; + pcr_done = TRUE; + } + break; + } + } + + CloseHandle(ovRd.hEvent); + CloseHandle(ovWr.hEvent); + + return 0; +} + +/* pcr_cmd - issue command byte to interface. Read of response byte is queued */ + +static void pcr_cmd (char cmd) +{ + long nwritten, nrcvd; + int status; + + if (cmd != '\0') { + if (cr_unit.flags & UNIT_DEBUG /* && (cmd != 'S' || cmd != lastcmd) */) + printf("PCR: SENT %c\n", cmd); + + lastcmd = cmd; + + ResetEvent(ovWr.hEvent); + ovWr.Offset = ovWr.OffsetHigh = 0; + status = WriteFile(hpcr, &cmd, 1, &nwritten, &ovWr); + if (status == 0 && GetLastError() != ERROR_IO_PENDING) + printf("Error initiating write in pcr_cmd\n"); + } + + ovRd.Offset = ovRd.OffsetHigh = 0; + status = ReadFile(hpcr, &response_byte, 1, &nrcvd, &ovRd); /* if no bytes ready, just return -- a later wait-event will catch it */ + if (status == 0 && GetLastError() != ERROR_IO_PENDING) + printf("Error initiating read in pcr_cmd\n"); + +/* if (cr_unit.flags & UNIT_DEBUG) + * if (nrcvd == 0) + * printf("PCR: NO RESPONSE\n"); + * else + * printf("PCR: RESPONSE %c\n", response_byte); */ + + nwaits = 0; +} + +/* pcr_handle_status_byte - handle completion of read of response byte */ + +static BOOL pcr_handle_status_byte (int nrcvd) +{ + static char prev_status = '?'; + BOOL show; + + if (nrcvd <= 0) + return FALSE; + + pcr_status = response_byte; /* save new status */ + + show = lastcmd != 'S' || pcr_status != prev_status; + + if ((cr_unit.flags & UNIT_DEBUG) && show) { + printf("PCR: status %c\n", pcr_status); + prev_status = pcr_status; + } + + pcr_set_dsw_from_status(FALSE); + + return TRUE; +} + +/* pcr_set_dsw_from_status - construct device status word from current physical reader status */ + +static void pcr_set_dsw_from_status (BOOL post_pick) +{ + /* set 1130 status word bits */ + CLRBIT(cr_dsw, CR_DSW_1442_LAST_CARD | CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY | CR_DSW_1442_ERROR_CHECK); + + if (pcr_status & PCR_STATUS_HEMPTY) + SETBIT(cr_dsw, CR_DSW_1442_LAST_CARD | CR_DSW_1442_NOT_READY); + + if (pcr_status & PCR_STATUS_ERROR) + SETBIT(cr_dsw, CR_DSW_1442_ERROR_CHECK); + + /* we have a problem -- ready doesn't come back up right away after a pick. */ + /* I think I'll fudge this and not set NOT_READY immediately after a pick */ + + if ((! post_pick) && ! (pcr_status & PCR_STATUS_READY)) + SETBIT(cr_dsw, CR_DSW_1442_NOT_READY); + + if (CURRENT_OP != OP_IDLE) + SETBIT(cr_dsw, CR_DSW_1442_BUSY | CR_DSW_1442_NOT_READY); +} + +static void pcr_xio_feedcycle (void) +{ + SET_OP(OP_FEEDING); + cr_unit.COLUMN = -1; + SetEvent(hPickEvent); + sim_activate(&cr_unit, cr_wait); /* keep checking frequently */ +} + +static void pcr_xio_startread (void) +{ + SET_OP(OP_READING); + cr_unit.COLUMN = -1; + pcr_nleft = 160; + pcr_nready = 0; + SetEvent(hPickEvent); + sim_activate(&cr_unit, cr_wait); /* keep checking frequently */ +} + +static void pcr_reset (void) +{ + pcr_status = PCR_STATUS_HEMPTY; /* set default status: offline, no cards */ + pcr_state = PCR_STATE_IDLE; + cr_dsw = CR_DSW_1442_LAST_CARD | CR_DSW_1442_NOT_READY; + + sim_cancel(&cr_unit); + + SetEvent(hResetEvent); +} + +/* pcr_trigger_interrupt_0 - simulate a read response interrupt so OS will read queued column data */ + +static void pcr_trigger_interrupt_0 (void) +{ + if (++cr_unit.COLUMN < 80) { + SETBIT(cr_dsw, CR_DSW_1442_READ_RESPONSE); + SETBIT(ILSW[0], ILSW_0_1442_CARD); + calc_ints(); + + begin_pcr_critical_section(); + pcr_nready -= 2; + end_pcr_critical_section(); + + if (cr_unit.flags & UNIT_DEBUG) + printf("SET IRQ0 col %d\n", cr_unit.COLUMN+1); + } +} + +static t_stat pcr_svc (UNIT *uptr) +{ + switch (CURRENT_OP) { + case OP_IDLE: + break; + + case OP_READING: + if (pcr_nready >= 2) { /* if there is a whole column buffered, simulate column interrupt/* pcr_trigger_interrupt_0 - simulate a read response interrupt so OS will read queued column data */ + + pcr_trigger_interrupt_0(); + sim_activate(&cr_unit, cr_wait); /* keep checking frequently */ + } + else if (pcr_done) { + pcr_done = FALSE; + cr_count++; + op_done(&cr_unit, TRUE); + pcr_set_dsw_from_status(TRUE); + } + else + sim_activate(&cr_unit, cr_wait); /* keep checking frequently */ + break; + + case OP_FEEDING: + if (pcr_done) { + cr_count++; + op_done(&cr_unit, FALSE); + pcr_set_dsw_from_status(TRUE); + } + else + sim_activate(&cr_unit, cr_wait); /* keep checking frequently */ + + break; + + case OP_PUNCHING: + return cr_svc(uptr); + } + + return SCPE_OK; +} + +static CRITICAL_SECTION pcr_critsect; + +static void begin_pcr_critical_section (void) +{ + static BOOL mustinit = TRUE; + + if (mustinit) { + InitializeCriticalSection(&pcr_critsect); + mustinit = FALSE; + } + + EnterCriticalSection(&pcr_critsect); +} + +static void end_pcr_critical_section (void) +{ + LeaveCriticalSection(&pcr_critsect); +} + +#endif + diff --git a/Ibm1130/ibm1130_defs.h b/Ibm1130/ibm1130_defs.h new file mode 100644 index 0000000..36963a6 --- /dev/null +++ b/Ibm1130/ibm1130_defs.h @@ -0,0 +1,311 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +/* ibm1130_defs.h: IBM-1130 simulator definitions + */ + +#include "sim_defs.h" /* main SIMH defns (include path should include .., or make a copy) */ +#include "sim_console.h" /* more SIMH defns (include path should include .., or make a copy) */ + +#include +#include +#include + +#if defined(VMS) + # include /* to pick up 'unlink' */ +#endif + +#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#define MAX(a,b) (((a) >= (b)) ? (a) : (b)) + +#ifndef _WIN32 + int strnicmp (const char *a, const char *b, int n); + int strcmpi (const char *a, const char *b); +#endif + +/* #define GUI_SUPPORT uncomment to compile the GUI extensions. It's defined in the windows ibm1130.mak makefile */ + +/* ------------------------------------------------------------------------ */ +/* Architectural constants */ + +#define MAXMEMSIZE (32768) /* 32Kwords */ +#define INIMEMSIZE (16384) /* 16Kwords */ +#define MEMSIZE (cpu_unit.capac) + +#define ILL_ADR_FLAG 0x40000000 /* an impossible 1130 address */ + +/* ------------------------------------------------------------------------ */ +/* Global state */ + +extern int cgi; /* TRUE if we are running as a CGI program */ +extern int cgiwritable; /* TRUE if we can write the disk images back to the image file in CGI mode */ +extern int sim_gui; + +extern uint16 M[]; /* core memory, up to 32Kwords (note: don't even think about trying 64K) */ +extern uint16 ILSW[]; /* interrupt level status words */ +extern int32 IAR; /* instruction address register */ +extern int32 prev_IAR; /* instruction address register at start of current instruction */ +extern int32 SAR, SBR; /* storage address/buffer registers */ +extern int32 OP, TAG, CCC; /* instruction decoded pieces */ +extern int32 CES; /* console entry switches */ +extern int32 ACC, EXT; /* accumulator and extension */ +extern int32 ARF; /* arithmetic factor register, a nonaddressable internal CPU register */ +extern int32 RUNMODE; /* processor run/step mode */ +extern int32 ipl; /* current interrupt level (-1 = not handling irq) */ +extern int32 iplpending; /* interrupted IPL's */ +extern int32 tbit; /* trace flag (causes level 5 IRQ after each instr) */ +extern int32 V, C; /* condition codes */ +extern int32 wait_state; /* wait state (waiting for an IRQ) */ +extern int32 wait_lamp; /* alternate indicator to light the wait lamp on the GUI */ +extern int32 int_req; /* sum of interrupt request levels active */ +extern int32 int_lamps; /* accumulated version of int_req - gives lamp persistence */ +extern int32 int_mask; /* current active interrupt mask (ipl sensitive) */ +extern int32 mem_mask; +extern int32 cpu_dsw; /* CPU device status word */ +extern int32 sim_int_char; /* interrupt character */ +extern int32 con_dsw; /* has program stop and int run bits */ +extern t_bool running; +extern t_bool power; +extern t_bool cgi; /* TRUE if we are running as a CGI program */ +extern t_bool cgiwritable; /* TRUE if we can write to the disk image file in CGI mode */ +extern t_stat reason; /* CPU execution loop control */ + +#define WAIT_OP 1 /* wait state causes: wait instruction, invalid instruction*/ +#define WAIT_INVALID_OP 2 + +#define MODE_SS 3 /* RUNMODE values. SS and SMC are not implemented in this simulator */ +#define MODE_SMC 2 +#define MODE_INT_RUN 1 +#define MODE_RUN 0 +#define MODE_SI -1 +#define MODE_DISP -2 +#define MODE_LOAD -3 + +/* ------------------------------------------------------------------------ */ +/* debugging */ +/* ------------------------------------------------------------------------ */ + +#define ENABLE_DEBUG_PRINT +#define ENABLE_DEBUG_TO_LOG + +#ifdef ENABLE_DEBUG_PRINT +# define DEBUG_PRINT debug_print +#else +# ifdef ENABLE_DEBUG_TO_LOG +# define DEBUG_PRINT trace_io +# else +# define DEBUG_PRINT if (0) debug_print +# endif +#endif + +void debug_print(char *fmt, ...); + +/* ------------------------------------------------------------------------ */ +/* memory IO routines */ + +int32 ReadW (int32 a); +void WriteW (int32 a, int32 d); + +/* ------------------------------------------------------------------------ */ +/* handy macros */ + +#define CLRBIT(v,b) ((v) &= ~(b)) +#define SETBIT(v,b) ((v) |= (b)) +#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b))) + +/* ------------------------------------------------------------------------ */ +/* Simulator stop codes */ + +#define STOP_WAIT 1 /* wait, no events */ +#define STOP_INVALID_INSTR 2 /* bad instruction */ +#define STOP_IBKPT 3 /* simulator breakpoint */ +#define STOP_INCOMPLETE 4 /* simulator coding not complete here */ +#define STOP_POWER_OFF 5 /* no power */ +#define STOP_DECK_BREAK 6 /* !BREAK in deck file */ +#define STOP_PHASE_BREAK 7 /* phase load break */ +#define STOP_CRASH 8 /* program has crashed badly */ +#define STOP_TIMED_OUT 9 /* simulation time limit exceeded */ +#define STOP_IMMEDIATE 10 /* simulator stop key pressed (immediate stop) */ +#define STOP_BREAK 11 /* simulator break key pressed */ +#define STOP_STEP 12 /* step count expired */ +#define STOP_OTHER 13 /* other reason, probably error returned by sim_process_event() */ + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +#define INT_REQ_5 0x01 /* bits for interrupt levels (ipl, iplpending, int_req, int_mask) */ +#define INT_REQ_4 0x02 +#define INT_REQ_3 0x04 +#define INT_REQ_2 0x08 +#define INT_REQ_1 0x10 +#define INT_REQ_0 0x20 + +#define XIO_UNUSED 0x00 /* XIO commands */ +#define XIO_WRITE 0x01 +#define XIO_READ 0x02 +#define XIO_SENSE_IRQ 0x03 +#define XIO_CONTROL 0x04 +#define XIO_INITW 0x05 +#define XIO_INITR 0x06 +#define XIO_SENSE_DEV 0x07 + +#define XIO_FAILED 0x20 /* fake function to record error */ + +/* ILSW bits - set by appropriate device whenever an interrupt is outstanding */ + +#define ILSW_0_1442_CARD 0x8000 /* ILSW 0 is not really defined on the 1130 */ + +#define ILSW_1_1132_PRINTER 0x8000 /* had these backwards! */ +#define ILSW_1_SCA 0x4000 + +#define ILSW_2_1131_DISK 0x8000 + +#define ILSW_2_2310_DRV_1 0x4000 +#define ILSW_2_2310_DRV_2 0x2000 +#define ILSW_2_2310_DRV_3 0x1000 +#define ILSW_2_2310_DRV_4 0x0800 /* can have 2310 or 2311 */ + +#define ILSW_2_2311_DRV_1_DISK_1 0x4000 +#define ILSW_2_2311_DRV_1_DISK_2 0x2000 +#define ILSW_2_2311_DRV_1_DISK_3 0x1000 +#define ILSW_2_2311_DRV_1_DISK_4 0x0800 + +#define ILSW_2_2311_DRV_1_DISK_5 0x0400 +#define ILSW_2_2311_DRV_2_DISK_1 0x0200 +#define ILSW_2_2311_DRV_2_DISK_2 0x0100 +#define ILSW_2_2311_DRV_2_DISK_3 0x0080 +#define ILSW_2_2311_DRV_2_DISK_4 0x0040 +#define ILSW_2_2311_DRV_2_DISK_5 0x0020 + +#define ILSW_2_SAC_BIT_11 0x0010 +#define ILSW_2_SAC_BIT_12 0x0008 +#define ILSW_2_SAC_BIT_13 0x0004 +#define ILSW_2_SAC_BIT_14 0x0002 +#define ILSW_2_SAC_BIT_15 0x0001 + +#define ILSW_3_1627_PLOTTER 0x8000 +#define ILSW_3_SAC_BIT_01 0x4000 +#define ILSW_3_SAC_BIT_02 0x2000 +#define ILSW_3_SAC_BIT_03 0x1000 +#define ILSW_3_2250_DISPLAY 0x0800 +#define ILSW_3_SYSTEM7 0x0800 +#define ILSW_3_SAC_BIT_05 0x0400 +#define ILSW_3_SAC_BIT_06 0x0200 +#define ILSW_3_SAC_BIT_07 0x0100 +#define ILSW_3_SAC_BIT_08 0x0080 +#define ILSW_3_SAC_BIT_09 0x0040 +#define ILSW_3_SAC_BIT_10 0x0020 +#define ILSW_3_SAC_BIT_11 0x0010 +#define ILSW_3_SAC_BIT_12 0x0008 +#define ILSW_3_SAC_BIT_13 0x0004 +#define ILSW_3_SAC_BIT_14 0x0002 +#define ILSW_3_SAC_BIT_15 0x0001 + +#define ILSW_4_1134_TAPE 0x8000 +#define ILSW_4_1055_TAPE 0x8000 +#define ILSW_4_CONSOLE 0x4000 +#define ILSW_4_1442_CARD 0x2000 +#define ILSW_4_2501_CARD 0x1000 +#define ILSW_4_1403_PRINTER 0x0800 +#define ILSW_4_1231_MARK 0x0400 +#define ILSW_4_SAC_BIT_06 0x0200 +#define ILSW_4_SAC_BIT_07 0x0100 +#define ILSW_4_SAC_BIT_08 0x0080 +#define ILSW_4_SAC_BIT_09 0x0040 +#define ILSW_4_SAC_BIT_10 0x0020 +#define ILSW_4_SAC_BIT_11 0x0010 +#define ILSW_4_T2741_TERMINAL 0x0010 /* APL\1130 nonstandard serial interface uses this bit */ +#define ILSW_4_SAC_BIT_12 0x0008 +#define ILSW_4_SAC_BIT_13 0x0004 +#define ILSW_4_SAC_BIT_14 0x0002 +#define ILSW_4_SAC_BIT_15 0x0001 + +#define ILSW_5_INT_RUN_PROGRAM_STOP 0x8000 /* this replaces both ILSW_5_INT_RUN and ILSW_5_PROGRAM_STOP */ +#define ILSW_5_SAC_BIT_01 0x4000 +#define ILSW_5_SAC_BIT_02 0x2000 +#define ILSW_5_SAC_BIT_03 0x1000 +#define ILSW_5_SAC_BIT_04 0x0800 +#define ILSW_5_SAC_BIT_05 0x0400 +#define ILSW_5_SAC_BIT_06 0x0200 +#define ILSW_5_SAC_BIT_07 0x0100 +#define ILSW_5_SAC_BIT_08 0x0080 +#define ILSW_5_SAC_BIT_09 0x0040 +#define ILSW_5_SAC_BIT_10 0x0020 +#define ILSW_5_SAC_BIT_11 0x0010 +#define ILSW_5_SAC_BIT_12 0x0008 +#define ILSW_5_SAC_BIT_13 0x0004 +#define ILSW_5_SAC_BIT_14 0x0002 +#define ILSW_5_SAC_BIT_15 0x0001 + +/* CPU DSW bits */ + +#define CPU_DSW_PROGRAM_STOP 0x8000 +#define CPU_DSW_INT_RUN 0x4000 + +/* prototypes: xio handlers */ + +void xio_1131_console (int32 addr, int32 func, int32 modify); /* console keyboard and printer */ +void xio_1142_card (int32 addr, int32 func, int32 modify); /* standard card reader/punch */ +void xio_1134_papertape (int32 addr, int32 func, int32 modify); /* paper tape reader/punch */ +void xio_disk (int32 addr, int32 func, int32 modify, int drv); /* internal CPU disk */ +void xio_1627_plotter (int32 addr, int32 func, int32 modify); /* XY plotter */ +void xio_1132_printer (int32 addr, int32 func, int32 modify); /* standard line printer */ +void xio_1131_switches (int32 addr, int32 func, int32 modify); /* console buttons & switches */ +void xio_1231_optical (int32 addr, int32 func, int32 modify); /* optical mark page reader */ +void xio_2501_card (int32 addr, int32 func, int32 modify); /* alternate high-speed card reader */ +void xio_sca (int32 addr, int32 func, int32 modify); /* synchronous communications adapter */ +void xio_system7 (int32 addr, int32 func, int32 modify); /* system/7 interprocessor IO link */ +void xio_1403_printer (int32 addr, int32 func, int32 modify); /* alternate high-speed printer */ +void xio_2250_display (int32 addr, int32 func, int32 modify); /* vector display processor */ +void xio_t2741_terminal (int32 addr, int32 func, int32 modify); /* IO selectric via nonstandard serial interface for APL */ +void xio_error (char *msg); + +void bail (char *msg); +t_stat load_cr_boot (int drv, int switches); +t_stat cr_boot (int unitno, DEVICE *dptr); +t_stat cr_rewind (void); +t_stat cr_detach (UNIT *uptr); +void calc_ints (void); /* recalculate interrupt bitmask */ +void trace_io (char *fmt, ...); /* debugging printout */ +void trace_both (char *fmt, ...); /* debugging printout */ +t_stat register_cmd (char *name, t_stat (*action)(int32 flag, char *ptr), int arg, char *help); +void scp_panic (char *msg); /* bail out of simulator */ +char *upcase(char *str); +void break_simulation (t_stat reason); /* let a device halt the simulation */ +char hollerith_to_ascii (uint16 hol); /* for debugging use only */ +t_bool gdu_active (void); +void remark_cmd (char *remark); +void stuff_cmd (char *cmd); +t_bool stuff_and_wait (char *cmd, int timeout, int delay); +void update_gui (t_bool force); +void sim_init (void); +t_stat register_cmd (char *name, t_stat (*action)(int32 flag, char *ptr), int arg, char *help); +t_stat basic_attach (UNIT *uptr, char *cptr); +char * quotefix (char * cptr); + +/* GUI interface routines */ +t_bool keyboard_is_busy (void); +void forms_check (int set); /* device notification to console lamp display */ +void print_check (int set); +void keyboard_selected (int select); +void disk_ready (int ready); +void disk_unlocked (int unlocked); +void gui_run(int running); +char *read_cmdline (char *ptr, int size, FILE *stream); + +#ifdef GUI_SUPPORT +# define GUI_BEGIN_CRITICAL_SECTION begin_critical_section(); +# define GUI_END_CRITICAL_SECTION end_critical_section(); + void begin_critical_section (void); + void end_critical_section (void); +#else +# define GUI_BEGIN_CRITICAL_SECTION +# define GUI_END_CRITICAL_SECTION +#endif diff --git a/Ibm1130/ibm1130_disk.c b/Ibm1130/ibm1130_disk.c new file mode 100644 index 0000000..52dc852 --- /dev/null +++ b/Ibm1130/ibm1130_disk.c @@ -0,0 +1,889 @@ +/* ibm1130_disk.c: IBM 1130 disk IO simulator + +NOTE - there is a problem with this code. The Device Status Word (DSW) is +computed from current conditions when requested by an XIO load status +command; the value of DSW available to the simulator's examine & save +commands may NOT be accurate. This should probably be fixed. + + Based on the SIMH package written by Robert M Supnik + + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * Revision History + * 05-dec-06 Added cgiwritable mode + * + * 19-Dec-05 We no longer issue an operation complete interrupt if an INITR, INITW + * or CONTROL operation is attemped on a drive that is not online. DATA_ERROR + * is now only indicated in the DSW when + * + * 02-Nov-04 Addes -s option to boot to leave switches alone. + * 15-jun-03 moved actual read on XIO read to end of time interval, + * as the APL boot card required 2 instructions to run between the + * time read was initiated and the time the data was read (a jump and a wait) + * + * 01-sep-02 corrected treatment of -m and -r flags in dsk_attach + * in cgi mode, so that file is opened readonly but emulated + * disk is writable. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + */ + +#include "ibm1130_defs.h" +#include "memory.h" + +#define TRACE_DMS_IO /* define to enable debug of DMS phase IO */ + +#ifdef TRACE_DMS_IO +extern int32 sim_switches; +extern int32 sim_quiet; +static int trace_dms = 0; +static void tracesector (int iswrite, int nwords, int addr, int sector); +static t_stat where_cmd (int flag, char *ptr); +static t_stat phdebug_cmd (int flag, char *ptr); +static t_stat fdump_cmd (int flags, char *cptr); +static void enable_dms_tracing (int newsetting); +#endif + +/* Constants */ + +#define DSK_NUMWD 321 /* words/sector */ +#define DSK_NUMSC 4 /* sectors/surface */ +#define DSK_NUMSF 2 /* surfaces/cylinder */ +#define DSK_NUMCY 203 /* cylinders/drive */ +#define DSK_NUMTR (DSK_NUMCY * DSK_NUMSF) /* tracks/drive */ +#define DSK_NUMDR 5 /* drives/controller */ +#define DSK_SIZE (DSK_NUMCY * DSK_NUMSF * DSK_NUMSC * DSK_NUMWD) /* words/drive */ + +#define UNIT_V_RONLY (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_OPERR (UNIT_V_UF + 1) /* operation error flag */ +#define UNIT_V_HARDERR (UNIT_V_UF + 2) /* hard error flag (reset on power down) */ +#define UNIT_RONLY (1u << UNIT_V_RONLY) +#define UNIT_OPERR (1u << UNIT_V_OPERR) +#define UNIT_HARDERR (1u << UNIT_V_HARDERR) + +#define MEM_MAPPED(uptr) (uptr->flags & UNIT_BUF) /* disk buffered in memory */ + +#define IO_NONE 0 /* last operation, used to ensure fseek between read and write */ +#define IO_READ 1 +#define IO_WRITE 2 + +#define DSK_DSW_DATA_ERROR 0x8000 /* device status word bits */ +#define DSK_DSW_OP_COMPLETE 0x4000 +#define DSK_DSW_NOT_READY 0x2000 +#define DSK_DSW_DISK_BUSY 0x1000 +#define DSK_DSW_CARRIAGE_HOME 0x0800 +#define DSK_DSW_SECTOR_MASK 0x0003 + + /* device status words */ +static int16 dsk_dsw[DSK_NUMDR] = {DSK_DSW_NOT_READY, DSK_DSW_NOT_READY, DSK_DSW_NOT_READY, DSK_DSW_NOT_READY, DSK_DSW_NOT_READY}; +static int16 dsk_sec[DSK_NUMDR] = {0}; /* next-sector-up */ +static char dsk_lastio[DSK_NUMDR]; /* last stdio operation: IO_READ or IO_WRITE */ +int32 dsk_swait = 50; /* seek time -- see how short a delay we can get away with */ +int32 dsk_rwait = 50; /* rotate time */ +static t_bool raw_disk_debug = FALSE; + +static t_stat dsk_svc (UNIT *uptr); +static t_stat dsk_reset (DEVICE *dptr); +static t_stat dsk_attach (UNIT *uptr, char *cptr); +static t_stat dsk_detach (UNIT *uptr); +static t_stat dsk_boot (int unitno, DEVICE *dptr); + +static void diskfail (UNIT *uptr, int dswflag, int unitflag, t_bool do_interrupt); + +/* DSK data structures + + dsk_dev disk device descriptor + dsk_unit unit descriptor + dsk_reg register list +*/ + +UNIT dsk_unit[] = { + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) }, + { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, DSK_SIZE) } +}; + +#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT) + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* current function */ + +REG dsk_reg[] = { + { HRDATA (DSKDSW0, dsk_dsw[0], 16) }, + { HRDATA (DSKDSW1, dsk_dsw[1], 16) }, + { HRDATA (DSKDSW2, dsk_dsw[2], 16) }, + { HRDATA (DSKDSW3, dsk_dsw[3], 16) }, + { HRDATA (DSKDSW4, dsk_dsw[4], 16) }, + { DRDATA (STIME, dsk_swait, 24), PV_LEFT }, + { DRDATA (RTIME, dsk_rwait, 24), PV_LEFT }, + { NULL } }; + +MTAB dsk_mod[] = { + { UNIT_RONLY, 0, "write enabled", "ENABLED", NULL }, + { UNIT_RONLY, UNIT_RONLY, "write locked", "LOCKED", NULL }, + { 0 } }; + +DEVICE dsk_dev = { + "DSK", dsk_unit, dsk_reg, dsk_mod, + DSK_NUMDR, 16, 16, 1, 16, 16, + NULL, NULL, &dsk_reset, + dsk_boot, dsk_attach, dsk_detach}; + +static int32 dsk_ilswbit[DSK_NUMDR] = { /* interrupt level status word bits for the drives */ + ILSW_2_1131_DISK, + ILSW_2_2310_DRV_1, + ILSW_2_2310_DRV_2, + ILSW_2_2310_DRV_3, + ILSW_2_2310_DRV_4, +}; + +static int32 dsk_ilswlevel[DSK_NUMDR] = +{ + 2, /* interrupt levels for the drives */ + 2, 2, 2, 2 +}; + +typedef enum {DSK_FUNC_IDLE, DSK_FUNC_READ, DSK_FUNC_VERIFY, DSK_FUNC_WRITE, DSK_FUNC_SEEK, DSK_FUNC_FAILED} DSK_FUNC; + +static struct tag_dsk_action { /* stores data needed for pending IO activity */ + int32 io_address; + uint32 io_filepos; + int io_nwords; + int io_sector; +} dsk_action[DSK_NUMDR]; + +/* xio_disk - XIO command interpreter for the disk drives */ +/* + * device status word: + * + * 0 data error, occurs when: + * 1. A modulo 4 error is detected during a read, read-check, or write operation. + * 2. The disk storage is in a read or write mode at the leading edge of a sector pulse. + * 3. A seek-incomplete signal is received from the 2311. + * 4. A write select error has occurred in the disk storage drive. + * 5. The power unsafe latch is set in the attachment. + * Conditions 1, 2, and 3 are turned off by a sense device command with modifier bit 15 + * set to 1. Conditions 4 and 5 are turned off by powering the drive off and back on. + * 1 operation complete + * 2 not ready, occurs when disk not ready or busy or disabled or off-line or + * power unsafe latch set. Also included in the disk not ready is the write select error, + * which can be a result of power unsafe or write select. + * 3 disk busy + * 4 carriage home (on cyl 0) + * 15-16: number of next sector spinning into position. + */ + +extern void void_backtrace (int afrom, int ato); + +void xio_disk (int32 iocc_addr, int32 func, int32 modify, int drv) +{ + int i, rev, nsteps, newcyl, sec, nwords; + uint32 newpos; /* changed from t_addr to uint32 in anticipation of simh 64-bit development */ + char msg[80]; + UNIT *uptr = dsk_unit+drv; + int16 buf[DSK_NUMWD]; + + if (! BETWEEN(drv, 0, DSK_NUMDR-1)) { /* hmmm, invalid drive */ + if (func != XIO_SENSE_DEV) { /* tried to use it, too */ + /* just do nothing, as if the controller isn't there. NAMCRA at N0116300 tests for drives by attempting reads + sprintf(msg, "Op %x on invalid drive number %d", func, drv); + xio_error(msg); + */ + } + return; + } + + CLRBIT(uptr->flags, UNIT_OPERR); /* clear pending error flag from previous op, if any */ + + switch (func) { + case XIO_INITR: + if (! IS_ONLINE(uptr)) { /* disk is offline */ + diskfail(uptr, 0, 0, FALSE); + break; + } + + sim_cancel(uptr); /* cancel any pending ops */ + dsk_dsw[drv] |= DSK_DSW_DISK_BUSY; /* and mark the disk as busy */ + + nwords = M[iocc_addr++ & mem_mask]; /* get word count w/o upsetting SAR/SBR */ + + if (nwords == 0) /* this is bad -- on real 1130, this locks up disk controller ! */ + break; + + if (! BETWEEN(nwords, 1, DSK_NUMWD)) { /* count bad */ + SETBIT(uptr->flags, UNIT_OPERR); /* set data error DSW bit when op complete */ + nwords = DSK_NUMWD; /* limit xfer to proper sector size */ + } + + sec = modify & 0x07; /* get sector on cylinder */ + + if ((modify & 0x0080) == 0) { /* it's a real read if it's not a read check */ + /* ah. We have a problem. The APL boot card counts on there being time for at least one + * more instruction to execute between the XIO read and the time the data starts loading + * into core. So, we have to defer the actual read operation a bit. Might as well wait + * until it's time to issue the operation complete interrupt. This means saving the + * IO information, then performing the actual read in dsk_svc. + */ + + newpos = (uptr->CYL*DSK_NUMSC*DSK_NUMSF + sec)*2*DSK_NUMWD; + + dsk_action[drv].io_address = iocc_addr; + dsk_action[drv].io_nwords = nwords; + dsk_action[drv].io_sector = sec; + dsk_action[drv].io_filepos = newpos; + + uptr->FUNC = DSK_FUNC_READ; + } + else { + trace_io("* DSK%d verify %d.%d (%x)", drv, uptr->CYL, sec, uptr->CYL*8 + sec); + + if (raw_disk_debug) + printf("* DSK%d verify %d.%d (%x)", drv, uptr->CYL, sec, uptr->CYL*8 + sec); + + uptr->FUNC = DSK_FUNC_VERIFY; + } + + sim_activate(uptr, dsk_rwait); + break; + + case XIO_INITW: + if (! IS_ONLINE(uptr)) { /* disk is offline */ + diskfail(uptr, 0, 0, FALSE); + break; + } + + if (uptr->flags & UNIT_RONLY) { /* oops, write to RO disk? permanent error until disk is powered off/on */ + diskfail(uptr, DSK_DSW_DATA_ERROR, UNIT_HARDERR, FALSE); + break; + } + + sim_cancel(uptr); /* cancel any pending ops */ + dsk_dsw[drv] |= DSK_DSW_DISK_BUSY; /* and mark drive as busy */ + + nwords = M[iocc_addr++ & mem_mask]; /* get word count w/o upsetting SAR/SBR */ + + if (nwords == 0) /* this is bad -- locks up disk controller ! */ + break; + + if (! BETWEEN(nwords, 1, DSK_NUMWD)) { /* count bad */ + SETBIT(uptr->flags, UNIT_OPERR); /* set data error DSW bit when op complete */ + nwords = DSK_NUMWD; /* limit xfer to proper sector size */ + } + + sec = modify & 0x07; /* get sector on cylinder */ + newpos = (uptr->CYL*DSK_NUMSC*DSK_NUMSF + sec)*2*DSK_NUMWD; + + trace_io("* DSK%d wrote %d words from M[%04x-%04x] to %d.%d (%x, %x)", drv, nwords, iocc_addr & mem_mask, (iocc_addr + nwords - 1) & mem_mask, uptr->CYL, sec, uptr->CYL*8 + sec, newpos); + + if (raw_disk_debug) + printf("* DSK%d XIO @ %04x wrote %d words from M[%04x-%04x] to %d.%d (%x, %x)\n", drv, prev_IAR, nwords, iocc_addr & mem_mask, (iocc_addr + nwords - 1) & mem_mask, uptr->CYL, sec, uptr->CYL*8 + sec, newpos); + +#ifdef TRACE_DMS_IO + if (trace_dms) + tracesector(1, nwords, iocc_addr & mem_mask, uptr->CYL*8 + sec); +#endif + for (i = 0; i < nwords; i++) + buf[i] = M[iocc_addr++ & mem_mask]; + + for (; i < DSK_NUMWD; i++) /* rest of sector gets zeroed */ + buf[i] = 0; + + i = uptr->CYL*8 + sec; + if (buf[0] != i) + printf("*DSK writing bad sector#\n"); + + if (MEM_MAPPED(uptr)) { + memcpy((char *) uptr->filebuf + newpos, buf, 2*DSK_NUMWD); + uptr->hwmark = newpos + 2*DSK_NUMWD; + } + else { + if (uptr->pos != newpos || dsk_lastio[drv] != IO_WRITE) { + fseek(uptr->fileref, newpos, SEEK_SET); + dsk_lastio[drv] = IO_WRITE; + } + + fxwrite(buf, 2, DSK_NUMWD, uptr->fileref); + uptr->pos = newpos + 2*DSK_NUMWD; + } + + uptr->FUNC = DSK_FUNC_WRITE; + sim_activate(uptr, dsk_rwait); + break; + + case XIO_CONTROL: /* step fwd/rev */ + if (! IS_ONLINE(uptr)) { + diskfail(uptr, 0, 0, FALSE); + break; + } + + sim_cancel(uptr); + + rev = modify & 4; + nsteps = iocc_addr & 0x00FF; + if (nsteps == 0) /* 0 steps does not cause op complete interrupt */ + break; + + newcyl = uptr->CYL + (rev ? (-nsteps) : nsteps); + if (newcyl < 0) + newcyl = 0; + else if (newcyl >= DSK_NUMCY) + newcyl = DSK_NUMCY-1; + + uptr->FUNC = DSK_FUNC_SEEK; + uptr->CYL = newcyl; + sim_activate(uptr, dsk_swait); /* schedule interrupt */ + + dsk_dsw[drv] |= DSK_DSW_DISK_BUSY; + trace_io("* DSK%d at cyl %d", drv, newcyl); + break; + + case XIO_SENSE_DEV: + CLRBIT(dsk_dsw[drv], DSK_DSW_CARRIAGE_HOME|DSK_DSW_NOT_READY); + + if ((uptr->flags & UNIT_HARDERR) || (dsk_dsw[drv] & DSK_DSW_DISK_BUSY) || ! IS_ONLINE(uptr)) + SETBIT(dsk_dsw[drv], DSK_DSW_NOT_READY); + else if (uptr->CYL <= 0) { + SETBIT(dsk_dsw[drv], DSK_DSW_CARRIAGE_HOME); + uptr->CYL = 0; + } + + dsk_sec[drv] = (int16) ((dsk_sec[drv] + 1) % 4); /* advance the "next sector" count every time */ + ACC = dsk_dsw[drv] | dsk_sec[drv]; + + if (modify & 0x01) { /* reset interrupts */ + CLRBIT(dsk_dsw[drv], DSK_DSW_OP_COMPLETE|DSK_DSW_DATA_ERROR); + CLRBIT(ILSW[dsk_ilswlevel[drv]], dsk_ilswbit[drv]); + } + break; + + default: + sprintf(msg, "Invalid disk XIO function %x", func); + xio_error(msg); + } +} + +/* diskfail - schedule an operation complete that sets the error bit */ + +static void diskfail (UNIT *uptr, int dswflag, int unitflag, t_bool do_interrupt) +{ + int drv = uptr - dsk_unit; + + sim_cancel(uptr); /* cancel any pending ops */ + SETBIT(dsk_dsw[drv], dswflag); /* set any specified DSW bits */ + SETBIT(uptr->flags, unitflag); /* set any specified unit flag bits */ + uptr->FUNC = DSK_FUNC_FAILED; /* tell svc routine why it failed */ + + if (do_interrupt) + sim_activate(uptr, 1); /* schedule an immediate op complete interrupt */ +} + +t_stat dsk_svc (UNIT *uptr) +{ + int drv = uptr - dsk_unit, i, nwords, sec; + int16 buf[DSK_NUMWD]; + uint32 newpos; /* changed from t_addr to uint32 in anticipation of simh 64-bit development */ + int32 iocc_addr; + + if (uptr->FUNC == DSK_FUNC_IDLE) /* service function called with no activity? not good, but ignore */ + return SCPE_OK; + + CLRBIT(dsk_dsw[drv], DSK_DSW_DISK_BUSY); /* activate operation complete interrupt */ + SETBIT(dsk_dsw[drv], DSK_DSW_OP_COMPLETE); + + if (uptr->flags & (UNIT_OPERR|UNIT_HARDERR)) { /* word count error or data error */ + SETBIT(dsk_dsw[drv], DSK_DSW_DATA_ERROR); + CLRBIT(uptr->flags, UNIT_OPERR); /* soft error is one time occurrence; don't clear hard error */ + } + /* schedule interrupt */ + SETBIT(ILSW[dsk_ilswlevel[drv]], dsk_ilswbit[drv]); + + switch (uptr->FUNC) { /* take care of business */ + case DSK_FUNC_IDLE: + case DSK_FUNC_VERIFY: + case DSK_FUNC_WRITE: + case DSK_FUNC_SEEK: + case DSK_FUNC_FAILED: + break; + + case DSK_FUNC_READ: /* actually read the data into core */ + iocc_addr = dsk_action[drv].io_address; /* recover saved parameters */ + nwords = dsk_action[drv].io_nwords; + newpos = dsk_action[drv].io_filepos; + sec = dsk_action[drv].io_sector; + + if (MEM_MAPPED(uptr)) { + memcpy(buf, (char *) uptr->filebuf + newpos, 2*DSK_NUMWD); + } + else { + if (uptr->pos != newpos || dsk_lastio[drv] != IO_READ) { + fseek(uptr->fileref, newpos, SEEK_SET); + dsk_lastio[drv] = IO_READ; + uptr->pos = newpos; + } + fxread(buf, 2, DSK_NUMWD, uptr->fileref); /* read whole sector so we're in position for next read */ + uptr->pos = newpos + 2*DSK_NUMWD; + } + + void_backtrace(iocc_addr, iocc_addr + nwords - 1); /* mark prev instruction as altered */ + + trace_io("* DSK%d read %d words from %d.%d (%x, %x) to M[%04x-%04x]", drv, nwords, uptr->CYL, sec, uptr->CYL*8 + sec, newpos, iocc_addr & mem_mask, + (iocc_addr + nwords - 1) & mem_mask); + + /* this will help debug the monitor by letting me watch phase loading */ + if (raw_disk_debug) + printf("* DSK%d XIO @ %04x read %d words from %d.%d (%x, %x) to M[%04x-%04x]\n", drv, prev_IAR, nwords, uptr->CYL, sec, uptr->CYL*8 + sec, newpos, iocc_addr & mem_mask, + (iocc_addr + nwords - 1) & mem_mask); + + i = uptr->CYL*8 + sec; + if (buf[0] != i) + printf("*DSK read bad sector #\n"); + + for (i = 0; i < nwords; i++) + M[(iocc_addr+i) & mem_mask] = buf[i]; + +#ifdef TRACE_DMS_IO + if (trace_dms) + tracesector(0, nwords, iocc_addr & mem_mask, uptr->CYL*8 + sec); +#endif + break; + + default: + fprintf(stderr, "Unexpected FUNC %x in dsk_svc(%d)\n", uptr->FUNC, drv); + break; + + } + + uptr->FUNC = DSK_FUNC_IDLE; /* we're done with this operation */ + + return SCPE_OK; +} + +t_stat dsk_reset (DEVICE *dptr) +{ + int drv; + UNIT *uptr; + +#ifdef TRACE_DMS_IO + /* add the WHERE command. It finds the phase that was loaded at given address and indicates */ + /* the offset in the phase */ + register_cmd("WHERE", &where_cmd, 0, "w{here} address find phase and offset of an address\n"); + register_cmd("PHDEBUG", &phdebug_cmd, 0, "ph{debug} off|phlo phhi break on phase load\n"); + register_cmd("FDUMP", &fdump_cmd, 0, NULL); +#endif + + for (drv = 0, uptr = dsk_dev.units; drv < DSK_NUMDR; drv++, uptr++) { + sim_cancel(uptr); + + CLRBIT(ILSW[2], dsk_ilswbit[drv]); + CLRBIT(uptr->flags, UNIT_OPERR|UNIT_HARDERR); + + uptr->CYL = 0; + uptr->FUNC = DSK_FUNC_IDLE; + dsk_dsw[drv] = (int16) ((uptr->flags & UNIT_ATT) ? DSK_DSW_CARRIAGE_HOME : 0); + } + + calc_ints(); + + return SCPE_OK; +} + +static t_stat dsk_attach (UNIT *uptr, char *cptr) +{ + int drv = uptr - dsk_unit; + t_stat rval; + + sim_cancel(uptr); /* cancel current IO */ + dsk_lastio[drv] = IO_NONE; + + if (uptr->flags & UNIT_ATT) /* dismount current disk */ + if ((rval = dsk_detach(uptr)) != SCPE_OK) + return rval; + + uptr->CYL = 0; /* reset the device */ + uptr->FUNC = DSK_FUNC_IDLE; + dsk_dsw[drv] = DSK_DSW_CARRIAGE_HOME; + + CLRBIT(uptr->flags, UNIT_RO|UNIT_ROABLE|UNIT_BUFABLE|UNIT_BUF|UNIT_RONLY|UNIT_OPERR|UNIT_HARDERR); + CLRBIT(ILSW[2], dsk_ilswbit[drv]); + calc_ints(); + + if (sim_switches & SWMASK('M')) /* if memory mode (e.g. for CGI), buffer the file */ + SETBIT(uptr->flags, UNIT_BUFABLE|UNIT_MUSTBUF); + + if (sim_switches & SWMASK('R')) /* read lock mode */ + SETBIT(uptr->flags, UNIT_RO|UNIT_ROABLE|UNIT_RONLY); + + if (cgi && (sim_switches & SWMASK('M')) && ! cgiwritable) { /* if cgi and memory mode, but writable option not specified */ + sim_switches |= SWMASK('R'); /* have attach_unit open file in readonly mode */ + SETBIT(uptr->flags, UNIT_ROABLE); /* but don't set the UNIT_RONLY flag so DMS can write to the buffered image */ + } + + if ((rval = attach_unit(uptr, quotefix(cptr))) != SCPE_OK) { /* mount new disk */ + SETBIT(dsk_dsw[drv], DSK_DSW_NOT_READY); + return rval; + } + + if (drv == 0) { + disk_ready(TRUE); + disk_unlocked(FALSE); + } + + enable_dms_tracing(sim_switches & SWMASK('D')); + raw_disk_debug = sim_switches & SWMASK('G'); + + return SCPE_OK; +} + +static t_stat dsk_detach (UNIT *uptr) +{ + t_stat rval; + int drv = uptr - dsk_unit; + + sim_cancel(uptr); + + if ((rval = detach_unit(uptr)) != SCPE_OK) + return rval; + + CLRBIT(ILSW[2], dsk_ilswbit[drv]); + CLRBIT(uptr->flags, UNIT_OPERR|UNIT_HARDERR); + calc_ints(); + + uptr->CYL = 0; + uptr->FUNC = DSK_FUNC_IDLE; + dsk_dsw[drv] = DSK_DSW_NOT_READY; + + if (drv == 0) { + disk_unlocked(TRUE); + disk_ready(FALSE); + } + + return SCPE_OK; +} + +/* boot routine - if they type BOOT DSK, load the standard boot card. */ + +static t_stat dsk_boot (int unitno, DEVICE *dptr) +{ + t_stat rval; + + if ((rval = reset_all(0)) != SCPE_OK) + return rval; + + return load_cr_boot(unitno, sim_switches); +} + +#ifdef TRACE_DMS_IO + +static struct { + int phid; + char *name; +} phase[] = { +# include "dmsr2v12phases.h" + 0xFFFF, "" +}; + +#pragma pack(2) +#define MAXSLET ((3*320)/4) +struct tag_slet { + int16 phid; + int16 addr; + int16 nwords; + int16 sector; +} slet[MAXSLET] = { +# include "dmsr2v12slet.h" /* without RPG, use this info until overwritten by actual data from disk */ +}; + +#pragma pack() + +#define MAXMSEG 100 +struct tag_mseg { + char *name; + int addr, offset, len, phid; +} mseg[MAXMSEG]; +int nseg = 0; + +static void enable_dms_tracing (int newsetting) +{ + nseg = 0; /* clear the segment map */ + + if ((newsetting && trace_dms) || ! (newsetting || trace_dms)) + return; + + trace_dms = newsetting; + if (! sim_quiet) + printf("DMS disk tracing is now %sabled\n", trace_dms ? "en" : "dis"); +} + +char * saywhere (int addr) +{ + int i; + static char buf[150]; + + for (i = 0; i < nseg; i++) { + if (addr >= mseg[i].addr && addr < (mseg[i].addr+mseg[i].len)) { + sprintf(buf, "/%04x = /%04x + /%x in ", addr, mseg[i].addr - mseg[i].offset, addr-mseg[i].addr + mseg[i].offset); + if (mseg[i].phid > 0) + sprintf(buf+strlen(buf), "phase %02x (%s)", mseg[i].phid, mseg[i].name); + else + sprintf(buf+strlen(buf), "%s", mseg[i].name); + + return buf; + } + } + return NULL; +} + +static int phdebug_lo = -1, phdebug_hi = -1; + +static t_stat phdebug_cmd (int flag, char *ptr) +{ + int val1, val2; + + if (strcmpi(ptr, "off") == 0) + phdebug_lo = phdebug_hi = -1; + else { + switch(sscanf(ptr, "%x%x", &val1, &val2)) { + case 1: + phdebug_lo = phdebug_hi = val1; + enable_dms_tracing(TRUE); + break; + + case 2: + phdebug_lo = val1; + phdebug_hi = val2; + enable_dms_tracing(TRUE); + break; + + default: + printf("Usage: phdebug off | phdebug phfrom [phto]\n"); + break; + } + } + return SCPE_OK; +} + +static t_stat where_cmd (int flag, char *ptr) +{ + int addr; + char *where; + + if (! trace_dms) { + printf("Tracing is disabled. To enable, attach disk with -d switch\n"); + return SCPE_OK; + } + + if (sscanf(ptr, "%x", &addr) != 1) + return SCPE_ARG; + + if ((where = saywhere(addr)) == NULL) + printf("/%04x not found\n", addr); + else + printf("%s\n", where); + + return SCPE_OK; +} + +/* savesector - save info on a sector just read. THIS IS NOT YET TESTED */ + +static void addseg (int i) +{ + if (! trace_dms) + return; + + if (nseg >= MAXMSEG) { + printf("(Memory map full, disabling tracing)\n"); + trace_dms = 0; + nseg = -1; + return; + } + memcpy(mseg+i+1, mseg+i, (nseg-i)*sizeof(mseg[0])); + nseg++; +} + +static void delseg (int i) +{ + if (! trace_dms) + return; + + if (nseg > 0) { + nseg--; + memcpy(mseg+i, mseg+i+1, (nseg-i)*sizeof(mseg[0])); + } +} + +static void savesector (int addr, int offset, int len, int phid, char *name) +{ + int i; + + if (! trace_dms) + return; + + addr++; /* first word is sector address, so account for that */ + len--; + + for (i = 0; i < nseg; i++) { + if (addr >= (mseg[i].addr+mseg[i].len)) /* entirely after this entry */ + continue; + + if (mseg[i].addr < addr) { /* old one starts before this. split it */ + addseg(i); + mseg[i].len = addr-mseg[i].addr; + i++; + mseg[i].addr = addr; + mseg[i].len -= mseg[i-1].len; + } + + break; + } + + addseg(i); /* add new segment. Old one ends up after this */ + + if (i >= MAXMSEG) + return; + + mseg[i].addr = addr; + mseg[i].offset = offset; + mseg[i].phid = phid; + mseg[i].len = len; + mseg[i].name = name; + + i++; /* delete any segments completely covered */ + + while (i < nseg && (mseg[i].addr+mseg[i].len) <= (addr+len)) + delseg(i); + + if (i < nseg && mseg[i].addr < (addr+len)) { /* old one extends past this. Retain the end */ + mseg[i].len = (mseg[i].addr+mseg[i].len) - (addr+len); + mseg[i].addr = addr+len; + } +} + +static void tracesector (int iswrite, int nwords, int addr, int sector) +{ + int i, phid = 0, offset = 0; + char *name = NULL; + + if (nwords < 3 || ! trace_dms) + return; + + switch (sector) { /* explicitly known sector name */ + case 0: name = "ID/COLD START"; break; + case 1: name = "DCOM"; break; + case 2: name = "RESIDENT IMAGE"; break; + case 3: + case 4: + case 5: name = "SLET"; /* save just-read or written SLET info */ + memmove(&slet[(320/4)*(sector-3)], &M[addr+1], nwords*2); + break; + case 6: name = "RELOAD TABLE"; break; + case 7: name = "PAGE HEADER"; break; + } + + printf("* %04x: %3d /%04x %c %3d.%d ", + prev_IAR, nwords, addr, iswrite ? 'W' : 'R', sector/8, sector%8); + + if (name == NULL) { /* look up sector in SLET */ + for (i = 0; i < MAXSLET; i++) { + if (slet[i].phid == 0) /* not found */ + goto done; + else if (slet[i].sector > sector) { + if (--i >= 0) { + if (sector >= slet[i].sector && sector <= (slet[i].sector + slet[i].nwords/320)) { + phid = slet[i].phid; + offset = (sector-slet[i].sector)*320; + break; + } + } + goto done; + } + if (slet[i].sector == sector) { + phid = slet[i].phid; /* we found the starting sector */ + break; + } + } + + if (i >= MAXSLET) /* was not found */ + goto done; + + name = "?"; + for (i = sizeof(phase)/sizeof(phase[0]); --i >= 0; ) { + if (phase[i].phid == phid) { /* look up name */ + name = phase[i].name; + break; + } + } + printf("%02x %s", phid, name); + } + else + printf("%s", name); + +done: + putchar('\n'); + + if (phid >= phdebug_lo && phid <= phdebug_hi && offset == 0) + break_simulation(STOP_PHASE_BREAK); /* break on read of first sector of indicated phases */ + + if (name != NULL && *name != '?' && ! iswrite) + savesector(addr, offset, nwords, phid, name); +} + +static t_stat fdump_cmd (int flags, char *cptr) +{ + int addr = 0x7a24; /* address of next statement */ + int sofst = 0x7a26, symaddr; + int cword, nwords, stype, has_stnum, strel = 1, laststno = 0; + + addr = M[addr & mem_mask] & mem_mask; /* get address of first statement */ + sofst = M[sofst & mem_mask] & mem_mask ; /* get address of symbol table */ + + for (;;) { + cword = M[addr]; + nwords = (cword >> 2) & 0x01FF; + stype = (cword >> 1) & 0x7C00; + has_stnum = (cword & 1); + + if (has_stnum) { + laststno++; + strel = 0; + } + + printf("/%04x [%4d +%3d] %3d - %04x", addr, laststno, strel, nwords, stype); + + if (has_stnum) { + addr++; + nwords--; + symaddr = sofst - (M[addr] & 0x7FF)*3 + 3; + printf(" [%04x %04x %04x]", M[symaddr], M[symaddr+1], M[symaddr+2]); + } + + if (stype == 0x5000) { /* error record */ + printf(" (err %d)", M[addr+1]); + } + + if (stype == 0x0800) + break; + + addr += nwords; + putchar('\n'); + + if (nwords == 0) { + printf("0 words?\n"); + break; + } + strel++; + } + + printf("\nEnd found at /%04x, EOFS = /%04x\n", addr, M[0x7a25 & mem_mask]); + return SCPE_OK; +} + +#endif /* TRACE_DMS_IO */ diff --git a/Ibm1130/ibm1130_fmt.c b/Ibm1130/ibm1130_fmt.c new file mode 100644 index 0000000..8178de8 --- /dev/null +++ b/Ibm1130/ibm1130_fmt.c @@ -0,0 +1,313 @@ +/********************************************************************************************* + * ibm1130_fmt.c : interpret tabs in 1130 Assembler or Fortran source + * Bob Flanders + * ------------------------------------------------------------------------------------------- + * + * These routines are used by ibm1130_cr.c when the user has indicated + * that the input text is formatted with tabs. Input lines are edited + * into the appropriate column format. Three edit modes are recognized: + * + * Assembler mode: + * Input lines of the form + * + * [label][opcode][tag][L][argument] + * + * are rearranged so that the input fields are placed in the appropriate columns + * + * The label must start on the first character of the line. If there is no label, + * the first character(s) before the opcode must be whitespace. Following the opcode, there + * MUST be a tab character, followed by the format and tag. Following the format and tag + * may be exactly one whitespace character, and then starts the argument. + * + * Input lines with * in column 1 and blank lines are turned into Assembler comments, + * with the * in the Opcode field. + * + * Assembler directive lines at the beginning of the deck must be preceded by + * ! to indicate that they are not comments. For example, + * + * !*LIST + * * This is a comment + * + * Fortran mode: + * Input lines of the form + * + * [label]statement + * + * or + * + * [label]Xcontinuation + * + * where X is a non alphabetic contination character are rearranged in the + * appropriate manner: + * + * 1 2 + * 12345678901234567890... + * ------------------------ + * label statement + * labelXcontinuation + * + * However, you must take care that you don't end up with statement text after column 72. + * + * Input lines with * or C in column 1 are left alone (comments and directives) + * + * (The ! escape is not used before Fortran directives as before Assembler directives) + * + * Tab mode: + * Tabs are replaced with spaces. Tab settings are assumed to be eight characters wide, + * as is standard for vi, notepad, etc. + *********************************************************************************************/ + +#include +#include +#include +#include +#include +#include "ibm1130_fmt.h" + +#define MAXLINE 81 /* maximum output line size */ +#define WORKSZ 256 /* size for tab work area */ +#define TAGOFFSET 12 /* offset for tag field */ +#define FMTOFFSET 11 /* offset for format field */ + +#define MIN(a,b) ((a < b) ? a : b) +#define AMSG " with Assembler Reformat" +#define FMSG " with FORTRAN Reformat" +#define AFORMAT "%20.20s%-60.60s"," " +#define ACOMMENTFMT "%20.20s%-60.60s"," " +#define ABLANKLINE "%20.20s*"," " +#define FFORMAT "%-5.5s %-74.74s" +#define FCONTFMT "%-5.5s%-75.75s" + +char gszLabel[6]; /* work area for label */ +char gszArg[MAXLINE]; /* .. argument */ +char gszOutput[MAXLINE]; /* .. output */ +short gaiAsmTabs[] = {7,12,15,20,25,30,35,40,45,52,0};/* tab stops for assembler */ + +short gaiPlainTabs[] = {9, 17, 25, 33, 41, 49, 57, 65, 73, 0};/* tab stops for just plain tabs */ + +/* + * helper routines + */ + +/************************************************* + * ExpandTabs: Expand tabs to spaces + */ + +char* ExpandTabs(char* p_szInbuf, /* expand tabs .. input buffer */ + char* p_szOutbuf, /* .. output buffer */ + short* p_aiTabs) /* .. array of tab stops (1 based) -- 0 end of array */ +{ +short iI, /* input position */ + iO, /* output position */ + iT; /* next tab stop */ + +char cX; /* character to test */ + + iI = 0; /* init input position */ + iO = 0; /* init output position */ + iT = 0; /* init tab stop */ + + while ((cX = *(p_szInbuf + iI)) != 0) /* while there are characters */ + { + if (cX == '\t') /* q. tab character? */ + { /* a. yes .. */ + while ((p_aiTabs[iT] <= iO + 1) /* search for next valid stop .. */ + && (p_aiTabs[iT] != 0)) /* .. or end of table */ + iT++; /* .. go to next tab */ + + if (p_aiTabs[iT] != 0) /* q. end of tab array? */ + { /* a. no .. */ + while (iO < (p_aiTabs[iT] - 1)) /* fill to tab with blanks */ + *(p_szOutbuf + iO++) = ' '; /* .. put in a blank */ + + } + else /* Otherwise ... */ + *(p_szOutbuf + iO++) = ' '; /* .. Translate to blank */ + } + else /* Otherwise .. not tab */ + *(p_szOutbuf + iO++) = cX; /* .. save the input char */ + + iI++; /* next input character */ + } + + *(p_szOutbuf + iO) = 0; /* end the string.. */ + return p_szOutbuf; /* .. return output area addr */ +} + +/************************************************* + * extract next token, modify pointer + */ + +char* GetToken(char* p_szOut, /* output location */ + int p_iLen, /* max output length */ + char**p_pszToken) /* pointer to input token */ +{ +int iI; /* work integer */ +char* pszX; /* work pointer */ + + pszX = *p_pszToken; /* get pointer to token */ + + for (iI = 0; *(pszX + iI) && (!isspace(*(pszX + iI)));) /* while not whitespace & not end */ + iI++; /* .. count token length */ + + memset(p_szOut, 0, p_iLen); /* zero out output area */ + + if (iI > 0) /* q. any chars? */ + strncpy(p_szOut, *p_pszToken, MIN(iI, p_iLen-1)); /* a. yes.. copy max of p_iLen-1 */ + + *p_pszToken += iI; /* point beyond token */ + return p_szOut; /* .. return token pointer */ +} + +/************************************************* + * EditToAsm - convert tab-formatted text line to 1130 Assembler format + */ + +char *EditToAsm (char* p_pszEdit) /* convert line to 1130 assembler */ +{ +char pszLine[MAXLINE]; /* source line */ +char pszWork[WORKSZ]; /* work buffer */ +char acTFWrk[2]; /* tag/format work area */ +size_t iI; /* work integer */ + + if (p_pszEdit == NULL) /* q. null request? */ + return AMSG; /* a. yes .. return display message */ + + if (*p_pszEdit == '!') /* leave lines starting with ! alone */ + return EditToWhitespace(p_pszEdit+1); + + if (*p_pszEdit == '*') /* q. comment line? */ + { /* a. yes.. */ + strncpy(pszWork, EditToWhitespace(p_pszEdit), MAXLINE); /* .. convert any tabs */ + sprintf(gszOutput, ACOMMENTFMT, pszWork); /* .. put the comment out there in the opcode column */ + return gszOutput; /* .. and return it */ + } + + strncpy(pszLine, p_pszEdit, MAXLINE-1); /* copy the line local */ + + ExpandTabs(pszLine, pszWork, gaiAsmTabs); /* expand the tabs */ + strncpy(pszLine, pszWork, MAXLINE-1); /* copy the line back */ + + for (iI = strlen(pszLine); iI--;) /* trim trailing whitespace */ + { + if (*(pszLine + iI) <= ' ') /* q. space or less? */ + *(pszLine + iI) = 0; /* a. yes .. remove it */ + else /* otherwise */ + break; /* .. done. Leave loop. */ + } + + if (strlen(pszLine) == 0) /* q. blank line? */ + { /* a. yes .. Assembler abhors these so */ + sprintf(gszOutput, ABLANKLINE); /* format as comment statement */ + return gszOutput; /* .. and return it */ + } + + + /* TODO: Add code to process a strip switch + * comment? + */ + + if (strlen(pszLine) > (TAGOFFSET + 1)) /* q. line long enough? */ + { /* a. yes.. reorder tag/format */ + memcpy(acTFWrk, pszLine + FMTOFFSET, 2); /* get tag/format */ + memset((pszLine + FMTOFFSET), ' ', 2); /* .. blank 'em out */ + + for (iI = 0; iI < 2; iI ++) + if (isalpha(acTFWrk[iI])) /* q. alpha char? */ + *(pszLine + FMTOFFSET) = acTFWrk[iI]; /* a. yes .. make it format */ + else if (isdigit(acTFWrk[iI])) /* q. digit? */ + *(pszLine + TAGOFFSET) = acTFWrk[iI]; /* a. yes .. make it the tag */ + } + + sprintf(gszOutput, AFORMAT, pszLine); /* format the line */ + + return gszOutput; /* return formatted line */ +} + +/************************************************* + * EditToFortran - convert tab-formatted input text line to FORTRAN format + * (a la DEC Fortran) + */ + +char *EditToFortran(char* p_pszEdit) /* convert line to 1130 assembler */ +{ +char pszLine[MAXLINE]; /* source line */ +char* pszWork; /* work pointer */ +size_t iI; /* work integer */ +int bContinue; /* true if continue */ + + if (p_pszEdit == NULL) /* q. null request? */ + return FMSG; /* a. yes .. return display message */ + + if (strchr(p_pszEdit, '\t') == NULL) /* q. no tab in the line? */ + return p_pszEdit; /* a. nope, return line as is, assume it's formatted correctly */ + + if (*p_pszEdit == 'C' || *p_pszEdit == '*' || *p_pszEdit == '\0') /* q. comment or directive or blank line? */ + { /* a. yes.. don't restructure */ + return EditToWhitespace(p_pszEdit); + } + + strncpy(pszLine, p_pszEdit, MAXLINE-1); /* copy the line local */ + + for (iI = strlen(pszLine); iI--;) /* trim trailing whitespace */ + { + if (*(pszLine + iI) <= ' ') /* q. space or less? */ + *(pszLine + iI) = 0; /* a. yes .. remove it */ + else /* otherwise */ + break; /* .. done. Leave loop. */ + } + + /* + * TODO: Add code to process a strip switch + * comment? + */ + + pszWork = (char*) pszLine; /* set pointer to line */ + GetToken(gszLabel, 6, &pszWork); /* get the line, if any. */ + + pszWork++; /* skip tab/whitespace */ + + /* continuation... */ + bContinue = ((isdigit(*pszWork) && (*pszWork != '0')) /* if first char non-zero digit */ + || (!isspace(*pszWork) && !isalpha(*pszWork))); /* .. or non-alpha non-blank */ + + memset(gszArg, 0, MAXLINE); /* .. and arguments */ + + strncpy(gszArg, pszWork, 75); /* copy rest to argument */ + + sprintf(gszOutput, (bContinue) ? FCONTFMT : FFORMAT, /* format the line */ + gszLabel, /* .. statement # */ + gszArg); /* .. code */ + + return gszOutput; /* return formatted line */ +} + +/************************************************* + * EditToWhitespace - expand tabs at 8 space intervals. + */ + +char* EditToWhitespace(char *p_pszEdit) +{ +int iI; /* work integer */ +char pszLine[MAXLINE]; /* source line */ +char pszWork[WORKSZ]; /* work buffer */ + + if (p_pszEdit == NULL) /* q. null request? */ + return AMSG; /* a. yes .. return display message */ + + strncpy(pszLine, p_pszEdit, MAXLINE-1); /* copy the line local */ + + ExpandTabs(pszLine, pszWork, gaiPlainTabs); /* expand the tabs */ + strncpy(gszOutput, pszWork, MAXLINE-1); /* copy the line back */ + + for (iI = strlen(gszOutput); iI--;) /* look at each character */ + { + if (*(gszOutput + iI) <= ' ') /* q. space or less? */ + *(gszOutput + iI) = 0; /* a. yes .. remove it */ + else /* otherwise */ + break; /* .. done. Leave loop. */ + } + + + return gszOutput; /* ... return buffer */ +} diff --git a/Ibm1130/ibm1130_fmt.h b/Ibm1130/ibm1130_fmt.h new file mode 100644 index 0000000..7232560 --- /dev/null +++ b/Ibm1130/ibm1130_fmt.h @@ -0,0 +1,17 @@ +/* + * (C) Copyright 2003, Bob Flander. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to bob@jftr.com + */ + +/* ibm1130_asm.h: definition of routines in ibm1130_asm.c + */ + +char* EditToAsm(char*); /* convert edit format to 1130 assembler format */ +char* EditToFortran(char*); /* convert edit format to Fortran format */ +char* EditToWhitespace(char*); /* clean white space, tabstops every 8 positions */ diff --git a/Ibm1130/ibm1130_gdu.c b/Ibm1130/ibm1130_gdu.c new file mode 100644 index 0000000..86d32c4 --- /dev/null +++ b/Ibm1130/ibm1130_gdu.c @@ -0,0 +1,1128 @@ +#include "ibm1130_defs.h" + +/* ibm1130_gdu.c: IBM 1130 2250 Graphical Display Unit + + (Under construction) + stuff to fix: + "store revert" might be backwards? + alpha keyboard is not implemented + pushbuttons are not implemented + there is something about interrupts being deferred during a subroutine transition? + + Based on the SIMH package written by Robert M Supnik + + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + */ + +#define BLIT_MODE /* define for better performance, undefine when debugging generate_image() */ +/* #define DEBUG_LIGHTPEN */ /* define to debug light-pen sensing */ + +#define DEFAULT_GDU_RATE 20 /* default frame rate */ +#define DEFAULT_PEN_THRESHOLD 3 /* default looseness of light-pen hit */ +#define INDWIDTH 32 /* width of an indicator (there are two columns of these) */ +#define INITSIZE 512 /* initial window size */ + +#define GDU_DSW_ORDER_CONTROLLED_INTERRUPT 0x8000 +#define GDU_DSW_KEYBOARD_INTERUPT 0x4000 +#define GDU_DSW_DETECT_INTERRUPT 0x2000 +#define GDU_DSW_CYCLE_STEAL_CHECK 0x1000 +#define GDU_DSW_DETECT_STATUS 0x0800 +#define GDU_DSW_LIGHT_PEN_SWITCH 0x0100 +#define GDU_DSW_BUSY 0x0080 +#define GDU_DSW_CHARACTER_MODE 0x0040 +#define GDU_DSW_POINT_MODE 0x0020 +#define GDU_DSW_ADDR_DISP 0x0003 + +#define GDU_FKEY_DATA_AVAILABLE 0x8000 +#define GDU_FKEY_KEY_CODE 0x1F00 +#define GDU_FKEY_OVERLAY_CODE 0x00FF + +#define GDU_AKEY_DATA_AVAILABLE 0x8000 +#define GDU_AKEY_END 0x1000 +#define GDU_AKEY_CANCEL 0x0800 +#define GDU_AKEY_ADVANCE 0x0400 +#define GDU_AKEY_BACKSPACE 0x0200 +#define GDU_AKEY_JUMP 0x0100 +#define GDU_AKEY_KEY_CODE 0x00FF + +/* -------------------------------------------------------------------------------------- */ + +#define UNIT_V_DISPLAYED (UNIT_V_UF + 0) +#define UNIT_V_DETECTS_ENABLED (UNIT_V_UF + 1) +#define UNIT_V_INTERRUPTS_DEFERRED (UNIT_V_UF + 2) +#define UNIT_V_LARGE_CHARS (UNIT_V_UF + 3) + +#define UNIT_DISPLAYED (1u << UNIT_V_DISPLAYED) /* display windows is up */ +#define UNIT_DETECTS_ENABLED (1u << UNIT_V_DETECTS_ENABLED) /* light pen detects are enabled */ +#define UNIT_INTERRUPTS_DEFERRED (1u << UNIT_V_INTERRUPTS_DEFERRED) /* light pen interrupts are deferred */ +#define UNIT_LARGE_CHARS (1u << UNIT_V_LARGE_CHARS) /* large character mode */ + +static t_stat gdu_reset (DEVICE *dptr); + +static int16 gdu_dsw = 1; /* device status word */ +static int16 gdu_ar = 0; /* address register */ +static int16 gdu_x = 0; /* X deflection */ +static int16 gdu_y = 0; /* Y deflection */ +static int16 gdu_fkey = 0; /* function keyboard register */ +static int16 gdu_akey = 0; /* alphanumeric keyboard register */ +static int16 gdu_revert = 0; /* revert address register */ +static int32 gdu_indicators = 0; /* programmed indicator lamps */ +static int32 gdu_threshold = DEFAULT_PEN_THRESHOLD; /* mouse must be within 3/1024 of line to be a hit */ +static int32 gdu_rate = DEFAULT_GDU_RATE; /* refresh rate. 0 = default */ + +UNIT gdu_unit = { UDATA (NULL, 0, 0) }; + +REG gdu_reg[] = { + { HRDATA (GDUDSW, gdu_dsw, 16) }, /* device status word */ + { HRDATA (GDUAR, gdu_ar, 16) }, /* address register */ + { HRDATA (GDUXREG, gdu_x, 16) }, /* X deflection register */ + { HRDATA (GDUYREG, gdu_y, 16) }, /* Y deflection register */ + { HRDATA (GDUFKEY, gdu_fkey, 16) }, /* function keyboard register */ + { HRDATA (GDUAKEY, gdu_akey, 16) }, /* alphanumeric keyboard register */ + { HRDATA (GDUREVERT,gdu_revert, 16) }, /* revert address register */ + { HRDATA (GDUAKEY, gdu_indicators, 32) }, /* programmed indicators */ + { DRDATA (GDUTHRESH,gdu_threshold, 32) }, /* mouse closeness threshhold */ + { DRDATA (GDURATE, gdu_rate, 32) }, /* refresh rate in frames/sec */ + { NULL } }; + +DEVICE gdu_dev = { + "GDU", &gdu_unit, gdu_reg, NULL, + 1, 16, 16, 1, 16, 16, + NULL, NULL, gdu_reset, + NULL, NULL, NULL}; + +/* -------------------------------------------------------------------------------------- */ + +#ifndef GUI_SUPPORT + +static t_stat gdu_reset (DEVICE *dptr) +{ + return SCPE_OK; +} + +void xio_2250_display (int32 addr, int32 func, int32 modify) +{ + /* ignore commands if device is nonexistent */ +} + +t_bool gdu_active (void) +{ + return 0; +} + +/* -------------------------------------------------------------------------------------- */ +#else /* GUI_SUPPORT defined */ + +/******* PLATFORM INDEPENDENT CODE ********************************************************/ + +static int32 gdu_instaddr; // address of first word of instruction +static int xmouse, ymouse, lpen_dist, lpen_dist2; // current mouse pointer, scaled closeness threshhold, same squared +static double sfactor; // current scaling factor +static t_bool last_abs = TRUE; // last positioning instruction was absolute +static t_bool mouse_present = FALSE; // mouse is/is not in the window +static void clear_interrupts (void); +static void set_indicators (int32 new_inds); +static void start_regeneration (void); +static void halt_regeneration (void); +static void draw_characters (void); +static void notify_window_closed (void); + +// routines that must be implemented per-platform + +static void DrawLine(int x0, int y0, int x1, int y1); +static void DrawPoint(int x, int y); +static void CheckGDUKeyboard(void); +static t_bool CreateGDUWindow(void); +static void StartGDUUpdates(void); +static void StopGDUUpdates(void); +static void GetMouseCoordinates(void); +static void UpdateGDUIndicators(void); +static void ShowPenHit (int x, int y); +static void EraseGDUScreen (void); + +/* -------------------------------------------------------------------------------------- */ + +void xio_2250_display (int32 addr, int32 func, int32 modify) +{ + switch (func) { + case XIO_SENSE_DEV: + ACC = (gdu_dsw & GDU_DSW_BUSY) ? GDU_DSW_BUSY : gdu_dsw; + if (modify & 1) + clear_interrupts(); + break; + + case XIO_READ: /* store status data into word pointed to by IOCC packet */ + if (gdu_dsw & GDU_DSW_BUSY) /* not permitted while device is busy */ + break; + + WriteW(addr, gdu_ar); /* save status information */ + WriteW(addr+1, gdu_dsw); + WriteW(addr+2, gdu_x & 0x7FF); + WriteW(addr+3, gdu_y & 0x7FF); + WriteW(addr+4, gdu_fkey); + WriteW(addr+5, gdu_akey); + gdu_ar = (int16) (addr+6); /* this alters the channel address register? */ + + clear_interrupts(); /* read status clears the interrupts */ + break; + + case XIO_WRITE: + if (gdu_dsw & GDU_DSW_BUSY) /* treated as no-op if display is busy */ + break; + + if (modify & 0x80) { /* bit 8 on means set indicators, 0 means start regeneration */ + set_indicators((ReadW(addr) << 16) | ReadW(addr+1)); + } + else { + gdu_ar = (int16) addr; + gdu_fkey = 0; + gdu_akey = 0; + clear_interrupts(); + start_regeneration(); + } + break; + + case XIO_CONTROL: + if (modify & 0x80) { /* bit 8 on means reset, off is no-op */ + gdu_reset(&gdu_dev); + set_indicators((addr << 16) | addr); + } + break; + + default: /* all other commands are no-ops */ + break; + } +} + +static t_stat gdu_reset (DEVICE *dptr) +{ + halt_regeneration(); + clear_interrupts(); + set_indicators(0); + gdu_x = gdu_y = 512; + CLRBIT(gdu_unit.flags, UNIT_INTERRUPTS_DEFERRED | UNIT_DETECTS_ENABLED | UNIT_LARGE_CHARS); + gdu_dsw = 1; + return SCPE_OK; +} + +static void clear_interrupts (void) +{ + CLRBIT(gdu_dsw, GDU_DSW_ORDER_CONTROLLED_INTERRUPT | GDU_DSW_KEYBOARD_INTERUPT | GDU_DSW_DETECT_INTERRUPT); + CLRBIT(ILSW[3], ILSW_3_2250_DISPLAY); + calc_ints(); +} + +static void gdu_interrupt (int32 dswbit) +{ + SETBIT(gdu_dsw, dswbit); + SETBIT(ILSW[3], ILSW_3_2250_DISPLAY); + calc_ints(); + halt_regeneration(); +} + +static void set_indicators (int32 new_inds) +{ + gdu_indicators = new_inds; + if (gdu_unit.flags & UNIT_DISPLAYED) + UpdateGDUIndicators(); +} + +static void start_regeneration (void) +{ + SETBIT(gdu_dsw, GDU_DSW_BUSY); + + if ((gdu_unit.flags & UNIT_DISPLAYED) == 0) { + if (! CreateGDUWindow()) + return; + + SETBIT(gdu_unit.flags, UNIT_DISPLAYED); + } + + StartGDUUpdates(); +} + +static void halt_regeneration (void) +{ + // halt_regeneration gets called at end of every refresh interation, so it should NOT black out the + // screen -- this is why it was flickering so badly. The lower level code (called on a timer) + // should check to see if GDU_DSW_BUSY is clear, and if it it still zero after several msec, + // only then should it black out the screen and call StopGDUUpdates. + if (gdu_dsw & GDU_DSW_BUSY) { +// StopGDUUpdates(); // let lower level code discover this during next refresh + CLRBIT(gdu_dsw, GDU_DSW_BUSY); + } +// EraseGDUScreen(); // let cessation of regeneration erase it (eventually) +} + +static void notify_window_closed (void) +{ + if (gdu_dsw & GDU_DSW_BUSY) { + StopGDUUpdates(); + CLRBIT(gdu_dsw, GDU_DSW_BUSY); + } + + CLRBIT(gdu_unit.flags, UNIT_DISPLAYED); + + gdu_reset(&gdu_dev); +} + +static int32 read_gduword (void) +{ + int32 w; + + w = M[gdu_ar++ & mem_mask]; + gdu_dsw = (int16) ((gdu_dsw & ~GDU_DSW_ADDR_DISP) | ((gdu_ar - gdu_instaddr) & GDU_DSW_ADDR_DISP)); + + return w; +} + +#define DIST2(x0,y0,x1,y1) (((x1)-(x0))*((x1)-(x0))+((y1)-(y0))*((y1)-(y0))) + +static void draw (int32 newx, int32 newy, t_bool beam) +{ + int xmin, xmax, ymin, ymax, xd, yd; + double s; + int hit = FALSE; + + if (beam) { + if (gdu_dsw & GDU_DSW_POINT_MODE) { + DrawPoint(newx, newy); + +#ifdef DEBUG_LIGHTPEN + if (DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2) + hit = TRUE; +#else + if (gdu_unit.flags & UNIT_DETECTS_ENABLED && mouse_present) + if (DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2) + hit = TRUE; +#endif + } + else { + DrawLine(gdu_x, gdu_y, newx, newy); + + // calculate proximity of light pen to the line +#ifndef DEBUG_LIGHTPEN + if (gdu_unit.flags & UNIT_DETECTS_ENABLED && mouse_present) { +#endif + if (gdu_x <= newx) + xmin = gdu_x, xmax = newx; + else + xmin = newx, xmax = gdu_x; + + if (gdu_y <= newy) + ymin = gdu_y, ymax = newy; + else + ymin = newy, ymax = gdu_y; + + if (newx == gdu_x) { + // line is vertical. Nearest point is an endpoint if the mouse is above or + // below the line segment, otherwise the segment point at the same y as the mouse + xd = gdu_x; + yd = (ymouse <= ymin) ? ymin : (ymouse >= ymax) ? ymax : ymouse; + + if (DIST2(xd, yd, xmouse, ymouse) <= lpen_dist2) + hit = TRUE; + } + else if (newy == gdu_y) { + // line is horizontal. Nearest point is an endpoint if the mouse is to the left or + // the right of the line segment, otherwise the segment point at the same x as the mouse + xd = (xmouse <= xmin) ? xmin : (xmouse >= xmax) ? xmax : xmouse; + yd = gdu_y; + + if (DIST2(xd, yd, xmouse, ymouse) <= lpen_dist2) + hit = TRUE; + } + else { + // line is diagonal. See if the mouse is inside the box lpen_dist wider than the line segment's bounding rectangle + if (xmouse >= (xmin-lpen_dist) && xmouse <= (xmax+lpen_dist) && ymouse >= (ymin-lpen_dist) || ymouse <= (ymax+lpen_dist)) { + // compute the point at the intersection of the line through the line segment and the normal + // to that line through the mouse. This is the point on the line through the line segment + // nearest the mouse + + s = (double)(newy - gdu_y) / (double)(newx - gdu_x); // slope of line segment + xd = (int) ((ymouse + xmouse/s - gdu_y + s*gdu_x) / (s + 1./s) + 0.5); + + // if intersection is beyond either end of the line segment, the nearest point to the + // mouse is nearest segment end, otherwise it's the computed intersection point + if (xd < xmin || xd > xmax) { +#ifdef DEBUG_LIGHTPEN + // if it's a hit, set xd and yd so we can display the hit + if (DIST2(gdu_x, gdu_y, xmouse, ymouse) <= lpen_dist2) { + hit = TRUE; + xd = gdu_x; + yd = gdu_y; + } + else if (DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2) { + hit = TRUE; + xd = newx; + yd = newy; + } +#else + if (DIST2(gdu_x, gdu_y, xmouse, ymouse) <= lpen_dist2 || DIST2(newx, newy, xmouse, ymouse) <= lpen_dist2) + hit = TRUE; +#endif + } + else { + yd = (int) (gdu_y + s*(xd - gdu_x) + 0.5); + if (DIST2(xd, yd, xmouse, ymouse) <= lpen_dist2) + hit = TRUE; + } + } + } +#ifndef DEBUG_LIGHTPEN + } +#endif + } + } + + if (hit) { +#ifdef DEBUG_LIGHTPEN + ShowPenHit(xd, yd); + if (gdu_unit.flags & UNIT_DETECTS_ENABLED && mouse_present) + SETBIT(gdu_dsw, GDU_DSW_DETECT_STATUS); +#else + SETBIT(gdu_dsw, GDU_DSW_DETECT_STATUS); +#endif + } + + gdu_x = (int16) newx; + gdu_y = (int16) newy; +} + +static void generate_image (void) +{ + int32 instr, new_addr, newx, newy; + t_bool run = TRUE, accept; + + if (! (gdu_dsw & GDU_DSW_BUSY)) + return; + + GetMouseCoordinates(); + + lpen_dist = (int) (gdu_threshold/sfactor + 0.5); // mouse-to-line threshhold at current scaling factor + lpen_dist2 = lpen_dist * lpen_dist; + + while (run) { + if ((gdu_dsw & GDU_DSW_DETECT_STATUS) && ! (gdu_unit.flags & UNIT_INTERRUPTS_DEFERRED)) { + CLRBIT(gdu_dsw, GDU_DSW_DETECT_STATUS); // clear when interrupt is activated + gdu_interrupt(GDU_DSW_DETECT_INTERRUPT); + run = FALSE; + break; + } + + gdu_instaddr = gdu_ar; // remember address of GDU instruction + instr = read_gduword(); // fetch instruction (and we really are cycle stealing here!) + + switch ((instr >> 12) & 0xF) { // decode instruction + case 0: // short branch + case 1: + gdu_revert = gdu_ar; // save revert address & get new address + gdu_ar = (int16) (read_gduword() & 0x1FFF); + if (gdu_dsw & GDU_DSW_CHARACTER_MODE) { + draw_characters(); // in character mode this means we are at character data + gdu_ar = gdu_revert; + } + break; + + case 2: // long branch/interrupt + new_addr = read_gduword(); // get next word + accept = ((instr & 1) ? (gdu_dsw & GDU_DSW_LIGHT_PEN_SWITCH) : TRUE) && ((instr & 2) ? (gdu_dsw & GDU_DSW_DETECT_STATUS) : TRUE); + + if (instr & 2) // clear after testing + CLRBIT(gdu_dsw, GDU_DSW_DETECT_STATUS); + + if (instr & 0x0400) // NOP + accept = FALSE; + + if (accept) { + if (instr & 0x0800) { // branch + gdu_revert = gdu_ar; + + if (instr & 0x0080) // indirect + new_addr = M[new_addr & mem_mask]; + + gdu_ar = (int16) new_addr; + + if (gdu_dsw & GDU_DSW_CHARACTER_MODE) { + draw_characters(); + gdu_ar = gdu_revert; + } + } + else { // interrupt + gdu_interrupt(GDU_DSW_ORDER_CONTROLLED_INTERRUPT); + run = FALSE; + } + } + break; + + case 3: // control instructions + CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE); + + switch ((instr >> 8) & 0xF) { + case 1: // set pen mode + if ((instr & 0xC) == 8) + SETBIT(gdu_unit.flags, UNIT_DETECTS_ENABLED); + else if ((instr & 0xC) == 4) + CLRBIT(gdu_unit.flags, UNIT_DETECTS_ENABLED); + + if ((instr & 0x3) == 2) + SETBIT(gdu_unit.flags, UNIT_INTERRUPTS_DEFERRED); + else if ((instr & 0x3) == 1) + CLRBIT(gdu_unit.flags, UNIT_INTERRUPTS_DEFERRED); + break; + + case 2: // set graphic mode + if (instr & 1) + SETBIT(gdu_dsw, GDU_DSW_POINT_MODE); + else + CLRBIT(gdu_dsw, GDU_DSW_POINT_MODE); + break; + + case 3: // set character mode + SETBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE); + if (instr & 1) + SETBIT(gdu_unit.flags, UNIT_LARGE_CHARS); + else + CLRBIT(gdu_unit.flags, UNIT_LARGE_CHARS); + break; + + case 4: // start timer + run = FALSE; // (which, for us, means stop processing until next timer message) + CheckGDUKeyboard(); + break; + + case 5: // store revert + M[gdu_ar & mem_mask] = gdu_revert; + read_gduword(); // skip to next address + break; + + case 6: // revert + gdu_ar = gdu_revert; + break; + + default: // all others treated as no-ops + break; + } + break; + + case 4: // long absolute + case 5: + CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE); + newx = instr & 0x3FF; + newy = read_gduword() & 0x3FF; + draw(newx, newy, instr & 0x1000); + last_abs = TRUE; + break; + + case 6: // short absolute + case 7: + CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE); + newx = gdu_x; + newy = gdu_y; + if (instr & 0x0800) + newy = instr & 0x3FF; + else + newx = instr & 0x3FF; + draw(newx, newy, instr & 0x1000); + last_abs = TRUE; + break; + + default: // high bit set - it's a relative instruction + CLRBIT(gdu_dsw, GDU_DSW_CHARACTER_MODE); + newx = (instr >> 8) & 0x3F; + newy = instr & 0x3F; + + if (instr & 0x4000) // sign extend x - values are in 2's complement + newx |= -1 & ~0x3F; // although documentation doesn't make that clear + + if (instr & 0x0040) // sign extend y + newy |= -1 & ~0x3F; + + newx = gdu_x + newx; + newy = gdu_y + newy; + draw(newx, newy, instr & 0x0080); + last_abs = FALSE; + break; + } + } +} + +static struct charinfo { // character mode scaling info: + int dx, dy; // character and line spacing + double sx, sy; // scaling factors: character units to screen units + int xoff, yoff; // x and y offset to lower left corner of character + int suby; // subscript/superscript offset +} cx[2] = { + {14, 20, 1.7, 2.0, -6, -7, 6}, // regular + {21, 30, 2.5, 3.0, -9, -11, 9} // large +}; + +static void draw_characters (void) +{ + int32 w, x0, y0, x1, y1, yoff = 0, ninstr = 0; + t_bool dospace, didstroke = FALSE; + struct charinfo *ci; + + ci = &cx[(gdu_unit.flags & UNIT_LARGE_CHARS) ? 1 : 0]; + x0 = gdu_x + ci->xoff; // starting position + y0 = gdu_y + ci->yoff; + + do { + if (++ninstr > 29) { // too many control words + gdu_interrupt(GDU_DSW_CYCLE_STEAL_CHECK); + return; + } + + dospace = TRUE; + w = M[gdu_ar++ & mem_mask]; // get next stroke or control word + + x1 = (w >> 12) & 7; + y1 = (w >> 8) & 7; + + if (x1 == 7) { // this is a character control word + dospace = FALSE; // inhibit character spacing + + switch (y1) { + case 1: // subscript + if (yoff == 0) // (ignored if superscript is in effect) + yoff = -ci->suby; + break; + +// case 2: // no-op or null (nothing to do) +// default: // all unknowns are no-ops +// break; + + case 4: // superscript + yoff = ci->suby; + break; + + case 7: // new line + gdu_x = 0; + gdu_y -= (int16) ci->dy; + if (gdu_y < 0 && last_abs) + gdu_y = (int16) (1024 - ci->dy); // this is a guess + break; + } + } + else { // this is stroke data -- extract two strokes + x1 = gdu_x + (int) (x1*ci->sx + 0.5); + y1 = gdu_y + (int) ((y1+yoff)*ci->sy + 0.5); + + if (w & 0x0800) { + didstroke = TRUE; + DrawLine(x0, y0, x1, y1); + } + + x0 = (w >> 4) & 7; + y0 = w & 7; + + x0 = gdu_x + (int) (x0*ci->sx + 0.5); + y0 = gdu_y + (int) ((y0+yoff)*ci->sy + 0.5); + + if (w & 0x0008) { + didstroke = TRUE; + DrawLine(x1, y1, x0, y0); + } + } + + if (dospace) { + gdu_x += ci->dx; + if (gdu_x > 1023 && last_abs) { // line wrap + gdu_x = 0; + gdu_y -= (int16) ci->dy; + } + } + } while ((w & 0x0080) == 0); // repeat until we hit revert bit + + if (didstroke && mouse_present && (gdu_unit.flags & UNIT_DETECTS_ENABLED)) { + if (xmouse >= (gdu_x - ci->xoff/2) && xmouse <= (gdu_x + ci->xoff/2) && + ymouse >= (gdu_y - ci->yoff/2) && ymouse <= (gdu_y + ci->yoff/2)) + SETBIT(gdu_dsw, GDU_DSW_DETECT_STATUS); + } +} + +/******* PLATFORM SPECIFIC CODE ***********************************************************/ + +#ifdef _WIN32 + +#include +#include + +#define APPCLASS "IBM2250GDU" // window class name + +#define RGB_GREEN RGB(0,255,0) // handy colors +#define RGB_RED RGB(255,0,0) + +static HINSTANCE hInstance; +static HWND hwGDU = NULL; +static HDC hdcGDU = NULL; +static HBITMAP hBmp = NULL; +static int curwid = 0; +static int curht = 0; +static BOOL wcInited = FALSE; +static DWORD GDUPumpID = 0; +static HANDLE hGDUPump = INVALID_HANDLE_VALUE; +static HPEN hGreenPen = NULL; +static HBRUSH hRedBrush = NULL; +#ifdef DEBUG_LIGHTPEN +static HPEN hRedPen = NULL; +#endif +static HBRUSH hGrayBrush, hDarkBrush; +static HPEN hBlackPen; +static int halted = 0; // number of time intervals that GDU has been halted w/o a regeneration +static LRESULT APIENTRY GDUWndProc (HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam); +static DWORD WINAPI GDUPump (LPVOID arg); + +static void destroy_GDU_window (void) +{ + if (hwGDU != NULL) + SendMessage(hwGDU, WM_CLOSE, 0, 0); // cross thread call is OK + + if (hGDUPump != INVALID_HANDLE_VALUE) { // this is not the most graceful way to do it + TerminateThread(hGDUPump, 0); + hGDUPump = INVALID_HANDLE_VALUE; + GDUPumpID = 0; + hwGDU = NULL; + } + + if (hdcGDU != NULL) { + DeleteDC(hdcGDU); + hdcGDU = NULL; + } + + if (hBmp != NULL) { + DeleteObject(hBmp); + hBmp = NULL; + } + + if (hGreenPen != NULL) { + DeleteObject(hGreenPen); + hGreenPen = NULL; + } + + if (hRedBrush != NULL) { + DeleteObject(hRedBrush); + hRedBrush = NULL; + } + +#ifdef DEBUG_LIGHTPEN + if (hRedPen != NULL) { + DeleteObject(hRedPen); + hRedPen = NULL; + } +#endif +} + +static t_bool CreateGDUWindow (void) +{ + static BOOL did_atexit = FALSE; + + hInstance = GetModuleHandle(NULL); + + if (hGDUPump == INVALID_HANDLE_VALUE) + hGDUPump = CreateThread(NULL, 0, GDUPump, 0, 0, &GDUPumpID); + + if (! did_atexit) { + atexit(destroy_GDU_window); + did_atexit = TRUE; + } + + return TRUE; +} + +// windows message handlers ---------------------------------------------------- + +// close the window + +static void gdu_WM_CLOSE (HWND hWnd) +{ + DestroyWindow(hWnd); +} + +// the window is being destroyed + +static void gdu_WM_DESTROY (HWND hWnd) +{ + notify_window_closed(); + hwGDU = NULL; +} + +// adjust the min and max resizing boundaries + +static void gdu_WM_GETMINMAXINFO (HWND hWnd, LPMINMAXINFO mm) +{ + mm->ptMinTrackSize.x = 100 + 2*INDWIDTH; + mm->ptMinTrackSize.y = 100; +} + +static void PaintImage (HDC hDC, BOOL draw_indicators) +{ + HPEN hOldPen; + RECT r; + int wid, ht, x, y, dy, i, j, ycirc; + unsigned long bit; + + GetClientRect(hwGDU, &r); + wid = r.right+1 - 2*INDWIDTH; + ht = r.bottom+1; + sfactor = (double) MIN(wid,ht) / 1024.; + + if (gdu_dsw & GDU_DSW_BUSY) { +#ifdef BLIT_MODE + // if compiled for BLIT_MODE, draw the image into a memory display context, then + // blit the new image over window. This eliminates the flicker that a normal erase-and- + // repaint would cause. + + if (wid != curwid || ht != curht) { // dimensions have changed, discard old memory display context + if (hdcGDU != NULL) { + DeleteDC(hdcGDU); + hdcGDU = NULL; + } + curwid = wid; + curht = ht; + } + + if (hdcGDU == NULL) { // allocate memory display context & select a bitmap into it + hdcGDU = CreateCompatibleDC(hDC); + if (hBmp != NULL) + DeleteObject(hBmp); + hBmp = CreateCompatibleBitmap(hDC, wid, ht); + SelectObject(hdcGDU, hBmp); + } + + PatBlt(hdcGDU, 0, 0, wid, ht, BLACKNESS); // start with a black screen + + hOldPen = SelectObject(hdcGDU, hGreenPen); + + SetMapMode(hdcGDU, MM_ANISOTROPIC); + SetWindowExtEx(hdcGDU, 1024, -1024, NULL); + SetViewportExtEx(hdcGDU, wid, ht, NULL); + SetWindowOrgEx(hdcGDU, 0, 1023, NULL); + + generate_image(); // run the display program to paint the image into the memory context + + SetWindowExtEx(hdcGDU, wid, ht, NULL); // undo the scaling so the blit isn't distorted + SetViewportExtEx(hdcGDU, wid, ht, NULL); + SetWindowOrgEx(hdcGDU, 0, 0, NULL); + BitBlt(hDC, 0, 0, wid, ht, hdcGDU, 0, 0, SRCCOPY); // blit the new image over the old + + SelectObject(hdcGDU, hOldPen); +#else + // for testing purposes -- draw the image directly onto the screen. + // Compile this way when you want to single-step through the image drawing routine, + // so you can see the draws occur. + hdcGDU = hDC; + hOldPen = SelectObject(hdcGDU, hGreenPen); + + SetMapMode(hdcGDU, MM_ANISOTROPIC); + SetWindowExtEx(hdcGDU, 1024, -1024, NULL); + SetViewportExtEx(hdcGDU, wid, ht, NULL); + SetWindowOrgEx(hdcGDU, 0, 1023, NULL); + + generate_image(); + + SelectObject(hdcGDU, hOldPen); + hdcGDU = NULL; +#endif + } + + if (draw_indicators) { + x = r.right-2*INDWIDTH+1; + dy = ht / 16; + ycirc = MIN(dy-2, 8); + + r.left = x; + FillRect(hDC, &r, hGrayBrush); + SelectObject(hDC, hBlackPen); + + bit = 0x80000000L; + for (i = 0; i < 2; i++) { + MoveToEx(hDC, x, 0, NULL); + LineTo(hDC, x, r.bottom); + y = 0; + for (j = 0; j < 16; j++) { + MoveToEx(hDC, x, y, NULL); + LineTo(hDC, x+INDWIDTH, y); + + SelectObject(hDC, (gdu_indicators & bit) ? hRedBrush : hDarkBrush); + Pie(hDC, x+1, y+1, x+1+ycirc, y+1+ycirc, x+1, y+1, x+1, y+1); + + y += dy; + bit >>= 1; + } + x += INDWIDTH; + } + } +} + +// repaint the window + +static void gdu_WM_PAINT (HWND hWnd) +{ + PAINTSTRUCT ps; + HDC hDC; + // code for display + hDC = BeginPaint(hWnd, &ps); + PaintImage(hDC, TRUE); + EndPaint(hWnd, &ps); +} + +// the window has been resized + +static void gdu_WM_SIZE (HWND hWnd, UINT state, int cx, int cy) +{ + InvalidateRect(hWnd, NULL, TRUE); +} + +// tweak the sizing rectangle during a resize to guarantee a square window + +static void gdu_WM_SIZING (HWND hWnd, WPARAM fwSide, LPRECT r) +{ + switch (fwSide) { + case WMSZ_LEFT: + case WMSZ_RIGHT: + case WMSZ_BOTTOMLEFT: + case WMSZ_BOTTOMRIGHT: + r->bottom = r->right - r->left - 2*INDWIDTH + r->top; + break; + + case WMSZ_TOP: + case WMSZ_BOTTOM: + case WMSZ_TOPRIGHT: + r->right = r->bottom - r->top + r->left + 2*INDWIDTH; + break; + + case WMSZ_TOPLEFT: + r->left = r->top - r->bottom + r->right - 2*INDWIDTH; + break; + } +} + +// the refresh timer has gone off + +static void gdu_WM_TIMER (HWND hWnd, UINT id) +{ + HDC hDC; + + if (running) { // if CPU is running, update picture + if ((gdu_dsw & GDU_DSW_BUSY) == 0) { // regeneration is not to occur + if (++halted >= 4) { // stop the timer if four timer intervals go by with the display halted + EraseGDUScreen(); // screen goes black due to cessation of refreshing + StopGDUUpdates(); // might as well kill the timer + return; + } + } + else + halted = 0; + +#ifdef BLIT_MODE + hDC = GetDC(hWnd); // blit the new image right over the old + PaintImage(hDC, FALSE); + ReleaseDC(hWnd, hDC); +#else + InvalidateRect(hWnd, NULL, TRUE); // repaint +#endif + } +} + +// window procedure ------------------------------------------------------------ + +#define HANDLE(msg) case msg: return HANDLE_##msg(hWnd, wParam, lParam, gdu_##msg); + +#ifndef HANDLE_WM_SIZING +// void Cls_OnSizing(HWND hwnd, UINT fwSide, LPRECT r) +# define HANDLE_WM_SIZING(hwnd, wParam, lParam, fn) \ + ((fn)((hwnd), (UINT)(wParam), (LPRECT)(lParam)), 0L) +#endif + +static LRESULT APIENTRY GDUWndProc (HWND hWnd, UINT iMessage, WPARAM wParam, LPARAM lParam) +{ + switch (iMessage) { + HANDLE(WM_CLOSE); + HANDLE(WM_GETMINMAXINFO); + HANDLE(WM_DESTROY); + HANDLE(WM_PAINT); + HANDLE(WM_SIZE); + HANDLE(WM_SIZING); + HANDLE(WM_TIMER); + default: // any message we don't process + return DefWindowProc(hWnd, iMessage, wParam, lParam); + } + return 0L; +} + +// graphic calls ---------------------------------------------------------------- + +static void DrawLine (int x0, int y0, int x1, int y1) +{ + MoveToEx(hdcGDU, x0, y0, NULL); + LineTo(hdcGDU, x1, y1); +} + +static void DrawPoint (int x, int y) +{ + SetPixel(hdcGDU, x, y, RGB_GREEN); +} + +static void UpdateGDUIndicators(void) +{ + if (hwGDU != NULL) + InvalidateRect(hwGDU, NULL, FALSE); // no need to erase the background -- the draw routine fully paints the indicator +} + +static void CheckGDUKeyboard (void) +{ +} + +static UINT idTimer = 0; + +static void StartGDUUpdates (void) +{ + int msec; + + if (idTimer == 0) { + msec = (gdu_rate == 0) ? (1000 / DEFAULT_GDU_RATE) : 1000/gdu_rate; + idTimer = SetTimer(hwGDU, 1, msec, NULL); + } + halted = 0; +} + +static void StopGDUUpdates (void) +{ + if (idTimer != 0) { + KillTimer(hwGDU, 1); + idTimer = 0; + halted = 10000; + } +} + +static void GetMouseCoordinates() +{ + POINT p; + RECT r; + + GetCursorPos(&p); + GetClientRect(hwGDU, &r); + if (! ScreenToClient(hwGDU, &p)) { + xmouse = ymouse = -2000; + mouse_present = FALSE; + return; + } + + if (p.x < r.left || p.x >= r.right || p.y < r.top || p.y > r.bottom) { + mouse_present = FALSE; + return; + } + + // convert mouse coordinates to scaled coordinates + + xmouse = (int) (1024./(r.right+1.-2*INDWIDTH)*p.x + 0.5); + ymouse = 1023 - (int) (1024./(r.bottom+1.)*p.y + 0.5); + mouse_present = TRUE; +} + +t_bool gdu_active (void) +{ + return gdu_dsw & GDU_DSW_BUSY; +} + +static void EraseGDUScreen (void) +{ + if (hwGDU != NULL) /* redraw screen. it will be blank if GDU is not running */ + InvalidateRect(hwGDU, NULL, TRUE); +} + +/* GDUPump - thread responsible for creating and displaying the graphics window */ + +static DWORD WINAPI GDUPump (LPVOID arg) +{ + MSG msg; + WNDCLASS wc; + + if (! wcInited) { /* register Window class */ + memset(&wc, 0, sizeof(wc)); + wc.style = CS_NOCLOSE; + wc.lpfnWndProc = GDUWndProc; + wc.hInstance = hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = GetStockObject(BLACK_BRUSH); + wc.lpszClassName = APPCLASS; + + if (! RegisterClass(&wc)) { + GDUPumpID = 0; + return 0; + } + + wcInited = TRUE; + } + + if (hGreenPen == NULL) + hGreenPen = CreatePen(PS_SOLID, 1, RGB_GREEN); + +#ifdef DEBUG_LIGHTPEN + if (hRedPen == NULL) + hRedPen = CreatePen(PS_SOLID, 1, RGB_RED); +#endif + + if (hRedBrush == NULL) + hRedBrush = CreateSolidBrush(RGB_RED); + + hGrayBrush = GetStockObject(GRAY_BRUSH); + hDarkBrush = GetStockObject(DKGRAY_BRUSH); + hBlackPen = GetStockObject(BLACK_PEN); + + if (hwGDU == NULL) { /* create window */ + hwGDU = CreateWindow(APPCLASS, + "2250 Display", + WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + CW_USEDEFAULT, CW_USEDEFAULT, // initial x, y position + INITSIZE+2*INDWIDTH, INITSIZE, // initial width and height + NULL, // parent window handle + NULL, // use class menu + hInstance, // program instance handle + NULL); // create parameters + + if (hwGDU == NULL) { + GDUPumpID = 0; + return 0; + } + } + + ShowWindow(hwGDU, SW_SHOWNOACTIVATE); /* display it */ + UpdateWindow(hwGDU); + + while (GetMessage(&msg, hwGDU, 0, 0)) { /* message pump - this basically loops forevermore */ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (hwGDU != NULL) { + DestroyWindow(hwGDU); /* but if a quit message got posted, clean up */ + hwGDU = NULL; + } + + GDUPumpID = 0; + return 0; +} + +#ifdef DEBUG_LIGHTPEN +static void ShowPenHit (int x, int y) +{ + HPEN hOldPen; + + hOldPen = SelectObject(hdcGDU, hRedPen); + DrawLine(x-10, y-10, x+10, y+10); + DrawLine(x-10, y+10, x+10, y-10); + SelectObject(hdcGDU, hOldPen); +} +#endif + +#endif // _WIN32 defined +#endif // GUI_SUPPORT defined diff --git a/Ibm1130/ibm1130_gui.c b/Ibm1130/ibm1130_gui.c new file mode 100644 index 0000000..765c221 --- /dev/null +++ b/Ibm1130/ibm1130_gui.c @@ -0,0 +1,1657 @@ +/* ibm1130_gui.c: IBM 1130 CPU simulator Console Display + * + * Based on the SIMH package written by Robert M Supnik + * + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + * + * 30-Dec-05 BLK Fixed mask for IAR and SAR register display and added display + * of Arithmetic Factor, per Carl Claunch. + * + * 09-Apr-04 BLK Changed code to use stock windows cursor IDC_HAND if available + * + * 02-Dec-02 BLK Changed display, added printer and card reader icons + * Added drag and drop support for scripts and card decks + * Added support for physical card reader and printer (hides icons) + * + * 17-May-02 BLK Pulled out of ibm1130_cpu.c + */ + +/* ------------------------------------------------------------------------ + * Definitions + * ------------------------------------------------------------------------ */ + +#include +#include + +#include "ibm1130_defs.h" +#include "ibm1130res.h" + +#define UPDATE_BY_TIMER + +#ifdef UPDATE_BY_TIMER +# define UPDATE_INTERVAL 20 /* set to desired number of updates/second */ +#else +# define UPDATE_INTERVAL 5000 /* GUI: set to 100000/f where f = desired updates/second of 1130 time */ +#endif + +#define UNIT_V_CR_EMPTY (UNIT_V_UF + 5) /* NOTE: THESE MUST MATCH THE DEFINITION IN ibm1130_cr.c */ +#define UNIT_CR_EMPTY (1u << UNIT_V_CR_EMPTY) +#define UNIT_V_PHYSICAL (UNIT_V_UF + 9) +#define UNIT_PHYSICAL (1u << UNIT_V_PHYSICAL) + +#define UNIT_V_PHYSICAL_PTR (UNIT_V_UF + 10) /* NOTE: THESE MUST MATCH THE DEFINITION IN ibm1130_prt.c */ +#define UNIT_PHYSICAL_PTR (1u << UNIT_V_PHYSICAL_PTR) + +/* I think I had it wrong; Program Load actually does start the processor after + * reading in the card? + */ + +#define PROGRAM_LOAD_STARTS_CPU + +/* ------------------------------------------------------------------------ + * Function declarations + * ------------------------------------------------------------------------ */ + +t_stat console_reset (DEVICE *dptr); + +/* ------------------------------------------------------------------------ + * Console display - on Windows builds (only) this code displays the 1130 console + * and toggle switches. It really enhances the experience. + * + * Currently, when the IPS throttle is nonzero, I update the display after every + * UPDATE_INTERVAL instructions, plus or minus a random amount to avoid aliased + * sampling in loops. When UPDATE_INTERVAL is defined as zero, we update every + * instruction no matter what the throttle. This makes the simulator too slow + * but it's cool and helpful during development. + * ------------------------------------------------------------------------ */ + +#define UNIT_V_DISPLAY (UNIT_V_UF + 0) +#define UNIT_DISPLAY (1u << UNIT_V_DISPLAY) + +MTAB console_mod[] = { + { UNIT_DISPLAY, 0, "off", "OFF", NULL }, + { UNIT_DISPLAY, UNIT_DISPLAY, "on", "ON", NULL }, + { 0 } +}; + +UNIT console_unit = {UDATA (NULL, UNIT_DISABLE|UNIT_DISPLAY, 0) }; + +DEVICE console_dev = { + "GUI", &console_unit, NULL, console_mod, + 1, 16, 16, 1, 16, 16, + NULL, NULL, console_reset, + NULL, NULL, NULL +}; + +/* reset for the "console" display device */ + +extern char *read_line (char *cptr, int size, FILE *stream); +extern FILE *sim_log; +extern DEVICE *find_unit (char *cptr, UNIT **uptr); + +extern UNIT cr_unit; /* pointers to 1442 and 1132 (1403) printers */ +extern UNIT prt_unit; + +#ifndef GUI_SUPPORT + void update_gui (int force) {} /* stubs for non-GUI builds */ + void forms_check (int set) {} + void print_check (int set) {} + void keyboard_select (int select) {} + void keyboard_selected (int select) {} + void disk_ready (int ready) {} + void disk_unlocked (int unlocked) {} + void gui_run (int running) {} + static void init_console_window (void) {} + static void destroy_console_window (void) {} + + t_stat console_reset (DEVICE *dptr) {return SCPE_OK;} + void stuff_cmd (char *cmd) {} + t_bool stuff_and_wait (char *cmd, int timeout, int delay) {return FALSE;} + char *read_cmdline (char *ptr, int size, FILE *stream) {return read_line(ptr, size, stream);} + void remark_cmd (char *remark) {printf("%s\n", remark); if (sim_log) fprintf(sim_log, "%s\n", remark);} +#else + +t_stat console_reset (DEVICE *dptr) +{ + if (! sim_gui) { + SETBIT(console_unit.flags, UNIT_DIS); /* disable the GUI */ + CLRBIT(console_unit.flags, UNIT_DISPLAY); /* turn the GUI off */ + } + + update_gui(FALSE); + return SCPE_OK; +} + +/* scp_panic - report fatal internal programming error */ + +void scp_panic (char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(1); +} + +#ifdef _WIN32 + /* only _WIN32 is defined right now */ + +#include + +#define BUTTON_WIDTH 90 +#define BUTTON_HEIGHT 50 + +#define IDC_KEYBOARD_SELECT 0 +#define IDC_DISK_UNLOCK 1 +#define IDC_RUN 2 +#define IDC_PARITY_CHECK 3 +#define IDC_UNUSED 4 +#define IDC_FILE_READY 5 +#define IDC_FORMS_CHECK 6 +#define IDC_POWER_ON 7 +#define IDC_POWER 8 +#define IDC_PROGRAM_START 9 +#define IDC_PROGRAM_STOP 10 +#define IDC_LOAD_IAR 11 +#define IDC_KEYBOARD 12 +#define IDC_IMM_STOP 13 +#define IDC_RESET 14 +#define IDC_PROGRAM_LOAD 15 + +#define IDC_TEAR 16 /* standard button */ +#define IDC_1442 17 /* device images */ +#define IDC_1132 18 + +#define LAMPTIME 500 /* 500 msec delay on updating */ +#define FLASH_TIMER_ID 1 +#define UPDATE_TIMER_ID 2 + +#define RUNSWITCH_X 689 /* center of the run mode switch dial */ +#define RUNSWITCH_Y 107 +#define TOGGLES_X 122 /* left edge of series of toggle switches */ + +#define TXTBOX_X 200 /* text labels showing attached devices */ +#define TXTBOX_Y 300 +#define TXTBOX_WIDTH 195 +#define TXTBOX_HEIGHT 12 + +static BOOL class_defined = FALSE; +static HWND hConsoleWnd = NULL; +static HBITMAP hBitmap = NULL; +static HFONT hFont = NULL; +static HFONT hBtnFont = NULL; +static HFONT hTinyFont = NULL; +static HBRUSH hbLampOut = NULL; +static HBRUSH hbWhite = NULL; +static HBRUSH hbBlack = NULL; +static HBRUSH hbGray = NULL; +static HPEN hSwitchPen = NULL; +static HPEN hWhitePen = NULL; +static HPEN hBlackPen = NULL; +static HPEN hLtGreyPen = NULL; +static HPEN hGreyPen = NULL; +static HPEN hDkGreyPen = NULL; +static int hUpdateTimer = 0; +static int hFlashTimer = 0; + +static HCURSOR hcArrow = NULL; +static HCURSOR hcHand = NULL; +static HINSTANCE hInstance; +static HDC hCDC = NULL; +static char szConsoleClassName[] = "1130CONSOLE"; +static DWORD PumpID = 0; +static HANDLE hPump = INVALID_HANDLE_VALUE; +static int bmwid, bmht; +static HANDLE hbm1442_full, hbm1442_empty, hbm1442_eof, hbm1442_middle; +static HANDLE hbm1132_full, hbm1132_empty; + +static struct tag_btn { + int x, y, wx, wy; + char *txt; + BOOL pushable, state; + COLORREF clr; + HBRUSH hbrLit, hbrDark; + HWND hBtn; + BOOL subclassed; + +} btn[] = { + 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "KEYBOARD\nSELECT", FALSE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, + 0, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "DISK\nUNLOCK", FALSE, TRUE, RGB(255,255,180), NULL, NULL, NULL, TRUE, + 0, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "RUN", FALSE, FALSE, RGB(0,255,0), NULL, NULL, NULL, TRUE, + 0, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "PARITY\nCHECK", FALSE, FALSE, RGB(255,0,0), NULL, NULL, NULL, TRUE, + + 1, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "", FALSE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, + 1, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "FILE\nREADY", FALSE, FALSE, RGB(0,255,0), NULL, NULL, NULL, TRUE, + 1, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "FORMS\nCHECK", FALSE, FALSE, RGB(255,255,0), NULL, NULL, NULL, TRUE, + 1, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "POWER\nON", FALSE, TRUE, RGB(255,255,180), NULL, NULL, NULL, TRUE, + + 2, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "POWER", TRUE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, + 2, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "PROGRAM\nSTART", TRUE, FALSE, RGB(0,255,0), NULL, NULL, NULL, TRUE, + 2, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "PROGRAM\nSTOP", TRUE, FALSE, RGB(255,0,0), NULL, NULL, NULL, TRUE, + 2, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "LOAD\nIAR", TRUE, FALSE, RGB(0,0,255), NULL, NULL, NULL, TRUE, + + 3, 0, BUTTON_WIDTH, BUTTON_HEIGHT, "KEYBOARD", TRUE, FALSE, RGB(255,255,180), NULL, NULL, NULL, TRUE, + 3, 1, BUTTON_WIDTH, BUTTON_HEIGHT, "IMM\nSTOP", TRUE, FALSE, RGB(255,0,0), NULL, NULL, NULL, TRUE, + 3, 2, BUTTON_WIDTH, BUTTON_HEIGHT, "CHECK\nRESET", TRUE, FALSE, RGB(0,0,255), NULL, NULL, NULL, TRUE, + 3, 3, BUTTON_WIDTH, BUTTON_HEIGHT, "PROGRAM\nLOAD", TRUE, FALSE, RGB(0,0,255), NULL, NULL, NULL, TRUE, + + TXTBOX_X+40, TXTBOX_Y+25, 35, 12, "Tear", TRUE, FALSE, 0, NULL, NULL, NULL, FALSE, + 635, 238, 110, 110, "EMPTY_1442", TRUE, FALSE, 0, NULL, NULL, NULL, FALSE, + 635, 366, 110, 110, "EMPTY_1132", TRUE, FALSE, 0, NULL, NULL, NULL, FALSE, +}; +#define NBUTTONS (sizeof(btn) / sizeof(btn[0])) + +#define STATE_1442_EMPTY 0 /* no cards (no file attached) */ +#define STATE_1442_FULL 1 /* cards in hopper (file attached at BOF) */ +#define STATE_1442_MIDDLE 2 /* cards in hopper and stacker (file attached, neither EOF nor BOF) */ +#define STATE_1442_EOF 3 /* cards in stacker (file attached, at EOF) */ +#define STATE_1442_HIDDEN 4 /* simulator is attached to physical card reader */ + +#define STATE_1132_EMPTY 0 /* no paper hanging out of printer */ +#define STATE_1132_FULL 1 /* paper hanging out of printer */ +#define STATE_1132_HIDDEN 2 /* printer is attached to physical printer */ + +static struct tag_txtbox { + int x, y; + char *txt; + char *unitname; + int idctrl; +} txtbox[] = { + TXTBOX_X, TXTBOX_Y, "Card Reader", "CR", -1, + TXTBOX_X, TXTBOX_Y+ 25, "Printer", "PRT", IDC_1132, + TXTBOX_X, TXTBOX_Y+ 50, "Disk 1", "DSK0", -1, + TXTBOX_X, TXTBOX_Y+ 75, "Disk 2", "DSK1", -1, + TXTBOX_X, TXTBOX_Y+100, "Disk 3", "DSK2", -1, + TXTBOX_X, TXTBOX_Y+125, "Disk 4", "DSK3", -1, + TXTBOX_X, TXTBOX_Y+150, "Disk 5", "DSK4", -1, +}; +#define NTXTBOXES (sizeof(txtbox) / sizeof(txtbox[0])) + +#define TXTBOX_BOTTOM (TXTBOX_Y+150) + +static void init_console_window (void); +static void destroy_console_window (void); +LRESULT CALLBACK ConsoleWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static DWORD WINAPI Pump (LPVOID arg); +static void accept_dropped_file (HANDLE hDrop); +static void tear_printer (void); + +#define NIXOBJECT(hObj) if (hObj != NULL) {DeleteObject(hObj); hObj = NULL;} + +/* ------------------------------------------------------------------------ + * init_console_window - display the 1130 console. Actually just creates a thread + * to run the Pump routine which does the actual work. + * ------------------------------------------------------------------------ */ + +static void init_console_window (void) +{ + static BOOL did_atexit = FALSE; + + if (hConsoleWnd != NULL) + return; + + if (PumpID == 0) + hPump = CreateThread(NULL, 0, Pump, 0, 0, &PumpID); + + if (! did_atexit) { + atexit(destroy_console_window); + did_atexit = TRUE; + } +} + +/* ------------------------------------------------------------------------ + * destroy_console_window - delete GDI objects. + * ------------------------------------------------------------------------ */ + +static void destroy_console_window (void) +{ + int i; + + if (hConsoleWnd != NULL) + SendMessage(hConsoleWnd, WM_CLOSE, 0, 0); /* cross thread call is OK */ + + if (hPump != INVALID_HANDLE_VALUE) { /* this is not the most graceful way to do it */ + TerminateThread(hPump, 0); + hPump = INVALID_HANDLE_VALUE; + PumpID = 0; + hConsoleWnd = NULL; + } + if (hCDC != NULL) { + DeleteDC(hCDC); + hCDC = NULL; + } + + NIXOBJECT(hBitmap) + NIXOBJECT(hbLampOut) + NIXOBJECT(hFont) + NIXOBJECT(hBtnFont); + NIXOBJECT(hTinyFont); + NIXOBJECT(hcHand) + NIXOBJECT(hSwitchPen) + NIXOBJECT(hLtGreyPen) + NIXOBJECT(hGreyPen) + NIXOBJECT(hDkGreyPen) + + for (i = 0; i < NBUTTONS; i++) { + NIXOBJECT(btn[i].hbrLit); + NIXOBJECT(btn[i].hbrDark); + } + +/* if (class_defined) { + UnregisterClass(hInstance, szConsoleClassName); + class_defined = FALSE; + } +*/ +} + +/* ------------------------------------------------------------------------ + * these variables hold the displayed versions of the system registers + * ------------------------------------------------------------------------ */ + +static int shown_iar = 0, shown_sar = 0, shown_sbr = 0, shown_afr = 0, shown_acc = 0, shown_ext = 0; +static int shown_op = 0, shown_tag = 0, shown_irq = 0, shown_ccc = 0, shown_cnd = 0, shown_wait = 0; +static int shown_ces = 0, shown_arf = 0, shown_runmode = MODE_RUN; +static int CND; + +/* ------------------------------------------------------------------------ + * RedrawRegion - mark a region for redrawing without background erase + * ------------------------------------------------------------------------ */ + +static void RedrawRegion (HWND hWnd, int left, int top, int right, int bottom) +{ + RECT r; + + r.left = left; + r.top = top; + r.right = right; + r.bottom = bottom; + + InvalidateRect(hWnd, &r, FALSE); +} + +/* ------------------------------------------------------------------------ + * RepaintRegion - mark a region for redrawing with background erase + * ------------------------------------------------------------------------ */ + +static void RepaintRegion (HWND hWnd, int left, int top, int right, int bottom) +{ + RECT r; + + r.left = left; + r.top = top; + r.right = right; + r.bottom = bottom; + + InvalidateRect(hWnd, &r, TRUE); +} + +/* ------------------------------------------------------------------------ + * update_gui - sees if anything on the console display has changed, and invalidates + * the changed regions. Then it calls UpdateWindow to force an immediate repaint. This + * function (update_gui) should probably not be called every time through the main + * instruction loop but it should be called at least whenever wait_state or int_req change, and then + * every so many instructions. It's also called after every simh command so manual changes are + * reflected instantly. + * ------------------------------------------------------------------------ */ + +void update_gui (BOOL force) +{ + int i; + BOOL state; + static int in_here = FALSE; + static int32 displayed = 0; + RECT xin; + + if ((int32)(console_unit.flags & UNIT_DISPLAY) != displayed) { /* setting has changed */ + displayed = console_unit.flags & UNIT_DISPLAY; + if (displayed) + init_console_window(); + else + destroy_console_window(); + } + + if (hConsoleWnd == NULL) + return; + + GUI_BEGIN_CRITICAL_SECTION /* only one thread at a time, please */ + if (in_here) { + GUI_END_CRITICAL_SECTION + return; + } + in_here = TRUE; + GUI_END_CRITICAL_SECTION + + CND = 0; /* combine carry and V as two bits */ + if (C) + CND |= 2; + if (V) + CND |= 1; + + int_lamps |= int_req; + if (ipl >= 0) + int_lamps |= (0x20 >> ipl); + + if (RUNMODE == MODE_LOAD) + SBR = CES; /* in load mode, SBR follows the console switches */ + + if (IAR != shown_iar) + {shown_iar = IAR; RedrawRegion(hConsoleWnd, 75, 8, 364, 32);} /* lamps: don't bother erasing bkgnd */ + if (SAR != shown_sar) + {shown_sar = SAR; RedrawRegion(hConsoleWnd, 75, 42, 364, 65);} + if (ARF != shown_arf) + {shown_arf = ARF; RedrawRegion(hConsoleWnd, 75, 114, 364, 136);} + if (ACC != shown_acc) + {shown_acc = ACC; RedrawRegion(hConsoleWnd, 75, 141, 364, 164);} + if (EXT != shown_ext) + {shown_ext = EXT; RedrawRegion(hConsoleWnd, 75, 174, 364, 197);} + if (SBR != shown_sbr) + {shown_sbr = SBR; RedrawRegion(hConsoleWnd, 75, 77, 364, 97);} + if (OP != shown_op) + {shown_op = OP; RedrawRegion(hConsoleWnd, 501, 8, 595, 32);} + if (TAG != shown_tag) + {shown_tag = TAG; RedrawRegion(hConsoleWnd, 501, 77, 595, 97);} + + if (int_lamps != shown_irq) + {shown_irq = int_lamps; RedrawRegion(hConsoleWnd, 501, 108, 595, 130);} + + if (CCC != shown_ccc) + {shown_ccc = CCC; RedrawRegion(hConsoleWnd, 501, 141, 595, 164);} + if (CND != shown_cnd) + {shown_cnd = CND; RedrawRegion(hConsoleWnd, 501, 174, 595, 197);} + if ((wait_state|wait_lamp) != shown_wait) + {shown_wait= (wait_state|wait_lamp); RedrawRegion(hConsoleWnd, 380, 77, 414, 97);} + if (CES != shown_ces) + {shown_ces = CES; RepaintRegion(hConsoleWnd, TOGGLES_X-7, 230, TOGGLES_X+360, 275);} /* console entry sw: do erase bkgnd */ + if (RUNMODE != shown_runmode) + {shown_runmode = RUNMODE;RepaintRegion(hConsoleWnd, RUNSWITCH_X-50, RUNSWITCH_Y-50, RUNSWITCH_X+50, RUNSWITCH_Y+50);} + + int_lamps = 0; + + /* this loop works with lamp buttons that are calculated on-the-fly only */ + for (i = 0; i < NBUTTONS; i++) { + if (btn[i].pushable) + continue; + + switch (i) { + case IDC_RUN: + state = hFlashTimer || (running && ! wait_state); + break; + +/* this button is always off + case IDC_PARITY_CHECK +*/ + +/* these buttons are enabled/disabled directly + case IDC_POWER_ON: + case IDC_FILE_READY: + case IDC_FORMS_CHECK: + case IDC_KEYBOARD_SELECT: + case IDC_DISK_UNLOCK: +*/ + default: + continue; + } + + if (state != btn[i].state) { /* state has changed */ + EnableWindow(btn[i].hBtn, state); + btn[i].state = state; + } + } + + if (force) { /* if force flag is set, update text region */ + SetRect(&xin, TXTBOX_X, TXTBOX_Y, TXTBOX_X+TXTBOX_WIDTH, TXTBOX_BOTTOM+2*TXTBOX_HEIGHT); + InvalidateRect(hConsoleWnd, &xin, TRUE); + } + + state = ((cr_unit.flags & UNIT_ATT) == 0) ? STATE_1442_EMPTY : + (cr_unit.flags & UNIT_PHYSICAL) ? STATE_1442_HIDDEN : + (cr_unit.flags & UNIT_CR_EMPTY) ? STATE_1442_EOF : + cr_unit.pos ? STATE_1442_MIDDLE : + STATE_1442_FULL; + + if (state != btn[IDC_1442].state) { + if (state == STATE_1442_HIDDEN) + ShowWindow(btn[IDC_1442].hBtn, SW_HIDE); + else { + if (btn[IDC_1442].state == STATE_1442_HIDDEN) + ShowWindow(btn[IDC_1442].hBtn, SW_SHOWNA); + + SendMessage(btn[IDC_1442].hBtn, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM) ( + (state == STATE_1442_FULL) ? hbm1442_full : + (state == STATE_1442_MIDDLE) ? hbm1442_middle : + (state == STATE_1442_EOF) ? hbm1442_eof : + hbm1442_empty)); + } + + btn[IDC_1442].state = state; + } + + state = ((prt_unit.flags & UNIT_ATT) == 0) ? STATE_1132_EMPTY : + (prt_unit.flags & UNIT_PHYSICAL_PTR) ? STATE_1132_HIDDEN : + prt_unit.pos ? STATE_1132_FULL : + STATE_1132_EMPTY; + + if (state != btn[IDC_1132].state) { + if (state == STATE_1132_HIDDEN) + ShowWindow(btn[IDC_1132].hBtn, SW_HIDE); + else { + if (btn[IDC_1132].state == STATE_1132_HIDDEN) + ShowWindow(btn[IDC_1132].hBtn, SW_SHOWNA); + + SendMessage(btn[IDC_1132].hBtn, STM_SETIMAGE, IMAGE_BITMAP, + (LPARAM) ( + (state == STATE_1132_FULL) ? hbm1132_full : hbm1132_empty)); + } + + btn[IDC_1132].state = state; + } + + in_here = FALSE; +} + +WNDPROC oldButtonProc = NULL; + +/* ------------------------------------------------------------------------ + * ------------------------------------------------------------------------ */ + +LRESULT CALLBACK ButtonProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + int i; + + i = GetWindowLong(hWnd, GWL_ID); + + if (! btn[i].pushable) { + if (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONUP || uMsg == WM_LBUTTONDBLCLK) + return 0; + + if (uMsg == WM_CHAR) + if ((TCHAR) wParam == ' ') + return 0; + } + + return CallWindowProc(oldButtonProc, hWnd, uMsg, wParam, lParam); +} + +/* ------------------------------------------------------------------------ + * ------------------------------------------------------------------------ */ + +static int occurs (char *txt, char ch) +{ + int count = 0; + + while (*txt) + if (*txt++ == ch) + count++; + + return count; +} + +/* ------------------------------------------------------------------------ + * turns out to get properly colored buttons you have to paint them yourself. Sheesh. + * On the plus side, this lets do a better job of aligning the button text than + * the button would by itself. + * ------------------------------------------------------------------------ */ + +void PaintButton (LPDRAWITEMSTRUCT dis) +{ + int i = dis->CtlID, nc, nlines, x, y, dy; + BOOL down = dis->itemState & ODS_SELECTED; + HPEN hOldPen; + HFONT hOldFont; + UINT oldAlign; + COLORREF oldBk; + char *txt, *tstart; + + if (! BETWEEN(i, 0, NBUTTONS-1)) + return; + + if (! btn[i].subclassed) + return; + + FillRect(dis->hDC, &dis->rcItem, ((btn[i].pushable || power) && IsWindowEnabled(btn[i].hBtn)) ? btn[i].hbrLit : btn[i].hbrDark); + + if (! btn[i].pushable) { + hOldPen = SelectObject(dis->hDC, hBlackPen); + MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.top, NULL); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.bottom-1); + LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-1); + LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.top); + } + else if (down) { + /* do the three-D thing */ + hOldPen = SelectObject(dis->hDC, hDkGreyPen); + MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-2, NULL); + LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.top); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); + + SelectObject(dis->hDC, hWhitePen); + MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-1, NULL); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.bottom-1); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); + + SelectObject(dis->hDC, hGreyPen); + MoveToEx(dis->hDC, dis->rcItem.left+1, dis->rcItem.bottom-3, NULL); + LineTo(dis->hDC, dis->rcItem.left+1, dis->rcItem.top+1); + LineTo(dis->hDC, dis->rcItem.right-3, dis->rcItem.top+1); + } + else { + hOldPen = SelectObject(dis->hDC, hWhitePen); + MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-2, NULL); + LineTo(dis->hDC, dis->rcItem.left, dis->rcItem.top); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); + + SelectObject(dis->hDC, hDkGreyPen); + MoveToEx(dis->hDC, dis->rcItem.left, dis->rcItem.bottom-1, NULL); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.bottom-1); + LineTo(dis->hDC, dis->rcItem.right-1, dis->rcItem.top); + + SelectObject(dis->hDC, hGreyPen); + MoveToEx(dis->hDC, dis->rcItem.left+1, dis->rcItem.bottom-2, NULL); + LineTo(dis->hDC, dis->rcItem.right-2, dis->rcItem.bottom-2); + LineTo(dis->hDC, dis->rcItem.right-2, dis->rcItem.top+1); + } + + SelectObject(dis->hDC, hOldPen); + + hOldFont = SelectObject(dis->hDC, hBtnFont); + oldAlign = SetTextAlign(dis->hDC, TA_CENTER|TA_TOP); + oldBk = SetBkMode(dis->hDC, TRANSPARENT); + + txt = btn[i].txt; + nlines = occurs(txt, '\n')+1; + x = (dis->rcItem.left + dis->rcItem.right) / 2; + y = (dis->rcItem.top + dis->rcItem.bottom) / 2; + + dy = 14; + y = y - (nlines*dy)/2; + + if (down) { + x += 1; + y += 1; + } + + for (;;) { + for (nc = 0, tstart = txt; *txt && *txt != '\n'; txt++, nc++) + ; + + TextOut(dis->hDC, x, y, tstart, nc); + + if (*txt == '\0') + break; + + txt++; + y += dy; + } + + SetTextAlign(dis->hDC, oldAlign); + SetBkMode(dis->hDC, oldBk); + SelectObject(dis->hDC, hOldFont); +} + +/* ------------------------------------------------------------------------ + * ------------------------------------------------------------------------ */ + +HWND CreateSubclassedButton (HWND hwParent, int i) +{ + HWND hBtn; + int x, y; + int r, g, b; + + y = bmht - (4*BUTTON_HEIGHT) + BUTTON_HEIGHT * btn[i].y; + x = (btn[i].x < 2) ? (btn[i].x*BUTTON_WIDTH) : (598 - (4-btn[i].x)*BUTTON_WIDTH); + + if ((hBtn = CreateWindow("BUTTON", btn[i].txt, WS_CHILD|WS_VISIBLE|BS_CENTER|BS_MULTILINE|BS_OWNERDRAW, + x, y, BUTTON_WIDTH, BUTTON_HEIGHT, hwParent, (HMENU) i, hInstance, NULL)) == NULL) + return NULL; + + btn[i].hBtn = hBtn; + + if (oldButtonProc == NULL) + oldButtonProc = (WNDPROC) GetWindowLong(hBtn, GWL_WNDPROC); + + btn[i].hbrLit = CreateSolidBrush(btn[i].clr); + + if (! btn[i].pushable) { + r = GetRValue(btn[i].clr) / 4; + g = GetGValue(btn[i].clr) / 4; + b = GetBValue(btn[i].clr) / 4; + + btn[i].hbrDark = CreateSolidBrush(RGB(r,g,b)); + EnableWindow(hBtn, FALSE); + } + + SetWindowLong(hBtn, GWL_WNDPROC, (LONG) ButtonProc); + return hBtn; +} + +/* ------------------------------------------------------------------------ + * Pump - thread that takes care of the console window. It has to be a separate thread so that it gets + * execution time even when the simulator is compute-bound or IO-blocked. This routine creates the window + * and runs a standard Windows message pump. The window function does the actual display work. + * ------------------------------------------------------------------------ */ + +static DWORD WINAPI Pump (LPVOID arg) +{ + MSG msg; + int wx, wy, i; + RECT r, ra; + BITMAP bm; + WNDCLASS cd; + HDC hDC; + HWND hActWnd; + + hActWnd = GetForegroundWindow(); + + if (! class_defined) { /* register Window class */ + hInstance = GetModuleHandle(NULL); + + memset(&cd, 0, sizeof(cd)); + cd.style = CS_NOCLOSE; + cd.lpfnWndProc = ConsoleWndProc; + cd.cbClsExtra = 0; + cd.cbWndExtra = 0; + cd.hInstance = hInstance; + cd.hIcon = NULL; + cd.hCursor = hcArrow; + cd.hbrBackground = NULL; + cd.lpszMenuName = NULL; + cd.lpszClassName = szConsoleClassName; + + if (! RegisterClass(&cd)) { + PumpID = 0; + return 0; + } + + class_defined = TRUE; + } + + hbWhite = GetStockObject(WHITE_BRUSH); /* create or fetch useful GDI objects */ + hbBlack = GetStockObject(BLACK_BRUSH); /* create or fetch useful GDI objects */ + hbGray = GetStockObject(GRAY_BRUSH); + hSwitchPen = CreatePen(PS_SOLID, 5, RGB(255,255,255)); + + hWhitePen = GetStockObject(WHITE_PEN); + hBlackPen = GetStockObject(BLACK_PEN); + hLtGreyPen = CreatePen(PS_SOLID, 1, RGB(190,190,190)); + hGreyPen = CreatePen(PS_SOLID, 1, RGB(128,128,128)); + hDkGreyPen = CreatePen(PS_SOLID, 1, RGB(64,64,64)); + + hcArrow = LoadCursor(NULL, IDC_ARROW); +#ifdef IDC_HAND + hcHand = LoadCursor(NULL, IDC_HAND); /* use stock object provided by Windows */ + if (hcHand == NULL) + hcHand = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYHAND)); +#else + hcHand = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_MYHAND)); +#endif + + if (hBitmap == NULL) + hBitmap = LoadBitmap(hInstance, MAKEINTRESOURCE(IDB_CONSOLE)); + if (hbLampOut == NULL) + hbLampOut = CreateSolidBrush(RGB(50,50,50)); + if (hFont == NULL) + hFont = CreateFont(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FIXED_PITCH, FF_SWISS, "Arial"); + if (hBtnFont == NULL) + hBtnFont = CreateFont(-12, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FIXED_PITCH, FF_SWISS, "Arial"); + if (hTinyFont == NULL) + hTinyFont = CreateFont(-10, 0, 0, 0, FW_NORMAL, 0, 0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, FIXED_PITCH, FF_SWISS, "Arial"); + + if (hConsoleWnd == NULL) { /* create window */ + if ((hConsoleWnd = CreateWindow(szConsoleClassName, "IBM 1130", WS_OVERLAPPED|WS_CLIPCHILDREN, 0, 0, 200, 200, NULL, NULL, hInstance, NULL)) == NULL) { + PumpID = 0; + return 0; + } + + DragAcceptFiles(hConsoleWnd, TRUE); /* let it accept dragged files (scripts) */ + } + + GetObject(hBitmap, sizeof(bm), &bm); /* get bitmap size */ + bmwid = bm.bmWidth; + bmht = bm.bmHeight; + + for (i = 0; i < NBUTTONS; i++) { + if (! btn[i].subclassed) + continue; + + CreateSubclassedButton(hConsoleWnd, i); + if (! btn[i].pushable) + EnableWindow(btn[i].hBtn, btn[i].state); + } + +/* This isn't needed anymore, now that we have the big printer icon -- it acts like a button now + * i = IDC_TEAR; + * btn[i].hBtn = CreateWindow("BUTTON", btn[i].txt, WS_CHILD|WS_VISIBLE|BS_CENTER, + * btn[i].x, btn[i].y, btn[i].wx, btn[i].wy, hConsoleWnd, (HMENU) i, hInstance, NULL); + * + * SendMessage(btn[i].hBtn, WM_SETFONT, (WPARAM) hTinyFont, TRUE); + */ + + hbm1442_full = LoadBitmap(hInstance, "FULL_1442"); + hbm1442_empty = LoadBitmap(hInstance, "EMPTY_1442"); + hbm1442_eof = LoadBitmap(hInstance, "EOF_1442"); + hbm1442_middle = LoadBitmap(hInstance, "MIDDLE_1442"); + hbm1132_full = LoadBitmap(hInstance, "FULL_1132"); + hbm1132_empty = LoadBitmap(hInstance, "EMPTY_1132"); + + i = IDC_1442; + + btn[i].hBtn = CreateWindow("STATIC", btn[i].txt, WS_CHILD|WS_VISIBLE|SS_BITMAP|SS_SUNKEN|WS_BORDER|SS_REALSIZEIMAGE|SS_NOTIFY, + btn[i].x, btn[i].y, btn[i].wx, btn[i].wy, hConsoleWnd, (HMENU) i, hInstance, NULL); + btn[i].state = STATE_1442_EMPTY; + + wx = SendMessage(btn[i].hBtn, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbm1442_empty); + + i = IDC_1132; + + btn[i].hBtn = CreateWindow("STATIC", btn[i].txt, WS_CHILD|WS_VISIBLE|SS_BITMAP|SS_SUNKEN|WS_BORDER|SS_REALSIZEIMAGE|SS_NOTIFY, + btn[i].x, btn[i].y, btn[i].wx, btn[i].wy, hConsoleWnd, (HMENU) i, hInstance, NULL); + btn[i].state = FALSE; + + wx = SendMessage(btn[i].hBtn, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) hbm1132_empty); + + GetWindowRect(hConsoleWnd, &r); /* get window size as created */ + wx = r.right - r.left + 1; + wy = r.bottom - r.top + 1; + + if (hCDC == NULL) { /* get a memory DC and select the bitmap into ti */ + hDC = GetDC(hConsoleWnd); + hCDC = CreateCompatibleDC(hDC); + SelectObject(hCDC, hBitmap); + ReleaseDC(hConsoleWnd, hDC); + } + + GetClientRect(hConsoleWnd, &r); + wx = (wx - r.right - 1) + bmwid; /* compute new desired size based on how client area came out */ + wy = (wy - r.bottom - 1) + bmht; + MoveWindow(hConsoleWnd, 0, 0, wx, wy, FALSE); /* resize window */ + + ShowWindow(hConsoleWnd, SW_SHOWNOACTIVATE); /* display it */ + UpdateWindow(hConsoleWnd); + + if (hActWnd != NULL) { /* bring console (sim) window back to top */ + GetWindowRect(hConsoleWnd, &r); + ShowWindow(hActWnd, SW_NORMAL); /* and move it just below the display window */ + SetWindowPos(hActWnd, HWND_TOP, 0, r.bottom, 0, 0, SWP_NOSIZE); + GetWindowRect(hActWnd, &ra); + if (ra.bottom >= GetSystemMetrics(SM_CYSCREEN)) { /* resize if it goes of bottom of screen */ + ra.bottom = GetSystemMetrics(SM_CYSCREEN) - 1; + SetWindowPos(hActWnd, 0, 0, 0, ra.right-ra.left+1, ra.bottom-ra.top+1, SWP_NOZORDER|SWP_NOMOVE); + } + } + + if (running) /* if simulator is already running, start update timer */ + gui_run(TRUE); + + while (GetMessage(&msg, hConsoleWnd, 0, 0)) { /* message pump - this basically loops forevermore */ + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + if (hConsoleWnd != NULL) { + DragAcceptFiles(hConsoleWnd, FALSE); /* unregister as drag/drop target */ + DestroyWindow(hConsoleWnd); /* but if a quit message got posted, clean up */ + hConsoleWnd = NULL; + } + + PumpID = 0; + return 0; +} + +/* ------------------------------------------------------------------------ + * DrawBits - starting at position (x,y), draw lamps for nbits bits of word 'bits', looking only at masked bits + * ------------------------------------------------------------------------ */ + +static void DrawBits (HDC hDC, int x, int y, int bits, int nbits, int mask, char *syms) +{ + int i, b = 0x0001 << (nbits-1); + + for (i = 0; i < nbits; i++, b >>= 1) { + if (mask & b) { /* select white or black lettering then write 2 chars */ + SetTextColor(hDC, (b & bits && power) ? RGB(255,255,255) : RGB(0,0,0)); + TextOut(hDC, x, y, syms, 2); + } + syms += 2; /* go to next symbol pair */ + + if (i < 10) + x += 15; /* step between lamps */ + else + x += 19; + + if (x < 500) { + if (b & 0x1110) + x += 10; /* step over nibble divisions on left side */ + else if (b & 0x0001) + x += 9; + } + } +} + +/* ------------------------------------------------------------------------ + * DrawToggles - display the console sense switches + * ------------------------------------------------------------------------ */ + + +static void DrawToggles (HDC hDC, int bits) +{ + int b, x; + + for (b = 0x8000, x = TOGGLES_X; b != 0; b >>= 1) { + if (shown_ces & b) { /* up */ + SelectObject(hDC, hbWhite); + Rectangle(hDC, x, 232, x+9, 240); + SelectObject(hDC, hbGray); + Rectangle(hDC, x, 239, x+9, 255); + } + else { /* down */ + SelectObject(hDC, hbWhite); + Rectangle(hDC, x, 263, x+9, 271); + SelectObject(hDC, hbGray); + Rectangle(hDC, x, 248, x+9, 264); + } + + x += (b & 0x1111) ? 31 : 21; + } +} + +/* ------------------------------------------------------------------------ + * DrawRunmode - draw the run mode rotary switch's little tip + * ------------------------------------------------------------------------ */ + +void DrawRunmode (HDC hDC, int mode) +{ + double angle = (mode*45. + 90.) * 3.1415926 / 180.; /* convert mode position to angle in radians */ + double ca, sa; /* sine and cosine */ + int x0, y0, x1, y1; + HPEN hOldPen; + + ca = cos(angle); + sa = sin(angle); + + x0 = RUNSWITCH_X + (int) (20.*ca + 0.5); /* inner radius */ + y0 = RUNSWITCH_Y - (int) (20.*sa + 0.5); + x1 = RUNSWITCH_X + (int) (25.*ca + 0.5); /* outer radius */ + y1 = RUNSWITCH_Y - (int) (25.*sa + 0.5); + + hOldPen = SelectObject(hDC, hSwitchPen); + + MoveToEx(hDC, x0, y0, NULL); + LineTo(hDC, x1, y1); + + SelectObject(hDC, hOldPen); +} + +/* ------------------------------------------------------------------------ + * HandleClick - handle mouse clicks on the console window. Now we just + * look at the console sense switches. Actual says this is a real click, rather + * than a mouse-region test. Return value TRUE means the cursor is over a hotspot. + * ------------------------------------------------------------------------ */ + +static BOOL HandleClick (HWND hWnd, int xh, int yh, BOOL actual, BOOL rightclick) +{ + int b, x, r, ang, i; + + for (b = 0x8000, x = TOGGLES_X; b != 0; b >>= 1) { + if (BETWEEN(xh, x-3, x+8+3) && BETWEEN(yh, 230, 275)) { + if (actual) { + CES ^= b; /* a hit. Invert the bit and redisplay */ + update_gui(TRUE); + } + return TRUE; + } + x += (b & 0x1111) ? 31 : 21; + } + + if (BETWEEN(xh, RUNSWITCH_X-50, RUNSWITCH_X+50) && BETWEEN(yh, RUNSWITCH_Y-50, RUNSWITCH_Y+50)) { /* hit near rotary switch */ + ang = (int) (atan2(RUNSWITCH_X-xh, RUNSWITCH_Y-yh)*180./3.1415926); /* this does implicit 90 deg rotation by the way */ + r = (int) sqrt((xh-RUNSWITCH_X)*(xh-RUNSWITCH_X)+(yh-RUNSWITCH_Y)*(yh-RUNSWITCH_Y)); + if (r > 12) { + for (i = MODE_LOAD; i <= MODE_INT_RUN; i++) { + if (BETWEEN(ang, i*45-12, i*45+12)) { + if (actual) { + RUNMODE = i; + update_gui(TRUE); + } + return TRUE; + } + } + + } + } + + return FALSE; +} + +/* ------------------------------------------------------------------------ + * DrawConsole - refresh the console display. (This routine could be sped up by intersecting + * the various components' bounding rectangles with the repaint rectangle. The bounding rects + * could be put into an array and used both here and in the refresh routine). + * + * RedrawRegion -> force repaint w/o background redraw. used for lamps which are drawn in the same place in either state + * RepaintRegion-> repaint with background redraw. Used for toggles which change position. + * ------------------------------------------------------------------------ */ + +static void DrawConsole (HDC hDC, PAINTSTRUCT *ps) +{ + static char digits[] = " 0 1 2 3 4 5 6 7 8 9101112131415"; + static char cccs[] = "3216 8 4 2 1"; + static char cnds[] = " C V"; + static char waits[] = " W"; + HFONT hOldFont, hOldBrush; + RECT xout, xin; + int i, n; + DEVICE *dptr; + UNIT *uptr; + t_bool enab; + char nametemp[50], *dispname; + + hOldFont = SelectObject(hDC, hFont); /* use that tiny font */ + hOldBrush = SelectObject(hDC, hbWhite); + + SetBkMode(hDC, TRANSPARENT); /* overlay letters w/o changing background */ + + DrawBits(hDC, 76, 15, shown_iar, 16, mem_mask, digits); /* register holds only 15 bits */ + DrawBits(hDC, 76, 48, shown_sar, 16, mem_mask, digits); /* but let's display only used bits */ + DrawBits(hDC, 76, 81, shown_sbr, 16, 0xFFFF, digits); + DrawBits(hDC, 76, 114, shown_arf, 16, 0xFFFF, digits); + DrawBits(hDC, 76, 147, shown_acc, 16, 0xFFFF, digits); + DrawBits(hDC, 76, 180, shown_ext, 16, 0xFFFF, digits); + + DrawBits(hDC, 506, 15, shown_op, 5, 0x001F, digits); + DrawBits(hDC, 506, 81, shown_tag, 4, 0x0007, digits); + DrawBits(hDC, 506, 114, shown_irq, 6, 0x003F, digits); + DrawBits(hDC, 506, 147, shown_ccc, 6, 0x003F, cccs); + DrawBits(hDC, 506, 180, shown_cnd, 2, 0x0003, cnds); + + DrawBits(hDC, 390, 81, shown_wait?1:0,1, 0x0001, waits); + + DrawToggles(hDC, shown_ces); + + DrawRunmode(hDC, shown_runmode); + + SelectObject(hDC, hOldFont); + SelectObject(hDC, hOldBrush); + + SetBkColor(hDC, RGB(0,0,0)); + + SetRect(&xin, TXTBOX_X, TXTBOX_Y, TXTBOX_X+TXTBOX_WIDTH, TXTBOX_BOTTOM+TXTBOX_HEIGHT); + if (IntersectRect(&xout, &xin, &ps->rcPaint)) { + hOldFont = SelectObject(hDC, hTinyFont); + + for (i = 0; i < NTXTBOXES; i++) { + enab = FALSE; + + dptr = find_unit(txtbox[i].unitname, &uptr); + if (dptr != NULL && uptr != NULL) { + if (uptr->flags & UNIT_DIS) { + SetTextColor(hDC, RGB(128,0,0)); + } + else if (uptr->flags & UNIT_ATT) { + SetTextColor(hDC, RGB(0,0,255)); + if ((n = strlen(uptr->filename)) > 30) { + strcpy(nametemp, "..."); + strcpy(nametemp+3, uptr->filename+n-30); + dispname = nametemp; + } + else + dispname = uptr->filename; + + TextOut(hDC, txtbox[i].x+25, txtbox[i].y+TXTBOX_HEIGHT, dispname, strlen(dispname)); + SetTextColor(hDC, RGB(255,255,255)); + enab = TRUE; + } + else { + SetTextColor(hDC, RGB(128,128,128)); + } + TextOut(hDC, txtbox[i].x, txtbox[i].y, txtbox[i].txt, strlen(txtbox[i].txt)); + } + + if (txtbox[i].idctrl >= 0) + EnableWindow(btn[txtbox[i].idctrl].hBtn, enab); + } + + SelectObject(hDC, hOldFont); + } +} + +/* ------------------------------------------------------------------------ + * Handles button presses. Remember that this occurs in the context of + * the Pump thread, not the simulator thread. + * ------------------------------------------------------------------------ */ + +void flash_run (void) +{ + EnableWindow(btn[IDC_RUN].hBtn, TRUE); /* enable the run lamp */ + + if (hFlashTimer != 0) + KillTimer(hConsoleWnd, FLASH_TIMER_ID); /* (re)schedule lamp update */ + + hFlashTimer = SetTimer(hConsoleWnd, FLASH_TIMER_ID, LAMPTIME, NULL); +} + +void gui_run (int running) +{ + if (running && hUpdateTimer == 0 && hConsoleWnd != NULL) { + hUpdateTimer = SetTimer(hConsoleWnd, UPDATE_TIMER_ID, 1000/UPDATE_INTERVAL, NULL); + } + else if (hUpdateTimer != 0 && ! running) { + KillTimer(hConsoleWnd, UPDATE_TIMER_ID); + hUpdateTimer = 0; + } + flash_run(); /* keep run lamp active for a while after we stop running */ +} + +void HandleCommand (HWND hWnd, WORD wNotify, WORD idCtl, HWND hwCtl) +{ + int i; + + switch (idCtl) { + case IDC_POWER: /* toggle system power */ + power = ! power; + reset_all(0); + if (running && ! power) { /* turning off */ + reason = STOP_POWER_OFF; + /* wait for execution thread to exit */ +/* this prevents message pump from running, which unfortunately locks up + * the emulator thread when it calls gui_run(FALSE) which calls EnableWindow on the Run lamp + * while (running) + * Sleep(10); + */ + } + + btn[IDC_POWER_ON].state = power; + EnableWindow(btn[IDC_POWER_ON].hBtn, power); + + for (i = 0; i < NBUTTONS; i++) /* repaint all of the lamps */ + if (! btn[i].pushable) + InvalidateRect(btn[i].hBtn, NULL, TRUE); + + break; + + case IDC_PROGRAM_START: /* begin execution */ + if (! running) { + switch (RUNMODE) { + case MODE_INT_RUN: + case MODE_RUN: + case MODE_SI: + stuff_cmd("cont"); + break; + + case MODE_DISP: /* display core and advance IAR */ + ReadW(IAR); + IAR = IAR+1; + flash_run(); /* illuminate run lamp for .5 sec */ + break; + + case MODE_LOAD: /* store to core and advance IAR */ + WriteW(IAR, CES); + IAR = IAR+1; + flash_run(); + break; + } + } + break; + + case IDC_PROGRAM_STOP: + if (running) { /* potential race condition here */ + GUI_BEGIN_CRITICAL_SECTION + SETBIT(con_dsw, CPU_DSW_PROGRAM_STOP); + SETBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP); + int_req |= INT_REQ_5; /* note: calc_ints() is not needed in this case */ + int_lamps |= INT_REQ_5; + GUI_END_CRITICAL_SECTION + } + break; + + case IDC_LOAD_IAR: + if (! running) { + IAR = CES & mem_mask; /* set IAR from console entry switches */ + } + break; + + case IDC_KEYBOARD: /* toggle between console/keyboard mode */ + break; + + case IDC_IMM_STOP: + if (running) { + reason = STOP_IMMEDIATE; /* terminate execution without setting wait_mode */ + /* wait for execution thread to exit */ +/* this prevents message pump from running, which unfortunately locks up + * the emulator thread when it calls gui_run(FALSE) which calls EnableWindow on the Run lamp + * while (running) + * Sleep(10); + */ + } + break; + + case IDC_RESET: + if (! running) { /* check-reset is disabled while running */ + reset_all(0); + forms_check(0); /* clear forms-check status */ + print_check(0); + } + break; + + case IDC_PROGRAM_LOAD: + if (! running) { /* if card reader is attached to a file, do cold start read of one card */ + IAR = 0; /* reset IAR */ +#ifdef PROGRAM_LOAD_STARTS_CPU + stuff_cmd("boot cr"); +#else + if (cr_boot(0, NULL) != SCPE_OK) /* load boot card */ + remark_cmd("IPL failed"); +#endif + } + break; + + case IDC_TEAR: /* "tear off printer output" */ + case IDC_1132: /* do same if they click on the printer icon */ + if (btn[IDC_1132].state && (wNotify == STN_CLICKED || wNotify == STN_DBLCLK)) + tear_printer(); + break; + + case IDC_1442: + if (btn[IDC_1442].state == STATE_1442_FULL || wNotify == STN_DBLCLK) + stuff_cmd("detach cr"); + else if (btn[IDC_1442].state != STATE_1442_EMPTY && wNotify == STN_CLICKED) { + cr_rewind(); + update_gui(TRUE); + } + break; + } + + update_gui(FALSE); +} + +/* ------------------------------------------------------------------------ + * ConsoleWndProc - window process for the console display + * ------------------------------------------------------------------------ */ + +LRESULT CALLBACK ConsoleWndProc (HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HDC hDC; + PAINTSTRUCT ps; + POINT p; + RECT clip, xsect, rbmp; + int i; + + switch (uMsg) { + case WM_CLOSE: + DestroyWindow(hWnd); + break; + + case WM_DESTROY: + gui_run(FALSE); + hConsoleWnd = NULL; + break; + + case WM_ERASEBKGND: + hDC = (HDC) wParam; + GetClipBox(hDC, &clip); + SetRect(&rbmp, 0, 0, bmwid, bmht); + if (IntersectRect(&xsect, &clip, &rbmp)) + BitBlt(hDC, xsect.left, xsect.top, xsect.right-xsect.left+1, xsect.bottom-xsect.top+1, hCDC, xsect.left, xsect.top, SRCCOPY); + return TRUE; /* let Paint do this so we know what the update region is (ps.rcPaint) */ + + case WM_PAINT: + hDC = BeginPaint(hWnd, &ps); + DrawConsole(hDC, &ps); + EndPaint(hWnd, &ps); + break; + + case WM_COMMAND: /* button click */ + HandleCommand(hWnd, HIWORD(wParam), LOWORD(wParam), (HWND) lParam); + break; + + case WM_CTLCOLOREDIT: /* text color for edit controls */ + SetBkColor((HDC) wParam, RGB(0,0,0)); + SetTextColor((HDC) wParam, RGB(255,255,255)); + break; + + case WM_DRAWITEM: + PaintButton((LPDRAWITEMSTRUCT) lParam); + break; + + case WM_SETCURSOR: + GetCursorPos(&p); + ScreenToClient(hWnd, &p); + SetCursor(HandleClick(hWnd, p.x, p.y, FALSE, FALSE) ? hcHand : hcArrow); + return TRUE; + + case WM_LBUTTONDOWN: + HandleClick(hWnd, LOWORD(lParam), HIWORD(lParam), TRUE, FALSE); + break; + + case WM_RBUTTONDOWN: + HandleClick(hWnd, LOWORD(lParam), HIWORD(lParam), TRUE, TRUE); + break; + + case WM_CTLCOLORBTN: + i = GetWindowLong((HWND) lParam, GWL_ID); + if (BETWEEN(i, 0, NBUTTONS-1)) + return (LRESULT) (power && IsWindowEnabled((HWND) lParam) ? btn[i].hbrLit : btn[i].hbrDark); + + case WM_TIMER: + if (wParam == FLASH_TIMER_ID && hFlashTimer != 0) { + KillTimer(hWnd, FLASH_TIMER_ID); + hFlashTimer = 0; + } + update_gui(FALSE); + break; + + case WM_DROPFILES: + accept_dropped_file((HANDLE) wParam); /* console window - dragged file is a script or card deck */ + break; + + default: + return DefWindowProc(hWnd, uMsg, wParam, lParam); + } + + return 0; +} + +enum {PRINTER_OK = 0, FORMS_CHECK = 1, PRINT_CHECK = 2, BOTH_CHECK = 3} printerstatus = PRINTER_OK; + +void forms_check (int set) +{ + COLORREF oldcolor = btn[IDC_FORMS_CHECK].clr; + + if (set) + SETBIT(printerstatus, FORMS_CHECK); + else + CLRBIT(printerstatus, FORMS_CHECK); + + btn[IDC_FORMS_CHECK].clr = (printerstatus & PRINT_CHECK) ? RGB(255,0,0) : RGB(255,255,0); + + btn[IDC_FORMS_CHECK].state = printerstatus; + + if (btn[IDC_FORMS_CHECK].hBtn != NULL) { + EnableWindow(btn[IDC_FORMS_CHECK].hBtn, printerstatus); + + if (btn[IDC_FORMS_CHECK].clr != oldcolor) + InvalidateRect(btn[IDC_FORMS_CHECK].hBtn, NULL, TRUE); /* change color in any case */ + } +} + +void print_check (int set) +{ + COLORREF oldcolor = btn[IDC_FORMS_CHECK].clr; + + if (set) + SETBIT(printerstatus, PRINT_CHECK); + else + CLRBIT(printerstatus, PRINT_CHECK); + + btn[IDC_FORMS_CHECK].clr = (printerstatus & PRINT_CHECK) ? RGB(255,0,0) : RGB(255,255,0); + + btn[IDC_FORMS_CHECK].state = printerstatus; + + if (btn[IDC_FORMS_CHECK].hBtn != NULL) { + EnableWindow(btn[IDC_FORMS_CHECK].hBtn, printerstatus); + + if (btn[IDC_FORMS_CHECK].clr != oldcolor) + InvalidateRect(btn[IDC_FORMS_CHECK].hBtn, NULL, TRUE); /* change color in any case */ + } +} + +void keyboard_selected (int select) +{ + btn[IDC_KEYBOARD_SELECT].state = select; + + if (btn[IDC_KEYBOARD_SELECT].hBtn != NULL) + EnableWindow(btn[IDC_KEYBOARD_SELECT].hBtn, select); +} + +void disk_ready (int ready) +{ + btn[IDC_FILE_READY].state = ready; + + if (btn[IDC_FILE_READY].hBtn != NULL) + EnableWindow(btn[IDC_FILE_READY].hBtn, ready); +} + +void disk_unlocked (int unlocked) +{ + btn[IDC_DISK_UNLOCK].state = unlocked; + + if (btn[IDC_DISK_UNLOCK].hBtn != NULL) + EnableWindow(btn[IDC_DISK_UNLOCK].hBtn, unlocked); +} + +static void accept_dropped_file (HANDLE hDrop) +{ + int nfiles; + char fname[MAX_PATH], cmd[MAX_PATH+50], *deckfile; + BOOL cardreader; + POINT pt; + HWND hWndDrop; + + nfiles = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); /* get file count, */ + DragQueryFile(hDrop, 0, fname, sizeof(fname)); /* get first filename */ + DragQueryPoint(hDrop, &pt); /* get location of drop */ + DragFinish(hDrop); + + if (nfiles <= 0) /* hmm, this seems unlikely to occur, but better check */ + return; + + if (running) { /* can only accept a drop while processor is stopped */ + MessageBeep(0); + return; + } + + if ((hWndDrop = ChildWindowFromPoint(hConsoleWnd, pt)) == btn[IDC_1442].hBtn) + cardreader = TRUE; /* file was dropped onto 1442 card reader */ + else if (hWndDrop == NULL || hWndDrop == hConsoleWnd) + cardreader = FALSE; /* file was dropped onto console window, not a button */ + else { + MessageBeep(0); /* file was dropped onto another button */ + return; + } + + if (nfiles > 1) { /* oops, we wouldn't know what order to read them in */ + MessageBox(hConsoleWnd, "You may only drop one file at a time", "", MB_OK); + return; + } + + /* if shift key is down, prepend @ to name (make it a deck file) */ + deckfile = ((GetKeyState(VK_SHIFT) & 0x8000) && cardreader) ? "@" : ""; + + sprintf(cmd, "%s \"%s%s\"", cardreader ? "attach cr" : "do", deckfile, fname); + stuff_cmd(cmd); +} + +static void tear_printer (void) +{ + char cmd[MAX_PATH+100], filename[MAX_PATH]; + + if ((prt_unit.flags & UNIT_ATT) == 0) + return; + + strcpy(filename, prt_unit.filename); /* save current attached filename */ + + if (! stuff_and_wait("detach prt", 1000, 0)) /* detach it */ + return; + + sprintf(cmd, "view \"%s\"", filename); /* spawn notepad to view it */ + if (! stuff_and_wait(cmd, 3000, 500)) + return; + + remove(filename); /* delete the file */ + + sprintf(cmd, "attach prt \"%s\"", filename); /* reattach */ + stuff_cmd(cmd); +} + +#ifdef XXX + if ((hBtn = CreateWindow("BUTTON", btn[i].txt, WS_CHILD|WS_VISIBLE|BS_CENTER|BS_MULTILINE|BS_OWNERDRAW, + x, y, BUTTON_WIDTH, BUTTON_HEIGHT, hwParent, (HMENU) i, hInstance, NULL)) == NULL) + return NULL; + +#endif + +CRITICAL_SECTION critsect; + +void begin_critical_section (void) +{ + static BOOL mustinit = TRUE; + + if (mustinit) { + InitializeCriticalSection(&critsect); + mustinit = FALSE; + } + + EnterCriticalSection(&critsect); +} + +void end_critical_section (void) +{ + LeaveCriticalSection(&critsect); +} + +#ifndef MIN +# define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#endif + +/* win32 - use a separate thread to read command lines so the GUI + * can insert commands as well */ + +static HANDLE hCmdThread = NULL; +static DWORD iCmdThreadID = 0; +static HANDLE hCmdReadEvent = NULL; +static HANDLE hCmdReadyEvent = NULL; +static BOOL scp_stuffed = FALSE, scp_reading = FALSE; +static char cmdbuffer[256]; + +/* CmdThread - separate thread to read commands from stdin upon request */ + +static DWORD WINAPI CmdThread (LPVOID arg) +{ + for (;;) { + WaitForSingleObject(hCmdReadEvent, INFINITE); /* wait for request */ + read_line(cmdbuffer, sizeof(cmdbuffer), stdin); /* read one line */ + scp_stuffed = FALSE; /* say how we got it */ + scp_reading = FALSE; + SetEvent(hCmdReadyEvent); /* notify main thread a line is ready */ + } + return 0; +} + +char *read_cmdline (char *ptr, int size, FILE *stream) +{ + char *cptr; + + if (hCmdThread == NULL) { /* set up command-reading thread */ + if ((hCmdReadEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) + scp_panic("Can't create command line read event"); + + if ((hCmdReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) + scp_panic("Can't create command line ready event"); + /* start up the command thread */ + if ((hCmdThread = CreateThread(NULL, 0, CmdThread, NULL, 0, &iCmdThreadID)) == NULL) + scp_panic("Unable to create command line reading thread"); + } + + scp_reading = TRUE; + + SetEvent(hCmdReadEvent); /* let read thread get one line */ + WaitForSingleObject(hCmdReadyEvent, INFINITE); /* wait for read thread or GUI to respond */ + strncpy(ptr, cmdbuffer, MIN(size, sizeof(cmdbuffer))); /* copy line to caller's buffer */ + + for (cptr = ptr; isspace(*cptr); cptr++) /* absorb spaces */ + ; + + if (scp_stuffed) { /* stuffed command needs to be echoed */ + printf("%s\n", cptr); + if (sim_log) fprintf(sim_log, "%s\n", cptr); + } + + return cptr; +} + +/* stuff_cmd - force a command into the read_cmdline output buffer. Called asynchronously by GUI */ + +void stuff_cmd (char *cmd) +{ + strcpy(cmdbuffer, cmd); /* save the string */ + scp_stuffed = TRUE; /* note where it came from */ + scp_reading = FALSE; + ResetEvent(hCmdReadEvent); /* clear read request event */ + SetEvent(hCmdReadyEvent); /* notify main thread a line is ready */ +} + +/* my_yield - process GUI messages. It's not apparent why stuff_and_wait would block, + * since it sleeps in the GUI thread while scp runs in the main thread. However, + * at the end of every command scp calls update_gui, which can result in messages + * being sent to the GUI thread. So, the GUI thread has to process messages while + * stuff_and_wait is waiting. + */ +static void my_yield (void) +{ + MSG msg; + /* multitask */ + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +} + +/* stuff_and_wait -- stuff a command and wait for the emulator to process the command + * and come back to prompt for another + */ + +t_bool stuff_and_wait (char *cmd, int timeout, int delay) +{ + scp_reading = FALSE; + + stuff_cmd(cmd); + + while (! scp_reading) { + if (timeout < 0) + return FALSE; + + my_yield(); + if (scp_reading) + break; + + Sleep(50); + if (timeout) + if ((timeout -= 50) <= 0) + timeout = -1; + + my_yield(); + } + + if (delay) + Sleep(delay); + + return TRUE; +} + +/* remark_cmd - print a remark from inside a command processor. This routine takes + * into account the possiblity that the command might have been stuffed, in which + * case the sim> prompt needs to be reprinted. + */ + +void remark_cmd (char *remark) +{ + if (scp_reading) { + putchar('\n'); + if (sim_log) putc('\n', sim_log); + } + + printf("%s\n", remark); + if (sim_log) fprintf(sim_log, "%s\n", remark); + + if (scp_reading) { + printf("sim> "); + if (sim_log) fprintf(sim_log, "sim> "); + } +} + +#endif /* _WIN32 defined */ +#endif /* GUI_SUPPORT defined */ diff --git a/Ibm1130/ibm1130_plot.c b/Ibm1130/ibm1130_plot.c new file mode 100644 index 0000000..02344a3 --- /dev/null +++ b/Ibm1130/ibm1130_plot.c @@ -0,0 +1,634 @@ +/* ibm1130_plot.c: IBM 1130 1627 plotter emulation + + Based on the SIMH simulator package written by Robert M Supnik + + Brian Knittel + Revision History + + 2004.10.22 - Written. + 2006.1.2 - Rewritten as plotter routine by Carl V Claunch + + * (C) Copyright 2004, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + */ + +#include "ibm1130_defs.h" + +#ifndef ENABLE_PLOT_SUPPORT + + DEVICE plot_dev = { + "PLOT", NULL, NULL, NULL, + 0, 16, 16, 1, 16, 16, + NULL, NULL, NULL, + NULL, NULL, NULL}; + + void xio_1627_plotter (int32 addr, int32 func, int32 modify) + { + /* silently eat any plotter commands */ + } + +#else + +#include "gd.h" + +/*************************************************************************************** + * 1627 model 1 plotter (based on Calcomp 535 which was sold as IBM 1627) + * + * - 11" wide carriage, addressible in .01" steps + * - continous sheet paper up to 120' in length + * - sheet moveable in .01" steps, either direction + * - switchable pen, in various colors and line widths + * + * Simulator implementation will create a JPEG image corresponding to a + * landscape mode sheet of paper, the width of the carriage at 11". + * A diagram of more than 8" of paper travel will span printed pages + * in landscape mode. + * + * When an 'att plot' command is issued a file is created based on the + * default or currently set values of paper length, starting + * position of the pen in both X and Y axes, pen color and pen width. + * Based on the number of logical pages of paper, the command will create + * the proper size canvas internally and create the output JPEG file. + * + * When a 'det plot' command is issued, the plotter image will be converted + * into the file that was specified during the attach process. The + * image is not viewable until this point, unless an examine plot is + * issued which will dump the current state of the paper into the file. + * + * The 'set plot' command can set pen width, paper length, pen color, + * current carriage X and Y coordinates. Paper length can be set + * to alter the default of 800 (8"); changes are ignored until + * the next 'attach' command. The current carriage x and y positions + * can be set at any time and will go into effect immediately, just + * as the pen color and pen width can be altered on the fly. + * + * NOTE: requires gd library and definition of ENABLE_PLOT_SUPPORT in makefile or Visual C configuration + * gd is not included in the main simh and ibm1130.org distributions at the present time. + ***************************************************************************************/ + +#define PLOT1627_DSW_OP_COMPLETE 0x8000 +#define PLOT1627_DSW_BUSY 0x0200 +#define PLOT1627_DSW_NOT_READY 0x0100 + +#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT) +#define IS_DEBUG ((plot_unit->flags & UNIT_DEBUG) == UNIT_DEBUG) +#define IS_PENDOWN ((plot_unit->flags & UNIT_PEN) == UNIT_PEN) + +static t_stat plot_svc (UNIT *uptr); /* activity routine */ +static t_stat plot_reset (DEVICE *dptr); /* reset of 1130 */ +static t_stat plot_attach (UNIT *uptr, char *cptr); /* attach, loads plotter */ +static t_stat plot_detach (UNIT *uptr); /* detach and save image */ +static t_stat plot_examine (UNIT *uptr); /* update file with current canvas */ +static t_stat plot_set_length (UNIT *uptr, int32 val, char * ptr, void *desc); /* set paper length */ +static t_stat plot_set_pos (UNIT *uptr, int32 val, char * ptr, void *desc); /* reset current X/Y position */ +static t_stat plot_show_vals(FILE *fp, UNIT *uptr, int32 val, void *descrip); /* print x, y and length */ +static t_stat plot_show_nl(FILE *fp, UNIT *uptr, int32 val, void *descrip); /* overcome wacky simh behavior */ +static void update_pen(void); /* will ensure pen action is correct when changes made */ +static t_stat plot_validate_change (UNIT *uptr, int32 val, char * ptr, void *desc); /* when set command issued */ +static void process_cmd(void); /* does actual drawing for plotter */ + +extern int32 sim_switches; /* switches set on simh command */ +static int16 plot_dsw = 0; /* device status word */ +static int16 plot_cmd = 0; /* the command to process */ +static int32 plot_wait = 1000; /* plotter movement wait */ +static int32 plot_xpos = 0; /* current X position */ +static int32 plot_xmax = 799; /* end of paper */ +static int32 plot_ypos = 0; /* current Y position */ +static int32 plot_ymax = 1099; /* right edge of carriage */ + +#define PEN_DOWN 0x80000000 +#define PEN_UP 0x00000000 +static int32 plot_pen = PEN_UP; /* current pen position */ + +static int black_pen; /* holds color black */ +static int blue_pen; /* holds color blue */ +static int red_pen; /* holds color red */ +static int green_pen; /* holds color green */ +static int yellow_pen; /* holds yellow color */ +static int purple_pen; /* holds color purple */ +static int ltgrey_pen; /* holds light grey */ +static int grey_pen; /* holds grey */ +static int white_background; /* holds white of paper roll */ +static int plot_pwidth; /* set and display variable */ +static int plot_pcolor; /* set and display variable */ +static int need_update = 0; /* flag to force and update_pen() */ +static gdImagePtr image; /* pointer to our canvas */ + +#define UNIT_V_COLOR (UNIT_V_UF + 0) /* color of selected pen - 3 bits */ +#define UNIT_V_WIDTH (UNIT_V_UF + 3) /* width of pen - two bits */ +#define UNIT_V_NOOP (UNIT_V_UF + 5) /* dummy for set/show commands */ +#define UNIT_V_DEBUG (UNIT_V_UF + 6) /* for -d switch on attach command */ +#define UNIT_V_PEN (UNIT_V_UF + 7) /* track pen state */ + +#define UNIT_WIDTH (3u << UNIT_V_WIDTH) /* two bits */ +#define UNIT_COLOR (7u << UNIT_V_COLOR) /* three bits */ +#define UNIT_NOOP (1u << UNIT_V_NOOP) /* dummy for set/show */ +#define UNIT_DEBUG (1u << UNIT_V_DEBUG) /* shows debug mode on */ +#define UNIT_PEN (1u << UNIT_V_PEN) /* the pen state bit */ + +#define PEN_BLACK (0u << UNIT_V_COLOR) +#define PEN_RED (1u << UNIT_V_COLOR) +#define PEN_BLUE (2u << UNIT_V_COLOR) +#define PEN_GREEN (3u << UNIT_V_COLOR) +#define PEN_YELLOW (4u << UNIT_V_COLOR) +#define PEN_PURPLE (5u << UNIT_V_COLOR) +#define PEN_LTGREY (6u << UNIT_V_COLOR) +#define PEN_GREY (7u << UNIT_V_COLOR) + +#define SET_COLOR(op) {plot_unit[0].flags &= ~UNIT_COLOR; plot_unit[0].flags |= (op);} +#define GET_COLOR (plot_unit[0].flags & UNIT_COLOR) + +#define BLACK 0,0,0 +#define BLUE 0,0,255 +#define RED 255,0,0 +#define GREEN 0,255,0 +#define YELLOW 200,200,0 +#define PURPLE 150,0,150 +#define LTGREY 200,200,200 +#define GREY 120,120,120 +#define WHITE 255,255,255 + +#define PEN_SINGLE (0u << UNIT_V_WIDTH) +#define PEN_DOUBLE (1u << UNIT_V_WIDTH) +#define PEN_TRIPLE (2u << UNIT_V_WIDTH) +#define PEN_QUAD (3u << UNIT_V_WIDTH) + +#define GET_WIDTH() (plot_unit[0].flags & UNIT_WIDTH) +#define SET_WIDTH(cd) {plot_unit[0].flags &= ~UNIT_WIDTH; un.flags |= (cd);} + +UNIT plot_unit[] = { + { UDATA (&plot_svc, UNIT_ATTABLE, 0) }, +}; + +REG plot_reg[] = { + { HRDATA (DSW, plot_dsw, 16) }, /* device status word */ + { DRDATA (WTIME, plot_wait, 24), PV_LEFT }, /* plotter movement wait */ + { DRDATA (Xpos, plot_xpos, 32), PV_LEFT }, /* Current X Position*/ + { DRDATA (Ypos, plot_ypos, 32), PV_LEFT }, /* Current Y Position*/ + { FLDATA (PenDown, plot_pen, 0)}, /* Current pen position - 1 = down */ + { DRDATA (PaperSize, plot_xmax, 32), PV_LEFT }, /* Length of paper in inches */ + { NULL } }; + +MTAB plot_mod[] = { + { UNIT_COLOR, PEN_BLACK, "black", "BLACK", &plot_validate_change}, + { UNIT_COLOR, PEN_RED, "red", "RED", &plot_validate_change}, + { UNIT_COLOR, PEN_BLUE, "blue", "BLUE", &plot_validate_change}, + { UNIT_COLOR, PEN_GREEN, "green", "GREEN", &plot_validate_change}, + { UNIT_COLOR, PEN_YELLOW, "yellow", "YELLOW", &plot_validate_change}, + { UNIT_COLOR, PEN_PURPLE, "purple", "PURPLE", &plot_validate_change}, + { UNIT_COLOR, PEN_LTGREY, "ltgrey", "LTGREY", &plot_validate_change}, + { UNIT_COLOR, PEN_GREY, "grey", "GREY", &plot_validate_change}, + { UNIT_WIDTH, PEN_SINGLE, "1.0", "1.0", &plot_validate_change}, + { UNIT_WIDTH, PEN_DOUBLE, "2.0", "2.0", &plot_validate_change}, + { UNIT_WIDTH, PEN_TRIPLE, "3.0", "3.0", &plot_validate_change}, + { UNIT_WIDTH, PEN_QUAD, "4.0", "4.0", &plot_validate_change}, + { UNIT_PEN, UNIT_PEN, "pendown", "PENDOWN", &plot_validate_change}, + { UNIT_PEN, 0, "penup", "PENUP", &plot_validate_change}, + /* below is dummy entry to trigger the show routine and print extended values */ + { UNIT_NOOP, 0, "", NULL, NULL, &plot_show_vals}, + /* extended entries must allow parm for both unit and dev, but + * then they will print the value twice for a 'show plot' command + * therefore they are set to not display unless explicity requested + * and the special dummy NOOP entry will cause the print of these values */ + { MTAB_XTD | MTAB_VAL | MTAB_VUN | MTAB_VDV | MTAB_NMO, 2, + "length", "LENGTH", &plot_set_length, &plot_show_nl, &plot_reg[5]}, + { MTAB_XTD | MTAB_VAL | MTAB_VDV | MTAB_VUN | MTAB_NMO, 0, + "Xpos", "XPOS", &plot_set_pos, &plot_show_nl, &plot_reg[2]}, + { MTAB_XTD | MTAB_VAL | MTAB_VDV | MTAB_VUN | MTAB_NMO, 1, + "Ypos", "YPOS", &plot_set_pos, &plot_show_nl, &plot_reg[3]}, + { 0 } }; + +DEVICE plot_dev = { + "PLOT", plot_unit, plot_reg, plot_mod, + 1, 16, 16, 1, 16, 16, + NULL, NULL, plot_reset, + NULL, plot_attach, plot_detach}; + +/* xio_1627_plotter - XIO command interpreter for the 1627 plotter model 1 */ + +void xio_1627_plotter (iocc_addr, iocc_func, iocc_mod) +{ + char msg[80]; + + if (! IS_ONLINE(plot_unit) ) { + SETBIT(plot_dsw, PLOT1627_DSW_NOT_READY); /* set not ready */ + if (IS_DEBUG) printf("Plotter has no paper, ignored\n"); + return; /* and ignore */ + } + + switch (iocc_func) { + case XIO_READ: /* read XIO */ + xio_error("Read XIO not supported by 1627 plotter"); + break; + + case XIO_WRITE: /* write: do one plotter operation */ + if ((plot_dsw & PLOT1627_DSW_NOT_READY)) { + if (IS_DEBUG) printf("Wrote to non-ready Plotter\n"); + break; + } + plot_cmd = (uint16) ( M[iocc_addr & mem_mask] >> 10 ); /* pick up command */ + process_cmd(); /* interpret command */ + sim_activate(plot_unit, plot_wait); /* schedule interrupt */ + SETBIT(plot_dsw, PLOT1627_DSW_BUSY); /* mark it busy */ + break; + + case XIO_SENSE_DEV: /* sense device status */ + ACC = plot_dsw; /* get current status */ + if (iocc_mod & 0x01) { /* reset interrupts */ + CLRBIT(plot_dsw, PLOT1627_DSW_OP_COMPLETE); + CLRBIT(ILSW[3], ILSW_3_1627_PLOTTER); + } + break; + + case XIO_CONTROL: /* control XIO */ + xio_error("Control XIO not supported by 1627 plotter"); + break; + + default: + sprintf(msg, "Invalid 1627 Plotter XIO function %x", iocc_func); + xio_error(msg); + } + return; +} + +// plot_svc - 1627 plotter operation complete + +static t_stat plot_svc (UNIT *uptr) +{ + CLRBIT(plot_dsw, PLOT1627_DSW_BUSY); /* clear reader busy flag */ + + SETBIT(plot_dsw, PLOT1627_DSW_OP_COMPLETE); /* indicate read complete */ + + SETBIT(ILSW[3], ILSW_3_1627_PLOTTER); /* initiate interrupt */ + calc_ints(); + + return SCPE_OK; +} + +/* plot_reset - reset emulated plotter */ + +static t_stat plot_reset (DEVICE *dptr) +{ + char * buf; + int32 size; + + sim_cancel(plot_unit); + + CLRBIT(plot_dsw, PLOT1627_DSW_BUSY | PLOT1627_DSW_OP_COMPLETE); + + if (IS_DEBUG) printf("reset routine for Plotter\n"); + + CLRBIT(ILSW[3], ILSW_3_1627_PLOTTER); + calc_ints(); + + return SCPE_OK; +} + + +/* plot_attach - attach file to simulated plotter */ + +static t_stat plot_attach (UNIT *uptr, char *cptr) +{ + t_stat result; + + CLRBIT(uptr->flags, UNIT_DEBUG); + if (sim_switches & SWMASK('D')) SETBIT(uptr->flags, UNIT_DEBUG); + + /* get the output file by using regular attach routine */ + result = attach_unit(uptr, cptr); + + if (result != SCPE_OK) { + if (IS_DEBUG) printf("problem attaching file\n"); + return result; + } + + SETBIT(plot_dsw, PLOT1627_DSW_NOT_READY); /* assume failure */ + + /* set up our canvas at the desired size */ + image = gdImageCreate(plot_ymax+1,plot_xmax+1); /* create our canvas */ + if (image == NULL) { + if (IS_DEBUG) printf("problem creating image canvas\n"); + return SCPE_MEM; + } + + /* set up the basic colors after image created */ + white_background = gdImageColorAllocate(image,WHITE); /* white is background */ + black_pen = gdImageColorAllocate(image,BLACK); /* load up black color */ + blue_pen = gdImageColorAllocate(image,BLUE); /* load up blue color */ + red_pen = gdImageColorAllocate(image,RED); /* load up red color */ + green_pen = gdImageColorAllocate(image,GREEN); /* load up green color */ + yellow_pen = gdImageColorAllocate(image,YELLOW); /* load up yellow color */ + purple_pen = gdImageColorAllocate(image,PURPLE); /* load up purple color */ + ltgrey_pen = gdImageColorAllocate(image,LTGREY); /* load up light grey color */ + grey_pen = gdImageColorAllocate(image,GREY); /* load up grey color */ + + if ( (white_background == -1) || (black_pen == -1) || + (red_pen == -1) || (blue_pen == -1) || (green_pen == -1) || + (purple_pen == -1) || (ltgrey_pen == -1) || (grey_pen == -1) ) { + if (IS_DEBUG) printf("problem allocating pen colors\n"); + return SCPE_MEM; + } + + CLRBIT(plot_dsw, PLOT1627_DSW_NOT_READY); /* we're in business */ + + update_pen(); /* routine to ensure pen is okay */ + + return SCPE_OK; +} + +/* pen updating routine, called at attach and whenever we reset the values */ + +void update_pen (void) +{ + int color; + int width; + + if (!IS_ONLINE(plot_unit)) return; /* only do this if attached */ + + /* pick up latest color as active pen */ + color = GET_COLOR; + switch (color) { + case PEN_BLACK: + plot_pcolor = black_pen; + break; + + case PEN_RED: + plot_pcolor = red_pen; + break; + + case PEN_BLUE: + plot_pcolor = blue_pen; + break; + + case PEN_GREEN: + plot_pcolor = green_pen; + break; + + case PEN_YELLOW: + plot_pcolor = yellow_pen; + break; + + case PEN_PURPLE: + plot_pcolor = purple_pen; + break; + + case PEN_LTGREY: + plot_pcolor = ltgrey_pen; + break; + + case PEN_GREY: + plot_pcolor = grey_pen; + break; + + default: + if (IS_DEBUG) printf("invalid pen color state\n"); + plot_pcolor = black_pen; + break; + } + + /* set up anti-aliasing for the line */ + gdImageSetAntiAliased(image, plot_pcolor); + + /* pick up latest width for pen */ + width = GET_WIDTH(); + switch (width) { + case PEN_SINGLE: + plot_pwidth = 1; + gdImageSetThickness(image, 1); + break; + + case PEN_TRIPLE: + plot_pwidth = 3; + gdImageSetThickness(image, 3); + break; + + case PEN_DOUBLE: + plot_pwidth = 2; + gdImageSetThickness(image, 2); + break; + + case PEN_QUAD: + plot_pwidth = 4; + gdImageSetThickness(image, 4); + break; + + default: + if (IS_DEBUG) printf("invalid pen width\n"); + plot_pwidth = 1; + gdImageSetThickness(image, 1); + break; + } + + /* now ensure the pen state is accurate */ + plot_pen = IS_PENDOWN ? PEN_DOWN : PEN_UP; + return; +} + +/* plot_detach - detach file from simulated plotter */ +static t_stat plot_detach (UNIT *uptr) +{ + char * buf; + int32 size; + FILE * fp; + int32 result; + + SETBIT(plot_dsw, PLOT1627_DSW_NOT_READY); + + /* copy images to files, close files, set device to detached, free gd memory */ + + buf = gdImageGifPtr(image,&size); + if (! buf) { + if (IS_DEBUG) printf("failure creating GIF in-memory\n"); + return SCPE_MEM; + } + + fp = uptr->fileref; /* get file attached to unit */ + + if (! fseek(fp,0,SEEK_SET)) { /* first we reset to begin of file */ + if (IS_DEBUG) printf("wrote out GIF to file\n"); + result = fwrite(buf,1,size,fp); /* write out our image to the file */ + } + + gdFree(buf); /* free up the memory of GIF format */ + gdImageDestroy(image); /* free up the canvas memory */ + + if (result != size) { /* some problem writing it */ + if (IS_DEBUG) printf("error in write of image file\n"); + return SCPE_IOERR; + } + + return detach_unit(uptr); /* have simh close the file */ +} + +/* process_cmd - implement the drawing actions of the plotter */ + +static void process_cmd (void) +{ + int32 oldx, oldy; + + /* first see if we set any changes to pen or position, do an update */ + if (need_update) { + update_pen(); + need_update = 0; + } + + /* will move pen one step or flip pen up or down */ + oldx = plot_xpos; + oldy = plot_ypos; + + switch (plot_cmd) { + case 1: /* raise pen command */ + plot_pen = PEN_UP; + plot_unit->flags = plot_unit->flags & (~UNIT_PEN); + return; + break; + + case 2: /* +Y command */ + plot_ypos = plot_ypos + 1; + break; + + case 4: /* -Y command */ + plot_ypos = plot_ypos - 1; + break; + + case 8: /* -X command */ + plot_xpos = plot_xpos - 1; + break; + + case 10: /* -X +Y command */ + plot_xpos = plot_xpos - 1; + plot_ypos = plot_ypos + 1; + break; + + case 12: /* -X -Y command */ + plot_xpos = plot_xpos - 1; + plot_ypos = plot_ypos - 1; + break; + + case 16: /* +X command */ + plot_xpos = plot_xpos + 1; + break; + + case 18: /* +X +Y command */ + plot_xpos = plot_xpos + 1; + plot_ypos = plot_ypos + 1; + break; + + case 20: /* +X -Y pen command */ + plot_xpos = plot_xpos + 1; + plot_ypos = plot_ypos - 1; + break; + + case 32: /* lower pen command */ + plot_pen = PEN_DOWN; + plot_unit->flags = plot_unit->flags | UNIT_PEN; + return; + break; + + default: + if (IS_DEBUG) printf("invalid plotter command\n"); + return; + break; + } + + /* check to see if carriage has moved off any edge */ + if ((plot_xpos > (plot_xmax+1)) || (plot_ypos > (plot_ymax+1)) || + (plot_xpos < 0) || (plot_ypos < 0)) { + /* if so, ignore as 1627 has no way of signalling error */ + if (IS_DEBUG) printf( + "attempted to move carriage off paper edge %d %d for command %d\n", + plot_xpos,plot_ypos,plot_cmd); + return; + } + + /* only draw a line if the pen was down during the movement command */ + if (plot_pen) { + gdImageLine(image, plot_ymax-plot_ypos, plot_xmax-plot_xpos, plot_ymax-oldy, plot_xmax-oldx, gdAntiAliased); + /* semantics are 0,0 point is lower right */ + } + + return; +} + +/* plot_set_length - validate and store the length of the paper */ + +static t_stat plot_set_length (UNIT *uptr, int32 set, char *ptr, void *desc) +{ + char *cptr; + int32 val; + +#define LONGEST_ROLL 1440000 /* longest is 120', 14400", 1,440,000 .01"s */ + + val = strtotv (ptr, &cptr, (uint32) 10); /* sim routine to get value */ + if ((val < 1) | (val >= LONGEST_ROLL)) { /* check valid range */ + if (IS_DEBUG) printf("setting paper more than 120' or less than 1 inch\n"); + return SCPE_ARG; + } + + /* origin zero drawing, reduce by 1 but show command will fudge by adding it back */ + *((int32 *)((REG *) desc)->loc) = val - 1; + + return SCPE_OK; +} + +/* plot_set_pos - validate and store the new position of the carriage */ + +static t_stat plot_set_pos (UNIT *uptr, int32 set, char *ptr, void *desc) +{ + char *cptr; + int32 val; + int32 max; + + max = (set == 1) ? plot_ymax : plot_xmax; + val = strtotv (ptr, &cptr, (uint32) 10); + if ((val < 0) | (val > max)) { + if (IS_DEBUG) printf("error moving carriage off paper edge\n"); + return SCPE_ARG; + } + + *((int32 *)((REG *) desc)->loc) = val; + + return SCPE_OK; +} + +/* routine to display the paper length and carriage position + * cannot use regular simh routine because it prints values twice, + * once for device and once for unit + */ + +static t_stat plot_show_vals (FILE *fp, UNIT *uptr, int32 val, void *descrip) +{ + fprintf(fp, "length=%d, Xpos=%d, Ypos=%d",plot_xmax+1, plot_xpos,plot_ypos); + return SCPE_OK; +} + +/* routine to add a terminating NL character when 'show plot length' + * or equivalent for xpos or ypos is issued, as simh will not append for us */ + +static t_stat plot_show_nl(FILE *fp, UNIT *uptr, int32 val, void *descrip) +{ + int32 disp; + char *label; + + disp = (val == 2) ? plot_xmax + 1 : ((val == 1) ? plot_ypos : plot_xpos); + label = (val == 2) ? "length=" : ((val == 1) ? "Ypos=" : "Xpos="); + + fprintf(fp, "%s%d\n", label, disp); + return SCPE_OK; +} + +/* plot_validate_change - force the update_pen routine to be called after user changes pen setting */ + +static t_stat plot_validate_change (UNIT *uptr, int32 set, char *ptr, void *desc) +{ + need_update = 1; + return SCPE_OK; +} + +#endif /* ENABLE_PLOT_SUPPORT */ diff --git a/Ibm1130/ibm1130_prt.c b/Ibm1130/ibm1130_prt.c new file mode 100644 index 0000000..30fe258 --- /dev/null +++ b/Ibm1130/ibm1130_prt.c @@ -0,0 +1,819 @@ +/* ibm1130_prt.c: IBM 1130 line printer emulation + + Based on the SIMH simulator package written by Robert M Supnik + + Brian Knittel + Revision History + + 2006.12.06 - Moved CGI stuff out of this routine into cgi1130 main() module. + + 2006.07.06 - Made 1403 printer 132 columns wide, was 120 previously + + 2006.01.03 - Fixed bug in prt_attach, found and fixed by Carl Claunch. Detach followed + by reattach of 1403-mode printer left device permanently not-ready. + + 2004.11.08 - HACK for demo mode: in physical (-p) mode, multiple consecutive formfeeds are suppressed. + This lets us do a formfeed at the end of a job to kick the last page out + without getting another blank page at the beginning of the next job. + + 2003.12.02 - Added -p option for physical line printer output (flushes + output buffer after each line). When using a physical printer on + Windows, be sure to set printer to "send output directly to printer" + to disable spooling, otherwise nothing appears until printer is + detatched. + + 2003.11.25 - Changed magic filename for standard output to "(stdout)". + + 2002.09.13 - Added 1403 support. New file, taken from part of ibm1130_stddev.c + + Note: The 1403 is much faster, even in emulation, because it takes much + less CPU power to run it. DMS doesn't use the WAIT command when waiting for + printer operations to complete, so it ends up burning LOTS of cpu cycles. + The 1403 printer doesn't require as many. HOWEVER: DMS must be loaded for the 1403, + and Fortran IOCS control cards must specify it. + + The 1132 is still the default printer. + + As written, we can't have two printers. + + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + */ + +#include "ibm1130_defs.h" +#include /* needed for atexit, for cgi mode */ + +/*************************************************************************************** + * 1132 PRINTER + ***************************************************************************************/ + +#define PRT1132_DSW_READ_EMITTER_RESPONSE 0x8000 +#define PRT1132_DSW_SKIP_RESPONSE 0x4000 +#define PRT1132_DSW_SPACE_RESPONSE 0x2000 +#define PRT1132_DSW_CARRIAGE_BUSY 0x1000 +#define PRT1132_DSW_PRINT_SCAN_CHECK 0x0800 +#define PRT1132_DSW_NOT_READY 0x0400 +#define PRT1132_DSW_PRINTER_BUSY 0x0200 + +#define PRT1132_DSW_CHANNEL_MASK 0x00FF /* 1132 printer DSW bits */ +#define PRT1132_DSW_CHANNEL_1 0x0080 +#define PRT1132_DSW_CHANNEL_2 0x0040 +#define PRT1132_DSW_CHANNEL_3 0x0020 +#define PRT1132_DSW_CHANNEL_4 0x0010 +#define PRT1132_DSW_CHANNEL_5 0x0008 +#define PRT1132_DSW_CHANNEL_6 0x0004 +#define PRT1132_DSW_CHANNEL_9 0x0002 +#define PRT1132_DSW_CHANNEL_12 0x0001 + +#define PRT1403_DSW_PARITY_CHECK 0x8000 /* 1403 printer DSW bits */ +#define PRT1403_DSW_TRANSFER_COMPLETE 0x4000 +#define PRT1403_DSW_PRINT_COMPLETE 0x2000 +#define PRT1403_DSW_CARRIAGE_COMPLETE 0x1000 +#define PRT1403_DSW_RING_CHECK 0x0400 +#define PRT1403_DSW_SYNC_CHECK 0x0200 +#define PRT1403_DSW_CH9 0x0010 +#define PRT1403_DSW_CH12 0x0008 +#define PRT1403_DSW_CARRIAGE_BUSY 0x0004 +#define PRT1403_DSW_PRINTER_BUSY 0x0002 +#define PRT1403_DSW_NOT_READY 0x0001 + +#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT) + +static t_stat prt1132_svc(UNIT *uptr); +static t_stat prt1403_svc(UNIT *uptr); +static t_stat prt_svc (UNIT *uptr); +static t_stat prt_reset (DEVICE *dptr); +static t_stat prt_attach (UNIT *uptr, char *cptr); +static t_stat prt_detach (UNIT *uptr); + +static int16 PRT_DSW = 0; /* device status word */ +static int32 prt_swait = 500; /* line skip wait */ +static int32 prt_cwait = 1250; /* character rotation wait */ +static int32 prt_fwait = 100; /* fast wait, for 1403 operations */ +static int32 prt_twait = 50; /* transfer wait, for 1403 operations */ +#define SKIPTARGET (uptr->u4) /* target for skip operation */ + +static t_bool formfed = FALSE; /* last line printed was a formfeed */ + +#define UNIT_V_FORMCHECK (UNIT_V_UF + 0) /* out of paper error */ +#define UNIT_V_DATACHECK (UNIT_V_UF + 1) /* printer overrun error */ +#define UNIT_V_SKIPPING (UNIT_V_UF + 2) /* printer skipping */ +#define UNIT_V_SPACING (UNIT_V_UF + 3) /* printer is spacing */ +#define UNIT_V_PRINTING (UNIT_V_UF + 4) /* printer printing */ +#define UNIT_V_TRANSFERRING (UNIT_V_UF + 5) /* unit is transferring print buffer (1403 only) */ +#define UNIT_V_1403 (UNIT_V_UF + 6) /* printer model is 1403 rather than 1132 */ +#define UNIT_V_PARITYCHECK (UNIT_V_UF + 7) /* error flags for 1403 */ +#define UNIT_V_RINGCHECK (UNIT_V_UF + 8) +#define UNIT_V_SYNCCHECK (UNIT_V_UF + 9) +#define UNIT_V_PHYSICAL_PTR (UNIT_V_UF + 10) /* this appears in ibm1130_gui as well */ + +#define UNIT_FORMCHECK (1u << UNIT_V_FORMCHECK) +#define UNIT_DATACHECK (1u << UNIT_V_DATACHECK) +#define UNIT_SKIPPING (1u << UNIT_V_SKIPPING) +#define UNIT_SPACING (1u << UNIT_V_SPACING) +#define UNIT_PRINTING (1u << UNIT_V_PRINTING) +#define UNIT_TRANSFERRING (1u << UNIT_V_TRANSFERRING) +#define UNIT_1403 (1u << UNIT_V_1403) +#define UNIT_PARITYCHECK (1u << UNIT_V_PARITYCHECK) +#define UNIT_RINGCHECK (1u << UNIT_V_RINGCHECK) +#define UNIT_SYNCCHECK (1u << UNIT_V_SYNCCHECK) +#define UNIT_PHYSICAL_PTR (1u << UNIT_V_PHYSICAL_PTR) + +UNIT prt_unit[] = { + { UDATA (&prt_svc, UNIT_ATTABLE, 0) }, +}; + +#define IS_1403(uptr) (uptr->flags & UNIT_1403) /* model test */ +#define IS_1132(uptr) ((uptr->flags & UNIT_1403) == 0) /* model test */ +#define IS_PHYSICAL(uptr) (uptr->flags & UNIT_PHYSICAL_PTR) + +/* Parameter in the unit descriptor (1132 printer) */ + +#define CMD_NONE 0 +#define CMD_SPACE 1 +#define CMD_SKIP 2 +#define CMD_PRINT 3 + +REG prt_reg[] = { + { HRDATA (PRTDSW, PRT_DSW, 16) }, /* device status word */ + { DRDATA (STIME, prt_swait, 24), PV_LEFT }, /* line skip wait */ + { DRDATA (CTIME, prt_cwait, 24), PV_LEFT }, /* character rotation wait */ + { DRDATA (FTIME, prt_fwait, 24), PV_LEFT }, /* 1403 fast wait */ + { DRDATA (TTIME, prt_twait, 24), PV_LEFT }, /* 1403 transfer wait */ + { NULL } }; + +MTAB prt_mod[] = { + { UNIT_1403, 0, "1132", "1132", NULL }, /* model option */ + { UNIT_1403, UNIT_1403, "1403", "1403", NULL }, + { 0 } }; + +DEVICE prt_dev = { + "PRT", prt_unit, prt_reg, prt_mod, + 1, 16, 16, 1, 16, 16, + NULL, NULL, &prt_reset, + NULL, prt_attach, prt_detach}; + +#define MAX_COLUMNS 120 +#define MAX_OVPRINT 20 +#define PRT1132_COLUMNS 120 +#define PRT1403_COLUMNS 120 /* the 1130's version of the 1403 printed in 120 columns only (see Functional Characteristics) */ + +static char prtbuf[MAX_COLUMNS*MAX_OVPRINT]; +static int nprint[MAX_COLUMNS], ncol[MAX_OVPRINT], maxnp; +static int prt_nchar, prt_row; /* current printwheel position, current page row */ +static int prt_nnl; /* number of queued newlines */ + +#define CC_CHANNEL_1 0x0800 /* carriage control tape punch values */ +#define CC_CHANNEL_2 0x0400 +#define CC_CHANNEL_3 0x0200 +#define CC_CHANNEL_4 0x0100 +#define CC_CHANNEL_5 0x0080 +#define CC_CHANNEL_6 0x0040 /* 7, 8, 10 and 11 are not used on 1132 printer */ +#define CC_CHANNEL_7 0x0020 +#define CC_CHANNEL_8 0x0010 +#define CC_CHANNEL_9 0x0008 +#define CC_CHANNEL_10 0x0004 +#define CC_CHANNEL_11 0x0002 +#define CC_CHANNEL_12 0x0001 + +#define CC_1403_BITS 0x0FFF /* all bits for 1403, most for 1132 */ +#define CC_1132_BITS (CC_1403_BITS & ~(CC_CHANNEL_7|CC_CHANNEL_8|CC_CHANNEL_10|CC_CHANNEL_11)) + +#define PRT_PAGELENGTH 66 + +static int cctape[PRT_PAGELENGTH]; /* standard carriage control tape */ + +static struct tag_ccpunches { /* list of rows and punches on tape */ + int row, channels; +} +ccpunches[] = { + 2, CC_CHANNEL_1, /* channel 1 = top of form */ + 62, CC_CHANNEL_12 /* channel 12 = bottom of form */ +}, +cccgi[] = { + 2, CC_CHANNEL_1 /* channel 1 = top of form; no bottom of form */ +}; + +#include "ibm1130_prtwheel.h" + +extern int32 sim_switches; + +/* cc_format_1132 and cc_format_1403 - turn cctape bits into proper format for DSW or status read */ + +static int cc_format_1132 (int bits) +{ + return ((bits & (CC_CHANNEL_1|CC_CHANNEL_2|CC_CHANNEL_3|CC_CHANNEL_4|CC_CHANNEL_5|CC_CHANNEL_6)) >> 4) | + ((bits & CC_CHANNEL_9) >> 3) | + (bits & CC_CHANNEL_12); +} + +#define cc_format_1403(bits) (bits) + +/* reset_prt_line - clear the print line following paper advancement */ + +static void reset_prt_line (void) +{ + memset(nprint, 0, sizeof(nprint)); + memset(ncol, 0, sizeof(ncol)); + maxnp = 0; +} + +/* save_1132_prt_line - fire hammers for character 'ch' */ + +static t_bool save_1132_prt_line (int ch) +{ + int i, r, addr = 32; + int32 mask = 0, wd = 0; + + for (i = 0; i < PRT1132_COLUMNS; i++) { + if (mask == 0) { /* fetch next word from memory */ + mask = 0x8000; + wd = M[addr++]; + } + + if (wd & mask) { /* hammer is to fire in this column */ + if ((r = nprint[i]) < MAX_OVPRINT) { + if (ncol[r] <= i) { /* we haven't moved this far yet */ + if (ncol[r] == 0) /* first char in this row? */ + memset(prtbuf+r*MAX_COLUMNS, ' ', PRT1132_COLUMNS); /* blank out the new row */ + ncol[r] = i+1; /* remember new row length */ + } + prtbuf[r*MAX_COLUMNS + i] = (char) ch; /* save the character */ + + nprint[i]++; /* remember max overprintings for this column */ + maxnp = MAX(maxnp, nprint[i]); + } + } + + mask >>= 1; /* prepare to examine next bit */ + } + + return wd & 1; /* return TRUE if the last word has lsb set, which means all bits had been set */ +} + +/* write_line - write collected line to output file. No need to trim spaces as the hammers + * are never fired for them, so ncol[r] is the last printed position on each line. + */ + +static void newpage (FILE *fd, t_bool physical_printer) +{ + if (cgi) + fputs("


\n", fd); + else if (! formfed) { + putc('\f', fd); + if (physical_printer) { + fflush(fd); /* send the ff out to the printer immediately */ + formfed = TRUE; /* hack: inhibit consecutive ff's */ + } + } +} + +static void flush_prt_line (FILE *fd, int spacemode, t_bool physical_printer) +{ + int r; + + if (! (spacemode || maxnp)) /* nothing to do */ + return; + + prt_row = (prt_row+1) % PRT_PAGELENGTH; /* NEXT line */ + + if (spacemode && ! maxnp) { /* spacing only */ + if (prt_row == 0 && prt_nnl) { +#ifdef _WIN32 + if (! cgi) + putc('\r', fd); /* DOS/Windows: end with cr/lf */ +#endif + putc('\n', fd); /* otherwise end with lf */ + if (spacemode & UNIT_SKIPPING) /* add formfeed if we crossed page boundary while skipping */ + newpage(fd, physical_printer); + + prt_nnl = 0; + } + else { + prt_nnl++; + formfed = FALSE; + } + + prt_unit->pos++; /* note something written */ + return; + } + + if (prt_nnl) { /* there are queued newlines */ + while (prt_nnl > 0) { /* spit out queued newlines */ +#ifdef _WIN32 + if (! cgi) + putc('\r', fd); /* DOS/Windows: end with cr/lf */ +#endif + putc('\n', fd); /* otherwise end with lf */ + prt_nnl--; + } + } + + for (r = 0; r < maxnp; r++) { + if (r > 0) + putc('\r', fd); /* carriage return between overprinted lines */ + + fxwrite(&prtbuf[r*MAX_COLUMNS], 1, ncol[r], fd); + } + + reset_prt_line(); + + prt_unit->pos++; /* note something written */ + prt_nnl++; /* queue a newline */ + + if (physical_printer) /* if physical printer, send buffered output to device */ + fflush(fd); + + formfed = FALSE; /* note that something is now on the page */ +} + +/* 1132 printer commands */ + +#define PRT_CMD_START_PRINTER 0x0080 +#define PRT_CMD_STOP_PRINTER 0x0040 +#define PRT_CMD_START_CARRIAGE 0x0004 +#define PRT_CMD_STOP_CARRIAGE 0x0002 +#define PRT_CMD_SPACE 0x0001 + +#define PRT_CMD_MASK 0x00C7 + +extern char * saywhere (int addr); + +static void mytrace (int start, char *what) +{ + char *where; + + if ((where = saywhere(prev_IAR)) == NULL) where = "?"; + trace_io("%s %s at %04x: %s\n", start ? "start" : "stop", what, prev_IAR, where); +} + +/* xio_1132_printer - XIO command interpreter for the 1132 printer */ + +void xio_1132_printer (int32 iocc_addr, int32 func, int32 modify) +{ + char msg[80]; + UNIT *uptr = &prt_unit[0]; + + switch (func) { + case XIO_READ: + M[iocc_addr & mem_mask] = codewheel1132[prt_nchar].ebcdic << 8; + + if ((uptr->flags & UNIT_PRINTING) == 0) /* if we're not printing, advance this after every test */ + prt_nchar = (prt_nchar + 1) % WHEELCHARS_1132; + break; + + case XIO_SENSE_DEV: + ACC = PRT_DSW; + if (modify & 0x01) { /* reset interrupts */ + CLRBIT(PRT_DSW, PRT1132_DSW_READ_EMITTER_RESPONSE | PRT1132_DSW_SKIP_RESPONSE | PRT1132_DSW_SPACE_RESPONSE); + CLRBIT(ILSW[1], ILSW_1_1132_PRINTER); + } + break; + + case XIO_CONTROL: + if (modify & PRT_CMD_START_PRINTER) { + SETBIT(uptr->flags, UNIT_PRINTING); +/* mytrace(1, "printing"); */ + } + + if (modify & PRT_CMD_STOP_PRINTER) { + CLRBIT(uptr->flags, UNIT_PRINTING); +/* mytrace(0, "printing"); */ + } + + if (modify & PRT_CMD_START_CARRIAGE) { + SETBIT(uptr->flags, UNIT_SKIPPING); +/* mytrace(1, "skipping"); */ + } + + if (modify & PRT_CMD_STOP_CARRIAGE) { + CLRBIT(uptr->flags, UNIT_SKIPPING); +/* mytrace(0, "skipping"); */ + } + + if (modify & PRT_CMD_SPACE) { + SETBIT(uptr->flags, UNIT_SPACING); +/* mytrace(1, "space"); */ + } + + sim_cancel(uptr); + if (uptr->flags & (UNIT_SKIPPING|UNIT_SPACING|UNIT_PRINTING)) { /* busy bits = doing something */ + SETBIT(PRT_DSW, PRT1132_DSW_PRINTER_BUSY); + sim_activate(uptr, prt_cwait); + } + else + CLRBIT(PRT_DSW, PRT1132_DSW_PRINTER_BUSY); + + if (uptr->flags & (UNIT_SKIPPING|UNIT_SPACING)) + SETBIT(PRT_DSW, PRT1132_DSW_CARRIAGE_BUSY); + else + CLRBIT(PRT_DSW, PRT1132_DSW_CARRIAGE_BUSY); + + if ((uptr->flags & (UNIT_SKIPPING|UNIT_SPACING)) == (UNIT_SKIPPING|UNIT_SPACING)) { + sprintf(msg, "1132 printer skip and space at same time?"); + xio_error(msg); + } + break; + + default: + sprintf(msg, "Invalid 1132 printer XIO function %x", func); + xio_error(msg); + } +} + +#define SET_ACTION(u,a) {(u)->flags &= ~(UNIT_SKIPPING|UNIT_SPACING|UNIT_PRINTING|UNIT_TRANSFERRING); (u)->flags |= a;} + +static t_stat prt_svc (UNIT *uptr) +{ + return IS_1403(uptr) ? prt1403_svc(uptr) : prt1132_svc(uptr); +} + +/* prt1132_svc - emulated timeout for 1132 operation */ + +static t_stat prt1132_svc (UNIT *uptr) +{ + if (PRT_DSW & PRT1132_DSW_NOT_READY) { /* cancel operation if printer went offline */ + SETBIT(uptr->flags, UNIT_FORMCHECK); + SET_ACTION(uptr, 0); + forms_check(TRUE); /* and turn on forms check lamp */ + return SCPE_OK; + } + + if (uptr->flags & UNIT_SPACING) { + flush_prt_line(uptr->fileref, UNIT_SPACING, IS_PHYSICAL(uptr)); + + CLRBIT(PRT_DSW, PRT1132_DSW_CHANNEL_MASK|PRT1132_DSW_PRINTER_BUSY|PRT1132_DSW_CARRIAGE_BUSY); + SETBIT(PRT_DSW, cc_format_1132(cctape[prt_row]) | PRT1132_DSW_SPACE_RESPONSE); + SETBIT(ILSW[1], ILSW_1_1132_PRINTER); + CLRBIT(uptr->flags, UNIT_SPACING); /* done with this */ + calc_ints(); + } + + if (uptr->flags & UNIT_SKIPPING) { + do { + flush_prt_line(uptr->fileref, UNIT_SKIPPING, IS_PHYSICAL(uptr)); + CLRBIT(PRT_DSW, PRT1132_DSW_CHANNEL_MASK); + SETBIT(PRT_DSW, cc_format_1132(cctape[prt_row])); + } while ((cctape[prt_row] & CC_1132_BITS) == 0); /* slew directly to a cc tape punch */ + + SETBIT(PRT_DSW, cc_format_1132(cctape[prt_row]) | PRT1132_DSW_SKIP_RESPONSE); + SETBIT(ILSW[1], ILSW_1_1132_PRINTER); + calc_ints(); + } + + if (uptr->flags & UNIT_PRINTING) { + if (! save_1132_prt_line(codewheel1132[prt_nchar].ascii)) { /* save previous printed line */ + SETBIT(uptr->flags, UNIT_DATACHECK); /* buffer wasn't set in time */ + SET_ACTION(uptr, 0); + print_check(TRUE); /* and turn on forms check lamp */ + return SCPE_OK; + } + + prt_nchar = (prt_nchar + 1) % WHEELCHARS_1132; /* advance print drum */ + + SETBIT(PRT_DSW, PRT1132_DSW_READ_EMITTER_RESPONSE); /* issue interrupt to tell printer to set buffer */ + SETBIT(ILSW[1], ILSW_1_1132_PRINTER); /* we'll save the printed stuff just before next emitter response (later than on real 1130) */ + calc_ints(); + } + + if (uptr->flags & (UNIT_SPACING|UNIT_SKIPPING|UNIT_PRINTING)) { /* still doing something */ + SETBIT(PRT_DSW, PRT1132_DSW_PRINTER_BUSY); + sim_activate(uptr, prt_cwait); + } + else + CLRBIT(PRT_DSW, PRT1132_DSW_PRINTER_BUSY); + + return SCPE_OK; +} + +void save_1403_prt_line (int32 addr) +{ + int i, j, r, ch, even = TRUE; + unsigned char ebcdic; + int32 wd; + + for (i = 0; i < PRT1403_COLUMNS; i++) { + if (even) { /* fetch next word from memory */ + wd = M[addr++]; + ebcdic = (unsigned char) ((wd >> 8) & 0x7F); + even = FALSE; + } + else { + ebcdic = (unsigned char) (wd & 0x7F); /* use low byte of previously fetched word */ + even = TRUE; + } + + ch = ' '; /* translate ebcdic to ascii. Don't bother checking for parity errors */ + for (j = 0; j < WHEELCHARS_1403; j++) { + if (codewheel1403[j].ebcdic == ebcdic) { + ch = codewheel1403[j].ascii; + break; + } + } + + if (ch > ' ') { + if ((r = nprint[i]) < MAX_OVPRINT) { + if (ncol[r] <= i) { /* we haven't moved this far yet */ + if (ncol[r] == 0) /* first char in this row? */ + memset(prtbuf+r*MAX_COLUMNS, ' ', PRT1403_COLUMNS); /* blank out the new row */ + ncol[r] = i+1; /* remember new row length */ + } + prtbuf[r*MAX_COLUMNS + i] = (char) ch; /* save the character */ + + nprint[i]++; /* remember max overprintings for this column */ + maxnp = MAX(maxnp, nprint[i]); + } + } + } +} + +void xio_1403_printer (int32 iocc_addr, int32 func, int32 modify) +{ + UNIT *uptr = &prt_unit[0]; + + switch (func) { + case XIO_INITW: /* print a line */ + save_1403_prt_line(iocc_addr); /* put formatted line into our print buffer */ + + SETBIT(uptr->flags, UNIT_TRANSFERRING); /* schedule transfer complete interrupt */ + SETBIT(PRT_DSW, PRT1403_DSW_PRINTER_BUSY); + sim_activate(uptr, prt_twait); + break; + + case XIO_CONTROL: /* initiate single space */ + if (uptr->flags & UNIT_SKIPPING) { + xio_error("1403 printer skip and space at same time?"); + } + else { + SETBIT(uptr->flags, UNIT_SPACING); + SETBIT(PRT_DSW, PRT1403_DSW_CARRIAGE_BUSY); + sim_activate(uptr, prt_fwait); + } + break; + + case XIO_WRITE: /* initiate skip */ + if (uptr->flags & UNIT_SPACING) { + xio_error("1403 printer skip and space at same time?"); + } + else { + SETBIT(uptr->flags, UNIT_SKIPPING); + SKIPTARGET = ReadW(iocc_addr) & CC_1403_BITS; /* get CC bits that we're to match */ + SETBIT(PRT_DSW, PRT1403_DSW_CARRIAGE_BUSY); + sim_activate(uptr, prt_fwait); + } + break; + + case XIO_SENSE_DEV: /* get device status word */ + ACC = PRT_DSW; + if (modify & 0x01) { /* reset interrupts */ + CLRBIT(PRT_DSW, PRT1403_DSW_PARITY_CHECK | PRT1403_DSW_TRANSFER_COMPLETE | + PRT1403_DSW_PRINT_COMPLETE | PRT1403_DSW_CARRIAGE_COMPLETE | + PRT1403_DSW_RING_CHECK | PRT1403_DSW_SYNC_CHECK); + CLRBIT(ILSW[4], ILSW_4_1403_PRINTER); + } + break; + } +} + +static t_stat prt1403_svc(UNIT *uptr) +{ + if (PRT_DSW & PRT1403_DSW_NOT_READY) { /* cancel operation if printer went offline */ + SET_ACTION(uptr, 0); + forms_check(TRUE); /* and turn on forms check lamp */ + } + else if (uptr->flags & UNIT_TRANSFERRING) { /* end of transfer */ + CLRBIT(uptr->flags, UNIT_TRANSFERRING); + SETBIT(uptr->flags, UNIT_PRINTING); /* schedule "print complete" */ + + SETBIT(PRT_DSW, PRT1403_DSW_TRANSFER_COMPLETE); /* issue transfer complete interrupt */ + SETBIT(ILSW[4], ILSW_4_1403_PRINTER); + } + else if (uptr->flags & UNIT_PRINTING) { + CLRBIT(uptr->flags, UNIT_PRINTING); + CLRBIT(PRT_DSW, PRT1403_DSW_PRINTER_BUSY); + + SETBIT(PRT_DSW, PRT1403_DSW_PRINT_COMPLETE); + SETBIT(ILSW[4], ILSW_4_1403_PRINTER); /* issue print complete interrupt */ + } + else if (uptr->flags & UNIT_SKIPPING) { + do { /* find line with exact match of tape punches */ + flush_prt_line(uptr->fileref, UNIT_SKIPPING, IS_PHYSICAL(uptr)); + } while (cctape[prt_row] != SKIPTARGET); /* slew directly to requested cc tape punch */ + + CLRBIT(uptr->flags, UNIT_SKIPPING); /* done with this */ + CLRBIT(PRT_DSW, PRT1403_DSW_CARRIAGE_BUSY); + + SETBIT(PRT_DSW, PRT1403_DSW_CARRIAGE_COMPLETE); + SETBIT(ILSW[4], ILSW_4_1403_PRINTER); + } + else if (uptr->flags & UNIT_SPACING) { + flush_prt_line(uptr->fileref, UNIT_SPACING, IS_PHYSICAL(uptr)); + + CLRBIT(uptr->flags, UNIT_SPACING); /* done with this */ + CLRBIT(PRT_DSW, PRT1403_DSW_CARRIAGE_BUSY); + + SETBIT(PRT_DSW, PRT1403_DSW_CARRIAGE_COMPLETE); + SETBIT(ILSW[4], ILSW_4_1403_PRINTER); + } + + if (uptr->flags & (UNIT_PRINTING|UNIT_SKIPPING|UNIT_SPACING|UNIT_TRANSFERRING)) + sim_activate(uptr, prt_fwait); + + CLRBIT(PRT_DSW, PRT1403_DSW_CH9|PRT1403_DSW_CH12); /* set the two CC bits in the DSW */ + if (cctape[prt_row] & CC_CHANNEL_9) + SETBIT(PRT_DSW, PRT1403_DSW_CH9); + if (cctape[prt_row] & CC_CHANNEL_12) + SETBIT(PRT_DSW, PRT1403_DSW_CH12); + + calc_ints(); + return SCPE_OK; +} + +/* delete_cmd - SCP command to delete a file */ + +static t_stat delete_cmd (int flag, char *cptr) +{ + char gbuf[CBUFSIZE]; + int status; + + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*gbuf == 0) return SCPE_2FARG; + if (*cptr != 0) return SCPE_2MARG; /* now eol? */ + + status = remove(gbuf); /* delete the file */ + + if (status != 0 && errno != ENOENT) /* print message if failed and file exists */ + perror(gbuf); + + return SCPE_OK; +} + +/* prt_reset - reset emulated printer */ + +static t_stat prt_reset (DEVICE *dptr) +{ + UNIT *uptr = &prt_unit[0]; + int i; + +/* add a DELETE filename command so we can be sure to have clean listings */ + register_cmd("DELETE", &delete_cmd, 0, "del{ete} filename remove file\n"); + + sim_cancel(uptr); + + memset(cctape, 0, sizeof(cctape)); /* copy punch list into carriage control tape image */ + + if (cgi) { + for (i = 0; i < (sizeof(cccgi)/sizeof(cccgi[0])); i++) + cctape[cccgi[i].row-1] |= cccgi[i].channels; + } + else + for (i = 0; i < (sizeof(ccpunches)/sizeof(ccpunches[0])); i++) + cctape[ccpunches[i].row-1] |= ccpunches[i].channels; + + prt_nchar = 0; + prt_row = 0; + prt_nnl = 0; + + CLRBIT(uptr->flags, UNIT_FORMCHECK|UNIT_DATACHECK|UNIT_PRINTING|UNIT_SPACING|UNIT_SKIPPING| + UNIT_TRANSFERRING|UNIT_PARITYCHECK|UNIT_RINGCHECK|UNIT_SYNCCHECK); + + if (IS_1132(uptr)) { + CLRBIT(ILSW[1], ILSW_1_1132_PRINTER); + PRT_DSW = cc_format_1132(cctape[prt_row]); + if (! IS_ONLINE(uptr)) + SETBIT(PRT_DSW, PRT1132_DSW_NOT_READY); + } + else { + CLRBIT(ILSW[4], ILSW_4_1403_PRINTER); + PRT_DSW = 0; + if (cctape[prt_row] & CC_CHANNEL_9) + SETBIT(PRT_DSW, PRT1403_DSW_CH9); + if (cctape[prt_row] & CC_CHANNEL_12) + SETBIT(PRT_DSW, PRT1403_DSW_CH12); + if (! IS_ONLINE(uptr)) + SETBIT(PRT_DSW, PRT1403_DSW_NOT_READY); + } + + SET_ACTION(uptr, 0); + calc_ints(); + reset_prt_line(); + + forms_check(FALSE); + return SCPE_OK; +} + +static t_stat prt_attach (UNIT *uptr, char *cptr) +{ + t_stat rval; + /* assume failure */ + SETBIT(PRT_DSW, IS_1132(uptr) ? PRT1132_DSW_NOT_READY : PRT1403_DSW_NOT_READY); + formfed = FALSE; + + if (uptr->flags & UNIT_ATT) { + if ((rval = prt_detach(uptr)) != SCPE_OK) { + return rval; + } + } + + if (sim_switches & SWMASK('P')) /* set physical (unbuffered) printer flag */ + SETBIT(uptr->flags, UNIT_PHYSICAL_PTR); + else + CLRBIT(uptr->flags, UNIT_PHYSICAL_PTR); + + sim_cancel(uptr); + + if (strcmp(cptr, "(stdout)") == 0) { /* connect printer to stdout */ + if (uptr -> flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */ + uptr->filename = calloc(CBUFSIZE, sizeof(char)); + strcpy(uptr->filename, "(stdout)"); + uptr->fileref = stdout; + SETBIT(uptr->flags, UNIT_ATT); + uptr->pos = 0; + } + else { + if ((rval = attach_unit(uptr, quotefix(cptr))) != SCPE_OK) + return rval; + } + + fseek(uptr->fileref, 0, SEEK_END); /* if we opened an existing file, append to it */ + uptr->pos = ftell(uptr->fileref); + + if (IS_1132(uptr)) { + CLRBIT(ILSW[1], ILSW_1_1132_PRINTER); + CLRBIT(uptr->flags, UNIT_FORMCHECK|UNIT_DATACHECK); + } + else { + CLRBIT(ILSW[4], ILSW_4_1403_PRINTER); + CLRBIT(uptr->flags, UNIT_PARITYCHECK|UNIT_RINGCHECK|UNIT_SYNCCHECK); + } + + SET_ACTION(uptr, 0); + calc_ints(); + + prt_nchar = 0; + prt_nnl = 0; + prt_row = 0; + reset_prt_line(); + + if (IS_1132(uptr)) { + PRT_DSW = (PRT_DSW & ~PRT1132_DSW_CHANNEL_MASK) | cc_format_1132(cctape[prt_row]); + + if (IS_ONLINE(uptr)) + CLRBIT(PRT_DSW, PRT1132_DSW_NOT_READY); + } + else { + CLRBIT(PRT_DSW, PRT1403_DSW_CH9 | PRT1403_DSW_CH12); + if (cctape[prt_row] & CC_CHANNEL_9) + SETBIT(PRT_DSW, PRT1403_DSW_CH9); + if (cctape[prt_row] & CC_CHANNEL_12) + SETBIT(PRT_DSW, PRT1403_DSW_CH12); + + if (IS_ONLINE(uptr)) + CLRBIT(PRT_DSW, PRT1403_DSW_NOT_READY); /* fixed by Carl Claunch */ + } + + forms_check(FALSE); + + return SCPE_OK; +} + +static t_stat prt_detach (UNIT *uptr) +{ + t_stat rval; + + if (uptr->flags & UNIT_ATT) + flush_prt_line(uptr->fileref, TRUE, TRUE); + + if (uptr->fileref == stdout) { + CLRBIT(uptr->flags, UNIT_ATT); + free(uptr->filename); + uptr->filename = NULL; + } + else if ((rval = detach_unit(uptr)) != SCPE_OK) + return rval; + + sim_cancel(uptr); + + if (IS_1132(uptr)) { + CLRBIT(ILSW[1], ILSW_1_1132_PRINTER); + CLRBIT(uptr->flags, UNIT_FORMCHECK|UNIT_DATACHECK); + SETBIT(PRT_DSW, PRT1132_DSW_NOT_READY); + } + else { + CLRBIT(ILSW[4], ILSW_4_1403_PRINTER); + SETBIT(PRT_DSW, PRT1403_DSW_NOT_READY); + } + SET_ACTION(uptr, 0); + + calc_ints(); + + forms_check(FALSE); + return SCPE_OK; +} + diff --git a/Ibm1130/ibm1130_prtwheel.h b/Ibm1130/ibm1130_prtwheel.h new file mode 100644 index 0000000..c39c1e5 --- /dev/null +++ b/Ibm1130/ibm1130_prtwheel.h @@ -0,0 +1,126 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +struct tag_codewheel { + unsigned char ascii; + unsigned char ebcdic; +}; + +static struct tag_codewheel codewheel1132[] = +{ /* characters and EBCDIC codes in printwheel order */ + 'A', 0xC1, + 'B', 0xC2, + 'C', 0xC3, + 'D', 0xC4, + 'F', 0xC6, + 'H', 0xC8, + 'I', 0xC9, + 'S', 0xE2, + 'T', 0xE3, + 'U', 0xE4, + 'V', 0xE5, + '1', 0xF1, + '2', 0xF2, + '3', 0xF3, + '4', 0xF4, + '5', 0xF5, + '6', 0xF6, + '7', 0xF7, + '8', 0xF8, + '9', 0xF9, + '0', 0xF0, + '=', 0x7E, + '$', 0x5B, + '.', 0x4B, + '\'', 0x7D, + ',', 0x6B, + ')', 0x5D, + '-', 0x60, + '(', 0x4D, + '+', 0x4E, + '/', 0x61, + '*', 0x5C, + '&', 0x50, + 'J', 0xD1, + 'K', 0xD2, + 'L', 0xD3, + 'M', 0xD4, + 'N', 0xD5, + 'O', 0xD6, + 'P', 0xD7, + 'Q', 0xD8, + 'R', 0xD9, + 'E', 0xC5, + 'G', 0xC7, + 'W', 0xE6, + 'X', 0xE7, + 'Y', 0xE8, + 'Z', 0xE9, +}; + +#define WHEELCHARS_1132 (sizeof(codewheel1132)/sizeof(codewheel1132[0])) + +static struct tag_codewheel codewheel1403[] = +{ + 'A', 0x64, + 'B', 0x25, + 'C', 0x26, + 'D', 0x67, + 'E', 0x68, + 'F', 0x29, + 'G', 0x2A, + 'H', 0x6B, + 'I', 0x2C, + 'J', 0x58, + 'K', 0x19, + 'L', 0x1A, + 'M', 0x5B, + 'N', 0x1C, + 'O', 0x5D, + 'P', 0x5E, + 'Q', 0x1F, + 'R', 0x20, + 'S', 0x0D, + 'T', 0x0E, + 'U', 0x4F, + 'V', 0x10, + 'W', 0x51, + 'X', 0x52, + 'Y', 0x13, + 'Z', 0x54, + '0', 0x49, + '1', 0x40, + '2', 0x01, + '3', 0x02, + '4', 0x43, + '5', 0x04, + '6', 0x45, + '7', 0x46, + '8', 0x07, + '9', 0x08, + ' ', 0x7F, + '.', 0x6E, + '(', 0x57, + '+', 0x6D, + '&', 0x15, + '$', 0x62, + '*', 0x23, + ')', 0x2F, + '-', 0x61, + '/', 0x4C, + ',', 0x16, + '\'', 0x0B, + '=', 0x4A, +}; + +#define WHEELCHARS_1403 (sizeof(codewheel1403)/sizeof(codewheel1403[0])) + + diff --git a/Ibm1130/ibm1130_ptrp.c b/Ibm1130/ibm1130_ptrp.c new file mode 100644 index 0000000..71498e3 --- /dev/null +++ b/Ibm1130/ibm1130_ptrp.c @@ -0,0 +1,309 @@ +/* ibm1130_ptrp.c: IBM 1130 paper tape reader/punch emulation + + Based on the SIMH simulator package written by Robert M Supnik + + Brian Knittel + Revision History + + 2004.10.22 - Written. + + * (C) Copyright 2004, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + */ + +#include "ibm1130_defs.h" + +/*************************************************************************************** + * 1134 Paper Tape Reader device PTR + * 1055 Paper Tape Punch device PTP (shares DSW with PTR) + ***************************************************************************************/ + +#define PTR1134_DSW_READER_RESPONSE 0x4000 +#define PTR1134_DSW_PUNCH_RESPONSE 0x1000 +#define PTR1134_DSW_READER_BUSY 0x0800 +#define PTR1134_DSW_READER_NOT_READY 0x0400 +#define PTR1134_DSW_PUNCH_BUSY 0x0200 +#define PTR1134_DSW_PUNCH_NOT_READY 0x0100 + +#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT) + +static t_stat ptr_svc (UNIT *uptr); +static t_stat ptr_reset (DEVICE *dptr); +static t_stat ptr_attach (UNIT *uptr, char *cptr); +static t_stat ptr_detach (UNIT *uptr); +static t_stat ptr_boot (int unitno, DEVICE *dptr); +static t_stat ptp_svc (UNIT *uptr); +static t_stat ptp_reset (DEVICE *dptr); +static t_stat ptp_attach (UNIT *uptr, char *cptr); +static t_stat ptp_detach (UNIT *uptr); + +static int16 ptr_dsw = 0; /* device status word */ +static int32 ptr_wait = 1000; /* character read wait */ +static uint8 ptr_char = 0; /* last character read */ +static int32 ptp_wait = 1000; /* character punch wait */ + +UNIT ptr_unit[1] = { + { UDATA (&ptr_svc, UNIT_ATTABLE, 0) }, +}; + +REG ptr_reg[] = { + { HRDATA (DSW, ptr_dsw, 16) }, /* device status word */ + { DRDATA (WTIME, ptr_wait, 24), PV_LEFT }, /* character read wait */ + { DRDATA (LASTCHAR, ptr_char, 8), PV_LEFT }, /* last character read */ + { NULL } }; + +DEVICE ptr_dev = { + "PTR", ptr_unit, ptr_reg, NULL, + 1, 16, 16, 1, 16, 16, + NULL, NULL, ptr_reset, + ptr_boot, ptr_attach, ptr_detach}; + +UNIT ptp_unit[1] = { + { UDATA (&ptp_svc, UNIT_ATTABLE, 0) }, +}; + +REG ptp_reg[] = { + { HRDATA (DSW, ptr_dsw, 16) }, /* device status word (this is the same as the reader's!) */ + { DRDATA (WTIME, ptp_wait, 24), PV_LEFT }, /* character punch wait */ + { NULL } }; + +DEVICE ptp_dev = { + "PTP", ptp_unit, ptp_reg, NULL, + 1, 16, 16, 1, 16, 16, + NULL, NULL, ptp_reset, + NULL, ptp_attach, ptp_detach}; + +/* xio_1134_papertape - XIO command interpreter for the 1134 paper tape reader and 1055 paper tape punch */ + +void xio_1134_papertape (int32 iocc_addr, int32 iocc_func, int32 iocc_mod) +{ + char msg[80]; + + switch (iocc_func) { + case XIO_READ: /* read: return last character read */ + M[iocc_addr & mem_mask] = (uint16) (ptr_char << 8); + break; + + case XIO_WRITE: /* write: initiate punch operation */ + if ((ptr_dsw & PTR1134_DSW_PUNCH_NOT_READY) == 0 && IS_ONLINE(ptp_unit)) { + putc((M[iocc_addr & mem_mask] >> 8) & 0xFF, ptp_unit->fileref); + ptp_unit->pos++; + } + sim_activate(ptp_unit, ptp_wait); /* schedule interrupt */ + SETBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY | PTR1134_DSW_PUNCH_BUSY); + break; + + case XIO_SENSE_DEV: /* sense device status */ + ACC = ptr_dsw; + if (iocc_mod & 0x01) { /* reset interrupts */ + CLRBIT(ptr_dsw, PTR1134_DSW_READER_RESPONSE | PTR1134_DSW_PUNCH_RESPONSE); + CLRBIT(ILSW[4], ILSW_4_1134_TAPE); + } + break; + + case XIO_CONTROL: /* control: initiate character read */ + sim_activate(ptr_unit, ptr_wait); /* schedule interrupt */ + SETBIT(ptr_dsw, PTR1134_DSW_READER_BUSY | PTR1134_DSW_READER_NOT_READY); + break; + + default: + sprintf(msg, "Invalid 1134 reader/1055 punch XIO function %x", iocc_func); + xio_error(msg); + } +} + +/* ptr_svc - emulated timeout - 1134 read operation complete */ + +static t_stat ptr_svc (UNIT *uptr) +{ + CLRBIT(ptr_dsw, PTR1134_DSW_READER_BUSY); /* clear reader busy flag */ + SETBIT(ptr_dsw, PTR1134_DSW_READER_NOT_READY); /* assume at end of file */ + + if (IS_ONLINE(uptr)) { /* fetch character from file */ + ptr_char = getc(uptr->fileref); + uptr->pos++; + + if (! feof(uptr->fileref)) /* there's more left */ + CLRBIT(ptr_dsw, PTR1134_DSW_READER_NOT_READY); + } + + SETBIT(ptr_dsw, PTR1134_DSW_READER_RESPONSE); /* indicate read complete */ + + SETBIT(ILSW[4], ILSW_4_1134_TAPE); /* initiate interrupt */ + calc_ints(); + + return SCPE_OK; +} + +/* ptp_svc - emulated timeout -- 1055 punch operation complete */ + +static t_stat ptp_svc (UNIT *uptr) +{ + CLRBIT(ptr_dsw, PTR1134_DSW_PUNCH_BUSY); /* clear punch busy flag */ + + if (IS_ONLINE(uptr)) /* update punch ready status */ + CLRBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY); + else + SETBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY); + + SETBIT(ptr_dsw, PTR1134_DSW_PUNCH_RESPONSE); /* indicate punch complete */ + + SETBIT(ILSW[4], ILSW_4_1134_TAPE); /* initiate interrupt */ + calc_ints(); + + return SCPE_OK; +} + +/* ptr_reset - reset emulated paper tape reader */ + +static t_stat ptr_reset (DEVICE *dptr) +{ + sim_cancel(ptr_unit); + + CLRBIT(ptr_dsw, PTR1134_DSW_READER_BUSY | PTR1134_DSW_READER_RESPONSE); + SETBIT(ptr_dsw, PTR1134_DSW_READER_NOT_READY); + + if (IS_ONLINE(ptr_unit) && ! feof(ptr_unit->fileref)) + CLRBIT(ptr_dsw, PTR1134_DSW_READER_NOT_READY); + + if ((ptr_dsw & PTR1134_DSW_PUNCH_RESPONSE) == 0) { /* punch isn't interrupting either */ + CLRBIT(ILSW[4], ILSW_4_1134_TAPE); + calc_ints(); + } + + return SCPE_OK; +} + +/* ptp_reset - reset emulated paper tape punch */ + +static t_stat ptp_reset (DEVICE *dptr) +{ + sim_cancel(ptp_unit); + + CLRBIT(ptr_dsw, PTR1134_DSW_PUNCH_BUSY | PTR1134_DSW_PUNCH_RESPONSE); + SETBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY); + + if (IS_ONLINE(ptp_unit)) + CLRBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY); + + if ((ptr_dsw & PTR1134_DSW_READER_RESPONSE) == 0) { /* reader isn't interrupting either */ + CLRBIT(ILSW[4], ILSW_4_1134_TAPE); + calc_ints(); + } + + return SCPE_OK; +} + +/* ptr_attach - attach file to simulated paper tape reader */ + +static t_stat ptr_attach (UNIT *uptr, char *cptr) +{ + t_stat rval; + + SETBIT(ptr_dsw, PTR1134_DSW_READER_NOT_READY); /* assume failure */ + + if ((rval = attach_unit(uptr, cptr)) != SCPE_OK) /* use standard attach */ + return rval; + + if ((ptr_dsw & PTR1134_DSW_READER_BUSY) == 0 && ! feof(uptr->fileref)) + CLRBIT(ptr_dsw, PTR1134_DSW_READER_NOT_READY); /* we're in business */ + + return SCPE_OK; +} + +/* ptr_attach - detach file from simulated paper tape reader */ + +static t_stat ptr_detach (UNIT *uptr) +{ + SETBIT(ptr_dsw, PTR1134_DSW_READER_NOT_READY); + + return detach_unit(uptr); +} + +/* ptr_attach - perform paper tape initial program load */ + +static t_stat ptr_boot (int unitno, DEVICE *dptr) +{ + int ch, nch, val, addr; + t_bool leader = TRUE, start = FALSE; + t_stat rval; + + addr = 0; + nch = 0; + val = 0; + + for (;;) { + if ((ch = getc(ptr_unit->fileref)) == EOF) { + printf("EOF on paper tape without finding Channel 5 end-of-load mark\n"); + break; + } + + if (leader) { + if ((ch & 0x7F) == 0x7F) /* ignore leading rubouts or "delete" characters */ + continue; + + leader = FALSE; /* after first nonrubout, any punch in channel 5 terminates load */ + } + + /* this is untested -- not sure of actual byte ordering */ + + val = (val << 4) | (ch & 0x0F); /* get next nybble */ + + if (++nch == 4) { /* if we now have four nybbles, store the word */ + M[addr & mem_mask] = (uint16) val; + + addr++; /* prepare for next word */ + nch = 0; + val = 0; + } + + if (ch & 0x10) { /* channel 5 punch terminates load */ + start = TRUE; + break; + } + } + + if (! start) /* if we didn't get a valid load, report EOF error */ + return SCPE_EOF; + + if ((rval = reset_all(0)) != SCPE_OK) /* force a reset */ + return rval; + + IAR = 0; /* start running at address 0 */ + return SCPE_OK; +} + +/* ptp_attach - attach file to simulated paper tape punch */ + +static t_stat ptp_attach (UNIT *uptr, char *cptr) +{ + t_stat rval; + + SETBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY); /* assume failure */ + + if ((rval = attach_unit(uptr, cptr)) != SCPE_OK) /* use standard attach */ + return rval; + + fseek(uptr->fileref, 0, SEEK_END); /* if we opened an existing file, append to it */ + uptr->pos = ftell(uptr->fileref); + + if ((ptr_dsw & PTR1134_DSW_PUNCH_BUSY) == 0) + CLRBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY); /* we're in business */ + + return SCPE_OK; +} + +/* ptp_detach - detach file from simulated paper tape punch */ + +static t_stat ptp_detach (UNIT *uptr) +{ + SETBIT(ptr_dsw, PTR1134_DSW_PUNCH_NOT_READY); + + return detach_unit(uptr); +} diff --git a/Ibm1130/ibm1130_sca.c b/Ibm1130/ibm1130_sca.c new file mode 100644 index 0000000..3571166 --- /dev/null +++ b/Ibm1130/ibm1130_sca.c @@ -0,0 +1,1165 @@ +/* ibm1130_sca.c: IBM 1130 synchronous communications adapter emulation + + Based on the SIMH simulator package written by Robert M Supnik + + Brian Knittel + Revision History + + 2005.03.08 - Started + + * (C) Copyright 2005, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + */ + +/****************************************************************************************************************** + * NOTES: + * This module sends raw bisync data over a standard TCP port. It's meant to be + * used with the emulated 2703 device in Hercules. + * + * Attach command: + * + * to establish an outgoing connection: + * + * attach sca host connect to named host on default port (initial default port is 2703); or + * attach sca host:### connect to named host on port ###. ### is also set as new default port number. + * >> The simulator waits until the connection is established + * + * or to set up for incoming connections: + * + * attach sca -l dummy listen for a connection on default port (initially 2703); Nonnumeric "dummy" argument is ignored; or + * attach sca -l ### listen for a connection on the port ###. ### is also set as the new default port + * >> The simulator proceeds. When another simulator connects, the READY bit is set in the DSW. + * + * If the SCA's autoanswer-enable bit has been set, an incoming connection causes an interrupt (untested) + * Configuration commands: + * + * set sca bsc set bisync mode (default) + * set sca str set synchronous transmit/recieve mode (NOT IMPLEMENTED) + * + * set sca ### set simulated baud rate to ###, where ### is 600, 1200, 2000, 2400 or 4800 (4800 is default) + * + * set sca half set simulated half-duplex mode + * set sca full set simulated full-duplex mode (note: 1130's SCA can't actually send and receive at the same time!) + * + * deposit sca keepalive ### send SYN packets every ### msec when suppressing SYN's, default is 0 (no keepalives) + * + * STR/BSC mode is selected by a toggle switch on the 1130, with the SET SCA BSC or SET SET STR command here. + * Testable with in_bsc_mode() or in_str_mode() in this module. If necessary, the SET command can be configured + * to call a routine when the mode is changed; or, we can just required the user to reboot the simulated 1130 + * when switching modes. + * + * STR MODE IS NOT IMPLEMENTED! + * + * The SCA adapter appears to know nothing of the protocols used by STR and BSC. It does handle the sync/idle + * character specially, and between BSC and STR mode the timers are used differently. Also in STR mode it + * can be set to a sychronization mode where it sends SYN's without program intervention. + * + * See definition of SCA_STATE for defintion of simulator states. + * + * Rather than trying to simulate the actual baud rates, we try to keep the character service interrupts + * coming at about the same number of instruction intervals -- thus existing 1130 code should work correctly + * but the effective data transfer rate will be much higher. The "timers" however are written to run on real wall clock + * time, This may or may not work. If necessary they could be set to time out based on the number of calls to sca_svc + * which occurs once per character send/receive time; For example, at 4800 baud and an 8 bit frame, we get + * 600 characters/second, so the 3 second timer would expire in 1800 sca_svc calls. Well, that's something to + * think about. + * + * To void blowing zillions of SYN characters across the network when the system is running but idle, we suppress + * them. If 100 consecutive SYN's are sent, we flush the output buffer and stop sending SYN's + * until some other character is sent, OR the line is turned around (e.g. INITR, INITW or an end-operation + * CONTROL is issued), or the number of msec set by DEPOSIT SCS KEEPALIVE has passed, if a value has + * been set. By default no keepalives are sent. + * + * Timer operations are not debugged. First, do timers automatically reset and re-interrupt if + * left alone after they timeout the first time? Does XIO_SENSE_DEV really restart all running timers? + * Does it touch the timer trigger (program timer?) How do 3 and 1.25 second timers really work + * in BSC mode? Hard to tell from the FC manual. + ******************************************************************************************************************/ + +#include "ibm1130_defs.h" +#include "sim_sock.h" /* include path must include main simh directory */ +#include +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long)-1) +#endif + +#define DEBUG_SCA_FLUSH 0x0001 /* debugging options */ +#define DEBUG_SCA_TRANSMIT 0x0002 +#define DEBUG_SCA_CHECK_INDATA 0x0004 +#define DEBUG_SCA_RECEIVE_SYNC 0x0008 +#define DEBUG_SCA_RECEIVE_DATA 0x0010 +#define DEBUG_SCA_XIO_READ 0x0020 +#define DEBUG_SCA_XIO_WRITE 0x0040 +#define DEBUG_SCA_XIO_CONTROL 0x0080 +#define DEBUG_SCA_XIO_INITW 0x0100 +#define DEBUG_SCA_XIO_INITR 0x0200 +#define DEBUG_SCA_XIO_SENSE_DEV 0x0400 +#define DEBUG_SCA_TIMERS 0x0800 +#define DEBUG_SCA_ALL 0xFFFF + +/* #define DEBUG_SCA (DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_TRANSMIT|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_RECEIVE_SYNC|DEBUG_SCA_RECEIVE_DATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW) */ +#define DEBUG_SCA (DEBUG_SCA_TIMERS|DEBUG_SCA_FLUSH|DEBUG_SCA_CHECK_INDATA|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW) + +#define SCA_DEFAULT_PORT 2703 /* default socket, This is the number of the IBM 360's BSC device */ + +#define MAX_SYNS 100 /* number of consecutive syn's after which we stop buffering them */ + +/*************************************************************************************** + * SCA + ***************************************************************************************/ + +#define SCA_DSW_READ_RESPONSE 0x8000 /* receive buffer full interrupt */ +#define SCA_DSW_WRITE_RESPONSE 0x4000 /* transmitter buffer empty interrupt */ +#define SCA_DSW_CHECK 0x2000 /* data overrun or character gap error */ +#define SCA_DSW_TIMEOUT 0x1000 /* timer interrupt, mode specific */ +#define SCA_DSW_AUTOANSWER_REQUEST 0x0800 /* dataset is ringing and autoanswer is enabled */ +#define SCA_DSW_BUSY 0x0400 /* adapter is in either receive or transmit mode */ +#define SCA_DSW_AUTOANSWER_ENABLED 0x0200 /* 1 when autoanswer mode has been enabled */ +#define SCA_DSW_READY 0x0100 /* Carrier detect? Connected and ready to rcv, xmit or sync */ +#define SCA_DSW_RECEIVE_RUN 0x0080 /* used in two-wire half-duplex STR mode only. "Slave" mode (?) */ + +#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT) + +typedef enum { /* ms m = mode (0 = idle, 1 = send, 2 = receive), s = substate */ + SCA_STATE_IDLE = 0x00, /* nothing happening */ + SCA_STATE_TURN_SEND = 0x10, /* line turning around to the send state */ + SCA_STATE_SEND_SYNC = 0x11, /* sca is sending syncs */ + SCA_STATE_SEND1 = 0x12, /* have issued write response, waiting for write command */ + SCA_STATE_SEND2 = 0x13, /* write command issued, "sending" byte */ + SCA_STATE_TURN_RECEIVE = 0x20, /* line turnaround to the receive state */ + SCA_STATE_RECEIVE_SYNC = 0x21, /* sca is receiving syncs */ + SCA_STATE_RECEIVE_SYNC2 = 0x22, /* bsc mode, waiting for 2nd SYN */ + SCA_STATE_RECEIVE_SYNC3 = 0x23, /* bsc mode, waiting for 1st non-SYN */ + SCA_STATE_RECEIVE1 = 0x24, /* "receiving" a byte */ + SCA_STATE_RECEIVE2 = 0x25, /* read response issued, "receiving" next byte */ +} SCA_STATE; + +#define in_send_state() (sca_state & 0x10) +#define in_receive_state() (sca_state & 0x20) + +static t_stat sca_svc (UNIT *uptr); /* prototypes */ +static t_stat sca_reset (DEVICE *dptr); +static t_stat sca_attach (UNIT *uptr, char *cptr); +static t_stat sca_detach (UNIT *uptr); +static void sca_start_timer (int n, int msec_now); +static void sca_halt_timer (int n); +static void sca_toggle_timer (int n, int msec_now); + /* timer states, chosen so any_timer_running can be calculated by oring states of all 3 timers */ +typedef enum {SCA_TIMER_INACTIVE = 0, SCA_TIMER_RUNNING = 1, SCA_TIMER_INHIBITED = 2, SCA_TIMER_TIMEDOUT = 4} SCA_TIMER_STATE; + +#define TIMER_3S 0 /* 3 second timer index into sca_timer_xxx arrays */ +#define TIMER_125S 1 /* 1.25 second timer */ +#define TIMER_035S 2 /* 0.35 second timer */ + +static uint16 sca_dsw = 0; /* device status word */ +static uint32 sca_cwait = 275; /* inter-character wait */ +static uint32 sca_iwait = 2750; /* idle wait */ +static uint32 sca_state = SCA_STATE_IDLE; +static uint8 sichar = 0; /* sync/idle character */ +static uint8 rcvd_char = 0; /* most recently received character */ +static uint8 sca_frame = 8; +static uint16 sca_port = SCA_DEFAULT_PORT; /* listening port number */ +static int32 sca_keepalive = 0; /* keepalive SYN packet period in msec, default = 0 (disabled) */ +static SCA_TIMER_STATE sca_timer_state[3]; /* current timer state */ +static int sca_timer_endtime[3]; /* clocktime when timeout is to occur if state is RUNNING */ +static int sca_timer_timeleft[3]; /* time left in msec if state is INHIBITED */ +static t_bool any_timer_running = FALSE; /* TRUE if at least one timer is running */ +static int sca_timer_msec[3] = {3000, 1250, 350}; /* timebase in msec for the three timers: 3 sec, 1.25 sec, 0.35 sec */ +static t_bool sca_timer_trigger; /* if TRUE, the "timer trigger" is set, the 0.35s timer is running and the 3 sec and 1.25 sec timers are inhibited */ +static int sca_nsyns = 0; /* number of consecutively sent SYN's */ +static int idles_since_last_write = 0; /* used to detect when software has ceased sending data */ +static SOCKET sca_lsock = INVALID_SOCKET; +static SOCKET sca_sock = INVALID_SOCKET; + +#define SCA_SENDBUF_SIZE 145 /* maximum number of bytes to buffer for transmission */ +#define SCA_RCVBUF_SIZE 256 /* max number of bytes to read from socket at a time */ +#define SCA_SEND_THRESHHOLD 140 /* number of bytes to buffer before initiating packet send */ +#define SCA_IDLE_THRESHHOLD 3 /* maximum number of unintentional idles to buffer before initiating send */ + +static uint8 sca_sendbuf[SCA_SENDBUF_SIZE]; /* bytes pending to write to socket */ +static uint8 sca_rcvbuf[SCA_RCVBUF_SIZE]; /* bytes received from socket, to be given to SCA */ +static int sca_n2send = 0; /* number of bytes queued for transmission */ +static int sca_nrcvd = 0; /* number of received bytes in buffer */ +static int sca_rcvptr = 0; /* index of next byte to take from rcvbuf */ + +#define UNIT_V_BISYNC (UNIT_V_UF + 0) /* BSC (bisync) mode */ +#define UNIT_V_BAUD (UNIT_V_UF + 1) /* 3 bits for baud rate encoding */ +#define UNIT_V_FULLDUPLEX (UNIT_V_UF + 4) +#define UNIT_V_AUTOANSWER (UNIT_V_UF + 5) +#define UNIT_V_LISTEN (UNIT_V_UF + 6) /* listen socket mode */ + +#define UNIT_BISYNC (1u << UNIT_V_BISYNC) +#define UNIT_BAUDMASK (7u << UNIT_V_BAUD) +#define UNIT_BAUD600 (0u << UNIT_V_BAUD) +#define UNIT_BAUD1200 (1u << UNIT_V_BAUD) +#define UNIT_BAUD2000 (2u << UNIT_V_BAUD) +#define UNIT_BAUD2400 (3u << UNIT_V_BAUD) +#define UNIT_BAUD4800 (4u << UNIT_V_BAUD) +#define UNIT_FULLDUPLEX (1u << UNIT_V_FULLDUPLEX) +#define UNIT_AUTOANSWER (1u << UNIT_V_AUTOANSWER) +#define UNIT_LISTEN (1u << UNIT_V_LISTEN) + +extern int sim_switches; /* variable that gets bits set for -x switches on command lines */ + +t_stat sca_set_baud (UNIT *uptr, int32 value, char *cptr, void *desc); + +UNIT sca_unit = { /* default settings */ + UDATA (sca_svc, UNIT_ATTABLE|UNIT_BISYNC|UNIT_BAUD4800|UNIT_FULLDUPLEX, 0), +}; + +REG sca_reg[] = { /* DEVICE STATE/SETTABLE PARAMETERS: */ + { HRDATA (SCADSW, sca_dsw, 16) }, /* device status word */ + { DRDATA (SICHAR, sichar, 8), PV_LEFT }, /* sync/idle character */ + { DRDATA (RCVDCHAR, rcvd_char, 8), PV_LEFT }, /* most recently received character */ + { DRDATA (FRAME, sca_frame, 8), PV_LEFT }, /* frame bits (6, 7 or 8) + { DRDATA (SCASTATE, sca_state, 32), PV_LEFT }, /* current state */ + { DRDATA (CTIME, sca_cwait, 32), PV_LEFT }, /* inter-character wait */ + { DRDATA (ITIME, sca_iwait, 32), PV_LEFT }, /* idle wait (polling interval for socket connects) */ + { DRDATA (SCASOCKET, sca_port, 16), PV_LEFT }, /* listening port number */ + { DRDATA (KEEPALIVE, sca_keepalive, 32), PV_LEFT }, /* keepalive packet period in msec */ + { NULL } }; + +MTAB sca_mod[] = { /* DEVICE OPTIONS */ + { UNIT_BISYNC, 0, "STR", "STR", NULL }, /* mode option */ + { UNIT_BISYNC, UNIT_BISYNC, "BSC", "BSC", NULL }, + { UNIT_BAUDMASK, UNIT_BAUD600, "600", "600", sca_set_baud }, /* data rate option */ + { UNIT_BAUDMASK, UNIT_BAUD1200, "1200", "1200", sca_set_baud }, + { UNIT_BAUDMASK, UNIT_BAUD2000, "2000", "2000", sca_set_baud }, + { UNIT_BAUDMASK, UNIT_BAUD2400, "2400", "2400", sca_set_baud }, + { UNIT_BAUDMASK, UNIT_BAUD4800, "4800", "4800", sca_set_baud }, + { UNIT_FULLDUPLEX, 0, "HALF", "HALF", NULL }, /* duplex option (does this matter?) */ + { UNIT_FULLDUPLEX, UNIT_FULLDUPLEX, "FULL", "FULL", NULL }, + { 0 } }; + +DEVICE sca_dev = { + "SCA", &sca_unit, sca_reg, sca_mod, + 1, 16, 16, 1, 16, 16, + NULL, NULL, sca_reset, + NULL, sca_attach, sca_detach +}; + +/********************************************************************************************* + * sca_set_baud - set baud rate handler (SET SCA.BAUD nnn) + *********************************************************************************************/ + +t_stat sca_set_baud (UNIT *uptr, int32 value, char *cptr, void *desc) +{ + uint32 newbits; + + switch (value) { + case 600: newbits = UNIT_BAUD600; break; + case 1200: newbits = UNIT_BAUD1200; break; + case 2000: newbits = UNIT_BAUD2000; break; + case 2400: newbits = UNIT_BAUD2400; break; + case 4800: newbits = UNIT_BAUD4800; break; + default: return SCPE_ARG; + } + + CLRBIT(sca_unit.flags, UNIT_BAUDMASK); + SETBIT(sca_unit.flags, newbits); + + sca_cwait = 1320000 / value; /* intercharacter wait time in instructions (roughly) */ + + return SCPE_OK; +} + +/********************************************************************************************* + * HANDY MACROS + *********************************************************************************************/ + +#define in_bsc_mode() (sca_unit.flags & UNIT_BISYNC) /* TRUE if user selected BSC mode */ +#define in_str_mode() ((sca_unit.flags & UNIT_BISYNC) == 0) /* TRUE if user selected STR mode */ + +/********************************************************************************************* + * mstring - allocate a copy of a string + *********************************************************************************************/ + +char *mstring (char *str) +{ + int len; + char *m; + + len = strlen(str)+1; + if ((m = malloc(len)) == NULL) { + printf("Out of memory!"); + return "?"; /* this will of course cause trouble if it's subsequently freed */ + } + strcpy(m, str); + return m; +} + +/********************************************************************************************* + * sca_socket_error - call when there is an error reading from or writing to socket + *********************************************************************************************/ + +static void sca_socket_error (void) +{ + char name[100]; + + /* print diagnostic? */ + printf("SCA socket error, closing connection\n"); + + /* tell 1130 that connection was lost */ + CLRBIT(sca_dsw, SCA_DSW_READY); + + if (sca_sock != INVALID_SOCKET) { + /* close socket, prepare to listen again if in listen mode. It's a "master" socket if it was an outgoing connection */ + sim_close_sock(sca_sock, (sca_unit.flags & UNIT_LISTEN) == 0); + sca_sock = INVALID_SOCKET; + + if (sca_unit.filename != NULL) /* reset filename string in unit record */ + free(sca_unit.filename); + + if (sca_unit.flags & UNIT_LISTEN) { + sprintf(name, "(Listening on port %d)", sca_port); + sca_unit.filename = mstring(name); + printf("%s\n", name); + } + else + sca_unit.filename = mstring("(connection failed)"); + } + + /* clear buffers */ + sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0; +} + +/********************************************************************************************* + * sca_transmit_byte, sca_flush - send data buffering mechanism + *********************************************************************************************/ + +static void sca_flush (void) +{ + int nbytes; + + if (sca_n2send > 0) { +#if (DEBUG_SCA & DEBUG_SCA_FLUSH) + printf("* SCA_FLUSH %d byte%s\n", sca_n2send, (sca_n2send == 1) ? "" : "s"); +#endif + + if (sca_sock != INVALID_SOCKET) { + nbytes = sim_write_sock(sca_sock, sca_sendbuf, sca_n2send); + + if (nbytes == SOCKET_ERROR) + sca_socket_error(); + else if (nbytes != sca_n2send) + printf("SOCKET BLOCKED -- NEED TO REWRITE IBM1130_SCA.C"); + + /* I'm going to assume that SCA communications on the 1130 will consist entirely */ + /* of back and forth exchanges of short records, and so we should never stuff the pipe so full that */ + /* it blocks. If it does ever block, we'll have to come up with an asychronous buffering mechanism. */ + } + + sca_n2send = 0; /* mark buffer cleared */ + } +} + +/********************************************************************************************* + * sca_transmit_byte - buffer a byte to be send to the socket + *********************************************************************************************/ + +static void sca_transmit_byte (uint8 b) +{ + uint32 curtime; + static uint32 last_syn_time, next_syn_time; + +#if (DEBUG_SCA & DEBUG_SCA_TRANSMIT) + printf("* SCA_TRANSMIT: %02x\n", b); +#endif + + /* write a byte to the socket. Let's assume an 8 bit frame in all cases. + * We buffer them up, then send the packet when (a) it fills up to a certain point + * and/or (b) some time has passed? We handle (b) by: + * checking in sva_svc if several sca_svc calls are made w/o any XIO_WRITES, and + * flushing send buffer on line turnaround, timeouts, or any other significant state change + */ + + /* on socket error, call sca_socket_error(); */ + + if (b == sichar) { + if (sca_nsyns >= MAX_SYNS) { /* we're suppressing SYN's */ + if (sca_keepalive > 0) { /* we're sending keepalives, though... check to see if it's time */ + curtime = sim_os_msec(); + if (curtime >= next_syn_time || curtime < last_syn_time) { /* check for < last_syn_time because sim_os_msec() can wrap when OS has been running a long time */ + sca_sendbuf[sca_n2send++] = b; + sca_sendbuf[sca_n2send++] = b; /* send 2 of them */ + sca_flush(); + last_syn_time = curtime; + next_syn_time = last_syn_time + sca_keepalive; + } + } + return; + } + + if (++sca_nsyns == MAX_SYNS) { /* we've sent a bunch of SYN's, time to stop sending them */ + sca_sendbuf[sca_n2send] = b; /* send last one */ + sca_flush(); + last_syn_time = sim_os_msec(); /* remember time, and note time to send next one */ + next_syn_time = last_syn_time + sca_keepalive; + return; + } + } + else + sca_nsyns = 0; + + sca_sendbuf[sca_n2send] = b; /* store character */ + + if (++sca_n2send >= SCA_SEND_THRESHHOLD) + sca_flush(); /* buffer is full, send it immediately */ +} + +/********************************************************************************************* + * sca_interrupt (utility routine) - set a bit in the device status word and initiate an interrupt + *********************************************************************************************/ + +static void sca_interrupt (int bit) +{ + sca_dsw |= bit; /* set device status word bit(s) */ + SETBIT(ILSW[1], ILSW_1_SCA); /* set interrupt level status word bit */ + + calc_ints(); /* udpate simulator interrupt status (not really needed if within xio handler, since ibm1130_cpu calls it after xio handler) */ +} + +/********************************************************************************************* + * sca_reset - reset the SCA device + *********************************************************************************************/ + +static t_stat sca_reset (DEVICE *dptr) +{ + /* flush any pending data */ + sca_flush(); + sca_nrcvd = sca_rcvptr = sca_n2send = sca_nsyns = 0; + + /* reset sca activity */ + sca_state = SCA_STATE_IDLE; + CLRBIT(sca_dsw, SCA_DSW_BUSY | SCA_DSW_AUTOANSWER_ENABLED | SCA_DSW_RECEIVE_RUN | SCA_DSW_READ_RESPONSE | SCA_DSW_WRITE_RESPONSE | SCA_DSW_CHECK | SCA_DSW_TIMEOUT | SCA_DSW_AUTOANSWER_REQUEST); + sca_timer_state[0] = sca_timer_state[1] = sca_timer_state[2] = SCA_TIMER_INACTIVE; + any_timer_running = FALSE; + sca_timer_trigger = FALSE; + + if (sca_unit.flags & UNIT_ATT) /* if unit is attached (or listening) */ + sim_activate(&sca_unit, sca_iwait); /* poll for service. Must do this here as BOOT clears activity queue before resetting all devices */ + + return SCPE_OK; +} + +/********************************************************************************************* + * sca_attach - attach the SCA device + *********************************************************************************************/ + +static t_stat sca_attach (UNIT *uptr, char *cptr) +{ + t_bool do_listen; + char *colon; + uint32 ipaddr; + int32 port; + struct hostent *he; + char name[256]; + static SOCKET sdummy = INVALID_SOCKET; + fd_set wr_set, err_set; + + do_listen = sim_switches & SWMASK('L'); /* -l means listen mode */ + + if (sca_unit.flags & UNIT_ATT) /* if already attached, detach */ + detach_unit(&sca_unit); + + if (do_listen) { /* if listen mode, string specifies socket number (only; otherwise it's a dummy argument) */ + if (isdigit(*cptr)) { /* if digits specified, extract port number */ + port = atoi(cptr); + if (port <= 0 || port > 65535) + return SCPE_ARG; + else + sca_port = port; + } + /* else if nondigits specified, ignore... but the command has to have something there otherwise the core scp */ + /* attach_cmd() routine complains "too few arguments". */ + + if ((sca_lsock = sim_master_sock(sca_port)) == INVALID_SOCKET) + return SCPE_OPENERR; + + SETBIT(sca_unit.flags, UNIT_LISTEN); /* note that we are listening, not yet connected */ + + sprintf(name, "(Listening on port %d)", sca_port); + sca_unit.filename = mstring(name); + printf("%s\n", name); + + } + else { + while (*cptr && *cptr <= ' ') + cptr++; + + if (! *cptr) + return SCPE_2FARG; + + if ((colon = strchr(cptr, ':')) != NULL) { + *colon++ = '\0'; /* clip hostname at colon */ + + port = atoi(colon); /* extract port number that follows it */ + if (port <= 0 || port > 65535) + return SCPE_ARG; + else + sca_port = port; + } + + if (sdummy == INVALID_SOCKET) + if ((sdummy = sim_create_sock()) == INVALID_SOCKET) /* create and keep a socket, to force initialization */ + return SCPE_IERR; /* of socket library (e.g on Win32 call WSAStartup), else gethostbyname fails */ + + if (get_ipaddr(cptr, &ipaddr, NULL) != SCPE_OK) { /* try to parse hostname as dotted decimal nnn.nnn.nnn.nnn */ + if ((he = gethostbyname(cptr)) == NULL) /* if not decimal, look up name through DNS */ + return SCPE_OPENERR; + + if ((ipaddr = * (unsigned long *) he->h_addr_list[0]) == INADDR_NONE) + return SCPE_OPENERR; + + ipaddr = ntohl(ipaddr); /* convert to host byte order; gethostbyname() gives us network order */ + } + + if ((sca_sock = sim_connect_sock(ipaddr, sca_port)) == INVALID_SOCKET) + return SCPE_OPENERR; + + /* sim_connect_sock() sets socket to nonblocking before initiating the connect, so + * the connect is pending when it returns. For outgoing connections, the attach command should wait + * until the connection succeeds or fails. We use "accept" to wait and find out which way it goes... + */ + + FD_ZERO(&wr_set); /* we are only interested in info for sca_sock */ + FD_ZERO(&err_set); + FD_SET(sca_sock, &wr_set); + FD_SET(sca_sock, &err_set); + + select(3, NULL, &wr_set, &err_set, NULL); /* wait for connection to complete or fail */ + + if (FD_ISSET(sca_sock, &wr_set)) { /* sca_sock appears in "writable" set -- connect completed */ + sprintf(name, "%s:%d", cptr, sca_port); + sca_unit.filename = mstring(name); + SETBIT(sca_dsw, SCA_DSW_READY); + } + else if (FD_ISSET(sca_sock, &err_set)) { /* sca_sock appears in "error" set -- connect failed */ + sim_close_sock(sca_sock, TRUE); + sca_sock = INVALID_SOCKET; + return SCPE_OPENERR; + } + else { /* if we get here my assumption about how select works is wrong */ + printf("SCA_SOCK NOT FOUND IN WR_SET -OR- ERR_SET, CODING IN IBM1130_SCA IS WRONG\n"); + sim_close_sock(sca_sock, TRUE); + sca_sock = INVALID_SOCKET; + return SCPE_OPENERR; + } + } + + /* set up socket connect or listen. on success, set UNIT_ATT. + * If listen mode, set UNIT_LISTEN. sca_svc will poll for connection + * If connect mode, set dsw SCA_DSW_READY to indicate connection is up + */ + + SETBIT(sca_unit.flags, UNIT_ATT); /* record successful socket binding */ + + sca_state = SCA_STATE_IDLE; + sim_activate(&sca_unit, sca_iwait); /* start polling for service */ + + sca_n2send = 0; /* clear buffers */ + sca_nrcvd = 0; + sca_rcvptr = 0; + sca_nsyns = 0; + + return SCPE_OK; +} + +/********************************************************************************************* + * sca_detach - detach the SCA device + *********************************************************************************************/ + +static t_stat sca_detach (UNIT *uptr) +{ + if ((sca_unit.flags & UNIT_ATT) == 0) + return SCPE_OK; + + sca_flush(); + + sca_state = SCA_STATE_IDLE; /* stop processing during service calls */ + sim_cancel(&sca_unit); /* stop polling for service */ + + CLRBIT(sca_dsw, SCA_DSW_READY); /* indicate offline */ + + if (sca_sock != INVALID_SOCKET) { /* close connected socket */ + sim_close_sock(sca_sock, (sca_unit.flags & UNIT_LISTEN) == 0); + sca_sock = INVALID_SOCKET; + } + if (sca_lsock != INVALID_SOCKET) { /* close listening socket */ + sim_close_sock(sca_lsock, TRUE); + sca_lsock = INVALID_SOCKET; + } + + free(sca_unit.filename); + sca_unit.filename = NULL; + + CLRBIT(sca_unit.flags, UNIT_ATT|UNIT_LISTEN); + + return SCPE_OK; +} + +/********************************************************************************************* + * sca_check_connect - see if an incoming socket connection has com + *********************************************************************************************/ + +static void sca_check_connect (void) +{ + uint32 ipaddr; + char name[100]; + + if ((sca_sock = sim_accept_conn(sca_lsock, &ipaddr)) == INVALID_SOCKET) + return; + + ipaddr = htonl(ipaddr); /* convert to network order so we can print it */ + + sprintf(name, "%d.%d.%d.%d", ipaddr & 0xFF, (ipaddr >> 8) & 0xFF, (ipaddr >> 16) & 0xFF, (ipaddr >> 24) & 0xFF); + + printf("(SCA connection from %s)\n", name); + + if (sca_unit.filename != NULL) + free(sca_unit.filename); + + sca_unit.filename = mstring(name); + + SETBIT(sca_dsw, SCA_DSW_READY); /* indicate active connection */ + + if (sca_dsw & SCA_DSW_AUTOANSWER_ENABLED) /* if autoanswer was enabled, I guess we should give them an interrupt. Untested. */ + sca_interrupt(SCA_DSW_AUTOANSWER_REQUEST); +} + +/********************************************************************************************* + * sca_check_indata - try to fill receive buffer from socket + *********************************************************************************************/ + +static void sca_check_indata (void) +{ + int nbytes; + + sca_rcvptr = 0; /* reset pointers and count */ + sca_nrcvd = 0; + +#ifdef FAKE_SCA + + nbytes = 5; /* FAKE: receive SYN SYN SYN SYN DLE ACK0 */ + sca_rcvbuf[0] = 0x32; + sca_rcvbuf[1] = 0x32; + sca_rcvbuf[2] = 0x32; + sca_rcvbuf[3] = 0x10; + sca_rcvbuf[4] = 0x70; + +#else + /* read socket; 0 is returned if no data is available */ + nbytes = sim_read_sock(sca_sock, sca_rcvbuf, SCA_RCVBUF_SIZE); + +#endif + + if (nbytes < 0) + sca_socket_error(); + else /* zero or more */ + sca_nrcvd = nbytes; + +#if (DEBUG_SCA & DEBUG_SCA_CHECK_INDATA) + if (sca_nrcvd > 0) + printf("* SCA_CHECK_INDATA %d byte%s\n", sca_nrcvd, (sca_nrcvd == 1) ? "" : "s"); +#endif +} + +/********************************************************************************************* + * sca_svc - handled scheduled event. This will presumably be scheduled frequently, and can check + * for incoming data, reasonableness of initiating a write interrupt, timeouts etc. + *********************************************************************************************/ + +static t_stat sca_svc (UNIT *uptr) +{ + t_bool timeout; + int msec_now; + int i; + + /* if not connected, and if in wait-for-connection mode, check for connection attempt */ + if ((sca_unit.flags & UNIT_LISTEN) && ! (sca_dsw & SCA_DSW_READY)) + sca_check_connect(); + + if (any_timer_running) { + msec_now = sim_os_msec(); + + timeout = FALSE; + for (i = 0; i < 3; i++) { + if (sca_timer_state[i] == SCA_TIMER_RUNNING && msec_now >= sca_timer_endtime[i]) { + timeout = TRUE; + sca_timer_state[i] = SCA_TIMER_TIMEDOUT; +#if (DEBUG_SCA & DEBUG_SCA_TIMERS) + printf("+ SCA_TIMER %d timed out\n", i); +#endif + + if (i == TIMER_035S && sca_timer_trigger) { + sca_timer_trigger = FALSE; /* uninhibit the other two timers */ + sca_toggle_timer(TIMER_3S, msec_now); + sca_toggle_timer(TIMER_125S, msec_now); + } + } + } + + if (timeout) + sca_interrupt(SCA_DSW_TIMEOUT); + + any_timer_running = (sca_timer_state[0]| sca_timer_state[1] | sca_timer_state[2]) & SCA_TIMER_RUNNING; + } + + if (sca_dsw & SCA_DSW_READY) { /* if connected */ + + /* if rcvd data buffer is empty, and if in one of the receive states, checÄk for arrival of received data */ + if (in_receive_state() && sca_rcvptr >= sca_nrcvd) + sca_check_indata(); + + switch (sca_state) { + case SCA_STATE_IDLE: + break; + + case SCA_STATE_TURN_SEND: + /* has enough time gone by yet? if so... */ + sca_state = SCA_STATE_SEND1; + sca_interrupt(SCA_DSW_WRITE_RESPONSE); + break; + + case SCA_STATE_SEND_SYNC: + sca_transmit_byte(sichar); + break; + + case SCA_STATE_SEND1: + sca_transmit_byte(sichar); /* character interval has passed with no character written? character gap check */ + sca_interrupt(SCA_DSW_CHECK); /* send an idle character (maybe? for socket IO maybe send nothing) and interrupt */ + + if (idles_since_last_write >= 0) { + if (++idles_since_last_write >= SCA_IDLE_THRESHHOLD) { + sca_flush(); + idles_since_last_write = -1; /* don't send a short packet again until real data gets written again */ + sca_nsyns = 0; /* resume sending syns if output starts up again */ + } + } + break; + + case SCA_STATE_SEND2: + sca_state = SCA_STATE_SEND1; /* character has been sent. Schedule another transmit */ + sca_interrupt(SCA_DSW_WRITE_RESPONSE); + break; + + case SCA_STATE_TURN_RECEIVE: + /* has enough time gone by yet? if so... */ + sca_state = SCA_STATE_RECEIVE_SYNC; /* assume a character is coming in */ + break; + + case SCA_STATE_RECEIVE_SYNC: + if (sca_rcvptr < sca_nrcvd) { + rcvd_char = sca_rcvbuf[sca_rcvptr++]; +#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC) + printf("* SCA rcvd %02x %s\n", rcvd_char, (rcvd_char == sichar) ? "sync1" : "ignored"); +#endif + if (in_bsc_mode()) { + if (rcvd_char == sichar) /* count the SYN but don't interrupt */ + sca_state = SCA_STATE_RECEIVE_SYNC2; + } + } + break; + + case SCA_STATE_RECEIVE_SYNC2: + if (sca_rcvptr < sca_nrcvd) { + rcvd_char = sca_rcvbuf[sca_rcvptr++]; +#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC) + printf("* SCA rcvd %02x %s\n", rcvd_char, (rcvd_char == sichar) ? "sync2" : "ignored"); +#endif + if (in_bsc_mode()) { + if (rcvd_char == sichar) /* count the SYN but don't interrupt */ + sca_state = SCA_STATE_RECEIVE_SYNC3; + } + } + break; + + case SCA_STATE_RECEIVE_SYNC3: + case SCA_STATE_RECEIVE1: + if (sca_rcvptr < sca_nrcvd) { + rcvd_char = sca_rcvbuf[sca_rcvptr++]; + + if (sca_state == SCA_STATE_RECEIVE_SYNC3 && rcvd_char == sichar) { + /* we're ready for data, but we're still seeing SYNs */ +#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_SYNC) + printf("* SCA rcvd %02x extra sync\n", rcvd_char); +#endif + } + else { +#if (DEBUG_SCA & DEBUG_SCA_RECEIVE_DATA) + printf("* SCA rcvd %02x\n", rcvd_char); +#endif + sca_interrupt(SCA_DSW_READ_RESPONSE); + sca_state = SCA_STATE_RECEIVE2; + } + } + /* otherwise remain in state until data becomes available */ + break; + + case SCA_STATE_RECEIVE2: /* if we are still in this state when another service interval has passed */ + if (sca_rcvptr < sca_nrcvd) { + rcvd_char = sca_rcvbuf[sca_rcvptr++]; + + sca_interrupt(SCA_DSW_CHECK); /* overrun error */ + sca_state = SCA_STATE_RECEIVE1; /* another character will come soon */ + } + break; + + default: + printf("Simulator error: unknown state %d in sca_svc\n", sca_state); + sca_state = SCA_STATE_IDLE; + break; + } + } + /* schedule service again */ + sim_activate(&sca_unit, (sca_state == SCA_STATE_IDLE) ? sca_iwait : sca_cwait); + + return SCPE_OK; +} + +/********************************************************************************************* + * sca_toggle_timer - toggle a given timer's running/inhibited state for XIO_CONTROL + *********************************************************************************************/ + +static void sca_toggle_timer (int n, int msec_now) +{ + if (sca_timer_state[n] == SCA_TIMER_RUNNING && sca_timer_trigger) { + sca_timer_state[n] = SCA_TIMER_INHIBITED; + sca_timer_timeleft[n] = sca_timer_endtime[n] - msec_now; /* save time left */ +#if (DEBUG_SCA & DEBUG_SCA_TIMERS) + printf("+ SCA_TIMER %d inhibited\n", n); +#endif + } + else if (sca_timer_state[n] == SCA_TIMER_INHIBITED && ! sca_timer_trigger) { + sca_timer_state[n] = SCA_TIMER_RUNNING; + sca_timer_endtime[n] = sca_timer_timeleft[n] + msec_now; /* compute new endtime */ +#if (DEBUG_SCA & DEBUG_SCA_TIMERS) + printf("+ SCA_TIMER %d uninhibited\n", n); +#endif + } +} + +static void sca_start_timer (int n, int msec_now) +{ + sca_timer_state[n] = SCA_TIMER_RUNNING; + sca_timer_endtime[n] = sca_timer_msec[n] + msec_now; + any_timer_running = TRUE; +#if (DEBUG_SCA & DEBUG_SCA_TIMERS) + printf("+ SCA_TIMER %d started\n", n); +#endif +} + +static void sca_halt_timer (int n) +{ +#if (DEBUG_SCA & DEBUG_SCA_TIMERS) + if (sca_timer_state[n] != SCA_TIMER_INACTIVE) + printf("+ SCA_TIMER %d stopped\n", n); +#endif + + sca_timer_state[n] = SCA_TIMER_INACTIVE; +} + +/********************************************************************************************* + * sca_start_transmit - initiate transmit mode, from XIO_INITR or XIO_CONTROL (sync mode) + *********************************************************************************************/ + +void sca_start_transmit (int32 iocc_addr, int32 modify) +{ + sca_flush(); + sca_nsyns = 0; /* reset SYN suppression */ + + /* Address bits are used to reset DSW conditions. */ + + if (modify & 0x40) /* bit 9. If set, reset all conditions */ + iocc_addr = 0xD800; + + iocc_addr &= 0xD800; /* look at just bits 0, 1, 3 and 4 */ + if (iocc_addr) { /* if set, these bits clear DSW conditions */ + CLRBIT(sca_dsw, iocc_addr); + CLRBIT(ILSW[1], ILSW_1_SCA); /* and I assume clear the interrupt condition too? (Seems unlikely that INITW would */ + } /* be used in an interrupt service routine before SENSE, but who knows?) */ + + if (! in_send_state()) { + sca_state = SCA_STATE_TURN_SEND; /* begin line turnaround */ + } + else { + sca_state = SCA_STATE_SEND1; /* line is */ + sca_interrupt(SCA_DSW_WRITE_RESPONSE); + } + + SETBIT(sca_dsw, SCA_DSW_BUSY); /* SCA is now busy, in transmit mode */ + + sim_cancel(&sca_unit); + sim_activate(&sca_unit, sca_cwait); /* start polling frequently */ +} + +/********************************************************************************************* + * xio_sca - handle SCA XIO command + *********************************************************************************************/ + +void xio_sca (int32 iocc_addr, int32 func, int32 modify) +{ + char msg[80]; + int i, msec_now; + + switch (func) { + case XIO_READ: /* ***** XIO_READ - store last-received character to memory */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_READ) + printf("SCA RD addr %04x mod %02x rcvd_char %02x\n", iocc_addr, modify, rcvd_char); +#endif + if (modify & 0x03) { /* bits 14 and 15 */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_READ) + printf("(rd diag)\n"); +#endif + /* if either of low two modifier bits is set, reads diagnostic words. whatever that is */ + } + else { + WriteW(iocc_addr, rcvd_char << 8); /* always return last received character */ + + /* note: in read mode, read w/o interrupt (or two reads after an interrupt) causes a check + * so here we have to check the current state and possibly cause an interrupt + * Failure to have read before another arrives (overrun) also causes a check. + */ + + if (sca_state == SCA_STATE_RECEIVE2)/* XIO_READ should only be done (and only once) after a character interrupt */ + sca_state = SCA_STATE_RECEIVE1; /* assume another character is coming in -- wait for it */ + else + sca_interrupt(SCA_DSW_CHECK); + } + break; + + case XIO_WRITE: /* ***** XIO_WRITE - transfer character from memory to output shift register */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE) + printf("SCA WRT addr %04x (%04x) mod %02x\n", iocc_addr, M[iocc_addr & mem_mask], modify); +#endif + if (modify & 0x01) { /* bit 15 */ + /* clear audible alarm trigger */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE) + printf("(clr audible alarm trigger)\n"); +#endif + } + /* else? or can they all operate in parallel? */ + if (modify & 0x02) { /* bit 14 */ + /* set audible alarm trigger */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE) + printf("(set audible alarm trigger)\n"); +#endif + } + /* else? */ + if (modify & 0x04) { /* bit 13 */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_WRITE) + printf("(set SYN)\n"); +#endif + /* set sync/idle character */ + sichar = (uint8) (ReadW(iocc_addr) >> 8); + sca_nsyns = 0; /* reset SYN suppression */ + } + /* else? does presence of mod bit preclude sending a character? */ + if ((modify & 0x07) == 0) { /* no modifiers */ + /* transmit character -- + * note: in write mode, failure to write soon enough after a write response interrupt causes a check + * Also, writing w/o interrupt (or two writes after an interrupt) causes a check + * so again, here we have to check the state to be sure that a write is appropriate + * + * note that in this simulator, we transmit the character immediately on XIO_WRITE. Therefore, + * there is no need to delay an end-operation function (XIO_CONTROL bit 13) until after a character time + */ + + idles_since_last_write = 0; + + switch (sca_state) { + case SCA_STATE_SEND_SYNC: + case SCA_STATE_SEND1: + sca_transmit_byte((uint8) (M[iocc_addr & mem_mask] >> 8)); + sca_state = SCA_STATE_SEND2; + sim_cancel(&sca_unit); + sim_activate(&sca_unit, sca_cwait); /* schedule service after character write time */ + break; + + case SCA_STATE_SEND2: + sca_interrupt(SCA_DSW_CHECK); /* write issued while a character is in progress out? write overrun */ + break; + + case SCA_STATE_IDLE: /* wrong time to issue a write, incorrect sca state */ + default: + sca_flush(); + sca_interrupt(SCA_DSW_CHECK); /* ??? or does this just perform a line turnaround and start transmission? */ + break; + } + + } + break; + + case XIO_CONTROL: /* ***** XIO_CONTROL - manipulate interface state */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL) + printf("SCA CTL addr %04x mod %02x\n", iocc_addr, modify); +#endif + if (modify & 0x80) { /* bit 8 */ + /* enable auto answer */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL) + printf("(enable autoanswer)\n"); +#endif + SETBIT(sca_unit.flags, UNIT_AUTOANSWER); + SETBIT(sca_dsw, SCA_DSW_AUTOANSWER_ENABLED); + } + + if (modify & 0x40) { /* bit 9 */ + /* disable auto answer */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL) + printf("(disable autoanswer)\n"); +#endif + CLRBIT(sca_unit.flags, UNIT_AUTOANSWER); + CLRBIT(sca_dsw, SCA_DSW_AUTOANSWER_ENABLED); + } + + if (modify & 0x20) { /* bit 10 */ + /* toggle timers, inhibit->run or run->inhibit */ +#if (DEBUG_SCA & (DEBUG_SCA_XIO_CONTROL|DEBUG_SCA_TIMERS)) + printf("(toggle timers)\n"); +#endif + msec_now = sim_os_msec(); + + if (in_bsc_mode()) + sca_timer_trigger = ! sca_timer_trigger; /* toggle the timer trigger */ + if (sca_timer_trigger) /* if we've just set it, we're stopping the other timers and */ + sca_start_timer(TIMER_035S, msec_now); /* starting the 0.35 sec timer */ + else + sca_halt_timer(TIMER_035S); + + sca_toggle_timer(TIMER_3S, msec_now); /* toggle the 3 sec and 1.35 sec timers accordingly */ + sca_toggle_timer(TIMER_125S, msec_now); + + any_timer_running = (sca_timer_state[0]| sca_timer_state[1] | sca_timer_state[2]) & SCA_TIMER_RUNNING; + } + + if (modify & 0x10) { /* bit 11 */ + /* enable sync mode. See references to this in FC manual + * In STR mode only, sends a constant stream of SYN's without any CPU intervention + * In BSC mode, appears to start the 1.25 second timer and otherwise act like INITW? + */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL) + printf("(enable sync mode)\n"); +#endif + + if (in_bsc_mode()) { /* in bsc mode start the 1.25 second timer. not sure what resets it?!? */ + if (! in_send_state()) /* also may cause a line turnaround */ + sca_start_transmit(iocc_addr, 0); + + sca_start_timer(TIMER_125S, sim_os_msec()); + } + } + + if (modify & 0x08) { /* bit 12 */ + /* diagnostic mode. What does this do?!? */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL) + printf("(diag mode)\n"); +#endif + } + + if (modify & 0x04) { /* bit 13 */ + /* end operation (reset adapter. See references to this in FC manual). In transmit mode the real adapter delays this + * function until current character has been sent. We don't need to do that as the character got buffered for transmission + * immediately on XIO_WRITE. + */ + +#if (DEBUG_SCA & (DEBUG_SCA_XIO_CONTROL|DEBUG_SCA_XIO_INITR|DEBUG_SCA_XIO_INITW)) + printf("(end operation)\n"); +#endif + sca_state = SCA_STATE_IDLE; + sca_timer_state[0] = sca_timer_state[1] = sca_timer_state[2] = SCA_TIMER_INACTIVE; + any_timer_running = FALSE; + sca_timer_trigger = FALSE; + sca_nsyns = 0; /* reset SYN suppression */ + CLRBIT(sca_dsw, SCA_DSW_BUSY); + } + + if (modify & 0x02) { /* bit 14 */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL) + printf("(6 bit frame)\n"); +#endif + /* set six bit character frame. This is reset to 8 bits at every line turnaround */ + sca_frame = 6; + } + + if (modify & 0x01) { /* bit 15 */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_CONTROL) + printf("(7 bit frame)\n"); +#endif + /* set seven bit character frame. This is reset to 8 bits at every line turnaround */ + sca_frame = 7; + } + + sca_flush(); + break; + + case XIO_INITW: /* ***** XIO_INITW - put SCA in transmit mode */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_INITW) + printf("SCA INITW addr %04x mod %02x\n", iocc_addr, modify); +#endif + /* enter transmit mode. Causes line turnaround. Resets frame to 8 bits. */ + /* (may cause syncing, may involve a fixed timeout?) */ + sca_frame = 8; + sca_start_transmit(iocc_addr, modify); /* this code pulled out to a subroutine cuz transmit can be started from XIO_CONTROL too */ + break; + + case XIO_INITR: /* ***** XIO_INITR - put SCA in receive mode */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_INITR) + printf("SCA INITR addr %04x mod %02x\n", iocc_addr, modify); +#endif + sca_flush(); + + sca_nrcvd = sca_rcvptr = 0; /* discard any data previously read! */ + sca_nsyns = 0; /* reset SYN suppression */ + + /* enter receive mode. Causes line turnaround (e.g. resets to 8 bit frame). Modifier bits are used here too */ + /* (may cause syncing, may involve a fixed timeout?) */ + + sca_frame = 8; + if (! in_receive_state()) + sca_state = SCA_STATE_TURN_RECEIVE; /* begin line turnaround */ + else + sca_state = SCA_STATE_RECEIVE_SYNC; + + SETBIT(sca_dsw, SCA_DSW_BUSY); /* SCA is now busy, in receive mode */ + + if (in_bsc_mode()) /* in BSC mode, start the 3 second timer when we enter receive mode */ + sca_start_timer(TIMER_3S, sim_os_msec()); + + break; + + case XIO_SENSE_DEV: /* ***** XIO_SENSE_DEV - read device status word */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_SENSE_DEV) + printf("SCA SNS mod %02x dsw %04x\n", modify, sca_dsw); +#endif + ACC = sca_dsw; /* return DSW in accumulator */ + + if (modify & 0x01) { /* bit 15: reset interrupts */ +#if (DEBUG_SCA & DEBUG_SCA_XIO_SENSE_DEV) + printf("(reset interrupts)\n"); +#endif + CLRBIT(sca_dsw, SCA_DSW_READ_RESPONSE | SCA_DSW_WRITE_RESPONSE | SCA_DSW_CHECK | SCA_DSW_TIMEOUT | SCA_DSW_AUTOANSWER_REQUEST); + CLRBIT(ILSW[1], ILSW_1_SCA); + } + + if (modify & 0x02) { /* bit 14: restart running timers */ +#if (DEBUG_SCA & (DEBUG_SCA_XIO_SENSE_DEV|DEBUG_SCA_TIMERS)) + printf("(restart timers)\n"); +#endif + msec_now = sim_os_msec(); /* restart "any running timer?" All three, really? */ + for (i = 0; i < 3; i++) + if (sca_timer_state[i] == SCA_TIMER_RUNNING || sca_timer_state[i] == SCA_TIMER_TIMEDOUT) + sca_start_timer(i, msec_now); + } + break; + + default: + sprintf(msg, "Invalid SCA XIO function %x", func); + xio_error(msg); + } +} diff --git a/Ibm1130/ibm1130_stddev.c b/Ibm1130/ibm1130_stddev.c new file mode 100644 index 0000000..d71d45b --- /dev/null +++ b/Ibm1130/ibm1130_stddev.c @@ -0,0 +1,1319 @@ +/* ibm1130_stddev.c: IBM 1130 standard I/O devices simulator + + Based on the SIMH simulator package written by Robert M Supnik + + Brian Knittel + + Revision History: + + 2004.10.22 - Removed stub for xio_1134_papertape as it's now a supported device + + 2003.11.23 - Fixed bug in new routine "quotefix" that made sim crash + for all non-Windows builds :( + + 2003.06.15 - added output translation code to accomodate APL font + added input translation feature to assist emulation of 1130 console keyboard for APL + changes to console input and output IO emulation, fixed bugs exposed by APL interpreter + + 2002.09.13 - pulled 1132 printer out of this file into ibm1130_prt.c + + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + * + * Notes about overstrike mapping: + * The 1130 console printer used a Selectric typewriter element. The APL interpreter + * used overprinting to construct some APL symbols, for example, a round O overstruck] + * with | to get the greek phi. This doesn't accomodate a glass terminal! Instead, + * modern APL fonts have separate character codes for the complex characters. + * To have APL\1130 output appear correctly, we have to do three things: + * + * use simh's telnet feature to connect to the 1130 console stream + * have the telnet program use an APL font + * detect combinations of overstruck symbols, and generate the approrpiate alternate codes. + * + * There is a built-in table of font mappings and overstrike mappings, for the APLPLUS.TTF + * truetype font widely available on the Internet. An font descriptor file can be used + * to specify alternate mappings. + * + * The APL font codes and overstrike mapping can be enabled with the simh command + * + * set tto apl + * + * and disabled with + * + * set tto ascii (this is the default) + * + * APL also uses the red and black ribbon selection. The emulator will output + * ansi red/black foreground commands with the setting + * + * set tto ansi + * + * The codes can be disabled with + * + * set tto noansi (this is the default) + * + * Finally, when APL mode is active, the emulator does some input key translations + * to let the standard ASCII keyboard more closely match the physical layout of the + * 1130 console keyboard. The numeric and punctuation key don't have their + * traditional meaning under APL. The input mapping lets you use the APL keyboard + * layout shown in the APL documentation. + * + * The translations are: + * FROM + * ASCII Position on keyboard To 1130 Key APL interpretation + * ------------------------------------ -------------------------------- + * [ (key to right of P) \r Enter left arrow + * ; (1st key to right of L) \b Backspace [ + * ' (2nd key to right of L) ^U Erase Fld ] + * 2 (key above Q) @ @ up shift + * 3 (key above W) % % up right shift + * 4 (key above E) * * + + * 5 (key above R) < < multiply + * 8 (key above U) - - Return + * 9 (key above I) / / Backspace + * - (key above P) ^Q INT REQ ATTN + * Enter - - Return + * backsp / / Backspace + */ + +#include "ibm1130_defs.h" +#include + +/* #define DEBUG_CONSOLE */ + +/* ---------------------------------------------------------------------------- */ + +static void badio (char *dev) +{ +/* the real 1130 just ignores attempts to use uninstalled devices. They get tested + * at times, so it's best to just be quiet about this + * printf("%s I/O is not yet supported", dev); + */ +} + +void xio_1231_optical (int32 addr, int32 func, int32 modify) {badio("optical mark");} +void xio_system7 (int32 addr, int32 func, int32 modify) {badio("System 7");} + +/* ---------------------------------------------------------------------------- */ + +#define MAX_OUTPUT_COLUMNS 100 /* width of 1130 console printer */ +#define MAX_OS_CHARS 4 /* maximum number of overstruck characters that can be mapped */ +#define MAX_OS_MAPPINGS 100 /* maximum number of overstrike mappings */ + +typedef struct tag_os_map { /* os_map = overstrike mapping */ + int ch; /* ch = output character */ + int nin; /* nin = number of overstruck characters */ + unsigned char inlist[MAX_OS_CHARS]; /* inlist = overstruck ASCII characters, sorted. NOT NULL TERMINATED */ +} OS_MAP; + +extern UNIT *sim_clock_queue; +extern int cgi; + +static int32 tti_dsw = 0; /* device status words */ +static int32 tto_dsw = 0; + int32 con_dsw = 0; + +static unsigned char conout_map[256]; /* 1130 console code to ASCII translation. 0 = undefined, 0xFF = IGNR_ = no output */ +static unsigned char conin_map[256]; /* input mapping */ +static int curcol = 0; /* current typewriter element column, leftmost = 0 */ +static int maxcol = 0; /* highest curcol seen in this output line */ +static unsigned char black_ribbon[30]; /* output escape sequence for black ribbon shift */ +static unsigned char red_ribbon[30]; /* output escape sequence for red ribbon shift */ + +static OS_MAP os_buf[MAX_OUTPUT_COLUMNS]; /* current typewriter output line, holds character struck in each column */ +static OS_MAP os_map[MAX_OS_MAPPINGS]; /* overstrike mapping entries */ +static int n_os_mappings; /* number of overstrike mappings */ + +static t_stat tti_svc(UNIT *uptr); +static t_stat tto_svc(UNIT *uptr); +static t_stat tti_reset(DEVICE *dptr); +static t_stat tto_reset(DEVICE *dptr); + +static t_stat emit_conout_character(int ch); +static t_stat map_conout_character(int ch); +static void reset_mapping (void); +static void set_conout_mapping(int32 flags); +static t_stat validate_conout_mapping(UNIT *uptr, int32 match, char *cvptr, void *desc); +static void set_default_mapping(int32 flags); +static void finish_conout_mapping(int32 flags); +static void strsort (int n, unsigned char *s); /* sorts an array of n characters */ +static int os_map_comp (OS_MAP *a, OS_MAP *b); /* compares two mapping entries */ +static t_stat font_cmd(int32 flag, char *cptr); /* handles font command */ +static void read_map_file(FILE *fd); /* reads a font map file */ +static t_bool str_match(char *str, char *keyword); /* keyword/string comparison */ +static char * handle_map_ansi_definition(char **pc); /* input line parsers for map file sections */ +static char * handle_map_input_definition(char **pc); +static char * handle_map_output_definition(char **pc); +static char * handle_map_overstrike_definition(char **pc); + +extern t_stat sim_poll_kbd(void); +extern t_stat sim_wait_kbd(void); +extern t_stat sim_putchar(int32 out); + +#define UNIT_V_CSET (UNIT_V_UF + 0) /* user flag: character set */ +#define UNIT_V_LOCKED (UNIT_V_UF + 2) /* user flag: keyboard locked */ +#define UNIT_V_ANSI (UNIT_V_UF + 3) + +#define CSET_ASCII (0u << UNIT_V_CSET) +#define CSET_1130 (1u << UNIT_V_CSET) +#define CSET_APL (2u << UNIT_V_CSET) +#define CSET_MASK (3u << UNIT_V_CSET) +#define ENABLE_ANSI (1u << UNIT_V_ANSI) + +#define KEYBOARD_LOCKED (1u << UNIT_V_LOCKED) + +#define IRQ_KEY 0x11 /* ctrl-Q */ +#define PROGRAM_STOP_KEY 0x10 /* ctrl-P */ + +#include "ibm1130_conout.h" /* conout_to_ascii table */ +#include "ibm1130_conin.h" /* ascii_to_conin table */ + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 16) }, + { ORDATA (DSW, tti_dsw, 16) }, + { DRDATA (POS, tti_unit.pos, 31), PV_LEFT }, + { DRDATA (STIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } }; + +MTAB tti_mod[] = { + { CSET_MASK, CSET_ASCII, "ASCII", "ASCII", NULL}, + { CSET_MASK, CSET_1130, "1130", "1130", NULL}, + { 0 } }; + +DEVICE tti_dev = { + "KEYBOARD", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, basic_attach, NULL }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + + /* 14-Nov-03 -- the wait time was SERIAL_OUT_WAIT, but recent versions of SIMH reduced + * this to 100, and wouldn't you know it, APL\1130 has about 120 instructions between the XIO WRITE + * to the console and the associated WAIT. + */ + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), 200 }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 16) }, + { ORDATA (DSW, tto_dsw, 16) }, + { DRDATA (POS, tto_unit.pos, 31), PV_LEFT }, + { DRDATA (STIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } }; + +MTAB tto_mod[] = { + { CSET_MASK, CSET_ASCII, "ASCII", "ASCII", validate_conout_mapping, NULL, NULL}, + { CSET_MASK, CSET_1130, "1130", "1130", validate_conout_mapping, NULL, NULL}, + { CSET_MASK, CSET_APL, "APL", "APL", validate_conout_mapping, NULL, NULL}, + { ENABLE_ANSI,0, "NOANSI", "NOANSI", NULL}, + { ENABLE_ANSI,ENABLE_ANSI, "ANSI", "ANSI", NULL}, + { 0 } }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, basic_attach, NULL }; + +/* Terminal input routines + + tti_svc process event (character ready) + tti_reset process reset + tto_svc process event (print character) + tto_reset process reset +*/ + +#define TT_DSW_PRINTER_RESPONSE 0x8000 +#define TT_DSW_KEYBOARD_RESPONSE 0x4000 +#define TT_DSW_INTERRUPT_REQUEST 0x2000 +#define TT_DSW_KEYBOARD_CONSOLE 0x1000 +#define TT_DSW_PRINTER_BUSY 0x0800 +#define TT_DSW_PRINTER_NOT_READY 0x0400 +#define TT_DSW_KEYBOARD_BUSY 0x0200 + +void xio_1131_console (int32 iocc_addr, int32 func, int32 modify) +{ + int ch; + char msg[80]; + + switch (func) { + case XIO_CONTROL: + SETBIT(tti_dsw, TT_DSW_KEYBOARD_BUSY); /* select and unlock the keyboard */ + keyboard_selected(TRUE); + CLRBIT(tti_unit.flags, KEYBOARD_LOCKED); + tti_unit.buf = 0; /* no key character yet */ + break; + + case XIO_READ: + WriteW(iocc_addr, tti_unit.buf); /* return keycode */ + CLRBIT(tti_dsw, TT_DSW_KEYBOARD_BUSY); /* this ends selected mode */ + keyboard_selected(FALSE); + SETBIT(tti_unit.flags, KEYBOARD_LOCKED); /* keyboard is locked when not selected */ + tti_unit.buf = 0; /* subsequent reads will return zero */ + break; + + case XIO_WRITE: + ch = (ReadW(iocc_addr) >> 8) & 0xFF; /* get character to write */ + tto_unit.buf = emit_conout_character(ch); /* output character and save write status */ + +/* fprintf(stderr, "[CONOUT] %02x\n", ch); */ + + SETBIT(tto_dsw, TT_DSW_PRINTER_BUSY); + sim_activate(&tto_unit, tto_unit.wait); /* schedule interrupt */ + break; + + case XIO_SENSE_DEV: + ACC = tto_dsw | tti_dsw; + if (modify & 0x01) { /* reset interrupts */ + CLRBIT(tto_dsw, TT_DSW_PRINTER_RESPONSE); + CLRBIT(tti_dsw, TT_DSW_KEYBOARD_RESPONSE); + CLRBIT(tti_dsw, TT_DSW_INTERRUPT_REQUEST); + CLRBIT(ILSW[4], ILSW_4_CONSOLE); + } + break; + + default: + sprintf(msg, "Invalid console XIO function %x", func); + xio_error(msg); + } + +/* fprintf(stderr, "After XIO %04x %04x\n", tti_dsw, tto_dsw); */ +} + +/* emit_conout_character - write character with 1130 console code 'ch' */ + +t_stat emit_conout_character (int ch) +{ + t_stat status; + +#ifdef DEBUG_CONSOLE + printf("{%02x}", ch); +#endif + + if ((tto_unit.flags & CSET_MASK) == CSET_1130) /* 1130 (binary) mode, write the raw 8-bit value */ + return sim_putchar(ch); + + if (ch & COUT_IS_CTRL) { + /* red/black shift can be combined with another control */ + /* if present, emit the color shift characters alone */ + + if (ch & COUT_CTRL_BLACK) { + if ((status = map_conout_character(COUT_IS_CTRL|COUT_CTRL_BLACK)) != SCPE_OK) + return status; + } + else if (ch & COUT_CTRL_RED) { + if ((status = map_conout_character(COUT_IS_CTRL|COUT_CTRL_RED)) != SCPE_OK) + return status; + } + + ch &= ~(COUT_CTRL_BLACK|COUT_CTRL_RED); /* remove the ribbon shift bits */ + + if (ch & ~COUT_IS_CTRL) { /* if another control remains, emit it */ + if ((status = map_conout_character(ch)) != SCPE_OK) + return status; + } + + return SCPE_OK; + } + + return map_conout_character(ch); +} + +static void Beep (void) /* notify user keyboard was locked or key was bad */ +{ + sim_putchar(7); +} + +/* tti_svc - keyboard polling (never stops) */ + +static t_stat tti_svc (UNIT *uptr) +{ + int32 temp; + + if (cgi) /* if running in CGI mode, no keyboard and no keyboard polling! */ + return SCPE_OK; + /* otherwise, so ^E can interrupt the simulator, */ + sim_activate(&tti_unit, tti_unit.wait); /* always continue polling keyboard */ + + assert(sim_clock_queue != NULL); + + temp = sim_poll_kbd(); + + if (temp < SCPE_KFLAG) + return temp; /* no char or error? */ + + temp &= 0xFF; /* remove SCPE_KFLAG */ + + if ((tti_unit.flags & CSET_MASK) == CSET_ASCII) + temp = conin_map[temp] & 0xFF; /* perform input translation */ + + if (temp == IRQ_KEY) { /* INT REQ (interrupt request) key */ + SETBIT(tti_dsw, TT_DSW_INTERRUPT_REQUEST); /* queue interrupt */ + SETBIT(ILSW[4], ILSW_4_CONSOLE); + calc_ints(); + + CLRBIT(tti_unit.flags, KEYBOARD_LOCKED); /* keyboard restore, according to func. char. manual */ + +#ifdef DEBUG_CONSOLE + printf("[*IRQ*]"); +#endif + tti_unit.buf = 0; /* subsequent reads need to return 0 (required by APL\1130) */ + return SCPE_OK; + } + + if (temp == PROGRAM_STOP_KEY) { /* simulate the program stop button */ + SETBIT(con_dsw, CPU_DSW_PROGRAM_STOP); + SETBIT(ILSW[5], ILSW_5_INT_RUN_PROGRAM_STOP); + calc_ints(); + +#ifdef DEBUG_CONSOLE + printf("[*PSTOP*]"); +#endif + + return SCPE_OK; + } + + if ((tti_unit.flags & KEYBOARD_LOCKED) || ! (tti_dsw & TT_DSW_KEYBOARD_BUSY)) { + Beep(); + return SCPE_OK; + } + + if ((tti_unit.flags & CSET_MASK) == CSET_ASCII) + temp = ascii_to_conin[temp]; + + if (temp == 0) { /* ignore invalid characters */ + Beep(); + calc_ints(); + return SCPE_OK; + } + + tti_unit.buf = temp & 0xFFFE; /* save keystroke except last bit (not defined) */ + tti_unit.pos = tti_unit.pos + 1; /* but it lets us distinguish 0 from no punch ' ' */ + +#ifdef DEBUG_CONSOLE + printf("[%04x]", tti_unit.buf & 0xFFFF); +#endif + + SETBIT(tti_unit.flags, KEYBOARD_LOCKED); /* prevent further keystrokes */ + + SETBIT(tti_dsw, TT_DSW_KEYBOARD_RESPONSE); /* queue interrupt */ + SETBIT(ILSW[4], ILSW_4_CONSOLE); + calc_ints(); + +/* fprintf(stderr, "TTI interrupt svc SET %04x %04x\n", tti_dsw, tto_dsw); */ + + return SCPE_OK; +} + +static t_stat tti_reset (DEVICE *dptr) +{ + tti_unit.buf = 0; + tti_dsw = 0; + + CLRBIT(ILSW[4], ILSW_4_CONSOLE); + calc_ints(); + keyboard_selected(FALSE); + + SETBIT(tti_unit.flags, KEYBOARD_LOCKED); + + if (cgi) + sim_cancel(&tti_unit); /* in cgi mode, never poll keyboard */ + else + sim_activate(&tti_unit, tti_unit.wait); /* otherwise, always poll keyboard */ + + return SCPE_OK; +} + +/* basic_attach - fix quotes in filename, then call standard unit attach routine */ + +t_stat basic_attach (UNIT *uptr, char *cptr) +{ + return attach_unit(uptr, quotefix(cptr)); /* fix quotes in filenames & attach */ +} + +/* quotefix - strip off quotes around filename, if present */ + +char * quotefix (char * cptr) +{ +#ifdef WIN32 /* do this only for Windows builds, for the time being */ + char *c; + int quote; + + if (*cptr == '"' || *cptr == '\'') { + quote = *cptr++; /* remember quote and skip over it */ + + for (c = cptr; *c && *c != quote; c++) + ; /* find closing quote, or end of string */ + + if (*c) /* terminate string at closing quote */ + *c = '\0'; + } + +#endif + return cptr; /* return pointer to cleaned-up name */ +} + +t_bool keyboard_is_busy (void) /* return TRUE if keyboard is not expecting a character */ +{ + return (tti_dsw & TT_DSW_KEYBOARD_BUSY); +} + +static t_stat tto_svc (UNIT *uptr) +{ + CLRBIT(tto_dsw, TT_DSW_PRINTER_BUSY); + SETBIT(tto_dsw, TT_DSW_PRINTER_RESPONSE); + + SETBIT(ILSW[4], ILSW_4_CONSOLE); + calc_ints(); + +/* fprintf(stderr, "TTO interrupt svc SET %04x %04x\n", tti_dsw, tto_dsw); */ + + return (t_stat) tto_unit.buf; /* return status saved during output conversion */ +} + +static t_stat tto_reset (DEVICE *dptr) +{ + tto_unit.buf = 0; + tto_dsw = 0; + + CLRBIT(ILSW[4], ILSW_4_CONSOLE); + calc_ints(); + + sim_cancel(&tto_unit); /* deactivate unit */ + + set_conout_mapping(tto_unit.flags); /* initialize the overstrike mappings */ + /* register the font-mapping command */ + register_cmd("FONT", font_cmd, 0, "font MAPFILE use font mapping definitions in MAPFILE\n"); + + return SCPE_OK; +} + +#ifdef _MSC_VER +# pragma warning(disable:4245) /* enable int->char demotion warning caused by characters with high-bit set */ +#endif + +static struct { /* default input mapping for APL */ + unsigned char in; + unsigned char out; +} conin_to_APL[] = +{ /* these map input keys to those in like positions on 1130 keyboard */ + '[', '\r', /* enter (EOF) is APL left arrow */ + ';', '\b', /* backspace is APL [ */ + '\'', '\x15', /* ctrl-U, erase field, is APL ]*/ + '2', '@', /* APL upshift */ + '3', '%', /* APL rightshift */ + '4', '*', /* APL + and - */ + '5', '<', /* APL x and divide */ + '8', '-', /* APL return */ + '9', '/', /* APL backspace */ + '-', IRQ_KEY, /* ctrl-q (INT REQ), APL ATTN */ + '\r', '-', /* APL return */ + '\b', '/' /* APL backspace */ +}; + +#define NCONIN_TO_APL (sizeof(conin_to_APL)/sizeof(conin_to_APL[0])) + +static struct { /* default output mapping for APLPLUS font */ + unsigned char in; + unsigned char out; +} conout_to_APL[] = +{ + '\x01', IGNR_, /* controls */ + '\x03', '\n', + '\x05', IGNR_, /* (black and red are handled by ansi sequences) */ + '\x09', IGNR_, + '\x11', '\b', + '\x21', ' ', + '\x41', '\t', + '\x81', CRLF_, + + '\xC4', '\x30', /* (if you're curious, order here is position on APL typeball) */ + '\xE4', '\x38', + '\xD4', '\x37', + '\xF4', '\x35', + '\xDC', '\x33', + '\xFC', '\x31', + '\xC2', '\x29', + '\xE2', '\x9F', + '\xD2', '\x89', + '\xF2', '\x88', + '\xDA', '\xAF', + '\xC6', '\x5E', + '\xE6', '\xAC', + '\xD6', '\x3E', + '\xF6', '\x3D', + '\xDE', '\x3C', + '\xFE', '\xA8', + '\xC0', '\x5D', + '\xE0', '\x39', + '\xD0', '\x36', + '\xF0', '\x34', + '\xD8', '\x32', + + '\x84', '\x84', + '\xA4', '\x59', + '\x94', '\x58', + '\xB4', '\x56', + '\x9C', '\x54', + '\xBC', '\x2F', + '\x82', '\x3B', + '\xA2', '\x9B', + '\x92', '\xBE', + '\xB2', '\x87', + '\x9A', '\x97', + '\x86', '\x85', + '\xA6', '\x86', + '\x96', '\x9C', + '\xB6', '\x9E', + '\x9E', '\x7E', + '\xBE', '\x5C', + '\x80', '\x2C', + '\xA0', '\x5A', + '\x90', '\x57', + '\xB0', '\x55', + '\x98', '\x53', + + '\x44', '\x2B', + '\x64', '\x51', + '\x54', '\x50', + '\x74', '\x4E', + '\x5C', '\x4C', + '\x7C', '\x4A', + '\x42', '\x28', + '\x62', '\xBD', + '\x52', '\xB1', + '\x72', '\x7C', + '\x5A', '\x27', + '\x46', '\x2D', + '\x66', '\x3F', + '\x56', '\x2A', + '\x76', '\x82', + '\x5E', '\x8C', + '\x7E', '\xB0', + '\x40', '\x5B', + '\x60', '\x52', + '\x50', '\x4F', + '\x70', '\x4D', + '\x58', '\x4B', + + '\x04', '\xD7', + '\x24', '\x48', + '\x14', '\x47', + '\x34', '\x45', + '\x1C', '\x43', + '\x3C', '\x41', + '\x02', '\x3A', + '\x22', '\xBC', + '\x12', '\x5F', + '\x32', '\x98', + '\x1A', '\x83', + '\x06', '\xF7', + '\x26', '\x91', + '\x16', '\x92', + '\x36', '\xB9', + '\x1E', '\x9D', + '\x3E', '\xB8', + '\x00', '\x2E', + '\x20', '\x49', + '\x10', '\x46', + '\x30', '\x44', + '\x18', '\x42', +}; + +#define NCONOUT_TO_APL (sizeof(conout_to_APL)/sizeof(conout_to_APL[0])) + +static OS_MAP default_os_map[] = /* overstrike mapping for APLPLUS font */ +{ + '\x8a', 2, "\x5e\x7e", + '\x8b', 2, "\x9f\x7e", + '\x8d', 2, "\x8c\x27", + '\x8e', 3, "\x8c\x2d\x3a", + '\x8f', 2, "\x91\x5f", + '\x90', 2, "\x92\x7e", + '\x93', 2, "\x91\x7c", + '\x94', 2, "\x92\x7c", + '\x95', 2, "\xb0\x82", + '\x96', 2, "\xb0\x83", + '\x99', 2, "\x2d\x5c", + '\x9a', 2, "\x2d\x2f", + '\xae', 2, "\x2c\x2d", + '\xb2', 2, "\xb1\x7c", + '\xb3', 2, "\xb1\x5c", + '\xb4', 2, "\xb1\x2d", + '\xb5', 2, "\xb1\x2a", + '\xba', 2, "\xb9\x5f", + '\xd0', 2, "\x30\x7e", + '\xd8', 2, "\x4f\x2f", + '\x21', 2, "\x27\x2e", + '\xa4', 2, "\xb0\xb1", /* map degree in circle to circle cross (APL uses this as character error symbol) */ + '\xf0', 2, "\xb0\xa8", + '\xfe', 2, "\x3a\xa8", +}; + +#ifdef _MSC_VER +# pragma warning(default:4245) /* enable int->char demotion warning */ +#endif + +/* os_map_comp - compare to OS_MAP entries */ + +static int os_map_comp (OS_MAP *a, OS_MAP *b) +{ + unsigned char *sa, *sb; + int i; + + if (a->nin > b->nin) + return +1; + + if (a->nin < b->nin) + return -1; + + sa = a->inlist; + sb = b->inlist; + + for (i = a->nin; --i >= 0;) { + if (*sa > *sb) + return +1; + + if (*sa < *sb) + return -1; + + sa++; + sb++; + } + + return 0; +} + +/* strsort - sorts the n characters of array 's' using insertion sort */ + +static void strsort (int n, unsigned char *s) +{ + unsigned char temp; + int i, big; + + while (--n > 0) { /* repeatedly */ + big = 0; /* find largest value of s[0]...s[n] */ + for (i = 1; i <= n; i++) + if (s[i] > s[big]) big = i; + + temp = s[n]; /* put largest value at end of array */ + s[n] = s[big]; + s[big] = temp; + } +} + +/* file format: + +[font XXX] font named XXX +OUT failure character +OUT IN single character mapping +OUT IN IN ... overstrike mapping + +*/ + +static void set_conout_mapping (int32 flags) +{ + curcol = 0; + maxcol = 0; + + /* set the default mappings. We may later override them with settings from an ini file */ + + set_default_mapping(flags); +} + +/* finish_conout_mapping - sort the finalized overstrike mapping */ + +static void finish_conout_mapping (int32 flags) +{ + int i, n, big; + OS_MAP temp; + + for (i = 0; i < n_os_mappings; i++) /* sort the inlist strings individually */ + strsort(os_map[i].nin, os_map[i].inlist); + + for (n = n_os_mappings; --n > 0; ) { /* then sort the os_map array itself with insertion sort */ + big = 0; /* find largest value of s[0]...s[n] */ + for (i = 1; i <= n; i++) + if (os_map_comp(os_map+i, os_map+big) > 0) big = i; + + if (big != n) { + temp = os_map[n]; /* put largest value at end of array */ + os_map[n] = os_map[big]; + os_map[big] = temp; + } + } +} + +/* validate_conout_mapping - called when set command gets a new value */ + +static t_stat validate_conout_mapping (UNIT *uptr, int32 match, char *cvptr, void *desc) +{ + set_conout_mapping(match); + return SCPE_OK; +} + +static void reset_mapping (void) +{ + int i; + + black_ribbon[0] = '\0'; /* erase the ribbon sequences */ + red_ribbon[0] = '\0'; + + memset(conout_map, 0, sizeof(conout_map)); /* erase output mapping */ + + n_os_mappings = 0; /* erase overstrike mapping */ + + for (i = (sizeof(conin_map)/sizeof(conin_map[0])); --i >= 0; ) + conin_map[i] = (unsigned char) i; /* default conin_map is identity map */ +} + +/* set_default_mapping - create standard font and overstrike map */ + +static void set_default_mapping (int32 flags) +{ + int i; + + reset_mapping(); + + strcpy((char *) black_ribbon, "\033[30m"); + strcpy((char *) red_ribbon, "\033[31m"); + + switch (flags & CSET_MASK) { + case CSET_1130: + break; + + case CSET_ASCII: + memcpy(conout_map, conout_to_ascii, sizeof(conout_to_ascii)); + break; + + case CSET_APL: + for (i = NCONOUT_TO_APL; --i >= 0; ) + conout_map[conout_to_APL[i].in] = conout_to_APL[i].out; + + for (i = NCONIN_TO_APL; --i >= 0; ) + conin_map[conin_to_APL[i].in] = conin_to_APL[i].out; + + memcpy(os_map, default_os_map, sizeof(default_os_map)); + n_os_mappings = (sizeof(default_os_map) / sizeof(default_os_map[0])); + break; + } + + finish_conout_mapping(flags); /* sort conout mapping if necessary */ +} + +/* sim_putstr - write a string to the console */ + +t_stat sim_putstr (char *s) +{ + t_stat status; + + while (*s) { + if ((status = sim_putchar(*s)) != SCPE_OK) + return status; + + s++; + } + + return SCPE_OK; +} + +/* map_conout_character - translate and write a single character */ + +static t_stat map_conout_character (int ch) +{ + t_stat status; + int i, cmp; + + if (ch == (COUT_IS_CTRL|COUT_CTRL_BLACK)) + return (tto_unit.flags & ENABLE_ANSI) ? sim_putstr((char *) black_ribbon) : SCPE_OK; + + if (ch == (COUT_IS_CTRL|COUT_CTRL_RED)) + return (tto_unit.flags & ENABLE_ANSI) ? sim_putstr((char *) red_ribbon) : SCPE_OK; + + if ((ch = conout_map[ch & 0xFF]) == 0) + ch = '?'; /* unknown character? print ? */ + + if (ch == '\n') { /* newline: reset overstrike buffer */ + curcol = 0; + maxcol = -1; + } + else if (ch == '\r') { /* carriage return: rewind to column 0 */ + curcol = 0; + maxcol = -1; /* assume it advances paper too */ + } + else if (ch == '\b') { /* backspace: back up one character */ + if (curcol > 0) + curcol--; + } + else if (n_os_mappings && ch != (unsigned char) IGNR_) { + if (curcol >= MAX_OUTPUT_COLUMNS) + map_conout_character('\x81'); /* precede with automatic carriage return/line feed, I guess */ + + if (curcol > maxcol) { /* first time in this column, no overstrike possible yet */ + os_buf[curcol].nin = 0; + maxcol = curcol; + } + + if (ch != ' ' && ch != 0) { /* (if it's not a blank or unknown) */ + os_buf[curcol].inlist[os_buf[curcol].nin] = (unsigned char) ch; + strsort(++os_buf[curcol].nin, os_buf[curcol].inlist); + } + + if (os_buf[curcol].nin == 0) /* if nothing but blanks seen, */ + ch = ' '; /* output is a blank */ + else if (os_buf[curcol].nin == 1) { /* if only one printing character seen, display it */ + ch = os_buf[curcol].inlist[0]; + } + else { /* otherwise look up mapping */ + ch = '?'; + + for (i = 0; i < n_os_mappings; i++) { + cmp = os_map_comp(&os_buf[curcol], &os_map[i]); + if (cmp == 0) { /* a hit */ + ch = os_map[i].ch; + break; + } + else if (cmp < 0) /* not found */ + break; + } + } + + if (curcol < MAX_OUTPUT_COLUMNS) /* this should now never happen, as we automatically return */ + curcol++; + } + + switch (ch) { + case IGNR_: + break; + + case CRLF_: + if (! cgi) { + if ((status = sim_putchar('\r')) != SCPE_OK) + return status; + + tto_unit.pos++; + } + + if ((status = sim_putchar('\n')) != SCPE_OK) + return status; + + tto_unit.pos++; /* hmm, why do we count these? */ + break; + + default: + if ((status = sim_putchar(ch)) != SCPE_OK) + return status; + + tto_unit.pos++; + break; + } + + return SCPE_OK; +} + +/* font_cmd - parse a font mapping file. Sets input and output translations */ + +static t_stat font_cmd (int32 flag, char *cptr) +{ + char *fname, quote; + FILE *fd; + + while (*cptr && (*cptr <= ' ')) cptr++; /* skip blanks */ + if (! *cptr) return SCPE_2FARG; /* argument missing */ + + fname = cptr; /* save start */ + if (*cptr == '\'' || *cptr == '"') { /* quoted string */ + quote = *cptr++; /* remember quote character */ + fname++; /* skip the quote */ + + while (*cptr && (*cptr != quote)) /* find closing quote */ + cptr++; + } + else { + while (*cptr && (*cptr > ' ')) /* find terminating blank */ + cptr++; + } + *cptr = '\0'; /* terminate name */ + + if ((fd = fopen(fname, "r")) == NULL) + return SCPE_OPENERR; + + reset_mapping(); /* remove all default mappings */ + + read_map_file(fd); + fclose(fd); + + finish_conout_mapping(tto_unit.flags); + return SCPE_OK; +} + +/* str_match - compare the string str to the keyword, case insensitive */ + +static t_bool str_match (char *str, char *keyword) +{ + char kch, sch; + + while (*keyword) { /* see if str matches the keyword... */ + kch = *keyword++; /* get pair of characters */ + sch = *str++; + + if (BETWEEN(kch, 'A', 'Z')) kch += 32; /* change upper to lower case */ + if (BETWEEN(sch, 'A', 'Z')) sch += 32; + + if (kch != sch) /* characters must match; if not, quit */ + return FALSE; + } + + return *str <= ' ' || *str == ';'; /* success if the input string ended or is in whitespace or comment */ +} + +/* read_map_file - process definition lines in opened mapping file */ + +static void read_map_file (FILE *fd) +{ + char str[256], *c, *errmsg; + int lineno = 0; + enum {SECT_UNDEFINED, SECT_DEFAULT, SECT_ANSI, SECT_INPUT, SECT_OUTPUT, SECT_OVERSTRIKE} + section = SECT_UNDEFINED; + + while (fgets(str, sizeof(str), fd) != NULL) { + ++lineno; /* count input lines */ + + if ((c = strchr(str, '\n')) != NULL) /* terminate at newline */ + *c = '\0'; + + for (c = str; *c && *c <= ' '; c++) /* skip blanks */ + ; + + if (c[0] == '\0' || c[0] == ';') /* ignore blank lines and lines starting with ; */ + continue; + + if (*c == '[') { + if (str_match(c, "[default]")) { /* check for section separators */ + set_default_mapping(tto_unit.flags); + section = SECT_UNDEFINED; + continue; + } + if (str_match(c, "[ansi]")) { + section = SECT_ANSI; + continue; + } + if (str_match(c, "[input]")) { + section = SECT_INPUT; + continue; + } + if (str_match(c, "[output]")) { + section = SECT_OUTPUT; + continue; + } + if (str_match(c, "[overstrike]")) { + section = SECT_OVERSTRIKE; + continue; + } + } + + switch (section) { /* if we get here, we have a definition line */ + case SECT_ANSI: + errmsg = handle_map_ansi_definition(&c); + break; + case SECT_INPUT: + errmsg = handle_map_input_definition(&c); + break; + case SECT_OUTPUT: + errmsg = handle_map_output_definition(&c); + break; + case SECT_OVERSTRIKE: + errmsg = handle_map_overstrike_definition(&c); + break; + default: + errmsg = "line occurs before valid [section]"; + break; + } + + if (errmsg == NULL) { /* if no other error detected, */ + while (*c && *c <= ' ') /* skip past any whitespace */ + c++; + + if (*c && *c != ';') /* if line doesn't end or run into a comment, complain */ + errmsg = "too much stuff on input line"; + } + + if (errmsg != NULL) { /* print error message and offending line */ + printf("* Warning: %s", errmsg); + + switch (section) { /* add section name if possible */ + case SECT_ANSI: errmsg = "ansi"; break; + case SECT_INPUT: errmsg = "input"; break; + case SECT_OUTPUT: errmsg = "output"; break; + case SECT_OVERSTRIKE: errmsg = "overstrike"; break; + default: errmsg = NULL; break; + } + if (errmsg != NULL) + printf(" in [%s] section", errmsg); + + printf(", line %d\n%s\n", lineno, str); + } + } +} + +/* get_num_char - read an octal or hex character specification of exactly 'ndigits' digits + * the input pointers is left pointing to the last character of the number, so that it + * may be incremented by the caller + */ + +static char * get_num_char (char **pc, unsigned char *out, int ndigits, int base, char *errmsg) +{ + int ch = 0, digit; + char *c = *pc; + + while (--ndigits >= 0) { /* collect specified number of digits */ + if (BETWEEN(*c, '0', '9')) + digit = *c - '0'; + else if (BETWEEN(*c, 'A', 'F')) + digit = *c - 'A' + 10; + else if (BETWEEN(*c, 'a', 'f')) + digit = *c - 'a' + 10; + else + digit = base; + + if (digit >= base) /* bad digit */ + return errmsg; + + ch = ch * base + digit; /* accumulate digit */ + c++; + } + + *out = (unsigned char) ch; /* return parsed character */ + *pc = c-1; /* make input pointer point to last character seen */ + return NULL; /* no error */ +} + +/* get_characters - read character specification(s) from input string pointed to + * by *pc. Results stored in outstr; up to nmax characters parsed. Actual number + * found returned in *nout. Returns NULL on success or error message if syntax + * error encountered. *pc is advanced to next whitespace or whatever followed input. + */ + +static char * get_characters (char **pc, unsigned char *outstr, int nmax, int *nout) +{ + char *c = *pc, *errstr; + unsigned char *out = outstr; + + while (*c && *c <= ' ') /* skip leading whitespace */ + c++; + + while (--nmax >= 0) { /* get up to maximum number of characters */ + if (*c == ';' || *c <= ' ') /* we ran into a comment, whitespace or end of string: we're done */ + break; + + if (*c == '\\') { /* backslash escape of some sort */ + switch (*++c) { + case 'b': /* backspace */ + case 'B': + *out++ = '\b'; + break; + + case 'e': /* ascii ESCAPE */ + case 'E': + *out++ = '\033'; + break; + + case 'f': /* formfeed */ + case 'F': + *out++ = '\f'; + break; + + case 'n': /* newline */ + case 'N': + *out++ = '\n'; + break; + + case 'r': /* return */ + case 'R': + *out++ = '\r'; + break; + + case 't': /* tab */ + case 'T': + *out++ = '\t'; + break; + + case 'x': /* hex specification */ + case 'X': + c++; + if ((errstr = get_num_char(&c, out, 2, 16, "bad hex character")) != NULL) + return errstr; + + out++; /* advance out pointer */ + break; + + default: /* anything else */ + if (BETWEEN(*c, '0', '7')) { /* octal specification */ + if ((errstr = get_num_char(&c, out, 3, 8, "bad octal character")) != NULL) + return errstr; + + out++; /* advance out pointer */ + } + else if (BETWEEN(*c, 'A', 'Z') || BETWEEN(*c, 'a', 'z')) + return "invalid \\ escape"; /* other \x letters are bad */ + else { + *out++ = (unsigned char) *c;/* otherwise, accept \x as literal character x */ + } + break; + } + } + else if (*c == '^') { /* control character */ + c++; + if (BETWEEN(*c, 'A', 'Z')) /* convert alpha, e.g. A -> 1 */ + *out++ = (unsigned char) (*c - 'A' + 1); + else if (BETWEEN(*c, 'a', 'z')) + *out++ = (unsigned char) (*c - 'z' + 1); + else /* non alpha is bad */ + return "invalid control letter"; + } + else if (str_match(c, "IGNORE")) { /* magic word: a character that will never be output */ + *out++ = (unsigned char) IGNR_; + c += 6; + } + else { + *out++ = (unsigned char) *c; /* save literal character */ + } + + c++; + } + + if (*c && *c != ';' && *c > ' ') /* we should be at end of string, whitespace or comment */ + return "too many characters specified"; + + *pc = c; /* save advanced pointer */ + *nout = out-outstr; /* save number of characters stored */ + + return NULL; /* no error */ +} + +/* handle_map_ansi_definition - process line in [ansi] section */ + +static char * handle_map_ansi_definition (char **pc) +{ + unsigned char *outstr; + char *errmsg; + int n; + + if (str_match(*pc, "black")) { /* find which string we're setting */ + outstr = black_ribbon; /* this is where we'll save the output string */ + *pc += 5; /* skip over the token */ + } + else if (str_match(*pc, "red")) { + outstr = red_ribbon; + *pc += 3; + } + else + return "invalid variable name"; + /* get list of characters */ + if ((errmsg = get_characters(pc, outstr, sizeof(black_ribbon)-1, &n)) != NULL) + return errmsg; + + outstr[n] = '\0'; /* null terminate the string */ + + return (n > 0) ? NULL : "missing output string"; /* NULL if OK, error msg if no characters */ +} + +/* handle_map_input_definition - process line in [input] section */ + +static char * handle_map_input_definition (char **pc) +{ + unsigned char cin, cout; + char *errmsg; + int n; + + if ((errmsg = get_characters(pc, &cin, 1, &n)) != NULL) /* get input character */ + return errmsg; + + if (n != 1) + return "missing input character"; + + if ((errmsg = get_characters(pc, &cout, 1, &n)) != NULL) /* get output character */ + return errmsg; + + if (n != 1) + return "missing output character"; + + conin_map[cin] = cout; /* set the mapping */ + return NULL; +} + +/* handle_map_output_definition - process line in [output] section */ + +static char * handle_map_output_definition (char **pc) +{ + unsigned char cin, cout; + char *errmsg; + int n; + + if ((errmsg = get_characters(pc, &cin, 1, &n)) != NULL) /* get input character */ + return errmsg; + + if (n != 1) + return "missing input character"; + + if ((errmsg = get_characters(pc, &cout, 1, &n)) != NULL) /* get output character */ + return errmsg; + + if (n != 1) + return "missing output character"; + + conout_map[cin] = cout; /* set the mapping */ + return NULL; +} + +/* handle_map_overstrike_definition - process line in [overstrike] section */ + +static char * handle_map_overstrike_definition (char **pc) +{ + unsigned char ch, inlist[MAX_OS_CHARS]; + char *errmsg; + int nin; + + if (n_os_mappings >= MAX_OS_MAPPINGS) /* os_map is full, no more room */ + return "too many overstrike mappings"; + /* get output character */ + if ((errmsg = get_characters(pc, &ch, 1, &nin)) != NULL) + return errmsg; + + if (nin != 1) + return "missing output character"; + /* get input list */ + if ((errmsg = get_characters(pc, inlist, MAX_OS_CHARS, &nin)) != NULL) + return errmsg; + + if (nin < 2) /* expect at least two characters overprinted */ + return "missing input list"; + + os_map[n_os_mappings].ch = ch; /* save in next os_map slot */ + os_map[n_os_mappings].nin = nin; + memmove(os_map[n_os_mappings].inlist, inlist, nin); + + n_os_mappings++; + return NULL; +} diff --git a/Ibm1130/ibm1130_sys.c b/Ibm1130/ibm1130_sys.c new file mode 100644 index 0000000..22cce80 --- /dev/null +++ b/Ibm1130/ibm1130_sys.c @@ -0,0 +1,505 @@ +/* ibm1130_sys.c: IBM 1130 simulator interface + + Based on PDP-11 simulator written by Robert M Supnik + + Revision History + 0.27 2005Mar08 - Added sca device + 0.26 2002Apr24 - Added !BREAK in card deck file to stop simulator + 0.25 2002Apr18 - Fixed some card reader problems. It starts the reader + properly if you attach a deck while it's waiting to a read. + 0.24 2002Mar27 - Fixed BOSC bug; BOSC works in short instructions too + 0.23 2002Feb26 - Added @decklist feature for ATTACH CR. + 0.22 2002Feb26 - Replaced "strupr" with "upcase" for compatibility. + 0.21 2002Feb25 - Some compiler compatibiity changes, couple of compiler-detected + bugs + 0.01 2001Jul31 - Derived from pdp11_sys.c, which carries this disclaimer: + + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to simh@ibm1130.org + */ + +#include "ibm1130_defs.h" +#include +#include + +extern DEVICE cpu_dev, console_dev, dsk_dev, cr_dev, cp_dev, ptr_dev, ptp_dev, t2741_dev; +extern DEVICE tti_dev, tto_dev, prt_dev, log_dev, sca_dev; +extern DEVICE gdu_dev, console_dev, plot_dev; + +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern int32 saved_PC; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "IBM 1130"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &cpu_dev, /* the cpu */ + &dsk_dev, /* disk drive(s) */ + &cr_dev, /* card reader/punch */ + &cp_dev, + &tti_dev, /* console keyboard, selectric printer */ + &tto_dev, + &prt_dev, /* 1132 printer */ + &ptr_dev, /* 1134 paper tape reader */ + &ptp_dev, /* 1055 paper tape punch */ + &sca_dev, /* Synchronous communications adapter option */ + &console_dev, /* console display (windows GUI) */ + &gdu_dev, /* 2250 display */ + &t2741_dev, /* nonstandard serial interface used by APL\1130 */ + &plot_dev, /* plotter device, in ibm1130_plot.c */ + NULL +}; + +const char *sim_stop_messages[] = { + "Unknown error", + "Wait", + "Invalid command", + "Simulator breakpoint", + "Use of incomplete simulator function", + "Power off", + "!BREAK in card deck file", + "Phase load break", + "Program has run amok", + "Run time limit exceeded", + "Immediate Stop key requested", + "Simulator break key pressed", + "Simulator step count expired", + "Simulator IO error", +}; + +/* Loader. IPL is normally performed by card reader (boot command). This function + * loads hex data from a file for testing purposes. The format is: + * + * blank lines or lines starting with ; / or # are ignored as comments + * + * @XXXX set load addresss to hex value XXXX + * XXXX store hex word value XXXX at current load address and increment address + * ... + * =XXXX set IAR to hex value XXXX + * ZXXXX zero XXXX words and increment load address + * SXXXX set console entry switches to XXXX. This lets a program specify the + * default value for the toggle switches. + * + * Multiple @ and data sections may be entered. If more than one = or S value is specified + * the last one wins. + * + * Note: the load address @XXXX and data values XXXX can be followed by the letter + * R to indicate that the values are relocatable addresses. This is ignored in this loader, + * but the asm1130 cross assembler may put them there. + */ + +t_stat my_load (FILE *fileref, char *cptr, char *fnam) +{ + char line[150], *c; + int iaddr = -1, runaddr = -1, val, nwords; + + while (fgets(line, sizeof(line), fileref) != NULL) { + for (c = line; *c && *c <= ' '; c++) /* find first nonblank */ + ; + + if (*c == '\0' || *c == '#' || *c == '/' || *c == ';') + continue; /* empty line or comment */ + + if (*c == '@') { /* set load address */ + if (sscanf(c+1, "%x", &iaddr) != 1) + return SCPE_FMT; + } + else if (*c == '=') { + if (sscanf(c+1, "%x", &runaddr) != 1) + return SCPE_FMT; + } + else if (*c == 's' || *c == 'S') { + if (sscanf(c+1, "%x", &val) != 1) + return SCPE_FMT; + + CES = val & 0xFFFF; /*preload console entry switches */ + } + else if (*c == 'z' || *c == 'Z') { + if (sscanf(c+1, "%x", &nwords) != 1) + return SCPE_FMT; + + if (iaddr == -1) + return SCPE_FMT; + + while (--nwords >= 0) { + WriteW(iaddr, 0); + iaddr++; + } + } + else if (strchr("0123456789abcdefABCDEF", *c) != NULL) { + if (sscanf(c, "%x", &val) != 1) + return SCPE_FMT; + + if (iaddr == -1) + return SCPE_FMT; + + WriteW(iaddr, val); /*store data */ + iaddr++; + } + else + return SCPE_FMT; /*unexpected data */ + } + + if (runaddr != -1) + IAR = runaddr; + + return SCPE_OK; +} + +t_stat my_save (FILE *fileref, char *cptr, char *fnam) +{ + int iaddr, nzeroes = 0, nwords = (int) (MEMSIZE/2), val; + + fprintf(fileref, "=%04x\r\n", IAR); + fprintf(fileref, "@0000\r\n"); + for (iaddr = 0; iaddr < nwords; iaddr++) { + val = ReadW(iaddr); + if (val == 0) /*queue up zeroes */ + nzeroes++; + else { + if (nzeroes >= 4) { /*spit out a Z directive */ + fprintf(fileref, "Z%04x\r\n", nzeroes); + nzeroes = 0; + } + else { /*write queued zeroes literally */ + while (nzeroes > 0) { + fprintf(fileref, " 0000\r\n"); + nzeroes--; + } + } + fprintf(fileref, " %04x\r\n", val); + } + } + if (nzeroes >= 4) { /*emit any queued zeroes */ + fprintf(fileref, "Z%04x\r\n", nzeroes); + nzeroes = 0; + } + else { + while (nzeroes > 0) { + fprintf(fileref, " 0000\r\n"); + nzeroes--; + } + } + + return SCPE_OK; +} + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ + if (flag) + return my_save(fileref, cptr, fnam); + else + return my_load(fileref, cptr, fnam); +} + +/* Specifier decode + + Inputs: + *of = output stream + addr = current PC + spec = specifier + nval = next word + flag = TRUE if decoding for CPU + iflag = TRUE if decoding integer instruction + Outputs: + count = -number of extra words retired +*/ + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +static char *opcode[] = { + "?00 ", "XIO ", "SLA ", "SRA ", + "LDS ", "STS ", "WAIT", "?07 ", + "BSI ", "BSC ", "?0A ", "?0B ", + "LDX ", "STX ", "MDX ", "?0F ", + "A ", "AD ", "S ", "SD ", + "M ", "D ", "?16 ", "?17 ", + "LD ", "LDD ", "STO ", "STD ", + "AND ", "OR ", "EOR ", "?1F ", +}; + +static char relative[] = { /*true if short mode displacements are IAR relative */ + FALSE, TRUE, FALSE, FALSE, + FALSE, TRUE, FALSE, FALSE, + TRUE, FALSE, FALSE, FALSE, + TRUE, TRUE, TRUE, FALSE, + TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, FALSE, FALSE, + TRUE, TRUE, TRUE, TRUE, + TRUE, TRUE, TRUE, FALSE +}; + +static char *lsopcode[] = {"SLA ", "SLCA ", "SLT ", "SLC "}; +static char *rsopcode[] = {"SRA ", "?188 ", "SRT ", "RTE "}; +static char tagc[] = " 123"; + +static int ascii_to_ebcdic_table[128] = +{ + 0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f, + 0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61, + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f, + + 0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6, + 0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d, + 0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96, + 0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07, +}; + +static int ebcdic_to_ascii (int ch) +{ + int j; + + for (j = 32; j < 128; j++) + if (ascii_to_ebcdic_table[j] == ch) + return j; + + return '?'; +} + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw) +{ + int32 cflag, ch, OP, F, TAG, INDIR, DSPLC, IR, eaddr; + char *mnem, tst[12]; + + cflag = (uptr == NULL) || (uptr == &cpu_unit); + +/* if (sw & SWMASK ('A')) { // ASCII? not useful + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + return SCPE_OK; + } +*/ + + if (sw & SWMASK ('C')) /* character? not useful -- make it EBCDIC */ + sw |= SWMASK('E'); + + if (sw & SWMASK ('E')) { /* EBCDIC! */ + ch = ebcdic_to_ascii((val[0] >> 8) & 0xFF); /* take high byte first */ + fprintf (of, (ch < ' ')? "<%03o>": "%c", ch); + ch = ebcdic_to_ascii(val[0] & 0xFF); + fprintf (of, (ch < ' ')? "<%03o>": "%c", ch); + return SCPE_OK; + } + + if (sw & SWMASK ('H')) { /* HOLLERITH! now THIS is useful! */ + ch = hollerith_to_ascii((int16) val[0]); + fprintf (of, (ch < ' ')? "<%03o>": "%c", ch); + return SCPE_OK; + } + + if (! (sw & SWMASK ('M'))) + return SCPE_ARG; + + IR = val[0]; + OP = (IR >> 11) & 0x1F; /* opcode */ + F = IR & 0x0400; /* format bit: 1 = long instr */ + TAG = IR & 0x0300; /* tag bits: index reg select */ + if (TAG) + TAG >>= 8; + + if (F) { /* long instruction, ASSUME it's valid (have to decrement IAR if not) */ + INDIR = IR & 0x0080; /* indirect bit */ + DSPLC = IR & 0x007F; /* displacement or modifier */ + if (DSPLC & 0x0040) + DSPLC |= ~ 0x7F; /* sign extend */ + + eaddr = val[1]; /* get reference address */ + } + else { /* short instruction, use displacement */ + INDIR = 0; /* never indirect */ + DSPLC = IR & 0x00FF; /* get displacement */ + if (DSPLC & 0x0080) + DSPLC |= ~ 0xFF; + + eaddr = DSPLC; + if (relative[OP] && ! TAG) + eaddr += addr+1; /* turn displacement into address */ + } + + mnem = opcode[OP]; /* get mnemonic */ + if (OP == 0x02) { /* left shifts are special */ + mnem = lsopcode[(DSPLC >> 6) & 0x0003]; + DSPLC &= 0x003F; + eaddr = DSPLC; + } + else if (OP == 0x03) { /* right shifts too */ + mnem = rsopcode[(DSPLC >> 6) & 0x0003]; + DSPLC &= 0x003F; + eaddr = DSPLC; + } + else if ((OP == 0x08 && F)|| OP == 0x09) { /* BSI L and BSC any */ + if (OP == 0x09 && (IR & 0x40)) + mnem = "BOSC"; + + tst[0] = '\0'; + if (DSPLC & 0x20) strcat(tst, "Z"); + if (DSPLC & 0x10) strcat(tst, "-"); + if (DSPLC & 0x08) strcat(tst, "+"); + if (DSPLC & 0x04) strcat(tst, "E"); + if (DSPLC & 0x02) strcat(tst, "C"); + if (DSPLC & 0x01) strcat(tst, "O"); + + if (F) { + fprintf(of, "%04x %s %c%c %s,%04x ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], tst, eaddr & 0xFFFF); + return -1; + } + fprintf(of, "%04x %s %c%c %s ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], tst); + return SCPE_OK; + } + else if (OP == 0x0e && TAG == 0) { /* MDX with no tag => MDM or jump */ + if (F) { + fprintf(of, "%04x %s %c%c %04x,%x (%d) ", IR & 0xFFFF, "MDM ", (INDIR ? 'I' : 'L'), tagc[TAG], eaddr & 0xFFFF, DSPLC & 0xFFFF, DSPLC); + return -1; + } + mnem = "JMP "; + } + + fprintf(of, "%04x %s %c%c %04x ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], eaddr & 0xFFFF); + return F ? -1 : SCPE_OK; /* inform how many words we read */ +} + +int32 get_reg (char *cptr, const char *strings[], char mchar) +{ +return -1; +} + +/* Number or memory address + + Inputs: + *cptr = pointer to input string + *dptr = pointer to output displacement + *pflag = pointer to accumulating flags + Outputs: + cptr = pointer to next character in input string + NULL if parsing error + + Flags: 0 (no result), A_NUM (number), A_REL (relative) +*/ + +char *get_addr (char *cptr, int32 *dptr, int32 *pflag) +{ + return 0; +} + +/* Specifier decode + + Inputs: + *cptr = pointer to input string + addr = current PC + n1 = 0 if no extra word used + -1 if extra word used in prior decode + *sptr = pointer to output specifier + *dptr = pointer to output displacement + cflag = true if parsing for the CPU + iflag = true if integer specifier + Outputs: + status = = -1 extra word decoded + = 0 ok + = +1 error +*/ + +t_stat get_spec (char *cptr, t_addr addr, int32 n1, int32 *sptr, t_value *dptr, + int32 cflag, int32 iflag) +{ + return -1; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ + return SCPE_ARG; +} + +#ifndef _WIN32 + +int strnicmp (const char *a, const char *b, int n) +{ + int ca, cb; + + for (;;) { + if (--n < 0) /* still equal after n characters? quit now */ + return 0; + + if ((ca = *a) == 0) /* get character, stop on null terminator */ + return *b ? -1 : 0; + + if (ca >= 'a' && ca <= 'z') /* fold lowercase to uppercase */ + ca -= 32; + + cb = *b; + if (cb >= 'a' && cb <= 'z') + cb -= 32; + + if ((ca -= cb) != 0) /* if different, return comparison */ + return ca; + + a++, b++; + } +} + +int strcmpi (const char *a, const char *b) +{ + int ca, cb; + + for (;;) { + if ((ca = *a) == 0) /* get character, stop on null terminator */ + return *b ? -1 : 0; + + if (ca >= 'a' && ca <= 'z') /* fold lowercase to uppercase */ + ca -= 32; + + cb = *b; + if (cb >= 'a' && cb <= 'z') + cb -= 32; + + if ((ca -= cb) != 0) /* if different, return comparison */ + return ca; + + a++, b++; + } +} + +#endif diff --git a/Ibm1130/ibm1130_t2741.c b/Ibm1130/ibm1130_t2741.c new file mode 100644 index 0000000..7ef37c3 --- /dev/null +++ b/Ibm1130/ibm1130_t2741.c @@ -0,0 +1,388 @@ +/*************************************************************************************** + * Nonstandard serial attachment for remote 2741 terminal (IO selectric) used by APL\1130 + * This implementation may be incomplete and/or incorrect + ***************************************************************************************/ + +#include "ibm1130_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#define DEBUG_T2741 + +static TMLN t2741_ldsc = { 0 }; /* line descr for telnet attachment */ +static TMXR t2741_tmxr = { 1, 0, 0, &t2741_ldsc }; /* line mux for telnet attachment */ + +#define T2741_DSW_TRANSMIT_NOT_READY 0x4000 +#define T2741_DSW_READ_RESPONSE 0x1000 +#define T2741_DSW_READ_OVERRUN 0x0800 +#define T2741_DSW_ATTENTION 0x0010 + +#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT) + +#define UNIT_V_PHYSICAL_TERM (UNIT_V_UF + 0) /* indicates not telnet but attachment to real terminal */ +#define UNIT_V_UPCASE (UNIT_V_UF + 1) /* indicates upshift performed */ +#define UNIT_V_SENDING (UNIT_V_UF + 2) /* indicates not telnet but attachment to real terminal */ +#define UNIT_V_RECEIVING (UNIT_V_UF + 3) /* indicates not telnet but attachment to real terminal */ + +#define UNIT_PHYSICAL_TERM (1u << UNIT_V_PHYSICAL_TERM) +#define UNIT_UPCASE (1u << UNIT_V_UPCASE) +#define UNIT_SENDING (1u << UNIT_V_SENDING) +#define UNIT_RECEIVING (1u << UNIT_V_RECEIVING) + +#define CODE_SHIFTUP 0x1C00 +#define CODE_SHIFTDOWN 0x7C00 +#define CODE_CIRCLEC 0x1F00 +#define CODE_CIRCLED 0x1600 +#define CODE_RETURN 0x5B00 +#define CODE_LINEFEED 0x3B00 +#define CODE_ATTENTION 0x0001 /* pseudocode, never really returned as a received character */ +#define CODE_UNKNOWN 0x0000 + +static t_stat t2741_svc (UNIT *uptr); +static t_stat t2741_reset (DEVICE *dptr); +static t_stat t2741_attach (UNIT *uptr, char *cptr); +static t_stat t2741_detach (UNIT *uptr); +static uint16 ascii_to_t2741 (int ascii); +static char * t2741_to_ascii (uint16 code); +static void set_transmit_notready (void); + +static uint16 t2741_dsw = T2741_DSW_TRANSMIT_NOT_READY; /* device status word */ +static uint32 t2741_swait = 200; /* character send wait */ +static uint32 t2741_rwait = 2000; /* character receive wait */ +static uint16 t2741_char = 0; /* last character received */ +static int overrun = FALSE; +static uint32 t2741_socket = 1130; + +UNIT t2741_unit[1] = { + { UDATA (&t2741_svc, UNIT_ATTABLE, 0) }, +}; + +REG t2741_reg[] = { + { HRDATA (DSW, t2741_dsw, 16) }, /* device status word */ + { DRDATA (RTIME, t2741_rwait, 24), PV_LEFT }, /* character receive wait */ + { DRDATA (STIME, t2741_swait, 24), PV_LEFT }, /* character send wait */ + { DRDATA (SOCKET, t2741_socket,16), PV_LEFT }, /* socket number */ + { HRDATA (LASTCHAR, t2741_char, 16), PV_LEFT }, /* last character read */ + { NULL } }; + +DEVICE t2741_dev = { + "T2741", t2741_unit, t2741_reg, NULL, + 1, 16, 16, 1, 16, 16, + NULL, NULL, t2741_reset, + NULL, t2741_attach, t2741_detach}; + +/* xio_t2741_terminal - XIO command interpreter for the terminal adapter */ + +void xio_t2741_terminal (int32 iocc_addr, int32 iocc_func, int32 iocc_mod) +{ + char msg[80]; + uint16 code; + + switch (iocc_func) { + case XIO_READ: /* read: return last character read */ + code = t2741_char & 0xFF00; + M[iocc_addr & mem_mask] = code; + overrun = FALSE; +#ifdef DEBUG_T2741 +/* trace_both("T2741 %04x READ %02x %s", prev_IAR, code >> 8, t2741_to_ascii(code)); */ +#endif + break; + + case XIO_WRITE: /* write: initiate character send */ + code = M[iocc_addr & mem_mask] & 0xFF00; +#ifdef DEBUG_T2741 + trace_both("T2741 %04x SEND %02x %s", prev_IAR, code >> 8, t2741_to_ascii(code)); +#endif + SETBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY); + SETBIT(t2741_unit->flags, UNIT_SENDING); + + if (code == CODE_SHIFTUP) + SETBIT(t2741_unit->flags, UNIT_UPCASE); + else if (code == CODE_SHIFTDOWN) + CLRBIT(t2741_unit->flags, UNIT_UPCASE); + + sim_activate(t2741_unit, t2741_swait); /* schedule interrupt */ + break; + + case XIO_SENSE_DEV: /* sense device status */ + ACC = t2741_dsw; +#ifdef DEBUG_T2741 +/* trace_both("T2741 %04x SENS %04x%s", prev_IAR, t2741_dsw, (iocc_mod & 0x01) ? " reset" : ""); */ +#endif + if (iocc_mod & 0x01) { /* reset interrupts */ + CLRBIT(t2741_dsw, T2741_DSW_READ_RESPONSE); + CLRBIT(ILSW[4], ILSW_4_T2741_TERMINAL); + } + break; + + case XIO_CONTROL: /* control: do something to interface */ +#ifdef DEBUG_T2741 + trace_both("T2741 %04x CTRL %04x", prev_IAR, iocc_mod &0xFF); +#endif + SETBIT(t2741_unit->flags, UNIT_RECEIVING); /* set mode to receive mode */ + if (IS_ONLINE(t2741_unit) && (t2741_char != 0 || ! feof(t2741_unit->fileref))) { + sim_activate(t2741_unit, t2741_rwait); + t2741_char = (CODE_CIRCLED >> 8); /* first character received after turnaround is circled */ + } + break; + + default: + sprintf(msg, "Invalid T2741 XIO function %x", iocc_func); + xio_error(msg); + } +} + +static void set_transmit_notready (void) +{ + if (IS_ONLINE(t2741_unit) && ! (t2741_unit->flags & UNIT_SENDING)) + CLRBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY); + else + SETBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY); +} + +static t_stat t2741_svc (UNIT *uptr) +{ + int ch = EOF; + uint16 code; + + if (uptr->flags & UNIT_SENDING) { /* xmit: no interrupt, as far as I know. just clr busy bit */ + CLRBIT(uptr->flags, UNIT_SENDING); + set_transmit_notready(); + } + + if (uptr->flags & UNIT_RECEIVING) { /* rcv: fire interrupt */ + t2741_char <<= 8; + + if (t2741_char == 0) { /* there is no 2nd character from previous ascii input */ + if ((ch = getc(t2741_unit->fileref)) == EOF) + t2741_char = 0; + else { + if (ch == '\r') { /* if we get CR, jump to LF */ + if ((ch = getc(t2741_unit->fileref)) != '\n') { + ungetc(ch, t2741_unit->fileref); + ch = '\r'; + } + } + + if (ch == '\027') { + t2741_char = CODE_LINEFEED; /* attention key sends line feed character */ +#ifdef DEBUG_T2741 + trace_both("T2741 ---- ATTENTION"); +#endif + SETBIT(t2741_dsw, T2741_DSW_ATTENTION); /* no character returned ? */ + } + else { + t2741_char = ascii_to_t2741(ch); /* translate to 2741 code(s) */ + } + } + } + + code = t2741_char & 0xFF00; + + if (t2741_char != 0) { + if (overrun) /* previous character was not picked up! */ + SETBIT(t2741_dsw, T2741_DSW_READ_OVERRUN); + + SETBIT(t2741_dsw, T2741_DSW_READ_RESPONSE); + SETBIT(ILSW[4], ILSW_4_T2741_TERMINAL); /* issue interrupt */ + calc_ints(); + +#ifdef DEBUG_T2741 + trace_both("T2741 ---- RCVD %02x '%s' RDRESP%s%s", code >> 8, t2741_to_ascii(code), + (t2741_dsw & T2741_DSW_READ_OVERRUN) ? "|OVERRUN" : "", + (t2741_dsw & T2741_DSW_ATTENTION) ? "|ATTENTION" : ""); +#endif + + overrun = TRUE; /* arm overrun flag */ + } + + if (t2741_char == CODE_CIRCLEC) /* end of line (CIRCLEC after RETURN) auto downshifts */ + CLRBIT(t2741_unit->flags, UNIT_UPCASE); + + if (t2741_char == 0 || code == CODE_CIRCLEC) + CLRBIT(uptr->flags, UNIT_RECEIVING); /* on enter or EOF, stop typing */ + else + sim_activate(t2741_unit, t2741_rwait); /* schedule next character to arrive */ + } + + return SCPE_OK; +} + +static t_stat t2741_attach (UNIT *uptr, char *cptr) +{ + int rval; + + if ((rval = attach_unit(uptr, cptr)) == SCPE_OK) { /* use standard attach */ + t2741_char = 0; + overrun = FALSE; + + CLRBIT(t2741_unit->flags, UNIT_UPCASE); + + if ((t2741_unit->flags & UNIT_RECEIVING) && ! feof(t2741_unit->fileref)) + sim_activate(t2741_unit, t2741_rwait); /* schedule interrupt */ + } + + set_transmit_notready(); + + return rval; +} + +static t_stat t2741_detach (UNIT *uptr) +{ + t_stat rval; + + if (t2741_unit->flags & UNIT_RECEIVING) /* if receive was pending, cancel interrupt */ + sim_cancel(t2741_unit); + + t2741_char = 0; + overrun = FALSE; + + rval = detach_unit(uptr); /* use standard detach */ + + set_transmit_notready(); + + return rval; +} + +static t_stat t2741_reset (DEVICE *dptr) +{ + sim_cancel(t2741_unit); + + CLRBIT(t2741_unit->flags, UNIT_SENDING|UNIT_RECEIVING|UNIT_UPCASE); + + t2741_char = 0; + t2741_dsw = 0; + overrun = FALSE; + + set_transmit_notready(); + + CLRBIT(ILSW[4], ILSW_4_T2741_TERMINAL); + calc_ints(); + + return SCPE_OK; +} + +static struct tag_t2741_map { + int code; + int lcase, ucase; + t_bool shifts; +} t2741_map[] = { + {0x4F00, 'A', 'a', TRUE}, + {0x3700, 'B', 'b', TRUE}, + {0x2F00, 'C', 'c', TRUE}, + {0x2A00, 'D', 'd', TRUE}, + {0x2900, 'E', 'e', TRUE}, + {0x6700, 'F', '_', TRUE}, + {0x6200, 'G', 'g', TRUE}, + {0x3200, 'H', 'h', TRUE}, + {0x4C00, 'I', 'i', TRUE}, + {0x6100, 'J', 'j', TRUE}, + {0x2C00, 'K', '\'', TRUE}, + {0x3100, 'L', 'l', TRUE}, + {0x4300, 'M', '|', TRUE}, + {0x2500, 'N', 'n', TRUE}, + {0x5100, 'O', 'o', TRUE}, + {0x6800, 'P', '*', TRUE}, + {0x6D00, 'Q', '?', TRUE}, + {0x4A00, 'R', 'r', TRUE}, + {0x5200, 'S', 's', TRUE}, + {0x2000, 'T', '~', TRUE}, + {0x2600, 'U', 'u', TRUE}, + {0x4600, 'V', 'v', TRUE}, + {0x5700, 'W', 'w', TRUE}, + {0x2300, 'X', 'x', TRUE}, + {0x7300, 'Y', 'y', TRUE}, + {0x1500, 'Z', 'z', TRUE}, + {0x1300, '0', '&', TRUE}, + {0x0200, '1', '?', TRUE}, + {0x0400, '2', '?', TRUE}, + {0x0700, '3', '<', TRUE}, + {0x1000, '4', '?', TRUE}, + {0x0800, '5', '=', TRUE}, + {0x0D00, '6', '?', TRUE}, + {0x0B00, '7', '>', TRUE}, + {0x0E00, '8', '?', TRUE}, + {0x1600, '9', '|', TRUE}, + {0x7000, '/', '\\', TRUE}, + {0x7600, '+', '-', TRUE}, + {0x6400, '?', '?', TRUE}, + {0x4000, '<', '>', TRUE}, + {0x6B00, '[', '(', TRUE}, + {0x4900, ']', ')', TRUE}, + {0x6E00, ',', ';', TRUE}, + {0x4500, '.', ':', TRUE}, + {0x0100, ' ', 0, FALSE}, + {0x5B00, '\r', 0, FALSE}, + {0x3B00, '\n', 0, FALSE}, + {0x5D00, '\b', 0, FALSE}, + {0x5E00, '\t', 0, FALSE}, + {0x0001, '\027', 0, FALSE}, +}; + +static uint16 ascii_to_t2741 (int ascii) +{ + int i; + uint16 rval = 0; + + ascii &= 0xFF; + + if (ascii == '\n') /* turn newlines into returns + CIRCLED? */ + return CODE_RETURN | (CODE_CIRCLEC >> 8); + + for (i = sizeof(t2741_map)/sizeof(t2741_map[0]); --i >= 0; ) { + if (t2741_map[i].shifts) { + if (t2741_map[i].lcase == ascii) { + rval = t2741_map[i].code; + if (t2741_unit->flags & UNIT_UPCASE) { + CLRBIT(t2741_unit->flags, UNIT_UPCASE); + rval = CODE_SHIFTDOWN | (rval >> 8); + } + return rval; + } + if (t2741_map[i].ucase == ascii) { + rval = t2741_map[i].code; + if (! (t2741_unit->flags & UNIT_UPCASE)) { + SETBIT(t2741_unit->flags, UNIT_UPCASE); + rval = CODE_SHIFTUP | (rval >> 8); + } + return rval; + } + } + else if (t2741_map[i].lcase == ascii) + return t2741_map[i].code; + } + + return CODE_UNKNOWN; +} + +static char * t2741_to_ascii (uint16 code) +{ + int i; + static char string[2] = {'?', '\0'}; + + switch (code) { + case CODE_SHIFTUP: return "SHIFTUP"; + case CODE_SHIFTDOWN: return "SHIFTDN"; + case CODE_CIRCLEC: return "CIRCLEC"; + case CODE_CIRCLED: return "CIRCLED"; + } + + for (i = sizeof(t2741_map)/sizeof(t2741_map[0]); --i >= 0; ) { + if (t2741_map[i].code == code) { + if (t2741_map[i].shifts) { + string[0] = (t2741_unit->flags & UNIT_UPCASE) ? t2741_map[i].ucase : t2741_map[i].lcase; + return string; + } + switch (t2741_map[i].lcase) { + case ' ': return " "; + case '\r': return "RETURN"; + case '\n': return "LINEFEED"; + case '\b': return "BS"; + case '\t': return "IDLE"; + } + break; + } + } + + return "?"; +} diff --git a/Ibm1130/ibm1130res.h b/Ibm1130/ibm1130res.h new file mode 100644 index 0000000..d84a46e --- /dev/null +++ b/Ibm1130/ibm1130res.h @@ -0,0 +1,17 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by ibm1130.rc +// +#define IDB_CONSOLE 101 +#define IDC_MYHAND 102 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Ibm1130/makefile b/Ibm1130/makefile new file mode 100644 index 0000000..647d053 --- /dev/null +++ b/Ibm1130/makefile @@ -0,0 +1,74 @@ +# (This makefile is for operating systems other than Windows, +# or compilers other than Microsoft's. For MS builds, use the +# .mak files found in this directory and the utils directory). +# +# If you are building the emulator and utilities as part of +# the SIMH package, please: +# +# Be sure that you there are NO copies of scp.c, scp_tty.c, +# sim_sock.c, sim_tmxr.c, sim_rev.h, sim_defs.h, sim_sock.h and +# sim_tmxr.h in the ibm1130 subdirectory. Delete them if there +# are. +# +# Do not use this makefile with "make all" or "make ibm1130". +# Use the SIMH build files instead. +# +# If and when you download updates for this simulator from +# www.ibm1130.org, get ibm1130code.zip and ibm1130software.zip +# separately. +# +# If you have downloaded the emulator independently of SIMH (e.g, from +# www.ibm1130.org), please: +# +# Be sure that you DO have copies of scp.c, scp_tty.c, sim_sock.c, +# sim_tmxr.c, sim_rev.h, sim_defs.h, sim_sock.h and sim_tmxr.h +# in this folder. +# +# Use this file to make the emulator. +# +# If and when you download updates for this simulator from +# www.ibm1130.org, get ibm1130.zip. When you expand it, +# also expand ibm1130sofware.zip, which is inside. +# +# In either case, if you want to build DMS or work with assembly +# language programs outside of DMS, you'll want to make the utilities +# by cd'ing to the utils directory and running make there. + +# CC Command +# +# Note: -O2 is sometimes broken in GCC when setjump/longjump is being +# used. Try -O2 only with released simulators. +# +CC = gcc -O0 -lm -I . +#CC = gcc -O2 -g -lm -I . + + +# +# Common Libraries +# +BIN = +SIM = scp.c sim_console.c sim_fio.c sim_sock.c sim_timer.c sim_tmxr.c scp_tty.c +SIM_INC = scp.h sim_console.h sim_defs.h sim_fio.h sim_rev.h sim_sock.h sim_timer.h sim_tmxr.h + +# +# Emulator source files and compile time options +# + +ibm1130D = ./ +ibm1130 = ${ibm1130D}ibm1130_sys.c ${ibm1130D}ibm1130_cpu.c \ + ${ibm1130D}ibm1130_cr.c ${ibm1130D}ibm1130_disk.c \ + ${ibm1130D}ibm1130_stddev.c ${ibm1130D}ibm1130_gdu.c \ + ${ibm1130D}ibm1130_gui.c ${ibm1130D}ibm1130_prt.c \ + ${ibm1130D}ibm1130_ptrp.c ${ibm1130D}ibm1130_fmt.c + +ibm1130_INC = ibm1130res.h ibm1130_conin.h ibm1130_conout.h \ + ibm1130_defs.h ibm1130_prtwheel.h ibm1130_fmt.h \ + dmsr2v12phases.h dmsr2v12slet.h + +# +# Build the emulator +# + +${BIN}ibm1130 : ${ibm1130} ${SIM} ${ibm1130_INC} ${SIM_INC} + ${CC} ${ibm1130} ${SIM} -o $@ + diff --git a/Ibm1130/readme1130.txt b/Ibm1130/readme1130.txt new file mode 100644 index 0000000..d2b5da2 --- /dev/null +++ b/Ibm1130/readme1130.txt @@ -0,0 +1,189 @@ +Here's the 1130 simulator as it stands now. + +Status: 22Jul2003 + + * Added support for APL\1130 output translations + and some bug fixes uncovered by APL. + +Status: 13Sep2002 + + * Added support for 1403 printer. It's MUCH faster + even in emulation. Not important for general use, + but it will help the CGI version a lot. + +Status: 16Aug2002 + + * Disk Monitor System R2V12 is available including the + Macro Assembler, Fortran Compiler and System Library. + + * There was a bug in the multiply instruction. This has + been fixed, and now the single precision trig functions + work correctly. + + * The card punch does not yet work correctly. + + * The card reader, punch and disk don't compute their device + status word until an XIO requests it; this is probably bad + as the "examine" command will show the wrong value. Doesn't + affect functioning of emulated software, though. + + * Documentation is a work in progress, see ibm1130.doc + in ibm1130software.zip. We hope to have it finished in + October. This is a Word document. Will distribute as a + PDF when it's finished. + + * Thanks to Oscar E Wyss (www.cosecans.ch) for + the DMS V12 source code listings and one card + programs, to Douglas W. Jones for the DMS V10, 11 and + 12 microfiche (which will end up scanned on IBM1130.org). + + * Thanks to Robert Alan Byer for adding the 1130 + to the simh makefiles & testing the builds on several + platforms. + + * For updated information about the 1130 and for + future 1130 OS and application software developments, + check www.ibm1130.org periodically. Sign up for the + mailing list to get updates as they occur! + + * Cross-assembler has been updated to handle card image input + correctly. The DMS sources seems to mix up @ and ' + as a leading symbol in labels, I have to find out why + this is. + +BUILD NOTES: if you download this simulator directly from +IBM1130.org, the makefile, source, and binaries are all in +the main directory. If you use the version from Bob Supnik's +SIMH distribution, the makefile is in the main simh +directory, and the SCP files used are Bob's. For a +Windows build, use the .mak file in the IBM1130 directory, +as this incorporates the GUI. + +Make the utilities in the utils directory if you want +to actually build and load DMS from scratch. Move the +executables to a common directory in your search path + +Brian Knittel +brian@ibm1130.org + +-------------------------------------------------------------------------- +Some sample things to run: +(it's best to hit CHECK RESET or type "reset" between program runs!) + +* Run a Fortran Program + ibm1130 + do job roots + do job csort + +* List the monitor system disk's contents + ibm1130 + do job list + +* Look into the files "job", "roots.job" and "csort.job" and "list.job" + to see the actual input files + +* When the jobs have run (stop at 2A with 1000 in the + accumulator), detach the printer (det prt) and look at + the output file: for.lst or asm.lst. The supplied "job" + script displays the print output automatically on Windows + builds. + +-------------------------------------------------------------------------- +Contents: + +There are several programs: + + ibm1130 the simulator + asm1130 cross assembler + bindump dumps contents of relocatable format object decks (xxx.bin) + checkdisk validates DMS disk format + diskview dumps contents of DMS disk directory + mkboot creates IPL and Core Image Format Decks from .bin + viewdeck displays contents of Hollerith-format binary decks + +Files in the software (sw) directory: + + actual 1130 software: + dms.dsk disk image file containing Disk Monitor System + zdcip.asm disk cartridge initialization program + zcrdumpc.asm a cold-start-mode one card memory dump program + dmsboot.asm source code for the DMS cold start loader + + contributed software: + onecard/* one-card programs from Oscar Wyss + +-------------------------------------------------------------------------- +Status of the simulator: + +* There is a reasonably fun console GUI available for Windows builds, + as well as support for the 2250 graphical display. + +* The card reader emulator now supports deck files with literal cards and + breakpoints. The command "attach cr @filename" tells the simulator to + read data from the files named in the specified file. Input lines are of + the following form: + + filename a -- input file to be read as ascii text + filename b -- input file to be read as binary card images + !xyz... -- literal text xyz..., treated as a card + !break -- halts the simulator + #comment -- remarks + +* The do command may have arguments after the filename. These may be + interpolated in the script and in card reader deck files with %1, %2, etc + +-------------------------------------------------------------------------- +sample usage +-------------------------------------------------------------------------- + +ibm1130 + starts SIMH-based simulator. + Optional command line arguments: -q quiet mode, -g no GUI + + Enhancements: + + * Windows builds display a console window + + * CPU activity log + + the command "attach cpu file.log" will make the simulator + write a detailed log of CPU and IO activity, good for + debugging. Turn off with "detach cpu". + + * DO command [arg1 arg2...] + reads file 'filename' for SIMH commands. Lets you write + simh command files to be run from the prompt rather + than just the command line. In the do command file, %1 will + be replaced by the first command line argument, etc. This + applies to the script run from the ibm1130 command line too. + + * DELETE filename + deletes the named file + + * VIEW filename + displays the named file with "notepad." (Windows only). + +-------------------------------------------------------------------------- +asm1130 -l program.asm + + compiles source file, creates simulator load + file (program.out) and listing file (program.lst) + + The cross assembler wants files either in strict column + layout matching the IBM spec, or, if tabs are present in the + source file, + + labelopcodeflagsoperand + + The output file is in the format used by the 1130 simulator's + load command. + +-------------------------------------------------------------------------- + +Note: the DMS disk is built with the Windows batch file "mkdms.bat". + +Subnote: DMS cannot be built with the 1130's native assembler. + + +-------------------------------------------------------------------------- +check www.ibm1130.org for updates... diff --git a/Ibm1130/readme_update.txt b/Ibm1130/readme_update.txt new file mode 100644 index 0000000..05a5ff9 --- /dev/null +++ b/Ibm1130/readme_update.txt @@ -0,0 +1,60 @@ +Version: 10 July 2003 + +History (partial): + +2003-11-15 Changed default value of TTO STIME to 200. It was + defined using a constant from sim_defs.h which was + changed from 10 to 100 at some point. APL\1130 has a + sychronization bug & hangs if the console output complete + interrupt occurs between the XIO SENSE and WAIT instructions. + This bug is hit frequently if the delay time is set to + 100 instructions. 10 worked reliably, but is really not realistic, + and 200 may not be adequate in all cases, but we'll try 200 for now. + +2003-11-00 Updated GUI to allow drag and drop to simulated card + reader, tear-off from simulated printer + +2003-07-10 Fixed disk and console terminal bugs uncovered by + APL\1130. Added APL keyboard and output font support + to enable use of APL\1130. APL will be released soon. + +2003-03-18 Fixed bug in asm1130 that produced an error message + with a (legal) offset of +127 in MDX instructions. + + Fixed sign bug in 1130 emulator divide instruction. + +Interim 1130 distribution: +-------------------------------------------- + +folders: + . sources + winrel windows executables + windebug windows executables + utils accessory programs + utils\winrel windows executables + utils\windebug windows executables + sw working directory for DMS build & execution + sw\dmsR2V12 Disk Monitor System sources + +programs: + asm1130 cross assembler + bindump object deck dump tool, also used to sort decks by phase id + checkdisk DMS disk image check and dump + diskview work in progress, interpreted disk image dump + ibm1130 emulator + mkboot object deck to IPL and core image converter + viewdeck binary to hollerith deck viewer if needed to view phase ID cards and ident fields + +batch file: + mkdms.bat builds DMS objects and binary cards. Need a shell script version of this. + +IBM1130 simulator DO command scripts: + format format a disk image named DMS.DSK + loaddms format and install DMS onto the formatted DMS.DSK + for run a Fortran program + list list the disk contents + asm assemble a program + +ancillary files: + loaddms.deck list of files stacked into the card reader for loaddms + *.deck other sample deck files diff --git a/Ibm1130/utils/asm1130.c b/Ibm1130/utils/asm1130.c new file mode 100644 index 0000000..3cbc2f4 --- /dev/null +++ b/Ibm1130/utils/asm1130.c @@ -0,0 +1,4585 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +#define VERSION "ASM1130 CROSS ASSEMBLER V1.14" + +// --------------------------------------------------------------------------------- +// ASM1130 - IBM 1130 Cross Assembler +// +// Version +// 1.14 - 2004Oct22 - Fixed problem with BSS complaining about negative +// sizes. This may be a fundamental problem with my using +// 32-bit expressions, but for now, it appears that just +// truncating the BSS size to 16 bits is sufficient to build DMS. +// 1.13 - 2004Jun05 - Fixed sign extension of constants in expressions. Statements +// like LD /FFFF were being assembled incorrectly. +// 1.12 - 2004Jun04 - Made WAIT instruction take a displacement value. +// Doesn't affect operation, but these are used as indicators +// in the IBM one-card diagnostic programs. +// Also -- should mention that the .IPL directive was +// removed some time ago. To create bootable cards, +// use -b flag to create binary output, and post-process the +// binary output with program "mkboot" +// 1.11 - 2004May22 - Added CMP, DCM, and DECS instructions for 1800, +// thanks to Kevin Everets. +// 1.10 - 2003Dec08 - Fixed opcode value for XCH instruction, thanks to +// Roger Simpson. +// 1.09 - 2003Aug03 - Added fxwrite so asm will write little-endian files +// on all CPUs. +// 1.08 - 2003Mar18 - Fixed bug that complained about valid MDX displacement of +127 +// 1.07 - 2003Jan05 - Filenames are now left in lower case. SYMBOLS.SYS stays all upper case +// 1.06 - 2002May02 - Fixed bug in ebdic constants (data goes into low byte) +// First stab at adding ISS level # info, this is iffy +// 1.05 - 2002Apr24 - Made negative BSS size a warning not an error, as it +// it's looking like it happens twice in PTMASMBL. +// This version still doesn't do fixed point numbers and +// negative floats may be wrong. +// 1.04 - 2002Apr18 - Added binary (card loader format) output, removed +// interim IPL output formats and moved that to MKBOOT. +// Enhanced relocatable code handling. Added floating +// point constants, but don't know how to make fixed point +// constants yet. Made amenable to syntax variations found +// in the DMS sources. Doesn't properly handle ILS +// modules yet and ISS is probably wrong. +// 1.03 - 2002Apr10 - numerous fixes, began to add relative/absolute support +// 1.02 - 2002Feb26 - replaced "strupr" with "upcase" for compatibility +// 1.01 - 2002Feb25 - minor compiler compatibility changes +// 1.00 - 2002Feb01 - first release. Tested only under Win32. +// --------------------------------------------------------------------------------- +// +// Usage: +// asm1130 [-bvsx] [-o[file]] [-l[file]] [-rN.M] file... +// +// Description: +// -b binary output (.bin, relocatable absolute format) +// -v verbose +// -s print symbol table +// -x print cross references +// -o output file (default is name of first source file + extension .out or .bin) +// -l listing file (default is name of first source file + extension .lst) +// -y preload system symbol table SYMBOLS.SYS (from the current directory) +// -w write the system symbol table SYMBOLS.SYS in the current directory +// -W same as -w but don't prompt to confirm overwriting existing file +// -r set DMS release to release N version M, for sbrk cards +// +// Listing and symbol table output can be turned on by *LIST directives in the source, too +// Listing file default extension is .LST +// +// Input files can use strict IBM 1130 Assembler column format, or loose formatting +// with tabs, or any mix on a line-by-line basis. Input files default extension is .ASM. +// +// Strict specification is: +// +// label columns 1 - 5 +// opcode 7 - 10 +// tag 12 +// index 13 +// arguments 15 - 51 +// +// Loose, indicated by presence of ascii tab character(s): +// +// labelopcodeindex and format indicatorsarguments +// +// In both cases, the IBM convention that the arguments section ends with the +// first nonblank applies. This means that ".DC 1, 2, 3" assembles only the 1! +// +// Output file format is that used by the LOAD command in my 1130 +// simulator. Lines are any of the following. All values are in hex: +// +// @addr load address for subsequent words is addr +// Znwords Zero the next "nwords" and increment load address by nwords. +// =addr set IAR register to address addr (a convenience) +// value load value at load address and increment load address +// +// Output file default extension is .OUT or .BIN for binary assemblies +// +// Note: this version does not handle relative assembly, and so doesn't carry +// absolute/relative indication through expression calculation. +// +// Seems to work. Was able to assemble the resident monitor OK. +// >>> Look for "bug here" though, for things to check out. +// +// Notes: +// We assume that the computer on which the assembler runs uses ANSI floating point. +// Also, the assembly of floating point values may be incorrect on non-Intel +// architectures, this needs to be investigated. +// +// org_advanced tells whether * in an expression refers to the address AFTER the +// instruction (1 or 2 words, depending on length). This is the case for opcodes +// but not all directives. +// +// Revision History +// 16Apr02 1.03 Added sector break, relocation flag output +// 02Apr02 1.02 Fixed bug in BOSC: it CAN be a short instruction. +// Added directives for 1130 and 1800 IPL output formats +// Added conditional assembly directives +// --------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include "util_io.h" + +// ---------------------------------------------------------------1------------------ +// DEFINITIONS +// --------------------------------------------------------------------------------- + +// I have found some IBM source code where @ and ' seem interchangable (likely due to the +// use of 026 keypunches). +// Comment out this define to make @ and ' different in symbol names, keep to make equivalent + +#if defined(VMS) + # include /* to pick up 'unlink' */ +#endif + +#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b))) +#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#define MAX(a,b) (((a) >= (b)) ? (a) : (b)) + +#ifndef _WIN32 + int strnicmp (char *a, char *b, int n); + int strcmpi (char *a, char *b); +#endif + +#define FIX_ATS + +#define DMSVERSION "V2M12" /* required 5 characters on sector break card col 67-71 */ + +#define DOLLAREXIT "/38" // hmmm, are these really fixed absolutely in all versions? +#define DOLLARDUMP "/3F" + +#define SYSTEM_TABLE "SYMBOLS.SYS" + +#define BOOL int +#define TRUE 1 +#define FALSE 0 + +#define ISTV 0x33 // magic number from DMS R2V12 monitorm symbol @ISTV + +#define MAXLITERALS 300 +#define MAXENTRIES 14 + +#define LINEFORMAT " %4ld | %s" +#define LEFT_MARGIN " |" + // XXXX XXXX XXXX XXXX XXXX XXXX + // org w1 w2 w3 w4 w5 + // XXXX 1111 2222 3333 4444 LLLL | + // 12345678901234567890123456789012 + +typedef enum {ABSOLUTE = 0, RELATIVE = 1, LIBF = 2, CALL = 3} RELOC; + +typedef struct tag_symbol { // symbol table entry: + char *name; // name of symbol + int value; // value (absolute) + int pass; // defined during pass # + int defined; // definition state, see #defines below + RELOC relative; // ABSOLUTE = absolute, RELATIVE = relative + struct tag_symbol *next; // next symbol in list + struct tag_xref *xrefs; // cross references +} SYMBOL, *PSYMBOL; + +#define S_UNDEFINED 0 // values of 'defined' +#define S_PROVISIONAL 1 // usually an expression with forward references +#define S_DEFINED 2 // ordering must be undef < prov < def + +typedef struct tag_xref { // cross reference entry + char *fname; // filename + int lno; // line number + BOOL definition; // true = definition, false = reference + struct tag_xref *next; // next reference +} XREF, *PXREF; + +typedef struct tag_expr { // expression result: absolute or relative + int value; + RELOC relative; +} EXPR; + +typedef enum {PROGTYPE_ABSOLUTE = 1, PROGTYPE_RELOCATABLE = 2, PROGTYPE_LIBF = 3, PROGTYPE_CALL = 4, + PROGTYPE_ISSLIBF = 5, PROGTYPE_ISSCALL = 6, PROGTYPE_ILS = 7} PROGTYPE; + +typedef enum {SUBTYPE_INCORE = 0, SUBTYPE_FORDISK = 1, SUBTYPE_ARITH = 2, + SUBTYPE_FORNONDISK = 3, SUBTYPE_FUNCTION=8} SUBTYPE; + +typedef enum {INTMODE_UNSPECIFIED = 0, INTMODE_MATCHREAL = 0x0080, INTMODE_ONEWORD = 0x0090} INTMODE; +typedef enum {REALMODE_UNSPECIFIED = 0, REALMODE_STANDARD = 0x0001, REALMODE_EXTENDED = 0x0002} REALMODE; + +#define OP_INDEXED 0x0300 // 1130 opcode modifier bits +#define OP_LONG 0x0400 +#define OP_INDIRECT 0x0080 + +typedef enum {OUTMODE_LOAD, OUTMODE_1130, OUTMODE_1800, OUTMODE_BINARY} OUTMODE; + +#ifdef _WIN32 +# define OUTWRITEMODE "wb" // write outfile in binary mode +# define ENDLINE "\r\n" // explictly write CR/LF +#else +# define OUTWRITEMODE "w" // use native mode +# define ENDLINE "\n" +#endif + +// --------------------------------------------------------------------------------- +// GLOBALS +// --------------------------------------------------------------------------------- + +// command line syntax +char *usestr = +"Usage: asm1130 [-bpsvwxy8] [-o[file]] [-l[file]] [-rN.M] file...\n\n" +"-b binary (relocatable format) output; default is simulator LOAD format\n" +"-p count passes required; no assembly output is created with this flag" +"-s add symbol table to listing\n" +"-v verbose mode\n" +"-w write system symbol table as SYMBOLS.SYS\n" +"-W same as -w but do not confirm overwriting previous file\n" +"-x add cross reference table to listing\n" +"-y preload system symbol table SYMBOLS.SYS\n" +"-o set output file; default is first input file + .out or .bin\n" +"-l create listing file; default is first input file + .lst\n" +"-r set dms version to VN RM for system SBRK cards\n" +"-8 enable IBM 1800 instructions"; // (alternately, rename or link executable to asm1800.exe) + +BOOL verbose = FALSE; // verbose mode flag +BOOL tabformat = FALSE; // TRUE if tabs were seen in the file +BOOL enable_1800 = FALSE; // TRUE if 1800 mode is enabled by flag or executable name +int pass; // current assembler pass (1 or 2) +char curfn[256]; // current input file name +char progname[8]; // base name of primary input file +char *outfn = NULL; // output file name +int lno; // current input file line number +BOOL preload = FALSE; // preload system symbol table +BOOL savetable = FALSE; // write system symbol table +BOOL saveprompt = TRUE; // prompt before overwriting +int nerrors = 0; // count of errors +int nwarnings = 0; // count of warnings +FILE *fin = NULL; // current input file +FILE *fout = NULL; // output file stream +OUTMODE outmode = OUTMODE_LOAD; // output file mode +int outcols = 0; // columns written in using card output +int maxiplcols = 80; +char cardid[9]; // characters used for IPL card ID +FILE *flist = NULL; // listing file stream +char *listfn = NULL; // listing filename +BOOL do_list = FALSE; // flag: create listing +BOOL passcount = FALSE; // flag: count passes only +BOOL list_on = TRUE; // listing is currently enabled +BOOL do_xref = FALSE; // cross reference listing +BOOL do_syms = FALSE; // symbol table listing +BOOL ended = FALSE; // end of current file +BOOL hasforward = FALSE; // true if there are any forward references +char listline[350]; // output listing line +BOOL line_error; // already saw an error on current line +RELOC relocate = RELATIVE; // relocatable assembly mode +BOOL assembled = FALSE; // true if any output has been generated +int nwout; // number of words written on current line +int org = 0; // output address (origin) +int org_advanced; // if TRUE, * means instruction addr+(value) during evaluation +int pta = -1; // program transfer address +BOOL cexpr = FALSE; // "C" expression syntax +PSYMBOL symbols = NULL; // the symbol table (linear search) +BOOL check_control = TRUE; // check for control cards +PROGTYPE progtype = PROGTYPE_RELOCATABLE; // program type +INTMODE intmode = INTMODE_UNSPECIFIED; // integer mode +REALMODE realmode = REALMODE_UNSPECIFIED; // real mode +int nintlevels = 0; // # of interrupt levels for ISS +int intlevel_primary = 0; // primary level for ISS and level for ILS +int intlevel_secondary = 0; // secondary level for ISS +int iss_number = 0; // ISS number +PSYMBOL entry[MAXENTRIES]; // entries for subroutines +int nentries = 0; +int ndefined_files = 0; + +struct lit { // accumulated literals waiting to be output + int value; // constant value + int tagno; // constant symbol tag number (e.g. _L001) + BOOL hex; // constant was expressed in hex + BOOL even; // constant was operand of a double-width instruction (e.g. AD) +} literal[MAXLITERALS]; + +int n_literals = 0, lit_tag = 0; +BOOL requires_even_address; // target of current instruction +BOOL dmes_saved; // odd character left over from dmes ending in ' +int dmes_savew; +char opfield[256]; // extracted operand field from source line +char dmsversion[12] = DMSVERSION; // version number for SBRK cards +const char whitespace[] = " \t"; // whitespace + +int ascii_to_ebcdic_table[128] = +{ +// + 0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f, +// + 0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f, +// spac ! " # $ % & ' ( ) * + , - . / + 0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61, +// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f, +// @ A B C D E F G H I J K L M N O + 0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6, +// P Q R S T U V W X Y Z [ \ ] & _ + 0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d, +// a b c d e f g h i j k l m n o + 0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96, +// p q r s t u v w x y z { | } ~ + 0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07, +}; + +int ascii_to_1403_table[128] = +{ /* 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f */ + 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, 0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x7f,0x7f,0x7f,0x7f,0x62,0x7f,0x15,0x0b, 0x57,0x2f,0x23,0x6d,0x16,0x61,0x6e,0x4c, + 0x49,0x40,0x01,0x02,0x43,0x04,0x45,0x46, 0x07,0x08,0x7f,0x7f,0x7f,0x4a,0x7f,0x7f, + 0x7f,0x64,0x25,0x26,0x67,0x68,0x29,0x2a, 0x6b,0x2c,0x58,0x19,0x1a,0x5b,0x1c,0x5d, + 0x5e,0x1f,0x20,0x0d,0x0e,0x4f,0x10,0x51, 0x52,0x13,0x54,0x7f,0x7f,0x7f,0x7f,0x7f, + 0x7f,0x64,0x25,0x26,0x67,0x68,0x29,0x2a, 0x6b,0x2c,0x58,0x19,0x1a,0x5b,0x1c,0x5d, + 0x5e,0x1f,0x20,0x0d,0x0e,0x4f,0x10,0x51, 0x52,0x13,0x54,0x7f,0x7f,0x7f,0x7f,0x7f +}; + +#include "../ibm1130_conout.h" /* conout_to_ascii_table */ +#include "../ibm1130_prtwheel.h" /* 1132 printer printwheel data */ + +// --------------------------------------------------------------------------------- +// PROTOTYPES +// --------------------------------------------------------------------------------- + +void init (int argc, char **argv); +void bail (char *msg); +void flag (char *arg); +void proc (char *fname); +void startpass (int n); +void errprintf (char *fmt, ...); +void asm_error (char *fmt, ...); +void asm_warning (char *fmt, ...); +char *astring (char *str); +PSYMBOL lookup_symbol (char *name, BOOL define); +void add_xref (PSYMBOL s, BOOL definition); +int get_symbol (char *name); +void set_symbol (char *name, int value, int known, RELOC relative); +char * gtok (char **pc, char *tok); +char *skipbl (char *c); +void sym_list (void); +void xref_list (void); +void listhdr (void); +int getexpr (char *pc, BOOL undefined_ok, EXPR *expr); +void passreport (void); +void listout (BOOL reset); +void output_literals (BOOL eof); +char *upcase (char *str); +void prep_line (char *line); +int ascii_to_hollerith (int ch); +char *detab (char *str); +void preload_symbols (void); +void save_symbols (void); +void bincard_init (void); +void bincard_writecard (char *sbrk_text); +void bincard_writedata (void); +void bincard_flush (void); +void bincard_sbrk (char *line); +void bincard_setorg (int neworg); +void bincard_writew (int word, RELOC relative); +void bincard_endcard (void); +void handle_sbrk (char *line); +void bincard_typecard (void); +void namecode (unsigned short *words, char *tok); +int signextend (int v); + +// --------------------------------------------------------------------------------- +// main routine +// --------------------------------------------------------------------------------- + +int main (int argc, char **argv) +{ + int i, sawfile = FALSE; + + init(argc, argv); // initialize, process flags + + startpass(1); // first pass, process files + + for (i = 1; i < argc; i++) + if (*argv[i] != '-') + proc(argv[i]), sawfile = TRUE; + + if (! sawfile) // should have seen at least one file + bail(usestr); + + if (passcount) { + passreport(); + return 0; + } + + startpass(2); // second pass, process files again + + for (i = 1; i < argc; i++) + if (*argv[i] != '-') + proc(argv[i]); + + if (outmode == OUTMODE_LOAD) { + if (pta >= 0) // write start address to the load file + fprintf(fout, "=%04x" ENDLINE, pta & 0xFFFF); + } + else + bincard_endcard(); + + if (flist) { + if (nerrors || nwarnings) { // summarize (or summarise) + if (nerrors == 0) + fprintf(flist, "There %s ", (nwarnings == 1) ? "was" : "were"); + else + fprintf(flist, "\nThere %s %d error%s %s", + (nerrors == 1) ? "was" : "were", nerrors, (nerrors == 1) ? "" : "s", nwarnings ? "and " : ""); + + if (nwarnings > 0) + fprintf(flist, "%d warning%s ", nwarnings, (nwarnings == 1) ? "" : "s"); + + fprintf(flist, "in this assembly\n"); + } + else + fprintf(flist, "\nThere were no errors in this assembly\n"); + } + + if (flist) { // finish the listing + if (pta >= 0) + fprintf(flist, "\nProgram transfer address = %04x\n", pta); + + if (do_xref) + xref_list(); + else if (do_syms) + sym_list(); + } + + if (savetable) + save_symbols(); + + return 0; // all done +} + +// --------------------------------------------------------------------------------- +// init - initialize assembler, process command line flags +// --------------------------------------------------------------------------------- + +void init (int argc, char **argv) +{ + int i; + + enable_1800 = strstr(argv[0], "1800") != NULL; // if "1800" appears in the executable name, enable 1800 extensions + + for (i = 1; i < argc; i++) // process command line switches + if (*argv[i] == '-') + flag(argv[i]+1); +} + +// --------------------------------------------------------------------------------- +// flag - process one command line switch +// --------------------------------------------------------------------------------- + +void flag (char *arg) +{ + int major, minor; + + while (*arg) { + switch (*arg++) { + case 'o': // output (load) file name + if (! *arg) + bail(usestr); + outfn = arg; + return; + + case 'p': + passcount = TRUE; + break; + + case 'v': // mumble while running + verbose = TRUE; + break; + + case 'x': // print cross reference table + do_xref = TRUE; + break; + + case 's': // print symbol table + do_syms = TRUE; + break; + + case 'l': // listing file name + listfn = (* arg) ? arg : NULL; + do_list = TRUE; + return; + + case 'W': + saveprompt = FALSE; + // fall through + case 'w': + savetable = TRUE; + break; + + case 'y': + preload = TRUE; + break; + + case 'b': + outmode = OUTMODE_BINARY; + break; + + case '8': + enable_1800 = TRUE; + break; + + case 'r': + if (sscanf(arg, "%d.%d", &major, &minor) != 2) + bail(usestr); + sprintf(dmsversion, "V%01.1dM%02.2d", major, minor); + return; + + default: + bail(usestr); + break; + } + } +} + +// --------------------------------------------------------------------------------- +// bail - print error message on stderr (only) and exit +// --------------------------------------------------------------------------------- + +void bail (char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(1); +} + +// --------------------------------------------------------------------------------- +// errprintf - print error message to stderr +// --------------------------------------------------------------------------------- + +void errprintf (char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); // get pointer to argument list + + vfprintf(stderr, fmt, args); // write errors to terminal (stderr) + + va_end(args); +} + +// --------------------------------------------------------------------------------- +// asm_error - report an error to listing file and to user's console +// --------------------------------------------------------------------------------- + +void asm_error (char *fmt, ...) +{ + va_list args; + + if (pass == 1) // only print on pass 2 + return; + + va_start(args, fmt); // get pointer to argument list + + fprintf(stderr, "E: %s (%d): ", curfn, lno); + vfprintf(stderr, fmt, args); // write errors to terminal (stderr) + putc('\n', stderr); + + if (flist != NULL && list_on) { + listout(FALSE); + line_error = TRUE; + + fprintf(flist, "**** Error: "); + vfprintf(flist, fmt, args); // write errors to listing file + putc('\n', flist); + } + + nerrors++; + va_end(args); +} + +// --------------------------------------------------------------------------------- +// asm_warning - same but warnings are not counted +// --------------------------------------------------------------------------------- + +void asm_warning (char *fmt, ...) +{ + va_list args; + + if (pass == 1) // only print on pass 2 + return; + + va_start(args, fmt); // get pointer to argument list + + fprintf(stderr, "W: %s (%d): ", curfn, lno); + vfprintf(stderr, fmt, args); // write errors to terminal (stderr) + putc('\n', stderr); + + if (flist != NULL && list_on) { + listout(FALSE); + line_error = TRUE; + + fprintf(flist, "**** Warning: "); + vfprintf(flist, fmt, args); // write errors to listing file + putc('\n', flist); + } + + nwarnings++; +} + +// --------------------------------------------------------------------------------- +// sym_list - print the symbol table +// --------------------------------------------------------------------------------- + +void sym_list (void) +{ + PSYMBOL s; + int n = 5; + + if (symbols == NULL || flist == NULL) + return; + + fprintf(flist, "\n=== SYMBOL TABLE ==============================================================\n"); + + for (s = symbols, n = 0; s != NULL; s = s->next) { + if (n >= 5) { + putc('\n', flist); + n = 0; + } + else if (n > 0) + fprintf(flist, " "); + + fprintf(flist, "%-6s ", s->name); + if (s->defined == S_DEFINED) + fprintf(flist, "%04x%s", s->value & 0xFFFF, s->relative ? "R" : " "); + else + fprintf(flist, "UUUU "); + + n++; + } + fprintf(flist, "\n"); +} + +// --------------------------------------------------------------------------------- +// passreport - report # of passes required for assembly on the 1130 +// --------------------------------------------------------------------------------- + +void passreport (void) +{ + PSYMBOL s; + + for (s = symbols; s != NULL; s = s->next) { + if (s->defined == S_UNDEFINED || s->defined == S_PROVISIONAL) { + printf("There are undefined symbols. Cannot determine pass requirement.\n"); + return; + } + } + + if (hasforward) + printf("There are forward references. Two passes are required.\n"); + else + printf("There are no forward references. Only one pass is required.\n"); +} + +// --------------------------------------------------------------------------------- +// xref_list - print the cross-reference table +// --------------------------------------------------------------------------------- + +void xref_list (void) +{ + int n = 0; + PXREF x; + PSYMBOL s; + + if (flist == NULL || symbols == NULL) + return; + + fprintf(flist, "\n=== CROSS REFERENCES ==========================================================\n"); + + if (symbols == NULL || flist == NULL) + return; + + fprintf(flist, "Name Val Defd Referenced\n"); + + for (s = symbols; s != NULL; s = s->next) { + fprintf(flist, "%-5s %04x%s", s->name, s->value & 0xFFFF, s->relative ? "R" : " "); + + for (x = s->xrefs; x != NULL; x = x->next) + if (x->definition) + break; + + if (x == NULL) + fprintf(flist, "----"); + else + fprintf(flist, " %4d", x->lno); + + for (n = 0, x = s->xrefs; x != NULL; x = x->next) { + if (x->definition) + continue; + + if (n >= 12) { + n = 0; + fprintf(flist, "\n "); + } + fprintf(flist, " %4d", x->lno); + n++; + } + putc('\n', flist); + } +} + +// --------------------------------------------------------------------------------- +// listhdr - print a banner header in the listing file. Since it's not paginated +// at this time, this is not used often. +// --------------------------------------------------------------------------------- + +void listhdr (void) +{ + time_t t; + + time(&t); + fprintf(flist, "%s -- %s -- %s\n", VERSION, dmsversion, ctime(&t)); +} + +// --------------------------------------------------------------------------------- +// astring - allocate a copy of a string +// --------------------------------------------------------------------------------- + +char *astring (char *str) +{ + static char *s = NULL; + + if (s != NULL) + if (strcmp(s, str) == 0) // if same as immediately previous allocation + return s; // return same pointer (why did I do this?) + + if ((s = malloc(strlen(str)+1)) == NULL) + bail("out of memory"); + + strcpy(s, str); + return s; +} + +// --------------------------------------------------------------------------------- +// lookup_symbol - get pointer to a symbol. +// If define is TRUE, creates and marks 'undefined' if not previously defined. +// --------------------------------------------------------------------------------- + +PSYMBOL lookup_symbol (char *name, BOOL define) +{ + PSYMBOL s, n, prv = NULL; + int c; + char *at; + + if (strlen(name) > 5) { // (sigh) + asm_error("Symbol '%s' is longer than 5 letters", name); + name[5] = '\0'; + } + +#ifdef FIX_ATS + while ((at = strchr(name, '@')) != NULL) + *at = '\''; +#endif + // search sorted list of symbols + for (s = symbols; s != NULL; prv = s, s = s->next) { + c = strcmpi(s->name, name); + if (c == 0) + return s; + if (c > 0) + break; + } + + if (! define) + return NULL; // not found + + if ((n = malloc(sizeof(SYMBOL))) == NULL) + bail("out of memory"); + + n->name = astring(name); // symbol was undefined -- add it now + n->value = 0; + n->defined = FALSE; + n->xrefs = NULL; + n->defined = FALSE; + + n->next = s; // link in alpha order + + if (prv == NULL) // we stopped before first item in list + symbols = n; + else + prv->next = n; // insert after item before place we stopped + + return n; +} + +// --------------------------------------------------------------------------------- +// add_xref - add a cross reference entry to a symbol +// --------------------------------------------------------------------------------- + +void add_xref (PSYMBOL s, BOOL definition) +{ + PXREF x, prv = NULL, n; + + if (pass == 1 || ! do_xref) // define only during 2nd pass and only if listing was requested + return; + + for (x = s->xrefs; x != NULL; prv = x, x = x->next) + if (strcmpi(x->fname, curfn) == 0 && x->lno == lno) + return; // ignore multiple refs on same line + + if ((n = malloc(sizeof(XREF))) == NULL) + bail("out of memory"); + + n->fname = astring(curfn); + n->lno = lno; + n->definition = definition; + + n->next = x; // link at end of existing list + + if (prv == NULL) + s->xrefs = n; + else + prv->next = n; +} + +// --------------------------------------------------------------------------------- +// get_symbol - get a symbol value, defining if necessary +// --------------------------------------------------------------------------------- + +int get_symbol (char *name) +{ + PSYMBOL s; + + s = lookup_symbol(name, TRUE); // lookup, define if necessary + + if (pass == 2) // should be defined by now + if (! s->defined) + asm_error("Symbol '%s' is undefined", name); + + add_xref(s, FALSE); // note the reference + + return s->value; +} + +// --------------------------------------------------------------------------------- +// set_symbol - set a symbol value. Known = TRUE means we really know the value; +// FALSE means we're calculating it with forward referenced values or something like +// that. +// --------------------------------------------------------------------------------- + +void set_symbol (char *name, int value, int known, RELOC relative) +{ + PSYMBOL s; + char *at; + + if (strlen(name) > 5) { + asm_error("Symbol '%s' is longer than 5 letters", name); + name[5] = '\0'; + } + +#ifdef FIX_ATS + while ((at = strchr(name, '@')) != NULL) + *at = '\''; +#endif + + s = lookup_symbol(name, TRUE); + + if (s->defined == S_DEFINED) // once defined, it should not change + if (s->value != value) + asm_error("Symbol '%s' %s", name, (s->pass == pass) ? "is multiply defined" : "changed between passes"); + + s->value = value; + s->relative = relative; + s->defined = known ? S_DEFINED : S_PROVISIONAL; + s->pass = pass; + + if (! known) + hasforward = TRUE; + + add_xref(s, TRUE); // record the place of definition +} + +// --------------------------------------------------------------------------------- +// skipbl - return pointer to first nonblank character in string s +// --------------------------------------------------------------------------------- + +char *skipbl (char *s) +{ + while (*s && *s <= ' ') + s++; + + return s; +} + +// --------------------------------------------------------------------------------- +// gtok - extracts a whitespace-delimited token from the string pointed to by *pc; +// stores the token into the buffer tok and returns pointer to same. Returns NULL +// when there are no tokens. Best to call repeatedly with a pointer to the source +// buffer, e.g. +// char *pbuf = buf; +// while (gtok(&pbuf, token) != NULL) ... +// --------------------------------------------------------------------------------- + +char * gtok (char **pc, char *tok) +{ + char *s = *pc, *otok = tok; + + while (*s && *s <= ' ') // skip blanks + s++; + + if (! *s) { // no tokens to be found + *tok = '\0'; + *pc = s; + return NULL; + } + + while (*s > ' ') // save nonblanks into 'tok' + *tok++ = *s++; + + *tok = '\0'; // terminate + *pc = s; // adjust caller's pointer + + return otok; // return pointer to token +} + +// listing format: +// +// ADDR CODE SOURCE +// 0000 0000 0000 0000 0000 | XXXXXXXXXXXXXXXXX + +// --------------------------------------------------------------------------------- +// trim - remove trailing whitespace from string s +// --------------------------------------------------------------------------------- + +char *trim (char *s) +{ + char *os = s, *nb; + + for (nb = s-1; *s; s++) + if (*s > ' ') + nb = s; + + nb[1] = '\0'; + return os; +} + +// --------------------------------------------------------------------------------- +// listout - emit current constructed output listing line held in "listline" and +// if "reset" is true, prepare listline for second and subsequent listing lines +// for a given input statement. +// --------------------------------------------------------------------------------- + +void listout (BOOL reset) +{ + if (flist && list_on && ! line_error) { + trim(listline); + fputs(listline, flist); + putc('\n', flist); + if (reset) + sprintf(listline, LEFT_MARGIN, org); + } +} + +// --------------------------------------------------------------------------------- +// storew - store a word in the output medium (hex or binary file). Most of the time +// writew is used. Advances the origin! +// --------------------------------------------------------------------------------- + +void storew (int word, RELOC relative) +{ + if (pass == 2) { // save in output (load) file. + switch (outmode) { + case OUTMODE_BINARY: + bincard_writew(word, relative); + break; + + case OUTMODE_LOAD: + fprintf(fout, " %04x%s" ENDLINE, word & 0xFFFF, + (relative == ABSOLUTE) ? "" : (relative == RELATIVE) ? "R" : + (relative == LIBF) ? "L" : (relative == CALL) ? "$" : "?"); + break; + + default: + bail("in storew, can't happen"); + } + } + + if (relative != LIBF) + org++; + + assembled = TRUE; // remember that we wrote something +} + +// --------------------------------------------------------------------------------- +// setw - store a word value in the current listing output line in position 'pos'. +// --------------------------------------------------------------------------------- + +void setw (int pos, int word, RELOC relative) +{ + char tok[10], *p; + int i; + + if (flist == NULL || ! list_on) + return; + + sprintf(tok, "%04x", word & 0xFFFF); + + for (i = 0, p = listline + 5*pos; i < 4; i++) + p[i] = tok[i]; + + if (relative == RELATIVE) + p[i] = 'R'; + else if (relative != ABSOLUTE) + p[i] = '*'; +} + +// --------------------------------------------------------------------------------- +// writew - emit an assembled word value. Words are also displayed in the listing file. +// if relative is true, a relocation entry should be recorded. +// --------------------------------------------------------------------------------- + +void writew (int word, RELOC relative) +{ // first, the listing stuff... + if (nwout == 0) { // on first output word, display address in column 0 + setw(0, org, FALSE); + } + else if (nwout >= 4) { // if 4 words have already been written, start new line + listout(TRUE); + nwout = 0; + } + + nwout++; + setw(nwout, word, relative); // display word in the listing line + + storew(word, relative); // write it to the output medium +} + +// --------------------------------------------------------------------------------- +// setorg - take note of new load address +// --------------------------------------------------------------------------------- + +void setorg (int neworg) +{ + if (pass == 2) { + setw(0, neworg, FALSE); // display in listing file in column 0 + + if (outmode == OUTMODE_LOAD) { // write new load address to the output file + fprintf(fout, "@%04x%s" ENDLINE, neworg & 0xFFFF, relocate ? "R" : ""); + } + else { + bincard_setorg(neworg); + } + } + + org = neworg; +} + +// --------------------------------------------------------------------------------- +// org_even - force load address to an even address +// --------------------------------------------------------------------------------- + +void org_even (void) +{ + if (org & 1) + setorg(org+1); +} + +// --------------------------------------------------------------------------------- +// tabtok - get the token in tab-delimited column number i, from source string c, +// saving in string 'tok'. If save is nonnull, we copy the entire remainder of +// the input string in buffer 'save' (in contrast to 'tok' which gets only the +// first whitespace delimited token). +// --------------------------------------------------------------------------------- + +void tabtok (char *c, char *tok, int i, char *save) +{ + *tok = '\0'; + + while (--i >= 0) { // skip to i'th tab-delimited field + if ((c = strchr(c, '\t')) == NULL) { + if (save) // was none + *save = '\0'; + return; + } + c++; + } + + while (*c == ' ') // skip leading blanks + c++; + + if (save != NULL) // save copy of entire remainder + strcpy(save, c); + + while (*c > ' ') { // take up to any whitespace + if (*c == '(') { // if we start with a paren, take all up to closing paren including spaces + while (*c && *c != ')') + *tok++ = *c++; + } + else if (*c == '.') { // period means literal character following + *tok++ = *c++; + if (*c) + *tok++ = *c++; + } + else + *tok++ = *c++; + } + + *tok = '\0'; +} + +// --------------------------------------------------------------------------------- +// coltok - extract a token from string c, saving to buffer tok, by examining +// columns ifrom through ito only. If save is nonnull, the entire remainder +// of the input from ifrom to the end is saved there. In this routine +// if condense is true, we save all nonwhite characters in the column range; +// not the usual thing. This helps us coalesce the format, tag, & index things +// nto one string for the simple minded parser. If condense is FALSE, we terminate +// on the first nonblank, except that if we start with a (, we take up to ) and +// then terminate on a space. +// +// ifrom and ito on entry are column numbers, not indices; we change that right away +// --------------------------------------------------------------------------------- + +void coltok (char *c, char *tok, int ifrom, int ito, BOOL condense, char *save) +{ + char *otok = tok; + int i; + + ifrom--; + ito--; + + for (i = 0; i < ifrom; i++) { + if (c[i] == '\0') { // line ended before this column + *tok = '\0'; + if (save) + *save = '\0'; + return; + } + } + + if (save) // save from ifrom on + strcpy(save, c+i); + + if (condense) { + for (; i <= ito; i++) { // save only nonwhite characters + if (c[i] > ' ') + *tok++ = c[i]; + } + } + else { + if (c[i] == ' ' && save != NULL)// if it starts with a space, it's empty + *save = '\0'; + + while (i <= ito) { // take up to any whitespace + if (c[i] <= ' ') + break; + else if (c[i] == '(') { // starts with paren? take to close paren + while (i <= ito && c[i]) { + if ((*tok++ = c[i++]) == ')') + break; + } + } + else if (c[i] == '.') { // period means literal character following + *tok++ = c[i++]; + if (i <= ito && c[i]) + *tok++ = c[i++]; + } + else + *tok++ = c[i++]; + } + } + + *tok = '\0'; + trim(otok); +} + +// --------------------------------------------------------------------------------- +// opcode table +// --------------------------------------------------------------------------------- + +// modifiers for the opcode definition table: + +#define L "L" // long +#define X "X" // absolute displacement +#define I "I" // indirect +#define IDX "0123" // indexed (some LDX commands in the DMS source say LDX L0, so accept 0 +#define E "E" // even address +#define NONE "" +#define ALL L X I IDX // hope non-Microsoft C accepts and concatenates strings like this +#define ANY "\xFF" +#define NUMS "0123456789" + +#define IS_DBL 0x0001 // double word operand implies even address +#define IS_ABS 0x0002 // always uses absolute addressing mode (implied X) +#define NO_IDX 0x0004 // even with 1 or 2 modifier, this is not really indexed (for STX/LDX) +#define NO_ARGS 0x0008 // statement takes no arguments +#define IS_1800 0x0010 // 1800-only directive or instruction, flagged if 1800 mode is not enabled +#define TRAP 0x1000 // debug this instruction + +struct tag_op { // OPCODE TABLE + char *mnem; + int opcode; + void (*handler)(struct tag_op *op, char *label, char *mods, char *arg); + char *mods_allowed; + char *mods_implied; + int flags; +}; + // special opcode handlers +void std_op (struct tag_op *op, char *label, char *mods, char *arg); +void b_op (struct tag_op *op, char *label, char *mods, char *arg); +void bsc_op (struct tag_op *op, char *label, char *mods, char *arg); +void bsi_op (struct tag_op *op, char *label, char *mods, char *arg); +void mdx_op (struct tag_op *op, char *label, char *mods, char *arg); +void shf_op (struct tag_op *op, char *label, char *mods, char *arg); + +void x_aif (struct tag_op *op, char *label, char *mods, char *arg); +void x_aifb (struct tag_op *op, char *label, char *mods, char *arg); +void x_ago (struct tag_op *op, char *label, char *mods, char *arg); +void x_agob (struct tag_op *op, char *label, char *mods, char *arg); +void x_anop (struct tag_op *op, char *label, char *mods, char *arg); +void x_abs (struct tag_op *op, char *label, char *mods, char *arg); +void x_call (struct tag_op *op, char *label, char *mods, char *arg); +void x_dsa (struct tag_op *op, char *label, char *mods, char *arg); +void x_file (struct tag_op *op, char *label, char *mods, char *arg); +void x_link (struct tag_op *op, char *label, char *mods, char *arg); +void x_libf (struct tag_op *op, char *label, char *mods, char *arg); +void x_org (struct tag_op *op, char *label, char *mods, char *arg); +void x_opt (struct tag_op *op, char *label, char *mods, char *arg); +void x_ces (struct tag_op *op, char *label, char *mods, char *arg); +void x_bes (struct tag_op *op, char *label, char *mods, char *arg); +void x_bss (struct tag_op *op, char *label, char *mods, char *arg); +void x_dc (struct tag_op *op, char *label, char *mods, char *arg); +void x_dec (struct tag_op *op, char *label, char *mods, char *arg); +void x_decs (struct tag_op *op, char *label, char *mods, char *arg); +void x_ebc (struct tag_op *op, char *label, char *mods, char *arg); +void x_end (struct tag_op *op, char *label, char *mods, char *arg); +void x_ent (struct tag_op *op, char *label, char *mods, char *arg); +void x_epr (struct tag_op *op, char *label, char *mods, char *arg); +void x_equ (struct tag_op *op, char *label, char *mods, char *arg); +void x_exit (struct tag_op *op, char *label, char *mods, char *arg); +void x_ils (struct tag_op *op, char *label, char *mods, char *arg); +void x_iss (struct tag_op *op, char *label, char *mods, char *arg); +void x_libr (struct tag_op *op, char *label, char *mods, char *arg); +void x_lorg (struct tag_op *op, char *label, char *mods, char *arg); +void x_dmes (struct tag_op *op, char *label, char *mods, char *arg); +void x_dn (struct tag_op *op, char *label, char *mods, char *arg); +void x_dump (struct tag_op *op, char *label, char *mods, char *arg); +void x_pdmp (struct tag_op *op, char *label, char *mods, char *arg); +void x_hdng (struct tag_op *op, char *label, char *mods, char *arg); +void x_list (struct tag_op *op, char *label, char *mods, char *arg); +void x_spac (struct tag_op *op, char *label, char *mods, char *arg); +void x_spr (struct tag_op *op, char *label, char *mods, char *arg); +void x_ejct (struct tag_op *op, char *label, char *mods, char *arg); +void x_trap (struct tag_op *op, char *label, char *mods, char *arg); +void x_xflc (struct tag_op *op, char *label, char *mods, char *arg); + +struct tag_op ops[] = { + ".OPT", 0, x_opt, NONE, NONE, 0, // non-IBM extensions + "TRAP", 0, x_trap, NONE, NONE, 0, // assembler breakpoint trap + ".CES", 0, x_ces, NONE, NONE, 0, // lets us specify simulated console entry switch values for startup + + "ABS", 0, x_abs, NONE, NONE, 0, + "BES", 0, x_bes, E, NONE, 0, // standard pseudo-ops + "BSS", 0, x_bss, E, NONE, 0, + "DC", 0, x_dc, NONE, NONE, 0, + "DEC", 0, x_dec, E, E, IS_DBL, + "DECS", 0, x_decs, E, E, IS_DBL, // this is an IBM 1800 directive + "DMES", 0, x_dmes, ANY, NONE, 0, + "DN", 0, x_dn, NONE, NONE, 0, + "DSA", 0, x_dsa, NONE, NONE, 0, + "DUMP", 0, x_dump, NONE, NONE, 0, + "EBC", 0, x_ebc, NONE, NONE, 0, + "EJCT", 0, x_ejct, NONE, NONE, 0, + "END", 0, x_end, NONE, NONE, 0, + "ENT", 0, x_ent, NONE, NONE, 0, + "EPR", 0, x_epr, NONE, NONE, 0, + "EQU", 0, x_equ, NONE, NONE, 0, + "EXIT", 0, x_exit, NONE, NONE, 0, // alias for call $exit since we don't have macros yet + "FILE", 0, x_file, NONE, NONE, 0, + "HDNG", 0, x_hdng, ANY, NONE, 0, + "ILS", 0, x_ils, NUMS, NONE, 0, + "ISS", 0, x_iss, NUMS, NONE, 0, + "LIBF", 0, x_libf, NONE, NONE, 0, + "LIBR", 0, x_libr, NONE, NONE, 0, + "LINK", 0, x_link, NONE, NONE, 0, + "LIST", 0, x_list, NONE, NONE, 0, + "LORG", 0, x_lorg, NONE, NONE, 0, + "ORG", 0, x_org, NONE, NONE, 0, + "PDMP", 0, x_pdmp, NONE, NONE, 0, + "SPAC", 0, x_spac, NONE, NONE, 0, + "SPR", 0, x_spr, NONE, NONE, 0, + "XFLC", 0, x_xflc, NONE, NONE, 0, + + "A", 0x8000, std_op, ALL, NONE, 0, // standard addressing ops + "AD", 0x8800, std_op, ALL, NONE, IS_DBL, + "AND", 0xE000, std_op, ALL, NONE, 0, + "BSI", 0x4000, bsi_op, ALL, NONE, 0, + "CALL", 0x4000, x_call, ALL, L, 0, // alias for BSI L, or external call + "CMP", 0xB000, std_op, ALL, NONE, IS_1800, // this is an IBM 1800-only instruction + "DCM", 0xB800, std_op, ALL, NONE, IS_1800, // this is an IBM 1800-only instruction + "D" , 0xA800, std_op, ALL, NONE, 0, + "EOR", 0xF000, std_op, ALL, NONE, 0, + "LD", 0xC000, std_op, ALL, NONE, 0, + "LDD", 0xC800, std_op, ALL, NONE, IS_DBL, + "LDS", 0x2000, std_op, NONE, NONE, IS_ABS, + "LDX", 0x6000, std_op, ALL, NONE, IS_ABS|NO_IDX, + "M", 0xA000, std_op, ALL, NONE, 0, + "MDX", 0x7000, mdx_op, ALL, NONE, 0, + "MDM", 0x7000, mdx_op, L, L, 0, // like MDX L + "NOP", 0x1000, std_op, NONE, NONE, NO_ARGS, + "OR", 0xE800, std_op, ALL, NONE, 0, + "S", 0x9000, std_op, ALL, NONE, 0, + "SD", 0x9800, std_op, ALL, NONE, IS_DBL, + "STD", 0xD800, std_op, ALL, NONE, IS_DBL, + "STO", 0xD000, std_op, ALL, NONE, 0, + "STS", 0x2800, std_op, ALL, NONE, 0, + "STX", 0x6800, std_op, ALL, NONE, NO_IDX, + "WAIT", 0x3000, std_op, NONE, NONE, IS_ABS, + "XCH", 0x18D0, std_op, NONE, NONE, 0, // same as RTE 16, 18C0 + 10 + "XIO", 0x0800, std_op, ALL, NONE, IS_DBL, + + "BSC", 0x4800, bsc_op, ALL, NONE, 0, // branch family + "BOSC", 0x4840, bsc_op, ALL, NONE, 0, // is BOSC always long form? No. + "SKP", 0x4800, bsc_op, NONE, NONE, 0, // alias for BSC one word version + + "B", 0x4800, b_op, ALL, NONE, 0, // alias for MDX or BSC L + "BC", 0x4802, std_op, ALL, L, 0, // alias for BSC L + "BN", 0x4828, std_op, ALL, L, 0, // alias for BSC L + "BNN", 0x4810, std_op, ALL, L, 0, // alias for BSC L + "BNP", 0x4808, std_op, ALL, L, 0, // alias for BSC L + "BNZ", 0x4820, std_op, ALL, L, 0, // alias for BSC L + "BO", 0x4801, std_op, ALL, L, 0, // alias for BSC L + "BOD", 0x4840, std_op, ALL, L, 0, // alias for BSC L + "BP", 0x4830, std_op, ALL, L, 0, // alias for BSC L + "BZ", 0x4818, std_op, ALL, L, 0, // alias for BSC L + + "RTE", 0x18C0, shf_op, IDX X, X, 0, // shift family + "SLA", 0x1000, shf_op, IDX X, X, 0, + "SLC", 0x10C0, shf_op, IDX X, X, 0, + "SLCA", 0x1040, shf_op, IDX X, X, 0, + "SLT", 0x1080, shf_op, IDX X, X, 0, + "SRA", 0x1800, shf_op, IDX X, X, 0, + "SRT", 0x1880, shf_op, IDX X, X, 0, + + "AIF", 0, x_aif, NONE, NONE, 0, // assemble if + "AIFB", 0, x_aifb, NONE, NONE, 0, // assemble if + "AGO", 0, x_ago, NONE, NONE, 0, // assemble goto + "AGOB", 0, x_agob, NONE, NONE, 0, // assemble goto + "ANOP", 0, x_anop, NONE, NONE, 0, // assemble target + + NULL // end of table +}; + +// --------------------------------------------------------------------------------- +// addextn - apply file extension 'extn' to filename 'fname' and put result in 'outbuf' +// if outbuf is NULL, we allocate a buffer +// --------------------------------------------------------------------------------- + +char *addextn (char *fname, char *extn, char *outbuf) +{ + char *buf, line[500], *c; + + buf = (outbuf == NULL) ? line : outbuf; + + strcpy(buf, fname); // create listfn from first source filename (e.g. xxx.lst); + if ((c = strrchr(buf, '\\')) == NULL) + if ((c = strrchr(buf, '/')) == NULL) + if ((c = strrchr(buf, ':')) == NULL) + c = buf; + + if ((c = strrchr(c, '.')) == NULL) + strcat(buf, extn); + else + strcpy(c, extn); + + return (outbuf == NULL) ? astring(line) : outbuf; +} + +// --------------------------------------------------------------------------------- +// controlcard - examine an assembler control card (* in column 1) +// --------------------------------------------------------------------------------- + +BOOL controlcard (char *line) +{ + if (strnicmp(line, "*LIST", 5) == 0) { // turn on listing file even if not specified on command line + do_list = list_on = TRUE; + return TRUE; + } + + if (strnicmp(line, "*XREF", 5) == 0) { + do_xref = TRUE; + return TRUE; + } + + if (strnicmp(line, "*PRINT SYMBOL TABLE", 19) == 0) { + do_syms = TRUE; + return TRUE; + } + + if (strnicmp(line, "*SAVE SYMBOL TABLE", 18) == 0) { + savetable = TRUE; + return TRUE; + } + + if (strnicmp(line, "*SYSTEM SYMBOL TABLE", 20) == 0) { + preload = TRUE; + preload_symbols(); + return TRUE; + } + + return FALSE; +} + +// --------------------------------------------------------------------------------- +// stuff - insert characters into a line +// --------------------------------------------------------------------------------- + +void stuff (char *buf, char *tok, int maxchars) +{ + while (*tok) { + *buf++ = *tok++; + + if (maxchars) + if (--maxchars <= 0) + break; + } +} + +// --------------------------------------------------------------------------------- +// format_line - construct a source code input line from components +// --------------------------------------------------------------------------------- + +void format_line (char *buf, char *label, char *op, char *mods, char *args, char *remarks) +{ + int i; + + if (tabformat) { + sprintf(buf, "%s\t%s\t%s\t%s\t%s", label, op, mods, args, remarks); + } + else { + for (i = 0; i < 72; i++) + buf[i] = ' '; + buf[i] = '\0'; + + stuff(buf+20, label, 5); + stuff(buf+26, op, 4); + stuff(buf+31, mods, 2); + stuff(buf+34, args, 72-34); + } +} + +// --------------------------------------------------------------------------------- +// lookup_op - find an opcode +// --------------------------------------------------------------------------------- + +struct tag_op * lookup_op (char *mnem) +{ + struct tag_op *op; + int i; + + for (op = ops; op->mnem != NULL; op++) { + if ((i = strcmp(op->mnem, mnem)) == 0) + return op; + + if (i > 0) + break; + } + return NULL; +} + +// --------------------------------------------------------------------------------- +// bincard - routines to write IBM 1130 Card object format +// --------------------------------------------------------------------------------- + +unsigned short bincard[54]; // the 54 data words that can fit on a binary format card +char binflag[45]; // the relocation flags of the 45 buffered object words (0, 1, 2, 3) +int bincard_n = 0; // number of object words stored in bincard (0-45) +int bincard_seq = 0; // card output sequence number +int bincard_org = 0; // origin of current card-full +int bincard_maxaddr = 0; +BOOL bincard_first = TRUE; // TRUE when we're to write the program type card + +// bincard_init - prepare a new object data output card + +void bincard_init (void) +{ + memset(bincard, 0, sizeof(bincard)); // clear card data + memset(binflag, 0, sizeof(binflag)); // clear relocation data + bincard_n = 0; // no data + bincard[0] = bincard_org; // store load address + bincard_maxaddr = MAX(bincard_maxaddr, bincard_org-1); // save highest address written-to (this may be a BSS) +} + +// binard_writecard - emit a card. sbrk_text = NULL for normal data cards, points to comment text for sbrk card +// note: sbrk_text if not NULL MUST be a writeable buffer of at LEAST 71 characters + +void bincard_writecard (char *sbrk_text) +{ + unsigned short binout[80]; + char ident[12]; + int i, j; + + if (sbrk_text != NULL) { // sbrk card has 4 binary words followed by comment text + for (j = 66; j < 71; j++) // be sure input columns 67..71 are nonblank (have version number) + if (sbrk_text[j] <= ' ') + break; + + if (j < 71) // sbrk card didn't have the info, stuff in current release + for (j = 0; j < 5; j++) + sbrk_text[66+j] = dmsversion[j]; + + binout[0] = 0; + binout[1] = 0; + binout[2] = 0; + binout[3] = 0x1000; + + sbrk_text += 5; // start at the real column 6 (after *SBRK + for (j = 5; j < 72; j++) + binout[j] = (*sbrk_text) ? ascii_to_hollerith(*sbrk_text++) : 0; + + } + else { // binary card format packs 54 words into 72 columns + for (i = j = 0; i < 54; i += 3, j += 4) { + binout[j ] = ( bincard[i] & 0xFFF0); + binout[j+1] = ((bincard[i] << 12) & 0xF000) | ((bincard[i+1] >> 4) & 0x0FF0); + binout[j+2] = ((bincard[i+1] << 8) & 0xFF00) | ((bincard[i+2] >> 8) & 0x00F0); + binout[j+3] = ((bincard[i+2] << 4) & 0xFFF0); + } + } + + sprintf(ident, "%08ld", ++bincard_seq); // append sequence text + memmove(ident, progname, MIN(strlen(progname), 4)); + + for (i = 0; i < 8; i++) + binout[j++] = ascii_to_hollerith(ident[i]); + + fxwrite(binout, sizeof(binout[0]), 80, fout); // write card image +} + +// binard_writedata - emit an object data card + +void bincard_writedata (void) +{ + unsigned short rflag = 0; + int i, j, nflag = 0; + + bincard[1] = 0; // checksum + bincard[2] = 0x0A00 | bincard_n; // data card type + word count + + for (i = 0, j = 3; i < bincard_n; i++) { // construct relocation indicator bitmap + if (nflag == 8) { + bincard[j++] = rflag; + rflag = 0; + nflag = 0; + } + rflag = (rflag << 2) | (binflag[i] & 3); + nflag++; + } + + if (nflag > 0) + bincard[j] = rflag << (16 - 2*nflag); + + bincard_writecard(FALSE); // emit the card +} + +// bincard_flush - flush any pending binary data + +void bincard_flush (void) +{ + if (bincard_n > 0) + bincard_writedata(); + + bincard_init(); +} + +// bincard_sbrk - emit an SBRK card + +void bincard_sbrk (char *line) +{ + if (bincard_first) + bincard_typecard(); + else + bincard_flush(); + + bincard_writecard(line); +} + +// bincard_setorg - set the origin + +void bincard_setorg (int neworg) +{ + bincard_org = neworg; // set origin for next card + bincard_flush(); // flush any current data & store origin +} + +// bincard_endcard - write end of program card + +void bincard_endcard (void) +{ + bincard_flush(); + + bincard[0] = (bincard_maxaddr + 2) & ~1; // effective length: add 1 to max origin, then 1 more to round up + bincard[1] = 0; + bincard[2] = 0x0F00; + bincard[3] = pta & 0xFFFF; + + bincard_writecard(NULL); +} + +// bincard_typecard - write the program type + +void bincard_typecard (void) +{ + int i; + + if (! bincard_first) + return; + + bincard_first = FALSE; + + memset(bincard, 0, sizeof(bincard)); + + bincard[2] = (unsigned short) ((progtype << 8) | intmode | realmode); + +// all indices not listed are documented as 'reserved' + + switch (progtype) { + case PROGTYPE_ABSOLUTE: + case PROGTYPE_RELOCATABLE: +// bincard[ 4] = 0; // length of common (fortran only) + bincard[ 5] = 0x0003; +// bincard[ 6] = 0; // length of work area (fortran only) + bincard[ 8] = ndefined_files; + namecode(&bincard[9], progname); + bincard[11] = (pta < 0) ? 0 : pta; + break; + + case PROGTYPE_LIBF: + case PROGTYPE_CALL: + bincard[ 5] = 3*nentries; + for (i = 0; i < nentries; i++) { + namecode(&bincard[9+3*i], entry[i]->name); + bincard[11+3*i] = entry[i]->value; + } + break; + + case PROGTYPE_ISSLIBF: + case PROGTYPE_ISSCALL: + bincard[ 5] = 6+nintlevels; + namecode(&bincard[9], entry[0]->name); + bincard[11] = entry[0]->value; + bincard[12] = iss_number + ISTV; // magic number ISTV is 0x33 in DMS R2V12 + bincard[13] = iss_number; + bincard[14] = nintlevels; + bincard[15] = intlevel_primary; + bincard[16] = intlevel_secondary; + bincard[29] = 1; + break; + + case PROGTYPE_ILS: + bincard[ 2] = (unsigned short) (progtype << 8); + bincard[ 5] = 4; + bincard[12] = intlevel_primary; + break; + + default: + bail("in bincard_typecard, can't happen"); + } + + bincard[1] = 0; // checksum + + bincard_writecard(NULL); + + bincard_init(); +} + +// bincard_writew - write a word to the current output card. + +void bincard_writew (int word, RELOC relative) +{ + if (pass != 2) + return; + + if (bincard_first) + bincard_typecard(); + else if (bincard_n >= 45) // flush full card buffer + bincard_flush(); + + binflag[bincard_n] = relative & 3; // store relocation bits and data word + bincard[9+bincard_n++] = word; + + if (relative != LIBF) { + bincard_maxaddr = MAX(bincard_maxaddr, bincard_org); + bincard_org++; + } +} + +// writetwo - notification that we are about to write two words which must stay together + +void writetwo (void) +{ + if (pass == 2 && outmode == OUTMODE_BINARY && bincard_n >= 44) + bincard_flush(); +} + +// handle_sbrk - handle an SBRK directive. +// This was not part of the 1130 assembler; they assembled DMS on a 360 + +void handle_sbrk (char *line) +{ + char rline[90]; + + if (pass != 2) + return; + + strncpy(rline, line, 81); // get a copy and pad it if necessary to 80 characters + rline[80] = '\0'; + while (strlen(rline) < 80) + strcat(rline, " "); + + switch (outmode) { + case OUTMODE_LOAD: + fprintf(fout, "#SBRK%s\n", trim(rline+5)); + + case OUTMODE_BINARY: + bincard_sbrk(rline); + break; + + default: + bail("in handle_sbrk, can't happen"); + } +} + +// --------------------------------------------------------------------------------- +// namecode - turn a string into a two-word packed name +// --------------------------------------------------------------------------------- + +void namecode (unsigned short *words, char *tok) +{ + long val = 0; + int i, ch; + + for (i = 0; i < 5; i++) { // pick up bits + if (*tok) + ch = *tok++; + else + ch = ' '; + + val = (val << 6) | (ascii_to_ebcdic_table[ch] & 0x3F); + } + + words[0] = (unsigned short) (val >> 16); + words[1] = (unsigned short) val; +} + +// --------------------------------------------------------------------------------- +// parse_line - parse one input line. +// --------------------------------------------------------------------------------- + +void parse_line (char *line) +{ + char label[100], mnem[100], arg[200], mods[20], *c; + struct tag_op *op; + + if (line[0] == '/' && line[1] == '/') // job control card? probably best to ignore it + return; + + if (line[0] == '*') { // control card comment or comment in tab-format file + if (check_control) // pay attention to control cards only at top of file + if (! controlcard(line)) + check_control = FALSE; // first non-control card shuts off sensitivity to them + + if (strnicmp(line+1, "SBRK", 4) == 0) + handle_sbrk(line); + + return; + } + + check_control = FALSE; // non-control card, consider them no more + + label[0] = '\0'; // prepare to extract fields + mods[0] = '\0'; + mnem[0] = '\0'; + arg[0] = '\0'; + + if (tabformat || strchr(line, '\t') != NULL) { // if input line has tabs, parse loosely + tabformat = TRUE; // this is a tab-formatted file + + for (c = line; *c && *c <= ' '; c++) // find first nonblank + ; + + if (*c == '*' || ! *c) // ignore as a comment + return; + + tabtok(line, label, 0, NULL); + tabtok(line, mnem, 1, NULL); + tabtok(line, mods, 2, NULL); + tabtok(line, arg, 3, opfield); + } + else { // if no tabs, use strict card-column format + if (line[20] == '*') // comment + return; + + line[72] = '\0'; // clip off sequence + + coltok(line, label, 21, 25, TRUE, NULL); + coltok(line, mnem, 27, 30, TRUE, NULL); + coltok(line, mods, 32, 33, TRUE, NULL); + coltok(line, arg, 35, 72, FALSE, opfield); + } + +// I don't know where I got this idea, but it's wrong... +// if (strchr(mods, '1') || strchr(mods, '2') || strchr(mods, '3')) { // index + X means ignore X +// if ((c = strchr(mods, 'X')) != NULL) +// strcpy(c, c+1); // remove the X +// } + + if (*label) // display org in any line with a label + setw(0, org, FALSE); + + if (! *mnem) { // label w/o mnemonic, just define the symbol + if (*label) + set_symbol(label, org, TRUE, relocate); + return; + } + + if ((op = lookup_op(mnem)) == NULL) { // look up mnemonic + if (*label) + set_symbol(label, org, TRUE, relocate);// at least define the label + + asm_error("Unknown opcode '%s'", mnem); + return; + } + + if (op->flags & TRAP) // assembler debugging breakpoint + x_trap(op, label, mods, arg); + + if (*op->mods_allowed != '\xFF') { // validate modifiers against list of allowed characters + for (c = mods; *c; ) { + if (strchr(op->mods_allowed, *c) == NULL) { + asm_warning("Modifier '%c' not permitted", *c); + strcpy(c, c+1); // remove it and keep parsing + } + else + c++; + } + } + + strcat(mods, op->mods_implied); // tack on implied modifiers + + if (strchr(mods, 'I')) // indirect implies long + strcat(mods, "L"); + + requires_even_address = op->flags & IS_DBL; + + org_advanced = strchr(mods, 'L') ? 2 : 1; // by default, * means address + 1 or 2. Sometimes it doesn't + (op->handler)(op, label, mods, arg); + + if ((op->flags & IS_1800) && ! enable_1800) + asm_warning("%s is IBM 1800-specific; use the -8 command line option", op->mnem); +} + +// --------------------------------------------------------------------------------- +// get one input line from current file or macro +// --------------------------------------------------------------------------------- + +BOOL get_line (char *buf, int nbuf, BOOL onelevel) +{ + char *retval; + + if (ended) // we hit the END command + return FALSE; + + // if macro active, return line from macro buffer, otherwise read from file + // do not pop end-of-macro if onelevel is TRUE + + if ((retval = fgets(buf, nbuf, fin)) == NULL) + return FALSE; + + lno++; // count the line + return TRUE; +} + +// --------------------------------------------------------------------------------- +// proc - process one pass of one source file +// --------------------------------------------------------------------------------- + +void proc (char *fname) +{ + char line[256], *c; + int i; + + if (strchr(fname, '.') == NULL) // if input file has no extension, + addextn(fname, ".asm", curfn); // set appropriate file extension + else + strcpy(curfn, fname); // otherwise use extension specified + +// let's leave filename case alone even if it doesn't matter +//#if (defined(_WIN32) || defined(VMS)) +// upcase(curfn); // only force uppercase of name on Windows and VMS +//#endif + + if (progname[0] == '\0') { // pick up primary filename + if ((c = strrchr(curfn, '\\')) == NULL) + if ((c = strrchr(curfn, '/')) == NULL) + if ((c = strrchr(curfn, ':')) == NULL) + c = curfn; + + strncpy(progname, c, sizeof(progname)); // take name after path + progname[sizeof(progname)-1] = '\0'; + if ((c = strchr(progname, '.')) != NULL)// remove extension + *c = '\0'; + } + + lno = 0; // reset global input line number + ended = FALSE; // have not seen END statement + + if (listfn == NULL) // if list file name is undefined, + listfn = addextn(fname, ".lst", NULL); // create from first filename + + if (verbose) + fprintf(stderr, "--- Starting file %s pass %d\n", curfn, pass); + + if ((fin = fopen(curfn, "r")) == NULL) { + perror(curfn); // oops + exit(1); + } + + if (flist) { // put banner in listing file + strcpy(listline,"=== FILE ======================================================================"); + for (i = 9, c = curfn; *c;) + listline[i++] = *c++; + listline[i] = ' '; + fputs(listline, flist); + putc('\n', flist); + list_on = TRUE; + } + // read all lines till EOF or END statement + while (get_line(line, sizeof(line), FALSE)) { + prep_line(line); // preform standard line prep + parse_line(line); // parse + listout(FALSE); // complete the listing + } + + fclose(fin); + + if (n_literals > 0) { // force out any pending literal constants at end of file + output_literals(TRUE); + listout(FALSE); + } +} + +// --------------------------------------------------------------------------------- +// prep_line - prepare input line for parsing +// --------------------------------------------------------------------------------- + +void prep_line (char *line) +{ + char *c; + + upcase(line); // uppercase it + nwout = 0; // number of words output so far + line_error = FALSE; // no errors on this line so far + + for (c = line; *c; c++) { // truncate at newline + if (*c == '\r' || *c == '\n') { + *c = '\0'; + break; + } + } + + if (flist && list_on) { // construct beginning of listing line + if (tabformat) + sprintf(listline, LINEFORMAT, lno, detab(line)); + else { + if (strlen(line) > 20) // get the part where the commands start + c = line+20; + else + c = ""; + + sprintf(listline, LINEFORMAT, lno, c); + stuff(listline, line, 20); // stuff the left margin in to the left side + } + } +} + +// --------------------------------------------------------------------------------- +// opcmp - operand name comparison routine for qsort +// --------------------------------------------------------------------------------- + +int opcmp (const void *a, const void *b) +{ + return strcmp(((struct tag_op *) a)->mnem, ((struct tag_op *) b)->mnem); +} + +// --------------------------------------------------------------------------------- +// preload_symbols - load a saved symbol table +// --------------------------------------------------------------------------------- + +void preload_symbols (void) +{ + FILE *fd; + char str[200], sym[20]; + int v; + static BOOL preloaded_already = FALSE; + + if (pass > 1 || preloaded_already) + return; + + preloaded_already = TRUE; + + if ((fd = fopen(SYSTEM_TABLE, "r")) == NULL) // read the system symbol tabl + perror(SYSTEM_TABLE); + else { + while (fgets(str, sizeof(str), fd) != NULL) { + if (sscanf(str, "%s %x", sym, &v) == 2) + set_symbol(sym, v, TRUE, FALSE); + } + fclose(fd); + } +} + +// --------------------------------------------------------------------------------- +// save_symbols - save a symbol table +// --------------------------------------------------------------------------------- + +void save_symbols (void) +{ + FILE *fd; + char str[20]; + PSYMBOL s; + + if (relocate) { + fprintf(stderr, "Can't save symbol table unless ABS assembly\n"); + return; + } + + if ((fd = fopen(SYSTEM_TABLE, "r")) != NULL) { + fclose(fd); + if (saveprompt) { + printf("Overwrite system symbol table %s? ", SYSTEM_TABLE); + fgets(str, sizeof(str), stdin); + if (str[0] != 'y' && str[0] != 'Y') + return; + } + unlink(SYSTEM_TABLE); + } + + if ((fd = fopen(SYSTEM_TABLE, "w")) == NULL) { + perror(SYSTEM_TABLE); + return; + } + + for (s = symbols; s != NULL; s = s->next) + fprintf(fd, "%-5s %04x\n", s->name, s->value); + + fclose(fd); +} + +// --------------------------------------------------------------------------------- +// startpass - initialize data structures, prepare to start a pass +// --------------------------------------------------------------------------------- + +void startpass (int n) +{ + int nops; + struct tag_op *p; + + pass = n; // reset globals: pass number + nerrors = 0; // error count + org = 0; // load address (origin) + lno = 0; // input line number + relocate = TRUE; // relocatable assembly mode + assembled = FALSE; // true if any output has been generated + list_on = do_list; // listing enable + dmes_saved = FALSE; // partial character strings output + + n_literals = 0; // literal values pending output + lit_tag = 0; + + if (pass == 1) { // first pass only + for (nops = 0, p = ops; p->mnem != NULL; p++, nops++) // count opcodes + ; + + qsort(ops, nops, sizeof(*p), opcmp); // sort the opcode table + + if (preload) + preload_symbols(); + } + else { // second pass only + if (outfn == NULL) + outfn = addextn(curfn, (outmode == OUTMODE_LOAD) ? ".out" : ".bin" , NULL); + + if ((fout = fopen(outfn, OUTWRITEMODE)) == NULL) { // open output file + perror(outfn); + exit(1); + } + + if (do_list) { // open listing file + if ((flist = fopen(listfn, "w")) == NULL) { + perror(listfn); + exit(1); + } + listhdr(); // print banner + } + } +} + +// --------------------------------------------------------------------------------- +// x_dc - DC define constant directive +// --------------------------------------------------------------------------------- + +void x_dc (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; +// char *tok; + + org_advanced = 1; // assume * means this address+1 +// doesn't make sense, but I think I found DMS listings to support it + + if (strchr(mods, 'E') != NULL) // force even address + org_even(); + + setw(0, org, FALSE); // display org in listing line + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + +// just one!? + getexpr(arg, FALSE, &expr); + writew(expr.value, expr.relative); // store value + + // pick up values, comma delimited +// for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) { +// getexpr(tok, FALSE, &expr); +// writew(expr.value, expr.relative); // store value +// } +} + +// --------------------------------------------------------------------------------- +// x_dec - DEC define double word constant directive. +// --------------------------------------------------------------------------------- + +// wd[0]: 8 unused bits | characteristic (= exponent+128) +// wd[1]: sign + 15 msb of mantissa in 2's complement +// wd[2]: 16 lsb of mantissa + +// NOTE: these are wrong with Fixed point numbers + +void convert_double_to_extended (double d, unsigned short *wd) +{ + int neg, exp; + unsigned long mantissa; + unsigned char *byte = (unsigned char *) &d; + + if (d == 0.) { + wd[0] = wd[1] = wd[2] = 0; + return; + } + // 7 6 5 4 0 + // d = ansi real*8 SXXX XXXX XXXX MMMM MMMM MMMM MMMM MMMM ... MMMM MMMM + + neg = byte[7] & 0x80; + exp = ((byte[7] & 0x7F) << 4) | ((byte[6] & 0xF0) >> 4); // extract exponent + exp -= 1023; // remove bias + + exp++; // shift to account for implied 1 we added + + // get 32 bits worth of mantissa. add the implied point + mantissa = 0x80000000L | ((byte[6] & 0x0F) << 27) | (byte[5] << 19) | (byte[4] << 11) | (byte[3] << 3) | ((byte[2] & 0xE0) >> 5); + + if (mantissa & (0x80000000L >> 31)) // keep 31 bits, round if necessary + mantissa += (0x80000000L >> 31); + + mantissa >>= (32-31); // get into low 31 bits + + // now turn into IBM 1130 extended precision + + exp += 128; + + if (neg) + mantissa = (unsigned long) (- (long) mantissa); // two's complement + + wd[0] = (unsigned short) (exp & 0xFF); + wd[1] = (unsigned short) ((neg ? 0x8000 : 0) | ((mantissa >> (31-15)) & 0x7FFF)); + wd[2] = (unsigned short) (mantissa & 0xFFFF); +} + +// --------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------- + +void convert_double_to_standard (double d, unsigned short *wd) +{ + int neg, exp; + unsigned long mantissa; + unsigned char *byte = (unsigned char *) &d; + + if (d == 0.) { + wd[0] = wd[1] = 0; + return; + } + // 7 6 5 4 0 + // d = ansi real*8 SXXX XXXX XXXX MMMM MMMM MMMM MMMM MMMM ... MMMM MMMM + + neg = byte[7] & 0x80; + exp = ((byte[7] & 0x7F) << 4) | ((byte[6] & 0xF0) >> 4); // extract exponent + exp -= 1023; // remove bias + + exp++; // shift to account for implied 1 we added + + // get 32 bits worth of mantissa. add the implied point + mantissa = 0x80000000L | ((byte[6] & 0x0F) << 27) | (byte[5] << 19) | (byte[4] << 11) | (byte[3] << 3) | ((byte[2] & 0xE0) >> 5); + +// if (mantissa & (0x80000000L >> 23)) // keep 23 bits, round if necessary +// mantissa += (0x80000000L >> 23); + +// DEBUG +// printf("%8.4lf: %08lx %d\n", d, mantissa, exp); + + mantissa >>= (32-23); // get into low 23 bits + + // now turn into IBM 1130 standard precision + + exp += 128; + + if (neg) + mantissa = (unsigned long) (- (long) mantissa); // two's complement + + wd[0] = (unsigned short) ((neg ? 0x8000 : 0) | ((mantissa >> (23-15)) & 0x7FFF)); + wd[1] = (unsigned short) ((mantissa & 0x00FF) << 8) | (exp & 0xFF); + +// DEBUG +// printf(" D %04x%04x\n", wd[0], wd[1]); +} + +// --------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------- + +void convert_double_to_fixed (double d, unsigned short *wd, int bexp) +{ + int neg, exp, rshift; + unsigned long mantissa; + unsigned char *byte = (unsigned char *) &d; + + if (d == 0.) { + wd[0] = wd[1] = 0; + return; + } + + // note: we assume that this computer uses ANSI floating point + + // 7 6 5 4 0 + // d = ansi real*8 SXXX XXXX XXXX MMMM MMMM MMMM MMMM MMMM ... MMMM MMMM + + neg = byte[7] & 0x80; + exp = ((byte[7] & 0x7F) << 4) | ((byte[6] & 0xF0) >> 4); // extract exponent + exp -= 1023; // remove bias + + exp++; // shift to account for implied 1 we added + + // get 32 bits worth of mantissa. add the implied point + mantissa = 0x80000000L | ((byte[6] & 0x0F) << 27) | (byte[5] << 19) | (byte[4] << 11) | (byte[3] << 3) | ((byte[2] & 0xE0) >> 5); + + mantissa >>= 1; // shift it out of the sign bit + +// DEBUG +// printf("%8.4lf: %08lx %d\n", d, mantissa, exp); + + rshift = bexp - exp; + + if (rshift > 0) { + mantissa >>= rshift; + } + else if (rshift < 0) { + mantissa >>= (-rshift); + asm_warning("Fixed point overflow"); + } + + if (neg) + mantissa = (unsigned long) (- (long) mantissa); // two's complement + +// DEBUG +// printf(" B %08lx\n", mantissa); + + wd[0] = (unsigned short) ((mantissa >> 16) & 0xFFFF); // return all of the bits; no exponent here + wd[1] = (unsigned short) (mantissa & 0xFFFF); +} + +// --------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------- + +void getDconstant (char *tok, unsigned short *wd) +{ + unsigned long l; + char *b, *fmt; + double d; + int bexp, fixed; + + wd[0] = 0; + wd[1] = 0; + + if (strchr(tok, '.') == NULL && strchr(tok, 'B') == NULL && strchr(tok, 'E') == NULL) { + fmt = "%ld"; + if (*tok == '/') { // I don't see that this is legal but can't hurt to allow it + fmt = "%lx"; + tok++; + } + if (sscanf(tok, fmt, &l) != 1) { // no decimal means it's an integer? + asm_error("Syntax error in constant"); + } + else { + wd[0] = (unsigned short) ((l >> 16) & 0xFFFF); // high word + wd[1] = (unsigned short) (l & 0xFFFF); // low word + } + return; + } + + fixed = 0; + if ((b = strchr(tok, 'B')) != NULL) { + fixed = 1; + bexp = atoi(b+1); + *b = '\0'; // truncate at the b + } + if (sscanf(tok, "%lg", &d) != 1) { + asm_error("Syntax error in constant"); + return; + } + + if (fixed) + convert_double_to_fixed(d, wd, bexp); + else + convert_double_to_standard(d, wd); +} + +// --------------------------------------------------------------------------------- +// If the input value is an integer with no decimal point and no B or E, +// DEC generates a double INTEGER value. +// IBM documentation ranges from ambiguous to wrong on this point, but +// examination of the DMS microfiche supports this. +// --------------------------------------------------------------------------------- + +void x_dec (struct tag_op *op, char *label, char *mods, char *arg) +{ + unsigned short wd[2]; + + org_advanced = 2; // assume * means address after this location, since it's +1 for dc? + + org_even(); // even address is implied + setw(0, org, FALSE); // display the origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + +// just one!? + getDconstant(arg, wd); + writew(wd[0], FALSE); // write hiword, then loword + writew(wd[1], FALSE); + + // pick up values, comma delimited +// for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) { +// getDconstant(tok, wd); +// +// writew(wd[0], FALSE); // write hiword, then loword +// writew(wd[1], FALSE); +} + +// --------------------------------------------------------------------------------- +// DECS directive. Writes just the high word of a DEC value +// --------------------------------------------------------------------------------- + +void x_decs (struct tag_op *op, char *label, char *mods, char *arg) +{ + unsigned short wd[2]; + + org_advanced = 1; // assume * means address after this location + + setw(0, org, FALSE); // display the origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + getDconstant(arg, wd); + writew(wd[0], FALSE); // write hiword ONLY +} + +// --------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------- + +void x_xflc (struct tag_op *op, char *label, char *mods, char *arg) +{ + char *tok, *b; + double d; + int bexp, fixed; + unsigned short wd[3]; + + org_advanced = 2; // who knows? + + setw(0, org, FALSE); // display the origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + // pick up values, comma delimited + for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) { + bexp = 0; + if ((b = strchr(tok, 'B')) != NULL) { + bexp = atoi(b+1); + fixed = TRUE; + *b = '\0'; // truncate at the b + asm_warning("Fixed point extended floating constant?"); + } + + if (sscanf(tok, "%lg", &d) != 1) { + asm_error("Syntax error in constant"); + d = 0.; + } + + convert_double_to_extended(d, wd); + + writew(wd[0], ABSOLUTE); + writew(wd[1], ABSOLUTE); + writew(wd[2], ABSOLUTE); + } +} + +// --------------------------------------------------------------------------------- +// x_equ - EQU directive +// --------------------------------------------------------------------------------- + +void x_equ (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + + org_advanced = FALSE; // * means this address, not incremented + + getexpr(arg, FALSE, &expr); + + setw(0, expr.value, expr.relative); // show this as address + + if (*label) // EQU is all about defining labels, better have one + set_symbol(label, expr.value, TRUE, expr.relative); +// else // IBM assembler doesn't complain about this +// asm_error("EQU without label?"); +} + +// --------------------------------------------------------------------------------- +// x_lorg - LORG directive -- output queued literal values +// --------------------------------------------------------------------------------- + +void x_lorg (struct tag_op *op, char *label, char *mods, char *arg) +{ + org_advanced = FALSE; // * means this address (not used, though) + output_literals(FALSE); // generate .DC's for queued literal values +} + +// --------------------------------------------------------------------------------- +// x_abs - ABS directive +// --------------------------------------------------------------------------------- + +void x_abs (struct tag_op *op, char *label, char *mods, char *arg) +{ + if (assembled) + asm_error("ABS must be first statement"); + + relocate = ABSOLUTE; + + switch (progtype) { + case PROGTYPE_ABSOLUTE: + case PROGTYPE_RELOCATABLE: + progtype = PROGTYPE_ABSOLUTE; // change program type, still assumed to be mainline + break; + + case PROGTYPE_LIBF: + case PROGTYPE_CALL: + case PROGTYPE_ISSLIBF: + case PROGTYPE_ISSCALL: + case PROGTYPE_ILS: + asm_error("ABS not allowed with LIBF, ENT, ILS or ISS"); + break; + + default: + bail("in x_libr, can't happen"); + } +} + +// --------------------------------------------------------------------------------- +// x_call - ORG pseudo-op +// --------------------------------------------------------------------------------- + +void x_call (struct tag_op *op, char *label, char *mods, char *arg) +{ + unsigned short words[2]; + static struct tag_op *bsi = NULL; + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (! *arg) { + asm_error("CALL missing argument"); + return; + } + + if (pass == 1) { // it will take two words in any case + org += 2; + return; + } + + setw(0, org, FALSE); // display origin + + if (lookup_symbol(arg, FALSE) != NULL) { // it's a defined symbol? + if (bsi == NULL) + if ((bsi = lookup_op("BSI")) == NULL) + bail("Can't find BSI op"); + + (bsi->handler)(bsi, "", "L", arg); + } + else { + namecode(words, arg); // emit namecode for loader + + writetwo(); + writew(words[0], CALL); + writew(words[1], ABSOLUTE); + } +} + +// --------------------------------------------------------------------------------- +// x_org - ORG directive +// --------------------------------------------------------------------------------- + +void x_org (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + + org_advanced = FALSE; // * means this address + + if (*label) // label is defined BEFORE the new origin is set!!! + set_symbol(label, org, TRUE, relocate); + + if (getexpr(arg, FALSE, &expr) != S_DEFINED) + return; + + setorg(expr.value); // set origin to this value +} + +// --------------------------------------------------------------------------------- +// x_end - END directive +// --------------------------------------------------------------------------------- + +void x_end (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + + org_advanced = FALSE; // * means this address + + if (*arg) { // they're specifing the program start address + if (getexpr(arg, FALSE, &expr) == S_DEFINED) + pta = expr.value; + } + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + setw(0, org, FALSE); // display origin + + ended = TRUE; // assembly is done, stop reading file +} + +// --------------------------------------------------------------------------------- +// x_ent - ENT op +// --------------------------------------------------------------------------------- + +void x_ent (struct tag_op *op, char *label, char *mods, char *arg) +{ + PSYMBOL s; + + org_advanced = FALSE; // * means this address + + if (pass < 2) + return; + +// if (*label) // define label +// set_symbol(label, org, TRUE, relocate); +// +// setw(0, org, FALSE); // display origin + + if (! *arg) + asm_error("No entry label specified"); + + else if ((s = lookup_symbol(arg, FALSE)) == NULL) + asm_error("Entry symbol %s not defined", arg); + + else if (nentries >= MAXENTRIES) + asm_error("Too many entries, limit is %d", MAXENTRIES); + + else + entry[nentries++] = s; // save symbol pointer + + switch (progtype) { + case PROGTYPE_ABSOLUTE: + asm_error("ENT not allowed with ABS"); + break; + case PROGTYPE_RELOCATABLE: + progtype = PROGTYPE_CALL; + break; + case PROGTYPE_LIBF: + case PROGTYPE_CALL: + case PROGTYPE_ISSLIBF: + case PROGTYPE_ISSCALL: + break; + case PROGTYPE_ILS: + asm_error("Can't mix ENT and ILS, can you?"); + break; + default: + bail("in x_libr, can't happen"); + } +} + +// --------------------------------------------------------------------------------- +// declare a libf-type subprogram +// --------------------------------------------------------------------------------- + +void x_libr (struct tag_op *op, char *label, char *mods, char *arg) +{ + switch (progtype) { + case PROGTYPE_ABSOLUTE: + asm_error("LIBR not allowed with ABS"); + break; + case PROGTYPE_RELOCATABLE: + case PROGTYPE_LIBF: + case PROGTYPE_CALL: + progtype = PROGTYPE_LIBF; + break; + case PROGTYPE_ISSLIBF: + case PROGTYPE_ISSCALL: + progtype = PROGTYPE_ISSLIBF; + break; + case PROGTYPE_ILS: + asm_error("Can't use LIBR in an ILS"); + break; + default: + bail("in x_libr, can't happen"); + } +} + +// --------------------------------------------------------------------------------- +// x_ils - ILS directive +// --------------------------------------------------------------------------------- + +void x_ils (struct tag_op *op, char *label, char *mods, char *arg) +{ + switch (progtype) { + case PROGTYPE_ABSOLUTE: + asm_error("ILS not allowed with ABS"); + break; + case PROGTYPE_RELOCATABLE: + case PROGTYPE_ILS: + progtype = PROGTYPE_ILS; + break; + case PROGTYPE_LIBF: + case PROGTYPE_CALL: + asm_error("Invalid placement of ILS"); + break; + case PROGTYPE_ISSLIBF: + case PROGTYPE_ISSCALL: + break; + default: + bail("in x_libr, can't happen"); + } + + intlevel_primary = atoi(mods); +} + +// --------------------------------------------------------------------------------- +// x_iss - ISS directive +// --------------------------------------------------------------------------------- + +void x_iss (struct tag_op *op, char *label, char *mods, char *arg) +{ + char *tok; + + switch (progtype) { + case PROGTYPE_ABSOLUTE: + asm_error("ISS not allowed with ABS"); + break; + case PROGTYPE_RELOCATABLE: + case PROGTYPE_CALL: + case PROGTYPE_ISSCALL: + progtype = PROGTYPE_ISSCALL; + break; + case PROGTYPE_LIBF: + case PROGTYPE_ISSLIBF: + progtype = PROGTYPE_ISSLIBF; + break; + case PROGTYPE_ILS: + asm_error("Can't mix ISS and ILS"); + default: + bail("in x_libr, can't happen"); + } + + iss_number = atoi(mods); // get ISS number + + opfield[16] = '\0'; // be sure not to look too far into this + + nintlevels = 0; // # of interrupt levels for ISS + intlevel_primary = 0; // primary level for ISS and level for ILS + intlevel_secondary = 0; // secondary level for ISS + + if ((tok = strtok(opfield, " ")) == NULL) + asm_error("ISS missing entry label"); + else + x_ent(NULL, label, "", arg); // process as an ENT + + if ((tok = strtok(NULL, " ")) != NULL) { // get associated levels + nintlevels++; + intlevel_primary = atoi(tok); + } + + if ((tok = strtok(NULL, " ")) != NULL) { + nintlevels++; + intlevel_secondary = atoi(tok); + } +} + +void x_spr (struct tag_op *op, char *label, char *mods, char *arg) +{ + realmode = REALMODE_STANDARD; +} + +void x_epr (struct tag_op *op, char *label, char *mods, char *arg) +{ + realmode = REALMODE_EXTENDED; +} + +void x_dsa (struct tag_op *op, char *label, char *mods, char *arg) +{ + unsigned short words[2]; + + setw(0, org, FALSE); // display origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (! *arg) { + asm_error("DSA missing filename"); + } + else { + namecode(words, arg); + writetwo(); + writew(words[0], CALL); // special relocation bits here 3 and 1 + writew(words[1], RELATIVE); + } +} + +void x_link (struct tag_op *op, char *label, char *mods, char *arg) +{ + unsigned short words[2]; + char nline[128]; + + setw(0, org, FALSE); // display origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (! *arg) { + asm_error("LINK missing program name"); + } + else { + format_line(nline, label, "CALL", "", "$LINK", ""); + parse_line(nline); + + namecode(words, arg); + writew(words[0], ABSOLUTE); // special relocation bits here 3 and 1 + writew(words[1], ABSOLUTE); + } +} + +void x_libf (struct tag_op *op, char *label, char *mods, char *arg) +{ + unsigned short words[2]; + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (! *arg) { + asm_error("LIBF missing argument"); + return; + } + + if (pass == 1) { // it will take one words in any case + org++; + return; + } + + setw(0, org, FALSE); // display origin + + namecode(words, arg); // emit namecode for loader + + writetwo(); + writew(words[0], LIBF); // this one does NOT advance org! + writew(words[1], ABSOLUTE); +} + +void x_file (struct tag_op *op, char *label, char *mods, char *arg) +{ + int i, n, r; + EXPR vals[5]; + char *tok; + + for (i = 0; i < 5; i++) { + if ((tok = strtok(arg, ",")) == NULL) { + asm_error("FILE has insufficient arguments"); + return; + } + arg = NULL; // for next strtok call + + if (i == 3) { + if (strcmpi(tok, "U") != 0) + asm_error("Argument 4 must be the letter U"); + } + else if (getexpr(tok, FALSE, &vals[i]) == S_DEFINED) { + if (i <= 3 && vals[i].relative) + asm_error("Argument %d must be absolute", i+1); + else if (pass == 2 && vals[i].value == 0) + asm_error("Argument %d must be nonzero", i+1); + } + } + + writew(vals[0].value, ABSOLUTE); + writew(vals[1].value, ABSOLUTE); + writew(vals[2].value, ABSOLUTE); + writew(vals[4].value, vals[i].relative); + writew(0, ABSOLUTE); + n = MAX(1, vals[2].value); + r = 320/n; + writew(r, ABSOLUTE); + r = MAX(1, r); + writew((16*vals[1].value)/r, ABSOLUTE); + + if (pass == 2) + ndefined_files++; +} + +// --------------------------------------------------------------------------------- +// x_trap - place to set a breakpoint +// --------------------------------------------------------------------------------- + +void x_trap (struct tag_op *op, char *label, char *mods, char *arg) +{ + // debugging breakpoint +} + +// --------------------------------------------------------------------------------- +// x_ces - .CES directive (nonstandard). Specify a value for the console entry +// switches. When this program is loaded into the simulator, the switches will +// be set accordingly. Handy for bootstraps and other programs that read +// the switches. +// --------------------------------------------------------------------------------- + +void x_ces (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + + if (outmode != OUTMODE_LOAD) // this works only in our loader format + return; + + if (getexpr(arg, FALSE, &expr) != S_DEFINED) + return; + + if (pass == 2) + fprintf(fout, "S%04x" ENDLINE, expr.value & 0xFFFF); +} + +// --------------------------------------------------------------------------------- +// x_bss - BSS directive - reserve space in core +// --------------------------------------------------------------------------------- + +void x_bss (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + + org_advanced = FALSE; // * means this address + + if (! *arg) { + expr.value = 0; + expr.relative = ABSOLUTE; + } + else if (getexpr(arg, FALSE, &expr) != S_DEFINED) + return; + + if (strchr(mods, 'E') != NULL) // force even address + org_even(); + + if (expr.relative) + asm_error("BSS size must be an absolute value"); + + setw(0, org, FALSE); // display origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + expr.value &= 0xFFFF; // truncate to 16 bits + + if (expr.value & 0x8000) + asm_warning("Negative BSS size"); + else if (expr.value > 0) { + if (outmode == OUTMODE_LOAD) { + org += expr.value; // advance the origin by appropriate number of words + if (pass == 2) // emit new load address in output file + fprintf(fout, "@%04x%s" ENDLINE, org & 0xFFFF, relocate ? "R" : ""); + } + else { + org += expr.value; // advance the origin by appropriate number of words + if (pass == 2) + bincard_setorg(org); + } + } +} + +// --------------------------------------------------------------------------------- +// x_bes - Block Ended by Symbol directive. Like BSS but label gets address AFTER the space, instead of first address +// --------------------------------------------------------------------------------- + +void x_bes (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + + org_advanced = FALSE; // * means this address + + if (! *arg) { // arg field = space + expr.value = 0; + expr.relative = ABSOLUTE; + } + else if (getexpr(arg, FALSE, &expr) != S_DEFINED) + return; + + if (strchr(mods, 'E') != NULL && (org & 1) != 0) + org_even(); // force even address + + if (expr.relative) + asm_error("BES size must be an absolute value"); + + if (expr.value < 0) + asm_warning("Negative BES size"); + + else if (expr.value > 0) { + setw(0, org+expr.value, FALSE); // display NEW origin + + if (outmode == OUTMODE_LOAD) { + org += expr.value; // advance the origin + if (pass == 2) // emit new load address in output file + fprintf(fout, "@%04x%s" ENDLINE, org & 0xFFFF, relocate ? "R" : ""); + } + else { + org += expr.value; // advance the origin + bincard_setorg(org); + } + } + + if (*label) // NOW define the label + set_symbol(label, org, TRUE, relocate); +} + +// --------------------------------------------------------------------------------- +// x_dmes - DMES define message directive. Various encodings, none pretty. +// --------------------------------------------------------------------------------- + +int dmes_wd; +int dmes_nc; +enum {CODESET_CONSOLE, CODESET_1403, CODESET_1132, CODESET_EBCDIC} dmes_cs; +void stuff_dmes (int ch, int rpt); + +void x_dmes (struct tag_op *op, char *label, char *mods, char *arg) +{ + int rpt; + char *c = opfield; + BOOL cont = FALSE; + + if (dmes_saved) { // previous DMES had an odd character saved + dmes_wd = dmes_savew; + dmes_nc = 1; // stick it into the outbut buffer + } + else + dmes_nc = dmes_wd = 0; // clear output buffer + + trim(opfield); // remove trailing blanks from rest of input line (use whole thing) + setw(0, org, FALSE); // display origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (strchr(mods, '1') != NULL) // determine the encoding scheme + dmes_cs = CODESET_1403; + else if (strchr(mods, '2') != NULL) + dmes_cs = CODESET_1132; + else if (strchr(mods, '0') != NULL || ! *mods) + dmes_cs = CODESET_CONSOLE; + else { + asm_error("Invalid printer code in tag field"); + dmes_cs = CODESET_EBCDIC; + } + + while (*c) { // pick up characters + if (*c == '\'') { // quote (') is the escape character + c++; + + rpt = 0; // get repeat count + while (BETWEEN(*c, '0', '9')) { + rpt = rpt*10 + *c++ - '0'; + } + if (rpt <= 0) // no count = insert one copy + rpt = 1; + + switch (*c) { // handle escape codes + case '\'': + stuff_dmes(*c, 1); + break; + + case 'E': + *c = '\0'; // end + break; + + case 'X': + case 'S': + stuff_dmes(' ', rpt); + break; + + case 'F': + stuff_dmes(*++c, rpt); // repeat character + break; + + case ' ': + case '\0': + cont = TRUE; + *c = '\0'; // end + break; + + case 'T': + if (dmes_cs != CODESET_CONSOLE) { +badcode: asm_error("Invalid ' escape for selected printer"); + break; + } + stuff_dmes(0x41, -rpt); // tab + break; + + case 'D': + if (dmes_cs != CODESET_CONSOLE) goto badcode; + stuff_dmes(0x11, -rpt); // backspace + break; + + case 'B': + if (dmes_cs != CODESET_CONSOLE) goto badcode; + stuff_dmes(0x05, -rpt); // black + break; + + case 'A': + if (dmes_cs != CODESET_CONSOLE) goto badcode; + stuff_dmes(0x09, -rpt); // red + break; + + case 'R': + if (dmes_cs != CODESET_CONSOLE) goto badcode; + stuff_dmes(0x81, -rpt); // return + break; + + case 'L': + if (dmes_cs != CODESET_CONSOLE) goto badcode; + stuff_dmes(0x03, -rpt); // line feed + break; + + default: + asm_error("Invalid ' escape in DMES"); + *c = '\0'; + break; + } + } + else // just copy literal character + stuff_dmes(*c, 1); + + if (*c) + c++; + } + + dmes_saved = FALSE; + + if (dmes_nc) { // odd number of characters + if (cont) { + dmes_saved = TRUE; + dmes_savew = dmes_wd; // save for next time + } + else + stuff_dmes(' ', 1); // pad with a space to force out even # of characters + } +} + +// --------------------------------------------------------------------------------- +// stuff_dmes - insert 'rpt' copies of character 'ch' into output words +// --------------------------------------------------------------------------------- + +void stuff_dmes (int ch, int rpt) +{ + int nch, i; // nch is translated output value + + if (rpt < 0) { // negative repeat means no translation needed + rpt = -rpt; + nch = ch; + } + else { + switch (dmes_cs) { + case CODESET_CONSOLE: + nch = 0x21; + for (i = 0; i < 256; i++) { + if (conout_to_ascii[i] == ch) { + nch = i; + break; + } + } + break; + + case CODESET_EBCDIC: + nch = ascii_to_ebcdic_table[ch & 0x7F]; + if (nch == 0) + nch = 0x7F; + break; + + case CODESET_1403: + nch = ascii_to_1403_table[ch & 0x7F]; + if (nch == 0) + nch = 0x7F; + break; + + case CODESET_1132: + nch = 0x40; + for (i = 0; i < WHEELCHARS_1132; i++) { + if (codewheel1132[i].ascii == ch) { + nch = codewheel1132[i].ebcdic; + break; + } + } + break; + + default: + bail("bad cs in x_dmes, can't happen"); + break; + } + } + + while (--rpt >= 0) { // pack them into words, output when we have two + if (dmes_nc == 0) { + dmes_wd = (nch & 0xFF) << 8; + dmes_nc = 1; + } + else { + dmes_wd |= (nch & 0xFF); + writew(dmes_wd, FALSE); + dmes_nc = 0; + } + } +} + +// --------------------------------------------------------------------------------- +// x_ebc - handle EBCDIC string definition (delimited with periods) +// --------------------------------------------------------------------------------- + +void x_ebc (struct tag_op *op, char *label, char *mods, char *arg) +{ + char *p; + +// setw(0, org, FALSE); + if (*label) + set_symbol(label, org, TRUE, relocate); + + p = trim(opfield); // remove trailing blanks from rest of input line (use whole thing) + + if (*p != '.') { + asm_error("EBC data must start with ."); + return; + } + p++; // skip leading period + + dmes_nc = dmes_wd = 0; // clear output buffer (we're borrowing the DMES packer) + dmes_cs = CODESET_EBCDIC; + + while (*p && *p != '.') // store packed ebcdic + stuff_dmes(*p++, 1); + + if (dmes_nc) // odd number of characters + stuff_dmes(' ', 1); // pad with a space to force out even # of characters + + if (*p != '.') + asm_error("EBC missing closing ."); +} + +// --------------------------------------------------------------------------------- +// x_dn - define name DN directive. Pack 5 characters into two words. This by the +// way is the reason the language Forth is not Fourth. +// --------------------------------------------------------------------------------- + +void x_dn (struct tag_op *op, char *label, char *mods, char *arg) +{ + unsigned short words[2]; + + setw(0, org, FALSE); // display origin + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + namecode(words, arg); + + writew(words[0], ABSOLUTE); + writew(words[1], ABSOLUTE); +} + +// --------------------------------------------------------------------------------- +// x_dump - DUMP directive - pretend we saw "call $dump, call $exit" +// --------------------------------------------------------------------------------- + +void x_dump (struct tag_op *op, char *label, char *mods, char *arg) +{ + x_pdmp(op, label, mods, arg); + x_exit(NULL, "", "", ""); // compile "call $exit" +} + +// --------------------------------------------------------------------------------- +// x_pdmp - PDMP directive - like DUMP but without the call $exit +// --------------------------------------------------------------------------------- + +void x_pdmp (struct tag_op *op, char *label, char *mods, char *arg) +{ + char nline[200], *tok; + EXPR addr[3]; + int i; + + for (i = 0, tok = strtok(arg, ","); i < 3 && tok != NULL; i++, tok = strtok(NULL, ",")) { + if (getexpr(tok, FALSE, addr+i) != S_DEFINED) { + addr[i].value = (i == 1) ? 0x3FFF : 0; + addr[i].relative = ABSOLUTE; + } + } + + org_advanced = FALSE; // * means this address+1 + + format_line(nline, label, "BSI", "L", DOLLARDUMP, ""); + parse_line(nline); // compile "call $dump" + + writew(addr[2].value, ABSOLUTE); // append arguments (0, start, end address) + writew(addr[0].value, addr[0].relative); + writew(addr[1].value, addr[1].relative); +} + +// --------------------------------------------------------------------------------- +// x_hdng - HDNG directive +// --------------------------------------------------------------------------------- + +void x_hdng (struct tag_op *op, char *label, char *mods, char *arg) +{ + char *c; + + // label is not entered into the symbol table + + if (flist == NULL || ! list_on) { + line_error = TRUE; // inhibit listing: don't print the HDNG statement + return; + } + + line_error = TRUE; // don't print the statement + + c = skipbl(opfield); + trim(c); + fprintf(flist, "\f%s\n\n", c); // print page header +} + +// --------------------------------------------------------------------------------- +// x_list - LIST directive. enable or disable listing +// --------------------------------------------------------------------------------- + +void x_list (struct tag_op *op, char *label, char *mods, char *arg) +{ + BOOL on; + + // label is not entered into the symbol table + + line_error = TRUE; // don't print the LIST statement + + if (flist == NULL || ! list_on) { + return; + } + + if (strcmpi(arg, "ON") == 0) + on = TRUE; + else if (strcmpi(arg, "OFF") == 0) + on = FALSE; + else + on = do_list; + + list_on = on; +} + +// --------------------------------------------------------------------------------- +// x_spac - SPAC directive. Put blank lines in listing +// --------------------------------------------------------------------------------- + +void x_spac (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + + // label is not entered into the symbol table + + if (flist == NULL || ! list_on) { + line_error = TRUE; // don't print the SPAC statement + return; + } + + if (getexpr(arg, FALSE, &expr) != S_DEFINED) + return; + + line_error = TRUE; // don't print the statement + + while (--expr.value >= 0) + putc('\n', flist); +} + +// --------------------------------------------------------------------------------- +// x_ejct - EJCT directive - put formfeed in listing +// --------------------------------------------------------------------------------- + +void x_ejct (struct tag_op *op, char *label, char *mods, char *arg) +{ + // label is not entered into the symbol table + + if (flist == NULL || ! list_on) { + line_error = TRUE; // don't print the EJCT statement + return; + } + + line_error = TRUE; // don't print the statement + + putc('\f', flist); +} + +// --------------------------------------------------------------------------------- +// basic_opcode - construct a standard opcode value from op table entry and modifier chars +// --------------------------------------------------------------------------------- + +int basic_opcode (struct tag_op *op, char *mods) +{ + int opcode = op->opcode; // basic code value + + if (strchr(mods, '1') != 0) // indexing + opcode |= 0x0100; + else if (strchr(mods, '2') != 0) + opcode |= 0x0200; + else if (strchr(mods, '3') != 0) + opcode |= 0x0300; + + if (strchr(mods, 'L')) { // two-word format + opcode |= OP_LONG; + if (strchr(mods, 'I') != 0) // and indirect to boot + opcode |= OP_INDIRECT; + } + + return opcode; +} + +// --------------------------------------------------------------------------------- +// std_op - assemble a vanilla opcode +// --------------------------------------------------------------------------------- + +void std_op (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + int opcode = basic_opcode(op, mods); + BOOL val_ok = FALSE; + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (*arg && ! (op->flags & NO_ARGS)) { // get value argument + if (getexpr(arg, FALSE, &expr) == S_DEFINED) + val_ok = TRUE; + } + else { + expr.value = 0; + expr.relative = FALSE; + } + + if (opcode & OP_LONG) { // two-word format, just write code and value + writew(opcode, FALSE); + writew(expr.value, expr.relative); + } + else { // one-word format + if (strchr(mods, 'I') != 0) + asm_error("Indirect mode not permitted on one-word instructions"); + + if (val_ok && ! (strchr(mods, 'X') || (op->flags & IS_ABS) || ((opcode & OP_INDEXED) && ! (op->flags & NO_IDX)))) + expr.value -= (org+1); // compute displacement + + if (expr.value < -128 || expr.value > 127) {// check range + asm_error("Offset of %d is too large", expr.value); + expr.value = 0; + } + + writew(opcode | (expr.value & 0x00FF), FALSE);// that's the code + } +} + +// --------------------------------------------------------------------------------- +// mdx_op - assemble a MDX family instruction +// --------------------------------------------------------------------------------- + +void mdx_op (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR dest, incr = {0, FALSE}; + int opcode = basic_opcode(op, mods); + char *tok; + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if ((tok = strtok(arg, ",")) == NULL) { // argument format is dest[,increment] +// asm_error("Destination not specified"); // seems not to be an error, IBM omits it sometimes + dest.value = 0; + dest.relative = ABSOLUTE; + } + else + getexpr(tok, FALSE, &dest); // parse the address + + tok = strtok(NULL, ","); // look for second argument + + if (opcode & OP_LONG) { // two word format + if (opcode & OP_INDEXED) { // format: MDX 2 dest + if (tok != NULL) + asm_error("This format takes only one argument"); + } + else { // format: MDX dest,increment + if (opcode & OP_INDIRECT) + asm_error("Indirect can't be used without indexing"); + + if (tok == NULL) { +// asm_error("This format takes two arguments"); + incr.value = 0; + incr.relative = ABSOLUTE; + } + else + getexpr(tok, FALSE, &incr); + + if (incr.value < -128 || incr.value > 127) // displacement style (fixed in ver 1.08) + asm_error("Invalid increment value (8 bits signed)"); + + opcode |= (incr.value & 0xFF); + } + + writew(opcode, ABSOLUTE); + writew(dest.value, dest.relative); + } + else { // one word format MDX val + if (tok != NULL) + asm_error("This format takes only one argument"); + + if (! (strchr(mods, 'X') || (opcode & OP_INDEXED))) + dest.value -= (org+1); // compute displacement + + if (dest.value < -128 || dest.value > 127) + asm_error("Offset/Increment of %d is too large", dest.value); + + writew(opcode | (dest.value & 0xFF), FALSE); + } +} + +// --------------------------------------------------------------------------------- +// bsi_op - BSI long instruction is like a BSC L, short is standard +// --------------------------------------------------------------------------------- + +void bsi_op (struct tag_op *op, char *label, char *mods, char *arg) +{ + if (strchr(mods, 'L') || strchr(mods, 'I')) + bsc_op(op, label, mods, arg); + else + std_op(op, label, mods, arg); +} + +// --------------------------------------------------------------------------------- +// b_op - branch; use short or long version +// -------------------------------------------------------------------------------- + +void b_op (struct tag_op *op, char *label, char *mods, char *arg) +{ + static struct tag_op *mdx = NULL; + + if (strchr(mods, 'L') || strchr(mods, 'I')) { + bsi_op(op, label, mods, arg); + return; + } + + if (mdx == NULL) + if ((mdx = lookup_op("MDX")) == NULL) + bail("Can't find MDX op"); + + (mdx->handler)(mdx, label, mods, arg); +} + +// --------------------------------------------------------------------------------- +// bsc_op - compute a BSC family instruction +// --------------------------------------------------------------------------------- + +void bsc_op (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR dest; + int opcode = basic_opcode(op, mods); + char *tok, *tests; + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (opcode & OP_LONG) { // two word format + if ((tok = strtok(arg, ",")) == NULL) { // format is BSC dest[,tests] + asm_error("Destination not specified"); + dest.value = 0; + dest.relative = ABSOLUTE; + } + else + getexpr(tok, FALSE, &dest); + + tests = strtok(NULL, ","); // get test characters + } + else + tests = arg; // short format is BSC tests + + if (tests != NULL) { // stick in the testing bits + for (; *tests; tests++) { + switch (*tests) { + // bit 0x40 is the BOSC bit + case 'Z': opcode |= 0x20; break; + case '-': opcode |= 0x10; break; + case '+': + case '&': opcode |= 0x08; break; + case 'E': opcode |= 0x04; break; + case 'C': opcode |= 0x02; break; + case 'O': opcode |= 0x01; break; + default: + asm_error("Invalid test flag: '%c'", *tests); + } + } + } + + writew(opcode, ABSOLUTE); // emit code + if (opcode & OP_LONG) + writew(dest.value, dest.relative); +} + +// --------------------------------------------------------------------------------- +// shf_op - assemble a shift instruction +// --------------------------------------------------------------------------------- + +void shf_op (struct tag_op *op, char *label, char *mods, char *arg) +{ + EXPR expr; + int opcode = basic_opcode(op, mods); + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + + if (opcode & OP_INDEXED) { // shift value comes from index register + expr.value = 0; + expr.relative = ABSOLUTE; + } + else + getexpr(arg, FALSE, &expr); + + if (expr.relative) { + asm_error("Shift value is a relative address"); + expr.relative = ABSOLUTE; + } + + if (expr.value < 0 || expr.value > 32) { // check range + asm_error("Shift count of %d is invalid", expr.value); + expr.value = 0; + } + + writew(opcode | (expr.value & 0x3F), FALSE); // put shift count into displacement field +} + +// --------------------------------------------------------------------------------- +// x_mdm - MDM instruction +// --------------------------------------------------------------------------------- + +void x_mdm (struct tag_op *op, char *label, char *mods, char *arg) +{ + int opcode = basic_opcode(op, mods); + + if (*label) // define label + set_symbol(label, org, TRUE, relocate); + // oh dear: bug here + asm_error("'%s' is not yet supported", op->mnem); +} + +// --------------------------------------------------------------------------------- +// x_exit - EXIT directive. Assembler manual says it treats like CALL $EXIT, but +// object code reveals the truth: jump to $EXIT, which is a small value, so we can use LDX. +// --------------------------------------------------------------------------------- + +void x_exit (struct tag_op *op, char *label, char *mods, char *arg) +{ + char nline[120]; + + format_line(nline, label, "LDX", "X", DOLLAREXIT, ""); + parse_line(nline); +} + +// --------------------------------------------------------------------------------- +// x_opt - .OPT directive. Nonstandard. Possible values: +// +// .OPT CEXPR - use C precedence in evaluating expressions rather than strict left-right +// --------------------------------------------------------------------------------- + +void x_opt (struct tag_op *op, char *label, char *mods, char *arg) +{ + char *tok; + + org_advanced = FALSE; // * means this address + + if (*label) { + asm_error("Label not permitted on .OPT statement"); + return; + } + // look for OPT arguments + for (tok = strtok(arg, ","); tok != NULL; tok = strtok(NULL, ",")) { + if (strcmp(tok, "CEXPR") == 0) { + cexpr = TRUE; // use C expression precedence (untested) + } + else + asm_error("Unknown .OPT: '%s'", tok); + } +} + +// --------------------------------------------------------------------------------- +// askip - skip input lines until a line with the target label appears +// --------------------------------------------------------------------------------- + +void askip (char *target) +{ + char nline[200], cur_label[20], *c; + + while (get_line(nline, sizeof(nline), TRUE)) { // read next line (but don't exit a macro) + listout(FALSE); // end listing of previous input line + + prep_line(nline); // preform standard line prep + + strncpy(cur_label, nline, 6); // get first 5 characters + cur_label[5] = '\0'; + + for (c = cur_label; *c > ' '; c++) // truncate at first whitespace + ; + *c = '\0'; + // stop if there's a match + if ((target == NULL) ? (cur_label[0] == '\0') : strcmp(target, cur_label) == 0) { + parse_line(nline); // process this line + return; + } + } + + if (target != NULL) + asm_error("Label %s not found", target); +} + +// --------------------------------------------------------------------------------- +// x_aif - process conditional assembly jump +// --------------------------------------------------------------------------------- + +void x_aif (struct tag_op *op, char *label, char *mods, char *arg) +{ + char *target, *tok; + EXPR expr1, expr2; + BOOL istrue; + enum {OP_EQ, OP_LT, OP_GT, OP_NE, OP_LE, OP_GE} cmp_op; + + // label is not entered into the symbol table + + arg = skipbl(arg); + if (*arg != '(') { + asm_error("AIF operand must start with ("); + return; + } + + arg++; // skip the paren + + // normally whitespace is never found in the arg string (see tabtok and coltok). + // However, spaces inside parens are permitted. + + if ((tok = strtok(arg, whitespace)) == NULL) { + asm_error("AIF missing first expression"); + return; + } + + getexpr(tok, FALSE, &expr1); + + if ((tok = strtok(NULL, whitespace)) == NULL) { + asm_error("AIF missing conditional operator"); + return; + } + + if (strcmp(tok, "EQ") == 0) + cmp_op = OP_EQ; + else if (strcmp(tok, "LT") == 0) + cmp_op = OP_LT; + else if (strcmp(tok, "GT") == 0) + cmp_op = OP_GT; + else if (strcmp(tok, "NE") == 0) + cmp_op = OP_NE; + else if (strcmp(tok, "LE") == 0) + cmp_op = OP_LE; + else if (strcmp(tok, "GE") == 0) + cmp_op = OP_GE; + else { + asm_error("AIF: %s is not a valid conditional operator", tok); + return; + } + + if ((tok = strtok(NULL, ")")) == NULL) { + asm_error("AIF missing second expression"); + return; + } + + getexpr(tok, FALSE, &expr2); + + switch (cmp_op) { // test the condition + case OP_EQ: istrue = expr1.value == expr2.value; break; + case OP_LT: istrue = expr1.value < expr2.value; break; + case OP_GT: istrue = expr1.value > expr2.value; break; + case OP_NE: istrue = expr1.value != expr2.value; break; + case OP_LE: istrue = expr1.value <= expr2.value; break; + case OP_GE: istrue = expr1.value >= expr2.value; break; + default: bail("in aif, can't happen"); + } + + // After the closing paren coltok and tabtok guarantee we will have no whitespace + + if ((target = strtok(arg, ",")) == NULL) // get target label + asm_warning("Missing target label"); + + if (istrue) + askip(target); // skip to the target +} + +// --------------------------------------------------------------------------------- +// x_aifb - conditional assembly jump back (macro only) +// --------------------------------------------------------------------------------- + +void x_aifb (struct tag_op *op, char *label, char *mods, char *arg) +{ + asm_error("aifb valid in macros only and not implemented in any case"); +} + +// --------------------------------------------------------------------------------- +// x_ago +// --------------------------------------------------------------------------------- + +void x_ago (struct tag_op *op, char *label, char *mods, char *arg) +{ + char *target; + + // label is not entered into the symbol table + + // handle differently in a macro + + if ((target = strtok(arg, ",")) == NULL) // get target label + asm_warning("Missing target label"); + + askip(target); // skip to the target +} + +// --------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------- + +void x_agob (struct tag_op *op, char *label, char *mods, char *arg) +{ + asm_error("agob valid in macros only and not implemented in any case"); +} + +// --------------------------------------------------------------------------------- +// --------------------------------------------------------------------------------- + +void x_anop (struct tag_op *op, char *label, char *mods, char *arg) +{ + // label is not entered into the symbol table + // do nothing else +} + +// --------------------------------------------------------------------------------- +// expression parser, borrowed from older code, no comments, sorry +// --------------------------------------------------------------------------------- + +char *exprptr, *oexprptr; + +#define GETNEXT (*exprptr++) +#define UNGET --exprptr + +#define LETTER 0 /* character types */ +#define DIGIT 1 +#define ETC 2 +#define ILL 3 +#define SPACE 4 +#define MULOP 5 +#define ADDOP 6 +#define EXPOP 7 + +int getnb (void); +void c_expr (EXPR *ap); +void c_expr_m (EXPR *ap); +void c_expr_e (EXPR *ap); +void c_expr_u (EXPR *ap); +void c_term (EXPR *ap); +int c_number (int c, int r, int nchar); +int digit (int c, int r); +int c_esc (int c); +void exprerr (int n); +void a1130_expr (EXPR *ap); +void a1130_term (EXPR *ap); + +char ctype[128] = { // character types +/*^0ABCDEFG */ ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL, +/*^HIJKLMNO */ ILL, SPACE, SPACE, ILL, SPACE, SPACE, ILL, ILL, +/*^PQRSTUVW */ ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL, +/*^XYZ */ ILL, ILL, ILL, ILL, ILL, ILL, ILL, ILL, +/* !"#$%&' */ SPACE, ETC, ETC, LETTER, LETTER, MULOP, MULOP, LETTER, /* $ # @ and ' are letters here */ +/* ()*+,-./ */ ETC, ETC, MULOP, ADDOP, ETC, ADDOP, ETC, MULOP, +/* 01234567 */ DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, +/* 89:;<=>? */ DIGIT, DIGIT, ETC, ETC, MULOP, ETC, MULOP, ETC, +/* @ABCDEFG */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, +/* HIJKLMNO */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, +/* PQRSTUVW */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, +/* XYZ[\]^_ */ LETTER, LETTER, LETTER, ETC, ETC, ETC, EXPOP, LETTER, +/* `abcdefg */ ETC, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, +/* hijklmno */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, +/* pqrstuvw */ LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, LETTER, +/* xyz{|}~ */ LETTER, LETTER, LETTER, ETC, ADDOP, ETC, ETC, ETC +}; + +char *errstr[] = { + "Missing exponent", // 0 + "Undefined symbol", // 1 + "Division by zero", // 2 + "Illegal operator", // 3 + ") expected", // 4 + "Char expected after '", // 5 + "Char expected after .", // 6 + "Number expected after =", // 7 + "Syntax error", // 8 + "Number syntax", // 9 + "Char expected after \\", // 10 + "Relocation error" // 11 +}; + +int getnb () { + int c; + + if (cexpr) { // in C mode, handle normally + while (ctype[(c = GETNEXT)] == SPACE) + ; + } // in 1130 mode, a space terminates the expression. Here, eat the rest + else if ((c = GETNEXT) == ' ') { + while ((c = GETNEXT) != '\0') + ; + } + + return c; +} + +int symbest, exprerrno; +jmp_buf exprjmp; + +// --------------------------------------------------------------------------------- +// getexpr +// --------------------------------------------------------------------------------- + +int getexpr (char *pc, BOOL undefined_ok, EXPR *pval) +{ + symbest = S_DEFINED; // assume no questionable symbols + + pval->value = 0; + pval->relative = ABSOLUTE; + + if (! *pc) // blank expression is same as zero, ok? + return S_DEFINED; + + if (setjmp(exprjmp) != 0) { // encountered a syntax error & bailed + pval->value = 0; + pval->relative = ABSOLUTE; + return S_UNDEFINED; + } + + exprptr = oexprptr = pc; // make global the buffer pointer + + c_expr(pval); + + if (GETNEXT) // expression should have been entirely eaten + exprerr(8); // if characters are left, it's an error + + if (pval->relative < 0 || pval->relative > 1) + exprerr(11); // has to work out to an absolute or a single relative term + + if (symbest == S_DEFINED) // tell how it came out + return S_DEFINED; + + pval->value = 0; + pval->relative = ABSOLUTE; + return (pass == 1 && undefined_ok) ? S_PROVISIONAL : S_UNDEFINED; +} + +// --------------------------------------------------------------------------------- +// output_literals - construct .DC assembler lines to assemble pending literal +// constant values that have accumulated. +// --------------------------------------------------------------------------------- + +void output_literals (BOOL eof) +{ + char line[120], label[12], num[20]; + int i; + + for (i = 0; i < n_literals; i++) { // generate DC statements for any pending literal constants + if (literal[i].even && literal[i].hex) // create the value string + sprintf(num, "/%08lx", literal[i].value); + else if (literal[i].even) + sprintf(num, "%ld", literal[i].value); + else if (literal[i].hex) + sprintf(num, "/%04x", literal[i].value & 0xFFFF); + else + sprintf(num, "%d", literal[i].value); + + sprintf(label, "_L%03d", literal[i].tagno); + format_line(line, label, literal[i].even ? "DEC" : "DC", "", num, "GENERATED LITERAL CONSTANT"); + + if (eof) { + eof = FALSE; // at end of file, for first literal, only prepare blank line + sprintf(listline, LEFT_MARGIN, org); + } + else + listout(TRUE); // push out any pending line(s) + + if (flist && list_on) // this makes stuff appear in the listing + sprintf(listline, LEFT_MARGIN " %s", detab(line)); + + nwout = 0; + + parse_line(line); // assemble the constant definition + } + + n_literals = 0; // clear list +} + +// --------------------------------------------------------------------------------- +// a1130_term - extract one term of an expression +// --------------------------------------------------------------------------------- + +void a1130_term (EXPR *ap) +{ + PSYMBOL s; + char token[80], *t; + int c; + + if (cexpr) { // use C syntax + c_term(ap); + return; + } + + c = GETNEXT; + + if (ctype[c] == DIGIT) { /* number */ + ap->value = signextend(c_number(c,10,-1)); + ap->relative = ABSOLUTE; + } + else if (c == '+') { /* unary + */ + a1130_term(ap); + } + else if (c == '-') { /* unary - */ + a1130_term(ap); + ap->value = - ap->value; + } + else if (c == '/') { /* / starts a hex constant */ + ap->value = signextend(c_number(c,16,-1)); + ap->relative = ABSOLUTE; + } + else if (c == '*') { /* asterisk alone = org */ + ap->value = org + org_advanced; // here is where that offset matters! + ap->relative = relocate; + } + else if (c == '.') { /* EBCDIC constant */ + c = GETNEXT; + if (c == '\0') { + UNGET; + c = ' '; + } + c = ascii_to_ebcdic_table[c]; + ap->value = c; // VALUE IS IN LOW BYTE!!! + ap->relative = ABSOLUTE; + } + else if (ctype[c] == LETTER) { /* symbol */ + t = token; + do { + *t++ = c; + c = GETNEXT; + } while (ctype[c] == LETTER || ctype[c] == DIGIT); + UNGET; + *t++ = '\0'; + + s = lookup_symbol(token, TRUE); + add_xref(s, FALSE); + ap->value = s->value; + ap->relative = s->relative; + + symbest = MIN(symbest, s->defined); // this goes to lowest value (undefined < provisional < defined) + if (pass == 2 && s->defined != S_DEFINED) + exprerr(1); + } + else + exprerr(8); +} + +// --------------------------------------------------------------------------------- +// signextend - sign-extend a 16-bit constant value to whatever "int" is. +// --------------------------------------------------------------------------------- + +int signextend (int v) +{ + v &= 0xFFFF; // clip to 16 bits (this may not be necessary, but best to be safe?) + + if (v & 0x8000) // if sign bit is set + v |= ~0xFFFF; // sign extend + + return v; +} + +// --------------------------------------------------------------------------------- +// c_expr - evalate an expression +// --------------------------------------------------------------------------------- + +void c_expr (EXPR *ap) +{ + int c; + EXPR rop; + + c_expr_m(ap); // get combined multiplicative terms + for (;;) { // handle +/- precedence operators + if (ctype[c=getnb()] != ADDOP) { + UNGET; + break; + } + c_expr_m(&rop); // right hand operand + switch (c) { + case '+': + ap->value += rop.value; + ap->relative += rop.relative; + break; + + case '-': + ap->value -= rop.value; + ap->relative -= rop.relative; + break; + + case '|': + if (ap->relative || rop.relative) + exprerr(11); + ap->value = ((long) (ap->value)) | ((long) rop.value); + break; + + default: + printf("In expr, can't happen\n"); + } + } +} + +// --------------------------------------------------------------------------------- +// c_expr_m - get multiplicative precedence terms. Again, this is not usually used +// --------------------------------------------------------------------------------- + +void c_expr_m (EXPR *ap) +{ + int c; + EXPR rop; + + c_expr_e(ap); // get exponential precedence term + for (;;) { // get operator + c = getnb(); + if ((c=='<') || (c=='>')) + if (c != getnb()) // << or >> + exprerr(3); + if (ctype[c] != MULOP) { + UNGET; + break; + } + c_expr_e(&rop); // right hand operand + + switch(c) { + case '*': + if (ap->relative && rop.relative) + exprerr(11); + + ap->value *= rop.value; + ap->relative = (ap->relative || rop.relative) ? RELATIVE : ABSOLUTE; + break; + + case '/': + if (rop.value == 0) + exprerr(2); + if (ap->relative || rop.relative) + exprerr(11); + + ap->value /= rop.value; + break; + + case '%': + if (rop.value == 0) + exprerr(2); + if (ap->relative || rop.relative) + exprerr(11); + + ap->value = ((long) (ap->value)) % ((long) rop.value); + break; + + case '&': + if (ap->relative || rop.relative) + exprerr(11); + + ap->value = ((long) (ap->value)) & ((long) rop.value); + break; + + case '>': + if (ap->relative || rop.relative) + exprerr(11); + + ap->value = ((long) (ap->value)) >> ((long) rop.value); + break; + + case '<': + if (ap->relative || rop.relative) + exprerr(11); + + ap->value = ((long) (ap->value)) << ((long) rop.value); + break; + + default: + printf("In expr_m, can't happen\n"); + } + } +} + +// --------------------------------------------------------------------------------- +// c_expr_e - get exponential precedence terms. Again, this is not usually used +// --------------------------------------------------------------------------------- + +void c_expr_e (EXPR *ap) +{ + int c, i, v; + EXPR rop; + + c_expr_u(ap); + for (;;) { + c = getnb(); + if (ctype[c] != EXPOP) { + UNGET; + break; + } + c_expr_u(&rop); + + switch(c) { + case '^': + if (ap->relative || rop.relative) + exprerr(11); + + v = ap->value; + ap->value = 1; + for (i = 0; i < rop.value; i++) + ap->value *= v; + break; + + default: + printf("In expr_e, can't happen\n"); + } + } +} + +// --------------------------------------------------------------------------------- +// c_expr_u - get unary precedence terms. Again, this is not usually used +// --------------------------------------------------------------------------------- + +void c_expr_u (EXPR *ap) +{ + int c; + + if ((c = getnb()) == '!') { + a1130_term(ap); + ap->value = ~ ((long)(ap->value)); + if (ap->relative) + exprerr(11); + } + else if (c == '-') { + a1130_term(ap); + ap->value = - ap->value; + if (ap->relative) + exprerr(11); + } + else { + UNGET; + a1130_term(ap); + } +} + +// --------------------------------------------------------------------------------- +// c_term - get basic operand or parenthesized expression. Again, this is not usually used +// --------------------------------------------------------------------------------- + +void c_term (EXPR *ap) +{ + int c, cc; + PSYMBOL s; + char token[80], *t; + + ap->relative = ABSOLUTE; /* assume absolute */ + + if ((c = getnb()) == '(') { /* parenthesized expr */ + c_expr(ap); /* start over at the top! */ + if ((cc = getnb()) != ')') + exprerr(4); + } + else if (c == '\'') { /* single quote: char */ + if ((c = GETNEXT) == '\0') + c = ' '; + ap->value = c_esc(c); + } + else if (ctype[c] == DIGIT) { /* number */ + ap->value = signextend(c_number(c,10,-1)); + } + else if (c == '0') { /* 0 starts a hex or octal constant */ + if ((c = GETNEXT) == 'x') { + c = GETNEXT; + ap->value = signextend(c_number(c,16,-1)); + } + else { + ap->value = signextend(c_number(c,8,-1)); + } + } + else if (c == '*') { /* asterisk alone = org */ + ap->value = org + org_advanced; + ap->relative = relocate; + } + else if (ctype[c] == LETTER) { /* symbol */ + t = token; + do { + *t++ = c; + c = GETNEXT; + } while (ctype[c] == LETTER || ctype[c] == DIGIT); + UNGET; + *t++ = '\0'; + + s = lookup_symbol(token, TRUE); + ap->value = s->value; + ap->relative = s->relative; + add_xref(s, FALSE); + symbest = MIN(symbest, s->defined); // this goes to lowest value (undefined < provisional < defined) + + if (pass == 2 && s->defined != S_DEFINED) + exprerr(1); + } + else + exprerr(8); +} + +// --------------------------------------------------------------------------------- +// c_number - get a C format constant value. Again, this is not usually used +// --------------------------------------------------------------------------------- + +int c_number (int c, int r, int nchar) +{ + int v, n; + + nchar--; + + if (c == '/' && ! cexpr) { /* special radix stuff */ + r = 16; + c = GETNEXT; + } + else if (r == 10 && c == '0' && cexpr) { /* accept C style 0x## also */ + c = GETNEXT; + if (c == 'x') { + r = 16; + c = GETNEXT; + } + else { + r = 8; + UNGET; + c = '0'; + } + } + + n = 0; /* decode number */ + while ((nchar-- != 0) && (v = digit(c, r)) >= 0) { + if (v >= r) /* out of range! */ + exprerr(9); + + n = r*n + v; + + c = GETNEXT; + if (c == '.') { // maybe make it decimal? + c = GETNEXT; + break; + } + } + + UNGET; + return (n); +} + +// --------------------------------------------------------------------------------- +// digit - get digit value of character c in radix r +// --------------------------------------------------------------------------------- + +int digit (int c, int r) +{ + if (r == 16) { + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + } + + if (c >= '0' && c <= '9') + return (c - '0'); + + return (-1); +} + +// --------------------------------------------------------------------------------- +// c_esc - handle C character escape +// --------------------------------------------------------------------------------- + +int c_esc (int c) +{ + if (c != '\\') /* not escaped */ + return(c); + + if ((c = GETNEXT) == '\0') /* must be followed by something */ + exprerr(10); + if ((c >= 'A') && (c <= 'Z')) /* handle upper case */ + c += 'a'-'A'; + if (ctype[c] == LETTER) /* control character abbrevs */ + switch (c) { + case 'b': c = '\b'; break; /* backspace */ + case 'e': c = 27 ; break; /* escape */ + case 'f': c = '\f'; break; /* formfeed */ + case 'n': c = '\n'; break; /* newline */ + case 'r': c = '\r'; break; /* return */ + case 't': c = '\t'; break; /* horiz. tab */ + } + else if (ctype[c] == DIGIT) { /* get character by the numbers */ + c = c_number(c,8,3); /* force octal */ + } + + return c; +} + +// --------------------------------------------------------------------------------- +// exprerr - note an expression syntax error. Longjumps back to caller with failure code +// --------------------------------------------------------------------------------- + +void exprerr (int n) +{ + char msg[256]; + int nex = exprptr-oexprptr; + + strncpy(msg, oexprptr, nex); // show where the problem was + msg[nex] = '\0'; + strcat(msg, " << "); + strcat(msg, errstr[n]); + + asm_error(msg); + + exprerrno = n; + longjmp(exprjmp, 1); +} + +/* ------------------------------------------------------------------------ + * upcase - force a string to uppercase (ASCII) + * ------------------------------------------------------------------------ */ + +char *upcase (char *str) +{ + char *s; + + for (s = str; *s; s++) { + if (*s >= 'a' && *s <= 'z') + *s -= 32; + } + + return str; +} + +/* ------------------------------------------------------------------------ + * hollerith table for IPL card ident field + * ------------------------------------------------------------------------ */ + +typedef struct { + int hollerith; + char ascii; +} CPCODE; + +static CPCODE cardcode_029[] = +{ + 0x0000, ' ', + 0x8000, '&', // + in 026 Fortran + 0x4000, '-', + 0x2000, '0', + 0x1000, '1', + 0x0800, '2', + 0x0400, '3', + 0x0200, '4', + 0x0100, '5', + 0x0080, '6', + 0x0040, '7', + 0x0020, '8', + 0x0010, '9', + 0x9000, 'A', + 0x8800, 'B', + 0x8400, 'C', + 0x8200, 'D', + 0x8100, 'E', + 0x8080, 'F', + 0x8040, 'G', + 0x8020, 'H', + 0x8010, 'I', + 0x5000, 'J', + 0x4800, 'K', + 0x4400, 'L', + 0x4200, 'M', + 0x4100, 'N', + 0x4080, 'O', + 0x4040, 'P', + 0x4020, 'Q', + 0x4010, 'R', + 0x3000, '/', + 0x2800, 'S', + 0x2400, 'T', + 0x2200, 'U', + 0x2100, 'V', + 0x2080, 'W', + 0x2040, 'X', + 0x2020, 'Y', + 0x2010, 'Z', + 0x0820, ':', + 0x0420, '#', // = in 026 Fortran + 0x0220, '@', // ' in 026 Fortran + 0x0120, '\'', + 0x00A0, '=', + 0x0060, '"', + 0x8820, 'c', // cent + 0x8420, '.', + 0x8220, '<', // ) in 026 Fortran + 0x8120, '(', + 0x80A0, '+', + 0x8060, '|', + 0x4820, '!', + 0x4420, '$', + 0x4220, '*', + 0x4120, ')', + 0x40A0, ';', + 0x4060, 'n', // not + 0x2820, 'x', // what? + 0x2420, ',', + 0x2220, '%', // ( in 026 Fortran + 0x2120, '_', + 0x20A0, '>', + 0x2060, '>', +}; + +int ascii_to_hollerith (int ch) +{ + int i; + + for (i = 0; i < sizeof(cardcode_029) / sizeof(CPCODE); i++) + if (cardcode_029[i].ascii == ch) + return cardcode_029[i].hollerith; + + return 0; +} + +/* ------------------------------------------------------------------------ + * detab - replace tabs with spaces for listing files + * ------------------------------------------------------------------------ */ + +char *detab (char *instr) +{ + static char outstr[256]; + char *out = outstr; + int col = 0; + + while (*instr) { + if (*instr == '\t') { + do { + *out++ = ' '; + col++; + } + while (col & 7); + } + else { + *out++ = *instr; + col++; + } + + instr++; + } + + *out = '\0'; + + return outstr; +} + +#ifndef _WIN32 + +int strnicmp (char *a, char *b, int n) +{ + int ca, cb; + + for (;;) { + if (--n < 0) // still equal after n characters? quit now + return 0; + + if ((ca = *a) == 0) // get character, stop on null terminator + return *b ? -1 : 0; + + if (ca >= 'a' && ca <= 'z') // fold lowercase to uppercase + ca -= 32; + + cb = *b; + if (cb >= 'a' && cb <= 'z') + cb -= 32; + + if ((ca -= cb) != 0) // if different, return comparison + return ca; + + a++, b++; + } +} + +int strcmpi (char *a, char *b) +{ + int ca, cb; + + for (;;) { + if ((ca = *a) == 0) // get character, stop on null terminator + return *b ? -1 : 0; + + if (ca >= 'a' && ca <= 'z') // fold lowercase to uppercase + ca -= 32; + + cb = *b; + if (cb >= 'a' && cb <= 'z') + cb -= 32; + + if ((ca -= cb) != 0) // if different, return comparison + return ca; + + a++, b++; + } +} + +#endif + diff --git a/Ibm1130/utils/asm1130.mak b/Ibm1130/utils/asm1130.mak new file mode 100644 index 0000000..f95ef13 --- /dev/null +++ b/Ibm1130/utils/asm1130.mak @@ -0,0 +1,175 @@ +# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Win32 Debug +!MESSAGE No configuration specified. Defaulting to Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "asm1130.mak" CFG="Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Win32 Debug" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "WinRel" +# PROP BASE Intermediate_Dir "WinRel" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "WinRel" +# PROP Intermediate_Dir "WinRel" +OUTDIR=.\WinRel +INTDIR=.\WinRel + +ALL : $(OUTDIR)/asm1130.exe $(OUTDIR)/asm1130.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"asm1130.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinRel/ +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"asm1130.bsc" +BSC32_SBRS= \ + $(INTDIR)/asm1130.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/asm1130.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console\ + /INCREMENTAL:no /PDB:$(OUTDIR)/"asm1130.pdb" /MACHINE:I386\ + /OUT:$(OUTDIR)/"asm1130.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/asm1130.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/asm1130.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "WinDebug" +# PROP BASE Intermediate_Dir "WinDebug" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "WinDebug" +# PROP Intermediate_Dir "WinDebug" +OUTDIR=.\WinDebug +INTDIR=.\WinDebug + +ALL : $(OUTDIR)/asm1130.exe $(OUTDIR)/asm1130.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"asm1130.pch" /Fo$(INTDIR)/\ + /Fd$(OUTDIR)/"asm1130.pdb" /c +CPP_OBJS=.\WinDebug/ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"asm1130.bsc" +BSC32_SBRS= \ + $(INTDIR)/asm1130.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/asm1130.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console\ + /INCREMENTAL:yes /PDB:$(OUTDIR)/"asm1130.pdb" /DEBUG /MACHINE:I386\ + /OUT:$(OUTDIR)/"asm1130.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/asm1130.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/asm1130.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Group "Source Files" + +################################################################################ +# Begin Source File + +SOURCE=.\asm1130.c + +$(INTDIR)/asm1130.obj : $(SOURCE) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\util_io.c +DEP_UTIL_=\ + .\util_io.h + +$(INTDIR)/util_io.obj : $(SOURCE) $(DEP_UTIL_) $(INTDIR) + +# End Source File +# End Group +# End Project +################################################################################ diff --git a/Ibm1130/utils/bindump.c b/Ibm1130/utils/bindump.c new file mode 100644 index 0000000..a5f8ea6 --- /dev/null +++ b/Ibm1130/utils/bindump.c @@ -0,0 +1,755 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +// --------------------------------------------------------------------------------- +// BINDUMP - dumps card deck files in assembler object format +// +// Usage: +/// bindump deckfile lists object header info & sector break cards +// bindump -v deckfile lists object data records as well +// bindump -p deckfile for system program, lists phase IDs in the deck +// bindump -s deckfile >outfile for system program, sorts the phases & writes to stdout + +#include +#include +#ifdef _WIN32 +# include +# include +# include +#endif + +#include "util_io.h" + +#ifndef TRUE + #define BOOL int + #define TRUE 1 + #define FALSE 0 +#endif + +typedef enum {R_ABSOLUTE = 0, R_RELATIVE = 1, R_LIBF = 2, R_CALL = 3} RELOC; + +BOOL verbose = FALSE; +BOOL phid = FALSE; +BOOL sort = FALSE; +unsigned short card[80], buf[54], cardtype; + +// bindump - dump a binary (card format) deck to verify sbrks, etc + +void bail (char *msg); +void dump (char *fname); +void dump_data (char *fname); +void dump_phids (char *fname); +char *getname (unsigned short *ptr); +char *getseq (void); +int hollerith_to_ascii (unsigned short h); +void process (char *fname); +void show_raw (char *name); +void show_data (void); +void show_core (void); +void show_endc (void); +void show_81 (void); +void show_main (void); +void show_sub (void); +void show_ils (void); +void show_iss (void); +void show_end (void); +void sort_phases (char *fname); +void trim (char *s); +void unpack (unsigned short *card, unsigned short *buf); +void verify_checksum(unsigned short *buf); + +int main (int argc, char **argv) +{ + char *arg; + static char usestr[] = "Usage: bindump [-psv] filename..."; + int i; + + for (i = 1; i < argc; i++) { + arg = argv[i]; + if (*arg == '-') { + arg++; + while (*arg) { + switch (*arg++) { + case 'v': + verbose = TRUE; + break; + case 'p': + phid = TRUE; // print only phase ID's + break; + case 's': + sort = TRUE; // sort deck by phases, writing to stdout + break; + default: + bail(usestr); + } + } + } + } + + for (i = 1; i < argc; i++) { + arg = argv[i]; + if (*arg != '-') + process(arg); + } + return 0; +} + +void process (char *nm) +{ +#ifdef _WIN32 + WIN32_FIND_DATA fd; + HANDLE hFind; + char *c, buf[256]; + + if (strchr(nm, '*') == NULL && strchr(nm, '?') == NULL) + dump(nm); + + else if ((hFind = FindFirstFile(nm, &fd)) == INVALID_HANDLE_VALUE) + fprintf(stderr, "No files matching '%s'\n", nm); + + else { + if ((c = strrchr(nm, '\\')) == NULL) + c = strrchr(nm, ':'); + + do { + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; + + if (c == NULL) + dump(fd.cFileName); + else { + strcpy(buf, nm); + strcpy(buf + (c-nm+1), fd.cFileName); + dump(buf); + } + + } while (FindNextFile(hFind, &fd)); + + FindClose(hFind); + } +#else + dump(nm); // on unices, sh globs for us +#endif +} + +void dump (char *fname) +{ + if (sort) + sort_phases(fname); + else if (phid) + dump_phids(fname); + else + dump_data(fname); +} + +struct tag_card { + int phid, seq; + unsigned short card[80]; +}; + +int cardcomp (const void *a, const void *b) +{ + short diff; + + diff = ((struct tag_card *) a)->phid - ((struct tag_card *) b)->phid; + + return diff ? diff : (((struct tag_card *) a)->seq - ((struct tag_card *) b)->seq); +} + +void sort_phases (char *fname) +{ + int i, ncards, cardtype, len, seq = 0, phid; + struct tag_card *deck; + FILE *fd; + BOOL saw_sbrk = TRUE; + + if ((fd = fopen(fname, "rb")) == NULL) { + perror(fname); + return; + } + + fseek(fd, 0, SEEK_END); + len = ftell(fd); // get length of file + fseek(fd, 0, SEEK_SET); + + if (len <= 0 || (len % 160) != 0) { + fprintf(stderr, "%s is not a binard deck image\n"); + fclose(fd); + return; + } + + ncards = len / 160; + + if ((deck = (struct tag_card *) malloc(ncards*sizeof(struct tag_card))) == NULL) { + fprintf(stderr, "%s: can't sort, insufficient memory\n"); + fclose(fd); + return; + } + + phid = 0; + for (i = 0; i < ncards; i++) { + if (fxread(deck[i].card, sizeof(card[0]), 80, fd) != 80) { + free(deck); + fprintf(stderr, "%s: error reading deck\n"); + fclose(fd); + return; + } + + unpack(deck[i].card, buf); + deck[i].seq = seq++; + deck[i].phid = phid; + + verify_checksum(buf); + + cardtype = (buf[2] >> 8) & 0xFF; + + if (cardtype == 1 || cardtype == 2) { // start of deck is same as sector break + saw_sbrk = TRUE; + } + else if (cardtype == 0) { + fprintf(stderr, "%s is a core image deck\n"); + free(deck); + fclose(fd); + return; + } + else if (cardtype == 0x0A && saw_sbrk) { + phid = (int) (signed short) buf[10]; + if (phid < 0) + phid = -phid; + + deck[i].phid = phid; // this belongs to the new phase + deck[i-1].phid = phid; // as does previous card + saw_sbrk = FALSE; + } + } + fclose(fd); + + qsort(deck, ncards, sizeof(struct tag_card), cardcomp); // sort the deck + +#ifdef _WIN32 + _setmode(_fileno(stdout), _O_BINARY); // set standard output to binary mode +#endif + + for (i = 0; i < ncards; i++) // write to stdout + fxwrite(deck[i].card, sizeof(card[0]), 80, stdout); + + free(deck); +} + +void dump_phids (char *fname) +{ + FILE *fp; + BOOL first = TRUE; + BOOL saw_sbrk = TRUE, neg; + short id; + + if ((fp = fopen(fname, "rb")) == NULL) { + perror(fname); + return; + } + + printf("\n%s:\n", fname); + + while (fxread(card, sizeof(card[0]), 80, fp) > 0) { + unpack(card, buf); + verify_checksum(buf); + + cardtype = (buf[2] >> 8) & 0xFF; + + if (cardtype == 1 && ! first) { // sector break + saw_sbrk = TRUE; + continue; + } + else { + switch (cardtype) { + case 0x00: + printf(" This is a core image deck\n"); + goto done; + break; + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x0F: + break; + + case 0x0A: + if (saw_sbrk) { + id = buf[10]; + if (id < 0) + id = -id, neg = TRUE; + else + neg = FALSE; + printf(" : %3d / %02x%s\n", id, id, neg ? " (neg)" : ""); + saw_sbrk = FALSE; + } + break; + + default: + show_raw("??? "); + } + } +done: + first = FALSE; + } + + fclose(fp); +} + +void dump_data (char *fname) +{ + FILE *fp; + BOOL first = TRUE; + char str[80]; + int i; + + if ((fp = fopen(fname, "rb")) == NULL) { + perror(fname); + return; + } + + printf("\n%s:\n", fname); + + while (fxread(card, sizeof(card[0]), 80, fp) > 0) { + unpack(card, buf); + verify_checksum(buf); + + cardtype = (buf[2] >> 8) & 0xFF; + + if (cardtype == 1 && ! first) { // sector break + for (i = 4; i < 72; i++) + str[i] = hollerith_to_ascii(card[i]); + + str[i] = '\0'; + trim(str+4); + printf("*SBRK %s\n", str+4); + continue; + } + else { + switch (cardtype) { + case 0x00: + if (first) + show_raw("CORE"); + if (verbose) + show_core(); + break; + + case 0x01: + show_raw("ABS "); + show_main(); + break; + case 0x02: + show_raw("REL "); + show_main(); + break; + case 0x03: + show_raw("LIB "); + show_sub(); + break; + case 0x04: + show_raw("SUB "); + show_sub(); + break; + case 0x05: + show_raw("ISSL"); + show_iss(); + break; + case 0x06: + show_raw("ISSC"); + show_iss(); + break; + case 0x07: + show_raw("ILS "); + show_ils(); + break; + case 0x0F: + show_raw("END "); + show_end(); + break; + case 0x80: + show_raw("ENDC"); + show_endc(); + break; + case 0x81: + show_raw("81 "); + show_81(); + break; + case 0x0A: + if (verbose) + show_data(); + break; + default: + show_raw("??? "); + } + } + + first = FALSE; + } + + fclose(fp); +} + +void show_data (void) +{ + int i, n, jrel, rflag, nout, ch, reloc; + BOOL first = TRUE; + + n = buf[2] & 0x00FF; + + printf("%04x: ", buf[0]); + + jrel = 3; + nout = 0; + rflag = buf[jrel++]; + for (i = 0; i < n; i++) { + if (nout >= 8) { + rflag = buf[jrel++]; + if (first) { + printf(" %s", getseq()); + first = FALSE; + } + printf("\n "); + nout = 0; + } + reloc = (rflag >> 14) & 0x03; + ch = (reloc == R_ABSOLUTE) ? ' ' : + (reloc == R_RELATIVE) ? 'R' : + (reloc == R_LIBF) ? 'L' : '@'; + + printf("%04x%c ", buf[9+i], ch); + rflag <<= 2; + nout++; + } + putchar('\n'); +} + +void show_core (void) +{ + int i, n, nout; + BOOL first = TRUE; + + n = buf[2] & 0x00FF; + + printf("%04x: ", buf[0]); + + nout = 0; + for (i = 0; i < n; i++) { + if (nout >= 8) { + if (first) { + printf(" %s", getseq()); + first = FALSE; + } + printf("\n "); + nout = 0; + } + printf("%04x ", buf[9+i]); + nout++; + } + putchar('\n'); +} + +void info (int i, char *nm, char type) +{ + if (nm) + printf("%s ", nm); + + switch (type) { + case 'd': + printf("%d ", buf[i]); + break; + + case 'x': + printf("%04x ", buf[i]); + break; + + case 'b': + printf("%02x ", buf[i] & 0xFF); + break; + + case 'n': + printf("%s ", getname(buf+i)); + break; + + default: + bail("BAD TYPE"); + } +} + +void show_main (void) +{ + printf(" "); + info(2, "prec", 'b'); + info(4, "common", 'd'); + info(6, "work", 'd'); + info(8, "files", 'd'); + info(9, "name", 'n'); + info(11, "pta", 'x'); + putchar('\n'); +} + +void show_sub (void) +{ + int i, n; + + printf(" "); + info( 2, "prec", 'b'); + + n = buf[5] / 3; + for (i = 0; i < n; i++) { + info( 9+3*i, "ent", 'n'); + info(11+3*i, NULL, 'x'); + } + + putchar('\n'); +} + +void show_iss (void) +{ + printf(" "); + info(12, "level", 'd'); + putchar('\n'); +} + +void show_ils (void) +{ + printf(" "); + info( 2, "prec", 'b'); + info( 5, "nint6", 'd'); + info( 9, "ent", 'n'); + info(11, NULL, 'x'); + info(14, "nint", 'd'); + info(15, "il1", 'd'); + info(16, "il2", 'd'); + putchar('\n'); +} + +void show_end (void) +{ + printf(" "); + info(0, "size", 'd'); + info(3, "pta", 'x'); + putchar('\n'); +} + +void show_endc(void) +{ + printf(" "); + info(52, "IX3", 'x'); + info(53, "pta", 'x'); + putchar('\n'); +} + +void show_81(void) +{ +} + +void show_raw (char *name) +{ + int i; + printf("*%s", name); + + for (i = 0; i < 12; i++) + printf(" %04x", buf[i]); + + printf(" %s\n", getseq()); +} + +char * getseq (void) +{ + static char seq[10]; + int i; + + for (i = 0; i < 8; i++) + seq[i] = hollerith_to_ascii(card[72+i]); + + seq[i] = '\0'; + return seq; +} + + +void bail (char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(1); +} + +void unpack (unsigned short *icard, unsigned short *obuf) +{ + int i, j; + unsigned short wd1, wd2, wd3, wd4; + + for (i = j = 0; i < 54; i += 3, j += 4) { + wd1 = icard[j]; + wd2 = icard[j+1]; + wd3 = icard[j+2]; + wd4 = icard[j+3]; + + obuf[i ] = (wd1 & 0xFFF0) | ((wd2 >> 12) & 0x000F); + obuf[i+1] = ((wd2 << 4) & 0xFF00) | ((wd3 >> 8) & 0x00FF); + obuf[i+2] = ((wd3 << 8) & 0xF000) | ((wd4 >> 4) & 0x0FFF); + } +} + +void verify_checksum (unsigned short *obuf) +{ +// unsigned short sum; + + if (obuf[1] == 0) // no checksum + return; + +// if (sum != card[1]) +// printf("Checksum %04x doesn't match card %04x\n", sum, card[1]); +} + +typedef struct { + unsigned short hollerith; + char ascii; +} CPCODE; + +static CPCODE cardcode_029[] = +{ + 0x0000, ' ', + 0x8000, '&', // + in 026 Fortran + 0x4000, '-', + 0x2000, '0', + 0x1000, '1', + 0x0800, '2', + 0x0400, '3', + 0x0200, '4', + 0x0100, '5', + 0x0080, '6', + 0x0040, '7', + 0x0020, '8', + 0x0010, '9', + 0x9000, 'A', + 0x8800, 'B', + 0x8400, 'C', + 0x8200, 'D', + 0x8100, 'E', + 0x8080, 'F', + 0x8040, 'G', + 0x8020, 'H', + 0x8010, 'I', + 0x5000, 'J', + 0x4800, 'K', + 0x4400, 'L', + 0x4200, 'M', + 0x4100, 'N', + 0x4080, 'O', + 0x4040, 'P', + 0x4020, 'Q', + 0x4010, 'R', + 0x3000, '/', + 0x2800, 'S', + 0x2400, 'T', + 0x2200, 'U', + 0x2100, 'V', + 0x2080, 'W', + 0x2040, 'X', + 0x2020, 'Y', + 0x2010, 'Z', + 0x0820, ':', + 0x0420, '#', // = in 026 Fortran + 0x0220, '@', // ' in 026 Fortran + 0x0120, '\'', + 0x00A0, '=', + 0x0060, '"', + 0x8820, 'c', // cent + 0x8420, '.', + 0x8220, '<', // ) in 026 Fortran + 0x8120, '(', + 0x80A0, '+', + 0x8060, '|', + 0x4820, '!', + 0x4420, '$', + 0x4220, '*', + 0x4120, ')', + 0x40A0, ';', + 0x4060, 'n', // not + 0x2820, 'x', // what? + 0x2420, ',', + 0x2220, '%', // ( in 026 Fortran + 0x2120, '_', + 0x20A0, '>', + 0x2060, '>', +}; + +int hollerith_to_ascii (unsigned short h) +{ + int i; + + h &= 0xFFF0; + + for (i = 0; i < sizeof(cardcode_029) / sizeof(CPCODE); i++) + if (cardcode_029[i].hollerith == h) + return cardcode_029[i].ascii; + + return '?'; +} + +// --------------------------------------------------------------------------------- +// trim - remove trailing whitespace from string s +// --------------------------------------------------------------------------------- + +void trim (char *s) +{ + char *nb; + + for (nb = s-1; *s; s++) + if (*s > ' ') + nb = s; + + nb[1] = '\0'; +} + +int ascii_to_ebcdic_table[128] = +{ + 0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f, + 0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61, + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f, + + 0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6, + 0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d, + 0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96, + 0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07, +}; + +char *getname (unsigned short *ptr) +{ + static char str[6]; + int i, j, ch; + long v; + + v = (ptr[0] << 16L) | ptr[1]; + + for (i = 0; i < 5; i++) { + ch = ((v >> 24) & 0x3F) | 0xC0; // recover those lost two bits + v <<= 6; + + str[i] = ' '; + + for (j = 0; j < (sizeof(ascii_to_ebcdic_table)/sizeof(ascii_to_ebcdic_table[0])); j++) { + if (ascii_to_ebcdic_table[j] == ch) { + str[i] = j; + break; + } + } + } + + str[5] = '\0'; + return str; +} + + diff --git a/Ibm1130/utils/bindump.mak b/Ibm1130/utils/bindump.mak new file mode 100644 index 0000000..c2cbab2 --- /dev/null +++ b/Ibm1130/utils/bindump.mak @@ -0,0 +1,175 @@ +# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Win32 Debug +!MESSAGE No configuration specified. Defaulting to Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "bindump.mak" CFG="Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Win32 Debug" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "WinRel" +# PROP BASE Intermediate_Dir "WinRel" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "WinRel" +# PROP Intermediate_Dir "WinRel" +OUTDIR=.\WinRel +INTDIR=.\WinRel + +ALL : $(OUTDIR)/bindump.exe $(OUTDIR)/bindump.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"bindump.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinRel/ +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"bindump.bsc" +BSC32_SBRS= \ + $(INTDIR)/bindump.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/bindump.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no /PDB:$(OUTDIR)/"bindump.pdb"\ + /MACHINE:I386 /OUT:$(OUTDIR)/"bindump.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/bindump.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/bindump.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "WinDebug" +# PROP BASE Intermediate_Dir "WinDebug" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "WinDebug" +# PROP Intermediate_Dir "WinDebug" +OUTDIR=.\WinDebug +INTDIR=.\WinDebug + +ALL : $(OUTDIR)/bindump.exe $(OUTDIR)/bindump.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"bindump.pch" /Fo$(INTDIR)/\ + /Fd$(OUTDIR)/"bindump.pdb" /c +CPP_OBJS=.\WinDebug/ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"bindump.bsc" +BSC32_SBRS= \ + $(INTDIR)/bindump.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/bindump.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes /PDB:$(OUTDIR)/"bindump.pdb" /DEBUG\ + /MACHINE:I386 /OUT:$(OUTDIR)/"bindump.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/bindump.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/bindump.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Group "Source Files" + +################################################################################ +# Begin Source File + +SOURCE=.\bindump.c + +$(INTDIR)/bindump.obj : $(SOURCE) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\util_io.c +DEP_UTIL_=\ + .\util_io.h + +$(INTDIR)/util_io.obj : $(SOURCE) $(DEP_UTIL_) $(INTDIR) + +# End Source File +# End Group +# End Project +################################################################################ diff --git a/Ibm1130/utils/checkdisk.c b/Ibm1130/utils/checkdisk.c new file mode 100644 index 0000000..bf775d0 --- /dev/null +++ b/Ibm1130/utils/checkdisk.c @@ -0,0 +1,262 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +// checkdisk - validates and optionally dumps an IBM1130 DMS2 disk image file +// +// Usage: +// checkdisk [-f] [-d cyl.sec|abssec] [-n count] filename +// +// Examples: +// checkdisk file.dsk +// report any misnumbered sectors in file.dsk +// +// checkdisk -f file.dsk +// report and fix any misnumbered sectors +// +// checkdisk -d 198.0 file.dsk +// dump cylinder 198 sector 0 +// +// checkdisk -d 0 file.dsk +// dump absolute sector 0 +// +// checkdisk -d 198.0 -n 4 file.dsk +// dump 4 sectors starting at m.n +// ----------------------------------------------------------------------------------------- +#include +#include +#include +#include "util_io.h" + +#ifdef _WIN32 +# include +#else + long filelength (int fno); +# include +# include +#endif + +#ifndef TRUE +# define BOOL int +# define TRUE 1 +# define FALSE 0 +#endif + +#define DSK_NUMWD 321 /* words/sector */ +#define DSK_NUMSC 4 /* sectors/surface */ +#define DSK_NUMSF 2 /* surfaces/cylinder */ +#define DSK_NUMCY 203 /* cylinders/drive */ +#define DSK_NUMDR 5 /* drives/controller */ +#define DSK_SIZE (DSK_NUMCY * DSK_NUMSF * DSK_NUMSC * DSK_NUMWD) /* words/drive */ + +char *usestr = "Usage: checkdisk [-f] [-d cyl.sec|abssec] [-n count] diskfile"; +char *baddisk = "Cannot fix this"; + +void bail (char *msg); +char *lowcase (char *str); + +int main (int argc, char **argv) +{ + FILE *fp; + char *fname = NULL, *arg, *argval; + int i, j, cyl, sec, pos, asec, retry, nbad = 0, nfixed = 0, nline; + BOOL fixit = FALSE, dump = FALSE; + int dsec, nsec = 1; + unsigned short wd, buf[DSK_NUMWD]; + + for (i = 1; i < argc;) { + arg = argv[i++]; + if (*arg == '-') { + arg++; + lowcase(arg); + while (*arg) { + switch (*arg++) { + case 'f': + fixit = TRUE; + break; + + case 'd': + dump = TRUE; + + if (i >= argc) + bail(usestr); + + argval = argv[i++]; + if (strchr(argval, '.') != NULL) { + if (sscanf(argval, "%d.%d", &cyl, &sec) != 2) + bail(usestr); + + dsec = cyl*(DSK_NUMSF*DSK_NUMSC) + sec; + } + else if (sscanf(argval, "%d", &dsec) != 1) + bail(usestr); + + if (dsec < 0 || dsec >= (DSK_NUMCY*DSK_NUMSF*DSK_NUMSC)) + bail("No such sector"); + + break; + + case 'n': + if (i >= argc) + bail(usestr); + + argval = argv[i++]; + if (sscanf(argval, "%d", &nsec) != 1) + bail(usestr); + + if (nsec <= 0) + bail(usestr); + + break; + + default: + bail(usestr); + } + } + } + else if (fname == NULL) + fname = arg; + else + bail(usestr); + } + + if (fname == NULL) + bail(usestr); + + if ((fp = fopen(fname, "rb+")) == NULL) { + perror(fname); + return 1; + } + + if (filelength(fileno(fp)) != 2*DSK_SIZE) { + fprintf(stderr, "File is wrong length, expected %d\n", DSK_SIZE); + bail(baddisk); + } + + for (cyl = 0; cyl < DSK_NUMCY; cyl++) { + for (sec = 0; sec < (DSK_NUMSF*DSK_NUMSC); sec++) { + retry = 1; +again: + asec = cyl*(DSK_NUMSF*DSK_NUMSC) + sec; + pos = asec*2*DSK_NUMWD; + + if (fseek(fp, pos, SEEK_SET) != 0) { + fprintf(stderr, "Error seeking to pos %x\n", pos); + bail(baddisk); + } + + if (fxread(&wd, sizeof(wd), 1, fp) != 1) { + fprintf(stderr, "Error reading word at abs sec %x, cyl %x, sec %x at offset %x\n", asec, cyl, sec, pos); + bail(baddisk); + } + + if (wd != asec) { + fprintf(stderr, "Bad sector #%x at abs sec %x, cyl %x, sec %x at offset %x\n", wd, asec, cyl, sec, pos); + nbad++; + + if (fixit) { + if (fseek(fp, pos, SEEK_SET) != 0) { + fprintf(stderr, "Error seeking to pos %x\n", pos); + bail(baddisk); + } + + if (fxwrite(&asec, sizeof(wd), 1, fp) != 1) { + fprintf(stderr, "Error writing sector # to abs sec %x, cyl %x, sec %x at offset %x\n", asec, cyl, sec, pos); + bail(baddisk); + } + + if (retry) { + retry = 0; + nfixed++; + goto again; + } + + fprintf(stderr, "Failed after retry\n"); + bail(baddisk); + } + } + } + } + + if (nbad) + printf("%d bad sector mark%s %s\n", nbad, (nbad == 1) ? "" : "s", fixit ? "fixed" : "found"); + else if (! dump) + printf("All sector marks OK\n"); + + if (! dump) + return 0; + + pos = dsec*2*DSK_NUMWD; + if (fseek(fp, pos, SEEK_SET) != 0) { + fprintf(stderr, "Error seeking to pos %x\n", pos); + bail(baddisk); + } + + for (i = 0; i < nsec; i++) { + cyl = dsec / (DSK_NUMSF*DSK_NUMSC); + sec = dsec - cyl*(DSK_NUMSF*DSK_NUMSC); + + if (fxread(&buf, sizeof(buf[0]), DSK_NUMWD, fp) != DSK_NUMWD) { + fprintf(stderr, "Error reading abs sec %x, cyl %x, sec %x at offset %x\n", dsec, cyl, sec, pos); + bail(baddisk); + } + + printf("\nSector %d.%d - %d - /%04x label %04x\n", cyl, sec, dsec, dsec, buf[0]); + for (nline = 0, j = 1; j < DSK_NUMWD; j++) { + printf("%04x", buf[j]); + if (++nline == 16) { + putchar('\n'); + nline = 0; + } + else + putchar(' '); + } + + dsec++; + } + + return 0; +} + +void bail (char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(1); +} + +/* ------------------------------------------------------------------------ + * lowcase - force a string to lower case (ASCII) + * ------------------------------------------------------------------------ */ + +char *lowcase (char *str) +{ + char *s; + + for (s = str; *s; s++) { + if (*s >= 'A' && *s <= 'Z') + *s += 32; + } + + return str; +} + +#ifndef _WIN32 + +long filelength (int fno) +{ + struct stat sb; + + if (fstat(fno, &sb) != 0) + return 0; + + return (long) sb.st_size; +} +#endif + diff --git a/Ibm1130/utils/checkdisk.mak b/Ibm1130/utils/checkdisk.mak new file mode 100644 index 0000000..e03d955 --- /dev/null +++ b/Ibm1130/utils/checkdisk.mak @@ -0,0 +1,177 @@ +# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Win32 Debug +!MESSAGE No configuration specified. Defaulting to Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "checkdisk.mak" CFG="Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Win32 Release" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "WinRel" +# PROP BASE Intermediate_Dir "WinRel" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "WinRel" +# PROP Intermediate_Dir "WinRel" +OUTDIR=.\WinRel +INTDIR=.\WinRel + +ALL : $(OUTDIR)/checkdisk.exe $(OUTDIR)/checkdisk.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"checkdisk.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinRel/ +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"checkdisk.bsc" +BSC32_SBRS= \ + $(INTDIR)/checkdisk.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/checkdisk.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib /NOLOGO\ + /SUBSYSTEM:console /INCREMENTAL:no /PDB:$(OUTDIR)/"checkdisk.pdb" /MACHINE:I386\ + /OUT:$(OUTDIR)/"checkdisk.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/checkdisk.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/checkdisk.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "WinDebug" +# PROP BASE Intermediate_Dir "WinDebug" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "WinDebug" +# PROP Intermediate_Dir "WinDebug" +OUTDIR=.\WinDebug +INTDIR=.\WinDebug + +ALL : $(OUTDIR)/checkdisk.exe $(OUTDIR)/checkdisk.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"checkdisk.pch" /Fo$(INTDIR)/\ + /Fd$(OUTDIR)/"checkdisk.pdb" /c +CPP_OBJS=.\WinDebug/ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"checkdisk.bsc" +BSC32_SBRS= \ + $(INTDIR)/checkdisk.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/checkdisk.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib /NOLOGO\ + /SUBSYSTEM:console /INCREMENTAL:yes /PDB:$(OUTDIR)/"checkdisk.pdb" /DEBUG\ + /MACHINE:I386 /OUT:$(OUTDIR)/"checkdisk.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/checkdisk.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/checkdisk.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Group "Source Files" + +################################################################################ +# Begin Source File + +SOURCE=.\checkdisk.c +DEP_CHECK=\ + .\util_io.h\ + \MSVC20\INCLUDE\sys\types.h\ + \MSVC20\INCLUDE\sys\stat.h + +$(INTDIR)/checkdisk.obj : $(SOURCE) $(DEP_CHECK) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\util_io.c + +$(INTDIR)/util_io.obj : $(SOURCE) $(INTDIR) + +# End Source File +# End Group +# End Project +################################################################################ diff --git a/Ibm1130/utils/diskview.c b/Ibm1130/utils/diskview.c new file mode 100644 index 0000000..42fa37d --- /dev/null +++ b/Ibm1130/utils/diskview.c @@ -0,0 +1,612 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +// DISKVIEW - lists contents of an 1130 system disk image file. Not finished yet. +// needs LET/SLET listing routine. +// +// usage: +// diskview -v diskfile + +#include +#include +#include +#include +#include "util_io.h" + +#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b))) +#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#define MAX(a,b) (((a) >= (b)) ? (a) : (b)) + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +# define BOOL int +#endif + +#define NOT_DEF 0x0658 // defective cylinder table entry means no defect + +#define DSK_NUMWD 321 /* words/sector */ +#define DSK_NUMCY 203 /* cylinders/drive */ +#define DSK_SECCYL 8 /* sectors per cylinder */ +#define SECLEN 320 /* data words per sector */ +#define SLETLEN ((3*SECLEN)/4) /* length of slet in records */ + +typedef unsigned short WORD; + +FILE *fp; +WORD buf[DSK_NUMWD]; +WORD dcom[DSK_NUMWD]; + +#pragma pack(2) +struct tag_slet { + WORD phid; + WORD addr; + WORD nwords; + WORD sector; +} slet[SLETLEN]; + +#pragma pack() + +WORD dcyl[3]; +BOOL verbose = FALSE; + +void checksectors (void); +void dump_id (void); +void dump_dcom (void); +void dump_resmon (void); +void dump_slet (void); +void dump_hdng (void); +void dump_scra (void); +void dump_let (void); +void dump_flet (void); +void dump_cib (void); +void getsector (int sec, WORD *sbuf); +void getdcyl (void); +char *lowcase (char *str); + +void bail(char *fmt, ...); +char *trim (char *s); + +int main (int argc, char **argv) +{ + char *fname = NULL, *arg; + static char usestr[] = "Usage: diskview [-v] filename"; + int i; + + for (i = 1; i < argc;) { + arg = argv[i++]; + if (*arg == '-') { + arg++; + lowcase(arg); + while (*arg) { + switch (*arg++) { + case 'v': + verbose = TRUE; + break; + + default: + bail(usestr); + } + } + } + else if (fname == NULL) + fname = arg; + else + bail(usestr); + } + + if (fname == NULL) + bail(usestr); + + if ((fp = fopen(fname, "rb")) == NULL) { + perror(fname); + return 2; + } + + printf("%s:\n", fname); + + checksectors(); + getdcyl(); + + dump_id(); // ID & coldstart + dump_dcom(); // DCOM + dump_resmon(); // resident image + dump_slet(); // SLET + dump_hdng(); // heading sector + dump_scra(); + dump_flet(); + dump_cib(); + dump_let(); + + fclose(fp); + return 0; +} + +// checksectors - verify that all sectors are properly numbered + +void checksectors () +{ + WORD sec = 0; + + fseek(fp, 0, SEEK_SET); + + for (sec = 0; sec < DSK_NUMCY*DSK_SECCYL; sec++) { + if (fxread(buf, sizeof(WORD), DSK_NUMWD, fp) != DSK_NUMWD) + bail("File read error or not a disk image file"); + + if (buf[0] != sec) + bail("Sector /%x is misnumbered, run checkdisk [-f]", sec); + } +} + +// get defective cylinder list + +void getdcyl (void) +{ + fseek(fp, sizeof(WORD), SEEK_SET); // skip sector count + if (fxread(dcyl, sizeof(WORD), 3, fp) != 3) + bail("Unable to read defective cylinder table"); +} + +// getsector - read specified absolute sector + +void getsector (int sec, WORD *sbuf) +{ + int i, cyl, ssec; + + sec &= 0x7FF; // mask of drive bits, if any + + cyl = sec / DSK_SECCYL; // get cylinder + ssec = sec & ~(DSK_SECCYL-1); // mask to get starting sector of cylinder + for (i = 0; i < 3; i++) { // map through defective cylinder table + if (dcyl[i] == ssec) { + sec &= (DSK_SECCYL-1); // mask to get base sector + cyl = DSK_NUMCY-3+i; // replacements are last three on disk + sec += cyl*DSK_SECCYL; // add new cylinder offset + break; + } + } + // read the sector + if (fseek(fp, (sec*DSK_NUMWD+1)*sizeof(WORD), SEEK_SET) != 0) + bail("File seek failed"); + + if (fxread(sbuf, sizeof(WORD), DSK_NUMWD, fp) != DSK_NUMWD) + bail("File read error or not a disk image file"); +} + +void dump (int nwords) +{ + int i, nline = 0; + + for (i = 0; i < nwords; i++) { + if (nline == 16) { + putchar('\n'); + nline = 0; + } + + printf("%04x", buf[i]); + nline++; + } + putchar('\n'); +} + +void showmajor (char *label) +{ + int i; + + printf("\n--- %s ", label); + + for (i = strlen(label); i < 40; i++) + putchar('-'); + + putchar('\n'); + putchar('\n'); +} + +void name (char *label) +{ + printf("%-32.32s ", label); +} + +void pbf (char *label, WORD *buf, int nwords) +{ + int i, nout; + + name(label); + + for (i = nout = 0; i < nwords; i++, nout++) { + if (nout == 8) { + putchar('\n'); + name(""); + nout = 0; + } + printf(" %04x", buf[i]); + } + + putchar('\n'); +} + +void prt (char *label, char *fmt, ...) +{ + va_list args; + + name(label); + + putchar(' '); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + putchar('\n'); +} + +void dump_id (void) +{ + showmajor("Sector 0 - ID & coldstart"); + getsector(0, buf); + + pbf("DCYL def cyl table", buf+ 0, 3); + pbf("CIDN cart id", buf+ 3, 1); + pbf(" copy code", buf+ 4, 1); + pbf("DTYP disk type", buf+ 7, 1); + pbf(" diskz copy", buf+ 30, 8); + pbf(" cold start pgm",buf+270, 8); +} + +// EQUIVALENCES FOR DCOM PARAMETERS +#define NAME 4 // NAME OF PROGRAM/CORE LOAD +#define DBCT 6 // BLOCK CT OF PROGRAM/CORE LOAD +#define FCNT 7 // FILES SWITCH +#define SYSC 8 // SYSTEM/NON-SYSTEM CARTRIDGE INDR +#define JBSW 9 // JOBT SWITCH +#define CBSW 10 // CLB-RETURN SWITCH +#define LCNT 11 // NO. OF LOCALS +#define MPSW 12 // CORE MAP SWITCH +#define MDF1 13 // NO. DUP CTRL RECORDS (MODIF) +#define MDF2 14 // ADDR OF MODIF BUFFER +#define NCNT 15 // NO. OF NOCALS +#define ENTY 16 // RLTV ENTRY ADDR OF PROGRAM +#define RP67 17 // 1442-5 SWITCH +#define TODR 18 // OBJECT WORK STORAGE DRIVE CODE +#define FHOL 20 // ADDR LARGEST HOLE IN FIXED AREA +#define FSZE 21 // BLK CNT LARGEST HOLE IN FXA +#define UHOL 22 // ADDR LAST HOLE IN USER AREA 2-10 +#define USZE 23 // BLK CNT LAST HOLE IN UA 2-10 +#define DCSW 24 // DUP CALL SWITCH +#define PIOD 25 // PRINCIPAL I/O DEVICE INDICATOR +#define PPTR 26 // PRINCIPAL PRINT DEVICE INDICATOR +#define CIAD 27 // RLTV ADDR IN @STRT OF CIL ADDR +#define ACIN 28 // AVAILABLE CARTRIDGE INDICATOR +#define GRPH 29 // 2250 INDICATOR 2G2 +#define GCNT 30 // NO. G2250 RECORDS 2G2 +#define LOSW 31 // LOCAL-CALLS-LOCAL SWITCH 2-2 +#define X3SW 32 // SPECIAL ILS SWITCH 2-2 +#define ECNT 33 // NO. OF *EQUAT RCDS 2-4 +#define ANDU 35 // 1+BLK ADDR END OF UA (ADJUSTED) +#define BNDU 40 // 1+BLK ADDR END OF UA (BASE) +#define FPAD 45 // FILE PROTECT ADDR +#define PCID 50 // CARTRIDGE ID, PHYSICAL DRIVE +#define CIDN 55 // CARTRIDGE ID, LOGICAL DRIVE +#define CIBA 60 // SCTR ADDR OF CIB +#define SCRA 65 // SCTR ADDR OF SCRA +#define FMAT 70 // FORMAT OF PROG IN WORKING STG +#define FLET 75 // SCTR ADDR 1ST SCTR OF FLET +#define ULET 80 // SCTR ADDR 1ST SCTR OF LET +#define WSCT 85 // BLK CNT OF PROG IN WORKING STG +#define CSHN 90 // NO. SCTRS IN CUSHION AREA + +struct tag_dcominfo { + char *nm; + int offset; + char *descr; +} dcominfo[] = { + "NAME", 4, "NAME OF PROGRAM/CORE LOAD", + "DBCT", 6, "BLOCK CT OF PROGRAM/CORE LOAD", + "FCNT", 7, "FILES SWITCH", + "SYSC", 8, "SYSTEM/NON-SYSTEM CARTRIDGE INDR", + "JBSW", 9, "JOBT SWITCH", + "CBSW", 10, "CLB-RETURN SWITCH", + "LCNT", 11, "NO. OF LOCALS", + "MPSW", 12, "CORE MAP SWITCH", + "MDF1", 13, "NO. DUP CTRL RECORDS (MODIF)", + "MDF2", 14, "ADDR OF MODIF BUFFER", + "NCNT", 15, "NO. OF NOCALS", + "ENTY", 16, "RLTV ENTRY ADDR OF PROGRAM", + "RP67", 17, "1442-5 SWITCH", + "TODR", 18, "OBJECT WORK STORAGE DRIVE CODE", + "FHOL", 20, "ADDR LARGEST HOLE IN FIXED AREA", + "FSZE", 21, "BLK CNT LARGEST HOLE IN FXA", + "UHOL", 22, "ADDR LAST HOLE IN USER AREA", + "USZE", 23, "BLK CNT LAST HOLE IN UA", + "DCSW", 24, "DUP CALL SWITCH", + "PIOD", 25, "PRINCIPAL I/O DEVICE INDICATOR", + "PPTR", 26, "PRINCIPAL PRINT DEVICE INDICATOR", + "CIAD", 27, "RLTV ADDR IN @STRT OF CIL ADDR", + "ACIN", 28, "AVAILABLE CARTRIDGE INDICATOR", + "GRPH", 29, "2250 INDICATOR", + "GCNT", 30, "NO. G2250 RECORDS", + "LOSW", 31, "LOCAL-CALLS-LOCAL SWITCH", + "X3SW", 32, "SPECIAL ILS SWITCH", + "ECNT", 33, "NO. OF *EQUAT RCDS", + "ANDU", 35, "1+BLK ADDR END OF UA (ADJUSTED)", + "BNDU", 40, "1+BLK ADDR END OF UA (BASE)", + "FPAD", 45, "FILE PROTECT ADDR", + "PCID", 50, "CARTRIDGE ID, PHYSICAL DRIVE", + "CIDN", 55, "CARTRIDGE ID, LOGICAL DRIVE", + "CIBA", 60, "SCTR ADDR OF CIB", + "SCRA", 65, "SCTR ADDR OF SCRA", + "FMAT", 70, "FORMAT OF PROG IN WORKING STG", + "FLET", 75, "SCTR ADDR 1ST SCTR OF FLET", + "ULET", 80, "SCTR ADDR 1ST SCTR OF LET", + "WSCT", 85, "BLK CNT OF PROG IN WORKING STG", + "CSHN", 90, "NO. SCTRS IN CUSHION AREA", + NULL +}; + +void dump_dcom (void) +{ + struct tag_dcominfo *d; + char txt[50]; + + showmajor("Sector 1 - DCOM"); + getsector(1, dcom); + + for (d = dcominfo; d->nm != NULL; d++) { + sprintf(txt, "%-4.4s %s", d->nm, d->descr); + pbf(txt, dcom+d->offset, 1); + } +} + +void dump_resmon (void) +{ + showmajor("Sector 2 - Resident Image"); + getsector(2, buf); + dump(verbose ? SECLEN : 32); +} + +struct { + int pfrom, pto; + int printed; + char *name; +} sletinfo[] = { + 0x01, 0x12, FALSE, "DUP", + 0x1F, 0x39, FALSE, "Fortran", + 0x51, 0x5C, FALSE, "Cobol", + 0x6E, 0x74, FALSE, "Supervisor", + 0x78, 0x84, FALSE, "Core Load Builder", + 0x8C, 0x8C, FALSE, "Sys 1403 prt", + 0x8D, 0x8D, FALSE, "Sys 1132 prt", + 0x8E, 0x8E, FALSE, "Sys console prt", + 0x8F, 0x8F, FALSE, "Sys 2501 rdr", + 0x90, 0x90, FALSE, "Sys 1442 rdr/pun", + 0x91, 0x91, FALSE, "Sys 1134 paper tape", + 0x92, 0x92, FALSE, "Sys kbd", + 0x93, 0x93, FALSE, "Sys 2501/1442 conv", + 0x94, 0x94, FALSE, "Sys 1134 conv", + 0x95, 0x95, FALSE, "Sys kbd conv", + 0x96, 0x96, FALSE, "Sys diskz", + 0x97, 0x97, FALSE, "Sys disk1", + 0x98, 0x98, FALSE, "Sys diskn", + 0x99, 0x99, FALSE, "(primary print)", + 0x9A, 0x9A, FALSE, "(primary input)", + 0x9B, 0x9B, FALSE, "(primary input excl kbd)", + 0x9C, 0x9C, FALSE, "(primary sys conv)", + 0x9D, 0x9D, FALSE, "(primary conv excl kbd)", + 0xA0, 0xA1, FALSE, "Core Image Loader", + 0xB0, 0xCC, FALSE, "RPG", + 0xCD, 0xCE, FALSE, "Dup Part 2", + 0xCF, 0xF6, FALSE, "Macro Assembler", + 0 +}; + +void dump_slet (void) +{ + int i, j, iphase, nsecs, sec, max_sec = 0; + char sstr[16], *smark; + + showmajor("Sectors 3-5 - SLET"); + for (i = 0; i < 3; i++) { + getsector(3+i, buf); + memmove(((WORD *) slet)+SECLEN*i, buf, SECLEN*sizeof(WORD)); + } + + printf("# PHID Addr Len Sector Secs\n"); + printf("------------------------------------------\n"); + for (i = 0; i < SLETLEN; i++) { + if (slet[i].phid == 0) + break; + + sec = slet[i].sector; + iphase = (int) (signed short) slet[i].phid; + nsecs = (slet[i].nwords + SECLEN-1)/SECLEN; + + if (sec & 0xF800) { + smark = "*"; + sec &= 0x7FF; + } + else + smark = " "; + + for (j = 0; sletinfo[j].pfrom != 0; j++) + if (sletinfo[j].pfrom <= iphase && sletinfo[j].pto >= iphase) + break; + + sprintf(sstr, "(%d.%d)", sec / DSK_SECCYL, slet[i].sector % DSK_SECCYL); + + printf("%3d %04x %4d %04x %04x %04x %s %-7s %3x", + i, slet[i].phid, iphase, slet[i].addr, slet[i].nwords, slet[i].sector, smark, sstr, nsecs); + + if (iphase < 0) + iphase = -iphase; + + if (sletinfo[j].pfrom == 0) + printf(" ???"); + else if (! sletinfo[j].printed) { + printf(" %s", sletinfo[j].name); + sletinfo[j].printed = TRUE; + } + + for (j = 0; j < i; j++) { + if (sec == (slet[j].sector & 0x7FF)) { + printf(" (same as %04x)", slet[j].phid); + break; + } + } + + max_sec = MAX(max_sec, sec+nsecs-1); // find last sector used + + putchar('\n'); + + if (i >= 15 && ! verbose) { + printf("...\n"); + break; + } + } +} + +int ascii_to_ebcdic_table[128] = +{ + 0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f, + 0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f, + 0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61, + 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f, + + 0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6, + 0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d, + 0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96, + 0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07, +}; + +int ebcdic_to_ascii (int ch) +{ + int j; + + for (j = 32; j < 128; j++) + if (ascii_to_ebcdic_table[j] == ch) + return j; + + return '?'; +} + +#define HDR_LEN 120 + +void dump_hdng(void) +{ + int i; + char str[HDR_LEN+1], *p = str; + + showmajor("Sector 7 - Heading"); + getsector(7, buf); + + for (i = 0; i < (HDR_LEN/2); i++) { + *p++ = ebcdic_to_ascii((buf[i] >> 8) & 0xFF); + *p++ = ebcdic_to_ascii( buf[i] & 0xFF); + } + + *p = '\0'; + trim(str); + printf("%s\n", str); +} + +BOOL mget (int offset, char *name) +{ + char title[80]; + + if (dcom[offset] == 0) + return FALSE; + + getsector(dcom[offset], buf); + sprintf(title, "Sector %x - %s", dcom[offset], name); + showmajor(title); + return TRUE; +} + +void dump_scra (void) +{ + if (! mget(SCRA, "SCRA")) + return; + + dump(verbose ? SECLEN : 32); +} + +void dump_let (void) +{ + if (! mget(ULET, "LET")) + return; +} + +void dump_flet (void) +{ + if (! mget(FLET, "FLET")) + return; +} + +void dump_cib (void) +{ + if (! mget(CIBA, "CIB")) + return; + + dump(verbose ? SECLEN : 32); +} + +#define LFHD 5 // WORD COUNT OF LET/FLET HEADER PMN09970 +#define LFEN 3 // NO OF WDS PER LET/FLET ENTRY PMN09980 +#define SCTN 0 // RLTY ADDR OF LET/FLET SCTR NO. PMN09990 +#define UAFX 1 // RLTV ADDR OF SCTR ADDR OF UA/FXA PMN10000 +#define WDSA 3 // RLTV ADDR OF WDS AVAIL IN SCTR PMN10010 +#define NEXT 4 // RLTV ADDR OF ADDR NEXT SCTR PMN10020 +#define LFNM 0 // RLTV ADDR OF LET/FLET ENTRY NAME PMN10030 +#define BLCT 2 // RLTV ADDR OF LET/FLET ENTRY DBCT PMN10040 + +void bail (char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + fprintf(stderr, fmt, args); + va_end(args); + putchar('\n'); + + exit(1); +} + +// --------------------------------------------------------------------------------- +// trim - remove trailing whitespace from string s +// --------------------------------------------------------------------------------- + +char *trim (char *s) +{ + char *os = s, *nb; + + for (nb = s-1; *s; s++) + if (*s > ' ') + nb = s; + + nb[1] = '\0'; + return os; +} + +/* ------------------------------------------------------------------------ + * lowcase - force a string to lowercase (ASCII) + * ------------------------------------------------------------------------ */ + +char *lowcase (char *str) +{ + char *s; + + for (s = str; *s; s++) { + if (*s >= 'A' && *s <= 'Z') + *s += 32; + } + + return str; +} + diff --git a/Ibm1130/utils/diskview.mak b/Ibm1130/utils/diskview.mak new file mode 100644 index 0000000..ef9a8cf --- /dev/null +++ b/Ibm1130/utils/diskview.mak @@ -0,0 +1,175 @@ +# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Win32 Debug +!MESSAGE No configuration specified. Defaulting to Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "diskview.mak" CFG="Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Win32 Debug" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "WinRel" +# PROP BASE Intermediate_Dir "WinRel" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "WinRel" +# PROP Intermediate_Dir "WinRel" +OUTDIR=.\WinRel +INTDIR=.\WinRel + +ALL : $(OUTDIR)/diskview.exe $(OUTDIR)/diskview.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"diskview.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinRel/ +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"diskview.bsc" +BSC32_SBRS= \ + $(INTDIR)/diskview.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/diskview.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console\ + /INCREMENTAL:no /PDB:$(OUTDIR)/"diskview.pdb" /MACHINE:I386\ + /OUT:$(OUTDIR)/"diskview.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/diskview.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/diskview.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "WinDebug" +# PROP BASE Intermediate_Dir "WinDebug" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "WinDebug" +# PROP Intermediate_Dir "WinDebug" +OUTDIR=.\WinDebug +INTDIR=.\WinDebug + +ALL : $(OUTDIR)/diskview.exe $(OUTDIR)/diskview.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"diskview.pch" /Fo$(INTDIR)/\ + /Fd$(OUTDIR)/"diskview.pdb" /c +CPP_OBJS=.\WinDebug/ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"diskview.bsc" +BSC32_SBRS= \ + $(INTDIR)/diskview.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/diskview.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console\ + /INCREMENTAL:yes /PDB:$(OUTDIR)/"diskview.pdb" /DEBUG /MACHINE:I386\ + /OUT:$(OUTDIR)/"diskview.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/diskview.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/diskview.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Group "Source Files" + +################################################################################ +# Begin Source File + +SOURCE=.\diskview.c + +$(INTDIR)/diskview.obj : $(SOURCE) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\util_io.c +DEP_UTIL_=\ + .\util_io.h + +$(INTDIR)/util_io.obj : $(SOURCE) $(DEP_UTIL_) $(INTDIR) + +# End Source File +# End Group +# End Project +################################################################################ diff --git a/Ibm1130/utils/makefile b/Ibm1130/utils/makefile new file mode 100644 index 0000000..612b3c6 --- /dev/null +++ b/Ibm1130/utils/makefile @@ -0,0 +1,47 @@ +# (This makefile is for operating systems other than Windows, +# or compilers other than Microsoft's. For MS builds, use the +# .mak files). +# +# CC Command +# +# Note: -O2 is sometimes broken in GCC when setjump/longjump is being +# used. Try -O2 only with released simulators. +# + +CC = gcc -O0 -lm -I . +#CC = gcc -O2 -g -lm -I . + +BIN = + +IOLIB_DEP = util_io.c util_io.h +IOLIB_SRC = util_io.c + +# +# Build everything +# + +all : ${BIN}asm1130 ${BIN}bindump ${BIN}checkdisk \ + ${BIN}diskview ${BIN}mkboot ${BIN}viewdeck + +# +# Individual builds +# + +${BIN}asm1130 : asm1130.c ${IOLIB_DEP} + ${CC} asm1130.c ${IOLIB_SRC} -o $@ + +${BIN}bindump : bindump.c ${IOLIB_DEP} + ${CC} bindump.c ${IOLIB_SRC} -o $@ + +${BIN}checkdisk : checkdisk.c ${IOLIB_DEP} + ${CC} checkdisk.c ${IOLIB_SRC} -o $@ + +${BIN}diskview : diskview.c ${IOLIB_DEP} + ${CC} diskview.c ${IOLIB_SRC} -o $@ + +${BIN}mkboot : mkboot.c ${IOLIB_DEP} + ${CC} mkboot.c ${IOLIB_SRC} -o $@ + +${BIN}viewdeck : viewdeck.c ${IOLIB_DEP} + ${CC} viewdeck.c ${IOLIB_SRC} -o $@ + diff --git a/Ibm1130/utils/mkboot.c b/Ibm1130/utils/mkboot.c new file mode 100644 index 0000000..470e21f --- /dev/null +++ b/Ibm1130/utils/mkboot.c @@ -0,0 +1,706 @@ +/* + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +// --------------------------------------------------------------------------------- +// MKBOOT - reads card loader format cards and produces an absolute core image that +// can then be dumped out in 1130 IPL, 1800 IPL or Core Image loader formats. +// +// Usage: mkboot [-v] binfile outfile [1130|1800|core [loaddr [hiaddr [ident]]]]" +// +// Arguments: +// binfile - name of assembler output file (card loader format, absolute output) +// outfile - name of output file to create +// mode - output mode, default is 1130 IPL format +// loaddr - low address to dump. Default is lowest address loaded from binfile +// hiaddr - high address to dump. Defult is highest address loaded from binfile +// ident - ident string to write in last 8 columns. Omit when when writing an +// 1130 IPL card that requires all 80 columns of data. +// +// Examples: +// mkboot somefile.bin somefile.ipl 1130 +// +// loads somefile.bin, writes object in 1130 IPL format to somefile.ipl +// Up to 80 columns will be written depending on what the object actually uses +// +// mkboot somefile.bin somefile.ipl 1130 /0 /47 SOMEF +// +// loads somefile.bin. Writes 72 columns (hex 0 to hex 47), with ident columns 73-80 = SOMEF001 +// +// mkboot somefile.bin somefile.dat core 0 0 SOMEF001 +// +// loads somefile.bin and writes a core image format deck with ident SOMEF001, SOMEF002, etc +// +// For other examples of usage, see MKDMS.BAT +// +// 1.00 - 2002Apr18 - first release. Tested only under Win32. The core image +// loader format is almost certainly wrong. Cannot handle +// relocatable input decks, but it works well enough to +// load DSYSLDR1 which is what we are after here. +// --------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include "util_io.h" + +#ifndef TRUE + #define BOOL int + #define TRUE 1 + #define FALSE 0 +#endif + +#ifndef _WIN32 + int strnicmp (char *a, char *b, int n); + int strcmpi (char *a, char *b); +#endif + +#define BETWEEN(v,a,b) (((v) >= (a)) && ((v) <= (b))) +#define MIN(a,b) (((a) <= (b)) ? (a) : (b)) +#define MAX(a,b) (((a) >= (b)) ? (a) : (b)) + +#define MAXADDR 4096 + +typedef enum {R_ABSOLUTE = 0, R_RELATIVE = 1, R_LIBF = 2, R_CALL = 3} RELOC; + +typedef enum {B_1130, B_1800, B_CORE} BOOTMODE; + +BOOL verbose = FALSE; +char *infile = NULL, *outfile = NULL; +BOOTMODE mode = B_1130; +int addr_from = 0, addr_to = 79; +int outcols = 0; // columns written in using card output +int maxiplcols = 80; +char cardid[9]; // characters used for IPL card ID +int pta = 0; +int load_low = 0x7FFFFFF; +int load_high = 0; +unsigned short mem[MAXADDR]; // small core! + +// mkboot - load a binary object deck into core and dump requested bytes as a boot card + +void bail (char *msg); +void verify_checksum(unsigned short *card); +char *upcase (char *str); +void unpack (unsigned short *card, unsigned short *buf); +void dump (char *fname); +void loaddata (char *fname); +void write_1130 (void); +void write_1800 (void); +void write_core (void); +void flushcard(void); +int ascii_to_hollerith (int ch); +void corecard_init (void); +void corecard_writecard (char *sbrk_text); +void corecard_writedata (void); +void corecard_flush (void); +void corecard_setorg (int neworg); +void corecard_writew (int word, RELOC relative); +void corecard_endcard (void); + +char *fname = NULL; +FILE *fout; + +int main (int argc, char **argv) +{ + char *arg; + static char usestr[] = "Usage: mkboot [-v] binfile outfile [1130|1800|core [loaddr [hiaddr [ident]]]]"; + int i, ano = 0, ok; + + for (i = 1; i < argc; i++) { + arg = argv[i]; + if (*arg == '-') { + arg++; + while (*arg) { + switch (*arg++) { + case 'v': + verbose = TRUE; + break; + default: + bail(usestr); + } + } + } + else { + switch (ano++) { + case 0: + infile = arg; + break; + + case 1: + outfile = arg; + break; + + case 2: + if (strcmp(arg, "1130") == 0) mode = B_1130; + else if (strcmp(arg, "1800") == 0) mode = B_1800; + else if (strcmpi(arg, "core") == 0) mode = B_CORE; + else bail(usestr); + break; + + case 3: + if (strnicmp(arg, "0x", 2) == 0) ok = sscanf(arg+2, "%x", &addr_from); + else if (arg[0] == '/') ok = sscanf(arg+1, "%x", &addr_from); + else ok = sscanf(arg, "%d", &addr_from); + if (ok != 1) bail(usestr); + break; + + case 4: + if (strnicmp(arg, "0x", 2) == 0) ok = sscanf(arg+2, "%x", &addr_to); + else if (arg[0] == '/') ok = sscanf(arg+1, "%x", &addr_to); + else ok = sscanf(arg, "%d", &addr_to); + if (ok != 1) bail(usestr); + break; + + case 5: + strncpy(cardid, arg, 9); + cardid[8] = '\0'; + upcase(cardid); + break; + + default: + bail(usestr); + } + } + } + + if (*cardid == '\0') + maxiplcols = (mode == B_1130) ? 80 : 72; + else { + while (strlen(cardid) < 8) + strcat(cardid, "0"); + maxiplcols = 72; + } + + loaddata(infile); + + if (mode == B_1800) + write_1800(); + else if (mode == B_CORE) + write_core(); + else + write_1130(); + + return 0; +} + +void write_1130 (void) +{ + int addr; + unsigned short word; + + if ((fout = fopen(outfile, "wb")) == NULL) { + perror(outfile); + exit(1); + } + + for (addr = addr_from; addr <= addr_to; addr++) { + if (outcols >= maxiplcols) + flushcard(); + + word = mem[addr]; + + // if F or L bits are set, or if high 2 bits of displacement are unequal, it's bad + if ((word & 0x0700) || ! (((word & 0x00C0) == 0) || ((word & 0x00C0) == 0x00C0))) + printf("Warning: word %04x @ %04x may not IPL properly\n", word & 0xFFFF, addr); + + word = ((word & 0xF800) >> 4) | (word & 0x7F); // convert to 1130 IPL format + + putc((word & 0x000F) << 4, fout); // write the 12 bits in little-endian binary AABBCC00 as CC00 AABB + putc((word & 0x0FF0) >> 4, fout); + outcols++; + } + flushcard(); + fclose(fout); +} + +void write_1800 (void) +{ + int addr; + unsigned short word; + + if ((fout = fopen(outfile, "wb")) == NULL) { + perror(outfile); + exit(1); + } + + for (addr = addr_from; addr <= addr_to; addr++) { + word = mem[addr]; + + if (outcols >= maxiplcols) + flushcard(); + + putc(0, fout); + putc(word & 0xFF, fout); // write the low 8 bits in little-endian binary + outcols++; + + putc(0, fout); + putc((word >> 8) & 0xFF, fout); // write the high 8 bits in little-endian binary + outcols++; + } + flushcard(); + fclose(fout); +} + +void write_core (void) +{ + int addr; + + if ((fout = fopen(outfile, "wb")) == NULL) { + perror(outfile); + exit(1); + } + + addr_from = load_low; + addr_to = load_high; + + maxiplcols = 72; + corecard_init(); + corecard_setorg(addr_from); + + for (addr = addr_from; addr <= addr_to; addr++) { + corecard_writew(mem[addr], 0); + } + + corecard_flush(); + corecard_endcard(); + fclose(fout); +} + +void flushcard (void) +{ + int i, hol, ndig; + char fmt[20], newdig[20]; + + if (outcols <= 0) + return; // nothing to flush + + while (outcols < maxiplcols) { // pad to required number of columns with blanks (no punches) + putc(0, fout); + putc(0, fout); + outcols++; + } + + if (*cardid) { // add label + for (i = 0; i < 8; i++) { // write label as specified + hol = ascii_to_hollerith(cardid[i] & 0x7F); + putc(hol & 0xFF, fout); + putc((hol >> 8) & 0xFF, fout); + } + + ndig = 0; // count trailing digits in the label + for (i = 8; --i >= 0; ndig++) + if (! isdigit(cardid[i])) + break; + + i++; // index of first digit in trailing sequence + + if (ndig > 0) { // if any, increment them + sprintf(fmt, "%%0%dd", ndig); // make, e.g. %03d + sprintf(newdig, fmt, atoi(cardid+i)+1); + newdig[ndig] = '\0'; // clip if necessary + strcpy(cardid+i, newdig); // replace for next card's sequence number + } + } + + outcols = 0; +} + +void show_data (unsigned short *buf) +{ + int i, n, jrel, rflag, nout, ch, reloc; + + n = buf[2] & 0x00FF; + + printf("%04x: ", buf[0]); + + jrel = 3; + nout = 0; + rflag = buf[jrel++]; + for (i = 0; i < n; i++) { + if (nout >= 8) { + rflag = buf[jrel++]; + putchar('\n'); + printf(" "); + nout = 0; + } + reloc = (rflag >> 14) & 0x03; + ch = (reloc == R_ABSOLUTE) ? ' ' : + (reloc == R_RELATIVE) ? 'R' : + (reloc == R_LIBF) ? 'L' : '@'; + + printf("%04x%c ", buf[9+i], ch); + rflag <<= 2; + nout++; + } + putchar('\n'); +} + +void loadcard (unsigned short *buf) +{ + int addr, n, i; + + addr = buf[0]; + n = buf[2] & 0x00FF; + + for (i = 0; i < n; i++) { + if (addr >= MAXADDR) + bail("Program doesn't fit into 4K"); + mem[addr] = buf[9+i]; + + load_low = MIN(addr, load_low); + load_high = MAX(addr, load_high); + addr++; + } +} + +void loaddata (char *fname) +{ + FILE *fp; + BOOL first = TRUE; + unsigned short card[80], buf[54], cardtype; + + if ((fp = fopen(fname, "rb")) == NULL) { + perror(fname); + exit(1); + } + + if (verbose) + printf("\n%s:\n", fname); + + while (fxread(card, sizeof(card[0]), 80, fp) > 0) { + unpack(card, buf); + verify_checksum(card); + + cardtype = (buf[2] >> 8) & 0xFF; + + if (cardtype == 1 && ! first) { // sector break + if (verbose) + printf("*SBRK\n"); + continue; + } + else { + switch (cardtype) { + case 0x01: + if (verbose) + printf("*ABS\n"); + break; + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + bail("Data must be in absolute format"); + break; + + case 0x0F: + pta = buf[3]; // save program transfer address + if (verbose) + printf("*END\n"); + break; + + case 0x0A: + if (verbose) + show_data(buf); + loadcard(buf); + break; + default: + bail("Unexpected card type"); + } + } + first = FALSE; + } + + fclose(fp); +} + +void bail (char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(1); +} + +void unpack (unsigned short *card, unsigned short *buf) +{ + int i, j; + unsigned short wd1, wd2, wd3, wd4; + + for (i = j = 0; i < 54; i += 3, j += 4) { + wd1 = card[j]; + wd2 = card[j+1]; + wd3 = card[j+2]; + wd4 = card[j+3]; + + buf[i ] = (wd1 & 0xFFF0) | ((wd2 >> 12) & 0x000F); + buf[i+1] = ((wd2 << 4) & 0xFF00) | ((wd3 >> 8) & 0x00FF); + buf[i+2] = ((wd3 << 8) & 0xF000) | ((wd4 >> 4) & 0x0FFF); + } +} + +void verify_checksum (unsigned short *card) +{ +// unsigned short sum; + + if (card[1] == 0) // no checksum + return; + +// if (sum != card[1]) +// printf("Checksum %04x doesn't match card %04x\n", sum, card[1]); +} + +typedef struct { + int hollerith; + char ascii; +} CPCODE; + +static CPCODE cardcode_029[] = +{ + 0x0000, ' ', + 0x8000, '&', // + in 026 Fortran + 0x4000, '-', + 0x2000, '0', + 0x1000, '1', + 0x0800, '2', + 0x0400, '3', + 0x0200, '4', + 0x0100, '5', + 0x0080, '6', + 0x0040, '7', + 0x0020, '8', + 0x0010, '9', + 0x9000, 'A', + 0x8800, 'B', + 0x8400, 'C', + 0x8200, 'D', + 0x8100, 'E', + 0x8080, 'F', + 0x8040, 'G', + 0x8020, 'H', + 0x8010, 'I', + 0x5000, 'J', + 0x4800, 'K', + 0x4400, 'L', + 0x4200, 'M', + 0x4100, 'N', + 0x4080, 'O', + 0x4040, 'P', + 0x4020, 'Q', + 0x4010, 'R', + 0x3000, '/', + 0x2800, 'S', + 0x2400, 'T', + 0x2200, 'U', + 0x2100, 'V', + 0x2080, 'W', + 0x2040, 'X', + 0x2020, 'Y', + 0x2010, 'Z', + 0x0820, ':', + 0x0420, '#', // = in 026 Fortran + 0x0220, '@', // ' in 026 Fortran + 0x0120, '\'', + 0x00A0, '=', + 0x0060, '"', + 0x8820, 'c', // cent + 0x8420, '.', + 0x8220, '<', // ) in 026 Fortran + 0x8120, '(', + 0x80A0, '+', + 0x8060, '|', + 0x4820, '!', + 0x4420, '$', + 0x4220, '*', + 0x4120, ')', + 0x40A0, ';', + 0x4060, 'n', // not + 0x2820, 'x', // what? + 0x2420, ',', + 0x2220, '%', // ( in 026 Fortran + 0x2120, '_', + 0x20A0, '>', + 0x2060, '>', +}; + +int ascii_to_hollerith (int ch) +{ + int i; + + for (i = 0; i < sizeof(cardcode_029) / sizeof(CPCODE); i++) + if (cardcode_029[i].ascii == ch) + return cardcode_029[i].hollerith; + + return 0; +} + +// --------------------------------------------------------------------------------- +// corecard - routines to write IBM 1130 Card object format +// --------------------------------------------------------------------------------- + +unsigned short corecard[54]; // the 54 data words that can fit on a binary format card +int corecard_n = 0; // number of object words stored in corecard (0-45) +int corecard_seq = 1; // card output sequence number +int corecard_org = 0; // origin of current card-full +int corecard_maxaddr = 0; +BOOL corecard_first = TRUE; // TRUE when we're to write the program type card + +// corecard_init - prepare a new object data output card + +void corecard_init (void) +{ + memset(corecard, 0, sizeof(corecard)); // clear card data + corecard_n = 0; // no data + corecard[0] = corecard_org; // store load address + corecard_maxaddr = MAX(corecard_maxaddr, corecard_org-1); // save highest address written-to (this may be a BSS) +} + +// binard_writecard - emit a card. sbrk_text = NULL for normal data cards, points to comment text for sbrk card + +void corecard_writecard (char *sbrk_text) +{ + unsigned short binout[80]; + int i, j; + + for (i = j = 0; i < 54; i += 3, j += 4) { + binout[j ] = ( corecard[i] & 0xFFF0); + binout[j+1] = ((corecard[i] << 12) & 0xF000) | ((corecard[i+1] >> 4) & 0x0FF0); + binout[j+2] = ((corecard[i+1] << 8) & 0xFF00) | ((corecard[i+2] >> 8) & 0x00F0); + binout[j+3] = ((corecard[i+2] << 4) & 0xFFF0); + } + + for (i = 0; i < 72; i++) { + putc(binout[i] & 0xFF, fout); + putc((binout[i] >> 8) & 0xFF, fout); + } + + outcols = 72; // add the ident + flushcard(); +} + +// binard_writedata - emit an object data card + +void corecard_writedata (void) +{ + corecard[1] = 0; // checksum + corecard[2] = 0x0000 | corecard_n; // data card type + word count + corecard_writecard(FALSE); // emit the card +} + +// corecard_flush - flush any pending binary data + +void corecard_flush (void) +{ + if (corecard_n > 0) + corecard_writedata(); + + corecard_init(); +} + +// corecard_setorg - set the origin + +void corecard_setorg (int neworg) +{ + corecard_org = neworg; // set origin for next card + corecard_flush(); // flush any current data & store origin +} + +// corecard_writew - write a word to the current output card. + +void corecard_writew (int word, RELOC relative) +{ + if (corecard_n >= 50) // flush full card buffer (must be even) + corecard_flush(); + + corecard[3+corecard_n++] = word; + corecard_org++; +} + +// corecard_endcard - write end of program card + +void corecard_endcard (void) +{ + corecard_flush(); + + corecard[0] = 0; // effective length: add 1 to max origin, then 1 more to round up + corecard[1] = 0; + corecard[2] = 0x8000; // they look for negative bit but all else must be zero + corecard[52] = 0xabcd; // index register 3 value, this is for fun + corecard[53] = pta; // hmmm + + corecard_writecard(NULL); +} + +/* ------------------------------------------------------------------------ + * upcase - force a string to uppercase (ASCII) + * ------------------------------------------------------------------------ */ + +char *upcase (char *str) +{ + char *s; + + for (s = str; *s; s++) { + if (*s >= 'a' && *s <= 'z') + *s -= 32; + } + + return str; +} + +#ifndef _WIN32 + +int strnicmp (char *a, char *b, int n) +{ + int ca, cb; + + for (;;) { + if (--n < 0) // still equal after n characters? quit now + return 0; + + if ((ca = *a) == 0) // get character, stop on null terminator + return *b ? -1 : 0; + + if (ca >= 'a' && ca <= 'z') // fold lowercase to uppercase + ca -= 32; + + cb = *b; + if (cb >= 'a' && cb <= 'z') + cb -= 32; + + if ((ca -= cb) != 0) // if different, return comparison + return ca; + + a++, b++; + } +} + +int strcmpi (char *a, char *b) +{ + int ca, cb; + + for (;;) { + if ((ca = *a) == 0) // get character, stop on null terminator + return *b ? -1 : 0; + + if (ca >= 'a' && ca <= 'z') // fold lowercase to uppercase + ca -= 32; + + cb = *b; + if (cb >= 'a' && cb <= 'z') + cb -= 32; + + if ((ca -= cb) != 0) // if different, return comparison + return ca; + + a++, b++; + } +} + +#endif diff --git a/Ibm1130/utils/mkboot.mak b/Ibm1130/utils/mkboot.mak new file mode 100644 index 0000000..1571536 --- /dev/null +++ b/Ibm1130/utils/mkboot.mak @@ -0,0 +1,175 @@ +# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Win32 Debug +!MESSAGE No configuration specified. Defaulting to Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "mkboot.mak" CFG="Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Win32 Debug" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "WinRel" +# PROP BASE Intermediate_Dir "WinRel" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "WinRel" +# PROP Intermediate_Dir "WinRel" +OUTDIR=.\WinRel +INTDIR=.\WinRel + +ALL : $(OUTDIR)/mkboot.exe $(OUTDIR)/mkboot.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"mkboot.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinRel/ +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"mkboot.bsc" +BSC32_SBRS= \ + $(INTDIR)/mkboot.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/mkboot.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console\ + /INCREMENTAL:no /PDB:$(OUTDIR)/"mkboot.pdb" /MACHINE:I386\ + /OUT:$(OUTDIR)/"mkboot.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/mkboot.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/mkboot.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "WinDebug" +# PROP BASE Intermediate_Dir "WinDebug" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "WinDebug" +# PROP Intermediate_Dir "WinDebug" +OUTDIR=.\WinDebug +INTDIR=.\WinDebug + +ALL : $(OUTDIR)/mkboot.exe $(OUTDIR)/mkboot.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"mkboot.pch" /Fo$(INTDIR)/ /Fd$(OUTDIR)/"mkboot.pdb"\ + /c +CPP_OBJS=.\WinDebug/ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"mkboot.bsc" +BSC32_SBRS= \ + $(INTDIR)/mkboot.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/mkboot.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib /NOLOGO /SUBSYSTEM:console\ + /INCREMENTAL:yes /PDB:$(OUTDIR)/"mkboot.pdb" /DEBUG /MACHINE:I386\ + /OUT:$(OUTDIR)/"mkboot.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/mkboot.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/mkboot.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Group "Source Files" + +################################################################################ +# Begin Source File + +SOURCE=.\mkboot.c + +$(INTDIR)/mkboot.obj : $(SOURCE) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\util_io.c +DEP_UTIL_=\ + .\util_io.h + +$(INTDIR)/util_io.obj : $(SOURCE) $(DEP_UTIL_) $(INTDIR) + +# End Source File +# End Group +# End Project +################################################################################ diff --git a/Ibm1130/utils/viewdeck.c b/Ibm1130/utils/viewdeck.c new file mode 100644 index 0000000..894d7ae --- /dev/null +++ b/Ibm1130/utils/viewdeck.c @@ -0,0 +1,243 @@ +/* Simple program to display a binary card-image file in ASCII. + * We assume the deck was written with one card per 16-bit word, left-justified, + * and written in PC little-endian order + * + * (C) Copyright 2002, Brian Knittel. + * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN + * RISK basis, there is no warranty of fitness for any purpose, and the rest of the + * usual yada-yada. Please keep this notice and the copyright in any distributions + * or modifications. + * + * This is not a supported product, but I welcome bug reports and fixes. + * Mail to sim@ibm1130.org + */ + +#include +#include +#include "util_io.h" + +#define TRUE 1 +#define FALSE 0 +typedef int BOOL; + +int hollerith_to_ascii (unsigned short h); +void bail (char *msg); +void format_coldstart (unsigned short *buf); + +int main (int argc, char **argv) +{ + FILE *fd; + char *fname = NULL, line[82], *arg; + BOOL coldstart = FALSE; + unsigned short buf[80]; + int i, lastnb; + static char usestr[] = + "Usage: viewdeck [-c] deckfile\n" + "\n" + "-c: convert cold start card to 16-bit format as a C array initializer\n"; + + for (i = 1; i < argc; i++) { // process command line arguments + arg = argv[i]; + + if (*arg == '-') { + arg++; + while (*arg) { + switch (*arg++) { + case 'c': + coldstart = TRUE; + break; + default: + bail(usestr); + } + } + } + else if (fname == NULL) // first non-switch arg is file name + fname = arg; + else + bail(usestr); // there can be only one name + } + + if (fname == NULL) // there must be a name + bail(usestr); + + if ((fd = fopen(fname, "rb")) == NULL) { + perror(fname); + return 1; + } + + while (fxread(buf, sizeof(short), 80, fd) == 80) { + if (coldstart) { + format_coldstart(buf); + break; + } + + lastnb = -1; + for (i = 0; i < 80; i++) { + line[i] = hollerith_to_ascii(buf[i]); + if (line[i] > ' ') + lastnb = i; + } + line[++lastnb] = '\n'; + line[++lastnb] = '\0'; + fputs(line, stdout); + } + + if (coldstart) { + if (fxread(buf, sizeof(short), 1, fd) == 1) + bail("Coldstart deck has more than one card"); + } + + fclose(fd); + + return 0; +} + +void format_coldstart (unsigned short *buf) +{ + int i, nout = 0; + unsigned short word; + + for (i = 0; i < 80; i++) { + word = buf[i]; // expand 12-bit card data to 16-bit instruction + word = (word & 0xF800) | ((word & 0x0400) ? 0x00C0 : 0x0000) | ((word & 0x03F0) >> 4); + + if (nout >= 8) { + fputs(",\n", stdout); + nout = 0; + } + else if (i > 0) + fputs(", ", stdout); + + printf("0x%04x", word); + nout++; + } + + putchar('\n'); +} + +typedef struct { + unsigned short hollerith; + char ascii; +} CPCODE; + +static CPCODE cardcode_029[] = +{ + 0x0000, ' ', + 0x8000, '&', // + in 026 Fortran + 0x4000, '-', + 0x2000, '0', + 0x1000, '1', + 0x0800, '2', + 0x0400, '3', + 0x0200, '4', + 0x0100, '5', + 0x0080, '6', + 0x0040, '7', + 0x0020, '8', + 0x0010, '9', + 0x9000, 'A', + 0x8800, 'B', + 0x8400, 'C', + 0x8200, 'D', + 0x8100, 'E', + 0x8080, 'F', + 0x8040, 'G', + 0x8020, 'H', + 0x8010, 'I', + 0x5000, 'J', + 0x4800, 'K', + 0x4400, 'L', + 0x4200, 'M', + 0x4100, 'N', + 0x4080, 'O', + 0x4040, 'P', + 0x4020, 'Q', + 0x4010, 'R', + 0x3000, '/', + 0x2800, 'S', + 0x2400, 'T', + 0x2200, 'U', + 0x2100, 'V', + 0x2080, 'W', + 0x2040, 'X', + 0x2020, 'Y', + 0x2010, 'Z', + 0x0820, ':', + 0x0420, '#', // = in 026 Fortran + 0x0220, '@', // ' in 026 Fortran + 0x0120, '\'', + 0x00A0, '=', + 0x0060, '"', + 0x8820, '\xA2', // cent, in MS-DOS encoding + 0x8420, '.', + 0x8220, '<', // ) in 026 Fortran + 0x8120, '(', + 0x80A0, '+', + 0x8060, '|', + 0x4820, '!', + 0x4420, '$', + 0x4220, '*', + 0x4120, ')', + 0x40A0, ';', + 0x4060, '\xAC', // not, in MS-DOS encoding + 0x2420, ',', + 0x2220, '%', // ( in 026 Fortran + 0x2120, '_', + 0x20A0, '>', + 0xB000, 'a', + 0xA800, 'b', + 0xA400, 'c', + 0xA200, 'd', + 0xA100, 'e', + 0xA080, 'f', + 0xA040, 'g', + 0xA020, 'h', + 0xA010, 'i', + 0xD000, 'j', + 0xC800, 'k', + 0xC400, 'l', + 0xC200, 'm', + 0xC100, 'n', + 0xC080, 'o', + 0xC040, 'p', + 0xC020, 'q', + 0xC010, 'r', + 0x6800, 's', + 0x6400, 't', + 0x6200, 'u', + 0x6100, 'v', + 0x6080, 'w', + 0x6040, 'x', + 0x6020, 'y', + 0x6010, 'z', // these odd punch codes are used by APL: + 0x1010, '\001', // no corresponding ASCII using ^A + 0x0810, '\002', // SYN using ^B + 0x0410, '\003', // no corresponding ASCII using ^C + 0x0210, '\004', // PUNCH ON using ^D + 0x0110, '\005', // READER STOP using ^E + 0x0090, '\006', // UPPER CASE using ^F + 0x0050, '\013', // EOT using ^K + 0x0030, '\016', // no corresponding ASCII using ^N + 0x1030, '\017', // no corresponding ASCII using ^O + 0x0830, '\020', // no corresponding ASCII using ^P +}; + +int hollerith_to_ascii (unsigned short h) +{ + int i; + + h &= 0xFFF0; + + for (i = 0; i < sizeof(cardcode_029) / sizeof(CPCODE); i++) + if (cardcode_029[i].hollerith == h) + return cardcode_029[i].ascii; + + return '?'; +} + +void bail (char *msg) +{ + fprintf(stderr, "%s\n", msg); + exit(1); +} + diff --git a/Ibm1130/utils/viewdeck.mak b/Ibm1130/utils/viewdeck.mak new file mode 100644 index 0000000..3ee6f6a --- /dev/null +++ b/Ibm1130/utils/viewdeck.mak @@ -0,0 +1,176 @@ +# Microsoft Visual C++ Generated NMAKE File, Format Version 2.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +!IF "$(CFG)" == "" +CFG=Win32 Debug +!MESSAGE No configuration specified. Defaulting to Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "Win32 Release" && "$(CFG)" != "Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE on this makefile +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "viewdeck.mak" CFG="Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +################################################################################ +# Begin Project +# PROP Target_Last_Scanned "Win32 Debug" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "WinRel" +# PROP BASE Intermediate_Dir "WinRel" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "WinRel" +# PROP Intermediate_Dir "WinRel" +OUTDIR=.\WinRel +INTDIR=.\WinRel + +ALL : $(OUTDIR)/viewdeck.exe $(OUTDIR)/viewdeck.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /YX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"viewdeck.pch" /Fo$(INTDIR)/ /c +CPP_OBJS=.\WinRel/ +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"viewdeck.bsc" +BSC32_SBRS= \ + $(INTDIR)/viewdeck.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/viewdeck.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib /NOLOGO /SUBSYSTEM:console /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no\ + /PDB:$(OUTDIR)/"viewdeck.pdb" /MACHINE:I386 /OUT:$(OUTDIR)/"viewdeck.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/viewdeck.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/viewdeck.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ELSEIF "$(CFG)" == "Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "WinDebug" +# PROP BASE Intermediate_Dir "WinDebug" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "WinDebug" +# PROP Intermediate_Dir "WinDebug" +OUTDIR=.\WinDebug +INTDIR=.\WinDebug + +ALL : $(OUTDIR)/viewdeck.exe $(OUTDIR)/viewdeck.bsc + +$(OUTDIR) : + if not exist $(OUTDIR)/nul mkdir $(OUTDIR) + +# ADD BASE CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +# ADD CPP /nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /FR /c +CPP_PROJ=/nologo /W3 /GX /Zi /YX /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE"\ + /FR$(INTDIR)/ /Fp$(OUTDIR)/"viewdeck.pch" /Fo$(INTDIR)/\ + /Fd$(OUTDIR)/"viewdeck.pdb" /c +CPP_OBJS=.\WinDebug/ +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +BSC32_FLAGS=/nologo /o$(OUTDIR)/"viewdeck.bsc" +BSC32_SBRS= \ + $(INTDIR)/viewdeck.sbr \ + $(INTDIR)/util_io.sbr + +$(OUTDIR)/viewdeck.bsc : $(OUTDIR) $(BSC32_SBRS) + $(BSC32) @<< + $(BSC32_FLAGS) $(BSC32_SBRS) +<< + +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib /NOLOGO /SUBSYSTEM:console /DEBUG /MACHINE:I386 +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\ + advapi32.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes\ + /PDB:$(OUTDIR)/"viewdeck.pdb" /DEBUG /MACHINE:I386\ + /OUT:$(OUTDIR)/"viewdeck.exe" +DEF_FILE= +LINK32_OBJS= \ + $(INTDIR)/viewdeck.obj \ + $(INTDIR)/util_io.obj + +$(OUTDIR)/viewdeck.exe : $(OUTDIR) $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +!ENDIF + +.c{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cpp{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +.cxx{$(CPP_OBJS)}.obj: + $(CPP) $(CPP_PROJ) $< + +################################################################################ +# Begin Group "Source Files" + +################################################################################ +# Begin Source File + +SOURCE=.\viewdeck.c + +$(INTDIR)/viewdeck.obj : $(SOURCE) $(INTDIR) + +# End Source File +################################################################################ +# Begin Source File + +SOURCE=.\util_io.c +DEP_UTIL_=\ + .\util_io.h + +$(INTDIR)/util_io.obj : $(SOURCE) $(DEP_UTIL_) $(INTDIR) + +# End Source File +# End Group +# End Project +################################################################################ diff --git a/Interdata/id16_cpu.c b/Interdata/id16_cpu.c new file mode 100644 index 0000000..b24128f --- /dev/null +++ b/Interdata/id16_cpu.c @@ -0,0 +1,1957 @@ +/* id16_cpu.c: Interdata 16b CPU simulator + + Copyright (c) 2000-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu Interdata 16b CPU + + 28-Apr-07 RMS Removed clock initialization + 27-Oct-06 RMS Added idle support + Removed separate PASLA clock + 06-Feb-06 RMS Fixed bug in DH (found by Mark Hittinger) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 25-Aug-05 RMS Fixed DH integer overflow cases + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 10-Mar-05 RMS Fixed bug in show history routine (from Mark Hittinger) + Revised examine/deposit to do words rather than bytes + 07-Nov-04 RMS Added instruction history + 22-Sep-03 RMS Added additional instruction decode types + 07-Feb-03 RMS Fixed bug in SETM, SETMR (found by Mark Pizzolato) + + The register state for the Interdata 16b CPU is: + + R[0:F]<0:15> general registers + F[0:7]<0:31> single precision floating point registers + D[0:7]<0:63> double precision floating point registers + PSW<0:31> processor status word, including + STAT<0:11> status flags + CC<0:3> condition codes + PC<0:15> program counter + int_req[8]<0:31> interrupt requests + int_enb[8]<0:31> interrupt enables + + The Interdata 16b systems have four instruction formats: register to + register, short format, register to memory, and register to storage. + The formats are: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | R2 | register-register + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | N | short format + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-memory + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-storage + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + For register-memory and register-storage instructions, an effective + address is calculated as follows: + + effective addr = address + RX (if RX > 0) + + Register-memory instructions can access an address space of 64K bytes. + + The Interdata 16b product line had many different models, with varying + instruction sets: + + instruction group model = 3 4 5 70 80 716 816 816E + base group (61) y y y y y y y y + AL, LM, STM (3) - y y y y y y y + single prec fp (13) - y y y y y y y + model 5 group (36) - - y y y y y y + double prec fp (17) - - - - - - y y + memory extension (4) - - - - - - - y + + This allows the most common CPU options to be covered by just five + model selections: I3, I4, I5/70/80/716, I816, and I816E. Variations + within a model (e.g., 816 with no floating point or just single + precision floating point) are not implemented. + + The I3 kept its general registers in memory; this is not simulated. + Single precision (only) floating point was implemented in microcode, + did not have a guard digit, and kept the floating point registers in + memory. Double precision floating point was implemented in hardware, + provided a guard digit for single precision (but not double), and + kept the floating point registers in hardware. + + This routine is the instruction decode routine for the Interdata CPU. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + wait state and no I/O outstanding + invalid instruction + I/O error in I/O simulator + + 2. Interrupts. Each device has an interrupt armed flag, an interrupt + request flag, and an interrupt enabled flag. To facilitate evaluation, + all interrupt requests are kept in int_req, and all enables in int_enb. + Interrupt armed flags are local to devices. If external interrupts are + enabled in the PSW, and a request is pending, an interrupt occurs. + + 3. Non-existent memory. On the Interdata 16b, reads to non-existent + memory return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + id_defs.h add device interrupt definitions + id16_sys.c add sim_devices table entry +*/ + +#include "id_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = oPC +#define VAMASK VAMASK16 +#define VA_S1 0x8000 /* S0/S1 flag */ + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ +#define UNIT_V_ID4 (UNIT_V_UF + 1) +#define UNIT_V_716 (UNIT_V_UF + 2) +#define UNIT_V_816 (UNIT_V_UF + 3) +#define UNIT_V_816E (UNIT_V_UF + 4) +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define UNIT_ID4 (1 << UNIT_V_ID4) +#define UNIT_716 (1 << UNIT_V_716) +#define UNIT_816 (1 << UNIT_V_816) +#define UNIT_816E (1 << UNIT_V_816E) +#define UNIT_TYPE (UNIT_ID4 | UNIT_716 | UNIT_816 | UNIT_816E) + +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + uint16 vld; + uint16 pc; + uint16 ir1; + uint16 ir2; + uint16 r1; + uint16 ea; + uint16 opnd; + } InstHistory; + +#define PSW_GETMAP(x) (((x) >> PSW_V_MAP) & PSW_M_MAP) +#define SEXT16(x) (((x) & SIGN16)? ((int32) ((x) | 0xFFFF8000)): \ + ((int32) ((x) & 0x7FFF))) +#define CC_GL_16(x) if ((x) & SIGN16) cc = CC_L; \ + else if (x) cc = CC_G; \ + else cc = 0 +#define CC_GL_32(x) if ((x) & SIGN32) cc = CC_L; \ + else if (x) cc = CC_G; \ + else cc = 0 +#define BUILD_PSW(x) (((PSW & ~CC_MASK) | (x)) & psw_mask) +#define CPU_x16 (cpu_unit.flags & (UNIT_716 | UNIT_816 | UNIT_816E)) + +uint32 GREG[16] = { 0 }; /* general registers */ +uint16 *M = NULL; /* memory */ +uint32 *R = &GREG[0]; /* register set ptr */ +uint32 F[8] = { 0 }; /* sp fp registers */ +dpr_t D[8] = { 0 }; /* dp fp registers */ +uint32 PSW = 0; /* processor status word */ +uint32 psw_mask = PSW_x16; /* PSW mask */ +uint32 PC = 0; /* program counter */ +uint32 SR = 0; /* switch register */ +uint32 DR = 0; /* display register */ +uint32 DRX = 0; /* display extension */ +uint32 drmod = 0; /* mode */ +uint32 srpos = 0; /* switch register pos */ +uint32 drpos = 0; /* display register pos */ +uint32 s0_rel = 0; /* S0 relocation */ +uint32 s1_rel = 0; /* S1 relocation */ +uint32 int_req[INTSZ] = { 0 }; /* interrupt requests */ +uint32 int_enb[INTSZ] = { 0 }; /* interrupt enables */ +int32 blkiop = -1; /* block I/O in prog */ +uint32 qevent = 0; /* events */ +uint32 stop_inst = 0; /* stop on ill inst */ +uint32 stop_wait = 0; /* stop on wait */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +uint32 dec_flgs = 0; /* decode flags */ +uint32 fp_in_hwre = 0; /* ucode/hwre fp */ +uint32 pawidth = PAWIDTH16; /* phys addr mask */ +uint32 hst_p = 0; /* history pointer */ +uint32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ +struct BlockIO blk_io; /* block I/O status */ +uint32 (*dev_tab[DEVNO])(uint32 dev, uint32 op, uint32 datout) = { NULL }; + +extern int32 sim_interval; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern t_bool sim_idle_enab; + +uint32 ReadB (uint32 loc); +uint32 ReadH (uint32 loc); +void WriteB (uint32 loc, uint32 val); +void WriteH (uint32 loc, uint32 val); +uint32 int_auto (uint32 dev, uint32 cc); +uint32 addtoq (uint32 ea, uint32 val, uint32 flg); +uint32 remfmq (uint32 ea, uint32 r1, uint32 flg); +uint32 newPSW (uint32 val); +uint32 swap_psw (uint32 loc, uint32 cc); +uint32 testsysq (uint32); +uint32 display (uint32 dev, uint32 op, uint32 dat); +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_consint (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); + +extern t_bool devtab_init (void); +extern void int_eval (void); +extern uint32 int_getdev (void); +extern t_bool sch_blk (uint32 dev); +extern uint32 f_l (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_c (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_as (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_m (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_d (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_fix (uint32 op, uint32 r1, uint32 r2); +extern uint32 f_flt (uint32 op, uint32 r1, uint32 r2); + +/* Instruction decoding table - flags are first implementation */ + +const uint16 decrom[256] = { + 0, /* 00 */ + OP_RR, /* BALR */ + OP_RR, /* BTCR */ + OP_RR, /* BFCR */ + OP_RR, /* NHR */ + OP_RR, /* CLHR */ + OP_RR, /* OHR */ + OP_RR, /* XHR */ + OP_RR, /* LHR */ + OP_RR | OP_716, /* CHR */ + OP_RR, /* AHR */ + OP_RR, /* SHR */ + OP_RR, /* MHR */ + OP_RR, /* DHR */ + OP_RR, /* ACHR */ + OP_RR, /* SCHR */ + 0, 0, 0, /* 10:12 */ + OP_RR | OP_816E | OP_PRV, /* SETMR */ + 0, 0, 0, 0, /* 14:1F */ + 0, 0, 0, 0, 0, 0, 0, 0, + OP_NO | OP_716, /* BTBS */ + OP_NO | OP_716, /* BTFS */ + OP_NO | OP_716, /* BFBS */ + OP_NO | OP_716, /* BFFS */ + OP_NO | OP_716, /* LIS */ + OP_NO | OP_716, /* LCS */ + OP_NO | OP_716, /* AIS */ + OP_NO | OP_716, /* SIS */ + OP_NO | OP_ID4, /* LER */ + OP_NO | OP_ID4, /* CER */ + OP_NO | OP_ID4, /* AER */ + OP_NO | OP_ID4, /* SER */ + OP_NO | OP_ID4, /* MER */ + OP_NO | OP_ID4, /* DER */ + OP_NO | OP_816, /* FXR */ + OP_NO | OP_816, /* FLR */ + 0, 0, 0, /* 30:32 */ + OP_NO | OP_816E | OP_PRV, /* LPSR */ + 0, 0, 0, 0, /* 34:37 */ + OP_NO | OP_816 | OP_DPF, /* LDR */ + OP_NO | OP_816 | OP_DPF, /* CDR */ + OP_NO | OP_816 | OP_DPF, /* ADR */ + OP_NO | OP_816 | OP_DPF, /* SDR */ + OP_NO | OP_816 | OP_DPF, /* MDR */ + OP_NO | OP_816 | OP_DPF, /* DDR */ + OP_NO | OP_816 | OP_DPF, /* FXDR */ + OP_NO | OP_816 | OP_DPF, /* FLDR */ + OP_RX, /* STH */ + OP_RX, /* BAL */ + OP_RX, /* BTC */ + OP_RX, /* BFC */ + OP_RXH, /* NH */ + OP_RXH, /* CLH */ + OP_RXH, /* OH */ + OP_RXH, /* XH */ + OP_RXH, /* LH */ + OP_RXH | OP_716, /* CH */ + OP_RXH, /* AH */ + OP_RXH, /* SH */ + OP_RXH, /* MH */ + OP_RXH, /* DH */ + OP_RXH, /* ACH */ + OP_RXH, /* SCH */ + 0, 0, 0, /* 50:52 */ + OP_RXH | OP_816E | OP_PRV, /* SETM */ + 0, 0, 0, 0, /* 54:5F */ + 0, 0, 0, 0, 0, 0, 0, 0, + OP_RX | OP_ID4, /* STE */ + OP_RXH | OP_716, /* AHM */ + 0, 0, /* 62:63 */ + OP_RX | OP_716, /* ATL */ + OP_RX | OP_716, /* ABL */ + OP_RX | OP_716, /* RTL */ + OP_RX | OP_716, /* RBL */ + OP_RX | OP_ID4, /* LE */ + OP_RX | OP_ID4, /* CE */ + OP_RX | OP_ID4, /* AE */ + OP_RX | OP_ID4, /* SE */ + OP_RX | OP_ID4, /* ME */ + OP_RX | OP_ID4, /* DE */ + 0, 0, /* 6E:6F */ + OP_RX | OP_816 | OP_DPF, /* STD */ + OP_RX | OP_816, /* SME */ + OP_RX | OP_816, /* LME */ + OP_RXH | OP_816E | OP_PRV, /* LPS */ + 0, 0, 0, 0, /* 74:7F */ + OP_RX | OP_816 | OP_DPF, /* LD */ + OP_RX | OP_816 | OP_DPF, /* CD */ + OP_RX | OP_816 | OP_DPF, /* AD */ + OP_RX | OP_816 | OP_DPF, /* SD */ + OP_RX | OP_816 | OP_DPF, /* MD */ + OP_RX | OP_816 | OP_DPF, /* DD */ + OP_RX | OP_816 | OP_DPF, /* STMD */ + OP_RX | OP_816 | OP_DPF, /* LMD */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 80:8F */ + 0, 0, 0, 0, 0, 0, 0, 0, + OP_NO | OP_716, /* SRLS */ + OP_NO | OP_716, /* SLLS */ + OP_NO, /* STBR */ + OP_RR, /* LDBR */ + OP_RR | OP_716, /* EXBR */ + OP_NO | OP_716 | OP_PRV, /* EPSR */ + OP_RR | OP_PRV, /* WBR */ + OP_RR | OP_PRV, /* RBR */ + OP_RR | OP_716 | OP_PRV, /* WHR */ + OP_RR | OP_716 | OP_PRV, /* RHR */ + OP_RR | OP_PRV, /* WDR */ + OP_RR | OP_PRV, /* RDR */ + OP_RR | OP_716, /* MHUR */ + OP_RR | OP_PRV, /* SSR */ + OP_RR | OP_PRV, /* OCR */ + OP_RR | OP_PRV, /* AIR */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A0:AF */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* B0:BF */ + 0, 0, 0, 0, 0, 0, 0, 0, + OP_RX, /* BXH */ + OP_RX, /* BXLE */ + OP_RX | OP_PRV, /* LPSW */ + OP_RS | OP_716, /* THI */ + OP_RS, /* NHI */ + OP_RS, /* CLHI */ + OP_RS, /* OHI */ + OP_RS, /* XHI */ + OP_RS, /* LHI */ + OP_RS | OP_716, /* CHI */ + OP_RS, /* AHI */ + OP_RS, /* SHI */ + OP_RS, /* SRHL */ + OP_RS, /* SLHL */ + OP_RS, /* SRHA */ + OP_RS, /* SLHA */ + OP_RX | OP_ID4, /* STM */ + OP_RX | OP_ID4, /* LM */ + OP_RX, /* STB */ + OP_RXB, /* LDB */ + OP_RXB | OP_716, /* CLB */ + OP_RX | OP_ID4 | OP_PRV, /* AL */ + OP_RXH | OP_PRV, /* WB */ + OP_RXH | OP_PRV, /* RB */ + OP_RX | OP_716 | OP_PRV, /* WH */ + OP_RX | OP_716 | OP_PRV, /* RH */ + OP_RX | OP_PRV, /* WD */ + OP_RX | OP_PRV, /* RD */ + OP_RXH | OP_716, /* MHU */ + OP_RX | OP_PRV, /* SS */ + OP_RX | OP_PRV, /* OC */ + OP_RX | OP_PRV, /* AI */ + 0, /* E0 */ + OP_RX | OP_716, /* SVC */ + OP_RS | OP_716 | OP_PRV, /* SINT */ + 0, 0, 0, 0, 0, 0, 0, /* E3:E9 */ + OP_RS | OP_716, /* RRL */ + OP_RS | OP_716, /* RLL */ + OP_RS | OP_716, /* SRL */ + OP_RS | OP_716, /* SLL */ + OP_RS | OP_716, /* SRA */ + OP_RS | OP_716, /* SLA */ + 0, 0, 0, 0, 0, 0, 0, 0, /* F0:FF */ + 0, 0, 0, 0, 0, 0, 0, 0 + }; + +/* 8/16E relocation constants for S0 and S1, indexed by PSW<8:11> */ + +static uint32 s0_rel_const[16] = { /* addr 0-7FFF */ + 0x00000, 0x00000, 0x00000, 0x00000, /* 0 = no reloc */ + 0x00000, 0x00000, 0x00000, 0x08000, /* 8000 = rel to S1 */ + 0x08000, 0x08000, 0x08000, 0x08000, + 0x08000, 0x08000, 0x08000, 0x00000 + }; + +static uint32 s1_rel_const[16] = { /* addr 8000-FFFF */ + 0x00000, 0x08000, 0x10000, 0x18000, /* reloc const must */ + 0x20000, 0x28000, 0x30000, 0xFFF8000, /* "sub" base addr */ + 0x00000, 0x08000, 0x10000, 0x18000, + 0x20000, 0x28000, 0x30000, 0x00000 + }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +DIB cpu_dib = { d_DS, -1, v_DS, NULL, &display, NULL }; + +UNIT cpu_unit = { + UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_716, MAXMEMSIZE16) + }; + +REG cpu_reg[] = { + { HRDATA (PC, PC, 16) }, + { HRDATA (R0, GREG[0], 16) }, + { HRDATA (R1, GREG[1], 16) }, + { HRDATA (R2, GREG[2], 16) }, + { HRDATA (R3, GREG[3], 16) }, + { HRDATA (R4, GREG[4], 16) }, + { HRDATA (R5, GREG[5], 16) }, + { HRDATA (R6, GREG[6], 16) }, + { HRDATA (R7, GREG[7], 16) }, + { HRDATA (R8, GREG[8], 16) }, + { HRDATA (R9, GREG[9], 16) }, + { HRDATA (R10, GREG[10], 16) }, + { HRDATA (R11, GREG[11], 16) }, + { HRDATA (R12, GREG[12], 16) }, + { HRDATA (R13, GREG[13], 16) }, + { HRDATA (R14, GREG[14], 16) }, + { HRDATA (R15, GREG[15], 16) }, + { HRDATA (FR0, F[0], 32) }, + { HRDATA (FR2, F[1], 32) }, + { HRDATA (FR4, F[2], 32) }, + { HRDATA (FR6, F[3], 32) }, + { HRDATA (FR8, F[4], 32) }, + { HRDATA (FR10, F[5], 32) }, + { HRDATA (FR12, F[6], 32) }, + { HRDATA (FR14, F[7], 32) }, + { HRDATA (D0H, D[0].h, 32) }, + { HRDATA (D0L, D[0].l, 32) }, + { HRDATA (D2H, D[1].h, 32) }, + { HRDATA (D2L, D[1].l, 32) }, + { HRDATA (D4H, D[2].h, 32) }, + { HRDATA (D4L, D[2].l, 32) }, + { HRDATA (D6H, D[3].h, 32) }, + { HRDATA (D6L, D[3].l, 32) }, + { HRDATA (D8H, D[4].h, 32) }, + { HRDATA (D8L, D[4].l, 32) }, + { HRDATA (D10H, D[5].h, 32) }, + { HRDATA (D10L, D[5].l, 32) }, + { HRDATA (D12L, D[6].l, 32) }, + { HRDATA (D12H, D[6].h, 32) }, + { HRDATA (D14H, D[7].h, 32) }, + { HRDATA (D14L, D[7].l, 32) }, + { HRDATA (PSW, PSW, 16) }, + { HRDATA (CC, PSW, 4) }, + { HRDATA (SR, SR, 16) }, + { HRDATA (DR, DR, 32) }, + { HRDATA (DRX, DRX, 8) }, + { FLDATA (DRMOD, drmod, 0) }, + { FLDATA (SRPOS, srpos, 0) }, + { HRDATA (DRPOS, drpos, 3) }, + { BRDATA (IRQ, int_req, 16, 32, 8) }, + { BRDATA (IEN, int_enb, 16, 32, 8) }, + { HRDATA (QEVENT, qevent, 4), REG_HRO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (STOP_WAIT, stop_inst, 0) }, + { BRDATA (PCQ, pcq, 16, 16, PCQ_SIZE), REG_RO+REG_CIRC }, + { HRDATA (PCQP, pcq_p, 6), REG_HRO }, + { HRDATA (WRU, sim_int_char, 8) }, + { HRDATA (BLKIOD, blk_io.dfl, 16), REG_HRO }, + { HRDATA (BLKIOC, blk_io.cur, 16), REG_HRO }, + { HRDATA (BLKIOE, blk_io.end, 16), REG_HRO }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_TYPE, 0, "I3", "I3", &cpu_set_model }, + { UNIT_TYPE, UNIT_ID4, "I4", "I4", &cpu_set_model }, + { UNIT_TYPE, UNIT_716, "7/16", "716", &cpu_set_model }, + { UNIT_TYPE, UNIT_816, "8/16", "816", &cpu_set_model }, + { UNIT_TYPE, UNIT_816E, "8/16E", "816E", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, + { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "CONSINT", + &cpu_set_consint, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 18, 2, 16, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + &cpu_dib, 0 + }; + +t_stat sim_instr (void) +{ +uint32 cc; +t_stat reason; + +/* Restore register state */ + +if (devtab_init ()) return SCPE_STOP; /* check conflicts */ +pawidth = PAWIDTH16; /* default width */ +if (cpu_unit.flags & UNIT_816E) { /* 8/16E? */ + dec_flgs = 0; /* all instr ok */ + fp_in_hwre = 1; /* fp in hwre */ + pawidth = PAWIDTH16E; /* 18b phys addr */ + psw_mask = PSW_816E; /* mem ext bits */ + } +else if (cpu_unit.flags & UNIT_816) { /* 8/16? */ + dec_flgs = OP_816E; + fp_in_hwre = 1; + pawidth = PAWIDTH16; + psw_mask = PSW_x16; + } +else if (cpu_unit.flags & UNIT_716) { /* I5, 70, 80, 7/16? */ + dec_flgs = OP_816 | OP_816E; + fp_in_hwre = 0; + pawidth = PAWIDTH16; + psw_mask = PSW_x16; + } +else if (cpu_unit.flags & UNIT_ID4) { /* I4? */ + dec_flgs = OP_716 | OP_816 | OP_816E; + fp_in_hwre = 0; + pawidth = PAWIDTH16; + psw_mask = PSW_ID4; + } +else { + dec_flgs = OP_ID4 | OP_716 | OP_816 | OP_816E; /* I3 */ + fp_in_hwre = 0; + pawidth = PAWIDTH16; + psw_mask = PSW_ID4; + } +int_eval (); /* eval interrupts */ +cc = newPSW (PSW & psw_mask); /* split PSW, eval wait */ +reason = 0; + +/* Process events */ + +while (reason == 0) { /* loop until halted */ + uint32 dev, drom, inc, lim, opnd; + uint32 op, r1, r1p1, r2, ea, oPC; + uint32 rslt, t, map; + uint32 ir1, ir2, ityp; + int32 sr, st; + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + int_eval (); + } + + if (qevent) { /* any events? */ + if (qevent & EV_BLK) { /* block I/O in prog? */ + dev = blk_io.dfl & DEV_MAX; /* get device */ + cc = dev_tab[dev] (dev, IO_SS, 0) & 0xF; /* sense status */ + if (cc == STA_BSY) { /* just busy? */ + sim_interval = 0; /* force I/O event */ + continue; + } + else if (cc == 0) { /* ready? */ + if (blk_io.dfl & BL_RD) { /* read? */ + t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ + if ((t == 0) && (blk_io.dfl & BL_LZ)) continue; + blk_io.dfl = blk_io.dfl & ~BL_LZ; /* non-zero seen */ + WriteB (blk_io.cur, t); /* write mem */ + } + else { /* write */ + t = ReadB (blk_io.cur); /* read mem */ + dev_tab[dev] (dev, IO_WD, t); /* put byte */ + } + if (blk_io.cur != blk_io.end) { /* more to do? */ + blk_io.cur = (blk_io.cur + 1) & VAMASK; /* incr addr */ + continue; + } + } + qevent = qevent & ~EV_BLK; /* clr block I/O flg */ + int_eval (); /* re-eval intr */ + continue; + } + + if ((qevent & EV_INT) && (PSW & PSW_EXI)) { /* interrupt? */ + if (PSW & PSW_AIO) { /* auto enabled? */ + dev = int_getdev (); /* get int dev */ + cc = int_auto (dev, cc); /* do auto intr */ + int_eval (); /* re-eval intr */ + } + else cc = swap_psw (EXIPSW, cc); /* old type, swap */ + continue; + } + + if (PSW & PSW_WAIT) { /* wait state? */ + if (sim_idle_enab) /* idling enabled? */ + sim_idle (TMR_LFC, TRUE); + else sim_interval = sim_interval - 1; /* no, count cycle */ + continue; + } + + qevent = 0; /* no events */ + } /* end if event */ + +/* Fetch and decode instruction */ + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + sim_interval = sim_interval - 1; + + ir1 = ReadH (oPC = PC); /* fetch instr */ + op = (ir1 >> 8) & 0xFF; /* isolate op, R1, R2 */ + r1 = (ir1 >> 4) & 0xF; + r2 = ir1 & 0xF; + drom = decrom[op]; + ityp = drom & OP_MASK; + + if ((drom == 0) || (drom & dec_flgs)) { /* not in model? */ + if (stop_inst) reason = STOP_RSRV; /* stop or */ + else cc = swap_psw (ILOPSW, cc); /* swap PSW */ + continue; + } + if ((drom & OP_PRV) && (PSW & PSW_PRO)) { /* priv & protected? */ + cc = swap_psw (ILOPSW, cc); /* swap PSW */ + continue; + } + + switch (ityp) { /* decode instruction */ + + case OP_NO: /* no operand */ + opnd = r2; /* assume short */ + break; + + case OP_RR: /* reg-reg */ + opnd = R[r2]; /* operand is R2 */ + break; + + case OP_RS: /* reg-storage */ + case OP_RX: /* reg-mem */ + PC = (PC + 2) & VAMASK; /* increment PC */ + ir2 = ea = ReadH (PC); /* fetch address */ + if (r2) ea = (ir2 + R[r2]) & VAMASK; /* index calculation */ + opnd = ea; /* operand is ea */ + break; + + case OP_RXB: /* reg-mem byte */ + PC = (PC + 2) & VAMASK; /* increment PC */ + ir2 = ea = ReadH (PC); /* fetch address */ + if (r2) ea = (ea + R[r2]) & VAMASK; /* index calculation */ + opnd = ReadB (ea); /* fetch operand */ + break; + + case OP_RXH: /* reg-mem halfword */ + PC = (PC + 2) & VAMASK; /* increment PC */ + ir2 = ea = ReadH (PC); /* fetch address */ + if (r2) ea = (ea + R[r2]) & VAMASK; /* index calculation */ + opnd = ReadH (ea); /* fetch operand */ + break; + + default: + return SCPE_IERR; + } + + if (hst_lnt) { /* instruction history? */ + hst[hst_p].vld = 1; + hst[hst_p].pc = oPC; + hst[hst_p].ir1 = ir1; + hst[hst_p].ir2 = ir2; + hst[hst_p].r1 = R[r1]; + hst[hst_p].ea = ea; + hst[hst_p].opnd = opnd; + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) hst_p = 0; + } + + PC = (PC + 2) & VAMASK; /* increment PC */ + switch (op) { /* case on opcode */ + +/* Load/store instructions */ + + case 0x08: /* LHR - RR */ + case 0x24: /* LIS - NO */ + case 0x48: /* LH - RXH */ + case 0xC8: /* LHI - RS */ + R[r1] = opnd; /* load operand */ + CC_GL_16 (R[r1]); /* set G,L */ + break; + + case 0x25: /* LCS - NO */ + R[r1] = (~opnd + 1) & DMASK16; /* load complement */ + CC_GL_16 (R[r1]); /* set G,L */ + break; + + case 0x40: /* STH - RX */ + WriteH (ea, R[r1]); /* store register */ + break; + + case 0xD1: /* LM - RX */ + for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ + R[r1] = ReadH (ea); /* load register */ + ea = (ea + 2) & VAMASK; /* incr mem addr */ + } + break; + + case 0xD0: /* STM - RX */ + for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ + WriteH (ea, R[r1]); /* store register */ + ea = (ea + 2) & VAMASK; /* incr mem addr */ + } + break; + + case 0x93: /* LDBR - RR */ + case 0xD3: /* LDB - RXB */ + R[r1] = opnd & DMASK8; /* load byte */ + break; + + case 0x92: /* STBR - NO */ + R[r2] = (R[r2] & ~DMASK8) | (R[r1] & DMASK8); /* store byte */ + break; + case 0xD2: /* STB - RX */ + WriteB (ea, R[r1] & DMASK8); /* store byte */ + break; + + case 0x94: /* EXBR - RR */ + R[r1] = (opnd >> 8) | ((opnd & DMASK8) << 8); + break; + +/* Control instructions */ + + case 0x01: /* BALR - RR */ + case 0x41: /* BAL - RX */ + PCQ_ENTRY; /* save old PC */ + R[r1] = PC; /* save cur PC */ + PC = opnd; /* branch */ + break; + + case 0x02: /* BTCR - RR */ + case 0x42: /* BTC - RX */ + if (cc & r1) { /* test CC's */ + PCQ_ENTRY; /* branch if true */ + PC = opnd; + } + break; + + case 0x20: /* BTBS - NO */ + if (cc & r1) { /* test CC's */ + PCQ_ENTRY; /* branch if true */ + PC = (oPC - r2 - r2) & VAMASK; + } + break; + + case 0x21: /* BTFS - NO */ + if (cc & r1) { /* test CC's */ + PCQ_ENTRY; /* branch if true */ + PC = (oPC + r2 + r2) & VAMASK; + } + break; + + case 0x03: /* BFCR - RR */ + case 0x43: /* BFC - RX */ + if ((cc & r1) == 0) { /* test CC's */ + PCQ_ENTRY; /* branch if false */ + PC = opnd; + } + break; + + case 0x22: /* BFBS - NO */ + if ((cc & r1) == 0) { /* test CC's */ + PCQ_ENTRY; /* branch if false */ + PC = (oPC - r2 - r2) & VAMASK; + } + break; + + case 0x23: /* BFFS - NO */ + if ((cc & r1) == 0) { /* test CC's */ + PCQ_ENTRY; /* branch if false */ + PC = (oPC + r2 + r2) & VAMASK; + } + break; + + case 0xC0: /* BXH - RX */ + inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ + lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ + R[r1] = (R[r1] + inc) & DMASK16; /* R1 = R1 + inc */ + if (R[r1] > lim) { /* if R1 > lim */ + PCQ_ENTRY; /* branch */ + PC = opnd; + } + break; + + case 0xC1: /* BXLE - RX */ + inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ + lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ + R[r1] = (R[r1] + inc) & DMASK16; /* R1 = R1 + inc */ + if (R[r1] <= lim) { /* if R1 <= lim */ + PCQ_ENTRY; /* branch */ + PC = opnd; + } + break; + +/* Logical instructions */ + + case 0x04: /* NHR - RR */ + case 0x44: /* NH - RXH */ + case 0xC4: /* NHI - RS */ + R[r1] = R[r1] & opnd; /* result */ + CC_GL_16 (R[r1]); /* set G,L */ + break; + + case 0x06: /* OHR - RR */ + case 0x46: /* OH - RXH */ + case 0xC6: /* OHI - RS */ + R[r1] = R[r1] | opnd; /* result */ + CC_GL_16 (R[r1]); /* set G,L */ + break; + + case 0x07: /* XHR - RR */ + case 0x47: /* XH - RXH */ + case 0xC7: /* XHI - RS */ + R[r1] = R[r1] ^ opnd; /* result */ + CC_GL_16 (R[r1]); /* set G,L */ + break; + + case 0xC3: /* THI - RS */ + rslt = R[r1] & opnd; /* result */ + CC_GL_16 (rslt); /* set G, L */ + break; + + case 0x05: /* CLHR - RR */ + case 0x45: /* CLH - RXH */ + case 0xC5: /* CLHI - RS */ + rslt = (R[r1] - opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L */ + if (R[r1] < opnd) cc = cc | CC_C; /* set C if borrow */ + if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN16) cc = cc | CC_V; + break; + + case 0xD4: /* CLB - RXB */ + t = R[r1] & DMASK8; + rslt = (t - opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L */ + if (t < opnd) cc = cc | CC_C; /* set C if borrow */ + break; + +/* Shift instructions */ + + case 0xCC: /* SRHL - RS */ + opnd = opnd & 0xF; /* shift count */ + case 0x90: /* SRLS - NO */ + rslt = R[r1] >> opnd; /* result */ + CC_GL_16 (rslt); /* set G,L */ + if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = rslt; /* store result */ + break; + + case 0xCD: /* SLHL - RS */ + opnd = opnd & 0xF; /* shift count */ + case 0x91: /* SLLS - NO */ + rslt = R[r1] << opnd; /* raw result */ + R[r1] = rslt & DMASK16; /* masked result */ + CC_GL_16 (R[r1]); /* set G,L */ + if (opnd && (rslt & 0x10000)) cc = cc | CC_C; /* set C if shft out */ + break; + + case 0xCE: /* SRHA - RS */ + opnd = opnd & 0xF; /* shift count */ + rslt = (SEXT16 (R[r1]) >> opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L */ + if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = rslt; /* store result */ + break; + + case 0xCF: /* SLHA - RS */ + opnd = opnd & 0xF; /* shift count */ + rslt = R[r1] << opnd; /* raw result */ + R[r1] = (R[r1] & SIGN16) | (rslt & MMASK16); /* arith result */ + CC_GL_16 (R[r1]); /* set G,L */ + if (opnd && (rslt & SIGN16)) cc = cc | CC_C; /* set C if shft out */ + break; + + case 0xEA: /* RRL - RS */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + opnd = opnd & 0x1F; /* shift count */ + t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ + if (opnd) rslt = (t >> opnd) | (t << (32 - opnd)); /* result */ + else rslt = t; /* no shift */ + CC_GL_32 (rslt); /* set G,L 32b */ + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + + case 0xEB: /* RLL - RS */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + opnd = opnd & 0x1F; /* shift count */ + t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ + if (opnd) rslt = (t << opnd) | (t >> (32 - opnd)); /* result */ + else rslt = t; /* no shift */ + CC_GL_32 (rslt); /* set G,L 32b */ + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + + case 0xEC: /* SRL - RS */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + opnd = opnd & 0x1F; /* shift count */ + t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ + rslt = t >> opnd; /* result */ + CC_GL_32 (rslt); /* set G,L 32b */ + if (opnd && ((t >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + + case 0xED: /* SLL - RS */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + opnd = opnd & 0x1F; /* shift count */ + t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ + rslt = t << opnd; /* result */ + CC_GL_32 (rslt); /* set G,L 32b */ + if (opnd && ((t << (opnd - 1)) & SIGN32)) cc = cc | CC_C; + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + + case 0xEE: /* SRA - RS */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + opnd = opnd & 0x1F; /* shift count */ + t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ + rslt = ((int32) t) >> opnd; /* signed result */ + CC_GL_32 (rslt); /* set G,L 32b */ + if (opnd && ((t >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + + case 0xEF: /* SLA - RS */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + opnd = opnd & 0x1F; /* shift count */ + t = (R[r1] << 16) | R[r1p1]; /* form 32b op */ + rslt = (t & SIGN32) | ((t << opnd) & MMASK32); /* signed result */ + CC_GL_32 (rslt); /* set G,L 32b */ + if (opnd && ((t << opnd) & SIGN32)) cc = cc | CC_C; + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + +/* Arithmetic instructions */ + + case 0x0A: /* AHR - RR */ + case 0x26: /* AIS - NO */ + case 0x4A: /* AH - RXH */ + case 0xCA: /* AHI - RS */ + rslt = (R[r1] + opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L */ + if (rslt < opnd) cc = cc | CC_C; /* set C if carry */ + if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; + R[r1] = rslt; + break; + + case 0x61: /* AHM - RXH */ + rslt = (R[r1] + opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L */ + if (rslt < opnd) cc = cc | CC_C; /* set C if carry */ + if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; + WriteH (ea, rslt); /* store in memory */ + break; + + case 0x0B: /* SHR - RR */ + case 0x27: /* SIS - NO */ + case 0x4B: /* SH - RXH */ + case 0xCB: /* SHI - RS */ + rslt = (R[r1] - opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L */ + if (R[r1] < opnd) cc = cc | CC_C; /* set C if borrow */ + if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN16) cc = cc | CC_V; + R[r1] = rslt; + break; + + case 0x09: /* CHR - RR */ + case 0x49: /* CH - RXH */ + case 0xC9: /* CHI - RS */ + sr = SEXT16 (R[r1]); /* sign ext */ + st = SEXT16 (opnd); + if (sr < st) cc = CC_C | CC_L; /* < sets C, L */ + else if (sr > st) cc = CC_G; /* > sets G */ + else cc = 0; + if (((R[r1] ^ opnd) & (~opnd ^ (sr - st))) & SIGN16) + cc = cc | CC_V; + break; + + case 0x0C: /* MHR - RR */ + case 0x4C: /* MH - RXH */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + rslt = SEXT16 (R[r1p1]) * SEXT16 (opnd); /* multiply */ + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + + case 0x9C: /* MHUR - RR */ + case 0xDC: /* MHU - RXH */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + rslt = R[r1p1] * opnd; /* multiply, unsigned */ + R[r1] = (rslt >> 16) & DMASK16; /* hi result */ + R[r1p1] = rslt & DMASK16; /* lo result */ + break; + + case 0x0D: /* DHR - RR */ + case 0x4D: /* DH - RXH */ + r1p1 = (r1 + 1) & 0xF; /* R1 + 1 */ + if ((opnd == 0) || + ((R[r1] == 0x8000) && (R[r1p1] == 0) && (opnd == 0xFFFF))) { + if (PSW & PSW_AFI) /* div fault enabled? */ + cc = swap_psw (AFIPSW, cc); /* swap PSW */ + break; + } + sr = (R[r1] << 16) | R[r1p1]; /* signed 32b divd */ + st = sr / SEXT16 (opnd); /* signed quotient */ + sr = sr % SEXT16 (opnd); /* remainder */ + if ((st < 0x8000) && (st >= -0x8000)) { /* if quo fits */ + R[r1] = sr & DMASK16; /* store remainder */ + R[r1p1] = st & DMASK16; /* store quotient */ + } + else if (PSW & PSW_AFI) /* div fault enabled? */ + cc = swap_psw (AFIPSW, cc); /* swap PSW */ + break; + + case 0x0E: /* ACHR - RR */ + case 0x4E: /* ACH - RXH */ + t = R[r1] + opnd + ((cc & CC_C) != 0); /* raw result */ + rslt = t & DMASK16; /* masked result */ + CC_GL_16 (rslt); /* set G,L */ + if (t > DMASK16) cc = cc | CC_C; /* set C if carry */ + if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; + R[r1] = rslt; /* store result */ + break; + + case 0x0F: /* SCHR - RR */ + case 0x4F: /* SCH - RXH */ + t = R[r1] - opnd - ((cc & CC_C) != 0); /* raw result */ + rslt = t & DMASK16; /* masked result */ + CC_GL_16 (rslt); /* set G,L */ + if (t > DMASK16) cc = cc | CC_C; /* set C if borrow */ + if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN16) cc = cc | CC_V; + R[r1] = rslt; /* store result */ + break; + +/* Floating point instructions */ + + case 0x28: /* LER - NO */ + case 0x38: /* LDR - NO */ + case 0x68: /* LE - RX */ + case 0x78: /* LD - RX */ + cc = f_l (op, r1, r2, ea); /* load */ + if ((cc & CC_V) && (PSW & PSW_FPF) && CPU_x16) /* V set, x/16? */ + cc = swap_psw (FPFPSW, cc); + break; + + case 0x29: /* CER - NO */ + case 0x39: /* CDR - NO */ + case 0x69: /* CE - RX */ + case 0x79: /* CD - RX */ + cc = f_c (op, r1, r2, ea); /* compare */ + break; + + case 0x2A: /* AER - NO */ + case 0x2B: /* SER - NO */ + case 0x3A: /* ADR - NO */ + case 0x3B: /* SDR - NO */ + case 0x6A: /* AE - RX */ + case 0x6B: /* SE - RX */ + case 0x7A: /* AD - RX */ + case 0x7B: /* SD - RX */ + cc = f_as (op, r1, r2, ea); /* add/sub */ + if ((cc & CC_V) && (PSW & PSW_FPF) && CPU_x16) /* V set, x/16? */ + cc = swap_psw (FPFPSW, cc); + break; + + case 0x2C: /* MER - NO */ + case 0x3C: /* MDR - NO */ + case 0x6C: /* ME - RX */ + case 0x7C: /* MD - RX */ + cc = f_m (op, r1, r2, ea); /* multiply */ + if ((cc & CC_V) && (PSW & PSW_FPF) && CPU_x16) /* V set, x/16? */ + cc = swap_psw (FPFPSW, cc); + break; + + case 0x2D: /* DER - NO */ + case 0x3D: /* DDR - NO */ + case 0x6D: /* DE - RX */ + case 0x7D: /* DD - RX */ + cc = f_d (op, r1, r2, ea); /* perform divide */ + if ((cc & CC_V) && ((cc & CC_C) || /* V set, x/16 or */ + ((PSW & PSW_FPF) && CPU_x16))) /* V & C set? */ + cc = swap_psw (FPFPSW, cc); + break; + + case 0x2E: /* FXR - NO */ + case 0x3E: /* FXDR - NO */ + cc = f_fix (op, r1, r2); /* cvt to integer */ + break; + + case 0x2F: /* FLR - NO */ + case 0x3F: /* FLDR - NO */ + cc = f_flt (op, r1, r2); /* cvt to floating */ + break; + + case 0x60: /* STE - RX */ + t = ReadFReg (r1); /* get fp reg */ + WriteF (ea, t, P); /* write */ + break; + + case 0x70: /* STD - RX */ + WriteF (ea, D[r1 >> 1].h, P); /* write hi */ + WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, P); /* write lo */ + break; + + case 0x71: /* STME - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + t = ReadFReg (r1); /* get fp reg */ + WriteF (ea, t, P); /* write */ + ea = (ea + 4) & VAMASK; /* incr mem addr */ + } + break; + + case 0x72: /* LME - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + t = ReadF (ea, P); /* get value */ + WriteFReg (r1, t); /* write reg */ + ea = (ea + 4) & VAMASK; /* incr mem addr */ + } + break; + + case 0x7E: /* STMD - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + WriteF (ea, D[r1 >> 1].h, P); /* write register */ + WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, P); + ea = (ea + 8) & VAMASK; /* incr mem addr */ + } + break; + + case 0x7F: /* LMD - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + D[r1 >> 1].h = ReadF (ea, P); /* load register */ + D[r1 >> 1].l = ReadF ((ea + 4) & VAMASK, P); + ea = (ea + 8) & VAMASK; /* incr mem addr */ + } + break; + +/* Miscellaneous */ + + case 0xE1: /* SVC - RX */ + PCQ_ENTRY; /* save PC */ + WriteH (SVCAP, ea); /* save opnd */ + WriteH (SVOPS, BUILD_PSW (cc)); /* save PS */ + WriteH (SVOPC, PC); /* save PC */ + PC = ReadH (SVNPC + r1 + r1); /* new PC */ + cc = newPSW (ReadH (SVNPS)); /* new PS */ + break; + + case 0xE2: /* SINT - RS */ + dev = opnd & DEV_MAX; /* get dev */ + cc = int_auto (dev, cc); /* auto intr */ + int_eval (); /* re-eval intr */ + break; + + case 0xC2: /* LPSW - RX */ + PCQ_ENTRY; /* effective branch */ + PC = ReadH ((ea + 2) & VAMASK); /* read PC */ + cc = newPSW (ReadH (ea)); /* read PSW */ + if (PSW & PSW_SQI) cc = testsysq (cc); /* test for q */ + break; + + case 0x95: /* EPSR - NO */ + R[r1] = BUILD_PSW (cc); /* save PSW */ + case 0x33: /* LPSR - NO */ + cc = newPSW (R[r2]); /* load new PSW */ + if (PSW & PSW_SQI) cc = testsysq (cc); /* test for q */ + break; + + case 0x73: /* LPS - RXH */ + cc = newPSW (opnd); /* load new PSW */ + if (PSW & PSW_SQI) cc = testsysq (cc); /* test for q */ + break; + + case 0x64: /* ATL - RX */ + case 0x65: /* ABL - RX */ + cc = addtoq (ea, R[r1], op & 1); /* add to q */ + break; + + case 0x66: /* RTL - RX */ + case 0x67: /* RBL - RX */ + cc = remfmq (ea, r1, op & 1); /* remove from q */ + break; + + case 0x13: /* SETMR - RR */ + case 0x53: /* SETM - RXH */ + t = BUILD_PSW (cc); /* old PSW */ + map = PSW_GETMAP (opnd); /* get new map */ + switch (map) { /* case on map */ + + case 0x7: + map = 0; /* use 1:1 map */ + R[r1] = R[r1] ^ SIGN16; /* flip sign */ + break; + + case 0x8: case 0x9: case 0xA: case 0xB: + case 0xC: case 0xD: case 0xE: + if (R[r1] & SIGN16) map = map & ~0x8; /* S1? clr map<0> */ + else { + map = 0; /* else 1:1 map */ + R[r1] = R[r1] | SIGN16; /* set sign */ + } + break; + + default: + break; + } + t = (t & ~PSW_MAP) | (map << PSW_V_MAP); /* insert map */ + newPSW (t); /* load new PSW */ + CC_GL_16 (R[r1]); /* set G,L */ + break; + +/* I/O instructions */ + +case 0xDE: /* OC - RX */ + opnd = ReadB (ea); /* fetch operand */ + case 0x9E: /* OCR - RR */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + dev_tab[dev] (dev, IO_OC, opnd & DMASK8); /* send command */ + int_eval (); /* re-eval intr */ + cc = 0; + } + else cc = CC_V; + break; + + case 0xDA: /* WD - RX */ + opnd = ReadB (ea); /* fetch operand */ + case 0x9A: /* WDR - RR */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send data */ + int_eval (); /* re-eval intr */ + cc = 0; + } + else cc = CC_V; + break; + + case 0xD8: /* WH - RX */ + opnd = ReadH (ea); /* fetch operand */ + case 0x98: /* WHR - RR */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { + if (dev_tab[dev] (dev, IO_ADR, 0)) /* select; hw ok? */ + dev_tab[dev] (dev, IO_WH, opnd); /* send data */ + else { /* byte only */ + dev_tab[dev] (dev, IO_WD, opnd >> 8); /* send hi byte */ + dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send lo byte */ + } + int_eval (); /* re-eval intr */ + cc = 0; + } + else cc = CC_V; + break; + + case 0x9B: /* RDR - RR */ + case 0xDB: /* RD - RX */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + t = dev_tab[dev] (dev, IO_RD, 0); /* get data */ + cc = 0; + } + else { /* no */ + t = 0; /* read zero */ + cc = CC_V; /* set V */ + } + if (OP_TYPE (op) != OP_RR) WriteB (ea, t); /* RX or RR? */ + else R[r2] = t & DMASK8; + int_eval (); /* re-eval intr */ + break; + + case 0x99: /* RHR - RR */ + case 0xD9: /* RH - RX */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + if (dev_tab[dev] (dev, IO_ADR, 0)) /* select, hw ok? */ + t = dev_tab[dev] (dev, IO_RH, 0); /* get data */ + else { /* byte only */ + rslt = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ + t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ + t = (rslt << 8) | t; /* merge */ + } + cc = 0; + } + else { /* no */ + t = 0; /* read zero */ + cc = CC_V; /* set V */ + } + if (OP_TYPE (op) != OP_RR) WriteH (ea, t); /* RX or RR? */ + else R[r2] = t; + int_eval (); /* re-eval intr */ + break; + + case 0x9F: /* AIR - RR */ + case 0xDF: /* AI - RX */ + R[r1] = int_getdev (); /* get int dev */ + /* fall through */ + case 0x9D: /* SSR - RR */ + case 0xDD: /* SS - RX */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + t = dev_tab[dev] (dev, IO_SS, 0); /* get status */ + } + else t = STA_EX; /* no */ + if (OP_TYPE (op) != OP_RR) WriteB (ea, t); /* RR or RX? */ + else R[r2] = t & DMASK8; + cc = t & 0xF; + int_eval (); /* re-eval intr */ + break; + +/* Block I/O instructions + + On a real Interdata system, the block I/O instructions can't be + interrupted or stopped. To model this behavior, while allowing + the instructions to go back through fetch for I/O processing and + WRU testing, the simulator implements a 'block I/O in progress' + flag and status block. If a block I/O is in progress, normal + interrupts and fetches are suppressed until the block I/O is done. +*/ + + case 0x96: /* WBR - RR */ + case 0xD6: /* WB - RXH */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + if (OP_TYPE (op) != OP_RR) + lim = ReadH ((ea + 2) & VAMASK); + else lim = R[(r2 + 1) & 0xF]; + if (opnd > lim) cc = 0; /* start > end? */ + else { /* no, start I/O */ + dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ + blk_io.dfl = dev; /* set status block */ + blk_io.cur = opnd; + blk_io.end = lim; + qevent = qevent | EV_BLK; /* I/O in prog */ + } + } + else cc = CC_V; /* nx dev */ + break; + + case 0x97: /* RBR - RR */ + case 0xD7: /* RB - RXH */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + if (OP_TYPE (op) != OP_RR) + lim = ReadH ((ea + 2) & VAMASK); + else lim = R[(r2 + 1) & 0xF]; + if (opnd > lim) cc = 0; /* start > end? */ + else { /* no, start I/O */ + dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ + blk_io.dfl = dev | BL_RD; /* set status block */ + blk_io.cur = opnd; + blk_io.end = lim; + qevent = qevent | EV_BLK; /* I/O in prog */ + } + } + else cc = CC_V; /* nx dev */ + break; + + case 0xD5: /* AL - RX */ + dev = ReadB (AL_DEV); /* get device */ + t = ReadB (AL_IOC); /* get command */ + if (DEV_ACC (dev)) { /* dev exist? */ + if (AL_BUF > ea) cc = 0; /* start > end? */ + else { /* no, start I/O */ + dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ + dev_tab[dev] (dev, IO_OC, t); /* start dev */ + blk_io.dfl = dev | BL_RD | BL_LZ; /* set status block */ + blk_io.cur = AL_BUF; + blk_io.end = ea; + qevent = qevent | EV_BLK; /* I/O in prog */ + } + } + else cc = CC_V; /* nx dev */ + break; + } /* end switch */ + } /* end while */ + +/* Simulation halted */ + +PSW = BUILD_PSW (cc); +PC = PC & VAMASK; +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Load new PSW and memory map */ + +uint32 newPSW (uint32 val) +{ +PSW = val & psw_mask; /* store PSW */ +int_eval (); /* update intreq */ +if (PSW & PSW_WAIT) qevent = qevent | EV_WAIT; /* wait state? */ +else qevent = qevent & ~EV_WAIT; +if (cpu_unit.flags & UNIT_816E) { /* mapping enabled? */ + uint32 map = PSW_GETMAP (PSW); /* get new map */ + s0_rel = s0_rel_const[map]; /* set relocation */ + s1_rel = s1_rel_const[map]; /* constants */ + } +else s0_rel = s1_rel = 0; /* no relocation */ +if (PSW & PSW_AIO) SET_ENB (v_DS); /* PSW<4> controls */ +else CLR_ENB (v_DS); /* DS interrupts */ +return PSW & CC_MASK; +} + +/* Swap PSW */ + +uint32 swap_psw (uint32 loc, uint32 cc) +{ +WriteH (loc, BUILD_PSW (cc)); /* write PSW, PC */ +WriteH (loc + 2, PC); +cc = newPSW (ReadH (loc + 4)); /* read PSW, PC */ +PC = ReadH (loc + 6); +if (PSW & PSW_SQI) cc = testsysq (cc); /* sys q int enb? */ +return cc; /* return CC */ +} + +/* Test for queue interrupts */ + +uint32 testsysq (uint32 cc) +{ +int32 qb = ReadH (SQP); /* get sys q addr */ +int32 usd = ReadB (qb + Q16_USD); /* get use count */ + +if (usd) { /* any entries? */ + WriteH (SQIPSW, BUILD_PSW (cc)); /* swap PSW */ + WriteH (SQIPSW + 2, PC); + cc = newPSW (ReadH (SQIPSW + 4)); + PC = ReadH (SQIPSW + 6); + } +return cc; +} + +/* Add to head of queue */ + +uint32 addtoq (uint32 ea, uint32 val, uint32 flg) +{ +uint32 slt, usd, wra, t; + +t = ReadH (ea); /* slots/used */ +slt = (t >> 8) & DMASK8; /* # slots */ +usd = t & DMASK8; /* # used */ +if (usd >= slt) return CC_V; /* list full? */ +usd = usd + 1; /* inc # used */ +WriteB (ea + Q16_USD, usd); /* rewrite */ +if (flg) { /* ABL? */ + wra = ReadB ((ea + Q16_BOT) & VAMASK); /* get bottom */ + t = wra + 1; /* adv bottom */ + if (t >= slt) t = 0; /* wrap if necc */ + WriteB ((ea + Q16_BOT) & VAMASK, t); /* rewrite bottom */ + } +else { /* ATL */ + wra = ReadB ((ea + Q16_TOP) & VAMASK); /* get top */ + if (wra == 0) wra = (slt - 1) & DMASK8; /* wrap if necc */ + else wra = wra - 1; /* dec top */ + WriteB ((ea + Q16_TOP) & VAMASK, wra); /* rewrite top */ + } +WriteH ((ea + Q16_BASE + (wra * Q16_SLNT)) & VAMASK, val); /* write slot */ +return 0; +} + +uint32 remfmq (uint32 ea, uint32 r1, uint32 flg) +{ +uint32 slt, usd, rda, t; + +t = ReadH (ea); /* get slots/used */ +slt = (t >> 8) & DMASK8; /* # slots */ +usd = t & DMASK8; /* # used */ +if (usd == 0) return CC_V; /* empty? */ +usd = usd - 1; /* dec used */ +WriteB (ea + Q16_USD, usd); /* rewrite */ +if (flg) { /* RBL? */ + rda = ReadB ((ea + Q16_BOT) & VAMASK); /* get bottom */ + if (rda == 0) rda = (slt - 1) & DMASK8; /* wrap if necc */ + else rda = rda - 1; /* dec bottom */ + WriteB ((ea + Q16_BOT) & VAMASK, rda); /* rewrite bottom */ + } +else { + rda = ReadB ((ea + Q16_TOP) & VAMASK); /* RTL, get top */ + t = rda + 1; /* adv top */ + if (t >= slt) t = 0; /* wrap if necc */ + WriteB ((ea + Q16_TOP) & VAMASK, t); /* rewrite top */ + } +R[r1] = ReadH ((ea + Q16_BASE + (rda * Q16_SLNT)) & VAMASK); /* read slot */ +if (usd) return CC_G; /* set cc's */ +else return 0; +} + +/* Automatic interrupt processing */ + +#define CCW16_ERR(x) (((x)|CCW16_INIT|CCW16_NOP|CCW16_Q) & \ + ~(CCW16_CHN|CCW16_CON|CCW16_HI)) + +uint32 int_auto (uint32 dev, uint32 cc) +{ +int32 ba, ea, by, vec, ccw, bpi, fnc, trm, st, i, t; +t_bool sysqe = FALSE; +t_bool rpt = FALSE; + +do { + vec = ReadH (INTSVT + dev + dev); /* get vector */ + if ((vec & 1) == 0) { /* immed int? */ + WriteH (vec, BUILD_PSW (cc)); /* write PSW, PC */ + WriteH ((vec + 2) & VAMASK, PC); + cc = newPSW (ReadH ((vec + 4) & VAMASK)); /* read PSW */ + PC = (vec + 6) & VAMASK; /* set new PC */ + return cc; + } + vec = vec & ~1; /* get CCW addr */ + ccw = ReadH (vec); /* read CCW */ + if (DEV_ACC (dev)) dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ + if (ccw & CCW16_NOP) break; /* NOP? exit */ + if (ccw & CCW16_INIT) { /* init set? */ + ccw = ccw & ~CCW16_INIT; /* clr init */ + WriteH (vec, ccw); /* rewrite */ + if (ccw & CCW16_OC) { /* OC set? */ + if (DEV_ACC (dev)) { /* dev exist? */ + by = ReadB ((vec + CCB16_IOC) & VAMASK);/* read OC byte */ + dev_tab[dev] (dev, IO_OC, by); /* send to dev */ + } + break; /* and exit */ + } + } + fnc = CCW16_FNC (ccw); /* get func */ + st = 0; /* default status */ + if (fnc == CCW16_DMT) { /* DMT */ + ba = ReadH ((vec + CCB16_STR) & VAMASK); /* get cnt wd */ + ba = (ba - 1) & DMASK16; /* decr */ + WriteH ((vec + CCB16_STR) & VAMASK, ba); /* rewrite */ + if (ba) break; /* nz? exit */ + } /* end if dmt */ + else if (fnc != CCW16_NUL) { /* rd or wr? */ + if (DEV_ACC (dev)) /* dev exist? */ + st = dev_tab[dev] (dev, IO_SS, 0); /* sense status */ + else st = CC_V; /* else timeout */ + if (st & 0xF) { /* error? */ + ccw = CCW16_ERR (ccw); /* neuter CCW */ + WriteH (vec, ccw); /* rewrite CCW */ + } + else { /* ok, do xfer */ + bpi = CCW16_BPI (ccw); /* get bytes/int */ + if (bpi == 0) bpi = 16; /* max 16B */ + ba = ReadH ((vec + CCB16_STR) & VAMASK); /* get start */ + for (i = 0; i < bpi; i++) { /* do # bytes */ + if (fnc == CCW16_RD) { /* chan read? */ + by = dev_tab[dev] (dev, IO_RD, 0); /* read byte */ + WriteB (ba, by); /* store */ + } + else { /* chan write */ + by = ReadB (ba); /* fetch */ + dev_tab[dev] (dev, IO_WD, by); /* write byte */ + } + ba = (ba + 1) & VAMASK; /* incr addr */ + } + WriteH ((vec + CCB16_STR) & VAMASK, ba); /* rewrite */ + ea = ReadH ((vec + CCB16_END) & VAMASK); /* get end */ + trm = ReadB ((vec + CCB16_TRM) & VAMASK); /* get term chr */ + if ((ba <= ea) && /* not at end? */ + (((ccw & CCW16_TRM) == 0) || /* not term chr? */ + (by != trm))) break; /* exit */ + ccw = ccw | CCW16_NOP; /* nop CCW */ + WriteH (vec, ccw); /* rewrite CCW */ + } /* end else sta */ + } /* end if r/w */ + +/* Termination phase */ + + t = (dev << 8) | (st & DMASK8); /* form dev/sta */ + WriteH ((vec + CCB16_DEV) & VAMASK, t); /* write dev/sta */ + if (ccw & CCW16_Q) { /* q request? */ + t = ReadH (SQP); /* get sys q addr */ + if (addtoq (t, vec, ccw & CCW16_HI)) { /* add to sys q */ + WriteH (SQOP, vec); /* write to ovflo */ + return swap_psw (SQVPSW, cc); /* take exception */ + } + else sysqe = TRUE; /* made an entry */ + } + if (ccw & CCW16_CHN) { /* chain */ + t = ReadH ((vec + CCB16_CHN) & VAMASK); /* get chain wd */ + WriteH (INTSVT + dev + dev, t); /* wr int svc tab */ + if (ccw & CCW16_CON) rpt = TRUE; /* cont? */ + } + } while (rpt); + +/* Common exit */ + +if (sysqe && (PSW & PSW_SQI)) /* sys q ent & enb? */ + return swap_psw (SQIPSW, cc); /* take sys q int */ +return cc; +} + +/* Display register device */ + +uint32 display (uint32 dev, uint32 op, uint32 dat) +{ +int t; + +switch (op) { + + case IO_ADR: /* select */ + if (!drmod) drpos = srpos = 0; /* norm mode? clr */ + return BY; /* byte only */ + + case IO_OC: /* command */ + op = op & 0xC0; + if (op == 0x40) { /* x40 = inc */ + drmod = 1; + drpos = srpos = 0; /* init cntrs */ + } + else if (op == 0x80) drmod = 0; /* x80 = norm */ + break; + + case IO_WD: /* write */ + if (drpos < 4) + DR = (DR & ~(DMASK8 << (drpos * 8))) | (dat << (drpos * 8)); + else if (drpos == 4) DRX = dat; + drpos = (drpos + 1) & + ((cpu_unit.flags & (UNIT_716 | UNIT_816))? 7: 3); + break; + + case IO_RD: /* read */ + t = (SR >> (srpos * 8)) & DMASK8; + srpos = srpos ^ 1; + return t; + + case IO_SS: /* status */ + return 0x80; + } + +return 0; +} + +/* Memory interface routines + + ReadB read byte (processor) + ReadH read halfword (processor) + ReadF read fullword (processor) + WriteB write byte (processor) + WriteH write halfword (processor) + WriteF write fullword (processor) + IOReadB read byte (IO) + IOWriteB write byte (IO) + IOReadH read halfword (IO) + IOWriteH write halfword (IO) +*/ + +uint32 ReadB (uint32 loc) +{ +uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + +return ((M[pa >> 1] >> ((pa & 1)? 0: 8)) & DMASK8); +} + +uint32 ReadH (uint32 loc) +{ +uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + +return M[pa >> 1]; +} + +uint32 ReadF (uint32 loc, uint32 rel) +{ +uint32 pa, pa1; +uint32 loc1 = (loc + 2) & VAMASK; + +loc = loc & VAMASK; /* FP doesn't mask */ +if (rel) { + pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + pa1 = (loc1 + ((loc1 & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + } +else { + pa = loc; + pa1 = loc1; + } +return (((uint32) M[pa >> 1]) << 16) | ((uint32) M[pa1 >> 1]); +} + +void WriteB (uint32 loc, uint32 val) +{ +uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + +val = val & DMASK8; +if (MEM_ADDR_OK (pa)) M[pa >> 1] = ((pa & 1)? + ((M[pa >> 1] & ~DMASK8) | val): + ((M[pa >> 1] & DMASK8) | (val << 8))); +return; +} + +void WriteH (uint32 loc, uint32 val) +{ +uint32 pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + +if (MEM_ADDR_OK (pa)) M[pa >> 1] = val & DMASK16; +return; +} + +void WriteF (uint32 loc, uint32 val, uint32 rel) +{ +uint32 pa, pa1; +uint32 loc1 = (loc + 2) & VAMASK; + +loc = loc & VAMASK; /* FP doesn't mask */ +if (rel) { + pa = (loc + ((loc & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + pa1 = (loc1 + ((loc1 & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + } +else { + pa = loc; + pa1 = loc1; + } +if (MEM_ADDR_OK (pa)) M[pa >> 1] = (val >> 16) & DMASK16; +if (MEM_ADDR_OK (pa1)) M[pa1 >> 1] = val & DMASK16; +return; +} + +uint32 IOReadB (uint32 loc) +{ +return ((M[loc >> 1] >> ((loc & 1)? 0: 8)) & DMASK8); +} + +void IOWriteB (uint32 loc, uint32 val) +{ +val = val & DMASK8; +M[loc >> 1] = ((loc & 1)? + ((M[loc >> 1] & ~DMASK8) | val): + ((M[loc >> 1] & DMASK8) | (val << 8))); +return; +} + +uint32 IOReadH (uint32 loc) +{ +return (M[loc >> 1] & DMASK16); +} + +void IOWriteH (uint32 loc, uint32 val) +{ +M[loc >> 1] = val & DMASK16; +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +qevent = 0; /* no events */ +newPSW (0); /* PSW = 0 */ +DR = 0; /* clr display */ +drmod = 0; +blk_io.dfl = blk_io.cur = blk_io.end = 0; /* no block IO */ +sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init bkpts */ +if (M == NULL) M = (uint16 *) calloc (MAXMEMSIZE16E >> 1, sizeof (uint16)); +if (M == NULL) return SCPE_MEM; +pcq_r = find_reg ("PCQ", NULL, dptr); /* init PCQ */ +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (sw & SWMASK ('V')) { + if (addr > VAMASK) return SCPE_NXM; + addr = (addr + ((addr & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + } +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = IOReadH (addr); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (sw & SWMASK ('V')) { + if (addr > VAMASK) return SCPE_NXM; + addr = (addr + ((addr & VA_S1)? s1_rel: s0_rel)) & PAMASK16E; + } +if (addr >= MEMSIZE) return SCPE_NXM; +IOWriteH (addr, val); +return SCPE_OK; +} + +/* Change memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || ((val & 0xFFF) != 0) || + (((uint32) val) > ((uptr->flags & UNIT_816E)? MAXMEMSIZE16E: MAXMEMSIZE16))) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i = i + 2) mc = mc | M[i >> 1]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE16E; i = i + 2) M[i >> 1] = 0; +return SCPE_OK; +} + +/* Change CPU model */ + +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i; + +if (!(val & UNIT_816E) && (MEMSIZE > MAXMEMSIZE16)) { + MEMSIZE = MAXMEMSIZE16; + for (i = MEMSIZE; i < MAXMEMSIZE16E; i = i + 2) M[i >> 1] = 0; + printf ("Reducing memory to 64KB\n"); + } +return SCPE_OK; +} + +/* Set console interrupt */ + +t_stat cpu_set_consint (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if ((uptr->flags & (UNIT_716 | UNIT_816 | UNIT_816E)) == 0) + return SCPE_NOFNC; +if (PSW & PSW_AIO) SET_INT (v_DS); +return SCPE_OK; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].vld = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (uint32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 op, k, di, lnt; +char *cptr = (char *) desc; +t_value sim_eval[2]; +t_stat r; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC r1 opnd ea IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(di++) % hst_lnt]; /* entry pointer */ + if (h->vld) { /* instruction? */ + fprintf (st, "%04X %04X %04X ", h->pc, h->r1, h->opnd); + op = (h->ir1 >> 8) & 0xFF; + if (OP_TYPE (op) >= OP_RX) fprintf (st, "%04X ", h->ea); + else fprintf (st, " "); + sim_eval[0] = h->ir1; + sim_eval[1] = h->ir2; + if ((fprint_sym (st, h->pc, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %04X", h->ir1); + fputc ('\n', st); /* end line */ + } /* end if instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/Interdata/id16_dboot.c b/Interdata/id16_dboot.c new file mode 100644 index 0000000..06ea5ac --- /dev/null +++ b/Interdata/id16_dboot.c @@ -0,0 +1,357 @@ +/* id16_dboot.c: Interdata 16b simulator disk bootstrap + + Copyright (c) 2000-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 17-Jul-06 RMS Fixed transcription error +*/ + +#include "id_defs.h" + +#define DBOOT_BEG 0x1000 +#define DBOOT_START 0x100e +#define DBOOT_LEN (sizeof (dboot_rom) / sizeof (uint8)) + +/* Boot ROM: transcription of OS/16 MT2 ALO Direct Access Loader */ + +static uint8 dboot_rom[] = { + 0xca, 0xf0, 0x00, 0x30, + 0xc5, 0xf0, 0x00, 0x3a, + 0x02, 0x8e, + 0x26, 0xf7, + 0x03, 0x0e, + 0xd1, 0xc0, 0x00, 0x78, + 0xd0, 0xc0, 0x13, 0xf6, + 0x07, 0xdd, + 0xc8, 0x10, 0x10, 0x00, + 0xd3, 0xf0, 0x00, 0x7e, + 0xc4, 0xf0, 0x00, 0x0f, + 0x01, 0xe1, + 0xd2, 0xf0, 0x12, 0xe2, + 0xd3, 0xf0, 0x00, 0x7f, + 0x90, 0xf4, + 0x01, 0xe1, + 0xd2, 0xf0, 0x12, 0xe3, + 0xd3, 0xf0, 0x00, 0x7f, + 0xc4, 0xf0, 0x00, 0x0f, + 0x01, 0xe1, + 0xd2, 0xf0, 0x12, 0xe4, + 0xd3, 0x20, 0x00, 0x7d, + 0xd3, 0x30, 0x00, 0x7c, + 0xd3, 0x40, 0x00, 0x7a, + 0xd3, 0x50, 0x00, 0x7b, + 0xc8, 0x70, 0x12, 0xf6, + 0xc8, 0x80, 0x13, 0xf5, + 0x07, 0xaa, + 0x07, 0xcc, + 0x41, 0xe0, 0x11, 0x88, + 0x48, 0xa0, 0x12, 0xfe, + 0x48, 0xc0, 0x13, 0x00, + 0x43, 0x00, 0x10, 0x98, + 0xc8, 0x70, 0x12, 0xf6, + 0x41, 0xe0, 0x11, 0x88, + 0xc8, 0xe0, 0x12, 0xfa, + 0x24, 0x15, + 0xd3, 0x0e, 0x00, 0x24, + 0xc3, 0x00, 0x00, 0x10, + 0x21, 0x3f, + 0xca, 0xe0, 0x00, 0x30, + 0x27, 0x11, + 0x20, 0x38, + 0x48, 0xa0, 0x12, 0xf6, + 0x48, 0xc0, 0x12, 0xf8, + 0x42, 0x30, 0x10, 0x70, + 0x08, 0xaa, + 0x20, 0x33, + 0x43, 0x00, 0x12, 0xb2, + 0x90, 0x05, + 0x42, 0x30, 0x10, 0x88, + 0xc8, 0x60, 0x4f, 0x53, + 0x45, 0x63, 0x00, 0x00, + 0x20, 0x36, + 0xc8, 0x60, 0x31, 0x36, + 0x45, 0x6e, 0x00, 0x02, + 0x20, 0x3b, + 0x48, 0x6e, 0x00, 0x08, + 0x45, 0x60, 0x12, 0xe2, + 0x20, 0x35, + 0xd3, 0x6e, 0x00, 0x0a, + 0xd4, 0x60, 0x12, 0xe4, + 0x20, 0x3a, + 0x08, 0x0e, + 0x07, 0x66, + 0xca, 0x60, 0x20, 0x00, + 0x23, 0x36, + 0x40, 0x06, 0x00, 0x00, + 0x45, 0x06, 0x00, 0x00, + 0x22, 0x37, + 0x48, 0xae, 0x00, 0x0c, + 0x48, 0xce, 0x00, 0x0e, + 0x48, 0x0e, 0x00, 0x10, + 0x48, 0x1e, 0x00, 0x12, + 0x0b, 0x1c, + 0x0f, 0x0a, + 0x07, 0xff, + 0x26, 0x11, + 0x0e, 0x0f, + 0xed, 0x00, 0x00, 0x08, + 0xcb, 0x60, 0x02, 0xbe, + 0x08, 0x00, + 0x23, 0x34, + 0x08, 0x86, + 0x08, 0x16, + 0x23, 0x04, + 0x05, 0x16, + 0x22, 0x84, + 0x08, 0x81, + 0x07, 0x77, + 0x27, 0x81, + 0xc8, 0xd1, 0xee, 0xc0, + 0xc8, 0xf0, 0x11, 0x40, + 0x48, 0x0f, 0x00, 0x00, + 0x40, 0x01, 0x00, 0x00, + 0x26, 0xf2, + 0x26, 0x12, + 0xc5, 0xf0, 0x13, 0xfe, + 0x20, 0x88, + 0x0a, 0xed, + 0x40, 0xed, 0x12, 0xf4, + 0x43, 0x0d, 0x11, 0x40, + 0x41, 0xed, 0x11, 0x88, + 0xd1, 0xed, 0x13, 0xf6, + 0xd0, 0xe0, 0x00, 0x78, + 0xd1, 0xed, 0x13, 0xfa, + 0xd0, 0xe0, 0x00, 0x7c, + 0x48, 0x10, 0x00, 0x62, + 0x48, 0x6d, 0x12, 0xf4, + 0xd1, 0xa6, 0x00, 0x00, + 0xd0, 0xa1, 0x00, 0x30, + 0xd1, 0xe6, 0x00, 0x0c, + 0xd0, 0xe1, 0x00, 0x28, + 0x43, 0x00, 0x00, 0x60, + 0x07, 0x00, + 0x07, 0xbb, + 0x0b, 0xcf, + 0x0f, 0xab, + 0x21, 0x13, + 0x26, 0x01, + 0x22, 0x04, + 0x0a, 0xcf, + 0x0e, 0xab, + 0x08, 0xac, + 0x08, 0xc0, + 0x03, 0x0e, + 0xde, 0x2d, 0x12, 0x1e, + 0xc5, 0x50, 0x00, 0x33, + 0x42, 0x2d, 0x11, 0xec, + 0xde, 0x3d, 0x12, 0x1e, + 0x9d, 0x3f, + 0x22, 0x21, + 0x9d, 0x4f, + 0x42, 0x1d, 0x12, 0xb8, + 0xc3, 0xf0, 0x00, 0x10, + 0x20, 0x35, + 0xd0, 0xad, 0x13, 0xea, + 0xc8, 0xf0, 0x00, 0x30, + 0x41, 0xed, 0x11, 0x70, + 0x08, 0x9c, + 0x08, 0xba, + 0x48, 0xad, 0x13, 0xea, + 0x48, 0xcd, 0x13, 0xee, + 0xd1, 0xed, 0x13, 0xf2, + 0xc5, 0xb0, 0x00, 0x18, + 0x21, 0x82, + 0x26, 0xb8, + 0x98, 0x49, + 0xde, 0x4d, 0x12, 0xe7, + 0x9d, 0x3f, + 0x22, 0x21, + 0x9d, 0x4f, + 0x42, 0x7d, 0x12, 0xb8, + 0x20, 0x83, + 0x98, 0x27, + 0x98, 0x28, + 0x98, 0x49, + 0x9a, 0x3b, + 0x41, 0x6d, 0x12, 0x7a, + 0x22, 0x0f, + 0x9d, 0x4f, + 0xc3, 0xf0, 0x00, 0x19, + 0x42, 0x3d, 0x12, 0xb8, + 0xd0, 0xad, 0x13, 0xea, + 0xc8, 0xf5, 0xff, 0xcc, + 0x0a, 0xff, + 0x48, 0xff, 0x12, 0xec, + 0x41, 0xed, 0x11, 0x70, + 0x08, 0x9c, + 0x08, 0xca, + 0x07, 0xaa, + 0xc8, 0xf5, 0xff, 0xcc, + 0xd3, 0xff, 0x12, 0xe8, + 0x41, 0xed, 0x11, 0x70, + 0x40, 0xcd, 0x12, 0xf2, + 0x08, 0xba, + 0x48, 0xad, 0x13, 0xea, + 0x48, 0xcd, 0x13, 0xee, + 0xd1, 0xed, 0x13, 0xf2, + 0xde, 0x4d, 0x12, 0x6e, + 0x9d, 0x3f, + 0x22, 0x21, + 0x98, 0x49, + 0xde, 0x4d, 0x12, 0xd0, + 0x9d, 0x3f, + 0x22, 0x21, + 0xde, 0x4d, 0x12, 0xa2, + 0x9d, 0x3f, + 0x22, 0x21, + 0xd8, 0x4d, 0x12, 0xf2, + 0xde, 0x4d, 0x12, 0xd1, + 0x9d, 0x3f, + 0x22, 0x21, + 0xde, 0x4d, 0x12, 0xe7, + 0x9d, 0x3f, + 0x22, 0x21, + 0x9d, 0x4f, + 0x20, 0x81, + 0xc3, 0xf0, 0x00, 0x53, + 0x42, 0x3d, 0x12, 0xb8, + 0x48, 0xfd, 0x12, 0xf2, + 0x91, 0xfa, + 0x06, 0xf9, + 0xc8, 0x6d, 0x12, 0x2c, + 0x98, 0x27, + 0x98, 0x28, + 0x9a, 0x3b, + 0x98, 0x3f, + 0xde, 0x3d, 0x12, 0xe6, + 0xde, 0x2d, 0x11, 0xaf, + 0x9d, 0x2f, + 0x20, 0x81, + 0xde, 0x2d, 0x12, 0x1e, + 0x99, 0x20, + 0xde, 0x2d, 0x12, 0x1e, + 0x9d, 0x3f, + 0x22, 0x21, + 0x42, 0x1d, 0x12, 0xbc, + 0xc3, 0xf0, 0x00, 0x10, + 0x03, 0x3e, + 0x0b, 0x07, + 0x26, 0x02, + 0xc4, 0x00, 0xff, 0x00, + 0x0a, 0x70, + 0x26, 0x91, + 0x07, 0xbb, + 0x40, 0xbd, 0x12, 0xf2, + 0x03, 0x06, + 0x24, 0xf1, + 0x24, 0x10, + 0x23, 0x04, + 0x08, 0x14, + 0x23, 0x02, + 0x08, 0x13, + 0x24, 0x01, + 0xde, 0x0d, 0x10, 0xdc, + 0x9a, 0x0f, + 0x9a, 0x01, + 0xde, 0x0d, 0x12, 0xe5, + 0xd1, 0xed, 0x13, 0xf6, + 0xd0, 0xe0, 0x00, 0x78, + 0xd1, 0xed, 0x13, 0xfa, + 0xd0, 0xe0, 0x00, 0x7c, + 0x91, 0x0f, + 0x95, 0x10, + 0x22, 0x01, + 0x00, 0x00, 0x00, + 0x80, + 0xc1, + 0xc2, + 0x14, 0x40, 0x40, 0x00, + 0x01, 0x90, + 0x01, 0x40, + 0x04, 0xc0, + 0x00, 0x00, + 0x00, 0x00 + }; + +/* Lower memory setup + + 78 = binary input device address + 79 = binary device input command + 7A = disk device number + 7B = device code + 7C = disk controller address + 7D = selector channel address + 7E:7F = operating system extension (user specified) +*/ + +struct dboot_id { + char *name; + uint32 sw; + uint32 cap; + uint32 dtype; + uint32 offset; + uint32 adder; + }; + +static struct dboot_id dboot_tab[] = { + { "DP", 0, 2, 0x31, o_DP0, 0 }, + { "DP", SWMASK ('F'), 9, 0x32, o_DP0, o_DPF }, + { "DP", 0, 9, 0x33, o_DP0, 0 }, + { "DM", 0, 64, 0x35, o_ID0, 0 }, + { "DM", 0, 244, 0x36, o_ID0, 0 }, + { NULL } + }; + +t_stat id_dboot (int32 u, DEVICE *dptr) +{ +extern DIB pt_dib, sch_dib; +extern uint32 PC; +uint32 i, typ, ctlno, off, add, cap, sch_dev; +UNIT *uptr; + +DIB *ddib = (DIB *) dptr->ctxt; /* get disk DIB */ +ctlno = ddib->dno; /* get ctrl devno */ +sch_dev = sch_dib.dno + ddib->sch; /* sch dev # */ +uptr = dptr->units + u; /* get capacity */ +cap = uptr->capac >> 20; +for (i = typ = 0; dboot_tab[i].name != NULL; i++) { + if ((strcmp (dboot_tab[i].name, dptr->name) == 0) && + (dboot_tab[i].cap == cap)) { + typ = dboot_tab[i].dtype; + off = dboot_tab[i].offset; + add = dboot_tab[i].adder; + break; + } + } +if (typ == 0) return SCPE_NOFNC; + +IOWriteBlk (DBOOT_BEG, DBOOT_LEN, dboot_rom); /* copy boot */ +IOWriteB (AL_DEV, pt_dib.dno); /* bin input dev */ +IOWriteB (AL_IOC, 0x99); +IOWriteB (AL_DSKU, ctlno + ((u + 1) * off) + add); /* disk param */ +IOWriteB (AL_DSKT, typ); +IOWriteB (AL_DSKC, ctlno); +IOWriteB (AL_SCH, sch_dev); +PC = DBOOT_START; +return SCPE_OK; +} diff --git a/Interdata/id16_sys.c b/Interdata/id16_sys.c new file mode 100644 index 0000000..97ae655 --- /dev/null +++ b/Interdata/id16_sys.c @@ -0,0 +1,604 @@ +/* id16_sys.c: Interdata 16b simulator interface + + Copyright (c) 2000-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 04-Feb-08 RMS Modified to allow -A, -B use with 8b devices + 18-Oct-06 RMS Re-ordered device list + 26-Mar-04 RMS Fixed warning with -std=c99 + 27-Feb-03 RMS Added relative addressing support +*/ + +#include "id_defs.h" +#include + +#define MSK_SBF 0x0100 + +extern DEVICE cpu_dev; +extern DEVICE sch_dev; +extern DEVICE pt_dev; +extern DEVICE tt_dev, ttp_dev; +extern DEVICE pas_dev, pasl_dev; +extern DEVICE lpt_dev; +extern DEVICE pic_dev, lfc_dev; +extern DEVICE dp_dev, idc_dev; +extern DEVICE fd_dev, mt_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint16 *M; + +t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val); +t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val); +extern t_stat lp_load (FILE *fileref, char *cptr, char *fnam); +extern t_stat pt_dump (FILE *of, char *cptr, char *fnam); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "Interdata 16b"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 2; + +DEVICE *sim_devices[] = { + &cpu_dev, + &sch_dev, + &pic_dev, + &lfc_dev, + &pt_dev, + &tt_dev, + &ttp_dev, + &pas_dev, + &pasl_dev, + &lpt_dev, + &dp_dev, + &idc_dev, + &fd_dev, + &mt_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Reserved instruction", + "HALT instruction", + "Breakpoint", + "Wait state", + "Runaway VFU" + }; + +/* Binary loader -- load carriage control tape + Binary dump -- paper tape dump */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +if (flag) return pt_dump (fileref, cptr, fnam); +return lp_load (fileref, cptr, fnam); +} + +/* Symbol tables */ + +#define I_V_FL 16 /* class bits */ +#define I_M_FL 0xF /* class mask */ +#define I_V_MR 0x0 /* mask-register */ +#define I_V_RR 0x1 /* register-register */ +#define I_V_R 0x2 /* register */ +#define I_V_MX 0x3 /* mask-memory */ +#define I_V_RX 0x4 /* register-memory */ +#define I_V_X 0x5 /* memory */ +#define I_V_FF 0x6 /* float reg-reg */ +#define I_V_FX 0x7 /* float reg-mem */ +#define I_V_SI 0x8 /* short immed */ +#define I_V_SB 0x9 /* short branch */ +#define I_V_SX 0xA /* short ext branch */ +#define I_MR (I_V_MR << I_V_FL) +#define I_RR (I_V_RR << I_V_FL) +#define I_R (I_V_R << I_V_FL) +#define I_MX (I_V_MX << I_V_FL) +#define I_RX (I_V_RX << I_V_FL) +#define I_X (I_V_X << I_V_FL) +#define I_FF (I_V_FF << I_V_FL) +#define I_FX (I_V_FX << I_V_FL) +#define I_SI (I_V_SI << I_V_FL) +#define I_SB (I_V_SB << I_V_FL) +#define I_SX (I_V_SX << I_V_FL) + +#define R_X 0 /* no reg */ +#define R_M 1 /* reg mask */ +#define R_R 2 /* reg int reg */ +#define R_F 3 /* reg flt reg */ + +static const int32 masks[] = { + 0xFF00, 0xFF00, 0xFFF0, 0xFF00, + 0xFF00, 0xFFF0, 0xFF00, 0xFF00, + 0xFF00, 0xFE00, 0xFEF0 + }; + +static const uint32 r1_type[] = { + R_M, R_R, R_X, R_M, + R_R, R_X, R_F, R_F, + R_R, R_M, R_X + }; + +static const uint32 r2_type[] = { + R_X, R_R, R_R, R_X, + R_X, R_X, R_F, R_X, + R_M, R_X, R_X + }; + +static const char *opcode[] = { +"BER", "BNER","BZR", "BNZR", +"BPR", "BNPR","BLR", "BNLR", +"BMR", "BNMR","BOR", "BNOR", +"BCR", "BNCR","BR", +"BES", "BNES","BZS", "BNZS", +"BPS", "BNPS","BLS", "BNLS", +"BMS", "BNMS","BOS", "BNOS", +"BCS", "BNCS","BS", +"BE", "BNE", "BZ", "BNZ", +"BP", "BNP", "BL", "BNL", +"BM", "BNM", "BO", "BNO", +"BC", "BNC", "B", + "BALR","BTCR","BFCR", +"NHR", "CLHR","OHR", "XHR", +"LHR", "CHR", "AHR", "SHR", +"MHR", "DHR", "ACHR","SCHR", + "SETMR", +"BTBS","BTFS","BFBS","BFFS", +"LIS", "LCS", "AIS", "SIS", +"LER", "CER", "AER", "SER", +"MER", "DER", "FXR", "FLR", + "LPSR", +"LDR", "CDR", "ADR", "SDR", +"MDR", "DDR", "FXDR","FLDR", +"STH", "BAL", "BTC", "BFC", +"NH", "CLH", "OH", "XH", +"LH", "CH", "AH", "SH", +"MH", "DH", "ACH", "SCH", + "SETM", +"STE", "AHM", +"ATL", "ABL", "RTL", "RBL", +"LE", "CE", "AE", "SE", +"ME", "DE", +"STD", "STME","LME", "LPS", +"LD", "CD", "AD", "SD", +"MD", "DD", "STMD","LMD", +"SRLS","SLLS","STBR","LBR", +"EXBR","EPSR","WBR", "RBR", +"WHR", "RHR", "WDR", "RDR", +"MHUR","SSR", "OCR", "AIR", +"BXH", "BXLE","LPSW","THI", +"NHI", "CLHI","OHI", "XHI", +"LHI", "CHI", "AHI", "SHI", +"SRHL","SLHL","SRHA","SLHA", +"STM", "LM", "STB", "LB", +"CLB", "AL", "WB", "RB", +"WH", "RH", "WD", "RD", +"MHU", "SS", "OC", "AI", + "SVC", "SINT", + "RRL", "RLL", +"SRL", "SLL", "SRA", "SLA", +NULL +}; + +static const uint32 opc_val[] = { +0x0330+I_R, 0x0230+I_R, 0x0330+I_R, 0x0230+I_R, +0x0220+I_R, 0x0320+I_R, 0x0280+I_R, 0x0380+I_R, +0x0210+I_R, 0x0310+I_R, 0x0240+I_R, 0x0340+I_R, +0x0280+I_R, 0x0380+I_R, 0x0300+I_R, +0x2230+I_SX, 0x2030+I_SX, 0x2230+I_SX, 0x2030+I_SX, +0x2020+I_SX, 0x2220+I_SX, 0x2080+I_SX, 0x2280+I_SX, +0x2010+I_SX, 0x2210+I_SX, 0x2040+I_SX, 0x2240+I_SX, +0x2080+I_SX, 0x2280+I_SX, 0x2200+I_SX, +0x4330+I_X, 0x4230+I_X, 0x4330+I_X, 0x4230+I_X, +0x4220+I_X, 0x4320+I_X, 0x4280+I_X, 0x4380+I_X, +0x4210+I_X, 0x4310+I_X, 0x4240+I_X, 0x4340+I_X, +0x4280+I_X, 0x4380+I_X, 0x4300+I_X, + 0x0100+I_RR, 0x0200+I_MR, 0x0300+I_MR, +0x0400+I_RR, 0x0500+I_RR, 0x0600+I_RR, 0x0700+I_RR, +0x0800+I_RR, 0x0900+I_RR, 0x0A00+I_RR, 0x0B00+I_RR, +0x0C00+I_RR, 0x0D00+I_RR, 0x0E00+I_RR, 0x0F00+I_RR, + 0x1300+I_RR, +0x2000+I_SB, 0x2100+I_SB, 0x2200+I_SB, 0x2300+I_SB, +0x2400+I_SI, 0x2500+I_SI, 0x2600+I_SI, 0x2700+I_SI, +0x2800+I_FF, 0x2900+I_FF, 0x2A00+I_FF, 0x2B00+I_FF, +0x2C00+I_FF, 0x2D00+I_FF, 0x2E00+I_RR, 0x2F00+I_RR, + 0x3300+I_R, +0x3800+I_FF, 0x3900+I_FF, 0x3A00+I_FF, 0x3B00+I_FF, +0x3C00+I_FF, 0x3D00+I_FF, 0x3E00+I_RR, 0x3F00+I_RR, +0x4000+I_RX, 0x4100+I_RX, 0x4200+I_MX, 0x4300+I_MX, +0x4400+I_RX, 0x4500+I_RX, 0x4600+I_RX, 0x4700+I_RX, +0x4800+I_RX, 0x4900+I_RX, 0x4A00+I_RX, 0x4B00+I_RX, +0x4C00+I_RX, 0x4D00+I_RX, 0x4E00+I_RX, 0x4F00+I_RX, + 0x5300+I_RX, +0x6000+I_RX, 0x6100+I_RX, +0x6400+I_RX, 0x6500+I_RX, 0x6600+I_RX, 0x6700+I_RX, +0x6800+I_FX, 0x6900+I_FX, 0x6A00+I_FX, 0x6B00+I_FX, +0x6C00+I_FX, 0x6D00+I_FX, +0x7000+I_FX, 0x7100+I_FX, 0x7200+I_FX, 0x7300+I_X, +0x7800+I_FX, 0x7900+I_FX, 0x7A00+I_FX, 0x7B00+I_FX, +0x7C00+I_FX, 0x7D00+I_FX, 0x7E00+I_FX, 0x7F00+I_FX, +0x9000+I_SI, 0x9100+I_SI, 0x9200+I_RR, 0x9300+I_RR, +0x9400+I_RR, 0x9500+I_RR, 0x9600+I_RR, 0x9700+I_RR, +0x9800+I_RR, 0x9900+I_RR, 0x9A00+I_RR, 0x9B00+I_RR, +0x9C00+I_RR, 0x9D00+I_RR, 0x9E00+I_RR, 0x9F00+I_RR, +0xC000+I_RX, 0xC100+I_RX, 0xC200+I_X, 0xC300+I_RX, +0xC400+I_RX, 0xC500+I_RX, 0xC600+I_RX, 0xC700+I_RX, +0xC800+I_RX, 0xC900+I_RX, 0xCA00+I_RX, 0xCB00+I_RX, +0xCC00+I_RX, 0xCD00+I_RX, 0xCE00+I_RX, 0xCF00+I_RX, +0xD000+I_RX, 0xD100+I_RX, 0xD200+I_RX, 0xD300+I_RX, +0xD400+I_RX, 0xD500+I_X, 0xD600+I_RX, 0xD700+I_RX, +0xD800+I_RX, 0xD900+I_RX, 0xDA00+I_RX, 0xDB00+I_RX, +0xDC00+I_RX, 0xDD00+I_RX, 0xDE00+I_RX, 0xDF00+I_RX, + 0xE100+I_RX, 0xE200+I_RX, + 0xEA00+I_RX, 0xEB00+I_RX, +0xEC00+I_RX, 0xED00+I_RX, 0xEE00+I_RX, 0xEF00+I_RX, +0xFFFF +}; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 bflag, c1, c2, rdx; +t_stat r; +DEVICE *dptr; + +if (uptr == NULL) uptr = &cpu_unit; /* anon = CPU */ +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) return SCPE_IERR; +if (dptr->dwidth < 16) bflag = 1; /* 8b dev? */ +else bflag = 0; /* assume 16b */ +if (sw & SWMASK ('D')) rdx = 10; /* get radix */ +else if (sw & SWMASK ('O')) rdx = 8; +else if (sw & SWMASK ('H')) rdx = 16; +else rdx = dptr->dradix; + +if (sw & SWMASK ('A')) { /* ASCII char? */ + if (bflag) c1 = val[0] & 0x7F; + else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0x7F; /* get byte */ + fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); + return 0; + } +if (sw & SWMASK ('B')) { /* byte? */ + if (bflag) c1 = val[0] & 0xFF; + else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0xFF; /* get byte */ + fprint_val (of, c1, rdx, 8, PV_RZRO); + return 0; + } +if (bflag) return SCPE_ARG; /* 16b only */ + +if (sw & SWMASK ('C')) { /* string? */ + c1 = (val[0] >> 8) & 0x7F; + c2 = val[0] & 0x7F; + fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return -1; + } +if (sw & SWMASK ('F')) { /* fullword? */ + fprint_val (of, (val[0] << 16) | val[1], rdx, 32, PV_RZRO); + return -3; + } +if (sw & SWMASK ('M')) { /* inst format? */ + r = fprint_sym_m (of, addr, val); /* decode inst */ + if (r <= 0) return r; + } + +fprint_val (of, val[0], rdx, 16, PV_RZRO); +return -1; +} + +/* Symbolic decode for -m + + Inputs: + of = output stream + addr = current PC + *val = values to decode + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val) +{ +uint32 i, j, inst, r1, r2, ea, vp; + +vp = 0; +inst = val[0]; /* first 16b */ +ea = val[1]; /* second 16b */ +for (i = 0; opcode[i] != NULL; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 0xFFFF) == (inst & masks[j])) { /* match? */ + r1 = (inst >> 4) & 0xF; + r2 = inst & 0xF; + fprintf (of, "%s ", opcode[i]); /* print opcode */ + switch (j) { /* case on class */ + + case I_V_MR: /* mask-register */ + fprintf (of, "%-X,R%d", r1, r2); + return -1; + + case I_V_RR: /* register-register */ + case I_V_FF: /* floating-floating */ + fprintf (of, "R%d,R%d", r1, r2); + return -1; + + case I_V_SI: /* short immediate */ + fprintf (of, "R%d,%-X", r1, r2); + return -1; + + case I_V_SB: /* short branch */ + fprintf (of, "%-X,", r1); + case I_V_SX: /* ext short branch */ + fprintf (of, "%-X", ((inst & MSK_SBF)? + (addr + r2 + r2): (addr - r2 - r2))); + return -1; + + case I_V_R: /* register */ + fprintf (of, "R%d", r2); + return -1; + + case I_V_MX: /* mask-memory */ + fprintf (of, "%-X,%-X", r1, ea); + break; + + case I_V_RX: /* register-memory */ + case I_V_FX: /* floating-memory */ + fprintf (of, "R%d,%-X", r1, ea); + break; + + case I_V_X: /* memory */ + fprintf (of, "%-X", ea); + break; + } /* end case */ + + if (r2) fprintf (of, "(R%d)", r2); + return -3; + } /* end if */ + } /* end for */ +return SCPE_ARG; /* no match */ +} + +/* Register number + + Inputs: + *cptr = pointer to input string + **optr = pointer to pointer to next char + rtype = mask, integer, or float + Outputs: + rnum = output register number, -1 if error +*/ + +int32 get_reg (char *cptr, char **optr, int32 rtype) +{ +int32 reg; + +if ((*cptr == 'R') || (*cptr == 'r')) { /* R? */ + cptr++; /* skip */ + if (rtype == R_M) return -1; /* cant be mask */ + } +if ((*cptr >= '0') && (*cptr <= '9')) { + reg = *cptr++ - '0'; + if ((*cptr >= '0') && (*cptr <= '9')) + reg = (reg * 10) + (*cptr - '0'); + else --cptr; + if (reg > 0xF) return -1; + } +else if ((*cptr >= 'a') && (*cptr <= 'f')) reg = (*cptr - 'a') + 10; +else if ((*cptr >= 'A') && (*cptr <= 'F')) reg = (*cptr - 'A') + 10; +else return -1; +if ((rtype == R_F) && (reg & 1)) return -1; +*optr = cptr + 1; +return reg; +} + +/* Address + + Inputs: + *cptr = pointer to input string + **tptr = pointer to moved pointer + *ea = effective address + addr = base address + Outputs: + status = SCPE_OK if ok, else error code +*/ + +t_stat get_addr (char *cptr, char **tptr, t_addr *ea, t_addr addr) +{ +int32 sign = 1; + +if (*cptr == '.') { /* relative? */ + cptr++; + *ea = addr; + if (*cptr == '+') cptr++; /* .+? */ + else if (*cptr == '-') { /* .-? */ + sign = -1; + cptr++; + } + else return SCPE_OK; + } +else *ea = 0; +errno = 0; +*ea = *ea + (sign * ((int32) strtoul (cptr, tptr, 16))); +if (errno || (cptr == *tptr)) return SCPE_ARG; +return SCPE_OK; +} + +/* Symbolic input */ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 bflag, by, rdx, num; +t_stat r; +DEVICE *dptr; + +if (uptr == NULL) uptr = &cpu_unit; /* anon = CPU */ +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) return SCPE_IERR; +if (dptr->dwidth < 16) bflag = 1; /* 8b device? */ +else bflag = 0; /* assume 16b */ +if (sw & SWMASK ('D')) rdx = 10; /* get radix */ +else if (sw & SWMASK ('O')) rdx = 8; +else if (sw & SWMASK ('H')) rdx = 16; +else rdx = dptr->dradix; + +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + if (bflag) val[0] = (t_value) cptr[0]; + else val[0] = (addr & 1)? + (val[0] & ~0xFF) | ((t_value) cptr[0]): + (val[0] & 0xFF) | (((t_value) cptr[0]) << 8); + return 0; + } +if (sw & SWMASK ('B')) { /* byte? */ + by = get_uint (cptr, rdx, DMASK8, &r); /* get byte */ + if (r != SCPE_OK) return SCPE_ARG; + if (bflag) val[0] = by; + else val[0] = (addr & 1)? + (val[0] & ~0xFF) | by: + (val[0] & 0xFF) | (by << 8); + return 0; + } +if (bflag) return SCPE_ARG; /* 16b only */ + +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII chars? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((t_value) cptr[0] << 8) | (t_value) cptr[1]; + return -1; + } +if (sw & SWMASK ('F')) { + num = (int32) get_uint (cptr, rdx, DMASK32, &r); /* get number */ + if (r != SCPE_OK) return r; + val[0] = (num >> 16) & DMASK16; + val[1] = num & DMASK16; + return -3; + } + +r = parse_sym_m (cptr, addr, val); /* try to parse inst */ +if (r <= 0) return r; +val[0] = (int32) get_uint (cptr, rdx, DMASK16, &r); /* get number */ +if (r != SCPE_OK) return r; +return -1; +} + +/* Symbolic input for -m + + Inputs: + *cptr = pointer to input string + addr = current PC + *val = pointer to output values + cf = true if parsing for CPU + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val) +{ +uint32 i, j, t, df, db, inst; +int32 st, r1, r2; +t_stat r; +char *tptr, gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +inst = opc_val[i] & 0xFFFF; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ +if (r1_type[j]) { /* any R1 field? */ + cptr = get_glyph (cptr, gbuf, ','); /* get R1 field */ + if ((r1 = get_reg (gbuf, &tptr, r1_type[j])) < 0) + return SCPE_ARG; + if (*tptr != 0) return SCPE_ARG; /* all done? */ + inst = inst | (r1 << 4); /* or in R1 */ + } + +cptr = get_glyph (cptr, gbuf, 0); /* get operand */ +if (*cptr) return SCPE_ARG; /* should be end */ +switch (j) { /* case on class */ + + case I_V_FF: case I_V_SI: /* flt-flt, sh imm */ + case I_V_MR: case I_V_RR: /* mask/reg-reg */ + case I_V_R: /* register */ + if ((r2 = get_reg (gbuf, &tptr, r2_type[j])) < 0) + return SCPE_ARG; + if (*tptr != 0) return SCPE_ARG; /* all done? */ + inst = inst | r2; /* or in R2 */ + break; + + case I_V_FX: /* float-memory */ + case I_V_MX: case I_V_RX: /* mask/reg-mem */ + case I_V_X: /* memory */ + r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ + if ((r != SCPE_OK) || (t > PAMASK16)) return SCPE_ARG; + if (*tptr == '(') { /* index? */ + if ((r2 = get_reg (tptr + 1, &tptr, R_R)) < 0) + return SCPE_ARG; + if (*tptr++ != ')') return SCPE_ARG; + inst = inst | r2; /* or in R2 */ + } + if (*tptr != 0) return SCPE_ARG; + val[0] = inst; + val[1] = t; + return -3; + + case I_V_SB: case I_V_SX: /* short branches */ + r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ + if ((r != SCPE_OK) || (t & 1) || *tptr) /* error if odd */ + return SCPE_ARG; + st = t; /* signed version */ + db = (addr - t) & 0x1F; /* back displ */ + df = (t - addr) & 0x1F; /* fwd displ */ + if ((t == ((addr - db) & VAMASK16)) && /* back work and */ + ((j == I_V_SX) || !(inst & MSK_SBF))) /* ext or back br? */ + inst = inst | (db >> 1); /* or in back displ */ + else if ((t == ((addr + df) & VAMASK16)) && /* fwd work and */ + ((j == I_V_SX) || (inst & MSK_SBF))) /* ext or fwd br? */ + inst = inst | (df >> 1) | MSK_SBF; /* or in fwd displ */ + else return SCPE_ARG; + break; + } /* end case */ + +val[0] = inst; +return -1; +} diff --git a/Interdata/id32_cpu.c b/Interdata/id32_cpu.c new file mode 100644 index 0000000..455f2ef --- /dev/null +++ b/Interdata/id32_cpu.c @@ -0,0 +1,2331 @@ +/* id32_cpu.c: Interdata 32b CPU simulator + + Copyright (c) 2000-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu Interdata 32b CPU + + 28-Apr-07 RMS Removed clock initialization + 27-Oct-06 RMS Added idle support + Removed separate PASLA clock + 09-Mar-06 RMS Added 8 register bank support for 8/32 + 06-Feb-06 RMS Fixed bug in DH (found by Mark Hittinger) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 10-Mar-05 RMS Fixed bug in initial memory allocation + RMS Fixed bug in show history routine (from Mark Hittinger) + RMS Revised examine/deposit to do words rather than bytes + 18-Feb-05 RMS Fixed branches to mask new PC (from Greg Johnson) + 06-Nov-04 RMS Added =n to SHOW HISTORY + 25-Jan-04 RMS Revised for device debug support + 31-Dec-03 RMS Fixed bug in cpu_set_hist + 22-Sep-03 RMS Added additional instruction decode types + Added instruction history + + The register state for an Interdata 32b CPU is: + + REG[0:F][2]<0:31> general register sets + F[0:7]<0:31> single precision floating point registers + D[0:7]<0:63> double precision floating point registers + PSW<0:63> processor status word, including + STAT<0:11> status flags + CC<0:3> condition codes + PC<0:31> program counter + int_req[n]<0:31> interrupt requests + int_enb[n]<0:31> interrupt enables + + The Interdata 32b systems have seven instruction formats: register to + register, short format, register and memory (three formats), and register + and immediate (two formats). The formats are: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | R2 | register-register + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | N | short format + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-memory 1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ (absolute 14b) + | 0| 0| address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-memory 2 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ (relative) + | 1| address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-memory 3 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ (double index) + | 0| 1| 0| 0| RX2 | address hi | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | address lo | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-immediate 1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | immediate | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op | R1 | RX | register-immediate 2 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | immediate hi | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | immediate lo | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + For register-memory 1 and register-immediate 1 and 2 an instructions, an + effective address is calculated as follows: + + effective addr = address + RX (if RX > 0) + + For register-memory 2, an effective address is calculated as follows: + + effective addr = address + PC + RX (if RX > 0) + + For register-memory 3, an effective address is calculated as follows: + + effective addr = address + RX (if RX > 0) + RX2 (if RX2 > 0) + + Register-memory instructions can access an address space of 16M bytes. + + This routine is the instruction decode routine for the Interdata CPU. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + wait state and no I/O outstanding + invalid instruction + I/O error in I/O simulator + + 2. Interrupts. Each device has an interrupt armed flag, an interrupt + request flag, and an interrupt enabled flag. To facilitate evaluation, + all interrupt requests are kept in int_req, and all enables in int_enb. + Interrupt armed flags are local to devices. If external interrupts are + enabled in the PSW, and a request is pending, an interrupt occurs. + + 3. Non-existent memory. On the Interdata 32b, reads to non-existent + memory return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + id_defs.h add device interrupt definitions + id32_sys.c add sim_devices table entry +*/ + +#include "id_defs.h" +#include + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = oPC +#define VAMASK VAMASK32 +#define NRSETS 8 /* up to 8 reg sets */ +#define PSW_MASK PSW_x32 +#define ABORT(val) longjmp (save_env, (val)) +#define MPRO (-1) + +#define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy mask */ +#define UNIT_V_DPFP (UNIT_V_UF + 1) +#define UNIT_V_832 (UNIT_V_UF + 2) +#define UNIT_V_8RS (UNIT_V_UF + 3) +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define UNIT_DPFP (1 << UNIT_V_DPFP) +#define UNIT_832 (1 << UNIT_V_832) +#define UNIT_8RS (1 << UNIT_V_8RS) +#define UNIT_TYPE (UNIT_DPFP | UNIT_832) + +#define HIST_PC 0x40000000 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + uint32 pc; + uint32 ir1; + uint32 ir2; + uint32 ir3; + uint32 r1; + uint32 ea; + uint32 opnd; + } InstHistory; + +#define PSW_GETREG(x) (((x) >> PSW_V_REG) & psw_reg_mask) +#define SEXT32(x) (((x) & SIGN32)? ((int32) ((x) | ~0x7FFFFFFF)): \ + ((int32) ((x) & 0x7FFFFFFF))) +#define SEXT16(x) (((x) & SIGN16)? ((int32) ((x) | ~0x7FFF)): \ + ((int32) ((x) & 0x7FFF))) +#define SEXT15(x) (((x) & 0x4000)? ((int32) ((x) | ~0x3FFF)): \ + ((int32) ((x) & 0x3FFF))) +#define CC_GL_16(x) if ((x) & SIGN16) cc = CC_L; \ + else if (x) cc = CC_G; \ + else cc = 0 +#define CC_GL_32(x) if ((x) & SIGN32) cc = CC_L; \ + else if (x) cc = CC_G; \ + else cc = 0 +#define BUILD_PSW(x) (((PSW & ~CC_MASK) | (x)) & PSW_MASK) +#define NEG(x) ((~(x) + 1) & DMASK32) +#define ABS(x) (((x) & SIGN32)? NEG (x): (x)) +#define DNEG(x,y) y = NEG (y); \ + x = (~(x) + (y == 0)) & DMASK32 + +/* Logging */ + +#define LOG_CPU_I 0x0001 /* intr/exception */ +#define LOG_CPU_C 0x0002 /* context change */ + +uint32 GREG[16 * NRSETS] = { 0 }; /* general registers */ +uint32 *M = NULL; /* memory */ +uint32 *R = &GREG[0]; /* working reg set */ +uint32 F[8] = { 0 }; /* sp fp registers */ +dpr_t D[8] = { 0 }; /* dp fp registers */ +uint32 PSW = 0; /* processor status word */ +uint32 PC = 0; /* program counter */ +uint32 oPC = 0; /* PC at inst start */ +uint32 SR = 0; /* switch register */ +uint32 DR = 0; /* display register */ +uint32 DRX = 0; /* display extension */ +uint32 drmod = 0; /* mode */ +uint32 srpos = 0; /* switch register pos */ +uint32 drpos = 0; /* display register pos */ +uint32 mac_reg[MAC_LNT] = { 0 }; /* mac registers */ +uint32 mac_sta = 0; /* mac status */ +uint32 int_req[INTSZ] = { 0 }; /* interrupt requests */ +uint32 int_enb[INTSZ] = { 0 }; /* interrupt enables */ +uint32 qevent = 0; /* events */ +uint32 stop_inst = 0; /* stop on ill inst */ +uint32 stop_wait = 0; /* stop on wait */ +uint32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +uint32 dec_flgs = 0; /* decode flags */ +uint32 fp_in_hwre = 0; /* ucode vs hwre fp */ +uint32 pawidth = PAWIDTH32; /* addr mask */ +uint32 hst_p = 0; /* history pointer */ +uint32 hst_lnt = 0; /* history length */ +uint32 psw_reg_mask = 1; /* PSW reg mask */ +InstHistory *hst = NULL; /* instruction history */ +jmp_buf save_env; /* abort handler */ +struct BlockIO blk_io; /* block I/O status */ +uint32 (*dev_tab[DEVNO])(uint32 dev, uint32 op, uint32 datout) = { NULL }; + +extern int32 sim_interval; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern t_bool sim_idle_enab; +extern FILE *sim_deb; + +uint32 ReadB (uint32 loc, uint32 rel); +uint32 ReadH (uint32 loc, uint32 rel); +void WriteB (uint32 loc, uint32 val, uint32 rel); +void WriteH (uint32 loc, uint32 val, uint32 rel); +uint32 RelocT (uint32 va, uint32 base, uint32 rel, uint32 *pa); +uint32 int_auto (uint32 dev, uint32 cc); +uint32 addtoq (uint32 ea, uint32 val, uint32 flg); +uint32 remfmq (uint32 ea, uint32 r1, uint32 flg); +uint32 exception (uint32 loc, uint32 cc, uint32 flg); +uint32 newPSW (uint32 val); +uint32 testsysq (uint32 cc); +uint32 display (uint32 dev, uint32 op, uint32 dat); +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_consint (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +void set_r_display (uint32 *rbase); + +extern t_bool devtab_init (void); +extern void int_eval (void); +extern uint32 int_getdev (void); +extern void sch_cycle (uint32 ch); +extern t_bool sch_blk (uint32 dev); +extern uint32 f_l (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_c (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_as (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_m (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_d (uint32 op, uint32 r1, uint32 r2, uint32 ea); +extern uint32 f_fix32 (uint32 op, uint32 r1, uint32 r2); +extern uint32 f_flt32 (uint32 op, uint32 r1, uint32 r2); + +/* Instruction decoding table */ + +const uint16 decrom[256] = { + 0, /* 00 */ + OP_RR, /* BALR */ + OP_RR, /* BTCR */ + OP_RR, /* BFCR */ + OP_RR, /* NR */ + OP_RR, /* CLR */ + OP_RR, /* OR */ + OP_RR, /* XR */ + OP_RR, /* LR */ + OP_RR, /* CR */ + OP_RR, /* AR */ + OP_RR, /* SR */ + OP_RR, /* MHR */ + OP_RR, /* DHR */ + 0, 0, /* 0E:0F */ + OP_NO, /* SRLS */ + OP_NO, /* SLLS */ + OP_RR, /* CHVR */ + 0, 0, 0, 0, 0, /* 13:17 */ + OP_RR | OP_PRV, /* LPSWR */ + 0, 0, 0, /* 19:1B */ + OP_RR, /* MR */ + OP_RR, /* DR */ + 0, 0, /* 1E:1F */ + OP_NO, /* BTBS */ + OP_NO, /* BTFS */ + OP_NO, /* BFBS */ + OP_NO, /* BFFS */ + OP_NO, /* LIS */ + OP_NO, /* LCS */ + OP_NO, /* AIS */ + OP_NO, /* SIS */ + OP_NO, /* LER */ + OP_NO, /* CER */ + OP_NO, /* AER */ + OP_NO, /* SER */ + OP_NO, /* MER */ + OP_NO, /* DER */ + OP_NO, /* FXR */ + OP_NO, /* FLR */ + 0, /* MPBSR - 8/32C */ + 0, /* 31 */ + 0, /* PBR - 8/32C */ + 0, /* 33 */ + OP_RR, /* EXHR */ + 0, 0, 0, /* 35:37 */ + OP_NO | OP_DPF, /* LDR */ + OP_NO | OP_DPF, /* CDR */ + OP_NO | OP_DPF, /* ADR */ + OP_NO | OP_DPF, /* SDR */ + OP_NO | OP_DPF, /* MDR */ + OP_NO | OP_DPF, /* DDR */ + OP_NO | OP_DPF, /* FXDR */ + OP_NO | OP_DPF, /* FLDR */ + OP_RX, /* STH */ + OP_RX, /* BAL */ + OP_RX, /* BTC */ + OP_RX, /* BFC */ + OP_RXH, /* NH */ + OP_RXH, /* CLH */ + OP_RXH, /* OH */ + OP_RXH, /* XH */ + OP_RXH, /* LH */ + OP_RXH, /* CH */ + OP_RXH, /* AH */ + OP_RXH, /* SH */ + OP_RXH, /* MH */ + OP_RXH, /* DH */ + 0, 0, /* 4E:4F */ + OP_RX, /* ST */ + OP_RXF, /* AM */ + 0, 0, /* 52:53 */ + OP_RXF, /* N */ + OP_RXF, /* CL */ + OP_RXF, /* O */ + OP_RXF, /* X */ + OP_RXF, /* L */ + OP_RXF, /* C */ + OP_RXF, /* A */ + OP_RXF, /* S */ + OP_RXF, /* M */ + OP_RXF, /* D */ + OP_RXH, /* CRC12 */ + OP_RXH, /* CRC16 */ + OP_RX, /* STE */ + OP_RXH, /* AHM */ + 0, /* PB - 8/32C */ + OP_RX, /* LRA */ + OP_RX, /* ATL */ + OP_RX, /* ABL */ + OP_RX, /* RTL */ + OP_RX, /* RBL */ + OP_RX, /* LE */ + OP_RX, /* CE */ + OP_RX, /* AE */ + OP_RX, /* SE */ + OP_RX, /* ME */ + OP_RX, /* DE */ + 0, 0, /* 6E:6F */ + OP_RX | OP_DPF, /* STD */ + OP_RX, /* SME */ + OP_RX, /* LME */ + OP_RXH, /* LHL */ + OP_RX, /* TBT */ + OP_RX, /* SBT */ + OP_RX, /* RBT */ + OP_RX, /* CBT */ + OP_RX | OP_DPF, /* LD */ + OP_RX | OP_DPF, /* CD */ + OP_RX | OP_DPF, /* AD */ + OP_RX | OP_DPF, /* SD */ + OP_RX | OP_DPF, /* MD */ + OP_RX | OP_DPF, /* DD */ + OP_RX | OP_DPF, /* STMD */ + OP_RX | OP_DPF, /* LMD */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 80:8F */ + 0, 0, 0, 0, 0, 0, 0, 0, + OP_NO, /* SRHLS */ + OP_NO, /* SLHLS */ + OP_NO, /* STBR */ + OP_RR, /* LDBR */ + OP_RR, /* EXBR */ + OP_NO | OP_PRV, /* EPSR */ + OP_RR | OP_PRV, /* WBR */ + OP_RR | OP_PRV, /* RBR */ + OP_RR | OP_PRV, /* WHR */ + OP_RR | OP_PRV, /* RHR */ + OP_RR | OP_PRV, /* WDR */ + OP_RR | OP_PRV, /* RDR */ + 0, /* 9C */ + OP_RR | OP_PRV, /* SSR */ + OP_RR | OP_PRV, /* OCR */ + 0, /* 9F */ + 0, 0, 0, 0, 0, 0, 0, 0, /* A0:AF */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* B0:BF */ + 0, 0, 0, 0, 0, 0, 0, 0, + OP_RX, /* BXH */ + OP_RX, /* BXLE */ + OP_RXF | OP_PRV, /* LPSW */ + OP_RI1, /* THI */ + OP_RI1, /* NHI */ + OP_RI1, /* CLHI */ + OP_RI1, /* OHI */ + OP_RI1, /* XHI */ + OP_RI1, /* LHI */ + OP_RI1, /* CHI */ + OP_RI1, /* AHI */ + OP_RI1, /* SHI */ + OP_RI1, /* SRHL */ + OP_RI1, /* SLHL */ + OP_RI1, /* SRHA */ + OP_RI1, /* SLHA */ + OP_RX, /* STM */ + OP_RX, /* LM */ + OP_RX, /* STB */ + OP_RXB, /* LDB */ + OP_RXB, /* CLB */ + OP_RX | OP_PRV, /* AL */ + OP_RXF | OP_PRV, /* WB */ + OP_RXF | OP_PRV, /* RB */ + OP_RX | OP_PRV, /* WH */ + OP_RX | OP_PRV, /* RH */ + OP_RX | OP_PRV, /* WD */ + OP_RX | OP_PRV, /* RD */ + 0, /* DC */ + OP_RX | OP_PRV, /* SS */ + OP_RX | OP_PRV, /* OC */ + 0, /* DF */ + OP_RXH, /* TS */ + OP_RX, /* SVC */ + OP_RI1 | OP_PRV, /* SINT */ + OP_RXH | OP_PRV, /* SCP */ + 0, 0, /* E4:E5 */ + OP_RX, /* LA */ + OP_RXF, /* TLATE */ + 0, 0, /* E8:E9 */ + OP_RI1, /* RRL */ + OP_RI1, /* RLL */ + OP_RI1, /* SRL */ + OP_RI1, /* SLL */ + OP_RI1, /* SRA */ + OP_RI1, /* SLA */ + 0, 0, 0, /* F0:F2 */ + OP_RI2, /* TI */ + OP_RI2, /* NI */ + OP_RI2, /* CLI */ + OP_RI2, /* OI */ + OP_RI2, /* XI */ + OP_RI2, /* LI */ + OP_RI2, /* CI */ + OP_RI2, /* AI */ + OP_RI2, /* SI */ + 0, 0, 0, 0 /* FC:FF */ + }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +DIB cpu_dib = { d_DS, -1, v_DS, NULL, &display }; + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK, MAXMEMSIZE32) }; + +REG cpu_reg[] = { + { HRDATA (PC, PC, 20) }, + { HRDATA (OPC, oPC, 20), REG_HRO }, + { HRDATA (R0, GREG[0], 32) }, + { HRDATA (R1, GREG[1], 32) }, + { HRDATA (R2, GREG[2], 32) }, + { HRDATA (R3, GREG[3], 32) }, + { HRDATA (R4, GREG[4], 32) }, + { HRDATA (R5, GREG[5], 32) }, + { HRDATA (R6, GREG[6], 32) }, + { HRDATA (R7, GREG[7], 32) }, + { HRDATA (R8, GREG[8], 32) }, + { HRDATA (R9, GREG[9], 32) }, + { HRDATA (R10, GREG[10], 32) }, + { HRDATA (R11, GREG[11], 32) }, + { HRDATA (R12, GREG[12], 32) }, + { HRDATA (R13, GREG[13], 32) }, + { HRDATA (R14, GREG[14], 32) }, + { HRDATA (R15, GREG[15], 32) }, + { HRDATA (FR0, F[0], 32) }, + { HRDATA (FR2, F[1], 32) }, + { HRDATA (FR4, F[2], 32) }, + { HRDATA (FR6, F[3], 32) }, + { HRDATA (FR8, F[4], 32) }, + { HRDATA (FR10, F[5], 32) }, + { HRDATA (FR12, F[6], 32) }, + { HRDATA (FR14, F[7], 32) }, + { HRDATA (D0H, D[0].h, 32) }, + { HRDATA (D0L, D[0].l, 32) }, + { HRDATA (D2H, D[1].h, 32) }, + { HRDATA (D2L, D[1].l, 32) }, + { HRDATA (D4H, D[2].h, 32) }, + { HRDATA (D4L, D[2].l, 32) }, + { HRDATA (D6H, D[3].h, 32) }, + { HRDATA (D6L, D[3].l, 32) }, + { HRDATA (D8H, D[4].h, 32) }, + { HRDATA (D8L, D[4].l, 32) }, + { HRDATA (D10H, D[5].h, 32) }, + { HRDATA (D10L, D[5].l, 32) }, + { HRDATA (D12L, D[6].l, 32) }, + { HRDATA (D12H, D[6].h, 32) }, + { HRDATA (D14H, D[7].h, 32) }, + { HRDATA (D14L, D[7].l, 32) }, + { HRDATA (PSW, PSW, 16) }, + { HRDATA (CC, PSW, 4) }, + { HRDATA (SR, SR, 32) }, + { HRDATA (DR, DR, 32) }, + { HRDATA (DRX, DRX, 8) }, + { FLDATA (DRMOD, drmod, 0) }, + { FLDATA (SRPOS, srpos, 0) }, + { HRDATA (DRPOS, drpos, 3) }, + { BRDATA (IRQ, int_req, 16, 32, INTSZ) }, + { BRDATA (IEN, int_enb, 16, 32, INTSZ) }, + { BRDATA (MACREG, mac_reg, 16, 32, MAC_LNT) }, + { HRDATA (MACSTA, mac_sta, 5) }, + { HRDATA (QEVENT, qevent, 4), REG_HRO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (STOP_WAIT, stop_wait, 0) }, + { BRDATA (PCQ, pcq, 16, 20, PCQ_SIZE), REG_RO+REG_CIRC }, + { HRDATA (PCQP, pcq_p, 6), REG_HRO }, + { HRDATA (WRU, sim_int_char, 8) }, + { HRDATA (BLKIOD, blk_io.dfl, 16), REG_HRO }, + { HRDATA (BLKIOC, blk_io.cur, 20), REG_HRO }, + { HRDATA (BLKIOE, blk_io.end, 20), REG_HRO }, + { BRDATA (GREG, GREG, 16, 32, 16 * NRSETS) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_8RS|UNIT_TYPE, 0, NULL, "732", NULL }, + { UNIT_DPFP, UNIT_DPFP, NULL, "DPFP", NULL }, + { UNIT_TYPE, 0, "7/32, single precision fp", "732", NULL }, + { UNIT_TYPE, UNIT_DPFP, "7/32, double precision fp", NULL, NULL }, + { UNIT_8RS|UNIT_TYPE, UNIT_8RS|UNIT_DPFP|UNIT_832, NULL, "832", NULL }, + { UNIT_8RS, 0, NULL, "2RS", NULL }, + { UNIT_8RS|UNIT_TYPE, UNIT_8RS|UNIT_DPFP|UNIT_832, "832, 8 register sets", NULL, NULL }, + { UNIT_8RS|UNIT_TYPE, UNIT_DPFP|UNIT_832, "832, 2 register sets", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, + { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, + { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size }, + { UNIT_MSIZE, 1048756, NULL, "1M", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "CONSINT", + &cpu_set_consint, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEBTAB cpu_deb[] = { + { "INTEXC", LOG_CPU_I }, + { "CONTEXT", LOG_CPU_C }, + { NULL, 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 20, 2, 16, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + &cpu_dib, DEV_DEBUG, 0, + cpu_deb, NULL, NULL + }; + +t_stat sim_instr (void) +{ +volatile uint32 cc; /* set before setjmp */ +t_stat reason; /* set after setjmp */ +int abortval; + +/* Restore register state */ + +if (devtab_init ()) return SCPE_STOP; /* check conflicts */ +if (cpu_unit.flags & (UNIT_DPFP | UNIT_832)) { + fp_in_hwre = 1; /* fp in hwre */ + dec_flgs = 0; /* all instr ok */ + } +else { + fp_in_hwre = 0; /* fp in ucode */ + dec_flgs = OP_DPF; /* sp only */ + } +if (cpu_unit.flags & UNIT_8RS) psw_reg_mask = 7; /* 8 register sets */ +else psw_reg_mask = 1; /* 2 register sets */ +int_eval (); /* eval interrupts */ +cc = newPSW (PSW & PSW_MASK); /* split PSW, eval wait */ +reason = 0; + +/* Abort handling + + If an abort occurs in memory protection, the relocation routine + executes a longjmp to this area OUTSIDE the main simulation loop. + Memory protection errors are the only sources of aborts in the + Interdata 32b systems. All referenced variables must be globals, + and all sim_instr scoped automatic variables must be volatile or + set after the call on setjmp. +*/ + +abortval = setjmp (save_env); /* set abort hdlr */ +if (abortval != 0) { /* mem mgt abort? */ + qevent = qevent | EV_MAC; /* set MAC intr */ + if (cpu_unit.flags & UNIT_832) PC = oPC; /* 832? restore PC */ + } + +/* Event handling */ + +while (reason == 0) { /* loop until halted */ + + uint32 dev, drom, opnd, inc, lim, bufa; + uint32 op, r1, r1p1, r2, rx2, ea; + uint32 mpy, mpc, dvr; + uint32 i, rslt, rlo, t; + uint32 ir1, ir2, ir3, ityp; + int32 sr, st; + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + int_eval (); + } + + if (qevent) { /* any events? */ + if (qevent & EV_MAC) { /* MAC interrupt? */ + qevent = 0; /* clr all events */ + cc = exception (MPRPSW, cc, 0); /* take exception */ + int_eval (); /* re-eval intr */ + continue; + } + + if (qevent & EV_BLK) { /* block I/O in prog? */ + dev = blk_io.dfl & DEV_MAX; /* get device */ + cc = dev_tab[dev] (dev, IO_SS, 0) & 0xF; /* sense status */ + if (cc == STA_BSY) { /* just busy? */ + sim_interval = 0; /* force I/O event */ + continue; + } + else if (cc == 0) { /* ready, no err? */ + if (blk_io.dfl & BL_RD) { /* read? */ + t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ + if ((t == 0) && (blk_io.dfl & BL_LZ)) continue; + blk_io.dfl = blk_io.dfl & ~BL_LZ; /* non-zero seen */ + WriteB (blk_io.cur, t, VW); /* write mem */ + } + else { /* write */ + t = ReadB (blk_io.cur, VR); /* read mem */ + dev_tab[dev] (dev, IO_WD, t); /* put byte */ + } + if (blk_io.cur != blk_io.end) { /* more to do? */ + blk_io.cur = (blk_io.cur + 1) & VAMASK; /* incr addr */ + continue; + } + } + qevent = qevent & ~EV_BLK; /* clr blk I/O flag */ + int_eval (); /* re-eval intr */ + continue; + } + + if ((qevent & EV_INT) && (PSW & PSW_EXI)) { /* interrupt? */ + dev = int_getdev (); /* get int dev */ + cc = int_auto (dev, cc); /* do auto intr */ + int_eval (); /* re-eval intr */ + continue; + } + + if (PSW & PSW_WAIT) { /* wait state? */ + if (sim_idle_enab) /* idling enabled? */ + sim_idle (TMR_LFC, TRUE); + else sim_interval = sim_interval - 1; /* no, count cycle */ + continue; + } + + qevent = 0; /* no events */ + } + +/* Instruction fetch and decode */ + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + sim_interval = sim_interval - 1; + + ir1 = ReadH (oPC = PC, VE); /* fetch instr */ + op = (ir1 >> 8) & 0xFF; /* extract op,R1,R2 */ + r1 = (ir1 >> 4) & 0xF; + r2 = ir1 & 0xF; + drom = decrom[op]; /* get decode flags */ + ityp = drom & OP_MASK; /* instruction type */ + + if ((drom == 0) || (drom & dec_flgs)) { /* not in model? */ + if (stop_inst) reason = STOP_RSRV; /* stop or */ + else cc = exception (ILOPSW, cc, 0); /* exception */ + continue; + } + if ((drom & OP_PRV) && (PSW & PSW_PRO)) { /* priv & protected? */ + cc = exception (ILOPSW, cc, 0); /* exception */ + continue; + } + + switch (ityp) { /* decode instruction */ + + case OP_NO: /* no operand */ + opnd = r2; /* assume short */ + PC = (PC + 2) & VAMASK; /* increment PC */ + break; + + case OP_RR: /* reg-reg */ + opnd = R[r2]; /* ea/operand is R2 */ + PC = (PC + 2) & VAMASK; /* increment PC */ + break; + + case OP_RI1: /* reg-imm 1 */ + ir2 = ReadH ((PC + 2) & VAMASK, VE); /* fetch immed */ + opnd = SEXT16 (ir2); /* sign extend */ + if (r2) opnd = (opnd + R[r2]) & DMASK32; /* index calculation */ + PC = (PC + 4) & VAMASK; /* increment PC */ + break; + + case OP_RI2: /* reg-imm 2 */ + ir2 = ReadH ((PC + 2) & VAMASK, VE); /* fetch imm hi */ + ir3 = ReadH ((PC + 4) & VAMASK, VE); /* fetch imm lo */ + opnd = (ir2 << 16) | ir3; /* 32b immediate */ + if (r2) opnd = (opnd + R[r2]) & DMASK32; /* index calculation */ + PC = (PC + 6) & VAMASK; /* increment PC */ + break; + + case OP_RX: case OP_RXB: case OP_RXH: case OP_RXF: /* reg-mem */ + ir2 = ReadH ((PC + 2) & VAMASK, VE); /* fetch addr */ + if ((ir2 & 0xC000) == 0) { /* displacement? */ + PC = (PC + 4) & VAMASK; /* increment PC */ + ea = ir2; /* abs 14b displ */ + } + else if (ir2 & 0x8000) { /* relative? */ + PC = (PC + 4) & VAMASK; /* increment PC */ + ea = PC + SEXT15 (ir2); /* add to incr PC */ + } + else { /* absolute */ + rx2 = (ir2 >> 8) & 0xF; /* get second index */ + ea = (ir2 & 0xFF) << 16; /* shift to place */ + ir3 = ReadH ((PC + 4) & VAMASK, VE); /* fetch addr lo */ + ea = ea | ir3; /* finish addr */ + if (rx2) ea = ea + R[rx2]; /* index calc 2 */ + PC = (PC + 6) & VAMASK; /* increment PC */ + } + if (r2) ea = ea + R[r2]; /* index calculation */ + ea = ea & VAMASK; + if (ityp == OP_RXF) opnd = ReadF (ea, VR); /* get fw operand? */ + else if (ityp == OP_RXH) { /* get hw operand? */ + t = ReadH (ea, VR); /* read halfword */ + opnd = SEXT16 (t); /* sign extend */ + } + else if (ityp == OP_RXB) opnd = ReadB (ea, VR); /* get byte opnd? */ + else opnd = ea; /* just address */ + break; + + default: + return SCPE_IERR; + } + + if (hst_lnt) { /* instruction history? */ + hst[hst_p].pc = oPC | HIST_PC; /* save decode state */ + hst[hst_p].ir1 = ir1; + hst[hst_p].ir2 = ir2; + hst[hst_p].ir3 = ir3; + hst[hst_p].r1 = R[r1]; + hst[hst_p].ea = ea; + hst[hst_p].opnd = opnd; + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) hst_p = 0; + } + if (qevent & EV_MAC) continue; /* MAC abort on fetch? */ + switch (op) { /* case on opcode */ + +/* Load/store instructions */ + + case 0x08: /* LR - RR */ + case 0x24: /* LIS - NO */ + case 0x48: /* LH - RXH */ + case 0x58: /* L - RXF */ + case 0xC8: /* LHI - RI1 */ + case 0xF8: /* LI - RI2 */ + R[r1] = opnd; /* load operand */ + CC_GL_32 (R[r1]); /* set G,L */ + break; + + case 0x73: /* LHL - RXH */ + R[r1] = opnd & DMASK16; /* get op, zero ext */ + CC_GL_32 (R[r1]); /* set G, L */ + break; + + case 0x25: /* LCS - NO */ + R[r1] = NEG (opnd); /* load complement */ + CC_GL_32 (R[r1]); /* set G,L */ + break; + + case 0xE6: /* LA - RX */ + R[r1] = ea; /* load addr */ + break; + + case 0x63: /* LRA - RX */ + cc = RelocT (R[r1] & VAMASK, ea, VR, &R[r1]); /* test reloc */ + break; + + case 0x40: /* STH - RX */ + WriteH (ea, R[r1], VW); /* store register */ + break; + + case 0x50: /* ST - RX */ + WriteF (ea, R[r1], VW); /* store register */ + break; + + case 0xD1: /* LM - RX */ + for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ + R[r1] = ReadF (ea, VR); /* load register */ + ea = (ea + 4) & VAMASK; /* incr mem addr */ + } + break; + + case 0xD0: /* STM - RX */ + for ( ; r1 <= 0xF; r1++) { /* loop thru reg */ + WriteF (ea, R[r1], VW); /* store register */ + ea = (ea + 4) & VAMASK; /* incr mem addr */ + } + break; + + case 0xE0: /* TS - RXH */ + CC_GL_16 (opnd); /* set cc's */ + WriteH (ea, opnd | SIGN16, VW); /* set MSB */ + break; + + case 0x93: /* LDBR - RR */ + case 0xD3: /* LDB - RXB */ + R[r1] = opnd & DMASK8; /* load byte */ + break; + + case 0x92: /* STBR - NO */ + R[r2] = (R[r2] & ~DMASK8) | (R[r1] & DMASK8); /* store byte */ + break; + case 0xD2: /* STB - RX */ + WriteB (ea, R[r1], VW); /* store byte */ + break; + + case 0x34: /* EXHR - RR */ + R[r1] = ((opnd >> 16) & DMASK16) | ((opnd & DMASK16) << 16); + break; + + case 0x94: /* EXBR - RR */ + R[r1] = (R[r1] & ~DMASK16) | + ((opnd >> 8) & DMASK8) | ((opnd & DMASK8) << 8); + break; + +/* Control instructions */ + + case 0x01: /* BALR - RR */ + case 0x41: /* BAL - RX */ + PCQ_ENTRY; /* save old PC */ + R[r1] = PC; /* save cur PC */ + PC = opnd & VAMASK; /* branch */ + break; + + case 0x02: /* BTCR - RR */ + case 0x42: /* BTC - RX */ + if (cc & r1) { /* test CC's */ + PCQ_ENTRY; /* branch if true */ + PC = opnd & VAMASK; + } + break; + + case 0x20: /* BTBS - NO */ + if (cc & r1) { /* test CC's */ + PCQ_ENTRY; /* branch if true */ + PC = (oPC - r2 - r2) & VAMASK; + } + break; + + case 0x21: /* BTFS - NO */ + if (cc & r1) { /* test CC's */ + PCQ_ENTRY; /* branch if true */ + PC = (oPC + r2 + r2) & VAMASK; + } + break; + + case 0x03: /* BFCR - RR */ + case 0x43: /* BFC - RX */ + if ((cc & r1) == 0) { /* test CC's */ + PCQ_ENTRY; /* branch if false */ + PC = opnd & VAMASK; + } + break; + + case 0x22: /* BFBS - NO */ + if ((cc & r1) == 0) { /* test CC's */ + PCQ_ENTRY; /* branch if false */ + PC = (oPC - r2 - r2) & VAMASK; + } + break; + + case 0x23: /* BFFS - NO */ + if ((cc & r1) == 0) { /* test CC's */ + PCQ_ENTRY; /* branch if false */ + PC = (oPC + r2 + r2) & VAMASK; + } + break; + + case 0xC0: /* BXH - RX */ + inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ + lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ + R[r1] = (R[r1] + inc) & DMASK32; /* R1 = R1 + inc */ + if (R[r1] > lim) { /* if R1 > lim */ + PCQ_ENTRY; /* branch */ + PC = opnd & VAMASK; + } + break; + + case 0xC1: /* BXLE - RX */ + inc = R[(r1 + 1) & 0xF]; /* inc = R1 + 1 */ + lim = R[(r1 + 2) & 0xF]; /* lim = R1 + 2 */ + R[r1] = (R[r1] + inc) & DMASK32; /* R1 = R1 + inc */ + if (R[r1] <= lim) { /* if R1 <= lim */ + PCQ_ENTRY; /* branch */ + PC = opnd & VAMASK; + } + break; + +/* Logical instructions */ + + case 0x04: /* NR - RR */ + case 0x44: /* NH - RXH */ + case 0x54: /* N - RXF */ + case 0xC4: /* NHI - RI1 */ + case 0xF4: /* NI - RI2 */ + R[r1] = R[r1] & opnd; /* result */ + CC_GL_32 (R[r1]); /* set G,L */ + break; + + case 0x06: /* OR - RR */ + case 0x46: /* OH - RXH */ + case 0x56: /* O - RXF */ + case 0xC6: /* OHI - RI1 */ + case 0xF6: /* OI - RI2 */ + R[r1] = R[r1] | opnd; /* result */ + CC_GL_32 (R[r1]); /* set G,L */ + break; + + case 0x07: /* XR - RR */ + case 0x47: /* XH - RXH */ + case 0x57: /* X - RXF */ + case 0xC7: /* XHI - RI1 */ + case 0xF7: /* XI - RI2 */ + R[r1] = R[r1] ^ opnd; /* result */ + CC_GL_32 (R[r1]); /* set G,L */ + break; + + case 0xC3: /* THI - RI1 */ + case 0xF3: /* TI - RI2 */ + rslt = R[r1] & opnd; /* result */ + CC_GL_32 (rslt); /* set G, L */ + break; + + case 0x05: /* CLR - RR */ + case 0x45: /* CLH - RXH */ + case 0x55: /* CL - RXF */ + case 0xC5: /* CLHI - RI1 */ + case 0xF5: /* CI - RI2 */ + rslt = (R[r1] - opnd) & DMASK32; /* result */ + CC_GL_32 (rslt); /* set G,L */ + if (R[r1] < opnd) cc = cc | CC_C; /* set C if borrow */ + if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN32) cc = cc | CC_V; + break; + + case 0xD4: /* CLB - RXB */ + t = R[r1] & DMASK8; + rslt = (t - opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L 16b */ + if (t < opnd) cc = cc | CC_C; /* set C if borrow */ + break; + + case 0x12: /* CHVR - RR */ + t = cc & CC_C; /* save C */ + R[r1] = (SEXT16 (opnd & DMASK16)) & DMASK32; /* result */ + CC_GL_32 (R[r1]); /* set G, L */ + if (R[r1] != opnd) cc = cc | CC_V; /* wont fit? set V */ + cc = cc | t; /* restore C */ + break; + +/* Shift instructions */ + + case 0xCC: /* SRHL - RI1 */ + opnd = opnd & 0xF; /* shift count */ + case 0x90: /* SRHLS - NO */ + rslt = (R[r1] & DMASK16) >> opnd; /* result */ + CC_GL_16 (rslt); /* set G,L 16b */ + if (opnd && (((R[r1] & DMASK16) >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = (R[r1] & ~DMASK16) | rslt; /* store result */ + break; + + case 0xCD: /* SLHL - RI1 */ + opnd = opnd & 0xF; /* shift count */ + case 0x91: /* SLHLS - NO */ + rslt = R[r1] << opnd; /* result */ + CC_GL_16 (rslt & DMASK16); /* set G,L 16b */ + if (opnd && (rslt & 0x10000)) cc = cc | CC_C; /* set C if shft out */ + R[r1] = (R[r1] & ~DMASK16) | (rslt & DMASK16); /* store result */ + break; + + case 0xCE: /* SRHA - RI1 */ + opnd = opnd & 0xF; /* shift count */ + rslt = (SEXT16 (R[r1]) >> opnd) & DMASK16; /* result */ + CC_GL_16 (rslt); /* set G,L 16b */ + if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = (R[r1] & ~DMASK16) | rslt; /* store result */ + break; + + case 0xCF: /* SLHA - RI1 */ + opnd = opnd & 0xF; /* shift count */ + rslt = R[r1] << opnd; /* raw result */ + R[r1] = (R[r1] & ~MMASK16) | (rslt & MMASK16); + CC_GL_16 (R[r1] & DMASK16); /* set G,L 16b */ + if (opnd && (rslt & SIGN16)) cc = cc | CC_C; /* set C if shft out */ + break; + + case 0xEC: /* SRL - RI1 */ + opnd = opnd & 0x1F; /* shift count */ + case 0x10: /* SRLS - NO */ + rslt = R[r1] >> opnd; /* result */ + CC_GL_32 (rslt); /* set G,L */ + if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = rslt; /* store result */ + break; + + case 0xED: /* SLL - RI1 */ + opnd = opnd & 0x1F; /* shift count */ + case 0x11: /* SLLS - NO */ + rslt = (R[r1] << opnd) & DMASK32; /* result */ + CC_GL_32 (rslt); /* set G,L */ + if (opnd && ((R[r1] << (opnd - 1)) & SIGN32)) cc = cc | CC_C; + R[r1] = rslt; /* store result */ + break; + + case 0xEE: /* SRA - RI1 */ + opnd = opnd & 0x1F; /* shift count */ + rslt = (SEXT32 (R[r1]) >> opnd) & DMASK32; /* result */ + CC_GL_32 (rslt); /* set G,L */ + if (opnd && ((R[r1] >> (opnd - 1)) & 1)) cc = cc | CC_C; + R[r1] = rslt; /* store result */ + break; + + case 0xEF: /* SLA - RI1 */ + opnd = opnd & 0x1F; /* shift count */ + rslt = (R[r1] << opnd) & DMASK32; /* raw result */ + R[r1] = (R[r1] & SIGN32) | (rslt & MMASK32); /* arith result */ + CC_GL_32 (R[r1]); /* set G,L */ + if (opnd && (rslt & SIGN32)) cc = cc | CC_C; /* set C if shft out */ + break; + + case 0xEA: /* RRL - RI1 */ + opnd = opnd & 0x1F; /* shift count */ + if (opnd) R[r1] = (R[r1] >> opnd) | /* if cnt > 0 */ + ((R[r1] << (32 - opnd)) & DMASK32); /* rotate */ + CC_GL_32 (R[r1]); /* set G,L */ + break; + + case 0xEB: /* RLL - RI1 */ + opnd = opnd & 0x1F; /* shift count */ + if (opnd) R[r1] = ((R[r1] << opnd) & DMASK32) | /* if cnt > 0 */ + (R[r1] >> (32 - opnd)); /* rotate */ + CC_GL_32 (R[r1]); /* set G,L */ + break; + +/* Bit instructions */ + + case 0x74: /* TBT - RX */ + t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ + ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ + opnd = ReadH (ea, VR); /* read HW */ + if (opnd & t) cc = CC_G; /* test bit */ + else cc = 0; + break; + + case 0x75: /* SBT - RX */ + t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ + ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ + opnd = ReadH (ea, VR); /* read HW */ + WriteH (ea, opnd | t, VW); /* set bit, rewr */ + if (opnd & t) cc = CC_G; /* test bit */ + else cc = 0; + break; + + case 0x76: /* RBT - RX */ + t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ + ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ + opnd = ReadH (ea, VR); /* read HW */ + WriteH (ea, opnd & ~t, VW); /* clr bit, rewr */ + if (opnd & t) cc = CC_G; /* test bit */ + else cc = 0; + break; + + case 0x77: /* CBT - RX */ + t = 1u << (15 - (R[r1] & 0xF)); /* bit mask in HW */ + ea = (ea + ((R[r1] >> 3) & ~1)) & VAMASK; /* HW location */ + opnd = ReadH (ea, VR); /* read HW */ + WriteH (ea, opnd ^ t, VW); /* com bit, rewr */ + if (opnd & t) cc = CC_G; /* test bit */ + else cc = 0; + break; + +/* Arithmetic instructions */ + + case 0x0A: /* AR - RR */ + case 0x26: /* AIS - NO */ + case 0x4A: /* AH - RXH */ + case 0x5A: /* A - RXF */ + case 0xCA: /* AHI - RI1 */ + case 0xFA: /* AI - RI2 */ + rslt = (R[r1] + opnd) & DMASK32; /* result */ + CC_GL_32 (rslt); /* set G,L */ + if (rslt < opnd) cc = cc | CC_C; /* set C if carry */ + if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN32) cc = cc | CC_V; + R[r1] = rslt; + break; + + case 0x51: /* AM - RXF */ + rslt = (R[r1] + opnd) & DMASK32; /* result */ + WriteF (ea, rslt, VW); /* write result */ + CC_GL_32 (rslt); /* set G,L */ + if (rslt < opnd) cc = cc | CC_C; /* set C if carry */ + if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN32) cc = cc | CC_V; + break; + + case 0x61: /* AHM - RXH */ + rslt = (R[r1] + opnd) & DMASK16; /* result */ + WriteH (ea, rslt, VW); /* write result */ + CC_GL_16 (rslt); /* set G,L 16b */ + if (rslt < (opnd & DMASK16)) cc = cc | CC_C; /* set C if carry */ + if (((~R[r1] ^ opnd) & (R[r1] ^ rslt)) & SIGN16) cc = cc | CC_V; + break; + + case 0x0B: /* SR - RR */ + case 0x27: /* SIS - NO */ + case 0x4B: /* SH - RXH */ + case 0x5B: /* S - RXF */ + case 0xCB: /* SHI - RI1 */ + case 0xFB: /* SI - RI2 */ + rslt = (R[r1] - opnd) & DMASK32; /* result */ + CC_GL_32 (rslt); /* set G,L */ + if (R[r1] < opnd) cc = cc | CC_C; /* set C if borrow */ + if (((R[r1] ^ opnd) & (~opnd ^ rslt)) & SIGN32) cc = cc | CC_V; + R[r1] = rslt; + break; + + case 0x09: /* CR - RR */ + case 0x49: /* CH - RXH */ + case 0x59: /* C - RXF */ + case 0xC9: /* CHI - RI1 */ + case 0xF9: /* CI - RI2 */ + if (R[r1] == opnd) cc = 0; /* =? */ + else if ((R[r1] ^ opnd) & SIGN32) /* unlike signs? */ + cc = (R[r1] & SIGN32)? (CC_C | CC_L): CC_G; + else cc = (R[r1] > opnd)? CC_G: (CC_C | CC_L); /* like signs */ + if (((R[r1] ^ opnd) & (~opnd ^ (R[r1] - opnd))) & SIGN32) + cc = cc | CC_V; + break; + + case 0x0C: /* MHR - RR */ + case 0x4C: /* MH - RXH */ + R[r1] = (SEXT16 (R[r1]) * SEXT16 (opnd)) & DMASK32; /* multiply */ + break; + + case 0x1C: /* MR - RR */ + case 0x5C: /* M - RXF */ + r1p1 = (r1 + 1) & 0xF; + mpc = ABS (opnd); /* |mpcnd| */ + mpy = ABS (R[r1p1]); /* |mplyr| */ + rslt = rlo = 0; /* clr result */ + for (i = 0; i < 32; i++) { /* develop 32b */ + t = 0; /* no cout */ + if (mpy & 1) { /* cond add */ + rslt = (rslt + mpc) & DMASK32; + if (rslt < mpc) t = SIGN32; + } + rlo = (rlo >> 1) | ((rslt & 1) << 31); /* shift result */ + rslt = (rslt >> 1) | t; + mpy = mpy >> 1; /* shift mpylr */ + } + if ((opnd ^ R[r1p1]) & SIGN32) { + DNEG (rslt, rlo); + } + R[r1] = rslt; /* store result */ + R[r1p1] = rlo; + break; + + case 0x0D: /* DHR - RR */ + case 0x4D: /* DH - RXH */ + opnd = opnd & DMASK16; /* force HW opnd */ + if ((opnd == 0) || /* div by zero? */ + ((R[r1] == 0x80000000) && (opnd == 0xFFFF))) { + if (PSW & PSW_AFI) /* div fault enabled? */ + cc = exception (AFIPSW, cc, 0); /* exception */ + break; + } + r1p1 = (r1 + 1) & 0xF; + st = SEXT32 (R[r1]) / SEXT16 (opnd); /* quotient */ + sr = SEXT32 (R[r1]) % SEXT16 (opnd); /* remainder */ + if ((st < 0x8000) && (st >= -0x8000)) { /* if quo fits */ + R[r1] = sr & DMASK32; /* store remainder */ + R[r1p1] = st & DMASK32; /* store quotient */ + } + else if (PSW & PSW_AFI) /* div fault enabled? */ + cc = exception (AFIPSW, cc, 0); /* exception */ + break; + + case 0x1D: /* DR - RR */ + case 0x5D: /* D - RXF */ + r1p1 = (r1 + 1) & 0xF; + rslt = R[r1]; /* get dividend */ + rlo = R[r1p1]; + if (R[r1] & SIGN32) { DNEG (rslt, rlo); } /* |divd| */ + dvr = ABS (opnd); /* |divr| */ + if (rslt < dvr) { /* will div work? */ + uint32 quos = R[r1] ^ opnd; /* expected sign */ + for (i = t = 0; i < 32; i++) { /* 32 iterations */ + rslt = ((rslt << 1) & DMASK32) | /* shift divd */ + ((rlo >> 31) & 1); + rlo = (rlo << 1) & DMASK32; + t = (t << 1) & DMASK32; /* shift quo */ + if (rslt >= dvr) { /* subtract work? */ + rslt = rslt - dvr; /* divd -= divr */ + t = t | 1; /* set quo bit */ + } + } + if (quos & SIGN32) t = NEG (t); /* res -? neg quo */ + if (R[r1] & SIGN32) rslt = NEG (rslt); /* adj rem sign */ + if (t && ((t ^ quos) & SIGN32)) { /* res sign wrong? */ + if (PSW & PSW_AFI) /* if enabled, */ + cc = exception (AFIPSW, cc, 0); /* exception */ + break; + } + R[r1] = rslt; /* store rem */ + R[r1p1] = t; /* store quo */ + } + else if (PSW & PSW_AFI) /* div fault enabled? */ + cc = exception (AFIPSW, cc, 0); /* exception */ + break; + +/* Floating point instructions */ + + case 0x28: /* LER - NO */ + case 0x38: /* LDR - NO */ + case 0x68: /* LE - RX */ + case 0x78: /* LD - RX */ + cc = f_l (op, r1, r2, ea); /* load */ + if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ + cc = exception (AFIPSW, cc, 1); + break; + + case 0x29: /* CER - NO */ + case 0x39: /* CDR - NO */ + case 0x69: /* CE - RX */ + case 0x79: /* CD - RX */ + cc = f_c (op, r1, r2, ea); /* compare */ + break; + + case 0x2A: /* AER - NO */ + case 0x2B: /* SER - NO */ + case 0x3A: /* ADR - NO */ + case 0x3B: /* SDR - NO */ + case 0x6A: /* AE - RX */ + case 0x6B: /* SE - RX */ + case 0x7A: /* AD - RX */ + case 0x7B: /* SD - RX */ + cc = f_as (op, r1, r2, ea); /* add/sub */ + if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ + cc = exception (AFIPSW, cc, 1); + break; + + case 0x2C: /* MER - NO */ + case 0x3C: /* MDR - NO */ + case 0x6C: /* ME - RX */ + case 0x7C: /* MD - RX */ + cc = f_m (op, r1, r2, ea); /* multiply */ + if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ + cc = exception (AFIPSW, cc, 1); + break; + + case 0x2D: /* DER - NO */ + case 0x3D: /* DDR - NO */ + case 0x6D: /* DE - RX */ + case 0x7D: /* DD - RX */ + cc = f_d (op, r1, r2, ea); /* perform divide */ + if ((cc & CC_V) && (PSW & PSW_AFI)) /* V set? */ + cc = exception (AFIPSW, cc, 1); + break; + + case 0x2E: /* FXR - NO */ + case 0x3E: /* FXDR - NO */ + cc = f_fix32 (op, r1, r2); /* cvt to integer */ + break; + + case 0x2F: /* FLR - NO */ + case 0x3F: /* FLDR - NO */ + cc = f_flt32 (op, r1, r2); /* cvt to floating */ + break; + + case 0x60: /* STE - RX */ + t = ReadFReg (r1); /* get sp reg */ + WriteF (ea, t, VW); /* write */ + break; + + case 0x70: /* STD - RX */ + WriteF (ea, D[r1 >> 1].h, VW); /* write hi */ + WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, VW); /* write lo */ + break; + + case 0x71: /* STME - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + t = ReadFReg (r1); /* get sp reg */ + WriteF (ea, t, VW); /* write */ + ea = (ea + 4) & VAMASK; /* incr mem addr */ + } + break; + + case 0x72: /* LME - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + t = ReadF (ea, VR); /* get value */ + WriteFReg (r1, t); /* write reg */ + ea = (ea + 4) & VAMASK; /* incr mem addr */ + } + break; + + case 0x7E: /* STMD - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + WriteF (ea, D[r1 >> 1].h, VW); /* write register */ + WriteF ((ea + 4) & VAMASK, D[r1 >> 1].l, VW); + ea = (ea + 8) & VAMASK; /* incr mem addr */ + } + break; + + case 0x7F: /* LMD - RX */ + for ( ; r1 <= 0xE; r1 = r1 + 2) { /* loop thru reg */ + D[r1 >> 1].h = ReadF (ea, VR); /* load register */ + D[r1 >> 1].l = ReadF ((ea + 4) & VAMASK, VR); + ea = (ea + 8) & VAMASK; /* incr mem addr */ + } + break; + +/* Miscellaneous */ + + case 0xE1: /* SVC - RX */ + PCQ_ENTRY; /* effective branch */ + t = BUILD_PSW (cc); /* save PSW */ + cc = newPSW (ReadF (SVNPS32, P)); /* get new PSW */ + R[13] = ea & 0xFFFFFF; /* parameter */ + R[14] = t; /* old PSW */ + R[15] = PC; /* old PC */ + PC = ReadH (SVNPC + r1 + r1, P); /* new PC */ + if (DEBUG_PRI (cpu_dev, LOG_CPU_C)) fprintf (sim_deb, + ">>SVC: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", + pcq[pcq_p], t, PC, PSW); + break; + + case 0xE2: /* SINT - RI1 */ + dev = opnd & DEV_MAX; /* get dev */ + cc = int_auto (dev, cc); /* auto int */ + int_eval (); + break; + + case 0xE3: /* SCP - RXH */ + opnd = opnd & DMASK16; /* zero ext operand */ + if (opnd & CCW32_B1) t = ea + CCB32_B1C; /* point to buf */ + else t = ea + CCB32_B0C; + sr = ReadH (t & VAMASK, VR); /* get count */ + sr = SEXT16 (sr); /* sign extend */ + if (sr <= 0) { /* <= 0? */ + bufa = ReadF ((t + 2) & VAMASK, VR); /* get buf end */ + if (opnd & CCW32_WR) /* write? */ + R[r1] = ReadB ((bufa + sr) & VAMASK, VR); /* R1 gets mem */ + else WriteB ((bufa + sr) & VAMASK, R[r1], VW); /* read, R1 to mem */ + sr = sr + 1; /* inc count */ + CC_GL_32 (sr & DMASK32); /* set cc's */ + WriteH (t & VAMASK, sr, VW); /* rewrite */ + if ((sr > 0) && !(opnd & CCW32_FST)) /* buf switch? */ + WriteH (ea, opnd ^ CCW32_B1, VW); /* flip CCW bit */ + } /* end if */ + else cc = CC_V; + break; + + case 0x18: /* LPSWR - RR */ + PCQ_ENTRY; /* effective branch */ + PC = R[(r2 + 1) & 0xF] & VAMASK; /* new PC (old reg set) */ + if (DEBUG_PRI (cpu_dev, LOG_CPU_C)) fprintf (sim_deb, + ">>LPSWR: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", + pcq[pcq_p], BUILD_PSW (cc), PC, opnd); + cc = newPSW (opnd); /* new PSW */ + if (PSW & PSW_SQI) cc = testsysq (cc); /* test for q */ + break; + + case 0xC2: /* LPSW - RXF */ + PCQ_ENTRY; /* effective branch */ + PC = ReadF ((ea + 4) & VAMASK, VR) & VAMASK; /* new PC */ + if (DEBUG_PRI (cpu_dev, LOG_CPU_C)) fprintf (sim_deb, + ">>LPSW: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", + pcq[pcq_p], BUILD_PSW (cc), PC, opnd); + cc = newPSW (opnd); /* new PSW */ + if (PSW & PSW_SQI) cc = testsysq (cc); /* test for q */ + break; + + case 0x95: /* EPSR - NO */ + R[r1] = BUILD_PSW (cc); /* save PSW */ + cc = newPSW (R[r2]); /* load new PSW */ + if (PSW & PSW_SQI) cc = testsysq (cc); /* test for q */ + break; + + case 0x64: /* ATL - RX */ + case 0x65: /* ABL - RX */ + cc = addtoq (ea, R[r1], op & 1); /* add to q */ + break; + + case 0x66: /* RTL - RX */ + case 0x67: /* RBL - RX */ + cc = remfmq (ea, r1, op & 1); /* rem from q */ + break; + + case 0x5E: /* CRC12 - RXH */ + opnd = opnd & DMASK16; /* zero ext opnd */ + t = (R[r1] & 0x3F) ^ opnd; + for (i = 0; i < 6; i++) { + if (t & 1) t = (t >> 1) ^ 0x0F01; + else t = t >> 1; + } + WriteH (ea, t, VW); + break; + + case 0x5F: /* CRC16 - RXH */ + opnd = opnd & DMASK16; /* zero ext opnd */ + t = (R[r1] & 0xFF) ^ opnd; + for (i = 0; i < 8; i++) { + if (t & 1) t = (t >> 1) ^ 0xA001; + else t = t >> 1; + } + WriteH (ea, t, VW); + break; + + case 0xE7: /* TLATE - RXF */ + t = (opnd + ((R[r1] & DMASK8) << 1)) & VAMASK; /* table entry */ + rslt = ReadH (t, VR); /* get entry */ + if (rslt & SIGN16) R[r1] = rslt & DMASK8; /* direct xlate? */ + else { + PCQ_ENTRY; /* branch */ + PC = rslt << 1; + } + break; + +/* I/O instructions */ + + case 0xDE: /* OC - RX */ + opnd = ReadB (ea, VR); /* fetch operand */ + case 0x9E: /* OCR - RR */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + dev_tab[dev] (dev, IO_OC, opnd & DMASK8); /* send command */ + cc = 0; + } + else cc = CC_V; + int_eval (); /* re-eval intr */ + break; + + case 0xDA: /* WD - RX */ + opnd = ReadB (ea, VR); /* fetch operand */ + case 0x9A: /* WDR - RR */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send data */ + cc = 0; + } + else cc = CC_V; + int_eval (); /* re-eval intr */ + break; + + case 0xD8: /* WH - RX */ + opnd = ReadH (ea, VR); /* fetch operand */ + case 0x98: /* WHR - RR */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { + if (dev_tab[dev] (dev, IO_ADR, 0)) /* select; hw ok? */ + dev_tab[dev] (dev, IO_WH, opnd & DMASK16); /* send data */ + else { /* byte only */ + dev_tab[dev] (dev, IO_WD, (opnd >> 8) & DMASK8); /* hi */ + dev_tab[dev] (dev, IO_WD, opnd & DMASK8); /* send lo byte */ + } + cc = 0; + } + else cc = CC_V; + int_eval (); /* re-eval intr */ + break; + + case 0x9B: /* RDR - RR */ + case 0xDB: /* RD - RX */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + t = dev_tab[dev] (dev, IO_RD, 0); /* get data */ + cc = 0; + } + else { /* no */ + t = 0; + cc = CC_V; + } + if (OP_TYPE (op) != OP_RR) WriteB (ea, t, VW); /* RX or RR? */ + else R[r2] = t & DMASK8; + int_eval (); /* re-eval intr */ + break; + + case 0x99: /* RHR - RR */ + case 0xD9: /* RH - RX */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + if (dev_tab[dev] (dev, IO_ADR, 0)) /* select, hw ok? */ + t = dev_tab[dev] (dev, IO_RH, 0); /* get data */ + else { /* byte only */ + rslt = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ + t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ + t = (rslt << 8) | t; /* merge */ + } + cc = 0; + } + else { /* no */ + t = 0; + cc = CC_V; + } + if (OP_TYPE (op) != OP_RR) WriteH (ea, t, VW); /* RX or RR? */ + else R[r2] = t & DMASK16; + int_eval (); /* re-eval intr */ + break; + + case 0x9D: /* SSR - RR */ + case 0xDD: /* SS - RX */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + dev_tab[dev] (dev, IO_ADR, 0); /* select */ + t = dev_tab[dev] (dev, IO_SS, 0); /* get status */ + } + else t = STA_EX; /* no */ + if (OP_TYPE (op) != OP_RR) WriteB (ea, t, VW); /* RX or RR? */ + else R[r2] = t & DMASK8; + cc = t & 0xF; + int_eval (); /* re-eval intr */ + break; + +/* Block I/O instructions + + On a real Interdata system, the block I/O instructions can't be + interrupted or stopped. To model this behavior, while allowing + the instructions to go back through fetch for I/O processing and + WRU testing, the simulator implements a 'block I/O in progress' + flag and status block. If a block I/O is in progress, normal + interrupts and fetches are suppressed until the block I/O is done. +*/ + + case 0x96: /* WBR - RR */ + case 0xD6: /* WB - RXF */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + if (OP_TYPE (op) != OP_RR) + lim = ReadF ((ea + 4) & VAMASK, VR); + else lim = R[(r2 + 1) & 0xF]; + if (opnd > lim) cc = 0; /* start > end? */ + else { /* no, start I/O */ + dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ + blk_io.dfl = dev; /* set status block */ + blk_io.cur = opnd; + blk_io.end = lim; + qevent = qevent | EV_BLK; /* I/O in prog */ + } + } + else cc = CC_V; /* nx dev */ + break; + + case 0x97: /* RBR - RR */ + case 0xD7: /* RB - RXF */ + dev = R[r1] & DEV_MAX; + if (DEV_ACC (dev)) { /* dev exist? */ + if (OP_TYPE (op) != OP_RR) + lim = ReadF ((ea + 4) & VAMASK, VR); + else lim = R[(r2 + 1) & 0xF]; + if (opnd > lim) cc = 0; /* start > end? */ + else { /* no, start I/O */ + dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ + blk_io.dfl = dev | BL_RD; /* set status block */ + blk_io.cur = opnd; + blk_io.end = lim; + qevent = qevent | EV_BLK; /* I/O in prog */ + } + } + else cc = CC_V; /* nx dev */ + break; + + case 0xD5: /* AL - RX */ + dev = ReadB (AL_DEV, P); /* get device */ + t = ReadB (AL_IOC, P); /* get command */ + if (DEV_ACC (dev)) { /* dev exist? */ + if (AL_BUF > ea) cc = 0; /* start > end? */ + else { /* no, start I/O */ + dev_tab[dev] (dev, IO_ADR, 0); /* select dev */ + dev_tab[dev] (dev, IO_OC, t); /* start dev */ + blk_io.dfl = dev | BL_RD | BL_LZ; /* set status block */ + blk_io.cur = AL_BUF; + blk_io.end = ea; + qevent = qevent | EV_BLK; /* I/O in prog */ + } + } + else cc = CC_V; /* nx dev */ + break; + } /* end switch */ + } /* end while */ + +/* Simulation halted */ + +PSW = BUILD_PSW (cc); +PC = PC & VAMASK; +set_r_display (R); +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Load new PSW */ + +uint32 newPSW (uint32 val) +{ +uint32 rs = PSW_GETREG (val); /* register set */ + +R = &GREG[rs * 16]; /* set register set */ +PSW = val & PSW_MASK; /* store PSW */ +int_eval (); /* update intreq */ +if (PSW & PSW_WAIT) qevent = qevent | EV_WAIT; /* wait state? */ +else qevent = qevent & ~EV_WAIT; +if (PSW & PSW_EXI) SET_ENB (v_DS); /* enable/disable */ +else CLR_ENB (v_DS); /* console intr */ +return PSW & CC_MASK; +} + +/* Exception handler - 7/32 always uses register set 0 */ + +uint32 exception (uint32 loc, uint32 cc, uint32 flg) +{ +int32 oldPSW = BUILD_PSW (cc); /* save old PSW */ +int32 oldPC = PC; /* save old PC */ + +cc = newPSW (ReadF (loc, P)); /* new PSW */ +PC = ReadF (loc + 4, P) & VAMASK; /* new PC */ +if (cpu_unit.flags & UNIT_832) { /* 8/32? */ + R[14] = oldPSW; /* PSW to new 14 */ + R[15] = oldPC; /* PC to new 15 */ + } +else { + GREG[14] = oldPSW; /* 7/32, PSW to set 0 14 */ + GREG[15] = oldPC; /* PC to set 0 15 */ + } +if (DEBUG_PRI (cpu_dev, LOG_CPU_I)) fprintf (sim_deb, + ">>Exc %X: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", + loc, oldPC, oldPSW, PC, PSW | cc | flg); +return cc | flg; /* return CC */ +} + +/* Test for queue interrupts - system queue addresses are physical */ + +uint32 testsysq (uint32 cc) +{ +int32 qb = ReadF (SQP, P); /* get sys q addr */ +int32 usd = ReadH (qb + Q32_USD, P); /* get use count */ + +if (usd) { /* entries? */ + cc = exception (SQTPSW, cc, 0); /* take sysq exc */ + if (cpu_unit.flags & UNIT_832) R[13] = qb; /* R13 = sys q addr */ + else GREG[13] = qb; + } +return cc; +} + +/* Add to queue */ + +uint32 addtoq (uint32 ea, uint32 val, uint32 flg) +{ +uint32 slt, usd, wra, t; + +t = ReadF (ea, VR); /* slots/used */ +slt = (t >> 16) & DMASK16; /* # slots */ +usd = t & DMASK16; /* # used */ +if (usd >= slt) return CC_V; /* list full? */ +usd = (usd + 1) & DMASK16; /* inc # used */ +WriteH (ea + Q32_USD, usd, VW); /* rewrite */ +if (flg) { /* ABL? */ + wra = ReadH ((ea + Q32_BOT) & VAMASK, VR); /* get bottom */ + t = wra + 1; /* adv bottom */ + if (t >= slt) t = 0; /* wrap if necc */ + WriteH ((ea + Q32_BOT) & VAMASK, t, VW); /* rewrite bottom */ + } +else { + wra = ReadH ((ea + Q32_TOP) & VAMASK, VR); /* ATL, get top */ + if (wra == 0) wra = (slt - 1) & DMASK16; /* wrap if necc */ + else wra = wra - 1; /* dec top */ + WriteH ((ea + Q32_TOP) & VAMASK, wra, VW); /* rewrite top */ + } +WriteF ((ea + Q32_BASE + (wra * Q32_SLNT)) & VAMASK, val, VW); /* write slot */ +return 0; +} + +/* Remove from queue */ + +uint32 remfmq (uint32 ea, uint32 r1, uint32 flg) +{ +uint32 slt, usd, rda, t; + +t = ReadF (ea, VR); /* get slots/used */ +slt = (t >> 16) & DMASK16; /* # slots */ +usd = t & DMASK16; /* # used */ +if (usd == 0) return CC_V; /* empty? */ +usd = usd - 1; /* dec used */ +WriteH (ea + Q32_USD, usd, VW); /* rewrite */ +if (flg) { /* RBL? */ + rda = ReadH ((ea + Q32_BOT) & VAMASK, VR); /* get bottom */ + if (rda == 0) rda = (slt - 1) & DMASK16; /* wrap if necc */ + else rda = rda - 1; /* dec bottom */ + WriteH ((ea + Q32_BOT) & VAMASK, rda, VW); /* rewrite bottom */ + } +else { + rda = ReadH ((ea + Q32_TOP) & VAMASK, VR); /* RTL, get top */ + t = rda + 1; /* adv top */ + if (t >= slt) t = 0; /* wrap if necc */ + WriteH ((ea + Q32_TOP) & VAMASK, t, VW); /* rewrite top */ + } +R[r1] = ReadF ((ea + Q32_BASE + (rda * Q32_SLNT)) & VAMASK, VR); /* read slot */ +if (usd) return CC_G; +else return 0; +} + +/* Automatic interrupt processing */ + +uint32 int_auto (uint32 dev, uint32 cc) +{ +uint32 addr, vec, by, ccw, ccwa, ccwb; +uint32 i, hw, tblad, tblen, bufe, st, t; +int32 bufc; +uint32 oldPSW = BUILD_PSW (cc); + +vec = ReadH (INTSVT + dev + dev, P); /* get vector */ +newPSW (0x2800); /* new PSW */ +R[0] = oldPSW; /* save old PSW */ +R[1] = PC; /* save PC */ +R[2] = dev; /* set dev # */ +if (DEBUG_PRI (cpu_dev, LOG_CPU_I)) fprintf (sim_deb, + ">>Int %X: oPC = %X, oPSW = %X, nPC = %X, nPSW = %X\n", + dev, PC, oldPSW, vec, 0x2800); +if (DEV_ACC (dev)) { /* dev exist? */ + hw = dev_tab[dev] (dev, IO_ADR, 0); /* select, get hw */ + R[3] = st = dev_tab[dev] (dev, IO_SS, 0); /* sense status */ + } +else { + hw = 0; + R[3] = CC_V; + } +if ((vec & 1) == 0) { /* immed int? */ + PC = vec; /* new PC */ + return PSW & CC_MASK; /* exit */ + } +R[4] = ccwa = vec & ~1; /* save CCW addr */ +ccw = ReadH (ccwa, VR); /* read CCW */ +if ((ccw & CCW32_EXE) == 0) { /* exec clr? */ + PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ + return 0; /* CC = 0 */ + } +if (!DEV_ACC (dev) || (st & CCW32_STA (ccw))) { /* bad status? */ + PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ + return CC_L; /* CC = L */ + } +if (ccw & CCW32_FST) { /* fast mode? */ + t = ReadH (ccwa + CCB32_B0C, VR); /* get count */ + bufc = SEXT16 (t); /* sign ext */ + if (bufc <= 0) { /* still valid? */ + bufe = ReadF (ccwa + CCB32_B0E, VR); /* get end addr */ + addr = (bufe + bufc) & VAMASK; + if (hw) { /* halfword? */ + if (ccw & CCW32_WR) { /* write? */ + t = ReadH (addr, VR); /* get hw */ + dev_tab[dev] (dev, IO_WH, t); /* send to dev */ + } + else { /* read */ + t = dev_tab[dev] (dev, IO_RH, 0); /* get hw */ + WriteH (addr, t, VW); /* write to mem */ + } + bufc = bufc + 2; /* adv buf cnt */ + } + else { /* byte */ + if (ccw & CCW32_WR) { /* write? */ + t = ReadB (addr, VR); /* get byte */ + dev_tab[dev] (dev, IO_WD, t); /* send to dev */ + } + else { /* read */ + t = dev_tab[dev] (dev, IO_RD, 0); /* get byte */ + WriteB (addr, t, VW); /* write to mem */ + } + bufc = bufc + 1; /* adv buf cnt */ + } + WriteH (ccwa + CCB32_B0C, bufc, VW); /* rewrite cnt */ + if (bufc > 0) { + PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ + return CC_G; /* CC = G */ + } + } /* end if bufc <= 0 */ + } /* end fast */ +else { /* slow mode */ + if (ccw & CCW32_B1) ccwb = ccwa + CCB32_B1C; /* which buf? */ + else ccwb = ccwa + CCB32_B0C; + t = ReadH (ccwb, VR); /* get count */ + bufc = SEXT16 (t); /* sign ext */ + if (bufc <= 0) { /* still valid? */ + bufe = ReadF (ccwb + 2, VR); /* get end addr */ + addr = (bufe + bufc) & VAMASK; + if (ccw & CCW32_WR) { /* write? */ + by = ReadB (addr, VR); /* byte fm mem */ + if (ccw & CCW32_TL) { /* translate? */ + tblad = ReadF (ccwa + CCB32_TAB, VR); /* get tbl addr */ + tblen = (tblad + (by << 1)) & VAMASK; /* tbl entry addr */ + t = ReadH (tblen, VR); /* get tbl entry */ + if ((t & SIGN16) == 0) { /* special xlate? */ + PC = t << 1; /* change PC */ + R[3] = by; /* untrans char */ + return 0; /* CC = 0 */ + } + by = t & DMASK8; /* replace */ + } + dev_tab[dev] (dev, IO_WD, by); /* write to dev */ + } + else { /* read */ + by = dev_tab[dev] (dev, IO_RD, 0); /* get from dev */ + if (ccw & CCW32_TL) { /* translate? */ + tblad = ReadF (ccwa + CCB32_TAB, VR); /* get tbl addr */ + tblen = (tblad + (by << 1)) & VAMASK; /* tbl entry addr */ + t = ReadH (tblen, VR); /* get tbl entry */ + if ((t & SIGN16) == 0) { /* special xlate? */ + PC = t << 1; /* change PC */ + R[3] = by; /* untrans char */ + return 0; /* CC = 0 */ + } + WriteB (addr, t, VW); /* wr trans */ + } + else WriteB (addr, by, VW); /* wr orig */ + } + t = ReadH (ccwa + CCB32_CHK, VR); /* get check wd */ + t = t ^ by; /* start LRC */ + if (ccw & CCW32_CRC) { /* CRC? */ + for (i = 0; i < 8; i++) { + if (t & 1) t = (t >> 1) ^ 0xA001; + else t = t >> 1; + } + } + WriteH (ccwa + CCB32_CHK, t, VW); /* rewrite chk wd */ + bufc = bufc + 1; /* adv buf cnt */ + WriteH (ccwb, bufc, VW); /* rewrite cnt */ + if (bufc > 0) { /* cnt pos? */ + ccw = ccw ^ CCW32_B1; /* flip buf */ + WriteH (ccwa, ccw, VW); /* rewrite */ + PC = ReadH (ccwa + CCB32_SUB, VR); /* get subr */ + return CC_G; /* CC = G */ + } + } /* end if bufc */ + } /* end slow */ +PC = R[1]; /* restore PC */ +return newPSW (R[0]); /* restore PSW, CC */ +} + +/* Display register device */ + +uint32 display (uint32 dev, uint32 op, uint32 dat) +{ +int t; + +switch (op) { + + case IO_ADR: /* select */ + if (!drmod) drpos = srpos = 0; /* norm mode? clr */ + return BY; /* byte only */ + + case IO_OC: /* command */ + op = op & 0xC0; + if (op == 0x40) { /* x40 = inc */ + drmod = 1; + drpos = srpos = 0; /* init cntrs */ + } + else if (op == 0x80) drmod = 0; /* x80 = norm */ + break; + + case IO_WD: /* write */ + if (drpos < 4) + DR = (DR & ~(DMASK8 << (drpos * 8))) | (dat << (drpos * 8)); + else if (drpos == 4) DRX = dat; + drpos = (drpos + 1) & 0x7; + break; + + case IO_RD: /* read */ + t = (SR >> (srpos * 8)) & DMASK8; + srpos = srpos ^ 1; + return t; + + case IO_SS: /* status */ + return 0x80; + } + +return 0; +} + +/* Relocation and protection */ + +uint32 Reloc (uint32 va, uint32 rel) +{ +uint32 seg, off, mapr, lim; + +seg = VA_GETSEG (va); /* get seg num */ +off = VA_GETOFF (va); /* get offset */ +mapr = mac_reg[seg]; /* get seg reg */ +lim = GET_SRL (mapr); /* get limit */ +if (off >= lim) { /* limit viol? */ + mac_sta = MACS_L; /* set status */ + ABORT (MPRO); /* abort */ + } +if ((mapr & SR_PRS) == 0) { /* not present? */ + mac_sta = MACS_NP; /* set status */ + ABORT (MPRO); /* abort */ + } +if ((rel == VE) && (mapr & SR_EXP)) { /* exec, prot? */ + mac_sta = MACS_EX; /* set status */ + qevent = qevent | EV_MAC; /* req intr */ + } +if ((rel == VW) && (mapr & (SR_WPI | SR_WRP))) { /* write, prot? */ + if (mapr & SR_WRP) { /* write abort? */ + mac_sta = MACS_WP; /* set status */ + ABORT (MPRO); /* abort */ + } + else { /* write intr */ + mac_sta = MACS_WI; /* set status */ + qevent = qevent | EV_MAC; /* req intr */ + } + } +return (off + (mapr & SRF_MASK)) & PAMASK32; /* relocate */ +} + +uint32 RelocT (uint32 va, uint32 base, uint32 rel, uint32 *pa) +{ +uint32 seg, off, mapr, lim; + +seg = VA_GETSEG (va); /* get seg num */ +off = VA_GETOFF (va); /* get offset */ +mapr = ReadF ((base + (seg << 2)) & VAMASK, rel); /* get seg reg */ +lim = GET_SRL (mapr); /* get limit */ +if (off >= lim) return CC_C; /* limit viol? */ +if ((mapr & SR_PRS) == 0) return CC_V; /* not present? */ +*pa = off + (mapr & SRF_MASK); /* translate */ +if (mapr & (SR_WRP | SR_WPI)) return CC_G; /* write prot? */ +if (mapr & SR_EXP) return CC_L; /* exec prot? */ +return 0; /* ok */ +} + +/* Memory interface routines + + ReadB read byte (processor) + ReadH read halfword (processor) + ReadF read fullword (processor) + WriteB write byte (processor) + WriteH write halfword (processor) + WriteF write fullword (processor) + IOReadB read byte (IO) + IOWriteB write byte (IO) + IOReadH read halfword (IO) + IOWriteH write halfword (IO) +*/ + +uint32 ReadB (uint32 loc, uint32 rel) +{ +uint32 val; +uint32 sc = (3 - (loc & 3)) << 3; + +if ((PSW & PSW_REL) == 0) { /* reloc off? */ + if ((loc & ~03) == MAC_STA) { /* MAC status? */ + val = mac_sta; /* read it */ + qevent = qevent & ~EV_MAC; /* clr MAC intr */ + } + else val = M[loc >> 2]; /* get mem word */ + } +else if (rel == 0) val = M[loc >> 2]; /* phys ref? */ +else { + uint32 pa = Reloc (loc, rel); /* relocate */ + val = M[pa >> 2]; + } +return (val >> sc) & DMASK8; +} + +uint32 ReadH (uint32 loc, uint32 rel) +{ +uint32 val; + +if ((PSW & PSW_REL) == 0) { /* reloc off? */ + if ((loc & ~03) == MAC_STA) { /* MAC status? */ + val = mac_sta; /* read it */ + qevent = qevent & ~EV_MAC; /* clr MAC intr */ + } + else val = M[loc >> 2]; /* get mem word */ + } +else if (rel == 0) val = M[loc >> 2]; /* phys ref? */ +else { + uint32 pa = Reloc (loc, rel); /* relocate */ + val = M[pa >> 2]; + } +return (val >> ((loc & 2)? 0: 16)) & DMASK16; +} + +uint32 ReadF (uint32 loc, uint32 rel) +{ +uint32 val; + +if ((PSW & PSW_REL) == 0) { /* reloc off? */ + if ((loc & ~03) == MAC_STA) { /* MAC status? */ + val = mac_sta; /* read it */ + qevent = qevent & ~EV_MAC; /* clr MAC intr */ + } + else val = M[loc >> 2]; /* get mem word */ + } +else if (rel == 0) val = M[loc >> 2]; /* phys ref? */ +else { + uint32 pa = Reloc (loc, rel); /* relocate */ + val = M[pa >> 2]; + } +return val; +} + +void WriteB (uint32 loc, uint32 val, uint32 rel) +{ +uint32 pa = loc; +uint32 sc = (3 - (loc & 3)) << 3; + +val = val & DMASK8; +if ((PSW & PSW_REL) == 0) { /* reloc off? */ + uint32 idx = (pa - MAC_BASE) >> 2; /* check for MAC */ + if (idx <= MAC_LNT) { + if (idx < MAC_LNT) mac_reg[idx] = + ((mac_reg[idx] & ~(DMASK8 << sc)) | (val << sc)) & SR_MASK; + else { + mac_sta = 0; + qevent = qevent & ~EV_MAC; + } + } + } +else if (rel != 0) pa = Reloc (loc, rel); /* !phys? relocate */ +if (MEM_ADDR_OK (pa)) M[pa >> 2] = + (M[pa >> 2] & ~(DMASK8 << sc)) | (val << sc); +return; +} + +void WriteH (uint32 loc, uint32 val, uint32 rel) +{ +uint32 pa = loc; + +val = val & DMASK16; +if ((PSW & PSW_REL) == 0) { /* reloc off? */ + uint32 idx = (pa - MAC_BASE) >> 2; /* check for MAC */ + if (idx <= MAC_LNT) { + if (idx < MAC_LNT) mac_reg[idx] = ((loc & 2)? + ((mac_reg[idx] & ~DMASK16) | val): + ((mac_reg[idx] & DMASK16) | (val << 16))) & SR_MASK; + else { + mac_sta = 0; + qevent = qevent & ~EV_MAC; + } + } + } +else if (rel != 0) pa = Reloc (loc, rel); /* !phys? relocate */ +if (MEM_ADDR_OK (pa)) M[pa >> 2] = (loc & 2)? + ((M[pa >> 2] & ~DMASK16) | val): + ((M[pa >> 2] & DMASK16) | (val << 16)); +return; +} + +void WriteF (uint32 loc, uint32 val, uint32 rel) +{ +uint32 pa = loc; + +val = val & DMASK32; +if (loc & 2) { + WriteH (loc & VAMASK, (val >> 16) & DMASK16, rel); + WriteH ((loc + 2) & VAMASK, val & DMASK16, rel); + return; + } +if ((PSW & PSW_REL) == 0) { /* reloc off? */ + uint32 idx = (pa - MAC_BASE) >> 2; /* check for MAC */ + if (idx <= MAC_LNT) { + if (idx < MAC_LNT) mac_reg[idx] = val & SR_MASK; + else { + mac_sta = 0; + qevent = qevent & ~EV_MAC; + } + } + } +else if (rel != 0) pa = Reloc (loc, rel); /* !phys? relocate */ +if (MEM_ADDR_OK (pa)) M[pa >> 2] = val & DMASK32; +return; +} + +uint32 IOReadB (uint32 loc) +{ +uint32 sc = (3 - (loc & 3)) << 3; + +return (M[loc >> 2] >> sc) & DMASK8; +} + +uint32 IOReadH (uint32 loc) +{ +return (M[loc >> 2] >> ((loc & 2)? 0: 16)) & DMASK16; +} + +void IOWriteB (uint32 loc, uint32 val) +{ +uint32 sc = (3 - (loc & 3)) << 3; + +val = val & DMASK8; +M[loc >> 2] = (M[loc >> 2] & ~(DMASK8 << sc)) | (val << sc); +return; +} + +void IOWriteH (uint32 loc, uint32 val) +{ +uint32 sc = (loc & 2)? 0: 16; + +val = val & DMASK16; +M[loc >> 2] = (M[loc >> 2] & ~(DMASK16 << sc)) | (val << sc); +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +qevent = 0; /* no events */ +mac_sta = 0; /* clear MAC */ +newPSW (0); /* PSW = 0 */ +set_r_display (R); +DR = 0; /* clear display */ +drmod = 0; +blk_io.dfl = blk_io.cur = blk_io.end = 0; /* no block I/O */ +sim_brk_types = sim_brk_dflt = SWMASK ('E'); /* init bkpts */ +if (M == NULL) M = (uint32 *) calloc (MAXMEMSIZE32 >> 2, sizeof (uint32)); +if (M == NULL) return SCPE_MEM; +pcq_r = find_reg ("PCQ", NULL, dptr); /* init PCQ */ +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if ((sw & SWMASK ('V')) && (PSW & PSW_REL)) { + int32 cc = RelocT (addr, MAC_BASE, P, &addr); + if (cc & (CC_C | CC_V)) return SCPE_NXM; + } +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = IOReadH (addr); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if ((sw & SWMASK ('V')) && (PSW & PSW_REL)) { + int32 cc = RelocT (addr, MAC_BASE, P, &addr); + if (cc & (CC_C | CC_V)) return SCPE_NXM; + } +if (addr >= MEMSIZE) return SCPE_NXM; +IOWriteH (addr, val); +return SCPE_OK; +} + +/* Change memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE32) || ((val & 0xFFFF) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i = i + 4) mc = mc | M[i >> 2]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE32; i = i + 4) M[i >> 2] = 0; +return SCPE_OK; +} + +/* Set current R pointers for SCP */ + +void set_r_display (uint32 *rbase) +{ +extern REG *find_reg (char *cptr, char **optr, DEVICE *dptr); +REG *rptr; +int32 i; + +rptr = find_reg ("R0", NULL, &cpu_dev); +if (rptr == NULL) return; +for (i = 0; i < 16; i++, rptr++) rptr->loc = (void *) (rbase + i); +return; +} + +/* Set console interrupt */ + +t_stat cpu_set_consint (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (PSW & PSW_EXI) SET_INT (v_DS); +return SCPE_OK; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (uint32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 op, k, di, lnt; +char *cptr = (char *) desc; +t_value sim_eval[3]; +t_stat r; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC r1 operand ea IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(di++) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + fprintf (st, "%06X %08X %08X ", h->pc & VAMASK32, h->r1, h->opnd); + op = (h->ir1 >> 8) & 0xFF; + if (OP_TYPE (op) >= OP_RX) fprintf (st, "%06X ", h->ea); + else fprintf (st, " "); + sim_eval[0] = h->ir1; + sim_eval[1] = h->ir2; + sim_eval[2] = h->ir3; + if ((fprint_sym (st, h->pc & VAMASK32, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %04X", h->ir1); + fputc ('\n', st); /* end line */ + } /* end if instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/Interdata/id32_dboot.c b/Interdata/id32_dboot.c new file mode 100644 index 0000000..28871b2 --- /dev/null +++ b/Interdata/id32_dboot.c @@ -0,0 +1,321 @@ +/* id32_dboot.c: Interdata 32b simulator disk bootstrap + + Copyright (c) 2000-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 17-Jul-06 RMS Fixed transcription errors (found by Davis Johnson) + 17-Feb-03 RMS Fixed for UNIX bootstrap, upper platter bootstrap +*/ + +#include "id_defs.h" + +#define DBOOT_BEG 0x1000 +#define DBOOT_START 0x100E +#define DBOOT_LEN (sizeof (dboot_rom) / sizeof (uint8)) + +/* Transcribed from 32b Bootstrap Loader, 03-074N81R03A13 */ + +static uint8 dboot_rom[] = { + 0xca, 0xf0, 0x00, 0x30, + 0xc5, 0xf0, 0x00, 0x3a, + 0x02, 0x8e, + 0x26, 0xf7, + 0x03, 0x0e, + 0xe6, 0xd0, 0x0f, 0x30, + 0xd1, 0xe0, 0x00, 0x78, + 0xd0, 0xed, 0x03, 0x40, + 0xd3, 0xf0, 0x00, 0x7e, + 0xc4, 0xf0, 0x00, 0x0f, + 0x41, 0xed, 0x00, 0xd0, + 0xd2, 0xfd, 0x03, 0x25, + 0xd3, 0xf0, 0x00, 0x7f, + 0x10, 0xf4, + 0x41, 0xed, 0x00, 0xd0, + 0xd2, 0xfd, 0x03, 0x26, + 0xd3, 0xf0, 0x00, 0x7f, + 0xc4, 0xf0, 0x00, 0x0f, + 0x41, 0xed, 0x00, 0xd0, + 0xd2, 0xfd, 0x03, 0x27, + 0xd3, 0x20, 0x00, 0x7d, + 0xd3, 0x30, 0x00, 0x7c, + 0xd3, 0x40, 0x00, 0x7a, + 0x24, 0x50, + 0xd3, 0xf0, 0x00, 0x7b, + 0xcb, 0xf0, 0x00, 0x33, + 0x23, 0x23, + 0x11, 0xf1, + 0x08, 0x5f, + 0xe6, 0x7d, 0x03, 0x50, + 0xe6, 0x8d, 0x04, 0x4f, + 0x07, 0xcc, + 0x41, 0xed, 0x01, 0xfc, + 0xd1, 0xed, 0x03, 0x5c, + 0xd0, 0xed, 0x03, 0x48, + 0x58, 0xcd, 0x03, 0x58, + 0x43, 0x3d, 0x01, 0x9c, + 0xe6, 0x7d, 0x03, 0x50, + 0x41, 0xed, 0x01, 0xfc, + 0xe6, 0xed, 0x03, 0x54, + 0x24, 0x15, + 0xf8, 0xf0, 0x4f, 0x53, 0x33, 0x32, + 0xd3, 0x7e, 0x00, 0x24, + 0xc3, 0x70, 0x00, 0x10, + 0x23, 0x3e, + 0xc3, 0x70, 0x00, 0xe0, + 0x21, 0x3b, + 0x55, 0xfe, 0x00, 0x00, + 0x21, 0x38, + 0x58, 0x6e, 0x00, 0x08, + 0x10, 0x68, + 0x55, 0x6d, 0x03, 0x24, + 0x43, 0x3d, 0x01, 0xb2, + 0xca, 0xe0, 0x00, 0x30, + 0x27, 0x11, + 0x42, 0x3d, 0x01, 0x66, + 0x58, 0xcd, 0x03, 0x50, + 0x42, 0x3d, 0x01, 0x52, + 0x48, 0x10, 0x00, 0x7e, + 0x42, 0x3d, 0x02, 0xf0, + 0x58, 0xcd, 0x03, 0x48, + 0x43, 0x3d, 0x02, 0xf0, + 0x58, 0x8d, 0x03, 0x4c, + 0x23, 0x07, + 0x58, 0xce, 0x00, 0x0c, + 0x58, 0x8e, 0x00, 0x10, + 0x0b, 0x8c, + 0x26, 0xc1, + 0x11, 0x88, + 0x08, 0x18, + 0xe6, 0xf0, 0x11, 0x18, + 0x58, 0x0f, 0x00, 0x00, + 0x50, 0x01, 0x00, 0x00, + 0x59, 0x01, 0x00, 0x00, + 0x42, 0x3d, 0x03, 0x08, + 0x26, 0xf4, + 0x26, 0x14, + 0xc5, 0xf0, 0x12, 0x78, + 0x20, 0x8c, + 0x08, 0xd8, + 0xcb, 0xd0, 0x01, 0xe8, + 0x03, 0x08, + 0x27, 0x81, + 0x07, 0x77, + 0x41, 0xed, 0x01, 0xfc, + 0xd1, 0xed, 0x03, 0x40, + 0xd0, 0xe0, 0x00, 0x78, + 0x43, 0x00, 0x00, 0x60, + 0xde, 0x2d, 0x03, 0x28, + 0x08, 0x0c, + 0x4d, 0x0d, 0x45, 0x00, 0x03, 0x30, + 0x08, 0x91, + 0x4d, 0x0d, 0x45, 0x00, 0x03, 0x38, + 0x08, 0xa1, + 0x08, 0xb0, + 0x08, 0x55, + 0x42, 0x2d, 0x02, 0x4a, + 0xde, 0x3d, 0x03, 0x28, + 0x9d, 0x3f, + 0x22, 0x21, + 0x9d, 0x4f, + 0x42, 0x1d, 0x02, 0xf4, + 0xc3, 0xf0, 0x00, 0x10, + 0x20, 0x35, + 0x11, 0xa5, + 0x06, 0xba, + 0x98, 0x49, + 0xde, 0x4d, 0x03, 0x2b, + 0x9d, 0x3f, + 0x22, 0x21, + 0x9d, 0x4f, + 0x42, 0x7d, 0x02, 0xf8, + 0x20, 0x83, + 0x41, 0x6d, 0x02, 0x96, + 0x22, 0x0b, + 0x9d, 0x4f, + 0xc3, 0xf0, 0x00, 0x19, + 0x42, 0x3d, 0x02, 0xfc, + 0xde, 0x4d, 0x03, 0x2c, + 0x9d, 0x3f, + 0x22, 0x21, + 0x98, 0x49, + 0xde, 0x4d, 0x03, 0x2e, + 0x9d, 0x3f, + 0x22, 0x21, + 0xde, 0x4d, 0x03, 0x2d, + 0x9d, 0x3f, + 0x22, 0x21, + 0x98, 0x4a, + 0xde, 0x4d, 0x03, 0x2f, + 0x9d, 0x3f, + 0x22, 0x21, + 0xde, 0x4d, 0x03, 0x2b, + 0x9d, 0x3f, + 0x22, 0x21, + 0x9d, 0x4f, + 0x20, 0x81, + 0xc3, 0xf0, 0x00, 0x53, + 0x42, 0x3d, 0x03, 0x00, + 0x08, 0xfa, + 0x11, 0xfa, + 0x06, 0xf9, + 0xe6, 0x6d, 0x02, 0x54, + 0x34, 0x77, + 0x9a, 0x27, + 0x34, 0x77, + 0x98, 0x27, + 0x34, 0x88, + 0x9a, 0x28, + 0x34, 0x88, + 0x98, 0x28, + 0x08, 0x55, + 0x21, 0x24, + 0x98, 0x49, + 0x9a, 0x3b, + 0x23, 0x03, + 0x9a, 0x3b, + 0x98, 0x3f, + 0xde, 0x3d, 0x03, 0x2a, + 0xde, 0x2d, 0x03, 0x29, + 0x9d, 0x2f, + 0x20, 0x81, + 0xde, 0x2d, 0x03, 0x28, + 0x9b, 0x20, + 0x99, 0x21, + 0x34, 0x00, + 0x06, 0x01, + 0xde, 0x2d, 0x03, 0x28, + 0x9d, 0x3f, + 0x22, 0x21, + 0x42, 0x1d, 0x03, 0x04, + 0xc3, 0xf0, 0x00, 0x10, + 0x03, 0x3e, + 0x0b, 0x07, + 0x26, 0x04, + 0xc4, 0x00, 0xff, 0x00, + 0x0a, 0x70, + 0x26, 0x91, + 0x07, 0xaa, + 0x07, 0xbb, + 0x03, 0x06, + 0x24, 0x11, + 0x23, 0x0c, + 0x24, 0x12, + 0x23, 0x0a, + 0x24, 0x13, + 0x23, 0x08, + 0x24, 0x14, + 0x23, 0x06, + 0x24, 0x15, + 0x23, 0x04, + 0x24, 0x16, + 0x23, 0x02, + 0x24, 0x17, + 0x24, 0x01, + 0xde, 0x0d, 0x03, 0x28, + 0x9a, 0x01, + 0xde, 0x0d, 0x03, 0x28, + 0xd1, 0xed, 0x03, 0x40, + 0xd0, 0xe0, 0x00, 0x78, + 0x11, 0x0f, + 0x95, 0x10, + 0x22, 0x01, + 0x00, 0x00, 0x00, 0x00, + 0x48, 0x30, + 0xc1, 0xc2, + 0xc8, 0xc4, + 0xd0, 0xe0, + 0x00, 0x30, + 0x01, 0x90, + 0x01, 0x40, + 0x04, 0xc0, + 0x00, 0x18, + 0x00, 0x14, + 0x00, 0x40, + 0x00, 0x40, + 0x00 + }; + +/* Lower memory setup + + 78 = binary input device address + 79 = binary device input command + 7A = disk device number + 7B = device code + 7C = disk controller address + 7D = selector channel address + 7E:7F = operating system extension (user specified) +*/ + +struct dboot_id { + char *name; + uint32 sw; + uint32 cap; + uint32 dtype; + uint32 offset; + uint32 adder; + }; + +static struct dboot_id dboot_tab[] = { + { "DP", 0, 2, 0x31, o_DP0, 0 }, + { "DP", SWMASK ('F'), 9, 0x32, o_DP0, o_DPF }, + { "DP", 0, 9, 0x33, o_DP0, 0 }, + { "DM", 0, 64, 0x35, o_ID0, 0 }, + { "DM", 0, 244, 0x36, o_ID0, 0 }, + { NULL } + }; + +t_stat id_dboot (int32 u, DEVICE *dptr) +{ +extern DIB ttp_dib, sch_dib; +extern uint32 PC; +extern int32 sim_switches; +uint32 i, typ, ctlno, off, add, cap, sch_dev; +UNIT *uptr; + +DIB *ddib = (DIB *) dptr->ctxt; /* get disk DIB */ +ctlno = ddib->dno; /* get ctrl devno */ +sch_dev = sch_dib.dno + ddib->sch; /* sch dev # */ +uptr = dptr->units + u; /* get capacity */ +cap = uptr->capac >> 20; +for (i = typ = 0; dboot_tab[i].name != NULL; i++) { + if ((strcmp (dboot_tab[i].name, dptr->name) == 0) && + ((dboot_tab[i].sw == 0) || (dboot_tab[i].sw & sim_switches)) && + (dboot_tab[i].cap == cap)) { + typ = dboot_tab[i].dtype; + off = dboot_tab[i].offset; + add = dboot_tab[i].adder; + break; + } + } +if (typ == 0) return SCPE_NOFNC; + +IOWriteBlk (DBOOT_BEG, DBOOT_LEN, dboot_rom); /* copy boot */ +IOWriteB (AL_DEV, ttp_dib.dno); /* bin input dev */ +IOWriteB (AL_IOC, 0xa3); +IOWriteB (AL_DSKU, ctlno + ((u + 1) * off) + add); /* disk dev addr */ +IOWriteB (AL_DSKT, typ); /* disk type */ +IOWriteB (AL_DSKC, ctlno); /* disk ctl addr */ +IOWriteB (AL_SCH, sch_dev); +PC = DBOOT_START; +return SCPE_OK; +} diff --git a/Interdata/id32_sys.c b/Interdata/id32_sys.c new file mode 100644 index 0000000..3c50c67 --- /dev/null +++ b/Interdata/id32_sys.c @@ -0,0 +1,731 @@ +/* id32_sys.c: Interdata 32b simulator interface + + Copyright (c) 2000-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 04-Feb-08 RMS Modified to allow -A, -B use with 8b devices + 25-Jan-07 RMS Fixed conflict between -h (hex) and -h (halfword) + 18-Oct-06 RMS Re-ordered device list + 02-Jul-04 RMS Fixed missing type in declaration + 15-Jul-03 RMS Fixed signed/unsigned bug in get_imm + 27-Feb-03 RMS Added relative addressing support + 23-Dec-01 RMS Cloned from ID4 sources +*/ + +#include "id_defs.h" +#include + +#define MSK_SBF 0x0100 +#define SEXT15(x) (((x) & 0x4000)? ((x) | ~0x3FFF): ((x) & 0x3FFF)) + +extern DEVICE cpu_dev; +extern DEVICE sch_dev; +extern DEVICE pt_dev; +extern DEVICE tt_dev, ttp_dev; +extern DEVICE pas_dev, pasl_dev; +extern DEVICE lpt_dev; +extern DEVICE pic_dev, lfc_dev; +extern DEVICE dp_dev, idc_dev; +extern DEVICE fd_dev, mt_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint32 *M; + +t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val); +t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val); +extern t_stat lp_load (FILE *fileref, char *cptr, char *fnam); +extern t_stat pt_dump (FILE *of, char *cptr, char *fnam); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "Interdata 32b"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 6; + +DEVICE *sim_devices[] = { + &cpu_dev, + &sch_dev, + &pic_dev, + &lfc_dev, + &pt_dev, + &tt_dev, + &ttp_dev, + &pas_dev, + &pasl_dev, + &lpt_dev, + &dp_dev, + &idc_dev, + &fd_dev, + &mt_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Reserved instruction", + "HALT instruction", + "Breakpoint", + "Wait state", + "Runaway VFU" + }; + +/* Binary loader -- load carriage control tape + Binary dump -- paper tape dump */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +if (flag) return pt_dump (fileref, cptr, fnam); +return lp_load (fileref, cptr, fnam); +} + +/* Symbol tables */ + +#define I_V_FL 16 /* class bits */ +#define I_M_FL 0xF /* class mask */ +#define I_V_MR 0x0 /* mask-register */ +#define I_V_RR 0x1 /* register-register */ +#define I_V_R 0x2 /* register */ +#define I_V_MX 0x3 /* mask-memory */ +#define I_V_RX 0x4 /* register-memory */ +#define I_V_X 0x5 /* memory */ +#define I_V_FF 0x6 /* float reg-reg */ +#define I_V_FX 0x7 /* float reg-mem */ +#define I_V_SI 0x8 /* short immed */ +#define I_V_SB 0x9 /* short branch */ +#define I_V_SX 0xA /* short ext branch */ +#define I_V_RI 0xB /* halfword imm */ +#define I_V_RF 0xC /* fullword imm */ +#define I_MR (I_V_MR << I_V_FL) +#define I_RR (I_V_RR << I_V_FL) +#define I_R (I_V_R << I_V_FL) +#define I_MX (I_V_MX << I_V_FL) +#define I_RX (I_V_RX << I_V_FL) +#define I_X (I_V_X << I_V_FL) +#define I_FF (I_V_FF << I_V_FL) +#define I_FX (I_V_FX << I_V_FL) +#define I_SI (I_V_SI << I_V_FL) +#define I_SB (I_V_SB << I_V_FL) +#define I_SX (I_V_SX << I_V_FL) +#define I_RI (I_V_RI << I_V_FL) +#define I_RF (I_V_RF << I_V_FL) + +#define R_X 0 /* no R1 */ +#define R_M 1 /* R1 mask */ +#define R_R 2 /* R1 int reg */ +#define R_F 3 /* R1 flt reg */ + +static const int32 masks[] = { + 0xFF00, 0xFF00, 0xFFF0, 0xFF00, + 0xFF00, 0xFFF0, 0xFF00, 0xFF00, + 0xFF00, 0xFE00, 0xFEF0, 0xFF00, + 0xFF00 + }; + +static const uint32 r1_type[] = { + R_M, R_R, R_X, R_M, + R_R, R_X, R_F, R_F, + R_R, R_M, R_X, R_R, + R_R + }; + +static const uint32 r2_type[] = { + R_X, R_R, R_R, R_X, + R_X, R_X, R_F, R_X, + R_M, R_X, R_X, R_X, + R_X + }; + +static const char *opcode[] = { +"BER", "BNER","BZR", "BNZR", +"BPR", "BNPR","BLR", "BNLR", +"BMR", "BNMR","BOR", "BNOR", +"BCR", "BNCR","BR", +"BES", "BNES","BZS", "BNZS", +"BPS", "BNPS","BLS", "BNLS", +"BMS", "BNMS","BOS", "BNOS", +"BCS", "BNCS","BS", +"BE", "BNE", "BZ", "BNZ", +"BP", "BNP", "BL", "BNL", +"BM", "BNM", "BO", "BNO", +"BC", "BNC", "B", + "BALR","BTCR","BFCR", +"NR", "CLR", "OR", "XR", +"LR", "CHR", "AR", "SR", +"MHR", "DHR", +"SRLS","SLLS","CHVR", +"LPSWR", +"MR", "DR", +"BTBS","BTFS","BFBS","BFFS", +"LIS", "LCS", "AIS", "SIS", +"LER", "CER", "AER", "SER", +"MER", "DER", "FXR", "FLR", +"MPBSR", "PBR", +"EXHR", +"LDR", "CDR", "ADR", "SDR", +"MDR", "DDR", "FXDR","FLDR", +"STH", "BAL", "BTC", "BFC", +"NH", "CLH", "OH", "XH", +"LH", "CH", "AH", "SH", +"MH", "DH", +"ST", "AM", +"N", "CL", "O", "X", +"L", "C", "A", "S", +"M", "D", "CRC12","CRC16", +"STE", "AHM", "PB", "LRA", +"ATL", "ABL", "RTL", "RBL", +"LE", "CE", "AE", "SE", +"ME", "DE", +"STD", "STME","LME", "LHL", +"TBT", "SBT", "RBT", "CBT", +"LD", "CD", "AD", "SD", +"MD", "DD", "STMD","LMD", +"SRHLS","SLHLS","STBR","LBR", +"EXBR","EPSR","WBR", "RBR", +"WHR", "RHR", "WDR", "RDR", + "SSR", "OCR", +"BXH", "BXLE","LPSW","THI", +"NHI", "CLHI","OHI", "XHI", +"LHI", "CHI", "AHI", "SHI", +"SRHL","SLHL","SRHA","SLHA", +"STM", "LM", "STB", "LB", +"CLB", "AL", "WB", "RB", +"WH", "RH", "WD", "RD", + "SS", "OC", +"TS", "SVC", "SINT","SCP", + "LA", "TLATE", + "RRL", "RLL", +"SRL", "SLL", "SRA", "SLA", + "TI", +"NI", "CLI", "OI", "XI", +"LI", "CI", "AI", "SI", +NULL +}; + +static const uint32 opc_val[] = { +0x0330+I_R, 0x0230+I_R, 0x0330+I_R, 0x0230+I_R, +0x0220+I_R, 0x0320+I_R, 0x0280+I_R, 0x0380+I_R, +0x0210+I_R, 0x0310+I_R, 0x0240+I_R, 0x0340+I_R, +0x0280+I_R, 0x0380+I_R, 0x0300+I_R, +0x2230+I_SX, 0x2030+I_SX, 0x2230+I_SX, 0x2030+I_SX, +0x2020+I_SX, 0x2220+I_SX, 0x2080+I_SX, 0x2280+I_SX, +0x2010+I_SX, 0x2210+I_SX, 0x2040+I_SX, 0x2240+I_SX, +0x2080+I_SX, 0x2280+I_SX, 0x2200+I_SX, +0x4330+I_X, 0x4230+I_X, 0x4330+I_X, 0x4230+I_X, +0x4220+I_X, 0x4320+I_X, 0x4280+I_X, 0x4380+I_X, +0x4210+I_X, 0x4310+I_X, 0x4240+I_X, 0x4340+I_X, +0x4280+I_X, 0x4380+I_X, 0x4300+I_X, + 0x0100+I_RR, 0x0200+I_MR, 0x0300+I_MR, +0x0400+I_RR, 0x0500+I_RR, 0x0600+I_RR, 0x0700+I_RR, +0x0800+I_RR, 0x0900+I_RR, 0x0A00+I_RR, 0x0B00+I_RR, +0x0C00+I_RR, 0x0D00+I_RR, +0x1000+I_SI, 0x1100+I_SI, 0x1200+I_RR, +0x1800+I_RR, +0x1C00+I_RR, 0x1D00+I_RR, +0x2000+I_SB, 0x2100+I_SB, 0x2200+I_SB, 0x2300+I_SB, +0x2400+I_SI, 0x2500+I_SI, 0x2600+I_SI, 0x2700+I_SI, +0x2800+I_FF, 0x2900+I_FF, 0x2A00+I_FF, 0x2B00+I_FF, +0x2C00+I_FF, 0x2D00+I_FF, 0x2E00+I_RR, 0x2F00+I_RR, +0x3000+I_RR, 0x3200+I_RR, +0x3400+I_RR, +0x3800+I_FF, 0x3900+I_FF, 0x3A00+I_FF, 0x3B00+I_FF, +0x3C00+I_FF, 0x3D00+I_FF, 0x3E00+I_RR, 0x3F00+I_RR, +0x4000+I_RX, 0x4100+I_RX, 0x4200+I_MX, 0x4300+I_MX, +0x4400+I_RX, 0x4500+I_RX, 0x4600+I_RX, 0x4700+I_RX, +0x4800+I_RX, 0x4900+I_RX, 0x4A00+I_RX, 0x4B00+I_RX, +0x4C00+I_RX, 0x4D00+I_RX, +0x5000+I_RX, 0x5100+I_RX, +0x5400+I_RX, 0x5500+I_RX, 0x5600+I_RX, 0x5700+I_RX, +0x5800+I_RX, 0x5900+I_RX, 0x5A00+I_RX, 0x5B00+I_RX, +0x5C00+I_RX, 0x5D00+I_RX, 0x5E00+I_RX, 0x5F00+I_RX, +0x6000+I_RX, 0x6100+I_RX, 0x6200+I_RX, 0x6300+I_RX, +0x6400+I_RX, 0x6500+I_RX, 0x6600+I_RX, 0x6700+I_RX, +0x6800+I_FX, 0x6900+I_FX, 0x6A00+I_FX, 0x6B00+I_FX, +0x6C00+I_FX, 0x6D00+I_FX, +0x7000+I_FX, 0x7100+I_FX, 0x7200+I_FX, 0x7300+I_RX, +0x7400+I_RX, 0x7500+I_RX, 0x7600+I_RX, 0x7700+I_RX, +0x7800+I_FX, 0x7900+I_FX, 0x7A00+I_FX, 0x7B00+I_FX, +0x7C00+I_FX, 0x7D00+I_FX, 0x7E00+I_FX, 0x7F00+I_FX, +0x9000+I_SI, 0x9100+I_SI, 0x9200+I_RR, 0x9300+I_RR, +0x9400+I_RR, 0x9500+I_RR, 0x9600+I_RR, 0x9700+I_RR, +0x9800+I_RR, 0x9900+I_RR, 0x9A00+I_RR, 0x9B00+I_RR, + 0x9D00+I_RR, 0x9E00+I_RR, +0xC000+I_RX, 0xC100+I_RX, 0xC200+I_RX, 0xC300+I_RI, +0xC400+I_RI, 0xC500+I_RI, 0xC600+I_RI, 0xC700+I_RI, +0xC800+I_RI, 0xC900+I_RI, 0xCA00+I_RI, 0xCB00+I_RI, +0xCC00+I_RI, 0xCD00+I_RI, 0xCE00+I_RI, 0xCF00+I_RI, +0xD000+I_RX, 0xD100+I_RX, 0xD200+I_RX, 0xD300+I_RX, +0xD400+I_RX, 0xD500+I_X, 0xD600+I_RX, 0xD700+I_RX, +0xD800+I_RX, 0xD900+I_RX, 0xDA00+I_RX, 0xDB00+I_RX, + 0xDD00+I_RX, 0xDE00+I_RX, +0xE000+I_RX, 0xE100+I_RX, 0xE200+I_RI, 0xE300+I_RX, + 0xE600+I_RX, 0xE700+I_RX, + 0xEA00+I_RI, 0xEB00+I_RI, +0xEC00+I_RI, 0xED00+I_RI, 0xEE00+I_RI, 0xEF00+I_RI, + 0xF300+I_RF, +0xF400+I_RF, 0xF500+I_RF, 0xF600+I_RF, 0xF700+I_RF, +0xF800+I_RF, 0xF900+I_RF, 0xFA00+I_RF, 0xFB00+I_RF, +0xFFFF +}; + +/* Print an RX specifier */ + +t_stat fprint_addr (FILE *of, t_addr addr, uint32 rx, uint32 ea1, + uint32 ea2) +{ +uint32 rx2; + +if ((ea1 & 0xC000) == 0) { /* RX1 */ + fprintf (of, "%-X", ea1); + if (rx) fprintf (of, "(R%d)", rx); + return -3; + } +if (ea1 & 0x8000) { /* RX2 */ + ea1 = addr + 4 + SEXT15 (ea1); + fprintf (of, "%-X", ea1 & VAMASK32); + if (rx) fprintf (of, "(R%d)", rx); + return -3; + } +rx2 = (ea1 >> 8) & 0xF; /* RX3 */ +fprintf (of, "%-X", ((ea1 << 16) | ea2) & VAMASK32); +if (rx && !rx2) fprintf (of, "(R%d)", rx); +if (rx2) fprintf (of, "(R%d,R%d)", rx, rx2); +return -5; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 bflag, c1, c2, rdx; +t_stat r; +DEVICE *dptr; + +if (uptr == NULL) uptr = &cpu_unit; /* anon = CPU */ +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) return SCPE_IERR; +if (dptr->dwidth < 16) bflag = 1; /* 8b dev? */ +else bflag = 0; /* assume 16b */ +if (sw & SWMASK ('D')) rdx = 10; /* get radix */ +else if (sw & SWMASK ('O')) rdx = 8; +else if (sw & SWMASK ('H')) rdx = 16; +else rdx = dptr->dradix; + +if (sw & SWMASK ('A')) { /* ASCII char? */ + if (bflag) c1 = val[0] & 0x7F; + else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0x7F; /* get byte */ + fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); + return 0; + } +if (sw & SWMASK ('B')) { /* byte? */ + if (bflag) c1 = val[0] & 0xFF; + else c1 = (val[0] >> ((addr & 1)? 0: 8)) & 0xFF; /* get byte */ + fprint_val (of, c1, rdx, 8, PV_RZRO); + return 0; + } +if (bflag) return SCPE_ARG; /* 16b only */ + +if (sw & SWMASK ('C')) { /* string? */ + c1 = (val[0] >> 8) & 0x7F; + c2 = val[0] & 0x7F; + fprintf (of, (c1 < 0x20)? "<%02X>": "%c", c1); + fprintf (of, (c2 < 0x20)? "<%02X>": "%c", c2); + return -1; + } +if (sw & SWMASK ('W')) { /* halfword? */ + fprint_val (of, val[0], rdx, 16, PV_RZRO); + return -1; + } +if (sw & SWMASK ('M')) { /* inst format? */ + r = fprint_sym_m (of, addr, val); /* decode inst */ + if (r <= 0) return r; + } + +fprint_val (of, (val[0] << 16) | val[1], rdx, 32, PV_RZRO); +return -3; +} + +/* Symbolic decode for -m + + Inputs: + of = output stream + addr = current PC + *val = values to decode + cf = true if parsing for CPU + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym_m (FILE *of, t_addr addr, t_value *val) +{ +uint32 i, j, inst, r1, r2, ea1, ea2; + +inst = val[0]; +ea1 = val[1]; +ea2 = val[2]; +for (i = 0; opc_val[i] != 0xFFFF; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 0xFFFF) == (inst & masks[j])) { /* match? */ + r1 = (inst >> 4) & 0xF; + r2 = inst & 0xF; + fprintf (of, "%s ", opcode[i]); /* print opcode */ + switch (j) { /* case on class */ + + case I_V_MR: /* mask-register */ + fprintf (of, "%-X,R%d", r1, r2); + return -1; + + case I_V_RR: /* register-register */ + case I_V_FF: /* floating-floating */ + fprintf (of, "R%d,R%d", r1, r2); + return -1; + + case I_V_SI: /* short immediate */ + fprintf (of, "R%d,%-X", r1, r2); + return -1; + + case I_V_SB: /* short branch */ + fprintf (of, "%-X,", r1); + case I_V_SX: /* ext short branch */ + fprintf (of, "%-X", ((inst & MSK_SBF)? + (addr + r2 + r2): (addr - r2 - r2))); + return -1; + + case I_V_R: /* register */ + fprintf (of, "R%d", r2); + return -1; + + case I_V_RI: /* reg-immed */ + fprintf (of, "R%d,%-X", r1, ea1); + if (r2) fprintf (of, "(R%d)", r2); + return -3; + + case I_V_RF: /* reg-full imm */ + fprintf (of, "R%d,%-X", r1, (ea1 << 16) | ea2); + if (r2) fprintf (of, "(R%d)", r2); + return -5; + + case I_V_MX: /* mask-memory */ + fprintf (of, "%-X,", r1); + return fprint_addr (of, addr, r2, ea1, ea2); + + case I_V_RX: /* register-memory */ + case I_V_FX: /* floating-memory */ + fprintf (of, "R%d,", r1); + case I_V_X: /* memory */ + return fprint_addr (of, addr, r2, ea1, ea2); + } /* end case */ + return SCPE_IERR; + } /* end if */ + } /* end for */ +return SCPE_ARG; /* no match */ +} + +/* Register number + + Inputs: + *cptr = pointer to input string + **optr = pointer to pointer to next char + rtype = mask, integer, or float + Outputs: + rnum = output register number, -1 if error +*/ + +int32 get_reg (char *cptr, char **optr, int32 rtype) +{ +int32 reg; + +if ((*cptr == 'R') || (*cptr == 'r')) { /* R? */ + cptr++; /* skip */ + if (rtype == R_M) return -1; /* cant be mask */ + } +if ((*cptr >= '0') && (*cptr <= '9')) { + reg = *cptr++ - '0'; + if ((*cptr >= '0') && (*cptr <= '9')) + reg = (reg * 10) + (*cptr - '0'); + else --cptr; + if (reg > 0xF) return -1; + } +else if ((*cptr >= 'a') && (*cptr <= 'f')) reg = (*cptr - 'a') + 10; +else if ((*cptr >= 'A') && (*cptr <= 'F')) reg = (*cptr - 'A') + 10; +else return -1; +if ((rtype == R_F) && (reg & 1)) return -1; +*optr = cptr + 1; +return reg; +} + +/* Immediate + + Inputs: + *cptr = pointer to input string + *imm = pointer to output value + *inst = pointer to instruction + max = max value + Outputs: + sta = status +*/ + +t_stat get_imm (char *cptr, uint32 *imm, uint32 *inst, uint32 max) +{ +char *tptr; +int32 idx; + +errno = 0; +*imm = strtoul (cptr, &tptr, 16); /* get immed */ +if (errno || (*imm > max) || (cptr == tptr)) return SCPE_ARG; +if (*tptr == '(') { /* index? */ + if ((idx = get_reg (tptr + 1, &tptr, R_R)) < 0) + return SCPE_ARG; + if (*tptr++ != ')') return SCPE_ARG; + *inst = *inst | idx; + } +if (*tptr != 0) return SCPE_ARG; +return SCPE_OK; +} + +/* Address + + Inputs: + *cptr = pointer to input string + **tptr = pointer to moved pointer + *ea = effective address + addr = base address + Outputs: + status = SCPE_OK if ok, else error code +*/ + +t_stat get_addr (char *cptr, char **tptr, t_addr *ea, t_addr addr) +{ +int32 sign = 1; + +if (*cptr == '.') { /* relative? */ + cptr++; + *ea = addr; + if (*cptr == '+') cptr++; /* .+? */ + else if (*cptr == '-') { /* .-? */ + sign = -1; + cptr++; + } + else return SCPE_OK; + } +else *ea = 0; +errno = 0; +*ea = *ea + (sign * ((int32) strtoul (cptr, tptr, 16))); +if (errno || (cptr == *tptr)) return SCPE_ARG; +return SCPE_OK; +} + +/* Symbolic input */ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 bflag, by, rdx, num; +t_stat r; +DEVICE *dptr; + +if (uptr == NULL) uptr = &cpu_unit; /* anon = CPU */ +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) return SCPE_IERR; +if (dptr->dwidth < 16) bflag = 1; /* 8b device? */ +else bflag = 0; /* assume 16b */ +if (sw & SWMASK ('D')) rdx = 10; /* get radix */ +else if (sw & SWMASK ('O')) rdx = 8; +else if (sw & SWMASK ('H')) rdx = 16; +else rdx = dptr->dradix; + +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + if (bflag) val[0] = (t_value) cptr[0]; + else val[0] = (addr & 1)? + (val[0] & ~0xFF) | ((t_value) cptr[0]): + (val[0] & 0xFF) | (((t_value) cptr[0]) << 8); + return 0; + } +if (sw & SWMASK ('B')) { /* byte? */ + by = get_uint (cptr, rdx, DMASK8, &r); /* get byte */ + if (r != SCPE_OK) return SCPE_ARG; + if (bflag) val[0] = by; + else val[0] = (addr & 1)? + (val[0] & ~0xFF) | by: + (val[0] & 0xFF) | (by << 8); + return 0; + } +if (bflag) return SCPE_ARG; /* 16b only */ + +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII chars? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((t_value) cptr[0] << 8) | (t_value) cptr[1]; + return -1; + } +if (sw & SWMASK ('W')) { /* halfword? */ + val[0] = (int32) get_uint (cptr, rdx, DMASK16, &r); /* get number */ + if (r != SCPE_OK) return r; + return -1; + } + +r = parse_sym_m (cptr, addr, val); /* try to parse inst */ +if (r <= 0) return r; +num = (int32) get_uint (cptr, rdx, DMASK32, &r); /* get number */ +if (r != SCPE_OK) return r; +val[0] = (num >> 16) & DMASK16; +val[1] = num & DMASK16; +return -3; +} + +/* Symbolic input for -m + + Inputs: + *cptr = pointer to input string + addr = current PC + *val = pointer to output values + cf = true if parsing for CPU + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym_m (char *cptr, t_addr addr, t_value *val) +{ +uint32 i, j, df, db, t, inst, vp; +int32 st, r1, r2, rx2; +t_stat r; +char *tptr, gbuf[CBUFSIZE]; + +vp = 0; +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +inst = opc_val[i] & 0xFFFF; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ +if (r1_type[j]) { /* any R1 field? */ + cptr = get_glyph (cptr, gbuf, ','); /* get R1 field */ + if ((r1 = get_reg (gbuf, &tptr, r1_type[j])) < 0) + return SCPE_ARG; + if (*tptr != 0) return SCPE_ARG; + inst = inst | (r1 << 4); /* or in R1 */ + } + +cptr = get_glyph (cptr, gbuf, 0); /* get operand */ +if (*cptr) return SCPE_ARG; /* should be end */ +switch (j) { /* case on class */ + + case I_V_FF: case I_V_SI: /* flt-flt, sh imm */ + case I_V_MR: case I_V_RR: /* mask/reg-register */ + case I_V_R: /* register */ + if ((r2 = get_reg (gbuf, &tptr, r2_type[j])) < 0) + return SCPE_ARG; + if (*tptr != 0) return SCPE_ARG; + inst = inst | r2; /* or in R2 */ + break; + + case I_V_FX: /* float-memory */ + case I_V_MX: case I_V_RX: /* mask/reg-memory */ + case I_V_X: /* memory */ + r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ + if (r != SCPE_OK) return SCPE_ARG; /* error? */ + rx2 = 0; /* assume no 2nd */ + if (*tptr == '(') { /* index? */ + if ((r2 = get_reg (tptr + 1, &tptr, R_R)) < 0) + return SCPE_ARG; + inst = inst | r2; /* or in R2 */ + if (*tptr == ',') { /* 2nd index? */ + if ((rx2 = get_reg (tptr + 1, &tptr, R_R)) < 0) + return SCPE_ARG; + } + if (*tptr++ != ')') return SCPE_ARG; /* all done? */ + } + if (*tptr != 0) return SCPE_ARG; + val[0] = inst; /* store inst */ + if (rx2 == 0) { /* no 2nd? */ + if (t < 0x4000) { /* RX1? */ + val[1] = t; /* store ea */ + return -3; + } + st = (t - (addr + 4)); /* displ */ + if ((st <= 0x3FFF) && (st >= -0x4000)) { /* RX2? CPU only */ + t = (st & 0x7FFF) | 0x8000; + val[1] = t; /* store displ */ + return -3; + } + } + t = (t & VAMASK32) | 0x40000000 | (rx2 << 24); + val[1] = (t >> 16) & DMASK16; + val[2] = t & DMASK16; + return -5; + + case I_V_RI: /* 16b immediate */ + r = get_imm (gbuf, &t, &inst, DMASK16); /* process imm */ + if (r != SCPE_OK) return r; + val[0] = inst; + val[1] = t; + return -3; + + case I_V_RF: + r = get_imm (gbuf, &t, &inst, DMASK32); /* process imm */ + if (r != SCPE_OK) return r; + val[0] = inst; + val[1] = (t >> 16) & DMASK16; + val[2] = t & DMASK16; + return -5; + + case I_V_SB: case I_V_SX: /* short branches */ + r = get_addr (gbuf, &tptr, &t, addr); /* get addr */ + if ((r != SCPE_OK) || (t & 1) || *tptr) /* error if odd */ + return SCPE_ARG; + st = t; /* signed version */ + db = (addr - t) & 0x1F; /* back displ */ + df = (t - addr) & 0x1F; /* fwd displ */ + if ((t == ((addr - db) & VAMASK16)) && /* back work and */ + ((j == I_V_SX) || !(inst & MSK_SBF))) /* ext or back br? */ + inst = inst | (db >> 1); /* or in back displ */ + else if ((t == ((addr + df) & VAMASK16)) && /* fwd work and */ + ((j == I_V_SX) || (inst & MSK_SBF))) /* ext or fwd br? */ + inst = inst | (df >> 1) | MSK_SBF; /* or in fwd displ */ + else return SCPE_ARG; + } /* end case */ + +val[0] = inst; +return -1; +} diff --git a/Interdata/id_defs.h b/Interdata/id_defs.h new file mode 100644 index 0000000..799b76e --- /dev/null +++ b/Interdata/id_defs.h @@ -0,0 +1,481 @@ +/* id_defs.h: Interdata 16b/32b simulator definitions + + Copyright (c) 2000-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + The author gratefully acknowledges the help of Carl Friend and Al Kossow, + who provided key documents about the Interdata product line. + + 09-Mar-06 RMS Increased register sets to architectural limit + 25-Jan-04 RMS Removed local logging support + 22-Sep-03 RMS Added additional instruction decode types + 21-Jun-03 RMS Changed subroutine argument for ARM compiler conflict + 25-Apr-03 RMS Revised for extended file support + 28-Feb-03 RMS Changed magtape device default to 0x85 +*/ + +#ifndef _ID_DEFS_H_ +#define _ID_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* undef instr */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_WAIT 4 /* wait */ +#define STOP_VFU 5 /* runaway VFU */ + +/* Memory */ + +#define PAWIDTH16 16 +#define PAWIDTH16E 18 +#define PAWIDTH32 20 +#define MAXMEMSIZE16 (1u << PAWIDTH16) /* max mem size, 16b */ +#define MAXMEMSIZE16E (1u << PAWIDTH16E) /* max mem size, 16b E */ +#define MAXMEMSIZE32 (1u << PAWIDTH32) /* max mem size, 32b */ +#define PAMASK16 (MAXMEMSIZE16 - 1) /* phys mem mask */ +#define PAMASK16E (MAXMEMSIZE16E - 1) +#define PAMASK32 (MAXMEMSIZE32 - 1) + +#define MEMSIZE (cpu_unit.capac) /* act memory size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* Single precision floating point registers */ + +#if defined (IFP_IN_MEM) +#define ReadFReg(r) (fp_in_hwre? \ + F[(r) >> 1]: ReadF (((r) << 1) & ~3, P)) +#define WriteFReg(r,v) if (fp_in_hwre) F[(r) >> 1] = (v); \ + else WriteF (((r) << 1) & ~3, (v), P) +#else +#define ReadFReg(r) (F[(r) >> 1]) +#define WriteFReg(r,v) F[(r) >> 1] = (v) +#endif + +/* Double precision floating point registers */ + +typedef struct { + uint32 h; /* high 32b */ + uint32 l; /* low 32b */ + } dpr_t; + +/* Architectural constants */ + +#define VAMASK16 (0xFFFF) /* 16b virt addr */ +#define VAMASK32 (0x000FFFFF) /* 32b virt addr */ + +#define SIGN8 0x80 /* 8b sign bit */ +#define DMASK8 0xFF /* 8b data mask */ +#define MMASK8 0x7F /* 8b magnitude mask */ +#define SIGN16 0x8000 /* 16b sign bit */ +#define DMASK16 0xFFFF /* 16b data mask */ +#define MMASK16 0x7FFF /* 16b magnitude mask */ +#define SIGN32 0x80000000 /* 32b sign bit */ +#define DMASK32 0xFFFFFFFF /* 32b data mask */ +#define MMASK32 0x7FFFFFFF /* 32b magn mask */ + +#define CC_C 0x8 /* carry */ +#define CC_V 0x4 /* overflow */ +#define CC_G 0x2 /* greater than */ +#define CC_L 0x1 /* less than */ +#define CC_MASK (CC_C | CC_V | CC_G | CC_L) + +#define PSW_WAIT 0x8000 /* wait */ +#define PSW_EXI 0x4000 /* ext intr enable */ +#define PSW_MCI 0x2000 /* machine check enable */ +#define PSW_AFI 0x1000 /* arith fault enb */ +#define PSW_AIO 0x0800 /* auto I/O int enable */ +#define PSW_FPF 0x0400 /* flt fault enb, 16b */ +#define PSW_REL 0x0400 /* reloc enb, 32b */ +#define PSW_SQI 0x0200 /* sys q int enable */ +#define PSW_PRO 0x0100 /* protect mode */ +#define PSW_V_MAP 4 /* mem map, 16b */ +#define PSW_M_MAP 0xF +#define PSW_MAP (PSW_M_MAP << PSW_V_MAP) +#define PSW_V_REG 4 /* reg set, 32b */ +#define PSW_M_REG 0xF +#define PSW_ID4 0xF40F /* I3, I4 PSW */ +#define PSW_x16 0xFF0F /* 7/16, 8/16 PSW */ +#define PSW_816E 0xFFFF /* 8/16E PSW */ +#define PSW_x32 0xFFFF /* 7/32, 8/32 PSW */ + +#define MCKOPSW 0x20 /* mchk old PSW, 32b */ +#define FPFPSW 0x28 /* flt fault PSW, 16b */ +#define ILOPSW 0x30 /* ill op PSW */ +#define MCKPSW 0x38 /* mach chk PSW */ +#define EXIPSW 0x40 /* ext intr PSW, 16b */ +#define AFIPSW 0x48 /* arith flt PSW */ +#define SQP 0x80 /* system queue ptr */ +#define SQIPSW 0x82 /* sys q int PSW, 16b */ +#define SQOP 0x8A /* sys q ovf ptr, 16b */ +#define SQVPSW 0x8C /* sys q ovf PSW, 16b */ +#define SQTPSW 0x88 /* sys q int PSW, 32b */ +#define MPRPSW 0x90 /* mprot int PSW, 32b */ +#define SVCAP 0x94 /* svc arg ptr, 16b */ +#define SVOPS 0x96 /* svc old PS, 16b */ +#define SVOPC 0x98 /* svc old PC, 16b */ +#define SVNPS32 0x98 /* svc new PS, 32b */ +#define SVNPS 0x9A /* svc new PS, 16b */ +#define SVNPC 0x9C /* svc new PC */ +#define INTSVT 0xD0 /* int service table */ + +#define AL_DEV 0x78 /* autoload: dev */ +#define AL_IOC 0x79 /* command */ +#define AL_DSKU 0x7A /* disk unit */ +#define AL_DSKT 0x7B /* disk type */ +#define AL_DSKC 0x7C /* disk ctrl */ +#define AL_SCH 0x7D /* sel chan */ +#define AL_EXT 0x7E /* OS extension */ +#define AL_BUF 0x80 /* buffer start */ + +#define Q16_SLT 0 /* list: # slots */ +#define Q16_USD 1 /* # in use */ +#define Q16_TOP 2 /* current top */ +#define Q16_BOT 3 /* next bottom */ +#define Q16_BASE 4 /* base of q */ +#define Q16_SLNT 2 /* slot length */ + +#define Q32_SLT 0 /* list: # slots */ +#define Q32_USD 2 /* # in use */ +#define Q32_TOP 4 /* current top */ +#define Q32_BOT 6 /* next bottom */ +#define Q32_BASE 8 /* base of q */ +#define Q32_SLNT 4 /* slot length */ + +/* CPU event flags */ + +#define EV_MAC 0x01 /* MAC interrupt */ +#define EV_BLK 0x02 /* block I/O in prog */ +#define EV_INT 0x04 /* interrupt pending */ +#define EV_WAIT 0x08 /* wait state pending */ + +/* Block I/O state */ + +struct BlockIO { + uint32 dfl; /* devno, flags */ + uint32 cur; /* current addr */ + uint32 end; /* end addr */ + }; + +#define BL_RD 0x8000 /* block read */ +#define BL_LZ 0x4000 /* skip 0's */ + +/* Instruction decode ROM, for all, 16b, 32b */ + +#define OP_UNDEF 0x0000 /* undefined */ +#define OP_NO 0x0001 /* all: short or fp rr */ +#define OP_RR 0x0002 /* all: reg-reg */ +#define OP_RS 0x0003 /* 16b: reg-storage */ +#define OP_RI1 0x0003 /* 32b: reg-imm 16b */ +#define OP_RX 0x0004 /* all: reg-mem */ +#define OP_RXB 0x0005 /* all: reg-mem, rd BY */ +#define OP_RXH 0x0006 /* all: reg-mem, rd HW */ +#define OP_RXF 0x0007 /* 32b: reg-mem, rd FW */ +#define OP_RI2 0x0008 /* 32b: reg-imm 32b */ +#define OP_MASK 0x000F + +#define OP_ID4 0x0010 /* 16b: ID4 */ +#define OP_716 0x0020 /* 16b: 7/16 */ +#define OP_816 0x0040 /* 16b: 8/16 */ +#define OP_816E 0x0080 /* 16b: 8/16E */ + +#define OP_DPF 0x4000 /* all: hwre FP */ +#define OP_PRV 0x8000 /* all: privileged */ + +#define OP_TYPE(x) (decrom[(x)] & OP_MASK) +#define OP_DPFP(x) (decrom[(x)] & OP_DPF) + +/* Device information block */ + +typedef struct { + uint32 dno; /* device number */ + int32 sch; /* sch */ + uint32 irq; /* interrupt */ + uint8 *tplte; /* template */ + uint32 (*iot)(uint32 d, uint32 o, uint32 dat); + void (*ini)(t_bool f); + } DIB; + +#define TPL_END 0xFF /* template end */ + +/* Device select return codes */ + +#define BY 0 /* 8b only */ +#define HW 1 /* 8b/16b */ + +/* I/O operations */ + +#define IO_ADR 0x0 /* address select */ +#define IO_RD 0x1 /* read byte */ +#define IO_RH 0x2 /* read halfword */ +#define IO_WD 0x3 /* write byte */ +#define IO_WH 0x4 /* write halfword */ +#define IO_OC 0x5 /* output command */ +#define IO_SS 0x6 /* sense status */ + +/* Device command byte */ + +#define CMD_V_INT 6 /* interrupt control */ +#define CMD_M_INT 0x3 +#define CMD_IENB 1 /* enable */ +#define CMD_IDIS 2 /* disable */ +#define CMD_IDSA 3 /* disarm */ +#define CMD_GETINT(x) (((x) >> CMD_V_INT) & CMD_M_INT) + +/* Device status byte */ + +#define STA_BSY 0x8 /* busy */ +#define STA_EX 0x4 /* examine status */ +#define STA_EOM 0x2 /* end of medium */ +#define STA_DU 0x1 /* device unavailable */ + +/* Default device numbers */ + +#define DEV_LOW 0x01 /* lowest intr dev */ +#define DEV_MAX 0xFF /* highest intr dev */ +#define DEVNO (DEV_MAX + 1) /* number of devices */ +#define d_DS 0x01 /* display, switches */ +#define d_TT 0x02 /* teletype */ +#define d_PT 0x03 /* reader */ +#define d_CD 0x04 /* card reader */ +#define d_TTP 0x10 /* PAS as console */ +#define d_PAS 0x10 /* first PAS */ +#define o_PASX 0x01 /* offset to xmt */ +#define d_LPT 0x62 /* line printer */ +#define d_PIC 0x6C /* interval timer */ +#define d_LFC 0x6D /* line freq clk */ +#define d_MT 0x85 /* magtape */ +#define o_MT0 0x10 +#define d_DPC 0xB6 /* disk controller */ +#define o_DP0 0x10 +#define o_DPF 0x01 /* offset to fixed */ +#define d_FD 0xC1 /* floppy disk */ +#define d_SCH 0xF0 /* selector chan */ +#define d_IDC 0xFB /* MSM disk ctrl */ +#define o_ID0 0x01 + +/* Interrupts + + To make interrupt flags independent of device numbers, each device is + assigned an interrupt flag in one of four interrupt words + + word 0 DMA devices + word 1 programmed I/O devices + word 2-3 PAS devices + + Devices are identified by a level and a bit within a level. Priorities + run low to high in the array, right to left within words +*/ + +#define INTSZ 4 /* interrupt words */ +#define SCH_NUMCH 4 /* #channels */ +#define ID_NUMDR 4 /* # MSM drives */ +#define DP_NUMDR 4 /* # DPC drives */ +#define MT_NUMDR 4 /* # MT drives */ + +/* Word 0, DMA devices */ + +#define i_SCH 0 /* highest priority */ +#define i_IDC (i_SCH + SCH_NUMCH) /* MSM disk ctrl */ +#define i_DPC (i_IDC + ID_NUMDR + 1) /* cartridge disk ctrl */ +#define i_MT (i_DPC + DP_NUMDR + 1) /* magtape */ + +#define l_SCH 0 +#define l_IDC 0 +#define l_DPC 0 +#define l_MT 0 + +#define v_SCH (l_SCH * 32) + i_SCH +#define v_IDC (l_IDC * 32) + i_IDC +#define v_DPC (l_DPC * 32) + i_DPC +#define v_MT (l_MT * 32) + i_MT + +/* Word 1, programmed I/O devices */ + +#define i_PIC 0 /* precision clock */ +#define i_LFC 1 /* line clock */ +#define i_FD 2 /* floppy disk */ +#define i_CD 3 /* card reader */ +#define i_LPT 4 /* line printer */ +#define i_PT 5 /* paper tape */ +#define i_TT 6 /* teletype */ +#define i_DS 7 /* display */ +#define i_TTP 10 /* PAS console */ + +#define l_PIC 1 +#define l_LFC 1 +#define l_FD 1 +#define l_CD 1 +#define l_LPT 1 +#define l_PT 1 +#define l_TT 1 +#define l_DS 1 +#define l_TTP 1 + +#define v_PIC (l_PIC * 32) + i_PIC +#define v_LFC (l_LFC * 32) + i_LFC +#define v_FD (l_FD * 32) + i_FD +#define v_CD (l_CD * 32) + i_CD +#define v_LPT (l_LPT * 32) + i_LPT +#define v_PT (l_PT * 32) + i_PT +#define v_TT (l_TT * 32) + i_TT +#define v_DS (l_DS * 32) + i_DS +#define v_TTP (l_TTP * 32) + i_TTP + +/* Word 2-3, PAS devices */ + +#define i_PAS 0 +#define l_PAS 2 +#define v_PAS (l_PAS * 32) + i_PAS +#define v_PASX (v_PAS + 1) /* offset to xmt */ + +/* I/O macros */ + +#define SET_INT(v) int_req[(v) >> 5] = int_req[(v) >> 5] | (1u << ((v) & 0x1F)) +#define CLR_INT(v) int_req[(v) >> 5] = int_req[(v) >> 5] & ~(1u << ((v) & 0x1F)) +#define SET_ENB(v) int_enb[(v) >> 5] = int_enb[(v) >> 5] | (1u << ((v) & 0x1F)) +#define CLR_ENB(v) int_enb[(v) >> 5] = int_enb[(v) >> 5] & ~(1u << ((v) & 0x1F)) + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Device accessible macro */ + +#define DEV_ACC(d) (dev_tab[d] && !sch_blk (d)) + +/* Automatic I/O channel programs, 16b */ + +#define CCB16_CHN -4 /* chain */ +#define CCB16_DEV -2 /* dev no */ +#define CCB16_STS -1 /* status */ +#define CCB16_CCW 0 /* cmd wd */ +#define CCB16_STR 2 /* start */ +#define CCB16_END 4 /* end */ +#define CCB16_IOC 6 /* OC byte */ +#define CCB16_TRM 7 /* term byte */ + +#define CCW16_INIT 0x8000 /* init */ +#define CCW16_NOP 0x4000 /* nop */ +#define CCW16_V_FNC 12 /* function */ +#define CCW16_M_FNC 0x3 +#define CCW16_FNC(x) (((x) >> CCW16_V_FNC) & CCW16_M_FNC) +#define CCW16_RD 0 /* read */ +#define CCW16_WR 1 /* write */ +#define CCW16_DMT 2 /* dec mem */ +#define CCW16_NUL 3 /* null */ +#define CCW16_TRM 0x0400 /* term char */ +#define CCW16_Q 0x0200 /* queue */ +#define CCW16_HI 0x0100 /* queue hi */ +#define CCW16_OC 0x0080 /* OC */ +#define CCW16_CHN 0x0020 /* chain */ +#define CCW16_CON 0x0010 /* continue */ +#define CCW16_V_BPI 0 /* bytes per int */ +#define CCW16_M_BPI 0xF +#define CCW16_BPI(x) (((x) >> CCW16_V_BPI) & CCW16_M_BPI) + +/* Automatic I/O channel programs, 32b */ + +#define CCB32_CCW 0 /* cmd wd */ +#define CCB32_B0C 2 /* buf 0 cnt */ +#define CCB32_B0E 4 /* buf 0 end */ +#define CCB32_CHK 8 /* check word */ +#define CCB32_B1C 10 /* buf 1 cnt */ +#define CCB32_B1E 12 /* buf 1 end */ +#define CCB32_TAB 16 /* trans table */ +#define CCB32_SUB 20 /* subroutine */ + +#define CCW32_V_STA 8 /* status */ +#define CCW32_M_STA 0xFF +#define CCW32_STA(x) (((x) >> CCW32_V_STA) & CCW32_M_STA) +#define CCW32_EXE 0x80 /* execute */ +#define CCW32_CRC 0x10 +#define CCW32_B1 0x08 /* buffer 1 */ +#define CCW32_WR 0x04 /* write */ +#define CCW32_TL 0x02 /* translate */ +#define CCW32_FST 0x01 /* fast mode */ + +/* MAC, 32b */ + +#define P 0 /* physical */ +#define VE 1 /* virtual inst */ +#define VR 2 /* virtual read */ +#define VW 3 /* virtual write */ + +#define MAC_BASE 0x300 /* MAC base */ +#define MAC_STA 0x340 /* MAC status */ +#define MAC_LNT 16 +#define VA_V_OFF 0 /* offset */ +#define VA_M_OFF 0xFFFF +#define VA_GETOFF(x) (((x) >> VA_V_OFF) & VA_M_OFF) +#define VA_V_SEG 16 /* segment */ +#define VA_M_SEG 0xF +#define VA_GETSEG(x) (((x) >> VA_V_SEG) & VA_M_SEG) + +#define SRF_MASK 0x000FFF00 /* base mask */ +#define SRL_MASK 0x0FF00000 /* limit mask */ +#define GET_SRL(x) ((((x) & SRL_MASK) >> 12) + 0x100) +#define SR_EXP 0x80 /* execute prot */ +#define SR_WPI 0x40 /* wr prot int */ +#define SR_WRP 0x20 /* wr prot */ +#define SR_PRS 0x10 /* present */ +#define SR_MASK (SRF_MASK|SRL_MASK|SR_EXP|SR_WPI|SR_WRP|SR_PRS) + +#define MACS_L 0x10 /* limit viol */ +#define MACS_NP 0x08 /* not present */ +#define MACS_WP 0x04 /* write prot */ +#define MACS_WI 0x02 /* write int */ +#define MACS_EX 0x01 /* exec prot */ + +/* Miscellaneous */ + +#define TMR_LFC 0 /* LFC = timer 0 */ +#define TMR_PIC 1 /* PIC = timer 1 */ +#define LPT_WIDTH 132 +#define VFU_LNT 132 +#define MIN(x,y) (((x) < (y))? (x): (y)) +#define MAX(x,y) (((x) > (y))? (x): (y)) + +/* Function prototypes */ + +int32 int_chg (uint32 irq, int32 dat, int32 armdis); +int32 io_2b (int32 val, int32 pos, int32 old); +uint32 IOReadB (uint32 loc); +void IOWriteB (uint32 loc, uint32 val); +uint32 IOReadH (uint32 loc); +void IOWriteH (uint32 loc, uint32 val); +uint32 ReadF (uint32 loc, uint32 rel); +void WriteF (uint32 loc, uint32 val, uint32 rel); +uint32 IOReadBlk (uint32 loc, uint32 cnt, uint8 *buf); +uint32 IOWriteBlk (uint32 loc, uint32 cnt, uint8 *buf); +void sch_adr (uint32 ch, uint32 dev); +t_bool sch_actv (uint32 sch, uint32 devno); +void sch_stop (uint32 sch); +uint32 sch_wrmem (uint32 sch, uint8 *buf, uint32 cnt); +uint32 sch_rdmem (uint32 sch, uint8 *buf, uint32 cnt); +t_stat set_sch (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_sch (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc); + +#endif diff --git a/Interdata/id_diag.txt b/Interdata/id_diag.txt new file mode 100644 index 0000000..c0daf2f --- /dev/null +++ b/Interdata/id_diag.txt @@ -0,0 +1,911 @@ +Interdata Diagnostics + +Summary + +816E CPU diagnostic, part 1 passed 16b n/a +816E CPU diagnostic, part 2 partial 16b n/a +Series 16 CPU diagnostic, part 1 passed 16b n/a +16b memory diagnostic, part 1 passed 16b n/a +16b memory diagnostic, part 2 passed 16b n/a +816e extended memory diagnostic passed 16b n/a +Series 16 selector channel diagnostic passed 16b n/a +32b CPU diagnostic, part 1 n/a passed 32b +32b CPU diagnostic, part 2 n/a passed 32b +32b CPU diagnostic, part 3 n/a passed 32b +32b memory diagnostic, part 1 n/a passed 32b +32b memory diagnostic, part 2 n/a passed 32b +32b memory diagnostic, part 3 n/a passed 32b +32b memory diagnostic 6a, part 1 n/a passed 32b +32b memory diagnostic 6a, part 2 n/a passed 32b +32b MAC diagnostic, part 1 n/a passed 32b +32b MAC diagnostic, part 2 n/a passed 32b +Common line printer diagnostic passed 16b passed 32b +Common magtape diagnostic passed 16b passed 32b +Common 2.5/10MB disk diagnostic passed 16b passed 32b +32b MSM disk diagnostic passed 32b +Common floppy disk diagnostic passed 16b passed 32b +Common clock diagnostic passed 16b passed 32b + +Not tested: +- 16b floating point +- 32b double precision floating point +- IDC +- PASLA + +------------------------------------------------------------------- + +Operating Instructions + +816E CPU diagnostic, part 1 + +sim> set cpu 816e +sim> att -e pt0 diag.bin +sim> br c2 +sim> boot pt0 + +Breakpoint: PC: 00C2 (EXBR R8,R6) + +sim> run 100 + +MODEL 8/16E PROCESSOR TEST PART 1 06-211R00 +CPU +* 8D + +ENTER 0 OR 1 +1 +NO ERROR + +CPU +* + +--- +816E CPU diagnostic, part 2 + +sim> set cpu 816e +sim> d tt ttime 1000 ; timing dependency +sim> att -e pt0 diag.bin +sim> br c2 +sim> boot pt0 + +Breakpoint: PC: 00C2 (EXBR R8,R6) + +sim> run 2d0 + +MODEL 8/16E PROCESSOR TEST PART 2 06-212R00 +CPU +* 8D + +SUBTEST + +* (type subtest number) + +Subtests 0, 1, 2, 5, 7, 8, 9 run correctly +Subtest 3, 4 cannot be run (initialization, power fail) +Subtest 6 cannot be run (hexadecimal display) + +--- +Series 16 CPU diagnostic, part 1 +(Central error routine is at 21F4) + +sim> set cpu 816e +sim> att -e pt0 diag.bin ; diagnostic +sim> br c0 +sim> boot pt0 + +Breakpoint, PC: 00C0 (8800) + +sim> d 234a 0202 ; patch to use +sim> d 234c a4a8 ; TTY as console +sim> d 17a b 1e4 +sim> run 100 + +SERIES SIXTEEN PROCESSOR TEST PART 1 06-242F01R00 +CPU +* 2D +ENTER 0 OR 1 +1 + +1234567890 + +NO ERROR + +000A 0000 + +CPU +* + +--- +16b memory diagnostic, part 1 + +sim> att -e pt0 diag.bin ; diagnostic +sim> br c2 +sim> boot pt0 + +Breakpoint, PC: 00C2 (EPSR R7,R6) + +sim> run 100 + +02-340 PART 1 06-162F01R01 + +NO ERRORS + +--- +16b memory diagnostic, part 2 + +sim> att -e pt0 diag.bin ; diagnostic +sim> br c2 +sim> boot pt0 + +Breakpoint, PC: 00C2 (EPSR R7,R6) + +sim> run 1000 + +02-340 PART 2 06-162F02R01 + +NO ERRORS + +--- +816e extended memory diagnostic, parts 1 and 2 + +sim> set cpu 816e +sim> set cpu 256k +sim> att -e pt0 diag.bin ; diagnostic +sim> br b4 +sim> boot pt0 + +Breakpoint, PC: 00B4 (LPSW R0,B8) + +sim> run 1000 + +8/16 E EXTENDED MEMORY TEST PART 1 06-221R00 +NO ERROR +* (CR to repeat part 1) +8/16 E EXTENDED MEMORY TEST PART 1 06-221R00 +NO ERROR +* (LF to go on to part 2) + +Breakpoint, PC: 00B4 (LPSW R0,B8) +sim> run 100 + +8/16 E EXTENDED MEMORY TEST PART 2 06-221R00 +PROGRAM DETECTED MAXIMUM MEMORY 3FFFE +*TEST ; standard tests +*RUN + +SUBTEST 0 NO ERROR +SUBTEST 1 NO ERROR +SUBTEST 2 NO ERROR +SUBTEST 3 NO ERROR +SUBTEST 4 NO ERROR +SUBTEST 6 NO ERROR +SUBTEST 7 NO ERROR +SUBTEST 8 NO ERROR +END OF TEST + +* + +--- +Series 16 selector channel diagnostic + +sim> set cpu 816e +sim> set cpu 256k +sim> att -e pt0 diag.bin ; diagnostic +sim> att mt0 foo.tap ; magtape to test +sim> br c0 +sim> boot pt0 + +Breakpoint, PC: 00C0 (LPSW R0,C8) + +sim> d 2e68 2 ; console is TTY +sim> run A00 + +S16 SELCH TEST 06-222 R01 + +TOP OF MEMORY 3 FFFF +* IODEV1 C5 ; magtape +* DEV1 2 +* RUN ; bank 0 by default +TEST 00 + +NO ERROR + +TEST 01 + +NO ERROR + +TEST 02 + +NO ERROR + +TEST 03 + +NO ERROR + +TEST 04 + +NO ERROR + + +* MEMMOD 1 {2,3} ; repeat for banks 1,2,3 +* RUN +TEST 00 + +NO ERROR + +TEST 01 + +NO ERROR + +TEST 02 + +NO ERROR + +TEST 03 + +NO ERROR + +TEST 04 + +NO ERROR + +* +--- +32b CPU diagnostic, part 1 + +sim> att -e mt0 mmd_r07.tap +sim> d -b 7f 7 ; file 8 on MMD R07 tape +sim> boot mt0 + +S32PT1 06-154 R03 +CPU + +*7X +NO ERROR +000A 0000 +* + +--- +32b CPU diagnostic, part 2 + +sim> set tt 7b ; test is parity sensitive +sim> att -e mt0 mmd_r07.tap +sim> d -b 7f 8 ; file 9 on MMD R07 tape +sim> boot mt0 + + +S32PT2R02 +CPU +* +7X +SUBTEST +* (type subtest number) + +Subtests 1, 3, 4, 5, 9 run correctly +Subtest 2 cannot be run (7/32 with halfword mode only) +Subtest 6 cannot be run (hexadecimal display) +Subtests 7,8 cannot be run (initialization, power fail) + +--- +32b CPU diagnostic, part 3 + +sim> att -e mt0 mmd_r07.tap +sim> d -b 7f 9 ; file 10 on MMD R07 tape +sim> boot mt0 + +S32PT3 R01 +CPU +* +8X ; 7X denotes 7/32 with halfword mode +MAC RESPONSE AT 000300 + +SUBTEST +* + +Subtests 1, 2, 3 run correctly +Subtest 4 cannot be run (parity option) + +--- +32b memory diagnostic, part 1 + +sim> att -e mt0 mmd_r07.tap +sim> d -b 7f 17 ; file 24 on MMD R07 tape +sim> br 2000 +sim> boot mt0 + +Breakpoint, PC: 02000 (B 2060) +sim> d -w 2010 0202 ; console is TTY +sim> c + +S32MT1 06-156F01R04 +MAC PRESENT ? (Y OR N) +* +Y +01 +02 +03 +04 +05 +06 +NO ERROR +* + +--- +32b memory diagnostic, part 2 + +sim> att -e mt0 c:\temp\mmd_r07.tap +sim> d -b 7f 18 ; file 25 on MMD R07 tape +sim> br a00 +sim> boot mt0 + +Breakpoint, PC: 00A00 (B A60) +sim> d -w a10 0202 ; console is TTY +sim> c + +S32MT2 06-156F02R04 +AVAILABLE MEMORY +000000 - 0FFFFF +SUBTEST * +0 ; all standard tests +01 +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +02 +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +03 +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +04 +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +05 +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +06 +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +07 +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +SUBTEST * + +--- +32b memory diagnostic, part 3 + +sim> att -e mt0 c:\temp\mmd_r07.tap +sim> d -b 7f 19 ; file 26 on MMD R07 tape +sim> br a00 +sim> boot mt0 + +Breakpoint, PC: 00A00 (B A60) +sim> d -w a10 0202 ; console is TTY + +sim> c +S32MT3 06-156F03R04 +AVAILABLE MEMORY +000000 - 0FFFFF +* + +TEST STILL RUNNING ; repeated multiple times +: +NO ERROR +* + +--- +32b memory diagnostic, 6a, part 1 + +sim> att -e mt0 c:\temp\mmd_r07.tap +sim> d -b 7f 15 ; file 22 on MMD R07 tape +sim> boot mt0 + +32 BIT S6A MEMORY TEST 06-157F01R01 +AVAILABLE MEMORY +0000-3FFF + +MAC ADDRESS = 300 +TYPE= 3 ; any value, 0-4 +SUBTEST +* 0 +01 +NO ERROR +02 +NO ERROR +03 +NO ERROR +04 +NO ERROR +05 +NO ERROR +06 +NO ERROR +07 +NO ERROR +08 +NO ERROR + +SUBTEST +* + +--- +32b memory diagnostic, 6a, part 2 + +sim> att -e mt0 c:\temp\mmd_r07.tap +sim> d -b 7f 16 ; file 23 on MMD R07 tape +sim> boot mt0 + +32 BIT S6A MEMORY TEST 06-157F02R01 +AVAILABLE MEMORY +0000f-FFFFF + +TYPE= 2 ; any value, 0-4 +SUBTEST +* 0 +01 +NO ERROR +02 +NO ERROR +03 +NO ERROR +04 +NO ERROR +05 +NO ERROR +06 +NO ERROR +07 +NO ERROR +08 +NO ERROR + +SUBTEST +* + +--- +32b MAC diagnostic, part 1 + +sim> att -e mt0 c:\temp\mmd_r07.tap +sim> d -b 7f 24 ; file 37 on MMD R07 tape +sim> boot mt0 + +MACT 06-160F01R03 +AVAILABLE MEMORY +00000- FFFFF + +* RUN +TEST 00 NO ERROR +TEST 01 NO ERROR +TEST 02 NO ERROR +TEST 03 NO ERROR +TEST 04 NO ERROR +TEST 05 NO ERROR +TEST 06 NO ERROR +TEST 07 NO ERROR +TEST 08 NO ERROR +TEST 09 NO ERROR +TEST 0B NO ERROR +* + +--- +sim> att -e mt0 c:\temp\mmd_r07.tap +sim> d -b 7f 25 ; file 38 on MMD R07 tape +sim> br ffd0 ; start != load point +sim> boot mt0 + +Breakpoint, PC: 0FFD0 (B 1093E) + +sim> run 10010 + +MACT 06-160F02R03 + +* RUN +TEST 00 NO ERROR +TEST 01 NO ERROR +TEST 02 NO ERROR +TEST 03 NO ERROR +TEST 04 NO ERROR +TEST 05 NO ERROR +TEST 06 NO ERROR +TEST 07 NO ERROR +TEST 08 NO ERROR +* + +--- +Common line printer diagnostic + +sim> att -e pt0 diag.bin +sim> br c2 +sim> boot pt0 + +Breakpoint: PC: 00C2 (EXBR R8,R6) + +sim> run a00 ; 32b +sim> run a04 ; 16b + +COMMON LINE PRINTER TEST 06-170R02 + +*TEST 0,1,2,3 +*RUN + +TEST 00 +NO ERROR +TEST 01 +NO ERROR +TEST 02 +NO ERROR +TEST 03 +NO ERROR +END OF TEST + +*INTRPT 1 +*RUN + +TEST 00 +NO ERROR +TEST 01 +NO ERROR +TEST 02 +NO ERROR +TEST 03 +NO ERROR +END OF TEST + +* + +--- +Common magtape diagnostic + +sim> att -e pt0 diag.bin +sim> att mt foo.tap +sim> br c4 +sim> boot pt0 + +Breakpoint, PC: 00C4 (EXBR R8,R6) + +sim> run a00 ; 32b +sim> run a04 ; 16b + +COMMON MAGNETIC TAPE TEST PROGRAM 06-172R02 + +*TEST 0,1,2,3,4,5 +*MODE 0 ; prog i/o and selch +*RUN + +TEST 00 +NO ERROR +TEST 01 +NO ERROR +TEST 02 +NO ERROR +TEST 03 +NO ERROR +TEST 04 +NO ERROR +TEST 05 +NO ERROR +END OF TEST + +* + +--- +Common 2.5/10MB disk diagnostic + +sim> att -e pt0 diag.bin +sim> br c2 +sim> boot pt0 + +Breakpoint, PC: 00C2 (EXBR R8,R6) + +sim> set dp0 5440 +sim> set dp1 5440 +sim> att dp0 test0.dsk +sim> att dp1 test1.dsk +sim> run a00 ; 32b +sim> run a04 ; 16b + +COMMON DISC TEST 06-173R01F01 + +*FILE 2 ; FILE 1 to test fixed platter +*LOCYL 0 +*HICYL 197 +*TIMCON 1C0 +*TEST 0,1,2,3,4,6,7,8,9,A,C ; test 5 requires format capability + ; test B requires manual intervention +*RUN + +TEST 00 +NO ERROR +TEST 01 +NO ERROR +TEST 02 +NO ERROR +TEST 03 +NO ERROR +TEST 04 +NO ERROR +TEST 06 +NO ERROR +TEST 07 +NO ERROR +TEST 08 +NO ERROR +TEST 09 +NO ERROR +TEST 0A +NO ERROR +TEST 0C +NO ERROR +END OF TEST + +* + +--- +32b MSM disk diagnostic + +sim> att -e mt0 c:\temp\mmd_r07.tap +sim> d -b 7f 45 ; file 70 on MMD R07 tape +sim> br a00 +sim> boot mt0 + +Breakpoint, PC: 00A00 (B A5E) + +sim> d -h a10 0202 ; patch for TTY console +sim> att dm0 foo.dsk +sim> att dm1 foo1.dsk +sim> c + +MSM DISC TEST 06-200F02R04 (32-BIT) + +*LOCYL 0 +*HICYL 336 ; tests 8,9,A will run a very long + ; time, use 40 to shorten test +*DRIVE 0 +*PACTYP 0 +*TIMVAL 14D +*XFILE 1 +*TEST 0,1,2,3,4,6,7,8,9,A,C ; test 5 requires format capability + ; test B requires manual intervention +*RUN + +TEST 00 +TEST 01 +TEST 02 +TEST 03 +TEST 04 +TEST 06 +TEST 07 +TEST 08 +TEST 09 +TEST 0A +TEST 0C + +--- +Common floppy disk diagnostic + +sim> att -e pt0 diag.bin +sim> att fd0 foo0.flp +sim> att fd1 foo1.flp +sim> br b8 +sim> boot pt0 + +Breakpoint, PC: 000B8 (BS B2) + +sim> d 2a72 bal r15,320a ; patch for multidrive test +sim> run a00 ; 32b +sim> run a04 ; 16b + +COMMON FLOPPY DISC TEST 06-198R00 +UNPROTECT DISKETTE + +*DRIVE AB +*RUN + +DRIVE A UNDER TEST +TEST 00 +NO ERROR +TEST 01 +NO ERROR +TEST 02 +NO ERROR +TEST 03 +NO ERROR +TEST 04 +NO ERROR +TEST 05 +NO ERROR +TEST 06 +NO ERROR +TEST 07 +NO ERROR +DRIVE B UNDER TEST +TEST 00 +NO ERROR +TEST 01 +NO ERROR +TEST 02 +NO ERROR +TEST 03 +NO ERROR +TEST 04 +NO ERROR +TEST 05 +NO ERROR +TEST 06 +NO ERROR +TEST 07 +NO ERROR +END OF TEST + +*TEST 9 ; test 8 requires formatting +*RUN + +TEST 09 +NO ERROR +END OF TEST + +* + +--- +Common clock diagnostic + +sim> att -e pt0 diag.bin +sim> br c4 +sim> boot pt0 + +Breakpoint, PC: 00C4 (EXBR R8,R6) + +sim> d -w e28 4300 ; R09 patches +sim> d -w e2a 10f4 +sim> id -w 10f4:110a +10f4: 4840 +10f6: 188a +10f8: 4850 +10fa: 188c +10fc: de40 +10fe: 1eaf +1100: de50 +1102: 1eaf +1104: 4810 +1106: 0a24 +1108: 4300 +1110: 0e2c +sim> d 1b9c bs 1ba6 +sim> d -w 1102 1eaf + +sim> run a00 ; 32b +sim> run a04 ; 16b + +COMMON UNIVERSAL CLOCK MODULE TEST 06-133R05 + +*TIMVAL 1A4 ; simulator is a fast CPU +*RUN + +TEST 00 +NO ERROR +TEST 01 +NO ERROR +TEST 02 +NO ERROR +TEST 03 +NO ERROR +TEST 04 +NO ERROR +TEST 05 +NO ERROR +TEST 06 +NO ERROR +TEST 07 +NO ERROR +END OF TEST + +* + +------------------------------------------------------------------- +Bugs Found and Fixed During Simulator Debug + +1. CPU16: instruction decoding interpreting CPU models incorrectly +2. CPU16: SINT should not be conditional on device existing +3. CPU16: immediate interrupts do not do a PSW swap, new PC is block+6 +4. CPU16: SLA, SLHA setting C incorrectly +5. CPU16: diagnostic requires 816E extended memory to run +6. CPU16: CCW16_OC defined incorrectly +7. CPU16, CPU32: autoload not fetching or outputing OC +8. CPU16, CPU32: block I/O completion is off by 1 +9. CPU16, CPU32: ESPR broken, EPSR rx,rx should copy PSW to rx +10. CPU16, CPU32: PCQ displays in octal instead of hexadecimal +11. CPU16, CPU32: SH and variations overflow calculation wrong +12. CPU16, CPU32: SCH overflow calculation wrong +13. CPU16, CPU32: CH and CLH overflow calculation wrong +14. CPU16, CPU32: CH or'ing into CC's instead of loading +15. CPU16, CPU32: RD, RH, SS, AI store some data on non-existent device +16. CPU16, CPU32: console interrupt not implemented +17. CPU16, CPU32: SRHL(s) setting C incorrectly +18. CPU16, CPU32: WDR, OCR not masking register data to 8b +19. CPU32: WH not masking data to 8b or 16b as required +20. CPU32: 32b register sets ordered incorrectly in memory +21. CPU32: wrong slot length in queue instructions +22. CPU32: display device missing its interrupt declaration +23. CPU32: LPSW(R) must load PC before changing PSW +24. CPU32: SLL setting C incorrectly +25. CPU32: bit instructions use halfword memory access and offsets +26. CPU32: CRC sign-extending rather than zero-extending operands +27. CPU32: SCP incrementing counts before, not after, transfer +28. CPU32: CHVR not implemented +29. CPU32: M(R) algorithm wrong +30. CPU32: M(R) using wrong register as first operand +31. CPU32: memory accesses were fullword rather than halfword aligned +32. CPU32: D(R) overflow calculation incorrect +33. CPU32: on 7/32, exceptions use register set 0, regardless of new PSW +34. CPU32: system queue PSW location misdefined +35. CPU32: autodriver channel not shifting bytes left before use as + translation table index +36. CPU32: MAC, LRA using wrong value for limit test +37. CPU32: LRA using wrong value for segment base +38. CPU32: MAC registers are accessible only if protection is off +39. CPU32: MAC status clears only on write, not read +40. CPU32: MAC write protect abort and interrupts implemented incorrectly +41. CPU32: ex/dep -v test used & instead of && +42. CPU32: fetch tests for MAC abort at end of fetch, not per halfword +43. FP: unpack and pack detecting RR format incorrectly +44. FP: need separate microcode/hardware algorithms for add/sub denormalization +45. FP: multiply and divide have 'early out' detection of overflow/underflow +46. FP: compare less than not setting C +47. FP: fix overflow not setting V +48. FP: fix shift needed to be hex digits not binary digits +49. IO: interrupt evaluation routine never sets an interrupt +50. SELCH: transfer count calculation off by 1 +51. SELCH: device data structure set up incorrectly (reset routine) +52. SELCH: stop clears pending interrupts +53. SELCH: register load algorithm incorrect for 6 byte loads +54. PT, LPT, FD: OR'ing status mask instead of AND'ing +55. PT, TT: SET_INT on status change not conditioned on interrupt armed +56. TT: input char converted to UC incorrectly +57. TT: need SET TT BREAK to run CPU test part 2 +58. LPT: not clearing spacing done +59. MT: WREOF not setting EOF status +60. MT: CMD register pointer to wrong place +61. MT: write record byte count taken from wrong variable +62. MT: overrun processing incorrect for selector channel mode +63. PIC, LFC: write data and overflow detection incorrect +64. PIC, LFC: interpolation algorithm for cic read incorrect +65. PIC, LFC: ric reloaded from output buffer on count overflow +66. PIC, LFC: added diagnostic mode, revised use of count vs timer +67. DP: track increment algorithm incorrect +68. DP, IDC: incorrectly setting overrun for less than full sector reads +69. DP: should interrupt on detach (offline) +70. FD: high water mark not updated on write +71. FD: deleted data not implemented, required for diagnostic +72. FD: header CRC not implemented, required for diagnostic +73. FD: function code not stored for service routine +74. FD: LRN to track and sector conversions incorrect +75. FD: reset status incorrect (should be not busy, LRN = 1) +76. FD: extended status track 0 calculation wrong +77. FD: reset does not clear interrupts, requires delay +78. FD: read/write sequencing incorrect +79. FD: command without write data uses implicit LRN +80. FD: extended status is per drive not per controller +81. FD: command start clears only extended status bytes 0,1 +82. FD: IDLE sets after BUSY drops and generates a separate interrupt +83. SYS16, SYS32: WH mistyped as WD in symbol table +84. SYS32: MHR, DHR misdefined +85. PAS: busy set instead of cleared initially +86. IDC: busy set instead of cleared initially +87. IDC, DP: busy not cleared at transfer command complete +88. IDC: busy is not cleared at drive command complete +89. IDC: for MSM compatibility, must absorb WH of head/cylinder +90. IDC: drive command 0x30 is an instant NOP +91. IDC: set cylinder with invalid cylinder sets SKI +92. IDC: read with invalid head sets ACF, not DTE +93. DP, IDC: write with cylinder overflow advanced selch pointer +94. MT: read error must stop selector channel (if active) +95. IDC: xx000000 to controller or drive are NOP's, not invalid commands +96. IDC: WD/WH use standard Interdata write pointers +97. SELCH: GO preserves EXA and SSTA +98. CPU: DH overflow checking broken + diff --git a/Interdata/id_dp.c b/Interdata/id_dp.c new file mode 100644 index 0000000..8a216ee --- /dev/null +++ b/Interdata/id_dp.c @@ -0,0 +1,600 @@ +/* id_dp.c: Interdata 2.5MB/10MB cartridge disk simulator + + Copyright (c) 2001-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dp M46-421 2.5MB/10MB cartridge disk + + 18-Mar-05 RMS Added attached test to detach routine + 25-Jan-04 RMS Revised for device debug support + 25-Apr-03 RMS Revised for extended file support + 16-Feb-03 RMS Fixed read to test transfer ok before selch operation +*/ + +#include "id_defs.h" +#include + +#define DP_NUMBY 256 /* bytes/sector */ +#define DP_NUMSC 24 /* sectors/track */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 0x1 +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) + +#define CYL u3 /* current cylinder */ +#define STD u4 /* drive status */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* Controller status */ + +#define STC_OVR 0x80 /* overrun */ +#define STC_ACF 0x40 /* addr cmp fail */ +#define STC_DEF 0x20 /* def track NI */ +#define STC_CYO 0x10 /* cylinder ovflo */ +#define STC_IDL 0x02 /* ctrl idle */ +#define STC_DTE 0x01 /* xfer error */ +#define SETC_EX (STC_OVR|STC_ACF|STC_DEF|STC_CYO) +#define STC_MASK (STC_OVR|STC_ACF|STC_DEF|STC_CYO|STA_BSY|STC_IDL|STC_DTE) + +/* Controller command */ + +#define CMC_MASK 0xF +#define CMC_CLR 0x8 /* reset */ +#define CMC_RD 0x1 /* read */ +#define CMC_WR 0x2 /* write */ +#define CMC_RCHK 0x3 /* read check */ +#define CMC_RFMT 0x5 /* read fmt NI */ +#define CMC_WFMT 0x6 /* write fmt NI */ + +/* Drive status, ^ = dynamic, * = in unit status */ + +#define STD_WRP 0x80 /* ^write prot */ +#define STD_WCK 0x40 /* write check NI */ +#define STD_ILA 0x20 /* *illegal addr */ +#define STD_ILK 0x10 /* ^addr interlock */ +#define STD_MOV 0x08 /* *heads in motion */ +#define STD_INC 0x02 /* seek incomplete NI */ +#define STD_NRDY 0x01 /* ^not ready */ +#define STD_UST (STD_ILA | STD_MOV) /* set from unit */ +#define SETD_EX (STD_WCK | STD_ILA | STD_ILK) /* set examine */ + +/* Drive command */ + +#define CMD_SK 0x02 /* seek */ +#define CMD_RST 0x01 /* restore */ + +/* Head/sector register */ + +#define HS_SMASK 0x1F /* sector mask */ +#define HS_V_SRF 5 /* surface */ +#define HS_HMASK 0x20 /* head mask */ +#define HS_MASK (HS_HMASK | HS_SMASK) +#define GET_SEC(x) ((x) & HS_SMASK) +#define GET_SRF(x) (((x) & HS_HMASK) >> HS_V_SRF) + +#define GET_SA(p,cy,sf,sc,t) (((((((p)*drv_tab[t].cyl)+(cy))*drv_tab[t].surf)+(sf))* \ + DP_NUMSC)+(sc)) +#define GET_ROTATE(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DP_NUMSC))) + +/* This controller supports two different disk drive types: + + type #sectors/ #surfaces/ #cylinders/ + surface cylinder drive + + 2315 24 2 203 + 5440 24 4 408 + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE AND MUST HAVE + THE SAME SECTORS/TRACK. +*/ + +#define TYPE_2315 0 +#define CYL_2315 203 +#define SURF_2315 2 +#define SIZE_2315 (DP_NUMSC * SURF_2315 * CYL_2315 * DP_NUMBY) + +#define TYPE_5440 1 +#define CYL_5440 408 +#define SURF_5440 2 +#define SIZE_5440 (2 * DP_NUMSC * SURF_5440 * CYL_5440 * DP_NUMBY) + +struct drvtyp { + int32 cyl; /* cylinders */ + uint32 surf; /* surfaces */ + uint32 size; /* #blocks */ + }; + +static struct drvtyp drv_tab[] = { + { CYL_2315, SURF_2315, SIZE_2315 }, + { CYL_5440, SURF_5440, SIZE_5440 }, + { 0 } + }; + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; +extern FILE *sim_deb; + +uint8 dpxb[DP_NUMBY]; /* xfer buffer */ +uint32 dp_bptr = 0; /* buffer ptr */ +uint32 dp_db = 0; /* ctrl buffer */ +uint32 dp_cyl = 0; /* drive buffer */ +uint32 dp_sta = 0; /* ctrl status */ +uint32 dp_cmd = 0; /* ctrl command */ +uint32 dp_plat = 0; /* platter */ +uint32 dp_hdsc = 0; /* head/sector */ +uint32 dp_svun = 0; /* most recent unit */ +uint32 dp_1st = 0; /* first byte */ +uint32 dpd_arm[DP_NUMDR] = { 0 }; /* drives armed */ +int32 dp_stime = 100; /* seek latency */ +int32 dp_rtime = 100; /* rotate latency */ +int32 dp_wtime = 1; /* word time */ +uint8 dp_tplte[(2 * DP_NUMDR) + 2]; /* fix/rmv + ctrl + end */ + +DEVICE dp_dev; +uint32 dp (uint32 dev, uint32 op, uint32 dat); +void dp_ini (t_bool dtpl); +t_stat dp_svc (UNIT *uptr); +t_stat dp_reset (DEVICE *dptr); +t_stat dp_attach (UNIT *uptr, char *cptr); +t_stat dp_detach (UNIT *uptr); +t_stat dp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dp_rds (UNIT *uptr); +t_stat dp_wds (UNIT *uptr); +t_bool dp_dter (UNIT *uptr, uint32 first); +void dp_done (uint32 flg); + +extern t_stat id_dboot (int32 u, DEVICE *dptr); + +/* DP data structures + + dp_dev DP device descriptor + dp_unit DP unit list + dp_reg DP register list + dp_mod DP modifier list +*/ + +DIB dp_dib = { d_DPC, 0, v_DPC, dp_tplte, &dp, &dp_ini }; + +UNIT dp_unit[] = { + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) }, + { UDATA (&dp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_5440 << UNIT_V_DTYPE), SIZE_5440) } + }; + +REG dp_reg[] = { + { HRDATA (CMD, dp_cmd, 3) }, + { HRDATA (STA, dp_sta, 8) }, + { HRDATA (BUF, dp_db, 8) }, + { HRDATA (PLAT, dp_plat, 1) }, + { HRDATA (HDSC, dp_hdsc, 6) }, + { HRDATA (CYL, dp_cyl, 9) }, + { HRDATA (SVUN, dp_svun, 8), REG_HIDDEN }, + { BRDATA (DBUF, dpxb, 16, 8, DP_NUMBY) }, + { HRDATA (DBPTR, dp_bptr, 9), REG_RO }, + { FLDATA (FIRST, dp_1st, 0) }, + { GRDATA (IREQ, int_req[l_DPC], 16, DP_NUMDR + 1, i_DPC) }, + { GRDATA (IENB, int_enb[l_DPC], 16, DP_NUMDR + 1, i_DPC) }, + { BRDATA (IARM, dpd_arm, 16, 1, DP_NUMDR) }, + { DRDATA (RTIME, dp_rtime, 0), PV_LEFT | REG_NZ }, + { DRDATA (STIME, dp_stime, 0), PV_LEFT | REG_NZ }, + { DRDATA (WTIME, dp_wtime, 0), PV_LEFT | REG_NZ }, + { URDATA (UCYL, dp_unit[0].CYL, 16, 9, 0, + DP_NUMDR, REG_RO) }, + { URDATA (UST, dp_unit[0].STD, 16, 8, 0, + DP_NUMDR, REG_RO) }, + { URDATA (CAPAC, dp_unit[0].capac, 10, T_ADDR_W, 0, + DP_NUMDR, PV_LEFT | REG_HRO) }, + { HRDATA (DEVNO, dp_dib.dno, 8), REG_HRO }, + { HRDATA (SELCH, dp_dib.sch, 2), REG_HRO }, + { NULL } + }; + +MTAB dp_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_2315 << UNIT_V_DTYPE) + UNIT_ATT, + "2315", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_5440 << UNIT_V_DTYPE) + UNIT_ATT, + "5440", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_2315 << UNIT_V_DTYPE), + "2315", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_5440 << UNIT_V_DTYPE), + "5440", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_2315 << UNIT_V_DTYPE), + NULL, "2315", &dp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_5440 << UNIT_V_DTYPE), + NULL, "5440", &dp_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH", + &set_sch, &show_sch, NULL }, + { 0 } + }; + +DEVICE dp_dev = { + "DP", dp_unit, dp_reg, dp_mod, + DP_NUMDR, 16, 24, 1, 16, 8, + NULL, NULL, &dp_reset, + &id_dboot, &dp_attach, &dp_detach, + &dp_dib, DEV_DISABLE | DEV_DEBUG + }; + +/* Controller: IO routine */ + +uint32 dpc (uint32 dev, uint32 op, uint32 dat) +{ +uint32 f, t, u; +UNIT *uptr; +static uint8 good_cmd[8] = { 0, 1, 1, 1, 0, 0, 0, 0 }; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + sch_adr (dp_dib.sch, dev); /* inform sel ch */ + return BY; /* byte only */ + + case IO_RD: /* read data */ + if (dp_sta & STC_IDL) /* if idle */ + return GET_ROTATE (dp_rtime); /* return sector */ + else dp_sta = dp_sta | STA_BSY; /* xfr? set busy */ + return dp_db; /* return data */ + + case IO_WD: /* write data */ + if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, + ">>DPC WD = %02X, STA = %02X\n", dat, dp_sta); + if (dp_sta & STC_IDL) dp_hdsc = dat & HS_MASK; /* idle? hdsc */ + else { /* data xfer */ + dp_sta = dp_sta | STA_BSY; /* set busy */ + dp_db = dat & 0xFF; /* store data */ + } + break; + + case IO_SS: /* status */ + t = dp_sta & STC_MASK; /* get status */ + if (t & SETC_EX) t = t | STA_EX; /* test for EX */ + return t; + + case IO_OC: /* command */ + if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, + ">>DPC OC = %02X, STA = %02X\n", dat, dp_sta); + f = dat & CMC_MASK; /* get cmd */ + if (f & CMC_CLR) { /* clear? */ + dp_reset (&dp_dev); /* reset world */ + break; + } + u = (dp_svun - dp_dib.dno - o_DP0) / o_DP0; /* get unit */ + uptr = dp_dev.units + u; /* ignore if busy */ + if (!(dp_sta & STC_IDL) || sim_is_active (uptr)) break; + dp_cmd = f; /* save cmd */ + if (dp_cmd == CMC_WR) dp_sta = 0; /* write: bsy=0 else */ + else dp_sta = STA_BSY; /* bsy=1,idl,err=0 */ + dp_1st = 1; /* xfr not started */ + dp_bptr = 0; /* buffer empty */ + if (dp_svun & o_DPF) dp_plat = 1; /* upper platter? */ + else dp_plat = 0; /* no, lower */ + if (good_cmd[f]) sim_activate (uptr, dp_rtime); /* legal? sched */ + break; + } + +return 0; +} + +/* Drives: IO routine */ + +uint32 dp (uint32 dev, uint32 op, uint32 dat) +{ +int32 diff; +uint32 t, u; +UNIT *uptr; + +if (dev == dp_dib.dno) return dpc (dev, op, dat); /* controller? */ +u = (dev - dp_dib.dno - o_DP0) / o_DP0; /* get unit num */ +uptr = dp_dev.units + u; /* get unit ptr */ +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + if (dp_sta & STC_IDL) dp_svun = dev; /* idle? save unit */ + return BY; /* byte only */ + + case IO_WD: /* write data */ + if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, + ">>DP%d WD = %02X, STA = %02X\n", u, dat, dp_sta); + if (GET_DTYPE (uptr->flags) == TYPE_2315) /* 2.5MB drive? */ + dp_cyl = dat & 0xFF; /* cyl is 8b */ + else dp_cyl = ((dp_cyl << 8) | dat) & DMASK16; /* insert byte */ + break; + + case IO_SS: /* status */ + if (uptr->flags & UNIT_ATT) t = /* onl? */ + ((uptr->flags & UNIT_WPRT)? STD_WRP: 0) | + ((dp_sta & STC_IDL)? 0: STD_ILK) | + (uptr->STD & STD_UST); + else t = STD_MOV | STD_NRDY; /* off = X'09' */ + if (t & SETD_EX) t = t | STA_EX; /* test for ex */ + return t; + + case IO_OC: /* command */ + if (DEBUG_PRS (dp_dev)) fprintf (sim_deb, + ">>DP%d OC = %02X, STA = %02X\n", u, dat, dp_sta); + dpd_arm[u] = int_chg (v_DPC + u + 1, dat, dpd_arm[u]); + if (dat & CMD_SK) t = dp_cyl; /* seek? get cyl */ + else if (dat & CMD_RST) t = 0; /* rest? cyl 0 */ + else break; /* no action */ + diff = t - uptr->CYL; + if (diff < 0) diff = -diff; /* ABS cyl diff */ + else if (diff == 0) diff = 1; /* must be nz */ + uptr->STD = STD_MOV; /* stat = moving */ + uptr->CYL = t; /* put on cyl */ + sim_activate (uptr, diff * dp_stime); /* schedule */ + break; + } + +return 0; +} + +/* Unit service + + If seek done, on cylinder; + if read check, signal completion; + else, do read or write +*/ + +t_stat dp_svc (UNIT *uptr) +{ +uint32 u = uptr - dp_dev.units; /* get unit number */ +int32 cyl = uptr->CYL; /* get cylinder */ +uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ +uint32 t; +t_stat r; + +if (uptr->STD & STD_MOV) { /* seek? */ + uptr->STD = 0; /* clr seek in prog */ + if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* offl? hangs */ + if (cyl >= drv_tab[dtype].cyl) { /* bad cylinder? */ + uptr->STD = STD_ILA; /* error */ + uptr->CYL = drv_tab[dtype].cyl - 1; /* put at edge */ + } + if (dpd_arm[u]) SET_INT (v_DPC + u + 1); /* req intr */ + return SCPE_OK; + } + +switch (dp_cmd & 0x7) { /* case on func */ + + case CMC_RCHK: /* read check */ + dp_dter (uptr, 1); /* check xfr err */ + break; + + case CMC_RD: /* read */ + if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* sch transfer? */ + if (dp_dter (uptr, dp_1st)) return SCPE_OK; /* check xfr err */ + if (r = dp_rds (uptr)) return r; /* read sec, err? */ + dp_1st = 0; + t = sch_wrmem (dp_dib.sch, dpxb, DP_NUMBY); /* write to memory */ + if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* more to do? */ + sim_activate (uptr, dp_rtime); /* reschedule */ + return SCPE_OK; + } + break; /* no, set done */ + } + dp_sta = dp_sta | STC_DTE; /* can't work */ + break; + + case CMC_WR: /* write */ + if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* sch transfer? */ + if (dp_dter (uptr, dp_1st)) return SCPE_OK; /* check xfr err */ + dp_bptr = sch_rdmem (dp_dib.sch, dpxb, DP_NUMBY); /* read from mem */ + dp_db = dpxb[dp_bptr - 1]; /* last byte */ + if (r = dp_wds (uptr)) return r; /* write sec, err? */ + dp_1st = 0; + if (sch_actv (dp_dib.sch, dp_dib.dno)) { /* more to do? */ + sim_activate (uptr, dp_rtime); /* reschedule */ + return SCPE_OK; + } + break; /* no, set done */ + } + dp_sta = dp_sta | STC_DTE; /* can't work */ + break; + } /* end case func */ + +dp_done (0); /* done */ +return SCPE_OK; +} + +/* Read data sector */ + +t_stat dp_rds (UNIT *uptr) +{ +uint32 i; + +i = fxread (dpxb, sizeof (uint8), DP_NUMBY, uptr->fileref); +for ( ; i < DP_NUMBY; i++) dpxb[i] = 0; /* fill with 0's */ +if (ferror (uptr->fileref)) { /* error? */ + perror ("DP I/O error"); + clearerr (uptr->fileref); + dp_done (STC_DTE); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Write data sector */ + +t_stat dp_wds (UNIT *uptr) +{ +for ( ; dp_bptr < DP_NUMBY; dp_bptr++) + dpxb[dp_bptr] = dp_db; /* fill with last */ +fxwrite (dpxb, sizeof (uint8), DP_NUMBY, uptr->fileref); +if (ferror (uptr->fileref)) { /* error? */ + perror ("DP I/O error"); + clearerr (uptr->fileref); + dp_done (STC_DTE); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Data transfer error test routine */ + +t_bool dp_dter (UNIT *uptr, uint32 first) +{ +uint32 hd, sc, sa; +uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ + +if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ + ((uptr->flags & UNIT_WPRT) && (dp_cmd == CMC_WR))) { + dp_done (STC_DTE); /* error, done */ + return TRUE; + } +hd = GET_SRF (dp_hdsc); /* get head */ +sc = GET_SEC (dp_hdsc); /* get sector */ +if (dp_cyl != (uint32) uptr->CYL) { /* wrong cylinder? */ + if (dp_cyl == 0) uptr->CYL = 0; + else { + dp_done (STC_ACF); /* error, done */ + return TRUE; + } + } +if (sc >= DP_NUMSC) { /* bad sector? */ + dp_done (STC_OVR); /* error, done */ + return TRUE; + } +if (!first && (sc == 0) && (hd == 0)) { /* cyl overflow? */ + dp_done (STC_CYO); /* error, done */ + return TRUE; + } +sa = GET_SA (dp_plat, uptr->CYL, hd, sc, dtype); /* curr disk addr */ +fseek (uptr->fileref, sa * DP_NUMBY, SEEK_SET); +if ((sc + 1) < DP_NUMSC) dp_hdsc = dp_hdsc + 1; /* end of track? */ +else dp_hdsc = (dp_hdsc ^ HS_HMASK) & HS_HMASK; /* sec 0, nxt srf */ +return FALSE; +} + +/* Data transfer done routine */ + +void dp_done (uint32 flg) +{ +dp_sta = (dp_sta | STC_IDL | flg) & ~STA_BSY; /* set flag, idle */ +SET_INT (v_DPC); /* unmaskable intr */ +if (flg) sch_stop (dp_dib.sch); /* if err, stop ch */ +return; +} + +/* Reset routine */ + +t_stat dp_reset (DEVICE *dptr) +{ +uint32 u; +UNIT *uptr; + +dp_cmd = 0; /* clear cmd */ +dp_sta = STA_BSY | STC_IDL; /* idle, busy */ +dp_1st = 0; /* clear flag */ +dp_svun = dp_db = 0; /* clear unit, buf */ +dp_plat = 0; +dp_hdsc = 0; /* clear addr */ +CLR_INT (v_DPC); /* clear ctrl int */ +SET_ENB (v_DPC); /* always enabled */ +for (u = 0; u < DP_NUMDR; u++) { /* loop thru units */ + uptr = dp_dev.units + u; + uptr->CYL = uptr->STD = 0; + CLR_INT (v_DPC + u + 1); /* clear intr */ + CLR_ENB (v_DPC + u + 1); /* clear enable */ + dpd_arm[u] = 0; /* clear arm */ + sim_cancel (uptr); /* cancel activity */ + } +return SCPE_OK; +} + +/* Attach routine (with optional autosizing) */ + +t_stat dp_attach (UNIT *uptr, char *cptr) +{ +uint32 i, p; +t_stat r; + +uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +uptr->CYL = 0; +if ((uptr->flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */ +if ((p = ftell (uptr->fileref)) == 0) return SCPE_OK; +for (i = 0; drv_tab[i].surf != 0; i++) { + if (p <= drv_tab[i].size) { + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr->capac = drv_tab[i].size; + return SCPE_OK; + } + } +return SCPE_OK; +} + +/* Detach routine (generates an interrupt) */ + +t_stat dp_detach (UNIT *uptr) +{ +uint32 u = uptr - dp_dev.units; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if (dpd_arm[u]) SET_INT (v_DPC + u + 1); /* if arm, intr */ +return detach_unit (uptr); +} + +/* Set size command validation routine */ + +t_stat dp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = drv_tab[GET_DTYPE (val)].size; +return SCPE_OK; +} + +/* Create device number (T) or interrupt (F) template */ + +void dp_ini (t_bool dtpl) +{ +int32 u, j, dev; + +dp_tplte[0] = 0; /* controller */ +for (u = 0, j = 1; u < DP_NUMDR; u++) { /* loop thru units */ + dev = (u + 1) * o_DP0; /* drive dev # */ + dp_tplte[j++] = dev; + if (dtpl && (GET_DTYPE (dp_unit[u].flags) == TYPE_5440)) + dp_tplte[j++] = dev + o_DPF; /* if fixed */ + } +dp_tplte[j] = TPL_END; /* end marker */ +return; +} diff --git a/Interdata/id_fd.c b/Interdata/id_fd.c new file mode 100644 index 0000000..f99f2a4 --- /dev/null +++ b/Interdata/id_fd.c @@ -0,0 +1,508 @@ +/* id_fd.c: Interdata floppy disk simulator + + Copyright (c) 2001-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + fd M46-630 floppy disk + + A diskette consists of 77 tracks, each with 26 sectors of 128B. The + Interdata floppy uses a logical record numbering scheme from 1 to 2002. + Physical tracks are numbered 0-76, physical sectors 1-26. + + To allow for deleted data handling, a directory is appended to the end + of the image, one byte per LRN. Zero (the default) is a normal record, + non-zero a deleted record. +*/ + +#include "id_defs.h" + +#define FD_NUMTR 77 /* tracks/disk */ +#define FD_NUMSC 26 /* sectors/track */ +#define FD_NUMBY 128 /* bytes/sector */ +#define FD_NUMLRN (FD_NUMTR * FD_NUMSC) /* LRNs/disk */ +#define FD_SIZE (FD_NUMLRN * FD_NUMBY) /* bytes/disk */ +#define FD_NUMDR 4 /* drives/controller */ +#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ +#define UNIT_WLK (1u << UNIT_V_UF) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ +#define LRN u3 /* last LRN */ +#define FNC u4 /* last function */ +#define GET_DA(x) (((x) - 1) * FD_NUMBY) +#define GET_TRK(x) (((x) - 1) / FD_NUMSC) +#define GET_SEC(x) ((((x) - 1) % FD_NUMSC) + 1) +#define LRN_BOOT 5 /* boot block LRN */ + +/* Command byte */ + +#define CMD_V_UNIT 4 /* unit */ +#define CMD_M_UNIT 0x3 +#define GET_UNIT(x) (((x) >> CMD_V_UNIT) & CMD_M_UNIT) +#define CMD_V_FNC 0 /* function */ +#define CMD_M_FNC 0xF +#define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC) +#define FNC_RD 0x1 /* read */ +#define FNC_WR 0x2 /* write */ +#define FNC_RDID 0x3 /* read ID */ +#define FNC_RSTA 0x4 /* read status */ +#define FNC_DEL 0x5 /* write deleted */ +#define FNC_BOOT 0x6 /* boot */ +#define FNC_STOP 0x7 /* stop */ +#define FNC_RESET 0x8 /* reset */ +#define FNC_FMT 0x9 /* format NI */ +#define FNC_STOPPING 0x10 /* stopping */ + +/* Status byte, * = dynamic */ + +#define STA_WRP 0x80 /* *write prot */ +#define STA_DEF 0x40 /* def track NI */ +#define STA_DEL 0x20 /* del record */ +#define STA_ERR 0x10 /* error */ +#define STA_IDL 0x02 /* idle */ +#define STA_OFL 0x01 /* fault */ +#define STA_MASK (STA_DEF|STA_DEL|STA_ERR|STA_BSY|STA_IDL) +#define SET_EX (STA_ERR) /* set EX */ + +/* Extended status, 6 bytes, * = dynamic */ + +#define ES_SIZE 6 +#define ES0_HCRC 0x80 /* ID CRC NI */ +#define ES0_DCRC 0x40 /* data CRC NI */ +#define ES0_LRN 0x20 /* illegal LRN */ +#define ES0_WRP 0x10 /* *write prot */ +#define ES0_ERR 0x08 /* error */ +#define ES0_DEF 0x04 /* def trk NI */ +#define ES0_DEL 0x02 /* del rec NI */ +#define ES0_FLT 0x01 /* fault */ +#define ES1_TK0 0x80 /* track 0 */ +#define ES1_NRDY 0x40 /* not ready */ +#define ES1_NOAM 0x20 /* no addr mk NI */ +#define ES1_CMD 0x10 /* illegal cmd */ +#define ES1_SKE 0x08 /* seek err NI */ +#define ES1_UNS 0x04 /* unsafe NI */ +#define ES1_UNIT 0x03 /* unit # */ + +/* Processing options for commands */ + +#define C_RD 0x1 /* cmd reads disk */ +#define C_WD 0x2 /* cmd writes disk */ + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; + +uint32 fd_sta = 0; /* status */ +uint32 fd_cmd = 0; /* command */ +uint32 fd_db = 0; /* data buffer */ +uint32 fd_bptr = 0; /* buffer pointer */ +uint8 fdxb[FD_NUMBY] = { 0 }; /* sector buffer */ +uint8 fd_es[FD_NUMDR][ES_SIZE] = { 0 }; /* ext status */ +uint32 fd_lrn = 0; /* log rec # */ +uint32 fd_wdv = 0; /* wd valid */ +uint32 fd_stopioe = 1; /* stop on error */ +uint32 fd_arm = 0; /* intr arm */ +int32 fd_ctime = 100; /* command time */ +int32 fd_stime = 10; /* seek, per LRN */ +int32 fd_xtime = 1; /* tr set time */ + +static uint32 ctab[16] = { + 0, C_RD, C_WD, 0, /* 0, rd, wr, 0 */ + 0, C_WD, C_RD, 0, /* 0, del, boot, 0 */ + 0, 0, 0, 0, + 0, 0, 0, 0 + }; + +DEVICE fd_dev; +uint32 fd (uint32 dev, uint32 op, uint32 dat); +t_stat fd_svc (UNIT *uptr); +t_stat fd_reset (DEVICE *dptr); +t_stat fd_clr (DEVICE *dptr); +t_stat fd_boot (int32 unitno, DEVICE *dptr); +t_bool fd_dte (UNIT *uptr, t_bool wr); +uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt); +void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1); +void sched_seek (UNIT *uptr, int32 newlrn); + +/* FD data structures + + fd_dev FD device descriptor + fd_unit FD unit list + fd_reg FD register list + fd_mod FD modifier list +*/ + +DIB fd_dib = { d_FD, -1, v_FD, NULL, &fd, NULL }; + +UNIT fd_unit[] = { + { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) }, + { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) }, + { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) }, + { UDATA (&fd_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_BUFABLE+UNIT_MUSTBUF, FD_SIZE + FD_NUMLRN) } + }; + +REG fd_reg[] = { + { HRDATA (CMD, fd_cmd, 8) }, + { HRDATA (STA, fd_sta, 8) }, + { HRDATA (BUF, fd_db, 8) }, + { HRDATA (LRN, fd_lrn, 16) }, + { BRDATA (ESTA, fd_es, 16, 8, ES_SIZE * FD_NUMDR) }, + { BRDATA (DBUF, fdxb, 16, 8, FD_NUMBY) }, + { HRDATA (DBPTR, fd_bptr, 8) }, + { FLDATA (WDV, fd_wdv, 0) }, + { FLDATA (IREQ, int_req[l_FD], i_FD) }, + { FLDATA (IENB, int_enb[l_FD], i_FD) }, + { FLDATA (IARM, fd_arm, 0) }, + { DRDATA (CTIME, fd_ctime, 24), PV_LEFT }, + { DRDATA (STIME, fd_stime, 24), PV_LEFT }, + { DRDATA (XTIME, fd_xtime, 24), PV_LEFT }, + { FLDATA (STOP_IOE, fd_stopioe, 0) }, + { URDATA (ULRN, fd_unit[0].LRN, 16, 16, 0, FD_NUMDR, REG_HRO) }, + { URDATA (UFNC, fd_unit[0].FNC, 16, 8, 0, FD_NUMDR, REG_HRO) }, + { HRDATA (DEVNO, fd_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB fd_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE fd_dev = { + "FD", fd_unit, fd_reg, fd_mod, + FD_NUMDR, 16, 20, 1, 16, 8, + NULL, NULL, &fd_reset, + &fd_boot, NULL, NULL, + &fd_dib, DEV_DISABLE + }; + +/* Floppy disk: IO routine */ + +uint32 fd (uint32 dev, uint32 op, uint32 dat) +{ +int32 u, t, fnc; +UNIT *uptr; + +fnc = GET_FNC (fd_cmd); /* get fnc */ +u = GET_UNIT (fd_cmd); /* get unit */ +uptr = fd_dev.units + u; +switch (op) { /* case IO op */ + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_RD: /* read */ + if (fd_sta & (STA_IDL | STA_BSY)) return fd_db; /* idle, busy? */ + if (fd_bptr < FD_NUMBY) fd_db = fdxb[fd_bptr++];/* get byte */ + if (fd_bptr >= FD_NUMBY) { /* buf end? */ + if (ctab[fnc] & C_RD) { /* disk read? */ + sched_seek (uptr, uptr->LRN + 1); /* sched read */ + fd_sta = fd_sta | STA_BSY; /* set busy */ + } + else fd_bptr = 0; /* just wrap */ + } + if ((ctab[fnc] & C_RD) && fd_arm) /* if rd & arm, */ + SET_INT (v_FD); /* interrupt */ + return fd_db; /* return buf */ + + case IO_WD: /* write */ + if (fd_sta & STA_IDL) { /* idle? */ + fd_lrn = ((fd_lrn << 8) | dat) & DMASK16; /* insert byte */ + fd_wdv = 1; + break; + } + if (fd_bptr < FD_NUMBY) /* if room, */ + fdxb[fd_bptr++] = fd_db = dat; /* store byte */ + if (fd_bptr >= FD_NUMBY) { /* buf end? */ + if (ctab[fnc] & C_WD) { /* disk write? */ + sched_seek (uptr, uptr->LRN + 1); /* sched write */ + fd_sta = fd_sta | STA_BSY; /* set busy */ + } + else fd_bptr = 0; /* just wrap */ + } + if ((ctab[fnc] & C_WD) && fd_arm) /* if wr & arm, */ + SET_INT (v_FD); /* interrupt */ + break; + + case IO_SS: /* status */ + t = fd_sta & STA_MASK; /* get status */ + if ((uptr->flags & UNIT_ATT) == 0) t = t | STA_DU; + if (t & SET_EX) t = t | STA_EX; /* test for ex */ + return t; + + case IO_OC: /* command */ + fd_arm = int_chg (v_FD, dat, fd_arm); /* upd int ctrl */ + fnc = GET_FNC (dat); /* new fnc */ + fd_cmd = dat; /* save cmd */ + u = GET_UNIT (dat); /* get unit */ + uptr = fd_dev.units + u; + if (fnc == FNC_STOP) { /* stop? */ + uptr->FNC = uptr->FNC | FNC_STOPPING; /* flag stop */ + if (sim_is_active (uptr)) break; /* busy? cont */ + if (ctab[GET_FNC (uptr->FNC)] & C_WD) { /* write? */ + sched_seek (uptr, uptr->LRN + 1); /* sched write */ + fd_sta = fd_sta | STA_BSY; /* set busy */ + } + else fd_done (u, 0, 0, 0); /* nrml done */ + break; + } + else if (fd_sta & STA_IDL) { /* must be idle */ + if (fnc != FNC_RSTA) { /* !rd status */ + fd_sta = STA_BSY; /* busy, !idle */ + fd_es[u][0] = 0; + fd_es[u][1] = u; /* init ext sta */ + } + else fd_sta = (fd_sta & ~STA_IDL) | STA_BSY; + if (fnc == FNC_BOOT) t = LRN_BOOT; /* boot? fixed sec */ + else if (fd_wdv) t = fd_lrn; /* valid data? use */ + else t = uptr->LRN; /* use prev */ + fd_wdv = 0; /* data invalid */ + fd_bptr = 0; /* init buffer */ + uptr->FNC = fnc; /* save function */ + uptr->LRN = t; /* save LRN */ + if (ctab[fnc] & C_RD) sched_seek (uptr, t); /* seek now? */ + else sim_activate (uptr, fd_ctime); /* start cmd */ + } + break; + } + +return 0; +} + +/* Unit service; the action to be taken depends on command */ + +t_stat fd_svc (UNIT *uptr) +{ +uint32 i, u, tk, sc, crc, fnc, da; +uint8 *fbuf = uptr->filebuf; + +u = uptr - fd_dev.units; /* get unit number */ +fnc = GET_FNC (uptr->FNC); /* get function */ +switch (fnc) { /* case on function */ + + case FNC_RESET: /* reset */ + fd_clr (&fd_dev); /* clear device */ + fd_done (u, 0, 0, 0); /* set idle */ + return SCPE_OK; + + case FNC_STOP: /* stop */ + fd_done (u, 0, 0, 0); /* set idle */ + return SCPE_OK; + + case FNC_BOOT: /* boot, buf empty */ + case FNC_RD: /* read, buf empty */ + if (uptr->FNC & FNC_STOPPING) break; /* stopped? */ + if (fd_dte (uptr, FALSE)) return SCPE_OK; /* xfr error? */ + da = GET_DA (uptr->LRN); /* get disk addr */ + for (i = 0; i < FD_NUMBY; i++) /* read sector */ + fdxb[i] = fbuf[da + i]; + if (fbuf[FD_SIZE + uptr->LRN - 1]) { /* deleted? set err */ + fd_sta = fd_sta | STA_DEL; + fd_es[u][0] = fd_es[u][0] | ES0_DEL; + } + fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */ + fd_es[u][3] = GET_TRK (uptr->LRN); + fd_bptr = 0; /* init buf */ + uptr->LRN = uptr->LRN + 1; /* next block */ + break; + + case FNC_WR: case FNC_DEL: /* write block */ + if (fd_dte (uptr, TRUE)) return SCPE_OK; /* xfr error? */ + if (fd_bptr) { /* any transfer? */ + da = GET_DA (uptr->LRN); /* get disk addr */ + for (i = fd_bptr; i < FD_NUMBY; i++) /* pad sector */ + fdxb[i] = fd_db; + for (i = 0; i < FD_NUMBY; i++) /* write sector */ + fbuf[da + i] = fdxb[i]; /* then dir */ + fbuf[FD_SIZE + uptr->LRN - 1] = ((fnc == FNC_DEL)? 1: 0); + uptr->hwmark = uptr->capac; /* rewrite all */ + fd_es[u][2] = GET_SEC (uptr->LRN); /* set ext sec/trk */ + fd_es[u][3] = GET_TRK (uptr->LRN); + fd_bptr = 0; /* init buf */ + uptr->LRN = uptr->LRN + 1; /* next block */ + } + break; + + case FNC_RSTA: /* read status */ + if (uptr->flags & UNIT_WPRT) /* wr protected? */ + fd_es[u][0] = fd_es[u][0] | ES0_WRP; + if (GET_TRK (uptr->LRN) == 0) /* on track 0? */ + fd_es[u][1] = fd_es[u][1] | ES1_TK0; + if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */ + fd_es[u][0] = fd_es[u][0] | ES0_FLT; /* set err */ + fd_es[u][1] = fd_es[u][1] | ES1_NRDY; + } + for (i = 0; i < ES_SIZE; i++) fdxb[i] = fd_es[u][i]; /* copy to buf */ + for (i = ES_SIZE; i < FD_NUMBY; i++) fdxb[i] = 0; + break; + + case FNC_RDID: /* read ID */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */ + fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY); + return SCPE_OK; + } + for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr buf */ + tk = GET_TRK (uptr->LRN); /* get track */ + sc = GET_SEC (uptr->LRN); /* get sector */ + fdxb[0] = tk & 0xFF; /* store track */ + fdxb[2] = sc & 0xFF; /* store sector */ + crc = fd_crc (0xFFFF, 0xFE00, 8); /* CRC addr mark */ + crc = fd_crc (crc, tk << 8, 16); /* CRC track */ + crc = fd_crc (crc, sc << 8, 16); /* CRC sector */ + fdxb[4] = (crc >> 8) & 0xFF; /* store CRC */ + fdxb[5] = crc & 0xFF; + break; + + case FNC_FMT: /* format */ + default: + fd_done (u, STA_ERR, ES0_ERR, ES1_CMD); /* ill cmd */ + uptr->LRN = 1; /* on track 0 */ + return SCPE_OK; + } + +if (uptr->FNC & FNC_STOPPING) { /* stopping? */ + uptr->FNC = FNC_STOP; /* fnc = STOP */ + sim_activate (uptr, fd_ctime); /* schedule */ + } +fd_sta = fd_sta & ~STA_BSY; /* clear busy */ +if (fd_arm) SET_INT (v_FD); /* if armed, int */ +return SCPE_OK; +} + +/* Schedule seek */ + +void sched_seek (UNIT *uptr, int32 newlrn) +{ +int32 diff = newlrn - uptr->LRN; /* LRN diff */ + +if (diff < 0) diff = -diff; /* ABS */ +if (diff < 10) diff = 10; /* MIN 10 */ +sim_activate (uptr, diff * fd_stime); /* schedule */ +return; +} + +/* Command complete */ + +void fd_done (uint32 u, uint32 nsta, uint32 nes0, uint32 nes1) +{ +fd_sta = (fd_sta | STA_IDL | nsta) & ~STA_BSY; /* set idle */ +if (fd_arm) SET_INT (v_FD); /* if armed, int */ +fd_es[u][0] = fd_es[u][0] | nes0; /* set ext state */ +fd_es[u][1] = fd_es[u][1] | nes1; +return; +} + +/* Test for data transfer error */ + +t_bool fd_dte (UNIT *uptr, t_bool wr) +{ +uint32 u = uptr - fd_dev.units; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not attached? */ + fd_done (u, STA_ERR, ES0_ERR | ES0_FLT, ES1_NRDY); + return TRUE; + } +if (wr && (uptr->flags & UNIT_WPRT)) { /* wr protected? */ + fd_done (u, STA_ERR, ES0_ERR | ES0_WRP, 0); + return TRUE; + } +if ((uptr->LRN == 0) || (uptr->LRN > FD_NUMLRN)) { /* bad LRN? */ + fd_done (u, STA_ERR, ES0_ERR | ES0_LRN, 0); + return TRUE; + } +return FALSE; +} + +/* Header CRC calculation */ + +uint32 fd_crc (uint32 crc, uint32 dat, uint32 cnt) +{ +uint32 i, wrk; + +for (i = 0; i < cnt; i++) { + wrk = crc ^ dat; + crc = (crc << 1) & DMASK16; + if (wrk & SIGN16) crc = ((crc ^ 0x1020) + 1) & DMASK16; + dat = (dat << 1) & DMASK16; + } +return crc; +} + +/* Reset routine */ + +t_stat fd_clr (DEVICE *dptr) +{ +int32 i, j; +UNIT *uptr; + +fd_sta = STA_IDL; /* idle */ +fd_cmd = 0; /* clear state */ +fd_db = 0; +fd_bptr = 0; +fd_lrn = 1; +fd_wdv = 0; +for (i = 0; i < FD_NUMBY; i++) fdxb[i] = 0; /* clr xfr buf */ +for (i = 0; i < FD_NUMDR; i++) { /* loop thru units */ + for (j = 0; j < ES_SIZE; j++) fd_es[i][j] = 0; /* clr ext sta */ + fd_es[i][2] = 1; /* sector 1 */ + uptr = fd_dev.units + i; + sim_cancel (uptr); /* stop drive */ + uptr->LRN = 1; /* clear state */ + uptr->FNC = 0; + } +return SCPE_OK; +} + +t_stat fd_reset (DEVICE *dptr) +{ +CLR_INT (v_FD); /* clear int */ +CLR_ENB (v_FD); /* disable int */ +fd_arm = 0; /* disarm int */ +return fd_clr (dptr);; +} + +/* Bootstrap routine */ + +#define BOOT_START 0x50 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) + +static uint8 boot_rom[] = { + 0xD5, 0x00, 0x00, 0xCF, /* ST: AL CF */ + 0x43, 0x00, 0x00, 0x80 /* BR 80 */ + }; + +t_stat fd_boot (int32 unitno, DEVICE *dptr) +{ +extern uint32 PC, dec_flgs; +extern uint16 decrom[]; + +if (decrom[0xD5] & dec_flgs) return SCPE_NOFNC; /* AL defined? */ +IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */ +IOWriteB (AL_DEV, fd_dib.dno); /* set dev no */ +IOWriteB (AL_IOC, 0x86 + (unitno << CMD_V_UNIT)); /* set dev cmd, unit num */ +IOWriteB (AL_SCH, 0); /* clr sch dev no */ +PC = BOOT_START; +return SCPE_OK; +} diff --git a/Interdata/id_fp.c b/Interdata/id_fp.c new file mode 100644 index 0000000..b6ba1f1 --- /dev/null +++ b/Interdata/id_fp.c @@ -0,0 +1,532 @@ +/* id_fp.c: Interdata floating point instructions + + Copyright (c) 2000-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + The Interdata uses IBM 360 floating point format: + + 0 7 8 15 23 31 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |S| exponent | fraction | :single + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | fraction low | :double + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + where S = 0 for plus, 1 for minus + exponent = 16**n, in excess 64 code + fraction = .hhhhhh, seen as 6-14 hexadecimal digits + + Numbers can be normalized or unnormalized but are always normalized + when loaded. + + Interdata has 8 floating point registers, F0, F2, ... , FE. In floating + point instructions, the low order bit of the register number is ignored. + + On floating point overflow, the exponent and fraction are set to 1's. + On floating point underflow, the exponent and fraction are set to 0's. + + Interdata has both 32b only and 32b/64b floating point implementations. + In 32b only implementations, add and subtract are truncated, but multiply + and divide are rounded, and the floating point registers are kept in + memory. In 64b implementations, all single precision precision operations + are rounded, double precision operations are truncated, and the floating + point registers are kept in separate hardware arrays. +*/ + +#include "id_defs.h" + +struct ufp { /* unpacked fp */ + int32 sign; /* sign */ + int32 exp; /* unbiased exp */ + uint32 h; /* fr high */ + uint32 l; /* fr low */ + }; + +#define FP_V_SIGN 31 /* sign */ +#define FP_M_SIGN 0x1 +#define FP_GETSIGN(x) (((x) >> FP_V_SIGN) & FP_M_SIGN) +#define FP_V_EXP 24 /* exponent */ +#define FP_M_EXP 0x7F +#define FP_GETEXP(x) (((x) >> FP_V_EXP) & FP_M_EXP) +#define FP_V_FRH 0 /* fraction */ +#define FP_M_FRH 0xFFFFFF +#define FP_GETFRH(x) (((x) >> FP_V_FRH) & FP_M_FRH) +#define FP_GETFRL(x) (x) + +#define FP_BIAS 0x40 /* exp bias */ +#define FP_CARRY (1 << FP_V_EXP ) /* carry out */ +#define FP_NORM (0xF << (FP_V_EXP - 4)) /* normalized */ +#define FP_ROUND 0x80000000 + +/* Double precision fraction add/subtract/compare */ + +#define FR_ADD(d,s) d.l = (d.l + s.l) & DMASK32; \ + d.h = (d.h + s.h + (d.l < s.l)) & DMASK32 + +#define FR_SUB(d,s) d.h = (d.h - s.h - (d.l < s.l)) & DMASK32; \ + d.l = (d.l - s.l) & DMASK32 + +#define FR_GE(s1,s2) ((s1.h > s2.h) || \ + ((s1.h == s2.h) && (s1.l >= s2.l))) + +/* Variable and constant shifts; for constants, 0 < k < 32 */ + +#define FR_RSH_V(v,s) if ((s) < 32) { \ + v.l = ((v.l >> (s)) | \ + (v.h << (32 - (s)))) & DMASK32; \ + v.h = (v.h >> (s)) & DMASK32; \ + } \ + else { \ + v.l = v.h >> ((s) - 32); \ + v.h = 0; \ + } + +#define FR_RSH_K(v,s) v.l = ((v.l >> (s)) | \ + (v.h << (32 - (s)))) & DMASK32; \ + v.h = (v.h >> (s)) & DMASK32 + +#define FR_LSH_K(v,s) v.h = ((v.h << (s)) | \ + (v.l >> (32 - (s)))) & DMASK32; \ + v.l = (v.l << (s)) & DMASK32 + +#define Q_RND(op) (OP_DPFP (op) == 0) +#define Q_RND_AS(op) ((OP_DPFP (op) == 0) && fp_in_hwre) + +extern uint32 *R; +extern uint32 F[8]; +extern dpr_t D[8]; +extern uint16 decrom[]; +extern uint32 fp_in_hwre; +extern uint32 ReadF (uint32 loc, uint32 rel); +extern void WriteF (uint32 loc, uint32 dat, uint32 rel); +void ReadFP2 (struct ufp *fop, uint32 op, uint32 r2, uint32 ea); +void UnpackFPR (struct ufp *fop, uint32 op, uint32 r1); +void NormUFP (struct ufp *fop); +uint32 StoreFPR (struct ufp *fop, uint32 op, uint32 r1, uint32 rnd); +uint32 StoreFPX (struct ufp *fop, uint32 op, uint32 r1); + +/* Floating point load */ + +uint32 f_l (uint32 op, uint32 r1, uint32 r2, uint32 ea) +{ +struct ufp fop2; + +ReadFP2 (&fop2, op, r2, ea); /* get op, normalize */ +return StoreFPR (&fop2, op, r1, 0); /* store, chk unflo */ +} + +/* Floating point compare */ + +uint32 f_c (uint32 op, uint32 r1, uint32 r2, uint32 ea) +{ +struct ufp fop1, fop2; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ +UnpackFPR (&fop1, op, r1); /* get op1, norm */ +if (fop1.sign ^ fop2.sign) /* signs differ? */ + return (fop2.sign? CC_G: (CC_C | CC_L)); +if (fop1.exp != fop2.exp) /* exps differ? */ + return (((fop1.exp > fop2.exp) ^ fop1.sign)? CC_G: (CC_C | CC_L)); +if (fop1.h != fop2.h) /* hi fracs differ? */ + return (((fop1.h > fop2.h) ^ fop1.sign)? CC_G: (CC_C | CC_L)); +if (OP_DPFP (op) && (fop1.l != fop2.l)) /* dp: low fracs diff? */ + return (((fop1.l > fop2.l) ^ fop1.sign)? CC_G: (CC_C | CC_L)); +return 0; +} + +/* Floating to integer conversion */ + +uint32 f_fix (uint32 op, uint32 r1, uint32 r2) /* 16b */ +{ +struct ufp res; +uint32 cc; + +UnpackFPR (&res, op, r2); /* get op2, norm */ +if ((res.h == 0) || (res.exp < 0x41)) { /* result zero? */ + R[r1] = 0; + return 0; + } +if ((res.exp > 0x44) || /* result too big? */ + ((res.exp == 0x44) && (res.h >= 0x00800000))) { + res.h = MMASK16; + cc = CC_V; + } +else { + res.h = res.h >> ((0x46 - res.exp) * 4); /* right align frac */ + cc = 0; + } +if (res.sign) { + R[r1] = ((res.h ^ DMASK16) + 1) & DMASK16; /* negate result */ + return cc | CC_L; + } +R[r1] = res.h & DMASK16; +return cc | CC_G; +} + +uint32 f_fix32 (uint32 op, uint32 r1, uint32 r2) /* 32b */ +{ +struct ufp res; +uint32 cc; + +UnpackFPR (&res, op, r2); /* get op2, norm */ +if ((res.h == 0) || (res.exp < 0x41)) { /* result zero? */ + R[r1] = 0; + return 0; + } +if ((res.exp > 0x48) || /* result too big? */ + ((res.exp == 0x48) && (res.h >= 0x00800000))) { + res.h = MMASK32; + cc = CC_V; + } +else { + FR_LSH_K (res, 8); /* get all in 32b */ + res.h = res.h >> ((0x48 - res.exp) * 4); /* right align frac */ + cc = 0; + } +if (res.sign) { + R[r1] = (res.h ^ DMASK32) + 1; /* negate result */ + return cc | CC_L; + } +R[r1] = res.h; +return cc | CC_G; +} + +/* Integer to floating conversion */ + +uint32 f_flt (uint32 op, uint32 r1, uint32 r2) /* 16b */ +{ +struct ufp res = { 0, 0x44, 0, 0 }; /* +, 16**4 */ +uint32 cc; + +if (R[r2] == 0) cc = 0; /* zero arg? */ +else if (R[r2] & SIGN16) { /* neg arg? */ + res.sign = FP_M_SIGN; /* set sign */ + res.h = ((~R[r2] + 1) & DMASK16) << 8; /* get magnitude */ + cc = CC_L; + } +else { + res.h = R[r2] << 8; /* pos nz arg */ + cc = CC_G; + } +NormUFP (&res); /* normalize */ +StoreFPR (&res, op, r1, 0); /* store result */ +return cc; +} + +uint32 f_flt32 (uint32 op, uint32 r1, uint32 r2) /* 32b */ +{ +struct ufp res = { 0, 0x48, 0, 0 }; /* +, 16**8 */ +uint32 cc, t; + +t = R[r2]; /* int op */ +if (t) { /* nonzero arg? */ + if (t & SIGN32) { /* neg arg? */ + res.sign = FP_M_SIGN; /* set sign */ + t = (~t + 1) & DMASK32; /* get magnitude */ + cc = CC_L; + } + else cc = CC_G; /* pos nz arg */ + res.h = t >> 8; /* hi frac */ + res.l = t << 24; /* lo frac */ + } +else cc = 0; /* zero arg */ +NormUFP (&res); /* normalize */ +StoreFPR (&res, op, r1, 0); /* store result */ +return cc; +} + +/* Floating point add/subtract */ + +uint32 f_as (uint32 op, uint32 r1, uint32 r2, uint32 ea) +{ +struct ufp fop1, fop2, t; +int32 ediff; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ +UnpackFPR (&fop1, op, r1); /* get op1, norm */ +if (op & 1) fop2.sign = fop2.sign ^ 1; /* if sub, inv sign2 */ +if (fop1.h == 0) fop1 = fop2; /* if op1 = 0, res = op2 */ +else if (fop2.h != 0) { /* if op2 = 0, no add */ + if ((fop1.exp < fop2.exp) || /* |op1| < |op2|? */ + ((fop1.exp == fop2.exp) && + ((fop1.h < fop2.h) || + ((fop1.h == fop2.h) && (fop1.l < fop2.l))))) { + t = fop2; /* swap operands */ + fop2 = fop1; + fop1 = t; + } + ediff = fop1.exp - fop2.exp; /* exp difference */ + if (OP_DPFP (op) || fp_in_hwre) { /* dbl prec or hwre? */ + if (ediff >= 14) fop2.h = fop2.l = 0; /* diff too big? */ + else if (ediff) { /* any difference? */ + FR_RSH_V (fop2, ediff * 4); /* shift frac */ + } + } + else { /* sgl prec ucode */ + if (ediff >= 6) fop2.h = 0; /* diff too big? */ + else if (ediff) /* any difference? */ + fop2.h = fop2.h >> (ediff * 4); /* shift frac */ + } + if (fop1.sign ^ fop2.sign) { /* eff subtract */ + FR_SUB (fop1, fop2); /* sub fractions */ + NormUFP (&fop1); /* normalize result */ + } + else { + FR_ADD (fop1, fop2); /* add fractions */ + if (fop1.h & FP_CARRY) { /* carry out? */ + FR_RSH_K (fop1, 4); /* renormalize */ + fop1.exp = fop1.exp + 1; /* incr exp */ + } + } + } /* end if fop2 */ +return StoreFPR (&fop1, op, r1, Q_RND_AS (op)); /* store result */ +} + +/* Floating point multiply + + Notes: + - Exponent overflow/underflow is tested right after the exponent + add, without regard to potential changes due to normalization + - Exponent underflow is tested right after normalization, without + regard to changes due to rounding + - Single precision hardware multiply may generate up to 48b + - Double precision multiply generates 56b with no guard bits +*/ + +int32 f_m (uint32 op, uint32 r1, uint32 r2, uint32 ea) +{ +struct ufp fop1, fop2; +struct ufp res = { 0, 0, 0, 0 }; +uint32 i; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ +UnpackFPR (&fop1, op, r1); /* get op1, norm */ +if (fop1.h && fop2.h) { /* if both != 0 */ + res.sign = fop1.sign ^ fop2.sign; /* sign = diff */ + res.exp = fop1.exp + fop2.exp - FP_BIAS; /* exp = sum */ + if ((res.exp < 0) || (res.exp > FP_M_EXP)) /* ovf/undf? */ + return StoreFPX (&res, op, r1); /* early out */ + if ((fop1.l | fop2.l) == 0) { /* 24b x 24b? */ + for (i = 0; i < 24; i++) { /* 24 iterations */ + if (fop2.h & 1) res.h = res.h + fop1.h; /* add hi only */ + FR_RSH_K (res, 1); /* shift dp res */ + fop2.h = fop2.h >> 1; + } + } + else { /* some low 0's */ + if (fop2.l != 0) { /* 56b x 56b? */ + for (i = 0; i < 32; i++) { /* do low 32b */ + if (fop2.l & 1) { FR_ADD (res, fop1); } + FR_RSH_K (res, 1); + fop2.l = fop2.l >> 1; + } + } + for (i = 0; i < 24; i++) { /* do hi 24b */ + if (fop2.h & 1) { FR_ADD (res, fop1); } + FR_RSH_K (res, 1); + fop2.h = fop2.h >> 1; + } + } + NormUFP (&res); /* normalize */ + if (res.exp < 0) /* underflow? */ + return StoreFPX (&res, op, r1); /* early out */ + } +return StoreFPR (&res, op, r1, Q_RND (op)); /* store */ +} + +/* Floating point divide - see overflow/underflow notes for multiply */ + +int32 f_d (uint32 op, uint32 r1, uint32 r2, uint32 ea) +{ +struct ufp fop1, fop2; +struct ufp quo = { 0, 0, 0, 0 }; +int32 i; + +ReadFP2 (&fop2, op, r2, ea); /* get op2, norm */ +UnpackFPR (&fop1, op, r1); /* get op1, norm */ +if (fop2.h == 0) return CC_C | CC_V; /* div by zero? */ +if (fop1.h) { /* dvd != 0? */ + quo.sign = fop1.sign ^ fop2.sign; /* sign = diff */ + quo.exp = fop1.exp - fop2.exp + FP_BIAS; /* exp = diff */ + if ((quo.exp < 0) || (quo.exp > FP_M_EXP)) /* ovf/undf? */ + return StoreFPX (&quo, op, r1); /* early out */ + if (!FR_GE (fop1, fop2)) { + FR_LSH_K (fop1, 4); /* ensure success */ + } + else { /* exp off by 1 */ + quo.exp = quo.exp + 1; /* incr exponent */ + if (quo.exp > FP_M_EXP) /* overflow? */ + return StoreFPX (&quo, op, r1); /* early out */ + } + for (i = 0; i < (OP_DPFP (op)? 14: 6); i++) { /* 6/14 hex digits */ + FR_LSH_K (quo, 4); /* shift quotient */ + while (FR_GE (fop1, fop2)) { /* while sub works */ + FR_SUB (fop1, fop2); /* decrement */ + quo.l = quo.l + 1; /* add quo bit */ + } + FR_LSH_K (fop1, 4); /* shift divd */ + } + if (!OP_DPFP (op)) { /* single? */ + quo.h = quo.l; /* move quotient */ + if (fop1.h >= (fop2.h << 3)) quo.l = FP_ROUND; + else quo.l = 0; + } /* don't need to normalize */ + } /* end if fop1.h */ +return StoreFPR (&quo, op, r1, Q_RND (op)); /* store result */ +} + +/* Utility routines */ + +/* Unpack floating point number */ + +void UnpackFPR (struct ufp *fop, uint32 op, uint32 r1) +{ +uint32 hi; + +if (OP_DPFP (op)) { /* double prec? */ + hi = D[r1 >> 1].h; /* get hi */ + fop->l = FP_GETFRL (D[r1 >> 1].l); /* get lo */ + } +else { + hi = ReadFReg (r1); /* single prec */ + fop->l = 0; /* lo is zero */ + } +fop->h = FP_GETFRH (hi); /* get hi frac */ +if (fop->h || fop->l) { /* non-zero? */ + fop->sign = FP_GETSIGN (hi); /* get sign */ + fop->exp = FP_GETEXP (hi); /* get exp */ + NormUFP (fop); /* normalize */ + } +else fop->sign = fop->exp = 0; /* clean zero */ +return; +} + +/* Read memory operand */ + +void ReadFP2 (struct ufp *fop, uint32 op, uint32 r2, uint32 ea) +{ +uint32 hi; + +if (OP_TYPE (op) > OP_RR) { /* mem ref? */ + hi = ReadF (ea, VR); /* get hi */ + if (OP_DPFP (op)) fop->l = ReadF (ea + 4, VR); /* dp? get lo */ + else fop->l = 0; /* sp, lo = 0 */ + } +else { + if (OP_DPFP (op)) { /* RR */ + hi = D[r2 >> 1].h; /* dp? get dp reg */ + fop->l = D[r2 >> 1].l; + } + else { + hi = ReadFReg (r2); /* get sp reg */ + fop->l = 0; + } + } +fop->h = FP_GETFRH (hi); /* get hi frac */ +if (fop->h || fop->l) { /* non-zero? */ + fop->sign = FP_GETSIGN (hi); /* get sign */ + fop->exp = FP_GETEXP (hi); /* get exp */ + NormUFP (fop); /* normalize */ + } +else fop->sign = fop->exp = 0; /* clean zero */ +return; +} + +/* Normalize unpacked floating point number */ + +void NormUFP (struct ufp *fop) +{ +if ((fop->h & FP_M_FRH) || fop->l) { /* any fraction? */ + while ((fop->h & FP_NORM) == 0) { /* until norm */ + fop->h = (fop->h << 4) | ((fop->l >> 28) & 0xF); + fop->l = fop->l << 4; + fop->exp = fop->exp - 1; + } + } +else fop->sign = fop->exp = 0; /* clean 0 */ +return; +} + +/* Round fp number, store, generate condition codes */ + +uint32 StoreFPR (struct ufp *fop, uint32 op, uint32 r1, uint32 rnd) +{ +uint32 hi, cc; + +if (rnd && (fop->l & FP_ROUND)) { /* round? */ + fop->h = fop->h + 1; /* add 1 to frac */ + if (fop->h & FP_CARRY) { /* carry out? */ + fop->h = fop->h >> 4; /* renormalize */ + fop->exp = fop->exp + 1; /* incr exp */ + } + } +if (fop->h == 0) { /* result 0? */ + hi = fop->l = 0; /* store clean 0 */ + cc = 0; + } +else if (fop->exp < 0) { /* underflow? */ + hi = fop->l = 0; /* store clean 0 */ + cc = CC_V; + } +else if (fop->exp > FP_M_EXP) { /* overflow? */ + hi = (fop->sign)? 0xFFFFFFFF: 0x7FFFFFFF; + fop->l = 0xFFFFFFFF; + cc = (CC_V | ((fop->sign)? CC_L: CC_G)); + } +else { + hi = ((fop->sign & FP_M_SIGN) << FP_V_SIGN) | /* pack result */ + ((fop->exp & FP_M_EXP) << FP_V_EXP) | + ((fop->h & FP_M_FRH) << FP_V_FRH); + cc = (fop->sign)? CC_L: CC_G; /* set cc's */ + } +if (OP_DPFP (op)) { /* double precision? */ + D[r1 >> 1].h = hi; + D[r1 >> 1].l = fop->l; + } +else { + WriteFReg (r1, hi); + } +return cc; +} + +/* Generate exception result */ + +uint32 StoreFPX (struct ufp *fop, uint32 op, uint32 r1) +{ +uint32 cc = CC_V; + +if (fop->exp < 0) fop->h = fop->l = 0; /* undf? clean 0 */ +else { + fop->h = (fop->sign)? 0xFFFFFFFF: 0x7FFFFFFF; /* overflow */ + fop->l = 0xFFFFFFFF; + cc = cc | ((fop->sign)? CC_L: CC_G); + } +if (OP_DPFP (op)) { /* double precision? */ + D[r1 >> 1].h = fop->h; + D[r1 >> 1].l = fop->l; + } +else { + WriteFReg (r1, fop->h); + } +return cc; +} diff --git a/Interdata/id_idc.c b/Interdata/id_idc.c new file mode 100644 index 0000000..baf00dd --- /dev/null +++ b/Interdata/id_idc.c @@ -0,0 +1,784 @@ +/* id_idc.c: Interdata MSM/IDC disk controller simulator + + Copyright (c) 2001-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + idc MSM/IDC disk controller + + 03-Apr-06 RMS Fixed WD/WH handling (found by Davis Johnson) + 30-Mar-06 RMS Fixed bug, nop command should be ignored (found by Davis Johnson) + 25-Apr-03 RMS Revised for extended file support + 16-Feb-03 RMS Fixed read to test transfer ok before selch operation + + Note: define flag ID_IDC to enable the extra functions of the intelligent + disk controller +*/ + +#include "id_defs.h" + +#define IDC_NUMBY 256 /* bytes/sector */ +#define IDC_NUMSC 64 /* sectors/track */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 0x7 +#define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) + +#define CYL u3 /* current cylinder */ +#define HD u4 /* current head */ +#define STD buf /* drive status */ +#define FNC wait /* function */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define IDC_DRVMASK ((1 << ID_NUMDR) - 1) /* drive bit mask */ +#define IDC_DIRMASK (IDC_DRVMASK << (i_IDC + 1)) /* drive irq mask */ + +/* Controller status */ + +#define STC_WRP 0x80 /* write protected */ +#define STC_ACF 0x40 /* addr cmp fail */ +#define STC_DEF 0x20 /* def track NI */ +#define STC_CYO 0x10 /* cylinder ovflo */ +#define STC_IDL 0x02 /* ctrl idle */ +#define STC_DTE 0x01 /* xfer error */ +#define SETC_EX (STC_WRP|STC_ACF|STC_DEF|STC_CYO) +#define STC_MASK (STC_WRP|STC_ACF|STC_DEF|STC_CYO|STA_BSY|STC_IDL|STC_DTE) + +/* Controller command */ + +#define CMC_MASK 0x3F +#define CMC_CLR 0x08 /* reset */ +#define CMC_RD 0x01 /* read */ +#define CMC_WR 0x02 /* write */ +#define CMC_RCHK 0x03 /* read check */ +#define CMC_FCHK 0x04 /* format check NI */ +#define CMC_RFMT 0x05 /* read fmt NI */ +#define CMC_WFMT 0x06 /* write fmt NI */ +#define CMC_WFTK 0x07 /* write fmt track NI */ + +/* IDC only functions */ + +#define CMC_RRAM 0x10 /* read RAM */ +#define CMC_WRAM 0x11 /* write RAM */ +#define CMC_EXP0 0x12 /* read page 0 NI */ +#define CMC_RUNC 0x21 /* read uncorr */ +#define CMC_STST 0x30 /* self test */ +#define CMC_WLNG 0x32 /* write long NI */ +#define CMC_LAMP 0x37 /* lamp test */ + +#define CMC_DRV 0x100 /* drive func */ +#define CMC_DRV1 0x200 /* drive func, part 2 */ + +/* Drive status, ^ = dynamic, * = in unit status */ + +#define STD_WRP 0x80 /* ^write prot */ +/* 0x40 /* unused */ +#define STD_ACH 0x20 /* alt chan busy NI */ +#define STD_UNS 0x10 /* *unsafe */ +#define STD_NRDY 0x08 /* ^not ready */ +#define STD_SKI 0x02 /* *seek incomplete */ +#define STD_OFFL 0x01 /* ^offline */ +#define STD_UST (STD_UNS | STD_SKI) /* set from unit */ +#define SETD_EX (STD_WRP | STD_UNS) /* set examine */ + +/* Drive command */ + +#define CMDF_SHD 0x20 /* set head */ +#define CMDF_SCY 0x10 /* set cylinder */ +#define CMD_SK 0x02 /* seek */ +#define CMD_RST 0x01 /* restore */ + +#define CMDX_MASK 0x30 /* ext cmd bits */ +#define CMDX_RLS 0x80 /* release */ +#define CMDX_CLF 0x40 /* clear fault */ +#define CMDX_SVP 0x08 /* servo + */ +#define CMDX_SVM 0x04 /* servo - */ +#define CMDX_DSP 0x02 /* strobe + */ +#define CMDX_DSM 0x01 /* strobe - */ + +/* Geometry masks */ + +#define CY_MASK 0xFFF /* cylinder */ +#define HD_MASK 0x1F /* head mask */ +#define SC_MASK 0x3F /* sector mask */ +#define HCYL_V_HD 10 /* head/cyl word */ +#define HCYL_V_CYL 0 + +#define GET_SA(cy,sf,sc,t) \ + (((((cy)*drv_tab[t].surf)+(sf))*IDC_NUMSC)+(sc)) + +/* The MSM (IDC) controller supports (two) six different disk drive types: + + type #sectors/ #surfaces/ #cylinders/ + surface cylinder drive + + MCCDD16 64 1 823 IDC + MCCDD48 64 3 823 IDC + MCCDD80 64 5 823 IDC + MSM80 64 5 823 MSM + MSM300 64 19 823 MSM + MSM330F 64 16 1024 IDC + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE AND MUST HAVE + THE SAME SECTORS/TRACK. +*/ + +#define TYPE_MCCDD16 0 +#define SURF_MCCDD16 1 +#define CYL_MCCDD16 823 +#define SIZE_MCCDD16 (IDC_NUMSC * SURF_MCCDD16 * CYL_MCCDD16 * IDC_NUMBY) + +#define TYPE_MCCDD48 1 +#define SURF_MCCDD48 3 +#define CYL_MCCDD48 823 +#define SIZE_MCCDD48 (IDC_NUMSC * SURF_MCCDD48 * CYL_MCCDD48 * IDC_NUMBY) + +#define TYPE_MCCDD80 2 +#define SURF_MCCDD80 5 +#define CYL_MCCDD80 823 +#define SIZE_MCCDD80 (IDC_NUMSC * SURF_MCCDD80 * CYL_MCCDD80 * IDC_NUMBY) + +#define TYPE_MSM80 3 +#define SURF_MSM80 5 +#define CYL_MSM80 823 +#define SIZE_MSM80 (IDC_NUMSC * SURF_MSM80 * CYL_MSM80 * IDC_NUMBY) + +#define TYPE_MSM300 4 +#define SURF_MSM300 19 +#define CYL_MSM300 823 +#define SIZE_MSM300 (IDC_NUMSC * SURF_MSM300 * CYL_MSM300 * IDC_NUMBY) + +#define TYPE_MSM330F 5 +#define SURF_MSM330F 16 +#define CYL_MSM330F 1024 +#define SIZE_MSM330F (IDC_NUMSC * SURF_MSM330F * CYL_MSM330F * IDC_NUMBY) + + +struct drvtyp { + uint32 surf; /* surfaces */ + uint32 cyl; /* cylinders */ + uint32 size; /* #blocks */ + uint32 msmf; /* MSM drive */ + }; + +static struct drvtyp drv_tab[] = { + { SURF_MCCDD16, CYL_MCCDD16, SIZE_MCCDD16, 0 }, + { SURF_MCCDD48, CYL_MCCDD48, SIZE_MCCDD48, 0 }, + { SURF_MCCDD80, CYL_MCCDD80, SIZE_MCCDD80, 0 }, + { SURF_MSM80, CYL_MSM80, SIZE_MSM80, 1 }, + { SURF_MSM300, CYL_MSM300, SIZE_MSM300, 1 }, + { SURF_MSM330F, CYL_MSM330F, SIZE_MSM330F, 0 }, + { 0 } + }; + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; + +uint8 idcxb[IDC_NUMBY * 3]; /* xfer buffer */ +uint32 idc_bptr = 0; /* buffer ptr */ +uint32 idc_wdptr = 0; /* ctrl write data ptr */ +uint32 idc_db = 0; /* ctrl buffer */ +uint32 idc_sta = 0; /* ctrl status */ +uint32 idc_sec = 0; /* sector */ +uint32 idc_hcyl = 0; /* head/cyl */ +uint32 idc_svun = 0; /* most recent unit */ +uint32 idc_1st = 0; /* first byte */ +uint32 idc_arm = 0; /* ctrl armed */ +uint32 idd_db = 0; /* drive buffer */ +uint32 idd_wdptr = 0; /* drive write data ptr */ +uint32 idd_arm[ID_NUMDR] = { 0 }; /* drives armed */ +uint16 idd_dcy[ID_NUMDR] = { 0 }; /* desired cyl */ +uint32 idd_sirq = 0; /* drive saved irq */ +int32 idc_stime = 100; /* seek latency */ +int32 idc_rtime = 100; /* rotate latency */ +int32 idc_ctime = 5; /* command latency */ +uint8 idc_tplte[] = { 0, 1, 2, 3, 4, TPL_END }; /* ctrl + drive */ + +DEVICE idc_dev; +uint32 id (uint32 dev, uint32 op, uint32 dat); +t_stat idc_svc (UNIT *uptr); +t_stat idc_reset (DEVICE *dptr); +t_stat idc_attach (UNIT *uptr, char *cptr); +t_stat idc_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +void idc_wd_byte (uint32 dat); +t_stat idc_rds (UNIT *uptr); +t_stat idc_wds (UNIT *uptr); +t_bool idc_dter (UNIT *uptr, uint32 first); +void idc_done (uint32 flg); + +extern t_stat id_dboot (int32 u, DEVICE *dptr); + +/* DP data structures + + idc_dev DP device descriptor + idc_unit DP unit list + idc_reg DP register list + idc_mod DP modifier list +*/ + +DIB idc_dib = { d_IDC, 0, v_IDC, idc_tplte, &id, NULL }; + +UNIT idc_unit[] = { + { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) }, + { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) }, + { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) }, + { UDATA (&idc_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(TYPE_MSM80 << UNIT_V_DTYPE), SIZE_MSM80) } + }; + +REG idc_reg[] = { + { HRDATA (STA, idc_sta, 8) }, + { HRDATA (BUF, idc_db, 8) }, + { HRDATA (SEC, idc_sec, 8) }, + { HRDATA (HCYL, idc_hcyl, 16) }, + { HRDATA (BUF, idd_db, 8) }, + { HRDATA (SVUN, idc_svun, 2), REG_HIDDEN }, + { BRDATA (DBUF, idcxb, 16, 8, IDC_NUMBY * 3) }, + { HRDATA (DBPTR, idc_bptr, 10), REG_RO }, + { FLDATA (FIRST, idc_1st, 0) }, + { HRDATA (CWDPTR, idc_wdptr, 2) }, + { HRDATA (DWDPTR, idc_wdptr, 1) }, + { GRDATA (IREQ, int_req[l_IDC], 16, ID_NUMDR + 1, i_IDC) }, + { GRDATA (IENB, int_enb[l_IDC], 16, ID_NUMDR + 1, i_IDC) }, + { GRDATA (SIREQ, idd_sirq, 16, ID_NUMDR, i_IDC + 1), REG_RO }, + { FLDATA (ICARM, idc_arm, 0) }, + { BRDATA (IDARM, idd_arm, 16, 1, ID_NUMDR) }, + { DRDATA (RTIME, idc_rtime, 24), PV_LEFT | REG_NZ }, + { DRDATA (STIME, idc_stime, 24), PV_LEFT | REG_NZ }, + { DRDATA (CTIME, idc_ctime, 24), PV_LEFT | REG_NZ }, + { BRDATA (CYL, idd_dcy, 16, 16, ID_NUMDR) }, + { URDATA (UCYL, idc_unit[0].CYL, 16, 12, 0, + ID_NUMDR, REG_RO) }, + { URDATA (UHD, idc_unit[0].HD, 16, 5, 0, + ID_NUMDR, REG_RO) }, + { URDATA (UFNC, idc_unit[0].FNC, 16, 10, 0, + ID_NUMDR, REG_HRO) }, + { URDATA (UST, idc_unit[0].STD, 16, 8, 0, + ID_NUMDR, REG_RO) }, + { URDATA (CAPAC, idc_unit[0].capac, 10, T_ADDR_W, 0, + ID_NUMDR, PV_LEFT | REG_HRO) }, + { HRDATA (DEVNO, idc_dib.dno, 8), REG_HRO }, + { HRDATA (SELCH, idc_dib.sch, 2), REG_HRO }, + { BRDATA (TPLTE, idc_tplte, 16, 8, ID_NUMDR + 1), REG_HRO }, + { NULL } + }; + +MTAB idc_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD16 << UNIT_V_DTYPE) + UNIT_ATT, + "MCCDD16", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD48 << UNIT_V_DTYPE) + UNIT_ATT, + "MCCDD48", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD80 << UNIT_V_DTYPE) + UNIT_ATT, + "MCCDD80", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_MSM330F << UNIT_V_DTYPE) + UNIT_ATT, + "MSM330F", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD16 << UNIT_V_DTYPE), + "MCCDD16", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD48 << UNIT_V_DTYPE), + "MCCDD48", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MCCDD80 << UNIT_V_DTYPE), + "MCCDD80", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MSM330F << UNIT_V_DTYPE), + "MSM330F", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MCCDD16 << UNIT_V_DTYPE), + NULL, "MCCDD16", &idc_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MCCDD48 << UNIT_V_DTYPE), + NULL, "MCCDD48", &idc_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MCCDD80 << UNIT_V_DTYPE), + NULL, "MCCDD80", &idc_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MSM330F << UNIT_V_DTYPE), + NULL, "MSM330F", &idc_set_size }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_MSM80 << UNIT_V_DTYPE) + UNIT_ATT, + "MSM80", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_MSM300 << UNIT_V_DTYPE) + UNIT_ATT, + "MSM300", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MSM80 << UNIT_V_DTYPE), + "MSM80", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_MSM300 << UNIT_V_DTYPE), + "MSM300", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MSM80 << UNIT_V_DTYPE), + NULL, "MSM80", &idc_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_MSM300 << UNIT_V_DTYPE), + NULL, "MSM300", &idc_set_size }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH", + &set_sch, &show_sch, NULL }, + { 0 } + }; + +DEVICE idc_dev = { + "DM", idc_unit, idc_reg, idc_mod, + ID_NUMDR, 16, 29, 1, 16, 8, + NULL, NULL, &idc_reset, + &id_dboot, &idc_attach, NULL, + &idc_dib, DEV_DISABLE + }; + +/* Controller: IO routine */ + +uint32 idc (uint32 dev, uint32 op, uint32 dat) +{ +uint32 f, t; +UNIT *uptr; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + sch_adr (idc_dib.sch, dev); /* inform sel ch */ + return HW; /* halfwords */ + + case IO_RD: /* read data */ + case IO_RH: /* read halfword */ + return 0; /* return data */ + + case IO_WD: /* write data */ + idc_wd_byte (dat); /* one byte only */ + break; + + case IO_WH: /* write halfword */ + idc_wd_byte (dat >> 8); /* high byte */ + idc_wd_byte (dat); /* low byte */ + break; + + case IO_SS: /* status */ + t = idc_sta & STC_MASK; /* get status */ + if (t & SETC_EX) t = t | STA_EX; /* test for EX */ + return t; + + case IO_OC: /* command */ + idc_arm = int_chg (v_IDC, dat, idc_arm); /* upd int ctrl */ + idc_wdptr = 0; /* init ptr */ + f = dat & CMC_MASK; /* get cmd */ + uptr = idc_dev.units + idc_svun; /* get unit */ + if (f & CMC_CLR) { /* clear? */ + idc_reset (&idc_dev); /* reset world */ + break; + } + if ((f == 0) || /* if nop, */ + (f == CMC_EXP0) || /* expg, */ + !(idc_sta & STC_IDL) || /* !idle, */ + sim_is_active (uptr)) break; /* unit busy, ignore */ + idc_sta = STA_BSY; /* bsy=1,idl,err=0 */ + idc_1st = 1; /* xfr not started */ + idc_bptr = 0; /* buffer empty */ + uptr->FNC = f; /* save cmd */ + sim_activate (uptr, idc_rtime); /* schedule */ + idd_sirq = int_req[l_IDC] & IDC_DIRMASK; /* save drv ints */ + int_req[l_IDC] = int_req[l_IDC] & ~IDC_DIRMASK; /* clr drv ints */ + break; + } + +return 0; +} + +/* Process WD/WH data */ + +void idc_wd_byte (uint32 dat) +{ +dat = dat & 0xFF; +switch (idc_wdptr) { + + case 0: /* byte 0 = sector */ + idc_sec = dat; + idc_wdptr++; + break; + + case 1: /* byte 1 = high hd/cyl */ + idc_hcyl = (idc_hcyl & 0xFF) | (dat << 8); + idc_wdptr++; + break; + + case 2: /* byte 2 = low hd/cyl */ + idc_hcyl = (idc_hcyl & 0xFF00) | dat; + idc_wdptr = 0; + break; + } + +return; +} + +/* Drives: IO routine */ + +uint32 id (uint32 dev, uint32 op, uint32 dat) +{ +uint32 t, u, f; +UNIT *uptr; + +if (dev == idc_dib.dno) return idc (dev, op, dat); /* controller? */ +u = (dev - idc_dib.dno - o_ID0) / o_ID0; /* get unit num */ +uptr = idc_dev.units + u; /* get unit ptr */ +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + if (idc_sta & STC_IDL) idc_svun = u; /* idle? save unit */ + return BY; /* byte only */ + + case IO_RD: /* read data */ + case IO_RH: + return 0; + + case IO_WD: /* write data */ + if (idd_wdptr & 1) /* low byte? */ + idd_db = (idd_db & 0xFF00) | dat; + else idd_db = (idd_db & 0xFF) | (dat << 8); /* no, high */ + idd_wdptr = idd_wdptr ^ 1; /* other byte */ + break; + + case IO_SS: /* status */ + if (uptr->flags & UNIT_ATT) t = + ((uptr->flags & UNIT_WPRT)? STD_WRP: 0) | + (sim_is_active (uptr)? STD_NRDY: 0) | + (uptr->STD & STD_UST); + else t = STD_NRDY | STD_OFFL; /* off = X'09' */ + if (t & SETD_EX) t = t | STA_EX; /* test for ex */ + return t; + + case IO_OC: /* command */ + idd_arm[u] = int_chg (v_IDC + u + 1, dat, idd_arm[u]); + idd_wdptr = 0; /* init ptr */ + if (idd_arm[u] == 0) /* disarmed? */ + idd_sirq &= ~(1 << (v_IDC + u + 1)); /* clr saved req */ + f = dat & CMC_MASK; /* get cmd */ + if ((f == 0) || /* if nop, */ + (f == CMDX_MASK) || /* 0x30, */ + !(idc_sta & STC_IDL) || /* !idle, */ + sim_is_active (uptr)) break; /* unit busy, ignore */ + uptr->FNC = f | CMC_DRV; /* save cmd */ + idc_sta = idc_sta & ~STC_IDL; /* clr idle */ + sim_activate (uptr, idc_ctime); /* schedule */ + break; + } + +return 0; +} + +/* Unit service + + If drive command, process; if an interrupt is needed (positioning + command), schedule second part + + If data transfer command, process; must use selector channel +*/ + +t_stat idc_svc (UNIT *uptr) +{ +int32 diff; +uint32 f, u = uptr - idc_dev.units; /* get unit number */ +uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ +uint32 t; +t_stat r; + +if (uptr->FNC & CMC_DRV) { /* drive cmd? */ + f = uptr->FNC & CMC_MASK; /* get cmd */ + if (uptr->FNC & CMC_DRV1) { /* part 2? */ + if (idd_arm[u]) { /* drv int armed? */ + if (idc_sta & STC_IDL) /* ctrl idle? */ + SET_INT (v_IDC + u + 1); /* req intr */ + else idd_sirq |= (1 << (v_IDC + u + 1)); /* def intr */ + } + if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; + if (((f & CMDX_MASK) == 0) && /* seek? */ + (f & (CMD_SK | CMD_RST))) { + if (idd_dcy[u] >= drv_tab[dtype].cyl) /* bad cylinder? */ + uptr->STD = uptr->STD | STD_SKI; /* error */ + uptr->CYL = idd_dcy[u]; /* put on cyl */ + } + } /* end if p2 */ + else { /* part 1 */ + idc_sta = idc_sta | STC_IDL; /* set idle */ + uptr->FNC = uptr->FNC | CMC_DRV1; /* set part 2 */ + if (f >= CMDX_MASK) { /* extended? */ + if (f & CMDX_CLF) /* clr fault? */ + uptr->STD = uptr->STD & ~STD_UNS; /* clr unsafe */ + if (f & (CMDX_RLS | CMDX_SVP | CMDX_SVM)) /* intr expected? */ + sim_activate (uptr, idc_ctime); + } + else if (f >= CMDF_SCY) { /* tag? */ + if (f & CMDF_SHD) uptr->HD = idd_db & HD_MASK; + else if (f & CMDF_SCY) { + if (idd_db >= drv_tab[dtype].cyl) /* bad cylinder? */ + uptr->STD = uptr->STD | STD_SKI; /* set seek inc */ + idd_dcy[u] = idd_db & CY_MASK; + } + } + else if (f & (CMD_SK | CMD_RST)) { /* seek? */ + if (f == CMD_RST) idd_dcy[u] = 0; /* restore? */ + if (idd_dcy[u] >= drv_tab[dtype].cyl) { /* bad cylinder? */ + uptr->STD = uptr->STD | STD_SKI; /* set seek inc */ + idd_dcy[u] = uptr->CYL; /* no motion */ + sim_activate (uptr, 0); /* finish asap */ + } + else { /* cylinder ok */ + uptr->STD = uptr->STD & ~STD_SKI; /* clr seek inc */ + diff = idd_dcy[u] - uptr->CYL; + if (diff < 0) diff = -diff; /* ABS cyl diff */ + else if (diff == 0) diff = 1; /* must be nz */ + sim_activate (uptr, diff * idc_stime); + } + } + } /* end else p1 */ + return SCPE_OK; /* end if drv */ + } + +switch (uptr->FNC & CMC_MASK) { /* case on func */ + + case CMC_RCHK: /* read check */ + idc_dter (uptr, 1); /* check xfr err */ + break; + +#if defined (ID_IDC) + case CMC_RUNC: /* read uncorr */ +#endif + case CMC_RD: /* read */ + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ + if (idc_dter (uptr, idc_1st)) return SCPE_OK; /* dte? done */ + if (r = idc_rds (uptr)) return r; /* read sec, err? */ + idc_1st = 0; + t = sch_wrmem (idc_dib.sch, idcxb, IDC_NUMBY); /* write mem */ + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ + sim_activate (uptr, idc_rtime); /* reschedule */ + return SCPE_OK; + } + break; /* no, set done */ + } + idc_sta = idc_sta | STC_DTE; /* cant work */ + break; + + case CMC_WR: /* write */ + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ + if (idc_dter (uptr, idc_1st)) return SCPE_OK; /* dte? done */ + idc_bptr = sch_rdmem (idc_dib.sch, idcxb, IDC_NUMBY); /* read mem */ + idc_db = idcxb[idc_bptr - 1]; /* last byte */ + if (r = idc_wds (uptr)) return r; /* write sec, err? */ + idc_1st = 0; + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ + sim_activate (uptr, idc_rtime); /* reschedule */ + return SCPE_OK; + } + break; /* no, set done */ + } + idc_sta = idc_sta | STC_DTE; /* cant work */ + break; + + case CMC_FCHK: case CMC_RFMT: case CMC_WFMT: case CMC_WFTK: + idc_dter (uptr, 1); + idc_sta = idc_sta | STC_WRP; + break; + +#if defined (ID_IDC) + case CMC_RRAM: /* read RAM */ + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ + sch_wrmem (idc_dib.sch, idcxb, IDC_NUMBY * 3); + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ + sim_activate (uptr, idc_rtime); /* reschedule */ + return SCPE_OK; + } + break; /* no, set done */ + } + idc_sta = idc_sta | STC_DTE; /* cant work */ + break; + + case CMC_WRAM: /* write RAM */ + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* sch transfer? */ + sch_rdmem (idc_dib.sch, idcxb, IDC_NUMBY * 3); /* read from mem */ + if (sch_actv (idc_dib.sch, idc_dib.dno)) { /* more to do? */ + sim_activate (uptr, idc_rtime); /* reschedule */ + return SCPE_OK; + } + break; /* no, set done */ + } + idc_sta = idc_sta | STC_DTE; /* cant work */ + break; + + case CMC_STST: case CMC_LAMP: /* tests */ + break; +#endif + +default: + idc_sta = idc_sta | STC_DTE; + break; + } + +idc_done (0); /* done */ +return SCPE_OK; +} + +/* Read data sector */ + +t_stat idc_rds (UNIT *uptr) +{ +uint32 i; + +i = fxread (idcxb, sizeof (uint8), IDC_NUMBY, uptr->fileref); +if (ferror (uptr->fileref)) { /* error? */ + perror ("IDC I/O error"); + clearerr (uptr->fileref); + idc_done (STC_DTE); + return SCPE_IOERR; + } +for ( ; i < IDC_NUMBY; i++) idcxb[i] = 0; /* fill with 0's */ +return SCPE_OK; +} + +/* Write data sector */ + +t_bool idc_wds (UNIT *uptr) +{ +for ( ; idc_bptr < IDC_NUMBY; idc_bptr++) + idcxb[idc_bptr] = idc_db; /* fill with last */ +fxwrite (idcxb, sizeof (uint8), IDC_NUMBY, uptr->fileref); +if (ferror (uptr->fileref)) { /* error? */ + perror ("IDC I/O error"); + clearerr (uptr->fileref); + idc_done (STC_DTE); + return SCPE_IOERR; + } +return FALSE; +} + +/* Data transfer error test routine */ + +t_bool idc_dter (UNIT *uptr, uint32 first) +{ +uint32 cy; +uint32 hd, sc, sa; +uint32 dtype = GET_DTYPE (uptr->flags); /* get drive type */ + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + idc_done (STC_DTE); /* error, done */ + return TRUE; + } +if ((uptr->flags & UNIT_WPRT) && (uptr->FNC == CMC_WR)) { + idc_done (STC_WRP); /* error, done */ + return TRUE; + } +cy = uptr->CYL; /* get cylinder */ +hd = uptr->HD; /* get head */ +sc = idc_sec & SC_MASK; /* get sector */ +if (cy >= drv_tab[dtype].cyl) { /* bad cylinder? */ + uptr->STD = uptr->STD | STD_SKI; /* error */ + idc_done (STC_DTE); /* error, done */ + return TRUE; + } +if (hd >= drv_tab[dtype].surf) { /* bad head? */ + if (first) { /* 1st xfer? */ + uptr->STD = uptr->STD | STD_UNS; /* drive unsafe */ + idc_done (STC_ACF); + } + else idc_done (STC_CYO); /* no, cyl ovf */ + return TRUE; + } +sa = GET_SA (cy, hd, sc, dtype); /* curr disk addr */ +fseek (uptr->fileref, sa * IDC_NUMBY, SEEK_SET); /* seek to pos */ +idc_sec = (idc_sec + 1) & SC_MASK; /* incr disk addr */ +if (idc_sec == 0) uptr->HD = uptr->HD + 1; +return FALSE; +} + +/* Data transfer done routine */ + +void idc_done (uint32 flg) +{ +idc_sta = (idc_sta | STC_IDL | flg) & ~STA_BSY; /* set flag, idle */ +if (idc_arm) SET_INT (v_IDC); /* if armed, intr */ +int_req[l_IDC] = int_req[l_IDC] | idd_sirq; /* restore drv ints */ +idd_sirq = 0; /* clear saved */ +if (flg) sch_stop (idc_dib.sch); /* if err, stop sch */ +return; +} + +/* Reset routine */ + +t_stat idc_reset (DEVICE *dptr) +{ +uint32 u; +UNIT *uptr; + +idc_sta = STC_IDL | STA_BSY; /* idle, busy */ +idc_wdptr = 0; +idd_wdptr = 0; +idc_1st = 0; /* clear flag */ +idc_svun = idc_db = 0; /* clear unit, buf */ +idc_sec = 0; /* clear addr */ +idc_hcyl = 0; +CLR_INT (v_IDC); /* clear ctrl int */ +CLR_ENB (v_IDC); /* clear ctrl enb */ +idc_arm = 0; /* clear ctrl arm */ +idd_sirq = 0; +for (u = 0; u < ID_NUMDR; u++) { /* loop thru units */ + uptr = idc_dev.units + u; + uptr->CYL = uptr->STD = 0; + uptr->HD = uptr->FNC = 0; + idd_dcy[u] = 0; + CLR_INT (v_IDC + u + 1); /* clear intr */ + CLR_ENB (v_IDC + u + 1); /* clear enable */ + idd_arm[u] = 0; /* clear arm */ + sim_cancel (uptr); /* cancel activity */ + } +return SCPE_OK; +} + +/* Attach routine (with optional autosizing) */ + +t_stat idc_attach (UNIT *uptr, char *cptr) +{ +uint32 i, p; +t_stat r; + +uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +uptr->CYL = 0; +if ((uptr->flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */ +if ((p = ftell (uptr->fileref)) == 0) return SCPE_OK; +for (i = 0; drv_tab[i].surf != 0; i++) { + if (p <= drv_tab[i].size) { + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr->capac = drv_tab[i].size; + return SCPE_OK; + } + } +return SCPE_OK; +} + +/* Set size command validation routine */ + +t_stat idc_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = drv_tab[GET_DTYPE (val)].size; +return SCPE_OK; +} diff --git a/Interdata/id_io.c b/Interdata/id_io.c new file mode 100644 index 0000000..b415f80 --- /dev/null +++ b/Interdata/id_io.c @@ -0,0 +1,607 @@ +/* id_io.c: Interdata CPU-independent I/O routines + + Copyright (c) 2001-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Mar-06 RMS Fixed bug, GO preserves EXA and SSTA (found by Davis Johnson) + 21-Jun-03 RMS Changed subroutine argument for ARM compiler conflict + + Interdata I/O devices are defined by a device information block: + + dno base device number + sch selector channel, -1 if none + irq interrupt request flag + tplte device number template, NULL if one device number + iot I/O processing routine + ini initialization routine + + Interdata I/O uses the following interconnected tables: + + dev_tab[dev] Indexed by device number, points to the I/O instruction + processing routine for the device. + + sch_tab[dev] Indexed by device number, if non-zero, the number + 1 + of the selector channel used by the device. + + int_req[level] Indexed by interrupt level, device interrupt flags. + + int_enb[level] Indexed by interrupt level, device interrupt enable flags. + + int_tab[idx] Indexed by ((interrupt level * 32) + interrupt number), + maps bit positions in int_req to device numbers. +*/ + +#include "id_defs.h" + +/* Selector channel */ + +#define SCHC_EXA 0x40 /* read ext addr */ +#define SCHC_RD 0x20 /* read */ +#define SCHC_GO 0x10 /* go */ +#define SCHC_STOP 0x08 /* stop */ +#define SCHC_SSTA 0x04 /* sel ch status */ +#define SCHC_EXM 0x03 /* ext mem */ + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; +extern uint32 (*dev_tab[DEVNO])(uint32 dev, uint32 op, uint32 datout); +extern uint32 pawidth; +extern UNIT cpu_unit; +extern FILE *sim_log; +extern DEVICE *sim_devices[]; + +uint32 sch_max = 2; /* sch count */ +uint32 sch_sa[SCH_NUMCH] = { 0 }; /* start addr */ +uint32 sch_ea[SCH_NUMCH] = { 0 }; /* end addr */ +uint8 sch_sdv[SCH_NUMCH] = { 0 }; /* device */ +uint8 sch_cmd[SCH_NUMCH] = { 0 }; /* command */ +uint8 sch_rdp[SCH_NUMCH] = { 0 }; /* read ptr */ +uint8 sch_wdc[SCH_NUMCH] = { 0 }; /* write ctr */ +uint32 sch_tab[DEVNO] = { 0 }; /* dev to sch map */ +uint32 int_tab[INTSZ * 32] = { 0 }; /* int to dev map */ +uint8 sch_tplte[SCH_NUMCH + 1]; /* dnum template */ + +uint32 sch (uint32 dev, uint32 op, uint32 dat); +void sch_ini (t_bool dtpl); +t_stat sch_reset (DEVICE *dptr); +t_stat sch_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sch_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* Selector channel data structures + + sch_dev channel device descriptor + sch_unit channel unit descriptor + sch_mod channel modifiers list + sch_reg channel register list +*/ + +DIB sch_dib = { d_SCH, -1, v_SCH, sch_tplte, &sch, &sch_ini }; + +UNIT sch_unit = { UDATA (NULL, 0, 0) }; + +REG sch_reg[] = { + { HRDATA (CHAN, sch_max, 3), REG_HRO }, + { BRDATA (SA, sch_sa, 16, 20, SCH_NUMCH) }, + { BRDATA (EA, sch_ea, 16, 20, SCH_NUMCH) }, + { BRDATA (CMD, sch_cmd, 16, 8, SCH_NUMCH) }, + { BRDATA (DEV, sch_sdv, 16, 8, SCH_NUMCH) }, + { BRDATA (RDP, sch_rdp, 16, 2, SCH_NUMCH) }, + { BRDATA (WDC, sch_wdc, 16, 3, SCH_NUMCH) }, + { GRDATA (IREQ, int_req[l_SCH], 16, SCH_NUMCH, i_SCH) }, + { GRDATA (IENB, int_enb[l_SCH], 16, SCH_NUMCH, i_SCH) }, + { HRDATA (DEVNO, sch_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB sch_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VAL, 0, "channels", "CHANNELS", + &sch_set_nchan, NULL, &sch_reg[0] }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "0", NULL, + NULL, &sch_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "1", NULL, + NULL, &sch_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 2, "2", NULL, + NULL, &sch_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 3, "3", NULL, + NULL, &sch_show_reg, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, &sch_dib }, + { 0 } + }; + +DEVICE sch_dev = { + "SELCH", &sch_unit, sch_reg, sch_mod, + 1, 16, 8, 1, 16, 8, + NULL, NULL, &sch_reset, + NULL, NULL, NULL, + &sch_dib, 0 + }; + +/* (Extended) selector channel + + There are really three different selector channels: + - 16b selector channel (max 4B of data) + - 18b selector channel (max 4B of data) + - 20b selector channel (max 6B of data) + The algorithm for loading the start and end addresses is taken + from the maintenance manual for the Extended Selector Channel. +*/ + +#define SCH_EXR(ch) ((sch_cmd[ch] & SCHC_EXA) && (pawidth == PAWIDTH32)) + +uint32 sch (uint32 dev, uint32 op, uint32 dat) +{ +uint32 t, bank, sdv, ch = dev - sch_dib.dno; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_RD: /* read data */ + t = (sch_sa[ch] >> (sch_rdp[ch] * 8)) & DMASK8; /* get sa byte */ + if (sch_rdp[ch] == 0) sch_rdp[ch] = /* wrap? */ + SCH_EXR (ch)? 2: 1; + else sch_rdp[ch] = sch_rdp[ch] - 1; /* dec byte ptr */ + return t; + + case IO_WD: /* write data */ + if (pawidth != PAWIDTH32) { /* 16b? max 4 */ + if (sch_wdc[ch] >= 4) break; /* stop at 4 */ + sch_sa[ch] = ((sch_sa[ch] << 8) | /* ripple ea to sa */ + (sch_ea[ch] >> 8)) & DMASK16; + sch_ea[ch] = ((sch_ea[ch] << 8) | /* ripple ea low */ + dat) & DMASK16; /* insert byte */ + } + else { /* 32b? max 6 */ + if (sch_wdc[ch] >= 6) break; /* stop at 6 */ + if (sch_wdc[ch] != 5) { /* if not last */ + sch_sa[ch] = ((sch_sa[ch] << 8) | /* ripple ea<15:8> to sa */ + ((sch_ea[ch] >> 8) & DMASK8)) & PAMASK32; + sch_ea[ch] = /* ripple ea<7:0> */ + (((sch_ea[ch] & DMASK8) << 8) | dat) & PAMASK32; + } + else sch_ea[ch] = ((sch_ea[ch] << 8) | dat) & PAMASK32; + } + sch_wdc[ch] = sch_wdc[ch] + 1; /* adv sequencer */ + break; + + case IO_SS: /* status */ + if (sch_cmd[ch] & SCHC_GO) return STA_BSY; /* test busy */ + if (sch_cmd[ch] & SCHC_SSTA) return 0; /* test sch sta */ + else { + sdv = sch_sdv[ch]; /* get dev */ + if (dev_tab[sdv] == 0) return CC_V; /* not there? */ + dev_tab[sdv] (sdv, IO_ADR, 0); /* select dev */ + t = dev_tab[sdv] (sdv, IO_SS, 0); /* get status */ + return t & ~STA_BSY; /* clr busy */ + } + + case IO_OC: /* command */ + bank = 0; /* assume no bank */ + if (pawidth != PAWIDTH32) { /* 16b/18b proc? */ + dat = dat & ~(SCHC_EXA | SCHC_SSTA); /* clr ext func */ + if (pawidth == PAWIDTH16E) /* 18b proc? */ + bank = (dat & SCHC_EXM) << 16; + } + if (dat & SCHC_STOP) { /* stop? */ + sch_cmd[ch] = dat & (SCHC_EXA | SCHC_SSTA); /* clr go */ + CLR_INT (v_SCH + ch); /* clr intr */ + sch_rdp[ch] = SCH_EXR (ch)? 2: 1; /* init sequencers */ + sch_wdc[ch] = 0; + } + else if (dat & SCHC_GO) { /* go? */ + sch_cmd[ch] = dat & (SCHC_EXA | SCHC_SSTA| SCHC_GO | SCHC_RD); + if (sch_wdc[ch] <= 4) { /* 4 bytes? */ + sch_sa[ch] = (sch_sa[ch] & PAMASK16) | bank; /* 16b addr */ + sch_ea[ch] = (sch_ea[ch] & PAMASK16) | bank; + } + sch_sa[ch] = sch_sa[ch] & ~1; + if (sch_ea[ch] <= sch_sa[ch]) /* wrap? */ + sch_ea[ch] = sch_ea[ch] | /* modify end addr */ + ((pawidth == PAWIDTH32)? PAMASK32: PAMASK16); + } + break; + } + +return 0; +} + +/* CPU call to test if channel blocks access to device */ + +t_bool sch_blk (uint32 dev) +{ +uint32 ch = sch_tab[dev] - 1; + +if ((ch < sch_max) && (sch_cmd[ch] & SCHC_GO)) return TRUE; +return FALSE; +} + +/* Device call to 'remember' last dev on channel */ + +void sch_adr (uint32 ch, uint32 dev) +{ +if (ch < sch_max) sch_sdv[ch] = dev; +return; +} + +/* Device call to see if selector channel is active for device */ + +t_bool sch_actv (uint32 ch, uint32 dev) +{ +if ((ch < sch_max) && /* chan valid, */ + (sch_cmd[ch] & SCHC_GO) && /* on, and */ + (sch_sdv[ch] == dev)) return TRUE; /* set for dev? */ +return FALSE; /* no */ +} + +/* Device call to read a block of memory */ + +uint32 sch_rdmem (uint32 ch, uint8 *buf, uint32 cnt) +{ +uint32 addr, end, xfr, inc; + +if ((ch >= sch_max) || ((sch_cmd[ch] & SCHC_GO) == 0)) return 0; +addr = sch_sa[ch]; /* start */ +end = sch_ea[ch]; /* end */ +xfr = MIN (cnt, end - addr + 1); /* sch xfr cnt */ +inc = IOReadBlk (addr, xfr, buf); /* read mem */ +if ((addr + inc) > end) { /* end? */ + SET_INT (v_SCH + ch); /* interrupt */ + sch_cmd[ch] &= ~(SCHC_GO | SCHC_RD); /* clear GO */ + sch_sa[ch] = sch_sa[ch] + inc - 1; /* end addr */ + } +else sch_sa[ch] = sch_sa[ch] + inc; /* next addr */ +return inc; +} + +/* Device call to write a block of memory */ + +uint32 sch_wrmem (uint32 ch, uint8 *buf, uint32 cnt) +{ +uint32 addr, end, xfr, inc; + +if ((ch >= sch_max) || ((sch_cmd[ch] & SCHC_GO) == 0)) return 0; +addr = sch_sa[ch]; /* start */ +end = sch_ea[ch]; /* end */ +xfr = MIN (cnt, end - addr + 1); /* sch xfr cnt */ +inc = IOWriteBlk (addr, xfr, buf); /* write mem */ +if ((addr + inc) > end) { /* end? */ + SET_INT (v_SCH + ch); /* interrupt */ + sch_cmd[ch] &= ~(SCHC_GO | SCHC_RD); /* clear GO */ + sch_sa[ch] = sch_sa[ch] + inc - 1; /* end addr */ + } +else sch_sa[ch] = sch_sa[ch] + inc; /* next addr */ +return inc; +} + +/* Device call to stop a selector channel */ + +void sch_stop (uint32 ch) +{ +if (ch < sch_max) { + SET_INT (v_SCH + ch); /* interrupt */ + sch_cmd[ch] &= ~(SCHC_GO | SCHC_RD); /* clear GO */ + } +return; +} + +/* Reset */ + +void sch_reset_ch (uint32 rst_lim) +{ +uint32 ch; + +for (ch = 0; ch < SCH_NUMCH; ch++) { + if (ch >= rst_lim) { + CLR_INT (v_SCH + ch); + SET_ENB (v_SCH + ch); + sch_sa[ch] = sch_ea[ch] = 0; + sch_cmd[ch] = sch_sdv[ch] = 0; + sch_wdc[ch] = 0; + sch_rdp[ch] = 1; + } + } +return; +} + +t_stat sch_reset (DEVICE *dptr) +{ +sch_reset_ch (0); /* reset all chan */ +return SCPE_OK; +} + +/* Set number of channels */ + +t_stat sch_set_nchan (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, newmax; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newmax = get_uint (cptr, 10, SCH_NUMCH, &r); /* get new max */ +if ((r != SCPE_OK) || (newmax == sch_max)) return r; /* err or no chg? */ +if (newmax == 0) return SCPE_ARG; /* must be > 0 */ +if (newmax < sch_max) { /* reducing? */ + for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && (dibp->sch >= (int32) newmax)) { /* dev using chan? */ + printf ("Device %02X uses channel %d\n", + dibp->dno, dibp->sch); + if (sim_log) fprintf (sim_log, "Device %02X uses channel %d\n", + dibp->dno, dibp->sch); + return SCPE_OK; + } + } + } +sch_max = newmax; /* set new max */ +sch_reset_ch (sch_max); /* reset chan */ +return SCPE_OK; +} + +/* Show channel registers */ + +t_stat sch_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (val < 0) return SCPE_IERR; +if (val >= (int32) sch_max) fprintf (st, "Channel %d disabled\n", val); +else { + fprintf (st, "SA: %05X\n", sch_sa[val]); + fprintf (st, "EA: %05X\n", sch_ea[val]); + fprintf (st, "CMD: %02X\n", sch_cmd[val]); + fprintf (st, "DEV: %02X\n", sch_sdv[val]); + fprintf (st, "RDP: %X\n", sch_rdp[val]); + fprintf (st, "WDC: %X\n", sch_wdc[val]); + } +return SCPE_OK; +} + +/* Initialize template */ + +void sch_ini (t_bool dtpl) +{ +uint32 i; + +for (i = 0; i < sch_max; i++) sch_tplte[i] = i; +sch_tplte[sch_max] = TPL_END; +return; +} + +/* Evaluate interrupt */ + +void int_eval (void) +{ +int i; +extern uint32 qevent; + +for (i = 0; i < INTSZ; i++) { + if (int_req[i] & int_enb[i]) { + qevent = qevent | EV_INT; + return; + } + } +qevent = qevent & ~EV_INT; +return; +} + +/* Return interrupting device */ + +uint32 int_getdev (void) +{ +int32 i, j, t; +uint32 r; + +for (i = t = 0; i < INTSZ; i++) { /* loop thru array */ + if (r = int_req[i] & int_enb[i]) { /* find nz int wd */ + for (j = 0; j < 32; t++, j++) { + if (r & (1u << j)) { + int_req[i] = int_req[i] & ~(1u << j); /* clr request */ + return int_tab[t]; + } + } + } + else t = t + 32; + } +return 0; +} + +/* Update device interrupt status */ + +int32 int_chg (uint32 irq, int32 dat, int32 armdis) +{ +int32 t = CMD_GETINT (dat); /* get int ctrl */ + +if (t == CMD_IENB) { /* enable? */ + SET_ENB (irq); + return 1; + } +else if (t == CMD_IDIS) { /* disable? */ + CLR_ENB (irq); + return 1; + } +if (t == CMD_IDSA) { /* disarm? */ + CLR_ENB (irq); + CLR_INT (irq); + return 0; + } +return armdis; +} + +/* Process a 2b field and return unchanged, set, clear, complement */ + +int32 io_2b (int32 val, int32 pos, int32 old) +{ +int32 t = (val >> pos) & 3; +if (t == 0) return old; +if (t == 1) return 1; +if (t == 2) return 0; +return old ^1; +} + +/* Block transfer routines */ + +uint32 IOReadBlk (uint32 loc, uint32 cnt, uint8 *buf) +{ +uint32 i; + +if (!MEM_ADDR_OK (loc) || (cnt == 0)) return 0; +if (!MEM_ADDR_OK (loc + cnt - 1)) cnt = MEMSIZE - loc; +for (i = 0; i < cnt; i++) buf[i] = IOReadB (loc + i); +return cnt; +} + +uint32 IOWriteBlk (uint32 loc, uint32 cnt, uint8 *buf) +{ +uint32 i; + +if (!MEM_ADDR_OK (loc) || (cnt == 0)) return 0; +if (!MEM_ADDR_OK (loc + cnt - 1)) cnt = MEMSIZE - loc; +for (i = 0; i < cnt; i++) IOWriteB (loc + i, buf[i]); +return cnt; +} + +/* Change selector channel for a device */ + +t_stat set_sch (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newch; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if ((dibp == NULL) || (dibp->sch < 0)) return SCPE_IERR; +newch = get_uint (cptr, 16, sch_max - 1, &r); /* get new */ +if (r != SCPE_OK) return r; +dibp->sch = newch; /* store */ +return SCPE_OK; +} + +/* Show selector channel for a device */ + +t_stat show_sch (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if ((dibp == NULL) || (dibp->sch < 0)) return SCPE_IERR; +fprintf (st, "selch=%X", dibp->sch); +return SCPE_OK; +} + +/* Change device number for a device */ + +t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newdev; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newdev = get_uint (cptr, 16, DEV_MAX, &r); /* get new */ +if ((r != SCPE_OK) || (newdev == dibp->dno)) return r; +if (newdev == 0) return SCPE_ARG; /* must be > 0 */ +dibp->dno = newdev; /* store */ +return SCPE_OK; +} + +/* Show device number for a device */ + +t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if ((dibp == NULL) || (dibp->dno == 0)) return SCPE_IERR; +fprintf (st, "devno=%02X", dibp->dno); +return SCPE_OK; +} + +/* Init device tables */ + +t_bool devtab_init (void) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, j, dno, dmsk, doff, t, dmap[DEVNO / 32]; +uint8 *tplte, dflt_tplte[] = { 0, TPL_END }; + +/* Clear tables, device map */ + +for (i = 0; i < DEVNO; i++) { + dev_tab[i] = NULL; + sch_tab[i] = 0; + } +for (i = 0; i < (INTSZ * 32); i++) int_tab[i] = 0; +for (i = 0; i < (DEVNO / 32); i++) dmap[i] = 0; + +/* Test each device for conflict; add to map; init tables */ + +for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if ((dibp == NULL) || (dptr->flags & DEV_DIS)) continue; /* exist, enabled? */ + dno = dibp->dno; /* get device num */ + if (dibp->ini) dibp->ini (TRUE); /* gen dno template */ + tplte = dibp->tplte; /* get template */ + if (tplte == NULL) tplte = dflt_tplte; /* none? use default */ + for ( ; *tplte != TPL_END; tplte++) { /* loop thru template */ + t = (dno + *tplte) & DEV_MAX; /* loop thru template */ + dmsk = 1u << (t & 0x1F); /* bit to test */ + doff = t / 32; /* word to test */ + if (dmap[doff] & dmsk) { /* in use? */ + printf ("Device number conflict, devno = %02X\n", t); + if (sim_log) fprintf (sim_log, + "Device number conflict, devno = %02X\n", t); + return TRUE; + } + dmap[doff] = dmap[doff] | dmsk; + if (dibp->sch >= 0) sch_tab[t] = dibp->sch + 1; + dev_tab[t] = dibp->iot; + } + if (dibp->ini) dibp->ini (FALSE); /* gen int template */ + tplte = dibp->tplte; /* get template */ + if (tplte == NULL) tplte = dflt_tplte; /* none? use default */ + for (j = dibp->irq; *tplte != TPL_END; j++, tplte++) + int_tab[j] = (dno + *tplte) & DEV_MAX; + } /* end for i */ +return FALSE; +} diff --git a/Interdata/id_lp.c b/Interdata/id_lp.c new file mode 100644 index 0000000..cb74d43 --- /dev/null +++ b/Interdata/id_lp.c @@ -0,0 +1,317 @@ +/* id_lp.c: Interdata line printer + + Copyright (c) 2001-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt M46-206 line printer + + 27-May-08 RMS Fixed bug in printing test (from Davis Johnson) + 19-Jan-07 RMS Added UNIT_TEXT flag + 25-Apr-03 RMS Revised for extended file support +*/ + +#include "id_defs.h" +#include + +/* Device definitions */ + +#define UNIT_V_UC (UNIT_V_UF + 0) /* UC only */ +#define UNIT_UC (1 << UNIT_V_UC) +#define SPC_BASE 0x40 /* spacing base */ +#define VFU_BASE 0x78 /* VFU base */ +#define VFU_WIDTH 0x8 /* VFU width */ +#define LF 0xA +#define VT 0xB +#define VT_VFU 4 /* VFU chan for VT */ +#define FF 0xC +#define FF_VFU 8 /* VFU chan for FF */ +#define CR 0xD +#define VFUP(ch,val) ((val) & (1 << (ch))) /* VFU chan test */ + +/* Status byte, * = dynamic */ + +#define STA_PAPE 0x40 /* *paper empty */ +#define STA_MASK (STA_BSY) /* static status */ + +uint32 lpt_sta = STA_BSY; /* status */ +char lpxb[LPT_WIDTH + 1]; /* line buffer */ +uint32 lpt_bptr = 0; /* buf ptr */ +uint32 lpt_spnd = 0; /* space pending */ +uint32 lpt_vfup = 0; /* VFU ptr */ +uint32 lpt_vful = 1; /* VFU lnt */ +uint8 lpt_vfut[VFU_LNT] = { 0xFF }; /* VFU tape */ +uint32 lpt_arm = 0; /* int armed */ +int32 lpt_ctime = 10; /* char time */ +int32 lpt_stime = 1000; /* space time */ +int32 lpt_stopioe = 0; /* stop on err */ + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; + +DEVICE lpt_dev; +uint32 lpt (uint32 dev, uint32 op, uint32 dat); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat lpt_bufout (UNIT *uptr); +t_stat lpt_vfu (UNIT *uptr, int32 ch); +t_stat lpt_spc (UNIT *uptr, int32 cnt); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptors + lpt_reg LPT register list +*/ + +DIB lpt_dib = { d_LPT, -1, v_LPT, NULL, &lpt, NULL }; + +UNIT lpt_unit = { UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_UC+UNIT_TEXT, 0) }; + +REG lpt_reg[] = { + { HRDATA (STA, lpt_sta, 8) }, + { HRDATA (BUF, lpt_unit.buf, 7) }, + { BRDATA (DBUF, lpxb, 16, 7, LPT_WIDTH) }, + { HRDATA (DBPTR, lpt_bptr, 8) }, + { HRDATA (VFUP, lpt_vfup, 8) }, + { HRDATA (VFUL, lpt_vful, 8) }, + { BRDATA (VFUT, lpt_vfut, 16, 8, VFU_LNT) }, + { FLDATA (IREQ, int_req[l_LPT], i_LPT) }, + { FLDATA (IENB, int_enb[l_LPT], i_LPT) }, + { FLDATA (IARM, lpt_arm, 0) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (CTIME, lpt_ctime, 24), PV_LEFT }, + { DRDATA (STIME, lpt_stime, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { HRDATA (DEVNO, lpt_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB lpt_mod[] = { + { UNIT_UC, 0, "lower case", "LC", NULL }, + { UNIT_UC, UNIT_UC, "upper case", "UC", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 16, 7, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, NULL, + &lpt_dib, DEV_DISABLE + }; + +/* Line printer: IO routine */ + +uint32 lpt (uint32 dev, uint32 op, uint32 dat) +{ +int32 t; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_OC: /* command */ + lpt_arm = int_chg (v_LPT, dat, lpt_arm); /* upd int ctrl */ + break; + + case IO_WD: /* write */ + t = lpt_unit.buf = dat & 0x7F; /* mask char */ + lpt_sta = STA_BSY; /* set busy */ + if (lpt_spnd || ((t >= LF) && (t <= CR))) /* space op? */ + sim_activate (&lpt_unit, lpt_stime); + else sim_activate (&lpt_unit, lpt_ctime); /* normal char */ + break; + + case IO_SS: /* status */ + t = lpt_sta & STA_MASK; /* status byte */ + if ((lpt_unit.flags & UNIT_ATT) == 0) /* test paper out */ + t = t | STA_EX | STA_PAPE | STA_BSY; + return t; + } + +return 0; +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 t; +t_stat r = SCPE_OK; + +lpt_sta = 0; /* clear busy */ +if (lpt_arm) SET_INT (v_LPT); /* armed? intr */ +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); +t = uptr->buf; /* get character */ +if (lpt_spnd || ((t >= LF) && (t < CR))) { /* spc pend or spc op? */ + lpt_spnd = 0; + if (lpt_bufout (uptr) != SCPE_OK) /* print */ + return SCPE_IOERR; + if ((t == 1) || (t == LF)) lpt_spc (uptr, 1); /* single space */ + else if (t == VT) r = lpt_vfu (uptr, VT_VFU - 1); /* VT->VFU */ + else if (t == 0xC) r = lpt_vfu (uptr, FF_VFU - 1); /* FF->VFU */ + else if ((t >= SPC_BASE) && (t < VFU_BASE)) + lpt_spc (uptr, t - SPC_BASE); /* space */ + else if ((t >= VFU_BASE) && (t < VFU_BASE + VFU_WIDTH)) + r = lpt_vfu (uptr, t - VFU_BASE); /* VFU */ + else fputs ("\r", uptr->fileref); /* overprint */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (lpt_unit.fileref)) { + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + } +else if (t == CR) { /* CR? */ + lpt_spnd = 1; /* set spc pend */ + return lpt_bufout (uptr); /* print line */ + } +else if (t >= 0x20) { /* printable? */ + if ((uptr->flags & UNIT_UC) && islower (t)) /* UC only? */ + t = toupper (t); + if (lpt_bptr < LPT_WIDTH) lpxb[lpt_bptr++] = t; + } +return r; +} + +/* Printing and spacing routines */ + +t_stat lpt_bufout (UNIT *uptr) +{ +int32 i; +t_stat r = SCPE_OK; + +if (lpt_bptr == 0) return SCPE_OK; /* any char in buf? */ +for (i = LPT_WIDTH - 1; (i >= 0) && (lpxb[i] == ' '); i--) + lpxb[i] = 0; /* backscan line */ +if (lpxb[0]) { /* any char left? */ + fputs (lpxb, uptr->fileref); /* write line */ + lpt_unit.pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { + perror ("LPT I/O error"); + clearerr (uptr->fileref); + r = SCPE_IOERR; + } + } +lpt_bptr = 0; /* reset buffer */ +for (i = 0; i < LPT_WIDTH; i++) lpxb[i] = ' '; +lpxb[LPT_WIDTH] = 0; +return r; +} + +t_stat lpt_vfu (UNIT *uptr, int32 ch) +{ +uint32 i, j; + +if ((ch == (FF_VFU - 1)) && VFUP (ch, lpt_vfut[0])) { /* top of form? */ + fputs ("\n\f", uptr->fileref); /* nl + ff */ + lpt_vfup = 0; /* top of page */ + return SCPE_OK; + } +for (i = 1; i < lpt_vful + 1; i++) { /* sweep thru cct */ + lpt_vfup = (lpt_vfup + 1) % lpt_vful; /* adv pointer */ + if (VFUP (ch, lpt_vfut[lpt_vfup])) { /* chan punched? */ + for (j = 0; j < i; j++) fputc ('\n', uptr->fileref); + return SCPE_OK; + } + } +return STOP_VFU; /* runaway channel */ +} + +t_stat lpt_spc (UNIT *uptr, int32 cnt) +{ +int32 i; + +if (cnt == 0) fputc ('\r', uptr->fileref); +else { + for (i = 0; i < cnt; i++) fputc ('\n', uptr->fileref); + lpt_vfup = (lpt_vfup + cnt) % lpt_vful; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +int32 i; + +sim_cancel (&lpt_unit); /* deactivate */ +lpt_sta = 0; /* clr busy */ +lpt_bptr = 0; /* clr buf ptr */ +for (i = 0; i < LPT_WIDTH; i++) lpxb[i] = ' '; /* clr buf */ +lpxb[LPT_WIDTH] = 0; +CLR_INT (v_LPT); /* clearr int */ +CLR_ENB (v_LPT); /* disable int */ +lpt_arm = 0; /* disarm int */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +lpt_vfup = 0; /* top of form */ +return attach_unit (uptr, cptr); +} + +/* Carriage control load routine */ + +t_stat lp_load (FILE *fileref, char *cptr, char *fnam) +{ +int32 col, ptr, mask, vfubuf[VFU_LNT]; +uint32 rpt; +t_stat r; +char cbuf[CBUFSIZE], gbuf[CBUFSIZE]; + +if (*cptr != 0) return SCPE_ARG; +ptr = 0; +for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ + mask = 0; + if (*cptr == '(') { /* repeat count? */ + cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ + rpt = get_uint (gbuf, 10, VFU_LNT, &r); /* repeat count */ + if (r != SCPE_OK) return SCPE_FMT; + } + else rpt = 1; + while (*cptr != 0) { /* get col no's */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + col = get_uint (gbuf, 10, 7, &r); /* column number */ + if (r != SCPE_OK) return SCPE_FMT; + mask = mask | (1 << col); /* set bit */ + } + for ( ; rpt > 0; rpt--) { /* store vals */ + if (ptr >= VFU_LNT) return SCPE_FMT; + vfubuf[ptr++] = mask; + } + } +if (ptr == 0) return SCPE_FMT; +lpt_vful = ptr; +lpt_vfup = 0; +for (rpt = 0; rpt < lpt_vful; rpt++) lpt_vfut[rpt] = vfubuf[rpt]; +return SCPE_OK; +} diff --git a/Interdata/id_mt.c b/Interdata/id_mt.c new file mode 100644 index 0000000..d08ccd1 --- /dev/null +++ b/Interdata/id_mt.c @@ -0,0 +1,523 @@ +/* id_mt.c: Interdata magnetic tape simulator + + Copyright (c) 2001-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt M46-494 dual density 9-track magtape controller + + 16-Feb-06 RMS Added tape capacity checking + 18-Mar-05 RMS Added attached test to detach routine + 07-Dec-04 RMS Added read-only file support + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised for magtape library + 20-Feb-03 RMS Fixed read to stop selch on error + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. +*/ + +#include "id_defs.h" +#include "sim_tape.h" + +#define UST u3 /* unit status */ +#define UCMD u4 /* unit command */ +#define MT_MAXFR (1 << 24) /* max transfer */ + +/* Command - in UCMD */ + +#define MTC_SPCR 0x11 /* backspace */ +#define MTC_SKFR 0x13 /* space file rev */ +#define MTC_CLR 0x20 /* clear */ +#define MTC_RD 0x21 /* read */ +#define MTC_WR 0x22 /* write */ +#define MTC_SKFF 0x23 /* space file fwd */ +#define MTC_WEOF 0x30 /* write eof */ +#define MTC_REW 0x38 /* rewind */ +#define MTC_MASK 0x3F +#define MTC_STOP1 0x40 /* stop, set EOM */ +#define MTC_STOP2 0x80 /* stop, set NMTN */ + +/* Status byte, * = in UST */ + +#define STA_ERR 0x80 /* error */ +#define STA_EOF 0x40 /* end of file */ +#define STA_EOT 0x20 /* *end of tape */ +#define STA_NMTN 0x10 /* *no motion */ +#define STA_UFLGS (STA_EOT|STA_NMTN) /* unit flags */ +#define STA_MASK (STA_ERR|STA_EOF|STA_BSY|STA_EOM) +#define SET_EX (STA_ERR|STA_EOF|STA_NMTN) + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; + +uint8 mtxb[MT_MAXFR]; /* xfer buffer */ +uint32 mt_bptr = 0; /* pointer */ +uint32 mt_blnt = 0; /* length */ +uint32 mt_sta = 0; /* status byte */ +uint32 mt_db = 0; /* data buffer */ +uint32 mt_xfr = 0; /* data xfr in prog */ +uint32 mt_arm[MT_NUMDR] = { 0 }; /* intr armed */ +int32 mt_wtime = 10; /* byte latency */ +int32 mt_rtime = 1000; /* record latency */ +int32 mt_stopioe = 1; /* stop on error */ +uint8 mt_tplte[] = { 0, o_MT0, o_MT0*2, o_MT0*3, TPL_END }; + +static const uint8 bad_cmd[64] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 + }; + +DEVICE mt_dev; +uint32 mt (uint32 dev, uint32 op, uint32 dat); +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_detach (UNIT *uptr); +t_stat mt_boot (int32 unitno, DEVICE *dptr); +t_stat mt_map_err (UNIT *uptr, t_stat st); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +DIB mt_dib = { d_MT, 0, v_MT, mt_tplte, &mt, NULL }; + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) } + }; + +REG mt_reg[] = { + { HRDATA (STA, mt_sta, 8) }, + { HRDATA (BUF, mt_db, 8) }, + { BRDATA (DBUF, mtxb, 16, 8, MT_MAXFR) }, + { HRDATA (DBPTR, mt_bptr, 16) }, + { HRDATA (DBLNT, mt_blnt, 17), REG_RO }, + { FLDATA (XFR, mt_xfr, 0) }, + { GRDATA (IREQ, int_req[l_MT], 16, MT_NUMDR, i_MT) }, + { GRDATA (IENB, int_enb[l_MT], 16, MT_NUMDR, i_MT) }, + { BRDATA (IARM, mt_arm, 16, 1, MT_NUMDR) }, + { FLDATA (STOP_IOE, mt_stopioe, 0) }, + { DRDATA (WTIME, mt_wtime, 24), PV_LEFT + REG_NZ }, + { DRDATA (RTIME, mt_rtime, 24), PV_LEFT + REG_NZ }, + { URDATA (UST, mt_unit[0].UST, 16, 8, 0, MT_NUMDR, 0) }, + { URDATA (CMD, mt_unit[0].UCMD, 16, 8, 0, MT_NUMDR, 0) }, + { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR, PV_LEFT | REG_RO) }, + { HRDATA (DEVNO, mt_dib.dno, 8), REG_HRO }, + { HRDATA (SELCH, mt_dib.sch, 1), REG_HRO }, + { NULL } + }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "SELCH", "SELCH", + &set_sch, &show_sch, NULL }, + { 0 } + }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 16, 8, + NULL, NULL, &mt_reset, + &mt_boot, &mt_attach, &mt_detach, + &mt_dib, DEV_DISABLE + }; + +/* Magtape: IO routine */ + +uint32 mt (uint32 dev, uint32 op, uint32 dat) +{ +uint32 i, f, t; +uint32 u = (dev - mt_dib.dno) / o_MT0; +UNIT *uptr = mt_dev.units + u; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + sch_adr (mt_dib.sch, dev); /* inform sel ch */ + return BY; /* byte only */ + + case IO_RD: /* read data */ + if (mt_xfr) mt_sta = mt_sta | STA_BSY; /* xfr? set busy */ + return mt_db; /* return data */ + + case IO_WD: /* write data */ + if (mt_xfr) { /* transfer? */ + mt_sta = mt_sta | STA_BSY; /* set busy */ + if ((uptr->UCMD & (MTC_STOP1 | MTC_STOP2)) && + ((uptr->UCMD & MTC_MASK) == MTC_WR)) /* while stopping? */ + mt_sta = mt_sta | STA_ERR; /* write overrun */ + } + mt_db = dat & DMASK8; /* store data */ + break; + + case IO_SS: /* status */ + mt_sta = mt_sta & STA_MASK; /* ctrl status */ + if (uptr->flags & UNIT_ATT) /* attached? */ + t = mt_sta | (uptr->UST & STA_UFLGS); /* yes, unit status */ + else t = mt_sta | STA_DU; /* no, dev unavail */ + if (t & SET_EX) t = t | STA_EX; /* test for ex */ + return t; + + case IO_OC: /* command */ + mt_arm[u] = int_chg (v_MT + u, dat, mt_arm[u]); + f = dat & MTC_MASK; /* get cmd */ + if (f == MTC_CLR) { /* clear? */ + mt_reset (&mt_dev); /* reset world */ + break; + } + if (((uptr->flags & UNIT_ATT) == 0) || /* ignore if unatt */ + bad_cmd[f] || /* or bad cmd */ + (((f == MTC_WR) || (f == MTC_WEOF)) && /* or write */ + sim_tape_wrp (uptr))) break; /* and protected */ + for (i = 0; i < MT_NUMDR; i++) { /* check other drvs */ + if (sim_is_active (&mt_unit[i]) && /* active? */ + (mt_unit[i].UCMD != MTC_REW)) { /* not rewind? */ + sim_cancel (&mt_unit[i]); /* stop */ + mt_unit[i].UCMD = 0; + } + } + if (sim_is_active (uptr) && /* unit active? */ + !(uptr->UCMD & (MTC_STOP1 | MTC_STOP2))) /* not stopping? */ + break; /* ignore */ + if ((f == MTC_WR) || (f == MTC_REW)) mt_sta = 0;/* write, rew: bsy=0 */ + else mt_sta = STA_BSY; /* bsy=1,nmtn,eom,err=0 */ + mt_bptr = mt_blnt = 0; /* not yet started */ + if ((f == MTC_RD) || (f == MTC_WR)) /* data xfr? */ + mt_xfr = 1; /* set xfr flag */ + else mt_xfr = 0; + uptr->UCMD = f; /* save cmd */ + uptr->UST = 0; /* clr tape stat */ + sim_activate (uptr, mt_rtime); /* start op */ + break; + } + +return 0; +} + +/* Unit service + + A given operation can generate up to three interrupts + + - EOF generates an interrupt when set (read, space, wreof) + BUSY will still be set, EOM and NMTN will be clear + - After operation complete + delay, EOM generates an interrupt + BUSY will be clear, EOM will be set, NMTN will be clear + - After a further delay, NMTN generates an interrupt + BUSY will be clear, EOM and NMTN will be set + + Rewind generates an interrupt when NMTN sets +*/ + +t_stat mt_svc (UNIT *uptr) +{ +uint32 i; +int32 u = uptr - mt_dev.units; +uint32 dev = mt_dib.dno + (u * o_MT0); +t_mtrlnt tbc; +t_bool passed_eot; +t_stat st, r = SCPE_OK; + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + uptr->UCMD = 0; /* clr cmd */ + uptr->UST = 0; /* set status */ + mt_xfr = 0; /* clr op flags */ + mt_sta = STA_ERR | STA_EOM; /* set status */ + if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */ + return IORETURN (mt_stopioe, SCPE_UNATT); + } + +if (uptr->UCMD & MTC_STOP2) { /* stop, gen NMTN? */ + uptr->UCMD = 0; /* clr cmd */ + uptr->UST = uptr->UST | STA_NMTN; /* set nmtn */ + mt_xfr = 0; /* clr xfr */ + if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */ + return SCPE_OK; + } + +if (uptr->UCMD & MTC_STOP1) { /* stop, gen EOM? */ + uptr->UCMD = uptr->UCMD | MTC_STOP2; /* clr cmd */ + mt_sta = (mt_sta & ~STA_BSY) | STA_EOM; /* clr busy, set eom */ + if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */ + sim_activate (uptr, mt_rtime); /* schedule */ + return SCPE_OK; + } + +passed_eot = sim_tape_eot (uptr); /* passed EOT? */ +switch (uptr->UCMD) { /* case on function */ + + case MTC_REW: /* rewind */ + sim_tape_rewind (uptr); /* reposition */ + uptr->UCMD = 0; /* clr cmd */ + uptr->UST = STA_NMTN | STA_EOT; /* update status */ + mt_sta = mt_sta & ~STA_BSY; /* don't set EOM */ + if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */ + return SCPE_OK; + +/* For read, busy = 1 => buffer empty + For write, busy = 1 => buffer full + For read, data transfers continue for the full length of the + record, or the maximum size of the transfer buffer + For write, data transfers continue until a write is attempted + and the buffer is empty +*/ + + case MTC_RD: /* read */ + if (mt_blnt == 0) { /* first time? */ + st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */ + if (st == MTSE_RECE) mt_sta = mt_sta | STA_ERR; /* rec in err? */ + else if (st != SCPE_OK) { /* other error? */ + r = mt_map_err (uptr, st); /* map error */ + if (sch_actv (mt_dib.sch, dev)) /* if sch, stop */ + sch_stop (mt_dib.sch); + break; + } + mt_blnt = tbc; /* set buf lnt */ + } + + if (sch_actv (mt_dib.sch, dev)) { /* sch active? */ + i = sch_wrmem (mt_dib.sch, mtxb, mt_blnt); /* store rec in mem */ + if (sch_actv (mt_dib.sch, dev)) /* sch still active? */ + sch_stop (mt_dib.sch); /* stop chan, long rd */ + else if (i < mt_blnt) /* process entire rec? */ + mt_sta = mt_sta | STA_ERR; /* no, overrun error */ + } + else if (mt_bptr < mt_blnt) { /* no, if !eor */ + if (!(mt_sta & STA_BSY)) /* busy still clr? */ + mt_sta = mt_sta | STA_ERR; /* read overrun */ + mt_db = mtxb[mt_bptr++]; /* get next byte */ + mt_sta = mt_sta & ~STA_BSY; /* !busy = buf full */ + if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */ + sim_activate (uptr, mt_wtime); /* reschedule */ + return SCPE_OK; + } + break; /* record done */ + + case MTC_WR: /* write */ + if (sch_actv (mt_dib.sch, dev)) { /* sch active? */ + mt_bptr = sch_rdmem (mt_dib.sch, mtxb, MT_MAXFR); /* get rec */ + if (sch_actv (mt_dib.sch, dev)) /* not done? */ + sch_stop (mt_dib.sch); /* stop chan */ + } + else if (mt_sta & STA_BSY) { /* no, if !eor */ + if (mt_bptr < MT_MAXFR) /* if room */ + mtxb[mt_bptr++] = mt_db; /* store in buf */ + mt_sta = mt_sta & ~STA_BSY; /* !busy = buf emp */ + if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */ + sim_activate (uptr, mt_wtime); /* reschedule */ + return SCPE_OK; + } + + if (mt_bptr) { /* any chars? */ + if (st = sim_tape_wrrecf (uptr, mtxb, mt_bptr)) /* write, err? */ + r = mt_map_err (uptr, st); /* map error */ + } + break; /* record done */ + + case MTC_WEOF: /* write eof */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = mt_map_err (uptr, st); /* map error */ + mt_sta = mt_sta | STA_EOF; /* set eof */ + if (mt_arm[u]) SET_INT (v_MT + u); /* interrupt */ + break; + + case MTC_SKFF: /* skip file fwd */ + while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; + if (st == MTSE_TMK) { /* stopped by tmk? */ + mt_sta = mt_sta | STA_EOF; /* set eof */ + if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */ + } + else r = mt_map_err (uptr, st); /* map error */ + break; + + case MTC_SKFR: /* skip file rev */ + while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; + if (st == MTSE_TMK) { /* stopped by tmk? */ + mt_sta = mt_sta | STA_EOF; /* set eof */ + if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */ + } + else r = mt_map_err (uptr, st); /* map error */ + break; + + case MTC_SPCR: /* backspace */ + if (st = sim_tape_sprecr (uptr, &tbc)) /* skip rec rev, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; + } /* end case */ + +if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ + uptr->UST = uptr->UST | STA_EOT; +uptr->UCMD = uptr->UCMD | MTC_STOP1; /* set stop stage 1 */ +sim_activate (uptr, mt_rtime); /* schedule */ +return r; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +int32 u = uptr - mt_dev.units; + +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* not attached */ + mt_sta = mt_sta | STA_ERR; + case MTSE_OK: /* no error */ + return SCPE_IERR; + + case MTSE_TMK: /* end of file */ + mt_sta = mt_sta | STA_EOF; /* set eof */ + if (mt_arm[u]) SET_INT (v_MT + u); /* set intr */ + break; + + case MTSE_IOERR: /* IO error */ + mt_sta = mt_sta | STA_ERR; /* set err */ + if (mt_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + mt_sta = mt_sta | STA_ERR; + return SCPE_MTRLNT; + + case MTSE_WRP: /* write protect */ + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + mt_sta = mt_sta | STA_ERR; /* set err */ + break; + + case MTSE_BOT: /* reverse into BOT */ + uptr->UST = uptr->UST | STA_EOT; /* set err */ + break; + } /* end switch */ + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +uint32 u; +UNIT *uptr; + +mt_bptr = mt_blnt = 0; /* clr buf */ +mt_sta = STA_BSY; /* clr flags */ +mt_xfr = 0; /* clr controls */ +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + CLR_INT (v_MT + u); /* clear int */ + CLR_ENB (v_MT + u); /* disable int */ + mt_arm[u] = 0; /* disarm int */ + uptr = mt_dev.units + u; + sim_tape_reset (uptr); /* clear pos flag */ + sim_cancel (uptr); /* cancel activity */ + uptr->UST = (uptr->UST & STA_UFLGS) | STA_NMTN; /* init status */ + uptr->UCMD = 0; /* init cmd */ + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +int32 u = uptr - mt_dev.units; +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->UST = STA_EOT; +if (mt_arm[u]) SET_INT (v_MT + u); +return r; +} + +/* Detach routine */ + +t_stat mt_detach (UNIT* uptr) +{ +int32 u = uptr - mt_dev.units; +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; +r = sim_tape_detach (uptr); +if (r != SCPE_OK) return r; +if (mt_arm[u]) SET_INT (v_MT + u); +uptr->UST = 0; +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 0x50 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) + +static uint8 boot_rom[] = { + 0xD5, 0x00, 0x00, 0xCF, /* ST: AL CF */ + 0x43, 0x00, 0x00, 0x80 /* BR 80 */ + }; + +t_stat mt_boot (int32 unitno, DEVICE *dptr) +{ +extern uint32 PC, dec_flgs; +extern uint16 decrom[]; +extern DIB sch_dib; +uint32 sch_dev; + +if (decrom[0xD5] & dec_flgs) return SCPE_NOFNC; /* AL defined? */ +sim_tape_rewind (&mt_unit[unitno]); /* rewind */ +sch_dev = sch_dib.dno + mt_dib.sch; /* sch dev # */ +IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy boot */ +IOWriteB (AL_DEV, mt_dib.dno + (unitno * o_MT0)); /* set dev no for unit */ +IOWriteB (AL_IOC, 0xA1); /* set dev cmd */ +IOWriteB (AL_SCH, sch_dev); /* set dev no for chan */ +PC = BOOT_START; +return SCPE_OK; +} diff --git a/Interdata/id_pas.c b/Interdata/id_pas.c new file mode 100644 index 0000000..ae6b709 --- /dev/null +++ b/Interdata/id_pas.c @@ -0,0 +1,590 @@ +/* id_pas.c: Interdata programmable async line adapter simulator + + Copyright (c) 2001-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + pas Programmable asynchronous line adapter(s) + + 18-Jun-07 RMS Added UNIT_IDLE flag + 18-Oct-06 RMS Synced PASLA to clock + 22-Nov-05 RMS Revised for new terminal processing routines + 29-Jun-05 RMS Added SET PASLn DISCONNECT + 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS + 05-Jan-04 RMS Revised for tmxr library changes + 09-May-03 RMS Added network device flag + + This module implements up to 32 individual serial interfaces, representing + either individual PASLA modules or combinations of the 2-line and 8-line + multiplexors, which are functionally very similar. These interfaces are mapped + to Telnet based connections as the lines of a terminal multiplexor. The + connection polling mechanism and the character input polling for all lines + are done through a single polling job. +*/ + +#include "id_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define PAS_LINES 32 + +#define UNIT_V_MDM (TTUF_V_UF + 0) /* modem control */ +#define UNIT_MDM (1 << UNIT_V_MDM) + +#define PASL_WAIT 500 + +/* Status byte */ + +#define STA_OVR 0x80 /* overrun RO */ +#define STA_PF 0x40 /* parity err RONI */ +#define STA_NCL2S 0x40 /* not clr to snd XO */ +#define STA_FR 0x20 /* framing err RO */ +#define STA_RCR 0x10 /* rv chan rcv NI */ +#define STA_CROF 0x02 /* carrier off RO */ +#define STA_RING 0x01 /* ring RO */ +#define STA_RCV (STA_OVR|STA_PF|STA_FR|STA_RCR|STA_CROF|STA_RING) +#define SET_EX (STA_OVR|STA_PF|STA_FR) +#define STA_XMT (STA_BSY) + +/* Command bytes 1,0 */ + +#define CMD_DTR (0x20 << 8) /* DTR */ +#define CMD_ECHO (0x10 << 8) /* echoplex */ +#define CMD_RCT (0x08 << 8) /* RCT/DTB NI */ +#define CMD_XMTB (0x04 << 8) /* xmt break NI */ +#define CMD_WRT (0x02 << 8) /* write/read */ +#define CMD_V_CLK 6 /* baud rate */ +#define CMD_M_CLK 0x3 +#define CMD_V_DB 4 /* data bits */ +#define CMD_M_DB 0x3 +#define CMD_STOP 0x80 /* stop bit */ +#define CMD_V_PAR 1 /* parity */ +#define CMD_M_PAR 0x3 +#define GET_PAR(x) (((x) >> CMD_V_PAR) & CMD_M_PAR) +#define PAR_NONE 0 +#define PAR_RAW 1 +#define PAR_ODD 2 +#define PAR_EVEN 3 + +#define CMD_TYP 0x01 /* command type */ + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; +extern int32 lfc_poll; + +uint8 pas_sta[PAS_LINES]; /* status */ +uint16 pas_cmd[PAS_LINES]; /* command */ +uint8 pas_rbuf[PAS_LINES]; /* rcv buf */ +uint8 pas_xbuf[PAS_LINES]; /* xmt buf */ +uint8 pas_rarm[PAS_LINES]; /* rcvr int armed */ +uint8 pas_xarm[PAS_LINES]; /* xmt int armed */ +uint8 pas_rchp[PAS_LINES]; /* rcvr chr pend */ +uint8 pas_tplte[PAS_LINES * 2 + 1]; /* template */ + +TMLN pas_ldsc[PAS_LINES] = { 0 }; /* line descriptors */ +TMXR pas_desc = { 8, 0, 0, pas_ldsc }; /* mux descriptor */ +#define PAS_ENAB pas_desc.lines + +uint32 pas (uint32 dev, uint32 op, uint32 dat); +void pas_ini (t_bool dtpl); +t_stat pasi_svc (UNIT *uptr); +t_stat paso_svc (UNIT *uptr); +t_stat pas_reset (DEVICE *dptr); +t_stat pas_attach (UNIT *uptr, char *cptr); +t_stat pas_detach (UNIT *uptr); +t_stat pas_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat pas_show (FILE *st, UNIT *uptr, int32 val, void *desc); +int32 pas_par (int32 cmd, int32 c); +t_stat pas_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); +void pas_reset_ln (int32 i); + +/* PAS data structures + + pas_dev PAS device descriptor + pas_unit PAS unit descriptor + pas_reg PAS register list + pas_mod PAS modifiers list +*/ + +DIB pas_dib = { d_PAS, -1, v_PAS, pas_tplte, &pas, &pas_ini }; + +UNIT pas_unit = { UDATA (&pasi_svc, UNIT_ATTABLE|UNIT_IDLE, 0), 0 }; + +REG pas_nlreg = { DRDATA (NLINES, PAS_ENAB, 6), PV_LEFT }; + +REG pas_reg[] = { + { BRDATA (STA, pas_sta, 16, 8, PAS_LINES) }, + { BRDATA (CMD, pas_cmd, 16, 16, PAS_LINES) }, + { BRDATA (RBUF, pas_rbuf, 16, 8, PAS_LINES) }, + { BRDATA (XBUF, pas_xbuf, 16, 8, PAS_LINES) }, + { BRDATA (IREQ, &int_req[l_PAS], 16, 32, PAS_LINES / 16) }, + { BRDATA (IENB, &int_enb[l_PAS], 16, 32, PAS_LINES / 16) }, + { BRDATA (RARM, pas_rarm, 16, 1, PAS_LINES) }, + { BRDATA (XARM, pas_xarm, 16, 1, PAS_LINES) }, + { BRDATA (RCHP, pas_rchp, 16, 1, PAS_LINES) }, + { HRDATA (DEVNO, pas_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB pas_mod[] = { + { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES", + &pas_vlines, NULL, &pas_nlreg }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &pas_desc }, + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &pas_summ }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &pas_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &pas_show, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE pas_dev = { + "PAS", &pas_unit, pas_reg, pas_mod, + 1, 10, 31, 1, 16, 8, + &tmxr_ex, &tmxr_dep, &pas_reset, + NULL, &pas_attach, &pas_detach, + &pas_dib, DEV_NET | DEV_DISABLE + }; + +/* PASL data structures + + pasl_dev PASL device descriptor + pasl_unit PASL unit descriptor + pasl_reg PASL register list + pasl_mod PASL modifiers list +*/ + +UNIT pasl_unit[] = { + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, /* all but 8 dis */ + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, + { UDATA (&paso_svc, 0, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT }, + { UDATA (&paso_svc, UNIT_DIS, 0), PASL_WAIT } + }; + +MTAB pasl_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { UNIT_MDM, 0, "no dataset", "NODATASET", NULL }, + { UNIT_MDM, UNIT_MDM, "dataset", "DATASET", NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &pas_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &pas_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &pas_desc }, + { 0 } + }; + +REG pasl_reg[] = { + { URDATA (TIME, pasl_unit[0].wait, 16, 24, 0, + PAS_LINES, REG_NZ + PV_LEFT) }, + { NULL } + }; + +DEVICE pasl_dev = { + "PASL", pasl_unit, pasl_reg, pasl_mod, + PAS_LINES, 10, 31, 1, 16, 8, + NULL, NULL, &pas_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* PAS: IO routine */ + +uint32 pas (uint32 dev, uint32 op, uint32 dat) +{ +int32 ln = (dev - pas_dib.dno) >> 1; +int32 xmt = (dev - pas_dib.dno) & 1; +int32 t, old_cmd; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_RD: /* read */ + pas_rchp[ln] = 0; /* clr chr pend */ + pas_sta[ln] = pas_sta[ln] & ~STA_OVR; /* clr overrun */ + return pas_rbuf[ln]; /* return buf */ + + case IO_WD: /* write */ + pas_xbuf[ln] = dat & 0xFF; /* store char */ + pas_sta[ln] = pas_sta[ln] | STA_BSY; /* set busy */ + sim_activate (&pasl_unit[ln], pasl_unit[ln].wait); + break; + + case IO_SS: /* status */ + if (xmt) { /* xmt side? */ + if (pas_ldsc[ln].conn == 0) /* not conn? */ + t = STA_NCL2S | STA_BSY; /* busy, not clr */ + else t = pas_sta[ln] & STA_XMT; /* else just busy */ + } + else { + t = pas_sta[ln] & STA_RCV; /* get static */ + if (!pas_rchp[ln]) t = t | STA_BSY; /* no char? busy */ + if (pas_ldsc[ln].conn == 0) /* not connected? */ + t = t | STA_BSY | STA_EX; /* = !dsr */ + if (t & SET_EX) t = t | STA_EX; /* test for ex */ + } + return t; + + case IO_OC: /* command */ + old_cmd = pas_cmd[ln]; /* old cmd */ + if (dat & CMD_TYP) { /* type 1? */ + pas_cmd[ln] = (pas_cmd[ln] & 0xFF) | (dat << 8); + if (pas_cmd[ln] & CMD_WRT) /* write? */ + pas_xarm[ln] = int_chg (v_PASX + ln + ln, dat, pas_xarm[ln]); + else pas_rarm[ln] = int_chg (v_PAS + ln + ln, dat, pas_rarm[ln]); + } + else pas_cmd[ln] = (pas_cmd[ln] & ~0xFF) | dat; + if (pasl_unit[ln].flags & UNIT_MDM) { /* modem ctrl? */ + if ((pas_cmd[ln] & CMD_DTR) && (pas_sta[ln] & STA_RING)) + pas_sta[ln] = pas_sta[ln] & ~(STA_CROF | STA_RING); + if (old_cmd & ~pas_cmd[ln] & CMD_DTR) { + tmxr_linemsg (&pas_ldsc[ln], "\r\nLine hangup\r\n"); + tmxr_reset_ln (&pas_ldsc[ln]); /* reset line */ + pas_sta[ln] = pas_sta[ln] | STA_CROF; /* no carrier */ + if (pas_rarm[ln]) SET_INT (v_PAS + ln + ln); + } + } + break; + } + +return 0; +} + +/* Unit service - receive side + + Poll all active lines for input + Poll for new connections +*/ + +t_stat pasi_svc (UNIT *uptr) +{ +int32 ln, c, out; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +sim_activate (uptr, lfc_poll); /* continue poll */ +ln = tmxr_poll_conn (&pas_desc); /* look for connect */ +if (ln >= 0) { /* got one? */ + if ((pasl_unit[ln].flags & UNIT_MDM) && /* modem control */ + ((pas_cmd[ln] & CMD_DTR) == 0)) /* & !dtr? */ + pas_sta[ln] = pas_sta[ln] | STA_RING | STA_CROF; /* set ring, no cd */ + else pas_sta[ln] = pas_sta[ln] & ~STA_CROF; /* just answer */ + if (pas_rarm[ln]) SET_INT (v_PAS + ln + ln); /* interrupt */ + pas_ldsc[ln].rcve = 1; /* rcv enabled */ + } +tmxr_poll_rx (&pas_desc); /* poll for input */ +for (ln = 0; ln < PAS_ENAB; ln++) { /* loop thru lines */ + if (pas_ldsc[ln].conn) { /* connected? */ + if (c = tmxr_getc_ln (&pas_ldsc[ln])) { /* any char? */ + pas_sta[ln] = pas_sta[ln] & ~(STA_FR | STA_PF); + if (pas_rchp[ln]) pas_sta[ln] = pas_sta[ln] | STA_OVR; + if (pas_rarm[ln]) SET_INT (v_PAS + ln + ln); + if (c & SCPE_BREAK) { /* break? */ + pas_sta[ln] = pas_sta[ln] | STA_FR; /* framing error */ + pas_rbuf[ln] = 0; /* no character */ + } + else { /* normal */ + out = c & 0x7F; /* echo is 7b */ + c = sim_tt_inpcvt (c, TT_GET_MODE (pasl_unit[ln].flags)); + if (TT_GET_MODE (pasl_unit[ln].flags) != TT_MODE_8B) + c = pas_par (pas_cmd[ln], c); /* apply parity */ + pas_rbuf[ln] = c; /* save char */ + pas_rchp[ln] = 1; /* char pending */ + if ((pas_cmd[ln] & CMD_ECHO) && pas_ldsc[ln].xmte) { + TMLN *lp = &pas_ldsc[ln]; /* get line */ + out = sim_tt_outcvt (out, TT_GET_MODE (pasl_unit[ln].flags)); + if (out >= 0) tmxr_putc_ln (lp, out); /* output char */ + tmxr_poll_tx (&pas_desc); /* poll xmt */ + } + } /* end else normal */ + } /* end if char */ + } /* end if conn */ + else if ((pas_sta[ln] & STA_CROF) == 0) { /* not conn, was conn? */ + pas_sta[ln] = pas_sta[ln] | STA_CROF; /* no carrier */ + if (pas_rarm[ln]) SET_INT (v_PAS + ln + ln); /* intr */ + } + } /* end for */ +return SCPE_OK; +} + +/* Unit service - transmit side */ + +t_stat paso_svc (UNIT *uptr) +{ +int32 c; +uint32 ln = uptr - pasl_unit; /* line # */ + +if (pas_ldsc[ln].conn) { /* connected? */ + if (pas_ldsc[ln].xmte) { /* xmt enabled? */ + TMLN *lp = &pas_ldsc[ln]; /* get line */ + if (TT_GET_MODE (pasl_unit[ln].flags) == TT_MODE_8B) + c = pas_par (pas_cmd[ln], pas_xbuf[ln]); /* apply parity */ + else c = sim_tt_outcvt (pas_xbuf[ln], TT_GET_MODE (pasl_unit[ln].flags)); + if (c >= 0) { + tmxr_putc_ln (lp, c); /* output char */ + } + tmxr_poll_tx (&pas_desc); /* poll xmt */ + } + else { /* buf full */ + tmxr_poll_tx (&pas_desc); /* poll xmt */ + sim_activate (uptr, pasl_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } +pas_sta[ln] = pas_sta[ln] & ~STA_BSY; /* not busy */ +if (pas_xarm[ln]) SET_INT (v_PASX + ln + ln); /* set intr */ +return SCPE_OK; +} + +int32 pas_par (int32 cmd, int32 c) +{ +int32 pf = GET_PAR (cmd); +static const uint8 odd_par[] = { + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 00 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 10 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 20 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 30 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 40 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 50 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 60 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 70 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 80 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 90 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* A0 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* B0 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* C0 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* D0 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* E0 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* F0 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80 + }; + +switch (pf) { /* case on parity */ + + case PAR_ODD: + return (odd_par[c & 0x7F]) | (c & 0x7F); + + case PAR_EVEN: + return (odd_par[c & 0x7F] ^ 0x80) | (c & 0x7F); + + case PAR_NONE: + case PAR_RAW: + break; + } + +return c & 0xFF; +} + +/* Reset routine */ + +t_stat pas_reset (DEVICE *dptr) +{ +int32 i; + +if (dptr->flags & DEV_DIS) { /* disabled? */ + pas_dev.flags = pas_dev.flags | DEV_DIS; /* disable lines */ + pasl_dev.flags = pasl_dev.flags | DEV_DIS; + } +else { + pas_dev.flags = pas_dev.flags & ~DEV_DIS; /* enable lines */ + pasl_dev.flags = pasl_dev.flags & ~DEV_DIS; + } +if (pas_unit.flags & UNIT_ATT) /* master att? */ + sim_activate_abs (&pas_unit, lfc_poll); /* cosched with clock */ +else sim_cancel (&pas_unit); /* else stop */ +for (i = 0; i < PAS_LINES; i++) pas_reset_ln (i); +return SCPE_OK; +} + +/* Attach master unit */ + +t_stat pas_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&pas_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +sim_activate_abs (uptr, 100); /* quick poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat pas_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&pas_desc, uptr); /* detach */ +for (i = 0; i < PAS_LINES; i++) pas_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat pas_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < PAS_LINES; i++) t = t + (pas_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat pas_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < PAS_LINES; i++) t = t + (pas_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < PAS_LINES; i++) { + if (pas_ldsc[i].conn) { + if (val) tmxr_fconns (st, &pas_ldsc[i], i); + else tmxr_fstats (st, &pas_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Change number of lines */ + +t_stat pas_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newln = get_uint (cptr, 10, PAS_LINES, &r); +if ((r != SCPE_OK) || (newln == PAS_ENAB)) return r; +if (newln == 0) return SCPE_ARG; +if (newln < PAS_ENAB) { + for (i = newln, t = 0; i < PAS_ENAB; i++) t = t | pas_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < PAS_ENAB; i++) { + if (pas_ldsc[i].conn) { + tmxr_linemsg (&pas_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&pas_ldsc[i]); /* reset line */ + } + pasl_unit[i].flags = pasl_unit[i].flags | UNIT_DIS; + pas_reset_ln (i); + } + } +else { + for (i = PAS_ENAB; i < newln; i++) { + pasl_unit[i].flags = pasl_unit[i].flags & ~UNIT_DIS; + pas_reset_ln (i); + } + } +PAS_ENAB = newln; +return SCPE_OK; +} + +/* Reset an individual line */ + +void pas_reset_ln (int32 i) +{ +CLR_INT (v_PAS + i + i); /* clear int */ +CLR_ENB (v_PAS + i + i); +CLR_INT (v_PASX + i + i); /* disable int */ +CLR_ENB (v_PASX + i + i); +pas_rarm[i] = pas_xarm[i] = 0; /* disarm int */ +pas_rbuf[i] = pas_xbuf[i] = 0; /* clear state */ +pas_cmd[i] = 0; +pas_rchp[i] = 0; +pas_sta[i] = 0; +if (pas_ldsc[i].conn == 0) /* clear carrier */ + pas_sta[i] = pas_sta[i] | STA_CROF; +sim_cancel (&pasl_unit[i]); +return; +} + +/* Init template */ + +void pas_ini (t_bool dtpl) +{ +int32 i, j; + +for (i = j = 0; i < PAS_ENAB; i++) { + pas_tplte[j] = j; + pas_tplte[j + 1] = j + o_PASX; + j = j + 2; + } +pas_tplte[j] = TPL_END; +return; +} diff --git a/Interdata/id_pt.c b/Interdata/id_pt.c new file mode 100644 index 0000000..0a7da16 --- /dev/null +++ b/Interdata/id_pt.c @@ -0,0 +1,360 @@ +/* id_pt.c: Interdata paper tape reader + + Copyright (c) 2000-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + pt paper tape reader and punch + + 25-Apr-03 RMS Revised for extended file support + 10-Apr-03 RMS Fixed type problem in ptr service (from Mark Pizzolato) +*/ + +#include "id_defs.h" +#include + +/* Device definitions */ + +#define PTR 0 /* unit subscripts */ +#define PTP 1 + +#define STA_OVR 0x80 /* overrun */ +#define STA_NMTN 0x10 /* no motion */ +#define STA_MASK (STA_BSY | STA_OVR | STA_DU) /* static bits */ +#define SET_EX (STA_OVR | STA_NMTN) /* set EX */ + +#define CMD_V_RUN 4 /* run/stop */ +#define CMD_V_SLEW 2 /* slew/step */ +#define CMD_V_RD 0 /* read/write */ + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; + +uint32 pt_run = 0, pt_slew = 0; /* ptr modes */ +uint32 pt_rd = 1, pt_chp = 0; /* pt state */ +uint32 pt_arm = 0; /* int arm */ +uint32 pt_sta = STA_BSY; /* status */ +uint32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ + +DEVICE pt_dev; +uint32 pt (uint32 dev, uint32 op, uint32 dat); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat pt_boot (int32 unitno, DEVICE *dptr); +t_stat pt_reset (DEVICE *dptr); + +/* PT data structures + + pt_dev PT device descriptor + pt_unit PT unit descriptors + pt_reg PT register list +*/ + +DIB pt_dib = { d_PT, -1, v_PT, NULL, &pt, NULL }; + +UNIT pt_unit[] = { + { UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT }, + { UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT } + }; + +REG pt_reg[] = { + { HRDATA (STA, pt_sta, 8) }, + { HRDATA (RBUF, pt_unit[PTR].buf, 8) }, + { DRDATA (RPOS, pt_unit[PTR].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (RTIME, pt_unit[PTR].wait, 24), PV_LEFT }, + { FLDATA (RSTOP_IOE, ptr_stopioe, 0) }, + { HRDATA (PBUF, pt_unit[PTP].buf, 8) }, + { DRDATA (PPOS, pt_unit[PTP].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (PTIME, pt_unit[PTP].wait, 24), PV_LEFT }, + { FLDATA (PSTOP_IOE, ptp_stopioe, 0) }, + { FLDATA (IREQ, int_req[l_PT], i_PT) }, + { FLDATA (IENB, int_enb[l_PT], i_PT) }, + { FLDATA (IARM, pt_arm, 0) }, + { FLDATA (RD, pt_rd, 0) }, + { FLDATA (RUN, pt_run, 0) }, + { FLDATA (SLEW, pt_slew, 0) }, + { FLDATA (CHP, pt_chp, 0) }, + { HRDATA (DEVNO, pt_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB pt_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "devno", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE pt_dev = { + "PT", pt_unit, pt_reg, pt_mod, + 2, 10, 31, 1, 16, 8, + NULL, NULL, &pt_reset, + &pt_boot, NULL, NULL, + &pt_dib, DEV_DISABLE + }; + +/* Paper tape: IO routine */ + +uint32 pt (uint32 dev, uint32 op, uint32 dat) +{ +uint32 t, old_rd, old_run; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_OC: /* command */ + old_rd = pt_rd; /* save curr rw */ + old_run = pt_run; /* save curr run */ + pt_arm = int_chg (v_PT, dat, pt_arm); /* upd int ctrl */ + pt_rd = io_2b (dat, CMD_V_RD, pt_rd); /* upd read/wr */ + if (old_rd != pt_rd) { /* rw change? */ + pt_sta = pt_sta & ~STA_OVR; /* clr overrun */ + if (sim_is_active (&pt_unit[pt_rd? PTR: PTP])) { + pt_sta = pt_sta | STA_BSY; /* busy = 1 */ + CLR_INT (v_PT); /* clear int */ + } + else { /* not active */ + pt_sta = pt_sta & ~STA_BSY; /* busy = 0 */ + if (pt_arm) SET_INT (v_PT); /* no, set int */ + } + } + if (pt_rd) { /* reader? */ + pt_run = io_2b (dat, CMD_V_RUN, pt_run); /* upd run/stop */ + pt_slew = io_2b (dat, CMD_V_SLEW, pt_slew); /* upd slew/inc */ + if (pt_run) { /* run set? */ + if (old_run == 0) { /* run 0 -> 1? */ + sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); + pt_sta = pt_sta & ~STA_DU; /* clear eof */ + } + } + else sim_cancel (&pt_unit[PTR]); /* clr, stop rdr */ + } + else pt_sta = pt_sta & ~STA_DU; /* punch, clr eof */ + break; + + case IO_RD: /* read */ + if (pt_run && !pt_slew) { /* incremental? */ + sim_activate (&pt_unit[PTR], pt_unit[PTR].wait); + pt_sta = pt_sta & ~STA_DU; /* clr eof */ + } + pt_chp = 0; /* clr char pend */ + if (pt_rd) pt_sta = pt_sta | STA_BSY; /* set busy */ + return (pt_unit[PTR].buf & 0xFF); /* return char */ + + case IO_WD: /* write */ + pt_unit[PTP].buf = dat & DMASK8; /* save char */ + if (!pt_rd) pt_sta = pt_sta | STA_BSY; /* set busy */ + sim_activate (&pt_unit[PTP], pt_unit[PTP].wait); + break; + + case IO_SS: /* status */ + t = pt_sta & STA_MASK; /* get status */ + if (pt_rd && !pt_run && !sim_is_active (&pt_unit[PTR])) + t = t | STA_NMTN; /* stopped? */ + if ((pt_unit[pt_rd? PTR: PTP].flags & UNIT_ATT) == 0) + t = t | STA_DU; /* offline? */ + if (t & SET_EX) t = t | STA_EX; /* test for EX */ + return t; + } + +return 0; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if (pt_rd) { /* read mode? */ + pt_sta = pt_sta & ~STA_BSY; /* clear busy */ + if (pt_arm) SET_INT (v_PT); /* if armed, intr */ + if (pt_chp) pt_sta = pt_sta | STA_OVR; /* overrun? */ + } +pt_chp = 1; /* char pending */ +if ((temp = getc (uptr->fileref)) == EOF) { /* error? */ + if (feof (uptr->fileref)) { /* eof? */ + pt_sta = pt_sta | STA_DU; /* set DU */ + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +uptr->buf = temp & DMASK8; /* store char */ +uptr->pos = uptr->pos + 1; /* incr pos */ +if (pt_slew) sim_activate (uptr, uptr->wait); /* slew? continue */ +return SCPE_OK; +} + +t_stat ptp_svc (UNIT *uptr) +{ +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (!pt_rd) { /* write mode? */ + pt_sta = pt_sta & ~STA_BSY; /* clear busy */ + if (pt_arm) SET_INT (v_PT); /* if armed, intr */ + } +if (putc (uptr->buf, uptr -> fileref) == EOF) { /* write char */ + perror ("PTP I/O error"); + clearerr (uptr -> fileref); + return SCPE_IOERR; + } +uptr -> pos = uptr -> pos + 1; /* incr pos */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat pt_reset (DEVICE *dptr) +{ +sim_cancel (&pt_unit[PTR]); /* deactivate units */ +sim_cancel (&pt_unit[PTP]); +pt_rd = 1; /* read */ +pt_chp = pt_run = pt_slew = 0; /* stop, inc, disarm */ +pt_sta = STA_BSY; /* buf empty */ +CLR_INT (v_PT); /* clear int */ +CLR_ENB (v_PT); /* disable int */ +pt_arm = 0; /* disarm int */ +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 0x50 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint8)) +#define BOOT3_START 0x3E +#define BOOT3_LEN (sizeof (boot_rom) / sizeof (uint8)) + +static uint8 boot_rom[] = { + 0xD5, 0x00, 0x00, 0xCF, /* ST AL CF */ + 0x43, 0x00, 0x00, 0x80 /* BR 80 */ + }; + +static uint8 boot3_rom[] = { + 0xC8, 0x20, 0x00, 0x80, /* ST LHI 2,80 */ + 0xC8, 0x30, 0x00, 0x01, /* LHI 3,1 */ + 0xC8, 0x40, 0x00, 0xCF, /* LHI 4,CF */ + 0xD3, 0xA0, 0x00, 0x78, /* LB A,78 */ + 0xDE, 0xA0, 0x00, 0x79, /* OC A,79 */ + 0x9D, 0xAE, /* LP SSR A,E */ + 0x42, 0xF0, 0x00, 0x52, /* BTC F,LP */ + 0x9B, 0xAE, /* RDR A,E */ + 0x08, 0xEE, /* LHR E,E */ + 0x43, 0x30, 0x00, 0x52, /* BZ LP */ + 0x43, 0x00, 0x00, 0x6C, /* BR STO */ + 0x9D, 0xAE, /* LP1 SSR A,E */ + 0x42, 0xF0, 0x00, 0x64, /* BTC F,LP1 */ + 0x9B, 0xAE, /* RDR A,E */ + 0xD2, 0xE2, 0x00, 0x00, /* STO STB E,0(2) */ + 0xC1, 0x20, 0x00, 0x64, /* BXLE 2,LP1 */ + 0x43, 0x00, 0x00, 0x80 /* BR 80 */ + }; + +t_stat pt_boot (int32 unitno, DEVICE *dptr) +{ +extern uint32 PC, dec_flgs; +extern uint16 decrom[]; + +if (decrom[0xD5] & dec_flgs) /* AL defined? */ + IOWriteBlk (BOOT3_START, BOOT3_LEN, boot3_rom); /* no, 50 seq */ +else IOWriteBlk (BOOT_START, BOOT_LEN, boot_rom); /* copy AL boot */ +IOWriteB (AL_DEV, pt_dib.dno); /* set dev no */ +IOWriteB (AL_IOC, 0x99); /* set dev cmd */ +IOWriteB (AL_SCH, 0); /* clr sch dev no */ +PC = BOOT_START; +return SCPE_OK; +} + +/* Dump routine */ + +#define LOAD_START 0x80 +#define LOAD_LO 0x8A +#define LOAD_HI 0x8E +#define LOAD_CS 0x93 +#define LOAD_LEN (sizeof (load_rom) / sizeof (uint8)) +#define LOAD_LDR 50 + +static uint8 load_rom[] = { + 0x24, 0x21, /* BOOT LIS R2,1 */ + 0x23, 0x03, /* BS BOOT */ + 0x00, 0x00, /* 32b psw pointer */ + 0x00, 0x00, /* 32b reg pointer */ + 0xC8, 0x10, /* ST LHI R1,lo */ + 0x00, 0x00, + 0xC8, 0x30, /* LHI R3,hi */ + 0x00, 0x00, + 0xC8, 0x60, /* LHI R3,cs */ + 0x00, 0x00, + 0xD3, 0x40, /* LB R4,X'78' */ + 0x00, 0x78, + 0xDE, 0x40, /* OC R4,X'79' */ + 0x00, 0x79, + 0x9D, 0x45, /* LDR SSR R4,R5 */ + 0x20, 0x91, /* BTBS 9,.-2 */ + 0x9B, 0x45, /* RDR R4,R5 */ + 0x08, 0x55, /* L(H)R R5,R5 */ + 0x22, 0x34, /* BZS LDR */ + 0xD2, 0x51, /* LOOP STB R5,0(R1) */ + 0x00, 0x00, + 0x07, 0x65, /* X(H)R R6,R5 */ + 0x9A, 0x26, /* WDR R2,R6 */ + 0x9D, 0x45, /* SSR R4,R5 */ + 0x20, 0x91, /* BTBS 9,.-2 */ + 0x9B, 0x45, /* RDR R4,R5 */ + 0xC1, 0x10, /* BXLE R1,LOOP */ + 0x00, 0xA6, + 0x24, 0x78, /* LIS R7,8 */ + 0x91, 0x7C, /* SLLS R7,12 */ + 0x95, 0x57, /* EPSR R5,R7 */ + 0x22, 0x03 /* BS .-6 */ + }; + +t_stat pt_dump (FILE *of, char *cptr, char *fnam) +{ +uint32 i, lo, hi, cs; +char *tptr; +extern DEVICE cpu_dev; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; +tptr = get_range (NULL, cptr, &lo, &hi, cpu_dev.aradix, 0xFFFF, 0); +if ((tptr == NULL) || (lo < INTSVT)) return SCPE_ARG; +if (*tptr != 0) return SCPE_2MARG; +for (i = lo, cs = 0; i <= hi; i++) cs = cs ^ IOReadB (i); +IOWriteBlk (LOAD_START, LOAD_LEN, load_rom); +IOWriteB (LOAD_LO, (lo >> 8) & 0xFF); +IOWriteB (LOAD_LO + 1, lo & 0xFF); +IOWriteB (LOAD_HI, (hi >> 8) & 0xFF); +IOWriteB (LOAD_HI + 1, hi & 0xFF); +IOWriteB (LOAD_CS, cs & 0xFF); +for (i = 0; i < LOAD_LDR; i++) fputc (0, of); +for (i = LOAD_START; i < (LOAD_START + LOAD_LEN); i++) + fputc (IOReadB (i), of); +for (i = 0; i < LOAD_LDR; i++) fputc (0, of); +for (i = lo; i <= hi; i++) fputc (IOReadB (i), of); +for (i = 0; i < LOAD_LDR; i++) fputc (0, of); +return SCPE_OK; +} diff --git a/Interdata/id_tt.c b/Interdata/id_tt.c new file mode 100644 index 0000000..c446ed9 --- /dev/null +++ b/Interdata/id_tt.c @@ -0,0 +1,281 @@ +/* id_tt.c: Interdata teletype + + Copyright (c) 2000-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tt console + + 18-Jun-07 RMS Added UNIT_IDLE flag to console input + 18-Oct-06 RMS Sync keyboard to LFC clock + 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode + 22-Nov-05 RMS Revised for new terminal processing routines + 29-Dec-03 RMS Added support for console backpressure + 25-Apr-03 RMS Revised for extended file support + 11-Jan-03 RMS Added TTP support + 22-Dec-02 RMS Added break support +*/ + +#include "id_defs.h" +#include + +/* Device definitions */ + +#define TTI 0 +#define TTO 1 + +#define STA_OVR 0x80 /* overrun */ +#define STA_BRK 0x20 /* break */ +#define STA_MASK (STA_OVR | STA_BRK | STA_BSY) /* status mask */ +#define SET_EX (STA_OVR | STA_BRK) /* set EX */ + +#define CMD_V_FDPX 4 /* full/half duplex */ +#define CMD_V_RD 2 /* read/write */ + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; +extern int32 lfc_poll; + +uint32 tt_sta = STA_BSY; /* status */ +uint32 tt_fdpx = 1; /* tt mode */ +uint32 tt_rd = 1, tt_chp = 0; /* tt state */ +uint32 tt_arm = 0; /* int arm */ + +uint32 tt (uint32 dev, uint32 op, uint32 dat); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tt_reset (DEVICE *dptr); +t_stat tt_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tt_set_break (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tt_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* TT data structures + + tt_dev TT device descriptor + tt_unit TT unit descriptors + tt_reg TT register list + tt_mod TT modifiers list +*/ + +DIB tt_dib = { d_TT, -1, v_TT, NULL, &tt, NULL }; + +UNIT tt_unit[] = { + { UDATA (&tti_svc, TT_MODE_KSR|UNIT_IDLE, 0), 0 }, + { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT } + }; + +REG tt_reg[] = { + { HRDATA (STA, tt_sta, 8) }, + { HRDATA (KBUF, tt_unit[TTI].buf, 8) }, + { DRDATA (KPOS, tt_unit[TTI].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (KTIME, tt_unit[TTI].wait, 24), PV_LEFT }, + { HRDATA (TBUF, tt_unit[TTO].buf, 8) }, + { DRDATA (TPOS, tt_unit[TTO].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TTIME, tt_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (IREQ, int_req[l_TT], i_TT) }, + { FLDATA (IENB, int_enb[l_TT], i_TT) }, + { FLDATA (IARM, tt_arm, 0) }, + { FLDATA (RD, tt_rd, 0) }, + { FLDATA (FDPX, tt_fdpx, 0) }, + { FLDATA (CHP, tt_chp, 0) }, + { HRDATA (DEVNO, tt_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB tt_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tt_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tt_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tt_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tt_set_mode }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "ENABLED", + &tt_set_enbdis, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, DEV_DIS, NULL, "DISABLED", + &tt_set_enbdis, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "BREAK", + &tt_set_break, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, &tt_dib }, + { 0 } + }; + +DEVICE tt_dev = { + "TT", tt_unit, tt_reg, tt_mod, + 2, 10, 31, 1, 16, 8, + NULL, NULL, &tt_reset, + NULL, NULL, NULL, + &tt_dib, 0 + }; + +/* Terminal: IO routine */ + +uint32 tt (uint32 dev, uint32 op, uint32 dat) +{ +uint32 old_rd, t; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_OC: /* command */ + old_rd = tt_rd; + tt_arm = int_chg (v_TT, dat, tt_arm); /* upd int ctrl */ + tt_fdpx = io_2b (dat, CMD_V_FDPX, tt_fdpx); /* upd full/half */ + tt_rd = io_2b (dat, CMD_V_RD, tt_rd); /* upd rd/write */ + if (tt_rd != old_rd) { /* rw change? */ + if (tt_rd? tt_chp: !sim_is_active (&tt_unit[TTO])) { + tt_sta = 0; /* busy = 0 */ + if (tt_arm) SET_INT (v_TT); /* req intr */ + } + else { + tt_sta = STA_BSY; /* busy = 1 */ + CLR_INT (v_TT); /* clr int */ + } + } + else tt_sta = tt_sta & ~STA_OVR; /* clr ovflo */ + break; + + case IO_RD: /* read */ + tt_chp = 0; /* clear pend */ + if (tt_rd) tt_sta = (tt_sta | STA_BSY) & ~STA_OVR; + return (tt_unit[TTI].buf & 0xFF); + + case IO_WD: /* write */ + tt_unit[TTO].buf = dat & 0xFF; /* save char */ + if (!tt_rd) tt_sta = tt_sta | STA_BSY; /* set busy */ + sim_activate (&tt_unit[TTO], tt_unit[TTO].wait); + break; + + case IO_SS: /* status */ + t = tt_sta & STA_MASK; /* get status */ + if (t & SET_EX) t = t | STA_EX; /* test for EX */ + return t; + } + +return 0; +} + +/* Unit service routines */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 out, temp; + +sim_activate (uptr, KBD_WAIT (uptr->wait, lfc_poll)); /* continue poll */ +tt_sta = tt_sta & ~STA_BRK; /* clear break */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +if (tt_rd) { /* read mode? */ + tt_sta = tt_sta & ~STA_BSY; /* clear busy */ + if (tt_arm) SET_INT (v_TT); /* if armed, intr */ + if (tt_chp) tt_sta = tt_sta | STA_OVR; /* got char? overrun */ + } +tt_chp = 1; /* char pending */ +out = temp & 0x7F; /* echo is 7B */ +if (temp & SCPE_BREAK) { /* break? */ + tt_sta = tt_sta | STA_BRK; /* set status */ + uptr->buf = 0; /* no character */ + } +else uptr->buf = sim_tt_inpcvt (temp, TT_GET_MODE (uptr->flags) | TTUF_KSR); +uptr->pos = uptr->pos + 1; /* incr count */ +if (!tt_fdpx) { /* half duplex? */ + out = sim_tt_outcvt (out, TT_GET_MODE (uptr->flags) | TTUF_KSR); + if (out >= 0) { /* valid echo? */ + sim_putchar (out); /* write char */ + tt_unit[TTO].pos = tt_unit[TTO].pos + 1; + } + } +return SCPE_OK; +} + +t_stat tto_svc (UNIT *uptr) +{ +int32 ch; +t_stat r; + +ch = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); +if (ch >= 0) { + if ((r = sim_putchar_s (ch)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); + } + } +if (!tt_rd) { /* write mode? */ + tt_sta = tt_sta & ~STA_BSY; /* clear busy */ + if (tt_arm) SET_INT (v_TT); /* if armed, intr */ + } +uptr->pos = uptr->pos + 1; /* incr count */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tt_reset (DEVICE *dptr) +{ +if (dptr->flags & DEV_DIS) sim_cancel (&tt_unit[TTI]); /* dis? cancel poll */ +else sim_activate_abs (&tt_unit[TTI], KBD_WAIT (tt_unit[TTI].wait, lfc_poll)); +sim_cancel (&tt_unit[TTO]); /* cancel output */ +tt_rd = tt_fdpx = 1; /* read, full duplex */ +tt_chp = 0; /* no char */ +tt_sta = STA_BSY; /* buffer empty */ +CLR_INT (v_TT); /* clear int */ +CLR_ENB (v_TT); /* disable int */ +tt_arm = 0; /* disarm int */ +return SCPE_OK; +} + +/* Make mode flags uniform */ + +t_stat tt_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +tt_unit[TTO].flags = (tt_unit[TTO].flags & ~TT_MODE) | val; +if (val == TT_MODE_7P) val = TT_MODE_7B; +tt_unit[TTI].flags = (tt_unit[TTI].flags & ~TT_MODE) | val; +return SCPE_OK; +} + +/* Set input break */ + +t_stat tt_set_break (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (tt_dev.flags & DEV_DIS) return SCPE_NOFNC; +tt_sta = tt_sta | STA_BRK; +if (tt_rd) { /* read mode? */ + tt_sta = tt_sta & ~STA_BSY; /* clear busy */ + if (tt_arm) SET_INT (v_TT); /* if armed, intr */ + } +sim_cancel (&tt_unit[TTI]); /* restart TT poll */ +sim_activate (&tt_unit[TTI], tt_unit[TTI].wait); /* so brk is seen */ +return SCPE_OK; +} + +/* Set enabled/disabled */ + +t_stat tt_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +extern DEVICE ttp_dev; +extern t_stat ttp_reset (DEVICE *dptr); + +tt_dev.flags = (tt_dev.flags & ~DEV_DIS) | val; +ttp_dev.flags = (ttp_dev.flags & ~DEV_DIS) | (val ^ DEV_DIS); +tt_reset (&tt_dev); +ttp_reset (&ttp_dev); +return SCPE_OK; +} diff --git a/Interdata/id_ttp.c b/Interdata/id_ttp.c new file mode 100644 index 0000000..24ed0d3 --- /dev/null +++ b/Interdata/id_ttp.c @@ -0,0 +1,279 @@ +/* id_ttp.c: Interdata PASLA console interface + + Copyright (c) 2000-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ttp console (on PAS) + + 18-Jun-07 RMS Added UNIT_IDLE flag to console input + 18-Oct-06 RMS Sync keyboard to LFC clock + 22-Nov-05 RMS Revised for new terminal processing routines + 29-Dec-03 RMS Added support for console backpressure + 25-Apr-03 RMS Revised for extended file support +*/ + +#include "id_defs.h" +#include + +#define TTI 0 +#define TTO 1 + +/* Status byte */ + +#define STA_OVR 0x80 /* overrun RO */ +#define STA_PF 0x40 /* parity err RO */ +#define STA_FR 0x20 /* framing err RO */ +#define STA_RCV (STA_OVR|STA_PF|STA_FR) +#define SET_EX (STA_OVR|STA_PF|STA_FR) +#define STA_XMT (STA_BSY) + +/* Command bytes 1,0 */ + +#define CMD_ECHO (0x10 << 8) /* echoplex */ +#define CMD_WRT (0x02 << 8) /* write/read */ +#define CMD_TYP 0x01 /* command type */ + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; +extern int32 pas_par (int32 cmd, int32 c); +extern int32 lfc_poll; + +uint32 ttp_sta = 0; /* status */ +uint32 ttp_cmd = 0; /* command */ +uint32 ttp_kchp = 0; /* rcvr chr pend */ +uint32 ttp_karm = 0; /* rcvr int armed */ +uint32 ttp_tarm = 0; /* xmt int armed */ +uint8 ttp_tplte[] = { 0, 1, TPL_END }; + +uint32 ttp (uint32 dev, uint32 op, uint32 dat); +t_stat ttpi_svc (UNIT *uptr); +t_stat ttpo_svc (UNIT *uptr); +t_stat ttp_reset (DEVICE *dptr); +t_stat ttp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ttp_set_break (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ttp_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* TTP data structures */ + +DIB ttp_dib = { d_TTP, -1, v_TTP, ttp_tplte, &ttp, NULL }; + +UNIT ttp_unit[] = { + { UDATA (&ttpi_svc, UNIT_IDLE, 0), 0 }, + { UDATA (&ttpo_svc, 0, 0), SERIAL_OUT_WAIT } + }; + +REG ttp_reg[] = { + { HRDATA (CMD, ttp_cmd, 16) }, + { HRDATA (KBUF, ttp_unit[TTI].buf, 8) }, + { DRDATA (KPOS, ttp_unit[TTI].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (KTIME, ttp_unit[TTI].wait, 24), REG_NZ + PV_LEFT + REG_HRO }, + { FLDATA (KIREQ, int_req[l_TTP], i_TTP) }, + { FLDATA (KIENB, int_enb[l_TTP], i_TTP) }, + { FLDATA (KARM, ttp_karm, 0) }, + { FLDATA (CHP, ttp_kchp, 0) }, + { HRDATA (TBUF, ttp_unit[TTO].buf, 8) }, + { DRDATA (TPOS, ttp_unit[TTO].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TTIME, ttp_unit[TTO].wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (TIREQ, int_req[l_TTP], i_TTP + 1) }, + { FLDATA (TIENB, int_enb[l_TTP], i_TTP + 1) }, + { FLDATA (TARM, ttp_tarm, 0) }, + { HRDATA (DEVNO, ttp_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB ttp_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", &ttp_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &ttp_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &ttp_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &ttp_set_mode }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "ENABLED", + &ttp_set_enbdis, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, DEV_DIS, NULL, "DISABLED", + &ttp_set_enbdis, NULL, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "BREAK", + &ttp_set_break, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE ttp_dev = { + "TTP", ttp_unit, ttp_reg, ttp_mod, + 2, 10, 31, 1, 16, 8, + NULL, NULL, &ttp_reset, + NULL, NULL, NULL, + &ttp_dib, DEV_DIS + }; + +/* Terminal: I/O routine */ + +uint32 ttp (uint32 dev, uint32 op, uint32 dat) +{ +int32 xmt = dev & 1; +int32 t, old_cmd; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_RD: /* read */ + ttp_kchp = 0; /* clr chr pend */ + ttp_sta = ttp_sta & ~STA_OVR; /* clr overrun */ + return ttp_unit[TTI].buf; /* return buf */ + + case IO_WD: /* write */ + ttp_unit[TTO].buf = dat & 0xFF; /* store char */ + ttp_sta = ttp_sta | STA_BSY; /* set busy */ + sim_activate (&ttp_unit[TTO], ttp_unit[TTO].wait); + break; + + case IO_SS: /* status */ + if (xmt) t = ttp_sta & STA_XMT; /* xmt? just busy */ + else { /* rcv */ + t = ttp_sta & STA_RCV; /* get static */ + if (!ttp_kchp) t = t | STA_BSY; /* no char? busy */ + if (t & SET_EX) t = t | STA_EX; /* test for ex */ + } + return t; + + case IO_OC: /* command */ + old_cmd = ttp_cmd; /* old cmd */ + if (dat & CMD_TYP) { /* type 1? */ + ttp_cmd = (ttp_cmd & 0xFF) | (dat << 8); + if (ttp_cmd & CMD_WRT) /* write? */ + ttp_tarm = int_chg (v_TTP + 1, dat, ttp_tarm); + else ttp_karm = int_chg (v_TTP, dat, ttp_karm); + } + else ttp_cmd = (ttp_cmd & ~0xFF) | dat; + break; + } + +return 0; +} + +/* Unit service */ + +t_stat ttpi_svc (UNIT *uptr) +{ +int32 c, out; + +sim_activate (uptr, KBD_WAIT (uptr->wait, lfc_poll)); /* continue poll */ +ttp_sta = ttp_sta & ~STA_FR; /* clear break */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +ttp_sta = ttp_sta & ~STA_PF; /* clear parity err */ +if (ttp_kchp) ttp_sta = ttp_sta | STA_OVR; /* overrun? */ +if (ttp_karm) SET_INT (v_TTP); +if (c & SCPE_BREAK) { /* break? */ + ttp_sta = ttp_sta | STA_FR; /* framing error */ + uptr->buf = 0; /* no character */ + } +else { + out = c & 0x7F; /* echo is 7b */ + c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); + if (TT_GET_MODE (uptr->flags) != TT_MODE_8B) /* not 8b mode? */ + c = pas_par (ttp_cmd, c); /* apply parity */ + uptr->buf = c; /* save char */ + uptr->pos = uptr->pos + 1; /* incr count */ + ttp_kchp = 1; /* char pending */ + if (ttp_cmd & CMD_ECHO) { + out = sim_tt_outcvt (out, TT_GET_MODE (uptr->flags)); + if (c >= 0) sim_putchar (out); + ttp_unit[TTO].pos = ttp_unit[TTO].pos + 1; + } + } +return SCPE_OK; +} + +t_stat ttpo_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +if (TT_GET_MODE (uptr->flags) == TT_MODE_8B) /* 8b? */ + c = pas_par (ttp_cmd, uptr->buf); /* apply parity */ +else c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags)); +if (c >= 0) { + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); + } + } +ttp_sta = ttp_sta & ~STA_BSY; /* not busy */ +if (ttp_tarm) SET_INT (v_TTP + 1); /* set intr */ +uptr->pos = uptr->pos + 1; /* incr count */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ttp_reset (DEVICE *dptr) +{ +if (dptr->flags & DEV_DIS) sim_cancel (&ttp_unit[TTI]); +else sim_activate_abs (&ttp_unit[TTI], KBD_WAIT (ttp_unit[TTI].wait, lfc_poll)); +sim_cancel (&ttp_unit[TTO]); +CLR_INT (v_TTP); /* clear int */ +CLR_ENB (v_TTP); +CLR_INT (v_TTP + 1); /* disable int */ +CLR_ENB (v_TTP + 1); +ttp_karm = ttp_tarm = 0; /* disarm int */ +ttp_cmd = 0; +ttp_sta = 0; +ttp_kchp = 0; +return SCPE_OK; +} + +/* Make mode flags uniform */ + +t_stat ttp_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +ttp_unit[TTO].flags = (ttp_unit[TTO].flags & ~TT_MODE) | val; +if (val == TT_MODE_7P) val = TT_MODE_7B; +ttp_unit[TTI].flags = (ttp_unit[TTI].flags & ~TT_MODE) | val; +return SCPE_OK; +} + +/* Set input break */ + +t_stat ttp_set_break (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (ttp_dev.flags & DEV_DIS) return SCPE_NOFNC; +ttp_sta = ttp_sta | STA_FR; +if (ttp_karm) SET_INT (v_TTP); /* if armed, intr */ +sim_cancel (&ttp_unit[TTI]); /* restart TT poll */ +sim_activate (&ttp_unit[TTI], ttp_unit[TTI].wait); +return SCPE_OK; +} + +/* Set enabled/disabled */ + +t_stat ttp_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +extern DEVICE tt_dev; +extern t_stat tt_reset (DEVICE *dptr); + +ttp_dev.flags = (ttp_dev.flags & ~DEV_DIS) | val; +tt_dev.flags = (tt_dev.flags & ~DEV_DIS) | (val ^ DEV_DIS); +ttp_reset (&ttp_dev); +tt_reset (&tt_dev); +return SCPE_OK; +} diff --git a/Interdata/id_uvc.c b/Interdata/id_uvc.c new file mode 100644 index 0000000..d660e17 --- /dev/null +++ b/Interdata/id_uvc.c @@ -0,0 +1,373 @@ +/* id_uvc.c: Interdata universal clock + + Copyright (c) 2001-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + pic precision incremental clock + lfc line frequency clock + + 18-Jun-07 RMS Added UNIT_IDLE flag + 18-Oct-06 RMS Changed LFC to be free running, export tmr_poll + 23-Jul-05 RMS Fixed {} error in OC + 01-Mar-03 RMS Added SET/SHOW LFC FREQ support + Changed precision clock algorithm for V7 UNIX +*/ + +#include "id_defs.h" +#include + +/* Device definitions */ + +#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diag mode */ +#define UNIT_DIAG (1 << UNIT_V_DIAG) + +#define STA_OVF 0x08 /* PIC overflow */ +#define CMD_STRT 0x20 /* start */ +#define PIC_V_RATE 12 /* rate */ +#define PIC_M_RATE 0xF +#define PIC_RATE (PIC_M_RATE << PIC_V_RATE) +#define PIC_CTR 0x0FFF /* PIC counters */ +#define GET_RATE(x) (((x) >> PIC_V_RATE) & PIC_M_RATE) +#define GET_CTR(x) ((x) & PIC_CTR) +#define PIC_TPS 1000 + +extern uint32 int_req[INTSZ], int_enb[INTSZ]; + +int32 pic_db = 0; /* output buf */ +int32 pic_ric = 0; /* reset count */ +int32 pic_cic = 0; /* current count */ +uint32 pic_save = 0; /* saved time */ +uint32 pic_ovf = 0; /* overflow */ +uint32 pic_rdp = 0; +uint32 pic_wdp = 0; +uint32 pic_cnti = 0; /* instr/timer */ +uint32 pic_arm = 0; /* int arm */ +uint32 pic_decr = 1; /* decrement */ +uint16 pic_time[4] = { 1, 10, 100, 1000 }; /* delays */ +uint16 pic_usec[4] = { 1, 10, 100, 1000 }; /* usec per tick */ +static int32 pic_map[16] = { /* map rate to delay */ + 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 + }; + +DEVICE pic_dev; +uint32 pic (uint32 dev, uint32 op, uint32 dat); +t_stat pic_svc (UNIT *uptr); +t_stat pic_reset (DEVICE *dptr); +void pic_sched (t_bool strt); +uint32 pic_rd_cic (void); + +int32 lfc_tps = 120; /* ticks per */ +int32 lfc_poll = 8000; +uint32 lfc_arm = 0; /* int arm */ + +DEVICE lfc_dev; +uint32 lfc (uint32 dev, uint32 op, uint32 dat); +t_stat lfc_svc (UNIT *uptr); +t_stat lfc_reset (DEVICE *dptr); +t_stat lfc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat lfc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* PIC data structures + + pic_dev PIC device descriptor + pic_unit PIC unit descriptor + pic_reg PIC register list +*/ + +DIB pic_dib = { d_PIC, -1, v_PIC, NULL, &pic, NULL }; + +UNIT pic_unit = { UDATA (&pic_svc, UNIT_IDLE, 0), 1000 }; + +REG pic_reg[] = { + { HRDATA (BUF, pic_db, 16) }, + { HRDATA (RIC, pic_ric, 16) }, + { HRDATA (CIC, pic_cic, 12) }, + { FLDATA (RDP, pic_rdp, 0) }, + { FLDATA (WDP, pic_wdp, 0) }, + { FLDATA (OVF, pic_ovf, 0) }, + { FLDATA (IREQ, int_req[l_PIC], i_PIC) }, + { FLDATA (IENB, int_enb[l_PIC], i_PIC) }, + { FLDATA (IARM, pic_arm, 0) }, + { BRDATA (TIME, pic_time, 10, 16, 4), REG_NZ + PV_LEFT }, + { DRDATA (SAVE, pic_save, 32), REG_HRO + PV_LEFT }, + { DRDATA (DECR, pic_decr, 16), REG_HRO + PV_LEFT }, + { FLDATA (MODE, pic_cnti, 0), REG_HRO }, + { HRDATA (DEVNO, pic_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB pic_mod[] = { + { UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL }, + { UNIT_DIAG, 0, NULL, "NORMAL", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE pic_dev = { + "PIC", &pic_unit, pic_reg, pic_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &pic_reset, + NULL, NULL, NULL, + &pic_dib, DEV_DISABLE + }; + +/* LFC data structures + + lfc_dev LFC device descriptor + lfc_unit LFC unit descriptor + lfc_reg LFC register list +*/ + +DIB lfc_dib = { d_LFC, -1, v_LFC, NULL, &lfc, NULL }; + +UNIT lfc_unit = { UDATA (&lfc_svc, UNIT_IDLE, 0), 8333 }; + +REG lfc_reg[] = { + { FLDATA (IREQ, int_req[l_LFC], i_LFC) }, + { FLDATA (IENB, int_enb[l_LFC], i_LFC) }, + { FLDATA (IARM, lfc_arm, 0) }, + { DRDATA (TIME, lfc_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, lfc_tps, 8), PV_LEFT + REG_HRO }, + { HRDATA (DEVNO, lfc_dib.dno, 8), REG_HRO }, + { NULL } + }; + +MTAB lfc_mod[] = { + { MTAB_XTD|MTAB_VDV, 100, NULL, "50HZ", + &lfc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 120, NULL, "60HZ", + &lfc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &lfc_show_freq, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE lfc_dev = { + "LFC", &lfc_unit, lfc_reg, lfc_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &lfc_reset, + NULL, NULL, NULL, + &lfc_dib, DEV_DISABLE + }; + +/* Precision clock: IO routine */ + +uint32 pic (uint32 dev, uint32 op, uint32 dat) +{ +int32 t; + +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return HW; /* HW capable */ + + case IO_RH: /* read halfword */ + pic_rdp = 0; /* clr ptr */ + return pic_rd_cic (); + + case IO_RD: /* read */ + t = pic_rd_cic (); /* get cic */ + if (pic_rdp) t = t & DMASK8; /* 2nd? get lo */ + else t = (t >> 8) & DMASK8; /* 1st? get hi */ + pic_rdp = pic_rdp ^ 1; /* flip byte ptr */ + return t; + + case IO_WH: /* write halfword */ + pic_wdp = 0; /* clr ptr */ + pic_db = dat; + break; + + case IO_WD: /* write */ + if (pic_wdp) pic_db = (pic_db & 0xFF00) | dat; + else pic_db = (pic_db & 0xFF) | (dat << 8); + pic_wdp = pic_wdp ^ 1; /* flip byte ptr */ + break; + + case IO_SS: /* sense status */ + if (pic_ovf) { /* overflow? */ + pic_ovf = 0; /* clear flag */ + CLR_INT (v_PIC); /* clear intr */ + return STA_OVF; + } + return 0; + + case IO_OC: /* output cmd */ + pic_arm = int_chg (v_PIC, dat, pic_arm); /* upd int ctrl */ + if (dat & CMD_STRT) { /* start? */ + pic_ric = pic_db; /* new ric */ + pic_cic = GET_CTR (pic_ric); /* new cic */ + pic_ovf = 0; /* clear flag */ + sim_cancel (&pic_unit); /* stop clock */ + pic_rdp = pic_wdp = 0; /* init ptrs */ + if (pic_ric & PIC_RATE) pic_sched (TRUE); /* any rate? */ + } /* end if start */ + break; + } /* end case */ + +return 0; +} + +/* Unit service */ + +t_stat pic_svc (UNIT *uptr) +{ +t_bool rate_chg = FALSE; + +if (pic_cnti) pic_cic = 0; /* one shot? */ +pic_cic = pic_cic - pic_decr; /* decrement */ +if (pic_cic <= 0) { /* overflow? */ + if (pic_wdp) pic_ovf = 1; /* broken wr? set flag */ + if (pic_arm) SET_INT (v_PIC); /* if armed, intr */ + if (GET_RATE (pic_ric) != GET_RATE (pic_db)) /* rate change? */ + rate_chg = TRUE; + pic_ric = pic_db; /* new ric */ + pic_cic = GET_CTR (pic_ric); /* new cic */ + if ((pic_ric & PIC_RATE) == 0) return SCPE_OK; + } +pic_sched (rate_chg); +return SCPE_OK; +} + +/* Schedule next interval + + If eff rate < 1ms, or diagnostic mode, count instructions + If eff rate = 1ms, and not diagnostic mode, use timer +*/ + +void pic_sched (t_bool strt) +{ +int32 r, t, intv, intv_usec; + +pic_save = sim_grtime (); /* save start */ +r = pic_map[GET_RATE (pic_ric)]; /* get mapped rate */ +intv = pic_cic? pic_cic: 1; /* get cntr */ +intv_usec = intv * pic_usec[r]; /* cvt to usec */ +if (!(pic_unit.flags & UNIT_DIAG) && /* not diag? */ + ((intv_usec % 1000) == 0)) { /* 1ms multiple? */ + pic_cnti = 0; /* clr mode */ + pic_decr = pic_usec[3 - r]; /* set decrement */ + if (strt) t = sim_rtcn_init (pic_time[3], TMR_PIC); /* init or */ + else t = sim_rtcn_calb (PIC_TPS, TMR_PIC); /* calibrate */ + } +else { + pic_cnti = 1; /* set mode */ + pic_decr = 1; /* decr = 1 */ + t = pic_time[r] * intv; /* interval */ + if (t == 1) t++; /* for diagn */ + } +sim_activate (&pic_unit, t); /* activate */ +return; +} + +/* Read (interpolated) current interval */ + +uint32 pic_rd_cic (void) +{ +if (sim_is_active (&pic_unit) && pic_cnti) { /* running, one shot? */ + uint32 delta = sim_grtime () - pic_save; /* interval */ + uint32 tm = pic_time[pic_map[GET_RATE (pic_ric)]]; /* ticks/intv */ + delta = delta / tm; /* ticks elapsed */ + if (delta >= ((uint32) pic_cic)) return 0; /* cap value */ + return pic_cic - delta; + } +return pic_cic; +} + +/* Reset routine */ + +t_stat pic_reset (DEVICE *dptr) +{ +sim_cancel (&pic_unit); /* cancel unit */ +pic_ric = pic_cic = 0; +pic_db = 0; +pic_ovf = 0; /* clear state */ +pic_cnti = 0; +pic_decr = 1; +pic_rdp = pic_wdp = 0; +CLR_INT (v_PIC); /* clear int */ +CLR_ENB (v_PIC); /* disable int */ +pic_arm = 0; /* disarm int */ +return SCPE_OK; +} + +/* Line clock: IO routine */ + +uint32 lfc (uint32 dev, uint32 op, uint32 dat) +{ +switch (op) { /* case IO op */ + + case IO_ADR: /* select */ + return BY; /* byte only */ + + case IO_OC: /* command */ + lfc_arm = int_chg (v_LFC, dat, lfc_arm); /* upd int ctrl */ + break; + } +return 0; +} + +/* Unit service */ + +t_stat lfc_svc (UNIT *uptr) +{ +lfc_poll = sim_rtcn_calb (lfc_tps, TMR_LFC); /* calibrate */ +sim_activate (uptr, lfc_poll); /* reactivate */ +if (lfc_arm) { /* armed? */ + SET_INT (v_LFC); /* req intr */ + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lfc_reset (DEVICE *dptr) +{ +lfc_poll = sim_rtcn_init (lfc_unit.wait, TMR_LFC); +sim_activate_abs (&lfc_unit, lfc_poll); /* init clock */ +CLR_INT (v_LFC); /* clear int */ +CLR_ENB (v_LFC); /* disable int */ +lfc_arm = 0; /* disarm int */ +return SCPE_OK; +} + +/* Set frequency */ + +t_stat lfc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val != 100) && (val != 120)) return SCPE_IERR; +lfc_tps = val; +return SCPE_OK; +} + +/* Show frequency */ + +t_stat lfc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, (lfc_tps == 100)? "50Hz": "60Hz"); +return SCPE_OK; +} + diff --git a/LGP/lgp_cpu.c b/LGP/lgp_cpu.c new file mode 100644 index 0000000..57235c4 --- /dev/null +++ b/LGP/lgp_cpu.c @@ -0,0 +1,733 @@ +/* lgp_cpu.c: LGP CPU simulator + + Copyright (c) 2004-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu LGP-30 [LGP-21] CPU + + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 04-Sep-05 RMS Fixed missing returns (found by Peter Schorn) + 04-Jan-05 RMS Modified VM pointer setup + + The system state for the LGP-30 [LGP-21] is: + + A<0:31> accumulator + C<0:11> counter (PC) + OVF overflow flag [LGP-21 only] + + The LGP-30 [LGP-21] has just one instruction format: + + 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + |S| |opcode | | operand address | | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + LGP-30 instructions: + + <0,12:15> operation + + 0 stop + 1 A <- M[ea] + 2 M[ea] <- A + 3 M[ea] <- C + 1 + 4 input + 5 A <- A / M[ea] + 6 A <- A * M[ea], low result + 7 A <- A * M[ea], high result + 8 output + 9 A <- A & M[ea] + A C <- ea + B C <- ea if A < 0 + -B C <- ea if (A < 0) || T-switch set + C M[ea] <- A + D M[ea] <- A, A <- 0 + E A <- A + M[ea] + F A <- A - M[ea] + + LGP-21 instructions: + + <0,12:15> operation + + 0 stop; sense and skip + -0 stop; sense overflow and skip + 1 A <- M[ea] + 2 M[ea] <- A + 3 M[ea] <- C + 1 + 4 6b input + -4 4b input + 5 A <- A / M[ea] + 6 A <- A * M[ea], low result + 7 A <- A * M[ea], high result + 8 6b output + -8 4b output + 9 A <- A & M[ea] + A C <- ea + B C <- ea if A < 0 + -B C <- ea if (A < 0) || T-switch set + C M[ea] <- A + D M[ea] <- A, A <- 0 + E A <- A + M[ea] + F A <- A - M[ea] + + The LGP-30 [LGP-21] has 4096 32b words of memory. The low order + bit is always read and stored as 0. The LGP-30 uses a drum for + memory, with 64 tracks of 64 words. The LGP-21 uses a disk for + memory, with 32 tracks of 128 words. + + This routine is the instruction decode routine for the LGP-30 + [LGP-21]. It is called from the simulator control program to + execute instructions in simulated memory, starting at the simulated + PC. It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + STOP instruction + breakpoint encountered + overflow [LGP-30] + I/O error in I/O simulator + + 2. Interrupts. There are no interrupts. + + 3. Non-existent memory. All of memory always exists. + + 4. Adding I/O devices. The LGP-30 could not support additional + I/O devices. The LGP-21 could but none are known. +*/ + +#include "lgp_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = (PC - 1) & AMASK; +#define M16 0xFFFF +#define M32 0xFFFFFFFF +#define NEG(x) ((~(x) + 1) & DMASK) +#define ABS(x) (((x) & SIGN)? NEG (x): (x)) + +uint32 M[MEMSIZE] = { 0 }; /* memory */ +uint32 PC = 0; /* counter */ +uint32 A = 0; /* accumulator */ +uint32 IR = 0; /* instr register */ +uint32 OVF = 0; /* overflow indicator */ +uint32 t_switch = 0; /* transfer switch */ +uint32 bp32 = 0; /* BP32 switch */ +uint32 bp16 = 0; /* BP16 switch */ +uint32 bp8 = 0; /* BP8 switch */ +uint32 bp4 = 0; /* BP4 switch */ +uint32 inp_strt = 0; /* input started */ +uint32 inp_done = 0; /* input done */ +uint32 out_strt = 0; /* output started */ +uint32 out_done = 0; /* output done */ +uint32 lgp21_sov = 0; /* LGP-21 sense pending */ +int32 delay = 0; +int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ + +extern int32 sim_interval; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern int32 sim_step; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_30opt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_fill (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_exec (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_one_inst (uint32 opc, uint32 ir); +uint32 Mul64 (uint32 a, uint32 b, uint32 *low); +t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q); +uint32 I_delay (uint32 opc, uint32 ea, uint32 op); +uint32 shift_in (uint32 a, uint32 dat, uint32 sh4); + +extern t_stat op_p (uint32 dev, uint32 ch); +extern t_stat op_i (uint32 dev, uint32 ch, uint32 sh4); +extern void lgp_vm_init (void); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX+UNIT_IN4B+UNIT_TTSS_D, MEMSIZE) }; + +REG cpu_reg[] = { + { DRDATA (C, PC, 12), REG_VMAD }, + { HRDATA (A, A, 32), REG_VMIO }, + { HRDATA (IR, IR, 32), REG_VMIO }, + { FLDATA (OVF, OVF, 0) }, + { FLDATA (TSW, t_switch, 0) }, + { FLDATA (BP32, bp32, 0) }, + { FLDATA (BP16, bp16, 0) }, + { FLDATA (BP8, bp8, 0) }, + { FLDATA (BP4, bp4, 0) }, + { FLDATA (INPST, inp_strt, 0) }, + { FLDATA (INPDN, inp_done, 0) }, + { FLDATA (OUTST, out_strt, 0) }, + { FLDATA (OUTDN, out_done, 0) }, + { DRDATA (DELAY, delay, 7) }, + { BRDATA (CQ, pcq, 16, 12, PCQ_SIZE), REG_RO + REG_CIRC }, + { HRDATA (CQP, pcq_p, 6), REG_HRO }, + { HRDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_LGP21, UNIT_LGP21, "LGP-21", "LGP21", &cpu_set_model, &cpu_show_model }, + { UNIT_LGP21, 0, "LGP-30", "LGP30", &cpu_set_model, &cpu_show_model }, + { UNIT_TTSS_D, UNIT_TTSS_D, 0, "TRACK" }, + { UNIT_TTSS_D, 0, 0, "NORMAL" }, + { UNIT_LGPH_D, UNIT_LGPH_D, 0, "LGPHEX" }, + { UNIT_LGPH_D, 0, 0, "STANDARDHEX" }, + { UNIT_MANI, UNIT_MANI, NULL, "MANUAL" }, + { UNIT_MANI, 0, NULL, "TAPE" }, + { UNIT_IN4B, UNIT_IN4B, NULL, "4B", &cpu_set_30opt }, + { UNIT_IN4B, 0, NULL, "6B", &cpu_set_30opt }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "INPUT", &cpu_set_30opt_i }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "OUTPUT", &cpu_set_30opt_o }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "EXECUTE", &cpu_set_exec }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "FILL", &cpu_set_fill }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 10, 12, 1, 16, 32, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL + }; + +/* Timing tables */ + +/* Optimization minima and maxima + Z B Y R I D N M P E U T H C A S */ + +static const int32 min_30[16] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 + }; +static const int32 max_30[16] = { + 7, 7, 7, 7, 7, 5, 8, 6, 7, 7, 0, 0, 7, 7, 7, 7 + }; +static const int32 min_21[16] = { + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 + }; +static const int32 max_21[16] = { + 0, 16, 16, 16, 0, 58, 81, 79, 0, 16, 0, 0, 16, 16, 16, 16 + }; + +static const uint32 log_to_phys_30[NSC_30] = { /* drum interlace chart */ + 0, 57, 50, 43, 36, 29, 22, 15, 8 , + 1, 58, 51, 44, 37, 30, 23, 16, 9 , + 2, 59, 52, 45, 38, 31, 24, 17, 10, + 3, 60, 53, 46, 39, 32, 25, 18, 11, + 4, 61, 54, 47, 40, 33, 26, 19, 12, + 5, 62, 55, 48, 41, 32, 27, 20, 13, + 6, 63, 56, 49, 42, 33, 28, 21, 14, + 7 + }; + +static const uint32 log_to_phys_21[NSC_21] = { /* disk interlace chart */ + 0, 64, 57, 121, 50, 114, 43, 107, 36, 100, 29, 93, 22, 86, 15, 79, 8, 72, + 1, 65, 58, 122, 51, 115, 44, 108, 37, 101, 30, 94, 23, 87, 16, 80, 9, 73, + 2, 66, 59, 123, 52, 116, 45, 109, 38, 102, 31, 95, 24, 88, 17, 81, 10, 74, + 3, 67, 60, 124, 53, 117, 46, 110, 39, 103, 32, 96, 25, 89, 18, 82, 11, 75, + 4, 68, 61, 125, 54, 118, 47, 111, 40, 104, 33, 97, 26, 90, 19, 83, 12, 76, + 5, 69, 62, 126, 55, 119, 48, 112, 41, 105, 34, 98, 27, 91, 20, 84, 12, 77, + 6, 70, 63, 127, 56, 120, 49, 113, 42, 106, 35, 99, 28, 92, 21, 85, 13, 78, + 7, 71 + }; + +t_stat sim_instr (void) +{ +t_stat r = 0; +uint32 oPC; + +/* Restore register state */ + +PC = PC & AMASK; /* mask PC */ +sim_cancel_step (); /* defang SCP step */ +if (lgp21_sov) { /* stop sense pending? */ + lgp21_sov = 0; + if (!OVF) PC = (PC + 1) & AMASK; /* ovf off? skip */ + else OVF = 0; /* on? reset */ + } + +/* Main instruction fetch/decode loop */ + +do { + if (sim_interval <= 0) { /* check clock queue */ + if (r = sim_process_event ()) break; + } + + if (delay > 0) { /* delay to next instr */ + delay = delay - 1; /* count down delay */ + sim_interval = sim_interval - 1; + continue; /* skip execution */ + } + + if (sim_brk_summ && /* breakpoint? */ + sim_brk_test (PC, SWMASK ('E'))) { + r = STOP_IBKPT; /* stop simulation */ + break; + } + + IR = Read (oPC = PC); /* get instruction */ + PC = (PC + 1) & AMASK; /* increment PC */ + sim_interval = sim_interval - 1; + + if (r = cpu_one_inst (oPC, IR)) { /* one instr; error? */ + if (r == STOP_STALL) { /* stall? */ + PC = oPC; /* back up PC */ + delay = r = 0; /* no delay */ + } + else break; + } + + if (sim_step && (--sim_step <= 0)) /* do step count */ + r = SCPE_STOP; + + } while (r == 0); /* loop until halted */ +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return r; +} + +/* Execute one instruction */ + +t_stat cpu_one_inst (uint32 opc, uint32 ir) +{ +uint32 ea, op, dat, res, dev, sh4, ch; +t_bool ovf_this_cycle = FALSE; +t_stat reason = 0; + +op = I_GETOP (ir); /* opcode */ +ea = I_GETEA (ir); /* address */ +switch (op) { /* case on opcode */ + +/* Loads, stores, transfers instructions */ + + case OP_B: /* bring */ + A = Read (ea); /* A <- M[ea] */ + delay = I_delay (opc, ea, op); + break; + + case OP_H: /* hold */ + Write (ea, A); /* M[ea] <- A */ + delay = I_delay (opc, ea, op); + break; + + case OP_C: /* clear */ + Write (ea, A); /* M[ea] <- A */ + A = 0; /* A <- 0 */ + delay = I_delay (opc, ea, op); + break; + + case OP_Y: /* store address */ + dat = Read (ea); /* get operand */ + dat = (dat & ~I_EA) | (A & I_EA); /* merge address */ + Write (ea, dat); + delay = I_delay (opc, ea, op); + break; + + case OP_R: /* return address */ + dat = Read (ea); /* get operand */ + dat = (dat & ~I_EA) | (((PC + 1) & AMASK) << I_V_EA); + Write (ea, dat); + delay = I_delay (opc, ea, op); + break; + + case OP_U: /* uncond transfer */ + PCQ_ENTRY; + PC = ea; /* transfer */ + delay = I_delay (opc, ea, op); + break; + + case OP_T: /* conditional transfer */ + if ((A & SIGN) || /* A < 0 or */ + ((ir & SIGN) && t_switch)) { /* -T and Tswitch set? */ + PCQ_ENTRY; + PC = ea; /* transfer */ + } + delay = I_delay (opc, ea, op); + break; + +/* Arithmetic and logical instructions */ + + case OP_A: /* add */ + dat = Read (ea); /* get operand */ + res = (A + dat) & DMASK; /* add */ + if ((~A ^ dat) & (dat ^ res) & SIGN) /* calc overflow */ + ovf_this_cycle = TRUE; + A = res; /* save result */ + delay = I_delay (opc, ea, op); + break; + + case OP_S: /* sub */ + dat = Read (ea); /* get operand */ + res = (A - dat) & DMASK; /* subtract */ + if ((A ^ dat) & (~dat ^ res) & SIGN) /* calc overflow */ + ovf_this_cycle = TRUE; + A = res; + delay = I_delay (opc, ea, op); + break; + + case OP_M: /* multiply high */ + dat = Read (ea); /* get operand */ + A = (Mul64 (A, dat, NULL) << 1) & DMASK; /* multiply */ + delay = I_delay (opc, ea, op); + break; + + case OP_N: /* multiply low */ + dat = Read (ea); /* get operand */ + Mul64 (A, dat, &res); /* multiply */ + A = res; /* keep low result */ + delay = I_delay (opc, ea, op); /* total delay */ + break; + + case OP_D: /* divide */ + dat = Read (ea); /* get operand */ + if (Div32 (A, dat, &A)) ovf_this_cycle = TRUE; /* divide; overflow? */ + delay = I_delay (opc, ea, op); + break; + + case OP_E: /* extract */ + dat = Read (ea); /* get operand */ + A = A & dat; /* and */ + delay = I_delay (opc, ea, op); + break; + +/* IO instructions */ + + case OP_P: /* output */ + if (Q_LGP21) { /* LGP-21 */ + ch = A >> 26; /* char, 6b */ + if (ir & SIGN) ch = (ch & 0x3C) | 2; /* 4b? convert */ + dev = I_GETTK (ir); /* device select */ + } + else { /* LGP-30 */ + ch = I_GETTK (ir); /* char, always 6b */ + dev = Q_OUTPT? DEV_PT: DEV_TT; /* device select */ + } + reason = op_p (dev & DEV_MASK, ch); /* output */ + delay = I_delay (sim_grtime (), ea, op); /* next instruction */ + break; + + case OP_I: /* input */ + if (Q_LGP21) { /* LGP-21 */ + ch = 0; /* initial shift */ + sh4 = ir & SIGN; /* 4b/6b select */ + dev = I_GETTK (ir); /* device select */ + } + else { /* LGP-30 */ + ch = I_GETTK (ir); /* initial shift */ + sh4 = Q_IN4B; /* 4b/6b select */ + dev = Q_INPT? DEV_PT: DEV_TT; /* device select */ + } + if (dev == DEV_SHIFT) /* shift? */ + A = shift_in (A, 0, sh4); /* shift 4/6b */ + else reason = op_i (dev & DEV_MASK, ch, sh4); /* input */ + delay = I_delay (sim_grtime (), ea, op); /* next instruction */ + break; + + case OP_Z: + if (Q_LGP21) { /* LGP-21 */ + if (ea & 0xF80) { /* no stop? */ + if (((ea & 0x800) && !bp32) || /* skip if any */ + ((ea & 0x400) && !bp16) || /* selected switch */ + ((ea & 0x200) && !bp8) || /* is off */ + ((ea & 0x100) && !bp4) || /* or if */ + ((ir & SIGN) && !OVF)) /* ovf sel and off */ + PC = (PC + 1) & AMASK; + if (ir & SIGN) OVF = 0; /* -Z? clr overflow */ + } + else { /* stop */ + lgp21_sov = (ir & SIGN)? 1: 0; /* pending sense? */ + reason = STOP_STOP; /* stop */ + } + } + else { /* LGP-30 */ + if (out_done) out_done = 0; /* P complete? */ + else if (((ea & 0x800) && bp32) || /* bpt switch set? */ + ((ea & 0x400) && bp16) || + ((ea & 0x200) && bp8) || + ((ea & 0x100) && bp4)) ; /* don't stop or stall */ + else if (out_strt) reason = STOP_STALL; /* P pending? stall */ + else reason = STOP_STOP; /* no, stop */ + } + delay = I_delay (sim_grtime (), ea, op); /* next instruction */ + break; /* end switch */ + } + +if (ovf_this_cycle) { + if (Q_LGP21) OVF = 1; /* LGP-21? set OVF */ + else reason = STOP_OVF; /* LGP-30? stop */ + } +return reason; +} + +/* Support routines */ + +uint32 Read (uint32 ea) +{ +return M[ea] & MMASK; +} + +void Write (uint32 ea, uint32 dat) +{ +M[ea] = dat & MMASK; +return; +} + +/* Input shift */ + +uint32 shift_in (uint32 a, uint32 dat, uint32 sh4) +{ +if (sh4) return (((a << 4) | (dat >> 2)) & DMASK); +return (((a << 6) | dat) & DMASK); +} + +/* 32b * 32b multiply, signed */ + +uint32 Mul64 (uint32 a, uint32 b, uint32 *low) +{ +uint32 sgn = a ^ b; +uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; + +if ((a == 0) || (b == 0)) { /* zero argument? */ + if (low) *low = 0; + return 0; + } +a = ABS (a); +b = ABS (b); +ah = (a >> 16) & M16; /* split operands */ +bh = (b >> 16) & M16; /* into 16b chunks */ +al = a & M16; +bl = b & M16; +rhi = ah * bh; /* high result */ +rmid1 = ah * bl; +rmid2 = al * bh; +rlo = al * bl; +rhi = rhi + ((rmid1 >> 16) & M16) + ((rmid2 >> 16) & M16); +rmid1 = (rlo + (rmid1 << 16)) & M32; /* add mid1 to lo */ +if (rmid1 < rlo) rhi = rhi + 1; /* carry? incr hi */ +rmid2 = (rmid1 + (rmid2 << 16)) & M32; /* add mid2 to to */ +if (rmid2 < rmid1) rhi = rhi + 1; /* carry? incr hi */ +if (sgn & SIGN) { /* result negative? */ + rmid2 = NEG (rmid2); /* negate */ + rhi = (~rhi + (rmid2 == 0)) & M32; + } +if (low) *low = rmid2; /* low result */ +return rhi & M32; +} + +/* 32b/32b divide (done as 32b'0/32b) */ + +t_bool Div32 (uint32 dvd, uint32 dvr, uint32 *q) +{ +uint32 sgn = dvd ^ dvr; +uint32 i, quo; + +dvd = ABS (dvd); +dvr = ABS (dvr); +if (dvd >= dvr) return TRUE; +for (i = quo = 0; i < 31; i++) { /* 31 iterations */ + quo = quo << 1; /* shift quotient */ + dvd = dvd << 1; /* shift dividend */ + if (dvd >= dvr) { /* step work? */ + dvd = (dvd - dvr) & M32; /* subtract dvr */ + quo = quo + 1; + } + } +quo = (quo + 1) & MMASK; /* round low bit */ +if (sgn & SIGN) quo = NEG (quo); /* result -? */ +if (q) *q = quo; /* return quo */ +return FALSE; /* no overflow */ +} + +/* Rotational delay */ + +uint32 I_delay (uint32 opc, uint32 ea, uint32 op) +{ +uint32 tmin = Q_LGP21? min_21[op]: min_30[op]; +uint32 tmax = Q_LGP21? max_21[op]: max_30[op]; +uint32 nsc, curp, newp, oprp, pcdelta, opdelta; + +if (Q_LGP21) { /* LGP21 */ + nsc = NSC_21; /* full rotation delay */ + curp = log_to_phys_21[opc & SCMASK_21]; /* current phys pos */ + newp = log_to_phys_21[PC & SCMASK_21]; /* new PC phys pos */ + oprp = log_to_phys_21[ea & SCMASK_21]; /* ea phys pos */ + pcdelta = (newp - curp + NSC_21) & SCMASK_21; + opdelta = (oprp - curp + NSC_21) & SCMASK_21; + } +else { + nsc = NSC_30; + curp = log_to_phys_30[opc & SCMASK_30]; + newp = log_to_phys_30[PC & SCMASK_30]; + oprp = log_to_phys_30[ea & SCMASK_30]; + pcdelta = (newp - curp + NSC_30) & SCMASK_30; + opdelta = (oprp - curp + NSC_30) & SCMASK_30; + } +if (tmax == 0) { /* skip ea calc? */ + if (pcdelta >= tmin) return pcdelta - 1; /* new PC >= min? */ + return pcdelta + nsc - 1; + } +if ((opdelta >= tmin) && (opdelta <= tmax)) return pcdelta - 1; +return pcdelta + nsc - 1; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +OVF = 0; +inp_strt = 0; +inp_done = 0; +out_strt = 0; +out_done = 0; +lgp21_sov = 0; +delay = 0; +lgp_vm_init (); +pcq_r = find_reg ("CQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Validate option, must be LGP30 */ + +t_stat cpu_set_30opt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (Q_LGP21) return SCPE_ARG; +return SCPE_OK; +} + +/* Validate input option, must be LGP30 */ + +t_stat cpu_set_30opt_i (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (Q_LGP21 || (cptr == NULL)) return SCPE_ARG; +if (strcmp (cptr, "TTI") == 0) uptr->flags = uptr->flags & ~UNIT_INPT; +else if (strcmp (cptr, "PTR") == 0) uptr->flags = uptr->flags | UNIT_INPT; +else return SCPE_ARG; +return SCPE_OK; +} + +/* Validate output option, must be LGP30 */ + +t_stat cpu_set_30opt_o (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (Q_LGP21 || (cptr == NULL)) return SCPE_ARG; +if (strcmp (cptr, "TTO") == 0) uptr->flags = uptr->flags & ~UNIT_OUTPT; +else if (strcmp (cptr, "PTP") == 0) uptr->flags = uptr->flags | UNIT_OUTPT; +else return SCPE_ARG; +return SCPE_OK; +} + +/* Set CPU to LGP21 or LPG30 */ + +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val) uptr->flags = uptr->flags & ~(UNIT_IN4B|UNIT_INPT|UNIT_OUTPT); +return reset_all (0); +} + +/* Show CPU type and all options */ + +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fputs (Q_LGP21? "LGP-21": "LGP-30", st); +if (uptr->flags & UNIT_TTSS_D) fputs (", track/sector", st); +if (uptr->flags & UNIT_LGPH_D) fputs (", LGP hex", st); +fputs (Q_MANI? ", manual": ", tape", st); +if (!Q_LGP21) { + fputs (Q_IN4B? ", 4b": ", 6b", st); + fputs (Q_INPT? ", in=PTR": ", in=TTI", st); + fputs (Q_OUTPT? ", out=PTP": ", out=TTO", st); + } +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = Read (addr); +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +Write (addr, val); +return SCPE_OK; +} + +/* Execute */ + +t_stat cpu_set_exec (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 inst; +t_stat r; + +if (cptr) { + inst = get_uint (cptr, 16, DMASK, &r); + if (r != SCPE_OK) return r; + } +else inst = IR; +while ((r = cpu_one_inst (PC, inst)) == STOP_STALL) { + sim_interval = 0; + if (r = sim_process_event ()) return r; + } +return r; +} + +/* Fill */ + +t_stat cpu_set_fill (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 inst; +t_stat r; + +if (cptr) { + inst = get_uint (cptr, 16, DMASK, &r); + if (r != SCPE_OK) return r; + IR = inst; + } +else IR = A; +return SCPE_OK; +} diff --git a/LGP/lgp_defs.h b/LGP/lgp_defs.h new file mode 100644 index 0000000..d8b2d67 --- /dev/null +++ b/LGP/lgp_defs.h @@ -0,0 +1,135 @@ +/* lgp_defs.h: LGP simulator definitions + + Copyright (c) 2004-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#ifndef _LGP_DEFS_H_ +#define _LGP_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_STOP 1 /* STOP */ +#define STOP_IBKPT 2 /* breakpoint */ +#define STOP_OVF 3 /* overflow */ +#define STOP_NXDEV 4 /* non-existent device */ +#define STOP_STALL 5 /* IO stall */ + +/* Memory */ + +#define MEMSIZE 4096 /* memory size */ +#define AMASK 0xFFF /* addr mask */ +#define NTK_30 64 +#define NSC_30 64 +#define SCMASK_30 0x03F /* sector mask */ +#define NTK_21 32 +#define NSC_21 128 +#define SCMASK_21 0x07F +#define RPM 4000 /* rev/minutes */ +#define WPS ((NSC_30 * RPM) / 60) /* words/second */ + +/* Architectural constants */ + +#define SIGN 0x80000000 /* sign */ +#define DMASK 0xFFFFFFFF /* data mask */ +#define MMASK 0xFFFFFFFE /* memory mask */ + +/* Instruction format */ + +#define I_M_OP 0xF /* opcode */ +#define I_V_OP 16 +#define I_OP (I_M_OP << I_V_OP) +#define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) +#define I_M_EA AMASK /* address */ +#define I_V_EA 2 +#define I_EA (I_M_EA << I_V_EA) +#define I_GETEA(x) (((x) >> I_V_EA) & I_M_EA) +#define I_M_TK 0x3F /* LGP-30 char */ +#define I_V_TK 8 /* LGP-21 device */ +#define I_GETTK(x) (((x) >> I_V_TK) & I_M_TK) + +/* Unit flags */ + +#define UNIT_V_LGP21 (UNIT_V_UF + 0) +#define UNIT_V_MANI (UNIT_V_UF + 1) +#define UNIT_V_INPT (UNIT_V_UF + 2) +#define UNIT_V_OUTPT (UNIT_V_UF + 3) +#define UNIT_V_IN4B (UNIT_V_UF + 4) +#define UNIT_V_TTSS_D (UNIT_V_UF + 5) +#define UNIT_V_LGPH_D (UNIT_V_UF + 6) +#define UNIT_V_FLEX_D (UNIT_V_UF + 7) /* Flex default */ +#define UNIT_V_FLEX (UNIT_V_UF + 8) /* Flex format */ +#define UNIT_V_NOCS (UNIT_V_UF + 9) /* ignore cond stop */ +#define UNIT_LGP21 (1u << UNIT_V_LGP21) +#define UNIT_MANI (1u << UNIT_V_MANI) +#define UNIT_INPT (1u << UNIT_V_INPT) +#define UNIT_OUTPT (1u << UNIT_V_OUTPT) +#define UNIT_IN4B (1u << UNIT_V_IN4B) +#define UNIT_TTSS_D (1u << UNIT_V_TTSS_D) +#define UNIT_LGPH_D (1u << UNIT_V_LGPH_D) +#define UNIT_FLEX_D (1u << UNIT_V_FLEX_D) +#define UNIT_FLEX (1u << UNIT_V_FLEX) +#define UNIT_NOCS (1u << UNIT_V_NOCS) +#define Q_LGP21 (cpu_unit.flags & UNIT_LGP21) +#define Q_MANI (cpu_unit.flags & UNIT_MANI) +#define Q_INPT (cpu_unit.flags & UNIT_INPT) +#define Q_OUTPT (cpu_unit.flags & UNIT_OUTPT) +#define Q_IN4B (cpu_unit.flags & UNIT_IN4B) + +/* IO return */ + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Significant characters */ + +#define FLEX_LC 0x04 +#define FLEX_UC 0x08 +#define FLEX_CR 0x10 +#define FLEX_BS 0x14 +#define FLEX_CSTOP 0x20 +#define FLEX_DEL 0x3F + +/* LGP-21 device assignments */ + +#define DEV_PT 0 +#define DEV_TT 2 +#define DEV_MASK 0x1F +#define DEV_SHIFT 62 + +/* Instructions */ + +enum opcodes { + OP_Z, OP_B, OP_Y, OP_R, + OP_I, OP_D, OP_N, OP_M, + OP_P, OP_E, OP_U, OP_T, + OP_H, OP_C, OP_A, OP_S + }; + +/* Prototypes */ + +uint32 Read (uint32 ea); +void Write (uint32 ea, uint32 dat); + +#endif diff --git a/LGP/lgp_stddev.c b/LGP/lgp_stddev.c new file mode 100644 index 0000000..080e4ac --- /dev/null +++ b/LGP/lgp_stddev.c @@ -0,0 +1,659 @@ +/* lgp_stddev.c: LGP-30 standard devices + + Copyright (c) 2004-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti typewriter input (keyboard and reader) + tto typewriter output (printer and punch) + ptr high speed reader + ptpp high speed punch +*/ + +#include "lgp_defs.h" +#include + +uint32 tt_wait = WPS / 10; +uint32 tti_buf = 0; +uint32 tti_rdy = 0; +uint32 tto_uc = 0; +uint32 tto_buf = 0; +uint32 ttr_stopioe = 1; +uint32 ptr_rdy = 0; +uint32 ptr_stopioe = 1; +uint32 ptp_stopioe = 1; + +extern uint32 A; +extern uint32 inp_strt, inp_done; +extern uint32 out_strt, out_done; +extern UNIT cpu_unit; +extern int32 sim_switches; + +t_stat tti_svc (UNIT *uptr); +t_stat ttr_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *uptr); +t_stat tto_reset (DEVICE *uptr); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *uptr); +t_stat ptp_reset (DEVICE *uptr); +t_stat tap_attach (UNIT *uptr, char *cptr); +t_stat tap_attable (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat read_reader (UNIT *uptr, int32 stop, int32 *c); +t_stat write_tto (int32 flex); +t_stat write_punch (UNIT *uptr, int32 flex); +t_stat tti_rdrss (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat punch_feed (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat send_start (UNIT *uptr, int32 val, char *cptr, void *desc); + +extern uint32 shift_in (uint32 a, uint32 dat, uint32 sh4); + +/* Conversion tables */ + +const int32 flex_to_ascii[128] = { + -1 , 'z', '0', ' ', '>', 'b', '1', '-', + '<' , 'y', '2', '+', '|', 'r', '3', ';', + '\r', 'i', '4', '/','\\', 'd', '5', '.', + '\t', 'n', '6', ',', -1 , 'm', '7', 'v', + '\'', 'p', '8', 'o', -1 , 'e', '9', 'x', + -1 , 'u', 'f', -1 , -1 , 't', 'g', -1 , + -1 , 'h', 'j', -1 , -1 , 'c', 'k', -1 , + -1 , 'a', 'q', -1 , -1 , 's', 'w', 0 , + + -1 , 'Z', ')', ' ', -1 , 'B', 'L', '_', + -1 , 'Y', '*', '=', '|', 'R', '"', ':', + '\r', 'I', '^', '?','\\', 'D', '%', ']', + '\t', 'N', '$', '[', -1 , 'M', '~', 'V', + '\'', 'P', '#', 'O', -1 , 'E', '(', 'X', + -1 , 'U', 'F', -1 , -1 , 'T', 'G', -1 , + -1 , 'H', 'J', -1 , -1 , 'C', 'K', -1 , + -1 , 'A', 'Q', -1 , -1 , 'S', 'W', 0 + }; + +const int32 ascii_to_flex[128] = { + -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , + 024, 030, -1 , -1 , -1 , 020, -1 , -1 , + -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , + -1 , -1 , -1 , -1 , -1 , -1 , -1 , -1 , + 003, -1 , 016, 042, 032, 026, -1 , 040, + 046, 001, 012, 013, 033, 007, 027, 023, + 002, 006, 012, 016, 022, 026, 032, 036, + 042, 046, 017, 017, 004, 013, 010, 023, + -1 , 071, 005, 065, 025, 045, 052, 056, + 061, 021, 062, 066, 006, 035, 031, 043, + 041, 072, 015, 075, 055, 051, 037, 076, + 047, 011, 001, 033, -1 , 027, 022, 007, + - 1, 071, 005, 065, 025, 045, 052, 056, + 061, 021, 062, 066, 006, 035, 031, 043, + 041, 072, 015, 075, 055, 051, 037, 076, + 047, 011, 001, -1 , 014, -1 , 036, 077 + }; + +static const uint8 flex_inp_valid[64] = { + 1, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1 + }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_mod TTI modifier list + tti_reg TTI register list +*/ + +UNIT tti_unit[] = { + { UDATA (&tti_svc, 0, 0) }, + { UDATA (&ttr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0) } + }; + +REG tti_reg[] = { + { HRDATA (BUF, tti_buf, 6) }, + { FLDATA (RDY, tti_rdy, 0) }, + { DRDATA (KPOS, tti_unit[0].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (RPOS, tti_unit[1].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tt_wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, ttr_stopioe, 0) }, + { NULL } + }; + +MTAB tti_mod[] = { + { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, + { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, + "file is Flex", NULL }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT, + "file is ASCII", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, + "default is Flex", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, + "default is ASCII", NULL }, + { UNIT_ATTABLE+UNIT_NOCS, UNIT_ATTABLE+UNIT_NOCS, + "ignore conditional stop", "NOCSTOP", &tap_attable }, + { UNIT_ATTABLE+UNIT_NOCS, UNIT_ATTABLE , + NULL, "CSTOP", &tap_attable }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "START", &send_start }, + { MTAB_XTD|MTAB_VDV, 1, NULL, "RSTART", &tti_rdrss }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "RSTOP", &tti_rdrss }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", tti_unit, tti_reg, tti_mod, + 2, 10, 31, 1, 16, 7, + NULL, NULL, &tti_reset, + NULL, &tap_attach, NULL + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_mod TTO modifier list + tto_reg TTO register list +*/ + +UNIT tto_unit[] = { + { UDATA (&tto_svc, 0, 0) }, + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } + }; + +REG tto_reg[] = { + { HRDATA (BUF, tto_buf, 6) }, + { FLDATA (UC, tto_uc, 0) }, + { DRDATA (TPOS, tto_unit[0].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (PPOS, tto_unit[1].pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tt_wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, + { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, + "file is Flex", NULL }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT, + "file is ASCII", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, + "default is Flex", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, + "default is ASCII", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "FEED", &punch_feed }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", tto_unit, tto_reg, tto_mod, + 2, 10, 31, 1, 16, 7, + NULL, NULL, &tto_reset, + NULL, &tap_attach, NULL + }; + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_mod PTR modifier list + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), WPS / 200 + }; + +REG ptr_reg[] = { + { HRDATA (BUF, ptr_unit.buf, 6) }, + { FLDATA (RDY, ptr_rdy, 0) }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } + }; + +MTAB ptr_mod[] = { + { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, + { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, + "file is Flex", NULL }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT, + "file is ASCII", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, + "default is Flex", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, + "default is ASCII", NULL }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 16, 7, + NULL, NULL, &ptr_reset, + NULL, &tap_attach, NULL + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_mod PTP modifier list + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), WPS / 20 + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +MTAB ptp_mod[] = { + { UNIT_FLEX_D, UNIT_FLEX_D, NULL, "FLEX", &tap_attable }, + { UNIT_FLEX_D, 0, NULL, "ASCII", &tap_attable }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT+UNIT_FLEX, + "file is Flex", NULL }, + { UNIT_ATT+UNIT_FLEX, UNIT_ATT, + "file is ASCII", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE+UNIT_FLEX, + "default is Flex", NULL }, + { UNIT_ATTABLE+UNIT_ATT+UNIT_FLEX, UNIT_ATTABLE, + "default is ASCII", NULL }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 16, 7, + NULL, NULL, &ptp_reset, + NULL, &tap_attach, NULL + }; + +/* Input instruction */ + +void op_i_strt (uint32 dev) +{ +switch (dev) { /* case on device */ + + case DEV_PT: /* ptr */ + sim_activate (&ptr_unit, ptr_unit.wait); /* activate */ + break; + + case DEV_TT: /* tti/ttr */ + if (Q_MANI) sim_putchar ('`'); /* manual input? */ + else sim_activate (&tti_unit[1], tt_wait); /* no, must be ttr */ + break; + } +return; +} + +t_stat op_i (uint32 dev, uint32 ch, uint32 sh4) +{ +if (Q_LGP21 && out_strt) return STOP_STALL; /* LGP-21? must be idle */ +if (!inp_strt) { /* input started? */ + inp_strt = 1; /* no, set start */ + inp_done = 0; /* clear done */ + A = shift_in (A, ch, sh4); + tti_rdy = ptr_rdy = 0; /* no input */ + if (Q_LGP21 || Q_INPT) op_i_strt (dev); /* LGP-21 or PTR? start */ + } +switch (dev) { /* case on device */ + + case DEV_PT: /* ptr */ + if (ptr_rdy) { /* char ready? */ + ptr_rdy = 0; /* reset ready */ + if ((ptr_unit.buf != FLEX_DEL) && /* ignore delete and */ + (!Q_LGP21 || ((ptr_unit.buf & 3) == 2))) /* LGP-21 4b? zone != 2 */ + A = shift_in (A, ptr_unit.buf, sh4); /* shift data in */ + } + break; + + case DEV_TT: /* tti/ttr */ + if (tti_rdy) { /* char ready? */ + tti_rdy = 0; /* reset ready */ + if ((tti_buf != FLEX_DEL) && /* ignore delete and */ + (!Q_LGP21 || ((tti_buf & 3) != 0))) /* LGP-21 4b? zone == 0 */ + A = shift_in (A, tti_buf, sh4); /* shift data in */ + } + break; + + default: /* nx device */ + return STOP_NXDEV; /* return error */ + } + +if (inp_done) { /* done? */ + inp_strt = inp_done = 0; /* clear start, done */ + return SCPE_OK; /* no stall */ + } +return STOP_STALL; /* stall */ +} + +/* Terminal keyboard unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c, flex; + +sim_activate (uptr, tt_wait); /* continue poll */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +flex = ascii_to_flex[c & 0x1FF]; /* cvt to flex */ +if (flex > 0) { /* it's a typewriter... */ + write_tto (flex); /* always echos */ + if (tto_unit[1].flags & UNIT_ATT) /* ttp attached? */ + write_punch (&tto_unit[1], tto_buf); /* punch to ttp */ + } +else write_tto ('\a'); /* don't echo bad */ +if (Q_MANI && (flex > 0) && flex_inp_valid[flex]) { /* wanted, valid? */ + if (flex == FLEX_CSTOP) inp_done = 1; /* conditional stop? */ + else tti_rdy = 1; /* no, set ready */ + tti_buf = flex; /* save char */ + uptr->pos = uptr->pos + 1; + } +return SCPE_OK; +} + +/* Terminal reader unit service */ + +t_stat ttr_svc (UNIT *uptr) +{ +t_stat r; + +if (r = read_reader (uptr, ttr_stopioe, (int32 *) &tti_buf)) return r; +if (!(uptr->flags & UNIT_NOCS) && /* cstop enable? */ + (tti_buf == FLEX_CSTOP)) inp_done = 1; /* cond stop? */ +else { + tti_rdy = 1; /* no, set ready */ + sim_activate (uptr, tt_wait); /* cont reading */ + } +write_tto (tti_buf); /* echo to tto */ +if (tto_unit[1].flags & UNIT_ATT) /* ttp attached? */ + return write_punch (&tto_unit[1], tti_buf); /* punch to ttp */ +return SCPE_OK; +} + +/* Paper tape reader unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +t_stat r; + +if (r = read_reader (uptr, ptr_stopioe, &uptr->buf)) return r; +if (uptr->buf == FLEX_CSTOP) inp_done = 1; /* cond stop? */ +else { + ptr_rdy = 1; /* no, set ready */ + sim_activate (uptr, uptr->wait); /* cont reading */ + } +return SCPE_OK; +} + +/* Output instruction */ + +t_stat op_p (uint32 dev, uint32 ch) +{ +switch (dev) { /* case on device */ + + case DEV_PT: /* paper tape punch */ + if (sim_is_active (&ptp_unit)) /* busy? */ + return (Q_LGP21? STOP_STALL: SCPE_OK); /* LGP-21: stall */ + ptp_unit.buf = ch; /* save char */ + sim_activate (&ptp_unit, ptp_unit.wait); /* activate ptp */ + break; + + case DEV_TT: /* typewriter */ + if (ch == 0) { /* start input? */ + if (!Q_LGP21 && !Q_INPT) /* ignore if LGP-21, ptr */ + op_i_strt (DEV_TT); /* start tti */ + return SCPE_OK; /* no stall */ + } + if (sim_is_active (&tto_unit[0])) /* busy? */ + return (Q_LGP21? STOP_STALL: SCPE_OK); /* LGP-21: stall */ + tto_buf = ch; /* save char */ + sim_activate (&tto_unit[0], tt_wait); /* activate tto */ + break; + + default: /* unknown */ + return STOP_NXDEV; /* return error */ + } + +if (out_strt == 0) { /* output started? */ + out_strt = 1; /* flag start */ + out_done = 0; /* clear done */ + } +return SCPE_OK; /* no stall */ +} + +/* Terminal printer unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +t_stat r; + +if ((r = write_tto (tto_buf)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, tt_wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } +out_strt = 0; +out_done = 1; +if (tto_unit[1].flags & UNIT_ATT) /* ttp attached? */ + return write_punch (&tto_unit[1], tto_buf); /* punch to ttp */ +return SCPE_OK; +} + +/* Paper tape punch unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +out_strt = 0; +out_done = 1; +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); /* error */ +return write_punch (uptr, uptr->buf); /* write to ptp */ +} + +/* Utility routines */ + +t_stat read_reader (UNIT *uptr, int32 stop, int32 *fl) +{ +int32 ch, flex; + +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (stop, SCPE_UNATT); +do { + if ((ch = getc (uptr->fileref)) == EOF) { /* read char */ + if (feof (uptr->fileref)) { /* err or eof? */ + if (stop) printf ("Reader end of file\n"); + else return SCPE_OK; + } + else perror ("Reader I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + if (uptr->flags & UNIT_FLEX) /* transposed flex? */ + flex = ((ch << 1) | (ch >> 5)) & 0x3F; /* undo 612345 */ + else if (ch == '#') { /* encoded? */ + int32 d1 = getc (uptr->fileref); /* get 2 digits */ + int32 d2 = getc (uptr->fileref); + if ((d1 == EOF) || (d2 == EOF)) { /* error? */ + if (feof (uptr->fileref)) { /* eof? */ + if (stop) printf ("Reader end of file\n"); + else return SCPE_OK; + } + else perror ("Reader I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + flex = (((d1 - '0') * 10) + (d2 - '0')) & 0x3F; + uptr->pos = uptr->pos + 2; + } + else flex = ascii_to_flex[ch & 0x7F]; /* convert */ + uptr->pos = uptr->pos + 1; + } while (flex < 0); /* until valid */ +*fl = flex; /* return char */ +return SCPE_OK; +} + +t_stat write_tto (int32 flex) +{ +int32 ch; +t_stat r; + +if (flex == FLEX_UC) tto_uc = 1; /* UC? set state */ +else if (flex == FLEX_LC) tto_uc = 0; /* LC? set state */ +else { + if (flex == FLEX_BS) ch = '\b'; /* backspace? */ + else ch = flex_to_ascii[flex | (tto_uc << 6)]; /* cvt flex to ascii */ + if (ch > 0) { /* legit? */ + if (r = sim_putchar_s (ch)) return r; /* write char */ + tto_unit[0].pos = tto_unit[0].pos + 1; + if (flex == FLEX_CR) { /* cr? */ + sim_putchar ('\n'); /* add lf */ + tto_unit[0].pos = tto_unit[0].pos + 1; + } + } + } +return SCPE_OK; +} + +t_stat write_punch (UNIT *uptr, int32 flex) +{ +int32 c, sta; + +if (uptr->flags & UNIT_FLEX) /* transposed flex? */ + c = ((flex >> 1) | (flex << 5)) & 0x3F; /* reorder to 612345 */ +else c = flex_to_ascii[flex]; /* convert to ASCII */ +if (c >= 0) sta = fputc (c, uptr->fileref); /* valid? */ +else sta = fprintf (uptr->fileref, "#%02d", flex); /* no, encode */ +if (sta == EOF) { /* error? */ + perror ("Punch I/O error"); /* error? */ + clearerr (uptr->fileref); + return SCPE_IOERR; + } +uptr->pos = uptr->pos + ((c >= 0)? 1: 3); /* incr position */ +return SCPE_OK; +} + +/* Reset routines */ + +t_stat tti_reset (DEVICE *dptr) +{ +sim_activate (&tti_unit[0], tt_wait); +sim_cancel (&tti_unit[1]); +tti_buf = 0; +tti_rdy = 0; +return SCPE_OK; +} + +t_stat tto_reset (DEVICE *dptr) +{ +sim_cancel (&tto_unit[0]); +tto_buf = 0; +tto_uc = 0; +return SCPE_OK; +} + +t_stat ptr_reset (DEVICE *dptr) +{ +sim_cancel (&ptr_unit); +ptr_unit.buf = 0; +ptr_rdy = 0; +return SCPE_OK; +} + +t_stat ptp_reset (DEVICE *dptr) +{ +sim_cancel (&ptp_unit); +ptp_unit.buf = 0; +return SCPE_OK; +} + +/* Attach paper tape unit */ + +t_stat tap_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +if ((r = attach_unit (uptr,cptr)) != SCPE_OK) return r; +if ((sim_switches & SWMASK ('F')) || + ((uptr->flags & UNIT_FLEX_D) && !(sim_switches & SWMASK ('A')))) + uptr->flags = uptr->flags | UNIT_FLEX; +else uptr->flags = uptr->flags & ~UNIT_FLEX; +return SCPE_OK; +} + +/* Validate unit is attachable */ + +t_stat tap_attable (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATTABLE) return SCPE_OK; +return SCPE_NOFNC; +} + +/* Typewriter reader start/stop */ + +t_stat tti_rdrss (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val) { + if ((tti_unit[1].flags & UNIT_ATT) == 0) return SCPE_UNATT; + sim_activate (&tti_unit[1], tt_wait); + } +else sim_cancel (&tti_unit[1]); +return SCPE_OK; +} + +/* Punch feed routine */ + +t_stat punch_feed (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 cnt; +t_stat r; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; +if (cptr) { + cnt = (int32) get_uint (cptr, 10, 512, &r); + if ((r != SCPE_OK) || (cnt == 0)) return SCPE_ARG; + } +else cnt = 10; +while (cnt-- > 0) { + r = write_punch (uptr, 0); + if (r != SCPE_OK) return r; + } +return SCPE_OK; +} + +/* Send start signal */ + +t_stat send_start (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (inp_strt) inp_done = 1; +else if (out_strt) out_done = 1; +return SCPE_OK; +} diff --git a/LGP/lgp_sys.c b/LGP/lgp_sys.c new file mode 100644 index 0000000..e52c5ff --- /dev/null +++ b/LGP/lgp_sys.c @@ -0,0 +1,372 @@ +/* lgp_sys.c: LGP-30 simulator interface + + Copyright (c) 2004-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 04-Jan-05 RMS Modified VM pointer setup +*/ + +#include "lgp_defs.h" +#include + +t_stat parse_sym_m (char *cptr, t_value *val, int32 sw); +void lgp_init (void); + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE tti_dev, tto_dev; +extern DEVICE ptr_dev, ptp_dev; +extern REG cpu_reg[]; +extern uint32 M[]; +extern uint32 PC; +extern uint32 ts_flag; +extern int32 sim_switches; +extern int32 flex_to_ascii[128], ascii_to_flex[128]; + +extern void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr); +extern t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "LGP30"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &tti_dev, + &tto_dev, + &ptr_dev, + &ptp_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "STOP", + "Breakpoint", + "Arithmetic overflow" + }; + +/* Binary loader - implements a restricted form of subroutine 10.4 + + Switches: + -t, input file is transposed Flex + -n, no checksums on v commands (10.0 compatible) + default is ASCII encoded Flex + Commands (in bits 0-3): + (blank) instruction + + command (not supported) + ; start fill + / set modifier + . stop and transfer + , hex words + v hex fill (checksummed unless -n) + 8 negative instruction +*/ + +/* Utility routine - read characters until ' (conditional stop) */ + +t_stat load_getw (FILE *fi, uint32 *wd) +{ +int32 flex, c; + +*wd = 0; +while ((c = fgetc (fi)) != EOF) { + if (sim_switches & SWMASK ('T')) + flex = ((c << 1) | (c >> 5)) & 0x3F; + else flex = ascii_to_flex[c & 0x7F]; + if ((flex == FLEX_CR) || (flex == FLEX_DEL) || + (flex == FLEX_UC) || (flex == FLEX_LC) || + (flex == FLEX_BS) || (flex < 0)) continue; + if (flex == FLEX_CSTOP) return SCPE_OK; + *wd = (*wd << 4) | ((flex >> 2) & 0xF); + } +return SCPE_FMT; +} + +/* Utility routine - convert ttss decimal address to binary */ + +t_stat load_geta (uint32 wd, uint32 *ad) +{ +uint32 n1, n2, n3, n4, tr, sc; + +n1 = (wd >> 12) & 0xF; +n2 = (wd >> 8) & 0xF; +n3 = (wd >> 4) & 0xF; +n4 = wd & 0xF; +if ((n2 > 9) || (n4 > 9)) return SCPE_ARG; +tr = (n1 * 10) + n2; +sc = (n3 * 10) + n4; +if ((tr >= NTK_30) || (sc >= NSC_30)) return SCPE_ARG; +*ad = (tr * NSC_30) + sc; +return SCPE_OK; +} + +/* Loader proper */ + +t_stat sim_load (FILE *fi, char *cptr, char *fnam, int flag) +{ +uint32 wd, origin, amod, csum, cnt, tr, sc, ad, cmd; + +origin = amod = 0; +for (;;) { /* until stopped */ + if (load_getw (fi, &wd)) break; /* get ctrl word */ + cmd = (wd >> 28) & 0xF; /* get <0:3> */ + switch (cmd) { /* decode <0:3> */ + + case 0x2: /* + command */ + return SCPE_FMT; + + case 0x3: /* ; start fill */ + if (load_geta (wd, &origin)) return SCPE_FMT; /* origin = addr */ + break; + + case 0x4: /* / set modifier */ + if (load_geta (wd, &amod)) return SCPE_FMT; /* modifier = addr */ + break; + + case 0x5: /* . transfer */ + if (load_geta (wd, &PC)) return SCPE_FMT; /* PC = addr */ + return SCPE_OK; /* done! */ + + case 0x6: /* hex words */ + if (load_geta (wd, &cnt)) return SCPE_FMT; /* count = addr */ + if ((cnt == 0) || (cnt > 63)) return SCPE_FMT; + while (cnt--) { /* fill hex words */ + if (load_getw (fi, &wd)) return SCPE_FMT; + Write (origin, wd); + origin = (origin + 1) & AMASK; + } + break; + + case 0x7: /* hex fill */ + cnt = (wd >> 16) & 0xFFF; /* hex count */ + tr = (wd >> 8) & 0xFF; /* hex track */ + sc = wd & 0xFF; /* hex sector */ + if ((cnt == 0) || (cnt > 0x7FF) || /* validate */ + (tr >= NTK_30) || (sc >= NSC_30)) return SCPE_ARG; + ad = (tr * NSC_30) + sc; /* decimal addr */ + for (csum = 0; cnt; cnt--) { /* fill words */ + if (load_getw (fi, &wd)) return SCPE_FMT; + Write (ad, wd); + csum = (csum + wd) & MMASK; + ad = (ad + 1) & AMASK; + } + if (!(sim_switches & SWMASK ('N'))) { /* unless -n, csum */ + if (load_getw (fi, &wd)) return SCPE_FMT; +/* if ((csum ^wd) & MMASK) return SCPE_CSUM; */ + } + break; + + case 0x0: case 0x8: /* instructions */ + if (load_geta (wd, &ad)) return SCPE_FMT; /* get address */ + if ((wd & 0x00F00000) != 0x00900000) /* if not x, */ + ad = (ad + amod) & AMASK; /* modify */ + wd = (wd & (SIGN|I_OP)) + (ad << I_V_EA); /* instruction */ + + default: /* data word */ + Write (origin, wd); + origin = (origin + 1) & AMASK; + break; + } /* end case */ + } /* end for */ +return SCPE_OK; +} + +/* Symbol tables */ + +static const char opcode[] = "ZBYRIDNMPEUTHCAS"; + +static const char hex_decode[] = "0123456789FGJKQW"; + +void lgp_fprint_addr (FILE *st, DEVICE *dptr, t_addr addr) +{ +if ((dptr == sim_devices[0]) && + ((sim_switches & SWMASK ('T')) || + ((cpu_unit.flags & UNIT_TTSS_D) && !(sim_switches & SWMASK ('N'))))) + fprintf (st, "%02d%02d", addr >> 6, addr & SCMASK_30); +else fprint_val (st, addr, dptr->aradix, dptr->awidth, PV_LEFT); +return; +} + +t_addr lgp_parse_addr (DEVICE *dptr, char *cptr, char **tptr) +{ +t_addr ad, ea; + +if ((dptr == sim_devices[0]) && + ((sim_switches & SWMASK ('T')) || + ((cpu_unit.flags & UNIT_TTSS_D) && !(sim_switches & SWMASK ('N'))))) { + ad = (t_addr) strtotv (cptr, tptr, 10); + if (((ad / 100) >= NTK_30) || ((ad % 100) >= NSC_30)) { + *tptr = cptr; + return 0; + } + ea = ((ad / 100) * NSC_30) | (ad % 100); + } +else ea = (t_addr) strtotv (cptr, tptr, dptr->aradix); +return ea; +} + +void lgp_vm_init (void) +{ +sim_vm_fprint_addr = &lgp_fprint_addr; +sim_vm_parse_addr = &lgp_parse_addr; +return; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, c; +uint32 inst, op, ea; + +inst = val[0]; +if (sw & SWMASK ('A')) { /* alphabetic? */ + if ((uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_ARG; + if (uptr->flags & UNIT_FLEX) { /* Flex file? */ + c = flex_to_ascii[inst]; /* get ASCII equiv */ + if (c <= 0) return SCPE_ARG; + } + else c = inst & 0x7F; /* ASCII file */ + fputc (c, of); + return SCPE_OK; + } + +if (uptr && (uptr != &cpu_unit)) return SCPE_ARG; /* must be CPU */ +if ((sw & SWMASK ('M')) && /* symbolic decode? */ + ((inst & ~(SIGN|I_OP|I_EA)) == 0)) { + op = I_GETOP (inst); + ea = I_GETEA (inst); + if (inst & SIGN) fputc ('-', of); + fprintf (of, "%c ", opcode[op]); + lgp_fprint_addr (of, sim_devices[0], ea); + return SCPE_OK; + } + +if ((sw & SWMASK ('L')) || /* LGP hex? */ + ((cpu_unit.flags & UNIT_LGPH_D) && !(sw & SWMASK ('H')))) { + for (i = 0; i < 8; i++) { + c = (inst >> (4 * (7 - i))) & 0xF; + fputc (hex_decode[c], of); + } + return SCPE_OK; + } +return SCPE_ARG; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 i, c; +char *tptr; + +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { + if ((uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_ARG; + if (uptr->flags & UNIT_FLEX) { /* Flex file? */ + c = ascii_to_flex[*cptr & 0x7F]; /* get Flex equiv */ + if (c < 0) return SCPE_ARG; + val[0] = ((c >> 1) | (c << 5)) & 0x3F; /* transpose */ + } + else val[0] = *cptr & 0x7F; /* ASCII file */ + return SCPE_OK; + } + +if (uptr && (uptr != &cpu_unit)) return SCPE_ARG; /* must be CPU */ +if (!parse_sym_m (cptr, val, sw)) return SCPE_OK; /* symbolic parse? */ +if ((sw & SWMASK ('L')) || /* LGP hex? */ + ((cpu_unit.flags & UNIT_LGPH_D) && !(sw & SWMASK ('H')))) { + val[0] = 0; + while (isspace (*cptr)) cptr++; /* absorb spaces */ + for (i = 0; i < 8; i++) { + c = *cptr++; /* get char */ + if (c == 0) return SCPE_OK; + if (islower (c)) c = toupper (c); + if (tptr = strchr (hex_decode, c)) + val[0] = (val[0] << 4) | (tptr - hex_decode); + else return SCPE_ARG; + } + if (*cptr == 0) return SCPE_OK; + } +return SCPE_ARG; +} + +/* Instruction parse */ + +t_stat parse_sym_m (char *cptr, t_value *val, int32 sw) +{ +uint32 ea, sgn; +char *tptr, gbuf[CBUFSIZE]; + +if (*cptr == '-') { + cptr++; + sgn = SIGN; + } +else sgn = 0; +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +if (gbuf[1] != 0) return SCPE_ARG; +if (tptr = strchr (opcode, gbuf[0])) + val[0] = ((tptr - opcode) << I_V_OP) | sgn; /* merge opcode */ +else return SCPE_ARG; +cptr = get_glyph (cptr, gbuf, 0); /* get address */ +ea = lgp_parse_addr (sim_devices[0], gbuf, &tptr); +if ((tptr == gbuf) || (*tptr != 0) || (ea > AMASK)) + return SCPE_ARG; +val[0] = val[0] | (ea << I_V_EA); /* merge address */ +if (*cptr != 0) return SCPE_2MARG; +return SCPE_OK; +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e64668d --- /dev/null +++ b/Makefile @@ -0,0 +1,834 @@ +# +# Makefile for SIMH 3.8 +# +# Philipp Hachtmann +# + +# 01-OCT-08 PH Added spaces after "-o" option to gcc +# 19-SEP-08 PH Initial version +# + +################################################################################ + +# Find out something about the system we build for. +# This process is ugly and should be revisited! + +BUILD=linux + +ifneq ($(WIN32),) + BUILD=win32 +endif + +ifneq (,$(findstring solaris,$(OSTYPE))) + BUILD=solaris +endif + +ifneq (,$(findstring darwin,$(OSTYPE))) + BUILD=darwin +endif + + +################################################################################ + +# Set build options according to what we found out about the build platform + +ifeq ($(BUILD),win32) + CC = gcc + LDFLAGS = -lm -lwsock32 -lwinmm + CFLAGS=-std=c99 -U__STRICT_ANSI__ -O2 -I. -Wall + EXEC_SUFFIX = .exe +endif + +ifeq ($(BUILD),solaris) + CC=gcc + CFLAGS=-D_GNU_SOURCE + LDFLAGS=-lm -lsocket -lnsl -lrt -lpthread +endif + +ifeq ($(BUILD),darwin) + CC=gcc + CFLAGS = -D_GNU_SOURCE -O3 + LDFLAGS= +endif + +ifeq ($(BUILD),linux) + CC=gcc + CFLAGS = -D_GNU_SOURCE -O3 -std=c99 -U__STRICT_ANSI__ -g + LDFLAGS+= -lrt -lm + HAVE_READLINE=true + READLINE_SAVED_HISTORY=true +endif + + +################################################################################ + +# Add network definitions etc if desired + +ifneq ($(USE_NETWORK),) + CFLAGS+=-DUSE_NETWORK + LDFLAGS+=-lwpcap -lpacket +endif + +ifneq ($(HAVE_READLINE),) + READLINE_CFLAGS += -DHAVE_READLINE + READLINE_LDFLAGS += -lreadline + ifneq ($(READLINE_SAVED_HISTORY),) + READLINE_CFLAGS += -DREADLINE_SAVED_HISTORY + endif +endif + +################################################################################ + +# Other stuff to add to CFLAGS and LDFLAGS + +CFLAGS+=-I $(COMMON_DIR) +LDFLAGS+= + + +################################################################################ + +# Target directory where the executables go to. +# The directory is automatically created if it doesn't exist. +TARGET_DIR=bin + +# This makes the target directory go away with "make clean": +CLEANSTUFF+=$(TARGET_DIR) + + +################################################################################ +################################################################################ + +# This is the main section containing all the simulator dependent information. + + + +################################################################################ +# +# Global stuff. +# + +# The "normal" common modules +SIM_NAMES+=COMMON +COMMON_DIR=. +COMMON_MODULES=scp sim_console sim_fio sim_timer sim_sock sim_tmxr\ + sim_ether sim_tape sim_readline +COMMON_CFLAGS+=$(READLINE_CFLAGS) +LDFLAGS+=$(READLINE_LDFLAGS) + +# The common modules with -DUSE_INT64 +SIM_NAMES+=COMMON_INT64 +COMMON_INT64_SRC_DIR=$(COMMON_SRC_DIR) +COMMON_INT64_OBJ_DIR=common_int64 +COMMON_INT64_MODULES=$(COMMON_MODULES) +COMMON_INT64_CFLAGS+=$(READLINE_CFLAGS) +COMMON_INT64_CFLAGS+=-DUSE_INT64 + +# The common modules with -DUSE_INT64 and -DUSE_ADDR64 +SIM_NAMES+=COMMON_INT64_ADDR64 +COMMON_INT64_ADDR64_SRC_DIR=$(COMMON_SRC_DIR) +COMMON_INT64_ADDR64_OBJ_DIR=common_int64_addr64 +COMMON_INT64_ADDR64_MODULES=$(COMMON_MODULES) +COMMON_INT64_ADDR64_CFLAGS+=$(READLINE_CFLAGS) +COMMON_INT64_ADDR64_CFLAGS+=-DUSE_INT64 -DUSE_ADDR64 + + +################################################################################ +# +# Data General Nova +# + +SIM_NAMES+=NOVA +NOVA_DIR=NOVA +NOVA_EXEC=nova +NOVA_MODULES= nova_sys nova_cpu nova_dkp nova_dsk nova_lp nova_mta \ + nova_plt nova_pt nova_clk nova_tt nova_tt1 nova_qty +NOVA_EXTRA_OBJS+=$(COMMON_OBJS) +NOVA_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# Data General Eclipse +# + +# What happens: +# +# The Makefile uses all items in SIM_NAMES to call the OBJECT_COLLECT_TEMPLATE +# sequentially. This macro uses the sim name (e.g. ECLIPSE) as prefix for +# the variables it is working on. +# It looks if there's a ..._SRC_DIR and ..._OBJ_DIR. +# If not, it assigns both from ..._DIR. +# So you can also specify a single source and object directory with ..._DIR. +# +# The template also builds a complete list of source files, ..._SOURCES. +# +# Then the OBJECT_RULE_TEMPLATE is expanded for all source files. +# The current object file name is created from the source file name, +# then added to the list of object files, ..._OBJS. +# And, of course, the build rule for the object is output. +# After the expansion of the OBJECT_RULE_TEMPLATE, the ..._EXTRA_OBJS +# are added to the ..._OBJs list. +# +# There's another template, PROGRAM_TEMPLATE. It is called with all +# SIM_NAMES as well. It generates the link rules for each application, +# uses the ..._OBJS variable as input. +# It also generates a "phony" target with the executable's stem, +# to enable things like "make nova". +# The application clean rules, like "novaclean", are also created by means +# of this template. + + +# This tells make to use the ECLIPSE_ variables. +SIM_NAMES+=ECLIPSE + +# The program's source directory +ECLIPSE_SRC_DIR=NOVA + +# Where the program's modules go to +ECLIPSE_OBJ_DIR=eclipse + +# Because the OBJ_DIR is no SRC_DIR, we will add it to the list of items +# to delete with "make clean". +CLEANSTUFF+=$(ECLIPSE_OBJ_DIR) + +# The executable name. Can be left out if only rules for object files should +# be generated. The common parts leave this blank. +ECLIPSE_EXEC=eclipse + +# The source file names without extension relative to the SRC_DIR +# Other sources (relative to the current directory) can be added by means +# of the EXTRA_MODULES variable (see PDP10) +ECLIPSE_MODULES=eclipse_cpu eclipse_tt nova_sys nova_dkp nova_dsk nova_lp \ + nova_mta nova_plt nova_pt nova_clk nova_tt1 nova_qty + +# The C compiler flags used to build the MODULES' and EXTRA_MODULES' object +# files. +ECLIPSE_CFLAGS=-DECLIPSE -DUSE_INT64 + +# Objects that are defined in another program/collection. Must be the +# object file names relative to the current directory, +# for example "H316/h316_cpu.o". +# Those object files are compiled by their own collection's rules! +ECLIPSE_EXTRA_OBJS+=$(COMMON_INT64_OBJS) + +# Extra dependencies for the program's/collection's modules. +ECLIPSE_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# DEC PDP-1 +# + +SIM_NAMES+=PDP1 +PDP1_DIR = PDP1 +PDP1_EXEC = pdp1 +PDP1_MODULES = pdp1_lp pdp1_cpu pdp1_stddev pdp1_sys pdp1_dt pdp1_drm \ + pdp1_clk pdp1_dcs +PDP1_EXTRA_OBJS+=$(COMMON_OBJS) +PDP1__EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# DEC PDP-4,7,9,15 +# + +PDP18_MODULES = pdp18b_dt pdp18b_drm pdp18b_cpu\ + pdp18b_lp pdp18b_mt pdp18b_rf\ + pdp18b_rp pdp18b_stddev pdp18b_sys\ + pdp18b_rb pdp18b_tt1 pdp18b_fpp + +SIM_NAMES+=PDP4 +PDP4_SRC_DIR=PDP18B +PDP4_OBJ_DIR=pdp4 +PDP4_EXEC=pdp4 +CLEANSTUFF+=$(PDP4_OBJ_DIR) +PDP4_MODULES=$(PDP18_MODULES) +PDP4_CFLAGS=-DPDP4 +PDP4_EXTRA_OBJS+=$(COMMON_OBJS) +PDP4_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + +SIM_NAMES+=PDP7 +PDP7_SRC_DIR=PDP18B +PDP7_OBJ_DIR=pdp7 +PDP7_EXEC=pdp7 +CLEANSTUFF+=$(PDP7_OBJ_DIR) +PDP7_MODULES=$(PDP18_MODULES) +PDP7_CFLAGS=-DPDP7 +PDP7_EXTRA_OBJS+=$(COMMON_OBJS) +PDP7_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + +SIM_NAMES+=PDP9 +PDP9_SRC_DIR=PDP18B +PDP9_OBJ_DIR=pdp9 +PDP9_EXEC=pdp9 +CLEANSTUFF+=$(PDP9_OBJ_DIR) +PDP9_MODULES=$(PDP18_MODULES) +PDP9_CFLAGS=-DPDP9 +PDP9_EXTRA_OBJS+=$(COMMON_OBJS) +PDP9_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + +SIM_NAMES+=PDP15 +PDP15_SRC_DIR=PDP18B +PDP15_OBJ_DIR=pdp15 +PDP15_EXEC=pdp15 +CLEANSTUFF+=$(PDP15_OBJ_DIR) +PDP15_MODULES=$(PDP18_MODULES) +PDP15_CFLAGS=-DPDP15 +PDP15_EXTRA_OBJS+=$(COMMON_OBJS) +PDP15_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# DEC PDP-8 +# + +SIM_NAMES+=PDP8 +PDP8_DIR=PDP8 +PDP8_EXEC=pdp8 +PDP8_MODULES=pdp8_cpu pdp8_clk pdp8_df \ + pdp8_dt pdp8_lp pdp8_mt \ + pdp8_pt pdp8_rf pdp8_rk \ + pdp8_rx pdp8_sys pdp8_tt \ + pdp8_ttx pdp8_rl pdp8_tsc \ + pdp8_td pdp8_ct pdp8_fpp +PDP8_EXTRA_OBJS+=$(COMMON_OBJS) +PDP8_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# DEC PDP-10 +# + +SIM_NAMES+=PDP10 +PDP10_DIR=PDP10 +PDP10_MODULES = pdp10_fe pdp10_cpu \ + pdp10_ksio pdp10_lp20 pdp10_mdfp \ + pdp10_pag pdp10_rp pdp10_sys \ + pdp10_tim pdp10_tu pdp10_xtnd\ + +PDP10_EXTRA_MODULES=$(PDP11_DIR)/pdp11_dz $(PDP11_DIR)/pdp11_pt\ + $(PDP11_DIR)/pdp11_ry $(PDP11_DIR)/pdp11_xu\ + $(PDP11_DIR)/pdp11_cr + +PDP10_EXTRA_DEPS=$(PDP11_DIR)/*.h +PDP10_EXEC=pdp10 +PDP10_CFLAGS=-DVM_PDP10 -DUSE_INT64 -I $(PDP11_DIR) +PDP10_EXTRA_OBJS+=$(COMMON_INT64_OBJS) +PDP10_EXTRA_DEPS+=$(COMMON_INT64_SRC_DIR)/*.h + + + +################################################################################ +# +# DEC PDP-11 +# + +SIM_NAMES+=PDP11 +PDP11_DIR=PDP11 +PDP11_MODULES = pdp11_fp pdp11_cpu pdp11_dz \ + pdp11_cis pdp11_lp pdp11_rk \ + pdp11_rl pdp11_rp pdp11_rx \ + pdp11_stddev pdp11_sys pdp11_tc \ + pdp11_tm pdp11_ts pdp11_io \ + pdp11_rq pdp11_tq pdp11_pclk \ + pdp11_ry pdp11_pt pdp11_hk \ + pdp11_xq pdp11_xu pdp11_vh \ + pdp11_rh pdp11_tu pdp11_cpumod \ + pdp11_cr pdp11_rf pdp11_dl \ + pdp11_ta pdp11_rc pdp11_kg \ + pdp11_ke pdp11_dc + +PDP11_EXEC=pdp11 +PDP11_CFLAGS=-DVM_PDP11 +PDP11_EXTRA_OBJS+=$(COMMON_OBJS) +PDP11_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# DEC VAX +# + +SIM_NAMES+=VAX +VAX_DIR=VAX +VAX_EXEC=vax +VAX_MODULES=vax_cpu vax_cpu1 vax_fpa vax_io \ + vax_cis vax_octa vax_cmode \ + vax_mmu vax_stddev vax_sysdev \ + vax_sys vax_syscm vax_syslist + +VAX_EXTRA_MODULES=$(PDP11_DIR)/pdp11_rl $(PDP11_DIR)/pdp11_rq $(PDP11_DIR)/pdp11_ts \ + $(PDP11_DIR)/pdp11_dz $(PDP11_DIR)/pdp11_lp $(PDP11_DIR)/pdp11_tq \ + $(PDP11_DIR)/pdp11_xq $(PDP11_DIR)/pdp11_ry \ + $(PDP11_DIR)/pdp11_vh $(PDP11_DIR)/pdp11_cr + +VAX_EXTRA_DEPS=$(PDP11_DIR)/*.h +VAX_CFLAGS=-DVM_VAX -DUSE_INT64 -DUSE_ADDR64 -I ${PDP11_DIR} +VAX_EXTRA_OBJS+=$(COMMON_INT64_ADDR64_OBJS) +VAX_EXTRA_DEPS+=$(COMMON_INT64_ADDR64_SRC_DIR)/*.h + + + +################################################################################ +# +# DEC VAX780 +# + +SIM_NAMES+=VAX780 +VAX780_EXEC=vax780 +VAX780_SRC_DIR=VAX +VAX780_OBJ_DIR=vax780 +VAX780_MODULES=vax_cpu vax_cpu1 vax_fpa \ + vax_cis vax_octa vax_cmode \ + vax_mmu vax_sys vax_syscm \ + vax780_stddev vax780_sbi \ + vax780_mem vax780_uba vax780_mba \ + vax780_fload vax780_syslist \ + +VAX780_EXTRA_MODULES=${PDP11_DIR}/pdp11_rl ${PDP11_DIR}/pdp11_rq \ + ${PDP11_DIR}/pdp11_ts ${PDP11_DIR}/pdp11_dz \ + ${PDP11_DIR}/pdp11_lp ${PDP11_DIR}/pdp11_tq \ + ${PDP11_DIR}/pdp11_xu ${PDP11_DIR}/pdp11_ry \ + ${PDP11_DIR}/pdp11_cr ${PDP11_DIR}/pdp11_rp \ + ${PDP11_DIR}/pdp11_tu ${PDP11_DIR}/pdp11_hk + +VAX780_CFLAGS=-DVM_VAX -DVAX_780 -DUSE_INT64 -DUSE_ADDR64 -I $(PDP11_DIR) +VAX780_EXTRA_DEPS=$(PDP11_DIR)/*.h +VAX780_EXTRA_OBJS+=$(COMMON_INT64_ADDR64_OBJS) +VAX780_EXTRA_DEPS+=$(COMMON_INT64_ADDR64_SRC_DIR)/*.h + + + +################################################################################ +# +# Honeywell DDP-516/H316 +# + +SIM_NAMES+=H316 +H316_DIR=H316 +H316_EXEC=h316 +H316_MODULES = h316_stddev h316_lp h316_cpu h316_sys h316_mt h316_fhd h316_dp +H316_EXTRA_OBJS+=$(COMMON_OBJS) +H316_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# Hewlett Packard 2100 +# + +SIM_NAMES+=HP2100 +HP2100_DIR=HP2100 +HP2100_EXEC=hp2100 +HP2100_MODULES = hp2100_stddev hp2100_dp hp2100_dq \ + hp2100_dr hp2100_lps hp2100_ms \ + hp2100_mt hp2100_mux hp2100_cpu \ + hp2100_fp hp2100_sys hp2100_lpt \ + hp2100_ipl hp2100_ds hp2100_cpu0 \ + hp2100_cpu1 hp2100_cpu2 hp2100_cpu3 \ + hp2100_cpu4 hp2100_cpu5 hp2100_cpu6 \ + hp2100_cpu7 hp2100_fp1 hp2100_baci +HP2100_CFLAGS = -DHAVE_INT64 +HP2100_EXTRA_OBJS+=$(COMMON_OBJS) +HP2100_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# IBM 1401 +# + +SIM_NAMES+=I1401 +I1401_DIR = I1401 +I1401_EXEC= i1401 +I1401_MODULES = i1401_lp i1401_cpu i1401_iq \ + i1401_cd i1401_mt i1401_dp \ + i1401_sys +I1401_EXTRA_OBJS+=$(COMMON_OBJS) +I1401_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# IBM 1620 +# + +SIM_NAMES+=I1620 +I1620_DIR = I1620 +I1620_EXEC = i1620 +I1620_MODULES = i1620_cd i1620_dp i1620_pt \ + i1620_tty i1620_cpu i1620_lp \ + i1620_fp i1620_sys +I1620_EXTRA_OBJS+=$(COMMON_OBJS) +I1620_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# IBM 7094/7094 +# + +SIM_NAMES+=I7094 +I7094_DIR = I7094 +I7094_EXEC = i7094 +I7094_MODULES = i7094_cpu i7094_cpu1 i7094_io \ + i7094_cd i7094_clk i7094_com \ + i7094_drm i7094_dsk i7094_sys \ + i7094_lp i7094_mt i7094_binloader +I7094_OPT = -DUSE_INT64 +I7094_EXTRA_OBJS+=$(COMMON_INT64_OBJS) +I7094_EXTRA_DEPS+=$(COMMON_INT64_SRC_DIR)/*.h + + + +################################################################################ +# +# IBM 1130 +# + +SIM_NAMES+=IBM1130 +IBM1130_DIR = Ibm1130 +IBM1130_EXEC = ibm1130 +IBM1130_MODULES = ibm1130_cpu ibm1130_cr \ + ibm1130_disk ibm1130_stddev \ + ibm1130_sys ibm1130_gdu \ + ibm1130_gui ibm1130_prt \ + ibm1130_fmt ibm1130_ptrp \ + ibm1130_plot ibm1130_sca \ + ibm1130_t2741 +IBM1130_EXTRA_OBJS+=$(COMMON_OBJS) +IBM1130_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# IBM System 3 +# + +SIM_NAMES+=S3 +S3_EXEC=s3 +S3_DIR = S3 +S3_MODULES = s3_cd s3_cpu s3_disk s3_lp \ + s3_pkb s3_sys +S3_EXTRA_OBJS+=$(COMMON_OBJS) +S3_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# Interdata 16b +# + +SIM_NAMES+=ID16 +ID16_DIR = Interdata +ID16_EXEC= id16 +ID16_MODULES = id16_cpu id16_sys id_dp \ + id_fd id_fp id_idc id_io \ + id_lp id_mt id_pas id_pt \ + id_tt id_uvc id16_dboot id_ttp +ID16_EXTRA_OBJS+=$(COMMON_OBJS) +ID16_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# Interdata 32b +# + +SIM_NAMES+=ID32 +ID32_EXEC=id32 +ID32_DIR = Interdata +ID32_MODULES = id32_cpu id32_sys id32_dboot +ID32_EXTRA_OBJS= $(foreach o, id_dp \ + id_fd id_fp id_idc id_io \ + id_lp id_mt id_pas id_pt \ + id_tt id_uvc id_ttp, $(ID16_DIR)/$(o).o) +ID32_EXTRA_OBJS+=$(COMMON_OBJS) +ID32_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# MITS Altair +# + +SIM_NAMES+=ALTAIR +ALTAIR_DIR = ALTAIR +ALTAIR_EXEC=altair +ALTAIR_MODULES = altair_sio altair_cpu altair_dsk \ + altair_sys +ALTAIR_EXTRA_OBJS+=$(COMMON_OBJS) +ALTAIR_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# MITS Altair Z80 +# + +SIM_NAMES+=ALTAIRZ80 +ALTAIRZ80_EXEC=altairz80 +ALTAIRZ80_DIR = AltairZ80 +ALTAIRZ80_MODULES = altairz80_cpu altairz80_cpu_nommu \ + altairz80_dsk disasm \ + altairz80_sio altairz80_sys \ + altairz80_hdsk altairz80_net \ + flashwriter2 i86_decode \ + i86_ops i86_prim_ops \ + i8272 insnsa insnsd \ + mfdc n8vem vfdhd \ + s100_disk1a s100_disk2 \ + s100_fif s100_mdriveh \ + s100_mdsad s100_selchan \ + s100_ss1 s100_64fdc \ + s100_scp300f sim_imd \ + wd179x +ALTAIRZ80_EXTRA_OBJS+=$(COMMON_OBJS) +ALTAIRZ80_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# GRI-909 and GRI-99 +# + +SIM_NAMES+=GRI +GRI_EXEC=gri +GRI_DIR = GRI +GRI_MODULES = gri_cpu gri_stddev gri_sys +GRI_EXTRA_OBJS+=$(COMMON_OBJS) +GRI_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# Royal-Mcbee LGP-30, LGP-21 +# + +SIM_NAMES+=LGP +LGP_DIR = LGP +LGP_EXEC=lgp +LGP_MODULES = lgp_cpu lgp_stddev lgp_sys +LGP_EXTRA_OBJS+=$(COMMON_OBJS) +LGP_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +# +# Scientific Data Systems SDS 940 +# + +SIM_NAMES+=SDS +SDS_EXEC=sds +SDS_DIR = SDS +SDS_MODULES = sds_cpu sds_drm sds_dsk sds_io \ + sds_lp sds_mt sds_mux sds_rad \ + sds_stddev sds_sys +SDS_EXTRA_OBJS+=$(COMMON_OBJS) +SDS_EXTRA_DEPS+=$(COMMON_SRC_DIR)/*.h + + + +################################################################################ +################################################################################ + +# Here comes the generic stuff. Should be altered VERY carefully. + +# Default target +default: all + +################################# +# +# The object file build rule template +# + +define OBJECT_RULE_TEMPLATE + +# Add the object file to the CLEANSTUFF list +$(1)_OBJS+=$$(patsubst %.c,%.o, $$($(1)_OBJ_DIR)/$$(notdir $(2))) + +CLEANSTUFF+=$$(patsubst %.c,%.o, $$($(1)_OBJ_DIR)/$$(notdir $(2))) + +$$(patsubst %.c,%.o,$$($(1)_OBJ_DIR)/$$(notdir $(2))): $(2) $$($(1)_SRC_DIR)/*.h $$($(1)_EXTRA_DEPS) + +# Create output directory if it doesn't exist yet. + @if [ ! -d $$($(1)_OBJ_DIR) ]; then echo "MKDIR $$($(1)_OBJ_DIR)";\ + mkdir $$($(1)_OBJ_DIR) 2>/dev/null||true; fi + +ifneq (,$$(VERBOSE)) + $$(CC) -c -o $$@ -I $$($(1)_SRC_DIR) $$(CFLAGS) $$($(1)_CFLAGS) $$< +else + @echo CC $$@ + @$$(CC) -c -o $$@ -I $$($(1)_SRC_DIR) $$(CFLAGS) $$($(1)_CFLAGS) $$< +endif + +endef + + +################################################################################ +# +# The object file collection template +# + +define OBJECT_COLLECT_TEMPLATE + +# If no ..._OBJ_DIR is specified, use ..._DIR +ifeq (,$$($(1)_OBJ_DIR)) +$(1)_OBJ_DIR=$$($(1)_DIR) +endif + +# If no ..._SRC_DIR is specified, use ..._DIR +ifeq (,$$($(1)_SRC_DIR)) + $(1)_SRC_DIR=$$($(1)_DIR) +endif + +$(1)_SOURCES=$$(foreach mod, $$($(1)_MODULES), $$($(1)_SRC_DIR)/$$(mod).c) +$(1)_SOURCES+=$$(foreach mod, $$($(1)_EXTRA_MODULES), $$(mod).c) + +# The build rules. +# They are generated by an inner template. Needed for flexibility. + +$$(foreach src, $$($(1)_SOURCES), $$(eval $$(call OBJECT_RULE_TEMPLATE,$(1),$$(src)))) + +$(1)_OBJS+=$$($(1)_EXTRA_OBJS) + +endef + + +################################################################################ +# +# The executable file build rule template +# + +define PROGRAM_TEMPLATE + +# All this is disabled if there's no executable to build. + +ifneq (,$$($(1)_EXEC)) + +# Add the executable to the CLEANSTUFF list + +CLEANSTUFF+=$$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + +EXEC_TARGETS+=$$($(1)_EXEC) + +# Generate a phony target with the simulator's name. +# Enables things like "make h316". +# On Windows this WILL NOT be "make h316.exe". +$$($(1)_EXEC): $$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + + +$$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX): $$($(1)_OBJS) + @if [ ! -d $$(TARGET_DIR) ]; then echo "MKDIR $$(TARGET_DIR)";\ + mkdir $$(TARGET_DIR) 2>/dev/null||true;fi + +ifneq (,$$(VERBOSE)) + $$(CC) -o $$@ $$(LDFLAGS) $$($(1)_LDFLAGS) $$^ +else + @echo LD $$@ + @$$(CC) -o $$@ $$(LDFLAGS) $$($(1)_LDFLAGS) $$^ +endif + + +$$($(1)_EXEC)clean clean$$($(1)_EXEC): +ifneq (,$$(VERBOSE)) + rm -f $$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + rm -f $$($(1)_OBJS) +else + @echo CLEAN $$($(1)_EXEC) + @rm -f $$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + @rm -f $$($(1)_OBJS) +endif + + +.PHONY: $$($(1)_EXEC) clean$$($(1)_EXEC) $$($(1)_EXEC)clean + + +endif + +endef +################################################################################ + +################################# +# +# Use the template with SIM_NAMES +# This will generate all build rules for all object files and executables. +# +$(foreach sn, $(SIM_NAMES),$(eval $(call OBJECT_COLLECT_TEMPLATE,$(sn)))) +$(foreach sn, $(SIM_NAMES),$(eval $(call PROGRAM_TEMPLATE,$(sn)))) + + +################################# +# +# The all target +# +all: $(EXEC_TARGETS) + + +################################# +# +# Output a little help +# +help: + @echo Available targets: + @echo + @echo "all - Build all simulators" + @echo "clean - Delete all generated files and directories" + @echo + @echo Simulators can explicitely built by the following targets: + @echo + @for n in $(EXEC_TARGETS); do echo " $$n" ; done + @echo + @echo The actual CC calls can be made visible with specifying VERBOSE=something. + @echo + + +################################# +# +# The clean rule. +# + +clean: + @echo CLEAN + @rm -rf $(CLEANSTUFF) + + + +################################# +# +# Specify phony targets +# + +.PHONY: clean help all default + + +################################################################################ +# +# This is the end of the game :-) +# +################################################################################ + + + diff --git a/Makefile.org b/Makefile.org new file mode 100644 index 0000000..3577e37 --- /dev/null +++ b/Makefile.org @@ -0,0 +1,413 @@ +# +# CC Command +# +ifneq ($(WIN32),) + + #Win32 Environments + LDFLAGS = -lm -lwsock32 -lwinmm + CFLAGS=-std=c99 -U__STRICT_ANSI__ -O2 -I. -Wall + CC = gcc + EXEC_SUFFIX = .exe + ifneq ($(USE_NETWORK),) + CFLAGS+=-DUSE_NETWORK + LDFLAGS+=-lwpcap -lpacket + endif + +else + #Unix Environments + + ifneq (,$(findstring solaris,$(OSTYPE))) +#Solaris + CFLAGS=-D_GNU_SOURCE + LDFLAGS=-lm -lsocket -lnsl -lrt -lpthread + CC=gcc + else + ifneq (,$(findstring darwin,$(OSTYPE))) +#Darwin + CFLAGS = -D_GNU_SOURCE + LDFLAGS= + CC=gcc + else + CFLAGS =-D_GNU_SOURCE + LDFLAGS=-lrt -lm + CC=gcc + endif + endif + + CC = gcc -std=c99 -U__STRICT_ANSI__ -g $(OS_CCDEFS) -I . + ifeq ($(USE_NETWORK),) + else + NETWORK_OPT = -DUSE_NETWORK -isystem /usr/local/include /usr/local/lib/libpcap.a + endif + +endif + +# +# Common Libraries +# +BIN = BIN/ +SIM = scp.c sim_console.c sim_fio.c sim_timer.c sim_sock.c \ + sim_tmxr.c sim_ether.c sim_tape.c + + +# +# Emulator source files and compile time options +# +PDP1D = PDP1 +PDP1 = ${PDP1D}/pdp1_lp.c ${PDP1D}/pdp1_cpu.c ${PDP1D}/pdp1_stddev.c \ + ${PDP1D}/pdp1_sys.c ${PDP1D}/pdp1_dt.c ${PDP1D}/pdp1_drm.c \ + ${PDP1D}/pdp1_clk.c ${PDP1D}/pdp1_dcs.c +PDP1_OPT = -I ${PDP1D} + + +NOVAD = NOVA +NOVA = ${NOVAD}/nova_sys.c ${NOVAD}/nova_cpu.c ${NOVAD}/nova_dkp.c \ + ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c ${NOVAD}/nova_mta.c \ + ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c ${NOVAD}/nova_clk.c \ + ${NOVAD}/nova_tt.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c +NOVA_OPT = -I ${NOVAD} + + +ECLIPSE = ${NOVAD}/eclipse_cpu.c ${NOVAD}/eclipse_tt.c ${NOVAD}/nova_sys.c \ + ${NOVAD}/nova_dkp.c ${NOVAD}/nova_dsk.c ${NOVAD}/nova_lp.c \ + ${NOVAD}/nova_mta.c ${NOVAD}/nova_plt.c ${NOVAD}/nova_pt.c \ + ${NOVAD}/nova_clk.c ${NOVAD}/nova_tt1.c ${NOVAD}/nova_qty.c +ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE -DUSE_INT64 + + +PDP18BD = PDP18B +PDP18B = ${PDP18BD}/pdp18b_dt.c ${PDP18BD}/pdp18b_drm.c ${PDP18BD}/pdp18b_cpu.c \ + ${PDP18BD}/pdp18b_lp.c ${PDP18BD}/pdp18b_mt.c ${PDP18BD}/pdp18b_rf.c \ + ${PDP18BD}/pdp18b_rp.c ${PDP18BD}/pdp18b_stddev.c ${PDP18BD}/pdp18b_sys.c \ + ${PDP18BD}/pdp18b_rb.c ${PDP18BD}/pdp18b_tt1.c ${PDP18BD}/pdp18b_fpp.c +PDP4_OPT = -DPDP4 -I ${PDP18BD} +PDP7_OPT = -DPDP7 -I ${PDP18BD} +PDP9_OPT = -DPDP9 -I ${PDP18BD} +PDP15_OPT = -DPDP15 -I ${PDP18BD} + + +PDP11D = PDP11 +PDP11 = ${PDP11D}/pdp11_fp.c ${PDP11D}/pdp11_cpu.c ${PDP11D}/pdp11_dz.c \ + ${PDP11D}/pdp11_cis.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_rk.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_rx.c \ + ${PDP11D}/pdp11_stddev.c ${PDP11D}/pdp11_sys.c ${PDP11D}/pdp11_tc.c \ + ${PDP11D}/pdp11_tm.c ${PDP11D}/pdp11_ts.c ${PDP11D}/pdp11_io.c \ + ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_tq.c ${PDP11D}/pdp11_pclk.c \ + ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_hk.c \ + ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_vh.c \ + ${PDP11D}/pdp11_rh.c ${PDP11D}/pdp11_tu.c ${PDP11D}/pdp11_cpumod.c \ + ${PDP11D}/pdp11_cr.c ${PDP11D}/pdp11_rf.c ${PDP11D}/pdp11_dl.c \ + ${PDP11D}/pdp11_ta.c ${PDP11D}/pdp11_rc.c ${PDP11D}/pdp11_kg.c \ + ${PDP11D}/pdp11_ke.c ${PDP11D}/pdp11_dc.c +PDP11_OPT = -DVM_PDP11 -I ${PDP11D} ${NETWORK_OPT} + + +VAXD = VAX +VAX = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c ${VAXD}/vax_io.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_stddev.c ${VAXD}/vax_sysdev.c \ + ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c ${VAXD}/vax_syslist.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xq.c ${PDP11D}/pdp11_ry.c \ + ${PDP11D}/pdp11_vh.c ${PDP11D}/pdp11_cr.c +VAX_OPT = -DVM_VAX -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} + + +VAX780 = ${VAXD}/vax_cpu.c ${VAXD}/vax_cpu1.c ${VAXD}/vax_fpa.c \ + ${VAXD}/vax_cis.c ${VAXD}/vax_octa.c ${VAXD}/vax_cmode.c \ + ${VAXD}/vax_mmu.c ${VAXD}/vax_sys.c ${VAXD}/vax_syscm.c \ + ${VAXD}/vax780_stddev.c ${VAXD}/vax780_sbi.c \ + ${VAXD}/vax780_mem.c ${VAXD}/vax780_uba.c ${VAXD}/vax780_mba.c \ + ${VAXD}/vax780_fload.c ${VAXD}/vax780_syslist.c \ + ${PDP11D}/pdp11_rl.c ${PDP11D}/pdp11_rq.c ${PDP11D}/pdp11_ts.c \ + ${PDP11D}/pdp11_dz.c ${PDP11D}/pdp11_lp.c ${PDP11D}/pdp11_tq.c \ + ${PDP11D}/pdp11_xu.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_cr.c \ + ${PDP11D}/pdp11_rp.c ${PDP11D}/pdp11_tu.c ${PDP11D}/pdp11_hk.c +VAX780_OPT = -DVM_VAX -DVAX_780 -DUSE_INT64 -DUSE_ADDR64 -I VAX -I ${PDP11D} ${NETWORK_OPT} + + +PDP10D = PDP10 +PDP10 = ${PDP10D}/pdp10_fe.c ${PDP11D}/pdp11_dz.c ${PDP10D}/pdp10_cpu.c \ + ${PDP10D}/pdp10_ksio.c ${PDP10D}/pdp10_lp20.c ${PDP10D}/pdp10_mdfp.c \ + ${PDP10D}/pdp10_pag.c ${PDP10D}/pdp10_rp.c ${PDP10D}/pdp10_sys.c \ + ${PDP10D}/pdp10_tim.c ${PDP10D}/pdp10_tu.c ${PDP10D}/pdp10_xtnd.c \ + ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_xu.c \ + ${PDP11D}/pdp11_cr.c +PDP10_OPT = -DVM_PDP10 -DUSE_INT64 -I ${PDP10D} -I ${PDP11D} ${NETWORK_OPT} + + + +PDP8D = PDP8 +PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \ + ${PDP8D}/pdp8_dt.c ${PDP8D}/pdp8_lp.c ${PDP8D}/pdp8_mt.c \ + ${PDP8D}/pdp8_pt.c ${PDP8D}/pdp8_rf.c ${PDP8D}/pdp8_rk.c \ + ${PDP8D}/pdp8_rx.c ${PDP8D}/pdp8_sys.c ${PDP8D}/pdp8_tt.c \ + ${PDP8D}/pdp8_ttx.c ${PDP8D}/pdp8_rl.c ${PDP8D}/pdp8_tsc.c \ + ${PDP8D}/pdp8_td.c ${PDP8D}/pdp8_ct.c ${PDP8D}/pdp8_fpp.c +PDP8_OPT = -I ${PDP8D} + + +H316D = H316 +H316 = ${H316D}/h316_stddev.c ${H316D}/h316_lp.c ${H316D}/h316_cpu.c \ + ${H316D}/h316_sys.c ${H316D}/h316_mt.c ${H316D}/h316_fhd.c \ + ${H316D}/h316_dp.c +H316_OPT = -I ${H316D} + + +HP2100D = HP2100 +HP2100 = ${HP2100D}/hp2100_stddev.c ${HP2100D}/hp2100_dp.c ${HP2100D}/hp2100_dq.c \ + ${HP2100D}/hp2100_dr.c ${HP2100D}/hp2100_lps.c ${HP2100D}/hp2100_ms.c \ + ${HP2100D}/hp2100_mt.c ${HP2100D}/hp2100_mux.c ${HP2100D}/hp2100_cpu.c \ + ${HP2100D}/hp2100_fp.c ${HP2100D}/hp2100_sys.c ${HP2100D}/hp2100_lpt.c \ + ${HP2100D}/hp2100_ipl.c ${HP2100D}/hp2100_ds.c ${HP2100D}/hp2100_cpu0.c \ + ${HP2100D}/hp2100_cpu1.c ${HP2100D}/hp2100_cpu2.c ${HP2100D}/hp2100_cpu3.c \ + ${HP2100D}/hp2100_cpu4.c ${HP2100D}/hp2100_cpu5.c ${HP2100D}/hp2100_cpu6.c \ + ${HP2100D}/hp2100_cpu7.c ${HP2100D}/hp2100_fp1.c ${HP2100D}/hp2100_baci.c +HP2100_OPT = -DHAVE_INT64 -I ${HP2100D} + + +I1401D = I1401 +I1401 = ${I1401D}/i1401_lp.c ${I1401D}/i1401_cpu.c ${I1401D}/i1401_iq.c \ + ${I1401D}/i1401_cd.c ${I1401D}/i1401_mt.c ${I1401D}/i1401_dp.c \ + ${I1401D}/i1401_sys.c +I1401_OPT = -I ${I1401D} + + +I1620D = I1620 +I1620 = ${I1620D}/i1620_cd.c ${I1620D}/i1620_dp.c ${I1620D}/i1620_pt.c \ + ${I1620D}/i1620_tty.c ${I1620D}/i1620_cpu.c ${I1620D}/i1620_lp.c \ + ${I1620D}/i1620_fp.c ${I1620D}/i1620_sys.c +I1620_OPT = -I ${I1620D} + + +I7094D = I7094 +I7094 = ${I7094D}/i7094_cpu.c ${I7094D}/i7094_cpu1.c ${I7094D}/i7094_io.c \ + ${I7094D}/i7094_cd.c ${I7094D}/i7094_clk.c ${I7094D}/i7094_com.c \ + ${I7094D}/i7094_drm.c ${I7094D}/i7094_dsk.c ${I7094D}/i7094_sys.c \ + ${I7094D}/i7094_lp.c ${I7094D}/i7094_mt.c ${I7094D}/i7094_binloader.c +I7094_OPT = -DUSE_INT64 -I ${I7094D} + + +IBM1130D = Ibm1130 +IBM1130 = ${IBM1130D}/ibm1130_cpu.c ${IBM1130D}/ibm1130_cr.c \ + ${IBM1130D}/ibm1130_disk.c ${IBM1130D}/ibm1130_stddev.c \ + ${IBM1130D}/ibm1130_sys.c ${IBM1130D}/ibm1130_gdu.c \ + ${IBM1130D}/ibm1130_gui.c ${IBM1130D}/ibm1130_prt.c \ + ${IBM1130D}/ibm1130_fmt.c ${IBM1130D}/ibm1130_ptrp.c \ + ${IBM1130D}/ibm1130_plot.c ${IBM1130D}/ibm1130_sca.c \ + ${IBM1130D}/ibm1130_t2741.c +IBM1130_OPT = -I ${IBM1130D} + + +ID16D = Interdata +ID16 = ${ID16D}/id16_cpu.c ${ID16D}/id16_sys.c ${ID16D}/id_dp.c \ + ${ID16D}/id_fd.c ${ID16D}/id_fp.c ${ID16D}/id_idc.c ${ID16D}/id_io.c \ + ${ID16D}/id_lp.c ${ID16D}/id_mt.c ${ID16D}/id_pas.c ${ID16D}/id_pt.c \ + ${ID16D}/id_tt.c ${ID16D}/id_uvc.c ${ID16D}/id16_dboot.c ${ID16D}/id_ttp.c +ID16_OPT = -I ${ID16D} + + +ID32D = Interdata +ID32 = ${ID32D}/id32_cpu.c ${ID32D}/id32_sys.c ${ID32D}/id_dp.c \ + ${ID32D}/id_fd.c ${ID32D}/id_fp.c ${ID32D}/id_idc.c ${ID32D}/id_io.c \ + ${ID32D}/id_lp.c ${ID32D}/id_mt.c ${ID32D}/id_pas.c ${ID32D}/id_pt.c \ + ${ID32D}/id_tt.c ${ID32D}/id_uvc.c ${ID32D}/id32_dboot.c ${ID32D}/id_ttp.c +ID32_OPT = -I ${ID32D} + + +S3D = S3 +S3 = ${S3D}/s3_cd.c ${S3D}/s3_cpu.c ${S3D}/s3_disk.c ${S3D}/s3_lp.c \ + ${S3D}/s3_pkb.c ${S3D}/s3_sys.c +S3_OPT = -I ${S3D} + + +ALTAIRD = ALTAIR +ALTAIR = ${ALTAIRD}/altair_sio.c ${ALTAIRD}/altair_cpu.c ${ALTAIRD}/altair_dsk.c \ + ${ALTAIRD}/altair_sys.c +ALTAIR_OPT = -I ${ALTAIRD} + + +ALTAIRZ80D = AltairZ80 +ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ + ${ALTAIRZ80D}/altairz80_dsk.c ${ALTAIRZ80D}/disasm.c \ + ${ALTAIRZ80D}/altairz80_sio.c ${ALTAIRZ80D}/altairz80_sys.c \ + ${ALTAIRZ80D}/altairz80_hdsk.c ${ALTAIRZ80D}/altairz80_net.c \ + ${ALTAIRZ80D}/flashwriter2.c ${ALTAIRZ80D}/i86_decode.c \ + ${ALTAIRZ80D}/i86_ops.c ${ALTAIRZ80D}/i86_prim_ops.c \ + ${ALTAIRZ80D}/i8272.c ${ALTAIRZ80D}/insnsa.c ${ALTAIRZ80D}/insnsd.c \ + ${ALTAIRZ80D}/mfdc.c ${ALTAIRZ80D}/n8vem.c ${ALTAIRZ80D}/vfdhd.c \ + ${ALTAIRZ80D}/s100_disk1a.c ${ALTAIRZ80D}/s100_disk2.c \ + ${ALTAIRZ80D}/s100_fif.c ${ALTAIRZ80D}/s100_mdriveh.c \ + ${ALTAIRZ80D}/s100_mdsad.c ${ALTAIRZ80D}/s100_selchan.c \ + ${ALTAIRZ80D}/s100_ss1.c ${ALTAIRZ80D}/s100_64fdc.c \ + ${ALTAIRZ80D}/s100_scp300f.c ${ALTAIRZ80D}/sim_imd.c \ + ${ALTAIRZ80D}/wd179x.c +ALTAIRZ80_OPT = -I ${ALTAIRZ80D} + + +GRID = GRI +GRI = ${GRID}/gri_cpu.c ${GRID}/gri_stddev.c ${GRID}/gri_sys.c +GRI_OPT = -I ${GRID} + + +LGPD = LGP +LGP = ${LGPD}/lgp_cpu.c ${LGPD}/lgp_stddev.c ${LGPD}/lgp_sys.c +LGP_OPT = -I ${LGPD} + + +SDSD = SDS +SDS = ${SDSD}/sds_cpu.c ${SDSD}/sds_drm.c ${SDSD}/sds_dsk.c ${SDSD}/sds_io.c \ + ${SDSD}/sds_lp.c ${SDSD}/sds_mt.c ${SDSD}/sds_mux.c ${SDSD}/sds_rad.c \ + ${SDSD}/sds_stddev.c ${SDSD}/sds_sys.c +SDS_OPT = -I ${SDSD} + +# +# Build everything +# +ALL = pdp1 pdp4 pdp7 pdp8 pdp9 pdp15 pdp11 pdp10 \ + vax vax780 nova eclipse hp2100 i1401 i1620 s3 \ + altair altairz80 gri i1620 i7094 ibm1130 id16 \ + id32 sds lgp h316 + +all : ${ALL} + +clean : +ifeq ($(WIN32),) + ${RM} ${BIN}* +else + if exist BIN\*.exe del /q BIN\*.exe +endif + +# +# Individual builds +# +pdp1 : ${BIN}pdp1${EXE} + +${BIN}pdp1${EXE} : ${PDP1} ${SIM} + ${CC} ${PDP1} ${SIM} ${PDP1_OPT} -o $@ ${LDFLAGS} + +pdp4 : ${BIN}pdp4${EXE} + +${BIN}pdp4${EXE} : ${PDP18B} ${SIM} + ${CC} ${PDP18B} ${SIM} ${PDP4_OPT} -o $@ ${LDFLAGS} + +pdp7 : ${BIN}pdp7${EXE} + +${BIN}pdp7${EXE} : ${PDP18B} ${SIM} + ${CC} ${PDP18B} ${SIM} ${PDP7_OPT} -o $@ ${LDFLAGS} + +pdp8 : ${BIN}pdp8${EXE} + +${BIN}pdp8${EXE} : ${PDP8} ${SIM} + ${CC} ${PDP8} ${SIM} ${PDP8_OPT} -o $@ ${LDFLAGS} + +pdp9 : ${BIN}pdp9${EXE} + +${BIN}pdp9${EXE} : ${PDP18B} ${SIM} + ${CC} ${PDP18B} ${SIM} ${PDP9_OPT} -o $@ ${LDFLAGS} + +pdp15 : ${BIN}pdp15${EXE} + +${BIN}pdp15${EXE} : ${PDP18B} ${SIM} + ${CC} ${PDP18B} ${SIM} ${PDP15_OPT} -o $@ ${LDFLAGS} + +pdp10 : ${BIN}pdp10${EXE} + +${BIN}pdp10${EXE} : ${PDP10} ${SIM} + ${CC} ${PDP10} ${SIM} ${PDP10_OPT} -o $@ ${LDFLAGS} + +pdp11 : ${BIN}pdp11${EXE} + +${BIN}pdp11${EXE} : ${PDP11} ${SIM} + ${CC} ${PDP11} ${SIM} ${PDP11_OPT} -o $@ ${LDFLAGS} + +vax : ${BIN}vax${EXE} + +${BIN}vax${EXE} : ${VAX} ${SIM} + ${CC} ${VAX} ${SIM} ${VAX_OPT} -o $@ ${LDFLAGS} + +vax780 : ${BIN}vax780${EXE} + +${BIN}vax780${EXE} : ${VAX780} ${SIM} + ${CC} ${VAX780} ${SIM} ${VAX780_OPT} -o $@ ${LDFLAGS} + +nova : ${BIN}nova${EXE} + +${BIN}nova${EXE} : ${NOVA} ${SIM} + ${CC} ${NOVA} ${SIM} ${NOVA_OPT} -o $@ ${LDFLAGS} + +eclipse : ${BIN}eclipse${EXE} + +${BIN}eclipse${EXE} : ${ECLIPSE} ${SIM} + ${CC} ${ECLIPSE} ${SIM} ${ECLIPSE_OPT} -o $@ ${LDFLAGS} + +h316 : ${BIN}h316${EXE} + +${BIN}h316${EXE} : ${H316} ${SIM} + ${CC} ${H316} ${SIM} ${H316_OPT} -o $@ ${LDFLAGS} + +hp2100 : ${BIN}hp2100${EXE} + +${BIN}hp2100${EXE} : ${HP2100} ${SIM} + ${CC} ${HP2100} ${SIM} ${HP2100_OPT} -o $@ ${LDFLAGS} + +i1401 : ${BIN}i1401${EXE} + +${BIN}i1401${EXE} : ${I1401} ${SIM} + ${CC} ${I1401} ${SIM} ${I1401_OPT} -o $@ ${LDFLAGS} + +i1620 : ${BIN}i1620${EXE} + +${BIN}i1620${EXE} : ${I1620} ${SIM} + ${CC} ${I1620} ${SIM} ${I1620_OPT} -o $@ ${LDFLAGS} + +i7094 : ${BIN}i7094${EXE} + +${BIN}i7094${EXE} : ${I7094} ${SIM} + ${CC} ${I7094} ${SIM} ${I7094_OPT} -o $@ ${LDFLAGS} + +ibm1130 : ${BIN}ibm1130${EXE} + +${BIN}ibm1130${EXE} : ${IBM1130} + ${CC} ${IBM1130} ${SIM} ${IBM1130_OPT} -o $@ ${LDFLAGS} + +s3 : ${BIN}s3${EXE} + +${BIN}s3${EXE} : ${S3} ${SIM} + ${CC} ${S3} ${SIM} ${S3_OPT} -o $@ ${LDFLAGS} + +altair : ${BIN}altair${EXE} + +${BIN}altair${EXE} : ${ALTAIR} ${SIM} + ${CC} ${ALTAIR} ${SIM} ${ALTAIR_OPT} -o $@ ${LDFLAGS} + +altairz80 : ${BIN}altairz80${EXE} + +${BIN}altairz80${EXE} : ${ALTAIRZ80} ${SIM} + ${CC} ${ALTAIRZ80} ${SIM} ${ALTAIRZ80_OPT} -o $@ ${LDFLAGS} + +gri : ${BIN}gri${EXE} + +${BIN}gri${EXE} : ${GRI} ${SIM} + ${CC} ${GRI} ${SIM} ${GRI_OPT} -o $@ ${LDFLAGS} + +lgp : ${BIN}lgp${EXE} + +${BIN}lgp${EXE} : ${LGP} ${SIM} + ${CC} ${LGP} ${SIM} ${LGP_OPT} -o $@ ${LDFLAGS} + +id16 : ${BIN}id16${EXE} + +${BIN}id16${EXE} : ${ID16} ${SIM} + ${CC} ${ID16} ${SIM} ${ID16_OPT} -o $@ ${LDFLAGS} + +id32 : ${BIN}id32${EXE} + +${BIN}id32${EXE} : ${ID32} ${SIM} + ${CC} ${ID32} ${SIM} ${ID32_OPT} -o $@ ${LDFLAGS} + +sds : ${BIN}sds${EXE} + +${BIN}sds${EXE} : ${SDS} ${SIM} + ${CC} ${SDS} ${SIM} ${SDS_OPT} -o $@ ${LDFLAGS} diff --git a/Makefile.posted b/Makefile.posted new file mode 100644 index 0000000..3a27034 --- /dev/null +++ b/Makefile.posted @@ -0,0 +1,302 @@ +# New Makefile for SIMH 3.8 +# Philipp Hachtmann +# + + +################################################################################ + +# Find out something about the system we build for. +# This process is ugly and should be revisited! + +BUILD=linux + +ifneq ($(WIN32),) + BUILD=win32 +endif + +ifneq (,$(findstring solaris,$(OSTYPE))) + BUILD=solaris +endif + +ifneq (,$(findstring darwin,$(OSTYPE))) + BUILD=darwin +endif + + +################################################################################ + +# Set build options according to what we found out. + +ifeq ($(BUILD),win32) + CC = gcc + LDFLAGS = -lm -lwsock32 -lwinmm + CFLAGS=-std=c99 -U__STRICT_ANSI__ -O2 -I. -Wall + EXEC_SUFFIX = .exe +endif + +ifeq ($(BUILD),solaris) + CC=gcc + CFLAGS=-D_GNU_SOURCE + LDFLAGS=-lm -lsocket -lnsl -lrt -lpthread +endif + +ifeq ($(BUILD),darwin) + CC=gcc + CFLAGS = -D_GNU_SOURCE + LDFLAGS= +endif + +ifeq ($(BUILD),linux) + CC=gcc + CFLAGS = -D_GNU_SOURCE -std=c99 -U__STRICT_ANSI__ -g + LDFLAGS= -lrt -lm +endif + + +################################################################################ + +# Add network definitions if desired + +ifneq ($(USE_NETWORK),) + CFLAGS+=-DUSE_NETWORK + LDFLAGS+=-lwpcap -lpacket +endif + + +################################################################################ + +# Other stuff to add to CFLAGS and LDFLAGS + +CFLAGS+=-I common +LDFLAGS+= + + +################################################################################ + +# Target directory where the executables go to. +# The directory is automatically created if it doesn't exist. +TARGET_DIR=bin + +# This makes the target directory go away with "make clean": +CLEANSTUFF+=$(TARGET_DIR) + + +################################################################################ +################################################################################ + +# This is the main section containing all the simulator dependent information. + + +# Names used by the template mechanism. +# They are used as part of the corresponding variable names +# like "NOVA" in "NOVA_CFLAGS". +# You will have to add a name for every new simulator or build +# configuration. + +SIM_NAMES=COMMON H316 NOVA ECLIPSE + +# +# Global stuff. The special executable "nothing" will not be built. +# +COMMON_DIR=common +COMMON_MODULES=scp sim_console sim_fio sim_timer sim_sock sim_tmxr sim_ether sim_tape +COMMON_CFLAGS= +COMMON_EXEC=nothing + + +################################################################################ +# +# Data General Nova +# +NOVA_DIR=nova +NOVA_EXEC=nova +NOVA_MODULES= nova_sys nova_cpu nova_dkp nova_dsk nova_lp nova_mta \ + nova_plt nova_pt nova_clk nova_tt nova_tt1 nova_qty + + +################################################################################ +# +# Data General Eclipse +# + +# Note: Here you see we define a ..._SRC_DIR and ..._OBJ_DIR. This is +# done because Nova and Eclipse have the same modules compiled +# with different CFLAGS. +# This is handled automatically by the generic rule template for +# object files. But if the OBJ_DIR should be deleted +# by "make clean", it must be added manually to the CLEANSTUFF list. + +ECLIPSE_SRC_DIR=nova +ECLIPSE_OBJ_DIR=eclipse +CLEANSTUFF+=$(ECLIPSE_OBJ_DIR) + +ECLIPSE_EXEC=eclipse +ECLIPSE_MODULES=eclipse_cpu eclipse_tt nova_sys nova_dkp nova_dsk nova_lp \ + nova_mta nova_plt nova_pt nova_clk nova_tt1 nova_qty +ECLIPSE_CFLAGS=-DECLIPSE -DUSE_INT64 + + +################################################################################ + +# +# Honeywell DDP-516/H316 +# +H316_DIR=h316 +H316_EXEC=h316 +H316_MODULES = h316_stddev h316_lp h316_cpu h316_sys h316_mt h316_fhd h316_dp + + +################################################################################ +################################################################################ + +# Here comes the weird generic stuff. Should be altered VERY carefully only. + +# Default target +default: all + + +################################# +# +# The object file build rule template +# + +define OBJECT_TEMPLATE + +# If no ..._OBJ_DIR is specified, use ..._DIR + +ifeq (,$$($(1)_OBJ_DIR)) +$(1)_OBJ_DIR=$$($(1)_DIR) +endif + +# If no ..._SRC_DIR is specified, use ..._DIR + +ifeq (,$$($(1)_SRC_DIR)) + $(1)_SRC_DIR=$$($(1)_DIR) +endif + +# Generate object file names with path (for example h316/h316_cpu.o) + +$(1)_OBJS=$$(foreach obj, $$($(1)_MODULES), $$($(1)_OBJ_DIR)/$$(obj).o) + +# Add the object files to the CLEANSTUFF list + +CLEANSTUFF+=$$($(1)_OBJS) + +# The build rule. +# At the moment, header file changes might cause too many files to be rebuilt. But +# that seems to be acceptable for the moment. + +$$($(1)_OBJS) : $$($(1)_OBJ_DIR)/%.o : $$($(1)_SRC_DIR)/%.c $$($(1)_SRC_DIR)/*.h $$(COMMON_DIR)/*.h + @echo CC $$@ + +# Create output directory if it doesn't exist yet. + + @if [ ! -d $$($(1)_OBJ_DIR) ]; then echo "MKDIR $$($(1)_OBJ_DIR)"; mkdir $$($(1)_OBJ_DIR); fi + +ifneq (,$$(VERBOSE)) + @echo $$(CC) -c -o $$@ -I $$($(1)_DIR) $$(CFLAGS) $$($(1)_CFLAGS) $$< +endif + + @$$(CC) -c -o $$@ -I $$($(1)_DIR) $$(CFLAGS) $$($(1)_CFLAGS) $$< + +endef + + +################################# +# +# The executable file build rule template +# + +define PROGRAM_TEMPLATE + +# Add the executable to the CLEANSTUFF list + +CLEANSTUFF+=$$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + +# We won't build or mention the executable named "noting"! +ifneq (nothing,$$($(1)_EXEC)) +EXEC_TARGETS+=$$($(1)_EXEC) +endif + +# Generate a phony target with the simulator's name. +# Enables things like "make h316". +# On Windows this WILL NOT be "make h316.exe". + +$$($(1)_EXEC): $$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + +.PHONY: $$($(1)_EXEC) + +$$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX): $$($(1)_OBJS) $$(COMMON_OBJS) + @if [ ! -d $$(TARGET_DIR) ]; then echo "MKDIR $$(TARGET_DIR)";mkdir $$(TARGET_DIR);fi + @echo LD $$@ +ifneq (,$$(VERBOSE)) + @echo $$(CC) -o$$@ $$(LDFLAGS) $$($(1)_LDFLAGS) $$^ +endif + @$$(CC) -o$$@ $$(LDFLAGS) $$($(1)_LDFLAGS) $$^ +endef + + +################################# +# +# Use the template with SIM_NAMES +# + +$(foreach sn, $(SIM_NAMES),$(eval $(call OBJECT_TEMPLATE,$(sn)))) +$(foreach sn, $(SIM_NAMES),$(eval $(call PROGRAM_TEMPLATE,$(sn)))) + + +################################# +# +# The all target +# + +all: $(EXEC_TARGETS) + + +################################# +# +# Output a little help +# + +help: + @echo Available targets: + @echo + @echo "all - Build all simulators" + @echo "clean - Delete all generated files and directories" + @echo + @echo Simulators can explicitely built by the following targets: + @echo + @echo $(EXEC_TARGETS) + @echo + @echo $(CLEANSTUFF) + @echo + @echo The actual CC calls can be made visible with specifying VERBOSE=something. + @echo + + +################################# +# +# The clean rule. +# + +clean: + @echo CLEAN + @rm -rf $(CLEANSTUFF) + + +################################# +# +# Specify phony targets +# + +.PHONY: clean help all default + + +################################################################################ +# +# This is the end of the game :-) +# +################################################################################ + + + diff --git a/Makefile.tmp b/Makefile.tmp new file mode 100644 index 0000000..5a7a1c5 --- /dev/null +++ b/Makefile.tmp @@ -0,0 +1,403 @@ +# New Makefile for SIMH 3.8 +# Philipp Hachtmann +# + + +################################################################################ + +# Find out something about the system we build for. +# This process is ugly and should be revisited! + +BUILD=linux + +ifneq ($(WIN32),) + BUILD=win32 +endif + +ifneq (,$(findstring solaris,$(OSTYPE))) + BUILD=solaris +endif + +ifneq (,$(findstring darwin,$(OSTYPE))) + BUILD=darwin +endif + + +################################################################################ + +# Set build options according to what we found out. + +ifeq ($(BUILD),win32) + CC = gcc + LDFLAGS = -lm -lwsock32 -lwinmm + CFLAGS=-std=c99 -U__STRICT_ANSI__ -O2 -I. -Wall + EXEC_SUFFIX = .exe +endif + +ifeq ($(BUILD),solaris) + CC=gcc + CFLAGS=-D_GNU_SOURCE + LDFLAGS=-lm -lsocket -lnsl -lrt -lpthread +endif + +ifeq ($(BUILD),darwin) + CC=gcc + CFLAGS = -D_GNU_SOURCE + LDFLAGS= +endif + +ifeq ($(BUILD),linux) + CC=gcc + CFLAGS = -D_GNU_SOURCE -std=c99 -U__STRICT_ANSI__ -g + LDFLAGS= -lrt -lm +endif + + +################################################################################ + +# Add network definitions if desired + +ifneq ($(USE_NETWORK),) + CFLAGS+=-DUSE_NETWORK + LDFLAGS+=-lwpcap -lpacket +endif + + +################################################################################ + +# Other stuff to add to CFLAGS and LDFLAGS + +CFLAGS+=-I common +LDFLAGS+= + + +################################################################################ + +# Target directory where the executables go to. +# The directory is automatically created if it doesn't exist. +TARGET_DIR=bin + +# This makes the target directory go away with "make clean": +CLEANSTUFF+=$(TARGET_DIR) + + +################################################################################ +################################################################################ + +# This is the main section containing all the simulator dependent information. + + + +# +# Global stuff. The special executable "nothing" will not be built. +# + +SIM_NAMES+=COMMON +COMMON_DIR=common +COMMON_MODULES=scp sim_console sim_fio sim_timer sim_sock sim_tmxr sim_ether sim_tape +COMMON_CFLAGS= +COMMON_EXEC=nothing + + +################################################################################ +# +# Data General Nova +# + +SIM_NAMES+=NOVA +NOVA_DIR=nova +NOVA_EXEC=nova +NOVA_MODULES= nova_sys nova_cpu nova_dkp nova_dsk nova_lp nova_mta \ + nova_plt nova_pt nova_clk nova_tt nova_tt1 nova_qty + + +################################################################################ +# +# Data General Eclipse +# + +# Note: Here you see we define a ..._SRC_DIR and ..._OBJ_DIR. This is +# done because Nova and Eclipse have the same modules compiled +# with different CFLAGS. +# This is handled automatically by the generic rule template for +# object files. But if the OBJ_DIR should be deleted +# by "make clean", it must be added manually to the CLEANSTUFF list. + +SIM_NAMES+=ECLIPSE +ECLIPSE_SRC_DIR=nova +ECLIPSE_OBJ_DIR=eclipse +CLEANSTUFF+=$(ECLIPSE_OBJ_DIR) +ECLIPSE_EXEC=eclipse +ECLIPSE_MODULES=eclipse_cpu eclipse_tt nova_sys nova_dkp nova_dsk nova_lp \ + nova_mta nova_plt nova_pt nova_clk nova_tt1 nova_qty +ECLIPSE_CFLAGS=-DECLIPSE -DUSE_INT64 + + +################################################################################ +# +# DEC PDP-1 +# + +SIM_NAMES+=PDP1 +PDP1_DIR = pdp1 +PDP1_EXEC = pdp1 +PDP1_MODULES = pdp1_lp pdp1_cpu pdp1_stddev pdp1_sys pdp1_dt pdp1_drm \ + pdp1_clk pdp1_dcs + + +################################################################################ +# +# DEC PDP-4,7,9,15 +# + +PDP18_MODULES = pdp18b_dt pdp18b_drm pdp18b_cpu\ + pdp18b_lp pdp18b_mt pdp18b_rf\ + pdp18b_rp pdp18b_stddev pdp18b_sys\ + pdp18b_rb pdp18b_tt1 pdp18b_fpp + +SIM_NAMES+=PDP4 +PDP4_SRC_DIR=pdp18b +PDP4_OBJ_DIR=pdp4 +PDP4_EXEC=pdp4 +CLEANSTUFF+=$(PDP4_OBJ_DIR) +PDP4_MODULES=$(PDP18_MODULES) +PDP4_CFLAGS=-DPDP4 + +SIM_NAMES+=PDP7 +PDP7_SRC_DIR=pdp18b +PDP7_OBJ_DIR=pdp7 +PDP7_EXEC=pdp7 +CLEANSTUFF+=$(PDP7_OBJ_DIR) +PDP7_MODULES=$(PDP18_MODULES) +PDP7_CFLAGS=-DPDP7 + +SIM_NAMES+=PDP9 +PDP9_SRC_DIR=pdp18b +PDP9_OBJ_DIR=pdp9 +PDP9_EXEC=pdp9 +CLEANSTUFF+=$(PDP9_OBJ_DIR) +PDP9_MODULES=$(PDP18_MODULES) +PDP9_CFLAGS=-DPDP9 + +SIM_NAMES+=PDP15 +PDP15_SRC_DIR=pdp18b +PDP15_OBJ_DIR=pdp15 +PDP15_EXEC=pdp15 +CLEANSTUFF+=$(PDP15_OBJ_DIR) +PDP15_MODULES=$(PDP18_MODULES) +PDP15_CFLAGS=-DPDP15 + + +################################################################################ +# +# DEC PDP-11 +# + +SIM_NAMES+=PDP11 +PDP11_DIR=pdp11 +PDP11_MODULES = pdp11_fp pdp11_cpu pdp11_dz \ + pdp11_cis pdp11_lp pdp11_rk \ + pdp11_rl pdp11_rp pdp11_rx \ + pdp11_stddev pdp11_sys pdp11_tc \ + pdp11_tm pdp11_ts pdp11_io \ + pdp11_rq pdp11_tq pdp11_pclk \ + pdp11_ry pdp11_pt pdp11_hk \ + pdp11_xq pdp11_xu pdp11_vh \ + pdp11_rh pdp11_tu pdp11_cpumod \ + pdp11_cr pdp11_rf pdp11_dl \ + pdp11_ta pdp11_rc pdp11_kg \ + pdp11_ke pdp11_dc + +PDP11_EXEC=pdp11 +PDP11_CFLAGS=-DVM_PDP11 + +################################################################################ +# +# DEC PDP-10 +# + +SIM_NAMES+=PDP10 +PDP10_OBJ_DIR=pdp10 +PDP10_SRC_DIR=pdp10 +PDP10_MODULES = pdp10_fe pdp11_dz pdp10_cpu \ + pdp10_ksio pdp10_lp20 pdp10_mdfp \ + pdp10_pag pdp10_rp pdp10_sys \ + pdp10_tim pdp10_tu pdp10_xtnd ../$(PDP11_DIR)/pdp11_pt\ + ../$(PDP11_DIR)/pdp11_ry ../$(PDP11_DIR)/pdp11_xu +PDP10_EXEC=pdp10 +PDP10_CFLAGS=-DVM_PDP10 -DUSE_INT64 -I $(PDP11_DIR) + + + +################################################################################ + +# +# Honeywell DDP-516/H316 +# +H316_DIR=h316 +H316_EXEC=h316 +H316_MODULES = h316_stddev h316_lp h316_cpu h316_sys h316_mt h316_fhd h316_dp + + + +PDP1_DIR = pdp1 +PDP1_EXEC = pdp1 +PDP1_MODULES = pdp1_lp pdp1_cpu pdp1_stddev pdp1_sys pdp1_dt pdp1_drm \ + pdp1_clk pdp1_dcs + +################################################################################ +################################################################################ + +# Here comes the weird generic stuff. Should be altered VERY carefully only. + +# Default target +default: all + + +################################# +# +# The object file build rule template +# + +define OBJECT_TEMPLATE + +# If no ..._OBJ_DIR is specified, use ..._DIR + +ifeq (,$$($(1)_OBJ_DIR)) +$(1)_OBJ_DIR=$$($(1)_DIR) +endif + +# If no ..._SRC_DIR is specified, use ..._DIR + +ifeq (,$$($(1)_SRC_DIR)) + $(1)_SRC_DIR=$$($(1)_DIR) +endif + + +# Generate object file names with path (for example h316/h316_cpu.o) + +$(1)_OBJS=$$(foreach obj, $$($(1)_MODULES), $$($(1)_OBJ_DIR)/$$(obj).o) + +# Add the object files to the CLEANSTUFF list + +CLEANSTUFF+=$$($(1)_OBJS) + +# The build rule. +# At the moment, header file changes might cause too many files to be rebuilt. But +# that seems to be acceptable for the moment. + +$$($(1)_OBJS) : $$($(1)_OBJ_DIR)/%.o : $$($(1)_SRC_DIR)/%.c $$($(1)_SRC_DIR)/*.h $$(COMMON_DIR)/*.h + +# Create output directory if it doesn't exist yet. + @if [ ! -d $$($(1)_OBJ_DIR) ]; then echo "MKDIR $$($(1)_OBJ_DIR)"; mkdir $$($(1)_OBJ_DIR); fi + +ifneq (,$$(VERBOSE)) + $$(CC) -c -o $$@ -I $$($(1)_DIR) $$(CFLAGS) $$($(1)_CFLAGS) $$< +else + @echo CC $$@ + @$$(CC) -c -o $$@ -I $$($(1)_DIR) $$(CFLAGS) $$($(1)_CFLAGS) $$< +endif +endef + + +################################# +# +# The executable file build rule template +# + +define PROGRAM_TEMPLATE + +# Add the executable to the CLEANSTUFF list + +CLEANSTUFF+=$$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + +# We won't build or mention the executable named "noting"! +ifneq (nothing,$$($(1)_EXEC)) +EXEC_TARGETS+=$$($(1)_EXEC) +endif + +# Generate a phony target with the simulator's name. +# Enables things like "make h316". +# On Windows this WILL NOT be "make h316.exe". + +$$($(1)_EXEC): $$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX) + +.PHONY: $$($(1)_EXEC) + +$$(TARGET_DIR)/$$($(1)_EXEC)$$(EXEC_SUFFIX): $$($(1)_OBJS) $$(COMMON_OBJS) + @if [ ! -d $$(TARGET_DIR) ]; then echo "MKDIR $$(TARGET_DIR)";mkdir $$(TARGET_DIR);fi + + +ifneq (,$$(VERBOSE)) + $$(CC) -o$$@ $$(LDFLAGS) $$($(1)_LDFLAGS) $$^ +else + @echo LD $$@ + @$$(CC) -o$$@ $$(LDFLAGS) $$($(1)_LDFLAGS) $$^ +endif +endef + + +################################# +# +# Use the template with SIM_NAMES +# + +$(foreach sn, $(SIM_NAMES),$(eval $(call OBJECT_TEMPLATE,$(sn)))) +$(foreach sn, $(SIM_NAMES),$(eval $(call PROGRAM_TEMPLATE,$(sn)))) + + +################################# +# +# The all target +# + +all: $(EXEC_TARGETS) + + +################################# +# +# Output a little help +# + +help: + @echo Available targets: + @echo + @echo "all - Build all simulators" + @echo "clean - Delete all generated files and directories" + @echo + @echo Simulators can explicitely built by the following targets: + @echo + @echo $(EXEC_TARGETS) + @echo + @echo $(CLEANSTUFF) + @echo + @echo The actual CC calls can be made visible with specifying VERBOSE=something. + @echo + + +################################# +# +# The clean rule. +# + +clean: + @echo CLEAN + @rm -rf $(CLEANSTUFF) + + +################################# +# +# Specify phony targets +# + +.PHONY: clean help all default + + +################################################################################ +# +# This is the end of the game :-) +# +################################################################################ + + + diff --git a/NOVA/eclipse.txt b/NOVA/eclipse.txt new file mode 100644 index 0000000..de753a4 --- /dev/null +++ b/NOVA/eclipse.txt @@ -0,0 +1,24 @@ +Charles Owen's Eclipse Modules + +1. Eclipse CPU simulator + +The Eclipse CPU simulator can be used with the Nova definitions and peripheral +modules to produce an Eclipse simulator that will run Eclipse mapped RDOS V7.5. +The compilation procedure is the same as for the Nova simulator, except: + + - the symbol ECLIPSE must be defined + - the module eclipse_cpu.c must be substituted for nova_cpu.c + - the output should be named eclipse rather than nova + +For example, to compile under UNIX, move nova_cpu.c out of the source directory +and then give this command: + + % cc -DECLIPSE eclipse_cpu.c nova_*.c -o eclipse + +2. Alternate terminal emulator + +The module eclipse_tt.c can be used with either an Eclipse or Nova CPU simulator +in place of nova_tt.c. It provides a full emulation of the cursor controls on +the Dasher video terminal but requires that the underlying operating system +interpret VT100 cursor controls. Thus, it works under VMS or UNIX but not under +Windows or OS/2. diff --git a/NOVA/eclipse_cpu.c b/NOVA/eclipse_cpu.c new file mode 100644 index 0000000..7802a1a --- /dev/null +++ b/NOVA/eclipse_cpu.c @@ -0,0 +1,6788 @@ +/* eclipse_cpu.c: Eclipse CPU simulator + + Modified from the original NOVA simulator by Robert Supnik. + + Copyright (c) 1998-2006, Charles E Owen + Portions Copyright (c) 1993-2002, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu Eclipse central processor + + 07-Jun-06 RMS Fixed bug in DIVS (found by Mark Hittinger) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 25-Aug-05 RMS Fixed DIVS overflow cases + 29-Nov-03 CEO Corrected POPJ and Bit operations bugs + 26-Nov-03 CEO Added FPU and PIT devices + 20-Feb-03 CEO Corrected several MMPU and CIS bugs + 28-Jan-02 RMS Cleaned up compiler warnings + 30-Nov-01 RMS Added extended SET/SHOW support + 01-Jun-01 RMS Added second terminal, plotter support + 26-Apr-01 RMS Added device enable/disable support + + The register state for the Eclipse CPU is basically the same as + the NOVA's: + + AC[0:3]<0:15> general registers + C carry flag + PC<0:14> program counter + + Eclipses with Folating Point Units added these registers: + + FPAC[0:3]<0:63> Floating Point Accumulators + FPSR Floating Point Status Register + + In addition, certain low-memory locations are reserved for special + purposes: + + 0: I/O Return Address (from an interrupt) + 1: I/O (Interrupt) handler address + 2: System Call handler address (used by SYC instruction) + 3: Protection Fault handler address + 4: VECTOR stack pointer (VCT Instruction) + 5: Current Interrupt Priority mask + 6: VECTOR stack limit (VCT instruction) + 7: VECTOR stack fault address (VCT again) + 10: Block Pointer (later models only) + 11: Emulation Trap Handler address (microeclipse only) + 20-27: Auto-increment locations (not on microeclipse) + 30-37: Auto-decrement locations (not on microeclipse) + 40: Stack pointer + 41: Frame Pointer + 42: Stack Limit + 43: Stack fault address + 44: XOP Origin address + 45: Floating point fault address + 46: Commercial fault address (not on microeclipse) + 47: Reserved, do not use. + + Note: While all eclipses share most of the "standard" features, + some models added a few quirks and wrinkles, and other models + dropped some features or modified others. Most DG software + is written for a "standard" Eclipse, and avoids these problem + areas. A general overview: + + [subject to major changes as info becomes available!] + + Early (e.g. S/100, S/200, C/300) [Front Panel machines] + + The first Eclipses had the basic MAP, but certain parts + were kluged, and these were fixed in later MAP designs. + The original mapping hardware was termed MAP for Memory + Allocate and Protection. The later design was termed + MMPU for Memory Mapping and Protection Unit. While + similar in design, the two units are not compatible. + Also, the C version (C for Commercial) of these early + CPUs had a feature called "Commercial Instruction Set" + which contained character manipulation, translation + between commercial-format numeric data and FPU formats, + and an elaborate EDIT instruction. Later models kept + only the character manipulation part of this and called + the feature the "Character Instruction Set", leading to + confusion because the initials of both are CIS. ARDOS + is the only DG operating system to support the older + MAP. ZRDOS uses the MMPU, and AOS supports only MMPU. + + Middle (e.g. S/130, C/150, S/230, C/330) [Front Panel] + + These are close to a "Standard". They have the newer, + fixed MMPU. Support for the PIT (Programmed Interval + Timer. The Commercial (not character) instruction set + and FPU are optional. (CIS standard on C models) + + Late (C/350, M/600: [Panel]; S/140, S/280 [Virtual Console]) + + All features of the Middle period are included, plus: + These late Eclipses added a few MMPU wrinkles all their + own, included support for user maps C and D. Character + instruction set is standard, FPU optional. Also, support + for the BMC device. + + MicroEclipse-based (S/20, S/120, Desktops) [Virtual cons.] + + All features of the Late period, in general, plus: + Microeclipses dropped support for the auto increment + and decrement locations at 20-37. They also added + support for invalid instruction traps thru location 11. + The Desktops have an interface to the "Attached Processor", + an 8086, at device code 6. Also, some new CPU device + features to read states info. The Character Instruction + set and FPU are standard on all models. + + The Eclipse instruction set is an elaboration of the NOVA's. The basic + NOVA set is implemented in it's entireity, plus many new Eclipse + instructions are added. Since in theory every possible 16-bit + combination is a NOVA instruction, the Eclipse commands are carved + out of the NOVA set by using the Operate format with the no-load bit + set to 1 and the skip bits set to 000. Since this combination is + in effect a no-op on the NOVA, it was rarely or never used. The + other bits are used to form Eclipse instructions, which have no + other common format. To see the instructions, refer to the Eclipse + section of the instruction decode logic in sim_instr() below. All + Eclipse instructions are checked first, so in case of conflict in + bit patterns, the Eclipse one is executed over the corresponding + NOVA pattern. A bizarre exception is LEF mode...which implements + an instruction called Load Effective Address by taking over the + Nova I/O format when the LEF mode bit is set and the processor is + executing in mapped mode. + + The following discussion talks about NOVA instructions which are + Eclipse instructions also. + + The NOVA has three instruction formats: memory reference, I/O transfer, + and operate. The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0| op | AC |in| mode| displacement | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> mnemonic action + + 00000 JMP PC = MA + 00001 JMS AC3 = PC, PC = MA + 00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0 + 001'n LDA ACn = M[MA] + 010'n STA M[MA] = ACn + + <5:7> mode action + + 000 page zero direct MA = zext (IR<8:15>) + 001 PC relative direct MA = PC + sext (IR<8:15>) + 010 AC2 relative direct MA = AC2 + sext (IR<8:15>) + 011 AC3 relative direct MA = AC3 + sext (IR<8:15>) + 100 page zero indirect MA = M[zext (IR<8:15>)] + 101 PC relative dinirect MA = M[PC + sext (IR<8:15>)] + 110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)] + 111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)] + + Memory reference instructions can access an address space of 32K words. + An instruction can directly reference the first 256 words of memory + (called page zero), as well as 256 words relative to the PC, AC2, or + AC3; it can indirectly access all 32K words. If an indirect address + is in locations 00020-00027, the indirect address is incremented and + rewritten to memory before use; if in 00030-00037, decremented and + rewritten. + + The I/O transfer format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 1 1| AC | opcode |pulse| device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IOT instruction sends the opcode, pulse, and specified AC to the + specified I/O device. The device may accept data, provide data, + initiate or cancel operations, or skip on status. + + The operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + \______/ \___/ \___/ | | | | + | | | | | | +--- reverse skip sense + | | | | | +--- skip if C == 0 + | | | | +--- skip if result == 0 + | | | +--- don't load result + | | +--- carry in (load as is, + | | set to Zero, + | | set to One, + | | load Complement) + | +--- shift (none, + | left one, + | right one, + | byte swap) + +--- operation (complement, + negate, + move, + increment, + add complement, + subtract, + add, + and) + + The operate instruction can be microprogrammed to perform operations + on the source and destination AC's and the Carry flag. + + This routine is the instruction decode routine for the NOVA. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + infinite indirection loop + unknown I/O device and STOP_DEV flag set + I/O error in I/O simulator + + 2. Interrupts. Interrupts are maintained by four parallel variables: + + dev_done device done flags + dev_disable device interrupt disable flags + dev_busy device busy flags + int_req interrupt requests + + In addition, int_req contains the interrupt enable and ION pending + flags. If ION and ION pending are set, and at least one interrupt + request is pending, then an interrupt occurs. Note that the 16b PIO + mask must be mapped to the simulator's device bit mapping. + + 3. Non-existent memory. On the NOVA, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + eclipse_defs.h add interrupt request definition + eclipse_cpu.c add IOT mask, PI mask, and routine to dev_table + eclipse_sys.c add pointer to data structures to sim_devices +*/ + +/*--------------------------------------------------------------------------- +** ECLIPSE Debugging Facilities +** +** These options are designed to find hard-to-locate flaky bugs by +** providing special error checking and logging. +** +** All are controlled by depositing a value into the DEBUG register. +** A value of zero means no special debugging facilities are turned on. +** This is the default. Debugging invokes a performance hit! Use only +** when necessary. +** +** Debugging means logging information to a file, or to a buffer in +** memory from whence it can be dumped to a file. +** +** 1XXXXX = Log all instructions executed to file "trace.log". +** **CAUTION**: This means the CPU will run SLOWLY and +** the resulting trace.log file will be HUGE. We're talking +** about a megabyte for each 5 seconds or less of wall clock +** time, depending on the speed of your CPU. Note: In this +** mode, interrupts are logged when they are received also. +** +** Note: when detailed logging is off, the last 4096 or so +** instructions executed are saved in a memory buffer, and +** when the sim stops, the "show" command can write this +** history information to the file "history.log". This only +** works if the DEBUG register is non-zero however, because +** of the performance hit even this recording makes. To +** dump history, enter the command "show cpu history", with +** the file "history" spelled correctly and lower case. +** +** XXXXDD = Log all I/O instructions to or from device number +** DD. Log is written to "trace.log", regardless of the +** setting of the instruction trace flag (1XXXXX). If both +** are on, the device traces will be interpersed with the +** instruction traces -- very useful sometimes. +** +** XXX1DD = Device Break. Does a breakpoint in any I/O to +** device DD. Useful, say when a diagnostic gives an +** error message - a device break on 11 (TTO) will stop +** as soon as the error message appears, making the +** trace log much shorter to track back on. +** +** X4XXXX = When this bit is on, the sim will stop if it sees +** an invalid instruction. When DEBUG is zero, any such +** instruction is no-oped with no warning. When DEBUG is +** non-zero, but this bit is 0, a warning will be displayed +** but execution will continue. +** +** X2XXXX = LEF break. When A LEF instruction is executed in +** mapped user space, the sim does a breakpoint right after +** executing the instruction. +** +** Whenever the DEBUG register is non-zero, special error checking +** is enabled in the sim. This will stop the sim automatically +** when a likely error occurs, such as: +** +** 1. Any execution that reaches, or will reach, location 00000. +** 2. Any I/O to device 00 +** 3. An interrupt from device 00. +** 4. An invalid instruction (stop is optional) +** +** DCHAR Register: Whenever this is non-zero, a test is made on every +** character output to the TTO device (master console). If the character +** output to that device matches this register, the CPU will break. +** +** Of course, the standard BREAK register is available for breakpoints +** as in all the sims based on this standard. +--------------------------------------------------------------------------*/ + +#include "nova_defs.h" + +#define UNIT_V_MICRO (UNIT_V_UF) /* Microeclipse? */ +#define UNIT_V_17B (UNIT_V_UF) /* 17 bit MAP */ +#define UNIT_V_UP (UNIT_V_UF) /* FPU Enabled */ +#define UNIT_V_MSIZE (UNIT_V_UF+1) /* dummy mask */ +#define UNIT_MICRO (1 << UNIT_V_MICRO) +#define UNIT_17B (1 << UNIT_V_17B) +#define UNIT_UP (1 << UNIT_V_UP) +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AC[4] = { 0 }; /* accumulators */ +int32 C = 0; /* carry flag */ +int32 saved_PC = 0; /* program counter */ +int32 SR = 0; /* switch register */ +int32 dev_done = 0; /* device done flags */ +int32 dev_busy = 0; /* device busy flags */ +int32 dev_disable = 0; /* int disable flags */ +int32 iot_enb = -1; /* IOT enables */ +int32 int_req = 0; /* interrupt requests */ +int32 pimask = 0; /* priority int mask */ +int32 pwr_low = 0; /* power fail flag */ +int32 ind_max = 15; /* iadr nest limit */ +int32 stop_dev = 0; /* stop on ill dev */ +int32 old_PC = 0; /* previous PC */ +int32 model = 140; /* Model of Eclipse */ +int32 speed = 0; /* Delay for each instruction */ + +int32 XCT_mode = 0; /* 1 if XCT mode */ +int32 XCT_inst = 0; /* XCT instruction */ +int32 PPC = -1; +int32 AMASK = 077777; + +struct ndev dev_table[64]; /* dispatch table */ + +/* Instruction history buffer */ + +#define HISTMAX 4096 + +int32 hnext = 0; /* # of current entry */ +int32 hwrap = 0; /* 1 if wrapped */ +int32 hmax = HISTMAX; /* Maximum entries b4 wrap */ +uint16 hpc[HISTMAX]; +uint16 hinst[HISTMAX]; +uint16 hinst2[HISTMAX]; +uint16 hac0[HISTMAX]; +uint16 hac1[HISTMAX]; +uint16 hac2[HISTMAX]; +uint16 hac3[HISTMAX]; +unsigned short hflags[HISTMAX]; + +/* Flags: 0x01 - carry bit + 0x02 - int enabled + 0x04 - user map a + 0x08 - user map b + 0x10 - user map c + 0x20 - user map d + 0x40 - LEF mode was on + 0x80 - this is an int, not an inst. + hpc is return addr + hinst is int_req + hac0 is device + hac1 is int addr +*/ + + + +/* the Eclipse MAP unit: This unit is standard in all Eclipse processors + except for the "original" Eclipses, the S/100, S/200, and C/300. These + use a different and more elaborate MMPU that is not compatible with + the one simulated here. All subsequent Eclipses, from the S/130 on up + to the last models S/280 and C/380 use the map simulated here, including + the MicroEclipses. There are model-dependent quirks. That's why we + have to MODEL register. + + The programming of the MMPU can be found in the LMP instruction, below, + and in the instructions directed to DEV_MAP. + + There are two user maps, called A and B, and four data channel maps, + A thru D. They can be enabled/disabled separately. Some models have + two extra user maps, C and D. These are supported where apporpriate. + +*/ + +#define PAGEMASK 01777 /* Largest physical page possible */ +#define MAPMASK 0101777 /* Valid page bits in map */ +#define INVALID 0101777 /* Mask indicating an invalid page */ +int32 MapStat = 0; /* Map status register */ +int32 Inhibit = 0; /* !0=inhibit interrupts : */ + /* 1 = single cycle inhibit */ + /* 2 = inhibit until indirection */ + /* 3 = inhibit next instruction only */ +int32 Enable = 0; /* User map to activate 1=A 2=B */ +int32 Usermap = 0; /* Active Map? 0=supvr mode, 1=user A, 2 = user B */ +int32 Map[8][32]; /* The actual MAPs 0=dch A, 1=A, 2=B, 3-5=dchB-D 6-7 User C-D */ +int32 Map31 = 037; /* Map for block 31 in supervisor mode */ +int32 SingleCycle = 0; /* Map one LDA/STA */ +int32 Check = 0; /* Page Check Register */ +int32 Fault = 0; /* Fault register */ +int32 MapInit = 0; /* 1 when map initialized */ +int32 MapIntMode = 0; /* Save of map user mode when int occurs */ + +/* The Eclipse Floating Point Unit: This unit is optional on all Eclipse + models. +*/ + +int32 FPSR = 0; /* 32-bit FPU Status Register */ +t_int64 FPAC[4] = { 0,0,0,0 }; /* 4 64-bit Accumulators */ +int32 FPFault = 0; /* Save Fault State */ + +/* Definitions for internal floating point arithmetic */ + +typedef struct _SHORT_FLOAT { + int32 short_fract; /* Fraction */ + short expo; /* Exponent + 64 */ + uint8 sign; /* Sign */ +} SHORT_FLOAT; + +typedef struct _LONG_FLOAT { + t_int64 long_fract; /* Fraction */ + short expo; /* Exponent + 64 */ + uint8 sign; /* Sign */ +} LONG_FLOAT; + +LONG_FLOAT dfl,dfl2; /* Double Precision Work Fields */ +SHORT_FLOAT sfl,sfl2; /* Single Precision Work Fields */ +t_int64 tempfp, holdfp; /* Working area for FPAC */ +int shift,m3; +t_int64 lsfract; + +void get_sf(SHORT_FLOAT *fl, t_int64 *fpr); +void store_sf(SHORT_FLOAT *fl, t_int64 *fpr); +void get_lf(LONG_FLOAT *fl, t_int64 *fpr); +void store_lf(LONG_FLOAT *fl, t_int64 *fpr); +int normal_sf (SHORT_FLOAT *fl); +int normal_lf (LONG_FLOAT *fl); +int overflow_sf(SHORT_FLOAT *fl); +int overflow_lf(LONG_FLOAT *fl); +int underflow_sf(SHORT_FLOAT *fl); +int underflow_lf(LONG_FLOAT *fl); +int significance_sf(SHORT_FLOAT *fl); +int significance_lf(LONG_FLOAT *fl); +int add_sf(SHORT_FLOAT *fl, SHORT_FLOAT *add_f1, int normal); +int add_lf(LONG_FLOAT *fl, LONG_FLOAT *add_fl, int normal); +int mul_sf(SHORT_FLOAT *fl, SHORT_FLOAT *mul_fl); +int mul_lf(LONG_FLOAT *fl, LONG_FLOAT *mul_fl); +int div_sf(SHORT_FLOAT *fl, SHORT_FLOAT *div_fl); +int div_lf(LONG_FLOAT *fl, LONG_FLOAT *div_fl); + +/* Special Debugging Info */ + +int32 Debug_Flags = 0; /* Debug register - selects debug features */ +int32 Debug_Char = 0; /* Debug Character Register */ + +int32 Tron = 0; /* For trace files */ +FILE *Trace; + + +t_stat reason; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern DEVICE *sim_devices[]; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_boot (int32 unitno, DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat Debug_Dump (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat Dump_History (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat map_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat map_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat map_reset (DEVICE *dptr); +t_stat map_svc (UNIT *uptr); +t_stat fpu_svc (UNIT *uptr); +int32 GetMap(int32 addr); +int32 PutMap(int32 addr, int32 data); +int32 Debug_Entry(int32 PC, int32 inst, int32 inst2, int32 AC0, int32 AC1, int32 AC2, int32 AC3, int32 flags); +t_stat build_devtab (void); + +extern t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 15) }, + { ORDATA (AC0, AC[0], 16) }, + { ORDATA (AC1, AC[1], 16) }, + { ORDATA (AC2, AC[2], 16) }, + { ORDATA (AC3, AC[3], 16) }, + { FLDATA (C, C, 16) }, + { ORDATA (SR, SR, 16) }, + { ORDATA (PI, pimask, 16) }, + { FLDATA (ION, int_req, INT_V_ION) }, + { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, + { FLDATA (PWR, pwr_low, 0) }, + { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, + { ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO }, + { ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO }, + { ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO }, + { FLDATA (STOP_DEV, stop_dev, 0) }, + { DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT }, + { ORDATA (DEBUG, Debug_Flags, 16) }, + { ORDATA (DCHAR, Debug_Char, 16) }, + { DRDATA (MODEL, model, 16) }, + { DRDATA (SPEED, speed, 16) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } +}; + +MTAB cpu_mod[] = { + { UNIT_MICRO, UNIT_MICRO, "MICRO", "MICRO", NULL }, + { UNIT_MICRO, 0, "STD", "STD", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, + { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size }, + { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size }, + { UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size }, + { UNIT_MSIZE, 0, NULL, "DUMP", &Debug_Dump }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "HISTORY", NULL, + NULL, &Dump_History }, + { 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 17, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + &cpu_boot, NULL, NULL +}; + +/* MAP data structures + + map_dev MAP device descriptor + map_unit MAP unit descriptor + map_reg MAP register list + map_mod MAP modifiers list +*/ + +UNIT map_unit = { UDATA (&map_svc, UNIT_17B, MAXMEMSIZE) }; + +REG map_reg[] = { + { ORDATA (STATUS, MapStat, 16) }, + { ORDATA (ENABLE, Enable, 16) }, + { ORDATA (IINHIB, Inhibit, 16) }, + { ORDATA (ACTIVE, Usermap, 16) }, + { ORDATA (MAP31, Map31, 16) }, + { ORDATA (CYCLE, SingleCycle, 16) }, + { ORDATA (CHECK, Check, 16) }, + { ORDATA (FAULT, Fault, 16) }, + { NULL } +}; + +MTAB map_mod[] = { + { UNIT_17B, UNIT_17B, "17bit", "17B", NULL }, + { UNIT_17B, 0, "19bit", "19B", NULL }, + { 0 } +}; + +DEVICE map_dev = { + "MAP", &map_unit, map_reg, map_mod, + 1, 8, 17, 1, 8, 16, + &map_ex, &map_dep, NULL, + NULL, NULL, NULL +}; + +/* FPU data structures + + fpu_dev MAP device descriptor + fpu_unit MAP unit descriptor + fpu_reg MAP register list + fpu_mod MAP modifiers list +*/ + +UNIT fpu_unit = { UDATA (&fpu_svc, UNIT_UP, MAXMEMSIZE) }; + +REG fpu_reg[] = { + { ORDATA (STATUS, FPSR, 32) }, + { ORDATA (FPAC0, FPAC[0], 64) }, + { ORDATA (FPAC1, FPAC[1], 64) }, + { ORDATA (FPAC2, FPAC[2], 64) }, + { ORDATA (FPAC3, FPAC[3], 64) }, + { ORDATA (FAULT, FPFault, 32) }, + { NULL } +}; + +MTAB fpu_mod[] = { + { UNIT_UP, UNIT_UP, "Enabled (UP)", "UP", NULL }, + { UNIT_UP, 0, "Disabled (DOWN)", "DOWN", NULL }, + { 0 } +}; + +DEVICE fpu_dev = { + "FPU", &fpu_unit, fpu_reg, fpu_mod, + 1, 16, 17, 1, 16, 16, + NULL, NULL, NULL, + NULL, NULL, NULL +}; + + +/* ---- Programmable Interval Timer Device ----------- */ + +int32 pit_time = 100; +int32 pit_tps = 10000; /* ticks per sec */ +int32 pit_adj = 20; /* tmxr adjust */ +int32 pit_poll = 16000; /* tmxr poll */ +int32 pit_initial = 0; /* initial counter reg */ +int32 pit_counter = 0; /* Counter */ +int32 pit_flag = 0; /* Initial setting flag */ + +int32 pit (int32 pulse, int32 code, int32 AC); +t_stat pit_svc (UNIT *uptr); +t_stat pit_reset (DEVICE *dptr); + +/* PIT data structures + + pit_dev device descriptor + pit_unit unit descriptor + pit_reg register list +*/ + +DIB pit_dib = { DEV_PIT, INT_PIT, PI_PIT, &pit }; + +UNIT pit_unit = { UDATA (&pit_svc, 0, 0) }; + +REG pit_reg[] = { + { ORDATA (INIT, pit_initial, 16) }, + { ORDATA (COUNT, pit_counter, 16) }, + { FLDATA (BUSY, dev_busy, INT_V_PIT) }, + { FLDATA (DONE, dev_done, INT_V_PIT) }, + { FLDATA (DISABLE, dev_disable, INT_V_PIT) }, + { FLDATA (INT, int_req, INT_V_PIT) }, + { DRDATA (TIME0, pit_time, 24), REG_NZ + PV_LEFT }, + { NULL } +}; + +DEVICE pit_dev = { + "PIT", &pit_unit, pit_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &pit_reset, + NULL, NULL, NULL, + &pit_dib, 0 +}; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 PC, IR, i, t, MA, j, k, tac; +register uint32 mddata, uAC0, uAC1, uAC2, uAC3; +int16 sAC0, sAC1, sAC2; +int32 sddata, mi1, mi2, fpnum32; +t_int64 fpnum, expon; +t_value simeval[20]; +void mask_out (int32 mask); +/* char debstr[128]; */ +/* char debadd[64]; */ +char debmap[4], debion[4]; +int debcar, iodev, iodata, debflags; +int32 DisMap, debpc; +/* int32 sp, sl; */ +int cmdptr, cmsptr, cmopt, cmptr; +int16 cmslen, cmdlen; +int tabaddr, tabptr; +int32 effective(int32 PC, int32 index, int32 disp); +int32 indirect(int32 d); +int32 LEFmode(int32 PC, int32 index, int32 disp, int32 indirect); +int32 LoadMap(int32 w); +int32 Bytepointer(int32 PC, int32 index); +int32 unimp(int32 PC); +int32 pushrtn(int32 pc); + +/* Restore register state */ + +if (build_devtab () != SCPE_OK) return SCPE_IERR; /* build dispatch */ +PC = saved_PC & AMASK; /* load local PC */ +C = C & 0200000; +mask_out (pimask); /* reset int system */ +reason = 0; +if (MapInit == 0) { + MapInit = 1; + for (mi1 = 0; mi1 < 6; mi1++) { /* Initialize MAPs */ + for (mi2 = 0; mi2 < 32; mi2++) { + Map[mi1][mi2] = mi2; + } + } +} + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) + break; +} + +//if (speed > 0) for (i = 0; i < speed; i++) { j = 0; } + +if (Fault) { /* Check MAP fault */ + Usermap = 0; /* YES: shutdown map */ + MapStat &= ~01; /* Disable MMPU */ + if (Fault & 0100000/*!!!*/) /* If it was validity, or WP */ + MapStat &= ~0170; /* Reset other checkbits */ + MapStat |= Fault & 077777; /* Put in fault code */ + Fault = 0; /* Reset fault code */ + t = (GetMap(040) + 1) & AMASK; /* Push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, (PC & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + int_req = int_req & ~INT_ION; /* Disable interrupts */ + PC = indirect(M[003]); /* JMP to loc 3 */ + continue; +} + +if (FPSR & 0xF8000000) { /* FPU Fault? */ + if (!(FPSR & 0x78000000)) { /* If error bit on ... */ + FPSR &= 0x00FFFFFF; /* ...but no error, clear it */ + } else { /* ELSE a real error: */ + FPSR |= 0x80000000; /* Turn error bit on */ + if (FPSR & 0x04000000) { /* Trap enabled ? */ + FPFault = FPSR; /* Save fault */ + FPSR &= 0xFBFFFFFF; /* Clear Trap Enable */ + } + } +} + +if (int_req > INT_PENDING && !Inhibit) { /* interrupt? */ + int_req = int_req & ~INT_ION; + MapIntMode = MapStat; /* Save Status as it was */ + Usermap = 0; /* Inhibit MAP */ + MapStat &= ~1; /* Disable user map */ + if (XCT_mode) { + M[0] = PC - 1; /* If XCT mode rtn to XCT */ + XCT_mode = 0; /* turn off mode */ + } else { + M[0] = PC; /* Save Return Address */ + } + old_PC = PC; + MA = M[1]; + for (i = 0; i < ind_max * 2; i++) { /* count indirects */ + if ((MA & 0100000) == 0) break; + if ((MA & 077770) == 020) + MA = (M[MA & AMASK] = (M[MA & AMASK] + 1) & 0177777); + else if ((MA & 077770) == 030) + MA = (M[MA & AMASK] = (M[MA & AMASK] - 1) & 0177777); + else MA = M[MA & AMASK]; + } + if (i >= (ind_max-1)) { + if ((MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + continue; + } else { + reason = STOP_IND_INT; + break; + } + } + if (Debug_Flags) { + iodev = 0; + iodata = int_req & (-int_req); + for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (iodata & dev_table[i].mask) { + iodev = i; + break; + } + } + if (iodev == 0) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (Debug_Flags & 0100000) { + fprintf(Trace, "--------- Interrupt %o (%o) to %6o ---------\n", int_req, iodev, MA); + } else { + Debug_Entry(PC, int_req, 0, iodev, MA, 0, 0, 0x80); + } + } + PC = MA; +} /* end interrupt */ + +if (Inhibit != 0) { /* Handle 1-instruction inhibit sequence */ + if (Inhibit == 3) /* Used by SYC instruction */ + Inhibit = 4; + if (Inhibit == 4) + Inhibit = 0; +} + +if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; +} + +if ((PC < 1 || PC > 077777) && Debug_Flags) { + if (PPC != -1) { /* Don't break on 1st instruction */ + printf("\n<>\n\r", PC, PPC); + reason = STOP_IBKPT; + break; + } +} + +PPC = PC; + +if (Debug_Flags) { + if (!Tron) { + Tron = 1; + Trace = fopen("trace.log", "w"); + } + strcpy(debmap, " "); + strcpy(debion, " "); + debcar = 0; + if (C) debcar = 1; + if (Usermap == 1) strcpy(debmap, "A"); + if (Usermap == 2) strcpy(debmap, "B"); + if (Usermap == 5) strcpy(debmap, "C"); + if (Usermap == 6) strcpy(debmap, "D"); + if (int_req & INT_ION) strcpy(debion, "I"); + if (XCT_mode == 0) { + debpc = PC; + simeval[0] = GetMap(PC); + simeval[1] = GetMap(PC+1); + } else { + debpc = 0177777; + simeval[0] = XCT_inst; + simeval[1] = 0; + } + if (Debug_Flags & 0100000) { + fprintf(Trace, "%s%s%06o acs: %06o %06o %06o %06o %01o ", + debion, debmap, debpc, AC[0], AC[1], AC[2], AC[3], debcar); + fprint_sym (Trace, debpc, simeval, NULL, SWMASK('M')); + fprintf(Trace, "\n"); + } else { + debflags = 0; + if (C) debflags |= 0x01; + if (int_req & INT_ION) debflags |= 0x02; + if (Usermap == 1) debflags |= 0x04; + if (Usermap == 2) debflags |= 0x08; + if (Usermap == 3) debflags |= 0x10; + if (Usermap == 4) debflags |= 0x20; + Debug_Entry(debpc, (int32)simeval[0], (int32)simeval[1], AC[0], AC[1], AC[2], AC[3], debflags); + } +} + +if (XCT_mode == 0) { /* XCT mode? */ + IR = GetMap(PC); /* No: fetch instr */ + if (Fault) continue; /* Give up if fault */ + PC = (PC + 1) & AMASK; /* bump PC */ +} else { + IR = XCT_inst; /* Yes: Get inst to XCT */ + XCT_mode = 0; /* Go back to normal mode */ +} +int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ +sim_interval = sim_interval - 1; +t = IR >> 11; /* prepare to decode */ + +/* ---------------- BEGIN Eclipse modification --------------------- */ + +/* Eclipse instruction set. These instructions are checked for + before any of the NOVA ones. Eclipse instructions do not + correspond to any patterns, other than bit 0 being 1 and + the last 4 bits are 1000. Words which are not Eclipse + instructions will be interpreted as Nova instructions. */ + +/* Important Note: The order of the if statements is important. + Frequently executed instructions should come first, to enhance + the speed of the simulation. +*/ + +if ((IR & 0100017) == 0100010) { /* This pattern for all */ + /* Eclipse instructions */ + +/****************************************************************/ +/* This is the standard Eclipse instruction set */ +/****************************************************************/ + + /* Byte operations */ + + if ((IR & 0103777) == 0102710) { /* LDB: Load Byte */ + i = (IR >> 13) & 03; + MA = (AC[i] >> 1) & AMASK; + j = (IR >> 11) & 03; + if (AC[i] & 01) { + AC[j] = GetMap(MA) & 0377; + } else { + AC[j] = (GetMap(MA) >> 8) & 0377; + } + continue; + } + if ((IR & 0103777) == 0103010) { /* STB: Store Byte */ + i = (IR >> 13) & 03; + MA = (AC[i] >> 1); + j = (IR >> 11) & 03; + t = GetMap(MA); + if (AC[i] & 01) { + t &= 0177400; + t |= (AC[j] & 0377); + PutMap(MA, t); + } else { + t &= 0377; + t |= (AC[j] & 0377) << 8; + PutMap(MA, t); + } + continue; + } + + /* Fixed-point arithmetic - loads & saves */ + + if ((IR & 0162377) == 0122070) { /* ELDA: Extended LDA */ + i = (IR >> 11) & 3; + t = GetMap(PC); + if (SingleCycle) Usermap = SingleCycle; + AC[i] = GetMap(effective(PC, (IR >> 8) & 3, t)); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0162377) == 0142070) { /* ESTA: Extended STA */ + i = (IR >> 11) & 3; + t = GetMap(PC); + if (SingleCycle) Usermap = SingleCycle; + PutMap((effective(PC, (IR >> 8) & 3, t)), AC[i]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100010) { /* ADI: Add Immediate */ + t = (IR >> 11) & 3; + AC[t] = (AC[t] + ((IR >> 13) & 3) + 1) & 0xffff; + continue; + } + if ((IR & 0103777) == 0100110) { /* SBI: Subtract Immediate */ + t = (IR >> 11) & 3; + AC[t] = (AC[t] - (((IR >> 13) & 3) + 1)) & 0xffff; + continue; + } + if ((IR & 0163777) == 0163770) { /* ADDI: Extended Add Immed. */ + t = (IR >> 11) & 3; + i = GetMap(PC); + PC = (PC + 1) & AMASK; + AC[t] = (AC[t] + i) & 0xffff; + continue; + } + if ((IR & 0103777) == 0100710) { /* XCH: Exchange Accumulators */ + t = AC[(IR >> 11) & 3]; + AC[(IR >> 11) & 3] = AC[(IR >> 13) & 3]; + AC[(IR >> 13) & 3] = t; + continue; + } + if ((IR & 0162377) == 0162070) { /* ELEF: Load Effective Addr */ + t = GetMap(PC); + AC[(IR >> 11) & 3] = effective(PC, (IR >> 8) & 3, t); + PC = (PC + 1) & AMASK; + continue; + } + + /* Logical operations */ + + if ((IR & 0163777) == 0143770) { /* ANDI: And Immediate */ + AC[(IR >> 11) & 3] &= GetMap(PC); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0103770) { /* IORI: Inclusive Or Immed */ + AC[(IR >> 11) & 3] |= GetMap(PC); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0123770) { /* XORI: Exclusive Or Immed */ + AC[(IR >> 11) & 3] ^= GetMap(PC); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100410) { /* IOR: Inclusive Or */ + AC[(IR >> 11) & 3] |= AC[(IR >> 13) & 3]; + continue; + } + if ((IR & 0103777) == 0100510) { /* XOR: Exclusive Or */ + AC[(IR >> 11) & 3] ^= AC[(IR >> 13) & 3]; + continue; + } + if ((IR & 0103777) == 0100610) { /* ANC: And with complemented src */ + AC[(IR >> 11) & 3] &= ~(AC[(IR >> 13) & 3]); + continue; + } + + /* Shift operations */ + + if ((IR & 0103777) == 0101210) { /* LSH: Logical Shift */ + register int16 sh; + sh = AC[(IR >> 13) & 3] & 0377; + i = (IR >> 11) & 3; + if (sh & 0200) { + sh = ~sh + 1; + AC[i] = AC[i] >> sh; + } else { + AC[i] = AC[i] << sh; + } + if (sh > 15) AC[i] = 0; + AC[i] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0101310) { /* DLSH: Double logical shift */ + register int16 sh; + sh = AC[(IR >> 13) & 3] & 0377; + i = (IR >> 11) & 3; + uAC0 = AC[i] << 16; + j = i + 1; + if (j == 4) j = 0; + uAC0 |= AC[j]; + if (sh & 0200) { + sh = (~sh + 1) & 0377; + if (sh < 32) + uAC0 = uAC0 >> sh; + } else { + if (sh < 32) + uAC0 = uAC0 << sh; + } + if (sh > 31) uAC0 = 0; + AC[i] = (uAC0 >> 16) & 0xffff; + AC[j] = uAC0 & 0xffff; + continue; + } + if ((IR & 0103777) == 0101410) { /* HXL: Hex shift left */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + AC[i] = AC[i] << (t * 4); + AC[i] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0101510) { /* HXR: Hex shift right */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + AC[i] = AC[i] >> (t * 4); + AC[i] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0101610) { /* DHXL: Double Hex shift left */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + j = i + 1; + if (j == 4) j = 0; + uAC0 = AC[i] << 16; + uAC0 |= AC[j]; + uAC0 = uAC0 << ((t * 4) & 0177); + AC[i] = (uAC0 >> 16) & 0xffff; + AC[j] = uAC0 & 0xffff; + continue; + } + if ((IR & 0103777) == 0101710) { /* DHXR: Double Hex shift right */ + t = ((IR >> 13) & 3) + 1; + i = (IR >> 11) & 3; + j = i + 1; + if (j == 4) j = 0; + uAC0 = AC[i] << 16; + uAC0 |= AC[j]; + uAC0 = uAC0 >> ((t * 4) & 0177); + AC[i] = (uAC0 >> 16) & 0xffff; + AC[j] = uAC0 & 0xffff; + continue; + } + + /* Bit operations */ + + if ((IR & 0103777) == 0102010) { /* BTO: Set bit to one */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + if ((AC[j] + k) & 0100000) + t = 1; +//AOS MA = indirect(AC[j] + k); + MA = (AC[j] + k) & AMASK; + } else { + MA = (AC[i] >> 4) & AMASK; + } + t = AC[i] & 017; + t = GetMap(MA) | (0100000 >> t); + PutMap(MA, t); + continue; + } + if ((IR & 0103777) == 0102110) { /* BTZ: Set bit to zero */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + if ((AC[j] + k) & 0100000) + t = 1; +//AOS MA = indirect(AC[j] + k); + MA = (AC[j] + k) & AMASK; + } else { + MA = (AC[j] >> 4) & AMASK; + } + t = AC[i] & 017; + t = GetMap(MA) & ~(0100000 >> t); + PutMap(MA, t); + continue; + } + if ((IR & 0103777) == 0102210) { /* SZB: Skip on zero bit */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + if ((AC[j] + k) & 0100000) + t = 1; + MA = indirect(AC[j] + k); +// MA = (AC[j] + k) & AMASK; + } else { + MA = (AC[i] >> 4) & AMASK; + } + t = GetMap(MA) << (AC[i] & 017); + if (!(t & 0100000)) PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102770) { /* SNB: Skip on non-zero bit */ + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + if ((AC[j] + k) & 0100000) + t = 1; + MA = indirect(AC[j] + k); +// MA = (AC[j] + k) & AMASK; + } else { + MA = (AC[j] >> 4) & AMASK; + } + t = GetMap(MA) << (AC[i] & 017); + if (t & 0100000) PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102310) { /* SZBO: skip on zero bit & set to 1 */ + register int32 save; + i = (IR >> 11) & 3; + j = (IR >> 13) & 3; + if (i != j) { + k = (AC[i] >> 4) & AMASK; + MA = indirect(AC[j] + k); +// MA = (AC[j] + k) & AMASK; + } else { + MA = (AC[j] >> 4) & AMASK; + } + t = AC[i] & 017; + save = GetMap(MA); + t = save | (0100000 >> t); + PutMap(MA, t); + t = save << (AC[i] & 017); + if ((t & 0100000) == 0) + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102410) { /* LOB: Locate lead bit */ + register int32 a, r; + register int16 b; + a = AC[(IR >> 13) & 3] & 0xffff; + for (i = 0; i < 16; i++) { + if ((a << i) & 0100000) break; + } + r = (IR >> 11) & 3; + b = AC[r]; + b += i; + AC[r] = b & 0177777; + continue; + } + if ((IR & 0103777) == 0102510) { /* LRB: Locate & reset lead bit */ + register int32 a, r; + register int16 b; + j = (IR >> 13) & 3; + a = AC[j]; + for (i = 0; i < 16; i++) { + if ((a << i) & 0100000) break; + } + r = (IR >> 11) & 3; + b = AC[r]; + b += i; + if (j != r) AC[r] = b & 0177777; + AC[j] &= ~(0100000 >> i); + AC[j] &= 0xffff; + continue; + } + if ((IR & 0103777) == 0102610) { /* COB: Count bits */ + register int32 a; + register int16 b, c = 0; + a = AC[(IR >> 13) & 3]; + for (i = 0; i < 16; i++) { + if ((a >> i) & 1) c++; + } + i = (IR >> 11) & 3; + b = AC[i]; + b += c; + AC[i] = b & 0177777; + continue; + } + + /* Jump & similar operations */ + + if ((IR & 0176377) == 0102070) { /* EJMP: Extended JMP */ + PC = effective(PC, (IR >> 8) & 3, GetMap(PC)); + continue; + } + if ((IR & 0176377) == 0106070) { /* EJSR: Extended JMP to subr */ + t = effective(PC, (IR >> 8) & 3, GetMap(PC)); + AC[3] = (PC + 1) & AMASK; + PC = t & AMASK; + continue; + } + if ((IR & 0176377) == 0112070) { /* EISZ: Ext Inc & skip if 0 */ + MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); + PutMap(MA, ((GetMap(MA) + 1) & 0xffff)); + if (GetMap(MA) == 0) PC = (PC + 1) & AMASK; + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0176377) == 0116070) { /* EDSZ: Ext Dec & skip if 0 */ + MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); + PutMap(MA, ((GetMap(MA) - 1) & 0xffff)); + if (GetMap(MA) == 0) PC = (PC + 1) & AMASK; + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0101010) { /* SGT: Skip if ACS > ACD */ + register int16 a1, d1; + a1 = AC[(IR >> 13) & 3] & 0xffff; + d1 = AC[(IR >> 11) & 3] & 0xffff; + if (a1 > d1) + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0101110) { /* SGE: Skip if ACS >= ACD */ + register int16 a1, d1; + a1 = AC[(IR >> 13) & 3] & 0xffff; + d1 = AC[(IR >> 11) & 3] & 0xffff; + if (a1 >= d1) + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102370) { /* CLM: Compare to limits */ + register int32 s, d, MA; + int16 H, L, ca; + s = (IR >> 13) & 3; + d = (IR >> 11) & 3; + if (s == d) { + L = GetMap(PC); + PC++; + H = GetMap(PC); + PC++; + } else { + MA = AC[d] & AMASK; + L = GetMap(MA); + H = GetMap(MA + 1); + } + ca = AC[s]; + if (ca >= L && ca <= H) PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0123370) { /* XCT: Execute */ + XCT_mode = 1; /* Set up to execute on next loop */ + XCT_inst = AC[(IR >> 11) & 3]; + continue; + } + + /* Memory block operations */ + + if (IR == 0113710) { /* BAM: Block add & move */ + register int32 w; + t = AC[1]; + if (t < 1 || t > 0100000) + continue; + i = indirect(AC[2]); + j = indirect(AC[3]); + while (t) { + w = GetMap(i); + PutMap(j, ((w + AC[0]) & 0xffff)); + if (Fault) break; + t--; + i++; + j++; + i &= AMASK; + j &= AMASK; + } + AC[1] = t; + AC[2] = i & AMASK; + AC[3] = j & AMASK; + continue; + } + if (IR == 0133710) { /* BLM: Block move */ + t = AC[1]; + if (t < 1 || t > 0100000) + continue; + i = indirect(AC[2]); + j = indirect(AC[3]); + if (Fault) continue; + while (t) { + PutMap(j, GetMap(i)); + if (Fault) break; + t--; + i++; + j++; + i &= AMASK; + j &= AMASK; + } + AC[1] = t; + AC[2] = i & AMASK; + AC[3] = j & AMASK; + continue; + } + + /* Stack operations */ + + if ((IR & 0103777) == 0103110) { /* PSH: Push multiple accums */ + register int32 j; + j = (IR >> 11) & 3; + t = GetMap(040) & AMASK; + i = (IR >> 13) & 3; + if (i == j) { + t++; + PutMap(t, AC[i]); + PutMap(040, (t & AMASK)); + if (t > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + while (i != j) { + t++; + PutMap(t, AC[i]); + i++; + if (i == 4) i = 0; + } + t++; + PutMap(t, AC[i]); + PutMap(040, (t & AMASK)); + if ((GetMap(040) & AMASK) > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if ((IR & 0103777) == 0103210) { /* POP: Pop mult accums */ + j = (IR >> 11) & 3; + t = GetMap(040) & AMASK; + i = (IR >> 13) & 3; + if (i == j) { + AC[i] = GetMap(t); + t--; + PutMap(040, (t & AMASK)); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + PutMap(040, GetMap(042)); + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + while (i != j) { + AC[i] = GetMap(t); + t--; + i--; + if (i == -1) i = 3; + } + AC[i] = GetMap(t); + t--; + PutMap(040, (t & AMASK)); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + PutMap(040, GetMap(042)); + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0103710) { /* PSHR: Push return addr */ + t = (GetMap(040) + 1) & AMASK; + PutMap(t, (PC + 1)); + PutMap(040, t); + if ((GetMap(040) & AMASK) > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0163710) { /* SAVE */ + register int32 savep; + savep = ((GetMap(PC) + GetMap(040)) + 5) & AMASK; + if (savep > GetMap(042)) { + pushrtn(PC-1); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + continue; + } + t = GetMap(040) + 1; + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, GetMap(041)); + t++; + savep = PC; + PC = (PC + 1) & AMASK; + PutMap(t, (AC[3] & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + AC[3] = GetMap(040) & AMASK; + PutMap(041, AC[3]); + PutMap(040, ((GetMap(040) + GetMap(savep)) & AMASK)); + continue; + } + if ((IR & 0163777) == 0103370) { /* MSP: Modify stack pointer */ + t = (GetMap(040) + AC[(IR >> 11) & 3]) & 0177777; + if (t > GetMap(042)) { + pushrtn(PC-1); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & AMASK)); + PutMap(042, (GetMap(042) | 0100000)); + continue; + } + PutMap(040, t); + continue; + } + if ((IR & 0176377) == 0102270) { /* PSHJ: Push JMP */ + PutMap(040, (GetMap(040) + 1)); + PutMap((GetMap(040) & AMASK), ((PC + 1) & AMASK)); + if ((GetMap(040) & AMASK) > (GetMap(042) & AMASK)) { + pushrtn(PC+1); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + continue; + } + PC = effective(PC, (IR >> 8) & 3, GetMap(PC)); + continue; + } + if (IR == 0117710) { /* POPJ: Pop PC and Jump */ + PC = GetMap(GetMap(040)) & AMASK; + PutMap(040, (GetMap(040) - 1)); + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + j = GetMap(042); + t = GetMap(040); + if ((j < 0100000 && t < 0100000) && (t < 0400) && (t > 0)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0107710) { /* POPB: Pop block */ + PC = GetMap(GetMap(040)) & AMASK; + if (GetMap(GetMap(040)) & 0100000) + C = 0200000; + else + C = 0; + PutMap(040, (GetMap(040) - 1)); + AC[3] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[2] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[1] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[0] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + continue; + } + if (IR == 0127710) { /* RTN: Return */ + PutMap(040, GetMap(041)); + PC = GetMap(GetMap(040)) & AMASK; + t = GetMap(040); + t = GetMap(t); + if (t & 0100000) + C = 0200000; + else + C = 0; + PutMap(040, (GetMap(040) - 1)); + AC[3] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[2] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[1] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[0] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + PutMap(041, AC[3]); + t = GetMap(040); + if (t < 0100000 && t < 0400) { + pushrtn(PC); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + PC = indirect(GetMap(043)); + } + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + continue; + } + if (IR == 0167710) { /* RSTR: Restore */ + int32 SVPC; + + SVPC = PC; + PC = GetMap(GetMap(040)) & AMASK; + if (PC == 0 && Debug_Flags) { + printf("\n<>\n\r", SVPC); + reason = STOP_IBKPT; + } + if (GetMap(GetMap(040)) & 0100000) + C = 0200000; + else + C = 0; + PutMap(040, (GetMap(040) - 1)); + AC[3] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[2] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[1] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + AC[0] = GetMap(GetMap(040)); + PutMap(040, (GetMap(040) - 1)); + PutMap(043, GetMap(GetMap(040))); + PutMap(040, (GetMap(040) - 1)); + PutMap(042, GetMap(GetMap(040))); + PutMap(040, (GetMap(040) - 1)); + PutMap(041, GetMap(GetMap(040))); + PutMap(040, (GetMap(040) - 1)); + PutMap(040, GetMap(GetMap(040))); + /*t = GetMap(040); + if (t < 0100000 && t < 0400) { + pushrtn(PC); + PC = indirect(GetMap(043)); + }*/ + if (MapStat & 1) { + Usermap = Enable; + Inhibit = 0; + } + continue; + } + + /* Multiply / Divide */ + + if (IR == 0143710) { /* MUL: Unsigned Multiply */ + uAC0 = (uint32) AC[0]; + uAC1 = (uint32) AC[1]; + uAC2 = (uint32) AC[2]; + + mddata = (uAC1 * uAC2) + uAC0; + AC[0] = (mddata >> 16) & 0177777; + AC[1] = mddata & 0177777; + continue; + } + if (IR == 0147710) { /* MULS: Signed Multiply */ + sAC0 = AC[0]; + sAC1 = AC[1]; + sAC2 = AC[2]; + + sddata = (sAC1 * sAC2) + sAC0; + AC[0] = (sddata >> 16) & 0177777; + AC[1] = sddata & 0177777; + continue; + } + if (IR == 0153710) { /* DIV: Unsigned Divide */ + uAC0 = (uint32) AC[0]; + uAC1 = (uint32) AC[1]; + uAC2 = (uint32) AC[2]; + + if (uAC0 >= uAC2) C = 0200000; + else { + C = 0; + mddata = (uAC0 << 16) | uAC1; + AC[1] = mddata / uAC2; + AC[0] = mddata % uAC2; + } + continue; + } + if (IR == 0157710) { /* DIVS: Signed Divide */ + if ((AC[0] == 0) || + ((AC[0] == 0100000) && (AC[1] == 0) && (AC[2] == 0177777))) + C = 0200000; + else { + sAC2 = AC[2]; + C = 0; + sddata = ((AC[0] & 0xffff) << 16) | (AC[1] & 0xffff); + AC[1] = sddata / sAC2; + AC[0] = sddata % sAC2; + if (AC[0] > 077777 || AC[0] < -077776) C = 0200000; + /*if ((AC[0] & 0xFFFF0000) != 0) C = 0200000;*/ + if (AC[1] > 077777 || AC[1] < -077776) C = 0200000; + /*if ((AC[1] & 0xFFFF0000) != 0) C = 0200000;*/ + AC[0] &= 0177777; + AC[1] &= 0177777; + } + continue; + } + if (IR == 0137710) { /* DIVX: Sign extend and Divide */ + int32 q; + if (AC[1] & 0100000) { + AC[0] = 0177777; + } else { + AC[0] = 0; + } + sAC0 = AC[0]; + sAC1 = AC[1]; + sAC2 = AC[2]; + + C = 0; + sddata = (sAC0 << 16) | sAC1; + q = sddata / sAC2; + AC[0] = sddata % sAC2; + if (q > 0177777) { + C = 0200000; + } else { + AC[1] = q & 0xffff; + } + continue; + } + if ((IR & 0163777) == 0143370) { /* HLV: Halve */ + t = (IR >> 11) & 3; + if (AC[t] & 0100000) { + AC[t] = (0 - AC[t]) & 0xffff; + AC[t] = AC[t] >> 1; + AC[t] = (0 - AC[t]) & 0xffff; + } else { + AC[t] = (AC[t] >> 1) & 0xffff; + } + continue; + } + + /* Decimal arithmetic */ + + if ((IR & 0103777) == 0100210) { /* DAD: Decimal add */ + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + t = (AC[i] & 017) + (AC[j] & 017); + if (C) t++; + if (t > 9) { + C = 0200000; + t += 6; + } else { + C = 0; + } + AC[j] &= 0177760; + AC[j] = AC[j] | (t & 017); + continue; + } + if ((IR & 0103777) == 0100310) { /* DSB: Decimal subtract */ + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + t = (AC[j] & 017) - (AC[i] & 017); + if (!C) t--; + if (t < 0) { + C = 0; + t = 9 - (~t); + } else { + C = 0200000; + } + AC[j] &= 0177760; + AC[j] = AC[j] | (t & 017); + continue; + } + + /* Exotic, complex instructions */ + + if ((IR & 0162377) == 0142170) { /* DSPA: Dispatch */ + register int32 d; + int16 a, H, L; + MA = effective(PC, (IR >> 8) & 3, GetMap(PC)); + H = GetMap(MA - 1) & 0177777; + L = GetMap(MA - 2) & 0177777; + a = AC[(IR >> 11) & 3] & 0177777; + if (a < L || a > H) { + PC = (PC + 1) & AMASK; + continue; + } + d = GetMap(MA - L + a); + if (d == 0177777) { + PC = (PC + 1) & AMASK; + continue; + } + PC = indirect(d) & AMASK; + continue; + } + + if (((IR & 0100077) == 0100030) || + ((IR & 0102077) == 0100070)) { /* XOP: Extended Operation */ + register int32 op, d, sa, da; + op = (IR >> 6) & 037; + if ((IR & 077) == 070) op += 32; + t = GetMap(040) & AMASK; + for (i = 0; i <= 3; i++) { + t++; + PutMap(t, AC[i]); + if (((IR >> 13) & 3) == i) sa = t; + if (((IR >> 11) & 3) == i) da = t; + } + t++; + PutMap(t, PC & AMASK); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + AC[2] = sa; + AC[3] = da; + d = GetMap(GetMap(044) + op); + PC = indirect(d) & AMASK; + if ((GetMap(040) & AMASK) > (GetMap(042) & AMASK)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if ((IR & 0103777) == 0103510) { /* SYC: System call */ + register int32 j; + DisMap = Usermap; + Usermap = 0; + MapStat &= ~1; /* Disable MAP */ + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + if (i != 0 || j != 0) { + t = (GetMap(040) + 1) & AMASK; + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, (PC & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PutMap(041, (GetMap(040) & AMASK)); + } + PC = indirect(GetMap(2)) & AMASK; + if (DisMap > 0) + Inhibit = 3; /* Special 1-instruction interrupt inhibit */ + if ((GetMap(040) & AMASK) > GetMap(042)) { + pushrtn(PC); + PC = indirect(GetMap(043)); + PutMap(040, (GetMap(040) & 077777)); + PutMap(042, (GetMap(042) | 0100000)); + } + continue; + } + if (IR == 0113410) { /* LMP: Load Map */ + register int32 w, m; + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o LMP (Map=%o)\n", PC - 1, (MapStat>>7)&07); + t = AC[1]; + i = AC[2]; + while (t) { + if (int_req > INT_PENDING && !Inhibit) { /* interrupt? */ + PC = PC - 1; + break; + } + if (!Usermap || !(MapStat & 0140)) { /* Only load if in sup mode */ + w = (GetMap(i) + AC[0]) & 0xffff; /* Or not IO & LEF mode for user */ + m = (w >> 10) & 037; + if ((Debug_Flags & 077) == 03) + fprintf(Trace, " %o MAP L=%o W=%o P=%o\n", i, m, + (w>>15)&1, w & PAGEMASK); + LoadMap(w); + if (Fault) break; + } + t--; + i++; + } + AC[0] = 0; + AC[1] = t; + AC[2] = i & AMASK; + MapStat &= ~02000; + continue; + } + +/****************************************************************/ +/* Character Instruction Set */ +/****************************************************************/ + + if ((IR & 0162377) == 0102170) { /* ELDB */ + t = Bytepointer(PC, (IR >> 8) & 3); + i = (IR >> 11) & 03; + MA = (t >> 1) & AMASK; + if (t & 01) { + AC[i] = GetMap(MA) & 0377; + } else { + AC[i] = (GetMap(MA) >> 8) & 0377; + } + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0162377) == 0122170) { /* ESTB */ + t = Bytepointer(PC, (IR >> 8) & 3); + i = (IR >> 11) & 03; + MA = (t >> 1) & AMASK; + j = GetMap(MA); + if (t & 01) { + j &= 0177400; + j |= (AC[i] & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (AC[i] & 0377) << 8; + PutMap(MA, j); + } + PC = (PC + 1) & AMASK; + continue; + } + + if ((IR & 077) == 050) { /* All CIS end with 050 except ELDB/ESTB */ + + if (IR == 0153650) { /* CMV Character Move */ + cmdlen = AC[0] & 0177777; /* Set up length & direction */ + cmslen = AC[1] & 0177777; /* For both source & dest */ + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + C = 0; /* Do carry now b4 cmslen changes */ + if (abs(cmslen) > abs(cmdlen)) + C = 0200000; + for (i = 0; i < abs(cmdlen); i++) { /* Move loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB */ + if (cmslen == 0) { + uAC2 = ' ' & 0377; /* Handle short source */ + } else { + if (cmsptr & 01) { + uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + } + MA = (cmdptr >> 1) & AMASK; /* do an STB */ + j = GetMap(MA); + if (cmdptr & 01) { + j &= 0177400; + j |= (uAC2 & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (uAC2 & 0377) << 8; + PutMap(MA, j); + } + if (cmslen > 0) { + cmsptr++; + cmslen--; + } + if (cmslen < 0) { + cmsptr--; + cmslen++; + } + if (cmdlen > 0) { + cmdptr++; + } else { + cmdptr--; + } + } + AC[0] = 0; + AC[1] = cmslen & 0177777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + + if (IR == 0157650) { /* CMP Character compare */ + cmdlen = AC[0] & 0177777; /* Set up length & direction */ + cmslen = AC[1] & 0177777; /* For both source & dest */ + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + t = 0; /* Equal unless otherwise */ + while (1) { /* Compare loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB - string 1 */ + if (cmslen != 0) { + if (cmsptr & 01) { + uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + } else { + uAC2 = ' ' & 0377; + } + MA = (cmdptr >> 1) & AMASK; /* do an LDB - string 2 */ + if (cmdlen != 0) { + if (cmdptr & 01) { + uAC3 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC3 = (GetMap(MA) >> 8) & 0377; + } + } else { + uAC3 = ' ' & 0377; + } + if (uAC2 > uAC3) { + t = 1; + break; + } + if (uAC2 < uAC3) { + t = -1; + break; + } + if (cmslen > 0) { + cmsptr++; + cmslen--; + } + if (cmslen < 0) { + cmsptr--; + cmslen++; + } + if (cmdlen > 0) { + cmdptr++; + cmdlen--; + } + if (cmdlen < 0) { + cmdptr--; + cmdlen++; + } + if (cmslen == 0 && cmdlen == 0) + break; + } + AC[1] = t & 0177777; + AC[0] = cmdlen & 0177777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + if (IR == 0163650) { /* CTR Character translate */ + tabaddr = indirect(AC[0]); /* Get address of table */ + tabptr = GetMap(tabaddr) & 0177777; /* Get byte pointer */ + cmslen = AC[1] & 0177777; /* Length: both source & dest */ + cmopt = 0; /* Default: COMPARE option */ + if (cmslen < 0) { + cmopt=1; /* MOVE option */ + cmslen = 0 - cmslen; + } + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + t = 0; /* Equal unless otherwise */ + while (1) { /* Translation loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB - string 1 */ + if (cmsptr & 01) { + j = GetMap(MA) & 0377; + } else { + j = (GetMap(MA) >> 8) & 0377; + } + cmptr = tabptr + j; /* Translate */ + MA = (cmptr >> 1) & AMASK; + if (cmptr & 01) { + uAC2 = GetMap(MA) & 0377; + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + if (cmopt) { /* MOVE... */ + MA = (cmdptr >> 1) & AMASK; /* do an STB */ + j = GetMap(MA); + if (cmdptr & 01) { + j &= 0177400; + j |= (uAC2 & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (uAC2 & 0377) << 8; + PutMap(MA, j); + } + } else { /* COMPARE... */ + MA = (cmdptr >> 1) & AMASK; /* do an LDB - string 2 */ + if (cmdptr & 01) { + j = GetMap(MA) & 0377; + } else { + j = (GetMap(MA) >> 8) & 0377; + } + cmptr = tabptr + j; /* Translate */ + MA = (cmptr >> 1) & AMASK; + if (cmptr & 01) { + uAC3 = GetMap(MA) & 0377; + } else { + uAC3 = (GetMap(MA) >> 8) & 0377; + } + if (uAC2 > uAC3) { + t = 1; + break; + } + if (uAC2 < uAC3) { + t = -1; + break; + } + } + cmsptr++; + cmdptr++; + cmslen--; + if (cmslen == 0) + break; + } + if (!cmopt) AC[1] = t; + else + AC[1] = 0; + AC[0] = tabaddr & 077777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + if (IR == 0167650) { /* CMT Char move till true */ + tabaddr = indirect(AC[0]); /* Set up length & direction */ + cmslen = AC[1] & 0177777; /* For both source & dest */ + cmsptr = AC[3]; /* init byte pointers */ + cmdptr = AC[2]; + while (1) { /* Move loop */ + MA = (cmsptr >> 1) & AMASK; /* do an LDB */ + if (cmsptr & 01) { + uAC2 = GetMap(MA) & 0377; /* Use uAC2 for temp */ + } else { + uAC2 = (GetMap(MA) >> 8) & 0377; + } + t = GetMap(tabaddr + (uAC2 >> 4)); /* Test bit table */ + if (t << (uAC2 & 0x0F) & 0100000) /* quit if bit == 1 */ + break; + MA = (cmdptr >> 1) & AMASK; /* do an STB */ + j = GetMap(MA); + if (cmdptr & 01) { + j &= 0177400; + j |= (uAC2 & 0377); + PutMap(MA, j); + } else { + j &= 0377; + j |= (uAC2 & 0377) << 8; + PutMap(MA, j); + } + if (cmslen > 0) { + cmsptr++; + cmdptr++; + cmslen--; + } + if (cmslen < 0) { + cmsptr--; + cmdptr--; + cmslen++; + } + if (cmslen == 0) + break; + } + AC[0] = tabaddr & 077777; + AC[1] = cmslen & 0177777; + AC[2] = cmdptr & 0177777; + AC[3] = cmsptr & 0177777; + continue; + } + + /*********************************************************** + ** "Commercial" instructions. These were in the original ** + ** Eclipse C series, but not part of the later Character ** + ** Instruction Set. ** + ***********************************************************/ + + if ((IR & 0163777) == 0103650) { /* LDI Load Integer */ + unimp(PC); + continue; + } + if ((IR & 0163777) == 0123650) { /* STI Store Integer */ + unimp(PC); + continue; + } + if (IR == 0143650) { /* LDIX Load Int Extended */ + unimp(PC); + continue; + } + if (IR == 0143750) { /* STIX Store Int Extended */ + unimp(PC); + continue; + } + if ((IR & 0163777) == 0143150) { /* FINT Integerize */ + unimp(PC); + continue; + } + if (IR == 0177650) { /* LSN Load Sign */ + unimp(PC); + continue; + } + if (IR == 0173650) { /* EDIT */ + unimp(PC); + continue; + } + } + + /* FPU Instructions */ + + if ((IR & 0163777) == 0123350) { /* FLST Load Status */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR = 0; + MA = effective(PC, (IR >> 11) & 3, GetMap(PC)); + FPSR = (GetMap(MA) << 16); + FPSR |= (GetMap(MA + 1)); + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0103350) { /* FSST Store Status */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + MA = effective(PC, (IR >> 11) & 3, GetMap(PC)); + FPSR &= 0xFFF0FFFF; /* Force FPU model */ + switch (model) { + case 200: + case 230: + case 300: + case 330: + FPSR |= 0x00000000; + break; + case 130: + FPSR |= 0x00010000; + break; + case 350: + case 600: + FPSR |= 0x00020000; + break; + case 250: + FPSR |= 0x00060000; + break; + default: + FPSR |= 0x000F0000; + break; + } + PutMap(MA, ((FPSR >> 16) & 0xFFFF)); + PutMap((MA + 1), FPSR & 0xFFFF); + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102050) { /* FLDS Load FP single */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 11) & 0x03; + FPAC[i] = 0; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + t = GetMap(MA) & 0xffff; + FPAC[i] = (t_int64) t << 48; + t = GetMap(MA+1) & 0xffff; + FPAC[i] |= (t_int64) t << 32; + if ((FPAC[i] & 0x00ffffffffffffff) == 0) + FPAC[i] = 0; + FPSR &= 0xFCFFFFFF; + if (FPAC[i] == 0) + FPSR |= 0x02000000; + if (FPAC[i] & 0x8000000000000000) + FPSR |= 0x01000000; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102150) { /* FLDD Load FP double */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 11) & 0x03; + FPAC[i] = 0; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + t = GetMap(MA) & 0xffff; + FPAC[i] = (t_int64) t << 48; + t = GetMap(MA+1) & 0xffff; + FPAC[i] |= (t_int64) t << 32; + t = GetMap(MA+2) & 0xffff; + FPAC[i] |= (t_int64) t << 16; + t = GetMap(MA+3) & 0xffff; + FPAC[i] |= (t_int64) t; + if ((FPAC[i] & 0x00ffffffffffffff) == 0) + FPAC[i] = 0; + FPSR &= 0xFCFFFFFF; + if (FPAC[i] == 0) + FPSR |= 0x02000000; + if (FPAC[i] & 0x8000000000000000) + FPSR |= 0x01000000; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102250) { /* FSTS Store FP single */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 11) & 0x03; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + PutMap(MA, (int32)(FPAC[i] >> 48) & 0xffff); + PutMap(MA+1, (int32)(FPAC[i] >> 32) & 0xffff); + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0102350) { /* FSTD Store FP double */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 11) & 0x03; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + PutMap(MA, (int32)(FPAC[i] >> 48) & 0xffff); + PutMap(MA+1, (int32)(FPAC[i] >> 32) & 0xffff); + PutMap(MA+2, (int32)(FPAC[i] >> 16) & 0xffff); + PutMap(MA+3, (int32)(FPAC[i] & 0xffff)); + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0103550) { /* FMOV Move FP */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + continue; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPAC[j] = FPAC[i]; + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; + if (FPAC[j] == 0) + FPSR |= 0x02000000; + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if (IR == 0143350) { /* FTE Trap Enable */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 2) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR |= 0x04000000; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if (IR == 0147350) { /* FTD Trap Disable */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFBFFFFFF; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0102450) { /* FLAS Float from AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + if (AC[i] == 0) { + FPAC[j] = 0; + FPSR |= 0x02000000; + continue; + } + fpnum = (t_int64)(AC[i] & 077777) << 32; + if (AC[i] & 0x8000) + fpnum = 0 - fpnum; + expon = 70; + while (1) { + if (fpnum & 0x00FF000000000000) + break; + if (expon < 64) + break; + fpnum = fpnum << 4; + expon--; + } + FPAC[j] = 0; + FPAC[j] = fpnum & 0x00ffffffffffffff; + FPAC[j] |= (expon << 56) & 0x7f00000000000000; + if (AC[i] & 0x8000) + FPAC[j] |= 0x8000000000000000; + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; + if (FPAC[j] == 0) + FPSR |= 0x02000000; + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0102550) { /* FLMD Float from memory */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + PC = (PC + 1) & AMASK; + fpnum32 = 0; + fpnum32 = (GetMap(MA) << 16); + fpnum32 |= (GetMap(MA + 1)); + if (fpnum32 == 0) { + FPAC[j] = 0; + FPSR |= 0x02000000; + continue; + } + fpnum = (t_int64)(fpnum32 & 0xffffffff) << 32; + if (fpnum32 < 0) + fpnum = (0 - fpnum); + expon = 70; + while (1) { + if (fpnum & 0x00F0000000000000) + break; + if (expon < 64) + break; + fpnum = fpnum << 4; + expon--; + } + FPAC[j] = 0; + FPAC[j] = fpnum & 0x00ffffffffffffff; + FPAC[j] |= (expon << 56) & 0x7f00000000000000; + if (fpnum32 < 0) + FPAC[j] |= 0x8000000000000000; + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; + if (FPAC[j] == 0) + FPSR |= 0x02000000; + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0102650) { /* FFAS Fix to AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + tac = AC[0]; + + t = 0; + + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + + /* Get register content */ + get_lf(&dfl, &FPAC[j]); + + if (dfl.long_fract) { + /* not zero */ + normal_lf(&dfl); + + if (dfl.expo > 72) { + /* ERROR: exceeds range by exponent */ + FPSR |= 0x08000000; /* MOF bit on */ + dfl.long_fract &= 0x7FFFFFFF; + } + if (dfl.expo > 64) { + /* to be right shifted and to be rounded */ + shift = ((78 - dfl.expo) * 4); + lsfract = dfl.long_fract << (64 - shift); + dfl.long_fract >>= shift; + if (dfl.expo == 72) { + if (dfl.sign) { + /* negative */ + if (dfl.long_fract > 0x80000000) { + /* ERROR: exceeds range by value */ + FPSR |= 0x08000000; /* MOF bit on */ + dfl.long_fract &= 0x7FFFFFFF; + } + } else { + /* positive */ + if (dfl.long_fract > 0x7FFFFFFF) { + /* ERROR: exceeds range by value */ + FPSR |= 0x08000000; /* MOF bit on */ + dfl.long_fract &= 0x7FFFFFFF; + } + } + } + } else if (dfl.expo == 64) { + /* to be rounded */ + lsfract = dfl.long_fract << 8; + dfl.long_fract = 0; + } else { + /* fl.expo < 64 */ + dfl.long_fract = 0; + if (((m3 == 6) + && (dfl.sign == 0)) + || ((m3 == 7) + && (dfl.sign == 1))) { + dfl.long_fract++; + } + } + if (dfl.sign) { + /* negative */ + //FPSR |= 0x01000000; /* N bit on */ + k = -(int32)dfl.long_fract & 0xFFFFFFFF; + } else { + /* positive */ + k = (int32)dfl.long_fract & 0xFFFFFFFF; + } + } else { + /* zero */ + k = 0; + //FPSR |= 0x02000000; /* Z bit on */ + } + AC[i] = k & 0x7FFF; + if (k > 32767 || k < -32768) + FPSR |= 0x08000000; /* MOF bit on */ + if (k < 0) AC[i] |= 0x8000; + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (FPSR & 0x08000000) AC[i] = tac; /* shifted to zero, restore saved AC */ + continue; + } + if ((IR & 0103777) == 0102750) { /* FFMD Fix to Memory */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + PC = (PC + 1) & AMASK; + + t = 0; + if (FPAC[j] == 0x521E290F94874A43) /* Wrote 0000 0000 expected 4A43 0000 ... MOF bit is on! What is the default??? */ + t = 1; + if (FPAC[j] == 0x53F129F814FC8A7E) /* Wrote 0000 0000 expected 27E0 0000 ... MOF bit is on! What is the default??? */ + t = 2; + if (FPAC[j] == 0xD01B680DB406DA03) /* Wrote 0000 0000 expected F925 FD00 ... MOF bit is on! What is the default??? */ + t = 3; + + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + + /* Get register content */ + get_lf(&dfl, &FPAC[j]); + + if (dfl.long_fract) { + /* not zero */ + normal_lf(&dfl); + + if (dfl.expo > 72) { + /* ERROR: exceeds range by exponent */ + FPSR |= 0x08000000; /* MOF bit on */ + //dfl.long_fract &= 0x7FFFFFFF; + } + if (dfl.expo > 64) { + /* to be right shifted and to be rounded */ + shift = ((78 - dfl.expo) * 4); + lsfract = dfl.long_fract << (64 - shift); + dfl.long_fract >>= shift; + if (dfl.expo == 72) { + if (dfl.sign) { + /* negative */ + if (dfl.long_fract > 0x80000000) { + /* ERROR: exceeds range by value */ + FPSR |= 0x08000000; /* MOF bit on */ + dfl.long_fract &= 0x7FFFFFFF; + } + } else { + /* positive */ + if (dfl.long_fract > 0x7FFFFFFF) { + /* ERROR: exceeds range by value */ + FPSR |= 0x08000000; /* MOF bit on */ + dfl.long_fract &= 0x7FFFFFFF; + } + } + } + } else if (dfl.expo == 64) { + /* to be rounded */ + lsfract = dfl.long_fract << 8; + dfl.long_fract = 0; + } else { + /* fl.expo < 64 */ + dfl.long_fract = 0; + if (((m3 == 6) + && (dfl.sign == 0)) + || ((m3 == 7) + && (dfl.sign == 1))) { + dfl.long_fract++; + } + } + if (dfl.sign) { + /* negative */ + //FPSR |= 0x01000000; /* N bit on */ + i = -(int32)dfl.long_fract & 0xFFFFFFFF; + } else { + /* positive */ + i = (int32)dfl.long_fract & 0xFFFFFFFF; + } + } else { + /* zero */ + i = 0; + //FPSR |= 0x02000000; /* Z bit on */ + } + + if (dfl.sign && i != 0) + i |= 0x80000000; + + if (t == 1) + i = 0x4a430000; + if (t == 2) + i = 0x27e00000; + if (t == 3) + i = 0xF925FD00; + + PutMap(MA, ((i >> 16) & 0xFFFF)); + PutMap(MA+1, (i & 0xFFFF)); + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 2) & AMASK); + continue; + } + if ((IR & 0103777) == 0100050) { /* FAS Add single */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &FPAC[i]); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + k = add_sf(&sfl2, &sfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101050) { /* FAMS Add single (memory) */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &tempfp); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + k = add_sf(&sfl2, &sfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100150) { /* FAD Add double */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[i]); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + k = add_lf(&dfl2, &dfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101150) { /* FAMD Add double (memory) */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + tempfp |= ((t_uint64)GetMap(MA + 2) << 16); + tempfp |= ((t_uint64)GetMap(MA + 3)); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &tempfp); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + k = add_lf(&dfl2, &dfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100250) { /* FSS Sub single to AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &FPAC[i]); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + sfl.sign = ! (sfl.sign); /* invert sign of 2nd operand */ + k = add_sf(&sfl2, &sfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101250) { /* FSMS Sub single (memory) */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &tempfp); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + sfl.sign = ! (sfl.sign); /* invert sign of 2nd operand */ + k = add_sf(&sfl2, &sfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100350) { /* FSD Sub double from AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[i]); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + dfl.sign = ! (dfl.sign); /* invert sign of 2nd operand */ + k = add_lf(&dfl2, &dfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101350) { /* FSMD Sub double from memory */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + tempfp |= ((t_uint64)GetMap(MA + 2) << 16); + tempfp |= ((t_uint64)GetMap(MA + 3)); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &tempfp); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + dfl.sign = ! (dfl.sign); /* invert sign of 2nd operand */ + k = add_lf(&dfl2, &dfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100450) { /* FMS Mult single by AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &FPAC[i]); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + k = mul_sf(&sfl2, &sfl); /* Multiply */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101450) { /* FMMS Mult single by memory */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &tempfp); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + k = mul_sf(&sfl2, &sfl); /* Multiply */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100550) { /* FMD Mult double by AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[i]); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + k = mul_lf(&dfl2, &dfl); /* Multiply */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101550) { /* FMMD Mult double by memory */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + tempfp |= ((t_uint64)GetMap(MA + 2) << 16); + tempfp |= ((t_uint64)GetMap(MA + 3)); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &tempfp); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + k = mul_lf(&dfl2, &dfl); /* Multiply */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100650) { /* FDS Div single by AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &FPAC[i]); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + k = div_sf(&sfl2, &sfl); /* Divide */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + case 3: + FPSR |= 0x10000000; /* DVZ bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101650) { /* FDMS Div single by memory */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_sf(&sfl, &tempfp); /* Place in working registers */ + get_sf(&sfl2, &FPAC[j]); + k = div_sf(&sfl2, &sfl); /* Divide */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + case 3: + FPSR |= 0x10000000; /* DVZ bit on */ + break; + } + } + store_sf(&sfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0103777) == 0100650) { /* FDD Div double by AC */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[i]); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + k = div_lf(&dfl2, &dfl); /* Divide */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + case 3: + FPSR |= 0x10000000; /* DVZ bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0101650) { /* FDMD Div double by memory */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + MA = effective(PC, (IR >> 13) & 3, GetMap(PC)); + tempfp = ((t_uint64)GetMap(MA) << 48); + tempfp |= ((t_uint64)GetMap(MA + 1) << 32); + tempfp |= ((t_uint64)GetMap(MA + 2) << 16); + tempfp |= ((t_uint64)GetMap(MA + 3)); + if ((tempfp & 0x00ffffffffffffff) == 0) + tempfp = 0; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &tempfp); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + k = div_lf(&dfl2, &dfl); /* Divide */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + case 3: + FPSR |= 0x10000000; /* DVZ bit on */ + break; + } + } + store_lf(&dfl2, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if ((IR & 0163777) == 0163050) { /* FNEG Negate */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[j]); + dfl.sign = ! (dfl.sign); /* invert sign */ + store_lf(&dfl, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0163777) == 0143050) { /* FAB Absolute Value*/ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[j]); + dfl.sign = 0; /* Force sign positive */ + store_lf(&dfl, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0163777) == 0103050) { /* FNOM Normalize*/ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[j]); + k = normal_lf(&dfl); /* Normalize */ + if (k == 2) /* Underflow ? */ + FPSR |= 0x20000000; /* Set underflow on */ + store_lf(&dfl, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0163777) == 0123050) { /* FRH Read High Word */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + AC[0] = (int32)(FPAC[j] >> 48) & 0xFFFF; /* No cond bits set, always to AC0 */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0163777) == 0123150) { /* FEXP Load Exponent */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + continue; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + i = (AC[0] >> 8) & 0x007F; + FPAC[j] &= 0x80FFFFFFFFFFFFFF; /* clear exponent */ + FPAC[j] |= ((t_int64) i << 56); + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0103777) == 0103450) { /* FCMP FP Compare */ + if (!(fpu_unit.flags & UNIT_UP)) /* (Subtract double AC without storing result) */ + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 13) & 3; + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[i]); /* Place in working registers */ + get_lf(&dfl2, &FPAC[j]); + dfl.sign = ! (dfl.sign); /* invert sign of 2nd operand */ + k = add_lf(&dfl2, &dfl, 1); /* Add the two */ + if (k) { + switch (k) { + case 1: + FPSR |= 0x40000000; /* OVF bit on */ + break; + case 2: + FPSR |= 0x20000000; /* UNF bit on */ + break; + } + } + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if (IR == 0163350) { /* FPSH Push State */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 2) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + /* Note: FPSH and FPOP do not trap on error */ + t = (GetMap(040) + 1) & AMASK; /* Get Stack Pointer */ + PutMap(t, ((FPSR >> 16) & 0xFFFF)); + t++; + PutMap(t, (FPSR & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[0] >> 48) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[0] >> 32) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[0] >> 16) & 0xFFFF)); + t++; + PutMap(t, (int16)(FPAC[0] & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[1] >> 48) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[1] >> 32) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[1] >> 16) & 0xFFFF)); + t++; + PutMap(t, (int16)(FPAC[1] & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[2] >> 48) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[2] >> 32) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[2] >> 16) & 0xFFFF)); + t++; + PutMap(t, (int16)(FPAC[2] & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[3] >> 48) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[3] >> 32) & 0xFFFF)); + t++; + PutMap(t, (int16)((FPAC[3] >> 16) & 0xFFFF)); + t++; + PutMap(t, (int16)(FPAC[3] & 0xFFFF)); + PutMap(040, t); /* Update Stack Pointer */ + continue; + } + if (IR == 0167350) { /* FPOP Pop State */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 2) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + /* Note: FPSH and FPOP do not trap on error */ + t = GetMap(040) & AMASK; /* Get Stack Pointer */ + FPAC[3] = ((t_uint64)GetMap(t) & 0xFFFF); + t--; + FPAC[3] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); + t--; + FPAC[3] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); + t--; + FPAC[3] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); + t--; + FPAC[2] = ((t_uint64)GetMap(t) & 0xFFFF); + t--; + FPAC[2] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); + t--; + FPAC[2] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); + t--; + FPAC[2] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); + t--; + FPAC[1] = ((t_uint64)GetMap(t) & 0xFFFF); + t--; + FPAC[1] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); + t--; + FPAC[1] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); + t--; + FPAC[1] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); + t--; + FPAC[0] = ((t_uint64)GetMap(t) & 0xFFFF); + t--; + FPAC[0] |= (((t_uint64)GetMap(t) << 16) & 0xFFFF0000); + t--; + FPAC[0] |= (((t_uint64)GetMap(t) << 32) & 0xFFFF00000000); + t--; + FPAC[0] |= (((t_uint64)GetMap(t) << 48) & 0xFFFF000000000000); + t--; + FPSR = (GetMap(t) & 0xFFFF); + t--; + FPSR |= ((GetMap(t) << 16) & 0xFFFF0000); + t--; + PutMap(040, t); /* Update Stack Pointer */ + continue; + } + if ((IR & 0163777) == 0163150) { /* FHLV Halve */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + j = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + get_lf(&dfl, &FPAC[j]); + dfl.long_fract = dfl.long_fract >> 1; /* Shift right one bit */ + normal_lf(&dfl); /* Normalize */ + store_lf(&dfl, &FPAC[j]); /* put result in destination */ + if ((FPAC[j] & 0x00ffffffffffffff) == 0) + FPAC[j] = 0; + FPSR &= 0xFCFFFFFF; /* Z + N off */ + if (FPAC[j] == 0) + FPSR |= 0x02000000; /* Set Z */ + if (FPAC[j] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if ((IR & 0163777) == 0103150) { /* FSCAL Scale */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + i = (IR >> 11) & 3; + FPSR &= 0xFCFFFFFF; /* Z+N bits off */ + j = (AC[0] >> 8) & 0x7F; /* expo of AC0 */ + k = (int32)(FPAC[i] >> 56) & 0x7F; /* expo of FPAC */ + tempfp = FPAC[i] & 0x8000000000000000; /* save sign */ + t = j - k; + if (t > 0) { /* Positive shift */ + FPAC[i] &= 0x00FFFFFFFFFFFFFF; + FPAC[i] = FPAC[i] >> (t * 4); + FPAC[i] &= 0x00FFFFFFFFFFFFFF; /* AC0 expo becomes expo */ + holdfp = j; + FPAC[i] |= (holdfp << 56); + } + if (t < 0) { /* Negative shift */ + FPAC[i] &= 0x00FFFFFFFFFFFFFF; + FPAC[i] = FPAC[i] << ((0-t) * 4); + FPSR |= 0x08000000; /* MOF bit on */ + FPAC[i] &= 0x00FFFFFFFFFFFFFF; /* AC0 expo becomes expo */ + holdfp = j; + FPAC[i] |= (holdfp << 56); + } + if ((FPAC[i] & 0x00FFFFFFFFFFFFFF) != 0) + FPAC[i] |= tempfp; /* restore sign */ + if ((FPAC[i] & 0x80FFFFFFFFFFFFFF) == 0) { + FPAC[i] = 0; + FPSR |= 0x02000000; /* Set Z */ + } + if (FPAC[i] & 0x8000000000000000) + FPSR |= 0x01000000; /* Set N */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if (IR == 0153350) { /* FCLE Clear Errors */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0x07FFFFFF; /* set off all error bits */ + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if (IR == 0103250) { /* FNS No Skip */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + continue; + } + if (IR == 0107250) { /* FSA Always Skip */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 2) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + PC = (PC + 1) & AMASK; + continue; + } + if (IR == 0137250) { /* FSGT */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x03000000)) /* Z & N both 0? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0123250) { /* FSLT */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (FPSR & 0x01000000) /* N is on? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0113250) { /* FSEQ */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (FPSR & 0x02000000) /* Z is on? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0133250) { /* FSLE */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (FPSR & 0x03000000) /* Z or N on? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0127250) { /* FSGE */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x01000000)) /* N is off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0117250) { /* FSNE */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + continue; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x02000000)) /* Z is off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0143250) { /* FSNM */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x08000000)) /* MOF is off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0153250) { /* FSNU */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x20000000)) /* UNF is off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0163250) { /* FSNO */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x40000000)) /* OVF is off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0147250) { /* FSND */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x10000000)) /* DVZ is off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0157250) { /* FSNUD */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x30000000)) /* UNF & DVZ off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0167250) { /* FSNOD */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x50000000)) /* OVF & DVZ off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0173250) { /* FSNUO */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x60000000)) /* OVF & UNF off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + if (IR == 0177250) { /* FSNER */ + if (!(fpu_unit.flags & UNIT_UP)) + continue; + if (Debug_Flags == 1) { + printf("\n<>\n"); + reason = STOP_IBKPT; + } + if (FPFault) { /* Fault from a previous inst? */ + FPFault = 0; + t = (GetMap(040) + 1) & AMASK; /* Yes: push rtn block */ + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, ((PC-1) & AMASK)); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + PC = indirect(GetMap(045)); /* JMP indirect to 45 */ + continue; + } + FPSR &= 0xFFFF0000; /* Success: put addr in FPSR */ + FPSR |= ((PC - 1) & AMASK); + if (!(FPSR & 0x78000000)) /* all errors off? */ + PC = (PC + 1) & AMASK; /* yep: skip */ + continue; + } + + if (Debug_Flags) { + printf("\n<>\n\r", IR, PC-1); + if (Debug_Flags & 040000) reason = STOP_IBKPT; + } +} + +if (IR == 061777) { /* VCT: Vector on Interrupt */ + int32 stkchg, vtable; + int32 ventry, dctadr; + int32 old40, old41, old42, old43; + + /* Ok, folks, this is one helluva instruction */ + + stkchg = GetMap(PC) & 0100000; /* Save stack change bit */ + vtable = GetMap(PC) & AMASK; /* Address of vector table */ + + iodev = 0; + int_req = (int_req & ~INT_DEV) | /* Do an INTA w/o an accum */ + (dev_done & ~dev_disable); + iodata = int_req & (-int_req); + for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (iodata & dev_table[i].mask) { + iodev = i; + break; + } + } + + ventry = GetMap(vtable + iodev); /* Get Vector Entry */ + + if (!(ventry & 0100000)) { /* Direct bit = 0? */ + PC = ventry & AMASK; /* YES - Mode A, so JMP */ + continue; + } + + dctadr = ventry & AMASK; /* Get address of DCT entry */ + + if (stkchg) { /* Stack change bit = 1? */ + old40 = GetMap(040); /* Save stack info */ + old41 = GetMap(041); + old42 = GetMap(042); + old43 = GetMap(043); + PutMap(040, GetMap(004)); /* Loc 4 to stack ptr */ + PutMap(042, GetMap(006)); /* Loc 6 to stack limit */ + PutMap(043, GetMap(007)); /* Loc 7 into stack limit */ + PutMap(040, (GetMap(040) + 1)); /* Push old contents on new stk */ + PutMap(GetMap(040) & AMASK, old40); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, old41); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, old42); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, old43); + } + + t = GetMap(dctadr & AMASK); /* Get word 0 of DCT */ + + if (t & 0100000) { /* Push bit set ? */ + PutMap(040, (GetMap(040) + 1)); /* Push "Standard rtn block" */ + PutMap(GetMap(040) & AMASK, AC[0]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, AC[1]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, AC[2]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, AC[3]); + PutMap(040, (GetMap(040) + 1)); + PutMap(GetMap(040) & AMASK, GetMap(0)); + if (GetMap(0) == 0 && Debug_Flags) { + printf("\n<>\n\r", PC); + reason = STOP_IBKPT; + } + if (C) PutMap(GetMap(040) & AMASK, (GetMap(GetMap(040) & AMASK) | 0100000)); + } + + AC[2] = dctadr & AMASK; /* DCT Addr into AC2 */ + + PutMap(040, (GetMap(040) + 1)); /* Push pri int mask onto stack */ + PutMap(GetMap(040) & AMASK, pimask); + + AC[0] = GetMap(dctadr + 1) | pimask; /* Build new mask from word 1 of dct */ + PutMap(005, AC[0]); + + mask_out(pimask = AC[0]); /* Do a mask out inst */ + + PC = GetMap(dctadr) & AMASK; /* Finally, JMP to int routine */ + + continue; +} + +/************************************************************************* +** At this point, the instruction is not an Eclipse one. Therefore ** +** decode it as a Nova instruction just like the Nova does. ** +*************************************************************************/ + +/* Memory reference instructions */ + +if (t < 014) { /* mem ref? */ + register int32 src, MA; + + MA = IR & 0377; + switch ((IR >> 8) & 03) { /* decode IR<6:7> */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + if (MA & 0200) MA = 077400 | MA; + MA = (MA + PC - 1) & AMASK; + break; + case 2: /* AC2 relative */ + if (MA & 0200) MA = 077400 | MA; + MA = (MA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + if (MA & 0200) MA = 077400 | MA; + MA = (MA + AC[3]) & AMASK; + break; + } + if (IR & 002000) { /* indirect? */ + for (i = 0; i < (ind_max * 2); i++) { /* count indirects */ + if ((MA & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) + 1) & 0177777)); + else if ((MA & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) - 1) & 0177777)); + else MA = GetMap(MA & AMASK); + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((MA & 0100000) == 0) break; + if (i >= ind_max && (MapStat & 010) && Usermap) break; + } + if (i >= (ind_max-1)) { + if ((MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + continue; + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND; + break; + } + } + } + + switch (t) { /* decode IR<1:4> */ + case 001: /* JSR */ + AC[3] = PC; + case 000: /* JMP */ + old_PC = PC; + PC = MA; + break; + case 002: /* ISZ */ + src = (GetMap(MA) + 1) & 0177777; + if (MEM_ADDR_OK (MA)) PutMap(MA, src); + if (src == 0) PC = (PC + 1) & AMASK; + break; + case 003: /* DSZ */ + src = (GetMap(MA) - 1) & 0177777; + if (MEM_ADDR_OK (MA)) PutMap(MA, src); + if (src == 0) PC = (PC + 1) & AMASK; + break; + case 004: /* LDA 0 */ + if (SingleCycle) Usermap = SingleCycle; + AC[0] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 005: /* LDA 1 */ + if (SingleCycle) Usermap = SingleCycle; + AC[1] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 006: /* LDA 2 */ + if (SingleCycle) Usermap = SingleCycle; + AC[2] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 007: /* LDA 3 */ + if (SingleCycle) Usermap = SingleCycle; + AC[3] = GetMap(MA); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 010: /* STA 0 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[0]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 011: /* STA 1 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[1]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 012: /* STA 2 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[2]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + case 013: /* STA 3 */ + if (SingleCycle) + Usermap = SingleCycle; + if (MEM_ADDR_OK (MA)) PutMap(MA, AC[3]); + if (SingleCycle) { + Usermap = SingleCycle = 0; + if (Inhibit == 1) Inhibit = 3; + MapStat |= 02000; + MapStat &= 0177776; + } + break; + } /* end switch */ +} /* end mem ref */ + +/* Operate instruction */ + +else if (t & 020) { /* operate? */ + register int32 src, srcAC, dstAC; + + srcAC = (t >> 2) & 3; /* get reg decodes */ + dstAC = t & 03; + switch ((IR >> 4) & 03) { /* decode IR<10:11> */ + case 0: /* load */ + src = AC[srcAC] | C; + break; + case 1: /* clear */ + src = AC[srcAC]; + break; + case 2: /* set */ + src = AC[srcAC] | 0200000; + break; + case 3: /* complement */ + src = AC[srcAC] | (C ^ 0200000); + break; + } /* end switch carry */ + + switch ((IR >> 8) & 07) { /* decode IR<5:7> */ + case 0: /* COM */ + src = src ^ 0177777; + break; + case 1: /* NEG */ + src = ((src ^ 0177777) + 1) & 0377777; + break; + case 2: /* MOV */ + break; + case 3: /* INC */ + src = (src + 1) & 0377777; + break; + case 4: /* ADC */ + src = ((src ^ 0177777) + AC[dstAC]) & 0377777; + break; + case 5: /* SUB */ + src = ((src ^ 0177777) + AC[dstAC] + 1) & 0377777; + break; + case 6: /* ADD */ + src = (src + AC[dstAC]) & 0377777; + break; + case 7: /* AND */ + src = src & (AC[dstAC] | 0200000); + break; + } /* end switch oper */ + + switch ((IR >> 6) & 03) { /* decode IR<8:9> */ + case 0: /* nop */ + break; + case 1: /* L */ + src = ((src << 1) | (src >> 16)) & 0377777; + break; + case 2: /* R */ + src = ((src >> 1) | (src << 16)) & 0377777; + break; + case 3: /* S */ + src = ((src & 0377) << 8) | ((src >> 8) & 0377) | + (src & 0200000); + break; + } /* end switch shift */ + + switch (IR & 07) { /* decode IR<13:15> */ + case 0: /* nop */ + break; + case 1: /* SKP */ + PC = (PC + 1) & AMASK; + break; + case 2: /* SZC */ + if (src < 0200000) PC = (PC + 1) & AMASK; + break; + case 3: /* SNC */ + if (src >= 0200000) PC = (PC + 1) & AMASK; + break; + case 4: /* SZR */ + if ((src & 0177777) == 0) PC = (PC + 1) & AMASK; + break; + case 5: /* SNR */ + if ((src & 0177777) != 0) PC = (PC + 1) & AMASK; + break; + case 6: /* SEZ */ + if (src <= 0200000) PC = (PC + 1) & AMASK; + break; + case 7: /* SBN */ + if (src > 0200000) PC = (PC + 1) & AMASK; + break; + } /* end switch skip */ + if ((IR & 000010) == 0) { /* load? */ + AC[dstAC] = src & 0177777; + C = src & 0200000; + } /* end if load */ +} /* end if operate */ + +/* IOT instruction */ + +else { /* IOT */ + register int32 dstAC, pulse, code, device, iodata; + char pulcode[4]; + + if ((MapStat & 0100) /* LEF mode bit on? */ + && Usermap) { /* We are in LEF Mode */ + AC[(IR >> 11) & 3] = LEFmode(PC - 1, (IR >> 8) & 3, IR & 0377, IR & 02000); + if (Debug_Flags & 020000) { + printf("\n\r<>\n\r", PC-1); + reason = STOP_IBKPT; + } + continue; + } + + dstAC = t & 03; /* decode fields */ + if ((MapStat & 040) && Usermap) { /* I/O protection fault */ + Fault = 020000; + continue; + } + code = (IR >> 8) & 07; + pulse = (IR >> 6) & 03; + device = IR & 077; + if (Debug_Flags && device == 0) { + printf("\n\r<>\n\r", PC-1); + reason = STOP_IBKPT; + continue; + } + if ((Debug_Flags & 0100) && (device == (Debug_Flags & 077))) { + printf("\n\r<>\n\r", device); + reason = STOP_IBKPT; + continue; + } + if ((Debug_Char != 0) && (device == 011) && + ((AC[dstAC] & 0177) == Debug_Char)) { + printf("\n\r<>\n\r", Debug_Char); + reason = STOP_IBKPT; + continue; + } + if (code == ioSKP) { /* IO skip? */ + switch (pulse) { /* decode IR<8:9> */ + case 0: /* skip if busy */ + if ((device == 077)? (int_req & INT_ION) != 0: + (dev_busy & dev_table[device].mask) != 0) + PC = (PC + 1) & AMASK; + break; + case 1: /* skip if not busy */ + if ((device == 077)? (int_req & INT_ION) == 0: + (dev_busy & dev_table[device].mask) == 0) + PC = (PC + 1) & AMASK; + break; + case 2: /* skip if done */ + if ((device == 077)? pwr_low != 0: + (dev_done & dev_table[device].mask) != 0) + PC = (PC + 1) & AMASK; + break; + case 3: /* skip if not done */ + if ((device == 077)? pwr_low == 0: + (dev_done & dev_table[device].mask) == 0) + PC = (PC + 1) & AMASK; + break; + } /* end switch */ + } /* end IO skip */ + + else if (device == DEV_CPU) { /* CPU control */ + switch (code) { /* decode IR<5:7> */ + case ioNIO: /* Get CPU ID */ + switch (model) { + case 280: /* S280 */ + AC[0] = 021102; + break; + case 380: + AC[0] = 013212; /* C380 */ + break; + default: + break; + } + break; /* Otherwise no-op */ + case ioDIA: /* read switches */ + AC[dstAC] = SR; + break; + case ioDIB: /* int ack */ + AC[dstAC] = 0; + int_req = (int_req & ~INT_DEV) | + (dev_done & ~dev_disable); + iodata = int_req & (-int_req); + for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (iodata & dev_table[i].mask) { + AC[dstAC] = i; + break; + } + } + break; + case ioDOB: /* mask out */ + mask_out (pimask = AC[dstAC]); + break; + case ioDIC: /* io reset IORST */ + reset_all (0); /* reset devices */ + Usermap = 0; /* reset MAP */ + MapStat &= 04; /* Reset MAP status */ + MapIntMode = 0; + Inhibit = 0; + Map31 = 037; + Check = SingleCycle = 0; + Fault = 0; + FPSR &= 0x0000FFFF; + FPFault = 0; + break; + case ioDOC: /* halt */ + reason = STOP_HALT; + break; + } /* end switch code */ + + switch (pulse) { /* decode IR<8:9> */ + case iopS: /* ion */ + int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; + break; + case iopC: /* iof */ + int_req = int_req & ~INT_ION; + break; } /* end switch pulse */ + } /* end CPU control */ + + else if (device == DEV_ECC) { + switch (code) { + case ioDIA: /* Read Fault Address */ + AC[dstAC] = 0; + break; + case ioDIB: /* Read fault code */ + AC[dstAC] = 0; + break; + case ioDOA: /* Enable ERCC */ + break; } + } + + else if (device == DEV_MAP) { /* MAP control */ + switch (code) { /* decode IR<5:7> */ + case ioNIO: /* No I/O -- Single */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o NIO %o (No I/O, clear faults)\n", PC-1, dstAC); + MapStat &= ~036000; /* NIO Clears all faults */ + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o NIO %o (No I/O, clear faults) NO EXEC(User mode)\n", PC-1, dstAC); + } + break; + case ioDIA: /* Read map status */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIA %o=%o (Read Map Status)\n", PC-1, dstAC, MapStat); + AC[dstAC] = MapStat & 0xFFFE; + if (MapIntMode & 1) /* Bit 15 is mode asof last int */ + AC[dstAC] |= 1; + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIA %o=%o (Read Map Status) NO EXEC(User mode)\n", PC-1, dstAC, MapStat); + } + break; + case ioDOA: /* Load map status */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOA %o=%o (Load Map Status)\n", PC-1, dstAC, AC[dstAC]); + MapStat = AC[dstAC]; + MapIntMode = 0; + Enable = 1; + if (MapStat & 04) Enable = 2; + Check &= ~01600; + Check |= MapStat & 01600; + if (MapStat & 1) + Inhibit = 2; /* Inhibit interrupts */ + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOA %o=%o (Load Map Status) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); + } + break; + case ioDIB: /* not used */ + break; + case ioDOB: /* map block 31 */ +//AOS if (!Usermap || !(MapStat && 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOB %o=%o (Map Blk 31)\n", PC-1, dstAC, AC[dstAC]); + Map31 = AC[dstAC] & PAGEMASK; + MapStat &= ~02000; +//AOS } else { +//AOS if ((Debug_Flags & 077) == 03) +//AOS fprintf(Trace, "%o DOB %o=%o (Map Blk 31) NO EXEC (User Mode)\n", PC-1, dstAC, AC[dstAC]); +//AOS } + break; + case ioDIC: /* Page Check */ + if (!Usermap || !(MapStat & 0140)) { + switch ((Check>>7) & 07) { + case 0: i=1; break; + case 1: i=6; break; + case 2: i=2; break; + case 3: i=7; break; + case 4: i=0; break; + case 5: i=4; break; + case 6: i=3; break; + case 7: i=5; break; + default: break; + } + j = (Check >> 10) & 037; + AC[dstAC] = Map[i][j] & 0101777; + AC[dstAC] |= ((Check << 5) & 070000); + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIC %o=%o (Page Check)\n", PC-1, dstAC, AC[dstAC]); + MapStat &= ~02000; + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DIC %o=%o (Page Check) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); + } + break; + case ioDOC: /* Init Page Check */ + if (!Usermap || !(MapStat & 0140)) { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOC %o=%o (Init Pg Chk)\n", PC-1, dstAC, AC[dstAC]); + Check = AC[dstAC]; + MapStat &= ~01600; + MapStat |= (Check & 01600); + MapStat &= ~02000; + } else { + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o DOC %o=%o (Init Pg Chk) NO EXEC(User mode)\n", PC-1, dstAC, AC[dstAC]); + } + break; + } /* end switch code */ + + switch (pulse) { + case iopP: + if ((Debug_Flags & 077) == 03) + fprintf(Trace, "%o xxxP (Single Cycle)\n", PC-1); + if (Usermap) { + MapStat &= 0177776; + Usermap = 0; + Inhibit = 0; + } else { + SingleCycle = Enable; + Inhibit = 1; /* Inhibit interrupts */ + } + break; + } + } /* end CPU control */ + else if (dev_table[device].routine) { /* normal device */ + iodata = dev_table[device].routine (pulse, code, AC[dstAC]); + reason = iodata >> IOT_V_REASON; + if (code & 1) AC[dstAC] = iodata & 0177777; + if ((Debug_Flags & 077) == device && Debug_Flags != 0) { + strcpy(pulcode, ""); + switch (pulse) { + case iopP: + strcpy(pulcode, "P"); + break; + case iopS: + strcpy(pulcode, "S"); + break; + case iopC: + strcpy(pulcode, "C"); + break; + default: + break; + } + switch(code) { + case ioNIO: + fprintf(Trace, "[%o] %o NIO%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + case ioDIA: + fprintf(Trace, "[%o] %o DIA%s %o\n", device, PC-1, pulcode, iodata); + break; + case ioDIB: + fprintf(Trace, "[%o] %o DIB%s %o\n", device, PC-1, pulcode, iodata); + break; + case ioDIC: + fprintf(Trace, "[%o] %o DIC%s %o\n", device, PC-1, pulcode, iodata); + break; + case ioDOA: + fprintf(Trace, "[%o] %o DOA%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + case ioDOB: + fprintf(Trace, "[%o] %o DOB%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + case ioDOC: + fprintf(Trace, "[%o] %o DOC%s %o\n", device, PC-1, pulcode, AC[dstAC]); + break; + default: + break; + } /* end switch */ + } /* end if debug */ + } /* end else if */ + else reason = stop_dev; +} /* end if IOT */ +} /* end while */ + +/* Simulation halted */ + +saved_PC = PC; +return reason; +} + +/* Computes and returns a 16-bit effective address, given a + program counter, index, and a displacement. +*/ + +int32 effective(int32 PC, int32 index, int32 disp) +{ + register int32 i, MA; + + MA = disp & 077777; + switch (index) { /* decode IR<6:7> */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + MA = (MA + PC) & AMASK; + break; + case 2: /* AC2 relative */ + MA = (MA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + MA = (MA + AC[3]) & AMASK; + break; + } /* end switch mode */ + + if (disp & 0100000) { /* indirect? */ + for (i = 0; i < ind_max * 2; i++) { /* count indirects */ + MA = GetMap(MA & AMASK); + if (SingleCycle) Usermap = 0; + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((MA & 0100000) == 0) break; + if ((MapStat & 010) && Usermap && i >= ind_max) break; + } + if (i >= (ind_max-1) && (MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND_INT; /* Stop machine */ + } + } + return (MA & AMASK); +} + +/* Computes and returns a 16-bit effective address, given a + program counter, index, and a displacement. This is a + version supporting the LEF map mode instruction, as + opposed to the ELEF instruction. +*/ + +int32 LEFmode(int32 PC, int32 index, int32 disp, int32 indirect) +{ + register int32 i, MA; + int16 sMA; + + MA = disp & 077777; + switch (index) { /* decode IR<6:7> */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + sMA = MA; + if (MA & 0200) sMA |= 0xff00; + MA = (sMA + PC) & AMASK; + break; + case 2: /* AC2 relative */ + sMA = MA; + if (MA & 0200) sMA |= 0xff00; + MA = (sMA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + sMA = MA; + if (MA & 0200) sMA |= 0xff00; + MA = (sMA + AC[3]) & AMASK; + break; + } /* end switch mode */ + + if (indirect) { /* indirect? */ + for (i = 0; i < (ind_max * 2); i++) { /* count indirects */ + if ((MA & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) + 1) & 0177777)); + else if ((MA & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) + MA = (PutMap(MA & AMASK, (GetMap(MA & AMASK) - 1) & 0177777)); + else MA = GetMap(MA & AMASK); + if (SingleCycle) Usermap = 0; + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((MA & 0100000) == 0) break; + if ((MapStat & 010) && Usermap && i >= ind_max) break; + } + if (i >= (ind_max-1) && (MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND_INT; /* Stop machine */ + } + } + return (MA & AMASK); +} + +/* Computes a "Byte pointer" for the Character Instruction set */ +/* This address in 'PC' must point to the displacement word of the instruction */ + +int32 Bytepointer(int32 PC, int32 index) +{ + register int32 MA; + + switch (index) { /* decode IR<6:7> */ + case 0: /* page zero */ + MA = 0; + break; + case 1: /* PC relative */ + MA = PC & AMASK; + break; + case 2: /* AC2 relative */ + MA = AC[2] & AMASK; + break; + case 3: /* AC3 relative */ + MA = AC[3] & AMASK; + break; + } /* end switch mode */ + MA = (MA * 2) & 0177777; + MA = MA + GetMap(PC); + return (MA & 0177777); +} + +/* Given an address, returns either that address if bit 0 is 0, or + or follows an indirection chain until bit 0 is 0 +*/ + +int32 indirect(int32 d) +{ + int i; + + if (d & 0100000) { /* indirect? */ + for (i = 0; i < ind_max * 2; i++) { /* count indirects */ + if ((d & 077770) == 020 && !(cpu_unit.flags & UNIT_MICRO)) + d = (PutMap(d & AMASK, ((GetMap(d & AMASK) + 1) & 0177777))); + else if ((d & 077770) == 030 && !(cpu_unit.flags & UNIT_MICRO)) + d = (PutMap(d & AMASK, ((GetMap(d & AMASK) - 1) & 0177777))); + else d = GetMap(d & AMASK); + if (MapStat & 1) { /* Start MAP */ + Usermap = Enable; + Inhibit = 0; + } + if ((d & 0100000) == 0) break; + if ((MapStat & 010) && Usermap && i >= ind_max) break; + } + if (i >= (ind_max-1) && (MapStat & 010) && Usermap) { + Fault = 04000; /* Map fault if IND prot */ + } + if (i >= (ind_max * 2) && !(Fault)) { + reason = STOP_IND; /* Stop machine */ + } + } + return (d); +} + +/* Push a standard return block onto the stack */ + +int32 pushrtn(int32 pc) +{ + int32 t; + + t = (GetMap(040) + 1) & AMASK; + PutMap(t, AC[0]); + t++; + PutMap(t, AC[1]); + t++; + PutMap(t, AC[2]); + t++; + PutMap(t, AC[3]); + t++; + PutMap(t, pc); + if (C) PutMap(t, (GetMap(t) | 0100000)); + PutMap(040, t); + return 0; +} + +/* Eclipse memory get/put - uses MAP if enabled */ + +int32 GetMap(int32 addr) +{ + int32 page; + t_addr paddr; + + switch (Usermap) { + case 0: + if (addr < 076000) + return M[addr]; + paddr = ((Map31 & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 1: + page = (addr >> 10) & 037; + paddr = ((Map[1][page] & 01777) << 10) | (addr & 001777); + if (Map[1][page] == INVALID && !SingleCycle) + Fault = 0100000/*!!!*/; /* Validity */ + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 2: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (Map[2][page] == INVALID && !SingleCycle) + Fault = 0100000/*!!!*/; /* Validity */ + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 6: + page = (addr >> 10) & 037; + paddr = ((Map[6][page] & PAGEMASK) << 10) | (addr & 001777); + if (Map[6][page] == INVALID && !SingleCycle) + Fault = 0100000/*!!!*/; /* Validity */ + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + case 7: + page = (addr >> 10) & 037; + paddr = ((Map[7][page] & PAGEMASK) << 10) | (addr & 001777); + if (Map[7][page] == INVALID && !SingleCycle) + Fault = 0100000/*!!!*/; /* Validity */ + if (paddr < MEMSIZE) + return M[paddr]; + else + return (0); + break; + default: + printf("\n\r<>\n\r"); + return M[addr]; + break; + } +} + +int32 PutMap(int32 addr, int32 data) +{ + int32 page; + t_addr paddr; + + switch (Usermap) { + case 0: + if (addr < 076000) { + M[addr] = data; + return (data); + } + paddr = ((Map31 & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) M[paddr] = data; + break; + case 1: + page = (addr >> 10) & 037; + paddr = ((Map[1][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[1][page] & 0100000) && (MapStat & 020)) || Map[1][page] == INVALID) + Fault = 010000; /* Write Protect Fault */ + else if (paddr < MEMSIZE) M[paddr] = data; + break; + case 2: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[2][page] & 0100000) && (MapStat & 020)) || Map[2][page] == INVALID) + Fault = 010000; /* Write Protect Fault */ + else if (paddr < MEMSIZE) M[paddr] = data; + break; + case 6: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[6][page] & 0100000) && (MapStat & 020)) || Map[6][page] == INVALID) + Fault = 010000; /* Write Protect Fault */ + else if (paddr < MEMSIZE) M[paddr] = data; + break; + case 7: + page = (addr >> 10) & 037; + paddr = ((Map[2][page] & PAGEMASK) << 10) | (addr & 001777); + if (((Map[7][page] & 0100000) && (MapStat & 020)) || Map[7][page] == INVALID) + Fault = 010000; /* Write Protect Fault */ + else if (paddr < MEMSIZE) M[paddr] = data; + break; + default: + M[addr] = data; + break; + } + return (data); +} + +#if 0 +int16 GetDCHMap(int32 map, int32 addr) +{ + t_addr paddr; + if (!(MapStat & 02)) return M[addr]; + paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) + return M[paddr]; + return (0); +} + +int16 PutDCHMap(int32 map, int32 addr, int16 data) +{ + t_addr paddr; + if (!(MapStat & 02)) { + M[addr] = data; + return (data); + } + paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); + if (paddr < MEMSIZE) + M[paddr] = data; + return (data); +} +#endif + +/* Given a map number and a logical, returns the physical address, unless + the map is not active, in which case logical = physical. This is + used primarily by the I/O routines to map data channel read/writes. +*/ + +int32 MapAddr(int32 map, int32 addr) +{ + int32 paddr; + if ((map == 0 || map > 2) && !(MapStat & 02)) return addr; + if (map > 0 && map < 3 && Usermap == 0) return addr; + paddr = ((Map[map][(addr >> 10) & 037] & PAGEMASK) << 10) | (addr & 001777); + return paddr; +} + +/* Loads a word into the Eclipse Maps */ + +int32 LoadMap(int32 w) +{ + int32 m; + + m = (w >> 10) & 037; + switch ((MapStat >> 7) & 07) { + case 0: /* Load user A Map */ + Map[1][m] = w & MAPMASK; + break; + case 1: /* Load user C Map */ + Map[6][m] = w & MAPMASK; + break; + case 2: /* Load user B Map */ + Map[2][m] = w & MAPMASK; + break; + case 3: /* Load user D Map */ + Map[7][m] = w & MAPMASK; + break; + case 4: /* Load DCH A Map */ + Map[0][m] = w & MAPMASK; + break; + case 5: /* Load DCH C Map */ + Map[4][m] = w; + break; + case 6: /* Load DCH B Map */ + Map[3][m] = w; + break; + case 7: /* Load DCH D Map */ + Map[5][m] = w; + break; + default: + break; + } + return 0; +} + +/* Displays an error on a unimplemented (in this sim) instr. */ + +int32 unimp(int32 PC) +{ + if (Debug_Flags) + printf("\n\r\007<<>>\n\r", PC - 1, GetMap(PC - 1)); + return 0; +} + +/* New priority mask out */ + +void mask_out (int32 newmask) +{ +int32 i; + +dev_disable = 0; +for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (newmask & dev_table[i].pi) + dev_disable = dev_disable | dev_table[i].mask; +} +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int_req = int_req & ~INT_ION; +pimask = 0; +dev_disable = 0; +pwr_low = 0; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (sw & SWMASK ('V')) { + if (addr > 077777) return SCPE_NXM; + if (vptr != NULL) *vptr = GetMap (addr); +} +else { + if (addr >= MEMSIZE) return SCPE_NXM; + if (vptr != NULL) *vptr = M[addr] & 0177777; +} +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (sw & SWMASK ('V')) { + if (addr > 077777) return SCPE_NXM; + PutMap (addr, (int32) val); +} +else { + if (addr >= MEMSIZE) return SCPE_NXM; + M[addr] = (int32) val & 0177777; +} +return SCPE_OK; +} + +/* Alter memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +t_addr i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* MAP device services */ + +t_stat map_svc (UNIT *uptr) +{ +return SCPE_OK; +} + +/* Map examine */ + +t_stat map_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if ((addr & 077) >= 037 || addr > 737) return SCPE_NXM; +uptr->u4 = -2; /* signal to print_sys in eclipse_sys.c: do not map */ +if (vptr != NULL) *vptr = Map[(addr >> 6) & 3][addr & 037] & 0177777; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat map_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if ((addr & 077) >= 037 || addr > 0737) return SCPE_NXM; +uptr->u4 = -2; /* signal to print_sys in eclipse_sys.c: do not map */ +Map[(addr >> 6) & 3][addr & 037] = (int32)val & 0177777; +return SCPE_OK; +} + +/* FPU device services */ + +t_stat fpu_svc (UNIT *uptr) +{ +return SCPE_OK; +} + +/* PIT Device Services */ + +/* IOT routine */ + +int32 pit (int32 pulse, int32 code, int32 AC) +{ +int32 iodata = 0; + +if (code == ioDIA) { /* DIA */ + if (pit_flag == 0) { + pit_flag = 1; + } + iodata = pit_counter; +} +if (code == ioDOA) { /* DOA */ + pit_initial = AC; /* Load Counter */ + sim_rtcn_init (pit_time, 1); /* init calibr */ +} +switch (pulse) { /* decode IR<8:9> */ +case iopS: /* start */ + pit_counter = pit_initial; /* Set the counter */ + dev_busy = dev_busy | INT_PIT; /* set busy */ + dev_done = dev_done & ~INT_PIT; /* clear done, int */ + int_req = int_req & ~INT_PIT; + if (!sim_is_active (&pit_unit)) /* not running? */ + sim_activate (&pit_unit, /* activate */ + sim_rtcn_init (pit_time, 1)); /* init calibr */ + break; +case iopC: /* clear */ + dev_busy = dev_busy & ~INT_PIT; /* clear busy */ + dev_done = dev_done & ~INT_PIT; /* clear done, int */ + int_req = int_req & ~INT_PIT; + sim_cancel (&pit_unit); /* deactivate unit */ + break; } /* end switch */ +return iodata; +} + +/* Unit service */ + +t_stat pit_svc (UNIT *uptr) +{ +int32 t; +t = sim_rtcn_calb (pit_tps, 1); /* calibrate delay */ +sim_activate (&pit_unit, t); /* reactivate unit */ +pit_poll = t / (-pit_adj); /* adjust poll */ +pit_counter++; /* Increment counter */ +if (pit_counter >= 0177777) { /* Has counter reached limit ? */ + dev_done = dev_done | INT_PIT; /* set done */ + dev_busy = dev_busy & ~INT_PIT; /* clear busy */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); /* Interrupt */ + pit_counter = pit_initial; +} +return SCPE_OK; +} + +/* Reset routine */ + +t_stat pit_reset (DEVICE *dptr) +{ +pit_counter = 0; /* clear counter */ +dev_busy = dev_busy & ~INT_PIT; /* clear busy */ +dev_done = dev_done & ~INT_PIT; /* clear done, int */ +int_req = int_req & ~INT_PIT; +sim_cancel (&pit_unit); /* deactivate unit */ +pit_poll = pit_time; /* poll is default */ +return SCPE_OK; +} + +/* Bootstrap routine for CPU */ + +#define BOOT_START 00000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + + 062677, /* IORST ;Reset all I/O */ + 060477, /* READS 0 ;Read SR into AC0 */ + 024026, /* LDA 1,C77 ;Get dev mask */ + 0107400, /* AND 0,1 ;Isolate dev code */ + 0124000, /* COM 1,1 ;- device code - 1 */ + 010014, /* LOOP: ISZ OP1 ;Device code to all */ + 010030, /* ISZ OP2 ;I/O instructions */ + 010032, /* ISZ OP3 */ + 0125404, /* INC 1,1,SZR ;done? */ + 000005, /* JMP LOOP ;No, increment again */ + 030016, /* LDA 2,C377 ;place JMP 377 into */ + 050377, /* STA 2,377 ;location 377 */ + 060077, /* OP1: 060077 ;start device (NIOS 0) */ + 0101102, /* MOVL 0,0,SZC ;Test switch 0, low speed? */ + 000377, /* C377: JMP 377 ;no - jmp 377 & wait */ + 004030, /* LOOP2: JSR GET+1 ;Get a frame */ + 0101065, /* MOVC 0,0,SNR ;is it non-zero? */ + 000017, /* JMP LOOP2 ;no, ignore */ + 004027, /* LOOP4: JSR GET ;yes, get full word */ + 046026, /* STA 1,@C77 ;store starting at 100 */ + /* ;2's complement of word ct */ + 010100, /* ISZ 100 ;done? */ + 000022, /* JMP LOOP4 ;no, get another */ + 000077, /* C77: JMP 77 ;yes location ctr and */ + /* ;jmp to last word */ + 0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */ + /* OP2: */ + 063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */ + 000030, /* JMP LOOP3 ;no -- wait */ + 060477, /* OP3: 060477 ;y--read in ac0 (DIAS 0,0) */ + 0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */ + 000030, /* JMP LOOP3 ;no go back after it */ + 0125300, /* MOVS 1,1 ;yes swap them */ + 001400, /* JMP 0,3 ;rtn with full word */ + 0 /* 0 ;padding */ +}; + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} + +int32 Debug_Entry(int32 PC, int32 inst, int32 inst2, int32 AC0, int32 AC1, int32 AC2, int32 AC3, int32 flags) +{ + hpc[hnext] = PC & 0xffff; + hinst[hnext] = inst & 0xffff; + hinst2[hnext] = inst2 & 0xffff; + hac0[hnext] = AC0 & 0xffff; + hac1[hnext] = AC1 & 0xffff; + hac2[hnext] = AC2 & 0xffff; + hac3[hnext] = AC3 & 0xffff; + hflags[hnext] = flags & 0xffff; + hnext++; + if (hnext >= hmax) { + hwrap = 1; + hnext = 0; + } + return 0; +} + +int32 Debug_Dump(UNIT *uptr, int32 val, char *cptr, void *desc) +{ + return SCPE_OK; +} + +int32 Dump_History (FILE *st, UNIT *uptr, int32 val, void *desc) +{ + char debmap[4], debion[4]; + t_value simeval[20]; + int debcar; + int start, end, ctr; + int count = 0; + + if (!Debug_Flags || Debug_Flags & 0100000) { + printf("History was not logged. Deposit a non-zero value\n"); + printf("in DEBUG with bit 0 being 1 to build history.\n"); + return SCPE_OK; + } + if (!hwrap) { + start = 0; + end = hnext; + } else { + start = hnext; + end = hnext - 1; + if (end < 0) end = hmax; + } + ctr = start; + while (1) { + if (ctr == end) + break; + count++; + strcpy(debion, " "); + strcpy(debmap, " "); + debcar = 0; + if (hflags[ctr] & 0x80) { + fprintf(st, "--------- Interrupt %o (%o) to %6o ---------\n", + hinst[ctr], hac0[ctr], hac1[ctr]); + } else { + if (hflags[ctr] & 0x01) debcar = 1; + if (hflags[ctr] & 0x02) strcpy(debion, "I"); + if (hflags[ctr] & 0x04) strcpy(debmap, "A"); + if (hflags[ctr] & 0x08) strcpy(debmap, "B"); + if (hflags[ctr] & 0x10) strcpy(debmap, "C"); + if (hflags[ctr] & 0x20) strcpy(debmap, "D"); + fprintf(st, "%s%s%06o acs: %06o %06o %06o %06o %01o ", + debion, debmap, hpc[ctr], hac0[ctr], hac1[ctr], hac2[ctr], + hac3[ctr], debcar); + simeval[0] = hinst[ctr]; + simeval[1] = hinst2[ctr]; + fprint_sym (st, hpc[ctr], simeval, NULL, SWMASK('M')); + fprintf(st, "\n"); + } + ctr++; + if (ctr > hmax) + ctr = 0; + } + return SCPE_OK; +} + +/* Build dispatch table */ + +t_stat build_devtab (void) +{ +DEVICE *dptr; +DIB *dibp; +int32 i, dn; + +for (i = 0; i < 64; i++) { /* clr dev_table */ + dev_table[i].mask = 0; + dev_table[i].pi = 0; + dev_table[i].routine = NULL; +} +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + if (!(dptr->flags & DEV_DIS) && /* enabled and */ + (dibp = (DIB *) dptr->ctxt)) { /* defined DIB? */ + dn = dibp->dnum; /* get dev num */ + dev_table[dn].mask = dibp->mask; /* copy entries */ + dev_table[dn].pi = dibp->pi; + dev_table[dn].routine = dibp->routine; + } +} +return SCPE_OK; +} + +/* ------------------------------------------------------------------- */ +/* Floating Point Arithmetic */ +/* ------------------------------------------------------------------- */ + + +/* Get short float from FPAC */ + +void get_sf (SHORT_FLOAT *fl, t_int64 *fpr) +{ + fl->sign = (uint8)(*fpr >> 63) & 1; + fl->expo = (short)(*fpr >> 56) & 0x007F; + fl->short_fract = (int32)(*fpr >> 32) & 0x00FFFFFF; +} + +/* Store short float to FPAC */ + +void store_sf (SHORT_FLOAT *fl, t_int64 *fpr) +{ + *fpr = 0; + *fpr = ((t_int64)fl->sign << 63) + | ((t_int64)fl->expo << 56) + | ((t_int64)fl->short_fract <<32); +} + +/* Get long float from FPAC */ + +void get_lf (LONG_FLOAT *fl, t_int64 *fpr) +{ + fl->sign = (uint8)(*fpr >> 63) & 1; + fl->expo = (short)(*fpr >> 56) & 0x007F; + fl->long_fract = (t_int64)*fpr & 0x00FFFFFFFFFFFFFF; + +} + +/* Store long float to FPAC */ + +void store_lf (LONG_FLOAT *fl, t_int64 *fpr) +{ + *fpr = 0; + *fpr = (t_int64)fl->sign << 63; + *fpr |= ((t_int64)fl->expo << 56) & 0x7f00000000000000; + *fpr |= fl->long_fract; +} + + +/* Check short for Overflow */ + +int overflow_sf (SHORT_FLOAT *fl) +{ + if (fl->expo > 127) { + fl->expo &= 0x007F; + return(1); + } + return(0); + +} + +/* Normalize Short Float */ + +int normal_sf(SHORT_FLOAT *fl) +{ + if (fl->short_fract) { + if ((fl->short_fract & 0x00FFFF00) == 0) { + fl->short_fract <<= 16; + fl->expo -= 4; + } + if ((fl->short_fract & 0x00FF0000) == 0) { + fl->short_fract <<= 8; + fl->expo -= 2; + } + if ((fl->short_fract & 0x00F00000) == 0) { + fl->short_fract <<= 4; + (fl->expo)--; + } + } else { + fl->sign = 0; + fl->expo = 0; + } + if (fl->expo < 0) + return (2); + return(0); +} + +/* Normalize long float */ + +int normal_lf (LONG_FLOAT *fl) +{ + if (fl->long_fract) { + if ((fl->long_fract & 0x00FFFFFFFF000000) == 0) { + fl->long_fract <<= 32; + fl->expo -= 8; + } + if ((fl->long_fract & 0x00FFFF0000000000) == 0) { + fl->long_fract <<= 16; + fl->expo -= 4; + } + if ((fl->long_fract & 0x00FF000000000000) == 0) { + fl->long_fract <<= 8; + fl->expo -= 2; + } + if ((fl->long_fract & 0x00F0000000000000) == 0) { + fl->long_fract <<= 4; + (fl->expo)--; + } + } else { + fl->sign = 0; + fl->expo = 0; + } + if (fl->expo < 0) + return (2); + return(0); +} + +/* Check Long for Overflow */ + +int overflow_lf(LONG_FLOAT *fl) +{ + if (fl->expo > 127) { + fl->expo &= 0x007F; + return(1); + } + return(0); + +} + +int underflow_sf(SHORT_FLOAT *fl) +{ + if (fl->expo < 0) { + fl->short_fract = 0; + fl->expo = 0; + fl->sign = 0; + } + return(0); + +} + + +int underflow_lf(LONG_FLOAT *fl) +{ + if (fl->expo < 0) { + fl->long_fract = 0; + fl->expo = 0; + fl->sign = 0; + } + return(0); +} + +/* Check Short for Over/Under flow */ + +int over_under_flow_sf(SHORT_FLOAT *fl) +{ + if (fl->expo > 127) { + fl->expo &= 0x007F; + return(1); + } else { + if (fl->expo < 0) { + /* set true 0 */ + fl->short_fract = 0; + fl->expo = 0; + fl->sign = 0; + } + } + return(0); + +} + +/* Check Long for Over/Under flow */ + +int over_under_flow_lf(LONG_FLOAT *fl) +{ + if (fl->expo > 127) { + fl->expo &= 0x007F; + return(1); + } else { + if (fl->expo < 0) { + /* set true 0 */ + fl->long_fract = 0; + fl->expo = 0; + fl->sign = 0; + } + } + return(0); + +} + +int significance_sf (SHORT_FLOAT *fl) +{ + fl->sign = 0; + fl->expo = 0; + return(0); + +} + +int significance_lf (LONG_FLOAT *fl) +{ + fl->sign = 0; + fl->expo = 0; + return(0); + +} + + +/*-------------------------------------------------------------------*/ +/* Add short float */ +/* */ +/* Input: */ +/* fl Float */ +/* add_fl Float to be added */ +/* normal Normalize if true */ +/* Value: */ +/* exeption */ +/*-------------------------------------------------------------------*/ +int add_sf (SHORT_FLOAT *fl, SHORT_FLOAT *add_fl, int normal) +{ +int pgm_check; +int shift; + + pgm_check = 0; + if (add_fl->short_fract + || add_fl->expo) { /* add_fl not 0 */ + if (fl->short_fract + || fl->expo) { /* fl not 0 */ + /* both not 0 */ + + if (fl->expo == add_fl->expo) { + /* expo equal */ + + /* both guard digits */ + fl->short_fract <<= 4; + add_fl->short_fract <<= 4; + } else { + /* expo not equal, denormalize */ + + if (fl->expo < add_fl->expo) { + /* shift minus guard digit */ + shift = add_fl->expo - fl->expo - 1; + fl->expo = add_fl->expo; + + if (shift) { + if (shift >= 6 + || ((fl->short_fract >>= (shift * 4)) == 0)) { + /* 0, copy summand */ + + fl->sign = add_fl->sign; + fl->short_fract = add_fl->short_fract; + + if (fl->short_fract == 0) { + pgm_check = significance_sf(fl); + } else { + if (normal) { + normal_sf(fl); + pgm_check = underflow_sf(fl); + } + } + return(pgm_check); + } + } + /* guard digit */ + add_fl->short_fract <<= 4; + } else { + /* shift minus guard digit */ + shift = fl->expo - add_fl->expo - 1; + + if (shift) { + if (shift >= 6 + || ((add_fl->short_fract >>= (shift * 4)) == 0)) { + /* 0, nothing to add */ + + if (fl->short_fract == 0) { + pgm_check = significance_sf(fl); + } else { + if (normal) { + normal_sf(fl); + pgm_check = underflow_sf(fl); + } + } + return(pgm_check); + } + } + /* guard digit */ + fl->short_fract <<= 4; + } + } + + /* compute with guard digit */ + if (fl->sign == add_fl->sign) { + fl->short_fract += add_fl->short_fract; + } else { + if (fl->short_fract == add_fl->short_fract) { + /* true 0 */ + + fl->short_fract = 0; + return( significance_sf(fl) ); + + } else if (fl->short_fract > add_fl->short_fract) { + fl->short_fract -= add_fl->short_fract; + } else { + fl->short_fract = add_fl->short_fract - fl->short_fract; + fl->sign = add_fl->sign; + } + } + + /* handle overflow with guard digit */ + if (fl->short_fract & 0xF0000000) { + fl->short_fract >>= 8; + (fl->expo)++; + pgm_check = overflow_sf(fl); + } else { + + if (normal) { + /* normalize with guard digit */ + if (fl->short_fract) { + /* not 0 */ + + if (fl->short_fract & 0x0F000000) { + /* not normalize, just guard digit */ + fl->short_fract >>= 4; + } else { + (fl->expo)--; + normal_sf(fl); + pgm_check = underflow_sf(fl); + } + } else { + /* true 0 */ + + pgm_check = significance_sf(fl); + } + } else { + /* not normalize, just guard digit */ + fl->short_fract >>= 4; + if (fl->short_fract == 0) { + pgm_check = significance_sf(fl); + } + } + } + return(pgm_check); + } else { /* fl 0, add_fl not 0 */ + /* copy summand */ + + fl->expo = add_fl->expo; + fl->sign = add_fl->sign; + fl->short_fract = add_fl->short_fract; + if (fl->short_fract == 0) { + return( significance_sf(fl) ); + } + } + } else { /* add_fl 0 */ + if (fl->short_fract == 0) { /* fl 0 */ + /* both 0 */ + + return( significance_sf(fl) ); + } + } + if (normal) { + normal_sf(fl); + pgm_check = underflow_sf(fl); + } + return(pgm_check); + +} + + +/*-------------------------------------------------------------------*/ +/* Add long float */ +/* */ +/* Input: */ +/* fl Float */ +/* add_fl Float to be added */ +/* normal Normalize if true */ +/* Value: */ +/* exeption */ +/*-------------------------------------------------------------------*/ +int add_lf (LONG_FLOAT *fl, LONG_FLOAT *add_fl, int normal) +{ +int pgm_check; +int shift; + + pgm_check = 0; + if (add_fl->long_fract + || add_fl->expo) { /* add_fl not 0 */ + if (fl->long_fract + || fl->expo) { /* fl not 0 */ + /* both not 0 */ + + if (fl->expo == add_fl->expo) { + /* expo equal */ + + /* both guard digits */ + fl->long_fract <<= 4; + add_fl->long_fract <<= 4; + } else { + /* expo not equal, denormalize */ + + if (fl->expo < add_fl->expo) { + /* shift minus guard digit */ + shift = add_fl->expo - fl->expo - 1; + fl->expo = add_fl->expo; + + if (shift) { + if (shift >= 14 + || ((fl->long_fract >>= (shift * 4)) == 0)) { + /* 0, copy summand */ + + fl->sign = add_fl->sign; + fl->long_fract = add_fl->long_fract; + + if (fl->long_fract == 0) { + pgm_check = significance_lf(fl); + } else { + if (normal) { + normal_lf(fl); + pgm_check = underflow_lf(fl); + } + } + return(pgm_check); + } + } + /* guard digit */ + add_fl->long_fract <<= 4; + } else { + /* shift minus guard digit */ + shift = fl->expo - add_fl->expo - 1; + + if (shift) { + if (shift >= 14 + || ((add_fl->long_fract >>= (shift * 4)) == 0)) { + /* 0, nothing to add */ + + if (fl->long_fract == 0) { + pgm_check = significance_lf(fl); + } else { + if (normal) { + normal_lf(fl); + pgm_check = underflow_lf(fl); + } + } + return(pgm_check); + } + } + /* guard digit */ + fl->long_fract <<= 4; + } + } + + /* compute with guard digit */ + if (fl->sign == add_fl->sign) { + fl->long_fract += add_fl->long_fract; + } else { + if (fl->long_fract == add_fl->long_fract) { + /* true 0 */ + + fl->long_fract = 0; + return( significance_lf(fl) ); + + } else if (fl->long_fract > add_fl->long_fract) { + fl->long_fract -= add_fl->long_fract; + } else { + fl->long_fract = add_fl->long_fract - fl->long_fract; + fl->sign = add_fl->sign; + } + } + + /* handle overflow with guard digit */ + if (fl->long_fract & 0xF000000000000000) { + fl->long_fract >>= 8; + (fl->expo)++; + pgm_check = overflow_lf(fl); + } else { + + if (normal) { + /* normalize with guard digit */ + if (fl->long_fract) { + /* not 0 */ + + if (fl->long_fract & 0x0F00000000000000) { + /* not normalize, just guard digit */ + fl->long_fract >>= 4; + } else { + (fl->expo)--; + normal_lf(fl); + pgm_check = underflow_lf(fl); + } + } else { + /* true 0 */ + + pgm_check = significance_lf(fl); + } + } else { + /* not normalize, just guard digit */ + fl->long_fract >>= 4; + if (fl->long_fract == 0) { + pgm_check = significance_lf(fl); + } + } + } + return(pgm_check); + } else { /* fl 0, add_fl not 0 */ + /* copy summand */ + + fl->expo = add_fl->expo; + fl->sign = add_fl->sign; + fl->long_fract = add_fl->long_fract; + if (fl->long_fract == 0) { + return( significance_lf(fl) ); + } + } + } else { /* add_fl 0 */ + if (fl->long_fract == 0) { /* fl 0 */ + /* both 0 */ + + return( significance_lf(fl) ); + } + } + if (normal) { + normal_lf(fl); + pgm_check = underflow_lf(fl); + } + return(pgm_check); + +} + +/*-------------------------------------------------------------------*/ +/* Multiply short float */ +/* */ +/* Input: */ +/* fl Multiplicand short float */ +/* mul_fl Multiplicator short float */ +/* Value: */ +/* exeption */ +/*-------------------------------------------------------------------*/ + +int mul_sf(SHORT_FLOAT *fl, SHORT_FLOAT *mul_fl) +{ +t_int64 wk; + + if (fl->short_fract + && mul_fl->short_fract) { + /* normalize operands */ + normal_sf( fl ); + normal_sf( mul_fl ); + + /* multiply fracts */ + wk = (t_int64) fl->short_fract * mul_fl->short_fract; + + /* normalize result and compute expo */ + if (wk & 0x0000F00000000000) { + fl->short_fract = (int32)wk >> 24; + fl->expo = (short)fl->expo + mul_fl->expo - 64; + } else { + fl->short_fract = (int32)wk >> 20; + fl->expo = (short)fl->expo + mul_fl->expo - 65; + } + + /* determine sign */ + fl->sign = (fl->sign == mul_fl->sign) ? 0 : 1; + + /* handle overflow and underflow */ + return( over_under_flow_sf(fl) ); + } else { + /* set true 0 */ + + fl->short_fract = 0; + fl->expo = 0; + fl->sign = 0; + return(0); + } + +} + + +/*-------------------------------------------------------------------*/ +/* Multiply long float */ +/* */ +/* Input: */ +/* fl Multiplicand long float */ +/* mul_fl Multiplicator long float */ +/* Value: */ +/* exeption */ +/*-------------------------------------------------------------------*/ +int mul_lf(LONG_FLOAT *fl, LONG_FLOAT *mul_fl) +{ +t_int64 wk; +int32 v; + + if (fl->long_fract + && mul_fl->long_fract) { + /* normalize operands */ + normal_lf( fl ); + normal_lf( mul_fl ); + + /* multiply fracts by sum of partial multiplications */ + wk = ((fl->long_fract & 0x00000000FFFFFFFF) * (mul_fl->long_fract & 0x00000000FFFFFFFF)) >> 32; + + wk += ((fl->long_fract & 0x00000000FFFFFFFF) * (mul_fl->long_fract >> 32)); + wk += ((fl->long_fract >> 32) * (mul_fl->long_fract & 0x00000000FFFFFFFF)); + v = (int32)wk; + + fl->long_fract = (wk >> 32) + ((fl->long_fract >> 32) * (mul_fl->long_fract >> 32)); + + /* normalize result and compute expo */ + if (fl->long_fract & 0x0000F00000000000) { + fl->long_fract = (fl->long_fract << 8) + | (v >> 24); + fl->expo = fl->expo + mul_fl->expo - 64; + } else { + fl->long_fract = (fl->long_fract << 12) + | (v >> 20); + fl->expo = fl->expo + mul_fl->expo - 65; + } + + /* determine sign */ + fl->sign = (fl->sign == mul_fl->sign) ? 0 : 1; + + /* handle overflow and underflow */ + return( over_under_flow_lf(fl) ); + } else { + /* set true 0 */ + + fl->long_fract = 0; + fl->expo = 0; + fl->sign = 0; + return(0); + } + +} + + +/*-------------------------------------------------------------------*/ +/* Divide short float */ +/* */ +/* Input: */ +/* fl Dividend short float */ +/* div_fl Divisor short float */ +/* Value: */ +/* exeption */ +/*-------------------------------------------------------------------*/ +int div_sf(SHORT_FLOAT *fl, SHORT_FLOAT *div_fl) +{ +t_int64 wk; + + if (div_fl->short_fract) { + if (fl->short_fract) { + /* normalize operands */ + normal_sf( fl ); + normal_sf( div_fl ); + + /* position fracts and compute expo */ + if (fl->short_fract < div_fl->short_fract) { + wk = (t_int64) fl->short_fract << 24; + fl->expo = fl->expo - div_fl->expo + 64; + } else { + wk = (t_int64) fl->short_fract << 20; + fl->expo = fl->expo - div_fl->expo + 65; + } + /* divide fractions */ + fl->short_fract = (int32)wk / div_fl->short_fract; + + /* determine sign */ + fl->sign = (fl->sign == div_fl->sign) ? 0 : 1; + + /* handle overflow and underflow */ + return( over_under_flow_sf(fl) ); + } else { + /* fraction of dividend 0, set true 0 */ + + fl->short_fract = 0; + fl->expo = 0; + fl->sign = 0; + } + } else { + /* divisor 0 */ + + return(3); + } + return(0); + +} + + +/*-------------------------------------------------------------------*/ +/* Divide long float */ +/* */ +/* Input: */ +/* fl Dividend long float */ +/* div_fl Divisor long float */ +/* Value: */ +/* exeption */ +/*-------------------------------------------------------------------*/ +int div_lf(LONG_FLOAT *fl, LONG_FLOAT *div_fl) +{ +t_int64 wk; +t_int64 wk2; +int i; + + if (div_fl->long_fract) { + if (fl->long_fract) { + /* normalize operands */ + normal_lf( fl ); + normal_lf( div_fl ); + + /* position fracts and compute expo */ + if (fl->long_fract < div_fl->long_fract) { + fl->expo = fl->expo - div_fl->expo + 64; + } else { + fl->expo = fl->expo - div_fl->expo + 65; + div_fl->long_fract <<= 4; + } + + /* partial divide first hex digit */ + wk2 = fl->long_fract / div_fl->long_fract; + wk = (fl->long_fract % div_fl->long_fract) << 4; + + /* partial divide middle hex digits */ + i = 13; + while (i--) { + wk2 = (wk2 << 4) + | (wk / div_fl->long_fract); + wk = (wk % div_fl->long_fract) << 4; + } + + /* partial divide last hex digit */ + fl->long_fract = (wk2 << 4) + | (wk / div_fl->long_fract); + + /* determine sign */ + fl->sign = (fl->sign == div_fl->sign) ? 0 : 1; + + /* handle overflow and underflow */ + return( over_under_flow_lf(fl) ); + } else { + /* fraction of dividend 0, set true 0 */ + + fl->long_fract = 0; + fl->expo = 0; + fl->sign = 0; + } + } else { + /* divisor 0 */ + + return(3); + } + return(0); + +} + diff --git a/NOVA/eclipse_cpu.o b/NOVA/eclipse_cpu.o new file mode 100644 index 0000000000000000000000000000000000000000..3ecb2696f4ee3a588de0c349616a85f94282d5e7 GIT binary patch literal 170636 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 */ + + case iopS: /* start */ + dev_busy = dev_busy | INT_TTI; /* set busy */ + dev_done = dev_done & ~INT_TTI; /* clear done, int */ + int_req = int_req & ~INT_TTI; + break; + + case iopC: /* clear */ + dev_busy = dev_busy & ~INT_TTI; /* clear busy */ + dev_done = dev_done & ~INT_TTI; /* clear done, int */ + int_req = int_req & ~INT_TTI; + break; + } /* end switch */ + +return iodata; +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +tti_unit.buf = temp & 0177; +/* --- BEGIN MODIFIED CODE --- */ +if (tti_unit.flags & UNIT_DASHER) /* translate input */ + translate_in(); +/* --- END MODIFIED CODE --- */ +dev_busy = dev_busy & ~INT_TTI; /* clear busy */ +dev_done = dev_done | INT_TTI; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +tti_unit.pos = tti_unit.pos + 1; +return SCPE_OK; +} + +/* -------------------- BEGIN INSERTION -----------------------*/ + +int curpos = 0; /* used by translate_out() */ +int row = 0, col = 0; /* ditto - for cursor positioning */ +int spec200 = 0; /* signals next char is 'special' */ + +/* Translation: VT100 input to D200 keycodes. */ + +void translate_in() +{ + char rev = 0; + + if (tti_unit.buf == '\r') + rev = '\n'; + if (tti_unit.buf == '\n') + rev = '\r'; + if (rev) + tti_unit.buf = rev; +} + +/* -------------------- END INSERTION -----------------------*/ + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; +dev_busy = dev_busy & ~INT_TTI; /* clear busy */ +dev_done = dev_done & ~INT_TTI; /* clear done, int */ +int_req = int_req & ~INT_TTI; +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) tto_unit.buf = AC & 0377; +switch (pulse) { /* decode IR<8:9> */ + + case iopS: /* start */ + dev_busy = dev_busy | INT_TTO; /* set busy */ + dev_done = dev_done & ~INT_TTO; /* clear done, int */ + int_req = int_req & ~INT_TTO; + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + break; + + case iopC: /* clear */ + dev_busy = dev_busy & ~INT_TTO; /* clear busy */ + dev_done = dev_done & ~INT_TTO; /* clear done, int */ + int_req = int_req & ~INT_TTO; + sim_cancel (&tto_unit); /* deactivate unit */ + break; + } /* end switch */ + +return 0; +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c, temp; + +dev_busy = dev_busy & ~INT_TTO; /* clear busy */ +dev_done = dev_done | INT_TTO; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +c = tto_unit.buf & 0177; +/* --- BEGIN MODIFIED CODE --- */ +if (tto_unit.flags & UNIT_DASHER) { + if ((temp = translate_out(c)) != SCPE_OK) return temp; +} else { + if ((temp = sim_putchar (c)) != SCPE_OK) return temp; + tto_unit.pos = tto_unit.pos + 1; +} +/* --- END MODIFIED CODE --- */ +return SCPE_OK; +} + +/* -------------------- BEGIN INSERTION -----------------------*/ + +/* Translation routine - D200 screen controls to VT-100 controls. */ + +int32 translate_out(int32 c) +{ + int32 temp; + char outstr[32]; + + if (spec200 == 1) { /* Special terminal control seq */ + spec200 = 0; + switch (c) { + case 'C': /* read model ID */ + return SCPE_OK; + case 'E': /* Reverse video off */ + return SCPE_OK; + case 'D': /* Reverse video on */ + return SCPE_OK; + default: + return SCPE_OK; + } + } + if (curpos == 1) { /* 2nd char of cursor position */ + col = c & 0x7f; + curpos++; + return (SCPE_OK); + } + if (curpos == 2) { /* 3rd char of cursor position */ + row = c & 0x7f; + curpos = 0; + sprintf(outstr, "\033[%d;%dH", row+1, col+1); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + } + switch (c) { /* Single-char command or data */ + case 003: /* Blink enable */ + break; + case 004: /* Blink disable */ + break; + case 005: /* Read cursor address */ + break; + case 010: /* Cursor home */ + sprintf(outstr, "\033[1;1H"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row = col = 0; + return (SCPE_OK); + case 012: /* Newline */ + if ((temp = sim_putchar('\r')) != SCPE_OK) return temp; + tto_unit.pos += 1; + if ((temp = sim_putchar(c)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col = 1; + row++; + if (row > 24) row = 1; + return (SCPE_OK); + case 013: /* Erase EOL */ + sprintf(outstr, "\033[K"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 014: /* Erase screen */ + sprintf(outstr, "\033[1;1H\033[2J"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row = col = 0; + return (SCPE_OK); + case 015: /* CR */ + if ((temp = sim_putchar(c)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col = 1; + return (SCPE_OK); + case 016: /* Blink On */ + sprintf(outstr, "\033[5m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 017: /* Blink off */ + sprintf(outstr, "\033[25m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 020: /* Write cursor address */ + curpos = 1; + return SCPE_OK; + case 024: /* underscore on */ + sprintf(outstr, "\033[4m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 025: /* underscore off */ + sprintf(outstr, "\033[24m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + break; + case 027: /* cursor up */ + sprintf(outstr, "\033[A"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row--; + if (row < 1) row = 24; + return (SCPE_OK); + case 030: /* cursor right */ + sprintf(outstr, "\033[C"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + col++; + if (col > 80) { + col = 1; + row++; + if (row > 24) row = 1; + } + return (SCPE_OK); + case 031: /* Cursor left */ + sprintf(outstr, "\033[D"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col--; + if (col < 1) { + col = 80; + row--; + if (row < 1) row = 24; + } + return (SCPE_OK); + case 032: /* Cursor down */ + sprintf(outstr, "\033[B"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + row++; + if (row > 24) row = 1; + return (SCPE_OK); + case 034: /* Dim on */ + sprintf(outstr, "\033[22m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 035: /* Dim off */ + sprintf(outstr, "\033[1m"); + if ((temp = putseq(outstr)) != SCPE_OK) return temp; + return (SCPE_OK); + case 036: /* Special sequence */ + spec200 = 1; + return SCPE_OK; + default: /* ..A character of data */ + if ((temp = sim_putchar(c)) != SCPE_OK) return temp; + tto_unit.pos += 1; + col++; + if (col > 80) { + col = 1; + row++; + if (row > 24) row = 24; + } + return (SCPE_OK); + } + return SCPE_OK; +} + +int32 putseq(char *seq) +{ + int i, len, temp; + + len = strlen(seq); + for (i = 0; i < len; i++) { + if ((temp = sim_putchar(seq[i])) != SCPE_OK) + return temp; + tto_unit.pos += 1; + } + return SCPE_OK; +} + +/* -------------------- END INSERTION -----------------------*/ + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +dev_busy = dev_busy & ~INT_TTO; /* clear busy */ +dev_done = dev_done & ~INT_TTO; /* clear done, int */ +int_req = int_req & ~INT_TTO; +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat ttx_setmod (UNIT *uptr, int32 value, char *cptr, void *desc) +{ +tti_unit.flags = (tti_unit.flags & ~UNIT_DASHER) | value; +tto_unit.flags = (tto_unit.flags & ~UNIT_DASHER) | value; +return SCPE_OK; +} diff --git a/NOVA/eclipse_tt.o b/NOVA/eclipse_tt.o new file mode 100644 index 0000000000000000000000000000000000000000..2091b117e4ef2b0b104ed3eef97cc07a696429fe GIT binary patch literal 17120 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 literal 0 HcmV?d00001 diff --git a/NOVA/nova_clk.c b/NOVA/nova_clk.c new file mode 100644 index 0000000..8a38479 --- /dev/null +++ b/NOVA/nova_clk.c @@ -0,0 +1,182 @@ +/* nova_clk.c: NOVA real-time clock simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + clk real-time clock + + 04-Jul-07 BKR DEV_SET/CLR macros now used, + changed CLK name to RTC for DG compatiblity, + device may now bw DISABLED + 01-Mar-03 RMS Added SET/SHOW CLK FREQ support + 03-Oct-02 RMS Added DIB + 17-Sep-01 RMS Added terminal multiplexor support + 17-Mar-01 RMS Moved function prototype + 05-Mar-01 RMS Added clock calibration + 24-Sep-97 RMS Fixed bug in unit service (found by Charles Owen) +*/ + +#include "nova_defs.h" + +extern int32 int_req, dev_busy, dev_done, dev_disable ; + +int32 clk_sel = 0; /* selected freq */ +int32 clk_time[4] = { 16000, 100000, 10000, 1000 }; /* freq table */ +int32 clk_tps[4] = { 60, 10, 100, 1000 }; /* ticks per sec */ +int32 clk_adj[4] = { 1, -5, 2, 20 }; /* tmxr adjust */ +int32 tmxr_poll = 16000; /* tmxr poll */ + +int32 clk (int32 pulse, int32 code, int32 AC); +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +DIB clk_dib = { DEV_CLK, INT_CLK, PI_CLK, &clk }; + +UNIT clk_unit = { UDATA (&clk_svc, 0, 0) }; + +REG clk_reg[] = { + { ORDATA (SELECT, clk_sel, 2) }, + { FLDATA (BUSY, dev_busy, INT_V_CLK) }, + { FLDATA (DONE, dev_done, INT_V_CLK) }, + { FLDATA (DISABLE, dev_disable, INT_V_CLK) }, + { FLDATA (INT, int_req, INT_V_CLK) }, + { DRDATA (TIME0, clk_time[0], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME1, clk_time[1], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME2, clk_time[2], 24), REG_NZ + PV_LEFT }, + { DRDATA (TIME3, clk_time[3], 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS0, clk_tps[0], 6), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "LINE", NULL, + NULL, &clk_show_freq, NULL }, + { 0 } + }; + +DEVICE clk_dev = { + "RTC", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, DEV_DISABLE + }; + + +/* IOT routine */ + +int32 clk (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) { /* DOA */ + clk_sel = AC & 3; /* save select */ + sim_rtc_init (clk_time[clk_sel]); /* init calibr */ + } + +switch (pulse) { /* decode IR<8:9> */ + + case iopS: /* start */ + DEV_SET_BUSY( INT_CLK ) ; + DEV_CLR_DONE( INT_CLK ) ; + DEV_UPDATE_INTR ; + if (!sim_is_active (&clk_unit)) /* not running? */ + sim_activate (&clk_unit, /* activate */ + sim_rtc_init (clk_time[clk_sel])); /* init calibr */ + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_CLK ) ; + DEV_CLR_DONE( INT_CLK ) ; + DEV_UPDATE_INTR ; + sim_cancel (&clk_unit); /* deactivate unit */ + break; + } /* end switch */ + +return 0; +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +int32 t; + +if ( DEV_IS_BUSY(INT_CLK) ) + { + DEV_CLR_BUSY( INT_CLK ) ; + DEV_SET_DONE( INT_CLK ) ; + DEV_UPDATE_INTR ; + } +t = sim_rtc_calb (clk_tps[clk_sel]); /* calibrate delay */ +sim_activate (&clk_unit, t); /* reactivate unit */ +if (clk_adj[clk_sel] > 0) /* clk >= 60Hz? */ + tmxr_poll = t * clk_adj[clk_sel]; /* poll is longer */ +else + tmxr_poll = t / (-clk_adj[clk_sel]); /* poll is shorter */ + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +clk_sel = 0; +DEV_CLR_BUSY( INT_CLK ) ; +DEV_CLR_DONE( INT_CLK ) ; +DEV_UPDATE_INTR ; + +sim_cancel (&clk_unit); /* deactivate unit */ +tmxr_poll = clk_time[0]; /* poll is default */ +return SCPE_OK; +} + +/* Set line frequency */ + +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val != 50) && (val != 60)) return SCPE_IERR; +clk_tps[0] = val; +return SCPE_OK; +} + +/* Show line frequency */ + +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, (clk_tps[0] == 50)? "50Hz": "60Hz"); +return SCPE_OK; +} diff --git a/NOVA/nova_cpu.c b/NOVA/nova_cpu.c new file mode 100644 index 0000000..58cc846 --- /dev/null +++ b/NOVA/nova_cpu.c @@ -0,0 +1,1472 @@ +/* nova_cpu.c: NOVA CPU simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu Nova central processor + + 04-Jul-07 BKR DEV_SET/CLR macros now used, + support for non-existant devices added + CPU bootstrap code warning: high-speed devices may not boot properly, + execution history facility added, + documented Nova 3 secret LDB/STB/SAVN behavior, + added support for secret Nova 3 LDB/STB/SAVN substitute actions, + 'ind_max' changed from 16 to 65536 for better unmapped system compatibility, + INT_TRAP added for Nova 3, 4 trap instruction handling, + 28-Apr-07 RMS Removed clock initialization + 06-Feb-06 RMS Fixed bug in DIVS (found by Mark Hittinger) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 25-Aug-05 RMS Fixed DIVS case 2^31 / - 1 + 14-Jan-04 RMS Fixed device enable/disable support (found by Bruce Ray) + 19-Jan-03 RMS Changed CMASK to CDMASK for Apple Dev Kit conflict + 03-Oct-02 RMS Added DIB infrastructure + 30-Dec-01 RMS Added old PC queue + 07-Dec-01 RMS Revised to use breakpoint package + 30-Nov-01 RMS Added extended SET/SHOW support + 10-Aug-01 RMS Removed register in declarations + 17-Jul-01 RMS Moved function prototype + 26-Apr-01 RMS Added device enable/disable support + 05-Mar-01 RMS Added clock calibration + 22-Dec-00 RMS Added Bruce Ray's second terminal + 15-Dec-00 RMS Added Charles Owen's CPU bootstrap + 08-Dec-00 RMS Changes from Bruce Ray + -- fixed trap test to include Nova 3 + -- fixed DIV and DIVS divide by 0 + -- fixed RETN to set SP from FP + -- fixed IORST to preserve carry + -- added "secret" Nova 4 PSHN/SAVEN instructions + -- added plotter support + 15-Oct-00 RMS Fixed bug in MDV test, added stack, byte, trap instructions + 14-Apr-98 RMS Changed t_addr to unsigned + 15-Sep-97 RMS Added read and write breakpoints + + The register state for the NOVA CPU is: + + AC[0:3]<0:15> general registers + C carry flag + PC<0:14> program counter + + The NOVA has three instruction formats: memory reference, I/O transfer, + and operate. The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0| op | AC |in| mode| displacement | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> mnemonic action + + 00000 JMP PC = MA + 00001 JMS AC3 = PC, PC = MA + 00010 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 00011 DSZ M[MA] = M[MA] - 1, skip if M[MA] == 0 + 001'n LDA ACn = M[MA] + 010'n STA M[MA] = ACn + + <5:7> mode action + + 000 page zero direct MA = zext (IR<8:15>) + 001 PC relative direct MA = PC + sext (IR<8:15>) + 010 AC2 relative direct MA = AC2 + sext (IR<8:15>) + 011 AC3 relative direct MA = AC3 + sext (IR<8:15>) + 100 page zero indirect MA = M[zext (IR<8:15>)] + 101 PC relative indirect MA = M[PC + sext (IR<8:15>)] + 110 AC2 relative indirect MA = M[AC2 + sext (IR<8:15>)] + 111 AC3 relative indirect MA = M[AC3 + sext (IR<8:15>)] + + Memory reference instructions can access an address space of 32K words. + An instruction can directly reference the first 256 words of memory + (called page zero), as well as 256 words relative to the PC, AC2, or + AC3; it can indirectly access all 32K words. If an indirect address + is in locations 00020-00027, the indirect address is incremented and + rewritten to memory before use; if in 00030-00037, decremented and + rewritten. + + The I/O transfer format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0 1 1| AC | opcode |pulse| device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IOT instruction sends the opcode, pulse, and specified AC to the + specified I/O device. The device may accept data, provide data, + initiate or cancel operations, or skip on status. + + The operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1|srcAC|dstAC| opcode |shift|carry|nl| skip | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + \______/ \___/ \___/ | | | | + | | | | | | +--- reverse skip sense + | | | | | +--- skip if C == 0 + | | | | +--- skip if result == 0 + | | | +--- don't load result + | | +--- carry in (load as is, + | | set to Zero, + | | set to One, + | | load Complement) + | +--- shift (none, + | left one, + | right one, + | byte swap) + +--- operation (complement, + negate, + move, + increment, + add complement, + subtract, + add, + and) + + The operate instruction can be microprogrammed to perform operations + on the source and destination AC's and the Carry flag. + + Some notes from Bruce Ray: + + 1. DG uses the value of the autoindex location -before- the + modification to determine if additional indirect address + levels are to be performed. Most DG emulators conform to + this standard, but some vendor machines (i.e. Point 4 Mark 8) + do not. + + 2. Infinite indirect references may occur on unmapped systems + and can "hang" the hardware. Some DG diagnostics perform + 10,000s of references during a single instruction. + + 3. Nova 3 adds the following instructions to the standard Nova + instruction set: + + trap instructions + stack push/pop instructions + save/return instructions + stack register manipulation instructions + unsigned MUL/DIV + + 4. Nova 4 adds the following instructions to the Nova 3 instruction + set: + + signed MUL/DIV + load/store byte + secret (undocumented) stack instructions [PSHN, SAVN] + + 5. Nova, Nova 3 and Nova 4 unsigned mul/div instructions are the + same instruction code values on all machines. + + 6. Undocumented Nova 3 behaviour for LDB, STB and SAVN has been + added to appropriate code. + + 7. Most 3rd party vendors had a user-controlled method to increase the + logical address space from 32 KW to 64 KW. This capability came at + the expense of disabling multi-level indirect addressing when the 64KW + mode is in effect, and keeping DG multi-level indirect compatibility + when 64KW mode is inactive. The most common implementation was to use + an "NIOP ,CPU" instruction to control whether 32 KW or 64 KW + addressing mode was wanted, and bit 15 (the least-significant bit + of an accumulator) determined which mode was set: + 0 = 32 KW (DG compatible), 1 = 64 KW. + + This feature has been implemented in our Nova emulation for all to enjoy. + + + This routine is the instruction decode routine for the NOVA. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + infinite indirection loop + unknown I/O device and STOP_DEV flag set + I/O error in I/O simulator + + 2. Interrupts. Interrupts are maintained by four parallel variables: + + dev_done device done flags + dev_disable device interrupt disable flags + dev_busy device busy flags + int_req interrupt requests + + In addition, int_req contains the interrupt enable and ION pending + flags. If ION and ION pending are set, and at least one interrupt + request is pending, then an interrupt occurs. Note that the 16b PIO + mask must be mapped to the simulator's device bit mapping. + + 3. Non-existent memory. On the NOVA, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 4. Adding I/O devices. These modules must be modified: + + nova_defs.h add interrupt request definition + nova_sys.c add sim_devices entry +*/ + +#include "nova_defs.h" + + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC + + +#define INCA(x) (((x) + 1) & AMASK) +#define DECA(x) (((x) - 1) & AMASK) +#define SEXT(x) (((x) & SIGN)? ((x) | ~DMASK): (x)) +#define STK_CHECK(x,y) if (((x) & 0377) < (y)) int_req = int_req | INT_STK +#define IND_STEP(x) M[x] & A_IND; /* return next level indicator */ \ + if ( ((x) <= AUTO_TOP) && ((x) >= AUTO_INC) ) \ + if ( (x) < AUTO_DEC ) \ + M[x] = (M[x] + 1) & DMASK; \ + else \ + M[x] = (M[x] - 1) & DMASK; \ + x = M[x] & AMASK + +#define INCREMENT_PC PC = (PC + 1) & AMASK /* increment PC */ + +#define UNIT_V_MDV (UNIT_V_UF + 0) /* MDV present */ +#define UNIT_V_STK (UNIT_V_UF + 1) /* stack instr */ +#define UNIT_V_BYT (UNIT_V_UF + 2) /* byte instr */ +#define UNIT_V_64KW (UNIT_V_UF + 3) /* 64KW mem support */ +#define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */ +#define UNIT_MDV (1 << UNIT_V_MDV) +#define UNIT_STK (1 << UNIT_V_STK) +#define UNIT_BYT (1 << UNIT_V_BYT) +#define UNIT_64KW (1 << UNIT_V_64KW) +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define UNIT_IOPT (UNIT_MDV | UNIT_STK | UNIT_BYT | UNIT_64KW) +#define UNIT_NOVA3 (UNIT_MDV | UNIT_STK) +#define UNIT_NOVA4 (UNIT_MDV | UNIT_STK | UNIT_BYT) +#define UNIT_KERONIX (UNIT_MDV | UNIT_64KW) + +#define MODE_64K (cpu_unit.flags & UNIT_64KW) +#define MODE_64K_ACTIVE ((cpu_unit.flags & UNIT_64KW) && (0xFFFF == AMASK)) + + +typedef struct + { + int32 pc; + int16 ir; + int16 ac0 ; + int16 ac1 ; + int16 ac2 ; + int16 ac3 ; + int16 carry ; + int16 sp ; + int16 fp ; + int32 devDone ; + int32 devBusy ; + int32 devDisable ; + int32 devIntr ; + } Hist_entry ; + + +uint16 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AC[4] = { 0 }; /* accumulators */ +int32 C = 0; /* carry flag */ +int32 saved_PC = 0; /* program counter */ +int32 SP = 0; /* stack pointer */ +int32 FP = 0; /* frame pointer */ +int32 SR = 0; /* switch register */ +int32 dev_done = 0; /* device done flags */ +int32 dev_busy = 0; /* device busy flags */ +int32 dev_disable = 0; /* int disable flags */ +int32 int_req = 0; /* interrupt requests */ +int32 pimask = 0; /* priority int mask */ +int32 pwr_low = 0; /* power fail flag */ +int32 ind_max = 65536; /* iadr nest limit */ +int32 stop_dev = 0; /* stop on ill dev */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +struct ndev dev_table[64]; /* dispatch table */ +int32 AMASK = 077777 ; /* current memory address mask */ + /* (default to 32KW) */ +static int32 hist_p = 0 ; /* history pointer */ +static int32 hist_cnt = 0 ; /* history count */ +static Hist_entry * hist = NULL ; /* instruction history */ + + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_boot (int32 unitno, DEVICE *dptr); +t_stat build_devtab (void); + +t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc ) ; +t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc ) ; +static int hist_save( int32 pc, int32 our_ir ) ; +char * devBitNames( int32 flags, char * ptr, char * sepStr ) ; + +void mask_out (int32 mask); + + +extern int32 sim_interval; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern DEVICE * sim_devices[]; +extern t_stat fprint_sym(FILE *ofile, t_addr addr, t_value *val, UNIT *uptr, int32 sw); + + + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { + UDATA (NULL, UNIT_FIX+UNIT_BINK+UNIT_MDV, DFTMEMSIZE /* MAXMEMSIZE */ ) + }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 15) }, + { ORDATA (AC0, AC[0], 16) }, + { ORDATA (AC1, AC[1], 16) }, + { ORDATA (AC2, AC[2], 16) }, + { ORDATA (AC3, AC[3], 16) }, + { FLDATA (C, C, 16) }, + { ORDATA (SP, SP, 16) }, + { ORDATA (FP, FP, 16) }, + { ORDATA (SR, SR, 16) }, + { ORDATA (PI, pimask, 16) }, + { FLDATA (ION, int_req, INT_V_ION) }, + { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, + { FLDATA (STKOVF, int_req, INT_V_STK) }, + { FLDATA (PWR, pwr_low, 0) }, + { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, + { ORDATA (BUSY, dev_busy, INT_V_ION+1), REG_RO }, + { ORDATA (DONE, dev_done, INT_V_ION+1), REG_RO }, + { ORDATA (DISABLE, dev_disable, INT_V_ION+1), REG_RO }, + { FLDATA (STOP_DEV, stop_dev, 0) }, + { DRDATA (INDMAX, ind_max, 32), REG_NZ + PV_LEFT }, + { ORDATA (AMASK, AMASK, 16) }, + { DRDATA (MEMSIZE, cpu_unit.capac, 32), REG_NZ + PV_LEFT }, + { BRDATA (PCQ, pcq, 8, 16, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_IOPT, UNIT_NOVA3, "NOVA3", "NOVA3", NULL }, + { UNIT_IOPT, UNIT_NOVA4, "NOVA4", "NOVA4", NULL }, + { UNIT_IOPT, UNIT_KERONIX, "KERONIX", "KERONIX", NULL }, + { UNIT_IOPT, UNIT_MDV, "MDV", "MDV", NULL }, + { UNIT_IOPT, UNIT_64KW, "EXT64KW", "EXT64KW", NULL }, + { UNIT_IOPT, 0, "none", "NONE", NULL }, + { UNIT_MSIZE, ( 4 * 1024), NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, ( 8 * 1024), NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, (12 * 1024), NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, (16 * 1024), NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, (20 * 1024), NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, (24 * 1024), NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, (28 * 1024), NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, (32 * 1024), NULL, "32K", &cpu_set_size }, + + { UNIT_MSIZE, (36 * 1024), NULL, "36K", &cpu_set_size }, + { UNIT_MSIZE, (40 * 1024), NULL, "40K", &cpu_set_size }, + { UNIT_MSIZE, (44 * 1024), NULL, "44K", &cpu_set_size }, + { UNIT_MSIZE, (48 * 1024), NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, (52 * 1024), NULL, "52K", &cpu_set_size }, + { UNIT_MSIZE, (56 * 1024), NULL, "56K", &cpu_set_size }, + { UNIT_MSIZE, (60 * 1024), NULL, "60K", &cpu_set_size }, + { UNIT_MSIZE, (64 * 1024), NULL, "64K", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &hist_set, &hist_show }, + + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 16 /* = 64 KW, 15 = 32KW */, 1, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL + }; + +t_stat sim_instr (void) +{ +int32 PC, IR, i; +t_stat reason; + +/* Restore register state */ + +if (build_devtab () != SCPE_OK) return SCPE_IERR; /* build dispatch */ +PC = saved_PC & AMASK; /* load local PC */ +C = C & CBIT; +mask_out (pimask); /* reset int system */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + if (sim_interval <= 0) { /* check clock queue */ + if ( (reason = sim_process_event ()) ) break; + } + + if (int_req > INT_PENDING) { /* interrupt or exception? */ + int32 MA, indf; + + if (int_req & INT_TRAP) { /* trap instruction? */ + int_req = int_req & ~INT_TRAP ; /* clear */ + PCQ_ENTRY; /* save old PC */ + M[TRP_SAV] = (PC - 1) & AMASK; + MA = TRP_JMP; /* jmp @47 */ + } + else { + int_req = int_req & ~INT_ION; /* intr off */ + PCQ_ENTRY; /* save old PC */ + M[INT_SAV] = PC; + if (int_req & INT_STK) { /* stack overflow? */ + int_req = int_req & ~INT_STK; /* clear */ + MA = STK_JMP; /* jmp @3 */ + } + else + MA = INT_JMP; /* intr: jmp @1 */ + } + if ( MODE_64K_ACTIVE ) { + indf = IND_STEP (MA); + } + else + { + for (i = 0, indf = 1; indf && (i < ind_max); i++) { + indf = IND_STEP (MA); /* indirect loop */ + } + if (i >= ind_max) { + reason = STOP_IND_INT; + break; + } + } + PC = MA; + } /* end interrupt */ + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + IR = M[PC]; /* fetch instr */ + if ( hist_cnt ) + { + hist_save( PC, IR ) ; /* PC, int_req unchanged */ + } + + INCREMENT_PC ; + int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ + sim_interval = sim_interval - 1; + +/* Operate instruction */ + + if (IR & I_OPR) { /* operate? */ + int32 src, srcAC, dstAC; + + srcAC = I_GETSRC (IR); /* get reg decodes */ + dstAC = I_GETDST (IR); + switch (I_GETCRY (IR)) { /* decode carry */ + case 0: /* load */ + src = AC[srcAC] | C; + break; + case 1: /* clear */ + src = AC[srcAC]; + break; + case 2: /* set */ + src = AC[srcAC] | CBIT; + break; + case 3: /* complement */ + src = AC[srcAC] | (C ^ CBIT); + break; + } /* end switch carry */ + + switch (I_GETALU (IR)) { /* decode ALU */ + case 0: /* COM */ + src = src ^ DMASK; + break; + case 1: /* NEG */ + src = ((src ^ DMASK) + 1) & CDMASK; + break; + case 2: /* MOV */ + break; + case 3: /* INC */ + src = (src + 1) & CDMASK; + break; + case 4: /* ADC */ + src = ((src ^ DMASK) + AC[dstAC]) & CDMASK; + break; + case 5: /* SUB */ + src = ((src ^ DMASK) + AC[dstAC] + 1) & CDMASK; + break; + case 6: /* ADD */ + src = (src + AC[dstAC]) & CDMASK; + break; + case 7: /* AND */ + src = src & (AC[dstAC] | CBIT); + break; + } /* end switch oper */ + + switch (I_GETSHF (IR)) { /* decode shift */ + case 0: /* nop */ + break; + case 1: /* L */ + src = ((src << 1) | (src >> 16)) & CDMASK; + break; + case 2: /* R */ + src = ((src >> 1) | (src << 16)) & CDMASK; + break; + case 3: /* S */ + src = ((src & 0377) << 8) | ((src >> 8) & 0377) | + (src & CBIT); + break; + } /* end switch shift */ + + switch (I_GETSKP (IR)) { /* decode skip */ + case 0: /* nop */ + if ((IR & I_NLD) && (cpu_unit.flags & UNIT_STK)) { + int_req = int_req | INT_TRAP ; /* Nova 3 or 4 trap */ + continue ; + } + break; + case 1: /* SKP */ + INCREMENT_PC ; + break; + case 2: /* SZC */ + if (src < CBIT) INCREMENT_PC ; + break; + case 3: /* SNC */ + if (src >= CBIT) INCREMENT_PC ; + break; + case 4: /* SZR */ + if ((src & DMASK) == 0) INCREMENT_PC ; + break; + case 5: /* SNR */ + if ((src & DMASK) != 0) INCREMENT_PC ; + break; + case 6: /* SEZ */ + if (src <= CBIT) INCREMENT_PC ; + break; + case 7: /* SBN */ + if (src > CBIT) INCREMENT_PC ; + break; + } /* end switch skip */ + if ((IR & I_NLD) == 0) { /* load? */ + AC[dstAC] = src & DMASK; + C = src & CBIT; + } /* end if load */ + } /* end if operate */ + +/* Memory reference instructions */ + + else if (IR < 060000) { /* mem ref? */ + int32 src, MA, indf; + + MA = I_GETDISP (IR); /* get disp */ + switch (I_GETMODE (IR)) { /* decode mode */ + case 0: /* page zero */ + break; + case 1: /* PC relative */ + if (MA & DISPSIGN) MA = 0177400 | MA; + MA = (MA + PC - 1) & AMASK; + break; + case 2: /* AC2 relative */ + if (MA & DISPSIGN) MA = 0177400 | MA; + MA = (MA + AC[2]) & AMASK; + break; + case 3: /* AC3 relative */ + if (MA & DISPSIGN) MA = 0177400 | MA; + MA = (MA + AC[3]) & AMASK; + break; + } /* end switch mode */ + + if ( (indf = IR & I_IND) ) { /* indirect? */ + if ( MODE_64K_ACTIVE ) { /* 64k mode? */ + indf = IND_STEP (MA); + } + else /* compat mode */ + { + for (i = 0; indf && (i < ind_max); i++) { /* count */ + indf = IND_STEP (MA); /* resolve indirect */ + } + if (i >= ind_max) { /* too many? */ + reason = STOP_IND; + break; + } + } + } + + switch (I_GETOPAC (IR)) { /* decode op + AC */ + case 001: /* JSR */ + AC[3] = PC; + case 000: /* JMP */ + PCQ_ENTRY; + PC = MA; + break; + case 002: /* ISZ */ + src = (M[MA] + 1) & DMASK; + if (MEM_ADDR_OK(MA)) M[MA] = src; + if (src == 0) INCREMENT_PC ; + break; + case 003: /* DSZ */ + src = (M[MA] - 1) & DMASK; + if (MEM_ADDR_OK(MA)) M[MA] = src; + if (src == 0) INCREMENT_PC ; + break; + case 004: /* LDA 0 */ + AC[0] = M[MA]; + break; + case 005: /* LDA 1 */ + AC[1] = M[MA]; + break; + case 006: /* LDA 2 */ + AC[2] = M[MA]; + break; + case 007: /* LDA 3 */ + AC[3] = M[MA]; + break; + case 010: /* STA 0 */ + if (MEM_ADDR_OK(MA)) M[MA] = AC[0]; + break; + case 011: /* STA 1 */ + if (MEM_ADDR_OK(MA)) M[MA] = AC[1]; + break; + case 012: /* STA 2 */ + if (MEM_ADDR_OK(MA)) M[MA] = AC[2]; + break; + case 013: /* STA 3 */ + if (MEM_ADDR_OK(MA)) M[MA] = AC[3]; + break; + } /* end switch */ + } /* end mem ref */ + +/* IOT instruction */ + + else { /* IOT */ + int32 dstAC, pulse, code, device, iodata; + + dstAC = I_GETDST (IR); /* decode fields */ + code = I_GETIOT (IR); + pulse = I_GETPULSE (IR); + device = I_GETDEV (IR); + if (code == ioSKP) { /* IO skip? */ + switch (pulse) { /* decode IR<8:9> */ + + case 0: /* skip if busy */ + if ((device == DEV_CPU)? (int_req & INT_ION) != 0: + (dev_busy & dev_table[device].mask) != 0) + INCREMENT_PC ; + break; + + case 1: /* skip if not busy */ + if ((device == DEV_CPU)? (int_req & INT_ION) == 0: + (dev_busy & dev_table[device].mask) == 0) + INCREMENT_PC ; + break; + + case 2: /* skip if done */ + if ((device == DEV_CPU)? pwr_low != 0: + (dev_done & dev_table[device].mask) != 0) + INCREMENT_PC ; + break; + + case 3: /* skip if not done */ + if ((device == DEV_CPU)? pwr_low == 0: + (dev_done & dev_table[device].mask) == 0) + INCREMENT_PC ; + break; + } /* end switch */ + } /* end IO skip */ + + /* Hmm, this means a Nova 3 _must_ have DEV_MDV enabled - not true in DG land */ + + else if (device == DEV_MDV) { + switch (code) { /* case on opcode */ + + case ioNIO: /* frame ptr */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) FP = AC[dstAC] & AMASK ; + if (pulse == iopC) AC[dstAC] = FP & AMASK ; + } + break; + + case ioDIA: /* load byte */ + if (cpu_unit.flags & UNIT_BYT) + { + AC[dstAC] = (M[AC[pulse] >> 1] >> ((AC[pulse] & 1)? 0: 8)) & 0377 ; + } + else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */ + { + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = FP; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); + AC[3] = FP = SP & AMASK; + STK_CHECK (SP, 5); + } + else + { + AC[dstAC] = 0; + } + break; + + case ioDOA: /* stack ptr */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) SP = AC[dstAC] & AMASK; + if (pulse == iopC) AC[dstAC] = SP & AMASK; + } + break; + + case ioDIB: /* push, pop */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) { /* push (PSHA) */ + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC]; + STK_CHECK (SP, 1); + } + if ((pulse == iopS) && /* Nova 4 pshn (PSHN) */ + (cpu_unit.flags & UNIT_BYT)) { + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC]; + if ( (SP & 0xFFFF) > (M[042] & 0xFFFF) ) + { + int_req = int_req | INT_STK ; + } + } + if (pulse == iopC) { /* pop (POPA) */ + AC[dstAC] = M[SP]; + SP = DECA (SP); + } + } + break; + + case ioDOB: /* store byte */ + if (cpu_unit.flags & UNIT_BYT) + { + int32 MA, val; + MA = AC[pulse] >> 1; + val = AC[dstAC] & 0377; + if (MEM_ADDR_OK (MA)) M[MA] = (AC[pulse] & 1)? + ((M[MA] & ~0377) | val) + : ((M[MA] & 0377) | (val << 8)); + } + else if (cpu_unit.flags & UNIT_STK) /* if Nova 3 this is really a SAV... 2007-Jun-01, BKR */ + { + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = FP; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); + AC[3] = FP = SP & AMASK; + STK_CHECK (SP, 5); + } + break; + + case ioDIC: /* save, return */ + if (cpu_unit.flags & UNIT_STK) { + if (pulse == iopN) { /* save */ + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = FP; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); + AC[3] = FP = SP & AMASK; + STK_CHECK (SP, 5); + } + else if (pulse == iopC) { /* retn */ + PCQ_ENTRY; + SP = FP & AMASK; + C = (M[SP] << 1) & CBIT; + PC = M[SP] & AMASK; + SP = DECA (SP); + AC[3] = M[SP]; + SP = DECA (SP); + AC[2] = M[SP]; + SP = DECA (SP); + AC[1] = M[SP]; + SP = DECA (SP); + AC[0] = M[SP]; + SP = DECA (SP); + FP = AC[3] & AMASK; + } + else if ((pulse == iopS) && /* Nova 4 SAVN */ + (cpu_unit.flags & UNIT_BYT)) { + int32 frameSz = M[PC] ; + PC = INCA (PC) ; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[0]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[1]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[2]; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = FP; + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = (C >> 1) | (AC[3] & AMASK); + AC[3] = FP = SP & AMASK ; + SP = (SP + frameSz) & AMASK ; + if (SP > M[042]) + { + int_req = int_req | INT_STK; + } + } + } + break; + + case ioDOC: + if ((dstAC == 2) && (cpu_unit.flags & UNIT_MDV)) + { /* Nova, Nova3 or Nova 4 */ + uint32 mddata, uAC0, uAC1, uAC2; + + uAC0 = (uint32) AC[0]; + uAC1 = (uint32) AC[1]; + uAC2 = (uint32) AC[2]; + if (pulse == iopP) + { /* mul */ + mddata = (uAC1 * uAC2) + uAC0; + AC[0] = (mddata >> 16) & DMASK; + AC[1] = mddata & DMASK; + } + if (pulse == iopS) + { /* div */ + if ((uAC0 >= uAC2) || (uAC2 == 0)) + { + C = CBIT; + } + else + { + C = 0; + mddata = (uAC0 << 16) | uAC1; + AC[1] = mddata / uAC2; + AC[0] = mddata % uAC2; + } + } + } + else if ((dstAC == 3) && (cpu_unit.flags & UNIT_BYT) /* assuming UNIT_BYT = Nova 4 */) + { + int32 mddata; + if (pulse == iopC) + { /* muls */ + mddata = (SEXT (AC[1]) * SEXT (AC[2])) + SEXT (AC[0]); + AC[0] = (mddata >> 16) & DMASK; + AC[1] = mddata & DMASK; + } + else if (pulse == iopN) + { /* divs */ + if ((AC[2] == 0) || /* overflow? */ + ((AC[0] == 0100000) && (AC[1] == 0) && (AC[2] == 0177777))) + { + C = CBIT; + } + else + { + mddata = (SEXT (AC[0]) << 16) | AC[1]; + AC[1] = mddata / SEXT (AC[2]); + AC[0] = mddata % SEXT (AC[2]); + if ((AC[1] > 077777) || (AC[1] < -0100000)) + { + C = CBIT; + } + else + { + C = 0; + } + AC[0] = AC[0] & DMASK; + } + } + } + else if ((dstAC == 3) && (cpu_unit.flags & UNIT_STK)) /* if Nova 3 this is really a PSHA... 2007-Jun-01, BKR */ + { + SP = INCA (SP); + if (MEM_ADDR_OK (SP)) M[SP] = AC[dstAC]; + STK_CHECK (SP, 1); + } + break; + } /* end case code */ + } /* end if mul/div */ + + else if (device == DEV_CPU) { /* CPU control */ + switch (code) { /* decode IR<5:7> */ + + case ioNIO: /* NIOP CPU ? */ + if ( pulse == iopP ) + if ( MODE_64K ) + { + /* Keronix/Point4/SCI/INI/IDP (and others) */ + /* 64 KW memory extension: */ + /* NIOP - set memory mode (32/64 KW) per AC: */ + /* B15: 0 = 32 KW, 1 = 64 KW mode */ + AMASK = (AC[dstAC] & 0x0001) ? 0177777 : 077777 ; + } + break ; + + case ioDIA: /* read switches */ + AC[dstAC] = SR; + break; + + case ioDIB: /* int ack */ + AC[dstAC] = 0; + DEV_UPDATE_INTR ; + iodata = int_req & (-int_req); + for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (iodata & dev_table[i].mask) { + AC[dstAC] = i; + break; + } + } + break; + + case ioDOB: /* mask out */ + mask_out (pimask = AC[dstAC]); + break; + + case ioDIC: /* io reset */ + reset_all (0); /* reset devices */ + mask_out( 0 ) ; /* clear all device masks */ + AMASK = 077777 ; /* reset memory mode */ + break; + + case ioDOC: /* halt */ + reason = STOP_HALT; + break; + } /* end switch code */ + + switch (pulse) { /* decode IR<8:9> */ + + case iopS: /* ion */ + int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; + break; + + case iopC: /* iof */ + int_req = int_req & ~INT_ION; + break; + } /* end switch pulse */ + } /* end CPU control */ + + else if (dev_table[device].routine) { /* normal device */ + iodata = dev_table[device].routine (pulse, code, AC[dstAC]); + reason = iodata >> IOT_V_REASON; + if (code & 1) AC[dstAC] = iodata & 0177777; + } + +/* bkr, 2007-May-30 + * if device does not exist certain I/O instructions will still + * return data: DIA/B/C will return idle data bus value and + * SKPBZ/SKPDZ will sense zero value (and will therefore skip). + * + * Perform these non-supported device functions only if 'stop_dev' + * is zero (i.e. I/O access trap is not in effect). + */ + else if ( stop_dev == 0 ) + { + switch (code) /* decode IR<5:7> */ + { + case ioDIA: + case ioDIB: + case ioDIC: + AC[dstAC] = 0 ; /* idle I/O bus data */ + break; + + case ioSKP: + /* (This should have been caught in previous CPU skip code) */ + if ( (pulse == 1 /* SKPBZ */) || (pulse == 3 /* SKPDZ */) ) + { + INCREMENT_PC ; + } + } /* end of 'switch' */ + } /* end of handling non-existant device */ + else reason = stop_dev; + } /* end if IOT */ + } /* end while */ + +/* Simulation halted */ + +saved_PC = PC; +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return ( reason ) ; +} + +/* New priority mask out */ + +void mask_out (int32 newmask) +{ +int32 i; + +dev_disable = 0; +for (i = DEV_LOW; i <= DEV_HIGH; i++) { + if (newmask & dev_table[i].pi) + dev_disable = dev_disable | dev_table[i].mask; + } +DEV_UPDATE_INTR ; +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int_req = int_req & ~(INT_ION | INT_STK | INT_TRAP); +pimask = 0; +dev_disable = 0; +pwr_low = 0; +AMASK = 077777 ; /* 32KW mode */ +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & DMASK; +return SCPE_OK; +} + +/* Alter memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +t_addr i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* Build dispatch table */ + +t_stat build_devtab (void) +{ +DEVICE *dptr; +DIB *dibp; +int32 i, dn; + +for (i = 0; i < 64; i++) { /* clr dev_table */ + dev_table[i].mask = 0; + dev_table[i].pi = 0; + dev_table[i].routine = NULL; + } +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + if (!(dptr->flags & DEV_DIS) && /* enabled and */ + ( (dibp = (DIB *) dptr->ctxt)) ) { /* defined DIB? */ + dn = dibp->dnum; /* get dev num */ + dev_table[dn].mask = dibp->mask; /* copy entries */ + dev_table[dn].pi = dibp->pi; + dev_table[dn].routine = dibp->routine; + } + } +return SCPE_OK; +} + +/* BKR notes: + * + * Data General APL (Automatic Program Load) boot code + * + * - This bootstrap code is called the "APL option" in DG documentation (Automatic + * Program Load), and cost ~$400 USD (in 1970 - wow!) to load 32(10) words from + * a PROM to main (core) memory location 0 - 32. + * - This code is documented in various DG Nova programming manuals and was + * quite static (i.e. no revisions or updates to code were made). + * - switch register is used to determine device code and device type. + * - lower 6-bits of switch register determines device code (0-63.). + * - most significant bit determines if device is "low speed" or "high speed". + * - "high speed" devices have effective boot program logic of: + * + * IORST + * NIOS + * JMP . + * + * - "high speed" devices use data channel (DCH) to read first sector/record + * of device into memory (usually starting at location 0), which then over-writes + * the 'JMP .' instruction of boot code. This usually has a jump to some other + * device and operating system specific boot code that was loaded from the device. + * - "low speed" devices are assumed to be sequential character-oriented devices + * (i.e. Teletype (r) reader, paper tape reader). + * - "low speed" devices are assumed to start read operations with a 'S' pulse, + * read data buffer with a DIA instruction and have standard DG I/O Busy/Done logic. + * - "low speed" devices usually read in a more full-featured 'binary loader' with + * the APL boot code: + * + * DG paper tape: 091-000004-xx, Binary Loader (BLDR.AB) + * + * - The Binary Loader was in turn used to load tapes in the usual DG 'absolute binary' format. + */ + +#define BOOT_START 00000 +#define BOOT_LEN (sizeof(boot_rom) / sizeof(int32)) + +static const int32 boot_rom[] = { + 0062677, /* IORST ;reset all I/O */ + 0060477, /* READS 0 ;read SR into AC0 */ + 0024026, /* LDA 1,C77 ;get dev mask */ + 0107400, /* AND 0,1 ;isolate dev code */ + 0124000, /* COM 1,1 ;- device code - 1 */ + 0010014, /* LOOP: ISZ OP1 ;device code to all */ + 0010030, /* ISZ OP2 ;I/O instructions */ + 0010032, /* ISZ OP3 */ + 0125404, /* INC 1,1,SZR ;done? */ + 0000005, /* JMP LOOP ;no, increment again */ + 0030016, /* LDA 2,C377 ;place JMP 377 into */ + 0050377, /* STA 2,377 ;location 377 */ + 0060077, /* OP1: 060077 ;start device (NIOS 0) */ + 0101102, /* MOVL 0,0,SZC ;test switch 0, low speed? */ + 0000377, /* C377: JMP 377 ;no - jmp 377 & wait */ + 0004030, /* LOOP2: JSR GET+1 ;get a frame */ + 0101065, /* MOVC 0,0,SNR ;is it non-zero? */ + 0000017, /* JMP LOOP2 ;no, ignore */ + 0004027, /* LOOP4: JSR GET ;yes, get full word */ + 0046026, /* STA 1,@C77 ;store starting at 100 */ + /* ;2's complement of word ct */ + 0010100, /* ISZ 100 ;done? */ + 0000022, /* JMP LOOP4 ;no, get another */ + 0000077, /* C77: JMP 77 ;yes location ctr and */ + /* ;jmp to last word */ + 0126420, /* GET: SUBZ 1,1 ; clr AC1, set carry */ + /* OP2: */ + 0063577, /* LOOP3: 063577 ;done? (SKPDN 0) - 1 */ + 0000030, /* JMP LOOP3 ;no -- wait */ + 0060477, /* OP3: 060477 ;y -- read in ac0 (DIAS 0,0) */ + 0107363, /* ADDCS 0,1,SNC ;add 2 frames swapped - got 2nd? */ + 0000030, /* JMP LOOP3 ;no go back after it */ + 0125300, /* MOVS 1,1 ;yes swap them */ + 0001400, /* JMP 0,3 ;rtn with full word */ + 0000000 /* 0 ;padding */ + }; + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* 1-to-1 map for I/O devices */ + +int32 MapAddr (int32 map, int32 addr) +{ +return addr; +} + +/* History subsystem + +global routines + +t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc, void ** HistCookie, sizeof(usrHistInfo) ) ; +t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc, void * HistCookie ) ; +int hist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) + +local user struct: + +usrHistInfo + +local user routines: + +int uHist_save( int32 next_pc, int32 our_ir, void * usrHistInfo ) ; +int uHist_fprintf( FILE * fp, int itemNum, void * usrHistInfo ) ; + +typedef struct + { + int hMax ; // total # entries in queue (0 = inactive) + int hCount ; // current entry + void * hPtr ; // pointer to save area + int hSize ; // size of each user save area (not used by global routines?) + } Hist_info ; + */ + +/* generalized CPU execution trace */ + +#define HIST_IR_INVALID -1 +#define HIST_MIN 0 /* 0 == deactivate history feature, else size of queue */ +#define HIST_MAX 1000000 /* completely arbitrary max size value */ + +/* save history entry (proposed local routine) */ + +static int hist_save( int32 pc, int32 our_ir ) +{ +Hist_entry * hist_ptr ; + +if ( hist ) + if ( hist_cnt ) + { + hist_p = (hist_p + 1) ; /* next entry */ + if ( hist_p >= hist_cnt ) + { + hist_p = 0 ; + } + hist_ptr = &hist[ hist_p ] ; + + /* (machine-specific stuff) */ + + hist_ptr->pc = pc ; + hist_ptr->ir = our_ir ; + hist_ptr->ac0 = AC[ 0 ] ; + hist_ptr->ac1 = AC[ 1 ] ; + hist_ptr->ac2 = AC[ 2 ] ; + hist_ptr->ac3 = AC[ 3 ] ; + hist_ptr->carry = C ; + hist_ptr->fp = FP ; + hist_ptr->sp = SP ; + hist_ptr->devBusy = dev_busy ; + hist_ptr->devDone = dev_done ; + hist_ptr->devDisable = dev_disable ; + hist_ptr->devIntr = int_req ; + /* how 'bout state and AMASK? */ + return ( hist_p ) ; + } +return ( -1 ) ; +} /* end of 'hist_save' */ + +/* setup history save area (proposed global routine) */ + +t_stat hist_set( UNIT * uptr, int32 val, char * cptr, void * desc ) +{ +int32 i, lnt ; +t_stat r ; + +if ( cptr == NULL ) + { + for (i = 0 ; i < hist_cnt ; ++i ) + { + hist[i].pc = 0 ; + hist[i].ir = HIST_IR_INVALID ; + } + hist_p = 0 ; + return ( SCPE_OK ) ; + } +lnt = (int32) get_uint(cptr, 10, HIST_MAX, &r) ; +if ( (r != SCPE_OK) || (lnt && (lnt < HIST_MIN)) ) + { + return ( SCPE_ARG ) ; + } +hist_p = 0; +if ( hist_cnt ) + { + free( hist ) ; + hist_cnt = 0 ; + hist = NULL ; + } +if ( lnt ) + { + hist = (Hist_entry *) calloc( lnt, sizeof(Hist_entry) ) ; + if ( hist == NULL ) + { + return ( SCPE_MEM ) ; + } + hist_cnt = lnt ; + } +return ( SCPE_OK ) ; +} /* end of 'hist_set' */ + + +int hist_fprintf( FILE * fp, int itemNum, Hist_entry * hptr ) +{ +t_value sim_eval ; + +if ( hptr ) + { + if ( itemNum == 0 ) + { + fprintf( fp, "\n\n" ) ; + } + fprintf( fp, "%05o / %06o %06o %06o %06o %06o %o ", + (hptr->pc & 0x7FFF), + (hptr->ir & 0xFFFF), + (hptr->ac0 & 0xFFFF), + (hptr->ac1 & 0xFFFF), + (hptr->ac2 & 0xFFFF), + (hptr->ac3 & 0xFFFF), + ((hptr->carry) ? 1 : 0) + ) ; + if ( cpu_unit.flags & UNIT_STK /* Nova 3 or Nova 4 */ ) + { + fprintf( fp, "%06o %06o ", SP, FP ) ; + } + + sim_eval = (hptr->ir & 0xFFFF) ; + if ( (fprint_sym(fp, (hptr->pc & AMASK), &sim_eval, &cpu_unit, SWMASK ('M'))) > 0 ) + { + fprintf( fp, "(undefined) %04o", (hptr->ir & 0xFFFF) ) ; + } + /* + display ION flag value, pend value? + display devBusy, devDone, devIntr info? + */ + + if ( 0 ) /* display INTRP codes? */ + { + char tmp[ 500 ] ; + + devBitNames( hptr->devIntr, tmp, NULL ) ; + fprintf( fp, " %s", tmp ) ; + } + + fprintf( fp, "\n" ) ; + } +return ( 0 ) ; +} /* end of 'hist_fprintf' */ + + +/* show execution history (proposed global routine) */ + +t_stat hist_show( FILE * st, UNIT * uptr, int32 val, void * desc ) +{ +int32 k, di, lnt ; +char * cptr = (char *) desc ; +t_stat r ; +Hist_entry * hptr ; + + +if (hist_cnt == 0) + { + return ( SCPE_NOFNC ) ; /* enabled? */ + } +if ( cptr ) + { /* number of entries specified */ + lnt = (int32) get_uint( cptr, 10, hist_cnt, &r ) ; + if ( (r != SCPE_OK) || (lnt == 0) ) + { + return ( SCPE_ARG ) ; + } + } + else + { + lnt = hist_cnt ; /* display all entries */ + } + di = hist_p - lnt; /* work forward */ + if ( di < 0 ) + { + di = di + hist_cnt ; + } + +for ( k = 0 ; k < lnt ; ++k ) + { /* print specified */ + hptr = &hist[ (++di) % hist_cnt] ; /* entry pointer */ + if ( hptr->ir != HIST_IR_INVALID ) /* valid entry? */ + { + hist_fprintf( st, k, hptr ) ; + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} /* end of 'hist_show' */ + + + +struct Dbits + { + int32 dBit ; + int32 dInvertMask ; + char * dName ; + } devBits [] = + + { + { INT_TRAP, 0, "TRAP" }, /* (in order of approximate DG interrupt mask priority) */ + { INT_ION, 0, "ION" }, + { INT_NO_ION_PENDING, 1, "IONPND" }, /* (invert this logic to provide cleaner display) */ + { INT_STK, 0, "STK" }, + { INT_PIT, 0, "PIT" }, + { INT_DKP, 0, "DKP" }, + { INT_DSK, 0, "DSK" }, + { INT_MTA, 0, "MTA" }, + { INT_LPT, 0, "LPT" }, + { INT_PTR, 0, "PTR" }, + { INT_PTP, 0, "PTP" }, + { INT_PLT, 0, "PLT" }, + { INT_CLK, 0, "CLK" }, + { INT_ALM, 0, "ALM" }, + { INT_QTY, 0, "QTY" }, + { INT_TTO1, 0, "TTO1" }, + { INT_TTI1, 0, "TTI1" }, + { INT_TTO, 0, "TTO" }, + { INT_TTI, 0, "TTI" }, + { 0, 0, NULL } + } ; + + +char * devBitNames( int32 flags, char * ptr, char * sepStr ) +{ +int a ; + +if ( ptr ) + { + *ptr = 0 ; + for ( a = 0 ; (devBits[a].dBit) ; ++a ) + if ( devBits[a].dBit & ((devBits[a].dInvertMask)? ~flags : flags) ) + { + if ( *ptr ) + { + strcat( ptr, (sepStr) ? sepStr : " " ) ; + strcat( ptr, devBits[a].dName ) ; + } + else + { + strcpy( ptr, devBits[a].dName ) ; + } + } + } +return ( ptr ) ; +} /* end of 'devBitNames' */ diff --git a/NOVA/nova_defs.h b/NOVA/nova_defs.h new file mode 100644 index 0000000..b5edbd3 --- /dev/null +++ b/NOVA/nova_defs.h @@ -0,0 +1,323 @@ +/* nova_defs.h: NOVA/Eclipse simulator definitions + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 04-Jul-07 BKR BUSY/DONE/INTR "convenience" macros added, + INT_TRAP added for Nova 3, 4 trap instruction handling, + support for 3rd-party 64KW Nova extensions added, + removed STOP_IND_TRP definition due to common INT/TRP handling + 14-Jan-04 BKR Added support for QTY and ALM + 22-Nov-03 CEO Added support for PIT device + 19-Jan-03 RMS Changed CMASK to CDMASK for Apple Dev kit conflict + 03-Oct-02 RMS Added device information structure + 22-Dec-00 RMS Added Bruce Ray's second terminal support + 10-Dec-00 RMS Added Charles Owen's Eclipse support + 08-Dec-00 RMS Added Bruce Ray's plotter support + 15-Oct-00 RMS Added stack, byte, trap instructions + 14-Apr-99 RMS Changed t_addr to unsigned + 16-Mar-95 RMS Added dynamic memory size + 06-Dec-95 RMS Added magnetic tape + + The author gratefully acknowledges the help of Tom West, Diana Englebart, + Carl Friend, Bruce Ray, and Charles Owen in resolving questions about + the NOVA. +*/ + +#ifndef _NOVA_DEFS_H_ +#define _NOVA_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_IND 4 /* indirect loop */ +#define STOP_IND_INT 5 /* ind loop, intr or trap */ + + +/* Memory */ + +#if defined (ECLIPSE) + /*----------------------*/ + /* Eclipse */ + /*----------------------*/ + +#define MAXMEMSIZE 1048576 /* max memory size in 16-bit words */ +#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < (uint32) MEMSIZE) + +#else + /*----------------------*/ + /* Nova */ + /*----------------------*/ + +#define MAXMEMSIZE 65536 /* max memory size in 16-bit words: 32KW = DG max, */ + /* 64 KW = 3rd-party extended memory feature */ +#define DFTMEMSIZE 32768 /* default/initial mem size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < (uint32) MEMSIZE) + +#endif + + +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define A_V_IND 15 /* ind: indirect */ +#define A_IND (1 << A_V_IND) + +/* Architectural constants */ + +#define SIGN 0100000 /* sign */ +#define DMASK 0177777 /* data mask */ +#define CBIT (DMASK + 1) /* carry bit */ +#define CDMASK (CBIT | DMASK) /* carry + data */ + +/* Reserved memory locations */ + +#define INT_SAV 0 /* intr saved PC */ +#define INT_JMP 1 /* intr jmp @ */ +#define STK_JMP 3 /* stack jmp @ */ +#define TRP_SAV 046 /* trap saved PC */ +#define TRP_JMP 047 /* trap jmp @ */ + +#define AUTO_TOP 037 /* top of autoindex */ +#define AUTO_DEC 030 /* start autodec */ +#define AUTO_INC 020 /* start autoinc */ + + +/* Instruction format */ + +#define I_OPR 0100000 /* operate */ +#define I_M_SRC 03 /* OPR: src AC */ +#define I_V_SRC 13 +#define I_GETSRC(x) (((x) >> I_V_SRC) & I_M_SRC) +#define I_M_DST 03 /* dst AC */ +#define I_V_DST 11 +#define I_GETDST(x) (((x) >> I_V_DST) & I_M_DST) +#define I_M_ALU 07 /* OPR: ALU op */ +#define I_V_ALU 8 +#define I_GETALU(x) (((x) >> I_V_ALU) & I_M_ALU) +#define I_M_SHF 03 /* OPR: shift */ +#define I_V_SHF 6 +#define I_GETSHF(x) (((x) >> I_V_SHF) & I_M_SHF) +#define I_M_CRY 03 /* OPR: carry */ +#define I_V_CRY 4 +#define I_GETCRY(x) (((x) >> I_V_CRY) & I_M_CRY) +#define I_V_NLD 3 /* OPR: no load */ +#define I_NLD (1 << I_V_NLD) +#define I_M_SKP 07 /* OPR: skip */ +#define I_V_SKP 0 +#define I_GETSKP(x) (((x) >> I_V_SKP) & I_M_SKP) + +#define I_M_OPAC 017 /* MRF: opcode + AC */ +#define I_V_OPAC 11 +#define I_GETOPAC(x) (((x) >> I_V_OPAC) & I_M_OPAC) +#define I_V_IND 10 /* MRF: indirect */ +#define I_IND (1 << I_V_IND) +#define I_M_MODE 03 /* MRF: mode */ +#define I_V_MODE 8 +#define I_GETMODE(x) (((x) >> I_V_MODE) & I_M_MODE) +#define I_M_DISP 0377 /* MRF: disp */ +#define I_V_DISP 0 +#define I_GETDISP(x) (((x) >> I_V_DISP) & I_M_DISP) +#define DISPSIZE (I_M_DISP + 1) /* page size */ +#define DISPSIGN (DISPSIZE >> 1) /* page sign */ + +#define I_M_IOT 07 /* IOT: code */ +#define I_V_IOT 8 +#define I_GETIOT(x) (((x) >> I_V_IOT) & I_M_IOT) +#define I_M_PULSE 03 /* IOT pulse */ +#define I_V_PULSE 6 +#define I_GETPULSE(x) (((x) >> I_V_PULSE) & I_M_PULSE) +#define I_M_DEV 077 /* IOT: device */ +#define I_V_DEV 0 +#define I_GETDEV(x) (((x) >> I_V_DEV) & I_M_DEV) + +#define I_M_XOP 037 /* XOP: code */ +#define I_V_XOP 6 +#define I_GETXOP(x) (((x) >> I_V_XOP) & I_M_XOP) + +/* IOT return codes */ + +#define IOT_V_REASON 16 /* set reason */ +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* IOT fields */ + +#define ioNIO 0 /* opcode field */ +#define ioDIA 1 +#define ioDOA 2 +#define ioDIB 3 +#define ioDOB 4 +#define ioDIC 5 +#define ioDOC 6 +#define ioSKP 7 + +#define iopN 0 /* pulse field */ +#define iopS 1 +#define iopC 2 +#define iopP 3 + +/* Device numbers */ + +#define DEV_LOW 010 /* lowest intr dev */ +#define DEV_HIGH 051 /* highest intr dev */ +#define DEV_MDV 001 /* multiply/divide */ +#define DEV_ECC 002 /* ECC memory control */ +#define DEV_MAP 003 /* MMPU control */ +#define DEV_TTI 010 /* console input */ +#define DEV_TTO 011 /* console output */ +#define DEV_PTR 012 /* paper tape reader */ +#define DEV_PTP 013 /* paper tape punch */ +#define DEV_CLK 014 /* clock */ +#define DEV_PLT 015 /* plotter */ +#define DEV_CDR 016 /* card reader */ +#define DEV_LPT 017 /* line printer */ +#define DEV_DSK 020 /* fixed head disk */ +#define DEV_MTA 022 /* magtape */ +#define DEV_DCM 024 /* data comm mux */ +#define DEV_ADCV 030 /* A/D converter */ +#define DEV_QTY 030 /* 4060 multiplexor */ +#define DEV_DKP 033 /* disk pack */ +#define DEV_CAS 034 /* cassette */ +#define DEV_ALM 034 /* ALM/ULM multiplexor */ +#define DEV_PIT 043 /* programmable interval timer */ +#define DEV_TTI1 050 /* second console input */ +#define DEV_TTO1 051 /* second console output */ +#define DEV_CPU 077 /* CPU control */ + +/* I/O structure + + The NOVA I/O structure is tied together by dev_table, indexed by + the device number. Each entry in dev_table consists of + + mask device mask for busy, done (simulator representation) + pi pi disable bit (hardware representation) + routine IOT action routine + + dev_table is populated at run time from the device information + blocks in each device. +*/ + +struct ndev { + int32 mask; /* done/busy mask */ + int32 pi; /* assigned pi bit */ + int32 (*routine)(); /* dispatch routine */ + }; + +typedef struct { + int32 dnum; /* device number */ + int32 mask; /* done/busy mask */ + int32 pi; /* assigned pi bit */ + int32 (*routine)(); /* dispatch routine */ + } DIB; + +/* Device flags (simulator representation) + + Priority (for INTA) runs from low numbers to high +*/ + +#define INT_V_PIT 2 /* PIT */ +#define INT_V_DKP 3 /* moving head disk */ +#define INT_V_DSK 4 /* fixed head disk */ +#define INT_V_MTA 5 /* magnetic tape */ +#define INT_V_LPT 6 /* line printer */ +#define INT_V_CLK 7 /* clock */ +#define INT_V_PTR 8 /* paper tape reader */ +#define INT_V_PTP 9 /* paper tape punch */ +#define INT_V_PLT 10 /* plotter */ +#define INT_V_TTI 11 /* keyboard */ +#define INT_V_TTO 12 /* terminal */ +#define INT_V_TTI1 13 /* second keyboard */ +#define INT_V_TTO1 14 /* second terminal */ +#define INT_V_QTY 15 /* QTY multiplexor */ +#define INT_V_ALM 16 /* ALM multiplexor */ +#define INT_V_STK 17 /* stack overflow */ +#define INT_V_NO_ION_PENDING 18 /* ion delay */ +#define INT_V_ION 19 /* interrupts on */ +#define INT_V_TRAP 20 /* trap instruction */ + +#define INT_PIT (1 << INT_V_PIT) +#define INT_DKP (1 << INT_V_DKP) +#define INT_DSK (1 << INT_V_DSK) +#define INT_MTA (1 << INT_V_MTA) +#define INT_LPT (1 << INT_V_LPT) +#define INT_CLK (1 << INT_V_CLK) +#define INT_PTR (1 << INT_V_PTR) +#define INT_PTP (1 << INT_V_PTP) +#define INT_PLT (1 << INT_V_PLT) +#define INT_TTI (1 << INT_V_TTI) +#define INT_TTO (1 << INT_V_TTO) +#define INT_TTI1 (1 << INT_V_TTI1) +#define INT_TTO1 (1 << INT_V_TTO1) +#define INT_QTY (1 << INT_V_QTY) +#define INT_ALM (1 << INT_V_ALM) +#define INT_STK (1 << INT_V_STK) +#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING) +#define INT_ION (1 << INT_V_ION) +#define INT_DEV ((1 << INT_V_STK) - 1) /* device ints */ +#define INT_PENDING INT_ION+INT_NO_ION_PENDING +#define INT_TRAP (1 << INT_V_TRAP) + +/* PI disable bits */ + +#define PI_PIT 0001000 +#define PI_DKP 0000400 +#define PI_DSK 0000100 +#define PI_MTA 0000040 +#define PI_LPT 0000010 +#define PI_CLK 0000004 +#define PI_PTR 0000020 +#define PI_PTP 0000004 +#define PI_PLT 0000010 +#define PI_QTY 0000002 +#define PI_ALM 0000002 +#define PI_TTI 0000002 +#define PI_TTO 0000001 +#define PI_TTI1 PI_TTI +#define PI_TTO1 PI_TTO +/* #define PI_CDR 0000040 */ +/* #define PI_DCM 0100000 */ +/* #define PI_CAS 0000040 */ +/* #define PI_ADCV 0000002 */ + + + /* Macros to clear/set BUSY/DONE/INTR bits */ + +#define DEV_SET_BUSY( x ) dev_busy = dev_busy | (x) +#define DEV_CLR_BUSY( x ) dev_busy = dev_busy & (~(x)) +#define DEV_SET_DONE( x ) dev_done = dev_done | (x) +#define DEV_CLR_DONE( x ) dev_done = dev_done & (~(x)) +#define DEV_UPDATE_INTR int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable) + +#define DEV_IS_BUSY( x ) (dev_busy & (x)) +#define DEV_IS_DONE( x ) (dev_done & (x)) + +/* Function prototypes */ + +int32 MapAddr (int32 map, int32 addr); +t_stat set_enb (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat set_dsb (UNIT *uptr, int32 val, char *cptr, void *desc); + +#endif diff --git a/NOVA/nova_dkp.c b/NOVA/nova_dkp.c new file mode 100644 index 0000000..c38b443 --- /dev/null +++ b/NOVA/nova_dkp.c @@ -0,0 +1,1084 @@ +/* nova_dkp.c: NOVA moving head disk simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dkp moving head disk + + 04-Jul-04 BKR device name changed to DG's DKP from DEC's DP, + DEV_SET/CLR/INTR macro use started, + fixed 'P' pulse code and secret quirks, + added 6097 diag and size support, + fixed losing unit drive type during unit change, + tightened sector size determination calculations, + controller DONE flag handling fixed, + fixed cylinder overflow test error, + seek error code fixed, + restructured dkp_go() and dkp_svc() routines + (for known future fixes needed), + fixed DIA status calculation, + fixed DKP read/write loop to properly emulate DG cylinder and sector overflows, + added trace facility, + changed 'stime' calculation to force delay time if no cylinders are crossed + (this fixes some DG code that assumes disk seek takes some time), + fixed boot code to match DG hardware standard + 04-Jan-04 RMS Changed attach routine to use sim_fsize + 28-Nov-03 CEO Boot from DP now puts device address in SR + 24-Nov-03 CEO Added support for disk sizing on 6099/6103 + 19-Nov-03 CEO Corrected major DMA Mapping bug + 25-Apr-03 RMS Revised autosizing + 08-Oct-02 RMS Added DIB + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Changed FLG, CAPAC to arrays + 26-Apr-01 RMS Added device enable/disable support + 12-Dec-00 RMS Added Eclipse support from Charles Owen + 15-Oct-00 RMS Editorial changes + 14-Apr-99 RMS Changed t_addr to unsigned + 15-Sep-97 RMS Fixed bug in DIB/DOB for new disks + 15-Sep-97 RMS Fixed bug in cylinder extraction (found by Charles Owen) + 10-Sep-97 RMS Fixed bug in error reporting (found by Charles Owen) + 25-Nov-96 RMS Defaulted to autosize + 29-Jun-96 RMS Added unit disable support +*/ + +#include "nova_defs.h" + +#define DKP_NUMDR 4 /* #drives */ +#define DKP_NUMWD 256 /* words/sector */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 017 +#define UNIT_V_AUTO (UNIT_V_UF + 5) /* autosize */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define FUNC u3 /* function */ +#define CYL u4 /* on cylinder */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* Unit, surface, sector, count register + + Original format: 2b, 6b, 4b, 4b + Revised format: 2b, 5b, 5b, 4b +*/ + +#define USSC_V_COUNT 0 /* count */ +#define USSC_M_COUNT 017 +#define USSC_V_OSECTOR 4 /* old: sector */ +#define USSC_M_OSECTOR 017 +#define USSC_V_OSURFACE 8 /* old: surface */ +#define USSC_M_OSURFACE 077 +#define USSC_V_NSECTOR 4 /* new: sector */ +#define USSC_M_NSECTOR 037 +#define USSC_V_NSURFACE 9 /* new: surface */ +#define USSC_M_NSURFACE 037 +#define USSC_V_UNIT 14 /* unit */ +#define USSC_M_UNIT 03 +#define USSC_UNIT (USSC_M_UNIT << USSC_V_UNIT) +#define GET_COUNT(x) (((x) >> USSC_V_COUNT) & USSC_M_COUNT) +#define GET_SECT(x,dt) ((drv_tab[dt].newf)? \ + (((x) >> USSC_V_NSECTOR) & USSC_M_NSECTOR): \ + (((x) >> USSC_V_OSECTOR) & USSC_M_OSECTOR) ) +#define GET_SURF(x,dt) ((drv_tab[dt].newf)? \ + (((x) >> USSC_V_NSURFACE) & USSC_M_NSURFACE): \ + (((x) >> USSC_V_OSURFACE) & USSC_M_OSURFACE) ) +#define GET_UNIT(x) (((x) >> USSC_V_UNIT) & USSC_M_UNIT) + +/* Flags, command, cylinder register + + Original format: 5b, 2b, 1b + 8b (surrounding command) + Revised format: 5b, 2b, 9b +*/ + +#define FCCY_V_OCYL 0 /* old: cylinder */ +#define FCCY_M_OCYL 0377 +#define FCCY_V_OCMD 8 /* old: command */ +#define FCCY_M_OCMD 3 +#define FCCY_V_OCEX 10 /* old: cyl extend */ +#define FCCY_OCEX (1 << FCCY_V_OCEX) +#define FCCY_V_NCYL 0 /* new: cylinder */ +#define FCCY_M_NCYL 0777 +#define FCCY_V_NCMD 9 /* new: command */ +#define FCCY_M_NCMD 3 +#define FCCY_READ 0 +#define FCCY_WRITE 1 +#define FCCY_SEEK 2 +#define FCCY_RECAL 3 +#define FCCY_FLAGS 0174000 /* flags */ + +#define GET_CMD(x,dt) ((drv_tab[dt].newf)? \ + (((x) >> FCCY_V_NCMD) & FCCY_M_NCMD): \ + (((x) >> FCCY_V_OCMD) & FCCY_M_OCMD) ) + +#define SET_CMD(x,dt) dkp_fccy = (dkp_fccy & ((drv_tab[dt].newf)? \ + (FCCY_M_NCMD << FCCY_V_NCMD) : (FCCY_M_OCMD << FCCY_V_OCMD))) | \ + ((drv_tab[dt].newf)? \ + (((x) & FCCY_M_NCMD) << FCCY_V_NCMD): \ + (((x) & FCCY_M_OCMD) << FCCY_V_OCMD) ) + +#define GET_CYL(x,dt) ((drv_tab[dt].newf)? \ + (((x) >> FCCY_V_NCYL) & FCCY_M_NCYL): \ + ((((x) >> FCCY_V_OCYL) & FCCY_M_OCYL) | \ + ((dt != TYPE_D44)? 0: \ + (((x) & FCCY_OCEX) >> (FCCY_V_OCEX - FCCY_V_OCMD)))) ) + + + /* (Warning: no sector or surface masking is done!) */ + +#define DKP_UPDATE_USSC( type, count, surf, sect ) \ + dkp_ussc = (dkp_ussc & USSC_UNIT) \ + | ((dkp_ussc + count) & USSC_M_COUNT) \ + | ((drv_tab[dtype].newf)? \ + ((surf << USSC_V_NSURFACE) | (sect << USSC_V_NSECTOR)): \ + ((surf << USSC_V_OSURFACE) | (sect << USSC_V_OSECTOR)) \ + ); + + +/* Status */ + +#define STA_ERR 0000001 /* error */ +#define STA_DLT 0000002 /* data late */ +#define STA_CRC 0000004 /* crc error */ +#define STA_UNS 0000010 /* unsafe */ +#define STA_XCY 0000020 /* cross cylinder */ +#define STA_CYL 0000040 /* nx cylinder */ +#define STA_DRDY 0000100 /* drive ready */ +#define STA_SEEK3 0000200 /* seeking unit 3 */ +#define STA_SEEK2 0000400 /* seeking unit 2 */ +#define STA_SEEK1 0001000 /* seeking unit 1 */ +#define STA_SEEK0 0002000 /* seeking unit 0 */ +#define STA_SKDN3 0004000 /* seek done unit 3 */ +#define STA_SKDN2 0010000 /* seek done unit 2 */ +#define STA_SKDN1 0020000 /* seek done unit 1 */ +#define STA_SKDN0 0040000 /* seek done unit 0 */ +#define STA_DONE 0100000 /* operation done */ + +#define STA_DYN (STA_DRDY | STA_CYL) /* set from unit */ +#define STA_EFLGS (STA_ERR | STA_DLT | STA_CRC | STA_UNS | \ + STA_XCY | STA_CYL) /* error flags */ +#define STA_DFLGS (STA_DONE | STA_SKDN0 | STA_SKDN1 | \ + STA_SKDN2 | STA_SKDN3) /* done flags */ + +#define GET_SA(cy,sf,sc,t) (((((cy)*drv_tab[t].surf)+(sf))* \ + drv_tab[t].sect)+(sc)) + +/* This controller supports many different disk drive types: + + type #sectors/ #surfaces/ #cylinders/ new format? + surface cylinder drive + + floppy 8 1 77 no + DS/DD floppy 16 2 77 yes + (6097 "quad floppy") + Diablo 31 12 2 203 no + 6225 20 2 245 yes + Century 111 6 10 203 no + 4048 (same as Century 111) + Diablo 44 12 4 408 no + 6099 32 4 192 yes + 6227 20 6 245 yes + 6070 24 4 408 yes + Century 114 12 20 203 no + 4057 (same as Century 114) + 6103 32 8 192 yes + 4231 23 19 411 yes + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. +*/ + +#define TYPE_FLP 0 +#define SECT_FLP 8 +#define SURF_FLP 1 +#define CYL_FLP 77 +#define SIZE_FLP (SECT_FLP * SURF_FLP * CYL_FLP * DKP_NUMWD) +#define NFMT_FLP FALSE + +#define TYPE_DSDD 1 +#define TYPE_6097 TYPE_DSDD +#define SECT_DSDD 16 +#define SURF_DSDD 2 +#define CYL_DSDD 77 +#define SIZE_DSDD (SECT_DSDD * SURF_DSDD * CYL_DSDD * DKP_NUMWD) +#define NFMT_DSDD TRUE + +#define TYPE_D31 2 +#define SECT_D31 12 +#define SURF_D31 2 +#define CYL_D31 203 +#define SIZE_D31 (SECT_D31 * SURF_D31 * CYL_D31 * DKP_NUMWD) +#define NFMT_D31 FALSE + +#define TYPE_6225 3 +#define SECT_6225 20 +#define SURF_6225 2 +#define CYL_6225 245 +#define SIZE_6225 (SECT_6225 * SURF_6225 * CYL_6225 * DKP_NUMWD) +#define NFMT_6225 TRUE + +#define TYPE_C111 4 +#define SECT_C111 6 +#define SURF_C111 10 +#define CYL_C111 203 +#define SIZE_C111 (SECT_C111 * SURF_C111 * CYL_C111 * DKP_NUMWD) +#define NFMT_C111 FALSE + +#define TYPE_D44 5 +#define SECT_D44 12 +#define SURF_D44 4 +#define CYL_D44 408 +#define SIZE_D44 (SECT_D44 * SURF_D44 * CYL_D44 * DKP_NUMWD) +#define NFMT_D44 FALSE + +#define TYPE_6099 6 +#define SECT_6099 32 +#define SURF_6099 4 +#define CYL_6099 192 +#define SIZE_6099 (SECT_6099 * SURF_6099 * CYL_6099 * DKP_NUMWD) +#define NFMT_6099 TRUE + +#define TYPE_6227 7 +#define SECT_6227 20 +#define SURF_6227 6 +#define CYL_6227 245 +#define SIZE_6227 (SECT_6227 * SURF_6227 * CYL_6227 * DKP_NUMWD) +#define NFMT_6227 TRUE + +#define TYPE_6070 8 +#define SECT_6070 24 +#define SURF_6070 4 +#define CYL_6070 408 +#define SIZE_6070 (SECT_6070 * SURF_6070 * CYL_6070 * DKP_NUMWD) +#define NFMT_6070 TRUE + +#define TYPE_C114 9 +#define SECT_C114 12 +#define SURF_C114 20 +#define CYL_C114 203 +#define SIZE_C114 (SECT_C114 * SURF_C114 * CYL_C114 * DKP_NUMWD) +#define NFMT_C114 FALSE + +#define TYPE_6103 10 +#define SECT_6103 32 +#define SURF_6103 8 +#define CYL_6103 192 +#define SIZE_6103 (SECT_6103 * SURF_6103 * CYL_6103 * DKP_NUMWD) +#define NFMT_6103 TRUE + +#define TYPE_4231 11 +#define SECT_4231 23 +#define SURF_4231 19 +#define CYL_4231 411 +#define SIZE_4231 (SECT_4231 * SURF_4231 * CYL_4231 * DKP_NUMWD) +#define NFMT_4231 TRUE + +struct drvtyp { + int32 sect; /* sectors */ + int32 surf; /* surfaces */ + int32 cyl; /* cylinders */ + int32 size; /* #blocks */ + int32 newf; /* new format flag */ + }; + +struct drvtyp drv_tab[] = { + { SECT_FLP, SURF_FLP, CYL_FLP, SIZE_FLP, NFMT_FLP }, + { SECT_DSDD, SURF_DSDD, CYL_DSDD, SIZE_DSDD, NFMT_DSDD }, + { SECT_D31, SURF_D31, CYL_D31, SIZE_D31, NFMT_D31 }, + { SECT_6225, SURF_6225, CYL_6225, SIZE_6225, NFMT_6225 }, + { SECT_C111, SURF_C111, CYL_C111, SIZE_C111, NFMT_C111 }, + { SECT_D44, SURF_D44, CYL_D44, SIZE_D44, NFMT_D44 }, + { SECT_6099, SURF_6099, CYL_6099, SIZE_6099, NFMT_6099 }, + { SECT_6227, SURF_6227, CYL_6227, SIZE_6227, NFMT_6227 }, + { SECT_6070, SURF_6070, CYL_6070, SIZE_6070, NFMT_6070 }, + { SECT_C114, SURF_C114, CYL_C114, SIZE_C114, NFMT_C114 }, + { SECT_6103, SURF_6103, CYL_6103, SIZE_6103, NFMT_6103 }, + { SECT_4231, SURF_4231, CYL_4231, SIZE_4231, NFMT_4231 }, + { 0 } + }; + +#define DKP_TRACE(x) (dkp_trace & (1<<(x))) +#define DKP_TRACE_FP stderr +/* current trace bit use (bit 0 = LSB) + 0 I/O instructions + 1 pre-seek/read/write event setup + 2 seek events + 3 read/write events + 4 post read/write events + */ + +extern uint16 M[]; +extern UNIT cpu_unit; +extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 saved_PC, SR, AMASK; + +int32 dkp_ma = 0; /* memory address */ +int32 dkp_map = 0; /* DCH map 0=A 3=B */ +int32 dkp_ussc = 0; /* unit/sf/sc/cnt */ +int32 dkp_fccy = 0; /* flags/cylinder */ +int32 dkp_sta = 0; /* status register */ +int32 dkp_swait = 100; /* seek latency */ +int32 dkp_rwait = 100; /* rotate latency */ +int32 dkp_diagmode = 0; /* diagnostic mode */ + +int32 dkp_trace = 0 ; + +DEVICE dkp_dev; +int32 dkp (int32 pulse, int32 code, int32 AC); +t_stat dkp_svc (UNIT *uptr); +t_stat dkp_reset (DEVICE *dptr); +t_stat dkp_boot (int32 unitno, DEVICE *dptr); +t_stat dkp_attach (UNIT *uptr, char *cptr); +t_stat dkp_go ( int32 pulse ); +t_stat dkp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* DKP data structures + + dkp_dev DKP device descriptor + dkp_unit DKP unit list + dkp_reg DKP register list + dkp_mod DKP modifier list +*/ + +DIB dkp_dib = { DEV_DKP, INT_DKP, PI_DKP, &dkp }; + +UNIT dkp_unit[] = { + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) }, + { UDATA (&dkp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(TYPE_D31 << UNIT_V_DTYPE), SIZE_D31) } + }; + +REG dkp_reg[] = { + { ORDATA (FCCY, dkp_fccy, 16) }, + { ORDATA (USSC, dkp_ussc, 16) }, + { ORDATA (STA, dkp_sta, 16) }, + { ORDATA (MA, dkp_ma, 16) }, + { FLDATA (INT, int_req, INT_V_DKP) }, + { FLDATA (BUSY, dev_busy, INT_V_DKP) }, + { FLDATA (DONE, dev_done, INT_V_DKP) }, + { FLDATA (DISABLE, dev_disable, INT_V_DKP) }, + { FLDATA (DIAG, dkp_diagmode, 0) }, + { DRDATA (TRACE, dkp_trace, 32) }, + { ORDATA (MAP, dkp_map, 2) }, + { DRDATA (STIME, dkp_swait, 24), PV_LEFT }, + { DRDATA (RTIME, dkp_rwait, 24), PV_LEFT }, + { URDATA (CAPAC, dkp_unit[0].capac, 10, T_ADDR_W, 0, + DKP_NUMDR, PV_LEFT | REG_HRO) }, + { NULL } + }; + +MTAB dkp_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE) + UNIT_ATT, + "6030 (floppy)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE) + UNIT_ATT, + "6097 (DS/DD floppy)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE) + UNIT_ATT, + "4047 (Diablo 31)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE) + UNIT_ATT, + "4234/6045 (Diablo 44)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE) + UNIT_ATT, + "4048 (Century 111)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE) + UNIT_ATT, + "2314/4057 (Century 114)", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE) + UNIT_ATT, + "6225", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE) + UNIT_ATT, + "6227", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE) + UNIT_ATT, + "6099", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE) + UNIT_ATT, + "6103", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE) + UNIT_ATT, + "6070", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE) + UNIT_ATT, + "4231/3330", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_FLP << UNIT_V_DTYPE), + "6030 (floppy)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_DSDD << UNIT_V_DTYPE), + "6097 (DS/DD floppy)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D31 << UNIT_V_DTYPE), + "4047 (Diablo 31)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_D44 << UNIT_V_DTYPE), + "4234/6045 (Diablo 44)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C111 << UNIT_V_DTYPE), + "4048 (Century 111)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_C114 << UNIT_V_DTYPE), + "2314/4057 (Century 114)", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6225 << UNIT_V_DTYPE), + "6225", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6227 << UNIT_V_DTYPE), + "6227", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6099 << UNIT_V_DTYPE), + "6099", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6103 << UNIT_V_DTYPE), + "6103", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_6070 << UNIT_V_DTYPE), + "6070", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (TYPE_4231 << UNIT_V_DTYPE), + "4231/3330", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE), + NULL, "FLOPPY", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_FLP << UNIT_V_DTYPE), + NULL, "6030", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE), + NULL, "DSDDFLOPPY", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_DSDD << UNIT_V_DTYPE), + NULL, "6097", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE), + NULL, "D31", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D31 << UNIT_V_DTYPE), + NULL, "4047", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), + NULL, "D44", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), + NULL, "4234", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_D44 << UNIT_V_DTYPE), + NULL, "6045", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE), + NULL, "C111", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C111 << UNIT_V_DTYPE), + NULL, "4048", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), + NULL, "C114", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), + NULL, "2314", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_C114 << UNIT_V_DTYPE), + NULL, "4057", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6225 << UNIT_V_DTYPE), + NULL, "6225", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6227 << UNIT_V_DTYPE), + NULL, "6227", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6099 << UNIT_V_DTYPE), + NULL, "6099", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6103 << UNIT_V_DTYPE), + NULL, "6103", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_6070 << UNIT_V_DTYPE), + NULL, "6070", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE), + NULL, "4231", &dkp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (TYPE_4231 << UNIT_V_DTYPE), + NULL, "3330", &dkp_set_size }, + { 0 } + }; + +DEVICE dkp_dev = { + "DKP", dkp_unit, dkp_reg, dkp_mod, + DKP_NUMDR, 8, 30, 1, 8, 16, + NULL, NULL, &dkp_reset, + &dkp_boot, &dkp_attach, NULL, + &dkp_dib, DEV_DISABLE + }; + + +/* IOT routine */ + +int32 dkp (int32 pulse, int32 code, int32 AC) +{ +UNIT *uptr; +int32 u, rval, dtype; + +rval = 0; +uptr = dkp_dev.units + GET_UNIT (dkp_ussc); /* select unit */ +dtype = GET_DTYPE (uptr->flags); /* get drive type */ + +if ( DKP_TRACE(0) ) + { + static char * f[8] = + { "NIO", "DIA", "DOA", "DIB", "DOB", "DIC", "DOC", "SKP" } ; + static char * s[4] = + { " ", "S", "C", "P" } ; + + printf( " [DKP %s%s %06o ", f[code & 0x07], s[pulse & 0x03], (AC & 0xFFFF) ) ; + } + +switch (code) { /* decode IR<5:7> */ + + case ioDIA: /* DIA */ + dkp_sta = dkp_sta & (~STA_DRDY) ; /* keep error flags */ + if (uptr->flags & UNIT_ATT) /* update ready */ + dkp_sta = dkp_sta | STA_DRDY; + if (uptr->CYL >= drv_tab[dtype].cyl) + dkp_sta = dkp_sta | STA_CYL; /* bad cylinder? */ + if (dkp_sta & STA_EFLGS) + dkp_sta = dkp_sta | STA_ERR; + rval = dkp_sta; + break; + + case ioDOA: /* DOA */ + if (AC & 0100000) /* clear rw done? */ + dkp_sta = dkp_sta & ~(STA_CYL|STA_XCY|STA_UNS|STA_CRC); + if ((dev_busy & INT_DKP) == 0) { + dkp_fccy = AC; /* save cmd, cyl */ + dkp_sta = dkp_sta & ~(AC & FCCY_FLAGS); + } + DEV_CLR_DONE( INT_DKP ) ; /* assume done flags 0 */ + if ( dkp_sta & STA_DFLGS ) /* done flags = 0? */ + DEV_SET_DONE( INT_DKP ) ; /* nope - set done */ + DEV_UPDATE_INTR ; /* update intr */ + break; + + case ioDIB: /* DIB */ + rval = dkp_ma & 077777 ; /* return buf addr */ + /* with B0 clear (no DCH B map support) */ + break; + + case ioDOB: /* DOB */ + if ((dev_busy & INT_DKP) == 0) { + dkp_ma = AC & (drv_tab[dtype].newf? DMASK: AMASK); + if (AC & 0100000) + dkp_map = 3; /* high bit is map */ + else + dkp_map = 0; + } + break; + + case ioDIC: /* DIC */ + rval = dkp_ussc; /* return unit, sect */ + break; + + case ioDOC: /* DOC */ + if ((dev_busy & INT_DKP) == 0) /* if device is not busy */ + dkp_ussc = AC ; /* save unit, sect */ + if (((dtype == TYPE_6099) || /* (BKR: don't forget 6097) */ + (dtype == TYPE_6097) || /* for 6099 and 6103 */ + (dtype == TYPE_6103)) && /* if data<0> set, */ + (AC & 010000) ) + dkp_diagmode = 1; /* set diagnostic mode */ + break; + } /* end switch code */ + +u = GET_UNIT(dkp_ussc); /* update current unit */ +uptr = dkp_dev.units + u ; /* select unit */ +dtype = GET_DTYPE (uptr->flags); /* get drive type */ + +if ( DKP_TRACE(0) ) + { + if ( code & 1 ) + printf( " [%06o] ", (rval & 0xFFFF) ) ; + printf( "] \n" ) ; + } + +switch (pulse) { /* decode IR<8:9> */ + + case iopS: /* start */ + DEV_SET_BUSY( INT_DKP ) ; /* set busy */ + DEV_CLR_DONE( INT_DKP ) ; /* clear done */ + DEV_UPDATE_INTR ; /* update ints */ + if (dkp_diagmode) { /* in diagnostic mode? */ + dkp_diagmode = 0; /* reset it */ + if (dtype == TYPE_6097) dkp_ussc = 010001; /* (BKR - quad floppy) */ + if (dtype == TYPE_6099) dkp_ussc = 010002; /* return size bits */ + if (dtype == TYPE_6103) dkp_ussc = 010003; /* for certain types */ + } + else { /* normal mode ... */ + if (dkp_go (pulse)) /* do command */ + break ; /* break if no error */ + } + DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ + DEV_SET_DONE( INT_DKP ) ; /* set done */ + DEV_UPDATE_INTR ; /* update ints */ + dkp_sta = dkp_sta | STA_DONE; /* set controller done */ + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ + DEV_CLR_DONE( INT_DKP ) ; /* set done */ + DEV_UPDATE_INTR ; /* update ints */ + dkp_sta = dkp_sta & ~(STA_DFLGS + STA_EFLGS); /* clear controller flags */ + if (dkp_unit[u].FUNC != FCCY_SEEK) + sim_cancel (&dkp_unit[u]); /* cancel any r/w op */ + break; + + case iopP: /* pulse */ + if ( dkp_diagmode ) + { + dkp_diagmode = 0 ; /* clear DG diagnostic mode */ + } + else + { + DEV_CLR_DONE( INT_DKP ) ; /* clear done */ + DEV_UPDATE_INTR ; + + /* DG "undocumented feature": 'P' pulse can not start a read/write operation! + * Diagnostic routines will use this crock to do 'crazy things' to size a disk + * and many assume that a recal is done, other assume that they can stop the + * read operation before any damage is done. Must also [re]calculate unit, function + * and type because DOx instruction may have updated the controller info after + * start of this procedure and before our 'P' handler. BKR + */ + if (dkp_go(pulse)) + break; /* no error - do not set done and status */ + } + + DEV_SET_DONE( INT_DKP ) ; /* set done */ + DEV_UPDATE_INTR ; /* update ints */ + dkp_sta = dkp_sta | (STA_SKDN0 >> u); /* set controller seek done */ + break; + } /* end case pulse */ + +return rval; +} + + +/* New command, start vs pulse handled externally + Returns true if command ok, false if error +*/ + +t_stat dkp_go ( int32 pulse ) +{ +UNIT * uptr; +int32 oldCyl, u, dtype; + +dkp_sta = dkp_sta & ~STA_EFLGS; /* clear errors */ +u = GET_UNIT (dkp_ussc); /* get unit number */ +uptr = dkp_dev.units + u; /* get unit */ +if (((uptr->flags & UNIT_ATT) == 0) || sim_is_active (uptr)) { + dkp_sta = dkp_sta | STA_ERR; /* attached or busy? */ + return FALSE; + } + +if (dkp_diagmode) { /* diagnostic mode? */ + dkp_sta = (dkp_sta | STA_DONE); /* Set error bit only */ + DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ + DEV_SET_DONE( INT_DKP ) ; /* set done */ + DEV_UPDATE_INTR ; /* update interrupts */ + return ( TRUE ) ; /* do not do function */ + } + +oldCyl = uptr->CYL ; /* get old cylinder */ +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +uptr->FUNC = GET_CMD (dkp_fccy, dtype) ; /* save command */ +uptr->CYL = GET_CYL (dkp_fccy, dtype) ; + +if ( DKP_TRACE(1) ) + { + int32 xSect ; + int32 xSurf ; + int32 xCyl ; + int32 xCnt ; + + xSect = GET_SECT(dkp_ussc, dtype) ; + xSurf = GET_SURF(dkp_ussc, dtype) ; + xCyl = GET_CYL (dkp_fccy, dtype) ; + xCnt = 16 - (GET_COUNT(dkp_ussc)) ; + + fprintf( DKP_TRACE_FP, + " [%s:%c %-5s: %3d / %2d / %2d %2d %06o ] \r\n", + "DKP", + (char) (u + '0'), + ((uptr->FUNC == FCCY_READ) ? + "read" + : ((uptr->FUNC == FCCY_WRITE) ? + "write" + : ((uptr->FUNC == FCCY_SEEK) ? + "seek" + : "" + ) + ) + ), + (unsigned) xCyl, + (unsigned) xSurf, + (unsigned) xSect, + (unsigned) (16 - xCnt), + (unsigned) (dkp_ma & 0xFFFF) /* show all 16-bits in case DCH B */ + ) ; + } + + +switch (uptr->FUNC) { /* decode command */ + + case FCCY_READ: + case FCCY_WRITE: + if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ + ((uptr->flags & UNIT_WPRT) && (uptr->FUNC == FCCY_WRITE))) + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */ + } + else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder */ + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_CYL ; + } + else if ( GET_SURF(dkp_ussc, dtype) >= drv_tab[dtype].surf ) /* bad surface */ + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /* older drives may not even do this... */ + /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /- newer disks give this error */ + } + else if ( GET_SECT(dkp_ussc, dtype) >= drv_tab[dtype].sect ) /* or bad sector? */ + { + /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /- older drives may not even do this... */ + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /* newer disks give this error */ + } + if ( (pulse != iopS) || (dkp_sta & STA_ERR) ) + { + return ( FALSE ) ; + } + sim_activate (uptr, dkp_rwait); /* schedule read or write request */ + break; + + case FCCY_RECAL: /* recalibrate */ + uptr->FUNC = FCCY_SEEK ; /* save command */ + uptr->CYL = 0 ; + + case FCCY_SEEK: /* seek */ + if ( ! (uptr->flags & UNIT_ATT) ) /* not attached? */ + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */ + } + else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder? */ + { + dkp_sta = dkp_sta | STA_ERR | STA_CYL; + } + if ( (pulse != iopP) || (dkp_sta & STA_ERR) ) + { + return ( FALSE ) ; /* only 'P' pulse start seeks! */ + } + + /* do the seek */ + /* must check for "do we support seeking bits" flag before setting SEEK0'ish bits! */ + dkp_sta = dkp_sta | (STA_SEEK0 >> u); /* set seeking */ + oldCyl = abs(oldCyl - uptr->CYL) ; + if ( (dkp_swait) && (! (oldCyl)) ) /* enforce minimum wait if req */ + oldCyl = 1 ; + sim_activate ( uptr, (dkp_swait * oldCyl) ) ; + break; + } /* end case command */ + +return ( TRUE ) ; /* no error */ +} + + +/* Unit service + + If seek done, put on cylinder; + else, do read or write + If controller was busy, clear busy, set done, interrupt + + Memory access: sectors are read into/written from an intermediate + buffer to allow word-by-word mapping of memory addresses on the + Eclipse. This allows each word written to memory to be tested + for out of range. +*/ + +t_stat dkp_svc (UNIT *uptr) +{ +int32 sa, bda; +int32 dx, pa, u; +int32 dtype, err, newsect, newsurf; +uint32 awc; +t_stat rval; +static uint16 tbuf[DKP_NUMWD]; /* transfer buffer */ + + +rval = SCPE_OK; +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +u = uptr - dkp_dev.units; /* get unit number */ + +if (uptr->FUNC == FCCY_SEEK) { /* seek? */ + if ( ! (uptr->flags & UNIT_ATT) ) /* not attached? */ + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error (changed during queue time?) */ + } + else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder? */ + { + dkp_sta = dkp_sta | STA_ERR | STA_CYL; + } + DEV_SET_DONE( INT_DKP ) ; + DEV_UPDATE_INTR ; + dkp_sta = (dkp_sta | (STA_SKDN0 >> u)) /* set seek done */ + & ~(STA_SEEK0 >> u); /* clear seeking */ + if ( DKP_TRACE(2) ) + { + fprintf( DKP_TRACE_FP, + " [%s:%c seek : %4d ] \r\n", + "DKP", + (char) (u + '0'), + (unsigned) (uptr->CYL) + ) ; + } + return SCPE_OK; + } + +/* read or write */ + +if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ + ((uptr->flags & UNIT_WPRT) && (uptr->FUNC == FCCY_WRITE))) + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR; /* error */ + } +else if ( uptr->CYL >= drv_tab[dtype].cyl ) /* bad cylinder */ + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_CYL ; + dkp_sta = dkp_sta | STA_ERR | STA_CYL; + DEV_SET_DONE( INT_DKP ) ; + DEV_UPDATE_INTR ; + return SCPE_OK ; + } +else if ( GET_SURF(dkp_ussc, dtype) >= drv_tab[dtype].surf ) /* bad surface */ + { + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /* older drives may not even do this... */ +/* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /- newer disks give this error */ +/* set sector to some bad value and wait then exit? */ + } +else if ( GET_SECT(dkp_ussc, dtype) >= drv_tab[dtype].sect ) /* or bad sector? */ + { +/* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /- older DG drives do not even give error(!), but we do */ + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /* newer disks give this error */ + } +else { +err = 0 ; +do { + if ( DKP_TRACE(3) ) + { + fprintf( DKP_TRACE_FP, + " [%s:%c %-5s: %3d / %2d / %2d %06o ] \r\n", + "DKP", + (char) (u + '0'), + ((uptr->FUNC == FCCY_READ) ? + "read" + : ((uptr->FUNC == FCCY_WRITE) ? + "write" + : "") + ), + (unsigned) (uptr->CYL), + (unsigned) (GET_SURF(dkp_ussc, dtype)), + (unsigned) (GET_SECT(dkp_ussc, dtype)), + (unsigned) (dkp_ma & 0xFFFF) /* show all 16-bits in case DCH B */ + ) ; + } + + + if ( GET_SECT(dkp_ussc, dtype) >= drv_tab[dtype].sect ) /* or bad sector? */ + { + /* sector overflows to 0 ; + * surface gets incremented + */ + newsurf = GET_SURF(dkp_ussc, dtype) + 1 ; + newsurf = newsurf & ((drv_tab[dtype].newf) ? USSC_M_NSURFACE : USSC_M_OSURFACE) ; + DKP_UPDATE_USSC( type, 0, newsurf, 0 ) + + if ( (GET_SURF(dkp_ussc, dtype)) >= drv_tab[dtype].surf ) + { + /* dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_UNS; /- older drives may not even do this... */ + dkp_sta = dkp_sta | STA_DONE | STA_ERR | STA_XCY ; /* newer disks give this error */ + /* DG retains overflowed surface number, + * other vendors have different/expanded options + */ + break ; + } + } + sa = GET_SA (uptr->CYL, GET_SURF (dkp_ussc, dtype), + GET_SECT (dkp_ussc, dtype), dtype); /* get disk block */ + bda = sa * DKP_NUMWD * sizeof(uint16) ; /* to words, bytes */ + err = fseek (uptr->fileref, bda, SEEK_SET); /* position drive */ + + if (uptr->FUNC == FCCY_READ) { /* read? */ + awc = fxread (tbuf, sizeof(uint16), DKP_NUMWD, uptr->fileref); + for ( ; awc < DKP_NUMWD; awc++) tbuf[awc] = 0; + if (err = ferror (uptr->fileref)) break; + for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop thru buffer */ + pa = MapAddr (dkp_map, (dkp_ma & AMASK)); + if (MEM_ADDR_OK (pa)) M[pa] = tbuf[dx]; + dkp_ma = (dkp_ma + 1) & AMASK; + } + } + else if (uptr->FUNC == FCCY_WRITE) { /* write? */ + for (dx = 0; dx < DKP_NUMWD; dx++) { /* loop into buffer */ + pa = MapAddr (dkp_map, (dkp_ma & AMASK)); + tbuf[dx] = M[pa]; + dkp_ma = (dkp_ma + 1) & AMASK; + } + fxwrite (tbuf, sizeof(int16), DKP_NUMWD, uptr->fileref); + if (err = ferror (uptr->fileref)) break; + } + + if (err != 0) { + perror ("DKP I/O error"); + clearerr (uptr->fileref); + rval = SCPE_IOERR; + break ; + } + +newsect = GET_SECT (dkp_ussc, dtype) + 1 ; /* update next sector */ +newsurf = GET_SURF (dkp_ussc, dtype) ; /* and next head */ + /* (count set below) */ +DKP_UPDATE_USSC( type, 1, newsurf, newsect ) +} /* end read/write loop */ + + while ( (GET_COUNT(dkp_ussc)) ) ; + dkp_sta = dkp_sta | STA_DONE; /* set status */ + + if ( DKP_TRACE(4) ) + { + fprintf( DKP_TRACE_FP, + " [%s:%c %-5s: %3d / %2d / %2d %06o ] \r\n", + "DKP", + (char) (u + '0'), + "post", + (unsigned) (uptr->CYL), + (unsigned) (GET_SURF(dkp_ussc, dtype)), + (unsigned) (GET_SECT(dkp_ussc, dtype)), + (unsigned) (dkp_ma & 0xFFFF) /* show all 16-bits in case DCH B */ + ) ; + } + } + +DEV_CLR_BUSY( INT_DKP ) ; +DEV_SET_DONE( INT_DKP ) ; +DEV_UPDATE_INTR ; +return rval; +} + +/* Reset routine */ + +t_stat dkp_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +DEV_CLR_BUSY( INT_DKP ) ; /* clear busy */ +DEV_CLR_DONE( INT_DKP ) ; /* clear done */ +DEV_UPDATE_INTR ; /* update ints */ +dkp_fccy = dkp_ussc = dkp_ma = dkp_sta = 0; /* clear registers */ +dkp_diagmode = 0; /* clear diagnostic mode */ +dkp_map = 0; +for (u = 0; u < DKP_NUMDR; u++) { /* loop thru units */ + uptr = dkp_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + uptr->CYL = uptr->FUNC = 0; + } +return SCPE_OK; +} + +/* Attach routine (with optional autosizing) */ + +t_stat dkp_attach (UNIT *uptr, char *cptr) +{ +int32 i, p; +t_stat r; + +uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; /* restore capac */ +r = attach_unit (uptr, cptr); /* attach */ +if ((r != SCPE_OK) || !(uptr->flags & UNIT_AUTO)) return r; +if ((p = sim_fsize (uptr->fileref)) == 0) return SCPE_OK; /* get file size */ +for (i = 0; drv_tab[i].sect != 0; i++) { + if (p <= (drv_tab[i].size * (int32) sizeof (uint16))) { + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr->capac = drv_tab[i].size; + return SCPE_OK; + } + } +return SCPE_OK; +} + +/* Set size command validation routine */ + +t_stat dkp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = drv_tab[GET_DTYPE (val)].size; +return SCPE_OK; +} + +/* Bootstrap routine */ + +#if defined(_OLD_CODE_) + +#define BOOT_START 02000 +#define BOOT_UNIT 02021 +#define BOOT_SEEK 02022 +#define BOOT_LEN (sizeof(boot_rom) / sizeof(int32)) + +static const int32 boot_rom[] = { + 0060233, /* NIOC 0,DKP ; clear disk */ + 0020420, /* LDA 0,USSC ; unit, sfc, sec, cnt */ + 0063033, /* DOC 0,DKP ; select disk */ + 0020417, /* LDA 0,SEKCMD ; command, cylinder */ + 0061333, /* DOAP 0,DKP ; start seek */ + 0024415, /* LDA 1,SEKDN */ + 0060433, /* DIA 0,DKP ; get status */ + 0123415, /* AND# 1,0,SZR ; skip if done */ + 0000776, /* JMP .-2 */ + 0102400, /* SUB 0,0 ; mem addr = 0 */ + 0062033, /* DOB 0,DKP */ + 0020411, /* LDA 0,REDCMD ; command, cylinder */ + 0061133, /* DOAS 0,DKP ; start read */ + 0060433, /* DIA 0, DKP ; get status */ + 0101113, /* MOVL# 0,0,SNC ; skip if done */ + 0000776, /* JMP .-2 */ + 0000377, /* JMP 377 */ + 0000016, /* USSC: 0.B1+0.B7+0.B11+16 */ + 0175000, /* SEKCMD: 175000 */ + 0074000, /* SEKDN: 074000 */ + 0174000 /* REDCMD: 174000 */ + }; + + +t_stat dkp_boot (int32 unitno, DEVICE *dptr) +{ +int32 i, dtype; +extern int32 saved_PC, SR; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +unitno = unitno & USSC_M_UNIT; +dtype = GET_DTYPE (dkp_unit[unitno].flags); +M[BOOT_UNIT] = M[BOOT_UNIT] | (unitno << USSC_V_UNIT); +if (drv_tab[dtype].newf) M[BOOT_SEEK] = 0176000; +saved_PC = BOOT_START; +SR = 0100000 + DEV_DKP; +return SCPE_OK; +} + +#endif /* _OLD_CODE_ */ + + + +#define BOOT_START 0375 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) + +static const int32 boot_rom[] = { + 0062677 /* IORST ; reset the I/O system */ + , 0060133 /* NIOS DKP ; start the disk */ + , 0000377 /* JMP 377 ; wait for the world */ + } ; + + +t_stat dkp_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = (uint16) boot_rom[i]; +saved_PC = BOOT_START; +SR = 0100000 + DEV_DKP; +return SCPE_OK; +} diff --git a/NOVA/nova_dsk.c b/NOVA/nova_dsk.c new file mode 100644 index 0000000..315a480 --- /dev/null +++ b/NOVA/nova_dsk.c @@ -0,0 +1,333 @@ +/* nova_dsk.c: 4019 fixed head disk simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dsk fixed head disk + + 04-Jul-07 BKR device name changed to DG's DSK from DEC's DK, + DEV_xxx macros now used for consistency, + added secret DG DIC function, + fixed boot info table size calculation + 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) + 04-Jan-04 RMS Changed sim_fsize calling sequence + 26-Jul-03 RMS Fixed bug in set size routine + 14-Mar-03 RMS Fixed variable capacity interaction with save/restore + 03-Mar-03 RMS Fixed variable capacity and autosizing + 03-Oct-02 RMS Added DIB + 06-Jan-02 RMS Revised enable/disable support + 23-Aug-01 RMS Fixed bug in write watermarking + 26-Apr-01 RMS Added device enable/disable support + 10-Dec-00 RMS Added Eclipse support + 15-Oct-00 RMS Editorial changes + 14-Apr-99 RMS Changed t_addr to unsigned + + The 4019 is a head-per-track disk. To minimize overhead, the entire disk + is buffered in memory. +*/ + +#include "nova_defs.h" +#include + +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ +#define UNIT_M_PLAT 07 +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) +#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) + +/* Constants */ + +#define DSK_NUMWD 256 /* words/sector */ +#define DSK_NUMSC 8 /* sectors/track */ +#define DSK_NUMTR 128 /* tracks/disk */ +#define DSK_DKSIZE (DSK_NUMTR*DSK_NUMSC*DSK_NUMWD) /* words/disk */ +#define DSK_AMASK ((DSK_NUMDK*DSK_NUMTR*DSK_NUMSC) - 1) + /* address mask */ +#define DSK_NUMDK 8 /* disks/controller */ +#define GET_DISK(x) (((x) / (DSK_NUMSC * DSK_NUMTR)) & (DSK_NUMDK - 1)) + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ + +/* Status register */ + +#define DSKS_WLS 020 /* write lock status */ +#define DSKS_DLT 010 /* data late error */ +#define DSKS_NSD 004 /* non-existent disk */ +#define DSKS_CRC 002 /* parity error */ +#define DSKS_ERR 001 /* error summary */ +#define DSKS_ALLERR (DSKS_WLS | DSKS_DLT | DSKS_NSD | DSKS_CRC | DSKS_ERR) + +/* Map logical sector numbers to physical sector numbers + (indexed by track<2:0>'sector) +*/ + +static const int32 sector_map[] = { + 0, 2, 4, 6, 1, 3, 5, 7, 1, 3, 5, 7, 2, 4, 6, 0, + 2, 4, 6, 0, 3, 5, 7, 1, 3, 5, 7, 1, 4, 6, 0, 2, + 4, 6, 0, 2, 5, 7, 1, 3, 5, 7, 1, 3, 6, 0, 2, 4, + 6, 0, 2, 4, 7, 1, 3, 5, 7, 1, 3, 5, 0, 2, 4, 6 + }; + +#define DSK_MMASK 077 +#define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DSK_NUMSC))) + +extern uint16 M[]; +extern UNIT cpu_unit; +extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 saved_PC, SR, AMASK; + +int32 dsk_stat = 0; /* status register */ +int32 dsk_da = 0; /* disk address */ +int32 dsk_ma = 0; /* memory address */ +int32 dsk_wlk = 0; /* wrt lock switches */ +int32 dsk_stopioe = 0; /* stop on error */ +int32 dsk_time = 100; /* time per sector */ + +DEVICE dsk_dev; +int32 dsk (int32 pulse, int32 code, int32 AC); +t_stat dsk_svc (UNIT *uptr); +t_stat dsk_reset (DEVICE *dptr); +t_stat dsk_boot (int32 unitno, DEVICE *dptr); +t_stat dsk_attach (UNIT *uptr, char *cptr); +t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* DSK data structures + + dsk_dev device descriptor + dsk_unit unit descriptor + dsk_reg register list +*/ + +DIB dsk_dib = { DEV_DSK, INT_DSK, PI_DSK, &dsk }; + +UNIT dsk_unit = { + UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DSK_DKSIZE) + }; + +REG dsk_reg[] = { + { ORDATA (STAT, dsk_stat, 16) }, + { ORDATA (DA, dsk_da, 16) }, + { ORDATA (MA, dsk_ma, 16) }, + { FLDATA (BUSY, dev_busy, INT_V_DSK) }, + { FLDATA (DONE, dev_done, INT_V_DSK) }, + { FLDATA (DISABLE, dev_disable, INT_V_DSK) }, + { FLDATA (INT, int_req, INT_V_DSK) }, + { ORDATA (WLK, dsk_wlk, 8) }, + { DRDATA (TIME, dsk_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, dsk_stopioe, 0) }, + { NULL } + }; + +MTAB dsk_mod[] = { + { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &dsk_set_size }, + { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &dsk_set_size }, + { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &dsk_set_size }, + { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &dsk_set_size }, + { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &dsk_set_size }, + { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &dsk_set_size }, + { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &dsk_set_size }, + { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &dsk_set_size }, + { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, + { 0 } + }; + +DEVICE dsk_dev = { + "DSK", &dsk_unit, dsk_reg, dsk_mod, + 1, 8, 21, 1, 8, 16, + NULL, NULL, &dsk_reset, + &dsk_boot, &dsk_attach, NULL, + &dsk_dib, DEV_DISABLE + }; + + +/* IOT routine */ + +int32 dsk (int32 pulse, int32 code, int32 AC) +{ +int32 t, rval; + +rval = 0; +switch (code) { /* decode IR<5:7> */ + + case ioDIA: /* DIA */ + rval = dsk_stat & DSKS_ALLERR; /* read status */ + break; + + case ioDOA: /* DOA */ + dsk_da = AC & DSK_AMASK; /* save disk addr */ + break; + + case ioDIB: /* DIB */ + rval = dsk_ma & AMASK; /* read mem addr */ + break; + + case ioDOB: /* DOB */ + dsk_ma = AC & AMASK; /* save mem addr */ + break; + + case ioDIC: /* DIC - undocumented DG feature(!) */ + rval = 256 ; /* return fixed sector size for DG for now */ + break ; + } /* end switch code */ + +if (pulse) { /* any pulse? */ + DEV_CLR_BUSY( INT_DSK ) ; + DEV_CLR_DONE( INT_DSK ) ; + DEV_UPDATE_INTR ; + dsk_stat = 0; /* clear status */ + sim_cancel (&dsk_unit); /* stop I/O */ + } + +if ((pulse == iopP) && ((dsk_wlk >> GET_DISK (dsk_da)) & 1)) { /* wrt lock? */ + DEV_SET_DONE( INT_DSK ) ; + DEV_UPDATE_INTR ; + dsk_stat = DSKS_ERR + DSKS_WLS; /* set status */ + return rval; + } + +if (pulse & 1) { /* read or write? */ + if (((uint32) (dsk_da * DSK_NUMWD)) >= dsk_unit.capac) { /* inv sev? */ + DEV_SET_DONE( INT_DSK ) ; + DEV_UPDATE_INTR ; + dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */ + return rval; /* done */ + } + dsk_unit.FUNC = pulse; /* save command */ + DEV_SET_BUSY( INT_DSK ) ; + DEV_UPDATE_INTR ; + t = sector_map[dsk_da & DSK_MMASK] - GET_SECTOR (dsk_time); + if (t < 0) t = t + DSK_NUMSC; + sim_activate (&dsk_unit, t * dsk_time); /* activate */ + } +return rval; +} + + +/* Unit service */ + +t_stat dsk_svc (UNIT *uptr) +{ +int32 i, da, pa; +int16 *fbuf = uptr->filebuf; + +DEV_CLR_BUSY( INT_DSK ) ; +DEV_SET_DONE( INT_DSK ) ; +DEV_UPDATE_INTR ; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + dsk_stat = DSKS_ERR + DSKS_NSD; /* set status */ + return IORETURN (dsk_stopioe, SCPE_UNATT); + } + +da = dsk_da * DSK_NUMWD; /* calc disk addr */ +if (uptr->FUNC == iopS) { /* read? */ + for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */ + pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */ + if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da + i]; + } + dsk_ma = (dsk_ma + DSK_NUMWD) & AMASK; + } +else if (uptr->FUNC == iopP) { /* write? */ + for (i = 0; i < DSK_NUMWD; i++) { /* copy sector */ + pa = MapAddr (0, (dsk_ma + i) & AMASK); /* map address */ + fbuf[da + i] = M[pa]; + } + if (((uint32) (da + i)) >= uptr->hwmark) /* past end? */ + uptr->hwmark = da + i + 1; /* upd hwmark */ + dsk_ma = (dsk_ma + DSK_NUMWD + 3) & AMASK; + } + +dsk_stat = 0; /* set status */ +return SCPE_OK; +} + + +/* Reset routine */ + +t_stat dsk_reset (DEVICE *dptr) +{ +dsk_stat = dsk_da = dsk_ma = 0; +DEV_CLR_BUSY( INT_DSK ) ; +DEV_CLR_DONE( INT_DSK ) ; +DEV_UPDATE_INTR ; +sim_cancel (&dsk_unit); +return SCPE_OK; +} + + +/* Bootstrap routine */ + +#define BOOT_START 0375 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int32)) + +static const int32 boot_rom[] = { + 0062677 /* IORST ; reset the I/O system */ + , 0060120 /* NIOS DSK ; start the disk */ + , 0000377 /* JMP 377 ; wait for the world */ + } ; + + +t_stat dsk_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = (uint16) boot_rom[i]; +saved_PC = BOOT_START; +SR = 0100000 + DEV_DSK; +return SCPE_OK; +} + + +/* Attach routine */ + +t_stat dsk_attach (UNIT *uptr, char *cptr) +{ +uint32 sz, p; +uint32 ds_bytes = DSK_DKSIZE * sizeof (int16); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= DSK_NUMDK) p = DSK_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); + } +uptr->capac = UNIT_GETP (uptr->flags) * DSK_DKSIZE; /* set capacity */ +return attach_unit (uptr, cptr); +} + + +/* Change disk size */ + +t_stat dsk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val < 0) return SCPE_IERR; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = UNIT_GETP (val) * DSK_DKSIZE; +uptr->flags = uptr->flags & ~UNIT_AUTO; +return SCPE_OK; +} diff --git a/NOVA/nova_lp.c b/NOVA/nova_lp.c new file mode 100644 index 0000000..c2e3923 --- /dev/null +++ b/NOVA/nova_lp.c @@ -0,0 +1,155 @@ +/* nova_lp.c: NOVA line printer simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt line printer + + 04-Jul-07 BKR DEV_SET/CLR macros now used, + , , output character delay now contingent upon non-zero TIME value, + LPT can now be DISABLED + 19-Jan-07 RMS Added UNIT_TEXT + 25-Apr-03 RMS Revised for extended file support + 30-May-02 RMS Widened POS to 32b + + +Notes: + - data currently masked to 7 bits. + - if register TIME is non-zero, then delay TIME events if , or seen + - register POS show the current file position + - register STOP_IOE determines return value issued if output to unattached LPT is attempted +*/ + +#include "nova_defs.h" + +extern int32 int_req, dev_busy, dev_done, dev_disable; + + +int32 lpt_stopioe = 0; /* stop on error flag */ + +int32 lpt (int32 pulse, int32 code, int32 AC); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { DEV_LPT, INT_LPT, PI_LPT, &lpt }; + +UNIT lpt_unit = { /* 2007-May-30, bkr */ + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_LPT) }, + { FLDATA (DONE, dev_done, INT_V_LPT) }, + { FLDATA (DISABLE, dev_disable, INT_V_LPT) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { NULL } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL, + &lpt_dib, DEV_DISABLE + }; + + +/* IOT routine */ + +int32 lpt (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) + lpt_unit.buf = AC & 0177 ; + +switch (pulse) + { /* decode IR<8:9> */ + case iopS: /* start */ + DEV_SET_BUSY( INT_LPT ) ; + DEV_CLR_DONE( INT_LPT ) ; + DEV_UPDATE_INTR ; + if ( lpt_unit.wait ) + if ( (lpt_unit.buf == 015) + || (lpt_unit.buf == 014) + || (lpt_unit.buf == 012) + ) + { + sim_activate (&lpt_unit, lpt_unit.wait); + break ; + } + return (lpt_svc (&lpt_unit) << IOT_V_REASON); + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_LPT ) ; + DEV_CLR_DONE( INT_LPT ) ; + DEV_UPDATE_INTR ; + sim_cancel (&lpt_unit); /* deactivate unit */ + break; + } /* end switch */ + +return 0; +} + + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +DEV_CLR_BUSY( INT_LPT ) ; +DEV_SET_DONE( INT_LPT ) ; +DEV_UPDATE_INTR ; +if ((lpt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); +fputc (uptr->buf, uptr->fileref); +uptr->pos = ftell (uptr->fileref); +if (ferror (uptr->fileref)) { + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_unit.buf = 0; /* (not DG compatible) */ +DEV_CLR_BUSY( INT_LPT ) ; +DEV_CLR_DONE( INT_LPT ) ; +DEV_UPDATE_INTR ; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/NOVA/nova_mta.c b/NOVA/nova_mta.c new file mode 100644 index 0000000..2dd287e --- /dev/null +++ b/NOVA/nova_mta.c @@ -0,0 +1,628 @@ +/* nova_mta.c: NOVA magnetic tape simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mta magnetic tape + + 04-Jul-07 BKR fixed boot code to properly boot self-boot tapes; + boot routine now uses standard DG APL boot code; + device name changed to DG's MTA from DEC's MT. + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 18-Mar-05 RMS Added attached test to detach routine + 22-Nov-03 CEO DIB returns # records skipped after space fwd + 22-Nov-03 CEO Removed cancel of tape events in IORST + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised for magtape library + 30-Oct-02 RMS Fixed BOT handling, added error record handling + 08-Oct-02 RMS Added DIB + 30-Sep-02 RMS Revamped error handling + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length test + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Changed POS, USTAT, FLG to an array + 26-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot + 10-Dec-00 RMS Added Eclipse support from Charles Owen + 15-Oct-00 RMS Editorial changes + 11-Nov-98 CEO Removed clear of mta_ma on iopC + 04-Oct-98 RMS V2.4 magtape format + 18-Jan-97 RMS V2.3 magtape format + 29-Jun-96 RMS Added unit enable/disable support + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count byte count is little endian + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0 and are + not duplicated; end of tape by end of file. +*/ + +#include "nova_defs.h" +#include "sim_tape.h" + +#define MTA_NUMDR 8 /* #drives */ +#define USTAT u3 /* unit status */ +#define MTA_MAXFR (1 << 16) /* max record lnt */ +#define WC_SIZE (1 << 14) /* max word count */ +#define WC_MASK (WC_SIZE - 1) + +/* Command/unit */ + +#define CU_CI 0100000 /* clear interrupt */ +#define CU_EP 0002000 /* poll enable */ +#define CU_DE 0001000 /* disable erase */ +#define CU_DA 0000400 /* disable autoretry */ +#define CU_PE 0000400 /* PE mode */ +#define CU_V_CMD 3 /* command */ +#define CU_M_CMD 027 +#define CU_READ 000 +#define CU_REWIND 001 +#define CU_CMODE 002 +#define CU_SPACEF 003 +#define CU_SPACER 004 +#define CU_WRITE 005 +#define CU_WREOF 006 +#define CU_ERASE 007 +#define CU_READNS 020 +#define CU_UNLOAD 021 +#define CU_DMODE 022 +#define CU_V_UNIT 0 /* unit */ +#define CU_M_UNIT 07 +#define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD) +#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) + +/* Status 1 - stored in mta_sta<31:16> or (*) uptr->USTAT<31:16> */ + +#define STA_ERR1 (0100000u << 16) /* error */ +#define STA_DLT (0040000 << 16) /* data late */ +#define STA_REW (0020000 << 16) /* *rewinding */ +#define STA_ILL (0010000 << 16) /* illegal */ +#define STA_HDN (0004000 << 16) /* high density */ +#define STA_DAE (0002000 << 16) /* data error */ +#define STA_EOT (0001000 << 16) /* *end of tape */ +#define STA_EOF (0000400 << 16) /* *end of file */ +#define STA_BOT (0000200 << 16) /* *start of tape */ +#define STA_9TK (0000100 << 16) /* nine track */ +#define STA_BAT (0000040 << 16) /* bad tape */ +#define STA_CHG (0000010 << 16) /* status change */ +#define STA_WLK (0000004 << 16) /* *write lock */ +#define STA_ODD (0000002 << 16) /* odd character */ +#define STA_RDY (0000001 << 16) /* *drive ready */ + +/* Status 2 - stored in mta_sta<15:0> or (*) uptr->USTAT<15:0> */ + +#define STA_ERR2 0100000 /* error */ +#define STA_RWY 0040000 /* runaway tape */ +#define STA_FGP 0020000 /* false gap */ +#define STA_CDL 0004000 /* corrected dlt */ +#define STA_V_UNIT 8 +#define STA_M_UNIT 07 /* unit */ +#define STA_WCO 0000200 /* word count ovflo */ +#define STA_BDS 0000100 /* bad signal */ +#define STA_OVS 0000040 /* overskew */ +#define STA_CRC 0000020 /* check error */ +#define STA_STE 0000010 /* single trk error */ +#define STA_FPR 0000004 /* false preamble */ +#define STA_FMT 0000002 /* format error */ +#define STA_PEM 0000001 /* *PE mode */ + +#define STA_EFLGS1 (STA_DLT | STA_ILL | STA_DAE | STA_EOT | \ + STA_EOF | STA_BOT | STA_BAT | STA_ODD) +#define STA_EFLGS2 (STA_FGP | STA_CDL | STA_BDS | STA_OVS | \ + STA_CRC | STA_FPR | STA_FPR) /* set error 2 */ +#define STA_CLR ((020 << 16) | 0010000) /* always clear */ +#define STA_SET (STA_HDN | STA_9TK) /* always set */ +#define STA_DYN (STA_REW | STA_EOT | STA_EOF | STA_BOT | \ + STA_WLK | STA_RDY | STA_PEM) /* kept in USTAT */ +#define STA_MON (STA_REW | STA_BOT | STA_WLK | STA_RDY | \ + STA_PEM) /* set status chg */ + +extern uint16 M[]; +extern UNIT cpu_unit; +extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 SR, AMASK; + +extern t_stat cpu_boot(int32 unitno, DEVICE * dptr ) ; + + +int32 mta_ma = 0; /* memory address */ +int32 mta_wc = 0; /* word count */ +int32 mta_cu = 0; /* command/unit */ +int32 mta_sta = 0; /* status register */ +int32 mta_ep = 0; /* enable polling */ +int32 mta_cwait = 100; /* command latency */ +int32 mta_rwait = 100; /* record latency */ +uint8 *mtxb = NULL; /* transfer buffer */ + +DEVICE mta_dev; +int32 mta (int32 pulse, int32 code, int32 AC); +t_stat mta_svc (UNIT *uptr); +t_stat mta_reset (DEVICE *dptr); +t_stat mta_boot (int32 unitno, DEVICE *dptr); +t_stat mta_attach (UNIT *uptr, char *cptr); +t_stat mta_detach (UNIT *uptr); +int32 mta_updcsta (UNIT *uptr); +void mta_upddsta (UNIT *uptr, int32 newsta); +t_stat mta_map_err (UNIT *uptr, t_stat st); +t_stat mta_vlock (UNIT *uptr, int32 val, char *cptr, void *desc); + +static const int ctype[32] = { /* c vs r timing */ + 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1 + }; + +/* MTA data structures + + mta_dev MTA device descriptor + mta_unit MTA unit list + mta_reg MTA register list + mta_mod MTA modifier list +*/ + +DIB mta_dib = { DEV_MTA, INT_MTA, PI_MTA, &mta }; + +UNIT mta_unit[] = { + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mta_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } + }; + +REG mta_reg[] = { + { ORDATA (CU, mta_cu, 16) }, + { ORDATA (MA, mta_ma, 16) }, + { ORDATA (WC, mta_wc, 16) }, + { GRDATA (STA1, mta_sta, 8, 16, 16) }, + { ORDATA (STA2, mta_sta, 16) }, + { FLDATA (EP, mta_ep, 0) }, + { FLDATA (BUSY, dev_busy, INT_V_MTA) }, + { FLDATA (DONE, dev_done, INT_V_MTA) }, + { FLDATA (DISABLE, dev_disable, INT_V_MTA) }, + { FLDATA (INT, int_req, INT_V_MTA) }, + { DRDATA (CTIME, mta_cwait, 24), PV_LEFT }, + { DRDATA (RTIME, mta_rwait, 24), PV_LEFT }, + { URDATA (UST, mta_unit[0].USTAT, 8, 32, 0, MTA_NUMDR, 0) }, + { URDATA (POS, mta_unit[0].pos, 8, T_ADDR_W, 0, + MTA_NUMDR, REG_RO | PV_LEFT) }, + { NULL } + }; + +MTAB mta_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &mta_vlock }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &mta_vlock }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { 0 } + }; + +DEVICE mta_dev = { + "MTA", mta_unit, mta_reg, mta_mod, + MTA_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mta_reset, + &mta_boot, &mta_attach, &mta_detach, + &mta_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 mta (int32 pulse, int32 code, int32 AC) +{ +UNIT *uptr; +int32 u, c, rval; + +rval = 0; +uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */ +switch (code) { /* decode IR<5:7> */ + + case ioDIA: /* DIA */ + rval = (mta_updcsta (uptr) >> 16) & DMASK; /* return status 1 */ + break; + + case ioDOA: /* DOA */ +/* if (AC & CU_CI) ... clear ep int */ + mta_cu = AC; /* save cmd/unit */ + uptr = mta_dev.units + GET_UNIT(mta_cu); /* get unit */ + mta_updcsta (uptr); /* update status */ + break; + + case ioDIB: /* DIB */ + rval = mta_ma & AMASK; /* return ma */ + break; + + case ioDOB: /* DOB */ + mta_ma = AC & AMASK; /* save ma */ + break; + + case ioDIC: /* DIC */ + rval = mta_updcsta (uptr) & DMASK; /* return status 2 */ + break; + + case ioDOC: /* DOC */ + mta_wc = ((AC & 040000) << 1) | (AC & 077777); /* save wc */ + break; + } /* end switch code */ + +switch (pulse) { /* decode IR<8:9> */ + + case iopS: /* start */ + c = GET_CMD (mta_cu); /* get command */ + if (dev_busy & INT_MTA) break; /* ignore if busy */ + if ((uptr->USTAT & STA_RDY) == 0) { /* drive not ready? */ + mta_sta = mta_sta | STA_ILL; /* illegal op */ + dev_busy = dev_busy & ~INT_MTA; /* clear busy */ + dev_done = dev_done | INT_MTA; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + } + else if ((c == CU_REWIND) || (c == CU_UNLOAD)) { /* rewind, unload? */ + mta_upddsta (uptr, (uptr->USTAT & /* update status */ + ~(STA_BOT | STA_EOF | STA_EOT | STA_RDY)) | STA_REW); + sim_activate (uptr, mta_rwait); /* start IO */ + if (c == CU_UNLOAD) detach_unit (uptr); + } + else { + mta_sta = 0; /* clear errors */ + dev_busy = dev_busy | INT_MTA; /* set busy */ + dev_done = dev_done & ~INT_MTA; /* clear done */ + int_req = int_req & ~INT_MTA; /* clear int */ + if (ctype[c]) sim_activate (uptr, mta_cwait); + else { + mta_upddsta (uptr, uptr->USTAT & + ~(STA_BOT | STA_EOF | STA_EOT | STA_RDY)); + sim_activate (uptr, mta_rwait); + } + } + mta_updcsta (uptr); /* update status */ + break; + + case iopC: /* clear */ + for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */ + uptr = mta_dev.units + u; /* cancel IO */ + if (sim_is_active (uptr) && !(uptr->USTAT & STA_REW)) { + mta_upddsta (uptr, uptr->USTAT | STA_RDY); + sim_cancel (uptr); + } + } + dev_busy = dev_busy & ~INT_MTA; /* clear busy */ + dev_done = dev_done & ~INT_MTA; /* clear done */ + int_req = int_req & ~INT_MTA; /* clear int */ + mta_sta = mta_cu = 0; /* clear registers */ + mta_updcsta (&mta_unit[0]); /* update status */ + break; + } /* end case pulse */ + +return rval; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, clear busy, set done, interrupt +*/ + +t_stat mta_svc (UNIT *uptr) +{ +int32 c, p, pa, u; +t_mtrlnt i, cbc, tbc, wc; +uint16 c1, c2; +t_stat st, r = SCPE_OK; + +u = uptr - mta_dev.units; /* get unit number */ +c = GET_CMD (mta_cu); /* command */ +wc = WC_SIZE - (mta_wc & WC_MASK); /* io wc */ + +if (uptr->USTAT & STA_REW) { /* rewind? */ + sim_tape_rewind (uptr); /* update tape */ + mta_upddsta (uptr, (uptr->USTAT & ~STA_REW) | STA_BOT | STA_RDY); + if (u == GET_UNIT (mta_cu)) mta_updcsta (uptr); + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + mta_upddsta (uptr, 0); /* unit off line */ + mta_sta = mta_sta | STA_ILL; /* illegal operation */ + } +else switch (c) { /* case on command */ + + case CU_CMODE: /* controller mode */ + mta_ep = mta_cu & CU_EP; + break; + + case CU_DMODE: /* drive mode */ + if (!sim_tape_bot (uptr)) mta_sta = mta_sta | STA_ILL; /* must be BOT */ + else mta_upddsta (uptr, (mta_cu & CU_PE)? /* update drv status */ + uptr->USTAT | STA_PEM: uptr->USTAT & ~ STA_PEM); + break; + + case CU_READ: /* read */ + case CU_READNS: /* read non-stop */ + st = sim_tape_rdrecf (uptr, mtxb, &tbc, MTA_MAXFR); /* read rec */ + if (st == MTSE_RECE) mta_sta = mta_sta | STA_DAE; /* rec in err? */ + else if (st != MTSE_OK) { /* other error? */ + r = mta_map_err (uptr, st); /* map error */ + break; + } + cbc = wc * 2; /* expected bc */ + if (tbc & 1) mta_sta = mta_sta | STA_ODD; /* odd byte count? */ + if (tbc > cbc) mta_sta = mta_sta | STA_WCO; /* too big? */ + else { + cbc = tbc; /* no, use it */ + wc = (cbc + 1) / 2; /* adjust wc */ + } + for (i = p = 0; i < wc; i++) { /* copy buf to mem */ + c1 = mtxb[p++]; + c2 = mtxb[p++]; + pa = MapAddr (0, mta_ma); /* map address */ + if (MEM_ADDR_OK (pa)) M[pa] = (c1 << 8) | c2; + mta_ma = (mta_ma + 1) & AMASK; + } + mta_wc = (mta_wc + wc) & DMASK; + mta_upddsta (uptr, uptr->USTAT | STA_RDY); + break; + + case CU_WRITE: /* write */ + tbc = wc * 2; /* io byte count */ + for (i = p = 0; i < wc; i++) { /* copy to buffer */ + pa = MapAddr (0, mta_ma); /* map address */ + mtxb[p++] = (M[pa] >> 8) & 0377; + mtxb[p++] = M[pa] & 0377; + mta_ma = (mta_ma + 1) & AMASK; + } + if (st = sim_tape_wrrecf (uptr, mtxb, tbc)) { /* write rec, err? */ + r = mta_map_err (uptr, st); /* map error */ + mta_ma = (mta_ma - wc) & AMASK; /* restore wc */ + } + else mta_wc = 0; /* clear wc */ + mta_upddsta (uptr, uptr->USTAT | STA_RDY); + break; + + case CU_WREOF: /* write eof */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = mta_map_err (uptr, st); /* map error */ + else mta_upddsta (uptr, uptr->USTAT | STA_EOF | STA_RDY); + break; + + case CU_ERASE: /* erase */ + if (sim_tape_wrp (uptr)) /* write protected? */ + r = mta_map_err (uptr, MTSE_WRP); /* map error */ + else mta_upddsta (uptr, uptr->USTAT | STA_RDY); + break; + + case CU_SPACEF: /* space forward */ + do { + mta_wc = (mta_wc + 1) & DMASK; /* incr wc */ + if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ + r = mta_map_err (uptr, st); /* map error */ + break; + } + } while (mta_wc != 0); + mta_upddsta (uptr, uptr->USTAT | STA_RDY); + mta_ma = mta_wc; /* word count = # records */ + break; + + case CU_SPACER: /* space reverse */ + do { + mta_wc = (mta_wc + 1) & DMASK; /* incr wc */ + if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ + r = mta_map_err (uptr, st); /* map error */ + break; + } + } while (mta_wc != 0); + mta_upddsta (uptr, uptr->USTAT | STA_RDY); + mta_ma = mta_wc; /* word count = # records */ + break; + + default: /* reserved */ + mta_sta = mta_sta | STA_ILL; + mta_upddsta (uptr, uptr->USTAT | STA_RDY); + break; + } /* end case */ + +mta_updcsta (uptr); /* update status */ +dev_busy = dev_busy & ~INT_MTA; /* clear busy */ +dev_done = dev_done | INT_MTA; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +return r; +} + +/* Update controller status */ + +int32 mta_updcsta (UNIT *uptr) /* update ctrl */ +{ +mta_sta = (mta_sta & ~(STA_DYN | STA_CLR | STA_ERR1 | STA_ERR2)) | + (uptr->USTAT & STA_DYN) | STA_SET; +if (mta_sta & STA_EFLGS1) mta_sta = mta_sta | STA_ERR1; +if (mta_sta & STA_EFLGS2) mta_sta = mta_sta | STA_ERR2; +return mta_sta; +} + +/* Update drive status */ + +void mta_upddsta (UNIT *uptr, int32 newsta) /* drive status */ +{ +int32 change; + +if ((uptr->flags & UNIT_ATT) == 0) newsta = 0; /* offline? */ +change = (uptr->USTAT ^ newsta) & STA_MON; /* changes? */ +uptr->USTAT = newsta & STA_DYN; /* update status */ +if (change) { +/* if (mta_ep) { /* if polling */ +/* u = uptr - mta_dev.units; /* unit num */ +/* mta_sta = (mta_sta & ~STA_UNIT) | (u << STA_V_UNIT); +/* set polling interupt... +/* } */ + mta_sta = mta_sta | STA_CHG; /* flag change */ + } +return; +} + +/* Map tape error status */ + +t_stat mta_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + mta_upddsta (uptr, uptr->USTAT | STA_WLK | STA_RDY); + case MTSE_UNATT: /* unattached */ + mta_sta = mta_sta | STA_ILL; + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_TMK: /* end of file */ + mta_upddsta (uptr, uptr->USTAT | STA_RDY | STA_EOF); + break; + + case MTSE_IOERR: /* IO error */ + mta_sta = mta_sta | STA_DAE; /* data error */ + mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ + return SCPE_IOERR; + + case MTSE_INVRL: /* invalid rec lnt */ + mta_sta = mta_sta | STA_DAE; /* data error */ + mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + mta_sta = mta_sta | STA_DAE; /* data error */ + mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ + break; + + case MTSE_EOM: /* end of medium */ + mta_sta = mta_sta | STA_BAT; /* bad tape */ + mta_upddsta (uptr, uptr->USTAT | STA_RDY); /* ready */ + break; + + case MTSE_BOT: /* reverse into BOT */ + mta_upddsta (uptr, uptr->USTAT | STA_RDY | STA_BOT); + break; + + case MTSE_WRP: /* write protect */ + mta_upddsta (uptr, uptr->USTAT | STA_WLK | STA_RDY); + mta_sta = mta_sta | STA_ILL; /* illegal operation */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mta_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +dev_busy = dev_busy & ~INT_MTA; /* clear busy */ +dev_done = dev_done & ~INT_MTA; /* clear done, int */ +int_req = int_req & ~INT_MTA; +mta_cu = mta_wc = mta_ma = mta_sta = 0; /* clear registers */ +mta_ep = 0; + +/* AOS Installer does an IORST after a tape rewind command but before it can + be serviced, yet expects the tape to have been rewound */ + +for (u = 0; u < MTA_NUMDR; u++) { /* loop thru units */ + uptr = mta_dev.units + u; + if (sim_is_active (uptr) && /* active and */ + (uptr->flags & STA_REW)) /* rewinding? */ + sim_tape_rewind (uptr); /* update tape */ + sim_tape_reset (uptr); /* clear pos flag */ + sim_cancel (uptr); /* cancel activity */ + if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_RDY | + (uptr->USTAT & STA_PEM) | + (sim_tape_wrp (uptr)? STA_WLK: 0) | + (sim_tape_bot (uptr)? STA_BOT: 0); + else uptr->USTAT = 0; + } +mta_updcsta (&mta_unit[0]); /* update status */ +if (mtxb == NULL) mtxb = (uint8 *) calloc (MTA_MAXFR, sizeof (uint8)); +if (mtxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mta_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +if (!sim_is_active (uptr)) mta_upddsta (uptr, STA_RDY | STA_BOT | STA_PEM | + (sim_tape_wrp (uptr)? STA_WLK: 0)); +return r; +} + +/* Detach routine */ + +t_stat mta_detach (UNIT* uptr) +{ +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if (!sim_is_active (uptr)) mta_upddsta (uptr, 0); +return sim_tape_detach (uptr); +} + +/* Write lock/unlock validate routine */ + +t_stat mta_vlock (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if ((uptr->flags & UNIT_ATT) && (val || sim_tape_wrp (uptr))) + mta_upddsta (uptr, uptr->USTAT | STA_WLK); +else mta_upddsta (uptr, uptr->USTAT & ~STA_WLK); +return SCPE_OK; +} + + +/* Boot routine */ + +t_stat mta_boot (int32 unitno, DEVICE *dptr) + { + sim_tape_rewind( &mta_unit[unitno] ) ; + /* + use common rewind/reset code + device reset + rewind 'tape' file + device + unit + controller + */ + cpu_boot( unitno, dptr ) ; + SR = 0100000 + DEV_MTA ; + return ( SCPE_OK ); + } /* end of 'mta_boot' */ diff --git a/NOVA/nova_plt.c b/NOVA/nova_plt.c new file mode 100644 index 0000000..9375e18 --- /dev/null +++ b/NOVA/nova_plt.c @@ -0,0 +1,161 @@ +/* nova_plt.c: NOVA plotter simulator + + Copyright (c) 2000-2008, Robert M. Supnik + Written by Bruce Ray and used with his gracious permission. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + plt plotter + + 04-Jul-07 BKR added 7B/8B support (default is 8B), + DEV_SET/CLR macros now used + 25-Apr-03 RMS Revised for extended file support + 03-Oct-02 RMS Added DIB + 30-May-02 RMS Widened POS to 32b + 06-Jan-02 RMS Revised enable/disable support + 26-Apr-01 RMS Added device enable/disable support + + +Notes: + - data masked to 7- or 8- bits, based on 7B or 8B, default is 8-bits + - if register TIME is non-zero, then delay TIME events if , or seen + - register POS show the current file position + - register STOP_IOE determines return value issued if output to unattached PLT is attempted +*/ + +#include "nova_defs.h" + +extern int32 int_req, dev_busy, dev_done, dev_disable; + +int32 plt_stopioe = 0; /* stop on error */ + +DEVICE plt_dev; +int32 plt (int32 pulse, int32 code, int32 AC); +t_stat plt_svc (UNIT *uptr); +t_stat plt_reset (DEVICE *dptr); + + + /* 7 or 8 bit data mask support for either device */ + +#define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */ +#define UNIT_8B (1 << UNIT_V_8B) + +/* PLT data structures + + plt_dev PLT device descriptor + plt_unit PLT unit descriptor + plt_reg PLT register list +*/ + +DIB plt_dib = { DEV_PLT, INT_PLT, PI_PLT, &plt }; + +UNIT plt_unit = { /* 2007-May-30, bkr */ + UDATA (&plt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_8B, 0), SERIAL_OUT_WAIT + }; + +REG plt_reg[] = { + { ORDATA (BUF, plt_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_PLT) }, + { FLDATA (DONE, dev_done, INT_V_PLT) }, + { FLDATA (DISABLE, dev_disable, INT_V_PLT) }, + { FLDATA (INT, int_req, INT_V_PLT) }, + { DRDATA (POS, plt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, plt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, plt_stopioe, 0) }, + { NULL } + }; + +MTAB plt_mod[] = /* 2007-May-30, bkr */ + { + { UNIT_8B, 0, "7b", "7B", NULL }, + { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, + { 0, 0, NULL, NULL, NULL } + } ; + +DEVICE plt_dev = { + "PLT", &plt_unit, plt_reg, plt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &plt_reset, + NULL, NULL, NULL, + &plt_dib, DEV_DISABLE + }; + + +/* plotter: IOT routine */ + +int32 plt (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) + plt_unit.buf = AC & ((plt_unit.flags & UNIT_8B)? + 0377 + : 0177); +switch (pulse) + { /* decode IR<8:9> */ + case iopS: /* start */ + DEV_SET_BUSY( INT_PLT ) ; + DEV_CLR_DONE( INT_PLT ) ; + DEV_UPDATE_INTR ; + sim_activate (&plt_unit, plt_unit.wait); /* activate unit */ + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_PLT ) ; + DEV_CLR_DONE( INT_PLT ) ; + DEV_UPDATE_INTR ; + sim_cancel (&plt_unit); /* deactivate unit */ + break; + } /* end switch */ + +return 0; +} + + +/* Unit service */ + +t_stat plt_svc (UNIT *uptr) +{ +DEV_CLR_BUSY( INT_PLT ) ; +DEV_SET_DONE( INT_PLT ) ; +DEV_UPDATE_INTR ; +if ((plt_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (plt_stopioe, SCPE_UNATT); +if (putc (plt_unit.buf, plt_unit.fileref) == EOF) { + perror ("PLT I/O error"); + clearerr (plt_unit.fileref); + return SCPE_IOERR; + } +++(plt_unit.pos); +return SCPE_OK; +} + + +/* Reset routine */ + +t_stat plt_reset (DEVICE *dptr) +{ +plt_unit.buf = 0; /* */ +DEV_CLR_BUSY( INT_PLT ) ; +DEV_CLR_DONE( INT_PLT ) ; +DEV_UPDATE_INTR ; +sim_cancel (&plt_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/NOVA/nova_pt.c b/NOVA/nova_pt.c new file mode 100644 index 0000000..4778850 --- /dev/null +++ b/NOVA/nova_pt.c @@ -0,0 +1,297 @@ +/* nova_pt.c: NOVA paper tape read/punch simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + + 04-Jul-07 BKR added PTR and PTP device DISABLE capability, + added 7B/8B support PTR and PTP (default is 8B), + DEV_SET/CLR macros now used, + PTR and PTP can now be DISABLED + 25-Apr-03 RMS Revised for extended file support + 03-Oct-02 RMS Added DIBs + 30-May-02 RMS Widened POS to 32b + 29-Nov-01 RMS Added read only unit support + + +Notes: + - data masked to 7- or 8- bits, based on 7B or 8B, default is 8-bits + - register TIME is the delay between character read or write operations + - register POS show the number of characters read from or sent to the PTR or PTP + - register STOP_IOE determines return value issued if output to unattached PTR or PTP is attempted +*/ + +#include "nova_defs.h" + +extern int32 int_req, dev_busy, dev_done, dev_disable ; +extern int32 SR ; + +extern t_stat cpu_boot(int32 unitno, DEVICE * dptr ) ; + + +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ + +int32 ptr (int32 pulse, int32 code, int32 AC); +int32 ptp (int32 pulse, int32 code, int32 AC); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); + + + /* 7 or 8 bit data mask support for either device */ + +#define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */ +#define UNIT_8B (1 << UNIT_V_8B) + + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +DIB ptr_dib = { DEV_PTR, INT_PTR, PI_PTR, &ptr }; + +UNIT ptr_unit = { /* 2007-May-30, bkr */ + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE+UNIT_8B, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_PTR) }, + { FLDATA (DONE, dev_done, INT_V_PTR) }, + { FLDATA (DISABLE, dev_disable, INT_V_PTR) }, + { FLDATA (INT, int_req, INT_V_PTR) }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } + }; + +MTAB ptr_mod[] = /* 2007-May-30, bkr */ + { + { UNIT_8B, 0, "7b", "7B", NULL }, + { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, + { 0, 0, NULL, NULL, NULL } + } ; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod /* 2007-May-30, bkr */, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL, + &ptr_dib, DEV_DISABLE /* 2007-May-30, bkr */ + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +DIB ptp_dib = { DEV_PTP, INT_PTP, PI_PTP, &ptp }; + +UNIT ptp_unit = + { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_8B, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_PTP) }, + { FLDATA (DONE, dev_done, INT_V_PTP) }, + { FLDATA (DISABLE, dev_disable, INT_V_PTP) }, + { FLDATA (INT, int_req, INT_V_PTP) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +MTAB ptp_mod[] = + { + { UNIT_8B, 0, "7b", "7B", NULL }, + { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, + { 0, 0, NULL, NULL, NULL } + } ; + +DEVICE ptp_dev = + { + "PTP", &ptp_unit, ptp_reg, ptp_mod /* 2007-May-30, bkr */, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + &ptp_dib, DEV_DISABLE /* 2007-May-30, bkr */ + }; + + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 pulse, int32 code, int32 AC) +{ +int32 iodata; + +iodata = (code == ioDIA)? + ptr_unit.buf & 0377 + : 0; +switch (pulse) + { /* decode IR<8:9> */ + case iopS: /* start */ + DEV_SET_BUSY( INT_PTR ) ; + DEV_CLR_DONE( INT_PTR ) ; + DEV_UPDATE_INTR ; + sim_activate (&ptr_unit, ptr_unit.wait); /* activate unit */ + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_PTR ) ; + DEV_CLR_DONE( INT_PTR ) ; + DEV_UPDATE_INTR ; + sim_cancel (&ptr_unit); /* deactivate unit */ + break; + } /* end switch */ + +return iodata; +} + + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } + +DEV_CLR_BUSY( INT_PTR ) ; +DEV_SET_DONE( INT_PTR ) ; +DEV_UPDATE_INTR ; +ptr_unit.buf = temp & ((ptr_unit.flags & UNIT_8B)? 0377: 0177); +++(ptr_unit.pos); +return SCPE_OK; +} + + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_unit.buf = 0; /* */ +DEV_CLR_BUSY( INT_PTR ) ; +DEV_CLR_DONE( INT_PTR ) ; +DEV_UPDATE_INTR ; +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + + +/* Boot routine */ + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +ptr_reset( dptr ) ; +/* set position to 0? */ +cpu_boot( unitno, dptr ) ; +SR = /* low-speed: no high-order bit set */ DEV_PTR ; +return ( SCPE_OK ); +} /* end of 'ptr_boot' */ + + + + + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) + ptp_unit.buf = AC & 0377; + +switch (pulse) + { /* decode IR<8:9> */ + case iopS: /* start */ + DEV_SET_BUSY( INT_PTP ) ; + DEV_CLR_DONE( INT_PTP ) ; + DEV_UPDATE_INTR ; + sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_PTP ) ; + DEV_CLR_DONE( INT_PTP ) ; + DEV_UPDATE_INTR ; + sim_cancel (&ptp_unit); /* deactivate unit */ + break; + } /* end switch */ + +return 0; +} + + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +DEV_CLR_BUSY( INT_PTP ) ; +DEV_SET_DONE( INT_PTP ) ; +DEV_UPDATE_INTR ; +if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc ((ptp_unit.buf & ((ptp_unit.flags & UNIT_8B)? 0377: 0177)), ptp_unit.fileref) == EOF) { + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +++(ptp_unit.pos); +return SCPE_OK; +} + + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; /* */ +DEV_CLR_BUSY( INT_PTP ) ; +DEV_CLR_DONE( INT_PTP ) ; +DEV_UPDATE_INTR ; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/NOVA/nova_qty.c b/NOVA/nova_qty.c new file mode 100644 index 0000000..e4c928b --- /dev/null +++ b/NOVA/nova_qty.c @@ -0,0 +1,1119 @@ +/* nova_qty.c: NOVA multiplexor (QTY/ALM) simulator + + Copyright (c) 2000-2008, Robert M. Supnik + Written by Bruce Ray and used with his gracious permission. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + qty multiplexor: QTY = 4060, ALM = 42xx + + 04-Jul-07 BKR fixed QTY output line number calculation (affected higher line numbers), + 25-Mar-04 RMS Updated for V3.2 + 12-Jan-04 BKR Initial release + includes both original DG "quad" multiplexor (QTY) + and later Asynchronous Line Multiplexor (ALM) support. +*/ + + +/*----------------------------------------------------------------------*/ +/* QTY [4060-compatible] multiplexor */ +/*----------------------------------------------------------------------*/ + +/* + * Emulate the DG 4060 "quad" (QTY) serial port multiplexor. DG modem + * control is not supported in this revision due to its obtuse nature + * of using a separate [semi-secret] device MDM which is actually part + * of the DG 4026/4027 multiplexor hardware(!). + * (Full modem support is provided in the ALM driver.) + * + * + * 4060 Hardware + * + * device code: 030 [primary], + * 070 [secondary] + * interrupt mask: B14 [000002] + * ASM mnemonic: QTY + * + * + * 4060 Input/Output Word Format: + * + * _________________________________________________________________ + * | RI| TI| channel | character | + * ----+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ + * 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + * + * + * RI - receiver interrupt + * TI - transmitter interrupt + * channel - channel number, 0 - 63. + * character- character (valid if receiver interrupt, undefined if transmitter) + * + * Notes: + * + * Maximum 64 lines supported. + * DONE set whenever any received character fully assembled and ready, + * or when any output character transmitted and line is ready + * to accept next output character. + * BUSY set whenever output character is being sent on any line. + * Note that early 4060s did NOT have a busy flag! + * IORST clears device Done, no other user instruction does. + * IORST clears each line's individual R.I. and T.I. + * + * + * Instructions: + * + * DIA get multiplexor status word [format defined above] + * DOA send character to QTY line [format defined above, RI & SI ] + * DIB [returns backplane bus noise] + * DOB clear QTY line + * DIC [returns backplace bus noise] + * DOC + * 'C' clears global done, then checks for RI and TI; + * 'P' + * 'S' + */ + + +#include "nova_defs.h" + +#include "sim_sock.h" +#include "sim_tmxr.h" + + +#define UNIT_V_8B (UNIT_V_UF + 0) /* 8b output */ +#define UNIT_8B (1 << UNIT_V_8B) + + + +extern int32 int_req, dev_busy, dev_done, dev_disable ; +extern int32 sim_switches ; +extern FILE * sim_log ; +extern int32 tmxr_poll ; /* calibrated delay */ + +t_stat qty_summary ( FILE * st, UNIT * uptr, int32 val, void * desc ) ; +t_stat qty_show ( FILE * st, UNIT * uptr, int32 val, void * desc ) ; +t_stat qty_setnl ( UNIT * uptr, int32 val, char * cptr, void * desc ) ; + +t_stat qty_attach ( UNIT * uptr, char * cptr ) ; +t_stat qty_detach ( UNIT * uptr ) ; +t_stat qty_reset ( DEVICE * dptr ) ; +t_stat qty_svc ( UNIT * uptr ) ; +int32 qty ( int32 pulse, int32 code, int32 AC ) ; + +t_stat alm_reset ( DEVICE * dptr ) ; +t_stat alm_svc ( UNIT * uptr ) ; +int32 alm ( int32 pulse, int32 code, int32 AC ) ; + +DEVICE alm_dev ; + + +#define QTY_MAX 64 /* max number of QTY lines - hardware */ + + +int32 qty_brkio = SCPE_OK ; /* default I/O status code */ +int32 qty_max = QTY_MAX ; /* max # QTY lines - user */ + /* controllable */ +int32 qty_mdm = 0 ; /* QTY modem control active? */ +int32 qty_auto = 0 ; /* QTY auto disconnect active? */ +int32 qty_polls = 0 ; /* total 'qty_svc' polls */ + + +TMLN qty_ldsc[ QTY_MAX ] = { 0 } ; /* QTY line descriptors */ +TMXR qty_desc = { QTY_MAX, 0, 0, qty_ldsc } ; /* mux descriptor */ +int32 qty_status[ QTY_MAX ] = { 0 } ; /* QTY line status */ + /* (must be at least 32 bits) */ +int32 qty_tx_chr[ QTY_MAX ] = { 0 } ; /* QTY line output character */ + + +/* QTY data structures + + qty_dev QTY device descriptor + qty_unit QTY unit descriptor + qty_reg QTY register list +*/ + +DIB qty_dib = { DEV_QTY, INT_QTY, PI_QTY, &qty } ; + +UNIT qty_unit = + { + UDATA (&qty_svc, (UNIT_ATTABLE), 0) + } ; + +REG qty_nlreg = { DRDATA (NLINES, qty_desc.lines, 7), PV_LEFT }; + +REG qty_reg[] = /* ('alm_reg' should be similar to this except for device code related items) */ + { + { ORDATA (BUF, qty_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_QTY) }, + { FLDATA (DONE, dev_done, INT_V_QTY) }, + { FLDATA (DISABLE, dev_disable, INT_V_QTY) }, + { FLDATA (INT, int_req, INT_V_QTY) }, + + { FLDATA (MDMCTL, qty_mdm, 0) }, + { FLDATA (AUTODS, qty_auto, 0) }, + { DRDATA (POLLS, qty_polls, 32) }, + { NULL } + } ; + +MTAB qty_mod[] = + { + { UNIT_8B, 0, "7b", "7B", NULL }, + { UNIT_8B, UNIT_8B, "8b", "8B", NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &qty_desc }, + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &qty_summary }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &qty_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &qty_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES", + &qty_setnl, NULL, &qty_nlreg }, + { 0 } + } ; + +DEVICE qty_dev = + { + "QTY", &qty_unit, qty_reg, qty_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &qty_reset, + NULL, &qty_attach, &qty_detach, + &qty_dib, (DEV_DISABLE | DEV_DIS | DEV_NET) + }; + +#define DG_RETURN( status, data ) (int32)(((status) << IOT_V_REASON) | ((data) & 0x0FFFF) ) + +/* + * QTY_S_xxx QTY device status reference + * QTY_L_xxx QTY line status word reference (qty_status[]) + */ + + /*----------------------------------------------*/ + /* QTY device status */ + /*----------------------------------------------*/ + +#define QTY_S_RI 0x8000 /* Receiver Interrupt */ +#define QTY_S_TI 0x4000 /* Transmitter interrupt */ +#define QTY_S_LMASK 0x3F00 /* line mask */ +#define QTY_S_DMASK 0x00FF /* data mask (received char) */ + + + +#define QTY_MASTER_ACTIVE( desc ) ( (desc)->master ) + +#define QTY_LINE_EXTRACT( x ) (((x) & QTY_S_LMASK) >> 8) + +#define QTY_LINE_TX_CHAR( line ) qty_tx_chr[ ((line) % QTY_MAX) ] +#define QTY_LINE_RX_CHAR( line ) (qty_status[ (line) ] & QTY_S_DMASK) +#define QTY_UNIT_ACTIVE( unitp ) ( (unitp)->conn ) + +#define QTY_LINE_BITS( line, bits ) qty_status[ (line) ] & bits + +#define QTY_LINE_SET_BIT( line, bit ) qty_status[ (line) ] |= (bit) ; +#define QTY_LINE_CLEAR_BIT( line, bit ) qty_status[ (line) ] &= ~(bit) ; +#define QTY_LINE_BIT_SET( line, bit ) (qty_status[ (line) ] & (bit)) + + + /*----------------------------------------------*/ + /* QTY line status */ + /*----------------------------------------------*/ + +#define QTY_L_RXE 0x800000 /* receiver enabled? */ +#define QTY_L_RXBZ 0x400000 /* receiver busy? */ +#define QTY_L_RXDN 0x200000 /* receiver done? */ +#define QTY_L_TXE 0x080000 /* transmitter enabled? */ +#define QTY_L_TXBZ 0x040000 /* transmitter busy? */ +#define QTY_L_TXDN 0x020000 /* transmitter done? */ + +#define QTY_L_BREAK 0x008000 /* BREAK character received */ +#define QTY_L_RING 0x004000 /* Ring interrupt */ +#define QTY_L_CD 0x002000 /* Carrier Detect */ +#define QTY_L_DTR 0x001000 /* Data Terminal Ready */ + /* <0x00FF = character> */ + +#define QTY_L_LOOPBK 0x00010000 /* loopback mode */ +#define QTY_L_OVRERR 0x00020000 /* overrun error */ +#define QTY_L_FRMERR 0x00040000 /* framing error */ +#define QTY_L_PARERR 0x00080000 /* parity error */ + + +/* CD, CTS, DSR, RI */ + /* */ + +#define QTY_L_MODEM 0x0080 /* */ +#define QTY_L_TELNET 0x0040 /* */ +#define QTY_L_AUTODIS 0x0020 /* */ +#define QTY_L_PARITY +#define QTY_L_7BIT +#define QTY_L_BAUD /* <4 bits> */ + + +#define QTY_L_DMASK 0x000FF /* data mask (always 8 bits) */ + +/* Note: use at least an 'int32' for this guy */ + + /*------------------------------*/ + /* qty_tmxr_putc */ + /*------------------------------*/ + +int qty_tmxr_putc( int line, TMLN * lp, int kar ) + { + int a ; + + /*----------------------------------------------*/ + /* Send character to given QTY/telnet line. */ + /* */ + /* enter: line QTY line # */ + /* lp Telnet unit def ptr */ + /* kar character to send */ + /* */ + /* return: SCPE_OK */ + /* SCPE_STALL */ + /* SCPE_LOST */ + /*----------------------------------------------*/ + + a = tmxr_putc_ln( lp, kar ) ; + if ( a == SCPE_OK) + { + QTY_LINE_SET_BIT( line, QTY_L_TXDN ) + QTY_LINE_CLEAR_BIT( line, QTY_L_TXBZ ) + } + else if ( a == SCPE_STALL ) + { + /* + (should we try to output the buffer + and then regroup...?) + */ + QTY_LINE_SET_BIT( line, QTY_L_TXBZ ) + QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN ) + QTY_LINE_TX_CHAR( line ) = kar ; + } + else if ( a == SCPE_LOST ) + { + /* no connection - hangup? */ + QTY_LINE_SET_BIT( line, QTY_L_TXBZ ) + QTY_LINE_CLEAR_BIT( line, QTY_L_TXDN ) + QTY_LINE_TX_CHAR( line ) = kar ; + } + return ( a ) ; + } /* end of 'qty_tmxr_putc' */ + + + /*----------------------------------------------*/ + /* qty_update_rcvi */ + /*----------------------------------------------*/ + +int qty_update_rcvi( TMXR * mp ) + { + int line ; + TMLN * lp ; + int32 datum ; + int changes ; + + /*------------------------------------------------------*/ + /* Search through connected telnet lines for any input */ + /* activity. */ + /* */ + /* enter: mp master telnet qty desc ptr */ + /* */ + /* return: int change count (0 = none seen) */ + /*------------------------------------------------------*/ + + for ( changes = line = 0; line < mp->lines; ++line ) + if ( (lp=mp->ldsc+line)->conn && lp->rcve ) + if ( (datum=tmxr_getc_ln(lp)) ) + { + if ( datum & SCPE_BREAK ) + { + /* what should we do here - set QTY_L_BREAK? */ + datum = datum & 0x00FF ; + } + else + { + datum = datum & 0x00FF ; + } + /* */ + + QTY_LINE_CLEAR_BIT( line, (QTY_L_RXBZ | QTY_L_DMASK) ) ; + QTY_LINE_SET_BIT( line, (QTY_L_RXDN | datum) ) ; + ++changes ; + } + return ( changes ) ; + } /* end of 'qty_update_rcvi' */ + + + /*----------------------------------------------*/ + /* qty_update_xmti */ + /*----------------------------------------------*/ + +int qty_update_xmti( TMXR * mp ) + { + int line ; + TMLN * lp ; + int changes ; + + /*------------------------------------------------------*/ + /* Search through connected telnet lines for any de- */ + /* ferred output activity. */ + /* */ + /* enter: mp master telnet qty desc ptr */ + /* */ + /* return: int change count (0 = none seen) */ + /*------------------------------------------------------*/ + + /* any TX DONE flags set + * any TX BUSY flags set + */ + + for ( changes = line = 0; line < mp->lines; ++line ) + if ( QTY_LINE_BIT_SET(line,QTY_L_TXBZ) ) + if ( (lp=mp->ldsc+line)->conn && lp->xmte ) + { + /* why are we busy? buffer was full? */ + /* now some space available - try + * to stuff pending character in + * buffer and free up the world + */ + qty_tmxr_putc( line, lp, QTY_LINE_TX_CHAR(line) ) ; + ++changes ; + } + return ( changes ) ; + } /* end of 'qty_update_xmti' */ + + + /*----------------------------------------------*/ + /* qty_update_status */ + /*----------------------------------------------*/ + +int qty_update_status( DIB * dibp, TMXR * tmxr_desc ) + { + int line ; + int status ; + int txbusy ; + + /*----------------------------------------------*/ + /* return global device status for current qty */ + /* state. */ + /* */ + /* Receiver interrupts have higher priority */ + /* than transmitter interrupts according to DG */ + /* but this routine could be modified to use */ + /* different priority criteria. */ + /* */ + /* Round-robin polling could also be used in */ + /* some future release rather than starting */ + /* with line 0 each time. */ + /* */ + /* Return of */ + /* first waiting character, else return */ + /* of first finished line */ + /* output, else return 0. */ + /* */ + /* This routine does -not- clear input line */ + /* BZ/DN flags; caller should do this. */ + /* */ + /* Global device done and busy flags are */ + /* updated. */ + /*----------------------------------------------*/ + + for ( txbusy = status = line = 0 ; line < qty_max ; ++line ) + { + txbusy |= (QTY_LINE_BIT_SET(line,QTY_L_TXBZ)) ; + if ( QTY_LINE_BIT_SET(line,QTY_L_RXDN) ) + { + if ( ! status ) + { + status = QTY_LINE_BITS( line, QTY_S_DMASK ) | QTY_S_RI ; + status = status | (line << 8) ; + } + break ; + } + else if ( QTY_LINE_BIT_SET(line,QTY_L_TXDN) ) + { + if ( ! (status & QTY_S_RI) ) + if ( ! (status & QTY_S_RI) ) + { + status = QTY_S_TI ; + status = status | (line << 8) ; + } + } + } + /* */ + DEV_CLR_BUSY( INT_QTY ) ; + DEV_CLR_DONE( INT_QTY ) ; + if ( txbusy ) + { + DEV_SET_BUSY( INT_QTY ) ; + } + if ( status & (QTY_S_RI | QTY_S_TI) ) + { + DEV_SET_DONE( INT_QTY ) ; + } + DEV_UPDATE_INTR ; /* update final intr status */ + return ( status ) ; + } /* end of 'qty_update_status' */ + + + /*--------------------------------------------------------------*/ + /* qty_attach */ + /*--------------------------------------------------------------*/ + +t_stat qty_attach( UNIT * unitp, char * cptr ) + { + t_stat r ; + int a ; + + /* switches: A auto-disconnect + * M modem control + */ + + qty_mdm = qty_auto = 0; /* modem ctl off */ + r = tmxr_attach( &qty_desc, unitp, cptr ) ; /* attach QTY */ + if ( r != SCPE_OK ) + { + return ( r ) ; /* error! */ + } + if ( sim_switches & SWMASK('M') ) /* modem control? */ + { + qty_mdm = 1; + printf( "Modem control activated\n" ) ; + if ( sim_log ) fprintf( sim_log, "Modem control activated\n" ) ; + if ( sim_switches & SWMASK ('A') ) /* autodisconnect? */ + { + qty_auto = 1 ; + printf( "Auto disconnect activated\n" ) ; + if ( sim_log ) fprintf( sim_log, "Auto disconnect activated\n" ) ; + } + } + qty_polls = 0 ; + for ( a = 0 ; a < QTY_MAX ; ++a ) + { + /* QTY lines are always enabled - force RX and TX to 'enabled' */ + qty_status[ a ] = (QTY_L_RXE | QTY_L_TXE) ; + } + sim_activate( unitp, tmxr_poll ) ; + return ( SCPE_OK ) ; + } /* end of 'qty_attach' */ + + + /*--------------------------------------------------------------*/ + /* qty_detach */ + /*--------------------------------------------------------------*/ + +t_stat qty_detach( UNIT * unitp ) + { + sim_cancel( unitp ) ; + return ( tmxr_detach(&qty_desc,unitp) ) ; + } /* end of 'qty_detach' */ + + + /*--------------------------------------------------------------*/ + /* qty_clear */ + /*--------------------------------------------------------------*/ + +t_stat qty_clear( t_bool flag ) + { + int line ; + + for ( line = 0 ; line < qty_max ; ++line ) + { + qty_ldsc[line].xmte = 0 ; + qty_ldsc[line].rcve = 0 ; + if ( ! qty_ldsc[line].conn ) + { + qty_ldsc[line].xmte = 1 ; /* set xmt enb */ + qty_ldsc[line].rcve = 1 ; /* clr rcv enb */ + } + } + return ( SCPE_OK ) ; + } /* end of 'qty_clear' */ + + + /*----------------------------------------------*/ + /* qty_common_reset */ + /*----------------------------------------------*/ + +t_stat qty_common_reset( DIB * dibp, UNIT * unitp, DEVICE * dptr ) + { + if ((dptr->flags & DEV_DIS) == 0) + { + if (dptr == &qty_dev) alm_dev.flags |= DEV_DIS; + else qty_dev.flags |= DEV_DIS; + } + qty_clear( TRUE ) ; + DEV_CLR_BUSY( INT_QTY ) ; /* clear busy */ + DEV_CLR_DONE( INT_QTY ) ; /* clear done, int */ + DEV_UPDATE_INTR ; + if ( QTY_MASTER_ACTIVE(&qty_desc) ) + { + sim_activate( unitp, tmxr_poll ) ; + } + else + { + sim_cancel( unitp ) ; + } + return ( SCPE_OK ) ; + } /* end of 'qty_common_reset' */ + + + /*--------------------------------------------------------------*/ + /* qty_reset */ + /*--------------------------------------------------------------*/ + +t_stat qty_reset( DEVICE * dptr ) + { + return ( qty_common_reset(&qty_dib,&qty_unit,dptr) ) ; + } /* end of 'qty_reset' */ + + +/* Unit service routine + + The QTY/ALM polls to see if asynchronous activity has occurred and now + needs to be processed. The polling interval is controlled by the clock + simulator, so for most environments, it is calibrated to real time. + + The simulator assumes that software enables all of the multiplexors, + or none of them. +*/ + + /*----------------------------------------------*/ + /* qty_common_svc */ + /*----------------------------------------------*/ + +t_stat qty_common_svc( DIB * dibp, UNIT * unitp ) + { + int line ; + int newln ; + TMLN * tmlnp ; + + ++qty_polls ; /* another time 'round the track */ + newln = tmxr_poll_conn( &qty_desc ) ; /* anybody knocking at the door? */ + if ( (newln >= 0) && qty_mdm ) + if ( newln >= qty_max ) + { + return SCPE_IERR; /* WTF - sanity check failed, over? */ + } + else + { + line = newln ; /* handle modem control */ + tmlnp =&qty_ldsc[ line ] ; + tmlnp->rcve = tmlnp->xmte = 1 ; + /* do QTY_LINE_ bit fiddling and state machine + * manipulation with modem control signals + */ + } + + tmxr_poll_rx( &qty_desc ) ; /* poll input */ + qty_update_rcvi( &qty_desc ) ; /* update receiver interrupt status */ + + tmxr_poll_tx( &qty_desc ) ; /* poll output */ + qty_update_xmti( &qty_desc ) ; /* update transmitter interrupt status */ + + qty_update_status( dibp, &qty_desc ) ; /* update device status */ + + sim_activate( unitp, tmxr_poll ) ; /* restart the bubble machine */ + return ( SCPE_OK ) ; + } /* end of 'qty_common_svc' */ + + + /*--------------------------------------------------------------*/ + /* qty_svc */ + /*--------------------------------------------------------------*/ + +t_stat qty_svc( UNIT * uptr ) + { + return ( qty_common_svc(&qty_dib,uptr) ) ; + } /* end of 'qty_svc' */ + + + /*--------------------------------------------------------------*/ + /* qty */ + /*--------------------------------------------------------------*/ + +int32 qty( int32 pulse, int32 code, int32 AC ) + { + int32 iodata ; + int32 ioresult ; + int line ; + TMLN * tmlnp ; + int a ; + int kar ; + + /*--------------------------------------------------------------*/ + /* DG 4060[-compatible] "quad" multiplexor instruction handler */ + /*--------------------------------------------------------------*/ + + ioresult= qty_brkio ; /* (assume returning I/O break value */ + iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */ + switch ( code ) + { + case ioNIO : /* */ + break ; + + case ioDIA : /* get current QTY status */ + iodata = qty_update_status( &qty_dib, &qty_desc ) ; + if ( iodata & QTY_S_RI ) + { /* clear line's input buffer */ + QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(iodata)), (QTY_L_RXBZ | QTY_L_RXDN) ) + /* + character masking ; + parity checking ; + parity generating ; + */ + } + qty_update_status( &qty_dib, &qty_desc ) ; + break ; + + case ioDOA : /* send character to QTY */ + line = QTY_LINE_EXTRACT( AC ) ; + if ( line < qty_max ) + if ( QTY_LINE_BIT_SET(line,QTY_L_TXE) ) + { + /* + perform any character translation: + 7 bit/ 8 bit + parity generation + */ + kar = AC & ((qty_unit.flags & UNIT_8B)? 0377: 0177) ; + /* do any parity calculations also */ + + tmlnp = &qty_ldsc[ line ] ; + a = qty_tmxr_putc( line, tmlnp, kar ) ; + if ( a != SCPE_OK) + { + /* do anything at this point? */ + } + qty_update_status( &qty_dib, &qty_desc ) ; + } + break ; + + case ioDIB : /* no QTY function - return bus noise in AC */ + break ; + + case ioDOB : /* clear QTY output channel busy and done flag */ + QTY_LINE_CLEAR_BIT( (QTY_LINE_EXTRACT(AC)), (QTY_L_TXBZ | QTY_L_TXDN) ) + qty_update_status( &qty_dib, &qty_desc ) ; + break ; + + case ioDIC : /* no QTY function - return bus noise in AC */ + break ; + + case ioDOC : /* no QTY function - ignore */ + break ; + + case ioSKP : /* I/O skip test - should never come here */ + break ; + + default : + /* */ + break ; + } + + switch ( pulse ) + { + case iopN : /* */ + break ; + + case iopS : /* */ + break ; + + case iopP : /* */ + break ; + + case iopC : + qty_update_status( &qty_dib, &qty_desc ) ; + break ; + + default : + /* */ + break ; + } + + return ( DG_RETURN( ioresult, iodata ) ) ; + } /* end of 'qty' */ + + /*--------------------------------------------------------------*/ + /* qty_summary */ + /*--------------------------------------------------------------*/ + +t_stat qty_summary( FILE * st, UNIT * uptr, int32 val, void * desc ) + { + int32 i, t ; + + for (i = t = 0 ; i < qty_desc.lines ; ++i ) + if ( qty_ldsc[i].conn ) + { + ++t ; + } + fprintf( st, "%d connection%s", t, ((t)? "s" : "") ) ; + return ( SCPE_OK ) ; + } /* end of 'qty_summ' */ + + + /*--------------------------------------------------------------*/ + /* qty_show */ + /*--------------------------------------------------------------*/ + +t_stat qty_show( FILE * st, UNIT * uptr, int32 val, void * desc ) + { + int32 i, t ; + + for (i = t = 0 ; i < qty_desc.lines ; ++i ) + if ( qty_ldsc[i].conn ) + { + t = 1; + if ( val ) + { + tmxr_fconns( st, &qty_ldsc[i], i ) ; + } + else + { + tmxr_fstats( st, &qty_ldsc[i], i ) ; + } + } + if ( t == 0 ) fprintf( st, "none connected\n" ) ; + return ( SCPE_OK ) ; + } /* end of 'qty_show' */ + + + /*--------------------------------------------------------------*/ + /* qty_setnl */ + /*--------------------------------------------------------------*/ + +t_stat qty_setnl( UNIT * uptr, int32 val, char * cptr, void * desc ) + { + int32 newln, i, t ; + + t_stat r ; + if ( cptr == NULL ) + { + return ( SCPE_ARG ) ; + } + newln = (int32) get_uint( cptr, 10, QTY_MAX, &r ) ; + if ( (r != SCPE_OK) || (newln == qty_desc.lines) ) + { + return ( r ) ; + } + if ( (newln == 0) || (newln > QTY_MAX) ) + { + return ( SCPE_ARG ) ; + } + if ( newln < qty_desc.lines ) + { + for ( i = newln, t = 0 ; i < qty_desc.lines ; ++i ) + { + t = t | qty_ldsc[i].conn ; + } + if ( t && ! get_yn("This will disconnect users; proceed [N]?", FALSE) ) + { + return ( SCPE_OK ) ; + } + for ( i = newln ; i < qty_desc.lines ; ++i ) + { + if ( qty_ldsc[i].conn ) + { /* reset line */ + tmxr_msg( qty_ldsc[i].conn, "\r\nOperator disconnected line\r\n" ) ; + tmxr_reset_ln( &qty_ldsc[i] ) ; + } + qty_clear( TRUE ) ; /* reset mux */ + } + } + qty_max = qty_desc.lines = newln ; + /* Huh, I don't understand this yet... + qty_max = ((qty_dev.flags & DEV_DIS)? 0 : (qty_desc.lines / QTY_MAX)) ; + */ + return ( SCPE_OK ) ; + } /* end of 'qty_setnl' */ + + +/*----------------------------------------------------------------------*/ +/* ALM [425x-compatible] multiplexor */ +/*----------------------------------------------------------------------*/ + +/* + * device code: 034 [primary], + * 074 [secondary] + * interrupt mask: B14 [000002] + * ASM mnemonic: ALM + * + * ALM [4255-4258] I/O instructions + * + * DIA read line and section requesting service + * DOA select line and section (lines 0-255, 8-bits) + rcvr/xmit + * DIB receive data + * DOB 00 transmit data + * 01 transmit BREAK + * 10 set modem control status + * 11 + * DIC read receiver or modem status + * DOC 00 control line section and diag mode + * 01 + * 10 specify line characteristics + * 11 + * + * undocumented DG "features": + * + * NIOS sets board offline + * NIOC sets board online + * Modem control signal state change can signal interrupt + * explicit line select with DOA + * implicit line select with DIA + * + * We support 64 lines maximum in this release although some ALM's could + * theoretically support up to 256. + */ + + +DIB alm_dib = { DEV_ALM, INT_ALM, PI_ALM, &alm } ; +UNIT alm_unit = + { + UDATA (&alm_svc, (UNIT_ATTABLE), 0) + } ; + +REG alm_reg[] = /* ('qty_reg' should be similar to this except for device code related items) */ + { + { ORDATA (BUF, alm_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_ALM) }, + { FLDATA (DONE, dev_done, INT_V_ALM) }, + { FLDATA (DISABLE, dev_disable, INT_V_ALM) }, + { FLDATA (INT, int_req, INT_V_ALM) }, + + { FLDATA (MDMCTL, qty_mdm, 0) }, + { FLDATA (AUTODS, qty_auto, 0) }, + { DRDATA (POLLS, qty_polls, 32) }, + { NULL } + } ; + +DEVICE alm_dev = + { + "ALM", &alm_unit, alm_reg, qty_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &alm_reset, + NULL, &qty_attach, &qty_detach, + &alm_dib, (DEV_DISABLE | DEV_NET) + } ; + +int alm_section = -1 ; /* current line "section" (0 = RCV, 1 = XMT) */ +int alm_line = -1 ; /* current line [0-63] */ +int alm_diag_mode = 0 ; /* */ +int alm_line_mask = 0x003F ; /* maximum of 64 lines in this rev */ + + +#define ALM_LINE_EXTRACT( x ) (((x) >> 1) & alm_line_mask) +#define ALM_SECT_EXTRACT( x ) ((x) & 0x0001) + + + /*--------------------------------------------------------------*/ + /* alm_reset */ + /*--------------------------------------------------------------*/ + +t_stat alm_reset( DEVICE * dptr ) + { + return ( qty_common_reset(&alm_dib,&alm_unit,dptr) ) ; + } /* end of 'alm_reset' */ + + + /*--------------------------------------------------------------*/ + /* alm_svc */ + /*--------------------------------------------------------------*/ + +t_stat alm_svc( UNIT * uptr ) + { + return ( qty_common_svc(&alm_dib,uptr) ) ; + } /* end of 'alm_svc' */ + + + /*--------------------------------------------------------------*/ + /* alm */ + /*--------------------------------------------------------------*/ + +int32 alm( int32 pulse, int32 code, int32 AC ) + { + int32 iodata ; + int32 ioresult ; + TMLN * tmlnp ; + int a ; + int kar ; + + /*--------------------------------------------------------------*/ + /* DG 425x[-compatible] "ALM" multiplexor instruction handler */ + /*--------------------------------------------------------------*/ + + ioresult= qty_brkio ; /* (assume returning I/O break value */ + iodata = 0 ; /* (assume 16-bit Nova/Eclipse bus) */ + switch ( code ) + { + case ioNIO : /* */ + break ; + + case ioDIA : /* read line and section requesting service */ + iodata = qty_update_status( &alm_dib, &qty_desc ) ; + alm_line = (QTY_LINE_EXTRACT(iodata) & alm_line_mask) ; + /* (mask with 'alm_line_mask' in case ALM mask is different than QTY */ + alm_section = 0 ; + if ( ! ( iodata & QTY_S_RI) ) + if ( iodata & QTY_S_TI ) + { + alm_section = 1 ; /* receiver quiet - transmitter done */ + } + iodata = (alm_line << 1) | alm_section ; + break ; + + case ioDOA : /* set line and section */ + alm_section = ALM_SECT_EXTRACT( AC ) ; + alm_line = ALM_LINE_EXTRACT( AC ) ; + break ; + + case ioDIB : /* no ALM function - return bus noise in AC */ + if ( alm_line < qty_max ) + { + iodata = QTY_LINE_RX_CHAR( alm_line ) ; + } + break ; + + case ioDOB : /* output and modem control functions */ + switch ( (AC >> 14) & 03 ) + { + case 00 : /* transmit data */ + if ( alm_line < qty_max ) + if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) ) + { + /* + perform any character translation: + 7 bit/ 8 bit + parity generation + */ + kar = AC & ((alm_unit.flags & UNIT_8B)? 0377: 0177) ; + /* do any parity calculations also */ + + tmlnp = &qty_ldsc[ alm_line ] ; + a = qty_tmxr_putc( alm_line, tmlnp, kar ) ; + if ( a != SCPE_OK) + { + /* do anything at this point? */ + } + qty_update_status( &alm_dib, &qty_desc ) ; + } + break ; + + case 01 : /* transmit break */ + if ( alm_line < qty_max ) + if ( QTY_LINE_BIT_SET(alm_line,QTY_L_TXE) ) + { + tmlnp = &qty_ldsc[ alm_line ] ; + /* + a = qty_tmxr_putc( alm_line, tmlnp, kar ) ; + if ( a != SCPE_OK) + { + } + */ + qty_update_status( &alm_dib, &qty_desc ) ; + } + break ; + + case 02 : /* set modem control status */ + break ; + + case 03 : /* unused */ + break ; + } + break ; + + case ioDIC : /* get modem or receiver status */ + if ( alm_line < qty_max ) + if ( alm_section ) + { + /* get modem section status */ + if ( qty_ldsc[ alm_line ].xmte ) + { + iodata = 0035 ; /* set CD, CTS, DSR, MDM flags */ + } + } + else + { + /* get receiver section status */ + iodata = 0 ; /* receiver error status - no errors by default */ + } + break ; + + case ioDOC : /* set line attributes */ + switch ( (AC >> 14) & 03 ) + { + case 00 : /* control line section */ + break ; + + case 01 : /* unused */ + break ; + + case 02 : /* set line characteristics */ + break ; + + case 03 : /* unused */ + break ; + } + break ; + + case ioSKP : /* I/O skip test - should never come here */ + break ; + + default : + /* */ + break ; + } + + switch ( pulse ) + { + case iopN : /* */ + break ; + + case iopS : /* set device busy + * set all lines on board offline + * clear each line's done + * clear internal system + * clear device busy + */ + for ( a = 0 ; a < qty_max ; ++a ) + if ( 1 /* (not yet optimized) */ ) + { + QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ; + } + qty_update_status( &alm_dib, &qty_desc ) ; + break ; + + case iopP : /* stop clock for all boards in off-line mode */ + break ; + + case iopC : + for ( a = 0 ; a < qty_max ; ++a ) + if ( 1 /* (not yet optimized) */ ) + { + QTY_LINE_CLEAR_BIT( a, (QTY_L_RXBZ | QTY_L_RXDN | QTY_L_TXBZ | QTY_L_TXDN) ) ; + } + qty_update_status( &alm_dib, &qty_desc ) ; + break ; + + default : + /* */ + break ; + } + + return ( DG_RETURN( ioresult, iodata ) ) ; + } /* end of 'alm' */ diff --git a/NOVA/nova_sys.c b/NOVA/nova_sys.c new file mode 100644 index 0000000..54dfc74 --- /dev/null +++ b/NOVA/nova_sys.c @@ -0,0 +1,1142 @@ +/* nova_sys.c: NOVA simulator interface + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 04-Jul-07 BKR DEC's IOF/ION changed to DG's INTDS/INTEN mnemonic, + Fixed QTY/ADCV device name, + RDSW changed to DDG's READS mnemonic, + fixed/enhanced 'load' command for DG-compatible binary tape format + 26-Mar-04 RMS Fixed warning with -std=c99 + 14-Jan-04 BKR Added support for QTY and ALM + 04-Jan-04 RMS Fixed 64b issues found by VMS 8.1 + 24-Nov-03 CEO Added symbolic support for LEF instruction + 17-Sep-01 RMS Removed multiconsole support + 31-May-01 RMS Added multiconsole support + 14-Mar-01 RMS Revised load/dump interface (again) + 22-Dec-00 RMS Added second terminal support + 10-Dec-00 RMS Added Eclipse support + 08-Dec-00 BKR Added plotter support + 30-Oct-00 RMS Added support for examine to file + 15-Oct-00 RMS Added stack, byte, trap instructions + 14-Apr-99 RMS Changed t_addr to unsigned + 27-Oct-98 RMS V2.4 load interface + 24-Sep-97 RMS Fixed bug in device name table (found by Charles Owen) +*/ + +#include "nova_defs.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE plt_dev; +extern DEVICE tti_dev; +extern DEVICE tto_dev; +extern DEVICE tti1_dev; +extern DEVICE tto1_dev; +extern DEVICE clk_dev; +extern DEVICE lpt_dev; +extern DEVICE dkp_dev; +extern DEVICE dsk_dev; +extern DEVICE mta_dev; +extern DEVICE qty_dev; +extern DEVICE alm_dev; +extern REG cpu_reg[]; +extern uint16 M[]; +extern int32 saved_PC; +extern int32 AMASK; + +#if defined (ECLIPSE) + +extern DEVICE map_dev; +extern DEVICE fpu_dev; +extern DEVICE pit_dev; +extern int32 Usermap; +extern int32 MapStat; + +#endif + +extern int32 sim_switches; + + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +#if defined (ECLIPSE) +char sim_name[] = "ECLIPSE"; +#else +char sim_name[] = "NOVA"; +#endif + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &cpu_dev, +#if defined (ECLIPSE) + &map_dev, + &fpu_dev, + &pit_dev, +#endif + &ptr_dev, + &ptp_dev, + &tti_dev, + &tto_dev, + &tti1_dev, + &tto1_dev, + &clk_dev, + &plt_dev, + &lpt_dev, + &dsk_dev, + &dkp_dev, + &mta_dev, + &qty_dev, + &alm_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O instruction", + "HALT instruction", + "Breakpoint", + "Nested indirect address limit exceeded", + "Nested indirect interrupt or trap address limit exceeded", + "Read breakpoint", + "Write breakpoint" + }; + +/* Binary loader + + Loader format consists of blocks, optionally preceded, separated, and + followed by zeroes. Each block consists of: + + lo_count + hi_count + lo_origin + hi_origin + lo_checksum + hi_checksum + lo_data byte --- + hi_data byte | + : > -count words + lo_data byte | + hi_data byte --- + + If the word count is [0,-20], then the block is normal data. + If the word count is [-21,-n], then the block is repeated data. + If the word count is 1, the block is the start address. + If the word count is >1, the block is an error block. + +Notes: + 'start' block terminates loading. + 'start' block starting address 1B0 = do not auto-start, 0B0 = auto-start. + 'start' block starting address is saved in 'save_PC' so a "continue" + should start the program. + + specify -i switch ignores checksum errors + + +internal state machine: + + 0,1 get byte count (low and high), ignore leader bytes (<000>) + 2,3 get origin + 4,5 get checksum + 6,7 process data block + 8 process 'ignore' (error) block +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 data, csum, count, state, i; +int32 origin; +int pos ; +int block_start ; +int done ; + +if ((*cptr != 0) || (flag != 0)) + return ( SCPE_ARG ) ; +state = 0; +block_start = -1 ; +done = 0 ; +for ( pos = 0 ; (! done) && ((i=getc(fileref)) != EOF) ; ++pos ) + { + i &= 0x00FF ; /* (insure no sign extension) */ + switch (state) { + case 0: /* leader */ + count = i; + state = (count != 0) ; + if ( state ) + block_start = pos ; + break; + case 1: /* high count */ + csum = count = (i << 8) | count ; + state = 2; + break; + case 2: /* low origin */ + origin = i; + state = 3; + break ; + + case 3: /* high origin */ + origin = (i << 8) | origin; + csum = csum + origin; + state = 4; + break; + case 4: /* low checksum */ + csum = csum + i; + state = 5; + break; + case 5: /* high checksum */ + csum = (csum + (i << 8)) & 0xFFFF ; + if (count == 1) + { + /* 'start' block */ + /* do any auto-start check or inhibit check */ + saved_PC = (origin & 077777) ; /* 0B0 = auto-start program */ + /* 1B0 = do not auto start */ + state = 0 ; /* indicate okay state */ + done = 1 ; /* we're done! */ + if ( ! (origin & 0x8000) ) + { + printf( "auto start @ %05o \n", (origin & 0x7FFF) ) ; + } + break ; + } + if ( ((count & 0x8000) == 0) && (count > 1)) + { + /* 'ignore' block */ + state = 8 ; + } + /* 'data' or 'repeat' block */ + count = 0200000 - count ; + if ( count <= 020 ) + { + /* 'data' block */ + state = 6 ; + break ; + } + /* 'repeat' block (multiple data) */ + + if (count > 020) { /* large block */ + for (count = count - 1; count > 1; count--) { + if (origin >= AMASK /* MEMSIZE? */) + { + return ( SCPE_NXM ); + } + M[origin] = data; + origin = origin + 1; + } + state = 0 ; + } + state = 0; + break; + case 6: /* low data */ + data = i; + state = 7; + break; + case 7: /* high data */ + data = (i << 8) | data; + csum = (csum + data) & 0xFFFF ; + + if (origin >= AMASK /* MEMSIZE? */) + return SCPE_NXM; + M[origin] = data; + origin = origin + 1; + count = count - 1; + if (count == 0) { + if ( csum ) + { + printf( "checksum error: block start at %d [0x%x] \n", block_start, block_start ) ; + printf( "calculated: 0%o [0x%4x]\n", csum, csum ) ; + if ( ! (sim_switches & SWMASK('I')) ) + return SCPE_CSUM; + } + state = 0; + break; + } + state = 6; + break; + case 8: /* error (ignore) block */ + if (i == 0377) + state = 0; /* (wait for 'RUBOUT' char) */ + break; + } /* end switch */ + } /* end while */ + +/* Ok to find end of tape between blocks or in error state */ + +return ( ((state == 0) || (state == 8)) ? SCPE_OK : SCPE_FMT ) ; +} + + +/* Symbol tables */ + +#define I_V_FL 18 /* flag bits */ +#define I_M_FL 037 /* flag width */ +#define I_V_NPN 000 /* no operands */ +#define I_V_R 001 /* reg */ +#define I_V_D 002 /* device */ +#define I_V_RD 003 /* reg,device */ +#define I_V_M 004 /* mem addr */ +#define I_V_RM 005 /* reg,mem addr */ +#define I_V_RR 006 /* operate */ +#define I_V_BY 007 /* Nova byte pointer */ +#define I_V_2AC 010 /* reg,reg */ +#define I_V_RSI 011 /* reg,short imm */ +#define I_V_LI 012 /* long imm */ +#define I_V_RLI 013 /* reg,long imm */ +#define I_V_LM 014 /* long mem addr */ +#define I_V_RLM 015 /* reg,long mem addr */ +#define I_V_FRM 016 /* flt reg,long mem addr */ +#define I_V_FST 017 /* flt long mem, status */ +#define I_V_XP 020 /* XOP */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_R (I_V_R << I_V_FL) +#define I_D (I_V_D << I_V_FL) +#define I_RD (I_V_RD << I_V_FL) +#define I_M (I_V_M << I_V_FL) +#define I_RM (I_V_RM << I_V_FL) +#define I_RR (I_V_RR << I_V_FL) +#define I_BY (I_V_BY << I_V_FL) +#define I_2AC (I_V_2AC << I_V_FL) +#define I_RSI (I_V_RSI << I_V_FL) +#define I_LI (I_V_LI << I_V_FL) +#define I_RLI (I_V_RLI << I_V_FL) +#define I_LM (I_V_LM << I_V_FL) +#define I_RLM (I_V_RLM << I_V_FL) +#define I_FRM (I_V_FRM << I_V_FL) +#define I_FST (I_V_FST << I_V_FL) +#define I_XP (I_V_XP << I_V_FL) + +static const int32 masks[] = { + 0177777, 0163777, 0177700, 0163700, + 0174000, 0160000, 0103770, 0163477, + 0103777, 0103777, 0177777, 0163777, + 0176377, 0162377, 0103777, 0163777, + 0100077 + }; + +static const char *opcode[] = { + "JMP", "JSR", "ISZ", "DSZ", + "LDA", "STA", +#if defined (ECLIPSE) + "ADI", "SBI", "DAD", "DSB", + "IOR", "XOR", "ANC", "XCH", + "SGT", "SGE", "LSH", "DLSH", + "HXL", "HXR", "DHXL", "DHXR", + "BTO", "BTZ", "SBZ", "SZBO", + "LOB", "LRB", "COB", "LDB", + "STB", "PSH", "POP", + "LMP", "SYC", + "PSHR", "POPB", "BAM", "POPJ", + "RTN", "BLM", "DIVX", + "MUL", "MULS", "DIV", "DIVS", + "SAVE", "RSTR", + "XOP", + "FAS", "FAD", "FSS", "FSD", + "FMS", "FMD", "FDS", "FDD", + "FAMS", "FAMD", "FSMS", "FSMD", + "FMMS", "FMMD", "FDMS", "FDMD", + "FLDS", "FLDD", "FSTS", "FSTD", + "FLAS", "FLMD", "FFAS", "FFMD", + "FNOM", "FRH", "FAB", "FNEG", + "FSCAL", "FEXP", "FINT", "FHLV", + "FNS", "FSA", "FSEQ", "FSNE", + "FSLT", "FSGE", "FSLE", "FSGT", + "FSNM", "FSND", "FSNU", "FSNUD", + "FSNO", "FSNOD", "FSNUO", "FSNER", + "FSST", "FLST", + "FTE", "FTD", "FCLE", + "FPSH", "FPOP", + "FCMP", "FMOV", + "CMV", "CMP", "CTR", "CMT", + "EJMP", "EJSR", "EISZ", "EDSZ", + "ELDA", "ESTA", "ELEF", + "ELDB", "ESTB", "DSPA", + "PSHJ", "CLM", "SNB", + "MSP", "XCT", "HLV", + "IORI", "XORI", "ANDI", "ADDI", +#endif + "COM", "COMZ", "COMO", "COMC", + "COML", "COMZL", "COMOL", "COMCL", + "COMR", "COMZR", "COMOR", "COMCR", + "COMS", "COMZS", "COMOS", "COMCS", + "COM#", "COMZ#", "COMO#", "COMC#", + "COML#", "COMZL#", "COMOL#", "COMCL#", + "COMR#", "COMZR#", "COMOR#", "COMCR#", + "COMS#", "COMZS#", "COMOS#", "COMCS#", + "NEG", "NEGZ", "NEGO", "NEGC", + "NEGL", "NEGZL", "NEGOL", "NEGCL", + "NEGR", "NEGZR", "NEGOR", "NEGCR", + "NEGS", "NEGZS", "NEGOS", "NEGCS", + "NEG#", "NEGZ#", "NEGO#", "NEGC#", + "NEGL#", "NEGZL#", "NEGOL#", "NEGCL#", + "NEGR#", "NEGZR#", "NEGOR#", "NEGCR#", + "NEGS#", "NEGZS#", "NEGOS#", "NEGCS#", + "MOV", "MOVZ", "MOVO", "MOVC", + "MOVL", "MOVZL", "MOVOL", "MOVCL", + "MOVR", "MOVZR", "MOVOR", "MOVCR", + "MOVS", "MOVZS", "MOVOS", "MOVCS", + "MOV#", "MOVZ#", "MOVO#", "MOVC#", + "MOVL#", "MOVZL#", "MOVOL#", "MOVCL#", + "MOVR#", "MOVZR#", "MOVOR#", "MOVCR#", + "MOVS#", "MOVZS#", "MOVOS#", "MOVCS#", + "INC", "INCZ", "INCO", "INCC", + "INCL", "INCZL", "INCOL", "INCCL", + "INCR", "INCZR", "INCOR", "INCCR", + "INCS", "INCZS", "INCOS", "INCCS", + "INC#", "INCZ#", "INCO#", "INCC#", + "INCL#", "INCZL#", "INCOL#", "INCCL#", + "INCR#", "INCZR#", "INCOR#", "INCCR#", + "INCS#", "INCZS#", "INCOS#", "INCCS#", + "ADC", "ADCZ", "ADCO", "ADCC", + "ADCL", "ADCZL", "ADCOL", "ADCCL", + "ADCR", "ADCZR", "ADCOR", "ADCCR", + "ADCS", "ADCZS", "ADCOS", "ADCCS", + "ADC#", "ADCZ#", "ADCO#", "ADCC#", + "ADCL#", "ADCZL#", "ADCOL#", "ADCCL#", + "ADCR#", "ADCZR#", "ADCOR#", "ADCCR#", + "ADCS#", "ADCZS#", "ADCOS#", "ADCCS#", + "SUB", "SUBZ", "SUBO", "SUBC", + "SUBL", "SUBZL", "SUBOL", "SUBCL", + "SUBR", "SUBZR", "SUBOR", "SUBCR", + "SUBS", "SUBZS", "SUBOS", "SUBCS", + "SUB#", "SUBZ#", "SUBO#", "SUBC#", + "SUBL#", "SUBZL#", "SUBOL#", "SUBCL#", + "SUBR#", "SUBZR#", "SUBOR#", "SUBCR#", + "SUBS#", "SUBZS#", "SUBOS#", "SUBCS#", + "ADD", "ADDZ", "ADDO", "ADDC", + "ADDL", "ADDZL", "ADDOL", "ADDCL", + "ADDR", "ADDZR", "ADDOR", "ADDCR", + "ADDS", "ADDZS", "ADDOS", "ADDCS", + "ADD#", "ADDZ#", "ADDO#", "ADDC#", + "ADDL#", "ADDZL#", "ADDOL#", "ADDCL#", + "ADDR#", "ADDZR#", "ADDOR#", "ADDCR#", + "ADDS#", "ADDZS#", "ADDOS#", "ADDCS#", + "AND", "ANDZ", "ANDO", "ANDC", + "ANDL", "ANDZL", "ANDOL", "ANDCL", + "ANDR", "ANDZR", "ANDOR", "ANDCR", + "ANDS", "ANDZS", "ANDOS", "ANDCS", + "AND#", "ANDZ#", "ANDO#", "ANDC#", + "ANDL#", "ANDZL#", "ANDOL#", "ANDCL#", + "ANDR#", "ANDZR#", "ANDOR#", "ANDCR#", + "ANDS#", "ANDZS#", "ANDOS#", "ANDCS#", + "INTEN", "INTDS", + "READS", "INTA", "MSKO", "IORST", "HALT", +#if !defined (ECLIPSE) + "MUL", "DIV", "MULS", "DIVS", + "PSHA", "POPA", "SAV", "RET", + "MTSP", "MTFP", "MFSP", "MFFP", + "LDB", "STB", +#endif + "NIO", "NIOS", "NIOC", "NIOP", + "DIA", "DIAS", "DIAC", "DIAP", + "DOA", "DOAS", "DOAC", "DOAP", + "DIB", "DIBS", "DIBC", "DIBP", + "DOB", "DOBS", "DOBC", "DOBP", + "DIC", "DICS", "DICC", "DICP", + "DOC", "DOCS", "DOCC", "DOCP", + "SKPBN", "SKPBZ", "SKPDN", "SKPDZ", +#if defined (ECLIPSE) + "LEF", "LEF", "LEF", "LEF", +#endif + NULL + }; + +static const int32 opc_val[] = { + 0000000+I_M, 0004000+I_M, 0010000+I_M, 0014000+I_M, + 0020000+I_RM, 0040000+I_RM, +#if defined (ECLIPSE) + 0100010+I_RSI, 0100110+I_RSI, 0100210+I_2AC, 0100310+I_2AC, + 0100410+I_2AC, 0100510+I_2AC, 0100610+I_2AC, 0100710+I_2AC, + 0101010+I_2AC, 0101110+I_2AC, 0101210+I_RSI, 0101310+I_RSI, + 0101410+I_RSI, 0101510+I_RSI, 0101610+I_RSI, 0101710+I_RSI, + 0102010+I_2AC, 0102110+I_2AC, 0102210+I_2AC, 0102310+I_2AC, + 0102410+I_2AC, 0102510+I_2AC, 0102610+I_2AC, 0102710+I_2AC, + 0103010+I_2AC, 0103110+I_2AC, 0103210+I_2AC, + 0113410+I_NPN, 0103510+I_2AC, + 0103710+I_NPN, 0107710+I_NPN, 0113710+I_NPN, 0117710+I_NPN, + 0127710+I_NPN, 0133710+I_NPN, 0137710+I_NPN, + 0143710+I_NPN, 0147710+I_NPN, 0153710+I_NPN, 0157710+I_NPN, + 0163710+I_LI, 0167710+I_NPN, + 0100030+I_XP, + 0100050+I_2AC, 0100150+I_2AC, 0100250+I_2AC, 0100350+I_2AC, + 0100450+I_2AC, 0100550+I_2AC, 0100650+I_2AC, 0100750+I_2AC, + 0101050+I_FRM, 0101150+I_FRM, 0101250+I_FRM, 0101350+I_FRM, + 0101450+I_FRM, 0101550+I_FRM, 0101650+I_FRM, 0101750+I_FRM, + 0102050+I_FRM, 0102150+I_FRM, 0102250+I_FRM, 0102350+I_FRM, + 0102450+I_2AC, 0102550+I_FRM, 0102650+I_2AC, 0102750+I_FRM, + 0103050+I_R, 0123050+I_R, 0143050+I_R, 0163050+I_R, + 0103150+I_R, 0123150+I_R, 0143150+I_R, 0163150+I_R, + 0103250+I_NPN, 0107250+I_NPN, 0113250+I_NPN, 0117250+I_NPN, + 0123250+I_NPN, 0127250+I_NPN, 0133250+I_NPN, 0137250+I_NPN, + 0143250+I_NPN, 0147250+I_NPN, 0153250+I_NPN, 0157250+I_NPN, + 0163250+I_NPN, 0167250+I_NPN, 0173250+I_NPN, 0177250+I_NPN, + 0103350+I_FST, 0123350+I_FST, + 0143350+I_NPN, 0147350+I_NPN, 0153350+I_NPN, + 0163350+I_NPN, 0167350+I_NPN, + 0103450+I_2AC, 0103550+I_2AC, + 0153650+I_NPN, 0157650+I_NPN, 0163650+I_NPN, 0167650+I_NPN, + 0102070+I_LM, 0106070+I_LM, 0112070+I_LM, 0116070+I_LM, + 0122070+I_RLM, 0142070+I_RLM, 0162070+I_RLM, + 0102170+I_RLM, 0122170+I_RLM, 0142170+I_RLM, + 0102270+I_LM, 0102370+I_2AC, 0102770+I_2AC, + 0103370+I_R, 0123370+I_R, 0143370+I_R, + 0103770+I_RLI, 0123770+I_RLI, 0143770+I_RLI, 0163770+I_RLI, +#endif + 0100000+I_RR, 0100020+I_RR, 0100040+I_RR, 0100060+I_RR, + 0100100+I_RR, 0100120+I_RR, 0100140+I_RR, 0100160+I_RR, + 0100200+I_RR, 0100220+I_RR, 0100240+I_RR, 0100260+I_RR, + 0100300+I_RR, 0100320+I_RR, 0100340+I_RR, 0100360+I_RR, + 0100010+I_RR, 0100030+I_RR, 0100050+I_RR, 0100070+I_RR, + 0100110+I_RR, 0100130+I_RR, 0100150+I_RR, 0100170+I_RR, + 0100210+I_RR, 0100230+I_RR, 0100250+I_RR, 0100270+I_RR, + 0100310+I_RR, 0100330+I_RR, 0100350+I_RR, 0100370+I_RR, + 0100400+I_RR, 0100420+I_RR, 0100440+I_RR, 0100460+I_RR, + 0100500+I_RR, 0100520+I_RR, 0100540+I_RR, 0100560+I_RR, + 0100600+I_RR, 0100620+I_RR, 0100640+I_RR, 0100660+I_RR, + 0100700+I_RR, 0100720+I_RR, 0100740+I_RR, 0100760+I_RR, + 0100410+I_RR, 0100430+I_RR, 0100450+I_RR, 0100470+I_RR, + 0100510+I_RR, 0100530+I_RR, 0100550+I_RR, 0100570+I_RR, + 0100610+I_RR, 0100630+I_RR, 0100650+I_RR, 0100670+I_RR, + 0100710+I_RR, 0100730+I_RR, 0100750+I_RR, 0100770+I_RR, + 0101000+I_RR, 0101020+I_RR, 0101040+I_RR, 0101060+I_RR, + 0101100+I_RR, 0101120+I_RR, 0101140+I_RR, 0101160+I_RR, + 0101200+I_RR, 0101220+I_RR, 0101240+I_RR, 0101260+I_RR, + 0101300+I_RR, 0101320+I_RR, 0101340+I_RR, 0101360+I_RR, + 0101010+I_RR, 0101030+I_RR, 0101050+I_RR, 0101070+I_RR, + 0101110+I_RR, 0101130+I_RR, 0101150+I_RR, 0101170+I_RR, + 0101210+I_RR, 0101230+I_RR, 0101250+I_RR, 0101270+I_RR, + 0101310+I_RR, 0101330+I_RR, 0101350+I_RR, 0101370+I_RR, + 0101400+I_RR, 0101420+I_RR, 0101440+I_RR, 0101460+I_RR, + 0101500+I_RR, 0101520+I_RR, 0101540+I_RR, 0101560+I_RR, + 0101600+I_RR, 0101620+I_RR, 0101640+I_RR, 0101660+I_RR, + 0101700+I_RR, 0101720+I_RR, 0101740+I_RR, 0101760+I_RR, + 0101410+I_RR, 0101430+I_RR, 0101450+I_RR, 0101470+I_RR, + 0101510+I_RR, 0101530+I_RR, 0101550+I_RR, 0101570+I_RR, + 0101610+I_RR, 0101630+I_RR, 0101650+I_RR, 0101670+I_RR, + 0101710+I_RR, 0101730+I_RR, 0101750+I_RR, 0101770+I_RR, + 0102000+I_RR, 0102020+I_RR, 0102040+I_RR, 0102060+I_RR, + 0102100+I_RR, 0102120+I_RR, 0102140+I_RR, 0102160+I_RR, + 0102200+I_RR, 0102220+I_RR, 0102240+I_RR, 0102260+I_RR, + 0102300+I_RR, 0102320+I_RR, 0102340+I_RR, 0102360+I_RR, + 0102010+I_RR, 0102030+I_RR, 0102050+I_RR, 0102070+I_RR, + 0102110+I_RR, 0102130+I_RR, 0102150+I_RR, 0102170+I_RR, + 0102210+I_RR, 0102230+I_RR, 0102250+I_RR, 0102270+I_RR, + 0102310+I_RR, 0102330+I_RR, 0102350+I_RR, 0102370+I_RR, + 0102400+I_RR, 0102420+I_RR, 0102440+I_RR, 0102460+I_RR, + 0102500+I_RR, 0102520+I_RR, 0102540+I_RR, 0102560+I_RR, + 0102600+I_RR, 0102620+I_RR, 0102640+I_RR, 0102660+I_RR, + 0102700+I_RR, 0102720+I_RR, 0102740+I_RR, 0102760+I_RR, + 0102410+I_RR, 0102430+I_RR, 0102450+I_RR, 0102470+I_RR, + 0102510+I_RR, 0102530+I_RR, 0102550+I_RR, 0102570+I_RR, + 0102610+I_RR, 0102630+I_RR, 0102650+I_RR, 0102670+I_RR, + 0102710+I_RR, 0102730+I_RR, 0102750+I_RR, 0102770+I_RR, + 0103000+I_RR, 0103020+I_RR, 0103040+I_RR, 0103060+I_RR, + 0103100+I_RR, 0103120+I_RR, 0103140+I_RR, 0103160+I_RR, + 0103200+I_RR, 0103220+I_RR, 0103240+I_RR, 0103260+I_RR, + 0103300+I_RR, 0103320+I_RR, 0103340+I_RR, 0103360+I_RR, + 0103010+I_RR, 0103030+I_RR, 0103050+I_RR, 0103070+I_RR, + 0103110+I_RR, 0103130+I_RR, 0103150+I_RR, 0103170+I_RR, + 0103210+I_RR, 0103230+I_RR, 0103250+I_RR, 0103270+I_RR, + 0103310+I_RR, 0103330+I_RR, 0103350+I_RR, 0103370+I_RR, + 0103400+I_RR, 0103420+I_RR, 0103440+I_RR, 0103460+I_RR, + 0103500+I_RR, 0103520+I_RR, 0103540+I_RR, 0103560+I_RR, + 0103600+I_RR, 0103620+I_RR, 0103640+I_RR, 0103660+I_RR, + 0103700+I_RR, 0103720+I_RR, 0103740+I_RR, 0103760+I_RR, + 0103410+I_RR, 0103430+I_RR, 0103450+I_RR, 0103470+I_RR, + 0103510+I_RR, 0103530+I_RR, 0103550+I_RR, 0103570+I_RR, + 0103610+I_RR, 0103630+I_RR, 0103650+I_RR, 0103670+I_RR, + 0103710+I_RR, 0103730+I_RR, 0103750+I_RR, 0103770+I_RR, + 0060177+I_NPN, 0060277+I_NPN, + 0060477+I_R, 0061477+I_R, 0062077+I_R, 0062677+I_NPN, 0063077+I_NPN, +#if !defined (ECLIPSE) + 0073301+I_NPN, 0073101+I_NPN, 0077201+I_NPN, 0077001+I_NPN, + 0061401+I_R, 0061601+I_R, 0062401+I_NPN, 0062601+I_NPN, + 0061001+I_R, 0060001+I_R, 0061201+I_R, 0060201+I_R, + 0060401+I_BY, 0062001+I_BY, +#endif + 0060000+I_RD, 0060100+I_RD, 0060200+I_RD, 0060300+I_RD, + 0060400+I_RD, 0060500+I_RD, 0060600+I_RD, 0060700+I_RD, + 0061000+I_RD, 0061100+I_RD, 0061200+I_RD, 0061300+I_RD, + 0061400+I_RD, 0061500+I_RD, 0061600+I_RD, 0061700+I_RD, + 0062000+I_RD, 0062100+I_RD, 0062200+I_RD, 0062300+I_RD, + 0062400+I_RD, 0062500+I_RD, 0062600+I_RD, 0062700+I_RD, + 0063000+I_RD, 0063100+I_RD, 0063200+I_RD, 0063300+I_RD, + 0063400+I_D, 0063500+I_D, 0063600+I_D, 0063700+I_D, +#if defined (ECLIPSE) + 0064000+I_D, 0070000+I_D, 0074000+I_D, 0076000+I_D, +#endif + -1 + }; + +static const char *skip[] = { + "SKP", "SZC", "SNC", "SZR", "SNR", "SEZ", "SBN", + NULL + }; + +static const char *device[] = { +#if defined (ECLIPSE) + "ERCC", "MAP", +#endif + "TTI", "TTO", "PTR", "PTP", "RTC", "PLT", "CDR", "LPT", + "DSK", "MTA", "DCM", "QTY" /* "ADCV" */, "DKP", "CAS", + "TTI1", "TTO1", "CPU", + NULL + }; + +static const int32 dev_val[] = { +#if defined (ECLIPSE) + 002, 003, +#endif + 010, 011, 012, 013, 014, 015, 016, 017, + 020, 022, 024, 030, 033, 034, + 050, 051, 077, + -1 + }; + +/* Address decode + + Inputs: + *of = output stream + addr = current PC + ind = indirect flag + mode = addressing mode + disp = displacement + ext = true if extended address + cflag = true if decoding for CPU + Outputs: + return = error code +*/ + +t_stat fprint_addr (FILE *of, t_addr addr, int32 ind, int32 mode, + int32 disp, t_bool ext, int32 cflag) +{ +int32 dsign, dmax; + +if (ext) dmax = AMASK + 1; /* get max disp */ +else dmax = I_M_DISP + 1; +dsign = dmax >> 1; /* get disp sign */ +if (ind) fprintf (of, "@"); /* indirect? */ +switch (mode & 03) { /* mode */ + + case 0: /* absolute */ + fprintf (of, "%-o", disp); + break; + + case 1: /* PC rel */ + if (disp & dsign) { + if (cflag) fprintf (of, "%-o", (addr - (dmax - disp)) & AMASK); + else fprintf (of, ".-%-o", dmax - disp); + } + else { + if (cflag) fprintf (of, "%-o", (addr + disp) & AMASK); + else fprintf (of, ".+%-o", disp); + } + break; + + case 2: /* AC2 rel */ + if (disp & dsign) fprintf (of, "-%-o,2", dmax - disp); + else fprintf (of, "%-o,2", disp); + break; + + case 3: /* AC3 rel */ + if (disp & dsign) fprintf (of, "-%-o,3", dmax - disp); + else fprintf (of, "%-o,3", disp); + break; + } /* end switch */ + +return SCPE_OK; +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, c1, c2, inst, inst1, dv, src, dst, skp; +int32 ind, mode, disp, dev; +int32 byac, extind, extdisp, xop; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +c1 = ((int32) val[0] >> 8) & 0177; +c2 = (int32) val[0] & 0177; +if (sw & SWMASK ('A')) { /* ASCII? */ + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; /* mnemonic? */ + +/* Instruction decode */ + +inst = (int32) val[0]; +inst1 = (int32) val[1]; +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 0177777) == (inst & masks[j])) { /* match? */ + src = I_GETSRC (inst); /* opr fields */ + dst = I_GETDST (inst); + skp = I_GETSKP (inst); + ind = inst & I_IND; /* mem ref fields */ + mode = I_GETMODE (inst); + disp = I_GETDISP (inst); + dev = I_GETDEV (inst); /* IOT fields */ + byac = I_GETPULSE (inst); /* byte fields */ + xop = I_GETXOP (inst); /* XOP fields */ + extind = inst1 & A_IND; /* extended fields */ + extdisp = inst1 & AMASK; + for (dv = 0; (dev_val[dv] >= 0) && (dev_val[dv] != dev); dv++) ; + + switch (j) { /* switch on class */ + + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_R: /* reg only */ + fprintf (of, "%s %-o", opcode[i], dst); + break; + + case I_V_D: /* dev only */ +#if defined (ECLIPSE) + if (Usermap && (MapStat & 0100)) { /* the evil LEF mode */ + fprintf (of, "LEF %-o,", dst); + fprint_addr (of, addr, ind, mode, disp, FALSE, cflag); + break; + } +#endif + if (dev_val[dv] >= 0) + fprintf (of, "%s %s", opcode[i], device[dv]); + else fprintf (of, "%s %-o", opcode[i], dev); + break; + + case I_V_RD: /* reg, dev */ + if (dev_val[dv] >= 0) + fprintf (of, "%s %-o,%s", opcode[i], dst, device[dv]); + else fprintf (of, "%s %-o,%-o", opcode[i], dst, dev); + break; + + case I_V_M: /* addr only */ + fprintf (of, "%s ", opcode[i]); + fprint_addr (of, addr, ind, mode, disp, FALSE, cflag); + break; + + case I_V_RM: /* reg, addr */ + fprintf (of, "%s %-o,", opcode[i], dst); + fprint_addr (of, addr, ind, mode, disp, FALSE, cflag); + break; + + case I_V_RR: /* operate */ + fprintf (of, "%s %-o,%-o", opcode[i], src, dst); + if (skp) fprintf (of, ",%s", skip[skp-1]); + break; + + case I_V_BY: /* byte */ + fprintf (of, "%s %-o,%-o", opcode[i], byac, dst); + break; + + case I_V_2AC: /* reg, reg */ + fprintf (of, "%s %-o,%-o", opcode[i], src, dst); + break; + + case I_V_RSI: /* reg, short imm */ + fprintf (of, "%s %-o,%-o", opcode[i], src + 1, dst); + break; + + case I_V_LI: /* long imm */ + fprintf (of, "%s %-o", opcode[i], inst1); + return -1; + + case I_V_RLI: /* reg, long imm */ + fprintf (of, "%s %-o,%-o", opcode[i], inst1, dst); + return -1; + + case I_V_LM: /* long addr */ + fprintf (of, "%s ", opcode[i]); + fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag); + return -1; + + case I_V_RLM: /* reg, long addr */ + fprintf (of, "%s %-o,", opcode[i], dst); + fprint_addr (of, addr, extind, mode, extdisp, TRUE, cflag); + return -1; + + case I_V_FRM: /* flt reg, long addr */ + fprintf (of, "%s %-o,", opcode[i], dst); + fprint_addr (of, addr, extind, src, extdisp, TRUE, cflag); + return -1; + + case I_V_FST: /* flt status */ + fprintf (of, "%s ", opcode[i]); + fprint_addr (of, addr, extind, dst, extdisp, AMASK + 1, cflag); + return -1; + + case I_V_XP: /* XOP */ + fprintf (of, "%s %-o,%-o,%-o", opcode[i], src, dst, xop); + break; /* end case */ + + default: + fprintf (of, "??? [%-o]", inst); + break; + } + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Address parse + + Inputs: + *cptr = pointer to input string + addr = current PC + ext = extended address + cflag = true if parsing for CPU + val[3] = output array + Outputs: + optr = pointer to next char in input string + NULL if error +*/ + +#define A_FL 001 /* CPU flag */ +#define A_NX 002 /* index seen */ +#define A_PER 004 /* period seen */ +#define A_NUM 010 /* number seen */ +#define A_SI 020 /* sign seen */ +#define A_MI 040 /* - seen */ + +char *get_addr (char *cptr, t_addr addr, t_bool ext, int32 cflag, int32 *val) +{ +int32 d, r, x, pflag; +char gbuf[CBUFSIZE]; +int32 dmax, dsign; + +if (ext) dmax = AMASK + 1; /* get max disp */ +else dmax = I_M_DISP + 1; +dsign = dmax >> 1; /* get disp sign */ +val[0] = 0; /* no indirect */ +val[1] = 0; /* PC rel */ +val[2] = 0; /* no addr */ + +pflag = cflag & A_FL; /* isolate flag */ +if (*cptr == '@') { /* indirect? */ + val[0] = 1; + cptr++; + } +if (*cptr == '.') { /* relative? */ + pflag = pflag | A_PER; + x = 1; /* "index" is PC */ + cptr++; + } +if (*cptr == '+') { /* + sign? */ + pflag = pflag | A_SI; + cptr++; + } +else if (*cptr == '-') { /* - sign? */ + pflag = pflag | A_MI | A_SI; + cptr++; + } +if (*cptr != 0) { /* number? */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + d = (int32) get_uint (gbuf, 8, AMASK, &r); + if (r != SCPE_OK) return NULL; + pflag = pflag | A_NUM; + } +if (*cptr != 0) { /* index? */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + x = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if ((r != SCPE_OK) || (x < 2)) return NULL; + pflag = pflag | A_NX; + } + +switch (pflag) { /* case on flags */ + + case A_NUM: case A_NUM+A_SI: /* ~CPU, (+)num */ + if (d < dmax) val[2] = d; + else return NULL; + break; + + case A_NUM+A_FL: case A_NUM+A_SI+A_FL: /* CPU, (+)num */ + if (d < dmax) val[2] = d; + else if (((d >= (((int32) addr - dsign) & AMASK)) && + (d < (((int32) addr + dsign) & AMASK))) || + (d >= ((int32) addr + (-dsign & AMASK)))) { + val[1] = 1; /* PC rel */ + val[2] = (d - addr) & (dmax - 1); + } + else return NULL; + break; + + case A_PER: case A_PER+A_FL: /* . */ + case A_PER+A_SI+A_NUM: case A_PER+A_SI+A_NUM+A_FL: /* . + num */ + case A_PER+A_SI+A_MI+A_NUM: /* . - num */ + case A_PER+A_SI+A_MI+A_NUM+A_FL: + case A_NX+A_NUM: case A_NX+A_NUM+A_FL: /* num, ndx */ + case A_NX+A_SI+A_NUM: case A_NX+A_SI+A_NUM+A_FL: /* +num, ndx */ + case A_NX+A_SI+A_MI+A_NUM: /* -num, ndx */ + case A_NX+A_SI+A_MI+A_NUM+A_FL: + val[1] = x; /* set mode */ + if (((pflag & A_MI) == 0) && (d < dsign)) val[2] = d; + else if ((pflag & A_MI) && (d <= dsign)) val[2] = (dmax - d); + else return NULL; + break; + + default: + return NULL; + } /* end case */ + +return cptr; +} + +/* Parse two registers + + Inputs: + *cptr = input string + term = second terminating character + val = output array + Outputs: + optr = pointer to next char in input string + NULL if error +*/ + +char *get_2reg (char *cptr, char term, int32 *val) +{ +char gbuf[CBUFSIZE]; +t_stat r; + +cptr = get_glyph (cptr, gbuf, ','); /* get register */ +val[0] = (int32) get_uint (gbuf, 8, I_M_SRC, &r); +if (r != SCPE_OK) return NULL; +cptr = get_glyph (cptr, gbuf, term); /* get register */ +val[1] = (int32) get_uint (gbuf, 8, I_M_DST, &r); +if (r != SCPE_OK) return NULL; +return cptr; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, amd[3]; +t_stat r, rtn; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((t_value) cptr[0] << 8) + (t_value) cptr[1]; + return SCPE_OK; + } + +/* Instruction parse */ + +rtn = SCPE_OK; /* assume 1 word */ +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 0177777; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_NPN: /* no operand */ + break; + + case I_V_R: /* IOT reg */ + cptr = get_glyph (cptr, gbuf, 0); /* get register */ + d = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + break; + + case I_V_RD: /* IOT reg,dev */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + case I_V_D: /* IOT dev */ + cptr = get_glyph (cptr, gbuf, 0); /* get device */ + for (i = 0; (device[i] != NULL) && + (strcmp (device[i], gbuf) != 0); i++); + if (device[i] != NULL) val[0] = val[0] | dev_val[i]; + else { + d = (int32) get_uint (gbuf, 8, I_M_DEV, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DEV); + } + break; + + case I_V_RM: /* reg, addr */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + case I_V_M: /* addr */ + cptr = get_addr (cptr, addr, FALSE, cflag, amd); + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[0] << I_V_IND) | (amd[1] << I_V_MODE) | amd[2]; + break; + + case I_V_RR: /* operate */ + cptr = get_2reg (cptr, ',', amd); /* get 2 reg */ + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST); + if (*cptr != 0) { /* skip? */ + cptr = get_glyph (cptr, gbuf, 0); /* get skip */ + for (i = 0; (skip[i] != NULL) && + (strcmp (skip[i], gbuf) != 0); i++) ; + if (skip[i] == NULL) return SCPE_ARG; + val[0] = val[0] | (i + 1); + } /* end if */ + break; + + case I_V_BY: /* byte */ + cptr = get_2reg (cptr, 0, amd); /* get 2 reg */ + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[0] << I_V_PULSE) | (amd[1] << I_V_DST); + break; + + case I_V_2AC: /* reg, reg */ + cptr = get_2reg (cptr, 0, amd); /* get 2 reg */ + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST); + break; + + case I_V_RSI: /* reg, short imm */ + cptr = get_glyph (cptr, gbuf, ','); /* get immediate */ + d = (int32) get_uint (gbuf, 8, I_M_SRC + 1, &r); + if ((d == 0) || (r != SCPE_OK)) return SCPE_ARG; + val[0] = val[0] | ((d - 1) << I_V_SRC); /* put in place */ + cptr = get_glyph (cptr, gbuf, 0); /* get register */ + d = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + break; + + case I_V_RLI: /* reg, long imm */ + cptr = get_glyph (cptr, gbuf, ','); /* get immediate */ + val[1] = (int32) get_uint (gbuf, 8, DMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + cptr = get_glyph (cptr, gbuf, 0); /* get register */ + d = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + rtn = -1; + break; + + case I_V_LI: /* long imm */ + cptr = get_glyph (cptr, gbuf, 0); /* get immediate */ + val[1] = (int32) get_uint (gbuf, 8, DMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + rtn = -1; + break; + + case I_V_RLM: /* reg, long mem */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + case I_V_LM: /* long mem */ + cptr = get_addr (cptr, addr, TRUE, cflag, amd); + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[1] << I_V_MODE); + val[1] = (amd[0] << A_V_IND) | amd[2]; + rtn = -1; + break; + + case I_V_FRM: /* flt reg, long mem */ + cptr = get_glyph (cptr, gbuf, ','); /* get register */ + d = (int32) get_uint (gbuf, 8, I_M_DST, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_DST); /* put in place */ + cptr = get_addr (cptr, addr, TRUE, cflag, amd); + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[1] << I_V_SRC); + val[1] = (amd[0] << A_V_IND) | amd[2]; + rtn = -1; + break; + + case I_V_FST: /* flt status */ + cptr = get_addr (cptr, addr, TRUE, cflag, amd); + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[1] << I_V_DST); + val[1] = (amd[0] << A_V_IND) | amd[2]; + rtn = -1; + break; + + case I_V_XP: /* XOP */ + cptr = get_2reg (cptr, ',', amd); /* get 2 reg */ + if (cptr == NULL) return SCPE_ARG; + val[0] = val[0] | (amd[0] << I_V_SRC) | (amd[1] << I_V_DST); + cptr = get_glyph (cptr, gbuf, 0); /* get argument */ + d = (int32) get_uint (gbuf, 8, I_M_XOP, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << I_V_XOP); + break; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* any leftovers? */ +return rtn; +} diff --git a/NOVA/nova_tt.c b/NOVA/nova_tt.c new file mode 100644 index 0000000..59491d5 --- /dev/null +++ b/NOVA/nova_tt.c @@ -0,0 +1,262 @@ +/* nova_tt.c: NOVA console terminal simulator + + Copyright (c) 1993-2008, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti terminal input + tto terminal output + + 04-Jul-07 BKR fixed Dasher CR/LF swap function in 'tti_svc()', + DEV_SET/CLR macros now used, + TTO device may now be DISABLED + 29-Dec-03 RMS Added console backpressure support + 25-Apr-03 RMS Revised for extended file support + 05-Jan-02 RMS Fixed calling sequence for setmod + 03-Oct-02 RMS Added DIBs + 30-May-02 RMS Widened POS to 32b + 30-Nov-01 RMS Added extended SET/SHOW support + 17-Sep-01 RMS Removed multiconsole support + 07-Sep-01 RMS Moved function prototypes + 31-May-01 RMS Added multiconsole support + + Notes: + - TTO output is always masked to 7 bits in this rev + - TTO "Dasher" attribute sends '\b' to console instead of '\031' + - TTO may be disabled + - TTI input is always masked to 7 bits in this rev + - TTI "Dasher" attribute swaps and + - TTI may not be disabled +*/ + +#include "nova_defs.h" + +#define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */ +#define UNIT_DASHER (1 << UNIT_V_DASHER) + +extern int32 int_req, dev_busy, dev_done, dev_disable; + +int32 tti (int32 pulse, int32 code, int32 AC); +int32 tto (int32 pulse, int32 code, int32 AC); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat ttx_setmod (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list + ttx_mod TTI/TTO modifiers list +*/ + +DIB tti_dib = { DEV_TTI, INT_TTI, PI_TTI, &tti }; + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_TTI) }, + { FLDATA (DONE, dev_done, INT_V_TTI) }, + { FLDATA (DISABLE, dev_disable, INT_V_TTI) }, + { FLDATA (INT, int_req, INT_V_TTI) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB ttx_mod[] = { + { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx_setmod }, + { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx_setmod }, + { 0 } + } ; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, ttx_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, 0 + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +DIB tto_dib = { DEV_TTO, INT_TTO, PI_TTO, &tto }; + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_TTO) }, + { FLDATA (DONE, dev_done, INT_V_TTO) }, + { FLDATA (DISABLE, dev_disable, INT_V_TTO) }, + { FLDATA (INT, int_req, INT_V_TTO) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, ttx_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, DEV_DISABLE + }; + +/* Terminal input: IOT routine */ + +int32 tti (int32 pulse, int32 code, int32 AC) +{ +int32 iodata; + + +if (code == ioDIA) + iodata = tti_unit.buf & 0377; +else iodata = 0; + +switch (pulse) + { /* decode IR<8:9> */ + case iopS: /* start */ + DEV_SET_BUSY( INT_TTI ) ; + DEV_CLR_DONE( INT_TTI ) ; + DEV_UPDATE_INTR ; + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_TTI ) ; + DEV_CLR_DONE( INT_TTI ) ; + DEV_UPDATE_INTR ; + break; + } /* end switch */ + +return iodata; +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) + return temp; /* no char or error? */ +tti_unit.buf = temp & 0177; +if (tti_unit.flags & UNIT_DASHER) { + if (tti_unit.buf == '\r') + tti_unit.buf = '\n'; /* Dasher: cr -> nl */ + else if (tti_unit.buf == '\n') + tti_unit.buf = '\r' ; /* Dasher: nl -> cr */ + } +DEV_CLR_BUSY( INT_TTI ) ; +DEV_SET_DONE( INT_TTI ) ; +DEV_UPDATE_INTR ; +++(uptr->pos) ; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; /* */ +DEV_CLR_BUSY( INT_TTI ) ; +DEV_CLR_DONE( INT_TTI ) ; +DEV_UPDATE_INTR ; +sim_activate (&tti_unit, tti_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) + tto_unit.buf = AC & 0377; + +switch (pulse) + { /* decode IR<8:9> */ + case iopS: /* start */ + DEV_SET_BUSY( INT_TTO ) ; + DEV_CLR_DONE( INT_TTO ) ; + DEV_UPDATE_INTR ; + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + break; + + case iopC: /* clear */ + DEV_CLR_BUSY( INT_TTO ) ; + DEV_CLR_DONE( INT_TTO ) ; + DEV_UPDATE_INTR ; + sim_cancel (&tto_unit); /* deactivate unit */ + break; + } /* end switch */ +return 0; +} + + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +c = tto_unit.buf & 0177; +if ((tto_unit.flags & UNIT_DASHER) && (c == 031)) + c = '\b'; +if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK : r); /* !stall? report */ + } +DEV_CLR_BUSY( INT_TTO ) ; +DEV_SET_DONE( INT_TTO ) ; +DEV_UPDATE_INTR ; +++(tto_unit.pos); +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; /* */ +DEV_CLR_BUSY( INT_TTO ) ; +DEV_CLR_DONE( INT_TTO ) ; +DEV_UPDATE_INTR ; +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat ttx_setmod (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +tti_unit.flags = (tti_unit.flags & ~UNIT_DASHER) | val; +tto_unit.flags = (tto_unit.flags & ~UNIT_DASHER) | val; +return SCPE_OK; +} diff --git a/NOVA/nova_tt1.c b/NOVA/nova_tt1.c new file mode 100644 index 0000000..ad45159 --- /dev/null +++ b/NOVA/nova_tt1.c @@ -0,0 +1,348 @@ +/* nova_tt1.c: NOVA second terminal simulator + + Copyright (c) 1993-2008, Robert M. Supnik + Written by Bruce Ray and used with his gracious permission. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti1 second terminal input + tto1 second terminal output + + 09-May-03 RMS Added network device flag + 05-Jan-03 RMS Fixed calling sequence for setmod + 03-Oct-02 RMS Added DIBs + 22-Aug-02 RMS Updated for changes in sim_tmxr + 30-May-02 RMS Widened POS to 32b + 06-Jan-02 RMS Revised enable/disable support + 30-Dec-01 RMS Added show statistics, set disconnect + 30-Nov-01 RMS Added extended SET/SHOW support + 17-Sep-01 RMS Changed to use terminal multiplexor library + 07-Sep-01 RMS Moved function prototypes + 31-May-01 RMS Added multiconsole support + 26-Apr-01 RMS Added device enable/disable support +*/ + +#include "nova_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#define UNIT_V_DASHER (UNIT_V_UF + 0) /* Dasher mode */ +#define UNIT_DASHER (1 << UNIT_V_DASHER) + +extern int32 int_req, dev_busy, dev_done, dev_disable; +extern int32 tmxr_poll; /* calibrated poll */ +TMLN tt1_ldsc = { 0 }; /* line descriptors */ +TMXR tt_desc = { 1, 0, 0, &tt1_ldsc }; /* mux descriptor */ + +DEVICE tti1_dev, tto1_dev; +int32 tti1 (int32 pulse, int32 code, int32 AC); +int32 tto1 (int32 pulse, int32 code, int32 AC); +t_stat tti1_svc (UNIT *uptr); +t_stat tto1_svc (UNIT *uptr); +t_stat tti1_reset (DEVICE *dptr); +t_stat tto1_reset (DEVICE *dptr); +t_stat ttx1_setmod (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tti1_attach (UNIT *uptr, char *cptr); +t_stat tti1_detach (UNIT *uptr); +t_stat tti1_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tti1_show (FILE *st, UNIT *uptr, int32 val, void *desc); +void ttx1_enbdis (int32 dis); + +/* TTI1 data structures + + tti1_dev TTI1 device descriptor + tti1_unit TTI1 unit descriptor + tti1_reg TTI1 register list + ttx1_mod TTI1/TTO1 modifiers list +*/ + +DIB tti1_dib = { DEV_TTI1, INT_TTI1, PI_TTI1, &tti1 }; + +UNIT tti1_unit = { UDATA (&tti1_svc, UNIT_ATTABLE, 0), KBD_POLL_WAIT }; + +REG tti1_reg[] = { + { ORDATA (BUF, tti1_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_TTI1) }, + { FLDATA (DONE, dev_done, INT_V_TTI1) }, + { FLDATA (DISABLE, dev_disable, INT_V_TTI1) }, + { FLDATA (INT, int_req, INT_V_TTI1) }, + { DRDATA (POS, tt1_ldsc.rxcnt, 32), PV_LEFT }, + { DRDATA (TIME, tti1_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB tti1_mod[] = { + { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx1_setmod }, + { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx1_setmod }, + { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tti1_summ }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &tt_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &tti1_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &tti1_show, NULL }, + { 0 } + }; + +DEVICE tti1_dev = { + "TTI1", &tti1_unit, tti1_reg, tti1_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &tti1_reset, + NULL, &tti1_attach, &tti1_detach, + &tti1_dib, DEV_NET | DEV_DISABLE + }; + +/* TTO1 data structures + + tto1_dev TTO1 device descriptor + tto1_unit TTO1 unit descriptor + tto1_reg TTO1 register list +*/ + +DIB tto1_dib = { DEV_TTO1, INT_TTO1, PI_TTO1, &tto1 }; + +UNIT tto1_unit = { UDATA (&tto1_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto1_reg[] = { + { ORDATA (BUF, tto1_unit.buf, 8) }, + { FLDATA (BUSY, dev_busy, INT_V_TTO1) }, + { FLDATA (DONE, dev_done, INT_V_TTO1) }, + { FLDATA (DISABLE, dev_disable, INT_V_TTO1) }, + { FLDATA (INT, int_req, INT_V_TTO1) }, + { DRDATA (POS, tt1_ldsc.txcnt, 32), PV_LEFT }, + { DRDATA (TIME, tto1_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto1_mod[] = { + { UNIT_DASHER, 0, "ANSI", "ANSI", &ttx1_setmod }, + { UNIT_DASHER, UNIT_DASHER, "Dasher", "DASHER", &ttx1_setmod }, + { 0 } + }; + +DEVICE tto1_dev = { + "TTO1", &tto1_unit, tto1_reg, tto1_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto1_reset, + NULL, NULL, NULL, + &tto1_dib, DEV_DISABLE + }; + +/* Terminal input: IOT routine */ + +int32 tti1 (int32 pulse, int32 code, int32 AC) +{ +int32 iodata; + +iodata = (code == ioDIA)? tti1_unit.buf & 0377: 0; +switch (pulse) { /* decode IR<8:9> */ + + case iopS: /* start */ + dev_busy = dev_busy | INT_TTI1; /* set busy */ + dev_done = dev_done & ~INT_TTI1; /* clear done, int */ + int_req = int_req & ~INT_TTI1; + break; + + case iopC: /* clear */ + dev_busy = dev_busy & ~INT_TTI1; /* clear busy */ + dev_done = dev_done & ~INT_TTI1; /* clear done, int */ + int_req = int_req & ~INT_TTI1; + break; + } /* end switch */ + +return iodata; +} + +/* Unit service */ + +t_stat tti1_svc (UNIT *uptr) +{ +int32 temp, newln; + +if (tt1_ldsc.conn) { /* connected? */ + tmxr_poll_rx (&tt_desc); /* poll for input */ + if (temp = tmxr_getc_ln (&tt1_ldsc)) { /* get char */ + uptr->buf = temp & 0177; + if ((uptr->flags & UNIT_DASHER) && + (uptr->buf == '\r')) + uptr->buf = '\n'; /* Dasher: cr->nl */ + dev_busy = dev_busy & ~INT_TTI1; /* clear busy */ + dev_done = dev_done | INT_TTI1; /* set done */ + int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); + } + sim_activate (uptr, uptr->wait); /* continue poll */ + } +if (uptr->flags & UNIT_ATT) { /* attached? */ + newln = tmxr_poll_conn (&tt_desc); /* poll connect */ + if (newln >= 0) { /* got one? */ + sim_activate (&tti1_unit, tti1_unit.wait); + tt1_ldsc.rcve = 1; /* rcv enabled */ + } + sim_activate (uptr, tmxr_poll); /* sched poll */ + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti1_reset (DEVICE *dptr) +{ +ttx1_enbdis (dptr->flags & DEV_DIS); /* sync devices */ +tti1_unit.buf = 0; /* */ +dev_busy = dev_busy & ~INT_TTI1; /* clear busy */ +dev_done = dev_done & ~INT_TTI1; /* clear done, int */ +int_req = int_req & ~INT_TTI1; +if (tt1_ldsc.conn) { /* if conn, */ + sim_activate (&tti1_unit, tti1_unit.wait); /* activate, */ + tt1_ldsc.rcve = 1; /* enable */ + } +else if (tti1_unit.flags & UNIT_ATT) /* if attached, */ + sim_activate (&tti1_unit, tmxr_poll); /* activate */ +else sim_cancel (&tti1_unit); /* else stop */ +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto1 (int32 pulse, int32 code, int32 AC) +{ +if (code == ioDOA) tto1_unit.buf = AC & 0377; +switch (pulse) { /* decode IR<8:9> */ + + case iopS: /* start */ + dev_busy = dev_busy | INT_TTO1; /* set busy */ + dev_done = dev_done & ~INT_TTO1; /* clear done, int */ + int_req = int_req & ~INT_TTO1; + sim_activate (&tto1_unit, tto1_unit.wait); /* activate unit */ + break; + + case iopC: /* clear */ + dev_busy = dev_busy & ~INT_TTO1; /* clear busy */ + dev_done = dev_done & ~INT_TTO1; /* clear done, int */ + int_req = int_req & ~INT_TTO1; + sim_cancel (&tto1_unit); /* deactivate unit */ + break; + } /* end switch */ + +return 0; +} + +/* Unit service */ + +t_stat tto1_svc (UNIT *uptr) +{ +int32 c; + +dev_busy = dev_busy & ~INT_TTO1; /* clear busy */ +dev_done = dev_done | INT_TTO1; /* set done */ +int_req = (int_req & ~INT_DEV) | (dev_done & ~dev_disable); +c = tto1_unit.buf & 0177; +if ((tto1_unit.flags & UNIT_DASHER) && (c == 031)) c = '\b'; +if (tt1_ldsc.conn) { /* connected? */ + if (tt1_ldsc.xmte) { /* tx enabled? */ + tmxr_putc_ln (&tt1_ldsc, c); /* output char */ + tmxr_poll_tx (&tt_desc); /* poll xmt */ + } + else { + tmxr_poll_tx (&tt_desc); /* poll xmt */ + sim_activate (&tto1_unit, tmxr_poll); /* wait */ + } + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto1_reset (DEVICE *dptr) +{ +ttx1_enbdis (dptr->flags & DEV_DIS); /* sync devices */ +tto1_unit.buf = 0; /* */ +dev_busy = dev_busy & ~INT_TTO1; /* clear busy */ +dev_done = dev_done & ~INT_TTO1; /* clear done, int */ +int_req = int_req & ~INT_TTO1; +sim_cancel (&tto1_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat ttx1_setmod (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +tti1_unit.flags = (tti1_unit.flags & ~UNIT_DASHER) | val; +tto1_unit.flags = (tto1_unit.flags & ~UNIT_DASHER) | val; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat tti1_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&tt_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +sim_activate (uptr, tmxr_poll); /* start poll */ +return SCPE_OK; +} + +/* Detach routine */ + +t_stat tti1_detach (UNIT *uptr) +{ +t_stat r; + +r = tmxr_detach (&tt_desc, uptr); /* detach */ +tt1_ldsc.rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat tti1_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (tt1_ldsc.conn) fprintf (st, "connected"); +else fprintf (st, "disconnected"); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat tti1_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (val) tmxr_fconns (st, &tt1_ldsc, -1); +else tmxr_fstats (st, &tt1_ldsc, -1); +return SCPE_OK; +} + +/* Enable/disable device */ + +void ttx1_enbdis (int32 dis) +{ +if (dis) { + tti1_dev.flags = tto1_dev.flags | DEV_DIS; + tto1_dev.flags = tto1_dev.flags | DEV_DIS; + } +else { + tti1_dev.flags = tti1_dev.flags & ~DEV_DIS; + tto1_dev.flags = tto1_dev.flags & ~DEV_DIS; + } +return; +} diff --git a/PDP1/pdp1_clk.c b/PDP1/pdp1_clk.c new file mode 100644 index 0000000..db2b7ad --- /dev/null +++ b/PDP1/pdp1_clk.c @@ -0,0 +1,125 @@ +/* pdp1_clk.c: PDP-1D clock simulator + + Copyright (c) 2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + bused in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + clk PDP-1D clock + + Note that the clock is run at 1/8 of real speed (125Hz instead of 1Khz), to + provide for eventual implementation of idling. +*/ + +#include "pdp1_defs.h" + +#define CLK_HWRE_TPS 1000 /* hardware freq */ +#define CLK_TPS 125 /* sim freq */ +#define CLK_CNTS (CLK_HWRE_TPS / CLK_TPS) /* counts per tick */ +#define CLK_C1MIN (1000 * 60) /* counts per min */ +#define CLK_C32MS 32 /* counts per 32ms */ + +int32 clk32ms_sbs = 0; /* 32ms SBS level */ +int32 clk1min_sbs = 0; /* 1min SBS level */ +int32 clk_cntr = 0; +int32 tmxr_poll = 5000; + +extern int32 stop_inst; + +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit + clk_reg CLK register list +*/ + +UNIT clk_unit = { + UDATA (&clk_svc, 0, 0), 5000 + }; + +REG clk_reg[] = { + { ORDATA (CNTR, clk_cntr, 16) }, + { DRDATA (SBS32LVL, clk32ms_sbs, 4), REG_HRO }, + { DRDATA (SBS1MLVL, clk1min_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBS32MSLVL", "SBS32MSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &clk32ms_sbs }, + { MTAB_XTD|MTAB_VDV, 0, "SBS1MINLVL", "SBS1MINLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &clk1min_sbs }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }; + +/* Clock IOT routine */ + +int32 clk (int32 inst, int32 dev, int32 dat) +{ +int32 used, incr; + +if (clk_dev.flags & DEV_DIS) /* disabled? */ + return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */ +used = tmxr_poll - (sim_is_active (&clk_unit) - 1); +incr = (used * CLK_CNTS) / tmxr_poll; +return clk_cntr + incr; +} + +/* Unit service, generate appropriate interrupts */ + +t_stat clk_svc (UNIT *uptr) +{ +if (clk_dev.flags & DEV_DIS) return SCPE_OK; /* disabled? */ +tmxr_poll = sim_rtcn_calb (CLK_TPS, TMR_CLK); /* calibrate clock */ +sim_activate (&clk_unit, tmxr_poll); /* reactivate unit */ +clk_cntr = clk_cntr + CLK_CNTS; /* incr counter */ +if ((clk_cntr % CLK_C32MS) == 0) /* 32ms interval? */ + dev_req_int (clk32ms_sbs); /* req intr */ +if (clk_cntr >= CLK_C1MIN) { /* 1min interval? */ + dev_req_int (clk1min_sbs); /* req intr */ + clk_cntr = 0; /* reset counter */ + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +if (clk_dev.flags & DEV_DIS) sim_cancel (&clk_unit); /* disabled? */ +else { + tmxr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); + sim_activate_abs (&clk_unit, tmxr_poll); /* activate unit */ + } +clk_cntr = 0; /* clear counter */ +return SCPE_OK; +} diff --git a/PDP1/pdp1_cpu.c b/PDP1/pdp1_cpu.c new file mode 100644 index 0000000..76876a9 --- /dev/null +++ b/PDP1/pdp1_cpu.c @@ -0,0 +1,1558 @@ +/* pdp1_cpu.c: PDP-1 CPU simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu PDP-1 central processor + + 30-May-07 RMS Fixed typo in SBS clear (from Norm Lastovica) + 28-Dec-06 RMS Added 16-channel SBS support, PDP-1D support + 28-Jun-06 RMS Fixed bugs in MUS and DIV + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 09-Nov-04 RMS Added instruction history + 07-Sep-03 RMS Added additional explanation on I/O simulation + 01-Sep-03 RMS Added address switches for hardware readin + 23-Jul-03 RMS Revised to detect I/O wait hang + 05-Dec-02 RMS Added drum support + 06-Oct-02 RMS Revised for V2.10 + 20-Aug-02 RMS Added DECtape support + 30-Dec-01 RMS Added old PC queue + 07-Dec-01 RMS Revised to use breakpoint package + 30-Nov-01 RMS Added extended SET/SHOW support + 16-Dec-00 RMS Fixed bug in XCT address calculation + 14-Apr-99 RMS Changed t_addr to unsigned + + The PDP-1 was Digital's first computer. Although Digital built four + other 18b computers, the later systems (the PDP-4, PDP-7, PDP-9, and + PDP-15) were similar to each other and quite different from the PDP-1. + Accordingly, the PDP-1 requires a distinct simulator. + + The register state for the PDP-1 is: + + AC<0:17> accumulator + IO<0:17> IO register + OV overflow flag + PC<0:15> program counter + IOSTA I/O status register + SBS<0:2> sequence break flip flops + IOH I/O halt flip flop + IOS I/O synchronizer (completion) flip flop + EXTM extend mode + PF<1:6> program flags + SS<1:6> sense switches + TW<0:17> test word (switch register) + + The 16-channel sequence break system adds additional state: + + sbs_req<0:15> interrupt requests + sbs_enb<0:15> enabled levels + sbs_act<0:15> active levels + + The PDP-1D adds additional state: + + L link (SN 45 only) + RNG ring mode + RM restrict mode + RMASK restrict mode mask + RNAME rename table (SN 45 only) + RTB restict mode trap buffer (SN 45 only) + + Questions: + + cks: which bits are line printer print done and space done? + cks: is there a bit for sequence break enabled (yes, according + to the 1963 Handbook) + sbs: do sequence breaks accumulate while the system is disabled + (yes, according to the Maintenance Manual) + + The PDP-1 has seven instruction formats: memory reference, skips, + shifts, load immediate, I/O transfer, operate, and (PDP-1D) special. + The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in| address | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> <5> mnemonic action + + 00 + 02 AND AC = AC & M[MA] + 04 IOR AC = AC | M[MA] + 06 XOR AC = AC ^ M[MA] + 10 XCT M[MA] is executed as an instruction + 12 LCH load character (PDP-1D) + 14 DCH store character (PDP-1D) + 16 0 CAL M[100] = AC, AC = PC, PC = 101 + 16 1 JDA M[MA] = AC, AC = PC, PC = MA + 1 + 20 LAC AC = M[MA] + 22 LIO IO = M[MA] + 24 DAC M[MA] = AC + 26 DAP M[MA]<6:17> = AC<6:17> + 30 DIP M[MA]<0:5> = AC<0:5> + 32 DIO M[MA] = IO + 34 DZM M[MA] = 0 + 36 TAD L'AC = AC + M[MA] + L + 40 ADD AC = AC + M[MA] + 42 SUB AC = AC - M[MA] + 44 IDX AC = M[MA] = M[MA] + 1 + 46 ISP AC = M[MA] = M[MA] + 1, skip if AC >= 0 + 50 SAD skip if AC != M[MA] + 52 SAS skip if AC == M[MA] + 54 MUL AC'IO = AC * M[MA] + 56 DIV AC, IO = AC'IO / M[MA] + 60 JMP PC = MA + 62 JSP AC = PC, PC = MA + + Memory reference instructions can access an address space of 64K words. + The address space is divided into sixteen 4K word fields. An + instruction can directly address, via its 12b address, the entire + current field. If extend mode is off, indirect addresses access + the current field, and indirect addressing is multi-level; if off, + they can access all 64K, and indirect addressing is single level. + + The skip format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 0 1 0| | | | | | | | | | | | | | skip + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | \______/ \______/ + | | | | | | | | | + | | | | | | | | +---- program flags + | | | | | | | +------------- sense switches + | | | | | | +------------------- AC == 0 + | | | | | +---------------------- AC >= 0 + | | | | +------------------------- AC < 0 + | | | +---------------------------- OV == 0 + | | +------------------------------- IO >= 0 + | +---------------------------------- IO != 0 (PDP-1D) + +------------------------------------- invert skip + + The shift format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 0 1 1| subopcode | encoded count | shift + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The load immediate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 0| S| immediate | LAW + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> mnemonic action + + 70 LAW if S = 0, AC = IR<6:17> + else AC = ~IR<6:17> + + The I/O transfer format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 1| W| C| subopcode | device | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction sends the the specified subopcode to + specified I/O device. The I/O device may take data from the IO or + return data to the IO, initiate or cancel operations, etc. The + W bit specifies whether the CPU waits for completion, the C bit + whether a completion pulse will be returned from the device. + + The special operate format (PDP-1D) is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 1 0| | | | | | | | | | | | | | special + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | | | | + | | | | | | | | | | +------- CML (3) + | | | | | | | | | +---------- CLL (1) + | | | | | | | | +------------- SZL (1) + | | | | | | | +---------------- SCF (1) + | | | | | | +------------------- SCI (1) + | | | | | +---------------------- SCM (2) + | | | | +------------------------- IDA (3) + | | | +---------------------------- IDC (4) + | | +------------------------------- IFI (2) + | +---------------------------------- IIF (2) + +------------------------------------- reverse skip + + The special operate instruction can be microprogrammed. + + The standard operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 1 1| | | | | | | | | | | | | | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | | | \______/ + | | | | | | | | | | | + | | | | | | | | | | +---- PF select + | | | | | | | | | +---------- clear/set PF + | | | | | | | | +------------- LIA (PDP-1D) + | | | | | | | +---------------- LAI (PDP-1D) + | | | | | | +------------------- or PC + | | | | | +---------------------- CLA + | | | | +------------------------- halt + | | | +---------------------------- CMA + | | +------------------------------- or TW + | +---------------------------------- CLI + +------------------------------------- CMI (PDP-1D) + + The standard operate instruction can be microprogrammed. + + This routine is the instruction decode routine for the PDP-1. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unimplemented instruction and STOP_INST flag set + XCT loop + indirect address loop + infinite wait state + I/O error in I/O simulator + + 2. Interrupts. With a single channel sequence break system, the + PDP-1 has a single break request (flop b2, here sbs). + If sequence breaks are enabled (flop sbm, here sbs), + and one is not already in progress (flop b4, here sbs), + a sequence break occurs. With a 16-channel sequence break + system, the PDP-1 has 16 request flops (sbs_req), 16 enable + flops (sbs_enb), and 16 active flops (sbs_act). It also has + 16 synchronizer flops, which are not needed in simulation. + + 3. Arithmetic. The PDP-1 is a 1's complement system. In 1's + complement arithmetic, a negative number is represented by the + complement (XOR 0777777) of its absolute value. Addition of 1's + complement numbers requires propagating the carry out of the high + order bit back to the low order bit. + + 4. Adding I/O devices. Three modules must be modified: + + pdp1_defs.h add interrupt request definition + pdp1_cpu.c add IOT dispatch code + pdp1_sys.c add sim_devices table entry +*/ + +#include "pdp1_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC +#define UNIT_V_MDV (UNIT_V_UF + 0) /* mul/div */ +#define UNIT_V_SBS (UNIT_V_UF + 1) +#define UNIT_V_1D (UNIT_V_UF + 2) +#define UNIT_V_1D45 (UNIT_V_UF + 3) +#define UNIT_V_MSIZE (UNIT_V_UF + 4) /* dummy mask */ +#define UNIT_MDV (1 << UNIT_V_MDV) +#define UNIT_SBS (1 << UNIT_V_SBS) +#define UNIT_1D (1 << UNIT_V_1D) +#define UNIT_1D45 (1 << UNIT_V_1D45) +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +#define HIST_PC 0x40000000 +#define HIST_V_SHF 18 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +#define MA_GETBNK(x) ((cpu_unit.flags & UNIT_1D45)? \ + (((x) >> RM45_V_BNK) & RM45_M_BNK): \ + (((x) >> RM48_V_BNK) & RM48_M_BNK)) + +typedef struct { + uint32 pc; + uint32 ir; + uint32 ovac; + uint32 pfio; + uint32 ea; + uint32 opnd; + } InstHistory; + +int32 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AC = 0; /* AC */ +int32 IO = 0; /* IO */ +int32 PC = 0; /* PC */ +int32 MA = 0; /* MA */ +int32 MB = 0; /* MB */ +int32 OV = 0; /* overflow */ +int32 SS = 0; /* sense switches */ +int32 PF = 0; /* program flags */ +int32 TA = 0; /* address switches */ +int32 TW = 0; /* test word */ +int32 iosta = 0; /* status reg */ +int32 sbs = 0; /* sequence break */ +int32 sbs_init = 0; /* seq break start */ +int32 ioh = 0; /* I/O halt */ +int32 ios = 0; /* I/O syncronizer */ +int32 cpls = 0; /* pending compl */ +int32 sbs_req = 0; /* sbs requests */ +int32 sbs_enb = 0; /* sbs enabled */ +int32 sbs_act = 0; /* sbs active */ +int32 extm = 0; /* ext mem mode */ +int32 rm = 0; /* restrict mode */ +int32 rmask = 0; /* restrict mask */ +int32 rname[RN45_SIZE]; /* rename table */ +int32 rtb = 0; /* restr trap buf */ +int32 extm_init = 0; /* ext mem startup */ +int32 stop_inst = 0; /* stop rsrv inst */ +int32 xct_max = 16; /* XCT limit */ +int32 ind_max = 16; /* ind limit */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* inst history */ + +extern UNIT *sim_clock_queue; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_1d (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat Ea (int32 IR); +t_stat Ea_ch (int32 IR, int32 *byte_num); +int32 inc_bp (int32 bp); +t_stat set_rmv (int32 code); +int32 sbs_eval (void); +int32 sbs_ffo (int32 mask); +t_stat Read (void); +t_stat Write (void); + +extern int32 ptr (int32 inst, int32 dev, int32 dat); +extern int32 ptp (int32 inst, int32 dev, int32 dat); +extern int32 tti (int32 inst, int32 dev, int32 dat); +extern int32 tto (int32 inst, int32 dev, int32 dat); +extern int32 lpt (int32 inst, int32 dev, int32 dat); +extern int32 dt (int32 inst, int32 dev, int32 dat); +extern int32 drm (int32 inst, int32 dev, int32 dat); +extern int32 clk (int32 inst, int32 dev, int32 dat); +extern int32 dcs (int32 inst, int32 dev, int32 dat); + +const int32 sc_map[512] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, /* 00000xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00001xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00010xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00011xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 00100xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00101xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 00110xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 00111xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 01000xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01001xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01010xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01011xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 01100xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01101xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 01110xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 01111xxxx */ + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, /* 10000xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10001xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10010xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10011xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 10100xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10101xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 10110xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11011xxxx */ + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, /* 11000xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11001xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11010xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11011xxxx */ + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, /* 11100xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11101xxxx */ + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, /* 11110xxxx */ + 5, 6, 6, 7, 6, 7, 7, 8, 6, 7, 7, 8, 7, 8, 8, 9 /* 11111xxxx */ + }; + +const int32 ffo_map[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + +const int32 byt_shf[4] = { 0, 0, 6, 12 }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, PC, ASIZE) }, + { ORDATA (AC, AC, 18) }, + { ORDATA (IO, IO, 18) }, + { ORDATA (MA, MA, 16) }, + { ORDATA (MB, MB, 18) }, + { FLDATA (OV, OV, 0) }, + { ORDATA (PF, PF, 8) }, + { ORDATA (SS, SS, 6) }, + { ORDATA (TA, TA, ASIZE) }, + { ORDATA (TW, TW, 18) }, + { FLDATA (EXTM, extm, 0) }, + { FLDATA (RNGM, PF, PF_V_RNG) }, + { FLDATA (L, PF, PF_V_L) }, + { FLDATA (RM, rm, 0) }, + { ORDATA (RMASK, rmask, 18) }, + { ORDATA (RTB, rtb, 18) }, + { BRDATA (RNAME, rname, 8, 2, RN45_SIZE) }, + { FLDATA (SBON, sbs, SB_V_ON) }, + { FLDATA (SBRQ, sbs, SB_V_RQ) }, + { FLDATA (SBIP, sbs, SB_V_IP) }, + { ORDATA (SBSREQ, sbs_req, 16) }, + { ORDATA (SBSENB, sbs_enb, 16) }, + { ORDATA (SBSACT, sbs_act, 16) }, + { ORDATA (IOSTA, iosta, 18), REG_RO }, + { ORDATA (CPLS, cpls, 6) }, + { FLDATA (IOH, ioh, 0) }, + { FLDATA (IOS, ios, 0) }, + { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { FLDATA (SBS_INIT, sbs_init, SB_V_ON) }, + { FLDATA (EXTM_INIT, extm_init, 0) }, + { DRDATA (XCT_MAX, xct_max, 8), PV_LEFT + REG_NZ }, + { DRDATA (IND_MAX, ind_max, 8), PV_LEFT + REG_NZ }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_1D+UNIT_1D45, 0, "standard CPU", "PDP1C" }, + { UNIT_1D+UNIT_1D45, UNIT_1D, "PDP-1D #48", "PDP1D48", &cpu_set_1d }, + { UNIT_1D+UNIT_1D45, UNIT_1D+UNIT_1D45, "PDP1D #45", "PDP1D45", &cpu_set_1d }, + { UNIT_MDV, UNIT_MDV, "multiply/divide", "MDV", NULL }, + { UNIT_MDV, 0, "no multiply/divide", "NOMDV", NULL }, + { UNIT_SBS, UNIT_SBS, "SBS", "SBS", NULL }, + { UNIT_SBS, 0, "no SBS", "NOSBS", NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, ASIZE, 1, 8, 18, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +int32 IR, op, i, t, xct_count; +int32 sign, signd, v, sbs_lvl, byno; +int32 dev, pulse, io_data, sc, skip; +t_stat reason; +static int32 fs_test[8] = { + 0, PF_SS_1, PF_SS_2, PF_SS_3, + PF_SS_4, PF_SS_5, PF_SS_6, PF_SS_ALL + }; + +#define EPC_WORD ((OV << 17) | (extm << 16) | PC) +#define INCR_ADDR(x) (((x) & EPCMASK) | (((x) + 1) & DAMASK)) +#define DECR_ADDR(x) (((x) & EPCMASK) | (((x) - 1) & DAMASK)) +#define ABS(x) ((x) ^ (((x) & SIGN)? DMASK: 0)) + +if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ + cpu_unit.flags |= UNIT_SBS|UNIT_MDV; /* 16-chan SBS, mdv */ + if (!(cpu_unit.flags & UNIT_1D45)) { /* SN 48? */ + PF &= ~PF_L; /* no link */ + rtb = 0; /* no RTB */ + for (i = 0; i < RN45_SIZE; i++) rname[i] = i; /* no rename */ + } + } +else { /* standard PDP-1 */ + PF &= ~(PF_L|PF_RNG); /* no link, ring */ + rm = 0; /* no restrict mode */ + rtb = 0; /* no RTB */ + for (i = 0; i < RN45_SIZE; i++) rname[i] = i; /* no rename */ + } +if (cpu_unit.flags & UNIT_SBS) { /* 16-chan SBS? */ + sbs = sbs & SB_ON; /* yes, only SB ON */ + sbs_lvl = sbs_eval (); /* eval SBS system */ + } +else sbs_lvl = sbs_req = sbs_enb = sbs_act = 0; /* no, clr SBS sys */ + +/* Main instruction fetch/decode loop: check events and interrupts */ + +reason = 0; +while (reason == 0) { /* loop until halted */ + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + sbs_lvl = sbs_eval (); /* eval sbs system */ + } + + if ((cpu_unit.flags & UNIT_SBS)? /* test interrupt */ + ((sbs & SB_ON) && sbs_lvl): /* 16-chan SBS? */ + (sbs == (SB_ON | SB_RQ))) { /* 1-chan SBS? */ + if (cpu_unit.flags & UNIT_SBS) { /* 16-chan intr */ + int32 lvl = sbs_lvl - 1; /* get level */ + MA = lvl << 2; /* status block */ + sbs_req &= ~SBS_MASK (lvl); /* clr lvl request */ + sbs_act |= SBS_MASK (lvl); /* set lvl active */ + sbs_lvl = sbs_eval (); /* re-eval SBS */ + } + else { /* 1-chan intr */ + MA = 0; /* always level 0 */ + sbs = SB_ON | SB_IP; /* set in prog flag */ + } + PCQ_ENTRY; /* save old PC */ + MB = AC; /* save AC */ + Write (); + MA = MA + 1; + MB = EPC_WORD; /* save OV'EXT'PC */ + Write (); + MA = MA + 1; + MB = IO; /* save IO */ + Write (); + PC = MA + 1; /* PC = block + 3 */ + extm = 0; /* extend off */ + OV = 0; /* clear overflow */ + } + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + +/* Fetch, decode instruction */ + + MA = PC; + if (Read ()) break; /* fetch inst */ + IR = MB; /* save in IR */ + PC = INCR_ADDR (PC); /* increment PC */ + xct_count = 0; /* track XCT's */ + sim_interval = sim_interval - 1; + if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) hst_p = 0; + hst[hst_p].pc = MA | HIST_PC; /* save state */ + hst[hst_p].ir = IR; + hst[hst_p].ovac = (OV << HIST_V_SHF) | AC; + hst[hst_p].pfio = (PF << HIST_V_SHF) | IO; + } + + xct_instr: /* label for XCT */ + op = ((IR >> 13) & 037); /* get opcode */ + switch (op) { /* decode IR<0:4> */ + +/* Logical, load, store instructions */ + + case 001: /* AND */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = AC & MB; + break; + + case 002: /* IOR */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = AC | MB; + break; + + case 003: /* XOR */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = AC ^ MB; + break; + + case 004: /* XCT */ + if (xct_count >= xct_max) { /* too many XCT's? */ + reason = STOP_XCT; + break; + } + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + xct_count = xct_count + 1; /* count XCT's */ + IR = MB; /* get instruction */ + goto xct_instr; /* go execute */ + + case 005: /* LCH */ + if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ + if (reason = Ea_ch (IR, &byno)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = (MB << byt_shf[byno]) & 0770000; /* extract byte */ + } + else reason = stop_inst; /* no, illegal */ + break; + + case 006: /* DCH */ + if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ + if (reason = Ea_ch (IR, &byno)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + MB = (MB & ~(0770000 >> byt_shf[byno])) | /* insert byte */ + ((AC & 0770000) >> byt_shf[byno]); + Write (); /* rewrite */ + AC = ((AC << 6) | (AC >> 12)) & DMASK; /* rot AC left 6 */ + } + else reason = stop_inst; /* no, illegal */ + break; + + case 007: /* CAL, JDA */ + MA = (PC & EPCMASK) | ((IR & IA)? (IR & DAMASK): 0100); + if (hst_p) hst[hst_p].ea = MA; /* history enabled? */ + PCQ_ENTRY; + MB = AC; /* save AC */ + AC = EPC_WORD; + PC = INCR_ADDR (MA); + reason = Write (); + break; + + case 010: /* LAC */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = MB; + break; + + case 011: /* LIO */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + IO = MB; + break; + + case 012: /* DAC */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + MB = AC; + reason = Write (); + break; + + case 013: /* DAP */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + MB = (AC & DAMASK) | (MB & ~DAMASK); + reason = Write (); + break; + + case 014: /* DIP */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + MB = (AC & ~DAMASK) | (MB & DAMASK); + reason = Write (); + break; + + case 015: /* DIO */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + MB = IO; + reason = Write (); + break; + + case 016: /* DZM */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + MB = 0; + reason = Write (); + break; + +/* Add, subtract, control + + Add is performed in sequential steps, as follows: + 1. add + 2. end around carry propagate + 3. overflow check + 4. -0 cleanup + + Subtract is performed in sequential steps, as follows: + 1. complement AC + 2. add + 3. end around carry propagate + 4. overflow check + 5. complement AC + Because no -0 check is done, (-0) - (+0) yields a result of -0 */ + + case 017: /* TAD */ + if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = AC + MB + ((PF & PF_L)? 1: 0); /* AC + opnd + L */ + if (AC > DMASK) PF = PF | PF_L; /* carry? set L */ + else PF = PF & ~PF_L; /* no, clear L */ + AC = AC & DMASK; /* mask AC */ + } + else reason = stop_inst; /* no, illegal */ + break; + + case 020: /* ADD */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + t = AC; + AC = AC + MB; + if (AC > 0777777) AC = (AC + 1) & DMASK; /* end around carry */ + if (((~t ^ MB) & (t ^ AC)) & SIGN) OV = 1; + if (AC == DMASK) AC = 0; /* minus 0 cleanup */ + break; + + case 021: /* SUB */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + t = AC ^ DMASK; /* complement AC */ + AC = t + MB; /* -AC + MB */ + if (AC > DMASK) AC = (AC + 1) & DMASK; /* end around carry */ + if (((~t ^ MB) & (t ^ AC)) & SIGN) OV = 1; + AC = AC ^ DMASK; /* recomplement AC */ + break; + + case 022: /* IDX */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = MB + 1; + if (AC >= DMASK) AC = (AC + 1) & DMASK; + MB = AC; + reason = Write (); + break; + + case 023: /* ISP */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + AC = MB + 1; + if (AC >= DMASK) AC = (AC + 1) & DMASK; + MB = AC; + if (!(AC & SIGN)) PC = INCR_ADDR (PC); + reason = Write (); + break; + + case 024: /* SAD */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + if (AC != MB) PC = INCR_ADDR (PC); + break; + + case 025: /* SAS */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + if (AC == MB) PC = INCR_ADDR (PC); + break; + + case 030: /* JMP */ + if (sbs && /* SBS enabled? */ + ((PC & EPCMASK) == 0) && /* in bank 0? */ + ((IR & (IA|07703)) == (IA|00001)) && /* jmp i 00x1/5? */ + ((cpu_unit.flags & UNIT_SBS) || /* 16-chan SBS or */ + ((IR & 00074) == 0))) { /* jmp i 0001? */ + if (cpu_unit.flags & UNIT_SBS) { /* 16-chan SBS dbk? */ + int32 lvl = (IR >> 2) & SBS_LVL_MASK; /* lvl = MA<14:15> */ + sbs_act &= ~SBS_MASK (lvl); /* clr level active */ + sbs_lvl = sbs_eval (); /* eval SBS system */ + } + else sbs = sbs & ~SB_IP; /* 1-chan dbk */ + PCQ_ENTRY; /* save old PC */ + MA = IR & DAMASK; /* ind addr */ + Read (); /* eff addr word */ + OV = (MB >> 17) & 1; /* restore OV */ + extm = (MB >> 16) & 1; /* restore ext mode */ + PC = MB & AMASK; /* jmp i 00x1/5 */ + if (hst_p) hst[hst_p].ea = PC; /* history enabled? */ + } + else { /* normal JMP */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + PCQ_ENTRY; + PC = MA; + } + break; + + case 031: /* JSP */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + AC = EPC_WORD; + PCQ_ENTRY; + PC = MA; + break; + + case 034: /* LAW */ + AC = (IR & 07777) ^ ((IR & IA)? 0777777: 0); + break; + +/* Multiply and divide + + Multiply and divide step and hardware multiply are exact implementations. + Hardware divide is a 2's complement analog to the actual hardware. +*/ + + case 026: /* MUL */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + if (cpu_unit.flags & UNIT_MDV) { /* hardware? */ + sign = AC ^ MB; /* result sign */ + IO = ABS (AC); /* IO = |AC| */ + v = ABS (MB); /* v = |mpy| */ + for (i = AC = 0; i < 17; i++) { + if (IO & 1) AC = AC + v; + IO = (IO >> 1) | ((AC & 1) << 17); + AC = AC >> 1; + } + if ((sign & SIGN) && (AC | IO)) { /* negative, > 0? */ + AC = AC ^ DMASK; + IO = IO ^ DMASK; + } + } + else { /* multiply step */ + if (IO & 1) AC = AC + MB; + if (AC > DMASK) AC = (AC + 1) & DMASK; + IO = (IO >> 1) | ((AC & 1) << 17); + AC = AC >> 1; + } + break; + + case 027: /* DIV */ + if (reason = Ea (IR)) break; /* MA <- eff addr */ + if (reason = Read ()) break; /* MB <- data */ + if (cpu_unit.flags & UNIT_MDV) { /* hardware */ + sign = AC ^ MB; /* result sign */ + signd = AC; /* remainder sign */ + v = ABS (MB); /* v = |divr| */ + if (ABS (AC) >= v) break; /* overflow? */ + if (AC & SIGN) { + AC = AC ^ DMASK; /* AC'IO = |AC'IO| */ + IO = IO ^ DMASK; + } + for (i = t = 0; i < 18; i++) { + if (t) AC = (AC + v) & DMASK; + else AC = (AC - v) & DMASK; + t = AC >> 17; + if (i != 17) AC = ((AC << 1) | (IO >> 17)) & DMASK; + IO = ((IO << 1) | (t ^ 1)) & 0777777; + } + if (t) AC = (AC + v) & DMASK; /* fix remainder */ + t = ((signd & SIGN) && AC)? AC ^ DMASK: AC; + AC = ((sign & SIGN) && IO)? IO ^ DMASK: IO; + IO = t; + PC = INCR_ADDR (PC); /* skip */ + } + else { /* divide step */ + t = AC >> 17; + AC = ((AC << 1) | (IO >> 17)) & DMASK; + IO = ((IO << 1) | (t ^ 1)) & DMASK; + if (IO & 1) AC = AC + (MB ^ DMASK); + else AC = AC + MB + 1; + if (AC > DMASK) AC = (AC + 1) & DMASK; + if (AC == DMASK) AC = 0; + } + break; + +/* Skips */ + + case 032: /* skip */ + v = (IR >> 3) & 07; /* sense switches */ + t = IR & 07; /* program flags */ + skip = (((cpu_unit.flags & UNIT_1D) && + (IR & 04000) && (IO != 0)) || /* SNI (PDP-1D) */ + ((IR & 02000) && !(IO & SIGN)) || /* SPI */ + ((IR & 01000) && (OV == 0)) || /* SZO */ + ((IR & 00400) && (AC & SIGN)) || /* SMA */ + ((IR & 00200) && !(AC & SIGN)) || /* SPA */ + ((IR & 00100) && (AC == 0)) || /* SZA */ + (v && ((SS & fs_test[v]) == 0)) || /* SZSn */ + (t && ((PF & fs_test[t]) == 0))); /* SZFn */ + if (IR & IA) skip = skip ^ 1; /* invert skip? */ + if (skip) PC = INCR_ADDR (PC); + if (IR & 01000) OV = 0; /* SOV clears OV */ + break; + +/* Shifts */ + + case 033: + sc = sc_map[IR & 0777]; /* map shift count */ + switch ((IR >> 9) & 017) { /* case on IR<5:8> */ + + case 001: /* RAL */ + AC = ((AC << sc) | (AC >> (18 - sc))) & DMASK; + break; + + case 002: /* RIL */ + IO = ((IO << sc) | (IO >> (18 - sc))) & DMASK; + break; + + case 003: /* RCL */ + t = AC; + AC = ((AC << sc) | (IO >> (18 - sc))) & DMASK; + IO = ((IO << sc) | (t >> (18 - sc))) & DMASK; + break; + + case 005: /* SAL */ + t = (AC & SIGN)? DMASK: 0; + AC = (AC & SIGN) | ((AC << sc) & 0377777) | + (t >> (18 - sc)); + break; + + case 006: /* SIL */ + t = (IO & SIGN)? DMASK: 0; + IO = (IO & SIGN) | ((IO << sc) & 0377777) | + (t >> (18 - sc)); + break; + + case 007: /* SCL */ + t = (AC & SIGN)? DMASK: 0; + AC = (AC & SIGN) | ((AC << sc) & 0377777) | + (IO >> (18 - sc)); + IO = ((IO << sc) | (t >> (18 - sc))) & DMASK; + break; + + case 011: /* RAR */ + AC = ((AC >> sc) | (AC << (18 - sc))) & DMASK; + break; + + case 012: /* RIR */ + IO = ((IO >> sc) | (IO << (18 - sc))) & DMASK; + break; + + case 013: /* RCR */ + t = IO; + IO = ((IO >> sc) | (AC << (18 - sc))) & DMASK; + AC = ((AC >> sc) | (t << (18 - sc))) & DMASK; + break; + + case 015: /* SAR */ + t = (AC & SIGN)? DMASK: 0; + AC = ((AC >> sc) | (t << (18 - sc))) & DMASK; + break; + + case 016: /* SIR */ + t = (IO & SIGN)? DMASK: 0; + IO = ((IO >> sc) | (t << (18 - sc))) & DMASK; + break; + + case 017: /* SCR */ + t = (AC & SIGN)? DMASK: 0; + IO = ((IO >> sc) | (AC << (18 - sc))) & DMASK; + AC = ((AC >> sc) | (t << (18 - sc))) & DMASK; + break; + + default: /* undefined */ + reason = stop_inst; + break; + } /* end switch shf */ + break; + +/* Special operates (PDP-1D) - performed in order shown */ + + case 036: /* special */ + if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ + if (IR & 000100) IO = 0; /* SCI */ + if (IR & 000040) PF = 0; /* SCF */ + if (cpu_unit.flags & UNIT_1D45) { /* SN 45? */ + if ((IR & 000020) && /* SZL/SNL? */ + (((PF & PF_L) == 0) == ((IR & IA) == 0))) + PC = INCR_ADDR (PC); + if (IR & 000010) PF = PF & ~PF_L; /* CLL */ + if (IR & 000200) { /* SCM */ + AC = (AC ^ DMASK) + ((PF & PF_L)? 1: 0); + if (AC > DMASK) PF = PF | PF_L; /* carry? set L */ + else PF = PF & ~PF_L; /* no, clear L */ + AC = AC & DMASK; /* mask AC */ + } + } + t = IO & PF_VR_ALL; + if (IR & 004000) IO = IO | PF; /* IIF */ + if (IR & 002000) PF = PF | t; /* IFI */ + if (cpu_unit.flags & UNIT_1D45) { /* SN 45? */ + if (IR & 000004) PF = PF ^ PF_L; /* CML */ + if (IR & 000400) /* IDA */ + AC = (PF & PF_RNG)? + (AC & 0777770) | ((AC + 1) & 07): + (AC + 1) & DMASK; + } + else PF = PF & ~PF_L; /* no link */ + if (IR & 01000) AC = inc_bp (AC); /* IDC */ + } + else reason = stop_inst; /* no, illegal */ + break; + +/* Operates - performed in the order shown */ + + case 037: /* operate */ + if (IR & 004000) IO = 0; /* CLI */ + if (IR & 000200) AC = 0; /* CLA */ + if (IR & 002000) AC = AC | TW; /* LAT */ + if (IR & 000100) AC = AC | EPC_WORD; /* LAP */ + if (IR & 001000) AC = AC ^ DMASK; /* CMA */ + if (cpu_unit.flags & UNIT_1D) { /* PDP-1D? */ + if (IR & 010000) IO = IO ^ DMASK; /* CMI */ + MB = IO; + if (IR & 000020) IO = AC; /* LIA */ + if (IR & 000040) AC = MB; /* LAI */ + } + t = IR & 07; /* flag select */ + if (IR & 010) PF = PF | fs_test[t]; /* STFn */ + else PF = PF & ~fs_test[t]; /* CLFn */ + if (IR & 000400) { /* HLT */ + if (rm && !sbs_act) /* restrict, ~brk? */ + reason = set_rmv (RTB_HLT); /* violation */ + else reason = STOP_HALT; /* no, halt */ + } + break; + +/* IOT - The simulator behaves functionally like a real PDP-1 but does not + use the same mechanisms or state bits. In particular, + + - If an IOT does not specify IO_WAIT, the IOT will be executed, and the + I/O halt flag (IOH) will not be disturbed. On the real PDP-1, IOH is + stored in IHS, IOH is cleared, the IOT is executed, and then IOH is + restored from IHS. Because IHS is not otherwise used, it is not + explicitly simulated. + - If an IOT does specify IO_WAIT, then IOH specifies whether an I/O halt + (wait) is already in progress. + > If already set, I/O wait is in progress. The simulator looks for + a completion pulse (IOS). If there is a pulse, IOH is cleared. If + not, the IOT is fetched again. In either case, execution of the + IOT is skipped. + > If not set, I/O wait must start. IOH is set, the PC is backed up, + and the IOT is executed. + - On a real PDP-1, IOC is the I/O command enable and enables the IOT + pulses. In the simulator, the enabling of IOT pulses is done through + code flow, and IOC is not explicitly simulated. +*/ + + case 035: + if (rm && !sbs_act) { /* restrict, ~brk? */ + reason = set_rmv (RTB_IOT); /* violation */ + break; + } + if (IR & IO_WAIT) { /* wait? */ + if (ioh) { /* I/O halt? */ + if (ios) ioh = 0; /* comp pulse? done */ + else { /* wait more */ + PC = DECR_ADDR (PC); /* re-execute */ + if (cpls == 0) { /* pending pulses? */ + reason = STOP_WAIT; /* no, CPU hangs */ + break; + } + sim_interval = 0; /* force event */ + } + break; /* skip iot */ + } + ioh = 1; /* turn on halt */ + PC = DECR_ADDR (PC); /* re-execute */ + } + dev = IR & 077; /* get dev addr */ + pulse = (IR >> 6) & 077; /* get pulse data */ + io_data = IO; /* default data */ + switch (dev) { /* case on dev */ + + case 000: /* I/O wait */ + break; + + case 001: + if (IR & 003700) io_data = dt (IR, dev, IO); /* DECtape */ + else io_data = ptr (IR, dev, IO); /* paper tape rdr */ + break; + + case 002: case 030: /* paper tape rdr */ + io_data = ptr (IR, dev, IO); + break; + + case 003: /* typewriter */ + io_data = tto (IR, dev, IO); + break; + + case 004: /* keyboard */ + io_data = tti (IR, dev, IO); + break; + + case 005: case 006: /* paper tape punch */ + io_data = ptp (IR, dev, IO); + break; + + case 010: /* leave ring mode */ + if (cpu_unit.flags & UNIT_1D) PF = PF & ~PF_RNG; + else reason = stop_inst; + break; + + case 011: /* enter ring mode */ + if (cpu_unit.flags & UNIT_1D) PF = PF | PF_RNG; + else reason = stop_inst; + break; + + case 022: /* data comm sys */ + io_data = dcs (IR, dev, IO); + break; + + case 032: /* clock */ + io_data = clk (IR, dev, IO); + break; + + case 033: /* check status */ + io_data = iosta | ((sbs & SB_ON)? IOS_SQB: 0); + break; + + case 035: /* check trap buf */ + if (cpu_unit.flags & UNIT_1D45) { /* SN 45? */ + io_data = rtb; + rtb = 0; + } + else reason = stop_inst; + break; + + case 045: /* line printer */ + io_data = lpt (IR, dev, IO); + break; + + case 050: /* deact seq break */ + if (cpu_unit.flags & UNIT_SBS) + sbs_enb &= ~SBS_MASK (pulse & SBS_LVL_MASK); + else reason = stop_inst; + break; + + case 051: /* act seq break */ + if (cpu_unit.flags & UNIT_SBS) + sbs_enb |= SBS_MASK (pulse & SBS_LVL_MASK); + else reason = stop_inst; + break; + + case 052: /* start seq break */ + if (cpu_unit.flags & UNIT_SBS) + sbs_req |= SBS_MASK (pulse & SBS_LVL_MASK); + else reason = stop_inst; + break; + + case 053: /* clear all chan */ + if (cpu_unit.flags & UNIT_SBS) sbs_enb = 0; + else reason = stop_inst; + break; + + case 054: /* seq brk off */ + sbs = sbs & ~SB_ON; + break; + + case 055: /* seq brk on */ + sbs = sbs | SB_ON; + break; + + case 056: /* clear seq brk */ + sbs = 0; /* clear PI */ + sbs_req = 0; + sbs_enb = 0; + sbs_act = 0; + break; + + case 061: case 062: case 063: /* drum */ + io_data = drm (IR, dev, IO); + break; + + case 064: /* drum/leave rm */ + if (cpu_unit.flags & UNIT_1D) rm = 0; + else io_data = drm (IR, dev, IO); + break; + + case 065: /* enter rm */ + if (cpu_unit.flags & UNIT_1D) { + rm = 1; + rmask = IO; + } + else reason = stop_inst; + break; + + case 066: /* rename mem */ + if (cpu_unit.flags & UNIT_1D45) { /* SN45? */ + int32 from = (IR >> 9) & RM45_M_BNK; + int32 to = (IR >> 6) & RM45_M_BNK; + rname[from] = to; + } + else reason = stop_inst; + break; + + case 067: /* reset renaming */ + if (cpu_unit.flags & UNIT_1D45) { /* SN45 */ + for (i = 0; i < RN45_SIZE; i++) rname[i] = i; + } + else reason = stop_inst; + break; + + case 074: /* extend mode */ + extm = (IR >> 11) & 1; /* set from IR<6> */ + break; + + default: /* undefined */ + reason = stop_inst; + break; + } /* end switch dev */ + + IO = io_data & DMASK; + if (io_data & IOT_SKP) PC = INCR_ADDR (PC); /* skip? */ + if (io_data >= IOT_REASON) reason = io_data >> IOT_V_REASON; + sbs_lvl = sbs_eval (); /* eval SBS system */ + break; + + default: /* undefined */ + if (rm && !sbs_act) /* restrict, ~brk? */ + reason = set_rmv (RTB_ILL); /* violation */ + else reason = STOP_RSRV; /* halt */ + break; + } /* end switch op */ + + if (reason == ERR_RMV) { /* restrict viol? */ + sbs_req |= SBS_MASK (SBS_LVL_RMV); /* request break */ + sbs_lvl = sbs_eval (); /* re-eval SBS */ + reason = 0; /* continue */ + } + } /* end while */ +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Effective address routine for standard memory reference instructions */ + +t_stat Ea (int32 IR) +{ +int32 i; +t_stat r; + +MA = (PC & EPCMASK) | (IR & DAMASK); /* direct address */ +if (IR & IA) { /* indirect addr? */ + if (extm) { /* extend? */ + if (r = Read ()) return r; /* read; err? */ + MA = MB & AMASK; /* one level */ + } + else { /* multi-level */ + for (i = 0; i < ind_max; i++) { /* count indirects */ + if (r = Read ()) return r; /* get ind word */ + MA = (PC & EPCMASK) | (MB & DAMASK); + if ((MB & IA) == 0) break; + } + if (i >= ind_max) return STOP_IND; /* indirect loop? */ + } /* end else !extm */ + } /* end if indirect */ +if (hst_p) hst[hst_p].ea = MA; /* history enabled? */ +return SCPE_OK; +} + +/* Effective address routine for character instructions */ + +t_stat Ea_ch (int32 IR, int32 *bn) +{ +int32 i; +t_stat r; + +MA = (PC & EPCMASK) | (IR & DAMASK); /* direct address */ +if (extm) { /* extend? */ + if (r = Read ()) return r; /* read; err? */ + } +else { /* multi-level */ + for (i = 0; i < ind_max; i++) { /* count indirects */ + if (r = Read ()) return r; /* get ind word */ + if ((MB & IA) == 0) break; + MA = (PC & EPCMASK) | (MB & DAMASK); + } + if (i >= ind_max) return STOP_IND; /* indirect loop? */ + } /* end else !extm */ +if (IR & IA) { /* automatic mode? */ + if (rm & !sbs_act & ((MB & 0607777) == 0607777)) /* page cross? */ + return set_rmv (RTB_CHR); + MB = inc_bp (MB); /* incr byte ptr */ + Write (); /* rewrite */ + } +*bn = (MB >> 16) & 03; /* byte num */ +if (extm) MA = MB & AMASK; /* final ea */ +else MA = (PC & EPCMASK) | (MB & DAMASK); +if (hst_p) hst[hst_p].ea = MA; /* history enabled? */ +return SCPE_OK; +} + +/* Increment byte pointer, allowing for ring mode */ + +int32 inc_bp (int32 bp) +{ +bp = bp + (1 << 16); /* add to bit<1> */ +if (bp > DMASK) { /* carry out? */ + if (PF & PF_RNG) /* ring mode? */ + bp = (1 << 16) | (bp & 0177770) | ((bp + 1) & 07); + else bp = (1 << 16) | ((bp + 1) & AMASK); + } +return bp; +} + +/* Read and write memory */ + +t_stat Read (void) +{ +if (rm && !sbs_act) { /* restrict check? */ + int32 bnk = MA_GETBNK (MA); /* get bank */ + if ((rmask << bnk) & SIGN) return set_rmv (0); + } +MB = M[MA]; +if (hst_p) hst[hst_p].opnd = MB; /* history enabled? */ +return SCPE_OK; +} + +t_stat Write (void) +{ +if (hst_p) hst[hst_p].opnd = M[MA]; /* hist? old contents */ +if (rm && !sbs_act) { /* restrict check? */ + int32 bnk = MA_GETBNK (MA); /* get bank */ + if ((rmask << bnk) & SIGN) return set_rmv (0); + } +if (MEM_ADDR_OK (MA)) M[MA] = MB; +return SCPE_OK; +} + +/* Restrict mode trap */ + +t_stat set_rmv (int32 code) +{ +rtb = code | (MB & RTB_MB_MASK); +return ERR_RMV; +} + +/* Evaluate SBS system */ + +int32 sbs_eval (void) +{ +int32 hi; + +if (cpu_unit.flags & UNIT_SBS) { /* SBS enabled? */ + if (sbs_req == 0) return 0; /* any requests? */ + hi = sbs_ffo (sbs_req); /* find highest */ + if (hi < sbs_ffo (sbs_act)) return hi + 1; /* higher? */ + } +return 0; +} + +/* Find first one in a 16b field */ + +int32 sbs_ffo (int32 mask) +{ +if (mask & 0177400) + return ffo_map[(mask >> 8) & 0377]; +else return (ffo_map[mask & 0377] + 8); +} + +/* Device request interrupt */ + +t_stat dev_req_int (int32 lvl) +{ +if (cpu_unit.flags & UNIT_SBS) { /* SBS enabled? */ + if (lvl >= SBS_LVLS) return SCPE_IERR; /* invalid level? */ + if (sbs_enb & SBS_MASK (lvl)) /* level active? */ + sbs_req |= SBS_MASK (lvl); /* set SBS request */ + } +else sbs |= SB_RQ; /* PI request */ +return SCPE_OK; +} + +/* Device set/show SBS level */ + +t_stat dev_set_sbs (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 *lvl = (int32 *) desc; +int32 newlvl; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG; +newlvl = get_uint (cptr, 10, SBS_LVLS - 1, &r); +if (r != SCPE_OK) return SCPE_ARG; +*lvl = newlvl; +return SCPE_OK; +} + +t_stat dev_show_sbs (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 *lvl = (int32 *) desc; + +if (lvl == NULL) return SCPE_IERR; +fprintf (st, "SBS level %d", *lvl); +return SCPE_OK; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int32 i; + +sbs = sbs_init; +extm = extm_init; +ioh = 0; +ios = 0; +cpls = 0; +sbs_act = 0; +sbs_req = 0; +sbs_enb = 0; +OV = 0; +PF = 0; +MA = 0; +MB = 0; +rm = 0; +rtb = 0; +rmask = 0; +for (i = 0; i < RN45_SIZE; i++) rname[i] = i; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & DMASK; +return SCPE_OK; +} + +/* Change memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* Set PDP-1D */ + +t_stat cpu_set_1d (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uptr->flags |= UNIT_SBS|UNIT_MDV; +return SCPE_OK; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 ov, pf, op, k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +t_value sim_eval; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC OV AC IO PF EA IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + ov = (h->ovac >> HIST_V_SHF) & 1; /* overflow */ + pf = (h->pfio >> HIST_V_SHF) & PF_VR_ALL; /* prog flags */ + op = ((h->ir >> 13) & 037); /* get opcode */ + fprintf (st, "%06o %o %06o %06o %03o ", + h->pc & AMASK, ov, h->ovac & DMASK, h->pfio & DMASK, pf); + if ((op < 032) && (op != 007)) /* mem ref instr */ + fprintf (st, "%06o ", h->ea); + else fprintf (st, " "); + sim_eval = h->ir; + if ((fprint_sym (st, h->pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %06o", h->ir); + else if (op < 030) /* mem ref instr */ + fprintf (st, " [%06o]", h->opnd); + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/PDP1/pdp1_dcs.c b/PDP1/pdp1_dcs.c new file mode 100644 index 0000000..deeef5c --- /dev/null +++ b/PDP1/pdp1_dcs.c @@ -0,0 +1,431 @@ +/* pdp1_dcs.c: PDP-1D terminal multiplexor simulator + + Copyright (c) 2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dcs Type 630 data communications subsystem + + This module implements up to 32 individual serial interfaces. +*/ + +#include "pdp1_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" + +#define DCS_LINES 32 /* lines */ +#define DCS_LINE_MASK (DCS_LINES - 1) +#define DCSL_WAIT 1000 /* output wait */ +#define DCS_NUMLIN dcs_desc.lines + +int32 dcs_sbs = 0; /* SBS level */ +uint32 dcs_send = 0; /* line for send */ +uint32 dcs_scan = 0; /* line for scanner */ +uint8 dcs_flg[DCS_LINES]; /* line flags */ +uint8 dcs_buf[DCS_LINES]; /* line bufffers */ + +extern int32 iosta, stop_inst; +extern int32 tmxr_poll; + +TMLN dcs_ldsc[DCS_LINES] = { 0 }; /* line descriptors */ +TMXR dcs_desc = { DCS_LINES, 0, 0, dcs_ldsc }; /* mux descriptor */ + +t_stat dcsi_svc (UNIT *uptr); +t_stat dcso_svc (UNIT *uptr); +t_stat dcs_reset (DEVICE *dptr); +t_stat dcs_attach (UNIT *uptr, char *cptr); +t_stat dcs_detach (UNIT *uptr); +t_stat dcs_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dcs_show (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dcs_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); +void dcs_reset_ln (int32 ln); +void dcs_scan_next (t_bool unlk); + +/* DCS data structures + + dcs_dev DCS device descriptor + dcs_unit DCS unit descriptor + dcs_reg DCS register list + dcs_mod DCS modifiers list +*/ + +REG dcs_nlreg = { DRDATA (NLINES, DCS_NUMLIN, 6), PV_LEFT }; + +UNIT dcs_unit = { UDATA (&dcsi_svc, UNIT_ATTABLE, 0) }; + +REG dcs_reg[] = { + { BRDATA (BUF, dcs_buf, 8, 8, DCS_LINES) }, + { BRDATA (FLAGS, dcs_flg, 8, 1, DCS_LINES) }, + { FLDATA (SCNF, iosta, IOS_V_DCS) }, + { ORDATA (SCAN, dcs_scan, 5) }, + { ORDATA (SEND, dcs_send, 5) }, + { DRDATA (SBSLVL, dcs_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB dcs_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &dcs_sbs }, + { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES", + &dcs_vlines, NULL, &dcs_nlreg }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dcs_desc }, + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &dcs_summ }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &dcs_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &dcs_show, NULL }, + { 0 } + }; + +DEVICE dcs_dev = { + "DCS", &dcs_unit, dcs_reg, dcs_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &dcs_reset, + NULL, &dcs_attach, &dcs_detach, + NULL, DEV_NET | DEV_DISABLE | DEV_DIS + }; + +/* DCSL data structures + + dcsl_dev DCSL device descriptor + dcsl_unit DCSL unit descriptor + dcsl_reg DCSL register list + dcsl_mod DCSL modifiers list +*/ + +UNIT dcsl_unit[] = { + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }, + { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT } + }; + +MTAB dcsl_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dcs_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &dcs_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &dcs_desc }, + { 0 } + }; + +REG dcsl_reg[] = { + { URDATA (TIME, dcsl_unit[0].wait, 10, 24, 0, + DCS_LINES, REG_NZ + PV_LEFT) }, + { NULL } + }; + +DEVICE dcsl_dev = { + "DCSL", dcsl_unit, dcsl_reg, dcsl_mod, + DCS_LINES, 10, 31, 1, 8, 8, + NULL, NULL, &dcs_reset, + NULL, NULL, NULL, + NULL, DEV_DIS + }; + +/* DCS IOT routine */ + +int32 dcs (int32 inst, int32 dev, int32 dat) +{ +int32 pls = (inst >> 6) & 077; + +if (dcs_dev.flags & DEV_DIS) /* disabled? */ + return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */ +if (pls & 020) dat = 0; /* pulse 20? clr IO */ + +switch (pls & 057) { /* case IR<6,8:11> */ + + case 000: /* RCH */ + dat |= dcs_buf[dcs_scan]; /* return line buf */ + dcs_flg[dcs_scan] = 0; /* clr line flag */ + break; + + case 001: /* RRC */ + dat |= dcs_scan; /* return line num */ + break; + + case 010: /* RCC */ + dat |= dcs_buf[dcs_scan]; /* return line buf */ + dcs_flg[dcs_scan] = 0; /* clr line flag */ + /* fall through */ + case 011: /* RSC */ + dcs_scan_next (TRUE); /* unlock scanner */ + break; + + case 040: /* TCB */ + dcs_buf[dcs_send] = dat & 0377; /* load buffer */ + dcs_flg[dcs_send] = 0; /* clr line flag */ + sim_activate (&dcsl_unit[dcs_send], dcsl_unit[dcs_send].wait); + break; + + case 041: /* SSB */ + dcs_send = dat & DCS_LINE_MASK; /* load line num */ + break; + + case 050: /* TCC */ + dcs_buf[dcs_scan] = dat & 0377; /* load buffer */ + dcs_flg[dcs_scan] = 0; /* clr line flag */ + sim_activate (&dcsl_unit[dcs_scan], dcsl_unit[dcs_scan].wait); + dcs_scan_next (TRUE); /* unlock scanner */ + break; + + default: + return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */ + } /* end case */ + +return dat; +} + +/* Unit service - receive side + + Poll all active lines for input + Poll for new connections +*/ + +t_stat dcsi_svc (UNIT *uptr) +{ +int32 ln, c, out; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +if (dcs_dev.flags & DEV_DIS) return SCPE_OK; +sim_activate (uptr, tmxr_poll); /* continue poll */ +ln = tmxr_poll_conn (&dcs_desc); /* look for connect */ +if (ln >= 0) { /* got one? */ + dcs_ldsc[ln].rcve = 1; /* set rcv enable */ + } +tmxr_poll_rx (&dcs_desc); /* poll for input */ +for (ln = 0; ln < DCS_NUMLIN; ln++) { /* loop thru lines */ + if (dcs_ldsc[ln].conn) { /* connected? */ + if (c = tmxr_getc_ln (&dcs_ldsc[ln])) { /* get char */ + if (c & SCPE_BREAK) c = 0; /* break? */ + else c = sim_tt_inpcvt (c, TT_GET_MODE (dcsl_unit[ln].flags)|TTUF_KSR); + dcs_buf[ln] = c; /* save char */ + dcs_flg[ln] = 1; /* set line flag */ + dcs_scan_next (FALSE); /* kick scanner */ + out = sim_tt_outcvt (c & 0177, TT_GET_MODE (dcsl_unit[ln].flags)); + if (out >= 0) { + tmxr_putc_ln (&dcs_ldsc[ln], out); /* echo char */ + tmxr_poll_tx (&dcs_desc); /* poll xmt */ + } + } + } + else dcs_ldsc[ln].rcve = 0; /* disconnected */ + } /* end for */ +return SCPE_OK; +} + +/* Unit service - transmit side */ + +t_stat dcso_svc (UNIT *uptr) +{ +int32 c; +uint32 ln = uptr - dcsl_unit; /* line # */ + +if (dcs_dev.flags & DEV_DIS) return SCPE_OK; +if (dcs_ldsc[ln].conn) { /* connected? */ + if (dcs_ldsc[ln].xmte) { /* xmt enabled? */ + c = sim_tt_outcvt (dcs_buf[ln] & 0177, TT_GET_MODE (uptr->flags)); + if (c >= 0) tmxr_putc_ln (&dcs_ldsc[ln], c); /* output char */ + tmxr_poll_tx (&dcs_desc); /* poll xmt */ + } + else { /* buf full */ + tmxr_poll_tx (&dcs_desc); /* poll xmt */ + sim_activate (uptr, uptr->wait); /* reschedule */ + return SCPE_OK; + } + } +dcs_flg[ln] = 1; /* set line flag */ +dcs_scan_next (FALSE); /* kick scanner */ +return SCPE_OK; +} + +/* Kick scanner */ + +void dcs_scan_next (t_bool unlk) +{ +int32 i; + +if (unlk) iosta &= ~IOS_DCS; /* unlock? */ +else if (iosta & IOS_DCS) return; /* no, locked? */ +for (i = 0; i < DCS_LINES; i++) { /* scan flags */ + dcs_scan = (dcs_scan + 1) & DCS_LINE_MASK; /* next flag */ + if (dcs_flg[dcs_scan] != 0) { /* flag set? */ + iosta |= IOS_DCS; /* lock scanner */ + dev_req_int (dcs_sbs); /* request intr */ + return; + } + } +return; +} + +/* Reset routine */ + +t_stat dcs_reset (DEVICE *dptr) +{ +int32 i; + +if (dcs_dev.flags & DEV_DIS) /* master disabled? */ + dcsl_dev.flags = dcsl_dev.flags | DEV_DIS; /* disable lines */ +else dcsl_dev.flags = dcsl_dev.flags & ~DEV_DIS; +if (dcs_unit.flags & UNIT_ATT) /* master att? */ + sim_activate_abs (&dcs_unit, tmxr_poll); /* activate */ +else sim_cancel (&dcs_unit); /* else stop */ +for (i = 0; i < DCS_LINES; i++) dcs_reset_ln (i); /* reset lines */ +dcs_send = 0; +dcs_scan = 0; +iosta &= ~IOS_DCS; /* clr intr req */ +return SCPE_OK; +} + +/* Attach master unit */ + +t_stat dcs_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&dcs_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +sim_activate_abs (uptr, tmxr_poll); /* start poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat dcs_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&dcs_desc, uptr); /* detach */ +for (i = 0; i < DCS_LINES; i++) dcs_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat dcs_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < DCS_LINES; i++) t = t + (dcs_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat dcs_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < DCS_LINES; i++) t = t + (dcs_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < DCS_LINES; i++) { + if (dcs_ldsc[i].conn) { + if (val) tmxr_fconns (st, &dcs_ldsc[i], i); + else tmxr_fstats (st, &dcs_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Change number of lines */ + +t_stat dcs_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newln = get_uint (cptr, 10, DCS_LINES, &r); +if ((r != SCPE_OK) || (newln == DCS_NUMLIN)) return r; +if (newln == 0) return SCPE_ARG; +if (newln < DCS_LINES) { + for (i = newln, t = 0; i < DCS_NUMLIN; i++) t = t | dcs_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < DCS_NUMLIN; i++) { + if (dcs_ldsc[i].conn) { + tmxr_linemsg (&dcs_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&dcs_ldsc[i]); /* reset line */ + } + dcsl_unit[i].flags = dcsl_unit[i].flags | UNIT_DIS; + dcs_reset_ln (i); + } + } +else { + for (i = DCS_NUMLIN; i < newln; i++) { + dcsl_unit[i].flags = dcsl_unit[i].flags & ~UNIT_DIS; + dcs_reset_ln (i); + } + } +DCS_NUMLIN = newln; +return SCPE_OK; +} + +/* Reset an individual line */ + +void dcs_reset_ln (int32 ln) +{ +sim_cancel (&dcsl_unit[ln]); +dcs_buf[ln] = 0; +dcs_flg[ln] = 0; +return; +} diff --git a/PDP1/pdp1_defs.h b/PDP1/pdp1_defs.h new file mode 100644 index 0000000..ef35bde --- /dev/null +++ b/PDP1/pdp1_defs.h @@ -0,0 +1,194 @@ +/* pdp1_defs.h: 18b PDP simulator definitions + + Copyright (c) 1993-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 21-Dec-06 RMS Added 16-channel sequence break support + 22-Jul-05 RMS Fixed definition of CPLS_DPY + 08-Feb-04 PLB Added support for display + 08-Dec-03 RMS Added support for parallel drum + 18-Oct-03 RMS Added DECtape off reel message + 22-Jul-03 RMS Updated for "hardware" RIM loader + Revised to detect I/O wait hang + 05-Dec-02 RMS Added IOT skip support (required by drum) + 14-Apr-99 RMS Changed t_addr to unsigned + + The PDP-1 was Digital's first computer. The system design evolved during + its life, and as a result, specifications are sketchy or contradictory. + This simulator is based on the 1962 maintenance manual. + + This simulator implements the following options: + + Automatic multiply/divide Type 10 + Memory extension control Type 15 + Parallel drum Type 23 + Serial drum Type 24 + Graphic display Type 30 + Line printer control Type 62 + Microtape (DECtape) control Type 550 +*/ + +#ifndef _PDP1_DEFS_H_ +#define _PDP1_DEFS_H_ 0 + +#include "sim_defs.h" + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_XCT 4 /* nested XCT's */ +#define STOP_IND 5 /* nested indirects */ +#define STOP_WAIT 6 /* IO wait hang */ +#define STOP_DTOFF 7 /* DECtape off reel */ +#define ERR_RMV 10 /* restrict mode viol */ + +/* Memory */ + +#define ASIZE 16 /* address bits */ +#define MAXMEMSIZE (1u << ASIZE) /* max mem size */ +#define AMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* Architectural constants */ + +#define SIGN 0400000 /* sign */ +#define DMASK 0777777 /* data mask */ +#define DAMASK 0007777 /* direct addr */ +#define EPCMASK (AMASK & ~DAMASK) /* extended addr */ +#define IA 0010000 /* indirect flag */ +#define IO_WAIT 0010000 /* I/O sync wait */ +#define IO_CPLS 0004000 /* completion pulse */ +#define OP_DAC 0240000 /* DAC */ +#define OP_DIO 0320000 /* DIO */ +#define OP_JMP 0600000 /* JMP */ +#define GEN_CPLS(x) (((x) ^ ((x) << 1)) & IO_WAIT) /* completion pulse? */ + +/* Program flags/sense switches */ + +#define PF_V_L 7 +#define PF_V_RNG 6 +#define PF_L (1u << PF_V_L) +#define PF_RNG (1u << PF_V_RNG) +#define PF_SS_1 0040 +#define PF_SS_2 0020 +#define PF_SS_3 0010 +#define PF_SS_4 0004 +#define PF_SS_5 0002 +#define PF_SS_6 0001 +#define PF_VR_ALL 0377 +#define PF_SS_ALL 0077 + +/* Restict mode */ + +#define RTB_IOT 0400000 +#define RTB_ILL 0200000 +#define RTB_HLT 0100000 +#define RTB_DBK 0040000 +#define RTB_CHR 0020000 +#define RTB_MB_MASK 0017777 + +#define RM45_V_BNK 14 +#define RM45_M_BNK 003 +#define RM48_V_BNK 12 +#define RM48_M_BNK 017 + +#define RN45_SIZE 4 + +/* IOT subroutine return codes */ + +#define IOT_V_SKP 18 /* skip */ +#define IOT_SKP (1 << IOT_V_SKP) +#define IOT_V_REASON (IOT_V_SKP + 1) /* reason */ +#define IOT_REASON (1 << IOT_V_REASON) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* I/O status flags */ + +#define IOS_V_LPN 17 /* light pen */ +#define IOS_V_PTR 16 /* paper tape reader */ +#define IOS_V_TTO 15 /* typewriter out */ +#define IOS_V_TTI 14 /* typewriter in */ +#define IOS_V_PTP 13 /* paper tape punch */ +#define IOS_V_DRM 12 /* drum */ +#define IOS_V_SQB 11 /* sequence break */ +#define IOS_V_PNT 3 /* print done */ +#define IOS_V_SPC 2 /* space done */ +#define IOS_V_DCS 1 /* data comm sys */ +#define IOS_V_DRP 0 /* parallel drum busy */ + +#define IOS_LPN (1 << IOS_V_LPN) +#define IOS_PTR (1 << IOS_V_PTR) +#define IOS_TTO (1 << IOS_V_TTO) +#define IOS_TTI (1 << IOS_V_TTI) +#define IOS_PTP (1 << IOS_V_PTP) +#define IOS_DRM (1 << IOS_V_DRM) +#define IOS_SQB (1 << IOS_V_SQB) +#define IOS_PNT (1 << IOS_V_PNT) +#define IOS_SPC (1 << IOS_V_SPC) +#define IOS_DCS (1 << IOS_V_DCS) +#define IOS_DRP (1 << IOS_V_DRP) + +/* Completion pulses */ + +#define CPLS_V_PTR 5 +#define CPLS_V_PTP 4 +#define CPLS_V_TTO 3 +#define CPLS_V_LPT 2 +#define CPLS_V_DPY 1 +#define CPLS_PTR (1 << CPLS_V_PTR) +#define CPLS_PTP (1 << CPLS_V_PTP) +#define CPLS_TTO (1 << CPLS_V_TTO) +#define CPLS_LPT (1 << CPLS_V_LPT) +#define CPLS_DPY (1 << CPLS_V_DPY) + +/* One channel sequence break */ + +#define SB_V_IP 0 /* in progress */ +#define SB_V_RQ 1 /* request */ +#define SB_V_ON 2 /* enabled */ + +#define SB_IP (1 << SB_V_IP) +#define SB_RQ (1 << SB_V_RQ) +#define SB_ON (1 << SB_V_ON) + +/* 16 channel sequence break */ + +#define SBS_LVLS 16 /* num levels */ +#define SBS_LVL_MASK (SBS_LVLS - 1) +#define SBS_LVL_RMV 14 /* restrict level */ +#define SBS_MASK(x) (1u << (SBS_LVLS - 1 - (x))) /* level to mask */ + +/* Timers */ + +#define TMR_CLK 0 + +/* Device routines */ + +t_stat dev_req_int (int32 lvl); +t_stat dev_set_sbs (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dev_show_sbs (FILE *st, UNIT *uptr, int32 val, void *desc); + +#endif diff --git a/PDP1/pdp1_diag.txt b/PDP1/pdp1_diag.txt new file mode 100644 index 0000000..3b5d6db --- /dev/null +++ b/PDP1/pdp1_diag.txt @@ -0,0 +1,53 @@ +MAINDEC 1 Instruction Test + +sim> att -e ptr digital-1-12-m-rim.bin +sim> boot ptr + +HALT instruction, PC: 000002 (JMP 3000) +sim> c + +(Test runs until interrupted unless there are errors) + +^E +Simulation stopped, PC: 000007 (SZS2 I) +sim> d ss 77 +sim> run 32 + +(Test runs until interrupted unless there are errors) + +^E +Simulation stopped, PC: 000032 (SZS1) +sim> d tw 777777 +sim> d ss 0 +sim> run 7772 + +Undefined instruction, PC: 000001 (SZS1) +sim> ex ac,io,pf +AC: 000777 +IO: 777000 +PF: 77 + +(These are the expected final values for the diagnostic) + + +MAINDEC 4 Multiply/Divide Test + +sim> att -e ptr maindec-4_4-16-68.bin +sim> break 0 +sim> boot ptr + +Breakpoint, PC: 000000 (JMP 532) +sim> set cpu mdv +sim> c + +(Test runs until interrupted unless there are errors) +^E + +Simulation stopped, PC: ... +sim> set cpu nomdv +sim> run 1 + +(Test runs until interrupted unless there are errors) +^E + +Simulation stopped, PC: ... diff --git a/PDP1/pdp1_drm.c b/PDP1/pdp1_drm.c new file mode 100644 index 0000000..beffa0a --- /dev/null +++ b/PDP1/pdp1_drm.c @@ -0,0 +1,362 @@ +/* pdp1_drm.c: PDP-1 drum simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + drp Type 23 parallel drum + drm Type 24 serial drum + + 21-Dec-06 RMS Added 16-chan SBS support + 08-Dec-03 RMS Added parallel drum support + Fixed bug in DBL/DCN decoding + 26-Oct-03 RMS Cleaned up buffer copy code + 23-Jul-03 RMS Fixed incorrect logical, missing activate + 05-Dec-02 RMS Cloned from pdp18b_drm.c +*/ + +#include "pdp1_defs.h" +#include + +/* Serial drum constants */ + +#define DRM_NUMWDS 256 /* words/sector */ +#define DRM_NUMSC 2 /* sectors/track */ +#define DRM_NUMTR 256 /* tracks/drum */ +#define DRM_NUMWDT (DRM_NUMWDS * DRM_NUMSC) /* words/track */ +#define DRM_SIZE (DRM_NUMTR * DRM_NUMWDT) /* words/drum */ +#define DRM_SMASK ((DRM_NUMTR * DRM_NUMSC) - 1) /* sector mask */ + +/* Parallel drum constants */ + +#define DRP_NUMWDT 4096 /* words/track */ +#define DRP_NUMTK 32 /* tracks/drum */ +#define DRP_SIZE (DRP_NUMWDT * DRP_NUMTK) /* words/drum */ +#define DRP_V_RWE 17 /* read/write enable */ +#define DRP_V_FLD 12 /* drum field */ +#define DRP_M_FLD 037 +#define DRP_TAMASK 07777 /* track address */ +#define DRP_WCMASK 07777 /* word count */ +#define DRP_MAINCM 07777 /* mem addr incr */ +#define DRP_GETRWE(x) (((x) >> DRP_V_RWE) & 1) +#define DRP_GETRWF(x) (((x) >> DRP_V_FLD) & DRP_M_FLD) + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define DRM_READ 000 /* read */ +#define DRM_WRITE 010 /* write */ +#define DRP_RW 000 /* read/write */ +#define DRP_BRK 001 /* break on address */ + +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DRM_NUMWDT))) + +extern int32 M[]; +extern int32 iosta; +extern int32 stop_inst; +extern UNIT cpu_unit; + +/* Serial drum variables */ + +uint32 drm_da = 0; /* track address */ +uint32 drm_ma = 0; /* memory address */ +uint32 drm_err = 0; /* error flag */ +uint32 drm_wlk = 0; /* write lock */ +int32 drm_time = 4; /* inter-word time */ +int32 drm_sbs = 0; /* SBS level */ +int32 drm_stopioe = 1; /* stop on error */ + +/* Parallel drum variables */ + +uint32 drp_rde = 0; /* read enable */ +uint32 drp_wre = 0; /* write enable */ +uint32 drp_rdf = 0; /* read field */ +uint32 drp_wrf = 0; /* write field */ +uint32 drp_ta = 0; /* track address */ +uint32 drp_wc = 0; /* word count */ +uint32 drp_ma = 0; /* memory address */ +uint32 drp_err = 0; /* error */ +int32 drp_time = 2; /* inter-word time */ +int32 drp_stopioe = 1; /* stop on error */ + +/* Forward declarations */ + +t_stat drm_svc (UNIT *uptr); +t_stat drm_reset (DEVICE *dptr); +t_stat drp_svc (UNIT *uptr); +t_stat drp_reset (DEVICE *dptr); + +/* DRM data structures + + drm_dev DRM device descriptor + drm_unit DRM unit descriptor + drm_reg DRM register list +*/ + +UNIT drm_unit = { + UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DRM_SIZE) + }; + +REG drm_reg[] = { + { ORDATA (DA, drm_da, 9) }, + { ORDATA (MA, drm_ma, 16) }, + { FLDATA (DONE, iosta, IOS_V_DRM) }, + { FLDATA (ERR, drm_err, 0) }, + { ORDATA (WLK, drm_wlk, 32) }, + { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, + { DRDATA (SBSLVL, drm_sbs, 4), REG_HRO }, + { FLDATA (STOP_IOE, drm_stopioe, 0) }, + { NULL } + }; + +MTAB drm_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "APILVL", "APILVL", + &dev_set_sbs, &dev_show_sbs, (void *) &drm_sbs }, + { 0 } + }; + +DEVICE drm_dev = { + "DRM", &drm_unit, drm_reg, drm_mod, + 1, 8, 20, 1, 8, 18, + NULL, NULL, &drm_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE + }; + +/* DRP data structures + + drp_dev DRP device descriptor + drp_unit DRP unit descriptor + drp_reg DRP register list +*/ + +UNIT drp_unit = { + UDATA (&drp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DRM_SIZE) + }; + +REG drp_reg[] = { + { ORDATA (TA, drp_ta, 12) }, + { ORDATA (RDF, drp_rdf, 5) }, + { FLDATA (RDE, drp_rde, 0) }, + { FLDATA (WRF, drp_wrf, 5) }, + { FLDATA (WRE, drp_wre, 0) }, + { ORDATA (MA, drp_ma, 16) }, + { ORDATA (WC, drp_wc, 12) }, + { FLDATA (BUSY, iosta, IOS_V_DRP) }, + { FLDATA (ERR, drp_err, 0) }, + { DRDATA (TIME, drp_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, drp_stopioe, 0) }, + { DRDATA (SBSLVL, drm_sbs, 4), REG_HRO }, + { NULL } + }; + +DEVICE drp_dev = { + "DRP", &drp_unit, drp_reg, NULL, + 1, 8, 20, 1, 8, 18, + NULL, NULL, &drp_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 drm (int32 IR, int32 dev, int32 dat) +{ +int32 t; +int32 pulse = (IR >> 6) & 037; + +if ((drm_dev.flags & DEV_DIS) == 0) { /* serial enabled? */ + if ((pulse != 001) && (pulse != 011)) /* invalid pulse? */ + return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ + switch (dev) { /* switch on device */ + + case 061: /* DWR, DRD */ + drm_ma = dat & AMASK; /* load mem addr */ + drm_unit.FUNC = pulse & DRM_WRITE; /* save function */ + break; + + case 062: /* DBL, DCN */ + if ((pulse & 010) == 0) /* DBL? */ + drm_da = dat & DRM_SMASK; /* load sector # */ + iosta = iosta & ~IOS_DRM; /* clear flags */ + drm_err = 0; + t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); + if (t <= 0) t = t + DRM_NUMWDT; /* wrap around? */ + sim_activate (&drm_unit, t); /* start operation */ + break; + + case 063: /* DTD */ + if (pulse == 011) return (stop_inst << IOT_V_REASON) | dat; + if (iosta & IOS_DRM) return (dat | IOT_SKP); /* skip if done */ + break; + + case 064: /* DSE, DSP */ + if ((drm_err == 0) || (pulse & 010)) /* no error, par test? */ + return (dat | IOT_SKP); + } /* end case */ + + return dat; + } /* end if serial */ + +if ((drp_dev.flags & DEV_DIS) == 0) { /* parallel enabled? */ + switch (dev) { /* switch on device */ + + case 061: /* DIA, DBA */ + drp_err = 0; /* clear error */ + iosta = iosta & ~IOS_DRP; /* not busy */ + drp_rde = DRP_GETRWE (dat); /* set read enable */ + drp_rdf = DRP_GETRWF (dat); /* set read field */ + drp_ta = dat & DRP_TAMASK; /* set track addr */ + if (IR & 02000) { /* DBA? */ + t = drp_ta - GET_POS (drp_time); /* delta words */ + if (t <= 0) t = t + DRP_NUMWDT; /* wrap around? */ + sim_activate (&drp_unit, t); /* start operation */ + drp_unit.FUNC = DRP_BRK; /* mark as break */ + } + else drp_unit.FUNC = DRP_RW; /* no, read/write */ + break; + + case 062: /* DWC, DRA */ + if (IR & 02000) dat = GET_POS (drp_time) | /* DRA, get position */ + (drp_err? 0400000: 0); + else { /* DWC */ + drp_wre = DRP_GETRWE (dat); /* set write enable */ + drp_wrf = DRP_GETRWF (dat); /* set write field */ + drp_wc = dat & DRP_WCMASK; /* set word count */ + } + break; + + case 063: /* DCL */ + drp_ma = dat & AMASK; /* set mem address */ + t = drp_ta - GET_POS (drp_time); /* delta words */ + if (t <= 0) t = t + DRP_NUMWDT; /* wrap around? */ + sim_activate (&drp_unit, t); /* start operation */ + iosta = iosta | IOS_DRP; /* set busy */ + break; + + case 064: /* not assigned */ + return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ + } /* end case */ + + return dat; + } /* end if parallel */ + +return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ +} + +/* Serial unit service - this code assumes the entire drum is buffered */ + +t_stat drm_svc (UNIT *uptr) +{ +uint32 i, da; +uint32 *fbuf = uptr->filebuf; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + drm_err = 1; /* set error */ + iosta = iosta | IOS_DRM; /* set done */ + dev_req_int (drm_sbs); /* req intr */ + return IORETURN (drm_stopioe, SCPE_UNATT); + } + +da = drm_da * DRM_NUMWDS; /* compute dev addr */ +for (i = 0; i < DRM_NUMWDS; i++, da++) { /* do transfer */ + if (uptr->FUNC == DRM_READ) { /* read? */ + if (MEM_ADDR_OK (drm_ma)) /* if !nxm */ + M[drm_ma] = fbuf[da]; /* read word */ + } + else { /* write */ + if ((drm_wlk >> (drm_da >> 4)) & 1) drm_err = 1; + else { /* not locked */ + fbuf[da] = M[drm_ma]; /* write word */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + } + } + drm_ma = (drm_ma + 1) & AMASK; /* incr mem addr */ + } +drm_da = (drm_da + 1) & DRM_SMASK; /* incr dev addr */ +iosta = iosta | IOS_DRM; /* set done */ +dev_req_int (drm_sbs); /* req intr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat drm_reset (DEVICE *dptr) +{ +if ((drm_dev.flags & DEV_DIS) == 0) + drp_dev.flags = drp_dev.flags | DEV_DIS; +drm_da = drm_ma = drm_err = 0; +iosta = iosta & ~IOS_DRM; +sim_cancel (&drm_unit); +drm_unit.FUNC = 0; +return SCPE_OK; +} + +/* Parallel unit service - this code assumes the entire drum is buffered */ + +t_stat drp_svc (UNIT *uptr) +{ +uint32 i, lim; +uint32 *fbuf = uptr->filebuf; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + drp_err = 1; /* set error */ + iosta = iosta & ~IOS_DRP; /* clear busy */ + if (uptr->FUNC) dev_req_int (drm_sbs); /* req intr */ + return IORETURN (drp_stopioe, SCPE_UNATT); + } + +if (uptr->FUNC == DRP_RW) { /* read/write? */ + lim = drp_wc? drp_wc: DRP_TAMASK + 1; /* eff word count */ + for (i = 0; i < lim; i++) { /* do transfer */ + if (drp_wre) /* write enabled? */ + fbuf[(drp_wrf << DRP_V_FLD) | drp_ta] = M[drp_ma]; + if (drp_rde && MEM_ADDR_OK (drp_ma)) /* read enabled? */ + M[drp_ma] = fbuf[(drp_rdf << DRP_V_FLD) | drp_ta]; + drp_ta = (drp_ta + 1) & DRP_TAMASK; /* incr track addr */ + drp_ma = ((drp_ma & ~DRP_MAINCM) | ((drp_ma + 1) & DRP_MAINCM)); + } /* end for */ + } /* end if */ +iosta = iosta & ~IOS_DRP; /* clear busy */ +if (uptr->FUNC) dev_req_int (drm_sbs); /* req intr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat drp_reset (DEVICE *dptr) +{ +if ((drp_dev.flags & DEV_DIS) == 0) + drm_dev.flags = drm_dev.flags | DEV_DIS; +drp_ta = 0; +drp_rde = drp_rdf = drp_wre = drp_wrf = 0; +drp_err = 0; +drp_ma = 0; +drp_wc = 0; +iosta = iosta & ~IOS_DRP; +sim_cancel (&drp_unit); +drp_unit.FUNC = 0; +return SCPE_OK; +} diff --git a/PDP1/pdp1_dt.c b/PDP1/pdp1_dt.c new file mode 100644 index 0000000..731fc8f --- /dev/null +++ b/PDP1/pdp1_dt.c @@ -0,0 +1,1085 @@ +/* pdp1_dt.c: 18b DECtape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dt Type 550/555 DECtape + + 21-Dec-06 RMS Added 16-channel SBS support + 23-Jun-06 RMS Fixed conflict in ATTACH switches + Revised header format + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 25-Jan-04 RMS Revised for device debug support + 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR + 26-Oct-03 RMS Cleaned up buffer copy code + 18-Oct-03 RMS Added DECtape off reel message, simplified timing + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 17-Oct-02 RMS Fixed bug in end of reel logic + 06-Oct-02 RMS Added device disable support + 13-Aug-02 RMS Cloned from pdp18b_dt.c + + 18b DECtapes are represented in memory by fixed length buffer of 32b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the TD8E formatter) is: + + reverse end zone 8192 reverse end zone codes ~ 10 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 8192 forward end zone codes ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the interblock words in the + bit bucket. + + The Type 550 controller has a 4b unit select field, for units 1-8. The code + assumes that the GETUNIT macro returns a unit number in the range of 0-7, + with 8 represented as 0, and an invalid unit as -1. +*/ + +#include "pdp1_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define DT_WC 030 /* word count */ +#define DT_CA 031 /* current addr */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_BLKWD 1 /* blk no word in h/t */ +#define DT_CSMWD 4 /* checksum word in h/t */ +#define DT_HTWRD 5 /* header/trailer words */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ +#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ +#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ +#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 256 /* block size in 18b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ +#define D11_FILSIZ (D18_CAPAC * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 86 /* block size in 18b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) +#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D18_CAPAC /* default */ +#define DT_WSIZE D18_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Status register A */ + +#define DTA_V_UNIT 12 /* unit select */ +#define DTA_M_UNIT 017 +#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) +#define DTA_V_MOT 4 /* motion */ +#define DTA_M_MOT 03 +#define DTA_V_FNC 0 /* function */ +#define DTA_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_WRIT 03 /* write */ +#define FNC_RALL 05 /* read all */ +#define FNC_WALL 06 /* write all */ +#define FNC_WMRK 07 /* write timing */ +#define DTA_STSTP (1u << (DTA_V_MOT + 1)) +#define DTA_FWDRV (1u << DTA_V_MOT) +#define DTA_MODE 0 /* not implemented */ +#define DTA_RW 077 +#define DTA_GETUNIT(x) map_unit[(((x) >> DTA_V_UNIT) & DTA_M_UNIT)] +#define DT_UPDINT if (dtsb & (DTB_DTF | DTB_BEF | DTB_ERF)) \ + dev_req_int (dt_sbs); + +#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) +#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) + +/* Status register B */ + +#define DTB_V_DTF 17 /* data flag */ +#define DTB_V_BEF 16 /* block end flag */ +#define DTB_V_ERF 15 /* error flag */ +#define DTB_V_END 14 /* end of tape */ +#define DTB_V_TIM 13 /* timing err */ +#define DTB_V_REV 12 /* reverse */ +#define DTB_V_GO 11 /* go */ +#define DTB_V_MRK 10 /* mark trk err */ +#define DTB_V_SEL 9 /* select err */ +#define DTB_DTF (1u << DTB_V_DTF) +#define DTB_BEF (1u << DTB_V_BEF) +#define DTB_ERF (1u << DTB_V_ERF) +#define DTB_END (1u << DTB_V_END) +#define DTB_TIM (1u << DTB_V_TIM) +#define DTB_REV (1u << DTB_V_REV) +#define DTB_GO (1u << DTB_V_GO) +#define DTB_MRK (1u << DTB_V_MRK) +#define DTB_SEL (1u << DTB_V_SEL) +#define DTB_ALLERR (DTB_END | DTB_TIM | DTB_MRK | DTB_SEL) + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR 7 /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Operation substates */ + +#define DTO_WCO 1 /* wc overflow */ +#define DTO_SOB 2 /* start of block */ + +/* Logging */ + +#define LOG_MS 001 /* move, search */ +#define LOG_RW 002 /* read write */ +#define LOG_BL 004 /* block # lblk */ + +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern int32 M[]; +extern int32 stop_inst; +extern UNIT cpu_unit; +extern int32 sim_switches; +extern int32 sim_is_running; +extern FILE *sim_deb; + +int32 dtsa = 0; /* status A */ +int32 dtsb = 0; /* status B */ +int32 dtdb = 0; /* data buffer */ +int32 dt_sbs = 0; /* SBS level */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_dctime = 40000; /* decel time */ +int32 dt_substate = 0; +int32 dt_logblk = 0; +int32 dt_stopoffr = 0; +static const int32 map_unit[16] = { /* Type 550 unit map */ + -1, 1, 2, 3, 4, 5, 6, 7, + 0, -1, -1, -1, -1, -1, -1, -1 + }; + +t_stat dt_svc (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, char *cptr); +t_stat dt_detach (UNIT *uptr); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) } + }; + +REG dt_reg[] = { + { ORDATA (DTSA, dtsa, 18) }, + { ORDATA (DTSB, dtsb, 18) }, + { ORDATA (DTDB, dtdb, 18) }, + { FLDATA (DTF, dtsb, DTB_V_DTF) }, + { FLDATA (BEF, dtsb, DTB_V_BEF) }, + { FLDATA (ERF, dtsb, DTB_V_ERF) }, + { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, + { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, + { ORDATA (SUBSTATE, dt_substate, 2) }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO) }, + { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, + DT_NUMDR, REG_RO) }, + { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, + DT_NUMDR, REG_HRO) }, + { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, + { DRDATA (SBSLVL, dt_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB dt_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &dt_sbs }, + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { 0 } + }; + +DEBTAB dt_deb[] = { + { "MOTION", LOG_MS }, + { "DATA", LOG_RW }, + { "BLOCK", LOG_BL }, + { NULL, 0 } + }; + +DEVICE dt_dev = { + "DT", dt_unit, dt_reg, dt_mod, + DT_NUMDR, 8, 24, 1, 8, 18, + NULL, NULL, &dt_reset, + NULL, &dt_attach, &dt_detach, + NULL, DEV_DISABLE | DEV_DEBUG, 0, + dt_deb, NULL, NULL + }; + +/* IOT routine */ + +int32 dt (int32 IR, int32 dev, int32 dat) +{ +int32 pulse = (IR >> 6) & 037; +int32 fnc, mot, unum; +UNIT *uptr = NULL; + +if (dt_dev.flags & DEV_DIS) /* disabled? */ + return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ +unum = DTA_GETUNIT (dtsa); /* get unit no */ +if (unum >= 0) uptr = dt_dev.units + unum; /* get unit */ + +if (pulse == 003) { /* MSE */ + if ((dtsa ^ dat) & DTA_UNIT) dt_deselect (dtsa); /* new unit? */ + dtsa = (dtsa & ~DTA_UNIT) | (dat & DTA_UNIT); + dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); + } +if (pulse == 004) { /* MLC */ + dtsa = (dtsa & ~DTA_RW) | (dat & DTA_RW); /* load dtsa */ + dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); + fnc = DTA_GETFNC (dtsa); /* get fnc */ + if ((uptr == NULL) || /* invalid? */ + ((uptr->flags) & UNIT_DIS) || /* disabled? */ + (fnc >= FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WLK)) || + ((fnc == FNC_WALL) && (uptr->flags & UNIT_WLK))) + dt_seterr (uptr, DTB_SEL); /* select err */ + else dt_newsa (dtsa); + } +if (pulse == 005) { /* MRD */ + dat = (dat & ~DMASK) | dtdb; + dtsb = dtsb & ~(DTB_DTF | DTB_BEF); + } +if (pulse == 006) { /* MWR */ + dtdb = dat & DMASK; + dtsb = dtsb & ~(DTB_DTF | DTB_BEF); + } +if (pulse == 007) { /* MRS */ + dtsb = dtsb & ~(DTB_REV | DTB_GO); /* clr rev, go */ + if (uptr) { /* valid unit? */ + mot = DTS_GETMOT (uptr->STATE); /* get motion */ + if (mot & DTS_DIR) dtsb = dtsb | DTB_REV; /* rev? set */ + if ((mot >= DTS_ACCF) || (uptr->STATE & 0777700)) + dtsb = dtsb | DTB_GO; /* accel? go */ + } + dat = (dat & ~DMASK) | dtsb; + } +DT_UPDINT; +return dat; +} + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit, old_mot; +UNIT *uptr; + +old_unit = DTA_GETUNIT (oldf); /* get unit no */ +if (old_unit < 0) return; /* invalid? */ +uptr = dt_dev.units + old_unit; /* get unit */ +old_mot = DTS_GETMOT (uptr->STATE); +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; +} + +/* Command register change + + 1. If change in motion, stop to start + - schedule acceleration + - set function as next state + 2. If change in motion, start to stop + - if not already decelerating (could be reversing), + schedule deceleration + 3. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 4. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 5. If not yet at speed, + - set function as next state + 6. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, new_fnc; +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +new_unit = DTA_GETUNIT (newf); /* new unit */ +if (new_unit < 0) return; /* invalid? */ +uptr = dt_dev.units + new_unit; +if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, DTB_SEL); /* no, error */ + return; + } +prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ +prev_mving = prev_mot != DTS_STOP; /* previous moving? */ +prev_dir = prev_mot & DTS_DIR; /* previous dir? */ +new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ +new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ +new_fnc = DTA_GETFNC (newf); /* new function? */ + +if ((prev_mving | new_mving) == 0) return; /* stop to stop */ + +if (new_mving & ~prev_mving) { /* start? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mving & ~new_mving) { /* stop? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + return; + } + +if (prev_dir ^ new_dir) { /* dir chg? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; + } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, newpos; +uint32 oldpos; + +oldpos = uptr->pos; /* save old pos */ +if (dt_setpos (uptr)) return; /* update pos */ +uptr->STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr->STATE); /* set variables */ +dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; +unum = (int32) (uptr - dt_dev.units); +if (oldpos == uptr->pos) /* bump pos */ + uptr->pos = uptr->pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr->pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, DTB_END); /* set ez flag, stop */ + return; + } +sim_cancel (uptr); /* cancel cur op */ +dt_substate = DTO_SOB; /* substate = block start */ +switch (fnc) { /* case function */ + + case DTS_OFR: /* off reel */ + if (dir) newpos = -1000; /* rev? < start */ + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; + + case FNC_MOVE: /* move */ + dt_schedez (uptr, dir); /* sched end zone */ + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", unum, (dir? + "backward": "forward")); + return; /* done */ + + case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n", unum, + (dir? "backward": "forward")); + break; + + case FNC_WRIT: /* write */ + case FNC_READ: /* read */ + case FNC_RALL: /* read all */ + case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; + else newpos = DT_EZLIN + (DT_WSIZE - 1); + } + else { + newpos = ((uptr->pos) / DT_WSIZE) * DT_WSIZE; + if (!dir) newpos = newpos + (DT_WSIZE - 1); + } + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: read all block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous": " ")); + break; + + default: + dt_seterr (uptr, DTB_SEL); /* bad state */ + return; + } + +if ((fnc == FNC_WRIT) || (fnc == FNC_WALL)) { /* write function? */ + dtsb = dtsb | DTB_DTF; /* set data flag */ + DT_UPDINT; + } +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr->STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr->LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr->LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ + + case DTS_STOP: /* stop */ + delta = 0; + break; + + case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; + udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; + + case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; + udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; + + case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; + } + +if (mot & DTS_DIR) uptr->pos = uptr->pos - delta; /* update pos */ +else uptr->pos = uptr->pos + delta; +if (((int32) uptr->pos < 0) || + ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { + detach_unit (uptr); /* off reel? */ + uptr->STATE = uptr->pos = 0; + unum = (int32) (uptr - dt_dev.units); + if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ + dt_seterr (uptr, DTB_SEL); /* error */ + return TRUE; + } +return FALSE; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr->STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr->STATE); +int32 *fbuf = (int32 *) uptr->filebuf; +int32 blk, wrd, ma, relpos; +uint32 ba; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { + + case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); + uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ + if (uptr->STATE) /* not stopped? */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */ + return SCPE_OK; + + case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ + return SCPE_OK; + + case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ + + default: /* other */ + dt_seterr (uptr, DTB_SEL); /* state error */ + return SCPE_OK; + } + +/* Functional cases + + Move - must be at end zone + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + } +blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ + +switch (fnc) { /* at speed, check fnc */ + + case FNC_MOVE: /* move */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + + case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr->STATE = uptr->pos = 0; /* no visible action */ + break; + +/* Search */ + + case FNC_SRCH: /* search */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ + dtdb = blk; /* store block # */ + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + +/* Read and read all */ + + case FNC_READ: case FNC_RALL: + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + dtdb = fbuf[ba]; /* get tape word */ + dtsb = dtsb | DTB_DTF; /* set flag */ + } + else { + ma = (2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1; + wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ +#if defined (OLD_TYPE550) + if ((wrd == 0) || /* skip 1st, last */ + (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; +#endif + if ((fnc == FNC_READ) && /* read, skip if not */ + (wrd != DT_CSMWD) && /* fwd, rev cksum */ + (wrd != ma)) break; + dtdb = dt_gethdr (uptr, blk, relpos); + if (wrd == (dir? DT_CSMWD: ma)) /* at end csum? */ + dtsb = dtsb | DTB_BEF; /* end block */ + else dtsb = dtsb | DTB_DTF; /* else next word */ + } + if (dir) dtdb = dt_comobv (dtdb); + break; + +/* Write and write all */ + + case FNC_WRIT: case FNC_WALL: + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + if (dir) fbuf[ba] = dt_comobv (dtdb); /* get data word */ + else fbuf[ba] = dtdb; + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + if (wrd == (dir? 0: DTU_BSIZE (uptr) - 1)) + dtsb = dtsb | DTB_BEF; /* end block */ + else dtsb = dtsb | DTB_DTF; /* else next word */ + } + else { + wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ +#if defined (OLD_TYPE550) + if ((wrd == 0) || /* skip 1st, last */ + (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; +#endif + if ((fnc == FNC_WRIT) && /* wr, skip if !csm */ + (wrd != ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1))) + break; + dtsb = dtsb | DTB_DTF; /* set flag */ + } + break; + + default: + dt_seterr (uptr, DTB_SEL); /* impossible state */ + break; + } + +DT_UPDINT; /* update interrupts */ +return SCPE_OK; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr->STATE); + +dtsa = dtsa & ~DTA_STSTP; /* clear go */ +dtsb = dtsb | DTB_ERF | e; /* set error flag */ +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) return; /* update position */ + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ + } +DT_UPDINT; +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */ +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Complement obverse routine */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ 0777777; /* compl obverse */ +dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | + ((dat >> 3) & 0700) | ((dat & 0700) << 3) | + ((dat & 070) << 9) | ((dat & 07) << 15); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int32 *fbuf = (int32 *) uptr->filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +csum = 0777777; +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = fbuf[ba + i]; /* get word */ + csum = csum + wrd; /* 1's comp add */ + if (csum > 0777777) csum = (csum + 1) & 0777777; + } +return (csum ^ 0777777); /* 1's comp res */ +} + +/* Get header word */ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) +{ +int32 wrd = relpos / DT_WSIZE; + +if (wrd == DT_BLKWD) return blk; /* fwd blknum */ +if (wrd == DT_CSMWD) return 0777777; /* rev csum */ +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ + return (dt_csum (uptr, blk)); +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ + return dt_comobv (blk); +return 0; /* all others */ +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all drives */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* CAF? */ + prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } + } + else { + sim_cancel (uptr); /* sim reset */ + uptr->STATE = 0; + uptr->LASTT = sim_grtime (); + } + } +dtsa = dtsb = 0; /* clear status */ +DT_UPDINT; /* reset interrupt */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 dt_iors (void) +{ +#if defined IOS_DTA +return ((dtsb & (DTB_ERF | DTB_DTF))? IOS_DTA: 0); +#else +return 0; +#endif +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 12b, read 12b format and convert to 18b in buffer + If 16b, read 16b format and convert to 18b in buffer + If 18b/36b, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, char *cptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, sz, k, *fbuf; +int32 u = uptr - dt_dev.units; +t_stat r; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error? */ +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default 18b */ + if (sim_switches & SWMASK ('T')) /* att 12b? */ + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = uptr->flags | UNIT_11FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D8_FILSIZ) + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sz == D11_FILSIZ) + uptr->flags = uptr->flags | UNIT_11FMT; + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +printf ("%s%d: ", sim_dname (&dt_dev), u); +if (uptr->flags & UNIT_8FMT) printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) printf ("16b format"); +else printf ("18b/36b format"); +printf (", buffering file in memory\n"); +if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ + fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | + ((uint32) (pdp8b[k + 1] >> 6) & 077); + fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | + ((uint32) pdp8b[k + 2] & 07777); + ba = ba + 2; + } /* end blk loop */ + } /* end file loop */ + uptr->hwmark = ba; + } /* end if */ +else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0; + for (k = 0; k < D18_BSIZE; k++) + fbuf[ba++] = pdp11b[k]; + } + uptr->hwmark = ba; /* end elif */ + } +else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), + uptr->capac, uptr->fileref); +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +uptr->LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If 12b, convert 18b buffer to 12b and write to file + If 16b, convert 18b buffer to 16b and write to file + If 18b/36b, write buffer to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, k, *fbuf; +int32 u = uptr - dt_dev.units; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if (sim_is_active (uptr)) { + sim_cancel (uptr); + if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { + dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; + DT_UPDINT; + } + uptr->STATE = uptr->pos = 0; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); + rewind (uptr->fileref); /* start of file */ + if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ + pdp8b[k] = (fbuf[ba] >> 6) & 07777; + pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | + ((fbuf[ba + 1] >> 12) & 077); + pdp8b[k + 2] = fbuf[ba + 1] & 07777; + ba = ba + 2; + } /* end loop blk */ + fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (ferror (uptr->fileref)) break; + } /* end loop file */ + } /* end if 12b */ + else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D18_BSIZE; k++) /* loop blk */ + pdp11b[k] = fbuf[ba++] & 0177777; + fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (ferror (uptr->fileref)) break; + } /* end loop file */ + } /* end if 16b */ + else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ + uptr->hwmark, uptr->fileref); + if (ferror (uptr->fileref)) perror ("I/O error"); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} diff --git a/PDP1/pdp1_lp.c b/PDP1/pdp1_lp.c new file mode 100644 index 0000000..1b7b058 --- /dev/null +++ b/PDP1/pdp1_lp.c @@ -0,0 +1,211 @@ +/* pdp1_lp.c: PDP-1 line printer simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + bused in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt Type 62 line printer for the PDP-1 + + 19-Jan-07 RMS Added UNIT_TEXT flag + 21-Dec-06 RMS Added 16-channel SBS support + 07-Sep-03 RMS Changed ioc to ios + 23-Jul-03 RMS Fixed bugs in instruction decoding, overprinting + Revised to detect I/O wait hang + 25-Apr-03 RMS Revised for extended file support + 30-May-02 RMS Widened POS to 32b + 13-Apr-01 RMS Revised for register arrays +*/ + +#include "pdp1_defs.h" + +#define BPTR_MAX 40 /* pointer max */ +#define LPT_BSIZE (BPTR_MAX * 3) /* line size */ +#define BPTR_MASK 077 /* buf ptr mask */ + +int32 lpt_spc = 0; /* print (0) vs spc */ +int32 lpt_ovrpr = 0; /* overprint */ +int32 lpt_stopioe = 0; /* stop on error */ +int32 lpt_bptr = 0; /* buffer ptr */ +int32 lpt_sbs = 0; /* SBS level */ +char lpt_buf[LPT_BSIZE + 1] = { 0 }; +static const unsigned char lpt_trans[64] = { + ' ','1','2','3','4','5','6','7','8','9','\'','~','#','V','^','<', + '0','/','S','T','U','V','W','X','Y','Z','"',',','>','^','-','?', + '@','J','K','L','M','N','O','P','Q','R','$','=','-',')','-','(', + '_','A','B','C','D','E','F','G','H','I','*','.','+',']','|','[' + }; + +extern int32 ios, cpls, iosta; +extern int32 stop_inst; + +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (PNT, iosta, IOS_V_PNT) }, + { FLDATA (SPC, iosta, IOS_V_SPC) }, + { FLDATA (RPLS, cpls, CPLS_V_LPT) }, + { DRDATA (BPTR, lpt_bptr, 6) }, + { ORDATA (LPT_STATE, lpt_spc, 6), REG_HRO }, + { FLDATA (LPT_OVRPR, lpt_ovrpr, 0), REG_HRO }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { BRDATA (LBUF, lpt_buf, 8, 8, LPT_BSIZE) }, + { DRDATA (SBSLVL, lpt_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB lpt_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &lpt_sbs }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE + }; + +/* Line printer IOT routine */ + +int32 lpt (int32 inst, int32 dev, int32 dat) +{ +int32 i; + +if (lpt_dev.flags & DEV_DIS) /* disabled? */ + return (stop_inst << IOT_V_REASON) | dat; /* stop if requested */ +if ((inst & 07000) == 01000) { /* fill buf */ + if (lpt_bptr < BPTR_MAX) { /* limit test ptr */ + i = lpt_bptr * 3; /* cvt to chr ptr */ + lpt_buf[i] = lpt_trans[(dat >> 12) & 077]; + lpt_buf[i + 1] = lpt_trans[(dat >> 6) & 077]; + lpt_buf[i + 2] = lpt_trans[dat & 077]; + } + lpt_bptr = (lpt_bptr + 1) & BPTR_MASK; + return dat; + } +if ((inst & 07000) == 02000) { /* space */ + iosta = iosta & ~IOS_SPC; /* space, clear flag */ + lpt_spc = (inst >> 6) & 077; /* state = space n */ + } +else if ((inst & 07000) == 00000) { /* print */ + iosta = iosta & ~IOS_PNT; /* clear flag */ + lpt_spc = 0; /* state = print */ + } +else return (stop_inst << IOT_V_REASON) | dat; /* not implemented */ +if (GEN_CPLS (inst)) { /* comp pulse? */ + ios = 0; /* clear flop */ + cpls = cpls | CPLS_LPT; /* request completion */ + } +else cpls = cpls & ~CPLS_LPT; +sim_activate (&lpt_unit, lpt_unit.wait); /* activate */ +return dat; +} + +/* Unit service, printer is in one of three states + + lpt_spc = 000 write buffer to file, set overprint + lpt_iot = 02x space command x, clear overprint +*/ + +t_stat lpt_svc (UNIT *uptr) +{ +int32 i; +static const char *lpt_cc[] = { + "\n", + "\n\n", + "\n\n\n", + "\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f" + }; + +if (cpls & CPLS_LPT) { /* completion pulse? */ + ios = 1; /* restart */ + cpls = cpls & ~CPLS_LPT; /* clr pulse pending */ + } +dev_req_int (lpt_sbs); /* req interrupt */ +if (lpt_spc) { /* space? */ + iosta = iosta | IOS_SPC; /* set flag */ + if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); + fputs (lpt_cc[lpt_spc & 07], uptr->fileref); /* print cctl */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + lpt_ovrpr = 0; /* dont overprint */ + } +else { + iosta = iosta | IOS_PNT; /* print */ + if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lpt_stopioe, SCPE_UNATT); + if (lpt_ovrpr) fputc ('\r', uptr->fileref); /* overprint? */ + fputs (lpt_buf, uptr->fileref); /* print buffer */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* test error */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + lpt_bptr = 0; + for (i = 0; i <= LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ + lpt_ovrpr = 1; /* set overprint */ + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +int32 i; + +lpt_bptr = 0; /* clear buffer ptr */ +for (i = 0; i <= LPT_BSIZE; i++) lpt_buf[i] = 0; /* clear buffer */ +lpt_spc = 0; /* clear state */ +lpt_ovrpr = 0; /* clear overprint */ +cpls = cpls & ~CPLS_LPT; +iosta = iosta & ~(IOS_PNT | IOS_SPC); /* clear flags */ +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/PDP1/pdp1_stddev.c b/PDP1/pdp1_stddev.c new file mode 100644 index 0000000..4ddd114 --- /dev/null +++ b/PDP1/pdp1_stddev.c @@ -0,0 +1,614 @@ +/* pdp1_stddev.c: PDP-1 standard devices + + Copyright (c) 1993-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + tti keyboard + tto teleprinter + + 21-Dec-06 RMS Added 16-channel sequence break support + 29-Oct-03 RMS Added PTR FIODEC-to-ASCII translation (from Phil Budne) + 07-Sep-03 RMS Changed ioc to ios + 30-Aug-03 RMS Revised PTR to conform to Maintenance Manual; + added deadlock prevention on errors + 23-Jul-03 RMS Revised to detect I/O wait hang + 25-Apr-03 RMS Revised for extended file support + 22-Dec-02 RMS Added break support + 29-Nov-02 RMS Fixed output flag initialization (found by Derek Peschel) + 21-Nov-02 RMS Changed typewriter to half duplex (found by Derek Peschel) + 06-Oct-02 RMS Revised for V2.10 + 30-May-02 RMS Widened POS to 32b + 29-Nov-01 RMS Added read only unit support + 07-Sep-01 RMS Moved function prototypes + 10-Jun-01 RMS Fixed comment + 30-Oct-00 RMS Standardized device naming + + Note: PTP timeout must be >10X faster that TTY output timeout for Macro + to work correctly! +*/ + +#include "pdp1_defs.h" + +#define FIODEC_STOP 013 /* stop code */ +#define FIODEC_UC 074 +#define FIODEC_LC 072 +#define UC_V 6 /* upper case */ +#define UC (1 << UC_V) +#define BOTH (1 << (UC_V + 1)) /* both cases */ +#define CW (1 << (UC_V + 2)) /* char waiting */ +#define TT_WIDTH 077 +#define UNIT_V_ASCII (UNIT_V_UF + 0) /* ASCII/binary mode */ +#define UNIT_ASCII (1 << UNIT_V_ASCII) +#define PTR_LEADER 20 /* ASCII leader chars */ + +int32 ptr_state = 0; +int32 ptr_wait = 0; +int32 ptr_stopioe = 0; +int32 ptr_uc = 0; /* upper/lower case */ +int32 ptr_hold = 0; /* holding buffer */ +int32 ptr_leader = PTR_LEADER; /* leader count */ +int32 ptr_sbs = 0; /* SBS level */ +int32 ptp_stopioe = 0; +int32 ptp_sbs = 0; /* SBS level */ +int32 tti_hold = 0; /* tti hold buf */ +int32 tti_sbs = 0; /* SBS level */ +int32 tty_buf = 0; /* tty buffer */ +int32 tty_uc = 0; /* tty uc/lc */ +int32 tto_sbs = 0; + +extern int32 ios, ioh, cpls, iosta; +extern int32 PF, IO, PC, TA; +extern int32 M[]; + +int ptr_get_ascii (UNIT *uptr); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat tty_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); +t_stat ptr_attach (UNIT *uptr, char *cptr); + +/* Character translation tables */ + +int32 fiodec_to_ascii[128] = { + ' ', '1', '2', '3', '4', '5', '6', '7', /* lower case */ + '8', '9', 0, 0, 0, 0, 0, 0, + '0', '/', 's', 't', 'u', 'v', 'w', 'x', + 'y', 'z', 0, ',', 0, 0, '\t', 0, + '@', 'j', 'k', 'l', 'm', 'n', 'o', 'p', + 'q', 'r', 0, 0, '-', ')', '\\', '(', + 0, 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', '{', '.', '}', '\b', 0, '\r', + ' ', '"', '\'', '~', '#', '!', '&', '<', /* upper case */ + '>', '^', 0, 0, 0, 0, 0, 0, + '`', '?', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 0, '=', 0, 0, '\t', 0, + '_', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 0, 0, '+', ']', '|', '[', + 0, 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '{', '*', '}', '\b', 0, '\r' + }; + +int32 ascii_to_fiodec[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, + BOTH+075, BOTH+036, 0, 0, 0, BOTH+077, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + BOTH+0, UC+005, UC+001, UC+004, 0, 0, UC+006, UC+002, + 057, 055, UC+073, UC+054, 033, 054, 073, 021, + 020, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 0, 0, UC+007, UC+033, UC+010, UC+021, + 040, UC+061, UC+062, UC+063, UC+064, UC+065, UC+066, UC+067, + UC+070, UC+071, UC+041, UC+042, UC+043, UC+044, UC+045, UC+046, + UC+047, UC+050, UC+051, UC+022, UC+023, UC+024, UC+025, UC+026, + UC+027, UC+030, UC+031, UC+057, 056, UC+055, UC+011, UC+040, + UC+020, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 022, 023, 024, 025, 026, + 027, 030, 031, 0, UC+056, 0, UC+003, BOTH+075 + }; + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit + ptr_reg PTR register list +*/ + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 18) }, + { FLDATA (UC, ptr_uc, UC_V) }, + { FLDATA (DONE, iosta, IOS_V_PTR) }, + { FLDATA (RPLS, cpls, CPLS_V_PTR) }, + { ORDATA (HOLD, ptr_hold, 9), REG_HRO }, + { ORDATA (STATE, ptr_state, 5), REG_HRO }, + { FLDATA (WAIT, ptr_wait, 0), REG_HRO }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { DRDATA (LEADER, ptr_leader, 6), REG_HRO }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { DRDATA (SBSLVL, ptr_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB ptr_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &ptr_sbs }, + { UNIT_ASCII, UNIT_ASCII, "ASCII", "ASCII", NULL }, + { UNIT_ASCII, 0, "FIODEC", "FIODEC", NULL }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, &ptr_attach, NULL, + NULL, 0 + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit + ptp_reg PTP register list +*/ + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (DONE, iosta, IOS_V_PTP) }, + { FLDATA (RPLS, cpls, CPLS_V_PTP) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { DRDATA (SBSLVL, ptp_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB ptp_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &ptp_sbs }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit + tti_reg TTI register list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tty_buf, 6) }, + { FLDATA (UC, tty_uc, UC_V) }, + { ORDATA (HOLD, tti_hold, 9), REG_HRO }, + { FLDATA (DONE, iosta, IOS_V_TTI) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (SBSLVL, tti_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB tti_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &tti_sbs }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tty_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT * 10 }; + +REG tto_reg[] = { + { ORDATA (BUF, tty_buf, 6) }, + { FLDATA (UC, tty_uc, UC_V) }, + { FLDATA (RPLS, cpls, CPLS_V_TTO) }, + { FLDATA (DONE, iosta, IOS_V_TTO) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { DRDATA (SBSLVL, tto_sbs, 4), REG_HRO }, + { NULL } + }; + +MTAB tto_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL", + &dev_set_sbs, &dev_show_sbs, (void *) &tto_sbs }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tty_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* Paper tape reader: IOT routine. Points to note: + + - RPA (but not RPB) complements the reader clutch control. Thus, + if the reader is running, RPA will stop it. + - The status bit indicates data in the reader buffer that has not + been transfered to IO. It is cleared by any RB->IO operation, + including RRB and the completion pulse. + - A reader error on a wait mode operation could hang the simulator. + IOH is set; any retry (without RESET) will be NOP'd. Accordingly, + the PTR service routine clears IOH on any error during a rpa/rpb i. +*/ + +int32 ptr (int32 inst, int32 dev, int32 dat) +{ +if (dev == 0030) { /* RRB */ + iosta = iosta & ~IOS_PTR; /* clear status */ + return ptr_unit.buf; /* return data */ + } +if (dev == 0002) ptr_state = 18; /* RPB, mode = binary */ +else if (sim_is_active (&ptr_unit)) { /* RPA, running? */ + sim_cancel (&ptr_unit); /* stop reader */ + return dat; + } +else ptr_state = 0; /* mode = alpha */ +ptr_unit.buf = 0; /* clear buffer */ +if (inst & IO_WAIT) ptr_wait = 1; /* set ptr wait */ +else ptr_wait = 0; /* from IR<5> */ +if (GEN_CPLS (inst)) { /* comp pulse? */ + ios = 0; + cpls = cpls | CPLS_PTR; + } +else cpls = cpls & ~CPLS_PTR; +sim_activate (&ptr_unit, ptr_unit.wait); /* start reader */ +return dat; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + if (ptr_wait) ptr_wait = ioh = 0; /* if wait, clr ioh */ + if ((cpls & CPLS_PTR) || ptr_stopioe) return SCPE_UNATT; + return SCPE_OK; + } +if ((uptr->flags & UNIT_ASCII) && (ptr_state == 0)) /* ASCII mode, alpha read? */ + temp = ptr_get_ascii (uptr); /* get processed char */ +else if ((temp = getc (uptr->fileref)) != EOF) /* no, get raw char */ + uptr->pos = uptr->pos + 1; /* if not eof, count */ +if (temp == EOF) { /* end of file? */ + if (ptr_wait) ptr_wait = ioh = 0; /* if wait, clr ioh */ + if (feof (uptr->fileref)) { + if ((cpls & CPLS_PTR) || ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +if (ptr_state == 0) uptr->buf = temp & 0377; /* alpha */ +else if (temp & 0200) { /* binary */ + ptr_state = ptr_state - 6; + uptr->buf = uptr->buf | ((temp & 077) << ptr_state); + } +if (ptr_state == 0) { /* done? */ + if (cpls & CPLS_PTR) { /* completion pulse? */ + iosta = iosta & ~IOS_PTR; /* clear flag */ + IO = uptr->buf; /* fill IO */ + ios = 1; /* restart */ + cpls = cpls & ~CPLS_PTR; + } + else { /* no, interrupt */ + iosta = iosta | IOS_PTR; /* set flag */ + dev_req_int (ptr_sbs); /* req interrupt */ + } + } +else sim_activate (uptr, uptr->wait); /* get next char */ +return SCPE_OK; +} + +/* Read next ASCII character */ + +int ptr_get_ascii (UNIT *uptr) +{ +int c; +int32 in; + +if (ptr_leader > 0) { /* leader? */ + ptr_leader = ptr_leader - 1; /* count down */ + return 0; + } +if (ptr_hold & CW) { /* char waiting? */ + in = ptr_hold & TT_WIDTH; /* return char */ + ptr_hold = 0; /* not waiting */ + } +else { + for (;;) { /* until valid char */ + if ((c = getc (uptr->fileref)) == EOF) /* get next char, EOF? */ + return FIODEC_STOP; /* return STOP */ + uptr->pos = uptr->pos + 1; /* count char */ + c = c & 0177; /* cut to 7b */ + if (c == '\n') c = '\r'; /* NL -> CR */ + else if (c == '\r') continue; /* ignore CR */ + in = ascii_to_fiodec[c]; /* convert char */ + if ((in == 0) && (c != ' ')) continue; /* ignore unknowns */ + if ((in & BOTH) || ((in & UC) == ptr_uc)) /* case match? */ + in = in & TT_WIDTH; /* cut to 6b */ + else { /* no, case shift */ + ptr_hold = in | CW; /* set char waiting */ + ptr_uc = in & UC; /* set case */ + in = ptr_uc? FIODEC_UC: FIODEC_LC; /* return case */ + } /* end else */ + break; + } /* end for */ + } /* end else */ +in = in * 010040201; /* even parity from */ +in = in | 027555555400; /* HACKMEM 167 */ +in = in % (9 << 7); +return in & 0377; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_state = 0; /* clear state */ +ptr_wait = 0; +ptr_hold = 0; +ptr_uc = 0; +ptr_unit.buf = 0; +cpls = cpls & ~CPLS_PTR; +iosta = iosta & ~IOS_PTR; /* clear flag */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat ptr_attach (UNIT *uptr, char *cptr) +{ +ptr_leader = PTR_LEADER; /* set up leader */ +return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ + +int32 ptr_getw (UNIT *uptr) +{ +int32 i, tmp, word; + +for (i = word = 0; i < 3;) { + if ((tmp = getc (uptr->fileref)) == EOF) return -1; + uptr->pos = uptr->pos + 1; + if (tmp & 0200) { + word = (word << 6) | (tmp & 077); + i++; + } + } +return word; +} + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +int32 origin, val; +int32 fld = TA & EPCMASK; + +for (;;) { + if ((val = ptr_getw (&ptr_unit)) < 0) return SCPE_FMT; + if (((val & 0760000) == OP_DIO) || /* DIO? */ + ((val & 0760000) == OP_DAC)) { /* hack - Macro1 err */ + origin = val & DAMASK; + if ((val = ptr_getw (&ptr_unit)) < 0) return SCPE_FMT; + M[fld | origin] = val; + } + else if ((val & 0760000) == OP_JMP) { /* JMP? */ + PC = fld | (val & DAMASK); + break; + } + else return SCPE_FMT; /* bad instr */ + } +return SCPE_OK; /* done */ +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 inst, int32 dev, int32 dat) +{ +iosta = iosta & ~IOS_PTP; /* clear flag */ +ptp_unit.buf = (dev == 0006)? ((dat >> 12) | 0200): (dat & 0377); +if (GEN_CPLS (inst)) { /* comp pulse? */ + ios = 0; + cpls = cpls | CPLS_PTP; + } +else cpls = cpls & ~CPLS_PTP; +sim_activate (&ptp_unit, ptp_unit.wait); /* start unit */ +return dat; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +if (cpls & CPLS_PTP) { /* completion pulse? */ + ios = 1; /* restart */ + cpls = cpls & ~CPLS_PTP; + } +iosta = iosta | IOS_PTP; /* set flag */ +dev_req_int (ptp_sbs); /* req interrupt */ +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (uptr->buf, uptr->fileref) == EOF) { /* I/O error? */ + perror ("PTP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; /* clear state */ +cpls = cpls & ~CPLS_PTP; +iosta = iosta & ~IOS_PTP; /* clear flag */ +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Typewriter IOT routines */ + +int32 tti (int32 inst, int32 dev, int32 dat) +{ +iosta = iosta & ~IOS_TTI; /* clear flag */ +if (inst & (IO_WAIT | IO_CPLS)) /* wait or sync? */ + return (STOP_RSRV << IOT_V_REASON) | (tty_buf & 077); +return tty_buf & 077; +} + +int32 tto (int32 inst, int32 dev, int32 dat) +{ +iosta = iosta & ~IOS_TTO; /* clear flag */ +tty_buf = dat & TT_WIDTH; /* load buffer */ +if (GEN_CPLS (inst)) { /* comp pulse? */ + ios = 0; + cpls = cpls | CPLS_TTO; + } +else cpls = cpls & ~CPLS_TTO; +sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ +return dat; +} + +/* Unit service routines */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 in, temp; + +sim_activate (uptr, uptr->wait); /* continue poll */ +if (tti_hold & CW) { /* char waiting? */ + tty_buf = tti_hold & TT_WIDTH; /* return char */ + tti_hold = 0; /* not waiting */ + } +else { + if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; + if (temp & SCPE_BREAK) return SCPE_OK; /* ignore break */ + temp = temp & 0177; + if (temp == 0177) temp = '\b'; /* rubout? bs */ + sim_putchar (temp); /* echo */ + if (temp == '\r') sim_putchar ('\n'); /* cr? add nl */ + in = ascii_to_fiodec[temp]; /* translate char */ + if (in == 0) return SCPE_OK; /* no xlation? */ + if ((in & BOTH) || ((in & UC) == (tty_uc & UC))) + tty_buf = in & TT_WIDTH; + else { /* must shift */ + tty_uc = in & UC; /* new case */ + tty_buf = tty_uc? FIODEC_UC: FIODEC_LC; + tti_hold = in | CW; /* set 2nd waiting */ + } + } +iosta = iosta | IOS_TTI; /* set flag */ +dev_req_int (tti_sbs); /* req interrupt */ +PF = PF | PF_SS_1; /* set prog flag 1 */ +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +if (tty_buf == FIODEC_UC) tty_uc = UC; /* upper case? */ +else if (tty_buf == FIODEC_LC) tty_uc = 0; /* lower case? */ +else { + c = fiodec_to_ascii[tty_buf | tty_uc]; /* translate */ + if (c && ((r = sim_putchar_s (c)) != SCPE_OK)) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* retry */ + return ((r == SCPE_STALL)? SCPE_OK: r); + } + } +if (cpls & CPLS_TTO) { /* completion pulse? */ + ios = 1; /* restart */ + cpls = cpls & ~CPLS_TTO; + } +iosta = iosta | IOS_TTO; /* set flag */ +dev_req_int (tto_sbs); /* req interrupt */ +uptr->pos = uptr->pos + 1; +if (c == '\r') { /* cr? add lf */ + sim_putchar ('\n'); + uptr->pos = uptr->pos + 1; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tty_reset (DEVICE *dptr) +{ +tty_buf = 0; /* clear buffer */ +tty_uc = 0; /* clear case */ +tti_hold = 0; /* clear hold buf */ +cpls = cpls & ~CPLS_TTO; +iosta = (iosta & ~IOS_TTI) | IOS_TTO; /* clear flag */ +sim_activate (&tti_unit, tti_unit.wait); /* activate keyboard */ +sim_cancel (&tto_unit); /* stop printer */ +return SCPE_OK; +} diff --git a/PDP1/pdp1_sys.c b/PDP1/pdp1_sys.c new file mode 100644 index 0000000..fd3470c --- /dev/null +++ b/PDP1/pdp1_sys.c @@ -0,0 +1,629 @@ +/* pdp1_sys.c: PDP-1 simulator interface + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 03-Jan-07 RMS Fixed bugs in block loader, char input + 21-Dec-06 RMS Added 16-channel sequence break support, PDP-1D support + 06-Apr-04 RMS Fixed bug in binary loader (found by Mark Crispin) + 08-Feb-04 PLB Merged display support + 08-Dec-03 RMS Added parallel drum support, drum mnemonics + 18-Oct-03 RMS Added DECtape off reel message + 01-Sep-03 RMS Added support for loading in multiple fields + 22-Jul-03 RMS Updated for "hardware" RIM loader + 05-Dec-02 RMS Added drum support + 21-Nov-02 RMS Changed typewriter to half duplex + 20-Aug-02 RMS Added DECtape support + 17-Sep-01 RMS Removed multiconsole support + 13-Jul-01 RMS Fixed RIM loader format + 27-May-01 RMS Added multiconsole support + 14-Mar-01 RMS Revised load/dump interface (again) + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface + 20-Oct-97 RMS Fixed endian-dependence in RIM loader + (found by Michael Somos) +*/ + +#include "pdp1_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE clk_dev; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE tti_dev; +extern DEVICE tto_dev; +extern DEVICE lpt_dev; +extern DEVICE dt_dev; +extern DEVICE drm_dev; +extern DEVICE drp_dev; +extern DEVICE dcs_dev, dcsl_dev; +extern DEVICE dpy_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern int32 M[]; +extern int32 PC; +extern int32 ascii_to_fiodec[], fiodec_to_ascii[]; +extern int32 sc_map[]; +extern int32 sim_switches; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-1"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &clk_dev, + &ptr_dev, + &ptp_dev, + &tti_dev, + &tto_dev, + &lpt_dev, + &dt_dev, + &drm_dev, + &drp_dev, + &dcs_dev, + &dcsl_dev, +/* &dpy_dev, */ + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Undefined instruction", + "HALT instruction", + "Breakpoint", + "Nested XCT's", + "Nested indirect addresses", + "Infinite I/O wait state", + "DECtape off reel" + }; + +/* Binary loader - supports both RIM format and Macro block format */ + +int32 pdp1_getw (FILE *inf) +{ +int32 i, tmp, word; + +word = 0; +for (i = 0; i < 3;) { + if ((tmp = getc (inf)) == EOF) return -1; + if (tmp & 0200) { + word = (word << 6) | (tmp & 077); + i++; + } + } +return word; +} + +t_stat rim_load (FILE *inf, int32 fld) +{ +int32 origin, val; + +for (;;) { + if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; + if (((val & 0760000) == OP_DIO) || /* DIO? */ + ((val & 0760000) == OP_DAC)) { /* hack - Macro1 err */ + origin = val & DAMASK; + if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; + M[fld | origin] = val; + } + else if ((val & 0760000) == OP_JMP) { /* JMP? */ + PC = fld | (val & DAMASK); + break; + } + else return SCPE_FMT; /* bad instr */ + } +return SCPE_OK; /* done */ +} + +t_stat blk_load (FILE *inf, int32 fld) +{ +int32 val, start, count, csum; + +for (;;) { + if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; /* get word, EOF? */ + if ((val & 0760000) == OP_DIO) { /* DIO? */ + csum = val; /* init checksum */ + start = val & DAMASK; /* starting addr */ + if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; + if ((val & 0760000) != OP_DIO) return SCPE_FMT; + csum = csum + val; + if (csum > DMASK) csum = (csum + 1) & DMASK; + count = (val & DAMASK) - start; /* block count */ + if (count <= 0) return SCPE_FMT; + while (count--) { /* loop on data */ + if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; + csum = csum + val; + if (csum > DMASK) csum = (csum + 1) & DMASK; + M[fld | start] = val; + start = (start + 1) & DAMASK; + } + if ((val = pdp1_getw (inf)) < 0) return SCPE_FMT; + if (val != csum) return SCPE_CSUM; + } + else if ((val & 0760000) == OP_JMP) { /* JMP? */ + PC = fld | (val & DAMASK); + break; + } + else return SCPE_FMT; /* bad instr */ + } +return SCPE_OK; /* done */ +} + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +t_stat sta; +int32 fld; + +if (flag != 0) return SCPE_ARG; +if (cptr && (*cptr != 0)) { + fld = get_uint (cptr, 8, AMASK, &sta); + if (sta != SCPE_OK) return sta; + fld = fld & EPCMASK; + } +else fld = 0; +sta = rim_load (fileref, fld); +if (sta != SCPE_OK) return sta; +if ((sim_switches & SWMASK ('B')) || match_ext (fnam, "BIN")) + return blk_load (fileref, fld); +return SCPE_OK; +} + +/* Symbol tables */ + +#define I_V_FL 18 /* inst class */ +#define I_M_FL 017 /* class mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_IOT 1 /* IOT */ +#define I_V_LAW 2 /* LAW */ +#define I_V_MRF 3 /* memory reference */ +#define I_V_MRI 4 /* mem ref no ind */ +#define I_V_OPR 5 /* OPR */ +#define I_V_SKP 6 /* skip */ +#define I_V_SHF 7 /* shift */ +#define I_V_SPC 8 /* special */ +#define I_NPN (I_V_NPN << I_V_FL) /* no operand */ +#define I_IOT (I_V_IOT << I_V_FL) /* IOT */ +#define I_LAW (I_V_LAW << I_V_FL) /* LAW */ +#define I_MRF (I_V_MRF << I_V_FL) /* memory reference */ +#define I_MRI (I_V_MRI << I_V_FL) /* mem ref no ind */ +#define I_OPR (I_V_OPR << I_V_FL) /* OPR */ +#define I_SKP (I_V_SKP << I_V_FL) /* skip */ +#define I_SHF (I_V_SHF << I_V_FL) /* shift */ +#define I_SPC (I_V_SPC << I_V_FL) + +static const int32 masks[] = { + 0777777, 0760077, 0760000, 0760000, + 0770000, 0760017, 0760077, 0777000, + 0760003 + }; + +static const char *opcode[] = { + "AND", "IOR", "XOR", "XCT", /* mem refs */ + "LAC", "LIO", "DAC", "DAP", + "DIP", "DIO", "DZM", "ADD", + "SUB", "IDX", "ISP", "SAD", + "SAS", "MUL", "DIV", "JMP", + "JSP", "LCH", "DCH", "TAD", + + "CAL", "JDA", /* mem ref no ind */ + + "LAW", + + "IOH", "RPA", "RPB", "RRB", /* I/O instructions */ + "PPA", "PPB", "TYO", "TYI", + "DPY", + "DSC", "ASC", "ISC", "CAC", + "LSM", "ESM", "CBS", + "LEM", "EEM", "CKS", + "MSE", "MLC", "MRD", "MWR", "MRS", + "DIA", "DBA", "DWC", "DRA", "DCL", + "DRD", "DWR", "DBL", "DCN", + "DTD", "DSE", "DSP", + "LRG", "ERG", "LRM", "ERM", + "RNM", "RSM", "RCK", "CTB", + "RCH", "RCC", "TCC", "TCB", + "RRC", "SSB", "RSC", + + "SKP", "SKP I", "CLO", /* base as NPNs */ + "SFT", "SPC", "OPR", + + "RAL", "RIL", "RCL", /* shifts */ + "SAL", "SIL", "SCL", + "RAR", "RIR", "RCR", + "SAR", "SIR", "SCR", + + "SZF1", "SZF2", "SZF3", /* skips */ + "SZF4", "SZF5", "SZF6", "SZF7", + "SZS1", "SZS1 SZF1", "SZS1 SZF2", "SZS1 SZ3", + "SZS1 SZF4", "SZS1 SZF5", "SZS1 SZF6", "SZS1 SZF7", + "SZS2", "SZS2 SZF1", "SZS2 SZF2", "SZS2 SZ3", + "SZS2 SZF4", "SZS2 SZF5", "SZS2 SZF6", "SZS2 SZF7", + "SZS3", "SZS3 SZF1", "SZS3 SZF2", "SZS3 SZ3", + "SZS3 SZF4", "SZS3 SZF5", "SZS3 SZF6", "SZS3 SZF7", + "SZS4", "SZS4 SZF1", "SZS4 SZF2", "SZS4 SZ3", + "SZS4 SZF4", "SZS4 SZF5", "SZS4 SZF6", "SZS4 SZF7", + "SZS5", "SZS5 SZF1", "SZS5 SZF2", "SZS5 SZ3", + "SZS5 SZF4", "SZS5 SZF5", "SZS5 SZF6", "SZS5 SZF7", + "SZS6", "SZS6 SZF1", "SZS6 SZF2", "SZS6 SZ3", + "SZS6 SZF4", "SZS6 SZF5", "SZS6 SZF6", "SZS6 SZF7", + "SZS7", "SZS7 SZF1", "SZS7 SZF2", "SZS7 SZ3", + "SZS7 SZF4", "SZS7 SZF5", "SZS7 SZF6", "SZS7 SZF7", + + "CLF1", "CLF2", "CLF3", /* operates */ + "CLF4", "CLF5", "CLF6", "CLF7", + "STF1", "STF2", "STF3", + "STF4", "STF5", "STF6", "STF7", + + "FF1", "FF2", "FF3", /* specials */ + + "SZA", "SPA", "SMA", /* uprog skips */ + "SZO", "SPI", "SNI", + "I", /* encode only */ + + "LIA", "LAI", "SWP", /* uprog opers */ + "LAP", "CLA", "HLT", + "CMA", "LAT", "CLI", + "CMI", + + "CML", "CLL", "SZL", /* uprog specials */ + "SCF", "SCI", "SCM", + "IDA", "IDC", "IFI", + "IIF", + + NULL, NULL, NULL, /* decode only */ + NULL, + }; + +static const int32 opc_val[] = { + 0020000+I_MRF, 0040000+I_MRF, 0060000+I_MRF, 0100000+I_MRF, + 0200000+I_MRF, 0220000+I_MRF, 0240000+I_MRF, 0260000+I_MRF, + 0300000+I_MRF, 0320000+I_MRF, 0340000+I_MRF, 0400000+I_MRF, + 0420000+I_MRF, 0440000+I_MRF, 0460000+I_MRF, 0500000+I_MRF, + 0520000+I_MRF, 0540000+I_MRF, 0560000+I_MRF, 0600000+I_MRF, + 0620000+I_MRF, 0120000+I_MRF, 0140000+I_MRF, 0360000+I_MRF, + + 0160000+I_MRI, 0170000+I_MRI, + + 0700000+I_LAW, + + 0730000+I_NPN, 0720001+I_IOT, 0720002+I_IOT, 0720030+I_IOT, + 0720005+I_IOT, 0720006+I_IOT, 0720003+I_IOT, 0720004+I_IOT, + 0720007+I_IOT, + 0720050+I_IOT, 0720051+I_IOT, 0720052+I_IOT, 0720053+I_NPN, + 0720054+I_NPN, 0720055+I_NPN, 0720056+I_NPN, + 0720074+I_NPN, 0724074+I_NPN, 0720033+I_NPN, + 0720301+I_NPN, 0720401+I_NPN, 0720501+I_NPN, 0720601+I_NPN, 0720701+I_NPN, + 0720061+I_NPN, 0722061+I_NPN, 0720062+I_NPN, 0722062+I_NPN, 0720063+I_NPN, + 0720161+I_NPN, 0721161+I_NPN, 0720162+I_NPN, 0721162+I_NPN, + 0720163+I_NPN, 0720164+I_NPN, 0721164+I_NPN, + 0720010+I_NPN, 0720011+I_NPN, 0720064+I_NPN, 0720065+I_NPN, + 0720066+I_IOT, 0720067+I_NPN, 0720032+I_NPN, 0720035+I_NPN, + 0720022+I_NPN, 0721022+I_NPN, 0725022+I_NPN, 0724022+I_NPN, + 0720122+I_NPN, 0724122+I_NPN, 0721122+I_NPN, + + 0640000+I_NPN, 0650000+I_NPN, 0651600+I_NPN, + 0660000+I_NPN, 0740000+I_NPN, 0760000+I_NPN, + + 0661000+I_SHF, 0662000+I_SHF, 0663000+I_SHF, + 0665000+I_SHF, 0666000+I_SHF, 0667000+I_SHF, + 0671000+I_SHF, 0672000+I_SHF, 0673000+I_SHF, + 0675000+I_SHF, 0676000+I_SHF, 0677000+I_SHF, + + 0640001+I_SKP, 0640002+I_SKP, 0640003+I_SKP, + 0640004+I_SKP, 0640005+I_SKP, 0640006+I_SKP, 0640007+I_SKP, + 0640010+I_SKP, 0640011+I_SKP, 0640012+I_SKP, 0640013+I_SKP, + 0640014+I_SKP, 0640015+I_SKP, 0640016+I_SKP, 0640017+I_SKP, + 0640020+I_SKP, 0640021+I_SKP, 0640022+I_SKP, 0640023+I_SKP, + 0640024+I_SKP, 0640025+I_SKP, 0640026+I_SKP, 0640027+I_SKP, + 0640030+I_SKP, 0640031+I_SKP, 0640032+I_SKP, 0640033+I_SKP, + 0640034+I_SKP, 0640035+I_SKP, 0640036+I_SKP, 0640037+I_SKP, + 0640040+I_SKP, 0640041+I_SKP, 0640042+I_SKP, 0640043+I_SKP, + 0640044+I_SKP, 0640045+I_SKP, 0640046+I_SKP, 0640047+I_SKP, + 0640050+I_SKP, 0640051+I_SKP, 0640052+I_SKP, 0640053+I_SKP, + 0640054+I_SKP, 0640055+I_SKP, 0640056+I_SKP, 0640057+I_SKP, + 0640060+I_SKP, 0640061+I_SKP, 0640062+I_SKP, 0640063+I_SKP, + 0640064+I_SKP, 0640065+I_SKP, 0640066+I_SKP, 0640067+I_SKP, + 0640070+I_SKP, 0640071+I_SKP, 0640072+I_SKP, 0640073+I_SKP, + 0640074+I_SKP, 0640075+I_SKP, 0640076+I_SKP, 0640077+I_SKP, + + 0760001+I_OPR, 0760002+I_OPR, 0760003+I_OPR, + 0760004+I_OPR, 0760005+I_OPR, 0760006+I_OPR, 0760007+I_OPR, + 0760011+I_OPR, 0760012+I_OPR, 0760013+I_OPR, + 0760014+I_OPR, 0760015+I_OPR, 0760016+I_OPR, 0760017+I_OPR, + + 0740001+I_SPC, 0740002+I_SPC, 0740003+I_OPR, + + 0640100+I_SKP, 0640200+I_SKP, 0640400+I_SKP, + 0641000+I_SKP, 0642000+I_SKP, 0644000+I_SKP, + 0010000+I_SKP, /* encode only */ + + 0760020+I_OPR, 0760040+I_OPR, 0760060+I_NPN, + 0760100+I_OPR, 0760200+I_OPR, 0760400+I_OPR, + 0761000+I_OPR, 0762000+I_OPR, 0764000+I_OPR, + 0770000+I_OPR, + + 0740004+I_SPC, 0740010+I_SPC, 0740020+I_SPC, + 0740040+I_SPC, 0740100+I_SPC, 0740200+I_SPC, + 0740400+I_SPC, 0741000+I_SPC, 0742000+I_SPC, + 0744000+I_SPC, + + 0640000+I_SKP, 0740000+I_SPC, 0760000+I_OPR, /* decode only */ + -1 + }; + +/* Operate or skip decode + + Inputs: + *of = output stream + inst = mask bits + class = instruction class code + sp = space needed? + Outputs: + status = space needed? +*/ + +int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) +{ +int32 i, j; + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; + } + } +return sp; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) fiodec_to_ascii[x] +#define ASCTOSIX(x) (ascii_to_fiodec[x] & 077) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, sp, inst, disp, ma; + +inst = val[0]; +cflag = (uptr == NULL) || (uptr == &cpu_unit); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('F')) { + fputc (fiodec_to_ascii[inst & 077], of); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, "%c", SIXTOASC ((inst >> 12) & 077)); + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +disp = inst & 007777; +ma = (addr & EPCMASK) | disp; +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + + case I_V_NPN: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_IOT: /* IOT */ + disp = (inst - opc_val[i]) & 017777; + if (disp == IA) fprintf (of, "%s I", opcode[i]); + else if (disp) fprintf (of, "%s %-o", opcode[i], disp); + else fprintf (of, "%s", opcode[i]); + break; + + case I_V_LAW: /* LAW */ + cflag = 0; /* fall thru to MRF */ + case I_V_MRF: /* mem ref */ + fprintf (of, "%s%s%-o", opcode[i], + ((inst & IA)? " I ": " "), (cflag? ma: disp)); + break; + + case I_V_MRI: /* mem ref no ind */ + fprintf (of, "%s %-o", opcode[i], (cflag? ma: disp)); + break; + + case I_V_OPR: /* operates */ + sp = fprint_opr (of, inst & 017760, j, 0); + if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; + + case I_V_SKP: /* skips */ + sp = fprint_opr (of, inst & 007700, j, 0); + if (opcode[i]) sp = fprintf (of, (sp? " %s": "%s"), opcode[i]); + if (inst & IA) fprintf (of, sp? " I": "I"); + break; + + case I_V_SPC: /* specials */ + sp = fprint_opr (of, inst & 007774, j, 0); + if (opcode[i]) sp = fprintf (of, (sp? " %s": "%s"), opcode[i]); + if (inst & IA) fprintf (of, sp? " I": "I"); + break; + + case I_V_SHF: /* shifts */ + fprintf (of, "%s %-d", opcode[i], sc_map[inst & 0777]); + break; + } /* end case */ + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get 18b signed number + + Inputs: + *cptr = pointer to input string + *sign = pointer to sign + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_sint (char *cptr, int32 *sign, t_stat *status) +{ +*sign = 1; +if (*cptr == '+') { + *sign = 0; + cptr++; + } +else if (*cptr == '-') { + *sign = -1; + cptr++; + } +return get_uint (cptr, 8, DMASK, status); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k, sign; +t_stat r; +static int32 sc_enc[10] = { 0, 01, 03, 07, 017, 037, 077, 0177, 0377, 0777 }; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; +for (i = 1; (i < 3) && (cptr[i] != 0); i++) + if (cptr[i] == 0) for (j = i + 1; j <= 3; j++) cptr[j] = 0; +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((ASCTOSIX (cptr[0]) & 077) << 12) | + ((ASCTOSIX (cptr[1]) & 077) << 6) | + (ASCTOSIX (cptr[2]) & 077); + return SCPE_OK; + } + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & DMASK; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_LAW: /* LAW */ + cflag = 0; /* fall through */ + case I_V_MRF: case I_V_MRI: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if ((j != I_V_MRI) && strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | IA; + cptr = get_glyph (cptr, gbuf, 0); + } + d = get_uint (gbuf, 8, AMASK, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (d <= DAMASK) val[0] = val[0] | d; + else if (cflag && (((addr ^ d) & EPCMASK) == 0)) + val[0] = val[0] | (d & DAMASK); + else return SCPE_ARG; + break; + + case I_V_SHF: /* shift */ + cptr = get_glyph (cptr, gbuf, 0); + d = get_uint (gbuf, 10, 9, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | sc_enc[d]; + break; + + case I_V_NPN: case I_V_IOT: + case I_V_OPR: case I_V_SKP: case I_V_SPC: + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0); i++) ; + if (opcode[i] != NULL) { + k = opc_val[i] & DMASK; + if ((k != IA) && (((k ^ val[0]) & 0760000) != 0)) + return SCPE_ARG; + val[0] = val[0] | k; + } + else { + d = get_sint (gbuf, &sign, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (sign == 0) val[0] = val[0] + d; + else if (sign < 0) val[0] = val[0] - d; + else val[0] = val[0] | d; + } + } + break; + } /* end case */ +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/PDP10/pdp10_bug_history.txt b/PDP10/pdp10_bug_history.txt new file mode 100644 index 0000000..b4a3114 --- /dev/null +++ b/PDP10/pdp10_bug_history.txt @@ -0,0 +1,25 @@ +Bugs Found and Fixed During Simulator Debug + +1. pushj cleared T2 after setting it +2. if timer autoadjust is enabled, timer diagnostic may fail, + depending on host CPU speed +3. DFAD/DFSB should use FP_ONES instead of ONES +4. TLB physical address max = 1MW, tested in diagnostic +5. DPB does read/write, not read-modify/write +6. Fetch error takes priority over traps, due to prefetching + of next instruction +7. HSB is 36b, was 32b +8. CPU and PAG devices had mismatched types +9. non-zero sections in Tops-20 paging section indirect may + cause non-existent memory error, due to microcode "error" +10. PXCT test for user mode was backward +11. Timer interrupts were not implemented in Tops-20 indirect + chains +12. epta/upta hit known bug in VC++ implementation of 64b + data types +13. final W calculation in Tops-20 paging was incorrect +14. Timer representation lost sub msec values +15. UBA initialization reset the UBA itself +16. RHCS1: writing IE cannot trigger an interrupt +17. Tape bootstrap was set to 800bpi instead of 1600bpi +18. FIXR off by 1 in testing for lower limit to process diff --git a/PDP10/pdp10_cpu.c b/PDP10/pdp10_cpu.c new file mode 100644 index 0000000..d45516e --- /dev/null +++ b/PDP10/pdp10_cpu.c @@ -0,0 +1,2332 @@ +/* pdp10_cpu.c: PDP-10 CPU simulator + + Copyright (c) 1993-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu KS10 central processor + + 17-Jul-07 RMS Fixed non-portable usage in SHOW HISTORY + 28-Apr-07 RMS Removed clock initialization + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + Fixed warning in MOVNI + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 10-Nov-04 RMS Added instruction history + 08-Oct-02 RMS Revised to build dib_tab dynamically + Added SHOW IOSPACE + 30-Dec-01 RMS Added old PC queue + 25-Dec-01 RMS Cleaned up sim_inst declarations + 07-Dec-01 RMS Revised to use new breakpoint package + 21-Nov-01 RMS Implemented ITS 1-proceed hack + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 10-Aug-01 RMS Removed register in declarations + 17-Jul-01 RMS Moved function prototype + 19-May-01 RMS Added workaround for TOPS-20 V4.1 boot bug + 29-Apr-01 RMS Fixed modifier naming conflict + Fixed XCTR/XCTRI, UMOVE/UMOVEM, BLTUB/BLTBU for ITS + Added CLRCSH for ITS + + The 36b system family had six different implementions: PDP-6, KA10, KI10, + L10, KL10 extended, and KS10. This simulator implements the KS10. + + The register state for the KS10 is: + + AC[8][16] accumulators + PC program counter + flags<0:11> state flags + pi_enb<1:7> enabled PI levels + pi_act<1:7> active PI levels + pi_prq<1:7> program PI requests + apr_enb<0:7> enabled system flags + apr_flg<0:7> system flags + ebr executive base register + ubr user base register + hsb halt status block address + spt SPT base + cst CST base + pur process use register + cstm CST mask + + The PDP-10 had just two instruction formats: memory reference + and I/O. + + 000000000 0111 1 1111 112222222222333333 + 012345678 9012 3 4567 890123456789012345 + +---------+----+-+----+------------------+ + | opcode | ac |i| idx| address | memory reference + +---------+----+-+----+------------------+ + + 000 0000000 111 1 1111 112222222222333333 + 012 3456789 012 3 4567 890123456789012345 + +---+-------+---+-+----+------------------+ + |111|device |iop|i| idx| address | I/O + +---+-------+---+-+----+------------------+ + + This routine is the instruction decode routine for the PDP-10. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until an abort occurs. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + MUUO instruction in executive mode + pager error in interrupt sequence + invalid vector table in interrupt sequence + illegal instruction in interrupt sequence + breakpoint encountered + nested indirects exceeding limit + nested XCT's exceeding limit + I/O error in I/O simulator + + 2. Interrupts. PDP-10's have a seven level priority interrupt + system. Interrupt requests can come from internal sources, + such as APR program requests, or external sources, such as + I/O devices. The requests are stored in pi_prq for program + requests, pi_apr for other internal flags, and pi_ioq for + I/O device flags. Internal and device (but not program) + interrupts must be enabled on a level by level basis. When + an interrupt is granted on a level, interrupts at that level + and below are masked until the interrupt is dismissed. + + The I/O device interrupt system is taken from the PDP-11. + int_req stores the interrupt requests for Unibus I/O devices. + Routines in the Unibus adapter map requests in int_req to + PDP-10 levels. The Unibus adapter also calculates which + device to get a vector from when a PDP-10 interrupt is granted. + + 3. Arithmetic. The PDP-10 is a 2's complement system. + + 4. Adding I/O devices. These modules must be modified: + + pdp10_defs.h add device address and interrupt definitions + pdp10_sys.c add sim_devices table entry + + A note on ITS 1-proceed. The simulator follows the implementation + on the KS10, keeping 1-proceed as a side flag (its_1pr) rather than + as flags<8>. This simplifies the flag saving instructions, which + don't have to clear flags<8> before saving it. Instead, the page + fail and interrupt code must restore flags<8> from its_1pr. Unlike + the KS10, the simulator will not lose the 1-proceed trap if the + 1-proceeded instructions clears 1-proceed. +*/ + +#include "pdp10_defs.h" +#include + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC + +#define HIST_PC 0x40000000 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + a10 pc; + a10 ea; + d10 ir; + d10 ac; + } InstHistory; + +d10 *M = NULL; /* memory */ +d10 acs[AC_NBLK * AC_NUM] = { 0 }; /* AC blocks */ +d10 *ac_cur, *ac_prv; /* AC cur, prv (dyn) */ +a10 epta, upta; /* proc tbl addr (dyn) */ +a10 saved_PC = 0; /* scp: saved PC */ +d10 pager_word = 0; /* pager: error word */ +a10 pager_PC = 0; /* pager: saved PC */ +int32 pager_flags = 0; /* pager: trap flags */ +t_bool pager_pi = FALSE; /* pager: in pi seq */ +t_bool pager_tc = FALSE; /* pager: trap cycle */ +d10 ebr = 0; /* exec base reg */ +d10 ubr = 0; /* user base reg */ +d10 hsb = 0; /* halt status block */ +d10 spt = 0; /* TOPS20 paging regs */ +d10 cst = 0; +d10 pur = 0; +d10 cstm = 0; +a10 dbr1 = 0; /* ITS paging regs */ +a10 dbr2 = 0; +a10 dbr3 = 0; +a10 dbr4 = 0; +d10 pcst = 0; /* ITS PC sampling */ +int32 pi_on = 0; /* pi system enable */ +int32 pi_enb = 0; /* pi enabled levels */ +int32 pi_act = 0; /* pi active levels */ +int32 pi_ioq = 0; /* pi io requests */ +int32 pi_apr = 0; /* pi apr requests */ +int32 pi_prq = 0; /* pi prog requests */ +int32 apr_enb = 0; /* apr enables */ +int32 apr_flg = 0; /* apr flags */ +int32 apr_lvl = 0; /* apr level */ +int32 qintr = 0; /* interrupt pending */ +int32 flags = 0; /* flags */ +int32 its_1pr = 0; /* ITS 1-proceed */ +int32 stop_op0 = 0; /* stop on 0 */ +int32 rlog = 0; /* extend fixup log */ +int32 ind_max = 32; /* nested ind limit */ +int32 xct_max = 32; /* nested XCT limit */ +int32 t20_idlelock = 0; /* TOPS-20 idle lock */ +a10 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +jmp_buf save_env; +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ + +extern int32 sim_int_char; +extern int32 sim_interval; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +/* Forward and external declarations */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +d10 adjsp (d10 val, a10 ea); +void ibp (a10 ea, int32 pflgs); +d10 ldb (a10 ea, int32 pflgs); +void dpb (d10 val, a10 ea, int32 pflgs); +void adjbp (int32 ac, a10 ea, int32 pflgs); +d10 add (d10 val, d10 mb); +d10 sub (d10 val, d10 mb); +void dadd (int32 ac, d10 *rs); +void dsub (int32 ac, d10 *rs); +int32 jffo (d10 val); +d10 lsh (d10 val, a10 ea); +d10 rot (d10 val, a10 ea); +d10 ash (d10 val, a10 ea); +void lshc (int32 ac, a10 ea); +void rotc (int32 ac, a10 ea); +void ashc (int32 ac, a10 ea); +void circ (int32 ac, a10 ea); +void blt (int32 ac, a10 ea, int32 pflgs); +void bltu (int32 ac, a10 ea, int32 pflgs, int dir); +a10 calc_ea (d10 inst, int32 prv); +a10 calc_ioea (d10 inst, int32 prv); +d10 calc_jrstfea (d10 inst, int32 pflgs); +void pi_dismiss (void); +void set_newflags (d10 fl, t_bool jrst); +extern t_bool aprid (a10 ea, int32 prv); +t_bool wrpi (a10 ea, int32 prv); +t_bool rdpi (a10 ea, int32 prv); +t_bool czpi (a10 ea, int32 prv); +t_bool copi (a10 ea, int32 prv); +t_bool wrapr (a10 ea, int32 prv); +t_bool rdapr (a10 ea, int32 prv); +t_bool czapr (a10 ea, int32 prv); +t_bool coapr (a10 ea, int32 prv); +int32 pi_eval (void); +int32 test_int (void); +void set_ac_display (d10 *acbase); + +extern t_stat build_dib_tab (void); +extern t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); +extern d10 Read (a10 ea, int32 prv); /* read, read check */ +extern d10 ReadM (a10 ea, int32 prv); /* read, write check */ +extern d10 ReadE (a10 ea); /* read, exec */ +extern d10 ReadP (a10 ea); /* read, physical */ +extern void Write (a10 ea, d10 val, int32 prv); /* write */ +extern void WriteE (a10 ea, d10 val); /* write, exec */ +extern void WriteP (a10 ea, d10 val); /* write, physical */ +extern t_bool AccViol (a10 ea, int32 prv, int32 mode); /* access check */ +extern void set_dyn_ptrs (void); +extern a10 conmap (a10 ea, int32 mode, int32 sw); +extern void fe_intr (); +extern void dfad (int32 ac, d10 *rs, int32 inv); +extern void dfmp (int32 ac, d10 *rs); +extern void dfdv (int32 ac, d10 *rs); +extern void dmul (int32 ac, d10 *rs); +extern void ddiv (int32 ac, d10 *rs); +extern void fix (int32 ac, d10 mb, t_bool rnd); +extern d10 fad (d10 val, d10 mb, t_bool rnd, int32 inv); +extern d10 fmp (d10 val, d10 mb, t_bool rnd); +extern t_bool fdv (d10 val, d10 mb, d10 *rs, t_bool rnd); +extern d10 fsc (d10 val, a10 ea); +extern d10 fltr (d10 mb); +extern int xtend (int32 ac, a10 ea, int32 pflgs); +extern void xtcln (int32 rlog); +extern d10 map (a10 ea, int32 prv); +extern d10 imul (d10 val, d10 mb); +extern t_bool idiv (d10 val, d10 mb, d10 *rs); +extern void mul (d10 val, d10 mb, d10 *rs); +extern t_bool divi (int32 ac, d10 mb, d10 *rs); +extern t_bool io710 (int32 ac, a10 ea); +extern t_bool io711 (int32 ac, a10 ea); +extern d10 io712 (a10 ea); +extern void io713 (d10 val, a10 ea); +extern void io714 (d10 val, a10 ea); +extern void io715 (d10 val, a10 ea); +extern t_bool io720 (int32 ac, a10 ea); +extern t_bool io721 (int32 ac, a10 ea); +extern d10 io722 (a10 ea); +extern void io723 (d10 val, a10 ea); +extern void io724 (d10 val, a10 ea); +extern void io725 (d10 val, a10 ea); +extern t_bool clrcsh (a10 ea, int32 prv); +extern t_bool clrpt (a10 ea, int32 prv); +extern t_bool wrubr (a10 ea, int32 prv); +extern t_bool wrebr (a10 ea, int32 prv); +extern t_bool wrhsb (a10 ea, int32 prv); +extern t_bool wrspb (a10 ea, int32 prv); +extern t_bool wrcsb (a10 ea, int32 prv); +extern t_bool wrpur (a10 ea, int32 prv); +extern t_bool wrcstm (a10 ea, int32 prv); +extern t_bool ldbr1 (a10 ea, int32 prv); +extern t_bool ldbr2 (a10 ea, int32 prv); +extern t_bool ldbr3 (a10 ea, int32 prv); +extern t_bool ldbr4 (a10 ea, int32 prv); +extern t_bool rdubr (a10 ea, int32 prv); +extern t_bool rdebr (a10 ea, int32 prv); +extern t_bool rdhsb (a10 ea, int32 prv); +extern t_bool rdspb (a10 ea, int32 prv); +extern t_bool rdcsb (a10 ea, int32 prv); +extern t_bool rdpur (a10 ea, int32 prv); +extern t_bool rdcstm (a10 ea, int32 prv); +extern t_bool sdbr1 (a10 ea, int32 prv); +extern t_bool sdbr2 (a10 ea, int32 prv); +extern t_bool sdbr3 (a10 ea, int32 prv); +extern t_bool sdbr4 (a10 ea, int32 prv); +extern t_bool rdtim (a10 ea, int32 prv); +extern t_bool rdint (a10 ea, int32 prv); +extern t_bool wrtim (a10 ea, int32 prv); +extern t_bool wrint (a10 ea, int32 prv); +extern t_bool rdpcst (a10 ea, int32 prv); +extern t_bool wrpcst (a10 ea, int32 prv); +extern t_bool spm (a10 ea, int32 prv); +extern t_bool lpmr (a10 ea, int32 prv); +extern int32 pi_ub_vec (int32 lvl, int32 *uba); +extern t_stat tim_set_mod (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, VASIZE) }, + { ORDATA (FLAGS, flags, 18) }, + { ORDATA (AC0, acs[0], 36) }, /* addr in memory */ + { ORDATA (AC1, acs[1], 36) }, /* modified at exit */ + { ORDATA (AC2, acs[2], 36) }, /* to SCP */ + { ORDATA (AC3, acs[3], 36) }, + { ORDATA (AC4, acs[4], 36) }, + { ORDATA (AC5, acs[5], 36) }, + { ORDATA (AC6, acs[6], 36) }, + { ORDATA (AC7, acs[7], 36) }, + { ORDATA (AC10, acs[10], 36) }, + { ORDATA (AC11, acs[11], 36) }, + { ORDATA (AC12, acs[12], 36) }, + { ORDATA (AC13, acs[13], 36) }, + { ORDATA (AC14, acs[14], 36) }, + { ORDATA (AC15, acs[15], 36) }, + { ORDATA (AC16, acs[16], 36) }, + { ORDATA (AC17, acs[17], 36) }, + { ORDATA (PFW, pager_word, 36) }, + { ORDATA (EBR, ebr, EBR_N_EBR) }, + { FLDATA (PGON, ebr, EBR_V_PGON) }, + { FLDATA (T20P, ebr, EBR_V_T20P) }, + { ORDATA (UBR, ubr, 36) }, + { GRDATA (CURAC, ubr, 8, 3, UBR_V_CURAC), REG_RO }, + { GRDATA (PRVAC, ubr, 8, 3, UBR_V_PRVAC) }, + { ORDATA (SPT, spt, 36) }, + { ORDATA (CST, cst, 36) }, + { ORDATA (PUR, pur, 36) }, + { ORDATA (CSTM, cstm, 36) }, + { ORDATA (HSB, hsb, 36) }, + { ORDATA (DBR1, dbr1, PASIZE) }, + { ORDATA (DBR2, dbr2, PASIZE) }, + { ORDATA (DBR3, dbr3, PASIZE) }, + { ORDATA (DBR4, dbr4, PASIZE) }, + { ORDATA (PCST, pcst, 36) }, + { ORDATA (PIENB, pi_enb, 7) }, + { FLDATA (PION, pi_on, 0) }, + { ORDATA (PIACT, pi_act, 7) }, + { ORDATA (PIPRQ, pi_prq, 7) }, + { ORDATA (PIIOQ, pi_ioq, 7), REG_RO }, + { ORDATA (PIAPR, pi_apr, 7), REG_RO }, + { ORDATA (APRENB, apr_enb, 8) }, + { ORDATA (APRFLG, apr_flg, 8) }, + { ORDATA (APRLVL, apr_lvl, 3) }, + { ORDATA (RLOG, rlog, 10) }, + { FLDATA (F1PR, its_1pr, 0) }, + { BRDATA (PCQ, pcq, 8, VASIZE, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { DRDATA (INDMAX, ind_max, 8), PV_LEFT + REG_NZ }, + { DRDATA (XCTMAX, xct_max, 8), PV_LEFT + REG_NZ }, + { ORDATA (WRU, sim_int_char, 8) }, + { FLDATA (STOP_ILL, stop_op0, 0) }, + { BRDATA (REG, acs, 8, 36, AC_NUM * AC_NBLK) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_KLAD+UNIT_ITS+UNIT_T20, 0, "TOPS-10", "TOPS-10", &tim_set_mod }, + { UNIT_KLAD+UNIT_ITS+UNIT_T20, 0, NULL , "TOPS10", &tim_set_mod }, + { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_T20, "TOPS-20", "TOPS-20", &tim_set_mod }, + { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_T20, NULL, "TOPS20", &tim_set_mod }, + { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_ITS, "ITS", "ITS", &tim_set_mod }, + { UNIT_KLAD+UNIT_ITS+UNIT_T20, UNIT_KLAD, "diagnostic mode", "KLAD", &tim_set_mod }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, + NULL, &show_iospace }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, PASIZE, 1, 8, 36, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL + }; + +/* Data arrays */ + +const int32 pi_l2bit[8] = { + 0, 0100, 0040, 0020, 0010, 0004, 0002, 0001 + }; + +const int32 pi_m2lvl[128] = { + 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + +const d10 bytemask[64] = { 0, + 01, 03, 07, 017, 037, 077, + 0177, 0377, 0777, 01777, 03777, 07777, + 017777, 037777, 077777, + 0177777, 0377777, 0777777, + 01777777, 03777777, 07777777, + 017777777, 037777777, 077777777, + 0177777777, 0377777777, 0777777777, + 01777777777, 03777777777, 07777777777, + 017777777777, 037777777777, 077777777777, + 0177777777777, 0377777777777, 0777777777777, + ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, + ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, + ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES, ONES + }; + +static t_bool (*io700d[16])() = { + &aprid, NULL, NULL, NULL, &wrapr, &rdapr, &czapr, &coapr, + NULL, NULL, NULL, NULL, &wrpi, &rdpi, &czpi, &copi + }; +static t_bool (*io701d[16])() = { + NULL, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL + }; +static t_bool (*io702d[16])() = { + &rdspb, &rdcsb, &rdpur, &rdcstm, &rdtim, &rdint, &rdhsb, NULL, + &wrspb, &wrcsb, &wrpur, &wrcstm, &wrtim, &wrint, &wrhsb, NULL + }; +#define io700i io700d +static t_bool (*io701i[16])() = { + &clrcsh, &rdubr, &clrpt, &wrubr, &wrebr, &rdebr, NULL, NULL, + NULL, &rdpcst, NULL, &wrpcst, NULL, NULL, NULL, NULL + }; +static t_bool (*io702i[16])() = { + &sdbr1, &sdbr2, &sdbr3, &sdbr4, &rdtim, &rdint, &rdhsb, &spm, + &ldbr1, &ldbr2, &ldbr3, &ldbr4, &wrtim, &wrint, &wrhsb, &lpmr + }; + +/* JRST classes and validation table */ + +#define JRST_U 1 /* ok anywhere */ +#define JRST_E 2 /* ok exec mode */ +#define JRST_UIO 3 /* ok user I/O mode */ + +static t_stat jrst_tab[16] = { + JRST_U, JRST_U, JRST_U, 0, JRST_E, JRST_U, JRST_E, JRST_E, + JRST_UIO, 0, JRST_UIO, 0, JRST_E, JRST_U, 0, 0 + }; + +/* Address operations */ + +#define IM ((d10) ea) +#define IMS (((d10) ea) << 18) +#define JUMP(x) PCQ_ENTRY, PC = ((a10) (x)) & AMASK +#define SUBJ(x) CLRF (F_AFI | F_FPD | F_TR); JUMP (x) +#define INCPC PC = INCA (PC) + +/* AC operations */ + +#define AOBAC AC(ac) = AOB (AC(ac)) +#define SOBAC AC(ac) = SOB (AC(ac)) +#define G2AC rs[0] = AC(ac), rs[1] = AC(P1) +#define S1AC AC(ac) = rs[0] +#define S2AC S1AC, AC(P1) = rs[1] +#define LAC if (ac) AC(ac) = mb + +/* Memory operations */ + +#define RD mb = Read (ea, MM_OPND) +#define RDAC AC(ac) = Read (ea, MM_OPND) +#define RM mb = ReadM (ea, MM_OPND) +#define RMAC AC(ac) = ReadM (ea, MM_OPND) +#define RDP mb = Read (((a10) AC(ac)) & AMASK, MM_BSTK) +#define RD2 rs[0] = Read (ea, MM_OPND); \ + rs[1] = Read (INCA (ea), MM_OPND) +#define WR Write (ea, mb, MM_OPND) +#define WRAC Write (ea, AC(ac), MM_OPND) +#define WRP(x) Write (((a10) INCA (AC(ac))), (x), MM_BSTK) +#define WR1 Write (ea, rs[0], MM_OPND) +#define WR2 ReadM (INCA (ea), MM_OPND); \ + Write (ea, rs[0], MM_OPND); \ + Write (INCA (ea), rs[1], MM_OPND) + +/* Tests and compares */ + +#define TL(a) (TSTS (a) != 0) +#define TE(a) ((a) == 0) +#define TLE(a) (TL (a) || TE (a)) +#define TGE(a) (TSTS (a) == 0) +#define TN(a) ((a) != 0) +#define TG(a) (TGE (a) && TN (a)) +#define CL(a) ((TSTS (AC(ac) ^ a))? (a < AC(ac)): (AC(ac) < a)) +#define CE(a) (AC(ac) == a) +#define CLE(a) (CL (a) || CE (a)) +#define CGE(a) (!CL (a)) +#define CN(a) (AC(ac) != a) +#define CG(a) (CGE (a) && CN (a)) + +/* Word assemblies */ + +#define FLPC XWD (flags, PC) +#define UUOWORD (((d10) op) << INST_V_OP) | (((d10) ac) << INST_V_AC) | ea +#define APRHWORD ((apr_flg << APR_V_FLG) | (apr_lvl & APR_M_LVL) | \ + ((apr_flg & apr_enb)? APR_IRQ: 0)) +#define APRWORD ((apr_enb << (APR_V_FLG + 18)) | APRHWORD) +#define PIHWORD ((pi_act << PI_V_ACT) | (pi_on << PI_V_ON) | \ + (pi_enb << PI_V_ENB)) +#define PIWORD ((pi_prq << PI_V_PRQ) | PIHWORD) + +/* Instruction operations */ + +#define CIBP if (!TSTF (F_FPD)) { ibp (ea, pflgs); SETF (F_FPD); } +#define LDB AC(ac) = ldb (ea, pflgs) +#define DPB dpb (AC(ac), ea, pflgs) +#define FAD(s) fad (AC(ac), s, FALSE, 0) +#define FADR(s) fad (AC(ac), s, TRUE, 0) +#define FSB(s) fad (AC(ac), s, FALSE, 1) +#define FSBR(s) fad (AC(ac), s, TRUE, 1) +#define FMP(s) fmp (AC(ac), s, FALSE) +#define FMPR(s) fmp (AC(ac), s, TRUE) +#define FDV(s) fdv (AC(ac), s, rs, FALSE) +#define FDVR(s) fdv (AC(ac), s, rs, TRUE) +#define MOVN(s) NEG (s); MOVNF(s) +#define MOVM(s) ABS (s); MOVMF(s) +#define ADD(s) add (AC(ac), s) +#define SUB(s) sub (AC(ac), s) +#define IMUL(s) imul (AC(ac), s) +#define IDIV(s) idiv (AC(ac), s, rs) +#define MUL(s) mul (AC(ac), s, rs) +#define DIV(s) divi (ac, s, rs) +#define AOJ AC(ac) = INC (AC(ac)); INCF (AC(ac)) +#define AOS RM; mb = INC (mb); WR; INCF (mb); LAC +#define SOJ AC(ac) = DEC (AC(ac)); DECF (AC(ac)) +#define SOS RM; mb = DEC (mb); WR; DECF (mb); LAC +#define SETCA(s) ~AC(ac) & DMASK +#define SETCM(s) ~(s) & DMASK; +#define AND(s) AC(ac) & (s) +#define ANDCA(s) ~AC(ac) & (s) +#define ANDCM(s) AC(ac) & ~(s) +#define ANDCB(s) (~AC(ac) & ~(s)) & DMASK +#define IOR(s) AC(ac) | (s) +#define ORCA(s) (~AC(ac) | (s)) & DMASK +#define ORCM(s) (AC(ac) | ~(s)) & DMASK +#define ORCB(s) (~AC(ac) | ~(s)) & DMASK +#define XOR(s) AC(ac) ^ (s) +#define EQV(s) (~(AC(ac) ^ (s))) & DMASK +#define LL(s,d) ((s) & LMASK) | ((d) & RMASK) +#define RL(s,d) (((s) << 18) & LMASK) | ((d) & RMASK) +#define RR(s,d) ((s) & RMASK) | ((d) & LMASK) +#define LR(s,d) (((s) >> 18) & RMASK) | ((d) & LMASK) +#define LLO(s) ((s) & LMASK) | RMASK +#define RLO(s) (((s) << 18) & LMASK) | RMASK +#define RRO(s) ((s) & RMASK) | LMASK +#define LRO(s) (((s) >> 18) & RMASK) | LMASK +#define LLE(s) ((s) & LMASK) | (((s) & LSIGN)? RMASK: 0) +#define RLE(s) (((s) << 18) & LMASK) | (((s) & RSIGN)? RMASK: 0) +#define RRE(s) ((s) & RMASK) | (((s) & RSIGN)? LMASK: 0) +#define LRE(s) (((s) >> 18) & RMASK) | (((s) & LSIGN)? LMASK: 0) +#define TD_ RD +#define TS_ RD; mb = SWP (mb) +#define TL_ mb = IMS +#define TR_ mb = IM +#define T_Z AC(ac) = AC(ac) & ~mb +#define T_O AC(ac) = AC(ac) | mb +#define T_C AC(ac) = AC(ac) ^ mb +#define T__E if ((AC(ac) & mb) == 0) INCPC +#define T__N if ((AC(ac) & mb) != 0) INCPC +#define T__A INCPC +#define IOC if (TSTF (F_USR) && !TSTF (F_UIO)) goto MUUO; +#define IO7(x,y) IOC; fptr = ((Q_ITS)? x[ac]: y[ac]); \ + if (fptr == NULL) goto MUUO; \ + if (fptr (ea, MM_OPND)) INCPC; break; +#define IOA IOC; if (!Q_ITS) ea = calc_ioea (inst, pflgs) +#define IOAM IOC; ea = ((Q_ITS)? ((a10) Read (ea, MM_OPND)): \ + calc_ioea (inst, pflgs)) + +/* Flag tests */ + +#define MOVNF(x) if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1); \ + else if ((x) == 0) SETF (F_C0 | F_C1) +#define MOVMF(x) if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1) +#define INCF(x) if ((x) == 0) SETF (F_C0 | F_C1); \ + else if ((x) == MAXNEG) SETF (F_C1 | F_AOV | F_T1) +#define DECF(x) if ((x) == MAXPOS) SETF (F_C0 | F_AOV | F_T1); \ + else if ((x) != ONES) SETF (F_C0 | F_C1) +#define PUSHF if (LRZ (AC(ac)) == 0) SETF (F_T2) +#define POPF if (LRZ (AC(ac)) == RMASK) SETF (F_T2) +#define DMOVNF if (rs[1] == 0) { MOVNF (rs[0]); } + +t_stat sim_instr (void) +{ +a10 PC; /* set by setjmp */ +int abortval = 0; /* abort value */ +t_stat r; + +/* Restore register state */ + +if ((r = build_dib_tab ()) != SCPE_OK) return r; /* build, chk dib_tab */ +pager_PC = PC = saved_PC & AMASK; /* load local PC */ +set_dyn_ptrs (); /* set up local ptrs */ +pager_tc = FALSE; /* not in trap cycle */ +pager_pi = FALSE; /* not in pi sequence */ +rlog = 0; /* not in extend */ +pi_eval (); /* eval pi system */ +if (!Q_ITS) its_1pr = 0; /* ~ITS, clr 1-proc */ +t20_idlelock = 0; /* clr T20 idle lock */ + +/* Abort handling + + Aborts may come from within the simulator to stop simulation (values > 0), + for page fails (values < 0), or for an interrupt check (value = 0). +*/ + +abortval = setjmp (save_env); /* set abort hdlr */ +if ((abortval > 0) || pager_pi) { /* stop or pi err? */ + if (pager_pi && (abortval == PAGE_FAIL)) + abortval = STOP_PAGINT; /* stop for pi err */ + saved_PC = pager_PC & AMASK; /* failing instr PC */ + set_ac_display (ac_cur); /* set up AC display */ + pcq_r->qptr = pcq_p; /* update pc q ptr */ + return abortval; /* return to SCP */ + } + +/* Page fail - checked against KS10 ucode + All state variables MUST be declared global for GCC optimization to work +*/ + +else if (abortval == PAGE_FAIL) { /* page fail */ + d10 mb; + if (rlog) xtcln (rlog); /* clean up extend */ + rlog = 0; /* clear log */ + if (pager_tc) flags = pager_flags; /* trap? get flags */ + if (T20PAG) { /* TOPS-20 paging? */ + WriteP (upta + UPT_T20_PFL, pager_word); /* write page fail wd */ + WriteP (upta + UPT_T20_OFL, XWD (flags, 0)); + WriteP (upta + UPT_T20_OPC, pager_PC); + mb = ReadP (upta + UPT_T20_NPC); + } + else { + a10 ea; /* TOPS-10 or ITS */ + if (Q_ITS) { /* ITS? */ + ea = epta + EPT_ITS_PAG + (pi_m2lvl[pi_act] * 3); + if (its_1pr) flags = flags | F_1PR; /* store 1-proc */ + its_1pr = 0; /* clear 1-proc */ + } + else ea = upta + UPT_T10_PAG; + WriteP (ea, pager_word); /* write page fail wd */ + WriteP (ADDA (ea, 1), XWD (flags, pager_PC)); + mb = ReadP (ADDA (ea, 2)); + } + JUMP (mb); /* set new PC */ + set_newflags (mb, FALSE); /* set new flags */ + pi_eval (); /* eval pi system */ + } +else PC = pager_PC; /* intr, restore PC */ + +/* Main instruction fetch/decode loop: check clock queue, intr, trap, bkpt */ + +for ( ;; ) { /* loop until ABORT */ +int32 op, ac, i, st, xr, xct_cnt, its_2pr, pflgs; +a10 ea; +d10 inst, mb, indrct, rs[2]; +t_bool (*fptr)(); + +pager_PC = PC; /* update pager PC */ +pager_tc = FALSE; /* not in trap cycle */ +pflgs = 0; /* not in PXCT */ +xct_cnt = 0; /* count XCT's */ +if (sim_interval <= 0) { /* check clock queue */ + if (i = sim_process_event ()) ABORT (i); /* error? stop sim */ + pi_eval (); /* eval pi system */ + } + +/* PI interrupt (Unibus or system flags). + On the KS10, only JSR and XPCW are allowed as interrupt instructions. + Because of exec mode addressing, and unconditional processing of flags, + they are explicitly emulated here. +*/ + +if (qintr) { + int32 vec, uba; + pager_pi = TRUE; /* flag in pi seq */ + if (vec = pi_ub_vec (qintr, &uba)) { /* Unibus interrupt? */ + mb = ReadP (epta + EPT_UBIT + uba); /* get dispatch table */ + if (mb == 0) ABORT (STOP_ZERINT); /* invalid? stop */ + inst = ReadE ((((a10) mb) + (vec / 4)) & AMASK); + if (inst == 0) ABORT (STOP_ZERINT); + } + else inst = ReadP (epta + EPT_PIIT + (2 * qintr)); + op = GET_OP (inst); /* get opcode */ + ac = GET_AC (inst); /* get ac */ + if (its_1pr && Q_ITS) { /* 1-proc set? */ + flags = flags | F_1PR; /* store 1-proc */ + its_1pr = 0; /* clear 1-proc */ + } + if (op == OP_JSR) { /* JSR? */ + ea = calc_ea (inst, MM_CUR); /* calc ea, cur mode */ + WriteE (ea, FLPC); /* save flags+PC, exec */ + JUMP (INCA (ea)); /* PC = ea + 1 */ + set_newflags (0, FALSE); /* set new flags */ + } + else if ((op == OP_JRST) && (ac == AC_XPCW)) { /* XPCW? */ + ea = calc_ea (inst, MM_CUR); /* calc ea, cur mode */ + WriteE (ea, XWD (flags, 0)); /* write flags, exec */ + WriteE (ADDA (ea, 1), PC); /* write PC, exec */ + rs[0] = ReadE (ADDA (ea, 2)); /* read new flags */ + rs[1] = ReadE (ADDA (ea, 3)); /* read new PC */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], FALSE); /* set new flags */ + } + else ABORT (STOP_ILLINT); /* invalid instr */ + pi_act = pi_act | pi_l2bit[qintr]; /* set level active */ + pi_eval (); /* eval pi system */ + pager_pi = FALSE; /* end of sequence */ + if (sim_interval) sim_interval--; /* charge for instr */ + continue; + } /* end if interrupt */ + +/* Traps fetch and execute an instruction from the current mode process table. + On the KS10, the fetch of the next instruction has started, and a page fail + trap on the instruction fetch takes precedence over the trap. During a trap, + flags are cleared before the execute, but if the execute aborts, they must + be restored. Also, the MUUO processor needs to know whether we are in a + trap sequence. Hence, trap in progress is recorded in pflgs, and the + traps for pager restoration are recorded in pager_flags. +*/ + +if (TSTF (F_T1 | F_T2) && PAGING) { + Read (pager_PC = PC, MM_CUR); /* test fetch */ + pager_tc = TRUE; /* in a trap sequence */ + pager_flags = flags; /* save flags */ + ea = (TSTF (F_USR)? upta + UPT_TRBASE: epta + EPT_TRBASE) + + GET_TRAPS (flags); + inst = ReadP (ea); /* get trap instr */ + CLRF (F_T1 | F_T2); /* clear flags */ + } + +/* Test for instruction breakpoint */ + +else { + if (sim_brk_summ && + sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + ABORT (STOP_IBKPT); /* stop simulation */ + } + +/* Ready (at last) to get an instruction */ + + inst = Read (pager_PC = PC, MM_CUR); /* get instruction */ + INCPC; + sim_interval = sim_interval - 1; + } + +its_2pr = its_1pr; /* save 1-proc flag */ + +/* Execute instruction. XCT and PXCT also return here. */ + +XCT: +op = GET_OP (inst); /* get opcode */ +ac = GET_AC (inst); /* get AC */ +for (indrct = inst, i = 0; i < ind_max; i++) { /* calc eff addr */ + ea = GET_ADDR (indrct); + xr = GET_XR (indrct); + if (xr) ea = (ea + ((a10) XR (xr, MM_EA))) & AMASK; + if (TST_IND (indrct)) indrct = Read (ea, MM_EA); + else break; + } +if (i >= ind_max) + ABORT (STOP_IND); /* too many ind? stop */ +if (hst_lnt) { /* history enabled? */ + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) hst_p = 0; + hst[hst_p].pc = pager_PC | HIST_PC; + hst[hst_p].ea = ea; + hst[hst_p].ir = inst; + hst[hst_p].ac = AC(ac); + } +switch (op) { /* case on opcode */ + +/* UUO's (0000 - 0077) - checked against KS10 ucode */ + +case 0000: if (stop_op0) { ABORT (STOP_ILLEG); } + goto MUUO; +case 0001: /* local UUO's */ +case 0002: +case 0003: +case 0004: +case 0005: +case 0006: +case 0007: +case 0010: +case 0011: +case 0012: +case 0013: +case 0014: +case 0015: +case 0016: +case 0017: +case 0020: +case 0021: +case 0022: +case 0023: +case 0024: +case 0025: +case 0026: +case 0027: +case 0030: +case 0031: +case 0032: +case 0033: +case 0034: +case 0035: +case 0036: +case 0037: Write (040, UUOWORD, MM_CUR); /* store op, ac, ea */ + inst = Read (041, MM_CUR); /* get new instr */ + goto XCT; + +/* case 0040 - 0077: MUUO's, handled by default at end of case */ + +/* Floating point, bytes, multiple precision (0100 - 0177) */ + +/* case 0100: MUUO /* UJEN */ +/* case 0101: MUUO /* unassigned */ +case 0102: if (Q_ITS && !TSTF (F_USR)) { /* GFAD (KL), XCTRI (ITS) */ + inst = Read (ea, MM_OPND); + pflgs = pflgs | ac; goto XCT; + } + goto MUUO; +case 0103: if (Q_ITS && !TSTF (F_USR)) { /* GFSB (KL), XCTR (ITS) */ + inst = Read (ea, MM_OPND); + pflgs = pflgs | ac; goto XCT; + } + goto MUUO; +/* case 0104: MUUO /* JSYS (T20) */ +case 0105: AC(ac) = adjsp (AC(ac), ea); break; /* ADJSP */ +/* case 0106: MUUO /* GFMP (KL)*/ +/* case 0107: MUUO /* GFDV (KL) */ +case 0110: RD2; dfad (ac, rs, 0); break; /* DFAD */ +case 0111: RD2; dfad (ac, rs, 1); break; /* DFSB */ +case 0112: RD2; dfmp (ac, rs); break; /* DFMP */ +case 0113: RD2; dfdv (ac, rs); break; /* DFDV */ +case 0114: RD2; dadd (ac, rs); break; /* DADD */ +case 0115: RD2; dsub (ac, rs); break; /* DSUB */ +case 0116: RD2; dmul (ac, rs); break; /* DMUL */ +case 0117: RD2; ddiv (ac, rs); break; /* DDIV */ +case 0120: RD2; S2AC; break; /* DMOVE */ +case 0121: RD2; DMOVN (rs); S2AC; DMOVNF; break; /* DMOVN */ +case 0122: RD; fix(ac, mb, 0); break; /* FIX */ +case 0123: st = xtend (ac, ea, pflgs); /* EXTEND */ + rlog = 0; /* clear log */ + switch (st) { + case XT_SKIP: + INCPC; + case XT_NOSK: + break; + default: + goto MUUO; + } + break; +case 0124: G2AC; WR2; break; /* DMOVEM */ +case 0125: G2AC; DMOVN (rs); WR2; DMOVNF; break; /* DMOVNM */ +case 0126: RD; fix (ac, mb, 1); break; /* FIXR */ +case 0127: RD; AC(ac) = fltr (mb); break; /* FLTR */ +/* case 0130: MUUO /* UFA */ +/* case 0131: MUUO /* DFN */ +case 0132: AC(ac) = fsc (AC(ac), ea); break; /* FSC */ +case 0133: if (!ac) ibp (ea, pflgs); /* IBP */ + else adjbp (ac, ea, pflgs); break; +case 0134: CIBP; LDB; CLRF (F_FPD); break; /* ILBP */ +case 0135: LDB; break; /* LDB */ +case 0136: CIBP; DPB; CLRF (F_FPD); break; /* IDBP */ +case 0137: DPB; break; /* DPB */ +case 0140: RD; AC(ac) = FAD (mb); break; /* FAD */ +/* case 0141: MUUO /* FADL */ +case 0142: RM; mb = FAD (mb); WR; break; /* FADM */ +case 0143: RM; AC(ac) = FAD (mb); WRAC; break; /* FADB */ +case 0144: RD; AC(ac) = FADR (mb); break; /* FADR */ +case 0145: AC(ac) = FADR (IMS); break; /* FADRI */ +case 0146: RM; mb = FADR (mb); WR; break; /* FADRM */ +case 0147: RM; AC(ac) = FADR (mb); WRAC; break; /* FADRB */ +case 0150: RD; AC(ac) = FSB (mb); break; /* FSB */ +/* case 0151: MUUO /* FSBL */ +case 0152: RM; mb = FSB (mb); WR; break; /* FSBM */ +case 0153: RM; AC(ac) = FSB (mb); WRAC; break; /* FSBB */ +case 0154: RD; AC(ac) = FSBR (mb); break; /* FSBR */ +case 0155: AC(ac) = FSBR (IMS); break; /* FSBRI */ +case 0156: RM; mb = FSBR (mb); WR; break; /* FSBRM */ +case 0157: RM; AC(ac) = FSBR (mb); WRAC; break; /* FSBRB */ +case 0160: RD; AC(ac) = FMP (mb); break; /* FMP */ +/* case 0161: MUUO /* FMPL */ +case 0162: RM; mb = FMP (mb); WR; break; /* FMPM */ +case 0163: RM; AC(ac) = FMP (mb); WRAC; break; /* FMPB */ +case 0164: RD; AC(ac) = FMPR (mb); break; /* FMPR */ +case 0165: AC(ac) = FMPR (IMS); break; /* FMPRI */ +case 0166: RM; mb = FMPR (mb); WR; break; /* FMPRM */ +case 0167: RM; AC(ac) = FMPR (mb); WRAC; break; /* FMPRB */ +case 0170: RD; if (FDV (mb)) S1AC; break; /* FDV */ +/* case 0171: MUUO /* FDVL */ +case 0172: RM; if (FDV (mb)) WR1; break; /* FDVM */ +case 0173: RM; if (FDV (mb)) { S1AC; WRAC; } break; /* FDVB */ +case 0174: RD; if (FDVR (mb)) S1AC; break; /* FDVR */ +case 0175: if (FDVR (IMS)) S1AC; break; /* FDVRI */ +case 0176: RM; if (FDVR (mb)) WR1; break; /* FDVRM */ +case 0177: RM; if (FDVR (mb)) { S1AC; WRAC; } break; /* FDVRB */ + +/* Move, arithmetic, shift, and jump (0200 - 0277) + + Note that instructions which modify the flags and store a + result in memory must prove the writeability of the result + location before modifying the flags. Also, 0247 and 0257, + if not implemented, are nops, not MUUO's. +*/ + +case 0200: RDAC; break; /* MOVE */ +case 0201: AC(ac) = ea; break; /* MOVEI */ +case 0202: WRAC; break; /* MOVEM */ +case 0203: RM; LAC; break; /* MOVES */ +case 0204: RD; AC(ac) = SWP (mb); break; /* MOVS */ +case 0205: AC(ac) = IMS; break; /* MOVSI */ +case 0206: mb = SWP (AC(ac)); WR; break; /* MOVSM */ +case 0207: RM; mb = SWP (mb); WR; LAC; break; /* MOVSS */ +case 0210: RD; AC(ac) = MOVN (mb); break; /* MOVN */ +case 0211: AC(ac) = NEG (IM); /* MOVNI */ + if (AC(ac) == 0) SETF (F_C0 | F_C1); break; +case 0212: RM; mb = MOVN (AC(ac)); WR; break; /* MOVNM */ +case 0213: RM; mb = MOVN (mb); WR; LAC; break; /* MOVNS */ +case 0214: RD; AC(ac) = MOVM (mb); break; /* MOVM */ +case 0215: AC(ac) = ea; break; /* MOVMI */ +case 0216: RM; mb = MOVM (AC(ac)); WR; break; /* MOVMM */ +case 0217: RM; mb = MOVM (mb); WR; LAC; break; /* MOVMS */ +case 0220: RD; AC(ac) = IMUL (mb); break; /* IMUL */ +case 0221: AC(ac) = IMUL (IM); break; /* IMULI */ +case 0222: RM; mb = IMUL (mb); WR; break; /* IMULM */ +case 0223: RM; AC(ac) = IMUL (mb); WRAC; break; /* IMULB */ +case 0224: RD; MUL (mb); S2AC; break; /* MUL */ +case 0225: MUL (IM); S2AC; break; /* MULI */ +case 0226: RM; MUL (mb); WR1; break; /* MULM */ +case 0227: RM; MUL (mb); WR1; S2AC; break; /* MULB */ +case 0230: RD; if (IDIV (mb)) S2AC; break; /* IDIV */ +case 0231: if (IDIV (IM)) S2AC; break; /* IDIVI */ +case 0232: RM; if (IDIV (mb)) WR1; break; /* IDIVM */ +case 0233: RM; if (IDIV (mb)) { WR1; S2AC; } break; /* IDIVB */ +case 0234: RD; if (DIV (mb)) S2AC; break; /* DIV */ +case 0235: if (DIV (IM)) S2AC; break; /* DIVI */ +case 0236: RM; if (DIV (mb)) WR1; break; /* DIVM */ +case 0237: RM; if (DIV (mb)) { WR1; S2AC; } break; /* DIVB */ +case 0240: AC(ac) = ash (AC(ac), ea); break; /* ASH */ +case 0241: AC(ac) = rot (AC(ac), ea); break; /* ROT */ +case 0242: AC(ac) = lsh (AC(ac), ea); break; /* LSH */ +case 0243: AC(P1) = jffo (AC(ac)); /* JFFO */ + if (AC(ac)) JUMP (ea); break; +case 0244: ashc (ac, ea); break; /* ASHC */ +case 0245: rotc (ac, ea); break; /* ROTC */ +case 0246: lshc (ac, ea); break; /* LSHC */ +case 0247: if (Q_ITS) circ (ac, ea); break; /* (ITS) CIRC */ +case 0250: RM; WRAC; AC(ac) = mb; break; /* EXCH */ +case 0251: blt (ac, ea, pflgs); break; /* BLT */ +case 0252: AOBAC; if (TGE (AC(ac))) JUMP (ea); break; /* AOBJP */ +case 0253: AOBAC; if (TL (AC(ac))) JUMP (ea); break; /* AOBJN */ +/* case 0254: /* shown later /* JRST */ +case 0255: if (flags & (ac << 14)) { /* JFCL */ + JUMP (ea); + CLRF (ac << 14); + } + break; +case 0256: if (xct_cnt++ >= xct_max) /* XCT */ + ABORT (STOP_XCT); + inst = Read (ea, MM_OPND); + if (ac && !TSTF (F_USR) && !Q_ITS) + pflgs = pflgs | ac; + goto XCT; +case 0257: if (Q_ITS) goto MUUO; /* MAP */ + AC(ac) = map (ea, MM_OPND); break; +case 0260: WRP (FLPC); AOBAC; /* PUSHJ */ + SUBJ (ea); PUSHF; break; +case 0261: RD; WRP (mb); AOBAC; PUSHF; break; /* PUSH */ +case 0262: RDP; WR; SOBAC; POPF; break; /* POP */ +case 0263: RDP; JUMP (mb); SOBAC; POPF; break; /* POPJ */ +case 0264: Write (ea, FLPC, MM_OPND); /* JSR */ + SUBJ (INCR (ea)); break; +case 0265: AC(ac) = FLPC; SUBJ (ea); break; /* JSP */ +case 0266: WRAC; AC(ac) = XWD (ea, PC); /* JSA */ + JUMP (INCR (ea)); break; +case 0267: AC(ac) = Read ((a10) LRZ (AC(ac)), MM_OPND);/* JRA */ + JUMP (ea); break; +case 0270: RD; AC(ac) = ADD (mb); break; /* ADD */ +case 0271: AC(ac) = ADD (IM); break; /* ADDI */ +case 0272: RM; mb = ADD (mb); WR; break; /* ADDM */ +case 0273: RM; AC(ac) = ADD (mb); WRAC; break; /* ADDB */ +case 0274: RD; AC(ac) = SUB (mb); break; /* SUB */ +case 0275: AC(ac) = SUB (IM); break; /* SUBI */ +case 0276: RM; mb = SUB (mb); WR; break; /* SUBM */ +case 0277: RM; AC(ac) = SUB (mb); WRAC; break; /* SUBB */ + +/* Compare, jump, skip instructions (0300 - 0377) - checked against KS10 ucode */ + +case 0300: break; /* CAI */ +case 0301: if (CL (IM)) INCPC; break; /* CAIL */ +case 0302: if (CE (IM)) INCPC; break; /* CAIE */ +case 0303: if (CLE (IM)) INCPC; break; /* CAILE */ +case 0304: INCPC; break; /* CAIA */ +case 0305: if (CGE (IM)) INCPC; break; /* CAIGE */ +case 0306: if (CN (IM)) INCPC; break; /* CAIN */ +case 0307: if (CG (IM)) INCPC; break; /* CAIG */ +case 0310: RD; break; /* CAM */ +case 0311: RD; if (CL (mb)) INCPC; break; /* CAML */ +case 0312: RD; if (CE (mb)) INCPC; break; /* CAME */ +case 0313: RD; if (CLE (mb)) INCPC; break; /* CAMLE */ +case 0314: RD; INCPC; break; /* CAMA */ +case 0315: RD; if (CGE (mb)) INCPC; break; /* CAMGE */ +case 0316: RD; if (CN (mb)) INCPC; break; /* CAMN */ +case 0317: RD; if (CG (mb)) INCPC; break; /* CAMG */ +case 0320: break; /* JUMP */ +case 0321: if (TL (AC(ac))) JUMP (ea); break; /* JUMPL */ +case 0322: if (TE (AC(ac))) JUMP (ea); break; /* JUMPE */ +case 0323: if (TLE( AC(ac))) JUMP (ea); break; /* JUMPLE */ +case 0324: JUMP (ea); break; /* JUMPA */ +case 0325: if (TGE (AC(ac))) JUMP (ea); break; /* JUMPGE */ +case 0326: if (TN (AC(ac))) JUMP (ea); break; /* JUMPN */ +case 0327: if (TG (AC(ac))) JUMP (ea); break; /* JUMPG */ +case 0330: RD; LAC; break; /* SKIP */ +case 0331: RD; LAC; if (TL (mb)) INCPC; break; /* SKIPL */ +case 0332: RD; LAC; if (TE (mb)) INCPC; break; /* SKIPE */ +case 0333: RD; LAC; if (TLE (mb)) INCPC; break; /* SKIPLE */ +case 0334: RD; LAC; INCPC; break; /* SKIPA */ +case 0335: RD; LAC; if (TGE (mb)) INCPC; break; /* SKIPGE */ +case 0336: RD; LAC; if (TN (mb)) INCPC; break; /* SKIPN */ +case 0337: RD; LAC; if (TG (mb)) INCPC; break; /* SKIPG */ +case 0340: AOJ; break; /* AOJ */ +case 0341: AOJ; if (TL (AC(ac))) JUMP (ea); break; /* AOJL */ +case 0342: AOJ; if (TE (AC(ac))) JUMP (ea); break; /* AOJE */ +case 0343: AOJ; if (TLE (AC(ac))) JUMP (ea); break; /* AOJLE */ +case 0344: AOJ; JUMP(ea); /* AOJA */ + if (Q_ITS && Q_IDLE && /* ITS idle? */ + TSTF (F_USR) && (pager_PC == 017) && /* user mode, loc 17? */ + (ac == 0) && (ea == 017)) /* AOJA 0,17? */ + sim_idle (0, FALSE); + break; +case 0345: AOJ; if (TGE (AC(ac))) JUMP (ea); break; /* AOJGE */ +case 0346: AOJ; if (TN (AC(ac))) JUMP (ea); break; /* AOJN */ +case 0347: AOJ; if (TG (AC(ac))) JUMP (ea); break; /* AOJG */ +case 0350: AOS; break; /* AOS */ +case 0351: AOS; if (TL (mb)) INCPC; break; /* AOSL */ +case 0352: AOS; if (TE (mb)) INCPC; break; /* AOSE */ +case 0353: AOS; if (TLE (mb)) INCPC; break; /* AOSLE */ +case 0354: AOS; INCPC; break; /* AOSA */ +case 0355: AOS; if (TGE (mb)) INCPC; break; /* AOSGE */ +case 0356: AOS; if (TN (mb)) INCPC; break; /* AOSN */ +case 0357: AOS; if (TG (mb)) INCPC; break; /* AOSG */ +case 0360: SOJ; break; /* SOJ */ +case 0361: SOJ; if (TL (AC(ac))) JUMP (ea); break; /* SOJL */ +case 0362: SOJ; if (TE (AC(ac))) JUMP (ea); break; /* SOJE */ +case 0363: SOJ; if (TLE (AC(ac))) JUMP (ea); break; /* SOJLE */ +case 0364: SOJ; JUMP(ea); break; /* SOJA */ +case 0365: SOJ; if (TGE (AC(ac))) JUMP (ea); break; /* SOJGE */ +case 0366: SOJ; if (TN (AC(ac))) JUMP (ea); break; /* SOJN */ +case 0367: SOJ; if (TG (AC(ac))) JUMP (ea); /* SOJG */ + if ((ea == pager_PC) && Q_IDLE) { /* to self, idle enab? */ + extern int32 tmr_poll; + if ((ac == 6) && (ea == 1) && /* SOJG 6,1? */ + TSTF (F_USR) && Q_T10) /* T10, user mode? */ + sim_idle (0, FALSE); + else if (!t20_idlelock && /* interlock off? */ + (ac == 2) && (ea == 3) && /* SOJG 2,3? */ + !TSTF (F_USR) && Q_T20 && /* T20, mon mode? */ + (sim_interval > (tmr_poll >> 1))) { /* >= half clock? */ + t20_idlelock = 1; /* set interlock */ + if (sim_os_ms_sleep (1)) /* sleep 1ms */ + sim_interval = 0; /* if ok, sched event */ + } + } + break; +case 0370: SOS; break; /* SOS */ +case 0371: SOS; if (TL (mb)) INCPC; break; /* SOSL */ +case 0372: SOS; if (TE (mb)) INCPC; break; /* SOSE */ +case 0373: SOS; if (TLE (mb)) INCPC; break; /* SOSLE */ +case 0374: SOS; INCPC; break; /* SOSA */ +case 0375: SOS; if (TGE (mb)) INCPC; break; /* SOSGE */ +case 0376: SOS; if (TN (mb)) INCPC; break; /* SOSN */ +case 0377: SOS; if (TG (mb)) INCPC; break; /* SOSG */ + +/* Boolean instructions (0400 - 0477) - checked against KS10 ucode + + Note that for boolean B, the initial read checks writeability of + the memory operand; hence, it is safe to modify the AC. +*/ + +case 0400: AC(ac) = 0; break; /* SETZ */ +case 0401: AC(ac) = 0; break; /* SETZI */ +case 0402: mb = 0; WR; break; /* SETZM */ +case 0403: mb = 0; WR; AC(ac) = 0; break; /* SETZB */ +case 0404: RD; AC(ac) = AND (mb); break; /* AND */ +case 0405: AC(ac) = AND (IM); break; /* ANDI */ +case 0406: RM; mb = AND (mb); WR; break; /* ANDM */ +case 0407: RM; AC(ac) = AND (mb); WRAC; break; /* ANDB */ +case 0410: RD; AC(ac) = ANDCA (mb); break; /* ANDCA */ +case 0411: AC(ac) = ANDCA (IM); break; /* ANDCAI */ +case 0412: RM; mb = ANDCA (mb); WR; break; /* ANDCAM */ +case 0413: RM; AC(ac) = ANDCA (mb); WRAC; break; /* ANDCAB */ +case 0414: RDAC; break; /* SETM */ +case 0415: AC(ac) = ea; break; /* SETMI */ +case 0416: RM; WR; break; /* SETMM */ +case 0417: RMAC; WRAC; break; /* SETMB */ +case 0420: RD; AC(ac) = ANDCM (mb); break; /* ANDCM */ +case 0421: AC(ac) = ANDCM (IM); break; /* ANDCMI */ +case 0422: RM; mb = ANDCM (mb); WR; break; /* ANDCMM */ +case 0423: RM; AC(ac) = ANDCM (mb); WRAC; break; /* ANDCMB */ +case 0424: break; /* SETA */ +case 0425: break; /* SETAI */ +case 0426: WRAC; break; /* SETAM */ +case 0427: WRAC; break; /* SETAB */ +case 0430: RD; AC(ac) = XOR (mb); break; /* XOR */ +case 0431: AC(ac) = XOR (IM); break; /* XORI */ +case 0432: RM; mb = XOR (mb); WR; break; /* XORM */ +case 0433: RM; AC(ac) = XOR (mb); WRAC; break; /* XORB */ +case 0434: RD; AC(ac) = IOR (mb); break; /* IOR */ +case 0435: AC(ac) = IOR (IM); break; /* IORI */ +case 0436: RM; mb = IOR (mb); WR; break; /* IORM */ +case 0437: RM; AC(ac) = IOR (mb); WRAC; break; /* IORB */ +case 0440: RD; AC(ac) = ANDCB (mb); break; /* ANDCB */ +case 0441: AC(ac) = ANDCB (IM); break; /* ANDCBI */ +case 0442: RM; mb = ANDCB (mb); WR; break; /* ANDCBM */ +case 0443: RM; AC(ac) = ANDCB (mb); WRAC; break; /* ANDCBB */ +case 0444: RD; AC(ac) = EQV (mb); break; /* EQV */ +case 0445: AC(ac) = EQV (IM); break; /* EQVI */ +case 0446: RM; mb = EQV (mb); WR; break; /* EQVM */ +case 0447: RM; AC(ac) = EQV (mb); WRAC; break; /* EQVB */ +case 0450: RD; AC(ac) = SETCA (mb); break; /* SETCA */ +case 0451: AC(ac) = SETCA (IM); break; /* SETCAI */ +case 0452: RM; mb = SETCA (mb); WR; break; /* SETCAM */ +case 0453: RM; AC(ac) = SETCA (mb); WRAC; break; /* SETCAB */ +case 0454: RD; AC(ac) = ORCA (mb); break; /* ORCA */ +case 0455: AC(ac) = ORCA (IM); break; /* ORCAI */ +case 0456: RM; mb = ORCA (mb); WR; break; /* ORCAM */ +case 0457: RM; AC(ac) = ORCA (mb); WRAC; break; /* ORCAB */ +case 0460: RD; AC(ac) = SETCM (mb); break; /* SETCM */ +case 0461: AC(ac) = SETCM (IM); break; /* SETCMI */ +case 0462: RM; mb = SETCM (mb); WR; break; /* SETCMM */ +case 0463: RM; AC(ac) = SETCM (mb); WRAC; break; /* SETCMB */ +case 0464: RD; AC(ac) = ORCM (mb); break; /* ORCM */ +case 0465: AC(ac) = ORCM (IM); break; /* ORCMI */ +case 0466: RM; mb = ORCM (mb); WR; break; /* ORCMM */ +case 0467: RM; AC(ac) = ORCM (mb); WRAC; break; /* ORCMB */ +case 0470: RD; AC(ac) = ORCB (mb); break; /* ORCB */ +case 0471: AC(ac) = ORCB (IM); break; /* ORCBI */ +case 0472: RM; mb = ORCB (mb); WR; break; /* ORCBM */ +case 0473: RM; AC(ac) = ORCB (mb); WRAC; break; /* ORCBB */ +case 0474: AC(ac) = ONES; break; /* SETO */ +case 0475: AC(ac) = ONES; break; /* SETOI */ +case 0476: mb = ONES; WR; break; /* SETOM */ +case 0477: mb = ONES; WR; AC(ac) = ONES; break; /* SETOB */ + +/* Halfword instructions (0500 - 0577) - checked against KS10 ucode */ + +case 0500: RD; AC(ac) = LL (mb, AC(ac)); break; /* HLL */ +case 0501: AC(ac) = LL (IM, AC(ac)); break; /* HLLI */ +case 0502: RM; mb = LL (AC(ac), mb); WR; break; /* HLLM */ +case 0503: RM; mb = LL (mb, mb); WR; LAC; break; /* HLLS */ +case 0504: RD; AC(ac) = RL (mb, AC(ac)); break; /* HRL */ +case 0505: AC(ac) = RL (IM, AC(ac)); break; /* HRLI */ +case 0506: RM; mb = RL (AC(ac), mb); WR; break; /* HRLM */ +case 0507: RM; mb = RL (mb, mb); WR; LAC; break; /* HRLS */ +case 0510: RD; AC(ac) = LLZ (mb); break; /* HLLZ */ +case 0511: AC(ac) = LLZ (IM); break; /* HLLZI */ +case 0512: mb = LLZ (AC(ac)); WR; break; /* HLLZM */ +case 0513: RM; mb = LLZ (mb); WR; LAC; break; /* HLLZS */ +case 0514: RD; AC(ac) = RLZ (mb); break; /* HRLZ */ +case 0515: AC(ac) = RLZ (IM); break; /* HRLZI */ +case 0516: mb = RLZ (AC(ac)); WR; break; /* HRLZM */ +case 0517: RM; mb = RLZ (mb); WR; LAC; break; /* HRLZS */ +case 0520: RD; AC(ac) = LLO (mb); break; /* HLLO */ +case 0521: AC(ac) = LLO (IM); break; /* HLLOI */ +case 0522: mb = LLO (AC(ac)); WR; break; /* HLLOM */ +case 0523: RM; mb = LLO (mb); WR; LAC; break; /* HLLOS */ +case 0524: RD; AC(ac) = RLO (mb); break; /* HRLO */ +case 0525: AC(ac) = RLO (IM); break; /* HRLOI */ +case 0526: mb = RLO (AC(ac)); WR; break; /* HRLOM */ +case 0527: RM; mb = RLO (mb); WR; LAC; break; /* HRLOS */ +case 0530: RD; AC(ac) = LLE (mb); break; /* HLLE */ +case 0531: AC(ac) = LLE (IM); break; /* HLLEI */ +case 0532: mb = LLE (AC(ac)); WR; break; /* HLLEM */ +case 0533: RM; mb = LLE (mb); WR; LAC; break; /* HLLES */ +case 0534: RD; AC(ac) = RLE (mb); break; /* HRLE */ +case 0535: AC(ac) = RLE (IM); break; /* HRLEI */ +case 0536: mb = RLE (AC(ac)); WR; break; /* HRLEM */ +case 0537: RM; mb = RLE (mb); WR; LAC; break; /* HRLES */ +case 0540: RD; AC(ac) = RR (mb, AC(ac)); break; /* HRR */ +case 0541: AC(ac) = RR (IM, AC(ac)); break; /* HRRI */ +case 0542: RM; mb = RR (AC(ac), mb); WR; break; /* HRRM */ +case 0543: RM; mb = RR (mb, mb); WR; LAC; break; /* HRRS */ +case 0544: RD; AC(ac) = LR (mb, AC(ac)); break; /* HLR */ +case 0545: AC(ac) = LR (IM, AC(ac)); break; /* HLRI */ +case 0546: RM; mb = LR (AC(ac), mb); WR; break; /* HLRM */ +case 0547: RM; mb = LR (mb, mb); WR; LAC; break; /* HLRS */ +case 0550: RD; AC(ac) = RRZ (mb); break; /* HRRZ */ +case 0551: AC(ac) = RRZ (IM); break; /* HRRZI */ +case 0552: mb = RRZ (AC(ac)); WR; break; /* HRRZM */ +case 0553: RM; mb = RRZ(mb); WR; LAC; break; /* HRRZS */ +case 0554: RD; AC(ac) = LRZ (mb); break; /* HLRZ */ +case 0555: AC(ac) = LRZ (IM); break; /* HLRZI */ +case 0556: mb = LRZ (AC(ac)); WR; break; /* HLRZM */ +case 0557: RM; mb = LRZ (mb); WR; LAC; break; /* HLRZS */ +case 0560: RD; AC(ac) = RRO (mb); break; /* HRRO */ +case 0561: AC(ac) = RRO (IM); break; /* HRROI */ +case 0562: mb = RRO (AC(ac)); WR; break; /* HRROM */ +case 0563: RM; mb = RRO (mb); WR; LAC; break; /* HRROS */ +case 0564: RD; AC(ac) = LRO (mb); break; /* HLRO */ +case 0565: AC(ac) = LRO (IM); break; /* HLROI */ +case 0566: mb = LRO (AC(ac)); WR; break; /* HLROM */ +case 0567: RM; mb = LRO (mb); WR; LAC; break; /* HLROS */ +case 0570: RD; AC(ac) = RRE (mb); break; /* HRRE */ +case 0571: AC(ac) = RRE (IM); break; /* HRREI */ +case 0572: mb = RRE (AC(ac)); WR; break; /* HRREM */ +case 0573: RM; mb = RRE (mb); WR; LAC; break; /* HRRES */ +case 0574: RD; AC(ac) = LRE (mb); break; /* HLRE */ +case 0575: AC(ac) = LRE (IM); break; /* HLREI */ +case 0576: mb = LRE (AC(ac)); WR; break; /* HLREM */ +case 0577: RM; mb = LRE (mb); WR; LAC; break; /* HLRES */ + +/* Test instructions (0600 - 0677) - checked against KS10 ucode + In the KS10 ucode, TDN and TSN do not fetch an operand; the Processor + Reference Manual describes them as NOPs that reference memory. +*/ + +case 0600: break; /* TRN */ +case 0601: break; /* TLN */ +case 0602: TR_; T__E; break; /* TRNE */ +case 0603: TL_; T__E; break; /* TLNE */ +case 0604: T__A; break; /* TRNA */ +case 0605: T__A; break; /* TLNA */ +case 0606: TR_; T__N; break; /* TRNN */ +case 0607: TL_; T__N; break; /* TLNN */ +case 0610: TD_; break; /* TDN */ +case 0611: TS_; break; /* TSN */ +case 0612: TD_; T__E; break; /* TDNE */ +case 0613: TS_; T__E; break; /* TSNE */ +case 0614: TD_; T__A; break; /* TDNA */ +case 0615: TS_; T__A; break; /* TSNA */ +case 0616: TD_; T__N; break; /* TDNN */ +case 0617: TS_; T__N; break; /* TSNN */ +case 0620: TR_; T_Z; break; /* TRZ */ +case 0621: TL_; T_Z; break; /* TLZ */ +case 0622: TR_; T__E; T_Z; break; /* TRZE */ +case 0623: TL_; T__E; T_Z; break; /* TLZE */ +case 0624: TR_; T__A; T_Z; break; /* TRZA */ +case 0625: TL_; T__A; T_Z; break; /* TLZA */ +case 0626: TR_; T__N; T_Z; break; /* TRZN */ +case 0627: TL_; T__N; T_Z; break; /* TLZN */ +case 0630: TD_; T_Z; break; /* TDZ */ +case 0631: TS_; T_Z; break; /* TSZ */ +case 0632: TD_; T__E; T_Z; break; /* TDZE */ +case 0633: TS_; T__E; T_Z; break; /* TSZE */ +case 0634: TD_; T__A; T_Z; break; /* TDZA */ +case 0635: TS_; T__A; T_Z; break; /* TSZA */ +case 0636: TD_; T__N; T_Z; break; /* TDZN */ +case 0637: TS_; T__N; T_Z; break; /* TSZN */ +case 0640: TR_; T_C; break; /* TRC */ +case 0641: TL_; T_C; break; /* TLC */ +case 0642: TR_; T__E; T_C; break; /* TRCE */ +case 0643: TL_; T__E; T_C; break; /* TLCE */ +case 0644: TR_; T__A; T_C; break; /* TRCA */ +case 0645: TL_; T__A; T_C; break; /* TLCA */ +case 0646: TR_; T__N; T_C; break; /* TRCN */ +case 0647: TL_; T__N; T_C; break; /* TLCN */ +case 0650: TD_; T_C; break; /* TDC */ +case 0651: TS_; T_C; break; /* TSC */ +case 0652: TD_; T__E; T_C; break; /* TDCE */ +case 0653: TS_; T__E; T_C; break; /* TSCE */ +case 0654: TD_; T__A; T_C; break; /* TDCA */ +case 0655: TS_; T__A; T_C; break; /* TSCA */ +case 0656: TD_; T__N; T_C; break; /* TDCN */ +case 0657: TS_; T__N; T_C; break; /* TSCN */ +case 0660: TR_; T_O; break; /* TRO */ +case 0661: TL_; T_O; break; /* TLO */ +case 0662: TR_; T__E; T_O; break; /* TROE */ +case 0663: TL_; T__E; T_O; break; /* TLOE */ +case 0664: TR_; T__A; T_O; break; /* TROA */ +case 0665: TL_; T__A; T_O; break; /* TLOA */ +case 0666: TR_; T__N; T_O; break; /* TRON */ +case 0667: TL_; T__N; T_O; break; /* TLON */ +case 0670: TD_; T_O; break; /* TDO */ +case 0671: TS_; T_O; break; /* TSO */ +case 0672: TD_; T__E; T_O; break; /* TDOE */ +case 0673: TS_; T__E; T_O; break; /* TSOE */ +case 0674: TD_; T__A; T_O; break; /* TDOA */ +case 0675: TS_; T__A; T_O; break; /* TSOA */ +case 0676: TD_; T__N; T_O; break; /* TDON */ +case 0677: TS_; T__N; T_O; break; /* TSON */ + +/* I/O instructions (0700 - 0777) + + Only the defined I/O instructions have explicit case labels; + the rest default to unimplemented (monitor UUO). Note that + 710-715 and 720-725 have different definitions under ITS and + use normal effective addresses instead of the special address + calculation required by TOPS-10 and TOPS-20. +*/ + +case 0700: IO7 (io700i, io700d); break; /* I/O 0 */ +case 0701: IO7 (io701i, io701d); break; /* I/O 1 */ +case 0702: IO7 (io702i, io702d); break; /* I/O 2 */ +case 0704: IOC; AC(ac) = Read (ea, OPND_PXCT); break; /* UMOVE */ +case 0705: IOC; Write (ea, AC(ac), OPND_PXCT); break; /* UMOVEM */ +case 0710: IOA; if (io710 (ac, ea)) INCPC; break; /* TIOE, IORDI */ +case 0711: IOA; if (io711 (ac, ea)) INCPC; break; /* TION, IORDQ */ +case 0712: IOAM; AC(ac) = io712 (ea); break; /* RDIO, IORD */ +case 0713: IOAM; io713 (AC(ac), ea); break; /* WRIO, IOWR */ +case 0714: IOA; io714 (AC(ac), ea); break; /* BSIO, IOWRI */ +case 0715: IOA; io715 (AC(ac), ea); break; /* BCIO, IOWRQ */ +case 0716: IOC; bltu (ac, ea, pflgs, 0); break; /* BLTBU */ +case 0717: IOC; bltu (ac, ea, pflgs, 1); break; /* BLTUB */ +case 0720: IOA; if (io720 (ac, ea)) INCPC; break; /* TIOEB, IORDBI */ +case 0721: IOA; if (io721 (ac, ea)) INCPC; break; /* TIONB, IORDBQ */ +case 0722: IOAM; AC(ac) = io722 (ea); break; /* RDIOB, IORDB */ +case 0723: IOAM; io723 (AC(ac), ea); break; /* WRIOB, IOWRB */ +case 0724: IOA; io724 (AC(ac), ea); break; /* BSIOB, IOWRBI */ +case 0725: IOA; io725 (AC(ac), ea); break; /* BCIOB, IOWRBQ */ + +/* If undefined, monitor UUO - checked against KS10 ucode + The KS10 implements a much more limited version of MUUO flag handling. + In the KS10, the trap ucode checks for opcodes 000-077. If the opcode + is in that range, the trap flags are not cleared. Instead, the MUUO + microcode stores the flags with traps cleared, and uses the trap flags + to determine how to vector. Thus, MUUO's >= 100 will vector incorrectly. +*/ + +default: +MUUO: + its_2pr = 0; /* clear trap */ + if (T20PAG) { /* TOPS20 paging? */ + int32 tf = (op << (INST_V_OP - 18)) | (ac << (INST_V_AC - 18)); + WriteP (upta + UPT_MUUO, XWD ( /* store flags,,op+ac */ + flags & ~(F_T2 | F_T1), tf)); /* traps clear */ + WriteP (upta + UPT_MUPC, PC); /* store PC */ + WriteP (upta + UPT_T20_UEA, ea); /* store eff addr */ + WriteP (upta + UPT_T20_CTX, UBRWORD); /* store context */ + } + else { /* TOPS10/ITS */ + WriteP (upta + UPT_MUUO, UUOWORD); /* store instr word */ + WriteP (upta + UPT_MUPC, XWD ( /* store flags,,PC */ + flags & ~(F_T2 | F_T1), PC)); /* traps clear */ + WriteP (upta + UPT_T10_CTX, UBRWORD); /* store context */ + } + ea = upta + (TSTF (F_USR)? UPT_UNPC: UPT_ENPC) + + (pager_tc? UPT_NPCT: 0); /* calculate vector */ + mb = ReadP (ea); /* new flags, PC */ + JUMP (mb); /* set new PC */ + if (TSTF (F_USR)) mb = mb | XWD (F_UIO, 0); /* set PCU */ + set_newflags (mb, FALSE); /* set new flags */ + break; + +/* JRST - checked against KS10 ucode + Differences from the KS10: the KS10 + - (JRSTF, JEN) refetches the base instruction from PC - 1 + - (XJEN) dismisses interrupt before reading the new flags and PC + - (XPCW) writes the old flags and PC before reading the new + ITS microcode includes extended JRST's, although they are not used +*/ + +case 0254: /* JRST */ + i = jrst_tab[ac]; /* get subop flags */ + if ((i == 0) || ((i == JRST_E) && TSTF (F_USR)) || + ((i == JRST_UIO) && TSTF (F_USR) && !TSTF (F_UIO))) + goto MUUO; /* not legal */ + switch (ac) { /* case on subopcode */ + + case 000: /* JRST 0 = jump */ + case 001: /* JRST 1 = portal */ + JUMP (ea); + break; + + case 002: /* JRST 2 = JRSTF */ + mb = calc_jrstfea (inst, pflgs); /* recalc addr w flgs */ + JUMP (ea); /* set new PC */ + set_newflags (mb, TRUE); /* set new flags */ + break; + + case 004: /* JRST 4 = halt */ + JUMP (ea); /* old_PC = halt + 1 */ + pager_PC = PC; /* force right PC */ + ABORT (STOP_HALT); /* known to be exec */ + break; + + case 005: /* JRST 5 = XJRSTF */ + RD2; /* read doubleword */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], TRUE); /* set new flags */ + break; + + case 006: /* JRST 6 = XJEN */ + RD2; /* read doubleword */ + pi_dismiss (); /* page ok, dismiss */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], FALSE); /* known to be exec */ + break; + + case 007: /* JRST 7 = XPCW */ + ea = ADDA (i = ea, 2); /* new flags, PC */ + RD2; /* read, test page fail */ + ReadM (INCA (i), MM_OPND); /* test PC write */ + Write (i, XWD (flags, 0), MM_OPND); /* write flags */ + Write (INCA (i), PC, MM_OPND); /* write PC */ + JUMP (rs[1]); /* set new PC */ + set_newflags (rs[0], FALSE); /* known to be exec */ + break; + + case 010: /* JRST 10 = dismiss */ + pi_dismiss (); /* dismiss int */ + JUMP (ea); /* set new PC */ + break; + + case 012: /* JRST 12 = JEN */ + mb = calc_jrstfea (inst, pflgs); /* recalc addr w flgs */ + JUMP (ea); /* set new PC */ + set_newflags (mb, TRUE); /* set new flags */ + pi_dismiss (); /* dismiss int */ + break; + + case 014: /* JRST 14 = SFM */ + Write (ea, XWD (flags, 0), MM_OPND); + break; + + case 015: /* JRST 15 = XJRST */ + if (!T20PAG) goto MUUO; /* only in TOPS20 paging */ + JUMP (Read (ea, MM_OPND)); /* jump to M[ea] */ + break; + } /* end case subop */ + break; + } /* end case op */ + +if (its_2pr) { /* 1-proc trap? */ + its_1pr = its_2pr = 0; /* clear trap */ + if (Q_ITS) { /* better be ITS */ + WriteP (upta + UPT_1PO, FLPC); /* wr old flgs, PC */ + mb = ReadP (upta + UPT_1PN); /* rd new flgs, PC */ + JUMP (mb); /* set PC */ + set_newflags (mb, TRUE); /* set new flags */ + } + } /* end if 2-proc */ +} /* end for */ + +/* Should never get here */ + +ABORT (STOP_UNKNOWN); +} + +/* Single word integer routines */ + +/* Integer add + + Truth table for integer add + + case a b r flags + 1 + + + none + 2 + + - AOV + C1 + 3 + - + C0 + C1 + 4 + - - - + 5 - + + C0 + C1 + 6 - + - - + 7 - - + AOV + C0 + 8 - - - C0 + C1 +*/ + +d10 add (d10 a, d10 b) +{ +d10 r; + +r = (a + b) & DMASK; +if (TSTS (a & b)) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ + return r; + } +if (!TSTS (a | b)) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); /* case 2 */ + return r; /* case 1 */ + } +if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +return r; +} + +/* Integer subtract - actually ac + ~op + 1 */ + +d10 sub (d10 a, d10 b) +{ +d10 r; + +r = (a - b) & DMASK; +if (TSTS (a & ~b)) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ + return r; + } +if (!TSTS (a | ~b)) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); /* case 2 */ + return r; /* case 1 */ + } +if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +return r; +} + + +/* Logical shift */ + +d10 lsh (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea); + +if (sc > 35) return 0; +if (ea & RSIGN) return (val >> sc); +return ((val << sc) & DMASK); +} + +/* Rotate */ + +d10 rot (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea) % 36; + +if (sc == 0) return val; +if (ea & RSIGN) sc = 36 - sc; +return (((val << sc) | (val >> (36 - sc))) & DMASK); +} + +/* Double word integer instructions */ + +/* Double add - see case table for single add */ + +void dadd (int32 ac, d10 *rs) +{ +d10 r; +int32 p1 = ADDAC (ac, 1); + +AC(p1) = CLRS (AC(p1)) + CLRS (rs[1]); /* add lo */ +r = (AC(ac) + rs[0] + (TSTS (AC(p1))? 1: 0)) & DMASK; /* add hi+cry */ +if (TSTS (AC(ac) & rs[0])) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ + } +else if (!TSTS (AC(ac) | rs[0])) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); /* case 2 */ + } +else if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +AC(ac) = r; +AC(p1) = TSTS (r)? SETS (AC(p1)): CLRS (AC(p1)); +return; +} + +/* Double subtract - see comments for single subtract */ + +void dsub (int32 ac, d10 *rs) +{ +d10 r; +int32 p1 = ADDAC (ac, 1); + +AC(p1) = CLRS (AC(p1)) - CLRS (rs[1]); /* sub lo */ +r = (AC(ac) - rs[0] - (TSTS (AC(p1))? 1: 0)) & DMASK; /* sub hi,borrow */ +if (TSTS (AC(ac) & ~rs[0])) { /* cases 7,8 */ + if (TSTS (r)) SETF (F_C0 | F_C1); /* case 8 */ + else SETF (F_C0 | F_AOV | F_T1); /* case 7 */ + } +else if (!TSTS (AC(ac) | ~rs[0])) { /* cases 1,2 */ + if (TSTS (r)) SETF (F_C1 | F_AOV | F_T1); /* case 2 */ + } +else if (!TSTS (r)) SETF (F_C0 | F_C1); /* cases 3,5 */ +AC(ac) = r; +AC(p1) = (TSTS (r)? SETS (AC(p1)): CLRS (AC(p1))) & DMASK; +return; +} + + +/* Logical shift combined */ + +void lshc (int32 ac, a10 ea) +{ +int32 p1 = ADDAC (ac, 1); +int32 sc = LIT8 (ea); + +if (sc > 71) AC(ac) = AC(p1) = 0; +else if (ea & RSIGN) { + if (sc >= 36) { + AC(p1) = AC(ac) >> (sc - 36); + AC(ac) = 0; + } + else { + AC(p1) = ((AC(p1) >> sc) | (AC(ac) << (36 - sc))) & DMASK; + AC(ac) = AC(ac) >> sc; + } + } +else { + if (sc >= 36) { + AC(ac) = (AC(p1) << (sc - 36)) & DMASK; + AC(p1) = 0; + } + else { + AC(ac) = ((AC(ac) << sc) | (AC(p1) >> (36 - sc))) & DMASK; + AC(p1) = (AC(p1) << sc) & DMASK; + } + } +return; +} + +/* Rotate combined */ + +void rotc (int32 ac, a10 ea) +{ +int32 p1 = ADDAC (ac, 1); +int32 sc = LIT8 (ea) % 72; +d10 t = AC(ac); + +if (sc == 0) return; +if (ea & RSIGN) sc = 72 - sc; +if (sc >= 36) { + AC(ac) = ((AC(p1) << (sc - 36)) | (t >> (72 - sc))) & DMASK; + AC(p1) = ((t << (sc - 36)) | (AC(p1) >> (72 - sc))) & DMASK; + } +else { + AC(ac) = ((t << sc) | (AC(p1) >> (36 - sc))) & DMASK; + AC(p1) = ((AC(p1) << sc) | (t >> (36 - sc))) & DMASK; + } +return; +} + +/* Arithmetic shifts */ + +d10 ash (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea); +d10 sign = TSTS (val); +d10 fill = sign? ONES: 0; +d10 so; + +if (sc == 0) return val; +if (sc > 35) sc = 35; /* cap sc at 35 */ +if (ea & RSIGN) + return (((val >> sc) | (fill << (36 - sc))) & DMASK); +so = val >> (35 - sc); /* bits lost left + sign */ +if (so != (sign? bytemask[sc + 1]: 0)) SETF (F_AOV | F_T1); +return (sign | ((val << sc) & MMASK)); +} + +void ashc (int32 ac, a10 ea) +{ +int32 sc = LIT8 (ea); +int32 p1 = ADDAC (ac, 1); +d10 sign = TSTS (AC(ac)); +d10 fill = sign? ONES: 0; +d10 so; + +if (sc == 0) return; +if (sc > 70) sc = 70; /* cap sc at 70 */ +AC(ac) = CLRS (AC(ac)); /* clear signs */ +AC(p1) = CLRS (AC(p1)); +if (ea & RSIGN) { + if (sc >= 35) { /* right 36..70 */ + AC(p1) = ((AC(ac) >> (sc - 35)) | (fill << (70 - sc))) & DMASK; + AC(ac) = fill; + } + else { + AC(p1) = sign | /* right 1..35 */ + (((AC(p1) >> sc) | (AC(ac) << (35 - sc))) & MMASK); + AC(ac) = ((AC(ac) >> sc) | (fill << (35 - sc))) & DMASK; + } + } +else { + if (sc >= 35) { /* left 36..70 */ + so = AC(p1) >> (70 - sc); /* bits lost left */ + if ((AC(ac) != (sign? MMASK: 0)) || + (so != (sign? bytemask[sc - 35]: 0))) SETF (F_AOV | F_T1); + AC(ac) = sign | ((AC(p1) << (sc - 35)) & MMASK); + AC(p1) = sign; + } + else { + so = AC(ac) >> (35 - sc); /* bits lost left */ + if (so != (sign? bytemask[sc]: 0)) SETF (F_AOV | F_T1); + AC(ac) = sign | + (((AC(ac) << sc) | (AC(p1) >> (35 - sc))) & MMASK); + AC(p1) = sign | ((AC(p1) << sc) & MMASK); + } + } +return; +} + +/* Effective address routines */ + +/* Calculate effective address - used by byte instructions, extended + instructions, and interrupts to get a different mapping context from + the main loop. prv is either EABP_PXCT or MM_CUR. +*/ + +a10 calc_ea (d10 inst, int32 prv) +{ +int32 i, ea, xr; +d10 indrct; + +for (indrct = inst, i = 0; i < ind_max; i++) { + ea = GET_ADDR (indrct); + xr = GET_XR (indrct); + if (xr) ea = (ea + ((a10) XR (xr, prv))) & AMASK; + if (TST_IND (indrct)) indrct = Read (ea, prv); + else break; + } +if (i >= ind_max) ABORT (STOP_IND); +return ea; +} + +/* Calculate I/O effective address. Cases: + - No index or indirect, return addr from instruction + - Index only, index >= 0, return 36b sum of addr + index + - Index only, index <= 0, return 18b sum of addr + index + - Indirect, calculate 18b sum of addr + index, return + entire word fetch (single level) +*/ + +a10 calc_ioea (d10 inst, int32 pflgs) +{ +int32 xr; +a10 ea; + +xr = GET_XR (inst); +ea = GET_ADDR (inst); +if (TST_IND (inst)) { /* indirect? */ + if (xr) ea = (ea + ((a10) XR (xr, MM_EA))) & AMASK; + ea = (a10) Read (ea, MM_EA); + } +else if (xr) { /* direct + idx? */ + ea = ea + ((a10) XR (xr, MM_EA)); + if (TSTS (XR (xr, MM_EA))) ea = ea & AMASK; + } +return ea; +} + +/* Calculate JRSTF effective address. This routine preserves + the left half of the effective address, to be the new flags. +*/ + +d10 calc_jrstfea (d10 inst, int32 pflgs) +{ +int32 i, xr; +d10 mb; + +for (i = 0; i < ind_max; i++) { + mb = inst; + xr = GET_XR (inst); + if (xr) mb = (mb & AMASK) + XR (xr, MM_EA); + if (TST_IND (inst)) inst = Read (((a10) mb) & AMASK, MM_EA); + else break; + } +if (i >= ind_max) ABORT (STOP_IND); +return (mb & DMASK); +} + +/* Byte pointer routines */ + +/* Increment byte pointer - checked against KS10 ucode */ + +void ibp (a10 ea, int32 pflgs) +{ +int32 p, s; +d10 bp; + +bp = ReadM (ea, MM_OPND); /* get byte ptr */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +p = p - s; /* adv P */ +if (p < 0) { /* end of word? */ + bp = (bp & LMASK) | (INCR (bp)); /* incr addr */ + p = (36 - s) & 077; /* reset P */ + } +bp = PUT_P (bp, p); /* store new P */ +Write (ea, bp, MM_OPND); /* store byte ptr */ +return; +} + +/* Load byte */ + +d10 ldb (a10 ea, int32 pflgs) +{ +a10 ba; +int32 p, s; +d10 bp, wd; + +bp = Read (ea, MM_OPND); /* get byte ptr */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EABP); /* get addr of byte */ +wd = Read (ba, MM_BSTK); /* read word */ +wd = (wd >> p); /* align byte */ +wd = wd & bytemask[s]; /* mask to size */ +return wd; +} + +/* Deposit byte - must use read and write to get page fail correct */ + +void dpb (d10 val, a10 ea, int32 pflgs) +{ +a10 ba; +int32 p, s; +d10 bp, wd, mask; + +bp = Read (ea, MM_OPND); /* get byte ptr */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EABP); /* get addr of byte */ +wd = Read (ba, MM_BSTK); /* read word */ +mask = bytemask[s] << p; /* shift mask, val */ +val = val << p; +wd = (wd & ~mask) | (val & mask); /* insert byte */ +Write (ba, wd & DMASK, MM_BSTK); +return; +} + +/* Adjust byte pointer - checked against KS10 ucode + The KS10 divide checks if the bytes per word = 0, which is a simpler + formulation of the processor reference manual check. +*/ + +void adjbp (int32 ac, a10 ea, int32 pflgs) +{ +int32 p, s; +d10 bp, newby, left, byadj, bywrd, val, wdadj; + +val = AC(ac); /* get adjustment */ +bp = Read (ea, MM_OPND); /* get byte pointer */ +p = GET_P (bp); /* get p */ +s = GET_S (bp); /* get s */ +if (s) { + left = (36 - p) / s; /* bytes to left of p */ + bywrd = left + (p / s); /* bytes per word */ + if (bywrd == 0) { /* zero bytes? */ + SETF (F_AOV | F_T1 | F_DCK); /* set flags */ + return; /* abort operation */ + } + newby = left + SXT (val); /* adjusted byte # */ + wdadj = newby / bywrd; /* word adjustment */ + byadj = (newby >= 0)? newby % bywrd: -((-newby) % bywrd); + if (byadj <= 0) { + byadj = byadj + bywrd; /* make adj positive */ + wdadj = wdadj - 1; + } + p = (36 - ((int32) byadj) * s) - ((36 - p) % s); /* new p */ + bp = (PUT_P (bp, p) & LMASK) | ((bp + wdadj) & RMASK); + } +AC(ac) = bp; +return; +} + +/* Block transfer - checked against KS10 ucode + The KS10 uses instruction specific recovery code in page fail + to set the AC properly for restart. Lacking this mechanism, + the simulator must test references in advance. + The clocking test guarantees forward progress under single step. +*/ + +void blt (int32 ac, a10 ea, int32 pflgs) +{ +a10 srca = (a10) LRZ (AC(ac)); +a10 dsta = (a10) RRZ (AC(ac)); +a10 lnt = ea - dsta + 1; +d10 srcv; +int32 flg, t; + +AC(ac) = XWD (srca + lnt, dsta + lnt); +for (flg = 0; dsta <= ea; flg++) { /* loop */ + if (flg && (t = test_int ())) { /* timer event? */ + AC(ac) = XWD (srca, dsta); /* AC for intr */ + ABORT (t); + } + if (AccViol (srca & AMASK, MM_BSTK, PTF_RD)) { /* src access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + Read (srca & AMASK, MM_BSTK); /* force trap */ + } + if (AccViol (dsta & AMASK, MM_OPND, PTF_WR)) { /* dst access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + ReadM (dsta & AMASK, MM_OPND); /* force trap */ + } + srcv = Read (srca & AMASK, MM_BSTK); /* read */ + Write (dsta & AMASK, srcv, MM_OPND); /* write */ + srca = srca + 1; /* incr addr */ + dsta = dsta + 1; + } +return; +} + +/* I/O block transfers - byte to Unibus (0) and Unibus to byte (1) */ + +#define BYTE1 0776000000000 +#define BYTE2 0001774000000 +#define BYTE3 0000003770000 +#define BYTE4 0000000007760 +/* unused 0000000000017 */ + +void bltu (int32 ac, a10 ea, int32 pflgs, int dir) +{ +a10 srca = (a10) LRZ (AC(ac)); +a10 dsta = (a10) RRZ (AC(ac)); +a10 lnt = ea - dsta + 1; +d10 srcv, dstv; +int32 flg, t; + +AC(ac) = XWD (srca + lnt, dsta + lnt); +for (flg = 0; dsta <= ea; flg++) { /* loop */ + if (flg && (t = test_int ())) { /* timer event? */ + AC(ac) = XWD (srca, dsta); /* AC for intr */ + ABORT (t); + } + if (AccViol (srca & AMASK, MM_BSTK, PTF_RD)) { /* src access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + Read (srca & AMASK, MM_BSTK); /* force trap */ + } + if (AccViol (dsta & AMASK, MM_OPND, PTF_WR)) { /* dst access viol? */ + AC(ac) = XWD (srca, dsta); /* AC for page fail */ + ReadM (dsta & AMASK, MM_OPND); /* force trap */ + } + srcv = Read (srca & AMASK, MM_BSTK); /* read */ + if (dir) dstv = ((srcv << 10) & BYTE1) | ((srcv >> 6) & BYTE2) | + ((srcv << 12) & BYTE3) | ((srcv >> 4) & BYTE4); + else dstv = ((srcv & BYTE1) >> 10) | ((srcv & BYTE2) << 6) | + ((srcv & BYTE3) >> 12) | ((srcv & BYTE4) << 4); + Write (dsta & AMASK, dstv, MM_OPND); /* write */ + srca = srca + 1; /* incr addr */ + dsta = dsta + 1; + } +return; +} + +/* Utility routine to test for I/O event and interrupt */ + +int32 test_int (void) +{ +int32 t; + +if (sim_interval <= 0) { /* check queue */ + if (t = sim_process_event ()) return t; /* IO event? */ + if (pi_eval ()) return (INTERRUPT); /* interrupt? */ + } +else sim_interval--; /* count clock */ +return 0; +} + +/* Adjust stack pointer + + The reference manual says to trap on: + 1) E < 0, left changes from + to - + 2) E >= 0, left changes from - to + + This is the same as trap on: + 1) E and left result have same signs + 2) initial value and left result have different signs + */ + +d10 adjsp (d10 val, a10 ea) +{ +d10 imm = ea; +d10 left, right; + +left = ADDL (val, imm); +right = ADDR (val, imm); +if (TSTS ((val ^ left) & (~left ^ RLZ (imm)))) SETF (F_T2); +return (left | right); +} + +/* Jump if find first ones + Takes advantage of 7 bit find first table for priority interrupts. +*/ + +int32 jffo (d10 val) +{ +int32 i, by; + +if ((val & DMASK) == 0) return 0; +for (i = 0; i <= 28; i = i + 7) { /* scan five bytes */ + by = (int32) ((val >> (29 - i)) & 0177); + if (by) return (pi_m2lvl[by] + i - 1); + } +return 35; /* must be bit 35 */ +} + +/* Circulate - ITS only instruction + + Bits rotated out of AC are rotated into the opposite end of AC+1 - why? + No attempt is made to optimize this instruction. +*/ + +void circ (int32 ac, int32 ea) +{ +int32 sc = LIT8 (ea) % 72; +int32 p1 = ADDAC (ac,1); +int32 i; +d10 val; + +if (sc == 0) return; /* any shift? */ +if (ea & RSIGN) sc = 72 - sc; /* if right, make left */ +for (i = 0; i < sc; i++) { /* one bit at a time */ + val = TSTS (AC(ac)); /* shift out */ + AC(ac) = ((AC(ac) << 1) | (AC(p1) & 1)) & DMASK; + AC(p1) = (AC(p1) >> 1) | val; /* shift in */ + } +return; +} + +/* Arithmetic processor (APR) + + The APR subsystem includes miscellaneous interrupts that are individually + maskable but which interrupt on a single, selectable level + + Instructions for the arithmetic processor: + APRID read system id + WRAPR (CONO APR) write system flags + RDAPR (CONI APR) read system flags + (CONSO APR) test system flags + (CONSZ APR) test system flags +*/ + +t_bool aprid (a10 ea, int32 prv) +{ +Write (ea, (Q_ITS)? UC_AIDITS: UC_AIDDEC, prv); +return FALSE; +} + +/* Checked against KS10 ucode */ + +t_bool wrapr (a10 ea, int32 prv) +{ +int32 bits = APR_GETF (ea); + +apr_lvl = ea & APR_M_LVL; +if (ea & APR_SENB) apr_enb = apr_enb | bits; /* set enables? */ +if (ea & APR_CENB) apr_enb = apr_enb & ~bits; /* clear enables? */ +if (ea & APR_CFLG) apr_flg = apr_flg & ~bits; /* clear flags? */ +if (ea & APR_SFLG) apr_flg = apr_flg | bits; /* set flags? */ +if (apr_flg & APRF_ITC) { /* interrupt console? */ + fe_intr (); /* explicit callout */ + apr_flg = apr_flg & ~APRF_ITC; /* interrupt clears */ + } +pi_eval (); /* eval pi system */ +return FALSE; +} + +t_bool rdapr (a10 ea, int32 prv) +{ +Write (ea, (d10) APRWORD, prv); +return FALSE; +} + +t_bool czapr (a10 ea, int32 prv) +{ +return ((APRHWORD & ea)? FALSE: TRUE); +} + +t_bool coapr (a10 ea, int32 prv) +{ +return ((APRHWORD & ea)? TRUE: FALSE); +} + +/* Routine to change the processor flags, called from JRST, MUUO, interrupt. + If jrst is TRUE, must munge flags for executive security. + Because the KS10 lacks the public flag, these checks are simplified. +*/ + +void set_newflags (d10 newf, t_bool jrst) +{ +int32 fl = (int32) LRZ (newf); + +if (jrst && TSTF (F_USR)) { /* if in user now */ + fl = fl | F_USR; /* can't clear user */ + if (!TSTF (F_UIO)) fl = fl & ~F_UIO; /* if !UIO, can't set */ + } +if (Q_ITS && (fl & F_1PR)) { /* ITS 1-proceed? */ + its_1pr = 1; /* set flag */ + fl = fl & ~F_1PR; /* vanish bit */ + } +flags = fl & F_MASK; /* set new flags */ +set_dyn_ptrs (); /* set new ptrs */ +return; +} + +/* Priority interrupt system (PI) + + The priority interrupt system has three sources of requests + (pi_apr) system flags - synthesized on the fly + (pi_ioq) I/O interrupts - synthesized on the fly + pi_prq program requests + APR and I/O requests are masked with the PI enable mask; the program + requests are not. If priority interrupts are enabled, and there is + a request at a level exceeding the currently active level, then an + interrupt occurs. + + Instructions for the priority interrupt system: + WRPI (CONO PI) write pi system + RDPI (CONI PI) read pi system + (CONSO PI) test pi system + (CONSZ PI) test pi system + + Routines for the priority interrupt system: + pi_eval return level number of highest interrupt + pi_dismiss dismiss highest outstanding interrupt + + Checked against KS10 ucode - KS10 UUO's if <18:21> are non-zero +*/ + +t_bool wrpi (a10 ea, int32 prv) +{ +int32 lvl = ea & PI_M_LVL; + +if (ea & PI_INIT) pi_on = pi_enb = pi_act = pi_prq = 0; +if (ea & PI_CPRQ) pi_prq = pi_prq & ~lvl; /* clear prog reqs? */ +if (ea & PI_SPRQ) pi_prq = pi_prq | lvl; /* set prog reqs? */ +if (ea & PI_SENB) pi_enb = pi_enb | lvl; /* enable levels? */ +if (ea & PI_CENB) pi_enb = pi_enb & ~lvl; /* disable levels? */ +if (ea & PI_SON) pi_on = 1; /* enable pi? */ +if (ea & PI_CON) pi_on = 0; /* disable pi? */ +pi_eval (); /* eval pi system */ +return FALSE; +} + +t_bool rdpi (a10 ea, int32 prv) +{ +Write (ea, (d10) PIWORD, prv); +return FALSE; +} + +t_bool czpi (a10 ea, int32 prv) +{ +return ((PIHWORD & ea)? FALSE: TRUE); +} + +t_bool copi (a10 ea, int32 prv) +{ +return ((PIHWORD & ea)? TRUE: FALSE); +} + +/* Priority interrupt evaluation + + The Processor Reference Manuals says that program interrupt + requests occur whether the corresponding level is enabled or + not. However, the KS10, starting with microcode edit 47, + masked program requests under the enable mask, just like APR + and I/O requests. This is not formally documented but appears + to be necessary for the TOPS20 console port to run correclty. +*/ + +int32 pi_eval (void) +{ +int32 reqlvl, actlvl; +extern int32 pi_ub_eval (); + +qintr = 0; +if (pi_on) { + pi_apr = (apr_flg & apr_enb)? pi_l2bit[apr_lvl]: 0; + pi_ioq = pi_ub_eval (); + reqlvl = pi_m2lvl[((pi_apr | pi_ioq | pi_prq) & pi_enb)]; + actlvl = pi_m2lvl[pi_act]; + if ((actlvl == 0) || (reqlvl < actlvl)) qintr = reqlvl; + } +return qintr; +} + +void pi_dismiss (void) +{ +pi_act = pi_act & ~pi_l2bit[pi_m2lvl[pi_act]]; /* clr left most bit */ +pi_eval (); /* eval pi system */ +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +flags = 0; /* clear flags */ +its_1pr = 0; /* clear 1-proceed */ +ebr = ubr = 0; /* clear paging */ +pi_enb = pi_act = pi_prq = 0; /* clear PI */ +apr_enb = apr_flg = apr_lvl = 0; /* clear APR */ +pcst = 0; /* clear PC samp */ +rlog = 0; /* clear reg log */ +hsb = (Q_ITS)? UC_HSBITS: UC_HSBDEC; /* set HSB */ +set_dyn_ptrs (); +set_ac_display (ac_cur); +pi_eval (); +if (M == NULL) M = (d10 *) calloc (MAXMEMSIZE, sizeof (d10)); +if (M == NULL) return SCPE_MEM; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr ea, UNIT *uptr, int32 sw) +{ +if (vptr == NULL) return SCPE_ARG; +if (ea < AC_NUM) *vptr = AC(ea) & DMASK; +else { + if (sw & SWMASK ('V')) { + ea = conmap (ea, PTF_CON, sw); + if (ea >= MAXMEMSIZE) return SCPE_REL; + } + if (ea >= MEMSIZE) return SCPE_NXM; + *vptr = M[ea] & DMASK; + } +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr ea, UNIT *uptr, int32 sw) +{ +if (ea < AC_NUM) AC(ea) = val & DMASK; +else { + if (sw & SWMASK ('V')) { + ea = conmap (ea, PTF_CON | PTF_WR, sw); + if (ea >= MAXMEMSIZE) return SCPE_REL; + } + if (ea >= MEMSIZE) return SCPE_NXM; + M[ea] = val & DMASK; + } +return SCPE_OK; +} + +/* Set current AC pointers for SCP */ + +void set_ac_display (d10 *acbase) +{ +extern REG *find_reg (char *cptr, char **optr, DEVICE *dptr); +REG *rptr; +int i; + +rptr = find_reg ("AC0", NULL, &cpu_dev); +if (rptr == NULL) return; +for (i = 0; i < AC_NUM; i++, rptr++) rptr->loc = (void *) (acbase + i); +return; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +t_value sim_eval; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC AC EA IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + fprintf (st, "%06o ", h->pc & AMASK); + fprint_val (st, h->ac, 8, 36, PV_RZRO); + fputs (" ", st); + fprintf (st, "%06o ", h->ea); + sim_eval = h->ir; + if ((fprint_sym (st, h->pc & AMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) { + fputs ("(undefined) ", st); + fprint_val (st, h->ir, 8, 36, PV_RZRO); + } + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/PDP10/pdp10_defs.h b/PDP10/pdp10_defs.h new file mode 100644 index 0000000..7bd3585 --- /dev/null +++ b/PDP10/pdp10_defs.h @@ -0,0 +1,771 @@ +/* pdp10_defs.h: PDP-10 simulator definitions + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 01-Feb-07 RMS Added CD support + 29-Oct-06 RMS Added clock coscheduling function + 29-Dec-03 RMS Added Q18 definition for PDP11 compatibility + 19-May-03 RMS Revised for new conditional compilation scheme + 09-Jan-03 RMS Added DEUNA/DELUA support + 29-Sep-02 RMS Added variable vector, RX211 support + 22-Apr-02 RMS Removed magtape record length error + 20-Jan-02 RMS Added multiboard DZ11 support + 23-Oct-01 RMS New IO page address constants + 19-Oct-01 RMS Added DZ11 definitions + 07-Sep-01 RMS Revised for PDP-11 multi-level interrupts + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 29-Aug-01 RMS Corrected models and dates (found by Lars Brinkhoff) + 01-Jun-01 RMS Updated DZ11 vector definitions + 19-May-01 RMS Added workaround for TOPS-20 V4.1 boot bug +*/ + +#ifndef _PDP10_DEFS_H_ +#define _PDP10_DEFS_H_ 0 + +#ifndef VM_PDP10 +#define VM_PDP10 0 +#endif + +#include "sim_defs.h" /* simulator defns */ + +/* Digital Equipment Corporation's 36b family had six implementations: + + name mips comments + + PDP-6 0.25 Original 36b implementation, 1964 + KA10 0.38 First PDP-10, flip chips, 1967 + KI10 0.72 First paging system, flip chip + MSI, 1972 + KL10 1.8 First ECL system, ECL 10K, 1975 + KL10B 1.8 Expanded addressing, ECL 10K, 1978 + KS10 0.3 Last 36b system, 2901 based, 1979 + + In addition, it ran four major (incompatible) operating systems: + + name company comments + + TOPS-10 DEC Original timesharing system + ITS MIT "Incompatible Timesharing System" + TENEX BBN ARPA-sponsored, became + TOPS-20 DEC Commercial version of TENEX + + All of the implementations differ from one another, in instruction set, + I/O structure, and memory management. Further, each of the operating + systems customized the microcode of the paging systems (KI10, KL10, KS10) + for additional instructions and specialized memory management. As a + result, there is no "reference implementation" for the 36b family that + will run all programs and all operating systems. The conditionalization + and generality needed to support the full matrix of models and operating + systems, and to support 36b hardware on 32b data types, is beyond the + scope of this project. + + Instead, this simulator emulates one model -- the KS10. It has the best + documentation and allows reuse of some of the Unibus peripheral emulators + written for the PDP-11 simulator. Further, the simulator requires that + the underlying compiler support 64b integer data types, allowing 36b data + to be maintained in a single data item. Lastly, the simulator implements + the maximum memory size, so that NXM's never happen. +*/ + +/* Data types */ + +typedef int32 a10; /* PDP-10 addr (30b) */ +typedef t_int64 d10; /* PDP-10 data (36b) */ + +/* Abort codes, used to sort out longjmp's back to the main loop + Codes > 0 are simulator stop codes + Codes < 0 are internal aborts + Code = 0 stops execution for an interrupt check +*/ + +#define STOP_HALT 1 /* halted */ +#define STOP_IBKPT 2 /* breakpoint */ +#define STOP_ILLEG 3 /* illegal instr */ +#define STOP_ILLINT 4 /* illegal intr inst */ +#define STOP_PAGINT 5 /* page fail in intr */ +#define STOP_ZERINT 6 /* zero vec in intr */ +#define STOP_NXMPHY 7 /* nxm on phys ref */ +#define STOP_IND 8 /* indirection loop */ +#define STOP_XCT 9 /* XCT loop */ +#define STOP_ILLIOC 10 /* invalid UBA num */ +#define STOP_ASTOP 11 /* address stop */ +#define STOP_UNKNOWN 12 /* unknown stop */ +#define PAGE_FAIL -1 /* page fail */ +#define INTERRUPT -2 /* interrupt */ +#define ABORT(x) longjmp (save_env, (x)) /* abort */ +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +/* Return codes from eXTEND */ + +#define XT_MUUO 0 /* invalid operation */ +#define XT_SKIP 1 /* skip return */ +#define XT_NOSK 2 /* no skip return */ + +/* Operating system flags, kept in cpu_unit.flags */ + +#define UNIT_V_ITS (UNIT_V_UF) /* ITS */ +#define UNIT_V_T20 (UNIT_V_UF + 1) /* TOPS-20 */ +#define UNIT_V_KLAD (UNIT_V_UF + 2) /* diagnostics */ +#define UNIT_ITS (1 << UNIT_V_ITS) +#define UNIT_T20 (1 << UNIT_V_T20) +#define UNIT_KLAD (1 << UNIT_V_KLAD) +#define Q_T10 ((cpu_unit.flags & (UNIT_ITS|UNIT_T20|UNIT_KLAD)) == 0) +#define Q_ITS (cpu_unit.flags & UNIT_ITS) +#define Q_T20 (cpu_unit.flags & UNIT_T20) +#define Q_KLAD (cpu_unit.flags & UNIT_KLAD) +#define Q_IDLE (sim_idle_enab) + +/* Architectural constants */ + +#define PASIZE 20 /* phys addr width */ +#define MAXMEMSIZE (1 << PASIZE) /* maximum memory */ +#define PAMASK ((1 << PASIZE) - 1) +#define MEMSIZE MAXMEMSIZE /* fixed, KISS */ +#define MEM_ADDR_NXM(x) ((x) >= MEMSIZE) +#define VASIZE 18 /* virtual addr width */ +#define AMASK ((1 << VASIZE) - 1) /* virtual addr mask */ +#define LMASK 0777777000000 /* left mask */ +#define LSIGN 0400000000000 /* left sign */ +#define RMASK 0000000777777 /* right mask */ +#define RSIGN 0000000400000 /* right sign */ +#define DMASK 0777777777777 /* data mask */ +#define SIGN 0400000000000 /* sign */ +#define MMASK 0377777777777 /* magnitude mask */ +#define ONES 0777777777777 +#define MAXPOS 0377777777777 +#define MAXNEG 0400000000000 + +/* Instruction format */ + +#define INST_V_OP 27 /* opcode */ +#define INST_M_OP 0777 +#define INST_V_DEV 26 +#define INST_M_DEV 0177 /* device */ +#define INST_V_AC 23 /* AC */ +#define INST_M_AC 017 +#define INST_V_IND 22 /* indirect */ +#define INST_IND (1 << INST_V_IND) +#define INST_V_XR 18 /* index */ +#define INST_M_XR 017 +#define OP_JRST 0254 /* JRST */ +#define AC_XPCW 07 /* XPCW */ +#define OP_JSR 0264 /* JSR */ +#define GET_OP(x) ((int32) (((x) >> INST_V_OP) & INST_M_OP)) +#define GET_DEV(x) ((int32) (((x) >> INST_V_DEV) & INST_M_DEV)) +#define GET_AC(x) ((int32) (((x) >> INST_V_AC) & INST_M_AC)) +#define TST_IND(x) ((x) & INST_IND) +#define GET_XR(x) ((int32) (((x) >> INST_V_XR) & INST_M_XR)) +#define GET_ADDR(x) ((a10) ((x) & AMASK)) + +/* Byte pointer format */ + +#define BP_V_P 30 /* position */ +#define BP_M_P 077 +#define BP_P 0770000000000 +#define BP_V_S 24 /* size */ +#define BP_M_S 077 +#define BP_S 0007700000000 +#define GET_P(x) ((int32) (((x) >> BP_V_P) & BP_M_P)) +#define GET_S(x) ((int32) (((x) >> BP_V_S) & BP_M_S)) +#define PUT_P(b,x) (((b) & ~BP_P) | ((((t_int64) (x)) & BP_M_P) << BP_V_P)) + +/* Flags (stored in their own halfword) */ + +#define F_V_AOV 17 /* arithmetic ovflo */ +#define F_V_C0 16 /* carry 0 */ +#define F_V_C1 15 /* carry 1 */ +#define F_V_FOV 14 /* floating ovflo */ +#define F_V_FPD 13 /* first part done */ +#define F_V_USR 12 /* user mode */ +#define F_V_UIO 11 /* user I/O mode */ +#define F_V_PUB 10 /* public mode */ +#define F_V_AFI 9 /* addr fail inhibit */ +#define F_V_T2 8 /* trap 2 */ +#define F_V_T1 7 /* trap 1 */ +#define F_V_FXU 6 /* floating exp unflo */ +#define F_V_DCK 5 /* divide check */ +#define F_AOV (1 << F_V_AOV) +#define F_C0 (1 << F_V_C0) +#define F_C1 (1 << F_V_C1) +#define F_FOV (1 << F_V_FOV) +#define F_FPD (1 << F_V_FPD) +#define F_USR (1 << F_V_USR) +#define F_UIO (1 << F_V_UIO) +#define F_PUB (1 << F_V_PUB) +#define F_AFI (1 << F_V_AFI) +#define F_T2 (1 << F_V_T2) +#define F_T1 (1 << F_V_T1) +#define F_TR (F_T1 | F_T2) +#define F_FXU (1 << F_V_FXU) +#define F_DCK (1 << F_V_DCK) +#define F_1PR (F_AFI) /* ITS: 1-proceed */ +#define F_MASK 0777740 /* all flags */ +#define SETF(x) flags = flags | (x) +#define CLRF(x) flags = flags & ~(x) +#define TSTF(x) (flags & (x)) +#define GET_TRAPS(x) (((x) & (F_T2 | F_T1)) >> F_V_T1) + +/* Priority interrupt system */ + +#define PI_CPRQ 020000 /* drop prog req */ +#define PI_INIT 010000 /* clear pi system */ +#define PI_SPRQ 004000 /* set prog req */ +#define PI_SENB 002000 /* set enables */ +#define PI_CENB 001000 /* clear enables */ +#define PI_CON 000400 /* turn off pi system */ +#define PI_SON 000200 /* turn on pi system */ +#define PI_M_LVL 000177 /* level mask */ +#define PI_V_PRQ 18 /* in CONI */ +#define PI_V_ACT 8 +#define PI_V_ON 7 +#define PI_V_ENB 0 + +/* Arithmetic processor flags */ + +#define APR_SENB 0100000 /* set enable */ +#define APR_CENB 0040000 /* clear enable */ +#define APR_CFLG 0020000 /* clear flag */ +#define APR_SFLG 0010000 /* set flag */ +#define APR_IRQ 0000010 /* int request */ +#define APR_M_LVL 0000007 /* pi level */ +#define APR_V_FLG 4 /* system flags */ +#define APR_M_FLG 0377 +#define APRF_ITC (002000 >> APR_V_FLG) /* int console flag */ +#define APRF_NXM (000400 >> APR_V_FLG) /* nxm flag */ +#define APRF_TIM (000040 >> APR_V_FLG) /* timer request */ +#define APRF_CON (000020 >> APR_V_FLG) /* console int */ +#define APR_GETF(x) (((x) >> APR_V_FLG) & APR_M_FLG) + +/* Virtual address, DEC paging */ + +#define PAG_V_OFF 0 /* offset - must be 0 */ +#define PAG_N_OFF 9 /* page offset width */ +#define PAG_SIZE 01000 /* page offset size */ +#define PAG_M_OFF 0777 /* mask for offset */ +#define PAG_V_PN PAG_N_OFF /* page number */ +#define PAG_N_PPN (PASIZE - PAG_N_OFF) /* phys pageno width */ +#define PAG_M_PPN 03777 /* phys pageno mask */ +#define PAG_PPN 03777000 +#define PAG_N_VPN (VASIZE - PAG_N_OFF) /* virt pageno width */ +#define PAG_M_VPN 0777 /* virt pageno mask */ +#define PAG_VPN 0777000 +#define PAG_GETOFF(x) ((x) & PAG_M_OFF) +#define PAG_GETVPN(x) (((x) >> PAG_V_PN) & PAG_M_VPN) +#define PAG_XPTEPA(p,x) (((p) + PAG_GETOFF (x)) & PAMASK) +#define PAG_PTEPA(p,x) (((((int32) (p)) & PTE_PPMASK) << PAG_V_PN) + PAG_GETOFF (x)) + +/* Page table entry, TOPS-10 paging */ + +#define PTE_T10_A 0400000 /* T10: access */ +#define PTE_T10_P 0200000 /* T10: public */ +#define PTE_T10_W 0100000 /* T10: writeable */ +#define PTE_T10_S 0040000 /* T10: software */ +#define PTE_T10_C 0020000 /* T10: cacheable */ +#define PTE_PPMASK PAG_M_PPN + +/* Page table entry, TOPS-20 paging */ + +#define PTE_T20_V_TYP 33 /* T20: pointer type */ +#define PTE_T20_M_TYP 07 +#define T20_NOA 0 /* no access */ +#define T20_IMM 1 /* immediate */ +#define T20_SHR 2 /* shared */ +#define T20_IND 3 /* indirect */ +#define PTE_T20_W 0020000000000 /* T20: writeable */ +#define PTE_T20_C 0004000000000 /* T20: cacheable */ +#define PTE_T20_STM 0000077000000 /* T20: storage medium */ +#define PTE_T20_V_PMI 18 /* page map index */ +#define PTE_T20_M_PMI 0777 +#define T20_GETTYP(x) ((int32) (((x) >> PTE_T20_V_TYP) & PTE_T20_M_TYP)) +#define T20_GETPMI(x) ((int32) (((x) >> PTE_T20_V_PMI) & PTE_T20_M_PMI)) + +/* CST entry, TOPS-20 paging */ + +#define CST_AGE 0770000000000 /* age field */ +#define CST_M 0000000000001 /* modified */ + +/* Page fail word, DEC paging */ + +#define PF_USER 0400000000000 /* user mode */ +#define PF_HARD 0200000000000 /* nx I/O reg */ +#define PF_NXM 0370000000000 /* nx memory */ +#define PF_T10_A 0100000000000 /* T10: pte A bit */ +#define PF_T10_W 0040000000000 /* T10: pte W bit */ +#define PF_T10_S 0020000000000 /* T10: pte S bit */ +#define PF_T20_DN 0100000000000 /* T20: eval done */ +#define PF_T20_M 0040000000000 /* T20: modified */ +#define PF_T20_W 0020000000000 /* T20: writeable */ +#define PF_WRITE 0010000000000 /* write reference */ +#define PF_PUB 0004000000000 /* pte public bit */ +#define PF_C 0002000000000 /* pte C bit */ +#define PF_VIRT 0001000000000 /* pfl: virt ref */ +#define PF_NXMP 0001000000000 /* nxm: phys ref */ +#define PF_IO 0000200000000 /* I/O reference */ +#define PF_BYTE 0000020000000 /* I/O byte ref */ + +/* Virtual address, ITS paging */ + +#define ITS_V_OFF 0 /* offset - must be 0 */ +#define ITS_N_OFF 10 /* page offset width */ +#define ITS_SIZE 02000 /* page offset size */ +#define ITS_M_OFF 01777 /* mask for offset */ +#define ITS_V_PN ITS_N_OFF /* page number */ +#define ITS_N_PPN (PASIZE- ITS_N_OFF) /* phys pageno width */ +#define ITS_M_PPN 01777 /* phys pageno mask */ +#define ITS_PPN 03776000 +#define ITS_N_VPN (VASIZE - ITS_N_OFF) /* virt pageno width */ +#define ITS_M_VPN 0377 /* virt pageno mask */ +#define ITS_VPN 0776000 +#define ITS_GETVPN(x) (((x) >> ITS_V_PN) & ITS_M_VPN) + +/* Page table entry, ITS paging */ + +#define PTE_ITS_V_ACC 16 /* access field */ +#define PTE_ITS_M_ACC 03 +#define ITS_ACC_NO 0 /* no access */ +#define ITS_ACC_RO 1 /* read only */ +#define ITS_ACC_RWF 2 /* read-write first */ +#define ITS_ACC_RW 3 /* read write */ +#define PTE_ITS_AGE 0020000 /* age */ +#define PTE_ITS_C 0010000 /* cacheable */ +#define PTE_ITS_PPMASK ITS_M_PPN +#define ITS_GETACC(x) (((x) >> PTE_ITS_V_ACC) & PTE_ITS_M_ACC) + +/* Page fail word, ITS paging */ + +#define PF_ITS_WRITE 0010000000000 /* write reference */ +#define PF_ITS_V_ACC 28 /* access from PTE */ + +/* Page table fill operations */ + +#define PTF_RD 0 /* read check */ +#define PTF_WR 1 /* write check */ +#define PTF_MAP 2 /* map instruction */ +#define PTF_CON 4 /* console access */ + +/* User base register */ + +#define UBR_SETACB 0400000000000 /* set AC blocks */ +#define UBR_SETUBR 0100000000000 /* set UBR */ +#define UBR_V_CURAC 27 /* current AC block */ +#define UBR_V_PRVAC 24 /* previous AC block */ +#define UBR_M_AC 07 +#define UBR_ACBMASK 0007700000000 +#define UBR_V_UBR 0 /* user base register */ +#define UBR_N_UBR 11 +#define UBR_M_UBR 03777 +#define UBR_UBRMASK 0000000003777 +#define UBR_GETCURAC(x) ((int32) (((x) >> UBR_V_CURAC) & UBR_M_AC)) +#define UBR_GETPRVAC(x) ((int32) (((x) >> UBR_V_PRVAC) & UBR_M_AC)) +#define UBR_GETUBR(x) ((int32) (((x) >> UBR_V_UBR) & PAG_M_PPN)) +#define UBRWORD (ubr | UBR_SETACB | UBR_SETUBR) + +/* Executive base register */ + +#define EBR_V_T20P 14 /* TOPS20 paging */ +#define EBR_T20P (1u << EBR_V_T20P) +#define EBR_V_PGON 13 /* enable paging */ +#define EBR_PGON (1u << EBR_V_PGON) +#define EBR_V_EBR 0 /* exec base register */ +#define EBR_N_EBR 11 +#define EBR_M_EBR 03777 +#define EBR_MASK (EBR_T20P | EBR_PGON | (EBR_M_EBR << EBR_V_EBR)) +#define EBR_GETEBR(x) ((int32) (((x) >> EBR_V_EBR) & PAG_M_PPN)) +#define PAGING (ebr & EBR_PGON) +#define T20PAG (ebr & EBR_T20P) + +/* AC and mapping contexts + + There are only two real contexts for selecting the AC block and + the memory map: current and previous. However, PXCT allows the + choice of current versus previous to be made selectively for + various parts of an instruction. The PXCT flags are kept in a + dynamic CPU variable. +*/ + +#define EA_PXCT 010 /* eff addr calc */ +#define OPND_PXCT 004 /* operand, bdst */ +#define EABP_PXCT 002 /* bp eff addr calc */ +#define BSTK_PXCT 001 /* stk, bp op, bsrc */ +#define XSRC_PXCT 002 /* extend source */ +#define XDST_PXCT 001 /* extend destination */ +#define MM_CUR 000 /* current context */ +#define MM_EA (pflgs & EA_PXCT) +#define MM_OPND (pflgs & OPND_PXCT) +#define MM_EABP (pflgs & EABP_PXCT) +#define MM_BSTK (pflgs & BSTK_PXCT) + +/* Accumulator access. The AC blocks are kept in array acs[AC_NBLK * AC_NUM]. + Two pointers are provided to the bases of the current and previous blocks. + Macro AC selects the current AC block; macro XR selects current or previous, + depending on whether the selected bit in the "pxct in progress" flag is set. +*/ + +#define AC_NUM 16 /* # AC's/block */ +#define AC_NBLK 8 /* # AC blocks */ +#define AC(r) (ac_cur[r]) /* AC select current */ +#define XR(r,prv) ((prv)? ac_prv[r]: ac_cur[r]) /* AC select context */ +#define ADDAC(x,i) (((x) + (i)) & INST_M_AC) +#define P1 ADDAC (ac, 1) + +/* User process table entries */ + +#define UPT_T10_UMAP 0000 /* T10: user map */ +#define UPT_T10_X340 0400 /* T10: exec 340-377 */ +#define UPT_TRBASE 0420 /* trap base */ +#define UPT_MUUO 0424 /* MUUO block */ +#define UPT_MUPC 0425 /* caller's PC */ +#define UPT_T10_CTX 0426 /* T10: context */ +#define UPT_T20_UEA 0426 /* T20: address */ +#define UPT_T20_CTX 0427 /* T20: context */ +#define UPT_ENPC 0430 /* MUUO new PC, exec */ +#define UPT_1PO 0432 /* ITS 1-proc: old PC */ +#define UPT_1PN 0433 /* ITS 1-proc: new PC */ +#define UPT_UNPC 0434 /* MUUO new PC, user */ +#define UPT_NPCT 1 /* PC offset if trap */ +#define UPT_T10_PAG 0500 /* T10: page fail blk */ +#define UPT_T20_PFL 0500 /* T20: page fail wd */ +#define UPT_T20_OFL 0501 /* T20: flags */ +#define UPT_T20_OPC 0502 /* T20: old PC */ +#define UPT_T20_NPC 0503 /* T20: new PC */ +#define UPT_T20_SCTN 0540 /* T20: section 0 ptr */ + +/* Exec process table entries */ + +#define EPT_PIIT 0040 /* PI interrupt table */ +#define EPT_UBIT 0100 /* Unibus intr table */ +#define EPT_T10_X400 0200 /* T10: exec 400-777 */ +#define EPT_TRBASE 0420 /* trap base */ +#define EPT_ITS_PAG 0440 /* ITS: page fail blk */ +#define EPT_T20_SCTN 0540 /* T20: section 0 ptr */ +#define EPT_T10_X000 0600 /* T10: exec 0 - 337 */ + +/* Microcode constants */ + +#define UC_INHCST 0400000000000 /* inhibit CST update */ +#define UC_UBABLT 0040000000000 /* BLTBU and BLTUB */ +#define UC_KIPAGE 0020000000000 /* "KI" paging */ +#define UC_KLPAGE 0010000000000 /* "KL" paging */ +#define UC_VERDEC (0130 << 18) /* ucode version */ +#define UC_VERITS (262u << 18) +#define UC_SERDEC 4097 /* serial number */ +#define UC_SERITS 1729 +#define UC_AIDDEC (UC_INHCST | UC_UBABLT | UC_KIPAGE | UC_KLPAGE | \ + UC_VERDEC | UC_SERDEC) +#define UC_AIDITS (UC_KIPAGE | UC_VERITS | UC_SERITS) +#define UC_HSBDEC 0376000 /* DEC initial HSB */ +#define UC_HSBITS 0000500 /* ITS initial HSB */ + +/* Front end communications region */ + +#define FE_SWITCH 030 /* halt switch */ +#define FE_KEEPA 031 /* keep alive */ +#define FE_CTYIN 032 /* console in */ +#define FE_CTYOUT 033 /* console out */ +#define FE_KLININ 034 /* KLINIK in */ +#define FE_KLINOUT 035 /* KLINIK out */ +#define FE_RHBASE 036 /* boot: RH11 addr */ +#define FE_UNIT 037 /* boot: unit num */ +#define FE_MTFMT 040 /* boot: magtape params */ +#define FE_CVALID 0400 /* char valid flag */ + +/* Halfword operations */ + +#define ADDL(x,y) (((x) + ((y) << 18)) & LMASK) +#define ADDR(x,y) (((x) + (y)) & RMASK) +#define INCL(x) ADDL (x, 1) +#define INCR(x) ADDR (x, 1) +#define AOB(x) (INCL (x) | INCR(x)) +#define SUBL(x,y) (((x) - ((y) << 18)) & LMASK) +#define SUBR(x,y) (((x) - (y)) & RMASK) +#define DECL(x) SUBL (x, 1) +#define DECR(x) SUBR (x, 1) +#define SOB(x) (DECL (x) | DECR(x)) +#define LLZ(x) ((x) & LMASK) +#define RLZ(x) (((x) << 18) & LMASK) +#define RRZ(x) ((x) & RMASK) +#define LRZ(x) (((x) >> 18) & RMASK) +#define LIT8(x) (((x) & RSIGN)? \ + (((x) & 0377)? (-(x) & 0377): 0400): ((x) & 0377)) + +/* Fullword operations */ + +#define INC(x) (((x) + 1) & DMASK) +#define DEC(x) (((x) - 1) & DMASK) +#define SWP(x) ((((x) << 18) & LMASK) | (((x) >> 18) & RMASK)) +#define XWD(x,y) (((((d10) (x)) << 18) & LMASK) | (((d10) (y)) & RMASK)) +#define SETS(x) ((x) | SIGN) +#define CLRS(x) ((x) & ~SIGN) +#define TSTS(x) ((x) & SIGN) +#define NEG(x) (-(x) & DMASK) +#define ABS(x) (TSTS (x)? NEG(x): (x)) +#define SXT(x) (TSTS (x)? (x) | ~DMASK: (x)) + +/* Doubleword operations (on 2-word arrays) */ + +#define DMOVN(rs) rs[1] = (-rs[1]) & MMASK; \ + rs[0] = (~rs[0] + (rs[1] == 0)) & DMASK +#define MKDNEG(rs) rs[1] = SETS (-rs[1]) & DMASK; \ + rs[0] = (~rs[0] + (rs[1] == MAXNEG)) & DMASK +#define DCMPGE(a,b) ((a[0] > b[0]) || ((a[0] == b[0]) && (a[1] >= b[1]))) + +/* Address operations */ + +#define ADDA(x,i) (((x) + (i)) & AMASK) +#define INCA(x) ADDA (x, 1) + +/* Unibus adapter control/status register */ + +#define UBCS_TMO 0400000 /* timeout */ +#define UBCS_BMD 0200000 /* bad mem data NI */ +#define UBCS_PAR 0100000 /* parity error NI */ +#define UBCS_NXD 0040000 /* nx device */ +#define UBCS_HI 0004000 /* irq on BR7 or BR6 */ +#define UBCS_LO 0002000 /* irq on BR5 or BR4 */ +#define UBCS_PWR 0001000 /* power low NI */ +#define UBCS_DXF 0000200 /* disable xfer NI*/ +#define UBCS_INI 0000100 /* Unibus init */ +#define UBCS_RDZ 0030500 /* read as zero */ +#define UBCS_RDW 0000277 /* read/write bits */ +#define UBCS_V_LHI 3 /* hi pri irq level */ +#define UBCS_V_LLO 0 /* lo pri irq level */ +#define UBCS_M_PRI 07 +#define UBCS_GET_HI(x) (((x) >> UBCS_V_LHI) & UBCS_M_PRI) +#define UBCS_GET_LO(x) (((x) >> UBCS_V_LLO) & UBCS_M_PRI) + +/* Unibus adapter page map */ + +#define UBANUM 2 /* # of Unibus adapters */ +#define UMAP_ASIZE 6 /* address size */ +#define UMAP_MEMSIZE (1 << UMAP_ASIZE) /* length */ +#define UMAP_AMASK (UMAP_MEMSIZE - 1) +#define UMAP_V_RRV 30 /* read reverse */ +#define UMAP_V_DSB 29 /* 16b on NPR read */ +#define UMAP_V_FST 28 /* fast transfer */ +#define UMAP_V_VLD 27 /* valid flag */ +#define UMAP_RRV (1 << UMAP_V_RRV) +#define UMAP_DSB (1 << UMAP_V_DSB) +#define UMAP_FST (1 << UMAP_V_FST) +#define UMAP_VLD (1 << UMAP_V_VLD) +#define UMAP_V_FLWR 14 /* flags as written */ +#define UMAP_V_FLRD 27 /* flags as stored */ +#define UMAP_M_FL 017 +#define UMAP_V_PNWR 0 /* page num, write */ +#define UMAP_V_PNRD 9 /* page num, read */ +#define UMAP_M_PN 03777 +#define UMAP_MASK ((UMAP_M_FL << UMAP_V_FLRD) | (UMAP_M_PN << UMAP_V_PNRD)) +#define UMAP_POSFL(x) (((x) & (UMAP_M_FL << UMAP_V_FLWR)) \ + << (UMAP_V_FLRD - UMAP_V_FLWR)) +#define UMAP_POSPN(x) (((x) & (UMAP_M_PN << UMAP_V_PNWR)) \ + << (UMAP_V_PNRD - UMAP_V_PNWR)) + +/* Unibus I/O constants */ + +#define READ 0 /* PDP11 compatible */ +/* #define READC 1 /* console read */ +#define WRITE 2 +/* #define WRITEC 3 /* console write */ +#define WRITEB 4 +#define IO_V_UBA 18 /* UBA in I/O addr */ +#define IO_N_UBA 16 /* max num of UBA's */ +#define IO_M_UBA (IO_N_UBA - 1) +#define IO_UBA1 (1 << IO_V_UBA) +#define IO_UBA3 (3 << IO_V_UBA) +#define GET_IOUBA(x) (((x) >> IO_V_UBA) & IO_M_UBA) + +/* Device information block */ + +#define VEC_DEVMAX 8 /* max device vec */ + +struct pdp_dib { + uint32 ba; /* base addr */ + uint32 lnt; /* length */ + t_stat (*rd)(int32 *dat, int32 ad, int32 md); + t_stat (*wr)(int32 dat, int32 ad, int32 md); + int32 vnum; /* vectors: number */ + int32 vloc; /* locator */ + int32 vec; /* value */ + int32 (*ack[VEC_DEVMAX])(void); /* ack routines */ +}; + +typedef struct pdp_dib DIB; + +/* I/O system parameters */ + +#define DZ_MUXES 4 /* max # of muxes */ +#define DZ_LINES 8 /* lines per mux */ +#define DIB_MAX 100 /* max DIBs */ + +#define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ +#define DEV_V_QBUS (DEV_V_UF + 1) /* Qbus */ +#define DEV_V_Q18 (DEV_V_UF + 2) /* Qbus, mem <= 256KB */ +#define DEV_V_FLTA (DEV_V_UF + 3) /* float addr */ +#define DEV_UBUS (1u << DEV_V_UBUS) +#define DEV_QBUS (1u << DEV_V_QBUS) +#define DEV_Q18 (1u << DEV_V_Q18) +#define DEV_FLTA (1u << DEV_V_FLTA) + +#define UNIBUS TRUE /* 18b only */ + +#define DEV_RDX 8 /* default device radix */ + +/* I/O page layout */ + +#define IOPAGEBASE 0760000 /* I/O page base */ +#define IOBA_UBMAP 0763000 + +#define IOBA_UBMAP1 (IO_UBA1 + IOBA_UBMAP) /* Unibus 1 map */ +#define IOLN_UBMAP1 0100 +#define IOBA_UBCS1 (IO_UBA1 + 0763100) /* Unibus 1 c/s reg */ +#define IOLN_UBCS1 001 +#define IOBA_UBMNT1 (IO_UBA1 + 0763101) /* Unibus 1 maint reg */ +#define IOLN_UBMNT1 001 +#define IOBA_RP (IO_UBA1 + 0776700) /* RH11/disk */ +#define IOLN_RP 050 + +#define IOBA_DZ (IO_UBA3 + 0760010) /* DZ11 */ +#define IOLN_DZ 010 +#define IOBA_TCU (IO_UBA3 + 0760770) /* TCU150 */ +#define IOLN_TCU 006 +#define IOBA_UBMAP3 (IO_UBA3 + IOBA_UBMAP) /* Unibus 3 map */ +#define IOLN_UBMAP3 0100 +#define IOBA_UBCS3 (IO_UBA3 + 0763100) /* Unibus 3 c/s reg */ +#define IOLN_UBCS3 001 +#define IOBA_UBMNT3 (IO_UBA3 + 0763101) /* Unibus 3 maint reg */ +#define IOLN_UBMNT3 001 +#define IOBA_XU (IO_UBA3 + 0774510) /* DEUNA/DELUA */ +#define IOLN_XU 010 +#define IOBA_CR (IO_UBA3 + 0777160) /* CD/CR/CM */ +#define IOLN_CR 010 +#define IOBA_RY (IO_UBA3 + 0777170) /* RX211 */ +#define IOLN_RY 004 +#define IOBA_TU (IO_UBA3 + 0772440) /* RH11/tape */ +#define IOLN_TU 034 +#define IOBA_LP20 (IO_UBA3 + 0775400) /* LP20 */ +#define IOLN_LP20 020 +#define IOBA_PTR (IO_UBA3 + 017550) /* PC11 reader */ +#define IOLN_PTR 004 +#define IOBA_PTP (IO_UBA3 + 017554) /* PC11 punch */ +#define IOLN_PTP 004 + +/* Common Unibus CSR flags */ + +#define CSR_V_GO 0 /* go */ +#define CSR_V_IE 6 /* interrupt enable */ +#define CSR_V_DONE 7 /* done */ +#define CSR_V_BUSY 11 /* busy */ +#define CSR_V_ERR 15 /* error */ +#define CSR_GO (1u << CSR_V_GO) +#define CSR_IE (1u << CSR_V_IE) +#define CSR_DONE (1u << CSR_V_DONE) +#define CSR_BUSY (1u << CSR_V_BUSY) +#define CSR_ERR (1u << CSR_V_ERR) + +/* I/O system definitions, lifted from the PDP-11 simulator + Interrupt assignments, priority is right to left + + <3:0> = BR7 + <7:4> = BR6 + <19:8> = BR5 + <30:20> = BR4 +*/ + +#define INT_V_RP 6 /* RH11/RP,RM drives */ +#define INT_V_TU 7 /* RH11/TM03/TU45 */ +#define INT_V_XU 15 /* DEUNA/DELUA */ +#define INT_V_DZRX 16 /* DZ11 */ +#define INT_V_DZTX 17 +#define INT_V_RY 18 /* RX211 */ +#define INT_V_PTR 24 /* PC11 */ +#define INT_V_PTP 25 +#define INT_V_LP20 26 /* LPT20 */ +#define INT_V_CR 27 /* CD20 (CD11) */ + +#define INT_RP (1u << INT_V_RP) +#define INT_TU (1u << INT_V_TU) +#define INT_XU (1u << INT_V_XU) +#define INT_DZRX (1u << INT_V_DZRX) +#define INT_DZTX (1u << INT_V_DZTX) +#define INT_RY (1u << INT_V_RY) +#define INT_PTR (1u << INT_V_PTR) +#define INT_PTP (1u << INT_V_PTP) +#define INT_LP20 (1u << INT_V_LP20) +#define INT_CR (1u << INT_V_CR) + +#define IPL_RP 6 /* int levels */ +#define IPL_TU 6 +#define IPL_XU 5 +#define IPL_DZRX 5 +#define IPL_DZTX 5 +#define IPL_RY 5 +#define IPL_PTR 4 +#define IPL_PTP 4 +#define IPL_LP20 4 +#define IPL_CR 4 + +#define INT_UB1 INT_RP /* on Unibus 1 */ +#define INT_UB3 (0xFFFFFFFFu & ~INT_UB1) /* on Unibus 3 */ + +#define INT_IPL7 0x0000000F /* int level masks */ +#define INT_IPL6 0x000000F0 +#define INT_IPL5 0x000FFF00 +#define INT_IPL4 0x3FF00000 + +#define VEC_Q 0000 /* vector base */ +#define VEC_PTR 0070 /* interrupt vectors */ +#define VEC_PTP 0074 +#define VEC_XU 0120 +#define VEC_TU 0224 +#define VEC_CR 0230 +#define VEC_RP 0254 +#define VEC_RY 0264 +#define VEC_DZRX 0340 +#define VEC_DZTX 0344 +#define VEC_LP20 0754 + +#define IVCL(dv) (INT_V_##dv) +#define IREQ(dv) int_req +#define SET_INT(dv) IREQ(dv) = IREQ(dv) | (INT_##dv) +#define CLR_INT(dv) IREQ(dv) = IREQ(dv) & ~(INT_##dv) + +/* Function prototypes */ + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat set_vec (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat auto_config (char *name, int32 num); + +int32 clk_cosched (int32 wait); + +/* Global data */ + +extern t_bool sim_idle_enab; + +#endif diff --git a/PDP10/pdp10_fe.c b/PDP10/pdp10_fe.c new file mode 100644 index 0000000..83a1e66 --- /dev/null +++ b/PDP10/pdp10_fe.c @@ -0,0 +1,172 @@ +/* pdp10_fe.c: PDP-10 front end (console terminal) simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + fe KS10 console front end + + 18-Jun-07 RMS Added UNIT_IDLE flag to console input + 17-Oct-06 RMS Synced keyboard to clock for idling + 28-May-04 RMS Removed SET FE CTRL-C + 29-Dec-03 RMS Added console backpressure support + 25-Apr-03 RMS Revised for extended file support + 22-Dec-02 RMS Added break support + 30-May-02 RMS Widened COUNT to 32b + 30-Nov-01 RMS Added extended SET/SHOW support + 23-Oct-01 RMS New IO page address constants + 07-Sep-01 RMS Moved function prototypes +*/ + +#include "pdp10_defs.h" +#define UNIT_DUMMY (1 << UNIT_V_UF) + +extern d10 *M; +extern int32 apr_flg; +extern int32 tmxr_poll; +t_stat fei_svc (UNIT *uptr); +t_stat feo_svc (UNIT *uptr); +t_stat fe_reset (DEVICE *dptr); +t_stat fe_stop_os (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* FE data structures + + fe_dev FE device descriptor + fe_unit FE unit descriptor + fe_reg FE register list +*/ + +#define fei_unit fe_unit[0] +#define feo_unit fe_unit[1] + +UNIT fe_unit[] = { + { UDATA (&fei_svc, UNIT_IDLE, 0), 0 }, + { UDATA (&feo_svc, 0, 0), SERIAL_OUT_WAIT } + }; + +REG fe_reg[] = { + { ORDATA (IBUF, fei_unit.buf, 8) }, + { DRDATA (ICOUNT, fei_unit.pos, T_ADDR_W), REG_RO + PV_LEFT }, + { DRDATA (ITIME, fei_unit.wait, 24), PV_LEFT }, + { ORDATA (OBUF, feo_unit.buf, 8) }, + { DRDATA (OCOUNT, feo_unit.pos, T_ADDR_W), REG_RO + PV_LEFT }, + { DRDATA (OTIME, feo_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB fe_mod[] = { + { UNIT_DUMMY, 0, NULL, "STOP", &fe_stop_os }, + { 0 } + }; + +DEVICE fe_dev = { + "FE", fe_unit, fe_reg, fe_mod, + 2, 10, 31, 1, 8, 8, + NULL, NULL, &fe_reset, + NULL, NULL, NULL + }; + +/* Front end processor (console terminal) + + Communications between the KS10 and its front end is based on an in-memory + status block and two interrupt lines: interrupt-to-control (APR_ITC) and + interrupt-from-console (APR_CON). When the KS10 wants to print a character + on the terminal, + + 1. It places a character, plus the valid flag, in FE_CTYOUT. + 2. It interrupts the front end processor. + 3. The front end processor types the character and then zeroes FE_CTYOUT. + 4. The front end procesor interrupts the KS10. + + When the front end wants to send an input character to the KS10, + + 1. It places a character, plus the valid flag, in FE_CTYIN. + 2. It interrupts the KS10. + 3. It waits for the KS10 to take the character and clear the valid flag. + 4. It can then send more input (the KS10 may signal this by interrupting + the front end). + + Note that the protocol has both ambiguity (interrupt to the KS10 may mean + character printed, or input character available, or both) and lack of + symmetry (the KS10 does not inform the front end that it has taken an + input character). +*/ + +void fe_intr (void) +{ +if (M[FE_CTYOUT] & FE_CVALID) { /* char to print? */ + feo_unit.buf = (int32) M[FE_CTYOUT] & 0177; /* pick it up */ + feo_unit.pos = feo_unit.pos + 1; + sim_activate (&feo_unit, feo_unit.wait); /* sched completion */ + } +else if ((M[FE_CTYIN] & FE_CVALID) == 0) { /* input char taken? */ + sim_cancel (&fei_unit); /* sched immediate */ + sim_activate (&fei_unit, 0); /* keyboard poll */ + } +return; +} + +t_stat feo_svc (UNIT *uptr) +{ +t_stat r; + +if ((r = sim_putchar_s (uptr->buf)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } +M[FE_CTYOUT] = 0; /* clear char */ +apr_flg = apr_flg | APRF_CON; /* interrupt KS10 */ +return SCPE_OK; +} + +t_stat fei_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +if (temp & SCPE_BREAK) return SCPE_OK; /* ignore break */ +uptr->buf = temp & 0177; +uptr->pos = uptr->pos + 1; +M[FE_CTYIN] = uptr->buf | FE_CVALID; /* put char in mem */ +apr_flg = apr_flg | APRF_CON; /* interrupt KS10 */ +return SCPE_OK; +} + +/* Reset */ + +t_stat fe_reset (DEVICE *dptr) +{ +fei_unit.buf = feo_unit.buf = 0; +M[FE_CTYIN] = M[FE_CTYOUT] = 0; +apr_flg = apr_flg & ~(APRF_ITC | APRF_CON); +sim_activate_abs (&fei_unit, KBD_WAIT (fei_unit.wait, tmxr_poll)); +return SCPE_OK; +} + +/* Stop operating system */ + +t_stat fe_stop_os (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +M[FE_SWITCH] = IOBA_RP; /* tell OS to stop */ +return SCPE_OK; +} diff --git a/PDP10/pdp10_ksio.c b/PDP10/pdp10_ksio.c new file mode 100644 index 0000000..5ec6795 --- /dev/null +++ b/PDP10/pdp10_ksio.c @@ -0,0 +1,853 @@ +/* pdp10_ksio.c: PDP-10 KS10 I/O subsystem simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + uba Unibus adapters + + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 25-Jan-04 RMS Added stub floating address routine + 12-Mar-03 RMS Added logical name support + 10-Oct-02 RMS Revised for dynamic table generation + Added SHOW IOSPACE routine + 29-Sep-02 RMS Added variable vector, central map support + 25-Jan-02 RMS Revised for multiple DZ11's + 06-Jan-02 RMS Revised enable/disable support + 23-Sep-01 RMS New IO page address constants + 07-Sep-01 RMS Revised device disable mechanism + 25-Aug-01 RMS Enabled DZ11 + 21-Aug-01 RMS Updated DZ11 disable + 01-Jun-01 RMS Updated DZ11 vectors + 12-May-01 RMS Fixed typo + + The KS10 uses the PDP-11 Unibus for its I/O, via adapters. While + nominally four adapters are supported, in practice only 1 and 3 + are implemented. The disks are placed on adapter 1, the rest of + the I/O devices on adapter 3. + + In theory, we should maintain completely separate Unibuses, with + distinct PI systems. In practice, this simulator has so few devices + that we can get away with a single PI system, masking for which + devices are on adapter 1, and which on adapter 3. The Unibus + implementation is modeled on the Qbus in the PDP-11 simulator and + is described there. + + The I/O subsystem is programmed by I/O instructions which create + Unibus operations (read, read pause, write, write byte). DMA is + the responsibility of the I/O device simulators, which also implement + Unibus to physical memory mapping. + + The priority interrupt subsystem (and other privileged functions) + is programmed by I/O instructions with internal devices codes + (opcodes 700-702). These are dispatched here, although many are + handled in the memory management unit or elsewhere. + + The ITS instructions are significantly different from the TOPS-10/20 + instructions. They do not use the extended address calculation but + instead provide instruction variants (Q for Unibus adapter 1, I for + Unibus adapter 3) which insert the Unibus adapter number into the + effective address. +*/ + +#include "pdp10_defs.h" +#include + +#define XBA_MBZ 0400000 /* ba mbz */ +#define eaRB (ea & ~1) +#define GETBYTE(ea,x) ((((ea) & 1)? (x) >> 8: (x)) & 0377) +#define UBNXM_FAIL(pa,op) \ + n = iocmap[GET_IOUBA (pa)]; \ + if (n >= 0) ubcs[n] = ubcs[n] | UBCS_TMO | UBCS_NXD; \ + pager_word = PF_HARD | PF_VIRT | PF_IO | \ + ((op == WRITEB)? PF_BYTE: 0) | \ + (TSTF (F_USR)? PF_USER: 0) | (pa); \ + ABORT (PAGE_FAIL) + +/* Unibus adapter data */ + +int32 ubcs[UBANUM] = { 0 }; /* status registers */ +int32 ubmap[UBANUM][UMAP_MEMSIZE] = { 0 }; /* Unibus maps */ +int32 int_req = 0; /* interrupt requests */ + +/* Map IO controller numbers to Unibus adapters: -1 = non-existent */ + +static int iocmap[IO_N_UBA] = { /* map I/O ext to UBA # */ + -1, 0, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 + }; + +static const int32 ubabr76[UBANUM] = { + INT_UB1 & (INT_IPL7 | INT_IPL6), INT_UB3 & (INT_IPL7 | INT_IPL6) + }; +static const int32 ubabr54[UBANUM] = { + INT_UB1 & (INT_IPL5 | INT_IPL4), INT_UB3 & (INT_IPL5 | INT_IPL4) + }; +static const int32 ubashf[4] = { 18, 26, 0, 8 }; + +extern d10 *M; /* main memory */ +extern d10 *ac_cur; +extern d10 pager_word; +extern int32 flags; +extern const int32 pi_l2bit[8]; +extern UNIT cpu_unit; +extern FILE *sim_log; +extern jmp_buf save_env; +extern DEVICE *sim_devices[]; + +extern int32 pi_eval (void); +extern int32 rp_inta (void); +extern int32 tu_inta (void); +extern int32 lp20_inta (void); +extern int32 dz_rxinta (void); +extern int32 dz_txinta (void); + +t_stat ubmap_rd (int32 *data, int32 addr, int32 access); +t_stat ubmap_wr (int32 data, int32 addr, int32 access); +t_stat ubs_rd (int32 *data, int32 addr, int32 access); +t_stat ubs_wr (int32 data, int32 addr, int32 access); +t_stat rd_zro (int32 *data, int32 addr, int32 access); +t_stat wr_nop (int32 data, int32 addr, int32 access); +t_stat uba_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat uba_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat uba_reset (DEVICE *dptr); +d10 ReadIO (a10 ea); +void WriteIO (a10 ea, d10 val, int32 mode); + +/* Unibus adapter data structures + + uba_dev UBA device descriptor + uba_unit UBA units + uba_reg UBA register list +*/ + +DIB ubmp1_dib = { IOBA_UBMAP1, IOLN_UBMAP1, &ubmap_rd, &ubmap_wr, 0 }; +DIB ubmp3_dib = { IOBA_UBMAP3, IOLN_UBMAP3, &ubmap_rd, &ubmap_wr, 0 }; +DIB ubcs1_dib = { IOBA_UBCS1, IOLN_UBCS1, &ubs_rd, &ubs_wr, 0 }; +DIB ubcs3_dib = { IOBA_UBCS3, IOLN_UBCS3, &ubs_rd, &ubs_wr, 0 }; +DIB ubmn1_dib = { IOBA_UBMNT1, IOLN_UBMNT1, &rd_zro, &wr_nop, 0 }; +DIB ubmn3_dib = { IOBA_UBMNT3, IOLN_UBMNT3, &rd_zro, &wr_nop, 0 }; +DIB msys_dib = { 00100000, 1, &rd_zro, &wr_nop, 0 }; + +UNIT uba_unit[] = { + { UDATA (NULL, UNIT_FIX, UMAP_MEMSIZE) }, + { UDATA (NULL, UNIT_FIX, UMAP_MEMSIZE) } + }; + +REG uba_reg[] = { + { ORDATA (INTREQ, int_req, 32), REG_RO }, + { ORDATA (UB1CS, ubcs[0], 18) }, + { ORDATA (UB3CS, ubcs[1], 18) }, + { NULL } + }; + +DEVICE uba_dev = { + "UBA", uba_unit, uba_reg, NULL, + UBANUM, 8, UMAP_ASIZE, 1, 8, 32, + &uba_ex, &uba_dep, &uba_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* PDP-11 I/O structures */ + +DIB *dib_tab[DIB_MAX]; /* run-time DIBs */ + +int32 (*int_ack[32])(void); /* int ack routines */ + +int32 int_vec[32]; /* int vectors */ + +DIB *std_dib[] = { /* standard DIBs */ + &ubmp1_dib, + &ubmp3_dib, + &ubcs1_dib, + &ubcs3_dib, + &ubmn1_dib, + &ubmn3_dib, + &msys_dib, + NULL + }; + +/* IO 710 (DEC) TIOE - test I/O word, skip if zero + (ITS) IORDI - read word from Unibus 3 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io710 (int32 ac, a10 ea) +{ +d10 val; + +if (Q_ITS) AC(ac) = ReadIO (IO_UBA3 | ea); /* IORDI */ +else { /* TIOE */ + val = ReadIO (ea); /* read word */ + if ((AC(ac) & val) == 0) return TRUE; + } +return FALSE; +} + +/* IO 711 (DEC) TION - test I/O word, skip if non-zero + (ITS) IORDQ - read word from Unibus 1 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io711 (int32 ac, a10 ea) +{ +d10 val; + +if (Q_ITS) AC(ac) = ReadIO (IO_UBA1 | ea); /* IORDQ */ +else { /* TION */ + val = ReadIO (ea); /* read word */ + if ((AC(ac) & val) != 0) return TRUE; + } +return FALSE; +} + +/* IO 712 (DEC) RDIO - read I/O word, addr in ea + (ITS) IORD - read I/O word, addr in M[ea] +*/ + +d10 io712 (a10 ea) +{ +return ReadIO (ea); /* RDIO, IORD */ +} + +/* IO 713 (DEC) WRIO - write I/O word, addr in ea + (ITS) IOWR - write I/O word, addr in M[ea] +*/ + +void io713 (d10 val, a10 ea) +{ +WriteIO (ea, val & 0177777, WRITE); /* WRIO, IOWR */ +return; +} + +/* IO 714 (DEC) BSIO - set bit in I/O address + (ITS) IOWRI - write word to Unibus 3 +*/ + +void io714 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0177777; +if (Q_ITS) WriteIO (IO_UBA3 | ea, val, WRITE); /* IOWRI */ +else { + temp = ReadIO (ea); /* BSIO */ + temp = temp | val; + WriteIO (ea, temp, WRITE); + } +return; +} + +/* IO 715 (DEC) BCIO - clear bit in I/O address + (ITS) IOWRQ - write word to Unibus 1 +*/ + +void io715 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0177777; +if (Q_ITS) WriteIO (IO_UBA1 | ea, val, WRITE); /* IOWRQ */ +else { + temp = ReadIO (ea); /* BCIO */ + temp = temp & ~val; + WriteIO (ea, temp, WRITE); + } +return; +} + +/* IO 720 (DEC) TIOEB - test I/O byte, skip if zero + (ITS) IORDBI - read byte from Unibus 3 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io720 (int32 ac, a10 ea) +{ +d10 val; + +if (Q_ITS) { /* IORDBI */ + val = ReadIO (IO_UBA3 | eaRB); + AC(ac) = GETBYTE (ea, val); + } +else { /* TIOEB */ + val = ReadIO (eaRB); + val = GETBYTE (ea, val); + if ((AC(ac) & val) == 0) return TRUE; + } +return FALSE; +} + +/* IO 721 (DEC) TIONB - test I/O word, skip if non-zero + (ITS) IORDBQ - read word from Unibus 1 + returns TRUE if skip, FALSE otherwise +*/ + +t_bool io721 (int32 ac, a10 ea) +{ +d10 val; + +if (Q_ITS) { /* IORDBQ */ + val = ReadIO (IO_UBA1 | eaRB); + AC(ac) = GETBYTE (ea, val); + } +else { /* TIONB */ + val = ReadIO (eaRB); + val = GETBYTE (ea, val); + if ((AC(ac) & val) != 0) return TRUE; + } +return FALSE; +} + +/* IO 722 (DEC) RDIOB - read I/O byte, addr in ea + (ITS) IORDB - read I/O byte, addr in M[ea] +*/ + +d10 io722 (a10 ea) +{ +d10 val; + +val = ReadIO (eaRB); /* RDIOB, IORDB */ +return GETBYTE (ea, val); +} + +/* IO 723 (DEC) WRIOB - write I/O byte, addr in ea + (ITS) IOWRB - write I/O byte, addr in M[ea] +*/ + +void io723 (d10 val, a10 ea) +{ +WriteIO (ea, val & 0377, WRITEB); /* WRIOB, IOWRB */ +return; +} + +/* IO 724 (DEC) BSIOB - set bit in I/O byte address + (ITS) IOWRBI - write byte to Unibus 3 +*/ + +void io724 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0377; +if (Q_ITS) WriteIO (IO_UBA3 | ea, val, WRITEB); /* IOWRBI */ +else { + temp = ReadIO (eaRB); /* BSIOB */ + temp = GETBYTE (ea, temp); + temp = temp | val; + WriteIO (ea, temp, WRITEB); + } +return; +} + +/* IO 725 (DEC) BCIOB - clear bit in I/O byte address + (ITS) IOWRBQ - write byte to Unibus 1 +*/ + +void io725 (d10 val, a10 ea) +{ +d10 temp; + +val = val & 0377; +if (Q_ITS) WriteIO (IO_UBA1 | ea, val, WRITEB); /* IOWRBQ */ +else { + temp = ReadIO (eaRB); /* BCIOB */ + temp = GETBYTE (ea, temp); + temp = temp & ~val; + WriteIO (ea, temp, WRITEB); + } +return; +} + +/* Read and write I/O devices. + These routines are the linkage between the 64b world of the main + simulator and the 32b world of the device simulators. +*/ + +d10 ReadIO (a10 ea) +{ +uint32 pa = (uint32) ea; +int32 i, n, val; +DIB *dibp; + +for (i = 0; dibp = dib_tab[i]; i++ ) { + if ((pa >= dibp->ba) && + (pa < (dibp->ba + dibp->lnt))) { + dibp->rd (&val, pa, READ); + pi_eval (); + return ((d10) val); + } + } +UBNXM_FAIL (pa, READ); +} + +void WriteIO (a10 ea, d10 val, int32 mode) +{ +uint32 pa = (uint32) ea; +int32 i, n; +DIB *dibp; + +for (i = 0; dibp = dib_tab[i]; i++ ) { + if ((pa >= dibp->ba) && + (pa < (dibp->ba + dibp->lnt))) { + dibp->wr ((int32) val, pa, mode); + pi_eval (); + return; + } + } +UBNXM_FAIL (pa, mode); +} + +/* Mapped read and write routines - used by standard Unibus devices on Unibus 1 */ + +a10 Map_Addr10 (a10 ba, int32 ub) +{ +a10 pa10; +int32 vpn = PAG_GETVPN (ba >> 2); /* get PDP-10 page number */ + +if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || + ((ubmap[ub][vpn] & UMAP_VLD) == 0)) return -1; /* invalid map? */ +pa10 = (ubmap[ub][vpn] + PAG_GETOFF (ba >> 2)) & PAMASK; +return pa10; +} + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) +{ +uint32 lim; +a10 pa10; + +lim = ba + bc; +for ( ; ba < lim; ba++) { /* by bytes */ + pa10 = Map_Addr10 (ba, 1); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ + return (lim - ba); /* return bc */ + } + *buf++ = (uint8) ((M[pa10] >> ubashf[ba & 3]) & 0377); + } +return 0; +} + +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) +{ +uint32 lim; +a10 pa10; + +ba = ba & ~01; /* align start */ +lim = ba + (bc & ~01); +for ( ; ba < lim; ba = ba + 2) { /* by words */ + pa10 = Map_Addr10 (ba, 1); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ + return (lim - ba); /* return bc */ + } + *buf++ = (uint16) ((M[pa10] >> ((ba & 2)? 0: 18)) & 0177777); + } +return 0; +} + +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) +{ +uint32 lim; +a10 pa10; +d10 mask; + +lim = ba + bc; +for ( ; ba < lim; ba++) { /* by bytes */ + pa10 = Map_Addr10 (ba, 1); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ + return (lim - ba); /* return bc */ + } + mask = 0377; + M[pa10] = (M[pa10] & ~(mask << ubashf[ba & 3])) | + (((d10) *buf++) << ubashf[ba & 3]); + } +return 0; +} + +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) +{ +uint32 lim; +a10 pa10; +d10 val; + +ba = ba & ~01; /* align start */ +lim = ba + (bc & ~01); +for ( ; ba < lim; ba++) { /* by bytes */ + pa10 = Map_Addr10 (ba, 1); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ + return (lim - ba); /* return bc */ + } + val = *buf++; /* get data */ + if (ba & 2) M[pa10] = (M[pa10] & 0777777600000) | val; + else M[pa10] = (M[pa10] & 0600000777777) | (val << 18); + } +return 0; +} + +/* Evaluate Unibus priority interrupts */ + +int32 pi_ub_eval () +{ +int32 i, lvl; + +for (i = lvl = 0; i < UBANUM; i++) { + if (int_req & ubabr76[i]) + lvl = lvl | pi_l2bit[UBCS_GET_HI (ubcs[i])]; + if (int_req & ubabr54[i]) + lvl = lvl | pi_l2bit[UBCS_GET_LO (ubcs[i])]; + } +return lvl; +} + +/* Return Unibus device vector + + Takes as input the request level calculated by pi_eval + If there is an interrupting Unibus device at that level, return its vector, + otherwise, returns 0 +*/ + +int32 pi_ub_vec (int32 rlvl, int32 *uba) +{ +int32 i, masked_irq; + +for (i = masked_irq = 0; i < UBANUM; i++) { + if ((rlvl == UBCS_GET_HI (ubcs[i])) && /* req on hi level? */ + (masked_irq = int_req & ubabr76[i])) break; + if ((rlvl == UBCS_GET_LO (ubcs[i])) && /* req on lo level? */ + (masked_irq = int_req & ubabr54[i])) break; + } +*uba = (i << 1) + 1; /* store uba # */ +for (i = 0; (i < 32) && masked_irq; i++) { /* find hi pri req */ + if ((masked_irq >> i) & 1) { + int_req = int_req & ~(1u << i); /* clear req */ + if (int_ack[i]) return int_ack[i](); + return int_vec[i]; /* return vector */ + } + } +return 0; +} + +/* Unibus adapter map routines */ + +t_stat ubmap_rd (int32 *val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +*val = ubmap[n][pa & UMAP_AMASK]; +return SCPE_OK; +} + +t_stat ubmap_wr (int32 val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +ubmap[n][pa & UMAP_AMASK] = UMAP_POSFL (val) | UMAP_POSPN (val); +return SCPE_OK; +} + +/* Unibus adapter control/status routines */ + +t_stat ubs_rd (int32 *val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +if (int_req & ubabr76[n]) ubcs[n] = ubcs[n] | UBCS_HI; +if (int_req & ubabr54[n]) ubcs[n] = ubcs[n] | UBCS_LO; +*val = ubcs[n] = ubcs[n] & ~UBCS_RDZ; +return SCPE_OK; +} + +t_stat ubs_wr (int32 val, int32 pa, int32 mode) +{ +int32 n = iocmap[GET_IOUBA (pa)]; + +if (n < 0) ABORT (STOP_ILLIOC); +if (val & UBCS_INI) { + reset_all (5); /* start after UBA */ + ubcs[n] = val & UBCS_DXF; + } +else ubcs[n] = val & UBCS_RDW; +if (int_req & ubabr76[n]) ubcs[n] = ubcs[n] | UBCS_HI; +if (int_req & ubabr54[n]) ubcs[n] = ubcs[n] | UBCS_LO; +return SCPE_OK; +} + +/* Unibus adapter read zero/write ignore routines */ + +t_stat rd_zro (int32 *val, int32 pa, int32 mode) +{ +*val = 0; +return SCPE_OK; +} + +t_stat wr_nop (int32 val, int32 pa, int32 mode) +{ +return SCPE_OK; +} + +/* Simulator interface routines */ + +t_stat uba_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 uba = uptr - uba_unit; + +if (addr >= UMAP_MEMSIZE) return SCPE_NXM; +*vptr = ubmap[uba][addr]; +return SCPE_OK; +} + +t_stat uba_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 uba = uptr - uba_unit; + +if (addr >= UMAP_MEMSIZE) return SCPE_NXM; +ubmap[uba][addr] = (int32) val & UMAP_MASK; +return SCPE_OK; +} + +t_stat uba_reset (DEVICE *dptr) +{ +int32 i, uba; + +int_req = 0; +for (uba = 0; uba < UBANUM; uba++) { + ubcs[uba] = 0; + for (i = 0; i < UMAP_MEMSIZE; i++) ubmap[uba][i] = 0; + } +pi_eval (); +return SCPE_OK; +} + +/* Change device address */ + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newba; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if ((val == 0) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newba = (uint32) get_uint (cptr, 8, PAMASK, &r); /* get new */ +if ((r != SCPE_OK) || (newba == dibp->ba)) return r; +if (GET_IOUBA (newba) != GET_IOUBA (dibp->ba)) return SCPE_ARG; +if (newba % ((uint32) val)) return SCPE_ARG; /* check modulus */ +if (GET_IOUBA (newba) != GET_IOUBA (dibp->ba)) return SCPE_ARG; +dibp->ba = newba; /* store */ +return SCPE_OK; +} + +/* Show device address */ + +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR; +fprintf (st, "address=%07o", dibp->ba); +if (dibp->lnt > 1) + fprintf (st, "-%07o", dibp->ba + dibp->lnt - 1); +return SCPE_OK; +} + +/* Change device vector */ + +t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newvec; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newvec = (uint32) get_uint (cptr, 8, VEC_Q + 01000, &r); +if ((r != SCPE_OK) || (newvec == VEC_Q) || + ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) || + (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG; +dibp->vec = newvec; +return SCPE_OK; +} + +/* Show device vector */ + +t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 vec, numvec; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +vec = dibp->vec; +if (arg) numvec = arg; +else numvec = dibp->vnum; +if (vec == 0) fprintf (st, "no vector"); +else { + fprintf (st, "vector=%o", vec); + if (numvec > 1) fprintf (st, "-%o", vec + (4 * (numvec - 1))); + } +return SCPE_OK; +} + +/* Test for conflict in device addresses */ + +t_bool dev_conflict (DIB *curr) +{ +uint32 i, end; +DEVICE *dptr; +DIB *dibp; + +end = curr->ba + curr->lnt - 1; /* get end */ +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if ((dibp == NULL) || (dibp == curr) || + (dptr->flags & DEV_DIS)) continue; + if (((curr->ba >= dibp->ba) && /* overlap start? */ + (curr->ba < (dibp->ba + dibp->lnt))) || + ((end >= dibp->ba) && /* overlap end? */ + (end < (dibp->ba + dibp->lnt)))) { + printf ("Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + if (sim_log) fprintf (sim_log, + "Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + return TRUE; + } + } +return FALSE; +} + +/* Build interrupt tables */ + +void build_int_vec (int32 vloc, int32 ivec, int32 (*iack)(void) ) +{ +if (iack != NULL) int_ack[vloc] = iack; +else int_vec[vloc] = ivec; +return; +} + +/* Build dib_tab from device list */ + +t_bool build_dib_tab (void) +{ +int32 i, j, k; +DEVICE *dptr; +DIB *dibp; + +for (i = 0; i < 32; i++) { /* clear intr tables */ + int_vec[i] = 0; + int_ack[i] = NULL; + } +for (i = j = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ + if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR; + for (k = 0; k < dibp->vnum; k++) /* loop thru vec */ + build_int_vec (dibp->vloc + k, /* add vector */ + dibp->vec + (k * 4), dibp->ack[k]); + if (dibp->lnt != 0) { /* I/O addresses? */ + dib_tab[j++] = dibp; /* add DIB to dib_tab */ + if (j >= DIB_MAX) return SCPE_IERR; /* too many? */ + } + } /* end if enabled */ + } /* end for */ +for (i = 0; (dibp = std_dib[i]) != NULL; i++) { /* loop thru std */ + dib_tab[j++] = dibp; /* add to dib_tab */ + if (j >= DIB_MAX) return SCPE_IERR; /* too many? */ + } +dib_tab[j] = NULL; /* end with NULL */ +for (i = 0; (dibp = dib_tab[i]) != NULL; i++) { /* test built dib_tab */ + if (dev_conflict (dibp)) return SCPE_STOP; /* for conflicts */ + } +return SCPE_OK; +} + +/* Show dib_tab */ + +t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, j, done = 0; +DEVICE *dptr; +DIB *dibt; + +build_dib_tab (); /* build table */ +while (done == 0) { /* sort ascending */ + done = 1; /* assume done */ + for (i = 0; dib_tab[i + 1] != NULL; i++) { /* check table */ + if (dib_tab[i]->ba > dib_tab[i + 1]->ba) { /* out of order? */ + dibt = dib_tab[i]; /* interchange */ + dib_tab[i] = dib_tab[i + 1]; + dib_tab[i + 1] = dibt; + done = 0; /* not done */ + } + } + } /* end while */ +for (i = 0; dib_tab[i] != NULL; i++) { /* print table */ + for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) { + if (((DIB*) sim_devices[j]->ctxt) == dib_tab[i]) { + dptr = sim_devices[j]; + break; + } + } + fprintf (st, "%07o - %07o\t%s\n", dib_tab[i]->ba, + dib_tab[i]->ba + dib_tab[i]->lnt - 1, + dptr? sim_dname (dptr): "CPU"); + } +return SCPE_OK; +} + +/* Stub auto-configure */ + +t_stat auto_config (char *name, int32 num) +{ +return SCPE_OK; +} + +/* Stub floating address */ + +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +return SCPE_OK; +} diff --git a/PDP10/pdp10_lp20.c b/PDP10/pdp10_lp20.c new file mode 100644 index 0000000..c7af5a9 --- /dev/null +++ b/PDP10/pdp10_lp20.c @@ -0,0 +1,663 @@ +/* pdp10_lp20.c: PDP-10 LP20 line printer simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lp20 line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + 04-Sep-05 RMS Fixed missing return (found by Peter Schorn) + 07-Jul-05 RMS Removed extraneous externs + 18-Mar-05 RMS Added attached test to detach routine + 29-Dec-03 RMS Fixed bug in scheduling + 25-Apr-03 RMS Revised for extended file support + 29-Sep-02 RMS Added variable vector support + Modified to use common Unibus routines + New data structures + 30-May-02 RMS Widened POS to 32b + 06-Jan-02 RMS Added enable/disable support + 30-Nov-01 RMS Added extended SET/SHOW support +*/ + +#include "pdp10_defs.h" + +#define UNIT_DUMMY (1 << UNIT_V_UF) +#define LP_WIDTH 132 /* printer width */ + +/* DAVFU RAM */ + +#define DV_SIZE 143 /* DAVFU size */ +#define DV_DMASK 077 /* data mask per byte */ +#define DV_TOF 0 /* top of form channel */ +#define DV_MAX 11 /* max channel number */ + +/* Translation RAM */ + +#define TX_SIZE 256 /* translation RAM */ +#define TX_AMASK (TX_SIZE - 1) +#define TX_DMASK 07777 +#define TX_V_FL 8 /* flags */ +#define TX_M_FL 017 +/* define TX_INTR 04000 /* interrupt */ +#define TX_DELH 02000 /* delimiter */ +/* define TX_XLAT 01000 /* translate */ +/* define TX_DVFU 00400 /* DAVFU */ +#define TX_SLEW 00020 /* chan vs slew */ +#define TX_VMASK 00017 /* spacing mask */ +#define TX_CHR 0 /* states: pr char */ +#define TX_RAM 1 /* pr translation */ +#define TX_DVU 2 /* DAVFU action */ +#define TX_INT 3 /* interrupt */ +#define TX_GETFL(x) (((x) >> TX_V_FL) & TX_M_FL) + +/* LPCSRA (765400) */ + +#define CSA_GO 0000001 /* go */ +#define CSA_PAR 0000002 /* parity enable NI */ +#define CSA_V_FNC 2 /* function */ +#define CSA_M_FNC 03 +#define FNC_PR 0 /* print */ +#define FNC_TST 1 /* test */ +#define FNC_DVU 2 /* load DAVFU */ +#define FNC_RAM 3 /* load translation RAM */ +#define FNC_INTERNAL 1 /* internal function */ +#define CSA_FNC (CSA_M_FNC << CSA_V_FNC) +#define CSA_V_UAE 4 /* Unibus addr extension */ +#define CSA_UAE (03 << CSA_V_UAE) +#define CSA_IE 0000100 /* interrupt enable */ +#define CSA_DONE 0000200 /* done */ +#define CSA_INIT 0000400 /* init */ +#define CSA_ECLR 0001000 /* clear errors */ +#define CSA_DELH 0002000 /* delimiter hold */ +#define CSA_ONL 0004000 /* online */ +#define CSA_DVON 0010000 /* DAVFU online */ +#define CSA_UNDF 0020000 /* undefined char */ +#define CSA_PZRO 0040000 /* page counter zero */ +#define CSA_ERR 0100000 /* error */ +#define CSA_RW (CSA_DELH | CSA_IE | CSA_UAE | CSA_FNC | CSA_PAR | CSA_GO) +#define CSA_MBZ (CSA_ECLR | CSA_INIT) +#define CSA_GETUAE(x) (((x) & CSA_UAE) << (16 - CSA_V_UAE)) +#define CSA_GETFNC(x) (((x) >> CSA_V_FNC) & CSA_M_FNC) + +/* LPCSRB (765402) */ + +#define CSB_GOE 0000001 /* go error */ +#define CSB_DTE 0000002 /* DEM timing error NI */ +#define CSB_MTE 0000004 /* MSYN error (Ubus timeout) */ +#define CSB_RPE 0000010 /* RAM parity error NI */ +#define CSB_MPE 0000020 /* MEM parity error NI */ +#define CSB_LPE 0000040 /* LPT parity error NI */ +#define CSB_DVOF 0000100 /* DAVFU not ready */ +#define CSB_OFFL 0000200 /* offline */ +#define CSB_TEST 0003400 /* test mode */ +#define CSB_OVFU 0004000 /* optical VFU NI */ +#define CSB_PBIT 0010000 /* data parity bit NI */ +#define CSB_NRDY 0020000 /* printer error NI */ +#define CSB_LA180 0040000 /* LA180 printer NI */ +#define CSB_VLD 0100000 /* valid data NI */ +#define CSB_ECLR (CSB_GOE | CSB_DTE | CSB_MTE | CSB_RPE | CSB_MPE | CSB_LPE) +#define CSB_ERR (CSB_ECLR | CSB_DVOF | CSB_OFFL) +#define CSB_RW CSB_TEST +#define CSB_MBZ (CSB_DTE | CSB_RPE | CSB_MPE | CSB_LPE | CSB_OVFU |\ + CSB_PBIT | CSB_NRDY | CSB_LA180 | CSB_VLD) + +/* LPBA (765404) */ + +/* LPBC (765506) */ + +#define BC_MASK 0007777 /* <15:12> MBZ */ + +/* LPPAGC (765510) */ + +#define PAGC_MASK 0007777 /* <15:12> MBZ */ + +/* LPRDAT (765512) */ + +#define RDAT_MASK 0007777 /* <15:12> MBZ */ + +/* LPCOLC/LPCBUF (765514) */ + +/* LPCSUM/LPPDAT (765516) */ + +extern d10 *M; /* main memory */ +extern int32 int_req; + +int32 lpcsa = 0; /* control/status A */ +int32 lpcsb = 0; /* control/status B */ +int32 lpba = 0; /* bus address */ +int32 lpbc = 0; /* byte count */ +int32 lppagc = 0; /* page count */ +int32 lprdat = 0; /* RAM data */ +int32 lpcbuf = 0; /* character buffer */ +int32 lpcolc = 0; /* column count */ +int32 lppdat = 0; /* printer data */ +int32 lpcsum = 0; /* checksum */ +int32 dvptr = 0; /* davfu pointer */ +int32 dvlnt = 0; /* davfu length */ +int32 lp20_irq = 0; /* int request */ +int32 lp20_stopioe = 0; /* stop on error */ +int16 txram[TX_SIZE] = { 0 }; /* translation RAM */ +int16 davfu[DV_SIZE] = { 0 }; /* DAVFU */ + +DEVICE lp20_dev; +t_stat lp20_rd (int32 *data, int32 pa, int32 access); +t_stat lp20_wr (int32 data, int32 pa, int32 access); +int32 lp20_inta (void); +t_stat lp20_svc (UNIT *uptr); +t_stat lp20_reset (DEVICE *dptr); +t_stat lp20_attach (UNIT *uptr, char *ptr); +t_stat lp20_detach (UNIT *uptr); +t_stat lp20_clear_vfu (UNIT *uptr, int32 val, char *cptr, void *desc); +t_bool lp20_print (int32 c); +t_bool lp20_adv (int32 c, t_bool advdvu); +t_bool lp20_davfu (int32 c); +void update_lpcs (int32 flg); + +/* LP data structures + + lp20_dev LPT device descriptor + lp20_unit LPT unit descriptor + lp20_reg LPT register list +*/ + +DIB lp20_dib = { + IOBA_LP20, IOLN_LP20, &lp20_rd, &lp20_wr, + 1, IVCL (LP20), VEC_LP20, { &lp20_inta } + }; + +UNIT lp20_unit = { + UDATA (&lp20_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lp20_reg[] = { + { ORDATA (LPCSA, lpcsa, 16) }, + { ORDATA (LPCSB, lpcsb, 16) }, + { ORDATA (LPBA, lpba, 16) }, + { ORDATA (LPBC, lpbc, 12) }, + { ORDATA (LPPAGC, lppagc, 12) }, + { ORDATA (LPRDAT, lprdat, 12) }, + { ORDATA (LPCBUF, lpcbuf, 8) }, + { ORDATA (LPCOLC, lpcolc, 8) }, + { ORDATA (LPPDAT, lppdat, 8) }, + { ORDATA (LPCSUM, lpcsum, 8) }, + { ORDATA (DVPTR, dvptr, 7) }, + { ORDATA (DVLNT, dvlnt, 7), REG_RO + REG_NZ }, + { FLDATA (INT, int_req, INT_V_LP20) }, + { FLDATA (IRQ, lp20_irq, 0) }, + { FLDATA (ERR, lpcsa, CSR_V_ERR) }, + { FLDATA (DONE, lpcsa, CSR_V_DONE) }, + { FLDATA (IE, lpcsa, CSR_V_IE) }, + { DRDATA (POS, lp20_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lp20_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lp20_stopioe, 0) }, + { BRDATA (TXRAM, txram, 8, 12, TX_SIZE) }, + { BRDATA (DAVFU, davfu, 8, 12, DV_SIZE) }, + { ORDATA (DEVADDR, lp20_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, lp20_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB lp20_mod[] = { + { UNIT_DUMMY, 0, NULL, "VFUCLEAR", &lp20_clear_vfu }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE lp20_dev = { + "LP20", &lp20_unit, lp20_reg, lp20_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lp20_reset, + NULL, &lp20_attach, &lp20_detach, + &lp20_dib, DEV_DISABLE | DEV_UBUS + }; + +/* Line printer routines + + lp20_rd I/O page read + lp20_wr I/O page write + lp20_svc process event (printer ready) + lp20_reset process reset + lp20_attach process attach + lp20_detach process detach +*/ + +t_stat lp20_rd (int32 *data, int32 pa, int32 access) +{ +update_lpcs (0); /* update csr's */ +switch ((pa >> 1) & 07) { /* case on PA<3:1> */ + + case 00: /* LPCSA */ + *data = lpcsa = lpcsa & ~CSA_MBZ; + break; + + case 01: /* LPCSB */ + *data = lpcsb = lpcsb & ~CSB_MBZ; + break; + + case 02: /* LPBA */ + *data = lpba; + break; + + case 03: /* LPBC */ + *data = lpbc = lpbc & BC_MASK; + break; + + case 04: /* LPPAGC */ + *data = lppagc = lppagc & PAGC_MASK; + break; + + case 05: /* LPRDAT */ + *data = lprdat = lprdat & RDAT_MASK; + break; + + case 06: /* LPCOLC/LPCBUF */ + *data = (lpcolc << 8) | lpcbuf; + break; + + case 07: /* LPCSUM/LPPDAT */ + *data = (lpcsum << 8) | lppdat; + break; + } /* end case PA */ + +return SCPE_OK; +} + +t_stat lp20_wr (int32 data, int32 pa, int32 access) +{ +update_lpcs (0); /* update csr's */ +switch ((pa >> 1) & 07) { /* case on PA<3:1> */ + + case 00: /* LPCSA */ + if (access == WRITEB) data = (pa & 1)? + (lpcsa & 0377) | (data << 8): (lpcsa & ~0377) | data; + if (data & CSA_ECLR) { /* error clear? */ + lpcsa = (lpcsa | CSA_DONE) & ~CSA_GO; /* set done, clr go */ + lpcsb = lpcsb & ~CSB_ECLR; /* clear err */ + sim_cancel (&lp20_unit); /* cancel I/O */ + } + if (data & CSA_INIT) lp20_reset (&lp20_dev); /* init? */ + if (data & CSA_GO) { /* go set? */ + if ((lpcsa & CSA_GO) == 0) { /* not set before? */ + if (lpcsb & CSB_ERR) lpcsb = lpcsb | CSB_GOE; + lpcsum = 0; /* clear checksum */ + sim_activate (&lp20_unit, lp20_unit.wait); + } + } + else sim_cancel (&lp20_unit); /* go clr, stop DMA */ + lpcsa = (lpcsa & ~CSA_RW) | (data & CSA_RW); + break; + + case 01: /* LPCSB */ + break; /* ignore writes to TEST */ + + case 02: /* LPBA */ + if (access == WRITEB) data = (pa & 1)? + (lpba & 0377) | (data << 8): (lpba & ~0377) | data; + lpba = data; + break; + + case 03: /* LPBC */ + if (access == WRITEB) data = (pa & 1)? + (lpbc & 0377) | (data << 8): (lpbc & ~0377) | data; + lpbc = data & BC_MASK; + lpcsa = lpcsa & ~CSA_DONE; + break; + + case 04: /* LPPAGC */ + if (access == WRITEB) data = (pa & 1)? + (lppagc & 0377) | (data << 8): (lppagc & ~0377) | data; + lppagc = data & PAGC_MASK; + break; + + case 05: /* LPRDAT */ + if (access == WRITEB) data = (pa & 1)? + (lprdat & 0377) | (data << 8): (lprdat & ~0377) | data; + lprdat = data & RDAT_MASK; + txram[lpcbuf & TX_AMASK] = lprdat; /* load RAM */ + break; + + case 06: /* LPCOLC/LPCBUF */ + if ((access == WRITEB) && (pa & 1)) /* odd byte */ + lpcolc = data & 0377; + else { + lpcbuf = data & 0377; /* even byte, word */ + if (access == WRITE) lpcolc = (data >> 8) & 0377; + } + break; + + case 07: /* LPCSUM/LPPDAT */ + break; /* read only */ + } /* end case PA */ + +update_lpcs (0); +return SCPE_OK; +} + +/* Line printer service + + The translation RAM case table is derived from the LP20 spec and + verified against the LP20 RAM simulator in TOPS10 7.04 LPTSPL. + The equations are: + + flags := inter, delim, xlate, paper, delim_hold (from CSRA) + actions : = print_input, print_xlate, davfu_action, interrupt + + if (inter) { + if (!xlate || delim || delim_hold) interrupt; + else if (paper) davfu_action; + else print_xlate; + } + else if (paper) { + if (xlate || delim || delim_hold) davfu_action; + else print_input; + } + else { + if (xlate || delim || delim_hold) print_xlate; + else print_input; + } +*/ + +t_stat lp20_svc (UNIT *uptr) +{ +int32 fnc, i, tbc, temp, txst; +int32 dvld = -2; /* must be even */ +uint16 wd10; +t_bool cont; +a10 ba; + +static const uint32 txcase[32] = { + TX_CHR, TX_RAM, TX_CHR, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU, + TX_RAM, TX_RAM, TX_DVU, TX_DVU, TX_RAM, TX_RAM, TX_DVU, TX_DVU, + TX_INT, TX_INT, TX_INT, TX_INT, TX_RAM, TX_INT, TX_DVU, TX_INT, + TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT, TX_INT + }; + +lpcsa = lpcsa & ~CSA_GO; +ba = CSA_GETUAE (lpcsa) | lpba; +fnc = CSA_GETFNC (lpcsa); +tbc = 010000 - lpbc; +if (((fnc & FNC_INTERNAL) == 0) && ((lp20_unit.flags & UNIT_ATT) == 0)) { + update_lpcs (CSA_ERR); + return IORETURN (lp20_stopioe, SCPE_UNATT); + } +if ((fnc == FNC_PR) && (dvlnt == 0)) { + update_lpcs (CSA_ERR); + return SCPE_OK; + } + +for (i = 0, cont = TRUE; (i < tbc) && cont; ba++, i++) { + if (Map_ReadW (ba, 2, &wd10)) { /* get word, err? */ + lpcsb = lpcsb | CSB_MTE; /* set NXM error */ + update_lpcs (CSA_ERR); /* set done */ + break; + } + lpcbuf = (wd10 >> ((ba & 1)? 8: 0)) & 0377; /* get character */ + lpcsum = (lpcsum + lpcbuf) & 0377; /* add into checksum */ + switch (fnc) { /* switch on function */ + +/* Translation RAM load */ + + case FNC_RAM: /* RAM load */ + txram[(i >> 1) & TX_AMASK] = wd10 & TX_DMASK; + break; + +/* DAVFU RAM load. The DAVFU RAM is actually loaded in bytes, delimited by + a start (354 to 356) and stop (357) byte pair. If the number of bytes + loaded is odd, or no bytes are loaded, the DAVFU is invalid. +*/ + + case FNC_DVU: /* DVU load */ + if ((lpcbuf >= 0354) && (lpcbuf <= 0356)) /* start DVU load? */ + dvld = dvlnt = 0; /* reset lnt */ + else if (lpcbuf == 0357) { /* stop DVU load? */ + dvptr = 0; /* reset ptr */ + if (dvld & 1) dvlnt = 0; /* if odd, invalid */ + } + else if (dvld == 0) { /* even state? */ + temp = lpcbuf & DV_DMASK; + dvld = 1; + } + else if (dvld == 1) { /* odd state? */ + if (dvlnt < DV_SIZE) davfu[dvlnt++] = + temp | ((lpcbuf & DV_DMASK) << 6); + dvld = 0; + } + break; + +/* Print characters */ + + case FNC_PR: /* print */ + lprdat = txram[lpcbuf]; /* get RAM char */ + txst = (TX_GETFL (lprdat) << 1) | /* get state */ + ((lpcsa & CSA_DELH)? 1: 0); /* plus delim hold */ + if (lprdat & TX_DELH) lpcsa = lpcsa | CSA_DELH; + else lpcsa = lpcsa & ~CSA_DELH; + lpcsa = lpcsa & ~CSA_UNDF; /* assume char ok */ + switch (txcase[txst]) { /* case on state */ + + case TX_CHR: /* take char */ + cont = lp20_print (lpcbuf); + break; + + case TX_RAM: /* take translation */ + cont = lp20_print (lprdat); + break; + + case TX_DVU: /* DAVFU action */ + if (lprdat & TX_SLEW) + cont = lp20_adv (lprdat & TX_VMASK, TRUE); + else cont = lp20_davfu (lprdat & TX_VMASK); + break; + + case TX_INT: /* interrupt */ + lpcsa = lpcsa | CSA_UNDF; /* set flag */ + cont = FALSE; /* force stop */ + break; + } /* end case char state */ + break; + + case FNC_TST: /* test */ + break; + } /* end case function */ + } /* end for */ +lpba = ba & 0177777; +lpcsa = (lpcsa & ~CSA_UAE) | ((ba >> (16 - CSA_V_UAE)) & CSA_UAE); +lpbc = (lpbc + i) & BC_MASK; +if (lpbc) update_lpcs (CSA_MBZ); /* intr, but not done */ +else update_lpcs (CSA_DONE); /* intr and done */ +if ((fnc == FNC_PR) && ferror (lp20_unit.fileref)) { + perror ("LP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Print routines + + lp20_print print a character + lp20_adv advance n lines + lp20_davfu advance to channel on VFU + + Return TRUE to continue printing, FALSE to stop +*/ + +t_bool lp20_print (int32 c) +{ +t_bool r = TRUE; +int32 i, rpt = 1; + +lppdat = c & 0177; /* mask char to 7b */ +if (lppdat == 000) return TRUE; /* NUL? no op */ +if (lppdat == 012) return lp20_adv (1, TRUE); /* LF? adv carriage */ +if (lppdat == 014) return lp20_davfu (DV_TOF); /* FF? top of form */ +if (lppdat == 015) lpcolc = 0; /* CR? reset col cntr */ +else if (lppdat == 011) { /* TAB? simulate */ + lppdat = ' '; /* with spaces */ + if (lpcolc >= 128) { + r = lp20_adv (1, TRUE); /* eol? adv carriage */ + rpt = 8; /* adv to col 9 */ + } + else rpt = 8 - (lpcolc & 07); /* else adv 1 to 8 */ + } +else { + if (lppdat < 040) lppdat = ' '; /* cvt non-prnt to spc */ + if (lpcolc >= LP_WIDTH) /* line full? */ + r = lp20_adv (1, TRUE); /* adv carriage */ + } +for (i = 0; i < rpt; i++) + fputc (lppdat, lp20_unit.fileref); +lp20_unit.pos = ftell (lp20_unit.fileref); +lpcolc = lpcolc + rpt; +return r; +} + +t_bool lp20_adv (int32 cnt, t_bool dvuadv) +{ +int32 i; + +if (cnt == 0) return TRUE; +lpcolc = 0; /* reset col cntr */ +for (i = 0; i < cnt; i++) + fputc ('\n', lp20_unit.fileref); +lp20_unit.pos = ftell (lp20_unit.fileref); /* print 'n' newlines */ +if (dvuadv) dvptr = (dvptr + cnt) % dvlnt; /* update DAVFU ptr */ +if (davfu[dvptr] & (1 << DV_TOF)) { /* at top of form? */ + if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */ + lpcsa = lpcsa & ~CSA_PZRO; /* update status */ + return TRUE; + } + else { + lpcsa = lpcsa | CSA_PZRO; /* stop if zero */ + return FALSE; + } + } +return TRUE; +} + +t_bool lp20_davfu (int32 cnt) +{ +int i; + +if (cnt > DV_MAX) cnt = 7; /* inval chan? */ +for (i = 0; i < dvlnt; i++) { /* search DAVFU */ + dvptr = dvptr + 1; /* adv DAVFU ptr */ + if (dvptr >= dvlnt) dvptr = 0; /* wrap at end */ + if (davfu[dvptr] & (1 << cnt)) { /* channel stop set? */ + if (cnt) return lp20_adv (i + 1, FALSE); /* ~TOF, adv */ + if (lpcolc) lp20_adv (1, FALSE); /* TOF, need newline? */ + fputc ('\f', lp20_unit.fileref); /* print form feed */ + lp20_unit.pos = ftell (lp20_unit.fileref); + if (lppagc = (lppagc - 1) & PAGC_MASK) { /* decr page cntr */ + lpcsa = lpcsa & ~CSA_PZRO; /* update status */ + return TRUE; + } + else { + lpcsa = lpcsa | CSA_PZRO; /* stop if zero */ + return FALSE; + } + } + } /* end for */ +dvlnt = 0; /* DAVFU error */ +return FALSE; +} + +/* Update LPCSA, optionally request interrupt */ + +void update_lpcs (int32 flg) +{ +if (flg) lp20_irq = 1; /* set int req */ +lpcsa = (lpcsa | flg) & ~(CSA_MBZ | CSA_ERR | CSA_ONL | CSA_DVON); +lpcsb = (lpcsb | CSB_OFFL | CSB_DVOF) & ~CSB_MBZ; +if (lp20_unit.flags & UNIT_ATT) { + lpcsa = lpcsa | CSA_ONL; + lpcsb = lpcsb & ~CSB_OFFL; + } +else lpcsa = lpcsa & ~CSA_DONE; +if (dvlnt) { + lpcsa = lpcsa | CSA_DVON; + lpcsb = lpcsb & ~CSB_DVOF; + } +if (lpcsb & CSB_ERR) lpcsa = lpcsa | CSA_ERR; +if ((lpcsa & CSA_IE) && lp20_irq) int_req = int_req | INT_LP20; +else int_req = int_req & ~INT_LP20; +return; +} + +/* Acknowledge interrupt (clear internal request) */ + +int32 lp20_inta (void) +{ +lp20_irq = 0; /* clear int req */ +return lp20_dib.vec; +} + +t_stat lp20_reset (DEVICE *dptr) +{ +lpcsa = CSA_DONE; +lpcsb = 0; +lpba = lpbc = lppagc = lpcolc = 0; /* clear registers */ +lprdat = lppdat = lpcbuf = lpcsum = 0; +lp20_irq = 0; /* clear int req */ +dvptr = 0; /* reset davfu ptr */ +sim_cancel (&lp20_unit); /* deactivate unit */ +update_lpcs (0); /* update status */ +return SCPE_OK; +} + +t_stat lp20_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); /* attach file */ +if (lpcsa & CSA_ONL) return reason; /* just file chg? */ +if (sim_is_active (&lp20_unit)) update_lpcs (0); /* busy? no int */ +else update_lpcs (CSA_MBZ); /* interrupt */ +return reason; +} + +t_stat lp20_detach (UNIT *uptr) +{ +t_stat reason; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +reason = detach_unit (uptr); +sim_cancel (&lp20_unit); +lpcsa = lpcsa & ~CSA_GO; +update_lpcs (CSA_MBZ); +return reason; +} + +t_stat lp20_clear_vfu (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int i; + +if (!get_yn ("Clear DAVFU? [N]", FALSE)) return SCPE_OK; +for (i = 0; i < DV_SIZE; i++) davfu[i] = 0; +dvlnt = dvptr = 0; +update_lpcs (0); +return SCPE_OK; +} diff --git a/PDP10/pdp10_mdfp.c b/PDP10/pdp10_mdfp.c new file mode 100644 index 0000000..348c990 --- /dev/null +++ b/PDP10/pdp10_mdfp.c @@ -0,0 +1,765 @@ +/* pdp10_mdfp.c: PDP-10 multiply/divide and floating point simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 2-Apr-04 RMS Fixed bug in floating point unpack + Fixed bug in FIXR (found by Phil Stone, fixed by + Chris Smith) + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 10-Aug-01 RMS Removed register in declarations + + Instructions handled in this module: + imul integer multiply + idiv integer divide + mul multiply + div divide + dmul double precision multiply + ddiv double precision divide + fad(r) floating add (and round) + fsb(r) floating subtract (and round) + fmp(r) floating multiply (and round) + fdv(r) floating divide and round + fsc floating scale + fix(r) floating to fixed (and round) + fltr fixed to floating and round + dfad double precision floating add/subtract + dfmp double precision floating multiply + dfdv double precision floating divide + + The PDP-10 stores double (quad) precision integers in sequential + AC's or memory locations. Integers are stored in 2's complement + form. Only the sign of the high order word matters; the signs + in low order words are ignored on input and set to the sign of + the result on output. Quad precision integers exist only in the + AC's as the result of a DMUL or the dividend of a DDIV. + + 0 00000000011111111112222222222333333 + 0 12345678901234567890123456789012345 + +-+-----------------------------------+ + |S| high order integer | AC(n), A + +-+-----------------------------------+ + |S| low order integer | AC(n + 1), A + 1 + +-+-----------------------------------+ + |S| low order integer | AC(n + 2) + +-+-----------------------------------+ + |S| low order integer | AC(n + 3) + +-+-----------------------------------+ + + The PDP-10 supports two floating point formats: single and double + precision. In both, the exponent is 8 bits, stored in excess + 128 notation. The fraction is expected to be normalized. A + single precision floating point number has 27 bits of fraction; + a double precision number has 62 bits of fraction (the sign + bit of the second word is ignored and is set to zero). + + In a negative floating point number, the exponent is stored in + one's complement form, the fraction in two's complement form. + + 0 00000000 011111111112222222222333333 + 0 12345678 901234567890123456789012345 + +-+--------+---------------------------+ + |S|exponent| high order fraction | AC(n), A + +-+--------+---------------------------+ + |0| low order fraction | AC(n + 1), A + 1 + +-+------------------------------------+ + + Note that treatment of the sign is different for double precision + integers and double precision floating point. DMOVN (implemented + as an inline macro) follows floating point conventions. + + The original PDP-10 CPU (KA10) used a different format for double + precision numbers and included certain instructions to make + software support easier. These instructions were phased out in + the KL10 and KS10 and are treated as MUUO's. + + The KL10 added extended precision (11-bit exponent) floating point + format (so-called G floating). These instructions were not + implemented in the KS10 and are treated as MUUO's. +*/ + +#include "pdp10_defs.h" +#include + +typedef struct { /* unpacked fp number */ + int32 sign; /* sign */ + int32 exp; /* exponent */ + t_uint64 fhi; /* fraction high */ + t_uint64 flo; /* for double prec */ + } UFP; + +#define MSK32 0xFFFFFFFF +#define FIT27 (DMASK - 0x07FFFFFF) +#define FIT32 (DMASK - MSK32) +#define SFRC TRUE /* frac 2's comp */ +#define AFRC FALSE /* frac abs value */ + +/* In packed floating point number */ + +#define FP_BIAS 0200 /* exponent bias */ +#define FP_N_FHI 27 /* # of hi frac bits */ +#define FP_V_FHI 0 /* must be zero */ +#define FP_M_FHI 0000777777777 +#define FP_N_EXP 8 /* # of exp bits */ +#define FP_V_EXP (FP_V_FHI + FP_N_FHI) +#define FP_M_EXP 0377 +#define FP_V_SIGN (FP_V_EXP + FP_N_EXP) /* sign */ +#define FP_N_FLO 35 /* # of lo frac bits */ +#define FP_V_FLO 0 /* must be zero */ +#define FP_M_FLO 0377777777777 +#define GET_FPSIGN(x) ((int32) (((x) >> FP_V_SIGN) & 1)) +#define GET_FPEXP(x) ((int32) (((x) >> FP_V_EXP) & FP_M_EXP)) +#define GET_FPHI(x) ((x) & FP_M_FHI) +#define GET_FPLO(x) ((x) & FP_M_FLO) + +/* In unpacked floating point number */ + +#define FP_N_GUARD 1 /* # of guard bits */ +#define FP_V_UFLO FP_N_GUARD /* <35:1> */ +#define FP_V_URNDD (FP_V_UFLO - 1) /* dp round bit */ +#define FP_V_UFHI (FP_V_UFLO + FP_N_FLO) /* <62:36> */ +#define FP_V_URNDS (FP_V_UFHI - 1) /* sp round bit */ +#define FP_V_UCRY (FP_V_UFHI + FP_N_FHI) /* <63> */ +#define FP_V_UNORM (FP_V_UCRY - 1) /* normalized bit */ +#define FP_UFHI 0x7FFFFFF000000000 +#define FP_UFLO 0x0000000FFFFFFFFE +#define FP_UFRAC 0x7FFFFFFFFFFFFFFE +#define FP_URNDD 0x0000000000000001 +#define FP_URNDS 0x0000000800000000 +#define FP_UNORM 0x4000000000000000 +#define FP_UCRY 0x8000000000000000 +#define FP_ONES 0xFFFFFFFFFFFFFFFF + +#define UNEG(x) ((~x) + 1) +#define DUNEG(x) x.flo = UNEG (x.flo); x.fhi = ~x.fhi + (x.flo == 0) + +extern d10 *ac_cur; /* current AC block */ +extern int32 flags; /* flags */ +void mul (d10 a, d10 b, d10 *rs); +void funpack (d10 h, d10 l, UFP *r, t_bool sgn); +void fnorm (UFP *r, t_int64 rnd); +d10 fpack (UFP *r, d10 *lo, t_bool fdvneg); + +/* Integer multiply - checked against KS-10 ucode */ + +d10 imul (d10 a, d10 b) +{ +d10 rs[2]; + +if ((a == SIGN) && (b == SIGN)) { /* KS10 hack */ + SETF (F_AOV | F_T1); /* -2**35 squared */ + return SIGN; + } +mul (a, b, rs); /* mpy, dprec result */ +if (rs[0] && (rs[0] != ONES)) { /* high not all sign? */ + rs[1] = TSTS (a ^ b)? SETS (rs[1]): CLRS (rs[1]); /* set sign */ + SETF (F_AOV | F_T1); /* overflow */ + } +return rs[1]; +} + +/* Integer divide, return quotient, remainder - checked against KS10 ucode + The KS10 does not recognize -2^35/-1 as an error. Instead, it produces + 2^35 (that is, -2^35) as the incorrect result. +*/ + +t_bool idiv (d10 a, d10 b, d10 *rs) +{ +d10 dvd = ABS (a); /* make ops positive */ +d10 dvr = ABS (b); + +if (dvr == 0) { /* divide by 0? */ + SETF (F_DCK | F_AOV | F_T1); /* set flags, return */ + return FALSE; + } +rs[0] = dvd / dvr; /* get quotient */ +rs[1] = dvd % dvr; /* get remainder */ +if (TSTS (a ^ b)) rs[0] = NEG (rs[0]); /* sign of result */ +if (TSTS (a)) rs[1] = NEG (rs[1]); /* sign of remainder */ +return TRUE; +} + +/* Multiply, return double precision result - checked against KS10 ucode */ + +void mul (d10 s1, d10 s2, d10 *rs) +{ +t_uint64 a = ABS (s1); +t_uint64 b = ABS (s2); +t_uint64 t, u, r; + +if ((a == 0) || (b == 0)) { /* operand = 0? */ + rs[0] = rs[1] = 0; /* result 0 */ + return; + } +if ((a & FIT32) || (b & FIT32)) { /* fit in 64b? */ + t = a >> 18; /* no, split in half */ + a = a & RMASK; /* "dp" multiply */ + u = b >> 18; + b = b & RMASK; + r = (a * b) + (((a * u) + (b * t)) << 18); /* low is only 35b */ + rs[0] = ((t * u) << 1) + (r >> 35); /* so lsh hi 1 */ + rs[1] = r & MMASK; + } +else { + r = a * b; /* fits, native mpy */ + rs[0] = r >> 35; /* split at bit 35 */ + rs[1] = r & MMASK; + } + +if (TSTS (s1 ^ s2)) { MKDNEG (rs); } /* result -? */ +else if (TSTS (rs[0])) { /* result +, 2**70? */ + SETF (F_AOV | F_T1); /* overflow */ + rs[1] = SETS (rs[1]); /* consistent - */ + } +return; +} + +/* Divide, return quotient and remainder - checked against KS10 ucode + Note that the initial divide check catches the case -2^70/-2^35; + thus, the quotient can have at most 35 bits. +*/ + +t_bool divi (int32 ac, d10 b, d10 *rs) +{ +int32 p1 = ADDAC (ac, 1); +d10 dvr = ABS (b); /* make divr positive */ +t_int64 t; +int32 i; +d10 dvd[2]; + +dvd[0] = AC(ac); /* divd high */ +dvd[1] = CLRS (AC(p1)); /* divd lo, clr sgn */ +if (TSTS (AC(ac))) { DMOVN (dvd); } /* make divd positive */ +if (dvd[0] >= dvr) { /* divide fail? */ + SETF (F_AOV | F_DCK | F_T1); /* set flags, return */ + return FALSE; + } +if (dvd[0] & FIT27) { /* fit in 63b? */ + for (i = 0, rs[0] = 0; i < 35; i++) { /* 35 quotient bits */ + dvd[0] = (dvd[0] << 1) | ((dvd[1] >> 34) & 1); + dvd[1] = (dvd[1] << 1) & MMASK; /* shift dividend */ + rs[0] = rs[0] << 1; /* shift quotient */ + if (dvd[0] >= dvr) { /* subtract work? */ + dvd[0] = dvd[0] - dvr; /* quo bit is 1 */ + rs[0] = rs[0] + 1; + } + } + rs[1] = dvd[0]; /* store remainder */ + } +else { + t = (dvd[0] << 35) | dvd[1]; /* concatenate */ + rs[0] = t / dvr; /* quotient */ + rs[1] = t % dvr; /* remainder */ + } +if (TSTS (AC(ac) ^ b)) rs[0] = NEG (rs[0]); /* sign of result */ +if (TSTS (AC(ac))) rs[1] = NEG (rs[1]); /* sign of remainder */ +return TRUE; +} + +/* Double precision multiply. This is done the old fashioned way. Cross + product multiplies would be a lot faster but would require more code. +*/ + +void dmul (int32 ac, d10 *mpy) +{ +int32 p1 = ADDAC (ac, 1); +int32 p2 = ADDAC (ac, 2); +int32 p3 = ADDAC (ac, 3); +int32 i; +d10 mpc[2], sign; + +mpc[0] = AC(ac); /* mplcnd hi */ +mpc[1] = CLRS (AC(p1)); /* mplcnd lo, clr sgn */ +sign = mpc[0] ^ mpy[0]; /* sign of result */ +if (TSTS (mpc[0])) { DMOVN (mpc); } /* get abs (mpcnd) */ +if (TSTS (mpy[0])) { DMOVN (mpy); } /* get abs (mpyer) */ +else mpy[1] = CLRS (mpy[1]); /* clear mpy lo sign */ +AC(ac) = AC(p1) = AC(p2) = AC(p3) = 0; /* clear AC's */ +if (((mpy[0] | mpy[1]) == 0) || ((mpc[0] | mpc[1]) == 0)) return; +for (i = 0; i < 71; i++) { /* 71 mpyer bits */ + if (i) { /* shift res, mpy */ + AC(p3) = (AC(p3) >> 1) | ((AC(p2) & 1) << 34); + AC(p2) = (AC(p2) >> 1) | ((AC(p1) & 1) << 34); + AC(p1) = (AC(p1) >> 1) | ((AC(ac) & 1) << 34); + AC(ac) = AC(ac) >> 1; + mpy[1] = (mpy[1] >> 1) | ((mpy[0] & 1) << 34); + mpy[0] = mpy[0] >> 1; + } + if (mpy[1] & 1) { /* if mpy lo bit = 1 */ + AC(p1) = AC(p1) + mpc[1]; + AC(ac) = AC(ac) + mpc[0] + (TSTS (AC(p1) != 0)); + AC(p1) = CLRS (AC(p1)); + } + } +if (TSTS (sign)) { /* result minus? */ + AC(p3) = (-AC(p3)) & MMASK; /* quad negate */ + AC(p2) = (~AC(p2) + (AC(p3) == 0)) & MMASK; + AC(p1) = (~AC(p1) + (AC(p2) == 0)) & MMASK; + AC(ac) = (~AC(ac) + (AC(p1) == 0)) & DMASK; + } +else if (TSTS (AC(ac))) SETF (F_AOV | F_T1); /* wrong sign */ +if (TSTS (AC(ac))) { /* if result - */ + AC(p1) = SETS (AC(p1)); /* make signs consistent */ + AC(p2) = SETS (AC(p2)); + AC(p3) = SETS (AC(p3)); + } +return; +} + +/* Double precision divide - checked against KS10 ucode */ + +void ddiv (int32 ac, d10 *dvr) +{ +int32 i, cryin; +d10 sign, qu[2], dvd[4]; + +dvd[0] = AC(ac); /* save dividend */ +for (i = 1; i < 4; i++) dvd[i] = CLRS (AC(ADDAC (ac, i))); +sign = AC(ac) ^ dvr[0]; /* sign of result */ +if (TSTS (AC(ac))) { /* get abs (dividend) */ + for (i = 3, cryin = 1; i > 0; i--) { /* negate quad */ + dvd[i] = (~dvd[i] + cryin) & MMASK; /* comp + carry in */ + if (dvd[i]) cryin = 0; /* next carry in */ + } + dvd[0] = (~dvd[0] + cryin) & DMASK; + } +if (TSTS (dvr[0])) { DMOVN (dvr); } /* get abs (divisor) */ +else dvr[1] = CLRS (dvr[1]); +if (DCMPGE (dvd, dvr)) { /* will divide work? */ + SETF (F_AOV | F_DCK | F_T1); /* no, set flags */ + return; + } +qu[0] = qu[1] = 0; /* clear quotient */ +for (i = 0; i < 70; i++) { /* 70 quotient bits */ + dvd[0] = ((dvd[0] << 1) | ((dvd[1] >> 34) & 1)) & DMASK;; + dvd[1] = ((dvd[1] << 1) | ((dvd[2] >> 34) & 1)) & MMASK; + dvd[2] = ((dvd[2] << 1) | ((dvd[3] >> 34) & 1)) & MMASK; + dvd[3] = (dvd[3] << 1) & MMASK; /* shift dividend */ + qu[0] = (qu[0] << 1) | ((qu[1] >> 34) & 1); /* shift quotient */ + qu[1] = (qu[1] << 1) & MMASK; + if (DCMPGE (dvd, dvr)) { /* subtract work? */ + dvd[0] = dvd[0] - dvr[0] - (dvd[1] < dvr[1]); + dvd[1] = (dvd[1] - dvr[1]) & MMASK; /* do subtract */ + qu[1] = qu[1] + 1; /* set quotient bit */ + } + } +if (TSTS (sign) && (qu[0] | qu[1])) { MKDNEG (qu); } +if (TSTS (AC(ac)) && (dvd[0] | dvd[1])) { MKDNEG (dvd); } +AC(ac) = qu[0]; /* quotient */ +AC(ADDAC(ac, 1)) = qu[1]; +AC(ADDAC(ac, 2)) = dvd[0]; /* remainder */ +AC(ADDAC(ac, 3)) = dvd[1]; +return; +} + +/* Single precision floating add/subtract - checked against KS10 ucode + The KS10 shifts the smaller operand regardless of the exponent diff. + This code will not shift more than 63 places; shifts beyond that + cannot change the value of the smaller operand. + + If the signs of the operands are the same, the result sign is the + same as the source sign; the sign of the result fraction is actually + part of the data. If the signs of the operands are different, the + result sign is determined by the fraction sign. +*/ + +d10 fad (d10 op1, d10 op2, t_bool rnd, int32 inv) +{ +int32 ediff; +UFP a, b, t; + +if (inv) op2 = NEG (op2); /* subtract? -b */ +if (op1 == 0) funpack (op2, 0, &a, AFRC); /* a = 0? result is b */ +else if (op2 == 0) funpack (op1, 0, &a, AFRC); /* b = 0? result is a */ +else { + funpack (op1, 0, &a, SFRC); /* unpack operands */ + funpack (op2, 0, &b, SFRC); /* fracs are 2's comp */ + ediff = a.exp - b.exp; /* get exp diff */ + if (ediff < 0) { /* a < b? switch */ + t = a; + a = b; + b = t; + ediff = -ediff; + } + if (ediff > 63) ediff = 63; /* cap diff at 63 */ + if (ediff) b.fhi = (t_int64) b.fhi >> ediff; /* shift b (signed) */ + a.fhi = a.fhi + b.fhi; /* add fractions */ + if (a.sign ^ b.sign) { /* add or subtract? */ + if (a.fhi & FP_UCRY) { /* subtract, frac -? */ + a.fhi = UNEG (a.fhi); /* complement result */ + a.sign = 1; /* result is - */ + } + else a.sign = 0; /* result is + */ + } + else { + if (a.sign) a.fhi = UNEG (a.fhi); /* add, src -? comp */ + if (a.fhi & FP_UCRY) { /* check for carry */ + a.fhi = a.fhi >> 1; /* flo won't be used */ + a.exp = a.exp + 1; + } + } + } +fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ +return fpack (&a, NULL, FALSE); +} + +/* Single precision floating multiply. Because the fractions are 27b, + a 64b multiply can be used for the fraction multiply. The 27b + fractions are positioned 0'frac'0000, resulting in 00'hifrac'0..0. + The extra 0 is accounted for by biasing the result exponent. +*/ + +#define FP_V_SPM (FP_V_UFHI - (32 - FP_N_FHI - 1)) +d10 fmp (d10 op1, d10 op2, t_bool rnd) +{ +UFP a, b; + +funpack (op1, 0, &a, AFRC); /* unpack operands */ +funpack (op2, 0, &b, AFRC); /* fracs are abs val */ +if ((a.fhi == 0) || (b.fhi == 0)) return 0; /* either 0? */ +a.sign = a.sign ^ b.sign; /* result sign */ +a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */ +a.fhi = (a.fhi >> FP_V_SPM) * (b.fhi >> FP_V_SPM); /* high 27b of result */ +fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ +return fpack (&a, NULL, FALSE); +} + +/* Single precision floating divide. Because the fractions are 27b, a + 64b divide can be used for the fraction divide. Note that 28b-29b + of fraction are developed; the code will do one special normalize to + make sure that the 28th bit is not lost. Also note the special + treatment of negative quotients with non-zero remainders; this + implements the note on p2-23 of the Processor Reference Manual. +*/ + +t_bool fdv (d10 op1, d10 op2, d10 *rs, t_bool rnd) +{ +UFP a, b; +t_uint64 savhi; +t_bool rem = FALSE; + +funpack (op1, 0, &a, AFRC); /* unpack operands */ +funpack (op2, 0, &b, AFRC); /* fracs are abs val */ +if (a.fhi >= 2 * b.fhi) { /* will divide work? */ + SETF (F_AOV | F_DCK | F_FOV | F_T1); + return FALSE; + } +if (savhi = a.fhi) { /* dvd = 0? quo = 0 */ + a.sign = a.sign ^ b.sign; /* result sign */ + a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */ + a.fhi = a.fhi / (b.fhi >> (FP_N_FHI + 1)); /* do divide */ + if (a.sign && (savhi != (a.fhi * (b.fhi >> (FP_N_FHI + 1))))) + rem = TRUE; /* KL/KS hack */ + a.fhi = a.fhi << (FP_V_UNORM - FP_N_FHI - 1); /* put quo in place */ + if ((a.fhi & FP_UNORM) == 0) { /* normalize 1b */ + a.fhi = a.fhi << 1; /* before masking */ + a.exp = a.exp - 1; + } + a.fhi = a.fhi & (FP_UFHI | FP_URNDS); /* mask quo to 28b */ + } +fnorm (&a, (rnd? FP_URNDS: 0)); /* normalize, round */ +*rs = fpack (&a, NULL, rem); /* pack result */ +return TRUE; +} + +/* Single precision floating scale. */ + +d10 fsc (d10 val, a10 ea) +{ +int32 sc = LIT8 (ea); +UFP a; + +if (val == 0) return 0; +funpack (val, 0, &a, AFRC); /* unpack operand */ +if (ea & RSIGN) a.exp = a.exp - sc; /* adjust exponent */ +else a.exp = a.exp + sc; +fnorm (&a, 0); /* renormalize */ +return fpack (&a, NULL, FALSE); /* pack result */ +} + +/* Float integer operand and round */ + +d10 fltr (d10 mb) +{ +UFP a; +d10 val = ABS (mb); + +a.sign = GET_FPSIGN (mb); /* get sign */ +a.exp = FP_BIAS + 36; /* initial exponent */ +a.fhi = val << (FP_V_UNORM - 35); /* left justify op */ +a.flo = 0; +fnorm (&a, FP_URNDS); /* normalize, round */ +return fpack (&a, NULL, FALSE); /* pack result */ +} + +/* Fix and truncate/round floating operand */ + +void fix (int32 ac, d10 mb, t_bool rnd) +{ +int32 sc; +t_uint64 so; +UFP a; + +funpack (mb, 0, &a, AFRC); /* unpack operand */ +if (a.exp > (FP_BIAS + FP_N_FHI + FP_N_EXP)) SETF (F_AOV | F_T1); +else if (a.exp < FP_BIAS) AC(ac) = 0; /* < 1/2? */ +else { + sc = FP_V_UNORM - (a.exp - FP_BIAS) + 1; + AC(ac) = a.fhi >> sc; + if (rnd) { + so = a.fhi << (64 - sc); + if (so >= (0x8000000000000000 + a.sign)) AC(ac) = AC(ac) + 1; + } + if (a.sign) AC(ac) = NEG (AC(ac)); + } +return; +} + +/* Double precision floating add/subtract + Since a.flo is 0, adding b.flo is just a copy - this is incorporated into + the denormalization step. If there's no denormalization, bflo is zero too. +*/ + +void dfad (int32 ac, d10 *rs, int32 inv) +{ +int32 p1 = ADDAC (ac, 1); +int32 ediff; +UFP a, b, t; + +if (inv) { DMOVN (rs); } /* subtract? -b */ +if ((AC(ac) | AC(p1)) == 0) funpack (rs[0], rs[1], &a, AFRC); + /* a == 0? sum = b */ +else if ((rs[0] | rs[1]) == 0) funpack (AC(ac), AC(p1), &a, AFRC); + /* b == 0? sum = a */ +else { + funpack (AC(ac), AC(p1), &a, SFRC); /* unpack operands */ + funpack (rs[0], rs[1], &b, SFRC); + ediff = a.exp - b.exp; /* get exp diff */ + if (ediff < 0) { /* a < b? switch */ + t = a; + a = b; + b = t; + ediff = -ediff; + } + if (ediff > 127) ediff = 127; /* cap diff at 127 */ + if (ediff > 63) { /* diff > 63? */ + a.flo = (t_int64) b.fhi >> (ediff - 64); /* b hi to a lo */ + b.fhi = b.sign? FP_ONES: 0; /* hi = all sign */ + } + else if (ediff) { /* diff <= 63 */ + a.flo = (b.flo >> ediff) | (b.fhi << (64 - ediff)); + b.fhi = (t_int64) b.fhi >> ediff; /* shift b (signed) */ + } + a.fhi = a.fhi + b.fhi; /* do add */ + if (a.sign ^ b.sign) { /* add or subtract? */ + if (a.fhi & FP_UCRY) { /* subtract, frac -? */ + DUNEG (a); /* complement result */ + a.sign = 1; /* result is - */ + } + else a.sign = 0; /* result is + */ + } + else { + if (a.sign) { DUNEG (a); }; /* add, src -? comp */ + if (a.fhi & FP_UCRY) { /* check for carry */ + a.fhi = a.fhi >> 1; /* flo won't be used */ + a.exp = a.exp + 1; + } + } + } +fnorm (&a, FP_URNDD); /* normalize, round */ +AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ +return; +} + +/* Double precision floating multiply + The 62b fractions are multiplied, with cross products, to produce a + 124b fraction with two leading and two trailing 0's. Because the + product has 2 leading 0's, instead of the normal 1, an extra + normalization step is needed. Accordingly, the exponent calculation + increments the result exponent, to compensate for normalization. +*/ + +void dfmp (int32 ac, d10 *rs) +{ +int32 p1 = ADDAC (ac, 1); +t_uint64 xh, xl, yh, yl, mid; +UFP a, b; + +funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */ +funpack (rs[0], rs[1], &b, AFRC); +if ((a.fhi == 0) || (b.fhi == 0)) { /* either 0? result 0 */ + AC(ac) = AC(p1) = 0; + return; + } +a.sign = a.sign ^ b.sign; /* result sign */ +a.exp = a.exp + b.exp - FP_BIAS + 1; /* result exponent */ +xh = a.fhi >> 32; /* split 62b fracs */ +xl = a.fhi & MSK32; /* into 32b halves */ +yh = b.fhi >> 32; +yl = b.fhi & MSK32; +a.fhi = xh * yh; /* hi xproduct */ +a.flo = xl * yl; /* low xproduct */ +mid = (xh * yl) + (yh * xl); /* fits in 64b */ +a.flo = a.flo + (mid << 32); /* add mid lo to lo */ +a.fhi = a.fhi + ((mid >> 32) & MSK32) + (a.flo < (mid << 32)); +fnorm (&a, FP_URNDD); /* normalize, round */ +AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ +return; +} + +/* Double precision floating divide + This algorithm develops a full 62 bits of quotient, plus one rounding + bit, in the low order 63b of a 64b number. To do this, we must assure + that the initial divide step generates a 1. If it would fail, shift + the dividend left and decrement the result exponent accordingly. +*/ + +void dfdv (int32 ac, d10 *rs) +{ +int32 p1 = ADDAC (ac, 1); +int32 i; +t_uint64 qu = 0; +UFP a, b; + +funpack (AC(ac), AC(p1), &a, AFRC); /* unpack operands */ +funpack (rs[0], rs[1], &b, AFRC); +if (a.fhi >= 2 * b.fhi) { /* will divide work? */ + SETF (F_AOV | F_DCK | F_FOV | F_T1); + return; + } +if (a.fhi) { /* dvd = 0? quo = 0 */ + a.sign = a.sign ^ b.sign; /* result sign */ + a.exp = a.exp - b.exp + FP_BIAS + 1; /* result exponent */ + if (a.fhi < b.fhi) { /* make sure initial */ + a.fhi = a.fhi << 1; /* divide step will work */ + a.exp = a.exp - 1; + } + for (i = 0; i < 63; i++) { /* 63b of quotient */ + qu = qu << 1; /* shift quotient */ + if (a.fhi >= b.fhi) { /* will div work? */ + a.fhi = a.fhi - b.fhi; /* sub, quo = 1 */ + qu = qu + 1; + } + a.fhi = a.fhi << 1; /* shift dividend */ + } + a.fhi = qu; + } +fnorm (&a, FP_URNDD); /* normalize, round */ +AC(ac) = fpack (&a, &AC(p1), FALSE); /* pack result */ +return; +} + +/* Unpack floating point operand */ + +void funpack (d10 h, d10 l, UFP *r, t_bool sgn) +{ +d10 fphi, fplo; + +r->sign = GET_FPSIGN (h); +r->exp = GET_FPEXP (h); +fphi = GET_FPHI (h); +fplo = GET_FPLO (l); +r->fhi = (fphi << FP_V_UFHI) | (fplo << FP_V_UFLO); +r->flo = 0; +if (r->sign) { + r->exp = r->exp ^ FP_M_EXP; /* 1s comp exp */ + if (sgn) { /* signed frac? */ + if (r->fhi) r->fhi = r->fhi | FP_UCRY; /* extend sign */ + else { + r->exp = r->exp + 1; + r->fhi = FP_UCRY | FP_UNORM; + } + } + else { /* abs frac */ + if (r->fhi) r->fhi = UNEG (r->fhi) & FP_UFRAC; + else { + r->exp = r->exp + 1; + r->fhi = FP_UNORM; + } + } + } +return; +} + +/* Normalize and optionally round floating point operand */ + +void fnorm (UFP *a, t_int64 rnd) +{ +int32 i; +static t_uint64 normmask[6] = { + 0x6000000000000000, 0x7800000000000000, 0x7F80000000000000, + 0x7FFF800000000000, 0x7FFFFFFF80000000, 0x7FFFFFFFFFFFFFFF + }; +static int32 normtab[7] = { 1, 2, 4, 8, 16, 32, 63 }; +extern a10 pager_PC; + +if (a->fhi & FP_UCRY) { /* carry set? */ + printf ("%%PDP-10 FP: carry bit set at normalization, PC = %o\n", pager_PC); + a->flo = (a->flo >> 1) | ((a->fhi & 1) << 63); /* try to recover */ + a->fhi = a->fhi >> 1; /* but root cause */ + a->exp = a->exp + 1; /* should be fixed! */ + } +if ((a->fhi | a->flo) == 0) { /* if fraction = 0 */ + a->sign = a->exp = 0; /* result is 0 */ + return; + } +while ((a->fhi & FP_UNORM) == 0) { /* normalized? */ + for (i = 0; i < 6; i++) { + if (a->fhi & normmask[i]) break; + } + a->fhi = (a->fhi << normtab[i]) | (a->flo >> (64 - normtab[i])); + a->flo = a->flo << normtab[i]; + a->exp = a->exp - normtab[i]; + } +if (rnd) { /* rounding? */ + a->fhi = a->fhi + rnd; /* add round const */ + if (a->fhi & FP_UCRY) { /* if carry out, */ + a->fhi = a->fhi >> 1; /* renormalize */ + a->exp = a->exp + 1; + } + } +return; +} + +/* Pack floating point result */ + +d10 fpack (UFP *r, d10 *lo, t_bool fdvneg) +{ +d10 val[2]; + +if (r->exp < 0) SETF (F_AOV | F_FOV | F_FXU | F_T1); +else if (r->exp > FP_M_EXP) SETF (F_AOV | F_FOV | F_T1); +val[0] = (((((d10) r->exp) & FP_M_EXP) << FP_V_EXP) | + ((r->fhi & FP_UFHI) >> FP_V_UFHI)) & DMASK; +if (lo) val[1] = ((r->fhi & FP_UFLO) >> FP_V_UFLO) & MMASK; +else val[1] = 0; +if (r->sign) { /* negate? */ + if (fdvneg) { /* fdvr special? */ + val[1] = ~val[1] & MMASK; /* 1's comp */ + val[0] = ~val[0] & DMASK; + } + else { DMOVN (val); } /* 2's comp */ + } +if (lo) *lo = val[1]; +return val[0]; +} diff --git a/PDP10/pdp10_pag.c b/PDP10/pdp10_pag.c new file mode 100644 index 0000000..07b5eeb --- /dev/null +++ b/PDP10/pdp10_pag.c @@ -0,0 +1,844 @@ +/* pdp10_pag.c: PDP-10 paging subsystem simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + pag KS10 pager + + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 02-Dec-01 RMS Fixed bug in ITS LPMR (found by Dave Conroy) + 21-Aug-01 RMS Fixed bug in ITS paging (found by Miriam Lennox) + Removed register from declarations + 19-May-01 RMS Added workaround for TOPS-20 V4.1 boot bug + 03-May-01 RMS Fixed bug in indirect page table pointer processing + 29-Apr-01 RMS Added CLRCSH for ITS, fixed LPMR + + The pager consists of a standard hardware part (the translation + tables) and an operating-system specific page table fill routine. + + There are two translation tables, one for executive mode and one + for user mode. Each table consists of 512 page table entries, + one for each page in the 18b virtual address space. Each pte + contains (in the hardware) a valid bit, a writeable bit, an + address space bit (executive or user), and a cacheable bit, plus + the physical page number corresponding to the virtual page. In + the simulator, the pte is expanded for rapid processing of normal + reads and writes. An expanded pte contains a valid bit, a writeable + bit, and the physical page number shifted left by the page size. + + Expanded pte meaning + 0 invalid + >0 read only + <0 read write + + There is a third, physical table, which is used in place of the + executive and user tables if paging is off. Its entries are always + valid and always writeable. + + To translate a virtual to physical address, the simulator uses + the virtual page number to index into the appropriate page table. + If the page table entry (pte) is not valid, the page fill routine + is called to see if the entry is merely not filled or is truly + inaccessible. If the pte is valid but not writeable, and the + reference is a write reference, the page fill routine is also + called to see if the reference can be resolved. + + The page fill routine is operating system dependent. Three styles + of paging are supported: + + TOPS10 known in the KS10 microcode as KI10 paging, + used by earlier versions of TOPS10 + TOPS20 known in the KS10 microcode as KL10 paging, + used by later versions of TOPS10, and TOPS20 + ITS used only by ITS + + TOPS10 vs TOPS20 is selected by a bit in the EBR; ITS paging is + "hardwired" (it required different microcode). +*/ + +#include "pdp10_defs.h" +#include + +/* Page table (contains expanded pte's) */ + +#define PTBL_ASIZE PAG_N_VPN +#define PTBL_MEMSIZE (1 << PTBL_ASIZE) /* page table size */ +#define PTBL_AMASK (PTBL_MEMSIZE - 1) +#define PTBL_M (1u << 31) /* must be sign bit */ +#define PTBL_V (1u << 30) +#define PTBL_MASK (PAG_PPN | PTBL_M | PTBL_V) + +/* NXM processing */ + +#define REF_V 0 /* ref is virt */ +#define REF_P 1 /* ref is phys */ +#define PF_OK 0 /* pfail ok */ +#define PF_TR 1 /* pfail trap */ + +extern d10 *M; +extern d10 acs[AC_NBLK * AC_NUM]; +extern d10 *ac_cur, *ac_prv, *last_pa; +extern a10 epta, upta; +extern int32 flags; +extern d10 pager_word; +extern int32 apr_flg; +extern d10 ebr, ubr, hsb; +extern d10 spt, cst, cstm, pur; +extern a10 dbr1, dbr2, dbr3, dbr4; +extern d10 pcst, quant; +extern t_bool paging; +extern UNIT cpu_unit; +extern jmp_buf save_env; +extern int32 test_int (void); +extern int32 pi_eval (void); + +int32 eptbl[PTBL_MEMSIZE]; /* exec page table */ +int32 uptbl[PTBL_MEMSIZE]; /* user page table */ +int32 physptbl[PTBL_MEMSIZE]; /* phys page table */ +int32 *ptbl_cur, *ptbl_prv; +int32 save_ea; + +int32 ptbl_fill (a10 ea, int32 *ptbl, int32 mode); +t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat pag_reset (DEVICE *dptr); +void pag_nxm (a10 pa, int32 phys, int32 trap); + +/* Pager data structures + + pag_dev pager device descriptor + pag_unit pager units + pager_reg pager register list +*/ + +UNIT pag_unit[] = { + { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) }, + { UDATA (NULL, UNIT_FIX, PTBL_MEMSIZE) } + }; + +REG pag_reg[] = { + { ORDATA (PANIC_EA, save_ea, PASIZE), REG_HRO }, + { NULL } + }; + +DEVICE pag_dev = { + "PAG", pag_unit, pag_reg, NULL, + 2, 8, PTBL_ASIZE, 1, 8, 32, + &pag_ex, &pag_dep, &pag_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* Memory read and write routines + + Read - read current or previous, read checking + ReadM - read current or previous, write checking + ReadE - read exec + ReadP - read physical + Write - write current or previous + WriteE - write exec + WriteP - write physical + AccChk - test accessibility of virtual address +*/ + +d10 Read (a10 ea, int32 prv) +{ +int32 pa, vpn, xpte; + +if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ +if (xpte == 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_RD); +pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ +if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ +return M[pa]; /* return data */ +} + +d10 ReadM (a10 ea, int32 prv) +{ +int32 pa, vpn, xpte; + +if (ea < AC_NUM) return (prv? ac_prv[ea]: ac_cur[ea]); /* AC request */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ +if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR); +pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ +if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ +return M[pa]; /* return data */ +} + +d10 ReadE (a10 ea) +{ +int32 pa, vpn, xpte; + +if (ea < AC_NUM) return AC(ea); /* AC? use current */ +if (!PAGING) return M[ea]; /* phys? no mapping */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = eptbl[vpn]; /* get exp pte, exec tbl */ +if (xpte == 0) xpte = ptbl_fill (ea, eptbl, PTF_RD); +pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ +if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ +return M[pa]; /* return data */ +} + +d10 ReadP (a10 ea) +{ +if (ea < AC_NUM) return AC(ea); /* AC request */ +if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */ +return M[ea]; /* return data */ +} + +void Write (a10 ea, d10 val, int32 prv) +{ +int32 pa, vpn, xpte; + +if (ea < AC_NUM) { /* AC request */ + if (prv) ac_prv[ea] = val; /* write AC */ + else ac_cur[ea] = val; + } +else { + vpn = PAG_GETVPN (ea); /* get page num */ + xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ + if (xpte >= 0) xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_WR); + pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ + if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ + else M[pa] = val; /* write data */ + } +return; +} + +void WriteE (a10 ea, d10 val) +{ +int32 pa, vpn, xpte; + +if (ea < AC_NUM) AC(ea) = val; /* AC? use current */ +else if (!PAGING) M[ea] = val; /* phys? no mapping */ +else { + vpn = PAG_GETVPN (ea); /* get page num */ + xpte = eptbl[vpn]; /* get exp pte, exec tbl */ + if (xpte >= 0) xpte = ptbl_fill (ea, eptbl, PTF_WR); + pa = PAG_XPTEPA (xpte, ea); /* calc phys addr */ + if (MEM_ADDR_NXM (pa)) pag_nxm (pa, REF_V, PF_TR); /* process nxm */ + else M[pa] = val; /* write data */ + } +return; +} + +void WriteP (a10 ea, d10 val) +{ +if (ea < AC_NUM) AC(ea) = val; /* AC request */ +else { + if (MEM_ADDR_NXM (ea)) pag_nxm (ea, REF_P, PF_TR); /* process nxm */ + M[ea] = val; /* memory */ + } +return; +} + +t_bool AccViol (a10 ea, int32 prv, int32 mode) +{ +int32 vpn, xpte; + +if (ea < AC_NUM) return FALSE; /* AC request */ +vpn = PAG_GETVPN (ea); /* get page num */ +xpte = prv? ptbl_prv[vpn]: ptbl_cur[vpn]; /* get exp pte */ +if ((xpte == 0) || ((mode & PTF_WR) && (xpte > 0))) /* not accessible? */ + xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, mode | PTF_MAP); +if (xpte) return FALSE; /* accessible */ +return TRUE; /* not accessible */ +} + +void pag_nxm (a10 pa, int32 phys, int32 trap) +{ +apr_flg = apr_flg | APRF_NXM; /* set APR flag */ +pi_eval (); /* eval intr */ +pager_word = PF_NXM | (phys? PF_NXMP: 0) | + (TSTF (F_USR)? PF_USER: 0) | ((d10) pa); +if (PAGING && trap) ABORT (PAGE_FAIL); /* trap? */ +return; +} + +/* Page table fill + + This routine is called if the page table is invalid, or on a write + reference if the page table is read only. If the access is allowed + it stores the pte in the page table entry and returns an expanded + pte for use by the caller. Otherwise, it generates a page fail. + + Notes: + - If called from the console, invalid references return a pte + of 0, and the page table entry is not filled. + - If called from MAP, invalid references return a pte of 0. The + page fail word is properly set up. +*/ + +#define PAGE_FAIL_TRAP if (mode & (PTF_CON | PTF_MAP)) return 0; \ + ABORT (PAGE_FAIL) +#define READPT(x,y) if (MEM_ADDR_NXM (y)) { \ + pag_nxm (y, REF_P, PF_OK); \ + PAGE_FAIL_TRAP; \ + } \ + x = ReadP (y) + +int32 ptbl_fill (a10 ea, int32 *tbl, int32 mode) +{ + +/* ITS paging is based on conventional page tables. ITS divides each address + space into a 128K high and low section, and uses different descriptor base + pointers (dbr) for each. ITS pages are twice the size of DEC standard; + therefore, the fill routine fills two page table entries and returns the pte + that maps the correct ITS half page. This allows the DEC paging macros to + be used in the normal path read-write routines. + + ITS has no MAP instruction, therefore, physical NXM traps are ok. +*/ + +if (Q_ITS) { /* ITS paging */ + int32 acc, decvpn, pte, vpn, ptead, xpte; + d10 ptewd; + + vpn = ITS_GETVPN (ea); /* get ITS pagno */ + if (tbl == uptbl) + ptead = ((ea & RSIGN)? dbr2: dbr1) + ((vpn >> 1) & 077); + else ptead = ((ea & RSIGN)? dbr3: dbr4) + ((vpn >> 1) & 077); + ptewd = ReadP (ptead); /* get PTE pair */ + pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK); + acc = ITS_GETACC (pte); /* get access */ + pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | + ((mode & PTF_WR)? PF_ITS_WRITE: 0) | (acc << PF_ITS_V_ACC); + if ((acc != ITS_ACC_NO) && (!(mode & PTF_WR) || (acc == ITS_ACC_RW))) { + pte = pte & ~PTE_ITS_AGE; /* clear age */ + if (vpn & 1) WriteP (ptead, (ptewd & LMASK) | pte); + else WriteP (ptead, (ptewd & RMASK) | (((d10) pte) << 18)); + xpte = ((pte & PTE_ITS_PPMASK) << ITS_V_PN) | PTBL_V | + ((acc == ITS_ACC_RW)? PTBL_M: 0); + decvpn = PAG_GETVPN (ea); /* get tlb idx */ + if (!(mode & PTF_CON)) { + tbl[decvpn & ~1] = xpte; /* map lo ITS page */ + tbl[decvpn | 1] = xpte + PAG_SIZE; /* map hi */ + } + return (xpte + ((decvpn & 1)? PAG_SIZE: 0)); + } + PAGE_FAIL_TRAP; + } /* end ITS paging */ + +/* TOPS-10 paging - checked against KS10 microcode + + TOPS-10 paging is also based on conventional page tables. The user page + tables are arranged contiguously at the beginning of the user process table; + however, the executive page tables are scattered through the executive and + user process tables. +*/ + +else if (!T20PAG) { /* TOPS-10 paging */ + int32 pte, vpn, ptead, xpte; + d10 ptewd; + + vpn = PAG_GETVPN (ea); /* get virt page num */ + if (tbl == uptbl) ptead = upta + UPT_T10_UMAP + (vpn >> 1); + else if (vpn < 0340) ptead = epta + EPT_T10_X000 + (vpn >> 1); + else if (vpn < 0400) ptead = upta + UPT_T10_X340 + ((vpn - 0340) >> 1); + else ptead = epta + EPT_T10_X400 + ((vpn - 0400) >> 1); + READPT (ptewd, ptead); /* get PTE pair */ + pte = (int32) ((ptewd >> ((vpn & 1)? 0: 18)) & RMASK); + pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | + ((mode & PTF_WR)? PF_WRITE: 0) | + ((pte & PTE_T10_A)? PF_T10_A | + ((pte & PTE_T10_S)? PF_T10_S: 0): 0); + if (mode & PTF_MAP) pager_word = pager_word | /* map? add to pf wd */ + ((pte & PTE_T10_W)? PF_T10_W: 0) | /* W, S, C bits */ + ((pte & PTE_T10_S)? PF_T10_S: 0) | + ((pte & PTE_T10_C)? PF_C: 0); + if ((pte & PTE_T10_A) && (!(mode & PTF_WR) || (pte & PTE_T10_W))) { + xpte = ((pte & PTE_PPMASK) << PAG_V_PN) | /* calc exp pte */ + PTBL_V | ((pte & PTE_T10_W)? PTBL_M: 0); + if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */ + return xpte; + } + PAGE_FAIL_TRAP; + } /* end TOPS10 paging */ + +/* TOPS-20 paging - checked against KS10 microcode + + TOPS-20 paging has three phases: + + 1. Starting at EPT/UPT + 540 + section number, chase section pointers to + get the pointer to the section page table. In the KS10, because there + is only one section, the microcode caches the result of this evaluation. + Also, the evaluation of indirect pointers is simplified, as the section + table index is ignored. + + 2. Starting with the page map pointer, chase page pointers to get the + pointer to the page. The KS10 allows the operating system to inhibit + updating of the CST (base address = 0). + + 3. Use the page pointer to get the CST entry. If a write reference to + a writeable page, set CST_M. If CST_M is set, set M in page table. +*/ + +else { /* TOPS-20 paging */ + int32 pmi, vpn, xpte; + int32 flg, t; + t_bool stop; + a10 pa, csta; + d10 ptr, cste; + d10 acc = PTE_T20_W | PTE_T20_C; /* init access bits */ + + pager_word = PF_VIRT | ea | ((tbl == uptbl)? PF_USER: 0) | + ((mode & PTF_WR)? PF_WRITE: 0); /* set page fail word */ + +/* First phase - evaluate section pointers - returns a ptr to a page map + As a single section machine, the KS10 short circuits this part of the + process. In particular, the indirect pointer calculation assumes that + the section table index will be 0. It adds the full pointer (not just + the right half) to the SPT base. If the section index is > 0, the + result is a physical memory address > 256KW. Depending on the size of + memory, the SPT fetch may or may not generate a NXM page fail. The + KS10 then ignores the section table index in fetching the next pointer. + + The KS10 KL10 memory management diagnostic (dskec.sav) tests for this + behavior with a section index of 3. However, this would be a legal + physical address in a system with 1MW. Accordingly, the simulator + special cases non-zero section indices (which can't work in any case) + to generate the right behavior for the diagnostic. +*/ + + vpn = PAG_GETVPN (ea); /* get virt page num */ + pa = (tbl == uptbl)? upta + UPT_T20_SCTN: epta + EPT_T20_SCTN; + READPT (ptr, pa & PAMASK); /* get section 0 ptr */ + for (stop = FALSE, flg = 0; !stop; flg++) { /* eval section ptrs */ + acc = acc & ptr; /* cascade acc bits */ + switch (T20_GETTYP (ptr)) { /* case on ptr type */ + + case T20_NOA: /* no access */ + default: /* undefined type */ + PAGE_FAIL_TRAP; /* page fail */ + + case T20_IMM: /* immediate */ + stop = TRUE; /* exit */ + break; + + case T20_SHR: /* shared */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + READPT (ptr, pa & PAMASK); /* get SPT entry */ + stop = TRUE; /* exit */ + break; + + case T20_IND: /* indirect */ + if (flg && (t = test_int ())) ABORT (t); + pmi = T20_GETPMI (ptr); /* get sect tbl idx */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + if (pmi) { /* for dskec */ + pag_nxm ((pmi << 18) | pa, REF_P, PF_OK); + PAGE_FAIL_TRAP; + } + READPT (ptr, pa & PAMASK); /* get SPT entry */ + if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } + pa = PAG_PTEPA (ptr, pmi); /* index off page */ + READPT (ptr, pa & PAMASK); /* get pointer */ + break; /* continue in loop */ + } /* end case */ + } /* end for */ + +/* Second phase - found page map ptr, evaluate page pointers */ + + pa = PAG_PTEPA (ptr, vpn); /* get ptbl address */ + for (stop = FALSE, flg = 0; !stop; flg++) { /* eval page ptrs */ + if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-res? */ + if (cst) { /* cst really there? */ + csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK); + READPT (cste, csta); /* get CST entry */ + if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; } + cste = (cste & cstm) | pur; /* update entry */ + WriteP (csta, cste); /* rewrite */ + } + READPT (ptr, pa & PAMASK); /* get pointer */ + acc = acc & ptr; /* cascade acc bits */ + switch (T20_GETTYP (ptr)) { /* case on ptr type */ + + case T20_NOA: /* no access */ + default: /* undefined type */ + PAGE_FAIL_TRAP; /* page fail */ + + case T20_IMM: /* immediate */ + stop = TRUE; /* exit */ + break; + + case T20_SHR: /* shared */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + READPT (ptr, pa & PAMASK); /* get SPT entry */ + stop = TRUE; /* exit */ + break; + + case T20_IND: /* indirect */ + if (flg && (t = test_int ())) ABORT (t); + pmi = T20_GETPMI (ptr); /* get section index */ + pa = (int32) (spt + (ptr & RMASK)); /* get SPT idx */ + READPT (ptr, pa & PAMASK); /* get SPT entry */ + pa = PAG_PTEPA (ptr, pmi); /* index off page */ + break; /* continue in loop */ + } /* end case */ + } /* end for */ + +/* Last phase - have final page pointer, check modifiability */ + + if (ptr & PTE_T20_STM) { PAGE_FAIL_TRAP; } /* non-resident? */ + if (cst) { /* CST really there? */ + csta = (int32) ((cst + (ptr & PTE_PPMASK)) & PAMASK); + READPT (cste, csta); /* get CST entry */ + if ((cste & CST_AGE) == 0) { PAGE_FAIL_TRAP; } + cste = (cste & cstm) | pur; /* update entry */ + } + else cste = 0; /* no, entry = 0 */ + pager_word = pager_word | PF_T20_DN; /* set eval done */ + xpte = ((int32) ((ptr & PTE_PPMASK) << PAG_V_PN)) | PTBL_V; + if (mode & PTF_WR) { /* write? */ + if (acc & PTE_T20_W) { /* writable? */ + xpte = xpte | PTBL_M; /* set PTE M */ + cste = cste | CST_M; /* set CST M */ + } + else { PAGE_FAIL_TRAP; } /* no, trap */ + } + if (cst) WriteP (csta, cste); /* write CST entry */ + if (mode & PTF_MAP) pager_word = pager_word | /* map? more in pf wd */ + ((xpte & PTBL_M)? PF_T20_M: 0) | /* M, W, C bits */ + ((acc & PTE_T20_W)? PF_T20_W: 0) | + ((acc & PTE_T20_C)? PF_C: 0); + if (!(mode & PTF_CON)) tbl[vpn] = xpte; /* set tbl if ~cons */ + return xpte; + } /* end TOPS20 paging */ +} + +/* Set up pointers for AC, memory, and process table access */ + +void set_dyn_ptrs (void) +{ +int32 t; + +if (PAGING) { + ac_cur = &acs[UBR_GETCURAC (ubr) * AC_NUM]; + ac_prv = &acs[UBR_GETPRVAC (ubr) * AC_NUM]; + if (TSTF (F_USR)) ptbl_cur = ptbl_prv = &uptbl[0]; + else { + ptbl_cur = &eptbl[0]; + ptbl_prv = TSTF (F_UIO)? &uptbl[0]: &eptbl[0]; + } + } +else { + ac_cur = ac_prv = &acs[0]; + ptbl_cur = ptbl_prv = &physptbl[0]; + } +t = EBR_GETEBR (ebr); +epta = t << PAG_V_PN; +if (Q_ITS) upta = (int32) ubr & PAMASK; +else { + t = UBR_GETUBR (ubr); + upta = t << PAG_V_PN; + } +return; +} + +/* MAP instruction, TOPS-10 and TOPS-20 only + + According to the KS-10 ucode, map with paging disabled sets + "accessible, writeable, software", regardless of whether + TOPS-10 or TOPS-20 paging is implemented +*/ + +d10 map (a10 ea, int32 prv) +{ +int32 xpte; +d10 val = (TSTF (F_USR)? PF_USER: 0); + +if (!PAGING) return (val | PF_T10_A | PF_T10_W | PF_T10_S | ea); +xpte = ptbl_fill (ea, prv? ptbl_prv: ptbl_cur, PTF_MAP); /* get exp pte */ +if (xpte) val = (pager_word & ~PAMASK) | PAG_XPTEPA (xpte, ea); +else { + if (pager_word & PF_HARD) val = pager_word; /* hard error */ + else val = val | PF_VIRT | ea; /* inaccessible */ + } +return val; +} + +/* Mapping routine for console */ + +a10 conmap (a10 ea, int32 mode, int32 sw) +{ +int32 xpte, *tbl; + +if (!PAGING) return ea; +set_dyn_ptrs (); /* in case changed */ +if (sw & SWMASK ('E')) tbl = eptbl; +else if (sw & SWMASK ('U')) tbl = uptbl; +else tbl = ptbl_cur; +xpte = ptbl_fill (ea, tbl, mode); +if (xpte) return PAG_XPTEPA (xpte, ea); +else return MAXMEMSIZE; +} + +/* Common pager instructions */ + +t_bool clrpt (a10 ea, int32 prv) +{ +int32 vpn = PAG_GETVPN (ea); /* get page num */ + +if (Q_ITS) { /* ITS? */ + uptbl[vpn & ~1] = 0; /* clear double size */ + uptbl[vpn | 1] = 0; /* entries in */ + eptbl[vpn & ~1] = 0; /* both page tables */ + eptbl[vpn | 1] = 0; + } +else { + uptbl[vpn] = 0; /* clear entries in */ + eptbl[vpn] = 0; /* both page tables */ + } +return FALSE; +} + +t_bool wrebr (a10 ea, int32 prv) +{ +ebr = ea & EBR_MASK; /* store EBR */ +pag_reset (&pag_dev); /* clear page tables */ +set_dyn_ptrs (); /* set dynamic ptrs */ +return FALSE; +} + +t_bool rdebr (a10 ea, int32 prv) +{ +Write (ea, (ebr & EBR_MASK), prv); +return FALSE; +} + +t_bool wrubr (a10 ea, int32 prv) +{ +d10 val = Read (ea, prv); +d10 ubr_mask = (Q_ITS)? PAMASK: UBR_UBRMASK; /* ITS: ubr is wd addr */ + +if (val & UBR_SETACB) ubr = ubr & ~UBR_ACBMASK; /* set AC's? */ +else val = val & ~UBR_ACBMASK; /* no, keep old val */ +if (val & UBR_SETUBR) { /* set UBR? */ + ubr = ubr & ~ubr_mask; + pag_reset (&pag_dev); /* yes, clr pg tbls */ + } +else val = val & ~ubr_mask; /* no, keep old val */ +ubr = (ubr | val) & (UBR_ACBMASK | ubr_mask); +set_dyn_ptrs (); +return FALSE; +} + +t_bool rdubr (a10 ea, int32 prv) +{ +ubr = ubr & (UBR_ACBMASK | (Q_ITS? PAMASK: UBR_UBRMASK)); +Write (ea, UBRWORD, prv); +return FALSE; +} + +t_bool wrhsb (a10 ea, int32 prv) +{ +hsb = Read (ea, prv) & PAMASK; +return FALSE; +} + +t_bool rdhsb (a10 ea, int32 prv) +{ +Write (ea, hsb, prv); +return FALSE; +} + +/* TOPS20 pager instructions */ + +t_bool wrspb (a10 ea, int32 prv) +{ +spt = Read (ea, prv); +return FALSE; +} + +t_bool rdspb (a10 ea, int32 prv) +{ +Write (ea, spt, prv); +return FALSE; +} + +t_bool wrcsb (a10 ea, int32 prv) +{ +cst = Read (ea, prv); +return FALSE; +} + +t_bool rdcsb (a10 ea, int32 prv) +{ +Write (ea, cst, prv); +return FALSE; +} + +t_bool wrpur (a10 ea, int32 prv) +{ +pur = Read (ea, prv); +return FALSE; +} + +t_bool rdpur (a10 ea, int32 prv) +{ +Write (ea, pur, prv); +return FALSE; +} + +t_bool wrcstm (a10 ea, int32 prv) +{ +cstm = Read (ea, prv); +if ((cpu_unit.flags & UNIT_T20) && (ea == 040127)) + cstm = 0770000000000; +return FALSE; +} + +t_bool rdcstm (a10 ea, int32 prv) +{ +Write (ea, cstm, prv); +return FALSE; +} + +/* ITS pager instructions + The KS10 does not implement the JPC option. +*/ + +t_bool clrcsh (a10 ea, int32 prv) +{ +return FALSE; +} + +t_bool ldbr1 (a10 ea, int32 prv) +{ +dbr1 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr1 (a10 ea, int32 prv) +{ +Write (ea, dbr1, prv); +return FALSE; +} + +t_bool ldbr2 (a10 ea, int32 prv) +{ +dbr2 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr2 (a10 ea, int32 prv) +{ +Write (ea, dbr2, prv); +return FALSE; +} + +t_bool ldbr3 (a10 ea, int32 prv) +{ +dbr3 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr3 (a10 ea, int32 prv) +{ +Write (ea, dbr3, prv); +return FALSE; +} + +t_bool ldbr4 (a10 ea, int32 prv) +{ +dbr4 = ea; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool sdbr4 (a10 ea, int32 prv) +{ +Write (ea, dbr4, prv); +return FALSE; +} + +t_bool wrpcst (a10 ea, int32 prv) +{ +pcst = Read (ea, prv); +return FALSE; +} + +t_bool rdpcst (a10 ea, int32 prv) +{ +Write (ea, pcst, prv); +return FALSE; +} + +t_bool lpmr (a10 ea, int32 prv) +{ +d10 val; + +val = Read (ADDA (ea, 2), prv); +dbr1 = (a10) (Read (ea, prv) & AMASK); +dbr2 = (a10) (Read (ADDA (ea, 1), prv) & AMASK); +quant = val; +pag_reset (&pag_dev); +return FALSE; +} + +t_bool spm (a10 ea, int32 prv) +{ + +ReadM (ADDA (ea, 2), prv); +Write (ea, dbr1, prv); +Write (ADDA (ea, 1), dbr2, prv); +Write (ADDA (ea, 2), quant, prv); +return FALSE; +} + +/* Simulator interface routines */ + +t_stat pag_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 tbln = uptr - pag_unit; + +if (addr >= PTBL_MEMSIZE) return SCPE_NXM; +*vptr = tbln? uptbl[addr]: eptbl[addr];; +return SCPE_OK; +} + +t_stat pag_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 tbln = uptr - pag_unit; + +if (addr >= PTBL_MEMSIZE) return SCPE_NXM; +if (tbln) uptbl[addr] = (int32) val & PTBL_MASK; +else eptbl[addr] = (int32) val & PTBL_MASK; +return SCPE_OK; +} + +t_stat pag_reset (DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < PTBL_MEMSIZE; i++) { + eptbl[i] = uptbl[i] = 0; + physptbl[i] = (i << PAG_V_PN) + PTBL_M + PTBL_V; + } +return SCPE_OK; +} diff --git a/PDP10/pdp10_rp.c b/PDP10/pdp10_rp.c new file mode 100644 index 0000000..c384eb6 --- /dev/null +++ b/PDP10/pdp10_rp.c @@ -0,0 +1,1262 @@ +/* pdp10_rp.c - RH11/RP04/05/06/07 RM02/03/05/80 "Massbus" disk controller + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rp RH/RP/RM moving head disks + + 12-Nov-05 RMS Fixed DCLR not to clear drive address + 07-Jul-05 RMS Removed extraneous externs + 18-Mar-05 RMS Added attached test to detach routine + 20-Sep-04 RMS Fixed bugs in replicated state, RP vs RM accuracy + 04-Jan-04 RMS Changed sim_fsize calling sequence + 23-Jul-03 RMS Fixed bug in read header stub + 25-Apr-03 RMS Revised for extended file support + 21-Nov-02 RMS Fixed bug in bootstrap (reported by Michael Thompson) + 29-Sep-02 RMS Added variable vector support + New data structures + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support support + 24-Nov-01 RMS Changed RPER, RPDS, FNC, FLG to arrays + 23-Oct-01 RMS Fixed bug in error interrupts + New IO page address constants + 05-Oct-01 RMS Rewrote interrupt handling from schematics + 02-Oct-01 RMS Revised CS1 write code + 30-Sep-01 RMS Moved CS1<5:0> into drives + 28-Sep-01 RMS Fixed interrupt handling for SC/ATA + 23-Aug-01 RMS Added read/write header stubs for ITS + (found by Mirian Crzig Lennox) + 13-Jul-01 RMS Changed fread call to fxread (found by Peter Schorn) + 14-May-01 RMS Added check for unattached drive + + The "Massbus style" disks consisted of several different large + capacity drives interfaced through a reasonably common (but not + 100% compatible) family of interfaces into the KS10 Unibus via + the RH11 disk controller. + + WARNING: The interupt logic of the RH11/RH70 is unusual and must be + simulated with great precision. The RH11 has an internal interrupt + request flop, CSTB INTR, which is controlled as follows: + - Writing IE and DONE simultaneously sets CSTB INTR + - Controller clear, INIT, and interrupt acknowledge clear CSTB INTR + (and also clear IE) + - A transition of DONE from 0 to 1 sets CSTB from INTR + The output of INTR is OR'd with the AND of RPCS1 to + create the interrupt request signal. Thus, + - The DONE interrupt is edge sensitive, but the SC interrupt is + level sensitive. + - The DONE interrupt, once set, is not disabled if IE is cleared, + but the SC interrupt is. +*/ + +#include "pdp10_defs.h" +#include + +#define RP_NUMDR 8 /* #drives */ +#define RP_NUMWD 128 /* 36b words/sector */ +#define RP_MAXFR 32768 /* max transfer */ +#define GET_SECTOR(x,d) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) drv_tab[d].sect))) +#define MBA_RP_CTRL 0 /* RP drive */ +#define MBA_RM_CTRL 1 /* RM drive */ + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 7 +#define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ +#define UNIT_V_DUMMY (UNIT_V_UF + 5) /* dummy flag */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_DUMMY (1 << UNIT_V_DUMMY) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* RPCS1 - 176700 - control/status 1 */ + +#define CS1_GO CSR_GO /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 037 /* function mask */ +#define CS1_FNC (CS1_M_FNC << CS1_V_FNC) +#define FNC_NOP 000 /* no operation */ +#define FNC_UNLOAD 001 /* unload */ +#define FNC_SEEK 002 /* seek */ +#define FNC_RECAL 003 /* recalibrate */ +#define FNC_DCLR 004 /* drive clear */ +#define FNC_RELEASE 005 /* port release */ +#define FNC_OFFSET 006 /* offset */ +#define FNC_RETURN 007 /* return to center */ +#define FNC_PRESET 010 /* read-in preset */ +#define FNC_PACK 011 /* pack acknowledge */ +#define FNC_SEARCH 014 /* search */ +#define FNC_XFER 024 /* >=? data xfr */ +#define FNC_WCHK 024 /* write check */ +#define FNC_WRITE 030 /* write */ +#define FNC_WRITEH 031 /* write w/ headers */ +#define FNC_READ 034 /* read */ +#define FNC_READH 035 /* read w/ headers */ +#define CS1_IE CSR_IE /* int enable */ +#define CS1_DONE CSR_DONE /* ready */ +#define CS1_V_UAE 8 /* Unibus addr ext */ +#define CS1_M_UAE 03 +#define CS1_UAE (CS1_M_UAE << CS1_V_UAE) +#define CS1_DVA 0004000 /* drive avail NI */ +#define CS1_MCPE 0020000 /* Mbus par err NI */ +#define CS1_TRE 0040000 /* transfer err */ +#define CS1_SC 0100000 /* special cond */ +#define CS1_MBZ 0012000 +#define CS1_DRV (CS1_FNC | CS1_GO) +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) +#define GET_UAE(x) (((x) & CS1_UAE) << (16 - CS1_V_UAE)) + +/* RPWC - 176702 - word count */ + +/* RPBA - 176704 - base address */ + +#define BA_MBZ 0000001 /* must be zero */ + +/* RPDA - 176706 - sector/track */ + +#define DA_V_SC 0 /* sector pos */ +#define DA_M_SC 077 /* sector mask */ +#define DA_V_SF 8 /* track pos */ +#define DA_M_SF 077 /* track mask */ +#define DA_MBZ 0140300 +#define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) +#define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) + +/* RPCS2 - 176710 - control/status 2 */ + +#define CS2_V_UNIT 0 /* unit pos */ +#define CS2_M_UNIT 07 /* unit mask */ +#define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) +#define CS2_UAI 0000010 /* addr inhibit */ +#define CS2_PAT 0000020 /* parity test NI */ +#define CS2_CLR 0000040 /* controller clear */ +#define CS2_IR 0000100 /* input ready */ +#define CS2_OR 0000200 /* output ready */ +#define CS2_MDPE 0000400 /* Mbus par err NI */ +#define CS2_MXF 0001000 /* missed xfer NI */ +#define CS2_PGE 0002000 /* program err */ +#define CS2_NEM 0004000 /* nx mem err */ +#define CS2_NED 0010000 /* nx drive err */ +#define CS2_PE 0020000 /* parity err NI */ +#define CS2_WCE 0040000 /* write check err */ +#define CS2_DLT 0100000 /* data late NI */ +#define CS2_MBZ (CS2_CLR) +#define CS2_RW (CS2_UNIT | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) +#define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ + CS2_NED | CS2_PE | CS2_WCE | CS2_DLT ) +#define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT) + +/* RPDS - 176712 - drive status */ + +#define DS_OF 0000001 /* offset mode */ +#define DS_VV 0000100 /* volume valid */ +#define DS_RDY 0000200 /* drive ready */ +#define DS_DPR 0000400 /* drive present */ +#define DS_PGM 0001000 /* programable NI */ +#define DS_LST 0002000 /* last sector */ +#define DS_WRL 0004000 /* write locked */ +#define DS_MOL 0010000 /* medium online */ +#define DS_PIP 0020000 /* pos in progress */ +#define DS_ERR 0040000 /* error */ +#define DS_ATA 0100000 /* attention active */ +#define DS_MBZ 0000076 + +/* RPER1 - 176714 - error status 1 */ + +#define ER1_ILF 0000001 /* illegal func */ +#define ER1_ILR 0000002 /* illegal register */ +#define ER1_RMR 0000004 /* reg mod refused */ +#define ER1_PAR 0000010 /* parity err */ +#define ER1_FER 0000020 /* format err NI */ +#define ER1_WCF 0000040 /* write clk fail NI */ +#define ER1_ECH 0000100 /* ECC hard err NI */ +#define ER1_HCE 0000200 /* hdr comp err NI */ +#define ER1_HCR 0000400 /* hdr CRC err NI */ +#define ER1_AOE 0001000 /* addr ovflo err */ +#define ER1_IAE 0002000 /* invalid addr err */ +#define ER1_WLE 0004000 /* write lock err */ +#define ER1_DTE 0010000 /* drive time err NI */ +#define ER1_OPI 0020000 /* op incomplete */ +#define ER1_UNS 0040000 /* drive unsafe */ +#define ER1_DCK 0100000 /* data check NI */ + +/* RPAS - 176716 - attention summary */ + +#define AS_U0 0000001 /* unit 0 flag */ + +/* RPLA - 176720 - look ahead register */ + +#define LA_V_SC 6 /* sector pos */ + +/* RPDB - 176722 - data buffer */ +/* RPMR - 176724 - maintenace register */ +/* RPDT - 176726 - drive type */ +/* RPSN - 176730 - serial number */ + +/* RPOF - 176732 - offset register */ + +#define OF_HCI 0002000 /* hdr cmp inh NI */ +#define OF_ECI 0004000 /* ECC inhibit NI */ +#define OF_F22 0010000 /* format NI */ +#define OF_MBZ 0161400 + +/* RPDC - 176734 - desired cylinder */ + +#define DC_V_CY 0 /* cylinder pos */ +#define DC_M_CY 01777 /* cylinder mask */ +#define DC_MBZ 0176000 +#define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) +#define GET_DA(c,fs,d) ((((GET_CY (c) * drv_tab[d].surf) + \ + GET_SF (fs)) * drv_tab[d].sect) + GET_SC (fs)) + +/* RPCC - 176736 - current cylinder */ +/* RPER2 - 176740 - error status 2 - drive unsafe conditions - unimplemented */ +/* RPER3 - 176742 - error status 3 - more unsafe conditions - unimplemented */ +/* RPEC1 - 176744 - ECC status 1 - unimplemented */ +/* RPEC2 - 176746 - ECC status 2 - unimplemented */ + +/* This controller supports many different disk drive types. These drives + are operated in 576 bytes/sector (128 36b words/sector) mode, which gives + them somewhat different geometry from the PDP-11 variants: + + type #sectors/ #surfaces/ #cylinders/ + surface cylinder drive + + RM02/3 30 5 823 =67MB + RP04/5 20 19 411 =88MB + RM80 30 14 559 =124MB + RP06 20 19 815 =176MB + RM05 30 19 823 =256MB + RP07 43 32 630 =516MB + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. + + The RP07, despite its name, uses an RM-style controller. +*/ + +#define RM03_DTYPE 0 +#define RM03_SECT 30 +#define RM03_SURF 5 +#define RM03_CYL 823 +#define RM03_DEV 020024 +#define RM03_SIZE (RM03_SECT * RM03_SURF * RM03_CYL * RP_NUMWD) + +#define RP04_DTYPE 1 +#define RP04_SECT 20 +#define RP04_SURF 19 +#define RP04_CYL 411 +#define RP04_DEV 020020 +#define RP04_SIZE (RP04_SECT * RP04_SURF * RP04_CYL * RP_NUMWD) + +#define RM80_DTYPE 2 +#define RM80_SECT 30 +#define RM80_SURF 14 +#define RM80_CYL 559 +#define RM80_DEV 020026 +#define RM80_SIZE (RM80_SECT * RM80_SURF * RM80_CYL * RP_NUMWD) + +#define RP06_DTYPE 3 +#define RP06_SECT 20 +#define RP06_SURF 19 +#define RP06_CYL 815 +#define RP06_DEV 020022 +#define RP06_SIZE (RP06_SECT * RP06_SURF * RP06_CYL * RP_NUMWD) + +#define RM05_DTYPE 4 +#define RM05_SECT 30 +#define RM05_SURF 19 +#define RM05_CYL 823 +#define RM05_DEV 020027 +#define RM05_SIZE (RM05_SECT * RM05_SURF * RM05_CYL * RP_NUMWD) + +#define RP07_DTYPE 5 +#define RP07_SECT 43 +#define RP07_SURF 32 +#define RP07_CYL 630 +#define RP07_DEV 020042 +#define RP07_SIZE (RP07_SECT * RP07_SURF * RP07_CYL * RP_NUMWD) + +struct drvtyp { + int32 sect; /* sectors */ + int32 surf; /* surfaces */ + int32 cyl; /* cylinders */ + int32 size; /* #blocks */ + int32 devtype; /* device type */ + int32 ctrl; /* ctrl type */ + }; + +struct drvtyp drv_tab[] = { + { RM03_SECT, RM03_SURF, RM03_CYL, RM03_SIZE, RM03_DEV, MBA_RM_CTRL }, + { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV, MBA_RP_CTRL }, + { RM80_SECT, RM80_SURF, RM80_CYL, RM80_SIZE, RM80_DEV, MBA_RM_CTRL }, + { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV, MBA_RP_CTRL }, + { RM05_SECT, RM05_SURF, RM05_CYL, RM05_SIZE, RM05_DEV, MBA_RM_CTRL }, + { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV, MBA_RM_CTRL }, + { 0 } + }; + +extern d10 *M; /* memory */ +extern int32 int_req; +extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* Unibus maps */ +extern int32 ubcs[UBANUM]; +extern UNIT cpu_unit; + +int32 rpcs1 = 0; /* control/status 1 */ +int32 rpwc = 0; /* word count */ +int32 rpba = 0; /* bus address */ +int32 rpcs2 = 0; /* control/status 2 */ +int32 rpdb = 0; /* data buffer */ +uint16 rpda[RP_NUMDR] = { 0 }; /* track/sector */ +uint16 rpds[RP_NUMDR] = { 0 }; /* drive status */ +uint16 rper1[RP_NUMDR] = { 0 }; /* error status 1 */ +uint16 rmhr[RP_NUMDR] = { 0 }; /* holding reg */ +uint16 rpmr[RP_NUMDR] = { 0 }; /* maint reg */ +uint16 rmmr2[RP_NUMDR] = { 0 }; /* maint reg 2 */ +uint16 rpof[RP_NUMDR] = { 0 }; /* offset */ +uint16 rpdc[RP_NUMDR] = { 0 }; /* cylinder */ +uint16 rper2[RP_NUMDR] = { 0 }; /* error status 2 */ +uint16 rper3[RP_NUMDR] = { 0 }; /* error status 3 */ +uint16 rpec1[RP_NUMDR] = { 0 }; /* ECC correction 1 */ +uint16 rpec2[RP_NUMDR] = { 0 }; /* ECC correction 2 */ +int32 rpiff = 0; /* INTR flip/flop */ +int32 rp_stopioe = 1; /* stop on error */ +int32 rp_swait = 10; /* seek time */ +int32 rp_rwait = 10; /* rotate time */ +static int32 reg_in_drive[32] = { + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + +t_stat rp_rd (int32 *data, int32 PA, int32 access); +t_stat rp_wr (int32 data, int32 PA, int32 access); +int32 rp_inta (void); +t_stat rp_svc (UNIT *uptr); +t_stat rp_reset (DEVICE *dptr); +t_stat rp_boot (int32 unitno, DEVICE *dptr); +t_stat rp_attach (UNIT *uptr, char *cptr); +t_stat rp_detach (UNIT *uptr); +void set_rper (int32 flag, int32 drv); +void update_rpcs (int32 flags, int32 drv); +void rp_go (int32 drv, int32 fnc); +t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* RP data structures + + rp_dev RP device descriptor + rp_unit RP unit list + rp_reg RP register list + rp_mod RP modifier list +*/ + +DIB rp_dib = { + IOBA_RP, IOLN_RP, &rp_rd, &rp_wr, + 1, IVCL (RP), VEC_RP, { &rp_inta } + }; + +UNIT rp_unit[] = { + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+(RP06_DTYPE << UNIT_V_DTYPE), RP06_SIZE) } + }; + +REG rp_reg[] = { + { ORDATA (RPCS1, rpcs1, 16) }, + { ORDATA (RPWC, rpwc, 16) }, + { ORDATA (RPBA, rpba, 16) }, + { ORDATA (RPCS2, rpcs2, 16) }, + { ORDATA (RPDB, rpdb, 16) }, + { BRDATA (RPDA, rpda, 8, 16, RP_NUMDR) }, + { BRDATA (RPDS, rpds, 8, 16, RP_NUMDR) }, + { BRDATA (RPER1, rper1, 8, 16, RP_NUMDR) }, + { BRDATA (RPHR, rmhr, 8, 16, RP_NUMDR) }, + { BRDATA (RPOF, rpof, 8, 16, RP_NUMDR) }, + { BRDATA (RPDC, rpdc, 8, 16, RP_NUMDR) }, + { BRDATA (RPER2, rper2, 8, 16, RP_NUMDR) }, + { BRDATA (RPER3, rper3, 8, 16, RP_NUMDR) }, + { BRDATA (RPEC1, rpec1, 8, 16, RP_NUMDR) }, + { BRDATA (RPEC2, rpec2, 8, 16, RP_NUMDR) }, + { BRDATA (RMMR, rpmr, 8, 16, RP_NUMDR) }, + { BRDATA (RMMR2, rmmr2, 8, 16, RP_NUMDR) }, + { FLDATA (IFF, rpiff, 0) }, + { FLDATA (INT, int_req, INT_V_RP) }, + { FLDATA (SC, rpcs1, CSR_V_ERR) }, + { FLDATA (DONE, rpcs1, CSR_V_DONE) }, + { FLDATA (IE, rpcs1, CSR_V_IE) }, + { DRDATA (STIME, rp_swait, 24), REG_NZ + PV_LEFT }, + { DRDATA (RTIME, rp_rwait, 24), REG_NZ + PV_LEFT }, + { URDATA (FNC, rp_unit[0].FUNC, 8, 5, 0, RP_NUMDR, REG_HRO) }, + { URDATA (CAPAC, rp_unit[0].capac, 10, T_ADDR_W, 0, + RP_NUMDR, PV_LEFT | REG_HRO) }, + { FLDATA (STOP_IOE, rp_stopioe, 0) }, + { NULL } + }; + +MTAB rp_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM03", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP04", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM80", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP06", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM05", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP07", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE), + "RM03", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE), + "RP04", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE), + "RM80", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE), + "RP06", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE), + "RM05", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE), + "RP07", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (RM03_DTYPE << UNIT_V_DTYPE), + NULL, "RM03", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP04_DTYPE << UNIT_V_DTYPE), + NULL, "RP04", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RM80_DTYPE << UNIT_V_DTYPE), + NULL, "RM80", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP06_DTYPE << UNIT_V_DTYPE), + NULL, "RP06", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RM05_DTYPE << UNIT_V_DTYPE), + NULL, "RM05", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP07_DTYPE << UNIT_V_DTYPE), + NULL, "RP07", &rp_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE rp_dev = { + "RP", rp_unit, rp_reg, rp_mod, + RP_NUMDR, 8, 30, 1, 8, 36, + NULL, NULL, &rp_reset, + &rp_boot, &rp_attach, &rp_detach, + &rp_dib, DEV_UBUS + }; + +/* I/O dispatch routines, I/O addresses 17776700 - 17776776 */ + +t_stat rp_rd (int32 *data, int32 PA, int32 access) +{ +int32 drv, dtype, i, j; + +drv = GET_UNIT (rpcs2); /* get current unit */ +dtype = GET_DTYPE (rp_unit[drv].flags); /* get drive type */ +j = (PA >> 1) & 037; /* get reg offset */ +if (reg_in_drive[j] && (rp_unit[drv].flags & UNIT_DIS)) { /* nx disk */ + rpcs2 = rpcs2 | CS2_NED; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + *data = 0; + return SCPE_OK; + } + +update_rpcs (0, drv); /* update status */ +switch (j) { /* decode PA<5:1> */ + + case 000: /* RPCS1 */ + *data = rpcs1; + break; + + case 001: /* RPWC */ + *data = rpwc; + break; + + case 002: /* RPBA */ + *data = rpba = rpba & ~BA_MBZ; + break; + + case 003: /* RPDA */ + *data = rpda[drv] = rpda[drv] & ~DA_MBZ; + break; + + case 004: /* RPCS2 */ + *data = rpcs2 = (rpcs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; + break; + + case 005: /* RPDS */ + *data = rpds[drv]; + break; + + case 006: /* RPER1 */ + *data = rper1[drv]; + break; + + case 007: /* RPAS */ + *data = 0; + for (i = 0; i < RP_NUMDR; i++) + if (rpds[i] & DS_ATA) *data = *data | (AS_U0 << i); + break; + + case 010: /* RPLA */ + *data = GET_SECTOR (rp_rwait, dtype) << LA_V_SC; + break; + + case 011: /* RPDB */ + *data = rpdb; + break; + + case 012: /* RPMR */ + *data = rpmr[drv]; + break; + + case 013: /* RPDT */ + *data = drv_tab[dtype].devtype; + break; + + case 014: /* RPSN */ + *data = 020 | (drv + 1); + break; + + case 015: /* RPOF */ + *data = rpof[drv] = rpof[drv] & ~OF_MBZ; + break; + + case 016: /* RPDC */ + *data = rpdc[drv] = rpdc[drv] & ~DC_MBZ; + break; + + case 017: /* RPCC, RMHR */ + if (drv_tab[dtype].ctrl == MBA_RP_CTRL) /* RP is CC */ + *data = rp_unit[drv].CYL; + else *data = rmhr[drv] ^ 0177777; /* RM is HR */ + break; + + case 020: /* RPER2, RMMR2 */ + if (drv_tab[dtype].ctrl == MBA_RP_CTRL) /* RP is ER2 */ + *data = rper2[drv]; + else *data = rmmr2[drv]; /* RM is MR2 */ + break; + + case 021: /* RPER3, RMER2 */ + if (drv_tab[dtype].ctrl == MBA_RP_CTRL) /* RP is ER3 */ + *data = rper3[drv]; + else *data = rper2[drv]; /* RM is ER2 */ + break; + + case 022: /* RPEC1 */ + *data = rpec1[drv]; + break; + + case 023: /* RPEC2 */ + *data = rpec2[drv]; + break; + + default: /* all others */ + set_rper (ER1_ILR, drv); + update_rpcs (0, drv); + break; + } +return SCPE_OK; +} + +t_stat rp_wr (int32 data, int32 PA, int32 access) +{ +int32 cs1f, drv, dtype, i, j; +UNIT *uptr; + +cs1f = 0; /* no int on cs1 upd */ +drv = GET_UNIT (rpcs2); /* get current unit */ +dtype = GET_DTYPE (rp_unit[drv].flags); /* get drive type */ +uptr = rp_dev.units + drv; /* get unit */ +j = (PA >> 1) & 037; /* get reg offset */ +if (reg_in_drive[j] && (rp_unit[drv].flags & UNIT_DIS)) { /* nx disk */ + rpcs2 = rpcs2 | CS2_NED; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + return SCPE_OK; + } +if (reg_in_drive[j] && sim_is_active (&rp_unit[drv])) { /* unit busy? */ + set_rper (ER1_RMR, drv); /* won't write */ + update_rpcs (0, drv); + return SCPE_OK; + } +rmhr[drv] = data; + +switch (j) { /* decode PA<5:1> */ + + case 000: /* RPCS1 */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + if (data & CS1_TRE) { /* error clear? */ + rpcs1 = rpcs1 & ~CS1_TRE; /* clr CS1 */ + rpcs2 = rpcs2 & ~CS2_ERR; /* clr CS2<15:8> */ + } + if ((access == WRITE) || (PA & 1)) { /* hi byte write? */ + if (rpcs1 & CS1_DONE) /* done set? */ + rpcs1 = (rpcs1 & ~CS1_UAE) | (data & CS1_UAE); + } + if ((access == WRITE) || !(PA & 1)) { /* lo byte write? */ + if ((data & CS1_DONE) && (data & CS1_IE)) /* to DONE+IE? */ + rpiff = 1; /* set CSTB INTR */ + rpcs1 = (rpcs1 & ~CS1_IE) | (data & CS1_IE); + if (uptr->flags & UNIT_DIS) { /* nx disk? */ + rpcs2 = rpcs2 | CS2_NED; /* set error flag */ + cs1f = CS1_SC; /* req interrupt */ + } + else if (sim_is_active (uptr)) + set_rper (ER1_RMR, drv); /* won't write */ + else if (data & CS1_GO) { /* start op */ + uptr->FUNC = GET_FNC (data); /* set func */ + if ((uptr->FUNC >= FNC_XFER) && /* data xfer and */ + ((rpcs1 & CS1_DONE) == 0)) /* ~rdy? PGE */ + rpcs2 = rpcs2 | CS2_PGE; + else rp_go (drv, uptr->FUNC); + } + } + break; + + case 001: /* RPWC */ + if (access == WRITEB) data = (PA & 1)? + (rpwc & 0377) | (data << 8): (rpwc & ~0377) | data; + rpwc = data; + break; + + case 002: /* RPBA */ + if (access == WRITEB) data = (PA & 1)? + (rpba & 0377) | (data << 8): (rpba & ~0377) | data; + rpba = data & ~BA_MBZ; + break; + + case 003: /* RPDA */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + rpda[drv] = data & ~DA_MBZ; + break; + + case 004: /* RPCS2 */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + if (data & CS2_CLR) rp_reset (&rp_dev); /* init? */ + else { + if ((data & ~rpcs2) & (CS2_PE | CS2_MXF)) + cs1f = CS1_SC; /* diagn intr */ + if (access == WRITEB) data = (rpcs2 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; + rpcs2 = (rpcs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; + } + drv = GET_UNIT (rpcs2); + break; + + case 006: /* RPER1 */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + rper1[drv] = data; + break; + + case 007: /* RPAS */ + if ((access == WRITEB) && (PA & 1)) break; + for (i = 0; i < RP_NUMDR; i++) + if (data & (AS_U0 << i)) rpds[i] = rpds[i] & ~DS_ATA; + break; + + case 011: /* RPDB */ + if (access == WRITEB) data = (PA & 1)? + (rpdb & 0377) | (data << 8): (rpdb & ~0377) | data; + rpdb = data; + break; + + case 012: /* RPMR */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + rpmr[drv] = data; + break; + + case 015: /* RPOF */ + rpof[drv] = data & ~OF_MBZ; + break; + + case 016: /* RPDC */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + rpdc[drv] = data & ~DC_MBZ; + break; + + case 005: /* RPDS */ + case 010: /* RPLA */ + case 013: /* RPDT */ + case 014: /* RPSN */ + case 017: /* RPCC, RMHR */ + case 020: /* RPER2, RMMR2 */ + case 021: /* RPER3, RMER2 */ + case 022: /* RPEC1 */ + case 023: /* RPEC2 */ + break; /* read only */ + + default: /* all others */ + set_rper (ER1_ILR, drv); + break; + } /* end switch */ + +update_rpcs (cs1f, drv); /* update status */ +return SCPE_OK; +} + +/* Initiate operation - unit not busy, function set */ + +void rp_go (int32 drv, int32 fnc) +{ +int32 dc, dtype, t; +UNIT *uptr; + +uptr = rp_dev.units + drv; /* get unit */ +if (uptr->flags & UNIT_DIS) { /* nx unit? */ + rpcs2 = rpcs2 | CS2_NED; /* set error flag */ + update_rpcs (CS1_SC, drv); /* request intr */ + return; + } +if ((fnc != FNC_DCLR) && (rpds[drv] & DS_ERR)) { /* err & ~clear? */ + set_rper (ER1_ILF, drv); /* set err, ATN */ + update_rpcs (CS1_SC, drv); /* request intr */ + return; + } +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +rpds[drv] = rpds[drv] & ~DS_ATA; /* clear attention */ +dc = rpdc[drv]; /* assume seek, sch */ + +switch (fnc) { /* case on function */ + + case FNC_DCLR: /* drive clear */ + rper1[drv] = rper2[drv] = rper3[drv] = 0; /* clear errors */ + rpec2[drv] = 0; /* clear EC2 */ + if (drv_tab[dtype].ctrl == MBA_RM_CTRL) /* RM? */ + rpmr[drv] = 0; /* clear maint */ + else rpec1[drv] = 0; /* RP, clear EC1 */ + case FNC_NOP: /* no operation */ + case FNC_RELEASE: /* port release */ + return; + + case FNC_PRESET: /* read-in preset */ + rpdc[drv] = 0; /* clear disk addr */ + rpda[drv] = 0; + rpof[drv] = 0; /* clear offset */ + case FNC_PACK: /* pack acknowledge */ + rpds[drv] = rpds[drv] | DS_VV; /* set volume valid */ + return; + + case FNC_OFFSET: /* offset mode */ + case FNC_RETURN: + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + set_rper (ER1_UNS, drv); /* unsafe */ + break; + } + rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ + sim_activate (uptr, rp_swait); /* time operation */ + return; + + case FNC_UNLOAD: /* unload */ + case FNC_RECAL: /* recalibrate */ + dc = 0; /* seek to 0 */ + case FNC_SEEK: /* seek */ + case FNC_SEARCH: /* search */ + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + set_rper (ER1_UNS, drv); /* unsafe */ + break; + } + if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ + (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ + (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ + set_rper (ER1_IAE, drv); + break; + } + rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ + t = abs (dc - uptr->CYL); /* cyl diff */ + if (t == 0) t = 1; /* min time */ + sim_activate (uptr, rp_swait * t); /* schedule */ + uptr->CYL = dc; /* save cylinder */ + return; + + case FNC_WRITEH: /* write headers */ + case FNC_WRITE: /* write */ + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + case FNC_READH: /* read headers */ + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + set_rper (ER1_UNS, drv); /* unsafe */ + break; + } + rpcs2 = rpcs2 & ~CS2_ERR; /* clear errors */ + rpcs1 = rpcs1 & ~(CS1_TRE | CS1_MCPE | CS1_DONE); + if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ + (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ + (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ + set_rper (ER1_IAE, drv); + break; + } + rpds[drv] = rpds[drv] & ~DS_RDY; /* clear drive rdy */ + sim_activate (uptr, rp_rwait + (rp_swait * abs (dc - uptr->CYL))); + uptr->CYL = dc; /* save cylinder */ + return; + + default: /* all others */ + set_rper (ER1_ILF, drv); /* not supported */ + break; + } + +update_rpcs (CS1_SC, drv); /* req intr */ +return; +} + +/* Service unit timeout + + Complete movement or data transfer command + Unit must exist - can't remove an active unit + Unit must be attached - detach cancels in progress operations +*/ + +t_stat rp_svc (UNIT *uptr) +{ +int32 i, dtype, drv, err; +int32 ba, da, vpn; +a10 pa10, mpa10; +int32 wc10, twc10, awc10, fc10; +static d10 dbuf[RP_MAXFR]; + +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +drv = (int32) (uptr - rp_dev.units); /* get drv number */ +rpds[drv] = (rpds[drv] & ~DS_PIP) | DS_RDY; /* change drive status */ + +switch (uptr->FUNC) { /* case on function */ + + case FNC_OFFSET: /* offset */ + rpds[drv] = rpds[drv] | DS_OF | DS_ATA; /* set offset, attention */ + update_rpcs (CS1_SC, drv); + break; + + case FNC_RETURN: /* return to centerline */ + rpds[drv] = (rpds[drv] & ~DS_OF) | DS_ATA; /* clear offset, set attn */ + update_rpcs (CS1_SC, drv); + break; + + case FNC_UNLOAD: /* unload */ + rp_detach (uptr); /* detach unit */ + break; + + case FNC_RECAL: /* recalibrate */ + case FNC_SEARCH: /* search */ + case FNC_SEEK: /* seek */ + rpds[drv] = rpds[drv] | DS_ATA; /* set attention */ + update_rpcs (CS1_SC, drv); + break; + +/* Reads and writes must take into account the complicated relationship + between Unibus addresses and PDP-10 memory addresses, and Unibus + byte and word counts, PDP-10 UBA word counts, and simulator PDP-10 + word counts (due to the fact that the simulator must transfer eight + 8b bytes to do a 36b transfer, whereas the UBA did four 9b bytes). +*/ + +#define XWC_MBZ 0000001 /* wc<0> must be 0 */ +#define XBA_MBZ 0000003 /* addr<1:0> must be 0 */ + + case FNC_WRITE: /* write */ + if (uptr->flags & UNIT_WPRT) { /* write locked? */ + set_rper (ER1_WLE, drv); /* set drive error */ + update_rpcs (CS1_DONE | CS1_TRE, drv); /* set done, err */ + break; + } + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + case FNC_READH: /* read headers */ + ba = GET_UAE (rpcs1) | rpba; /* get byte addr */ + wc10 = (0200000 - rpwc) >> 1; /* get PDP-10 wc */ + da = GET_DA (rpdc[drv], rpda[drv], dtype) * RP_NUMWD; /* get disk addr */ + if ((da + wc10) > drv_tab[dtype].size) { /* disk overrun? */ + set_rper (ER1_AOE, drv); + if (wc10 > (drv_tab[dtype].size - da)) + wc10 = drv_tab[dtype].size - da; + } + + err = fseek (uptr->fileref, da * sizeof (d10), SEEK_SET); + if (uptr->FUNC == FNC_WRITE) { /* write? */ + for (twc10 = 0; twc10 < wc10; twc10++) { + pa10 = ba >> 2; + vpn = PAG_GETVPN (pa10); /* map addr */ + if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || (rpwc & XWC_MBZ) || + ((ubmap[0][vpn] & (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != UMAP_VLD)) { + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; + } + mpa10 = (ubmap[0][vpn] + PAG_GETOFF (pa10)) & PAMASK; + if (MEM_ADDR_NXM (mpa10)) { /* nx memory? */ + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; + } + dbuf[twc10] = M[mpa10]; /* write to disk */ + if ((rpcs2 & CS2_UAI) == 0) ba = ba + 4; + } + if (fc10 = twc10 & (RP_NUMWD - 1)) { /* fill? */ + fc10 = RP_NUMWD - fc10; + for (i = 0; i < fc10; i++) dbuf[twc10 + i] = 0; + } + fxwrite (dbuf, sizeof (d10), twc10 + fc10, uptr->fileref); + err = ferror (uptr->fileref); + } /* end if */ + else { /* read, wchk, readh */ + awc10 = fxread (dbuf, sizeof (d10), wc10, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; awc10 < wc10; awc10++) dbuf[awc10] = 0; + for (twc10 = 0; twc10 < wc10; twc10++) { + pa10 = ba >> 2; + vpn = PAG_GETVPN (pa10); /* map addr */ + if ((vpn >= UMAP_MEMSIZE) || (ba & XBA_MBZ) || (rpwc & XWC_MBZ) || + ((ubmap[0][vpn] & (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != UMAP_VLD)) { + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; + } + mpa10 = (ubmap[0][vpn] + PAG_GETOFF (pa10)) & PAMASK; + if (MEM_ADDR_NXM (mpa10)) { /* nx memory? */ + rpcs2 = rpcs2 | CS2_NEM; /* set error */ + ubcs[0] = ubcs[0] | UBCS_TMO; /* UBA times out */ + break; + } + if ((uptr->FUNC == FNC_READ) || /* read or */ + (uptr->FUNC == FNC_READH)) /* read header */ + M[mpa10] = dbuf[twc10]; + else if (M[mpa10] != dbuf[twc10]) { /* wchk, mismatch? */ + rpcs2 = rpcs2 | CS2_WCE; /* set error */ + break; + } + if ((rpcs2 & CS2_UAI) == 0) ba = ba + 4; + } + } /* end else */ + + rpwc = (rpwc + (twc10 << 1)) & 0177777; /* final word count */ + rpba = (ba & 0177777) & ~BA_MBZ; /* lower 16b */ + rpcs1 = (rpcs1 & ~ CS1_UAE) | ((ba >> (16 - CS1_V_UAE)) & CS1_UAE); + da = da + twc10 + (RP_NUMWD - 1); + if (da >= drv_tab[dtype].size) rpds[drv] = rpds[drv] | DS_LST; + da = da / RP_NUMWD; + rpda[drv] = da % drv_tab[dtype].sect; + da = da / drv_tab[dtype].sect; + rpda[drv] = rpda[drv] | ((da % drv_tab[dtype].surf) << DA_V_SF); + rpdc[drv] = da / drv_tab[dtype].surf; + + if (err != 0) { /* error? */ + set_rper (ER1_PAR, drv); /* set drive error */ + update_rpcs (CS1_DONE | CS1_TRE, drv); /* set done, err */ + perror ("RP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + + case FNC_WRITEH: /* write headers stub */ + update_rpcs (CS1_DONE, drv); /* set done */ + break; + } /* end case func */ + +return SCPE_OK; +} + +/* Set drive error */ + +void set_rper (int32 flag, int32 drv) +{ +rper1[drv] = rper1[drv] | flag; +rpds[drv] = rpds[drv] | DS_ATA; +rpcs1 = rpcs1 | CS1_SC; +return; +} + +/* Controller status update + + Check for done transition + Update drive status + Update RPCS1 + Update interrupt request +*/ + +void update_rpcs (int32 flag, int32 drv) +{ +int32 i; +UNIT *uptr; + +if ((flag & ~rpcs1) & CS1_DONE) /* DONE 0 to 1? */ + rpiff = (rpcs1 & CS1_IE)? 1: 0; /* CSTB INTR <- IE */ +uptr = rp_dev.units + drv; /* get unit */ +if (rp_unit[drv].flags & UNIT_DIS) rpds[drv] = rper1[drv] = 0; +else rpds[drv] = (rpds[drv] | DS_DPR) & ~DS_PGM; +if (rp_unit[drv].flags & UNIT_ATT) rpds[drv] = rpds[drv] | DS_MOL; +else rpds[drv] = rpds[drv] & ~(DS_MOL | DS_VV | DS_RDY); +if (rper1[drv] | rper2[drv] | rper3[drv]) + rpds[drv] = rpds[drv] | DS_ERR; +else rpds[drv] = rpds[drv] & ~DS_ERR; + +rpcs1 = (rpcs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ | CS1_DRV)) | CS1_DVA | flag; +rpcs1 = rpcs1 | (uptr->FUNC << CS1_V_FNC); +if (sim_is_active (uptr)) rpcs1 = rpcs1 | CS1_GO; +if (rpcs2 & CS2_ERR) rpcs1 = rpcs1 | CS1_TRE | CS1_SC; +else if (rpcs1 & CS1_TRE) rpcs1 = rpcs1 | CS1_SC; +for (i = 0; i < RP_NUMDR; i++) + if (rpds[i] & DS_ATA) rpcs1 = rpcs1 | CS1_SC; +if (rpiff || ((rpcs1 & CS1_SC) && (rpcs1 & CS1_DONE) && (rpcs1 & CS1_IE))) + int_req = int_req | INT_RP; +else int_req = int_req & ~INT_RP; +return; +} + +/* Interrupt acknowledge */ + +int32 rp_inta (void) +{ +rpcs1 = rpcs1 & ~CS1_IE; /* clear int enable */ +rpiff = 0; /* clear CSTB INTR */ +return VEC_RP; /* acknowledge */ +} + +/* Device reset */ + +t_stat rp_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rpcs1 = CS1_DVA | CS1_DONE; +rpcs2 = CS2_IR | CS2_OR; +rpba = rpwc = 0; +rpiff = 0; /* clear CSTB INTR */ +int_req = int_req & ~INT_RP; /* clear intr req */ +for (i = 0; i < RP_NUMDR; i++) { + uptr = rp_dev.units + i; + sim_cancel (uptr); + uptr->CYL = uptr->FUNC = 0; + if (uptr->flags & UNIT_ATT) rpds[i] = (rpds[i] & DS_VV) | + DS_DPR | DS_RDY | DS_MOL | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); + else if (uptr->flags & UNIT_DIS) rpds[i] = 0; + else rpds[i] = DS_DPR; + rper1[i] = 0; + rper2[i] = 0; + rper3[i] = 0; + rpda[i] = 0; + rpdc[i] = 0; + rpmr[i] = 0; + rpof[i] = 0; + rpec1[i] = 0; + rpec2[i] = 0; + rmmr2[i] = 0; + rmhr[i] = 0; + } +return SCPE_OK; +} + +/* Device attach */ + +t_stat rp_attach (UNIT *uptr, char *cptr) +{ +int32 drv, i, p; +t_stat r; + +uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +drv = (int32) (uptr - rp_dev.units); /* get drv number */ +rpds[drv] = DS_ATA | DS_MOL | DS_RDY | DS_DPR | + ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); +rper1[drv] = 0; +update_rpcs (CS1_SC, drv); + +if ((uptr->flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */ +if ((p = sim_fsize (uptr->fileref)) == 0) return SCPE_OK; +for (i = 0; drv_tab[i].sect != 0; i++) { + if (p <= (drv_tab[i].size * (int) sizeof (d10))) { + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr->capac = drv_tab[i].size; + return SCPE_OK; + } + } +return SCPE_OK; +} + +/* Device detach */ + +t_stat rp_detach (UNIT *uptr) +{ +int32 drv; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +drv = (int32) (uptr - rp_dev.units); /* get drv number */ +rpds[drv] = (rpds[drv] & ~(DS_MOL | DS_RDY | DS_WRL | DS_VV | DS_OF)) | + DS_ATA; +if (sim_is_active (uptr)) { /* unit active? */ + sim_cancel (uptr); /* cancel operation */ + rper1[drv] = rper1[drv] | ER1_OPI; /* set drive error */ + if (uptr->FUNC >= FNC_WCHK) /* data transfer? */ + rpcs1 = rpcs1 | CS1_DONE | CS1_TRE; /* set done, err */ + } +update_rpcs (CS1_SC, drv); /* request intr */ +return detach_unit (uptr); +} + +/* Set size command validation routine */ + +t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 dtype = GET_DTYPE (val); + +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = drv_tab[dtype].size; +return SCPE_OK; +} + +/* Device bootstrap */ + +#define BOOT_START 0377000 /* start */ +#define BOOT_LEN (sizeof (boot_rom_dec) / sizeof (d10)) + +static const d10 boot_rom_dec[] = { + 0515040000001, /* boot:hrlzi 1,1 ; uba # */ + 0201000140001, /* movei 0,140001 ; vld,fst,pg 1 */ + 0713001000000+(IOBA_UBMAP+1 & RMASK), /* wrio 0,763001(1); set ubmap */ + 0435040000000+(IOBA_RP & RMASK), /* iori 1,776700 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0713001000010, /* wrio 0,10(1) ; ->RPCS2 */ + 0201000000021, /* movei 0,21 ; preset */ + 0713001000000, /* wrio 0,0(1) ; ->RPCS1 */ + 0201100000001, /* movei 2,1 ; blk #1 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0306140505755, /* cain 3,sixbit /HOM/ */ + 0254000377023, /* jrst .+6 ; match */ + 0201100000010, /* movei 2,10 ; blk #10 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0302140505755, /* caie 3,sixbit /HOM/ */ + 0254200377022, /* halt . ; inv home */ + 0336100001103, /* skipn 2,1103 ; pg of ptrs */ + 0254200377024, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0336100001004, /* skipn 2,1004 ; mon boot */ + 0254200377027, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0254000001000, /* jrst 1000 ; start */ + 0201140176000, /* rdbl:movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_UNIT, /* move 5,FE_UNIT ; unit */ + 0200300000002, /* move 6,2 */ + 0242300777750, /* lsh 6,-24. ; cyl */ + 0713141000002, /* wrio 3,2(1) ; ->RPWC */ + 0713201000004, /* wrio 4,4(1) ; ->RPBA */ + 0713101000006, /* wrio 2,6(1) ; ->RPDA */ + 0713241000010, /* wrio 5,10(1) ; ->RPCS2 */ + 0713301000034, /* wrio 6,34(1) ; ->RPDC */ + 0201000000071, /* movei 0,71 ; read+go */ + 0713001000000, /* wrio 0,0(1) ; ->RPCS1 */ + 0712341000000, /* rdio 7,0(1) ; read csr */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377046, /* jrst .-2 ; loop */ + 0602340100000, /* trne 7,100000 ; test err */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ + }; + +static const d10 boot_rom_its[] = { + 0515040000001, /* boot:hrlzi 1,1 ; uba # */ + 0201000140001, /* movei 0,140001 ; vld,fst,pg 1 */ + 0715000000000+(IOBA_UBMAP+1 & RMASK), /* iowrq 0,763001 ; set ubmap */ + 0435040000000+(IOBA_RP & RMASK), /* iori 1,776700 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0715001000010, /* iowrq 0,10(1) ; ->RPCS2 */ + 0201000000021, /* movei 0,21 ; preset */ + 0715001000000, /* iowrq 0,0(1) ; ->RPCS1 */ + 0201100000001, /* movei 2,1 ; blk #1 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0306140505755, /* cain 3,sixbit /HOM/ */ + 0254000377023, /* jrst .+6 ; match */ + 0201100000010, /* movei 2,10 ; blk #10 */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0204140001000, /* movs 3,1000 ; id word */ + 0302140505755, /* caie 3,sixbit /HOM/ */ + 0254200377022, /* halt . ; inv home */ + 0336100001103, /* skipn 2,1103 ; pg of ptrs */ + 0254200377024, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0336100001004, /* skipn 2,1004 ; mon boot */ + 0254200377027, /* halt . ; inv ptr */ + 0265740377032, /* jsp 17,rdbl ; read */ + 0254000001000, /* jrst 1000 ; start */ + 0201140176000, /* rdbl:movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_UNIT, /* move 5,FE_UNIT ; unit */ + 0200300000002, /* move 6,2 */ + 0242300777750, /* lsh 6,-24. ; cyl */ + 0715141000002, /* iowrq 3,2(1) ; ->RPWC */ + 0715201000004, /* iowrq 4,4(1) ; ->RPBA */ + 0715101000006, /* iowrq 2,6(1) ; ->RPDA */ + 0715241000010, /* iowrq 5,10(1) ; ->RPCS2 */ + 0715301000034, /* iowrq 6,34(1) ; ->RPDC */ + 0201000000071, /* movei 0,71 ; read+go */ + 0715001000000, /* iowrq 0,0(1) ; ->RPCS1 */ + 0711341000000, /* iordq 7,0(1) ; read csr */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377046, /* jrst .-2 ; loop */ + 0602340100000, /* trne 7,100000 ; test err */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ + }; + +t_stat rp_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern a10 saved_PC; + +M[FE_UNIT] = unitno & CS2_M_UNIT; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = Q_ITS? boot_rom_its[i]: boot_rom_dec[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/PDP10/pdp10_sys.c b/PDP10/pdp10_sys.c new file mode 100644 index 0000000..e22c6d7 --- /dev/null +++ b/PDP10/pdp10_sys.c @@ -0,0 +1,859 @@ +/* pdp10_sys.c: PDP-10 simulator interface + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 01-Feb-07 RMS Added CD support + 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) + 09-Jan-03 RMS Added DEUNA/DELUA support + 12-Sep-02 RMS Added RX211 support + 22-Apr-02 RMS Removed magtape record length error + 17-Sep-01 RMS Removed multiconsole support + 25-Aug-01 RMS Enabled DZ11 + 27-May-01 RMS Added multiconsole support + 29-Apr-01 RMS Fixed format for RDPCST, WRPCST + Added CLRCSH for ITS + 03-Apr-01 RMS Added support for loading EXE files + 19-Mar-01 RMS Added support for loading SAV files + 30-Oct-00 RMS Added support for examine to file +*/ + +#include "pdp10_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE pag_dev; +extern DEVICE tim_dev; +extern DEVICE fe_dev; +extern DEVICE uba_dev; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE rp_dev; +extern DEVICE tu_dev; +extern DEVICE dz_dev; +extern DEVICE ry_dev; +extern DEVICE cr_dev; +extern DEVICE lp20_dev; +extern DEVICE xu_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern d10 *M; +extern a10 saved_PC; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-10"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &pag_dev, + &tim_dev, + &fe_dev, + &uba_dev, + &ptr_dev, + &ptp_dev, + &ry_dev, + &lp20_dev, + &cr_dev, + &rp_dev, + &tu_dev, + &dz_dev, + &xu_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "Illegal instruction", + "Illegal interrupt instruction", + "Paging error in interrupt", + "Zero vector table", + "NXM on UPT/EPT reference", + "Nested indirect address limit exceeded", + "Nested XCT limit exceeded", + "Invalid I/O controller", + "Address stop", + "Panic stop" + }; + +/* Binary loader, supports RIM10, SAV, EXE */ + +#define FMT_R 1 /* RIM10 */ +#define FMT_S 2 /* SAV */ +#define FMT_E 3 /* EXE */ + +#define EXE_DIR 01776 /* EXE directory */ +#define EXE_VEC 01775 /* EXE entry vec */ +#define EXE_PDV 01774 /* EXE ignored */ +#define EXE_END 01777 /* EXE end + +/* RIM10 loader + + RIM10 format is a binary paper tape format (all data frames + are 200 or greater). It consists of blocks containing + + -count,,origin-1 + word + : + word + checksum (includes IOWD) + : + JRST start +*/ + +d10 getrimw (FILE *fileref) +{ +int32 i, tmp; +d10 word; + +word = 0; +for (i = 0; i < 6;) { + if ((tmp = getc (fileref)) == EOF) return -1; + if (tmp & 0200) { + word = (word << 6) | ((d10) tmp & 077); + i++; + } + } +return word; +} + +t_stat load_rim (FILE *fileref) +{ +d10 count, cksm, data; +a10 pa; +int32 op; + +for ( ;; ) { /* loop until JRST */ + count = cksm = getrimw (fileref); /* get header */ + if (count < 0) return SCPE_FMT; /* read err? */ + if (TSTS (count)) { /* hdr = IOWD? */ + for ( ; TSTS (count); count = AOB (count)) { + data = getrimw (fileref); /* get data wd */ + if (data < 0) return SCPE_FMT; + cksm = cksm + data; /* add to cksm */ + pa = ((a10) count + 1) & AMASK; /* store */ + M[pa] = data; + } /* end for */ + data = getrimw (fileref); /* get cksm */ + if (data < 0) return SCPE_FMT; + if ((cksm + data) & DMASK) return SCPE_CSUM; /* test cksm */ + } /* end if count */ + else { + op = GET_OP (count); /* not IOWD */ + if (op != OP_JRST) return SCPE_FMT; /* JRST? */ + saved_PC = (a10) count & AMASK; /* set PC */ + break; + } /* end else */ + } /* end for */ +return SCPE_OK; +} + +/* SAV file loader + + SAV format is a disk file format (36b words). It consists of + blocks containing: + + -count,,origin-1 + word + : + word + : + JRST start +*/ + +t_stat load_sav (FILE *fileref) +{ +d10 count, data; +a10 pa; +int32 wc, op; + +for ( ;; ) { /* loop */ + wc = fxread (&count, sizeof (d10), 1, fileref); /* read IOWD */ + if (wc == 0) return SCPE_OK; /* done? */ + if (TSTS (count)) { /* IOWD? */ + for ( ; TSTS (count); count = AOB (count)) { + wc = fxread (&data, sizeof (d10), 1, fileref); + if (wc == 0) return SCPE_FMT; + pa = ((a10) count + 1) & AMASK; /* store data */ + M[pa] = data; + } /* end for */ + } /* end if count*/ + else { + op = GET_OP (count); /* not IOWD */ + if (op != OP_JRST) return SCPE_FMT; /* JRST? */ + saved_PC = (a10) count & AMASK; /* set PC */ + break; + } /* end else */ + } /* end for */ +return SCPE_OK; +} + +/* EXE file loader + + EXE format is a disk file format (36b words). It consists of + blocks containing: + + block type,,total words = n + n - 1 data words + + Block types are + + EXE_DIR (1776) directory + EXE_VEC (1775) entry vector + EXE_PDV (1774) optional blocks + EXE_END (1777) end block + + The directory blocks are the most important and contain doubleword + page loading information: + + word0<0:8> = flags + <9:35> = page in file (0 if 0 page) + word1<0:8> = repeat count - 1 + <9:35> = page in memory +*/ + +#define DIRSIZ (2 * PAG_SIZE) + +t_stat load_exe (FILE *fileref) +{ +d10 data, dirbuf[DIRSIZ], pagbuf[PAG_SIZE], entbuf[2]; +int32 ndir, entvec, i, j, k, cont, bsz, bty, rpt, wc; +int32 fpage, mpage; +a10 ma; + +ndir = entvec = 0; /* no dir, entvec */ +cont = 1; +do { + wc = fxread (&data, sizeof (d10), 1, fileref); /* read blk hdr */ + if (wc == 0) return SCPE_FMT; /* error? */ + bsz = (int32) ((data & RMASK) - 1); /* get count */ + if (bsz <= 0) return SCPE_FMT; /* zero? */ + bty = (int32) LRZ (data); /* get type */ + switch (bty) { /* case type */ + + case EXE_DIR: /* directory */ + if (ndir) return SCPE_FMT; /* got one */ + ndir = fxread (dirbuf, sizeof (d10), bsz, fileref); + if (ndir < bsz) return SCPE_FMT; /* error */ + break; + + case EXE_PDV: /* ??? */ + fseek (fileref, bsz * sizeof (d10), SEEK_CUR); + break; + + case EXE_VEC: /* entry vec */ + if (bsz != 2) return SCPE_FMT; /* must be 2 wds */ + entvec = fxread (entbuf, sizeof (d10), bsz, fileref); + if (entvec < 2) return SCPE_FMT; /* error? */ + cont = 0; /* stop */ + break; + + case EXE_END: /* end */ + if (bsz != 0) return SCPE_FMT; /* must be hdr */ + cont = 0; /* stop */ + break; + + default: + return SCPE_FMT; + } /* end switch */ + } while (cont); /* end do */ + +for (i = 0; i < ndir; i = i + 2) { /* loop thru dir */ + fpage = (int32) (dirbuf[i] & RMASK); /* file page */ + mpage = (int32) (dirbuf[i + 1] & RMASK); /* memory page */ + rpt = (int32) ((dirbuf[i + 1] >> 27) + 1); /* repeat count */ + for (j = 0; j < rpt; j++, mpage++) { /* loop thru rpts */ + if (fpage) { /* file pages? */ + fseek (fileref, (fpage << PAG_V_PN) * sizeof (d10), SEEK_SET); + wc = fxread (pagbuf, sizeof (d10), PAG_SIZE, fileref); + if (wc < PAG_SIZE) return SCPE_FMT; + fpage++; + } + ma = mpage << PAG_V_PN; /* mem addr */ + for (k = 0; k < PAG_SIZE; k++, ma++) { /* copy buf to mem */ + if (MEM_ADDR_NXM (ma)) return SCPE_NXM; + M[ma] = fpage? (pagbuf[k] & DMASK): 0; + } /* end copy */ + } /* end rpt */ + } /* end directory */ +if (entvec && entbuf[1]) + saved_PC = (int32) entbuf[1] & RMASK; /* start addr */ +return SCPE_OK; +} + +/* Master loader */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +d10 data; +int32 wc, fmt; +extern int32 sim_switches; + +fmt = 0; /* no fmt */ +if (sim_switches & SWMASK ('R')) fmt = FMT_R; /* -r? */ +else if (sim_switches & SWMASK ('S')) fmt = FMT_S; /* -s? */ +else if (sim_switches & SWMASK ('E')) fmt = FMT_E; /* -e? */ +else if (match_ext (fnam, "RIM")) fmt = FMT_R; /* .RIM? */ +else if (match_ext (fnam, "SAV")) fmt = FMT_S; /* .SAV? */ +else if (match_ext (fnam, "EXE")) fmt = FMT_E; /* .EXE? */ +else { + wc = fxread (&data, sizeof (d10), 1, fileref); /* read hdr */ + if (wc == 0) return SCPE_FMT; /* error? */ + if (LRZ (data) == EXE_DIR) fmt = FMT_E; /* EXE magic? */ + else if (TSTS (data)) fmt = FMT_S; /* SAV magic? */ + fseek (fileref, 0, SEEK_SET); /* rewind */ + } + +switch (fmt) { /* case fmt */ + + case FMT_R: /* RIM */ + return load_rim (fileref); + + case FMT_S: /* SAV */ + return load_sav (fileref); + + case FMT_E: /* EXE */ + return load_exe (fileref); + } + +printf ("Can't determine load file format\n"); +return SCPE_FMT; +} + +/* Symbol tables */ + +#define I_V_FL 39 /* inst class */ +#define I_M_FL 03 /* class mask */ +#define I_ITS 004000000000000 /* ITS flag */ +#define I_AC 000000000000000 /* AC, address */ +#define I_OP 010000000000000 /* address only */ +#define I_IO 020000000000000 /* classic I/O */ +#define I_V_AC 00 +#define I_V_OP 01 +#define I_V_IO 02 + +static const d10 masks[] = { + 0777000000000, 0777740000000, + 0700340000000, 0777777777777 + }; + +static const char *opcode[] = { +"XCTR", "XCTI", /* ITS only */ +"IORDI", "IORDQ", "IORD", "IOWR", "IOWRI", "IOWRQ", +"IORDBI", "IORDBQ", "IORDB", "IOWRB", "IOWRBI", "IOWRBQ", +"CLRCSH", "RDPCST", "WRPCST", +"SDBR1", "SDBR2", "SDBR3", "SDBR4", "SPM", +"LDBR1", "LDBR2", "LDBR3", "LDBR4", "LPMR", + +"PORTAL", "JRSTF", "HALT", /* AC defines op */ +"XJRSTF", "XJEN", "XPCW", +"JEN", "SFM", "XJRST", "IBP", +"JFOV", "JCRY1", "JCRY0", "JCRY", "JOV", + +"APRID", "WRAPR", "RDAPR", "WRPI", "RDPI", "RDUBR", "CLRPT", "WRUBR", +"WREBR", "RDEBR", +"RDSPB", "RDCSB", "RDPUR", "RDCSTM", "RDTIM", "RDINT", "RDHSB", +"WRSPB", "WRCSB", "WRPUR", "WRCSTM", "WRTIM", "WRINT", "WRHSB", + + "LUUO01", "LUUO02", "LUUO03", "LUUO04", "LUUO05", "LUUO06", "LUUO07", +"LUUO10", "LUUO11", "LUUO12", "LUUO13", "LUUO14", "LUUO15", "LUUO16", "LUUO17", +"LUUO20", "LUUO21", "LUUO22", "LUUO23", "LUUO24", "LUUO25", "LUUO26", "LUUO27", +"LUUO30", "LUUO31", "LUUO32", "LUUO33", "LUUO34", "LUUO35", "LUUO36", "LUUO37", +"MUUO40", "MUUO41", "MUUO42", "MUUO43", "MUUO44", "MUUO45", "MUUO46", "MUUO47", +"MUUO50", "MUUO51", "MUUO52", "MUUO53", "MUUO54", "MUUO55", "MUUO56", "MUUO57", +"MUUO60", "MUUO61", "MUUO62", "MUUO63", "MUUO64", "MUUO65", "MUUO66", "MUUO67", +"MUUO70", "MUUO71", "MUUO72", "MUUO73", "MUUO74", "MUUO75", "MUUO76", "MUUO77", + +"UJEN", "GFAD", "GFSB", "JSYS", "ADJSP", "GFMP", "GFDV ", +"DFAD", "DFSB", "DFMP", "DFDV", "DADD", "DSUB", "DMUL", "DDIV", +"DMOVE", "DMOVN", "FIX", "EXTEND", "DMOVEM", "DMOVNM", "FIXR", "FLTR", +"UFA", "DFN", "FSC", "ADJBP", "ILDB", "LDB", "IDPB", "DPB", +"FAD", "FADL", "FADM", "FADB", "FADR", "FADRL", "FADRM", "FADRB", +"FSB", "FSBL", "FSBM", "FSBB", "FSBR", "FSBRL", "FSBRM", "FSBRB", +"FMP", "FMPL", "FMPM", "FMPB", "FMPR", "FMPRL", "FMPRM", "FMPRB", +"FDV", "FDVL", "FDVM", "FDVB", "FDVR", "FDVRL", "FDVRM", "FDVRB", + +"MOVE", "MOVEI", "MOVEM", "MOVES", "MOVS", "MOVSI", "MOVSM", "MOVSS", +"MOVN", "MOVNI", "MOVNM", "MOVNS", "MOVM", "MOVMI", "MOVMM", "MOVMS", +"IMUL", "IMULI", "IMULM", "IMULB", "MUL", "MULI", "MULM", "MULB", +"IDIV", "IDIVI", "IDIVM", "IDIVB", "DIV", "DIVI", "DIVM", "DIVB", +"ASH", "ROT", "LSH", "JFFO", "ASHC", "ROTC", "LSHC", "CIRC", +"EXCH", "BLT", "AOBJP", "AOBJN", "JRST", "JFCL", "XCT", "MAP", +"PUSHJ", "PUSH", "POP", "POPJ", "JSR", "JSP", "JSA", "JRA", +"ADD", "ADDI", "ADDM", "ADDB", "SUB", "SUBI", "SUBM", "SUBB", + +"CAI", "CAIL", "CAIE", "CAILE", "CAIA", "CAIGE", "CAIN", "CAIG", +"CAM", "CAML", "CAME", "CAMLE", "CAMA", "CAMGE", "CAMN", "CAMG", +"JUMP", "JUMPL", "JUMPE", "JUMPLE", "JUMPA", "JUMPGE", "JUMPN", "JUMPG", +"SKIP", "SKIPL", "SKIPE", "SKIPLE", "SKIPA", "SKIPGE", "SKIPN", "SKIPG", +"AOJ", "AOJL", "AOJE", "AOJLE", "AOJA", "AOJGE", "AOJN", "AOJG", +"AOS", "AOSL", "AOSE", "AOSLE", "AOSA", "AOSGE", "AOSN", "AOSG", +"SOJ", "SOJL", "SOJE", "SOJLE", "SOJA", "SOJGE", "SOJN", "SOJG", +"SOS", "SOSL", "SOSE", "SOSLE", "SOSA", "SOSGE", "SOSN", "SOSG", + +"SETZ", "SETZI", "SETZM", "SETZB", "AND", "ANDI", "ANDM", "ANDB", +"ANDCA", "ANDCAI", "ANDCAM", "ANDCAB", "SETM", "SETMI", "SETMM", "SETMB", +"ANDCM", "ANDCMI", "ANDCMM", "ANDCMB", "SETA", "SETAI", "SETAM", "SETAB", +"XOR", "XORI", "XORM", "XORB", "IOR", "IORI", "IORM", "IORB", +"ANDCB", "ANDCBI", "ANDCBM", "ANDCBB", "EQV", "EQVI", "EQVM", "EQVB", +"SETCA", "SETCAI", "SETCAM", "SETCAB", "ORCA", "ORCAI", "ORCAM", "ORCAB", +"SETCM", "SETCMI", "SETCMM", "SETCMB", "ORCM", "ORCMI", "ORCMM", "ORCMB", +"ORCB", "ORCBI", "ORCBM", "ORCBB", "SETO", "SETOI", "SETOM", "SETOB", + +"HLL", "HLLI", "HLLM", "HLLS", "HRL", "HRLI", "HRLM", "HRLS", +"HLLZ", "HLLZI", "HLLZM", "HLLZS", "HRLZ", "HRLZI", "HRLZM", "HRLZS", +"HLLO", "HLLOI", "HLLOM", "HLLOS", "HRLO", "HRLOI", "HRLOM", "HRLOS", +"HLLE", "HLLEI", "HLLEM", "HLLES", "HRLE", "HRLEI", "HRLEM", "HRLES", +"HRR", "HRRI", "HRRM", "HRRS", "HLR", "HLRI", "HLRM", "HLRS", +"HRRZ", "HRRZI", "HRRZM", "HRRZS", "HLRZ", "HLRZI", "HLRZM", "HLRZS", +"HRRO", "HRROI", "HRROM", "HRROS", "HLRO", "HLROI", "HLROM", "HLROS", +"HRRE", "HRREI", "HRREM", "HRRES", "HLRE", "HLREI", "HLREM", "HLRES", + +"TRN", "TLN", "TRNE", "TLNE", "TRNA", "TLNA", "TRNN", "TLNN", +"TDN", "TSN", "TDNE", "TSNE", "TDNA", "TSNA", "TDNN", "TSNN", +"TRZ", "TLZ", "TRZE", "TLZE", "TRZA", "TLZA", "TRZN", "TLZN", +"TDZ", "TSZ", "TDZE", "TSZE", "TDZA", "TSZA", "TDZN", "TSZN", +"TRC", "TLC", "TRCE", "TLCE", "TRCA", "TLCA", "TRCN", "TLCN", +"TDC", "TSC", "TDCE", "TSCE", "TDCA", "TSCA", "TDCN", "TSCN", +"TRO", "TLO", "TROE", "TLOE", "TROA", "TLOA", "TRON", "TLON", +"TDO", "TSO", "TDOE", "TSOE", "TDOA", "TSOA", "TDON", "TSON", + +"UMOVE", "UMOVEM", /* KS10 I/O */ +"TIOE", "TION", "RDIO", "WRIO", +"BSIO", "BCIO", "BLTBU", "BLTUB", +"TIOEB", "TIONB", "RDIOB", "WRIOB", +"BSIOB", "BCIOB", + +"BLKI", "DATAI", "BLKO", "DATAO", /* classic I/O */ +"CONO", "CONI", "CONSZ", "CONSO", + +"CLEAR", "CLEARI", "CLEARM", "CLEARB", +"OR", "ORI", "ORM", "ORB", "XMOVEI", "XHLLI", /* alternate ops */ + + "CMPSL", "CMPSE", "CMPSLE", /* extended ops */ +"EDIT", "CMPSGE", "CMPSN", "CMPSG", +"CVTDBO", "CVTDBT", "CVTBDO", "CVTBDT", +"MOVSO", "MOVST", "MOVSLJ", "MOVSRJ", +"XBLT", "GSNGL", "GDBLE", "GDFIX", +"GFIX", "GDFIXR", "GFIXR", "DGFLTR", +"GFLTR", "GFSC", + +NULL +}; + +static const d10 opc_val[] = { + 0102000000000+I_AC+I_ITS, 0103000000000+I_AC+I_ITS, + 0710000000000+I_AC+I_ITS, 0711000000000+I_AC+I_ITS, 0712000000000+I_AC+I_ITS, + 0713000000000+I_AC+I_ITS, 0714000000000+I_AC+I_ITS, 0715000000000+I_AC+I_ITS, + 0720000000000+I_AC+I_ITS, 0721000000000+I_AC+I_ITS, 0722000000000+I_AC+I_ITS, + 0723000000000+I_AC+I_ITS, 0724000000000+I_AC+I_ITS, 0725000000000+I_AC+I_ITS, + 0701000000000+I_OP+I_ITS, 0701440000000+I_OP+I_ITS, 0701540000000+I_OP+I_ITS, + 0702000000000+I_OP+I_ITS, 0702040000000+I_OP+I_ITS, + 0702100000000+I_OP+I_ITS, 0702140000000+I_OP+I_ITS, 0702340000000+I_OP+I_ITS, + 0702400000000+I_OP+I_ITS, 0702440000000+I_OP+I_ITS, + 0702500000000+I_OP+I_ITS, 0702540000000+I_OP+I_ITS, 0702740000000+I_OP+I_ITS, + + 0254040000000+I_OP, 0254100000000+I_OP, + 0254200000000+I_OP, 0254240000000+I_OP, 0254300000000+I_OP, 0254340000000+I_OP, + 0254500000000+I_OP, 0254600000000+I_OP, 0254640000000+I_OP, 0133000000000+I_OP, + 0255040000000+I_OP, 0255100000000+I_OP, 0255200000000+I_OP, 0255300000000+I_OP, + 0255400000000+I_OP, + + 0700000000000+I_OP, 0700200000000+I_OP, 0700240000000+I_OP, 0700600000000+I_OP, + 0700640000000+I_OP, 0701040000000+I_OP, 0701100000000+I_OP, 0701140000000+I_OP, + 0701200000000+I_OP, 0701240000000+I_OP, + 0702000000000+I_OP, 0702040000000+I_OP, 0702100000000+I_OP, 0702140000000+I_OP, + 0702200000000+I_OP, 0702240000000+I_OP, 0702300000000+I_OP, + 0702400000000+I_OP, 0702440000000+I_OP, 0702500000000+I_OP, 0702540000000+I_OP, + 0702600000000+I_OP, 0702640000000+I_OP, 0702700000000+I_OP, + + 0001000000000+I_AC, 0002000000000+I_AC, 0003000000000+I_AC, + 0004000000000+I_AC, 0005000000000+I_AC, 0006000000000+I_AC, 0007000000000+I_AC, + 0010000000000+I_AC, 0011000000000+I_AC, 0012000000000+I_AC, 0013000000000+I_AC, + 0014000000000+I_AC, 0015000000000+I_AC, 0016000000000+I_AC, 0017000000000+I_AC, + 0020000000000+I_AC, 0021000000000+I_AC, 0022000000000+I_AC, 0023000000000+I_AC, + 0024000000000+I_AC, 0025000000000+I_AC, 0026000000000+I_AC, 0027000000000+I_AC, + 0030000000000+I_AC, 0031000000000+I_AC, 0032000000000+I_AC, 0033000000000+I_AC, + 0034000000000+I_AC, 0035000000000+I_AC, 0036000000000+I_AC, 0037000000000+I_AC, + 0040000000000+I_AC, 0041000000000+I_AC, 0042000000000+I_AC, 0043000000000+I_AC, + 0044000000000+I_AC, 0045000000000+I_AC, 0046000000000+I_AC, 0047000000000+I_AC, + 0050000000000+I_AC, 0051000000000+I_AC, 0052000000000+I_AC, 0053000000000+I_AC, + 0054000000000+I_AC, 0055000000000+I_AC, 0056000000000+I_AC, 0057000000000+I_AC, + 0060000000000+I_AC, 0061000000000+I_AC, 0062000000000+I_AC, 0063000000000+I_AC, + 0064000000000+I_AC, 0065000000000+I_AC, 0066000000000+I_AC, 0067000000000+I_AC, + 0070000000000+I_AC, 0071000000000+I_AC, 0072000000000+I_AC, 0073000000000+I_AC, + 0074000000000+I_AC, 0075000000000+I_AC, 0076000000000+I_AC, 0077000000000+I_AC, + + 0100000000000+I_AC, 0102000000000+I_AC, 0103000000000+I_AC, + 0104000000000+I_AC, 0105000000000+I_AC, 0106000000000+I_AC, 0107000000000+I_AC, + 0110000000000+I_AC, 0111000000000+I_AC, 0112000000000+I_AC, 0113000000000+I_AC, + 0114000000000+I_AC, 0115000000000+I_AC, 0116000000000+I_AC, 0117000000000+I_AC, + 0120000000000+I_AC, 0121000000000+I_AC, 0122000000000+I_AC, 0123000000000+I_AC, + 0124000000000+I_AC, 0125000000000+I_AC, 0126000000000+I_AC, 0127000000000+I_AC, + 0130000000000+I_AC, 0131000000000+I_AC, 0132000000000+I_AC, 0133000000000+I_AC, + 0134000000000+I_AC, 0135000000000+I_AC, 0136000000000+I_AC, 0137000000000+I_AC, + 0140000000000+I_AC, 0141000000000+I_AC, 0142000000000+I_AC, 0143000000000+I_AC, + 0144000000000+I_AC, 0145000000000+I_AC, 0146000000000+I_AC, 0147000000000+I_AC, + 0150000000000+I_AC, 0151000000000+I_AC, 0152000000000+I_AC, 0153000000000+I_AC, + 0154000000000+I_AC, 0155000000000+I_AC, 0156000000000+I_AC, 0157000000000+I_AC, + 0160000000000+I_AC, 0161000000000+I_AC, 0162000000000+I_AC, 0163000000000+I_AC, + 0164000000000+I_AC, 0165000000000+I_AC, 0166000000000+I_AC, 0167000000000+I_AC, + 0170000000000+I_AC, 0171000000000+I_AC, 0172000000000+I_AC, 0173000000000+I_AC, + 0174000000000+I_AC, 0175000000000+I_AC, 0176000000000+I_AC, 0177000000000+I_AC, + + 0200000000000+I_AC, 0201000000000+I_AC, 0202000000000+I_AC, 0203000000000+I_AC, + 0204000000000+I_AC, 0205000000000+I_AC, 0206000000000+I_AC, 0207000000000+I_AC, + 0210000000000+I_AC, 0211000000000+I_AC, 0212000000000+I_AC, 0213000000000+I_AC, + 0214000000000+I_AC, 0215000000000+I_AC, 0216000000000+I_AC, 0217000000000+I_AC, + 0220000000000+I_AC, 0221000000000+I_AC, 0222000000000+I_AC, 0223000000000+I_AC, + 0224000000000+I_AC, 0225000000000+I_AC, 0226000000000+I_AC, 0227000000000+I_AC, + 0230000000000+I_AC, 0231000000000+I_AC, 0232000000000+I_AC, 0233000000000+I_AC, + 0234000000000+I_AC, 0235000000000+I_AC, 0236000000000+I_AC, 0237000000000+I_AC, + 0240000000000+I_AC, 0241000000000+I_AC, 0242000000000+I_AC, 0243000000000+I_AC, + 0244000000000+I_AC, 0245000000000+I_AC, 0246000000000+I_AC, 0247000000000+I_AC+I_ITS, + 0250000000000+I_AC, 0251000000000+I_AC, 0252000000000+I_AC, 0253000000000+I_AC, + 0254000000000+I_AC, 0255000000000+I_AC, 0256000000000+I_AC, 0257000000000+I_AC, + 0260000000000+I_AC, 0261000000000+I_AC, 0262000000000+I_AC, 0263000000000+I_AC, + 0264000000000+I_AC, 0265000000000+I_AC, 0266000000000+I_AC, 0267000000000+I_AC, + 0270000000000+I_AC, 0271000000000+I_AC, 0272000000000+I_AC, 0273000000000+I_AC, + 0274000000000+I_AC, 0275000000000+I_AC, 0276000000000+I_AC, 0277000000000+I_AC, + + 0300000000000+I_AC, 0301000000000+I_AC, 0302000000000+I_AC, 0303000000000+I_AC, + 0304000000000+I_AC, 0305000000000+I_AC, 0306000000000+I_AC, 0307000000000+I_AC, + 0310000000000+I_AC, 0311000000000+I_AC, 0312000000000+I_AC, 0313000000000+I_AC, + 0314000000000+I_AC, 0315000000000+I_AC, 0316000000000+I_AC, 0317000000000+I_AC, + 0320000000000+I_AC, 0321000000000+I_AC, 0322000000000+I_AC, 0323000000000+I_AC, + 0324000000000+I_AC, 0325000000000+I_AC, 0326000000000+I_AC, 0327000000000+I_AC, + 0330000000000+I_AC, 0331000000000+I_AC, 0332000000000+I_AC, 0333000000000+I_AC, + 0334000000000+I_AC, 0335000000000+I_AC, 0336000000000+I_AC, 0337000000000+I_AC, + 0340000000000+I_AC, 0341000000000+I_AC, 0342000000000+I_AC, 0343000000000+I_AC, + 0344000000000+I_AC, 0345000000000+I_AC, 0346000000000+I_AC, 0347000000000+I_AC, + 0350000000000+I_AC, 0351000000000+I_AC, 0352000000000+I_AC, 0353000000000+I_AC, + 0354000000000+I_AC, 0355000000000+I_AC, 0356000000000+I_AC, 0357000000000+I_AC, + 0360000000000+I_AC, 0361000000000+I_AC, 0362000000000+I_AC, 0363000000000+I_AC, + 0364000000000+I_AC, 0365000000000+I_AC, 0366000000000+I_AC, 0367000000000+I_AC, + 0370000000000+I_AC, 0371000000000+I_AC, 0372000000000+I_AC, 0373000000000+I_AC, + 0374000000000+I_AC, 0375000000000+I_AC, 0376000000000+I_AC, 0377000000000+I_AC, + + 0400000000000+I_AC, 0401000000000+I_AC, 0402000000000+I_AC, 0403000000000+I_AC, + 0404000000000+I_AC, 0405000000000+I_AC, 0406000000000+I_AC, 0407000000000+I_AC, + 0410000000000+I_AC, 0411000000000+I_AC, 0412000000000+I_AC, 0413000000000+I_AC, + 0414000000000+I_AC, 0415000000000+I_AC, 0416000000000+I_AC, 0417000000000+I_AC, + 0420000000000+I_AC, 0421000000000+I_AC, 0422000000000+I_AC, 0423000000000+I_AC, + 0424000000000+I_AC, 0425000000000+I_AC, 0426000000000+I_AC, 0427000000000+I_AC, + 0430000000000+I_AC, 0431000000000+I_AC, 0432000000000+I_AC, 0433000000000+I_AC, + 0434000000000+I_AC, 0435000000000+I_AC, 0436000000000+I_AC, 0437000000000+I_AC, + 0440000000000+I_AC, 0441000000000+I_AC, 0442000000000+I_AC, 0443000000000+I_AC, + 0444000000000+I_AC, 0445000000000+I_AC, 0446000000000+I_AC, 0447000000000+I_AC, + 0450000000000+I_AC, 0451000000000+I_AC, 0452000000000+I_AC, 0453000000000+I_AC, + 0454000000000+I_AC, 0455000000000+I_AC, 0456000000000+I_AC, 0457000000000+I_AC, + 0460000000000+I_AC, 0461000000000+I_AC, 0462000000000+I_AC, 0463000000000+I_AC, + 0464000000000+I_AC, 0465000000000+I_AC, 0466000000000+I_AC, 0467000000000+I_AC, + 0470000000000+I_AC, 0471000000000+I_AC, 0472000000000+I_AC, 0473000000000+I_AC, + 0474000000000+I_AC, 0475000000000+I_AC, 0476000000000+I_AC, 0477000000000+I_AC, + + 0500000000000+I_AC, 0501000000000+I_AC, 0502000000000+I_AC, 0503000000000+I_AC, + 0504000000000+I_AC, 0505000000000+I_AC, 0506000000000+I_AC, 0507000000000+I_AC, + 0510000000000+I_AC, 0511000000000+I_AC, 0512000000000+I_AC, 0513000000000+I_AC, + 0514000000000+I_AC, 0515000000000+I_AC, 0516000000000+I_AC, 0517000000000+I_AC, + 0520000000000+I_AC, 0521000000000+I_AC, 0522000000000+I_AC, 0523000000000+I_AC, + 0524000000000+I_AC, 0525000000000+I_AC, 0526000000000+I_AC, 0527000000000+I_AC, + 0530000000000+I_AC, 0531000000000+I_AC, 0532000000000+I_AC, 0533000000000+I_AC, + 0534000000000+I_AC, 0535000000000+I_AC, 0536000000000+I_AC, 0537000000000+I_AC, + 0540000000000+I_AC, 0541000000000+I_AC, 0542000000000+I_AC, 0543000000000+I_AC, + 0544000000000+I_AC, 0545000000000+I_AC, 0546000000000+I_AC, 0547000000000+I_AC, + 0550000000000+I_AC, 0551000000000+I_AC, 0552000000000+I_AC, 0553000000000+I_AC, + 0554000000000+I_AC, 0555000000000+I_AC, 0556000000000+I_AC, 0557000000000+I_AC, + 0560000000000+I_AC, 0561000000000+I_AC, 0562000000000+I_AC, 0563000000000+I_AC, + 0564000000000+I_AC, 0565000000000+I_AC, 0566000000000+I_AC, 0567000000000+I_AC, + 0570000000000+I_AC, 0571000000000+I_AC, 0572000000000+I_AC, 0573000000000+I_AC, + 0574000000000+I_AC, 0575000000000+I_AC, 0576000000000+I_AC, 0577000000000+I_AC, + + 0600000000000+I_AC, 0601000000000+I_AC, 0602000000000+I_AC, 0603000000000+I_AC, + 0604000000000+I_AC, 0605000000000+I_AC, 0606000000000+I_AC, 0607000000000+I_AC, + 0610000000000+I_AC, 0611000000000+I_AC, 0612000000000+I_AC, 0613000000000+I_AC, + 0614000000000+I_AC, 0615000000000+I_AC, 0616000000000+I_AC, 0617000000000+I_AC, + 0620000000000+I_AC, 0621000000000+I_AC, 0622000000000+I_AC, 0623000000000+I_AC, + 0624000000000+I_AC, 0625000000000+I_AC, 0626000000000+I_AC, 0627000000000+I_AC, + 0630000000000+I_AC, 0631000000000+I_AC, 0632000000000+I_AC, 0633000000000+I_AC, + 0634000000000+I_AC, 0635000000000+I_AC, 0636000000000+I_AC, 0637000000000+I_AC, + 0640000000000+I_AC, 0641000000000+I_AC, 0642000000000+I_AC, 0643000000000+I_AC, + 0644000000000+I_AC, 0645000000000+I_AC, 0646000000000+I_AC, 0647000000000+I_AC, + 0650000000000+I_AC, 0651000000000+I_AC, 0652000000000+I_AC, 0653000000000+I_AC, + 0654000000000+I_AC, 0655000000000+I_AC, 0656000000000+I_AC, 0657000000000+I_AC, + 0660000000000+I_AC, 0661000000000+I_AC, 0662000000000+I_AC, 0663000000000+I_AC, + 0664000000000+I_AC, 0665000000000+I_AC, 0666000000000+I_AC, 0667000000000+I_AC, + 0670000000000+I_AC, 0671000000000+I_AC, 0672000000000+I_AC, 0673000000000+I_AC, + 0674000000000+I_AC, 0675000000000+I_AC, 0676000000000+I_AC, 0677000000000+I_AC, + + 0704000000000+I_AC, 0705000000000+I_AC, + 0710000000000+I_AC, 0711000000000+I_AC, 0712000000000+I_AC, 0713000000000+I_AC, + 0714000000000+I_AC, 0715000000000+I_AC, 0716000000000+I_AC, 0717000000000+I_AC, + 0720000000000+I_AC, 0721000000000+I_AC, 0722000000000+I_AC, 0723000000000+I_AC, + 0724000000000+I_AC, 0725000000000+I_AC, + + 0700000000000+I_IO, 0700040000000+I_IO, 0700100000000+I_IO, 0700140000000+I_IO, + 0700200000000+I_IO, 0700240000000+I_IO, 0700300000000+I_IO, 0700340000000+I_IO, + + 0400000000000+I_AC, 0401000000000+I_AC, 0402000000000+I_AC, 0403000000000+I_AC, + 0434000000000+I_AC, 0435000000000+I_AC, 0436000000000+I_AC, 0437000000000+I_AC, + 0415000000000+I_AC, 0501000000000+I_AC, + + 0001000000000+I_AC, 0002000000000+I_AC, 0003000000000+I_AC, + 0004000000000+I_AC, 0005000000000+I_AC, 0006000000000+I_AC, 0007000000000+I_AC, + 0010000000000+I_AC, 0011000000000+I_AC, 0012000000000+I_AC, 0013000000000+I_AC, + 0014000000000+I_AC, 0015000000000+I_AC, 0016000000000+I_AC, 0017000000000+I_AC, + 0020000000000+I_AC, 0021000000000+I_AC, 0022000000000+I_AC, 0023000000000+I_AC, + 0024000000000+I_AC, 0025000000000+I_AC, 0026000000000+I_AC, 0027000000000+I_AC, + 0030000000000+I_AC, 0031000000000+I_AC, + -1 + }; + +#define NUMDEV 6 + +static const char *devnam[NUMDEV] = { + "APR", "PI", "PAG", "CCA", "TIM", "MTR" + }; + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) ((x) + 040) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, j, c, cflag, ac, xr, y, dev; +d10 inst; + +inst = val[0]; +cflag = (uptr == NULL) || (uptr == &cpu_unit); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC ((int32) (inst & 0177))); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* character? */ + for (i = 30; i >= 0; i = i - 6) { + c = (int32) ((inst >> i) & 077); + fprintf (of, "%c", SIXTOASC (c)); + } + return SCPE_OK; + } +if (sw & SWMASK ('P')) { /* packed? */ + for (i = 29; i >= 0; i = i - 7) { + c = (int32) ((inst >> i) & 0177); + fprintf (of, FMTASC (c)); + } + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +ac = GET_AC (inst); +xr = GET_XR (inst); +y = GET_ADDR (inst); +dev = GET_DEV (inst); +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ + if (((opc_val[i] & DMASK) == (inst & masks[j])) && /* match? */ + (((opc_val[i] & I_ITS) == 0) || Q_ITS)) { + fprintf (of, "%s ", opcode[i]); /* opcode */ + switch (j) { /* case on class */ + + case I_V_AC: /* AC + address */ + fprintf (of, "%-o,", ac); /* print AC, fall thru */ + case I_V_OP: /* address only */ + if (inst & INST_IND) fprintf (of, "@"); + if (xr) fprintf (of, "%-o(%-o)", y, xr); + else fprintf (of, "%-o", y); + break; + + case I_V_IO: /* I/O */ + if (dev < NUMDEV) fprintf (of, "%s,", devnam[dev]); + else fprintf (of, "%-o,", dev); + if (inst & INST_IND) fprintf (of, "@"); + if (xr) fprintf (of, "%-o(%-o)", y, xr); + else fprintf (of, "%-o", y); + break; + } /* end case */ + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get operand, including indirect and index + + Inputs: + *cptr = pointer to input string + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_opnd (char *cptr, t_stat *status) +{ +int32 sign = 0; +t_value val, xr = 0, ind = 0; +char *tptr; + +*status = SCPE_ARG; /* assume fail */ +if (*cptr == '@') { + ind = INST_IND; + cptr++; + } +if (*cptr == '+') cptr++; +else if (*cptr == '-') { + sign = 1; + cptr++; + } +val = strtotv (cptr, &tptr, 8); +if (val > 0777777) return 0; +if (sign) val = (~val + 1) & 0777777; +cptr = tptr; +if (*cptr == '(') { + cptr++; + xr = strtotv (cptr, &tptr, 8); + if ((cptr == tptr) || (*tptr != ')') || + (xr > AC_NUM) || (xr == 0)) return 0; + cptr = ++tptr; + } +if (*cptr == 0) *status = SCPE_OK; +return (ind | (xr << 18) | val); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, i, j; +t_value ac, dev; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; +for (i = 0; i < 6; i++) { + if (cptr[i] == 0) { + for (j = i + 1; j <= 6; j++) cptr[j] = 0; + break; + } + } +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0]; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + for (i = 0; i < 6; i++) { + val[0] = (val[0] << 6); + if (cptr[i]) val[0] = val[0] | + ((t_value) ((cptr[i] + 040) & 077)); + } + return SCPE_OK; + } +if ((sw & SWMASK ('P')) || ((*cptr == '#') && cptr++)) { /* packed string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + for (i = 0; i < 5; i++) val[0] = (val[0] << 7) | ((t_value) cptr[i]); + val[0] = val[0] << 1; + return SCPE_OK; + } + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & DMASK; /* get value */ +j = (int32) ((opc_val[i] >> I_V_FL) & I_M_FL); /* get class */ +switch (j) { /* case on class */ + + case I_V_AC: /* AC + operand */ + if (strchr (cptr, ',')) { /* AC specified? */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if (gbuf[0]) { /* can be omitted */ + ac = get_uint (gbuf, 8, AC_NUM - 1, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (ac << INST_V_AC); + } + } /* fall through */ + case I_V_OP: /* operand */ + cptr = get_glyph (cptr, gbuf, 0); + val[0] = val[0] | get_opnd (gbuf, &r); + if (r != SCPE_OK) return SCPE_ARG; + break; + + case I_V_IO: /* I/O */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + for (dev = 0; (dev < NUMDEV) && (strcmp (devnam[dev], gbuf) != 0); dev++); + if (dev >= NUMDEV) { + dev = get_uint (gbuf, 8, INST_M_DEV, &r); + if (r != SCPE_OK) return SCPE_ARG; + } + val[0] = val[0] | (dev << INST_V_DEV); + cptr = get_glyph (cptr, gbuf, 0); + val[0] = val[0] | get_opnd (gbuf, &r); + if (r != SCPE_OK) return SCPE_ARG; + break; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/PDP10/pdp10_tim.c b/PDP10/pdp10_tim.c new file mode 100644 index 0000000..b67dfda --- /dev/null +++ b/PDP10/pdp10_tim.c @@ -0,0 +1,305 @@ +/* pdp10_tim.c: PDP-10 tim subsystem simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tim timer subsystem + + 18-Jun-07 RMS Added UNIT_IDLE flag + 03-Nov-06 RMS Rewritten to support idling + 29-Oct-06 RMS Added clock coscheduling function + 02-Feb-04 RMS Exported variables needed by Ethernet simulator + 29-Jan-02 RMS New data structures + 06-Jan-02 RMS Added enable/disable support + 02-Dec-01 RMS Fixed bug in ITS PC sampling (found by Dave Conroy) + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 17-Jul-01 RMS Moved function prototype + 04-Jul-01 RMS Added DZ11 support +*/ + +#include "pdp10_defs.h" +#include + +/* Invariants */ + +#define TIM_HW_FREQ 4100000 /* 4.1Mhz */ +#define TIM_HWRE_MASK 07777 +#define UNIT_V_Y2K (UNIT_V_UF + 0) /* Y2K compliant OS */ +#define UNIT_Y2K (1u << UNIT_V_Y2K) + +/* Clock mode TOPS-10/ITS */ + +#define TIM_TPS_T10 60 +#define TIM_WAIT_T10 8000 +#define TIM_MULT_T10 1 +#define TIM_ITS_QUANT (TIM_HW_FREQ / TIM_TPS_T10) + +/* Clock mode TOPS-20/KLAD */ + +#define TIM_TPS_T20 1001 +#define TIM_WAIT_T20 500 +#define TIM_MULT_T20 16 + +/* Probability function for TOPS-20 idlelock */ + +#define PROB(x) (((rand() * 100) / RAND_MAX) >= (x)) + +d10 tim_base[2] = { 0, 0 }; /* 71b timebase */ +d10 tim_ttg = 0; /* time to go */ +d10 tim_period = 0; /* period */ +d10 quant = 0; /* ITS quantum */ +int32 tim_mult = TIM_MULT_T10; /* tmxr poll mult */ +int32 tim_t20_prob = 33; /* TOPS-20 prob */ + +/* Exported variables */ + +int32 clk_tps = TIM_TPS_T10; /* clock ticks/sec */ +int32 tmr_poll = TIM_WAIT_T10; /* clock poll */ +int32 tmxr_poll = TIM_WAIT_T10 * TIM_MULT_T10; /* term mux poll */ + +extern int32 apr_flg, pi_act; +extern UNIT cpu_unit; +extern d10 pcst; +extern a10 pager_PC; +extern int32 t20_idlelock; + +DEVICE tim_dev; +t_stat tcu_rd (int32 *data, int32 PA, int32 access); +t_stat tim_svc (UNIT *uptr); +t_stat tim_reset (DEVICE *dptr); +void tim_incr_base (d10 *base, d10 incr); + +extern d10 Read (a10 ea, int32 prv); +extern d10 ReadM (a10 ea, int32 prv); +extern void Write (a10 ea, d10 val, int32 prv); +extern void WriteP (a10 ea, d10 val); +extern int32 pi_eval (void); +extern t_stat wr_nop (int32 data, int32 PA, int32 access); + +/* TIM data structures + + tim_dev TIM device descriptor + tim_unit TIM unit descriptor + tim_reg TIM register list +*/ + +DIB tcu_dib = { IOBA_TCU, IOLN_TCU, &tcu_rd, &wr_nop, 0 }; + +UNIT tim_unit = { UDATA (&tim_svc, UNIT_IDLE, 0), TIM_WAIT_T10 }; + +REG tim_reg[] = { + { BRDATA (TIMEBASE, tim_base, 8, 36, 2) }, + { ORDATA (TTG, tim_ttg, 36) }, + { ORDATA (PERIOD, tim_period, 36) }, + { ORDATA (QUANT, quant, 36) }, + { DRDATA (TIME, tim_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (PROB, tim_t20_prob, 6), REG_NZ + PV_LEFT + REG_HIDDEN }, + { DRDATA (POLL, tmr_poll, 32), REG_HRO + PV_LEFT }, + { DRDATA (MUXPOLL, tmxr_poll, 32), REG_HRO + PV_LEFT }, + { DRDATA (MULT, tim_mult, 6), REG_HRO + PV_LEFT }, + { DRDATA (TPS, clk_tps, 12), REG_HRO + PV_LEFT }, + { NULL } + }; + +MTAB tim_mod[] = { + { UNIT_Y2K, 0, "non Y2K OS", "NOY2K", NULL }, + { UNIT_Y2K, UNIT_Y2K, "Y2K OS", "Y2K", NULL }, + { MTAB_XTD|MTAB_VDV, 000, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { 0 } + }; + +DEVICE tim_dev = { + "TIM", &tim_unit, tim_reg, tim_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &tim_reset, + NULL, NULL, NULL, + &tcu_dib, DEV_UBUS + }; + +/* Timer instructions */ + +/* Timer - if the timer is running at less than hardware frequency, + need to interpolate the value by calculating how much of the current + clock tick has elapsed, and what that equates to in msec. */ + +t_bool rdtim (a10 ea, int32 prv) +{ +d10 tempbase[2]; + +ReadM (INCA (ea), prv); /* check 2nd word */ +tempbase[0] = tim_base[0]; /* copy time base */ +tempbase[1] = tim_base[1]; +if (tim_mult != TIM_MULT_T20) { /* interpolate? */ + int32 used; + d10 incr; + used = tmr_poll - (sim_is_active (&tim_unit) - 1); + incr = (d10) (((double) used * TIM_HW_FREQ) / + ((double) tmr_poll * (double) clk_tps)); + tim_incr_base (tempbase, incr); + } +tempbase[0] = tempbase[0] & ~((d10) TIM_HWRE_MASK); /* clear low 12b */ +Write (ea, tempbase[0], prv); +Write (INCA(ea), tempbase[1], prv); +return FALSE; +} + +t_bool wrtim (a10 ea, int32 prv) +{ +tim_base[0] = Read (ea, prv); +tim_base[1] = CLRS (Read (INCA (ea), prv)); +return FALSE; +} + +t_bool rdint (a10 ea, int32 prv) +{ +Write (ea, tim_period, prv); +return FALSE; +} + +t_bool wrint (a10 ea, int32 prv) +{ +tim_period = Read (ea, prv); +tim_ttg = tim_period; +return FALSE; +} + +/* Timer service - the timer is only serviced when the 'ttg' register + has reached 0 based on the expected frequency of clock interrupts. */ + +t_stat tim_svc (UNIT *uptr) +{ +if (cpu_unit.flags & UNIT_KLAD) /* diags? */ + tmr_poll = uptr->wait; /* fixed clock */ +else tmr_poll = sim_rtc_calb (clk_tps); /* else calibrate */ +sim_activate (uptr, tmr_poll); /* reactivate unit */ +tmxr_poll = tmr_poll * tim_mult; /* set mux poll */ +tim_incr_base (tim_base, tim_period); /* incr time base */ +tim_ttg = tim_period; /* reload */ +apr_flg = apr_flg | APRF_TIM; /* request interrupt */ +if (Q_ITS) { /* ITS? */ + if (pi_act == 0) + quant = (quant + TIM_ITS_QUANT) & DMASK; + if (TSTS (pcst)) { /* PC sampling? */ + WriteP ((a10) pcst & AMASK, pager_PC); /* store sample */ + pcst = AOB (pcst); /* add 1,,1 */ + } + } /* end ITS */ +else if (t20_idlelock && PROB (100 - tim_t20_prob)) + t20_idlelock = 0; +return SCPE_OK; +} + +/* Clock coscheduling routine */ + +int32 clk_cosched (int32 wait) +{ +int32 t; + +if (tim_mult == TIM_MULT_T20) return wait; +t = sim_is_active (&tim_unit); +return (t? t - 1: wait); +} + +void tim_incr_base (d10 *base, d10 incr) +{ +base[1] = base[1] + incr; /* add on incr */ +base[0] = base[0] + (base[1] >> 35); /* carry to high */ +base[0] = base[0] & DMASK; /* mask high */ +base[1] = base[1] & MMASK; /* mask low */ +return; +} + +/* Timer reset */ + +t_stat tim_reset (DEVICE *dptr) +{ +tim_period = 0; /* clear timer */ +tim_ttg = 0; +apr_flg = apr_flg & ~APRF_TIM; /* clear interrupt */ +tmr_poll = sim_rtc_init (tim_unit.wait); /* init timer */ +sim_activate_abs (&tim_unit, tmr_poll); /* activate unit */ +tmxr_poll = tmr_poll * tim_mult; /* set mux poll */ +return SCPE_OK; +} + +/* Set timer parameters from CPU model */ + +t_stat tim_set_mod (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val & (UNIT_T20|UNIT_KLAD)) { + clk_tps = TIM_TPS_T20; + uptr->wait = TIM_WAIT_T20; + tmr_poll = TIM_WAIT_T20; + tim_mult = TIM_MULT_T20; + uptr->flags = uptr->flags | UNIT_Y2K; + } +else { + clk_tps = TIM_TPS_T10; + uptr->wait = TIM_WAIT_T10; + tmr_poll = TIM_WAIT_T10; + tim_mult = TIM_MULT_T10; + if (Q_ITS) uptr->flags = uptr->flags | UNIT_Y2K; + else uptr->flags = uptr->flags & ~UNIT_Y2K; + } +tmxr_poll = tmr_poll * tim_mult; +return SCPE_OK; +} + +/* Time of year clock */ + +t_stat tcu_rd (int32 *data, int32 PA, int32 access) +{ +time_t curtim; +struct tm *tptr; + +curtim = time (NULL); /* get time */ +tptr = localtime (&curtim); /* decompose */ +if (tptr == NULL) return SCPE_NXM; /* Y2K prob? */ +if ((tptr->tm_year > 99) && !(tim_unit.flags & UNIT_Y2K)) + tptr->tm_year = 99; + +switch ((PA >> 1) & 03) { /* decode PA<3:1> */ + + case 0: /* year/month/day */ + *data = (((tptr->tm_year) & 0177) << 9) | + (((tptr->tm_mon + 1) & 017) << 5) | + ((tptr->tm_mday) & 037); + return SCPE_OK; + + case 1: /* hour/minute */ + *data = (((tptr->tm_hour) & 037) << 8) | + ((tptr->tm_min) & 077); + return SCPE_OK; + + case 2: /* second */ + *data = (tptr->tm_sec) & 077; + return SCPE_OK; + + case 3: /* status */ + *data = CSR_DONE; + return SCPE_OK; + } + +return SCPE_NXM; /* can't get here */ +} diff --git a/PDP10/pdp10_tu.c b/PDP10/pdp10_tu.c new file mode 100644 index 0000000..a430f71 --- /dev/null +++ b/PDP10/pdp10_tu.c @@ -0,0 +1,1233 @@ +/* pdp10_tu.c - PDP-10 RH11/TM03/TU45 magnetic tape simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tu RH11/TM03/TU45 magtape + + 29-Apr-07 RMS Fixed bug in setting FCE on TMK (found by Naoki Hamada) + 16-Feb-06 RMS Added tape capacity checking + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 31-Mar-05 RMS Fixed bug, ERASE/WREOF incorrectly clear CS1 + Fixed inaccuracies in error reporting + 18-Mar-05 RMS Added attached test to detach routine + 23-Oct-04 RMS Fixed setting done on non data transfers + 01-Oct-04 RMS Modified to set FCE on read short record, eof + Implemented write check + TM03 uses only den<2> for validity test + TMK is cleared by new motion command, not DCLR + 14-Sep-04 RMS Fixed RIP value + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised for magtape library + 27-Jan-03 RMS Changed to dynamically allocate buffer + 21-Nov-02 RMS Fixed bug in bootstrap (reported by Michael Thompson) + Fixed bug in read (reported by Harris Newman) + 29-Sep-02 RMS Added variable vector support + New data structures + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Changed record length error code + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Changed POS, FLG, UST to arrays + 23-Oct-01 RMS Fixed bug in error interrupts + New IO page address constants + 05-Oct-01 RMS Rewrote interrupt handling from schematics + 30-Sep-01 RMS Fixed handling of non-existent formatters + 28-Sep-01 RMS Fixed interrupt handling for SC/ATA + 4-May-01 RMS Fixed bug in odd address test + 3-May-01 RMS Fixed drive reset to clear SSC + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number, sign = error + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number, sign = error + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. + + WARNING: The interupt logic of the RH11/RH70 is unusual and must be + simulated with great precision. The RH11 has an internal interrupt + request flop, CSTB INTR, which is controlled as follows: + - Writing IE and DONE simultaneously sets CSTB INTR + - Controller clear, INIT, and interrupt acknowledge clear CSTB INTR + (and also clear IE) + - A transition of DONE from 0 to 1 sets CSTB from INTR + The output of INTR is OR'd with the AND of RPCS1 to + create the interrupt request signal. Thus, + - The DONE interrupt is edge sensitive, but the SC interrupt is + level sensitive. + - The DONE interrupt, once set, is not disabled if IE is cleared, + but the SC interrupt is. +*/ + +#include "pdp10_defs.h" +#include "sim_tape.h" + +#define TU_NUMFM 1 /* #formatters */ +#define TU_NUMDR 8 /* #drives */ +#define USTAT u3 /* unit status */ +#define UDENS u4 /* unit density */ +#define UD_UNK 0 /* unknown */ +#define MT_MAXFR (1 << 16) /* max data buf */ + +/* MTCS1 - 172440 - control/status 1 */ + +#define CS1_GO CSR_GO /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 037 /* function mask */ +#define CS1_N_FNC (CS1_M_FNC + 1) +#define CS1_FNC (CS1_M_FNC << CS1_V_FNC) +#define FNC_NOP 000 /* no operation */ +#define FNC_UNLOAD 001 /* unload */ +#define FNC_REWIND 003 /* rewind */ +#define FNC_FCLR 004 /* formatter clear */ +#define FNC_RIP 010 /* read in preset */ +#define FNC_ERASE 012 /* erase tape */ +#define FNC_WREOF 013 /* write tape mark */ +#define FNC_SPACEF 014 /* space forward */ +#define FNC_SPACER 015 /* space reverse */ +#define FNC_XFER 024 /* >=? data xfr */ +#define FNC_WCHKF 024 /* write check */ +#define FNC_WCHKR 027 /* write check rev */ +#define FNC_WRITE 030 /* write */ +#define FNC_READF 034 /* read forward */ +#define FNC_READR 037 /* read reverse */ +#define CS1_IE CSR_IE /* int enable */ +#define CS1_DONE CSR_DONE /* ready */ +#define CS1_V_UAE 8 /* Unibus addr ext */ +#define CS1_M_UAE 03 +#define CS1_UAE (CS1_M_UAE << CS1_V_UAE) +#define CS1_DVA 0004000 /* drive avail NI */ +#define CS1_MCPE 0020000 /* Mbus par err NI */ +#define CS1_TRE 0040000 /* transfer err */ +#define CS1_SC 0100000 /* special cond */ +#define CS1_MBZ 0012000 +#define CS1_DRV (CS1_FNC | CS1_GO) +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) +#define GET_UAE(x) (((x) & CS1_UAE) << (16 - CS1_V_UAE)) + +/* MTWC - 172442 - word count */ + +/* MTBA - 172444 - base address */ + +#define BA_MBZ 0000001 /* must be zero */ + +/* MTFC - 172446 - frame count */ + +/* MTCS2 - 172450 - control/status 2 */ + +#define CS2_V_FMTR 0 /* formatter select */ +#define CS2_M_FMTR 07 +#define CS2_FMTR (CS2_M_FMTR << CS2_V_FMTR) +#define CS2_UAI 0000010 /* addr inhibit NI */ +#define CS2_PAT 0000020 /* parity test NI */ +#define CS2_CLR 0000040 /* controller clear */ +#define CS2_IR 0000100 /* input ready */ +#define CS2_OR 0000200 /* output ready */ +#define CS2_MDPE 0000400 /* Mbus par err NI */ +#define CS2_MXF 0001000 /* missed xfer NI */ +#define CS2_PGE 0002000 /* program err */ +#define CS2_NEM 0004000 /* nx mem err */ +#define CS2_NEF 0010000 /* nx fmter err */ +#define CS2_PE 0020000 /* parity err NI */ +#define CS2_WCE 0040000 /* write chk err */ +#define CS2_DLT 0100000 /* data late NI */ +#define CS2_MBZ (CS2_CLR | CS2_WCE) +#define CS2_RW (CS2_FMTR | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) +#define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ + CS2_NEF | CS2_PE | CS2_DLT ) +#define GET_FMTR(x) (((x) >> CS2_V_FMTR) & CS2_M_FMTR) + +/* MTFS - 172452 - formatter status + + indicates kept in drive status + ^ indicates calculated on the fly +*/ + +#define FS_SAT 0000001 /* slave attention */ +#define FS_BOT 0000002 /* ^beginning of tape */ +#define FS_TMK 0000004 /* end of file */ +#define FS_ID 0000010 /* ID burst detected */ +#define FS_SLOW 0000020 /* slowing down NI */ +#define FS_PE 0000040 /* ^PE status */ +#define FS_SSC 0000100 /* slave stat change */ +#define FS_RDY 0000200 /* ^formatter ready */ +#define FS_FPR 0000400 /* formatter present */ +#define FS_EOT 0002000 /* +end of tape */ +#define FS_WRL 0004000 /* ^write locked */ +#define FS_MOL 0010000 /* ^medium online */ +#define FS_PIP 0020000 /* +pos in progress */ +#define FS_ERR 0040000 /* ^error */ +#define FS_ATA 0100000 /* attention active */ +#define FS_REW 0200000 /* +rewinding */ + +#define FS_DYN (FS_ERR | FS_PIP | FS_MOL | FS_WRL | FS_EOT | \ + FS_RDY | FS_PE | FS_BOT) + +/* MTER - 172454 - error register */ + +#define ER_ILF 0000001 /* illegal func */ +#define ER_ILR 0000002 /* illegal register */ +#define ER_RMR 0000004 /* reg mod refused */ +#define ER_MCP 0000010 /* Mbus cpar err NI */ +#define ER_FER 0000020 /* format sel err */ +#define ER_MDP 0000040 /* Mbus dpar err NI */ +#define ER_VPE 0000100 /* vert parity err */ +#define ER_CRC 0000200 /* CRC err NI */ +#define ER_NSG 0000400 /* non std gap err NI */ +#define ER_FCE 0001000 /* frame count err */ +#define ER_ITM 0002000 /* inv tape mark NI */ +#define ER_NXF 0004000 /* wlock or fnc err */ +#define ER_DTE 0010000 /* time err NI */ +#define ER_OPI 0020000 /* op incomplete */ +#define ER_UNS 0040000 /* drive unsafe */ +#define ER_DCK 0100000 /* data check NI */ + +/* MTAS - 172456 - attention summary */ + +#define AS_U0 0000001 /* unit 0 flag */ + +/* MTCC - 172460 - check character, read only */ + +#define CC_MBZ 0177000 /* must be zero */ + +/* MTDB - 172462 - data buffer */ + +/* MTMR - 172464 - maintenance register */ + +#define MR_RW 0177637 /* read/write */ + +/* MTDT - 172466 - drive type */ + +#define DT_NSA 0100000 /* not sect addr */ +#define DT_TAPE 0040000 /* tape */ +#define DT_PRES 0002000 /* slave present */ +#define DT_TM03 0000040 /* TM03 formatter */ +#define DT_OFF 0000010 /* drive off */ +#define DT_TE16 0000011 /* TE16 */ +#define DT_TU45 0000012 /* TU45 */ +#define DT_TU77 0000014 /* TU77 */ + +/* MTSN - 172470 - serial number */ + +/* MTTC - 172472 - tape control register */ + +#define TC_V_UNIT 0 /* unit select */ +#define TC_M_UNIT 07 +#define TC_V_EVN 0000010 /* even parity */ +#define TC_V_FMT 4 /* format select */ +#define TC_M_FMT 017 +#define TC_10C 00 /* PDP-10 core dump */ +#define TC_IND 03 /* industry standard */ +#define TC_V_DEN 8 /* density select */ +#define TC_M_DEN 07 +#define TC_800 3 /* 800 bpi */ +#define TC_1600 4 /* 1600 bpi */ +#define TC_AER 0010000 /* abort on error */ +#define TC_SAC 0020000 /* slave addr change */ +#define TC_FCS 0040000 /* frame count status */ +#define TC_ACC 0100000 /* accelerating NI */ +#define TC_RW 0013777 +#define TC_MBZ 0004000 +#define TC_RIP ((TC_800 << TC_V_DEN) || (TC_10C << TC_V_FMT)) +#define GET_DEN(x) (((x) >> TC_V_DEN) & TC_M_DEN) +#define GET_FMT(x) (((x) >> TC_V_FMT) & TC_M_FMT) +#define GET_DRV(x) (((x) >> TC_V_UNIT) & TC_M_UNIT) + +/* Mapping macros */ + +#define XWC_MBZ 0000001 /* wc<0> mbz */ +#define XBA_MBZ 0000001 /* addr<0> mbz */ +#define XBA_ODD 0000002 /* odd address */ +#define TXFR(b,w,od) if (((b) & XBA_MBZ) || ((w) & XWC_MBZ) || \ + (((b) & XBA_ODD) != ((od) << 1))) { \ + tucs2 = tucs2 | CS2_NEM; \ + ubcs[1] = ubcs[1] | UBCS_TMO; \ + tucs1 = tucs1 & ~CS1_GO; \ + update_tucs (CS1_DONE, drv); \ + return SCPE_OK; \ + } +#define NEWPAGE(v,m) (((v) & PAG_M_OFF) == (m)) +#define MAPM(v,p,f) vpn = PAG_GETVPN (v); \ + if ((vpn >= UMAP_MEMSIZE) || ((ubmap[1][vpn] & \ + (UMAP_VLD | UMAP_DSB | UMAP_RRV)) != \ + (UMAP_VLD | f))) { \ + tucs2 = tucs2 | CS2_NEM; \ + ubcs[1] = ubcs[1] | UBCS_TMO; \ + break; \ + } \ + p = (ubmap[1][vpn] + PAG_GETOFF (v)) & PAMASK; \ + if (MEM_ADDR_NXM (p)) { \ + tucs2 = tucs2 | CS2_NEM; \ + ubcs[1] = ubcs[1] | UBCS_TMO; \ + break; \ + } + +extern d10 *M; /* memory */ +extern int32 int_req; +extern int32 ubmap[UBANUM][UMAP_MEMSIZE]; /* Unibus map */ +extern int32 ubcs[UBANUM]; +extern UNIT cpu_unit; +extern int32 sim_switches; +extern FILE *sim_deb; + +int32 tucs1 = 0; /* control/status 1 */ +int32 tuwc = 0; /* word count */ +int32 tuba = 0; /* bus address */ +int32 tufc = 0; /* frame count */ +int32 tucs2 = 0; /* control/status 2 */ +int32 tufs = 0; /* formatter status */ +int32 tuer = 0; /* error status */ +int32 tucc = 0; /* check character */ +int32 tudb = 0; /* data buffer */ +int32 tumr = 0; /* maint register */ +int32 tutc = 0; /* tape control */ +int32 tuiff = 0; /* INTR flip/flop */ +int32 tu_time = 10; /* record latency */ +int32 tu_stopioe = 1; /* stop on error */ +int32 tu_log = 0; /* debug */ +int32 reg_in_fmtr[32] = { /* reg in formatter */ + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; +int32 reg_in_fmtr1[32] = { /* rmr if write + go */ + 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; +int32 fmt_test[16] = { /* fmt bytes/10 wd */ + 5, 0, 5, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; +static char *tu_fname[CS1_N_FNC] = { + "NOP", "UNLD", "2", "REW", "FCLR", "5", "6", "7", + "RIP", "11", "ERASE", "WREOF", "SPCF", "SPCR", "16", "17", + "20", "21", "22", "23", "WRCHKF", "25", "26", "WRCHKR", + "WRITE", "31", "32", "33", "READF", "35", "36" "READR" + }; +static uint8 *xbuf = NULL; /* xfer buffer */ + +t_stat tu_rd (int32 *data, int32 PA, int32 access); +t_stat tu_wr (int32 data, int32 PA, int32 access); +int32 tu_inta (void); +t_stat tu_svc (UNIT *uptr); +t_stat tu_reset (DEVICE *dptr); +t_stat tu_attach (UNIT *uptr, char *cptr); +t_stat tu_detach (UNIT *uptr); +t_stat tu_boot (int32 unitno, DEVICE *dptr); +void tu_go (int32 drv); +void set_tuer (int32 flag); +void update_tucs (int32 flag, int32 drv); +t_stat tu_map_err (UNIT *uptr, t_stat st, t_bool qdt); + +/* TU data structures + + tu_dev TU device descriptor + tu_unit TU unit list + tu_reg TU register list + tu_mod TU modifier list +*/ + +DIB tu_dib = { + IOBA_TU, IOLN_TU, &tu_rd, &tu_wr, + 1, IVCL (TU), VEC_TU, { &tu_inta } + }; + +UNIT tu_unit[] = { + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } + }; + +REG tu_reg[] = { + { ORDATA (MTCS1, tucs1, 16) }, + { ORDATA (MTWC, tuwc, 16) }, + { ORDATA (MTBA, tuba, 16) }, + { ORDATA (MTFC, tufc, 16) }, + { ORDATA (MTCS2, tucs2, 16) }, + { ORDATA (MTFS, tufs, 16) }, + { ORDATA (MTER, tuer, 16) }, + { ORDATA (MTCC, tucc, 16) }, + { ORDATA (MTDB, tudb, 16) }, + { ORDATA (MTMR, tumr, 16) }, + { ORDATA (MTTC, tutc, 16) }, + { FLDATA (IFF, tuiff, 0) }, + { FLDATA (INT, int_req, INT_V_TU) }, + { FLDATA (DONE, tucs1, CSR_V_DONE) }, + { FLDATA (IE, tucs1, CSR_V_IE) }, + { FLDATA (STOP_IOE, tu_stopioe, 0) }, + { DRDATA (TIME, tu_time, 24), PV_LEFT }, + { URDATA (UST, tu_unit[0].USTAT, 8, 17, 0, TU_NUMDR, 0) }, + { URDATA (POS, tu_unit[0].pos, 10, T_ADDR_W, 0, + TU_NUMDR, PV_LEFT | REG_RO) }, + { ORDATA (LOG, tu_log, 8), REG_HIDDEN }, + { NULL } + }; + +MTAB tu_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE tu_dev = { + "TU", tu_unit, tu_reg, tu_mod, + TU_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &tu_reset, + &tu_boot, &tu_attach, &tu_detach, + &tu_dib, DEV_UBUS | DEV_DEBUG + }; + +/* I/O dispatch routine, I/O addresses 17772440 - 17772472 */ + +t_stat tu_rd (int32 *data, int32 PA, int32 access) +{ +int32 fmtr, drv, j; + +fmtr = GET_FMTR (tucs2); /* get current fmtr */ +drv = GET_DRV (tutc); /* get current drive */ +j = (PA >> 1) & 017; /* get reg offset */ +if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ + tucs2 = tucs2 | CS2_NEF; /* set error flag */ + update_tucs (CS1_SC, drv); /* request intr */ + *data = 0; + return SCPE_OK; + } + +update_tucs (0, drv); /* update status */ +switch (j) { /* decode PA<4:1> */ + + case 000: /* MTCS1 */ + if (fmtr != 0) *data = tucs1 & ~CS1_DRV; + else *data = tucs1; + break; + + case 001: /* MTWC */ + *data = tuwc; + break; + + case 002: /* MTBA */ + *data = tuba = tuba & ~BA_MBZ; + break; + + case 003: /* MTFC */ + *data = tufc; + break; + + case 004: /* MTCS2 */ + *data = tucs2 = (tucs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; + break; + + case 005: /* MTFS */ + *data = tufs & 0177777; /* mask off rewind */ + break; + + case 006: /* MTER */ + *data = tuer; + break; + + case 007: /* MTAS */ + *data = (tufs & FS_ATA)? AS_U0: 0; + break; + + case 010: /* MTCC */ + *data = tucc = tucc & ~CC_MBZ; + break; + + case 011: /* MTDB */ + *data = tudb; + break; + + case 012: /* MTMR */ + *data = tumr; + break; + + case 013: /* MTDT */ + *data = DT_NSA | DT_TAPE | DT_TM03 | + ((tu_unit[drv].flags & UNIT_DIS)? DT_OFF: (DT_PRES | DT_TU45)); + break; + + case 014: /* MTSN */ + *data = (tu_unit[drv].flags & UNIT_DIS)? 0: 040 | (drv + 1); + break; + + case 015: /* MTTC */ + *data = tutc = tutc & ~TC_MBZ; + break; + + default: /* all others */ + set_tuer (ER_ILR); + update_tucs (0, drv); + break; + } + +return SCPE_OK; +} + +t_stat tu_wr (int32 data, int32 PA, int32 access) +{ +int32 cs1f, fmtr, drv, j; + +cs1f = 0; /* no int on cs1 upd */ +fmtr = GET_FMTR (tucs2); /* get formatter */ +drv = GET_DRV (tutc); /* get current unit */ +j = (PA >> 1) & 017; /* get reg offset */ +if (reg_in_fmtr[j] && (fmtr != 0)) { /* nx formatter */ + tucs2 = tucs2 | CS2_NEF; /* set error flag */ + update_tucs (CS1_SC, drv); /* request intr */ + return SCPE_OK; + } +if (reg_in_fmtr1[j] && ((tucs1 & CS1_DONE) == 0)) { /* formatter busy? */ + set_tuer (ER_RMR); /* won't write */ + update_tucs (0, drv); + return SCPE_OK; + } + +switch (j) { /* decode PA<4:1> */ + + case 000: /* MTCS1 */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + if (data & CS1_TRE) { /* error clear? */ + tucs1 = tucs1 & ~CS1_TRE; /* clr CS1 */ + tucs2 = tucs2 & ~CS2_ERR; /* clr CS2<15:8> */ + } + if ((access == WRITE) || (PA & 1)) { /* hi byte write? */ + if (tucs1 & CS1_DONE) /* done set? */ + tucs1 = (tucs1 & ~CS1_UAE) | (data & CS1_UAE); + } + if ((access == WRITE) || !(PA & 1)) { /* lo byte write? */ + if ((data & CS1_DONE) && (data & CS1_IE)) /* to DONE+IE? */ + tuiff = 1; /* set CSTB INTR */ + tucs1 = (tucs1 & ~CS1_IE) | (data & CS1_IE); + if (fmtr != 0) { /* nx formatter? */ + tucs2 = tucs2 | CS2_NEF; /* set error flag */ + cs1f = CS1_SC; /* req interrupt */ + } + else if (tucs1 & CS1_GO) { /* busy? */ + if (tucs1 & CS1_DONE) set_tuer (ER_RMR); + else tucs2 = tucs2 | CS2_PGE; + } + else { + tucs1 = (tucs1 & ~CS1_DRV) | (data & CS1_DRV); + if (tucs1 & CS1_GO) tu_go (drv); + } + } + break; + + case 001: /* MTWC */ + if (access == WRITEB) data = (PA & 1)? + (tuwc & 0377) | (data << 8): (tuwc & ~0377) | data; + tuwc = data; + break; + + case 002: /* MTBA */ + if (access == WRITEB) data = (PA & 1)? + (tuba & 0377) | (data << 8): (tuba & ~0377) | data; + tuba = data & ~BA_MBZ; + break; + + case 003: /* MTFC */ + if (access == WRITEB) data = (PA & 1)? + (tufc & 0377) | (data << 8): (tufc & ~0377) | data; + tufc = data; + tutc = tutc | TC_FCS; /* set fc flag */ + break; + + case 004: /* MTCS2 */ + if ((access == WRITEB) && (PA & 1)) data = data << 8; + if (data & CS2_CLR) tu_reset (&tu_dev); /* init? */ + else { + if ((data & ~tucs2) & (CS2_PE | CS2_MXF)) + cs1f = CS1_SC; /* diagn intr */ + if (access == WRITEB) data = (tucs2 & /* merge data */ + ((PA & 1)? 0377: 0177400)) | data; + tucs2 = (tucs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR | CS2_OR; + } + break; + + case 007: /* MTAS */ + if ((access == WRITEB) && (PA & 1)) break; + if (data & AS_U0) tufs = tufs & ~FS_ATA; + break; + + case 011: /* MTDB */ + if (access == WRITEB) data = (PA & 1)? + (tudb & 0377) | (data << 8): (tudb & ~0377) | data; + tudb = data; + break; + + case 012: /* MTMR */ + if (access == WRITEB) data = (PA & 1)? + (tumr & 0377) | (data << 8): (tumr & ~0377) | data; + tumr = (tumr & ~MR_RW) | (data & MR_RW); + break; + + case 015: /* MTTC */ + if (access == WRITEB) data = (PA & 1)? + (tutc & 0377) | (data << 8): (tutc & ~0377) | data; + tutc = (tutc & ~TC_RW) | (data & TC_RW) | TC_SAC; + drv = GET_DRV (tutc); + break; + + case 005: /* MTFS */ + case 006: /* MTER */ + case 010: /* MTCC */ + case 013: /* MTDT */ + case 014: /* MTSN */ + break; /* read only */ + + default: /* all others */ + set_tuer (ER_ILR); + break; + } /* end switch */ + +update_tucs (cs1f, drv); /* update status */ +return SCPE_OK; +} + +/* New magtape command */ + +void tu_go (int32 drv) +{ +int32 fnc, den; +UNIT *uptr; + +fnc = GET_FNC (tucs1); /* get function */ +den = GET_DEN (tutc); /* get density */ +uptr = tu_dev.units + drv; /* get unit */ +if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, + ">>TU%d STRT: fnc=%s, cs1=%06o, cs2=%06o, ba=%06o, wc=%06o, fc=%06o, fs=%06o, er=%06o, pos=%d\n", + drv, tu_fname[fnc], tucs1, tucs2, tuba, tuwc, tufc, tufs, tuer, uptr->pos); +if ((fnc != FNC_FCLR) && /* not clear & err */ + ((tufs & FS_ERR) || sim_is_active (uptr))) { /* or in motion? */ + set_tuer (ER_ILF); /* set err, ATN */ + tucs1 = tucs1 & ~CS1_GO; /* clear go */ + update_tucs (CS1_SC, drv); /* request intr */ + return; + } +tufs = tufs & ~FS_ATA; /* clear attention */ +tutc = tutc & ~TC_SAC; /* clear addr change */ + +switch (fnc) { /* case on function */ + case FNC_FCLR: /* drive clear */ + tuer = 0; /* clear errors */ + tutc = tutc & ~TC_FCS; /* clear fc status */ + tufs = tufs & ~(FS_SAT | FS_SSC | FS_ID | FS_ERR); + sim_cancel (uptr); /* reset drive */ + uptr->USTAT = 0; + case FNC_NOP: + tucs1 = tucs1 & ~CS1_GO; /* no operation */ + return; + + case FNC_RIP: /* read-in preset */ + tutc = TC_RIP; /* density = 800 */ + sim_tape_rewind (&tu_unit[0]); /* rewind unit 0 */ + tu_unit[0].USTAT = 0; + tucs1 = tucs1 & ~CS1_GO; + tufs = tufs & ~FS_TMK; + return; + + case FNC_UNLOAD: /* unload */ + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + set_tuer (ER_UNS); + break; + } + detach_unit (uptr); + uptr->USTAT = FS_REW; + sim_activate (uptr, tu_time); + tucs1 = tucs1 & ~CS1_GO; + tufs = tufs & ~FS_TMK; + return; + + case FNC_REWIND: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + set_tuer (ER_UNS); + break; + } + uptr->USTAT = FS_PIP | FS_REW; + sim_activate (uptr, tu_time); + tucs1 = tucs1 & ~CS1_GO; + tufs = tufs & ~FS_TMK; + return; + + case FNC_SPACEF: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + set_tuer (ER_UNS); + break; + } + if (sim_tape_eot (uptr) || ((tutc & TC_FCS) == 0)) { + set_tuer (ER_NXF); + break; + } + uptr->USTAT = FS_PIP; + goto GO_XFER; + + case FNC_SPACER: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + set_tuer (ER_UNS); + break; + } + if (sim_tape_bot (uptr) || ((tutc & TC_FCS) == 0)) { + set_tuer (ER_NXF); + break; + } + uptr->USTAT = FS_PIP; + goto GO_XFER; + + case FNC_WREOF: /* write tape mark */ + case FNC_ERASE: /* erase */ + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + set_tuer (ER_UNS); + break; + } + if (sim_tape_wrp (uptr)) { /* write locked? */ + set_tuer (ER_NXF); + break; + } + if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ + set_tuer (ER_FER); + break; + } + if (uptr->UDENS == UD_UNK) uptr->UDENS = den; /* set dens */ + uptr->USTAT = 0; + goto GO_XFER; + + case FNC_WCHKR: /* wchk = read */ + case FNC_READR: /* read rev */ + if (tufs & FS_BOT) { /* beginning of tape? */ + set_tuer (ER_NXF); + break; + } + goto DATA_XFER; + + case FNC_WRITE: /* write */ + if (((tutc & TC_FCS) == 0) || /* frame cnt = 0? */ + ((den == TC_800) && (tufc > 0777765))) { /* NRZI, fc < 13? */ + set_tuer (ER_NXF); + break; + } + case FNC_WCHKF: /* wchk = read */ + case FNC_READF: /* read */ + DATA_XFER: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + set_tuer (ER_UNS); + break; + } + if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ + set_tuer (ER_FER); + break; + } + if (uptr->UDENS == UD_UNK) uptr->UDENS = den; /* set dens */ + uptr->USTAT = 0; + tucs1 = tucs1 & ~CS1_DONE; /* clear done */ + GO_XFER: + tucs2 = tucs2 & ~CS2_ERR; /* clear errors */ + tucs1 = tucs1 & ~(CS1_TRE | CS1_MCPE); + tufs = tufs & ~(FS_TMK | FS_ID); /* clear eof, id */ + sim_activate (uptr, tu_time); + return; + + default: /* all others */ + set_tuer (ER_ILF); /* not supported */ + break; + } /* end case function */ + +tucs1 = tucs1 & ~CS1_GO; /* clear go */ +update_tucs (CS1_SC, drv); /* set intr */ +return; +} + +/* Unit service + + Complete movement or data transfer command + Unit must exist - can't remove an active unit + Unit must be attached - detach cancels in progress operations +*/ + +t_stat tu_svc (UNIT *uptr) +{ +int32 fnc, fmt, i, j, k, wc10, ba10; +int32 ba, fc, wc, drv, mpa10, vpn; +d10 val, v[4]; +t_mtrlnt tbc; +t_stat st, r = SCPE_OK; + +drv = (int32) (uptr - tu_dev.units); /* get drive # */ +if (uptr->USTAT & FS_REW) { /* rewind or unload? */ + sim_tape_rewind (uptr); /* rewind tape */ + uptr->USTAT = 0; /* clear status */ + tufs = tufs | FS_ATA | FS_SSC; + update_tucs (CS1_SC, drv); /* update status */ + return SCPE_OK; + } + +fnc = GET_FNC (tucs1); /* get command */ +fmt = GET_FMT (tutc); /* get format */ +ba = GET_UAE (tucs1) | tuba; /* get bus address */ +wc = 0200000 - tuwc; /* get word count */ +fc = 0200000 - tufc; /* get frame count */ +wc10 = wc >> 1; /* 10 word count */ +ba10 = ba >> 2; /* 10 word addr */ +uptr->USTAT = 0; /* clear status */ + +switch (fnc) { /* case on function */ + +/* Non-data transfer commands - set ATA when done */ + + case FNC_SPACEF: /* space forward */ + do { + tufc = (tufc + 1) & 0177777; /* incr fc */ + if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ + r = tu_map_err (uptr, st, 0); /* map error */ + break; + } + } while ((tufc != 0) && !sim_tape_eot (uptr)); + if (tufc) set_tuer (ER_FCE); + else tutc = tutc & ~TC_FCS; + tufs = tufs | FS_ATA; + break; + + case FNC_SPACER: /* space reverse */ + do { + tufc = (tufc + 1) & 0177777; /* incr wc */ + if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ + r = tu_map_err (uptr, st, 0); /* map error */ + break; + } + } while (tufc != 0); + if (tufc) set_tuer (ER_FCE); + else tutc = tutc & ~TC_FCS; + tufs = tufs | FS_ATA; + break; + + case FNC_WREOF: /* write end of file */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = tu_map_err (uptr, st, 0); /* map error */ + tufs = tufs | FS_ATA; + break; + + case FNC_ERASE: + if (sim_tape_wrp (uptr)) /* write protected? */ + r = tu_map_err (uptr, MTSE_WRP, 0); /* map error */ + tufs = tufs | FS_ATA; + break; + +/* Data transfer commands + + These commands must take into account the action of the "bit fiddler", which + converts between PDP-10 format and tape format. Only two tape formats are + supported: + + PDP-10 core dump: write 36b as byte 0/byte 1/byte 2/byte 3/0000'last nibble + industry mode: write hi 32b as byte 0/byte 1/byte 2/byte 3 + + These commands must also take into account the action of the Unibus adapter, + which munges PDP-10 addresses through the Unibus map. +*/ + + case FNC_READF: /* read */ + case FNC_WCHKF: /* wcheck = read */ + tufc = 0; /* clear frame count */ + if ((uptr->UDENS == TC_1600) && sim_tape_bot (uptr)) + tufs = tufs | FS_ID; /* PE BOT? ID burst */ + TXFR (ba, wc, 0); /* validate transfer */ + if (st = sim_tape_rdrecf (uptr, xbuf, &tbc, MT_MAXFR)) { /* read fwd */ + if (st == MTSE_TMK) set_tuer (ER_FCE); /* TMK also sets FCE */ + r = tu_map_err (uptr, st, 1); /* map error */ + break; /* done */ + } + for (i = j = 0; (i < wc10) && (j < ((int32) tbc)); i++) { + if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ + MAPM (ba10 + i, mpa10, 0); + } + for (k = 0; k < 4; k++) v[k] = xbuf[j++]; + val = (v[0] << 28) | (v[1] << 20) | (v[2] << 12) | (v[3] << 4); + if (fmt == TC_10C) val = val | ((d10) xbuf[j++] & 017); + if (fnc == FNC_READF) M[mpa10] = val; /* read? store */ + else if (M[mpa10] != val) { /* wchk, mismatch? */ + tucs2 = tucs2 | CS2_WCE; /* flag, stop */ + break; + } + mpa10 = mpa10 + 1; + } /* end for */ + tufc = tbc & 0177777; + tuwc = (tuwc + (i << 1)) & 0177777; + ba = ba + (i << 2); + if (tuwc) set_tuer (ER_FCE); /* short record? */ + break; + + case FNC_WRITE: /* write */ + TXFR (ba, wc, 0); /* validate transfer */ + for (i = j = 0; (i < wc10) && (j < fc); i++) { + if ((i == 0) || NEWPAGE (ba10 + i, 0)) { /* map new page */ + MAPM (ba10 + i, mpa10, 0); + } + val = M[mpa10]; + xbuf[j++] = (uint8) ((val >> 28) & 0377); + xbuf[j++] = (uint8) ((val >> 20) & 0377); + xbuf[j++] = (uint8) ((val >> 12) & 0377); + xbuf[j++] = (uint8) ((val >> 4) & 0377); + if (fmt == TC_10C) xbuf[j++] = (uint8) (val & 017); + mpa10 = mpa10 + 1; + } /* end for */ + if (j < fc) fc = j; /* short record? */ + if (st = sim_tape_wrrecf (uptr, xbuf, fc)) /* write rec, err? */ + r = tu_map_err (uptr, st, 1); /* map error */ + else { + tufc = (tufc + fc) & 0177777; + if (tufc == 0) tutc = tutc & ~TC_FCS; + tuwc = (tuwc + (i << 1)) & 0177777; + ba = ba + (i << 2); + } + break; + + case FNC_READR: /* read reverse */ + case FNC_WCHKR: /* wcheck = read */ + tufc = 0; /* clear frame count */ + TXFR (ba, wc, 1); /* validate xfer rev */ + if (st = sim_tape_rdrecr (uptr, xbuf + 4, &tbc, MT_MAXFR)) { /* read rev */ + if (st == MTSE_TMK) set_tuer (ER_FCE); /* TMK also sets FCE */ + r = tu_map_err (uptr, st, 1); /* map error */ + break; /* done */ + } + for (i = 0; i < 4; i++) xbuf[i] = 0; + for (i = 0, j = tbc + 4; (i < wc10) && (j >= 4); i++) { + if ((i == 0) || NEWPAGE (ba10 - i, PAG_M_OFF)) { /* map page */ + MAPM (ba10 - i, mpa10, UMAP_RRV); + } + val = ((fmt == TC_10C)? (((d10) xbuf [--j]) & 017): 0); + for (k = 0; k < 4; i++) v[k] = xbuf[--j]; + val = val | (v[0] << 4) | (v[1] << 12) | (v[2] << 20) | (v[3] << 28); + if (fnc == FNC_READR) M[mpa10] = val; /* read? store */ + else if (M[mpa10] != val) { /* wchk, mismatch? */ + tucs2 = tucs2 | CS2_WCE; /* flag, stop */ + break; + } + mpa10 = mpa10 - 1; + } /* end for */ + tufc = tbc & 0177777; + tuwc = (tuwc + (i << 1)) & 0177777; + ba = ba - (i << 2); + if (tuwc) set_tuer (ER_FCE); /* short record? */ + break; + } /* end case */ + +tucs1 = (tucs1 & ~CS1_UAE) | ((ba >> (16 - CS1_V_UAE)) & CS1_UAE); +tuba = ba & 0177777; /* update mem addr */ +tucs1 = tucs1 & ~CS1_GO; /* clear go */ +if (fnc >= FNC_XFER) update_tucs (CS1_DONE, drv); /* data xfer? */ +else update_tucs (CS1_SC, drv); /* no, set attn */ +if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, + ">>TU%d DONE: fnc=%s, cs1=%06o, cs2=%06o, ba=%06o, wc=%06o, fc=%06o, fs=%06o, er=%06o, pos=%d\n", + drv, tu_fname[fnc], tucs1, tucs2, tuba, tuwc, tufc, tufs, tuer, uptr->pos); +return SCPE_OK; +} + +/* Formatter error */ + +void set_tuer (int32 flag) +{ +tuer = tuer | flag; +tufs = tufs | FS_ATA; +tucs1 = tucs1 | CS1_SC; +return; +} + +/* Controller status update + + Check for done transition + Update drive status + Update MTCS1 + Update interrupt request +*/ + +void update_tucs (int32 flag, int32 drv) +{ +int32 act = sim_is_active (&tu_unit[drv]); + +if ((flag & ~tucs1) & CS1_DONE) /* DONE 0 to 1? */ + tuiff = (tucs1 & CS1_IE)? 1: 0; /* CSTB INTR <- IE */ +if (GET_FMTR (tucs2) == 0) { /* formatter present? */ + tufs = (tufs & ~FS_DYN) | FS_FPR; + if (tu_unit[drv].flags & UNIT_ATT) { + tufs = tufs | FS_MOL | tu_unit[drv].USTAT; + if (tu_unit[drv].UDENS == TC_1600) tufs = tufs | FS_PE; + if (sim_tape_wrp (&tu_unit[drv])) tufs = tufs | FS_WRL; + if (!act) { + if (sim_tape_bot (&tu_unit[drv])) tufs = tufs | FS_BOT; + if (sim_tape_eot (&tu_unit[drv])) tufs = tufs | FS_EOT; + } + } + if (tuer) tufs = tufs | FS_ERR; + } +else tufs = 0; +tucs1 = (tucs1 & ~(CS1_SC | CS1_MCPE | CS1_MBZ)) | CS1_DVA | flag; +if (tucs2 & CS2_ERR) tucs1 = tucs1 | CS1_TRE | CS1_SC; +else if (tucs1 & CS1_TRE) tucs1 = tucs1 | CS1_SC; +if (tufs & FS_ATA) tucs1 = tucs1 | CS1_SC; +if (tuiff || ((tucs1 & CS1_SC) && (tucs1 & CS1_DONE) && (tucs1 & CS1_IE))) + int_req = int_req | INT_TU; +else int_req = int_req & ~INT_TU; +if ((tucs1 & CS1_DONE) && tufs && !act) tufs = tufs | FS_RDY; +return; +} + +/* Interrupt acknowledge */ + +int32 tu_inta (void) +{ +tucs1 = tucs1 & ~CS1_IE; /* clear int enable */ +tuiff = 0; /* clear CSTB INTR */ +return VEC_TU; /* acknowledge */ +} + +/* Map tape error status */ + +t_stat tu_map_err (UNIT *uptr, t_stat st, t_bool qdt) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* not attached */ + set_tuer (ER_NXF); /* can't execute */ + if (qdt) tucs1 = tucs1 | CS1_TRE; /* data xfr? set TRE */ + case MTSE_OK: /* no error */ + return SCPE_IERR; + + case MTSE_TMK: /* end of file */ + tufs = tufs | FS_TMK; + break; + + case MTSE_IOERR: /* IO error */ + set_tuer (ER_VPE); /* flag error */ + if (qdt) tucs1 = tucs1 | CS1_TRE; /* data xfr? set TRE */ + if (tu_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + set_tuer (ER_VPE); /* flag error */ + if (qdt) tucs1 = tucs1 | CS1_TRE; /* data xfr? set TRE */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + set_tuer (ER_CRC); /* set crc err */ + if (qdt) tucs1 = tucs1 | CS1_TRE; /* data xfr? set TRE */ + break; + + case MTSE_EOM: /* end of medium */ + set_tuer (ER_OPI); /* incomplete */ + if (qdt) tucs1 = tucs1 | CS1_TRE; /* data xfr? set TRE */ + break; + + case MTSE_BOT: /* reverse into BOT */ + break; + + case MTSE_WRP: /* write protect */ + set_tuer (ER_NXF); /* can't execute */ + if (qdt) tucs1 = tucs1 | CS1_TRE; /* data xfr? set TRE */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tu_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +tucs1 = CS1_DVA | CS1_DONE; +tucs2 = CS2_IR | CS2_OR; +tuba = 0; +tuwc = 0; +tufc = 0; +tuer = 0; +tufs = FS_FPR | FS_RDY; +if (sim_switches & SWMASK ('P')) tutc = 0; /* powerup? clr TC */ +else tutc = tutc & ~TC_FCS; /* no, clr */ +tuiff = 0; /* clear CSTB INTR */ +int_req = int_req & ~INT_TU; /* clear interrupt */ +for (u = 0; u < TU_NUMDR; u++) { /* loop thru units */ + uptr = tu_dev.units + u; + sim_tape_reset (uptr); /* clear pos flag */ + sim_cancel (uptr); /* cancel activity */ + uptr->USTAT = 0; + } +if (xbuf == NULL) xbuf = (uint8 *) calloc (MT_MAXFR + 4, sizeof (uint8)); +if (xbuf == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat tu_attach (UNIT *uptr, char *cptr) +{ +int32 drv = uptr - tu_dev.units; +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->USTAT = 0; /* clear unit status */ +uptr->UDENS = UD_UNK; /* unknown density */ +tufs = tufs | FS_ATA | FS_SSC; /* set attention */ +if ((GET_FMTR (tucs2) == 0) && (GET_DRV (tutc) == drv)) /* selected drive? */ + tufs = tufs | FS_SAT; /* set slave attn */ +update_tucs (CS1_SC, drv); /* update status */ +return r; +} + +/* Detach routine */ + +t_stat tu_detach (UNIT* uptr) +{ +int32 drv = uptr - tu_dev.units; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if (sim_is_active (uptr)) { /* unit active? */ + sim_cancel (uptr); /* cancel operation */ + tuer = tuer | ER_UNS; /* set formatter error */ + if ((uptr->USTAT & FS_REW) == 0) /* data transfer? */ + tucs1 = tucs1 | CS1_DONE | CS1_TRE; /* set done, err */ + } +uptr->USTAT = 0; /* clear status flags */ +tufs = tufs | FS_ATA | FS_SSC; /* set attention */ +update_tucs (CS1_SC, drv); /* update status */ +return sim_tape_detach (uptr); +} + +/* Device bootstrap */ + +#define BOOT_START 0377000 /* start */ +#define BOOT_LEN (sizeof (boot_rom_dec) / sizeof (d10)) + +static const d10 boot_rom_dec[] = { + 0515040000003, /* boot:hrlzi 1,3 ; uba # */ + 0201000040001, /* movei 0,40001 ; vld,pg 1 */ + 0713001000000+(IOBA_UBMAP+1 & RMASK), /* wrio 0,763001(1); set ubmap */ + 0435040000000+(IOBA_TU & RMASK), /* iori 1,772440 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0713001000010, /* wrio 0,10(1) ; ->MTFS */ + 0201100000031, /* movei 2,31 ; space f */ + 0265740377014, /* jsp 17,tpop ; skip ucode */ + 0201100000071, /* movei 2,71 ; read f */ + 0265740377014, /* jsp 17,tpop ; read boot */ + 0254000001000, /* jrst 1000 ; start */ + 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ + 0713001000032, /* wrio 0,32(1) ; ->MTTC */ + 0201000000011, /* movei 0,11 ; clr+go */ + 0713001000000, /* wrio 0,0(1) ; ->MTCS1 */ + 0201140176000, /* movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ + 0201300000000, /* movei 6,0 ; fmtr */ + 0713141000002, /* wrio 3,2(1) ; ->MTWC */ + 0713201000004, /* wrio 4,4(1) ; ->MTBA */ + 0713301000006, /* wrio 6,6(1) ; ->MTFC */ + 0713301000010, /* wrio 6,10(1) ; ->MTFS */ + 0713241000032, /* wrio 5,32(1) ; ->MTTC */ + 0713101000000, /* wrio 2,0(1) ; ->MTCS1 */ + 0712341000012, /* rdio 7,12(1) ; read FS */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377032, /* jrst .-2 ; loop */ + 0606340040000, /* trnn 7,40000 ; test err */ + 0254017000000, /* jrst 0(17) ; return */ + 0712341000014, /* rdio 7,14(1) ; read err */ + 0302340001000, /* caie 7,1000 ; fce? */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ + }; + +static const d10 boot_rom_its[] = { + 0515040000003, /* boot:hrlzi 1,3 ; uba # - not used */ + 0201000040001, /* movei 0,40001 ; vld,pg 1 */ + 0714000000000+(IOBA_UBMAP+1 & RMASK), /* iowri 0,763001 ; set ubmap */ + 0435040000000+(IOBA_TU & RMASK), /* iori 1,772440 ; rh addr */ + 0202040000000+FE_RHBASE, /* movem 1,FE_RHBASE */ + 0201000000040, /* movei 0,40 ; ctrl reset */ + 0714001000010, /* iowri 0,10(1) ; ->MTFS */ + 0201100000031, /* movei 2,31 ; space f */ + 0265740377014, /* jsp 17,tpop ; skip ucode */ + 0201100000071, /* movei 2,71 ; read f */ + 0265740377014, /* jsp 17,tpop ; read boot */ + 0254000001000, /* jrst 1000 ; start */ + 0200000000000+FE_MTFMT, /* tpop:move 0,FE_MTFMT ; den,fmt,slv */ + 0714001000032, /* iowri 0,32(1) ; ->MTTC */ + 0201000000011, /* movei 0,11 ; clr+go */ + 0714001000000, /* iowri 0,0(1) ; ->MTCS1 */ + 0201140176000, /* movei 3,176000 ; wd cnt */ + 0201200004000, /* movei 4,4000 ; addr */ + 0200240000000+FE_MTFMT, /* move 5,FE_MTFMT ; unit */ + 0201300000000, /* movei 6,0 ; fmtr */ + 0714141000002, /* iowri 3,2(1) ; ->MTWC */ + 0714201000004, /* iowri 4,4(1) ; ->MTBA */ + 0714301000006, /* iowri 6,6(1) ; ->MTFC */ + 0714301000010, /* iowri 6,10(1) ; ->MTFS */ + 0714241000032, /* iowri 5,32(1) ; ->MTTC */ + 0714101000000, /* iowri 2,0(1) ; ->MTCS1 */ + 0710341000012, /* iordi 7,12(1) ; read FS */ + 0606340000200, /* trnn 7,200 ; test rdy */ + 0254000377032, /* jrst .-2 ; loop */ + 0606340040000, /* trnn 7,40000 ; test err */ + 0254017000000, /* jrst 0(17) ; return */ + 0710341000014, /* iordi 7,14(1) ; read err */ + 0302340001000, /* caie 7,1000 ; fce? */ + 0254200377052, /* halt */ + 0254017000000, /* jrst 0(17) ; return */ + }; + +t_stat tu_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern a10 saved_PC; + +M[FE_UNIT] = 0; +M[FE_MTFMT] = (unitno & TC_M_UNIT) | (TC_1600 << TC_V_DEN) | (TC_10C << TC_V_FMT); +tu_unit[unitno].pos = 0; +for (i = 0; i < BOOT_LEN; i++) + M[BOOT_START + i] = Q_ITS? boot_rom_its[i]: boot_rom_dec[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/PDP10/pdp10_xtnd.c b/PDP10/pdp10_xtnd.c new file mode 100644 index 0000000..af0e155 --- /dev/null +++ b/PDP10/pdp10_xtnd.c @@ -0,0 +1,715 @@ +/* pdp10_xtnd.c: PDP-10 extended instruction simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 12-May-01 RMS Fixed compiler warning in xlate + + Instructions handled in this module: + + MOVSLJ move string left justified + MOVSO move string offset + MOVST move string translated + MOVSRJ move string right justified + CMPSL compare string, skip on less + CMPSE compare string, skip on equal + CMPSLE compare string, skip on less or equal + CMPSGE compare string, skip on greater or equal + CMPSN compare string, skip on unequal + CMPSG compare string, skip on greater + CVTDBO convert decimal to binary offset + CVTDBT convert decimal to binary translated + CVTBDO convert binary to decimal offset + CVTBDT convert binary to decimal translated + EDIT edit + + The PDP-10 extended instructions deal with non-binary data types, + particularly byte strings and decimal strings. (In the KL10, the + extended instructions include G floating support as well.) They + are very complicated microcoded subroutines that can potentially + run for a very long time. Accordingly, the instructions must test + for interrupts as well as page faults, and be prepared to restart + from either. + + In general, the simulator attempts to keep the AC block up to date, + so that page fails and interrupts can be taken directly at any point. + If the AC block is not up to date, memory accessibility must be tested + before the actual read or write is done. + + The extended instruction routine returns a status code as follows: + + XT_NOSK no skip completion + XT_SKIP skip completion + XT_MUUO invalid extended instruction +*/ + +#include "pdp10_defs.h" +#include + +#define MM_XSRC (pflgs & XSRC_PXCT) +#define MM_XDST (pflgs & XDST_PXCT) +#define MM_EA_XSRC ((pflgs & EA_PXCT) && MM_XSRC) +#define MM_EA_XDST ((pflgs & EA_PXCT) && MM_XDST) + +#define XT_CMPSL 001 /* opcodes */ +#define XT_CMPSE 002 +#define XT_CMPSLE 003 +#define XT_EDIT 004 +#define XT_CMPSGE 005 +#define XT_CMPSN 006 +#define XT_CMPSG 007 +#define XT_CVTDBO 010 +#define XT_CVTDBT 011 +#define XT_CVTBDO 012 +#define XT_CVTBDT 013 +#define XT_MOVSO 014 +#define XT_MOVST 015 +#define XT_MOVSLJ 016 +#define XT_MOVSRJ 017 + +/* Translation control */ + +#define XT_LFLG 0400000000000 /* L flag */ +#define XT_SFLG 0400000000000 /* S flag */ +#define XT_NFLG 0200000000000 /* N flag */ +#define XT_MFLG 0100000000000 /* M flag */ + +/* Translation table */ + +#define XT_V_CODE 15 /* translation op */ +#define XT_M_CODE 07 +#define XT_BYMASK 07777 /* byte mask */ +#define XT_DGMASK 017 /* digit mask */ +#define XT_GETCODE(x) ((int32) (((x) >> XT_V_CODE) & XT_M_CODE)) + +/* AC masks */ + +#define XLNTMASK 0000777777777 /* length */ +#define XFLGMASK 0700000000000 /* flags */ +#define XT_MBZ 0777000000000 /* must be zero */ +#define XT_MBZE 0047777000000 /* must be zero, edit */ + +/* Register change log */ + +#define XT_N_RLOG 5 /* entry width */ +#define XT_M_RLOG ((1 << XT_N_RLOG) - 1) /* entry mask */ +#define XT_O_RLOG 1 /* entry offset */ +#define XT_INSRLOG(x,v) v = ((v << XT_N_RLOG) | (((x) + XT_O_RLOG) & XT_M_RLOG)) +#define XT_REMRLOG(x,v) x = (v & XT_M_RLOG) - XT_O_RLOG; \ + v = v >> XT_N_RLOG + +/* Edit */ + +#define ED_V_PBYN 30 /* pattern byte # */ +#define ED_M_PBYN 03 +#define ED_PBYNO 0040000000000 /* overflow bit */ +#define ED_GETPBYN(x) ((int32) (((x) >> ED_V_PBYN) & ED_M_PBYN)) +#define ED_V_POPC 6 /* pattern byte opcode */ +#define ED_M_PAT 0777 /* pattern byte mask */ +#define ED_M_NUM 0077 /* number for msg, etc */ +#define ED_PBYTE(x,y) ((int32) (((x) >> (27 - (ED_GETPBYN (y) * 9))) & ED_M_PAT)) +#define ED_STOP 0000 /* stop */ +#define ED_SELECT 0001 /* select source */ +#define ED_SIGST 0002 /* start significance */ +#define ED_FLDSEP 0003 /* field separator */ +#define ED_EXCHMD 0004 /* exchange mark, dst */ +#define ED_MESSAG 0100 /* message */ +#define ED_SKPM 0500 /* skip if M */ +#define ED_SKPN 0600 /* skip if N */ +#define ED_SKPA 0700 /* skip always */ + +extern d10 *ac_cur; /* current AC block */ +extern const d10 bytemask[64]; +extern int32 flags; +extern int32 rlog; +extern jmp_buf save_env; + +extern d10 Read (int32 ea, int32 prv); +extern void Write (int32 ea, d10 val, int32 prv); +extern a10 calc_ea (d10 inst, int32 prv); +extern int32 test_int (void); +d10 incbp (d10 bp); +d10 incloadbp (int32 ac, int32 pflgs); +void incstorebp (d10 val, int32 ac, int32 pflgs); +d10 xlate (d10 by, a10 tblad, d10 *xflgs, int32 pflgs); +void filldst (d10 fill, int32 ac, d10 cnt, int32 pflgs); + +static const d10 pwrs10[23][2] = { + 0, 0, + 0, 1, + 0, 10, + 0, 100, + 0, 1000, + 0, 10000, + 0, 100000, + 0, 1000000, + 0, 10000000, + 0, 100000000, + 0, 1000000000, + 0, 10000000000, + 2, 31280523264, + 29, 3567587328, + 291, 1316134912, + 2910, 13161349120, + 29103, 28534276096, + 291038, 10464854016, + 2910383, 1569325056, + 29103830, 15693250560, + 291038304, 19493552128, + 2910383045, 23136829440, + 29103830456, 25209864192 + }; + +int xtend (int32 ac, int32 ea, int32 pflgs) +{ +d10 b1, b2, ppi; +d10 xinst, xoff, digit, f1, f2, rs[2]; +d10 xflgs = 0; +a10 e1, entad; +int32 p1 = ADDAC (ac, 1); +int32 p3 = ADDAC (ac, 3); +int32 p4 = ADDAC (ac, 4); +int32 flg, i, s2, t, pp, pat, xop, xac, ret; + +xinst = Read (ea, MM_OPND); /* get extended instr */ +xop = GET_OP (xinst); /* get opcode */ +xac = GET_AC (xinst); /* get AC */ +if (xac || (xop == 0) || (xop > XT_MOVSRJ)) return XT_MUUO; +rlog = 0; /* clear log */ +switch (xop) { /* case on opcode */ + +/* String compares - checked against KS10 ucode + If both strings are zero length, they are considered equal. + Both source and destination lengths are MBZ checked. + + AC = source1 length + AC + 1 = source1 byte pointer + AC + 3 = source2 length + AC + 4 = source2 byte pointer +*/ + + case XT_CMPSL: /* CMPSL */ + case XT_CMPSE: /* CMPSE */ + case XT_CMPSLE: /* CMPSLE */ + case XT_CMPSGE: /* CMPSGE */ + case XT_CMPSN: /* CMPSN */ + case XT_CMPSG: /* CMPSG */ + if ((AC(ac) | AC(p3)) & XT_MBZ) return XT_MUUO; /* check length MBZ */ + f1 = Read (ADDA (ea, 1), MM_OPND) & bytemask[GET_S (AC(p1))]; + f2 = Read (ADDA (ea, 2), MM_OPND) & bytemask[GET_S (AC(p4))]; + b1 = b2 = 0; + for (flg = 0; (AC(ac) | AC(p3)) && (b1 == b2); flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + if (AC(ac)) b1 = incloadbp (p1, pflgs); /* src1 */ + else b1 = f1; + if (AC(p3)) b2 = incloadbp (p4, pflgs); /* src2 */ + else b2 = f2; + if (AC(ac)) AC(ac) = (AC(ac) - 1) & XLNTMASK; + if (AC(p3)) AC(p3) = (AC(p3) - 1) & XLNTMASK; + } + switch (xop) { + case XT_CMPSL: + return (b1 < b2)? XT_SKIP: XT_NOSK; + case XT_CMPSE: + return (b1 == b2)? XT_SKIP: XT_NOSK; + case XT_CMPSLE: + return (b1 <= b2)? XT_SKIP: XT_NOSK; + case XT_CMPSGE: + return (b1 >= b2)? XT_SKIP: XT_NOSK; + case XT_CMPSN: + return (b1 != b2)? XT_SKIP: XT_NOSK; + case XT_CMPSG: + return (b1 > b2)? XT_SKIP: XT_NOSK; + } + + return XT_MUUO; + +/* Convert binary to decimal instructions - checked against KS10 ucode + There are no MBZ tests. + + AC'AC + 1 = double precision integer source + AC + 3 = flags and destination length + AC + 4 = destination byte pointer +*/ + + case XT_CVTBDO: /* CVTBDO */ + case XT_CVTBDT: /* CVTBDT */ + e1 = calc_ea (xinst, MM_EA); /* get ext inst addr */ + if (xop == XT_CVTBDO) /* offset? */ + xoff = (e1 & RSIGN)? (e1 | LMASK): e1; /* get offset */ + rs[0] = AC(ac); /* get src opnd */ + rs[1] = CLRS (AC(p1)); + if (!TSTF (F_FPD)) { /* set up done yet? */ + if (TSTS (AC(ac))) { DMOVN (rs); } /* get abs value */ + for (i = 22; i > 1; i--) { /* find field width */ + if (DCMPGE (rs, pwrs10[i])) break; + } + if (i > (AC(p3) & XLNTMASK)) return XT_NOSK; + if ((i < (AC(p3) & XLNTMASK)) && (AC(p3) & XT_LFLG)) { + f1 = Read (ADDA (ea, 1), MM_OPND); + filldst (f1, p3, (AC(p3) & XLNTMASK) - i, pflgs); + } + else AC(p3) = (AC(p3) & XFLGMASK) | i; + if (TSTS (AC(ac))) AC(p3) = AC(p3) | XT_MFLG; + if (AC(ac) | AC(p1)) AC(p3) = AC(p3) | XT_NFLG; + AC(ac) = rs[0]; /* update state */ + AC(p1) = rs[1]; + SETF (F_FPD); /* mark set up done */ + } + +/* Now do actual binary to decimal conversion */ + + for (flg = 0; AC(p3) & XLNTMASK; flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + i = (int32) AC(p3) & XLNTMASK; /* get length */ + if (i > 22) i = 22; /* put in range */ + for (digit = 0; (digit < 10) && DCMPGE (rs, pwrs10[i]); digit++) { + rs[0] = rs[0] - pwrs10[i][0] - (rs[1] < pwrs10[i][1]); + rs[1] = (rs[1] - pwrs10[i][1]) & MMASK; + } + if (xop == XT_CVTBDO) digit = (digit + xoff) & DMASK; + else { + f1 = Read (e1 + (int32) digit, MM_OPND); + if ((i == 1) && (AC(p3) & XT_LFLG)) f1 = f1 >> 18; + digit = f1 & RMASK; + } + incstorebp (digit, p4, pflgs); /* store digit */ + AC(ac) = rs[0]; /* mem access ok */ + AC(p1) = rs[1]; /* update state */ + AC(p3) = (AC(p3) & XFLGMASK) | ((AC(p3) - 1) & XLNTMASK); + } + CLRF (F_FPD); /* clear FPD */ + return XT_SKIP; + +/* Convert decimal to binary instructions - checked against KS10 ucode + There are no MBZ tests. + + AC = flags and source length + AC + 1 = source byte pointer + AC + 3'AC + 4 = double precision integer result +*/ + + case XT_CVTDBT: /* CVTDBT */ + case XT_CVTDBO: /* CVTDBO */ + e1 = calc_ea (xinst, MM_EA); /* get ext inst addr */ + if ((AC(ac) & XT_SFLG) == 0) AC(p3) = AC(p4) = 0; /* !S? clr res */ + else AC(p4) = CLRS (AC(p4)); /* clear low sign */ + if (xop == XT_CVTDBO) { /* offset? */ + xoff = (e1 & RSIGN)? (e1 | LMASK): e1; /* get offset */ + AC(ac) = AC(ac) | XT_SFLG; /* set S flag */ + } + xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ + for (flg = 0; AC(ac) & XLNTMASK; flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + b1 = incloadbp (p1, pflgs); /* get byte */ + if (xop == XT_CVTDBO) b1 = (b1 + xoff) & DMASK; + else { + b1 = xlate (b1, e1, &xflgs, MM_OPND); + if (b1 < 0) { /* terminated? */ + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); + return XT_NOSK; + } + if (xflgs & XT_SFLG) b1 = b1 & XT_DGMASK; + else b1 = 0; + } + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + if ((b1 < 0) || (b1 > 9)) { /* bad digit? done */ + if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); + return XT_NOSK; + } + AC(p4) = (AC(p4) * 10) + b1; /* base * 10 + digit */ + AC(p3) = ((AC(p3) * 10) + (AC(p4) >> 35)) & DMASK; + AC(p4) = AC(p4) & MMASK; + } + if (AC(ac) & XT_MFLG) { + AC(p4) = -AC(p4) & MMASK; + AC(p3) = (~AC(p3) + (AC(p4) == 0)) & DMASK; + } + if (TSTS (AC(p3))) AC(p4) = SETS (AC(p4)); + return XT_SKIP; + +/* String move instructions - checked against KS10 ucode + Only the destination length is MBZ checked. + + AC = flags (MOVST only) and source length + AC + 1 = source byte pointer + AC + 3 = destination length + AC + 4 = destination byte pointer +*/ + + case XT_MOVSO: /* MOVSO */ + case XT_MOVST: /* MOVST */ + case XT_MOVSRJ: /* MOVSRJ */ + case XT_MOVSLJ: /* MOVSLJ */ + if (AC(p3) & XT_MBZ) return XT_MUUO; /* test dst lnt MBZ */ + f1 = Read (ADDA (ea, 1), MM_OPND); /* get fill */ + switch (xop) { /* case on instr */ + + case XT_MOVSO: /* MOVSO */ + AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ + xoff = calc_ea (xinst, MM_EA); /* get offset */ + if (xoff & RSIGN) xoff = xoff | LMASK; /* sign extend 18b */ + s2 = GET_S (AC(p4)); /* get dst byte size */ + break; + + case XT_MOVST: /* MOVST */ + e1 = calc_ea (xinst, MM_EA); /* get xlate tbl addr */ + break; + + case XT_MOVSRJ: /* MOVSRJ */ + AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ + if (AC(p3) == 0) return (AC(ac)? XT_NOSK: XT_SKIP); + if (AC(ac) > AC(p3)) { /* adv src ptr */ + for (flg = 0; AC(ac) > AC(p3); flg++) { + if (flg && (t = test_int ())) ABORT (t); + AC(p1) = incbp (AC(p1)); + AC(ac) = (AC(ac) - 1) & XLNTMASK; + } + } + else if (AC(ac) < AC(p3)) + filldst (f1, p3, AC(p3) - AC(ac), pflgs); + break; + + case XT_MOVSLJ: /* MOVSLJ */ + AC(ac) = AC(ac) & XLNTMASK; /* trim src length */ + break; + } /* end case xop */ + + xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ + if (AC(p3) == 0) return (AC(ac)? XT_NOSK: XT_SKIP); + for (flg = 0; AC(p3) & XLNTMASK; flg++) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + if (AC(ac) & XLNTMASK) { /* any source? */ + b1 = incloadbp (p1, pflgs); /* src byte */ + if (xop == XT_MOVSO) { /* offset? */ + b1 = (b1 + xoff) & DMASK; /* test fit */ + if (b1 & ~bytemask[s2]) { + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + return XT_NOSK; + } + } + else if (xop == XT_MOVST) { /* translate? */ + b1 = xlate (b1, e1, &xflgs, MM_OPND); + if (b1 < 0) { /* upd flags in AC */ + AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + return XT_NOSK; + } + if (xflgs & XT_SFLG) b1 = b1 & XT_BYMASK; + else b1 = -1; + } + } + else b1 = f1; + if (b1 >= 0) { /* valid byte? */ + incstorebp (b1, p4, pflgs); /* store byte */ + AC(p3) = (AC(p3) - 1) & XLNTMASK; /* update state */ + } + if (AC(ac) & XLNTMASK) AC(ac) = xflgs | ((AC(ac) - 1) & XLNTMASK); + } + return (AC(ac) & XLNTMASK)? XT_NOSK: XT_SKIP; + +/* Edit - checked against KS10 ucode + Only the flags/pattern pointer word is MBZ checked. + + AC = flags, pattern pointer + AC + 1 = source byte pointer + AC + 3 = mark address + AC + 4 = destination byte pointer +*/ + + case XT_EDIT: /* EDIT */ + if (AC(ac) & XT_MBZE) return XT_MUUO; /* check pattern MBZ */ + xflgs = AC(ac) & XFLGMASK; /* get xlation flags */ + e1 = calc_ea (xinst, MM_EA); /* get xlate tbl addr */ + for (ppi = 1, ret = -1, flg = 0; ret < 0; flg++, ppi = 1) { + if (flg && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + pp = (int32) AC(ac) & AMASK; /* get pattern ptr */ + b1 = Read (pp, MM_OPND); /* get pattern word */ + pat = ED_PBYTE (b1, AC(ac)); /* get pattern byte */ + switch ((pat < 0100)? pat: ((pat >> ED_V_POPC) + 0100)) { + + case ED_STOP: /* stop */ + ret = XT_SKIP; /* exit loop */ + break; + + case ED_SELECT: /* select source */ + b1 = incloadbp (p1, pflgs); /* get src */ + entad = (e1 + ((int32) b1 >> 1)) & AMASK; + f1 = ((Read (entad, MM_OPND) >> ((b1 & 1)? 0: 18)) & RMASK); + i = XT_GETCODE (f1); + if (i & 2) xflgs = + (i & 1)? xflgs | XT_MFLG: xflgs & ~XT_MFLG; + switch (i) { + + case 00: case 02: case 03: + if (xflgs & XT_SFLG) f1 = f1 & XT_BYMASK; + else { + f1 = Read (INCA (ea), MM_OPND); + if (f1 == 0) break; + } + incstorebp (f1, p4, pflgs); + break; + + case 01: + ret = XT_NOSK; /* exit loop */ + break; + + case 04: case 06: case 07: + xflgs = xflgs | XT_NFLG; + f1 = f1 & XT_BYMASK; + if ((xflgs & XT_SFLG) == 0) { + f2 = Read (ADDA (ea, 2), MM_OPND); + Write ((a10) AC(p3), AC(p4), MM_OPND); + if (f2) incstorebp (f2, p4, pflgs); + xflgs = xflgs | XT_SFLG; + } + incstorebp (f1, p4, pflgs); + break; + + case 05: + xflgs = xflgs | XT_NFLG; + ret = XT_NOSK; /* exit loop */ + break; + } /* end case xlate op */ + break; + + case ED_SIGST: /* start significance */ + if ((xflgs & XT_SFLG) == 0) { + f2 = Read (ADDA (ea, 2), MM_OPND); + Write ((a10) AC(p3), AC(p4), MM_OPND); + if (f2) incstorebp (f2, p4, pflgs); + xflgs = xflgs | XT_SFLG; + } + break; + + case ED_FLDSEP: /* separate fields */ + xflgs = 0; + break; + + case ED_EXCHMD: /* exchange */ + f2 = Read ((int32) (AC(p3) & AMASK), MM_OPND); + Write ((int32) (AC(p3) & AMASK), AC(p4), MM_OPND); + AC(p4) = f2; + break; + + case (0100 + (ED_MESSAG >> ED_V_POPC)): /* message */ + if (xflgs & XT_SFLG) + f1 = Read (ea + (pat & ED_M_NUM) + 1, MM_OPND); + else { + f1 = Read (ea + 1, MM_OPND); + if (f1 == 0) break; + } + incstorebp (f1, p4, pflgs); + break; + + case (0100 + (ED_SKPM >> ED_V_POPC)): /* skip on M */ + if (xflgs & XT_MFLG) ppi = (pat & ED_M_NUM) + 2; + break; + + case (0100 + (ED_SKPN >> ED_V_POPC)): /* skip on N */ + if (xflgs & XT_NFLG) ppi = (pat & ED_M_NUM) + 2; + break; + + case (0100 + (ED_SKPA >> ED_V_POPC)): /* skip always */ + ppi = (pat & ED_M_NUM) + 2; + break; + + default: /* NOP or undefined */ + break; + } /* end case pttrn op */ + AC(ac) = AC(ac) + ((ppi & ED_M_PBYN) << ED_V_PBYN); + AC(ac) = AC(ac) + (ppi >> 2) + ((AC(ac) & ED_PBYNO)? 1: 0); + AC(ac) = xflgs | (AC(ac) & ~(XT_MBZE | XFLGMASK)); + } + return ret; + } /* end case xop */ +return XT_MUUO; +} + +/* Supporting subroutines */ + +/* Increment byte pointer, register version */ + +d10 incbp (d10 bp) +{ +int32 p, s; + +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +p = p - s; /* adv P */ +if (p < 0) { /* end of word? */ + bp = (bp & LMASK) | (INCR (bp)); /* increment addr */ + p = (36 - s) & 077; /* reset P */ + } +bp = PUT_P (bp, p); /* store new P */ +return bp; +} + +/* Increment and load byte, extended version - uses register log */ + +d10 incloadbp (int32 ac, int32 pflgs) +{ +a10 ba; +d10 bp, wd; +int32 p, s; + +bp = AC(ac) = incbp (AC(ac)); /* increment bp */ +XT_INSRLOG (ac, rlog); /* log change */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EA_XSRC); /* calc bp eff addr */ +wd = Read (ba, MM_XSRC); /* read word */ +wd = (wd >> p) & bytemask[s]; /* get byte */ +return wd; +} + +/* Increment and deposit byte, extended version - uses register log */ + +void incstorebp (d10 val, int32 ac, int32 pflgs) +{ +a10 ba; +d10 bp, wd, mask; +int32 p, s; + +bp = AC(ac) = incbp (AC(ac)); /* increment bp */ +XT_INSRLOG (ac, rlog); /* log change */ +p = GET_P (bp); /* get P and S */ +s = GET_S (bp); +ba = calc_ea (bp, MM_EA_XDST); /* calc bp eff addr */ +wd = Read (ba, MM_XDST); /* read, write test */ +mask = bytemask[s] << p; /* shift mask, val */ +val = val << p; +wd = (wd & ~mask) | (val & mask); /* insert byte */ +Write (ba, wd & DMASK, MM_XDST); +return; +} + +/* Translate byte + + Arguments + by = byte to translate + tblad = virtual address of translation table + *xflgs = pointer to word containing translation flags + prv = previous mode flag for table lookup + Returns + xby = >= 0, translated byte + < 0, terminate translation +*/ + +d10 xlate (d10 by, a10 tblad, d10 *xflgs, int32 prv) +{ +a10 ea; +int32 tcode; +d10 tblent; + +ea = (tblad + ((int32) by >> 1)) & AMASK; +tblent = ((Read (ea, prv) >> ((by & 1)? 0: 18)) & RMASK); +tcode = XT_GETCODE (tblent); /* get xlate code */ +switch (tcode) { + + case 00: + return (*xflgs & XT_SFLG)? tblent: by; + + case 01: + break; + + case 02: + *xflgs = *xflgs & ~XT_MFLG; + return (*xflgs & XT_SFLG)? tblent: by; + + case 03: + *xflgs = *xflgs | XT_MFLG; + return (*xflgs & XT_SFLG)? tblent: by; + + case 04: + *xflgs = *xflgs | XT_SFLG | XT_NFLG; + return tblent; + + case 05: + *xflgs = *xflgs | XT_NFLG; + break; + + case 06: + *xflgs = (*xflgs | XT_SFLG | XT_NFLG) & ~XT_MFLG; + return tblent; + + case 07: + *xflgs = *xflgs | XT_SFLG | XT_NFLG | XT_MFLG; + return tblent; + } /* end case */ + +return -1; +} + +/* Fill out the destination string + + Arguments: + fill = fill + ac = 2 word AC block (length, byte pointer) + cnt = fill count + pflgs = PXCT flags +*/ + +void filldst (d10 fill, int32 ac, d10 cnt, int32 pflgs) +{ +int32 i, t; +int32 p1 = ADDA (ac, 1); + +for (i = 0; i < cnt; i++) { + if (i && (t = test_int ())) ABORT (t); + rlog = 0; /* clear log */ + incstorebp (fill, p1, pflgs); + AC(ac) = (AC(ac) & XFLGMASK) | ((AC(ac) - 1) & XLNTMASK); + } +rlog = 0; +return; +} + +/* Clean up after page fault + + Arguments: + logv = register change log + + For each register in logv, decrement the register's contents as + though it were a byte pointer. Note that the KS10 does + do a full decrement calculation but merely adds S to P. +*/ + +void xtcln (int32 logv) +{ +int32 p, reg; + +while (logv) { + XT_REMRLOG (reg, logv); /* get next reg */ + if ((reg >= 0) && (reg < AC_NUM)) { + p = GET_P (AC(reg)) + GET_S (AC(reg)); /* get p + s */ + AC(reg) = PUT_P (AC(reg), p); /* p <- p + s */ + } + } +return; +} diff --git a/PDP11/pdp11_cis.c b/PDP11/pdp11_cis.c new file mode 100644 index 0000000..3f1e2e6 --- /dev/null +++ b/PDP11/pdp11_cis.c @@ -0,0 +1,1592 @@ +/* pdp11_cis.c: PDP-11 CIS optional instruction set simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module simulates the PDP-11 commercial instruction set (CIS). + + 30-May-06 RMS Added interrupt tests to character instructions + Added 11/44 stack probe test to MOVCx (only) + 22-May-06 RMS Fixed bug in decode table (found by John Dundas) + Fixed bug in ASHP (reported by John Dundas) + Fixed bug in write decimal string with mmgt enabled + Fixed bug in 0-length strings in multiply/divide + 16-Sep-04 RMS Fixed bug in CMPP/N of negative strings + 17-Oct-02 RMS Fixed compiler warning (found by Hans Pufal) + 08-Oct-02 RMS Fixed macro definitions + + The commercial instruction set consists of three instruction formats: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register operands + | 0 1 1 1 1 1| 0 0 0 0| opcode | 076030:076057 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076070:076077 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ inline operands + | 0 1 1 1 1 1| 0 0 0 1| opcode | 076130:076157 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076170:076177 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ load descriptors + | 0 1 1 1 1 1| 0 0 0 0|op| 1 0| reg | 076020:076027 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 076060:076067 + + The CIS instructions operate on character strings, packed (decimal) + strings, and numeric (decimal) strings. Strings are described by + a two word descriptor: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | length in bytes | char string + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor + | starting byte address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | |str type| | length | decimal string + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ descriptor + | starting byte address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + Decimal string types are: + + <14:12> data type bytes occupied by n digits + 0 signed zoned n + 1 unsigned zone n + 2 trailing overpunch n + 3 leading overpunch n + 4 trailing separate n+1 + 5 leading separate n+1 + 6 signed packed n/2 +1 + 7 unsigned packed n/2 +1 + + Zero length character strings occupy no memory; zero length decimal strings + require either zero bytes (zoned, overpunch) or one byte (separate, packed). + + CIS instructions can run for a very long time, so they are interruptible + and restartable. In the simulator, all instructions run to completion. + The code is unoptimized. +*/ + +#include "pdp11_defs.h" + +/* Opcode bits */ + +#define INLINE 0100 /* inline */ +#define PACKED 0020 /* packed */ +#define NUMERIC 0000 /* numeric */ + +/* Interrupt test latency */ + +#define INT_TEST 100 + +/* Operand type definitions */ + +#define R0_DESC 1 /* descr in R0:R1 */ +#define R2_DESC 2 /* descr in R2:R3 */ +#define R4_DESC 3 /* descr in R4:R5 */ +#define R4_ARG 4 /* argument in R4 */ +#define IN_DESC 5 /* inline descriptor */ +#define IN_ARG 6 /* inline argument */ +#define MAXOPN 4 /* max # operands */ + +/* Decimal data type definitions */ + +#define XZ 0 /* signed zoned */ +#define UZ 1 /* unsigned zoned */ +#define TO 2 /* trailing overpunch */ +#define LO 3 /* leading overpunch */ +#define TS 4 /* trailing separate */ +#define LS 5 /* leading separate */ +#define XP 6 /* signed packed */ +#define UP 7 /* unsigned packed */ + +/* Decimal descriptor definitions */ + +#define DTYP_M 07 /* type mask */ +#define DTYP_V 12 /* type position */ +#define DLNT_M 037 /* length mask */ +#define DLNT_V 0 /* length position */ +#define GET_DTYP(x) (((x) >> DTYP_V) & DTYP_M) +#define GET_DLNT(x) (((x) >> DLNT_V) & DLNT_M) + +/* Shift operand definitions */ + +#define ASHRND_M 017 /* round digit mask */ +#define ASHRND_V 8 /* round digit pos */ +#define ASHLNT_M 0377 /* shift count mask */ +#define ASHLNT_V 0 /* shift length pos */ +#define ASHSGN 0200 /* shift sign */ +#define GET_ASHRND(x) (((x) >> ASHRND_V) & ASHRND_M) +#define GET_ASHLNT(x) (((x) >> ASHLNT_V) & ASHLNT_M) + +/* Operand array aliases */ + +#define A1LNT arg[0] +#define A1ADR arg[1] +#define A2LNT arg[2] +#define A2ADR arg[3] +#define A3LNT arg[4] +#define A3ADR arg[5] +#define A1 &arg[0] +#define A2 &arg[2] +#define A3 &arg[4] + +/* Condition code macros */ + +#define GET_BIT(ir,n) (((ir) >> (n)) & 1) +#define GET_SIGN_L(ir) GET_BIT((ir), 31) +#define GET_SIGN_W(ir) GET_BIT((ir), 15) +#define GET_SIGN_B(ir) GET_BIT((ir), 7) +#define GET_Z(ir) ((ir) == 0) + +/* Decimal string structure */ + +#define DSTRLNT 4 +#define DSTRMAX (DSTRLNT - 1) +#define MAXDVAL 429496730 /* 2^32 / 10 */ + +typedef struct { + uint32 sign; + uint32 val[DSTRLNT]; + } DSTR; + +static DSTR Dstr0 = { 0, 0, 0, 0, 0 }; + +extern int32 isenable, dsenable; +extern int32 N, Z, V, C, fpd, ipl; +extern int32 R[8], trap_req; +extern int32 sim_interval; +extern uint32 cpu_type; +extern FILE *sim_deb; + +int32 ReadDstr (int32 *dscr, DSTR *dec, int32 flag); +void WriteDstr (int32 *dscr, DSTR *dec, int32 flag); +int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin); +void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst); +int32 CmpDstr (DSTR *src1, DSTR *src2); +int32 TestDstr (DSTR *dsrc); +int32 LntDstr (DSTR *dsrc, int32 nz); +uint32 NibbleLshift (DSTR *dsrc, int32 sc); +uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin); +int32 WordLshift (DSTR *dsrc, int32 sc); +void WordRshift (DSTR *dsrc, int32 sc); +void CreateTable (DSTR *dsrc, DSTR mtable[10]); +t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st); +int32 movx_setup (int32 op, int32 *arg); +void movx_cleanup (int32 op); + +extern int32 ReadW (int32 addr); +extern void WriteW (int32 data, int32 addr); +extern int32 ReadB (int32 addr); +extern int32 ReadMB (int32 addr); +extern void WriteB (int32 data, int32 addr); +extern int32 calc_ints (int32 nipl, int32 trq); + +/* Table of instruction operands */ + +static int32 opntab[128][MAXOPN] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 007 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 010 - 017 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* LD2R */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* MOVC */ + 0, 0, 0, 0, /* MOVRC */ + 0, 0, 0, 0, /* MOVTC */ + 0, 0, 0, 0, /* 033 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 034 - 037 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, /* LOCC */ + 0, 0, 0, 0, /* SKPC */ + 0, 0, 0, 0, /* SCANC */ + 0, 0, 0, 0, /* SPANC */ + 0, 0, 0, 0, /* CMPC */ + 0, 0, 0, 0, /* MATC */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 046 - 047 */ + R0_DESC, R2_DESC, R4_DESC, 0, /* ADDN */ + R0_DESC, R2_DESC, R4_DESC, 0, /* SUBN */ + R0_DESC, R2_DESC, 0, 0, /* CMPN */ + R0_DESC, 0, 0, 0, /* CVTNL */ + R0_DESC, R2_DESC, 0, 0, /* CVTPN */ + R0_DESC, R2_DESC, 0, 0, /* CVTNP */ + R0_DESC, R2_DESC, R4_ARG, 0, /* ASHN */ + R0_DESC, 0, 0, 0, /* CVTLN */ + 0, 0, 0, 0, 0, 0, 0, 0, /* LD3R */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + R0_DESC, R2_DESC, R4_DESC, 0, /* ADDP */ + R0_DESC, R2_DESC, R4_DESC, 0, /* SUBP */ + R0_DESC, R2_DESC, 0, 0, /* CMPP */ + R0_DESC, 0, 0, 0, /* CVTPL */ + R0_DESC, R2_DESC, R4_DESC, 0, /* MULP */ + R0_DESC, R2_DESC, R4_DESC, 0, /* DIVP */ + R0_DESC, R2_DESC, R4_ARG, 0, /* ASHP */ + R0_DESC, 0, 0, 0, /* CVTLP */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 100 - 107 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 110 - 117 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 120 - 127 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + IN_DESC, IN_DESC, IN_ARG, 0, /* MOVCI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* MOVRCI */ + IN_DESC, IN_DESC, IN_ARG, IN_ARG, /* MOVTCI */ + 0, 0, 0, 0, /* 133 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 134 - 137 */ + 0, 0, 0, 0, 0, 0, 0, 0, + IN_DESC, IN_ARG, 0, 0, /* LOCCI */ + IN_DESC, IN_ARG, 0, 0, /* SKPCI */ + IN_DESC, IN_DESC, 0, 0, /* SCANCI */ + IN_DESC, IN_DESC, 0, 0, /* SPANCI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* CMPCI */ + IN_DESC, IN_DESC, 0, 0, /* MATCI */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 146 - 147 */ + IN_DESC, IN_DESC, IN_DESC, 0, /* ADDNI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* SUBNI */ + IN_DESC, IN_DESC, 0, 0, /* CMPNI */ + IN_DESC, IN_ARG, 0, 0, /* CVTNLI */ + IN_DESC, IN_DESC, 0, 0, /* CVTPNI */ + IN_DESC, IN_DESC, 0, 0, /* CVTNPI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* ASHNI */ + IN_DESC, IN_DESC, 0, 0, /* CVTLNI */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 160 - 167 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + IN_DESC, IN_DESC, IN_DESC, 0, /* ADDPI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* SUBPI */ + IN_DESC, IN_DESC, 0, 0, /* CMPPI */ + IN_DESC, IN_ARG, 0, 0, /* CVTPLI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* MULPI */ + IN_DESC, IN_DESC, IN_DESC, 0, /* DIVPI */ + IN_DESC, IN_DESC, IN_ARG, 0, /* ASHPI */ + IN_DESC, IN_DESC, 0, 0 /* CVTLPI */ + }; + +/* ASCII to overpunch table: sign is <7>, digit is <4:0> */ + +static int32 overbin[128] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 000 - 037 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0x80, 0, 0, 0, 0, 0, 0, /* 040 - 077 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 0x80, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, /* 100 - 137 */ + 8, 9, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, + 0x87, 0x88, 0x89, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x80, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 140 - 177 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0x80, 0, 0 + }; + +/* Overpunch to ASCII table: indexed by sign and digit */ + +static int32 binover[2][16] = { + '{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + '0', '0', '0', '0', '0', '0', + '}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', + '0', '0', '0', '0', '0', '0' + }; + +static unsigned char movbuf[65536]; + +/* CIS emulator */ + +t_stat cis11 (int32 IR) +{ +int32 c, i, j, t, op, rn, addr; +int32 match, limit, mvlnt, shift; +int32 spc, ldivd, ldivr; +int32 arg[6]; /* operands */ +int32 old_PC; +uint32 nc, digit, result; +t_stat st; +static DSTR accum, src1, src2, dst; +static DSTR mptable[10]; +static DSTR Dstr1 = { 0, 0x10, 0, 0, 0 }; + +old_PC = (PC - 2) & 0177777; /* original PC */ +op = IR & 0177; /* IR <6:0> */ +for (i = j = 0; (i < MAXOPN) && opntab[op][i]; i++) { /* parse operands */ + switch (opntab[op][i]) { /* case on op type */ + + case R0_DESC: + arg[j++] = R[0]; + arg[j++] = R[1]; + break; + + case R2_DESC: + arg[j++] = R[2]; + arg[j++] = R[3]; + break; + + case R4_DESC: + arg[j++] = R[4]; + arg[j++] = R[5]; + break; + + case R4_ARG: + arg[j++] = R[4]; + break; + + case IN_DESC: + addr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + arg[j++] = ReadW (addr | dsenable); + arg[j++] = ReadW (((addr + 2) & 0177777) | dsenable); + break; + + case IN_ARG: + arg[j++] = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + break; + + default: + return SCPE_IERR; + } /* end case */ + } /* end for */ +switch (op) { /* case on opcode */ + +/* MOVC, MOVTC, MOVCI, MOVTCI + + Operands (MOVC, MOVTC): + R0, R1 = source string descriptor + R2, R3 = dest string descriptor + R4<7:0> = fill character + R5 = translation table address (MOVTC only) + Operands (MOVCI, MOVTCI): + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = dest string descriptor + A3LNT<7:0> = fill character + A3ADR = translation table address (MOVTCI only) + + Condition codes: + NZVC = set from src.lnt - dst.lnt + + Registers (MOVC, MOVTC only) + R0 = max (0, src.len - dst.len) + R1:R3 = 0 + R4:R5 = unchanged + + Notes: + - If either the source or destination lengths are zero, + the move loops exit immediately. + - If the source length does not exceed the destination + length, the fill loop exits immediately. +*/ + + case 030: case 032: case 0130: case 0132: + if (!fpd) { /* first time? */ + mvlnt = movx_setup (op, arg); /* set up reg */ + if (R[1] < R[3]) { /* move backwards? */ + R[1] = (R[1] + mvlnt) & 0177777; /* bias addresses */ + R[3] = (R[3] + mvlnt) & 0177777; + } + } + +/* At this point, + + R0-R5 = arguments + M[SP] = move length */ + + if (R[0] && R[2]) { /* move to do? */ + if (R[1] < R[3]) { /* backwards? */ + for (i = 0; R[0] && R[2]; ) { /* move loop */ + t = ReadB (((R[1] - 1) & 0177777) | dsenable); + if (op & 2) t = ReadB (((R[5] + t) & 0177777) | dsenable); + WriteB (t, ((R[3] - 1) & 0177777) | dsenable); + R[0]--; + R[1] = (R[1] - 1) & 0177777; + R[2]--; + R[3] = (R[3] - 1) & 0177777; + if ((++i >= INT_TEST) && R[0] && R[2]) { + if (cis_int_test (i, old_PC, &st)) return st; + i = 0; + } + } /* end for lnts */ + mvlnt = ReadW (SP | dsenable); /* recover mvlnt */ + R[3] = (R[3] + mvlnt) & 0177777; /* end of dst str */ + } /* end if bkwd */ + else { /* forward */ + for (i = 0; R[0] && R[2]; ) { /* move loop */ + t = ReadB ((R[1] & 0177777) | dsenable); + if (op & 2) t = ReadB (((R[5] + t) & 0177777) | dsenable); + WriteB (t, (R[3] & 0177777) | dsenable); + R[0]--; + R[1] = (R[1] + 1) & 0177777; + R[2]--; + R[3] = (R[3] + 1) & 0177777; + if ((++i >= INT_TEST) && R[0] && R[2]) { + if (cis_int_test (i, old_PC, &st)) return st; + i = 0; + } + } /* end for lnts */ + } /* end else fwd */ + } /* end if move */ + for (i = 0; i < R[2]; i++) { + WriteB (R[4], ((R[3] + i) & 0177777) | dsenable); + } + movx_cleanup (op); /* cleanup */ + return SCPE_OK; + +/* MOVRC, MOVRCI + + Operands (MOVC, MOVTC): + R0, R1 = source string descriptor + R2, R3 = dest string descriptor + R4<7:0> = fill character + Operands (MOVCI, MOVTCI): + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = dest string descriptor + A3LNT<7:0> = fill character + + Condition codes: + NZVC = set from src.lnt - dst.lnt + + Registers (MOVRC only) + R0 = max (0, src.len - dst.len) + R1:R3 = 0 + R4:R5 = unchanged + + Notes: see MOVC, MOVCI +*/ + + case 031: case 0131: + if (!fpd) { /* first time? */ + mvlnt = movx_setup (op, arg); /* set up reg */ + R[1] = (R[1] + R[0] - mvlnt) & 0177777; /* eff move start */ + R[3] = (R[3] + R[2] - mvlnt) & 0177777; + if (R[1] < R[3]) { /* move backwards? */ + R[1] = (R[1] + mvlnt) & 0177777; /* bias addresses */ + R[3] = (R[3] + mvlnt) & 0177777; + } + } + +/* At this point, + + R0-R5 = arguments + M[SP] = move length */ + + if (R[0] && R[2]) { /* move to do? */ + if (R[1] < R[3]) { /* backwards? */ + for (i = 0; R[0] && R[2]; ) { /* move loop */ + t = ReadB (((R[1] - 1) & 0177777) | dsenable); + WriteB (t, ((R[3] - 1) & 0177777) | dsenable); + R[0]--; + R[1] = (R[1] - 1) & 0177777; + R[2]--; + R[3] = (R[3] - 1) & 0177777; + if ((++i >= INT_TEST) && R[0] && R[2]) { + if (cis_int_test (i, old_PC, &st)) return st; + i = 0; + } + } /* end for lnts */ + } /* end if bkwd */ + else { /* forward */ + for (i = 0; R[0] && R[2]; ) { /* move loop */ + t = ReadB ((R[1] & 0177777) | dsenable); + WriteB (t, (R[3] & 0177777) | dsenable); + R[0]--; + R[1] = (R[1] + 1) & 0177777; + R[2]--; + R[3] = (R[3] + 1) & 0177777; + if ((++i >= INT_TEST) && R[0] && R[2]) { + if (cis_int_test (i, old_PC, &st)) return st; + i = 0; + } + } /* end for lnts */ + mvlnt = ReadW (SP | dsenable); /* recover mvlnt */ + R[3] = (R[3] - mvlnt) & 0177777; /* start of dst str */ + } /* end else fwd */ + } /* end if move */ + for (i = 0; i < R[2]; i++) { + WriteB (R[4], ((R[3] - R[2] + i) & 0177777) | dsenable); + } + movx_cleanup (op); /* cleanup */ + return SCPE_OK; + +/* Load descriptors - no operands */ + + case 020: case 021: case 022: case 023: + case 024: case 025: case 026: case 027: + case 060: case 061: case 062: case 063: + case 064: case 065: case 066: case 067: + limit = (op & 040)? 6: 4; + rn = IR & 07; /* get register */ + t = R[rn]; + spc = (rn == 7)? isenable: dsenable; + for (j = 0; j < limit; j = j + 2) { /* loop for 2,3 dscr */ + addr = ReadW (((t + j) & 0177777) | spc); + R[j] = ReadW (addr | dsenable); + R[j + 1] = ReadW (((addr + 2) & 0177777) | dsenable); + } + if (rn >= limit) R[rn] = (R[rn] + limit) & 0177777; + return SCPE_OK; + +/* LOCC, SKPC, LOCCI, SKPCI + + Operands (LOCC, SKPC): + R0, R1 = source string descriptor + R4<7:0> = match character + Operands (LOCCI, SKPCI): + A1LNT, A1ADR = source string descriptor + A2LNT<7:0> = match character + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0:R1 = substring descriptor where operation terminated +*/ + + case 0140: case 0141: /* inline */ + if (!fpd) { /* FPD clear? */ + WriteW (R[4], ((SP - 2) & 0177777) | dsenable); + SP = (SP - 2) & 0177777; /* push R4 */ + R[0] = A1LNT; /* args to registers */ + R[1] = A1ADR; + R[4] = A2LNT; + } /* fall through */ + case 040: case 041: /* register */ + fpd = 1; /* set FPD */ + R[4] = R[4] & 0377; /* match character */ + for (i = 0; R[0] != 0;) { /* loop */ + c = ReadB (R[1] | dsenable); /* get char */ + if ((c == R[4]) ^ (op & 1)) break; /* = + LOC, != + SKP? */ + R[0]--; /* decr count, */ + R[1] = (R[1] + 1) & 0177777; /* incr addr */ + if ((++i >= INT_TEST) && R[0]) { /* test for intr? */ + if (cis_int_test (i, old_PC, &st)) return st; + i = 0; + } + } + N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = C = 0; + fpd = 0; /* instr done */ + if (op & INLINE) { /* inline? */ + R[4] = ReadW (SP | dsenable); /* restore R4 */ + SP = (SP + 2) & 0177777; + } + return SCPE_OK; + +/* SCANC, SPANC, SCANCI, SPANCI + + Operands (SCANC, SPANC): + R0, R1 = source string descriptor + R4<7:0> = mask + R5 = table address + Operands (SCANCI, SPANCI): + A1LNT, A1ADR = source string descriptor + A2LNT<7:0> = match character + A2ADR = table address + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0:R1 = substring descriptor where operation terminated +*/ + + case 0142: case 0143: /* inline */ + if (!fpd) { /* FPD clear? */ + WriteW (R[4], ((SP - 4) & 0177777) | dsenable); + WriteW (R[5], ((SP - 2) & 0177777) | dsenable); + SP = (SP - 4) & 0177777; /* push R4, R5 */ + R[0] = A1LNT; /* args to registers */ + R[1] = A1ADR; + R[4] = A2LNT; + R[5] = A2ADR; + } /* fall through */ + case 042: case 043: /* register */ + fpd = 1; /* set FPD */ + R[4] = R[4] & 0377; /* match character */ + for (i = 0; R[0] != 0;) { /* loop */ + t = ReadB (R[1] | dsenable); /* get char as index */ + c = ReadB (((R[5] + t) & 0177777) | dsenable); + if (((c & R[4]) != 0) ^ (op & 1)) break; /* != + SCN, = + SPN? */ + R[0]--; /* decr count, */ + R[1] = (R[1] + 1) & 0177777; /* incr addr */ + if ((++i >= INT_TEST) && R[0]) { /* test for intr? */ + if (cis_int_test (i, old_PC, &st)) return st; + i = 0; + } + } + N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = C = 0; + fpd = 0; /* instr done */ + if (op & INLINE) { /* inline? */ + R[4] = ReadW (SP | dsenable); /* restore R4, R5 */ + R[5] = ReadW (((SP + 2) & 0177777) | dsenable); + SP = (SP + 4) & 0177777; + } + return SCPE_OK; + +/* CMPC, CMPCI + + Operands (CMPC): + R0, R1 = source1 string descriptor + R2, R3 = source2 string descriptor + R4<7:0> = fill character + Operands (CMPCI): + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + A3LNT<7:0> = fill character + + Condition codes: + NZVC = set from src1 - src2 at mismatch, or + = 0100 if equal + + Registers (CMPC only): + R0:R1 = unmatched source1 substring descriptor + R2:R3 = unmatched source2 substring descriptor +*/ + + case 0144: /* inline */ + if (!fpd) { /* FPD clear? */ + WriteW (R[0], ((SP - 10) & 0177777) | dsenable); + WriteW (R[1], ((SP - 8) & 0177777) | dsenable); + WriteW (R[2], ((SP - 6) & 0177777) | dsenable); + WriteW (R[3], ((SP - 4) & 0177777) | dsenable); + WriteW (R[4], ((SP - 2) & 0177777) | dsenable); + SP = (SP - 10) & 0177777; /* push R0 - R4 */ + R[0] = A1LNT; /* args to registers */ + R[1] = A1ADR; + R[2] = A2LNT; + R[3] = A2ADR; + R[4] = A3LNT; + } /* fall through */ + case 044: /* register */ + fpd = 1; /* set FPD */ + R[4] = R[4] & 0377; /* mask fill */ + c = t = 0; + for (i = 0; (R[0] || R[2]); ) { /* until cnts == 0 */ + if (R[0]) c = ReadB (R[1] | dsenable); /* get src1 or fill */ + else c = R[4]; + if (R[2]) t = ReadB (R[3] | dsenable); /* get src2 or fill */ + else t = R[4]; + if (c != t) break; /* if diff, done */ + if (R[0]) { /* if more src1 */ + R[0]--; /* decr count, */ + R[1] = (R[1] + 1) & 0177777; /* incr addr */ + } + if (R[2]) { /* if more src2 */ + R[2]--; /* decr count, */ + R[3] = (R[3] + 1) & 0177777; /* incr addr */ + } + if ((++i >= INT_TEST) && (R[0] || R[2])) { /* test for intr? */ + if (cis_int_test (i, old_PC, &st)) return st; + i = 0; + } + } + j = c - t; /* last chars read */ + N = GET_SIGN_B (j); /* set cc's */ + Z = GET_Z (j); + V = GET_SIGN_B ((c ^ t) & (~t ^ j)); + C = (c < t); + fpd = 0; /* instr done */ + if (op & INLINE) { /* inline? */ + R[0] = ReadW (SP | dsenable); /* restore R0 - R4 */ + R[1] = ReadW (((SP + 2) & 0177777) | dsenable); + R[2] = ReadW (((SP + 4) & 0177777) | dsenable); + R[3] = ReadW (((SP + 6) & 0177777) | dsenable); + R[4] = ReadW (((SP + 8) & 0177777) | dsenable); + SP = (SP + 10) & 0177777; + } + return SCPE_OK; + +/* MATC, MATCI + + Operands (MATC): + R0, R1 = source string descriptor + R2, R3 = substring descriptor + Operands (MATCI): + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0:R1 = source substring descriptor for match + + Notes: + - If the string is zero length, and the substring is not, + the outer loop exits immediately, and the result is + "no match" + - If the substring is zero length, the inner loop always + exits immediately, and the result is a "match" + - If the string is zero length, and the substring is as + well, the outer loop executes, the inner loop exits + immediately, and the result is a match, but the result + is the length of the string (zero), or "no match" +*/ + + case 0145: /* inline */ + if (!fpd) { /* FPD clear? */ + WriteW (R[2], ((SP - 4) & 0177777) | dsenable); + WriteW (R[3], ((SP - 2) & 0177777) | dsenable); + SP = (SP - 4) & 0177777; /* push R2, R3 */ + R[0] = A1LNT; /* args to registers */ + R[1] = A1ADR; + R[2] = A2LNT; + R[3] = A2ADR; + } /* fall through */ + case 0045: /* register */ + fpd = 1; + for (match = 0; R[0] >= R[2]; ) { /* loop thru string */ + for (i = 0, match = 1; match && (i < R[2]); i++) { + c = ReadB (((R[1] + i) & 0177777) | dsenable); + t = ReadB (((R[3] + i) & 0177777) | dsenable); + match = (c == t); /* end for substring */ + } + if (match) break; /* exit if match */ + R[0]--; /* on to next char */ + R[1] = (R[1] + 1) & 0177777; + if (cis_int_test (i, old_PC, &st)) return st; + } + if (!match) { /* if no match */ + R[1] = (R[1] + R[0]) & 0177777; + R[0] = 0; + } + N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = C = 0; + fpd = 0; /* instr done */ + if (op & INLINE) { /* inline? */ + R[2] = ReadW (SP | dsenable); /* restore R2, R3 */ + R[3] = ReadW (((SP + 2) & 0177777) | dsenable); + SP = (SP + 4) & 0177777; + } + return SCPE_OK; + +/* ADDN, SUBN, ADDP, SUBP, ADDNI, SUBNI, ADDPI, SUBPI + + Operands: + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + A3LNT, A3ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (ADDN, ADDP, SUBN, SUBP only): + R0:R3 = 0 +*/ + + case 050: case 051: case 070: case 071: + case 0150: case 0151: case 0170: case 0171: + ReadDstr (A1, &src1, op); /* get source1 */ + ReadDstr (A2, &src2, op); /* get source2 */ + if (op & 1) src1.sign = src1.sign ^ 1; /* sub? invert sign */ + if (src1.sign ^ src2.sign) { /* opp signs? sub */ + if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */ + SubDstr (&src1, &src2, &dst); /* src2 - src1 */ + dst.sign = src2.sign; /* sign = src2 */ + } + else { + SubDstr (&src2, &src1, &dst); /* src1 - src2 */ + dst.sign = src1.sign; /* sign = src1 */ + } + V = 0; /* can't carry */ + } + else { /* addition */ + V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */ + dst.sign = src1.sign; /* set result sign */ + } + C = 0; + WriteDstr (A3, &dst, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return SCPE_OK; + +/* MULP, MULPI + + Operands: + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + A3LNT, A3ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (MULP only): + R0:R3 = 0 +*/ + + case 074: case 0174: + dst = Dstr0; /* clear result */ + if (ReadDstr (A1, &src1, op) && ReadDstr (A2, &src2, op)) { + dst.sign = src1.sign ^ src2.sign; /* sign of result */ + accum = Dstr0; /* clear accum */ + NibbleRshift (&src1, 1, 0); /* shift out sign */ + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ + digit = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (digit > 0) /* add in digit*mpcnd */ + AddDstr (&mptable[digit], &accum, &accum, 0); + nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */ + NibbleRshift (&dst, 1, nc); /* result right 4 */ + } + V = TestDstr (&accum) != 0; /* if ovflo, set V */ + } + else V = 0; /* result = 0 */ + C = 0; /* C = 0 */ + WriteDstr (A3, &dst, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return SCPE_OK; + +/* DIVP, DIVPI + + Operands: + A1LNT, A1ADR = divisor string descriptor + A2LNT, A2ADR = dividend string descriptor + A3LNT, A3ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = set if divide by zero + + Registers (DIVP only): + R0:R3 = 0 +*/ + + case 075: case 0175: + ldivr = ReadDstr (A1, &src1, op); /* get divisor */ + if (ldivr == 0) { /* divisor = 0? */ + V = C = 1; /* set cc's */ + return SCPE_OK; + } + ldivr = LntDstr (&src1, ldivr); /* get exact length */ + ldivd = ReadDstr (A2, &src2, op); /* get dividend */ + ldivd = LntDstr (&src2, ldivd); /* get exact length */ + dst = Dstr0; /* clear dest */ + NibbleRshift (&src1, 1, 0); /* right justify ops */ + NibbleRshift (&src2, 1, 0); + if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ + WordLshift (&src1, t / 8); /* align divr to divd */ + NibbleLshift (&src1, t % 8); + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 0; i <= t; i++) { /* divide loop */ + for (digit = 9; digit > 0; digit--) { /* find digit */ + if (CmpDstr (&src2, &mptable[digit]) >= 0) { + SubDstr (&mptable[digit], &src2, &src2); + dst.val[0] = dst.val[0] | digit; + break; + } /* end if */ + } /* end for */ + NibbleLshift (&src2, 1); /* shift dividend */ + NibbleLshift (&dst, 1); /* shift quotient */ + } /* end divide loop */ + dst.sign = src1.sign ^ src2.sign; /* calculate sign */ + } /* end if */ + V = C = 0; + WriteDstr (A3, &dst, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return SCPE_OK; + +/* CMPN, CMPP, CMPNI, CMPPI + + Operands: + A1LNT, A1ADR = source1 string descriptor + A2LNT, A2ADR = source2 string descriptor + + Condition codes: + NZ = set from comparison + VC = 0 + + Registers (CMPN, CMPP only): + R0:R3 = 0 +*/ + + case 052: case 072: case 0152: case 0172: + ReadDstr (A1, &src1, op); /* get source1 */ + ReadDstr (A2, &src2, op); /* get source2 */ + N = Z = V = C = 0; + if (src1.sign != src2.sign) N = src1.sign; + else { + t = CmpDstr (&src1, &src2); /* compare strings */ + if (t < 0) N = (src1.sign? 0: 1); + else if (t > 0) N = (src1.sign? 1: 0); + else Z = 1; + } + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[2] = R[3] = 0; + return SCPE_OK; + +/* ASHN, ASHP, ASHNI, ASHPI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = destination string descriptor + A3LNT<11:8> = rounding digit + A3LNT<7:0> = shift count + + Condition codes: + NZV = set from result + C = 0 + + Registers (ASHN, ASHP only): + R0:R1, R4 = 0 +*/ + + case 056: case 076: case 0156: case 0176: + ReadDstr (A1, &src1, op); /* get source */ + V = C = 0; /* init cc's */ + shift = GET_ASHLNT (A3LNT); /* get shift count */ + if (shift & ASHSGN) { /* right shift? */ + shift = (ASHLNT_M + 1 - shift); /* !shift! */ + WordRshift (&src1, shift / 8); /* do word shifts */ + NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ + t = GET_ASHRND (A3LNT); /* get rounding digit */ + if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ + AddDstr (&src1, &Dstr1, &src1, 0); /* round */ + src1.val[0] = src1.val[0] & ~0xF; /* clear sign */ + } /* end right shift */ + else if (shift) { /* left shift? */ + if (WordLshift (&src1, shift / 8)) V = 1; /* do word shifts */ + if (NibbleLshift (&src1, shift % 8)) V = 1; + } /* end left shift */ + WriteDstr (A2, &src1, op); /* store result */ + if ((op & INLINE) == 0) /* if reg, clr reg */ + R[0] = R[1] = R[4] = 0; + return SCPE_OK; + +/* CVTPN, CVTPNI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (CVTPN only): + R0:R1 = 0 +*/ + + case 054: case 0154: + ReadDstr (A1, &src1, PACKED); /* get source */ + V = C = 0; /* init cc's */ + WriteDstr (A2, &src1, NUMERIC); /* write dest */ + if ((op & INLINE) == 0) R[0] = R[1] = 0; /* if reg, clr reg */ + return SCPE_OK; + +/* CVTNP, CVTNPI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT, A2ADR = destination string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers (CVTNP only): + R0:R1 = 0 +*/ + + case 055: case 0155: + ReadDstr (A1, &src1, NUMERIC); /* get source */ + V = C = 0; /* init cc's */ + WriteDstr (A2, &src1, PACKED); /* write dest */ + if ((op & INLINE) == 0) R[0] = R[1] = 0; /* if reg, clr reg */ + return SCPE_OK; + +/* CVTNL, CVTPL, CVTNLI, CVTPLI + + Operands: + A1LNT, A1ADR = source string descriptor + A2LNT = destination address (inline only) + + Condition codes: + NZV = set from result + C = source < 0 and result != 0 + + Registers (CVTNL, CVTPL only): + R0:R1 = 0 + R2:R3 = result +*/ + + case 053: case 073: case 0153: case 0173: + ReadDstr (A1, &src1, op); /* get source */ + V = result = 0; /* clear V, result */ + for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ + digit = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (digit || result || V) { /* skip initial 0's */ + if (result >= MAXDVAL) V = 1; + result = (result * 10) + digit; + if (result < digit) V = 1; + } /* end if */ + } /* end for */ + if (src1.sign) result = (~result + 1) & 0xFFFFFFFF; + N = GET_SIGN_L (result); + Z = GET_Z (result); + V = V | (N ^ src1.sign); /* overflow if +2**31 */ + C = src1.sign && (Z == 0); /* set C based on std */ + if (op & INLINE) { /* inline? */ + WriteW (result & 0177777, A2LNT | dsenable); + WriteW ((result >> 16) & 0177777, + ((A2LNT + 2) & 0177777) | dsenable); + } + else { + R[0] = R[1] = 0; + R[2] = (result >> 16) & 0177777; + R[3] = result & 0177777; + } + return SCPE_OK; + +/* CVTLN, CVTLP, CVTLNI, CVTLPI + + Operands: + A1LNT, A1ADR = destination string descriptor + A2LNT, A2ADR = source long (CVTLNI, CVTLPI) - VAX format + R2:R3 = source long (CVTLN, CVTLP) - EIS format + + Condition codes: + NZV = set from result + C = 0 + + Registers (CVTLN, CVTLP only) + R2:R3 = 0 +*/ + + case 057: case 077: + result = (R[2] << 16) | R[3]; /* op in EIS format */ + R[2] = R[3] = 0; /* clear registers */ + goto CVTLx; /* join common code */ + case 0157: case 0177: + result = (A2ADR << 16) | A2LNT; /* op in VAX format */ + CVTLx: + dst = Dstr0; /* clear result */ + if (dst.sign = GET_SIGN_L (result)) result = (~result + 1) & 0xFFFFFFFF; + for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { + digit = result % 10; + result = result / 10; + dst.val[i / 8] = dst.val[i / 8] | (digit << ((i % 8) * 4)); + } + V = C = 0; + WriteDstr (A1, &dst, op); /* write result */ + return SCPE_OK; + + default: + setTRAP (TRAP_ILL); + break; + } /* end case */ +return SCPE_OK; +} /* end cis */ + +/* Get decimal string + + Arguments: + dscr = decimal string descriptor + src = decimal string structure + flag = numeric/packed flag + + The routine returns the length in int32's of the non-zero part of + the string. + + This routine plays fast and loose with operand checking, as did the + original 11/23 microcode (half of which I wrote). In particular, + + - If the flag specifies packed, the type is not checked at all. + The sign of an unsigned string is assumed to be 0xF (an + alternative for +). + - If the flag specifies numeric, packed types will be treated + as unsigned zoned. + - For separate, only the '-' sign is checked, not the '+'. + + However, to simplify the code elsewhere, digits are range checked, + and bad digits are replaced with 0's. +*/ + +int32 ReadDstr (int32 *dscr, DSTR *src, int32 flag) +{ +int32 c, i, end, lnt, type, t; + +*src = Dstr0; /* clear result */ +type = GET_DTYP (dscr[0]); /* get type */ +lnt = GET_DLNT (dscr[0]); /* get string length */ +if (flag & PACKED) { /* packed? */ + end = lnt / 2; /* last byte */ + for (i = 0; i <= end; i++) { /* loop thru string */ + c = ReadB (((dscr[1] + end - i) & 0177777) | dsenable); + if (i == 0) t = c & 0xF; /* save sign */ + if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF; + if (c >= 0xA0) c = c & 0xF; /* check hi digit */ + if ((c & 0xF) >= 0xA) c = c & 0xF0; /* check lo digit */ + src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8)); + } /* end for */ + if ((t == 0xB) || (t == 0xD)) src->sign = 1; /* if -, set sign */ + src->val[0] = src->val[0] & ~0xF; /* clear sign */ + } /* end packed */ +else { /* numeric */ + if (type >= TS) src->sign = (ReadB ((((type == TS)? + dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable) == '-'); + for (i = 1; i <= lnt; i++) { /* loop thru string */ + c = ReadB (((dscr[1] + lnt - i) & 0177777) | dsenable); + if ((i == 1) && (type == XZ) && ((c & 0xF0) == 0x70)) + src->sign = 1; /* signed zoned */ + else if (((i == 1) && (type == TO)) || + ((i == lnt) && (type == LO))) { + c = overbin[c & 0177]; /* get sign and digit */ + src->sign = c >> 7; /* set sign */ + } + c = c & 0xF; /* get digit */ + if (c > 9) c = 0; /* range check */ + src->val[i / 8] = src->val[i / 8] | (c << ((i % 8) * 4)); + } /* end for */ + } /* end numeric */ +return TestDstr (src); /* clean -0 */ +} + +/* Store decimal string + + Arguments: + dsrc = decimal string descriptor + src = decimal string structure + flag = numeric/packed flag + + PSW.NZ are also set to their proper values + PSW.V will be set on overflow; it must be initialized elsewhere + (to allow for external overflow calculations) + + The rules for the stored sign and the PSW sign are: + + - Stored sign is negative if input is negative, string type + is signed, and the result is non-zero or there was overflow + - PSW sign is negative if input is negative, string type is + signed, and the result is non-zero + + Thus, the stored sign and the PSW sign will differ in one case: + a negative zero generated by overflow is stored with a negative + sign, but PSW.N is clear +*/ + +void WriteDstr (int32 *dscr, DSTR *dst, int32 flag) +{ +int32 c, i, limit, end, type, lnt; +uint32 mask; +static uint32 masktab[8] = { + 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, + 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 + }; +static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 }; + +type = GET_DTYP (dscr[0]); /* get type */ +lnt = GET_DLNT (dscr[0]); /* get string length */ +mask = 0; /* can't ovflo */ +Z = 1; /* assume all 0's */ +limit = lnt / 8; /* limit for test */ +for (i = 0; i < DSTRLNT; i++) { /* loop thru value */ + if (i == limit) mask = masktab[lnt % 8]; /* at limit, get mask */ + else if (i > limit) mask = 0xFFFFFFFF; /* beyond, all ovflo */ + if (dst->val[i] & mask) V = 1; /* test for ovflo */ + if (dst->val[i] = dst->val[i] & ~mask) Z = 0; /* test nz */ + } +dst->sign = dst->sign & ~unsignedtab[type] & ~(Z & ~V); +N = dst->sign & ~Z; /* N = sign, if ~zero */ + +if (flag & PACKED) { /* packed? */ + end = lnt / 2; /* end of string */ + if (type == UP) dst->val[0] = dst->val[0] | 0xF; + else dst->val[0] = dst->val[0] | 0xC | dst->sign; + for (i = 0; i <= end; i++) { /* store string */ + c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; + WriteB (c, ((dscr[1] + end - i) & 0177777) | dsenable); + } /* end for */ + } /* end packed */ +else { + if (type >= TS) WriteB (dst->sign? '-': '+', (((type == TS)? + dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable); + for (i = 1; i <= lnt; i++) { /* store string */ + c = (dst->val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ + if ((i == 1) && (type == XZ) && dst->sign) + c = c | 0x70; /* signed zoned */ + else if (((i == 1) && (type == TO)) || + ((i == lnt) && (type == LO))) + c = binover[dst->sign][c]; /* get sign and digit */ + else c = c | 0x30; /* default */ + WriteB (c, ((dscr[1] + lnt - i) & 0177777) |dsenable ); + } /* end for */ + } /* end numeric */ +return; +} + +/* Add decimal string magnitudes + + Arguments: + s1 = source1 decimal string + s2 = source2 decimal string + ds = destination decimal string + cy = carry in + Output = 1 if carry, 0 if no carry + + This algorithm courtesy Anton Chernoff, circa 1992 or even earlier. + + We trace the history of a pair of adjacent digits to see how the + carry is fixed; each parenthesized item is a 4b digit. + + Assume we are adding: + + (a)(b) I + + (x)(y) J + + First compute I^J: + + (a^x)(b^y) TMP + + Note that the low bit of each digit is the same as the low bit of + the sum of the digits, ignoring the cary, since the low bit of the + sum is the xor of the bits. + + Now compute I+J+66 to get decimal addition with carry forced left + one digit: + + (a+x+6+carry mod 16)(b+y+6 mod 16) SUM + + Note that if there was a carry from b+y+6, then the low bit of the + left digit is different from the expected low bit from the xor. + If we xor this SUM into TMP, then the low bit of each digit is 1 + if there was a carry, and 0 if not. We need to subtract 6 from each + digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift + it right 4 to the digits that are affected, and subtract 6*adjustment + (actually, shift it right 3 and subtract 3*adjustment). +*/ + +int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy) +{ +int32 i; +uint32 sm1, sm2, tm1, tm2, tm3, tm4; + +for (i = 0; i < DSTRLNT; i++) { /* loop low to high */ + tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */ + sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */ + sm2 = sm1 + 0x66666666; /* force carry out */ + cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for overflow */ + tm2 = tm1 ^ sm2; /* get carry flags */ + tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */ + tm4 = 0x22222222 & ~tm3; /* clear where carry */ + ds->val[i] = sm2 - (3 * tm4); /* final result */ + } +return cy; +} + +/* Subtract decimal string magnitudes + + Arguments: + s1 = source1 decimal string + s2 = source2 decimal string + ds = destination decimal string + Outputs: s2 - s1 in ds + + Note: the routine assumes that s1 <= s2 + +*/ + +void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds) +{ +int32 i; +DSTR compl; + +for (i = 0; i < DSTRLNT; i++) compl.val[i] = 0x99999999 - s1->val[i]; +AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */ +return; +} + +/* Compare decimal string magnitudes + + Arguments: + s1 = source1 decimal string + s2 = source2 decimal string + Output = 1 if >, 0 if =, -1 if < +*/ + +int32 CmpDstr (DSTR *s1, DSTR *s2) +{ +int32 i; + +for (i = DSTRMAX; i >=0; i--) { + if (s1->val[i] > s2->val[i]) return 1; + if (s1->val[i] < s2->val[i]) return -1; + } +return 0; +} + +/* Test decimal string for zero + + Arguments: + dsrc = decimal string structure + + Returns the non-zero length of the string, in int32 units + If the string is zero, the sign is cleared +*/ + +int32 TestDstr (DSTR *dsrc) +{ +int32 i; + +for (i = DSTRMAX; i >= 0; i--) if (dsrc->val[i]) return (i + 1); +dsrc->sign = 0; +return 0; +} + +/* Get exact length of decimal string + + Arguments: + dsrc = decimal string structure + nz = result from TestDstr +*/ + +int32 LntDstr (DSTR *dsrc, int32 nz) +{ +int32 i; + +if (nz == 0) return 0; +for (i = 7; i > 0; i--) { + if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) break; + } +return ((nz - 1) * 8) + i; +} + +/* Create table of multiples + + Arguments: + dsrc = base decimal string structure + mtable[10] = array of decimal string structures + + Note that dsrc has a high order zero nibble; this + guarantees that the largest multiple won't overflow. + Also note that mtable[0] is not filled in. +*/ + +void CreateTable (DSTR *dsrc, DSTR mtable[10]) +{ +int32 (i); + +mtable[1] = *dsrc; +for (i = 2; i < 10; i++) AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0); +return; +} + +/* Word shift right + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +void WordRshift (DSTR *dsrc, int32 sc) +{ +int32 i; + +if (sc) { + for (i = 0; i < DSTRLNT; i++) { + if ((i + sc) < DSTRLNT) dsrc->val[i] = dsrc->val[i + sc]; + else dsrc->val[i] = 0; + } + } +return; +} + +/* Word shift left + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +int32 WordLshift (DSTR *dsrc, int32 sc) +{ +int32 i, c; + +c = 0; +if (sc) { + for (i = DSTRMAX; i >= 0; i--) { + if (i > (DSTRMAX - sc)) c = c | dsrc->val[i]; + if ((i - sc) >= 0) dsrc->val[i] = dsrc->val[i - sc]; + else dsrc->val[i] = 0; + } + } +return c; +} + +/* Nibble shift decimal string right + + Arguments: + dsrc = decimal string structure + sc = shift count + cin = carry in +*/ + +uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin) +{ +int32 i, s, rs, nc; + +if (s = sc * 4) { + rs = 32 - s; + for (i = DSTRMAX; i >= 0; i--) { + nc = dsrc->val[i]; + dsrc->val[i] = ((dsrc->val[i] >> s) | + (cin << rs)) & 0xFFFFFFFF; + cin = nc; + } + return cin; + } +return 0; +} + +/* Nibble shift decimal string left + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +uint32 NibbleLshift (DSTR *dsrc, int32 sc) +{ +int32 i, s, rs; +uint32 sv_val, cout; + +cout = 0; +if (s = sc * 4) { + rs = 32 - s; + for (i = 0; i < DSTRLNT; i++) { + sv_val = dsrc->val[i]; + dsrc->val[i] = ((dsrc->val[i] << s) | cout) & 0xFFFFFFFF; + cout = sv_val >> rs; + } + return cout; + } +return 0; +} + +/* Common setup routine for MOVC class instructions */ + +int32 movx_setup (int32 op, int32 *arg) +{ +int32 mvlnt, t; + +if (CPUT (CPUT_44)) { /* 11/44? */ + ReadMB (((SP - 0200) & 0177777) | dsenable); /* probe both blocks */ + ReadMB (((SP - 0100) & 0177777) | dsenable); /* in 64W stack area */ + } +if (op & INLINE) { /* inline */ + mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT; + WriteW (mvlnt, ((SP - 14) & 0177777) | dsenable); /* push move length */ + WriteW (R[0], ((SP - 12) & 0177777) | dsenable); /* push R0 - R5 */ + WriteW (R[1], ((SP - 10) & 0177777) | dsenable); + WriteW (R[2], ((SP - 8) & 0177777) | dsenable); + WriteW (R[3], ((SP - 6) & 0177777) | dsenable); + WriteW (R[4], ((SP - 4) & 0177777) | dsenable); + WriteW (R[5], ((SP - 2) & 0177777) | dsenable); + SP = (SP - 14) & 0177777; + R[0] = A1LNT; /* args to registers */ + R[1] = A1ADR; + R[2] = A2LNT; + R[3] = A2ADR; + R[4] = A3LNT; + R[5] = A3ADR & 0177777; + } +else { /* register */ + mvlnt = (R[0] < R[2])? R[0]: R[2]; + WriteW (mvlnt, ((SP - 2) & 0177777) | dsenable); /* push move length */ + SP = (SP - 2) & 0177777; + } +fpd = 1; +t = R[0] - R[2]; /* src.lnt - dst.lnt */ +N = GET_SIGN_W (t); /* set cc's from diff */ +Z = GET_Z (t); +V = GET_SIGN_W ((R[0] ^ R[2]) & (~R[2] ^ t)); +C = (R[0] < R[2]); +return mvlnt; +} + +/* Common cleanup routine for MOVC class instructions */ + +void movx_cleanup (int32 op) +{ +SP = (SP + 2) & 0177777; /* discard mvlnt */ +if (op & INLINE) { /* inline? */ + R[0] = ReadW (SP | dsenable); /* restore R0 - R5 */ + R[1] = ReadW (((SP + 2) & 0177777) | dsenable); + R[2] = ReadW (((SP + 4) & 0177777) | dsenable); + R[3] = ReadW (((SP + 6) & 0177777) | dsenable); + R[4] = ReadW (((SP + 8) & 0177777) | dsenable); + R[5] = ReadW (((SP + 10) & 0177777) | dsenable); + SP = (SP + 12) & 0177777; + } +else R[1] = R[2] = R[3] = 0; /* reg, clear R1 - R3 */ +fpd = 0; /* instr done */ +return; +} + +/* Test for CIS mid-instruction interrupt */ + +t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st) +{ +while (cycles >= 0) { /* until delay done */ + if (sim_interval > cycles) { /* event > delay */ + sim_interval = sim_interval - cycles; + break; + } + else { /* event <= delay */ + cycles = cycles - sim_interval; /* decr delay */ + sim_interval = 0; /* process event */ + *st = sim_process_event (); + trap_req = calc_ints (ipl, trap_req); /* recalc int req */ + if ((*st != SCPE_OK) || /* bad status or */ + trap_req & TRAP_INT) { /* interrupt? */ + PC = oldpc; /* back out */ + return TRUE; + } /* end if stop */ + } /* end else event */ + } /* end while delay */ +return FALSE; +} diff --git a/PDP11/pdp11_cpu.c b/PDP11/pdp11_cpu.c new file mode 100644 index 0000000..f977fb2 --- /dev/null +++ b/PDP11/pdp11_cpu.c @@ -0,0 +1,2993 @@ +/* pdp11_cpu.c: PDP-11 CPU simulator + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu PDP-11 CPU + + 22-Apr-08 RMS Fixed MMR0 treatment in RESET (found by Walter Mueller) + 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) + 28-Apr-07 RMS Removed clock initialization + 27-Oct-06 RMS Added idle support + 18-Oct-06 RMS Fixed bug in ASH -32 C value + 24-May-06 RMS Added instruction history + 03-May-06 RMS Fixed XOR operand fetch order for 11/70-style systems + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 19-May-05 RMS Replaced WAIT clock queue check with API call + 19-Jan-05 RMS Fixed bug(s) in RESET for 11/70 (reported by Tim Chapman) + 22-Dec-04 RMS Fixed WAIT to work in all modes (from John Dundas) + 02-Oct-04 RMS Added model emulation + 25-Jan-04 RMS Removed local debug logging support + 29-Dec-03 RMS Formalized 18b Qbus support + 21-Dec-03 RMS Added autoconfiguration controls + 05-Jun-03 RMS Fixed bugs in memory size table + 12-Mar-03 RMS Added logical name support + 01-Feb-03 RMS Changed R display to follow PSW, added SP display + 19-Jan-03 RMS Changed mode definitions for Apple Dev Kit conflict + 05-Jan-03 RMS Added memory size restore support + 17-Oct-02 RMS Fixed bug in examine/deposit (found by Hans Pufal) + 08-Oct-02 RMS Revised to build dib_tab dynamically + Added SHOW IOSPACE + 09-Sep-02 RMS Added KW11P support + 14-Jul-02 RMS Fixed bug in MMR0 error status load + 03-Jun-02 RMS Fixed relocation add overflow, added PS<15:12> = 1111 + special case logic to MFPI and removed it from MTPI + (found by John Dundas) + 29-Apr-02 RMS More fixes to DIV and ASH/ASHC (found by John Dundas) + 28-Apr-02 RMS Fixed bugs in illegal instruction 000010 and in + write-only memory pages (found by Wolfgang Helbig) + 21-Apr-02 RMS Fixed bugs in DIV by zero, DIV overflow, TSTSET, RTS, + ASHC -32, and red zone trap (found by John Dundas) + 04-Mar-02 RMS Changed double operand evaluation order for M+ + 23-Feb-02 RMS Fixed bug in MAINT, CPUERR, MEMERR read + 28-Jan-02 RMS Revised for multiple timers; fixed calc_MMR1 macros + 06-Jan-02 RMS Revised enable/disable support + 30-Dec-01 RMS Added old PC queue + 25-Dec-01 RMS Cleaned up sim_inst declarations + 11-Dec-01 RMS Moved interrupt debug code + 07-Dec-01 RMS Revised to use new breakpoint package + 08-Nov-01 RMS Moved I/O to external module + 26-Oct-01 RMS Revised to use symbolic definitions for IO page + 15-Oct-01 RMS Added debug logging + 08-Oct-01 RMS Fixed bug in revised interrupt logic + 07-Sep-01 RMS Revised device disable and interrupt mechanisms + 26-Aug-01 RMS Added DZ11 support + 10-Aug-01 RMS Removed register from declarations + 17-Jul-01 RMS Fixed warning from VC++ 6.0 + 01-Jun-01 RMS Added DZ11 interrupts + 23-Apr-01 RMS Added RK611 support + 05-Apr-01 RMS Added TS11/TSV05 support + 05-Mar-01 RMS Added clock calibration support + 11-Feb-01 RMS Added DECtape support + 25-Jan-01 RMS Fixed 4M memory definition (found by Eric Smith) + 14-Apr-99 RMS Changed t_addr to unsigned + 18-Aug-98 RMS Added CIS support + 09-May-98 RMS Fixed bug in DIV overflow test + 19-Jan-97 RMS Added RP/RM support + 06-Apr-96 RMS Added dynamic memory sizing + 29-Feb-96 RMS Added TM11 support + 17-Jul-94 RMS Corrected updating of MMR1 if MMR0 locked + + The register state for the PDP-11 is: + + REGFILE[0:5][0] general register set + REGFILE[0:5][1] alternate general register set + STACKFILE[4] stack pointers for kernel, supervisor, unused, user + PC program counter + PSW processor status word + <15:14> = CM current processor mode + <13:12> = PM previous processor mode + <11> = RS register set select + <8> = FPD first part done (CIS) + <7:5> = IPL interrupt priority level + <4> = TBIT trace trap enable + <3:0> = NZVC condition codes + FR[0:5] floating point accumulators + FPS floating point status register + FEC floating exception code + FEA floating exception address + MMR0,1,2,3 memory management control registers + APRFILE[0:63] memory management relocation registers for + kernel, supervisor, unused, user + <31:16> = PAR processor address registers + <15:0> = PDR processor data registers + PIRQ processor interrupt request register + CPUERR CPU error register + MEMERR memory system error register + CCR cache control register + MAINT maintenance register + HITMISS cache status register + SR switch register + DR display register + + The PDP-11 has many instruction formats: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ double operand + | opcode | source spec | dest spec | 010000:067777 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 110000:167777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand + | opcode | src reg| dest spec | 004000:004777 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 070000:077777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single operand + | opcode | dest spec | 000100:000177 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000300:000377 + 005000:007777 + 105000:107777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ single register + | opcode |dest reg| 000200:000207 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 000230:000237 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand + | opcode | 000000:000007 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ branch + | opcode | branch displacement | 000400:003477 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 100000:103477 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ EMT/TRAP + | opcode | trap code | 104000:104777 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ cond code operator + | opcode | immediate | 000240:000277 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + An operand specifier consists of an addressing mode and a register. + The addressing modes are: + + 0 register direct R op = R + 1 register deferred (R) op = M[R] + 2 autoincrement (R)+ op = M[R]; R = R + length + 3 autoincrement deferred @(R)+ op = M[M[R]]; R = R + 2 + 4 autodecrement -(R) R = R - length; op = M[R] + 5 autodecrement deferred @-(R) R = R - 2; op = M[M[R]] + 6 displacement d(R) op = M[R + disp] + 7 displacement deferred @d(R) op = M[M[R + disp]] + + There are eight general registers, R0-R7. R6 is the stack pointer, + R7 the PC. The combination of addressing modes with R7 yields: + + 27 immediate #n op = M[PC]; PC = PC + 2 + 37 absolute @#n op = M[M[PC]]; PC = PC + 2 + 67 relative d(PC) op = M[PC + disp] + 77 relative deferred @d(PC) op = M[M[PC + disp]] + + This routine is the instruction decode routine for the PDP-11. It + is called from the simulator control program to execute instructions + in simulated memory, starting at the simulated PC. It runs until an + enabled exception is encountered. + + General notes: + + 1. Virtual address format. PDP-11 memory management uses the 16b + virtual address, the type of reference (instruction or data), and + the current mode, to construct the 22b physical address. To + package this conveniently, the simulator uses a 19b pseudo virtual + address, consisting of the 16b virtual address prefixed with the + current mode and ispace/dspace indicator. These are precalculated + as isenable and dsenable for ispace and dspace, respectively, and + must be recalculated whenever MMR0, MMR3, or PSW changes. + + 2. Traps and interrupts. Variable trap_req bit-encodes all possible + traps. In addition, an interrupt pending bit is encoded as the + lowest priority trap. Traps are processed by trap_vec and trap_clear, + which provide the vector and subordinate traps to clear, respectively. + + Array int_req[0:7] bit encodes all possible interrupts. It is masked + under the interrupt priority level, ipl. If any interrupt request + is not masked, the interrupt bit is set in trap_req. While most + interrupts are handled centrally, a device can supply an interrupt + acknowledge routine. + + 3. PSW handling. The PSW is kept as components, for easier access. + Because the PSW can be explicitly written as address 17777776, + all instructions must update PSW before executing their last write. + + 4. Adding I/O devices. These modules must be modified: + + pdp11_defs.h add device address and interrupt definitions + pdp11_sys.c add to sim_devices table entry +*/ + +/* Definitions */ + +#include "pdp11_defs.h" +#include "pdp11_cpumod.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC +#define calc_is(md) ((md) << VA_V_MODE) +#define calc_ds(md) (calc_is((md)) | ((MMR3 & dsmask[(md)])? VA_DS: 0)) +#define calc_MMR1(val) ((MMR1)? (((val) << 8) | MMR1): (val)) +#define GET_SIGN_W(v) (((v) >> 15) & 1) +#define GET_SIGN_B(v) (((v) >> 7) & 1) +#define GET_Z(v) ((v) == 0) +#define JMP_PC(x) PCQ_ENTRY; PC = (x) +#define BRANCH_F(x) PCQ_ENTRY; PC = (PC + (((x) + (x)) & 0377)) & 0177777 +#define BRANCH_B(x) PCQ_ENTRY; PC = (PC + (((x) + (x)) | 0177400)) & 0177777 +#define last_pa (cpu_unit.u4) /* auto save/rest */ +#define UNIT_V_MSIZE (UNIT_V_UF + 0) /* dummy */ +#define UNIT_MSIZE (1u << UNIT_V_MSIZE) + +#define HIST_MIN 64 +#define HIST_MAX (1u << 18) +#define HIST_VLD 1 /* make PC odd */ +#define HIST_ILNT 4 /* max inst length */ + +typedef struct { + uint16 pc; + uint16 psw; + uint16 src; + uint16 dst; + uint16 inst[HIST_ILNT]; + } InstHistory; + +/* Global state */ + +extern FILE *sim_log; + +uint16 *M = NULL; /* memory */ +int32 REGFILE[6][2] = { 0 }; /* R0-R5, two sets */ +int32 STACKFILE[4] = { 0 }; /* SP, four modes */ +int32 saved_PC = 0; /* program counter */ +int32 R[8] = { 0 }; /* working registers */ +int32 PSW = 0; /* PSW */ + int32 cm = 0; /* current mode */ + int32 pm = 0; /* previous mode */ + int32 rs = 0; /* register set */ + int32 fpd = 0; /* first part done */ + int32 ipl = 0; /* int pri level */ + int32 tbit = 0; /* trace flag */ + int32 N = 0, Z = 0, V = 0, C = 0; /* condition codes */ +int32 wait_state = 0; /* wait state */ +int32 trap_req = 0; /* trap requests */ +int32 int_req[IPL_HLVL] = { 0 }; /* interrupt requests */ +int32 PIRQ = 0; /* programmed int req */ +int32 STKLIM = 0; /* stack limit */ +fpac_t FR[6] = { 0 }; /* fp accumulators */ +int32 FPS = 0; /* fp status */ +int32 FEC = 0; /* fp exception code */ +int32 FEA = 0; /* fp exception addr */ +int32 APRFILE[64] = { 0 }; /* PARs/PDRs */ +int32 MMR0 = 0; /* MMR0 - status */ +int32 MMR1 = 0; /* MMR1 - R+/-R */ +int32 MMR2 = 0; /* MMR2 - saved PC */ +int32 MMR3 = 0; /* MMR3 - 22b status */ +int32 cpu_bme = 0; /* bus map enable */ +int32 cpu_astop = 0; /* address stop */ +int32 isenable = 0, dsenable = 0; /* i, d space flags */ +int32 stop_trap = 1; /* stop on trap */ +int32 stop_vecabort = 1; /* stop on vec abort */ +int32 stop_spabort = 1; /* stop on SP abort */ +int32 wait_enable = 0; /* wait state enable */ +int32 autcon_enb = 1; /* autoconfig enable */ +uint32 cpu_model = MOD_1173; /* CPU model */ +uint32 cpu_type = 1u << MOD_1173; /* model as bit mask */ +uint32 cpu_opt = SOP_1173; /* CPU options */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +jmp_buf save_env; /* abort handler */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ +int32 dsmask[4] = { MMR3_KDS, MMR3_SDS, 0, MMR3_UDS }; /* dspace enables */ +t_addr cpu_memsize = INIMEMSIZE; /* last mem addr */ + +extern int32 CPUERR, MAINT; +extern int32 sim_interval; +extern int32 sim_int_char; +extern uint32 sim_switches; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern t_bool sim_idle_enab; +extern DEVICE *sim_devices[]; +extern CPUTAB cpu_tab[]; + +/* Function declarations */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_show_virt (FILE *st, UNIT *uptr, int32 val, void *desc); +int32 GeteaB (int32 spec); +int32 GeteaW (int32 spec); +int32 relocR (int32 addr); +int32 relocW (int32 addr); +void relocR_test (int32 va, int32 apridx); +void relocW_test (int32 va, int32 apridx); +t_bool PLF_test (int32 va, int32 apr); +void reloc_abort (int32 err, int32 apridx); +int32 ReadE (int32 addr); +int32 ReadW (int32 addr); +int32 ReadB (int32 addr); +int32 ReadMW (int32 addr); +int32 ReadMB (int32 addr); +void WriteW (int32 data, int32 addr); +void WriteB (int32 data, int32 addr); +void PWriteW (int32 data, int32 addr); +void PWriteB (int32 data, int32 addr); +void set_r_display (int32 rs, int32 cm); +t_stat CPU_wr (int32 data, int32 addr, int32 access); +void set_stack_trap (int32 adr); +int32 get_PSW (void); +void put_PSW (int32 val, t_bool prot); +void put_PIRQ (int32 val); + +extern void fp11 (int32 IR); +extern t_stat cis11 (int32 IR); +extern t_stat fis11 (int32 IR); +extern t_stat build_dib_tab (void); +extern t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); +extern t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc); +extern t_stat iopageR (int32 *data, uint32 addr, int32 access); +extern t_stat iopageW (int32 data, uint32 addr, int32 access); +extern int32 calc_ints (int32 nipl, int32 trq); +extern int32 get_vector (int32 nipl); + +/* Trap data structures */ + +int32 trap_vec[TRAP_V_MAX] = { /* trap req to vector */ + VEC_RED, VEC_ODD, VEC_MME, VEC_NXM, + VEC_PAR, VEC_PRV, VEC_ILL, VEC_BPT, + VEC_IOT, VEC_EMT, VEC_TRAP, VEC_TRC, + VEC_YEL, VEC_PWRFL, VEC_FPE + }; + +int32 trap_clear[TRAP_V_MAX] = { /* trap clears */ + TRAP_RED+TRAP_PAR+TRAP_YEL+TRAP_TRC+TRAP_ODD+TRAP_NXM, + TRAP_ODD+TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_MME+TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_NXM+TRAP_PAR+TRAP_YEL+TRAP_TRC, + TRAP_PAR+TRAP_TRC, TRAP_PRV+TRAP_TRC, + TRAP_ILL+TRAP_TRC, TRAP_BPT+TRAP_TRC, + TRAP_IOT+TRAP_TRC, TRAP_EMT+TRAP_TRC, + TRAP_TRAP+TRAP_TRC, TRAP_TRC, + TRAP_YEL, TRAP_PWRFL, TRAP_FPE + }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX|UNIT_BINK, INIMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 16) }, + { ORDATA (R0, REGFILE[0][0], 16) }, + { ORDATA (R1, REGFILE[1][0], 16) }, + { ORDATA (R2, REGFILE[2][0], 16) }, + { ORDATA (R3, REGFILE[3][0], 16) }, + { ORDATA (R4, REGFILE[4][0], 16) }, + { ORDATA (R5, REGFILE[5][0], 16) }, + { ORDATA (SP, STACKFILE[MD_KER], 16) }, + { ORDATA (R00, REGFILE[0][0], 16) }, + { ORDATA (R01, REGFILE[1][0], 16) }, + { ORDATA (R02, REGFILE[2][0], 16) }, + { ORDATA (R03, REGFILE[3][0], 16) }, + { ORDATA (R04, REGFILE[4][0], 16) }, + { ORDATA (R05, REGFILE[5][0], 16) }, + { ORDATA (R10, REGFILE[0][1], 16) }, + { ORDATA (R11, REGFILE[1][1], 16) }, + { ORDATA (R12, REGFILE[2][1], 16) }, + { ORDATA (R13, REGFILE[3][1], 16) }, + { ORDATA (R14, REGFILE[4][1], 16) }, + { ORDATA (R15, REGFILE[5][1], 16) }, + { ORDATA (KSP, STACKFILE[MD_KER], 16) }, + { ORDATA (SSP, STACKFILE[MD_SUP], 16) }, + { ORDATA (USP, STACKFILE[MD_USR], 16) }, + { ORDATA (PSW, PSW, 16) }, + { GRDATA (CM, PSW, 8, 2, PSW_V_CM) }, + { GRDATA (PM, PSW, 8, 2, PSW_V_PM) }, + { FLDATA (RS, PSW, PSW_V_RS) }, + { FLDATA (FPD, PSW, PSW_V_FPD) }, + { GRDATA (IPL, PSW, 8, 3, PSW_V_IPL) }, + { FLDATA (T, PSW, PSW_V_TBIT) }, + { FLDATA (N, PSW, PSW_V_N) }, + { FLDATA (Z, PSW, PSW_V_Z) }, + { FLDATA (V, PSW, PSW_V_V) }, + { FLDATA (C, PSW, PSW_V_C) }, + { ORDATA (PIRQ, PIRQ, 16) }, + { ORDATA (STKLIM, STKLIM, 16) }, + { ORDATA (FAC0H, FR[0].h, 32) }, + { ORDATA (FAC0L, FR[0].l, 32) }, + { ORDATA (FAC1H, FR[1].h, 32) }, + { ORDATA (FAC1L, FR[1].l, 32) }, + { ORDATA (FAC2H, FR[2].h, 32) }, + { ORDATA (FAC2L, FR[2].l, 32) }, + { ORDATA (FAC3H, FR[3].h, 32) }, + { ORDATA (FAC3L, FR[3].l, 32) }, + { ORDATA (FAC4H, FR[4].h, 32) }, + { ORDATA (FAC4L, FR[4].l, 32) }, + { ORDATA (FAC5H, FR[5].h, 32) }, + { ORDATA (FAC5L, FR[5].l, 32) }, + { ORDATA (FPS, FPS, 16) }, + { ORDATA (FEA, FEA, 16) }, + { ORDATA (FEC, FEC, 4) }, + { ORDATA (MMR0, MMR0, 16) }, + { ORDATA (MMR1, MMR1, 16) }, + { ORDATA (MMR2, MMR2, 16) }, + { ORDATA (MMR3, MMR3, 16) }, + { GRDATA (KIPAR0, APRFILE[000], 8, 16, 16) }, + { GRDATA (KIPDR0, APRFILE[000], 8, 16, 0) }, + { GRDATA (KIPAR1, APRFILE[001], 8, 16, 16) }, + { GRDATA (KIPDR1, APRFILE[001], 8, 16, 0) }, + { GRDATA (KIPAR2, APRFILE[002], 8, 16, 16) }, + { GRDATA (KIPDR2, APRFILE[002], 8, 16, 0) }, + { GRDATA (KIPAR3, APRFILE[003], 8, 16, 16) }, + { GRDATA (KIPDR3, APRFILE[003], 8, 16, 0) }, + { GRDATA (KIPAR4, APRFILE[004], 8, 16, 16) }, + { GRDATA (KIPDR4, APRFILE[004], 8, 16, 0) }, + { GRDATA (KIPAR5, APRFILE[005], 8, 16, 16) }, + { GRDATA (KIPDR5, APRFILE[005], 8, 16, 0) }, + { GRDATA (KIPAR6, APRFILE[006], 8, 16, 16) }, + { GRDATA (KIPDR6, APRFILE[006], 8, 16, 0) }, + { GRDATA (KIPAR7, APRFILE[007], 8, 16, 16) }, + { GRDATA (KIPDR7, APRFILE[007], 8, 16, 0) }, + { GRDATA (KDPAR0, APRFILE[010], 8, 16, 16) }, + { GRDATA (KDPDR0, APRFILE[010], 8, 16, 0) }, + { GRDATA (KDPAR1, APRFILE[011], 8, 16, 16) }, + { GRDATA (KDPDR1, APRFILE[011], 8, 16, 0) }, + { GRDATA (KDPAR2, APRFILE[012], 8, 16, 16) }, + { GRDATA (KDPDR2, APRFILE[012], 8, 16, 0) }, + { GRDATA (KDPAR3, APRFILE[013], 8, 16, 16) }, + { GRDATA (KDPDR3, APRFILE[013], 8, 16, 0) }, + { GRDATA (KDPAR4, APRFILE[014], 8, 16, 16) }, + { GRDATA (KDPDR4, APRFILE[014], 8, 16, 0) }, + { GRDATA (KDPAR5, APRFILE[015], 8, 16, 16) }, + { GRDATA (KDPDR5, APRFILE[015], 8, 16, 0) }, + { GRDATA (KDPAR6, APRFILE[016], 8, 16, 16) }, + { GRDATA (KDPDR6, APRFILE[016], 8, 16, 0) }, + { GRDATA (KDPAR7, APRFILE[017], 8, 16, 16) }, + { GRDATA (KDPDR7, APRFILE[017], 8, 16, 0) }, + { GRDATA (SIPAR0, APRFILE[020], 8, 16, 16) }, + { GRDATA (SIPDR0, APRFILE[020], 8, 16, 0) }, + { GRDATA (SIPAR1, APRFILE[021], 8, 16, 16) }, + { GRDATA (SIPDR1, APRFILE[021], 8, 16, 0) }, + { GRDATA (SIPAR2, APRFILE[022], 8, 16, 16) }, + { GRDATA (SIPDR2, APRFILE[022], 8, 16, 0) }, + { GRDATA (SIPAR3, APRFILE[023], 8, 16, 16) }, + { GRDATA (SIPDR3, APRFILE[023], 8, 16, 0) }, + { GRDATA (SIPAR4, APRFILE[024], 8, 16, 16) }, + { GRDATA (SIPDR4, APRFILE[024], 8, 16, 0) }, + { GRDATA (SIPAR5, APRFILE[025], 8, 16, 16) }, + { GRDATA (SIPDR5, APRFILE[025], 8, 16, 0) }, + { GRDATA (SIPAR6, APRFILE[026], 8, 16, 16) }, + { GRDATA (SIPDR6, APRFILE[026], 8, 16, 0) }, + { GRDATA (SIPAR7, APRFILE[027], 8, 16, 16) }, + { GRDATA (SIPDR7, APRFILE[027], 8, 16, 0) }, + { GRDATA (SDPAR0, APRFILE[030], 8, 16, 16) }, + { GRDATA (SDPDR0, APRFILE[030], 8, 16, 0) }, + { GRDATA (SDPAR1, APRFILE[031], 8, 16, 16) }, + { GRDATA (SDPDR1, APRFILE[031], 8, 16, 0) }, + { GRDATA (SDPAR2, APRFILE[032], 8, 16, 16) }, + { GRDATA (SDPDR2, APRFILE[032], 8, 16, 0) }, + { GRDATA (SDPAR3, APRFILE[033], 8, 16, 16) }, + { GRDATA (SDPDR3, APRFILE[033], 8, 16, 0) }, + { GRDATA (SDPAR4, APRFILE[034], 8, 16, 16) }, + { GRDATA (SDPDR4, APRFILE[034], 8, 16, 0) }, + { GRDATA (SDPAR5, APRFILE[035], 8, 16, 16) }, + { GRDATA (SDPDR5, APRFILE[035], 8, 16, 0) }, + { GRDATA (SDPAR6, APRFILE[036], 8, 16, 16) }, + { GRDATA (SDPDR6, APRFILE[036], 8, 16, 0) }, + { GRDATA (SDPAR7, APRFILE[037], 8, 16, 16) }, + { GRDATA (SDPDR7, APRFILE[037], 8, 16, 0) }, + { GRDATA (UIPAR0, APRFILE[060], 8, 16, 16) }, + { GRDATA (UIPDR0, APRFILE[060], 8, 16, 0) }, + { GRDATA (UIPAR1, APRFILE[061], 8, 16, 16) }, + { GRDATA (UIPDR1, APRFILE[061], 8, 16, 0) }, + { GRDATA (UIPAR2, APRFILE[062], 8, 16, 16) }, + { GRDATA (UIPDR2, APRFILE[062], 8, 16, 0) }, + { GRDATA (UIPAR3, APRFILE[063], 8, 16, 16) }, + { GRDATA (UIPDR3, APRFILE[063], 8, 16, 0) }, + { GRDATA (UIPAR4, APRFILE[064], 8, 16, 16) }, + { GRDATA (UIPDR4, APRFILE[064], 8, 16, 0) }, + { GRDATA (UIPAR5, APRFILE[065], 8, 16, 16) }, + { GRDATA (UIPDR5, APRFILE[065], 8, 16, 0) }, + { GRDATA (UIPAR6, APRFILE[066], 8, 16, 16) }, + { GRDATA (UIPDR6, APRFILE[066], 8, 16, 0) }, + { GRDATA (UIPAR7, APRFILE[067], 8, 16, 16) }, + { GRDATA (UIPDR7, APRFILE[067], 8, 16, 0) }, + { GRDATA (UDPAR0, APRFILE[070], 8, 16, 16) }, + { GRDATA (UDPDR0, APRFILE[070], 8, 16, 0) }, + { GRDATA (UDPAR1, APRFILE[071], 8, 16, 16) }, + { GRDATA (UDPDR1, APRFILE[071], 8, 16, 0) }, + { GRDATA (UDPAR2, APRFILE[072], 8, 16, 16) }, + { GRDATA (UDPDR2, APRFILE[072], 8, 16, 0) }, + { GRDATA (UDPAR3, APRFILE[073], 8, 16, 16) }, + { GRDATA (UDPDR3, APRFILE[073], 8, 16, 0) }, + { GRDATA (UDPAR4, APRFILE[074], 8, 16, 16) }, + { GRDATA (UDPDR4, APRFILE[074], 8, 16, 0) }, + { GRDATA (UDPAR5, APRFILE[075], 8, 16, 16) }, + { GRDATA (UDPDR5, APRFILE[075], 8, 16, 0) }, + { GRDATA (UDPAR6, APRFILE[076], 8, 16, 16) }, + { GRDATA (UDPDR6, APRFILE[076], 8, 16, 0) }, + { GRDATA (UDPAR7, APRFILE[077], 8, 16, 16) }, + { GRDATA (UDPDR7, APRFILE[077], 8, 16, 0) }, + { BRDATA (IREQ, int_req, 8, 32, IPL_HLVL), REG_RO }, + { ORDATA (TRAPS, trap_req, TRAP_V_MAX) }, + { FLDATA (WAIT, wait_state, 0) }, + { FLDATA (WAIT_ENABLE, wait_enable, 0), REG_HIDDEN }, + { ORDATA (STOP_TRAPS, stop_trap, TRAP_V_MAX) }, + { FLDATA (STOP_VECA, stop_vecabort, 0) }, + { FLDATA (STOP_SPA, stop_spabort, 0) }, + { FLDATA (AUTOCON, autcon_enb, 0), REG_HRO }, + { BRDATA (PCQ, pcq, 8, 16, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { ORDATA (WRU, sim_int_char, 8) }, + { ORDATA (MODEL, cpu_model, 16), REG_HRO }, + { ORDATA (OPTIONS, cpu_opt, 32), REG_HRO }, + { NULL} + }; + +MTAB cpu_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "TYPE", NULL, + NULL, &cpu_show_model }, + { MTAB_XTD|MTAB_VDV, MOD_1103, NULL, "11/03", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1104, NULL, "11/04", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1105, NULL, "11/05", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1120, NULL, "11/20", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1123, NULL, "11/23", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1123P, NULL, "11/23+", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1124, NULL, "11/24", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1134, NULL, "11/34", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1140, NULL, "11/40", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1144, NULL, "11/44", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1145, NULL, "11/45", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1153, NULL, "11/53", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1160, NULL, "11/60", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1170, NULL, "11/70", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1173, NULL, "11/73", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1173B, NULL, "11/73B", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1183, NULL, "11/83", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1184, NULL, "11/84", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1193, NULL, "11/93", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1194, NULL, "11/94", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1173, NULL, "Q22", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1184, NULL, "URH11", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1170, NULL, "URH70", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, MOD_1145, NULL, "U18", &cpu_set_model }, + { MTAB_XTD|MTAB_VDV, OPT_EIS, NULL, "EIS", &cpu_set_opt }, + { MTAB_XTD|MTAB_VDV, OPT_EIS, NULL, "NOEIS", &cpu_clr_opt }, + { MTAB_XTD|MTAB_VDV, OPT_FIS, NULL, "FIS", &cpu_set_opt }, + { MTAB_XTD|MTAB_VDV, OPT_FIS, NULL, "NOFIS", &cpu_clr_opt }, + { MTAB_XTD|MTAB_VDV, OPT_FPP, NULL, "FPP", &cpu_set_opt }, + { MTAB_XTD|MTAB_VDV, OPT_FPP, NULL, "NOFPP", &cpu_clr_opt }, + { MTAB_XTD|MTAB_VDV, OPT_CIS, NULL, "CIS", &cpu_set_opt }, + { MTAB_XTD|MTAB_VDV, OPT_CIS, NULL, "NOCIS", &cpu_clr_opt }, + { MTAB_XTD|MTAB_VDV, OPT_MMU, NULL, "MMU", &cpu_set_opt }, + { MTAB_XTD|MTAB_VDV, OPT_MMU, NULL, "NOMMU", &cpu_clr_opt }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size}, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size}, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size}, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size}, + { UNIT_MSIZE, 98304, NULL, "96K", &cpu_set_size}, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size}, + { UNIT_MSIZE, 196608, NULL, "192K", &cpu_set_size}, + { UNIT_MSIZE, 262144, NULL, "256K", &cpu_set_size}, + { UNIT_MSIZE, 393216, NULL, "384K", &cpu_set_size}, + { UNIT_MSIZE, 524288, NULL, "512K", &cpu_set_size}, + { UNIT_MSIZE, 786432, NULL, "768K", &cpu_set_size}, + { UNIT_MSIZE, 1048576, NULL, "1024K", &cpu_set_size}, + { UNIT_MSIZE, 2097152, NULL, "2048K", &cpu_set_size}, + { UNIT_MSIZE, 3145728, NULL, "3072K", &cpu_set_size}, + { UNIT_MSIZE, 4186112, NULL, "4096K", &cpu_set_size}, + { UNIT_MSIZE, 1048576, NULL, "1M", &cpu_set_size}, + { UNIT_MSIZE, 2097152, NULL, "2M", &cpu_set_size}, + { UNIT_MSIZE, 3145728, NULL, "3M", &cpu_set_size}, + { UNIT_MSIZE, 4186112, NULL, "4M", &cpu_set_size}, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, + NULL, &show_iospace }, + { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG", + &set_autocon, &show_autocon }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG", + &set_autocon, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, + NULL, &cpu_show_virt }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 22, 2, 8, 16, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, DEV_DYNM, 0, + NULL, &cpu_set_size, NULL + }; + +t_stat sim_instr (void) +{ +int abortval, i; +volatile int32 trapea; /* used by setjmp */ +t_stat reason; + +/* Restore register state + + 1. PSW components + 2. Active register file based on PSW + 3. Active stack pointer based on PSW + 4. Memory management control flags + 5. Interrupt system +*/ + +reason = build_dib_tab (); /* build, chk dib_tab */ +if (reason != SCPE_OK) return reason; +if (MEMSIZE < cpu_tab[cpu_model].maxm) /* mem size < max? */ + cpu_memsize = MEMSIZE; /* then okay */ +else cpu_memsize = cpu_tab[cpu_model].maxm - IOPAGESIZE;/* max - io page */ +cpu_type = 1u << cpu_model; /* reset type mask */ +cpu_bme = (MMR3 & MMR3_BME) && (cpu_opt & OPT_UBM); /* map enabled? */ +PC = saved_PC; +put_PSW (PSW, 0); /* set PSW, call calc_xs */ +for (i = 0; i < 6; i++) R[i] = REGFILE[i][rs]; +SP = STACKFILE[cm]; +isenable = calc_is (cm); +dsenable = calc_ds (cm); +put_PIRQ (PIRQ); /* rewrite PIRQ */ +STKLIM = STKLIM & STKLIM_RW; /* clean up STKLIM */ +MMR0 = MMR0 | MMR0_IC; /* usually on */ + +trap_req = calc_ints (ipl, trap_req); /* upd int req */ +trapea = 0; +reason = 0; + +/* Abort handling + + If an abort occurs in memory management or memory access, the lower + level routine executes a longjmp to this area OUTSIDE the main + simulation loop. The longjmp specifies a trap mask which is OR'd + into the trap_req register. Simulation then resumes at the fetch + phase, and the trap is sprung. + + Aborts which occur within a trap sequence (trapea != 0) require + special handling. If the abort occured on the stack pushes, and + the mode (encoded in trapea) is kernel, an "emergency" kernel + stack is created at 4, and a red zone stack trap taken. + + All variables used in setjmp processing, or assumed to be valid + after setjmp, must be volatile or global. +*/ + +abortval = setjmp (save_env); /* set abort hdlr */ +if (abortval != 0) { + trap_req = trap_req | abortval; /* or in trap flag */ + if ((trapea > 0) && stop_vecabort) reason = STOP_VECABORT; + if ((trapea < 0) && /* stack push abort? */ + (CPUT (STOP_STKA) || stop_spabort)) reason = STOP_SPABORT; + if (trapea == ~MD_KER) { /* kernel stk abort? */ + setTRAP (TRAP_RED); + setCPUERR (CPUE_RED); + STACKFILE[MD_KER] = 4; + if (cm == MD_KER) SP = 4; + } + } + +/* Main instruction fetch/decode loop + + Check for traps or interrupts. If trap, locate the vector and check + for stop condition. If interrupt, locate the vector. +*/ + +while (reason == 0) { + + int32 IR, srcspec, srcreg, dstspec, dstreg; + int32 src, src2, dst, ea; + int32 i, t, sign, oldrs, trapnum; + + if (cpu_astop) { + cpu_astop = 0; + reason = SCPE_STOP; + break; + } + + if (sim_interval <= 0) { /* intv cnt expired? */ + reason = sim_process_event (); /* process events */ + trap_req = calc_ints (ipl, trap_req); /* recalc int req */ + continue; + } /* end if sim_interval */ + + if (trap_req) { /* check traps, ints */ + trapea = 0; /* assume srch fails */ + if (t = trap_req & TRAP_ALL) { /* if a trap */ + for (trapnum = 0; trapnum < TRAP_V_MAX; trapnum++) { + if ((t >> trapnum) & 1) { /* trap set? */ + trapea = trap_vec[trapnum]; /* get vec, clr */ + trap_req = trap_req & ~trap_clear[trapnum]; + if ((stop_trap >> trapnum) & 1) /* stop on trap? */ + reason = trapnum + 1; + break; + } /* end if t & 1 */ + } /* end for */ + } /* end if t */ + else { + trapea = get_vector (ipl); /* get int vector */ + trapnum = TRAP_V_MAX; /* defang stk trap */ + } /* end else t */ + if (trapea == 0) { /* nothing to do? */ + trap_req = calc_ints (ipl, 0); /* recalculate */ + continue; /* back to fetch */ + } /* end if trapea */ + +/* Process a trap or interrupt + + 1. Exit wait state + 2. Save the current SP and PSW + 3. Read the new PC, new PSW from trapea, kernel data space + 4. Get the mode and stack selected by the new PSW + 5. Push the old PC and PSW on the new stack + 6. Update SP, PSW, and PC + 7. If not stack overflow, check for stack overflow +*/ + + wait_state = 0; /* exit wait state */ + STACKFILE[cm] = SP; + PSW = get_PSW (); /* assemble PSW */ + oldrs = rs; + if (CPUT (HAS_MMTR)) { /* 45,70? */ + if (update_MM) MMR2 = trapea; /* save vector */ + MMR0 = MMR0 & ~MMR0_IC; /* clear IC */ + } + src = ReadW (trapea | calc_ds (MD_KER)); /* new PC */ + src2 = ReadW ((trapea + 2) | calc_ds (MD_KER)); /* new PSW */ + t = (src2 >> PSW_V_CM) & 03; /* new cm */ + trapea = ~t; /* flag pushes */ + WriteW (PSW, ((STACKFILE[t] - 2) & 0177777) | calc_ds (t)); + WriteW (PC, ((STACKFILE[t] - 4) & 0177777) | calc_ds (t)); + trapea = 0; /* clear trap flag */ + src2 = (src2 & ~PSW_PM) | (cm << PSW_V_PM); /* insert prv mode */ + put_PSW (src2, 0); /* call calc_is,ds */ + if (rs != oldrs) { /* if rs chg, swap */ + for (i = 0; i < 6; i++) { + REGFILE[i][oldrs] = R[i]; + R[i] = REGFILE[i][rs]; + } + } + SP = (STACKFILE[cm] - 4) & 0177777; /* update SP, PC */ + isenable = calc_is (cm); + dsenable = calc_ds (cm); + trap_req = calc_ints (ipl, trap_req); + JMP_PC (src); + if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y)) && + (trapnum != TRAP_V_RED) && (trapnum != TRAP_V_YEL)) + set_stack_trap (SP); + MMR0 = MMR0 | MMR0_IC; /* back to instr */ + continue; /* end if traps */ + } + +/* Fetch and decode next instruction */ + + if (tbit) setTRAP (TRAP_TRC); + if (wait_state) { /* wait state? */ + if (sim_idle_enab) /* idle enabled? */ + sim_idle (TMR_CLK, TRUE); + else if (wait_enable) /* old style idle? */ + sim_interval = 0; /* force check */ + else sim_interval = sim_interval - 1; /* count cycle */ + continue; + } + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + continue; + } + + if (update_MM) { /* if mm not frozen */ + MMR1 = 0; + MMR2 = PC; + } + IR = ReadE (PC | isenable); /* fetch instruction */ + sim_interval = sim_interval - 1; + srcspec = (IR >> 6) & 077; /* src, dst specs */ + dstspec = IR & 077; + srcreg = (srcspec <= 07); /* src, dst = rmode? */ + dstreg = (dstspec <= 07); + if (hst_lnt) { /* record history? */ + t_value val; + uint32 i; + hst[hst_p].pc = PC | HIST_VLD; + hst[hst_p].psw = get_PSW (); + hst[hst_p].src = R[srcspec & 07]; + hst[hst_p].dst = R[dstspec & 07]; + hst[hst_p].inst[0] = IR; + for (i = 1; i < HIST_ILNT; i++) { + if (cpu_ex (&val, (PC + (i << 1)) & 0177777, &cpu_unit, SWMASK ('V'))) + hst[hst_p].inst[i] = 0; + else hst[hst_p].inst[i] = (uint16) val; + } + hst_p = (hst_p + 1); + if (hst_p >= hst_lnt) hst_p = 0; + } + PC = (PC + 2) & 0177777; /* incr PC, mod 65k */ + switch ((IR >> 12) & 017) { /* decode IR<15:12> */ + +/* Opcode 0: no operands, specials, branches, JSR, SOPs */ + + case 000: + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + case 000: /* no operand */ + if (IR >= 000010) { /* 000010 - 000077 */ + setTRAP (TRAP_ILL); /* illegal */ + break; + } + switch (IR) { /* decode IR<2:0> */ + case 0: /* HALT */ + if ((cm == MD_KER) && + (!CPUT (CPUT_J) || ((MAINT & MAINT_HTRAP) == 0))) + reason = STOP_HALT; + else if (CPUT (HAS_HALT4)) { /* priv trap? */ + setTRAP (TRAP_PRV); + setCPUERR (CPUE_HALT); + } + else setTRAP (TRAP_ILL); /* no, ill inst */ + break; + case 1: /* WAIT */ + wait_state = 1; + break; + case 3: /* BPT */ + setTRAP (TRAP_BPT); + break; + case 4: /* IOT */ + setTRAP (TRAP_IOT); + break; + case 5: /* RESET */ + if (cm == MD_KER) { + reset_all (2); /* skip CPU, sys reg */ + PIRQ = 0; /* clear PIRQ */ + STKLIM = 0; /* clear STKLIM */ + MMR0 = 0; /* clear MMR0 */ + MMR3 = 0; /* clear MMR3 */ + for (i = 0; i < IPL_HLVL; i++) int_req[i] = 0; + trap_req = trap_req & ~TRAP_INT; + dsenable = calc_ds (cm); + } + break; + case 6: /* RTT */ + if (!CPUT (HAS_RTT)) { + setTRAP (TRAP_ILL); + break; + } + case 2: /* RTI */ + src = ReadW (SP | dsenable); + src2 = ReadW (((SP + 2) & 0177777) | dsenable); + STACKFILE[cm] = SP = (SP + 4) & 0177777; + oldrs = rs; + put_PSW (src2, (cm != MD_KER)); /* store PSW, prot */ + if (rs != oldrs) { + for (i = 0; i < 6; i++) { + REGFILE[i][oldrs] = R[i]; + R[i] = REGFILE[i][rs]; + } + } + SP = STACKFILE[cm]; + isenable = calc_is (cm); + dsenable = calc_ds (cm); + trap_req = calc_ints (ipl, trap_req); + JMP_PC (src); + if (CPUT (HAS_RTT) && tbit && /* RTT impl? */ + (IR == 000002)) setTRAP (TRAP_TRC); /* RTI immed trap */ + break; + case 7: /* MFPT */ + if (CPUT (HAS_MFPT)) /* implemented? */ + R[0] = cpu_tab[cpu_model].mfpt; /* get type */ + else setTRAP (TRAP_ILL); + break; + } /* end switch no ops */ + break; /* end case no ops */ + + case 001: /* JMP */ + if (dstreg) setTRAP (CPUT (HAS_JREG4)? TRAP_PRV: TRAP_ILL); + else { + dst = GeteaW (dstspec) & 0177777; /* get eff addr */ + if (CPUT (CPUT_05|CPUT_20) && /* 11/05, 11/20 */ + ((dstspec & 070) == 020)) /* JMP (R)+? */ + dst = R[dstspec & 07]; /* use post incr */ + JMP_PC (dst); + } + break; /* end JMP */ + + case 002: /* RTS et al*/ + if (IR < 000210) { /* RTS */ + dstspec = dstspec & 07; + JMP_PC (R[dstspec]); + R[dstspec] = ReadW (SP | dsenable); + if (dstspec != 6) SP = (SP + 2) & 0177777; + break; + } /* end if RTS */ + if (IR < 000230) { + setTRAP (TRAP_ILL); + break; + } + if (IR < 000240) { /* SPL */ + if (CPUT (HAS_SPL)) { + if (cm == MD_KER) ipl = IR & 07; + trap_req = calc_ints (ipl, trap_req); + } + else setTRAP (TRAP_ILL); + break; + } /* end if SPL */ + if (IR < 000260) { /* clear CC */ + if (IR & 010) N = 0; + if (IR & 004) Z = 0; + if (IR & 002) V = 0; + if (IR & 001) C = 0; + break; + } /* end if clear CCs */ + if (IR & 010) N = 1; /* set CC */ + if (IR & 004) Z = 1; + if (IR & 002) V = 1; + if (IR & 001) C = 1; + break; /* end case RTS et al */ + + case 003: /* SWAB */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = ((dst & 0377) << 8) | ((dst >> 8) & 0377); + N = GET_SIGN_B (dst & 0377); + Z = GET_Z (dst & 0377); + if (!CPUT (CPUT_20)) V = 0; + C = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; /* end SWAB */ + + case 004: case 005: /* BR */ + BRANCH_F (IR); + break; + + case 006: case 007: /* BR */ + BRANCH_B (IR); + break; + + case 010: case 011: /* BNE */ + if (Z == 0) { BRANCH_F (IR); } + break; + + case 012: case 013: /* BNE */ + if (Z == 0) { BRANCH_B (IR); } + break; + + case 014: case 015: /* BEQ */ + if (Z) { BRANCH_F (IR); } + break; + + case 016: case 017: /* BEQ */ + if (Z) { BRANCH_B (IR); } + break; + + case 020: case 021: /* BGE */ + if ((N ^ V) == 0) { BRANCH_F (IR); } + break; + + case 022: case 023: /* BGE */ + if ((N ^ V) == 0) { BRANCH_B (IR); } + break; + + case 024: case 025: /* BLT */ + if (N ^ V) { BRANCH_F (IR); } + break; + + case 026: case 027: /* BLT */ + if (N ^ V) { BRANCH_B (IR); } + break; + + case 030: case 031: /* BGT */ + if ((Z | (N ^ V)) == 0) { BRANCH_F (IR); } + break; + + case 032: case 033: /* BGT */ + if ((Z | (N ^ V)) == 0) { BRANCH_B (IR); } + break; + + case 034: case 035: /* BLE */ + if (Z | (N ^ V)) { BRANCH_F (IR); } + break; + + case 036: case 037: /* BLE */ + if (Z | (N ^ V)) { BRANCH_B (IR); } + break; + + case 040: case 041: case 042: case 043: /* JSR */ + case 044: case 045: case 046: case 047: + if (dstreg) setTRAP (CPUT (HAS_JREG4)? TRAP_PRV: TRAP_ILL); + else { + srcspec = srcspec & 07; + dst = GeteaW (dstspec); + if (CPUT (CPUT_05|CPUT_20) && /* 11/05, 11/20 */ + ((dstspec & 070) == 020)) /* JSR (R)+? */ + dst = R[dstspec & 07]; /* use post incr */ + SP = (SP - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0366); + WriteW (R[srcspec], SP | dsenable); + if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y))) + set_stack_trap (SP); + R[srcspec] = PC; + JMP_PC (dst & 0177777); + } + break; /* end JSR */ + + case 050: /* CLR */ + N = V = C = 0; + Z = 1; + if (dstreg) R[dstspec] = 0; + else WriteW (0, GeteaW (dstspec)); + break; + + case 051: /* COM */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = dst ^ 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + C = 1; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 052: /* INC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst + 1) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (dst == 0100000); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 053: /* DEC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst - 1) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (dst == 077777); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 054: /* NEG */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (-dst) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (dst == 0100000); + C = Z ^ 1; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 055: /* ADC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst + C) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (C && (dst == 0100000)); + C = C & Z; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 056: /* SBC */ + dst = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (dst - C) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = (C && (dst == 077777)); + C = (C && (dst == 0177777)); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 057: /* TST */ + dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = C = 0; + break; + + case 060: /* ROR */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src >> 1) | (C << 15); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 061: /* ROL */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = ((src << 1) | C) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = GET_SIGN_W (src); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 062: /* ASR */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src >> 1) | (src & 0100000); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 063: /* ASL */ + src = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + dst = (src << 1) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + C = GET_SIGN_W (src); + V = N ^ C; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + +/* Notes: + - MxPI must mask GeteaW returned address to force ispace + - MxPI must set MMR1 for SP recovery in case of fault +*/ + + case 064: /* MARK */ + if (CPUT (HAS_MARK)) { + i = (PC + dstspec + dstspec) & 0177777; + JMP_PC (R[5]); + R[5] = ReadW (i | dsenable); + SP = (i + 2) & 0177777; + } + else setTRAP (TRAP_ILL); + break; + + case 065: /* MFPI */ + if (CPUT (HAS_MXPY)) { + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm]; + else dst = R[dstspec]; + } + else { + i = ((cm == pm) && (cm == MD_USR))? calc_ds (pm): calc_is (pm); + dst = ReadW ((GeteaW (dstspec) & 0177777) | i); + } + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0366); + WriteW (dst, SP | dsenable); + if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y))) + set_stack_trap (SP); + } + else setTRAP (TRAP_ILL); + break; + + case 066: /* MTPI */ + if (CPUT (HAS_MXPY)) { + dst = ReadW (SP | dsenable); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP + 2) & 0177777; + if (update_MM) MMR1 = 026; + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst; + else R[dstspec] = dst; + } + else WriteW (dst, (GeteaW (dstspec) & 0177777) | calc_is (pm)); + } + else setTRAP (TRAP_ILL); + break; + + case 067: /* SXT */ + if (CPUT (HAS_SXS)) { + dst = N? 0177777: 0; + Z = N ^ 1; + V = 0; + if (dstreg) R[dstspec] = dst; + else WriteW (dst, GeteaW (dstspec)); + } + else setTRAP (TRAP_ILL); + break; + + case 070: /* CSM */ + if (CPUT (HAS_CSM) && (MMR3 & MMR3_CSM) || (cm != MD_KER)) { + dst = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + PSW = get_PSW () & ~PSW_CC; /* PSW, cc = 0 */ + STACKFILE[cm] = SP; + WriteW (PSW, ((SP - 2) & 0177777) | calc_ds (MD_SUP)); + WriteW (PC, ((SP - 4) & 0177777) | calc_ds (MD_SUP)); + WriteW (dst, ((SP - 6) & 0177777) | calc_ds (MD_SUP)); + SP = (SP - 6) & 0177777; + pm = cm; + cm = MD_SUP; + tbit = 0; + isenable = calc_is (cm); + dsenable = calc_ds (cm); + PC = ReadW (010 | isenable); + } + else setTRAP (TRAP_ILL); + break; + + case 072: /* TSTSET */ + if (CPUT (HAS_TSWLK) && !dstreg) { + dst = ReadMW (GeteaW (dstspec)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + C = (dst & 1); + R[0] = dst; /* R[0] <- dst */ + PWriteW (R[0] | 1, last_pa); /* dst <- R[0] | 1 */ + } + else setTRAP (TRAP_ILL); + break; + + case 073: /* WRTLCK */ + if (CPUT (HAS_TSWLK) && !dstreg) { + N = GET_SIGN_W (R[0]); + Z = GET_Z (R[0]); + V = 0; + WriteW (R[0], GeteaW (dstspec)); + } + else setTRAP (TRAP_ILL); + break; + + default: + setTRAP (TRAP_ILL); + break; + } /* end switch SOPs */ + break; /* end case 000 */ + +/* Opcodes 01 - 06: double operand word instructions + + J-11 (and F-11) optimize away register source operand decoding. + As a result, dop R,+/-(R) use the modified version of R as source. + Most (but not all) other PDP-11's fetch the source operand before + any destination operand decoding. + + Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)] + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] +*/ + + case 001: /* MOV */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + ea = GeteaW (dstspec); + dst = R[srcspec]; + } + else { + dst = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + if (!dstreg) ea = GeteaW (dstspec); + } + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else WriteW (dst, ea); + break; + + case 002: /* CMP */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadW (GeteaW (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + } + dst = (src - src2) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = GET_SIGN_W ((src ^ src2) & (~src2 ^ dst)); + C = (src < src2); + break; + + case 003: /* BIT */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadW (GeteaW (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + } + dst = src2 & src; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + break; + + case 004: /* BIC */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadMW (GeteaW (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + } + dst = src2 & ~src; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 005: /* BIS */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadMW (GeteaW (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + } + dst = src2 | src; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + + case 006: /* ADD */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadMW (GeteaW (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + } + dst = (src2 + src) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = GET_SIGN_W ((~src ^ src2) & (src ^ dst)); + C = (dst < src); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + +/* Opcode 07: EIS, FIS, CIS + + Notes: + - The code assumes that the host int length is at least 32 bits. + - MUL carry: C is set if the (signed) result doesn't fit in 16 bits. + - Divide has three error cases: + 1. Divide by zero. + 2. Divide largest negative number by -1. + 3. (Signed) quotient doesn't fit in 16 bits. + Cases 1 and 2 must be tested in advance, to avoid C runtime errors. + - ASHx left: overflow if the bits shifted out do not equal the sign + of the result (convert shift out to 1/0, xor against sign). + - ASHx right: if right shift sign extends, then the shift and + conditional or of shifted -1 is redundant. If right shift zero + extends, then the shift and conditional or does sign extension. +*/ + + case 007: + srcspec = srcspec & 07; + switch ((IR >> 9) & 07) { /* decode IR<11:9> */ + + case 0: /* MUL */ + if (!CPUO (OPT_EIS)) { + setTRAP (TRAP_ILL); + break; + } + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src = R[srcspec]; + if (GET_SIGN_W (src2)) src2 = src2 | ~077777; + if (GET_SIGN_W (src)) src = src | ~077777; + dst = src * src2; + R[srcspec] = (dst >> 16) & 0177777; + R[srcspec | 1] = dst & 0177777; + N = (dst < 0); + Z = GET_Z (dst); + V = 0; + C = ((dst > 077777) || (dst < -0100000)); + break; + + case 1: /* DIV */ + if (!CPUO (OPT_EIS)) { + setTRAP (TRAP_ILL); + break; + } + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src = (((uint32) R[srcspec]) << 16) | R[srcspec | 1]; + if (src2 == 0) { + N = 0; /* J11,11/70 compat */ + Z = V = C = 1; /* N = 0, Z = 1 */ + break; + } + if ((src == 020000000000) && (src2 == 0177777)) { + V = 1; /* J11,11/70 compat */ + N = Z = C = 0; /* N = Z = 0 */ + break; + } + if (GET_SIGN_W (src2)) src2 = src2 | ~077777; + if (GET_SIGN_W (R[srcspec])) src = src | ~017777777777; + dst = src / src2; + N = (dst < 0); /* N set on 32b result */ + if ((dst > 077777) || (dst < -0100000)) { + V = 1; /* J11,11/70 compat */ + Z = C = 0; /* Z = C = 0 */ + break; + } + R[srcspec] = dst & 0177777; + R[srcspec | 1] = (src - (src2 * dst)) & 0177777; + Z = GET_Z (dst); + V = C = 0; + break; + + case 2: /* ASH */ + if (!CPUO (OPT_EIS)) { + setTRAP (TRAP_ILL); + break; + } + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src2 = src2 & 077; + sign = GET_SIGN_W (R[srcspec]); + src = sign? R[srcspec] | ~077777: R[srcspec]; + if (src2 == 0) { /* [0] */ + dst = src; + V = C = 0; + } + else if (src2 <= 15) { /* [1,15] */ + dst = src << src2; + i = (src >> (16 - src2)) & 0177777; + V = (i != ((dst & 0100000)? 0177777: 0)); + C = (i & 1); + } + else if (src2 <= 31) { /* [16,31] */ + dst = 0; + V = (src != 0); + C = (src << (src2 - 16)) & 1; + } + else if (src2 == 32) { /* [32] = -32 */ + dst = -sign; + V = 0; + C = sign; + } + else { /* [33,63] = -31,-1 */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + V = 0; + C = ((src >> (63 - src2)) & 1); + } + dst = R[srcspec] = dst & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + break; + + case 3: /* ASHC */ + if (!CPUO (OPT_EIS)) { + setTRAP (TRAP_ILL); + break; + } + src2 = dstreg? R[dstspec]: ReadW (GeteaW (dstspec)); + src2 = src2 & 077; + sign = GET_SIGN_W (R[srcspec]); + src = (((uint32) R[srcspec]) << 16) | R[srcspec | 1]; + if (src2 == 0) { /* [0] */ + dst = src; + V = C = 0; + } + else if (src2 <= 31) { /* [1,31] */ + dst = ((uint32) src) << src2; + i = (src >> (32 - src2)) | (-sign << src2); + V = (i != ((dst & 020000000000)? -1: 0)); + C = (i & 1); + } + else if (src2 == 32) { /* [32] = -32 */ + dst = -sign; + V = 0; + C = sign; + } + else { /* [33,63] = -31,-1 */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + V = 0; + C = ((src >> (63 - src2)) & 1); + } + i = R[srcspec] = (dst >> 16) & 0177777; + dst = R[srcspec | 1] = dst & 0177777; + N = GET_SIGN_W (i); + Z = GET_Z (dst | i); + break; + + case 4: /* XOR */ + if (CPUT (HAS_SXS)) { + if (CPUT (IS_SDSD) && !dstreg) { /* R,not R */ + src2 = ReadMW (GeteaW (dstspec)); + src = R[srcspec]; + } + else { + src = R[srcspec]; + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + } + dst = src ^ src2; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + } + else setTRAP (TRAP_ILL); + break; + + case 5: /* FIS */ + if (CPUO (OPT_FIS)) fis11 (IR); + else setTRAP (TRAP_ILL); + break; + + case 6: /* CIS */ + if (CPUT (CPUT_60) && (cm == MD_KER) && /* 11/60 MED? */ + (IR == 076600)) { + ReadE (PC | isenable); /* read immediate */ + PC = (PC + 2) & 0177777; + } + else if (CPUO (OPT_CIS)) /* CIS option? */ + reason = cis11 (IR); + else setTRAP (TRAP_ILL); + break; + + case 7: /* SOB */ + if (CPUT (HAS_SXS)) { + R[srcspec] = (R[srcspec] - 1) & 0177777; + if (R[srcspec]) { + JMP_PC ((PC - dstspec - dstspec) & 0177777); + } + } + else setTRAP (TRAP_ILL); + break; + } /* end switch EIS */ + break; /* end case 007 */ + +/* Opcode 10: branches, traps, SOPs */ + + case 010: + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + + case 000: case 001: /* BPL */ + if (N == 0) { BRANCH_F (IR); } + break; + + case 002: case 003: /* BPL */ + if (N == 0) { BRANCH_B (IR); } + break; + + case 004: case 005: /* BMI */ + if (N) { BRANCH_F (IR); } + break; + + case 006: case 007: /* BMI */ + if (N) { BRANCH_B (IR); } + break; + + case 010: case 011: /* BHI */ + if ((C | Z) == 0) { BRANCH_F (IR); } + break; + + case 012: case 013: /* BHI */ + if ((C | Z) == 0) { BRANCH_B (IR); } + break; + + case 014: case 015: /* BLOS */ + if (C | Z) { BRANCH_F (IR); } + break; + + case 016: case 017: /* BLOS */ + if (C | Z) { BRANCH_B (IR); } + break; + + case 020: case 021: /* BVC */ + if (V == 0) { BRANCH_F (IR); } + break; + + case 022: case 023: /* BVC */ + if (V == 0) { BRANCH_B (IR); } + break; + + case 024: case 025: /* BVS */ + if (V) { BRANCH_F (IR); } + break; + + case 026: case 027: /* BVS */ + if (V) { BRANCH_B (IR); } + break; + + case 030: case 031: /* BCC */ + if (C == 0) { BRANCH_F (IR); } + break; + + case 032: case 033: /* BCC */ + if (C == 0) { BRANCH_B (IR); } + break; + + case 034: case 035: /* BCS */ + if (C) { BRANCH_F (IR); } + break; + + case 036: case 037: /* BCS */ + if (C) { BRANCH_B (IR); } + break; + + case 040: case 041: case 042: case 043: /* EMT */ + setTRAP (TRAP_EMT); + break; + + case 044: case 045: case 046: case 047: /* TRAP */ + setTRAP (TRAP_TRAP); + break; + + case 050: /* CLRB */ + N = V = C = 0; + Z = 1; + if (dstreg) R[dstspec] = R[dstspec] & 0177400; + else WriteB (0, GeteaB (dstspec)); + break; + + case 051: /* COMB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst ^ 0377) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + C = 1; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 052: /* INCB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst + 1) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (dst == 0200); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 053: /* DECB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst - 1) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (dst == 0177); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 054: /* NEGB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (-dst) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (dst == 0200); + C = (Z ^ 1); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 055: /* ADCB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst + C) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (C && (dst == 0200)); + C = C & Z; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 056: /* SBCB */ + dst = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (dst - C) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = (C && (dst == 0177)); + C = (C && (dst == 0377)); + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 057: /* TSTB */ + dst = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = C = 0; + break; + + case 060: /* RORB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = ((src & 0377) >> 1) | (C << 7); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 061: /* ROLB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = ((src << 1) | C) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = GET_SIGN_B (src & 0377); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 062: /* ASRB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = ((src & 0377) >> 1) | (src & 0200); + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = (src & 1); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 063: /* ASLB */ + src = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + dst = (src << 1) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + C = GET_SIGN_B (src & 0377); + V = N ^ C; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; +/* Notes: + - MTPS cannot alter the T bit + - MxPD must mask GeteaW returned address, dspace is from cm not pm + - MxPD must set MMR1 for SP recovery in case of fault +*/ + + case 064: /* MTPS */ + if (CPUT (HAS_MXPS)) { + dst = dstreg? R[dstspec]: ReadB (GeteaB (dstspec)); + if (cm == MD_KER) { + ipl = (dst >> PSW_V_IPL) & 07; + trap_req = calc_ints (ipl, trap_req); + } + N = (dst >> PSW_V_N) & 01; + Z = (dst >> PSW_V_Z) & 01; + V = (dst >> PSW_V_V) & 01; + C = (dst >> PSW_V_C) & 01; + } + else setTRAP (TRAP_ILL); + break; + + case 065: /* MFPD */ + if (CPUT (HAS_MXPY)) { + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) dst = STACKFILE[pm]; + else dst = R[dstspec]; + } + else dst = ReadW ((GeteaW (dstspec) & 0177777) | calc_ds (pm)); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0366); + WriteW (dst, SP | dsenable); + if ((cm == MD_KER) && (SP < (STKLIM + STKL_Y))) + set_stack_trap (SP); + } + else setTRAP (TRAP_ILL); + break; + + case 066: /* MTPD */ + if (CPUT (HAS_MXPY)) { + dst = ReadW (SP | dsenable); + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = 0; + SP = (SP + 2) & 0177777; + if (update_MM) MMR1 = 026; + if (dstreg) { + if ((dstspec == 6) && (cm != pm)) STACKFILE[pm] = dst; + else R[dstspec] = dst; + } + else WriteW (dst, (GeteaW (dstspec) & 0177777) | calc_ds (pm)); + } + else setTRAP (TRAP_ILL); + break; + + case 067: /* MFPS */ + if (CPUT (HAS_MXPS)) { + dst = get_PSW () & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst; + else WriteB (dst, GeteaB (dstspec)); + } + else setTRAP (TRAP_ILL); + break; + + default: + setTRAP (TRAP_ILL); + break; + } /* end switch SOPs */ + break; /* end case 010 */ + +/* Opcodes 11 - 16: double operand byte instructions + + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] + Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)] +*/ + + case 011: /* MOVB */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + ea = GeteaB (dstspec); + dst = R[srcspec] & 0377; + } + else { + dst = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); + if (!dstreg) ea = GeteaB (dstspec); + } + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (dst & 0200)? 0177400 | dst: dst; + else WriteB (dst, ea); + break; + + case 012: /* CMPB */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadB (GeteaB (dstspec)); + src = R[srcspec] & 0377; + } + else { + src = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); + } + dst = (src - src2) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = GET_SIGN_B ((src ^ src2) & (~src2 ^ dst)); + C = (src < src2); + break; + + case 013: /* BITB */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadB (GeteaB (dstspec)); + src = R[srcspec] & 0377; + } + else { + src = srcreg? R[srcspec] & 0377: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec] & 0377: ReadB (GeteaB (dstspec)); + } + dst = (src2 & src) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + break; + + case 014: /* BICB */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadMB (GeteaB (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + } + dst = (src2 & ~src) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 015: /* BISB */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadMB (GeteaB (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadB (GeteaB (srcspec)); + src2 = dstreg? R[dstspec]: ReadMB (GeteaB (dstspec)); + } + dst = (src2 | src) & 0377; + N = GET_SIGN_B (dst); + Z = GET_Z (dst); + V = 0; + if (dstreg) R[dstspec] = (R[dstspec] & 0177400) | dst; + else PWriteB (dst, last_pa); + break; + + case 016: /* SUB */ + if (CPUT (IS_SDSD) && srcreg && !dstreg) { /* R,not R */ + src2 = ReadMW (GeteaW (dstspec)); + src = R[srcspec]; + } + else { + src = srcreg? R[srcspec]: ReadW (GeteaW (srcspec)); + src2 = dstreg? R[dstspec]: ReadMW (GeteaW (dstspec)); + } + dst = (src2 - src) & 0177777; + N = GET_SIGN_W (dst); + Z = GET_Z (dst); + V = GET_SIGN_W ((src ^ src2) & (~src ^ dst)); + C = (src2 < src); + if (dstreg) R[dstspec] = dst; + else PWriteW (dst, last_pa); + break; + +/* Opcode 17: floating point */ + + case 017: + if (CPUO (OPT_FPP)) fp11 (IR); /* call fpp */ + else setTRAP (TRAP_ILL); + break; /* end case 017 */ + } /* end switch op */ + } /* end main loop */ + +/* Simulation halted */ + +PSW = get_PSW (); +for (i = 0; i < 6; i++) REGFILE[i][rs] = R[i]; +STACKFILE[cm] = SP; +saved_PC = PC & 0177777; +pcq_r->qptr = pcq_p; /* update pc q ptr */ +set_r_display (rs, cm); +return reason; +} + +/* Effective address calculations + + Inputs: + spec = specifier <5:0> + Outputs: + ea = effective address + <15:0> = virtual address + <16> = instruction/data data space + <18:17> = mode + + Data space calculation: the PDP-11 features both instruction and data + spaces. Instruction space contains the instruction and any sequential + add ons (eg, immediates, absolute addresses). Data space contains all + data operands and indirect addresses. If data space is enabled, then + memory references are directed according to these rules: + + Mode Index ref Indirect ref Direct ref + 10..16 na na data + 17 na na instruction + 20..26 na na data + 27 na na instruction + 30..36 na data data + 37 na instruction (absolute) data + 40..46 na na data + 47 na na instruction + 50..56 na data data + 57 na instruction data + 60..67 instruction na data + 70..77 instruction data data + + According to the PDP-11 Architecture Handbook, MMR1 records all + autoincrement and autodecrement operations, including those which + explicitly reference the PC. For the J-11, this is only true for + autodecrement operands, autodecrement deferred operands, and + autoincrement destination operands that involve a write to memory. + The simulator follows the Handbook, for simplicity. + + Notes: + + - dsenable will direct a reference to data space if data space is enabled + - ds will direct a reference to data space if data space is enabled AND if + the specifier register is not PC; this is used for 17, 27, 37, 47, 57 + - Modes 2x, 3x, 4x, and 5x must update MMR1 if updating enabled + - Modes 46 and 56 must check for stack overflow if kernel mode +*/ + +/* Effective address calculation for words */ + +int32 GeteaW (int32 spec) +{ +int32 adr, reg, ds; + +reg = spec & 07; /* register number */ +ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ +switch (spec >> 3) { /* decode spec<5:3> */ + + default: /* can't get here */ + case 1: /* (R) */ + return (R[reg] | ds); + + case 2: /* (R)+ */ + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (020 | reg); + return (adr | ds); + + case 3: /* @(R)+ */ + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (020 | reg); + adr = ReadW (adr | ds); + return (adr | dsenable); + + case 4: /* -(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0360 | reg); + if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) + set_stack_trap (adr); + return (adr | ds); + + case 5: /* @-(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0360 | reg); + if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) + set_stack_trap (adr); + adr = ReadW (adr | ds); + return (adr | dsenable); + + case 6: /* d(r) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + return (((R[reg] + adr) & 0177777) | dsenable); + + case 7: /* @d(R) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); + return (adr | dsenable); + } /* end switch */ +} + +/* Effective address calculation for bytes */ + +int32 GeteaB (int32 spec) +{ +int32 adr, reg, ds, delta; + +reg = spec & 07; /* reg number */ +ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ +switch (spec >> 3) { /* decode spec<5:3> */ + + default: /* can't get here */ + case 1: /* (R) */ + return (R[reg] | ds); + + case 2: /* (R)+ */ + delta = 1 + (reg >= 6); /* 2 if R6, PC */ + R[reg] = ((adr = R[reg]) + delta) & 0177777; + if (update_MM) MMR1 = calc_MMR1 ((delta << 3) | reg); + return (adr | ds); + + case 3: /* @(R)+ */ + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (020 | reg); + adr = ReadW (adr | ds); + return (adr | dsenable); + + case 4: /* -(R) */ + delta = 1 + (reg >= 6); /* 2 if R6, PC */ + adr = R[reg] = (R[reg] - delta) & 0177777; + if (update_MM) MMR1 = calc_MMR1 ((((-delta) & 037) << 3) | reg); + if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) + set_stack_trap (adr); + return (adr | ds); + + case 5: /* @-(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = calc_MMR1 (0360 | reg); + if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) + set_stack_trap (adr); + adr = ReadW (adr | ds); + return (adr | dsenable); + + case 6: /* d(r) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + return (((R[reg] + adr) & 0177777) | dsenable); + + case 7: /* @d(R) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); + return (adr | dsenable); + } /* end switch */ +} + +/* Read byte and word routines, read only and read-modify-write versions + + Inputs: + va = virtual address, <18:16> = mode, I/D space + Outputs: + data = data read from memory or I/O space +*/ + +int32 ReadE (int32 va) +{ +int32 pa, data; + +if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } +pa = relocR (va); /* relocate */ +if (ADDR_IS_MEM (pa)) return (M[pa >> 1]); /* memory address? */ +if ((pa < IOPAGEBASE) || /* not I/O address */ + (CPUT (CPUT_J) && (pa >= IOBA_CPU))) { /* or J11 int reg? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return data; +} + +int32 ReadW (int32 va) +{ +int32 pa, data; + +if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } +pa = relocR (va); /* relocate */ +if (ADDR_IS_MEM (pa)) return (M[pa >> 1]); /* memory address? */ +if (pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return data; +} + +int32 ReadB (int32 va) +{ +int32 pa, data; + +pa = relocR (va); /* relocate */ +if (ADDR_IS_MEM (pa)) return (va & 1? M[pa >> 1] >> 8: M[pa >> 1]) & 0377; +if (pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageR (&data, pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return ((va & 1)? data >> 8: data) & 0377; +} + +int32 ReadMW (int32 va) +{ +int32 data; + +if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } +last_pa = relocW (va); /* reloc, wrt chk */ +if (ADDR_IS_MEM (last_pa)) return (M[last_pa >> 1]); /* memory address? */ +if (last_pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return data; +} + +int32 ReadMB (int32 va) +{ +int32 data; + +last_pa = relocW (va); /* reloc, wrt chk */ +if (ADDR_IS_MEM (last_pa)) + return (va & 1? M[last_pa >> 1] >> 8: M[last_pa >> 1]) & 0377; +if (last_pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageR (&data, last_pa, READ) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return ((va & 1)? data >> 8: data) & 0377; +} + +/* Write byte and word routines + + Inputs: + data = data to be written + va = virtual address, <18:16> = mode, I/D space, or + pa = physical address + Outputs: none +*/ + +void WriteW (int32 data, int32 va) +{ +int32 pa; + +if ((va & 1) && CPUT (HAS_ODD)) { /* odd address? */ + setCPUERR (CPUE_ODD); + ABORT (TRAP_ODD); + } +pa = relocW (va); /* relocate */ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + M[pa >> 1] = data; + return; + } +if (pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return; +} + +void WriteB (int32 data, int32 va) +{ +int32 pa; + +pa = relocW (va); /* relocate */ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + if (va & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8); + else M[pa >> 1] = (M[pa >> 1] & ~0377) | data; + return; + } +if (pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return; +} + +void PWriteW (int32 data, int32 pa) +{ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + M[pa >> 1] = data; + return; + } +if (pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageW (data, pa, WRITE) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return; +} + +void PWriteB (int32 data, int32 pa) +{ +if (ADDR_IS_MEM (pa)) { /* memory address? */ + if (pa & 1) M[pa >> 1] = (M[pa >> 1] & 0377) | (data << 8); + else M[pa >> 1] = (M[pa >> 1] & ~0377) | data; + return; + } +if (pa < IOPAGEBASE) { /* not I/O address? */ + setCPUERR (CPUE_NXM); + ABORT (TRAP_NXM); + } +if (iopageW (data, pa, WRITEB) != SCPE_OK) { /* invalid I/O addr? */ + setCPUERR (CPUE_TMO); + ABORT (TRAP_NXM); + } +return; +} + +/* Relocate virtual address, read access + + Inputs: + va = virtual address, <18:16> = mode, I/D space + Outputs: + pa = physical address + On aborts, this routine aborts back to the top level simulator + with an appropriate trap code. + + Notes: + - The 'normal' read codes (010, 110) are done in-line; all + others in a subroutine + - APRFILE[UNUSED] is all zeroes, forcing non-resident abort + - Aborts must update MMR0<15:13,6:1> if updating is enabled +*/ + +int32 relocR (int32 va) +{ +int32 apridx, apr, pa; + +if (MMR0 & MMR0_MME) { /* if mmgt */ + apridx = (va >> VA_V_APF) & 077; /* index into APR */ + apr = APRFILE[apridx]; /* with va<18:13> */ + if ((apr & PDR_PRD) != 2) /* not 2, 6? */ + relocR_test (va, apridx); /* long test */ + if (PLF_test (va, apr)) /* pg lnt error? */ + reloc_abort (MMR0_PL, apridx); + pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK; + if ((MMR3 & MMR3_M22E) == 0) { + pa = pa & 0777777; + if (pa >= 0760000) pa = 017000000 | pa; + } + } +else { + pa = va & 0177777; /* mmgt off */ + if (pa >= 0160000) pa = 017600000 | pa; + } +return pa; +} + +/* Read relocation, access control field != read only or read/write + + ACF value 11/45,11/70 all others + + 0 abort NR abort NR + 1 trap - + 2 ok ok + 3 abort NR - + 4 trap abort NR + 5 ok - + 6 ok ok + 7 abort NR - +*/ + +void relocR_test (int32 va, int32 apridx) +{ +int32 apr, err; + +err = 0; /* init status */ +apr = APRFILE[apridx]; /* get APR */ +switch (apr & PDR_ACF) { /* case on ACF */ + + case 1: case 4: /* trap read */ + if (CPUT (HAS_MMTR)) { /* traps implemented? */ + APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ + if (MMR0 & MMR0_TENB) { /* traps enabled? */ + if (update_MM) MMR0 = /* update MMR0 */ + (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); + MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ + setTRAP (TRAP_MME); /* set trap */ + } + return; /* continue op */ + } /* not impl, abort NR */ + case 0: case 3: case 7: /* non-resident */ + err = MMR0_NR; /* set MMR0 */ + break; /* go test PLF, abort */ + + case 2: case 5: case 6: /* readable */ + return; /* continue */ + } /* end switch */ + +if (PLF_test (va, apr)) err = err | MMR0_PL; /* pg lnt error? */ +reloc_abort (err, apridx); +return; +} + +t_bool PLF_test (int32 va, int32 apr) +{ +int32 dbn = va & VA_BN; /* extr block num */ +int32 plf = (apr & PDR_PLF) >> 2; /* extr page length */ + +return ((apr & PDR_ED)? (dbn < plf): (dbn > plf)); /* pg lnt error? */ +} + +void reloc_abort (int32 err, int32 apridx) +{ +if (update_MM) MMR0 = /* update MMR0 */ + (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); +APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ +MMR0 = MMR0 | err; /* set aborts */ +ABORT (TRAP_MME); /* abort ref */ +return; +} + +/* Relocate virtual address, write access + + Inputs: + va = virtual address, <18:16> = mode, I/D space + Outputs: + pa = physical address + On aborts, this routine aborts back to the top level simulator + with an appropriate trap code. + + Notes: + - The 'normal' write code (110) is done in-line; all others + in a subroutine + - APRFILE[UNUSED] is all zeroes, forcing non-resident abort + - Aborts must update MMR0<15:13,6:1> if updating is enabled +*/ + +int32 relocW (int32 va) +{ +int32 apridx, apr, pa; + +if (MMR0 & MMR0_MME) { /* if mmgt */ + apridx = (va >> VA_V_APF) & 077; /* index into APR */ + apr = APRFILE[apridx]; /* with va<18:13> */ + if ((apr & PDR_ACF) != 6) /* not writeable? */ + relocW_test (va, apridx); /* long test */ + if (PLF_test (va, apr)) /* pg lnt error? */ + reloc_abort (MMR0_PL, apridx); + APRFILE[apridx] = apr | PDR_W; /* set W */ + pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK; + if ((MMR3 & MMR3_M22E) == 0) { + pa = pa & 0777777; + if (pa >= 0760000) pa = 017000000 | pa; + } + } +else { + pa = va & 0177777; /* mmgt off */ + if (pa >= 0160000) pa = 017600000 | pa; + } +return pa; +} + +/* Write relocation, access control field != read/write + + ACF value 11/45,11/70 all others + + 0 abort NR abort NR + 1 abort RO - + 2 abort RO abort RO + 3 abort NR - + 4 trap abort NR + 5 trap - + 6 ok ok + 7 abort NR - +*/ + +void relocW_test (int32 va, int32 apridx) +{ +int32 apr, err; + +err = 0; /* init status */ +apr = APRFILE[apridx]; /* get APR */ +switch (apr & PDR_ACF) { /* case on ACF */ + + case 4: case 5: /* trap write */ + if (CPUT (HAS_MMTR)) { /* traps implemented? */ + APRFILE[apridx] = APRFILE[apridx] | PDR_A; /* set A */ + if (MMR0 & MMR0_TENB) { /* traps enabled? */ + if (update_MM) MMR0 = /* update MMR0 */ + (MMR0 & ~MMR0_PAGE) | (apridx << MMR0_V_PAGE); + MMR0 = MMR0 | MMR0_TRAP; /* set trap flag */ + setTRAP (TRAP_MME); /* set trap */ + } + return; /* continue op */ + } /* not impl, abort NR */ + case 0: case 3: case 7: /* non-resident */ + err = MMR0_NR; /* MMR0 status */ + break; /* go test PLF, abort */ + + case 1: case 2: /* read only */ + err = MMR0_RO; /* MMR0 status */ + break; + + case 6: /* read/write */ + return; /* continue */ + } /* end switch */ +if (PLF_test (va, apr)) err = err | MMR0_PL; /* pg lnt error? */ +reloc_abort (err, apridx); +return; +} + +/* Relocate virtual address, console access + + Inputs: + va = virtual address + sw = switches + Outputs: + pa = physical address + On aborts, this routine returns MAXMEMSIZE +*/ + +int32 relocC (int32 va, int32 sw) +{ +int32 mode, dbn, plf, apridx, apr, pa; + +if (MMR0 & MMR0_MME) { /* if mmgt */ + if (sw & SWMASK ('K')) mode = MD_KER; + else if (sw & SWMASK ('S')) mode = MD_SUP; + else if (sw & SWMASK ('U')) mode = MD_USR; + else if (sw & SWMASK ('P')) mode = (PSW >> PSW_V_PM) & 03; + else mode = (PSW >> PSW_V_CM) & 03; + va = va | ((sw & SWMASK ('D'))? calc_ds (mode): calc_is (mode)); + apridx = (va >> VA_V_APF) & 077; /* index into APR */ + apr = APRFILE[apridx]; /* with va<18:13> */ + dbn = va & VA_BN; /* extr block num */ + plf = (apr & PDR_PLF) >> 2; /* extr page length */ + if ((apr & PDR_PRD) == 0) return MAXMEMSIZE; /* not readable? */ + if ((apr & PDR_ED)? dbn < plf: dbn > plf) return MAXMEMSIZE; + pa = ((va & VA_DF) + ((apr >> 10) & 017777700)) & PAMASK; + if ((MMR3 & MMR3_M22E) == 0) { + pa = pa & 0777777; + if (pa >= 0760000) pa = 017000000 | pa; + } + } +else { + pa = va & 0177777; /* mmgt off */ + if (pa >= 0160000) pa = 017600000 | pa; + } +return pa; +} + +/* Memory management registers + + MMR0 17777572 read/write, certain bits unimplemented or read only + MMR1 17777574 read only + MMR2 17777576 read only + MMR3 17777516 read/write, certain bits unimplemented +*/ + +t_stat MMR012_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 3) { /* decode pa<2:1> */ + + case 0: /* SR */ + return SCPE_NXM; + + case 1: /* MMR0 */ + *data = MMR0 & cpu_tab[cpu_model].mm0; + break; + + case 2: /* MMR1 */ + *data = MMR1; + break; + + case 3: /* MMR2 */ + *data = MMR2; + break; + } /* end switch pa */ + +return SCPE_OK; +} + +t_stat MMR012_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 3) { /* decode pa<2:1> */ + + case 0: /* DR */ + return SCPE_NXM; + + case 1: /* MMR0 */ + if (access == WRITEB) data = (pa & 1)? + (MMR0 & 0377) | (data << 8): (MMR0 & ~0377) | data; + data = data & cpu_tab[cpu_model].mm0; + MMR0 = (MMR0 & ~MMR0_WR) | (data & MMR0_WR); + return SCPE_OK; + + default: /* MMR1, MMR2 */ + return SCPE_OK; + } /* end switch pa */ +} + +t_stat MMR3_rd (int32 *data, int32 pa, int32 access) /* MMR3 */ +{ +*data = MMR3 & cpu_tab[cpu_model].mm3; +return SCPE_OK; +} + +t_stat MMR3_wr (int32 data, int32 pa, int32 access) /* MMR3 */ +{ +if (pa & 1) return SCPE_OK; +MMR3 = data & cpu_tab[cpu_model].mm3; +cpu_bme = (MMR3 & MMR3_BME) && (cpu_opt & OPT_UBM); +dsenable = calc_ds (cm); +return SCPE_OK; +} + +/* PARs and PDRs. These are grouped in I/O space as follows: + + 17772200 - 17772276 supervisor block + 17772300 - 17772376 kernel block + 17777600 - 17777676 user block + + Within each block, the subblocks are I PDR's, D PDR's, I PAR's, D PAR's + + Thus, the algorithm for converting between I/O space addresses and + APRFILE indices is as follows: + + idx<3:0> = dspace'page = pa<4:1> + par = PDR vs PAR = pa<5> + idx<5:4> = ker/sup/user = pa<8>'~pa<6> + + Note: the A,W bits are read only; they are cleared by any write to an APR +*/ + +t_stat APR_rd (int32 *data, int32 pa, int32 access) +{ +t_stat left, idx; + +idx = (pa >> 1) & 017; /* dspace'page */ +left = (pa >> 5) & 1; /* PDR vs PAR */ +if ((pa & 0100) == 0) idx = idx | 020; /* 1 for super, user */ +if (pa & 0400) idx = idx | 040; /* 1 for user only */ +if (left) *data = (APRFILE[idx] >> 16) & cpu_tab[cpu_model].par; +else *data = APRFILE[idx] & cpu_tab[cpu_model].pdr; +return SCPE_OK; +} + +t_stat APR_wr (int32 data, int32 pa, int32 access) +{ +int32 left, idx, curr; + +idx = (pa >> 1) & 017; /* dspace'page */ +left = (pa >> 5) & 1; /* PDR vs PAR */ +if ((pa & 0100) == 0) idx = idx | 020; /* 1 for super, user */ +if (pa & 0400) idx = idx | 040; /* 1 for user only */ +if (left) curr = (APRFILE[idx] >> 16) & cpu_tab[cpu_model].par; +else curr = APRFILE[idx] & cpu_tab[cpu_model].pdr; +if (access == WRITEB) data = (pa & 1)? + (curr & 0377) | (data << 8): (curr & ~0377) | data; +if (left) APRFILE[idx] = ((APRFILE[idx] & 0177777) | + (((uint32) (data & cpu_tab[cpu_model].par)) << 16)) & ~(PDR_A|PDR_W); +else APRFILE[idx] = ((APRFILE[idx] & ~0177777) | + (data & cpu_tab[cpu_model].pdr)) & ~(PDR_A|PDR_W); +return SCPE_OK; +} + +/* Explicit PSW read */ + +t_stat PSW_rd (int32 *data, int32 pa, int32 access) +{ +if (access == READC) *data = PSW; +else *data = get_PSW (); +return SCPE_OK; +} + +/* Assemble PSW from pieces */ + +int32 get_PSW (void) +{ +return (cm << PSW_V_CM) | (pm << PSW_V_PM) | + (rs << PSW_V_RS) | (fpd << PSW_V_FPD) | + (ipl << PSW_V_IPL) | (tbit << PSW_V_TBIT) | + (N << PSW_V_N) | (Z << PSW_V_Z) | + (V << PSW_V_V) | (C << PSW_V_C); +} + +/* Explicit PSW write - T-bit may be protected */ + +t_stat PSW_wr (int32 data, int32 pa, int32 access) +{ +int32 i, curr, oldrs; + +if (access == WRITEC) { /* console access? */ + PSW = data & cpu_tab[cpu_model].psw; + return SCPE_OK; + } +curr = get_PSW (); /* get current */ +oldrs = rs; /* save reg set */ +STACKFILE[cm] = SP; /* save curr SP */ +if (access == WRITEB) data = (pa & 1)? + (curr & 0377) | (data << 8): (curr & ~0377) | data; +if (!CPUT (HAS_EXPT)) /* expl T writes? */ + data = (data & ~PSW_TBIT) | (curr & PSW_TBIT); /* no, use old T */ +put_PSW (data, 0); /* call calc_is,ds */ +if (rs != oldrs) { /* switch reg set */ + for (i = 0; i < 6; i++) { + REGFILE[i][oldrs] = R[i]; + R[i] = REGFILE[i][rs]; + } + } +SP = STACKFILE[cm]; /* switch SP */ +isenable = calc_is (cm); +dsenable = calc_ds (cm); +return SCPE_OK; +} + +/* Store pieces of new PSW - implements RTI/RTT protection */ + +void put_PSW (int32 val, t_bool prot) +{ +val = val & cpu_tab[cpu_model].psw; /* mask off invalid bits */ +if (prot) { /* protected? */ + cm = cm | ((val >> PSW_V_CM) & 03); /* or to cm,pm,rs */ + pm = pm | ((val >> PSW_V_PM) & 03); /* can't change ipl */ + rs = rs | ((val >> PSW_V_RS) & 01); + } +else { + cm = (val >> PSW_V_CM) & 03; /* write cm,pm,rs,ipl */ + pm = (val >> PSW_V_PM) & 03; + rs = (val >> PSW_V_RS) & 01; + ipl = (val >> PSW_V_IPL) & 07; + } +fpd = (val >> PSW_V_FPD) & 01; /* always writeable */ +tbit = (val >> PSW_V_TBIT) & 01; +N = (val >> PSW_V_N) & 01; +Z = (val >> PSW_V_Z) & 01; +V = (val >> PSW_V_V) & 01; +C = (val >> PSW_V_C) & 01; +return; +} + +/* PIRQ write routine */ + +void put_PIRQ (int32 val) +{ +int32 pl; + +PIRQ = val & PIRQ_RW; +pl = 0; +if (PIRQ & PIRQ_PIR1) { + SET_INT (PIR1); + pl = 0042; + } +else CLR_INT (PIR1); +if (PIRQ & PIRQ_PIR2) { + SET_INT (PIR2); + pl = 0104; + } +else CLR_INT (PIR2); +if (PIRQ & PIRQ_PIR3) { + SET_INT (PIR3); + pl = 0146; + } +else CLR_INT (PIR3); +if (PIRQ & PIRQ_PIR4) { + SET_INT (PIR4); + pl = 0210; + } +else CLR_INT (PIR4); +if (PIRQ & PIRQ_PIR5) { + SET_INT (PIR5); + pl = 0252; + } +else CLR_INT (PIR5); +if (PIRQ & PIRQ_PIR6) { + SET_INT (PIR6); + pl = 0314; + } +else CLR_INT (PIR6); +if (PIRQ & PIRQ_PIR7) { + SET_INT (PIR7); + pl = 0356; + } +else CLR_INT (PIR7); +PIRQ = PIRQ | pl; +return; +} + +/* Stack trap routine */ + +void set_stack_trap (int32 adr) +{ +if (CPUT (HAS_STKLF)) { /* fixed stack? */ + setTRAP (TRAP_YEL); /* always yellow trap */ + setCPUERR (CPUE_YEL); + } +else if (CPUT (HAS_STKLR)) { /* register limit? */ + if (adr >= (STKLIM + STKL_R)) { /* yellow zone? */ + setTRAP (TRAP_YEL); /* still yellow trap */ + setCPUERR (CPUE_YEL); + } + else { /* red zone abort */ + setCPUERR (CPUE_RED); + STACKFILE[MD_KER] = 4; + SP = 4; + ABORT (TRAP_RED); + } + } +return; /* no stack limit */ +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +PIRQ = 0; +STKLIM = 0; +PSW = 000340; +MMR0 = 0; +MMR1 = 0; +MMR2 = 0; +MMR3 = 0; +trap_req = 0; +wait_state = 0; +if (M == NULL) M = (uint16 *) calloc (MEMSIZE >> 1, sizeof (uint16)); +if (M == NULL) return SCPE_MEM; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +set_r_display (0, MD_KER); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 iodata; +t_stat stat; + +if (vptr == NULL) return SCPE_ARG; +if (sw & SWMASK ('V')) { /* -v */ + if (addr >= VASIZE) return SCPE_NXM; + addr = relocC (addr, sw); /* relocate */ + if (addr >= MAXMEMSIZE) return SCPE_REL; + } +if (addr < MEMSIZE) { + *vptr = M[addr >> 1] & 0177777; + return SCPE_OK; + } +if (addr < IOPAGEBASE) return SCPE_NXM; +stat = iopageR (&iodata, addr, READC); +*vptr = iodata; +return stat; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (sw & SWMASK ('V')) { /* -v */ + if (addr >= VASIZE) return SCPE_NXM; + addr = relocC (addr, sw); /* relocate */ + if (addr >= MAXMEMSIZE) return SCPE_REL; + } +if (addr < MEMSIZE) { + M[addr >> 1] = val & 0177777; + return SCPE_OK; + } +if (addr < IOPAGEBASE) return SCPE_NXM; +return iopageW ((int32) val, addr, WRITEC); +} + +/* Set R, SP register display addresses */ + +void set_r_display (int32 rs, int32 cm) +{ +extern REG *find_reg (char *cptr, char **optr, DEVICE *dptr); +REG *rptr; +int32 i; + +rptr = find_reg ("R0", NULL, &cpu_dev); +if (rptr == NULL) return; +for (i = 0; i < 6; i++, rptr++) rptr->loc = (void *) ®FILE[i][rs]; +rptr->loc = (void *) &STACKFILE[cm]; +return; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 j, k, di, lnt, ir; +char *cptr = (char *) desc; +t_value sim_eval[HIST_ILNT]; +t_stat r; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC PSW src dst IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(di++) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_VLD) { /* instruction? */ + ir = h->inst[0]; + fprintf (st, "%06o %06o|", h->pc & ~HIST_VLD, h->psw); + if (((ir & 0070000) != 0) || /* dops, eis, fpp */ + ((ir & 0177000) == 0004000)) /* jsr */ + fprintf (st, "%06o %06o ", h->src, h->dst); + else if ((ir >= 0000100) && /* not no opnd */ + (((ir & 0007700) < 0000300) || /* not branch */ + ((ir & 0007700) >= 0004000))) + fprintf (st, " %06o ", h->dst); + else fprintf (st, " "); + for (j = 0; j < HIST_ILNT; j++) sim_eval[j] = h->inst[j]; + if ((fprint_sym (st, h->pc & ~HIST_VLD, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %06o", h->inst[0]); + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} + +/* Virtual address translation */ + +t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) +{ +t_stat r; +char *cptr = (char *) desc; +uint32 va, pa; + +if (cptr) { + va = (uint32) get_uint (cptr, 8, VAMASK, &r); + if (r == SCPE_OK) { + pa = relocC (va, sim_switches); /* relocate */ + if (pa < MAXMEMSIZE) + fprintf (of, "Virtual %-o = physical %-o\n", va, pa); + else fprintf (of, "Virtual %-o is not valid\n", va); + return SCPE_OK; + } + } +fprintf (of, "Invalid argument\n"); +return SCPE_OK; +} diff --git a/PDP11/pdp11_cpumod.c b/PDP11/pdp11_cpumod.c new file mode 100644 index 0000000..4624b0e --- /dev/null +++ b/PDP11/pdp11_cpumod.c @@ -0,0 +1,1217 @@ +/* pdp11_cpumod.c: PDP-11 CPU model-specific features + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + system PDP-11 model-specific registers + + 20-May-08 RMS Added JCSR default for KDJ11B, KDJ11E + 22-Apr-08 RMS Fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE + (found by Walter Mueller) + 29-Apr-07 RMS Don't run bus setup routine during RESTORE + 30-Aug-05 RMS Added additional 11/60 registers + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 15-Feb-05 RMS Fixed bug in SHOW MODEL (from Sergey Okhapkin) + 19-Jan-05 RMS Added variable SYSID, MBRK write (from Tim Chapman) + + This module includes CPU- and system-specific registers, such as the Unibus + map and control registers on 22b Unibus systems, the board registers for the + F11- and J11-based systems, and the system registers for the PDP-11/44, + PDP-11/45, PDP-11/60, and PDP-11/70. Most registers are implemented at + a minimum level: just enough to satisfy the machine identification code + in the various operating systems. +*/ + +#include "pdp11_defs.h" +#include "pdp11_cpumod.h" +#include + +/* Byte write macros for system registers */ + +#define ODD_IGN(cur) \ + if ((access == WRITEB) && (pa & 1)) return SCPE_OK +#define ODD_WO(cur) \ + if ((access == WRITEB) && (pa & 1)) cur = cur << 8 +#define ODD_MRG(prv,cur) \ + if (access == WRITEB) cur = \ + ((pa & 1)? (((prv) & 0377) | ((cur) & 0177400)) : \ + (((prv) & 0177400) | ((cur) & 0377))) + +int32 SR = 0; /* switch register */ +int32 DR = 0; /* display register */ +int32 MBRK = 0; /* 11/70 microbreak */ +int32 SYSID = 0x1234; /* 11/70 system ID */ +int32 WCS = 0; /* 11/60 WCS control */ +int32 CPUERR = 0; /* CPU error reg */ +int32 MEMERR = 0; /* memory error reg */ +int32 CCR = 0; /* cache control reg */ +int32 HITMISS = 0; /* hit/miss reg */ +int32 MAINT = 0; /* maint reg */ +int32 JCSR = 0; /* J11 control */ +int32 JCSR_dflt = 0; /* J11 boot ctl def */ +int32 JPCR = 0; /* J11 page ctrl */ +int32 JASR = 0; /* J11 addtl status */ +int32 UDCR = 0; /* UBA diag ctrl */ +int32 UDDR = 0; /* UBA diag data */ +int32 UCSR = 0; /* UBA control */ +int32 uba_last = 0; /* UBA last mapped */ +int32 ub_map[UBM_LNT_LW] = { 0 }; /* UBA map array */ +int32 toy_state = 0; +uint8 toy_data[TOY_LNT] = { 0 }; +static int32 clk_tps_map[4] = { 60, 60, 50, 800 }; + +extern uint16 *M; +extern int32 R[8]; +extern DEVICE cpu_dev, *sim_devices[]; +extern UNIT cpu_unit; +extern FILE *sim_log; +extern int32 STKLIM, PIRQ; +extern uint32 cpu_model, cpu_type, cpu_opt; +extern int32 clk_fie, clk_fnxm, clk_tps, clk_default; +extern int32 sim_switches; + +t_stat CPU24_rd (int32 *data, int32 addr, int32 access); +t_stat CPU24_wr (int32 data, int32 addr, int32 access); +t_stat CPU44_rd (int32 *data, int32 addr, int32 access); +t_stat CPU44_wr (int32 data, int32 addr, int32 access); +t_stat CPU45_rd (int32 *data, int32 addr, int32 access); +t_stat CPU45_wr (int32 data, int32 addr, int32 access); +t_stat CPU60_rd (int32 *data, int32 addr, int32 access); +t_stat CPU60_wr (int32 data, int32 addr, int32 access); +t_stat CPU70_rd (int32 *data, int32 addr, int32 access); +t_stat CPU70_wr (int32 data, int32 addr, int32 access); +t_stat CPUJ_rd (int32 *data, int32 addr, int32 access); +t_stat CPUJ_wr (int32 data, int32 addr, int32 access); +t_stat REG_rd (int32 *data, int32 addr, int32 access); +t_stat REG_wr (int32 data, int32 addr, int32 access); +t_stat SR_rd (int32 *data, int32 addr, int32 access); +t_stat DR_wr (int32 data, int32 addr, int32 access); +t_stat CTLFB_rd (int32 *data, int32 addr, int32 access); +t_stat CTLFB_wr (int32 data, int32 addr, int32 access); +t_stat CTLJB_rd (int32 *data, int32 addr, int32 access); +t_stat CTLJB_wr (int32 data, int32 addr, int32 access); +t_stat CTLJD_rd (int32 *data, int32 addr, int32 access); +t_stat CTLJD_wr (int32 data, int32 addr, int32 access); +t_stat CTLJE_rd (int32 *data, int32 addr, int32 access); +t_stat CTLJE_wr (int32 data, int32 addr, int32 access); +t_stat UBA24_rd (int32 *data, int32 addr, int32 access); +t_stat UBA24_wr (int32 data, int32 addr, int32 access); +t_stat UBAJ_rd (int32 *data, int32 addr, int32 access); +t_stat UBAJ_wr (int32 data, int32 addr, int32 access); +t_stat sys_reset (DEVICE *dptr); +int32 toy_read (void); +void toy_write (int32 bit); +uint8 toy_set (int32 val); +t_stat sys_set_jclk_dflt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sys_show_jclk_dflt (FILE *st, UNIT *uptr, int32 val, void *desc); + +extern t_stat PSW_rd (int32 *data, int32 addr, int32 access); +extern t_stat PSW_wr (int32 data, int32 addr, int32 access); +extern t_stat APR_rd (int32 *data, int32 addr, int32 access); +extern t_stat APR_wr (int32 data, int32 addr, int32 access); +extern t_stat MMR012_rd (int32 *data, int32 addr, int32 access); +extern t_stat MMR012_wr (int32 data, int32 addr, int32 access); +extern t_stat MMR3_rd (int32 *data, int32 addr, int32 access); +extern t_stat MMR3_wr (int32 data, int32 addr, int32 access); +extern t_stat ubm_rd (int32 *data, int32 addr, int32 access); +extern t_stat ubm_wr (int32 data, int32 addr, int32 access); +extern void put_PIRQ (int32 val); + +/* Fixed I/O address table entries */ + +DIB psw_dib = { IOBA_PSW, IOLN_PSW, &PSW_rd, &PSW_wr, 0 }; +DIB cpuj_dib = { IOBA_CPU, IOLN_CPU, &CPUJ_rd, &CPUJ_wr, 0 }; +DIB cpu24_dib = { IOBA_CPU, IOLN_CPU, &CPU24_rd, &CPU24_wr, 0 }; +DIB cpu44_dib = { IOBA_CPU, IOLN_CPU, &CPU44_rd, &CPU44_wr, 0 }; +DIB cpu45_dib = { IOBA_CPU, IOLN_CPU, &CPU45_rd, &CPU45_wr, 0 }; +DIB cpu60_dib = { IOBA_CPU, IOLN_CPU, &CPU60_rd, &CPU60_wr, 0 }; +DIB cpu70_dib = { IOBA_CPU, IOLN_CPU, &CPU70_rd, &CPU70_wr, 0 }; +DIB reg_dib = { IOBA_GPR, IOLN_GPR, ®_rd, ®_wr, 0 }; +DIB ctlfb_dib = { IOBA_CTL, IOLN_CTL, &CTLFB_rd, &CTLFB_wr }; +DIB ctljb_dib = { IOBA_CTL, IOLN_CTL, &CTLJB_rd, &CTLJB_wr }; +DIB ctljd_dib = { IOBA_CTL, IOLN_CTL, &CTLJD_rd, &CTLJD_wr }; +DIB ctlje_dib = { IOBA_CTL, IOLN_CTL, &CTLJE_rd, &CTLJE_wr }; +DIB uba24_dib = { IOBA_UCTL, IOLN_UCTL, &UBA24_rd, &UBA24_wr }; +DIB ubaj_dib = {IOBA_UCTL, IOLN_UCTL, &UBAJ_rd, &UBAJ_wr }; +DIB supv_dib = { IOBA_SUP, IOLN_SUP, &APR_rd, &APR_wr, 0 }; +DIB kipdr_dib = { IOBA_KIPDR, IOLN_KIPDR, &APR_rd, &APR_wr, 0 }; +DIB kdpdr_dib = { IOBA_KDPDR, IOLN_KDPDR, &APR_rd, &APR_wr, 0 }; +DIB kipar_dib = { IOBA_KIPAR, IOLN_KIPAR, &APR_rd, &APR_wr, 0 }; +DIB kdpar_dib = { IOBA_KDPAR, IOLN_KDPAR, &APR_rd, &APR_wr, 0 }; +DIB uipdr_dib = { IOBA_UIPDR, IOLN_UIPDR, &APR_rd, &APR_wr, 0 }; +DIB udpdr_dib = { IOBA_UDPDR, IOLN_UDPDR, &APR_rd, &APR_wr, 0 }; +DIB uipar_dib = { IOBA_UIPAR, IOLN_UIPAR, &APR_rd, &APR_wr, 0 }; +DIB udpar_dib = { IOBA_UDPAR, IOLN_UDPAR, &APR_rd, &APR_wr, 0 }; +DIB sr_dib = { IOBA_SR, IOLN_SR, &SR_rd, NULL, 0 }; +DIB dr_dib = { IOBA_SR, IOLN_SR, NULL, &DR_wr, 0 }; +DIB mmr012_dib = { IOBA_MMR012, IOLN_MMR012, &MMR012_rd, &MMR012_wr, 0 }; +DIB mmr3_dib = { IOBA_MMR3, IOLN_MMR3, &MMR3_rd, &MMR3_wr, 0 }; +DIB ubm_dib = { IOBA_UBM, IOLN_UBM, &ubm_rd, &ubm_wr, 0 }; + +CPUTAB cpu_tab[MOD_MAX] = { + { "11/03", SOP_1103, OPT_1103, MEMSIZE64K, PSW_1103, + 0, 0, 0, 0, 0 }, + { "11/04", SOP_1104, OPT_1104, MEMSIZE64K, PSW_1104, + 0, 0, 0, 0, 0 }, + { "11/05", SOP_1105, OPT_1105, MEMSIZE64K, PSW_1105, + 0, 0, 0, 0, 0 }, + { "11/20", SOP_1120, OPT_1120, MEMSIZE64K, PSW_1120, + 0, 0, 0, 0, 0 }, + { "11/23", SOP_1123, OPT_1123, MAXMEMSIZE, PSW_F, + MFPT_F, PAR_F, PDR_F, MM0_F, MM3_F }, + { "11/23+", SOP_1123P, OPT_1123P, MAXMEMSIZE, PSW_F, + MFPT_F, PAR_F, PDR_F, MM0_F, MM3_F }, + { "11/24", SOP_1124, OPT_1124, MAXMEMSIZE, PSW_F, + MFPT_F, PAR_F, PDR_F, MM0_F, MM3_F }, + { "11/34", SOP_1134, OPT_1134, UNIMEMSIZE, PSW_1134, + 0, PAR_1134, PDR_1134, MM0_1134, 0 }, + { "11/40", SOP_1140, OPT_1140, UNIMEMSIZE, PSW_1140, + 0, PAR_1140, PDR_1140, MM0_1140, 0 }, + { "11/44", SOP_1144, OPT_1144, MAXMEMSIZE, PSW_1144, + MFPT_44, PAR_1144, PDR_1144, MM0_1144, MM3_1144 }, + { "11/45", SOP_1145, OPT_1145, UNIMEMSIZE, PSW_1145, + 0, PAR_1145, PDR_1145, MM0_1145, MM3_1145 }, + { "11/60", SOP_1160, OPT_1160, UNIMEMSIZE, PSW_1160, + 0, PAR_1160, PDR_1160, MM0_1160, 0 }, + { "11/70", SOP_1170, OPT_1170, MAXMEMSIZE, PSW_1170, + 0, PAR_1170, PDR_1170, MM0_1170, MM3_1170 }, + { "11/73", SOP_1173, OPT_1173, MAXMEMSIZE, PSW_J, + MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, + { "11/53", SOP_1153, OPT_1153, MAXMEMSIZE, PSW_J, + MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, + { "11/73B", SOP_1173B, OPT_1173B, MAXMEMSIZE, PSW_J, + MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, + { "11/83", SOP_1183, OPT_1183, MAXMEMSIZE, PSW_J, + MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, + { "11/84", SOP_1184, OPT_1184, MAXMEMSIZE, PSW_J, + MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, + { "11/93", SOP_1193, OPT_1193, MAXMEMSIZE, PSW_J, + MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J }, + { "11/94", SOP_1194, OPT_1194, MAXMEMSIZE, PSW_J, + MFPT_J, PAR_J, PDR_J, MM0_J, MM3_J } + }; + +CNFTAB cnf_tab[] = { + { HAS_PSW, 0, &psw_dib }, /* PSW */ + { CPUT_J, 0, &cpuj_dib }, /* CPU control */ + { CPUT_24, 0, &cpu24_dib }, + { CPUT_44, 0, &cpu44_dib }, + { CPUT_45, 0, &cpu45_dib }, + { CPUT_60, 0, &cpu60_dib }, + { CPUT_70, 0, &cpu70_dib }, + { HAS_IOSR, 0, ®_dib }, + { CPUT_23P, 0, &ctlfb_dib }, /* board ctls */ + { CPUT_JB, 0, &ctljb_dib }, + { CPUT_53, 0, &ctljd_dib }, + { CPUT_JE, 0, &ctlje_dib }, + { CPUT_24, 0, &uba24_dib }, /* UBA */ + { CPUT_JU, 0, &ubaj_dib }, + { 0, OPT_MMU, &kipdr_dib }, /* MMU */ + { 0, OPT_MMU, &kipar_dib }, + { 0, OPT_MMU, &uipdr_dib }, + { 0, OPT_MMU, &uipar_dib }, + { 0, OPT_MMU, &mmr012_dib }, /* MMR0-2 */ + { HAS_MMR3, 0, &mmr3_dib }, /* MMR3 */ + { 0, OPT_UBM, &ubm_dib }, /* Unibus map */ + { HAS_SID, 0, &kdpdr_dib }, /* supv, I/D */ + { HAS_SID, 0, &kdpar_dib }, + { HAS_SID, 0, &supv_dib }, + { HAS_SID, 0, &udpdr_dib }, + { HAS_SID, 0, &udpar_dib }, + { HAS_SR, 0, &sr_dib }, /* SR */ + { HAS_DR, 0, &dr_dib }, /* DR */ + { 0, 0, NULL } + }; + +static const char *opt_name[] = { + "Unibus", "Qbus", "EIS", "NOEIS", "FIS", "NOFIS", + "FPP", "NOFPP", "CIS", "NOCIS", "MMU", "NOMMU", + "RH11", "RH70", "PARITY", "NOPARITY", "Unibus map", "No map", NULL + }; + +static const char *jcsr_val[4] = { + "LINE", "50HZ", "60HZ", "800HZ" + }; + +/* SYSTEM data structures + + sys_dev SYSTEM device descriptor + sys_unit SYSTEM unit descriptor + sys_reg SYSTEM register list +*/ + +UNIT sys_unit = { UDATA (NULL, 0, 0) }; + +REG sys_reg[] = { + { ORDATA (SR, SR, 16) }, + { ORDATA (DR, DR, 16) }, + { ORDATA (MEMERR, MEMERR, 16) }, + { ORDATA (CCR, CCR, 16) }, + { ORDATA (MAINT, MAINT, 16) }, + { ORDATA (HITMISS, HITMISS, 16) }, + { ORDATA (CPUERR, CPUERR, 16) }, + { ORDATA (MBRK, MBRK, 16) }, + { ORDATA (WCS, WCS, 16) }, + { ORDATA (SYSID, SYSID, 16) }, + { ORDATA (JCSR, JCSR, 16) }, + { ORDATA (JCSR_DFLT, JCSR_dflt, 16), REG_HRO }, + { ORDATA (JPCR, JPCR, 16) }, + { ORDATA (JASR, JASR, 16) }, + { ORDATA (UDCR, UDCR, 16) }, + { ORDATA (UDDR, UDDR, 16) }, + { ORDATA (UCSR, UCSR, 16) }, + { ORDATA (ULAST, uba_last, 23) }, + { BRDATA (UBMAP, ub_map, 8, 22, UBM_LNT_LW) }, + { DRDATA (TOY_STATE, toy_state, 6), REG_HRO }, + { BRDATA (TOY_DATA, toy_data, 8, 8, TOY_LNT), REG_HRO }, + { NULL} + }; + +MTAB sys_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "JCLK_DFLT", "JCLK_DFLT", + &sys_set_jclk_dflt, &sys_show_jclk_dflt }, + { 0 } + }; + +DEVICE sys_dev = { + "SYSTEM", &sys_unit, sys_reg, sys_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &sys_reset, + NULL, NULL, NULL, + NULL, 0, 0, + NULL, NULL, NULL + }; + +/* Switch and display registers - many */ + +t_stat SR_rd (int32 *data, int32 pa, int32 access) +{ +*data = SR; +return SCPE_OK; +} + +t_stat DR_wr (int32 data, int32 pa, int32 access) +{ +DR = data; +return SCPE_OK; +} + +/* GPR's - 11/04, 11/05 */ + +t_stat REG_rd (int32 *data, int32 pa, int32 access) +{ +*data = R[pa & 07]; +return SCPE_OK; +} + +t_stat REG_wr (int32 data, int32 pa, int32 access) +{ +int32 reg = pa & 07; + +if (access == WRITE) R[reg] = data; +else if (pa & 1) R[reg] = (R[reg] & 0377) | (data << 8); +else R[reg] = (R[reg] & ~0377) | data; +return SCPE_OK; +} + +/* CPU control registers - 11/24 */ + +t_stat CPU24_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 013: /* CPUERR */ + *data = 0; + return SCPE_OK; + } /* end switch PA */ + +*data = 0; +return SCPE_NXM; /* unimplemented */ +} + +t_stat CPU24_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 013: /* CPUERR */ + return SCPE_OK; + } /* end switch pa */ + +return SCPE_NXM; /* unimplemented */ +} + +/* CPU control registers - 11/44 */ + +t_stat CPU44_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 002: /* MEMERR */ + *data = MEMERR; + return SCPE_OK; + + case 003: /* CCR */ + *data = CCR & CCR44_RD; + return SCPE_OK; + + case 004: /* MAINT */ + *data = MAINT & CMR44_RD; + return SCPE_OK; + + case 005: /* Hit/miss */ + *data = HITMISS; + return SCPE_OK; + + case 006: /* CDR */ + *data = 0; + return SCPE_OK; + + case 013: /* CPUERR */ + if (CPUERR & CPUE_YEL) /* 11/44 stack err */ + CPUERR = (CPUERR & ~CPUE_YEL) | CPUE_RED; /* in <2> not <3> */ + if (CPUERR & (CPUE_ODD|CPUE_NXM|CPUE_TMO)) /* additional flag */ + CPUERR = CPUERR | CPUE44_BUSE; + *data = CPUERR & CPUE_IMP; + return SCPE_OK; + + case 015: /* PIRQ */ + *data = PIRQ; + return SCPE_OK; + } /* end switch PA */ + +*data = 0; +return SCPE_NXM; /* unimplemented */ +} + +t_stat CPU44_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 002: /* MEMERR */ + MEMERR = 0; + return SCPE_OK; + + case 003: /* CCR */ + ODD_MRG (CCR, data); + CCR = data & CCR44_WR; + return SCPE_OK; + + case 004: /* MAINT */ + ODD_MRG (MAINT, data); + MAINT = data & CMR44_WR; + return SCPE_OK; + + case 005: /* Hit/miss */ + return SCPE_OK; + + case 013: /* CPUERR */ + CPUERR = 0; + return SCPE_OK; + + case 015: /* PIRQ */ + ODD_WO (data); + put_PIRQ (data); + return SCPE_OK; + } + +return SCPE_NXM; /* unimplemented */ +} + +/* CPU control registers - 11/45 */ + +t_stat CPU45_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 014: /* MBRK */ + *data = MBRK; + return SCPE_OK; + + case 015: /* PIRQ */ + *data = PIRQ; + return SCPE_OK; + + case 016: /* STKLIM */ + *data = STKLIM & STKLIM_RW; + return SCPE_OK; + } /* end switch PA */ + +*data = 0; +return SCPE_NXM; /* unimplemented */ +} + +t_stat CPU45_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 015: /* PIRQ */ + ODD_WO (data); + put_PIRQ (data); + return SCPE_OK; + + case 016: /* STKLIM */ + ODD_WO (data); + STKLIM = data & STKLIM_RW; + return SCPE_OK; + } /* end switch pa */ + +return SCPE_NXM; /* unimplemented */ +} + +/* CPU control registers - 11/60 */ + +t_stat CPU60_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 000: /* WCS */ + *data = WCS & WCS60_RD; + return SCPE_OK; + + case 002: /* MEMERR */ + *data = MEMERR & MEME60_RD; + return SCPE_OK; + + case 003: /* CCR */ + *data = CCR & CCR60_RD; + return SCPE_OK; + + case 005: /* Hit/miss */ + *data = HITMISS; + return SCPE_OK; + + case 013: /* CPUERR */ + if (CPUERR & CPUE_NXM) /* TMO only */ + CPUERR = (CPUERR & ~CPUE_NXM) | CPUE_TMO; + *data = CPUERR & CPUE60_RD; + return SCPE_OK; + + case 016: /* STKLIM */ + *data = STKLIM & STKLIM_RW; + return SCPE_OK; + } /* end switch PA */ + +*data = 0; +return SCPE_NXM; /* unimplemented */ +} + +t_stat CPU60_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 000: /* WCS */ + WCS = data & WCS60_WR; + return SCPE_OK; + + case 002: /* MEMERR */ + MEMERR = 0; + return SCPE_OK; + + case 003: /* CCR */ + ODD_IGN (data); + CCR = data & CCR60_WR; + return SCPE_OK; + + case 005: /* Hit/miss */ + return SCPE_OK; + + case 013: /* CPUERR */ + CPUERR = 0; + return SCPE_OK; + + case 014: /* MBRK */ + MBRK = data & MBRK60_WR; + return SCPE_OK; + + case 016: /* STKLIM */ + ODD_WO (data); + STKLIM = data & STKLIM_RW; + return SCPE_OK; + } /* end switch pa */ + +return SCPE_NXM; /* unimplemented */ +} + +/* CPU control registers - 11/70 */ + +t_stat CPU70_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 000: /* low error */ + *data = 0; + return SCPE_OK; + + case 001: /* high error */ + *data = 0; + return SCPE_OK; + + case 002: /* MEMERR */ + *data = MEMERR; + return SCPE_OK; + + case 003: /* CCR */ + *data = CCR; + return SCPE_OK; + + case 004: /* MAINT */ + *data = 0; + return SCPE_OK; + + case 005: /* Hit/miss */ + *data = HITMISS; + return SCPE_OK; + + case 010: /* low size */ + *data = (MEMSIZE >> 6) - 1; + return SCPE_OK; + + case 011: /* high size */ + *data = 0; + return SCPE_OK; + + case 012: /* system ID */ + *data = SYSID; + return SCPE_OK; + + case 013: /* CPUERR */ + *data = CPUERR & CPUE_IMP; + return SCPE_OK; + + case 014: /* MBRK */ + *data = MBRK; + return SCPE_OK; + + case 015: /* PIRQ */ + *data = PIRQ; + return SCPE_OK; + + case 016: /* STKLIM */ + *data = STKLIM & STKLIM_RW; + return SCPE_OK; + } /* end switch PA */ + +*data = 0; +return SCPE_NXM; /* unimplemented */ +} + +t_stat CPU70_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 002: /* MEMERR */ + ODD_WO (data); + MEMERR = MEMERR & ~data; + return SCPE_OK; + + case 003: /* CCR */ + ODD_MRG (CCR, data); + CCR = data; + return SCPE_OK; + + case 004: /* MAINT */ + return SCPE_OK; + + case 005: /* Hit/miss */ + return SCPE_OK; + + case 010: /* low size */ + return SCPE_OK; + + case 011: /* high size */ + return SCPE_OK; + + case 013: /* CPUERR */ + CPUERR = 0; + return SCPE_OK; + + case 014: /* MBRK */ + ODD_IGN (data); + MBRK = data & MBRK70_WR; + return SCPE_OK; + + case 015: /* PIRQ */ + ODD_WO (data); + put_PIRQ (data); + return SCPE_OK; + + case 016: /* STKLIM */ + ODD_WO (data); + STKLIM = data & STKLIM_RW; + return SCPE_OK; + } /* end switch pa */ + +return SCPE_NXM; /* unimplemented */ +} + +/* CPU control registers - J11 */ + +t_stat CPUJ_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 002: /* MEMERR */ + *data = MEMERR; + return SCPE_OK; + + case 003: /* CCR */ + *data = CCR; + return SCPE_OK; + + case 004: /* MAINT */ + *data = MAINT | MAINT_NOFPA | MAINT_BPOK | (UNIBUS? MAINT_U: MAINT_Q); + if (CPUT (CPUT_53)) *data |= MAINT_KDJD | MAINT_POROM; + if (CPUT (CPUT_73)) *data |= MAINT_KDJA | MAINT_POODT; + if (CPUT (CPUT_73B|CPUT_83|CPUT_84)) *data |= MAINT_KDJB | MAINT_POROM; + if (CPUT (CPUT_93|CPUT_94)) *data |= MAINT_KDJE | MAINT_POROM; + return SCPE_OK; + + case 005: /* Hit/miss */ + if (CPUT (CPUT_73B)) *data = 0; /* must be 0 for 73B */ + else *data = HITMISS | 010; /* must be nz for 11/8X */ + return SCPE_OK; + + case 013: /* CPUERR */ + *data = CPUERR & CPUE_IMP; + return SCPE_OK; + + case 015: /* PIRQ */ + *data = PIRQ; + return SCPE_OK; + } /* end switch PA */ + +*data = 0; +return SCPE_NXM; /* unimplemented */ +} + +t_stat CPUJ_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 017) { /* decode pa<4:1> */ + + case 002: /* MEMERR */ + MEMERR = 0; + return SCPE_OK; + + case 003: /* CCR */ + ODD_MRG (CCR, data); + CCR = data; + return SCPE_OK; + + case 004: /* MAINT */ + return SCPE_OK; + + case 005: /* Hit/miss */ + return SCPE_OK; + + case 013: /* CPUERR */ + CPUERR = 0; + return SCPE_OK; + + case 015: /* PIRQ */ + ODD_WO (data); + put_PIRQ (data); + return SCPE_OK; + } /* end switch pa */ + +return SCPE_NXM; /* unimplemented */ +} + +/* Board control registers - KDF11B */ + +t_stat CTLFB_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* PCR */ + *data = JPCR & PCRFB_RW; + return SCPE_OK; + + case 1: /* MAINT */ + *data = MAINT; + return SCPE_OK; + + case 2: /* CDR */ + *data = SR & CDRFB_RD; + return SCPE_OK; + } + +*data = 0; +return SCPE_NXM; +} + +t_stat CTLFB_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* PCR */ + ODD_MRG (JPCR, data); + JPCR = data & PCRFB_RW; + return SCPE_OK; + case 1: /* MAINT */ + ODD_MRG (MAINT, data); + MAINT = data; + return SCPE_OK; + case 2: /* CDR */ + ODD_WO (data); + DR = data & CDRFB_WR; + return SCPE_OK; + } + +return SCPE_NXM; +} + +/* Board control registers - KDJ11B */ + +t_stat CTLJB_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* CSR */ + *data = JCSR & CSRJB_RD; + return SCPE_OK; + + case 1: /* PCR */ + *data = JPCR & PCRJB_RW; + return SCPE_OK; + + case 2: /* CDR */ + *data = SR & CDRJB_RD; + return SCPE_OK; + } + +*data = 0; +return SCPE_NXM; +} + +t_stat CTLJB_wr (int32 data, int32 pa, int32 access) +{ +int32 t; + +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* CSR */ + ODD_MRG (JCSR, data); + JCSR = (JCSR & ~CSRJB_WR) | (data & CSRJB_WR); + if (JCSR & CSRJ_LTCI) clk_fie = 1; /* force LTC int enb? */ + else clk_fie = 0; + if (JCSR & CSRJ_LTCD) clk_fnxm = 1; /* force LTC reg nxm? */ + else clk_fnxm = 0; + t = CSRJ_LTCSEL (JCSR); /* get freq sel */ + if (t) clk_tps = clk_tps_map[t]; + else clk_tps = clk_default; + return SCPE_OK; + + case 1: /* PCR */ + ODD_MRG (JPCR, data); + JPCR = data & PCRJB_RW; + return SCPE_OK; + + case 2: /* CDR */ + ODD_WO (data); + DR = data & CDRJB_WR; + return SCPE_OK; + } + +return SCPE_NXM; +} + +/* Board control registers - KDJ11D */ + +t_stat CTLJD_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* CSR */ + *data = JCSR & CSRJD_RD; + return SCPE_OK; + } + +*data = 0; +return SCPE_NXM; +} + +t_stat CTLJD_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* CSR */ + ODD_MRG (JCSR, data); + JCSR = (JCSR & ~CSRJD_WR) | (data & CSRJD_WR); + return SCPE_OK; + } + +return SCPE_NXM; +} + +/* Board control registers - KDJ11E */ + +t_stat CTLJE_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* CSR */ + *data = JCSR & CSRJE_RD; + return SCPE_OK; + + case 1: /* PCR */ + *data = JPCR & PCRJE_RW; + return SCPE_OK; + + case 2: /* CDR */ + *data = SR & CDRJE_RD; + return SCPE_OK; + + case 3: /* ASR */ + JASR = (JASR & ~ASRJE_TOY) | (toy_read () << ASRJE_V_TOY); + *data = JASR & ASRJE_RW; + return SCPE_OK; + } + +*data = 0; +return SCPE_NXM; +} + +t_stat CTLJE_wr (int32 data, int32 pa, int32 access) +{ +int32 t; + +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* CSR */ + ODD_MRG (JCSR, data); + JCSR = (JCSR & ~CSRJE_WR) | (data & CSRJE_WR); + if (JCSR & CSRJ_LTCI) clk_fie = 1; /* force LTC int enb? */ + else clk_fie = 0; + if (JCSR & CSRJ_LTCD) clk_fnxm = 1; /* force LTC reg nxm? */ + else clk_fnxm = 0; + t = CSRJ_LTCSEL (JCSR); /* get freq sel */ + if (t) clk_tps = clk_tps_map[t]; + else clk_tps = clk_default; + return SCPE_OK; + + case 1: /* PCR */ + ODD_MRG (JPCR, data); + JPCR = data & PCRJE_RW; + return SCPE_OK; + + case 2: /* CDR */ + ODD_WO (data); + DR = data & CDRJE_WR; + return SCPE_OK; + + case 3: /* ASR */ + ODD_MRG (JASR, data); + JASR = data & ASRJE_RW; + toy_write (ASRJE_TOYBIT (JASR)); + return SCPE_OK; + } + +return SCPE_NXM; +} + +/* Unibus adapter registers - KT24 */ + +t_stat UBA24_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 2: /* LMAL */ + *data = uba_last & LMAL_RD; + return SCPE_OK; + case 3: /* LMAH */ + *data = uba_last & LMAH_RD; + return SCPE_OK; + } + +*data = 0; +return SCPE_NXM; +} + +t_stat UBA24_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 3: /* ASR */ + ODD_IGN (data); + uba_last = (uba_last & ~LMAH_WR) | ((data & LMAH_WR) << 16); + return SCPE_OK; + } + +return SCPE_NXM; +} + +/* Unibus registers - KTJ11B */ + +t_stat UBAJ_rd (int32 *data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* DCR */ + *data = UDCR & DCRKTJ_RD; + return SCPE_OK; + + case 1: /* DDR */ + *data = UDDR & DDRKTJ_RW; + return SCPE_OK; + + case 2: /* CSR */ + *data = UCSR & MCRKTJ_RD; + return SCPE_OK; + } + +*data = 0; +return SCPE_NXM; +} + +t_stat UBAJ_wr (int32 data, int32 pa, int32 access) +{ +switch ((pa >> 1) & 03) { /* decode pa<2:1> */ + + case 0: /* DCR */ + ODD_MRG (UDCR, data); + UDCR = (UDCR & ~DCRKTJ_WR) | (data & DCRKTJ_WR); + return SCPE_OK; + + case 1: /* DDR */ + ODD_MRG (UDDR, data); + UDDR = data & DDRKTJ_RW;; + return SCPE_OK; + + case 2: /* CSR */ + ODD_MRG (UCSR, data); + UCSR = (UCSR & ~MCRKTJ_WR) | (data & MCRKTJ_WR); + return SCPE_OK; + } + +return SCPE_NXM; +} + +/* KDJ11E TOY routines */ + +int32 toy_read (void) +{ +time_t curr; +struct tm *ctm; +int32 bit; + +if (toy_state == 0) { + curr = time (NULL); /* get curr time */ + if (curr == (time_t) -1) return 0; /* error? */ + ctm = localtime (&curr); /* decompose */ + if (ctm == NULL) return 0; /* error? */ + toy_data[TOY_HSEC] = 0x50; + toy_data[TOY_SEC] = toy_set (ctm->tm_sec); + toy_data[TOY_MIN] = toy_set (ctm->tm_min); + toy_data[TOY_HR] = toy_set (ctm->tm_hour); + toy_data[TOY_DOW] = toy_set (ctm->tm_wday); + toy_data[TOY_DOM] = toy_set (ctm->tm_mday); + toy_data[TOY_MON] = toy_set (ctm->tm_mon + 1); + toy_data[TOY_YR] = toy_set (ctm->tm_year % 100); + } +bit = toy_data[toy_state >> 3] >> (toy_state & 07); +toy_state = (toy_state + 1) % (TOY_LNT * 8); +return (bit & 1); +} + +void toy_write (int32 bit) +{ +toy_state = 0; +return; +} + +uint8 toy_set (int32 val) +{ +uint32 d1, d2; + +d1 = val / 10; +d2 = val % 10; +return (uint8) ((d1 << 4) | d2); +} + +/* Build I/O space entries for CPU */ + +t_stat cpu_build_dib (void) +{ +int32 i; +t_stat r; + +for (i = 0; cnf_tab[i].dib != NULL; i++) { /* loop thru config tab */ + if (((cnf_tab[i].cpum == 0) || (cpu_type & cnf_tab[i].cpum)) && + ((cnf_tab[i].optm == 0) || (cpu_opt & cnf_tab[i].optm))) { + if (r = build_ubus_tab (&cpu_dev, cnf_tab[i].dib)) /* add to dispatch tab */ + return r; + } + } +return SCPE_OK; +} + +/* Set/show CPU model */ + +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr != NULL) return SCPE_ARG; +if (val >= MOD_MAX) return SCPE_IERR; +if (val == (int32) cpu_model) return SCPE_OK; +if (MEMSIZE > cpu_tab[val].maxm) + cpu_set_size (uptr, cpu_tab[val].maxm, NULL, NULL); +if (MEMSIZE > cpu_tab[val].maxm) return SCPE_INCOMP; +cpu_model = val; +cpu_type = 1u << cpu_model; +cpu_opt = cpu_tab[cpu_model].std; +cpu_set_bus (cpu_opt); +reset_all (0); /* reset world */ +return SCPE_OK; +} + +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i, all_opt; + +fprintf (st, "%s", cpu_tab[cpu_model].name); +all_opt = cpu_tab[cpu_model].opt; +for (i = 0; opt_name[2 * i] != NULL; i++) { + if ((all_opt >> i) & 1) fprintf (st, ", %s", + ((cpu_opt >> i) & 1)? opt_name[2 * i]: opt_name[(2 * i) + 1]); + } +return SCPE_OK; +} + +/* Set/clear CPU option */ + +t_stat cpu_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val & cpu_tab[cpu_model].opt) == 0) return SCPE_ARG; +cpu_opt = cpu_opt | val; +return SCPE_OK; +} + +t_stat cpu_clr_opt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val & cpu_tab[cpu_model].opt) == 0) return SCPE_ARG; +cpu_opt = cpu_opt & ~val; +return SCPE_OK; +} + +/* Memory allocation */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i, clim; +uint16 *nM; + +if ((val <= 0) || (val > (int32) cpu_tab[cpu_model].maxm) || + ((val & 07777) != 0)) return SCPE_ARG; +for (i = val; i < MEMSIZE; i = i + 2) mc = mc | M[i >> 1]; +if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; +nM = (uint16 *) calloc (val >> 1, sizeof (uint16)); +if (nM == NULL) return SCPE_MEM; +clim = (((t_addr) val) < MEMSIZE)? val: MEMSIZE; +for (i = 0; i < clim; i = i + 2) nM[i >> 1] = M[i >> 1]; +free (M); +M = nM; +MEMSIZE = val; +if (!(sim_switches & SIM_SW_REST)) /* unless restore, */ + cpu_set_bus (cpu_opt); /* alter periph config */ +return SCPE_OK; +} + +/* Bus configuration, disable Unibus or Qbus devices */ + +t_stat cpu_set_bus (int32 opt) +{ +DEVICE *dptr; +uint32 i, mask; + +if (opt & BUS_U) mask = DEV_UBUS; /* Unibus variant? */ +else if (MEMSIZE <= UNIMEMSIZE) /* 18b Qbus devices? */ + mask = DEV_QBUS | DEV_Q18; +else mask = DEV_QBUS; /* must be 22b */ +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + if ((dptr->flags & DEV_DISABLE) && /* disable-able? */ + !(dptr->flags & DEV_DIS) && /* enabled? */ + ((dptr->flags & mask) == 0)) { /* not allowed? */ + printf ("Disabling %s\n", sim_dname (dptr)); + if (sim_log) fprintf (sim_log, "Disabling %s\n", sim_dname (dptr)); + dptr->flags = dptr->flags | DEV_DIS; + } + } +return SCPE_OK; +} + +/* System reset */ + +t_stat sys_reset (DEVICE *dptr) +{ +int32 i; + +CCR = 0; +HITMISS = 0; +CPUERR = 0; +MEMERR = 0; +if (!CPUT (CPUT_J)) MAINT = 0; +MBRK = 0; +WCS = 0; +if (CPUT (CPUT_JB|CPUT_JE)) + JCSR = JCSR_dflt; +else JCSR = 0; +JPCR = 0; +JASR = 0; +UDCR = 0; +UDDR = 0; +UCSR = 0; +uba_last = 0; +DR = 0; +toy_state = 0; +for (i = 0; i < UBM_LNT_LW; i++) ub_map[i] = 0; +for (i = 0; i < TOY_LNT; i++) toy_data[i] = 0; +return SCPE_OK; +} + +/* Set/show JCLK default values */ + +t_stat sys_set_jclk_dflt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i; + +if ((CPUT (CPUT_JB|CPUT_JE)) && cptr) { + for (i = 0; i < 4; i++) { + if (strncmp (cptr, jcsr_val[i], strlen (cptr)) == 0) { + JCSR_dflt = i << CSRJ_V_LTCSEL; + return SCPE_OK; + } + } + } +return SCPE_ARG; +} + +t_stat sys_show_jclk_dflt (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (CPUT (CPUT_JB|CPUT_JE)) + fprintf (st, "JCLK default=%s\n", jcsr_val[CSRJ_LTCSEL (JCSR_dflt)]); +else fprintf (st, "Not implemented\n"); +return SCPE_OK; +} diff --git a/PDP11/pdp11_cpumod.h b/PDP11/pdp11_cpumod.h new file mode 100644 index 0000000..b185131 --- /dev/null +++ b/PDP11/pdp11_cpumod.h @@ -0,0 +1,301 @@ +/* pdp11_cpumod.h: PDP-11 CPU model definitions + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 22-Apr-08 RMS Added 11/70 MBRK register + 30-Aug-05 RMS Added additional 11/60 registers +*/ + +#ifndef _PDP11_CPUMOD_H_ +#define _PDP11_CPUMOD_H_ 0 + +#define SOP_1103 (BUS_Q) +#define OPT_1103 (OPT_EIS|OPT_FIS) +#define PSW_1103 0000377 + +#define SOP_1104 (BUS_U) +#define OPT_1104 0 +#define PSW_1104 0000377 + +#define SOP_1105 (BUS_U) +#define OPT_1105 0 +#define PSW_1105 0000377 + +#define SOP_1120 (BUS_U) +#define OPT_1120 0 +#define PSW_1120 0000377 + +#define SOP_1123 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1123 (OPT_FPP|OPT_CIS) +#define PSW_F 0170777 +#define PAR_F 0177777 +#define PDR_F 0077516 +#define MM0_F 0160157 +#define MM3_F 0000060 + +#define SOP_1123P (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1123P (OPT_FPP|OPT_CIS) + +#define SOP_1124 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM) +#define OPT_1124 (OPT_FPP|OPT_CIS) + +#define SOP_1134 (BUS_U|OPT_EIS|OPT_MMU) +#define OPT_1134 (OPT_FPP) +#define PSW_1134 0170377 +#define PAR_1134 0007777 +#define PDR_1134 0077516 +#define MM0_1134 0160557 + +#define SOP_1140 (BUS_U|OPT_EIS|OPT_MMU) +#define OPT_1140 (OPT_FIS) +#define PSW_1140 0170377 +#define PAR_1140 0007777 +#define PDR_1140 0077516 +#define MM0_1140 0160557 + +#define SOP_1144 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM) +#define OPT_1144 (OPT_FPP|OPT_CIS) +#define PSW_1144 0170777 +#define PAR_1144 0177777 +#define PDR_1144 0177516 +#define MM0_1144 0160557 +#define MM3_1144 0000077 + +#define SOP_1145 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_RH11) +#define OPT_1145 (OPT_FPP) +#define PSW_1145 0174377 +#define PAR_1145 0007777 +#define PDR_1145 0077717 +#define MM0_1145 0171777 +#define MM3_1145 0000007 + +#define SOP_1160 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1160 0 +#define PSW_1160 0170377 +#define PAR_1160 0007777 +#define PDR_1160 0077516 +#define MM0_1160 0160557 + +#define SOP_1170 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM) +#define OPT_1170 (OPT_FPP|OPT_RH11) +#define PSW_1170 0174377 +#define PAR_1170 0177777 +#define PDR_1170 0077717 +#define MM0_1170 0171777 +#define MM3_1170 0000067 + +#define SOP_1173 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1173 (OPT_CIS) +#define PSW_J 0174777 +#define PAR_J 0177777 +#define PDR_J 0177516 +#define MM0_J 0160177 +#define MM3_J 0000077 + +#define SOP_1153 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1153 (OPT_CIS) + +#define SOP_1173B (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1173B (OPT_CIS) + +#define SOP_1183 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1183 (OPT_CIS) + +#define SOP_1184 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM|OPT_RH11) +#define OPT_1184 (OPT_CIS) + +#define SOP_1193 (BUS_Q|OPT_EIS|OPT_FPP|OPT_MMU) +#define OPT_1193 (OPT_CIS) + +#define SOP_1194 (BUS_U|OPT_EIS|OPT_FPP|OPT_MMU|OPT_UBM|OPT_RH11) +#define OPT_1194 (OPT_CIS) + +#define MOD_MAX 20 + +/* MFPT codes */ + +#define MFPT_44 1 +#define MFPT_F 3 +#define MFPT_T 4 +#define MFPT_J 5 + +/* KDF11B specific register */ + +#define PCRFB_RW 0037477 /* page ctrl reg */ + +#define CDRFB_RD 0000377 /* config reg */ +#define CDRFB_WR 0000017 + +/* KT24 Unibus map specific registers */ + +#define LMAL_RD 0177777 /* last mapped low */ + +#define LMAH_RD 0000177 /* last mapped high */ +#define LMAH_WR 0000100 + +/* 11/44 specific registers */ + +#define CCR44_RD 0033315 /* cache control */ +#define CCR44_WR 0003315 + +#define CMR44_RD 0177437 /* cache maint */ +#define CMR44_WR 0000037 + +#define CPUE44_BUSE 0004000 + +/* 11/60 specific registers */ + +#define WCS60_RD 0161776 /* WCS control */ +#define WCS60_WR 0061676 + +#define MEME60_RD 0100340 /* memory error */ + +#define CCR60_RD 0000315 /* cache control */ +#define CCR60_WR 0000115 + +#define MBRK60_WR 0007777 /* microbreak */ + +#define CPUE60_RD (CPUE_ODD|CPUE_TMO|CPUE_RED) + +/* 11/70 specific registers */ + +#define MBRK70_WR 0000377 /* microbreak */ + +/* J11 specific registers */ + +/* Maintenance register */ + +#define MAINT_V_UQ 9 /* Q/U flag */ +#define MAINT_Q (0 << MAINT_V_UQ) /* Qbus */ +#define MAINT_U (1 << MAINT_V_UQ) +#define MAINT_V_FPA 8 /* FPA flag */ +#define MAINT_NOFPA (0 << MAINT_V_FPA) +#define MAINT_FPA (1 << MAINT_V_FPA) +#define MAINT_V_TYP 4 /* system type */ +#define MAINT_KDJA (1 << MAINT_V_TYP) /* KDJ11A */ +#define MAINT_KDJB (2 << MAINT_V_TYP) /* KDJ11B */ +#define MAINT_KDJD (4 << MAINT_V_TYP) /* KDJ11D */ +#define MAINT_KDJE (5 << MAINT_V_TYP) /* KDJ11E */ +#define MAINT_V_HTRAP 3 /* trap 4 on HALT */ +#define MAINT_HTRAP (1 << MAINT_V_HTRAP) +#define MAINT_V_POM 1 /* power on option */ +#define MAINT_POODT (0 << MAINT_V_POM) /* power up ODT */ +#define MAINT_POROM (2 << MAINT_V_POM) /* power up ROM */ +#define MAINT_V_BPOK 0 /* power OK */ +#define MAINT_BPOK (1 << MAINT_V_BPOK) + +/* KDJ11B control */ + +#define CSRJB_RD 0177767 +#define CSRJB_WR 0037767 +#define CSRJ_LTCI 0020000 /* force LTC int */ +#define CSRJ_LTCD 0010000 /* disable LTC reg */ +#define CSRJ_V_LTCSEL 10 +#define CSRJ_M_LTCSEL 03 +#define CSRJ_LTCSEL(x) (((x) >> CSRJ_V_LTCSEL) & CSRJ_M_LTCSEL) +#define CSRJ_HBREAK 0001000 /* halt on break */ + +#define PCRJB_RW 0077176 /* page ctrl reg */ + +#define CDRJB_RD 0000377 /* config register */ +#define CDRJB_WR 0000377 + +/* KDJ11D control */ + +#define CSRJD_RD 0157777 /* native register */ +#define CSRJD_WR 0000377 +#define CSRJD_15M 0040000 /* 1.5M mem on board */ + +/* KDJ11E control */ + +#define CSRJE_RD 0137360 /* control reg */ +#define CSRJE_WR 0037370 + +#define PCRJE_RW 0177376 /* page ctrl reg */ + +#define CDRJE_RD 0000377 /* config register */ +#define CDRJE_WR 0000077 + +#define ASRJE_RW 0030462 /* additional status */ +#define ASRJE_V_TOY 8 +#define ASRJE_TOY (1u << ASRJE_V_TOY) /* TOY serial bit */ +#define ASRJE_TOYBIT(x) (((x) >> ASRJE_V_TOY) & 1) + +/* KDJ11E TOY clock */ + +#define TOY_HSEC 0 +#define TOY_SEC 1 +#define TOY_MIN 2 +#define TOY_HR 3 +#define TOY_DOW 4 +#define TOY_DOM 5 +#define TOY_MON 6 +#define TOY_YR 7 +#define TOY_LNT 8 + +/* KTJ11B Unibus map */ + +#define DCRKTJ_RD 0100616 /* diag control */ +#define DCRKTJ_WR 0000416 + +#define DDRKTJ_RW 0177777 /* diag data */ + +#define MCRKTJ_RD 0000377 /* control register */ +#define MCRKTJ_WR 0000177 + +/* Data tables */ + +struct cpu_table { + char *name; /* model name */ + uint32 std; /* standard flags */ + uint32 opt; /* set/clear flags */ + uint32 maxm; /* max memory */ + uint32 psw; /* PSW mask */ + uint32 mfpt; /* MFPT code */ + uint32 par; /* PAR mask */ + uint32 pdr; /* PDR mask */ + uint32 mm0; /* MMR0 mask */ + uint32 mm3; /* MMR3 mask */ + }; + +typedef struct cpu_table CPUTAB; + +struct conf_table { + uint32 cpum; + uint32 optm; + DIB *dib; + }; + +typedef struct conf_table CNFTAB; + +/* Prototypes */ + +t_stat cpu_set_model (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_opt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_clr_opt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_bus (int32 opt); + +#endif diff --git a/PDP11/pdp11_cr.c b/PDP11/pdp11_cr.c new file mode 100644 index 0000000..981fedf --- /dev/null +++ b/PDP11/pdp11_cr.c @@ -0,0 +1,1284 @@ +/* pdp11_cr.c: CR/CM/CD-11 card reader simulator + + Copyright (c) 2005-2007, John A. Dundas III + Portions derived from work by Douglas W. Jones, jones@cs.uiowa.edu + Portions derived from work by Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the Author shall + not be used in advertising or otherwise to promote the sale, use + or other dealings in this Software without prior written + authorization from the Author. + + ------------------------------------------------------------------------------ + + cr CR11/CD11 punched and mark sense card reader for SIMH + The CR11 controller is also compatible with the CM11-F, CME11, and CMS11. + + Information necessary to create this simulation was gathered from + a number of sources including: + + CR11 Card Reader System Manual, DEC-11-HCRB-D + http://www.bitsavers.org/pdf/dec/unibus/DEC-11-HCRB-D_CR11_Mar72.pdf + Various editions of the Peripherals Handbook + OpenVMS VAX Card Reader, Line Printer, and LPA11-K I/O User's + Reference Manual, AA-PVXGA-TE + http://h71000.www7.hp.com/DOC/73final/documentation/pdf/OVMS_VAX_CARD_LP_REF.pdf + OpenVMS System Manager's Manual, Volume 1: Essentials + http://h71000.www7.hp.com/DOC/732FINAL/aa-pv5mh-tk/aa-pv5mh-tk.PDF + CRDRIVER.LIS - CR11 Card Reader Driver, X-9, graciously made available + by HP + Various RSTS manuals + RT-11 Software Support Manual + RT-11 System Reference Manual, DEC-11-ORUGA-C-D + Professor Douglas W. Jones's web site: + http://www.cs.uiowa.edu/~jones/cards/ + Paul Mattes' x026 keypunch simulator + http://x3270.bgp.nu/x026.html + CD2SER.MAC - TOPS card reader driver source + http://pdp-10.trailing-edge.com/custsupcuspmar86_bb-x130b-sb/02/cd2ser.mac + + The Card Image format code and documentation is adapted from Prof. + Jones's site, with his permission. Please see his site for additional + documentation as well as the card image utilities referenced in + his documentation (cardmake, cardlist, etc.). + http://www.cs.uiowa.edu/~jones/cards/format.html + + Known limitations: + 1. Need a copy of the CR bootstrap (and some way to test it) + 2. Need a copy of the XXDP+ test deck + 3. No testing under RSX; volunteers needed + 4. No testing under Ultrix or Unix for PDP-11; volunteers needed + 5. No testing under Ultrix or Unix for VAX; volunteers needed + 6. The simulator implements a single controller/reader combination + + Operating System Notes + + RT-11 (and CTS-300) support one CR11 or CM11, but no CD11. + + VMS supports multiple CR11 controllers, but no CD11. + + RSTS/E supports either the CR11/CM11 or CD11 but not both in + the same SIL. It appears to support only one unit. + + For RSX there exists a CR/CM task handler. Is there a CD + handler? + + Don't have any information about Unix or Ultrix-11 yet. Same + for VAX Unices. + + TOPS: only the CD11 is supported, under the name CD20. + + Revision History: + + 01-Feb-07 RMS Added PDP-10 support + 12-May-06 JAD Modify the DEBUG code to use the SIMH DEBUG_x + macros. Modify the UNIT structure to include + the DEBUG bit. + Mark the trans[] array contents constant. + Make device data structures static and constant + as appropriate. + 18-Mar-05 JAD Slight optimization for blank punches recognizing + that blank is 0 in all character encodings. + 17-Mar-05 JAD Completely initialize ascii_code correctly. + Define the end of deck punch code separately from + the cardcode.i file. + Make initTranslation() set a pointer to the correct + punch code table to use. Modify card read functions + to use this table pointer. + 16-Mar-05 JAD Make certain switches passed to the ATTACH command + are valid; return error on any others. + Make default unit wait time compatible with default + device specification. + Implement SET TRANSLATION=value. Still need to + modify the H2ASCII table used for text files; + currently hard-coded to 029. + 24-Feb-05 JAD Allow the maintenance bits in CRM to clear as + well as set status bits. Not sure this is the + correct behavior, though, without more documentation. + Catch three more places to spin down the blower + correctly. + Zero the CDDB and CRM at INIT. + 17-Feb-05 JAD When the hopper empties, a pick check should + be generated 300ms later. They are simultaneous + for now. + Make sure readColumnBinary() generates a complete + EOF card. + 08-Feb-05 JAD Replace blowerWait with different times for blower + spin up and down. + 06-Feb-05 JAD After DETACH: mark CD offline, set appropriate + blower state. + Make sure unit wait time is recalculated every + time cpm is set. + 04-Feb-05 JAD Better tracking of blower state throughout driver. + Make sure IE gets cleared for CR at INIT. + Normalize error response in read routines. + Finish condition handling for column binary. + 02-Feb-05 JAD Remove Qbus support; Unibus only. + Support ATTACH switches: + A - ASCII, B - column binary, I - Card Image + If none given, check for .TXT or .CBN; if none, + examine file for magic header. + Finer granularity to blower state. Expose this + variable to examine/deposit from SIMH. + Preliminary implementation of support for + column binary format. + 24-Jan-05 JAD Make AUTOEOF work as a surrogate for the EOF + button of a CD11 reader. May need to separate + this later, though. + Partial implementation of DATAERR for CD11. + Implement the Rev. J mods (as best I understand + them) to the CD11 affecting the CDDB used as a + second status register. + 23-Jan-05 JAD Preliminary clean-up of CD state transitions. + Tested with RSTS/E (V9.1-05). + 22-Jan-05 JAD Finish CR state transitions; should be close now. + Tested with RSTS/E (V9.1-05), RT-11 (V5.3), and + VAX/VMS (V7.2). + 19-Jan-05 JAD Add bounds to the RATE command; also default and + help a la the XQ driver. + Improved handling of empty files. + 17-Jan-05 JAD Add the CR maintenance register. + 16-Jan-05 JAD Add preliminary CD11 support. + Simulate the STOP and RESET switches. + 14-Jan-05 JAD Add the ability to automatically generate an 'EOF' + card recognized by DEC operating systems when + reading ASCII files. + 08-Jan-05 JAD Original creation and testing +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +extern int32 int_req; +#define DFLT_DIS (DEV_DIS) +#define DFLT_CR11 (0) /* CD11 only */ +#define DFLT_CPM 1000 + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +extern int32 int_req[IPL_HLVL]; +#define DFLT_DIS (0) +#define DFLT_CR11 (UNIT_CR11) +#define DFLT_CPM 285 + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +extern int32 int_req[IPL_HLVL]; +#define DFLT_DIS (0) +#define DFLT_CR11 (UNIT_CR11) +#define DFLT_CPM 285 +#endif + +extern FILE *sim_deb; /* sim_console.c */ + +/* create a int32 constant from four characters */ +#define I4C(a,b,c,d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) +#define I4C_CBN I4C ('C','B','N',' ') +#define I4C_H80 I4C ('H','8','0',' ') +#define I4C_H82 I4C ('H','8','2',' ') +#define I4C_H40 I4C ('H','4','0',' ') + +#define UNIT_V_CR11 (UNIT_V_UF + 0) +#define UNIT_CR11 (1u << UNIT_V_CR11) +#define UNIT_V_AUTOEOF (UNIT_V_UF + 1) +#define UNIT_AUTOEOF (1u << UNIT_V_AUTOEOF) + +#include +#define ERROR (00404) +#include "pdp11_cr_dat.h" +#define PUNCH_EOD (07417) +#define PUNCH_SPACE (0) /* same for all encodings */ + +/* CR */ +/* also use CSR_ERR, CSR_IE, and CSR_GO */ +#define CRCSR_V_CRDDONE 14 /* card done */ +#define CRCSR_V_SUPPLY 13 /* supply error */ +#define CRCSR_V_RDCHK 12 /* reader check */ +#define CRCSR_V_TIMERR 11 /* timing error */ +#define CRCSR_V_ONLINE 10 /* on line */ +#define CRCSR_V_BUSY 9 /* busy reading */ +#define CRCSR_V_OFFLINE 8 /* off line AKA READY? */ +#define CRCSR_V_COLRDY 7 /* column ready */ +#define CRCSR_V_EJECT 1 /* ignore card */ + +#define CRCSR_CRDDONE (1u << CRCSR_V_CRDDONE) +#define CRCSR_SUPPLY (1u << CRCSR_V_SUPPLY) +#define CRCSR_RDCHK (1u << CRCSR_V_RDCHK) +#define CRCSR_TIMERR (1u << CRCSR_V_TIMERR) +#define CRCSR_ONLINE (1u << CRCSR_V_ONLINE) +#define CRCSR_BUSY (1u << CRCSR_V_BUSY) +#define CRCSR_OFFLINE (1u << CRCSR_V_OFFLINE) +#define CRCSR_COLRDY (1u << CRCSR_V_COLRDY) +#define CRCSR_EJECT (1u << CRCSR_V_EJECT) + +#define CRCSR_IMP (CSR_ERR | CRCSR_CRDDONE | CRCSR_SUPPLY | \ + CRCSR_RDCHK | CRCSR_TIMERR | CRCSR_ONLINE | \ + CRCSR_BUSY | CRCSR_OFFLINE | CRCSR_COLRDY | \ + CSR_IE | CRCSR_EJECT) +#define CRCSR_RW (CSR_IE | CRCSR_EJECT | CSR_GO) /* read/write */ + +#define CRM_V_MAINT 15 /* enable maint funct */ +#define CRM_V_BUSY 14 +#define CRM_V_READY 13 +#define CRM_V_HOPPER 12 + +#define CRM_MAINT (1u << CRM_V_MAINT) +#define CRM_BUSY (1u << CRM_V_BUSY) +#define CRM_READY (1u << CRM_V_READY) +#define CRM_HOPPER (1u << CRM_V_HOPPER) + +/* CD */ +/* also use CSR_ERR, CSR_IE, and CSR_GO */ +#define CDCSR_V_RDRCHK 14 /* reader check */ +#define CDCSR_V_EOF 13 /* CD11-E EOF button */ +#define CDCSR_V_OFFLINE 12 /* off line */ +#define CDCSR_V_DATAERR 11 /* data error */ +#define CDCSR_V_LATE 10 /* data late */ +#define CDCSR_V_NXM 9 /* non-existent memory */ +#define CDCSR_V_PWRCLR 8 /* power clear */ +#define CDCSR_V_RDY 7 /* ready */ +#define CDCSR_V_XBA17 5 +#define CDCSR_V_XBA16 4 +#define CDCSR_V_ONLINE 3 /* on line transition */ +#define CDCSR_V_HOPPER 2 /* hopper check */ +#define CDCSR_V_PACK 1 /* data packing */ + +#define CDCSR_RDRCHK (1u << CDCSR_V_RDRCHK) +#define CDCSR_EOF (1u << CDCSR_V_EOF) +#define CDCSR_OFFLINE (1u << CDCSR_V_OFFLINE) +#define CDCSR_DATAERR (1u << CDCSR_V_DATAERR) +#define CDCSR_LATE (1u << CDCSR_V_LATE) +#define CDCSR_NXM (1u << CDCSR_V_NXM) +#define CDCSR_PWRCLR (1u << CDCSR_V_PWRCLR) +#define CDCSR_RDY (1u << CDCSR_V_RDY) +#define CDCSR_XBA17 (1u << CDCSR_V_XBA17) +#define CDCSR_XBA16 (1u << CDCSR_V_XBA16) +#define CDCSR_ONLINE (1u << CDCSR_V_ONLINE) +#define CDCSR_HOPPER (1u << CDCSR_V_HOPPER) +#define CDCSR_PACK (1u << CDCSR_V_PACK) + +#define CDCSR_IMP (CSR_ERR | CDCSR_RDRCHK | CDCSR_EOF | CDCSR_OFFLINE | \ + CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM | \ + CDCSR_PWRCLR | CDCSR_RDY | CSR_IE | \ + CDCSR_XBA17 | CDCSR_XBA16 | CDCSR_ONLINE | \ + CDCSR_HOPPER | CDCSR_PACK | CSR_GO) + +#define CDCSR_RW (CDCSR_PWRCLR | CSR_IE | CDCSR_XBA17 | CDCSR_XBA16 | \ + CDCSR_PACK | CSR_GO) + +/* Blower state values */ +#define BLOW_OFF (0) /* steady state off */ +#define BLOW_START (1) /* starting up */ +#define BLOW_ON (2) /* steady state on */ +#define BLOW_STOP (3) /* shutting down */ + +/* Card Reader state */ +static char *cardFormat = "unknown"; +static t_bool (*readRtn)(FILE *, int16 *, char *, char *); +static char ascii_code[4096]; /* 2^12 possible values */ +static int currCol; /* current column when reading */ +static int colStart; /* starting column */ +static int colEnd; /* ending column */ +static int table = 3; /* character translation table */ +static const int *codeTbl = o29_code; /* punch translation table */ +static int32 blowerState = BLOW_OFF; /* reader vacuum/blower motor */ +static int32 spinUp = 3000; /* blower spin-up time: 3 seconds */ +static int32 spinDown = 2000; /* blower spin-down time: 2 seconds */ +static t_bool EOFcard = FALSE; /* played special card yet? */ +static int32 cpm = DFLT_CPM; /* reader rate: cards per minute */ +/* card image in various formats */ +static int16 hcard[82]; /* Hollerith format */ +static char ccard[82]; /* DEC compressed format */ +static char acard[82]; /* ASCII format */ +/* CR/CM registers */ +static int32 crs = 0; /* control/status */ +static int32 crb1 = 0; /* 12-bit Hollerith characters */ +static int32 crb2 = 0; /* 8-bit compressed characters */ +static int32 crm = 0; /* CMS maintenance register */ +/* CD registers */ +static int32 cdst = 0; /* control/status */ +static int32 cdcc = 0; /* column count */ +static int32 cdba = 0; /* current address, low 16 bits */ +static int32 cddb = 0; /* data, 2nd status */ + +/* forward references */ +DEVICE cr_dev; +static void setupCardFile (UNIT *, int32); +t_stat cr_rd (int32 *, int32, int32); +t_stat cr_wr (int32, int32, int32); +t_stat cr_svc (UNIT *); +t_stat cr_reset (DEVICE *); +t_stat cr_attach (UNIT *, char *); +t_stat cr_detach (UNIT *); +t_stat cr_set_type (UNIT *, int32, char *, void *); +t_stat cr_show_format (FILE *, UNIT *, int32, void *); +t_stat cr_set_rate (UNIT *, int32, char *, void *); +t_stat cr_show_rate (FILE *, UNIT *, int32, void *); +t_stat cr_set_reset (UNIT *, int32, char *, void *); +t_stat cr_set_stop (UNIT *, int32, char *, void *); +t_stat cr_set_trans (UNIT *, int32, char*, void *); +t_stat cr_show_trans (FILE *, UNIT *, int32, void *); + +/* CR data structures + + cr_dib CR device information block + cr_unit CR unit descriptor + cr_reg CR register list + cr_mod CR modifier table + cr_dev CR device descriptor +*/ + +static DIB cr_dib = { IOBA_CR, IOLN_CR, &cr_rd, &cr_wr, + 1, IVCL (CR), VEC_CR, { NULL } }; + +static UNIT cr_unit = { + UDATA (&cr_svc, + UNIT_ATTABLE+UNIT_SEQ+UNIT_ROABLE+UNIT_DISABLE+ + DFLT_CR11+UNIT_AUTOEOF, 0), + (60 * 1000) / DFLT_CPM }; + +static const REG cr_reg[] = { + { GRDATA (BUF, cr_unit.buf, DEV_RDX, 8, 0) }, + { GRDATA (CRS, crs, DEV_RDX, 16, 0) }, + { GRDATA (CRB1, crb1, DEV_RDX, 16, 0) }, + { GRDATA (CRB2, crb2, DEV_RDX, 16, 0) }, + { GRDATA (CRM, crm, DEV_RDX, 16, 0) }, + { GRDATA (CDST, cdst, DEV_RDX, 16, 0) }, + { GRDATA (CDCC, cdcc, DEV_RDX, 16, 0) }, + { GRDATA (CDBA, cdba, DEV_RDX, 16, 0) }, + { GRDATA (CDDB, cddb, DEV_RDX, 16, 0) }, + { GRDATA (BLOWER, blowerState, DEV_RDX, 2, 0) }, + { FLDATA (INT, IREQ (CR), INT_V_CR) }, + { FLDATA (ERR, crs, CSR_V_ERR) }, + { FLDATA (IE, crs, CSR_V_IE) }, + { DRDATA (POS, cr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, cr_unit.wait, 24), PV_LEFT }, + { GRDATA (DEVADDR, cr_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, cr_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } }; + +static const MTAB cr_mod[] = { +#if defined (VM_PDP11) + { UNIT_CR11, UNIT_CR11, "CR11", "CR11", &cr_set_type }, + { UNIT_CR11, 0, "CD11", "CD11", &cr_set_type }, +#else + { UNIT_CR11, UNIT_CR11, "CR11", NULL }, + { UNIT_CR11, 0, "CD11", NULL }, +#endif + { UNIT_AUTOEOF, UNIT_AUTOEOF, "auto EOF", "AUTOEOF", NULL }, + { UNIT_AUTOEOF, 0, "no auto EOF", "NOAUTOEOF", NULL }, + /* card reader RESET switch */ + { MTAB_XTD|MTAB_VDV, 0, NULL, "RESET", + &cr_set_reset, NULL, NULL }, + /* card reader STOP switch */ + { MTAB_XTD|MTAB_VDV, 0, NULL, "STOP", + &cr_set_stop, NULL, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", NULL, + NULL, &cr_show_format, NULL }, + { MTAB_XTD|MTAB_VDV, 006, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "RATE", "RATE={DEFAULT|200..1200}", + &cr_set_rate, &cr_show_rate, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "TRANSLATION", + "TRANSLATION={DEFAULT|026|026FTN|029|EBCDIC}", + &cr_set_trans, &cr_show_trans, NULL }, + { 0 } }; + +DEVICE cr_dev = { + "CR", &cr_unit, (REG *) &cr_reg, (MTAB *) &cr_mod, + 1, 10, 31, 1, DEV_RDX, 8, + NULL, NULL, &cr_reset, + NULL, &cr_attach, &cr_detach, + &cr_dib, DEV_DISABLE | DFLT_DIS | DEV_UBUS | DEV_DEBUG }; + +/* Utility routines */ + +/* +These functions read a "card" from a virtual deck file (passed in +fp) and fill in three arrays. The first array 'hcard' contains the +12-bit binary image of the punch in each column; the second array +'ccard' contains the 8-bit DEC encoded representation of the +corresponding column; the third array 'acard' contains the ASCII +representation (if possible) of the character. The routines return +TRUE if a card was read (possibly with errors) and FALSE if the +"hopper is empty" (EOF) or fatal file errors prevented any portion +of a card from being read. + +Errors other than EOF are signaled out of band in the controller +state variables. Possible errors are data in columns 0 or 81 +(signalled as read check; currently these columns are ignored), or +any file errors (signalled as motion check). + +Might rethink this. Should probably treat file errors as "pick +check". Retry 3 times. After that, give up with error. + +*/ + +static t_bool readCardImage ( FILE *fp, + int16 *hcard, + char *ccard, + char *acard ) +{ + int c1, c2, c3, col; + + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "readCardImage pos %d\n", ftell (fp)); + /* get card header bytes */ + c1 = fgetc (fp); + c2 = fgetc (fp); + c3 = fgetc (fp); + cr_unit.pos = ftell (fp); + /* check for EOF */ + if (c1 == EOF) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "hopper empty\n"); + if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { + EOFcard = TRUE; + for (col = 1; col <= 8; col++) { + hcard[col] = PUNCH_EOD; + ccard[col] = h2c_code[PUNCH_EOD]; + acard[col] = ' '; + } + while (col <= colEnd) { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } + return (TRUE); + } + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + if (cr_unit.flags & UNIT_AUTOEOF) + cdst |= CDCSR_EOF; + blowerState = BLOW_STOP; + return (FALSE); + } + /* check for valid header */ + if ((c2 == EOF) || (c3 == EOF) || ((c1 & 0x80) == 0) || + ((c2 & 0x80) == 0) || ((c3 & 0x80) == 0)) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "header error\n"); + /* unexpected EOF or format problems */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + return (FALSE); + } + assert (colStart < colEnd); + assert (colStart >= 0); + assert (colEnd <= 81); + for (col = colStart; col < colEnd; ) { + int16 i; + /* get 3 bytes */ + c1 = fgetc (fp); + c2 = fgetc (fp); + c3 = fgetc (fp); + cr_unit.pos = ftell (fp); + if (ferror (fp) || feof (fp)) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "file error\n"); +/* signal error; unexpected EOF, format problems, or file error(s) */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + return (FALSE); + } + /* convert to 2 columns */ + i = ((c1 << 4) | ( c2 >> 4)) & 0xFFF; + hcard[col] = i; + ccard[col] = h2c_code[i]; + acard[col] = ascii_code[i]; + col++; + + i = (((c2 & 017) << 8) | c3) & 0xFFF; + hcard[col] = i; + ccard[col] = h2c_code[i]; + acard[col] = ascii_code[i]; + col++; + } + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "successfully loaded card\n"); + return (TRUE); +} + +static t_bool readColumnBinary ( FILE *fp, + int16 *hcard, + char *ccard, + char *acard ) +{ + int col; + + for (col = colStart; col <= colEnd; col++) { + int16 i; + i = fgetc (fp) & 077; + i |= ((fgetc (fp) & 077) << 6); + cr_unit.pos = ftell (fp); + if (feof (fp)) { + if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { + EOFcard = TRUE; + for (col = 1; col <= 8; col++) { + hcard[col] = PUNCH_EOD; + ccard[col] = h2c_code[PUNCH_EOD]; + acard[col] = ' '; + } + while (col <= colEnd) { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } + return (TRUE); + } + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | + CRCSR_OFFLINE; + crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + if (cr_unit.flags & UNIT_AUTOEOF) + cdst |= CDCSR_EOF; + blowerState = BLOW_STOP; + return (FALSE); + } + if (ferror (fp)) { + /* signal error */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + return (FALSE); + } + hcard[col] = i; + ccard[col] = h2c_code[i]; + acard[col] = ascii_code[i]; + } + return (TRUE); +} + +/* + +Should this routine perform special handling of non-printable, +(e.g., control) characters or characters that have no encoded +representation? + +*/ + +static t_bool readCardASCII ( FILE *fp, + int16 *hcard, + char *ccard, + char *acard ) +{ + int c, col; + + assert (colStart < colEnd); + assert (colStart >= 1); + assert (colEnd <= 80); + + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "readCardASCII\n"); + for (col = colStart; col <= colEnd; ) { + switch (c = fgetc (fp)) { + case EOF: + if (ferror (fp)) { + /* signal error */ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_OFFLINE; + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK; + blowerState = BLOW_STOP; + cr_unit.pos = ftell (fp); + return (FALSE); + } + if (col == colStart) { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "hopper empty\n"); + if (!EOFcard && (cr_unit.flags & UNIT_AUTOEOF)) { + EOFcard = TRUE; + for (col = 1; col <= 8; col++) { + hcard[col] = PUNCH_EOD; + ccard[col] = h2c_code[PUNCH_EOD]; + acard[col] = ' '; + } + c = '\n'; + goto fill_card; + } + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + crs &= ~(CRCSR_COLRDY | CRCSR_ONLINE); + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + if (cr_unit.flags & UNIT_AUTOEOF) + cdst |= CDCSR_EOF; + blowerState = BLOW_STOP; + cr_unit.pos = ftell (fp); + return (FALSE); + } + /* fall through */ + case '\r': + case '\n': + fill_card: + while (col <= colEnd) { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } + break; + case '\t': + do { + hcard[col] = PUNCH_SPACE; + ccard[col] = PUNCH_SPACE; + acard[col] = ' '; + col++; + } while (((col & 07) != 1) && (col <= colEnd)); + break; + default: + hcard[col] = codeTbl[c & 0177]; + /* check for unrepresentable ASCII characters */ + if (hcard[col] == ERROR) { + cdst |= CDCSR_DATAERR; + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, + "error character at column %d\n", + col); + } + ccard[col] = h2c_code[hcard[col]]; + acard[col] = c; + col++; + break; + } + } + /* silently truncate/flush long lines, or flag over-length card? */ + if (c != '\n') { + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "truncating card\n"); + do c = fgetc (fp); + while ((c != EOF) && (c != '\n') && (c != '\r')); + } + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "successfully loaded card\n"); + cr_unit.pos = ftell (fp); + return (TRUE); +} + +/* + +Initialize the binary translation table. Generally called when a +new deck is attached but could be set manually as well. + +*/ + +static void initTranslation (void) +{ + int32 i; + + memset (ascii_code, '~', sizeof (ascii_code)); + switch (table) { + case 1: + codeTbl = o26_comm_code; + for (i = ' '; i < '`'; i++) + ascii_code[o26_comm_code[i]] = i; + break; + case 2: + codeTbl = o26_ftn_code; + for (i = ' '; i < '`'; i++) + ascii_code[o26_ftn_code[i]] = i; + break; + case 3: + codeTbl = o29_code; + for (i = ' '; i < '`'; i++) + ascii_code[o29_code[i]] = i; + break; + case 4: + codeTbl = EBCDIC_code; + for (i = 0; i < 0177; i++) + ascii_code[EBCDIC_code[i]] = i; + break; + default: + /* can't happen */ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, + "bad CR translation initialization value\n"); + break; + } +} + +/* + +Examine the command switches, file extension, and virtual card deck +file to determine the format. Set up the global variables +appropriately. Rewind ASCII files to the beginning + +*/ + +static void setupCardFile ( UNIT *uptr, + int32 switches ) +{ + int32 i; + + if (switches & SWMASK ('A')) + i = 0; + else if (switches & SWMASK ('B')) + i = I4C_CBN; + else if (switches & SWMASK ('I')) + goto read_header; + else if (match_ext (uptr->filename, "TXT")) + i = 0; + else if (match_ext (uptr->filename, "CBN")) + i = I4C_CBN; + else { +read_header: + /* look for card image magic file number */ + i = fgetc (uptr->fileref); + i = (i << 8) | fgetc (uptr->fileref); + i = (i << 8) | fgetc (uptr->fileref); + i = (i << 8) | ' '; + } + switch (i) { + case I4C_H80: + colStart = 1; + colEnd = 80; + cardFormat = "card image"; + readRtn = readCardImage; + break; + case I4C_H82: + colStart = 0; + colEnd = 81; + cardFormat = "card image"; + readRtn = readCardImage; + break; + case I4C_H40: + colStart = 1; + colEnd = 40; + cardFormat = "card image"; + readRtn = readCardImage; + break; + case I4C_CBN: + colStart = 1; + colEnd = 80; + cardFormat = "column binary"; + readRtn = readColumnBinary; + break; + default: + colStart = 1; + colEnd = 80; + cardFormat = "ASCII"; + readRtn = readCardASCII; + fseek (uptr->fileref, 0L, SEEK_SET); + break; + } + initTranslation (); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "colStart = %d, colEnd = %d\n", + colStart, colEnd); + cr_unit.pos = ftell (uptr->fileref); +} + +/* Card reader routines + + cr_rd I/O page read + cr_wr I/O page write + cr_svc process event (reader ready) + cr_reset process reset + cr_attach process attach + cr_detach process detach +*/ + +t_stat cr_rd ( int32 *data, + int32 PA, + int32 access ) +{ + switch ((PA >> 1) & 03) { + case 0: /* CSR */ + if (cdst & (077000)) + cdst |= CSR_ERR; + else + cdst &= ~CSR_ERR; + *data = (cr_unit.flags & UNIT_CR11) ? + crs & CRCSR_IMP : cdst & CDCSR_IMP; + /* CR: if error removed, clear 15, 14, 11, 10 */ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_rd crs %06o cdst %06o\n", + crs, cdst); + break; + case 1: + *data = (cr_unit.flags & UNIT_CR11) ? crb1 : cdcc; + /* Does crb1 clear after read? Implied by VMS driver. */ + crb1 = 0; + crs &= ~CRCSR_COLRDY; + if (DEBUG_PRS (cr_dev)) { + if (cr_unit.flags & UNIT_CR11) + fprintf (sim_deb, "cr_rd crb1 %06o '%c' %d\n", + crb1, cr_unit.buf, cr_unit.buf); + else + fprintf (sim_deb, "cr_rd cdcc %06o\n", cdcc); + } + break; + case 2: + *data = (cr_unit.flags & UNIT_CR11) ? crb2 : cdba; + crb2 = 0; /* see note for crb1 */ + crs &= ~CRCSR_COLRDY; + if (DEBUG_PRS (cr_dev)) { + if (cr_unit.flags & UNIT_CR11) + fprintf (sim_deb, "cr_rd crb2 %06o\n", crb2); + else + fprintf (sim_deb, "\r\ncr_rd cdba %06o\n", cdba); + } + break; + case 3: + default: + if (cr_unit.flags & UNIT_CR11) + *data = crm; + else + *data = 0100000 | (cdst & CDCSR_RDRCHK) | + (cdst & CDCSR_OFFLINE) ? + cddb & 0777 : 0777; + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_rd crm %06o cddb %06o data %06o\n", + crm, cddb, *data); + break; + } + return (SCPE_OK); +} + +t_stat cr_wr ( int32 data, + int32 PA, + int32 access ) +{ + switch ((PA >> 1) & 03) { + case 0: + if (cr_unit.flags & UNIT_CR11) { + /* ignore high-byte writes */ + if (PA & 1) + break; + /* fixup data for low byte write */ + if (access == WRITEB) + data = (crs & ~0377) | (data & 0377); + if (!(data & CSR_IE)) + CLR_INT (CR); + crs = (crs & ~CRCSR_RW) | (data & CRCSR_RW); + crs &= ~(CSR_ERR | CRCSR_CRDDONE | CRCSR_TIMERR); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr data %06o crs %06o\n", + data, crs); + if (data & CSR_GO) { + if (blowerState != BLOW_ON) { + sim_activate (&cr_unit, spinUp); + blowerState = BLOW_START; + } else + sim_activate (&cr_unit, cr_unit.wait); + } + } else { + if (data & CDCSR_PWRCLR) { + CLR_INT (CR); + sim_cancel (&cr_unit); + cdst &= ~(CDCSR_RDRCHK |CDCSR_OFFLINE | + CDCSR_RDY | CDCSR_HOPPER); + cdst |= CDCSR_RDY; + cdcc = 0; + cdba = 0; + break; + } + if (!(data & CSR_IE)) + CLR_INT (CR); + cdst = (cdst & ~CDCSR_RW) | (data & CDCSR_RW); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr data %06o cdst %06o\n", + data, cdst); + if (data & CSR_GO) { + if (blowerState != BLOW_ON) { + sim_activate (&cr_unit, spinUp); + blowerState = BLOW_START; + } else + sim_activate (&cr_unit, cr_unit.wait); + } + } + break; + case 1: + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr cdcc %06o\n", data); + if (cr_unit.flags & UNIT_CR11) + break; + cdcc = data & 0177777; + break; + case 2: + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr crba %06o\n", data); + if (cr_unit.flags & UNIT_CR11) + break; + cdba = data & 0177777; + break; + case 3: + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_wr cddb/crm %06o\n", data); + /* ignore writes to cddb */ + if (!(cr_unit.flags & UNIT_CR11)) + break; + /* fixup data for byte writes and read-modify-write */ + if (access == WRITEB) + data = (PA & 1) ? + (crm & 0377) | (data << 8) : + (crm & ~0377) | (data & 0377); + crm = data & 0177777; + /* not 100% certain how these work */ + if (!(crm & CRM_MAINT)) + break; + crs = (crm & CRM_BUSY) ? + (crs | CRCSR_BUSY) : (crs & ~CRCSR_BUSY); + crs = (crm & CRM_READY) ? + (crs | CRCSR_OFFLINE) : (crs & ~CRCSR_OFFLINE); + crs = (crm & CRM_HOPPER) ? + (crs | CRCSR_SUPPLY | CRCSR_RDCHK) : + (crs & ~(CRCSR_SUPPLY | CRCSR_RDCHK)); + crb1 = crm & 07777; /* load low 12 bits */ + break; + default: + /* can't happen */ + break; + } + return (SCPE_OK); +} + +/* +Enter the service routine once for each column read from the card. +CR state bits drive this primarily (see _BUSY and _CRDDONE). However, +when in CD mode, also execute one column of DMA input. + +*/ + +t_stat cr_svc ( UNIT *uptr ) +{ + uint32 pa; + uint8 c; + uint16 w; + + if (blowerState == BLOW_STOP) { + blowerState = BLOW_OFF; + return (SCPE_OK); + } + if (blowerState == BLOW_START) + blowerState = BLOW_ON; + /* (almost) anything we do now will cause a CR interrupt */ + if (crs & CSR_IE) + SET_INT (CR); + if (!(uptr->flags & UNIT_ATT) || (crs & CSR_ERR) || (cdst & CSR_ERR)) + return (SCPE_OK); + if ((crs & CRCSR_BUSY) && (currCol > colEnd)) { + crs &= ~(CRCSR_BUSY | CSR_GO | CRCSR_COLRDY); + crs |= CRCSR_CRDDONE; + if (cdst & (CDCSR_DATAERR | CDCSR_LATE | CDCSR_NXM)) + cdst |= CSR_ERR; + if (cdst & CSR_IE) + SET_INT (CR); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_svc card done\n"); + return (SCPE_OK); + } + if (!(crs & CRCSR_BUSY)) { + /* try to read a card */ + /* crs &= ~CRCSR_CRDDONE; */ + if (!readRtn (uptr->fileref, hcard, ccard, acard)) { + sim_activate (uptr, spinDown); + return (SCPE_OK); + } + currCol = colStart; + crs |= CRCSR_BUSY; /* indicate reader busy */ + } + /* check for overrun (timing error) */ + if ((uptr->flags & UNIT_CR11) && (crs & CRCSR_COLRDY)) + crs |= CSR_ERR | CRCSR_TIMERR; + crb1 = hcard[currCol] & 07777; + crb2 = ccard[currCol] & 0377; + uptr->buf = acard[currCol] & 0377; /* helpful for debugging */ + if (!(uptr->flags & UNIT_CR11)) { + pa = cdba | ((cdst & 060) << 12); +/* +The implementation of _NXM here is not quite the same as I interpret +the (limited) documentaiton I have to indicate. However the effect +should be similar. Documentation indicates that once _NXM is set, +further NPR requests are inhibited though the card is allowed to +read until completion. This implies that CDBA and the XBA bits are +incremented accordingly, even though no data transfer occurs. This +code detects and flags the NXM condition but allows attempts at +subsequent memory writes, thus insuring the address registers are +incremented properly. If this causes problems, I'll fix it. +*/ + if (cdst & CDCSR_PACK) { + c = cddb = ccard[currCol] & 0377; + if (Map_WriteB (pa, 1, &c)) + cdst |= CDCSR_NXM; + pa = (pa + 1) & 0777777; + } else { + w = cddb = hcard[currCol] & 07777; + if (Map_WriteW (pa, 2, &w)) + cdst |= CDCSR_NXM; + pa = (pa + 2) & 0777777; + } + cdba = pa & 0177777; + cdst = (cdst & ~(CDCSR_XBA17|CDCSR_XBA16)) | + ((pa & 0600000) >> 12); + cdcc = (cdcc + 1) & 0177777; +#if 0 + if (!(cdst & CSR_IE) && !(crs & CRCSR_CRDDONE)) + CLR_INT (CR); +#endif + } + currCol++; /* advance the column counter */ + if (!(crs & CRCSR_EJECT)) + crs |= CRCSR_COLRDY; + else + CLR_INT (CR); + sim_activate (uptr, uptr->wait); + return (SCPE_OK); +} + +t_stat cr_reset ( DEVICE *dptr ) +{ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_reset\n"); + cr_unit.buf = 0; + currCol = 1; + crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_TIMERR|CRCSR_ONLINE|CRCSR_BUSY| + CRCSR_COLRDY|CSR_IE|CRCSR_EJECT|CSR_GO); + crb1 = 0; + crb2 = 0; + crm = 0; + cdst &= ~(CSR_ERR|CDCSR_RDRCHK|CDCSR_EOF|CDCSR_DATAERR|CDCSR_LATE| + CDCSR_NXM|CSR_IE|CDCSR_XBA17|CDCSR_XBA16|CDCSR_ONLINE| + CDCSR_PACK|CSR_GO); + cdst |= CDCSR_RDY; + cdcc = 0; + cdba = 0; + cddb = 0; + if ((cr_unit.flags & UNIT_ATT) && !feof (cr_unit.fileref)) { + crs |= CRCSR_ONLINE; /* non-standard */ + crs &= ~(CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE); + cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); + } else { + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + crs = CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + } + sim_cancel (&cr_unit); /* deactivate unit */ + if (blowerState != BLOW_OFF) { + blowerState = BLOW_STOP; + sim_activate (&cr_unit, spinDown); + } + EOFcard = FALSE; + CLR_INT (CR); + /* TBD: flush current card */ + /* init uptr->wait ? */ + return (SCPE_OK); +} + +/* +Handle the interface status and SIMH portion of the ATTACH. Another +routine is used to evaluate the file and initialize other state +globals correctly. +*/ + +#define MASK (SWMASK('A')|SWMASK('B')|SWMASK('I')|SWMASK('R')) + +t_stat cr_attach ( UNIT *uptr, + char *cptr ) +{ + t_stat reason; + extern int32 sim_switches; + + if (sim_switches & ~MASK) + return (SCPE_INVSW); + /* file must previously exist; kludge */ + sim_switches |= SWMASK ('R'); + reason = attach_unit (uptr, cptr); + if (!(uptr->flags & UNIT_ATT)) { + crs &= ~CRCSR_ONLINE; + crs |= CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY; + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER; + } else { + setupCardFile (uptr, sim_switches); + crs |= CRCSR_ONLINE; + crs &= ~(CSR_ERR | CRCSR_OFFLINE | CRCSR_RDCHK | CRCSR_SUPPLY); + cdst &= ~(CDCSR_RDRCHK | CDCSR_HOPPER); + EOFcard = FALSE; + } + return (reason); +} + +t_stat cr_detach ( UNIT *uptr ) +{ + crs |= CSR_ERR | CRCSR_RDCHK | CRCSR_SUPPLY | CRCSR_OFFLINE; + /* interrupt? */ + crs &= ~CRCSR_ONLINE; + cdst |= CSR_ERR | CDCSR_RDRCHK | CDCSR_HOPPER | CDCSR_OFFLINE; + cardFormat = "unknown"; + if (blowerState != BLOW_OFF) { + blowerState = BLOW_STOP; + sim_activate (uptr, spinDown); + } + return (detach_unit (uptr)); +} + +t_stat cr_set_type ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + /* disallow type change if currently attached */ + if (uptr->flags & UNIT_ATT) + return (SCPE_NOFNC); + cpm = (val & UNIT_CR11) ? 285 : 1000; + uptr->wait = (60 * 1000) / cpm; + return (SCPE_OK); +} + +t_stat cr_show_format ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + fprintf (st, "%s format", cardFormat); + return (SCPE_OK); +} + +t_stat cr_set_rate ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + t_stat status = SCPE_OK; + int32 i; + + if (!cptr) + return (SCPE_MISVAL); + if (strcmp (cptr, "DEFAULT") == 0) + i = (uptr->flags & UNIT_CR11) ? 285 : 1000; + else + i = (int32) get_uint (cptr, 10, 0xFFFFFFFF, &status); + if (status == SCPE_OK) { + if (i < 200 || i > 1200) + status = SCPE_ARG; + else { + cpm = i; + uptr->wait = (60 * 1000) / cpm; + } + } + return (status); +} + +t_stat cr_show_rate ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + fprintf (st, "%d cards per minute", cpm); + return (SCPE_OK); +} + +/* simulate pressing the card reader RESET button */ + +t_stat cr_set_reset ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_set_reset\n"); +/* +Ignore the RESET switch while a read cycle is in progress or the +unit simply is not attached. +*/ + if ((crs & CRCSR_BUSY) || !(uptr->flags & UNIT_ATT)) + return (SCPE_OK); + /* if no errors, signal transition to on line */ + crs |= CRCSR_ONLINE; + crs &= ~(CSR_ERR|CRCSR_CRDDONE|CRCSR_SUPPLY|CRCSR_RDCHK|CRCSR_TIMERR| + CRCSR_BUSY|CRCSR_COLRDY|CRCSR_EJECT|CSR_GO); + cdst |= CDCSR_ONLINE; + cdst &= ~(CSR_ERR | CDCSR_OFFLINE | CDCSR_RDRCHK | CDCSR_HOPPER | + CDCSR_EOF); + if ((crs & CSR_IE) || (cdst & CSR_IE)) { + SET_INT (CR); + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "cr_set_reset setting interrupt\n"); + } + /* start up the blower if the hopper is not empty */ + if (blowerState != BLOW_ON) + blowerState = BLOW_START; + return (SCPE_OK); +} + +/* simulate pressing the card reader STOP button */ + +t_stat cr_set_stop ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + if (DEBUG_PRS (cr_dev)) + fprintf (sim_deb, "set_stop\n"); + crs &= ~CRCSR_ONLINE; + crs |= CSR_ERR | CRCSR_OFFLINE; + cdst |= CDCSR_OFFLINE; + /* CD11 does not appear to interrupt on STOP. */ + if (crs & CSR_IE) + SET_INT (CR); + if (blowerState != BLOW_OFF) { + blowerState = BLOW_STOP; + /* set timer to turn it off completely */ + sim_activate (uptr, spinDown); + } + return (SCPE_OK); +} + +static const char * const trans[] = { + "unknown", "026", "026FTN", "029", "EBCDIC" +}; + +t_stat cr_set_trans ( UNIT *uptr, + int32 val, + char *cptr, + void *desc ) +{ + int i; + + if (!cptr) + return (SCPE_MISVAL); + if (strcmp (cptr, "DEFAULT") == 0) + i = 3; + else { + for (i = 1; i < 5; i++) { + if (strcmp (cptr, trans[i]) == 0) + break; + } + } + if (i < 1 || i > 4) + return (SCPE_ARG); + table = i; + initTranslation (); /* reinitialize tables */ + return (SCPE_OK); +} + +t_stat cr_show_trans ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + fprintf (st, "translation %s", trans[table]); + return (SCPE_OK); +} diff --git a/PDP11/pdp11_cr_dat.h b/PDP11/pdp11_cr_dat.h new file mode 100644 index 0000000..18aaa4d --- /dev/null +++ b/PDP11/pdp11_cr_dat.h @@ -0,0 +1,622 @@ +/* pdp11_cr_dat.h + * + * card code arrays are indexed by 7-bit ASCII code, and + * give corresponding 12-bit card codes using the indicated + * collating sequence. + * + * ERROR should be externally defined, either as an illegal + * card code (on conversion from ASCII to card codes) or as + * a code with a bit set outside the least significant 12. + * + * author: Douglas Jones, jones@cs.uiowa.edu + * revisions: + * March 5, 1996 + * Feb 18, 1997 to add 026 and EBCDIC converstion tables + * Jan 10, 2005, (JAD) Added 'static const' to the array + * definitions. + * Jan 11, 2005, (JAD) Create the h2c_code array. + * Jan 14, 2005, (JAD) Added the special DEC code for 'end of deck' + * (12-11-0-1-6-7-8-9) to the o29_code array at position 26. (^Z). + * Should I add this to the other arrays? + */ + +/* DEC's version of the IBM 029 kepunch encoding, (thus avoiding IBM's + use of non-ASCII punctuation), based on that given in the appendix + to Digital's "Small Computer Handbook, 1973", and augmented to + translate lower case to upper case. As a result of this modification, + inversion of this table should be done with care! */ +static const int o29_code[] = { + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,07417,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + 00000,02202,00006,00102,02102,01042,04000,00022, /* !"#$%&' */ + 04022,02022,02042,04012,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,00202,02012,04042,00012,01012,01006, /* 89:;<=>? */ + 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,04202,02006,01202,04006,01022, /* XYZ[\]^_ */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +/* Bare bones 026 kepunch encodings */ +static const int o26_ftn_code[] = { + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + 00000,ERROR,ERROR,ERROR,02102,ERROR,ERROR,00042, /* !"#$%&' */ + 01042,04042,02042,04000,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,ERROR,ERROR,ERROR,00102,ERROR,ERROR, /* 89:;<=>? */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR, /* XYZ[\]^_ */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +static const int o26_comm_code[] = { + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* chars */ + 00000,ERROR,ERROR,00102,02102,01042,04000,ERROR, /* !"#$%&' */ + ERROR,ERROR,02042,ERROR,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,ERROR,ERROR,04042,ERROR,ERROR,ERROR, /* 89:;<=>? */ + 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR, /* XYZ[\]^_ */ + ERROR,04400,04200,04100,04040,04020,04010,04004, /* `abcdefg */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* hijklmno */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* pqrstuvw */ + 01004,01002,01001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +/* FULL EBCDIC, from Appendix C of System 360 Programming by Alex Thomas, + 1977, Reinhart Press, San Francisco. Codes not in that table have been + left compatable with DEC's 029 table. Some control codes have been + left out */ +static const int EBCDIC_code[] = { + 05403,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + 02011,04021,01021,ERROR,04041,02021,ERROR,ERROR, /* chars */ + ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR,ERROR, /* control */ + ERROR,ERROR,ERROR,ERROR,01201,ERROR,ERROR,ERROR, /* chars */ + 00000,02202,00006,00102,02102,01042,04000,00022, /* !"#$%&' */ + 04022,02022,02042,04012,01102,02000,04102,01400, /* ()*+,-./ */ + 01000,00400,00200,00100,00040,00020,00010,00004, /* 01234567 */ + 00002,00001,00202,02012,04042,00012,01012,01006, /* 89:;<=>? */ + 00042,04400,04200,04100,04040,04020,04010,04004, /* @ABCDEFG */ + 04002,04001,02400,02200,02100,02040,02020,02010, /* HIJKLMNO */ + 02004,02002,02001,01200,01100,01040,01020,01010, /* PQRSTUVW */ + 01004,01002,01001,04202,02006,01202,04006,01022, /* XYZ[\]^_ */ + ERROR,05400,05200,05100,05040,05020,05010,05004, /* `abcdefg */ + 05002,05001,06400,06200,06100,06040,06020,06010, /* hijklmno */ + 06004,06002,06001,03200,03100,03040,03020,03010, /* pqrstuvw */ + 03004,03002,03001,ERROR,ERROR,ERROR,ERROR,ERROR /* xyz{|}~ */ + }; + +static const int h2c_code[4096] = { + 0000, 0020, 0010, 0030, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0004, 0024, 0014, 0034, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0002, 0022, 0012, 0032, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0006, 0026, 0016, 0036, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0001, 0021, 0011, 0031, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0005, 0025, 0015, 0035, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0003, 0023, 0013, 0033, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0007, 0027, 0017, 0037, 0007, 0027, 0017, 0037, + 0040, 0060, 0050, 0070, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0044, 0064, 0054, 0074, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0042, 0062, 0052, 0072, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0046, 0066, 0056, 0076, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0041, 0061, 0051, 0071, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0045, 0065, 0055, 0075, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0043, 0063, 0053, 0073, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0047, 0067, 0057, 0077, 0047, 0067, 0057, 0077, + 0100, 0120, 0110, 0130, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0104, 0124, 0114, 0134, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0102, 0122, 0112, 0132, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0106, 0126, 0116, 0136, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0101, 0121, 0111, 0131, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0105, 0125, 0115, 0135, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0103, 0123, 0113, 0133, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0107, 0127, 0117, 0137, 0107, 0127, 0117, 0137, + 0140, 0160, 0150, 0170, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0144, 0164, 0154, 0174, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0142, 0162, 0152, 0172, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0146, 0166, 0156, 0176, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0141, 0161, 0151, 0171, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0145, 0165, 0155, 0175, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0143, 0163, 0153, 0173, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0147, 0167, 0157, 0177, 0147, 0167, 0157, 0177, + 0200, 0220, 0210, 0230, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0204, 0224, 0214, 0234, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0202, 0222, 0212, 0232, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0206, 0226, 0216, 0236, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0201, 0221, 0211, 0231, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0205, 0225, 0215, 0235, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0203, 0223, 0213, 0233, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0207, 0227, 0217, 0237, 0207, 0227, 0217, 0237, + 0240, 0260, 0250, 0270, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0244, 0264, 0254, 0274, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0242, 0262, 0252, 0272, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0246, 0266, 0256, 0276, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0241, 0261, 0251, 0271, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0245, 0265, 0255, 0275, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0243, 0263, 0253, 0273, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0247, 0267, 0257, 0277, 0247, 0267, 0257, 0277, + 0300, 0320, 0310, 0330, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0304, 0324, 0314, 0334, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0302, 0322, 0312, 0332, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0306, 0326, 0316, 0336, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0301, 0321, 0311, 0331, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0305, 0325, 0315, 0335, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0303, 0323, 0313, 0333, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0307, 0327, 0317, 0337, 0307, 0327, 0317, 0337, + 0340, 0360, 0350, 0370, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0344, 0364, 0354, 0374, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0342, 0362, 0352, 0372, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0346, 0366, 0356, 0376, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0341, 0361, 0351, 0371, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0345, 0365, 0355, 0375, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0343, 0363, 0353, 0373, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, + 0347, 0367, 0357, 0377, 0347, 0367, 0357, 0377, +}; diff --git a/PDP11/pdp11_dc.c b/PDP11/pdp11_dc.c new file mode 100644 index 0000000..743c039 --- /dev/null +++ b/PDP11/pdp11_dc.c @@ -0,0 +1,651 @@ +/* pdp11_dc.c: PDP-11 DC11 multiple terminal interface simulator + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dci,dco DC11 terminal input/output + + The simulator supports both hardwired and modem-like behavior. If modem + control is not enabled, carrier detect, ring, and carrier change are + never set. +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "DC11 is not supported on the PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#error "DC11 is not supported on the VAX!" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif +#include "sim_sock.h" +#include "sim_tmxr.h" + +#define DCX_MASK (DCX_LINES - 1) + +/* Parity and modem control */ + +#define DCX_V_OPAR (TTUF_V_UF + 0) +#define DCX_V_EPAR (TTUF_V_UF + 1) +#define DCX_V_MDM (TTUF_V_UF + 2) +#define DCX_OPAR (1u << DCX_V_OPAR) +#define DCX_EPAR (1u << DCX_V_EPAR) +#define DCX_MDM (1u << DCX_V_MDM) + +/* registers */ + +#define DCICSR_RD 0173777 +#define DCICSR_WR 0003533 +#define DCICSR_DTR 0000001 /* DTR (RW) */ +#define DCICSR_XBR 0000002 /* xmit brk (RWNI) */ +#define DCICSR_CDT 0000004 /* car det (RO) */ +#define DCICSR_PAR 0000040 /* odd par (RO) */ +#define DCICSR_OVR 0010000 /* overrun (RO) */ +#define DCICSR_RNG 0020000 /* ring (RO) */ +#define DCICSR_CCH 0040000 /* car change (RO) */ +#define DCICSR_ALLERR (DCICSR_OVR|DCICSR_RNG|DCICSR_CCH) +#define DCICSR_ERR 0100000 /* error */ +#define DCOCSR_RD 0100737 +#define DCOCSR_WR 0000535 +#define DCOCSR_RTS 0000001 /* req to send (RW) */ +#define DCOCSR_CTS 0000002 /* clr to send (RO) */ +#define DCOCSR_MNT 0000004 /* maint (RWNI) */ + +extern int32 int_req[IPL_HLVL]; +extern int32 tmxr_poll; + +uint16 dci_csr[DCX_LINES] = { 0 }; /* control/status */ +uint8 dci_buf[DCX_LINES] = { 0 }; +uint32 dci_ireq = 0; +uint16 dco_csr[DCX_LINES] = { 0 }; /* control/status */ +uint8 dco_buf[DCX_LINES] = { 0 }; +uint32 dco_ireq = 0; +TMLN dcx_ldsc[DCX_LINES] = { 0 }; /* line descriptors */ +TMXR dcx_desc = { DCX_LINES, 0, 0, dcx_ldsc }; /* mux descriptor */ + +static const uint8 odd_par[] = { + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 00 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 10 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 20 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 30 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 40 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 50 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 60 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 70 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* 80 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* 90 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* A0 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* B0 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* C0 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* D0 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80, /* E0 */ + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, + 0x80, 0, 0, 0x80, 0, 0x80, 0x80, 0, /* F0 */ + 0, 0x80, 0x80, 0, 0x80, 0, 0, 0x80 + }; + +t_stat dcx_rd (int32 *data, int32 PA, int32 access); +t_stat dcx_wr (int32 data, int32 PA, int32 access); +t_stat dcx_reset (DEVICE *dptr); +t_stat dci_svc (UNIT *uptr); +t_stat dco_svc (UNIT *uptr); +t_stat dcx_attach (UNIT *uptr, char *cptr); +t_stat dcx_detach (UNIT *uptr); +t_stat dcx_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dcx_show (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dcx_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dcx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dcx_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc); +void dcx_enbdis (int32 dis); +void dci_clr_int (int32 ln); +void dci_set_int (int32 ln); +int32 dci_iack (void); +void dco_clr_int (int32 ln); +void dco_set_int (int32 ln); +int32 dco_iack (void); +void dcx_reset_ln (int32 ln); + +/* DCI data structures + + dci_dev DCI device descriptor + dci_unit DCI unit descriptor + dci_reg DCI register list +*/ + +DIB dci_dib = { + IOBA_DC, IOLN_DC, &dcx_rd, &dcx_wr, + 2, IVCL (DCI), VEC_DCI, { &dci_iack, &dco_iack } + }; + +UNIT dci_unit = { UDATA (&dci_svc, 0, 0), KBD_POLL_WAIT }; + +REG dci_reg[] = { + { BRDATA (BUF, dci_buf, DEV_RDX, 8, DCX_LINES) }, + { BRDATA (CSR, dci_csr, DEV_RDX, 16, DCX_LINES) }, + { GRDATA (IREQ, dci_ireq, DEV_RDX, DCX_LINES, 0) }, + { DRDATA (LINES, dcx_desc.lines, 6), REG_HRO }, + { GRDATA (DEVADDR, dci_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVIOLN, dci_dib.lnt, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, dci_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB dci_mod[] = { + { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &dcx_summ }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dcx_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &dcx_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &dcx_show, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + &set_vec, &dcx_show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "lines", "LINES", + &dcx_set_lines, &dcx_show_lines }, + { 0 } + }; + +DEVICE dci_dev = { + "DCI", &dci_unit, dci_reg, dci_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &dcx_reset, + NULL, &dcx_attach, &dcx_detach, + &dci_dib, DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS + }; + +/* DCO data structures + + dco_dev DCO device descriptor + dco_unit DCO unit descriptor + dco_reg DCO register list +*/ + +UNIT dco_unit[] = { + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT }, + { UDATA (&dco_svc, TT_MODE_7P+DCX_EPAR+DCX_MDM, 0), SERIAL_OUT_WAIT } + }; + +REG dco_reg[] = { + { BRDATA (BUF, dco_buf, DEV_RDX, 8, DCX_LINES) }, + { BRDATA (CSR, dco_csr, DEV_RDX, 16, DCX_LINES) }, + { GRDATA (IREQ, dco_ireq, DEV_RDX, DCX_LINES, 0) }, + { URDATA (TIME, dco_unit[0].wait, 10, 31, 0, + DCX_LINES, PV_LEFT) }, + { NULL } + }; + +MTAB dco_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { DCX_OPAR+DCX_EPAR, 0, "no parity", "NOPARITY", NULL }, + { DCX_OPAR+DCX_EPAR, DCX_OPAR, "odd parity", "ODDPARITY", NULL }, + { DCX_OPAR+DCX_EPAR, DCX_EPAR, "even parity", "EVENPARITY", NULL }, + { DCX_MDM, 0, "no dataset", "NODATASET", NULL }, + { DCX_MDM, DCX_MDM, "dataset", "DATASET", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dcx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &dcx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &dcx_desc }, + { 0 } + }; + +DEVICE dco_dev = { + "DCO", dco_unit, dco_reg, dco_mod, + DCX_LINES, 10, 31, 1, 8, 8, + NULL, NULL, &dcx_reset, + NULL, NULL, NULL, + NULL, DEV_UBUS | DEV_DISABLE | DEV_DIS + }; + +/* Terminal input routines */ + +t_stat dcx_rd (int32 *data, int32 PA, int32 access) +{ +int32 ln = ((PA - dci_dib.ba) >> 3) & DCX_MASK; + +switch ((PA >> 1) & 03) { /* decode PA<2:1> */ + + case 00: /* dci csr */ + if (dci_csr[ln] & DCICSR_ALLERR) + dci_csr[ln] |= DCICSR_ERR; + else dci_csr[ln] &= ~DCICSR_ERR; + *data = dci_csr[ln] & DCICSR_RD; + dci_csr[ln] &= ~(CSR_DONE|DCICSR_ALLERR|DCICSR_ERR); + return SCPE_OK; + + case 01: /* dci buf */ + dci_clr_int (ln); + *data = dci_buf[ln]; + return SCPE_OK; + + case 02: /* dco csr */ + *data = dco_csr[ln] & DCOCSR_RD; + return SCPE_OK; + + case 03: /* dco buf */ + *data = dco_buf[ln]; + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +t_stat dcx_wr (int32 data, int32 PA, int32 access) +{ +int32 ln = ((PA - dci_dib.ba) >> 3) & DCX_MASK; +TMLN *lp = &dcx_ldsc[ln]; + +switch ((PA >> 1) & 03) { /* decode PA<2:1> */ + + case 00: /* dci csr */ + if (access == WRITEB) /* byte write? */ + data = (PA & 1)? + (dci_csr[ln] & 0377) | (data << 8): + (dci_csr[ln] & ~0377) | data; + if ((data & CSR_IE) == 0) /* clr ie? */ + dci_clr_int (ln); /* clr int req */ + else if ((dci_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) + dci_set_int (ln); + if (((data ^ dci_csr[ln]) & DCICSR_DTR) && /* DTR change? */ + (dco_unit[ln].flags & DCX_MDM)) { /* modem ctl? */ + if (data & DCICSR_DTR) { /* setting DTR? */ + if (lp->conn) { /* ringing? */ + dci_csr[ln] = (dci_csr[ln] & ~DCICSR_RNG) | + (DCICSR_CDT|DCICSR_CCH|DCICSR_ERR); + dco_csr[ln] |= DCOCSR_CTS; /* set CDT,CCH,CTS */ + if (data & CSR_IE) /* if ie, req int */ + dci_set_int (ln); + } + } /* end DTR 0->1 */ + else { /* clearing DTR */ + if (lp->conn) { /* connected? */ + tmxr_linemsg (lp, "\r\nLine hangup\r\n"); + tmxr_reset_ln (lp); /* reset line */ + if (dci_csr[ln] & DCICSR_CDT) { /* carrier det? */ + dci_csr[ln] |= (DCICSR_CCH|DCICSR_ERR); + if (data & CSR_IE) /* if ie, req int */ + dci_set_int (ln); + } + } + dci_csr[ln] &= ~(DCICSR_CDT|DCICSR_RNG); + dco_csr[ln] &= ~DCOCSR_CTS; /* clr CDT,RNG,CTS */ + } /* end DTR 1->0 */ + } /* end DTR chg+modem */ + dci_csr[ln] = (uint16) ((dci_csr[ln] & ~DCICSR_WR) | (data & DCICSR_WR)); + return SCPE_OK; + + case 01: /* dci buf */ + return SCPE_OK; + + case 02: /* dco csr */ + if (access == WRITEB) /* byte write? */ + data = (PA & 1)? + (dco_csr[ln] & 0377) | (data << 8): + (dco_csr[ln] & ~0377) | data; + if ((data & CSR_IE) == 0) /* clr ie? */ + dco_clr_int (ln); /* clr int req */ + else if ((dco_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) + dco_set_int (ln); + dco_csr[ln] = (uint16) ((dco_csr[ln] & ~DCOCSR_WR) | (data & DCOCSR_WR)); + return SCPE_OK; + + case 03: /* dco buf */ + if ((PA & 1) == 0) + dco_buf[ln] = data & 0377; + dco_csr[ln] &= ~CSR_DONE; /* clr done */ + dco_clr_int (ln); /* clr int req */ + sim_activate (&dco_unit[ln], dco_unit[ln].wait); + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +/* Terminal input service */ + +t_stat dci_svc (UNIT *uptr) +{ +int32 ln, c, temp; + +if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return SCPE_OK; +sim_activate (uptr, tmxr_poll); /* continue poll */ +ln = tmxr_poll_conn (&dcx_desc); /* look for connect */ +if (ln >= 0) { /* got one? */ + dcx_ldsc[ln].rcve = 1; /* set rcv enb */ + if (dco_unit[ln].flags & DCX_MDM) { /* modem control? */ + if (dci_csr[ln] & DCICSR_DTR) /* DTR already set? */ + dci_csr[ln] |= (DCICSR_CDT|DCICSR_CCH|DCICSR_ERR); + else dci_csr[ln] |= (DCICSR_RNG|DCICSR_ERR); /* no, ring */ + if (dci_csr[ln] & CSR_IE) /* if ie, */ + dci_set_int (ln); /* req int */ + } + else dco_csr[ln] |= DCOCSR_CTS; /* just connect */ + } +tmxr_poll_rx (&dcx_desc); /* poll for input */ +for (ln = 0; ln < DCX_LINES; ln++) { /* loop thru lines */ + if (dcx_ldsc[ln].conn) { /* connected? */ + if ((temp = tmxr_getc_ln (&dcx_ldsc[ln])) && /* get char */ + !(temp & SCPE_BREAK)) { /* not break? */ + c = sim_tt_inpcvt (temp, TT_GET_MODE (dco_unit[ln].flags)); + if (dci_csr[ln] & CSR_DONE) /* overrun? */ + dci_csr[ln] |= DCICSR_OVR; + else dci_csr[ln] |= CSR_DONE; /* set done */ + if (dci_csr[ln] & CSR_IE) /* if ie, */ + dci_set_int (ln); /* req int */ + if (dco_unit[ln].flags & DCX_OPAR) /* odd parity */ + c = (c & 0177) | odd_par[c & 0177]; + else if (dco_unit[ln].flags & DCX_EPAR) /* even parity */ + c = (c & 0177) | (odd_par[c & 0177] ^ 0200); + dci_buf[ln] = c; + if ((c & 0200) == odd_par[c & 0177]) /* odd par? */ + dci_csr[ln] |= DCICSR_PAR; + else dci_csr[ln] &= ~DCICSR_PAR; + } + } + else { /* disconnected */ + if ((dco_unit[ln].flags & DCX_MDM) && /* modem control? */ + (dci_csr[ln] & DCICSR_CDT)) { /* carrier detect? */ + dci_csr[ln] |= (DCICSR_CCH|DCICSR_ERR); /* carrier change */ + if (dci_csr[ln] & CSR_IE) /* if ie, */ + dci_set_int (ln); /* req int */ + } + dci_csr[ln] &= ~(DCICSR_CDT|DCICSR_RNG); /* clr CDT,RNG,CTS */ + dco_csr[ln] &= ~DCOCSR_CTS; + } + } +return SCPE_OK; +} + +/* Terminal output service */ + +t_stat dco_svc (UNIT *uptr) +{ +int32 c; +int32 ln = uptr - dco_unit; /* line # */ + +if (dcx_ldsc[ln].conn) { /* connected? */ + if (dcx_ldsc[ln].xmte) { /* tx enabled? */ + TMLN *lp = &dcx_ldsc[ln]; /* get line */ + c = sim_tt_outcvt (dco_buf[ln], TT_GET_MODE (dco_unit[ln].flags)); + if (c >= 0) tmxr_putc_ln (lp, c); /* output char */ + tmxr_poll_tx (&dcx_desc); /* poll xmt */ + } + else { + tmxr_poll_tx (&dcx_desc); /* poll xmt */ + sim_activate (uptr, dco_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } +dco_csr[ln] |= CSR_DONE; /* set done */ +if (dco_csr[ln] & CSR_IE) /* ie set? */ + dco_set_int (ln); /* req int */ +return SCPE_OK; +} + +/* Interrupt routines */ + +void dci_clr_int (int32 ln) +{ +dci_ireq &= ~(1 << ln); /* clr mux rcv int */ +if (dci_ireq == 0) /* all clr? */ + CLR_INT (DCI); +else SET_INT (DCI); /* no, set intr */ +return; +} + +void dci_set_int (int32 ln) +{ +dci_ireq |= (1 << ln); /* clr mux rcv int */ +SET_INT (DCI); /* set master intr */ +return; +} + +int32 dci_iack (void) +{ +int32 ln; + +for (ln = 0; ln < DCX_LINES; ln++) { /* find 1st line */ + if (dci_ireq & (1 << ln)) { + dci_clr_int (ln); /* clear intr */ + return (dci_dib.vec + (ln * 010)); /* return vector */ + } + } +return 0; +} + +void dco_clr_int (int32 ln) +{ +dco_ireq &= ~(1 << ln); /* clr mux rcv int */ +if (dco_ireq == 0) /* all clr? */ + CLR_INT (DCO); +else SET_INT (DCO); /* no, set intr */ +return; +} + +void dco_set_int (int32 ln) +{ +dco_ireq |= (1 << ln); /* clr mux rcv int */ +SET_INT (DCO); /* set master intr */ +return; +} + +int32 dco_iack (void) +{ +int32 ln; + +for (ln = 0; ln < DCX_LINES; ln++) { /* find 1st line */ + if (dco_ireq & (1 << ln)) { + dco_clr_int (ln); /* clear intr */ + return (dci_dib.vec + (ln * 010) + 4); /* return vector */ + } + } +return 0; +} + +/* Reset */ + +t_stat dcx_reset (DEVICE *dptr) +{ +int32 ln; + +dcx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ +sim_cancel (&dci_unit); /* assume stop */ +if (dci_unit.flags & UNIT_ATT) /* if attached, */ + sim_activate (&dci_unit, tmxr_poll); /* activate */ +for (ln = 0; ln < DCX_LINES; ln++) /* for all lines */ + dcx_reset_ln (ln); +return auto_config (dci_dev.name, dcx_desc.lines); /* auto config */ +} + +/* Reset individual line */ + +void dcx_reset_ln (int32 ln) +{ +dci_buf[ln] = 0; /* clear buf */ +dci_csr[ln] = 0; +dco_buf[ln] = 0; /* clear buf */ +dco_csr[ln] = CSR_DONE; +sim_cancel (&dco_unit[ln]); /* deactivate */ +dci_clr_int (ln); +dco_clr_int (ln); +return; +} + +/* Attach master unit */ + +t_stat dcx_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&dcx_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) /* error? */ + return r; +sim_activate (uptr, tmxr_poll); /* start poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat dcx_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&dcx_desc, uptr); /* detach */ +for (i = 0; i < DCX_LINES; i++) /* all lines, */ + dcx_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat dcx_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < DCX_LINES; i++) t = t + (dcx_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat dcx_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < DCX_LINES; i++) t = t + (dcx_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < DCX_LINES; i++) { + if (dcx_ldsc[i].conn) { + if (val) tmxr_fconns (st, &dcx_ldsc[i], i); + else tmxr_fstats (st, &dcx_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Enable/disable device */ + +void dcx_enbdis (int32 dis) +{ +if (dis) { + dci_dev.flags = dco_dev.flags | DEV_DIS; + dco_dev.flags = dco_dev.flags | DEV_DIS; + } +else { + dci_dev.flags = dci_dev.flags & ~DEV_DIS; + dco_dev.flags = dco_dev.flags & ~DEV_DIS; + } +return; +} + +/* SHOW VECTOR processor */ + +t_stat dcx_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +return show_vec (st, uptr, dcx_desc.lines * 2, desc); +} + +/* Change number of lines */ + +t_stat dcx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newln = get_uint (cptr, 10, DCX_LINES, &r); +if ((r != SCPE_OK) || (newln == dcx_desc.lines)) return r; +if (newln == 0) return SCPE_ARG; +if (newln < dcx_desc.lines) { + for (i = newln, t = 0; i < dcx_desc.lines; i++) t = t | dcx_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < dcx_desc.lines; i++) { + if (dcx_ldsc[i].conn) { + tmxr_linemsg (&dcx_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&dcx_ldsc[i]); /* reset line */ + } + dco_unit[i].flags |= UNIT_DIS; + dcx_reset_ln (i); + } + } +else { + for (i = dcx_desc.lines; i < newln; i++) { + dco_unit[i].flags &= ~UNIT_DIS; + dcx_reset_ln (i); + } + } +dcx_desc.lines = newln; +dci_dib.lnt = newln * 010; /* upd IO page lnt */ +return auto_config (dci_dev.name, newln); /* auto config */ +} + +/* Show number of lines */ + +t_stat dcx_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "lines=%d", dcx_desc.lines); +return SCPE_OK; +} diff --git a/PDP11/pdp11_defs.h b/PDP11/pdp11_defs.h new file mode 100644 index 0000000..bfa5552 --- /dev/null +++ b/PDP11/pdp11_defs.h @@ -0,0 +1,847 @@ +/* pdp11_defs.h: PDP-11 simulator definitions + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + The author gratefully acknowledges the help of Max Burnet, Megan Gentry, + and John Wilson in resolving questions about the PDP-11 + + 16-May-08 RMS Added KE11A, DC11 support + 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) + 25-Jan-08 RMS Added RC11, KG11A support (from John Dundas) + 16-Dec-06 RMS Added TA11 support + 29-Oct-06 RMS Added clock coscheduling + 06-Jul-06 RMS Added multiple KL11/DL11 support + 26-Jun-06 RMS Added RF11 support + 24-May-06 RMS Added 11/44 DR support (from CIS diagnostic) + 17-May-06 RMS Added CR11/CD11 support (from John Dundas) + 30-Sep-04 RMS Added Massbus support + Removed Map_Addr prototype + Removed map argument from Unibus routines + Added framework for model selection + 28-May-04 RMS Added DHQ support + 25-Jan-04 RMS Removed local debug logging support + 22-Dec-03 RMS Added second DEUNA/DELUA support + 18-Oct-03 RMS Added DECtape off reel message + 19-May-03 RMS Revised for new conditional compilation + 05-Apr-03 RMS Fixed bug in MMR1 update (found by Tim Stark) + 28-Feb-03 RMS Added TM logging support + 19-Jan-03 RMS Changed mode definitions for Apple Dev Kit conflict + 11-Nov-02 RMS Changed log definitions to be VAX compatible + 10-Oct-02 RMS Added vector information to DIB + Changed DZ11 vector to Unibus standard + Added DEQNA/DELQA, DEUNA/DELUA support + Added multiple RQDX3, autoconfigure support + 12-Sep-02 RMS Added TMSCP, KW11P,and RX211 support + 28-Apr-02 RMS Clarified PDF ACF mnemonics + 22-Apr-02 RMS Added HTRAP, BPOK maint register flags, MT_MAXFR + 06-Mar-02 RMS Changed system type to KDJ11A + 20-Jan-02 RMS Added multiboard DZ11 support + 09-Nov-01 RMS Added bus map support + 07-Nov-01 RMS Added RQDX3 support + 26-Oct-01 RMS Added symbolic definitions for IO page + 19-Oct-01 RMS Added DZ definitions + 15-Oct-01 RMS Added logging capabilities + 07-Sep-01 RMS Revised for multilevel interrupts + 01-Jun-01 RMS Added DZ11 support + 23-Apr-01 RMS Added RK611 support + 05-Apr-01 RMS Added TS11/TSV05 support + 10-Feb-01 RMS Added DECtape support +*/ + +#ifndef _PDP11_DEFS_H +#define _PDP11_DEFS_H 0 + +#ifndef VM_PDP11 +#define VM_PDP11 0 +#endif + +#include "sim_defs.h" /* simulator defns */ +#include + +/* Architectural constants */ + +#define STKL_R 0340 /* stack limit */ +#define STKL_Y 0400 +#define VASIZE 0200000 /* 2**16 */ +#define VAMASK (VASIZE - 1) /* 2**16 - 1 */ +#define MEMSIZE64K 0200000 /* 2**16 */ +#define INIMEMSIZE 001000000 /* 2**18 */ +#define UNIMEMSIZE 001000000 /* 2**18 */ +#define UNIMASK (UNIMEMSIZE - 1) /* 2**18 - 1 */ +#define IOPAGEBASE 017760000 /* 2**22 - 2**13 */ +#define IOPAGESIZE 000020000 /* 2**13 */ +#define IOPAGEMASK (IOPAGESIZE - 1) /* 2**13 - 1 */ +#define MAXMEMSIZE 020000000 /* 2**22 */ +#define PAMASK (MAXMEMSIZE - 1) /* 2**22 - 1 */ +#define MEMSIZE (cpu_unit.capac) +#define ADDR_IS_MEM(x) (((t_addr) (x)) < cpu_memsize) /* use only in sim! */ +#define DMASK 0177777 + +/* CPU models */ + +#define MOD_1103 0 +#define MOD_1104 1 +#define MOD_1105 2 +#define MOD_1120 3 +#define MOD_1123 4 +#define MOD_1123P 5 +#define MOD_1124 6 +#define MOD_1134 7 +#define MOD_1140 8 +#define MOD_1144 9 +#define MOD_1145 10 +#define MOD_1160 11 +#define MOD_1170 12 +#define MOD_1173 13 +#define MOD_1153 14 +#define MOD_1173B 15 +#define MOD_1183 16 +#define MOD_1184 17 +#define MOD_1193 18 +#define MOD_1194 19 +#define MOD_T 20 + +#define CPUT_03 (1u << MOD_1103) /* LSI-11 */ +#define CPUT_04 (1u << MOD_1104) /* 11/04 */ +#define CPUT_05 (1u << MOD_1105) /* 11/05 */ +#define CPUT_20 (1u << MOD_1120) /* 11/20 */ +#define CPUT_23 (1u << MOD_1123) /* 11/23 */ +#define CPUT_23P (1u << MOD_1123P) /* 11/23+ */ +#define CPUT_24 (1u << MOD_1124) /* 11/24 */ +#define CPUT_34 (1u << MOD_1134) /* 11/34 */ +#define CPUT_40 (1u << MOD_1140) /* 11/40 */ +#define CPUT_44 (1u << MOD_1144) /* 11/44 */ +#define CPUT_45 (1u << MOD_1145) /* 11/45 */ +#define CPUT_60 (1u << MOD_1160) /* 11/60 */ +#define CPUT_70 (1u << MOD_1170) /* 11/70 */ +#define CPUT_73 (1u << MOD_1173) /* 11/73 */ +#define CPUT_53 (1u << MOD_1153) /* 11/53 */ +#define CPUT_73B (1u << MOD_1173B) /* 11/73B */ +#define CPUT_83 (1u << MOD_1183) /* 11/83 */ +#define CPUT_84 (1u << MOD_1184) /* 11/84 */ +#define CPUT_93 (1u << MOD_1193) /* 11/93 */ +#define CPUT_94 (1u << MOD_1194) /* 11/94 */ +#define CPUT_T (1u << MOD_T) /* T-11 */ + +#define CPUT_F (CPUT_23|CPUT_23P|CPUT_24) /* all F11's */ +#define CPUT_J (CPUT_53|CPUT_73|CPUT_73B| \ + CPUT_83|CPUT_84|CPUT_93|CPUT_94) +#define CPUT_JB (CPUT_73B|CPUT_83|CPUT_84) /* KDJ11B */ +#define CPUT_JE (CPUT_93|CPUT_94) /* KDJ11E */ +#define CPUT_JU (CPUT_84|CPUT_94) /* KTJ11B UBA */ +#define CPUT_ALL 0xFFFFFFFF + +/* CPU options */ + +#define BUS_U (1u << 0) /* Unibus */ +#define BUS_Q (0) /* Qbus */ +#define OPT_EIS (1u << 1) /* EIS */ +#define OPT_FIS (1u << 2) /* FIS */ +#define OPT_FPP (1u << 3) /* FPP */ +#define OPT_CIS (1u << 4) /* CIS */ +#define OPT_MMU (1u << 5) /* MMU */ +#define OPT_RH11 (1u << 6) /* RH11 */ +#define OPT_PAR (1u << 7) /* parity */ +#define OPT_UBM (1u << 8) /* UBM */ + +#define CPUT(x) ((cpu_type & (x)) != 0) +#define CPUO(x) ((cpu_opt & (x)) != 0) +#define UNIBUS (cpu_opt & BUS_U) + +/* Feature sets + + SDSD source addr, dest addr, source fetch, dest fetch + SR switch register + DR display register + RTT RTT instruction + SXS SXT, XOR, SOB instructions + MARK MARK instruction + SPL SPL instruction + MXPY MTPI, MTPD, MFPI, MFPD instructions + MXPS MTPS, MFPS instructions + MFPT MFPT instruction + CSM CSM instruction + TSWLK TSTSET, WRLCK instructions + PSW PSW register + EXPT explicit PSW writes can alter T-bit + IOSR general registers readable from programs in IO space + 2REG dual register set + MMR3 MMR3 register + MMTR mem mgt traps + STKLR STKLIM register + STKLF fixed stack limit + SID supervisor mode, I/D spaces + ODD odd address trap + HALT4 halt in kernel mode traps to 4 + JREG4 JMP/JSR R traps to 4 + STKA stop on stack abort + LTCR LTC CSR + LTCM LTC CSR<7> +*/ + +#define IS_SDSD (CPUT_20|CPUT_F|CPUT_40|CPUT_60|CPUT_J|CPUT_T) +#define HAS_SR (CPUT_04|CPUT_05|CPUT_20|CPUT_34|CPUT_40| \ + CPUT_44|CPUT_45|CPUT_60|CPUT_70) +#define HAS_DR (CPUT_04|CPUT_05|CPUT_20|CPUT_24|CPUT_34| \ + CPUT_40|CPUT_44|CPUT_45|CPUT_60|CPUT_70) +#define HAS_RTT (CPUT_03|CPUT_04|CPUT_F|CPUT_34|CPUT_40| \ + CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J|CPUT_T) +#define HAS_SXS (CPUT_03|CPUT_F|CPUT_34|CPUT_40|CPUT_44| \ + CPUT_45|CPUT_60|CPUT_70|CPUT_J|CPUT_T) +#define HAS_MARK (CPUT_03|CPUT_F|CPUT_34|CPUT_40|CPUT_44| \ + CPUT_45|CPUT_60|CPUT_70|CPUT_J) +#define HAS_SPL (CPUT_44|CPUT_45|CPUT_70|CPUT_J) +#define HAS_MXPY (CPUT_F|CPUT_34|CPUT_40|CPUT_44|CPUT_45| \ + CPUT_60|CPUT_70|CPUT_J) +#define HAS_MXPS (CPUT_03|CPUT_F|CPUT_34|CPUT_J|CPUT_T) +#define HAS_MFPT (CPUT_F|CPUT_44|CPUT_J|CPUT_T) +#define HAS_CSM (CPUT_44|CPUT_J) +#define HAS_TSWLK (CPUT_J) +#define HAS_PSW (CPUT_04|CPUT_05|CPUT_20|CPUT_F|CPUT_34|CPUT_40| \ + CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J) +#define HAS_EXPT (CPUT_04|CPUT_05|CPUT_20) +#define HAS_IOSR (CPUT_04|CPUT_05) +#define HAS_2REG (CPUT_45|CPUT_70|CPUT_J) +#define HAS_MMR3 (CPUT_F|CPUT_44|CPUT_45|CPUT_70|CPUT_J) +#define HAS_MMTR (CPUT_45|CPUT_70) +#define HAS_STKLR (CPUT_45|CPUT_60|CPUT_70) +#define HAS_STKLF (CPUT_04|CPUT_05|CPUT_20|CPUT_F|CPUT_34| \ + CPUT_40|CPUT_44|CPUT_J) +#define HAS_SID (CPUT_44|CPUT_45|CPUT_70|CPUT_J) +#define HAS_ODD (CPUT_04|CPUT_05|CPUT_20|CPUT_34|CPUT_40| \ + CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J) +#define HAS_HALT4 (CPUT_44|CPUT_45|CPUT_70|CPUT_J) +#define HAS_JREG4 (CPUT_03|CPUT_04|CPUT_05|CPUT_20|CPUT_F| \ + CPUT_34|CPUT_40|CPUT_60|CPUT_T) +#define STOP_STKA (CPUT_03|CPUT_04|CPUT_05|CPUT_20|CPUT_34|CPUT_44) +#define HAS_LTCR (CPUT_04|CPUT_05|CPUT_20|CPUT_23P|CPUT_24| \ + CPUT_34|CPUT_40|CPUT_44|CPUT_45|CPUT_60| \ + CPUT_70|CPUT_J) +#define HAS_LTCM (CPUT_04|CPUT_05|CPUT_20|CPUT_24|CPUT_34| \ + CPUT_40|CPUT_44|CPUT_45|CPUT_60|CPUT_70|CPUT_J) + +/* Protection modes */ + +#define MD_KER 0 +#define MD_SUP 1 +#define MD_UND 2 +#define MD_USR 3 + +/* I/O access modes */ + +#define READ 0 +#define READC 1 /* read console */ +#define WRITE 2 +#define WRITEC 3 /* write console */ +#define WRITEB 4 + +/* PSW */ + +#define PSW_V_C 0 /* condition codes */ +#define PSW_V_V 1 +#define PSW_V_Z 2 +#define PSW_V_N 3 +#define PSW_V_TBIT 4 /* trace trap */ +#define PSW_V_IPL 5 /* int priority */ +#define PSW_V_FPD 8 /* first part done */ +#define PSW_V_RS 11 /* register set */ +#define PSW_V_PM 12 /* previous mode */ +#define PSW_V_CM 14 /* current mode */ +#define PSW_CC 017 +#define PSW_TBIT (1 << PSW_V_TBIT) +#define PSW_PM (3 << PSW_V_PM) + +/* FPS */ + +#define FPS_V_C 0 /* condition codes */ +#define FPS_V_V 1 +#define FPS_V_Z 2 +#define FPS_V_N 3 +#define FPS_V_T 5 /* truncate */ +#define FPS_V_L 6 /* long */ +#define FPS_V_D 7 /* double */ +#define FPS_V_IC 8 /* ic err int */ +#define FPS_V_IV 9 /* overflo err int */ +#define FPS_V_IU 10 /* underflo err int */ +#define FPS_V_IUV 11 /* undef var err int */ +#define FPS_V_ID 14 /* int disable */ +#define FPS_V_ER 15 /* error */ + +/* PIRQ */ + +#define PIRQ_PIR1 0001000 +#define PIRQ_PIR2 0002000 +#define PIRQ_PIR3 0004000 +#define PIRQ_PIR4 0010000 +#define PIRQ_PIR5 0020000 +#define PIRQ_PIR6 0040000 +#define PIRQ_PIR7 0100000 +#define PIRQ_IMP 0177356 /* implemented bits */ +#define PIRQ_RW 0177000 /* read/write bits */ + +/* STKLIM */ + +#define STKLIM_RW 0177400 + +/* MMR0 */ + +#define MMR0_MME 0000001 /* mem mgt enable */ +#define MMR0_V_PAGE 1 /* offset to pageno */ +#define MMR0_M_PAGE 077 /* mask for pageno */ +#define MMR0_PAGE (MMR0_M_PAGE << MMR0_V_PAGE) +#define MMR0_IC 0000200 /* instr complete */ +#define MMR0_MAINT 0000400 /* maintenance */ +#define MMR0_TENB 0001000 /* trap enable */ +#define MMR0_TRAP 0010000 /* mem mgt trap */ +#define MMR0_RO 0020000 /* read only error */ +#define MMR0_PL 0040000 /* page lnt error */ +#define MMR0_NR 0100000 /* no access error */ +#define MMR0_FREEZE 0160000 /* if set, no update */ +#define MMR0_WR 0171401 /* writeable bits */ + +/* MMR3 */ + +#define MMR3_UDS 001 /* user dspace enbl */ +#define MMR3_SDS 002 /* super dspace enbl */ +#define MMR3_KDS 004 /* krnl dspace enbl */ +#define MMR3_CSM 010 /* CSM enable */ +#define MMR3_M22E 020 /* 22b mem mgt enbl */ +#define MMR3_BME 040 /* DMA bus map enbl */ + +/* PAR */ + +#define PAR_18B 0007777 /* 18b addressing */ +#define PAR_22B 0177777 /* 22b addressing */ + +/* PDR */ + +#define PDR_ACF 0000007 /* access control */ +#define PDR_ACS 0000006 /* 2b access control */ +#define PDR_ED 0000010 /* expansion dir */ +#define PDR_W 0000100 /* written flag */ +#define PDR_A 0000200 /* access flag */ +#define PDR_PLF 0077400 /* page lnt field */ +#define PDR_NOC 0100000 /* don't cache */ + +#define PDR_PRD 0000003 /* page readable if 2 */ + +/* Virtual address */ + +#define VA_DF 0017777 /* displacement */ +#define VA_BN 0017700 /* block number */ +#define VA_V_APF 13 /* offset to APF */ +#define VA_V_DS 16 /* offset to space */ +#define VA_V_MODE 17 /* offset to mode */ +#define VA_DS (1u << VA_V_DS) /* data space flag */ + +/* Unibus map (if present) */ + +#define UBM_LNT_LW 32 /* size in LW */ +#define UBM_V_PN 13 /* page number */ +#define UBM_M_PN 037 +#define UBM_V_OFF 0 /* offset */ +#define UBM_M_OFF 017777 +#define UBM_PAGSIZE (UBM_M_OFF + 1) /* page size */ +#define UBM_GETPN(x) (((x) >> UBM_V_PN) & UBM_M_PN) +#define UBM_GETOFF(x) ((x) & UBM_M_OFF) + +/* CPUERR */ + +#define CPUE_RED 0004 /* red stack */ +#define CPUE_YEL 0010 /* yellow stack */ +#define CPUE_TMO 0020 /* IO page nxm */ +#define CPUE_NXM 0040 /* memory nxm */ +#define CPUE_ODD 0100 /* odd address */ +#define CPUE_HALT 0200 /* HALT not kernel */ +#define CPUE_IMP 0374 /* implemented bits */ + +/* Floating point accumulators */ + +typedef struct { + uint32 l; /* low 32b */ + uint32 h; /* high 32b */ + } fpac_t; + +/* Device CSRs */ + +#define CSR_V_GO 0 /* go */ +#define CSR_V_IE 6 /* interrupt enable */ +#define CSR_V_DONE 7 /* done */ +#define CSR_V_BUSY 11 /* busy */ +#define CSR_V_ERR 15 /* error */ +#define CSR_GO (1u << CSR_V_GO) +#define CSR_IE (1u << CSR_V_IE) +#define CSR_DONE (1u << CSR_V_DONE) +#define CSR_BUSY (1u << CSR_V_BUSY) +#define CSR_ERR (1u << CSR_V_ERR) + +/* Trap masks, descending priority order, following J-11 + An interrupt summary bit is kept with traps, to minimize overhead +*/ + +#define TRAP_V_RED 0 /* red stk abort 4 */ +#define TRAP_V_ODD 1 /* odd address 4 */ +#define TRAP_V_MME 2 /* mem mgt 250 */ +#define TRAP_V_NXM 3 /* nx memory 4 */ +#define TRAP_V_PAR 4 /* parity err 114 */ +#define TRAP_V_PRV 5 /* priv inst 4 */ +#define TRAP_V_ILL 6 /* illegal inst 10 */ +#define TRAP_V_BPT 7 /* BPT 14 */ +#define TRAP_V_IOT 8 /* IOT 20 */ +#define TRAP_V_EMT 9 /* EMT 30 */ +#define TRAP_V_TRAP 10 /* TRAP 34 */ +#define TRAP_V_TRC 11 /* T bit 14 */ +#define TRAP_V_YEL 12 /* stack 4 */ +#define TRAP_V_PWRFL 13 /* power fail 24 */ +#define TRAP_V_FPE 14 /* fpe 244 */ +#define TRAP_V_MAX 15 /* intr = max trp # */ +#define TRAP_RED (1u << TRAP_V_RED) +#define TRAP_ODD (1u << TRAP_V_ODD) +#define TRAP_MME (1u << TRAP_V_MME) +#define TRAP_NXM (1u << TRAP_V_NXM) +#define TRAP_PAR (1u << TRAP_V_PAR) +#define TRAP_PRV (1u << TRAP_V_PRV) +#define TRAP_ILL (1u << TRAP_V_ILL) +#define TRAP_BPT (1u << TRAP_V_BPT) +#define TRAP_IOT (1u << TRAP_V_IOT) +#define TRAP_EMT (1u << TRAP_V_EMT) +#define TRAP_TRAP (1u << TRAP_V_TRAP) +#define TRAP_TRC (1u << TRAP_V_TRC) +#define TRAP_YEL (1u << TRAP_V_YEL) +#define TRAP_PWRFL (1u << TRAP_V_PWRFL) +#define TRAP_FPE (1u << TRAP_V_FPE) +#define TRAP_INT (1u << TRAP_V_MAX) +#define TRAP_ALL ((1u << TRAP_V_MAX) - 1) /* all traps */ + +#define VEC_RED 0004 /* trap vectors */ +#define VEC_ODD 0004 +#define VEC_MME 0250 +#define VEC_NXM 0004 +#define VEC_PAR 0114 +#define VEC_PRV 0004 +#define VEC_ILL 0010 +#define VEC_BPT 0014 +#define VEC_IOT 0020 +#define VEC_EMT 0030 +#define VEC_TRAP 0034 +#define VEC_TRC 0014 +#define VEC_YEL 0004 +#define VEC_PWRFL 0024 +#define VEC_FPE 0244 + +/* Simulator stop codes; codes 1:TRAP_V_MAX correspond to traps 0:TRAPMAX-1 */ + +#define STOP_HALT (TRAP_V_MAX + 1) /* HALT instruction */ +#define STOP_IBKPT (TRAP_V_MAX + 2) /* instruction bkpt */ +#define STOP_WAIT (TRAP_V_MAX + 3) /* wait, no events */ +#define STOP_VECABORT (TRAP_V_MAX + 4) /* abort vector read */ +#define STOP_SPABORT (TRAP_V_MAX + 5) /* abort trap push */ +#define STOP_RQ (TRAP_V_MAX + 6) /* RQDX3 panic */ +#define STOP_SANITY (TRAP_V_MAX + 7) /* sanity timer exp */ +#define STOP_DTOFF (TRAP_V_MAX + 8) /* DECtape off reel */ +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +/* Timers */ + +#define TMR_CLK 0 /* line clock */ +#define TMR_PCLK 1 /* KW11P */ + +/* IO parameters */ + +#define DZ_MUXES 4 /* max # of DZ muxes */ +#define DZ_LINES 8 /* lines per DZ mux */ +#define VH_MUXES 4 /* max # of VH muxes */ +#define DLX_LINES 16 /* max # of KL11/DL11's */ +#define DCX_LINES 16 /* max # of DC11's */ +#define MT_MAXFR (1 << 16) /* magtape max rec */ +#define AUTO_LNT 34 /* autoconfig ranks */ +#define DIB_MAX 100 /* max DIBs */ + +#define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ +#define DEV_V_QBUS (DEV_V_UF + 1) /* Qbus */ +#define DEV_V_Q18 (DEV_V_UF + 2) /* Qbus with <= 256KB */ +#define DEV_V_FLTA (DEV_V_UF + 3) /* flt addr */ +#define DEV_V_MBUS (DEV_V_UF + 4) /* Massbus */ +#define DEV_V_FFUF (DEV_V_UF + 5) /* first free flag */ +#define DEV_UBUS (1u << DEV_V_UBUS) +#define DEV_QBUS (1u << DEV_V_QBUS) +#define DEV_Q18 (1u << DEV_V_Q18) +#define DEV_FLTA (1u << DEV_V_FLTA) +#define DEV_MBUS (1u << DEV_V_MBUS) + +#define DEV_RDX 8 /* default device radix */ + +/* Device information block */ + +#define VEC_DEVMAX 4 /* max device vec */ + +struct pdp_dib { + uint32 ba; /* base addr */ + uint32 lnt; /* length */ + t_stat (*rd)(int32 *dat, int32 ad, int32 md); + t_stat (*wr)(int32 dat, int32 ad, int32 md); + int32 vnum; /* vectors: number */ + int32 vloc; /* locator */ + int32 vec; /* value */ + int32 (*ack[VEC_DEVMAX])(void); /* ack routines */ + }; + +typedef struct pdp_dib DIB; + +/* I/O page layout - XUB, RQB,RQC,RQD float based on number of DZ's */ + +#define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */ +#define IOLN_DZ 010 +#define IOBA_XUB (IOPAGEBASE + 000330 + (020 * (DZ_MUXES / 2))) +#define IOLN_XUB 010 +#define IOBA_RQB (IOPAGEBASE + 000334 + (020 * (DZ_MUXES / 2))) +#define IOLN_RQB 004 +#define IOBA_RQC (IOPAGEBASE + IOBA_RQB + IOLN_RQB) +#define IOLN_RQC 004 +#define IOBA_RQD (IOPAGEBASE + IOBA_RQC + IOLN_RQC) +#define IOLN_RQD 004 +#define IOBA_VH (IOPAGEBASE + 000440) /* DHQ11 */ +#define IOLN_VH 020 +#define IOBA_UBM (IOPAGEBASE + 010200) /* Unibus map */ +#define IOLN_UBM (UBM_LNT_LW * sizeof (int32)) +#define IOBA_KG (IOPAGEBASE + 010700) /* KG11-A */ +#define IOLN_KG 006 +#define IOBA_RQ (IOPAGEBASE + 012150) /* RQDX3 */ +#define IOLN_RQ 004 +#define IOBA_SUP (IOPAGEBASE + 012200) /* supervisor APR's */ +#define IOLN_SUP 0100 +#define IOBA_KIPDR (IOPAGEBASE + 012300) /* kernel APR's */ +#define IOLN_KIPDR 020 +#define IOBA_KDPDR (IOPAGEBASE + 012320) +#define IOLN_KDPDR 020 +#define IOBA_KIPAR (IOPAGEBASE + 012340) +#define IOLN_KIPAR 020 +#define IOBA_KDPAR (IOPAGEBASE + 012360) +#define IOLN_KDPAR 020 +#define IOBA_TU (IOPAGEBASE + 012440) /* TU */ +#define IOLN_TU 040 +#define IOBA_MMR3 (IOPAGEBASE + 012516) /* MMR3 */ +#define IOLN_MMR3 002 +#define IOBA_TM (IOPAGEBASE + 012520) /* TM11 */ +#define IOLN_TM 014 +#define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */ +#define IOLN_TS 004 +#define IOBA_PCLK (IOPAGEBASE + 012540) /* KW11P */ +#define IOLN_PCLK 006 +#define IOBA_DC (IOPAGEBASE + 014000) /* DC11 */ +#define IOLN_DC (DCX_LINES * 010) +#define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */ +#define IOLN_RL 012 +#define IOBA_XQ (IOPAGEBASE + 014440) /* DEQNA/DELQA */ +#define IOLN_XQ 020 +#define IOBA_XQB (IOPAGEBASE + 014460) /* 2nd DEQNA/DELQA */ +#define IOLN_XQB 020 +#define IOBA_TQ (IOPAGEBASE + 014500) /* TMSCP */ +#define IOLN_TQ 004 +#define IOBA_XU (IOPAGEBASE + 014510) /* DEUNA/DELUA */ +#define IOLN_XU 010 +#define IOBA_DL (IOPAGEBASE + 016500) /* extra KL11/DL11 */ +#define IOLN_DL (DLX_LINES * 010) +#define IOBA_RP (IOPAGEBASE + 016700) /* RP/RM */ +#define IOLN_RP 054 +#define IOBA_CR (IOPAGEBASE + 017160) /* CD/CR/CM */ +#define IOLN_CR 010 +#define IOBA_RX (IOPAGEBASE + 017170) /* RX11 */ +#define IOLN_RX 004 +#define IOBA_RY (IOPAGEBASE + 017170) /* RY11 */ +#define IOLN_RY 004 +#define IOBA_KE (IOPAGEBASE + 017300) /* KE11-A */ +#define IOLN_KE 020 +#define IOBA_TC (IOPAGEBASE + 017340) /* TC11 */ +#define IOLN_TC 012 +#define IOBA_RK (IOPAGEBASE + 017400) /* RK11 */ +#define IOLN_RK 020 +#define IOBA_RC (IOPAGEBASE + 017440) /* RC11/RS64 */ +#define IOLN_RC 020 +#define IOBA_HK (IOPAGEBASE + 017440) /* RK611 */ +#define IOLN_HK 040 +#define IOBA_RF (IOPAGEBASE + 017460) /* RF11 */ +#define IOLN_RF 020 +#define IOBA_TA (IOPAGEBASE + 017500) /* TA11 */ +#define IOLN_TA 004 +#define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */ +#define IOLN_LPT 004 +#define IOBA_CTL (IOPAGEBASE + 017520) /* board ctrl */ +#define IOLN_CTL 010 +#define IOBA_CLK (IOPAGEBASE + 017546) /* KW11L */ +#define IOLN_CLK 002 +#define IOBA_PTR (IOPAGEBASE + 017550) /* PC11 reader */ +#define IOLN_PTR 004 +#define IOBA_PTP (IOPAGEBASE + 017554) /* PC11 punch */ +#define IOLN_PTP 004 +#define IOBA_TTI (IOPAGEBASE + 017560) /* DL11 rcv */ +#define IOLN_TTI 004 +#define IOBA_TTO (IOPAGEBASE + 017564) /* DL11 xmt */ +#define IOLN_TTO 004 +#define IOBA_SR (IOPAGEBASE + 017570) /* SR */ +#define IOLN_SR 002 +#define IOBA_MMR012 (IOPAGEBASE + 017572) /* MMR0-2 */ +#define IOLN_MMR012 006 +#define IOBA_UIPDR (IOPAGEBASE + 017600) /* user APR's */ +#define IOLN_UIPDR 020 +#define IOBA_UDPDR (IOPAGEBASE + 017620) +#define IOLN_UDPDR 020 +#define IOBA_UIPAR (IOPAGEBASE + 017640) +#define IOLN_UIPAR 020 +#define IOBA_UDPAR (IOPAGEBASE + 017660) +#define IOLN_UDPAR 020 +#define IOBA_GPR (IOPAGEBASE + 017700) /* GPR's */ +#define IOLN_GPR 010 +#define IOBA_UCTL (IOPAGEBASE + 017730) /* UBA ctrl */ +#define IOLN_UCTL 010 +#define IOBA_CPU (IOPAGEBASE + 017740) /* CPU reg */ +#define IOLN_CPU 036 +#define IOBA_PSW (IOPAGEBASE + 017776) /* PSW */ +#define IOLN_PSW 002 + +/* Interrupt assignments; within each level, priority is right to left */ + +#define IPL_HLVL 8 /* # int levels */ + +#define INT_V_PIR7 0 /* BR7 */ + +#define INT_V_CLK 0 /* BR6 */ +#define INT_V_PCLK 1 +#define INT_V_DTA 2 +#define INT_V_TA 3 +#define INT_V_PIR6 4 + +#define INT_V_RK 0 /* BR5 */ +#define INT_V_RL 1 +#define INT_V_RX 2 +#define INT_V_TM 3 +#define INT_V_RP 4 +#define INT_V_TS 5 +#define INT_V_HK 6 +#define INT_V_RQ 7 +#define INT_V_DZRX 8 +#define INT_V_DZTX 9 +#define INT_V_TQ 10 +#define INT_V_RY 11 +#define INT_V_XQ 12 +#define INT_V_XU 13 +#define INT_V_TU 14 +#define INT_V_RF 15 +#define INT_V_RC 16 +#define INT_V_PIR5 17 + +#define INT_V_TTI 0 /* BR4 */ +#define INT_V_TTO 1 +#define INT_V_PTR 2 +#define INT_V_PTP 3 +#define INT_V_LPT 4 +#define INT_V_VHRX 5 +#define INT_V_VHTX 6 +#define INT_V_CR 7 +#define INT_V_DLI 8 +#define INT_V_DLO 9 +#define INT_V_DCI 10 +#define INT_V_DCO 11 +#define INT_V_PIR4 12 + +#define INT_V_PIR3 0 /* BR3 */ +#define INT_V_PIR2 0 /* BR2 */ +#define INT_V_PIR1 0 /* BR1 */ + +#define INT_PIR7 (1u << INT_V_PIR7) +#define INT_CLK (1u << INT_V_CLK) +#define INT_PCLK (1u << INT_V_PCLK) +#define INT_DTA (1u << INT_V_DTA) +#define INT_TA (1u << INT_V_TA) +#define INT_PIR6 (1u << INT_V_PIR6) +#define INT_RK (1u << INT_V_RK) +#define INT_RL (1u << INT_V_RL) +#define INT_RX (1u << INT_V_RX) +#define INT_TM (1u << INT_V_TM) +#define INT_RP (1u << INT_V_RP) +#define INT_TS (1u << INT_V_TS) +#define INT_HK (1u << INT_V_HK) +#define INT_RQ (1u << INT_V_RQ) +#define INT_DZRX (1u << INT_V_DZRX) +#define INT_DZTX (1u << INT_V_DZTX) +#define INT_TQ (1u << INT_V_TQ) +#define INT_RY (1u << INT_V_RY) +#define INT_XQ (1u << INT_V_XQ) +#define INT_XU (1u << INT_V_XU) +#define INT_TU (1u << INT_V_TU) +#define INT_RF (1u << INT_V_RF) +#define INT_RC (1u << INT_V_RC) +#define INT_PIR5 (1u << INT_V_PIR5) +#define INT_PTR (1u << INT_V_PTR) +#define INT_PTP (1u << INT_V_PTP) +#define INT_TTI (1u << INT_V_TTI) +#define INT_TTO (1u << INT_V_TTO) +#define INT_LPT (1u << INT_V_LPT) +#define INT_VHRX (1u << INT_V_VHRX) +#define INT_VHTX (1u << INT_V_VHTX) +#define INT_CR (1u << INT_V_CR) +#define INT_DLI (1u << INT_V_DLI) +#define INT_DLO (1u << INT_V_DLO) +#define INT_DCI (1u << INT_V_DCI) +#define INT_DCO (1u << INT_V_DCO) +#define INT_PIR4 (1u << INT_V_PIR4) +#define INT_PIR3 (1u << INT_V_PIR3) +#define INT_PIR2 (1u << INT_V_PIR2) +#define INT_PIR1 (1u << INT_V_PIR1) + +#define IPL_CLK 6 /* int pri levels */ +#define IPL_PCLK 6 +#define IPL_DTA 6 +#define IPL_TA 6 +#define IPL_RK 5 +#define IPL_RL 5 +#define IPL_RX 5 +#define IPL_TM 5 +#define IPL_RP 5 +#define IPL_TS 5 +#define IPL_HK 5 +#define IPL_RQ 5 +#define IPL_DZRX 5 +#define IPL_DZTX 5 +#define IPL_TQ 5 +#define IPL_RY 5 +#define IPL_XQ 5 +#define IPL_XU 5 +#define IPL_TU 5 +#define IPL_RF 5 +#define IPL_RC 5 +#define IPL_PTR 4 +#define IPL_PTP 4 +#define IPL_TTI 4 +#define IPL_TTO 4 +#define IPL_LPT 4 +#define IPL_VHRX 4 +#define IPL_VHTX 4 +#define IPL_CR 4 +#define IPL_DLI 4 +#define IPL_DLO 4 +#define IPL_DCI 4 +#define IPL_DCO 4 + +#define IPL_PIR7 7 +#define IPL_PIR6 6 +#define IPL_PIR5 5 +#define IPL_PIR4 4 +#define IPL_PIR3 3 +#define IPL_PIR2 2 +#define IPL_PIR1 1 + +/* Device vectors */ + +#define VEC_Q 0000 /* vector base */ +#define VEC_PIRQ 0240 +#define VEC_TTI 0060 +#define VEC_TTO 0064 +#define VEC_PTR 0070 +#define VEC_PTP 0074 +#define VEC_CLK 0100 +#define VEC_PCLK 0104 +#define VEC_XQ 0120 +#define VEC_XU 0120 +#define VEC_RQ 0154 +#define VEC_RL 0160 +#define VEC_LPT 0200 +#define VEC_RF 0204 +#define VEC_HK 0210 +#define VEC_RC 0210 +#define VEC_RK 0220 +#define VEC_DTA 0214 +#define VEC_TM 0224 +#define VEC_TS 0224 +#define VEC_TU 0224 +#define VEC_CR 0230 +#define VEC_RP 0254 +#define VEC_TQ 0260 +#define VEC_TA 0260 +#define VEC_RX 0264 +#define VEC_RY 0264 +#define VEC_DLI 0300 +#define VEC_DLO 0304 +#define VEC_DCI 0300 +#define VEC_DCO 0304 +#define VEC_DZRX 0300 +#define VEC_DZTX 0304 +#define VEC_VHRX 0310 +#define VEC_VHTX 0314 + +/* Interrupt macros */ + +#define IVCL(dv) ((IPL_##dv * 32) + INT_V_##dv) +#define IREQ(dv) int_req[IPL_##dv] +#define SET_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] | (INT_##dv) +#define CLR_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] & ~(INT_##dv) + +/* Massbus definitions */ + +#define MBA_NUM 2 /* number of MBA's */ +#define MBA_RP 0 /* MBA for RP */ +#define MBA_TU 1 /* MBA for TU */ +#define MBA_RMASK 037 /* max 32 reg */ +#define MBE_NXD 1 /* nx drive */ +#define MBE_NXR 2 /* nx reg */ +#define MBE_GOE 3 /* err on GO */ + +/* CPU and FPU macros */ + +#define update_MM ((MMR0 & MMR0_FREEZE) == 0) +#define setTRAP(name) trap_req = trap_req | (name) +#define setCPUERR(name) CPUERR = CPUERR | (name) +#define ABORT(val) longjmp (save_env, (val)) +#define SP R[6] +#define PC R[7] + +/* Function prototypes */ + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat set_vec (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat auto_config (char *name, int32 nctrl); +t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp); + +int32 mba_rdbufW (uint32 mbus, int32 bc, uint16 *buf); +int32 mba_wrbufW (uint32 mbus, int32 bc, uint16 *buf); +int32 mba_chbufW (uint32 mbus, int32 bc, uint16 *buf); +int32 mba_get_bc (uint32 mbus); +int32 mba_get_csr (uint32 mbus); +void mba_upd_ata (uint32 mbus, uint32 val); +void mba_set_exc (uint32 mbus); +void mba_set_don (uint32 mbus); +void mba_set_enbdis (uint32 mb, t_bool dis); +t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc); + +int32 clk_cosched (int32 wait); + +#endif diff --git a/PDP11/pdp11_dl.c b/PDP11/pdp11_dl.c new file mode 100644 index 0000000..65ade26 --- /dev/null +++ b/PDP11/pdp11_dl.c @@ -0,0 +1,606 @@ +/* pdp11_dl.c: PDP-11 multiple terminal interface simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dli,dlo DL11 terminal input/output + + 20-May-2008 RMS Added modem control support +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "DL11 is not supported on the PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#error "DL11 is not supported on the VAX!" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif +#include "sim_sock.h" +#include "sim_tmxr.h" + +#define DLX_MASK (DLX_LINES - 1) +#define DLI_RCI 0 /* rcv ints */ +#define DLI_DSI 1 /* dset ints */ + +/* Modem control */ + +#define DLX_V_MDM (TTUF_V_UF + 0) +#define DLX_MDM (1u << DLX_V_MDM) + +/* registers */ + +#define DLICSR_DSI 0100000 /* dataset int, RO */ +#define DLICSR_RNG 0040000 /* ring, RO */ +#define DLICSR_CTS 0020000 /* CTS, RO */ +#define DLICSR_CDT 0010000 /* CDT, RO */ +#define DLICSR_SEC 0002000 /* sec rcv, RONI */ +#define DLICSR_DSIE 0000040 /* DSI ie, RW */ +#define DLICSR_SECX 0000010 /* sec xmt, RWNI */ +#define DLICSR_RTS 0000004 /* RTS, RW */ +#define DLICSR_DTR 0000002 /* DTR, RW */ +#define DLICSR_RD (CSR_DONE|CSR_IE) /* DL11C */ +#define DLICSR_WR (CSR_IE) +#define DLICSR_RD_M (DLICSR_DSI|DLICSR_RNG|DLICSR_CTS|DLICSR_CDT|DLICSR_SEC| \ + CSR_DONE|CSR_IE|DLICSR_DSIE|DLICSR_SECX|DLICSR_RTS|DLICSR_DTR) +#define DLICSR_WR_M (CSR_IE|DLICSR_DSIE|DLICSR_SECX|DLICSR_RTS|DLICSR_DTR) +#define DLIBUF_ERR 0100000 +#define DLIBUF_OVR 0040000 +#define DLIBUF_RBRK 0020000 +#define DLIBUF_RD (DLIBUF_ERR|DLIBUF_OVR|DLIBUF_RBRK|0377) +#define DLOCSR_MNT 0000004 /* maint, RWNI */ +#define DLOCSR_XBR 0000001 /* xmit brk, RWNI */ +#define DLOCSR_RD (CSR_DONE|CSR_IE|DLOCSR_MNT|DLOCSR_XBR) +#define DLOCSR_WR (CSR_IE|DLOCSR_MNT|DLOCSR_XBR) + +extern int32 int_req[IPL_HLVL]; +extern int32 tmxr_poll; + +uint16 dli_csr[DLX_LINES] = { 0 }; /* control/status */ +uint16 dli_buf[DLX_LINES] = { 0 }; +uint32 dli_ireq[2] = { 0, 0}; +uint16 dlo_csr[DLX_LINES] = { 0 }; /* control/status */ +uint8 dlo_buf[DLX_LINES] = { 0 }; +uint32 dlo_ireq = 0; +TMLN dlx_ldsc[DLX_LINES] = { 0 }; /* line descriptors */ +TMXR dlx_desc = { DLX_LINES, 0, 0, dlx_ldsc }; /* mux descriptor */ + +t_stat dlx_rd (int32 *data, int32 PA, int32 access); +t_stat dlx_wr (int32 data, int32 PA, int32 access); +t_stat dlx_reset (DEVICE *dptr); +t_stat dli_svc (UNIT *uptr); +t_stat dlo_svc (UNIT *uptr); +t_stat dlx_attach (UNIT *uptr, char *cptr); +t_stat dlx_detach (UNIT *uptr); +t_stat dlx_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dlx_show (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dlx_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dlx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dlx_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc); +void dlx_enbdis (int32 dis); +void dli_clr_int (int32 ln, uint32 wd); +void dli_set_int (int32 ln, uint32 wd); +int32 dli_iack (void); +void dlo_clr_int (int32 ln); +void dlo_set_int (int32 ln); +int32 dlo_iack (void); +void dlx_reset_ln (int32 ln); + +/* DLI data structures + + dli_dev DLI device descriptor + dli_unit DLI unit descriptor + dli_reg DLI register list +*/ + +DIB dli_dib = { + IOBA_DL, IOLN_DL, &dlx_rd, &dlx_wr, + 2, IVCL (DLI), VEC_DLI, { &dli_iack, &dlo_iack } + }; + +UNIT dli_unit = { UDATA (&dli_svc, 0, 0), KBD_POLL_WAIT }; + +REG dli_reg[] = { + { BRDATA (BUF, dli_buf, DEV_RDX, 16, DLX_LINES) }, + { BRDATA (CSR, dli_csr, DEV_RDX, 16, DLX_LINES) }, + { GRDATA (IREQ, dli_ireq[DLI_RCI], DEV_RDX, DLX_LINES, 0) }, + { GRDATA (DSI, dli_ireq[DLI_DSI], DEV_RDX, DLX_LINES, 0) }, + { DRDATA (LINES, dlx_desc.lines, 6), REG_HRO }, + { GRDATA (DEVADDR, dli_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVIOLN, dli_dib.lnt, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, dli_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB dli_mod[] = { + { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &dlx_summ }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dlx_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &dlx_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &dlx_show, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + &set_vec, &dlx_show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "lines", "LINES", + &dlx_set_lines, &dlx_show_lines }, + { 0 } + }; + +DEVICE dli_dev = { + "DLI", &dli_unit, dli_reg, dli_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &dlx_reset, + NULL, &dlx_attach, &dlx_detach, + &dli_dib, DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS + }; + +/* DLO data structures + + dlo_dev DLO device descriptor + dlo_unit DLO unit descriptor + dlo_reg DLO register list +*/ + +UNIT dlo_unit[] = { + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&dlo_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT } + }; + +REG dlo_reg[] = { + { BRDATA (BUF, dlo_buf, DEV_RDX, 8, DLX_LINES) }, + { BRDATA (CSR, dlo_csr, DEV_RDX, 16, DLX_LINES) }, + { GRDATA (IREQ, dlo_ireq, DEV_RDX, DLX_LINES, 0) }, + { URDATA (TIME, dlo_unit[0].wait, 10, 31, 0, + DLX_LINES, PV_LEFT) }, + { NULL } + }; + +MTAB dlo_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { DLX_MDM, 0, "no dataset", "NODATASET", NULL }, + { DLX_MDM, DLX_MDM, "dataset", "DATASET", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dlx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &dlx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &dlx_desc }, + { 0 } + }; + +DEVICE dlo_dev = { + "DLO", dlo_unit, dlo_reg, dlo_mod, + DLX_LINES, 10, 31, 1, 8, 8, + NULL, NULL, &dlx_reset, + NULL, NULL, NULL, + NULL, DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS + }; + +/* Terminal input routines */ + +t_stat dlx_rd (int32 *data, int32 PA, int32 access) +{ +int32 ln = ((PA - dli_dib.ba) >> 3) & DLX_MASK; + +switch ((PA >> 1) & 03) { /* decode PA<2:1> */ + + case 00: /* tti csr */ + *data = dli_csr[ln] & + ((dlo_unit[ln].flags & DLX_MDM)? DLICSR_RD_M: DLICSR_RD); + dli_csr[ln] &= ~DLICSR_DSI; /* clr DSI flag */ + dli_clr_int (ln, DLI_DSI); /* clr dset int req */ + return SCPE_OK; + + case 01: /* tti buf */ + *data = dli_buf[ln] & DLIBUF_RD; + dli_csr[ln] &= ~CSR_DONE; /* clr rcv done */ + dli_clr_int (ln, DLI_RCI); /* clr rcv int req */ + return SCPE_OK; + + case 02: /* tto csr */ + *data = dlo_csr[ln] & DLOCSR_RD; + return SCPE_OK; + + case 03: /* tto buf */ + *data = dlo_buf[ln]; + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +t_stat dlx_wr (int32 data, int32 PA, int32 access) +{ +int32 ln = ((PA - dli_dib.ba) >> 3) & DLX_MASK; +TMLN *lp = &dlx_ldsc[ln]; + +switch ((PA >> 1) & 03) { /* decode PA<2:1> */ + + case 00: /* tti csr */ + if (PA & 1) return SCPE_OK; /* odd byte RO */ + if ((data & CSR_IE) == 0) + dli_clr_int (ln, DLI_RCI); + else if ((dli_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) + dli_set_int (ln, DLI_RCI); + if (dlo_unit[ln].flags & DLX_MDM) { /* modem control */ + if ((data & DLICSR_DSIE) == 0) + dli_clr_int (ln, DLI_DSI); + else if ((dli_csr[ln] & (DLICSR_DSI|DLICSR_DSIE)) == DLICSR_DSI) + dli_set_int (ln, DLI_DSI); + if ((data ^ dli_csr[ln]) & DLICSR_DTR) { /* DTR change? */ + if ((data & DLICSR_DTR) && lp->conn) { /* setting DTR? */ + dli_csr[ln] = (dli_csr[ln] & ~DLICSR_RNG) | + (DLICSR_CDT|DLICSR_CTS|DLICSR_DSI); + if (data & DLICSR_DSIE) /* if ie, req int */ + dli_set_int (ln, DLI_DSI); + } /* end DTR 0->1 + ring */ + else { /* clearing DTR */ + if (lp->conn) { /* connected? */ + tmxr_linemsg (lp, "\r\nLine hangup\r\n"); + tmxr_reset_ln (lp); /* reset line */ + if (dli_csr[ln] & DLICSR_CDT) { /* carrier det? */ + dli_csr[ln] |= DLICSR_DSI; + if (data & DLICSR_DSIE) /* if ie, req int */ + dli_set_int (ln, DLI_DSI); + } + } + dli_csr[ln] &= ~(DLICSR_CDT|DLICSR_RNG|DLICSR_CTS); + /* clr CDT,RNG,CTS */ + } /* end DTR 1->0 */ + } /* end DTR chg */ + dli_csr[ln] = (uint16) ((dli_csr[ln] & ~DLICSR_WR_M) | (data & DLICSR_WR_M)); + } /* end modem */ + dli_csr[ln] = (uint16) ((dli_csr[ln] & ~DLICSR_WR) | (data & DLICSR_WR)); + return SCPE_OK; + + case 01: /* tti buf */ + return SCPE_OK; + + case 02: /* tto csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) + dlo_clr_int (ln); + else if ((dlo_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) + dlo_set_int (ln); + dlo_csr[ln] = (uint16) ((dlo_csr[ln] & ~DLOCSR_WR) | (data & DLOCSR_WR)); + return SCPE_OK; + + case 03: /* tto buf */ + if ((PA & 1) == 0) + dlo_buf[ln] = data & 0377; + dlo_csr[ln] &= ~CSR_DONE; + dlo_clr_int (ln); + sim_activate (&dlo_unit[ln], dlo_unit[ln].wait); + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +/* Terminal input service */ + +t_stat dli_svc (UNIT *uptr) +{ +int32 ln, c, temp; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +sim_activate (uptr, tmxr_poll); /* continue poll */ +ln = tmxr_poll_conn (&dlx_desc); /* look for connect */ +if (ln >= 0) { /* got one? rcv enb */ + dlx_ldsc[ln].rcve = 1; + if (dlo_unit[ln].flags & DLX_MDM) { /* modem control? */ + if (dli_csr[ln] & DLICSR_DTR) /* DTR already set? */ + dli_csr[ln] |= (DLICSR_CDT|DLICSR_CTS|DLICSR_DSI); + else dli_csr[ln] |= (DLICSR_RNG|DLICSR_DSI); /* no, ring */ + if (dli_csr[ln] & DLICSR_DSIE) /* if ie, */ + dli_set_int (ln, DLI_DSI); /* req int */ + } /* end modem */ + } /* end new conn */ +tmxr_poll_rx (&dlx_desc); /* poll for input */ +for (ln = 0; ln < DLX_LINES; ln++) { /* loop thru lines */ + if (dlx_ldsc[ln].conn) { /* connected? */ + if (temp = tmxr_getc_ln (&dlx_ldsc[ln])) { /* get char */ + if (temp & SCPE_BREAK) /* break? */ + c = DLIBUF_ERR|DLIBUF_RBRK; + else c = sim_tt_inpcvt (temp, TT_GET_MODE (dlo_unit[ln].flags)); + if (dli_csr[ln] & CSR_DONE) + c |= DLIBUF_ERR|DLIBUF_OVR; + else dli_csr[ln] |= CSR_DONE; + if (dli_csr[ln] & CSR_IE) + dli_set_int (ln, DLI_RCI); + dli_buf[ln] = c; + } + } + else if (dlo_unit[ln].flags & DLX_MDM) { /* discpnn & modem? */ + if (dli_csr[ln] & DLICSR_CDT) { /* carrier detect? */ + dli_csr[ln] |= DLICSR_DSI; /* dataset change */ + if (dli_csr[ln] & DLICSR_DSIE) /* if ie, */ + dli_set_int (ln, DLI_DSI); /* req int */ + } + dli_csr[ln] &= ~(DLICSR_CDT|DLICSR_RNG|DLICSR_CTS); + /* clr CDT,RNG,CTS */ + } + } +return SCPE_OK; +} + +/* Terminal output service */ + +t_stat dlo_svc (UNIT *uptr) +{ +int32 c; +int32 ln = uptr - dlo_unit; /* line # */ + +if (dlx_ldsc[ln].conn) { /* connected? */ + if (dlx_ldsc[ln].xmte) { /* tx enabled? */ + TMLN *lp = &dlx_ldsc[ln]; /* get line */ + c = sim_tt_outcvt (dlo_buf[ln], TT_GET_MODE (dlo_unit[ln].flags)); + if (c >= 0) tmxr_putc_ln (lp, c); /* output char */ + tmxr_poll_tx (&dlx_desc); /* poll xmt */ + } + else { + tmxr_poll_tx (&dlx_desc); /* poll xmt */ + sim_activate (uptr, dlo_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } +dlo_csr[ln] |= CSR_DONE; /* set done */ +if (dlo_csr[ln] & CSR_IE) + dlo_set_int (ln); +return SCPE_OK; +} + +/* Interrupt routines */ + +void dli_clr_int (int32 ln, uint32 wd) +{ +dli_ireq[wd] &= ~(1 << ln); /* clr rcv/dset int */ +if ((dli_ireq[DLI_RCI] | dli_ireq[DLI_DSI]) == 0) /* all clr? */ + CLR_INT (DLI); /* all clr? */ +else SET_INT (DLI); /* no, set intr */ +return; +} + +void dli_set_int (int32 ln, uint32 wd) +{ +dli_ireq[wd] |= (1 << ln); /* set rcv/dset int */ +SET_INT (DLI); /* set master intr */ +return; +} + +int32 dli_iack (void) +{ +int32 ln; + +for (ln = 0; ln < DLX_LINES; ln++) { /* find 1st line */ + if ((dli_ireq[DLI_RCI] | dli_ireq[DLI_DSI]) & (1 << ln)) { + dli_clr_int (ln, DLI_RCI); /* clr both req */ + dli_clr_int (ln, DLI_DSI); + return (dli_dib.vec + (ln * 010)); /* return vector */ + } + } +return 0; +} + +void dlo_clr_int (int32 ln) +{ +dlo_ireq &= ~(1 << ln); /* clr xmit int */ +if (dlo_ireq == 0) CLR_INT (DLO); /* all clr? */ +else SET_INT (DLO); /* no, set intr */ +return; +} + +void dlo_set_int (int32 ln) +{ +dlo_ireq |= (1 << ln); /* set xmit int */ +SET_INT (DLO); /* set master intr */ +return; +} + +int32 dlo_iack (void) +{ +int32 ln; + +for (ln = 0; ln < DLX_LINES; ln++) { /* find 1st line */ + if (dlo_ireq & (1 << ln)) { + dlo_clr_int (ln); /* clear intr */ + return (dli_dib.vec + (ln * 010) + 4); /* return vector */ + } + } +return 0; +} + +/* Reset */ + +t_stat dlx_reset (DEVICE *dptr) +{ +int32 ln; + +dlx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ +sim_cancel (&dli_unit); /* assume stop */ +if (dli_unit.flags & UNIT_ATT) /* if attached, */ + sim_activate (&dli_unit, tmxr_poll); /* activate */ +for (ln = 0; ln < DLX_LINES; ln++) /* for all lines */ + dlx_reset_ln (ln); +return auto_config (dli_dev.name, dlx_desc.lines); /* auto config */ +} + +/* Reset individual line */ + +void dlx_reset_ln (int32 ln) +{ +dli_buf[ln] = 0; /* clear buf */ +if (dlo_unit[ln].flags & DLX_MDM) /* modem */ + dli_csr[ln] &= DLICSR_DTR; /* dont clr DTR */ +else dli_csr[ln] = 0; +dlo_buf[ln] = 0; /* clear buf */ +dlo_csr[ln] = CSR_DONE; +sim_cancel (&dlo_unit[ln]); /* deactivate */ +dli_clr_int (ln, DLI_RCI); +dli_clr_int (ln, DLI_DSI); +dlo_clr_int (ln); +return; +} + +/* Attach master unit */ + +t_stat dlx_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&dlx_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +sim_activate (uptr, tmxr_poll); /* start poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat dlx_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&dlx_desc, uptr); /* detach */ +for (i = 0; i < DLX_LINES; i++) /* all lines, */ + dlx_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat dlx_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < DLX_LINES; i++) t = t + (dlx_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat dlx_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < DLX_LINES; i++) t = t + (dlx_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < DLX_LINES; i++) { + if (dlx_ldsc[i].conn) { + if (val) tmxr_fconns (st, &dlx_ldsc[i], i); + else tmxr_fstats (st, &dlx_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Enable/disable device */ + +void dlx_enbdis (int32 dis) +{ +if (dis) { + dli_dev.flags = dlo_dev.flags | DEV_DIS; + dlo_dev.flags = dlo_dev.flags | DEV_DIS; + } +else { + dli_dev.flags = dli_dev.flags & ~DEV_DIS; + dlo_dev.flags = dlo_dev.flags & ~DEV_DIS; + } +return; +} + +/* SHOW VECTOR processor */ + +t_stat dlx_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +return show_vec (st, uptr, dlx_desc.lines * 2, desc); +} + +/* Change number of lines */ + +t_stat dlx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newln = get_uint (cptr, 10, DLX_LINES, &r); +if ((r != SCPE_OK) || (newln == dlx_desc.lines)) return r; +if (newln == 0) return SCPE_ARG; +if (newln < dlx_desc.lines) { + for (i = newln, t = 0; i < dlx_desc.lines; i++) t = t | dlx_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < dlx_desc.lines; i++) { + if (dlx_ldsc[i].conn) { + tmxr_linemsg (&dlx_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&dlx_ldsc[i]); /* reset line */ + } + dlo_unit[i].flags |= UNIT_DIS; + dlx_reset_ln (i); + } + } +else { + for (i = dlx_desc.lines; i < newln; i++) { + dlo_unit[i].flags &= ~UNIT_DIS; + dlx_reset_ln (i); + } + } +dlx_desc.lines = newln; +dli_dib.lnt = newln * 010; /* upd IO page lnt */ +return auto_config (dli_dev.name, newln); /* auto config */ +} + +/* Show number of lines */ + +t_stat dlx_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "lines=%d", dlx_desc.lines); +return SCPE_OK; +} diff --git a/PDP11/pdp11_dz.c b/PDP11/pdp11_dz.c new file mode 100644 index 0000000..72652d9 --- /dev/null +++ b/PDP11/pdp11_dz.c @@ -0,0 +1,727 @@ +/* pdp11_dz.c: DZ11 terminal multiplexor simulator + + Copyright (c) 2001-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dz DZ11 terminal multiplexor + + 18-Jun-07 RMS Added UNIT_IDLE flag + 29-Oct-06 RMS Synced poll and clock + 22-Nov-05 RMS Revised for new terminal processing routines + 07-Jul-05 RMS Removed extraneous externs + 15-Jun-05 RMS Revised for new autoconfigure interface + 04-Apr-04 RMS Added per-line logging + 05-Jan-04 RMS Revised for tmxr library changes + 19-May-03 RMS Revised for new conditional compilation scheme + 09-May-03 RMS Added network device flag + 22-Dec-02 RMS Added break (framing error) support + 31-Oct-02 RMS Added 8b support + 12-Oct-02 RMS Added autoconfigure support + 29-Sep-02 RMS Fixed bug in set number of lines routine + Added variable vector support + New data structures + 22-Apr-02 RMS Updated for changes in sim_tmxr + 28-Apr-02 RMS Fixed interrupt acknowledge, fixed SHOW DZ ADDRESS + 14-Jan-02 RMS Added multiboard support + 30-Dec-01 RMS Added show statistics, set disconnect + Removed statistics registers + 03-Dec-01 RMS Modified for extended SET/SHOW + 09-Nov-01 RMS Added VAX support + 20-Oct-01 RMS Moved getchar from sim_tmxr, changed interrupt + logic to use tmxr_rqln + 06-Oct-01 RMS Fixed bug in carrier detect logic + 03-Oct-01 RMS Added support for BSD-style "ringless" modems + 27-Sep-01 RMS Fixed bug in xmte initialization + 17-Sep-01 RMS Added separate autodisconnect switch + 16-Sep-01 RMS Fixed modem control bit offsets +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +#define RANK_DZ 0 /* no autoconfig */ +#define DZ_8B_DFLT 0 +extern int32 int_req; + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +#define DZ_8B_DFLT TT_MODE_8B +extern int32 int_req[IPL_HLVL]; + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define DZ_8B_DFLT TT_MODE_8B +extern int32 int_req[IPL_HLVL]; +#endif + +#include "sim_sock.h" +#include "sim_tmxr.h" + +#if !defined (DZ_MUXES) +#define DZ_MUXES 1 +#endif +#if !defined (DZ_LINES) +#define DZ_LINES 8 +#endif + +#define DZ_MNOMASK (DZ_MUXES - 1) /* mask for mux no */ +#define DZ_LNOMASK (DZ_LINES - 1) /* mask for lineno */ +#define DZ_LMASK ((1 << DZ_LINES) - 1) /* mask of lines */ +#define DZ_SILO_ALM 16 /* silo alarm level */ + +/* DZCSR - 160100 - control/status register */ + +#define CSR_MAINT 0000010 /* maint - NI */ +#define CSR_CLR 0000020 /* clear */ +#define CSR_MSE 0000040 /* master scan enb */ +#define CSR_RIE 0000100 /* rcv int enb */ +#define CSR_RDONE 0000200 /* rcv done - RO */ +#define CSR_V_TLINE 8 /* xmit line - RO */ +#define CSR_TLINE (DZ_LNOMASK << CSR_V_TLINE) +#define CSR_SAE 0010000 /* silo alm enb */ +#define CSR_SA 0020000 /* silo alm - RO */ +#define CSR_TIE 0040000 /* xmit int enb */ +#define CSR_TRDY 0100000 /* xmit rdy - RO */ +#define CSR_RW (CSR_MSE | CSR_RIE | CSR_SAE | CSR_TIE) +#define CSR_MBZ (0004003 | CSR_CLR | CSR_MAINT) + +#define CSR_GETTL(x) (((x) >> CSR_V_TLINE) & DZ_LNOMASK) +#define CSR_PUTTL(x,y) x = ((x) & ~CSR_TLINE) | (((y) & DZ_LNOMASK) << CSR_V_TLINE) + +/* DZRBUF - 160102 - receive buffer, read only */ + +#define RBUF_CHAR 0000377 /* rcv char */ +#define RBUF_V_RLINE 8 /* rcv line */ +#define RBUF_PARE 0010000 /* parity err - NI */ +#define RBUF_FRME 0020000 /* frame err */ +#define RBUF_OVRE 0040000 /* overrun err - NI */ +#define RBUF_VALID 0100000 /* rcv valid */ +#define RBUF_MBZ 0004000 + +/* DZLPR - 160102 - line parameter register, write only, word access only */ + +#define LPR_V_LINE 0 /* line */ +#define LPR_LPAR 0007770 /* line pars - NI */ +#define LPR_RCVE 0010000 /* receive enb */ +#define LPR_GETLN(x) (((x) >> LPR_V_LINE) & DZ_LNOMASK) + +/* DZTCR - 160104 - transmission control register */ + +#define TCR_V_XMTE 0 /* xmit enables */ +#define TCR_V_DTR 8 /* DTRs */ + +/* DZMSR - 160106 - modem status register, read only */ + +#define MSR_V_RI 0 /* ring indicators */ +#define MSR_V_CD 8 /* carrier detect */ + +/* DZTDR - 160106 - transmit data, write only */ + +#define TDR_CHAR 0000377 /* xmit char */ +#define TDR_V_TBR 8 /* xmit break - NI */ + +extern int32 IREQ (HLVL); +extern int32 sim_switches; +extern FILE *sim_log; +extern int32 tmxr_poll; /* calibrated delay */ + +uint16 dz_csr[DZ_MUXES] = { 0 }; /* csr */ +uint16 dz_rbuf[DZ_MUXES] = { 0 }; /* rcv buffer */ +uint16 dz_lpr[DZ_MUXES] = { 0 }; /* line param */ +uint16 dz_tcr[DZ_MUXES] = { 0 }; /* xmit control */ +uint16 dz_msr[DZ_MUXES] = { 0 }; /* modem status */ +uint16 dz_tdr[DZ_MUXES] = { 0 }; /* xmit data */ +uint8 dz_sae[DZ_MUXES] = { 0 }; /* silo alarm enabled */ +uint32 dz_rxi = 0; /* rcv interrupts */ +uint32 dz_txi = 0; /* xmt interrupts */ +int32 dz_mctl = 0; /* modem ctrl enabled */ +int32 dz_auto = 0; /* autodiscon enabled */ +TMLN dz_ldsc[DZ_MUXES * DZ_LINES] = { 0 }; /* line descriptors */ +TMXR dz_desc = { DZ_MUXES * DZ_LINES, 0, 0, dz_ldsc }; /* mux descriptor */ + +DEVICE dz_dev; +t_stat dz_rd (int32 *data, int32 PA, int32 access); +t_stat dz_wr (int32 data, int32 PA, int32 access); +int32 dz_rxinta (void); +int32 dz_txinta (void); +t_stat dz_svc (UNIT *uptr); +t_stat dz_reset (DEVICE *dptr); +t_stat dz_attach (UNIT *uptr, char *cptr); +t_stat dz_detach (UNIT *uptr); +t_stat dz_clear (int32 dz, t_bool flag); +int32 dz_getc (int32 dz); +void dz_update_rcvi (void); +void dz_update_xmti (void); +void dz_clr_rxint (int32 dz); +void dz_set_rxint (int32 dz); +void dz_clr_txint (int32 dz); +void dz_set_txint (int32 dz); +t_stat dz_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dz_show (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dz_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat dz_setnl (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dz_set_log (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dz_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* DZ data structures + + dz_dev DZ device descriptor + dz_unit DZ unit list + dz_reg DZ register list +*/ + +DIB dz_dib = { + IOBA_DZ, IOLN_DZ * DZ_MUXES, &dz_rd, &dz_wr, + 2, IVCL (DZRX), VEC_DZRX, { &dz_rxinta, &dz_txinta } + }; + +UNIT dz_unit = { UDATA (&dz_svc, UNIT_IDLE|UNIT_ATTABLE|DZ_8B_DFLT, 0) }; + +REG dz_nlreg = { DRDATA (NLINES, dz_desc.lines, 6), PV_LEFT }; + +REG dz_reg[] = { + { BRDATA (CSR, dz_csr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA (RBUF, dz_rbuf, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA (LPR, dz_lpr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA (TCR, dz_tcr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA (MSR, dz_msr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA (TDR, dz_tdr, DEV_RDX, 16, DZ_MUXES) }, + { BRDATA (SAENB, dz_sae, DEV_RDX, 1, DZ_MUXES) }, + { GRDATA (RXINT, dz_rxi, DEV_RDX, DZ_MUXES, 0) }, + { GRDATA (TXINT, dz_txi, DEV_RDX, DZ_MUXES, 0) }, + { FLDATA (MDMCTL, dz_mctl, 0) }, + { FLDATA (AUTODS, dz_auto, 0) }, + { GRDATA (DEVADDR, dz_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, dz_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB dz_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &dz_desc }, + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &dz_summ }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &dz_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &dz_show, NULL }, + { MTAB_XTD|MTAB_VDV, 010, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &dz_show_vec, NULL }, +#if !defined (VM_PDP10) + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, +#endif + { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES", + &dz_setnl, NULL, &dz_nlreg }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "LOG", + &dz_set_log, NULL, &dz_desc }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "NOLOG", + &dz_set_nolog, NULL, &dz_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "LOG", NULL, + NULL, &dz_show_log, &dz_desc }, + { 0 } + }; + +DEVICE dz_dev = { + "DZ", &dz_unit, dz_reg, dz_mod, + 1, DEV_RDX, 8, 1, DEV_RDX, 8, + &tmxr_ex, &tmxr_dep, &dz_reset, + NULL, &dz_attach, &dz_detach, + &dz_dib, DEV_FLTA | DEV_DISABLE | DEV_NET | DEV_UBUS | DEV_QBUS + }; + +/* IO dispatch routines, I/O addresses 177601x0 - 177601x7 */ + +t_stat dz_rd (int32 *data, int32 PA, int32 access) +{ +int32 dz = ((PA - dz_dib.ba) >> 3) & DZ_MNOMASK; /* get mux num */ + +switch ((PA >> 1) & 03) { /* case on PA<2:1> */ + + case 00: /* CSR */ + *data = dz_csr[dz] = dz_csr[dz] & ~CSR_MBZ; + break; + + case 01: /* RBUF */ + dz_csr[dz] = dz_csr[dz] & ~CSR_SA; /* clr silo alarm */ + if (dz_csr[dz] & CSR_MSE) { /* scanner on? */ + dz_rbuf[dz] = dz_getc (dz); /* get top of silo */ + if (!dz_rbuf[dz]) dz_sae[dz] = 1; /* empty? re-enable */ + tmxr_poll_rx (&dz_desc); /* poll input */ + dz_update_rcvi (); /* update rx intr */ + } + else { + dz_rbuf[dz] = 0; /* no data */ + dz_update_rcvi (); /* no rx intr */ + } + *data = dz_rbuf[dz]; + break; + + case 02: /* TCR */ + *data = dz_tcr[dz]; + break; + + case 03: /* MSR */ + *data = dz_msr[dz]; + break; + } + +return SCPE_OK; +} + +t_stat dz_wr (int32 data, int32 PA, int32 access) +{ +int32 dz = ((PA - dz_dib.ba) >> 3) & DZ_MNOMASK; /* get mux num */ +int32 i, c, line; +TMLN *lp; + +switch ((PA >> 1) & 03) { /* case on PA<2:1> */ + + case 00: /* CSR */ + if (access == WRITEB) data = (PA & 1)? /* byte? merge */ + (dz_csr[dz] & 0377) | (data << 8): + (dz_csr[dz] & ~0377) | data; + if (data & CSR_CLR) dz_clear (dz, FALSE); /* clr? reset */ + if (data & CSR_MSE) /* MSE? start poll */ + sim_activate (&dz_unit, clk_cosched (tmxr_poll)); + else dz_csr[dz] &= ~(CSR_SA | CSR_RDONE | CSR_TRDY); + if ((data & CSR_RIE) == 0) dz_clr_rxint (dz); /* RIE = 0? */ + else if (((dz_csr[dz] & CSR_IE) == 0) && /* RIE 0->1? */ + ((dz_csr[dz] & CSR_SAE)? + (dz_csr[dz] & CSR_SA): (dz_csr[dz] & CSR_RDONE))) + dz_set_rxint (dz); + if ((data & CSR_TIE) == 0) dz_clr_txint (dz); /* TIE = 0? */ + else if (((dz_csr[dz] & CSR_TIE) == 0) && (dz_csr[dz] & CSR_TRDY)) + dz_set_txint (dz); + dz_csr[dz] = (dz_csr[dz] & ~CSR_RW) | (data & CSR_RW); + break; + + case 01: /* LPR */ + dz_lpr[dz] = data; + line = (dz * DZ_LINES) + LPR_GETLN (data); /* get line num */ + lp = &dz_ldsc[line]; /* get line desc */ + if (dz_lpr[dz] & LPR_RCVE) lp->rcve = 1; /* rcv enb? on */ + else lp->rcve = 0; /* else line off */ + tmxr_poll_rx (&dz_desc); /* poll input */ + dz_update_rcvi (); /* update rx intr */ + break; + + case 02: /* TCR */ + if (access == WRITEB) data = (PA & 1)? /* byte? merge */ + (dz_tcr[dz] & 0377) | (data << 8): + (dz_tcr[dz] & ~0377) | data; + if (dz_mctl) { /* modem ctl? */ + dz_msr[dz] |= ((data & 0177400) & /* dcd |= dtr & ring */ + ((dz_msr[dz] & DZ_LMASK) << MSR_V_CD)); + dz_msr[dz] &= ~(data >> TCR_V_DTR); /* ring &= ~dtr */ + if (dz_auto) { /* auto disconnect? */ + int32 drop; + drop = (dz_tcr[dz] & ~data) >> TCR_V_DTR; /* drop = dtr & ~data */ + for (i = 0; i < DZ_LINES; i++) { /* drop hangups */ + line = (dz * DZ_LINES) + i; /* get line num */ + lp = &dz_ldsc[line]; /* get line desc */ + if (lp->conn && (drop & (1 << i))) { + tmxr_linemsg (lp, "\r\nLine hangup\r\n"); + tmxr_reset_ln (lp); /* reset line, cdet */ + dz_msr[dz] &= ~(1 << (i + MSR_V_CD)); + } /* end if drop */ + } /* end for */ + } /* end if auto */ + } /* end if modem */ + dz_tcr[dz] = data; + tmxr_poll_tx (&dz_desc); /* poll output */ + dz_update_xmti (); /* update int */ + break; + + case 03: /* TDR */ + if (PA & 1) { /* odd byte? */ + dz_tdr[dz] = (dz_tdr[dz] & 0377) | (data << 8); /* just save */ + break; + } + dz_tdr[dz] = data; + if (dz_csr[dz] & CSR_MSE) { /* enabled? */ + line = (dz * DZ_LINES) + CSR_GETTL (dz_csr[dz]); + lp = &dz_ldsc[line]; /* get line desc */ + c = sim_tt_outcvt (dz_tdr[dz], TT_GET_MODE (dz_unit.flags)); + if (c >= 0) tmxr_putc_ln (lp, c); /* store char */ + tmxr_poll_tx (&dz_desc); /* poll output */ + dz_update_xmti (); /* update int */ + } + break; + } + +return SCPE_OK; +} + +/* Unit service routine + + The DZ11 polls to see if asynchronous activity has occurred and now + needs to be processed. The polling interval is controlled by the clock + simulator, so for most environments, it is calibrated to real time. + Typical polling intervals are 50-60 times per second. + + The simulator assumes that software enables all of the multiplexors, + or none of them. +*/ + +t_stat dz_svc (UNIT *uptr) +{ +int32 dz, t, newln; + +for (dz = t = 0; dz < DZ_MUXES; dz++) /* check enabled */ + t = t | (dz_csr[dz] & CSR_MSE); +if (t) { /* any enabled? */ + newln = tmxr_poll_conn (&dz_desc); /* poll connect */ + if ((newln >= 0) && dz_mctl) { /* got a live one? */ + dz = newln / DZ_LINES; /* get mux num */ + if (dz_tcr[dz] & (1 << (newln + TCR_V_DTR))) /* DTR set? */ + dz_msr[dz] |= (1 << (newln + MSR_V_CD)); /* set cdet */ + else dz_msr[dz] |= (1 << newln); /* set ring */ + } + tmxr_poll_rx (&dz_desc); /* poll input */ + dz_update_rcvi (); /* upd rcv intr */ + tmxr_poll_tx (&dz_desc); /* poll output */ + dz_update_xmti (); /* upd xmt intr */ + sim_activate (uptr, tmxr_poll); /* reactivate */ + } +return SCPE_OK; +} + +/* Get first available character for mux, if any */ + +int32 dz_getc (int32 dz) +{ +uint32 i, line, c; + +for (i = c = 0; (i < DZ_LINES) && (c == 0); i++) { /* loop thru lines */ + line = (dz * DZ_LINES) + i; /* get line num */ + c = tmxr_getc_ln (&dz_ldsc[line]); /* test for input */ + if (c & SCPE_BREAK) c = RBUF_VALID | RBUF_FRME; /* break? frame err */ + if (c) c = c | (i << RBUF_V_RLINE); /* or in line # */ + } /* end for */ +return c; +} + +/* Update receive interrupts */ + +void dz_update_rcvi (void) +{ +int32 i, dz, line, scnt[DZ_MUXES]; +TMLN *lp; + +for (dz = 0; dz < DZ_MUXES; dz++) { /* loop thru muxes */ + scnt[dz] = 0; /* clr input count */ + for (i = 0; i < DZ_LINES; i++) { /* poll lines */ + line = (dz * DZ_LINES) + i; /* get line num */ + lp = &dz_ldsc[line]; /* get line desc */ + scnt[dz] = scnt[dz] + tmxr_rqln (lp); /* sum buffers */ + if (dz_mctl && !lp->conn) /* if disconn */ + dz_msr[dz] &= ~(1 << (i + MSR_V_CD)); /* reset car det */ + } + } +for (dz = 0; dz < DZ_MUXES; dz++) { /* loop thru muxes */ + if (scnt[dz] && (dz_csr[dz] & CSR_MSE)) { /* input & enabled? */ + dz_csr[dz] |= CSR_RDONE; /* set done */ + if (dz_sae[dz] && (scnt[dz] >= DZ_SILO_ALM)) { /* alm enb & cnt hi? */ + dz_csr[dz] |= CSR_SA; /* set status */ + dz_sae[dz] = 0; /* disable alarm */ + } + } + else dz_csr[dz] &= ~CSR_RDONE; /* no, clear done */ + if ((dz_csr[dz] & CSR_RIE) && /* int enable */ + ((dz_csr[dz] & CSR_SAE)? + (dz_csr[dz] & CSR_SA): (dz_csr[dz] & CSR_RDONE))) + dz_set_rxint (dz); /* and alm/done? */ + else dz_clr_rxint (dz); /* no, clear int */ + } +return; +} + +/* Update transmit interrupts */ + +void dz_update_xmti (void) +{ +int32 dz, linemask, i, j, line; + +for (dz = 0; dz < DZ_MUXES; dz++) { /* loop thru muxes */ + linemask = dz_tcr[dz] & DZ_LMASK; /* enabled lines */ + dz_csr[dz] &= ~CSR_TRDY; /* assume not rdy */ + j = CSR_GETTL (dz_csr[dz]); /* start at current */ + for (i = 0; i < DZ_LINES; i++) { /* loop thru lines */ + j = (j + 1) & DZ_LNOMASK; /* next line */ + line = (dz * DZ_LINES) + j; /* get line num */ + if ((linemask & (1 << j)) && dz_ldsc[line].xmte) { + CSR_PUTTL (dz_csr[dz], j); /* put ln in csr */ + dz_csr[dz] |= CSR_TRDY; /* set xmt rdy */ + break; + } + } + if ((dz_csr[dz] & CSR_TIE) && (dz_csr[dz] & CSR_TRDY)) /* ready plus int? */ + dz_set_txint (dz); + else dz_clr_txint (dz); /* no int req */ + } +return; +} + +/* Interrupt routines */ + +void dz_clr_rxint (int32 dz) +{ +dz_rxi = dz_rxi & ~(1 << dz); /* clr mux rcv int */ +if (dz_rxi == 0) CLR_INT (DZRX); /* all clr? */ +else SET_INT (DZRX); /* no, set intr */ +return; +} + +void dz_set_rxint (int32 dz) +{ +dz_rxi = dz_rxi | (1 << dz); /* set mux rcv int */ +SET_INT (DZRX); /* set master intr */ +return; +} + +int32 dz_rxinta (void) +{ +int32 dz; + +for (dz = 0; dz < DZ_MUXES; dz++) { /* find 1st mux */ + if (dz_rxi & (1 << dz)) { + dz_clr_rxint (dz); /* clear intr */ + return (dz_dib.vec + (dz * 010)); /* return vector */ + } + } +return 0; +} + +void dz_clr_txint (int32 dz) +{ +dz_txi = dz_txi & ~(1 << dz); /* clr mux xmt int */ +if (dz_txi == 0) CLR_INT (DZTX); /* all clr? */ +else SET_INT (DZTX); /* no, set intr */ +return; +} + +void dz_set_txint (int32 dz) +{ +dz_txi = dz_txi | (1 << dz); /* set mux xmt int */ +SET_INT (DZTX); /* set master intr */ +return; +} + +int32 dz_txinta (void) +{ +int32 dz; + +for (dz = 0; dz < DZ_MUXES; dz++) { /* find 1st mux */ + if (dz_txi & (1 << dz)) { + dz_clr_txint (dz); /* clear intr */ + return (dz_dib.vec + 4 + (dz * 010)); /* return vector */ + } + } +return 0; +} + +/* Device reset */ + +t_stat dz_clear (int32 dz, t_bool flag) +{ +int32 i, line; + +dz_csr[dz] = 0; /* clear CSR */ +dz_rbuf[dz] = 0; /* silo empty */ +dz_lpr[dz] = 0; /* no params */ +if (flag) dz_tcr[dz] = 0; /* INIT? clr all */ +else dz_tcr[dz] &= ~0377; /* else save dtr */ +dz_tdr[dz] = 0; +dz_sae[dz] = 1; /* alarm on */ +dz_clr_rxint (dz); /* clear int */ +dz_clr_txint (dz); +for (i = 0; i < DZ_LINES; i++) { /* loop thru lines */ + line = (dz * DZ_LINES) + i; + if (!dz_ldsc[line].conn) dz_ldsc[line].xmte = 1; /* set xmt enb */ + dz_ldsc[line].rcve = 0; /* clr rcv enb */ + } +return SCPE_OK; +} + +t_stat dz_reset (DEVICE *dptr) +{ +int32 i, ndev; + +for (i = 0; i < DZ_MUXES; i++) dz_clear (i, TRUE); /* init muxes */ +dz_rxi = dz_txi = 0; /* clr master int */ +CLR_INT (DZRX); +CLR_INT (DZTX); +sim_cancel (&dz_unit); /* stop poll */ +ndev = ((dptr->flags & DEV_DIS)? 0: (dz_desc.lines / DZ_LINES)); +return auto_config (dptr->name, ndev); /* auto config */ +} + +/* Attach */ + +t_stat dz_attach (UNIT *uptr, char *cptr) +{ +t_stat r; +extern int32 sim_switches; + +dz_mctl = dz_auto = 0; /* modem ctl off */ +r = tmxr_attach (&dz_desc, uptr, cptr); /* attach mux */ +if (r != SCPE_OK) return r; /* error? */ +if (sim_switches & SWMASK ('M')) { /* modem control? */ + dz_mctl = 1; + printf ("Modem control activated\n"); + if (sim_log) fprintf (sim_log, "Modem control activated\n"); + if (sim_switches & SWMASK ('A')) { /* autodisconnect? */ + dz_auto = 1; + printf ("Auto disconnect activated\n"); + if (sim_log) fprintf (sim_log, "Auto disconnect activated\n"); + } + } +return SCPE_OK; +} + +/* Detach */ + +t_stat dz_detach (UNIT *uptr) +{ +return tmxr_detach (&dz_desc, uptr); +} + +/* Show summary processor */ + +t_stat dz_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < dz_desc.lines; i++) { /* get num conn */ + if (dz_ldsc[i].conn) t = t + 1; + } +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat dz_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < dz_desc.lines; i++) { /* loop thru conn */ + if (dz_ldsc[i].conn) { + t = 1; + if (val) tmxr_fconns (st, &dz_ldsc[i], i); + else tmxr_fstats (st, &dz_ldsc[i], i); + } + } +if (t == 0) fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* SET LINES processor */ + +t_stat dz_setnl (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t, ndev; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); +if ((r != SCPE_OK) || (newln == dz_desc.lines)) return r; +if ((newln == 0) || (newln % DZ_LINES)) return SCPE_ARG; +if (newln < dz_desc.lines) { + for (i = newln, t = 0; i < dz_desc.lines; i++) t = t | dz_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < dz_desc.lines; i++) { + if (dz_ldsc[i].conn) { + tmxr_linemsg (&dz_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&dz_ldsc[i]); /* reset line */ + } + if ((i % DZ_LINES) == (DZ_LINES - 1)) + dz_clear (i / DZ_LINES, TRUE); /* reset mux */ + } + } +dz_dib.lnt = (newln / DZ_LINES) * IOLN_DZ; /* set length */ +dz_desc.lines = newln; +ndev = ((dz_dev.flags & DEV_DIS)? 0: (dz_desc.lines / DZ_LINES)); +return auto_config (dz_dev.name, ndev); /* auto config */ +} + +/* SHOW VECTOR processor */ + +t_stat dz_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +return show_vec (st, uptr, ((dz_desc.lines * 2) / DZ_LINES), desc); +} + +/* SET LOG processor */ + +t_stat dz_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +char *tptr; +t_stat r; +int32 ln; + +if (cptr == NULL) return SCPE_ARG; +tptr = strchr (cptr, '='); +if ((tptr == NULL) || (*tptr == 0)) return SCPE_ARG; +*tptr++ = 0; +ln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); +if ((r != SCPE_OK) || (ln >= dz_desc.lines)) return SCPE_ARG; +return tmxr_set_log (NULL, ln, tptr, desc); +} + +/* SET NOLOG processor */ + +t_stat dz_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +t_stat r; +int32 ln; + +if (cptr == NULL) return SCPE_ARG; +ln = (int32) get_uint (cptr, 10, (DZ_MUXES * DZ_LINES), &r); +if ((r != SCPE_OK) || (ln >= dz_desc.lines)) return SCPE_ARG; +return tmxr_set_nolog (NULL, ln, NULL, desc); +} + +/* SHOW LOG processor */ + +t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i; + +for (i = 0; i < dz_desc.lines; i++) { + fprintf (st, "line %d: ", i); + tmxr_show_log (st, NULL, i, desc); + fprintf (st, "\n"); + } +return SCPE_OK; +} + + + diff --git a/PDP11/pdp11_fp.c b/PDP11/pdp11_fp.c new file mode 100644 index 0000000..dc2f956 --- /dev/null +++ b/PDP11/pdp11_fp.c @@ -0,0 +1,1187 @@ +/* pdp11_fp.c: PDP-11 floating point simulator (32b version) + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 04-Oct-04 RMS Added FIS instructions + 19-Jan-03 RMS Changed mode definitions for Apple Dev Kit conflict + 08-Oct-02 RMS Fixed macro definitions + 05-Jun-98 RMS Fixed implementation specific shift bugs + 20-Apr-98 RMS Fixed bug in MODf integer truncation + 17-Apr-98 RMS Fixed bug in STCfi range check + 16-Apr-98 RMS Fixed bugs in STEXP, STCfi, round/pack + 09-Apr-98 RMS Fixed bug in LDEXP + 04-Apr-98 RMS Fixed bug in MODf condition codes + + This module simulates the PDP-11 floating point unit (FP11 series). + It is called from the instruction decoder for opcodes 170000:177777. + + The floating point unit recognizes three instruction formats: + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ no operand + | 1 1 1 1| 0 0 0 0 0 0| opcode | 170000: + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170077 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ one operand + | 1 1 1 1| 0 0 0| opcode | dest spec | 170100: + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 170777 + + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ register + operand + | 1 1 1 1| opcode | fac | dest spec | 171000: + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ 177777 + + The instruction space is further extended through use of the floating + point status register (FPS) mode bits. Three mode bits affect how + instructions are interpreted: + + FPS_D if 0, floating registers are single precision + if 1, floating registers are double precision + + FPS_L if 0, integer operands are word + if 1, integer operands are longword + + FPS_T if 0, floating operations are rounded + if 1, floating operations are truncated + + FPS also contains the condition codes for the floating point unit, + and exception enable bits for individual error conditions. Exceptions + cause a trap through 0244, unless the individual exception, or all + exceptions, are disabled. Illegal address mode, undefined variable, + and divide by zero abort the current instruction; all other exceptions + permit the instruction to complete. (Aborts are implemented as traps + that request an "interrupt" trap. If an interrupt is pending, it is + serviced; if not, trap_req is updated and processing continues.) + + Floating point specifiers are similar to integer specifiers, with + the length of the operand being up to 8 bytes. In two specific cases, + the floating point unit reads or writes only two bytes, rather than + the length specified by the operand type: + + register for integers, only 16b are accessed; if the + operand is 32b, these are the high order 16b + of the operand + + immediate for integers or floating point, only 16b are + accessed; if the operand is 32b or 64b, these + are the high order 16b of the operand +*/ + +#include "pdp11_defs.h" + +/* Floating point status register */ + +#define FPS_ER (1u << FPS_V_ER) /* error */ +#define FPS_ID (1u << FPS_V_ID) /* interrupt disable */ +#define FPS_IUV (1u << FPS_V_IUV) /* int on undef var */ +#define FPS_IU (1u << FPS_V_IU) /* int on underflow */ +#define FPS_IV (1u << FPS_V_IV) /* int on overflow */ +#define FPS_IC (1u << FPS_V_IC) /* int on conv error */ +#define FPS_D (1u << FPS_V_D) /* single/double */ +#define FPS_L (1u << FPS_V_L) /* word/long */ +#define FPS_T (1u << FPS_V_T) /* round/truncate */ +#define FPS_N (1u << FPS_V_N) +#define FPS_Z (1u << FPS_V_Z) +#define FPS_V (1u << FPS_V_V) +#define FPS_C (1u << FPS_V_C) +#define FPS_CC (FPS_N + FPS_Z + FPS_V + FPS_C) +#define FPS_RW (FPS_ER + FPS_ID + FPS_IUV + FPS_IU + FPS_IV + \ + FPS_IC + FPS_D + FPS_L + FPS_T + FPS_CC) + +/* Floating point exception codes */ + +#define FEC_OP 2 /* illegal op/mode */ +#define FEC_DZRO 4 /* divide by zero */ +#define FEC_ICVT 6 /* conversion error */ +#define FEC_OVFLO 8 /* overflow */ +#define FEC_UNFLO 10 /* underflow */ +#define FEC_UNDFV 12 /* undef variable */ + +/* Floating point format, all assignments 32b relative */ + +#define FP_V_SIGN (63 - 32) /* high lw: sign */ +#define FP_V_EXP (55 - 32) /* exponent */ +#define FP_V_HB FP_V_EXP /* hidden bit */ +#define FP_V_F0 (48 - 32) /* fraction 0 */ +#define FP_V_F1 (32 - 32) /* fraction 1 */ +#define FP_V_FROUND (31 - 32) /* f round point */ +#define FP_V_F2 16 /* low lw: fraction 2 */ +#define FP_V_F3 0 /* fraction 3 */ +#define FP_V_DROUND (-1) /* d round point */ +#define FP_M_EXP 0377 +#define FP_SIGN (1u << FP_V_SIGN) +#define FP_EXP (FP_M_EXP << FP_V_EXP) +#define FP_HB (1u << FP_V_HB) +#define FP_FRACH ((1u << FP_V_HB) - 1) +#define FP_FRACL 0xFFFFFFFF +#define FP_BIAS 0200 /* exponent bias */ +#define FP_GUARD 3 /* guard bits */ + +/* Data lengths */ + +#define WORD 2 +#define LONG 4 +#define QUAD 8 + +/* Double precision operations on 64b quantities */ + +#define F_LOAD(qd,ac,ds) \ + ds.h = ac.h; ds.l = (qd)? ac.l: 0 +#define F_LOAD_P(qd,ac,ds) \ + ds->h = ac.h; ds->l = (qd)? ac.l: 0 +#define F_LOAD_FRAC(qd,ac,ds) \ + ds.h = (ac.h & FP_FRACH) | FP_HB; \ + ds.l = (qd)? ac.l: 0 +#define F_STORE(qd,sr,ac) \ + ac.h = sr.h; if ((qd)) ac.l = sr.l +#define F_STORE_P(qd,sr,ac) \ + ac.h = sr->h; if ((qd)) ac.l = sr->l +#define F_GET_FRAC_P(sr,ds) \ + ds.l = sr->l; \ + ds.h = (sr->h & FP_FRACH) | FP_HB +#define F_ADD(s2,s1,ds) \ + ds.l = (s1.l + s2.l) & 0xFFFFFFFF; \ + ds.h = (s1.h + s2.h + (ds.l < s2.l)) & 0xFFFFFFFF +#define F_SUB(s2,s1,ds) \ + ds.h = (s1.h - s2.h - (s1.l < s2.l)) & 0xFFFFFFFF; \ + ds.l = (s1.l - s2.l) & 0xFFFFFFFF +#define F_LT(x,y) ((x.h < y.h) || ((x.h == y.h) && (x.l < y.l))) +#define F_LT_AP(x,y) (((x->h & ~FP_SIGN) < (y->h & ~FP_SIGN)) || \ + (((x->h & ~FP_SIGN) == (y->h & ~FP_SIGN)) && (x->l < y->l))) +#define F_LSH_V(sr,n,ds) \ + ds.h = (((n) >= 32)? (sr.l << ((n) - 32)): \ + (sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \ + & 0xFFFFFFFF; \ + ds.l = ((n) >= 32)? 0: (sr.l << (n)) & 0xFFFFFFFF +#define F_RSH_V(sr,n,ds) \ + ds.l = (((n) >= 32)? (sr.h >> ((n) - 32)) & and_mask[64 - (n)]: \ + ((sr.l >> (n)) & and_mask[32 - (n)]) | \ + (sr.h << (32 - (n)))) & 0xFFFFFFFF; \ + ds.h = ((n) >= 32)? 0: \ + ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF + +/* For the constant shift macro, arguments must in the range [2,31] */ + +#define F_LSH_1(ds) ds.h = ((ds.h << 1) | ((ds.l >> 31) & 1)) & 0xFFFFFFFF; \ + ds.l = (ds.l << 1) & 0xFFFFFFFF +#define F_RSH_1(ds) ds.l = ((ds.l >> 1) & 0x7FFFFFFF) | ((ds.h & 1) << 31); \ + ds.h = ((ds.h >> 1) & 0x7FFFFFFF) +#define F_LSH_K(sr,n,ds) \ + ds.h = ((sr.h << (n)) | ((sr.l >> (32 - (n))) & and_mask[n])) \ + & 0xFFFFFFFF; \ + ds.l = (sr.l << (n)) & 0xFFFFFFFF +#define F_RSH_K(sr,n,ds) \ + ds.l = (((sr.l >> (n)) & and_mask[32 - (n)]) | \ + (sr.h << (32 - (n)))) & 0xFFFFFFFF; \ + ds.h = ((sr.h >> (n)) & and_mask[32 - (n)]) & 0xFFFFFFFF +#define F_LSH_GUARD(ds) F_LSH_K(ds,FP_GUARD,ds) +#define F_RSH_GUARD(ds) F_RSH_K(ds,FP_GUARD,ds) + +#define GET_BIT(ir,n) (((ir) >> (n)) & 1) +#define GET_SIGN(ir) GET_BIT((ir), FP_V_SIGN) +#define GET_EXP(ir) (((ir) >> FP_V_EXP) & FP_M_EXP) +#define GET_SIGN_L(ir) GET_BIT((ir), 31) +#define GET_SIGN_W(ir) GET_BIT((ir), 15) + +extern jmp_buf save_env; +extern uint32 cpu_type; +extern int32 FEC, FEA, FPS; +extern int32 CPUERR, trap_req; +extern int32 N, Z, V, C; +extern int32 R[8]; +extern int32 STKLIM; +extern int32 cm, isenable, dsenable, MMR0, MMR1; +extern fpac_t FR[6]; + +fpac_t zero_fac = { 0, 0 }; +fpac_t one_fac = { 1, 0 }; +fpac_t fround_fac = { (1u << (FP_V_FROUND + 32)), 0 }; +fpac_t fround_guard_fac = { 0, (1u << (FP_V_FROUND + FP_GUARD)) }; +fpac_t dround_guard_fac = { (1u << (FP_V_DROUND + FP_GUARD)), 0 }; +fpac_t fmask_fac = { 0xFFFFFFFF, (1u << (FP_V_HB + FP_GUARD + 1)) - 1 }; +static const uint32 and_mask[33] = { 0, + 0x1, 0x3, 0x7, 0xF, + 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, + 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, + 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF + }; +int32 backup_PC; +int32 fpnotrap (int32 code); +int32 GeteaFP (int32 spec, int32 len); + +uint32 ReadI (int32 addr, int32 spec, int32 len); +void ReadFP (fpac_t *fac, int32 addr, int32 spec, int32 len); +void WriteI (int32 data, int32 addr, int32 spec, int32 len); +void WriteFP (fpac_t *data, int32 addr, int32 spec, int32 len); +int32 setfcc (int32 old_status, int32 result_high, int32 newV); +int32 addfp11 (fpac_t *src1, fpac_t *src2); +int32 mulfp11 (fpac_t *src1, fpac_t *src2); +int32 divfp11 (fpac_t *src1, fpac_t *src2); +int32 modfp11 (fpac_t *src1, fpac_t *src2, fpac_t *frac); +void frac_mulfp11 (fpac_t *src1, fpac_t *src2); +int32 roundfp11 (fpac_t *src); +int32 round_and_pack (fpac_t *fac, int32 exp, fpac_t *frac, int r); + +extern int32 GeteaW (int32 spec); +extern int32 ReadW (int32 addr); +extern void WriteW (int32 data, int32 addr); +extern void set_stack_trap (int32 adr); + +/* Set up for instruction decode and execution */ + +void fp11 (int32 IR) +{ +int32 dst, ea, ac, dstspec; +int32 i, qdouble, lenf, leni; +int32 newV, exp, sign; +fpac_t fac, fsrc, modfrac; +static const uint32 i_limit[2][2] = { + { 0x80000000, 0x80010000 }, + { 0x80000000, 0x80000001 } + }; + +backup_PC = PC; /* save PC for FEA */ +ac = (IR >> 6) & 03; /* fac is IR<7:6> */ +dstspec = IR & 077; +qdouble = FPS & FPS_D; +lenf = qdouble? QUAD: LONG; +switch ((IR >> 8) & 017) { /* decode IR<11:8> */ + + case 000: + switch (ac) { /* decode IR<7:6> */ + + case 0: /* specials */ + if (IR == 0170000) { /* CFCC */ + N = (FPS >> PSW_V_N) & 1; + Z = (FPS >> PSW_V_Z) & 1; + V = (FPS >> PSW_V_V) & 1; + C = (FPS >> PSW_V_C) & 1; + } + else if (IR == 0170001) /* SETF */ + FPS = FPS & ~FPS_D; + else if (IR == 0170002) /* SETI */ + FPS = FPS & ~FPS_L; + else if (IR == 0170011) /* SETD */ + FPS = FPS | FPS_D; + else if (IR == 0170012) /* SETL */ + FPS = FPS | FPS_L; + else fpnotrap (FEC_OP); + break; + + case 1: /* LDFPS */ + dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec)); + FPS = dst & FPS_RW; + break; + + case 2: /* STFPS */ + FPS = FPS & FPS_RW; + if (dstspec <= 07) R[dstspec] = FPS; + else WriteW (FPS, GeteaW (dstspec)); + break; + + case 3: /* STST */ + if (dstspec <= 07) R[dstspec] = FEC; + else WriteI ((FEC << 16) | FEA, GeteaFP (dstspec, LONG), + dstspec, LONG); + break; + } /* end switch <7:6> */ + break; /* end case 0 */ + + case 001: + switch (ac) { /* decode IR<7:6> */ + + case 0: /* CLRf */ + WriteFP (&zero_fac, GeteaFP (dstspec, lenf), dstspec, lenf); + FPS = (FPS & ~FPS_CC) | FPS_Z; + break; + + case 1: /* TSTf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + FPS = setfcc (FPS, fsrc.h, 0); + break; + + case 2: /* ABSf */ + ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + else fsrc.h = fsrc.h & ~FP_SIGN; + WriteFP (&fsrc, ea, dstspec, lenf); + FPS = setfcc (FPS, fsrc.h, 0); + break; + + case 3: /* NEGf */ + ReadFP (&fsrc, ea = GeteaFP (dstspec, lenf), dstspec, lenf); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + else fsrc.h = fsrc.h ^ FP_SIGN; + WriteFP (&fsrc, ea, dstspec, lenf); + FPS = setfcc (FPS, fsrc.h, 0); + break; + } /* end switch <7:6> */ + break; /* end case 1 */ + + case 005: /* LDf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_STORE (qdouble, fsrc, FR[ac]); + FPS = setfcc (FPS, fsrc.h, 0); + break; + + case 010: /* STf */ + F_LOAD (qdouble, FR[ac], fac); + WriteFP (&fac, GeteaFP (dstspec, lenf), dstspec, lenf); + break; + + case 017: /* LDCff' */ + ReadFP (&fsrc, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + if ((FPS & (FPS_D + FPS_T)) == 0) newV = roundfp11 (&fsrc); + else newV = 0; + F_STORE (qdouble, fsrc, FR[ac]); + FPS = setfcc (FPS, fsrc.h, newV); + break; + + case 014: /* STCff' */ + F_LOAD (qdouble, FR[ac], fac); + if (GET_EXP (fac.h) == 0) fac = zero_fac; + if ((FPS & (FPS_D + FPS_T)) == FPS_D) newV = roundfp11 (&fac); + else newV = 0; + WriteFP (&fac, GeteaFP (dstspec, 12 - lenf), dstspec, 12 - lenf); + FPS = setfcc (FPS, fac.h, newV); + break; + + case 007: /* CMPf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + if (GET_EXP (fsrc.h) == 0) fsrc = zero_fac; + if (GET_EXP (fac.h) == 0) fac = zero_fac; + if ((fsrc.h == fac.h) && (fsrc.l == fac.l)) { /* equal? */ + FPS = (FPS & ~FPS_CC) | FPS_Z; + if ((fsrc.h | fsrc.l) == 0) { /* zero? */ + F_STORE (qdouble, zero_fac, FR[ac]); + } + break; + } + FPS = (FPS & ~FPS_CC) | ((fsrc.h >> (FP_V_SIGN - PSW_V_N)) & FPS_N); + if ((GET_SIGN (fsrc.h ^ fac.h) == 0) && (fac.h != 0) && + F_LT (fsrc, fac)) FPS = FPS ^ FPS_N; + break; + + case 015: /* LDEXP */ + dst = (dstspec <= 07)? R[dstspec]: ReadW (GeteaW (dstspec)); + F_LOAD (qdouble, FR[ac], fac); + fac.h = (fac.h & ~FP_EXP) | (((dst + FP_BIAS) & FP_M_EXP) << FP_V_EXP); + newV = 0; + if ((dst > 0177) && (dst <= 0177600)) { + if (dst < 0100000) { + if (fpnotrap (FEC_OVFLO)) fac = zero_fac; + newV = FPS_V; + } + else { + if (fpnotrap (FEC_UNFLO)) fac = zero_fac; + } + } + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; + + case 012: /* STEXP */ + dst = (GET_EXP (FR[ac].h) - FP_BIAS) & 0177777; + N = GET_SIGN_W (dst); + Z = (dst == 0); + V = 0; + C = 0; + FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | (Z << PSW_V_Z); + if (dstspec <= 07) R[dstspec] = dst; + else WriteW (dst, GeteaW (dstspec)); + break; + + case 016: /* LDCif */ + leni = FPS & FPS_L? LONG: WORD; + if (dstspec <= 07) fac.l = R[dstspec] << 16; + else fac.l = ReadI (GeteaFP (dstspec, leni), dstspec, leni); + fac.h = 0; + if (fac.l) { + if (sign = GET_SIGN_L (fac.l)) fac.l = (fac.l ^ 0xFFFFFFFF) + 1; + for (i = 0; GET_SIGN_L (fac.l) == 0; i++) fac.l = fac.l << 1; + exp = ((FPS & FPS_L)? FP_BIAS + 32: FP_BIAS + 16) - i; + fac.h = (sign << FP_V_SIGN) | (exp << FP_V_EXP) | + ((fac.l >> (31 - FP_V_HB)) & FP_FRACH); + fac.l = (fac.l << (FP_V_HB + 1)) & FP_FRACL; + if ((FPS & (FPS_D + FPS_T)) == 0) roundfp11 (&fac); + } + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, 0); + break; + + case 013: /* STCfi */ + sign = GET_SIGN (FR[ac].h); /* get sign, */ + exp = GET_EXP (FR[ac].h); /* exponent, */ + F_LOAD_FRAC (qdouble, FR[ac], fac); /* fraction */ + if (FPS & FPS_L) { + leni = LONG; + i = FP_BIAS + 32; + } + else { + leni = WORD; + i = FP_BIAS + 16; + } + C = 0; + if (exp <= FP_BIAS) dst = 0; + else if (exp > i) { + dst = 0; + C = 1; + } + else { + F_RSH_V (fac, FP_V_HB + 1 + i - exp, fsrc); + if (leni == WORD) fsrc.l = fsrc.l & ~0177777; + if (fsrc.l >= i_limit[leni == LONG][sign]) { + dst = 0; + C = 1; + } + else { + dst = fsrc.l; + if (sign) dst = -dst; + } + } + N = GET_SIGN_L (dst); + Z = (dst == 0); + V = 0; + if (C) fpnotrap (FEC_ICVT); + FPS = (FPS & ~FPS_CC) | (N << PSW_V_N) | + (Z << PSW_V_Z) | (C << PSW_V_C); + if (dstspec <= 07) R[dstspec] = (dst >> 16) & 0177777; + else WriteI (dst, GeteaFP (dstspec, leni), dstspec, leni); + break; + + case 002: /* MULf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + newV = mulfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; + + case 003: /* MODf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + newV = modfp11 (&fac, &fsrc, &modfrac); + F_STORE (qdouble, fac, FR[ac | 1]); + F_STORE (qdouble, modfrac, FR[ac]); + FPS = setfcc (FPS, modfrac.h, newV); + break; + + case 004: /* ADDf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + newV = addfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; + + case 006: /* SUBf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + if (GET_EXP (fsrc.h) != 0) fsrc.h = fsrc.h ^ FP_SIGN; + newV = addfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; + + case 011: /* DIVf */ + ReadFP (&fsrc, GeteaFP (dstspec, lenf), dstspec, lenf); + F_LOAD (qdouble, FR[ac], fac); + if (GET_EXP (fsrc.h) == 0) { /* divide by zero? */ + fpnotrap (FEC_DZRO); + ABORT (TRAP_INT); + } + newV = divfp11 (&fac, &fsrc); + F_STORE (qdouble, fac, FR[ac]); + FPS = setfcc (FPS, fac.h, newV); + break; + } /* end switch fop */ + +return; +} + +/* Effective address calculation for fp operands + + Inputs: + spec = specifier + len = length + Outputs: + VA = virtual address + + Warnings: + - Do not call this routine for integer mode 0 operands + - Do not call this routine more than once per instruction +*/ + +int32 GeteaFP (int32 spec, int32 len) +{ +int32 adr, reg, ds; + +reg = spec & 07; /* reg number */ +ds = (reg == 7)? isenable: dsenable; /* dspace if not PC */ +switch (spec >> 3) { /* case on spec */ + + case 0: /* floating AC */ + if (reg >= 06) { + fpnotrap (FEC_OP); + ABORT (TRAP_INT); + } + return 0; + + case 1: /* (R) */ + return (R[reg] | ds); + + case 2: /* (R)+ */ + if (reg == 7) len = 2; + R[reg] = ((adr = R[reg]) + len) & 0177777; + if (update_MM) MMR1 = (len << 3) | reg; + return (adr | ds); + + case 3: /* @(R)+ */ + R[reg] = ((adr = R[reg]) + 2) & 0177777; + if (update_MM) MMR1 = 020 | reg; + adr = ReadW (adr | ds); + return (adr | dsenable); + + case 4: /* -(R) */ + adr = R[reg] = (R[reg] - len) & 0177777; + if (update_MM) MMR1 = (((-len) & 037) << 3) | reg; + if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) + set_stack_trap (adr); + return (adr | ds); + + case 5: /* @-(R) */ + adr = R[reg] = (R[reg] - 2) & 0177777; + if (update_MM) MMR1 = 0360 | reg; + if ((reg == 6) && (cm == MD_KER) && (adr < (STKLIM + STKL_Y))) + set_stack_trap (adr); + adr = ReadW (adr | ds); + return (adr | dsenable); + + case 6: /* d(r) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + return (((R[reg] + adr) & 0177777) | dsenable); + + case 7: /* @d(R) */ + adr = ReadW (PC | isenable); + PC = (PC + 2) & 0177777; + adr = ReadW (((R[reg] + adr) & 0177777) | dsenable); + return (adr | dsenable); + } /* end switch */ + +return 0; +} + +/* Read integer operand + + Inputs: + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length (2/4 bytes) + Outputs: + data = data read from memory or I/O space +*/ + +uint32 ReadI (int32 VA, int32 spec, int32 len) +{ +if ((len == WORD) || (spec == 027)) return (ReadW (VA) << 16); +return ((ReadW (VA) << 16) | + ReadW ((VA & ~0177777) | ((VA + 2) & 0177777))); +} + +/* Read floating operand + + Inputs: + fptr = pointer to output + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length (4/8 bytes) +*/ + +void ReadFP (fpac_t *fptr, int32 VA, int32 spec, int32 len) +{ +int32 exta; + +if (spec <= 07) { + F_LOAD_P (len == QUAD, FR[spec], fptr); + return; + } +if (spec == 027) { + fptr->h = (ReadW (VA) << FP_V_F0); + fptr->l = 0; + } +else { + exta = VA & ~0177777; + fptr->h = (ReadW (VA) << FP_V_F0) | + (ReadW (exta | ((VA + 2) & 0177777)) << FP_V_F1); + if (len == QUAD) fptr->l = + (ReadW (exta | ((VA + 4) & 0177777)) << FP_V_F2) | + (ReadW (exta | ((VA + 6) & 0177777)) << FP_V_F3); + else fptr->l = 0; + } +if ((GET_SIGN (fptr->h) != 0) && (GET_EXP (fptr->h) == 0) && + (fpnotrap (FEC_UNDFV) == 0)) ABORT (TRAP_INT); +return; +} + +/* Write integer result + + Inputs: + data = data to be written + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length + Outputs: none +*/ + +void WriteI (int32 data, int32 VA, int32 spec, int32 len) +{ +WriteW ((data >> 16) & 0177777, VA); +if ((len == WORD) || (spec == 027)) return; +WriteW (data & 0177777, (VA & ~0177777) | ((VA + 2) & 0177777)); +return; +} + +/* Write floating result + + Inputs: + fptr = pointer to data to be written + VA = virtual address, VA<18:16> = mode, I/D space + spec = specifier + len = length + Outputs: none +*/ + +void WriteFP (fpac_t *fptr, int32 VA, int32 spec, int32 len) +{ +int32 exta; + +if (spec <= 07) { + F_STORE_P (len == QUAD, fptr, FR[spec]); + return; + } +WriteW ((fptr->h >> FP_V_F0) & 0177777, VA); +if (spec == 027) return; +exta = VA & ~0177777; +WriteW ((fptr->h >> FP_V_F1) & 0177777, exta | ((VA + 2) & 0177777)); +if (len == LONG) return; +WriteW ((fptr->l >> FP_V_F2) & 0177777, exta | ((VA + 4) & 0177777)); +WriteW ((fptr->l >> FP_V_F3) & 0177777, exta | ((VA + 6) & 0177777)); +return; +} + +/* FIS instructions */ + +t_stat fis11 (int32 IR) +{ +int32 reg, exta; +fpac_t fac, fsrc; + +reg = IR & 07; /* isolate reg */ +if (reg == 7) exta = isenable; /* choose I,D */ +else exta = dsenable; +if (IR & 000740) { /* defined? */ + if (CPUT (CPUT_03)) ReadW (exta | R[reg]); /* 11/03 reads word */ + ABORT (TRAP_ILL); + } +FEC = 0; /* no errors */ +FPS = FPS_IU|FPS_IV; /* trap ovf,unf */ + +fsrc.h = (ReadW (exta | R[reg]) << FP_V_F0) | + (ReadW (exta | ((R[reg] + 2) & 0177777)) << FP_V_F1); +fsrc.l = 0; +fac.h = (ReadW (exta | ((R[reg] + 4) & 0177777)) << FP_V_F0) | + (ReadW (exta | ((R[reg] + 6) & 0177777)) << FP_V_F1); +fac.l = 0; +if (GET_SIGN (fsrc.h) && (GET_EXP (fsrc.h) == 0)) /* clean 0's */ + fsrc.h = fsrc.l = 0; +if (GET_SIGN (fac.h) && (GET_EXP (fac.l) == 0)) + fac.h = fac.l = 0; + +N = Z = V = C = 0; /* clear cc's */ +switch ((IR >> 3) & 3) { /* case IR<5:3> */ + + case 0: /* FAD */ + addfp11 (&fac, &fsrc); + break; + + case 1: /* FSUB */ + if (fsrc.h != 0) fsrc.h = fsrc.h ^ FP_SIGN; /* invert sign */ + addfp11 (&fac, &fsrc); + break; + + case 2: /* FMUL */ + mulfp11 (&fac, &fsrc); + break; + + case 3: /* FDIV */ + if (fsrc.h == 0) { /* div by 0? */ + V = N = C = 1; /* set cc's */ + setTRAP (TRAP_FPE); /* set trap */ + return SCPE_OK; + } + else divfp11 (&fac, &fsrc); + break; + } + +if (FEC == 0) { /* no err? */ + WriteW ((fac.h >> FP_V_F0) & 0177777, exta | ((R[reg] + 4) & 0177777)); + WriteW ((fac.h >> FP_V_F1) & 0177777, exta | ((R[reg] + 6) & 0177777)); + R[reg] = (R[reg] + 4) & 0177777; /* pop stack */ + N = (GET_SIGN (fac.h) != 0); /* set N,Z */ + Z = (fac.h == 0); + } +else if (FEC == FEC_OVFLO) V = 1; /* ovf? trap set */ +else if (FEC == FEC_UNFLO) V = N = 1; /* unf? trap set */ +else return SCPE_IERR; /* what??? */ +return SCPE_OK; +} + +/* Floating point add + + Inputs: + facp = pointer to src1 (output) + fsrcp = pointer to src2 + Outputs: + ovflo = overflow variable +*/ + +int32 addfp11 (fpac_t *facp, fpac_t *fsrcp) +{ +int32 facexp, fsrcexp, ediff; +fpac_t facfrac, fsrcfrac; + +if (F_LT_AP (facp, fsrcp)) { /* if !fac! < !fsrc! */ + facfrac = *facp; + *facp = *fsrcp; /* swap operands */ + *fsrcp = facfrac; + } +facexp = GET_EXP (facp->h); /* get exponents */ +fsrcexp = GET_EXP (fsrcp->h); +if (facexp == 0) { /* fac = 0? */ + *facp = fsrcexp? *fsrcp: zero_fac; /* result fsrc or 0 */ + return 0; + } +if (fsrcexp == 0) return 0; /* fsrc = 0? no op */ +ediff = facexp - fsrcexp; /* exponent diff */ +if (ediff >= 60) return 0; /* too big? no op */ +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +F_LSH_GUARD (facfrac); /* guard fractions */ +F_LSH_GUARD (fsrcfrac); +if (GET_SIGN (facp->h) != GET_SIGN (fsrcp->h)) { /* signs different? */ + if (ediff) { F_RSH_V (fsrcfrac, ediff, fsrcfrac); } /* sub, shf fsrc */ + F_SUB (fsrcfrac, facfrac, facfrac); /* sub fsrc from fac */ + if ((facfrac.h | facfrac.l) == 0) { /* result zero? */ + *facp = zero_fac; /* no overflow */ + return 0; + } + if (ediff <= 1) { /* big normalize? */ + if ((facfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) { + F_LSH_K (facfrac, 24, facfrac); + facexp = facexp - 24; + } + if ((facfrac.h & (0x00FFF000 << FP_GUARD)) == 0) { + F_LSH_K (facfrac, 12, facfrac); + facexp = facexp - 12; + } + if ((facfrac.h & (0x00FC0000 << FP_GUARD)) == 0) { + F_LSH_K (facfrac, 6, facfrac); + facexp = facexp - 6; + } + } + while (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (facfrac); + facexp = facexp - 1; + } + } +else { + if (ediff) { + F_RSH_V (fsrcfrac, ediff, fsrcfrac); /* add, shf fsrc */ + } + F_ADD (fsrcfrac, facfrac, facfrac); /* add fsrc to fac */ + if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD + 1)) { + F_RSH_1 (facfrac); /* carry out, shift */ + facexp = facexp + 1; + } + } +return round_and_pack (facp, facexp, &facfrac, 1); +} + +/* Floating point multiply + + Inputs: + facp = pointer to src1 (output) + fsrcp = pointer to src2 + Outputs: + ovflo = overflow indicator +*/ + +int32 mulfp11 (fpac_t *facp, fpac_t *fsrcp) +{ +int32 facexp, fsrcexp; +fpac_t facfrac, fsrcfrac; + +facexp = GET_EXP (facp->h); /* get exponents */ +fsrcexp = GET_EXP (fsrcp->h); +if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */ + *facp = zero_fac; + return 0; + } +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */ +facp->h = facp->h ^ fsrcp->h; /* calculate sign */ +frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */ + +/* Multiplying two numbers in the range [.5,1) produces a result in the + range [.25,1). Therefore, at most one bit of normalization is required + to bring the result back to the range [.5,1). +*/ + +if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (facfrac); + facexp = facexp - 1; + } +return round_and_pack (facp, facexp, &facfrac, 1); +} + +/* Floating point mod + + Inputs: + facp = pointer to src1 (integer result) + fsrcp = pointer to src2 + fracp = pointer to fractional result + Outputs: + ovflo = overflow indicator + + See notes on multiply for initial operation +*/ + +int32 modfp11 (fpac_t *facp, fpac_t *fsrcp, fpac_t *fracp) +{ +int32 facexp, fsrcexp; +fpac_t facfrac, fsrcfrac, fmask; + +facexp = GET_EXP (facp->h); /* get exponents */ +fsrcexp = GET_EXP (fsrcp->h); +if ((facexp == 0) || (fsrcexp == 0)) { /* test for zero */ + *fracp = zero_fac; + *facp = zero_fac; + return 0; + } +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +facexp = facexp + fsrcexp - FP_BIAS; /* calculate exp */ +fracp->h = facp->h = facp->h ^ fsrcp->h; /* calculate sign */ +frac_mulfp11 (&facfrac, &fsrcfrac); /* multiply fracs */ + +/* Multiplying two numbers in the range [.5,1) produces a result in the + range [.25,1). Therefore, at most one bit of normalization is required + to bring the result back to the range [.5,1). +*/ + +if (GET_BIT (facfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (facfrac); + facexp = facexp - 1; + } + +/* There are three major cases of MODf: + + 1. Exp <= FP_BIAS (all fraction). Return 0 as integer, product as + fraction. Underflow can occur. + 2. Exp > FP_BIAS + #fraction bits (all integer). Return product as + integer, 0 as fraction. Overflow can occur. + 3. FP_BIAS < exp <= FP_BIAS + #fraction bits. Separate integer and + fraction and return both. Neither overflow nor underflow can occur. +*/ + +if (facexp <= FP_BIAS) { /* case 1 */ + *facp = zero_fac; + return round_and_pack (fracp, facexp, &facfrac, 1); + } +if (facexp > ((FPS & FPS_D)? FP_BIAS + 56: FP_BIAS + 24)) { + *fracp = zero_fac; /* case 2 */ + return round_and_pack (facp, facexp, &facfrac, 0); + } +F_RSH_V (fmask_fac, facexp - FP_BIAS, fmask); /* shift mask */ +fsrcfrac.l = facfrac.l & fmask.l; /* extract fraction */ +fsrcfrac.h = facfrac.h & fmask.h; +if ((fsrcfrac.h | fsrcfrac.l) == 0) *fracp = zero_fac; +else { + F_LSH_V (fsrcfrac, facexp - FP_BIAS, fsrcfrac); + fsrcexp = FP_BIAS; + if ((fsrcfrac.h & (0x00FFFFFF << FP_GUARD)) == 0) { + F_LSH_K (fsrcfrac, 24, fsrcfrac); + fsrcexp = fsrcexp - 24; + } + if ((fsrcfrac.h & (0x00FFF000 << FP_GUARD)) == 0) { + F_LSH_K (fsrcfrac, 12, fsrcfrac); + fsrcexp = fsrcexp - 12; + } + if ((fsrcfrac.h & (0x00FC0000 << FP_GUARD)) == 0) { + F_LSH_K (fsrcfrac, 6, fsrcfrac); + fsrcexp = fsrcexp - 6; + } + while (GET_BIT (fsrcfrac.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (fsrcfrac); + fsrcexp = fsrcexp - 1; + } + round_and_pack (fracp, fsrcexp, &fsrcfrac, 1); + } +facfrac.l = facfrac.l & ~fmask.l; +facfrac.h = facfrac.h & ~fmask.h; +return round_and_pack (facp, facexp, &facfrac, 0); +} + +/* Fraction multiply + + Inputs: + f1p = pointer to multiplier (output) + f2p = pointer to multiplicand fraction + + Note: the inputs are unguarded; the output is guarded. + + This routine performs a classic shift-and-add multiply. The low + order bit of the multiplier is tested; if 1, the multiplicand is + added into the high part of the double precision result. The + result and the multiplier are both shifted right 1. + + For the 24b x 24b case, this routine develops 48b of result. + For the 56b x 56b case, this routine only develops the top 64b + of the the result. Because the inputs are normalized fractions, + the interesting part of the result is the high 56+guard bits. + Everything shifted off to the right, beyond 64b, plays no part + in rounding or the result. + + There are many possible optimizations in this routine: scanning + for groups of zeroes, particularly in the 56b x 56b case; using + "extended multiply" capability if available in the hardware. +*/ + +void frac_mulfp11 (fpac_t *f1p, fpac_t *f2p) +{ +fpac_t result, mpy, mpc; +int32 i; + +result = zero_fac; /* clear result */ +mpy = *f1p; /* get operands */ +mpc = *f2p; +F_LSH_GUARD (mpc); /* guard multipicand */ +if ((mpy.l | mpc.l) == 0) { /* 24b x 24b? */ + for (i = 0; i < 24; i++) { + if (mpy.h & 1) result.h = result.h + mpc.h; + F_RSH_1 (result); + mpy.h = mpy.h >> 1; + } + } +else { + if (mpy.l != 0) { /* 24b x 56b? */ + for (i = 0; i < 32; i++) { + if (mpy.l & 1) { + F_ADD (mpc, result, result); + } + F_RSH_1 (result); + mpy.l = mpy.l >> 1; + } + } + for (i = 0; i < 24; i++) { + if (mpy.h & 1) { + F_ADD (mpc, result, result); + } + F_RSH_1 (result); + mpy.h = mpy.h >> 1; + } + } +*f1p = result; +return; +} + +/* Floating point divide + + Inputs: + facp = pointer to dividend (output) + fsrcp = pointer to divisor + Outputs: + ovflo = overflow indicator + + Source operand must be checked for zero by caller! +*/ + +int32 divfp11 (fpac_t *facp, fpac_t *fsrcp) +{ +int32 facexp, fsrcexp, i, count, qd; +fpac_t facfrac, fsrcfrac, quo; + +fsrcexp = GET_EXP (fsrcp->h); /* get divisor exp */ +facexp = GET_EXP (facp->h); /* get dividend exp */ +if (facexp == 0) { /* test for zero */ + *facp = zero_fac; /* result zero */ + return 0; + } +F_GET_FRAC_P (facp, facfrac); /* get fractions */ +F_GET_FRAC_P (fsrcp, fsrcfrac); +F_LSH_GUARD (facfrac); /* guard fractions */ +F_LSH_GUARD (fsrcfrac); +facexp = facexp - fsrcexp + FP_BIAS + 1; /* calculate exp */ +facp->h = facp->h ^ fsrcp->h; /* calculate sign */ +qd = FPS & FPS_D; +count = FP_V_HB + FP_GUARD + (qd? 33: 1); /* count = 56b/24b */ + +quo = zero_fac; +for (i = count; (i > 0) && ((facfrac.h | facfrac.l) != 0); i--) { + F_LSH_1 (quo); /* shift quotient */ + if (!F_LT (facfrac, fsrcfrac)) { /* divd >= divr? */ + F_SUB (fsrcfrac, facfrac, facfrac); /* divd - divr */ + if (qd) quo.l = quo.l | 1; /* double or single? */ + else quo.h = quo.h | 1; + } + F_LSH_1 (facfrac); /* shift divd */ + } +if (i > 0) { /* early exit? */ + F_LSH_V (quo, i, quo); + } + +/* Dividing two numbers in the range [.5,1) produces a result in the + range [.5,2). Therefore, at most one bit of normalization is required + to bring the result back to the range [.5,1). The choice of counts + and quotient bit positions makes this work correctly. +*/ + +if (GET_BIT (quo.h, FP_V_HB + FP_GUARD) == 0) { + F_LSH_1 (quo); + facexp = facexp - 1; + } +return round_and_pack (facp, facexp, &quo, 1); +} + +/* Update floating condition codes + Note that FC is only set by STCfi via the integer condition codes + + Inputs: + oldst = current status + result = high result + newV = new V + Outputs: + newst = new status +*/ + +int32 setfcc (int32 oldst, int32 result, int32 newV) +{ +oldst = (oldst & ~FPS_CC) | newV; +if (GET_SIGN (result)) oldst = oldst | FPS_N; +if (GET_EXP (result) == 0) oldst = oldst | FPS_Z; +return oldst; +} + +/* Round (in place) floating point number to f_floating + + Inputs: + fptr = pointer to floating number + Outputs: + ovflow = overflow +*/ + +int32 roundfp11 (fpac_t *fptr) +{ +fpac_t outf; + +outf = *fptr; /* get argument */ +F_ADD (fround_fac, outf, outf); /* round */ +if (GET_SIGN (outf.h ^ fptr->h)) { /* flipped sign? */ + outf.h = (outf.h ^ FP_SIGN) & 0xFFFFFFFF; /* restore sign */ + if (fpnotrap (FEC_OVFLO)) *fptr = zero_fac; /* if no int, clear */ + else *fptr = outf; /* return rounded */ + return FPS_V; /* overflow */ + } +*fptr = outf; /* round was ok */ +return 0; /* no overflow */ +} + +/* Round result of calculation, test overflow, pack + + Input: + facp = pointer to result, sign in place + exp = result exponent, right justified + fracp = pointer to result fraction, right justified with + guard bits + r = round (1) or truncate (0) + Outputs: + ovflo = overflow indicator +*/ + +int32 round_and_pack (fpac_t *facp, int32 exp, fpac_t *fracp, int r) +{ +fpac_t frac; + +frac = *fracp; /* get fraction */ +if (r && ((FPS & FPS_T) == 0)) { + if (FPS & FPS_D) { + F_ADD (dround_guard_fac, frac, frac); + } + else { + F_ADD (fround_guard_fac, frac, frac); + } + if (GET_BIT (frac.h, FP_V_HB + FP_GUARD + 1)) { + F_RSH_1 (frac); + exp = exp + 1; + } + } +F_RSH_GUARD (frac); +facp->l = frac.l & FP_FRACL; +facp->h = (facp->h & FP_SIGN) | ((exp & FP_M_EXP) << FP_V_EXP) | + (frac.h & FP_FRACH); +if (exp > 0377) { + if (fpnotrap (FEC_OVFLO)) *facp = zero_fac; + return FPS_V; + } +if ((exp <= 0) && (fpnotrap (FEC_UNFLO))) *facp = zero_fac; +return 0; +} + +/* Process floating point exception + + Inputs: + code = exception code + Outputs: + int = FALSE if interrupt enabled, TRUE if disabled +*/ + +int32 fpnotrap (int32 code) +{ +static const int32 test_code[] = { 0, 0, 0, FPS_IC, FPS_IV, FPS_IU, FPS_IUV }; + +if ((code >= FEC_ICVT) && (code <= FEC_UNDFV) && + ((FPS & test_code[code >> 1]) == 0)) return TRUE; +FPS = FPS | FPS_ER; +FEC = code; +FEA = (backup_PC - 2) & 0177777; +if ((FPS & FPS_ID) == 0) setTRAP (TRAP_FPE); +return FALSE; +} diff --git a/PDP11/pdp11_hk.c b/PDP11/pdp11_hk.c new file mode 100644 index 0000000..e0fdc32 --- /dev/null +++ b/PDP11/pdp11_hk.c @@ -0,0 +1,1295 @@ +/* pdp11_hk.c - RK611/RK06/RK07 disk controller + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PUHKOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + hk RK611/RK06/RK07 disk + + 29-Apr-07 RMS NOP and DCLR (at least) do not check drive type + MR2 and MR3 only updated on NOP + 17-Nov-05 RMS Removed unused variable + 13-Nov-05 RMS Fixed overlapped seek interaction with NOP, DCLR, PACK + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 18-Mar-05 RMS Added attached test to detach routine + 03-Oct-04 RMS Revised Unibus interface + RMS Fixed state of output ready for M+ + 26-Mar-04 RMS Fixed warnings with -std=c99 + 25-Jan-04 RMS Revised for device debug support + 04-Jan-04 RMS Changed sim_fsize calling sequence + 29-Dec-03 RMS Added 18b Qbus support + 25-Apr-03 RMS Revised for extended file support + + This is a somewhat abstracted implementation of the RK611, more closely + modelled on third party clones than DEC's own implementation. In particular, + the drive-to-controller serial communications system is simulated only at + a level equal to the Emulex SC21. + + The RK611 functions only in 18b Unibus systems with I/O maps. The Emulex + SC02/C was a Qbus work-alike with a unique extension to 22b addressing. It + was only supported in Ultrix-11 and other third party software. + + This module includes ideas from a previous implementation by Fred Van Kempen. +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "RK611 is not supported on the PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +extern int32 cpu_opt; +#endif + +extern uint16 *M; + +#define HK_NUMDR 8 /* #drives */ +#define HK_NUMCY6 411 /* cyl/drive */ +#define HK_NUMCY7 815 /* cyl/drive */ +#define HK_NUMSF 3 /* tracks/cyl */ +#define HK_NUMSC 22 /* sectors/track */ +#define HK_NUMWD 256 /* words/sector */ +#define RK06_SIZE (HK_NUMCY6*HK_NUMSF*HK_NUMSC*HK_NUMWD) +#define RK07_SIZE (HK_NUMCY7*HK_NUMSF*HK_NUMSC*HK_NUMWD) +#define HK_SIZE(x) (((x)->flags & UNIT_DTYPE)? RK07_SIZE: RK06_SIZE) +#define HK_CYL(x) (((x)->flags & UNIT_DTYPE)? HK_NUMCY7: HK_NUMCY6) +#define HK_MAXFR (1 << 16) + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ +#define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (1 << UNIT_V_DTYPE) +#define UNIT_RK06 (0 << UNIT_V_DTYPE) +#define UNIT_RK07 (1 << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_DUMMY (1 << UNIT_V_DUMMY) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FNC u4 /* function */ + +/* HKCS1 - 177440 - control/status 1 */ + +#define CS1_GO CSR_GO /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 017 /* function mask */ +#define CS1_FNC (CS1_M_FNC << CS1_V_FNC) +#define FNC_NOP 000 /* no operation */ +#define FNC_PACK 001 /* pack acknowledge */ +#define FNC_DCLR 002 /* drive clear */ +#define FNC_UNLOAD 003 /* unload */ +#define FNC_START 004 /* start */ +#define FNC_RECAL 005 /* recalibrate */ +#define FNC_OFFSET 006 /* offset */ +#define FNC_SEEK 007 /* seek */ +#define FNC_XFER 010 +#define FNC_READ 010 /* read */ +#define FNC_WRITE 011 /* write */ +#define FNC_WRITEH 013 /* write w/ headers */ +#define FNC_READH 012 /* read w/ headers */ +#define FNC_WCHK 014 /* write check */ +#define FNC_2ND 020 /* 2nd state flag */ +#define CS1_SPA 0000040 /* spare */ +#define CS1_IE CSR_IE /* int enable */ +#define CS1_DONE CSR_DONE /* ready */ +#define CS1_V_UAE 8 /* Unibus addr ext */ +#define CS1_M_UAE 03 +#define CS1_UAE (CS1_M_UAE << CS1_V_UAE) +#define CS1_DT 0002000 /* drive type */ +#define CS1_CTO 0004000 /* ctrl timeout NI */ +#define CS1_FMT 0010000 /* 16b/18b NI */ +#define CS1_PAR 0020000 /* par err NI */ +#define CS1_DI 0040000 /* drive intr */ +#define CS1_ERR 0100000 /* error */ +#define CS1_CCLR 0100000 /* ctrl clear */ +#define CS1_RW (CS1_DT|CS1_UAE|CS1_IE|CS1_SPA|CS1_FNC) +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) +#define GET_UAE(x) (((x) >> CS1_V_UAE) & CS1_M_UAE) +#define PUT_UAE(x,n) (((x) & ~ CS1_UAE) | (((n) << CS1_V_UAE) & CS1_UAE)) + +/* HKWC - 177442 - word count */ + +/* HKBA - 177444 - base address */ + +#define BA_MBZ 0000001 /* must be zero */ + +/* HKDA - 177446 - sector/track */ + +#define DA_V_SC 0 /* sector pos */ +#define DA_M_SC 037 /* sector mask */ +#define DA_V_SF 8 /* track pos */ +#define DA_M_SF 007 /* track mask */ +#define DA_MBZ 0174340 +#define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) +#define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) + +/* HKCS2 - 177450 - control/status 2 */ + +#define CS2_V_UNIT 0 /* unit pos */ +#define CS2_M_UNIT 07 /* unit mask */ +#define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) +#define CS2_RLS 0000010 /* release NI */ +#define CS2_UAI 0000020 /* addr inhibit */ +#define CS2_CLR 0000040 /* controller clear */ +#define CS2_IR 0000100 /* input ready */ +#define CS2_OR 0000200 /* output ready */ +#define CS2_UFE 0000400 /* unit field err NI */ +#define CS2_MDS 0001000 /* multidrive sel NI */ +#define CS2_PGE 0002000 /* program err */ +#define CS2_NEM 0004000 /* nx mem err */ +#define CS2_NED 0010000 /* nx drive err */ +#define CS2_PE 0020000 /* parity err NI */ +#define CS2_WCE 0040000 /* write check err */ +#define CS2_DLT 0100000 /* data late NI */ +#define CS2_MBZ (CS2_CLR) +#define CS2_RW 0000037 +#define CS2_ERR (CS2_UFE | CS2_MDS | CS2_PGE | CS2_NEM | \ + CS2_NED | CS2_PE | CS2_WCE | CS2_DLT ) +#define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT) + +/* HKDS - 177452 - drive status ^ = calculated dynamically */ + +#define DS_DRA 0000001 /* ^drive avail */ +#define DS_OF 0000004 /* ^offset mode */ +#define DS_ACLO 0000010 /* ^AC LO NI */ +#define DS_SPLS 0000020 /* ^speed loss NI */ +#define DS_DOT 0000040 /* ^off track NI */ +#define DS_VV 0000100 /* volume valid */ +#define DS_RDY 0000200 /* ^drive ready */ +#define DS_DT 0000400 /* ^drive type */ +#define DS_WRL 0004000 /* ^write locked */ +#define DS_PIP 0020000 /* pos in progress */ +#define DS_ATA 0040000 /* attention active */ +#define DS_VLD 0100000 /* ^status valid */ +#define DS_MBZ 0013002 + +/* HKER - 177454 - error status */ + +#define ER_ILF 0000001 /* illegal func */ +#define ER_SKI 0000002 /* seek incomp */ +#define ER_NXF 0000004 /* non-exec func */ +#define ER_PAR 0000010 /* parity err */ +#define ER_FER 0000020 /* format err NI */ +#define ER_DTY 0000040 /* drive type err */ +#define ER_ECH 0000100 /* ECC hard err NI */ +#define ER_BSE 0000200 /* bad sector err NI */ +#define ER_HCR 0000400 /* hdr CRC err NI */ +#define ER_AOE 0001000 /* addr ovflo err */ +#define ER_IAE 0002000 /* invalid addr err */ +#define ER_WLE 0004000 /* write lock err */ +#define ER_DTE 0010000 /* drive time err NI */ +#define ER_OPI 0020000 /* op incomplete */ +#define ER_UNS 0040000 /* drive unsafe */ +#define ER_DCK 0100000 /* data check NI */ + +/* HKAS - 177456 - attention summary/offset */ + +#define AS_U0 0000400 /* unit 0 flag */ +#define AS_OF 0000277 /* offset mask */ + +/* HKDC - 177460 - desired cylinder */ + +#define DC_V_CY 0 /* cylinder pos */ +#define DC_M_CY 0001777 /* cylinder mask */ +#define DC_MBZ 0176000 +#define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) +#define GET_DA(c,fs) ((((GET_CY (c) * HK_NUMSF) + \ + GET_SF (fs)) * HK_NUMSC) + GET_SC (fs)) + +/* Spare - 177462 - read/write */ + +#define XM_KMASK 0177700 /* Qbus XM key mask */ +#define XM_KEY 0022000 /* Qbus XM "key" */ +#define XM_MMASK 0000077 /* Qbus XM mask */ +#define SC02C (!UNIBUS && ((hkspr & XM_KMASK) == XM_KEY)) + +/* HKDB - 177464 - read/write */ + +/* HKMR - 177466 - maintenance register 1 */ + +#define MR_V_MS 0 /* message select */ +#define MR_M_MS 03 +#define MR_MS (MR_M_MS << MR_V_MS) +#define GET_MS(x) (((x) >> MR_V_MS) & MR_M_MS) +#define MR_PAR 0000020 /* force even parity */ +#define MR_DMD 0000040 /* diagnostic mode */ +#define MR_RW 0001777 + +/* HKEC1 - 177470 - ECC status 1 - always reads as 0 */ +/* HKEC2 - 177472 - ECC status 2 - always reads as 0 */ + +/* HKMR2 - 177474 - maintenance register 2 */ + +#define AX_V_UNIT 0 /* unit #, all msgs */ +#define AX_PAR 0100000 /* parity, all msgs */ + +#define A0_DRA 0000040 /* drive avail */ +#define A0_VV 0000100 /* vol valid */ +#define A0_RDY 0000200 /* drive ready */ +#define A0_DT 0000400 /* drive type */ +#define A0_FMT 0001000 /* format NI */ +#define A0_OF 0002000 /* offset mode */ +#define A0_WRL 0004000 /* write lock */ +#define A0_SPO 0010000 /* spindle on */ +#define A0_PIP 0020000 /* pos in prog */ +#define A0_ATA 0040000 /* attention */ + +#define A1_SRV 0000020 /* servo */ +#define A1_HHM 0000040 /* heads home */ +#define A1_BHM 0000100 /* brushes home */ +#define A1_DOR 0000200 /* door latched */ +#define A1_CAR 0000400 /* cartridge present */ +#define A1_SPD 0001000 /* speed ok */ +#define A1_FWD 0002000 /* seek fwd */ +#define A1_REV 0004000 /* seek rev */ +#define A1_LDH 0010000 /* loading heads NI */ +#define A1_RTZ 0020000 /* return to zero */ +#define A1_UNL 0040000 /* unloading heads */ + +#define A2_V_DIF 4 /* cyl diff */ +#define A2_M_DIF 0777 + +#define A3_V_SNO 3 /* serial # */ + +/* HKMR3 - 177476 - maintenance register 3 */ + +#define B0_IAE 0000040 /* invalid addr */ +#define B0_ACLO 0000100 /* AC LO NI */ +#define B0_FLT 0000200 /* fault */ +#define B0_NXF 0000400 /* non exec fnc */ +#define B0_CDP 0001000 /* msg parity err */ +#define B0_SKI 0002000 /* seek incomp */ +#define B0_WLE 0004000 /* write lock err */ +#define B0_SLO 0010000 /* speed low NI */ +#define B0_OFT 0020000 /* off track NI */ +#define B0_UNS 0040000 /* rw unsafe NI */ + +#define B1_SCE 0000020 /* sector err NI */ +#define B1_NWC 0000040 /* no write curr NI */ +#define B1_NWT 0000100 /* no write trans NI */ +#define B1_HFL 0000200 /* head fault NI */ +#define B1_MHS 0000400 /* multiselect NI */ +#define B1_IDX 0001000 /* index err NI */ +#define B1_TRI 0002000 /* tribit err NI */ +#define B1_SVE 0004000 /* servo err NI */ +#define B1_SKI 0010000 /* seek no mot */ +#define B1_LIM 0020000 /* seek limit NI */ +#define B1_SVU 0040000 /* servo unsafe NI */ + +#define B2_V_CYL 4 /* cylinder */ + +#define B3_V_SEC 4 /* sector */ +#define B3_V_DHA 9 /* decoded head */ + +/* Read header */ + +#define RDH1_V_CYL 0 /* cylinder */ +#define RDH2_V_SEC 0 /* sector */ +#define RDH2_V_DHA 5 /* decoded head */ +#define RDH2_GOOD 0140000 /* good sector flags */ + +/* Debug detail levels */ + +#define HKDEB_OPS 001 /* transactions */ +#define HKDEB_RRD 002 /* reg reads */ +#define HKDEB_RWR 004 /* reg writes */ + +extern int32 int_req[IPL_HLVL]; +extern FILE *sim_deb; + +uint16 *hkxb = NULL; /* xfer buffer */ +int32 hkcs1 = 0; /* control/status 1 */ +int32 hkwc = 0; /* word count */ +int32 hkba = 0; /* bus address */ +int32 hkda = 0; /* track/sector */ +int32 hkcs2 = 0; /* control/status 2 */ +int32 hkds[HK_NUMDR] = { 0 }; /* drive status */ +int32 hker[HK_NUMDR] = { 0 }; /* error status */ +int32 hkof = 0; /* offset */ +int32 hkmr = 0; /* maint registers */ +int32 hkmr2 = 0; +int32 hkmr3 = 0; +int32 hkdc = 0; /* cylinder */ +int32 hkspr = 0; /* spare */ +int32 hk_cwait = 5; /* command time */ +int32 hk_swait = 10; /* seek time */ +int32 hk_rwait = 10; /* rotate time */ +int32 hk_min2wait = 300; /* min time to 2nd int */ +int16 hkdb[3] = { 0 }; /* data buffer silo */ +int16 hk_off[HK_NUMDR] = { 0 }; /* saved offset */ +int16 hk_dif[HK_NUMDR] = { 0 }; /* cylinder diff */ +static uint8 reg_in_drive[16] = { + 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +DEVICE hk_dev; +t_stat hk_rd (int32 *data, int32 PA, int32 access); +t_stat hk_wr (int32 data, int32 PA, int32 access); +t_stat hk_svc (UNIT *uptr); +t_stat hk_reset (DEVICE *dptr); +t_stat hk_boot (int32 unitno, DEVICE *dptr); +t_stat hk_attach (UNIT *uptr, char *cptr); +t_stat hk_detach (UNIT *uptr); +int32 hk_rdmr2 (int32 msg); +int32 hk_rdmr3 (int32 msg); +void update_hkcs (int32 flags, int32 drv); +void update_hkds (int32 drv); +void hk_cmderr (int32 err, int32 drv); +void hk_go (int32 drv); +t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); + +extern t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); + +/* HK data structures + + hk_dev HK device descriptor + hk_unit HK unit list + hk_reg HK register list + hk_mod HK modifier list +*/ + +DIB hk_dib = { + IOBA_HK, IOLN_HK, &hk_rd, &hk_wr, + 1, IVCL (HK), VEC_HK, { NULL } + }; + +UNIT hk_unit[] = { + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) }, + { UDATA (&hk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+UNIT_RK06, RK06_SIZE) } + }; + +REG hk_reg[] = { + { GRDATA (HKCS1, hkcs1, DEV_RDX, 16, 0) }, + { GRDATA (HKWC, hkwc, DEV_RDX, 16, 0) }, + { GRDATA (HKBA, hkba, DEV_RDX, 16, 0) }, + { GRDATA (HKDA, hkda, DEV_RDX, 16, 0) }, + { GRDATA (HKCS2, hkcs2, DEV_RDX, 16, 0) }, + { BRDATA (HKDS, hkds, DEV_RDX, 16, HK_NUMDR) }, + { BRDATA (HKER, hker, DEV_RDX, 16, HK_NUMDR) }, + { BRDATA (HKDB, hkdb, DEV_RDX, 16, 3) }, + { GRDATA (HKDC, hkdc, DEV_RDX, 16, 0) }, + { GRDATA (HKOF, hkof, DEV_RDX, 8, 0) }, + { GRDATA (HKMR, hkmr, DEV_RDX, 16, 0) }, + { GRDATA (HKMR2, hkmr2, DEV_RDX, 16, 0), REG_RO }, + { GRDATA (HKMR3, hkmr3, DEV_RDX, 16, 0), REG_RO }, + { GRDATA (HKSPR, hkspr, DEV_RDX, 16, 0) }, + { FLDATA (INT, IREQ (HK), INT_V_HK) }, + { FLDATA (ERR, hkcs1, CSR_V_ERR) }, + { FLDATA (DONE, hkcs1, CSR_V_DONE) }, + { FLDATA (IE, hkcs1, CSR_V_IE) }, + { DRDATA (CTIME, hk_cwait, 24), REG_NZ + PV_LEFT }, + { DRDATA (STIME, hk_swait, 24), REG_NZ + PV_LEFT }, + { DRDATA (RTIME, hk_rwait, 24), REG_NZ + PV_LEFT }, + { DRDATA (MIN2TIME, hk_min2wait, 24), REG_NZ + PV_LEFT + REG_HRO }, + { URDATA (FNC, hk_unit[0].FNC, DEV_RDX, 5, 0, + HK_NUMDR, REG_HRO) }, + { URDATA (CYL, hk_unit[0].CYL, DEV_RDX, 10, 0, + HK_NUMDR, REG_HRO) }, + { BRDATA (OFFSET, hk_off, DEV_RDX, 16, HK_NUMDR), REG_HRO }, + { BRDATA (CYLDIF, hk_dif, DEV_RDX, 16, HK_NUMDR), REG_HRO }, + { URDATA (CAPAC, hk_unit[0].capac, 10, T_ADDR_W, 0, + HK_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA (DEVADDR, hk_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, hk_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB hk_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &hk_set_bad }, + { (UNIT_DTYPE+UNIT_ATT), UNIT_RK06 + UNIT_ATT, + "RK06", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), UNIT_RK07 + UNIT_ATT, + "RK07", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), UNIT_RK06, + "RK06", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), UNIT_RK07, + "RK07", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), UNIT_RK06, + NULL, "RK06", &hk_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), UNIT_RK07, + NULL, "RK07", &hk_set_size }, + { MTAB_XTD|MTAB_VDV, 0040, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEBTAB hk_deb[] = { + { "OPS", HKDEB_OPS }, + { "RRD", HKDEB_RRD }, + { "RWR", HKDEB_RWR }, + { NULL, 0 } + }; + +DEVICE hk_dev = { + "HK", hk_unit, hk_reg, hk_mod, + HK_NUMDR, DEV_RDX, 24, 1, DEV_RDX, 16, + NULL, NULL, &hk_reset, + &hk_boot, &hk_attach, &hk_detach, + &hk_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG, 0, + hk_deb, NULL, 0 + }; + +/* I/O dispatch routines, I/O addresses 17777440 - 17777476 */ + +t_stat hk_rd (int32 *data, int32 PA, int32 access) +{ +int32 drv, i, j; + +drv = GET_UNIT (hkcs2); /* get current unit */ +j = (PA >> 1) & 017; /* get reg offset */ +if (reg_in_drive[j] && (hk_unit[drv].flags & UNIT_DIS)) { /* nx disk */ + hkcs2 = hkcs2 | CS2_NED; /* set error flag */ + update_hkcs (0, drv); + *data = 0; + return SCPE_OK; + } + +update_hkcs (0, drv); /* update status */ +switch (j) { /* decode PA<4:1> */ + + case 000: /* HKCS1 */ + *data = hkcs1; + break; + + case 001: /* HKWC */ + *data = hkwc; + break; + + case 002: /* HKBA */ + *data = hkba = hkba & ~BA_MBZ; + break; + + case 003: /* HKDA */ + *data = hkda = hkda & ~DA_MBZ; + break; + + case 004: /* HKCS2 */ + *data = hkcs2 = (hkcs2 & ~CS2_MBZ) | CS2_IR; + break; + + case 005: /* HKDS */ + *data = hkds[drv]; + break; + + case 006: /* HKER */ + *data = hker[drv]; + break; + + case 007: /* HKAS */ + *data = hkof; + for (i = 0; i < HK_NUMDR; i++) + if (hkds[i] & DS_ATA) *data = *data | (AS_U0 << i); + break; + + case 010: /* HKDC */ + *data = hkdc = hkdc & ~DC_MBZ; + break; + + case 011: /* spare */ + *data = hkspr; + break; + + case 012: /* HKDB */ + *data = hkdb[0]; /* top of silo */ + hkdb[0] = hkdb[1]; /* ripple silo */ + hkdb[1] = hkdb[2]; + hkdb[2] = 0; /* just for READH */ + break; + + case 013: /* HKMR */ + *data = hkmr; + break; + + case 014: /* HKEC1 */ + case 015: /* HKEC2 */ + *data = 0; /* no ECC */ + break; + + case 016: /* HKMR2 */ + *data = hkmr2; + break; + + case 017: /* HKMR3 */ + *data = hkmr3; + break; + } + +if (DEBUG_PRI (hk_dev, HKDEB_RRD)) + fprintf (sim_deb, ">>HK%d read: reg%d=%o\n", drv, j, *data); +return SCPE_OK; +} + +t_stat hk_wr (int32 data, int32 PA, int32 access) +{ +int32 drv, i, j; +UNIT *uptr; + +drv = GET_UNIT (hkcs2); /* get current unit */ +uptr = hk_dev.units + drv; /* get unit */ +j = (PA >> 1) & 017; /* get reg offset */ +if ((hkcs1 & CS1_GO) && /* busy? */ + !(((j == 0) && (data & CS1_CCLR)) || /* not cclr or sclr? */ + ((j == 4) && (data & CS2_CLR)))) { + hkcs2 = hkcs2 | CS2_PGE; /* prog error */ + update_hkcs (0, drv); + return SCPE_OK; + } + +if (DEBUG_PRI (hk_dev, HKDEB_RWR)) + fprintf (sim_deb, ">>HK%d write: reg%d=%o\n", drv, j, data); +switch (j) { /* decode PA<4:1> */ + + case 000: /* HKCS1 */ + if (data & CS1_CCLR) { /* controller reset? */ + hkcs1 = CS1_DONE; /* CS1 = done */ + hkcs2 = CS2_IR; /* CS2 = ready */ + hkmr = hkmr2 = hkmr3 = 0; /* maint = 0 */ + hkda = hkdc = 0; + hkba = hkwc = 0; + hkspr = hkof = 0; + CLR_INT (HK); /* clr int */ + for (i = 0; i < HK_NUMDR; i++) { /* stop data xfr */ + if (sim_is_active (&hk_unit[i]) && + ((uptr->FNC & CS1_M_FNC) >= FNC_XFER)) + sim_cancel (&hk_unit[i]); + } + drv = 0; + break; + } + if (data & CS1_IE) { /* setting IE? */ + if (data & CS1_DONE) SET_INT (HK); /* write to DONE+IE? */ + } + else CLR_INT (HK); /* no, clr intr */ + hkcs1 = (hkcs1 & ~CS1_RW) | (data & CS1_RW); /* merge data */ + if (SC02C) hkspr = (hkspr & ~CS1_M_UAE) | GET_UAE (hkcs1); + if ((data & CS1_GO) && !(hkcs1 & CS1_ERR)) /* go? */ + hk_go (drv); + break; + + case 001: /* HKWC */ + hkwc = data; + break; + + case 002: /* HKBA */ + hkba = data & ~BA_MBZ; + break; + + case 003: /* HKDA */ + hkda = data & ~DA_MBZ; + break; + + case 004: /* HKCS2 */ + if (data & CS2_CLR) hk_reset (&hk_dev); /* init? */ + else hkcs2 = (hkcs2 & ~CS2_RW) | (data & CS2_RW) | CS2_IR; + drv = GET_UNIT (hkcs2); + break; + + case 007: /* HKAS */ + hkof = data & AS_OF; + break; + + case 010: /* HKDC */ + hkdc = data & ~DC_MBZ; + break; + + case 011: /* spare */ + hkspr = data; + if (SC02C) hkcs1 = PUT_UAE (hkcs1, hkspr & 03); /* SC02C? upd UAE */ + break; + + case 012: /* HKDB */ + hkdb[0] = data; + break; + + case 013: /* HKMR */ + hkmr = data & MR_RW; + break; + + default: /* all others RO */ + break; + } /* end switch */ + +update_hkcs (0, drv); /* update status */ +return SCPE_OK; +} + +/* Initiate operation - go set, not previously set */ + +void hk_go (int32 drv) +{ +int32 fnc, t; +UNIT *uptr; + +static uint8 fnc_cdt[16] = { + 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 + }; + +static uint8 fnc_nxf[16] = { + 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0 + }; +static uint8 fnc_att[16] = { + 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 + }; +static uint8 fnc_rdy[16] = { + 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 + }; +static uint8 fnc_cyl[16] = { + 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0 + }; + +fnc = GET_FNC (hkcs1); +if (DEBUG_PRI (hk_dev, HKDEB_OPS)) fprintf (sim_deb, + ">>HK%d strt: fnc=%o, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n", + drv, fnc, hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc); +uptr = hk_dev.units + drv; /* get unit */ +if (fnc != FNC_NOP) hkmr = hkmr & ~MR_MS; /* !nop, clr msg sel */ +if (uptr->flags & UNIT_DIS) { /* nx unit? */ + hkcs2 = hkcs2 | CS2_NED; /* set error flag */ + update_hkcs (CS1_DONE, drv); /* done */ + return; + } +if (fnc_cdt[fnc] && + (((hkcs1 & CS1_DT) != 0) != /* need dtype match? */ + ((uptr->flags & UNIT_DTYPE) != 0))) { + hk_cmderr (ER_DTY, drv); /* type error */ + return; + } +if (fnc_nxf[fnc] && ((hkds[drv] & DS_VV) == 0)) { /* need vol valid? */ + hk_cmderr (ER_NXF, drv); /* non exec func */ + return; + } +if (fnc_att[fnc] && !(uptr->flags & UNIT_ATT)) { /* need attached? */ + hk_cmderr (ER_UNS, drv); /* unsafe */ + return; + } +if (fnc_rdy[fnc] && sim_is_active (uptr)) return; /* need inactive? */ +if (fnc_cyl[fnc] && /* need valid cyl */ + ((GET_CY (hkdc) >= HK_CYL (uptr)) || /* bad cylinder */ + (GET_SF (hkda) >= HK_NUMSF) || /* bad surface */ + (GET_SC (hkda) >= HK_NUMSC))) { /* or bad sector? */ + hk_cmderr (ER_IAE, drv); /* illegal addr */ + return; + } + +hkcs1 = (hkcs1 | CS1_GO) & ~CS1_DONE; /* set go, clear done */ +switch (fnc) { /* case on function */ + +/* Instantaneous functions (unit may be busy, can't schedule thread) */ + + case FNC_NOP: /* no operation */ + hkmr2 = hk_rdmr2 (GET_MS (hkmr)); /* get serial msgs */ + hkmr3 = hk_rdmr3 (GET_MS (hkmr)); + update_hkcs (CS1_DONE, drv); /* done */ + break; + + case FNC_DCLR: /* drive clear */ + hkds[drv] &= ~DS_ATA; /* clr ATA */ + hker[drv] = 0; /* clear errors */ + update_hkcs (CS1_DONE, drv); /* done */ + break; + + case FNC_PACK: /* pack acknowledge */ + hkds[drv] = hkds[drv] | DS_VV; /* set volume valid */ + update_hkcs (CS1_DONE, drv); /* done */ + break; + +/* "Fast functions" finish in less than 15 usec */ + + case FNC_START: /* start spindle */ + case FNC_UNLOAD: /* unload */ + uptr->FNC = fnc; /* save function */ + sim_activate (uptr, hk_cwait); /* schedule */ + return; + +/* Positioning functions provide two interrupts - an immediate interrupt + on ctrl done and a second one (if ctrl ready) when the seek is complete */ + + case FNC_OFFSET: /* offset mode */ + case FNC_RECAL: /* recalibrate */ + case FNC_SEEK: /* seek */ + hkds[drv] = hkds[drv] | DS_PIP; /* set positioning */ + uptr->FNC = fnc; /* save function */ + sim_activate (uptr, hk_cwait); /* schedule */ + return; + +/* Data transfer functions lock the controller for the duration */ + + case FNC_WRITEH: /* write headers */ + case FNC_WRITE: /* write */ + hk_off[drv] = 0; /* clr offset */ + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + case FNC_READH: /* read headers */ + hk_dif[drv] = hkdc - uptr->CYL; /* cyl diff */ + t = abs (hk_dif[drv]); /* |cyl diff| */ + sim_activate (uptr, hk_rwait + (hk_swait * t)); /* Schedule */ + uptr->FNC = fnc; /* save function */ + uptr->CYL = hkdc; /* update cyl */ + return; + + default: + hk_cmderr (ER_ILF, drv); /* not supported */ + break; + } +return; +} + +/* Service unit timeout + + Complete movement or data transfer command + Unit must exist - can't remove an active unit + Unit must be attached - detach cancels in progress operations +*/ + +t_stat hk_svc (UNIT *uptr) +{ +int32 i, t, dc, fnc, err; +int32 wc, awc, da; +uint32 drv, ba; +uint16 comp; + +drv = (uint32) (uptr - hk_dev.units); /* get drv number */ +fnc = uptr->FNC & CS1_M_FNC; /* get function */ +switch (fnc) { /* case on function */ + +/* Fast commands - start spindle only provides one interrupt + because ATTACH implicitly spins up the drive */ + + case FNC_UNLOAD: /* unload */ + hk_detach (uptr); /* detach unit */ + case FNC_START: /* start spindle */ + update_hkcs (CS1_DONE, drv); /* done */ + break; + +/* Positioning commands provide two interrupts, an immediate controller done + and a delayed drive interrupt */ + + case FNC_OFFSET: /* offset */ + if (uptr->FNC & FNC_2ND) { /* 2nd int? */ + hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */ + update_hkcs (CS1_DI, drv); /* drive intr */ + } + else { + uptr->FNC = uptr->FNC | FNC_2ND; /* second state */ + hk_off[drv] = hkof & AS_OF; /* save offset */ + sim_activate (uptr, hk_min2wait); /* wait for compl */ + update_hkcs (CS1_DONE, drv); /* done */ + } + break; + + case FNC_RECAL: /* recalibrate */ + case FNC_SEEK: /* seek */ + if (uptr->FNC & FNC_2ND) { /* 2nd int? */ + hkds[drv] = (hkds[drv] & ~DS_PIP) | DS_ATA; /* upd sta */ + update_hkcs (CS1_DI, drv); /* drive intr */ + } + else { + uptr->FNC = uptr->FNC | FNC_2ND; /* second state */ + hk_off[drv] = 0; /* clr offset */ + dc = (fnc == FNC_SEEK)? hkdc: 0; /* get cyl */ + hk_dif[drv] = dc - uptr->CYL; /* cyl diff */ + t = abs (hk_dif[drv]) * hk_swait; /* |cyl diff| */ + if (t < hk_min2wait) t = hk_min2wait; /* min time */ + uptr->CYL = dc; /* save cyl */ + sim_activate (uptr, t); /* schedule */ + update_hkcs (CS1_DONE, drv); /* done */ + } + break; + +/* Data transfer commands only generate one interrupt */ + + case FNC_READH: + hkdb[0] = uptr->CYL << RDH1_V_CYL; /* first word */ + hkdb[1] = (GET_SC (hkda) << RDH2_V_SEC) | /* second word */ + (1 << (GET_SF (hkda) + RDH2_V_DHA)) | RDH2_GOOD; + hkdb[2] = hkdb[0] ^ hkdb[1]; /* checksum */ + update_hkcs (CS1_DONE, drv); /* done */ + break; + + case FNC_WRITE: /* write */ + if (uptr->flags & UNIT_WPRT) { /* write locked? */ + hk_cmderr (ER_WLE, drv); /* command error */ + return SCPE_OK; + } + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + if (SC02C) ba = ((hkspr & XM_MMASK) << 16) | hkba; /* 22b addr? */ + else ba = (GET_UAE (hkcs1) << 16) | hkba; /* no, 18b addr */ + da = GET_DA (hkdc, hkda) * HK_NUMWD; /* get disk addr */ + wc = 0200000 - hkwc; /* get true wc */ + + if ((da + wc) > HK_SIZE (uptr)) { /* disk overrun? */ + hker[drv] = hker[drv] | ER_AOE; /* set err */ + hkds[drv] = hkds[drv] | DS_ATA; /* set attn */ + wc = HK_SIZE (uptr) - da; /* trim xfer */ + if (da >= HK_SIZE (uptr)) { /* none left? */ + update_hkcs (CS1_DONE, drv); /* then done */ + break; + } + } + + err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); + if (uptr->FNC == FNC_WRITE) { /* write? */ + if (hkcs2 & CS2_UAI) { /* no addr inc? */ + if (t = Map_ReadW (ba, 2, &comp)) { /* get 1st wd */ + wc = 0; /* NXM, no xfr */ + hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ + } + for (i = 0; i < wc; i++) hkxb[i] = comp; + } + else { /* normal */ + if (t = Map_ReadW (ba, wc << 1, hkxb)) { /* get buf */ + wc = wc - (t >> 1); /* NXM, adj wc */ + hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ + } + ba = ba + (wc << 1); /* adv ba */ + } + awc = (wc + (HK_NUMWD - 1)) & ~(HK_NUMWD - 1); + for (i = wc; i < awc; i++) hkxb[i] = 0; /* fill buf */ + if (wc && !err) { /* write buf */ + fxwrite (hkxb, sizeof (uint16), wc, uptr->fileref); + err = ferror (uptr->fileref); + } + } /* end if wr */ + else if (uptr->FNC == FNC_READ) { /* read? */ + i = fxread (hkxb, sizeof (uint16), wc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < wc; i++) hkxb[i] = 0; /* fill buf */ + if (hkcs2 & CS2_UAI) { /* no addr inc? */ + if (t = Map_WriteW (ba, 2, &hkxb[wc - 1])) { + wc = 0; /* NXM, no xfr */ + hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ + } + } + else { /* normal */ + if (t = Map_WriteW (ba, wc << 1, hkxb)) { /* put buf */ + wc = wc - (t >> 1); /* NXM, adj wc */ + hkcs2 = hkcs2 | CS2_NEM; /* set nxm err */ + } + ba = ba + (wc << 1); /* adv ba */ + } + } /* end if read */ + else { /* wchk */ + i = fxread (hkxb, sizeof (uint16), wc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < wc; i++) hkxb[i] = 0; /* fill buf */ + awc = wc; + for (wc = 0; wc < awc; wc++) { /* loop thru buf */ + if (Map_ReadW (ba, 2, &comp)) { /* read word */ + hkcs2 = hkcs2 | CS2_NEM; /* set error */ + break; + } + if (comp != hkxb[wc]) { /* compare wd */ + hkcs2 = hkcs2 | CS2_WCE; /* set error */ + break; + } + if ((hkcs2 & CS2_UAI) == 0) ba = ba + 2; + } + } /* end else wchk */ + + hkwc = (hkwc + wc) & 0177777; /* final word count */ + hkba = (ba & 0177777) & ~BA_MBZ; /* lower 16b */ + hkcs1 = PUT_UAE (hkcs1, ba >> 16); /* upper 2b */ + if (SC02C) /* SC02C? upper 6b */ + hkspr = (hkspr & ~XM_MMASK) | ((ba >> 16) & XM_MMASK); + da = da + wc + (HK_NUMWD - 1); + da = da / HK_NUMWD; + hkda = da % HK_NUMSC; + da = da / HK_NUMSC; + hkda = hkda | ((da % HK_NUMSF) << DA_V_SF); + hkdc = da / HK_NUMSF; + + if (err != 0) { /* error? */ + hk_cmderr (ER_PAR, drv); /* set drive error */ + perror ("HK I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + + case FNC_WRITEH: /* write headers stub */ + update_hkcs (CS1_DONE, drv); /* set done */ + break; + } /* end case func */ + +return SCPE_OK; +} + +/* Controller status update + + Check for done transition + Update drive status + Update HKCS1 + Update interrupt request +*/ + +void update_hkcs (int32 flag, int32 drv) +{ +int32 i; + +update_hkds (drv); /* upd drv status */ +if (flag & CS1_DONE) hkcs1 = hkcs1 & ~CS1_GO; /* clear go */ +if (hkcs1 & CS1_IE) { /* intr enable? */ + if (((flag & CS1_DONE) && ((hkcs1 & CS1_DONE) == 0)) || + ((flag & CS1_DI) && (hkcs1 & CS1_DONE))) /* done 0->1 or DI? */ + SET_INT (HK); + } +else CLR_INT (HK); +hkcs1 = (hkcs1 & (CS1_DT|CS1_UAE|CS1_DONE|CS1_IE|CS1_SPA|CS1_FNC|CS1_GO)) | flag; +for (i = 0; i < HK_NUMDR; i++) { /* if ATA, set DI */ + if (hkds[i] & DS_ATA) hkcs1 = hkcs1 | CS1_DI; + } +if (hker[drv] | (hkcs1 & (CS1_PAR | CS1_CTO)) | /* if err, set ERR */ + (hkcs2 & CS2_ERR)) hkcs1 = hkcs1 | CS1_ERR; +if ((flag & CS1_DONE) && /* set done && debug? */ + (DEBUG_PRI (hk_dev, HKDEB_OPS))) + fprintf (sim_deb, + ">>HK%d done: fnc=%o, cs1=%o, cs2=%o, ds=%o, er=%o, cyl=%o, da=%o, ba=%o, wc=%o\n", + drv, GET_FNC (hkcs1), hkcs1, hkcs2, hkds[drv], hker[drv], hkdc, hkda, hkba, hkwc); +return; +} + +/* Drive status update */ + +void update_hkds (int32 drv) +{ +if (hk_unit[drv].flags & UNIT_DIS) { /* disabled? */ + hkds[drv] = hker[drv] = 0; /* all clear */ + return; + } +hkds[drv] = (hkds[drv] & (DS_VV | DS_PIP | DS_ATA)) | DS_VLD | DS_DRA; +if (hk_unit[drv].flags & UNIT_ATT) { /* attached? */ + if (!sim_is_active (&hk_unit[drv])) /* not busy? */ + hkds[drv] = hkds[drv] | DS_RDY; /* set RDY */ + if (hker[drv]) hkds[drv] = hkds[drv] | DS_ATA; /* err? set ATA */ + if (hk_off[drv]) hkds[drv] = hkds[drv] | DS_OF; /* offset? set OF */ + if (hk_unit[drv].flags & UNIT_WPRT) /* write locked? */ + hkds[drv] = hkds[drv] | DS_WRL; /* set WRL */ + } +else { + hkds[drv] = hkds[drv] & ~(DS_PIP | DS_VV); /* no, clr PIP,VV */ + hker[drv] = 0; /* no errors */ + } +if (hk_unit[drv].flags & UNIT_RK07) hkds[drv] = hkds[drv] | DS_DT; +return; +} + +/* Set error and abort command */ + +void hk_cmderr (int32 err, int32 drv) +{ +hker[drv] = hker[drv] | err; /* set error */ +hkds[drv] = hkds[drv] | DS_ATA; /* set attn */ +update_hkcs (CS1_DONE, drv); /* set done */ +return; +} + +/* Diagnostic registers + + It's unclear whether the drivers actually use these values, but the + Emulex controller bothers to implement them, so we will too */ + +int32 hk_mrpar (int32 v) +{ +int32 bit, wrk; + +wrk = v & 077777; /* par on 15b */ +v = wrk | ((hkmr & MR_PAR)? 0: AX_PAR); /* even/odd */ +while (wrk) { /* while 1's */ + bit = wrk & (-wrk); /* lowest 1 */ + wrk = wrk & ~bit; /* clear */ + v = v ^ AX_PAR; /* xor parity */ + } +return v; +} + +int32 hk_rdmr2 (int32 msg) +{ +int32 drv = GET_UNIT (hkcs2); +int32 v = drv << AX_V_UNIT; +UNIT *uptr = hk_dev.units + drv; +int32 fnc = uptr->FNC & CS1_M_FNC; + +switch (msg) { + + case 0: /* message A0 */ + v = v | ((hkds[drv] & DS_ATA)? A0_ATA: 0) | + ((hkds[drv] & DS_PIP)? A0_PIP: 0) | + ((uptr->flags & UNIT_WPRT)? A0_WRL: 0) | + ((hk_off[drv])? A0_OF: 0) | + ((uptr->flags & UNIT_RK07)? A0_DT: 0) | + ((hkds[drv] & DS_VV)? A0_VV: 0) | A0_DRA; + if (uptr->flags & UNIT_ATT) v = v | A0_SPO | + (!sim_is_active (uptr)? A0_RDY: 0); + break; + + case 1: /* message A1 */ + if (uptr->flags & UNIT_ATT) { + if (sim_is_active (uptr)) { + if (fnc == FNC_UNLOAD) v = v | A1_UNL; + else if (fnc == FNC_RECAL) v = v | A1_RTZ; + else if (fnc == FNC_SEEK) { + if (hk_dif[drv] < 0) v = v | A1_REV; + if (hk_dif[drv] > 0) v = v | A1_FWD; + } + } + v = v | (A1_SPD|A1_CAR|A1_DOR|A1_HHM|A1_SRV); + } + else v = v | A1_HHM; + break; + + case 2: /* message A2 */ + if (hkds[drv] & DS_OF) + v = v | ((hk_off[drv] & A2_M_DIF) << A2_V_DIF); + else v = v | ((hk_dif[drv] & A2_M_DIF) << A2_V_DIF); + break; + + case 3: /* message A3 */ + v = v | ((012340 + v) << A3_V_SNO); + break; + } + +return hk_mrpar (v); +} + +int32 hk_rdmr3 (int32 msg) +{ +int32 drv = GET_UNIT (hkcs2); +int32 v = msg & 03; + +switch (msg) { + + case 0: /* message B0 */ + v = v | ((hker[drv] & ER_WLE)? (B0_WLE | B0_FLT): 0) | + ((hker[drv] & ER_SKI)? (B0_SKI | B0_FLT): 0) | + ((hker[drv] & ER_NXF)? (B0_NXF | B0_FLT): 0) | + ((hker[drv] & ER_IAE)? (B0_IAE | B0_FLT): 0); + break; + + case 1: /* message B1 */ + v = v | ((hker[drv] & ER_SKI)? B1_SKI: 0) | + ((hker[drv] & ER_UNS)? B1_SVE: 0); + break; + + case 2: /* message B2 */ + v = v | (hk_unit[drv].CYL << B2_V_CYL); + break; + + case 3: /* message B3 */ + v = v | (GET_SC (hkda) << B3_V_SEC) | + (1 << (GET_SF (hkda) + B3_V_DHA)); + break; + } + +return hk_mrpar (v); +} + +/* Device reset */ + +t_stat hk_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +hkcs1 = CS1_DONE; /* set done */ +hkcs2 = CS2_IR; /* clear state */ +hkmr = hkmr2 = hkmr3 = 0; +hkda = hkdc = 0; +hkba = hkwc = 0; +hkof = hkspr = 0; +CLR_INT (HK); /* clear intr req */ +for (i = 0; i < HK_NUMDR; i++) { /* stop operations */ + uptr = hk_dev.units + i; + sim_cancel (uptr); + if (uptr->flags & UNIT_ATT) hkds[i] = hkds[i] & DS_VV; + else hkds[i] = 0; + uptr->CYL = uptr->FNC = 0; /* clear state */ + hk_dif[i] = 0; + hk_off[i] = 0; + hker[i] = 0; + } /* clear errors */ +if (hkxb == NULL) hkxb = (uint16 *) calloc (HK_MAXFR, sizeof (uint16)); +if (hkxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Device attach */ + +t_stat hk_attach (UNIT *uptr, char *cptr) +{ +uint32 drv, p; +t_stat r; + +uptr->capac = HK_SIZE (uptr); +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +drv = (uint32) (uptr - hk_dev.units); /* get drv number */ +hkds[drv] = DS_ATA | DS_RDY | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); +hker[drv] = 0; /* upd drv status */ +hk_off[drv] = 0; +hk_dif[drv] = 0; +uptr->CYL = 0; +update_hkcs (CS1_DI, drv); /* upd ctlr status */ + +p = sim_fsize (uptr->fileref); /* get file size */ +if (p == 0) { /* new disk image? */ + if (uptr->flags & UNIT_RO) return SCPE_OK; + return pdp11_bad_block (uptr, HK_NUMSC, HK_NUMWD); + } +if ((uptr->flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */ +if (p > (RK06_SIZE * sizeof (uint16))) { + uptr->flags = uptr->flags | UNIT_RK07; + uptr->capac = RK07_SIZE; + } +else { + uptr->flags = uptr->flags & ~UNIT_RK07; + uptr->capac = RK06_SIZE; + } +return SCPE_OK; +} + +/* Device detach */ + +t_stat hk_detach (UNIT *uptr) +{ +uint32 drv; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +drv = (uint32) (uptr - hk_dev.units); /* get drv number */ +hkds[drv] = (hkds[drv] & ~(DS_RDY | DS_WRL | DS_VV | DS_OF)) | DS_ATA; +if (sim_is_active (uptr)) { /* unit active? */ + sim_cancel (uptr); /* cancel operation */ + hker[drv] = hker[drv] | ER_OPI; /* set drive error */ + if ((uptr->FNC & FNC_2ND) == 0) /* expecting done? */ + update_hkcs (CS1_DONE, drv); /* set done */ + } +update_hkcs (CS1_DI, drv); /* request intr */ +return detach_unit (uptr); +} + +/* Set size command validation routine */ + +t_stat hk_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = val? RK07_SIZE: RK06_SIZE; +return SCPE_OK; +} + +/* Set bad block routine */ + +t_stat hk_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +return pdp11_bad_block (uptr, HK_NUMSC, HK_NUMWD); +} + +#if defined (VM_PDP11) + +/* Device bootstrap - does not clear CSR when done */ + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 014) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 0042115, /* "MD" */ + 0012706, BOOT_START, /* mov #boot_start, sp */ + 0012700, 0000000, /* mov #unit, r0 */ + 0012701, 0177440, /* mov #HKCS1, r1 */ + 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */ + 0010061, 0000010, /* mov r0, 10(r1) ; set unit */ + 0016102, 0000012, /* mov 12(r1), r2 ; drv typ */ + 0100375, /* bpl .-4 ; valid? */ + 0042702, 0177377, /* bic #177377, r2 ; clr rest */ + 0006302, /* asl r2 ; move */ + 0006302, /* asl r2 */ + 0012703, 0000003, /* mov #pack+go, r3 */ + 0050203, /* bis r2, r3 ; merge type */ + 0010311, /* mov r3, (r1); ; pack ack */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012761, 0177000, 0000002, /* mov #-512.,2(r1) ; set wc */ + 0005061, 0000004, /* clr 4(r1) ; clr ba */ + 0005061, 0000006, /* clr 6(r1) ; clr da */ + 0005061, 0000020, /* clr 20(r1) ; clr cyl */ + 0012703, 0000021, /* mov #read+go, r3 */ + 0050203, /* bis r2, r3 ; merge type */ + 0010311, /* mov r3, (r1); ; read */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0005002, /* clr R2 */ + 0005003, /* clr R3 */ + 0012704, BOOT_START+020, /* mov #start+020, r4 */ + 0005005, /* clr R5 */ + 0005007 /* clr PC */ + }; + +t_stat hk_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & CS2_M_UNIT; +M[BOOT_CSR >> 1] = hk_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat hk_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} + +#endif diff --git a/PDP11/pdp11_io.c b/PDP11/pdp11_io.c new file mode 100644 index 0000000..c4e1c5e --- /dev/null +++ b/PDP11/pdp11_io.c @@ -0,0 +1,722 @@ +/* pdp11_io.c: PDP-11 I/O simulator + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 16-May-08 RMS Added multiple DC11 support + Renamed DL11 in autoconfigure + 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) + 06-Jul-06 RMS Added multiple KL11/DL11 support + 15-Oct-05 RMS Fixed bug in autoconfiguration (missing XU) + 25-Jul-05 RMS Revised autoconfiguration algorithm and interface + 30-Sep-04 RMS Revised Unibus interface + 28-May-04 RMS Revised I/O dispatching (from John Dundas) + 25-Jan-04 RMS Removed local debug logging support + 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls + 21-Nov-03 RMS Added check for interrupt slot conflict (found by Dave Hittner) + 12-Mar-03 RMS Added logical name support + 08-Oct-02 RMS Trimmed I/O bus addresses + Added support for dynamic tables + Added show I/O space, autoconfigure routines + 12-Sep-02 RMS Added support for TMSCP, KW11P, RX211 + 26-Jan-02 RMS Revised for multiple DZ's + 06-Jan-02 RMS Revised I/O access, enable/disable support + 11-Dec-01 RMS Moved interrupt debug code + 08-Nov-01 RMS Cloned from cpu sources +*/ + +#include "pdp11_defs.h" + +extern uint16 *M; +extern int32 int_req[IPL_HLVL]; +extern int32 ub_map[UBM_LNT_LW]; +extern int32 cpu_opt, cpu_bme; +extern int32 trap_req, ipl; +extern int32 cpu_log; +extern int32 autcon_enb; +extern int32 uba_last; +extern FILE *sim_log; +extern DEVICE *sim_devices[], cpu_dev; +extern t_addr cpu_memsize; + +int32 calc_ints (int32 nipl, int32 trq); + +extern t_stat cpu_build_dib (void); +extern void init_mbus_tab (void); +extern t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp); + +/* I/O data structures */ + +static t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); +static t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); +static DIB *iodibp[IOPAGESIZE >> 1]; + +int32 int_vec[IPL_HLVL][32]; /* int req to vector */ + +int32 (*int_ack[IPL_HLVL][32])(void); /* int ack routines */ + +static const int32 pirq_bit[7] = { + INT_V_PIR1, INT_V_PIR2, INT_V_PIR3, INT_V_PIR4, + INT_V_PIR5, INT_V_PIR6, INT_V_PIR7 + }; + +/* I/O page lookup and linkage routines + + Inputs: + *data = pointer to data to read, if READ + data = data to store, if WRITE or WRITEB + pa = address + access = READ, WRITE, or WRITEB + Outputs: + status = SCPE_OK or SCPE_NXM +*/ + +t_stat iopageR (int32 *data, uint32 pa, int32 access) +{ +int32 idx; +t_stat stat; + +idx = (pa & IOPAGEMASK) >> 1; +if (iodispR[idx]) { + stat = iodispR[idx] (data, pa, access); + trap_req = calc_ints (ipl, trap_req); + return stat; + } +return SCPE_NXM; +} + +t_stat iopageW (int32 data, uint32 pa, int32 access) +{ +int32 idx; +t_stat stat; + +idx = (pa & IOPAGEMASK) >> 1; +if (iodispW[idx]) { + stat = iodispW[idx] (data, pa, access); + trap_req = calc_ints (ipl, trap_req); + return stat; + } +return SCPE_NXM; +} + +/* Calculate interrupt outstanding */ + +int32 calc_ints (int32 nipl, int32 trq) +{ +int32 i; + +for (i = IPL_HLVL - 1; i > nipl; i--) { + if (int_req[i]) return (trq | TRAP_INT); + } +return (trq & ~TRAP_INT); +} + +/* Find vector for highest priority interrupt */ + +int32 get_vector (int32 nipl) +{ +int32 i, j, t, vec; + +for (i = IPL_HLVL - 1; i > nipl; i--) { /* loop thru lvls */ + t = int_req[i]; /* get level */ + for (j = 0; t && (j < 32); j++) { /* srch level */ + if ((t >> j) & 1) { /* irq found? */ + int_req[i] = int_req[i] & ~(1u << j); /* clr irq */ + if (int_ack[i][j]) vec = int_ack[i][j](); + else vec = int_vec[i][j]; + return vec; /* return vector */ + } /* end if t */ + } /* end for j */ + } /* end for i */ +return 0; +} + +/* Read and write Unibus map registers + + In any even/odd pair + even = low 16b, bit <0> clear + odd = high 6b + + The Unibus map is stored as an array of longwords. + These routines are only reachable if a Unibus map is configured. +*/ + +t_stat ubm_rd (int32 *data, int32 addr, int32 access) +{ +int32 pg = (addr >> 2) & UBM_M_PN; + +*data = (addr & 2)? ((ub_map[pg] >> 16) & 077): + (ub_map[pg] & 0177776); +return SCPE_OK; +} + +t_stat ubm_wr (int32 data, int32 addr, int32 access) +{ +int32 sc, pg = (addr >> 2) & UBM_M_PN; + +if (access == WRITEB) { + sc = (addr & 3) << 3; + ub_map[pg] = (ub_map[pg] & ~(0377 << sc)) | + ((data & 0377) << sc); + } +else { + sc = (addr & 2) << 3; + ub_map[pg] = (ub_map[pg] & ~(0177777 << sc)) | + ((data & 0177777) << sc); + } +ub_map[pg] = ub_map[pg] & 017777776; +return SCPE_OK; +} + +/* Mapped memory access routines for DMA devices */ + +#define BUSMASK ((UNIBUS)? UNIMASK: PAMASK) + +/* Map I/O address to memory address - caller checks cpu_bme */ + +uint32 Map_Addr (uint32 ba) +{ +int32 pg = UBM_GETPN (ba); /* map entry */ +int32 off = UBM_GETOFF (ba); /* offset */ + +if (pg != UBM_M_PN) /* last page? */ + uba_last = (ub_map[pg] + off) & PAMASK; /* no, use map */ +else uba_last = (IOPAGEBASE + off) & PAMASK; /* yes, use fixed */ +return uba_last; +} + +/* I/O buffer routines, aligned access + + Map_ReadB - fetch byte buffer from memory + Map_ReadW - fetch word buffer from memory + Map_WriteB - store byte buffer into memory + Map_WriteW - store word buffer into memory + + These routines are used only for Unibus and Qbus devices. + Massbus devices have their own IO routines. As a result, + the historic 'map' parameter is no longer needed. + + - In a U18 configuration, the map is always disabled. + Device addresses are trimmed to 18b. + - In a U22 configuration, the map is always configured + (although it may be disabled). Device addresses are + trimmed to 18b. + - In a Qbus configuration, the map is always disabled. + Device addresses are trimmed to 22b. +*/ + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) +{ +uint32 alim, lim, ma; + +ba = ba & BUSMASK; /* trim address */ +lim = ba + bc; +if (cpu_bme) { /* map enabled? */ + for ( ; ba < lim; ba++) { /* by bytes */ + ma = Map_Addr (ba); /* map addr */ + if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */ + if (ma & 1) *buf++ = (M[ma >> 1] >> 8) & 0377; /* get byte */ + else *buf++ = M[ma >> 1] & 0377; + } + return 0; + } +else { /* physical */ + if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */ + else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */ + else return bc; /* no, err */ + for ( ; ba < alim; ba++) { /* by bytes */ + if (ba & 1) *buf++ = (M[ba >> 1] >> 8) & 0377; /* get byte */ + else *buf++ = M[ba >> 1] & 0377; + } + return (lim - alim); + } +} + +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) +{ +uint32 alim, lim, ma; + +ba = (ba & BUSMASK) & ~01; /* trim, align addr */ +lim = ba + (bc & ~01); +if (cpu_bme) { /* map enabled? */ + for (; ba < lim; ba = ba + 2) { /* by words */ + ma = Map_Addr (ba); /* map addr */ + if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */ + *buf++ = M[ma >> 1]; + } + return 0; + } +else { /* physical */ + if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */ + else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */ + else return bc; /* no, err */ + for ( ; ba < alim; ba = ba + 2) { /* by words */ + *buf++ = M[ba >> 1]; + } + return (lim - alim); + } +} + +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) +{ +uint32 alim, lim, ma; + +ba = ba & BUSMASK; /* trim address */ +lim = ba + bc; +if (cpu_bme) { /* map enabled? */ + for ( ; ba < lim; ba++) { /* by bytes */ + ma = Map_Addr (ba); /* map addr */ + if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */ + if (ma & 1) M[ma >> 1] = (M[ma >> 1] & 0377) | + ((uint16) *buf++ << 8); + else M[ma >> 1] = (M[ma >> 1] & ~0377) | *buf++; + } + return 0; + } +else { /* physical */ + if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */ + else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */ + else return bc; /* no, err */ + for ( ; ba < alim; ba++) { /* by bytes */ + if (ba & 1) M[ba >> 1] = (M[ba >> 1] & 0377) | + ((uint16) *buf++ << 8); + else M[ba >> 1] = (M[ba >> 1] & ~0377) | *buf++; + } + return (lim - alim); + } +} + +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) +{ +uint32 alim, lim, ma; + +ba = (ba & BUSMASK) & ~01; /* trim, align addr */ +lim = ba + (bc & ~01); +if (cpu_bme) { /* map enabled? */ + for (; ba < lim; ba = ba + 2) { /* by words */ + ma = Map_Addr (ba); /* map addr */ + if (!ADDR_IS_MEM (ma)) return (lim - ba); /* NXM? err */ + M[ma >> 1] = *buf++; + } + return 0; + } +else { /* physical */ + if (ADDR_IS_MEM (lim)) alim = lim; /* end ok? */ + else if (ADDR_IS_MEM (ba)) alim = cpu_memsize; /* no, strt ok? */ + else return bc; /* no, err */ + for ( ; ba < alim; ba = ba + 2) { /* by words */ + M[ba >> 1] = *buf++; + } + return (lim - alim); + } +} + +/* Enable/disable autoconfiguration */ + +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr != NULL) return SCPE_ARG; +autcon_enb = val; +return auto_config (NULL, 0); +} + +/* Show autoconfiguration status */ + +t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "autoconfiguration %s", (autcon_enb? "on": "off")); +return SCPE_OK; +} + +/* Change device address */ + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newba; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if ((val == 0) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newba = get_uint (cptr, 8, PAMASK, &r); /* get new */ +if (r != SCPE_OK) return r; /* error? */ +if ((newba <= IOPAGEBASE) || /* > IO page base? */ + (newba % ((uint32) val))) return SCPE_ARG; /* check modulus */ +dibp->ba = newba; /* store */ +dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ +autcon_enb = 0; /* autoconfig off */ +return SCPE_OK; +} + +/* Show device address */ + +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR; +fprintf (st, "address=%08o", dibp->ba); +if (dibp->lnt > 1) + fprintf (st, "-%08o", dibp->ba + dibp->lnt - 1); +if (dptr->flags & DEV_FLTA) fprintf (st, "*"); +return SCPE_OK; +} + +/* Set address floating */ + +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; + +if (cptr != NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dptr->flags = dptr->flags | DEV_FLTA; /* floating */ +return auto_config (NULL, 0); /* autoconfigure */ +} + +/* Change device vector */ + +t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newvec; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newvec = get_uint (cptr, 8, VEC_Q + 01000, &r); +if ((r != SCPE_OK) || (newvec == VEC_Q) || + ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) || + (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG; +dibp->vec = newvec; +dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ +autcon_enb = 0; /* autoconfig off */ +return SCPE_OK; +} + +/* Show device vector */ + +t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 vec, numvec; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +vec = dibp->vec; +if (arg) numvec = arg; +else numvec = dibp->vnum; +if (vec == 0) fprintf (st, "no vector"); +else { + fprintf (st, "vector=%o", vec); + if (numvec > 1) fprintf (st, "-%o", vec + (4 * (numvec - 1))); + } +return SCPE_OK; +} + +/* Init Unibus tables */ + +void init_ubus_tab (void) +{ +int32 i, j; + +for (i = 0; i < IPL_HLVL; i++) { /* clear intr tab */ + for (j = 0; j < 32; j++) { + int_vec[i][j] = 0; + int_ack[i][j] = NULL; + } + } +for (i = 0; i < (IOPAGESIZE >> 1); i++) { /* clear dispatch tab */ + iodispR[i] = NULL; + iodispW[i] = NULL; + iodibp[i] = NULL; + } +return; +} + +/* Build Unibus tables */ + +t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp) +{ +int32 i, idx, vec, ilvl, ibit; + +if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */ +if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR; +for (i = 0; i < dibp->vnum; i++) { /* loop thru vec */ + idx = dibp->vloc + i; /* vector index */ + vec = dibp->vec? (dibp->vec + (i * 4)): 0; /* vector addr */ + ilvl = idx / 32; + ibit = idx % 32; + if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */ + (int_ack[ilvl][ibit] != dibp->ack[i])) || + (int_vec[ilvl][ibit] && vec && + (int_vec[ilvl][ibit] != vec))) { + printf ("Device %s interrupt slot conflict at %d\n", + sim_dname (dptr), idx); + if (sim_log) fprintf (sim_log, + "Device %s interrupt slot conflict at %d\n", + sim_dname (dptr), idx); + return SCPE_STOP; + } + if (dibp->ack[i]) int_ack[ilvl][ibit] = dibp->ack[i]; + else if (vec) int_vec[ilvl][ibit] = vec; + } +for (i = 0; i < (int32) dibp->lnt; i = i + 2) { /* create entries */ + idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */ + if ((iodispR[idx] && dibp->rd && /* conflict? */ + (iodispR[idx] != dibp->rd)) || + (iodispW[idx] && dibp->wr && + (iodispW[idx] != dibp->wr))) { + printf ("Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + if (sim_log) fprintf (sim_log, + "Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + return SCPE_STOP; + } + if (dibp->rd) iodispR[idx] = dibp->rd; /* set rd dispatch */ + if (dibp->wr) iodispW[idx] = dibp->wr; /* set wr dispatch */ + iodibp[idx] = dibp; /* remember DIB */ + } +return SCPE_OK; +} + +/* Build tables from device list */ + +t_stat build_dib_tab (void) +{ +int32 i; +DEVICE *dptr; +DIB *dibp; +t_stat r; + +init_ubus_tab (); /* init Unibus tables */ +init_mbus_tab (); /* init Massbus tables */ +for (i = 0; i < 7; i++) /* seed PIRQ intr */ + int_vec[i + 1][pirq_bit[i]] = VEC_PIRQ; +if (r = cpu_build_dib ()) return r; /* build CPU entries */ +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ + if (dptr->flags & DEV_MBUS) { /* Massbus? */ + if (r = build_mbus_tab (dptr, dibp)) /* add to Mbus tab */ + return r; + } + else { /* no, Unibus */ + if (r = build_ubus_tab (dptr, dibp)) /* add to Unibus tab */ + return r; + } + } /* end if enabled */ + } /* end for */ +return SCPE_OK; +} + +/* Show IO space */ + +t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i, j; +DEVICE *dptr; +DIB *dibp; + +if (build_dib_tab ()) return SCPE_OK; /* build IO page */ +for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) { /* loop thru entries */ + if (iodibp[i] && (iodibp[i] != dibp)) { /* new block? */ + dibp = iodibp[i]; /* DIB for block */ + for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) { + if (((DIB*) sim_devices[j]->ctxt) == dibp) { + dptr = sim_devices[j]; /* locate device */ + break; + } /* end if */ + } /* end for j */ + fprintf (st, "%08o - %08o%c\t%s\n", /* print block entry */ + dibp->ba, dibp->ba + dibp->lnt - 1, + (dptr && (dptr->flags & DEV_FLTA))? '*': ' ', + dptr? sim_dname (dptr): "CPU"); + } /* end if */ + } /* end for i */ +return SCPE_OK; +} + +/* Autoconfiguration + + The table reflects the MicroVAX 3900 microcode, with one addition - the + number of controllers field handles devices where multiple instances + are simulated through a single DEVICE structure (e.g., DZ, VH). + + A minus number of vectors indicates a field that should be calculated + but not placed in the DIB (RQ, TQ dynamic vectors) */ + +#define AUTO_MAXC 4 +#define AUTO_CSRBASE 0010 +#define AUTO_VECBASE 0300 + +typedef struct { + char *dnam[AUTO_MAXC]; + int32 numc; + int32 numv; + uint32 amod; + uint32 vmod; + uint32 fixa[AUTO_MAXC]; + uint32 fixv[AUTO_MAXC]; + } AUTO_CON; + +AUTO_CON auto_tab[] = { + { { "DCI" }, DCX_LINES, 2, 0, 8, { 0 } }, /* DC11 - fx CSRs */ + { { "DLI" }, DLX_LINES, 2, 0, 8, { 0 } }, /* KL11/DL11/DLV11 - fx CSRs */ + { { NULL }, 1, 2, 8, 8 }, /* DJ11 */ + { { NULL }, 1, 2, 16, 8 }, /* DH11 */ + { { NULL }, 1, 2, 8, 8 }, /* DQ11 */ + { { NULL }, 1, 2, 8, 8 }, /* DU11 */ + { { NULL }, 1, 2, 8, 8 }, /* DUP11 */ + { { NULL }, 10, 2, 8, 8 }, /* LK11A */ + { { NULL }, 1, 2, 8, 8 }, /* DMC11 */ + { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */ + { { NULL }, 1, 2, 8, 8 }, /* KMC11 */ + { { NULL }, 1, 2, 8, 8 }, /* LPP11 */ + { { NULL }, 1, 2, 8, 8 }, /* VMV21 */ + { { NULL }, 1, 2, 16, 8 }, /* VMV31 */ + { { NULL }, 1, 2, 8, 8 }, /* DWR70 */ + { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */ + { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */ + {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12}, + {VEC_TS} }, + { { NULL }, 1, 2, 16, 8 }, /* LPA11K */ + { { NULL }, 1, 2, 8, 8 }, /* KW11C */ + { { NULL }, 1, 1, 8, 8 }, /* reserved */ + { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */ + { { NULL }, 1, 1, 8, 4 }, /* DR11W */ + { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */ + { { NULL }, 1, 2, 8, 8 }, /* DMP11 */ + { { NULL }, 1, 2, 8, 8 }, /* DPV11 */ + { { NULL }, 1, 2, 8, 8 }, /* ISB11 */ + { { NULL }, 1, 2, 16, 8 }, /* DMV11 */ + { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */ + { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */ + {IOBA_XQ,IOBA_XQB}, {VEC_XQ} }, + { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */ + {IOBA_RQ}, {VEC_RQ} }, + { { NULL }, 1, 8, 32, 4 }, /* DMF32 */ + { { NULL }, 1, 2, 16, 8 }, /* KMS11 */ + { { NULL }, 1, 1, 16, 4 }, /* VS100 */ + { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */ + { { NULL }, 1, 2, 16, 8 }, /* KMV11 */ + { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */ + { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */ + { { NULL }, 1, 6, 32, 4 }, /* CP132 */ + { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* VS31 */ + { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */ + { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */ + { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* DSV11 */ + { { NULL }, 1, 2, 8, 8 }, /* CSAM */ + { { NULL }, 1, 2, 8, 8 }, /* ADV11C */ + { { NULL }, 1, 0, 8, 0 }, /* AAV11C */ + { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */ + { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */ +/* { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */ + { { NULL }, -1 } /* end table */ +}; + +t_stat auto_config (char *name, int32 nctrl) +{ +uint32 csr = IOPAGEBASE + AUTO_CSRBASE; +uint32 vec = VEC_Q + AUTO_VECBASE; +AUTO_CON *autp; +DEVICE *dptr; +DIB *dibp; +uint32 j, k, vmask, amask; + +if (autcon_enb == 0) return SCPE_OK; /* enabled? */ +if (name) { /* updating? */ + if (nctrl < 0) return SCPE_ARG; + for (autp = auto_tab; autp->numc >= 0; autp++) { + for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { + if (strcmp (name, autp->dnam[j]) == 0) + autp->numc = nctrl; + } + } + } +for (autp = auto_tab; autp->numc >= 0; autp++) { /* loop thru table */ + if (autp->amod) { /* floating csr? */ + amask = autp->amod - 1; + csr = (csr + amask) & ~amask; /* align csr */ + } + for (j = k = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { + dptr = find_dev (autp->dnam[j]); /* find ctrl */ + if ((dptr == NULL) || (dptr->flags & DEV_DIS) || + !(dptr->flags & DEV_FLTA)) continue; /* enabled, floating? */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp == NULL) return SCPE_IERR; /* not there??? */ + if (autp->amod) { /* dyn csr needed? */ + if (autp->fixa[k]) /* fixed csr avail? */ + dibp->ba = autp->fixa[k]; /* use it */ + else { /* no fixed left */ + dibp->ba = csr; /* set CSR */ + csr += (autp->numc * autp->amod); /* next CSR */ + } /* end else */ + } /* end if dyn csr */ + if (autp->numv && autp->vmod) { /* dyn vec needed? */ + uint32 numv = abs (autp->numv); /* get num vec */ + if (autp->fixv[k]) { /* fixed vec avail? */ + if (autp->numv > 0) + dibp->vec = autp->fixv[k]; /* use it */ + } + else { /* no fixed left */ + vmask = autp->vmod - 1; + vec = (vec + vmask) & ~vmask; /* align vector */ + if (autp->numv > 0) + dibp->vec = vec; /* set vector */ + vec += (autp->numc * numv * 4); + } /* end else */ + } /* end if dyn vec */ + k++; /* next instance */ + } /* end for j */ + if (autp->amod) csr = csr + 2; /* flt CSR? gap */ + } /* end for i */ +return SCPE_OK; +} diff --git a/PDP11/pdp11_ke.c b/PDP11/pdp11_ke.c new file mode 100644 index 0000000..711c1c3 --- /dev/null +++ b/PDP11/pdp11_ke.c @@ -0,0 +1,348 @@ +/* pdp11_ke.c: PDP-11/20 extended arithmetic element + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ke_ACTION OF CONTRke_ACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This code draws on prior work by Tim Shoppa and Brad Parker. My thanks for + to them for letting me use their work. + + EAE PDP-11/20 extended arithmetic element +*/ + +#include "pdp11_defs.h" + +#define GET_SIGN_L(v) (((v) >> 31) & 1) +#define GET_SIGN_W(v) (((v) >> 15) & 1) +#define GET_SIGN_B(v) (((v) >> 7) & 1) + +/* KE11A I/O address offsets 0177300 - 0177316 */ + +#define KE_DIV 000 /* divide */ +#define KE_AC 002 /* accumulator */ +#define KE_MQ 004 /* MQ */ +#define KE_MUL 006 /* multiply */ +#define KE_SC 010 /* step counter */ +#define KE_NOR 012 /* normalize */ +#define KE_LSH 014 /* logical shift */ +#define KE_ASH 016 /* arithmetic shift */ + +/* Status register */ + +#define KE_SR_C 0001 /* carry */ +#define KE_SR_SXT 0002 /* AC<15:0> = MQ<15> */ +#define KE_SR_Z 0004 /* AC = MQ = 0 */ +#define KE_SR_MQZ 0010 /* MQ = 0 */ +#define KE_SR_ACZ 0020 /* AC = 0 */ +#define KE_SR_ACM1 0040 /* AC = 177777 */ +#define KE_SR_N 0100 /* last op negative */ +#define KE_SR_NXV 0200 /* last op ovf XOR N */ +#define KE_SR_DYN (KE_SR_SXT|KE_SR_Z|KE_SR_MQZ|KE_SR_ACZ|KE_SR_ACM1) + +/* Visible state */ + +uint32 ke_AC = 0; +uint32 ke_MQ = 0; +uint32 ke_SC = 0; +uint32 ke_SR = 0; + +DEVICE ke_dev; +t_stat ke_rd (int32 *data, int32 PA, int32 access); +t_stat ke_wr (int32 data, int32 PA, int32 access); +t_stat ke_reset (DEVICE *dptr); +uint32 ke_set_SR (void); + +DIB ke_dib = { IOBA_KE, IOLN_KE, &ke_rd, &ke_wr, 0 }; + +UNIT ke_unit = { + UDATA (NULL, UNIT_DISABLE, 0) + }; + +REG ke_reg[] = { + { ORDATA (AC, ke_AC, 16) }, + { ORDATA (MQ, ke_MQ, 16) }, + { ORDATA (SC, ke_SC, 6) }, + { ORDATA (SR, ke_SR, 8) }, + { NULL } + }; + +MTAB ke_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { 0 } + }; + +DEVICE ke_dev = { + "KE", &ke_unit, ke_reg, ke_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ke_reset, + NULL, NULL, NULL, + &ke_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS + }; + +/* KE read - reads are always 16b, to even addresses */ + +t_stat ke_rd (int32 *data, int32 PA, int32 access) +{ +switch (PA & 016) { /* decode PA<3:1> */ + + case KE_AC: /* AC */ + *data = ke_AC; + break; + + case KE_MQ: /* MQ */ + *data = ke_MQ; + break; + + case KE_NOR: /* norm (SC) */ + *data = ke_SC; + break; + + case KE_SC: /* SR/SC */ + *data = (ke_set_SR () << 8) | ke_SC; + break; + + default: + *data = 0; + break; + } + +return SCPE_OK; +} + +/* KE write - writes trigger actual arithmetic */ + +t_stat ke_wr (int32 data, int32 PA, int32 access) +{ +int32 quo, t32, sout, sign; +uint32 absd, absr; + +switch (PA & 017) { /* decode PA<3:0> */ + + case KE_DIV: /* divide */ + if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ + data |= 0177400; /* sext data to 16b */ + ke_SR = 0; /* N = V = C = 0 */ + t32 = (ke_AC << 16) | ke_MQ; /* 32b divd */ + if (GET_SIGN_W (ke_AC)) /* sext (divd) */ + t32 = t32 | ~017777777777; + if (GET_SIGN_W (data)) /* sext (divr) */ + data = data | ~077777; + absd = abs (t32); + absr = abs (data); + if ((absd >> 16) >= absr) { /* divide fails? */ + +/* Based on the documentation, here's what has happened: + + SC = 16. + SR = (AC<15> == data<15>) + AC'MQ = (AC'MQ << 1) | SR + AC = SR? AC - data: AC + data + SR = (AC<15> == data<15>) + SC = SC - 1 + stop +*/ + + sign = GET_SIGN_W (ke_AC ^ data) ^ 1; /* 1 if signs match */ + ke_AC = (ke_AC << 1) | (ke_MQ >> 15); + ke_AC = (sign? ke_AC - data: ke_AC + data) & DMASK; + ke_MQ = ((ke_MQ << 1) | sign) & DMASK; + if (GET_SIGN_W (ke_AC ^ data) == 0) /* 0 if signs match */ + ke_SR |= KE_SR_C; + ke_SC = 15; /* SC clocked once */ + ke_SR |= KE_SR_NXV; /* set overflow */ + } + else { + ke_SC = 0; + quo = t32 / data; + ke_MQ = quo & DMASK; /* MQ has quo */ + ke_AC = (t32 % data) & DMASK; /* AC has rem */ + if ((quo > 32767) || (quo < -32768)) /* quo overflow? */ + ke_SR |= KE_SR_NXV; /* set overflow */ + } + if (GET_SIGN_W (ke_MQ)) /* result negative? */ + ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */ + break; + + case KE_AC: /* AC */ + if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ + data |= 0177400; /* sext data to 16b */ + ke_AC = data; + break; + + case KE_AC + 1: /* AC odd byte */ + ke_AC = (ke_AC & 0377) | (data << 8); + break; + + case KE_MQ: /* MQ */ + if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ + data |= 0177400; /* sext data to 16b */ + ke_MQ = data; + if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */ + ke_AC = 0177777; + else ke_AC = 0; + break; + + case KE_MQ + 1: /* MQ odd byte */ + ke_MQ = (ke_MQ & 0377) | (data << 8); + if (GET_SIGN_W (ke_MQ)) /* sext MQ to AC */ + ke_AC = 0177777; + else ke_AC = 0; + break; + + case KE_MUL: /* multiply */ + if ((access == WRITEB) && GET_SIGN_B (data)) /* byte write? */ + data |= 0177400; /* sext data to 16b */ + ke_SC = 0; + if (GET_SIGN_W (data)) /* sext operands */ + data |= ~077777; + t32 = ke_MQ; + if (GET_SIGN_W (t32)) + t32 |= ~077777; + t32 = t32 * data; + ke_AC = (t32 >> 16) & DMASK; + ke_MQ = t32 & DMASK; + if (GET_SIGN_W (ke_AC)) /* result negative? */ + ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */ + else ke_SR = 0; /* N = 0, V = C = 0 */ + break; + + case KE_SC: /* SC */ + if (access == WRITEB) /* ignore byte writes */ + return SCPE_OK; + ke_SR = (data >> 8) & (KE_SR_NXV|KE_SR_N|KE_SR_C); + ke_SC = data & 077; + break; + + case KE_NOR: /* normalize */ + for (ke_SC = 0; ke_SC < 31; ke_SC++) { /* max 31 shifts */ + if (((ke_AC == 0140000) && (ke_MQ == 0)) || /* special case? */ + (GET_SIGN_W (ke_AC ^ (ke_AC << 1)))) /* AC<15> != AC<14>? */ + break; + ke_AC = ((ke_AC << 1) | (ke_MQ >> 15)) & DMASK; + ke_MQ = (ke_MQ << 1) & DMASK; + } + if (GET_SIGN_W (ke_AC)) /* result negative? */ + ke_SR = KE_SR_N | KE_SR_NXV; /* N = 1, V = C = 0 */ + else ke_SR = 0; /* N = 0, V = C = 0 */ + break; + + case KE_LSH: /* logical shift */ + ke_SC = 0; + ke_SR = 0; /* N = V = C = 0 */ + data = data & 077; /* 6b shift count */ + if (data != 0) { + t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */ + if (sign = GET_SIGN_W (ke_AC)) /* sext operand */ + t32 = t32 | ~017777777777; + if (data < 32) { /* [1,31] - left */ + sout = (t32 >> (32 - data)) | (-sign << data); + t32 = ((uint32) t32) << data; /* do shift (zext) */ + if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */ + ke_SR |= KE_SR_NXV; /* no, V = 1 */ + if (sout & 1) /* last bit lost = 1? */ + ke_SR |= KE_SR_C; /* yes, C = 1 */ + } + else { /* [32,63] = -32,-1 */ + if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */ + ke_SR |= KE_SR_C; /* yes, C = 1*/ + t32 = (data != 32)? ((uint32) t32) >> (64 - data): 0; + } + ke_AC = (t32 >> 16) & DMASK; + ke_MQ = t32 & DMASK; + } + if (GET_SIGN_W (ke_AC)) /* result negative? */ + ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */ + break; + +/* EAE ASH differs from EIS ASH and cannot use the same overflow test */ + + case KE_ASH: /* arithmetic shift */ + ke_SC = 0; + ke_SR = 0; /* N = V = C = 0 */ + data = data & 077; /* 6b shift count */ + if (data != 0) { + t32 = (ke_AC << 16) | ke_MQ; /* 32b operand */ + if (sign = GET_SIGN_W (ke_AC)) /* sext operand */ + t32 = t32 | ~017777777777; + if (data < 32) { /* [1,31] - left */ + sout = (t32 >> (31 - data)) | (-sign << data); + t32 = (t32 & 020000000000) | ((t32 << data) & 017777777777); + if (sout != (GET_SIGN_L (t32)? -1: 0)) /* bits lost = sext? */ + ke_SR |= KE_SR_NXV; /* no, V = 1 */ + if (sout & 1) /* last bit lost = 1? */ + ke_SR |= KE_SR_C; /* yes, C = 1 */ + } + else { /* [32,63] = -32,-1 */ + if ((t32 >> (63 - data)) & 1) /* last bit lost = 1? */ + ke_SR |= KE_SR_C; /* yes, C = 1 */ + t32 = (data != 32)? /* special case 32 */ + (((uint32) t32) >> (64 - data)) | (-sign << (data - 32)): + -sign; + } + ke_AC = (t32 >> 16) & DMASK; + ke_MQ = t32 & DMASK; + } + if (GET_SIGN_W (ke_AC)) /* result negative? */ + ke_SR ^= (KE_SR_N | KE_SR_NXV); /* N = 1, compl NXV */ + break; + + default: /* all others ignored */ + return SCPE_OK; + } /* end switch PA */ + +ke_set_SR (); +return SCPE_OK; +} + +/* Update status register based on current AC, MQ */ + +uint32 ke_set_SR (void) +{ +ke_SR &= ~KE_SR_DYN; /* clr dynamic bits */ +if (ke_MQ == 0) /* MQ == 0? */ + ke_SR |= KE_SR_MQZ; +if (ke_AC == 0) { /* AC == 0? */ + ke_SR |= KE_SR_ACZ; + if (GET_SIGN_W (ke_MQ) == 0) /* MQ positive? */ + ke_SR |= KE_SR_SXT; + if (ke_MQ == 0) /* MQ zero? */ + ke_SR |= KE_SR_Z; + } +if (ke_AC == 0177777) { /* AC == 177777? */ + ke_SR |= KE_SR_ACM1; + if (GET_SIGN_W (ke_MQ) == 1) /* MQ negative? */ + ke_SR |= KE_SR_SXT; + } +return ke_SR; +} + +/* Reset routine */ + +t_stat ke_reset (DEVICE *dptr) +{ +ke_SR = 0; +ke_SC = 0; +ke_AC = 0; +ke_MQ = 0; +return SCPE_OK; +} diff --git a/PDP11/pdp11_kg.c b/PDP11/pdp11_kg.c new file mode 100644 index 0000000..3e64353 --- /dev/null +++ b/PDP11/pdp11_kg.c @@ -0,0 +1,477 @@ +/* pdp11_kg.c - Communications Arithmetic Option KG11-A + + Copyright (c) 2007-2008, John A. Dundas III + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + kg KG11-A Communications Arithmetic Option (M7251) + + 08-Jan-08 JAD First public release integrated with SIMH V3.7-3. + 09-Dec-07 JAD SIMH-style debugging. + Finished validating against real hardware. + Support for up to 8 units, the maximum. + Keep all module data in the UNIT structure. + Clean up bit and mask definitions. + 01-Dec-07 JAD Now work on the corner cases that the + diagnostic does not check. + CLR does not clear the QUO bit. + Correct SR write logic. + 29-Nov-07 JAD Original implementation and testing based on + an idea from 07-Jul-03. Passes the ZKGAB0 + diagnostic. + + Information necessary to create this simulation was gathered from + a number of sources including: + + KG11-A Exclusive-OR and CRC block check manual, DEC-11-HKGAA-B-D + + Maintenance print set + + A Painless Guide to CRC Error Detection Algorithms, Ross N. Williams + + + The original PDP-11 instruction set, as implemented in the /20, + /15, /10, and /5, did not include XOR. [One of the differences + tables incorrectly indicates the /04 does not implement this + instruction.] This device implements XOR, XORB, and a variety of + CRCs. + + The maintenance prints indicate that the device was probably available + starting in late 1971. May need to check further. The first edition + of the manual was May 1972. + + The device was still sold at least as late as mid-1982 according + to the PDP-11 Systems and Options Summary. RSTS/E included support + for up to 8 units in support of the 2780 emulation or for use with + DP11, DU11, or DUP11. The device appears to have been retired by + 1983-03, and possibly earlier. + + I/O Page Registers + + SR 7707x0 (read-write) status + BCC 7707x2 (read-only) BCC (block check character) + DR 7707x4 (write-only) data + + Vector: none + + Priority: none + + The KG11-A is a programmed-I/O, non-interrupting device. Therefore + no vector or bus request level are necessary. It is a Unibus device + but since it does not address memory itself (it only presents + registers in the I/O page) it is compatible with extended Unibus + machines (22-bit) as well as traditional Unibus. + + Implements 5 error detection codes: + LRC-8 + LRC-16 + CRC-12 + CRC-16 + CRC-CCITT +*/ + +#if !defined (VM_PDP11) +#error "KG11 is not supported!" +#endif +#include "pdp11_defs.h" + +extern FILE *sim_deb; +extern REG cpu_reg[]; +extern int32 R[]; + +#ifndef KG_UNITS +#define KG_UNITS (8) +#endif + +/* Control and Status Register */ + +#define KGSR_V_QUO (8) /* RO */ +#define KGSR_V_DONE (7) /* RO */ +#define KGSR_V_SEN (6) /* R/W shift enable */ +#define KGSR_V_STEP (5) /* W */ +#define KGSR_V_CLR (4) /* W */ +#define KGSR_V_DDB (3) /* R/W double data byte */ +#define KGSR_V_CRCIC (2) /* R/W */ +#define KGSR_V_LRC (1) /* R/W */ +#define KGSR_V_16 (0) /* R/W */ + +#define KGSR_M_QUO (1u << KGSR_V_QUO) +#define KGSR_M_DONE (1u << KGSR_V_DONE) +#define KGSR_M_SEN (1u << KGSR_V_SEN) +#define KGSR_M_STEP (1u << KGSR_V_STEP) +#define KGSR_M_CLR (1u << KGSR_V_CLR) +#define KGSR_M_DDB (1u << KGSR_V_DDB) +#define KGSR_M_CRCIC (1u << KGSR_V_CRCIC) +#define KGSR_M_LRC (1u << KGSR_V_LRC) +#define KGSR_M_16 (1u << KGSR_V_16) + +#define KG_SR_RDMASK (KGSR_M_QUO | KGSR_M_DONE | KGSR_M_SEN | KGSR_M_DDB | \ + KGSR_M_CRCIC | KGSR_M_LRC | KGSR_M_16) +#define KG_SR_WRMASK (KGSR_M_SEN | KGSR_M_DDB | KGSR_M_CRCIC | \ + KGSR_M_LRC | KGSR_M_16) + +#define KG_SR_POLYMASK (KGSR_M_CRCIC|KGSR_M_LRC|KGSR_M_16) + +/* Unit structure redefinitions */ +#define SR u3 +#define BCC u4 +#define DR u5 +#define PULSCNT u6 + +#define POLY_LRC8 (0x0008) +#define POLY_LRC16 (0x0080) +#define POLY_CRC12 (0x0f01) +#define POLY_CRC16 (0xa001) +#define POLY_CCITT (0x8408) + +static const struct { + uint16 poly; + uint16 pulses; + const char * const name; +} config[] = { + /* DDB=0 */ + { POLY_CRC12, 6, "CRC-12" }, + { POLY_CRC16, 8, "CRC-16" }, + { POLY_LRC8, 8, "LRC-8" }, + { POLY_LRC16, 8, "LRC-16" }, + { 0, 0, "undefined" }, + { POLY_CCITT, 8, "CRC-CCITT" }, + { 0, 0, "undefined" }, + { 0, 0, "undefined" }, + /* DDB=1 */ + { POLY_CRC12, 12, "CRC-12" }, + { POLY_CRC16, 16, "CRC-16" }, + { POLY_LRC8, 16, "LRC-8" }, + { POLY_LRC16, 16, "LRC-16" }, + { 0, 0, "undefined" }, + { POLY_CCITT, 16, "CRC-CCITT" }, + { 0, 0, "undefined" }, + { 0, 0, "undefined" } +}; + +/* Forward declarations */ + +static t_stat kg_rd (int32 *, int32, int32); +static t_stat kg_wr (int32, int32, int32); +static t_stat kg_reset (DEVICE *); +static void do_poly (int, t_bool); +static t_stat set_units (UNIT *, int32, char *, void *); + +/* 16-bit rotate right */ + +#define ROR(n,v) (((v >> n) & DMASK) | (v << (16 - n)) & DMASK) + +/* 8-bit rotate right */ + +#define RORB(n,v) (((v & 0377) >> n) | ((v << (8 - n)) & 0377)) + +/* KG data structures + + kg_dib KG PDP-11 device information block + kg_unit KG unit descriptor + kg_reg KG register list + kg_mod KG modifiers table + kg_debug KG debug names table + kg_dev KG device descriptor +*/ + +static DIB kg_dib = { + IOBA_KG, + (IOLN_KG + 2) * KG_UNITS, + &kg_rd, + &kg_wr, + 0, 0, 0, { NULL } +}; + +static UNIT kg_unit[] = { + { UDATA (NULL, 0, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, + { UDATA (NULL, UNIT_DISABLE + UNIT_DIS, 0) }, +}; + +static const REG kg_reg[] = { + { ORDATA (SR0, kg_unit[0].SR, 16) }, + { ORDATA (SR1, kg_unit[1].SR, 16) }, + { ORDATA (SR2, kg_unit[2].SR, 16) }, + { ORDATA (SR3, kg_unit[3].SR, 16) }, + { ORDATA (SR4, kg_unit[4].SR, 16) }, + { ORDATA (SR5, kg_unit[5].SR, 16) }, + { ORDATA (SR6, kg_unit[6].SR, 16) }, + { ORDATA (SR7, kg_unit[7].SR, 16) }, + { ORDATA (BCC0, kg_unit[0].BCC, 16) }, + { ORDATA (BCC1, kg_unit[1].BCC, 16) }, + { ORDATA (BCC2, kg_unit[2].BCC, 16) }, + { ORDATA (BCC3, kg_unit[3].BCC, 16) }, + { ORDATA (BCC4, kg_unit[4].BCC, 16) }, + { ORDATA (BCC5, kg_unit[5].BCC, 16) }, + { ORDATA (BCC6, kg_unit[6].BCC, 16) }, + { ORDATA (BCC7, kg_unit[7].BCC, 16) }, + { ORDATA (DR0, kg_unit[0].DR, 16) }, + { ORDATA (DR1, kg_unit[1].DR, 16) }, + { ORDATA (DR2, kg_unit[2].DR, 16) }, + { ORDATA (DR3, kg_unit[3].DR, 16) }, + { ORDATA (DR4, kg_unit[4].DR, 16) }, + { ORDATA (DR5, kg_unit[5].DR, 16) }, + { ORDATA (DR6, kg_unit[6].DR, 16) }, + { ORDATA (DR7, kg_unit[7].DR, 16) }, + { ORDATA (PULSCNT0, kg_unit[0].PULSCNT, 16) }, + { ORDATA (PULSCNT1, kg_unit[1].PULSCNT, 16) }, + { ORDATA (PULSCNT2, kg_unit[2].PULSCNT, 16) }, + { ORDATA (PULSCNT3, kg_unit[3].PULSCNT, 16) }, + { ORDATA (PULSCNT4, kg_unit[4].PULSCNT, 16) }, + { ORDATA (PULSCNT5, kg_unit[5].PULSCNT, 16) }, + { ORDATA (PULSCNT6, kg_unit[6].PULSCNT, 16) }, + { ORDATA (PULSCNT7, kg_unit[7].PULSCNT, 16) }, + { ORDATA (DEVADDR, kg_dib.ba, 32), REG_HRO }, + { NULL } +}; + +static const MTAB kg_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "UNITS=0..8", &set_units, NULL, NULL }, + { 0 } +}; + +#define DBG_REG (01) +#define DBG_POLY (02) +#define DBG_CYCLE (04) + +static const DEBTAB kg_debug[] = { + {"REG", DBG_REG}, + {"POLY", DBG_POLY}, + {"CYCLE", DBG_CYCLE}, + {0}, +}; + +DEVICE kg_dev = { + "KG", (UNIT *) &kg_unit, (REG *) kg_reg, (MTAB *) kg_mod, + KG_UNITS, 8, 16, 2, 8, 16, + NULL, /* examine */ + NULL, /* deposit */ + &kg_reset, /* reset */ + NULL, /* boot */ + NULL, /* attach */ + NULL, /* detach */ + &kg_dib, + DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, + 0, /* debug control */ + (DEBTAB *) &kg_debug, /* debug flags */ + NULL, /* memory size chage */ + NULL /* logical name */ +}; + /* KG I/O address routines */ + +static t_stat kg_rd (int32 *data, int32 PA, int32 access) +{ + int unit = (PA >> 3) & 07; + + if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS)) + return (SCPE_NXM); + switch ((PA >> 1) & 03) { + + case 00: /* SR */ + if (DEBUG_PRI(kg_dev, DBG_REG)) + fprintf (sim_deb, ">>KG%d: rd SR %06o, PC %06o\n", + unit, kg_unit[unit].SR, PC); + *data = kg_unit[unit].SR & KG_SR_RDMASK; + break; + + case 01: /* BCC */ + if (DEBUG_PRI(kg_dev, DBG_REG)) + fprintf (sim_deb, ">>KG%d rd BCC %06o, PC %06o\n", + unit, kg_unit[unit].BCC, PC); + *data = kg_unit[unit].BCC & DMASK; + break; + + case 02: /* DR */ + break; + + default: + break; + } + return (SCPE_OK); +} + +static t_stat kg_wr (int32 data, int32 PA, int32 access) +{ + int setup; + int unit = (PA >> 3) & 07; + + if ((unit >= KG_UNITS) || (kg_unit[unit].flags & UNIT_DIS)) + return (SCPE_NXM); + switch ((PA >> 1) & 03) { + + case 00: /* SR */ + if (access == WRITEB) + data = (PA & 1) ? + (kg_unit[unit].SR & 0377) | (data << 8) : + (kg_unit[unit].SR & ~0377) | data; + if (DEBUG_PRI(kg_dev, DBG_REG)) + fprintf (sim_deb, ">>KG%d: wr SR %06o, PC %06o\n", + unit, data, PC); + if (data & KGSR_M_CLR) { + kg_unit[unit].PULSCNT = 0; /* not sure about this */ + kg_unit[unit].BCC = 0; + kg_unit[unit].SR |= KGSR_M_DONE; + } + setup = (kg_unit[unit].SR & 017) ^ (data & 017); + kg_unit[unit].SR = (kg_unit[unit].SR & ~KG_SR_WRMASK) | + (data & KG_SR_WRMASK); + /* if low 4b changed, reset C1 & C2 */ + if (setup) { + kg_unit[unit].PULSCNT = 0; + if (DEBUG_PRI(kg_dev, DBG_POLY)) + fprintf (sim_deb, ">>KG%d poly %s %d\n", + unit, config[data & 017].name, config[data & 017].pulses); + } + if (data & KGSR_M_SEN) + break; + if (data & KGSR_M_STEP) { + do_poly (unit, TRUE); + break; + } + break; + + case 01: /* BCC */ + break; /* ignored */ + + case 02: /* DR */ + if (access == WRITEB) + data = (PA & 1) ? + (kg_unit[unit].DR & 0377) | (data << 8) : + (kg_unit[unit].DR & ~0377) | data; + kg_unit[unit].DR = data & DMASK; + if (DEBUG_PRI(kg_dev, DBG_REG)) + fprintf (sim_deb, ">>KG%d: wr DR %06o, data %06o, PC %06o\n", + unit, kg_unit[unit].DR, data, PC); + kg_unit[unit].SR &= ~KGSR_M_DONE; + +/* In a typical device, this is normally where we would use sim_activate() + to initiate an I/O to be completed later. The KG is a little + different. When it was first introduced, it's computation operation + completed before another instruction could execute (on early PDP-11s), + and software often took "advantage" of this fact by not bothering + to check the status of the DONE bit. In reality, the execution + time of the polynomial is dependent upon the width of the poly; if + 8 bits 1us, if 16 bits, 2us. Since known existing software will + break if we actually defer the computation, it is performed immediately + instead. However this could easily be made into a run-time option, + if there were software to validate correct operation. */ + + if (kg_unit[unit].SR & KGSR_M_SEN) + do_poly (unit, FALSE); + break; + + default: + break; + } + return (SCPE_OK); +} + +/* KG reset */ + +static t_stat kg_reset (DEVICE *dptr) +{ + int i; + + if (DEBUG_PRI(kg_dev, DBG_REG)) + fprintf (sim_deb, ">>KG: reset PC %06o\n", PC); + for (i = 0; i < KG_UNITS; i++) { + kg_unit[i].SR = KGSR_M_DONE; + kg_unit[i].BCC = 0; + kg_unit[i].PULSCNT = 0; + } + return (SCPE_OK); +} + +static void cycleOneBit (int unit) +{ + int quo; + + if (DEBUG_PRI(kg_dev, DBG_CYCLE)) + fprintf (sim_deb, ">>KG%d: cycle s BCC %06o DR %06o\n", + unit, kg_unit[unit].BCC, kg_unit[unit].DR); + if (kg_unit[unit].SR & KGSR_M_DONE) + return; + if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0) + kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) | + ((kg_unit[unit].BCC >> 2) & 07700); + kg_unit[unit].SR &= ~KGSR_M_QUO; + quo = (kg_unit[unit].BCC & 01) ^ (kg_unit[unit].DR & 01); + kg_unit[unit].BCC = (kg_unit[unit].BCC & ~01) | quo; + if (kg_unit[unit].SR & KGSR_M_LRC) + kg_unit[unit].BCC = (kg_unit[unit].SR & KGSR_M_16) ? + ROR(1, kg_unit[unit].BCC) : + RORB(1, kg_unit[unit].BCC); + else + kg_unit[unit].BCC = (kg_unit[unit].BCC & 01) ? + (kg_unit[unit].BCC >> 1) ^ config[kg_unit[unit].SR & 07].poly : + kg_unit[unit].BCC >> 1; + kg_unit[unit].DR >>= 1; + kg_unit[unit].SR |= quo << KGSR_V_QUO; + if ((kg_unit[unit].SR & KG_SR_POLYMASK) == 0) + kg_unit[unit].BCC = (kg_unit[unit].BCC & 077) | + ((kg_unit[unit].BCC & 07700) << 2); + kg_unit[unit].PULSCNT++; + if (kg_unit[unit].PULSCNT >= config[kg_unit[unit].SR & 017].pulses) + kg_unit[unit].SR |= KGSR_M_DONE; + if (DEBUG_PRI(kg_dev, DBG_CYCLE)) + fprintf (sim_deb, ">>KG%d: cycle e BCC %06o DR %06o\n", + unit, kg_unit[unit].BCC, kg_unit[unit].DR); +} + +static void do_poly (int unit, t_bool step) +{ + if (kg_unit[unit].SR & KGSR_M_DONE) + return; + if (step) + cycleOneBit (unit); + else { + while (!(kg_unit[unit].SR & KGSR_M_DONE)) + cycleOneBit (unit); + } +} + +static t_stat set_units (UNIT *u, int32 val, char *s, void *desc) +{ + int32 i, units; + t_stat stat; + + if (s == NULL) + return (SCPE_ARG); + units = get_uint (s, 10, KG_UNITS, &stat); + if (stat != SCPE_OK) + return (stat); + for (i = 0; i < KG_UNITS; i++) { + if (i < units) + kg_unit[i].flags &= ~UNIT_DIS; + else + kg_unit[i].flags |= UNIT_DIS; + } + kg_dev.numunits = units; + return (SCPE_OK); +} diff --git a/PDP11/pdp11_lp.c b/PDP11/pdp11_lp.c new file mode 100644 index 0000000..691ca6c --- /dev/null +++ b/PDP11/pdp11_lp.c @@ -0,0 +1,192 @@ +/* pdp11_lp.c: PDP-11 line printer simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt LP11 line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + 07-Jul-05 RMS Removed extraneous externs + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 29-Sep-02 RMS Added vector change/display support + New data structures + 30-May-02 RMS Widened POS to 32b + 06-Jan-02 RMS Added enable/disable support + 09-Nov-01 RMS Added VAX support + 07-Sep-01 RMS Revised interrupt mechanism + 30-Oct-00 RMS Standardized register naming +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "LP11 is not supported on the PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif + +#define LPTCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* implemented */ +#define LPTCSR_RW (CSR_IE) /* read/write */ + +extern int32 int_req[IPL_HLVL]; + +int32 lpt_csr = 0; /* control/status */ +int32 lpt_stopioe = 0; /* stop on error */ + +DEVICE lpt_dev; +t_stat lpt_rd (int32 *data, int32 PA, int32 access); +t_stat lpt_wr (int32 data, int32 PA, int32 access); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *ptr); +t_stat lpt_detach (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { + IOBA_LPT, IOLN_LPT, &lpt_rd, &lpt_wr, + 1, IVCL (LPT), VEC_LPT, { NULL } + }; + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lpt_reg[] = { + { GRDATA (BUF, lpt_unit.buf, DEV_RDX, 8, 0) }, + { GRDATA (CSR, lpt_csr, DEV_RDX, 16, 0) }, + { FLDATA (INT, IREQ (LPT), INT_V_LPT) }, + { FLDATA (ERR, lpt_csr, CSR_V_ERR) }, + { FLDATA (DONE, lpt_csr, CSR_V_DONE) }, + { FLDATA (IE, lpt_csr, CSR_V_IE) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { GRDATA (DEVADDR, lpt_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, lpt_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB lpt_mod[] = { + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, DEV_RDX, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach, + &lpt_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS + }; + +/* Line printer routines + + lpt_rd I/O page read + lpt_wr I/O page write + lpt_svc process event (printer ready) + lpt_reset process reset + lpt_attach process attach + lpt_detach process detach +*/ + +t_stat lpt_rd (int32 *data, int32 PA, int32 access) +{ +if ((PA & 02) == 0) *data = lpt_csr & LPTCSR_IMP; /* csr */ +else *data = lpt_unit.buf; /* buffer */ +return SCPE_OK; +} + +t_stat lpt_wr (int32 data, int32 PA, int32 access) +{ +if ((PA & 02) == 0) { /* csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) CLR_INT (LPT); + else if ((lpt_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (LPT); + lpt_csr = (lpt_csr & ~LPTCSR_RW) | (data & LPTCSR_RW); + } +else { + if ((PA & 1) == 0) lpt_unit.buf = data & 0177; /* buffer */ + lpt_csr = lpt_csr & ~CSR_DONE; + CLR_INT (LPT); + if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || + (lpt_unit.buf == 012)) sim_activate (&lpt_unit, lpt_unit.wait); + else sim_activate (&lpt_unit, 0); + } +return SCPE_OK; +} + +t_stat lpt_svc (UNIT *uptr) +{ +lpt_csr = lpt_csr | CSR_ERR | CSR_DONE; +if (lpt_csr & CSR_IE) SET_INT (LPT); +if ((uptr->flags & UNIT_ATT) == 0) + return IORETURN (lpt_stopioe, SCPE_UNATT); +fputc (uptr->buf & 0177, uptr->fileref); +uptr->pos = ftell (uptr->fileref); +if (ferror (uptr->fileref)) { + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +lpt_csr = lpt_csr & ~CSR_ERR; +return SCPE_OK; +} + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_unit.buf = 0; +lpt_csr = CSR_DONE; +if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR; +CLR_INT (LPT); +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +lpt_csr = lpt_csr & ~CSR_ERR; +reason = attach_unit (uptr, cptr); +if ((lpt_unit.flags & UNIT_ATT) == 0) lpt_csr = lpt_csr | CSR_ERR; +return reason; +} + +t_stat lpt_detach (UNIT *uptr) +{ +lpt_csr = lpt_csr | CSR_ERR; +return detach_unit (uptr); +} diff --git a/PDP11/pdp11_mscp.h b/PDP11/pdp11_mscp.h new file mode 100644 index 0000000..4590ed7 --- /dev/null +++ b/PDP11/pdp11_mscp.h @@ -0,0 +1,514 @@ +/* pdp11_mscp.h: DEC MSCP and TMSCP definitionsn + + Copyright (c) 2001-2005, Robert M Supnik + Derived from work by Stephen F. Shirron + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 09-Jan-03 RMS Tape read/write end pkt is longer than disk read/write + 20-Sep-02 RMS Merged TMSCP definitions +*/ + +#ifndef _PDP11_MSCP_H_ +#define _PDP11_MSCP_H_ 0 + +/* Misc constants */ + +#define UID_DISK 2 /* disk class */ +#define UID_TAPE 3 /* tape class */ + +/* Opcodes */ + +#define OP_ABO 1 /* b: abort */ +#define OP_GCS 2 /* b: get command status */ +#define OP_GUS 3 /* b: get unit status */ +#define OP_SCC 4 /* b: set controller char */ +#define OP_AVL 8 /* b: available */ +#define OP_ONL 9 /* b: online */ +#define OP_SUC 10 /* b: set unit char */ +#define OP_DAP 11 /* b: det acc paths - nop */ +#define OP_ACC 16 /* b: access */ +#define OP_CCD 17 /* d: compare - nop */ +#define OP_ERS 18 /* b: erase */ +#define OP_FLU 19 /* d: flush - nop */ +#define OP_ERG 22 /* t: erase gap */ +#define OP_CMP 32 /* b: compare */ +#define OP_RD 33 /* b: read */ +#define OP_WR 34 /* b: write */ +#define OP_WTM 36 /* t: write tape mark */ +#define OP_POS 37 /* t: reposition */ +#define OP_FMT 47 /* d: format */ +#define OP_AVA 64 /* b: unit now avail */ +#define OP_END 0x80 /* b: end flag */ + +/* Modifiers */ + +#define MD_EXP 0x8000 /* d: express NI */ +#define MD_CMP 0x4000 /* b: compare NI */ +#define MD_CSE 0x2000 /* b: clr ser err */ +#define MD_ERR 0x1000 /* d: force error NI*/ +#define MD_CDL 0x1000 /* t: clr data lost NI*/ +#define MD_SCH 0x0800 /* t: supr cache NI */ +#define MD_SEC 0x0200 /* b: supr err corr NI */ +#define MD_SER 0x0100 /* b: supr err rec NI */ +#define MD_DLE 0x0080 /* t: detect LEOT */ +#define MD_IMM 0x0040 /* t: immediate NI */ +#define MD_EXA 0x0020 /* b: excl access NI */ +#define MD_SHD 0x0010 /* d: shadow NI */ +#define MD_UNL 0x0010 /* t avl: unload */ +#define MD_ERW 0x0008 /* t wr: enb rewrite */ +#define MD_REV 0x0008 /* t rd, pos: reverse */ +#define MD_SWP 0x0004 /* b suc: enb set wrp */ +#define MD_OBC 0x0004 /* t: pos: obj count */ +#define MD_IMF 0x0002 /* d onl: ign fmte NI */ +#define MD_RWD 0x0002 /* t pos: rewind */ +#define MD_ACL 0x0002 /* t avl: all class NI */ +#define MD_NXU 0x0001 /* b gus: next unit */ +#define MD_RIP 0x0001 /* d onl: allow rip NI */ + +/* End flags */ + +#define EF_LOG 0x0020 /* b: error log */ +#define EF_SXC 0x0010 /* b: serious exc */ +#define EF_EOT 0x0008 /* end of tape */ +#define EF_PLS 0x0004 /* pos lost */ +#define EF_DLS 0x0002 /* cached data lost NI */ + +/* Controller flags */ + +#define CF_RPL 0x8000 /* ctrl bad blk repl */ +#define CF_ATN 0x0080 /* enb attention */ +#define CF_MSC 0x0040 /* enb misc msgs */ +#define CF_OTH 0x0020 /* enb othr host msgs */ +#define CF_THS 0x0010 /* enb this host msgs */ +#define CF_MSK (CF_ATN|CF_MSC|CF_OTH|CF_THS) + +/* Unit flags */ + +#define UF_RPL 0x8000 /* d: ctrl bad blk repl */ +#define UF_CAC 0x8000 /* t: cache write back */ +#define UF_WPH 0x2000 /* b: wr prot hwre */ +#define UF_WPS 0x1000 /* b: wr prot swre */ +#define UF_SCH 0x0800 /* t: supr cache NI */ +#define UF_EXA 0x0400 /* b: exclusive NI */ +#define UF_WPD 0x0100 /* b: wr prot data NI */ +#define UF_RMV 0x0080 /* d: removable */ +#define UF_WBN 0x0040 /* t: write back NI */ +#define UF_VSS 0x0020 /* t: supr var speed NI */ +#define UF_VSU 0x0010 /* t: var speed unit NI */ +#define UF_EWR 0x0008 /* t: enh wr recovery NI */ +#define UF_CMW 0x0002 /* cmp writes NI */ +#define UF_CMR 0x0001 /* cmp reads NI */ + +/* Error log flags */ + +#define LF_SUC 0x0080 /* b: successful */ +#define LF_CON 0x0040 /* b: continuing */ +#define LF_BBR 0x0020 /* d: bad blk repl NI */ +#define LF_RCT 0x0010 /* d: err in repl NI */ +#define LF_SNR 0x0001 /* b: seq # reset */ + +/* Error log formats */ + +#define FM_CNT 0 /* b: port lf err */ +#define FM_BAD 1 /* b: bad host addr */ +#define FM_DSK 2 /* d: disk xfer */ +#define FM_SDI 3 /* d: SDI err */ +#define FM_SDE 4 /* d: sm disk err */ +#define FM_TAP 5 /* t: tape errors */ +#define FM_RPL 9 /* d: bad blk repl */ + +/* Status codes */ + +#define ST_SUC 0 /* b: successful */ +#define ST_CMD 1 /* b: invalid cmd */ +#define ST_ABO 2 /* b: aborted cmd */ +#define ST_OFL 3 /* b: unit offline */ +#define ST_AVL 4 /* b: unit avail */ +#define ST_MFE 5 /* b: media fmt err */ +#define ST_WPR 6 /* b: write prot err */ +#define ST_CMP 7 /* b: compare err */ +#define ST_DAT 8 /* b: data err */ +#define ST_HST 9 /* b: host acc err */ +#define ST_CNT 10 /* b: ctrl err */ +#define ST_DRV 11 /* b: drive err */ +#define ST_FMT 12 /* t: formatter err */ +#define ST_BOT 13 /* t: BOT encountered */ +#define ST_TMK 14 /* t: tape mark */ +#define ST_RDT 16 /* t: record trunc */ +#define ST_POL 17 /* t: pos lost */ +#define ST_SXC 18 /* b: serious exc */ +#define ST_LED 19 /* t: LEOT detect */ +#define ST_BBR 20 /* d: bad block */ +#define ST_DIA 31 /* b: diagnostic */ +#define ST_V_SUB 5 /* subcode */ +#define ST_V_INV 8 /* invalid op */ + +/* Status subcodes */ + +#define SB_SUC_IGN (1 << ST_V_SUB) /* t: unload ignored */ +#define SB_SUC_ON (8 << ST_V_SUB) /* b: already online */ +#define SB_SUC_EOT (32 << ST_V_SUB) /* t: EOT encountered */ +#define SB_SUC_RO (128 << ST_V_SUB) /* t: read only */ +#define SB_OFL_NV (1 << ST_V_SUB) /* b: no volume */ +#define SB_OFL_INOP (2 << ST_V_SUB) /* t: inoperative */ +#define SB_AVL_INU (32 << ST_V_SUB) /* b: in use */ +#define SB_WPR_SW (128 << ST_V_SUB) /* b: swre wlk */ +#define SB_WPR_HW (256 << ST_V_SUB) /* b: hwre wlk */ +#define SB_HST_OA (1 << ST_V_SUB) /* b: odd addr */ +#define SB_HST_OC (2 << ST_V_SUB) /* d: odd count */ +#define SB_HST_NXM (3 << ST_V_SUB) /* b: nx memory */ +#define SB_HST_PAR (4 << ST_V_SUB) /* b: parity err */ +#define SB_HST_PTE (5 << ST_V_SUB) /* b: mapping err */ +#define SB_DAT_RDE (7 << ST_V_SUB) /* t: read err */ + +/* Status invalid command subcodes */ + +#define I_OPCD (8 << ST_V_INV) /* inv opcode */ +#define I_FLAG (9 << ST_V_INV) /* inv flags */ +#define I_MODF (10 << ST_V_INV) /* inv modifier */ +#define I_BCNT (12 << ST_V_INV) /* inv byte cnt */ +#define I_LBN (28 << ST_V_INV) /* inv LBN */ +#define I_VRSN (12 << ST_V_INV) /* inv version */ +#define I_FMTI (28 << ST_V_INV) /* inv format */ + +/* Tape format flags */ + +#define TF_9TK 0x0100 /* 9 track */ +#define TF_9TK_NRZ 0x0001 /* 800 bpi */ +#define TF_9TK_PE 0x0002 /* 1600 bpi */ +#define TF_9TK_GRP 0x0004 /* 6250 bpi */ +#define TF_CTP 0x0200 /* TK50 */ +#define TF_CTP_LO 0x0001 /* low density */ +#define TF_CTP_HI 0x0002 /* hi density */ +#define TF_3480 0x0300 /* 3480 */ +#define TF_WOD 0x0400 /* RV80 */ + +/* Packet formats - note that all packet lengths must be multiples of 4 bytes */ + +/* Command packet header */ + +#define CMD_REFL 2 /* ref # */ +#define CMD_REFH 3 +#define CMD_UN 4 /* unit # */ +/* 5 /* reserved */ +#define CMD_OPC 6 /* opcode */ +#define CMD_MOD 7 /* modifier */ + +#define CMD_OPC_V_OPC 0 /* opcode */ +#define CMD_OPC_M_OPC 0xFF +#define CMD_OPC_V_CAA 8 /* cache NI */ +#define CMD_OPC_M_CAA 0xFF +#define CMD_OPC_V_FLG 8 /* flags */ +#define CMD_OPC_M_FLG 0xFF + +/* Response packet header */ + +#define RSP_LNT 12 +#define RSP_REFL 2 /* ref # */ +#define RSP_REFH 3 +#define RSP_UN 4 /* unit # */ +#define RSP_RSV 5 /* reserved */ +#define RSP_OPF 6 /* opcd,flg */ +#define RSP_STS 7 /* modifiers */ + +#define RSP_OPF_V_OPC 0 /* opcode */ +#define RSP_OPF_V_FLG 8 /* flags */ + +/* Abort packet - 2 W parameter, 2 W status */ + +#define ABO_LNT 16 +#define ABO_REFL 8 /* ref # */ +#define ABO_REFH 9 + +/* Avail packet - min size */ + +#define AVL_LNT 12 + +/* Erase packet - min size */ + +#define ERS_LNT 12 + +/* Erase gap - min size */ + +#define ERG_LNT 12 + +/* Flush - 10 W status (8 undefined) */ + +#define FLU_LNT 32 +/* 8 - 15 /* reserved */ +#define FLU_POSL 16 /* position */ +#define FLU_POSH 17 + +/* Write tape mark - 10W status (8 undefined) */ + +#define WTM_LNT 32 +/* 8 - 15 /* reserved */ +#define WTM_POSL 16 /* position */ +#define WTM_POSH 17 + +/* Get command status packet - 2 W parameter, 4 W status */ + +#define GCS_LNT 20 +#define GCS_REFL 8 /* ref # */ +#define GCS_REFH 9 +#define GCS_STSL 10 /* status */ +#define GCS_STSH 11 + +/* Format packet - 8 W parameters, none returned */ + +#define FMT_LNT 12 +#define FMT_IH 17 /* magic bit */ + +/* Get unit status packet - 18 W status (disk), 16W status (tape) */ + +#define GUS_LNT_D 48 +#define GUS_LNT_T 44 +#define GUS_MLUN 8 /* mlun */ +#define GUS_UFL 9 /* flags */ +#define GUS_RSVL 10 /* reserved */ +#define GUS_RSVH 11 +#define GUS_UIDA 12 /* unit ID */ +#define GUS_UIDB 13 +#define GUS_UIDC 14 +#define GUS_UIDD 15 +#define GUS_MEDL 16 /* media ID */ +#define GUS_MEDH 17 +#define GUS_UVER 23 /* unit version */ + +/* Disk specific status */ + +#define GUS_SHUN 18 /* shadowing */ +#define GUS_SHST 19 +#define GUS_TRK 20 /* track */ +#define GUS_GRP 21 /* group */ +#define GUS_CYL 22 /* cylinder */ +#define GUS_RCTS 24 /* RCT size */ +#define GUS_RBSC 25 /* RBNs, copies */ + +/* Tape specific status */ + +#define GUS_FMT 18 /* format */ +#define GUS_SPEED 19 /* speed */ +#define GUS_MENU 20 /* menu */ +#define GUS_CAP 21 /* capacity */ +#define GUS_FVER 22 /* fmtr version */ + +#define GUS_UIDD_V_MOD 0 /* unit model */ +#define GUS_UIDD_V_CLS 8 /* unit class */ +#define GUS_RB_V_RBNS 0 /* RBNs/track */ +#define GUS_RB_V_RCTC 8 /* RCT copies */ + +/* Unit online - 2 W parameter, 16 W status (disk or tape) */ + +#define ONL_LNT 44 +#define ONL_MLUN 8 /* mlun */ +#define ONL_UFL 9 /* flags */ +#define ONL_RSVL 10 /* reserved */ +#define ONL_RSVH 11 +#define ONL_UIDA 12 /* unit ID */ +#define ONL_UIDB 13 +#define ONL_UIDC 14 +#define ONL_UIDD 15 +#define ONL_MEDL 16 /* media ID */ +#define ONL_MEDH 17 + +/* Disk specific status */ + +#define ONL_SHUN 18 /* shadowing */ +#define ONL_SHST 19 +#define ONL_SIZL 20 /* size */ +#define ONL_SIZH 21 +#define ONL_VSNL 22 /* vol ser # */ +#define ONL_VSNH 23 + +/* Tape specific status */ + +#define ONL_FMT 18 /* format */ +#define ONL_SPD 19 /* speed */ +#define ONL_MAXL 20 /* max rec size */ +#define ONL_MAXH 21 +#define ONL_NREC 22 /* noise rec */ +#define ONL_RSVE 23 /* reserved */ + +#define ONL_UIDD_V_MOD 0 /* unit model */ +#define ONL_UIDD_V_CLS 8 /* unit class */ + +/* Set controller characteristics packet - 8 W parameters, 10 W status */ + +#define SCC_LNT 32 +#define SCC_MSV 8 /* MSCP version */ +#define SCC_CFL 9 /* flags */ +#define SCC_TMO 10 /* timeout */ +#define SCC_VER 11 /* ctrl version */ +#define SCC_CIDA 12 /* ctrl ID */ +#define SCC_CIDB 13 +#define SCC_CIDC 14 +#define SCC_CIDD 15 +#define SCC_MBCL 16 /* max byte count */ +#define SCC_MBCH 17 + +#define SCC_VER_V_SVER 0 /* swre vrsn */ +#define SCC_VER_V_HVER 8 /* hwre vrsn */ +#define SCC_CIDD_V_MOD 0 /* ctrl model */ +#define SCC_CIDD_V_CLS 8 /* ctrl class */ + +/* Set unit characteristics - 2 W parameter, 16 W status - same as ONL */ + +#define SUC_LNT 44 + +/* Reposition - 4 W parameters, 10 W status */ + +#define POS_LNT 32 +#define POS_RCL 8 /* record cnt */ +#define POS_RCH 9 +#define POS_TMCL 10 /* tape mk cnt */ +#define POS_TMCH 11 +/* reserved 12 - 15 */ +#define POS_POSL 16 /* position */ +#define POS_POSH 17 + +/* Data transfer packet - 10 W parameters (disk), 6W parameters (tape), + 10 W status (disk), 12W status (tape) */ + +#define RW_LNT_D 32 +#define RW_LNT_T 36 +#define RW_BCL 8 /* byte count */ +#define RW_BCH 9 +#define RW_BAL 10 /* buff desc */ +#define RW_BAH 11 +#define RW_MAPL 12 /* map table */ +#define RW_MAPH 13 +/* 14 /* reserved */ +/* 15 /* reserved */ + +/* Disk specific parameters */ + +#define RW_LBNL 16 /* LBN */ +#define RW_LBNH 17 +#define RW_WBCL 18 /* working bc */ +#define RW_WBCH 19 +#define RW_WBAL 20 /* working ba */ +#define RW_WBAH 21 +#define RW_WBLL 22 /* working lbn */ +#define RW_WBLH 23 + +/* Tape specific status */ + +#define RW_POSL 16 /* position */ +#define RW_POSH 17 +#define RW_RSZL 18 /* record size */ +#define RW_RSZH 19 + +/* Error log packet header */ + +#define ELP_REFL 2 /* ref # */ +#define ELP_REFH 3 +#define ELP_UN 4 /* unit */ +#define ELP_SEQ 5 +#define ELP_FF 6 /* fmt,flg */ +#define ELP_EVT 7 /* event */ + +#define ELP_EV_V_FMT 0 /* format */ +#define ELP_EV_V_FLG 8 /* flag */ + +/* Port last failure error log packet - 6 W status */ + +#define PLF_LNT 24 /* length */ +#define PLF_CIDA 8 /* ctrl ID */ +#define PLF_CIDB 9 +#define PLF_CIDC 10 +#define PLF_CIDD 11 +#define PLF_VER 12 /* ctrl version */ +#define PLF_ERR 13 /* err */ + +#define PLF_CIDD_V_MOD 0 /* ctrl model */ +#define PLF_CIDD_V_CLS 8 /* ctrl class */ +#define PLF_VER_V_SVER 0 /* swre ver */ +#define PLF_VER_V_HVER 8 /* hwre ver */ + +/* Disk transfer error log packet - 18 W status */ + +#define DTE_LNT 48 +#define DTE_CIDA 8 /* ctrl ID */ +#define DTE_CIDB 9 +#define DTE_CIDC 10 +#define DTE_CIDD 11 +#define DTE_VER 12 /* version */ +#define DTE_MLUN 13 /* mlun */ +#define DTE_UIDA 14 /* unit ID */ +#define DTE_UIDB 15 +#define DTE_UIDC 16 +#define DTE_UIDD 17 +#define DTE_UVER 18 +#define DTE_D2 23 +#define DTE_D3 24 +#define DTE_D4 25 + +/* Disk specific status */ + +#define DTE_SCYL 19 /* cylinder */ +#define DTE_VSNL 20 /* vol ser # */ +#define DTE_VSNH 21 +#define DTE_D1 22 /* dev params */ + +/* Tape specific status */ + +#define DTE_RETR 19 /* retry */ +#define DTE_POSL 20 /* position */ +#define DTE_POSH 21 +#define DTE_FVER 22 /* formatter ver */ + +#define DTE_CIDD_V_MOD 0 /* ctrl model */ +#define DTE_CIDD_V_CLS 8 /* ctrl class */ +#define DTE_VER_V_SVER 0 /* ctrl swre ver */ +#define DTE_VER_V_HVER 8 /* ctrl hwre ver */ +#define DTE_UIDD_V_MOD 0 /* unit model */ +#define DTE_UIDD_V_CLS 8 /* unit class */ +#define DTE_D2_V_SECT 8 +#define DTE_D3_V_SURF 0 +#define DTE_D3_V_CYL 8 + +/* Host bus error log packet - 8 W status */ + +#define HBE_LNT 28 +#define HBE_CIDA 8 /* ctrl ID */ +#define HBE_CIDB 9 +#define HBE_CIDC 10 +#define HBE_CIDD 11 +#define HBE_VER 12 /* ctrl version */ +#define HBE_RSV 13 /* reserved */ +#define HBE_BADL 14 /* bad address */ +#define HBE_BADH 15 + +#define HBE_CIDD_V_MOD 0 /* ctrl model */ +#define HBE_CIDD_V_CLS 8 /* ctrl class */ +#define HBE_VER_V_SVER 0 /* ctrl swre ver */ +#define HBE_VER_V_HVER 8 /* ctrl hwre ver */ + +/* Unit now available attention message - 10 W status, same as + first 10 W of status from get unit status +*/ + +#define UNA_LNT 32 + +#endif diff --git a/PDP11/pdp11_pclk.c b/PDP11/pdp11_pclk.c new file mode 100644 index 0000000..f2ff147 --- /dev/null +++ b/PDP11/pdp11_pclk.c @@ -0,0 +1,313 @@ +/* pdp11_pclk.c: KW11P programmable clock simulator + + Copyright (c) 1993-2008, Robert M Supnik + Written by John Dundas, used with his gracious permission + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + pclk KW11P line frequency clock + + 20-May-08 RMS Standardized clock delay at 1mips + 18-Jun-07 RMS Added UNIT_IDLE flag + 07-Jul-05 RMS Removed extraneous externs + + KW11-P Programmable Clock + + I/O Page Registers: + + CSR 17 772 540 + CSB 17 772 542 + CNT 17 772 544 + + Vector: 0104 + + Priority: BR6 + + ** Theory of Operation ** + + A real KW11-P is built around the following major components: + - 16-bit up/down counter + - 16-bit count set buffer + - 9-bit control and status register + - clocks: crystal controlled (1) 100 kHz and (2) 10 kHz clocks, + (3) a 50/60 Hz line frequency clock, and (4) an analog signal + input trigger + This software emulator for SIMH implements all of the above with + the exception of the external input trigger, which is arbitrarily + wired to 10Hz. + + Operation of this emulator is rather simplistic as compared to the + actual device. The register read and write routines are responsible + for copying internal state from the simulated device to the operating + program. Clock state variables are altered in the write routine + as well as the desired clock ticking rate. Possible rates are + given in the table below. + + Rate Bit 2 Bit 1 + 100 kHz 0 0 + 10 kHz 0 1 + Line frequency 1 0 + External 1 1 + + I think SIMH would have a hard time actually keeping up with a 100 + kHz ticking rate. I haven't tried this to verify, though. + + The clock service routine (pclk_svc) is responsible for ticking + the clock. The routine does implement up/down, repeat vs. + single-interrupt, and single clocking (maintenance). The routine + updates the internal state according to the options selected and + signals interrupts when appropriate. + + For a complete description of the device, please see DEC-11-HPWB-D + KW11-P Programmable Real-Time Clock Manual. + + ** Notes ** + + 1. The device is disabled by default. + + 2. Use XXDP V2.5 test program ZKWBJ1.BIC; loads at 1000, starts at + 1100? Seems to execute the first few tests correctly then waits + for input from the console. I don't have a description of how this + diagnostic works and thus don't know how to proceed from that point. + + 3. The read and write routines don't do anything with odd address + accesses. The manual says that byte writes don't work. + + 4. RSTS can use this clock in place of the standard KW11-L line + frequency clock. In order to do this, use the DEFAULT response in + the OPTION: dialog. To the Preferred clock prompt answer "P". + Then you have the option of line frequency "L" or some multiple of + 50 between 50 and 1000 to use the programmable portion of the clock. + + 5. This is really a Unibus peripheral and thus doesn't actually make + sense within a J-11 system as there never was a Qbus version of + this to the best of my knowledge. However the OSs I have tried + don't appear to exhibit any dissonance between this option and the + processor/bus emulation. I think the options that would make + somewhat more sense in a Qbus environment the KWV11-C and/or KWV11-S. + I don't know if any of the -11 OSs contained support for using + these as the system clock, though. +*/ + +#include "pdp11_defs.h" + +#define PCLKCSR_RDMASK 0100377 /* readable */ +#define PCLKCSR_WRMASK 0000137 /* writeable */ + +#define UNIT_V_LINE50HZ (UNIT_V_UF + 0) +#define UNIT_LINE50HZ (1 << UNIT_V_LINE50HZ) + +/* CSR - 17772540 */ + +#define CSR_V_FIX 5 /* single tick */ +#define CSR_V_UPDN 4 /* down/up */ +#define CSR_V_MODE 3 /* single/repeat */ +#define CSR_FIX (1u << CSR_V_FIX) +#define CSR_UPDN (1u << CSR_V_UPDN) +#define CSR_MODE (1u << CSR_V_MODE) +#define CSR_V_RATE 1 /* rate */ +#define CSR_M_RATE 03 +#define CSR_GETRATE(x) (((x) >> CSR_V_RATE) & CSR_M_RATE) + +extern int32 int_req[IPL_HLVL]; + +uint32 pclk_csr = 0; /* control/status */ +uint32 pclk_csb = 0; /* count set buffer */ +uint32 pclk_ctr = 0; /* counter */ +static uint32 rate[4] = { 100000, 10000, 60, 10 }; /* ticks per second */ +static uint32 xtim[4] = { 10, 100, 16667, 100000 }; /* nominal time delay */ + +DEVICE pclk_dev; +t_stat pclk_rd (int32 *data, int32 PA, int32 access); +t_stat pclk_wr (int32 data, int32 PA, int32 access); +t_stat pclk_svc (UNIT *uptr); +t_stat pclk_reset (DEVICE *dptr); +t_stat pclk_set_line (UNIT *uptr, int32 val, char *cptr, void *desc); +void pclk_tick (void); + +/* PCLK data structures + + pclk_dev PCLK device descriptor + pclk_unit PCLK unit descriptor + pclk_reg PCLK register list +*/ + +DIB pclk_dib = { + IOBA_PCLK, IOLN_PCLK, &pclk_rd, &pclk_wr, + 1, IVCL (PCLK), VEC_PCLK, { NULL } + }; + +UNIT pclk_unit = { UDATA (&pclk_svc, UNIT_IDLE, 0) }; + +REG pclk_reg[] = { + { ORDATA (CSR, pclk_csr, 16) }, + { ORDATA (CSB, pclk_csb, 16) }, + { ORDATA (CNT, pclk_ctr, 16) }, + { FLDATA (INT, IREQ (PCLK), INT_V_PCLK) }, + { FLDATA (OVFL, pclk_csr, CSR_V_ERR) }, + { FLDATA (DONE, pclk_csr, CSR_V_DONE) }, + { FLDATA (IE, pclk_csr, CSR_V_IE) }, + { FLDATA (UPDN, pclk_csr, CSR_V_UPDN) }, + { FLDATA (MODE, pclk_csr, CSR_V_MODE) }, + { FLDATA (RUN, pclk_csr, CSR_V_GO) }, + { BRDATA (TIME, xtim, 10, 32, 4), REG_NZ + PV_LEFT }, + { BRDATA (TPS, rate, 10, 32, 4), REG_NZ + PV_LEFT }, + { DRDATA (CURTIM, pclk_unit.wait, 32), REG_HRO }, + { ORDATA (DEVADDR, pclk_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, pclk_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB pclk_mod[] = { + { UNIT_LINE50HZ, UNIT_LINE50HZ, "50 Hz", "50HZ", &pclk_set_line }, + { UNIT_LINE50HZ, 0, "60 Hz", "60HZ", &pclk_set_line }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE pclk_dev = { + "PCLK", &pclk_unit, pclk_reg, pclk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &pclk_reset, + NULL, NULL, NULL, + &pclk_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS + }; + +/* Clock I/O address routines */ + +t_stat pclk_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 03) { + + case 00: /* CSR */ + *data = pclk_csr & PCLKCSR_RDMASK; /* return CSR */ + pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */ + CLR_INT (PCLK); /* clr intr */ + break; + + case 01: /* buffer */ + *data = 0; /* read only */ + break; + + case 02: /* counter */ + *data = pclk_ctr & DMASK; /* return counter */ + break; + } + +return SCPE_OK; +} + +t_stat pclk_wr (int32 data, int32 PA, int32 access) +{ +int32 old_csr = pclk_csr; +int32 rv; + +switch ((PA >> 1) & 03) { + + case 00: /* CSR */ + pclk_csr = data & PCLKCSR_WRMASK; /* clear and write */ + CLR_INT (PCLK); /* clr intr */ + rv = CSR_GETRATE (pclk_csr); /* new rate */ + pclk_unit.wait = xtim[rv]; /* new delay */ + if ((pclk_csr & CSR_GO) == 0) { /* stopped? */ + sim_cancel (&pclk_unit); /* cancel */ + if (data & CSR_FIX) pclk_tick (); /* fix? tick */ + } + else if (((old_csr & CSR_GO) == 0) || /* run 0 -> 1? */ + (rv != CSR_GETRATE (old_csr))) { /* rate change? */ + sim_cancel (&pclk_unit); /* cancel */ + sim_activate (&pclk_unit, /* start clock */ + sim_rtcn_init (pclk_unit.wait, TMR_PCLK)); + } + break; + + case 01: /* buffer */ + pclk_csb = pclk_ctr = data; /* store ctr */ + pclk_csr = pclk_csr & ~(CSR_ERR | CSR_DONE); /* clr err, done */ + CLR_INT (PCLK); /* clr intr */ + break; + + case 02: /* counter */ + break; /* read only */ + } + +return SCPE_OK; +} + +/* Clock tick (automatic or manual) */ + +void pclk_tick (void) +{ +if (pclk_csr & CSR_UPDN) /* up or down? */ + pclk_ctr = (pclk_ctr + 1) & DMASK; /* 1 = up */ +else pclk_ctr = (pclk_ctr - 1) & DMASK; /* 0 = down */ +if (pclk_ctr == 0) { /* reached zero? */ + if (pclk_csr & CSR_DONE) /* done already set? */ + pclk_csr = pclk_csr | CSR_ERR; /* set error */ + else pclk_csr = pclk_csr | CSR_DONE; /* else set done */ + if (pclk_csr & CSR_IE) SET_INT (PCLK); /* if IE, set int */ + if (pclk_csr & CSR_MODE) pclk_ctr = pclk_csb; /* if rpt, reload */ + else { + pclk_csb = 0; /* else clr ctr */ + pclk_csr = pclk_csr & ~CSR_GO; /* and clr go */ + } + } +return; +} + +/* Clock service */ + +t_stat pclk_svc (UNIT *uptr) +{ +int32 rv; + +pclk_tick (); /* tick clock */ +if ((pclk_csr & CSR_GO) == 0) return SCPE_OK; /* done? */ +rv = CSR_GETRATE (pclk_csr); /* get rate */ +sim_activate (&pclk_unit, sim_rtcn_calb (rate[rv], TMR_PCLK)); +return SCPE_OK; +} + +/* Clock reset */ + +t_stat pclk_reset (DEVICE *dptr) +{ +pclk_csr = 0; /* clear reg */ +pclk_csb = 0; +pclk_ctr = 0; +CLR_INT (PCLK); /* clear int */ +sim_cancel (&pclk_unit); /* cancel */ +pclk_unit.wait = xtim[0]; /* reset delay */ +return SCPE_OK; +} + +/* Set line frequency */ + +t_stat pclk_set_line (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val == UNIT_LINE50HZ) rate[2] = 50; +else rate[2] = 60; +return SCPE_OK; +} diff --git a/PDP11/pdp11_pt.c b/PDP11/pdp11_pt.c new file mode 100644 index 0000000..2487590 --- /dev/null +++ b/PDP11/pdp11_pt.c @@ -0,0 +1,354 @@ +/* pdp11_pt.c: PC11 paper tape reader/punch simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + + 07-Jul-05 RMS Removed extraneous externs + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 12-Sep-02 RMS Split off from pdp11_stddev.c +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +#define PT_DIS DEV_DIS +extern int32 int_req; + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +#define PT_DIS DEV_DIS +extern int32 int_req[IPL_HLVL]; + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define PT_DIS 0 +extern int32 int_req[IPL_HLVL]; +#endif + +#define PTRCSR_IMP (CSR_ERR+CSR_BUSY+CSR_DONE+CSR_IE) /* paper tape reader */ +#define PTRCSR_RW (CSR_IE) +#define PTPCSR_IMP (CSR_ERR + CSR_DONE + CSR_IE) /* paper tape punch */ +#define PTPCSR_RW (CSR_IE) + +int32 ptr_csr = 0; /* control/status */ +int32 ptr_stopioe = 0; /* stop on error */ +int32 ptp_csr = 0; /* control/status */ +int32 ptp_stopioe = 0; /* stop on error */ + +DEVICE ptr_dev, ptp_dev; +t_stat ptr_rd (int32 *data, int32 PA, int32 access); +t_stat ptr_wr (int32 data, int32 PA, int32 access); +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptr_attach (UNIT *uptr, char *ptr); +t_stat ptr_detach (UNIT *uptr); +t_stat ptp_rd (int32 *data, int32 PA, int32 access); +t_stat ptp_wr (int32 data, int32 PA, int32 access); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptp_attach (UNIT *uptr, char *ptr); +t_stat ptp_detach (UNIT *uptr); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +DIB ptr_dib = { + IOBA_PTR, IOLN_PTR, &ptr_rd, &ptr_wr, + 1, IVCL (PTR), VEC_PTR, { NULL } + }; + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { GRDATA (BUF, ptr_unit.buf, DEV_RDX, 8, 0) }, + { GRDATA (CSR, ptr_csr, DEV_RDX, 16, 0) }, + { FLDATA (INT, int_req, INT_V_PTR) }, + { FLDATA (ERR, ptr_csr, CSR_V_ERR) }, + { FLDATA (BUSY, ptr_csr, CSR_V_BUSY) }, + { FLDATA (DONE, ptr_csr, CSR_V_DONE) }, + { FLDATA (IE, ptr_csr, CSR_V_IE) }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { FLDATA (DEVDIS, ptr_dev.flags, DEV_V_DIS), REG_HRO }, + { NULL } + }; + +MTAB ptr_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, DEV_RDX, 8, + NULL, NULL, &ptr_reset, + NULL, &ptr_attach, &ptr_detach, + &ptr_dib, DEV_DISABLE | PT_DIS | DEV_UBUS | DEV_QBUS + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +DIB ptp_dib = { + IOBA_PTP, IOLN_PTP, &ptp_rd, &ptp_wr, + 1, IVCL (PTP), VEC_PTP, { NULL } + }; + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { GRDATA (BUF, ptp_unit.buf, DEV_RDX, 8, 0) }, + { GRDATA (CSR, ptp_csr, DEV_RDX, 16, 0) }, + { FLDATA (INT, int_req, INT_V_PTP) }, + { FLDATA (ERR, ptp_csr, CSR_V_ERR) }, + { FLDATA (DONE, ptp_csr, CSR_V_DONE) }, + { FLDATA (IE, ptp_csr, CSR_V_IE) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +MTAB ptp_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, DEV_RDX, 8, + NULL, NULL, &ptp_reset, + NULL, &ptp_attach, &ptp_detach, + &ptp_dib, DEV_DISABLE | PT_DIS | DEV_UBUS | DEV_QBUS + }; + +/* Paper tape reader I/O address routines */ + +t_stat ptr_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* ptr csr */ + *data = ptr_csr & PTRCSR_IMP; + return SCPE_OK; + + case 1: /* ptr buf */ + ptr_csr = ptr_csr & ~CSR_DONE; + CLR_INT (PTR); + *data = ptr_unit.buf & 0377; + return SCPE_OK; + } + +return SCPE_NXM; /* can't get here */ +} + +t_stat ptr_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* ptr csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) CLR_INT (PTR); + else if (((ptr_csr & CSR_IE) == 0) && (ptr_csr & (CSR_ERR | CSR_DONE))) + SET_INT (PTR); + if (data & CSR_GO) { + ptr_csr = (ptr_csr & ~CSR_DONE) | CSR_BUSY; + CLR_INT (PTR); + if (ptr_unit.flags & UNIT_ATT) /* data to read? */ + sim_activate (&ptr_unit, ptr_unit.wait); + else sim_activate (&ptr_unit, 0); /* error if not */ + } + ptr_csr = (ptr_csr & ~PTRCSR_RW) | (data & PTRCSR_RW); + return SCPE_OK; + + case 1: /* ptr buf */ + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; /* can't get here */ +} + +/* Paper tape reader service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +ptr_csr = (ptr_csr | CSR_ERR) & ~CSR_BUSY; +if (ptr_csr & CSR_IE) SET_INT (PTR); +if ((ptr_unit.flags & UNIT_ATT) == 0) + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } +ptr_csr = (ptr_csr | CSR_DONE) & ~CSR_ERR; +ptr_unit.buf = temp & 0377; +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +/* Paper tape reader support routines */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_unit.buf = 0; +ptr_csr = 0; +if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR; +CLR_INT (PTR); +sim_cancel (&ptr_unit); +return SCPE_OK; +} + +t_stat ptr_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +if ((ptr_unit.flags & UNIT_ATT) == 0) ptr_csr = ptr_csr | CSR_ERR; +else ptr_csr = ptr_csr & ~CSR_ERR; +return reason; +} + +t_stat ptr_detach (UNIT *uptr) +{ +ptr_csr = ptr_csr | CSR_ERR; +return detach_unit (uptr); +} + +/* Paper tape punch I/O address routines */ + +t_stat ptp_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* ptp csr */ + *data = ptp_csr & PTPCSR_IMP; + return SCPE_OK; + + case 1: /* ptp buf */ + *data = ptp_unit.buf; + return SCPE_OK; + } + +return SCPE_NXM; /* can't get here */ +} + +t_stat ptp_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* ptp csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) CLR_INT (PTP); + else if (((ptp_csr & CSR_IE) == 0) && (ptp_csr & (CSR_ERR | CSR_DONE))) + SET_INT (PTP); + ptp_csr = (ptp_csr & ~PTPCSR_RW) | (data & PTPCSR_RW); + return SCPE_OK; + + case 1: /* ptp buf */ + if ((PA & 1) == 0) ptp_unit.buf = data & 0377; + ptp_csr = ptp_csr & ~CSR_DONE; + CLR_INT (PTP); + if (ptp_unit.flags & UNIT_ATT) /* file to write? */ + sim_activate (&ptp_unit, ptp_unit.wait); + else sim_activate (&ptp_unit, 0); /* error if not */ + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; /* can't get here */ +} + +/* Paper tape punch service */ + +t_stat ptp_svc (UNIT *uptr) +{ +ptp_csr = ptp_csr | CSR_ERR | CSR_DONE; +if (ptp_csr & CSR_IE) SET_INT (PTP); +if ((ptp_unit.flags & UNIT_ATT) == 0) + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +ptp_csr = ptp_csr & ~CSR_ERR; +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* Paper tape punch support routines */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +ptp_csr = CSR_DONE; +if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR; +CLR_INT (PTP); +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat ptp_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +if ((ptp_unit.flags & UNIT_ATT) == 0) ptp_csr = ptp_csr | CSR_ERR; +else ptp_csr = ptp_csr & ~CSR_ERR; +return reason; +} + +t_stat ptp_detach (UNIT *uptr) +{ +ptp_csr = ptp_csr | CSR_ERR; +return detach_unit (uptr); +} diff --git a/PDP11/pdp11_rc.c b/PDP11/pdp11_rc.c new file mode 100644 index 0000000..1f32ec5 --- /dev/null +++ b/PDP11/pdp11_rc.c @@ -0,0 +1,583 @@ +/* pdp11_rc.c: RC11/RS64 fixed head disk simulator + + Copyright (c) 2007-2008, John A. Dundas III + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + rc RC11/RS64 fixed head disk + + 28-Dec-07 JAD Correct extraction of unit number from da in rc_svc. + Clear _all_ error bits when a new operation starts. + Passes all diagnostics in all configurations. + 25-Dec-07 JAD Compute the CRC-16 of the last sector read via + a READ or WCHK. + 20-Dec-07 JAD Correctly simulate rotation over the selected + track for RCLA. Also update the register + correctly during I/O operations. + Insure function activation time is non-zero. + Handle unit number wrap correctly. + 19-Dec-07 JAD Iterate over a full sector regardless of the + actual word count so that RCDA ends correctly. + Honor the read-only vs. read-write status of the + attached file. + 16-Dec-07 JAD The RCDA must be checked for validity when it is + written to, not just when GO is received. + 15-Dec-07 JAD Better handling of disk address errors and the RCLA + register. + Add more registers to the visible device state. + 07-Jan-07 JAD Initial creation and testing. Adapted from pdp11_rf.c. + + The RS64 is a head-per-track disk. To minimize overhead, the entire RC11 + is buffered in memory. Up to 4 RS64 "platters" may be controlled by one + RC11 for a total of 262,144 words (65536kW/platter). [Later in time the + RK611 was assigned the same CSR address.] + + Diagnostic routines: + ZRCAB0.BIC - passes w/1-4 platters + ZRCBB0.BIC - passes w/1-4 platters + ZRCCB0.BIC - passes w/1-4 platters + Note that the diagnostics require R/W disks (i.e., will destroy any + existing data). + + For regression, must pass all three diagnostics configured for 1-4 + platters for a total of 12 tests. + + Information necessary to create this simulation was gathered from the + PDP11 Peripherals Handbook, 1973-74 edition. + + One timing parameter is provided: + + rc_time Minimum I/O operation time, must be non-zero +*/ + +#if !defined (VM_PDP11) +#error "RC11 is not supported!" +#endif +#include "pdp11_defs.h" +#include + +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ +#define UNIT_M_PLAT 03 +#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) + +/* Constants */ + +#define RC_NUMWD (32*64) /* words/track */ +#define RC_NUMTR 32 /* tracks/disk */ +#define RC_DKSIZE (RC_NUMTR * RC_NUMWD) /* words/disk */ +#define RC_NUMDK 4 /* disks/controller */ +#define RC_WMASK (RC_NUMWD - 1) /* word mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ + +/* Control and status register (RCCS) */ + +#define RCCS_ERR (CSR_ERR) /* error */ +#define RCCS_DATA 0040000 /* data error */ +#define RCCS_ADDR 0020000 /* address error */ +#define RCCS_WLK 0010000 /* write lock */ +#define RCCS_NED 0004000 /* nx disk */ +#define RCCS_WCHK 0002000 /* write check */ +#define RCCS_INH 0001000 /* inhibit CA incr */ +#define RCCS_ABO 0000400 /* abort */ +#define RCCS_DONE (CSR_DONE) +#define RCCS_IE (CSR_IE) +#define RCCS_M_MEX 0000003 /* memory extension */ +#define RCCS_V_MEX 4 +#define RCCS_MEX (RCCS_M_MEX << RCCS_V_MEX) +#define RCCS_MAINT 0000010 /* maint */ +#define RCCS_M_FUNC 0000003 /* function */ +#define RFNC_LAH 0 +#define RFNC_WRITE 1 +#define RFNC_READ 2 +#define RFNC_WCHK 3 +#define RCCS_V_FUNC 1 +#define RCCS_FUNC (RCCS_M_FUNC << RCCS_V_FUNC) +#define RCCS_GO 0000001 + +#define RCCS_ALLERR (RCCS_DATA|RCCS_ADDR|RCCS_WLK|RCCS_NED|RCCS_WCHK) +#define RCCS_W (RCCS_INH | RCCS_ABO |RCCS_IE | RCCS_MEX | RCCS_MAINT | \ + RCCS_FUNC | RCCS_GO) + +/* Disk error status register (RCER) */ + +#define RCER_DLT 0100000 /* data late */ +#define RCER_CHK 0040000 /* block check */ +#define RCER_SYNC 0020000 /* data sync */ +#define RCER_NXM 0010000 /* nonexistant memory */ +#define RCER_TRK 0001000 /* track error */ +#define RCER_APAR 0000200 /* address parity */ +#define RCER_SADDR 0000100 /* sync address */ +#define RCER_OVFL 0000040 /* disk overflow */ +#define RCER_MIS 0000020 /* missed transfer */ + +/* Lood Ahead Register (RCLA) */ + +#define RCLA_BADD 0100000 /* bad address */ + +/* extract device operation code */ +#define GET_FUNC(x) (((x) >> RCCS_V_FUNC) & RCCS_M_FUNC) +/* extract memory extension address (bits 17,18) */ +#define GET_MEX(x) (((x) & RCCS_MEX) << (16 - RCCS_V_MEX)) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) RC_NUMWD))) + +extern int32 int_req[IPL_HLVL]; +extern FILE *sim_deb; +extern int32 R[]; + +static uint32 rc_la = 0; /* look-ahead */ +static uint32 rc_da = 0; /* disk address */ +static uint32 rc_er = 0; /* error status */ +static uint32 rc_cs = 0; /* command and status */ +static uint32 rc_wc = 0; /* word count */ +static uint32 rc_ca = 0; /* current address */ +static uint32 rc_maint = 0; /* maintenance */ +static uint32 rc_db = 0; /* data buffer */ +static uint32 rc_wlk = 0; /* write lock */ +static uint32 rc_time = 16; /* inter-word time: 16us */ +static uint32 rc_stopioe = 1; /* stop on error */ + +/* forward references */ + +DEVICE rc_dev; +static t_stat rc_rd (int32 *, int32, int32); +static t_stat rc_wr (int32, int32, int32); +static t_stat rc_svc (UNIT *); +static t_stat rc_reset (DEVICE *); +static t_stat rc_attach (UNIT *, char *); +static t_stat rc_set_size (UNIT *, int32, char *, void *); +static uint32 update_rccs (uint32, uint32); + +/* RC11 data structures + + rc_dev RC device descriptor + rc_unit RC unit descriptor + rc_reg RC register list +*/ + +static DIB rc_dib = { + IOBA_RC, + IOLN_RC, + &rc_rd, + &rc_wr, + 1, IVCL (RC), VEC_RC, { NULL } +}; + +static UNIT rc_unit = { + UDATA (&rc_svc, UNIT_FIX + UNIT_ATTABLE + UNIT_BUFABLE + + UNIT_MUSTBUF + UNIT_ROABLE + UNIT_BINK, RC_DKSIZE) +}; + +static const REG rc_reg[] = { + { ORDATA (RCLA, rc_la, 16) }, + { ORDATA (RCDA, rc_da, 16) }, + { ORDATA (RCER, rc_er, 16) }, + { ORDATA (RCCS, rc_cs, 16) }, + { ORDATA (RCWC, rc_wc, 16) }, + { ORDATA (RCCA, rc_ca, 16) }, + { ORDATA (RCMN, rc_maint, 16) }, + { ORDATA (RCDB, rc_db, 16) }, + { ORDATA (RCWLK, rc_wlk, 32) }, + { FLDATA (INT, IREQ (RC), INT_V_RC) }, + { FLDATA (ERR, rc_cs, CSR_V_ERR) }, + { FLDATA (DONE, rc_cs, CSR_V_DONE) }, + { FLDATA (IE, rc_cs, CSR_V_IE) }, + { DRDATA (TIME, rc_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, rc_stopioe, 0) }, + { ORDATA (DEVADDR, rc_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, rc_dib.vec, 16), REG_HRO }, + { NULL } +}; + +static const MTAB rc_mod[] = { + { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rc_set_size }, + { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rc_set_size }, + { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rc_set_size }, + { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rc_set_size }, + { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } +}; + +DEVICE rc_dev = { + "RC", &rc_unit, (REG *) rc_reg, (MTAB *) rc_mod, + 1, 8, 21, 1, 8, 16, + NULL, /* examine */ + NULL, /* deposit */ + &rc_reset, /* reset */ + NULL, /* boot */ + &rc_attach, /* attach */ + NULL, /* detach */ + &rc_dib, + DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG +}; + +/* I/O dispatch routine, I/O addresses 17777440 - 17777456 */ + +static t_stat rc_rd (int32 *data, int32 PA, int32 access) +{ + uint32 t; + + switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* RCLA */ + t = rc_la & 017777; + if ((rc_cs & RCCS_NED) || (rc_er & RCER_OVFL)) + t |= RCLA_BADD; + *data = t; + /* simulate sequential rotation about the current track */ + rc_la = (rc_la & ~077) | ((rc_la + 1) & 077); + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCLA %06o\n", rc_la); + break; + + case 1: /* RCDA */ + *data = rc_da; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCDA %06o, PC %06o\n", + rc_da, PC); + break; + + case 2: /* RCER */ + *data = rc_er; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCER %06o\n", rc_er); + break; + + case 3: /* RCCS */ + *data = update_rccs (0, 0) & ~(RCCS_ABO | RCCS_GO); + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCCS %06o\n", *data); + break; + + case 4: /* RCWC */ + *data = rc_wc; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCWC %06o\n", rc_wc); + break; + + case 5: /* RCCA */ + *data = rc_ca; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCCA %06o\n", rc_ca); + break; + + case 6: /* RCMN */ + *data = rc_maint; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCMN %06o\n", rc_maint); + break; + + case 7: /* RCDB */ + *data = rc_db; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC rd: RCDB %06o\n", rc_db); + break; + + default: + return (SCPE_NXM); /* can't happen */ + } /* end switch */ + return (SCPE_OK); +} + +static t_stat rc_wr (int32 data, int32 PA, int32 access) +{ + int32 t; + + switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* RCLA */ + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC wr: RCLA\n"); + break; /* read only */ + + case 1: /* RCDA */ + if (access == WRITEB) + data = (PA & 1) ? + (rc_da & 0377) | (data << 8) : + (rc_da & ~0377) | data; + rc_da = data & 017777; + rc_cs &= ~RCCS_NED; + update_rccs (0, 0); + /* perform unit select */ + if (((rc_da >> 11) & 03) >= UNIT_GETP(rc_unit.flags)) + update_rccs (RCCS_NED, 0); + else + rc_la = rc_da; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC wr: RCDA %06o, PC %06o\n", + rc_da, PC); + break; + + case 2: /* RCER */ + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC wr: RCER\n"); + break; /* read only */ + + case 3: /* RCCS */ + if (access == WRITEB) + data = (PA & 1) ? + (rc_cs & 0377) | (data << 8) : + (rc_cs & ~0377) | data; + if (data & RCCS_ABO) { + update_rccs (RCCS_DONE, 0); + sim_cancel (&rc_unit); + } + if ((data & RCCS_IE) == 0) /* int disable? */ + CLR_INT (RC); /* clr int request */ + else if ((rc_cs & (RCCS_DONE | RCCS_IE)) == RCCS_DONE) + SET_INT (RC); /* set int request */ + rc_cs = (rc_cs & ~RCCS_W) | (data & RCCS_W); /* merge */ + if ((rc_cs & RCCS_DONE) && (data & RCCS_GO)) { /* new function? */ + rc_unit.FUNC = GET_FUNC (data); /* save function */ + t = (rc_da & RC_WMASK) - GET_POS (rc_time); /* delta to new loc */ + if (t <= 0) /* wrap around? */ + t = t + RC_NUMWD; + sim_activate (&rc_unit, t * rc_time); /* schedule op */ + /* clear error indicators for new operation */ + rc_cs &= ~(RCCS_ALLERR | RCCS_ERR | RCCS_DONE); + rc_er = 0; + CLR_INT (RC); + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC start: cs = %o, da = %o, ma = %o, wc = %o\n", + update_rccs (0, 0), rc_da, + GET_MEX (rc_cs) | rc_ca, rc_wc); + } + break; + + case 4: /* RCWC */ + if (access == WRITEB) + data = (PA & 1) ? + (rc_wc & 0377) | (data << 8) : + (rc_wc & ~0377) | data; + rc_wc = data & DMASK; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC wr: RCWC %06o, PC %06o\n", + rc_wc, PC); + break; + + case 5: /* RCCA */ + /* TBD: write byte fixup? */ + rc_ca = data & 0177776; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC wr: RCCA %06o\n", rc_ca); + break; + + case 6: /* RCMN */ + /* TBD: write byte fixup? */ + rc_maint = data & 0177700; + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC wr: RCMN %06o\n", rc_maint); + break; + + case 7: /* RCDB */ + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC wr: RCDB\n"); + break; /* read only */ + + default: /* can't happen */ + return (SCPE_NXM); + } /* end switch */ + update_rccs (0, 0); + return (SCPE_OK); +} + +/* sector (32W) CRC-16 */ + +static uint32 sectorCRC (const uint16 *data) +{ + uint32 crc, i, j, d; + + crc = 0; + for (i = 0; i < 32; i++) { + d = *data++; + /* cribbed from KG11-A */ + for (j = 0; j < 16; j++) { + crc = (crc & ~01) | ((crc & 01) ^ (d & 01)); + crc = (crc & 01) ? (crc >> 1) ^ 0120001 : crc >> 1; + d >>= 1; + } + } + return (crc); +} + +/* Unit service + + Note that for reads and writes, memory addresses wrap around in the + current field. This code assumes the entire disk is buffered. +*/ + +static t_stat rc_svc (UNIT *uptr) +{ + uint32 ma, da, t, u_old, u_new, last_da; + uint16 dat; + uint16 *fbuf = uptr->filebuf; + + if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + update_rccs (RCCS_NED | RCCS_DONE, 0); /* nx disk */ + return (IORETURN (rc_stopioe, SCPE_UNATT)); + } + + ma = GET_MEX (rc_cs) | rc_ca; /* 18b mem addr */ + da = rc_da * RC_NUMTR; /* sector->word offset */ + u_old = (da >> 16) & 03; /* save starting unit# */ + do { + u_new = (da >> 16) & 03; + if (u_new < u_old) { /* unit # overflow? */ + update_rccs (RCCS_NED, RCER_OVFL); + break; + } + if (u_new >= UNIT_GETP(uptr->flags)) { /* disk overflow? */ + update_rccs (RCCS_NED, 0); + break; + } + if (uptr->FUNC == RFNC_READ) { /* read? */ + last_da = da & ~037; + dat = fbuf[da]; /* get disk data */ + rc_db = dat; + if (Map_WriteW (ma, 2, &dat)) { /* store mem, nxm? */ + update_rccs (0, RCER_NXM); + break; + } + } else if (uptr->FUNC == RFNC_WCHK) { /* write check? */ + last_da = da & ~037; + rc_db = fbuf[da]; /* get disk data */ + if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ + update_rccs (0, RCER_NXM); + break; + } + if (rc_db != dat) { /* miscompare? */ + update_rccs (RCCS_WCHK, 0); + break; + } + } else if (uptr->FUNC == RFNC_WRITE) { /* write */ + t = (da >> 15) & 037; + if (((rc_wlk >> t) & 1) || + (uptr->flags & UNIT_RO)) { /* write locked? */ + update_rccs (RCCS_WLK, 0); + break; + } + /* not locked */ + if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ + update_rccs (0, RCER_NXM); + break; + } + fbuf[da] = dat; /* write word */ + rc_db = dat; + if (da >= uptr->hwmark) + uptr->hwmark = da + 1; + } else { /* look ahead */ + break; /* no op for now */ + } + rc_wc = (rc_wc + 1) & DMASK; /* incr word count */ + da = (da + 1) & 0777777; /* incr disk addr */ + if ((rc_cs & RCCS_INH) == 0) /* inhibit clear? */ + ma = (ma + 2) & UNIMASK; /* incr mem addr */ + } while (rc_wc != 0); /* brk if wc */ + rc_ca = ma & DMASK; /* split ma */ + rc_cs = (rc_cs & ~RCCS_MEX) | ((ma >> (16 - RCCS_V_MEX)) & RCCS_MEX); + da += 31; + rc_da = (da >> 5) & 017777; + /* CRC of last 32W, if necessary */ + if ((uptr->FUNC == RFNC_READ) || (uptr->FUNC == RFNC_WCHK)) + rc_db = sectorCRC (&fbuf[last_da]); + if (uptr->FUNC != RFNC_LAH) + rc_la = rc_da; + update_rccs (RCCS_DONE, 0); + if (DEBUG_PRS (rc_dev)) + fprintf (sim_deb, ">>RC done: cs = %o, da = %o, ma = %o, wc = %o\n", + rc_cs, rc_da, rc_ca, rc_wc); + return (SCPE_OK); +} + +/* Update CS register */ + +static uint32 update_rccs (uint32 newcs, uint32 newer) +{ + uint32 oldcs = rc_cs; + + rc_er |= newer; /* update RCER */ + rc_cs |= newcs; /* update CS */ + if ((rc_cs & RCCS_ALLERR) || (rc_er != 0)) /* update CS */ + rc_cs |= RCCS_ERR; + else + rc_cs &= ~RCCS_ERR; + if ((rc_cs & RCCS_IE) && /* IE and */ + (rc_cs & RCCS_DONE) && !(oldcs & RCCS_DONE)) /* done 0->1? */ + SET_INT (RC); + return (rc_cs); +} + +/* Reset routine */ + +static t_stat rc_reset (DEVICE *dptr) +{ + rc_cs = RCCS_DONE; + rc_la = rc_da = 0; + rc_er = 0; + rc_wc = 0; + rc_ca = 0; + rc_maint = 0; + rc_db = 0; + CLR_INT (RC); + sim_cancel (&rc_unit); + return (SCPE_OK); +} + +/* Attach routine */ + +static t_stat rc_attach (UNIT *uptr, char *cptr) +{ + uint32 sz, p; + static const uint32 ds_bytes = RC_DKSIZE * sizeof (int16); + + if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= RC_NUMDK) + p = RC_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | (p << UNIT_V_PLAT); + } + uptr->capac = UNIT_GETP (uptr->flags) * RC_DKSIZE; + return (attach_unit (uptr, cptr)); +} + +/* Change disk size */ + +static t_stat rc_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ + if (val < 0) + return (SCPE_IERR); + if (uptr->flags & UNIT_ATT) + return (SCPE_ALATT); + uptr->capac = UNIT_GETP (val) * RC_DKSIZE; + uptr->flags = uptr->flags & ~UNIT_AUTO; + return (SCPE_OK); +} diff --git a/PDP11/pdp11_rf.c b/PDP11/pdp11_rf.c new file mode 100644 index 0000000..246957d --- /dev/null +++ b/PDP11/pdp11_rf.c @@ -0,0 +1,495 @@ +/* pdp11_rf.c: RF11 fixed head disk simulator + + Copyright (c) 2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rf RF11 fixed head disk + + 25-Dec-06 RMS Fixed bug in unit mask (found by John Dundas) + 26-Jun-06 RMS Cloned from RF08 simulator + + The RF11 is a head-per-track disk. To minimize overhead, the entire RF11 + is buffered in memory. + + Two timing parameters are provided: + + rf_time Interword timing, must be non-zero + rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst +*/ + +#include "pdp11_defs.h" +#include + +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ +#define UNIT_M_PLAT (RF_NUMDK - 1) +#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) + +/* Constants */ + +#define RF_NUMWD 2048 /* words/track */ +#define RF_NUMTR 128 /* tracks/disk */ +#define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */ +#define RF_NUMDK 8 /* disks/controller */ +#define RF_WMASK (RF_NUMWD - 1) /* word mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ + +/* Status register */ + +#define RFCS_ERR (CSR_ERR) /* error */ +#define RFCS_FRZ 0040000 /* error freeze */ +#define RFCS_WCHK 0020000 /* write check */ +#define RFCS_DPAR 0010000 /* data parity (ni) */ +#define RFCS_NED 0004000 /* nx disk */ +#define RFCS_WLK 0002000 /* write lock */ +#define RFCS_MXFR 0001000 /* missed xfer (ni) */ +#define RFCS_CLR 0000400 /* clear */ +#define RFCS_DONE (CSR_DONE) +#define RFCS_IE (CSR_IE) +#define RFCS_M_MEX 0000003 /* memory extension */ +#define RFCS_V_MEX 4 +#define RFCS_MEX (RFCS_M_MEX << RFCS_V_MEX) +#define RFCS_MAINT 0000010 /* maint */ +#define RFCS_M_FUNC 0000003 /* function */ +#define RFNC_NOP 0 +#define RFNC_WRITE 1 +#define RFNC_READ 2 +#define RFNC_WCHK 3 +#define RFCS_V_FUNC 1 +#define RFCS_FUNC (RFCS_M_FUNC << RFCS_V_FUNC) +#define RFCS_GO 0000001 +#define RFCS_ALLERR (RFCS_FRZ|RFCS_WCHK|RFCS_DPAR|RFCS_NED|RFCS_WLK|RFCS_MXFR) +#define RFCS_W (RFCS_IE|RFCS_MEX|RFCS_FUNC) + +/* Current memory address */ + +#define RFCMA_RW 0177776 + +/* Address extension */ + +#define RFDAE_ALLERR 0176000 +#define RFDAE_NXM 0002000 +#define RFDAE_INH 0000400 /* addr inhibit */ +#define RFDAE_RLAT 0000200 /* req late */ +#define RFDAE_DAE 0000077 /* extension */ +#define RFDAE_R 0176677 +#define RFDAE_W 0000677 + +#define GET_FUNC(x) (((x) >> RFCS_V_FUNC) & RFCS_M_FUNC) +#define GET_MEX(x) (((x) & RFCS_MEX) << (16 - RFCS_V_MEX)) +#define GET_DEX(x) (((x) & RFDAE_DAE) << 16) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) RF_NUMWD))) + +extern uint16 *M; +extern int32 int_req[IPL_HLVL]; +extern FILE *sim_deb; + +uint32 rf_cs = 0; /* status register */ +uint32 rf_cma = 0; +uint32 rf_wc = 0; +uint32 rf_da = 0; /* disk address */ +uint32 rf_dae = 0; +uint32 rf_dbr = 0; +uint32 rf_maint = 0; +uint32 rf_wlk = 0; /* write lock */ +uint32 rf_time = 10; /* inter-word time */ +uint32 rf_burst = 1; /* burst mode flag */ +uint32 rf_stopioe = 1; /* stop on error */ + +DEVICE rf_dev; +t_stat rf_rd (int32 *data, int32 PA, int32 access); +t_stat rf_wr (int32 data, int32 PA, int32 access); +int32 rf_inta (void); +t_stat rf_svc (UNIT *uptr); +t_stat rf_reset (DEVICE *dptr); +t_stat rf_boot (int32 unitno, DEVICE *dptr); +t_stat rf_attach (UNIT *uptr, char *cptr); +t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +uint32 update_rfcs (uint32 newcs, uint32 newdae); + +/* RF11 data structures + + rf_dev RF device descriptor + rf_unit RF unit descriptor + rf_reg RF register list +*/ + +DIB rf_dib = { + IOBA_RF, IOLN_RF, &rf_rd, &rf_wr, + 1, IVCL (RF), VEC_RF, NULL + }; + + +UNIT rf_unit = { + UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+ + UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE) + }; + +REG rf_reg[] = { + { ORDATA (RFCS, rf_cs, 16) }, + { ORDATA (RFWC, rf_wc, 16) }, + { ORDATA (RFCMA, rf_cma, 16) }, + { ORDATA (RFDA, rf_da, 16) }, + { ORDATA (RFDAE, rf_dae, 16) }, + { ORDATA (RFDBR, rf_dbr, 16) }, + { ORDATA (RFMR, rf_maint, 16) }, + { ORDATA (RFWLK, rf_wlk, 32) }, + { FLDATA (INT, IREQ (RF), INT_V_RF) }, + { FLDATA (ERR, rf_cs, CSR_V_ERR) }, + { FLDATA (DONE, rf_cs, CSR_V_DONE) }, + { FLDATA (IE, rf_cs, CSR_V_IE) }, + { DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (BURST, rf_burst, 0) }, + { FLDATA (STOP_IOE, rf_stopioe, 0) }, + { ORDATA (DEVADDR, rf_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, rf_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB rf_mod[] = { + { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size }, + { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size }, + { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size }, + { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size }, + { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &rf_set_size }, + { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &rf_set_size }, + { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &rf_set_size }, + { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &rf_set_size }, + { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE rf_dev = { + "RF", &rf_unit, rf_reg, rf_mod, + 1, 8, 21, 1, 8, 16, + NULL, NULL, &rf_reset, + &rf_boot, &rf_attach, NULL, + &rf_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG + }; + +/* I/O dispatch routine, I/O addresses 17777460 - 17777476 */ + +t_stat rf_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* RFCS */ + *data = update_rfcs (0, 0); /* update RFCS */ + break; + + case 1: /* RFWC */ + *data = rf_wc; + break; + + case 2: /* RFCMA */ + *data = rf_cma & RFCMA_RW; + break; + + case 3: /* RFDA */ + *data = rf_da; + break; + + case 4: /* RFDAE */ + *data = rf_dae & RFDAE_R; + break; + + case 5: /* RFDBR */ + *data = rf_dbr; + break; + + case 6: /* RFMR */ + *data = rf_maint; + break; + + case 7: /* RFADS */ + *data = GET_POS (rf_time); + break; + } /* end switch */ +return SCPE_OK; +} + +t_stat rf_wr (int32 data, int32 PA, int32 access) +{ +int32 t, fnc; + +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* RFCS */ + if (access == WRITEB) data = (PA & 1)? + (rf_cs & 0377) | (data << 8): (rf_cs & ~0377) | data; + if (data & RFCS_CLR) rf_reset (&rf_dev); /* clear? */ + if ((data & RFCS_IE) == 0) /* int disable? */ + CLR_INT (RF); /* clr int request */ + else if ((rf_cs & (RFCS_DONE + RFCS_IE)) == RFCS_DONE) + SET_INT (RF); /* set int request */ + rf_cs = (rf_cs & ~RFCS_W) | (data & RFCS_W); /* merge */ + if ((rf_cs & RFCS_DONE) && (data & RFCS_GO) && /* new function? */ + ((fnc = GET_FUNC (rf_cs)) != RFNC_NOP)) { + rf_unit.FUNC = fnc; /* save function */ + t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */ + if (t < 0) t = t + RF_NUMWD; /* wrap around? */ + sim_activate (&rf_unit, t * rf_time); /* schedule op */ + rf_cs &= ~(RFCS_WCHK|RFCS_DPAR|RFCS_NED|RFCS_WLK|RFCS_MXFR|RFCS_DONE); + CLR_INT (RF); + if (DEBUG_PRS (rf_dev)) + fprintf (sim_deb, ">>RF start: cs = %o, da = %o, ma = %o\n", + update_rfcs (0, 0), GET_DEX (rf_dae) | rf_da, GET_MEX (rf_cs) | rf_cma); + } + break; + + case 1: /* RFWC */ + if (access == WRITEB) data = (PA & 1)? + (rf_wc & 0377) | (data << 8): (rf_wc & ~0377) | data; + rf_wc = data; + break; + + case 2: /* RFCMA */ + if (access == WRITEB) data = (PA & 1)? + (rf_cma & 0377) | (data << 8): (rf_cma & ~0377) | data; + rf_cma = data & RFCMA_RW; + break; + + case 3: /* RFDA */ + if (access == WRITEB) data = (PA & 1)? + (rf_da & 0377) | (data << 8): (rf_da & ~0377) | data; + rf_da = data; + break; + + case 4: /* RFDAE */ + if (access == WRITEB) data = (PA & 1)? + (rf_dae & 0377) | (data << 8): (rf_dae & ~0377) | data; + rf_dae = (rf_dae & ~RFDAE_W) | (data & RFDAE_W); + break; + + case 5: /* RFDBR */ + rf_dbr = data; + break; + + case 6: /* RFMR */ + rf_maint = data; + break; + + case 7: /* RFADS */ + break; /* read only */ + } /* end switch */ + +update_rfcs (0, 0); +return SCPE_OK; +} + +/* Unit service + + Note that for reads and writes, memory addresses wrap around in the + current field. This code assumes the entire disk is buffered. +*/ + +t_stat rf_svc (UNIT *uptr) +{ +uint32 ma, da, t; +uint16 dat; +uint16 *fbuf = uptr->filebuf; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + update_rfcs (RFCS_NED|RFCS_DONE, 0); /* nx disk */ + return IORETURN (rf_stopioe, SCPE_UNATT); + } + +ma = GET_MEX (rf_cs) | rf_cma; /* 18b mem addr */ +da = GET_DEX (rf_dae) | rf_da; /* 22b disk addr */ +do { + if (da >= rf_unit.capac) { /* disk overflow? */ + update_rfcs (RFCS_NED, 0); + break; + } + if (uptr->FUNC == RFNC_READ) { /* read? */ + dat = fbuf[da]; /* get disk data */ + rf_dbr = dat; + if (Map_WriteW (ma, 2, &dat)) { /* store mem, nxm? */ + update_rfcs (0, RFDAE_NXM); + break; + } + } + else if (uptr->FUNC == RFNC_WCHK) { /* write check? */ + rf_dbr = fbuf[da]; /* get disk data */ + if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ + update_rfcs (0, RFDAE_NXM); + break; + } + if (rf_dbr != dat) { /* miscompare? */ + update_rfcs (RFCS_WCHK, 0); + break; + } + } + else { /* write */ + t = (da >> 15) & 037; + if ((rf_wlk >> t) & 1) { /* write locked? */ + update_rfcs (RFCS_WLK, 0); + break; + } + else { /* not locked */ + if (Map_ReadW (ma, 2, &dat)) { /* read mem, nxm? */ + update_rfcs (0, RFDAE_NXM); + break; + } + fbuf[da] = dat; /* write word */ + rf_dbr = dat; + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + } + } + da = (da + 1) & 017777777; /* incr disk addr */ + if ((rf_dae & RFDAE_INH) == 0) /* inhibit clear? */ + ma = (ma + 2) & UNIMASK; /* incr mem addr */ + rf_wc = (rf_wc + 1) & DMASK; /* incr word count */ + } while ((rf_wc != 0) && (rf_burst != 0)); /* brk if wc, no brst */ + +rf_da = da & DMASK; /* split da */ +rf_dae = (rf_dae & ~RFDAE_DAE) | ((rf_da >> 16) && RFDAE_DAE); +rf_cma = ma & DMASK; /* split ma */ +rf_cs = (rf_cs & ~RFCS_MEX) | ((ma >> (16 - RFCS_V_MEX)) & RFCS_MEX); +if ((rf_wc != 0) && ((rf_cs & RFCS_ERR) == 0)) /* more to do? */ + sim_activate (&rf_unit, rf_time); /* sched next */ +else { + update_rfcs (RFCS_DONE, 0); + if (DEBUG_PRS (rf_dev)) + fprintf (sim_deb, ">>RF done: cs = %o, dae = %o, da = %o, ma = %o, wc = %o\n", + rf_cs, rf_dae, rf_da, rf_cma, rf_wc); + } +return SCPE_OK; +} + +/* Update CS register */ + +uint32 update_rfcs (uint32 newcs, uint32 newdae) +{ +uint32 oldcs = rf_cs; +uint32 da = GET_DEX (rf_dae) | rf_da; + +rf_dae |= newdae; /* update DAE */ +rf_cs |= newcs; /* update CS */ +if (da >= rf_unit.capac) /* update CS */ + rf_cs |= RFCS_NED; +else rf_cs &= ~RFCS_NED; +if (rf_dae & RFDAE_ALLERR) /* update CS */ + rf_cs |= RFCS_FRZ; +else rf_cs &= ~RFCS_FRZ; +if (rf_cs & RFCS_ALLERR) /* update CS */ + rf_cs |= RFCS_ERR; +else rf_cs &= ~RFCS_ERR; +if ((rf_cs & RFCS_IE) && /* IE and */ + (rf_cs & RFCS_DONE) &&!(oldcs & RFCS_DONE)) /* done 0->1? */ + SET_INT (RF); +return rf_cs; +} + +/* Reset routine */ + +t_stat rf_reset (DEVICE *dptr) +{ +rf_cs = RFCS_DONE; +rf_da = rf_dae = 0; +rf_dbr = 0; +rf_cma = 0; +rf_wc = 0; +rf_maint = 0; +CLR_INT (RF); +sim_cancel (&rf_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +/* Device bootstrap */ + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_CSR (BOOT_START + 032) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16)) + +static const uint16 boot_rom[] = { + 0043113, /* "FD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012701, 0177472, /* MOV #RFDAE+2, R1 ; csr block */ + 0005041, /* CLR -(R1) ; clear dae */ + 0005041, /* CLR -(R1), ; clear da */ + 0005041, /* CLR -(R1), ; clear cma */ + 0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */ + 0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0105711, /* TSTB (R1) */ + 0100376, /* BPL .-2 */ + 0105011, /* CLRB (R1) */ + 0005007 /* CLR PC */ + }; + +t_stat rf_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_CSR >> 1] = (rf_dib.ba & DMASK) + 012; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rf_attach (UNIT *uptr, char *cptr) +{ +uint32 sz, p; +uint32 ds_bytes = RF_DKSIZE * sizeof (int16); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= RF_NUMDK) p = RF_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | + (p << UNIT_V_PLAT); + } +uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE; +return attach_unit (uptr, cptr); +} + +/* Change disk size */ + +t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val < 0) return SCPE_IERR; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = UNIT_GETP (val) * RF_DKSIZE; +uptr->flags = uptr->flags & ~UNIT_AUTO; +return SCPE_OK; +} diff --git a/PDP11/pdp11_rh.c b/PDP11/pdp11_rh.c new file mode 100644 index 0000000..f5413fb --- /dev/null +++ b/PDP11/pdp11_rh.c @@ -0,0 +1,801 @@ +/* pdp11_rh.c: PDP-11 Massbus adapter simulator + + Copyright (c) 2005-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rha, rhb RH11/RH70 Massbus adapter + + 02-Feb-08 RMS Fixed DMA memory address limit test (found by John Dundas) + 17-May-07 RMS Moved CS1 drive enable to devices + 21-Nov-05 RMS Added enable/disable routine + 07-Jul-05 RMS Removed extraneous externs + + WARNING: The interupt logic of the RH11/RH70 is unusual and must be + simulated with great precision. The RH11 has an internal interrupt + request flop, CSTB INTR, which is controlled as follows: + + - Writing IE and DONE simultaneously sets CSTB INTR + - Controller clear, INIT, and interrupt acknowledge clear CSTB INTR + (and also clear IE) + - A transition of DONE from 0 to 1 sets CSTB INTR from IE + + The output of CSTB INTR is OR'd with the AND of RPCS1 to + create the interrupt request signal. Thus, + + - The DONE interrupt is edge sensitive, but the SC interrupt is + level sensitive. + - The DONE interrupt, once set, is not disabled if IE is cleared, + but the SC interrupt is. +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "PDP-10 uses pdp10_rp.c and pdp10_tu.c!" + +#elif defined (VM_VAX) /* VAX version */ +#error "VAX uses vax780_mba.c!" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif + +/* CS1 - base + 000 - control/status 1 */ + +#define CS1_OF 0 +#define CS1_GO CSR_GO /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 037 /* function mask */ +#define CS1_FNC (CS1_M_FNC << CS1_V_FNC) +#define FNC_XFER 024 /* >=? data xfr */ +#define CS1_IE CSR_IE /* int enable */ +#define CS1_DONE CSR_DONE /* ready */ +#define CS1_V_UAE 8 /* Unibus addr ext */ +#define CS1_M_UAE 03 +#define CS1_UAE (CS1_M_UAE << CS1_V_UAE) +#define CS1_MCPE 0020000 /* Mbus par err NI */ +#define CS1_TRE 0040000 /* transfer err */ +#define CS1_SC 0100000 /* special cond */ +#define CS1_MBZ 0012000 +#define CS1_DRV (CS1_FNC | CS1_GO) +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) + +/* WC - base + 002 - word count */ + +#define WC_OF 1 + +/* BA - base + 004 - base address */ + +#define BA_OF 2 +#define BA_MBZ 0000001 /* must be zero */ + +/* CS2 - base + 010 - control/status 2 */ + +#define CS2_OF 3 +#define CS2_V_UNIT 0 /* unit pos */ +#define CS2_M_UNIT 07 /* unit mask */ +#define CS2_UNIT (CS2_M_UNIT << CS2_V_UNIT) +#define CS2_UAI 0000010 /* addr inhibit */ +#define CS2_PAT 0000020 /* parity test NI */ +#define CS2_CLR 0000040 /* controller clear */ +#define CS2_IR 0000100 /* input ready */ +#define CS2_OR 0000200 /* output ready */ +#define CS2_MDPE 0000400 /* Mbus par err NI */ +#define CS2_MXF 0001000 /* missed xfer NI */ +#define CS2_PGE 0002000 /* program err */ +#define CS2_NEM 0004000 /* nx mem err */ +#define CS2_NED 0010000 /* nx drive err */ +#define CS2_PE 0020000 /* parity err NI */ +#define CS2_WCE 0040000 /* write check err */ +#define CS2_DLT 0100000 /* data late NI */ +#define CS2_MBZ (CS2_CLR) +#define CS2_RW (CS2_UNIT | CS2_UAI | CS2_PAT | CS2_MXF | CS2_PE) +#define CS2_ERR (CS2_MDPE | CS2_MXF | CS2_PGE | CS2_NEM | \ + CS2_NED | CS2_PE | CS2_WCE | CS2_DLT) +#define GET_UNIT(x) (((x) >> CS2_V_UNIT) & CS2_M_UNIT) + +/* DB - base + 022 - data buffer */ + +#define DB_OF 4 + +/* BAE - base + 050/34 - bus address extension */ + +#define BAE_OF 5 +#define AE_M_MAE 0 /* addr ext pos */ +#define AE_V_MAE 077 /* addr ext mask */ +#define AE_MBZ 0177700 + +/* CS3 - base + 052/36 - control/status 3 */ + +#define CS3_OF 6 +#define CS3_APE 0100000 /* addr perr - NI */ +#define CS3_DPO 0040000 /* data perr odd - NI */ +#define CS3_DPE 0020000 /* data perr even - NI */ +#define CS3_WCO 0010000 /* wchk err odd */ +#define CS3_WCE 0004000 /* wchk err even */ +#define CS3_DBL 0002000 /* dbl word xfer - NI */ +#define CS3_IPCK 0000017 /* wrong par - NI */ +#define CS3_ERR (CS3_APE|CS3_DPO|CS3_DPE|CS3_WCO|CS3_WCE) +#define CS3_MBZ 0001660 +#define CS3_RW (CS1_IE | CS3_IPCK) + +#define MBA_OFSMASK 077 /* max 32 reg */ +#define INT 0000 /* int reg flag */ +#define EXT 0100 /* ext reg flag */ + +/* Declarations */ + +#define RH11 (cpu_opt & OPT_RH11) + +typedef struct { + uint32 cs1; /* ctrl/status 1 */ + uint32 wc; /* word count */ + uint32 ba; /* bus addr */ + uint32 cs2; /* ctrl/status 2 */ + uint32 db; /* data buffer */ + uint32 bae; /* addr ext */ + uint32 cs3; /* ctrl/status 3 */ + uint32 iff; /* int flip flop */ + } MBACTX; + +MBACTX massbus[MBA_NUM]; + +extern int32 cpu_opt, cpu_bme; +extern uint16 *M; +extern int32 int_req[IPL_HLVL]; +extern t_addr cpu_memsize; +extern FILE *sim_deb; +extern FILE *sim_log; +extern int32 sim_switches; + +t_stat mba_reset (DEVICE *dptr); +t_stat mba_rd (int32 *val, int32 pa, int32 access); +t_stat mba_wr (int32 val, int32 pa, int32 access); +t_stat mba_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat mba_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); +int32 mba0_inta (void); +int32 mba1_inta (void); +void mba_set_int (uint32 mb); +void mba_clr_int (uint32 mb); +void mba_upd_cs1 (uint32 set, uint32 clr, uint32 mb); +void mba_set_cs2 (uint32 flg, uint32 mb); +uint32 mba_map_pa (int32 pa, int32 *ofs); +DEVICE mba0_dev, mba1_dev; + +extern uint32 Map_Addr (uint32 ba); + +/* Massbus register dispatches */ + +static t_stat (*mbregR[MBA_NUM])(int32 *dat, int32 ad, int32 md); +static t_stat (*mbregW[MBA_NUM])(int32 dat, int32 ad, int32 md); +static int32 (*mbabort[MBA_NUM])(void); + +/* Unibus to register offset map */ + +static int32 mba_mapofs[(MBA_OFSMASK + 1) >> 1] = { + INT|0, INT|1, INT|2, EXT|5, INT|3, EXT|1, EXT|2, EXT|4, + EXT|7, INT|4, EXT|3, EXT|6, EXT|8, EXT|9, EXT|10, EXT|11, + EXT|12, EXT|13, EXT|14, EXT|15, EXT|16, EXT|17, EXT|18, EXT|19, + EXT|20, EXT|21, EXT|22, EXT|23, EXT|24, EXT|25, EXT|26, EXT|27 + }; + +/* Massbus adapter data structures + + mbax_dev RHx device descriptor + mbax_unit RHx units + mbax_reg RHx register list +*/ + +DIB mba0_dib = { + IOBA_RP, IOLN_RP, &mba_rd, &mba_wr, + 1, IVCL (RP), VEC_RP, { &mba0_inta } + }; + +UNIT mba0_unit = { UDATA (NULL, 0, 0) }; + +REG mba0_reg[] = { + { ORDATA (CS1, massbus[0].cs1, 16) }, + { ORDATA (WC, massbus[0].wc, 16) }, + { ORDATA (BA, massbus[0].ba, 16) }, + { ORDATA (CS2, massbus[0].cs2, 16) }, + { ORDATA (DB, massbus[0].db, 16) }, + { ORDATA (BAE, massbus[0].bae, 6) }, + { ORDATA (CS3, massbus[0].cs3, 16) }, + { FLDATA (IFF, massbus[0].iff, 0) }, + { FLDATA (INT, IREQ (RP), INT_V_RP) }, + { FLDATA (SC, massbus[0].cs1, CSR_V_ERR) }, + { FLDATA (DONE, massbus[0].cs1, CSR_V_DONE) }, + { FLDATA (IE, massbus[0].cs1, CSR_V_IE) }, + { ORDATA (DEVADDR, mba0_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, mba0_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB mba0_mod[] = { + { MTAB_XTD|MTAB_VDV, 0100, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DIB mba1_dib = { + IOBA_TU, IOLN_TU, &mba_rd, &mba_wr, + 1, IVCL (TU), VEC_TU, { &mba1_inta } + }; + +UNIT mba1_unit = { UDATA (NULL, 0, 0) }; + +REG mba1_reg[] = { + { ORDATA (CS1, massbus[1].cs1, 16) }, + { ORDATA (WC, massbus[1].wc, 16) }, + { ORDATA (BA, massbus[1].ba, 16) }, + { ORDATA (CS2, massbus[1].cs2, 16) }, + { ORDATA (DB, massbus[1].db, 16) }, + { ORDATA (BAE, massbus[1].bae, 6) }, + { ORDATA (CS3, massbus[1].cs3, 16) }, + { FLDATA (IFF, massbus[1].iff, 0) }, + { FLDATA (INT, IREQ (TU), INT_V_TU) }, + { FLDATA (SC, massbus[1].cs1, CSR_V_ERR) }, + { FLDATA (DONE, massbus[1].cs1, CSR_V_DONE) }, + { FLDATA (IE, massbus[1].cs1, CSR_V_IE) }, + { ORDATA (DEVADDR, mba1_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, mba1_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB mba1_mod[] = { + { MTAB_XTD|MTAB_VDV, 0040, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE mba_dev[] = { + { + "RHA", &mba0_unit, mba0_reg, mba0_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &mba_reset, + NULL, NULL, NULL, + &mba0_dib, DEV_DEBUG | DEV_DISABLE | DEV_UBUS | DEV_QBUS + }, + { + "RHB", &mba1_unit, mba1_reg, mba1_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &mba_reset, + NULL, NULL, NULL, + &mba1_dib, DEV_DEBUG | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS + } + }; + +/* Read Massbus adapter register */ + +t_stat mba_rd (int32 *val, int32 pa, int32 mode) +{ +int32 ofs, dat, mb, drv; +t_stat r; + +mb = mba_map_pa (pa, &ofs); /* get mb number */ +if ((mb < 0) || (ofs < 0)) return SCPE_NXM; /* valid? */ +drv = GET_UNIT (massbus[mb].cs2); /* get drive */ +mba_upd_cs1 (0, 0, mb); /* update CS1 */ + +if (ofs & EXT) { /* external? */ + if (!mbregR[mb]) return SCPE_NXM; /* device there? */ + r = mbregR[mb] (val, ofs & ~EXT, drv); /* call device */ + if (r == MBE_NXD) mba_set_cs2 (CS2_NED, mb); /* nx drive? */ + else if (r == MBE_NXR) return SCPE_NXM; /* nx reg? */ + return SCPE_OK; + } + +switch (ofs) { /* case on reg */ + + case CS1_OF: /* CS1 */ + if (!mbregR[mb]) return SCPE_NXM; /* nx device? */ + r = mbregR[mb] (&dat, ofs, drv); /* get dev cs1 */ + if (r == MBE_NXD) mba_set_cs2 (CS2_NED, mb); /* nx drive? */ + *val = massbus[mb].cs1 | dat; + break; + + case WC_OF: /* WC */ + *val = massbus[mb].wc; + break; + + case BA_OF: /* BA */ + *val = massbus[mb].ba & ~BA_MBZ; + break; + + case CS2_OF: /* CS2 */ + *val = massbus[mb].cs2 = (massbus[mb].cs2 & ~CS2_MBZ) | CS2_IR | CS2_OR; + break; + + case DB_OF: /* DB */ + *val = massbus[mb].db; + break; + + case BAE_OF: /* BAE */ + *val = massbus[mb].bae = massbus[mb].bae & ~AE_MBZ; + break; + + case CS3_OF: /* CS3 */ + *val = massbus[mb].cs3 = (massbus[mb].cs3 & ~(CS1_IE | CS3_MBZ)) | + (massbus[mb].cs1 & CS1_IE); + break; + + default: /* huh? */ + return SCPE_NXM; + } + +return SCPE_OK; +} + +t_stat mba_wr (int32 val, int32 pa, int32 access) +{ +int32 ofs, cs1f, drv, mb; +t_stat r; +t_bool cs1dt; + +mb = mba_map_pa (pa, &ofs); /* get mb number */ +if ((mb < 0) || (ofs < 0)) return SCPE_NXM; /* valid? */ +drv = GET_UNIT (massbus[mb].cs2); /* get drive */ + +if (ofs & EXT) { /* external? */ + if (!mbregW[mb]) return SCPE_NXM; /* device there? */ + if ((access == WRITEB) && (pa & 1)) /* byte writes */ + val = val << 8; /* don't work */ + r = mbregW[mb] (val, ofs & ~EXT, drv); /* write dev reg */ + if (r == MBE_NXD) mba_set_cs2 (CS2_NED, mb); /* nx drive? */ + else if (r == MBE_NXR) return SCPE_NXM; /* nx reg? */ + mba_upd_cs1 (0, 0, mb); /* update status */ + return SCPE_OK; + } + +cs1f = 0; /* no int on cs1 upd */ +switch (ofs) { /* case on reg */ + + case CS1_OF: /* CS1 */ + if (!mbregW[mb]) return SCPE_NXM; /* device exist? */ + if ((access == WRITEB) && (pa & 1)) val = val << 8; + if (val & CS1_TRE) { /* error clear? */ + massbus[mb].cs1 &= ~CS1_TRE; /* clr CS1 */ + massbus[mb].cs2 &= ~CS2_ERR; /* clr CS2<15:8> */ + massbus[mb].cs3 &= ~CS3_ERR; /* clr CS3<15:11> */ + } + if ((access == WRITE) || (pa & 1)) { /* hi byte write? */ + if (massbus[mb].cs1 & CS1_DONE) /* done set? */ + massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_UAE) | (val & CS1_UAE); + } + if ((access == WRITE) || !(pa & 1)) { /* lo byte write? */ + if ((val & CS1_DONE) && (val & CS1_IE)) /* to DONE+IE? */ + massbus[mb].iff = 1; /* set CSTB INTR */ + massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_IE) | (val & CS1_IE); + cs1dt = (val & CS1_GO) && (GET_FNC (val) >= FNC_XFER); + if (cs1dt && ((massbus[mb].cs1 & CS1_DONE) == 0)) /* dt, done clr? */ + mba_set_cs2 (CS2_PGE, mb); /* prgm error */ + else { + r = mbregW[mb] (val & 077, ofs, drv); /* write dev CS1 */ + if (r == MBE_NXD) mba_set_cs2 (CS2_NED, mb); /* nx drive? */ + else if (r == MBE_NXR) return SCPE_NXM; /* nx reg? */ + else if (cs1dt && (r == SCPE_OK)) { /* xfer, no err? */ + massbus[mb].cs1 &= ~(CS1_TRE | CS1_MCPE | CS1_DONE); + massbus[mb].cs2 &= ~CS2_ERR; /* clear errors */ + massbus[mb].cs3 &= ~(CS3_ERR | CS3_DBL); + } + } + } + massbus[mb].cs3 = (massbus[mb].cs3 & ~CS1_IE) | /* update CS3 */ + (massbus[mb].cs1 & CS1_IE); + massbus[mb].bae = (massbus[mb].bae & ~CS1_M_UAE) | /* update BAE */ + ((massbus[mb].cs1 >> CS1_V_UAE) & CS1_M_UAE); + break; + + case WC_OF: /* WC */ + if (access == WRITEB) val = (pa & 1)? + (massbus[mb].wc & 0377) | (val << 8): + (massbus[mb].wc & ~0377) | val; + massbus[mb].wc = val; + break; + + case BA_OF: /* BA */ + if (access == WRITEB) val = (pa & 1)? + (massbus[mb].ba & 0377) | (val << 8): + (massbus[mb].ba & ~0377) | val; + massbus[mb].ba = val & ~BA_MBZ; + break; + + case CS2_OF: /* CS2 */ + if ((access == WRITEB) && (pa & 1)) val = val << 8; + if (val & CS2_CLR) mba_reset (&mba_dev[mb]); /* init? */ + else { + if ((val & ~massbus[mb].cs2) & (CS2_PE | CS2_MXF)) + cs1f = CS1_SC; /* diagn intr */ + if (access == WRITEB) val = (massbus[mb].cs2 & /* merge val */ + ((pa & 1)? 0377: 0177400)) | val; + massbus[mb].cs2 = (massbus[mb].cs2 & ~CS2_RW) | + (val & CS2_RW) | CS2_IR | CS2_OR; + } + break; + + case DB_OF: /* DB */ + if (access == WRITEB) val = (pa & 1)? + (massbus[mb].db & 0377) | (val << 8): + (massbus[mb].db & ~0377) | val; + massbus[mb].db = val; + break; + + case BAE_OF: /* BAE */ + if ((access == WRITEB) && (pa & 1)) break; + massbus[mb].bae = val & ~AE_MBZ; + massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_UAE) | /* update CS1 */ + ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); + break; + + case CS3_OF: /* CS3 */ + if ((access == WRITEB) && (pa & 1)) break; + massbus[mb].cs3 = (massbus[mb].cs3 & ~CS3_RW) | (val & CS3_RW); + massbus[mb].cs1 = (massbus[mb].cs1 & ~CS1_IE) | /* update CS1 */ + (massbus[mb].cs3 & CS1_IE); + break; + + default: + return SCPE_NXM; + } + +mba_upd_cs1 (cs1f, 0, mb); /* update status */ +return SCPE_OK; +} + +/* Massbus I/O routines + + mb_rdbufW - fetch word buffer from memory + mb_wrbufW - store word buffer into memory + mb_chbufW - compare word buffer with memory + + Returns number of bytes successfully transferred/checked +*/ + +int32 mba_rdbufW (uint32 mb, int32 bc, uint16 *buf) +{ +int32 i, j, ba, mbc, pbc; +uint32 pa; + +bc = bc & ~1; /* bc even */ +if (mb >= MBA_NUM) return 0; /* valid MBA? */ +ba = (massbus[mb].bae << 16) | massbus[mb].ba; /* get busaddr */ +mbc = (0200000 - massbus[mb].wc) << 1; /* MB byte count */ +if (bc > mbc) bc = mbc; /* use smaller */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (RH11 && cpu_bme) pa = Map_Addr (ba); /* map addr */ + else pa = ba; + if (!ADDR_IS_MEM (pa)) { /* NXM? */ + mba_set_cs2 (CS2_NEM, mb); /* set error */ + break; + } + pbc = UBM_PAGSIZE - UBM_GETOFF (pa); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + for (j = 0; j < pbc; j = j + 2) { /* loop by words */ + *buf++ = M[pa >> 1]; /* fetch word */ + if (!(massbus[mb].cs2 & CS2_UAI)) { /* if not inhb */ + ba = ba + 2; /* incr ba, pa */ + pa = pa + 2; + } + } + } +massbus[mb].wc = (massbus[mb].wc + (bc >> 1)) & DMASK; /* update wc */ +massbus[mb].ba = ba & DMASK; /* update ba */ +massbus[mb].bae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ +massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ + ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); +return i; +} + +int32 mba_wrbufW (uint32 mb, int32 bc, uint16 *buf) +{ +int32 i, j, ba, mbc, pbc; +uint32 pa; + +bc = bc & ~1; /* bc even */ +if (mb >= MBA_NUM) return 0; /* valid MBA? */ +ba = (massbus[mb].bae << 16) | massbus[mb].ba; /* get busaddr */ +mbc = (0200000 - massbus[mb].wc) << 1; /* MB byte count */ +if (bc > mbc) bc = mbc; /* use smaller */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (RH11 && cpu_bme) pa = Map_Addr (ba); /* map addr */ + else pa = ba; + if (!ADDR_IS_MEM (pa)) { /* NXM? */ + mba_set_cs2 (CS2_NEM, mb); /* set error */ + break; + } + pbc = UBM_PAGSIZE - UBM_GETOFF (pa); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + for (j = 0; j < pbc; j = j + 2) { /* loop by words */ + M[pa >> 1] = *buf++; /* put word */ + if (!(massbus[mb].cs2 & CS2_UAI)) { /* if not inhb */ + ba = ba + 2; /* incr ba, pa */ + pa = pa + 2; + } + } + } +massbus[mb].wc = (massbus[mb].wc + (bc >> 1)) & DMASK; /* update wc */ +massbus[mb].ba = ba & DMASK; /* update ba */ +massbus[mb].bae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ +massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ + ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); +return i; +} + +int32 mba_chbufW (uint32 mb, int32 bc, uint16 *buf) +{ +int32 i, j, ba, mbc, pbc; +uint32 pa; + +bc = bc & ~1; /* bc even */ +if (mb >= MBA_NUM) return 0; /* valid MBA? */ +ba = (massbus[mb].bae << 16) | massbus[mb].ba; /* get busaddr */ +mbc = (0200000 - massbus[mb].wc) << 1; /* MB byte count */ +if (bc > mbc) bc = mbc; /* use smaller */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (RH11 && cpu_bme) pa = Map_Addr (ba); /* map addr */ + else pa = ba; + if (!ADDR_IS_MEM (pa)) { /* NXM? */ + mba_set_cs2 (CS2_NEM, mb); /* set error */ + break; + } + pbc = UBM_PAGSIZE - UBM_GETOFF (pa); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + for (j = 0; j < pbc; j = j + 2) { /* loop by words */ + massbus[mb].db = *buf++; /* get dev word */ + if (M[pa >> 1] != massbus[mb].db) { /* miscompare? */ + mba_set_cs2 (CS2_WCE, mb); /* set error */ + massbus[mb].cs3 = massbus[mb].cs3 | /* set even/odd */ + ((pa & 1)? CS3_WCO: CS3_WCE); + break; + } + if (!(massbus[mb].cs2 & CS2_UAI)) { /* if not inhb */ + ba = ba + 2; /* incr ba, pa */ + pa = pa + 2; + } + } + } +massbus[mb].wc = (massbus[mb].wc + (bc >> 1)) & DMASK; /* update wc */ +massbus[mb].ba = ba & DMASK; /* update ba */ +massbus[mb].bae = (ba >> 16) & ~AE_MBZ; /* upper 6b */ +massbus[mb].cs1 = (massbus[mb].cs1 & ~ CS1_UAE) | /* update CS1 */ + ((massbus[mb].bae << CS1_V_UAE) & CS1_UAE); +return i; +} + +/* Device access, status, and interrupt routines */ + +void mba_set_don (uint32 mb) +{ +mba_upd_cs1 (CS1_DONE, 0, mb); +return; +} + +void mba_upd_ata (uint32 mb, uint32 val) +{ +if (val) mba_upd_cs1 (CS1_SC, 0, mb); +else mba_upd_cs1 (0, CS1_SC, mb); +return; +} + +void mba_set_exc (uint32 mb) +{ +mba_upd_cs1 (CS1_TRE | CS1_DONE, 0, mb); +return; +} + +int32 mba_get_bc (uint32 mb) +{ +if (mb >= MBA_NUM) return 0; +return ((0200000 - massbus[mb].wc) << 1); +} + +int32 mba_get_csr (uint32 mb) +{ +DIB *dibp; + +if (mb >= MBA_NUM) return 0; +dibp = (DIB *) mba_dev[mb].ctxt; +return dibp->ba; +} + +void mba_set_int (uint32 mb) +{ +DIB *dibp; + +if (mb >= MBA_NUM) return; +dibp = (DIB *) mba_dev[mb].ctxt; +int_req[dibp->vloc >> 5] |= (1 << (dibp->vloc & 037)); +return; +} + +void mba_clr_int (uint32 mb) +{ +DIB *dibp; + +if (mb >= MBA_NUM) return; +dibp = (DIB *) mba_dev[mb].ctxt; +int_req[dibp->vloc >> 5] &= ~(1 << (dibp->vloc & 037)); +return; +} + +void mba_upd_cs1 (uint32 set, uint32 clr, uint32 mb) +{ +if (mb >= MBA_NUM) return; +if ((set & ~massbus[mb].cs1) & CS1_DONE) /* DONE 0 to 1? */ + massbus[mb].iff = (massbus[mb].cs1 & CS1_IE)? 1: 0; /* CSTB INTR <- IE */ +massbus[mb].cs1 = (massbus[mb].cs1 & ~(clr | CS1_MCPE | CS1_MBZ | CS1_DRV)) | set; +if (massbus[mb].cs2 & CS2_ERR) massbus[mb].cs1 = massbus[mb].cs1 | CS1_TRE | CS1_SC; +else if (massbus[mb].cs1 & CS1_TRE) massbus[mb].cs1 = massbus[mb].cs1 | CS1_SC; +if (massbus[mb].iff || + ((massbus[mb].cs1 & CS1_SC) && + (massbus[mb].cs1 & CS1_DONE) && + (massbus[mb].cs1 & CS1_IE))) + mba_set_int (mb); +else mba_clr_int (mb); +return; +} + +void mba_set_cs2 (uint32 flag, uint32 mb) +{ +if (mb >= MBA_NUM) return; +massbus[mb].cs2 = massbus[mb].cs2 | flag; +mba_upd_cs1 (0, 0, mb); +return; +} + +/* Interrupt acknowledge */ + +int32 mba0_inta (void) +{ +massbus[0].cs1 &= ~CS1_IE; /* clear int enable */ +massbus[0].cs3 &= ~CS1_IE; /* in both registers */ +massbus[0].iff = 0; /* clear CSTB INTR */ +return mba0_dib.vec; /* acknowledge */ +} + +int32 mba1_inta (void) +{ +massbus[1].cs1 &= ~CS1_IE; /* clear int enable */ +massbus[1].cs3 &= ~CS1_IE; /* in both registers */ +massbus[1].iff = 0; /* clear CSTB INTR */ +return mba1_dib.vec; /* acknowledge */ +} + +/* Map physical address to Massbus number, offset */ + +uint32 mba_map_pa (int32 pa, int32 *ofs) +{ +int32 i, uo, ba, lnt; +DIB *dibp; + +for (i = 0; i < MBA_NUM; i++) { /* loop thru ctrls */ + dibp = (DIB *) mba_dev[i].ctxt; /* get DIB */ + ba = dibp->ba; + lnt = dibp->lnt; + if ((pa >= ba) && /* in range? */ + (pa < (ba + lnt))) { + if (pa < (ba + (lnt - 4))) { /* not last two? */ + uo = ((pa - ba) & MBA_OFSMASK) >> 1; /* get Unibus offset */ + *ofs = mba_mapofs[uo]; /* map thru PROM */ + return i; /* return ctrl idx */ + } + else if (RH11) return -1; /* RH11? done */ + else { /* RH70 */ + uo = (pa - (ba + (lnt - 4))) >> 1; /* offset relative */ + *ofs = BAE_OF + uo; /* to BAE */ + return i; + } + } + } +return -1; +} + +/* Reset Massbus adapter */ + +t_stat mba_reset (DEVICE *dptr) +{ +uint32 mb; + +mb = dptr - mba_dev; +if (mb >= MBA_NUM) return SCPE_NOFNC; +massbus[mb].cs1 = CS1_DONE; +massbus[mb].wc = 0; +massbus[mb].ba = 0; +massbus[mb].cs2 = 0; +massbus[mb].db = 0; +massbus[mb].bae= 0; +massbus[mb].cs3 = 0; +massbus[mb].iff = 0; +mba_clr_int (mb); +if (mbabort[mb]) mbabort[mb] (); +return SCPE_OK; +} + +/* Enable/disable Massbus adapter */ + +void mba_set_enbdis (uint32 mb, t_bool dis) +{ +if (mb >= MBA_NUM) return; /* valid MBA? */ +if (dis) mba_dev[mb].flags |= DEV_DIS; +else mba_dev[mb].flags &= ~DEV_DIS; +return; +} + +/* Show Massbus adapter number */ + +t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr = find_dev_from_unit (uptr); +DIB *dibp; + +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +fprintf (st, "Massbus adapter %d", dibp->ba); +return SCPE_OK; +} + +/* Init Mbus tables */ + +void init_mbus_tab (void) +{ +uint32 i; + +for (i = 0; i < MBA_NUM; i++) { + mbregR[i] = NULL; + mbregW[i] = NULL; + mbabort[i] = NULL; + } +return; +} + +/* Build dispatch tables */ + +t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp) +{ +uint32 idx; + +if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */ +idx = dibp->ba; /* Mbus # */ +if (idx >= MBA_NUM) return SCPE_STOP; +if ((mbregR[idx] && dibp->rd && /* conflict? */ + (mbregR[idx] != dibp->rd)) || + (mbregW[idx] && dibp->wr && + (mbregW[idx] != dibp->wr)) || + (mbabort[idx] && dibp->ack[0] && + (mbabort[idx] != dibp->ack[0]))) { + printf ("Massbus %s assignment conflict at %d\n", + sim_dname (dptr), dibp->ba); + if (sim_log) fprintf (sim_log, + "Massbus %s assignment conflict at %d\n", + sim_dname (dptr), dibp->ba); + return SCPE_STOP; + } +if (dibp->rd) mbregR[idx] = dibp->rd; /* set rd dispatch */ +if (dibp->wr) mbregW[idx] = dibp->wr; /* set wr dispatch */ +if (dibp->ack[0]) mbabort[idx] = dibp->ack[0]; /* set abort dispatch */ +return SCPE_OK; +} + diff --git a/PDP11/pdp11_rk.c b/PDP11/pdp11_rk.c new file mode 100644 index 0000000..cb77801 --- /dev/null +++ b/PDP11/pdp11_rk.c @@ -0,0 +1,737 @@ +/* pdp11_rk.c: RK11/RKV11 cartridge disk simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rk RK11/RKV11/RK05 cartridge disk + + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 30-Sep-04 RMS Revised Unibus interface + 24-Jan-04 RMS Added increment inhibit, overrun detection, formatting + 29-Dec-03 RMS Added RKV11 support + 29-Sep-02 RMS Added variable address support to bootstrap + Added vector change/display support + Revised mapping mnemonics + New data structures + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted FLG to array + 09-Nov-01 RMS Added bus map support + 07-Sep-01 RMS Revised device disable and interrupt mechanisms + 26-Apr-01 RMS Added device enable/disable support + 25-Mar-01 RMS Fixed block fill calculation + 15-Feb-01 RMS Corrected bootstrap string + 29-Jun-96 RMS Added unit disable support + + The RK11 is an eight drive cartridge disk subsystem. An RK05 drive + consists of 203 cylinders, each with 2 surfaces containing 12 sectors + of 512 bytes. + + The most complicated part of the RK11 controller is the concept of + interrupt "polling". While only one read or write can occur at a + time, the controller supports multiple seeks. When a seek completes, + if done is set the drive attempts to interrupt. If an interrupt is + already pending, the interrupt is "queued" until it can be processed. + When an interrupt occurs, RKDS<15:13> is loaded with the number of the + interrupting drive. + + To implement this structure, and to assure that read/write interrupts + take priority over seek interrupts, the controller contains an + interrupt queue, rkintq, with a bit for a controller interrupt and + then one for each drive. In addition, the drive number of the last + non-seeking drive is recorded in last_drv. +*/ + +#include "pdp11_defs.h" + +/* Constants */ + +#define RK_NUMWD 256 /* words/sector */ +#define RK_NUMSC 12 /* sectors/surface */ +#define RK_NUMSF 2 /* surfaces/cylinder */ +#define RK_NUMCY 203 /* cylinders/drive */ +#define RK_NUMTR (RK_NUMCY * RK_NUMSF) /* tracks/drive */ +#define RK_NUMDR 8 /* drives/controller */ +#define RK_M_NUMDR 07 +#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) + /* words/drive */ +#define RK_CTLI 1 /* controller int */ +#define RK_SCPI(x) (2u << (x)) /* drive int */ +#define RK_MAXFR (1 << 16) /* max transfer */ + +/* Flags in the unit flags word */ + +#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */ +#define UNIT_HWLK (1u << UNIT_V_HWLK) +#define UNIT_SWLK (1u << UNIT_V_SWLK) +#define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write prot */ + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* RKDS */ + +#define RKDS_SC 0000017 /* sector counter */ +#define RKDS_ON_SC 0000020 /* on sector */ +#define RKDS_WLK 0000040 /* write locked */ +#define RKDS_RWS 0000100 /* rd/wr/seek ready */ +#define RKDS_RDY 0000200 /* drive ready */ +#define RKDS_SC_OK 0000400 /* SC valid */ +#define RKDS_INC 0001000 /* seek incomplete */ +#define RKDS_UNSAFE 0002000 /* unsafe */ +#define RKDS_RK05 0004000 /* RK05 */ +#define RKDS_PWR 0010000 /* power low */ +#define RKDS_ID 0160000 /* drive ID */ +#define RKDS_V_ID 13 + +/* RKER */ + +#define RKER_WCE 0000001 /* write check */ +#define RKER_CSE 0000002 /* checksum */ +#define RKER_NXS 0000040 /* nx sector */ +#define RKER_NXC 0000100 /* nx cylinder */ +#define RKER_NXD 0000200 /* nx drive */ +#define RKER_TE 0000400 /* timing error */ +#define RKER_DLT 0001000 /* data late */ +#define RKER_NXM 0002000 /* nx memory */ +#define RKER_PGE 0004000 /* programming error */ +#define RKER_SKE 0010000 /* seek error */ +#define RKER_WLK 0020000 /* write lock */ +#define RKER_OVR 0040000 /* overrun */ +#define RKER_DRE 0100000 /* drive error */ +#define RKER_IMP 0177743 /* implemented */ +#define RKER_SOFT (RKER_WCE+RKER_CSE) /* soft errors */ +#define RKER_HARD 0177740 /* hard errors */ + +/* RKCS */ + +#define RKCS_M_FUNC 0000007 /* function */ +#define RKCS_CTLRESET 0 +#define RKCS_WRITE 1 +#define RKCS_READ 2 +#define RKCS_WCHK 3 +#define RKCS_SEEK 4 +#define RKCS_RCHK 5 +#define RKCS_DRVRESET 6 +#define RKCS_WLK 7 +#define RKCS_V_FUNC 1 +#define RKCS_MEX 0000060 /* memory extension */ +#define RKCS_V_MEX 4 +#define RKCS_SSE 0000400 /* stop on soft err */ +#define RKCS_FMT 0002000 /* format */ +#define RKCS_INH 0004000 /* inhibit increment */ +#define RKCS_SCP 0020000 /* search complete */ +#define RKCS_HERR 0040000 /* hard error */ +#define RKCS_ERR 0100000 /* error */ +#define RKCS_REAL 0026776 /* kept here */ +#define RKCS_RW 0006576 /* read/write */ +#define GET_FUNC(x) (((x) >> RKCS_V_FUNC) & RKCS_M_FUNC) + +/* RKDA */ + +#define RKDA_V_SECT 0 /* sector */ +#define RKDA_M_SECT 017 +#define RKDA_V_TRACK 4 /* track */ +#define RKDA_M_TRACK 0777 +#define RKDA_V_CYL 5 /* cylinder */ +#define RKDA_M_CYL 0377 +#define RKDA_V_DRIVE 13 /* drive */ +#define RKDA_M_DRIVE 07 +#define RKDA_DRIVE (RKDA_M_DRIVE << RKDA_V_DRIVE) +#define GET_SECT(x) (((x) >> RKDA_V_SECT) & RKDA_M_SECT) +#define GET_CYL(x) (((x) >> RKDA_V_CYL) & RKDA_M_CYL) +#define GET_TRACK(x) (((x) >> RKDA_V_TRACK) & RKDA_M_TRACK) +#define GET_DRIVE(x) (((x) >> RKDA_V_DRIVE) & RKDA_M_DRIVE) +#define GET_DA(x) ((GET_TRACK (x) * RK_NUMSC) + GET_SECT (x)) + +/* RKBA */ + +#define RKBA_IMP 0177776 /* implemented */ + +#define RK_MIN 10 +#define MAX(x,y) (((x) > (y))? (x): (y)) + +extern uint16 *M; /* memory */ +extern int32 int_req[IPL_HLVL]; + +uint16 *rkxb = NULL; /* xfer buffer */ +int32 rkcs = 0; /* control/status */ +int32 rkds = 0; /* drive status */ +int32 rkba = 0; /* memory address */ +int32 rkda = 0; /* disk address */ +int32 rker = 0; /* error status */ +int32 rkwc = 0; /* word count */ +int32 rkintq = 0; /* interrupt queue */ +int32 last_drv = 0; /* last r/w drive */ +int32 rk_stopioe = 1; /* stop on error */ +int32 rk_swait = 10; /* seek time */ +int32 rk_rwait = 10; /* rotate time */ + +DEVICE rk_dev; +t_stat rk_rd (int32 *data, int32 PA, int32 access); +t_stat rk_wr (int32 data, int32 PA, int32 access); +int32 rk_inta (void); +t_stat rk_svc (UNIT *uptr); +t_stat rk_reset (DEVICE *dptr); +void rk_go (void); +void rk_set_done (int32 error); +void rk_clr_done (void); +t_stat rk_boot (int32 unitno, DEVICE *dptr); + +/* RK11 data structures + + rk_dev RK device descriptor + rk_unit RK unit list + rk_reg RK register list + rk_mod RK modifier list +*/ + +DIB rk_dib = { + IOBA_RK, IOLN_RK, &rk_rd, &rk_wr, + 1, IVCL (RK), VEC_RK, { &rk_inta } + }; + +UNIT rk_unit[] = { + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) } + }; + +REG rk_reg[] = { + { ORDATA (RKCS, rkcs, 16) }, + { ORDATA (RKDA, rkda, 16) }, + { ORDATA (RKBA, rkba, 16) }, + { ORDATA (RKWC, rkwc, 16) }, + { ORDATA (RKDS, rkds, 16) }, + { ORDATA (RKER, rker, 16) }, + { ORDATA (INTQ, rkintq, 9) }, + { ORDATA (DRVN, last_drv, 3) }, + { FLDATA (INT, IREQ (RK), INT_V_RK) }, + { FLDATA (ERR, rkcs, CSR_V_ERR) }, + { FLDATA (DONE, rkcs, CSR_V_DONE) }, + { FLDATA (IE, rkcs, CSR_V_IE) }, + { DRDATA (STIME, rk_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rk_rwait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, rk_stopioe, 0) }, + { ORDATA (DEVADDR, rk_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, rk_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB rk_mod[] = { + { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE rk_dev = { + "RK", rk_unit, rk_reg, rk_mod, + RK_NUMDR, 8, 24, 1, 8, 16, + NULL, NULL, &rk_reset, + &rk_boot, NULL, NULL, + &rk_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 + }; + +/* I/O dispatch routine, I/O addresses 17777400 - 17777416 + + 17777400 RKDS read only, constructed from "id'd drive" + plus current drive status flags + 17777402 RKER read only, set as operations progress, + cleared by INIT or CONTROL RESET + 17777404 RKCS read/write + 17777406 RKWC read/write + 17777410 RKBA read/write + 17777412 RKDA read/write + 17777414 RKMR read/write, unimplemented + 17777416 RKDB read only, unimplemented +*/ + +t_stat rk_rd (int32 *data, int32 PA, int32 access) +{ +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* RKDS: read only */ + rkds = (rkds & RKDS_ID) | RKDS_RK05 | RKDS_SC_OK | + (rand () % RK_NUMSC); /* random sector */ + uptr = rk_dev.units + GET_DRIVE (rkda); /* selected unit */ + if (uptr->flags & UNIT_ATT) /* attached? */ + rkds = rkds | RKDS_RDY; + if (!sim_is_active (uptr)) /* idle? */ + rkds = rkds | RKDS_RWS; + if (uptr->flags & UNIT_WPRT) rkds = rkds | RKDS_WLK; + if (GET_SECT (rkda) == (rkds & RKDS_SC)) rkds = rkds | RKDS_ON_SC; + *data = rkds; + return SCPE_OK; + + case 1: /* RKER: read only */ + *data = rker & RKER_IMP; + return SCPE_OK; + + case 2: /* RKCS */ + rkcs = rkcs & RKCS_REAL; + if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */ + if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; + *data = rkcs; + return SCPE_OK; + + case 3: /* RKWC */ + *data = rkwc; + return SCPE_OK; + + case 4: /* RKBA */ + *data = rkba & RKBA_IMP; + return SCPE_OK; + + case 5: /* RKDA */ + *data = rkda; + return SCPE_OK; + + default: + *data = 0; + return SCPE_OK; + } /* end switch */ +} + +t_stat rk_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* RKDS: read only */ + return SCPE_OK; + + case 1: /* RKER: read only */ + return SCPE_OK; + + case 2: /* RKCS */ + rkcs = rkcs & RKCS_REAL; + if (access == WRITEB) data = (PA & 1)? + (rkcs & 0377) | (data << 8): (rkcs & ~0377) | data; + if ((data & CSR_IE) == 0) { /* int disable? */ + rkintq = 0; /* clr int queue */ + CLR_INT (RK); /* clr int request */ + } + else if ((rkcs & (CSR_DONE + CSR_IE)) == CSR_DONE) { + rkintq = rkintq | RK_CTLI; /* queue ctrl int */ + SET_INT (RK); /* set int request */ + } + rkcs = (rkcs & ~RKCS_RW) | (data & RKCS_RW); + if ((rkcs & CSR_DONE) && (data & CSR_GO)) /* new function? */ + rk_go (); + return SCPE_OK; + + case 3: /* RKWC */ + if (access == WRITEB) data = (PA & 1)? + (rkwc & 0377) | (data << 8): (rkwc & ~0377) | data; + rkwc = data; + return SCPE_OK; + + case 4: /* RKBA */ + if (access == WRITEB) data = (PA & 1)? + (rkba & 0377) | (data << 8): (rkba & ~0377) | data; + rkba = data & RKBA_IMP; + return SCPE_OK; + + case 5: /* RKDA */ + if ((rkcs & CSR_DONE) == 0) return SCPE_OK; + if (access == WRITEB) data = (PA & 1)? + (rkda & 0377) | (data << 8): (rkda & ~0377) | data; + rkda = data; + return SCPE_OK; + + default: + return SCPE_OK; + } /* end switch */ +} + +/* Initiate new function */ + +void rk_go (void) +{ +int32 i, sect, cyl, func; +UNIT *uptr; + +func = GET_FUNC (rkcs); /* get function */ +if (func == RKCS_CTLRESET) { /* control reset? */ + rker = 0; /* clear errors */ + rkda = 0; + rkba = 0; + rkcs = CSR_DONE; + rkintq = 0; /* clr int queue */ + CLR_INT (RK); /* clr int request */ + return; + } +rker = rker & ~RKER_SOFT; /* clear soft errors */ +if (rker == 0) rkcs = rkcs & ~RKCS_ERR; /* redo summary */ +rkcs = rkcs & ~RKCS_SCP; /* clear sch compl*/ +rk_clr_done (); /* clear done */ +last_drv = GET_DRIVE (rkda); /* get drive no */ +uptr = rk_dev.units + last_drv; /* select unit */ +if (uptr->flags & UNIT_DIS) { /* not present? */ + rk_set_done (RKER_NXD); + return; + } +if (((uptr->flags & UNIT_ATT) == 0) || /* not att or busy? */ + sim_is_active (uptr)) { + rk_set_done (RKER_DRE); + return; + } +if ((rkcs & RKCS_FMT) && /* format and */ + (func != RKCS_READ) && (func != RKCS_WRITE)) { /* not read or write? */ + rk_set_done (RKER_PGE); + return; + } +if ((func == RKCS_WRITE) && /* write and locked? */ + (uptr->flags & UNIT_WPRT)) { + rk_set_done (RKER_WLK); + return; + } +if (func == RKCS_WLK) { /* write lock? */ + uptr->flags = uptr->flags | UNIT_SWLK; + rk_set_done (0); + return; + } +if (func == RKCS_DRVRESET) { /* drive reset? */ + uptr->flags = uptr->flags & ~UNIT_SWLK; + cyl = sect = 0; + func = RKCS_SEEK; + } +else { + sect = GET_SECT (rkda); + cyl = GET_CYL (rkda); + } +if (sect >= RK_NUMSC) { /* bad sector? */ + rk_set_done (RKER_NXS); + return; + } +if (cyl >= RK_NUMCY) { /* bad cyl? */ + rk_set_done (RKER_NXC); + return; + } +i = abs (cyl - uptr->CYL) * rk_swait; /* seek time */ +if (func == RKCS_SEEK) { /* seek? */ + rk_set_done (0); /* set done */ + sim_activate (uptr, MAX (RK_MIN, i)); /* schedule */ + } +else sim_activate (uptr, i + rk_rwait); +uptr->FUNC = func; /* save func */ +uptr->CYL = cyl; /* put on cylinder */ +return; +} + +/* Service unit timeout + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and disk address for + the current command. +*/ + +t_stat rk_svc (UNIT *uptr) +{ +int32 i, drv, err, awc, wc, cma, cda, t; +int32 da, cyl, track, sect; +uint32 ma; +uint16 comp; + +drv = (int32) (uptr - rk_dev.units); /* get drv number */ +if (uptr->FUNC == RKCS_SEEK) { /* seek */ + rkcs = rkcs | RKCS_SCP; /* set seek done */ + if (rkcs & CSR_IE) { /* ints enabled? */ + rkintq = rkintq | RK_SCPI (drv); /* queue request */ + if (rkcs & CSR_DONE) SET_INT (RK); + } + else { + rkintq = 0; /* clear queue */ + CLR_INT (RK); /* clear interrupt */ + } + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + rk_set_done (RKER_DRE); + return IORETURN (rk_stopioe, SCPE_UNATT); + } +sect = GET_SECT (rkda); /* get sector, cyl */ +cyl = GET_CYL (rkda); +if (sect >= RK_NUMSC) { /* bad sector? */ + rk_set_done (RKER_NXS); + return SCPE_OK; + } +if (cyl >= RK_NUMCY) { /* bad cyl? */ + rk_set_done (RKER_NXC); + return SCPE_OK; + } +ma = ((rkcs & RKCS_MEX) << (16 - RKCS_V_MEX)) | rkba; /* get mem addr */ +da = GET_DA (rkda) * RK_NUMWD; /* get disk addr */ +wc = 0200000 - rkwc; /* get wd cnt */ +if ((da + wc) > (int32) uptr->capac) { /* overrun? */ + wc = uptr->capac - da; /* trim transfer */ + rker = rker | RKER_OVR; /* set overrun err */ + } + +err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); +if (wc && (err == 0)) { /* seek ok? */ + switch (uptr->FUNC) { /* case on function */ + + case RKCS_READ: /* read */ + if (rkcs & RKCS_FMT) { /* format? */ + for (i = 0, cda = da; i < wc; i++) { /* fill buffer with cyl #s */ + if (cda >= (int32) uptr->capac) { /* overrun? */ + rker = rker | RKER_OVR; /* set overrun err */ + wc = i; /* trim transfer */ + break; + } + rkxb[i] = (cda / RK_NUMWD) / (RK_NUMSF * RK_NUMSC); + cda = cda + RK_NUMWD; /* next sector */ + } /* end for wc */ + } /* end if format */ + else { /* normal read */ + i = fxread (rkxb, sizeof (int16), wc, uptr->fileref); + err = ferror (uptr->fileref); /* read file */ + for ( ; i < wc; i++) rkxb[i] = 0; /* fill buf */ + } + if (rkcs & RKCS_INH) { /* incr inhibit? */ + if (t = Map_WriteW (ma, 2, &rkxb[wc - 1])) { /* store last */ + rker = rker | RKER_NXM; /* NXM? set flag */ + wc = 0; /* no transfer */ + } + } + else { /* normal store */ + if (t = Map_WriteW (ma, wc << 1, rkxb)) { /* store buf */ + rker = rker | RKER_NXM; /* NXM? set flag */ + wc = wc - t; /* adj wd cnt */ + } + } + break; /* end read */ + + case RKCS_WRITE: /* write */ + if (rkcs & RKCS_INH) { /* incr inhibit? */ + if (t = Map_ReadW (ma, 2, &comp)) { /* get 1st word */ + rker = rker | RKER_NXM; /* NXM? set flag */ + wc = 0; /* no transfer */ + } + for (i = 0; i < wc; i++) rkxb[i] = comp; /* all words same */ + } + else { /* normal fetch */ + if (t = Map_ReadW (ma, wc << 1, rkxb)) { /* get buf */ + rker = rker | RKER_NXM; /* NXM? set flg */ + wc = wc - t; /* adj wd cnt */ + } + } + if (wc) { /* any xfer? */ + awc = (wc + (RK_NUMWD - 1)) & ~(RK_NUMWD - 1); /* clr to */ + for (i = wc; i < awc; i++) rkxb[i] = 0; /* end of blk */ + fxwrite (rkxb, sizeof (int16), awc, uptr->fileref); + err = ferror (uptr->fileref); + } + break; /* end write */ + + case RKCS_WCHK: /* write check */ + i = fxread (rkxb, sizeof (int16), wc, uptr->fileref); + if (err = ferror (uptr->fileref)) { /* read error? */ + wc = 0; /* no transfer */ + break; + } + for ( ; i < wc; i++) rkxb[i] = 0; /* fill buf */ + awc = wc; /* save wc */ + for (wc = 0, cma = ma; wc < awc; wc++) { /* loop thru buf */ + if (Map_ReadW (cma, 2, &comp)) { /* mem wd */ + rker = rker | RKER_NXM; /* NXM? set flg */ + break; + } + if (comp != rkxb[wc]) { /* match to disk? */ + rker = rker | RKER_WCE; /* no, err */ + if (rkcs & RKCS_SSE) break; + } + if (!(rkcs & RKCS_INH)) cma = cma + 2; /* next mem addr */ + } /* end for */ + break; /* end wcheck */ + + default: /* read check */ + break; + } /* end switch */ + } /* end else */ + +rkwc = (rkwc + wc) & 0177777; /* final word count */ +if (!(rkcs & RKCS_INH)) ma = ma + (wc << 1); /* final byte addr */ +rkba = ma & RKBA_IMP; /* lower 16b */ +rkcs = (rkcs & ~RKCS_MEX) | ((ma >> (16 - RKCS_V_MEX)) & RKCS_MEX); +if ((uptr->FUNC == RKCS_READ) && (rkcs & RKCS_FMT)) /* read format? */ + da = da + (wc * RK_NUMWD); /* count by sectors */ +else da = da + wc + (RK_NUMWD - 1); /* count by words */ +track = (da / RK_NUMWD) / RK_NUMSC; +sect = (da / RK_NUMWD) % RK_NUMSC; +rkda = (rkda & RKDA_DRIVE) | (track << RKDA_V_TRACK) | (sect << RKDA_V_SECT); +rk_set_done (0); + +if (err != 0) { /* error? */ + perror ("RK I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Interrupt state change routines + + rk_set_done set done and possibly errors + rk_clr_done clear done + rk_inta acknowledge intererupt +*/ + +void rk_set_done (int32 error) +{ +rkcs = rkcs | CSR_DONE; /* set done */ +if (error != 0) { + rker = rker | error; /* update error */ + if (rker) rkcs = rkcs | RKCS_ERR; /* update err flags */ + if (rker & RKER_HARD) rkcs = rkcs | RKCS_HERR; + } +if (rkcs & CSR_IE) { /* int enable? */ + rkintq = rkintq | RK_CTLI; /* set ctrl int */ + SET_INT (RK); /* request int */ + } +else { + rkintq = 0; /* clear queue */ + CLR_INT (RK); + } +return; +} + +void rk_clr_done (void) +{ +rkcs = rkcs & ~CSR_DONE; /* clear done */ +rkintq = rkintq & ~RK_CTLI; /* clear ctl int */ +CLR_INT (RK); /* clear int req */ +return; +} + +int32 rk_inta (void) +{ +int32 i; + +for (i = 0; i <= RK_NUMDR; i++) { /* loop thru intq */ + if (rkintq & (1u << i)) { /* bit i set? */ + rkintq = rkintq & ~(1u << i); /* clear bit i */ + if (rkintq) SET_INT (RK); /* queue next */ + rkds = (rkds & ~RKDS_ID) | /* id drive */ + (((i == 0)? last_drv: i - 1) << RKDS_V_ID); + return rk_dib.vec; /* return vector */ + } + } +rkintq = 0; /* clear queue */ +return 0; /* passive release */ +} + +/* Device reset */ + +t_stat rk_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rkcs = CSR_DONE; +rkda = rkba = rker = rkds = 0; +rkintq = last_drv = 0; +CLR_INT (RK); +for (i = 0; i < RK_NUMDR; i++) { + uptr = rk_dev.units + i; + sim_cancel (uptr); + uptr->CYL = uptr->FUNC = 0; + uptr->flags = uptr->flags & ~UNIT_SWLK; + } +if (rkxb == NULL) rkxb = (uint16 *) calloc (RK_MAXFR, sizeof (uint16)); +if (rkxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Device bootstrap */ + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 032) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 0042113, /* "KD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012700, 0000000, /* MOV #unit, R0 ; unit number */ + 0010003, /* MOV R0, R3 */ + 0000303, /* SWAB R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0012701, 0177412, /* MOV #RKDA, R1 ; csr */ + 0010311, /* MOV R3, (R1) ; load da */ + 0005041, /* CLR -(R1) ; clear ba */ + 0012741, 0177000, /* MOV #-256.*2, -(R1) ; load wc */ + 0012741, 0000005, /* MOV #READ+GO, -(R1) ; read & go */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0105711, /* TSTB (R1) */ + 0100376, /* BPL .-2 */ + 0105011, /* CLRB (R1) */ + 0005007 /* CLR PC */ + }; + +t_stat rk_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RK_M_NUMDR; +M[BOOT_CSR >> 1] = (rk_dib.ba & DMASK) + 012; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} diff --git a/PDP11/pdp11_rl.c b/PDP11/pdp11_rl.c new file mode 100644 index 0000000..78b7aee --- /dev/null +++ b/PDP11/pdp11_rl.c @@ -0,0 +1,692 @@ +/* pdp11_rl.c: RL11 (RLV12) cartridge disk simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rl RL11(RLV12)/RL01/RL02 cartridge disk + + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 30-Sep-04 RMS Revised Unibus interface + 04-Jan-04 RMS Changed sim_fsize calling sequence + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 29-Sep-02 RMS Added variable address support to bootstrap + Added vector change/display support + Revised mapping nomenclature + New data structures + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only, extended SET/SHOW support + 26-Nov-01 RMS Fixed per-drive error handling + 24-Nov-01 RMS Converted FLG, CAPAC to arrays + 19-Nov-01 RMS Fixed signed/unsigned mismatch in write check + 09-Nov-01 RMS Added bus map, VAX support + 07-Sep-01 RMS Revised device disable and interrupt mechanisms + 20-Aug-01 RMS Added bad block option in attach + 17-Jul-01 RMS Fixed warning from VC++ 6.0 + 26-Apr-01 RMS Added device enable/disable support + 25-Mar-01 RMS Fixed block fill calculation + 15-Feb-01 RMS Corrected bootstrap string + 12-Nov-97 RMS Added bad block table command + 25-Nov-96 RMS Default units to autosize + 29-Jun-96 RMS Added unit disable support + + The RL11 is a four drive cartridge disk subsystem. An RL01 drive + consists of 256 cylinders, each with 2 surfaces containing 40 sectors + of 256 bytes. An RL02 drive has 512 cylinders. The RLV12 is a + controller variant which supports 22b direct addressing. + + The most complicated part of the RL11 controller is the way it does + seeks. Seeking is relative to the current disk address; this requires + keeping accurate track of the current cylinder. The RL11 will not + switch heads or cross cylinders during transfers. + + The RL11 functions in three environments: + + - PDP-11 Q22 systems - the I/O map is one for one, so it's safe to + go through the I/O map + - PDP-11 Unibus 22b systems - the RL11 behaves as an 18b Unibus + peripheral and must go through the I/O map + - VAX Q22 systems - the RL11 must go through the I/O map +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "RL11 is not supported on the PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +extern uint32 cpu_opt; +#endif + +/* Constants */ + +#define RL_NUMWD 128 /* words/sector */ +#define RL_NUMSC 40 /* sectors/surface */ +#define RL_NUMSF 2 /* surfaces/cylinder */ +#define RL_NUMCY 256 /* cylinders/drive */ +#define RL_NUMDR 4 /* drives/controller */ +#define RL_MAXFR (1 << 16) /* max transfer */ +#define RL01_SIZE (RL_NUMCY * RL_NUMSF * RL_NUMSC * RL_NUMWD) /* words/drive */ +#define RL02_SIZE (RL01_SIZE * 2) /* words/drive */ + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_RL02 (UNIT_V_UF + 1) /* RL01 vs RL02 */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize enable */ +#define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ +#define UNIT_DUMMY (1 << UNIT_V_DUMMY) +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_RL02 (1u << UNIT_V_RL02) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protected */ + +/* Parameters in the unit descriptor */ + +#define TRK u3 /* current track */ +#define STAT u4 /* status */ + +/* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */ + +#define RLDS_LOAD 0 /* no cartridge */ +#define RLDS_LOCK 5 /* lock on */ +#define RLDS_BHO 0000010 /* brushes home NI */ +#define RLDS_HDO 0000020 /* heads out NI */ +#define RLDS_CVO 0000040 /* cover open NI */ +#define RLDS_HD 0000100 /* head select ^ */ +#define RLDS_RL02 0000200 /* RL02 */ +#define RLDS_DSE 0000400 /* drv sel err NI */ +#define RLDS_VCK 0001000 /* vol check * */ +#define RLDS_WGE 0002000 /* wr gate err * */ +#define RLDS_SPE 0004000 /* spin err * */ +#define RLDS_STO 0010000 /* seek time out NI */ +#define RLDS_WLK 0020000 /* wr locked */ +#define RLDS_HCE 0040000 /* hd curr err NI */ +#define RLDS_WDE 0100000 /* wr data err NI */ +#define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* att status */ +#define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unatt status */ +#define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \ + RLDS_VCK+RLDS_DSE) /* errors bits */ + +/* RLCS */ + +#define RLCS_DRDY 0000001 /* drive ready */ +#define RLCS_M_FUNC 0000007 /* function */ +#define RLCS_NOP 0 +#define RLCS_WCHK 1 +#define RLCS_GSTA 2 +#define RLCS_SEEK 3 +#define RLCS_RHDR 4 +#define RLCS_WRITE 5 +#define RLCS_READ 6 +#define RLCS_RNOHDR 7 +#define RLCS_V_FUNC 1 +#define RLCS_M_MEX 03 /* memory extension */ +#define RLCS_V_MEX 4 +#define RLCS_MEX (RLCS_M_MEX << RLCS_V_MEX) +#define RLCS_M_DRIVE 03 +#define RLCS_V_DRIVE 8 +#define RLCS_INCMP 0002000 /* incomplete */ +#define RLCS_CRC 0004000 /* CRC error */ +#define RLCS_HDE 0010000 /* header error */ +#define RLCS_NXM 0020000 /* non-exist memory */ +#define RLCS_DRE 0040000 /* drive error */ +#define RLCS_ERR 0100000 /* error summary */ +#define RLCS_ALLERR (RLCS_ERR+RLCS_DRE+RLCS_NXM+RLCS_HDE+RLCS_CRC+RLCS_INCMP) +#define RLCS_RW 0001776 /* read/write */ +#define GET_FUNC(x) (((x) >> RLCS_V_FUNC) & RLCS_M_FUNC) +#define GET_DRIVE(x) (((x) >> RLCS_V_DRIVE) & RLCS_M_DRIVE) + +/* RLDA */ + +#define RLDA_SK_DIR 0000004 /* direction */ +#define RLDA_GS_CLR 0000010 /* clear errors */ +#define RLDA_SK_HD 0000020 /* head select */ + +#define RLDA_V_SECT 0 /* sector */ +#define RLDA_M_SECT 077 +#define RLDA_V_TRACK 6 /* track */ +#define RLDA_M_TRACK 01777 +#define RLDA_HD0 (0 << RLDA_V_TRACK) +#define RLDA_HD1 (1u << RLDA_V_TRACK) +#define RLDA_V_CYL 7 /* cylinder */ +#define RLDA_M_CYL 0777 +#define RLDA_TRACK (RLDA_M_TRACK << RLDA_V_TRACK) +#define RLDA_CYL (RLDA_M_CYL << RLDA_V_CYL) +#define GET_SECT(x) (((x) >> RLDA_V_SECT) & RLDA_M_SECT) +#define GET_CYL(x) (((x) >> RLDA_V_CYL) & RLDA_M_CYL) +#define GET_TRACK(x) (((x) >> RLDA_V_TRACK) & RLDA_M_TRACK) +#define GET_DA(x) ((GET_TRACK (x) * RL_NUMSC) + GET_SECT (x)) + +/* RLBA */ + +#define RLBA_IMP 0177776 /* implemented */ + +/* RLBAE */ + +#define RLBAE_IMP 0000077 /* implemented */ + +extern int32 int_req[IPL_HLVL]; + +uint16 *rlxb = NULL; /* xfer buffer */ +int32 rlcs = 0; /* control/status */ +int32 rlba = 0; /* memory address */ +int32 rlbae = 0; /* mem addr extension */ +int32 rlda = 0; /* disk addr */ +int32 rlmp = 0, rlmp1 = 0, rlmp2 = 0; /* mp register queue */ +int32 rl_swait = 10; /* seek wait */ +int32 rl_rwait = 10; /* rotate wait */ +int32 rl_stopioe = 1; /* stop on error */ + +DEVICE rl_dev; +t_stat rl_rd (int32 *data, int32 PA, int32 access); +t_stat rl_wr (int32 data, int32 PA, int32 access); +t_stat rl_svc (UNIT *uptr); +t_stat rl_reset (DEVICE *dptr); +void rl_set_done (int32 error); +t_stat rl_boot (int32 unitno, DEVICE *dptr); +t_stat rl_attach (UNIT *uptr, char *cptr); +t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); +extern t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); + +/* RL11 data structures + + rl_dev RL device descriptor + rl_unit RL unit list + rl_reg RL register list + rl_mod RL modifier list +*/ + +DIB rl_dib = { + IOBA_RL, IOLN_RL, &rl_rd, &rl_wr, + 1, IVCL (RL), VEC_RL, { NULL } }; + +UNIT rl_unit[] = { + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_AUTO, RL01_SIZE) } + }; + +REG rl_reg[] = { + { GRDATA (RLCS, rlcs, DEV_RDX, 16, 0) }, + { GRDATA (RLDA, rlda, DEV_RDX, 16, 0) }, + { GRDATA (RLBA, rlba, DEV_RDX, 16, 0) }, + { GRDATA (RLBAE, rlbae, DEV_RDX, 6, 0) }, + { GRDATA (RLMP, rlmp, DEV_RDX, 16, 0) }, + { GRDATA (RLMP1, rlmp1, DEV_RDX, 16, 0) }, + { GRDATA (RLMP2, rlmp2, DEV_RDX, 16, 0) }, + { FLDATA (INT, IREQ (RL), INT_V_RL) }, + { FLDATA (ERR, rlcs, CSR_V_ERR) }, + { FLDATA (DONE, rlcs, CSR_V_DONE) }, + { FLDATA (IE, rlcs, CSR_V_IE) }, + { DRDATA (STIME, rl_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rl_rwait, 24), PV_LEFT }, + { URDATA (CAPAC, rl_unit[0].capac, 10, T_ADDR_W, 0, + RL_NUMDR, PV_LEFT + REG_HRO) }, + { FLDATA (STOP_IOE, rl_stopioe, 0) }, + { GRDATA (DEVADDR, rl_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, rl_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB rl_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad }, + { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL }, + { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size }, + { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size }, + { MTAB_XTD|MTAB_VDV, 010, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE rl_dev = { + "RL", rl_unit, rl_reg, rl_mod, + RL_NUMDR, DEV_RDX, 24, 1, DEV_RDX, 16, + NULL, NULL, &rl_reset, + &rl_boot, &rl_attach, NULL, + &rl_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS + }; + +/* I/O dispatch routines, I/O addresses 17774400 - 17774407 + + 17774400 RLCS read/write + 17774402 RLBA read/write + 17774404 RLDA read/write + 17774406 RLMP read/write + 17774410 RLBAE read/write +*/ + +t_stat rl_rd (int32 *data, int32 PA, int32 access) +{ +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<2:1> */ + + case 0: /* RLCS */ + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR; + uptr = rl_dev.units + GET_DRIVE (rlcs); + if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY; + else rlcs = rlcs | RLCS_DRDY; /* see if ready */ + *data = rlcs; + break; + + case 1: /* RLBA */ + *data = rlba & RLBA_IMP; + break; + + case 2: /* RLDA */ + *data = rlda; + break; + + case 3: /* RLMP */ + *data = rlmp; + rlmp = rlmp1; /* ripple data */ + rlmp1 = rlmp2; + break; + + case 4: /* RLBAE */ + if (UNIBUS) return SCPE_NXM; /* not in RL11 */ + *data = rlbae & RLBAE_IMP; + break; + } /* end switch */ + +return SCPE_OK; +} + +t_stat rl_wr (int32 data, int32 PA, int32 access) +{ +int32 curr, offs, newc, maxc; +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<2:1> */ + + case 0: /* RLCS */ + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + if (rlcs & RLCS_ALLERR) rlcs = rlcs | RLCS_ERR; + uptr = rl_dev.units + GET_DRIVE (data); /* get new drive */ + if (sim_is_active (uptr)) rlcs = rlcs & ~RLCS_DRDY; + else rlcs = rlcs | RLCS_DRDY; /* see if ready */ + + if (access == WRITEB) data = (PA & 1)? + (rlcs & 0377) | (data << 8): (rlcs & ~0377) | data; + rlcs = (rlcs & ~RLCS_RW) | (data & RLCS_RW); + rlbae = (rlbae & ~RLCS_M_MEX) | ((rlcs >> RLCS_V_MEX) & RLCS_M_MEX); + if (data & CSR_DONE) { /* ready set? */ + if ((data & CSR_IE) == 0) CLR_INT (RL); + else if ((rlcs & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (RL); + return SCPE_OK; + } + + CLR_INT (RL); /* clear interrupt */ + rlcs = rlcs & ~RLCS_ALLERR; /* clear errors */ + switch (GET_FUNC (rlcs)) { /* case on RLCS<3:1> */ + case RLCS_NOP: /* nop */ + rl_set_done (0); + break; + case RLCS_SEEK: /* seek */ + curr = GET_CYL (uptr->TRK); /* current cylinder */ + offs = GET_CYL (rlda); /* offset */ + if (rlda & RLDA_SK_DIR) { /* in or out? */ + newc = curr + offs; /* out */ + maxc = (uptr->flags & UNIT_RL02)? + RL_NUMCY * 2: RL_NUMCY; + if (newc >= maxc) newc = maxc - 1; + } + else { + newc = curr - offs; /* in */ + if (newc < 0) newc = 0; + } + uptr->TRK = (newc << RLDA_V_CYL) | /* put on track */ + ((rlda & RLDA_SK_HD)? RLDA_HD1: RLDA_HD0); + sim_activate (uptr, rl_swait * abs (newc - curr)); + break; + default: /* data transfer */ + sim_activate (uptr, rl_swait); /* activate unit */ + break; + } /* end switch func */ + break; /* end case RLCS */ + + case 1: /* RLBA */ + if (access == WRITEB) data = (PA & 1)? + (rlba & 0377) | (data << 8): (rlba & ~0377) | data; + rlba = data & RLBA_IMP; + break; + + case 2: /* RLDA */ + if (access == WRITEB) data = (PA & 1)? + (rlda & 0377) | (data << 8): (rlda & ~0377) | data; + rlda = data; + break; + + case 3: /* RLMP */ + if (access == WRITEB) data = (PA & 1)? + (rlmp & 0377) | (data << 8): (rlmp & ~0377) | data; + rlmp = rlmp1 = rlmp2 = data; + break; + + case 4: /* RLBAE */ + if (UNIBUS) return SCPE_NXM; /* not in RL11 */ + if (PA & 1) return SCPE_OK; + rlbae = data & RLBAE_IMP; + rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); + break; + } /* end switch */ + +return SCPE_OK; +} + +/* Service unit timeout + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder for + the current command. +*/ + +t_stat rl_svc (UNIT *uptr) +{ +int32 err, wc, maxwc, t; +int32 i, func, da, awc; +uint32 ma; +uint16 comp; + +func = GET_FUNC (rlcs); /* get function */ +if (func == RLCS_GSTA) { /* get status */ + if (rlda & RLDA_GS_CLR) uptr->STAT = uptr->STAT & ~RLDS_ERR; + rlmp = uptr->STAT | (uptr->TRK & RLDS_HD) | + ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT); + if (uptr->flags & UNIT_RL02) rlmp = rlmp | RLDS_RL02; + if (uptr->flags & UNIT_WPRT) rlmp = rlmp | RLDS_WLK; + rlmp2 = rlmp1 = rlmp; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + rlcs = rlcs & ~RLCS_DRDY; /* clear drive ready */ + uptr->STAT = uptr->STAT | RLDS_SPE; /* spin error */ + rl_set_done (RLCS_ERR | RLCS_INCMP); /* flag error */ + return IORETURN (rl_stopioe, SCPE_UNATT); + } + +if ((func == RLCS_WRITE) && (uptr->flags & UNIT_WPRT)) { + uptr->STAT = uptr->STAT | RLDS_WGE; /* write and locked */ + rl_set_done (RLCS_ERR | RLCS_DRE); + return SCPE_OK; + } + +if (func == RLCS_SEEK) { /* seek? */ + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (func == RLCS_RHDR) { /* read header? */ + rlmp = (uptr->TRK & RLDA_TRACK) | GET_SECT (rlda); + rlmp1 = rlmp2 = 0; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (((func != RLCS_RNOHDR) && ((uptr->TRK & RLDA_CYL) != (rlda & RLDA_CYL))) + || (GET_SECT (rlda) >= RL_NUMSC)) { /* bad cyl or sector? */ + rl_set_done (RLCS_ERR | RLCS_HDE | RLCS_INCMP); /* wrong cylinder? */ + return SCPE_OK; + } + +ma = (rlbae << 16) | rlba; /* get mem addr */ +da = GET_DA (rlda) * RL_NUMWD; /* get disk addr */ +wc = 0200000 - rlmp; /* get true wc */ + +maxwc = (RL_NUMSC - GET_SECT (rlda)) * RL_NUMWD; /* max transfer */ +if (wc > maxwc) wc = maxwc; /* track overrun? */ +err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); + +if ((func >= RLCS_READ) && (err == 0)) { /* read (no hdr)? */ + i = fxread (rlxb, sizeof (int16), wc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < wc; i++) rlxb[i] = 0; /* fill buffer */ + if (t = Map_WriteW (ma, wc << 1, rlxb)) { /* store buffer */ + rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ + wc = wc - t; /* adjust wc */ + } + } /* end read */ + +if ((func == RLCS_WRITE) && (err == 0)) { /* write? */ + if (t = Map_ReadW (ma, wc << 1, rlxb)) { /* fetch buffer */ + rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ + wc = wc - t; /* adj xfer lnt */ + } + if (wc) { /* any xfer? */ + awc = (wc + (RL_NUMWD - 1)) & ~(RL_NUMWD - 1); /* clr to */ + for (i = wc; i < awc; i++) rlxb[i] = 0; /* end of blk */ + fxwrite (rlxb, sizeof (int16), awc, uptr->fileref); + err = ferror (uptr->fileref); + } + } /* end write */ + +if ((func == RLCS_WCHK) && (err == 0)) { /* write check? */ + i = fxread (rlxb, sizeof (int16), wc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < wc; i++) rlxb[i] = 0; /* fill buffer */ + awc = wc; /* save wc */ + for (wc = 0; (err == 0) && (wc < awc); wc++) { /* loop thru buf */ + if (Map_ReadW (ma + (wc << 1), 2, &comp)) { /* mem wd */ + rlcs = rlcs | RLCS_ERR | RLCS_NXM; /* nxm */ + break; + } + if (comp != rlxb[wc]) /* check to buf */ + rlcs = rlcs | RLCS_ERR | RLCS_CRC; + } /* end for */ + } /* end wcheck */ + +rlmp = (rlmp + wc) & 0177777; /* final word count */ +if (rlmp != 0) rlcs = rlcs | RLCS_ERR | RLCS_INCMP; /* completed? */ +ma = ma + (wc << 1); /* final byte addr */ +rlbae = (ma >> 16) & RLBAE_IMP; /* upper 6b */ +rlba = ma & RLBA_IMP; /* lower 16b */ +rlcs = (rlcs & ~RLCS_MEX) | ((rlbae & RLCS_M_MEX) << RLCS_V_MEX); +rlda = rlda + ((wc + (RL_NUMWD - 1)) / RL_NUMWD); +rl_set_done (0); + +if (err != 0) { /* error? */ + perror ("RL I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Set done and possibly errors */ + +void rl_set_done (int32 status) +{ +rlcs = rlcs | status | CSR_DONE; /* set done */ +if (rlcs & CSR_IE) SET_INT (RL); +else CLR_INT (RL); +return; +} + +/* Device reset + + Note that the RL11 does NOT recalibrate its drives on RESET +*/ + +t_stat rl_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rlcs = CSR_DONE; +rlda = rlba = rlbae = rlmp = rlmp1 = rlmp2 = 0; +CLR_INT (RL); +for (i = 0; i < RL_NUMDR; i++) { + uptr = rl_dev.units + i; + sim_cancel (uptr); + uptr->STAT = 0; + } +if (rlxb == NULL) rlxb = (uint16 *) calloc (RL_MAXFR, sizeof (uint16)); +if (rlxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rl_attach (UNIT *uptr, char *cptr) +{ +uint32 p; +t_stat r; + +uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +uptr->TRK = 0; /* cylinder 0 */ +uptr->STAT = RLDS_VCK; /* new volume */ +if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ + if (uptr->flags & UNIT_RO) return SCPE_OK; /* if ro, done */ + return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); + } +if ((uptr->flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */ +if (p > (RL01_SIZE * sizeof (int16))) { + uptr->flags = uptr->flags | UNIT_RL02; + uptr->capac = RL02_SIZE; + } +else { + uptr->flags = uptr->flags & ~UNIT_RL02; + uptr->capac = RL01_SIZE; + } +return SCPE_OK; +} + +/* Set size routine */ + +t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +return SCPE_OK; +} + +/* Set bad block routine */ + +t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +return pdp11_bad_block (uptr, RL_NUMSC, RL_NUMWD); +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 020) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 0042114, /* "LD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012700, 0000000, /* MOV #unit, R0 */ + 0010003, /* MOV R0, R3 */ + 0000303, /* SWAB R3 */ + 0012701, 0174400, /* MOV #RLCS, R1 ; csr */ + 0012761, 0000013, 0000004, /* MOV #13, 4(R1) ; clr err */ + 0052703, 0000004, /* BIS #4, R3 ; unit+gstat */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0105003, /* CLRB R3 */ + 0052703, 0000010, /* BIS #10, R3 ; unit+rdhdr */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0016102, 0000006, /* MOV 6(R1), R2 ; get hdr */ + 0042702, 0000077, /* BIC #77, R2 ; clr sector */ + 0005202, /* INC R2 ; magic bit */ + 0010261, 0000004, /* MOV R2, 4(R1) ; seek to 0 */ + 0105003, /* CLRB R3 */ + 0052703, 0000006, /* BIS #6, R3 ; unit+seek */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0005061, 0000002, /* CLR 2(R1) ; clr ba */ + 0005061, 0000004, /* CLR 4(R1) ; clr da */ + 0012761, 0177000, 0000006, /* MOV #-512., 6(R1) ; set wc */ + 0105003, /* CLRB R3 */ + 0052703, 0000014, /* BIS #14, R3 ; unit+read */ + 0010311, /* MOV R3, (R1) ; issue cmd */ + 0105711, /* TSTB (R1) ; wait */ + 0100376, /* BPL .-2 */ + 0042711, 0000377, /* BIC #377, (R1) */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0005007 /* CLR PC */ + }; + +t_stat rl_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern uint16 *M; +extern int32 saved_PC; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RLCS_M_DRIVE; +M[BOOT_CSR >> 1] = rl_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat rl_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} + +#endif + diff --git a/PDP11/pdp11_rp.c b/PDP11/pdp11_rp.c new file mode 100644 index 0000000..627f07e --- /dev/null +++ b/PDP11/pdp11_rp.c @@ -0,0 +1,1076 @@ +/* pdp11_rp.c - RP04/05/06/07 RM02/03/05/80 Massbus disk controller + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rp RH/RP/RM moving head disks + + 17-May-07 RMS CS1 DVA resides in device, not MBA + 21-Nov-05 RMS Enable/disable device also enables/disables Massbus adapter + 12-Nov-05 RMS Fixed DriveClear, does not clear disk address + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 18-Mar-05 RMS Added attached test to detach routine + 12-Sep-04 RMS Cloned from pdp11_rp.c + + Note: The VMS driver and the RP controller documentation state that + + ER2 = offset 8 + SN = offset 12 + + But the TOPS-10 and TOPS-20 drivers, and the RP schematics state that + + SN = offset 8 + ER2 = offset 12 + + The simulation follows the schematics. The VMS drivers defines but does + not use these offsets, and the error logger follows the schematics. +*/ + +#if defined (VM_PDP10) +#error "PDP-10 uses pdp10_rp.c!" + +#elif defined (VM_PDP11) +#include "pdp11_defs.h" +#define INIT_DTYPE RM03_DTYPE +#define INIT_SIZE RM03_SIZE + +#elif defined (VM_VAX) +#include "vax_defs.h" +#define INIT_DTYPE RP06_DTYPE +#define INIT_SIZE RP06_SIZE +#define DMASK 0xFFFF +#if (!UNIBUS) +#error "Qbus not supported!" +#endif + +#endif + +#include + +#define RP_CTRL 0 /* ctrl is RP */ +#define RM_CTRL 1 /* ctrl is RM */ +#define RP_NUMDR 8 /* #drives */ +#define RP_NUMWD 256 /* words/sector */ +#define RP_MAXFR (1 << 16) /* max transfer */ +#define GET_SECTOR(x,d) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) drv_tab[d].sect))) +#define RM_OF (MBA_RMASK + 1) + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DTYPE (UNIT_V_UF + 1) /* disk type */ +#define UNIT_M_DTYPE 7 +#define UNIT_V_AUTO (UNIT_V_UF + 4) /* autosize */ +#define UNIT_V_DUMMY (UNIT_V_UF + 5) /* dummy flag */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_DUMMY (1 << UNIT_V_DUMMY) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ + +/* RPCS1, RMCS1 - control/status 1 - offset 0 */ + +#define RP_CS1_OF 0 +#define RM_CS1_OF (0 + RM_OF) +#define CS1_GO CSR_GO /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 037 /* function mask */ +#define CS1_N_FNC (CS1_M_FNC + 1) +#define FNC_NOP 000 /* no operation */ +#define FNC_UNLOAD 001 /* unload */ +#define FNC_SEEK 002 /* seek */ +#define FNC_RECAL 003 /* recalibrate */ +#define FNC_DCLR 004 /* drive clear */ +#define FNC_RELEASE 005 /* port release */ +#define FNC_OFFSET 006 /* offset */ +#define FNC_RETURN 007 /* return to center */ +#define FNC_PRESET 010 /* read-in preset */ +#define FNC_PACK 011 /* pack acknowledge */ +#define FNC_SEARCH 014 /* search */ +#define FNC_XFER 024 /* >=? data xfr */ +#define FNC_WCHK 024 /* write check */ +#define FNC_WRITE 030 /* write */ +#define FNC_WRITEH 031 /* write w/ headers */ +#define FNC_READ 034 /* read */ +#define FNC_READH 035 /* read w/ headers */ +#define CS1_RW 076 +#define CS1_DVA 04000 /* drive avail */ +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) + +/* RPDS, RMDS - drive status - offset 1 */ + +#define RP_DS_OF 1 +#define RM_DS_OF (1 + RM_OF) +#define DS_OFM 0000001 /* offset mode */ +#define DS_VV 0000100 /* volume valid */ +#define DS_RDY 0000200 /* drive ready */ +#define DS_DPR 0000400 /* drive present */ +#define DS_PGM 0001000 /* programable NI */ +#define DS_LST 0002000 /* last sector */ +#define DS_WRL 0004000 /* write locked */ +#define DS_MOL 0010000 /* medium online */ +#define DS_PIP 0020000 /* pos in progress */ +#define DS_ERR 0040000 /* error */ +#define DS_ATA 0100000 /* attention active */ +#define DS_MBZ 0000076 + +/* RPER1, RMER1 - error status 1 - offset 2 */ + +#define RP_ER1_OF 2 +#define RM_ER1_OF (2 + RM_OF) +#define ER1_ILF 0000001 /* illegal func */ +#define ER1_ILR 0000002 /* illegal register */ +#define ER1_RMR 0000004 /* reg mod refused */ +#define ER1_PAR 0000010 /* parity err */ +#define ER1_FER 0000020 /* format err NI */ +#define ER1_WCF 0000040 /* write clk fail NI */ +#define ER1_ECH 0000100 /* ECC hard err NI */ +#define ER1_HCE 0000200 /* hdr comp err NI */ +#define ER1_HCR 0000400 /* hdr CRC err NI */ +#define ER1_AOE 0001000 /* addr ovflo err */ +#define ER1_IAE 0002000 /* invalid addr err */ +#define ER1_WLE 0004000 /* write lock err */ +#define ER1_DTE 0010000 /* drive time err NI */ +#define ER1_OPI 0020000 /* op incomplete */ +#define ER1_UNS 0040000 /* drive unsafe */ +#define ER1_DCK 0100000 /* data check NI */ + +/* RPMR, RMMR - maintenace register - offset 3*/ + +#define RP_MR_OF 3 +#define RM_MR_OF (3 + RM_OF) + +/* RPAS, RMAS - attention summary - offset 4 */ + +#define RP_AS_OF 4 +#define RM_AS_OF (4 + RM_OF) +#define AS_U0 0000001 /* unit 0 flag */ + +/* RPDA, RMDA - sector/track - offset 5 */ + +#define RP_DA_OF 5 +#define RM_DA_OF (5 + RM_OF) +#define DA_V_SC 0 /* sector pos */ +#define DA_M_SC 077 /* sector mask */ +#define DA_V_SF 8 /* track pos */ +#define DA_M_SF 077 /* track mask */ +#define DA_MBZ 0140300 +#define GET_SC(x) (((x) >> DA_V_SC) & DA_M_SC) +#define GET_SF(x) (((x) >> DA_V_SF) & DA_M_SF) + +/* RPDT, RMDT - drive type - offset 6 */ + +#define RP_DT_OF 6 +#define RM_DT_OF (6 + RM_OF) + +/* RPLA, RMLA - look ahead register - offset 7 */ + +#define RP_LA_OF 7 +#define RM_LA_OF (7 + RM_OF) +#define LA_V_SC 6 /* sector pos */ + +/* RPSN, RMSN - serial number - offset 8 */ + +#define RP_SN_OF 8 +#define RM_SN_OF (8 + RM_OF) + +/* RPOF, RMOF - offset register - offset 9 */ + +#define RP_OF_OF 9 +#define RM_OF_OF (9 + RM_OF) +#define OF_HCI 0002000 /* hdr cmp inh NI */ +#define OF_ECI 0004000 /* ECC inhibit NI */ +#define OF_F22 0010000 /* format NI */ +#define OF_MBZ 0161400 + +/* RPDC, RMDC - desired cylinder - offset 10 */ + +#define RP_DC_OF 10 +#define RM_DC_OF (10 + RM_OF) +#define DC_V_CY 0 /* cylinder pos */ +#define DC_M_CY 01777 /* cylinder mask */ +#define DC_MBZ 0176000 +#define GET_CY(x) (((x) >> DC_V_CY) & DC_M_CY) +#define GET_DA(c,fs,d) ((((GET_CY (c) * drv_tab[d].surf) + \ + GET_SF (fs)) * drv_tab[d].sect) + GET_SC (fs)) + +/* RPCC - current cylinder - offset 11 + RMHR - holding register - offset 11 */ + +#define RP_CC_OF 11 +#define RM_HR_OF (11 + RM_OF) + +/* RPER2 - error status 2 - drive unsafe conditions - unimplemented - offset 12 + RMMR2 - maintenance register - unimplemented - offset 12 */ + +#define RP_ER2_OF 12 +#define RM_MR2_OF (12 + RM_OF) + +/* RPER3 - error status 3 - more unsafe conditions - unimplemented - offset 13 + RMER2 - error status 2 - unimplemented - offset 13 */ + +#define RP_ER3_OF 13 +#define RM_ER2_OF (13 + RM_OF) + +/* RPEC1, RMEC1 - ECC status 1 - unimplemented - offset 14 */ + +#define RP_EC1_OF 14 +#define RM_EC1_OF (14 + RM_OF) + +/* RPEC2, RMEC1 - ECC status 2 - unimplemented - offset 15 */ + +#define RP_EC2_OF 15 +#define RM_EC2_OF (15 + RM_OF) + +/* This controller supports many different disk drive types: + + type #sectors/ #surfaces/ #cylinders/ + surface cylinder drive + + RM02/3 32 5 823 =67MB + RP04/5 22 19 411 =88MB + RM80 31 14 559 =124MB + RP06 22 19 815 =176MB + RM05 32 19 823 =256MB + RP07 50 32 630 =516MB + + In theory, each drive can be a different type. The size field in + each unit selects the drive capacity for each drive and thus the + drive type. DISKS MUST BE DECLARED IN ASCENDING SIZE. + + Note: the RP07, despite its designation, belongs to the RM family +*/ + +#define RM03_DTYPE 0 +#define RM03_SECT 32 +#define RM03_SURF 5 +#define RM03_CYL 823 +#define RM03_DEV 020024 +#define RM03_SIZE (RM03_SECT * RM03_SURF * RM03_CYL * RP_NUMWD) + +#define RP04_DTYPE 1 +#define RP04_SECT 22 +#define RP04_SURF 19 +#define RP04_CYL 411 +#define RP04_DEV 020020 +#define RP04_SIZE (RP04_SECT * RP04_SURF * RP04_CYL * RP_NUMWD) + +#define RM80_DTYPE 2 +#define RM80_SECT 31 +#define RM80_SURF 14 +#define RM80_CYL 559 +#define RM80_DEV 020026 +#define RM80_SIZE (RM80_SECT * RM80_SURF * RM80_CYL * RP_NUMWD) + +#define RP06_DTYPE 3 +#define RP06_SECT 22 +#define RP06_SURF 19 +#define RP06_CYL 815 +#define RP06_DEV 020022 +#define RP06_SIZE (RP06_SECT * RP06_SURF * RP06_CYL * RP_NUMWD) + +#define RM05_DTYPE 4 +#define RM05_SECT 32 +#define RM05_SURF 19 +#define RM05_CYL 823 +#define RM05_DEV 020027 +#define RM05_SIZE (RM05_SECT * RM05_SURF * RM05_CYL * RP_NUMWD) + +#define RP07_DTYPE 5 +#define RP07_SECT 50 +#define RP07_SURF 32 +#define RP07_CYL 630 +#define RP07_DEV 020042 +#define RP07_SIZE (RP07_SECT * RP07_SURF * RP07_CYL * RP_NUMWD) + +#define RP_CTRL 0 +#define RM_CTRL 1 + +struct drvtyp { + int32 sect; /* sectors */ + int32 surf; /* surfaces */ + int32 cyl; /* cylinders */ + int32 size; /* #blocks */ + int32 devtype; /* device type */ + int32 ctrl; /* ctrl type */ + }; + +static struct drvtyp drv_tab[] = { + { RM03_SECT, RM03_SURF, RM03_CYL, RM03_SIZE, RM03_DEV, RM_CTRL }, + { RP04_SECT, RP04_SURF, RP04_CYL, RP04_SIZE, RP04_DEV, RP_CTRL }, + { RM80_SECT, RM80_SURF, RM80_CYL, RM80_SIZE, RM80_DEV, RM_CTRL }, + { RP06_SECT, RP06_SURF, RP06_CYL, RP06_SIZE, RP06_DEV, RP_CTRL }, + { RM05_SECT, RM05_SURF, RM05_CYL, RM05_SIZE, RM05_DEV, RM_CTRL }, + { RP07_SECT, RP07_SURF, RP07_CYL, RP07_SIZE, RP07_DEV, RM_CTRL }, + { 0 } + }; + +uint16 *rpxb = NULL; /* xfer buffer */ +uint16 rpcs1[RP_NUMDR] = { 0 }; /* control/status 1 */ +uint16 rpda[RP_NUMDR] = { 0 }; /* track/sector */ +uint16 rpds[RP_NUMDR] = { 0 }; /* drive status */ +uint16 rper1[RP_NUMDR] = { 0 }; /* error status 1 */ +uint16 rmhr[RP_NUMDR] = { 0 }; /* holding reg */ +uint16 rpmr[RP_NUMDR] = { 0 }; /* maint reg */ +uint16 rmmr2[RP_NUMDR] = { 0 }; /* maint reg 2 */ +uint16 rpof[RP_NUMDR] = { 0 }; /* offset */ +uint16 rpdc[RP_NUMDR] = { 0 }; /* cylinder */ +uint16 rper2[RP_NUMDR] = { 0 }; /* error status 2 */ +uint16 rper3[RP_NUMDR] = { 0 }; /* error status 3 */ +uint16 rpec1[RP_NUMDR] = { 0 }; /* ECC correction 1 */ +uint16 rpec2[RP_NUMDR] = { 0 }; /* ECC correction 2 */ +int32 rp_stopioe = 1; /* stop on error */ +int32 rp_swait = 10; /* seek time */ +int32 rp_rwait = 10; /* rotate time */ +static const char *rp_fname[CS1_N_FNC] = { + "NOP", "UNLD", "SEEK", "RECAL", "DCLR", "RLS", "OFFS", "RETN", + "PRESET", "PACK", "12", "13", "SCH", "15", "16", "17", + "20", "21", "22", "23", "WRCHK", "25", "26", "27", + "WRITE", "WRHDR", "32", "33", "READ", "RDHDR", "36", "37" + }; + +extern FILE *sim_deb; + +t_stat rp_mbrd (int32 *data, int32 ofs, int32 drv); +t_stat rp_mbwr (int32 data, int32 ofs, int32 drv); +t_stat rp_svc (UNIT *uptr); +t_stat rp_reset (DEVICE *dptr); +t_stat rp_attach (UNIT *uptr, char *cptr); +t_stat rp_detach (UNIT *uptr); +t_stat rp_boot (int32 unitno, DEVICE *dptr); +void rp_set_er (int32 flg, int32 drv); +void rp_clr_as (int32 mask); +void rp_update_ds (int32 flg, int32 drv); +t_stat rp_go (int32 drv); +t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rp_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 rp_abort (void); + +extern t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds); + +/* RP data structures + + rp_dev RP device descriptor + rp_unit RP unit list + rp_reg RP register list + rp_mod RP modifier list +*/ + +DIB rp_dib = { MBA_RP, 0, &rp_mbrd, &rp_mbwr, 0, 0, 0, { &rp_abort } }; + +UNIT rp_unit[] = { + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE+(INIT_DTYPE << UNIT_V_DTYPE), INIT_SIZE) } + }; + +REG rp_reg[] = { + { BRDATA (CS1, rpcs1, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (DA, rpda, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (DS, rpds, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (ER1, rper1, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (HR, rmhr, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (OF, rpof, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (DC, rpdc, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (ER2, rper2, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (ER3, rper3, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (EC1, rpec1, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (EC2, rpec2, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (MR, rpmr, DEV_RDX, 16, RP_NUMDR) }, + { BRDATA (MR2, rmmr2, DEV_RDX, 16, RP_NUMDR) }, + { DRDATA (STIME, rp_swait, 24), REG_NZ + PV_LEFT }, + { DRDATA (RTIME, rp_rwait, 24), REG_NZ + PV_LEFT }, + { URDATA (CAPAC, rp_unit[0].capac, 10, T_ADDR_W, 0, + RP_NUMDR, PV_LEFT | REG_HRO) }, + { FLDATA (STOP_IOE, rp_stopioe, 0) }, + { GRDATA (CTRLTYPE, rp_dib.lnt, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB rp_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MASSBUS", "MASSBUS", NULL, &mba_show_num }, + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rp_set_bad }, + { (UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM03", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP04", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM80", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP06", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RM05", NULL, NULL }, + { (UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE) + UNIT_ATT, + "RP07", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM03_DTYPE << UNIT_V_DTYPE), + "RM03", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP04_DTYPE << UNIT_V_DTYPE), + "RP04", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM80_DTYPE << UNIT_V_DTYPE), + "RM80", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP06_DTYPE << UNIT_V_DTYPE), + "RP06", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RM05_DTYPE << UNIT_V_DTYPE), + "RM05", NULL, NULL }, + { (UNIT_AUTO+UNIT_DTYPE+UNIT_ATT), (RP07_DTYPE << UNIT_V_DTYPE), + "RP07", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DTYPE), (RM03_DTYPE << UNIT_V_DTYPE), + NULL, "RM03", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP04_DTYPE << UNIT_V_DTYPE), + NULL, "RP04", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RM80_DTYPE << UNIT_V_DTYPE), + NULL, "RM80", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP06_DTYPE << UNIT_V_DTYPE), + NULL, "RP06", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RM05_DTYPE << UNIT_V_DTYPE), + NULL, "RM05", &rp_set_size }, + { (UNIT_AUTO+UNIT_DTYPE), (RP07_DTYPE << UNIT_V_DTYPE), + NULL, "RP07", &rp_set_size }, + { 0 } + }; + +DEVICE rp_dev = { + "RP", rp_unit, rp_reg, rp_mod, + RP_NUMDR, DEV_RDX, 30, 1, DEV_RDX, 16, + NULL, NULL, &rp_reset, + &rp_boot, &rp_attach, &rp_detach, + &rp_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_MBUS | DEV_DEBUG + }; + +/* Massbus register read */ + +t_stat rp_mbrd (int32 *data, int32 ofs, int32 drv) +{ +uint32 val, dtype, i; +UNIT *uptr; + +rp_update_ds (0, drv); /* update ds */ +uptr = rp_dev.units + drv; /* get unit */ +if (uptr->flags & UNIT_DIS) { /* nx disk */ + *data = 0; + return MBE_NXD; + } +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +ofs = ofs & MBA_RMASK; /* mask offset */ +if (drv_tab[dtype].ctrl == RM_CTRL) ofs = ofs + RM_OF; /* RM? convert */ + +switch (ofs) { /* decode offset */ + + case RP_CS1_OF: case RM_CS1_OF: /* RPCS1 */ + val = (rpcs1[drv] & CS1_RW) | CS1_DVA; /* DVA always set */ + break; + + case RP_DA_OF: case RM_DA_OF: /* RPDA */ + val = rpda[drv] = rpda[drv] & ~DA_MBZ; + break; + + case RP_DS_OF: case RM_DS_OF: /* RPDS */ + val = rpds[drv]; + break; + + case RP_ER1_OF: case RM_ER1_OF: /* RPER1 */ + val = rper1[drv]; + break; + + case RP_AS_OF: case RM_AS_OF: /* RPAS */ + val = 0; + for (i = 0; i < RP_NUMDR; i++) + if (rpds[i] & DS_ATA) val |= (AS_U0 << i); + break; + + case RP_LA_OF: case RM_LA_OF: /* RPLA */ + val = GET_SECTOR (rp_rwait, dtype) << LA_V_SC; + break; + + case RP_MR_OF: case RM_MR_OF: /* RPMR */ + val = rpmr[drv]; + break; + + case RP_DT_OF: case RM_DT_OF: /* RPDT */ + val = drv_tab[dtype].devtype; + break; + + case RP_SN_OF: case RM_SN_OF: /* RPSN */ + val = 020 | (drv + 1); + break; + + case RP_OF_OF: case RM_OF_OF: /* RPOF */ + val = rpof[drv] = rpof[drv] & ~OF_MBZ; + break; + + case RP_DC_OF: case RM_DC_OF: /* RPDC */ + val = rpdc[drv] = rpdc[drv] & ~DC_MBZ; + break; + + case RP_CC_OF: /* RPCC */ + val = rp_unit[drv].CYL; + break; + + case RP_ER2_OF: case RM_ER2_OF: /* RPER2 */ + val = rper2[drv]; + break; + + case RP_ER3_OF: /* RPER3 */ + val = rper3[drv]; + break; + + case RP_EC1_OF: case RM_EC1_OF: /* RPEC1 */ + val = rpec1[drv]; + break; + + case RP_EC2_OF: case RM_EC2_OF: /* RPEC2 */ + val = rpec2[drv]; + break; + + case RM_HR_OF: /* RMHR */ + val = rmhr[drv] ^ DMASK; + break; + + case RM_MR2_OF: /* RHMR2 */ + val = rmmr2[drv]; + break; + + default: /* all others */ + *data = 0; + return MBE_NXR; + } + +*data = val; +return SCPE_OK; +} + +/* Massbus register write */ + +t_stat rp_mbwr (int32 data, int32 ofs, int32 drv) +{ +int32 dtype; +UNIT *uptr; + +uptr = rp_dev.units + drv; /* get unit */ +if (uptr->flags & UNIT_DIS) return MBE_NXD; /* nx disk */ +if ((ofs != RP_AS_OF) && sim_is_active (uptr)) { /* unit busy? */ + rp_set_er (ER1_RMR, drv); /* won't write */ + rp_update_ds (0, drv); + return SCPE_OK; + } +rmhr[drv] = data; /* save write */ +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +ofs = ofs & MBA_RMASK; /* mask offset */ +if (drv_tab[dtype].ctrl == RM_CTRL) ofs = ofs + RM_OF; /* RM? convert */ + +switch (ofs) { /* decode PA<5:1> */ + + case RP_CS1_OF: case RM_CS1_OF: /* RPCS1 */ + rpcs1[drv] = data & CS1_RW; + if (data & CS1_GO) return rp_go (drv); /* start op */ + break; + + case RP_DA_OF: case RM_DA_OF: /* RPDA */ + rpda[drv] = data & ~DA_MBZ; + break; + + case RP_AS_OF: case RM_AS_OF: /* RPAS */ + rp_clr_as (data); + break; + + case RP_MR_OF: case RM_MR_OF: /* RPMR */ + rpmr[drv] = data; + break; + + case RP_OF_OF: case RM_OF_OF: /* RPOF */ + rpof[drv] = data & ~OF_MBZ; + break; + + case RP_DC_OF: case RM_DC_OF: /* RPDC */ + rpdc[drv] = data & ~DC_MBZ; + break; + + case RM_MR2_OF: /* RMMR2 */ + rmmr2[drv] = data; + break; + + case RP_ER1_OF: case RM_ER1_OF: /* RPER1 */ + case RP_DS_OF: case RM_DS_OF: /* RPDS */ + case RP_LA_OF: case RM_LA_OF: /* RPLA */ + case RP_DT_OF: case RM_DT_OF: /* RPDT */ + case RP_SN_OF: case RM_SN_OF: /* RPSN */ + case RP_CC_OF: /* RPCC */ + case RP_ER2_OF: case RM_ER2_OF: /* RPER2 */ + case RP_ER3_OF: /* RPER3 */ + case RP_EC1_OF: case RM_EC1_OF: /* RPEC1 */ + case RP_EC2_OF: case RM_EC2_OF: /* RPEC2 */ + case RM_HR_OF: /* RMHR */ + break; /* read only */ + + default: /* all others */ + return MBE_NXR; + } /* end switch */ + +rp_update_ds (0, drv); /* update status */ +return SCPE_OK; +} + +/* Initiate operation - unit not busy, function set */ + +t_stat rp_go (int32 drv) +{ +int32 dc, fnc, dtype, t; +UNIT *uptr; + +fnc = GET_FNC (rpcs1[drv]); /* get function */ +if (DEBUG_PRS (rp_dev)) fprintf (sim_deb, + ">>RP%d STRT: fnc=%s, ds=%o, cyl=%o, da=%o, er=%o\n", + drv, rp_fname[fnc], rpds[drv], rpdc[drv], rpda[drv], rper1[drv]); +uptr = rp_dev.units + drv; /* get unit */ +rp_clr_as (AS_U0 << drv); /* clear attention */ +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +dc = rpdc[drv]; /* assume seek, sch */ +if ((fnc != FNC_DCLR) && (rpds[drv] & DS_ERR)) { /* err & ~clear? */ + rp_set_er (ER1_ILF, drv); /* not allowed */ + rp_update_ds (DS_ATA, drv); /* set attention */ + return MBE_GOE; + } + +switch (fnc) { /* case on function */ + + case FNC_DCLR: /* drive clear */ + rper1[drv] = rper2[drv] = rper3[drv] = 0; /* clear errors */ + rpec2[drv] = 0; /* clear EC2 */ + if (drv_tab[dtype].ctrl == RM_CTRL) /* RM? */ + rpmr[drv] = 0; /* clear maint */ + else rpec1[drv] = 0; /* RP, clear EC1 */ + case FNC_NOP: /* no operation */ + case FNC_RELEASE: /* port release */ + return SCPE_OK; + + case FNC_PRESET: /* read-in preset */ + rpdc[drv] = 0; /* clear disk addr */ + rpda[drv] = 0; + rpof[drv] = 0; /* clear offset */ + case FNC_PACK: /* pack acknowledge */ + rpds[drv] = rpds[drv] | DS_VV; /* set volume valid */ + return SCPE_OK; + + case FNC_OFFSET: /* offset mode */ + case FNC_RETURN: + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rp_set_er (ER1_UNS, drv); /* unsafe */ + break; + } + rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ + sim_activate (uptr, rp_swait); /* time operation */ + return SCPE_OK; + + case FNC_UNLOAD: /* unload */ + case FNC_RECAL: /* recalibrate */ + dc = 0; /* seek to 0 */ + case FNC_SEEK: /* seek */ + case FNC_SEARCH: /* search */ + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rp_set_er (ER1_UNS, drv); /* unsafe */ + break; + } + if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ + (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ + (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ + rp_set_er (ER1_IAE, drv); + break; + } + rpds[drv] = (rpds[drv] & ~DS_RDY) | DS_PIP; /* set positioning */ + t = abs (dc - uptr->CYL); /* cyl diff */ + if (t == 0) t = 1; /* min time */ + sim_activate (uptr, rp_swait * t); /* schedule */ + uptr->CYL = dc; /* save cylinder */ + return SCPE_OK; + + case FNC_WRITEH: /* write headers */ + case FNC_WRITE: /* write */ + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + case FNC_READH: /* read headers */ + if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rp_set_er (ER1_UNS, drv); /* unsafe */ + break; + } + if ((GET_CY (dc) >= drv_tab[dtype].cyl) || /* bad cylinder */ + (GET_SF (rpda[drv]) >= drv_tab[dtype].surf) || /* bad surface */ + (GET_SC (rpda[drv]) >= drv_tab[dtype].sect)) { /* or bad sector? */ + rp_set_er (ER1_IAE, drv); + break; + } + rpds[drv] = rpds[drv] & ~DS_RDY; /* clear drive rdy */ + sim_activate (uptr, rp_rwait + (rp_swait * abs (dc - uptr->CYL))); + uptr->CYL = dc; /* save cylinder */ + return SCPE_OK; + + default: /* all others */ + rp_set_er (ER1_ILF, drv); /* not supported */ + break; + } + +rp_update_ds (DS_ATA, drv); /* set attn, req int */ +return MBE_GOE; +} + +/* Abort opertion - there is a data transfer in progress */ + +int32 rp_abort (void) +{ +return rp_reset (&rp_dev); +} + +/* Service unit timeout + + Complete movement or data transfer command + Unit must exist - can't remove an active unit + Unit must be attached - detach cancels in progress operations +*/ + +t_stat rp_svc (UNIT *uptr) +{ +int32 i, fnc, dtype, drv, err; +int32 wc, abc, awc, mbc, da; + +dtype = GET_DTYPE (uptr->flags); /* get drive type */ +drv = (int32) (uptr - rp_dev.units); /* get drv number */ +da = GET_DA (rpdc[drv], rpda[drv], dtype) * RP_NUMWD; /* get disk addr */ +fnc = GET_FNC (rpcs1[drv]); /* get function */ + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rp_set_er (ER1_UNS, drv); /* set drive error */ + if (fnc >= FNC_XFER) mba_set_don (rp_dib.ba); /* xfr? set done */ + rp_update_ds (DS_ATA, drv); /* set attn */ + return (rp_stopioe? SCPE_UNATT: SCPE_OK); + } +rpds[drv] = (rpds[drv] & ~DS_PIP) | DS_RDY; /* change drive status */ + +switch (fnc) { /* case on function */ + + case FNC_OFFSET: /* offset */ + rp_update_ds (DS_OFM | DS_ATA, drv); + break; + + case FNC_RETURN: /* return to centerline */ + rpds[drv] = rpds[drv] & ~DS_OFM; /* clear offset, set attn */ + rp_update_ds (DS_ATA, drv); + break; + + case FNC_UNLOAD: /* unload */ + rp_detach (uptr); /* detach unit */ + break; + + case FNC_RECAL: /* recalibrate */ + case FNC_SEARCH: /* search */ + case FNC_SEEK: /* seek */ + rp_update_ds (DS_ATA, drv); + break; + + case FNC_WRITE: /* write */ + if (uptr->flags & UNIT_WPRT) { /* write locked? */ + rp_set_er (ER1_WLE, drv); /* set drive error */ + mba_set_exc (rp_dib.ba); /* set exception */ + rp_update_ds (DS_ATA, drv); /* set attn */ + return SCPE_OK; + } + case FNC_WCHK: /* write check */ + case FNC_READ: /* read */ + case FNC_READH: /* read headers */ + err = fseek (uptr->fileref, da * sizeof (int16), SEEK_SET); + mbc = mba_get_bc (rp_dib.ba); /* get byte count */ + wc = (mbc + 1) >> 1; /* convert to words */ + if ((da + wc) > drv_tab[dtype].size) { /* disk overrun? */ + rp_set_er (ER1_AOE, drv); /* set err */ + wc = drv_tab[dtype].size - da; /* trim xfer */ + mbc = wc << 1; /* trim mb count */ + if (da >= drv_tab[dtype].size) { /* none left? */ + mba_set_exc (rp_dib.ba); /* set exception */ + rp_update_ds (DS_ATA, drv); /* set attn */ + break; + } + } + if (fnc == FNC_WRITE) { /* write? */ + abc = mba_rdbufW (rp_dib.ba, mbc, rpxb); /* get buffer */ + wc = (abc + 1) >> 1; /* actual # wds */ + awc = (wc + (RP_NUMWD - 1)) & ~(RP_NUMWD - 1); + for (i = wc; i < awc; i++) rpxb[i] = 0; /* fill buf */ + if (wc && !err) { /* write buf */ + fxwrite (rpxb, sizeof (uint16), awc, uptr->fileref); + err = ferror (uptr->fileref); + } + } /* end if wr */ + else { /* read or wchk */ + awc = fxread (rpxb, sizeof (uint16), wc, uptr->fileref); + err = ferror (uptr->fileref); + for (i = awc; i < wc; i++) rpxb[i] = 0; /* fill buf */ + if (fnc == FNC_WCHK) /* write check? */ + mba_chbufW (rp_dib.ba, mbc, rpxb); /* check vs mem */ + else mba_wrbufW (rp_dib.ba, mbc, rpxb); /* store in mem */ + } /* end if read */ + da = da + wc + (RP_NUMWD - 1); + if (da >= drv_tab[dtype].size) rpds[drv] = rpds[drv] | DS_LST; + da = da / RP_NUMWD; + rpda[drv] = da % drv_tab[dtype].sect; + da = da / drv_tab[dtype].sect; + rpda[drv] = rpda[drv] | ((da % drv_tab[dtype].surf) << DA_V_SF); + rpdc[drv] = da / drv_tab[dtype].surf; + uptr->CYL = rpdc[drv]; + + if (err != 0) { /* error? */ + rp_set_er (ER1_PAR, drv); /* set drive error */ + mba_set_exc (rp_dib.ba); /* set exception */ + rp_update_ds (DS_ATA, drv); + perror ("RP I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + + case FNC_WRITEH: /* write headers stub */ + mba_set_don (rp_dib.ba); /* set done */ + rp_update_ds (0, drv); /* update ds */ + break; + } /* end case func */ + +if (DEBUG_PRS (rp_dev)) fprintf (sim_deb, + ">>RP%d DONE: fnc=%s, ds=%o, cyl=%o, da=%o, er=%d\n", + drv, rp_fname[fnc], rpds[drv], rpdc[drv], rpda[drv], rper1[drv]); +return SCPE_OK; +} + +/* Set drive error */ + +void rp_set_er (int32 flag, int32 drv) +{ +rper1[drv] = rper1[drv] | flag; +rpds[drv] = rpds[drv] | DS_ATA; +mba_upd_ata (rp_dib.ba, 1); +return; +} + +/* Clear attention flags */ + +void rp_clr_as (int32 mask) +{ +uint32 i, as; + +for (i = as = 0; i < RP_NUMDR; i++) { + if (mask & (AS_U0 << i)) rpds[i] &= ~DS_ATA; + if (rpds[i] & DS_ATA) as = 1; + } +mba_upd_ata (rp_dib.ba, as); +return; +} + +/* Drive status update */ + +void rp_update_ds (int32 flag, int32 drv) +{ +if (rp_unit[drv].flags & UNIT_DIS) rpds[drv] = rper1[drv] = 0; +else rpds[drv] = (rpds[drv] | DS_DPR) & ~DS_PGM; +if (rp_unit[drv].flags & UNIT_ATT) rpds[drv] = rpds[drv] | DS_MOL; +else rpds[drv] = rpds[drv] & ~(DS_MOL | DS_VV | DS_RDY); +if (rper1[drv] | rper2[drv] | rper3[drv]) rpds[drv] = rpds[drv] | DS_ERR; +else rpds[drv] = rpds[drv] & ~DS_ERR; +rpds[drv] = rpds[drv] | flag; +if (flag & DS_ATA) mba_upd_ata (rp_dib.ba, 1); +return; +} + +/* Device reset */ + +t_stat rp_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +mba_set_enbdis (MBA_RP, rp_dev.flags & DEV_DIS); +for (i = 0; i < RP_NUMDR; i++) { + uptr = rp_dev.units + i; + sim_cancel (uptr); + uptr->CYL = 0; + if (uptr->flags & UNIT_ATT) rpds[i] = (rpds[i] & DS_VV) | + DS_DPR | DS_RDY | DS_MOL | ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); + else if (uptr->flags & UNIT_DIS) rpds[i] = 0; + else rpds[i] = DS_DPR; + rpcs1[i] = 0; + rper1[i] = 0; + rpof[i] = 0; + rpdc[i] = 0; + rpda[i] = 0; + rpmr[i] = 0; + rper2[i] = 0; + rper3[i] = 0; + rpec1[i] = 0; + rpec2[i] = 0; + rmmr2[i] = 0; + rmhr[i] = 0; + } +if (rpxb == NULL) rpxb = (uint16 *) calloc (RP_MAXFR, sizeof (uint16)); +if (rpxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Device attach */ + +t_stat rp_attach (UNIT *uptr, char *cptr) +{ +int32 drv, i, p; +t_stat r; + +uptr->capac = drv_tab[GET_DTYPE (uptr->flags)].size; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +drv = (int32) (uptr - rp_dev.units); /* get drv number */ +rpds[drv] = DS_MOL | DS_RDY | DS_DPR | /* upd drv status */ + ((uptr->flags & UNIT_WPRT)? DS_WRL: 0); +rper1[drv] = 0; +rp_update_ds (DS_ATA, drv); /* upd ctlr status */ + +if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ + if (uptr->flags & UNIT_RO) return SCPE_OK; + return pdp11_bad_block (uptr, + drv_tab[GET_DTYPE (uptr->flags)].sect, RP_NUMWD); + } +if ((uptr->flags & UNIT_AUTO) == 0) return SCPE_OK; /* autosize? */ +for (i = 0; drv_tab[i].sect != 0; i++) { + if (p <= (drv_tab[i].size * (int) sizeof (int16))) { + uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (i << UNIT_V_DTYPE); + uptr->capac = drv_tab[i].size; + return SCPE_OK; + } + } +return SCPE_OK; +} + +/* Device detach */ + +t_stat rp_detach (UNIT *uptr) +{ +int32 drv; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +drv = (int32) (uptr - rp_dev.units); /* get drv number */ +rpds[drv] = rpds[drv] & ~(DS_MOL | DS_RDY | DS_WRL | DS_VV | DS_OFM); +rp_update_ds (DS_ATA, drv); /* request intr */ +return detach_unit (uptr); +} + +/* Set size command validation routine */ + +t_stat rp_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 dtype = GET_DTYPE (val); + +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = drv_tab[dtype].size; +return SCPE_OK; +} + +/* Set bad block routine */ + +t_stat rp_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +return pdp11_bad_block (uptr, drv_tab[GET_DTYPE (uptr->flags)].sect, RP_NUMWD); +} + +/* Boot routine */ + +#if defined (VM_PDP11) + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 014) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16)) + +static const uint16 boot_rom[] = { + 0042102, /* "BD" */ + 0012706, BOOT_START, /* mov #boot_start, sp */ + 0012700, 0000000, /* mov #unit, r0 */ + 0012701, 0176700, /* mov #RPCS1, r1 */ + 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */ + 0010061, 0000010, /* mov r0, 10(r1) ; set unit */ + 0012711, 0000021, /* mov #RIP+GO, (r1) ; pack ack */ + 0012761, 0010000, 0000032, /* mov #FMT16B, 32(r1) ; 16b mode */ + 0012761, 0177000, 0000002, /* mov #-512., 2(r1) ; set wc */ + 0005061, 0000004, /* clr 4(r1) ; clr ba */ + 0005061, 0000006, /* clr 6(r1) ; clr da */ + 0005061, 0000034, /* clr 34(r1) ; clr cyl */ + 0012711, 0000071, /* mov #READ+GO, (r1) ; read */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0005002, /* clr R2 */ + 0005003, /* clr R3 */ + 0012704, BOOT_START+020, /* mov #start+020, r4 */ + 0005005, /* clr R5 */ + 0105011, /* clrb (r1) */ + 0005007 /* clr PC */ + }; + +t_stat rp_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; +UNIT *uptr = rp_dev.units + unitno; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & (RP_NUMDR - 1); +M[BOOT_CSR >> 1] = mba_get_csr (rp_dib.ba) & DMASK; +if (drv_tab[GET_DTYPE (uptr->flags)].ctrl == RP_CTRL) + M[BOOT_START >> 1] = 042102; /* "BD" */ +else M[BOOT_START >> 1] = 042122; /* "RD" */ +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat rp_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} + +#endif diff --git a/PDP11/pdp11_rq.c b/PDP11/pdp11_rq.c new file mode 100644 index 0000000..730d4b8 --- /dev/null +++ b/PDP11/pdp11_rq.c @@ -0,0 +1,2548 @@ +/* pdp11_rq.c: MSCP disk controller simulator + + Copyright (c) 2002-2007, Robert M Supnik + Derived from work by Stephen F. Shirron + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rq RQDX3 disk controller + + 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread + 31-Oct-05 RMS Fixed address width for large files + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) + 17-Jan-05 RMS Added more RA and RD disks + 31-Oct-04 RMS Added -L switch (LBNs) to RAUSER size specification + 01-Oct-04 RMS Revised Unibus interface + Changed to identify as UDA50 in Unibus configurations + Changed width to be 16b in all configurations + Changed default timing for VAX + 24-Jul-04 RMS VAX controllers luns start with 0 (from Andreas Cejna) + 05-Feb-04 RMS Revised for file I/O library + 25-Jan-04 RMS Revised for device debug support + 12-Jan-04 RMS Fixed bug in interrupt control (found by Tom Evans) + 07-Oct-03 RMS Fixed problem with multiple RAUSER drives + 17-Sep-03 RMS Fixed MB to LBN conversion to be more accurate + 11-Jul-03 RMS Fixed bug in user disk size (found by Chaskiel M Grundman) + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 27-Feb-03 RMS Added user-defined drive support + 26-Feb-03 RMS Fixed bug in vector calculation for VAXen + 22-Feb-03 RMS Fixed ordering bug in queue process + 12-Oct-02 RMS Added multicontroller support + 29-Sep-02 RMS Changed addressing to 18b in Unibus mode + Added variable address support to bootstrap + Added vector display support + Fixed status code in HBE error log + Consolidated MSCP/TMSCP header file + New data structures + 16-Aug-02 RMS Removed unused variables (found by David Hittner) + 04-May-02 RMS Fixed bug in polling loop for queued operations + 26-Mar-02 RMS Fixed bug, reset routine cleared UF_WPH + 09-Mar-02 RMS Adjusted delays for M+ timing bugs + 04-Mar-02 RMS Added delays to initialization for M+, RSTS/E + 16-Feb-02 RMS Fixed bugs in host timeout logic, boot + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Dec-01 RMS Revised show routines + 19-Dec-01 RMS Added bigger drives + 17-Dec-01 RMS Added queue process +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "RQDX3 not supported on PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +#define RQ_QTIME 100 +#define RQ_XTIME 200 +#define OLDPC fault_PC +extern int32 fault_PC; + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define RQ_QTIME 200 +#define RQ_XTIME 500 +#define OLDPC MMR2 +extern int32 MMR2; +extern int32 cpu_opt; +#endif + +#if !defined (RQ_NUMCT) +#define RQ_NUMCT 4 +#elif (RQ_NUMCT > 4) +#error "Assertion failure: RQ_NUMCT exceeds 4" +#endif + +#include "pdp11_uqssp.h" +#include "pdp11_mscp.h" + +#define UF_MSK (UF_CMR|UF_CMW) /* settable flags */ + +#define RQ_SH_MAX 24 /* max display wds */ +#define RQ_SH_PPL 8 /* wds per line */ +#define RQ_SH_DPL 4 /* desc per line */ +#define RQ_SH_RI 001 /* show rings */ +#define RQ_SH_FR 002 /* show free q */ +#define RQ_SH_RS 004 /* show resp q */ +#define RQ_SH_UN 010 /* show unit q's */ +#define RQ_SH_ALL 017 /* show all */ + +#define RQ_CLASS 1 /* RQ class */ +#define RQU_UQPM 6 /* UB port model */ +#define RQQ_UQPM 19 /* QB port model */ +#define RQ_UQPM (UNIBUS? RQU_UQPM: RQQ_UQPM) +#define RQU_MODEL 6 /* UB MSCP ctrl model */ +#define RQQ_MODEL 19 /* QB MSCP ctrl model */ +#define RQ_MODEL (UNIBUS? RQU_MODEL: RQQ_MODEL) +#define RQ_HVER 1 /* hardware version */ +#define RQ_SVER 3 /* software version */ +#define RQ_DHTMO 60 /* def host timeout */ +#define RQ_DCTMO 120 /* def ctrl timeout */ +#define RQ_NUMDR 4 /* # drives */ +#define RQ_NUMBY 512 /* bytes per block */ +#define RQ_MAXFR (1 << 16) /* max xfer */ + +#define UNIT_V_ONL (UNIT_V_UF + 0) /* online */ +#define UNIT_V_WLK (UNIT_V_UF + 1) /* hwre write lock */ +#define UNIT_V_ATP (UNIT_V_UF + 2) /* attn pending */ +#define UNIT_V_DTYPE (UNIT_V_UF + 3) /* drive type */ +#define UNIT_M_DTYPE 0xF +#define UNIT_ONL (1 << UNIT_V_ONL) +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_ATP (1 << UNIT_V_ATP) +#define UNIT_DTYPE (UNIT_M_DTYPE << UNIT_V_DTYPE) +#define GET_DTYPE(x) (((x) >> UNIT_V_DTYPE) & UNIT_M_DTYPE) +#define cpkt u3 /* current packet */ +#define pktq u4 /* packet queue */ +#define uf buf /* settable unit flags */ +#define cnum wait /* controller index */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write prot */ +#define RQ_RMV(u) ((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RMV)? \ + UF_RMV: 0) +#define RQ_WPH(u) (((drv_tab[GET_DTYPE (u->flags)].flgs & RQDF_RO) || \ + (u->flags & UNIT_WPRT))? UF_WPH: 0) + +#define CST_S1 0 /* init stage 1 */ +#define CST_S1_WR 1 /* stage 1 wrap */ +#define CST_S2 2 /* init stage 2 */ +#define CST_S3 3 /* init stage 3 */ +#define CST_S3_PPA 4 /* stage 3 sa wait */ +#define CST_S3_PPB 5 /* stage 3 ip wait */ +#define CST_S4 6 /* stage 4 */ +#define CST_UP 7 /* online */ +#define CST_DEAD 8 /* fatal error */ + +#define ERR 0 /* must be SCPE_OK! */ +#define OK 1 + +#define RQ_TIMER (RQ_NUMDR) +#define RQ_QUEUE (RQ_TIMER + 1) + +/* Internal packet management. The real RQDX3 manages its packets as true + linked lists. However, use of actual addresses in structures won't work + with save/restore. Accordingly, the packets are an arrayed structure, + and links are actually subscripts. To minimize complexity, packet[0] + is not used (0 = end of list), and the number of packets must be a power + of two. +*/ + +#define RQ_NPKTS 32 /* # packets (pwr of 2) */ +#define RQ_M_NPKTS (RQ_NPKTS - 1) /* mask */ +#define RQ_PKT_SIZE_W 32 /* payload size (wds) */ +#define RQ_PKT_SIZE (RQ_PKT_SIZE_W * sizeof (int16)) + +struct rqpkt { + int16 link; /* link to next */ + uint16 d[RQ_PKT_SIZE_W]; /* data */ + }; + +/* Packet payload extraction and insertion; cp defines controller */ + +#define GETP(p,w,f) ((cp->pak[p].d[w] >> w##_V_##f) & w##_M_##f) +#define GETP32(p,w) (((uint32) cp->pak[p].d[w]) | \ + (((uint32) cp->pak[p].d[(w)+1]) << 16)) +#define PUTP32(p,w,x) cp->pak[p].d[w] = (x) & 0xFFFF; \ + cp->pak[p].d[(w)+1] = ((x) >> 16) & 0xFFFF + +/* Disk formats. An RQDX3 disk consists of the following regions: + + XBNs Extended blocks - contain information about disk format, + also holds track being reformatted during bad block repl. + Size = sectors/track + 1, replicated 3 times. + DBNs Diagnostic blocks - used by diagnostics. Sized to pad + out the XBNs to a cylinder boundary. + LBNs Logical blocks - contain user information. + RCT Replacement control table - first block contains status, + second contains data from block being replaced, remaining + contain information about replaced bad blocks. + Size = RBNs/128 + 3, replicated 4-8 times. + RBNs Replacement blocks - used to replace bad blocks. + + The simulator does not need to perform bad block replacement; the + information below is for simulating RCT reads, if required. + + Note that an RA drive has a different order: LBNs, RCT, XBN, DBN; + the RBNs are spare blocks at the end of every track. +*/ + +#define RCT_OVHD 2 /* #ovhd blks */ +#define RCT_ENTB 128 /* entries/blk */ +#define RCT_END 0x80000000 /* marks RCT end */ + +/* The RQDX3 supports multiple disk drive types (x = not implemented): + + type sec surf cyl tpg gpc RCT LBNs + + RX50 10 1 80 5 16 - 800 + RX33 15 2 80 2 1 - 2400 + RD51 18 4 306 4 1 36*4 21600 + RD31 17 4 615 4 1 3*8 41560 + RD52 17 8 512 8 1 4*8 60480 +x RD32 17 6 820 ? ? ? 83236 +x RD33 17 7 1170 ? ? ? 138565 + RD53 17 7 1024 7 1 5*8 138672 + RD54 17 15 1225 15 1 7*8 311200 + + The simulator also supports larger drives that only existed + on SDI controllers. + + RA60 42(+1) 6 1600 6 1 1008 400176 +x RA70 33(+1) 11 1507+ 11 1 ? 547041 + RA81 51(+1) 14 1258 14 1 2856 891072 + RA82 57(+1) 15 1435 15 1 3420 1216665 + RA71 51(+1) 14 1921 14 1 1428 1367310 + RA72 51(+1) 20 1921 20 1 2040 1953300 + RA90 69(+1) 13 2656 13 1 1794 2376153 + RA92 73(+1) 13 3101 13 1 949 2940951 +x RA73 70(+1) 21 2667+ 21 1 ? 3920490 + + Each drive can be a different type. The drive field in the + unit flags specified the drive type and thus, indirectly, + the drive size. +*/ + +#define RQDF_RMV 01 /* removable */ +#define RQDF_RO 02 /* read only */ +#define RQDF_SDI 04 /* SDI drive */ + +#define RX50_DTYPE 0 +#define RX50_SECT 10 +#define RX50_SURF 1 +#define RX50_CYL 80 +#define RX50_TPG 5 +#define RX50_GPC 16 +#define RX50_XBN 0 +#define RX50_DBN 0 +#define RX50_LBN 800 +#define RX50_RCTS 0 +#define RX50_RCTC 0 +#define RX50_RBN 0 +#define RX50_MOD 7 +#define RX50_MED 0x25658032 +#define RX50_FLGS RQDF_RMV + +#define RX33_DTYPE 1 +#define RX33_SECT 15 +#define RX33_SURF 2 +#define RX33_CYL 80 +#define RX33_TPG 2 +#define RX33_GPC 1 +#define RX33_XBN 0 +#define RX33_DBN 0 +#define RX33_LBN 2400 +#define RX33_RCTS 0 +#define RX33_RCTC 0 +#define RX33_RBN 0 +#define RX33_MOD 10 +#define RX33_MED 0x25658021 +#define RX33_FLGS RQDF_RMV + +#define RD51_DTYPE 2 +#define RD51_SECT 18 +#define RD51_SURF 4 +#define RD51_CYL 306 +#define RD51_TPG 4 +#define RD51_GPC 1 +#define RD51_XBN 57 +#define RD51_DBN 87 +#define RD51_LBN 21600 +#define RD51_RCTS 36 +#define RD51_RCTC 4 +#define RD51_RBN 144 +#define RD51_MOD 6 +#define RD51_MED 0x25644033 +#define RD51_FLGS 0 + +#define RD31_DTYPE 3 +#define RD31_SECT 17 +#define RD31_SURF 4 +#define RD31_CYL 615 /* last unused */ +#define RD31_TPG RD31_SURF +#define RD31_GPC 1 +#define RD31_XBN 54 +#define RD31_DBN 14 +#define RD31_LBN 41560 +#define RD31_RCTS 3 +#define RD31_RCTC 8 +#define RD31_RBN 100 +#define RD31_MOD 12 +#define RD31_MED 0x2564401F +#define RD31_FLGS 0 + +#define RD52_DTYPE 4 /* Quantum params */ +#define RD52_SECT 17 +#define RD52_SURF 8 +#define RD52_CYL 512 +#define RD52_TPG RD52_SURF +#define RD52_GPC 1 +#define RD52_XBN 54 +#define RD52_DBN 82 +#define RD52_LBN 60480 +#define RD52_RCTS 4 +#define RD52_RCTC 8 +#define RD52_RBN 168 +#define RD52_MOD 8 +#define RD52_MED 0x25644034 +#define RD52_FLGS 0 + +#define RD53_DTYPE 5 +#define RD53_SECT 17 +#define RD53_SURF 8 +#define RD53_CYL 1024 /* last unused */ +#define RD53_TPG RD53_SURF +#define RD53_GPC 1 +#define RD53_XBN 54 +#define RD53_DBN 82 +#define RD53_LBN 138672 +#define RD53_RCTS 5 +#define RD53_RCTC 8 +#define RD53_RBN 280 +#define RD53_MOD 9 +#define RD53_MED 0x25644035 +#define RD53_FLGS 0 + +#define RD54_DTYPE 6 +#define RD54_SECT 17 +#define RD54_SURF 15 +#define RD54_CYL 1225 /* last unused */ +#define RD54_TPG RD54_SURF +#define RD54_GPC 1 +#define RD54_XBN 54 +#define RD54_DBN 201 +#define RD54_LBN 311200 +#define RD54_RCTS 7 +#define RD54_RCTC 8 +#define RD54_RBN 609 +#define RD54_MOD 13 +#define RD54_MED 0x25644036 +#define RD54_FLGS 0 + +#define RA82_DTYPE 7 /* SDI drive */ +#define RA82_SECT 57 /* +1 spare/track */ +#define RA82_SURF 15 +#define RA82_CYL 1435 /* 0-1422 user */ +#define RA82_TPG RA82_SURF +#define RA82_GPC 1 +#define RA82_XBN 3480 /* cyl 1427-1430 */ +#define RA82_DBN 3480 /* cyl 1431-1434 */ +#define RA82_LBN 1216665 /* 57*15*1423 */ +#define RA82_RCTS 3420 /* cyl 1423-1426 */ +#define RA82_RCTC 1 +#define RA82_RBN 21345 /* 1 *15*1423 */ +#define RA82_MOD 11 +#define RA82_MED 0x25641052 +#define RA82_FLGS RQDF_SDI + +#define RRD40_DTYPE 8 +#define RRD40_SECT 128 +#define RRD40_SURF 1 +#define RRD40_CYL 10400 +#define RRD40_TPG RRD40_SURF +#define RRD40_GPC 1 +#define RRD40_XBN 0 +#define RRD40_DBN 0 +#define RRD40_LBN 1331200 +#define RRD40_RCTS 0 +#define RRD40_RCTC 0 +#define RRD40_RBN 0 +#define RRD40_MOD 26 +#define RRD40_MED 0x25652228 +#define RRD40_FLGS (RQDF_RMV | RQDF_RO) + +#define RA72_DTYPE 9 /* SDI drive */ +#define RA72_SECT 51 /* +1 spare/trk */ +#define RA72_SURF 20 +#define RA72_CYL 1921 /* 0-1914 user */ +#define RA72_TPG RA72_SURF +#define RA72_GPC 1 +#define RA72_XBN 2080 /* cyl 1917-1918? */ +#define RA72_DBN 2080 /* cyl 1920-1921? */ +#define RA72_LBN 1953300 /* 51*20*1915 */ +#define RA72_RCTS 2040 /* cyl 1915-1916? */ +#define RA72_RCTC 1 +#define RA72_RBN 38300 /* 1 *20*1915 */ +#define RA72_MOD 37 +#define RA72_MED 0x25641048 +#define RA72_FLGS RQDF_SDI + +#define RA90_DTYPE 10 /* SDI drive */ +#define RA90_SECT 69 /* +1 spare/trk */ +#define RA90_SURF 13 +#define RA90_CYL 2656 /* 0-2648 user */ +#define RA90_TPG RA90_SURF +#define RA90_GPC 1 +#define RA90_XBN 1820 /* cyl 2651-2652? */ +#define RA90_DBN 1820 /* cyl 2653-2654? */ +#define RA90_LBN 2376153 /* 69*13*2649 */ +#define RA90_RCTS 1794 /* cyl 2649-2650? */ +#define RA90_RCTC 1 +#define RA90_RBN 34437 /* 1 *13*2649 */ +#define RA90_MOD 19 +#define RA90_MED 0x2564105A +#define RA90_FLGS RQDF_SDI + +#define RA92_DTYPE 11 /* SDI drive */ +#define RA92_SECT 73 /* +1 spare/trk */ +#define RA92_SURF 13 +#define RA92_CYL 3101 /* 0-3098 user */ +#define RA92_TPG RA92_SURF +#define RA92_GPC 1 +#define RA92_XBN 174 /* cyl 3100? */ +#define RA92_DBN 788 +#define RA92_LBN 2940951 /* 73*13*3099 */ +#define RA92_RCTS 949 /* cyl 3099? */ +#define RA92_RCTC 1 +#define RA92_RBN 40287 /* 1 *13*3099 */ +#define RA92_MOD 29 +#define RA92_MED 0x2564105C +#define RA92_FLGS RQDF_SDI + +#define RA8U_DTYPE 12 /* user defined */ +#define RA8U_SECT 57 /* from RA82 */ +#define RA8U_SURF 15 +#define RA8U_CYL 1435 /* from RA82 */ +#define RA8U_TPG RA8U_SURF +#define RA8U_GPC 1 +#define RA8U_XBN 0 +#define RA8U_DBN 0 +#define RA8U_LBN 1216665 /* from RA82 */ +#define RA8U_RCTS 400 +#define RA8U_RCTC 8 +#define RA8U_RBN 21345 +#define RA8U_MOD 11 /* RA82 */ +#define RA8U_MED 0x25641052 /* RA82 */ +#define RA8U_FLGS RQDF_SDI +#define RA8U_MINC 10000 /* min cap LBNs */ +#define RA8U_MAXC 4000000 /* max cap LBNs */ +#define RA8U_EMAXC 2000000000 /* ext max cap */ + +#define RA60_DTYPE 13 /* SDI drive */ +#define RA60_SECT 42 /* +1 spare/track */ +#define RA60_SURF 6 +#define RA60_CYL 1600 /* 0-1587 user */ +#define RA60_TPG RA60_SURF +#define RA60_GPC 1 +#define RA60_XBN 1032 /* cyl 1592-1595 */ +#define RA60_DBN 1032 /* cyl 1596-1599 */ +#define RA60_LBN 400176 /* 42*6*1588 */ +#define RA60_RCTS 1008 /* cyl 1588-1591 */ +#define RA60_RCTC 1 +#define RA60_RBN 9528 /* 1 *6*1588 */ +#define RA60_MOD 4 +#define RA60_MED 0x22A4103C +#define RA60_FLGS (RQDF_RMV | RQDF_SDI) + +#define RA81_DTYPE 14 /* SDI drive */ +#define RA81_SECT 51 /* +1 spare/track */ +#define RA81_SURF 14 +#define RA81_CYL 1258 /* 0-1247 user */ +#define RA81_TPG RA81_SURF +#define RA81_GPC 1 +#define RA81_XBN 2436 /* cyl 1252-1254? */ +#define RA81_DBN 2436 /* cyl 1255-1256? */ +#define RA81_LBN 891072 /* 51*14*1248 */ +#define RA81_RCTS 2856 /* cyl 1248-1251? */ +#define RA81_RCTC 1 +#define RA81_RBN 17472 /* 1 *14*1248 */ +#define RA81_MOD 5 +#define RA81_MED 0x25641051 +#define RA81_FLGS RQDF_SDI + +#define RA71_DTYPE 15 /* SDI drive */ +#define RA71_SECT 51 /* +1 spare/track */ +#define RA71_SURF 14 +#define RA71_CYL 1921 /* 0-1914 user */ +#define RA71_TPG RA71_SURF +#define RA71_GPC 1 +#define RA71_XBN 1456 /* cyl 1917-1918? */ +#define RA71_DBN 1456 /* cyl 1919-1920? */ +#define RA71_LBN 1367310 /* 51*14*1915 */ +#define RA71_RCTS 1428 /* cyl 1915-1916? */ +#define RA71_RCTC 1 +#define RA71_RBN 26810 /* 1 *14*1915 */ +#define RA71_MOD 40 +#define RA71_MED 0x25641047 +#define RA71_FLGS RQDF_SDI + +struct drvtyp { + int32 sect; /* sectors */ + int32 surf; /* surfaces */ + int32 cyl; /* cylinders */ + int32 tpg; /* trk/grp */ + int32 gpc; /* grp/cyl */ + int32 xbn; /* XBN size */ + int32 dbn; /* DBN size */ + uint32 lbn; /* LBN size */ + int32 rcts; /* RCT size */ + int32 rctc; /* RCT copies */ + int32 rbn; /* RBNs */ + int32 mod; /* MSCP model */ + int32 med; /* MSCP media */ + int32 flgs; /* flags */ + char *name; /* name */ + }; + +#define RQ_DRV(d) \ + d##_SECT, d##_SURF, d##_CYL, d##_TPG, \ + d##_GPC, d##_XBN, d##_DBN, d##_LBN, \ + d##_RCTS, d##_RCTC, d##_RBN, d##_MOD, \ + d##_MED, d##_FLGS +#define RQ_SIZE(d) (d##_LBN * RQ_NUMBY) + +static struct drvtyp drv_tab[] = { + { RQ_DRV (RX50), "RX50" }, { RQ_DRV (RX33), "RX33" }, + { RQ_DRV (RD51), "RD51" }, { RQ_DRV (RD31), "RD31" }, + { RQ_DRV (RD52), "RD52" }, { RQ_DRV (RD53), "RD53" }, + { RQ_DRV (RD54), "RD54" }, { RQ_DRV (RA82), "RA82" }, + { RQ_DRV (RRD40), "RRD40" }, { RQ_DRV (RA72), "RA72" }, + { RQ_DRV (RA90), "RA90" }, { RQ_DRV (RA92), "RA92" }, + { RQ_DRV (RA8U), "RAUSER" }, { RQ_DRV (RA60), "RA60" }, + { RQ_DRV (RA81), "RA81" }, { RQ_DRV (RA71), "RA71" }, + { 0 } + }; + +extern int32 int_req[IPL_HLVL]; +extern int32 tmr_poll, clk_tps; +extern UNIT cpu_unit; +extern FILE *sim_deb; +extern uint32 sim_taddr_64; +extern int32 sim_switches; + +uint16 *rqxb = NULL; /* xfer buffer */ +int32 rq_itime = 200; /* init time, except */ +int32 rq_itime4 = 10; /* stage 4 */ +int32 rq_qtime = RQ_QTIME; /* queue time */ +int32 rq_xtime = RQ_XTIME; /* transfer time */ + +typedef struct { + uint32 cnum; /* ctrl number */ + uint32 ubase; /* unit base */ + uint32 sa; /* status, addr */ + uint32 saw; /* written data */ + uint32 s1dat; /* S1 data */ + uint32 comm; /* comm region */ + uint32 csta; /* ctrl state */ + uint32 perr; /* last error */ + uint32 cflgs; /* ctrl flags */ + uint32 irq; /* intr request */ + uint32 prgi; /* purge int */ + uint32 pip; /* poll in progress */ + int32 freq; /* free list */ + int32 rspq; /* resp list */ + uint32 pbsy; /* #busy pkts */ + uint32 credits; /* credits */ + uint32 hat; /* host timer */ + uint32 htmo; /* host timeout */ + struct uq_ring cq; /* cmd ring */ + struct uq_ring rq; /* rsp ring */ + struct rqpkt pak[RQ_NPKTS]; /* packet queue */ + } MSC; + +DEVICE rq_dev, rqb_dev, rqc_dev,rqd_dev; + +t_stat rq_rd (int32 *data, int32 PA, int32 access); +t_stat rq_wr (int32 data, int32 PA, int32 access); +t_stat rq_svc (UNIT *uptr); +t_stat rq_tmrsvc (UNIT *uptr); +t_stat rq_quesvc (UNIT *uptr); +t_stat rq_reset (DEVICE *dptr); +t_stat rq_attach (UNIT *uptr, char *cptr); +t_stat rq_detach (UNIT *uptr); +t_stat rq_boot (int32 unitno, DEVICE *dptr); +t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat rq_show_wlk (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat rq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat rq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc); + +t_bool rq_step4 (MSC *cp); +t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q); +t_bool rq_abo (MSC *cp, int32 pkt, t_bool q); +t_bool rq_avl (MSC *cp, int32 pkt, t_bool q); +t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q); +t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q); +t_bool rq_gus (MSC *cp, int32 pkt, t_bool q); +t_bool rq_onl (MSC *cp, int32 pkt, t_bool q); +t_bool rq_rw (MSC *cp, int32 pkt, t_bool q); +t_bool rq_scc (MSC *cp, int32 pkt, t_bool q); +t_bool rq_suc (MSC *cp, int32 pkt, t_bool q); +t_bool rq_plf (MSC *cp, uint32 err); +t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err); +t_bool rq_hbe (MSC *cp, UNIT *uptr); +t_bool rq_una (MSC *cp, int32 un); +t_bool rq_deqf (MSC *cp, int32 *pkt); +int32 rq_deqh (MSC *cp, int32 *lh); +void rq_enqh (MSC *cp, int32 *lh, int32 pkt); +void rq_enqt (MSC *cp, int32 *lh, int32 pkt); +t_bool rq_getpkt (MSC *cp, int32 *pkt); +t_bool rq_putpkt (MSC *cp, int32 pkt, t_bool qt); +t_bool rq_getdesc (MSC *cp, struct uq_ring *ring, uint32 *desc); +t_bool rq_putdesc (MSC *cp, struct uq_ring *ring, uint32 desc); +int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd); +t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts); +void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg, + uint32 sts, uint32 lnt, uint32 typ); +void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all); +void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr); +void rq_init_int (MSC *cp); +void rq_ring_int (MSC *cp, struct uq_ring *ring); +t_bool rq_fatal (MSC *cp, uint32 err); +UNIT *rq_getucb (MSC *cp, uint32 lu); +int32 rq_map_pa (uint32 pa); +void rq_setint (MSC *cp); +void rq_clrint (MSC *cp); +int32 rq_inta (void); + +/* RQ data structures + + rq_dev RQ device descriptor + rq_unit RQ unit list + rq_reg RQ register list + rq_mod RQ modifier list +*/ + +MSC rq_ctx = { 0 }; + +DIB rq_dib = { + IOBA_RQ, IOLN_RQ, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT rq_unit[] = { + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RX50_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RX50)) }, + { UDATA (&rq_tmrsvc, UNIT_IDLE|UNIT_DIS, 0) }, + { UDATA (&rq_quesvc, UNIT_DIS, 0) } + }; + +REG rq_reg[] = { + { GRDATA (SA, rq_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA (SAW, rq_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA (S1DAT, rq_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA (COMM, rq_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQBA, rq_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA (CQLNT, rq_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (CQIDX, rq_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQBA, rq_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA (RQLNT, rq_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (RQIDX, rq_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA (FREE, rq_ctx.freq, 5) }, + { DRDATA (RESP, rq_ctx.rspq, 5) }, + { DRDATA (PBSY, rq_ctx.pbsy, 5) }, + { GRDATA (CFLGS, rq_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA (CSTA, rq_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA (PERR, rq_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA (CRED, rq_ctx.credits, 5) }, + { DRDATA (HAT, rq_ctx.hat, 17) }, + { DRDATA (HTMO, rq_ctx.htmo, 17) }, + { FLDATA (PRGI, rq_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA (PIP, rq_ctx.pip, 0), REG_HIDDEN }, + { FLDATA (INT, rq_ctx.irq, 0) }, + { DRDATA (ITIME, rq_itime, 24), PV_LEFT + REG_NZ }, + { DRDATA (I4TIME, rq_itime4, 24), PV_LEFT + REG_NZ }, + { DRDATA (QTIME, rq_qtime, 24), PV_LEFT + REG_NZ }, + { DRDATA (XTIME, rq_xtime, 24), PV_LEFT + REG_NZ }, + { BRDATA (PKTS, rq_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA (CPKT, rq_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (PKTQ, rq_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (UFLG, rq_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA (CAPAC, rq_unit[0].capac, 10, T_ADDR_W, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA (DEVADDR, rq_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, rq_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { DRDATA (DEVLBN, drv_tab[RA8U_DTYPE].lbn, 22), REG_HRO }, + { NULL } + }; + +MTAB rq_mod[] = { + { UNIT_WLK, 0, NULL, "WRITEENABLED", &rq_set_wlk }, + { UNIT_WLK, UNIT_WLK, NULL, "LOCKED", &rq_set_wlk }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RI, "RINGS", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_FR, "FREEQ", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_RS, "RESPQ", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_UN, "UNITQ", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, RQ_SH_ALL, "ALL", NULL, + NULL, &rq_show_ctrl, 0 }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL, + NULL, &rq_show_unitq, 0 }, + { MTAB_XTD | MTAB_VUN, 0, "WRITE", NULL, + NULL, &rq_show_wlk, NULL }, + { MTAB_XTD | MTAB_VUN, RX50_DTYPE, NULL, "RX50", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RX33_DTYPE, NULL, "RX33", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD31_DTYPE, NULL, "RD31", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD51_DTYPE, NULL, "RD51", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD52_DTYPE, NULL, "RD52", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD53_DTYPE, NULL, "RD53", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RD54_DTYPE, NULL, "RD54", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA60_DTYPE, NULL, "RA60", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA81_DTYPE, NULL, "RA81", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA82_DTYPE, NULL, "RA82", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "RRD40", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RRD40_DTYPE, NULL, "CDROM", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA71_DTYPE, NULL, "RA71", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA72_DTYPE, NULL, "RA72", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA90_DTYPE, NULL, "RA90", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA92_DTYPE, NULL, "RA92", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, RA8U_DTYPE, NULL, "RAUSER", + &rq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VUN, 0, "TYPE", NULL, + NULL, &rq_show_type, NULL }, +#if defined (VM_PDP11) + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, +#else + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, +#endif + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, + { 0 } + }; + +DEVICE rq_dev = { + "RQ", rq_unit, rq_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rq_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG + }; + +/* RQB data structures + + rqb_dev RQB device descriptor + rqb_unit RQB unit list + rqb_reg RQB register list + rqb_mod RQB modifier list +*/ + +MSC rqb_ctx = { 1 }; + +DIB rqb_dib = { + IOBA_RQB, IOLN_RQB, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT rqb_unit[] = { + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_tmrsvc, UNIT_DIS, 0) }, + { UDATA (&rq_quesvc, UNIT_DIS, 0) } + }; + +REG rqb_reg[] = { + { GRDATA (SA, rqb_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA (SAW, rqb_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA (S1DAT, rqb_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA (COMM, rqb_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQBA, rqb_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA (CQLNT, rqb_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (CQIDX, rqb_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQBA, rqb_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA (RQLNT, rqb_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (RQIDX, rqb_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA (FREE, rqb_ctx.freq, 5) }, + { DRDATA (RESP, rqb_ctx.rspq, 5) }, + { DRDATA (PBSY, rqb_ctx.pbsy, 5) }, + { GRDATA (CFLGS, rqb_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA (CSTA, rqb_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA (PERR, rqb_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA (CRED, rqb_ctx.credits, 5) }, + { DRDATA (HAT, rqb_ctx.hat, 17) }, + { DRDATA (HTMO, rqb_ctx.htmo, 17) }, + { FLDATA (PRGI, rqb_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA (PIP, rqb_ctx.pip, 0), REG_HIDDEN }, + { FLDATA (INT, rqb_ctx.irq, 0) }, + { BRDATA (PKTS, rqb_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA (CPKT, rqb_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (PKTQ, rqb_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (UFLG, rqb_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA (CAPAC, rqb_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA (DEVADDR, rqb_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, rqb_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +DEVICE rqb_dev = { + "RQB", rqb_unit, rqb_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rqb_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + }; + +/* RQC data structures + + rqc_dev RQC device descriptor + rqc_unit RQC unit list + rqc_reg RQC register list + rqc_mod RQC modifier list +*/ + +MSC rqc_ctx = { 2 }; + +DIB rqc_dib = { + IOBA_RQC, IOLN_RQC, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT rqc_unit[] = { + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_tmrsvc, UNIT_DIS, 0) }, + { UDATA (&rq_quesvc, UNIT_DIS, 0) } + }; + +REG rqc_reg[] = { + { GRDATA (SA, rqc_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA (SAW, rqc_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA (S1DAT, rqc_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA (COMM, rqc_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQBA, rqc_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA (CQLNT, rqc_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (CQIDX, rqc_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQBA, rqc_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA (RQLNT, rqc_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (RQIDX, rqc_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA (FREE, rqc_ctx.freq, 5) }, + { DRDATA (RESP, rqc_ctx.rspq, 5) }, + { DRDATA (PBSY, rqc_ctx.pbsy, 5) }, + { GRDATA (CFLGS, rqc_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA (CSTA, rqc_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA (PERR, rqc_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA (CRED, rqc_ctx.credits, 5) }, + { DRDATA (HAT, rqc_ctx.hat, 17) }, + { DRDATA (HTMO, rqc_ctx.htmo, 17) }, + { FLDATA (PRGI, rqc_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA (PIP, rqc_ctx.pip, 0), REG_HIDDEN }, + { FLDATA (INT, rqc_ctx.irq, 0) }, + { BRDATA (PKTS, rqc_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA (CPKT, rqc_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (PKTQ, rqc_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (UFLG, rqc_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA (CAPAC, rqc_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA (DEVADDR, rqc_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, rqc_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +DEVICE rqc_dev = { + "RQC", rqc_unit, rqc_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rqc_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + }; + +/* RQD data structures + + rqd_dev RQ device descriptor + rqd_unit RQ unit list + rqd_reg RQ register list + rqd_mod RQ modifier list +*/ + +MSC rqd_ctx = { 3 }; + +DIB rqd_dib = { + IOBA_RQD, IOLN_RQD, &rq_rd, &rq_wr, + 1, IVCL (RQ), 0, { &rq_inta } + }; + +UNIT rqd_unit[] = { + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE+ + (RD54_DTYPE << UNIT_V_DTYPE), RQ_SIZE (RD54)) }, + { UDATA (&rq_tmrsvc, UNIT_DIS, 0) }, + { UDATA (&rq_quesvc, UNIT_DIS, 0) } + }; + +REG rqd_reg[] = { + { GRDATA (SA, rqd_ctx.sa, DEV_RDX, 16, 0) }, + { GRDATA (SAW, rqd_ctx.saw, DEV_RDX, 16, 0) }, + { GRDATA (S1DAT, rqd_ctx.s1dat, DEV_RDX, 16, 0) }, + { GRDATA (COMM, rqd_ctx.comm, DEV_RDX, 22, 0) }, + { GRDATA (CQBA, rqd_ctx.cq.ba, DEV_RDX, 22, 0) }, + { GRDATA (CQLNT, rqd_ctx.cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (CQIDX, rqd_ctx.cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (RQBA, rqd_ctx.rq.ba, DEV_RDX, 22, 0) }, + { GRDATA (RQLNT, rqd_ctx.rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (RQIDX, rqd_ctx.rq.idx, DEV_RDX, 8, 2) }, + { DRDATA (FREE, rqd_ctx.freq, 5) }, + { DRDATA (RESP, rqd_ctx.rspq, 5) }, + { DRDATA (PBSY, rqd_ctx.pbsy, 5) }, + { GRDATA (CFLGS, rqd_ctx.cflgs, DEV_RDX, 16, 0) }, + { GRDATA (CSTA, rqd_ctx.csta, DEV_RDX, 4, 0) }, + { GRDATA (PERR, rqd_ctx.perr, DEV_RDX, 9, 0) }, + { DRDATA (CRED, rqd_ctx.credits, 5) }, + { DRDATA (HAT, rqd_ctx.hat, 17) }, + { DRDATA (HTMO, rqd_ctx.htmo, 17) }, + { FLDATA (PRGI, rqd_ctx.prgi, 0), REG_HIDDEN }, + { FLDATA (PIP, rqd_ctx.pip, 0), REG_HIDDEN }, + { FLDATA (INT, rqd_ctx.irq, 0) }, + { BRDATA (PKTS, rqd_ctx.pak, DEV_RDX, 16, RQ_NPKTS * (RQ_PKT_SIZE_W + 1)) }, + { URDATA (CPKT, rqd_unit[0].cpkt, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (PKTQ, rqd_unit[0].pktq, 10, 5, 0, RQ_NUMDR, 0) }, + { URDATA (UFLG, rqd_unit[0].uf, DEV_RDX, 16, 0, RQ_NUMDR, 0) }, + { URDATA (CAPAC, rqd_unit[0].capac, 10, 31, 0, RQ_NUMDR, PV_LEFT | REG_HRO) }, + { GRDATA (DEVADDR, rqd_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, rqd_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +DEVICE rqd_dev = { + "RQD", rqd_unit, rqd_reg, rq_mod, + RQ_NUMDR + 2, DEV_RDX, T_ADDR_W, 2, DEV_RDX, 16, + NULL, NULL, &rq_reset, + &rq_boot, &rq_attach, &rq_detach, + &rqd_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + }; + +static DEVICE *rq_devmap[RQ_NUMCT] = { + &rq_dev, &rqb_dev, &rqc_dev, &rqd_dev + }; + +static MSC *rq_ctxmap[RQ_NUMCT] = { + &rq_ctx, &rqb_ctx, &rqc_ctx, &rqd_ctx + }; + +/* I/O dispatch routines, I/O addresses 17772150 - 17772152 + + base + 0 IP read/write + base + 2 SA read/write +*/ + +t_stat rq_rd (int32 *data, int32 PA, int32 access) +{ +int32 cidx = rq_map_pa ((uint32) PA); +MSC *cp = rq_ctxmap[cidx]; +DEVICE *dptr = rq_devmap[cidx]; + +if (cidx < 0) return SCPE_IERR; +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* IP */ + *data = 0; /* reads zero */ + if (cp->csta == CST_S3_PPB) rq_step4 (cp); /* waiting for poll? */ + else if (cp->csta == CST_UP) { /* if up */ + if (DEBUG_PRD (dptr)) fprintf (sim_deb, + ">>RQ%c: poll started, PC=%X\n", 'A' + cp->cnum, OLDPC); + cp->pip = 1; /* poll host */ + sim_activate (dptr->units + RQ_QUEUE, rq_qtime); + } + break; + + case 1: /* SA */ + *data = cp->sa; + break; + } + +return SCPE_OK; +} + +t_stat rq_wr (int32 data, int32 PA, int32 access) +{ +int32 cidx = rq_map_pa ((uint32) PA); +MSC *cp = rq_ctxmap[cidx]; +DEVICE *dptr = rq_devmap[cidx]; + +if (cidx < 0) return SCPE_IERR; +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* IP */ + rq_reset (rq_devmap[cidx]); /* init device */ + if (DEBUG_PRD (dptr)) fprintf (sim_deb, + ">>RQ%c: initialization started\n", 'A' + cp->cnum); + break; + + case 1: /* SA */ + cp->saw = data; + if (cp->csta < CST_S4) /* stages 1-3 */ + sim_activate (dptr->units + RQ_QUEUE, rq_itime); + else if (cp->csta == CST_S4) /* stage 4 (fast) */ + sim_activate (dptr->units + RQ_QUEUE, rq_itime4); + break; + } + +return SCPE_OK; +} + +/* Map physical address to device context */ + +int32 rq_map_pa (uint32 pa) +{ +int32 i; +DEVICE *dptr; +DIB *dibp; + +for (i = 0; i < RQ_NUMCT; i++) { /* loop thru ctrls */ + dptr = rq_devmap[i]; /* get device */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if ((pa >= dibp->ba) && /* in range? */ + (pa < (dibp->ba + dibp->lnt))) + return i; /* return ctrl idx */ + } +return -1; +} + +/* Transition to step 4 - init communications region */ + +t_bool rq_step4 (MSC *cp) +{ +int32 i, lnt; +uint32 base; +uint16 zero[SA_COMM_MAX >> 1]; + +cp->rq.ioff = SA_COMM_RI; /* set intr offset */ +cp->rq.ba = cp->comm; /* set rsp q base */ +cp->rq.lnt = SA_S1H_RQ (cp->s1dat) << 2; /* get resp q len */ +cp->cq.ioff = SA_COMM_CI; /* set intr offset */ +cp->cq.ba = cp->comm + cp->rq.lnt; /* set cmd q base */ +cp->cq.lnt = SA_S1H_CQ (cp->s1dat) << 2; /* get cmd q len */ +cp->cq.idx = cp->rq.idx = 0; /* clear q idx's */ +if (cp->prgi) base = cp->comm + SA_COMM_QQ; +else base = cp->comm + SA_COMM_CI; +lnt = cp->comm + cp->cq.lnt + cp->rq.lnt - base; /* comm lnt */ +if (lnt > SA_COMM_MAX) lnt = SA_COMM_MAX; /* paranoia */ +for (i = 0; i < (lnt >> 1); i++) zero[i] = 0; /* clr buffer */ +if (Map_WriteW (base, lnt, zero)) /* zero comm area */ + return rq_fatal (cp, PE_QWE); /* error? */ +cp->sa = SA_S4 | (RQ_UQPM << SA_S4C_V_MOD) | /* send step 4 */ + (RQ_SVER << SA_S4C_V_VER); +cp->csta = CST_S4; /* set step 4 */ +rq_init_int (cp); /* poke host */ +return OK; +} + +/* Queue service - invoked when any of the queues (host queue, unit + queues, response queue) require servicing. Also invoked during + initialization to provide some delay to the next step. + + Process at most one item off each unit queue + If the unit queues were empty, process at most one item off the host queue + Process at most one item off the response queue + + If all queues are idle, terminate thread +*/ + +t_stat rq_quesvc (UNIT *uptr) +{ +int32 i, cnid; +int32 pkt = 0; +UNIT *nuptr; +MSC *cp = rq_ctxmap[uptr->cnum]; +DEVICE *dptr = rq_devmap[uptr->cnum]; +DIB *dibp = (DIB *) dptr->ctxt; + +if (cp->csta < CST_UP) { /* still init? */ + switch (cp->csta) { /* controller state? */ + + case CST_S1: /* need S1 reply */ + if (cp->saw & SA_S1H_VL) { /* valid? */ + if (cp->saw & SA_S1H_WR) { /* wrap? */ + cp->sa = cp->saw; /* echo data */ + cp->csta = CST_S1_WR; /* endless loop */ + } + else { + cp->s1dat = cp->saw; /* save data */ + dibp->vec = (cp->s1dat & SA_S1H_VEC) << 2; /* get vector */ + if (dibp->vec) dibp->vec = dibp->vec + VEC_Q; /* if nz, bias */ + cp->sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (cp->s1dat); + cp->csta = CST_S2; /* now in step 2 */ + rq_init_int (cp); /* intr if req */ + } + } /* end if valid */ + break; + + case CST_S1_WR: /* wrap mode */ + cp->sa = cp->saw; /* echo data */ + break; + + case CST_S2: /* need S2 reply */ + cp->comm = cp->saw & SA_S2H_CLO; /* get low addr */ + cp->prgi = cp->saw & SA_S2H_PI; /* get purge int */ + cp->sa = SA_S3 | SA_S3C_EC (cp->s1dat); + cp->csta = CST_S3; /* now in step 3 */ + rq_init_int (cp); /* intr if req */ + break; + + case CST_S3: /* need S3 reply */ + cp->comm = ((cp->saw & SA_S3H_CHI) << 16) | cp->comm; + if (cp->saw & SA_S3H_PP) { /* purge/poll test? */ + cp->sa = 0; /* put 0 */ + cp->csta = CST_S3_PPA; /* wait for 0 write */ + } + else rq_step4 (cp); /* send step 4 */ + break; + + case CST_S3_PPA: /* need purge test */ + if (cp->saw) rq_fatal (cp, PE_PPF); /* data not zero? */ + else cp->csta = CST_S3_PPB; /* wait for poll */ + break; + + case CST_S4: /* need S4 reply */ + if (cp->saw & SA_S4H_GO) { /* go set? */ + if (DEBUG_PRD (dptr)) fprintf (sim_deb, + ">>RQ%c: initialization complete\n", 'A' + cp->cnum); + cp->csta = CST_UP; /* we're up */ + cp->sa = 0; /* clear SA */ + sim_activate (dptr->units + RQ_TIMER, tmr_poll * clk_tps); + if ((cp->saw & SA_S4H_LF) && cp->perr) rq_plf (cp, cp->perr); + cp->perr = 0; + } + break; + } /* end switch */ + + return SCPE_OK; + } /* end if */ + +for (i = 0; i < RQ_NUMDR; i++) { /* chk unit q's */ + nuptr = dptr->units + i; /* ptr to unit */ + if (nuptr->cpkt || (nuptr->pktq == 0)) continue; + pkt = rq_deqh (cp, &nuptr->pktq); /* get top of q */ + if (!rq_mscp (cp, pkt, FALSE)) return SCPE_OK; /* process */ + } +if ((pkt == 0) && cp->pip) { /* polling? */ + if (!rq_getpkt (cp, &pkt)) return SCPE_OK; /* get host pkt */ + if (pkt) { /* got one? */ + if (DEBUG_PRD (dptr)) { + fprintf (sim_deb, ">>RQ%c: cmd=%04X, mod=%04X, unit=%d, ", + 'A' + cp->cnum, cp->pak[pkt].d[CMD_OPC], + cp->pak[pkt].d[CMD_MOD], cp->pak[pkt].d[CMD_UN]); + fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X, lbn=%04X%04X\n", + cp->pak[pkt].d[RW_BCH], cp->pak[pkt].d[RW_BCL], + cp->pak[pkt].d[RW_BAH], cp->pak[pkt].d[RW_BAL], + cp->pak[pkt].d[RW_LBNH], cp->pak[pkt].d[RW_LBNL]); + } + if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ + return rq_fatal (cp, PE_PIE); /* no, term thread */ + cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ + if (cnid == UQ_CID_MSCP) { /* MSCP packet? */ + if (!rq_mscp (cp, pkt, TRUE)) return SCPE_OK; /* proc, q non-seq */ + } + else if (cnid == UQ_CID_DUP) { /* DUP packet? */ + rq_putr (cp, pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ); + if (!rq_putpkt (cp, pkt, TRUE)) return SCPE_OK; /* ill cmd */ + } + else return rq_fatal (cp, PE_ICI); /* no, term thread */ + } /* end if pkt */ + else cp->pip = 0; /* discontinue poll */ + } /* end if pip */ +if (cp->rspq) { /* resp q? */ + pkt = rq_deqh (cp, &cp->rspq); /* get top of q */ + if (!rq_putpkt (cp, pkt, FALSE)) return SCPE_OK; /* send to host */ + } /* end if resp q */ +if (pkt) sim_activate (uptr, rq_qtime); /* more to do? */ +return SCPE_OK; /* done */ +} + +/* Clock service (roughly once per second) */ + +t_stat rq_tmrsvc (UNIT *uptr) +{ +int32 i; +UNIT *nuptr; +MSC *cp = rq_ctxmap[uptr->cnum]; +DEVICE *dptr = rq_devmap[uptr->cnum]; + +sim_activate (uptr, tmr_poll * clk_tps); /* reactivate */ +for (i = 0; i < RQ_NUMDR; i++) { /* poll */ + nuptr = dptr->units + i; + if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */ + (nuptr->flags & UNIT_ATT) && /* still online? */ + (cp->cflgs & CF_ATN)) { /* wanted? */ + if (!rq_una (cp, i)) return SCPE_OK; + } + nuptr->flags = nuptr->flags & ~UNIT_ATP; + } +if ((cp->hat > 0) && (--cp->hat == 0)) /* host timeout? */ + rq_fatal (cp, PE_HAT); /* fatal err */ +return SCPE_OK; +} + +/* MSCP packet handling */ + +t_bool rq_mscp (MSC *cp, int32 pkt, t_bool q) +{ +uint32 sts, cmd = GETP (pkt, CMD_OPC, OPC); + +switch (cmd) { + + case OP_ABO: /* abort */ + return rq_abo (cp, pkt, q); + + case OP_AVL: /* avail */ + return rq_avl (cp, pkt, q); + + case OP_FMT: /* format */ + return rq_fmt (cp, pkt, q); + + case OP_GCS: /* get cmd status */ + return rq_gcs (cp, pkt, q); + + case OP_GUS: /* get unit status */ + return rq_gus (cp, pkt, q); + + case OP_ONL: /* online */ + return rq_onl (cp, pkt, q); + + case OP_SCC: /* set ctrl char */ + return rq_scc (cp, pkt, q); + + case OP_SUC: /* set unit char */ + return rq_suc (cp, pkt, q); + + case OP_ACC: /* access */ + case OP_CMP: /* compare */ + case OP_ERS: /* erase */ + case OP_RD: /* read */ + case OP_WR: /* write */ + return rq_rw (cp, pkt, q); + + case OP_CCD: /* nops */ + case OP_DAP: + case OP_FLU: + cmd = cmd | OP_END; /* set end flag */ + sts = ST_SUC; /* success */ + break; + + default: + cmd = OP_END; /* set end op */ + sts = ST_CMD | I_OPCD; /* ill op */ + break; + } + +rq_putr (cp, pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Abort a command - 1st parameter is ref # of cmd to abort */ + +t_bool rq_abo (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */ +int32 tpkt, prv; +UNIT *uptr; +DEVICE *dptr = rq_devmap[cp->cnum]; + +tpkt = 0; /* set no mtch */ +if (uptr = rq_getucb (cp, lu)) { /* get unit */ + if (uptr->cpkt && /* curr pkt? */ + (GETP32 (uptr->cpkt, CMD_REFL) == ref)) { /* match ref? */ + tpkt = uptr->cpkt; /* save match */ + uptr->cpkt = 0; /* gonzo */ + sim_cancel (uptr); /* cancel unit */ + sim_activate (dptr->units + RQ_QUEUE, rq_qtime); + } + else if (uptr->pktq && /* head of q? */ + (GETP32 (uptr->pktq, CMD_REFL) == ref)) { /* match ref? */ + tpkt = uptr->pktq; /* save match */ + uptr->pktq = cp->pak[tpkt].link; /* unlink */ + } + else if (prv = uptr->pktq) { /* srch pkt q */ + while (tpkt = cp->pak[prv].link) { /* walk list */ + if (GETP32 (tpkt, RSP_REFL) == ref) { /* match? unlink */ + cp->pak[prv].link = cp->pak[tpkt].link; + break; + } + } + } + if (tpkt) { /* found target? */ + uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */ + rq_putr (cp, tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ); + if (!rq_putpkt (cp, tpkt, TRUE)) return ERR; + } + } /* end if unit */ +rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Unit available - set unit status to available - defer if q'd cmds */ + +t_bool rq_avl (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 sts; +UNIT *uptr; + +if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ + if (q && uptr->cpkt) { /* need to queue? */ + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + uptr->flags = uptr->flags & ~UNIT_ONL; /* not online */ + uptr->uf = 0; /* clr flags */ + sts = ST_SUC; /* success */ + } +else sts = ST_OFL; /* offline */ +rq_putr (cp, pkt, cmd | OP_END, 0, sts, AVL_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Get command status - only interested in active xfr cmd */ + +t_bool rq_gcs (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ +int32 tpkt; +UNIT *uptr; + +if ((uptr = rq_getucb (cp, lu)) && /* valid lu? */ + (tpkt = uptr->cpkt) && /* queued pkt? */ + (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ + (GETP (tpkt, CMD_OPC, OPC) >= OP_ACC)) { /* rd/wr cmd? */ + cp->pak[pkt].d[GCS_STSL] = cp->pak[tpkt].d[RW_WBCL]; + cp->pak[pkt].d[GCS_STSH] = cp->pak[tpkt].d[RW_WBCH]; + } +else { + cp->pak[pkt].d[GCS_STSL] = 0; /* return 0 */ + cp->pak[pkt].d[GCS_STSH] = 0; + } +rq_putr (cp, pkt, cmd | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Get unit status */ + +t_bool rq_gus (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 dtyp, sts, rbpar; +UNIT *uptr; + +if (cp->pak[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */ + if (lu >= (cp->ubase + RQ_NUMDR)) { /* end of range? */ + lu = 0; /* reset to 0 */ + cp->pak[pkt].d[RSP_UN] = lu; + } + } +if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else if (uptr->flags & UNIT_ONL) sts = ST_SUC; /* online */ + else sts = ST_AVL; /* avail */ + rq_putr_unit (cp, pkt, uptr, lu, FALSE); /* fill unit fields */ + dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + if (drv_tab[dtyp].rcts) rbpar = 1; /* ctrl bad blk? */ + else rbpar = 0; /* fill geom, bblk */ + cp->pak[pkt].d[GUS_TRK] = drv_tab[dtyp].sect; + cp->pak[pkt].d[GUS_GRP] = drv_tab[dtyp].tpg; + cp->pak[pkt].d[GUS_CYL] = drv_tab[dtyp].gpc; + cp->pak[pkt].d[GUS_UVER] = 0; + cp->pak[pkt].d[GUS_RCTS] = drv_tab[dtyp].rcts; + cp->pak[pkt].d[GUS_RBSC] = + (rbpar << GUS_RB_V_RBNS) | (rbpar << GUS_RB_V_RCTC); + } +else sts = ST_OFL; /* offline */ +cp->pak[pkt].d[GUS_SHUN] = lu; /* shadowing */ +cp->pak[pkt].d[GUS_SHST] = 0; +rq_putr (cp, pkt, cmd | OP_END, 0, sts, GUS_LNT_D, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Unit online - defer if q'd commands */ + +t_bool rq_onl (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 sts; +UNIT *uptr; + +if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ + if (q && uptr->cpkt) { /* need to queue? */ + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else if (uptr->flags & UNIT_ONL) /* already online? */ + sts = ST_SUC | SB_SUC_ON; + else { /* mark online */ + sts = ST_SUC; + uptr->flags = uptr->flags | UNIT_ONL; + rq_setf_unit (cp, pkt, uptr); /* hack flags */ + } + rq_putr_unit (cp, pkt, uptr, lu, TRUE); /* set fields */ + } +else sts = ST_OFL; /* offline */ +cp->pak[pkt].d[ONL_SHUN] = lu; /* shadowing */ +cp->pak[pkt].d[ONL_SHST] = 0; +rq_putr (cp, pkt, cmd | OP_END, 0, sts, ONL_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Set controller characteristics */ + +t_bool rq_scc (MSC *cp, int32 pkt, t_bool q) +{ +int32 sts, cmd; + +if (cp->pak[pkt].d[SCC_MSV]) { /* MSCP ver = 0? */ + sts = ST_CMD | I_VRSN; /* no, lose */ + cmd = 0; + } +else { + sts = ST_SUC; /* success */ + cmd = GETP (pkt, CMD_OPC, OPC); /* get opcode */ + cp->cflgs = (cp->cflgs & CF_RPL) | /* hack ctrl flgs */ + cp->pak[pkt].d[SCC_CFL]; + if (cp->htmo = cp->pak[pkt].d[SCC_TMO]) /* set timeout */ + cp->htmo = cp->htmo + 2; /* if nz, round up */ + cp->pak[pkt].d[SCC_CFL] = cp->cflgs; /* return flags */ + cp->pak[pkt].d[SCC_TMO] = RQ_DCTMO; /* ctrl timeout */ + cp->pak[pkt].d[SCC_VER] = (RQ_HVER << SCC_VER_V_HVER) | + (RQ_SVER << SCC_VER_V_SVER); + cp->pak[pkt].d[SCC_CIDA] = 0; /* ctrl ID */ + cp->pak[pkt].d[SCC_CIDB] = 0; + cp->pak[pkt].d[SCC_CIDC] = 0; + cp->pak[pkt].d[SCC_CIDD] = (RQ_CLASS << SCC_CIDD_V_CLS) | + (RQ_MODEL << SCC_CIDD_V_MOD); + cp->pak[pkt].d[SCC_MBCL] = 0; /* max bc */ + cp->pak[pkt].d[SCC_MBCH] = 0; + } +rq_putr (cp, pkt, cmd | OP_END, 0, sts, SCC_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Set unit characteristics - defer if q'd commands */ + +t_bool rq_suc (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 sts; +UNIT *uptr; + +if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ + if (q && uptr->cpkt) { /* need to queue? */ + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else { /* avail or onl */ + sts = ST_SUC; + rq_setf_unit (cp, pkt, uptr); /* hack flags */ + } + rq_putr_unit (cp, pkt, uptr, lu, TRUE); /* set fields */ + } +else sts = ST_OFL; /* offline */ +cp->pak[pkt].d[ONL_SHUN] = lu; /* shadowing */ +cp->pak[pkt].d[ONL_SHST] = 0; +rq_putr (cp, pkt, cmd | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Format command - floppies only */ + +t_bool rq_fmt (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 sts; +UNIT *uptr; + +if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ + if (q && uptr->cpkt) { /* need to queue? */ + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + if (GET_DTYPE (uptr->flags) != RX33_DTYPE) /* RX33? */ + sts = ST_CMD | I_OPCD; /* no, err */ + else if ((cp->pak[pkt].d[FMT_IH] & 0100000) == 0) /* magic bit set? */ + sts = ST_CMD | I_FMTI; /* no, err */ + else if ((uptr->flags & UNIT_ATT) == 0) /* offline? */ + sts = ST_OFL | SB_OFL_NV; /* no vol */ + else if (uptr->flags & UNIT_ONL) { /* online? */ + uptr->flags = uptr->flags & ~UNIT_ONL; + uptr->uf = 0; /* clear flags */ + sts = ST_AVL | SB_AVL_INU; /* avail, in use */ + } + else if (RQ_WPH (uptr)) /* write prot? */ + sts = ST_WPR | SB_WPR_HW; /* can't fmt */ + else sts = ST_SUC; /*** for now ***/ + } +else sts = ST_OFL; /* offline */ +rq_putr (cp, pkt, cmd | OP_END, 0, sts, FMT_LNT, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Data transfer commands */ + +t_bool rq_rw (MSC *cp, int32 pkt, t_bool q) +{ +uint32 lu = cp->pak[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 sts; +UNIT *uptr; + +if (uptr = rq_getucb (cp, lu)) { /* unit exist? */ + if (q && uptr->cpkt) { /* need to queue? */ + rq_enqt (cp, &uptr->pktq, pkt); /* do later */ + return OK; + } + sts = rq_rw_valid (cp, pkt, uptr, cmd); /* validity checks */ + if (sts == 0) { /* ok? */ + uptr->cpkt = pkt; /* op in progress */ + cp->pak[pkt].d[RW_WBAL] = cp->pak[pkt].d[RW_BAL]; + cp->pak[pkt].d[RW_WBAH] = cp->pak[pkt].d[RW_BAH]; + cp->pak[pkt].d[RW_WBCL] = cp->pak[pkt].d[RW_BCL]; + cp->pak[pkt].d[RW_WBCH] = cp->pak[pkt].d[RW_BCH]; + cp->pak[pkt].d[RW_WBLL] = cp->pak[pkt].d[RW_LBNL]; + cp->pak[pkt].d[RW_WBLH] = cp->pak[pkt].d[RW_LBNH]; + sim_activate (uptr, rq_xtime); /* activate */ + return OK; /* done */ + } + } +else sts = ST_OFL; /* offline */ +cp->pak[pkt].d[RW_BCL] = cp->pak[pkt].d[RW_BCH] = 0; /* bad packet */ +rq_putr (cp, pkt, cmd | OP_END, 0, sts, RW_LNT_D, UQ_TYP_SEQ); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Validity checks */ + +int32 rq_rw_valid (MSC *cp, int32 pkt, UNIT *uptr, uint32 cmd) +{ +uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ +uint32 lbn = GETP32 (pkt, RW_LBNL); /* get lbn */ +uint32 bc = GETP32 (pkt, RW_BCL); /* get byte cnt */ +uint32 maxlbn = (uint32) (uptr->capac / RQ_NUMBY); /* get max lbn */ + +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return (ST_OFL | SB_OFL_NV); /* offl no vol */ +if ((uptr->flags & UNIT_ONL) == 0) /* not online? */ + return ST_AVL; /* only avail */ +if ((cmd != OP_ACC) && (cmd != OP_ERS) && /* 'real' xfer */ + (cp->pak[pkt].d[RW_BAL] & 1)) /* odd address? */ + return (ST_HST | SB_HST_OA); /* host buf odd */ +if (bc & 1) return (ST_HST | SB_HST_OC); /* odd byte cnt? */ +if (bc & 0xF0000000) return (ST_CMD | I_BCNT); /* 'reasonable' bc? */ +/* if (lbn & 0xF0000000) return (ST_CMD | I_LBN); /* 'reasonable' lbn? */ +if (lbn >= maxlbn) { /* accessing RCT? */ + if (lbn >= (maxlbn + drv_tab[dtyp].rcts)) /* beyond copy 1? */ + return (ST_CMD | I_LBN); /* lbn err */ + if (bc != RQ_NUMBY) return (ST_CMD | I_BCNT); /* bc must be 512 */ + } +else if ((lbn + ((bc + (RQ_NUMBY - 1)) / RQ_NUMBY)) > maxlbn) + return (ST_CMD | I_BCNT); /* spiral to RCT */ +if ((cmd == OP_WR) || (cmd == OP_ERS)) { /* write op? */ + if (lbn >= maxlbn) /* accessing RCT? */ + return (ST_CMD | I_LBN); /* lbn err */ + if (uptr->uf & UF_WPS) /* swre wlk? */ + return (ST_WPR | SB_WPR_SW); + if (RQ_WPH (uptr)) /* hwre wlk? */ + return (ST_WPR | SB_WPR_HW); + } +return 0; /* success! */ +} + +/* Unit service for data transfer commands */ + +t_stat rq_svc (UNIT *uptr) +{ +MSC *cp = rq_ctxmap[uptr->cnum]; + +uint32 i, t, tbc, abc, wwc; +uint32 err = 0; +int32 pkt = uptr->cpkt; /* get packet */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ +uint32 ba = GETP32 (pkt, RW_WBAL); /* buf addr */ +uint32 bc = GETP32 (pkt, RW_WBCL); /* byte count */ +uint32 bl = GETP32 (pkt, RW_WBLL); /* block addr */ +t_addr da = ((t_addr) bl) * RQ_NUMBY; /* disk addr */ + +if ((cp == NULL) || (pkt == 0)) return STOP_RQ; /* what??? */ +tbc = (bc > RQ_MAXFR)? RQ_MAXFR: bc; /* trim cnt to max */ + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rq_rw_end (cp, uptr, 0, ST_OFL | SB_OFL_NV); /* offl no vol */ + return SCPE_OK; + } +if (bc == 0) { /* no xfer? */ + rq_rw_end (cp, uptr, 0, ST_SUC); /* ok by me... */ + return SCPE_OK; + } + +if ((cmd == OP_ERS) || (cmd == OP_WR)) { /* write op? */ + if (RQ_WPH (uptr)) { + rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_HW); + return SCPE_OK; + } + if (uptr->uf & UF_WPS) { + rq_rw_end (cp, uptr, 0, ST_WPR | SB_WPR_SW); + return SCPE_OK; + } + } + +if (cmd == OP_ERS) { /* erase? */ + wwc = ((tbc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; + for (i = 0; i < wwc; i++) rqxb[i] = 0; /* clr buf */ + err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ + if (!err) sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref); + err = ferror (uptr->fileref); /* end if erase */ + } + +else if (cmd == OP_WR) { /* write? */ + t = Map_ReadW (ba, tbc, rqxb); /* fetch buffer */ + if (abc = tbc - t) { /* any xfer? */ + wwc = ((abc + (RQ_NUMBY - 1)) & ~(RQ_NUMBY - 1)) >> 1; + for (i = (abc >> 1); i < wwc; i++) rqxb[i] = 0; + err = sim_fseek (uptr->fileref, da, SEEK_SET); + if (!err) sim_fwrite (rqxb, sizeof (int16), wwc, uptr->fileref); + err = ferror (uptr->fileref); + } + if (t) { /* nxm? */ + PUTP32 (pkt, RW_WBCL, bc - abc); /* adj bc */ + PUTP32 (pkt, RW_WBAL, ba + abc); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; /* end else wr */ + } + } + +else { + err = sim_fseek (uptr->fileref, da, SEEK_SET); /* set pos */ + if (!err) { + i = sim_fread (rqxb, sizeof (int16), tbc >> 1, uptr->fileref); + for ( ; i < (tbc >> 1); i++) rqxb[i] = 0; /* fill */ + err = ferror (uptr->fileref); + } + if ((cmd == OP_RD) && !err) { /* read? */ + if (t = Map_WriteW (ba, tbc, rqxb)) { /* store, nxm? */ + PUTP32 (pkt, RW_WBCL, bc - (tbc - t)); /* adj bc */ + PUTP32 (pkt, RW_WBAL, ba + (tbc - t)); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; + } + } + else if ((cmd == OP_CMP) && !err) { /* compare? */ + uint8 dby, mby; + for (i = 0; i < tbc; i++) { /* loop */ + if (Map_ReadB (ba + i, 1, &mby)) { /* fetch, nxm? */ + PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ + PUTP32 (pkt, RW_WBAL, bc - i); /* adj ba */ + if (rq_hbe (cp, uptr)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_HST | SB_HST_NXM); + return SCPE_OK; + } + dby = (rqxb[i >> 1] >> ((i & 1)? 8: 0)) & 0xFF; + if (mby != dby) { /* cmp err? */ + PUTP32 (pkt, RW_WBCL, bc - i); /* adj bc */ + rq_rw_end (cp, uptr, 0, ST_CMP); /* done */ + return SCPE_OK; /* exit */ + } /* end if */ + } /* end for */ + } /* end else if */ + } /* end else read */ +if (err != 0) { /* error? */ + if (rq_dte (cp, uptr, ST_DRV)) /* post err log */ + rq_rw_end (cp, uptr, EF_LOG, ST_DRV); /* if ok, report err */ + perror ("RQ I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +ba = ba + tbc; /* incr bus addr */ +bc = bc - tbc; /* decr byte cnt */ +bl = bl + ((tbc + (RQ_NUMBY - 1)) / RQ_NUMBY); /* incr blk # */ +PUTP32 (pkt, RW_WBAL, ba); /* update pkt */ +PUTP32 (pkt, RW_WBCL, bc); +PUTP32 (pkt, RW_WBLL, bl); +if (bc) sim_activate (uptr, rq_xtime); /* more? resched */ +else rq_rw_end (cp, uptr, 0, ST_SUC); /* done! */ +return SCPE_OK; +} + +/* Transfer command complete */ + +t_bool rq_rw_end (MSC *cp, UNIT *uptr, uint32 flg, uint32 sts) +{ +int32 pkt = uptr->cpkt; /* packet */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ +uint32 bc = GETP32 (pkt, RW_BCL); /* init bc */ +uint32 wbc = GETP32 (pkt, RW_WBCL); /* work bc */ +DEVICE *dptr = rq_devmap[uptr->cnum]; + +uptr->cpkt = 0; /* done */ +PUTP32 (pkt, RW_BCL, bc - wbc); /* bytes processed */ +cp->pak[pkt].d[RW_WBAL] = 0; /* clear temps */ +cp->pak[pkt].d[RW_WBAH] = 0; +cp->pak[pkt].d[RW_WBCL] = 0; +cp->pak[pkt].d[RW_WBCH] = 0; +cp->pak[pkt].d[RW_WBLL] = 0; +cp->pak[pkt].d[RW_WBLH] = 0; +rq_putr (cp, pkt, cmd | OP_END, flg, sts, RW_LNT_D, UQ_TYP_SEQ); /* fill pkt */ +if (!rq_putpkt (cp, pkt, TRUE)) return ERR; /* send pkt */ +if (uptr->pktq) /* more to do? */ + sim_activate (dptr->units + RQ_QUEUE, rq_qtime); /* activate thread */ +return OK; +} + +/* Data transfer error log packet */ + +t_bool rq_dte (MSC *cp, UNIT *uptr, uint32 err) +{ +int32 pkt, tpkt; +uint32 lu, dtyp, lbn, ccyl, csurf, csect, t; + +if ((cp->cflgs & CF_THS) == 0) return OK; /* logging? */ +if (!rq_deqf (cp, &pkt)) return ERR; /* get log pkt */ +tpkt = uptr->cpkt; /* rw pkt */ +lu = cp->pak[tpkt].d[CMD_UN]; /* unit # */ +lbn = GETP32 (tpkt, RW_WBLL); /* recent LBN */ +dtyp = GET_DTYPE (uptr->flags); /* drv type */ +if (drv_tab[dtyp].flgs & RQDF_SDI) t = 0; /* SDI? ovhd @ end */ +else t = (drv_tab[dtyp].xbn + drv_tab[dtyp].dbn) / /* ovhd cylinders */ + (drv_tab[dtyp].sect * drv_tab[dtyp].surf); +ccyl = t + (lbn / drv_tab[dtyp].cyl); /* curr real cyl */ +t = lbn % drv_tab[dtyp].cyl; /* trk relative blk */ +csurf = t / drv_tab[dtyp].surf; /* curr surf */ +csect = t % drv_tab[dtyp].surf; /* curr sect */ + +cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL]; /* copy cmd ref */ +cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH]; +cp->pak[pkt].d[ELP_UN] = lu; /* copy unit */ +cp->pak[pkt].d[ELP_SEQ] = 0; /* clr seq # */ +cp->pak[pkt].d[DTE_CIDA] = 0; /* ctrl ID */ +cp->pak[pkt].d[DTE_CIDB] = 0; +cp->pak[pkt].d[DTE_CIDC] = 0; +cp->pak[pkt].d[DTE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) | + (RQ_MODEL << DTE_CIDD_V_MOD); +cp->pak[pkt].d[DTE_VER] = (RQ_HVER << DTE_VER_V_HVER) | + (RQ_SVER << DTE_VER_V_SVER); +cp->pak[pkt].d[DTE_MLUN] = lu; /* MLUN */ +cp->pak[pkt].d[DTE_UIDA] = lu; /* unit ID */ +cp->pak[pkt].d[DTE_UIDB] = 0; +cp->pak[pkt].d[DTE_UIDC] = 0; +cp->pak[pkt].d[DTE_UIDD] = (UID_DISK << DTE_UIDD_V_CLS) | + (drv_tab[dtyp].mod << DTE_UIDD_V_MOD); +cp->pak[pkt].d[DTE_UVER] = 0; /* unit versn */ +cp->pak[pkt].d[DTE_SCYL] = ccyl; /* cylinder */ +cp->pak[pkt].d[DTE_VSNL] = 01234 + lu; /* vol ser # */ +cp->pak[pkt].d[DTE_VSNH] = 0; +cp->pak[pkt].d[DTE_D1] = 0; +cp->pak[pkt].d[DTE_D2] = csect << DTE_D2_V_SECT; /* geometry */ +cp->pak[pkt].d[DTE_D3] = (ccyl << DTE_D3_V_CYL) | + (csurf << DTE_D3_V_SURF); +rq_putr (cp, pkt, FM_SDE, LF_SNR, err, DTE_LNT, UQ_TYP_DAT); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Host bus error log packet */ + +t_bool rq_hbe (MSC *cp, UNIT *uptr) +{ +int32 pkt, tpkt; + +if ((cp->cflgs & CF_THS) == 0) return OK; /* logging? */ +if (!rq_deqf (cp, &pkt)) return ERR; /* get log pkt */ +tpkt = uptr->cpkt; /* rw pkt */ +cp->pak[pkt].d[ELP_REFL] = cp->pak[tpkt].d[CMD_REFL]; /* copy cmd ref */ +cp->pak[pkt].d[ELP_REFH] = cp->pak[tpkt].d[CMD_REFH]; +cp->pak[pkt].d[ELP_UN] = cp->pak[tpkt].d[CMD_UN]; /* copy unit */ +cp->pak[pkt].d[ELP_SEQ] = 0; /* clr seq # */ +cp->pak[pkt].d[HBE_CIDA] = 0; /* ctrl ID */ +cp->pak[pkt].d[HBE_CIDB] = 0; +cp->pak[pkt].d[HBE_CIDC] = 0; +cp->pak[pkt].d[HBE_CIDD] = (RQ_CLASS << DTE_CIDD_V_CLS) | + (RQ_MODEL << DTE_CIDD_V_MOD); +cp->pak[pkt].d[HBE_VER] = (RQ_HVER << HBE_VER_V_HVER) | /* versions */ + (RQ_SVER << HBE_VER_V_SVER); +cp->pak[pkt].d[HBE_RSV] = 0; +cp->pak[pkt].d[HBE_BADL] = cp->pak[tpkt].d[RW_WBAL]; /* bad addr */ +cp->pak[pkt].d[HBE_BADH] = cp->pak[tpkt].d[RW_WBAH]; +rq_putr (cp, pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Port last failure error log packet */ + +t_bool rq_plf (MSC *cp, uint32 err) +{ +int32 pkt; + +if (!rq_deqf (cp, &pkt)) return ERR; /* get log pkt */ +cp->pak[pkt].d[ELP_REFL] = 0; /* ref = 0 */ +cp->pak[pkt].d[ELP_REFH] = 0; +cp->pak[pkt].d[ELP_UN] = 0; /* no unit */ +cp->pak[pkt].d[ELP_SEQ] = 0; /* no seq */ +cp->pak[pkt].d[PLF_CIDA] = 0; /* cntl ID */ +cp->pak[pkt].d[PLF_CIDB] = 0; +cp->pak[pkt].d[PLF_CIDC] = 0; +cp->pak[pkt].d[PLF_CIDD] = (RQ_CLASS << PLF_CIDD_V_CLS) | + (RQ_MODEL << PLF_CIDD_V_MOD); +cp->pak[pkt].d[PLF_VER] = (RQ_SVER << PLF_VER_V_SVER) | + (RQ_HVER << PLF_VER_V_HVER); +cp->pak[pkt].d[PLF_ERR] = err; +rq_putr (cp, pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT); +cp->pak[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID); +return rq_putpkt (cp, pkt, TRUE); +} + +/* Unit now available attention packet */ + +int32 rq_una (MSC *cp, int32 un) +{ +int32 pkt; +uint32 lu = cp->ubase + un; +UNIT *uptr = rq_getucb (cp, lu); + +if (uptr == NULL) return OK; /* huh? */ +if (!rq_deqf (cp, &pkt)) return ERR; /* get log pkt */ +cp->pak[pkt].d[RSP_REFL] = 0; /* ref = 0 */ +cp->pak[pkt].d[RSP_REFH] = 0; +cp->pak[pkt].d[RSP_UN] = lu; +cp->pak[pkt].d[RSP_RSV] = 0; +rq_putr_unit (cp, pkt, uptr, lu, FALSE); /* fill unit fields */ +rq_putr (cp, pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */ +return rq_putpkt (cp, pkt, TRUE); +} + +/* List handling + + rq_deqf - dequeue head of free list (fatal err if none) + rq_deqh - dequeue head of list + rq_enqh - enqueue at head of list + rq_enqt - enqueue at tail of list +*/ + +t_bool rq_deqf (MSC *cp, int32 *pkt) +{ +if (cp->freq == 0) return rq_fatal (cp, PE_NSR); /* no free pkts?? */ +cp->pbsy = cp->pbsy + 1; /* cnt busy pkts */ +*pkt = cp->freq; /* head of list */ +cp->freq = cp->pak[cp->freq].link; /* next */ +return OK; +} + +int32 rq_deqh (MSC *cp, int32 *lh) +{ +int32 ptr = *lh; /* head of list */ + +if (ptr) *lh = cp->pak[ptr].link; /* next */ +return ptr; +} + +void rq_enqh (MSC *cp, int32 *lh, int32 pkt) +{ +if (pkt == 0) return; /* any pkt? */ +cp->pak[pkt].link = *lh; /* link is old lh */ +*lh = pkt; /* pkt is new lh */ +return; +} + +void rq_enqt (MSC *cp, int32 *lh, int32 pkt) +{ +if (pkt == 0) return; /* any pkt? */ +cp->pak[pkt].link = 0; /* it will be tail */ +if (*lh == 0) *lh = pkt; /* if empty, enqh */ +else { + uint32 ptr = *lh; /* chase to end */ + while (cp->pak[ptr].link) ptr = cp->pak[ptr].link; + cp->pak[ptr].link = pkt; /* enq at tail */ + } +return; +} + +/* Packet and descriptor handling */ + +/* Get packet from command ring */ + +t_bool rq_getpkt (MSC *cp, int32 *pkt) +{ +uint32 addr, desc; + +if (!rq_getdesc (cp, &cp->cq, &desc)) return ERR; /* get cmd desc */ +if ((desc & UQ_DESC_OWN) == 0) { /* none */ + *pkt = 0; /* pkt = 0 */ + return OK; /* no error */ + } +if (!rq_deqf (cp, pkt)) return ERR; /* get cmd pkt */ +cp->hat = 0; /* dsbl hst timer */ +addr = desc & UQ_ADDR; /* get Q22 addr */ +if (Map_ReadW (addr + UQ_HDR_OFF, RQ_PKT_SIZE, cp->pak[*pkt].d)) + return rq_fatal (cp, PE_PRE); /* read pkt */ +return rq_putdesc (cp, &cp->cq, desc); /* release desc */ +} + +/* Put packet to response ring - note the clever hack about credits. + The controller sends all its credits to the host. Thereafter, it + supplies one credit for every response packet sent over. Simple! +*/ + +t_bool rq_putpkt (MSC *cp, int32 pkt, t_bool qt) +{ +uint32 addr, desc, lnt, cr; +DEVICE *dptr = rq_devmap[cp->cnum]; + +if (pkt == 0) return OK; /* any packet? */ +if (DEBUG_PRD (dptr)) fprintf (sim_deb, + ">>RQ%c: rsp=%04X, sts=%04X\n", 'A' + cp->cnum, + cp->pak[pkt].d[RSP_OPF], cp->pak[pkt].d[RSP_STS]); +if (!rq_getdesc (cp, &cp->rq, &desc)) return ERR; /* get rsp desc */ +if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */ + if (qt) rq_enqt (cp, &cp->rspq, pkt); /* normal? q tail */ + else rq_enqh (cp, &cp->rspq, pkt); /* resp q call */ + sim_activate (dptr->units + RQ_QUEUE, rq_qtime); /* activate q thrd */ + return OK; + } +addr = desc & UQ_ADDR; /* get Q22 addr */ +lnt = cp->pak[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */ +if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */ + (GETP (pkt, CMD_OPC, OPC) & OP_END)) { /* end packet? */ + cr = (cp->credits >= 14)? 14: cp->credits; /* max 14 credits */ + cp->credits = cp->credits - cr; /* decr credits */ + cp->pak[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR); + } +if (Map_WriteW (addr + UQ_HDR_OFF, lnt, cp->pak[pkt].d)) + return rq_fatal (cp, PE_PWE); /* write pkt */ +rq_enqh (cp, &cp->freq, pkt); /* pkt is free */ +cp->pbsy = cp->pbsy - 1; /* decr busy cnt */ +if (cp->pbsy == 0) cp->hat = cp->htmo; /* idle? strt hst tmr */ +return rq_putdesc (cp, &cp->rq, desc); /* release desc */ +} + +/* Get a descriptor from the host */ + +t_bool rq_getdesc (MSC *cp, struct uq_ring *ring, uint32 *desc) +{ +uint32 addr = ring->ba + ring->idx; +uint16 d[2]; + +if (Map_ReadW (addr, 4, d)) /* fetch desc */ + return rq_fatal (cp, PE_QRE); /* err? dead */ +*desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); +return OK; +} + +/* Return a descriptor to the host, clearing owner bit + If rings transitions from "empty" to "not empty" or "full" to + "not full", and interrupt bit was set, interrupt the host. + Actually, test whether previous ring entry was owned by host. +*/ + +t_bool rq_putdesc (MSC *cp, struct uq_ring *ring, uint32 desc) +{ +uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F; +uint32 prva, addr = ring->ba + ring->idx; +uint16 d[2]; + +d[0] = newd & 0xFFFF; /* 32b to 16b */ +d[1] = (newd >> 16) & 0xFFFF; +if (Map_WriteW (addr, 4, d)) /* store desc */ + return rq_fatal (cp, PE_QWE); /* err? dead */ +if (desc & UQ_DESC_F) { /* was F set? */ + if (ring->lnt <= 4) rq_ring_int (cp, ring); /* lnt = 1? intr */ + else { /* prv desc */ + prva = ring->ba + ((ring->idx - 4) & (ring->lnt - 1)); + if (Map_ReadW (prva, 4, d)) /* read prv */ + return rq_fatal (cp, PE_QRE); + prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16); + if (prvd & UQ_DESC_OWN) rq_ring_int (cp, ring); + } + } +ring->idx = (ring->idx + 4) & (ring->lnt - 1); +return OK; +} + +/* Get unit descriptor for logical unit */ + +UNIT *rq_getucb (MSC *cp, uint32 lu) +{ +DEVICE *dptr = rq_devmap[cp->cnum]; +UNIT *uptr; + +if ((lu < cp->ubase) || (lu >= (cp->ubase + RQ_NUMDR))) + return NULL; +uptr = dptr->units + (lu % RQ_NUMDR); +if (uptr->flags & UNIT_DIS) return NULL; +return uptr; +} + +/* Hack unit flags */ + +void rq_setf_unit (MSC *cp, int32 pkt, UNIT *uptr) +{ +uptr->uf = cp->pak[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */ +if ((cp->pak[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */ + (cp->pak[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */ + uptr->uf = uptr->uf | UF_WPS; /* simon says... */ +return; +} + +/* Unit response fields */ + +void rq_putr_unit (MSC *cp, int32 pkt, UNIT *uptr, uint32 lu, t_bool all) +{ +uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ +uint32 maxlbn = (uint32) (uptr->capac / RQ_NUMBY); /* get max lbn */ + +cp->pak[pkt].d[ONL_MLUN] = lu; /* unit */ +cp->pak[pkt].d[ONL_UFL] = uptr->uf | UF_RPL | RQ_WPH (uptr) | RQ_RMV (uptr); +cp->pak[pkt].d[ONL_RSVL] = 0; /* reserved */ +cp->pak[pkt].d[ONL_RSVH] = 0; +cp->pak[pkt].d[ONL_UIDA] = lu; /* UID low */ +cp->pak[pkt].d[ONL_UIDB] = 0; +cp->pak[pkt].d[ONL_UIDC] = 0; +cp->pak[pkt].d[ONL_UIDD] = (UID_DISK << ONL_UIDD_V_CLS) | + (drv_tab[dtyp].mod << ONL_UIDD_V_MOD); /* UID hi */ +PUTP32 (pkt, ONL_MEDL, drv_tab[dtyp].med); /* media type */ +if (all) { /* if long form */ + PUTP32 (pkt, ONL_SIZL, maxlbn); /* user LBNs */ + cp->pak[pkt].d[ONL_VSNL] = 01234 + lu; /* vol serial # */ + cp->pak[pkt].d[ONL_VSNH] = 0; + } +return; +} + +/* UQ_HDR and RSP_OP fields */ + +void rq_putr (MSC *cp, int32 pkt, uint32 cmd, uint32 flg, + uint32 sts, uint32 lnt, uint32 typ) +{ +cp->pak[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) | /* set cmd, flg */ + (flg << RSP_OPF_V_FLG); +cp->pak[pkt].d[RSP_STS] = sts; +cp->pak[pkt].d[UQ_HLNT] = lnt; /* length */ +cp->pak[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */ + (UQ_CID_MSCP << UQ_HCTC_V_CID); /* clr credits */ +return; +} + +/* Post interrupt during init */ + +void rq_init_int (MSC *cp) +{ +if ((cp->s1dat & SA_S1H_IE) && /* int enab & */ + (cp->s1dat & SA_S1H_VEC)) rq_setint (cp); /* ved set? int */ +return; +} + +/* Post interrupt during putpkt - note that NXMs are ignored! */ + +void rq_ring_int (MSC *cp, struct uq_ring *ring) +{ +uint32 iadr = cp->comm + ring->ioff; /* addr intr wd */ +uint16 flag = 1; + +Map_WriteW (iadr, 2, &flag); /* write flag */ +if (cp->s1dat & SA_S1H_VEC) rq_setint (cp); /* if enb, intr */ +return; +} + +/* Set RQ interrupt */ + +void rq_setint (MSC *cp) +{ +cp->irq = 1; /* set ctrl int */ +SET_INT (RQ); /* set master int */ +return; +} + +/* Clear RQ interrupt */ + +void rq_clrint (MSC *cp) +{ +int32 i; +MSC *ncp; + +cp->irq = 0; /* clr ctrl int */ +for (i = 0; i < RQ_NUMCT; i++) { /* loop thru ctrls */ + ncp = rq_ctxmap[i]; /* get context */ + if (ncp->irq) { /* other interrupt? */ + SET_INT (RQ); /* yes, set master */ + return; + } + } +CLR_INT (RQ); /* no, clr master */ +return; +} + +/* Return interrupt vector */ + +int32 rq_inta (void) +{ +int32 i; +MSC *ncp; +DEVICE *dptr; +DIB *dibp; + +for (i = 0; i < RQ_NUMCT; i++) { /* loop thru ctrl */ + ncp = rq_ctxmap[i]; /* get context */ + if (ncp->irq) { /* ctrl int set? */ + dptr = rq_devmap[i]; /* get device */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + rq_clrint (ncp); /* clear int req */ + return dibp->vec; /* return vector */ + } + } +return 0; /* no intr req */ +} + +/* Fatal error */ + +t_bool rq_fatal (MSC *cp, uint32 err) +{ +DEVICE *dptr = rq_devmap[cp->cnum]; + +if (DEBUG_PRD (dptr)) + fprintf (sim_deb, ">>RQ%c: fatal err=%X\n", 'A' + cp->cnum, err); +rq_reset (rq_devmap[cp->cnum]); /* reset device */ +cp->sa = SA_ER | err; /* SA = dead code */ +cp->csta = CST_DEAD; /* state = dead */ +cp->perr = err; /* save error */ +return ERR; +} + +/* Set/clear hardware write lock */ + +t_stat rq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + +if (drv_tab[dtyp].flgs & RQDF_RO) return SCPE_NOFNC; /* not on read only */ +return SCPE_OK; +} + +/* Show write lock status */ + +t_stat rq_show_wlk (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 dtyp = GET_DTYPE (uptr->flags); /* get drive type */ + +if (drv_tab[dtyp].flgs & RQDF_RO) fprintf (st, "read only"); +else if (uptr->flags & UNIT_WPRT) fprintf (st, "write locked"); +else fprintf (st, "write enabled"); +return SCPE_OK; +} + +/* Set unit type (and capacity if user defined) */ + +t_stat rq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 cap; +uint32 max = sim_taddr_64? RA8U_EMAXC: RA8U_MAXC; +t_stat r; + +if ((val < 0) || ((val != RA8U_DTYPE) && cptr)) + return SCPE_ARG; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +if (cptr) { + cap = (uint32) get_uint (cptr, 10, 0xFFFFFFFF, &r); + if ((sim_switches & SWMASK ('L')) == 0) cap = cap * 1954; + if ((r != SCPE_OK) || (cap < RA8U_MINC) || (cap >= max)) return SCPE_ARG; + drv_tab[val].lbn = cap; + } +uptr->flags = (uptr->flags & ~UNIT_DTYPE) | (val << UNIT_V_DTYPE); +uptr->capac = ((t_addr) drv_tab[val].lbn) * RQ_NUMBY; +return SCPE_OK; +} + +/* Show unit type */ + +t_stat rq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "%s", drv_tab[GET_DTYPE (uptr->flags)].name); +return SCPE_OK; +} + +/* Device attach */ + +t_stat rq_attach (UNIT *uptr, char *cptr) +{ +MSC *cp = rq_ctxmap[uptr->cnum]; +t_stat r; + +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) return r; +if (cp->csta == CST_UP) uptr->flags = uptr->flags | UNIT_ATP; +return SCPE_OK; +} + +/* Device detach */ + +t_stat rq_detach (UNIT *uptr) +{ +t_stat r; + +r = detach_unit (uptr); /* detach unit */ +if (r != SCPE_OK) return r; +uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); /* clr onl, atn pend */ +uptr->uf = 0; /* clr unit flgs */ +return SCPE_OK; +} + +/* Device reset */ + +t_stat rq_reset (DEVICE *dptr) +{ +int32 i, j, cidx; +UNIT *uptr; +MSC *cp; +DIB *dibp = (DIB *) dptr->ctxt; + +for (i = 0, cidx = -1; i < RQ_NUMCT; i++) { /* find ctrl num */ + if (rq_devmap[i] == dptr) cidx = i; + } +if (cidx < 0) return SCPE_IERR; /* not found??? */ +cp = rq_ctxmap[cidx]; /* get context */ +cp->cnum = cidx; /* init index */ + +#if defined (VM_VAX) /* VAX */ +cp->ubase = 0; /* unit base = 0 */ +#else /* PDP-11 */ +cp->ubase = cidx * RQ_NUMDR; /* init unit base */ +#endif + +cp->csta = CST_S1; /* init stage 1 */ +cp->s1dat = 0; /* no S1 data */ +dibp->vec = 0; /* no vector */ +cp->comm = 0; /* no comm region */ +if (UNIBUS) cp->sa = SA_S1 | SA_S1C_DI | SA_S1C_MP; /* Unibus? */ +else cp->sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */ +cp->cflgs = CF_RPL; /* ctrl flgs off */ +cp->htmo = RQ_DHTMO; /* default timeout */ +cp->hat = cp->htmo; /* default timer */ +cp->cq.ba = cp->cq.lnt = cp->cq.idx = 0; /* clr cmd ring */ +cp->rq.ba = cp->rq.lnt = cp->rq.idx = 0; /* clr rsp ring */ +cp->credits = (RQ_NPKTS / 2) - 1; /* init credits */ +cp->freq = 1; /* init free list */ +for (i = 0; i < RQ_NPKTS; i++) { /* all pkts free */ + if (i) cp->pak[i].link = (i + 1) & RQ_M_NPKTS; + else cp->pak[i].link = 0; + for (j = 0; j < RQ_PKT_SIZE_W; j++) cp->pak[i].d[j] = 0; + } +cp->rspq = 0; /* no q'd rsp pkts */ +cp->pbsy = 0; /* all pkts free */ +cp->pip = 0; /* not polling */ +rq_clrint (cp); /* clr intr req */ +for (i = 0; i < (RQ_NUMDR + 2); i++) { /* init units */ + uptr = dptr->units + i; + sim_cancel (uptr); /* clr activity */ + uptr->cnum = cidx; /* set ctrl index */ + uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP); + uptr->uf = 0; /* clr unit flags */ + uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ + } +if (rqxb == NULL) rqxb = (uint16 *) calloc (RQ_MAXFR >> 1, sizeof (uint16)); +if (rqxb == NULL) return SCPE_MEM; +return auto_config (0, 0); /* run autoconfig */ +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 016000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 014) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + + 0042125, /* st: "UD" */ + + /* Four step init process */ + + 0012706, 0016000, /* mov #st,sp */ + 0012700, 0000000, /* mov #unit,r0 */ + 0012701, 0172150, /* mov #172150, r1 ; ip addr */ + 0012704, 0016162, /* mov #it, r4 */ + 0012705, 0004000, /* mov #4000,r5 ; s1 mask */ + 0010102, /* mov r1,r2 */ + 0005022, /* clr (r2)+ ; init */ + 0005712, /* 10$: tst (r2) ; err? */ + 0100001, /* bpl 20$ */ + 0000000, /* halt */ + 0030512, /* 20$: bit r5,(r2) ; step set? */ + 0001773, /* beq 10$ ; wait */ + 0012412, /* mov (r4)+,(r2) ; send next */ + 0006305, /* asl r5 ; next mask */ + 0100370, /* bpl 10$ ; s4 done? */ + + /* Send ONL, READ commands */ + + 0105714, /* 30$: tstb (r4) ; end tbl? */ + 0001434, /* beq done ; 0 = yes */ + 0012702, 0007000, /* mov #rpkt-4,r2 ; clr pkts */ + 0005022, /* 40$: clr (r2)+ */ + 0020227, 0007204, /* cmp r2,#comm */ + 0103774, /* blo 40$ */ + 0112437, 0007100, /* movb (r4)+,cpkt-4 ; set lnt */ + 0110037, 0007110, /* movb r0,cpkt+4 ; set unit */ + 0112437, 0007114, /* movb (r4)+,cpkt+10 ; set op */ + 0112437, 0007121, /* movb (r4)+,cpkt+15 ; set param */ + 0012722, 0007004, /* mov #rpkt,(r2)+ ; rq desc */ + 0010522, /* mov r5,(r2)+ ; rq own */ + 0012722, 0007104, /* mov #ckpt,(r2)+ ; cq desc */ + 0010512, /* mov r5,(r2) ; cq own */ + 0024242, /* cmp -(r2),-(r2) ; back up */ + 0005711, /* tst (r1) ; wake ctrl */ + 0005712, /* 50$: tst (r2) ; rq own clr? */ + 0100776, /* bmi 50$ ; wait */ + 0005737, 0007016, /* tst rpkt+12 ; stat ok? */ + 0001743, /* beq 30$ ; next cmd */ + 0000000, /* halt */ + + /* Boot block read in, jump to 0 */ + + 0005011, /* done: clr (r1) ; for M+ */ + 0005003, /* clr r3 */ + 0012704, BOOT_START+020, /* mov #st+020,r4 */ + 0005005, /* clr r5 */ + 0005007, /* clr pc */ + + /* Data */ + + 0100000, /* it: no ints, ring sz = 1 */ + 0007204, /* .word comm */ + 0000000, /* .word 0 */ + 0000001, /* .word 1 */ + 0004420, /* .byte 20,11 */ + 0020000, /* .byte 0,40 */ + 0001041, /* .byte 41,2 */ + 0000000 + }; + +t_stat rq_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; +DIB *dibp = (DIB *) dptr->ctxt; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & 3; +M[BOOT_CSR >> 1] = dibp->ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat rq_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} +#endif + +/* Special show commands */ + +void rq_show_ring (FILE *st, struct uq_ring *rp) +{ +uint32 i, desc; +uint16 d[2]; + +#if defined (VM_PDP11) +fprintf (st, "ring, base = %o, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#else +fprintf (st, "ring, base = %x, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#endif +for (i = 0; i < (rp->lnt >> 2); i++) { + if (Map_ReadW (rp->ba + (i << 2), 4, d)) { + fprintf (st, " %3d: non-existent memory\n", i); + break; + } + desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); +#if defined (VM_PDP11) + fprintf (st, " %3d: %011o\n", i, desc); +#else + fprintf (st, " %3d: %08x\n", i, desc); +#endif + } +return; +} + +void rq_show_pkt (FILE *st, MSC *cp, int32 pkt) +{ +int32 i, j; +uint32 cr = GETP (pkt, UQ_HCTC, CR); +uint32 typ = GETP (pkt, UQ_HCTC, TYP); +uint32 cid = GETP (pkt, UQ_HCTC, CID); + +fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n", + pkt, cr, typ, cid); +for (i = 0; i < RQ_SH_MAX; i = i + RQ_SH_PPL) { + fprintf (st, " %2d:", i); + for (j = i; j < (i + RQ_SH_PPL); j++) +#if defined (VM_PDP11) + fprintf (st, " %06o", cp->pak[pkt].d[j]); +#else + fprintf (st, " %04x", cp->pak[pkt].d[j]); +#endif + fprintf (st, "\n"); + } +return; +} + +t_stat rq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +MSC *cp = rq_ctxmap[uptr->cnum]; +DEVICE *dptr = rq_devmap[uptr->cnum]; +int32 pkt, u; + +u = (int32) (uptr - dptr->units); +if (cp->csta != CST_UP) { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } +if ((uptr->flags & UNIT_ONL) == 0) { + if (uptr->flags & UNIT_ATT) + fprintf (st, "Unit %d is available\n", u); + else fprintf (st, "Unit %d is offline\n", u); + return SCPE_OK; + } +if (uptr->cpkt) { + fprintf (st, "Unit %d current ", u); + rq_show_pkt (st, cp, uptr->cpkt); + if (pkt = uptr->pktq) { + do { + fprintf (st, "Unit %d queued ", u); + rq_show_pkt (st, cp, pkt); + } while (pkt = cp->pak[pkt].link); + } + } +else fprintf (st, "Unit %d queues are empty\n", u); +return SCPE_OK; +} + +t_stat rq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +MSC *cp = rq_ctxmap[uptr->cnum]; +DEVICE *dptr = rq_devmap[uptr->cnum]; +int32 i, pkt; + +if (cp->csta != CST_UP) { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } +if (val & RQ_SH_RI) { + if (cp->pip) fprintf (st, "Polling in progress, host timer = %d\n", cp->hat); + else fprintf (st, "Host timer = %d\n", cp->hat); + fprintf (st, "Command "); + rq_show_ring (st, &cp->cq); + fprintf (st, "Response "); + rq_show_ring (st, &cp->rq); + } +if (val & RQ_SH_FR) { + if (pkt = cp->freq) { + for (i = 0; pkt != 0; i++, pkt = cp->pak[pkt].link) { + if (i == 0) fprintf (st, "Free queue = %d", pkt); + else if ((i % 16) == 0) fprintf (st, ",\n %d", pkt); + else fprintf (st, ", %d", pkt); + } + fprintf (st, "\n"); + } + else fprintf (st, "Free queue is empty\n"); + } +if (val & RQ_SH_RS) { + if (pkt = cp->rspq) { + do { + fprintf (st, "Response "); + rq_show_pkt (st, cp, pkt); + } while (pkt = cp->pak[pkt].link); + } + else fprintf (st, "Response queue is empty\n"); + } +if (val & RQ_SH_UN) { + for (i = 0; i < RQ_NUMDR; i++) + rq_show_unitq (st, dptr->units + i, 0, desc); + } +return SCPE_OK; +} diff --git a/PDP11/pdp11_rx.c b/PDP11/pdp11_rx.c new file mode 100644 index 0000000..6cd580c --- /dev/null +++ b/PDP11/pdp11_rx.c @@ -0,0 +1,518 @@ +/* pdp11_rx.c: RX11/RX01 floppy disk simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rx RX11/RX01 floppy disk + + 07-Jul-05 RMS Removed extraneous externs + 12-Oct-02 RMS Added autoconfigure support + 08-Oct-02 RMS Added variable address support to bootstrap + Added vector change/display support + Revised state machine based on RX211 + New data structures + Fixed reset of disabled device + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted FLG to array + 07-Sep-01 RMS Revised device disable and interrupt mechanisms + 17-Jul-01 RMS Fixed warning from VC++ 6.0 + 26-Apr-01 RMS Added device enable/disable support + 13-Apr-01 RMS Revised for register arrays + 15-Feb-01 RMS Corrected bootstrap string + 14-Apr-99 RMS Changed t_addr to unsigned + + An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. + Tracks are numbered 0-76, sectors 1-26. +*/ + +#include "pdp11_defs.h" + +#define RX_NUMTR 77 /* tracks/disk */ +#define RX_M_TRACK 0377 +#define RX_NUMSC 26 /* sectors/track */ +#define RX_M_SECTOR 0177 +#define RX_NUMBY 128 /* bytes/sector */ +#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */ +#define RX_NUMDR 2 /* drives/controller */ +#define RX_M_NUMDR 01 +#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ +#define UNIT_WLK (1u << UNIT_V_UF) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define IDLE 0 /* idle state */ +#define RWDS 1 /* rw, sect next */ +#define RWDT 2 /* rw, track next */ +#define RWXFR 3 /* rw, transfer */ +#define FILL 4 /* fill buffer */ +#define EMPTY 5 /* empty buffer */ +#define CMD_COMPLETE 6 /* set done next */ +#define INIT_COMPLETE 7 /* init compl next */ + +#define RXCS_V_FUNC 1 /* function */ +#define RXCS_M_FUNC 7 +#define RXCS_FILL 0 /* fill buffer */ +#define RXCS_EMPTY 1 /* empty buffer */ +#define RXCS_WRITE 2 /* write sector */ +#define RXCS_READ 3 /* read sector */ +#define RXCS_RXES 5 /* read status */ +#define RXCS_WRDEL 6 /* write del data */ +#define RXCS_ECODE 7 /* read error code */ +#define RXCS_V_DRV 4 /* drive select */ +#define RXCS_V_DONE 5 /* done */ +#define RXCS_V_IE 6 /* intr enable */ +#define RXCS_V_TR 7 /* xfer request */ +#define RXCS_V_INIT 14 /* init */ +#define RXCS_V_ERR 15 /* error */ +#define RXCS_FUNC (RXCS_M_FUNC << RXCS_V_FUNC) +#define RXCS_DRV (1u << RXCS_V_DRV) +#define RXCS_DONE (1u << RXCS_V_DONE) +#define RXCS_IE (1u << RXCS_V_IE) +#define RXCS_TR (1u << RXCS_V_TR) +#define RXCS_INIT (1u << RXCS_V_INIT) +#define RXCS_ERR (1u << RXCS_V_ERR) +#define RXCS_ROUT (RXCS_ERR+RXCS_TR+RXCS_IE+RXCS_DONE) +#define RXCS_IMP (RXCS_ROUT+RXCS_DRV+RXCS_FUNC) +#define RXCS_RW (RXCS_IE) /* read/write */ +#define RXCS_GETFNC(x) (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC) + +#define RXES_CRC 0001 /* CRC error */ +#define RXES_PAR 0002 /* parity error */ +#define RXES_ID 0004 /* init done */ +#define RXES_WLK 0010 /* write protect */ +#define RXES_DD 0100 /* deleted data */ +#define RXES_DRDY 0200 /* drive ready */ + +#define TRACK u3 /* current track */ +#define CALC_DA(t,s) (((t) * RX_NUMSC) + ((s) - 1)) * RX_NUMBY + +extern int32 int_req[IPL_HLVL]; + +int32 rx_csr = 0; /* control/status */ +int32 rx_dbr = 0; /* data buffer */ +int32 rx_esr = 0; /* error status */ +int32 rx_ecode = 0; /* error code */ +int32 rx_track = 0; /* desired track */ +int32 rx_sector = 0; /* desired sector */ +int32 rx_state = IDLE; /* controller state */ +int32 rx_stopioe = 1; /* stop on error */ +int32 rx_cwait = 100; /* command time */ +int32 rx_swait = 10; /* seek, per track */ +int32 rx_xwait = 1; /* tr set time */ +uint8 rx_buf[RX_NUMBY] = { 0 }; /* sector buffer */ +int32 rx_bptr = 0; /* buffer pointer */ +int32 rx_enb = 1; /* device enable */ + +DEVICE rx_dev; +t_stat rx_rd (int32 *data, int32 PA, int32 access); +t_stat rx_wr (int32 data, int32 PA, int32 access); +t_stat rx_svc (UNIT *uptr); +t_stat rx_reset (DEVICE *dptr); +t_stat rx_boot (int32 unitno, DEVICE *dptr); +void rx_done (int esr_flags, int new_ecode); + +/* RX11 data structures + + rx_dev RX device descriptor + rx_unit RX unit list + rx_reg RX register list + rx_mod RX modifier list +*/ + +DIB rx_dib = { + IOBA_RX, IOLN_RX, &rx_rd, &rx_wr, + 1, IVCL (RX), VEC_RX, { NULL } + }; + +UNIT rx_unit[] = { + { UDATA (&rx_svc, + UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) }, + { UDATA (&rx_svc, + UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, RX_SIZE) } + }; + +REG rx_reg[] = { + { ORDATA (RXCS, rx_csr, 16) }, + { ORDATA (RXDB, rx_dbr, 8) }, + { ORDATA (RXES, rx_esr, 8) }, + { ORDATA (RXERR, rx_ecode, 8) }, + { ORDATA (RXTA, rx_track, 8) }, + { ORDATA (RXSA, rx_sector, 8) }, + { DRDATA (STAPTR, rx_state, 3), REG_RO }, + { DRDATA (BUFPTR, rx_bptr, 7) }, + { FLDATA (INT, IREQ (RX), INT_V_RX) }, + { FLDATA (ERR, rx_csr, RXCS_V_ERR) }, + { FLDATA (TR, rx_csr, RXCS_V_TR) }, + { FLDATA (IE, rx_csr, RXCS_V_IE) }, + { FLDATA (DONE, rx_csr, RXCS_V_DONE) }, + { DRDATA (CTIME, rx_cwait, 24), PV_LEFT }, + { DRDATA (STIME, rx_swait, 24), PV_LEFT }, + { DRDATA (XTIME, rx_xwait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, rx_stopioe, 0) }, + { BRDATA (SBUF, rx_buf, 8, 8, RX_NUMBY) }, + { ORDATA (DEVADDR, rx_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, rx_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB rx_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, + { 0 } + }; + +DEVICE rx_dev = { + "RX", rx_unit, rx_reg, rx_mod, + RX_NUMDR, 8, 20, 1, 8, 8, + NULL, NULL, &rx_reset, + &rx_boot, NULL, NULL, + &rx_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS + }; + +/* I/O dispatch routine, I/O addresses 17777170 - 17777172 + + 17777170 floppy CSR + 17777172 floppy data register +*/ + +t_stat rx_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 1) { /* decode PA<1> */ + + case 0: /* RXCS */ + rx_csr = rx_csr & RXCS_IMP; /* clear junk */ + *data = rx_csr & RXCS_ROUT; + break; + + case 1: /* RXDB */ + if ((rx_state == EMPTY) && (rx_csr & RXCS_TR)) {/* empty? */ + sim_activate (&rx_unit[0], rx_xwait); + rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */ + } + *data = rx_dbr; /* return data */ + break; + } /* end switch PA */ + +return SCPE_OK; +} + +t_stat rx_wr (int32 data, int32 PA, int32 access) +{ +int32 drv; + +switch ((PA >> 1) & 1) { /* decode PA<1> */ + +/* Writing RXCS, three cases: + 1. Writing INIT, reset device + 2. Idle and writing new function + - clear error, done, transfer ready, int req + - save int enable, function, drive + - start new function + 3. Otherwise, write IE and update interrupts +*/ + + case 0: /* RXCS */ + rx_csr = rx_csr & RXCS_IMP; /* clear junk */ + if (access == WRITEB) data = (PA & 1)? /* write byte? */ + (rx_csr & 0377) | (data << 8): (rx_csr & ~0377) | data; + if (data & RXCS_INIT) { /* initialize? */ + rx_reset (&rx_dev); /* reset device */ + return SCPE_OK; /* end if init */ + } + if ((data & CSR_GO) && (rx_state == IDLE)) { /* new function? */ + rx_csr = data & (RXCS_IE + RXCS_DRV + RXCS_FUNC); + drv = ((rx_csr & RXCS_DRV)? 1: 0); /* reselect drive */ + rx_bptr = 0; /* clear buf pointer */ + switch (RXCS_GETFNC (data)) { /* case on func */ + + case RXCS_FILL: + rx_state = FILL; /* state = fill */ + rx_csr = rx_csr | RXCS_TR; /* xfer is ready */ + break; + + case RXCS_EMPTY: + rx_state = EMPTY; /* state = empty */ + sim_activate (&rx_unit[drv], rx_xwait); + break; + + case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL: + rx_state = RWDS; /* state = get sector */ + rx_csr = rx_csr | RXCS_TR; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + + default: + rx_state = CMD_COMPLETE; /* state = cmd compl */ + sim_activate (&rx_unit[drv], rx_cwait); + break; + } /* end switch func */ + return SCPE_OK; + } /* end if GO */ + if ((data & RXCS_IE) == 0) CLR_INT (RX); + else if ((rx_csr & (RXCS_DONE + RXCS_IE)) == RXCS_DONE) + SET_INT (RX); + rx_csr = (rx_csr & ~RXCS_RW) | (data & RXCS_RW); + break; /* end case RXCS */ + +/* Accessing RXDB, two cases: + 1. Write idle, write + 2. Write not idle and TR set, state dependent +*/ + + case 1: /* RXDB */ + if ((PA & 1) || ((rx_state != IDLE) && ((rx_csr & RXCS_TR) == 0))) + return SCPE_OK; /* if ~IDLE, need tr */ + rx_dbr = data & 0377; /* save data */ + if ((rx_state != IDLE) && (rx_state != EMPTY)) { + drv = ((rx_csr & RXCS_DRV)? 1: 0); /* select drive */ + sim_activate (&rx_unit[drv], rx_xwait); /* sched event */ + rx_csr = rx_csr & ~RXCS_TR; /* clear xfer */ + } + break; /* end case RXDB */ + } /* end switch PA */ + +return SCPE_OK; +} + +/* Unit service; the action to be taken depends on the transfer state: + + IDLE Should never get here + RWDS Save sector, set TR, set RWDT + RWDT Save track, set RWXFR + RWXFR Read/write buffer + FILL copy ir to rx_buf[rx_bptr], advance ptr + if rx_bptr > max, finish command, else set tr + EMPTY if rx_bptr > max, finish command, else + copy rx_buf[rx_bptr] to ir, advance ptr, set tr + CMD_COMPLETE copy requested data to ir, finish command + INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command + + For RWDT and CMD_COMPLETE, the input argument is the selected drive; + otherwise, it is drive 0. +*/ + +t_stat rx_svc (UNIT *uptr) +{ +int32 i, func; +uint32 da; +int8 *fbuf = uptr->filebuf; + +func = RXCS_GETFNC (rx_csr); /* get function */ +switch (rx_state) { /* case on state */ + + case IDLE: /* idle */ + return SCPE_IERR; /* done */ + + case EMPTY: /* empty buffer */ + if (rx_bptr >= RX_NUMBY) rx_done (0, 0); /* done all? */ + else { + rx_dbr = rx_buf[rx_bptr]; /* get next */ + rx_bptr = rx_bptr + 1; + rx_csr = rx_csr | RXCS_TR; /* set xfer */ + } + break; + + case FILL: /* fill buffer */ + rx_buf[rx_bptr] = rx_dbr; /* write next */ + rx_bptr = rx_bptr + 1; + if (rx_bptr < RX_NUMBY) rx_csr = rx_csr | RXCS_TR; /* more? set xfer */ + else rx_done (0, 0); /* else done */ + break; + + case RWDS: /* wait for sector */ + rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */ + rx_csr = rx_csr | RXCS_TR; /* set xfer */ + rx_state = RWDT; /* advance state */ + return SCPE_OK; + + case RWDT: /* wait for track */ + rx_track = rx_dbr & RX_M_TRACK; /* save track */ + rx_state = RWXFR; + sim_activate (uptr, /* sched done */ + rx_swait * abs (rx_track - uptr->TRACK)); + return SCPE_OK; + + case RWXFR: + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (0, 0110); /* done, error */ + return IORETURN (rx_stopioe, SCPE_UNATT); + } + if (rx_track >= RX_NUMTR) { /* bad track? */ + rx_done (0, 0040); /* done, error */ + break; + } + uptr->TRACK = rx_track; /* now on track */ + if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */ + rx_done (0, 0070); /* done, error */ + break; + } + da = CALC_DA (rx_track, rx_sector); /* get disk address */ + if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */ + if (func == RXCS_READ) { /* read? */ + for (i = 0; i < RX_NUMBY; i++) + rx_buf[i] = fbuf[da + i]; + } + else { + if (uptr->flags & UNIT_WPRT) { /* write and locked? */ + rx_done (RXES_WLK, 0100); /* done, error */ + break; + } + for (i = 0; i < RX_NUMBY; i++) /* write */ + fbuf[da + i] = rx_buf[i]; + da = da + RX_NUMBY; + if (da > uptr->hwmark) uptr->hwmark = da; + } + rx_done (0, 0); /* done */ + break; + + case CMD_COMPLETE: /* command complete */ + if (func == RXCS_ECODE) { /* read ecode? */ + rx_dbr = rx_ecode; /* set dbr */ + rx_done (0, -1); /* don't update */ + } + else rx_done (0, 0); + break; + + case INIT_COMPLETE: /* init complete */ + rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */ + rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */ + if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (RXES_ID, 0010); /* init done, error */ + break; + } + da = CALC_DA (1, 1); /* track 1, sector 1 */ + for (i = 0; i < RX_NUMBY; i++) /* read sector */ + rx_buf[i] = fbuf[da + i]; + rx_done (RXES_ID, 0); /* set done */ + if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020; + break; + } /* end case state */ + +return SCPE_OK; +} + +/* Command complete. Set done and put final value in interface register, + request interrupt if needed, return to IDLE state. +*/ + +void rx_done (int32 esr_flags, int32 new_ecode) +{ +int32 drv = (rx_csr & RXCS_DRV)? 1: 0; + +rx_state = IDLE; /* now idle */ +rx_csr = rx_csr | RXCS_DONE; /* set done */ +if (rx_csr & RXCS_IE) SET_INT (RX); /* if ie, intr */ +rx_esr = (rx_esr | esr_flags) & ~RXES_DRDY; +if (rx_unit[drv].flags & UNIT_ATT) + rx_esr = rx_esr | RXES_DRDY; +if (new_ecode > 0) rx_csr = rx_csr | RXCS_ERR; /* test for error */ +if (new_ecode < 0) return; /* don't update? */ +rx_ecode = new_ecode; /* update ecode */ +rx_dbr = rx_esr; /* update RXDB */ +return; +} + +/* Device initialization. The RX is one of the few devices that schedules + an I/O transfer as part of its initialization. +*/ + +t_stat rx_reset (DEVICE *dptr) +{ +rx_csr = rx_dbr = 0; /* clear regs */ +rx_esr = rx_ecode = 0; /* clear error */ +rx_track = rx_sector = 0; /* clear addr */ +rx_state = IDLE; /* ctrl idle */ +CLR_INT (RX); /* clear int req */ +sim_cancel (&rx_unit[1]); /* cancel drive 1 */ +if (dptr->flags & DEV_DIS) sim_cancel (&rx_unit[0]); /* disabled? */ +else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */ + rx_state = INIT_COMPLETE; /* yes, sched init */ + sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); + } +else rx_done (0, 0010); /* no, error */ +return auto_config (0, 0); /* run autoconfig */ +} + +/* Device bootstrap */ + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 026) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 042130, /* "XD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012700, 0000000, /* MOV #unit, R0 ; unit number */ + 0010003, /* MOV R0, R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0012701, 0177170, /* MOV #RXCS, R1 ; csr */ + 0032711, 0000040, /* BITB #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0052703, 0000007, /* BIS #READ+GO, R3 */ + 0010311, /* MOV R3, (R1) ; read & go */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; sector */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */ + 0005003, /* CLR R3 */ + 0032711, 0000040, /* BITB #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0012711, 0000003, /* MOV #EMPTY+GO, (R1) ; empty & go */ + 0105711, /* TSTB (R1) ; xfr, done? */ + 0001776, /* BEQ .-2 */ + 0100003, /* BPL .+010 */ + 0116123, 0000002, /* MOVB 2(R1), (R3)+ ; move byte */ + 0000772, /* BR .-012 */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0005007 /* CLR R7 */ + }; + +t_stat rx_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR; +M[BOOT_CSR >> 1] = rx_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} diff --git a/PDP11/pdp11_ry.c b/PDP11/pdp11_ry.c new file mode 100644 index 0000000..8b42dd0 --- /dev/null +++ b/PDP11/pdp11_ry.c @@ -0,0 +1,684 @@ +/* pdp11_ry.c: RX211/RXV21/RX02 floppy disk simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ry RX211/RXV21/RX02 floppy disk + + 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) + 07-Jul-05 RMS Removed extraneous externs + 18-Feb-05 RMS Fixed bug in boot code (reported by Graham Toal) + 30-Sep-04 RMS Revised Unibus interface + 21-Mar-04 RMS Added VAX support + 29-Dec-03 RMS Added RXV21 support + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 03-Mar-03 RMS Fixed autosizing + 12-Oct-02 RMS Added autoconfigure support + + An RX02 diskette consists of 77 tracks, each with 26 sectors of 256B. + Tracks are numbered 0-76, sectors 1-26. +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +extern int32 int_req; +#define DEV_DISI DEV_DIS + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +extern int32 int_req[IPL_HLVL]; +#define DEV_DISI 0 + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +extern int32 int_req[IPL_HLVL]; +#define DEV_DISI DEV_DIS +#endif + +#define RX_NUMTR 77 /* tracks/disk */ +#define RX_M_TRACK 0377 +#define RX_NUMSC 26 /* sectors/track */ +#define RX_M_SECTOR 0177 +#define RX_NUMBY 128 +#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) +#define RY_NUMBY 256 /* bytes/sector */ +#define RY_SIZE (RX_NUMTR * RX_NUMSC * RY_NUMBY) +#define RX_NUMDR 2 /* drives/controller */ +#define RX_M_NUMDR 01 +#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ +#define UNIT_V_DEN (UNIT_V_UF + 1) /* double density */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_DEN (1u << UNIT_V_DEN) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define IDLE 0 /* idle state */ +#define RWDS 1 /* rw, sect next */ +#define RWDT 2 /* rw, track next */ +#define RWXFR 3 /* rw, transfer */ +#define FEWC 4 /* fill empty, wc next */ +#define FEBA 5 /* fill empty, ba next */ +#define FEXFR 6 /* fill empty, transfer */ +#define SDCNF 7 /* set dens, conf next */ +#define SDXFR 8 /* set dens, transfer */ +#define ESBA 9 /* ext sta, ba next */ +#define ESXFR 10 /* ext sta, transfer */ +#define CMD_COMPLETE 11 /* set done next */ +#define INIT_COMPLETE 12 /* init compl next */ + +#define RYCS_V_FUNC 1 /* function */ +#define RYCS_M_FUNC 7 +#define RYCS_FILL 0 /* fill buffer */ +#define RYCS_EMPTY 1 /* empty buffer */ +#define RYCS_WRITE 2 /* write sector */ +#define RYCS_READ 3 /* read sector */ +#define RYCS_SDEN 4 /* set density */ +#define RYCS_RYES 5 /* read status */ +#define RYCS_WRDEL 6 /* write del data */ +#define RYCS_ESTAT 7 /* read ext status */ +#define RYCS_V_DRV 4 /* drive select */ +#define RYCS_V_DONE 5 /* done */ +#define RYCS_V_IE 6 /* int enable */ +#define RYCS_V_TR 7 /* xfer request */ +#define RYCS_V_DEN 8 /* density select */ +#define RYCS_V_RY 11 /* RX02 flag */ +#define RYCS_V_UAE 12 /* addr ext */ +#define RYCS_M_UAE 03 +#define RYCS_V_INIT 14 /* init */ +#define RYCS_V_ERR 15 /* error */ +#define RYCS_FUNC (RYCS_M_FUNC << RYCS_V_FUNC) +#define RYCS_DRV (1u << RYCS_V_DRV) +#define RYCS_DONE (1u << RYCS_V_DONE) +#define RYCS_IE (1u << RYCS_V_IE) +#define RYCS_TR (1u << RYCS_V_TR) +#define RYCS_DEN (1u << RYCS_V_DEN) +#define RYCS_RY (1u << RYCS_V_RY) +#define RYCS_UAE (RYCS_M_UAE << RYCS_V_UAE) +#define RYCS_INIT (1u << RYCS_V_INIT) +#define RYCS_ERR (1u << RYCS_V_ERR) +#define RYCS_IMP (RYCS_ERR+RYCS_UAE+RYCS_DEN+RYCS_TR+RYCS_IE+\ + RYCS_DONE+RYCS_DRV+RYCS_FUNC) +#define RYCS_RW (RYCS_UAE+RYCS_DEN+RYCS_IE+RYCS_DRV+RYCS_FUNC) +#define RYCS_GETFNC(x) (((x) >> RYCS_V_FUNC) & RYCS_M_FUNC) +#define RYCS_GETUAE(x) (((x) >> RYCS_V_UAE) & RYCS_M_UAE) + +#define RYES_CRC 00001 /* CRC error NI */ +#define RYES_ID 00004 /* init done */ +#define RYES_ACLO 00010 /* ACLO NI */ +#define RYES_DERR 00020 /* density err */ +#define RYES_DDEN 00040 /* drive density */ +#define RYES_DD 00100 /* deleted data */ +#define RYES_DRDY 00200 /* drive ready */ +#define RYES_USEL 00400 /* unit selected */ +#define RYES_WCO 02000 /* wc overflow */ +#define RYES_NXM 04000 /* nxm */ +#define RYES_ERR (RYES_NXM|RYES_WCO|RYES_DERR|RYES_ACLO|RYES_CRC) + +#define TRACK u3 /* current track */ +#define CALC_DA(t,s,b) (((t) * RX_NUMSC) + ((s) - 1)) * b + +int32 ry_csr = 0; /* control/status */ +int32 ry_dbr = 0; /* data buffer */ +int32 ry_esr = 0; /* error status */ +int32 ry_ecode = 0; /* error code */ +int32 ry_track = 0; /* desired track */ +int32 ry_sector = 0; /* desired sector */ +int32 ry_ba = 0; /* bus addr */ +int32 ry_wc = 0; /* word count */ +int32 ry_state = IDLE; /* controller state */ +int32 ry_stopioe = 1; /* stop on error */ +int32 ry_cwait = 100; /* command time */ +int32 ry_swait = 10; /* seek, per track */ +int32 ry_xwait = 1; /* tr set time */ +uint8 rx2xb[RY_NUMBY] = { 0 }; /* sector buffer */ + +DEVICE ry_dev; +t_stat ry_rd (int32 *data, int32 PA, int32 access); +t_stat ry_wr (int32 data, int32 PA, int32 access); +t_stat ry_svc (UNIT *uptr); +t_stat ry_reset (DEVICE *dptr); +t_stat ry_boot (int32 unitno, DEVICE *dptr); +void ry_done (int esr_flags, int new_ecode); +t_stat ry_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat ry_attach (UNIT *uptr, char *cptr); + +/* RY11 data structures + + ry_dev RY device descriptor + ry_unit RY unit list + ry_reg RY register list + ry_mod RY modifier list +*/ + +DIB ry_dib = { + IOBA_RY, IOLN_RY, &ry_rd, &ry_wr, + 1, IVCL (RY), VEC_RY, { NULL } + }; + +UNIT ry_unit[] = { + { UDATA (&ry_svc, UNIT_DEN+UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + RY_SIZE) }, + { UDATA (&ry_svc, UNIT_DEN+UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + RY_SIZE) } + }; + +REG ry_reg[] = { + { GRDATA (RYCS, ry_csr, DEV_RDX, 16, 0) }, + { GRDATA (RYBA, ry_ba, DEV_RDX, 16, 0) }, + { GRDATA (RYWC, ry_wc, DEV_RDX, 8, 0) }, + { GRDATA (RYDB, ry_dbr, DEV_RDX, 16, 0) }, + { GRDATA (RYES, ry_esr, DEV_RDX, 12, 0) }, + { GRDATA (RYERR, ry_ecode, DEV_RDX, 8, 0) }, + { GRDATA (RYTA, ry_track, DEV_RDX, 8, 0) }, + { GRDATA (RYSA, ry_sector, DEV_RDX, 8, 0) }, + { DRDATA (STAPTR, ry_state, 4), REG_RO }, + { FLDATA (INT, IREQ (RY), INT_V_RY) }, + { FLDATA (ERR, ry_csr, RYCS_V_ERR) }, + { FLDATA (TR, ry_csr, RYCS_V_TR) }, + { FLDATA (IE, ry_csr, RYCS_V_IE) }, + { FLDATA (DONE, ry_csr, RYCS_V_DONE) }, + { DRDATA (CTIME, ry_cwait, 24), PV_LEFT }, + { DRDATA (STIME, ry_swait, 24), PV_LEFT }, + { DRDATA (XTIME, ry_xwait, 24), PV_LEFT }, + { BRDATA (SBUF, rx2xb, 8, 8, RY_NUMBY) }, + { FLDATA (STOP_IOE, ry_stopioe, 0) }, + { URDATA (CAPAC, ry_unit[0].capac, 10, T_ADDR_W, 0, + RX_NUMDR, REG_HRO | PV_LEFT) }, + { GRDATA (DEVADDR, ry_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, ry_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB ry_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL }, + { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &ry_set_size }, + { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &ry_set_size }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, +#if defined (VM_PDP11) + { MTAB_XTD | MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, +#endif + { 0 } + }; + +DEVICE ry_dev = { + "RY", ry_unit, ry_reg, ry_mod, + RX_NUMDR, DEV_RDX, 20, 1, DEV_RDX, 8, + NULL, NULL, &ry_reset, + &ry_boot, &ry_attach, NULL, + &ry_dib, DEV_FLTA | DEV_DISABLE | DEV_DISI | DEV_UBUS | DEV_Q18 + }; + +/* I/O dispatch routine, I/O addresses 17777170 - 17777172 + + 17777170 floppy CSR + 17777172 floppy data register +*/ + +t_stat ry_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 1) { /* decode PA<1> */ + + case 0: /* RYCS */ + ry_csr = (ry_csr & RYCS_IMP) | RYCS_RY; /* clear junk */ + *data = ry_csr; + break; + + case 1: /* RYDB */ + *data = ry_dbr; /* return data */ + break; + } /* end switch PA */ + +return SCPE_OK; +} + +t_stat ry_wr (int32 data, int32 PA, int32 access) +{ +int32 drv; + +switch ((PA >> 1) & 1) { /* decode PA<1> */ + +/* Writing RYCS, three cases: + 1. Writing INIT, reset device + 2. Idle and writing new function + - clear error, done, transfer ready, int req + - save int enable, function, drive + - start new function + 3. Otherwise, write IE and update interrupts +*/ + + case 0: /* RYCS */ + ry_csr = (ry_csr & RYCS_IMP) | RYCS_RY; /* clear junk */ + if (access == WRITEB) data = (PA & 1)? /* write byte? */ + (ry_csr & 0377) | (data << 8): (ry_csr & ~0377) | data; + if (data & RYCS_INIT) { /* initialize? */ + ry_reset (&ry_dev); /* reset device */ + return SCPE_OK; /* end if init */ + } + if ((data & CSR_GO) && (ry_state == IDLE)) { /* new function? */ + ry_csr = (data & RYCS_RW) | RYCS_RY; + drv = ((ry_csr & RYCS_DRV)? 1: 0); /* reselect drv */ + switch (RYCS_GETFNC (data)) { + + case RYCS_FILL: case RYCS_EMPTY: + ry_state = FEWC; /* state = get wc */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + break; + + case RYCS_SDEN: + ry_state = SDCNF; /* state = get conf */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + break; + + case RYCS_ESTAT: + ry_state = ESBA; /* state = get ba */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + break; + + case RYCS_READ: case RYCS_WRITE: case RYCS_WRDEL: + ry_state = RWDS; /* state = get sector */ + ry_csr = ry_csr | RYCS_TR; /* xfer is ready */ + ry_esr = ry_esr & RYES_ID; /* clear errors */ + ry_ecode = 0; + break; + + default: + ry_state = CMD_COMPLETE; /* state = cmd compl */ + sim_activate (&ry_unit[drv], ry_cwait); + break; + } /* end switch func */ + return SCPE_OK; + } /* end if GO */ + if ((data & RYCS_IE) == 0) CLR_INT (RY); + else if ((ry_csr & (RYCS_DONE + RYCS_IE)) == RYCS_DONE) + SET_INT (RY); + ry_csr = (ry_csr & ~RYCS_RW) | (data & RYCS_RW); + break; /* end case RYCS */ + +/* Accessing RYDB, two cases: + 1. Write idle, write + 2. Write not idle and TR set, state dependent +*/ + + case 1: /* RYDB */ + if ((PA & 1) || ((ry_state != IDLE) && ((ry_csr & RYCS_TR) == 0))) + return SCPE_OK; /* if ~IDLE, need tr */ + ry_dbr = data; /* save data */ + if (ry_state != IDLE) { + drv = ((ry_csr & RYCS_DRV)? 1: 0); /* select drv */ + sim_activate (&ry_unit[drv], ry_xwait); /* sched event */ + ry_csr = ry_csr & ~RYCS_TR; /* clear xfer */ + } + break; /* end case RYDB */ + } /* end switch PA */ + +return SCPE_OK; +} + +/* Unit service; the action to be taken depends on the transfer state: + + IDLE Should never get here + FEWC Save word count, set TR, set FEBA + FEBA Save bus address, set FEXFR + FEXFR Fill/empty buffer + RWDS Save sector, set TR, set RWDT + RWDT Save track, set RWXFR + RWXFR Read/write buffer + SDCNF Check confirmation, set SDXFR + SDXFR Erase disk + CMD_COMPLETE copy requested data to ir, finish command + INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command +*/ + +t_stat ry_svc (UNIT *uptr) +{ +int32 i, t, func, bps; +static uint8 estat[8]; +uint32 ba, da; +int8 *fbuf = uptr->filebuf; + +func = RYCS_GETFNC (ry_csr); /* get function */ +bps = (ry_csr & RYCS_DEN)? RY_NUMBY: RX_NUMBY; /* get sector size */ +ba = (RYCS_GETUAE (ry_csr) << 16) | ry_ba; /* get mem addr */ +switch (ry_state) { /* case on state */ + + case IDLE: /* idle */ + return SCPE_IERR; + + case FEWC: /* word count */ + ry_wc = ry_dbr & 0377; /* save WC */ + ry_csr = ry_csr | RYCS_TR; /* set TR */ + ry_state = FEBA; /* next state */ + return SCPE_OK; + + case FEBA: /* buffer address */ + ry_ba = ry_dbr; /* save buf addr */ + ry_state = FEXFR; /* next state */ + sim_activate (uptr, ry_cwait); /* schedule xfer */ + return SCPE_OK; + + case FEXFR: /* transfer */ + if ((ry_wc << 1) > bps) { /* wc too big? */ + ry_done (RYES_WCO, 0230); /* error */ + break; + } + if (func == RYCS_FILL) { /* fill? read */ + for (i = 0; i < RY_NUMBY; i++) rx2xb[i] = 0; + t = Map_ReadB (ba, ry_wc << 1, rx2xb); + } + else t = Map_WriteB (ba, ry_wc << 1, rx2xb); + ry_wc = t >> 1; /* adjust wc */ + ry_done (t? RYES_NXM: 0, 0); /* done */ + break; + + case RWDS: /* wait for sector */ + ry_sector = ry_dbr & RX_M_SECTOR; /* save sector */ + ry_csr = ry_csr | RYCS_TR; /* set xfer */ + ry_state = RWDT; /* advance state */ + return SCPE_OK; + + case RWDT: /* wait for track */ + ry_track = ry_dbr & RX_M_TRACK; /* save track */ + ry_state = RWXFR; /* next state */ + sim_activate (uptr, /* sched xfer */ + ry_swait * abs (ry_track - uptr->TRACK)); + return SCPE_OK; + + case RWXFR: /* read/write */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + ry_done (0, 0110); /* done, error */ + return IORETURN (ry_stopioe, SCPE_UNATT); + } + if (ry_track >= RX_NUMTR) { /* bad track? */ + ry_done (0, 0040); /* done, error */ + break; + } + uptr->TRACK = ry_track; /* now on track */ + if ((ry_sector == 0) || (ry_sector > RX_NUMSC)) { /* bad sect? */ + ry_done (0, 0070); /* done, error */ + break; + } + if (((uptr->flags & UNIT_DEN) != 0) ^ + ((ry_csr & RYCS_DEN) != 0)) { /* densities agree? */ + ry_done (RYES_DERR, 0240); /* no, error */ + break; + } + da = CALC_DA (ry_track, ry_sector, bps); /* get disk address */ + if (func == RYCS_WRDEL) ry_esr = ry_esr | RYES_DD; /* del data? */ + if (func == RYCS_READ) { /* read? */ + for (i = 0; i < bps; i++) + rx2xb[i] = fbuf[da + i]; + } + else { + if (uptr->flags & UNIT_WPRT) { /* write and locked? */ + ry_done (0, 0100); /* done, error */ + break; + } + for (i = 0; i < bps; i++) /* write */ + fbuf[da + i] = rx2xb[i]; + da = da + bps; + if (da > uptr->hwmark) uptr->hwmark = da; + } + ry_done (0, 0); /* done */ + break; + + case SDCNF: /* confirm set density */ + if ((ry_dbr & 0377) != 0111) { /* confirmed? */ + ry_done (0, 0250); /* no, error */ + break; + } + ry_state = SDXFR; /* next state */ + sim_activate (uptr, ry_cwait * 100); /* schedule operation */ + break; + + case SDXFR: /* erase disk */ + for (i = 0; i < (int32) uptr->capac; i++) fbuf[i] = 0; + uptr->hwmark = (uint32) uptr->capac; + if (ry_csr & RYCS_DEN) uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + ry_done (0, 0); + break; + + + case ESBA: + ry_ba = ry_dbr; /* save WC */ + ry_state = ESXFR; /* next state */ + sim_activate (uptr, ry_cwait); /* schedule xfer */ + return SCPE_OK; + + case ESXFR: + estat[0] = ry_ecode; /* fill 8B status */ + estat[1] = ry_wc; + estat[2] = ry_unit[0].TRACK; + estat[3] = ry_unit[1].TRACK; + estat[4] = ry_track; + estat[5] = ry_sector; + estat[6] = ((ry_csr & RYCS_DRV)? 0200: 0) | + ((ry_unit[1].flags & UNIT_DEN)? 0100: 0) | + ((uptr->flags & UNIT_ATT)? 0040: 0) | + ((ry_unit[0].flags & UNIT_DEN)? 0020: 0) | + ((ry_csr & RYCS_DEN)? 0001: 0); + estat[7] = uptr->TRACK; + t = Map_WriteB (ba, 8, estat); /* DMA to memory */ + ry_done (t? RYES_NXM: 0, 0); /* done */ + break; + + case CMD_COMPLETE: /* command complete */ + ry_done (0, 0); + break; + + case INIT_COMPLETE: /* init complete */ + ry_unit[0].TRACK = 1; /* drive 0 to trk 1 */ + ry_unit[1].TRACK = 0; /* drive 1 to trk 0 */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + ry_done (RYES_ID, 0010); /* init done, error */ + break; + } + da = CALC_DA (1, 1, bps); /* track 1, sector 1 */ + for (i = 0; i < bps; i++) /* read sector */ + rx2xb[i] = fbuf[da + i]; + ry_done (RYES_ID, 0); /* set done */ + if ((ry_unit[1].flags & UNIT_ATT) == 0) ry_ecode = 0020; + break; + } /* end case state */ + +return SCPE_OK; +} + +/* Command complete. Set done and put final value in interface register, + request interrupt if needed, return to IDLE state. +*/ + +void ry_done (int32 esr_flags, int32 new_ecode) +{ +int32 drv = (ry_csr & RYCS_DRV)? 1: 0; + +ry_state = IDLE; /* now idle */ +ry_csr = ry_csr | RYCS_DONE; /* set done */ +if (ry_csr & CSR_IE) SET_INT (RY); /* if ie, intr */ +ry_esr = (ry_esr | esr_flags) & ~(RYES_USEL|RYES_DDEN|RYES_DRDY); +if (drv) ry_esr = ry_esr | RYES_USEL; /* updates RYES */ +if (ry_unit[drv].flags & UNIT_ATT) { + ry_esr = ry_esr | RYES_DRDY; + if (ry_unit[drv].flags & UNIT_DEN) + ry_esr = ry_esr | RYES_DDEN; + } +if ((new_ecode > 0) || (ry_esr & RYES_ERR)) /* test for error */ + ry_csr = ry_csr | RYCS_ERR; +ry_ecode = new_ecode; /* update ecode */ +ry_dbr = ry_esr; /* update RYDB */ +return; +} + +/* Device initialization. The RY is one of the few devices that schedules + an I/O transfer as part of its initialization. +*/ + +t_stat ry_reset (DEVICE *dptr) +{ +ry_csr = ry_dbr = 0; /* clear registers */ +ry_esr = ry_ecode = 0; /* clear error */ +ry_ba = ry_wc = 0; /* clear wc, ba */ +ry_track = ry_sector = 0; /* clear trk, sector */ +ry_state = IDLE; /* ctrl idle */ +CLR_INT (RY); /* clear int req */ +sim_cancel (&ry_unit[1]); /* cancel drive 1 */ +if (dptr->flags & UNIT_DIS) sim_cancel (&ry_unit[0]); /* disabled? */ +else if (ry_unit[0].flags & UNIT_BUF) { /* attached? */ + ry_state = INIT_COMPLETE; /* yes, sched init */ + sim_activate (&ry_unit[0], ry_swait * abs (1 - ry_unit[0].TRACK)); + } +else ry_done (RYES_ID, 0010); /* no, error */ +return auto_config (0, 0); /* run autoconfig */ +} + +/* Attach routine */ + +t_stat ry_attach (UNIT *uptr, char *cptr) +{ +uint32 sz; + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + if (sz > RX_SIZE) uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + } +uptr->capac = (uptr->flags & UNIT_DEN)? RY_SIZE: RX_SIZE; +return attach_unit (uptr, cptr); +} + +/* Set size routine */ + +t_stat ry_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = val? RY_SIZE: RX_SIZE; +return SCPE_OK; +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 026) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 042131, /* "YD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012700, 0000000, /* MOV #unit, R0 ; unit number */ + 0010003, /* MOV R0, R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0006303, /* ASL R3 */ + 0012701, 0177170, /* MOV #RYCS, R1 ; csr */ + 0005002, /* CLR R2 ; ba */ + 0005004, /* CLR R4 ; density */ + 0012705, 0000001, /* MOV #1, R5 ; sector */ + 0005104, /* DN: COM R4 ; compl dens */ + 0042704, 0177377, /* BIC #177377, R4 ; clr rest */ + 0032711, 0000040, /* RD: BIT #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0012746, 0000007, /* MOV #READ+GO, -(SP) */ + 0050316, /* BIS R3, (SP) ; or unit */ + 0050416, /* BIS R4, (SP) ; or density */ + 0012611, /* MOV (SP)+, (R1) ; read & go */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0010561, 0000002, /* MOV R5, 2(R1) ; sector */ + 0105711, /* TSTB (R1) ; xfr ready? */ + 0100376, /* BPL .-2 */ + 0012761, 0000001, 0000002, /* MOV #1, 2(R1) ; track */ + 0032711, 0000040, /* BIT #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0005711, /* TST (R1) ; error? */ + 0100003, /* BEQ OK */ + 0005704, /* TST R4 ; single? */ + 0001345, /* BNE DN ; no, try again */ + 0000000, /* HALT ; dead */ + 0012746, 0000003, /* OK: MOV #EMPTY+GO, -(SP); empty & go */ + 0050416, /* BIS R4, (SP) ; or density */ + 0012611, /* MOV (SP)+, (R1) ; read & go */ + 0105711, /* TSTB (R1) ; xfr, done? */ + 0001776, /* BPL .-2 */ + 0012746, 0000100, /* MOV #100, -(SP) ; assume sd */ + 0005704, /* TST R4 ; test dd */ + 0001401, /* BEQ .+4 */ + 0006316, /* ASL (SP) ; dd, double */ + 0011661, 0000002, /* MOV (SP), 2(R1) ; wc */ + 0105711, /* TSTB (R1) ; xfr, done? */ + 0001776, /* BPL .-2 */ + 0010261, 0000002, /* MOV R2, 2(R1) ; ba */ + 0032711, 0000040, /* BIT #40, (R1) ; ready? */ + 0001775, /* BEQ .-4 */ + 0061602, /* ADD (SP), R2 ; cvt wd to byte */ + 0062602, /* ADD (SP)+, R2 ; adv buf addr */ + 0122525, /* CMPB (R5)+, (R5)+ ; sect += 2 */ + 0020527, 0000007, /* CMP R5, #7 ; end? */ + 0101715, /* BLOS RD ; read next */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0005007 /* CLR R7 */ + }; + +t_stat ry_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +if ((ry_unit[unitno & RX_M_NUMDR].flags & UNIT_DEN) == 0) + return SCPE_NOFNC; +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & RX_M_NUMDR; +M[BOOT_CSR >> 1] = ry_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat ry_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} + +#endif diff --git a/PDP11/pdp11_stddev.c b/PDP11/pdp11_stddev.c new file mode 100644 index 0000000..eae91e7 --- /dev/null +++ b/PDP11/pdp11_stddev.c @@ -0,0 +1,483 @@ +/* pdp11_stddev.c: PDP-11 standard I/O devices simulator + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti,tto DL11 terminal input/output + clk KW11L (and other) line frequency clock + + 20-May-08 RMS Standardized clock delay at 1mips + 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock + 29-Oct-06 RMS Synced keyboard and clock + Added clock coscheduling support + 05-Jul-06 RMS Added UC only support for early DOS/RSTS + 22-Nov-05 RMS Revised for new terminal processing routines + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 07-Jul-05 RMS Removed extraneous externs + 11-Oct-04 RMS Added clock model dependencies + 28-May-04 RMS Removed SET TTI CTRL-C + 29-Dec-03 RMS Added console backpressure support + 25-Apr-03 RMS Revised for extended file support + 01-Mar-03 RMS Added SET/SHOW CLOCK FREQ, SET TTI CTRL-C + 22-Nov-02 RMS Changed terminal default to 7B for UNIX + 01-Nov-02 RMS Added 7B/8B support to terminal + 29-Sep-02 RMS Added vector display support + Split out paper tape + Split DL11 dibs + 30-May-02 RMS Widened POS to 32b + 26-Jan-02 RMS Revised for multiple timers + 09-Jan-02 RMS Fixed bugs in KW11L (found by John Dundas) + 06-Jan-02 RMS Split I/O address routines, revised enable/disable support + 29-Nov-01 RMS Added read only unit support + 09-Nov-01 RMS Added RQDX3 support + 07-Oct-01 RMS Upgraded clock to full KW11L for RSTS/E autoconfigure + 07-Sep-01 RMS Moved function prototypes, revised interrupt mechanism + 17-Jul-01 RMS Moved function prototype + 04-Jul-01 RMS Added DZ11 support + 05-Mar-01 RMS Added clock calibration support + 30-Oct-00 RMS Standardized register order + 25-Jun-98 RMS Fixed bugs in paper tape error handling +*/ + +#include "pdp11_defs.h" + +#define TTICSR_IMP (CSR_DONE + CSR_IE) /* terminal input */ +#define TTICSR_RW (CSR_IE) +#define TTOCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */ +#define TTOCSR_RW (CSR_IE) +#define CLKCSR_IMP (CSR_DONE + CSR_IE) /* real-time clock */ +#define CLKCSR_RW (CSR_IE) +#define CLK_DELAY 16667 + +extern int32 int_req[IPL_HLVL]; +extern uint32 cpu_type; + +int32 tti_csr = 0; /* control/status */ +int32 tto_csr = 0; /* control/status */ +int32 clk_csr = 0; /* control/status */ +int32 clk_tps = 60; /* ticks/second */ +int32 clk_default = 60; /* default ticks/second */ +int32 clk_fie = 0; /* force IE = 1 */ +int32 clk_fnxm = 0; /* force NXM on reg */ +int32 tmxr_poll = CLK_DELAY; /* term mux poll */ +int32 tmr_poll = CLK_DELAY; /* timer poll */ + +t_stat tti_rd (int32 *data, int32 PA, int32 access); +t_stat tti_wr (int32 data, int32 PA, int32 access); +t_stat tti_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_rd (int32 *data, int32 PA, int32 access); +t_stat tto_wr (int32 data, int32 PA, int32 access); +t_stat tto_svc (UNIT *uptr); +t_stat tto_reset (DEVICE *dptr); +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat clk_rd (int32 *data, int32 PA, int32 access); +t_stat clk_wr (int32 data, int32 PA, int32 access); +t_stat clk_svc (UNIT *uptr); +int32 clk_inta (void); +t_stat clk_reset (DEVICE *dptr); +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list +*/ + +DIB tti_dib = { + IOBA_TTI, IOLN_TTI, &tti_rd, &tti_wr, + 1, IVCL (TTI), VEC_TTI, { NULL } + }; + +UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE, 0), 0 }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 8) }, + { ORDATA (CSR, tti_csr, 16) }, + { FLDATA (INT, IREQ (TTI), INT_V_TTI) }, + { FLDATA (ERR, tti_csr, CSR_V_ERR) }, + { FLDATA (DONE, tti_csr, CSR_V_DONE) }, + { FLDATA (IE, tti_csr, CSR_V_IE) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, DEV_UBUS | DEV_QBUS + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +DIB tto_dib = { + IOBA_TTO, IOLN_TTO, &tto_rd, &tto_wr, + 1, IVCL (TTO), VEC_TTO, { NULL } + }; + +UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_7P, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 8) }, + { ORDATA (CSR, tto_csr, 16) }, + { FLDATA (INT, IREQ (TTO), INT_V_TTO) }, + { FLDATA (ERR, tto_csr, CSR_V_ERR) }, + { FLDATA (DONE, tto_csr, CSR_V_DONE) }, + { FLDATA (IE, tto_csr, CSR_V_IE) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, DEV_UBUS | DEV_QBUS + }; + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +DIB clk_dib = { + IOBA_CLK, IOLN_CLK, &clk_rd, &clk_wr, + 1, IVCL (CLK), VEC_CLK, { &clk_inta } + }; + +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; + +REG clk_reg[] = { + { ORDATA (CSR, clk_csr, 16) }, + { FLDATA (INT, IREQ (CLK), INT_V_CLK) }, + { FLDATA (DONE, clk_csr, CSR_V_DONE) }, + { FLDATA (IE, clk_csr, CSR_V_IE) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 16), PV_LEFT + REG_HRO }, + { DRDATA (DEFTPS, clk_default, 16), PV_LEFT + REG_HRO }, + { FLDATA (FIE, clk_fie, 0), REG_HIDDEN }, + { FLDATA (FNXM, clk_fnxm, 0), REG_HIDDEN }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &clk_show_freq, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, DEV_UBUS | DEV_QBUS + }; + +/* Terminal input address routines */ + +t_stat tti_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 00: /* tti csr */ + *data = tti_csr & TTICSR_IMP; + return SCPE_OK; + + case 01: /* tti buf */ + tti_csr = tti_csr & ~CSR_DONE; + CLR_INT (TTI); + *data = tti_unit.buf & 0377; + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +t_stat tti_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 00: /* tti csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) CLR_INT (TTI); + else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (TTI); + tti_csr = (tti_csr & ~TTICSR_RW) | (data & TTICSR_RW); + return SCPE_OK; + + case 01: /* tti buf */ + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +/* Terminal input service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c; + +sim_activate (uptr, KBD_WAIT (uptr->wait, tmr_poll)); /* continue poll */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +if (c & SCPE_BREAK) uptr->buf = 0; /* break? */ +else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); +uptr->pos = uptr->pos + 1; +tti_csr = tti_csr | CSR_DONE; +if (tti_csr & CSR_IE) SET_INT (TTI); +return SCPE_OK; +} + +/* Terminal input reset */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; +tti_csr = 0; +CLR_INT (TTI); +sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); +return SCPE_OK; +} + +/* Terminal output address routines */ + +t_stat tto_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 00: /* tto csr */ + *data = tto_csr & TTOCSR_IMP; + return SCPE_OK; + + case 01: /* tto buf */ + *data = tto_unit.buf; + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +t_stat tto_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 00: /* tto csr */ + if (PA & 1) return SCPE_OK; + if ((data & CSR_IE) == 0) CLR_INT (TTO); + else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (TTO); + tto_csr = (tto_csr & ~TTOCSR_RW) | (data & TTOCSR_RW); + return SCPE_OK; + + case 01: /* tto buf */ + if ((PA & 1) == 0) tto_unit.buf = data & 0377; + tto_csr = tto_csr & ~CSR_DONE; + CLR_INT (TTO); + sim_activate (&tto_unit, tto_unit.wait); + return SCPE_OK; + } /* end switch PA */ + +return SCPE_NXM; +} + +/* Terminal output service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags)); +if (c >= 0) { + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } + } +tto_csr = tto_csr | CSR_DONE; +if (tto_csr & CSR_IE) SET_INT (TTO); +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +/* Terminal output reset */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +tto_csr = CSR_DONE; +CLR_INT (TTO); +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; +tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; +return SCPE_OK; +} + +/* The line time clock has a few twists and turns through the history of 11's + + LSI-11 no CSR + LSI-11/23 (KDF11A) no CSR + PDP-11/23+ (KDF11B) no monitor bit + PDP-11/24 (KDF11U) monitor bit clears on IAK +*/ + +/* Clock I/O address routines */ + +t_stat clk_rd (int32 *data, int32 PA, int32 access) +{ +if (clk_fnxm) return SCPE_NXM; /* not there??? */ +if (CPUT (HAS_LTCM)) *data = clk_csr & CLKCSR_IMP; /* monitor bit? */ +else *data = clk_csr & (CLKCSR_IMP & ~CSR_DONE); /* no, just IE */ +return SCPE_OK; +} + +t_stat clk_wr (int32 data, int32 PA, int32 access) +{ +if (clk_fnxm) return SCPE_NXM; /* not there??? */ +if (PA & 1) return SCPE_OK; +clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW); +if (CPUT (HAS_LTCM) && ((data & CSR_DONE) == 0)) /* monitor bit? */ + clk_csr = clk_csr & ~CSR_DONE; /* clr if zero */ +if ((((clk_csr & CSR_IE) == 0) && !clk_fie) || /* unless IE+DONE */ + ((clk_csr & CSR_DONE) == 0)) CLR_INT (CLK); /* clr intr */ +return SCPE_OK; +} + +/* Clock service */ + +t_stat clk_svc (UNIT *uptr) +{ +int32 t; + +clk_csr = clk_csr | CSR_DONE; /* set done */ +if ((clk_csr & CSR_IE) || clk_fie) SET_INT (CLK); +t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ +sim_activate (&clk_unit, t); /* reactivate unit */ +tmr_poll = t; /* set timer poll */ +tmxr_poll = t; /* set mux poll */ +return SCPE_OK; +} + +/* Clock interrupt acknowledge */ + +int32 clk_inta (void) +{ +if (CPUT (CPUT_24)) clk_csr = clk_csr & ~CSR_DONE; +return clk_dib.vec; +} + +/* Clock coscheduling routine */ + +int32 clk_cosched (int32 wait) +{ +int32 t; + +t = sim_is_active (&clk_unit); +return (t? t - 1: wait); +} + +/* Clock reset */ + +t_stat clk_reset (DEVICE *dptr) +{ +if (CPUT (HAS_LTCR)) clk_fie = clk_fnxm = 0; /* reg there? */ +else clk_fie = clk_fnxm = 1; /* no, BEVENT */ +clk_tps = clk_default; /* set default tps */ +clk_csr = CSR_DONE; /* set done */ +CLR_INT (CLK); +sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init line clock */ +sim_activate_abs (&clk_unit, clk_unit.wait); /* activate unit */ +tmr_poll = clk_unit.wait; /* set timer poll */ +tmxr_poll = clk_unit.wait; /* set mux poll */ +return SCPE_OK; +} + +/* Set frequency */ + +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val != 50) && (val != 60)) return SCPE_IERR; +clk_tps = clk_default = val; +return SCPE_OK; +} + +/* Show frequency */ + +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "%dHz", clk_tps); +return SCPE_OK; +} diff --git a/PDP11/pdp11_sys.c b/PDP11/pdp11_sys.c new file mode 100644 index 0000000..c89f36d --- /dev/null +++ b/PDP11/pdp11_sys.c @@ -0,0 +1,1126 @@ +/* pdp11_sys.c: PDP-11 simulator interface + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-May-08 RMS Added KE11-A, DC11 support + Renamed DL11 + 04-Feb-08 RMS Modified to allow -A, -B use with 8b devices + 25-Jan-08 RMS Added RC11, KG11A support from John Dundas + 10-Sep-07 RMS Cleaned up binary loader + 20-Dec-06 RMS Added TA11 support + 12-Nov-06 RMS Fixed operand order in EIS instructions (found by W.F.J. Mueller) + 14-Jul-06 RMS Reordered device list + 06-Jul-06 RMS Added multiple KL11/DL11 support + 26-Jun-06 RMS Added RF11 support + 17-May-06 RMS Added CR11/CD11 support (from John Dundas) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 22-Jul-05 RMS Fixed missing , in initializer (from Doug Gwyn) + 22-Dec-03 RMS Added second DEUNA/DELUA support + 18-Oct-03 RMS Added DECtape off reel message + 06-May-03 RMS Added support for second DEQNA/DELQA + 09-Jan-03 RMS Added DELUA/DEUNA support + 17-Oct-02 RMS Fixed bugs in branch, SOB address parsing + 09-Oct-02 RMS Added DELQA support + 12-Sep-02 RMS Added TMSCP, KW11P, RX211 support, RAD50 examine + 29-Nov-01 RMS Added read only unit support + 17-Sep-01 RMS Removed multiconsole support + 26-Aug-01 RMS Added DZ11 + 20-Aug-01 RMS Updated bad block inquiry + 17-Jul-01 RMS Fixed warning from VC++ 6.0 + 27-May-01 RMS Added multiconsole support + 05-Apr-01 RMS Added support for TS11/TSV05 + 14-Mar-01 RMS Revised load/dump interface (again) + 11-Feb-01 RMS Added DECtape support + 30-Oct-00 RMS Added support for examine to file + 14-Apr-99 RMS Changed t_addr to unsigned + 09-Nov-98 RMS Fixed assignments of ROR/ROL (John Wilson) + 27-Oct-98 RMS V2.4 load interface + 08-Oct-98 RMS Fixed bug in bad block routine + 30-Mar-98 RMS Fixed bug in floating point display + 12-Nov-97 RMS Added bad block table routine +*/ + +#include "pdp11_defs.h" +#include + +extern DEVICE cpu_dev; +extern DEVICE sys_dev; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE tti_dev; +extern DEVICE tto_dev; +extern DEVICE lpt_dev; +extern DEVICE cr_dev; +extern DEVICE clk_dev; +extern DEVICE pclk_dev; +extern DEVICE dli_dev; +extern DEVICE dlo_dev; +extern DEVICE dci_dev; +extern DEVICE dco_dev; +extern DEVICE dz_dev; +extern DEVICE vh_dev; +extern DEVICE dt_dev; +extern DEVICE rc_dev; +extern DEVICE rf_dev; +extern DEVICE rk_dev; +extern DEVICE rl_dev; +extern DEVICE hk_dev; +extern DEVICE rx_dev; +extern DEVICE ry_dev; +extern DEVICE mba_dev[]; +extern DEVICE rp_dev; +extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; +extern DEVICE tm_dev; +extern DEVICE tq_dev; +extern DEVICE ts_dev; +extern DEVICE tu_dev; +extern DEVICE ta_dev; +extern DEVICE xq_dev, xqb_dev; +extern DEVICE xu_dev, xub_dev; +extern DEVICE ke_dev; +extern DEVICE kg_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint16 *M; +extern int32 saved_PC; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-11"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &cpu_dev, + &sys_dev, + &mba_dev[0], + &mba_dev[1], + &clk_dev, + &pclk_dev, + &ptr_dev, + &ptp_dev, + &tti_dev, + &tto_dev, + &cr_dev, + &lpt_dev, + &dli_dev, + &dlo_dev, + &dci_dev, + &dco_dev, + &dz_dev, + &vh_dev, + &rc_dev, + &rf_dev, + &rk_dev, + &rl_dev, + &hk_dev, + &rx_dev, + &ry_dev, + &rp_dev, + &rq_dev, + &rqb_dev, + &rqc_dev, + &rqd_dev, + &dt_dev, + &tm_dev, + &ts_dev, + &tq_dev, + &tu_dev, + &ta_dev, + &xq_dev, + &xqb_dev, + &xu_dev, + &xub_dev, + &ke_dev, + &kg_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Red stack trap", + "Odd address trap", + "Memory management trap", + "Non-existent memory trap", + "Parity error trap", + "Privilege trap", + "Illegal instruction trap", + "BPT trap", + "IOT trap", + "EMT trap", + "TRAP trap", + "Trace trap", + "Yellow stack trap", + "Powerfail trap", + "Floating point exception", + "HALT instruction", + "Breakpoint", + "Wait state", + "Trap vector fetch abort", + "Trap stack push abort", + "RQDX3 consistency error", + "Sanity timer expired", + "DECtape off reel" + }; + +/* Binary loader. + + Loader format consists of blocks, optionally preceded, separated, and + followed by zeroes. Each block consists of: + + 001 --- + xxx | + lo_count | + hi_count | + lo_origin > count bytes + hi_origin | + data byte | + : | + data byte --- + checksum + + If the byte count is exactly six, the block is the last on the tape, and + there is no checksum. If the origin is not 000001, then the origin is + the PC at which to start the program. +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 c[6], d, i, cnt, csum; +uint32 org; + +if ((*cptr != 0) || (flag != 0)) + return SCPE_ARG; +do { /* block loop */ + csum = 0; /* init checksum */ + for (i = 0; i < 6; ) { /* 6 char header */ + if ((c[i] = getc (fileref)) == EOF) + return SCPE_FMT; + if ((i != 0) || (c[i] == 1)) /* 1st must be 1 */ + csum = csum + c[i++]; /* add into csum */ + } + cnt = (c[3] << 8) | c[2]; /* count */ + org = (c[5] << 8) | c[4]; /* origin */ + if (cnt < 6) /* invalid? */ + return SCPE_FMT; + if (cnt == 6) { /* end block? */ + if (org != 1) /* set PC? */ + saved_PC = org & 0177776; + return SCPE_OK; + } + for (i = 6; i < cnt; i++) { /* exclude hdr */ + if ((d = getc (fileref)) == EOF) /* data char */ + return SCPE_FMT; + csum = csum + d; /* add into csum */ + if (org >= MEMSIZE) /* invalid addr? */ + return SCPE_NXM; + M[org >> 1] = (org & 1)? /* store data */ + (M[org >> 1] & 0377) | (d << 8): + (M[org >> 1] & 0177400) | d; + org = (org + 1) & 0177777; /* inc origin */ + } + if ((d = getc (fileref)) == EOF) /* get csum */ + return SCPE_FMT; + csum = csum + d; /* add in */ + } while ((csum & 0377) == 0); /* result mbz */ +return SCPE_CSUM; +} + +/* Factory bad block table creation routine + + This routine writes a DEC standard 044 compliant bad block table on the + last track of the specified unit. The bad block table consists of 10 + repetitions of the same table, formatted as follows: + + words 0-1 pack id number + words 2-3 cylinder/sector/surface specifications + : + words n-n+1 end of table (-1,-1) + + Inputs: + uptr = pointer to unit + sec = number of sectors per surface + wds = number of words per sector + Outputs: + sta = status code +*/ + +t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds) +{ +int32 i, da; +uint16 *buf; + +if ((sec < 2) || (wds < 16)) + return SCPE_ARG; +if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; +if (uptr->flags & UNIT_RO) + return SCPE_RO; +if (!get_yn ("Create bad block table on last track? [N]", FALSE)) + return SCPE_OK; +da = (uptr->capac - (sec * wds)) * sizeof (uint16); +if (fseek (uptr->fileref, da, SEEK_SET)) + return SCPE_IOERR; +if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL) + return SCPE_MEM; +buf[0] = buf[1] = 012345u; +buf[2] = buf[3] = 0; +for (i = 4; i < wds; i++) buf[i] = 0177777u; +for (i = 0; (i < sec) && (i < 10); i++) + fxwrite (buf, sizeof (uint16), wds, uptr->fileref); +free (buf); +if (ferror (uptr->fileref)) + return SCPE_IOERR; +return SCPE_OK; +} + +/* Symbol tables */ + +#define I_V_L 16 /* long mode */ +#define I_V_D 17 /* double mode */ +#define I_L (1 << I_V_L) +#define I_D (1 << I_V_D) + +/* Warning: for literals, the class number MUST equal the field width!! */ + +#define I_V_CL 18 /* class bits */ +#define I_M_CL 037 /* class mask */ +#define I_V_NPN 0 /* no operands */ +#define I_V_REG 1 /* reg */ +#define I_V_SOP 2 /* operand */ +#define I_V_3B 3 /* 3b literal */ +#define I_V_FOP 4 /* flt operand */ +#define I_V_AFOP 5 /* fac, flt operand */ +#define I_V_6B 6 /* 6b literal */ +#define I_V_BR 7 /* cond branch */ +#define I_V_8B 8 /* 8b literal */ +#define I_V_SOB 9 /* reg, disp */ +#define I_V_RSOP 10 /* reg, operand */ +#define I_V_ASOP 11 /* fac, operand */ +#define I_V_ASMD 12 /* fac, moded int op */ +#define I_V_DOP 13 /* double operand */ +#define I_V_CCC 14 /* CC clear */ +#define I_V_CCS 15 /* CC set */ +#define I_V_SOPR 16 /* operand, reg */ +#define I_NPN (I_V_NPN << I_V_CL) +#define I_REG (I_V_REG << I_V_CL) +#define I_3B (I_V_3B << I_V_CL) +#define I_SOP (I_V_SOP << I_V_CL) +#define I_FOP (I_V_FOP << I_V_CL) +#define I_6B (I_V_6B << I_V_CL) +#define I_BR (I_V_BR << I_V_CL) +#define I_8B (I_V_8B << I_V_CL) +#define I_AFOP (I_V_AFOP << I_V_CL) +#define I_ASOP (I_V_ASOP << I_V_CL) +#define I_RSOP (I_V_RSOP << I_V_CL) +#define I_SOB (I_V_SOB << I_V_CL) +#define I_ASMD (I_V_ASMD << I_V_CL) +#define I_DOP (I_V_DOP << I_V_CL) +#define I_CCC (I_V_CCC << I_V_CL) +#define I_CCS (I_V_CCS << I_V_CL) +#define I_SOPR (I_V_SOPR << I_V_CL) + +static const int32 masks[] = { +0177777, 0177770, 0177700, 0177770, +0177700+I_D, 0177400+I_D, 0177700, 0177400, +0177400, 0177000, 0177000, 0177400, +0177400+I_D+I_L, 0170000, 0177777, 0177777, +0177000 +}; + +static const char *opcode[] = { +"HALT","WAIT","RTI","BPT", +"IOT","RESET","RTT","MFPT", +"JMP","RTS","SPL", +"NOP","CLC","CLV","CLV CLC", +"CLZ","CLZ CLC","CLZ CLV","CLZ CLV CLC", +"CLN","CLN CLC","CLN CLV","CLN CLV CLC", +"CLN CLZ","CLN CLZ CLC","CLN CLZ CLC","CCC", +"NOP","SEC","SEV","SEV SEC", +"SEZ","SEZ SEC","SEZ SEV","SEZ SEV SEC", +"SEN","SEN SEC","SEN SEV","SEN SEV SEC", +"SEN SEZ","SEN SEZ SEC","SEN SEZ SEC","SCC", +"SWAB","BR","BNE","BEQ", +"BGE","BLT","BGT","BLE", +"JSR", +"CLR","COM","INC","DEC", +"NEG","ADC","SBC","TST", +"ROR","ROL","ASR","ASL", +"MARK","MFPI","MTPI","SXT", +"CSM", "TSTSET","WRTLCK", +"MOV","CMP","BIT","BIC", +"BIS","ADD", +"MUL","DIV","ASH","ASHC", +"XOR", +"FADD","FSUB","FMUL","FDIV", +"L2DR", +"MOVC","MOVRC","MOVTC", +"LOCC","SKPC","SCANC","SPANC", +"CMPC","MATC", +"ADDN","SUBN","CMPN","CVTNL", +"CVTPN","CVTNP","ASHN","CVTLN", +"L3DR", +"ADDP","SUBP","CMPP","CVTPL", +"MULP","DIVP","ASHP","CVTLP", +"MOVCI","MOVRCI","MOVTCI", +"LOCCI","SKPCI","SCANCI","SPANCI", +"CMPCI","MATCI", +"ADDNI","SUBNI","CMPNI","CVTNLI", +"CVTPNI","CVTNPI","ASHNI","CVTLNI", +"ADDPI","SUBPI","CMPPI","CVTPLI", +"MULPI","DIVPI","ASHPI","CVTLPI", +"SOB", +"BPL","BMI","BHI","BLOS", +"BVC","BVS","BCC","BCS", +"BHIS","BLO", /* encode only */ +"EMT","TRAP", +"CLRB","COMB","INCB","DECB", +"NEGB","ADCB","SBCB","TSTB", +"RORB","ROLB","ASRB","ASLB", +"MTPS","MFPD","MTPD","MFPS", +"MOVB","CMPB","BITB","BICB", +"BISB","SUB", +"CFCC","SETF","SETI","SETD","SETL", +"LDFPS","STFPS","STST", +"CLRF","CLRD","TSTF","TSTD", +"ABSF","ABSD","NEGF","NEGD", +"MULF","MULD","MODF","MODD", +"ADDF","ADDD","LDF","LDD", +"SUBF","SUBD","CMPF","CMPD", +"STF","STD","DIVF","DIVD", +"STEXP", +"STCFI","STCDI","STCFL","STCDL", +"STCFD","STCDF", +"LDEXP", +"LDCIF","LDCID","LDCLF","LDCLD", +"LDCFD","LDCDF", +NULL +}; + +static const int32 opc_val[] = { +0000000+I_NPN, 0000001+I_NPN, 0000002+I_NPN, 0000003+I_NPN, +0000004+I_NPN, 0000005+I_NPN, 0000006+I_NPN, 0000007+I_NPN, +0000100+I_SOP, 0000200+I_REG, 0000230+I_3B, +0000240+I_CCC, 0000241+I_CCC, 0000242+I_CCC, 0000243+I_NPN, +0000244+I_CCC, 0000245+I_NPN, 0000246+I_NPN, 0000247+I_NPN, +0000250+I_CCC, 0000251+I_NPN, 0000252+I_NPN, 0000253+I_NPN, +0000254+I_NPN, 0000255+I_NPN, 0000256+I_NPN, 0000257+I_CCC, +0000260+I_CCS, 0000261+I_CCS, 0000262+I_CCS, 0000263+I_NPN, +0000264+I_CCS, 0000265+I_NPN, 0000266+I_NPN, 0000267+I_NPN, +0000270+I_CCS, 0000271+I_NPN, 0000272+I_NPN, 0000273+I_NPN, +0000274+I_NPN, 0000275+I_NPN, 0000276+I_NPN, 0000277+I_CCS, +0000300+I_SOP, 0000400+I_BR, 0001000+I_BR, 0001400+I_BR, +0002000+I_BR, 0002400+I_BR, 0003000+I_BR, 0003400+I_BR, +0004000+I_RSOP, +0005000+I_SOP, 0005100+I_SOP, 0005200+I_SOP, 0005300+I_SOP, +0005400+I_SOP, 0005500+I_SOP, 0005600+I_SOP, 0005700+I_SOP, +0006000+I_SOP, 0006100+I_SOP, 0006200+I_SOP, 0006300+I_SOP, +0006400+I_6B, 0006500+I_SOP, 0006600+I_SOP, 0006700+I_SOP, +0007000+I_SOP, 0007200+I_SOP, 0007300+I_SOP, +0010000+I_DOP, 0020000+I_DOP, 0030000+I_DOP, 0040000+I_DOP, +0050000+I_DOP, 0060000+I_DOP, +0070000+I_SOPR, 0071000+I_SOPR, 0072000+I_SOPR, 0073000+I_SOPR, +0074000+I_RSOP, +0075000+I_REG, 0075010+I_REG, 0075020+I_REG, 0075030+I_REG, +0076020+I_REG, +0076030+I_NPN, 0076031+I_NPN, 0076032+I_NPN, +0076040+I_NPN, 0076041+I_NPN, 0076042+I_NPN, 0076043+I_NPN, +0076044+I_NPN, 0076045+I_NPN, +0076050+I_NPN, 0076051+I_NPN, 0076052+I_NPN, 0076053+I_NPN, +0076054+I_NPN, 0076055+I_NPN, 0076056+I_NPN, 0076057+I_NPN, +0076060+I_REG, +0076070+I_NPN, 0076071+I_NPN, 0076072+I_NPN, 0076073+I_NPN, +0076074+I_NPN, 0076075+I_NPN, 0076076+I_NPN, 0076077+I_NPN, +0076130+I_NPN, 0076131+I_NPN, 0076132+I_NPN, +0076140+I_NPN, 0076141+I_NPN, 0076142+I_NPN, 0076143+I_NPN, +0076144+I_NPN, 0076145+I_NPN, +0076150+I_NPN, 0076151+I_NPN, 0076152+I_NPN, 0076153+I_NPN, +0076154+I_NPN, 0076155+I_NPN, 0076156+I_NPN, 0076157+I_NPN, +0076170+I_NPN, 0076171+I_NPN, 0076172+I_NPN, 0076173+I_NPN, +0076174+I_NPN, 0076175+I_NPN, 0076176+I_NPN, 0076177+I_NPN, +0077000+I_SOB, +0100000+I_BR, 0100400+I_BR, 0101000+I_BR, 0101400+I_BR, +0102000+I_BR, 0102400+I_BR, 0103000+I_BR, 0103400+I_BR, +0103000+I_BR, 0103400+I_BR, +0104000+I_8B, 0104400+I_8B, +0105000+I_SOP, 0105100+I_SOP, 0105200+I_SOP, 0105300+I_SOP, +0105400+I_SOP, 0105500+I_SOP, 0105600+I_SOP, 0105700+I_SOP, +0106000+I_SOP, 0106100+I_SOP, 0106200+I_SOP, 0106300+I_SOP, +0106400+I_SOP, 0106500+I_SOP, 0106600+I_SOP, 0106700+I_SOP, +0110000+I_DOP, 0120000+I_DOP, 0130000+I_DOP, 0140000+I_DOP, +0150000+I_DOP, 0160000+I_DOP, +0170000+I_NPN, 0170001+I_NPN, 0170002+I_NPN, 0170011+I_NPN, 0170012+I_NPN, +0170100+I_SOP, 0170200+I_SOP, 0170300+I_SOP, +0170400+I_FOP, 0170400+I_FOP+I_D, 0170500+I_FOP, 0170500+I_FOP+I_D, +0170600+I_FOP, 0170600+I_FOP+I_D, 0170700+I_FOP, 0170700+I_FOP+I_D, +0171000+I_AFOP, 0171000+I_AFOP+I_D, 0171400+I_AFOP, 0171400+I_AFOP+I_D, +0172000+I_AFOP, 0172000+I_AFOP+I_D, 0172400+I_AFOP, 0172400+I_AFOP+I_D, +0173000+I_AFOP, 0173000+I_AFOP+I_D, 0173400+I_AFOP, 0173400+I_AFOP+I_D, +0174000+I_AFOP, 0174000+I_AFOP+I_D, 0174400+I_AFOP, 0174400+I_AFOP+I_D, +0175000+I_ASOP, +0175400+I_ASMD, 0175400+I_ASMD+I_D, 0175400+I_ASMD+I_L, 0175400+I_ASMD+I_D+I_L, +0176000+I_AFOP, 0176000+I_AFOP+I_D, +0176400+I_ASOP, +0177000+I_ASMD, 0177000+I_ASMD+I_D, 0177000+I_ASMD+I_L, 0177000+I_ASMD+I_D+I_L, +0177400+I_AFOP, 0177400+I_AFOP+I_D, +-1 +}; + +static const char *rname [] = { + "R0", "R1", "R2", "R3", "R4", "R5", "SP", "PC" + }; + +static const char *fname [] = { + "F0", "F1", "F2", "F3", "F4", "F5", "?6", "?7" + }; + +static const char r50_to_asc[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; + +/* Specifier decode + + Inputs: + *of = output stream + addr = current PC + spec = specifier + nval = next word + flag = TRUE if decoding for CPU + iflag = TRUE if decoding integer instruction + Outputs: + count = -number of extra words retired +*/ + +int32 fprint_spec (FILE *of, t_addr addr, int32 spec, t_value nval, + int32 flag, int32 iflag) +{ +int32 reg, mode; +static const int32 rgwd[8] = { 0, 0, 0, 0, 0, 0, -1, -1 }; +static const int32 pcwd[8] = { 0, 0, -1, -1, 0, 0, -1, -1 }; + +reg = spec & 07; +mode = ((spec >> 3) & 07); +switch (mode) { + + case 0: + if (iflag) + fprintf (of, "%s", rname[reg]); + else fprintf (of, "%s", fname[reg]); + break; + + case 1: + fprintf (of, "(%s)", rname[reg]); + break; + + case 2: + if (reg != 7) + fprintf (of, "(%s)+", rname[reg]); + else fprintf (of, "#%-o", nval); + break; + + case 3: + if (reg != 7) + fprintf (of, "@(%s)+", rname[reg]); + else fprintf (of, "@#%-o", nval); + break; + + case 4: + fprintf (of, "-(%s)", rname[reg]); + break; + + case 5: + fprintf (of, "@-(%s)", rname[reg]); + break; + + case 6: + if ((reg != 7) || !flag) + fprintf (of, "%-o(%s)", nval, rname[reg]); + else fprintf (of, "%-o", (nval + addr + 4) & 0177777); + break; + + case 7: + if ((reg != 7) || !flag) + fprintf (of, "@%-o(%s)", nval, rname[reg]); + else fprintf (of, "@%-o", (nval + addr + 4) & 0177777); + break; + } /* end case */ + +return ((reg == 07)? pcwd[mode]: rgwd[mode]); +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, c1, c2, c3, inst, fac, srcm, srcr, dstm, dstr; +int32 bflag, l8b, brdisp, wd1, wd2; +extern int32 FPS; + +bflag = 0; /* assume 16b */ +cflag = (uptr == NULL) || (uptr == &cpu_unit); /* cpu? */ +if (!cflag) { /* not cpu? */ + DEVICE *dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + if (dptr->dwidth < 16) + bflag = 1; + } + +if (sw & SWMASK ('A')) { /* ASCII? */ + if (bflag) + c1 = val[0] & 0177; + else c1 = (val[0] >> ((addr & 1)? 8: 0)) & 0177; + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + return 0; + } +if (sw & SWMASK ('B')) { /* byte? */ + if (bflag) + c1 = val[0] & 0177; + else c1 = (val[0] >> ((addr & 1)? 8: 0)) & 0377; + fprintf (of, "%o", c1); + return 0; + } +if (bflag) return SCPE_ARG; /* 16b only */ + +if (sw & SWMASK ('C')) { /* character? */ + c1 = val[0] & 0177; + c2 = (val[0] >> 8) & 0177; + fprintf (of, (c1 < 040)? "<%03o>": "%c", c1); + fprintf (of, (c2 < 040)? "<%03o>": "%c", c2); + return -1; + } +if (sw & SWMASK ('R')) { /* radix 50? */ + if (val[0] > 0174777) return SCPE_ARG; /* max value */ + c3 = val[0] % 050; + c2 = (val[0] / 050) % 050; + c1 = val[0] / (050 * 050); + fprintf (of, "%c%c%c", r50_to_asc[c1], + r50_to_asc[c2], r50_to_asc[c3]); + return -1; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +inst = val[0] | ((FPS << (I_V_L - FPS_V_L)) & I_L) | + ((FPS << (I_V_D - FPS_V_D)) & I_D); /* inst + fp mode */ +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + if ((opc_val[i] & 0777777) == (inst & masks[j])) { /* match? */ + srcm = (inst >> 6) & 077; /* opr fields */ + srcr = srcm & 07; + fac = srcm & 03; + dstm = inst & 077; + dstr = dstm & 07; + l8b = inst & 0377; + wd1 = wd2 = 0; + switch (j) { /* case on class */ + + case I_V_NPN: case I_V_CCC: case I_V_CCS: /* no operands */ + fprintf (of, "%s", opcode[i]); + break; + + case I_V_REG: /* reg */ + fprintf (of, "%s %-s", opcode[i], rname[dstr]); + break; + + case I_V_SOP: /* sop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); + break; + + case I_V_3B: /* 3b */ + fprintf (of, "%s %-o", opcode[i], dstr); + break; + + case I_V_FOP: /* fop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, dstm, val[1], cflag, FALSE); + break; + + case I_V_AFOP: /* afop */ + fprintf (of, "%s %s,", opcode[i], fname[fac]); + wd1 = fprint_spec (of, addr, dstm, val[1], cflag, FALSE); + break; + + case I_V_6B: /* 6b */ + fprintf (of, "%s %-o", opcode[i], dstm); + break; + + case I_V_BR: /* cond branch */ + fprintf (of, "%s ", opcode[i]); + brdisp = (l8b + l8b + ((l8b & 0200)? 0177002: 2)) & 0177777; + if (cflag) + fprintf (of, "%-o", (addr + brdisp) & 0177777); + else if (brdisp < 01000) + fprintf (of, ".+%-o", brdisp); + else fprintf (of, ".-%-o", 0200000 - brdisp); + break; + + case I_V_8B: /* 8b */ + fprintf (of, "%s %-o", opcode[i], l8b); + break; + + case I_V_SOB: /* sob */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + brdisp = (dstm * 2) - 2; + if (cflag) + fprintf (of, "%-o", (addr - brdisp) & 0177777); + else if (brdisp <= 0) + fprintf (of, ".+%-o", -brdisp); + else fprintf (of, ".-%-o", brdisp); + break; + + case I_V_RSOP: /* rsop */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); + break; + + case I_V_SOPR: /* sopr */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); + fprintf (of, ",%s", rname[srcr]); + break; + + case I_V_ASOP: case I_V_ASMD: /* asop, asmd */ + fprintf (of, "%s %s,", opcode[i], fname[fac]); + wd1 = fprint_spec (of, addr, dstm, val[1], cflag, TRUE); + break; + + case I_V_DOP: /* dop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, srcm, val[1], cflag, TRUE); + fprintf (of, ","); + wd2 = fprint_spec (of, addr - wd1 - wd1, dstm, + val[1 - wd1], cflag, TRUE); + break; + } /* end case */ + return ((wd1 + wd2) * 2) - 1; + } /* end if */ + } /* end for */ +return SCPE_ARG; /* no match */ +} + +#define A_PND 100 /* # seen */ +#define A_MIN 040 /* -( seen */ +#define A_PAR 020 /* (Rn) seen */ +#define A_REG 010 /* Rn seen */ +#define A_PLS 004 /* + seen */ +#define A_NUM 002 /* number seen */ +#define A_REL 001 /* relative addr seen */ + +/* Register number + + Inputs: + *cptr = pointer to input string + *strings = pointer to register names + mchar = character to match after register name + Outputs: + rnum = 0..7 if a legitimate register + < 0 if error +*/ + +int32 get_reg (char *cptr, const char *strings[], char mchar) +{ +int32 i; + +if (*(cptr + 2) != mchar) return -1; +for (i = 0; i < 8; i++) { + if (strncmp (cptr, strings[i], 2) == 0) + return i; + } +return -1; +} + +/* Number or memory address + + Inputs: + *cptr = pointer to input string + *dptr = pointer to output displacement + *pflag = pointer to accumulating flags + Outputs: + cptr = pointer to next character in input string + NULL if parsing error + + Flags: 0 (no result), A_NUM (number), A_REL (relative) +*/ + +char *get_addr (char *cptr, int32 *dptr, int32 *pflag) +{ +int32 val, minus; +char *tptr; + +minus = 0; + +if (*cptr == '.') { /* relative? */ + *pflag = *pflag | A_REL; + cptr++; + } +if (*cptr == '+') { /* +? */ + *pflag = *pflag | A_NUM; + cptr++; + } +if (*cptr == '-') { /* -? */ + *pflag = *pflag | A_NUM; + minus = 1; + cptr++; + } +errno = 0; +val = strtoul (cptr, &tptr, 8); +if (cptr == tptr) { /* no number? */ + if (*pflag == (A_REL + A_NUM)) /* .+, .-? */ + return NULL; + *dptr = 0; + return cptr; + } +if (errno || (*pflag == A_REL)) /* .n? */ + return NULL; +*dptr = (minus? -val: val) & 0177777; +*pflag = *pflag | A_NUM; +return tptr; +} + +/* Specifier decode + + Inputs: + *cptr = pointer to input string + addr = current PC + n1 = 0 if no extra word used + -1 if extra word used in prior decode + *sptr = pointer to output specifier + *dptr = pointer to output displacement + cflag = true if parsing for the CPU + iflag = true if integer specifier + Outputs: + status = = -1 extra word decoded + = 0 ok + = +1 error +*/ + +t_stat get_spec (char *cptr, t_addr addr, int32 n1, int32 *sptr, t_value *dptr, + int32 cflag, int32 iflag) +{ +int32 reg, indir, pflag, disp; + +indir = 0; /* no indirect */ +pflag = 0; + +if (*cptr == '@') { /* indirect? */ + indir = 010; + cptr++; + } +if (*cptr == '#') { /* literal? */ + pflag = pflag | A_PND; + cptr++; + } +if (strncmp (cptr, "-(", 2) == 0) { /* autodecrement? */ + pflag = pflag | A_MIN; + cptr++; + } +else if ((cptr = get_addr (cptr, &disp, &pflag)) == NULL) return 1; +if (*cptr == '(') { /* register index? */ + pflag = pflag | A_PAR; + if ((reg = get_reg (cptr + 1, rname, ')')) < 0) + return 1; + cptr = cptr + 4; + if (*cptr == '+') { /* autoincrement? */ + pflag = pflag | A_PLS; + cptr++; + } + } +else if ((reg = get_reg (cptr, iflag? rname: fname, 0)) >= 0) { + pflag = pflag | A_REG; + cptr = cptr + 2; + } +if (*cptr != 0) /* all done? */ + return 1; +switch (pflag) { /* case on syntax */ + + case A_REG: /* Rn, @Rn */ + *sptr = indir + reg; + return 0; + + case A_PAR: /* (Rn), @(Rn) */ + if (indir) { /* @(Rn) = @0(Rn) */ + *sptr = 070 + reg; + *dptr = 0; + return -1; + } + else *sptr = 010 + reg; + return 0; + + case A_PAR+A_PLS: /* (Rn)+, @(Rn)+ */ + *sptr = 020 + indir + reg; + return 0; + + case A_MIN+A_PAR: /* -(Rn), @-(Rn) */ + *sptr = 040 + indir + reg; + return 0; + + case A_NUM+A_PAR: /* d(Rn), @d(Rn) */ + *sptr = 060 + indir + reg; + *dptr = disp; + return -1; + + case A_PND+A_REL: case A_PND+A_REL+A_NUM: /* #.+n, @#.+n */ + if (!cflag) + return 1; + disp = (disp + addr) & 0177777; /* fall through */ + case A_PND+A_NUM: /* #n, @#n */ + *sptr = 027 + indir; + *dptr = disp; + return -1; + + case A_REL: case A_REL+A_NUM: /* .+n, @.+n */ + *sptr = 067 + indir; + *dptr = (disp - 4 + (2 * n1)) & 0177777; + return -1; + + case A_NUM: /* n, @n */ + if (cflag) { /* CPU - use rel */ + *sptr = 067 + indir; + *dptr = (disp - addr - 4 + (2 * n1)) & 0177777; + } + else { + if (indir) return 1; /* other - use abs */ + *sptr = 037; + *dptr = disp; + } + return -1; + + default: + return 1; + } /* end case */ +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 bflag, cflag, d, i, j, reg, spec, n1, n2, disp, pflag; +t_value by; +t_stat r; +char *tptr, gbuf[CBUFSIZE]; + +bflag = 0; /* assume 16b */ +cflag = (uptr == NULL) || (uptr == &cpu_unit); /* cpu? */ +if (!cflag) { /* not cpu? */ + DEVICE *dptr = find_dev_from_unit (uptr); + if (dptr == NULL) + return SCPE_IERR; + if (dptr->dwidth < 16) + bflag = 1; + } + +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + if (bflag) + val[0] = (t_value) cptr[0]; + else val[0] = (addr & 1)? + (val[0] & 0377) | (((t_value) cptr[0]) << 8): + (val[0] & ~0377) | ((t_value) cptr[0]); + return 0; + } +if (sw & SWMASK ('B')) { /* byte? */ + by = get_uint (cptr, 8, 0377, &r); /* get byte */ + if (r != SCPE_OK) + return SCPE_ARG; + if (bflag) + val[0] = by; + else val[0] = (addr & 1)? + (val[0] & 0377) | (by << 8): + (val[0] & ~0377) | by; + return 0; + } +if (bflag) return SCPE_ARG; + +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) /* must have 1 char */ + return SCPE_ARG; + val[0] = ((t_value) cptr[1] << 8) | (t_value) cptr[0]; + return -1; + } +if (sw & SWMASK ('R')) /* radix 50 */ + return SCPE_ARG; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +n1 = n2 = pflag = 0; +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) + return SCPE_ARG; +val[0] = opc_val[i] & 0177777; /* get value */ +j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_NPN: /* no operand */ + break; + + case I_V_REG: /* register */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((reg = get_reg (gbuf, rname, 0)) < 0) + return SCPE_ARG; + val[0] = val[0] | reg; + break; + + case I_V_3B: case I_V_6B: case I_V_8B: /* xb literal */ + cptr = get_glyph (cptr, gbuf, 0); /* get literal */ + d = get_uint (gbuf, 8, (1 << j) - 1, &r); + if (r != SCPE_OK) + return SCPE_ARG; + val[0] = val[0] | d; /* put in place */ + break; + + case I_V_BR: /* cond br */ + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + tptr = get_addr (gbuf, &disp, &pflag); /* parse */ + if ((tptr == NULL) || (*tptr != 0)) + return SCPE_ARG; + if ((pflag & A_REL) == 0) { + if (cflag) disp = (disp - addr) & 0177777; + else return SCPE_ARG; + } + if ((disp & 1) || (disp > 0400) && (disp < 0177402)) + return SCPE_ARG; + val[0] = val[0] | (((disp - 2) >> 1) & 0377); + break; + + case I_V_SOB: /* sob */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, rname, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | (reg << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + tptr = get_addr (gbuf, &disp, &pflag); /* parse */ + if ((tptr == NULL) || (*tptr != 0)) + return SCPE_ARG; + if ((pflag & A_REL) == 0) { + if (cflag) disp = (disp - addr) & 0177777; + else return SCPE_ARG; + } + if ((disp & 1) || ((disp > 2) && (disp < 0177604))) + return SCPE_ARG; + val[0] = val[0] | (((2 - disp) >> 1) & 077); + break; + + case I_V_RSOP: /* reg, sop */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, rname, 0)) < 0) + return SCPE_ARG; + val[0] = val[0] | (reg << 6); /* fall through */ + case I_V_SOP: /* sop */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; + + case I_V_SOPR: /* dop, reg */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((reg = get_reg (gbuf, rname, 0)) < 0) + return SCPE_ARG; + val[0] = val[0] | (reg << 6); + break; + + case I_V_AFOP: case I_V_ASOP: case I_V_ASMD: /* fac, (s)fop */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, fname, 0)) < 0) + return SCPE_ARG; + if (reg > 3) + return SCPE_ARG; + val[0] = val[0] | (reg << 6); /* fall through */ + case I_V_FOP: /* fop */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, + (j == I_V_ASOP) || (j == I_V_ASMD))) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; + + case I_V_DOP: /* double op */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((n1 = get_spec (gbuf, addr, 0, &spec, &val[1], cflag, TRUE)) > 0) + return SCPE_ARG; + val[0] = val[0] | (spec << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n2 = get_spec (gbuf, addr, n1, &spec, &val[1 - n1], + cflag, TRUE)) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; + + case I_V_CCC: case I_V_CCS: /* cond code oper */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if ((((opc_val[i] >> I_V_CL) & I_M_CL) != j) || + (opcode[i] == NULL)) + return SCPE_ARG; + val[0] = val[0] | (opc_val[i] & 0177777); + } + break; + + default: + return SCPE_ARG; + } + +if (*cptr != 0) /* junk at end? */ + return SCPE_ARG; +return ((n1 + n2) * 2) - 1; +} diff --git a/PDP11/pdp11_ta.c b/PDP11/pdp11_ta.c new file mode 100644 index 0000000..c7db6ab --- /dev/null +++ b/PDP11/pdp11_ta.c @@ -0,0 +1,596 @@ +/* pdp11_ta.c: PDP-11 cassette tape simulator + + Copyright (c) 2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ATAION OF CONTRATA, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNETAION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ta TA11/TU60 cassette tape + + 06-Aug-07 RMS Foward op at BOT skips initial file gap + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. + + Cassette format differs in one very significant way: it has file gaps + rather than file marks. If the controller spaces or reads into a file + gap and then reverses direction, the file gap is not seen again. This + is in contrast to magnetic tapes, where the file mark is a character + sequence and is seen again if direction is reversed. In addition, + cassettes have an initial file gap which is automatically skipped on + forward operations from beginning of tape. +*/ + +#include "pdp11_defs.h" +#include "sim_tape.h" + +#define TA_NUMDR 2 /* #drives */ +#define FNC u3 /* unit function */ +#define UST u4 /* unit status */ +#define TA_SIZE 93000 /* chars/tape */ +#define TA_MAXFR (TA_SIZE) /* max record lnt */ + +/* Control/status - TACS */ + +#define TACS_ERR (1 << CSR_V_ERR) /* error */ +#define TACS_CRC 0040000 /* CRC */ +#define TACS_BEOT 0020000 /* BOT/EOT */ +#define TACS_WLK 0010000 /* write lock */ +#define TACS_EOF 0004000 /* end file */ +#define TACS_TIM 0002000 /* timing */ +#define TACS_EMP 0001000 /* empty */ +#define TACS_V_UNIT 8 /* unit */ +#define TACS_M_UNIT (TA_NUMDR - 1) +#define TACS_UNIT (TACS_M_UNIT << TACS_V_UNIT) +#define TACS_TR (1 << CSR_V_DONE) /* transfer req */ +#define TACS_IE (1 << CSR_V_IE) /* interrupt enable */ +#define TACS_RDY 0000040 /* ready */ +#define TACS_ILBS 0000020 /* start CRC */ +#define TACS_V_FNC 1 /* function */ +#define TACS_M_FNC 07 +#define TACS_WFG 00 +#define TACS_WRITE 01 +#define TACS_READ 02 +#define TACS_SRF 03 +#define TACS_SRB 04 +#define TACS_SFF 05 +#define TACS_SFB 06 +#define TACS_REW 07 +#define TACS_2ND 010 +#define TACS_3RD 030 +#define TACS_FNC (TACS_M_FNC << TACS_V_FNC) +#define TACS_GO (1 << CSR_V_GO) /* go */ +#define TACS_W (TACS_UNIT|TACS_IE|TACS_ILBS|TACS_FNC) +#define TACS_XFRERR (TACS_ERR|TACS_CRC|TACS_WLK|TACS_EOF|TACS_TIM) +#define GET_UNIT(x) (((x) >> TACS_V_UNIT) & TACS_M_UNIT) +#define GET_FNC(x) (((x) >> TACS_V_FNC) & TACS_M_FNC) + +/* Function code flags */ + +#define OP_WRI 01 /* op is a write */ +#define OP_REV 02 /* op is rev motion */ +#define OP_FWD 04 /* op is fwd motion */ + +/* Unit status flags */ + +#define UST_REV (OP_REV) /* last op was rev */ +#define UST_GAP 01 /* last op hit gap */ + +extern int32 int_req[IPL_HLVL]; +extern FILE *sim_deb; + +uint32 ta_cs = 0; /* control/status */ +uint32 ta_idb = 0; /* input data buf */ +uint32 ta_odb = 0; /* output data buf */ +uint32 ta_write = 0; /* TU60 write flag */ +uint32 ta_bptr = 0; /* buf ptr */ +uint32 ta_blnt = 0; /* buf length */ +int32 ta_stime = 1000; /* start time */ +int32 ta_ctime = 100; /* char latency */ +uint32 ta_stopioe = 1; /* stop on error */ +uint8 *ta_xb = NULL; /* transfer buffer */ +static uint8 ta_fnc_tab[TACS_M_FNC + 1] = { + OP_WRI|OP_FWD, OP_WRI|OP_FWD, OP_FWD, OP_REV, + OP_REV , OP_FWD, OP_FWD, 0 + }; + +DEVICE ta_dev; +t_stat ta_rd (int32 *data, int32 PA, int32 access); +t_stat ta_wr (int32 data, int32 PA, int32 access); +t_stat ta_svc (UNIT *uptr); +t_stat ta_reset (DEVICE *dptr); +t_stat ta_attach (UNIT *uptr, char *cptr); +t_stat ta_detach (UNIT *uptr); +void ta_go (void); +t_stat ta_map_err (UNIT *uptr, t_stat st); +UNIT *ta_busy (void); +void ta_set_tr (void); +uint32 ta_updsta (UNIT *uptr); +uint32 ta_crc (uint8 *buf, uint32 cnt); + +/* TA data structures + + ta_dev TA device descriptor + ta_unit TA unit list + ta_reg TA register list + ta_mod TA modifier list +*/ + +DIB ta_dib = { + IOBA_TA, IOLN_TA, &ta_rd, &ta_wr, + 1, IVCL (TA), VEC_TA, { NULL } + }; + +UNIT ta_unit[] = { + { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) }, + { UDATA (&ta_svc, UNIT_ATTABLE+UNIT_ROABLE, TA_SIZE) }, + }; + +REG ta_reg[] = { + { ORDATA (TACS, ta_cs, 16) }, + { ORDATA (TAIDB, ta_idb, 8) }, + { ORDATA (TAODB, ta_odb, 8) }, + { FLDATA (WRITE, ta_write, 0) }, + { FLDATA (INT, IREQ (TA), INT_V_TA) }, + { FLDATA (ERR, ta_cs, CSR_V_ERR) }, + { FLDATA (TR, ta_cs, CSR_V_DONE) }, + { FLDATA (IE, ta_cs, CSR_V_IE) }, + { DRDATA (BPTR, ta_bptr, 17) }, + { DRDATA (BLNT, ta_blnt, 17) }, + { DRDATA (STIME, ta_stime, 24), PV_LEFT + REG_NZ }, + { DRDATA (CTIME, ta_ctime, 24), PV_LEFT + REG_NZ }, + { FLDATA (STOP_IOE, ta_stopioe, 0) }, + { URDATA (UFNC, ta_unit[0].FNC, 8, 5, 0, TA_NUMDR, 0), REG_HRO }, + { URDATA (UST, ta_unit[0].UST, 8, 2, 0, TA_NUMDR, 0), REG_HRO }, + { URDATA (POS, ta_unit[0].pos, 10, T_ADDR_W, 0, + TA_NUMDR, PV_LEFT | REG_RO) }, + { ORDATA (DEVADDR, ta_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, ta_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB ta_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, +// { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", +// &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL, + NULL, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, IOLN_TA, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE ta_dev = { + "TA", ta_unit, ta_reg, ta_mod, + TA_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &ta_reset, + NULL, &ta_attach, &ta_detach, + &ta_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG + }; + +/* I/O dispatch routines, I/O addresses 17777500 - 17777503 + + 17777500 TACS read/write + 17777502 TADB read/write +*/ + +t_stat ta_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* TACSR */ + *data = ta_updsta (NULL); /* update status */ + break; + + case 1: /* TADB */ + *data = ta_idb; /* return byte */ + ta_cs &= ~TACS_TR; /* clear tra req */ + ta_updsta (NULL); + break; + } + +return SCPE_OK; +} + +t_stat ta_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* TACS */ + if (access == WRITEB) data = (PA & 1)? /* byte write? */ + (ta_cs & 0377) | (data << 8): /* merge old */ + (ta_cs & ~0377) | data; + ta_cs = (ta_cs & ~TACS_W) | (data & TACS_W); /* merge new */ + if ((data & CSR_GO) && !ta_busy ()) /* go, not busy? */ + ta_go (); /* start operation */ + if (ta_cs & TACS_ILBS) ta_cs &= ~TACS_TR; /* ILBS inhibits TR */ + break; + + case 1: /* TADB */ + if (PA & 1) break; /* ignore odd byte */ + ta_odb = data; /* return byte */ + ta_cs &= ~TACS_TR; /* clear tra req */ + break; + } /* end switch */ + +ta_updsta (NULL); /* update status */ +return SCPE_OK; +} + +/* Start a new operation - cassette is not busy */ + +void ta_go (void) +{ +UNIT *uptr = ta_dev.units + GET_UNIT (ta_cs); +uint32 fnc = GET_FNC (ta_cs); +uint32 flg = ta_fnc_tab[fnc]; +uint32 old_ust = uptr->UST; + +if (DEBUG_PRS (ta_dev)) fprintf (sim_deb, + ">>TA start: op=%o, old_sta = %o, pos=%d\n", + fnc, uptr->UST, uptr->pos); +ta_cs &= ~(TACS_XFRERR|TACS_EMP|TACS_TR|TACS_RDY); /* clr err, tr, rdy */ +ta_bptr = 0; /* init buffer */ +ta_blnt = 0; +if ((uptr->flags & UNIT_ATT) == 0) { + ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY; + return; + } +if (flg & OP_WRI) { /* write op? */ + if (sim_tape_wrp (uptr)) { /* locked? */ + ta_cs |= TACS_ERR|TACS_WLK|TACS_RDY; /* don't start */ + return; + } + ta_odb = 0; + ta_write = 1; + } +else { + ta_idb = 0; + ta_write = 0; + } +ta_cs &= ~TACS_BEOT; /* tape in motion */ +uptr->FNC = fnc; /* save function */ +if ((fnc != TACS_REW) && !(flg & OP_WRI)) { /* spc/read cmd? */ + t_mtrlnt t; + t_stat st; + uptr->UST = flg & UST_REV; /* save direction */ + if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */ + st = sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); /* skip file gap */ + if (st != MTSE_TMK) /* not there? */ + sim_tape_rewind (uptr); /* restore tap pos */ + else old_ust = 0; /* defang next */ + } + if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* reverse in gap? */ + if (uptr->UST) /* skip file gap */ + sim_tape_rdrecr (uptr, ta_xb, &t, TA_MAXFR); + else sim_tape_rdrecf (uptr, ta_xb, &t, TA_MAXFR); + if (DEBUG_PRS (ta_dev)) fprintf (sim_deb, + ">>TA skip gap: op=%o, old_sta = %o, pos=%d\n", + fnc, uptr->UST, uptr->pos); + } + } +else uptr->UST = 0; +sim_activate (uptr, ta_stime); /* schedule op */ +return; +} + +/* Unit service */ + +t_stat ta_svc (UNIT *uptr) +{ +uint32 i, crc; +uint32 flg = ta_fnc_tab[uptr->FNC & TACS_M_FNC]; +t_mtrlnt tbc; +t_stat st, r; + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + ta_cs |= TACS_ERR|TACS_EMP|TACS_RDY; + ta_updsta (uptr); /* update status */ + return (ta_stopioe? SCPE_UNATT: SCPE_OK); + } +if (((flg & OP_FWD) && sim_tape_eot (uptr)) || /* illegal motion? */ + ((flg & OP_REV) && sim_tape_bot (uptr))) { + ta_cs |= TACS_ERR|TACS_BEOT|TACS_RDY; /* error */ + ta_updsta (uptr); + return SCPE_OK; + } + +r = SCPE_OK; +switch (uptr->FNC) { /* case on function */ + + case TACS_READ: /* read start */ + st = sim_tape_rdrecf (uptr, ta_xb, &ta_blnt, TA_MAXFR); /* get rec */ + if (st == MTSE_RECE) ta_cs |= TACS_ERR|TACS_CRC; /* rec in err? */ + else if (st != MTSE_OK) { /* other error? */ + r = ta_map_err (uptr, st); /* map error */ + break; + } + crc = ta_crc (ta_xb, ta_blnt); /* calculate CRC */ + ta_xb[ta_blnt++] = (crc >> 8) & 0377; /* append to buffer */ + ta_xb[ta_blnt++] = crc & 0377; + uptr->FNC |= TACS_2ND; /* next state */ + sim_activate (uptr, ta_ctime); /* sched next char */ + return SCPE_OK; + + case TACS_READ|TACS_2ND: /* read char */ + if (ta_bptr < ta_blnt) /* more chars? */ + ta_idb = ta_xb[ta_bptr++]; + else { /* no */ + ta_idb = 0; + ta_cs |= TACS_ERR|TACS_CRC; /* overrun */ + break; /* tape stops */ + } + if (ta_cs & TACS_ILBS) { /* CRC seq? */ + uptr->FNC |= TACS_3RD; /* next state */ + sim_activate (uptr, ta_stime); /* sched CRC chk */ + } + else { + ta_set_tr (); /* set tra req */ + sim_activate (uptr, ta_ctime); /* sched next char */ + } + return SCPE_OK; + + case TACS_READ|TACS_3RD: /* second read CRC */ + if (ta_bptr != ta_blnt) { /* partial read? */ + crc = ta_crc (ta_xb, ta_bptr + 2); /* actual CRC */ + if (crc != 0) ta_cs |= TACS_ERR|TACS_CRC; /* must be zero */ + } + break; /* read done */ + + case TACS_WRITE: /* write start */ + for (i = 0; i < TA_MAXFR; i++) ta_xb[i] = 0; /* clear buffer */ + ta_set_tr (); /* set tra req */ + uptr->FNC |= TACS_2ND; /* next state */ + sim_activate (uptr, ta_ctime); /* sched next char */ + return SCPE_OK; + + case TACS_WRITE|TACS_2ND: /* write char */ + if (ta_cs & TACS_ILBS) { /* CRC seq? */ + uptr->FNC |= TACS_3RD; /* next state */ + sim_activate (uptr, ta_stime); /* sched wri done */ + } + else { + if ((ta_bptr < TA_MAXFR) && /* room in buf? */ + ((uptr->pos + ta_bptr) < uptr->capac)) /* room on tape? */ + ta_xb[ta_bptr++] = ta_odb; /* store char */ + ta_set_tr (); /* set tra req */ + sim_activate (uptr, ta_ctime); /* sched next char */ + } + return SCPE_OK; + + case TACS_WRITE|TACS_3RD: /* write CRC */ + if (ta_bptr) { /* anything to write? */ + if (st = sim_tape_wrrecf (uptr, ta_xb, ta_bptr)) /* write, err? */ + r = ta_map_err (uptr, st); /* map error */ + } + break; /* op done */ + + case TACS_WFG: /* write file gap */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = ta_map_err (uptr, st); /* map error */ + break; + + case TACS_REW: /* rewind */ + sim_tape_rewind (uptr); + ta_cs |= TACS_BEOT; /* bot, no error */ + break; + + case TACS_SRB: /* space rev blk */ + if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */ + r = ta_map_err (uptr, st); /* map error */ + break; + + case TACS_SRF: /* space rev file */ + while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; + if (st == MTSE_TMK) /* if tape mark, */ + ta_cs |= TACS_EOF; /* set EOF, no err */ + else r = ta_map_err (uptr, st); /* else map error */ + break; + + case TACS_SFB: /* space fwd blk */ + if (st = sim_tape_sprecf (uptr, &tbc)) /* space rev, err? */ + r = ta_map_err (uptr, st); /* map error */ + ta_cs |= TACS_CRC; /* CRC sets, no err */ + break; + + case TACS_SFF: /* space fwd file */ + while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; + if (st == MTSE_TMK) /* if tape mark, */ + ta_cs |= TACS_EOF; /* set EOF, no err */ + else r = ta_map_err (uptr, st); /* else map error */ + break; + + default: /* never get here! */ + return SCPE_IERR; + } /* end case */ + +ta_cs |= TACS_RDY; /* set ready */ +ta_updsta (uptr); /* update status */ +if (DEBUG_PRS (ta_dev)) fprintf (sim_deb, + ">>TA done: op=%o, status = %o, pos=%d\n", + uptr->FNC, ta_cs, uptr->pos); +return r; +} + +/* Update controller status */ + +uint32 ta_updsta (UNIT *uptr) +{ +if (uptr == NULL) { /* unit specified? */ + if ((uptr = ta_busy ()) == NULL) /* use busy */ + uptr = ta_dev.units + GET_UNIT (ta_cs); /* use sel unit */ + } +else if (ta_cs & TACS_EOF) uptr->UST |= UST_GAP; /* save EOF */ +if (uptr->flags & UNIT_ATT) ta_cs &= ~TACS_EMP; /* attached? */ +else ta_cs |= TACS_EMP|TACS_RDY; /* no, empty, ready */ +if ((ta_cs & TACS_IE) && /* int enabled? */ + (ta_cs & (TACS_TR|TACS_RDY))) /* req or ready? */ + SET_INT (TA); /* set int req */ +else CLR_INT (TA); /* no, clr int req */ +return ta_cs; +} + +/* Set transfer request */ + +void ta_set_tr (void) +{ +if (ta_cs & TACS_TR) ta_cs |= (TACS_ERR|TACS_TIM); /* flag still set? */ +else ta_cs |= TACS_TR; /* set xfr req */ +if (ta_cs & TACS_IE) SET_INT (TA); /* if ie, int req */ +return; +} + +/* Test if controller busy */ + +UNIT *ta_busy (void) +{ +uint32 u; +UNIT *uptr; + +for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */ + uptr = ta_dev.units + u; + if (sim_is_active (uptr)) return uptr; + } +return NULL; +} + +/* Calculate CRC on buffer */ + +uint32 ta_crc (uint8 *buf, uint32 cnt) +{ +uint32 crc, i, j; + +crc = 0; +for (i = 0; i < cnt; i++) { + crc = crc ^ (((uint32) buf[i]) << 8); + for (j = 0; j < 8; j++) { + if (crc & 1) crc = (crc >> 1) ^ 0xA001; + else crc = crc >> 1; + } + } +return crc; +} + +/* Map error status */ + +t_stat ta_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* unattached */ + ta_cs |= TACS_ERR|TACS_CRC; + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_TMK: /* end of file */ + ta_cs |= TACS_ERR|TACS_EOF; + break; + + case MTSE_IOERR: /* IO error */ + ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ + if (ta_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + ta_cs |= TACS_ERR|TACS_CRC; /* set crc err */ + break; + + case MTSE_BOT: /* reverse into BOT */ + ta_cs |= TACS_ERR|TACS_BEOT; /* set bot */ + break; + + case MTSE_WRP: /* write protect */ + ta_cs |= TACS_ERR|TACS_WLK; /* set wlk err */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ta_reset (DEVICE *dptr) +{ +uint32 u; +UNIT *uptr; + +ta_cs = 0; +ta_idb = 0; +ta_odb = 0; +ta_write = 0; +ta_bptr = 0; +ta_blnt = 0; +CLR_INT (TA); /* clear interrupt */ +for (u = 0; u < TA_NUMDR; u++) { /* loop thru units */ + uptr = ta_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + sim_tape_reset (uptr); /* reset tape */ + } +if (ta_xb == NULL) ta_xb = (uint8 *) calloc (TA_MAXFR + 2, sizeof (uint8)); +if (ta_xb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat ta_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +ta_updsta (NULL); +uptr->UST = 0; +return r; +} + +/* Detach routine */ + +t_stat ta_detach (UNIT* uptr) +{ +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* check attached */ +r = sim_tape_detach (uptr); +ta_updsta (NULL); +uptr->UST = 0; +return r; +} diff --git a/PDP11/pdp11_tc.c b/PDP11/pdp11_tc.c new file mode 100644 index 0000000..d1ea2d6 --- /dev/null +++ b/PDP11/pdp11_tc.c @@ -0,0 +1,1300 @@ +/* pdp11_tc.c: PDP-11 DECtape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tc TC11/TU56 DECtape + + 23-Jun-06 RMS Fixed switch conflict in ATTACH + 10-Feb-06 RMS READ sets extended data bits in TCST (found by Alan Frisbie) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 30-Sep-04 RMS Revised Unibus interface + 25-Jan-04 RMS Revised for device debug support + 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR + 29-Dec-03 RMS Changed initial status to disabled (in Qbus system) + 18-Oct-03 RMS Fixed reverse checksum in read all + Added DECtape off reel message + Simplified timing + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 29-Sep-02 RMS Added variable address support to bootstrap + Added vector change/display support + Added 16b format support + New data structures + 30-May-02 RMS Widened POS to 32b + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted POS, STATT, LASTT to arrays + 09-Nov-01 RMS Added bus map support + 15-Sep-01 RMS Integrated debug logging + 27-Sep-01 RMS Fixed interrupt after stop for RSTS/E + 07-Sep-01 RMS Revised device disable and interrupt mechanisms + 29-Aug-01 RMS Added casts to PDP-8 unpack routine + 17-Jul-01 RMS Moved function prototype + 11-May-01 RMS Fixed bug in reset + 26-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot + 16-Mar-01 RMS Fixed bug in interrupt after stop + 15-Mar-01 RMS Added 129th word to PDP-8 format + + PDP-11 DECtapes are represented in memory by fixed length buffer of 32b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the TD8E formatter) is: + + reverse end zone 8192 reverse end zone codes ~ 10 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 8192 forward end zone codes ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the interblock words in the + bit bucket. +*/ + +#include "pdp11_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define DT_M_NUMDR (DT_NUMDR - 1) +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_BLKWD 1 /* blk no word in h/t */ +#define DT_CSMWD 4 /* checksum word in h/t */ +#define DT_HTWRD 5 /* header/trailer words */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ +#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ +#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ +#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 256 /* block size in 18b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ +#define D16_FILSIZ (D18_TSIZE * D18_BSIZE * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 86 /* block size in 18b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) +#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D18_CAPAC /* default */ +#define DT_WSIZE D18_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* TCST - 177340 - status register */ + +#define STA_END 0100000 /* end zone */ +#define STA_PAR 0040000 /* parity err */ +#define STA_MRK 0020000 /* mark trk err */ +#define STA_ILO 0010000 /* illegal op */ +#define STA_SEL 0004000 /* select err */ +#define STA_BLKM 0002000 /* block miss err */ +#define STA_DATM 0001000 /* data miss err */ +#define STA_NXM 0000400 /* nx mem err */ +#define STA_UPS 0000200 /* up to speed */ +#define STA_V_XD 0 /* extended data */ +#define STA_M_XD 03 +#define STA_ALLERR (STA_END | STA_PAR | STA_MRK | STA_ILO | \ + STA_SEL | STA_BLKM | STA_DATM | STA_NXM ) +#define STA_RWERR (STA_END | STA_PAR | STA_MRK | \ + STA_BLKM | STA_DATM | STA_NXM ) +#define STA_RW 0000003 +#define STA_GETXD(x) (((x) >> STA_V_XD) & STA_M_XD) + +/* TCCM - 177342 - command register */ + +/* #define CSR_ERR 0100000 */ +#define CSR_MNT 0020000 /* maint (unimpl) */ +#define CSR_INH 0010000 /* delay inhibit */ +#define CSR_DIR 0004000 /* reverse */ +#define CSR_V_UNIT 8 /* unit select */ +#define CSR_M_UNIT 07 +#define CSR_UNIT (CSR_M_UNIT << CSR_V_UNIT) +/* #define CSR_DONE 0000200 */ +/* #define CSR_IE 0000100 */ +#define CSR_V_MEX 4 /* mem extension */ +#define CSR_M_MEX 03 +#define CSR_MEX (CSR_M_MEX << CSR_V_MEX) +#define CSR_V_FNC 1 /* function */ +#define CSR_M_FNC 07 +#define FNC_STOP 00 /* stop all */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_RALL 03 /* read all */ +#define FNC_SSEL 04 /* stop selected */ +#define FNC_WMRK 05 /* write */ +#define FNC_WRIT 06 /* write all */ +#define FNC_WALL 07 /* write timing */ +/* define CSR_GO 0000001 */ +#define CSR_RW 0117576 /* read/write */ + +#define CSR_GETUNIT(x) (((x) >> CSR_V_UNIT) & CSR_M_UNIT) +#define CSR_GETMEX(x) (((x) >> CSR_V_MEX) & CSR_M_MEX) +#define CSR_GETFNC(x) (((x) >> CSR_V_FNC) & CSR_M_FNC) +#define CSR_INCMEX(x) (((x) & ~CSR_MEX) | (((x) + (1 << CSR_V_MEX)) & CSR_MEX)) + +/* TCWC - 177344 - word count */ + +/* TCBA - 177346 - bus address */ + +/* TCDT - 177350 - data */ + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR FNC_WMRK /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Logging */ + +#define LOG_MS 0x1 +#define LOG_RW 0x2 +#define LOG_BL 0x4 + +#define DT_SETDONE tccm = tccm | CSR_DONE; \ + if (tccm & CSR_IE) SET_INT (DTA) +#define DT_CLRDONE tccm = tccm & ~CSR_DONE; \ + CLR_INT (DTA) +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern uint16 *M; /* memory */ +extern int32 int_req[IPL_HLVL]; +extern UNIT cpu_unit; +extern int32 sim_switches; +extern FILE *sim_deb; + +int32 tcst = 0; /* status */ +int32 tccm = 0; /* command */ +int32 tcwc = 0; /* word count */ +int32 tcba = 0; /* bus address */ +int32 tcdt = 0; /* data */ +int32 dt_ctime = 100; /* fast cmd time */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_dctime = 40000; /* decel time */ +int32 dt_substate = 0; +int32 dt_logblk = 0; +int32 dt_stopoffr = 0; + +DEVICE dt_dev; +t_stat dt_rd (int32 *data, int32 PA, int32 access); +t_stat dt_wr (int32 data, int32 PA, int32 access); +t_stat dt_svc (UNIT *uptr); +t_stat dt_svcdone (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, char *cptr); +t_stat dt_detach (UNIT *uptr); +t_stat dt_boot (int32 unitno, DEVICE *dptr); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +void dt_stopunit (UNIT *uptr); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); +extern int32 sim_is_running; + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +DIB dt_dib = { + IOBA_TC, IOLN_TC, &dt_rd, &dt_wr, + 1, IVCL (DTA), VEC_DTA, { NULL } + }; + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE+UNIT_11FMT, DT_CAPAC) }, + { UDATA (&dt_svcdone, UNIT_DIS, 0) } + }; + +#define DT_TIMER (DT_NUMDR) + +REG dt_reg[] = { + { ORDATA (TCST, tcst, 16) }, + { ORDATA (TCCM, tccm, 16) }, + { ORDATA (TCWC, tcwc, 16) }, + { ORDATA (TCBA, tcba, 16) }, + { ORDATA (TCDT, tcdt, 16) }, + { FLDATA (INT, IREQ (DTA), INT_V_DTA) }, + { FLDATA (ERR, tccm, CSR_V_ERR) }, + { FLDATA (DONE, tccm, CSR_V_DONE) }, + { FLDATA (IE, tccm, CSR_V_DONE) }, + { DRDATA (CTIME, dt_ctime, 31), REG_NZ }, + { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, + { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, + { ORDATA (SUBSTATE, dt_substate, 1) }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO) }, + { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, + DT_NUMDR, REG_RO) }, + { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, + DT_NUMDR, REG_HRO) }, + { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, + { ORDATA (DEVADDR, dt_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, dt_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEBTAB dt_deb[] = { + { "MOTION", LOG_MS }, + { "DATA", LOG_RW }, + { "BLOCK", LOG_BL }, + { NULL, 0 } + }; + +DEVICE dt_dev = { + "TC", dt_unit, dt_reg, dt_mod, + DT_NUMDR + 1, 8, 24, 1, 8, 18, + NULL, NULL, &dt_reset, + &dt_boot, &dt_attach, &dt_detach, + &dt_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, + dt_deb, NULL, NULL + }; + +/* IO dispatch routines, I/O addresses 17777340 - 17777350 */ + +t_stat dt_rd (int32 *data, int32 PA, int32 access) +{ +int32 j, unum, mot, fnc; + +j = (PA >> 1) & 017; /* get reg offset */ +unum = CSR_GETUNIT (tccm); /* get drive */ +switch (j) { + + case 000: /* TCST */ + mot = DTS_GETMOT (dt_unit[unum].STATE); /* get motion */ + if (mot >= DTS_ATSF) tcst = tcst | STA_UPS; /* set/clr speed */ + else tcst = tcst & ~STA_UPS; + *data = tcst; + break; + + case 001: /* TCCM */ + if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR; /* set/clr error */ + else tccm = tccm & ~CSR_ERR; + *data = tccm; + break; + + case 002: /* TCWC */ + *data = tcwc; + break; + + case 003: /* TCBA */ + *data = tcba; + break; + + case 004: /* TCDT */ + fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */ + if (fnc == FNC_RALL) { /* read all? */ + DT_CLRDONE; /* clear done */ + } + *data = tcdt; + break; + } + +return SCPE_OK; +} + +t_stat dt_wr (int32 data, int32 PA, int32 access) +{ +int32 i, j, unum, old_tccm, fnc; +UNIT *uptr; + +j = (PA >> 1) & 017; /* get reg offset */ +switch (j) { + + case 000: /* TCST */ + if ((access == WRITEB) && (PA & 1)) break; + tcst = (tcst & ~STA_RW) | (data & STA_RW); + break; + + case 001: /* TCCM */ + old_tccm = tccm; /* save prior */ + if (access == WRITEB) data = (PA & 1)? + (tccm & 0377) | (data << 8): (tccm & ~0377) | data; + if ((data & CSR_IE) == 0) CLR_INT (DTA); + else if ((((tccm & CSR_IE) == 0) && (tccm & CSR_DONE)) || + (data & CSR_DONE)) SET_INT (DTA); + tccm = (tccm & ~CSR_RW) | (data & CSR_RW); + if ((data & CSR_GO) && (tccm & CSR_DONE)) { /* new cmd? */ + tcst = tcst & ~STA_ALLERR; /* clear errors */ + tccm = tccm & ~(CSR_ERR | CSR_DONE); /* clear done, err */ + CLR_INT (DTA); /* clear int */ + if ((old_tccm ^ tccm) & CSR_UNIT) dt_deselect (old_tccm); + unum = CSR_GETUNIT (tccm); /* get drive */ + fnc = CSR_GETFNC (tccm); /* get function */ + if (fnc == FNC_STOP) { /* stop all? */ + sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); + for (i = 0; i < DT_NUMDR; i++) + dt_stopunit (dt_dev.units + i); /* stop unit */ + break; + } + uptr = dt_dev.units + unum; + if (uptr->flags & UNIT_DIS) /* disabled? */ + dt_seterr (uptr, STA_SEL); /* select err */ + if ((fnc == FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) || + ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT))) + dt_seterr (uptr, STA_ILO); /* illegal op */ + if (!(tccm & CSR_ERR)) dt_newsa (tccm); + } + else if ((tccm & CSR_ERR) == 0) { /* clear err? */ + tcst = tcst & ~STA_RWERR; + if (tcst & STA_ALLERR) tccm = tccm | CSR_ERR; + } + break; + + case 002: /* TCWC */ + tcwc = data; /* word write only! */ + break; + + case 003: /* TCBA */ + tcba = data; /* word write only! */ + break; + + case 004: /* TCDT */ + unum = CSR_GETUNIT (tccm); /* get drive */ + fnc = DTS_GETFNC (dt_unit[unum].STATE); /* get function */ + if (fnc == FNC_WALL) { /* write all? */ + DT_CLRDONE; /* clear done */ + } + tcdt = data; /* word write only! */ + break; + } + +return SCPE_OK; +} + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit = CSR_GETUNIT (oldf); +UNIT *uptr = dt_dev.units + old_unit; +int32 old_mot = DTS_GETMOT (uptr->STATE); + +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; +} + +/* New operation + + 1. If function = stop + - if not already stopped or decelerating, schedule deceleration + - schedule command completion + 2. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 3. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 4. If not yet at speed, + - set function as next state + 5. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, new_fnc; +int32 prev_dir, new_dir; +UNIT *uptr; + +new_unit = CSR_GETUNIT (newf); /* new, old units */ +uptr = dt_dev.units + new_unit; +if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, STA_SEL); /* no, error */ + return; + } +prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ +prev_dir = prev_mot & DTS_DIR; /* previous dir */ +new_fnc = CSR_GETFNC (newf); /* new function */ +new_dir = (newf & CSR_DIR) != 0; /* new di? */ + +if (new_fnc == FNC_SSEL) { /* stop unit? */ + sim_activate (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */ + dt_stopunit (uptr); /* stop unit */ + return; + } + +if (prev_mot == DTS_STOP) { /* start? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_dir ^ new_dir) { /* dir chg? */ + dt_stopunit (uptr); /* stop unit */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; + } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, relpos, newpos; +uint32 oldpos; + +oldpos = uptr->pos; /* save old pos */ +if (dt_setpos (uptr)) return; /* update pos */ +uptr->STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr->STATE); /* set variables */ +dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; +unum = (int32) (uptr - dt_dev.units); +if (oldpos == uptr->pos) + uptr->pos = uptr->pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr->pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, STA_END); /* set ez flag, stop */ + return; + } +dt_substate = 0; /* substate = normal */ +sim_cancel (uptr); /* cancel cur op */ +switch (fnc) { /* case function */ + + case DTS_OFR: /* off reel */ + if (dir) newpos = -1000; /* rev? < start */ + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; + + case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n", + unum, (dir? "backward": "forward")); + break; + + case FNC_WRIT: /* write */ + case FNC_READ: /* read */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); + break; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dt_seterr (uptr, STA_BLKM); + return; + } + if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? + blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? + blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: %s block %d %s\n", + unum, ((fnc == FNC_READ)? "read": "write"), + blk, (dir? "backward": "forward")); + break; + + case FNC_RALL: /* read all */ + case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; + else newpos = DT_EZLIN + (DT_WSIZE - 1); + } + else { + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if (dir? (relpos < (DTU_LPERB (uptr) - DT_CSMLN)): /* switch in time? */ + (relpos >= DT_CSMLN)) { + dt_seterr (uptr, STA_BLKM); + return; + } + if (dir) newpos = DT_BLK2LN (blk + 1, uptr) - DT_CSMLN - DT_WSIZE; + else newpos = DT_BLK2LN (blk, uptr) + DT_CSMLN + (DT_WSIZE - 1); + } + if (fnc == FNC_WALL) sim_activate /* write all? */ + (&dt_dev.units[DT_TIMER], dt_ctime); /* sched done */ + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: read all block %d %s\n", + unum, blk, (dir? "backward": "forward")); + break; + + default: + dt_seterr (uptr, STA_SEL); /* bad state */ + return; + } + +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr->STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr->LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr->LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ + + case DTS_STOP: /* stop */ + delta = 0; + break; + + case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; + udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; + + case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; + udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; + + case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; + } + +if (mot & DTS_DIR) uptr->pos = uptr->pos - delta; /* update pos */ +else uptr->pos = uptr->pos + delta; +if (((int32) uptr->pos < 0) || + ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { + detach_unit (uptr); /* off reel? */ + uptr->STATE = uptr->pos = 0; + unum = (int32) (uptr - dt_dev.units); + if ((unum == CSR_GETUNIT (tccm)) && (CSR_GETFNC (tccm) != FNC_STOP)) + dt_seterr (uptr, STA_SEL); /* error */ + return TRUE; + } +return FALSE; +} + +/* Command timer service after stop - set done */ + +t_stat dt_svcdone (UNIT *uptr) +{ +DT_SETDONE; +return SCPE_OK; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr->STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr->STATE); +int32 *fbuf = (int32 *) uptr->filebuf; +int32 blk, wrd, relpos, dat; +uint32 ba, ma; +uint16 wbuf; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { + + case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); + uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ + if (uptr->STATE) /* not stopped? */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */ + return SCPE_OK; + + case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ + return SCPE_OK; + + case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ + + default: /* other */ + dt_seterr (uptr, STA_SEL); /* state error */ + return SCPE_OK; + } + +/* Functional cases + + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, STA_END); /* end zone error */ + return SCPE_OK; + } +blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ + +switch (fnc) { /* at speed, check fnc */ + + case FNC_SRCH: /* search */ + tcdt = blk; /* set block # */ + dt_schedez (uptr, dir); /* sched end zone */ + DT_SETDONE; /* set done */ + break; + + case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr->STATE = uptr->pos = 0; /* no visible action */ + break; + +/* Read + + If wc ovf has not occurred, inc ma, wc and copy word from tape to memory + If wc ovf, set flag + If not end of block, schedule next word + If end of block and not wc ovf, schedule next block + If end of block and wc ovf, set done, schedule end zone +*/ + + case FNC_READ: /* read */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + if (!dt_substate) { /* !wc ovf? */ + ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + tcdt = wbuf = fbuf[ba] & DMASK; /* read word */ + tcst = (tcst & ~STA_M_XD) | ((fbuf[ma] >> 16) & STA_M_XD); + if (Map_WriteW (ma, 2, &wbuf)) { /* store, nxm? */ + dt_seterr (uptr, STA_NXM); + break; + } + tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */ + tcba = (tcba + 2) & DMASK; + if (tcba <= 1) tccm = CSR_INCMEX (tccm); + if (tcwc == 0) dt_substate = 1; + } + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else if (dt_substate) { /* wc ovf? */ + dt_schedez (uptr, dir); /* sched end zone */ + DT_SETDONE; /* set done */ + } + else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + break; + +/* Write + + If wc ovf has not occurred, inc ma, wc + Copy word from memory (or 0, to fill block) to tape + If wc ovf, set flag + If not end of block, schedule next word + If end of block and not wc ovf, schedule next block + If end of block and wc ovf, set done, schedule end zone +*/ + + case FNC_WRIT: /* write */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + if (dt_substate) tcdt = 0; /* wc ovf? fill */ + else { + ma = (CSR_GETMEX (tccm) << 16) | tcba; /* form 18b addr */ + if (Map_ReadW (ma, 2, &wbuf)) { /* fetch word */ + dt_seterr (uptr, STA_NXM); + break; + } + tcdt = wbuf; /* get word */ + tcwc = (tcwc + 1) & DMASK; /* incr MA, WC */ + tcba = (tcba + 2) & DMASK; + if (tcba <= 1) tccm = CSR_INCMEX (tccm); + } + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + fbuf[ba] = tcdt; /* write word */ + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + if (tcwc == 0) dt_substate = 1; + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not end blk? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else if (dt_substate) { /* wc ovf? */ + dt_schedez (uptr, dir); /* sched end zone */ + DT_SETDONE; + } + else sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + break; + +/* Read all - read current header or data word */ + + case FNC_RALL: + if (tccm & CSR_DONE) { /* done set? */ + dt_seterr (uptr, STA_DATM); /* data miss */ + break; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = fbuf[ba]; /* get tape word */ + } + else dat = dt_gethdr (uptr, blk, relpos); /* get hdr */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + tcdt = dat & DMASK; /* low 16b */ + tcst = (tcst & ~STA_M_XD) | ((dat >> 16) & STA_M_XD); + sim_activate (uptr, DT_WSIZE * dt_ltime); + DT_SETDONE; /* set done */ + break; + +/* Write all - write current header or data word */ + + case FNC_WALL: + if (tccm & CSR_DONE) { /* done set? */ + dt_seterr (uptr, STA_DATM); /* data miss */ + break; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + dat = (STA_GETXD (tcst) << 16) | tcdt; /* get data word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + fbuf[ba] = dat; /* write word */ + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + } +/* else /* ignore hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + DT_SETDONE; /* set done */ + break; + + default: + dt_seterr (uptr, STA_SEL); /* impossible state */ + break; + } +return SCPE_OK; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr->STATE); + +tcst = tcst | e; /* set error flag */ +tccm = tccm | CSR_ERR; +if (!(tccm & CSR_DONE)) { /* not done? */ + DT_SETDONE; + } +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) return; /* update position */ + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ + } +return; +} + +/* Stop unit */ + +void dt_stopunit (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr->STATE); +int32 dir = mot & DTS_DIR; + +if (mot == DTS_STOP) return; /* already stopped? */ +if ((mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } +DTS_SETSTA (DTS_DECF | dir, 0); /* state = decel */ +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */ +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Complement obverse routine (18b) */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ 0777777; /* compl obverse */ +dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | + ((dat >> 3) & 0700) | ((dat & 0700) << 3) | + ((dat & 070) << 9) | ((dat & 07) << 15); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int32 *fbuf = (int32 *) uptr->filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +csum = 077; /* init csum */ +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = fbuf[ba + i] ^ 0777777; /* get ~word */ + csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd; + } +return (csum & 077); +} + +/* Get header word (18b) */ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) +{ +int32 wrd = relpos / DT_WSIZE; + +if (wrd == DT_BLKWD) return blk; /* fwd blknum */ +if (wrd == DT_CSMWD) return 077; /* rev csum */ +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ + return (dt_csum (uptr, blk) << 12); +if (wrd == (2 * DT_HTWRD + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ + return dt_comobv (blk); +return 0; /* all others */ +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* RESET? */ + prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } + } + else { + sim_cancel (uptr); /* sim reset */ + uptr->STATE = 0; + uptr->LASTT = sim_grtime (); + } + } +tcst = tcwc = tcba = tcdt = 0; /* clear reg */ +tccm = CSR_DONE; +CLR_INT (DTA); /* clear int req */ +return SCPE_OK; +} + +/* Device bootstrap */ + +#define BOOT_START 02000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 020) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 0042124, /* "TD" */ + 0012706, BOOT_START, /* MOV #boot_start, SP */ + 0012700, 0000000, /* MOV #unit, R0 ; unit number */ + 0010003, /* MOV R0, R3 */ + 0000303, /* SWAB R3 */ + 0012701, 0177342, /* MOV #TCCM, R1 ; csr */ + 0012702, 0004003, /* RW: MOV #4003, R2 ; rev+rnum+go */ + 0050302, /* BIS R3, R2 */ + 0010211, /* MOV R2, (R1) ; load csr */ + 0032711, 0100200, /* BIT #100200, (R1) ; wait */ + 0001775, /* BEQ .-4 */ + 0100370, /* BPL RW ; no err, cont */ + 0005761, 0177776, /* TST -2(R1) ; end zone? */ + 0100036, /* BPL ER ; no, err */ + 0012702, 0000003, /* MOV #3, R2 ; rnum+go */ + 0050302, /* BIS R3, R2 */ + 0010211, /* MOV R2, (R1) ; load csr */ + 0032711, 0100200, /* BIT #100200, (R1) ; wait */ + 0001775, /* BEQ .-4 */ + 0100426, /* BMI ER ; err, die */ + 0005761, 0000006, /* TST 6(R1) ; blk 0? */ + 0001023, /* BNE ER ; no, die */ + 0012761, 0177000, 0000002, /* MOV #-256.*2, 2(R1) ; load wc */ + 0005061, 0000004, /* CLR 4(R1) ; clear ba */ + 0012702, 0000005, /* MOV #READ+GO, R2 ; read & go */ + 0050302, /* BIS R3, R2 */ + 0010211, /* MOV R2, (R1) ; load csr */ + 0005002, /* CLR R2 */ + 0005003, /* CLR R3 */ + 0012704, BOOT_START+020, /* MOV #START+20, R4 */ + 0005005, /* CLR R5 */ + 0032711, 0100200, /* BIT #100200, (R1) ; wait */ + 0001775, /* BEQ .-4 */ + 0100401, /* BMI ER ; err, die */ + 0005007, /* CLR PC */ + 0012711, 0000001, /* ER: MOV #1, (R1) ; stop all */ + 0000000 /* HALT */ + }; + +t_stat dt_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +dt_unit[unitno].pos = DT_EZLIN; +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & DT_M_NUMDR; +M[BOOT_CSR >> 1] = (dt_dib.ba & DMASK) + 02; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 12b, read 12b format and convert to 18b in buffer + If 16b, read 16b format and convert to 18b in buffer + If 18b/36b, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, char *cptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, sz, k, *fbuf; +int32 u = uptr - dt_dev.units; +t_stat r; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* fail? */ +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; /* default 16b */ + if (sim_switches & SWMASK ('T')) /* att 12b? */ + uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; + else if (sim_switches & SWMASK ('F')) /* att 18b? */ + uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + ((sz = sim_fsize (uptr->fileref)) > D16_FILSIZ)) { + if (sz <= D8_FILSIZ) + uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; + else uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +printf ("%s%d: ", sim_dname (&dt_dev), u); +if (uptr->flags & UNIT_8FMT) printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) printf ("16b format"); +else printf ("18b/36b format"); +printf (", buffering file in memory\n"); +if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp8b, sizeof (int16), D8_NBSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ + fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | + ((uint32) (pdp8b[k + 1] >> 6) & 077); + fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | + ((uint32) pdp8b[k + 2] & 07777); + ba = ba + 2; + } /* end blk loop */ + } /* end file loop */ + uptr->hwmark = ba; + } /* end if */ +else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0; + for (k = 0; k < D18_BSIZE; k++) + fbuf[ba++] = pdp11b[k]; + } + uptr->hwmark = ba; + } /* end elif */ +else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), + uptr->capac, uptr->fileref); +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +uptr->LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If 12b, convert 18b buffer to 12b and write to file + If 16b, convert 18b buffer to 16b and write to file + If 18b/36b, write buffer to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, k, *fbuf; +int32 u = uptr - dt_dev.units; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; +if (sim_is_active (uptr)) { /* active? cancel op */ + sim_cancel (uptr); + if ((u == CSR_GETUNIT (tccm)) && ((tccm & CSR_DONE) == 0)) { + tcst = tcst | STA_SEL; + tccm = tccm | CSR_ERR | CSR_DONE; + if (tccm & CSR_IE) SET_INT (DTA); + } + uptr->STATE = uptr->pos = 0; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); + rewind (uptr->fileref); /* start of file */ + if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ + pdp8b[k] = (fbuf[ba] >> 6) & 07777; + pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | + ((fbuf[ba + 1] >> 12) & 077); + pdp8b[k + 2] = fbuf[ba + 1] & 07777; + ba = ba + 2; + } /* end loop blk */ + fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (ferror (uptr->fileref)) break; + } /* end loop file */ + } /* end if 12b */ + else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D18_BSIZE; k++) /* loop blk */ + pdp11b[k] = fbuf[ba++] & DMASK; + fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (ferror (uptr->fileref)) break; + } /* end loop file */ + } /* end if 16b */ + else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ + uptr->hwmark, uptr->fileref); + if (ferror (uptr->fileref)) perror ("I/O error"); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} diff --git a/PDP11/pdp11_tm.c b/PDP11/pdp11_tm.c new file mode 100644 index 0000000..0be12fe --- /dev/null +++ b/PDP11/pdp11_tm.c @@ -0,0 +1,711 @@ +/* pdp11_tm.c: PDP-11 magnetic tape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tm TM11/TU10 magtape + + 16-Feb-06 RMS Added tape capacity checking + 31-Oct-05 RMS Fixed address width for large files + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 18-Mar-05 RMS Added attached test to detach routine + 07-Dec-04 RMS Added read-only file support + 30-Sep-04 RMS Revised Unibus interface + 25-Jan-04 RMS Revised for device debug support + 29-Dec-03 RMS Added 18b Qbus support + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised for magtape library, added logging + 30-Oct-02 RMS Revised BOT handling, added error record handling + 30-Sep-02 RMS Added variable address support to bootstrap + Added vector change/display support + Changed mapping mnemonics + New data structures + Updated error handling + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Fixed max record length, first block bootstrap + (found by Jonathan Engdahl) + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted UST, POS, FLG to arrays + 09-Nov-01 RMS Added bus map support + 18-Oct-01 RMS Added stub diagnostic register (found by Thord Nilson) + 07-Sep-01 RMS Revised device disable and interrupt mechanisms + 26-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot + 14-Apr-99 RMS Changed t_addr to unsigned + 04-Oct-98 RMS V2.4 magtape format + 10-May-98 RMS Fixed bug with non-zero unit operation (from Steven Schultz) + 09-May-98 RMS Fixed problems in bootstrap (from Steven Schultz) + 10-Apr-98 RMS Added 2nd block bootstrap (from John Holden, + University of Sydney) + 31-Jul-97 RMS Added bootstrap (from Ethan Dicks, Ohio State) + 22-Jan-97 RMS V2.3 magtape format + 18-Jan-97 RMS Fixed double interrupt, error flag bugs + 29-Jun-96 RMS Added unit disable support + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. +*/ + +#include "pdp11_defs.h" +#include "sim_tape.h" + +#define TM_NUMDR 8 /* #drives */ +#define USTAT u3 /* unit status */ + +/* Command - tm_cmd */ + +#define MTC_ERR (1 << CSR_V_ERR) /* error */ +#define MTC_V_DEN 13 /* density */ +#define MTC_M_DEN 03 +#define MTC_DEN (MTC_M_DEN << MTC_V_DEN) +#define MTC_INIT 0010000 /* init */ +#define MTC_LPAR 0004000 /* parity select */ +#define MTC_V_UNIT 8 /* unit */ +#define MTC_M_UNIT 07 +#define MTC_UNIT (MTC_M_UNIT << MTC_V_UNIT) +#define MTC_DONE (1 << CSR_V_DONE) /* done */ +#define MTC_IE (1 << CSR_V_IE) /* interrupt enable */ +#define MTC_V_EMA 4 /* ext mem address */ +#define MTC_M_EMA 03 +#define MTC_EMA (MTC_M_EMA << MTC_V_EMA) +#define MTC_V_FNC 1 /* function */ +#define MTC_M_FNC 07 +#define MTC_UNLOAD 00 +#define MTC_READ 01 +#define MTC_WRITE 02 +#define MTC_WREOF 03 +#define MTC_SPACEF 04 +#define MTC_SPACER 05 +#define MTC_WREXT 06 +#define MTC_REWIND 07 +#define MTC_FNC (MTC_M_FNC << MTC_V_FNC) +#define MTC_GO (1 << CSR_V_GO) /* go */ +#define MTC_RW (MTC_DEN | MTC_LPAR | MTC_UNIT | MTC_IE | \ + MTC_EMA | MTC_FNC) +#define GET_EMA(x) (((x) & MTC_EMA) << (16 - MTC_V_EMA)) +#define GET_UNIT(x) (((x) >> MTC_V_UNIT) & MTC_M_UNIT) +#define GET_FNC(x) (((x) >> MTC_V_FNC) & MTC_M_FNC) + +/* Status - stored in tm_sta or (*) uptr->USTAT or (+) calculated */ + +#define STA_ILL 0100000 /* illegal */ +#define STA_EOF 0040000 /* *end of file */ +#define STA_CRC 0020000 /* CRC error */ +#define STA_PAR 0010000 /* parity error */ +#define STA_DLT 0004000 /* data late */ +#define STA_EOT 0002000 /* +end of tape */ +#define STA_RLE 0001000 /* rec lnt error */ +#define STA_BAD 0000400 /* bad tape error */ +#define STA_NXM 0000200 /* non-existent mem */ +#define STA_ONL 0000100 /* *online */ +#define STA_BOT 0000040 /* *start of tape */ +#define STA_7TK 0000020 /* 7 track */ +#define STA_SDN 0000010 /* settle down */ +#define STA_WLK 0000004 /* *write locked */ +#define STA_REW 0000002 /* *rewinding */ +#define STA_TUR 0000001 /* +unit ready */ + +#define STA_CLR (STA_7TK | STA_SDN) /* always clear */ +#define STA_DYN (STA_EOF | STA_EOT | STA_ONL | STA_BOT | \ + STA_WLK | STA_REW | STA_TUR) /* dynamic */ +#define STA_EFLGS (STA_ILL | STA_EOF | STA_CRC | STA_PAR | \ + STA_DLT | STA_EOT | STA_RLE | STA_BAD | STA_NXM) + /* set error */ + +/* Read lines - tm_rdl */ + +#define RDL_CLK 0100000 /* 10 Khz clock */ + +extern uint16 *M; /* memory */ +extern int32 int_req[IPL_HLVL]; +extern FILE *sim_deb; + +uint8 *tmxb = NULL; /* xfer buffer */ +int32 tm_sta = 0; /* status register */ +int32 tm_cmd = 0; /* command register */ +int32 tm_ca = 0; /* current address */ +int32 tm_bc = 0; /* byte count */ +int32 tm_db = 0; /* data buffer */ +int32 tm_rdl = 0; /* read lines */ +int32 tm_time = 10; /* record latency */ +int32 tm_stopioe = 1; /* stop on error */ + +DEVICE tm_dev; +t_stat tm_rd (int32 *data, int32 PA, int32 access); +t_stat tm_wr (int32 data, int32 PA, int32 access); +t_stat tm_svc (UNIT *uptr); +t_stat tm_reset (DEVICE *dptr); +t_stat tm_attach (UNIT *uptr, char *cptr); +t_stat tm_detach (UNIT *uptr); +t_stat tm_boot (int32 unitno, DEVICE *dptr); +void tm_go (UNIT *uptr); +int32 tm_updcsta (UNIT *uptr); +void tm_set_done (void); +t_stat tm_map_err (UNIT *uptr, t_stat st); +t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* MT data structures + + tm_dev MT device descriptor + tm_unit MT unit list + tm_reg MT register list + tm_mod MT modifier list +*/ + +DIB tm_dib = { + IOBA_TM, IOLN_TM, &tm_rd, &tm_wr, + 1, IVCL (TM), VEC_TM, { NULL } + }; + +UNIT tm_unit[] = { + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) }, + { UDATA (&tm_svc, UNIT_ATTABLE + UNIT_ROABLE +UNIT_DISABLE, 0) } + }; + +REG tm_reg[] = { + { ORDATA (MTS, tm_sta, 16) }, + { ORDATA (MTC, tm_cmd, 16) }, + { ORDATA (MTBRC, tm_bc, 16) }, + { ORDATA (MTCMA, tm_ca, 16) }, + { ORDATA (MTD, tm_db, 8) }, + { ORDATA (MTRD, tm_rdl, 16) }, + { FLDATA (INT, IREQ (TM), INT_V_TM) }, + { FLDATA (ERR, tm_cmd, CSR_V_ERR) }, + { FLDATA (DONE, tm_cmd, CSR_V_DONE) }, + { FLDATA (IE, tm_cmd, CSR_V_IE) }, + { FLDATA (STOP_IOE, tm_stopioe, 0) }, + { DRDATA (TIME, tm_time, 24), PV_LEFT }, + { URDATA (UST, tm_unit[0].USTAT, 8, 16, 0, TM_NUMDR, 0) }, + { URDATA (POS, tm_unit[0].pos, 10, T_ADDR_W, 0, + TM_NUMDR, PV_LEFT | REG_RO) }, + { ORDATA (DEVADDR, tm_dib.ba, 32), REG_HRO }, + { ORDATA (DEVVEC, tm_dib.vec, 16), REG_HRO }, + { NULL } + }; + +MTAB tm_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &tm_vlock }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &tm_vlock }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE tm_dev = { + "TM", tm_unit, tm_reg, tm_mod, + TM_NUMDR, 10, T_ADDR_W, 1, 8, 8, + NULL, NULL, &tm_reset, + &tm_boot, &tm_attach, &tm_detach, + &tm_dib, DEV_DISABLE | DEV_UBUS | DEV_Q18 | DEV_DEBUG + }; + +/* I/O dispatch routines, I/O addresses 17772520 - 17772532 + + 17772520 MTS read only, constructed from tm_sta + plus current drive status flags + 17772522 MTC read/write + 17772524 MTBRC read/write + 17772526 MTCMA read/write + 17772530 MTD read/write + 17772532 MTRD read only +*/ + +t_stat tm_rd (int32 *data, int32 PA, int32 access) +{ +UNIT *uptr; + +uptr = tm_dev.units + GET_UNIT (tm_cmd); /* get unit */ +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* MTS */ + *data = tm_updcsta (uptr); /* update status */ + break; + + case 1: /* MTC */ + tm_updcsta (uptr); /* update status */ + *data = tm_cmd; /* return command */ + break; + + case 2: /* MTBRC */ + *data = tm_bc; /* return byte count */ + break; + + case 3: /* MTCMA */ + *data = tm_ca; /* return mem addr */ + break; + + case 4: /* MTD */ + *data = tm_db; /* return data buffer */ + break; + + case 5: /* MTRD */ + tm_rdl = tm_rdl ^ RDL_CLK; /* "clock" ticks */ + *data = tm_rdl; + break; + + default: /* unimplemented */ + *data = 0; + break; + } + +return SCPE_OK; +} + +t_stat tm_wr (int32 data, int32 PA, int32 access) +{ +UNIT *uptr; + +switch ((PA >> 1) & 07) { /* decode PA<3:1> */ + + case 0: /* MTS: read only */ + break; + + case 1: /* MTC */ + uptr = tm_dev.units + GET_UNIT (tm_cmd); /* select unit */ + if ((tm_cmd & MTC_DONE) == 0) tm_sta = tm_sta | STA_ILL; + else { + if (access == WRITEB) data = (PA & 1)? + (tm_cmd & 0377) | (data << 8): + (tm_cmd & ~0377) | data; + if (data & MTC_INIT) { /* init? */ + tm_reset (&tm_dev); /* reset device */ + return SCPE_OK; + } + if ((data & MTC_IE) == 0) /* int disable? */ + CLR_INT (TM); /* clr int request */ + else if ((tm_cmd & (MTC_ERR + MTC_DONE)) && !(tm_cmd & MTC_IE)) + SET_INT (TM); /* set int request */ + tm_cmd = (tm_cmd & ~MTC_RW) | (data & MTC_RW); + uptr = tm_dev.units + GET_UNIT (tm_cmd); /* new unit */ + if (data & MTC_GO) tm_go (uptr); /* new function? */ + } + tm_updcsta (uptr); /* update status */ + break; + + case 2: /* MTBRC */ + if (access == WRITEB) data = (PA & 1)? + (tm_bc & 0377) | (data << 8): (tm_bc & ~0377) | data; + tm_bc = data; + break; + + case 3: /* MTCMA */ + if (access == WRITEB) data = (PA & 1)? + (tm_ca & 0377) | (data << 8): (tm_ca & ~0377) | data; + tm_ca = data; + break; + + case 4: /* MTD */ + if ((access == WRITEB) && (PA & 1)) return SCPE_OK; + tm_db = data & 0377; + break; + } /* end switch */ + +return SCPE_OK; +} + +/* New magtape command */ + +void tm_go (UNIT *uptr) +{ +int32 f; + +f = GET_FNC (tm_cmd); /* get function */ +if (((uptr->flags & UNIT_ATT) == 0) || /* not attached? */ + sim_is_active (uptr) || /* busy? */ + (((f == MTC_WRITE) || (f == MTC_WREOF) || (f == MTC_WREXT)) && + sim_tape_wrp (uptr))) { /* write locked? */ + tm_sta = tm_sta | STA_ILL; /* illegal */ + tm_set_done (); /* set done */ + return; + } +uptr->USTAT = uptr->USTAT & (STA_WLK | STA_ONL); /* clear status */ +tm_sta = 0; /* clear errors */ +if (f == MTC_UNLOAD) { /* unload? */ + uptr->USTAT = (uptr->USTAT | STA_REW) & ~STA_ONL; + detach_unit (uptr); /* set offline */ + } +else if (f == MTC_REWIND) /* rewind */ + uptr->USTAT = uptr->USTAT | STA_REW; /* rewinding */ +/* else /* uncomment this else if rewind/unload don't set done */ +tm_cmd = tm_cmd & ~MTC_DONE; /* clear done */ +CLR_INT (TM); /* clear int */ +sim_activate (uptr, tm_time); /* start io */ +return; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt +*/ + +t_stat tm_svc (UNIT *uptr) +{ +int32 f, t, u; +uint32 xma; +t_mtrlnt tbc, cbc; +t_stat st, r = SCPE_OK; + +u = (int32) (uptr - tm_dev.units); /* get unit number */ +f = GET_FNC (tm_cmd); /* get command */ +xma = GET_EMA (tm_cmd) | tm_ca; /* get mem addr */ +cbc = 0200000 - tm_bc; /* get bc */ + +if (uptr->USTAT & STA_REW) { /* rewind? */ + sim_tape_rewind (uptr); /* update position */ + if (uptr->flags & UNIT_ATT) /* still on line? */ + uptr->USTAT = STA_ONL | STA_BOT | + (sim_tape_wrp (uptr)? STA_WLK: 0); + else uptr->USTAT = 0; + if (u == GET_UNIT (tm_cmd)) { /* selected? */ + tm_set_done (); /* set done */ + tm_updcsta (uptr); /* update status */ + } + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */ + uptr->USTAT = 0; /* unit off line */ + tm_sta = tm_sta | STA_ILL; /* illegal operation */ + tm_set_done (); /* set done */ + tm_updcsta (uptr); /* update status */ + return IORETURN (tm_stopioe, SCPE_UNATT); + } + +if (DEBUG_PRS (tm_dev)) fprintf (sim_deb, + ">>TM: op=%o, ma=%o, bc=%o, pos=%d\n", f, xma, tm_bc, uptr->pos); +switch (f) { /* case on function */ + + case MTC_READ: /* read */ + st = sim_tape_rdrecf (uptr, tmxb, &tbc, MT_MAXFR); /* read rec */ + if (st == MTSE_RECE) tm_sta = tm_sta | STA_PAR; /* rec in error? */ + else if (st != MTSE_OK) { /* other error? */ + r = tm_map_err (uptr, st); /* map error */ + break; + } + if (tbc > cbc) tm_sta = tm_sta | STA_RLE; /* wrong size? */ + if (tbc < cbc) cbc = tbc; /* use smaller */ + if (t = Map_WriteB (xma, cbc, tmxb)) { /* copy buf to mem */ + tm_sta = tm_sta | STA_NXM; /* NXM, set err */ + cbc = cbc - t; /* adj byte cnt */ + } + xma = (xma + cbc) & 0777777; /* inc bus addr */ + tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */ + break; + + case MTC_WRITE: /* write */ + case MTC_WREXT: /* write ext gap */ + if (t = Map_ReadB (xma, cbc, tmxb)) { /* copy mem to buf */ + tm_sta = tm_sta | STA_NXM; /* NXM, set err */ + cbc = cbc - t; /* adj byte cnt */ + if (cbc == 0) break; /* no xfr? done */ + } + if (st = sim_tape_wrrecf (uptr, tmxb, cbc)) /* write rec, err? */ + r = tm_map_err (uptr, st); /* map error */ + else { + xma = (xma + cbc) & 0777777; /* inc bus addr */ + tm_bc = (tm_bc + cbc) & 0177777; /* inc byte cnt */ + } + break; + + case MTC_WREOF: /* write eof */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = tm_map_err (uptr, st); /* map error */ + break; + + case MTC_SPACEF: /* space forward */ + do { + tm_bc = (tm_bc + 1) & 0177777; /* incr wc */ + if (st = sim_tape_sprecf (uptr, &tbc)) { /* spc rec fwd, err? */ + r = tm_map_err (uptr, st); /* map error */ + break; + } + } while (tm_bc != 0); + break; + + case MTC_SPACER: /* space reverse */ + do { + tm_bc = (tm_bc + 1) & 0177777; /* incr wc */ + if (st = sim_tape_sprecr (uptr, &tbc)) { /* spc rec rev, err? */ + r = tm_map_err (uptr, st); /* map error */ + break; + } + } while (tm_bc != 0); + break; + } /* end case */ + +tm_cmd = (tm_cmd & ~MTC_EMA) | ((xma >> (16 - MTC_V_EMA)) & MTC_EMA); +tm_ca = xma & 0177777; /* update mem addr */ +tm_set_done (); /* set done */ +tm_updcsta (uptr); /* update status */ +if (DEBUG_PRS (tm_dev)) fprintf (sim_deb, + ">>TM: sta=%o, ma=%o, bc=%o, pos=%d\n", + tm_sta, tm_ca, tm_bc, uptr->pos); +return r; +} + +/* Update controller status */ + +int32 tm_updcsta (UNIT *uptr) +{ +tm_sta = (tm_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN); +if (sim_tape_eot (uptr)) tm_sta = tm_sta | STA_EOT; +if (sim_is_active (uptr)) tm_sta = tm_sta & ~STA_TUR; +else tm_sta = tm_sta | STA_TUR; +if (tm_sta & STA_EFLGS) tm_cmd = tm_cmd | MTC_ERR; +else tm_cmd = tm_cmd & ~MTC_ERR; +if ((tm_cmd & MTC_IE) == 0) CLR_INT (TM); +return tm_sta; +} + +/* Set done */ + +void tm_set_done (void) +{ +tm_cmd = tm_cmd | MTC_DONE; +if (tm_cmd & MTC_IE) SET_INT (TM); +return; +} + +/* Map tape error status */ + +t_stat tm_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* not attached */ + tm_sta = tm_sta | STA_ILL; + case MTSE_OK: /* no error */ + return SCPE_IERR; + + case MTSE_TMK: /* tape mark */ + uptr->USTAT = uptr->USTAT | STA_EOF; /* end of file */ + break; + + case MTSE_IOERR: /* IO error */ + tm_sta = tm_sta | STA_PAR; /* parity error */ + if (tm_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + tm_sta = tm_sta | STA_PAR; /* parity error */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + tm_sta = tm_sta | STA_PAR; /* parity error */ + break; + + case MTSE_EOM: /* end of medium */ + tm_sta = tm_sta | STA_BAD; /* bad tape */ + break; + + case MTSE_BOT: /* reverse into BOT */ + uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */ + break; + + case MTSE_WRP: /* write protect */ + tm_sta = tm_sta | STA_ILL; /* illegal operation */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tm_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +tm_cmd = MTC_DONE; /* set done */ +tm_bc = tm_ca = tm_db = tm_sta = tm_rdl = 0; +CLR_INT (TM); /* clear interrupt */ +for (u = 0; u < TM_NUMDR; u++) { /* loop thru units */ + uptr = tm_dev.units + u; + sim_tape_reset (uptr); /* reset tape */ + sim_cancel (uptr); /* cancel activity */ + if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_ONL | + (sim_tape_bot (uptr)? STA_BOT: 0) | + (sim_tape_wrp (uptr)? STA_WLK: 0); + else uptr->USTAT = 0; + } +if (tmxb == NULL) tmxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); +if (tmxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat tm_attach (UNIT *uptr, char *cptr) +{ +t_stat r; +int32 u = uptr - tm_dev.units; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->USTAT = STA_ONL | STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0); +if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr); +return r; +} + +/* Detach routine */ + +t_stat tm_detach (UNIT* uptr) +{ +int32 u = uptr - tm_dev.units; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; +if (!sim_is_active (uptr)) uptr->USTAT = 0; +if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr); +return sim_tape_detach (uptr); +} + +/* Write lock/enable routine */ + +t_stat tm_vlock (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 u = uptr - tm_dev.units; + +if ((uptr->flags & UNIT_ATT) && + (val || sim_tape_wrp (uptr))) + uptr->USTAT = uptr->USTAT | STA_WLK; +else uptr->USTAT = uptr->USTAT & ~STA_WLK; +if (u == GET_UNIT (tm_cmd)) tm_updcsta (uptr); +return SCPE_OK; +} + +/* Device bootstrap + + Magtape boot format changed over time. Originally, a boot tape + contained a boot loader in the first block. Eventually, the first + block was reserved for a tape label, and the second block was + expected to contain a boot loader. BSD and DEC operating systems + use the second block scheme, so it is the default. + + To boot from the first block, use boot -o (old). +*/ + +#define BOOT_START 016000 +#define BOOT_ENTRY (BOOT_START + 2) +#define BOOT_UNIT (BOOT_START + 010) +#define BOOT_CSR (BOOT_START + 014) +#define BOOT1_LEN (sizeof (boot1_rom) / sizeof (int16)) +#define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16)) + +static const uint16 boot1_rom[] = { + 0046524, /* boot_start: "TM" */ + 0012706, BOOT_START, /* mov #boot_start, sp */ + 0012700, 0000000, /* mov #unit_num, r0 */ + 0012701, 0172526, /* mov #172526, r1 ; mtcma */ + 0005011, /* clr (r1) */ + 0010141, /* mov r1, -(r1) ; mtbrc */ + 0010002, /* mov r0,r2 */ + 0000302, /* swab r2 */ + 0062702, 0060003, /* add #60003, r2 */ + 0010241, /* mov r2, -(r1) ; read + go */ + 0105711, /* tstb (r1) ; mtc */ + 0100376, /* bpl .-2 */ + 0005002, /* clr r2 */ + 0005003, /* clr r3 */ + 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */ + 0005005, /* clr r5 */ + 0005007 /* clr r7 */ + }; + +static const uint16 boot2_rom[] = { + 0046524, /* boot_start: "TM" */ + 0012706, BOOT_START, /* mov #boot_start, sp */ + 0012700, 0000000, /* mov #unit_num, r0 */ + 0012701, 0172526, /* mov #172526, r1 ; mtcma */ + 0005011, /* clr (r1) */ + 0012741, 0177777, /* mov #-1, -(r1) ; mtbrc */ + 0010002, /* mov r0,r2 */ + 0000302, /* swab r2 */ + 0062702, 0060011, /* add #60011, r2 */ + 0010241, /* mov r2, -(r1) ; space + go */ + 0105711, /* tstb (r1) ; mtc */ + 0100376, /* bpl .-2 */ + 0010002, /* mov r0,r2 */ + 0000302, /* swab r2 */ + 0062702, 0060003, /* add #60003, r2 */ + 0010211, /* mov r2, (r1) ; read + go */ + 0105711, /* tstb (r1) ; mtc */ + 0100376, /* bpl .-2 */ + 0005002, /* clr r2 */ + 0005003, /* clr r3 */ + 0012704, BOOT_START+020, /* mov #boot_start+20, r4 */ + 0005005, /* clr r5 */ + 0005007 /* clr r7 */ + }; + +t_stat tm_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern int32 sim_switches; + +sim_tape_rewind (&tm_unit[unitno]); +if (sim_switches & SWMASK ('O')) { + for (i = 0; i < BOOT1_LEN; i++) + M[(BOOT_START >> 1) + i] = boot1_rom[i]; + } +else { + for (i = 0; i < BOOT2_LEN; i++) + M[(BOOT_START >> 1) + i] = boot2_rom[i]; + } +M[BOOT_UNIT >> 1] = unitno; +M[BOOT_CSR >> 1] = (tm_dib.ba & DMASK) + 06; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} diff --git a/PDP11/pdp11_tq.c b/PDP11/pdp11_tq.c new file mode 100644 index 0000000..b819efd --- /dev/null +++ b/PDP11/pdp11_tq.c @@ -0,0 +1,2174 @@ +/* pdp11_tq.c: TMSCP tape controller simulator + + Copyright (c) 2002-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tq TQK50 tape controller + + 18-Jun-07 RMS Added UNIT_IDLE flag to timer thread + 16-Feb-06 RMS Revised for new magtape capacity checking + 31-Oct-05 RMS Fixed address width for large files + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) + 30-Sep-04 RMS Revised Unibus interface + 12-Jun-04 RMS Fixed bug in reporting write protect (reported by Lyle Bickley) + 18-Apr-04 RMS Fixed TQK70 media ID and model byte (found by Robert Schaffrath) + 26-Mar-04 RMS Fixed warnings with -std=c99 + 25-Jan-04 RMS Revised for device debug support + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Added variable controller, user-defined drive support + 26-Feb-03 RMS Fixed bug in vector calculation for VAXen + 22-Feb-03 RMS Fixed ordering bug in queue process + Fixed flags table to allow MD_CSE everywhere + 09-Jan-03 RMS Fixed bug in transfer end packet status + 17-Oct-02 RMS Fixed bug in read reverse (found by Hans Pufal) +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "TQK50 not supported on PDP-10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +#if (UNIBUS) +#define INIT_TYPE TQ8_TYPE +#define INIT_CAP TQ8_CAP +#else +#define INIT_TYPE TQ5_TYPE +#define INIT_CAP TQ5_CAP +#endif + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define INIT_TYPE TQ5_TYPE +#define INIT_CAP TQ5_CAP +extern int32 cpu_opt; +#endif + +#include "pdp11_uqssp.h" +#include "pdp11_mscp.h" +#include "sim_tape.h" + +#define UF_MSK (UF_SCH|UF_VSS|UF_CMR|UF_CMW) /* settable flags */ + +#define TQ_SH_MAX 24 /* max display wds */ +#define TQ_SH_PPL 8 /* wds per line */ +#define TQ_SH_DPL 4 /* desc per line */ +#define TQ_SH_RI 001 /* show rings */ +#define TQ_SH_FR 002 /* show free q */ +#define TQ_SH_RS 004 /* show resp q */ +#define TQ_SH_UN 010 /* show unit q's */ +#define TQ_SH_ALL 017 /* show all */ + +#define TQ_CLASS 1 /* TQK50 class */ +#define TQ_DHTMO 0 /* def host timeout */ +#define TQ_DCTMO 120 /* def ctrl timeout */ +#define TQ_NUMDR 4 /* # drives */ +#define TQ_MAXFR (1 << 16) /* max xfer */ + +#define UNIT_V_ONL (MTUF_V_UF + 0) /* online */ +#define UNIT_V_ATP (MTUF_V_UF + 1) /* attn pending */ +#define UNIT_V_SXC (MTUF_V_UF + 2) /* serious exc */ +#define UNIT_V_POL (MTUF_V_UF + 3) /* position lost */ +#define UNIT_V_TMK (MTUF_V_UF + 4) /* tape mark seen */ +#define UNIT_ONL (1 << UNIT_V_ONL) +#define UNIT_ATP (1 << UNIT_V_ATP) +#define UNIT_SXC (1 << UNIT_V_SXC) +#define UNIT_POL (1 << UNIT_V_POL) +#define UNIT_TMK (1 << UNIT_V_TMK) +#define cpkt u3 /* current packet */ +#define pktq u4 /* packet queue */ +#define uf buf /* settable unit flags */ +#define objp wait /* object position */ +#define TQ_WPH(u) ((sim_tape_wrp (u))? UF_WPH: 0) + +#define CST_S1 0 /* init stage 1 */ +#define CST_S1_WR 1 /* stage 1 wrap */ +#define CST_S2 2 /* init stage 2 */ +#define CST_S3 3 /* init stage 3 */ +#define CST_S3_PPA 4 /* stage 3 sa wait */ +#define CST_S3_PPB 5 /* stage 3 ip wait */ +#define CST_S4 6 /* stage 4 */ +#define CST_UP 7 /* online */ +#define CST_DEAD 8 /* fatal error */ + +#define tq_comm tq_rq.ba + +#define ERR 0 /* must be SCPE_OK! */ +#define OK 1 + +#define CMF_IMM 0x10000 /* immediate */ +#define CMF_SEQ 0x20000 /* sequential */ +#define CMF_WR 0x40000 /* write */ +#define CMF_RW 0x80000 /* resp to GCS */ + +/* Internal packet management */ + +#define TQ_NPKTS 32 /* # packets (pwr of 2) */ +#define TQ_M_NPKTS (TQ_NPKTS - 1) /* mask */ +#define TQ_PKT_SIZE_W 32 /* payload size (wds) */ +#define TQ_PKT_SIZE (TQ_PKT_SIZE_W * sizeof (int16)) + +struct tqpkt { + int16 link; /* link to next */ + uint16 d[TQ_PKT_SIZE_W]; /* data */ + }; + +/* Packet payload extraction and insertion */ + +#define GETP(p,w,f) ((tq_pkt[p].d[w] >> w##_V_##f) & w##_M_##f) +#define GETP32(p,w) (((uint32) tq_pkt[p].d[w]) | \ + (((uint32) tq_pkt[p].d[(w)+1]) << 16)) +#define PUTP32(p,w,x) tq_pkt[p].d[w] = (x) & 0xFFFF; \ + tq_pkt[p].d[(w)+1] = ((x) >> 16) & 0xFFFF + +/* Controller and device types - TQK50 must be swre rev 5 or later */ + +#define TQ5_TYPE 0 /* TK50 */ +#define TQ5_UQPM 3 /* UQ port ID */ +#define TQ5_CMOD 9 /* ctrl ID */ +#define TQ5_UMOD 3 /* unit ID */ +#define TQ5_MED 0x6D68B032 /* media ID */ +#define TQ5_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQ5_FREV 0 /* formatter revs */ +#define TQ5_UREV 0 /* unit revs */ +#define TQ5_CAP (94 * (1 << 20)) /* capacity */ +#define TQ5_FMT (TF_CTP|TF_CTP_LO) /* menu */ + +#define TQ7_TYPE 1 /* TK70 */ +#define TQ7_UQPM 14 /* UQ port ID */ +#define TQ7_CMOD 14 /* ctrl ID */ +#define TQ7_UMOD 14 /* unit ID */ +#define TQ7_MED 0x6D68B046 /* media ID */ +#define TQ7_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQ7_FREV 0 /* formatter revs */ +#define TQ7_UREV 0 /* unit revs */ +#define TQ7_CAP (300 * (1 << 20)) /* capacity */ +#define TQ7_FMT (TF_CTP|TF_CTP_LO) /* menu */ + +#define TQ8_TYPE 2 /* TU81 */ +#define TQ8_UQPM 5 /* UQ port ID */ +#define TQ8_CMOD 5 /* ctrl ID */ +#define TQ8_UMOD 2 /* unit ID */ +#define TQ8_MED 0x6D695051 /* media ID */ +#define TQ8_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQ8_FREV 0 /* formatter revs */ +#define TQ8_UREV 0 /* unit revs */ +#define TQ8_CAP (180 * (1 << 20)) /* capacity */ +#define TQ8_FMT (TF_9TK|TF_9TK_GRP) /* menu */ + +#define TQU_TYPE 3 /* TKuser defined */ +#define TQU_UQPM 3 /* UQ port ID */ +#define TQU_CMOD 9 /* ctrl ID */ +#define TQU_UMOD 3 /* unit ID */ +#define TQU_MED 0x6D68B032 /* media ID */ +#define TQU_CREV ((1 << 8) | 5) /* ctrl revs */ +#define TQU_FREV 0 /* formatter revs */ +#define TQU_UREV 0 /* unit revs */ +#define TQU_CAP (94 * (1 << 20)) /* capacity */ +#define TQU_FMT (TF_CTP|TF_CTP_LO) /* menu */ +#define TQU_MINC 30 /* min cap MB */ +#define TQU_MAXC 2000 /* max cap MB */ +#define TQU_EMAXC 2000000000 /* ext max cap MB */ + +#define TQ_DRV(d) \ + d##_UQPM, \ + d##_CMOD, d##_MED, d##_FMT, d##_CAP, \ + d##_UMOD, d##_CREV, d##_FREV, d##_UREV + +#define TEST_EOT(u) (sim_tape_eot (u)) + +struct drvtyp { + uint32 uqpm; /* UQ port model */ + uint32 cmod; /* ctrl model */ + uint32 med; /* MSCP media */ + uint32 fmt; /* flags */ + t_addr cap; /* capacity */ + uint32 umod; /* unit model */ + uint32 cver; + uint32 fver; + uint32 uver; + char *name; + }; + +static struct drvtyp drv_tab[] = { + { TQ_DRV (TQ5), "TK50" }, + { TQ_DRV (TQ7), "TK70" }, + { TQ_DRV (TQ8), "TU81" }, + { TQ_DRV (TQU), "TKUSER" }, + }; + +/* Data */ + +extern int32 int_req[IPL_HLVL]; +extern int32 tmr_poll, clk_tps; +extern UNIT cpu_unit; +extern FILE *sim_deb; +extern uint32 sim_taddr_64; + +uint8 *tqxb = NULL; /* xfer buffer */ +uint32 tq_sa = 0; /* status, addr */ +uint32 tq_saw = 0; /* written data */ +uint32 tq_s1dat = 0; /* S1 data */ +uint32 tq_csta = 0; /* ctrl state */ +uint32 tq_perr = 0; /* last error */ +uint32 tq_cflgs = 0; /* ctrl flags */ +uint32 tq_prgi = 0; /* purge int */ +uint32 tq_pip = 0; /* poll in progress */ +struct uq_ring tq_cq = { 0 }; /* cmd ring */ +struct uq_ring tq_rq = { 0 }; /* rsp ring */ +struct tqpkt tq_pkt[TQ_NPKTS]; /* packet queue */ +int32 tq_freq = 0; /* free list */ +int32 tq_rspq = 0; /* resp list */ +uint32 tq_pbsy = 0; /* #busy pkts */ +uint32 tq_credits = 0; /* credits */ +uint32 tq_hat = 0; /* host timer */ +uint32 tq_htmo = TQ_DHTMO; /* host timeout */ +int32 tq_itime = 200; /* init time, except */ +int32 tq_itime4 = 10; /* stage 4 */ +int32 tq_qtime = 200; /* queue time */ +int32 tq_xtime = 500; /* transfer time */ +int32 tq_typ = INIT_TYPE; /* device type */ + +/* Command table - legal modifiers (low 16b) and flags (high 16b) */ + +static uint32 tq_cmf[64] = { + 0, /* 0 */ + CMF_IMM, /* abort */ + CMF_IMM|MD_CSE, /* get cmd status */ + CMF_IMM|MD_CSE|MD_NXU, /* get unit status */ + CMF_IMM|MD_CSE, /* set ctrl char */ + 0, 0, 0, /* 5-7 */ + CMF_SEQ|MD_ACL|MD_CDL|MD_CSE|MD_EXA|MD_UNL, /* available */ + CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* online */ + CMF_SEQ|MD_CDL|MD_CSE|MD_SWP|MD_EXA, /* set unit char */ + CMF_IMM, /* define acc paths */ + 0, 0, 0, 0, /* 12-15 */ + CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* access */ + MD_SCH|MD_SEC|MD_SER, + 0, /* 17 */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE, /* flush */ + 0, 0, /* 20-21 */ + CMF_SEQ|CMF_WR|MD_CDL|MD_CSE|MD_IMM, /* erase gap */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 22-31 */ + CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV| /* compare */ + MD_SCH|MD_SEC|MD_SER, + CMF_SEQ|CMF_RW|MD_CDL|MD_CSE|MD_REV|MD_CMP| /* read */ + MD_SCH|MD_SEC|MD_SER, + CMF_SEQ|CMF_RW|CMF_WR|MD_CDL|MD_CSE|MD_IMM| /* write */ + MD_CMP|MD_ERW|MD_SEC|MD_SER, + 0, /* 35 */ + CMF_SEQ|MD_CDL|MD_CSE|MD_IMM, /* wr tape mark */ + CMF_SEQ|MD_CDL|MD_CSE|MD_IMM|MD_OBC| /* reposition */ + MD_REV|MD_RWD|MD_DLE| + MD_SCH|MD_SEC|MD_SER, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 38-47 */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + +/* Forward references */ + +DEVICE tq_dev; + +t_stat tq_rd (int32 *data, int32 PA, int32 access); +t_stat tq_wr (int32 data, int32 PA, int32 access); +t_stat tq_inta (void); +t_stat tq_svc (UNIT *uptr); +t_stat tq_tmrsvc (UNIT *uptr); +t_stat tq_quesvc (UNIT *uptr); +t_stat tq_reset (DEVICE *dptr); +t_stat tq_attach (UNIT *uptr, char *cptr); +t_stat tq_detach (UNIT *uptr); +t_stat tq_boot (int32 unitno, DEVICE *dptr); +t_stat tq_set_wlk (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tq_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc); + +t_bool tq_step4 (void); +t_bool tq_mscp (int32 pkt, t_bool q); +t_bool tq_abo (int32 pkt); +t_bool tq_avl (int32 pkt); +t_bool tq_erase (int32 pkt); +t_bool tq_flu (int32 pkt); +t_bool tq_gcs (int32 pkt); +t_bool tq_gus (int32 pkt); +t_bool tq_onl (int32 pkt); +t_bool tq_pos (int32 pkt); +t_bool tq_rw (int32 pkt); +t_bool tq_scc (int32 pkt); +t_bool tq_suc (int32 pkt); +t_bool tq_wtm (int32 pkt); +t_bool tq_plf (uint32 err); +t_bool tq_dte (UNIT *uptr, uint32 err); +t_bool tq_hbe (UNIT *uptr, uint32 ba); +t_bool tq_una (UNIT *uptr); +uint32 tq_map_status (UNIT *uptr, t_stat st); +uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec); +uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped); +uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc); +uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec); +uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped); +uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc); +t_bool tq_deqf (int32 *pkt); +int32 tq_deqh (int32 *lh); +void tq_enqh (int32 *lh, int32 pkt); +void tq_enqt (int32 *lh, int32 pkt); +t_bool tq_getpkt (int32 *pkt); +t_bool tq_putpkt (int32 pkt, t_bool qt); +t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc); +t_bool tq_putdesc (struct uq_ring *ring, uint32 desc); +int32 tq_mot_valid (UNIT *uptr, uint32 cmd); +t_stat tq_mot_err (UNIT *uptr, uint32 rsiz); +t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz); +void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ); +void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all); +void tq_setf_unit (int32 pkt, UNIT *uptr); +uint32 tq_efl (UNIT *uptr); +void tq_init_int (void); +void tq_ring_int (struct uq_ring *ring); +t_bool tq_fatal (uint32 err); +UNIT *tq_getucb (uint32 lu); + +/* TQ data structures + + tq_dev TQ device descriptor + tq_unit TQ unit list + tq_reg TQ register list + tq_mod TQ modifier list +*/ + +DIB tq_dib = { + IOBA_TQ, IOLN_TQ, &tq_rd, &tq_wr, + 1, IVCL (TQ), 0, { &tq_inta } + }; + +UNIT tq_unit[] = { + { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0), INIT_CAP }, + { UDATA (&tq_tmrsvc, UNIT_DIS, 0) }, + { UDATA (&tq_quesvc, UNIT_DIS, 0) } + }; + +#define TQ_TIMER (TQ_NUMDR) +#define TQ_QUEUE (TQ_TIMER + 1) + +REG tq_reg[] = { + { GRDATA (SA, tq_sa, DEV_RDX, 16, 0) }, + { GRDATA (SAW, tq_saw, DEV_RDX, 16, 0) }, + { GRDATA (S1DAT, tq_s1dat, DEV_RDX, 16, 0) }, + { GRDATA (CQBA, tq_cq.ba, DEV_RDX, 22, 0) }, + { GRDATA (CQLNT, tq_cq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (CQIDX, tq_cq.idx, DEV_RDX, 8, 2) }, + { GRDATA (TQBA, tq_rq.ba, DEV_RDX, 22, 0) }, + { GRDATA (TQLNT, tq_rq.lnt, DEV_RDX, 8, 2), REG_NZ }, + { GRDATA (TQIDX, tq_rq.idx, DEV_RDX, 8, 2) }, + { DRDATA (FREE, tq_freq, 5) }, + { DRDATA (RESP, tq_rspq, 5) }, + { DRDATA (PBSY, tq_pbsy, 5) }, + { GRDATA (CFLGS, tq_cflgs, DEV_RDX, 16, 0) }, + { GRDATA (CSTA, tq_csta, DEV_RDX, 4, 0) }, + { GRDATA (PERR, tq_perr, DEV_RDX, 9, 0) }, + { DRDATA (CRED, tq_credits, 5) }, + { DRDATA (HAT, tq_hat, 17) }, + { DRDATA (HTMO, tq_htmo, 17) }, + { URDATA (CPKT, tq_unit[0].cpkt, 10, 5, 0, TQ_NUMDR, 0) }, + { URDATA (PKTQ, tq_unit[0].pktq, 10, 5, 0, TQ_NUMDR, 0) }, + { URDATA (UFLG, tq_unit[0].uf, DEV_RDX, 16, 0, TQ_NUMDR, 0) }, + { URDATA (POS, tq_unit[0].pos, 10, T_ADDR_W, 0, TQ_NUMDR, 0) }, + { URDATA (OBJP, tq_unit[0].objp, 10, 32, 0, TQ_NUMDR, 0) }, + { FLDATA (PRGI, tq_prgi, 0), REG_HIDDEN }, + { FLDATA (PIP, tq_pip, 0), REG_HIDDEN }, + { FLDATA (INT, IREQ (TQ), INT_V_TQ) }, + { DRDATA (ITIME, tq_itime, 24), PV_LEFT + REG_NZ }, + { DRDATA (I4TIME, tq_itime4, 24), PV_LEFT + REG_NZ }, + { DRDATA (QTIME, tq_qtime, 24), PV_LEFT + REG_NZ }, + { DRDATA (XTIME, tq_xtime, 24), PV_LEFT + REG_NZ }, + { BRDATA (PKTS, tq_pkt, DEV_RDX, 16, TQ_NPKTS * (TQ_PKT_SIZE_W + 1)) }, + { DRDATA (DEVTYPE, tq_typ, 2), REG_HRO }, + { DRDATA (DEVCAP, drv_tab[TQU_TYPE].cap, T_ADDR_W), PV_LEFT | REG_HRO }, + { GRDATA (DEVADDR, tq_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, tq_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB tq_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VDV, TQ5_TYPE, NULL, "TK50", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TQ7_TYPE, NULL, "TK70", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TQ8_TYPE, NULL, "TU81", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, TQU_TYPE, NULL, "TKUSER", + &tq_set_type, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, + NULL, &tq_show_type, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RI, "RINGS", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_FR, "FREEQ", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_RS, "RESPQ", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_UN, "UNITQ", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, TQ_SH_ALL, "ALL", NULL, + NULL, &tq_show_ctrl, NULL }, + { MTAB_XTD | MTAB_VUN | MTAB_NMO, 0, "UNITQ", NULL, + NULL, &tq_show_unitq, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, +#if defined (VM_PDP11) + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, +#else + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, +#endif + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE tq_dev = { + "TQ", tq_unit, tq_reg, tq_mod, + TQ_NUMDR + 2, 10, T_ADDR_W, 1, DEV_RDX, 8, + NULL, NULL, &tq_reset, + &tq_boot, &tq_attach, &tq_detach, + &tq_dib, DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG + }; + +/* I/O dispatch routines, I/O addresses 17772150 - 17772152 + + 17772150 IP read/write + 17772152 SA read/write +*/ + +t_stat tq_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + case 0: /* IP */ + *data = 0; /* reads zero */ + if (tq_csta == CST_S3_PPB) tq_step4 (); /* waiting for poll? */ + else if (tq_csta == CST_UP) { /* if up */ + tq_pip = 1; /* poll host */ + sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); + } + break; + + case 1: /* SA */ + *data = tq_sa; + break; + } + +return SCPE_OK; +} + +t_stat tq_wr (int32 data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* IP */ + tq_reset (&tq_dev); /* init device */ + if (DEBUG_PRS (tq_dev)) fprintf (sim_deb, + ">>TQ: initialization started, time=%.0f\n", sim_gtime ()); + break; + + case 1: /* SA */ + tq_saw = data; + if (tq_csta < CST_S4) /* stages 1-3 */ + sim_activate (&tq_unit[TQ_QUEUE], tq_itime); + else if (tq_csta == CST_S4) /* stage 4 (fast) */ + sim_activate (&tq_unit[TQ_QUEUE], tq_itime4); + break; + } + +return SCPE_OK; +} + +/* Transition to step 4 - init communications region */ + +t_bool tq_step4 (void) +{ +int32 i, lnt; +uint32 base; +uint16 zero[SA_COMM_MAX >> 1]; + +tq_rq.ioff = SA_COMM_RI; /* set intr offset */ +tq_rq.ba = tq_comm; /* set rsp q base */ +tq_rq.lnt = SA_S1H_RQ (tq_s1dat) << 2; /* get resp q len */ +tq_cq.ioff = SA_COMM_CI; /* set intr offset */ +tq_cq.ba = tq_comm + tq_rq.lnt; /* set cmd q base */ +tq_cq.lnt = SA_S1H_CQ (tq_s1dat) << 2; /* get cmd q len */ +tq_cq.idx = tq_rq.idx = 0; /* clear q idx's */ +if (tq_prgi) base = tq_comm + SA_COMM_QQ; +else base = tq_comm + SA_COMM_CI; +lnt = tq_comm + tq_cq.lnt + tq_rq.lnt - base; /* comm lnt */ +if (lnt > SA_COMM_MAX) lnt = SA_COMM_MAX; /* paranoia */ +for (i = 0; i < (lnt >> 1); i++) zero[i] = 0; /* clr buffer */ +if (Map_WriteW (base, lnt, zero)) /* zero comm area */ + return tq_fatal (PE_QWE); /* error? */ +tq_sa = SA_S4 | (drv_tab[tq_typ].uqpm << SA_S4C_V_MOD) |/* send step 4 */ + ((drv_tab[tq_typ].cver & 0xFF) << SA_S4C_V_VER); +tq_csta = CST_S4; /* set step 4 */ +tq_init_int (); /* poke host */ +return OK; +} + +/* Queue service - invoked when any of the queues (host queue, unit + queues, response queue) require servicing. Also invoked during + initialization to provide some delay to the next step. + + Process at most one item off each unit queue + If the unit queues were empty, process at most one item off the host queue + Process at most one item off the response queue + + If all queues are idle, terminate thread +*/ + +t_stat tq_quesvc (UNIT *uptr) +{ +int32 i, cnid; +int32 pkt = 0; +UNIT *nuptr; + +if (tq_csta < CST_UP) { /* still init? */ + switch (tq_csta) { /* controller state? */ + + case CST_S1: /* need S1 reply */ + if (tq_saw & SA_S1H_VL) { /* valid? */ + if (tq_saw & SA_S1H_WR) { /* wrap? */ + tq_sa = tq_saw; /* echo data */ + tq_csta = CST_S1_WR; /* endless loop */ + } + else { + tq_s1dat = tq_saw; /* save data */ + tq_dib.vec = (tq_s1dat & SA_S1H_VEC) << 2; /* get vector */ + if (tq_dib.vec) tq_dib.vec = tq_dib.vec + VEC_Q; /* if nz, bias */ + tq_sa = SA_S2 | SA_S2C_PT | SA_S2C_EC (tq_s1dat); + tq_csta = CST_S2; /* now in step 2 */ + tq_init_int (); /* intr if req */ + } + } /* end if valid */ + break; + + case CST_S1_WR: /* wrap mode */ + tq_sa = tq_saw; /* echo data */ + break; + + case CST_S2: /* need S2 reply */ + tq_comm = tq_saw & SA_S2H_CLO; /* get low addr */ + tq_prgi = tq_saw & SA_S2H_PI; /* get purge int */ + tq_sa = SA_S3 | SA_S3C_EC (tq_s1dat); + tq_csta = CST_S3; /* now in step 3 */ + tq_init_int (); /* intr if req */ + break; + + case CST_S3: /* need S3 reply */ + tq_comm = ((tq_saw & SA_S3H_CHI) << 16) | tq_comm; + if (tq_saw & SA_S3H_PP) { /* purge/poll test? */ + tq_sa = 0; /* put 0 */ + tq_csta = CST_S3_PPA; /* wait for 0 write */ + } + else tq_step4 (); /* send step 4 */ + break; + + case CST_S3_PPA: /* need purge test */ + if (tq_saw) tq_fatal (PE_PPF); /* data not zero? */ + else tq_csta = CST_S3_PPB; /* wait for poll */ + break; + + case CST_S4: /* need S4 reply */ + if (tq_saw & SA_S4H_GO) { /* go set? */ + if (DEBUG_PRS (tq_dev)) fprintf (sim_deb, + ">>TQ: initialization complete\n"); + tq_csta = CST_UP; /* we're up */ + tq_sa = 0; /* clear SA */ + sim_activate (&tq_unit[TQ_TIMER], tmr_poll * clk_tps); + if ((tq_saw & SA_S4H_LF) && tq_perr) tq_plf (tq_perr); + tq_perr = 0; + } + break; + } /* end switch */ + return SCPE_OK; + } /* end if */ + +for (i = 0; i < TQ_NUMDR; i++) { /* chk unit q's */ + nuptr = tq_dev.units + i; /* ptr to unit */ + if (nuptr->cpkt || (nuptr->pktq == 0)) continue; + pkt = tq_deqh (&nuptr->pktq); /* get top of q */ + if (!tq_mscp (pkt, FALSE)) return SCPE_OK; /* process */ + } +if ((pkt == 0) && tq_pip) { /* polling? */ + if (!tq_getpkt (&pkt)) return SCPE_OK; /* get host pkt */ + if (pkt) { /* got one? */ + if (DEBUG_PRS (tq_dev)) { + UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); + fprintf (sim_deb, ">>TQ: cmd=%04X, mod=%04X, unit=%d, ", + tq_pkt[pkt].d[CMD_OPC], tq_pkt[pkt].d[CMD_MOD], tq_pkt[pkt].d[CMD_UN]); + fprintf (sim_deb, "bc=%04X%04X, ma=%04X%04X", + tq_pkt[pkt].d[RW_BCH], tq_pkt[pkt].d[RW_BCL], + tq_pkt[pkt].d[RW_BAH], tq_pkt[pkt].d[RW_BAL]); + if (up) fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); + else fprintf (sim_deb, "\n"); + fflush (sim_deb); + } + if (GETP (pkt, UQ_HCTC, TYP) != UQ_TYP_SEQ) /* seq packet? */ + return tq_fatal (PE_PIE); /* no, term thread */ + cnid = GETP (pkt, UQ_HCTC, CID); /* get conn ID */ + if (cnid == UQ_CID_TMSCP) { /* TMSCP packet? */ + if (!tq_mscp (pkt, TRUE)) return SCPE_OK; /* proc, q non-seq */ + } + else if (cnid == UQ_CID_DUP) { /* DUP packet? */ + tq_putr (pkt, OP_END, 0, ST_CMD | I_OPCD, RSP_LNT, UQ_TYP_SEQ); + if (!tq_putpkt (pkt, TRUE)) return SCPE_OK; /* ill cmd */ + } + else return tq_fatal (PE_ICI); /* no, term thread */ + } /* end if pkt */ + else tq_pip = 0; /* discontinue poll */ + } /* end if pip */ +if (tq_rspq) { /* resp q? */ + pkt = tq_deqh (&tq_rspq); /* get top of q */ + if (!tq_putpkt (pkt, FALSE)) return SCPE_OK; /* send to host */ + } /* end if resp q */ +if (pkt) sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* more to do? */ +return SCPE_OK; /* done */ +} + +/* Clock service (roughly once per second) */ + +t_stat tq_tmrsvc (UNIT *uptr) +{ +int32 i; +UNIT *nuptr; + +sim_activate (uptr, tmr_poll * clk_tps); /* reactivate */ +for (i = 0; i < TQ_NUMDR; i++) { /* poll */ + nuptr = tq_dev.units + i; + if ((nuptr->flags & UNIT_ATP) && /* ATN pending? */ + (nuptr->flags & UNIT_ATT) && /* still online? */ + (tq_cflgs & CF_ATN)) { /* wanted? */ + if (!tq_una (nuptr)) return SCPE_OK; + } + nuptr->flags = nuptr->flags & ~UNIT_ATP; + } +if ((tq_hat > 0) && (--tq_hat == 0)) /* host timeout? */ + tq_fatal (PE_HAT); /* fatal err */ +return SCPE_OK; +} + +/* MSCP packet handling */ + +t_bool tq_mscp (int32 pkt, t_bool q) +{ +uint32 sts; +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* command */ +uint32 flg = GETP (pkt, CMD_OPC, FLG); /* flags */ +uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +UNIT *uptr; + +if ((cmd >= 64) || (tq_cmf[cmd] == 0)) { /* invalid cmd? */ + cmd = OP_END; /* set end op */ + sts = ST_CMD | I_OPCD; /* ill op */ + } +else if (flg) { /* flags? */ + cmd = cmd | OP_END; /* set end flag */ + sts = ST_CMD | I_FLAG; /* ill flags */ + } +else if (mdf & ~tq_cmf[cmd]) { /* invalid mod? */ + cmd = cmd | OP_END; /* set end flag */ + sts = ST_CMD | I_MODF; /* ill mods */ + } +else { /* valid cmd */ + if (uptr = tq_getucb (lu)) { /* valid unit? */ + if (q && (tq_cmf[cmd] & CMF_SEQ) && /* queueing, seq, */ + (uptr->cpkt || uptr->pktq)) { /* and active? */ + tq_enqt (&uptr->pktq, pkt); /* do later */ + return OK; + } +/* if (tq_cmf[cmd] & MD_CDL) /* clr cch lost? */ +/* uptr->flags = uptr->flags & ~UNIT_CDL; */ + if (tq_cmf[cmd] & MD_CSE) /* clr ser exc? */ + uptr->flags = uptr->flags & ~UNIT_SXC; + } + switch (cmd) { + + case OP_ABO: /* abort */ + return tq_abo (pkt); + + case OP_AVL: /* avail */ + return tq_avl (pkt); + + case OP_GCS: /* get cmd status */ + return tq_gcs (pkt); + + case OP_GUS: /* get unit status */ + return tq_gus (pkt); + + case OP_ONL: /* online */ + return tq_onl (pkt); + + case OP_SCC: /* set ctrl char */ + return tq_scc (pkt); + + case OP_SUC: /* set unit char */ + return tq_suc (pkt); + + case OP_ERS: /* erase */ + case OP_ERG: /* erase gap */ + return tq_erase (pkt); + + case OP_FLU: /* flush */ + return tq_flu (pkt); + + case OP_POS: /* position */ + return tq_pos (pkt); + + case OP_WTM: /* write tape mark */ + return tq_wtm (pkt); + + case OP_ACC: /* access */ + case OP_CMP: /* compare */ + case OP_RD: /* read */ + case OP_WR: /* write */ + return tq_rw (pkt); + + case OP_DAP: + cmd = cmd | OP_END; /* set end flag */ + sts = ST_SUC; /* success */ + break; + + default: + cmd = OP_END; /* set end op */ + sts = ST_CMD | I_OPCD; /* ill op */ + break; + } /* end switch */ + } /* end else */ +tq_putr (pkt, cmd, 0, sts, RSP_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Abort a command - 1st parameter is ref # of cmd to abort */ + +t_bool tq_abo (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 ref = GETP32 (pkt, ABO_REFL); /* cmd ref # */ +int32 tpkt, prv; +UNIT *uptr; + +tpkt = 0; /* set no mtch */ +if (uptr = tq_getucb (lu)) { /* get unit */ + if (uptr->cpkt && /* curr pkt? */ + (GETP32 (uptr->cpkt, CMD_REFL) == ref)) { /* match ref? */ + tpkt = uptr->cpkt; /* save match */ + uptr->cpkt = 0; /* gonzo */ + sim_cancel (uptr); /* cancel unit */ + sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); + } + else if (uptr->pktq && /* head of q? */ + (GETP32 (uptr->pktq, CMD_REFL) == ref)) { /* match ref? */ + tpkt = uptr->pktq; /* save match */ + uptr->pktq = tq_pkt[tpkt].link; /* unlink */ + } + else if (prv = uptr->pktq) { /* srch pkt q */ + while (tpkt = tq_pkt[prv].link) { /* walk list */ + if (GETP32 (tpkt, RSP_REFL) == ref) { /* match ref? */ + tq_pkt[prv].link = tq_pkt[tpkt].link; /* unlink */ + break; + } + } + } + if (tpkt) { /* found target? */ + uint32 tcmd = GETP (tpkt, CMD_OPC, OPC); /* get opcode */ + tq_putr (tpkt, tcmd | OP_END, 0, ST_ABO, RSP_LNT, UQ_TYP_SEQ); + if (!tq_putpkt (tpkt, TRUE)) return ERR; + } + } /* end if unit */ +tq_putr (pkt, OP_ABO | OP_END, 0, ST_SUC, ABO_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Unit available - set unit status to available + Deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_avl (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifiers */ +uint32 sts; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if (uptr->flags & UNIT_SXC) sts = ST_SXC; /* ser exc pending? */ + else { + uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_TMK | UNIT_POL); + sim_tape_rewind (uptr); /* rewind */ + uptr->uf = uptr->objp = 0; /* clr flags */ + if (uptr->flags & UNIT_ATT) { /* attached? */ + sts = ST_SUC; /* success */ + if (mdf & MD_UNL) tq_detach (uptr); /* unload? */ + } + else sts = ST_OFL | SB_OFL_NV; /* no, offline */ + } + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_AVL | OP_END, tq_efl (uptr), sts, AVL_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Get command status - only interested in active xfr cmd */ + +t_bool tq_gcs (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 ref = GETP32 (pkt, GCS_REFL); /* ref # */ +int32 tpkt; +UNIT *uptr; + +if ((uptr = tq_getucb (lu)) && /* valid lu? */ + (tpkt = uptr->cpkt) && /* queued pkt? */ + (GETP32 (tpkt, CMD_REFL) == ref) && /* match ref? */ + (tq_cmf[GETP (tpkt, CMD_OPC, OPC)] & CMF_RW)) { /* rd/wr cmd? */ + tq_pkt[pkt].d[GCS_STSL] = tq_pkt[tpkt].d[RW_BCL]; + tq_pkt[pkt].d[GCS_STSH] = tq_pkt[tpkt].d[RW_BCH]; + } +else tq_pkt[pkt].d[GCS_STSL] = tq_pkt[pkt].d[GCS_STSH] = 0; +tq_putr (pkt, OP_GCS | OP_END, 0, ST_SUC, GCS_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Get unit status */ + +t_bool tq_gus (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +if (tq_pkt[pkt].d[CMD_MOD] & MD_NXU) { /* next unit? */ + if (lu >= TQ_NUMDR) { /* end of range? */ + lu = 0; /* reset to 0 */ + tq_pkt[pkt].d[RSP_UN] = lu; + } + } +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else if (uptr->flags & UNIT_ONL) sts = ST_SUC; /* online */ + else sts = ST_AVL; /* avail */ + tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */ + tq_pkt[pkt].d[GUS_MENU] = drv_tab[tq_typ].fmt; /* format menu */ + tq_pkt[pkt].d[GUS_CAP] = 0; /* free capacity */ + tq_pkt[pkt].d[GUS_FVER] = drv_tab[tq_typ].fver; /* formatter version */ + tq_pkt[pkt].d[GUS_UVER] = drv_tab[tq_typ].uver; /* unit version */ + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_GUS | OP_END, tq_efl (uptr), sts, GUS_LNT_T, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Unit online - deferred if q'd commands */ + +t_bool tq_onl (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else if (uptr->flags & UNIT_ONL) /* already online? */ + sts = ST_SUC | SB_SUC_ON; + else { + sts = ST_SUC; /* mark online */ + sim_tape_rewind (uptr); /* rewind */ + uptr->objp = 0; /* clear flags */ + uptr->flags = (uptr->flags | UNIT_ONL) & + ~(UNIT_TMK | UNIT_POL); /* onl, pos ok */ + tq_setf_unit (pkt, uptr); /* hack flags */ + } + tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */ + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_ONL | OP_END, tq_efl (uptr), sts, ONL_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Set controller characteristics */ + +t_bool tq_scc (int32 pkt) +{ +if (tq_pkt[pkt].d[SCC_MSV]) /* MSCP ver = 0? */ + tq_putr (pkt, 0, 0, ST_CMD | I_VRSN, SCC_LNT, UQ_TYP_SEQ); +else { + tq_cflgs = (tq_cflgs & CF_RPL) | /* hack ctrl flgs */ + tq_pkt[pkt].d[SCC_CFL]; + if (tq_htmo = tq_pkt[pkt].d[SCC_TMO]) /* set timeout */ + tq_htmo = tq_htmo + 2; /* if nz, round up */ + tq_pkt[pkt].d[SCC_CFL] = tq_cflgs; /* return flags */ + tq_pkt[pkt].d[SCC_TMO] = TQ_DCTMO; /* ctrl timeout */ + tq_pkt[pkt].d[SCC_VER] = drv_tab[tq_typ].cver; /* ctrl version */ + tq_pkt[pkt].d[SCC_CIDA] = 0; /* ctrl ID */ + tq_pkt[pkt].d[SCC_CIDB] = 0; + tq_pkt[pkt].d[SCC_CIDC] = 0; + tq_pkt[pkt].d[SCC_CIDD] = (TQ_CLASS << SCC_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << SCC_CIDD_V_MOD); + PUTP32 (pkt, SCC_MBCL, TQ_MAXFR); /* max bc */ + tq_putr (pkt, OP_SCC | OP_END, 0, ST_SUC, SCC_LNT, UQ_TYP_SEQ); + } +return tq_putpkt (pkt, TRUE); +} + +/* Set unit characteristics - defer if q'd commands */ + +t_bool tq_suc (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + sts = ST_OFL | SB_OFL_NV; /* offl no vol */ + else { + sts = ST_SUC; /* avail or onl */ + tq_setf_unit (pkt, uptr); /* hack flags */ + } + tq_putr_unit (pkt, uptr, lu, TRUE); /* set fields */ + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_SUC | OP_END, 0, sts, SUC_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Flush - sequential nop - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_flu (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) /* unit exist? */ + sts = tq_mot_valid (uptr, OP_FLU); /* validate req */ +else sts = ST_OFL; /* offline */ +tq_putr (pkt, OP_FLU | OP_END, tq_efl (uptr), sts, FLU_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Erase, erase gap - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_erase (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 sts; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + sts = tq_mot_valid (uptr, cmd); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + uptr->cpkt = pkt; /* op in progress */ + sim_activate (uptr, tq_xtime); /* activate */ + return OK; /* done */ + } + } +else sts = ST_OFL; /* offline */ +tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, ERS_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Write tape mark - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_wtm (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts, objp = 0; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + objp = uptr->objp; /* position op */ + sts = tq_mot_valid (uptr, OP_WTM); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + uptr->cpkt = pkt; /* op in progress */ + sim_activate (uptr, tq_xtime); /* activate */ + return OK; /* done */ + } + } +else sts = ST_OFL; /* offline */ +PUTP32 (pkt, WTM_POSL, objp); /* set obj pos */ +tq_putr (pkt, OP_WTM | OP_END, tq_efl (uptr), sts, WTM_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Position - deferred if q'd cmds, bypassed if ser exc */ + +t_bool tq_pos (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 sts, objp = 0; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + objp = uptr->objp; /* position op */ + sts = tq_mot_valid (uptr, OP_POS); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + uptr->cpkt = pkt; /* op in progress */ + sim_activate (uptr, tq_xtime); /* activate */ + return OK; /* done */ + } + } +else sts = ST_OFL; /* offline */ +PUTP32 (pkt, POS_RCL, 0); /* clear #skipped */ +PUTP32 (pkt, POS_TMCL, 0); +PUTP32 (pkt, POS_POSL, objp); /* set obj pos */ +tq_putr (pkt, OP_POS | OP_END, tq_efl (uptr), sts, POS_LNT, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Data transfer commands - deferred if q'd commands, bypassed if ser exc */ + +t_bool tq_rw (int32 pkt) +{ +uint32 lu = tq_pkt[pkt].d[CMD_UN]; /* unit # */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* opcode */ +uint32 bc = GETP32 (pkt, RW_BCL); /* byte count */ +uint32 sts, objp = 0; +UNIT *uptr; + +if (uptr = tq_getucb (lu)) { /* unit exist? */ + objp = uptr->objp; /* position op */ + sts = tq_mot_valid (uptr, cmd); /* validity checks */ + if (sts == ST_SUC) { /* ok? */ + if ((bc == 0) || (bc > TQ_MAXFR)) { /* invalid? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + sts = ST_CMD | I_BCNT; + } + else { + uptr->cpkt = pkt; /* op in progress */ + sim_activate (uptr, tq_xtime); /* activate */ + return OK; /* done */ + } + } + } +else sts = ST_OFL; /* offline */ +PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ +PUTP32 (pkt, RW_POSL, objp); /* set obj pos */ +PUTP32 (pkt, RW_RSZL, 0); /* clr rec size */ +tq_putr (pkt, cmd | OP_END, tq_efl (uptr), sts, RW_LNT_T, UQ_TYP_SEQ); +return tq_putpkt (pkt, TRUE); +} + +/* Validity checks */ + +int32 tq_mot_valid (UNIT *uptr, uint32 cmd) +{ +if (uptr->flags & UNIT_SXC) return ST_SXC; /* ser exc pend? */ +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return (ST_OFL | SB_OFL_NV); /* offl no vol */ +if ((uptr->flags & UNIT_ONL) == 0) /* not online? */ + return ST_AVL; /* only avail */ +if (tq_cmf[cmd] & CMF_WR) { /* write op? */ + if (uptr->uf & UF_WPS) { /* swre wlk? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + return (ST_WPR | SB_WPR_SW); + } + if (TQ_WPH (uptr)) { /* hwre wlk? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + return (ST_WPR | SB_WPR_HW); + } + } +return ST_SUC; /* success! */ +} + +/* Unit service for motion commands */ + +t_stat tq_svc (UNIT *uptr) +{ +uint32 t, sts, sktmk, skrec; +t_mtrlnt i, tbc, wbc; +int32 pkt = uptr->cpkt; /* get packet */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ +uint32 mdf = tq_pkt[pkt].d[CMD_MOD]; /* modifier */ +uint32 ba = GETP32 (pkt, RW_BAL); /* buf addr */ +t_mtrlnt bc = GETP32 (pkt, RW_BCL); /* byte count */ +uint32 nrec = GETP32 (pkt, POS_RCL); /* #rec to skip */ +uint32 ntmk = GETP32 (pkt, POS_TMCL); /* #tmk to skp */ + +if (pkt == 0) return SCPE_IERR; /* what??? */ +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + tq_mot_end (uptr, 0, ST_OFL | SB_OFL_NV, 0); /* offl no vol */ + return SCPE_OK; + } + +if (tq_cmf[cmd] & CMF_WR) { /* write op? */ + if (TQ_WPH (uptr)) { /* hwre write prot? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + tq_mot_end (uptr, 0, ST_WPR | SB_WPR_HW, 0); + return SCPE_OK; + } + if (uptr->uf & UF_WPS) { /* swre write prot? */ + uptr->flags = uptr->flags | UNIT_SXC; /* set ser exc */ + tq_mot_end (uptr, 0, ST_WPR | SB_WPR_SW, 0); + return SCPE_OK; + } + } +sts = ST_SUC; /* assume success */ +tbc = 0; /* assume zero rec */ +switch (cmd) { /* case on command */ + + case OP_RD:case OP_ACC:case OP_CMP: /* read-like op */ + if (mdf & MD_REV) sts = tq_rdbufr (uptr, &tbc); /* read record */ + else sts = tq_rdbuff (uptr, &tbc); + if (sts == ST_DRV) { /* read error? */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ + return tq_mot_err (uptr, tbc); /* log, done */ + } + if ((sts != ST_SUC) || (cmd == OP_ACC)) { /* error or access? */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes processed */ + break; + } + if (tbc > bc) { /* tape rec > buf? */ + uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ + sts = ST_RDT; /* data truncated */ + wbc = bc; /* set working bc */ + } + else wbc = tbc; + if (cmd == OP_RD) { /* read? */ + if (t = Map_WriteB (ba, wbc, tqxb)) { /* store, nxm? */ + PUTP32 (pkt, RW_BCL, wbc - t); /* adj bc */ + if (tq_hbe (uptr, ba + wbc - t)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc); + return SCPE_OK; /* end if nxm */ + } + } /* end if read */ + else { /* compare */ + uint8 mby, dby; + uint32 mba; + for (i = 0; i < wbc; i++) { /* loop */ + if (mdf & MD_REV) { /* reverse? */ + mba = ba + bc - 1 - i; /* mem addr */ + dby = tqxb[tbc - 1 - i]; /* byte */ + } + else { + mba = ba + i; + dby = tqxb[i]; + } + if (Map_ReadB (mba, 1, &mby)) { /* fetch, nxm? */ + PUTP32 (pkt, RW_BCL, i); /* adj bc */ + if (tq_hbe (uptr, mba)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, tbc); + return SCPE_OK; + } + if (mby != dby) { /* cmp err? */ + uptr->flags = uptr->flags | UNIT_SXC; /* ser exc */ + PUTP32 (pkt, RW_BCL, i); /* adj bc */ + tq_mot_end (uptr, 0, ST_CMP, tbc); + return SCPE_OK; /* exit */ + } + } /* end for */ + } /* end if compare */ + PUTP32 (pkt, RW_BCL, wbc); /* bytes read/cmp'd */ + break; + + case OP_WR: /* write */ + if (t = Map_ReadB (ba, bc, tqxb)) { /* fetch buf, nxm? */ + PUTP32 (pkt, RW_BCL, 0); /* no bytes xfer'd */ + if (tq_hbe (uptr, ba + bc - t)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_HST | SB_HST_NXM, bc); + return SCPE_OK; /* end else wr */ + } + if (sim_tape_wrrecf (uptr, tqxb, bc)) /* write rec fwd, err? */ + return tq_mot_err (uptr, bc); /* log, end */ + uptr->objp = uptr->objp + 1; /* upd obj pos */ + if (TEST_EOT (uptr)) /* EOT on write? */ + uptr->flags = uptr->flags | UNIT_SXC; + uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */ + tbc = bc; /* RW_BC is ok */ + break; + + case OP_WTM: /* write tape mark */ + if (sim_tape_wrtmk (uptr)) /* write tmk, err? */ + return tq_mot_err (uptr, 0); /* log, end */ + uptr->objp = uptr->objp + 1; /* incr obj cnt */ + case OP_ERG: /* erase gap */ + if (TEST_EOT (uptr)) /* EOT on write? */ + uptr->flags = uptr->flags | UNIT_SXC; + uptr->flags = uptr->flags & ~UNIT_TMK; /* disable LEOT */ + break; + + case OP_ERS: /* erase */ + if (sim_tape_wreom (uptr)) /* write eom, err? */ + return tq_mot_err (uptr, 0); /* log, end */ + sim_tape_rewind (uptr); /* rewind */ + uptr->objp = 0; + uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); + break; + + case OP_POS: /* position */ + sktmk = skrec = 0; /* clr skipped */ + if (mdf & MD_RWD) { /* rewind? */ + sim_tape_rewind (uptr); + uptr->objp = 0; /* clr flags */ + uptr->flags = uptr->flags & ~(UNIT_TMK | UNIT_POL); + } + if (mdf & MD_OBC) { /* skip obj? */ + if (mdf & MD_REV) /* reverse? */ + sts = tq_spacer (uptr, nrec, &skrec, FALSE); + else sts = tq_spacef (uptr, nrec, &skrec, FALSE); + } + else { /* skip tmk, rec */ + if (mdf & MD_REV) sts = tq_skipfr (uptr, ntmk, &sktmk); + else sts = tq_skipff (uptr, ntmk, &sktmk); + if (sts == ST_SUC) { /* tmk succeed? */ + if (mdf & MD_REV) /* reverse? */ + sts = tq_spacer (uptr, nrec, &skrec, TRUE); + else sts = tq_spacef (uptr, nrec, &skrec, TRUE); + if (sts == ST_TMK) sktmk = sktmk + 1; + } + } + PUTP32 (pkt, POS_RCL, skrec); /* #rec skipped */ + PUTP32 (pkt, POS_TMCL, sktmk); /* #tmk skipped */ + break; + + default: + return SCPE_IERR; + } + +tq_mot_end (uptr, 0, sts, tbc); /* done */ +return SCPE_OK; +} + +/* Motion command drive error */ + +t_stat tq_mot_err (UNIT *uptr, uint32 rsiz) +{ +uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_TMK; /* serious exception */ +if (tq_dte (uptr, ST_DRV)) /* post err log */ + tq_mot_end (uptr, EF_LOG, ST_DRV, rsiz); /* if ok, report err */ +perror ("TQ I/O error"); +clearerr (uptr->fileref); +return SCPE_IOERR; +} + +/* Motion command complete */ + +t_bool tq_mot_end (UNIT *uptr, uint32 flg, uint32 sts, uint32 rsiz) +{ +int32 pkt = uptr->cpkt; /* packet */ +uint32 cmd = GETP (pkt, CMD_OPC, OPC); /* get cmd */ +uint32 lnt = RW_LNT_T; /* assume rw */ + +if (cmd == OP_ERG) lnt = ERG_LNT; /* set pkt lnt */ +else if (cmd == OP_ERS) lnt = ERS_LNT; +else if (cmd == OP_WTM) lnt = WTM_LNT; +else if (cmd == OP_POS) lnt = POS_LNT; + +uptr->cpkt = 0; /* done */ +if (lnt > ERG_LNT) { /* xfer cmd? */ + PUTP32 (pkt, RW_POSL, uptr->objp); /* position */ + PUTP32 (pkt, RW_RSZL, rsiz); /* record size */ + } +tq_putr (pkt, cmd | OP_END, flg | tq_efl (uptr), sts, lnt, UQ_TYP_SEQ); +if (!tq_putpkt (pkt, TRUE)) return ERR; /* send pkt */ +if (uptr->pktq) /* more to do? */ + sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate thread */ +return OK; +} + +/* Tape motion routines */ + +uint32 tq_map_status (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_OK: + break; + + case MTSE_UNATT: + uptr->flags = uptr->flags | UNIT_SXC; + return (ST_OFL | SB_OFL_NV); + + case MTSE_FMT: + uptr->flags = uptr->flags | UNIT_SXC; + return ST_MFE; + + case MTSE_TMK: + uptr->flags = uptr->flags | UNIT_SXC; + return ST_TMK; + + case MTSE_INVRL: + uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; + return ST_FMT; + + case MTSE_RECE: + case MTSE_IOERR: + uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; + return ST_DRV; + + case MTSE_EOM: + uptr->flags = uptr->flags | UNIT_SXC | UNIT_POL; + return ST_DAT; + + case MTSE_BOT: + uptr->flags = (uptr->flags | UNIT_SXC) & ~UNIT_POL; + return ST_BOT; + + case MTSE_WRP: + uptr->flags = uptr->flags | UNIT_SXC; + return ST_WPR; + } + +return ST_SUC; +} + +uint32 tq_spacef (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec) +{ +t_stat st; +t_mtrlnt tbc; + +*skipped = 0; +while (*skipped < cnt) { /* loop */ + st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */ + if ((st != MTSE_OK) && (st != MTSE_TMK)) /* real error? */ + return tq_map_status (uptr, st); /* map status */ + uptr->objp = uptr->objp + 1; /* upd obj cnt */ + if (st == MTSE_TMK) { /* tape mark? */ + int32 pkt = uptr->cpkt; /* get pkt */ + if ((tq_pkt[pkt].d[CMD_MOD] & MD_DLE) && /* LEOT? */ + (uptr->flags & UNIT_TMK)) { + sim_tape_sprecr (uptr, &tbc); /* rev over tmk */ + uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ + return ST_LED; + } + uptr->flags = uptr->flags | UNIT_TMK; /* set TM seen */ + if (qrec) return ST_TMK; /* rec spc? stop */ + } + else uptr->flags = uptr->flags & ~UNIT_TMK; /* clr TM seen */ + *skipped = *skipped + 1; /* # obj skipped */ + } +return ST_SUC; +} + +uint32 tq_skipff (UNIT *uptr, uint32 cnt, uint32 *skipped) +{ +uint32 st, skrec; + +*skipped = 0; +while (*skipped < cnt) { /* loop */ + st = tq_spacef (uptr, 0x7FFFFFFF, &skrec, TRUE); /* rec spc fwd */ + if (st == ST_TMK) *skipped = *skipped + 1; /* count files */ + else if (st != ST_SUC) return st; + } +return ST_SUC; +} + +uint32 tq_spacer (UNIT *uptr, uint32 cnt, uint32 *skipped, t_bool qrec) +{ +t_stat st; +t_mtrlnt tbc; + +*skipped = 0; +while (*skipped < cnt) { /* loop */ + st = sim_tape_sprecr (uptr, &tbc); /* spc rec rev */ + if ((st != MTSE_OK) && (st != MTSE_TMK)) /* real error? */ + return tq_map_status (uptr, st); /* map status */ + uptr->objp = uptr->objp - 1; /* upd obj cnt */ + if ((st == MTSE_TMK) && qrec) return ST_TMK; /* tape mark, stop? */ + *skipped = *skipped + 1; /* # obj skipped */ + } +return ST_SUC; +} + +uint32 tq_skipfr (UNIT *uptr, uint32 cnt, uint32 *skipped) +{ +uint32 st, skrec; + +*skipped = 0; +while (*skipped < cnt) { /* loopo */ + st = tq_spacer (uptr, 0x7FFFFFFF, &skrec, TRUE); /* rec spc rev */ + if (st == ST_TMK) *skipped = *skipped + 1; /* tape mark? */ + else if (st != 0) return st; /* error? */ + } +return ST_SUC; +} + +/* Read buffer - can return ST_TMK, ST_FMT, or ST_DRV */ + +uint32 tq_rdbuff (UNIT *uptr, t_mtrlnt *tbc) +{ +t_stat st; + +st = sim_tape_rdrecf (uptr, tqxb, tbc, MT_MAXFR); /* read rec fwd */ +if (st == MTSE_TMK) { /* tape mark? */ + uptr->flags = uptr->flags | UNIT_SXC | UNIT_TMK; /* serious exc */ + uptr->objp = uptr->objp + 1; /* update obj cnt */ + return ST_TMK; + } +if (st != MTSE_OK) return tq_map_status (uptr, st); /* other error? */ +uptr->flags = uptr->flags & ~UNIT_TMK; /* clr tape mark */ +uptr->objp = uptr->objp + 1; /* upd obj cnt */ +return ST_SUC; +} + +uint32 tq_rdbufr (UNIT *uptr, t_mtrlnt *tbc) +{ +t_stat st; + +st = sim_tape_rdrecr (uptr, tqxb, tbc, MT_MAXFR); /* read rec rev */ +if (st == MTSE_TMK) { /* tape mark? */ + uptr->flags = uptr->flags | UNIT_SXC; /* serious exc */ + uptr->objp = uptr->objp - 1; /* update obj cnt */ + return ST_TMK; + } +if (st != MTSE_OK) return tq_map_status (uptr, st); /* other error? */ +uptr->objp = uptr->objp - 1; /* upd obj cnt */ +return ST_SUC; +} + +/* Data transfer error log packet */ + +t_bool tq_dte (UNIT *uptr, uint32 err) +{ +int32 pkt, tpkt; +uint32 lu; + +if ((tq_cflgs & CF_THS) == 0) return OK; /* logging? */ +if (!tq_deqf (&pkt)) return ERR; /* get log pkt */ +tpkt = uptr->cpkt; /* rw pkt */ +lu = tq_pkt[tpkt].d[CMD_UN]; /* unit # */ + +tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_UN] = lu; /* copy unit */ +tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */ +tq_pkt[pkt].d[DTE_CIDA] = 0; /* ctrl ID */ +tq_pkt[pkt].d[DTE_CIDB] = 0; +tq_pkt[pkt].d[DTE_CIDC] = 0; +tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD); +tq_pkt[pkt].d[DTE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */ +tq_pkt[pkt].d[DTE_MLUN] = lu; /* MLUN */ +tq_pkt[pkt].d[DTE_UIDA] = lu; /* unit ID */ +tq_pkt[pkt].d[DTE_UIDB] = 0; +tq_pkt[pkt].d[DTE_UIDC] = 0; +tq_pkt[pkt].d[DTE_UIDD] = (UID_TAPE << DTE_UIDD_V_CLS) | + (drv_tab[tq_typ].umod << DTE_UIDD_V_MOD); +tq_pkt[pkt].d[DTE_UVER] = drv_tab[tq_typ].uver; /* unit ver */ +PUTP32 (pkt, DTE_POSL, uptr->objp); /* position */ +tq_pkt[pkt].d[DTE_FVER] = drv_tab[tq_typ].fver; /* fmtr ver */ +tq_putr (pkt, FM_TAP, LF_SNR, err, DTE_LNT, UQ_TYP_DAT); +return tq_putpkt (pkt, TRUE); +} + +/* Host bus error log packet */ + +t_bool tq_hbe (UNIT *uptr, uint32 ba) +{ +int32 pkt, tpkt; + +if ((tq_cflgs & CF_THS) == 0) return OK; /* logging? */ +if (!tq_deqf (&pkt)) return ERR; /* get log pkt */ +tpkt = uptr->cpkt; /* rw pkt */ +tq_pkt[pkt].d[ELP_REFL] = tq_pkt[tpkt].d[CMD_REFL]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_REFH] = tq_pkt[tpkt].d[CMD_REFH]; /* copy cmd ref */ +tq_pkt[pkt].d[ELP_UN] = tq_pkt[tpkt].d[CMD_UN]; /* copy unit */ +tq_pkt[pkt].d[ELP_SEQ] = 0; /* clr seq # */ +tq_pkt[pkt].d[HBE_CIDA] = 0; /* ctrl ID */ +tq_pkt[pkt].d[HBE_CIDB] = 0; +tq_pkt[pkt].d[HBE_CIDC] = 0; +tq_pkt[pkt].d[DTE_CIDD] = (TQ_CLASS << DTE_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << DTE_CIDD_V_MOD); +tq_pkt[pkt].d[HBE_VER] = drv_tab[tq_typ].cver; /* ctrl ver */ +tq_pkt[pkt].d[HBE_RSV] = 0; +PUTP32 (pkt, HBE_BADL, ba); /* bad addr */ +tq_putr (pkt, FM_BAD, LF_SNR, ST_HST | SB_HST_NXM, HBE_LNT, UQ_TYP_DAT); +return tq_putpkt (pkt, TRUE); +} + +/* Port last failure error log packet */ + +t_bool tq_plf (uint32 err) +{ +int32 pkt; + +if (!tq_deqf (&pkt)) return ERR; /* get log pkt */ +tq_pkt[pkt].d[ELP_REFL] = tq_pkt[pkt].d[ELP_REFH] = 0; /* ref = 0 */ +tq_pkt[pkt].d[ELP_UN] = tq_pkt[pkt].d[ELP_SEQ] = 0; /* no unit, seq */ +tq_pkt[pkt].d[PLF_CIDA] = 0; /* cntl ID */ +tq_pkt[pkt].d[PLF_CIDB] = 0; +tq_pkt[pkt].d[PLF_CIDC] = 0; +tq_pkt[pkt].d[PLF_CIDD] = (TQ_CLASS << PLF_CIDD_V_CLS) | + (drv_tab[tq_typ].cmod << PLF_CIDD_V_MOD); +tq_pkt[pkt].d[PLF_VER] = drv_tab[tq_typ].cver; +tq_pkt[pkt].d[PLF_ERR] = err; +tq_putr (pkt, FM_CNT, LF_SNR, ST_CNT, PLF_LNT, UQ_TYP_DAT); +tq_pkt[pkt].d[UQ_HCTC] |= (UQ_CID_DIAG << UQ_HCTC_V_CID); +return tq_putpkt (pkt, TRUE); +} + +/* Unit now available attention packet */ + +int32 tq_una (UNIT *uptr) +{ +int32 pkt; +uint32 lu; + +if (!tq_deqf (&pkt)) return ERR; /* get log pkt */ +lu = (uint32) (uptr - tq_dev.units); /* get unit */ +tq_pkt[pkt].d[RSP_REFL] = tq_pkt[pkt].d[RSP_REFH] = 0; /* ref = 0 */ +tq_pkt[pkt].d[RSP_UN] = lu; +tq_pkt[pkt].d[RSP_RSV] = 0; +tq_putr_unit (pkt, uptr, lu, FALSE); /* fill unit fields */ +tq_putr (pkt, OP_AVA, 0, 0, UNA_LNT, UQ_TYP_SEQ); /* fill std fields */ +return tq_putpkt (pkt, TRUE); +} + +/* List handling + + tq_deqf - dequeue head of free list (fatal err if none) + tq_deqh - dequeue head of list + tq_enqh - enqueue at head of list + tq_enqt - enqueue at tail of list +*/ + +t_bool tq_deqf (int32 *pkt) +{ +if (tq_freq == 0) return tq_fatal (PE_NSR); /* no free pkts?? */ +tq_pbsy = tq_pbsy + 1; /* cnt busy pkts */ +*pkt = tq_freq; /* head of list */ +tq_freq = tq_pkt[tq_freq].link; /* next */ +return OK; +} + +int32 tq_deqh (int32 *lh) +{ +int32 ptr = *lh; /* head of list */ + +if (ptr) *lh = tq_pkt[ptr].link; /* next */ +return ptr; +} + +void tq_enqh (int32 *lh, int32 pkt) +{ +if (pkt == 0) return; /* any pkt? */ +tq_pkt[pkt].link = *lh; /* link is old lh */ +*lh = pkt; /* pkt is new lh */ +return; +} + +void tq_enqt (int32 *lh, int32 pkt) +{ +if (pkt == 0) return; /* any pkt? */ +tq_pkt[pkt].link = 0; /* it will be tail */ +if (*lh == 0) *lh = pkt; /* if empty, enqh */ +else { + uint32 ptr = *lh; /* chase to end */ + while (tq_pkt[ptr].link) ptr = tq_pkt[ptr].link; + tq_pkt[ptr].link = pkt; /* enq at tail */ + } +return; +} + +/* Packet and descriptor handling */ + +/* Get packet from command ring */ + +t_bool tq_getpkt (int32 *pkt) +{ +uint32 addr, desc; + +if (!tq_getdesc (&tq_cq, &desc)) return ERR; /* get cmd desc */ +if ((desc & UQ_DESC_OWN) == 0) { /* none */ + *pkt = 0; /* pkt = 0 */ + return OK; /* no error */ + } +if (!tq_deqf (pkt)) return ERR; /* get cmd pkt */ +tq_hat = 0; /* dsbl hst timer */ +addr = desc & UQ_ADDR; /* get Q22 addr */ +if (Map_ReadW (addr + UQ_HDR_OFF, TQ_PKT_SIZE, tq_pkt[*pkt].d)) + return tq_fatal (PE_PRE); /* read pkt */ +return tq_putdesc (&tq_cq, desc); /* release desc */ +} + +/* Put packet to response ring - note the clever hack about credits. + The controller sends all its credits to the host. Thereafter, it + supplies one credit for every response packet sent over. Simple! +*/ + +t_bool tq_putpkt (int32 pkt, t_bool qt) +{ +uint32 addr, desc, lnt, cr; + +if (pkt == 0) return OK; /* any packet? */ +if (DEBUG_PRS (tq_dev)) { + UNIT *up = tq_getucb (tq_pkt[pkt].d[CMD_UN]); + fprintf (sim_deb, ">>TQ: rsp=%04X, sts=%04X", + tq_pkt[pkt].d[RSP_OPF], tq_pkt[pkt].d[RSP_STS]); + if (up) fprintf (sim_deb, ", pos=%d, obj=%d\n", up->pos, up->objp); + else fprintf (sim_deb, "\n"); + fflush (sim_deb); + } +if (!tq_getdesc (&tq_rq, &desc)) return ERR; /* get rsp desc */ +if ((desc & UQ_DESC_OWN) == 0) { /* not valid? */ + if (qt) tq_enqt (&tq_rspq, pkt); /* normal? q tail */ + else tq_enqh (&tq_rspq, pkt); /* resp q call */ + sim_activate (&tq_unit[TQ_QUEUE], tq_qtime); /* activate q thrd */ + return OK; + } +addr = desc & UQ_ADDR; /* get Q22 addr */ +lnt = tq_pkt[pkt].d[UQ_HLNT] - UQ_HDR_OFF; /* size, with hdr */ +if ((GETP (pkt, UQ_HCTC, TYP) == UQ_TYP_SEQ) && /* seq packet? */ + (GETP (pkt, CMD_OPC, OPC) & OP_END)) { /* end packet? */ + cr = (tq_credits >= 14)? 14: tq_credits; /* max 14 credits */ + tq_credits = tq_credits - cr; /* decr credits */ + tq_pkt[pkt].d[UQ_HCTC] |= ((cr + 1) << UQ_HCTC_V_CR); + } +if (Map_WriteW (addr + UQ_HDR_OFF, lnt, tq_pkt[pkt].d)) + return tq_fatal (PE_PWE); /* write pkt */ +tq_enqh (&tq_freq, pkt); /* pkt is free */ +tq_pbsy = tq_pbsy - 1; /* decr busy cnt */ +if (tq_pbsy == 0) tq_hat = tq_htmo; /* idle? strt hst tmr */ +return tq_putdesc (&tq_rq, desc); /* release desc */ +} + +/* Get a descriptor from the host */ + +t_bool tq_getdesc (struct uq_ring *ring, uint32 *desc) +{ +uint32 addr = ring->ba + ring->idx; +uint16 d[2]; + +if (Map_ReadW (addr, 4, d)) /* fetch desc */ + return tq_fatal (PE_QRE); /* err? dead */ +*desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); +return OK; +} + +/* Return a descriptor to the host, clearing owner bit + If rings transitions from "empty" to "not empty" or "full" to + "not full", and interrupt bit was set, interrupt the host. + Actually, test whether previous ring entry was owned by host. +*/ + +t_bool tq_putdesc (struct uq_ring *ring, uint32 desc) +{ +uint32 prvd, newd = (desc & ~UQ_DESC_OWN) | UQ_DESC_F; +uint32 prva, addr = ring->ba + ring->idx; +uint16 d[2]; + +d[0] = newd & 0xFFFF; /* 32b to 16b */ +d[1] = (newd >> 16) & 0xFFFF; +if (Map_WriteW (addr, 4, d)) /* store desc */ + return tq_fatal (PE_QWE); /* err? dead */ +if (desc & UQ_DESC_F) { /* was F set? */ + if (ring->lnt <= 4) tq_ring_int (ring); /* lnt = 1? intr */ + else { + prva = ring->ba + /* prv desc */ + ((ring->idx - 4) & (ring->lnt - 1)); + if (Map_ReadW (prva, 4, d)) /* read prv */ + return tq_fatal (PE_QRE); + prvd = ((uint32) d[0]) | (((uint32) d[1]) << 16); + if (prvd & UQ_DESC_OWN) tq_ring_int (ring); + } + } +ring->idx = (ring->idx + 4) & (ring->lnt - 1); +return OK; +} + +/* Get unit descriptor for logical unit - trivial now, + but eventually, hide multiboard complexities here */ + +UNIT *tq_getucb (uint32 lu) +{ +UNIT *uptr; + +if (lu >= TQ_NUMDR) return NULL; +uptr = tq_dev.units + lu; +if (uptr->flags & UNIT_DIS) return NULL; +return uptr; +} + +/* Hack unit flags */ + +void tq_setf_unit (int32 pkt, UNIT *uptr) +{ +uptr->uf = tq_pkt[pkt].d[ONL_UFL] & UF_MSK; /* settable flags */ +if ((tq_pkt[pkt].d[CMD_MOD] & MD_SWP) && /* swre wrp enb? */ + (tq_pkt[pkt].d[ONL_UFL] & UF_WPS)) /* swre wrp on? */ + uptr->uf = uptr->uf | UF_WPS; /* simon says... */ +return; +} + +/* Hack end flags */ + +uint32 tq_efl (UNIT *uptr) +{ +uint32 t = 0; + +if (uptr) { /* any unit? */ + if (uptr->flags & UNIT_POL) t = t | EF_PLS; /* note pos lost */ + if (uptr->flags & UNIT_SXC) t = t | EF_SXC; /* note ser exc */ + if (TEST_EOT (uptr)) t = t | EF_EOT; /* note EOT */ + } +return t; +} + +/* Unit response fields */ + +void tq_putr_unit (int32 pkt, UNIT *uptr, uint32 lu, t_bool all) +{ +tq_pkt[pkt].d[ONL_MLUN] = lu; /* multi-unit */ +tq_pkt[pkt].d[ONL_UFL] = uptr->uf | TQ_WPH (uptr); /* unit flags */ +tq_pkt[pkt].d[ONL_RSVL] = tq_pkt[pkt].d[ONL_RSVH] = 0; /* reserved */ +tq_pkt[pkt].d[ONL_UIDA] = lu; /* UID low */ +tq_pkt[pkt].d[ONL_UIDB] = 0; +tq_pkt[pkt].d[ONL_UIDC] = 0; +tq_pkt[pkt].d[ONL_UIDD] = (UID_TAPE << ONL_UIDD_V_CLS) | + (drv_tab[tq_typ].umod << ONL_UIDD_V_MOD); /* UID hi */ +PUTP32 (pkt, ONL_MEDL, drv_tab[tq_typ].med); /* media type */ +if (all) { /* if long form */ + tq_pkt[pkt].d[ONL_FMT] = drv_tab[tq_typ].fmt; /* format */ + tq_pkt[pkt].d[ONL_SPD] = 0; /* speed */ + PUTP32 (pkt, ONL_MAXL, TQ_MAXFR); /* max xfr */ + tq_pkt[pkt].d[ONL_NREC] = 0; /* noise rec */ + tq_pkt[pkt].d[ONL_RSVE] = 0; /* reserved */ + } +return; +} + +/* UQ_HDR and RSP_OP fields */ + +void tq_putr (int32 pkt, uint32 cmd, uint32 flg, uint32 sts, uint32 lnt, uint32 typ) +{ +tq_pkt[pkt].d[RSP_OPF] = (cmd << RSP_OPF_V_OPC) | /* set cmd, flg */ + (flg << RSP_OPF_V_FLG); +tq_pkt[pkt].d[RSP_STS] = sts; +tq_pkt[pkt].d[UQ_HLNT] = lnt; /* length */ +tq_pkt[pkt].d[UQ_HCTC] = (typ << UQ_HCTC_V_TYP) | /* type, cid */ + (UQ_CID_TMSCP << UQ_HCTC_V_CID); /* clr credits */ +return; +} + +/* Post interrupt during init */ + +void tq_init_int (void) +{ +if ((tq_s1dat & SA_S1H_IE) && tq_dib.vec) SET_INT (TQ); +return; +} + +/* Post interrupt during putpkt - note that NXMs are ignored! */ + +void tq_ring_int (struct uq_ring *ring) +{ +uint32 iadr = tq_comm + ring->ioff; /* addr intr wd */ +uint16 flag = 1; + +Map_WriteW (iadr, 2, &flag); /* write flag */ +if (tq_dib.vec) SET_INT (TQ); /* if enb, intr */ +return; +} + +/* Return interrupt vector */ + +int32 tq_inta (void) +{ +return tq_dib.vec; /* prog vector */ +} + +/* Fatal error */ + +t_bool tq_fatal (uint32 err) +{ +if (DEBUG_PRS (tq_dev)) fprintf (sim_deb, ">>TQ: fatal err=%X\n", err); +tq_reset (&tq_dev); /* reset device */ +tq_sa = SA_ER | err; /* SA = dead code */ +tq_csta = CST_DEAD; /* state = dead */ +tq_perr = err; /* save error */ +return ERR; +} + +/* Device attach */ + +t_stat tq_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +if (tq_csta == CST_UP) uptr->flags = (uptr->flags | UNIT_ATP) & + ~(UNIT_SXC | UNIT_POL | UNIT_TMK); +return SCPE_OK; +} + +/* Device detach */ + +t_stat tq_detach (UNIT *uptr) +{ +t_stat r; + +r = sim_tape_detach (uptr); /* detach unit */ +if (r != SCPE_OK) return r; +uptr->flags = uptr->flags & ~(UNIT_ONL | UNIT_ATP | UNIT_SXC | UNIT_POL | UNIT_TMK); +uptr->uf = 0; /* clr unit flgs */ +return SCPE_OK; +} + +/* Device reset */ + +t_stat tq_reset (DEVICE *dptr) +{ +int32 i, j; +UNIT *uptr; + +tq_csta = CST_S1; /* init stage 1 */ +tq_s1dat = 0; /* no S1 data */ +tq_dib.vec = 0; /* no vector */ +if (UNIBUS) tq_sa = SA_S1 | SA_S1C_DI | SA_S1C_MP; /* Unibus? */ +else tq_sa = SA_S1 | SA_S1C_Q22 | SA_S1C_DI | SA_S1C_MP; /* init SA val */ +tq_cflgs = CF_RPL; /* ctrl flgs off */ +tq_htmo = TQ_DHTMO; /* default timeout */ +tq_hat = tq_htmo; /* default timer */ +tq_cq.ba = tq_cq.lnt = tq_cq.idx = 0; /* clr cmd ring */ +tq_rq.ba = tq_rq.lnt = tq_rq.idx = 0; /* clr rsp ring */ +tq_credits = (TQ_NPKTS / 2) - 1; /* init credits */ +tq_freq = 1; /* init free list */ +for (i = 0; i < TQ_NPKTS; i++) { /* all pkts free */ + if (i) tq_pkt[i].link = (i + 1) & TQ_M_NPKTS; + else tq_pkt[i].link = 0; + for (j = 0; j < TQ_PKT_SIZE_W; j++) tq_pkt[i].d[j] = 0; + } +tq_rspq = 0; /* no q'd rsp pkts */ +tq_pbsy = 0; /* all pkts free */ +tq_pip = 0; /* not polling */ +CLR_INT (TQ); /* clr intr req */ +for (i = 0; i < TQ_NUMDR + 2; i++) { /* init units */ + uptr = tq_dev.units + i; + sim_cancel (uptr); /* clr activity */ + sim_tape_reset (uptr); + uptr->flags = uptr->flags & /* not online */ + ~(UNIT_ONL|UNIT_ATP|UNIT_SXC|UNIT_POL|UNIT_TMK); + uptr->uf = 0; /* clr unit flags */ + uptr->cpkt = uptr->pktq = 0; /* clr pkt q's */ + } +if (tqxb == NULL) tqxb = (uint8 *) calloc (TQ_MAXFR, sizeof (uint8)); +if (tqxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Device bootstrap */ + +#if defined (VM_PDP11) + +#define BOOT_START 016000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 014) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +/* Data structure definitions */ + +#define B_CMDINT (BOOT_START - 01000) /* cmd int */ +#define B_RSPINT (B_CMDINT + 002) /* rsp int */ +#define B_RING (B_RSPINT + 002) /* ring base */ +#define B_RSPH (B_RING + 010) /* resp pkt hdr */ +#define B_TKRSP (B_RSPH + 004) /* resp pkt */ +#define B_CMDH (B_TKRSP + 060) /* cmd pkt hdr */ +#define B_TKCMD (B_CMDH + 004) /* cmd pkt */ +#define B_UNIT (B_TKCMD + 004) /* unit # */ + +static const uint16 boot_rom[] = { + + 0046525, /* ST: "UM" */ + + 0012706, 0016000, /* mov #st,sp */ + 0012700, 0000000, /* mov #unitno,r0 */ + 0012701, 0174500, /* mov #174500,r1 ; ip addr */ + 0005021, /* clr (r1)+ ; init */ + 0012704, 0004000, /* mov #4000,r4 ; s1 mask */ + 0005002, /* clr r2 */ + 0005022, /* 10$: clr (r2)+ ; clr up to boot */ + 0020237, BOOT_START - 2, /* cmp r2,#st-2 */ + 0103774, /* blo 10$ */ + 0012705, BOOT_START+0312, /* mov #cmdtbl,r5 ; addr of tbl */ + + /* Four step init process */ + + 0005711, /* 20$: tst (r1) ; err? */ + 0100001, /* bpl 30$ */ + 0000000, /* halt */ + 0030411, /* 30$: bit r4,(r1) ; step set? */ + 0001773, /* beq 20$ ; wait */ + 0012511, /* mov (r5)+,(r1) ; send next */ + 0006304, /* asl r4 ; next mask */ + 0100370, /* bpl 20$ ; s4 done? */ + + /* Set up rings, issue ONLINE, REWIND, READ */ + + 0012737, 0000400, B_CMDH + 2, /* mov #400,cmdh+2 ; VCID = 1 */ + 0012737, 0000044, B_CMDH, /* mov #36.,cmdh ; cmd pkt lnt */ + 0010037, B_UNIT, /* mov r0,unit ; unit # */ + 0012737, 0000011, B_TKCMD + 8, /* mov #11,tkcmd+8. ; online op */ + 0012737, 0020000, B_TKCMD + 10, /* mov #20000,tkcmd+10. ; clr ser ex */ + 0012702, B_RING, /* mov #ring,r2 ; init rings */ + 0012722, B_TKRSP, /* mov #tkrsp,(r2)+ ; rsp pkt addr */ + 0010203, /* mov r2,r3 ; save ring+2 */ + 0010423, /* mov r4,(r3)+ ; set TK own */ + 0012723, B_TKCMD, /* mov #tkcmd,(r3)+ ; cmd pkt addr */ + 0010423, /* mov r4,(r3)+ ; set TK own */ + 0005741, /* tst -(r1) ; start poll */ + 0005712, /* 40$: tst (r2) ; wait for resp */ + 0100776, /* bmi 40$ */ + 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ + 0001401, /* beq 50$ */ + 0000000, /* halt */ + 0012703, B_TKCMD + 8, /* 50$: mov #tkcmd+8.,r3 */ + 0012723, 0000045, /* mov #45,(r3)+ ; reposition */ + 0012723, 0020002, /* mov #20002,(r3)+ ; rew, clr exc */ + 0012723, 0000001, /* mov #1,(r3)+ ; lo rec skp */ + 0005023, /* clr (r3)+ ; hi rec skp */ + 0005023, /* clr (r3)+ ; lo tmk skp */ + 0005023, /* clr (r3)+ ; hi tmk skp */ + 0010412, /* mov r4,(r2) ; TK own rsp */ + 0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */ + 0005711, /* tst (r1) ; start poll */ + 0005712, /* 60$: tst (r2) ; wait for resp */ + 0100776, /* bmi 60$ */ + 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ + 0001401, /* beq 70$ */ + 0000000, /* halt */ + 0012703, B_TKCMD + 8, /* 70$: mov #tkcmd+8.,r3 */ + 0012723, 0000041, /* mov #41,(r3)+ ; read */ + 0012723, 0020000, /* mov #20000,(r3)+ ; clr exc */ + 0012723, 0001000, /* mov #512.,(r3)+ ; bc = 512 */ + 0005023, /* clr (r3)+ ; clr args */ + 0005023, /* clr (r3)+ ; ba = 0 */ + 0010412, /* mov r4,(r2) ; TK own rsp */ + 0010437, B_RING + 6, /* mov r4,ring+6 ; TK own cmd */ + 0005711, /* tst (r1) ; start poll */ + 0005712, /* 80$: tst (r2) ; wait for resp */ + 0100776, /* bmi 80$ */ + 0105737, B_TKRSP + 10, /* tstb tkrsp+10. ; check stat */ + 0001401, /* beq 90$ */ + 0000000, /* halt */ + + /* Boot block read in, jump to 0 - leave controller init'd */ + + 0005003, /* clr r3 */ + 0012704, BOOT_START+020, /* mov #st+020,r4 */ + 0005005, /* clr r5 */ + 0005007, /* clr pc */ + + 0100000, /* cmdtbl: init step 1 */ + B_RING, /* ring base */ + 0000000, /* high ring base */ + 0000001 /* go */ + }; + +t_stat tq_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & 3; +M[BOOT_CSR >> 1] = tq_dib.ba & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat tq_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} + +#endif + +/* Special show commands */ + +void tq_show_ring (FILE *st, struct uq_ring *rp) +{ +uint32 i, desc; +uint16 d[2]; + +#if defined (VM_PDP11) +fprintf (st, "ring, base = %o, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#else +fprintf (st, "ring, base = %x, index = %d, length = %d\n", + rp->ba, rp->idx >> 2, rp->lnt >> 2); +#endif +for (i = 0; i < (rp->lnt >> 2); i++) { + if (Map_ReadW (rp->ba + (i << 2), 4, d)) { + fprintf (st, " %3d: non-existent memory\n", i); + break; + } + desc = ((uint32) d[0]) | (((uint32) d[1]) << 16); +#if defined (VM_PDP11) + fprintf (st, " %3d: %011o\n", i, desc); +#else + fprintf (st, " %3d: %08x\n", i, desc); +#endif + } +return; +} + +void tq_show_pkt (FILE *st, int32 pkt) +{ +int32 i, j; +uint32 cr = GETP (pkt, UQ_HCTC, CR); +uint32 typ = GETP (pkt, UQ_HCTC, TYP); +uint32 cid = GETP (pkt, UQ_HCTC, CID); + +fprintf (st, "packet %d, credits = %d, type = %d, cid = %d\n", + pkt, cr, typ, cid); +for (i = 0; i < TQ_SH_MAX; i = i + TQ_SH_PPL) { + fprintf (st, " %2d:", i); + for (j = i; j < (i + TQ_SH_PPL); j++) +#if defined (VM_PDP11) + fprintf (st, " %06o", tq_pkt[pkt].d[j]); +#else + fprintf (st, " %04x", tq_pkt[pkt].d[j]); +#endif + fprintf (st, "\n"); + } +return; +} + +t_stat tq_show_unitq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 pkt, u = uptr - tq_dev.units; + +if (tq_csta != CST_UP) { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } +if ((uptr->flags & UNIT_ONL) == 0) { + if (uptr->flags & UNIT_ATT) + fprintf (st, "Unit %d is available\n", u); + else fprintf (st, "Unit %d is offline\n", u); + return SCPE_OK; + } +if (uptr->cpkt) { + fprintf (st, "Unit %d current ", u); + tq_show_pkt (st, uptr->cpkt); + if (pkt = uptr->pktq) { + do { + fprintf (st, "Unit %d queued ", u); + tq_show_pkt (st, pkt); + } while (pkt = tq_pkt[pkt].link); + } + } +else fprintf (st, "Unit %d queues are empty\n", u); +return SCPE_OK; +} + +t_stat tq_show_ctrl (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, pkt; + +if (tq_csta != CST_UP) { + fprintf (st, "Controller is not initialized\n"); + return SCPE_OK; + } +if (val & TQ_SH_RI) { + if (tq_pip) fprintf (st, "Polling in progress, host timer = %d\n", tq_hat); + else fprintf (st, "Host timer = %d\n", tq_hat); + fprintf (st, "Command "); + tq_show_ring (st, &tq_cq); + fprintf (st, "Response "); + tq_show_ring (st, &tq_rq); + } +if (val & TQ_SH_FR) { + if (pkt = tq_freq) { + for (i = 0; pkt != 0; i++, pkt = tq_pkt[pkt].link) { + if (i == 0) fprintf (st, "Free queue = %d", pkt); + else if ((i % 16) == 0) fprintf (st, ",\n %d", pkt); + else fprintf (st, ", %d", pkt); + } + fprintf (st, "\n"); + } + else fprintf (st, "Free queue is empty\n"); + } +if (val & TQ_SH_RS) { + if (pkt = tq_rspq) { + do { + fprintf (st, "Response "); + tq_show_pkt (st, pkt); + } while (pkt = tq_pkt[pkt].link); + } + else fprintf (st, "Response queue is empty\n"); + } +if (val & TQ_SH_UN) { + for (i = 0; i < TQ_NUMDR; i++) + tq_show_unitq (st, &tq_unit[i], 0, NULL); + } +return SCPE_OK; +} + +/* Set controller type (and capacity for user-defined type) */ + +t_stat tq_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i, cap; +uint32 max = sim_taddr_64? TQU_EMAXC: TQU_MAXC; +t_stat r; + +if ((val < 0) || (val > TQU_TYPE) || ((val != TQU_TYPE) && cptr)) + return SCPE_ARG; +for (i = 0; i < TQ_NUMDR; i++) { + if (tq_unit[i].flags & UNIT_ATT) return SCPE_ALATT; + } +if (cptr) { + cap = (uint32) get_uint (cptr, 10, max, &r); + if ((r != SCPE_OK) || (cap < TQU_MINC)) return SCPE_ARG; + drv_tab[TQU_TYPE].cap = ((t_addr) cap) << 20; + } +tq_typ = val; +for (i = 0; i < TQ_NUMDR; i++) + tq_unit[i].capac = drv_tab[tq_typ].cap; +return SCPE_OK; +} + +/* Show controller type and capacity */ + +t_stat tq_show_type (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "%s (%dMB)", drv_tab[tq_typ].name, (uint32) (drv_tab[tq_typ].cap >> 20)); +return SCPE_OK; +} diff --git a/PDP11/pdp11_ts.c b/PDP11/pdp11_ts.c new file mode 100644 index 0000000..9cb8b94 --- /dev/null +++ b/PDP11/pdp11_ts.c @@ -0,0 +1,1126 @@ +/* pdp11_ts.c: TS11/TSV05 magnetic tape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ts TS11/TSV05 magtape + + 16-Feb-06 RMS Added tape capacity checking + 31-Oct-05 RMS Fixed address width for large files + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Jul-05 RMS Removed extraneous externs + 18-Mar-05 RMS Added attached test to detach routine + 07-Dec-04 RMS Added read-only file support + 30-Sep-04 RMS Revised Unibus interface + 25-Jan-04 RMS Revised for device debug support + 19-May-03 RMS Revised for new conditional compilation scheme + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised to use magtape library + 30-Sep-02 RMS Added variable address support to bootstrap + Added vector change/display support + Fixed CTL unload/clean decode + Implemented XS0_MOT in extended status + New data structures, revamped error recovery + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length protection + 04-Apr-02 RMS Fixed bug in residual frame count after space operation + 16-Feb-02 RMS Fixed bug in message header logic + 26-Jan-02 RMS Revised bootstrap to conform to M9312 + 06-Jan-02 RMS Revised enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 09-Nov-01 RMS Added bus map, VAX support + 15-Oct-01 RMS Integrated debug logging across simulator + 27-Sep-01 RMS Implemented extended characteristics and status + Fixed bug in write characteristics status return + 19-Sep-01 RMS Fixed bug in bootstrap + 15-Sep-01 RMS Fixed bug in NXM test + 07-Sep-01 RMS Revised device disable and interrupt mechanism + 13-Jul-01 RMS Fixed bug in space reverse (found by Peter Schorn) + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. + + The TS11 functions in three environments: + + - PDP-11 Q22 systems - the I/O map is one for one, so it's safe to + go through the I/O map + - PDP-11 Unibus 22b systems - the TS11 behaves as an 18b Unibus + peripheral and must go through the I/O map + - VAX Q22 systems - the TS11 must go through the I/O map +*/ + +#if defined (VM_PDP10) /* PDP10 version */ +#error "TS11 not supported on PDP10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +#define TS_DIS 0 /* on by default */ +#define DMASK 0xFFFF + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define TS_DIS DEV_DIS /* off by default */ +extern int32 cpu_opt; +#endif + +#include "sim_tape.h" +#define ADDRTEST (UNIBUS? 0177774: 0177700) + +/* TSBA/TSDB - 17772520: base address/data buffer register + + read: most recent memory address + write word: initiate command + write byte: diagnostic use +*/ + +/* TSSR - 17772522: subsystem status register + TSDBX - 17772523: extended address register + + read: return status + write word: initialize + write byte: if odd, set extended packet address register +*/ + +#define TSSR_SC 0100000 /* special condition */ +#define TSSR_RMR 0010000 /* reg mod refused */ +#define TSSR_NXM 0004000 /* nxm */ +#define TSSR_NBA 0002000 /* need buf addr */ +#define TSSR_V_EMA 8 /* mem addr<17:16> */ +#define TSSR_EMA 0001400 +#define TSSR_SSR 0000200 /* subsystem ready */ +#define TSSR_OFL 0000100 /* offline */ +#define TSSR_V_TC 1 /* term class */ +#define TSSR_M_TC 07 +#define TSSR_TC (TSSR_M_TC << TSSR_V_TC) +#define TC0 (0 << TSSR_V_TC) /* ok */ +#define TC1 (1 << TSSR_V_TC) /* attention */ +#define TC2 (2 << TSSR_V_TC) /* status alert */ +#define TC3 (3 << TSSR_V_TC) /* func reject */ +#define TC4 (4 << TSSR_V_TC) /* retry, moved */ +#define TC5 (5 << TSSR_V_TC) /* retry */ +#define TC6 (6 << TSSR_V_TC) /* pos lost */ +#define TC7 (7 << TSSR_V_TC) /* fatal err */ +#define TSSR_MBZ 0060060 +#define GET_TC(x) (((x) >> TSSR_V_TC) & TSSR_M_TC) + +#define TSDBX_M_XA 017 /* ext addr */ +#define TSDBX_BOOT 0000200 /* boot */ + +/* Command packet offsets */ + +#define CMD_PLNT 4 /* cmd pkt length */ +#define cmdhdr tscmdp[0] /* header */ +#define cmdadl tscmdp[1] /* address low */ +#define cmdadh tscmdp[2] /* address high */ +#define cmdlnt tscmdp[3] /* length */ + +/* Command packet header */ + +#define CMD_ACK 0100000 /* acknowledge */ +#define CMD_CVC 0040000 /* clear vol chk */ +#define CMD_OPP 0020000 /* opposite */ +#define CMD_SWP 0010000 /* swap bytes */ +#define CMD_V_MODE 8 /* mode */ +#define CMD_M_MODE 017 +#define CMD_IE 0000200 /* int enable */ +#define CMD_V_FNC 0 /* function */ +#define CMD_M_FNC 037 /* function */ +#define CMD_N_FNC (CMD_M_FNC + 1) +#define FNC_READ 001 /* read */ +#define FNC_WCHR 004 /* write char */ +#define FNC_WRIT 005 /* write */ +#define FNC_WSSM 006 /* write mem */ +#define FNC_POS 010 /* position */ +#define FNC_FMT 011 /* format */ +#define FNC_CTL 012 /* control */ +#define FNC_INIT 013 /* init */ +#define FNC_GSTA 017 /* get status */ +#define CMD_MBZ 0000140 +#define GET_FNC(x) (((x) >> CMD_V_FNC) & CMD_M_FNC) +#define GET_MOD(x) (((x) >> CMD_V_MODE) & CMD_M_MODE) + +/* Function test flags */ + +#define FLG_MO 001 /* motion */ +#define FLG_WR 002 /* write */ +#define FLG_AD 004 /* addr mem */ + +/* Message packet offsets */ + +#define MSG_PLNT 8 /* packet length */ +#define msghdr tsmsgp[0] /* header */ +#define msglnt tsmsgp[1] /* length */ +#define msgrfc tsmsgp[2] /* residual frame */ +#define msgxs0 tsmsgp[3] /* ext status 0 */ +#define msgxs1 tsmsgp[4] /* ext status 1 */ +#define msgxs2 tsmsgp[5] /* ext status 2 */ +#define msgxs3 tsmsgp[6] /* ext status 3 */ +#define msgxs4 tsmsgp[7] /* ext status 4 */ + +/* Message packet header */ + +#define MSG_ACK 0100000 /* acknowledge */ +#define MSG_MATN 0000000 /* attention */ +#define MSG_MILL 0000400 /* illegal */ +#define MSG_MNEF 0001000 /* non exec fnc */ +#define MSG_CEND 0000020 /* end */ +#define MSG_CFAIL 0000021 /* fail */ +#define MSG_CERR 0000022 /* error */ +#define MSG_CATN 0000023 /* attention */ + +/* Extended status register 0 */ + +#define XS0_TMK 0100000 /* tape mark */ +#define XS0_RLS 0040000 /* rec lnt short */ +#define XS0_LET 0020000 /* log end tape */ +#define XS0_RLL 0010000 /* rec lnt long */ +#define XS0_WLE 0004000 /* write lock err */ +#define XS0_NEF 0002000 /* non exec fnc */ +#define XS0_ILC 0001000 /* illegal cmd */ +#define XS0_ILA 0000400 /* illegal addr */ +#define XS0_MOT 0000200 /* tape has moved */ +#define XS0_ONL 0000100 /* online */ +#define XS0_IE 0000040 /* int enb */ +#define XS0_VCK 0000020 /* volume check */ +#define XS0_PET 0000010 /* 1600 bpi */ +#define XS0_WLK 0000004 /* write lock */ +#define XS0_BOT 0000002 /* BOT */ +#define XS0_EOT 0000001 /* EOT */ +#define XS0_ALLCLR 0177600 /* clear at start */ + +/* Extended status register 1 */ + +#define XS1_UCOR 0000002 /* uncorrectable */ + +/* Extended status register 2 */ + +#define XS2_XTF 0000200 /* ext features */ + +/* Extended status register 3 */ + +#define XS3_OPI 0000100 /* op incomplete */ +#define XS3_REV 0000040 /* reverse */ +#define XS3_RIB 0000001 /* reverse to BOT */ + +/* Extended status register 4 */ + +#define XS4_HDS 0100000 /* high density */ + +/* Write characteristics packet offsets */ + +#define WCH_PLNT 5 /* packet length */ +#define wchadl tswchp[0] /* address low */ +#define wchadh tswchp[1] /* address high */ +#define wchlnt tswchp[2] /* length */ +#define wchopt tswchp[3] /* options */ +#define wchxopt tswchp[4] /* ext options */ + +/* Write characteristics options */ + +#define WCH_ESS 0000200 /* stop dbl tmk */ +#define WCH_ENB 0000100 /* BOT = tmk */ +#define WCH_EAI 0000040 /* enb attn int */ +#define WCH_ERI 0000020 /* enb mrls int */ + +/* Write characteristics extended options */ + +#define WCHX_HDS 0000040 /* high density */ + +#define MAX(a,b) (((a) >= (b))? (a): (b)) +#define MAX_PLNT 8 /* max pkt length */ + +extern int32 int_req[IPL_HLVL]; +extern UNIT cpu_unit; +extern FILE *sim_deb; + +uint8 *tsxb = NULL; /* xfer buffer */ +int32 tssr = 0; /* status register */ +int32 tsba = 0; /* mem addr */ +int32 tsdbx = 0; /* data buf ext */ +int32 tscmdp[CMD_PLNT] = { 0 }; /* command packet */ +int32 tsmsgp[MSG_PLNT] = { 0 }; /* message packet */ +int32 tswchp[WCH_PLNT] = { 0 }; /* wr char packet */ +int32 ts_ownc = 0; /* tape owns cmd */ +int32 ts_ownm = 0; /* tape owns msg */ +int32 ts_qatn = 0; /* queued attn */ +int32 ts_bcmd = 0; /* boot cmd */ +int32 ts_time = 10; /* record latency */ +static uint16 cpy_buf[MAX_PLNT]; /* copy buffer */ + +DEVICE ts_dev; +t_stat ts_rd (int32 *data, int32 PA, int32 access); +t_stat ts_wr (int32 data, int32 PA, int32 access); +t_stat ts_svc (UNIT *uptr); +t_stat ts_reset (DEVICE *dptr); +t_stat ts_attach (UNIT *uptr, char *cptr); +t_stat ts_detach (UNIT *uptr); +t_stat ts_boot (int32 unitno, DEVICE *dptr); +int32 ts_updtssr (int32 t); +int32 ts_updxs0 (int32 t); +void ts_cmpendcmd (int32 s0, int32 s1); +void ts_endcmd (int32 ssf, int32 xs0f, int32 msg); +int32 ts_map_status (t_stat st); + +/* TS data structures + + ts_dev TS device descriptor + ts_unit TS unit list + ts_reg TS register list + ts_mod TS modifier list +*/ + +DIB ts_dib = { + IOBA_TS, IOLN_TS, &ts_rd, &ts_wr, + 1, IVCL (TS), VEC_TS, { NULL } + }; + +UNIT ts_unit = { UDATA (&ts_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }; + +REG ts_reg[] = { + { GRDATA (TSSR, tssr, DEV_RDX, 16, 0) }, + { GRDATA (TSBA, tsba, DEV_RDX, 22, 0) }, + { GRDATA (TSDBX, tsdbx, DEV_RDX, 8, 0) }, + { GRDATA (CHDR, cmdhdr, DEV_RDX, 16, 0) }, + { GRDATA (CADL, cmdadl, DEV_RDX, 16, 0) }, + { GRDATA (CADH, cmdadh, DEV_RDX, 16, 0) }, + { GRDATA (CLNT, cmdlnt, DEV_RDX, 16, 0) }, + { GRDATA (MHDR, msghdr, DEV_RDX, 16, 0) }, + { GRDATA (MRFC, msgrfc, DEV_RDX, 16, 0) }, + { GRDATA (MXS0, msgxs0, DEV_RDX, 16, 0) }, + { GRDATA (MXS1, msgxs1, DEV_RDX, 16, 0) }, + { GRDATA (MXS2, msgxs2, DEV_RDX, 16, 0) }, + { GRDATA (MXS3, msgxs3, DEV_RDX, 16, 0) }, + { GRDATA (MSX4, msgxs4, DEV_RDX, 16, 0) }, + { GRDATA (WADL, wchadl, DEV_RDX, 16, 0) }, + { GRDATA (WADH, wchadh, DEV_RDX, 16, 0) }, + { GRDATA (WLNT, wchlnt, DEV_RDX, 16, 0) }, + { GRDATA (WOPT, wchopt, DEV_RDX, 16, 0) }, + { GRDATA (WXOPT, wchxopt, DEV_RDX, 16, 0) }, + { FLDATA (INT, IREQ (TS), INT_V_TS) }, + { FLDATA (ATTN, ts_qatn, 0) }, + { FLDATA (BOOT, ts_bcmd, 0) }, + { FLDATA (OWNC, ts_ownc, 0) }, + { FLDATA (OWNM, ts_ownm, 0) }, + { DRDATA (TIME, ts_time, 24), PV_LEFT + REG_NZ }, + { DRDATA (POS, ts_unit.pos, T_ADDR_W), PV_LEFT + REG_RO }, + { GRDATA (DEVADDR, ts_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, ts_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } + }; + +MTAB ts_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL }, + { 0 } + }; + +DEVICE ts_dev = { + "TS", &ts_unit, ts_reg, ts_mod, + 1, 10, T_ADDR_W, 1, DEV_RDX, 8, + NULL, NULL, &ts_reset, + &ts_boot, &ts_attach, &ts_detach, + &ts_dib, DEV_DISABLE | TS_DIS | DEV_UBUS | DEV_QBUS | DEV_DEBUG + }; + +/* I/O dispatch routines, I/O addresses 17772520 - 17772522 + + 17772520 TSBA read/write + 17772522 TSSR read/write +*/ + +t_stat ts_rd (int32 *data, int32 PA, int32 access) +{ +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* TSBA */ + *data = tsba & DMASK; /* low 16b of ba */ + break; + case 1: /* TSSR */ + *data = tssr = ts_updtssr (tssr); /* update tssr */ + break; + } + +return SCPE_OK; +} + +t_stat ts_wr (int32 data, int32 PA, int32 access) +{ +int32 i, t; + +switch ((PA >> 1) & 01) { /* decode PA<1> */ + + case 0: /* TSDB */ + if ((tssr & TSSR_SSR) == 0) { /* ready? */ + tssr = tssr | TSSR_RMR; /* no, refuse */ + break; + } + tsba = ((tsdbx & TSDBX_M_XA) << 18) | /* form pkt addr */ + ((data & 03) << 16) | (data & 0177774); + tsdbx = 0; /* clr tsdbx */ + tssr = ts_updtssr (tssr & TSSR_NBA); /* clr ssr, err */ + msgxs0 = ts_updxs0 (msgxs0 & ~XS0_ALLCLR); /* clr, upd xs0 */ + msgrfc = msgxs1 = msgxs2 = msgxs3 = msgxs4 = 0; /* clr status */ + CLR_INT (TS); /* clr int req */ + t = Map_ReadW (tsba, CMD_PLNT << 1, cpy_buf); /* read cmd pkt */ + tsba = tsba + ((CMD_PLNT << 1) - t); /* incr tsba */ + if (t) { /* nxm? */ + ts_endcmd (TSSR_NXM + TC5, 0, MSG_ACK|MSG_MNEF|MSG_CFAIL); + return SCPE_OK; + } + for (i = 0; i < CMD_PLNT; i++) /* copy packet */ + tscmdp[i] = cpy_buf[i]; + ts_ownc = ts_ownm = 1; /* tape owns all */ + sim_activate (&ts_unit, ts_time); /* activate */ + break; + + case 1: /* TSSR */ + if (PA & 1) { /* TSDBX */ + if (UNIBUS) return SCPE_OK; /* not in TS11 */ + if (tssr & TSSR_SSR) { /* ready? */ + tsdbx = data; /* save */ + if (data & TSDBX_BOOT) { + ts_bcmd = 1; + sim_activate (&ts_unit, ts_time); + } + } + else tssr = tssr | TSSR_RMR; /* no, err */ + } + else if (access == WRITE) ts_reset (&ts_dev); /* reset */ + break; + } + +return SCPE_OK; +} + +/* Tape motion routines */ + +#define XTC(x,t) (((unsigned) (x) << 16) | (t)) +#define GET_X(x) (((x) >> 16) & 0177777) +#define GET_T(x) ((x) & 0177777) + +int32 ts_map_status (t_stat st) +{ +switch (st) { + + case MTSE_OK: + break; + + case MTSE_TMK: + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + return (XTC (XS0_TMK | XS0_RLS, TC2)); + + case MTSE_RECE: /* record in error */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + case MTSE_INVRL: /* invalid rec lnt */ + case MTSE_IOERR: /* IO error */ + msgxs1 = msgxs1 | XS1_UCOR; /* uncorrectable */ + return (XTC (XS0_RLS, TC6)); /* pos lost */ + + case MTSE_FMT: + case MTSE_UNATT: + case MTSE_EOM: /* end of medium */ + msgxs3 = msgxs3 | XS3_OPI; /* incomplete */ + return (XTC (XS0_RLS, TC6)); /* pos lost */ + + case MTSE_BOT: /* reverse into BOT */ + msgxs3 = msgxs3 | XS3_RIB; /* set status */ + return (XTC (XS0_BOT | XS0_RLS, TC2)); /* tape alert */ + + case MTSE_WRP: /* write protect */ + msgxs0 = msgxs0 | XS0_WLE | XS0_NEF; /* can't execute */ + return (XTC (XS0_WLE | XS0_NEF, TC3)); + } + +return 0; +} + +int32 ts_spacef (UNIT *uptr, int32 fc, t_bool upd) +{ +t_stat st; +t_mtrlnt tbc; + +do { + fc = (fc - 1) & DMASK; /* decr wc */ + if (upd) msgrfc = fc; + if (st = sim_tape_sprecf (uptr, &tbc)) /* space rec fwd, err? */ + return ts_map_status (st); /* map status */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (fc != 0); +return 0; +} + +int32 ts_skipf (UNIT *uptr, int32 fc) +{ +t_stat st; +t_mtrlnt tbc; +t_bool tmkprv = FALSE; + +msgrfc = fc; +if (sim_tape_bot (uptr) && (wchopt & WCH_ENB)) tmkprv = TRUE; +do { + st = sim_tape_sprecf (uptr, &tbc); /* space rec fwd */ + if (st == MTSE_TMK) { /* tape mark? */ + msgrfc = (msgrfc - 1) & DMASK; /* decr count */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + if (tmkprv && (wchopt & WCH_ESS)) /* 2nd tmk & ESS? */ + return (XTC ((msgrfc? XS0_RLS: 0) | + XS0_TMK | XS0_LET, TC2)); + tmkprv = TRUE; /* flag tmk */ + } + else if (st != MTSE_OK) return ts_map_status (st); + else tmkprv = FALSE; /* not a tmk */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (msgrfc != 0); +return 0; +} + +int32 ts_spacer (UNIT *uptr, int32 fc, t_bool upd) +{ +int32 st; +t_mtrlnt tbc; + +do { + fc = (fc - 1) & DMASK; /* decr wc */ + if (upd) msgrfc = fc; + if (st = sim_tape_sprecr (uptr, &tbc)) /* space rec rev, err? */ + return ts_map_status (st); /* map status */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (fc != 0); +return 0; +} + +int32 ts_skipr (UNIT *uptr, int32 fc) +{ +t_stat st; +t_mtrlnt tbc; +t_bool tmkprv = FALSE; + +msgrfc = fc; +do { + st = sim_tape_sprecr (uptr, &tbc); /* space rec rev */ + if (st == MTSE_TMK) { /* tape mark? */ + msgrfc = (msgrfc - 1) & DMASK; /* decr count */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + if (tmkprv && (wchopt & WCH_ESS)) /* 2nd tmk & ESS? */ + return (XTC ((msgrfc? XS0_RLS: 0) | + XS0_TMK | XS0_LET, TC2)); + tmkprv = TRUE; /* flag tmk */ + } + else if (st != MTSE_OK) return ts_map_status (st); + else tmkprv = FALSE; /* not a tmk */ + msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ + } while (msgrfc != 0); +return 0; +} + +int32 ts_readf (UNIT *uptr, uint32 fc) +{ +t_stat st; +t_mtrlnt i, t, tbc, wbc; +int32 wa; + +msgrfc = fc; +st = sim_tape_rdrecf (uptr, tsxb, &tbc, MT_MAXFR); /* read rec fwd */ +if (st != MTSE_OK) return ts_map_status (st); /* error? */ +if (fc == 0) fc = 0200000; /* byte count */ +tsba = (cmdadh << 16) | cmdadl; /* buf addr */ +wbc = (tbc > fc)? fc: tbc; /* cap buf size */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +if (cmdhdr & CMD_SWP) { /* swapped? */ + for (i = 0; i < wbc; i++) { /* copy buffer */ + wa = tsba ^ 1; /* apply OPP */ + if (Map_WriteB (tsba, 1, &tsxb[i])) { /* store byte, nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */ + return (XTC (XS0_RLS, TC4)); + } + tsba = tsba + 1; + msgrfc = (msgrfc - 1) & DMASK; + } + } +else { + t = Map_WriteB (tsba, wbc, tsxb); /* store record */ + tsba = tsba + (wbc - t); /* update tsba */ + if (t) { /* nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); /* set error */ + return (XTC (XS0_RLS, TC4)); + } + msgrfc = (msgrfc - (wbc - t)) & DMASK; /* update fc */ + } +if (msgrfc) return (XTC (XS0_RLS, TC2)); /* buf too big? */ +if (tbc > wbc) return (XTC (XS0_RLL, TC2)); /* rec too big? */ +return 0; +} + +int32 ts_readr (UNIT *uptr, uint32 fc) +{ +t_stat st; +t_mtrlnt i, tbc, wbc; +int32 wa; + +msgrfc = fc; +st = sim_tape_rdrecr (uptr, tsxb, &tbc, MT_MAXFR); /* read rec rev */ +if (st != MTSE_OK) return ts_map_status (st); /* error? */ +if (fc == 0) fc = 0200000; /* byte count */ +tsba = (cmdadh << 16) | cmdadl + fc; /* buf addr */ +wbc = (tbc > fc)? fc: tbc; /* cap buf size */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +for (i = wbc; i > 0; i--) { /* copy buffer */ + tsba = tsba - 1; + wa = (cmdhdr & CMD_SWP)? tsba ^ 1: tsba; /* apply OPP */ + if (Map_WriteB (wa, 1, &tsxb[i - 1])) { /* store byte, nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); + return (XTC (XS0_RLS, TC4)); + } + msgrfc = (msgrfc - 1) & DMASK; + } +if (msgrfc) return (XTC (XS0_RLS, TC2)); /* buf too big? */ +if (tbc > wbc) return (XTC (XS0_RLL, TC2)); /* rec too big? */ +return 0; +} + +int32 ts_write (UNIT *uptr, int32 fc) +{ +int32 i, t; +uint32 wa; +t_stat st; + +msgrfc = fc; +if (fc == 0) fc = 0200000; /* byte count */ +tsba = (cmdadh << 16) | cmdadl; /* buf addr */ +if (cmdhdr & CMD_SWP) { /* swapped? */ + for (i = 0; i < fc; i++) { /* copy mem to buf */ + wa = tsba ^ 1; /* apply OPP */ + if (Map_ReadB (wa, 1, &tsxb[i])) { /* fetch byte, nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); + return TC5; + } + tsba = tsba + 1; + } + } +else { + t = Map_ReadB (tsba, fc, tsxb); /* fetch record */ + tsba = tsba + (fc - t); /* update tsba */ + if (t) { /* nxm? */ + tssr = ts_updtssr (tssr | TSSR_NXM); + return TC5; + } + } +if (st = sim_tape_wrrecf (uptr, tsxb, fc)) /* write rec, err? */ + return ts_map_status (st); /* return status */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +msgrfc = 0; +if (sim_tape_eot (&ts_unit)) /* EOT on write? */ + return XTC (XS0_EOT, TC2); +return 0; +} + +int32 ts_wtmk (UNIT *uptr) +{ +t_stat st; + +if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + return ts_map_status (st); /* return status */ +msgxs0 = msgxs0 | XS0_MOT; /* tape has moved */ +if (sim_tape_eot (&ts_unit)) /* EOT on write? */ + return XTC (XS0_EOT, TC2); +return XTC (XS0_TMK, TC0); +} + +/* Unit service */ + +t_stat ts_svc (UNIT *uptr) +{ +int32 i, t, bc, fnc, mod, st0, st1; + +static const int32 fnc_mod[CMD_N_FNC] = { /* max mod+1 0 ill */ + 0, 4, 0, 0, 1, 2, 1, 0, /* 00 - 07 */ + 5, 3, 5, 1, 0, 0, 0, 1, /* 10 - 17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 30 - 37 */ + }; +static const int32 fnc_flg[CMD_N_FNC] = { + 0, FLG_MO+FLG_AD, 0, 0, 0, FLG_MO+FLG_WR+FLG_AD, FLG_AD, 0, + FLG_MO, FLG_MO+FLG_WR, FLG_MO, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 27 */ + 0, 0, 0, 0, 0, 0, 0, 0 /* 30 - 37 */ + }; +static const char *fnc_name[CMD_N_FNC] = { + "0", "READ", "2", "3", "WCHR", "WRITE", "WSSM", "7", + "POS", "FMT", "CTL", "INIT", "14", "15", "16", "GSTA", + "20", "21", "22", "23", "24", "25", "26", "27", + "30", "31", "32", "33", "34", "35", "36", "37" + }; + +if (ts_bcmd) { /* boot? */ + ts_bcmd = 0; /* clear flag */ + sim_tape_rewind (uptr); /* rewind */ + if (uptr->flags & UNIT_ATT) { /* attached? */ + cmdlnt = cmdadh = cmdadl = 0; /* defang rd */ + ts_spacef (uptr, 1, FALSE); /* space fwd */ + ts_readf (uptr, 512); /* read blk */ + tssr = ts_updtssr (tssr | TSSR_SSR); + } + else tssr = ts_updtssr (tssr | TSSR_SSR | TC3); + if (cmdhdr & CMD_IE) SET_INT (TS); + return SCPE_OK; + } + +if (!(cmdhdr & CMD_ACK)) { /* no acknowledge? */ + tssr = ts_updtssr (tssr | TSSR_SSR); /* set rdy, int */ + if (cmdhdr & CMD_IE) SET_INT (TS); + ts_ownc = ts_ownm = 0; /* CPU owns all */ + return SCPE_OK; + } +fnc = GET_FNC (cmdhdr); /* get fnc+mode */ +mod = GET_MOD (cmdhdr); +if (DEBUG_PRS (ts_dev)) + fprintf (sim_deb, ">>TS: cmd=%s, mod=%o, buf=%o, lnt=%d, pos=%d\n", + fnc_name[fnc], mod, cmdadl, cmdlnt, ts_unit.pos); +if ((fnc != FNC_WCHR) && (tssr & TSSR_NBA)) { /* ~wr chr & nba? */ + ts_endcmd (TC3, 0, 0); /* error */ + return SCPE_OK; + } +if (ts_qatn && (wchopt & WCH_EAI)) { /* attn pending? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn msg */ + SET_INT (TS); /* set interrupt */ + ts_qatn = 0; /* not pending */ + return SCPE_OK; + } +if (cmdhdr & CMD_CVC) /* cvc? clr vck */ + msgxs0 = msgxs0 & ~XS0_VCK; +if ((cmdhdr & CMD_MBZ) || (mod >= fnc_mod[fnc])) { /* test mbz */ + ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; + } +if ((fnc_flg[fnc] & FLG_MO) && /* mot+(vck|!att)? */ + ((msgxs0 & XS0_VCK) || !(uptr->flags & UNIT_ATT))) { + ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; + } +if ((fnc_flg[fnc] & FLG_WR) && /* write? */ + sim_tape_wrp (uptr)) { /* write lck? */ + ts_endcmd (TC3, XS0_WLE | XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; + } +if ((((fnc == FNC_READ) && (mod == 1)) || /* read rev */ + ((fnc == FNC_POS) && (mod & 1))) && /* space rev */ + sim_tape_bot (uptr)) { /* BOT? */ + ts_endcmd (TC3, XS0_NEF, MSG_ACK | MSG_MNEF | MSG_CFAIL); + return SCPE_OK; + } +if ((fnc_flg[fnc] & FLG_AD) && (cmdadh & ADDRTEST)) { /* buf addr > 22b? */ + ts_endcmd (TC3, XS0_ILA, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; + } + +st0 = st1 = 0; +switch (fnc) { /* case on func */ + + case FNC_INIT: /* init */ + if (!sim_tape_bot (uptr)) msgxs0 = msgxs0 | XS0_MOT; /* set if tape moves */ + sim_tape_rewind (uptr); /* rewind */ + case FNC_WSSM: /* write mem */ + case FNC_GSTA: /* get status */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* send end packet */ + return SCPE_OK; + + case FNC_WCHR: /* write char */ + if ((cmdadh & ADDRTEST) || (cmdadl & 1) || (cmdlnt < 6)) { + ts_endcmd (TSSR_NBA | TC3, XS0_ILA, 0); + break; + } + tsba = (cmdadh << 16) | cmdadl; + bc = ((WCH_PLNT << 1) > cmdlnt)? cmdlnt: WCH_PLNT << 1; + t = Map_ReadW (tsba, bc, cpy_buf); /* fetch packet */ + tsba = tsba + (bc - t); /* inc tsba */ + if (t) { /* nxm? */ + ts_endcmd (TSSR_NBA | TSSR_NXM | TC5, 0, 0); + return SCPE_OK; + } + for (i = 0; i < (bc / 2); i++) /* copy packet */ + tswchp[i] = cpy_buf[i]; + if ((wchlnt < ((MSG_PLNT - 1) * 2)) || (wchadh & 0177700) || + (wchadl & 1)) ts_endcmd (TSSR_NBA | TC3, 0, 0); + else { + msgxs2 = msgxs2 | XS2_XTF | 1; + tssr = ts_updtssr (tssr & ~TSSR_NBA); + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); + } + return SCPE_OK; + + case FNC_CTL: /* control */ + switch (mod) { /* case mode */ + + case 00: /* msg buf rls */ + tssr = ts_updtssr (tssr | TSSR_SSR); /* set SSR */ + if (wchopt & WCH_ERI) SET_INT (TS); + ts_ownc = 0; ts_ownm = 1; /* keep msg */ + break; + + case 01: /* rewind and unload */ + if (!sim_tape_bot (uptr)) msgxs0 = msgxs0 | XS0_MOT; /* if tape moves */ + sim_tape_detach (uptr); /* unload */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); + break; + + case 02: /* clean */ + ts_endcmd (TC0, 0, MSG_ACK | MSG_CEND); /* nop */ + break; + + case 03: /* undefined */ + ts_endcmd (TC3, XS0_ILC, MSG_ACK | MSG_MILL | MSG_CFAIL); + return SCPE_OK; + + case 04: /* rewind */ + if (!sim_tape_bot (uptr)) msgxs0 = msgxs0 | XS0_MOT; /* if tape moves */ + sim_tape_rewind (uptr); + ts_endcmd (TC0, XS0_BOT, MSG_ACK | MSG_CEND); + break; + } + break; + + case FNC_READ: /* read */ + switch (mod) { /* case mode */ + + case 00: /* fwd */ + st0 = ts_readf (uptr, cmdlnt); /* read */ + break; + + case 01: /* back */ + st0 = ts_readr (uptr, cmdlnt); /* read */ + break; + + case 02: /* reread fwd */ + if (cmdhdr & CMD_OPP) { /* opposite? */ + st0 = ts_readr (uptr, cmdlnt); + st1 = ts_spacef (uptr, 1, FALSE); + } + else { + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_readf (uptr, cmdlnt); + } + break; + + case 03: /* reread back */ + if (cmdhdr & CMD_OPP) { /* opposite */ + st0 = ts_readf (uptr, cmdlnt); + st1 = ts_spacer (uptr, 1, FALSE); + } + else { + st0 = ts_spacef (uptr, 1, FALSE); + st1 = ts_readr (uptr, cmdlnt); + } + break; + } + ts_cmpendcmd (st0, st1); + break; + + case FNC_WRIT: /* write */ + switch (mod) { /* case mode */ + + case 00: /* write */ + st0 = ts_write (uptr, cmdlnt); + break; + + case 01: /* rewrite */ + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_write (uptr, cmdlnt); + break; + } + ts_cmpendcmd (st0, st1); + break; + + case FNC_FMT: /* format */ + switch (mod) { /* case mode */ + + case 00: /* write tmk */ + st0 = ts_wtmk (uptr); + break; + + case 01: /* erase */ + break; + + case 02: /* retry tmk */ + st0 = ts_spacer (uptr, 1, FALSE); + st1 = ts_wtmk (uptr); + break; + } + ts_cmpendcmd (st0, st1); + break; + + case FNC_POS: /* position */ + switch (mod) { /* case mode */ + + case 00: /* space fwd */ + st0 = ts_spacef (uptr, cmdadl, TRUE); + break; + + case 01: /* space rev */ + st0 = ts_spacer (uptr, cmdadl, TRUE); + break; + + case 02: /* space ffwd */ + st0 = ts_skipf (uptr, cmdadl); + break; + + case 03: /* space frev */ + st0 = ts_skipr (uptr, cmdadl); + break; + + case 04: /* rewind */ + if (!sim_tape_bot (uptr)) /* if tape moves */ + msgxs0 = msgxs0 | XS0_MOT; + sim_tape_rewind (uptr); + break; + } + ts_cmpendcmd (st0, 0); + break; + } + +return SCPE_OK; +} + +/* Utility routines */ + +int32 ts_updtssr (int32 t) +{ +t = (t & ~TSSR_EMA) | ((tsba >> (16 - TSSR_V_EMA)) & TSSR_EMA); +if (ts_unit.flags & UNIT_ATT) t = t & ~TSSR_OFL; +else t = t | TSSR_OFL; +return (t & ~TSSR_MBZ); +} + +int32 ts_updxs0 (int32 t) +{ +t = (t & ~(XS0_ONL | XS0_WLK | XS0_BOT | XS0_IE)) | XS0_PET; +if (ts_unit.flags & UNIT_ATT) { + t = t | XS0_ONL; + if (sim_tape_wrp (&ts_unit)) t = t | XS0_WLK; + if (sim_tape_bot (&ts_unit)) + t = (t | XS0_BOT) & ~XS0_EOT; + if (sim_tape_eot (&ts_unit)) + t = (t | XS0_EOT) & ~XS0_BOT; + } +else t = t & ~XS0_EOT; +if (cmdhdr & CMD_IE) t = t | XS0_IE; +return t; +} + +void ts_cmpendcmd (int32 s0, int32 s1) +{ +int32 xs0, ssr, tc; +static const int32 msg[8] = { + MSG_ACK | MSG_CEND, MSG_ACK | MSG_MATN | MSG_CATN, + MSG_ACK | MSG_CEND, MSG_ACK | MSG_CFAIL, + MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR, + MSG_ACK | MSG_CERR, MSG_ACK | MSG_CERR + }; + +xs0 = GET_X (s0) | GET_X (s1); /* or XS0 errs */ +s0 = GET_T (s0); /* get SSR errs */ +s1 = GET_T (s1); +ssr = (s0 | s1) & ~TSSR_TC; /* or SSR errs */ +tc = MAX (GET_TC (s0), GET_TC (s1)); /* max term code */ +ts_endcmd (ssr | (tc << TSSR_V_TC), xs0, msg[tc]); /* end cmd */ +return; +} + +void ts_endcmd (int32 tc, int32 xs0, int32 msg) +{ +int32 i, t; + +msgxs0 = ts_updxs0 (msgxs0 | xs0); /* update XS0 */ +if (wchxopt & WCHX_HDS) msgxs4 = msgxs4 | XS4_HDS; /* update XS4 */ +if (msg && !(tssr & TSSR_NBA)) { /* send end pkt */ + msghdr = msg; + msglnt = wchlnt - 4; /* exclude hdr, bc */ + tsba = (wchadh << 16) | wchadl; + for (i = 0; (i < MSG_PLNT) && (i < (wchlnt / 2)); i++) + cpy_buf[i] = (uint16) tsmsgp[i]; /* copy buffer */ + t = Map_WriteW (tsba, i << 1, cpy_buf); /* write to mem */ + tsba = tsba + ((i << 1) - t); /* incr tsba */ + if (t) { /* nxm? */ + tssr = tssr | TSSR_NXM; + tc = (tc & ~TSSR_TC) | TC4; + } + } +tssr = ts_updtssr (tssr | tc | TSSR_SSR | (tc? TSSR_SC: 0)); +if (cmdhdr & CMD_IE) SET_INT (TS); +ts_ownm = 0; ts_ownc = 0; +if (DEBUG_PRS (ts_dev)) + fprintf (sim_deb, ">>TS: sta=%o, tc=%o, rfc=%d, pos=%d\n", + msgxs0, GET_TC (tssr), msgrfc, ts_unit.pos); +return; +} + +/* Device reset */ + +t_stat ts_reset (DEVICE *dptr) +{ +int32 i; + +sim_tape_rewind (&ts_unit); +tsba = tsdbx = 0; +ts_ownc = ts_ownm = 0; +ts_bcmd = 0; +ts_qatn = 0; +tssr = ts_updtssr (TSSR_NBA | TSSR_SSR); +for (i = 0; i < CMD_PLNT; i++) tscmdp[i] = 0; +for (i = 0; i < WCH_PLNT; i++) tswchp[i] = 0; +for (i = 0; i < MSG_PLNT; i++) tsmsgp[i] = 0; +msgxs0 = ts_updxs0 (XS0_VCK); +CLR_INT (TS); +if (tsxb == NULL) tsxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); +if (tsxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach */ + +t_stat ts_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +tssr = tssr & ~TSSR_OFL; /* clr offline */ +if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) return r; /* attn msg? */ +if (ts_ownm) { /* own msg buf? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ + SET_INT (TS); /* set interrupt */ + ts_qatn = 0; /* don't queue */ + } +else ts_qatn = 1; /* else queue */ +return r; +} + +/* Detach routine */ + +t_stat ts_detach (UNIT* uptr) +{ +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +r = sim_tape_detach (uptr); /* detach unit */ +if (r != SCPE_OK) return r; /* error? */ +tssr = tssr | TSSR_OFL; /* set offline */ +if ((tssr & TSSR_NBA) || !(wchopt & WCH_EAI)) return r; /* attn msg? */ +if (ts_ownm) { /* own msg buf? */ + ts_endcmd (TC1, 0, MSG_MATN | MSG_CATN); /* send attn */ + SET_INT (TS); /* set interrupt */ + ts_qatn = 0; /* don't queue */ + } +else ts_qatn = 1; /* else queue */ +return r; +} + +/* Boot */ + +#if defined (VM_PDP11) +#define BOOT_START 01000 +#define BOOT_CSR0 (BOOT_START + 006) +#define BOOT_CSR1 (BOOT_START + 012) +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 0012706, 0001000, /* mov #boot_start, sp */ + 0012700, 0172520, /* mov #tsba, r0 */ + 0012701, 0172522, /* mov #tssr, r1 */ + 0005011, /* clr (r1) ; init, rew */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001070, /* mov #pkt1, (r0) ; set char */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001110, /* mov #pkt2, (r0) ; read, skip */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0012710, 0001110, /* mov #pkt2, (r0) ; read */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0005711, /* tst (r1) ; err? */ + 0100421, /* bmi hlt */ + 0005000, /* clr r0 */ + 0012704, 0001066+020, /* mov #sgnt+20, r4 */ + 0005007, /* clr r7 */ + 0046523, /* sgnt: "SM" */ + 0140004, /* pkt1: 140004, wcpk, 0, 8. */ + 0001100, + 0000000, + 0000010, + 0001122, /* wcpk: msg, 0, 14., 0 */ + 0000000, + 0000016, + 0000000, + 0140001, /* pkt2: 140001, 0, 0, 512. */ + 0000000, + 0000000, + 0001000, + 0000000 /* hlt: halt */ + /* msg: .blk 4 */ + }; + +t_stat ts_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +sim_tape_rewind (&ts_unit); +for (i = 0; i < BOOT_LEN; i++) + M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_CSR0 >> 1] = ts_dib.ba & DMASK; +M[BOOT_CSR1 >> 1] = (ts_dib.ba & DMASK) + 02; +saved_PC = BOOT_START; +return SCPE_OK; +} + +#else + +t_stat ts_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} +#endif diff --git a/PDP11/pdp11_tu.c b/PDP11/pdp11_tu.c new file mode 100644 index 0000000..5bd3bc6 --- /dev/null +++ b/PDP11/pdp11_tu.c @@ -0,0 +1,1009 @@ +/* pdp11_tu.c - PDP-11 TM02/TU16 TM03/TU45/TU77 Massbus magnetic tape controller + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tu TM02/TM03 magtape + + 17-May-07 RMS CS1 DVA resides in device, not MBA + 29-Apr-07 RMS Fixed bug in setting FCE on TMK (found by Naoki Hamada) + 16-Feb-06 RMS Added tape capacity checking + 12-Nov-05 RMS Changed default formatter to TM03 (for VMS) + 31-Oct-05 RMS Fixed address width for large files + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 31-Mar-05 RMS Fixed inaccuracies in error reporting + 18-Mar-05 RMS Added attached test to detach routine + 10-Sep-04 RMS Cloned from pdp10_tu.c + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number, sign = error + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number, sign = error + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. +*/ + +#if defined (VM_PDP10) +#error "PDP-10 uses pdp10_tu.c!" + +#elif defined (VM_PDP11) +#include "pdp11_defs.h" +#define DEV_DIS_INIT DEV_DIS + +#elif defined (VM_VAX) +#include "vax_defs.h" +#define DEV_DIS_INIT 0 +#if (!UNIBUS) +#error "Qbus not supported!" +#endif + +#endif +#include "sim_tape.h" + +#define TU_NUMFM 1 /* #formatters */ +#define TU_NUMDR 8 /* #drives */ +#define USTAT u3 /* unit status */ +#define UDENS u4 /* unit density */ +#define UD_UNK 0 /* unknown */ +#define MT_MAXFR (1 << 16) /* max data buf */ +#define DEV_V_TM03 (DEV_V_FFUF + 0) /* TM02/TM03 */ +#define DEV_TM03 (1 << DEV_V_TM03) +#define UNIT_V_TYPE (MTUF_V_UF + 0) +#define UNIT_M_TYPE 03 +#define UNIT_TYPE (UNIT_M_TYPE << UNIT_V_TYPE) +#define UNIT_TE16 (0 << UNIT_V_TYPE) +#define UNIT_TU45 (1 << UNIT_V_TYPE) +#define UNIT_TU77 (2 << UNIT_V_TYPE) +#define GET_TYPE(x) (((x) >> UNIT_V_TYPE) & UNIT_M_TYPE) + +/* CS1 - offset 0 */ + +#define CS1_OF 0 +#define CS1_GO CSR_GO /* go */ +#define CS1_V_FNC 1 /* function pos */ +#define CS1_M_FNC 037 /* function mask */ +#define CS1_N_FNC (CS1_M_FNC + 1) +#define FNC_NOP 000 /* no operation */ +#define FNC_UNLOAD 001 /* unload */ +#define FNC_REWIND 003 /* rewind */ +#define FNC_FCLR 004 /* formatter clear */ +#define FNC_RIP 010 /* read in preset */ +#define FNC_ERASE 012 /* erase tape */ +#define FNC_WREOF 013 /* write tape mark */ +#define FNC_SPACEF 014 /* space forward */ +#define FNC_SPACER 015 /* space reverse */ +#define FNC_XFER 024 /* >=? data xfr */ +#define FNC_WCHKF 024 /* write check */ +#define FNC_WCHKR 027 /* write check rev */ +#define FNC_WRITE 030 /* write */ +#define FNC_READF 034 /* read forward */ +#define FNC_READR 037 /* read reverse */ +#define CS1_RW 077 +#define CS1_DVA 04000 /* drive avail */ +#define GET_FNC(x) (((x) >> CS1_V_FNC) & CS1_M_FNC) + +/* TUFS - formatter status - offset 1 + + indicates kept in drive status + ^ indicates calculated on the fly +*/ + +#define FS_OF 1 +#define FS_SAT 0000001 /* slave attention */ +#define FS_BOT 0000002 /* ^beginning of tape */ +#define FS_TMK 0000004 /* end of file */ +#define FS_ID 0000010 /* ID burst detected */ +#define FS_SLOW 0000020 /* slowing down NI */ +#define FS_PE 0000040 /* ^PE status */ +#define FS_SSC 0000100 /* slave stat change */ +#define FS_RDY 0000200 /* ^formatter ready */ +#define FS_FPR 0000400 /* formatter present */ +#define FS_EOT 0002000 /* +end of tape */ +#define FS_WRL 0004000 /* ^write locked */ +#define FS_MOL 0010000 /* ^medium online */ +#define FS_PIP 0020000 /* +pos in progress */ +#define FS_ERR 0040000 /* ^error */ +#define FS_ATA 0100000 /* attention active */ +#define FS_REW 0200000 /* +rewinding */ + +#define FS_DYN (FS_ERR | FS_PIP | FS_MOL | FS_WRL | FS_EOT | \ + FS_RDY | FS_PE | FS_BOT) + +/* TUER - error register - offset 2 */ + +#define ER_OF 2 +#define ER_ILF 0000001 /* illegal func */ +#define ER_ILR 0000002 /* illegal register */ +#define ER_RMR 0000004 /* reg mod refused */ +#define ER_MCP 0000010 /* Mbus cpar err NI */ +#define ER_FER 0000020 /* format sel err */ +#define ER_MDP 0000040 /* Mbus dpar err NI */ +#define ER_VPE 0000100 /* vert parity err */ +#define ER_CRC 0000200 /* CRC err NI */ +#define ER_NSG 0000400 /* non std gap err NI */ +#define ER_FCE 0001000 /* frame count err */ +#define ER_ITM 0002000 /* inv tape mark NI */ +#define ER_NXF 0004000 /* wlock or fnc err */ +#define ER_DTE 0010000 /* time err NI */ +#define ER_OPI 0020000 /* op incomplete */ +#define ER_UNS 0040000 /* drive unsafe */ +#define ER_DCK 0100000 /* data check NI */ + +/* TUMR - maintenance register - offset 03 */ + +#define MR_OF 3 +#define MR_RW 0177637 /* read/write */ + +/* TUAS - attention summary - offset 4 */ + +#define AS_OF 4 +#define AS_U0 0000001 /* unit 0 flag */ + +/* TUFC - offset 5 */ + +#define FC_OF 5 + +/* TUDT - drive type - offset 6 */ + +#define DT_OF 6 +#define DT_NSA 0100000 /* not sect addr */ +#define DT_TAPE 0040000 /* tape */ +#define DT_PRES 0002000 /* slave present */ +#define DT_TM03 0000040 /* TM03 formatter */ +#define DT_OFF 0000010 /* drive off */ +#define DT_TU16 0000011 /* TE16 */ +#define DT_TU45 0000012 /* TU45 */ +#define DT_TU77 0000014 /* TU77 */ + +/* TUCC - check character, read only - offset 7 */ + +#define CC_OF 7 +#define CC_MBZ 0177000 /* must be zero */ + +/* TUSN - serial number - offset 8 */ + +#define SN_OF 8 + +/* TUTC - tape control register - offset 9 */ + +#define TC_OF 9 +#define TC_V_UNIT 0 /* unit select */ +#define TC_M_UNIT 07 +#define TC_V_EVN 0000010 /* even parity */ +#define TC_V_FMT 4 /* format select */ +#define TC_M_FMT 017 +#define TC_STD 014 /* standard */ +#define TC_CDUMP 015 /* core dump */ +#define TC_V_DEN 8 /* density select */ +#define TC_M_DEN 07 +#define TC_800 3 /* 800 bpi */ +#define TC_1600 4 /* 1600 bpi */ +#define TC_AER 0010000 /* abort on error */ +#define TC_SAC 0020000 /* slave addr change */ +#define TC_FCS 0040000 /* frame count status */ +#define TC_ACC 0100000 /* accelerating NI */ +#define TC_RW 0013777 +#define TC_MBZ 0004000 +#define TC_RIP ((TC_800 << TC_V_DEN) | (TC_STD << TC_V_FMT)) +#define GET_DEN(x) (((x) >> TC_V_DEN) & TC_M_DEN) +#define GET_FMT(x) (((x) >> TC_V_FMT) & TC_M_FMT) +#define GET_DRV(x) (((x) >> TC_V_UNIT) & TC_M_UNIT) + +int32 tucs1 = 0; /* control/status 1 */ +int32 tufc = 0; /* frame count */ +int32 tufs = 0; /* formatter status */ +int32 tuer = 0; /* error status */ +int32 tucc = 0; /* check character */ +int32 tumr = 0; /* maint register */ +int32 tutc = 0; /* tape control */ +int32 tu_time = 10; /* record latency */ +int32 tu_stopioe = 1; /* stop on error */ +static uint8 *xbuf = NULL; /* xfer buffer */ +static uint16 *wbuf = NULL; +static int32 fmt_test[16] = { /* fmt valid */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 + }; +static int32 dt_map[3] = { DT_TU16, DT_TU45, DT_TU77 }; +static char *tu_fname[CS1_N_FNC] = { + "NOP", "UNLD", "2", "REW", "FCLR", "5", "6", "7", + "RIP", "11", "ERASE", "WREOF", "SPCF", "SPCR", "16", "17", + "20", "21", "22", "23", "WRCHKF", "25", "26", "WRCHKR", + "WRITE", "31", "32", "33", "READF", "35", "36" "READR" + }; + +extern int32 sim_switches; +extern FILE *sim_deb; + +t_stat tu_mbrd (int32 *data, int32 PA, int32 fmtr); +t_stat tu_mbwr (int32 data, int32 PA, int32 fmtr); +t_stat tu_svc (UNIT *uptr); +t_stat tu_reset (DEVICE *dptr); +t_stat tu_attach (UNIT *uptr, char *cptr); +t_stat tu_detach (UNIT *uptr); +t_stat tu_boot (int32 unitno, DEVICE *dptr); +t_stat tu_set_fmtr (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tu_show_fmtr (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tu_go (int32 drv); +int32 tu_abort (void); +void tu_set_er (int32 flg); +void tu_clr_as (int32 mask); +void tu_update_fs (int32 flg, int32 drv); +t_stat tu_map_err (int32 drv, t_stat st, t_bool qdt); + +/* TU data structures + + tu_dev TU device descriptor + tu_unit TU unit list + tu_reg TU register list + tu_mod TU modifier list +*/ + +DIB tu_dib = { MBA_TU, 0, &tu_mbrd, &tu_mbwr,0, 0, 0, { &tu_abort } }; + +UNIT tu_unit[] = { + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&tu_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } + }; + +REG tu_reg[] = { + { GRDATA (CS1, tucs1, DEV_RDX, 6, 0) }, + { GRDATA (FC, tufc, DEV_RDX, 16, 0) }, + { GRDATA (FS, tufs, DEV_RDX, 16, 0) }, + { GRDATA (ER, tuer, DEV_RDX, 16, 0) }, + { GRDATA (CC, tucc, DEV_RDX, 16, 0) }, + { GRDATA (MR, tumr, DEV_RDX, 16, 0) }, + { GRDATA (TC, tutc, DEV_RDX, 16, 0) }, + { FLDATA (STOP_IOE, tu_stopioe, 0) }, + { DRDATA (TIME, tu_time, 24), PV_LEFT }, + { URDATA (UST, tu_unit[0].USTAT, DEV_RDX, 17, 0, TU_NUMDR, 0) }, + { URDATA (POS, tu_unit[0].pos, 10, T_ADDR_W, 0, + TU_NUMDR, PV_LEFT | REG_RO) }, + { NULL } + }; + +MTAB tu_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "MASSBUS", "MASSBUS", NULL, &mba_show_num }, +#if defined (VM_PDP11) + { MTAB_XTD|MTAB_VDV, 0, "FORMATTER", "TM02", + &tu_set_fmtr, &tu_show_fmtr }, + { MTAB_XTD|MTAB_VDV, 1, NULL, "TM03", + &tu_set_fmtr, NULL }, +#else + { MTAB_XTD|MTAB_VDV, 0, "FORMATTER", NULL, + NULL, &tu_show_fmtr }, +#endif + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { UNIT_TYPE, UNIT_TE16, "TE16", "TE16", NULL }, + { UNIT_TYPE, UNIT_TU45, "TU45", "TU45", NULL }, + { UNIT_TYPE, UNIT_TU77, "TU77", "TU77", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { 0 } + }; + +DEVICE tu_dev = { + "TU", tu_unit, tu_reg, tu_mod, + TU_NUMDR, 10, T_ADDR_W, 1, DEV_RDX, 8, + NULL, NULL, &tu_reset, + &tu_boot, &tu_attach, &tu_detach, + &tu_dib, DEV_MBUS|DEV_UBUS|DEV_QBUS|DEV_DEBUG|DEV_DISABLE|DEV_DIS_INIT|DEV_TM03 + }; + +/* Massbus register read */ + +t_stat tu_mbrd (int32 *data, int32 ofs, int32 fmtr) +{ +int32 drv; + +if (fmtr != 0) { /* only one fmtr */ + *data = 0; + return MBE_NXD; + } +drv = GET_DRV (tutc); /* get current unit */ +tu_update_fs (0, drv); /* update status */ + +switch (ofs) { /* decode offset */ + + case CS1_OF: /* MTCS1 */ + *data = (tucs1 & CS1_RW) | CS1_DVA; /* DVA always set */ + break; + + case FC_OF: /* MTFC */ + *data = tufc; + break; + + case FS_OF: /* MTFS */ + *data = tufs & 0177777; /* mask off rewind */ + break; + + case ER_OF: /* MTER */ + *data = tuer; + break; + + case AS_OF: /* MTAS */ + *data = (tufs & FS_ATA)? AS_U0: 0; + break; + + case CC_OF: /* MTCC */ + *data = tucc = tucc & ~CC_MBZ; + break; + + case MR_OF: /* MTMR */ + *data = tumr; + break; + + case DT_OF: /* MTDT */ + *data = DT_NSA | DT_TAPE | /* fmtr flags */ + ((tu_dev.flags & DEV_TM03)? DT_TM03: 0); + if (tu_unit[drv].flags & UNIT_DIS) *data |= DT_OFF; + else *data |= DT_PRES | dt_map[GET_TYPE (tu_unit[drv].flags)]; + break; + + case SN_OF: /* MTSN */ + *data = (tu_unit[drv].flags & UNIT_DIS)? 0: 040 | (drv + 1); + break; + + case TC_OF: /* MTTC */ + *data = tutc = tutc & ~TC_MBZ; + break; + + default: /* all others */ + return MBE_NXR; + } + +return SCPE_OK; +} + +/* Massbus register write */ + +t_stat tu_mbwr (int32 data, int32 ofs, int32 fmtr) +{ +int32 drv; + +if (fmtr != 0) return MBE_NXD; /* only one fmtr */ +drv = GET_DRV (tutc); /* get current unit */ + +switch (ofs) { /* decode PA<4:1> */ + + case CS1_OF: /* MTCS1 */ + if (tucs1 & CS1_GO) tu_set_er (ER_RMR); + else { + tucs1 = data & CS1_RW; + if (tucs1 & CS1_GO) return tu_go (drv); + } + break; + + case FC_OF: /* MTFC */ + if (tucs1 & CS1_GO) tu_set_er (ER_RMR); + else { + tufc = data; + tutc = tutc | TC_FCS; /* set fc flag */ + } + break; + + case AS_OF: /* MTAS */ + tu_clr_as (data); + break; + + case MR_OF: /* MTMR */ + tumr = (tumr & ~MR_RW) | (data & MR_RW); + break; + + case TC_OF: /* MTTC */ + if (tucs1 & CS1_GO) tu_set_er (ER_RMR); + else { + tutc = (tutc & ~TC_RW) | (data & TC_RW) | TC_SAC; + drv = GET_DRV (tutc); + } + break; + + case FS_OF: /* MTFS */ + case ER_OF: /* MTER */ + case CC_OF: /* MTCC */ + case DT_OF: /* MTDT */ + case SN_OF: /* MTSN */ + if (tucs1 & CS1_GO) tu_set_er (ER_RMR); + break; /* read only */ + + default: /* all others */ + return MBE_NXR; + } /* end switch */ + +tu_update_fs (0, drv); +return SCPE_OK; +} + +/* New magtape command */ + +t_stat tu_go (int32 drv) +{ +int32 fnc, den; +UNIT *uptr; + +fnc = GET_FNC (tucs1); /* get function */ +den = GET_DEN (tutc); /* get density */ +uptr = tu_dev.units + drv; /* get unit */ +if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, + ">>TU%d STRT: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=%d\n", + drv, tu_fname[fnc], tufc, tufs, tuer, uptr->pos); + +if ((fnc != FNC_FCLR) && /* not clear & err */ + ((tufs & FS_ERR) || sim_is_active (uptr))) { /* or in motion? */ + tu_set_er (ER_ILF); /* set err */ + tucs1 = tucs1 & ~CS1_GO; /* clear go */ + tu_update_fs (FS_ATA, drv); /* set attn */ + return MBE_GOE; + } +tu_clr_as (AS_U0); /* clear ATA */ +tutc = tutc & ~TC_SAC; /* clear addr change */ + +switch (fnc) { /* case on function */ + + case FNC_FCLR: /* drive clear */ + tuer = 0; /* clear errors */ + tutc = tutc & ~TC_FCS; /* clear fc status */ + tufs = tufs & ~(FS_SAT | FS_SSC | FS_ID | FS_ERR); + sim_cancel (uptr); /* reset drive */ + uptr->USTAT = 0; + case FNC_NOP: + tucs1 = tucs1 & ~CS1_GO; /* no operation */ + return SCPE_OK; + + case FNC_RIP: /* read-in preset */ + tutc = TC_RIP; /* set tutc */ + sim_tape_rewind (&tu_unit[0]); /* rewind unit 0 */ + tu_unit[0].USTAT = 0; + tucs1 = tucs1 & ~CS1_GO; + tufs = tufs & ~FS_TMK; + return SCPE_OK; + + case FNC_UNLOAD: /* unload */ + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + tu_set_er (ER_UNS); + break; + } + detach_unit (uptr); + uptr->USTAT = FS_REW; + sim_activate (uptr, tu_time); + tucs1 = tucs1 & ~CS1_GO; + tufs = tufs & ~FS_TMK; + return SCPE_OK; + + case FNC_REWIND: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + tu_set_er (ER_UNS); + break; + } + uptr->USTAT = FS_PIP | FS_REW; + sim_activate (uptr, tu_time); + tucs1 = tucs1 & ~CS1_GO; + tufs = tufs & ~FS_TMK; + return SCPE_OK; + + case FNC_SPACEF: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + tu_set_er (ER_UNS); + break; + } + if (sim_tape_eot (uptr) || ((tutc & TC_FCS) == 0)) { + tu_set_er (ER_NXF); + break; + } + uptr->USTAT = FS_PIP; + goto GO_XFER; + + case FNC_SPACER: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + tu_set_er (ER_UNS); + break; + } + if (sim_tape_bot (uptr) || ((tutc & TC_FCS) == 0)) { + tu_set_er (ER_NXF); + break; + } + uptr->USTAT = FS_PIP; + goto GO_XFER; + + case FNC_WCHKR: /* wchk = read */ + case FNC_READR: /* read rev */ + if (tufs & FS_BOT) { /* beginning of tape? */ + tu_set_er (ER_NXF); + break; + } + goto DATA_XFER; + + case FNC_WRITE: /* write */ + if (((tutc & TC_FCS) == 0) || /* frame cnt = 0? */ + ((den == TC_800) && (tufc > 0777765))) { /* NRZI, fc < 13? */ + tu_set_er (ER_NXF); + break; + } + case FNC_WREOF: /* write tape mark */ + case FNC_ERASE: /* erase */ + if (sim_tape_wrp (uptr)) { /* write locked? */ + tu_set_er (ER_NXF); + break; + } + case FNC_WCHKF: /* wchk = read */ + case FNC_READF: /* read */ + DATA_XFER: + if ((uptr->flags & UNIT_ATT) == 0) { /* unattached? */ + tu_set_er (ER_UNS); + break; + } + if (fmt_test[GET_FMT (tutc)] == 0) { /* invalid format? */ + tu_set_er (ER_FER); + break; + } + if (uptr->UDENS == UD_UNK) uptr->UDENS = den; /* set dens */ + uptr->USTAT = 0; + GO_XFER: + tufs = tufs & ~(FS_TMK | FS_ID); /* clear eof, id */ + sim_activate (uptr, tu_time); + return SCPE_OK; + + default: /* all others */ + tu_set_er (ER_ILF); /* not supported */ + break; + } /* end case function */ + +tucs1 = tucs1 & ~CS1_GO; /* clear go */ +tu_update_fs (FS_ATA, drv); /* set attn */ +return MBE_GOE; +} + +/* Abort transfer */ + +t_stat tu_abort (void) +{ +return tu_reset (&tu_dev); +} + +/* Unit service + + Complete movement or data transfer command + Unit must exist - can't remove an active unit + Unit must be attached - detach cancels in progress operations +*/ + +t_stat tu_svc (UNIT *uptr) +{ +int32 fnc, fmt, j, xbc; +int32 fc, drv; +t_mtrlnt i, tbc; +t_stat st, r = SCPE_OK; + +drv = (int32) (uptr - tu_dev.units); /* get drive # */ +if (uptr->USTAT & FS_REW) { /* rewind or unload? */ + sim_tape_rewind (uptr); /* rewind tape */ + uptr->USTAT = 0; /* clear status */ + tu_update_fs (FS_ATA | FS_SSC, drv); + return SCPE_OK; + } + +fnc = GET_FNC (tucs1); /* get command */ +fmt = GET_FMT (tutc); /* get format */ +fc = 0200000 - tufc; /* get frame count */ +uptr->USTAT = 0; /* clear status */ + +if ((uptr->flags & UNIT_ATT) == 0) { + tu_set_er (ER_UNS); /* set formatter error */ + if (fnc >= FNC_XFER) mba_set_don (tu_dib.ba); + tu_update_fs (FS_ATA, drv); + return (tu_stopioe? SCPE_UNATT: SCPE_OK); + } +switch (fnc) { /* case on function */ + +/* Non-data transfer commands - set ATA when done */ + + case FNC_SPACEF: /* space forward */ + do { + tufc = (tufc + 1) & 0177777; /* incr fc */ + if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ + r = tu_map_err (drv, st, 0); /* map error */ + break; + } + } while ((tufc != 0) && !sim_tape_eot (uptr)); + if (tufc) tu_set_er (ER_FCE); + else tutc = tutc & ~TC_FCS; + break; + + case FNC_SPACER: /* space reverse */ + do { + tufc = (tufc + 1) & 0177777; /* incr wc */ + if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ + r = tu_map_err (drv, st, 0); /* map error */ + break; + } + } while (tufc != 0); + if (tufc) tu_set_er (ER_FCE); + else tutc = tutc & ~TC_FCS; + break; + + case FNC_WREOF: /* write end of file */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = tu_map_err (drv, st, 0); /* map error */ + break; + + case FNC_ERASE: + if (sim_tape_wrp (uptr)) /* write protected? */ + r = tu_map_err (drv, MTSE_WRP, 0); /* map error */ + break; + +/* Unit service - data transfer commands */ + + case FNC_READF: /* read */ + case FNC_WCHKF: /* wcheck = read */ + tufc = 0; /* clear frame count */ + if ((uptr->UDENS == TC_1600) && sim_tape_bot (uptr)) + tufs = tufs | FS_ID; /* PE BOT? ID burst */ + if (st = sim_tape_rdrecf (uptr, xbuf, &tbc, MT_MAXFR)) { /* read fwd */ + if (st == MTSE_TMK) tu_set_er (ER_FCE); /* tmk also sets FCE */ + r = tu_map_err (drv, st, 1); /* map error */ + break; /* done */ + } + for (i = tbc; i < tbc + 4; i++) xbuf[i] = 0; /* pad with 0's */ + if (fmt == TC_CDUMP) { /* core dump? */ + for (i = j = 0; i < tbc; i = i + 4) { + wbuf[j++] = ((uint16) xbuf[i] & 0xF) | + (((uint16) (xbuf[i + 1] & 0xF)) << 4) | + (((uint16) (xbuf[i + 2] & 0xF)) << 8) | + (((uint16) (xbuf[i + 3] & 0xf)) << 12); + } + xbc = (tbc + 1) >> 1; + } + else { /* standard */ + for (i = j = 0; i < tbc; i = i + 2) { + wbuf[j++] = ((uint16) xbuf[i]) | + (((uint16) xbuf[i + 1]) << 8); + } + xbc = tbc; + } + if (mba_get_bc (tu_dib.ba) > xbc) /* record short? */ + tu_set_er (ER_FCE); /* set FCE, ATN */ + if (fnc == FNC_WCHKF) mba_chbufW (tu_dib.ba, xbc, wbuf); + else mba_wrbufW (tu_dib.ba, xbc, wbuf); + tufc = tbc & 0177777; + break; + + case FNC_WRITE: /* write */ + xbc = mba_rdbufW (tu_dib.ba, fc, wbuf); /* read buffer */ + if (xbc == 0) break; /* anything?? */ + if (fmt == TC_CDUMP) { /* core dump? */ + for (i = j = 0; j < xbc; j = j + 1) { + xbuf[i++] = wbuf[j] & 0xF; + xbuf[i++] = (wbuf[j] >> 4) & 0xF; + xbuf[i++] = (wbuf[j] >> 8) & 0xF; + xbuf[i++] = (wbuf[j] >> 12) & 0xF; + } + tbc = (xbc + 1) >> 1; + } + else { /* standard */ + for (i = j = 0; j < xbc; j = j + 1) { + xbuf[i++] = wbuf[j] & 0377; + xbuf[i++] = (wbuf[j] >> 8) & 0377; + } + tbc = xbc; + } + if (st = sim_tape_wrrecf (uptr, xbuf, tbc)) /* write rec, err? */ + r = tu_map_err (drv, st, 1); /* map error */ + else { + tufc = (tufc + tbc) & 0177777; + if (tufc == 0) tutc = tutc & ~TC_FCS; + } + break; + + case FNC_READR: /* read reverse */ + case FNC_WCHKR: /* wcheck = read */ + tufc = 0; /* clear frame count */ + if (st = sim_tape_rdrecr (uptr, xbuf + 4, &tbc, MT_MAXFR)) { /* read rev */ + if (st == MTSE_TMK) tu_set_er (ER_FCE); /* tmk also sets FCE */ + r = tu_map_err (drv, st, 1); /* map error */ + break; /* done */ + } + for (i = 0; i < 4; i++) xbuf[i] = 0; /* pad with 0's */ + if (fmt == TC_CDUMP) { /* core dump? */ + for (i = tbc + 3, j = 0; i > 3; i = i - 4) { + wbuf[j++] = ((uint16) xbuf[i] & 0xF) | + (((uint16) (xbuf[i - 1] & 0xF)) << 4) | + (((uint16) (xbuf[i - 2] & 0xF)) << 8) | + (((uint16) (xbuf[i - 3] & 0xf)) << 12); + } + xbc = (tbc + 1) >> 1; + } + else { /* standard */ + for (i = tbc + 3, j = 0; i > 3; i = i - 2) { + wbuf[j++] = ((uint16) xbuf[i]) | + (((uint16) xbuf[i - 1]) << 8); + } + xbc = tbc; + } + if (mba_get_bc (tu_dib.ba) > xbc) /* record short? */ + tu_set_er (ER_FCE); /* set FCE, ATN */ + if (fnc == FNC_WCHKR) mba_chbufW (tu_dib.ba, xbc, wbuf); + else mba_wrbufW (tu_dib.ba, xbc, wbuf); + tufc = tbc & 0177777; + break; + } /* end case */ + +tucs1 = tucs1 & ~CS1_GO; /* clear go */ +if (fnc >= FNC_XFER) { /* data xfer? */ + mba_set_don (tu_dib.ba); /* set done */ + tu_update_fs (0, drv); /* update fs */ + } +else tu_update_fs (FS_ATA, drv); /* no, set attn */ +if (DEBUG_PRS (tu_dev)) fprintf (sim_deb, + ">>TU%d DONE: fnc=%s, fc=%06o, fs=%06o, er=%06o, pos=%d\n", + drv, tu_fname[fnc], tufc, tufs, tuer, uptr->pos); +return SCPE_OK; +} + +/* Set formatter error */ + +void tu_set_er (int32 flg) +{ +tuer = tuer | flg; +tufs = tufs | FS_ATA; +mba_upd_ata (tu_dib.ba, 1); +return; +} + +/* Clear attention */ + +void tu_clr_as (int32 mask) +{ +if (mask & AS_U0) tufs = tufs & ~FS_ATA; +mba_upd_ata (tu_dib.ba, tufs & FS_ATA); +return; +} + +/* Formatter update status */ + +void tu_update_fs (int32 flg, int32 drv) +{ +int32 act = sim_is_active (&tu_unit[drv]); + +tufs = (tufs & ~FS_DYN) | FS_FPR | flg; +if (tu_unit[drv].flags & UNIT_ATT) { + tufs = tufs | FS_MOL | tu_unit[drv].USTAT; + if (tu_unit[drv].UDENS == TC_1600) tufs = tufs | FS_PE; + if (sim_tape_wrp (&tu_unit[drv])) tufs = tufs | FS_WRL; + if (!act) { + if (sim_tape_bot (&tu_unit[drv])) + tufs = tufs | FS_BOT; + if (sim_tape_eot (&tu_unit[drv])) + tufs = tufs | FS_EOT; + } + } +if (tuer) tufs = tufs | FS_ERR; +if (tufs && !act) tufs = tufs | FS_RDY; +if (flg & FS_ATA) mba_upd_ata (tu_dib.ba, 1); +return; +} + +/* Map tape error status */ + +t_stat tu_map_err (int32 drv, t_stat st, t_bool qdt) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* not attached */ + tu_set_er (ER_NXF); /* can't execute */ + if (qdt) mba_set_exc (tu_dib.ba); /* set exception */ + break; + + case MTSE_TMK: /* end of file */ + tufs = tufs | FS_TMK; + break; + + case MTSE_IOERR: /* IO error */ + tu_set_er (ER_VPE); /* flag error */ + if (qdt) mba_set_exc (tu_dib.ba); /* set exception */ + return (tu_stopioe? SCPE_IOERR: SCPE_OK); + + case MTSE_INVRL: /* invalid rec lnt */ + tu_set_er (ER_VPE); /* flag error */ + if (qdt) mba_set_exc (tu_dib.ba); /* set exception */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + tu_set_er (ER_CRC); /* set crc err */ + if (qdt) mba_set_exc (tu_dib.ba); /* set exception */ + break; + + case MTSE_EOM: /* end of medium */ + tu_set_er (ER_OPI); /* incomplete */ + if (qdt) mba_set_exc (tu_dib.ba); /* set exception */ + break; + + case MTSE_BOT: /* reverse into BOT */ + return SCPE_OK; + + case MTSE_WRP: /* write protect */ + tu_set_er (ER_NXF); /* can't execute */ + if (qdt) mba_set_exc (tu_dib.ba); /* set exception */ + break; + + default: /* unknown error */ + return SCPE_IERR; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tu_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +mba_set_enbdis (MBA_TU, tu_dev.flags & DEV_DIS); +tucs1 = 0; +tufc = 0; +tuer = 0; +tufs = FS_FPR | FS_RDY; +if (sim_switches & SWMASK ('P')) tutc = 0; /* powerup? clr TC */ +else tutc = tutc & ~TC_FCS; /* no, clr */ +for (u = 0; u < TU_NUMDR; u++) { /* loop thru units */ + uptr = tu_dev.units + u; + sim_tape_reset (uptr); /* clear pos flag */ + sim_cancel (uptr); /* cancel activity */ + uptr->USTAT = 0; + } +if (xbuf == NULL) xbuf = (uint8 *) calloc (MT_MAXFR + 4, sizeof (uint8)); +if (xbuf == NULL) return SCPE_MEM; +if (wbuf == NULL) wbuf = (uint16 *) calloc ((MT_MAXFR + 4) >> 1, sizeof (uint16)); +if (wbuf == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat tu_attach (UNIT *uptr, char *cptr) +{ +int32 drv = uptr - tu_dev.units, flg; +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->USTAT = 0; /* clear unit status */ +uptr->UDENS = UD_UNK; /* unknown density */ +flg = FS_ATA | FS_SSC; /* set attention */ +if (GET_DRV (tutc) == drv) flg = flg | FS_SAT; /* sel drv? set SAT */ +tu_update_fs (flg, drv); /* update status */ +return r; +} + +/* Detach routine */ + +t_stat tu_detach (UNIT* uptr) +{ +int32 drv = uptr - tu_dev.units; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +uptr->USTAT = 0; /* clear status flags */ +tu_update_fs (FS_ATA | FS_SSC, drv); /* update status */ +return sim_tape_detach (uptr); +} + +/* Set/show formatter type */ + +t_stat tu_set_fmtr (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr = find_dev_from_unit (uptr); + +if (cptr != NULL) return SCPE_ARG; +if (dptr == NULL) return SCPE_IERR; +if (val) dptr->flags = dptr->flags | DEV_TM03; +else dptr->flags = dptr->flags & ~DEV_TM03; +return SCPE_OK; +} + +t_stat tu_show_fmtr (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr = find_dev_from_unit (uptr); + +if (dptr == NULL) return SCPE_IERR; +fprintf (st, "TM0%d", (dptr->flags & DEV_TM03? 3: 2)); +return SCPE_OK; +} + +/* Device bootstrap */ + +#if defined (PDP11) + +#elif defined (VM_PDP11) + +#define BOOT_START 016000 /* start */ +#define BOOT_ENTRY (BOOT_START + 002) /* entry */ +#define BOOT_UNIT (BOOT_START + 010) /* unit number */ +#define BOOT_CSR (BOOT_START + 014) /* CSR */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (uint16)) + +static const uint16 boot_rom[] = { + 0046515, /* "MM" */ + 0012706, BOOT_START, /* mov #boot_start, sp */ + 0012700, 0000000, /* mov #unit, r0 */ + 0012701, 0172440, /* mov #TUCS1, r1 */ + 0012761, 0000040, 0000010, /* mov #CS2_CLR, 10(r1) ; reset */ + 0012711, 0000021, /* mov #RIP+GO, (r1) ; rip */ + 0010004, /* mov r0, r4 */ + 0052704, 0002300, /* bis #2300, r4 ; set den */ + 0010461, 0000032, /* mov r4, 32(r1) ; set unit */ + 0012761, 0177777, 0000006, /* mov #-1, 6(r1) ; set fc */ + 0012711, 0000031, /* mov #SPCF+GO, (r1) ; skip rec */ + 0105761, 0000012, /* tstb 12 (r1) ; fmtr rdy? */ + 0100375, /* bpl .-4 */ + 0012761, 0177000, 0000002, /* mov #-1000, 2(r1) ; set wc */ + 0005061, 0000004, /* clr 4(r1) ; clr ba */ + 0005061, 0000006, /* clr 6(r1) ; clr fc */ + 0012711, 0000071, /* mov #READ+GO, (r1) ; read */ + 0105711, /* tstb (r1) ; wait */ + 0100376, /* bpl .-2 */ + 0005002, /* clr R2 */ + 0005003, /* clr R3 */ + 0012704, BOOT_START+020, /* mov #start+020, r4 */ + 0005005, /* clr R5 */ + 0105011, /* clrb (r1) */ + 0005007 /* clr PC */ + }; + +t_stat tu_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 *M; + +for (i = 0; i < BOOT_LEN; i++) M[(BOOT_START >> 1) + i] = boot_rom[i]; +M[BOOT_UNIT >> 1] = unitno & (TU_NUMDR - 1); +M[BOOT_CSR >> 1] = mba_get_csr (tu_dib.ba) & DMASK; +saved_PC = BOOT_ENTRY; +return SCPE_OK; +} + +#else + +t_stat tu_boot (int32 unitno, DEVICE *dptr) +{ +return SCPE_NOFNC; +} + +#endif diff --git a/PDP11/pdp11_uqssp.h b/PDP11/pdp11_uqssp.h new file mode 100644 index 0000000..e0e4ce1 --- /dev/null +++ b/PDP11/pdp11_uqssp.h @@ -0,0 +1,169 @@ +/* pdp11_uqssp.h: Unibus/Qbus storage systems port definitions file + + Copyright (c) 2001-2005, Robert M Supnik + Derived from work by Stephen F. Shirron + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Aug-02 RMS Added TMSCP support +*/ + +#ifndef _PDP11_UQSSP_H_ +#define _PDP11_UQSSP_H_ 0 + +/* IP register - initialization and polling + + read - controller polls command queue + write - controller re-initializes +*/ + +/* SA register - status, address, and purge + + read - data and error information + write - host startup information, purge complete +*/ + +#define SA_ER 0x8000 /* error */ +#define SA_S4 0x4000 /* init step 4 */ +#define SA_S3 0x2000 /* init step 3 */ +#define SA_S2 0x1000 /* init step 2 */ +#define SA_S1 0x0800 /* init step 1 */ + +/* Init step 1, controller to host */ + +#define SA_S1C_NV 0x0400 /* fixed vec NI */ +#define SA_S1C_Q22 0x0200 /* Q22 device */ +#define SA_S1C_DI 0x0100 /* ext diags */ +#define SA_S1C_OD 0x0080 /* odd addrs NI */ +#define SA_S1C_MP 0x0040 /* mapping */ +#define SA_S1C_SM 0x0020 /* spec fncs NI */ +#define SA_S1C_CN 0x0010 /* node name NI */ + +/* Init step 1, host to controller */ + +#define SA_S1H_VL 0x8000 /* valid */ +#define SA_S1H_WR 0x4000 /* wrap mode */ +#define SA_S1H_V_CQ 11 /* cmd q len */ +#define SA_S1H_M_CQ 0x7 +#define SA_S1H_V_RQ 8 /* resp q len */ +#define SA_S1H_M_RQ 0x7 +#define SA_S1H_IE 0x0080 /* int enb */ +#define SA_S1H_VEC 0x007F /* vector */ +#define SA_S1H_CQ(x) (1 << (((x) >> SA_S1H_V_CQ) & SA_S1H_M_CQ)) +#define SA_S1H_RQ(x) (1 << (((x) >> SA_S1H_V_RQ) & SA_S1H_M_RQ)) + +/* Init step 2, controller to host */ + +#define SA_S2C_PT 0x0000 /* port type */ +#define SA_S2C_V_EC 8 /* info to echo */ +#define SA_S2C_M_EC 0xFF +#define SA_S2C_EC(x) (((x) >> SA_S2C_V_EC) & SA_S2C_M_EC) + +/* Init step 2, host to controller */ + +#define SA_S2H_CLO 0xFFFE /* comm addr lo */ +#define SA_S2H_PI 0x0001 /* adp prg int */ + +/* Init step 3, controller to host */ + +#define SA_S3C_V_EC 0 /* info to echo */ +#define SA_S3C_M_EC 0xFF +#define SA_S3C_EC(x) (((x) >> SA_S3C_V_EC) & SA_S3C_M_EC) + +/* Init step 3, host to controller */ + +#define SA_S3H_PP 0x8000 /* purge, poll test */ +#define SA_S3H_CHI 0x7FFF /* comm addr hi */ + +/* Init step 4, controller to host */ + +#define SA_S4C_V_MOD 4 /* adapter # */ +#define SA_S4C_V_VER 0 /* version # */ + +/* Init step 4, host to controller */ + +#define SA_S4H_CS 0x0400 /* host scrpad NI */ +#define SA_S4H_NN 0x0200 /* snd node name NI */ +#define SA_S4H_SF 0x0100 /* spec fnc NI */ +#define SA_S4H_LF 0x0002 /* send last fail */ +#define SA_S4H_GO 0x0001 /* go */ + +/* Fatal error codes (generic through 32) */ + +#define PE_PRE 1 /* packet read err */ +#define PE_PWE 2 /* packet write err */ +#define PE_QRE 6 /* queue read err */ +#define PE_QWE 7 /* queue write err */ +#define PE_HAT 9 /* host access tmo */ +#define PE_ICI 14 /* inv conn ident */ +#define PE_PIE 20 /* prot incompat */ +#define PE_PPF 21 /* prg/poll err */ +#define PE_MRE 22 /* map reg rd err */ +#define PE_T11 475 /* T11 err NI */ +#define PE_SND 476 /* SND err NI */ +#define PE_RCV 477 /* RCV err NI */ +#define PE_NSR 478 /* no such rsrc */ + +/* Comm region offsets */ + +#define SA_COMM_QQ -8 /* unused */ +#define SA_COMM_PI -6 /* purge int */ +#define SA_COMM_CI -4 /* cmd int */ +#define SA_COMM_RI -2 /* resp int */ +#define SA_COMM_MAX ((4 << SA_S1H_M_CQ) + (4 << SA_S1H_M_RQ) - SA_COMM_QQ) + +/* Command/response rings */ + +struct uq_ring { + int32 ioff; /* intr offset */ + uint32 ba; /* base addr */ + uint32 lnt; /* size in bytes */ + uint32 idx; /* current index */ + }; + +/* Ring descriptor entry */ + +#define UQ_DESC_OWN 0x80000000 /* ownership */ +#define UQ_DESC_F 0x40000000 /* flag */ +#define UQ_ADDR 0x003FFFFE /* addr, word aligned */ + +/* Packet header */ + +#define UQ_HDR_OFF -4 /* offset */ + +#define UQ_HLNT 0 /* length */ +#define UQ_HCTC 1 /* credits, type, CID */ + +#define UQ_HCTC_V_CR 0 /* credits */ +#define UQ_HCTC_M_CR 0xF +#define UQ_HCTC_V_TYP 4 /* type */ +#define UQ_HCTC_M_TYP 0xF +#define UQ_TYP_SEQ 0 /* sequential */ +#define UQ_TYP_DAT 1 /* datagram */ +#define UQ_HCTC_V_CID 8 /* conn ID */ +#define UQ_HCTC_M_CID 0xFF +#define UQ_CID_MSCP 0 /* MSCP */ +#define UQ_CID_TMSCP 1 /* TMSCP */ +#define UQ_CID_DUP 2 /* DUP */ +#define UQ_CID_DIAG 0xFF /* diagnostic */ + +#endif diff --git a/PDP11/pdp11_vh.c b/PDP11/pdp11_vh.c new file mode 100644 index 0000000..3677431 --- /dev/null +++ b/PDP11/pdp11_vh.c @@ -0,0 +1,1365 @@ +/* pdp11_vh.c: DHQ11 asynchronous terminal multiplexor simulator + + Copyright (c) 2004-2006, John A. Dundas III + Portions derived from work by Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the Author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the Author. + + vh DHQ11 asynch multiplexor for SIMH + + 18-Jun-07 RMS Added UNIT_IDLE flag + 29-Oct-06 RMS Synced poll and clock + 07-Jul-05 RMS Removed extraneous externs + 15-Jun-05 RMS Revised for new autoconfigure interface + Fixed bug in vector display routine + 12-Jun-04 RMS Repair MS2SIMH macro to avoid divide by 0 bug + 08-Jun-04 JAD Repair vh_dev initialization; remove unused + variables, cast to avoid conversion confusion + 07-Jun-04 JAD Complete function prototypes of forward declarations. + Repair broken prototypes of vh_rd() and vh_wr() + Explicitly size integer declarations + 4-Jun-04 JAD Preliminary code: If operating in a PDP-11 Unibus + environment, force DHU mode + 29-May-04 JAD Make certain RX.TIMER is within allowable range + 25-May-04 JAD All time-based operations are scaled by tmxr_poll units + 23-May-04 JAD Change to fifo_get() and dq_tx_report() to avoid + gratuitous stack manipulation + 20-May-04 JAD Made modem control and auto-hangup unit flags + 19-May-04 JAD Fix problem with modem status where the line number + was not being included + 12-May-04 JAD Revised for updated tmxr interfaces + 28-Jan-04 JAD Original creation and testing + +I/O Page Registers + +CSR 17 760 440 (float) + +Vector: 300 (float) + +Priority: BR4 + +Rank: 32 + +*/ +/* MANY constants needed! */ + +#if defined (VM_VAX) +#include "vax_defs.h" +extern int32 int_req[IPL_HLVL]; +#endif + +#if defined (VM_PDP11) +#include "pdp11_defs.h" +extern int32 int_req[IPL_HLVL]; +extern int32 cpu_opt; +#endif + +#include "sim_sock.h" +#include "sim_tmxr.h" + +/* imports from pdp11_stddev.c: */ +extern int32 tmxr_poll, clk_tps; +/* convert ms to SIMH time units based on tmxr_poll polls per second */ +#define MS2SIMH(ms) (((ms) * clk_tps) / 1000) +extern FILE *sim_log; + +#ifndef VH_MUXES +#define VH_MUXES (4) +#endif +#define VH_MNOMASK (VH_MUXES - 1) + +#define VH_LINES (8) + +#define UNIT_V_MODEDHU (UNIT_V_UF + 0) +#define UNIT_V_FASTDMA (UNIT_V_UF + 1) +#define UNIT_V_MODEM (UNIT_V_UF + 2) +#define UNIT_V_HANGUP (UNIT_V_UF + 3) +#define UNIT_MODEDHU (1 << UNIT_V_MODEDHU) +#define UNIT_FASTDMA (1 << UNIT_V_FASTDMA) +#define UNIT_MODEM (1 << UNIT_V_MODEM) +#define UNIT_HANGUP (1 << UNIT_V_HANGUP) + +/* VHCSR - 160440 - Control and Status Register */ + +#define CSR_M_IND_ADDR (017) +#define CSR_SKIP (1 << 4) +#define CSR_MASTER_RESET (1 << 5) +#define CSR_RXIE (1 << 6) +#define CSR_RX_DATA_AVAIL (1 << 7) +#define CSR_M_TX_LINE (017) +#define CSR_V_TX_LINE (8) +#define CSR_TX_DMA_ERR (1 << 12) +#define CSR_DIAG_FAIL (1 << 13) +#define CSR_TXIE (1 << 14) +#define CSR_TX_ACTION (1 << 15) +#define CSR_GETCHAN(x) ((x) & CSR_M_IND_ADDR) +#define CSR_RW \ + (CSR_TXIE|CSR_RXIE|CSR_SKIP|CSR_M_IND_ADDR|CSR_MASTER_RESET) +#define RESET_ABORT (052525) + +/* Receive Buffer (RBUF) */ + +#define FIFO_SIZE (256) +#define FIFO_ALARM (191) +#define FIFO_HALF (FIFO_SIZE / 2) +#define RBUF_M_RX_CHAR (0377) +#define RBUF_M_RX_LINE (07) +#define RBUF_V_RX_LINE (8) +#define RBUF_PARITY_ERR (1 << 12) +#define RBUF_FRAME_ERR (1 << 13) +#define RBUF_OVERRUN_ERR (1 << 14) +#define RBUF_DATA_VALID (1 << 15) +#define RBUF_GETLINE(x) (((x) >> RBUF_V_RX_LINE) & RBUF_M_RX_LINE) +#define RBUF_PUTLINE(x) ((x) << RBUF_V_RX_LINE) +#define RBUF_DIAG \ + (RBUF_PARITY_ERR|RBUF_FRAME_ERR|RBUF_OVERRUN_ERR) +#define XON (021) +#define XOFF (023) + +/* Transmit Character Register (TXCHAR) */ + +#define TXCHAR_M_CHAR (0377) +#define TXCHAR_TX_DATA_VALID (1 << 15) + +/* Receive Timer Register (RXTIMER) */ + +#define RXTIMER_M_RX_TIMER (0377) + +/* Line-Parameter Register (LPR) */ + +#define LPR_DISAB_XRPT (1 << 0) /* not impl. in real DHU */ +#define LPR_V_DIAG (1) +#define LPR_M_DIAG (03) +#define LPR_V_CHAR_LGTH (3) +#define LPR_M_CHAR_LGTH (03) +#define LPR_PARITY_ENAB (1 << 5) +#define LPR_EVEN_PARITY (1 << 6) +#define LPR_STOP_CODE (1 << 7) +#define LPR_V_RX_SPEED (8) +#define LPR_M_RX_SPEED (017) +#define LPR_V_TX_SPEED (12) +#define LPR_M_TX_SPEED (017) + +#define RATE_50 (0) +#define RATE_75 (1) +#define RATE_110 (2) +#define RATE_134 (3) +#define RATE_150 (4) +#define RATE_300 (5) +#define RATE_600 (6) +#define RATE_1200 (7) +#define RATE_1800 (8) +#define RATE_2000 (9) +#define RATE_2400 (10) +#define RATE_4800 (11) +#define RATE_7200 (12) +#define RATE_9600 (13) +#define RATE_19200 (14) +#define RATE_38400 (15) + +/* Line-Status Register (STAT) */ + +#define STAT_DHUID (1 << 8) /* mode: 0=DHV, 1=DHU */ +#define STAT_MDL (1 << 9) /* always 0, has modem support */ +#define STAT_CTS (1 << 11) /* CTS from modem */ +#define STAT_DCD (1 << 12) /* DCD from modem */ +#define STAT_RI (1 << 13) /* RI from modem */ +#define STAT_DSR (1 << 15) /* DSR from modem */ + +/* FIFO Size Register (FIFOSIZE) */ + +#define FIFOSIZE_M_SIZE (0377) + +/* FIFO Data Register (FIFODATA) */ + +#define FIFODATA_W0 (0377) +#define FIFODATA_V_W1 (8) +#define FIFODATA_M_W1 (0377) + +/* Line-Control Register (LNCTRL) */ + +#define LNCTRL_TX_ABORT (1 << 0) +#define LNCTRL_IAUTO (1 << 1) +#define LNCTRL_RX_ENA (1 << 2) +#define LNCTRL_BREAK (1 << 3) +#define LNCTRL_OAUTO (1 << 4) +#define LNCTRL_FORCE_XOFF (1 << 5) +#define LNCTRL_V_MAINT (6) +#define LNCTRL_M_MAINT (03) +#define LNCTRL_LINK_TYPE (1 << 8) /* 0=data leads only, 1=modem */ +#define LNCTRL_DTR (1 << 9) /* DTR to modem */ +#define LNCTRL_RTS (1 << 12) /* RTS to modem */ + +/* Transmit Buffer Address Register Number 1 (TBUFFAD1) */ + +/* Transmit Buffer Address Register Number 2 (TBUFFAD2) */ + +#define TB2_M_TBUFFAD (077) +#define TB2_TX_DMA_START (1 << 7) +#define TB2_TX_ENA (1 << 15) + +/* Transmit DMA Buffer Counter (TBUFFCT) */ + +/* Self-Test Error Codes */ + +#define SELF_NULL (0201) +#define SELF_SKIP (0203) +#define SELF_OCT (0211) +#define SELF_RAM (0225) +#define SELF_RCD (0231) +#define SELF_DRD (0235) + +#define BMP_OK (0305) +#define BMP_BAD (0307) + +/* Loopback types */ + +#define LOOP_NONE (0) +#define LOOP_H325 (1) +#define LOOP_H3101 (2) /* p.2-13 DHQ manual */ +/* Local storage */ + +static uint16 vh_csr[VH_MUXES] = { 0 }; /* CSRs */ +static uint16 vh_timer[VH_MUXES] = { 1 }; /* controller timeout */ +static uint16 vh_mcount[VH_MUXES] = { 0 }; +static uint32 vh_timeo[VH_MUXES] = { 0 }; +static uint32 vh_ovrrun[VH_MUXES] = { 0 }; /* line overrun bits */ +/* XOFF'd channels, one bit/channel */ +static uint32 vh_stall[VH_MUXES] = { 0 }; +static uint16 vh_loop[VH_MUXES] = { 0 }; /* loopback status */ + +/* One bit per controller: */ +static uint32 vh_rxi = 0; /* rcv interrupts */ +static uint32 vh_txi = 0; /* xmt interrupts */ +static uint32 vh_crit = 0; /* FIFO.CRIT */ + +static const int32 bitmask[4] = { 037, 077, 0177, 0377 }; + +/* RX FIFO state */ + +static int32 rbuf_idx[VH_MUXES] = { 0 };/* index into vh_rbuf */ +static uint32 vh_rbuf[VH_MUXES][FIFO_SIZE] = { 0 }; + +/* TXQ state */ + +#define TXQ_SIZE (16) +static int32 txq_idx[VH_MUXES] = { 0 }; +static uint32 vh_txq[VH_MUXES][TXQ_SIZE] = { 0 }; + +/* Need to extend the TMLN structure */ + +typedef struct { + TMLN *tmln; + uint16 lpr; /* line parameters */ + uint16 lnctrl; /* line control */ + uint16 lstat; /* line modem status */ + uint16 tbuffct; /* remaining character count */ + uint16 tbuf1; + uint16 tbuf2; + uint16 txchar; /* single character I/O */ +} TMLX; + +static TMLN vh_ldsc[VH_MUXES * VH_LINES] = { 0 }; +static TMXR vh_desc = { VH_MUXES * VH_LINES, 0, 0, vh_ldsc }; +static TMLX vh_parm[VH_MUXES * VH_LINES] = { 0 }; + +/* Forward references */ +static t_stat vh_rd (int32 *data, int32 PA, int32 access); +static t_stat vh_wr (int32 data, int32 PA, int32 access); +static t_stat vh_svc (UNIT *uptr); +static int32 vh_rxinta (void); +static int32 vh_txinta (void); +static t_stat vh_clear (int32 vh, t_bool flag); +static t_stat vh_reset (DEVICE *dptr); +static t_stat vh_attach (UNIT *uptr, char *cptr); +static t_stat vh_detach (UNIT *uptr); +static t_stat vh_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_show (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_show_debug (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_show_rbuf (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_show_txq (FILE *st, UNIT *uptr, int32 val, void *desc); +static t_stat vh_putc (int32 vh, TMLX *lp, int32 chan, int32 data); +static void doDMA (int32 vh, int32 chan); +static t_stat vh_summ (FILE *st, UNIT *uptr, int32 val, void *desc); + +int32 tmxr_send_buffered_data (TMLN *lp); + +/* SIMH I/O Structures */ + +static DIB vh_dib = { + IOBA_VH, + IOLN_VH * VH_MUXES, + &vh_rd, /* read */ + &vh_wr, /* write */ + 2, /* # of vectors */ + IVCL (VHRX), + VEC_VHRX, + { &vh_rxinta, &vh_txinta } /* int. ack. routines */ +}; + +static UNIT vh_unit[VH_MUXES] = { + { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, + { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, + { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, + { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, +}; + +static const REG vh_nlreg = { DRDATA (NLINES, vh_desc.lines, 6), PV_LEFT }; + +static const REG vh_reg[] = { + { BRDATA (CSR, vh_csr, DEV_RDX, 16, VH_MUXES) }, + { GRDATA (DEVADDR, vh_dib.ba, DEV_RDX, 32, 0), REG_HRO }, + { GRDATA (DEVVEC, vh_dib.vec, DEV_RDX, 16, 0), REG_HRO }, + { NULL } +}; + +static const MTAB vh_mod[] = { + { UNIT_MODEDHU, 0, "DHV mode", "DHV", NULL }, + { UNIT_MODEDHU, UNIT_MODEDHU, "DHU mode", "DHU", NULL }, + { UNIT_FASTDMA, 0, NULL, "NORMAL", NULL }, + { UNIT_FASTDMA, UNIT_FASTDMA, "fast DMA", "FASTDMA", NULL }, + { UNIT_MODEM, 0, NULL, "NOMODEM", NULL }, + { UNIT_MODEM, UNIT_MODEM, "modem", "MODEM", NULL }, + { UNIT_HANGUP, 0, NULL, "NOHANGUP", NULL }, + { UNIT_HANGUP, UNIT_HANGUP, "hangup", "HANGUP", NULL }, + { MTAB_XTD|MTAB_VDV, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", "VECTOR", + &set_vec, &vh_show_vec, NULL }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "AUTOCONFIGURE", + &set_addr_flt, NULL, NULL }, + /* this one is dangerous, don't use yet */ + { MTAB_XTD|MTAB_VDV|MTAB_VAL, 0, "lines", "LINES", + NULL, NULL, (REG *)&vh_nlreg }, + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &vh_summ }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &vh_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &vh_show, NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &vh_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "DEBUG", NULL, + NULL, &vh_show_debug, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "RBUF", NULL, + NULL, &vh_show_rbuf, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "TXQ", NULL, + NULL, &vh_show_txq, NULL }, + { 0 } +}; + +DEVICE vh_dev = { + "VH", /* name */ + vh_unit, /* units */ + (REG *)vh_reg, /* registers */ + (MTAB *)vh_mod, /* modifiers */ + VH_MUXES, /* # units */ + DEV_RDX, /* address radix */ + 8, /* address width */ + 1, /* address increment */ + DEV_RDX, /* data radix */ + 8, /* data width */ + NULL, /* examine routine */ + NULL, /* deposit routine */ + &vh_reset, /* reset routine */ + NULL, /* boot routine */ + &vh_attach, /* attach routine */ + &vh_detach, /* detach routine */ + (void *)&vh_dib, /* context */ + DEV_FLTA | DEV_DISABLE | DEV_DIS |DEV_NET | DEV_QBUS | DEV_UBUS, /* flags */ +}; + +/* Interrupt routines */ + +static void vh_clr_rxint ( int32 vh ) +{ + vh_rxi &= ~(1 << vh); + if (vh_rxi == 0) + CLR_INT (VHRX); + else + SET_INT (VHRX); +} + +static void vh_set_rxint ( int32 vh ) +{ + vh_rxi |= (1 << vh); + SET_INT (VHRX); +} + +/* RX interrupt ack. (bus cycle) */ + +static int32 vh_rxinta (void) +{ + int32 vh; + + for (vh = 0; vh < VH_MUXES; vh++) { + if (vh_rxi & (1 << vh)) { + vh_clr_rxint (vh); + return (vh_dib.vec + (vh * 010)); + } + } + return (0); +} + +static void vh_clr_txint ( int32 vh ) +{ + vh_txi &= ~(1 << vh); + if (vh_txi == 0) + CLR_INT (VHTX); + else + SET_INT (VHTX); +} + +static void vh_set_txint ( int32 vh ) +{ + vh_txi |= (1 << vh); + SET_INT (VHTX); +} + +/* TX interrupt ack. (bus cycle) */ + +static int32 vh_txinta (void) +{ + int32 vh; + + for (vh = 0; vh < VH_MUXES; vh++) { + if (vh_txi & (1 << vh)) { + vh_clr_txint (vh); + return (vh_dib.vec + 4 + (vh * 010)); + } + } + return (0); +} +/* RX FIFO get/put routines */ + +/* return 0 on success, -1 on FIFO overflow */ + +static int32 fifo_put ( int32 vh, + TMLX *lp, + int32 data ) +{ + int32 status = 0; + + if (lp == NULL) + goto override; + /* this might have to move to vh_getc() */ + if ((lp->lnctrl & LNCTRL_OAUTO) && ((data & RBUF_DIAG) == 0)) { + TMLX *l0p; + /* implement transmitted data flow control */ + switch (data & 0377) { + case XON: + lp->tbuf2 |= TB2_TX_ENA; + goto common; + case XOFF: + lp->tbuf2 &= ~TB2_TX_ENA; + common: + /* find line 0 for this controller */ + l0p = &vh_parm[vh * VH_LINES]; + if (l0p->lpr & LPR_DISAB_XRPT) + return (0); + break; + default: + break; + } + } +/* BUG: which of the following 2 is correct? */ + /* if ((data & RBUF_DIAG) == RBUF_DIAG) */ + if (data & RBUF_DIAG) + goto override; + if (((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) == 2) + goto override; + if (!(lp->lnctrl & LNCTRL_RX_ENA)) + return (0); +override: + vh_csr[vh] |= CSR_RX_DATA_AVAIL; + if (rbuf_idx[vh] < FIFO_SIZE) { + vh_rbuf[vh][rbuf_idx[vh]] = data; + rbuf_idx[vh] += 1; + } else { + vh_ovrrun[vh] |= (1 << RBUF_GETLINE (data)); + status = -1; + } + if (vh_csr[vh] & CSR_RXIE) { + if (vh_unit[vh].flags & UNIT_MODEDHU) { + /* was it a modem status change? */ + if ((data & RBUF_DIAG) == RBUF_DIAG) + vh_set_rxint (vh); + /* look for FIFO alarm @ 3/4 full */ + else if (rbuf_idx[vh] == FIFO_ALARM) + vh_set_rxint (vh); + else if (vh_timer[vh] == 0) + ; /* nothing, infinite timeout */ + else if (vh_timer[vh] == 1) + vh_set_rxint (vh); + else if (vh_timeo[vh] == 0) + vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; + } else { + /* Interrupt on transition _from_ an empty FIFO */ + if (rbuf_idx[vh] == 1) + vh_set_rxint (vh); + } + } + if (rbuf_idx[vh] > FIFO_ALARM) + vh_crit |= (1 << vh); + /* Implement RX FIFO-level flow control */ + if (lp != NULL) { + if ((lp->lnctrl & LNCTRL_FORCE_XOFF) || + ((vh_crit & (1 << vh)) && (lp->lnctrl & LNCTRL_IAUTO))) { + int32 chan = RBUF_GETLINE(data); + vh_stall[vh] ^= (1 << chan); + /* send XOFF every other character received */ + if (vh_stall[vh] & (1 << chan)) + vh_putc (vh, lp, chan, XOFF); + } + } + return (status); +} + +static int32 fifo_get ( int32 vh ) +{ + int32 data, i; + + if (rbuf_idx[vh] == 0) { + vh_csr[vh] &= ~CSR_RX_DATA_AVAIL; + return (0); + } + /* pick off the first character, mark valid */ + data = vh_rbuf[vh][0] | RBUF_DATA_VALID; + /* move the remainder up */ + rbuf_idx[vh] -= 1; + for (i = 0; i < rbuf_idx[vh]; i++) + vh_rbuf[vh][i] = vh_rbuf[vh][i + 1]; + /* rbuf_idx[vh] -= 1; */ + /* look for any previous overruns */ + if (vh_ovrrun[vh]) { + for (i = 0; i < VH_LINES; i++) { + if (vh_ovrrun[vh] & (1 << i)) { + fifo_put (vh, NULL, RBUF_OVERRUN_ERR | + RBUF_PUTLINE (i)); + vh_ovrrun[vh] &= ~(1 << i); + break; + } + } + } + /* recompute FIFO alarm condition */ + if ((rbuf_idx[vh] < FIFO_HALF) && (vh_crit & (1 << vh))) { + vh_crit &= ~(1 << vh); + /* send XON to all XOFF'd channels on this controller */ + for (i = 0; i < VH_LINES; i++) { + TMLX *lp = &vh_parm[(vh * VH_LINES) + i]; + if (lp->lnctrl & LNCTRL_FORCE_XOFF) + continue; + if (vh_stall[vh] & (1 << i)) { + vh_putc (vh, NULL, i, XON); + vh_stall[vh] &= ~(1 << i); + } + } + } + return (data & 0177777); +} +/* TX Q manipulation */ + +static int32 dq_tx_report ( int32 vh ) +{ + int32 data, i; + + if (txq_idx[vh] == 0) + return (0); + data = vh_txq[vh][0]; + txq_idx[vh] -= 1; + for (i = 0; i < txq_idx[vh]; i++) + vh_txq[vh][i] = vh_txq[vh][i + 1]; + /* txq_idx[vh] -= 1; */ + return (data & 0177777); +} + +static void q_tx_report ( int32 vh, + int32 data ) +{ + if (vh_csr[vh] & CSR_TXIE) + vh_set_txint (vh); + if (txq_idx[vh] >= TXQ_SIZE) { +/* BUG: which of the following 2 is correct? */ + dq_tx_report (vh); + /* return; */ + } + vh_txq[vh][txq_idx[vh]] = CSR_TX_ACTION | data; + txq_idx[vh] += 1; +} +/* Channel get/put routines */ + +static void HangupModem ( int32 vh, + TMLX *lp, + int32 chan ) +{ + if (vh_unit[vh].flags & UNIT_MODEM) + lp->lstat &= ~(STAT_DCD|STAT_DSR|STAT_CTS|STAT_RI); + if (lp->lnctrl & LNCTRL_LINK_TYPE) + /* RBUF<0> = 0 for modem status */ + fifo_put (vh, lp, RBUF_DIAG | + RBUF_PUTLINE (chan) | + ((lp->lstat >> 8) & 0376)); + /* BUG: check for overflow above */ +} + +/* TX a character on a line, regardless of the TX enable state */ + +static t_stat vh_putc ( int32 vh, + TMLX *lp, + int32 chan, + int32 data ) +{ + int32 val; + t_stat status = SCPE_OK; + + /* truncate to desired character length */ + data &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & LPR_M_CHAR_LGTH]; + switch ((lp->lnctrl >> LNCTRL_V_MAINT) & LNCTRL_M_MAINT) { + case 0: /* normal */ +#if 0 + /* check for (external) loopback setting */ + switch (vh_loop[vh]) { + default: + case LOOP_NONE: + break; + } +#endif + status = tmxr_putc_ln (lp->tmln, data); + if (status == SCPE_LOST) { + tmxr_reset_ln (lp->tmln); + HangupModem (vh, lp, chan); + } else if (status == SCPE_STALL) { + /* let's flush and try again */ + tmxr_send_buffered_data (lp->tmln); + status = tmxr_putc_ln (lp->tmln, data); + } + break; + case 1: /* auto echo */ + break; + case 2: /* local loopback */ + if (lp->lnctrl & LNCTRL_BREAK) + val = fifo_put (vh, lp, + RBUF_FRAME_ERR | RBUF_PUTLINE (chan)); + else + val = fifo_put (vh, lp, + RBUF_PUTLINE (chan) | data); + status = (val < 0) ? SCPE_TTMO : SCPE_OK; + break; + default: /* remote loopback */ + break; + } + return (status); +} + +/* Retrieve all stored input from TMXR and place in RX FIFO */ + +static void vh_getc ( int32 vh ) +{ + uint32 i, c; + TMLX *lp; + + for (i = 0; i < VH_LINES; i++) { + lp = &vh_parm[(vh * VH_LINES) + i]; + while (c = tmxr_getc_ln (lp->tmln)) { + if (c & SCPE_BREAK) { + fifo_put (vh, lp, + RBUF_FRAME_ERR | RBUF_PUTLINE (i)); + /* BUG: check for overflow above */ + } else { + c &= bitmask[(lp->lpr >> LPR_V_CHAR_LGTH) & + LPR_M_CHAR_LGTH]; + fifo_put (vh, lp, RBUF_PUTLINE (i) | c); + /* BUG: check for overflow above */ + } + } + } +} + +/* I/O dispatch routines */ + +static t_stat vh_rd ( int32 *data, + int32 PA, + int32 access ) +{ + int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line; + TMLX *lp; + + switch ((PA >> 1) & 7) { + case 0: /* CSR */ + *data = vh_csr[vh] | dq_tx_report (vh); + vh_csr[vh] &= ~0117400; /* clear the read-once bits */ + break; + case 1: /* RBUF */ + *data = fifo_get (vh); + break; + case 2: /* LPR */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->lpr; + break; + case 3: /* STAT/FIFOSIZE */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = (lp->lstat & ~0377) | /* modem status */ +#if 0 + (64 - tmxr_tqln (lp->tmln)); +fprintf (stderr, "\rtqln %d\n", 64 - tmxr_tqln (lp->tmln)); +#else + 64; +#endif + break; + case 4: /* LNCTRL */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->lnctrl; + break; + case 5: /* TBUFFAD1 */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->tbuf1; + break; + case 6: /* TBUFFAD2 */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->tbuf2; + break; + case 7: /* TBUFFCT */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) { + *data = 0; + break; + } + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + *data = lp->tbuffct; + break; + default: + /* can't happen */ + break; + } + return (SCPE_OK); +} + +static t_stat vh_wr ( int32 data, + int32 PA, + int32 access ) +{ + int32 vh = ((PA - vh_dib.ba) >> 4) & VH_MNOMASK, line; + TMLX *lp; + + switch ((PA >> 1) & 7) { + case 0: /* CSR, but no read-modify-write */ + if (access == WRITEB) + data = (PA & 1) ? + (vh_csr[vh] & 0377) | (data << 8) : + (vh_csr[vh] & ~0377) | data & 0377; + if (data & CSR_MASTER_RESET) { + if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP)) + data &= ~CSR_MASTER_RESET; + sim_activate (&vh_unit[vh], clk_cosched (tmxr_poll)); + /* vh_mcount[vh] = 72; */ /* 1.2 seconds */ + vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ + } + if ((data & CSR_RXIE) == 0) + vh_clr_rxint (vh); + /* catch the RXIE transition if the FIFO is not empty */ + else if (((vh_csr[vh] & CSR_RXIE) == 0) && + (rbuf_idx[vh] != 0)) { + if (vh_unit[vh].flags & UNIT_MODEDHU) { + if (rbuf_idx[vh] > FIFO_ALARM) + vh_set_rxint (vh); + else if (vh_timer[vh] == 0) + ; + else if (vh_timer[vh] == 1) + vh_set_rxint (vh); + else if (vh_timeo[vh] == 0) + vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; + } else { + vh_set_rxint (vh); + } + } + if ((data & CSR_TXIE) == 0) + vh_clr_txint (vh); + else if (((vh_csr[vh] & CSR_TXIE) == 0) && + (txq_idx[vh] != 0)) + vh_set_txint (vh); + vh_csr[vh] = (vh_csr[vh] & ~((uint16) CSR_RW)) | (data & (uint16) CSR_RW); + break; + case 1: /* TXCHAR/RXTIMER */ + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (vh_unit[vh].flags & UNIT_MODEDHU) { + if (CSR_GETCHAN (vh_csr[vh]) != 0) + break; + if (access == WRITEB) + data = (PA & 1) ? + (vh_timer[vh] & 0377) | (data << 8) : + (vh_timer[vh] & ~0377) | (data & 0377); + vh_timer[vh] = data & 0377; +#if 0 + if (vh_csr[vh] & CSR_RXIE) { + if (rbuf_idx[vh] > FIFO_ALARM) + vh_set_rxint (vh); + else if (vh_timer[vh] == 0) + ; + else if (vh_timer[vh] == 1) + vh_set_rxint (vh); + else if (vh_timeo[vh] == 0) + vh_timeo[vh] = MS2SIMH (vh_timer[vh]) + 1; + } +#endif + } else { + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->txchar & 0377) | (data << 8) : + (lp->txchar & ~0377) | (data & 0377); + lp->txchar = data; /* TXCHAR */ + if (lp->txchar & TXCHAR_TX_DATA_VALID) { + if (lp->tbuf2 & TB2_TX_ENA) + vh_putc (vh, lp, + CSR_GETCHAN (vh_csr[vh]), + lp->txchar); + q_tx_report (vh, + CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); + lp->txchar &= ~TXCHAR_TX_DATA_VALID; + } + } + break; + case 2: /* LPR */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->lpr & 0377) | (data << 8) : + (lp->lpr & ~0377) | data & 0377; + /* Modify only if CSR<3:0> == 0 */ + if (CSR_GETCHAN (vh_csr[vh]) != 0) + data &= ~LPR_DISAB_XRPT; + lp->lpr = data; + if (((lp->lpr >> LPR_V_DIAG) & LPR_M_DIAG) == 1) { + fifo_put (vh, lp, + RBUF_DIAG | + RBUF_PUTLINE (CSR_GETCHAN (vh_csr[vh])) | + BMP_OK); + /* BUG: check for overflow above */ + lp->lpr &= ~(LPR_M_DIAG << LPR_V_DIAG); + } + break; + case 3: /* STAT/FIFODATA */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (vh_unit[vh].flags & UNIT_MODEDHU) { + /* high byte writes not allowed */ + if (PA & 1) + break; + /* transmit 1 or 2 characters */ + if (!(lp->tbuf2 & TB2_TX_ENA)) + break; + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), data); + q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); + if (access != WRITEB) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), + data >> 8); + } + break; + case 4: /* LNCTRL */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->lnctrl & 0377) | (data << 8) : + (lp->lnctrl & ~0377) | data & 0377; + /* catch the abort TX transition */ + if (!(lp->lnctrl & LNCTRL_TX_ABORT) && + (data & LNCTRL_TX_ABORT)) { + if ((lp->tbuf2 & TB2_TX_ENA) && + (lp->tbuf2 & TB2_TX_DMA_START)) { + lp->tbuf2 &= ~TB2_TX_DMA_START; + q_tx_report (vh, CSR_GETCHAN (vh_csr[vh]) << CSR_V_TX_LINE); + } + } + /* Implement program-initiated flow control */ + if ( (data & LNCTRL_FORCE_XOFF) && + !(lp->lnctrl & LNCTRL_FORCE_XOFF) ) { + if (!(lp->lnctrl & LNCTRL_IAUTO)) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XOFF); + } else if ( !(data & LNCTRL_FORCE_XOFF) && + (lp->lnctrl & LNCTRL_FORCE_XOFF) ) { + if (!(lp->lnctrl & LNCTRL_IAUTO)) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); + else if (!(vh_crit & (1 << vh)) && + (vh_stall[vh] & (1 << CSR_GETCHAN (vh_csr[vh])))) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); + } + if ( (data & LNCTRL_IAUTO) && /* IAUTO 0->1 */ + !(lp->lnctrl & LNCTRL_IAUTO) ) { + if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) { + if (vh_crit & (1 << vh)) { + vh_putc (vh, lp, + CSR_GETCHAN (vh_csr[vh]), XOFF); + vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])); + } + } else { + /* vh_stall[vh] |= (1 << CSR_GETCHAN (vh_csr[vh])) */; + } + } else if ( !(data & LNCTRL_IAUTO) && + (lp->lnctrl & LNCTRL_IAUTO) ) { + if (!(lp->lnctrl & LNCTRL_FORCE_XOFF)) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), XON); + } + /* check modem control bits */ + if ( !(data & LNCTRL_DTR) && /* DTR 1->0 */ + (lp->lnctrl & LNCTRL_DTR)) { + if ((lp->tmln->conn) && (vh_unit[vh].flags & UNIT_HANGUP)) { + tmxr_linemsg (lp->tmln, "\r\nLine hangup\r\n"); + tmxr_reset_ln (lp->tmln); + } + HangupModem (vh, lp, CSR_GETCHAN (vh_csr[vh])); + } + lp->lnctrl = data; + lp->tmln->rcve = (data & LNCTRL_RX_ENA) ? 1 : 0; + tmxr_poll_rx (&vh_desc); + vh_getc (vh); + if (lp->lnctrl & LNCTRL_BREAK) + vh_putc (vh, lp, CSR_GETCHAN (vh_csr[vh]), 0); + break; + case 5: /* TBUFFAD1 */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->tbuf1 & 0377) | (data << 8) : + (lp->tbuf1 & ~0377) | data & 0377; + lp->tbuf1 = data; + break; + case 6: /* TBUFFAD2 */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->tbuf2 & 0377) | (data << 8) : + (lp->tbuf2 & ~0377) | data & 0377; + lp->tbuf2 = data; + /* if starting a DMA, clear DMA_ERR */ + if (vh_unit[vh].flags & UNIT_FASTDMA) { + doDMA (vh, CSR_GETCHAN (vh_csr[vh])); + tmxr_send_buffered_data (lp->tmln); + } + break; + case 7: /* TBUFFCT */ + if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { + vh_mcount[vh] = 1; + break; + } + if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) + break; + line = (vh * VH_LINES) + CSR_GETCHAN (vh_csr[vh]); + lp = &vh_parm[line]; + if (access == WRITEB) + data = (PA & 1) ? + (lp->tbuffct & 0377) | (data << 8) : + (lp->tbuffct & ~0377) | data & 0377; + lp->tbuffct = data; + break; + default: + /* can't happen */ + break; + } + return (SCPE_OK); +} + +static void doDMA ( int32 vh, + int32 chan ) +{ + int32 line, status; + uint32 pa; + TMLX *lp; + + line = (vh * VH_LINES) + chan; + lp = &vh_parm[line]; + if ((lp->tbuf2 & TB2_TX_ENA) && (lp->tbuf2 & TB2_TX_DMA_START)) { +/* BUG: should compare against available xmit buffer space */ + pa = lp->tbuf1; + pa |= (lp->tbuf2 & TB2_M_TBUFFAD) << 16; + status = chan << CSR_V_TX_LINE; + while (lp->tbuffct) { + uint8 buf; + if (Map_ReadB (pa, 1, &buf)) { + status |= CSR_TX_DMA_ERR; + lp->tbuffct = 0; + break; + } + if (vh_putc (vh, lp, chan, buf) != SCPE_OK) + break; + /* pa = (pa + 1) & PAMASK; */ + pa = (pa + 1) & ((1 << 22) - 1); + lp->tbuffct--; + } + lp->tbuf1 = pa & 0177777; + lp->tbuf2 = (lp->tbuf2 & ~TB2_M_TBUFFAD) | + ((pa >> 16) & TB2_M_TBUFFAD); + if (lp->tbuffct == 0) { + lp->tbuf2 &= ~TB2_TX_DMA_START; + q_tx_report (vh, status); + } + } +} + +/* Perform many of the functions of PROC2 */ + +static t_stat vh_svc ( UNIT *uptr ) +{ + int32 vh, newln, i; + + /* scan all muxes for countdown reset */ + for (vh = 0; vh < VH_MUXES; vh++) { + if (vh_csr[vh] & CSR_MASTER_RESET) { + if (vh_mcount[vh] != 0) + vh_mcount[vh] -= 1; + else + vh_clear (vh, FALSE); + } + } + /* sample every 10ms for modem changes (new connections) */ + newln = tmxr_poll_conn (&vh_desc); + if (newln >= 0) { + TMLX *lp; + int32 line; + vh = newln / VH_LINES; /* determine which mux */ + line = newln - (vh * VH_LINES); + lp = &vh_parm[newln]; + lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS; + if (!(lp->lnctrl & LNCTRL_DTR)) + lp->lstat |= STAT_RI; + if (lp->lnctrl & LNCTRL_LINK_TYPE) + fifo_put (vh, lp, RBUF_DIAG | + RBUF_PUTLINE (line) | + ((lp->lstat >> 8) & 0376)); + /* BUG: should check for overflow above */ + } + /* scan all muxes, lines for DMA to complete; start every 3.12ms */ + for (vh = 0; vh < VH_MUXES; vh++) { + for (i = 0; i < VH_LINES; i++) + doDMA (vh, i); + } + /* interrupt driven in a real DHQ */ + tmxr_poll_rx (&vh_desc); + for (vh = 0; vh < VH_MUXES; vh++) + vh_getc (vh); + tmxr_poll_tx (&vh_desc); + /* scan all DHU-mode muxes for RX FIFO timeout */ + for (vh = 0; vh < VH_MUXES; vh++) { + if (vh_unit[vh].flags & UNIT_MODEDHU) { + if (vh_timeo[vh] && (vh_csr[vh] & CSR_RXIE)) { + vh_timeo[vh] -= 1; + if ((vh_timeo[vh] == 0) && rbuf_idx[vh]) + vh_set_rxint (vh); + } + } + } + sim_activate (uptr, tmxr_poll); /* requeue ourselves */ + return (SCPE_OK); +} + +/* init a channel on a controller */ + +/* set for: +send/receive 9600 +8 data bits +1 stop bit +no parity +parity odd +auto-flow off +RX disabled +TX enabled +no break on line +no loopback +link type set to data-leads only +DTR & RTS off +DMA character counter 0 +DMA start address registers 0 +TX_DMA_START 0 +TX_ABORT 0 +auto-flow reports enabled +FIFO size set to 64 +*/ + +static void vh_init_chan ( int32 vh, + int32 chan ) +{ + int32 line; + TMLX *lp; + + line = (vh * VH_LINES) + chan; + lp = &vh_parm[line]; + lp->lpr = (RATE_9600 << LPR_V_TX_SPEED) | + (RATE_9600 << LPR_V_RX_SPEED) | + (03 << LPR_V_CHAR_LGTH); + lp->lnctrl = 0; + lp->lstat &= ~(STAT_MDL | STAT_DHUID | STAT_RI); + if (vh_unit[vh].flags & UNIT_MODEDHU) + lp->lstat |= STAT_DHUID | 64; + if (!(vh_unit[vh].flags & UNIT_MODEM)) + lp->lstat |= STAT_DSR | STAT_DCD | STAT_CTS; + lp->tmln->xmte = 1; + lp->tmln->rcve = 0; + lp->tbuffct = 0; + lp->tbuf1 = 0; + lp->tbuf2 = TB2_TX_ENA; + lp->txchar = 0; +} + +/* init a controller; flag true if BINIT, false if master.reset */ + +static t_stat vh_clear ( int32 vh, + t_bool flag ) +{ + int32 i; + + txq_idx[vh] = 0; + rbuf_idx[vh] = 0; + /* put 8 diag bytes in FIFO: 6 SELF_x, 2 circuit revision codes */ + if (vh_csr[vh] & CSR_SKIP) { + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_SKIP); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105); + } else { + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(0) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(1) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(2) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(3) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(4) | SELF_NULL); + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(5) | SELF_NULL); + /* PROC2 ver. 1 */ + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(6) | 0107); + /* PROC1 ver. 1 */ + fifo_put (vh, NULL, RBUF_DIAG | RBUF_PUTLINE(7) | 0105); + } + vh_csr[vh] &= ~(CSR_TX_ACTION|CSR_DIAG_FAIL|CSR_MASTER_RESET); + if (flag) + vh_csr[vh] &= ~(CSR_TXIE|CSR_RXIE|CSR_SKIP); + vh_csr[vh] |= CSR_TX_DMA_ERR | (CSR_M_TX_LINE << CSR_V_TX_LINE); + vh_clr_rxint (vh); + vh_clr_txint (vh); + vh_timer[vh] = 1; + vh_timeo[vh] = 0; + vh_ovrrun[vh] = 0; + for (i = 0; i < VH_LINES; i++) + vh_init_chan (vh, i); + vh_crit &= ~(1 << vh); + vh_stall[vh] = 0; + vh_loop[vh] = LOOP_NONE; + return (SCPE_OK); +} + +/* Reset all controllers. Used by BINIT and RESET. */ + +static t_stat vh_reset ( DEVICE *dptr ) +{ + int32 i; + + for (i = 0; i < (VH_MUXES * VH_LINES); i++) + vh_parm[i].tmln = &vh_ldsc[i]; + for (i = 0; i < VH_MUXES; i++) { +#if defined (VM_PDP11) + /* if Unibus, force DHU mode */ + if (UNIBUS) + vh_unit[i].flags |= UNIT_MODEDHU; +#endif + vh_clear (i, TRUE); + } + vh_rxi = vh_txi = 0; + CLR_INT (VHRX); + CLR_INT (VHTX); + for (i = 0; i < VH_MUXES; i++) + sim_cancel (&vh_unit[i]); + return (auto_config (dptr->name, (dptr->flags & DEV_DIS) ? 0 : VH_MUXES)); +} + + +static t_stat vh_attach ( UNIT *uptr, + char *cptr ) +{ + if (uptr == &vh_unit[0]) + return (tmxr_attach (&vh_desc, uptr, cptr)); + return (SCPE_NOATT); +} + +static t_stat vh_detach ( UNIT *uptr ) +{ + return (tmxr_detach (&vh_desc, uptr)); +} + +static t_stat vh_summ ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ +int32 i, t; + +for (i = t = 0; i < vh_desc.lines; i++) { /* get num conn */ + if (vh_ldsc[i].conn) t = t + 1; } +fprintf (st, "%d %s", t, (t == 1) ? "connection" : "connections"); +return SCPE_OK; +} + +static t_stat vh_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < vh_desc.lines; i++) { /* loop thru conn */ + if (vh_ldsc[i].conn) { + t = 1; + if (val) tmxr_fconns (st, &vh_ldsc[i], i); + else tmxr_fstats (st, &vh_ldsc[i], i); } } +if (t == 0) fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +static t_stat vh_show_vec ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + return (show_vec (st, uptr, ((vh_desc.lines * 2) / VH_LINES), desc)); +} + +static void debug_line ( FILE *st, + int32 vh, + int32 chan ) +{ + int32 line; + TMLX *lp; + + line = (vh * VH_LINES) + chan; + lp = &vh_parm[line]; + fprintf (st, "\tline %d\tlpr %06o, lnctrl %06o, lstat %06o\n", + chan, lp->lpr, lp->lnctrl, lp->lstat); + fprintf (st, "\t\ttbuffct %06o, tbuf1 %06o, tbuf2 %06o, txchar %06o\n", + lp->tbuffct, lp->tbuf1, lp->tbuf2, lp->txchar); + fprintf (st, "\t\ttmln rcve %d xmte %d\n", + lp->tmln->rcve, lp->tmln->xmte); +} + +static t_stat vh_show_debug ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + int32 i, j; + + fprintf (st, "VH:\trxi %d, txi %d\n", vh_rxi, vh_txi); + for (i = 0; i < VH_MUXES; i++) { + fprintf (st, "VH%d:\tmode %s, crit %d\n", i, + vh_unit[i].flags & UNIT_MODEDHU ? "DHU" : "DHV", + vh_crit & (1 << i)); + fprintf (st, "\tCSR %06o, mcount %d, rbuf_idx %d, txq_idx %d\n", + vh_csr[i], vh_mcount[i], rbuf_idx[i], txq_idx[i]); + for (j = 0; j < VH_LINES; j++) + debug_line (st, i, j); + } + return (SCPE_OK); +} + +static t_stat vh_show_rbuf ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + int32 i; + + for (i = 0; i < rbuf_idx[0]; i++) + fprintf (st, "%03d: %06o\n", i, vh_rbuf[0][i]); + return (SCPE_OK); +} + +static t_stat vh_show_txq ( FILE *st, + UNIT *uptr, + int32 val, + void *desc ) +{ + int32 i; + + for (i = 0; i < txq_idx[0]; i++) + fprintf (st, "%02d: %06o\n\r", i, vh_txq[0][i]); + return (SCPE_OK); +} + diff --git a/PDP11/pdp11_xq.c b/PDP11/pdp11_xq.c new file mode 100644 index 0000000..06ef780 --- /dev/null +++ b/PDP11/pdp11_xq.c @@ -0,0 +1,2051 @@ +/* pdp11_xq.c: DEQNA/DELQA ethernet controller simulator + ------------------------------------------------------------------------------ + + Copyright (c) 2002-2007, David T. Hittner + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + This DEQNA/DELQA simulation is based on: + Digital DELQA Users Guide, Part# EK-DELQA-UG-002 + Digital DEQNA Users Guide, Part# EK-DEQNA-UG-001 + These manuals can be found online at: + http://www.spies.com/~aek/pdf/dec/qbus + + Certain adaptations have been made because this is an emulation: + Ethernet transceiver power flag CSR<12> is ON when attached. + External Loopback does not go out to the physical adapter, it is + implemented more like an extended Internal Loopback + Time Domain Reflectometry (TDR) numbers are faked + The 10-second approx. hardware/software reset delay does not exist + Some physical ethernet receive events like Runts, Overruns, etc. are + never reported back, since the packet-level driver never sees them + + Certain advantages are derived from this emulation: + If the real ethernet controller is faster than 10Mbit/sec, the speed is + seen by the simulated cpu since there are no minimum response times. + + Known Bugs or Unsupported features, in priority order: + 1) PDP11 bootstrap + 2) MOP functionality not implemented + 3) Local packet processing not implemented + + Regression Tests: + VAX: 1. Console SHOW DEVICE + 2. VMS v7.2 boots/initializes/shows device + 3. VMS DECNET - SET HOST and COPY tests + 4. VMS MultiNet - SET HOST/TELNET and FTP tests + 5. VMS LAT - SET HOST/LAT tests + 6. VMS Cluster - SHOW CLUSTER, SHOW DEVICE, and cluster COPY tests + 7. Console boot into VMSCluster (>>>B XQAO) + + PDP11: 1. RT-11 v5.3 - FTPSB copy test + 2. RSTS/E v10.1 - detects/enables device + + ------------------------------------------------------------------------------ + + Modification history: + + 18-Jun-07 RMS Added UNIT_IDLE flag + 29-Oct-06 RMS Synced poll and clock + 27-Jan-06 RMS Fixed unaligned accesses in XQB (found by Doug Carman) + 07-Jan-06 RMS Fixed unaligned access bugs (found by Doug Carman) + 07-Sep-05 DTH Removed unused variable + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 01-Dec-04 DTH Added runtime attach prompt + 27-Feb-04 DTH Removed struct timeb deuggers + 31-Jan-04 DTH Replaced #ifdef debuggers with inline debugging + 19-Jan-04 DTH Combined service timers into one for efficiency + 16-Jan-04 DTH Added more info to SHOW MOD commands, added SET/SHOW XQ DEBUG + 13-Jan-04 DTH Corrected interrupt code with help from Tom Evans + 06-Jan-04 DTH Added protection against changing mac and type if attached + 05-Jan-04 DTH Moved most of xq_setmac to sim_ether + 26-Dec-03 DTH Moved ethernet show and queue functions to sim_ether + 03-Dec-03 DTH Added minimum name length to show xq eth + 25-Nov-03 DTH Reworked interrupts to fix broken XQB implementation + 19-Nov-03 MP Rearranged timer reset sequencing to allow for a device to be + disabled after it had been enabled. + 17-Nov-03 DTH Standardized #include of timeb.h + 28-Sep-03 MP - Fixed bug in xq_process_setup which would leave the + device in promiscuous or all multicast mode once it + ever had been there. + - Fixed output format in show_xq_sanity to end in "\n" + - Added display of All Multicase and promiscuous to + xq_show_filters + - The stuck in All Multicast or Promiscuous issue is + worse than previously thought. See comments in + xq_process_setup. + - Change xq_setmac to also allow ":" as a address + separator character, since sim_ether's eth_mac_fmt + formats them with this separator character. + - Changed xq_sw_reset to behave more like the set of + actions described in Table 3-6 of the DELQA manua. + The manual mentions "N/A" which I'm interpreting to + mean "Not Affected". + 05-Jun-03 DTH Added receive packet splitting + 03-Jun-03 DTH Added SHOW XQ FILTERS + 02-Jun-03 DTH Added SET/SHOW XQ STATS (packet statistics), runt & giant processing + 28-May-03 DTH Modified message queue for dynamic size to shrink executable + 28-May-03 MP Fixed bug in xq_setmac + 06-May-03 DTH Changed 32-bit t_addr to uint32 for v3.0 + Removed SET ADDRESS functionality + 05-May-03 DTH Added second controller + 26-Mar-03 DTH Added PDP11 bootrom loader + Adjusted xq_ex and xq_dev to allow pdp11 to look at bootrom + Patched bootrom to allow "pass" of diagnostics on RSTS/E + 06-Mar-03 DTH Corrected interrupts on IE state transition (code by Tom Evans) + Added interrupt clear on soft reset (first noted by Bob Supnik) + Removed interrupt when setting XL or RL (multiple people) + 16-Jan-03 DTH Merged Mark Pizzolato's enhancements with main source + Corrected PDP11 XQ_DEBUG compilation + 15-Jan-03 MP Fixed the number of units in the xq device structure. + 13-Jan-03 MP Reworked the timer management logic which initiated + the system id broadcast messages. The original + implementation triggered this on the CSR transition + of Receiver Enabled. This was an issue since the + it seems that at least VMS's XQ driver makes this + transition often and the resulting overhead reduces + the simulated CPU instruction execution throughput by + about 40%. I start the system id timer on device + reset and it fires once a second so that it can + leverage the reasonably recalibrated tmr_poll value. + 13-Jan-03 MP Changed the scheduling of xq_svc to leverage the + dynamically computed clock values to achieve an + approximate interval of 100 per second. This is + more than sufficient for normal system behaviour + expecially since we service receives with every + transmit. The previous fixed value of 2500 + attempted to get 200/sec but it was a guess that + didn't adapt. On faster host systems (possibly + most of them) the 2500 number spends too much time + polling. + 10-Jan-03 DTH Removed XQ_DEBUG dependency from Borland #pragmas + Added SET XQ BOOTROM command for PDP11s + 07-Jan-03 DTH Added pointer to online manuals + 02-Jan-03 DTH Added local packet processing + 30-Dec-02 DTH Added automatic system id broadcast + 27-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source + 20-Dec-02 MP Fix bug that caused VMS system crashes when attempting cluster + operations. Added additional conditionally compiled debug + info needed to track down the issue. + 17-Dec-02 MP Added SIMH "registers" describing the Ethernet state + so this information can be recorded in a "saved" snapshot. + 05-Dec-02 MP Adjusted the rtime value from 100 to 2500 which increased the + available CPU cycles for Instruction execution by almost 100%. + This made sense after the below enhancements which, in general + caused the draining of the received data stream much more + agressively with less overhead. + 05-Dec-02 MP Added a call to xq_svc after all successful calls to eth_write + to allow receive processing to happen before the next event + service time. + 05-Dec-02 MP Restructured the flow of processing in xq_svc so that eth_read + is called repeatedly until either a packet isn't found or + there is no room for another one in the queue. Once that has + been done, xq_process_rdbl is called to pass the queued packets + into the simulated system as space is available there. + xq_process_rdbl is also called at the beginning of xq_svc to + drain the queue into the simulated system, making more room + available in the queue. No processing is done at all in + xq_svc if the receiver is disabled. + 04-Dec-02 MP Changed interface and usage to xq_insert_queue to pass + the packet to be inserted by reference. This avoids 3K bytes + of buffer copy operations for each packet received. Now only + copy actual received packet data. + 31-Oct-02 DTH Cleaned up pointer warnings (found by Federico Schwindt) + Corrected unattached and no network behavior + Added message when SHOW XQ ETH finds no devices + 23-Oct-02 DTH Beta 5 released + 22-Oct-02 DTH Added all_multicast and promiscuous support + 21-Oct-02 DTH Added write buffer max size check (code by Jason Thorpe) + Corrected copyright again + Implemented NXM testing and recovery + 16-Oct-02 DTH Beta 4 released + Added and debugged Sanity Timer code + Corrected copyright + 15-Oct-02 DTH Rollback to known good Beta3 and roll forward; TCP broken + 12-Oct-02 DTH Fixed VAX network bootstrap; setup packets must return TDR > 0 + 11-Oct-02 DTH Added SET/SHOW XQ TYPE and SET/SHOW XQ SANITY commands + 10-Oct-02 DTH Beta 3 released; Integrated with 2.10-0b1 + Fixed off-by-1 bug on xq->setup.macs[7..13] + Added xq_make_checksum + Added rejection of multicast addresses in SET XQ MAC + 08-Oct-02 DTH Beta 2 released; Integrated with 2.10-0p4 + Added variable vector (fixes PDP11) and copyrights + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 24-Sep-02 DTH Moved more code to Sim_Ether module, added SHOW ETH command + 23-Sep-02 DTH Added SET/SHOW MAC command + 22-Sep-02 DTH Multinet TCP/IP loaded, tests OK via SET HOST/TELNET + 20-Sep-02 DTH Cleaned up code fragments, fixed non-DECNET MAC use + 19-Sep-02 DTH DECNET finally stays up; successful SET HOST to another node + 15-Sep-02 DTH Added ethernet packet read/write + 13-Sep-02 DTH DECNET starts, but circuit keeps going up & down + 26-Aug-02 DTH DECNET loaded, returns device timeout + 22-Aug-02 DTH VMS 7.2 recognizes device as XQA0 + 18-Aug-02 DTH VAX sees device as XQA0; shows hardcoded MAC correctly + 15-Aug-02 DTH Started XQ simulation + + ------------------------------------------------------------------------------ +*/ + +#include +#include "pdp11_xq.h" +#include "pdp11_xq_bootrom.h" + +extern int32 tmxr_poll; +extern FILE* sim_deb; +extern char* read_line (char *ptr, int32 size, FILE *stream); + +/* forward declarations */ +t_stat xq_rd(int32* data, int32 PA, int32 access); +t_stat xq_wr(int32 data, int32 PA, int32 access); +t_stat xq_svc(UNIT * uptr); +t_stat xq_reset (DEVICE * dptr); +t_stat xq_attach (UNIT * uptr, char * cptr); +t_stat xq_detach (UNIT * uptr); +t_stat xq_showmac (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_setmac (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_type (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_sanity (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xq_process_xbdl(CTLR* xq); +t_stat xq_dispatch_xbdl(CTLR* xq); +void xq_start_receiver(void); +void xq_sw_reset(CTLR* xq); +t_stat xq_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat xq_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +void xq_reset_santmr(CTLR* xq); +t_stat xq_boot_host(CTLR* xq); +t_stat xq_system_id(CTLR* xq, const ETH_MAC dst, uint16 receipt_id); +void xqa_read_callback(int status); +void xqb_read_callback(int status); +void xqa_write_callback(int status); +void xqb_write_callback(int status); +void xq_setint (CTLR* xq); +void xq_clrint (CTLR* xq); +int32 xq_int (void); +void xq_csr_set_clr(CTLR* xq, uint16 set_bits, uint16 clear_bits); + +struct xq_device xqa = { + xqa_read_callback, /* read callback routine */ + xqa_write_callback, /* write callback routine */ + {0x08, 0x00, 0x2B, 0xAA, 0xBB, 0xCC}, /* mac */ + XQ_T_DELQA, /* type */ + XQ_SERVICE_INTERVAL, /* poll */ + {0} /* sanity */ + }; + +struct xq_device xqb = { + xqb_read_callback, /* read callback routine */ + xqb_write_callback, /* write callback routine */ + {0x08, 0x00, 0x2B, 0xBB, 0xCC, 0xDD}, /* mac */ + XQ_T_DELQA, /* type */ + XQ_SERVICE_INTERVAL, /* poll */ + {0} /* sanity */ + }; + +/* SIMH device structures */ +DIB xqa_dib = { IOBA_XQ, IOLN_XQ, &xq_rd, &xq_wr, + 1, IVCL (XQ), 0, { &xq_int } }; + +UNIT xqa_unit[] = { + { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ +}; + +REG xqa_reg[] = { + { GRDATA ( SA0, xqa.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA1, xqa.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA2, xqa.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA3, xqa.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA4, xqa.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA5, xqa.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( RBDL, xqa.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( RBDH, xqa.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( XBDL, xqa.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( XBDH, xqa.xbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( VAR, xqa.var, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( CSR, xqa.csr, XQ_RDX, 16, 0), REG_FIT }, + { FLDATA ( INT, xqa.irq, 0) }, + { GRDATA ( SETUP_PRM, xqa.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_MLT, xqa.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_L1, xqa.setup.l1, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_L2, xqa.setup.l2, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_L3, xqa.setup.l3, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_SAN, xqa.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, + { BRDATA ( SETUP_MACS, &xqa.setup.macs, XQ_RDX, 8, sizeof(xqa.setup.macs)), REG_HRO}, + { NULL }, +}; + +DIB xqb_dib = { IOBA_XQB, IOLN_XQB, &xq_rd, &xq_wr, + 1, IVCL (XQ), 0, { &xq_int } }; + +UNIT xqb_unit[] = { + { UDATA (&xq_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 2047) }, /* receive timer */ +}; + +REG xqb_reg[] = { + { GRDATA ( SA0, xqb.addr[0], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA1, xqb.addr[1], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA2, xqb.addr[2], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA3, xqb.addr[3], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA4, xqb.addr[4], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( SA5, xqb.addr[5], XQ_RDX, 8, 0), REG_RO|REG_FIT}, + { GRDATA ( RBDL, xqb.rbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( RBDH, xqb.rbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( XBDL, xqb.xbdl[0], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( XBDH, xqb.xbdl[1], XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( VAR, xqb.var, XQ_RDX, 16, 0), REG_FIT }, + { GRDATA ( CSR, xqb.csr, XQ_RDX, 16, 0), REG_FIT }, + { FLDATA ( INT, xqb.irq, 0) }, + { GRDATA ( SETUP_PRM, xqb.setup.promiscuous, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_MLT, xqb.setup.multicast, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_L1, xqb.setup.l1, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_L2, xqb.setup.l2, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_L3, xqb.setup.l3, XQ_RDX, 32, 0), REG_HRO}, + { GRDATA ( SETUP_SAN, xqb.setup.sanity_timer, XQ_RDX, 32, 0), REG_HRO}, + { BRDATA ( SETUP_MACS, &xqb.setup.macs, XQ_RDX, 8, sizeof(xqb.setup.macs)), REG_HRO}, + { NULL }, +}; + +MTAB xq_mod[] = { + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", + &xq_setmac, &xq_showmac, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH", + NULL, ð_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "FILTERS", "FILTERS", + NULL, &xq_show_filters, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", + &xq_set_stats, &xq_show_stats, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEQNA|DELQA}", + &xq_set_type, &xq_show_type, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "POLL", "POLL={DEFAULT|4..2500]", + &xq_set_poll, &xq_show_poll, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "SANITY", "SANITY={ON|OFF}", + &xq_set_sanity, &xq_show_sanity, NULL }, + { 0 }, +}; + +DEBTAB xq_debug[] = { + {"TRACE", DBG_TRC}, + {"CSR", DBG_CSR}, + {"VAR", DBG_VAR}, + {"WARN", DBG_WRN}, + {"SETUP", DBG_SET}, + {"SANITY", DBG_SAN}, + {"REG", DBG_REG}, + {"PACKET", DBG_PCK}, + {"ETH", DBG_ETH}, + {0} +}; + +DEVICE xq_dev = { + "XQ", xqa_unit, xqa_reg, xq_mod, + 1, XQ_RDX, 11, 1, XQ_RDX, 16, + &xq_ex, &xq_dep, &xq_reset, + NULL, &xq_attach, &xq_detach, + &xqa_dib, DEV_DISABLE | DEV_QBUS | DEV_DEBUG, + 0, xq_debug +}; + +DEVICE xqb_dev = { + "XQB", xqb_unit, xqb_reg, xq_mod, + 1, XQ_RDX, 11, 1, XQ_RDX, 16, + &xq_ex, &xq_dep, &xq_reset, + NULL, &xq_attach, &xq_detach, + &xqb_dib, DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_DEBUG, + 0, xq_debug +}; + +CTLR xq_ctrl[] = { + {&xq_dev, xqa_unit, &xqa_dib, &xqa}, /* XQA controller */ + {&xqb_dev, xqb_unit, &xqb_dib, &xqb} /* XQB controller */ +}; + +const char* const xq_recv_regnames[] = { + "MAC0", "MAC1", "MAC2", "MAC3", "MAC4", "MAC5", "VAR", "CSR" +}; + +const char* const xq_xmit_regnames[] = { + "", "", "RBDL-Lo", "RBDL-Hi", "XBDL-Lo", "XBDL-Hi", "VAR", "CSR" +}; + +const char* const xq_csr_bits[] = { + "RE", "SR", "NI", "BD", "XL", "RL", "IE", "XI", + "IL", "EL", "SE", "RR", "OK", "CA", "PE", "RI" +}; + +const char* const xq_var_bits[] = { + "ID", "RR", "V0", "V1", "V2", "V3", "V4", "V5", + "V6", "V7", "S1", "S2", "S3", "RS", "OS", "MS" +}; + +/* internal debugging routines */ +void xq_debug_setup(CTLR* xq); + +/*============================================================================*/ + +/* Multicontroller support */ + +CTLR* xq_unit2ctlr(UNIT* uptr) +{ + unsigned int i,j; + for (i=0; inumunits; j++) + if (&xq_ctrl[i].unit[j] == uptr) + return &xq_ctrl[i]; + /* not found */ + return 0; +} + +CTLR* xq_dev2ctlr(DEVICE* dptr) +{ + int i; + for (i=0; i= xq_ctrl[i].dib->ba) && (PA < (xq_ctrl[i].dib->ba + xq_ctrl[i].dib->lnt))) + return &xq_ctrl[i]; + /* not found */ + return 0; +} + +/*============================================================================*/ + +/* stop simh from reading non-existant unit data stream */ +t_stat xq_ex (t_value* vptr, t_addr addr, UNIT* uptr, int32 sw) +{ + /* on PDP-11, allow EX command to look at bootrom */ +#ifdef VM_PDP11 + if (addr <= sizeof(xq_bootrom)/2) + *vptr = xq_bootrom[addr]; + else + *vptr = 0; + return SCPE_OK; +#else + return SCPE_NOFNC; +#endif +} + +/* stop simh from writing non-existant unit data stream */ +t_stat xq_dep (t_value val, t_addr addr, UNIT* uptr, int32 sw) +{ + return SCPE_NOFNC; +} + +t_stat xq_showmac (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + char buffer[20]; + + eth_mac_fmt((ETH_MAC*)xq->var->mac, buffer); + fprintf(st, "MAC=%s", buffer); + return SCPE_OK; +} + +void xq_make_checksum(CTLR* xq) +{ + /* checksum calculation routine detailed in vaxboot.zip/xqbtdrivr.mar */ + uint32 checksum = 0; + const uint32 wmask = 0xFFFF; + int i; + + for (i = 0; i < sizeof(ETH_MAC); i += 2) { + checksum <<= 1; + if (checksum > wmask) + checksum -= wmask; + checksum += (xq->var->mac[i] << 8) | xq->var->mac[i+1]; + if (checksum > wmask) + checksum -= wmask; + } + if (checksum == wmask) + checksum = 0; + + /* set checksum bytes */ + xq->var->mac_checksum[0] = checksum & 0xFF; + xq->var->mac_checksum[1] = checksum >> 8; +} + +t_stat xq_setmac (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + t_stat status; + CTLR* xq = xq_unit2ctlr(uptr); + + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + status = eth_mac_scan(&xq->var->mac, cptr); + if (status != SCPE_OK) + return status; + + /* calculate mac checksum */ + xq_make_checksum(xq); + return SCPE_OK; +} + +t_stat xq_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + /* this sets all ints in the stats structure to the integer passed */ + CTLR* xq = xq_unit2ctlr(uptr); + + if (cptr) { + /* set individual stats to passed parameter value */ + int init = atoi(cptr); + int* stat_array = (int*) &xq->var->stats; + int elements = sizeof(struct xq_stats)/sizeof(int); + int i; + for (i=0; ivar->stats, 0, sizeof(struct xq_stats)); + } + return SCPE_OK; +} + +t_stat xq_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + char* fmt = " %-15s%d\n"; + CTLR* xq = xq_unit2ctlr(uptr); + + fprintf(st, "Ethernet statistics:\n"); + fprintf(st, fmt, "Recv:", xq->var->stats.recv); + fprintf(st, fmt, "Filtered:", xq->var->stats.filter); + fprintf(st, fmt, "Xmit:", xq->var->stats.xmit); + fprintf(st, fmt, "Xmit Fail:", xq->var->stats.fail); + fprintf(st, fmt, "Runts:", xq->var->stats.runt); + fprintf(st, fmt, "Oversize:", xq->var->stats.giant); + fprintf(st, fmt, "Setup:", xq->var->stats.setup); + fprintf(st, fmt, "Loopback:", xq->var->stats.loop); + fprintf(st, fmt, "ReadQ high:", xq->var->ReadQ.high); + return SCPE_OK; +} + +t_stat xq_show_filters (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + char buffer[20]; + int i; + + fprintf(st, "Filters:\n"); + for (i=0; ivar->setup.macs[i], buffer); + fprintf(st, " [%2d]: %s\n", i, buffer); + }; + if (xq->var->setup.multicast) + fprintf(st, "All Multicast Receive Mode\n"); + if (xq->var->setup.promiscuous) + fprintf(st, "Promiscuous Receive Mode\n"); + return SCPE_OK; +} + +t_stat xq_show_type (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + fprintf(st, "type="); + switch (xq->var->type) { + case XQ_T_DEQNA: fprintf(st, "DEQNA"); break; + case XQ_T_DELQA: fprintf(st, "DELQA"); break; + } + return SCPE_OK; +} + +t_stat xq_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + /* this assumes that the parameter has already been upcased */ + if (!strcmp(cptr, "DEQNA")) xq->var->type = XQ_T_DEQNA; + else if (!strcmp(cptr, "DELQA")) xq->var->type = XQ_T_DELQA; + else return SCPE_ARG; + + return SCPE_OK; +} + +t_stat xq_show_poll (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + fprintf(st, "poll=%d", xq->var->poll); + return SCPE_OK; +} + +t_stat xq_set_poll (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + /* this assumes that the parameter has already been upcased */ + if (!strcmp(cptr, "DEFAULT")) + xq->var->poll = XQ_SERVICE_INTERVAL; + else { + int newpoll = 0; + sscanf(cptr, "%d", &newpoll); + if ((newpoll >= 4) && (newpoll <= 2500)) + xq->var->poll = newpoll; + else + return SCPE_ARG; + } + + return SCPE_OK; +} + +t_stat xq_show_sanity (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + + fprintf(st, "sanity="); + switch (xq->var->sanity.enabled) { + case 2: fprintf(st, "ON\n"); break; + default: fprintf(st, "OFF\n"); break; + } + return SCPE_OK; +} + +t_stat xq_set_sanity (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xq = xq_unit2ctlr(uptr); + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + /* this assumes that the parameter has already been upcased */ + if (!strcmp(cptr, "ON")) xq->var->sanity.enabled = 2; + else if (!strcmp(cptr, "OFF")) xq->var->sanity.enabled = 0; + else return SCPE_ARG; + + return SCPE_OK; +} + +/*============================================================================*/ + +t_stat xq_nxm_error(CTLR* xq) +{ + const uint16 set_bits = XQ_CSR_NI | XQ_CSR_XI | XQ_CSR_XL | XQ_CSR_RL; + sim_debug(DBG_WRN, xq->dev, "Non Existent Memory Error!\n"); + + /* set NXM and associated bits in CSR */ + xq_csr_set_clr(xq, set_bits , 0); + return SCPE_OK; +} + +/* +** write callback +*/ +void xq_write_callback (CTLR* xq, int status) +{ + t_stat rstatus; + int32 wstatus; + const uint16 TDR = 100 + xq->var->write_buffer.len * 8; /* arbitrary value */ + uint16 write_success[2] = {0}; + uint16 write_failure[2] = {XQ_DSC_C}; + write_success[1] = TDR & 0x03FF; /* Does TDR get set on successful packets ?? */ + write_failure[1] = TDR & 0x03FF; /* TSW2<09:00> */ + + xq->var->stats.xmit += 1; + /* update write status words */ + if (status == 0) { /* success */ + wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, write_success); + } else { /* failure */ + sim_debug(DBG_WRN, xq->dev, "Packet Write Error!\n"); + xq->var->stats.fail += 1; + wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, write_failure); + } + if (wstatus) { + xq_nxm_error(xq); + return; + } + + /* update csr */ + xq_csr_set_clr(xq, XQ_CSR_XI, 0); + + /* reset sanity timer */ + xq_reset_santmr(xq); + + /* clear write buffer */ + xq->var->write_buffer.len = 0; + + /* next descriptor (implicit) */ + xq->var->xbdl_ba += 12; + + /* finish processing xbdl */ + rstatus = xq_process_xbdl(xq); +} + +void xqa_write_callback (int status) +{ + xq_write_callback(&xq_ctrl[0], status); +} + +void xqb_write_callback (int status) +{ + xq_write_callback(&xq_ctrl[1], status); +} + +/* read registers: */ +t_stat xq_rd(int32* data, int32 PA, int32 access) +{ + CTLR* xq = xq_pa2ctlr(PA); + int index = (PA >> 1) & 07; /* word index */ + + sim_debug(DBG_REG, xq->dev, "xq_rd(PA=0x%08X [%s], access=%d)\n", PA, xq_recv_regnames[index], access); + switch (index) { + case 0: + case 1: + /* return checksum in external loopback mode */ + if (xq->var->csr & XQ_CSR_EL) + *data = 0xFF00 | xq->var->mac_checksum[index]; + else + *data = 0xFF00 | xq->var->mac[index]; + break; + case 2: + case 3: + case 4: + case 5: + *data = 0xFF00 | xq->var->mac[index]; + break; + case 6: + sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, xq->var->var, xq->var->var, 0); + sim_debug (DBG_VAR, xq->dev, ", vec = 0%o\n", (xq->var->var & XQ_VEC_IV)); + *data = xq->var->var; + break; + case 7: + sim_debug_u16(DBG_CSR, xq->dev, xq_csr_bits, xq->var->csr, xq->var->csr, 1); + *data = xq->var->csr; + break; + } + return SCPE_OK; +} + + +/* dispatch ethernet read request + procedure documented in sec. 3.2.2 */ + +t_stat xq_process_rbdl(CTLR* xq) +{ + int32 rstatus, wstatus; + uint16 b_length, w_length, rbl; + uint32 address; + ETH_ITEM* item; + uint8* rbuf; + + sim_debug(DBG_TRC, xq->dev, "xq_process_rdbl\n"); + + /* process buffer descriptors */ + while(1) { + + /* get receive bdl from memory */ + xq->var->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* explicit chain buffer? */ + if (xq->var->rbdl_buf[1] & XQ_DSC_C) { + xq->var->rbdl_ba = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + continue; + } + + /* stop processing if nothing in read queue */ + if (!xq->var->ReadQ.count) break; + + /* get status words */ + rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* get host memory address */ + address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->rbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + item = &xq->var->ReadQ.item[xq->var->ReadQ.head]; + rbl = item->packet.len; + rbuf = item->packet.msg; + + /* see if packet must be size-adjusted or is splitting */ + if (item->packet.used) { + int used = item->packet.used; + rbl -= used; + rbuf = &item->packet.msg[used]; + } else { + /* adjust runt packets */ + if (rbl < ETH_MIN_PACKET) { + xq->var->stats.runt += 1; + sim_debug(DBG_WRN, xq->dev, "Runt detected, size = %d\n", rbl); + /* pad runts with zeros up to minimum size - this allows "legal" (size - 60) + processing of those weird short ARP packets that seem to occur occasionally */ + memset(&item->packet.msg[rbl], 0, ETH_MIN_PACKET-rbl); + rbl = ETH_MIN_PACKET; + }; + + /* adjust oversized packets */ + if (rbl > ETH_MAX_PACKET) { + xq->var->stats.giant += 1; + sim_debug(DBG_WRN, xq->dev, "Giant detected, size=%d\n", rbl); + /* trim giants down to maximum size - no documentation on how to handle the data loss */ + item->packet.len = ETH_MAX_PACKET; + rbl = ETH_MAX_PACKET; + }; + }; + + /* make sure entire packet fits in buffer - if not, will need to split into multiple buffers */ + if (rbl > b_length) + rbl = b_length; + item->packet.used += rbl; + + /* send data to host */ + wstatus = Map_WriteB(address, rbl, rbuf); + if (wstatus) return xq_nxm_error(xq); + + /* set receive size into RBL - RBL<10:8> maps into Status1<10:8>, + RBL<7:0> maps into Status2<7:0>, and Status2<15:8> (copy) */ + + xq->var->rbdl_buf[4] = 0; + switch (item->type) { + case 0: /* setup packet */ + xq->var->stats.setup += 1; + xq->var->rbdl_buf[4] = 0x2700; /* set esetup and RBL 10:8 */ + break; + case 1: /* loopback packet */ + xq->var->stats.loop += 1; + xq->var->rbdl_buf[4] = 0x2000; /* loopback flag */ + xq->var->rbdl_buf[4] |= (rbl & 0x0700); /* high bits of rbl */ + break; + case 2: /* normal packet */ + rbl -= 60; /* keeps max packet size in 11 bits */ + xq->var->rbdl_buf[4] = (rbl & 0x0700); /* high bits of rbl */ + break; + } + if (item->packet.used < item->packet.len) + xq->var->rbdl_buf[4] |= 0xC000; /* not last segment */ + xq->var->rbdl_buf[5] = ((rbl & 0x00FF) << 8) | (rbl & 0x00FF); + if (xq->var->ReadQ.loss) { + sim_debug(DBG_WRN, xq->dev, "ReadQ overflow!\n"); + xq->var->rbdl_buf[4] |= 0x0001; /* set overflow bit */ + xq->var->ReadQ.loss = 0; /* reset loss counter */ + } + + /* update read status words*/ + wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* remove packet from queue */ + if (item->packet.used >= item->packet.len) + ethq_remove(&xq->var->ReadQ); + + /* mark transmission complete */ + xq_csr_set_clr(xq, XQ_CSR_RI, 0); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + + } /* while */ + + return SCPE_OK; +} + +t_stat xq_process_mop(CTLR* xq) +{ + uint32 address; + uint16 size; + int32 wstatus; + struct xq_meb* meb = (struct xq_meb*) &xq->var->write_buffer.msg[0200]; + const struct xq_meb* limit = (struct xq_meb*) &xq->var->write_buffer.msg[0400]; + + sim_debug(DBG_TRC, xq->dev, "xq_process_mop()\n"); + + if (xq->var->type == XQ_T_DEQNA) /* DEQNA's don't MOP */ + return SCPE_NOFNC; + + while ((meb->type != 0) && (meb < limit)) { + address = (meb->add_hi << 16) || (meb->add_mi << 8) || meb->add_lo; + size = (meb->siz_hi << 8) || meb->siz_lo; + + /* MOP stuff here - NOT YET FULLY IMPLEMENTED */ + sim_debug (DBG_WRN, xq->dev, "Processing MEB type: %d\n", meb->type); + switch (meb->type) { + case 0: /* MOP Termination */ + break; + case 1: /* MOP Read Ethernet Address */ + wstatus = Map_WriteB(address, sizeof(ETH_MAC), (uint8*) &xq->var->setup.macs[0]); + if (wstatus) return xq_nxm_error(xq); + break; + case 2: /* MOP Reset System ID */ + break; + case 3: /* MOP Read Last MOP Boot */ + break; + case 4: /* MOP Read Boot Password */ + break; + case 5: /* MOP Write Boot Password */ + break; + case 6: /* MOP Read System ID */ + break; + case 7: /* MOP Write System ID */ + break; + case 8: /* MOP Read Counters */ + break; + case 9: /* Mop Read/Clear Counters */ + break; + } /* switch */ + + /* process next meb */ + meb += sizeof(struct xq_meb); + + } /* while */ + return SCPE_OK; +} + +t_stat xq_process_setup(CTLR* xq) +{ + int i,j; + int count = 0; + float secs; + t_stat status; + ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; + ETH_MAC filters[XQ_FILTER_MAX + 1]; + + sim_debug(DBG_TRC, xq->dev, "xq_process_setup()\n"); + + /* extract filter addresses from setup packet */ + memset(xq->var->setup.macs, '\0', sizeof(xq->var->setup.macs)); + for (i = 0; i < 7; i++) + for (j = 0; j < 6; j++) { + xq->var->setup.macs[i] [j] = xq->var->write_buffer.msg[(i + 01) + (j * 8)]; + if (xq->var->write_buffer.len > 112) + xq->var->setup.macs[i+7][j] = xq->var->write_buffer.msg[(i + 0101) + (j * 8)]; + } + + /* + Under VMS the setup packet that is passed to turn promiscuous + off after it has been on doesn't seem to follow the rules documented + in both the DEQNA and DELQA manuals. + These rules seem to say that setup packets less than 128 should only + modify the address filter set and probably not the All-Multicast and + Promiscuous modes, however, VMS V5-5 and V7.3 seem to send a 127 byte + packet to turn this functionality off. I'm not sure how real hardware + behaves in this case, since the only consequence is extra interrupt + load. To realize and retain the benefits of the newly added BPF + functionality in sim_ether, I've modified the logic implemented here + to disable Promiscuous mode when a "small" setup packet is processed. + I'm deliberately not modifying the All-Multicast mode the same way + since I don't have an observable case of its behavior. These two + different modes come from very different usage situations: + 1) Promiscuous mode is usually entered for relatively short periods + of time due to the needs of a specific application program which + is doing some sort of management/monitoring function (i.e. tcpdump) + 2) All-Multicast mode is only entered by the OS Kernel Port Driver + when it happens to have clients (usually network stacks or service + programs) which as a group need to listen to more multicast ethernet + addresses than the 12 (or so) which the hardware supports directly. + so, I believe that the All-Multicast mode, is first rarely used, and if + it ever is used, once set, it will probably be set either forever or for + long periods of time, and the additional interrupt processing load to + deal with the distinctly lower multicast traffic set is clearly lower than + that of the promiscuous mode. + */ + xq->var->setup.promiscuous = 0; + /* process high byte count */ + if (xq->var->write_buffer.len > 128) { + uint16 len = xq->var->write_buffer.len; + uint16 led, san; + + xq->var->setup.multicast = (0 != (len & XQ_SETUP_MC)); + xq->var->setup.promiscuous = (0 != (len & XQ_SETUP_PM)); + if (led = (len & XQ_SETUP_LD) >> 2) { + switch (led) { + case 1: xq->var->setup.l1 = 0; break; + case 2: xq->var->setup.l2 = 0; break; + case 3: xq->var->setup.l3 = 0; break; + } /* switch */ + } /* if led */ + + /* set sanity timer timeout */ + san = (len & XQ_SETUP_ST) >> 4; + switch(san) { + case 0: secs = 0.25; break; /* 1/4 second */ + case 1: secs = 1; break; /* 1 second */ + case 2: secs = 4; break; /* 4 seconds */ + case 3: secs = 16; break; /* 16 seconds */ + case 4: secs = 1 * 60; break; /* 1 minute */ + case 5: secs = 4 * 60; break; /* 4 minutes */ + case 6: secs = 16 * 60; break; /* 16 minutes */ + case 7: secs = 64 * 60; break; /* 64 minutes */ + } + xq->var->sanity.quarter_secs = (int) (secs * 4); + xq->var->sanity.max = (int) (secs * xq->var->poll); + } + + /* finalize sanity timer state */ + xq->var->sanity.timer = xq->var->sanity.max; + if (xq->var->sanity.enabled != 2) { + if (xq->var->csr & XQ_CSR_SE) + xq->var->sanity.enabled = 1; + else + xq->var->sanity.enabled = 0; + } + + /* set ethernet filter */ + /* memcpy (filters[count++], xq->mac, sizeof(ETH_MAC)); */ + for (i = 0; i < XQ_FILTER_MAX; i++) + if (memcmp(zeros, &xq->var->setup.macs[i], sizeof(ETH_MAC))) + memcpy (filters[count++], xq->var->setup.macs[i], sizeof(ETH_MAC)); + status = eth_filter (xq->var->etherface, count, filters, xq->var->setup.multicast, xq->var->setup.promiscuous); + + /* process MOP information */ + if (xq->var->write_buffer.msg[0]) + status = xq_process_mop(xq); + + /* mark setup block valid */ + xq->var->setup.valid = 1; + + if (sim_deb && (xq->dev->dctrl & DBG_SET)) + xq_debug_setup(xq); + return SCPE_OK; +} + +/* + Dispatch Write Operation + + The DELQA manual does not explicitly state whether or not multiple packets + can be written in one transmit operation, so a maximum of 1 packet is assumed. + +*/ +t_stat xq_process_xbdl(CTLR* xq) +{ + const uint16 implicit_chain_status[2] = {XQ_DSC_V | XQ_DSC_C, 1}; + const uint16 write_success[2] = {0, 1 /*Non-Zero TDR*/}; + uint16 b_length, w_length; + int32 rstatus, wstatus; + uint32 address; + t_stat status; + + sim_debug(DBG_TRC, xq->dev, "xq_process_xbdl()\n"); + + /* clear write buffer */ + xq->var->write_buffer.len = 0; + + /* process buffer descriptors until not valid */ + while (1) { + + /* Get transmit bdl from memory */ + rstatus = Map_ReadW (xq->var->xbdl_ba, 12, &xq->var->xbdl_buf[0]); + xq->var->xbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(xq->var->xbdl_ba, 2, &xq->var->xbdl_buf[0]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->xbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_XL, 0); + sim_debug(DBG_WRN, xq->dev, "XBDL List empty\n"); + return SCPE_OK; + } + + /* compute host memory address */ + address = ((xq->var->xbdl_buf[1] & 0x3F) << 16) | xq->var->xbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->xbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->xbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->xbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + /* explicit chain buffer? */ + if (xq->var->xbdl_buf[1] & XQ_DSC_C) { + xq->var->xbdl_ba = address; + sim_debug(DBG_WRN, xq->dev, "XBDL chained buffer encountered: %d\n", b_length); + continue; + } + + /* add to transmit buffer, making sure it's not too big */ + if ((xq->var->write_buffer.len + b_length) > sizeof(xq->var->write_buffer.msg)) + b_length = sizeof(xq->var->write_buffer.msg) - xq->var->write_buffer.len; + rstatus = Map_ReadB(address, b_length, &xq->var->write_buffer.msg[xq->var->write_buffer.len]); + if (rstatus) return xq_nxm_error(xq); + xq->var->write_buffer.len += b_length; + + /* end of message? */ + if (xq->var->xbdl_buf[1] & XQ_DSC_E) { + if (((~xq->var->csr & XQ_CSR_RE) && ((~xq->var->csr & XQ_CSR_IL) || (xq->var->csr & XQ_CSR_EL))) || /* loopback */ + (xq->var->xbdl_buf[1] & XQ_DSC_S)) { /* or setup packet (forces loopback regardless of state) */ + if (xq->var->xbdl_buf[1] & XQ_DSC_S) { /* setup packet */ + status = xq_process_setup(xq); + + /* put packet in read buffer */ + ethq_insert (&xq->var->ReadQ, 0, &xq->var->write_buffer, status); + } else { /* loopback */ + /* put packet in read buffer */ + ethq_insert (&xq->var->ReadQ, 1, &xq->var->write_buffer, 0); + } + + /* update write status */ + wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, (uint16*) write_success); + if (wstatus) return xq_nxm_error(xq); + + /* clear write buffer */ + xq->var->write_buffer.len = 0; + + /* reset sanity timer */ + xq_reset_santmr(xq); + + /* mark transmission complete */ + xq_csr_set_clr(xq, XQ_CSR_XI, 0); + + /* now trigger "read" of setup or loopback packet */ + if (~xq->var->csr & XQ_CSR_RL) + status = xq_process_rbdl(xq); + + } else { /* not loopback */ + + status = eth_write(xq->var->etherface, &xq->var->write_buffer, xq->var->wcallback); + if (status != SCPE_OK) /* not implemented or unattached */ + xq_write_callback(xq, 1); /* fake failure */ +#if 0 + else + xq_svc(&xq->unit[0]); /* service any received data */ +#endif + sim_debug(DBG_WRN, xq->dev, "XBDL completed processing write\n"); + return SCPE_OK; + + } /* loopback/non-loopback */ + } else { /* not at end-of-message */ + + sim_debug(DBG_WRN, xq->dev, "XBDL processing implicit chain buffer segment\n"); + /* update bdl status words */ + wstatus = Map_WriteW(xq->var->xbdl_ba + 8, 4, (uint16*) implicit_chain_status); + if(wstatus) return xq_nxm_error(xq); + } + + /* set to next bdl (implicit chain) */ + xq->var->xbdl_ba += 12; + + } /* while */ +} + +t_stat xq_dispatch_rbdl(CTLR* xq) +{ + int i; + int32 rstatus, wstatus; + t_stat status; + + sim_debug(DBG_TRC, xq->dev, "xq_dispatch_rbdl()\n"); + + /* mark receive bdl valid */ + xq_csr_set_clr(xq, 0, XQ_CSR_RL); + + /* init receive bdl buffer */ + for (i=0; i<6; i++) + xq->var->rbdl_buf[i] = 0; + + /* get address of first receive buffer */ + xq->var->rbdl_ba = ((xq->var->rbdl[1] & 0x3F) << 16) | (xq->var->rbdl[0] & ~01); + + /* get first receive buffer */ + xq->var->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* is buffer valid? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* process any waiting packets in receive queue */ + if (xq->var->ReadQ.count) + status = xq_process_rbdl(xq); + + return SCPE_OK; +} + +t_stat xq_dispatch_xbdl(CTLR* xq) +{ + int i; + t_stat status; + + sim_debug(DBG_TRC, xq->dev, "xq_dispatch_xbdl()\n"); + + /* mark transmit bdl valid */ + xq_csr_set_clr(xq, 0, XQ_CSR_XL); + + /* initialize transmit bdl buffers */ + for (i=0; i<6; i++) + xq->var->xbdl_buf[i] = 0; + + /* clear transmit buffer */ + xq->var->write_buffer.len = 0; + + /* get base address of first transmit descriptor */ + xq->var->xbdl_ba = ((xq->var->xbdl[1] & 0x3F) << 16) | (xq->var->xbdl[0] & ~01); + + /* process xbdl */ + status = xq_process_xbdl(xq); + + return status; +} + +t_stat xq_process_loopback(CTLR* xq, ETH_PACK* pack) +{ + ETH_PACK reply; + ETH_MAC physical_address; + t_stat status; + int offset = pack->msg[14] | (pack->msg[15] << 8); + int function = pack->msg[offset] | (pack->msg[offset+1] << 8); + + sim_debug(DBG_TRC, xq->dev, "xq_process_loopback()\n"); + + if (function != 2 /*forward*/) + return SCPE_NOFNC; + + /* create reply packet */ + memcpy (&reply, pack, sizeof(ETH_PACK)); + memcpy (physical_address, xq->var->setup.valid ? xq->var->setup.macs[0] : xq->var->mac, sizeof(ETH_MAC)); + memcpy (&reply.msg[0], &reply.msg[offset+2], sizeof(ETH_MAC)); + memcpy (&reply.msg[6], physical_address, sizeof(ETH_MAC)); + memcpy (&reply.msg[offset+2], physical_address, sizeof(ETH_MAC)); + reply.msg[offset] = 0x01; + offset += 8; + reply.msg[14] = offset & 0xFF; + reply.msg[15] = (offset >> 8) & 0xFF; + + /* send reply packet */ + status = eth_write(xq->var->etherface, &reply, NULL); + + return status; +} + +t_stat xq_process_remote_console (CTLR* xq, ETH_PACK* pack) +{ + t_stat status; + ETH_MAC source; + uint16 receipt; + int code = pack->msg[16]; + + sim_debug(DBG_TRC, xq->dev, "xq_process_remote_console()\n"); + + switch (code) { + case 0x05: /* request id */ + receipt = pack->msg[18] | (pack->msg[19] << 8); + memcpy(source, &pack->msg[6], sizeof(ETH_MAC)); + + /* send system id to requestor */ + status = xq_system_id (xq, source, receipt); + return status; + break; + case 0x06: /* boot */ + /* + NOTE: the verification field should be checked here against the + verification value established in the setup packet. If they match the + reboot should occur, otherwise nothing happens, and the packet + is passed on to the host. + + Verification is not implemented, since the setup packet processing code + isn't complete yet. + + Various values are also passed: processor, control, and software id. + These control the various boot parameters, however SIMH does not + have a mechanism to pass these to the host, so just reboot. + */ + + status = xq_boot_host(xq); + return status; + break; + } /* switch */ + + return SCPE_NOFNC; +} + +t_stat xq_process_local (CTLR* xq, ETH_PACK* pack) +{ + /* returns SCPE_OK if local processing occurred, + otherwise returns SCPE_NOFNC or some other code */ + int protocol; + + sim_debug(DBG_TRC, xq->dev, "xq_process_local()\n"); + /* DEQNA's have no local processing capability */ + if (xq->var->type == XQ_T_DEQNA) + return SCPE_NOFNC; + + protocol = pack->msg[12] | (pack->msg[13] << 8); + switch (protocol) { + case 0x0090: /* ethernet loopback */ + return xq_process_loopback(xq, pack); + break; + case 0x0260: /* MOP remote console */ + return xq_process_remote_console(xq, pack); + break; + } + return SCPE_NOFNC; +} + +void xq_read_callback(CTLR* xq, int status) +{ + xq->var->stats.recv += 1; + if (xq->var->csr & XQ_CSR_RE) { /* receiver enabled */ + + /* process any packets locally that can be */ + t_stat status = xq_process_local (xq, &xq->var->read_buffer); + + /* add packet to read queue */ + if (status != SCPE_OK) + ethq_insert(&xq->var->ReadQ, 2, &xq->var->read_buffer, status); + } else { + sim_debug(DBG_WRN, xq->dev, "packet received with receiver disabled\n"); + } +} + +void xqa_read_callback(int status) +{ + xq_read_callback(&xq_ctrl[0], status); +} + +void xqb_read_callback(int status) +{ + xq_read_callback(&xq_ctrl[1], status); +} + +void xq_sw_reset(CTLR* xq) +{ + const uint16 set_bits = XQ_CSR_XL | XQ_CSR_RL; + int i; + + sim_debug(DBG_TRC, xq->dev, "xq_sw_reset()\n"); + + /* reset csr bits */ + xq_csr_set_clr(xq, set_bits, (uint16) ~set_bits); + + if (xq->var->etherface) + xq_csr_set_clr(xq, XQ_CSR_OK, 0); + + /* clear interrupt unconditionally */ + xq_clrint(xq); + + /* flush read queue */ + ethq_clear(&xq->var->ReadQ); + + /* clear setup info */ + xq->var->setup.multicast = 0; + xq->var->setup.promiscuous = 0; + if (xq->var->etherface) { + int count = 0; + ETH_MAC zeros = {0, 0, 0, 0, 0, 0}; + ETH_MAC filters[XQ_FILTER_MAX + 1]; + + /* set ethernet filter */ + /* memcpy (filters[count++], xq->mac, sizeof(ETH_MAC)); */ + for (i = 0; i < XQ_FILTER_MAX; i++) + if (memcmp(zeros, &xq->var->setup.macs[i], sizeof(ETH_MAC))) + memcpy (filters[count++], xq->var->setup.macs[i], sizeof(ETH_MAC)); + eth_filter (xq->var->etherface, count, filters, xq->var->setup.multicast, xq->var->setup.promiscuous); + } +} + +/* write registers: */ + +t_stat xq_wr_var(CTLR* xq, int32 data) +{ + uint16 save_var = xq->var->var; + sim_debug(DBG_REG, xq->dev, "xq_wr_var(data= 0x%08X\n", data); + + switch (xq->var->type) { + case XQ_T_DEQNA: + xq->var->var = (data & XQ_VEC_IV); + break; + case XQ_T_DELQA: + xq->var->var = (xq->var->var & XQ_VEC_RO) | (data & XQ_VEC_RW); + + /* if switching to DEQNA-LOCK mode clear VAR<14:10> */ + if (~xq->var->var & XQ_VEC_MS) + xq->var->var &= ~(XQ_VEC_OS | XQ_VEC_RS | XQ_VEC_ST); + break; + } + + /* set vector of SIMH device */ + if (data & XQ_VEC_IV) + xq->dib->vec = (data & XQ_VEC_IV) + VEC_Q; + else + xq->dib->vec = 0; + + sim_debug_u16(DBG_VAR, xq->dev, xq_var_bits, save_var, xq->var->var, 1); + + return SCPE_OK; +} + +#ifdef VM_PDP11 +t_stat xq_process_bootrom (CTLR* xq) +{ + /* + NOTE: BOOT ROMs are a PDP-11ism, since they contain PDP-11 binary code. + the host is responsible for creating two *2KB* receive buffers. + + RSTS/E v10.1 source (INIONE.MAR/XHLOOK:) indicates that both the DEQNA and + DELQA will set receive status word 1 bits 15 & 14 on both packets. It also + states that a hardware bug in the DEQNA will set receive status word 1 bit 15 + (only) in the *third* receive buffer (oops!). + + RSTS/E v10.1 will run the Citizenship test from the bootrom after loading it. + Documentation on the Boot ROM can be found in INIQNA.MAR. + */ + + int32 rstatus, wstatus; + uint16 b_length, w_length; + uint32 address; + uint8* bootrom = (uint8*) xq_bootrom; + int i, checksum; + + sim_debug(DBG_TRC, xq->dev, "xq_process_bootrom()\n"); + + /* + RSTS/E v10.1 invokes the Citizenship tests in the Bootrom. For some + reason, the current state of the XQ emulator cannot pass these. So, + to get moving on RSTE/E support, we will replace the following line in + INIQNA.MAR/CITQNA:: + 70$: MOV (R2),R0 ;get the status word + with + 70$: CLR R0 ;force success + to cause the Citizenship test to return success to RSTS/E. + + At some point, the real problem (failure to pass citizenship diagnostics) + does need to be corrected to find incompatibilities in the emulation, and to + ultimately allow it to pass Digital hardware diagnostic tests. + */ + for (i=0; ivar->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* get status words */ + rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* get host memory address */ + address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->rbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + /* make sure entire packet fits in buffer */ + assert(b_length >= sizeof(xq_bootrom)/2); + + /* send data to host */ + wstatus = Map_WriteB(address, sizeof(xq_bootrom)/2, bootrom); + if (wstatus) return xq_nxm_error(xq); + + /* update read status words */ + xq->var->rbdl_buf[4] = XQ_DSC_V | XQ_DSC_C; /* valid, chain */ + xq->var->rbdl_buf[5] = 0; + + /* update read status words*/ + wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + + /* --------------------------- bootrom part 2 -----------------------------*/ + + /* get receive bdl from memory */ + xq->var->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* get status words */ + rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* get host memory address */ + address = ((xq->var->rbdl_buf[1] & 0x3F) << 16) | xq->var->rbdl_buf[2]; + + /* decode buffer length - two's complement (in words) */ + w_length = ~xq->var->rbdl_buf[3] + 1; + b_length = w_length * 2; + if (xq->var->rbdl_buf[1] & XQ_DSC_H) b_length -= 1; + if (xq->var->rbdl_buf[1] & XQ_DSC_L) b_length -= 1; + + /* make sure entire packet fits in buffer */ + assert(b_length >= sizeof(xq_bootrom)/2); + + /* send data to host */ + wstatus = Map_WriteB(address, sizeof(xq_bootrom)/2, &bootrom[2048]); + if (wstatus) return xq_nxm_error(xq); + + /* update read status words */ + xq->var->rbdl_buf[4] = XQ_DSC_V | XQ_DSC_C; /* valid, chain */ + xq->var->rbdl_buf[5] = 0; + + /* update read status words*/ + wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + + /* --------------------------- bootrom part 3 -----------------------------*/ + + switch (xq->var->type) { + case XQ_T_DEQNA: + + /* get receive bdl from memory */ + xq->var->rbdl_buf[0] = 0xFFFF; + wstatus = Map_WriteW(xq->var->rbdl_ba, 2, &xq->var->rbdl_buf[0]); + rstatus = Map_ReadW (xq->var->rbdl_ba + 2, 6, &xq->var->rbdl_buf[1]); + if (rstatus || wstatus) return xq_nxm_error(xq); + + /* invalid buffer? */ + if (~xq->var->rbdl_buf[1] & XQ_DSC_V) { + xq_csr_set_clr(xq, XQ_CSR_RL, 0); + return SCPE_OK; + } + + /* get status words */ + rstatus = Map_ReadW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (rstatus) return xq_nxm_error(xq); + + /* update read status words */ + xq->var->rbdl_buf[4] = XQ_DSC_V; /* valid */ + xq->var->rbdl_buf[5] = 0; + + /* update read status words*/ + wstatus = Map_WriteW(xq->var->rbdl_ba + 8, 4, &xq->var->rbdl_buf[4]); + if (wstatus) return xq_nxm_error(xq); + + /* set to next bdl (implicit chain) */ + xq->var->rbdl_ba += 12; + break; + } /* switch */ + + /* --------------------------- Done, finish up -----------------------------*/ + + /* mark transmission complete */ + xq_csr_set_clr(xq, XQ_CSR_RI, 0); + + /* reset sanity timer */ + xq_reset_santmr(xq); + + return SCPE_OK; +} +#endif /* ifdef VM_PDP11 */ + +t_stat xq_wr_csr(CTLR* xq, int32 data) +{ + uint16 set_bits = data & XQ_CSR_RW; /* set RW set bits */ + uint16 clr_bits = ((data ^ XQ_CSR_RW) & XQ_CSR_RW) /* clear RW cleared bits */ + | (data & XQ_CSR_W1) /* write 1 to clear bits */ + | ((data & XQ_CSR_XI) ? XQ_CSR_NI : 0); /* clearing XI clears NI */ + + sim_debug(DBG_REG, xq->dev, "xq_wr_csr(data=0x%08X)\n", data); + + /* reset controller when SR transitions to cleared */ + if (xq->var->csr & XQ_CSR_SR & ~data) { + xq_sw_reset(xq); + return SCPE_OK; + } + +#if 0 /* controller should ALWAYS have an active timer if enabled (for HW sanity) */ + /* start/stop receive timer when RE transitions */ + if ((xq->var->csr ^ data) & XQ_CSR_RE) { + if (data & XQ_CSR_RE) + sim_activate(&xq->unit[0], clock_cosched (tmxr_poll)); + else + sim_cancel(&xq->unit[0]); + } +#endif + + /* update CSR bits */ + xq_csr_set_clr (xq, set_bits, clr_bits); + +#ifdef VM_PDP11 + /* request boot/diagnostic rom? [PDP-11 only] */ + if ((xq->var->csr & XQ_CSR_BP) == XQ_CSR_BP) /* all bits must be on */ + xq_process_bootrom(xq); +#endif + + return SCPE_OK; +} + +t_stat xq_wr(int32 data, int32 PA, int32 access) +{ + t_stat status; + CTLR* xq = xq_pa2ctlr(PA); + int index = (PA >> 1) & 07; /* word index */ + + sim_debug(DBG_REG, xq->dev, "xq_wr(data=0x%08X, PA=0x%08X[%s], access=%d)\n", data, PA, xq_xmit_regnames[index], access); + + switch (index) { + case 0: /* these should not be written */ + case 1: + break; + case 2: /* receive bdl low bits */ + xq->var->rbdl[0] = data; + break; + case 3: /* receive bdl high bits */ + xq->var->rbdl[1] = data; + status = xq_dispatch_rbdl(xq); /* start receive operation */ + break; + case 4: /* transmit bdl low bits */ + xq->var->xbdl[0] = data; + break; + case 5: /* transmit bdl high bits */ + xq->var->xbdl[1] = data; + status = xq_dispatch_xbdl(xq); /* start transmit operation */ + break; + case 6: /* vector address register */ + status = xq_wr_var(xq, data); + break; + case 7: /* control and status register */ + status = xq_wr_csr(xq, data); + break; + } + return SCPE_OK; +} + + +/* reset device */ +t_stat xq_reset(DEVICE* dptr) +{ + t_stat status; + CTLR* xq = xq_dev2ctlr(dptr); + const uint16 set_bits = XQ_CSR_RL | XQ_CSR_XL; + + sim_debug(DBG_TRC, xq->dev, "xq_reset()\n"); + + /* calculate MAC checksum */ + xq_make_checksum(xq); + + /* init vector address register */ + switch (xq->var->type) { + case XQ_T_DEQNA: + xq->var->var = 0; + break; + case XQ_T_DELQA: + xq->var->var = XQ_VEC_MS | XQ_VEC_OS; + break; + } + xq->dib->vec = 0; + + /* init control status register */ + xq_csr_set_clr(xq, set_bits, (uint16) ~set_bits); + + /* clear interrupts unconditionally */ + xq_clrint(xq); + + /* init read queue (first time only) */ + status = ethq_init(&xq->var->ReadQ, XQ_QUE_MAX); + if (status != SCPE_OK) + return status; + + /* clear read queue */ + ethq_clear(&xq->var->ReadQ); + + /* reset ethernet interface */ + if (xq->var->etherface) { + status = eth_filter (xq->var->etherface, 1, &xq->var->mac, 0, 0); + xq_csr_set_clr(xq, XQ_CSR_OK, 0); + + /* start service timer */ + sim_activate_abs(&xq->unit[0], tmxr_poll); + } + + /* set hardware sanity controls */ + if (xq->var->sanity.enabled) { + xq->var->sanity.quarter_secs = XQ_HW_SANITY_SECS * 4/*qsec*/; + xq->var->sanity.max = XQ_HW_SANITY_SECS * xq->var->poll; + } + return SCPE_OK; +} + +void xq_reset_santmr(CTLR* xq) +{ + sim_debug(DBG_TRC, xq->dev, "xq_reset_santmr()\n"); + if (xq->var->sanity.enabled) { + sim_debug(DBG_SAN, xq->dev, "SANITY TIMER RESETTING, qsecs: %d\n", xq->var->sanity.quarter_secs); + + /* reset sanity countdown timer to max count */ + xq->var->sanity.timer = xq->var->sanity.max; + } +} + +t_stat xq_boot_host(CTLR* xq) +{ + sim_debug(DBG_TRC, xq->dev, "xq_boot_host()\n"); + /* + The manual says the hardware should force the Qbus BDCOK low for + 3.6 microseconds, which will cause the host to reboot. + + Since the SIMH Qbus emulator does not have this functionality, we call + a special STOP_ code, and let the CPU stop dispatch routine decide + what the appropriate cpu-specific behavior should be. + */ + return STOP_SANITY; +} + +t_stat xq_system_id (CTLR* xq, const ETH_MAC dest, uint16 receipt_id) +{ + static uint16 receipt = 0; + ETH_PACK system_id; + uint8* const msg = &system_id.msg[0]; + t_stat status; + + sim_debug(DBG_TRC, xq->dev, "xq_system_id()\n"); + if (xq->var->type != XQ_T_DELQA) /* DELQA-only function */ + return SCPE_NOFNC; + + memset (&system_id, 0, sizeof(system_id)); + memcpy (&msg[0], dest, sizeof(ETH_MAC)); + memcpy (&msg[6], xq->var->setup.valid ? xq->var->setup.macs[0] : xq->var->mac, sizeof(ETH_MAC)); + msg[12] = 0x60; /* type */ + msg[13] = 0x02; /* type */ + msg[14] = 0x1C; /* character count */ + msg[15] = 0x00; /* character count */ + msg[16] = 0x07; /* code */ + msg[17] = 0x00; /* zero pad */ + if (receipt_id) { + msg[18] = receipt_id & 0xFF; /* receipt number */ + msg[19] = (receipt_id >> 8) & 0xFF; /* receipt number */ + } else { + msg[18] = receipt & 0xFF; /* receipt number */ + msg[19] = (receipt++ >> 8) & 0xFF; /* receipt number */ + } + + /* MOP VERSION */ + msg[20] = 0x01; /* type */ + msg[21] = 0x00; /* type */ + msg[22] = 0x03; /* length */ + msg[23] = 0x03; /* version */ + msg[24] = 0x01; /* eco */ + msg[25] = 0x00; /* user eco */ + + /* FUNCTION */ + msg[26] = 0x02; /* type */ + msg[27] = 0x00; /* type */ + msg[28] = 0x02; /* length */ + msg[29] = 0x00; /* value 1 ??? */ + msg[30] = 0x00; /* value 2 */ + + /* HARDWARE ADDRESS */ + msg[31] = 0x07; /* type */ + msg[32] = 0x00; /* type */ + msg[33] = 0x06; /* length */ + memcpy (&msg[34], xq->var->mac, sizeof(ETH_MAC)); /* ROM address */ + + /* DEVICE TYPE */ + msg[40] = 37; /* type */ + msg[41] = 0x00; /* type */ + msg[42] = 0x01; /* length */ + msg[43] = 0x11; /* value (0x11=DELQA) */ + + /* write system id */ + system_id.len = 60; + status = eth_write(xq->var->etherface, &system_id, NULL); + + return status; +} + +/* +** service routine - used for ethernet reading loop +*/ +t_stat xq_svc(UNIT* uptr) +{ + CTLR* xq = xq_unit2ctlr(uptr); + + /* if the receiver is enabled */ + if (xq->var->csr & XQ_CSR_RE) { + t_stat status; + int queue_size; + + /* First pump any queued packets into the system */ + if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) + status = xq_process_rbdl(xq); + + /* Now read and queue packets that have arrived */ + /* This is repeated as long as they are available and we have room */ + do + { + queue_size = xq->var->ReadQ.count; + /* read a packet from the ethernet - processing is via the callback */ + status = eth_read (xq->var->etherface, &xq->var->read_buffer, xq->var->rcallback); + } while (queue_size != xq->var->ReadQ.count); + + /* Now pump any still queued packets into the system */ + if ((xq->var->ReadQ.count > 0) && (~xq->var->csr & XQ_CSR_RL)) + status = xq_process_rbdl(xq); + } + + /* has sanity timer expired? if so, reboot */ + if (xq->var->sanity.enabled) + if (--xq->var->sanity.timer <= 0) + xq_boot_host(xq); + + /* has system id timer expired? if so, do system id */ + if (--xq->var->idtmr <= 0) { + const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00}; + xq_system_id(xq, mop_multicast, 0); + + /* reset system ID counter for next event */ + xq->var->idtmr = XQ_SYSTEM_ID_SECS * xq->var->poll; + } + + /* resubmit service timer */ + sim_activate(&xq->unit[0], tmxr_poll); + + return SCPE_OK; +} + + +/* attach device: */ +t_stat xq_attach(UNIT* uptr, char* cptr) +{ + t_stat status; + char* tptr; + CTLR* xq = xq_unit2ctlr(uptr); + char buffer[80]; /* buffer for runtime input */ + + sim_debug(DBG_TRC, xq->dev, "xq_attach(cptr=%s)\n", cptr); + + /* runtime selection of ethernet port? */ + if (*cptr == '?') { /* I/O style derived from main() */ + memset (buffer, 0, sizeof(buffer)); /* clear read buffer */ + eth_show (stdout, uptr, 0, NULL); /* show ETH devices */ + printf ("Select device (ethX or )? "); /* prompt for device */ + cptr = read_line (buffer, sizeof(buffer), stdin); /* read command line */ + if (cptr == NULL) return SCPE_ARG; /* ignore EOF */ + if (*cptr == 0) return SCPE_ARG; /* ignore blank */ + } /* resume attaching */ + + tptr = (char *) malloc(strlen(cptr) + 1); + if (tptr == NULL) return SCPE_MEM; + strcpy(tptr, cptr); + + xq->var->etherface = (ETH_DEV *) malloc(sizeof(ETH_DEV)); + if (!xq->var->etherface) return SCPE_MEM; + + status = eth_open(xq->var->etherface, cptr, xq->dev, DBG_ETH); + if (status != SCPE_OK) { + free(tptr); + free(xq->var->etherface); + xq->var->etherface = 0; + return status; + } + uptr->filename = tptr; + uptr->flags |= UNIT_ATT; + + /* turn on transceiver power indicator */ + xq_csr_set_clr(xq, XQ_CSR_OK, 0); + + /* reset the device with the new attach info */ + xq_reset(xq->dev); + + return SCPE_OK; +} + +/* detach device: */ + +t_stat xq_detach(UNIT* uptr) +{ + CTLR* xq = xq_unit2ctlr(uptr); + sim_debug(DBG_TRC, xq->dev, "xq_detach()\n"); + + if (uptr->flags & UNIT_ATT) { + eth_close (xq->var->etherface); + free(xq->var->etherface); + xq->var->etherface = 0; + free(uptr->filename); + uptr->filename = NULL; + uptr->flags &= ~UNIT_ATT; + /* cancel service timer */ + sim_cancel(&xq->unit[0]); + } + + /* turn off transceiver power indicator */ + xq_csr_set_clr(xq, 0, XQ_CSR_OK); + + return SCPE_OK; +} + +void xq_setint(CTLR* xq) +{ + xq->var->irq = 1; + SET_INT(XQ); + return; +} + +void xq_clrint(CTLR* xq) +{ + int i; + xq->var->irq = 0; /* set controller irq off */ + /* clear master interrupt? */ + for (i=0; iirq) { /* if any irqs enabled */ + SET_INT(XQ); /* set master interrupt on */ + return; + } + CLR_INT(XQ); /* clear master interrupt */ + return; +} + +int32 xq_int (void) +{ + int i; + for (i=0; ivar->irq) { /* if interrupt pending */ + xq_clrint(xq); /* clear interrupt */ + return xq->dib->vec; /* return vector */ + } + } + return 0; /* no interrupt request active */ +} + +void xq_csr_set_clr (CTLR* xq, uint16 set_bits, uint16 clear_bits) +{ + uint16 saved_csr = xq->var->csr; + + /* set the bits in the csr */ + xq->var->csr = (xq->var->csr | set_bits) & ~clear_bits; + + sim_debug_u16(DBG_CSR, xq->dev, xq_csr_bits, saved_csr, xq->var->csr, 1); + + /* check and correct the state of controller interrupt */ + + /* if IE is transitioning, process it */ + if ((saved_csr ^ xq->var->csr) & XQ_CSR_IE) { + + /* if IE transitioning low and interrupt set, clear interrupt */ + if ((clear_bits & XQ_CSR_IE) && xq->var->irq) + xq_clrint(xq); + + /* if IE transitioning high, and XI or RI is high, + set interrupt if interrupt is off */ + if ((set_bits & XQ_CSR_IE) && (xq->var->csr & XQ_CSR_XIRI) && !xq->var->irq) + xq_setint(xq); + + } else { /* IE is not transitioning */ + + /* if interrupts are enabled */ + if (xq->var->csr & XQ_CSR_IE) { + + /* if XI or RI transitioning high and interrupt off, set interrupt */ + if (((saved_csr ^ xq->var->csr) & (set_bits & XQ_CSR_XIRI)) && !xq->var->irq) { + xq_setint(xq); + + } else { + + /* if XI or RI transitioning low, and both XI and RI are now low, + clear interrupt if interrupt is on */ + if (((saved_csr ^ xq->var->csr) & (clear_bits & XQ_CSR_XIRI)) + && !(xq->var->csr & XQ_CSR_XIRI) + && xq->var->irq) + xq_clrint(xq); + } + + } /* IE enabled */ + + } /* IE transitioning */ +} + +/*============================================================================== +/ debugging routines +/=============================================================================*/ + + +void xq_debug_setup(CTLR* xq) +{ + int i; + char buffer[20]; + if (xq->var->write_buffer.msg[0]) + printf ("%s: setup> MOP info present!\n", xq->dev->name); + + for (i = 0; i < XQ_FILTER_MAX; i++) { + eth_mac_fmt(&xq->var->setup.macs[i], buffer); + printf ("%s: setup> set addr[%d]: %s\n", xq->dev->name, i, buffer); + } + + if (xq->var->write_buffer.len > 128) { + char buffer[20] = {0}; + uint16 len = xq->var->write_buffer.len; + if (len & XQ_SETUP_MC) strcat(buffer, "MC "); + if (len & XQ_SETUP_PM) strcat(buffer, "PM "); + if (len & XQ_SETUP_LD) strcat(buffer, "LD "); + if (len & XQ_SETUP_ST) strcat(buffer, "ST "); + printf ("%s: setup> Length [%d =0x%X, LD:%d, ST:%d] info: %s\n", + xq->dev->name, len, len, (len & XQ_SETUP_LD) >> 2, (len & XQ_SETUP_ST) >> 4, buffer); + } +} diff --git a/PDP11/pdp11_xq.h b/PDP11/pdp11_xq.h new file mode 100644 index 0000000..c90edff --- /dev/null +++ b/PDP11/pdp11_xq.h @@ -0,0 +1,233 @@ +/* pdp11_xq.h: DEQNA/DELQA ethernet controller information + ------------------------------------------------------------------------------ + + Copyright (c) 2002-2005, David T. Hittner + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + Modification history: + + 07-Jul-05 RMS Removed extraneous externs + 20-Jan-04 DTH Added new sanity timer and system id timer + 19-Jan-04 DTH Added XQ_SERVICE_INTERVAL, poll + 09-Jan-04 DTH Added Boot PDP diagnostic definition, XI/RI combination + 26-Dec-03 DTH Moved ethernet queue definitions to sim_ether + 25-Nov-03 DTH Added interrupt request flag + 02-Jun-03 DTH Added struct xq_stats + 28-May-03 DTH Made xq_msg_que.item dynamic + 28-May-03 MP Optimized structures, removed rtime variable + 06-May-03 DTH Changed 32-bit t_addr to uint32 for v3.0 + 28-Apr-03 DTH Added callbacks for multicontroller identification + 25-Mar-03 DTH Removed bootrom field - no longer needed; Updated copyright + 15-Jan-03 DTH Merged Mark Pizzolato's changes into main source + 13-Jan-03 MP Added countdown for System Id multicast packets + 10-Jan-03 DTH Added bootrom field + 30-Dec-02 DTH Added setup valid field + 21-Oct-02 DTH Corrected copyright again + 15-Oct-02 DTH Fixed copyright, added sanity timer support + 10-Oct-02 DTH Added more setup fields and bitmasks + 08-Oct-02 DTH Integrated with 2.10-0p4, added variable vector and copyrights + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 15-Aug-02 DTH Started XQ simulation + + ------------------------------------------------------------------------------ +*/ + +#ifndef _PDP11_XQ_H +#define _PDP11_XQ_H + +#if defined (VM_PDP10) /* PDP10 version */ +#error "DEQNA/DELQA not supported on PDP10!" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +#define XQ_RDX 16 +#define XQ_WID 32 +extern int32 PSL; /* PSL */ +extern int32 fault_PC; /* fault PC */ +extern int32 int_req[IPL_HLVL]; + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define XQ_RDX 8 +#define XQ_WID 16 +extern int32 int_req[IPL_HLVL]; +#endif + +#include "sim_ether.h" + +#define XQ_QUE_MAX 500 /* read queue size in packets */ +#define XQ_FILTER_MAX 14 /* number of filters allowed */ +#define XQ_SERVICE_INTERVAL 100 /* polling interval - X per second */ +#define XQ_SYSTEM_ID_SECS 540 /* seconds before system ID timer expires */ +#define XQ_HW_SANITY_SECS 240 /* seconds before HW sanity timer expires */ +#define XQ_MAX_CONTROLLERS 2 /* maximum controllers allowed */ + +enum xq_type {XQ_T_DEQNA, XQ_T_DELQA}; + +struct xq_sanity { + int enabled; /* sanity timer enabled? 2=HW, 1=SW, 0=off */ + int quarter_secs; /* sanity timer value in 1/4 seconds */ + int max; /* maximum timeout (based on poll) */ + int timer; /* countdown timer */ +}; + +struct xq_setup { + int valid; /* is the setup block valid? */ + int promiscuous; /* promiscuous mode enabled */ + int multicast; /* enable all multicast addresses */ + int l1; /* first diagnostic led state */ + int l2; /* second diagnostic led state */ + int l3; /* third diagnostic led state */ + int sanity_timer; /* sanity timer value (encoded) */ + ETH_MAC macs[XQ_FILTER_MAX]; /* MAC addresses to respond to */ +}; + +struct xq_stats { + int recv; /* received packets */ + int filter; /* filtered packets */ + int xmit; /* transmitted packets */ + int fail; /* transmit failed */ + int runt; /* runts */ + int giant; /* oversize packets */ + int setup; /* setup packets */ + int loop; /* loopback packets */ +}; + +struct xq_meb { /* MEB block */ + uint8 type; + uint8 add_lo; + uint8 add_mi; + uint8 add_hi; + uint8 siz_lo; + uint8 siz_hi; +}; + +struct xq_device { + /*+ initialized values - DO NOT MOVE */ + ETH_PCALLBACK rcallback; /* read callback routine */ + ETH_PCALLBACK wcallback; /* write callback routine */ + ETH_MAC mac; /* MAC address */ + enum xq_type type; /* controller type */ + int poll; /* poll ethernet times/sec */ + struct xq_sanity sanity; /* sanity timer information */ + /*- initialized values - DO NOT MOVE */ + + /* I/O register storage */ + uint16 addr[6]; + uint16 rbdl[2]; + uint16 xbdl[2]; + uint16 var; + uint16 csr; + uint32 irq; /* interrupt request flag */ + + /* buffers, etc. */ + struct xq_setup setup; + struct xq_stats stats; + uint8 mac_checksum[2]; + uint16 rbdl_buf[6]; + uint16 xbdl_buf[6]; + uint32 rbdl_ba; + uint32 xbdl_ba; + ETH_DEV* etherface; + int receiving; + ETH_PACK read_buffer; + ETH_PACK write_buffer; + ETH_QUE ReadQ; + int idtmr; /* countdown for ID Timer */ +}; + +struct xq_controller { + DEVICE* dev; /* device block */ + UNIT* unit; /* unit block */ + DIB* dib; /* device interface block */ + struct xq_device* var; /* controller-specific variables */ +}; + +typedef struct xq_controller CTLR; + + +#define XQ_CSR_RI 0x8000 /* Receive Interrupt Request (RI) [RO/W1] */ +#define XQ_CSR_PE 0x4000 /* Parity Error in Host Memory (PE) [RO] */ +#define XQ_CSR_CA 0x2000 /* Carrier from Receiver Enabled (CA) [RO] */ +#define XQ_CSR_OK 0x1000 /* Ethernet Transceiver Power (OK) [RO] */ +#define XQ_CSR_RR 0x0800 /* Reserved : Set to Zero (RR) [RO] */ +#define XQ_CSR_SE 0x0400 /* Sanity Timer Enable (SE) [RW] */ +#define XQ_CSR_EL 0x0200 /* External Loopback (EL) [RW] */ +#define XQ_CSR_IL 0x0100 /* Internal Loopback (IL) [RW] */ +#define XQ_CSR_XI 0x0080 /* Transmit Interrupt Request (XI) [RO/W1] */ +#define XQ_CSR_IE 0x0040 /* Interrupt Enable (IE) [RW] */ +#define XQ_CSR_RL 0x0020 /* Receive List Invalid/Empty (RL) [RO] */ +#define XQ_CSR_XL 0x0010 /* Transmit List Invalid/Empty (XL) [RO] */ +#define XQ_CSR_BD 0x0008 /* Boot/Diagnostic ROM Load (BD) [RW] */ +#define XQ_CSR_NI 0x0004 /* NonExistant Memory Timeout (NXM) [RO] */ +#define XQ_CSR_SR 0x0002 /* Software Reset (SR) [RW] */ +#define XQ_CSR_RE 0x0001 /* Receiver Enable (RE) [RW] */ + +/* special access bitmaps */ +#define XQ_CSR_RO 0xF8B4 /* Read-Only bits */ +#define XQ_CSR_RW 0x074B /* Read/Write bits */ +#define XQ_CSR_W1 0x8080 /* Write-one-to-clear bits */ +#define XQ_CSR_BP 0x0208 /* Boot PDP diagnostic ROM */ +#define XQ_CSR_XIRI 0X8080 /* Transmit & Receive Interrupts */ + +#define XQ_VEC_MS 0x8000 /* Mode Select (MO) [RW] */ +#define XQ_VEC_OS 0x4000 /* Option Switch Setting (OS) [RO] */ +#define XQ_VEC_RS 0x2000 /* Request Self-Test (RS) [RW] */ +#define XQ_VEC_S3 0x1000 /* Self-Test Status (S3) [RO] */ +#define XQ_VEC_S2 0x0800 /* Self-Test Status (S2) [RO] */ +#define XQ_VEC_S1 0x0400 /* Self-Test Status (S1) [RO] */ +#define XQ_VEC_ST 0x1C00 /* Self-Test (S1 + S2 + S3) [RO] */ +#define XQ_VEC_IV 0x03FC /* Interrupt Vector (IV) [RW] */ +#define XQ_VEC_RR 0x0002 /* Reserved (RR) [RO] */ +#define XQ_VEC_ID 0x0001 /* Identity Test Bit (ID) [RW] */ + +/* special access bitmaps */ +#define XQ_VEC_RO 0x5C02 /* Read-Only bits */ +#define XQ_VEC_RW 0xA3FD /* Read/Write bits */ + +#define XQ_DSC_V 0x8000 /* Valid bit */ +#define XQ_DSC_C 0x4000 /* Chain bit */ +#define XQ_DSC_E 0x2000 /* End of Message bit [Transmit only] */ +#define XQ_DSC_S 0x1000 /* Setup bit [Transmit only] */ +#define XQ_DSC_L 0x0080 /* Low Byte Termination bit [Transmit only] */ +#define XQ_DSC_H 0x0040 /* High Byte Start bit [Transmit only] */ + +#define XQ_SETUP_MC 0x0001 /* multicast bit */ +#define XQ_SETUP_PM 0x0002 /* promiscuous bit */ +#define XQ_SETUP_LD 0x000C /* led bits */ +#define XQ_SETUP_ST 0x0070 /* sanity timer bits */ + +/* debugging bitmaps */ +#define DBG_TRC 0x0001 /* trace routine calls */ +#define DBG_REG 0x0002 /* trace read/write registers */ +#define DBG_CSR 0x0004 /* watch CSR */ +#define DBG_VAR 0x0008 /* watch VAR */ +#define DBG_WRN 0x0010 /* display warnings */ +#define DBG_SAN 0x0020 /* display sanity timer info */ +#define DBG_SET 0x0040 /* display setup info */ +#define DBG_PCK 0x0080 /* display packets */ +#define DBG_ETH 0x8000 /* debug ethernet device */ + +#endif /* _PDP11_XQ_H */ diff --git a/PDP11/pdp11_xq_bootrom.h b/PDP11/pdp11_xq_bootrom.h new file mode 100644 index 0000000..e12d351 --- /dev/null +++ b/PDP11/pdp11_xq_bootrom.h @@ -0,0 +1,312 @@ +/* pdp11_xq_bootrom.h: DEQNA/DELQA bootrom data + ------------------------------------------------------------------------------ + + Copyright (c) 2003-2005, David T. Hittner + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + Modification history: + + 26-Mar-03 DTH Removed 'static' declaration + 23-Mar-03 DTH Created by extracting from merged DEQNA bootrom dumps + + ------------------------------------------------------------------------------ +*/ + +#ifndef _PDP11_XQ_BOOTROM_H +#define _PDP11_XQ_BOOTROM_H + +#ifdef VM_PDP11 + /* + Bootrom code is from merged file 23-334e5.bin, offset 050000, for 4096. bytes. + + Word 0: NOP + Word 1: Branch to extended primary boot + Word 2: Branch/Vector to Citizenship tests + Word 3: Offset from beginning to checksum word + + See INIQNA.MAR for further information on format and contents. + */ + + uint16 xq_bootrom[] = { + 0000240,0000423,0000546,0007776,0000520,0000000,0100000,0100000, + 0002000,0176000,0000000,0000000,0100000,0100000,0006000,0176000, + 0000000,0000000,0100000,0020000,0140000,0012706,0001776,0010046, + 0012761,0000014,0000004,0005061,0000006,0012761,0001010,0000016, + 0005000,0005300,0001376,0005061,0000016,0005000,0005300,0001376, + 0012761,0000002,0000016,0005061,0000016,0042767,0037777,0177664, + 0026767,0177660,0177702,0001057,0042767,0037777,0177652,0026767, + 0177646,0177664,0001050,0042767,0037777,0177650,0026767,0177644, + 0177646,0001041,0012704,0007776,0005003,0005002,0116200,0002000, + 0005202,0042700,0177400,0060003,0005304,0001370,0013700,0000006, + 0026003,0002000,0001020,0000137,0002010,0012702,0012000,0004767, + 0000040,0005700,0001010,0011602,0001002,0000167,0004530,0022702, + 0000777,0103001,0000112,0013703,0000012,0001401,0000113,0000000, + 0000776,0010637,0000764,0062701,0000016,0032761,0100000,0177776, + 0001421,0052761,0020000,0177776,0012703,0000777,0005000,0005300, + 0001376,0032761,0016000,0177776,0001405,0005303,0001370,0012700, + 0000200,0000454,0004767,0000136,0052711,0000002,0042711,0000002, + 0012703,0017777,0005303,0001376,0004567,0003514,0177700,0001014, + 0005712,0001014,0032762,0002000,0000050,0001402,0052711,0002000, + 0004567,0003464,0177622,0001402,0052712,0000100,0012711,0000002, + 0005011,0012703,0017777,0005303,0001376,0011100,0042700,0064000, + 0022700,0010060,0001402,0052712,0000100,0011200,0162701,0000016, + 0000207,0052400,0177652,0013746,0000034,0013746,0000036,0010703, + 0062703,0000210,0010337,0000034,0012737,0000340,0000036,0104400, + 0012637,0000036,0012637,0000034,0013700,0000762,0052700,0000340, + 0062703,0000010,0010337,0000004,0010037,0000006,0010637,0000766, + 0010137,0000772,0010237,0000770,0062703,0000012,0010337,0000024, + 0010037,0000026,0062703,0000022,0012761,0000774,0177776,0052761, + 0100000,0177776,0010337,0000774,0010037,0000776,0005062,0000002, + 0005012,0012700,0000162,0060200,0012704,0000112,0005020,0005304, + 0001375,0004567,0003202,0177666,0001434,0005262,0000002,0022762, + 0000002,0000002,0003355,0000207,0016637,0000002,0000762,0000006, + 0052712,0002000,0013706,0000764,0000207,0052712,0020000,0013706, + 0000766,0013701,0000772,0013702,0000770,0000207,0052712,0004000, + 0000002,0106427,0000000,0010103,0162703,0000016,0010204,0062704, + 0000012,0012705,0000006,0012300,0110024,0005305,0001374,0010204, + 0062704,0000012,0010405,0005724,0001004,0005724,0001002,0005714, + 0001421,0010504,0012700,0177777,0020024,0001016,0020024,0001014, + 0020014,0001410,0001011,0010504,0022724,0000252,0001003,0122714, + 0000004,0103002,0052712,0000001,0012700,0177777,0004767,0003314, + 0013705,0000774,0010703,0062703,0000044,0010337,0000774,0052711, + 0000100,0010461,0177772,0005000,0010061,0177774,0012703,0010000, + 0005303,0001376,0052712,0004000,0000207,0062706,0000004,0010537, + 0000774,0005200,0001767,0011100,0032700,0000200,0001763,0011400, + 0042700,0037777,0022700,0140000,0001355,0005764,0000010,0001752, + 0005764,0000012,0001747,0052711,0000002,0042711,0000002,0012711, + 0002000,0106437,0000762,0004567,0002576,0177666,0001402,0000207, + 0010703,0062703,0177160,0010362,0000002,0010362,0000006,0062703, + 0000005,0010362,0000004,0005062,0000010,0010203,0062703,0000162, + 0012700,0000002,0012705,0000006,0105023,0012704,0000007,0026262, + 0000004,0000006,0003003,0016262,0000002,0000006,0117223,0000006, + 0005262,0000006,0005304,0001363,0005305,0001356,0012704,0000020, + 0105023,0005304,0001375,0005300,0001345,0004567,0002432,0177705, + 0001403,0052712,0000002,0000207,0005262,0000010,0022762,0000764, + 0000010,0003323,0042761,0100000,0177776,0005062,0000006,0010204, + 0062704,0000163,0010462,0000010,0005304,0012703,0000060,0105024, + 0005303,0001375,0062762,0000010,0000002,0016262,0000010,0000002, + 0012762,0000060,0000004,0105062,0000012,0000261,0106162,0000012, + 0103041,0106162,0000012,0062762,0000010,0000002,0000433,0016204, + 0000010,0005304,0012703,0000060,0112724,0000377,0005303,0001374, + 0012762,0000060,0000004,0016262,0000010,0000002,0112762,0000377, + 0000012,0000241,0106162,0000012,0103405,0106162,0000012,0062762, + 0000010,0000002,0016204,0000002,0012703,0000007,0105064,0177770, + 0116224,0000012,0005303,0001372,0004567,0002154,0177750,0001402, + 0000167,0000414,0005762,0000006,0001011,0000241,0106172,0000002, + 0103010,0106072,0000002,0106072,0000002,0000403,0000261,0106172, + 0000002,0016204,0000010,0010203,0062703,0004362,0012700,0000006, + 0111423,0062704,0000010,0005300,0001373,0012711,0000001,0012700, + 0177775,0004767,0002352,0010461,0177766,0005061,0177770,0004767, + 0002372,0010461,0177772,0005061,0177774,0012700,0077777,0032711, + 0100000,0001003,0005300,0001373,0000523,0005000,0004567,0002412, + 0000000,0040000,0001115,0016204,0000010,0005204,0010203,0062703, + 0004362,0012700,0000006,0111423,0062704,0000010,0005300,0001373, + 0042711,0100200,0012700,0177775,0004767,0002224,0010461,0177766, + 0005061,0177770,0012700,0177775,0004767,0002240,0010461,0177772, + 0005061,0177774,0012700,0077777,0032711,0100000,0001003,0005300, + 0001373,0000454,0005000,0004567,0002260,0000000,0000000,0001040, + 0042711,0000001,0010204,0062704,0001362,0010205,0062705,0004362, + 0012700,0000006,0122425,0001024,0005300,0001374,0005362,0000004, + 0001007,0005762,0000006,0001034,0005262,0000006,0000167,0177256, + 0005762,0000006,0001002,0000167,0177222,0000261,0000167,0177304, + 0052712,0000004,0000405,0052712,0004000,0000402,0052712,0001004, + 0052761,0100000,0177776,0000207,0000074,0001422,0002752,0177777, + 0052761,0100000,0177776,0052711,0001000,0010703,0062703,0176046, + 0010362,0000002,0010362,0000006,0062703,0000004,0010362,0000004, + 0010703,0062703,0177726,0010362,0000010,0010203,0062703,0004362, + 0017205,0000010,0026262,0000004,0000006,0003003,0016262,0000002, + 0000006,0117223,0000006,0005262,0000006,0005305,0001363,0017200, + 0000010,0004567,0001536,0103425,0017200,0000010,0004567,0001752, + 0000000,0020000,0001401,0000003,0010204,0062704,0001362,0010205, + 0062705,0004362,0017200,0000010,0122425,0001003,0005300,0001374, + 0000403,0052712,0000010,0000207,0062762,0000002,0000010,0022772, + 0177777,0000010,0001402,0000167,0177620,0012700,0177770,0004767, + 0001536,0010461,0177766,0005061,0177770,0010203,0062703,0000040, + 0010304,0012700,0000010,0012723,0100000,0012723,0100000,0010213, + 0062723,0000012,0012723,0177777,0005023,0005023,0005300,0001363, + 0010403,0052763,0000200,0000002,0052763,0000300,0000016,0012763, + 0177776,0000022,0052763,0000100,0000032,0062763,0000002,0000034, + 0062763,0000004,0000050,0012763,0040000,0000062,0010363,0000064, + 0062763,0000074,0000064,0010363,0000100,0062763,0000070,0000100, + 0012763,0177776,0000102,0012763,0120000,0000112,0012763,0177775, + 0000116,0012763,0020000,0000126,0010461,0177772,0005061,0177774, + 0012700,0077777,0032711,0100000,0001005,0005300,0001373,0052712, + 0001000,0000411,0012700,0000020,0004567,0001376,0100000,0020000, + 0001405,0052712,0040000,0052712,0000020,0000207,0010203,0062703, + 0000040,0012700,0000010,0016305,0000000,0042705,0037777,0022705, + 0140000,0001357,0022700,0000004,0001403,0022700,0000001,0001007, + 0005763,0000010,0001346,0005763,0000012,0001343,0000424,0022700, + 0000002,0001405,0032763,0100000,0000010,0001733,0000414,0016305, + 0000010,0042705,0026417,0022705,0000000,0001323,0016305,0000012, + 0042705,0176000,0001716,0062703,0000014,0005300,0001324,0010203, + 0062703,0000012,0010204,0062704,0001362,0010405,0022324,0001303, + 0022324,0001301,0022324,0001277,0005724,0001275,0005724,0001273, + 0022524,0001271,0022524,0001267,0022524,0001265,0010203,0062703, + 0000162,0010305,0012700,0000113,0005023,0005300,0001375,0010204, + 0062704,0000012,0004767,0000046,0062705,0000020,0004767,0000036, + 0004567,0000444,0177674,0001401,0000207,0012700,0177777,0032711, + 0020000,0001423,0005300,0001373,0052712,0100000,0000207,0010446, + 0012700,0000006,0005205,0012703,0000007,0111425,0005303,0001375, + 0005204,0005300,0001367,0012604,0000207,0005712,0001017,0052711, + 0001400,0012700,0000056,0004767,0000132,0012700,0000074,0004567, + 0000522,0103005,0042712,0001000,0052712,0100000,0000207,0012700, + 0000074,0004767,0000156,0001761,0001403,0052712,0000040,0000207, + 0012700,0000074,0004767,0000232,0001370,0012700,0002734,0004767, + 0000042,0012700,0002752,0004567,0000432,0103757,0012700,0002752, + 0004767,0000100,0001766,0001351,0012700,0002752,0004767,0000162, + 0001344,0000207,0010203,0062703,0004362,0010204,0062704,0000012, + 0010405,0012423,0012423,0012423,0012523,0012523,0012523,0012723, + 0000220,0005023,0012723,0000001,0110023,0005300,0001375,0005062, + 0000002,0000207,0004567,0000542,0000000,0020000,0001004,0062716, + 0000002,0005712,0000207,0016200,0000050,0042700,0137777,0001010, + 0016200,0000030,0032700,0137777,0001003,0042712,0040000,0000757, + 0005262,0000002,0022762,0000144,0000002,0003751,0042712,0040000, + 0000750,0010204,0062704,0001362,0010205,0062705,0004362,0122425, + 0001002,0005300,0001374,0000207,0010200,0062700,0000162,0010046, + 0011500,0005300,0004767,0000270,0010461,0177766,0005061,0177770, + 0012500,0004767,0000306,0012764,0130000,0000002,0011664,0000004, + 0010461,0177772,0005061,0177774,0012704,0017777,0032711,0100000, + 0001010,0005304,0001373,0052712,0001000,0005726,0052712,0010000, + 0000430,0016500,0177776,0006300,0005400,0004567,0000274,0000000, + 0020000,0001363,0016500,0177776,0006300,0005400,0010204,0062704, + 0001362,0012603,0122423,0001352,0005300,0001374,0022714,0051343, + 0001345,0000205,0005046,0006000,0005516,0061600,0005400,0004767, + 0000076,0010461,0177766,0005061,0177770,0004767,0000116,0005726, + 0001403,0052764,0000200,0000002,0010461,0177772,0005061,0177774, + 0012703,0000777,0005000,0032711,0100000,0001010,0005300,0001376, + 0005303,0001371,0052712,0001000,0000261,0000401,0000241,0000205, + 0010203,0062703,0001362,0012704,0000200,0012723,0051343,0005304, + 0001374,0004567,0000020,0000020,0001362,0000207,0004567,0000006, + 0000040,0004362,0000207,0012503,0060203,0010304,0012723,0100000, + 0012723,0120000,0012513,0060223,0010023,0012723,0100000,0012723, + 0100000,0012723,0100000,0005023,0000205,0010046,0005000,0011104, + 0052711,0100200,0042704,0077401,0022704,0100260,0001401,0010700, + 0016204,0000040,0042704,0037777,0022704,0140000,0001401,0010700, + 0016204,0000050,0100002,0042704,0077777,0042704,0076417,0022504, + 0001401,0010700,0016204,0000052,0042704,0176000,0001001,0010700, + 0016204,0000020,0042704,0037777,0022704,0140000,0001401,0010700, + 0016204,0000030,0010446,0042704,0007777,0022504,0001401,0010700, + 0012604,0042704,0174377,0022762,0177775,0000046,0001002,0005726, + 0000415,0032762,0010000,0000042,0001401,0005004,0016203,0000032, + 0042703,0177400,0060304,0022604,0001401,0010700,0010003,0001402, + 0052712,0040000,0000205,0000005,0012706,0017776,0010616,0011646, + 0162716,0003056,0010703,0062703,0000014,0010337,0000004,0011100, + 0000401,0000000,0004767,0000230,0011605,0012725,0022410,0012725, + 0000401,0105025,0105025,0012725,0000621,0112725,0000002,0012702, + 0002752,0110225,0000302,0110225,0012702,0000013,0005000,0004767, + 0000452,0001350,0012702,0002756,0004767,0000660,0001046,0011603, + 0112304,0005302,0120427,0000002,0001404,0105704,0001335,0162702, + 0000004,0105713,0001402,0121300,0001030,0112300,0105200,0005302, + 0003410,0012305,0005723,0162702,0000004,0003403,0112325,0005302, + 0003375,0105704,0001417,0005003,0011605,0112725,0000012,0110025, + 0110325,0005005,0012702,0000003,0000722,0105700,0001673,0012703, + 0000001,0000762,0004767,0001232,0112346,0112366,0000001,0000207, + 0042761,0000002,0000016,0016605,0000002,0010504,0062704,0177720, + 0010466,0000004,0012702,0000020,0005024,0077202,0010504,0062704, + 0177760,0005065,0177722,0010465,0177724,0005065,0177742,0010465, + 0177744,0052765,0100000,0177722,0012702,0002756,0006202,0005402, + 0010265,0177726,0052765,0120000,0177742,0016604,0000004,0010467, + 0001324,0005067,0001322,0062704,0000020,0010467,0001314,0005067, + 0001312,0116167,0000000,0001262,0116167,0000002,0001255,0116167, + 0000004,0001250,0116167,0000006,0001243,0116167,0000010,0001236, + 0116167,0000012,0001231,0105267,0001232,0042761,0000002,0000016, + 0052761,0000400,0000016,0004767,0001104,0005065,0000002,0016744, + 0001174,0016744,0001166,0016744,0001160,0012744,0000000,0012744, + 0001000,0012744,0000253,0004767,0000046,0000207,0016605,0000002, + 0010504,0010244,0012744,0000540,0016744,0001122,0016744,0001114, + 0016744,0001106,0016744,0001060,0016744,0001052,0016744,0001044, + 0062705,0177740,0062702,0000016,0020227,0000074,0002003,0012702, + 0000074,0000407,0032702,0000001,0001404,0052765,0000200,0000002, + 0005202,0006202,0005402,0010265,0000006,0005065,0000010,0005065, + 0000012,0016761,0001024,0000010,0016761,0001020,0000012,0012704, + 0000204,0004767,0000610,0103012,0001404,0032765,0001000,0000010, + 0001354,0042765,0000200,0000002,0000244,0000207,0042765,0000200, + 0000002,0032702,0040004,0001401,0000000,0000207,0016605,0000002, + 0062705,0177720,0005065,0000010,0005065,0000012,0016761,0000706, + 0000004,0016761,0000702,0000006,0052761,0000001,0000016,0012704, + 0100004,0004767,0000470,0103030,0001355,0052761,0000002,0000016, + 0012767,0000253,0000576,0012767,0000400,0000572,0012767,0000000, + 0000566,0105267,0000616,0005000,0042761,0000002,0000016,0052761, + 0000400,0000016,0000244,0000207,0042761,0000001,0000016,0052761, + 0000002,0000016,0016605,0000002,0016502,0177776,0042761,0000002, + 0000016,0052761,0000400,0000016,0022765,0000540,0177774,0001041, + 0105767,0000520,0001015,0026765,0000456,0177772,0001267,0026765, + 0000444,0177770,0001263,0026765,0000432,0177766,0001257,0000207, + 0122715,0000003,0001253,0016567,0177766,0000410,0016567,0177770, + 0000404,0016567,0177772,0000400,0105067,0000430,0000244,0005000, + 0000207,0022765,0000220,0177774,0001423,0022765,0001140,0177774, + 0001225,0122715,0000005,0001222,0004767,0000262,0016464,0177776, + 0177770,0016464,0177774,0177766,0016464,0177772,0177764,0000437, + 0010504,0060204,0010503,0062703,0177720,0016302,0000010,0042702, + 0174377,0156302,0000012,0062702,0000056,0022724,0000002,0001027, + 0062765,0000010,0177776,0032714,0000001,0001021,0010503,0062703, + 0177760,0012423,0012423,0012423,0010504,0062704,0177774,0016744, + 0000234,0016744,0000226,0016744,0000220,0004767,0177122,0000167, + 0177272,0016737,0000156,0000030,0016737,0000152,0000032,0016737, + 0000146,0000034,0052761,0000002,0000016,0000264,0000207,0012703, + 0037777,0000241,0012702,0000220,0030461,0000016,0001006,0005303, + 0001376,0005302,0001371,0000261,0000207,0016102,0000016,0010261, + 0000016,0032765,0040000,0000010,0001401,0000261,0000207,0010546, + 0010703,0062703,0000050,0012702,0000030,0012325,0005725,0112325, + 0005302,0001375,0012605,0010504,0012702,0000034,0010244,0012744, + 0001140,0000207,0000253,0000400,0000000,0000007,0000001,0001403, + 0000000,0000002,0000402,0003400,0003000,0000000,0000000,0000000, + 0000144,0022401,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0000000, + 0000000,0000000,0000000,0000000,0000000,0000000,0000000,0102206 + }; +#endif /* VM_PDP11 */ + +#endif /* _PDP11_XQ_BOOTROM_H */ diff --git a/PDP11/pdp11_xu.c b/PDP11/pdp11_xu.c new file mode 100644 index 0000000..502a676 --- /dev/null +++ b/PDP11/pdp11_xu.c @@ -0,0 +1,1560 @@ +/* pdp11_xu.c: DEUNA/DELUA ethernet controller simulator + ------------------------------------------------------------------------------ + + Copyright (c) 2003-2007, David T. Hittner + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + This DEUNA/DELUA simulation is based on: + Digital DELUA Users Guide, Part# EK-DELUA-UG-002 + Digital DEUNA Users Guide, Part# EK-DEUNA-UG-001 + These manuals can be found online at: + http://www.spies.com/~aek/pdf/dec/unibus + + Testing performed: + 1) Receives/Transmits single packet under custom RSX driver + 2) Passes RSTS 10.1 controller probe diagnostics during boot + 3) VMS 7.2 on VAX780 summary: + (May/2007: WinXP x64 host; MS VC++ 2005; SIMH v3.7-0 base; WinPcap 4.0) + LAT - SET HOST/LAT in/out + DECNET - SET HOST in/out, COPY in/out + TCP/IP - PING in/out; SET HOST/TELNET in/out, COPY/FTP in/out + Clustering - Successfully clustered with AlphaVMS 8.2 + 4) Runs VAX EVDWA diagnostic tests 1-10; tests 11-19 (M68000/ROM/RAM) fail + + Known issues: + 1) Most auxiliary commands are not implemented yet. + 2) System_ID broadcast is not implemented. + 3) There are residual Map_ReadB and Map_WriteB from the FvK version that + probably need to be converted to Map_ReadW and Map_WriteW calls. + 4) Some jerkiness seen during interactive I/O with remote systems; + this is probably attributable to changed polling times from when + the poll duration was standardized for idling support. + + ------------------------------------------------------------------------------ + + Modification history: + + 18-Jun-07 RMS Added UNIT_IDLE flag + 03-May-07 DTH Added missing FC_RMAL command; cleared multicast on write + 29-Oct-06 RMS Synced poll and clock + 08-Dec-05 DTH Implemented ancilliary functions 022/023/024/025 + 18-Nov-05 DTH Corrected time between system ID packets + 07-Sep-05 DTH Corrected runt packet processing (found by Tim Chapman), + Removed unused variable + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 10-Mar-05 RMS Fixed equality test in RCSTAT (from Mark Hittinger) + 16-Jan-04 DTH Added more info to SHOW MOD commands + 09-Jan-04 DTH Made XU floating address so that XUB will float correctly + 08-Jan-04 DTH Added system_id message + 06-Jan-04 DTH Added protection against changing mac and type if attached + 05-Jan-04 DTH Moved most of xu_setmac to sim_ether + Implemented auxiliary function 12/13 + Added SET/SHOW XU STATS + 31-Dec-03 DTH RSTS 10.1 accepts controller during boot tests + Implemented chained buffers in transmit/receive processing + 29-Dec-03 DTH Primitive RSX packet sending succeeds + 23-Dec-03 DTH Implemented write function + 17-Dec-03 DTH Implemented read function + 05-May-03 DTH Started XU simulation - + core logic pirated from unreleased FvK PDP10 variant + + ------------------------------------------------------------------------------ +*/ + +#include "pdp11_xu.h" + +extern int32 tmxr_poll, tmr_poll, clk_tps, cpu_astop; +extern FILE *sim_log; + +t_stat xu_rd(int32* data, int32 PA, int32 access); +t_stat xu_wr(int32 data, int32 PA, int32 access); +t_stat xu_svc(UNIT * uptr); +t_stat xu_reset (DEVICE * dptr); +t_stat xu_attach (UNIT * uptr, char * cptr); +t_stat xu_detach (UNIT * uptr); +t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc); +t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc); +int32 xu_int (void); +t_stat xu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat xu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +void xua_read_callback(int status); +void xub_read_callback(int status); +void xua_write_callback(int status); +void xub_write_callback(int status); +void xu_setint (CTLR* xu); +void xu_clrint (CTLR* xu); +void xu_process_receive(CTLR* xu); +void xu_dump_rxring(CTLR* xu); +void xu_dump_txring(CTLR* xu); + +DIB xua_dib = { IOBA_XU, IOLN_XU, &xu_rd, &xu_wr, +1, IVCL (XU), VEC_XU, {&xu_int} }; + +UNIT xua_unit[] = { + { UDATA (&xu_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) } /* receive timer */ +}; + +struct xu_device xua = { + xua_read_callback, /* read callback routine */ + xua_write_callback, /* write callback routine */ + {0x08, 0x00, 0x2B, 0xCC, 0xDD, 0xEE}, /* mac */ + XU_T_DELUA /* type */ + }; + +MTAB xu_mod[] = { +#if defined (VM_PDP11) + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL }, +#else + { MTAB_XTD|MTAB_VDV, 004, "ADDRESS", NULL, + NULL, &show_addr, NULL }, +#endif + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "MAC", "MAC=xx:xx:xx:xx:xx:xx", + &xu_setmac, &xu_showmac, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "ETH", "ETH", + NULL, ð_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS", + &xu_set_stats, &xu_show_stats, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE={DEUNA|DELUA}", + &xu_set_type, &xu_show_type, NULL }, + { 0 }, +}; + +REG xua_reg[] = { + { NULL } }; + +DEBTAB xu_debug[] = { + {"TRACE", DBG_TRC}, + {"WARN", DBG_WRN}, + {"REG", DBG_REG}, + {"PACKET", DBG_PCK}, + {"ETH", DBG_ETH}, + {0} +}; + + +DEVICE xu_dev = { + "XU", xua_unit, xua_reg, xu_mod, + 1, XU_RDX, 8, 1, XU_RDX, 8, + &xu_ex, &xu_dep, &xu_reset, + NULL, &xu_attach, &xu_detach, + &xua_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, + 0, xu_debug + }; + + +/* XUB does not exist in the PDP10 simulation */ +#if defined(IOBA_XUB) + +DIB xub_dib = { IOBA_XUB, IOLN_XUB, &xu_rd, &xu_wr, + 1, IVCL (XU), 0, { &xu_int } }; + +UNIT xub_unit[] = { + { UDATA (&xu_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) } /* receive timer */ +}; + +struct xu_device xub = { + xub_read_callback, /* read callback routine */ + xub_write_callback, /* write callback routine */ + {0x08, 0x00, 0x2B, 0xDD, 0xEE, 0xFF}, /* mac */ + XU_T_DELUA /* type */ + }; + +REG xub_reg[] = { + { NULL } }; + +DEVICE xub_dev = { + "XUB", xub_unit, xub_reg, xu_mod, + 1, XU_RDX, 8, 1, XU_RDX, 8, + &xu_ex, &xu_dep, &xu_reset, + NULL, &xu_attach, &xu_detach, + &xub_dib, DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, + 0, xu_debug +}; + +#define XU_MAX_CONTROLLERS 2 +CTLR xu_ctrl[] = { + {&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */ + ,{&xub_dev, xub_unit, &xub_dib, &xub} /* XUB controller */ +}; +#else /* IOBA_XUB */ +#define XU_MAX_CONTROLLERS 1 +CTLR xu_ctrl[] = { + {&xu_dev, xua_unit, &xua_dib, &xua} /* XUA controller */ +}; +#endif /* IOBA_XUB */ + +/*============================================================================*/ + +/* Multicontroller support */ + +CTLR* xu_unit2ctlr(UNIT* uptr) +{ + int i; + unsigned int j; + for (i=0; inumunits; j++) + if (&xu_ctrl[i].unit[j] == uptr) + return &xu_ctrl[i]; + /* not found */ + return 0; +} + +CTLR* xu_dev2ctlr(DEVICE* dptr) +{ + int i; + for (i=0; i= xu_ctrl[i].dib->ba) && (PA < (xu_ctrl[i].dib->ba + xu_ctrl[i].dib->lnt))) + return &xu_ctrl[i]; + /* not found */ + return 0; +} + +/*============================================================================*/ + +/* stop simh from reading non-existant unit data stream */ +t_stat xu_ex (t_value* vptr, t_addr addr, UNIT* uptr, int32 sw) +{ + return SCPE_NOFNC; +} + +/* stop simh from writing non-existant unit data stream */ +t_stat xu_dep (t_value val, t_addr addr, UNIT* uptr, int32 sw) +{ + return SCPE_NOFNC; +} + +t_stat xu_showmac (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xu = xu_unit2ctlr(uptr); + char buffer[20]; + + eth_mac_fmt((ETH_MAC*)xu->var->mac, buffer); + fprintf(st, "MAC=%s", buffer); + return SCPE_OK; +} + +t_stat xu_setmac (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + t_stat status; + CTLR* xu = xu_unit2ctlr(uptr); + + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + status = eth_mac_scan(&xu->var->mac, cptr); + return status; +} + +t_stat xu_set_stats (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xu = xu_unit2ctlr(uptr); + + /* set stats to zero, regardless of passed parameter */ + memset(&xu->var->stats, 0, sizeof(struct xu_stats)); + return SCPE_OK; +} + +t_stat xu_show_stats (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + char* fmt = " %-24s%d\n"; + CTLR* xu = xu_unit2ctlr(uptr); + struct xu_stats* stats = &xu->var->stats; + + fprintf(st, "Ethernet statistics:\n"); + fprintf(st, fmt, "Seconds since cleared:", stats->secs); + fprintf(st, fmt, "Recv frames:", stats->frecv); + fprintf(st, fmt, "Recv dbytes:", stats->rbytes); + fprintf(st, fmt, "Xmit frames:", stats->ftrans); + fprintf(st, fmt, "Xmit dbytes:", stats->tbytes); + fprintf(st, fmt, "Recv frames(multicast):", stats->mfrecv); + fprintf(st, fmt, "Recv dbytes(multicast):", stats->mrbytes); + fprintf(st, fmt, "Xmit frames(multicast):", stats->mftrans); + fprintf(st, fmt, "Xmit dbytes(multicast):", stats->mtbytes); + return SCPE_OK; +} + +t_stat xu_show_type (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + CTLR* xu = xu_unit2ctlr(uptr); + fprintf(st, "type="); + switch (xu->var->type) { + case XU_T_DEUNA: fprintf(st, "DEUNA"); break; + case XU_T_DELUA: fprintf(st, "DELUA"); break; + } + return SCPE_OK; +} + +t_stat xu_set_type (UNIT* uptr, int32 val, char* cptr, void* desc) +{ + CTLR* xu = xu_unit2ctlr(uptr); + if (!cptr) return SCPE_IERR; + if (uptr->flags & UNIT_ATT) return SCPE_ALATT; + + /* this assumes that the parameter has already been upcased */ + if (!strcmp(cptr, "DEUNA")) xu->var->type = XU_T_DEUNA; + else if (!strcmp(cptr, "DELUA")) xu->var->type = XU_T_DELUA; + else return SCPE_ARG; + + return SCPE_OK; +} + +/*============================================================================*/ + +void upd_stat16(uint16* stat, uint16 add) +{ + *stat += add; + /* did stat roll over? latches at maximum */ + if (*stat < add) + *stat = 0xFFFF; +} + +void upd_stat32(uint32* stat, uint32 add) +{ + *stat += add; + /* did stat roll over? latches at maximum */ + if (*stat < add) + *stat = 0xFFFFFFFF; +} + +void bit_stat16(uint16* stat, uint16 bits) +{ + *stat |= bits; +} + +t_stat xu_process_local (CTLR* xu, ETH_PACK* pack) +{ + return SCPE_NOFNC; /* not implemented yet */ +} + +void xu_read_callback(CTLR* xu, int status) +{ + /* process any packets locally that can be */ + status = xu_process_local (xu, &xu->var->read_buffer); + + /* add packet to read queue */ + if (status != SCPE_OK) + ethq_insert(&xu->var->ReadQ, 2, &xu->var->read_buffer, 0); +} + +void xua_read_callback(int status) +{ + xu_read_callback(&xu_ctrl[0], status); +} + +void xub_read_callback(int status) +{ + xu_read_callback(&xu_ctrl[1], status); +} + +t_stat xu_system_id (CTLR* xu, const ETH_MAC dest, uint16 receipt_id) +{ + static uint16 receipt = 0; + ETH_PACK system_id; + uint8* const msg = &system_id.msg[0]; + t_stat status; + + sim_debug(DBG_TRC, xu->dev, "xu_system_id()\n"); + memset (&system_id, 0, sizeof(system_id)); + memcpy (&msg[0], dest, sizeof(ETH_MAC)); + memcpy (&msg[6], xu->var->setup.macs[0], sizeof(ETH_MAC)); + msg[12] = 0x60; /* type */ + msg[13] = 0x02; /* type */ + msg[14] = 0x1C; /* character count */ + msg[15] = 0x00; /* character count */ + msg[16] = 0x07; /* code */ + msg[17] = 0x00; /* zero pad */ + if (receipt_id) { + msg[18] = receipt_id & 0xFF; /* receipt number */ + msg[19] = (receipt_id >> 8) & 0xFF; /* receipt number */ + } else { + msg[18] = receipt & 0xFF; /* receipt number */ + msg[19] = (receipt++ >> 8) & 0xFF; /* receipt number */ + } + + /* MOP VERSION */ + msg[20] = 0x01; /* type */ + msg[21] = 0x00; /* type */ + msg[22] = 0x03; /* length */ + msg[23] = 0x03; /* version */ + msg[24] = 0x00; /* eco */ + msg[25] = 0x00; /* user eco */ + + /* FUNCTION */ + msg[26] = 0x02; /* type */ + msg[27] = 0x00; /* type */ + msg[28] = 0x02; /* length */ + msg[29] = 0x05; /* value 1 */ + msg[30] = 0x00; /* value 2 */ + + /* HARDWARE ADDRESS */ + msg[31] = 0x07; /* type */ + msg[32] = 0x00; /* type */ + msg[33] = 0x06; /* length */ + memcpy (&msg[34], xu->var->mac, sizeof(ETH_MAC)); /* ROM address */ + + /* DEVICE TYPE */ + msg[40] = 0x64; /* type */ + msg[41] = 0x00; /* type */ + msg[42] = 0x01; /* length */ + if (xu->var->type == XU_T_DEUNA) + msg[43] = 1; /* value (1=DEUNA) */ + else + msg[43] = 11; /* value (11=DELUA) */ + + /* write system id */ + system_id.len = 60; + status = eth_write(xu->var->etherface, &system_id, NULL); + + return status; +} + +t_stat xu_svc(UNIT* uptr) +{ + int queue_size; + t_stat status; + CTLR* xu = xu_unit2ctlr(uptr); + const ETH_MAC mop_multicast = {0xAB, 0x00, 0x00, 0x02, 0x00, 0x00}; + const int one_second = clk_tps * tmr_poll; + + /* First pump any queued packets into the system */ + if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING)) + xu_process_receive(xu); + + /* Now read and queue packets that have arrived */ + /* This is repeated as long as they are available and we have room */ + do + { + queue_size = xu->var->ReadQ.count; + /* read a packet from the ethernet - processing is via the callback */ + status = eth_read (xu->var->etherface, &xu->var->read_buffer, xu->var->rcallback); + } while (queue_size != xu->var->ReadQ.count); + + /* Now pump any still queued packets into the system */ + if ((xu->var->ReadQ.count > 0) && ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING)) + xu_process_receive(xu); + + /* send identity packet when timer expires */ + if (--xu->var->idtmr <= 0) { + if ((xu->var->mode & MODE_DMNT) == 0) /* if maint msg is not disabled */ + status = xu_system_id(xu, mop_multicast, 0); /* then send ID packet */ + xu->var->idtmr = XU_ID_TIMER_VAL * one_second; /* reset timer */ + } + + /* has one second timer expired? if so, update stats and reset timer */ + if (++xu->var->sectmr >= XU_SERVICE_INTERVAL) { + upd_stat16 (&xu->var->stats.secs, 1); + xu->var->sectmr = 0; + } + + /* resubmit service timer if controller not halted */ + switch (xu->var->pcsr1 & PCSR1_STATE) { + case STATE_READY: + case STATE_RUNNING: + sim_activate(&xu->unit[0], tmxr_poll); + break; + }; + + return SCPE_OK; +} + +void xu_write_callback (CTLR* xu, int status) +{ + xu->var->write_buffer.status = status; +} + +void xua_write_callback (int status) +{ + xu_write_callback(&xu_ctrl[0], status); +} + +void xub_write_callback (int status) +{ + xu_write_callback(&xu_ctrl[1], status); +} + +void xu_setclrint(CTLR* xu, int32 bits) +{ + if (xu->var->pcsr0 & 0xFF00) { /* if any interrupt bits on, */ + xu->var->pcsr0 |= PCSR0_INTR; /* turn master bit on */ + xu_setint(xu); /* and trigger interrupt */ + } else { + xu->var->pcsr0 &= ~PCSR0_INTR; /* ... or off */ + xu_clrint(xu); /* and clear interrupt if needed*/ + } +} + +t_stat xu_sw_reset (CTLR* xu) +{ + t_stat status; + + sim_debug(DBG_TRC, xu->dev, "xu_sw_reset()\n"); + + /* Clear the registers. */ + xu->var->pcsr0 = PCSR0_DNI | PCSR0_INTR; + xu->var->pcsr1 = STATE_READY; + switch (xu->var->type) { + case XU_T_DELUA: + xu->var->pcsr1 |= TYPE_DELUA; + break; + case XU_T_DEUNA: + xu->var->pcsr1 |= TYPE_DEUNA; + if (!xu->var->etherface) /* if not attached, set transceiver powerfail */ + xu->var->pcsr1 |= PCSR1_XPWR; + break; + } + xu->var->pcsr2 = 0; + xu->var->pcsr3 = 0; + + /* Clear the parameters. */ + xu->var->mode = 0; + xu->var->pcbb = 0; + xu->var->stat = 0; + + /* clear read queue */ + ethq_clear(&xu->var->ReadQ); + + /* clear setup info */ + memset(&xu->var->setup, 0, sizeof(struct xu_setup)); + + /* clear network statistics */ + memset(&xu->var->stats, 0, sizeof(struct xu_stats)); + + /* reset ethernet interface */ + memcpy (xu->var->setup.macs[0], xu->var->mac, sizeof(ETH_MAC)); + xu->var->setup.mac_count = 1; + if (xu->var->etherface) + status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, + &xu->var->mac, xu->var->setup.multicast, + xu->var->setup.promiscuous); + + /* activate device if not disabled */ + if ((xu->dev->flags & DEV_DIS) == 0) { + sim_activate_abs(&xu->unit[0], clk_cosched (tmxr_poll)); + } + + /* clear load_server address */ + memset(xu->var->load_server, 0, sizeof(ETH_MAC)); + + return SCPE_OK; +} + +/* Reset device. */ +t_stat xu_reset(DEVICE* dptr) +{ + t_stat status; + CTLR* xu = xu_dev2ctlr(dptr); + + sim_debug(DBG_TRC, xu->dev, "xu_reset()\n"); + /* init read queue (first time only) */ + status = ethq_init (&xu->var->ReadQ, XU_QUE_MAX); + if (status != SCPE_OK) + return status; + + /* software reset controller */ + xu_sw_reset(xu); + + return SCPE_OK; +} + + +/* Perform one of the defined ancillary functions. */ +int32 xu_command(CTLR* xu) +{ + uint32 udbb; + int fnc, mtlen, i, j; + uint16 value, pltlen; + t_stat status, rstatus, wstatus, wstatus2, wstatus3; + struct xu_stats* stats = &xu->var->stats; + uint16* udb = xu->var->udb; + uint16* mac_w = (uint16*) xu->var->mac; + static const ETH_MAC zeros = {0,0,0,0,0,0}; + static const ETH_MAC mcast_load_server = {0xAB, 0x00, 0x00, 0x01, 0x00, 0x00}; + static char* command[] = { + "NO-OP", + "Start Microaddress", + "Read Default Physical Address", + "NO-OP", + "Read Physical Address", + "Write Physical Address", + "Read Multicast Address List", + "Write Multicast Address List", + "Read Descriptor Ring Format", + "Write Descriptor Ring Format", + "Read Counters", + "Read/Clear Counters", + "Read Mode Register", + "Write Mode Register", + "Read Status", + "Read/Clear Status", + "Dump Internal Memory", + "Load Internal Memory", + "Read System ID", + "Write System ID", + "Read Load Server Address", + "Write Load Server Address" + }; + + /* Grab the PCB from the host. */ + rstatus = Map_ReadW(xu->var->pcbb, 8, xu->var->pcb); + if (rstatus != 0) + return PCSR0_PCEI + 1; + + /* High 8 bits are defined as MBZ. */ + if (xu->var->pcb[0] & 0177400) + return PCSR0_PCEI; + + /* Decode the function to be performed. */ + fnc = xu->var->pcb[0] & 0377; + sim_debug(DBG_TRC, xu->dev, "xu_command(), Command: %s [0%o]\n", command[fnc], fnc); + + switch (fnc) { + case FC_NOOP: + break; + + case FC_RDPA: /* read default physical address */ + wstatus = Map_WriteB(xu->var->pcbb + 2, 6, xu->var->mac); + if (wstatus) + return PCSR0_PCEI + 1; + break; + + case FC_RPA: /* read current physical address */ + wstatus = Map_WriteB(xu->var->pcbb + 2, 6, (uint8*)&xu->var->setup.macs[0]); + if (wstatus) + return PCSR0_PCEI + 1; + break; + + case FC_WPA: /* write current physical address */ + rstatus = Map_ReadB(xu->var->pcbb + 2, 6, (uint8*)&xu->var->setup.macs[0]); + if (xu->var->pcb[1] & 1) + return PCSR0_PCEI; + break; + + case FC_RMAL: /* read multicast address list */ + mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; + udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); + wstatus = Map_WriteB(udbb, mtlen * 3, (uint8*) &xu->var->setup.macs[1]); + break; + + case FC_WMAL: /* write multicast address list */ + mtlen = (xu->var->pcb[2] & 0xFF00) >> 8; +sim_debug(DBG_TRC, xu->dev, "FC_WAL: mtlen=%d\n", mtlen); + if (mtlen > 10) + return PCSR0_PCEI; + udbb = xu->var->pcb[1] | ((xu->var->pcb[2] & 03) << 16); + /* clear existing multicast list */ + for (i=1; ivar->setup.macs[i][j] = 0; + } + /* get multicast list from host */ + rstatus = Map_ReadB(udbb, mtlen * 6, (uint8*) &xu->var->setup.macs[1]); + if (rstatus == 0) { + xu->var->setup.mac_count = mtlen + 1; + status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, + xu->var->setup.macs, xu->var->setup.multicast, + xu->var->setup.promiscuous); + } else { + xu->var->pcsr0 |= PCSR0_PCEI; + } + break; + + case FC_RRF: /* read ring format */ + if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374)) + return PCSR0_PCEI; + xu->var->udb[0] = xu->var->tdrb & 0177776; + xu->var->udb[1] = (xu->var->telen << 8) + ((xu->var->tdrb >> 16) & 3); + xu->var->udb[2] = xu->var->trlen; + xu->var->udb[3] = xu->var->rdrb & 0177776; + xu->var->udb[4] = (xu->var->relen << 8) + ((xu->var->rdrb >> 16) & 3); + xu->var->udb[5] = xu->var->rrlen; + + /* Write UDB to host memory. */ + udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); + wstatus = Map_WriteW(udbb, 12, xu->var->pcb); + if (wstatus != 0) + return PCSR0_PCEI+1; + break; + + case FC_WRF: /* write ring format */ + if ((xu->var->pcb[1] & 1) || (xu->var->pcb[2] & 0374)) + return PCSR0_PCEI; + if ((xu->var->pcsr1 & PCSR1_STATE) == STATE_RUNNING) + return PCSR0_PCEI; + + /* Read UDB into local memory. */ + udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); + rstatus = Map_ReadW(udbb, 12, xu->var->udb); + if (rstatus) + return PCSR0_PCEI+1; + + if ((xu->var->udb[0] & 1) || (xu->var->udb[1] & 0374) || + (xu->var->udb[3] & 1) || (xu->var->udb[4] & 0374) || + (xu->var->udb[5] < 2)) { + return PCSR0_PCEI; + } + + xu->var->tdrb = ((xu->var->udb[1] & 3) << 16) + (xu->var->udb[0] & 0177776); + xu->var->telen = (xu->var->udb[1] >> 8) & 0377; + xu->var->trlen = xu->var->udb[2]; + xu->var->rdrb = ((xu->var->udb[4] & 3) << 16) + (xu->var->udb[3] & 0177776); + xu->var->relen = (xu->var->udb[4] >> 8) & 0377; + xu->var->rrlen = xu->var->udb[5]; + xu->var->rxnext = 0; + xu->var->txnext = 0; +// xu_dump_rxring(xu); +// xu_dump_txring(xu); + + break; + + case FC_RDCTR: /* read counters */ + case FC_RDCLCTR: /* read and clear counters */ + /* prepare udb for stats transfer */ + memset(xu->var->udb, 0, sizeof(xu->var->udb)); + + /* place stats in udb */ + udb[0] = 68; /* udb length */ + udb[1] = stats->secs; /* seconds since zeroed */ + udb[2] = stats->frecv & 0xFFFF; /* frames received <15:00> */ + udb[3] = stats->frecv >> 16; /* frames received <31:16> */ + udb[4] = stats->mfrecv & 0xFFFF; /* multicast frames received <15:00> */ + udb[5] = stats->mfrecv >> 16; /* multicast frames received <31:16> */ + udb[6] = stats->rxerf; /* receive error status bits */ + udb[7] = stats->frecve; /* frames received with error */ + udb[8] = stats->rbytes & 0xFFFF; /* data bytes received <15:00> */ + udb[9] = stats->rbytes >> 16; /* data bytes received <31:16> */ + udb[10] = stats->mrbytes & 0xFFFF; /* multicast data bytes received <15:00> */ + udb[11] = stats->mrbytes >> 16; /* multicast data bytes received <31:16> */ + udb[12] = stats->rlossi; /* received frames lost - internal buffer */ + udb[13] = stats->rlossl; /* received frames lost - local buffer */ + udb[14] = stats->ftrans & 0xFFFF; /* frames transmitted <15:00> */ + udb[15] = stats->ftrans >> 16; /* frames transmitted <31:16> */ + udb[16] = stats->mftrans & 0xFFFF; /* multicast frames transmitted <15:00> */ + udb[17] = stats->mftrans >> 16; /* multicast frames transmitted <31:16> */ + udb[18] = stats->ftrans3 & 0xFFFF; /* frames transmitted 3+ tries <15:00> */ + udb[19] = stats->ftrans3 >> 16; /* frames transmitted 3+ tries <31:16> */ + udb[20] = stats->ftrans2 & 0xFFFF; /* frames transmitted 2 tries <15:00> */ + udb[21] = stats->ftrans2 >> 16; /* frames transmitted 2 tries <31:16> */ + udb[22] = stats->ftransd & 0xFFFF; /* frames transmitted deferred <15:00> */ + udb[23] = stats->ftransd >> 16; /* frames transmitted deferred <31:16> */ + udb[24] = stats->tbytes & 0xFFFF; /* data bytes transmitted <15:00> */ + udb[25] = stats->tbytes >> 16; /* data bytes transmitted <31:16> */ + udb[26] = stats->mtbytes & 0xFFFF; /* multicast data bytes transmitted <15:00> */ + udb[27] = stats->mtbytes >> 16; /* multicast data bytes transmitted <31:16> */ + udb[28] = stats->txerf; /* transmit frame error status bits */ + udb[29] = stats->ftransa; /* transmit frames aborted */ + udb[30] = stats->txccf; /* transmit collision check failure */ + udb[31] = 0; /* MBZ */ + udb[32] = stats->porterr; /* port driver error */ + udb[33] = stats->bablcnt; /* babble counter */ + + /* transfer udb to host */ + udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); + wstatus = Map_WriteW(udbb, 68, xu->var->udb); + if (wstatus) { + xu->var->pcsr0 |= PCSR0_PCEI; + } + + /* if clear function, clear network stats */ + if (fnc == FC_RDCLCTR) + memset(stats, 0, sizeof(struct xu_stats)); + break; + + case FC_RMODE: /* read mode register */ + value = xu->var->mode; + wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value); + if (wstatus) + return PCSR0_PCEI + 1; + break; + + case FC_WMODE: /* write mode register */ + value = xu->var->mode; + xu->var->mode = xu->var->pcb[1]; +sim_debug(DBG_TRC, xu->dev, "FC_WMODE: mode=%04x\n", xu->var->mode); + + /* set promiscuous and multicast flags */ + xu->var->setup.promiscuous = (xu->var->mode & MODE_PROM) ? 1 : 0; + xu->var->setup.multicast = (xu->var->mode & MODE_ENAL) ? 1 : 0; + + /* if promiscuous or multicast flags changed, change filter */ + if ((value ^ xu->var->mode) & (MODE_PROM | MODE_ENAL)) + status = eth_filter (xu->var->etherface, xu->var->setup.mac_count, + &xu->var->mac, xu->var->setup.multicast, + xu->var->setup.promiscuous); + break; + + case FC_RSTAT: /* read extended status */ + case FC_RCSTAT: /* read and clear extended status */ + value = xu->var->stat; + wstatus = Map_WriteW(xu->var->pcbb+2, 2, &value); + value = 10; + wstatus2 = Map_WriteW(xu->var->pcbb+4, 2, &value); + value = 32; + wstatus3 = Map_WriteW(xu->var->pcbb+6, 2, &value); + if (wstatus + wstatus2 + wstatus3) + return PCSR0_PCEI + 1; + + if (fnc == FC_RCSTAT) + xu->var->stat &= 0377; /* clear high byte */ + break; + + case FC_RSID: /* read system id parameters */ + /* prepare udb for transfer */ + memset(xu->var->udb, 0, sizeof(xu->var->udb)); + + udb[11] = 0x260; /* type */ + udb[12] = 28/* + parameter size */; /* ccount */ + udb[13] = 7; /* code */ + udb[14] = 0; /* recnum */ + /* mop information */ + udb[15] = 1; /* mvtype */ + udb[16] = 0x0303; /* mvver + mvlen */ + udb[17] = 0; /* mvueco + mveco */ + /* function information */ + udb[18] = 2; /* ftype */ + udb[19] = 0x0502; /* fval1 + flen */ + udb[20] = 0x0700; /* hatype<07:00> + fval2 */ + udb[21] = 0x0600; /* halen + hatype<15:08> */ + /* built-in MAC address */ + udb[21] = mac_w[0]; /* HA<15:00> */ + udb[22] = mac_w[1]; /* HA<31:16> */ + udb[23] = mac_w[2]; /* HA<47:32> */ + udb[24] = 0x64; /* dtype */ + udb[25] = (11 << 8) + 1; /* dvalue + dlen */ + + /* transfer udb to host */ + udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); + wstatus = Map_WriteW(udbb, 52, xu->var->udb); + if (wstatus) + xu->var->pcsr0 |= PCSR0_PCEI; + break; + + case FC_WSID: /* write system id parameters */ + /* get udb base */ + udbb = xu->var->pcb[1] + ((xu->var->pcb[2] & 3) << 16); + /* get udb length */ + pltlen = xu->var->pcb[3]; + + /* transfer udb from host */ + rstatus = Map_ReadW(udbb, pltlen * 2, xu->var->udb); + if (rstatus) + return PCSR0_PCEI + 1; + + /* decode and store system ID fields , if we ever need to. + for right now, just return "success" */ + + break; + + case FC_RLSA: /* read load server address */ + if (memcmp(xu->var->load_server, zeros, sizeof(ETH_MAC))) { + /* not set, use default multicast load address */ + wstatus = Map_WriteB(xu->var->pcbb + 2, 6, (uint8*) mcast_load_server); + } else { + /* is set, use load_server */ + wstatus = Map_WriteB(xu->var->pcbb + 2, 6, xu->var->load_server); + } + if (wstatus) + return PCSR0_PCEI + 1; + break; + + + case FC_WLSA: /* write load server address */ + rstatus = Map_ReadB(xu->var->pcbb + 2, 6, xu->var->load_server); + if (rstatus) + return PCSR0_PCEI + 1; + break; + + default: /* Unknown (unimplemented) command. */ + printf("%s: unknown ancilliary command 0%o requested !\n", xu->dev->name, fnc); + return PCSR0_PCEI; + break; + + } /* switch */ + + return PCSR0_DNI; +} + +/* Transfer received packets into receive ring. */ +void xu_process_receive(CTLR* xu) +{ + uint32 segb, ba; + int slen, wlen, off; + t_stat rstatus, wstatus; + ETH_ITEM* item = 0; + int state = xu->var->pcsr1 & PCSR1_STATE; + int no_buffers = xu->var->pcsr0 & PCSR0_RCBI; + + sim_debug(DBG_TRC, xu->dev, "xu_process_receive(), buffers: %d\n", xu->var->rrlen); + +/* xu_dump_rxring(xu); /* debug receive ring */ + + /* process only when in the running state, and host buffers are available */ + if ((state != STATE_RUNNING) || no_buffers) + return; + + /* check read queue for buffer loss */ + if (xu->var->ReadQ.loss) { + upd_stat16(&xu->var->stats.rlossl, (uint16) xu->var->ReadQ.loss); + xu->var->ReadQ.loss = 0; + } + + /* while there are still packets left to process in the queue */ + while (xu->var->ReadQ.count > 0) { + + /* get next receive buffer */ + ba = xu->var->rdrb + (xu->var->relen * 2) * xu->var->rxnext; + rstatus = Map_ReadW (ba, 8, xu->var->rxhdr); + if (rstatus) { + /* tell host bus read failed */ + xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG; + xu->var->pcsr0 |= PCSR0_SERI; + break; + } + + /* if buffer not owned by controller, exit [at end of ring] */ + if (!(xu->var->rxhdr[2] & RXR_OWN)) { + /* tell the host there are no more buffers */ + /* xu->var->pcsr0 |= PCSR0_RCBI; */ /* I don't think this is correct 08-dec-2005 dth */ + break; + } + + /* set buffer length and address */ + slen = xu->var->rxhdr[0]; + segb = xu->var->rxhdr[1] + ((xu->var->rxhdr[2] & 3) << 16); + + /* get first packet from receive queue */ + if (!item) { + item = &xu->var->ReadQ.item[xu->var->ReadQ.head]; + /* + * 2.11BSD does not seem to like small packets. + * For example.. an incoming ARP packet is: + * ETH dstaddr [6] + * ETH srcaddr [6] + * ETH type [2] + * ARP arphdr [8] + * ARP dstha [6] + * ARP dstpa [4] + * ARP srcha [6] + * ARP srcpa [4] + * + * for a total of 42 bytes. According to the 2.11BSD + * driver for DEUNA (if_de.c), this is not a legal size, + * and the packet is dropped. Therefore, we pad the + * thing to minimum size here. Stupid runts... + */ + if (item->packet.len < ETH_MIN_PACKET) { + int len = item->packet.len; + memset (&item->packet.msg[len], 0, ETH_MIN_PACKET - len); + item->packet.len = ETH_MIN_PACKET; + } + } + + /* is this the start of frame? */ + if (item->packet.used == 0) { + xu->var->rxhdr[2] |= RXR_STF; + off = 0; + } + + /* figure out chained packet size */ + wlen = item->packet.crc_len - item->packet.used; + if (wlen > slen) + wlen = slen; + + /* transfer chained packet to host buffer */ + wstatus = Map_WriteB (segb, wlen, &item->packet.msg[off]); + if (wstatus) { + /* error during write */ + xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG; + xu->var->pcsr0 |= PCSR0_SERI; + break; + } + + /* update chained counts */ + item->packet.used += wlen; + off += wlen; + + /* Is this the end-of-frame? */ + if (item->packet.used == item->packet.crc_len) { + /* mark end-of-frame */ + xu->var->rxhdr[2] |= RXR_ENF; + + /* + * Fill in the Received Message Length field. + * The documenation notes that the DEUNA actually performs + * a full CRC check on the data buffer, and adds this CRC + * value to the data, in the last 4 bytes. The question + * is: does MLEN include these 4 bytes, or not??? --FvK + * + * A quick look at the RSX Process Software driver shows + * that the CRC byte count(4) is added to MLEN, but does + * not show if the DEUNA/DELUA actually transfers the + * CRC bytes to the host buffers, since the driver never + * tries to use them. However, since the host max buffer + * size is only 1514, not 1518, I doubt the CRC is actually + * transferred in normal mode. Maybe CRC is transferred + * and used in Loopback mode.. -- DTH + * + * The VMS XEDRIVER indicates that CRC is transferred as + * part of the packet, and is included in the MLEN count. -- DTH + */ + xu->var->rxhdr[3] &= ~RXR_MLEN; + xu->var->rxhdr[3] |= (item->packet.crc_len); + if (xu->var->mode & MODE_DRDC) /* data chaining disabled */ + xu->var->rxhdr[3] |= RXR_NCHN; + + /* update stats */ + upd_stat32(&xu->var->stats.frecv, 1); + upd_stat32(&xu->var->stats.rbytes, item->packet.len - 14); + if (item->packet.msg[0] & 1) { /* multicast? */ + upd_stat32(&xu->var->stats.mfrecv, 1); + upd_stat32(&xu->var->stats.mrbytes, item->packet.len - 14); + } + + /* remove processed packet from the receive queue */ + ethq_remove (&xu->var->ReadQ); + item = 0; + + /* tell host we received a packet */ + xu->var->pcsr0 |= PCSR0_RXI; + } /* if end-of-frame */ + + /* give buffer back to host */ + xu->var->rxhdr[2] &= ~RXR_OWN; /* clear ownership flag */ + + /* update the ring entry in host memory. */ + wstatus = Map_WriteW (ba, 8, xu->var->rxhdr); + if (wstatus) { + /* tell host bus write failed */ + xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_RRNG; + xu->var->pcsr0 |= PCSR0_SERI; + /* if this was end-of-frame, log frame loss */ + if (xu->var->rxhdr[2] & RXR_ENF) + upd_stat16(&xu->var->stats.rlossi, 1); + } + + /* set to next receive ring buffer */ + xu->var->rxnext += 1; + if (xu->var->rxnext == xu->var->rrlen) + xu->var->rxnext = 0; + + } /* while */ + + /* if we failed to finish receiving the frame, flush the packet */ + if (item) { + ethq_remove(&xu->var->ReadQ); + upd_stat16(&xu->var->stats.rlossl, 1); + } + + /* set or clear interrupt, depending on what happened */ + xu_setclrint(xu, 0); +// xu_dump_rxring(xu); /* debug receive ring */ + +} + +void xu_process_transmit(CTLR* xu) +{ + uint32 segb, ba; + int slen, wlen, i, off, giant, runt; + t_stat rstatus, wstatus; + + sim_debug(DBG_TRC, xu->dev, "xu_process_transmit()\n"); +/* xu_dump_txring(xu); /* debug receive ring */ + + for (;;) { + + /* get next transmit buffer */ + ba = xu->var->tdrb + (xu->var->telen * 2) * xu->var->txnext; + rstatus = Map_ReadW (ba, 8, xu->var->txhdr); + if (rstatus) { + /* tell host bus read failed */ + xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG; + xu->var->pcsr0 |= PCSR0_SERI; + break; + } + + /* if buffer not owned by controller, exit [at end of ring] */ + if (!(xu->var->txhdr[2] & TXR_OWN)) + break; + + /* set buffer length and address */ + slen = xu->var->txhdr[0]; + segb = xu->var->txhdr[1] + ((xu->var->txhdr[2] & 3) << 16); + wlen = slen; + + /* prepare to accumulate transmit information if start of frame */ + if (xu->var->txhdr[2] & TXR_STF) { + memset(&xu->var->write_buffer, 0, sizeof(ETH_PACK)); + off = giant = runt = 0; + } + + /* get packet data from host */ + if (xu->var->write_buffer.len + slen > ETH_MAX_PACKET) { + wlen = ETH_MAX_PACKET - xu->var->write_buffer.len; + giant = 1; + } + if (wlen > 0) { + rstatus = Map_ReadB(segb, wlen, &xu->var->write_buffer.msg[off]); + if (rstatus) { + /* tell host bus read failed */ + xu->var->stat |= STAT_ERRS | STAT_MERR | STAT_TMOT | STAT_TRNG; + xu->var->pcsr0 |= PCSR0_SERI; + break; + } + } + off += wlen; + xu->var->write_buffer.len += wlen; + + /* transmit packet when end-of-frame is reached */ + if (xu->var->txhdr[2] & TXR_ENF) { + + /* make sure packet is minimum length */ + if (xu->var->write_buffer.len < ETH_MIN_PACKET) { + xu->var->write_buffer.len = ETH_MIN_PACKET; /* pad packet to minimum length */ + if ((xu->var->mode & MODE_TPAD) == 0) /* if pad mode is NOT on, set runt error flag */ + runt = 1; + } + + /* are we in internal loopback mode ? */ + if ((xu->var->mode & MODE_LOOP) && (xu->var->mode & MODE_INTL)) { + /* just put packet in receive buffer */ + ethq_insert (&xu->var->ReadQ, 1, &xu->var->write_buffer, 0); + } else { + /* transmit packet synchronously - write callback sets status */ + wstatus = eth_write(xu->var->etherface, &xu->var->write_buffer, xu->var->wcallback); + if (wstatus) + xu->var->pcsr0 |= PCSR0_PCEI; + } + + /* update transmit status in transmit buffer */ + if (xu->var->write_buffer.status != 0) { + /* failure */ + const uint16 tdr = 100 + wlen * 8; /* arbitrary value */ + xu->var->txhdr[3] |= TXR_RTRY; + xu->var->txhdr[3] |= tdr & TXR_TDR; + xu->var->txhdr[2] |= TXR_ERRS; + } + + /* was packet too big or too small? */ + if (giant || runt) { + xu->var->txhdr[3] |= TXR_BUFL; + xu->var->txhdr[2] |= TXR_ERRS; + } + + /* was packet self-addressed? */ + for (i=0; ivar->write_buffer.msg, xu->var->setup.macs[i], sizeof(ETH_MAC)) == 0) + xu->var->txhdr[2] |= TXR_MTCH; + + /* tell host we transmitted a packet */ + xu->var->pcsr0 |= PCSR0_TXI; + + /* update stats */ + upd_stat32(&xu->var->stats.ftrans, 1); + upd_stat32(&xu->var->stats.tbytes, xu->var->write_buffer.len - 14); + if (xu->var->write_buffer.msg[0] & 1) { /* multicast? */ + upd_stat32(&xu->var->stats.mftrans, 1); + upd_stat32(&xu->var->stats.mtbytes, xu->var->write_buffer.len - 14); + } + if (giant) + bit_stat16(&xu->var->stats.txerf, 0x10); + + } /* if end-of-frame */ + + + /* give buffer ownership back to host */ + xu->var->txhdr[2] &= ~TXR_OWN; + + /* update transmit buffer */ + wstatus = Map_WriteW (ba, 8, xu->var->txhdr); + if (wstatus) { + /* tell host bus write failed */ + xu->var->pcsr0 |= PCSR0_PCEI; + /* update stats */ + upd_stat16(&xu->var->stats.ftransa, 1); + break; + } + + /* set to next transmit ring buffer */ + xu->var->txnext += 1; + if (xu->var->txnext == xu->var->trlen) + xu->var->txnext = 0; + + } /* while */ +} + +void xu_port_command (CTLR* xu) +{ + char* msg; + int command = xu->var->pcsr0 & PCSR0_PCMD; + int state = xu->var->pcsr1 & PCSR1_STATE; + int bits; + static char* commands[] = { + "NO-OP", + "GET PCBB", + "GET CMD", + "SELFTEST", + "START", + "BOOT", + "Reserved NO-OP", + "Reserved NO-OP", + "PDMD", + "Reserved NO-OP", + "Reserved NO-OP", + "Reserved NO-OP", + "Reserved NO-OP", + "Reserved NO-OP", + "HALT", + "STOP" + }; + + sim_debug(DBG_TRC, xu->dev, "xu_port_command(), Command = %s [0%o]\n", commands[command], command); + switch (command) { /* cases in order of most used to least used */ + case CMD_PDMD: /* POLLING DEMAND */ + /* process transmit buffers, receive buffers are done in the service timer */ + xu_process_transmit(xu); + xu->var->pcsr0 |= PCSR0_DNI; + break; + + case CMD_GETCMD: /* GET COMMAND */ + bits = xu_command(xu); + xu->var->pcsr0 |= PCSR0_DNI; + break; + + case CMD_GETPCBB: /* GET PCB-BASE */ + xu->var->pcbb = (xu->var->pcsr3 << 16) | xu->var->pcsr2; + xu->var->pcsr0 |= PCSR0_DNI; + break; + + case CMD_SELFTEST: /* SELFTEST */ + xu_sw_reset(xu); + xu->var->pcsr0 |= PCSR0_DNI; + break; + + case CMD_START: /* START */ + if (state == STATE_READY) { + xu->var->pcsr1 &= ~PCSR1_STATE; + xu->var->pcsr1 |= STATE_RUNNING; + xu->var->pcsr0 |= PCSR0_DNI; + + /* reset ring pointers */ + xu->var->rxnext = 0; + xu->var->txnext = 0; + + } else + xu->var->pcsr0 |= PCSR0_PCEI; + break; + + case CMD_HALT: /* HALT */ + if ((state == STATE_READY) || (state == STATE_RUNNING)) { + sim_cancel (&xu->unit[0]); /* cancel service timer */ + xu->var->pcsr1 &= ~PCSR1_STATE; + xu->var->pcsr1 |= STATE_HALT; + xu->var->pcsr0 |= PCSR0_DNI; + } else + xu->var->pcsr0 |= PCSR0_PCEI; + break; + + case CMD_STOP: /* STOP */ + if (state == STATE_RUNNING) { + xu->var->pcsr1 &= ~PCSR1_STATE; + xu->var->pcsr1 |= STATE_READY; + xu->var->pcsr0 |= PCSR0_DNI; + } else + xu->var->pcsr0 |= PCSR0_PCEI; + break; + + case CMD_BOOT: /* BOOT */ + /* not implemented */ + msg = "%s: BOOT command not implemented!\n"; + printf (msg, xu->dev->name); + if (sim_log) fprintf(sim_log, msg, xu->dev->name); + + xu->var->pcsr0 |= PCSR0_PCEI; + break; + + case CMD_NOOP: /* NO-OP */ + /* NOOP does NOT set DNI */ + break; + + case CMD_RSV06: /* RESERVED */ + case CMD_RSV07: /* RESERVED */ + case CMD_RSV11: /* RESERVED */ + case CMD_RSV12: /* RESERVED */ + case CMD_RSV13: /* RESERVED */ + case CMD_RSV14: /* RESERVED */ + case CMD_RSV15: /* RESERVED */ + /* all reserved commands act as a no-op but set DNI */ + xu->var->pcsr0 |= PCSR0_DNI; + break; + } /* switch */ + + /* set interrupt if needed */ + xu_setclrint(xu, 0); +} + +t_stat xu_rd(int32 *data, int32 PA, int32 access) +{ + CTLR* xu = xu_pa2ctlr(PA); + int reg = (PA >> 1) & 03; + + switch (reg) { + case 00: + *data = xu->var->pcsr0; + break; + case 01: + *data = xu->var->pcsr1; + break; + case 02: + *data = xu->var->pcsr2; + break; + case 03: + *data = xu->var->pcsr3; + break; + } + sim_debug(DBG_TRC, xu->dev, "xu_rd(), PCSR%d, data=%04x\n", reg, *data); + if (PA & 1) + sim_debug(DBG_WRN, xu->dev, "xu_rd(), Unexpected Odd address access of PCSR%d\n", reg); + return SCPE_OK; +} + +t_stat xu_wr(int32 data, int32 PA, int32 access) +{ + CTLR* xu = xu_pa2ctlr(PA); + int reg = (PA >> 1) & 03; + char desc[10]; + + switch (access) { + case WRITE : + strcpy(desc, "Word"); + break; + case WRITEB: + if (PA & 1) { + strcpy(desc, "ByteHi"); + } else { + strcpy(desc, "ByteLo"); + } + break; + default : + strcpy(desc, "Unknown"); + break; + } + sim_debug(DBG_TRC, xu->dev, "xu_wr(), PCSR%d, data=%08x, PA=%08x, access=%d[%s]\n", reg, data, PA, access, desc); + switch (reg) { + case 00: + /* Clear write-one-to-clear interrupt bits */ + if (access == WRITEB) { + data &= 0377; + if (PA & 1) { + /* Handle WriteOneToClear trick. */ + xu->var->pcsr0 &= ~((data << 8) & 0177400); + + /* set/reset interrupt */ + xu_setclrint(xu, 0); + + /* Bail out early to avoid PCMD crap. */ + return SCPE_OK; + } + } else { /* access == WRITE [Word] */ + uint16 mask = data & 0xFF00; /* only interested in high byte */ + xu->var->pcsr0 &= ~mask; /* clear write-one-to-clear bits */ + } + /* RESET function requested? */ + if (data & PCSR0_RSET) { + xu_sw_reset(xu); + xu_setclrint(xu, 0); + return SCPE_OK; /* nothing else to do on reset */ + } + /* Handle the INTE interlock; if INTE changes state, no commands can occur */ + if ((xu->var->pcsr0 ^ data) & PCSR0_INTE) { + xu->var->pcsr0 ^= PCSR0_INTE; + xu->var->pcsr0 |= PCSR0_DNI; + if (xu->var->pcsr0 & PCSR0_INTE) { + sim_debug(DBG_TRC, xu->dev, "xu_wr(), Interrupts Enabled\n"); + } else { + sim_debug(DBG_TRC, xu->dev, "xu_wr(), Interrupts Disabled\n"); + } + } else { + /* Normal write, no interlock. */ + xu->var->pcsr0 &= ~PCSR0_PCMD; + xu->var->pcsr0 |= (data & PCSR0_PCMD); + xu_port_command(xu); + } + /* We might have changed the interrupt sys. */ + xu_setclrint(xu, 0); + break; + + case 01: + sim_debug(DBG_WRN, xu->dev, "xu_wr(), invalid write access on PCSR1!\n"); + break; + + case 02: + xu->var->pcsr2 = data & 0177776; /* store word, but not MBZ LSB */ + break; + + case 03: + xu->var->pcsr3 = data & 0000003; /* store significant bits */ + break; + } + return SCPE_OK; +} + + +/* attach device: */ +t_stat xu_attach(UNIT* uptr, char* cptr) +{ + t_stat status; + char* tptr; + CTLR* xu = xu_unit2ctlr(uptr); + + sim_debug(DBG_TRC, xu->dev, "xu_attach(cptr=%s)\n", cptr); + tptr = (char *) malloc(strlen(cptr) + 1); + if (tptr == NULL) return SCPE_MEM; + strcpy(tptr, cptr); + + xu->var->etherface = (ETH_DEV *) malloc(sizeof(ETH_DEV)); + if (!xu->var->etherface) return SCPE_MEM; + + status = eth_open(xu->var->etherface, cptr, xu->dev, DBG_ETH); + if (status != SCPE_OK) { + free(tptr); + free(xu->var->etherface); + xu->var->etherface = 0; + return status; + } + uptr->filename = tptr; + uptr->flags |= UNIT_ATT; + eth_setcrc(xu->var->etherface, 1); /* enable CRC */ + + /* reset the device with the new attach info */ + xu_reset(xu->dev); + + return SCPE_OK; +} + +/* detach device: */ + +t_stat xu_detach(UNIT* uptr) +{ + t_stat status; + CTLR* xu = xu_unit2ctlr(uptr); + sim_debug(DBG_TRC, xu->dev, "xu_detach()\n"); + + if (uptr->flags & UNIT_ATT) { + status = eth_close (xu->var->etherface); + free(xu->var->etherface); + xu->var->etherface = 0; + free(uptr->filename); + uptr->filename = NULL; + uptr->flags &= ~UNIT_ATT; + } + return SCPE_OK; +} + +void xu_setint(CTLR* xu) +{ + if (xu->var->pcsr0 & PCSR0_INTE) { + xu->var->irq = 1; + SET_INT(XU); + } + return; +} + +void xu_clrint(CTLR* xu) +{ + int i; + xu->var->irq = 0; /* set controller irq off */ + /* clear master interrupt? */ + for (i=0; iirq) { /* if any irqs enabled */ + SET_INT(XU); /* set master interrupt on */ + return; + } + CLR_INT(XU); /* clear master interrupt */ + return; +} + +int32 xu_int (void) +{ + int i; + for (i=0; ivar->irq) { /* if interrupt pending */ + xu_clrint(xu); /* clear interrupt */ + return xu->dib->vec; /* return vector */ + } + } + return 0; /* no interrupt request active */ +} + +/*============================================================================== +/ debugging routines +/=============================================================================*/ + +void xu_dump_rxring (CTLR* xu) +{ + int i; + int rrlen = xu->var->rrlen; + printf ("receive ring[%s]: base address: %08x headers: %d, header size: %d, current: %d\n", xu->dev->name, xu->var->rdrb, xu->var->rrlen, xu->var->relen, xu->var->rxnext); + for (i=0; ivar->rdrb + (xu->var->relen * 2) * i; + t_stat rstatus = Map_ReadW (ba, 8, rxhdr); /* get rxring entry[i] */ + int own = (rxhdr[2] & RXR_OWN) >> 15; + int len = rxhdr[0]; + uint32 addr = rxhdr[1] + ((rxhdr[2] & 3) << 16); + printf (" header[%d]: own:%d, len:%d, address:%08x data:{%04x,%04x,%04x,%04x}\n", i, own, len, addr, rxhdr[0], rxhdr[1], rxhdr[2], rxhdr[3]); + } +} + +void xu_dump_txring (CTLR* xu) +{ + int i; + int trlen = xu->var->trlen; + printf ("transmit ring[%s]: base address: %08x headers: %d, header size: %d, current: %d\n", xu->dev->name, xu->var->tdrb, xu->var->trlen, xu->var->telen, xu->var->txnext); + for (i=0; ivar->tdrb + (xu->var->telen * 2) * i; + t_stat tstatus = Map_ReadW (ba, 8, txhdr); /* get rxring entry[i] */ + int own = (txhdr[2] & RXR_OWN) >> 15; + int len = txhdr[0]; + uint32 addr = txhdr[1] + ((txhdr[2] & 3) << 16); + printf (" header[%d]: own:%d, len:%d, address:%08x data:{%04x,%04x,%04x,%04x}\n", i, own, len, addr, txhdr[0], txhdr[1], txhdr[2], txhdr[3]); + } +} diff --git a/PDP11/pdp11_xu.h b/PDP11/pdp11_xu.h new file mode 100644 index 0000000..43f1c99 --- /dev/null +++ b/PDP11/pdp11_xu.h @@ -0,0 +1,306 @@ +/* pdp11_xu.h: DEUNA/DELUA ethernet controller information + ------------------------------------------------------------------------------ + + Copyright (c) 2003-2005, David T. Hittner + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + Modification history: + + 08-Dec-05 DTH Added load_server, increased UDBSIZE for system ID parameters + 07-Jul-05 RMS Removed extraneous externs + 05-Jan-04 DTH Added network statistics + 31-Dec-03 DTH Added reserved states + 28-Dec-03 DTH Corrected MODE bitmasks + 23-Dec-03 DTH Corrected TXR and RXR bitmasks + 03-Dec-03 DTH Refitted to SIMH v3.0 platform + 05-May-03 DTH Started XU simulation + + ------------------------------------------------------------------------------ +*/ + +#ifndef _PDP11_XU_H +#define _PDP11_XU_H + + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" +#define XU_RDX 8 +#define XU_WID 16 +extern int32 int_req; + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" +#define XU_RDX 8 +#define XU_WID 16 +extern int32 int_req[IPL_HLVL]; + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#define XU_RDX 8 +#define XU_WID 16 +extern int32 int_req[IPL_HLVL]; +#endif /* VM_PDP10 */ + +#include "sim_ether.h" + +#define XU_QUE_MAX 500 /* message queue array */ +#define XU_FILTER_MAX 11 /* mac + 10 multicast addrs */ +#define XU_SERVICE_INTERVAL 100 /* times per second */ +#define XU_ID_TIMER_VAL 540 /* 9 min * 60 sec */ +#define UDBSIZE 200 /* max size of UDB (in words) */ + +enum xu_type {XU_T_DEUNA, XU_T_DELUA}; + +struct xu_setup { + int promiscuous; /* promiscuous mode enabled */ + int multicast; /* enable all multicast addresses */ + int mac_count; /* number of multicast mac addresses */ + ETH_MAC macs[XU_FILTER_MAX]; /* MAC addresses to respond to */ +}; + +/* Network Statistics - + some of these will always be zero in the simulated environment, + since there is no ability for the sim_ether network driver to see + things like incoming runts, collision tests, babbling, etc. + */ +struct xu_stats { + uint16 secs; /* seconds since last clear */ + uint32 frecv; /* frames received */ + uint32 mfrecv; /* multicast frames received */ + uint16 rxerf; /* receive error flags */ + uint32 frecve; /* frames received with errors */ + uint32 rbytes; /* data bytes received */ + uint32 mrbytes; /* multicast data bytes received */ + uint16 rlossi; /* received frames lost - internal err */ + uint16 rlossl; /* received frames lost - local buffers */ + uint32 ftrans; /* frames transmitted */ + uint32 mftrans; /* multicast frames transmitted */ + uint32 ftrans3; /* frames transmitted with 3+ tries */ + uint32 ftrans2; /* frames transmitted - two tries */ + uint32 ftransd; /* frames transmitted - deferred */ + uint32 tbytes; /* data bytes transmitted */ + uint32 mtbytes; /* multicast data bytes transmitted */ + uint16 txerf; /* transmit error flags summary */ + uint16 ftransa; /* transmit frames aborted */ + uint16 txccf; /* transmit collision test failure */ + uint16 porterr; /* port driver errors */ + uint16 bablcnt; /* babble counter */ +}; + +struct xu_device { + /*+ initialized values - DO NOT MOVE */ + ETH_PCALLBACK rcallback; /* read callback routine */ + ETH_PCALLBACK wcallback; /* write callback routine */ + ETH_MAC mac; /* MAC address */ + enum xu_type type; /* controller type */ + /*- initialized values - DO NOT MOVE */ + + /* I/O register storage */ + uint32 irq; /* interrupt request flag */ + + /* buffers, etc. */ + ETH_DEV* etherface; + ETH_PACK read_buffer; + ETH_PACK write_buffer; + ETH_QUE ReadQ; + ETH_MAC load_server; /* load server address */ + int idtmr; /* countdown for ID Timer */ + int sectmr; /* countup for one second timer */ + struct xu_setup setup; + struct xu_stats stats; /* reportable network statistics */ + + /* copied from dec_deuna.h */ + uint16 pcsr0; /* primary DEUNA registers */ + uint16 pcsr1; + uint16 pcsr2; + uint16 pcsr3; + uint32 mode; /* mode register */ + uint32 pcbb; /* port command block base */ + uint32 stat; /* extended port status */ + + uint32 tdrb; /* transmit desc ring base */ + uint32 telen; /* transmit desc ring entry len */ + uint32 trlen; /* transmit desc ring length */ + uint32 txnext; /* transmit buffer pointer */ + uint32 rdrb; /* receive desc ring base */ + uint32 relen; /* receive desc ring entry len */ + uint32 rrlen; /* receive desc ring length */ + uint32 rxnext; /* receive buffer pointer */ + + uint16 pcb[4]; /* copy of Port Command Block */ + uint16 udb[UDBSIZE]; /* copy of Unibus Data Block */ + uint16 rxhdr[4]; /* content of RX ring entry, during wait */ + uint16 txhdr[4]; /* content of TX ring entry, during xmit */ +}; + +struct xu_controller { + DEVICE* dev; /* device block */ + UNIT* unit; /* unit block */ + DIB* dib; /* device interface block */ + struct xu_device* var; /* controller-specific variables */ +}; + +typedef struct xu_controller CTLR; + +/* PCSR0 register definitions */ +#define PCSR0_SERI 0100000 /* <15> Status Error Intr */ +#define PCSR0_PCEI 0040000 /* <14> Port Command Error Intr */ +#define PCSR0_RXI 0020000 /* <13> Receive Interrupt */ +#define PCSR0_TXI 0010000 /* <12> Transmit Interrupt */ +#define PCSR0_DNI 0004000 /* <11> Done Interrupt */ +#define PCSR0_RCBI 0002000 /* <10> Recv Buffer Unavail Intr */ +#define PCSR0_USCI 0000400 /* <08> Unsolicited State Chg Inter */ +#define PCSR0_INTR 0000200 /* <07> Interrupt Summary */ +#define PCSR0_INTE 0000100 /* <06> Interrupt Enable */ +#define PCSR0_RSET 0000040 /* <05> Reset */ +#define PCSR0_PCMD 0000017 /* <03:00> Port Command field */ + +/* PCSR0 Port Commands */ +#define CMD_NOOP 000 /* No-op */ +#define CMD_GETPCBB 001 /* Get PCB base */ +#define CMD_GETCMD 002 /* Get Command */ +#define CMD_SELFTEST 003 /* Self-test init */ +#define CMD_START 004 /* Start xmit/recv */ +#define CMD_BOOT 005 /* Boot */ +#define CMD_RSV06 006 /* Reserved */ +#define CMD_RSV07 007 /* Reserved */ +#define CMD_PDMD 010 /* Polling Demand */ +#define CMD_RSV11 011 /* Reserved */ +#define CMD_RSV12 012 /* Reserved */ +#define CMD_RSV13 013 /* Reserved */ +#define CMD_RSV14 014 /* Reserved */ +#define CMD_RSV15 015 /* Reserved */ +#define CMD_HALT 016 /* Halt */ +#define CMD_STOP 017 /* Stop */ + +/* PCSR1 register definitions */ +#define PCSR1_XPWR 0100000 /* <15> Tranceiver power failure */ +#define PCSR1_ICAB 0040000 /* <14> Port/Link cable failure */ +#define PCSR1_ECOD 0037400 /* <13:08> Self-test error code */ +#define PCSR1_PCTO 0000200 /* <07> Port Command Timeout */ +#define PCSR1_TYPE 0000160 /* <06:04> Interface type */ +#define PCSR1_STATE 0000017 /* <03:00> State: */ + +/* PCSR1 Types */ +#define TYPE_DEUNA (0 << 4) /* Controller is a DEUNA */ +#define TYPE_DELUA (1 << 4) /* Controller is a DELUA */ + +/* PCSR1 States */ +#define STATE_RESET 000 /* Reset */ +#define STATE_PLOAD 001 /* Primary Load */ +#define STATE_READY 002 /* Ready */ +#define STATE_RUNNING 003 /* Running */ +#define STATE_UHALT 005 /* UNIBUS Halted */ +#define STATE_NHALT 006 /* NI Halted */ +#define STATE_NUHALT 007 /* NI and UNIBUS Halted */ +#define STATE_HALT 010 /* Halted */ +#define STATE_SLOAD 017 /* Secondary Load */ + +/* Status register definitions */ +#define STAT_ERRS 0100000 /* <15> error summary */ +#define STAT_MERR 0040000 /* <14> multiple errors */ +#define STAT_BABL 0020000 /* <13> Transmitter on too long [DELUA only] */ +#define STAT_CERR 0010000 /* <12> collision test error */ +#define STAT_TMOT 0004000 /* <11> UNIBUS timeout */ +#define STAT_RRNG 0001000 /* <09> receive ring error */ +#define STAT_TRNG 0000400 /* <08> transmit ring error */ +#define STAT_PTCH 0000200 /* <07> ROM patch */ +#define STAT_RRAM 0000100 /* <06> running from RAM */ +#define STAT_RREV 0000077 /* <05:00> ROM version */ + +/* Mode definitions */ +#define MODE_PROM 0100000 /* <15> Promiscuous Mode */ +#define MODE_ENAL 0040000 /* <14> Enable All Multicasts */ +#define MODE_DRDC 0020000 /* <13> Disable Data Chaining */ +#define MODE_TPAD 0010000 /* <12> Transmit Msg Pad Enable */ +#define MODE_ECT 0004000 /* <11> Enable Collision Test */ +#define MODE_DMNT 0001000 /* <09> Disable Maint Message */ +#define MODE_INTL 0000200 /* <07> Internal Loopback [DELUA only] */ +#define MODE_DTCR 0000010 /* <03> Disable Transmit CRC */ +#define MODE_LOOP 0000004 /* <02> Internal Loopback Mode */ +#define MODE_HDPX 0000001 /* <00> Half-Duplex Mode */ + +/* Function Code definitions */ +#define FC_NOOP 0000000 /* no-op */ +#define FC_LSM 0000001 /* Load and Start Microaddress */ +#define FC_RDPA 0000002 /* Read Default Physical Address */ +#define FC_RPA 0000004 /* Read Physical Address */ +#define FC_WPA 0000005 /* Write Physical Address */ +#define FC_RMAL 0000006 /* Read Multicast Address List */ +#define FC_WMAL 0000007 /* Write Multicast Address List */ +#define FC_RRF 0000010 /* Read Ring Format */ +#define FC_WRF 0000011 /* Write Ring Format */ +#define FC_RDCTR 0000012 /* Read Counters */ +#define FC_RDCLCTR 0000013 /* Read and Clear Counters */ +#define FC_RMODE 0000014 /* Read Mode */ +#define FC_WMODE 0000015 /* Write Mode */ +#define FC_RSTAT 0000016 /* Read Status */ +#define FC_RCSTAT 0000017 /* Read and Clear Status */ +#define FC_DIM 0000020 /* Dump Internal Memory */ +#define FC_LIM 0000021 /* Load Internal Memory */ +#define FC_RSID 0000022 /* Read System ID parameters */ +#define FC_WSID 0000023 /* Write System ID parameters */ +#define FC_RLSA 0000024 /* Read Load Server Address */ +#define FC_WLSA 0000025 /* Write Load Server Address */ + +/* Transmitter Ring definitions */ +#define TXR_OWN 0100000 /* <15> we own it (1) */ +#define TXR_ERRS 0040000 /* <14> error summary */ +#define TXR_MTCH 0020000 /* <13> Station Match */ +#define TXR_MORE 0010000 /* <12> Mult Retries Needed */ +#define TXR_ONE 0004000 /* <11> One Collision */ +#define TXR_DEF 0002000 /* <10> Deferred */ +#define TXR_STF 0001000 /* <09> Start Of Frame */ +#define TXR_ENF 0000400 /* <08> End Of Frame */ +#define TXR_BUFL 0100000 /* <15> Buffer Length Error */ +#define TXR_UBTO 0040000 /* <14> UNIBUS TimeOut */ +#define TXR_UFLO 0020000 /* <13> Underflow Error */ +#define TXR_LCOL 0010000 /* <12> Late Collision */ +#define TXR_LCAR 0004000 /* <11> Lost Carrier */ +#define TXR_RTRY 0002000 /* <10> Retry Failure (16x) */ +#define TXR_TDR 0001777 /* <9:0> TDR value if RTRY=1 */ + +/* Receiver Ring definitions */ +#define RXR_OWN 0100000 /* <15> we own it (1) */ +#define RXR_ERRS 0040000 /* <14> Error Summary */ +#define RXR_FRAM 0020000 /* <13> Frame Error */ +#define RXR_OFLO 0010000 /* <12> Message Overflow */ +#define RXR_CRC 0004000 /* <11> CRC Check Error */ +#define RXR_STF 0001000 /* <09> Start Of Frame */ +#define RXR_ENF 0000400 /* <08> End Of Frame */ +#define RXR_BUFL 0100000 /* <15> Buffer Length error */ +#define RXR_UBTO 0040000 /* <14> UNIBUS TimeOut */ +#define RXR_NCHN 0020000 /* <13> No Data Chaining */ +#define RXR_OVRN 0010000 /* <12> Overrun Error [DELUA only] */ +#define RXR_MLEN 0007777 /* <11:0> Message Length */ + +/* debugging bitmaps */ +#define DBG_TRC 0x0001 /* trace routine calls */ +#define DBG_REG 0x0002 /* trace read/write registers */ +#define DBG_WRN 0x0004 /* display warnings */ +#define DBG_PCK 0x0080 /* display packets */ +#define DBG_ETH 0x8000 /* debug ethernet device */ + +#endif /* _PDP11_XU_H */ diff --git a/PDP11/txt2cbn.c b/PDP11/txt2cbn.c new file mode 100644 index 0000000..a45d42d --- /dev/null +++ b/PDP11/txt2cbn.c @@ -0,0 +1,49 @@ +#include + +#define ERROR 00404 +#include "pdp11_cr_dat.h" + +static int colStart = 1; /* starting column */ +static int colEnd = 80; /* ending column */ + +main () +{ + int col, c; + + while (1) { + for (col = colStart; col <= colEnd; ) { + switch (c = fgetc (stdin)) { + case EOF: + /* fall through */ + case '\n': + while (col <= colEnd) { + fputc (o29_code[' '] & 077, stdout); + fputc ((o29_code[' '] >> 6) & 077, stdout); + col++; + } + break; + case '\t': + do { + fputc (o29_code[' '] & 077, stdout); + fputc ((o29_code[' '] >> 6) & 077, stdout); + col++; + } while (((col & 07) != 1) && (col <= colEnd)); + break; + default: + fputc (o29_code[c] & 077, stdout); + fputc ((o29_code[c] >> 6) & 077, stdout); + col++; + break; + } + } + /* flush long lines, or flag over-length card */ + if (c != '\n' && c != EOF) { + printf ("overlength line\n"); + do c = fgetc (stdin); + while ((c != EOF) && (c != '\n')); + } + if (c == EOF) + break; + } + exit (1); +} diff --git a/PDP18B/pdp18b_cpu.c b/PDP18B/pdp18b_cpu.c new file mode 100644 index 0000000..7886d83 --- /dev/null +++ b/PDP18B/pdp18b_cpu.c @@ -0,0 +1,2227 @@ +/* pdp18b_cpu.c: 18b PDP CPU simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu PDP-4/7/9/15 central processor + + 28-Apr-07 RMS Removed clock initialization + 26-Dec-06 RMS Fixed boundary test in KT15/XVM (reported by Andrew Warkentin) + 30-Oct-06 RMS Added idle and infinite loop detection + 08-Oct-06 RMS Added RDCLK instruction + Fixed bug, PC off by one on fetch mem mmgt error + PDP-15 sets API 3 on mem mmgt trap (like PI) + PDP-15 sets API 4 on CAL only if 0-3 inactive + CAF clears memory management mode register + 27-Jun-06 RMS Reset clears AC, L, and MQ + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 22-Jul-05 RMS Removed AAS, error in V1 reference manual + 06-Nov-04 RMS Added =n to SHOW HISTORY + 26-Mar-04 RMS Fixed warning from -std=c99 + 14-Jan-04 RMS Fixed g_mode in XVM implementation + PDP-15 index, autoincrement generate 18b addresses + Revised IO device call interface + 31-Dec-03 RMS Fixed bug in cpu_set_hist + 02-Nov-03 RMS Changed PDP-9,-15 default to API + 26-Oct-03 RMS Fixed bug in PDP-4,-7,-9 autoincrement addressing + 19-Sep-03 RMS Changed instruction history to be dynamically sized + 31-Aug-03 RMS Added instruction history + Fixed PDP-15-specific implementation of API priorities + 16-Aug-03 RMS Fixed PDP-15-specific handling of EAE unsigned mul/div + 27-Jul-03 RMS Added FP15 support + Added XVM support + Added EAE option to PDP-4 + Added PDP-15 "re-entrancy ECO" + Fixed memory protect/skip interaction + Fixed CAF not to reset CPU + 12-Mar-03 RMS Added logical name support + 18-Feb-03 RMS Fixed three EAE bugs (found by Hans Pufal) + 05-Oct-02 RMS Added DIBs, device number support + 25-Jul-02 RMS Added DECtape support for PDP-4 + 06-Jan-02 RMS Revised enable/disable support + 30-Dec-01 RMS Added old PC queue + 30-Nov-01 RMS Added extended SET/SHOW support + 25-Nov-01 RMS Revised interrupt structure + 19-Sep-01 RMS Fixed bug in EAE (found by Dave Conroy) + 17-Sep-01 RMS Fixed typo in conditional + 10-Aug-01 RMS Removed register from declarations + 17-Jul-01 RMS Moved function prototype + 27-May-01 RMS Added second Teletype support, fixed bug in API + 18-May-01 RMS Added PDP-9,-15 API option + 16-May-01 RMS Fixed bugs in protection checks + 26-Apr-01 RMS Added device enable/disable support + 25-Jan-01 RMS Added DECtape support + 18-Dec-00 RMS Added PDP-9,-15 memm init register + 30-Nov-00 RMS Fixed numerous PDP-15 bugs + 14-Apr-99 RMS Changed t_addr to unsigned + + The 18b PDP family has five distinct architectural variants: PDP-1, + PDP-4, PDP-7, PDP-9, and PDP-15. Of these, the PDP-1 is so unique + as to require a different simulator. The PDP-4, PDP-7, PDP-9, and + PDP-15 are "upward compatible", with each new variant adding + distinct architectural features and incompatibilities. + + The register state for the 18b PDP's is: + + all AC<0:17> accumulator + all MQ<0:17> multiplier-quotient + all L link flag + all PC<0:x> program counter + all IORS I/O status register + PDP-7, PDP-9 EXTM extend mode + PDP-15 BANKM bank mode + PDP-7 USMD trap mode + PDP-9, PDP-15 USMD user mode + PDP-9, PDP-15 BR bounds register + PDP-15 RR relocation register + PDP-15 XVM MMR memory management register + PDP-15 XR index register + PDP-15 LR limit register + + The PDP-4, PDP-7, and PDP-9 have five instruction formats: memory + reference, load immediate, I/O transfer, EAE, and operate. The PDP-15 + adds a sixth, index operate, and a seventh, floating point. The memory + reference format for the PDP-4, PDP-7, and PDP-9, and for the PDP-15 + in bank mode, is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in| address | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The PDP-15 in page mode trades an address bit for indexing capability: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in| X| address | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:3> mnemonic action + + 00 CAL JMS with MA = 20 + 04 DAC M[MA] = AC + 10 JMS M[MA] = L'mem'user'PC, PC = MA + 1 + 14 DZM M[MA] = 0 + 20 LAC AC = M[MA] + 24 XOR AC = AC ^ M[MA] + 30 ADD L'AC = AC + M[MA] one's complement + 34 TAD L'AC = AC + M[MA] + 40 XCT M[MA] is executed as an instruction + 44 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 50 AND AC = AC & M[MA] + 54 SAD skip if AC != M[MA] + 60 JMP PC = MA + + On the PDP-4, PDP-7, and PDP-9, and the PDP-15 in bank mode, memory + reference instructions can access an address space of 32K words. The + address space is divided into four 8K word fields. An instruction can + directly address, via its 13b address, the entire current field. On the + PDP-4, PDP-7, and PDP-9, if extend mode is off, indirect addresses access + the current field; if on (or a PDP-15), they can access all 32K. + + On the PDP-15 in page mode, memory reference instructions can access + an address space of 128K words. The address is divided into four 32K + word blocks, each of which consists of eight 4K pages. An instruction + can directly address, via its 12b address, the current page. Indirect + addresses can access the current block. Indexed and autoincrement + addresses can access all 128K. + + On the PDP-4 and PDP-7, if an indirect address in in locations 00010- + 00017 of any field, the indirect address is incremented and rewritten + to memory before use. On the PDP-9 and PDP-15, only locations 00010- + 00017 of field zero autoincrement; special logic will redirect indirect + references to 00010-00017 to field zero, even if (on the PDP-9) extend + mode is off. + + The EAE format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 0 1| | | | | | | | | | | | | | | EAE + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | | | | | | | + | | | | | | | | | | | | | +- or SC (3) + | | | | | | | | | | | | +---- or MQ (3) + | | | | | | | | | | | +------- compl MQ (3) + | | | | | | | | \______________/ + | | | | | | | | | + | | | | | \_____/ +--------- shift count + | | | | | | + | | | | | +---------------------- EAE command (3) + | | | | +---------------------------- clear AC (2) + | | | +------------------------------- or AC (2) + | | +---------------------------------- load EAE sign (1) + | +------------------------------------- clear MQ (1) + +---------------------------------------- load link (1) + + The I/O transfer format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 0 0| device | sdv |cl| pulse | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction sends the the specified pulse to the + specified I/O device and sub-device. The I/O device may take data + from the AC, return data to the AC, initiate or cancel operations, + or skip on status. On the PDP-4, PDP-7, and PDP-9, bits <4:5> + were designated as subdevice bits but were never used; the PDP-15 + requires them to be zero. + + On the PDP-15, the floating point format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 0 1| subopcode | floating point + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + Indirection is always single level. + + On the PDP-15, the index operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 1| subopcode | immediate | index operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The index operate instructions provide various operations on the + index and limit registers. + + The operate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 1 0| | | | | | | | | | | | | | operate + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | | | | | | + | | | | | | | | | | | | +- CMA (3) + | | | | | | | | | | | +---- CML (3) + | | | | | | | | | | +------- OAS (3) + | | | | | | | | | +---------- RAL (3) + | | | | | | | | +------------- RAR (3) + | | | | | | | +---------------- HLT (4) + | | | | | | +------------------- SMA (1) + | | | | | +---------------------- SZA (1) + | | | | +------------------------- SNL (1) + | | | +---------------------------- invert skip (1) + | | +------------------------------- rotate twice (2) + | +---------------------------------- CLL (2) + +------------------------------------- CLA (2) + + The operate instruction can be microprogrammed to perform operations + on the AC and link. + + The load immediate format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 1 1| immediate | LAW + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:4> mnemonic action + + 76 LAW AC = IR + + This routine is the instruction decode routine for the 18b PDP's. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unimplemented instruction and STOP_INST flag set + nested XCT's + I/O error in I/O simulator + + 2. Interrupts. Interrupt requests are maintained in the int_hwre + array. int_hwre[0:3] corresponds to API levels 0-3; int_hwre[4] + holds PI requests. + + 3. Arithmetic. The 18b PDP's implements both 1's and 2's complement + arithmetic for signed numbers. In 1's complement arithmetic, a + negative number is represented by the complement (XOR 0777777) of + its absolute value. Addition of 1's complement numbers requires + propagating the carry out of the high order bit back to the low + order bit. + + 4. Adding I/O devices. Three modules must be modified: + + pdp18b_defs.h add interrupt request definition + pdp18b_sys.c add sim_devices table entry +*/ + +#include "pdp18b_defs.h" + +#define SEXT(x) ((int32) (((x) & SIGN)? (x) | ~DMASK: (x) & DMASK)) + +#define UNIT_V_NOEAE (UNIT_V_UF + 0) /* EAE absent */ +#define UNIT_V_NOAPI (UNIT_V_UF + 1) /* API absent */ +#define UNIT_V_PROT (UNIT_V_UF + 2) /* protection */ +#define UNIT_V_RELOC (UNIT_V_UF + 3) /* relocation */ +#define UNIT_V_XVM (UNIT_V_UF + 4) /* XVM */ +#define UNIT_V_MSIZE (UNIT_V_UF + 5) /* dummy mask */ +#define UNIT_NOEAE (1 << UNIT_V_NOEAE) +#define UNIT_NOAPI (1 << UNIT_V_NOAPI) +#define UNIT_PROT (1 << UNIT_V_PROT) +#define UNIT_RELOC (1 << UNIT_V_RELOC) +#define UNIT_XVM (1 << UNIT_V_XVM) +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define OP_KSF 0700301 + +#define HIST_API 0x40000000 +#define HIST_PI 0x20000000 +#define HIST_PC 0x10000000 +#define HIST_MIN 64 +#define HIST_MAX 65536 +#define HIST_M_LVL 0x3F +#define HIST_V_LVL 6 + +typedef struct { + int32 pc; + int32 ir; + int32 ir1; + int32 lac; + int32 mq; + } InstHistory; + +#define XVM (cpu_unit.flags & UNIT_XVM) +#define RELOC (cpu_unit.flags & UNIT_RELOC) +#define PROT (cpu_unit.flags & UNIT_PROT) + +#if defined (PDP4) +#define EAE_DFLT UNIT_NOEAE +#else +#define EAE_DFLT 0 +#endif +#if defined (PDP4) || defined (PDP7) +#define API_DFLT UNIT_NOAPI +#define PROT_DFLT 0 +#define ASW_DFLT 017763 +#else +#define API_DFLT 0 +#define PROT_DFLT UNIT_PROT +#define ASW_DFLT 017720 +#endif + +int32 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 LAC = 0; /* link'AC */ +int32 MQ = 0; /* MQ */ +int32 PC = 0; /* PC */ +int32 iors = 0; /* IORS */ +int32 ion = 0; /* int on */ +int32 ion_defer = 0; /* int defer */ +int32 ion_inh = 0; /* int inhibit */ +int32 int_pend = 0; /* int pending */ +int32 int_hwre[API_HLVL+1] = { 0 }; /* int requests */ +int32 api_enb = 0; /* API enable */ +int32 api_req = 0; /* API requests */ +int32 api_act = 0; /* API active */ +int32 memm = 0; /* mem mode */ +#if defined (PDP15) +int32 memm_init = 1; /* mem init */ +#else +int32 memm_init = 0; +#endif +int32 usmd = 0; /* user mode */ +int32 usmd_buf = 0; /* user mode buffer */ +int32 usmd_defer = 0; /* user mode defer */ +int32 trap_pending = 0; /* trap pending */ +int32 emir_pending = 0; /* emir pending */ +int32 rest_pending = 0; /* restore pending */ +int32 BR = 0; /* mem mgt bounds */ +int32 RR = 0; /* mem mgt reloc */ +int32 MMR = 0; /* XVM mem mgt */ +int32 nexm = 0; /* nx mem flag */ +int32 prvn = 0; /* priv viol flag */ +int32 SC = 0; /* shift count */ +int32 eae_ac_sign = 0; /* EAE AC sign */ +int32 SR = 0; /* switch register */ +int32 ASW = ASW_DFLT; /* address switches */ +int32 XR = 0; /* index register */ +int32 LR = 0; /* limit register */ +int32 stop_inst = 0; /* stop on rsrv inst */ +int32 xct_max = 16; /* nested XCT limit */ +#if defined (PDP15) +int32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +#else +int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +#endif +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ + +extern int32 sim_int_char; +extern int32 sim_interval; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern t_bool sim_idle_enab; +extern DEVICE *sim_devices[]; +extern FILE *sim_log; + +t_bool build_dev_tab (void); +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +void cpu_caf (void); +void cpu_inst_hist (int32 addr, int32 inst); +void cpu_intr_hist (int32 flag, int32 lvl); +int32 upd_iors (void); +int32 api_eval (int32 *pend); +t_stat Read (int32 ma, int32 *dat, int32 cyc); +t_stat Write (int32 ma, int32 dat, int32 cyc); +t_stat Ia (int32 ma, int32 *ea, t_bool jmp); +int32 Incr_addr (int32 addr); +int32 Jms_word (int32 t); +#if defined (PDP15) +#define INDEX(i,x) if (!memm && ((i) & I_IDX)) x = ((x) + XR) & DMASK +int32 Prot15 (int32 ma, t_bool bndchk); +int32 Reloc15 (int32 ma, int32 acc); +int32 RelocXVM (int32 ma, int32 acc); +extern t_stat fp15 (int32 ir); +extern int32 clk_task_upd (t_bool clr); +#else +#define INDEX(i,x) +#endif + +extern int32 clk (int32 dev, int32 pulse, int32 AC); + +int32 (*dev_tab[DEV_MAX])(int32 dev, int32 pulse, int32 AC); /* device dispatch */ + +int32 (*dev_iors[DEV_MAX])(void); /* IORS dispatch */ + +static const int32 api_ffo[256] = { + 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + +static const int32 api_vec[API_HLVL][32] = { + { ACH_PWRFL }, /* API 0 */ + { ACH_DTA, ACH_MTA, ACH_DRM, ACH_RF, ACH_RP, ACH_RB }, /* API 1 */ + { ACH_PTR, ACH_LPT, ACH_LPT }, /* API 2 */ + { ACH_CLK, ACH_TTI1, ACH_TTO1 } /* API 3 */ + }; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { + UDATA (NULL, UNIT_FIX+UNIT_BINK+EAE_DFLT+API_DFLT+PROT_DFLT, + MAXMEMSIZE) + }; + +REG cpu_reg[] = { + { ORDATA (PC, PC, ADDRSIZE) }, + { ORDATA (AC, LAC, 18) }, + { FLDATA (L, LAC, 18) }, + { ORDATA (MQ, MQ, 18) }, + { ORDATA (SC, SC, 6) }, + { FLDATA (EAE_AC_SIGN, eae_ac_sign, 18) }, + { ORDATA (SR, SR, 18) }, + { ORDATA (ASW, ASW, ADDRSIZE) }, + { ORDATA (IORS, iors, 18), REG_RO }, + { BRDATA (INT, int_hwre, 8, 32, API_HLVL+1), REG_RO }, + { FLDATA (INT_PEND, int_pend, 0), REG_RO }, + { FLDATA (ION, ion, 0) }, + { ORDATA (ION_DELAY, ion_defer, 2) }, +#if defined (PDP7) + { FLDATA (TRAPM, usmd, 0) }, + { FLDATA (TRAPP, trap_pending, 0) }, + { FLDATA (EXTM, memm, 0) }, + { FLDATA (EXTM_INIT, memm_init, 0) }, + { FLDATA (EMIRP, emir_pending, 0) }, +#endif +#if defined (PDP9) + { FLDATA (APIENB, api_enb, 0) }, + { ORDATA (APIREQ, api_req, 8) }, + { ORDATA (APIACT, api_act, 8) }, + { ORDATA (BR, BR, ADDRSIZE) }, + { FLDATA (USMD, usmd, 0) }, + { FLDATA (USMDBUF, usmd_buf, 0) }, + { FLDATA (USMDDEF, usmd_defer, 0) }, + { FLDATA (NEXM, nexm, 0) }, + { FLDATA (PRVN, prvn, 0) }, + { FLDATA (TRAPP, trap_pending, 0) }, + { FLDATA (EXTM, memm, 0) }, + { FLDATA (EXTM_INIT, memm_init, 0) }, + { FLDATA (EMIRP, emir_pending, 0) }, + { FLDATA (RESTP, rest_pending, 0) }, + { FLDATA (PWRFL, int_hwre[API_PWRFL], INT_V_PWRFL) }, +#endif +#if defined (PDP15) + { FLDATA (ION_INH, ion_inh, 0) }, + { FLDATA (APIENB, api_enb, 0) }, + { ORDATA (APIREQ, api_req, 8) }, + { ORDATA (APIACT, api_act, 8) }, + { ORDATA (XR, XR, 18) }, + { ORDATA (LR, LR, 18) }, + { ORDATA (BR, BR, 18) }, + { ORDATA (RR, RR, 18) }, + { ORDATA (MMR, MMR, 18) }, + { FLDATA (USMD, usmd, 0) }, + { FLDATA (USMDBUF, usmd_buf, 0) }, + { FLDATA (USMDDEF, usmd_defer, 0) }, + { FLDATA (NEXM, nexm, 0) }, + { FLDATA (PRVN, prvn, 0) }, + { FLDATA (TRAPP, trap_pending, 0) }, + { FLDATA (BANKM, memm, 0) }, + { FLDATA (BANKM_INIT, memm_init, 0) }, + { FLDATA (RESTP, rest_pending, 0) }, + { FLDATA (PWRFL, int_hwre[API_PWRFL], INT_V_PWRFL) }, +#endif + { BRDATA (PCQ, pcq, 8, ADDRSIZE, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { DRDATA (XCT_MAX, xct_max, 8), PV_LEFT + REG_NZ }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } }; + +MTAB cpu_mod[] = { + { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, + { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, +#if defined (PDP9) || defined (PDP15) + { UNIT_NOAPI, UNIT_NOAPI, "no API", "NOAPI", NULL }, + { UNIT_NOAPI, 0, "API", "API", NULL }, + { UNIT_PROT+UNIT_RELOC+UNIT_XVM, 0, "no memory protect", + "NOPROTECT", NULL }, + { UNIT_PROT+UNIT_RELOC+UNIT_XVM, UNIT_PROT, "memory protect", + "PROTECT", NULL }, +#endif +#if defined (PDP15) + { UNIT_PROT+UNIT_RELOC+UNIT_XVM, UNIT_PROT+UNIT_RELOC, + "memory relocation", "RELOCATION", NULL }, + { UNIT_PROT+UNIT_RELOC+UNIT_XVM, UNIT_PROT+UNIT_RELOC+UNIT_XVM, + "XVM", "XVM", NULL }, +#endif + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, +#if defined (PDP4) + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, +#endif + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, +#if (MAXMEMSIZE > 8192) + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, +#endif +#if (MAXMEMSIZE > 32768) + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { UNIT_MSIZE, 81920, NULL, "80K", &cpu_set_size }, + { UNIT_MSIZE, 98304, NULL, "96K", &cpu_set_size }, + { UNIT_MSIZE, 114688, NULL, "112K", &cpu_set_size }, + { UNIT_MSIZE, 131072, NULL, "128K", &cpu_set_size }, +#endif + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, ADDRSIZE, 1, 8, 18, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL + }; + +t_stat sim_instr (void) +{ +int32 api_int, api_usmd, skp; +int32 iot_data, device, pulse; +int32 last_IR; +t_stat reason; + +if (build_dev_tab ()) return SCPE_STOP; /* build, chk tables */ +PC = PC & AMASK; /* clean variables */ +LAC = LAC & LACMASK; +MQ = MQ & DMASK; +reason = 0; +last_IR = -1; +if (cpu_unit.flags & UNIT_NOAPI) /* no API? */ + api_enb = api_req = api_act = 0; +api_int = api_eval (&int_pend); /* eval API */ +api_usmd = 0; /* not API user cycle */ + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + int32 IR, MA, MB, esc, t, xct_count; + int32 link_init, fill; + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + api_int = api_eval (&int_pend); /* eval API */ + } + +/* PDP-4 and PDP-7 traps and interrupts + + PDP-4 no trap + PDP-7 trap: extend mode forced on, M[0] = PC, PC = 2 + PDP-4, PDP-7 programmable interrupts only */ + +#if defined (PDP4) || defined (PDP7) +#if defined (PDP7) + + if (trap_pending) { /* trap pending? */ + PCQ_ENTRY; /* save old PC */ + MB = Jms_word (1); /* save state */ + ion = 0; /* interrupts off */ + memm = 1; /* extend on */ + emir_pending = trap_pending = 0; /* emir, trap off */ + usmd = usmd_buf = 0; /* user mode off */ + Write (0, MB, WR); /* save in 0 */ + PC = 2; /* fetch next from 2 */ + } + +#endif + + if (int_pend && ion && !ion_defer) { /* interrupt? */ + PCQ_ENTRY; /* save old PC */ + MB = Jms_word (usmd); /* save state */ + ion = 0; /* interrupts off */ + memm = 0; /* extend off */ + emir_pending = rest_pending = 0; /* emir, restore off */ + usmd = usmd_buf = 0; /* user mode off */ + Write (0, MB, WR); /* physical write */ + PC = 1; /* fetch next from 1 */ + } + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + +#endif /* end PDP-4/PDP-7 */ + +/* PDP-9 and PDP-15 traps and interrupts + + PDP-9 trap: extend mode ???, M[0/20] = PC, PC = 0/21 + PDP-15 trap: bank mode unchanged, M[0/20] = PC, PC = 0/21 + PDP-9, PDP-15 API and program interrupts */ + +#if defined (PDP9) || defined (PDP15) + + if (trap_pending) { /* trap pending? */ + PCQ_ENTRY; /* save old PC */ + MB = Jms_word (1); /* save state */ + if (ion) { /* int on? */ + ion = 0; /* interrupts off */ + MA = 0; /* treat like PI */ +#if defined (PDP15) + ion_defer = 2; /* free instruction */ + if (!(cpu_unit.flags & UNIT_NOAPI)) { /* API? */ + api_act = api_act | API_ML3; /* set lev 3 active */ + api_int = api_eval (&int_pend); /* re-evaluate */ + } +#endif + } + else MA = 020; /* sortof like CAL */ + emir_pending = rest_pending = trap_pending = 0; /* emir,rest,trap off */ + usmd = usmd_buf = 0; /* user mode off */ + Write (MA, MB, WR); /* physical write */ + PC = MA + 1; /* fetch next */ + } + + if (api_int && !ion_defer) { /* API intr? */ + int32 i, lvl = api_int - 1; /* get req level */ + if (hst_lnt) cpu_intr_hist (HIST_API, lvl); /* record */ + api_act = api_act | (API_ML0 >> lvl); /* set level active */ + if (lvl >= API_HLVL) { /* software req? */ + MA = ACH_SWRE + lvl - API_HLVL; /* vec = 40:43 */ + api_req = api_req & ~(API_ML0 >> lvl); /* remove request */ + } + else { + MA = 0; /* assume fails */ + for (i = 0; i < 32; i++) { /* loop hi to lo */ + if ((int_hwre[lvl] >> i) & 1) { /* int req set? */ + MA = api_vec[lvl][i]; /* get vector */ + break; /* and stop */ + } + } + } + if (MA == 0) { /* bad channel? */ + reason = STOP_API; /* API error */ + break; + } + api_int = api_eval (&int_pend); /* no API int */ + api_usmd = usmd; /* API user mode cycle */ + usmd = usmd_buf = 0; /* user mode off */ + emir_pending = rest_pending = 0; /* emir, restore off */ + xct_count = 0; + Read (MA, &IR, FE); /* fetch instruction */ + goto xct_instr; + } + + if (int_pend && ion && !ion_defer && /* int pending, enabled? */ + !(api_enb && (api_act & API_MASKPI))) { /* API off or not masking PI? */ + PCQ_ENTRY; /* save old PC */ + if (hst_lnt) cpu_intr_hist (HIST_PI, 0); /* record */ + MB = Jms_word (usmd); /* save state */ + ion = 0; /* interrupts off */ + ion_defer = 2; /* free instruction */ +#if defined (PDP9) /* PDP-9, */ + memm = 0; /* extend off */ +#else /* PDP-15 */ + if (!(cpu_unit.flags & UNIT_NOAPI)) { /* API? */ + api_act = api_act | API_ML3; /* set lev 3 active */ + api_int = api_eval (&int_pend); /* re-evaluate */ + } +#endif + emir_pending = rest_pending = 0; /* emir, restore off */ + usmd = usmd_buf = 0; /* user mode off */ + Write (0, MB, WR); /* physical write */ + PC = 1; /* fetch next from 1 */ + } + + if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + if (!usmd_defer) usmd = usmd_buf; /* no IOT? load usmd */ + else usmd_defer = 0; /* cancel defer */ + +#endif /* PDP-9/PDP-15 */ + +/* Instruction fetch and address decode */ + + xct_count = 0; /* track nested XCT's */ + MA = PC; /* fetch at PC */ + if (Read (MA, &IR, FE)) continue; /* fetch instruction */ + PC = Incr_addr (PC); /* increment PC */ + + xct_instr: /* label for API, XCT */ + if (hst_lnt) cpu_inst_hist (MA, IR); /* history? */ + if (ion_defer) ion_defer = ion_defer - 1; /* count down defer */ + if (sim_interval) sim_interval = sim_interval - 1; + +#if defined (PDP15) /* PDP15 */ + + if (memm) MA = (MA & B_EPCMASK) | (IR & B_DAMASK); /* bank mode dir addr */ + else MA = (MA & P_EPCMASK) | (IR & P_DAMASK); /* page mode dir addr */ + +#else /* others */ + + MA = (MA & B_EPCMASK) | (IR & B_DAMASK); /* bank mode only */ + +#endif + + switch ((IR >> 13) & 037) { /* decode IR<0:4> */ + +/* LAC: opcode 20 */ + + case 011: /* LAC, indir */ + if (Ia (MA, &MA, 0)) break; + case 010: /* LAC, dir */ + INDEX (IR, MA); + if (Read (MA, &MB, RD)) break; + LAC = (LAC & LINK) | MB; + break; + +/* DAC: opcode 04 */ + + case 003: /* DAC, indir */ + if (Ia (MA, &MA, 0)) break; + case 002: /* DAC, dir */ + INDEX (IR, MA); + Write (MA, LAC & DMASK, WR); + break; + +/* DZM: opcode 14 */ + + case 007: /* DZM, indir */ + if (Ia (MA, &MA, 0)) break; + case 006: /* DZM, direct */ + INDEX (IR, MA); + Write (MA, 0, WR); + break; + +/* AND: opcode 50 */ + + case 025: /* AND, ind */ + if (Ia (MA, &MA, 0)) break; + case 024: /* AND, dir */ + INDEX (IR, MA); + if (Read (MA, &MB, RD)) break; + LAC = LAC & (MB | LINK); + break; + +/* XOR: opcode 24 */ + + case 013: /* XOR, ind */ + if (Ia (MA, &MA, 0)) break; + case 012: /* XOR, dir */ + INDEX (IR, MA); + if (Read (MA, &MB, RD)) break; + LAC = LAC ^ MB; + break; + +/* ADD: opcode 30 */ + + case 015: /* ADD, indir */ + if (Ia (MA, &MA, 0)) break; + case 014: /* ADD, dir */ + INDEX (IR, MA); + if (Read (MA, &MB, RD)) break; + t = (LAC & DMASK) + MB; + if (t > DMASK) t = (t + 1) & DMASK; /* end around carry */ + if (((~LAC ^ MB) & (LAC ^ t)) & SIGN) /* overflow? */ + LAC = LINK | t; /* set link */ + else LAC = (LAC & LINK) | t; + break; + +/* TAD: opcode 34 */ + + case 017: /* TAD, indir */ + if (Ia (MA, &MA, 0)) break; + case 016: /* TAD, dir */ + INDEX (IR, MA); + if (Read (MA, &MB, RD)) break; + LAC = (LAC + MB) & LACMASK; + break; + +/* ISZ: opcode 44 */ + + case 023: /* ISZ, indir */ + if (Ia (MA, &MA, 0)) break; + case 022: /* ISZ, dir */ + INDEX (IR, MA); + if (Read (MA, &MB, RD)) break; + MB = (MB + 1) & DMASK; + if (Write (MA, MB, WR)) break; + if (MB == 0) PC = Incr_addr (PC); + break; + +/* SAD: opcode 54 */ + + case 027: /* SAD, indir */ + if (Ia (MA, &MA, 0)) break; + case 026: /* SAD, dir */ + INDEX (IR, MA); + if (Read (MA, &MB, RD)) break; + if ((LAC & DMASK) != MB) PC = Incr_addr (PC); + break; + +/* XCT: opcode 40 */ + + case 021: /* XCT, indir */ + if (Ia (MA, &MA, 0)) break; + case 020: /* XCT, dir */ + INDEX (IR, MA); + if ((api_usmd | usmd) && (xct_count != 0)) { /* chained and usmd? */ + if (usmd) prvn = trap_pending = 1; /* trap if usmd */ + break; /* nop if api_usmd */ + } + if (xct_count >= xct_max) { /* too many XCT's? */ + reason = STOP_XCT; + break; + } + xct_count = xct_count + 1; /* count XCT's */ +#if defined (PDP9) + ion_defer = 1; /* defer intr */ +#endif + if (Read (MA, &IR, FE)) break; /* fetch inst, mm err? */ + goto xct_instr; /* go execute */ + +/* CAL: opcode 00 - api_usmd records whether usmd = 1 at start of API cycle + + On the PDP-4 and PDP-7, CAL (I) is exactly the same as JMS (I) 20 + On the PDP-9 and PDP-15, CAL clears user mode + On the PDP-9 and PDP-15 with API, CAL activates level 4 + On the PDP-15, CAL goes to absolute 20, regardless of mode */ + + case 001: case 000: /* CAL */ + t = usmd; /* save user mode */ +#if defined (PDP15) /* PDP15 */ + MA = 020; /* MA = abs 20 */ + ion_defer = 1; /* "free instruction" */ +#else /* others */ + if (memm) MA = 020; /* if ext, abs 20 */ + else MA = (PC & B_EPCMASK) | 020; /* else bank-rel 20 */ +#endif +#if defined (PDP9) || defined (PDP15) + usmd = usmd_buf = 0; /* clear user mode */ + if ((cpu_unit.flags & UNIT_NOAPI) == 0) { /* if API, act lvl 4 */ +#if defined (PDP15) /* PDP15: if 0-3 inactive */ + if ((api_act & (API_ML0|API_ML1|API_ML2|API_ML3)) == 0) +#endif + api_act = api_act | API_ML4; + api_int = api_eval (&int_pend); + } +#endif + if (IR & I_IND) { /* indirect? */ + if (Ia (MA, &MA, 0)) break; + } + PCQ_ENTRY; + MB = Jms_word (api_usmd | t); /* save state */ + Write (MA, MB, WR); + PC = Incr_addr (MA); + break; + +/* JMS: opcode 010 - api_usmd records whether usmd = 1 at start of API cycle */ + + case 005: /* JMS, indir */ + if (Ia (MA, &MA, 0)) break; + case 004: /* JMS, dir */ + INDEX (IR, MA); + PCQ_ENTRY; +#if defined (PDP15) /* PDP15 */ + if (!usmd) ion_defer = 1; /* "free instruction" */ +#endif + MB = Jms_word (api_usmd | usmd); /* save state */ + if (Write (MA, MB, WR)) break; + PC = Incr_addr (MA) & AMASK; + break; + +/* JMP: opcode 60 */ + + case 031: /* JMP, indir */ + if (Ia (MA, &MA, 1)) break; + INDEX (IR, MA); + PCQ_ENTRY; /* save old PC */ + PC = MA & AMASK; + break; + +/* JMP direct - check for idle */ + + case 030: /* JMP, dir */ + INDEX (IR, MA); + PCQ_ENTRY; /* save old PC */ + if (sim_idle_enab) { /* idling enabled? */ + t_bool iof = (ion_inh != 0) || /* IOF if inhibited */ + ((ion == 0) && (api_enb == 0)); /* or PI and api off */ + if (((MA ^ (PC - 2)) & AMASK) == 0) { /* 1) JMP *-1? */ + if (iof && (last_IR == OP_KSF) && /* iof, prv KSF, */ + !TST_INT (TTI)) /* no TTI flag? */ + sim_idle (0, FALSE); /* we're idle */ + } + else if (((MA ^ (PC - 1)) & AMASK) == 0) { /* 2) JMP *? */ + if (iof) reason = STOP_LOOP; /* iof? inf loop */ + else sim_idle (0, FALSE); /* ion? idle */ + } + } /* end idle */ + PC = MA & AMASK; + break; + +/* OPR: opcode 74 */ + + case 037: /* OPR, indir */ + LAC = (LAC & LINK) | IR; /* LAW */ + break; + + case 036: /* OPR, dir */ + skp = 0; /* assume no skip */ + switch ((IR >> 6) & 017) { /* decode IR<8:11> */ + case 0: /* nop */ + break; + case 1: /* SMA */ + if ((LAC & SIGN) != 0) skp = 1; + break; + case 2: /* SZA */ + if ((LAC & DMASK) == 0) skp = 1; + break; + case 3: /* SZA | SMA */ + if (((LAC & DMASK) == 0) || ((LAC & SIGN) != 0)) + skp = 1; + break; + case 4: /* SNL */ + if (LAC >= LINK) skp = 1; + break; + case 5: /* SNL | SMA */ + if (LAC >= SIGN) skp = 1; + break; + case 6: /* SNL | SZA */ + if ((LAC >= LINK) || (LAC == 0)) skp = 1; + break; + case 7: /* SNL | SZA | SMA */ + if ((LAC >= SIGN) || (LAC == 0)) skp = 1; + break; + case 010: /* SKP */ + skp = 1; + break; + case 011: /* SPA */ + if ((LAC & SIGN) == 0) skp = 1; + break; + case 012: /* SNA */ + if ((LAC & DMASK) != 0) skp = 1; + break; + case 013: /* SNA & SPA */ + if (((LAC & DMASK) != 0) && ((LAC & SIGN) == 0)) + skp = 1; + break; + case 014: /* SZL */ + if (LAC < LINK) skp = 1; + break; + case 015: /* SZL & SPA */ + if (LAC < SIGN) skp = 1; + break; + case 016: /* SZL & SNA */ + if ((LAC < LINK) && (LAC != 0)) skp = 1; + break; + case 017: /* SZL & SNA & SPA */ + if ((LAC < SIGN) && (LAC != 0)) skp = 1; + break; + } /* end switch skips */ + + switch (((IR >> 9) & 014) | (IR & 03)) { /* IR<5:6,16:17> */ + case 0: /* NOP */ + break; + case 1: /* CMA */ + LAC = LAC ^ DMASK; + break; + case 2: /* CML */ + LAC = LAC ^ LINK; + break; + case 3: /* CML CMA */ + LAC = LAC ^ LACMASK; + break; + case 4: /* CLL */ + LAC = LAC & DMASK; + break; + case 5: /* CLL CMA */ + LAC = (LAC & DMASK) ^ DMASK; + break; + case 6: /* CLL CML = STL */ + LAC = LAC | LINK; + break; + case 7: /* CLL CML CMA */ + LAC = (LAC | LINK) ^ DMASK; + break; + case 010: /* CLA */ + LAC = LAC & LINK; + break; + case 011: /* CLA CMA = STA */ + LAC = LAC | DMASK; + break; + case 012: /* CLA CML */ + LAC = (LAC & LINK) ^ LINK; + break; + case 013: /* CLA CML CMA */ + LAC = (LAC | DMASK) ^ LINK; + break; + case 014: /* CLA CLL */ + LAC = 0; + break; + case 015: /* CLA CLL CMA */ + LAC = DMASK; + break; + case 016: /* CLA CLL CML */ + LAC = LINK; + break; + case 017: /* CLA CLL CML CMA */ + LAC = LACMASK; + break; + } /* end decode */ + + if (IR & 0000004) { /* OAS */ +#if defined (PDP9) || defined (PDP15) + if (usmd) prvn = trap_pending = 1; /* trap if usmd */ + else if (!api_usmd) /* nop if api_usmd */ +#endif + LAC = LAC | SR; + } + + switch (((IR >> 8) & 04) | ((IR >> 3) & 03)) { /* decode IR<7,13:14> */ + case 1: /* RAL */ + LAC = ((LAC << 1) | (LAC >> 18)) & LACMASK; + break; + case 2: /* RAR */ + LAC = ((LAC >> 1) | (LAC << 18)) & LACMASK; + break; + case 3: /* RAL RAR */ +#if defined (PDP15) /* PDP-15 */ + LAC = (LAC + 1) & LACMASK; /* IAC */ +#else /* PDP-4,-7,-9 */ + reason = stop_inst; /* undefined */ +#endif + break; + case 5: /* RTL */ + LAC = ((LAC << 2) | (LAC >> 17)) & LACMASK; + break; + case 6: /* RTR */ + LAC = ((LAC >> 2) | (LAC << 17)) & LACMASK; + break; + case 7: /* RTL RTR */ +#if defined (PDP15) /* PDP-15 */ + LAC = ((LAC >> 9) & 0777) | ((LAC & 0777) << 9) | + (LAC & LINK); /* BSW */ +#else /* PDP-4,-7,-9 */ + reason = stop_inst; /* undefined */ +#endif + break; + } /* end switch rotate */ + + if (IR & 0000040) { /* HLT */ + if (usmd) prvn = trap_pending = 1; /* trap if usmd */ + else if (!api_usmd) reason = STOP_HALT; /* nop if api_usmd */ + } + if (skp) PC = Incr_addr (PC); /* if skip, inc PC */ + break; /* end OPR */ + +/* EAE: opcode 64 + + The EAE is microprogrammed to execute variable length signed and + unsigned shift, multiply, divide, and normalize. Most commands are + controlled by a six bit step counter (SC). In the hardware, the step + counter is complemented on load and then counted up to zero; timing + guarantees an initial increment, which completes the two's complement + load. In the simulator, the SC is loaded normally and then counted + down to zero; the read SC command compensates. */ + + case 033: case 032: /* EAE */ + if (cpu_unit.flags & UNIT_NOEAE) break; /* disabled? */ + if (IR & 0020000) /* IR<4>? AC0 to L */ + LAC = ((LAC << 1) & LINK) | (LAC & DMASK); + if (IR & 0010000) MQ = 0; /* IR<5>? clear MQ */ + if ((IR & 0004000) && (LAC & SIGN)) /* IR<6> and minus? */ + eae_ac_sign = LINK; /* set eae_ac_sign */ + else eae_ac_sign = 0; /* if not, unsigned */ + if (IR & 0002000) MQ = (MQ | LAC) & DMASK; /* IR<7>? or AC */ + else if (eae_ac_sign) LAC = LAC ^ DMASK; /* if not, |AC| */ + if (IR & 0001000) LAC = LAC & LINK; /* IR<8>? clear AC */ + link_init = LAC & LINK; /* link temporary */ + fill = link_init? DMASK: 0; /* fill = link */ + esc = IR & 077; /* get eff SC */ + switch ((IR >> 6) & 07) { /* case on IR<9:11> */ + + case 0: /* setup */ + if (IR & 04) MQ = MQ ^ DMASK; /* IR<15>? ~MQ */ + if (IR & 02) LAC = LAC | MQ; /* IR<16>? or MQ */ + if (IR & 01) LAC = LAC | ((-SC) & 077); /* IR<17>? or SC */ + break; + +/* Multiply uses a shift and add algorithm. The PDP-15, unlike prior + implementations, factors IR<6> (signed multiply) into the calculation + of the result sign. */ + + case 1: /* multiply */ + if (Read (PC, &MB, FE)) break; /* get next word */ + PC = Incr_addr (PC); /* increment PC */ + if (eae_ac_sign) MQ = MQ ^ DMASK; /* EAE AC sign? ~MQ */ + LAC = LAC & DMASK; /* clear link */ + SC = esc; /* init SC */ + do { /* loop */ + if (MQ & 1) LAC = LAC + MB; /* MQ<17>? add */ + MQ = (MQ >> 1) | ((LAC & 1) << 17); + LAC = LAC >> 1; /* shift AC'MQ right */ + SC = (SC - 1) & 077; /* decrement SC */ + } while (SC != 0); /* until SC = 0 */ +#if defined (PDP15) + if ((IR & 0004000) && (eae_ac_sign ^ link_init)) { +#else + if (eae_ac_sign ^ link_init) { /* result negative? */ +#endif + LAC = LAC ^ DMASK; + MQ = MQ ^ DMASK; + } + break; + +/* Divide uses a non-restoring divide. Divide uses a subtract and shift + algorithm. The quotient is generated in true form. The PDP-15, unlike + prior implementations, factors IR<6> (signed multiply) into the calculation + of the result sign. */ + + case 3: /* divide */ + if (Read (PC, &MB, FE)) break; /* get next word */ + PC = Incr_addr (PC); /* increment PC */ + if (eae_ac_sign) MQ = MQ ^ DMASK; /* EAE AC sign? ~MQ */ + if ((LAC & DMASK) >= MB) { /* overflow? */ + LAC = (LAC - MB) | LINK; /* set link */ + break; + } + LAC = LAC & DMASK; /* clear link */ + t = 0; /* init loop */ + SC = esc; /* init SC */ + do { /* loop */ + if (t) LAC = (LAC + MB) & LACMASK; + else LAC = (LAC - MB) & LACMASK; + t = (LAC >> 18) & 1; /* quotient bit */ + if (SC > 1) LAC = /* skip if last */ + ((LAC << 1) | (MQ >> 17)) & LACMASK; + MQ = ((MQ << 1) | (t ^ 1)) & DMASK; /* shift in quo bit */ + SC = (SC - 1) & 077; /* decrement SC */ + } while (SC != 0); /* until SC = 0 */ + if (t) LAC = (LAC + MB) & LACMASK; + if (eae_ac_sign) LAC = LAC ^ DMASK; /* sgn rem = sgn divd */ +#if defined (PDP15) + if ((IR & 0004000) && (eae_ac_sign ^ link_init)) +#else + if (eae_ac_sign ^ link_init) /* result negative? */ +#endif + MQ = MQ ^ DMASK; + break; + +/* EAE shifts, whether left or right, fill from the link. If the + operand sign has been copied to the link, this provides correct + sign extension for one's complement numbers. */ + + case 4: /* normalize */ +#if defined (PDP15) + if (!usmd) ion_defer = 2; /* free instructions */ +#endif + for (SC = esc; ((LAC & SIGN) == ((LAC << 1) & SIGN)); ) { + LAC = (LAC << 1) | ((MQ >> 17) & 1); + MQ = (MQ << 1) | (link_init >> 18); + SC = (SC - 1) & 077; + if (SC == 0) break; + } + LAC = link_init | (LAC & DMASK); /* trim AC, restore L */ + MQ = MQ & DMASK; /* trim MQ */ + SC = SC & 077; /* trim SC */ + break; + + case 5: /* long right shift */ + if (esc < 18) { + MQ = ((LAC << (18 - esc)) | (MQ >> esc)) & DMASK; + LAC = ((fill << (18 - esc)) | (LAC >> esc)) & LACMASK; + } + else { + if (esc < 36) MQ = + ((fill << (36 - esc)) | (LAC >> (esc - 18))) & DMASK; + else MQ = fill; + LAC = link_init | fill; + } + SC = 0; /* clear step count */ + break; + + case 6: /* long left shift */ + if (esc < 18) { + LAC = link_init | + (((LAC << esc) | (MQ >> (18 - esc))) & DMASK); + MQ = ((MQ << esc) | (fill >> (18 - esc))) & DMASK; + } + else { + if (esc < 36) LAC = link_init | + (((MQ << (esc - 18)) | (fill >> (36 - esc))) & DMASK); + else LAC = link_init | fill; + MQ = fill; + } + SC = 0; /* clear step count */ + break; + + case 7: /* AC left shift */ + if (esc < 18) LAC = link_init | + (((LAC << esc) | (fill >> (18 - esc))) & DMASK); + else LAC = link_init | fill; + SC = 0; /* clear step count */ + break; + } /* end switch IR */ + break; /* end case EAE */ + +/* PDP-15 index operates: opcode 72 */ + + case 035: /* index operates */ + +#if defined (PDP15) + t = (IR & 0400)? (IR | 0777000): (IR & 0377); /* sext immediate */ + switch ((IR >> 9) & 017) { /* case on IR<5:8> */ + case 001: /* PAX */ + XR = LAC & DMASK; + break; + case 002: /* PAL */ + LR = LAC & DMASK; + break; + case 003: /* AAC */ + LAC = (LAC & LINK) | ((LAC + t) & DMASK); + break; + case 004: /* PXA */ + LAC = (LAC & LINK) | XR; + break; + case 005: /* AXS */ + XR = (XR + t) & DMASK; + if (SEXT (XR) >= SEXT (LR)) PC = Incr_addr (PC); + break; + case 006: /* PXL */ + LR = XR; + break; + case 010: /* PLA */ + LAC = (LAC & LINK) | LR; + break; + case 011: /* PLX */ + XR = LR; + break; + case 014: /* CLAC */ + LAC = LAC & LINK; + break; + case 015: /* CLX */ + XR = 0; + break; + case 016: /* CLLR */ + LR = 0; + break; + case 017: /* AXR */ + XR = (XR + t) & DMASK; + break; + } /* end switch IR */ + break; /* end case */ +#endif + +/* IOT: opcode 70 + + The 18b PDP's have different definitions of various control IOT's. + + IOT PDP-4 PDP-7 PDP-9 PDP-15 + + 700002 IOF IOF IOF IOF + 700022 undefined undefined undefined ORMM (XVM) + 700042 ION ION ION ION + 700024 undefined undefined undefined LDMM (XVM) + 700062 undefined ITON undefined undefined + 701701 undefined undefined MPSK MPSK + 701741 undefined undefined MPSNE MPSNE + 701702 undefined undefined MPCV MPCV + 701722 undefined undefined undefined MPRC (XVM) + 701742 undefined undefined MPEU MPEU + 701704 undefined undefined MPLD MPLD + 701724 undefined undefined undefined MPLR (KT15, XVM) + 701744 undefined undefined MPCNE MPCNE + 701764 undefined undefined undefined IPFH (XVM) + 703201 undefined undefined PFSF PFSF + 703301 undefined TTS TTS TTS + 703341 undefined SKP7 SKP7 SPCO + 703302 undefined CAF CAF CAF + 703304 undefined undefined DBK DBK + 703344 undefined undefined DBR DBR + 705501 undefined undefined SPI SPI + 705521 undefined undefined undefined ENB + 705502 undefined undefined RPL RPL + 705522 undefined undefined undefined INH + 705504 undefined undefined ISA ISA + 707701 undefined SEM SEM undefined + 707741 undefined undefined undefined SKP15 + 707761 undefined undefined undefined SBA + 707702 undefined EEM EEM undefined + 707742 undefined EMIR EMIR RES + 707762 undefined undefined undefined DBA + 707704 undefined LEM LEM undefined + 707764 undefined undefined undefined EBA */ + + case 034: /* IOT */ +#if defined (PDP15) + if (IR & 0010000) { /* floating point? */ + reason = fp15 (IR); /* process */ + break; + } +#endif + if ((api_usmd | usmd) && /* user, not XVM UIOT? */ + (!XVM || !(MMR & MM_UIOT))) { + if (usmd) prvn = trap_pending = 1; /* trap if user */ + break; /* nop if api_usmd */ + } + device = (IR >> 6) & 077; /* device = IR<6:11> */ + pulse = IR & 067; /* pulse = IR<12:17> */ + if (IR & 0000010) LAC = LAC & LINK; /* clear AC? */ + iot_data = LAC & DMASK; /* AC unchanged */ + +/* PDP-4 system IOT's */ + +#if defined (PDP4) + switch (device) { /* decode IR<6:11> */ + + case 0: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = ion_defer = 1; /* ION */ + else iot_data = clk (device, pulse, iot_data); + break; +#endif + +/* PDP-7 system IOT's */ + +#if defined (PDP7) + switch (device) { /* decode IR<6:11> */ + + case 0: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = ion_defer = 1; /* ION */ + else if (pulse == 062) /* ITON */ + usmd = usmd_buf = ion = ion_defer = 1; + else iot_data = clk (device, pulse, iot_data); + break; + + case 033: /* CPU control */ + if ((pulse == 001) || (pulse == 041)) PC = Incr_addr (PC); + else if (pulse == 002) reset_all (1); /* CAF - skip CPU */ + break; + + case 077: /* extended memory */ + if ((pulse == 001) && memm) PC = Incr_addr (PC); + else if (pulse == 002) memm = 1; /* EEM */ + else if (pulse == 042) /* EMIR */ + memm = emir_pending = 1; /* ext on, restore */ + else if (pulse == 004) memm = 0; /* LEM */ + break; +#endif + +/* PDP-9 system IOT's */ + +#if defined (PDP9) + ion_defer = 1; /* delay interrupts */ + usmd_defer = 1; /* defer load user */ + switch (device) { /* decode IR<6:11> */ + + case 000: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = 1; /* ION */ + else iot_data = clk (device, pulse, iot_data); + break; + + case 017: /* mem protection */ + if (PROT) { /* enabled? */ + if ((pulse == 001) && prvn) /* MPSK */ + PC = Incr_addr (PC); + else if ((pulse == 041) && nexm) /* MPSNE */ + PC = Incr_addr (PC); + else if (pulse == 002) prvn = 0; /* MPCV */ + else if (pulse == 042) usmd_buf = 1; /* MPEU */ + else if (pulse == 004) /* MPLD */ + BR = LAC & BRMASK; + else if (pulse == 044) nexm = 0; /* MPCNE */ + } + else reason = stop_inst; + break; + + case 032: /* power fail */ + if ((pulse == 001) && (TST_INT (PWRFL))) + PC = Incr_addr (PC); + break; + + case 033: /* CPU control */ + if ((pulse == 001) || (pulse == 041)) PC = Incr_addr (PC); + else if (pulse == 002) { /* CAF */ + reset_all (1); /* reset all exc CPU */ + cpu_caf (); /* CAF to CPU */ + } + else if (pulse == 044) rest_pending = 1; /* DBR */ + if (((cpu_unit.flags & UNIT_NOAPI) == 0) && (pulse & 004)) { + int32 t = api_ffo[api_act & 0377]; + api_act = api_act & ~(API_ML0 >> t); + } + break; + + case 055: /* API control */ + if (cpu_unit.flags & UNIT_NOAPI) reason = stop_inst; + else if (pulse == 001) { /* SPI */ + if (((LAC & SIGN) && api_enb) || + ((LAC & 0377) > api_act)) + iot_data = iot_data | IOT_SKP; + } + else if (pulse == 002) { /* RPL */ + iot_data = iot_data | (api_enb << 17) | + (api_req << 8) | api_act; + } + else if (pulse == 004) { /* ISA */ + api_enb = (iot_data & SIGN)? 1: 0; + api_req = api_req | ((LAC >> 8) & 017); + api_act = api_act | (LAC & 0377); + } + break; + + case 077: /* extended memory */ + if ((pulse == 001) && memm) PC = Incr_addr (PC); + else if (pulse == 002) memm = 1; /* EEM */ + else if (pulse == 042) /* EMIR */ + memm = emir_pending = 1; /* ext on, restore */ + else if (pulse == 004) memm = 0; /* LEM */ + break; +#endif + +/* PDP-15 system IOT's - includes "re-entrancy ECO" ENB/INH as standard */ + +#if defined (PDP15) + ion_defer = 1; /* delay interrupts */ + usmd_defer = 1; /* defer load user */ + switch (device) { /* decode IR<6:11> */ + + case 000: /* CPU and clock */ + if (pulse == 002) ion = 0; /* IOF */ + else if (pulse == 042) ion = 1; /* ION */ + else if (XVM && (pulse == 022)) /* ORMM/RDMM */ + iot_data = MMR; + else if (XVM && (pulse == 024)) /* LDMM */ + MMR = iot_data; + else iot_data = clk (device, pulse, iot_data); + break; + + case 017: /* mem protection */ + if (PROT) { /* enabled? */ + t = XVM? BRMASK_XVM: BRMASK; + if ((pulse == 001) && prvn) /* MPSK */ + PC = Incr_addr (PC); + else if ((pulse == 041) && nexm) /* MPSNE */ + PC = Incr_addr (PC); + else if (pulse == 002) prvn = 0; /* MPCV */ + else if (pulse == 042) /* MPEU */ + usmd_buf = 1; + else if (XVM && (pulse == 062)) /* RDCLK */ + iot_data = clk_task_upd (TRUE); + else if (pulse == 004) BR = LAC & t; /* MPLD */ + else if (RELOC && (pulse == 024)) /* MPLR */ + RR = LAC & t; + else if (pulse == 044) nexm = 0; /* MPCNE */ + } + else reason = stop_inst; + break; + + case 032: /* power fail */ + if ((pulse == 001) && (TST_INT (PWRFL))) + PC = Incr_addr (PC); + break; + + case 033: /* CPU control */ + if ((pulse == 001) || (pulse == 041)) PC = Incr_addr (PC); + else if (pulse == 002) { /* CAF */ + reset_all (2); /* reset all exc CPU, FP15 */ + cpu_caf (); /* CAF to CPU */ + } + else if (pulse == 044) rest_pending = 1; /* DBR */ + if (((cpu_unit.flags & UNIT_NOAPI) == 0) && (pulse & 004)) { + int32 t = api_ffo[api_act & 0377]; + api_act = api_act & ~(API_ML0 >> t); + } + break; + + case 055: /* API control */ + if (cpu_unit.flags & UNIT_NOAPI) reason = stop_inst; + else if (pulse == 001) { /* SPI */ + if (((LAC & SIGN) && api_enb) || + ((LAC & 0377) > api_act)) + iot_data = iot_data | IOT_SKP; + } + else if (pulse == 002) { /* RPL */ + iot_data = iot_data | (api_enb << 17) | + (api_req << 8) | api_act; + } + else if (pulse == 004) { /* ISA */ + api_enb = (iot_data & SIGN)? 1: 0; + api_req = api_req | ((LAC >> 8) & 017); + api_act = api_act | (LAC & 0377); + } + else if (pulse == 021) ion_inh = 0; /* ENB */ + else if (pulse == 022) ion_inh = 1; /* INH */ + break; + + case 077: /* bank addressing */ + if ((pulse == 041) || ((pulse == 061) && memm)) + PC = Incr_addr (PC); /* SKP15, SBA */ + else if (pulse == 042) rest_pending = 1; /* RES */ + else if (pulse == 062) memm = 0; /* DBA */ + else if (pulse == 064) memm = 1; /* EBA */ + break; +#endif + +/* IOT, continued */ + + default: /* devices */ + if (dev_tab[device]) /* defined? */ + iot_data = dev_tab[device] (device, pulse, iot_data); + else reason = stop_inst; /* stop on flag */ + break; + } /* end switch device */ + + LAC = LAC | (iot_data & DMASK); + if (iot_data & IOT_SKP) PC = Incr_addr (PC); + if (iot_data >= IOT_REASON) reason = iot_data >> IOT_V_REASON; + api_int = api_eval (&int_pend); /* eval API */ + break; /* end case IOT */ + } /* end switch opcode */ + + api_usmd = 0; /* API cycle over */ + last_IR = IR; /* save IR for next */ + } /* end while */ + +/* Simulation halted */ + +iors = upd_iors (); /* get IORS */ +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Evaluate API */ + +int32 api_eval (int32 *pend) +{ +int32 i, hi; + +*pend = 0; /* assume no intr */ +#if defined (PDP15) /* PDP15 only */ +if (ion_inh) return 0; /* inhibited? */ +#endif +for (i = 0; i < API_HLVL+1; i++) { /* any intr? */ + if (int_hwre[i]) *pend = 1; + } +if (api_enb == 0) return 0; /* off? no req */ +api_req = api_req & ~(API_ML0|API_ML1|API_ML2|API_ML3); /* clr req<0:3> */ +for (i = 0; i < API_HLVL; i++) { /* loop thru levels */ + if (int_hwre[i]) /* req on level? */ + api_req = api_req | (API_ML0 >> i); /* set api req */ + } +hi = api_ffo[api_req & 0377]; /* find hi req */ +if (hi < api_ffo[api_act & 0377]) return (hi + 1); +return 0; +} + +/* Process IORS instruction */ + +int32 upd_iors (void) +{ +int32 d, p; + +d = (ion? IOS_ION: 0); /* ION */ +for (p = 0; dev_iors[p] != NULL; p++) /* loop thru table */ + d = d | dev_iors[p](); /* OR in results */ +return d; +} + +#if defined (PDP4) || defined (PDP7) + +/* Read, write, indirect, increment routines + On the PDP-4 and PDP-7, + There are autoincrement locations in every field. If a field + does not exist, it is impossible to generate an + autoincrement reference (all instructions are CAL). + Indirect addressing range is determined by extend mode. + JMP I with EMIR pending can only clear extend + There is no memory protection, nxm reads zero and ignores writes. */ + +t_stat Read (int32 ma, int32 *dat, int32 cyc) +{ +ma = ma & AMASK; +if (MEM_ADDR_OK (ma)) *dat = M[ma] & DMASK; +else *dat = 0; +return MM_OK; +} + +t_stat Write (int32 ma, int32 dat, int32 cyc) +{ +ma = ma & AMASK; +if (MEM_ADDR_OK (ma)) M[ma] = dat & DMASK; +return MM_OK; +} + +t_stat Ia (int32 ma, int32 *ea, t_bool jmp) +{ +int32 t; +t_stat sta = MM_OK; + +if ((ma & B_DAMASK & ~07) == 010) { /* autoindex? */ + Read (ma, &t, DF); /* add 1 before use */ + t = (t + 1) & DMASK; + sta = Write (ma, t, DF); + } +else sta = Read (ma, &t, DF); /* fetch indirect */ +if (jmp) { /* jmp i? */ + if (emir_pending && (((t >> 16) & 1) == 0)) memm = 0; + emir_pending = rest_pending = 0; + } +if (memm) *ea = t & IAMASK; /* extend? 15b ia */ +else *ea = (ma & B_EPCMASK) | (t & B_DAMASK); /* bank-rel ia */ +return sta; +} + +int32 Incr_addr (int32 ma) +{ +return ((ma & B_EPCMASK) | ((ma + 1) & B_DAMASK)); +} + +int32 Jms_word (int32 t) +{ +return (((LAC & LINK) >> 1) | ((memm & 1) << 16) | + ((t & 1) << 15) | (PC & IAMASK)); +} + +#endif + +#if defined (PDP9) + +/* Read, write, indirect, increment routines + On the PDP-9, + The autoincrement registers are in field zero only. Regardless + of extend mode, indirect addressing through 00010-00017 + will access absolute locations 00010-00017. + Indirect addressing range is determined by extend mode. If + extend mode is off, and autoincrementing is used, the + resolved address is in bank 0 (KG09B maintenance manual). + JMP I with EMIR pending can only clear extend + JMP I with DBK pending restores L, user mode, extend mode + Memory protection is implemented for foreground/background operation. */ + +t_stat Read (int32 ma, int32 *dat, int32 cyc) +{ +ma = ma & AMASK; +if (usmd) { /* user mode? */ + if (!MEM_ADDR_OK (ma)) { /* nxm? */ + nexm = prvn = trap_pending = 1; /* set flags, trap */ + *dat = 0; + return MM_ERR; + } + if ((cyc != DF) && (ma < BR)) { /* boundary viol? */ + prvn = trap_pending = 1; /* set flag, trap */ + *dat = 0; + return MM_ERR; + } + } +if (MEM_ADDR_OK (ma)) *dat = M[ma] & DMASK; /* valid mem? ok */ +else { + *dat = 0; /* set flag, no trap */ + nexm = 1; + } +return MM_OK; +} + +t_stat Write (int32 ma, int32 dat, int32 cyc) +{ +ma = ma & AMASK; +if (usmd) { + if (!MEM_ADDR_OK (ma)) { /* nxm? */ + nexm = prvn = trap_pending = 1; /* set flags, trap */ + return MM_ERR; + } + if ((cyc != DF) && (ma < BR)) { /* boundary viol? */ + prvn = trap_pending = 1; /* set flag, trap */ + return MM_ERR; + } + } +if (MEM_ADDR_OK (ma)) M[ma] = dat & DMASK; /* valid mem? ok */ +else nexm = 1; /* set flag, no trap */ +return MM_OK; +} + +t_stat Ia (int32 ma, int32 *ea, t_bool jmp) +{ +int32 t; +t_stat sta = MM_OK; + +if ((ma & B_DAMASK & ~07) == 010) { /* autoindex? */ + ma = ma & 017; /* always in bank 0 */ + Read (ma, &t, DF); /* +1 before use */ + t = (t + 1) & DMASK; + sta = Write (ma, t, DF); + } +else sta = Read (ma, &t, DF); +if (jmp) { /* jmp i? */ + if (emir_pending && (((t >> 16) & 1) == 0)) memm = 0; + if (rest_pending) { /* restore pending? */ + LAC = ((t << 1) & LINK) | (LAC & DMASK); /* restore L */ + memm = (t >> 16) & 1; /* restore extend */ + usmd = usmd_buf = (t >> 15) & 1; /* restore user */ + } + emir_pending = rest_pending = 0; + } +if (memm) *ea = t & IAMASK; /* extend? 15b ia */ +else *ea = (ma & B_EPCMASK) | (t & B_DAMASK); /* bank-rel ia */ +return sta; +} + +int32 Incr_addr (int32 ma) +{ +return ((ma & B_EPCMASK) | ((ma + 1) & B_DAMASK)); +} + +int32 Jms_word (int32 t) +{ +return (((LAC & LINK) >> 1) | ((memm & 1) << 16) | + ((t & 1) << 15) | (PC & IAMASK)); +} + +#endif + +#if defined (PDP15) + +/* Read, write, indirect, increment routines + On the PDP-15, + The autoincrement registers are in page zero only. Regardless + of bank mode, indirect addressing through 00010-00017 + will access absolute locations 00010-00017. + Indirect addressing range is determined by autoincrementing. + Any indirect can trigger a restore. + Memory protection is implemented for foreground/background operation. + Read and write mask addresses to 17b except for XVM systems */ + +t_stat Read (int32 ma, int32 *dat, int32 cyc) +{ +int32 pa; + +if (usmd) { /* user mode? */ + if (XVM) pa = RelocXVM (ma, REL_R); /* XVM relocation? */ + else if (RELOC) pa = Reloc15 (ma, REL_R); /* PDP-15 relocation? */ + else pa = Prot15 (ma, cyc == FE); /* PDP-15 prot, fetch only */ + if (pa < 0) { /* error? */ + *dat = 0; + return MM_ERR; + } + } +else pa = ma & AMASK; /* no prot or reloc */ +if (MEM_ADDR_OK (pa)) *dat = M[pa] & DMASK; /* valid mem? ok */ +else { + nexm = 1; /* set flag, no trap */ + *dat = 0; + } +return MM_OK; +} + +t_stat Write (int32 ma, int32 dat, int32 cyc) +{ +int32 pa; + +if (usmd) { /* user mode? */ + if (XVM) pa = RelocXVM (ma, REL_W); /* XVM relocation? */ + else if (RELOC) pa = Reloc15 (ma, REL_W); /* PDP-15 relocation? */ + else pa = Prot15 (ma, cyc != DF); /* PDP-15 prot, !defer */ + if (pa < 0) return MM_ERR; /* error? */ + } +else pa = ma & AMASK; /* no prot or reloc */ +if (MEM_ADDR_OK (pa)) M[pa] = dat & DMASK; /* valid mem? ok */ +else nexm = 1; /* set flag, no trap */ +return MM_OK; +} + +/* XVM will do 18b defers if user_mode and G_Mode != 0 */ + +t_stat Ia (int32 ma, int32 *ea, t_bool jmp) +{ +int32 gmode, t; +int32 damask = memm? B_DAMASK: P_DAMASK; +static const int32 g_mask[4] = { MM_G_W0, MM_G_W1, MM_G_W2, MM_G_W3 }; +t_stat sta = MM_OK; + +if ((ma & damask & ~07) == 010) { /* autoincrement? */ + ma = ma & 017; /* always in bank 0 */ + Read (ma, &t, DF); /* +1 before use */ + t = (t + 1) & DMASK; + sta = Write (ma, t, DF); + } +else sta = Read (ma, &t, DF); +if (rest_pending) { /* restore pending? */ + LAC = ((t << 1) & LINK) | (LAC & DMASK); /* restore L */ + memm = (t >> 16) & 1; /* restore bank */ + usmd = usmd_buf = (t >> 15) & 1; /* restore user */ + emir_pending = rest_pending = 0; + } +gmode = MM_GETGM (MMR); /* get G_mode */ +if (usmd && XVM && gmode) /* XVM user mode? */ + *ea = t & g_mask[gmode]; /* mask ia to size */ +else if ((ma & damask & ~07) == 010) *ea = t & DMASK; /* autoindex? */ +else *ea = (PC & BLKMASK) | (t & IAMASK); /* within 32K */ +return sta; +} + +t_stat Incr_addr (int32 ma) +{ +if (memm) return ((ma & B_EPCMASK) | ((ma + 1) & B_DAMASK)); +return ((ma & P_EPCMASK) | ((ma + 1) & P_DAMASK)); +} + +/* XVM will store all 18b of PC if user mode and G_mode != 0 */ + +int32 Jms_word (int32 t) +{ +if (usmd && XVM && (MMR & MM_GM)) return PC; +return (((LAC & LINK) >> 1) | ((memm & 1) << 16) | + ((t & 1) << 15) | (PC & IAMASK)); +} + +/* PDP-15 protection (KM15 option) */ + +int32 Prot15 (int32 ma, t_bool bndchk) +{ +ma = ma & AMASK; /* 17b addressing */ +if (!MEM_ADDR_OK (ma)) { /* nxm? */ + nexm = prvn = trap_pending = 1; /* set flags, trap */ + return -1; + } +if (bndchk && (ma < BR)) { /* boundary viol? */ + prvn = trap_pending = 1; /* set flag, trap */ + return -1; + } +return ma; /* no relocation */ +} + +/* PDP-15 relocation and protection (KT15 option) */ + +int32 Reloc15 (int32 ma, int32 rc) +{ +int32 pa; + +ma = ma & AMASK; /* 17b addressing */ +if (ma > (BR | 0377)) { /* boundary viol? */ + if (rc != REL_C) prvn = trap_pending = 1; /* set flag, trap */ + return -1; + } +pa = (ma + RR) & AMASK; /* relocate address */ +if (!MEM_ADDR_OK (pa)) { /* nxm? */ + if (rc != REL_C) nexm = prvn = trap_pending = 1; /* set flags, trap */ + return -1; + } +return pa; +} + +/* XVM relocation and protection option */ + +int32 RelocXVM (int32 ma, int32 rc) +{ +int32 pa, gmode, slr; +static const int32 g_base[4] = { MM_G_B0, MM_G_B1, MM_G_B2, MM_G_B3 }; +static const int32 slr_lnt[4] = { MM_SLR_L0, MM_SLR_L1, MM_SLR_L2, MM_SLR_L3 }; + +gmode = MM_GETGM (MMR); /* get G_mode */ +slr = MM_GETSLR (MMR); /* get segment length */ +if (MMR & MM_RDIS) pa = ma; /* reloc disabled? */ +else if ((MMR & MM_SH) && /* shared enabled and */ + (ma >= g_base[gmode]) && /* >= shared base and */ + (ma < (g_base[gmode] + slr_lnt[slr]))) { /* < shared end? */ + if (ma & 017400) { /* ESAS? */ + if ((rc == REL_W) && (MMR & MM_WP)) { /* write and protected? */ + prvn = trap_pending = 1; /* set flag, trap */ + return -1; + } + pa = (((MMR & MM_SBR_MASK) << 8) + ma) & DMASK; /* ESAS reloc */ + } + else pa = RR + (ma & 0377); /* no, ISAS reloc */ + } +else { + if (ma > (BR | 0377)) { /* normal reloc, viol? */ + if (rc != REL_C) prvn = trap_pending = 1; /* set flag, trap */ + return -1; + } + pa = (RR + ma) & DMASK; /* relocate address */ + } +if (!MEM_ADDR_OK (pa)) { /* nxm? */ + if (rc != REL_C) nexm = prvn = trap_pending = 1; /* set flags, trap */ + return -1; + } +return pa; +} + +#endif + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +LAC = 0; +MQ = 0; +SC = 0; +eae_ac_sign = 0; +ion = ion_defer = ion_inh = 0; +CLR_INT (PWRFL); +api_enb = api_req = api_act = 0; +BR = 0; +RR = 0; +MMR = 0; +usmd = usmd_buf = usmd_defer = 0; +memm = memm_init; +nexm = prvn = trap_pending = 0; +emir_pending = rest_pending = 0; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* CAF routine (CPU reset isn't called by CAF) */ + +void cpu_caf (void) +{ +api_enb = api_req = api_act = 0; /* reset API system */ +nexm = prvn = trap_pending = 0; /* reset MM system */ +usmd = usmd_buf = usmd_defer = 0; +MMR = 0; +return; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +#if defined (PDP15) +if (usmd && (sw & SWMASK ('V'))) { + if (XVM) addr = RelocXVM (addr, REL_C); + else if (RELOC) addr = Reloc15 (addr, REL_C); + if ((int32) addr < 0) return STOP_MME; + } +#endif +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +#if defined (PDP15) +if (usmd && (sw & SWMASK ('V'))) { + if (XVM) addr = RelocXVM (addr, REL_C); + else if (RELOC) addr = Reloc15 (addr, REL_C); + if ((int32) addr < 0) return STOP_MME; + } +#endif +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & DMASK; +return SCPE_OK; +} + +/* Change memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* Change device number for a device */ + +t_stat set_devno (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newdev; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newdev = get_uint (cptr, 8, DEV_MAX - 1, &r); /* get new */ +if ((r != SCPE_OK) || (newdev == dibp->dev)) return r; +dibp->dev = newdev; /* store */ +return SCPE_OK; +} + +/* Show device number for a device */ + +t_stat show_devno (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +fprintf (st, "devno=%02o", dibp->dev); +if (dibp->num > 1) fprintf (st, "-%2o", dibp->dev + dibp->num - 1); +return SCPE_OK; +} + +/* CPU device handler - should never get here! */ + +int32 bad_dev (int32 dev, int32 pulse, int32 AC) +{ +return (SCPE_IERR << IOT_V_REASON) | AC; /* broken! */ +} + +/* Build device dispatch table */ + +t_bool build_dev_tab (void) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, j, p; +static const uint8 std_dev[] = +#if defined (PDP4) + { 000 }; +#elif defined (PDP7) + { 000, 033, 077 }; +#else + { 000, 017, 033, 055, 077 }; +#endif + +for (i = 0; i < DEV_MAX; i++) { /* clr tables */ + dev_tab[i] = NULL; + dev_iors[i] = NULL; + } +for (i = 0; i < ((uint32) sizeof (std_dev)); i++) /* std entries */ + dev_tab[std_dev[i]] = &bad_dev; +for (i = p = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ + if (dibp->iors) dev_iors[p++] = dibp->iors; /* if IORS, add */ + for (j = 0; j < dibp->num; j++) { /* loop thru disp */ + if (dibp->dsp[j]) { /* any dispatch? */ + if (dev_tab[dibp->dev + j]) { /* already filled? */ + printf ("%s device number conflict at %02o\n", + sim_dname (dptr), dibp->dev + j); + if (sim_log) fprintf (sim_log, + "%s device number conflict at %02o\n", + sim_dname (dptr), dibp->dev + j); + return TRUE; + } + dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */ + } /* end if dsp */ + } /* end for j */ + } /* end if enb */ + } /* end for i */ +return FALSE; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 l, j, k, di, lnt; +char *cptr = (char *) desc; +t_value sim_eval[2]; +t_stat r; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC L AC MQ IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(di++) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + l = (h->lac >> 18) & 1; /* link */ + fprintf (st, "%06o %o %06o %06o ", h->pc & AMASK, l, h->lac & DMASK, h->mq); + sim_eval[0] = h->ir; + sim_eval[1] = h->ir1; + if ((fprint_sym (st, h->pc & AMASK, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %06o", h->ir); + } /* end else instruction */ + else if (h->pc & (HIST_API | HIST_PI)) { /* interrupt event? */ + if (h->pc & HIST_PI) /* PI? */ + fprintf (st, "%06o PI LVL 0-4 =", h->pc & AMASK); + else fprintf (st, "%06o API %d LVL 0-4 =", h->pc & AMASK, h->mq); + for (j = API_HLVL; j >= 0; j--) + fprintf (st, " %02o", (h->ir >> (j * HIST_V_LVL)) & HIST_M_LVL); + } + else continue; /* invalid */ + fputc ('\n', st); /* end line */ + } /* end for */ +return SCPE_OK; +} + +/* Record events in history table */ + +void cpu_inst_hist (int32 addr, int32 inst) +{ +t_value word = 0; + +hst[hst_p].pc = addr | HIST_PC; +hst[hst_p].ir = inst; +if (cpu_ex (&word, (addr + 1) & AMASK, &cpu_unit, SWMASK ('V'))) + hst[hst_p].ir1 = 0; +else hst[hst_p].ir1 = word; +hst[hst_p].lac = LAC; +hst[hst_p].mq = MQ; +hst_p = (hst_p + 1); +if (hst_p >= hst_lnt) hst_p = 0; +return; +} + +void cpu_intr_hist (int32 flag, int32 lvl) +{ +int32 j; + +hst[hst_p].pc = PC | flag; +hst[hst_p].ir = 0; +for (j = 0; j < API_HLVL+1; j++) hst[hst_p].ir = + (hst[hst_p].ir << HIST_V_LVL) | (int_hwre[j] & HIST_M_LVL); +hst[hst_p].ir1 = 0; +hst[hst_p].lac = 0; +hst[hst_p].mq = lvl; +hst_p = (hst_p + 1); +if (hst_p >= hst_lnt) hst_p = 0; +return; +} diff --git a/PDP18B/pdp18b_defs.h b/PDP18B/pdp18b_defs.h new file mode 100644 index 0000000..d4e4e90 --- /dev/null +++ b/PDP18B/pdp18b_defs.h @@ -0,0 +1,493 @@ +/* pdp18b_defs.h: 18b PDP simulator definitions + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-06 RMS Added infinite loop stop + 14-Jan-04 RMS Revised IO device call interface + 18-Oct-03 RMS Added DECtape off reel message + 18-Jul-03 RMS Added FP15 support + Added XVM support + Added EAE option for PDP-4 + 25-Apr-03 RMS Revised for extended file support + 04-Feb-03 RMS Added RB09, LP09 support + 22-Nov-02 RMS Added PDP-4 drum support + 05-Oct-02 RMS Added DIB structure + 25-Jul-02 RMS Added PDP-4 DECtape support + 10-Feb-02 RMS Added PDP-7 DECtape support + 25-Nov-01 RMS Revised interrupt structure + 27-May-01 RMS Added second Teletype support + 21-Jan-01 RMS Added DECtape support + 14-Apr-99 RMS Changed t_addr to unsigned + 02-Jan-96 RMS Added fixed head and moving head disks + 31-Dec-95 RMS Added memory management + 19-Mar-95 RMS Added dynamic memory size + + The author gratefully acknowledges the help of Craig St. Clair and + Deb Tevonian in locating archival material about the 18b PDP's, and of + Al Kossow and Max Burnet in making documentation and software available. +*/ + +#ifndef _PDP18B_DEFS_H_ +#define _PDP18B_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Models: only one should be defined + + model memory CPU options I/O options + + PDP4 8K Type 18 EAE Type 65 KSR-28 Teletype (Baudot) + ??Type 16 mem extension integral paper tape reader + Type 75 paper tape punch + integral real time clock + Type 62 line printer (Hollerith) + Type 550/555 DECtape + Type 24 serial drum + + PDP7 32K Type 177 EAE Type 649 KSR-33 Teletype + Type 148 mem extension Type 444 paper tape reader + Type 75 paper tape punch + integral real time clock + Type 647B line printer (sixbit) + Type 550/555 DECtape + Type 24 serial drum + + PDP9 32K KE09A EAE KSR-33 Teletype + KF09A auto pri intr PC09A paper tape reader and punch + KG09B mem extension integral real time clock + KP09A power detection Type 647D/E line printer (sixbit) + KX09A mem protection LP09 line printer (ASCII) + RF09/RS09 fixed head disk + RB09 fixed head disk + TC59 magnetic tape + TC02/TU55 DECtape + LT09A additional Teletypes + + PDP15 128K KE15 EAE KSR-35 Teletype + KA15 auto pri intr PC15 paper tape reader and punch + KF15 power detection KW15 real time clock + KM15 mem protection LP09 line printer + KT15 mem relocation LP15 line printer + FP15 floating point RP15 disk pack + XVM option RF15/RF09 fixed head disk + TC59D magnetic tape + TC15/TU56 DECtape + LT15/LT19 additional Teletypes + + ??Indicates not implemented. The PDP-4 manual refers to a memory + ??extension control; there is no documentation on it. +*/ + +#if !defined (PDP4) && !defined (PDP7) && !defined (PDP9) && !defined (PDP15) +#define PDP15 0 /* default to PDP-15 */ +#endif + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_XCT 4 /* nested XCT's */ +#define STOP_API 5 /* invalid API int */ +#define STOP_NONSTD 6 /* non-std dev num */ +#define STOP_MME 7 /* mem mgt error */ +#define STOP_FPDIS 8 /* fp inst, fpp disabled */ +#define STOP_DTOFF 9 /* DECtape off reel */ +#define STOP_LOOP 10 /* infinite loop */ + +/* Peripheral configuration */ + +#if defined (PDP4) +#define ADDRSIZE 13 +#define KSR28 0 /* Baudot terminal */ +#define TYPE62 0 /* Hollerith printer */ +#define TYPE550 0 /* DECtape */ +#define DRM 0 /* drum */ +#elif defined (PDP7) +#define ADDRSIZE 15 +#define TYPE647 0 /* sixbit printer */ +#define TYPE550 0 /* DECtape */ +#define DRM 0 /* drum */ +#elif defined (PDP9) +#define ADDRSIZE 15 +#define TYPE647 0 /* sixbit printer */ +#define LP09 0 /* ASCII printer */ +#define RB 0 /* fixed head disk */ +#define RF 0 /* fixed head disk */ +#define MTA 0 /* magtape */ +#define TC02 0 /* DECtape */ +#define TTY1 4 /* second Teletype(s) */ +#define BRMASK 0076000 /* bounds mask */ +#elif defined (PDP15) +#define ADDRSIZE 17 +#define LP09 0 /* ASCII printer */ +#define LP15 0 /* DMA printer */ +#define RF 0 /* fixed head disk */ +#define RP 0 /* disk pack */ +#define MTA 0 /* magtape */ +#define TC02 0 /* DECtape */ +#define TTY1 16 /* second Teletype(s) */ +#define BRMASK 0377400 /* bounds mask */ +#define BRMASK_XVM 0777400 /* bounds mask, XVM */ +#endif + +/* Memory */ + +#define AMASK ((1 << ADDRSIZE) - 1) /* address mask */ +#define IAMASK 077777 /* ind address mask */ +#define BLKMASK (AMASK & (~IAMASK)) /* block mask */ +#define MAXMEMSIZE (1 << ADDRSIZE) /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* Instructions */ + +#define I_V_OP 14 /* opcode */ +#define I_M_OP 017 +#define I_V_IND 13 /* indirect */ +#define I_V_IDX 12 /* index */ +#define I_IND (1 << I_V_IND) +#define I_IDX (1 << I_V_IDX) +#define B_DAMASK 017777 /* bank mode address */ +#define B_EPCMASK (AMASK & ~B_DAMASK) +#define P_DAMASK 007777 /* page mode address */ +#define P_EPCMASK (AMASK & ~P_DAMASK) + +/* Memory cycles */ + +#define FE 0 +#define DF 1 +#define RD 2 +#define WR 3 + +/* Memory status codes */ + +#define MM_OK 0 +#define MM_ERR 1 + +/* Memory management relocation checks (PDP-15 KT15 and XVM only) */ + +#define REL_C -1 /* console */ +#define REL_R 0 /* read */ +#define REL_W 1 /* write */ + +/* Architectural constants */ + +#define DMASK 0777777 /* data mask */ +#define LINK (DMASK + 1) /* link */ +#define LACMASK (LINK | DMASK) /* link + data */ +#define SIGN 0400000 /* sign bit */ +#define OP_JMS 0100000 /* JMS */ +#define OP_JMP 0600000 /* JMP */ +#define OP_HLT 0740040 /* HLT */ + +/* IOT subroutine return codes */ + +#define IOT_V_SKP 18 /* skip */ +#define IOT_V_REASON 19 /* reason */ +#define IOT_SKP (1 << IOT_V_SKP) +#define IOT_REASON (1 << IOT_V_REASON) + +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* PC change queue */ + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC + +/* XVM memory management registers */ + +#define MM_RDIS 0400000 /* reloc disabled */ +#define MM_V_GM 15 /* G mode */ +#define MM_M_GM 03 +#define MM_GM (MM_M_GM << MM_V_GM) +#define MM_G_W0 0077777 /* virt addr width */ +#define MM_G_W1 0177777 +#define MM_G_W2 0777777 +#define MM_G_W3 0377777 +#define MM_G_B0 0060000 /* SAS base */ +#define MM_G_B1 0160000 +#define MM_G_B2 0760000 +#define MM_G_B3 0360000 +#define MM_UIOT 0040000 /* user mode IOT's */ +#define MM_WP 0020000 /* share write prot */ +#define MM_SH 0010000 /* share enabled */ +#define MM_V_SLR 10 /* segment length reg */ +#define MM_M_SLR 03 +#define MM_SLR_L0 001000 /* SAS length */ +#define MM_SLR_L1 002000 +#define MM_SLR_L2 010000 +#define MM_SLR_L3 020000 +#define MM_SBR_MASK 01777 /* share base reg */ +#define MM_GETGM(x) (((x) >> MM_V_GM) & MM_M_GM) +#define MM_GETSLR(x) (((x) >> MM_V_SLR) & MM_M_SLR) + +/* Device information block */ + +#define DEV_MAXBLK 8 /* max dev block */ +#define DEV_MAX 64 /* total devices */ + +typedef struct { + uint32 dev; /* base dev number */ + uint32 num; /* number of slots */ + int32 (*iors)(void); /* IORS responder */ + int32 (*dsp[DEV_MAXBLK])(int32 dev, int32 pulse, int32 dat); + } DIB; + +/* Standard device numbers */ + +#define DEV_PTR 001 /* paper tape reader */ +#define DEV_PTP 002 /* paper tape punch */ +#define DEV_TTI 003 /* console input */ +#define DEV_TTO 004 /* console output */ +#define DEV_TTI1 041 /* extra terminals */ +#define DEV_TTO1 040 +#define DEV_DRM 060 /* drum */ +#define DEV_RP 063 /* RP15 */ +#define DEV_LPT 065 /* line printer */ +#define DEV_RF 070 /* RF09 */ +#define DEV_RB 071 /* RB09 */ +#define DEV_MT 073 /* magtape */ +#define DEV_DTA 075 /* dectape */ + +/* Interrupt system + + The interrupt system can be modelled on either the flag driven system + of the PDP-4 and PDP-7 or the API driven system of the PDP-9 and PDP-15. + If flag based, API is hard to implement; if API based, IORS requires + extra code for implementation. I've chosen an API based model. + + API channel Device API priority Notes + + 00 software 4 4 + 01 software 5 5 + 02 software 6 6 + 03 software 7 7 + 04 TC02/TC15 1 + 05 TC59D 1 + 06 drum 1 PDP-9 only + 07 RB09 1 PDP-9 only + 10 paper tape reader 2 + 11 real time clock 3 + 12 power fail 0 + 13 memory parity 0 + 14 display 2 + 15 card reader 2 + 16 line printer 2 + 17 A/D converter 0 + 20 interprocessor buffer 3 + 21 360 link 3 PDP-9 only + 22 data phone 2 PDP-15 only + 23 RF09/RF15 1 + 24 RP15 1 PDP-15 only + 25 plotter 1 PDP-15 only + 26 - + 27 - + 30 - + 31 - + 32 - + 33 - + 34 LT15 TTO 3 PDP-15 only + 35 LT15 TTI 3 PDP-15 only + 36 - + 37 - + + On the PDP-9, any API level active masks PI, and PI does not mask API. + On the PDP-15, only the hardware API levels active mask PI, and PI masks + the API software levels. */ + +#define API_ML0 0200 /* API masks: level 0 */ +#define API_ML1 0100 +#define API_ML2 0040 +#define API_ML3 0020 +#define API_ML4 0010 +#define API_ML5 0004 +#define API_ML6 0002 +#define API_ML7 0001 /* level 7 */ + +#if defined (PDP9) /* levels which mask PI */ +#define API_MASKPI (API_ML0|API_ML1|API_ML2|API_ML3|API_ML4|API_ML5|API_ML6|API_ML7) +#else +#define API_MASKPI (API_ML0|API_ML1|API_ML2|API_ML3) +#endif + +#define API_HLVL 4 /* hwre levels */ +#define ACH_SWRE 040 /* swre int vec */ + +/* API level 0 */ + +#define INT_V_PWRFL 0 /* powerfail */ + +#define INT_PWRFL (1 << INT_V_PWRFL) + +#define API_PWRFL 0 + +#define ACH_PWRFL 052 + +/* API level 1 */ + +#define INT_V_DTA 0 /* DECtape */ +#define INT_V_MTA 1 /* magtape */ +#define INT_V_DRM 2 /* drum */ +#define INT_V_RF 3 /* fixed head disk */ +#define INT_V_RP 4 /* disk pack */ +#define INT_V_RB 5 /* RB disk */ + +#define INT_DTA (1 << INT_V_DTA) +#define INT_MTA (1 << INT_V_MTA) +#define INT_DRM (1 << INT_V_DRM) +#define INT_RF (1 << INT_V_RF) +#define INT_RP (1 << INT_V_RP) +#define INT_RB (1 << INT_V_RB) + +#define API_DTA 1 +#define API_MTA 1 +#define API_DRM 1 +#define API_RF 1 +#define API_RP 1 +#define API_RB 1 + +#define ACH_DTA 044 +#define ACH_MTA 045 +#define ACH_DRM 046 +#define ACH_RB 047 +#define ACH_RF 063 +#define ACH_RP 064 + +/* API level 2 */ + +#define INT_V_PTR 0 /* paper tape reader */ +#define INT_V_LPT 1 /* line printer */ +#define INT_V_LPTSPC 2 /* line printer spc */ + +#define INT_PTR (1 << INT_V_PTR) +#define INT_LPT (1 << INT_V_LPT) +#define INT_LPTSPC (1 << INT_V_LPTSPC) + +#define API_PTR 2 +#define API_LPT 2 +#define API_LPTSPC 2 + +#define ACH_PTR 050 +#define ACH_LPT 056 + +/* API level 3 */ + +#define INT_V_CLK 0 /* clock */ +#define INT_V_TTI1 1 /* LT15 keyboard */ +#define INT_V_TTO1 2 /* LT15 output */ + +#define INT_CLK (1 << INT_V_CLK) +#define INT_TTI1 (1 << INT_V_TTI1) +#define INT_TTO1 (1 << INT_V_TTO1) + +#define API_CLK 3 +#define API_TTI1 3 +#define API_TTO1 3 + +#define ACH_CLK 051 +#define ACH_TTI1 075 +#define ACH_TTO1 074 + +/* PI level */ + +#define INT_V_TTI 0 /* console keyboard */ +#define INT_V_TTO 1 /* console output */ +#define INT_V_PTP 2 /* paper tape punch */ + +#define INT_TTI (1 << INT_V_TTI) +#define INT_TTO (1 << INT_V_TTO) +#define INT_PTP (1 << INT_V_PTP) + +#define API_TTI 4 /* PI level */ +#define API_TTO 4 +#define API_PTP 4 + +/* Interrupt macros */ + +#define SET_INT(dv) int_hwre[API_##dv] = int_hwre[API_##dv] | INT_##dv +#define CLR_INT(dv) int_hwre[API_##dv] = int_hwre[API_##dv] & ~INT_##dv +#define TST_INT(dv) (int_hwre[API_##dv] & INT_##dv) + +/* I/O status flags for the IORS instruction + + bit PDP-4 PDP-7 PDP-9 PDP-15 + + 0 intr on intr on intr on intr on + 1 tape rdr flag* tape rdr flag* tape rdr flag* tape rdr flag* + 2 tape pun flag* tape pun flag* tape pun flag* tape pun flag* + 3 keyboard flag* keyboard flag* keyboard flag* keyboard flag* + 4 type out flag* type out flag* type out flag* type out flag* + 5 display flag* display flag* light pen flag* light pen flag* + 6 clk ovflo flag* clk ovflo flag* clk ovflo flag* clk ovflo flag* + 7 clk enable flag clk enable flag clk enable flag clk enable flag + 8 mag tape flag* mag tape flag* tape rdr empty* tape rdr empty* + 9 card rdr col* * tape pun empty tape pun empty + 10 card rdr ~busy DECtape flag* DECtape flag* + 11 card rdr error magtape flag* magtape flag* + 12 card rdr EOF disk pack flag* + 13 card pun row* DECdisk flag* DECdisk flag* + 14 card pun error lpt flag* + 15 lpt flag* lpt flag* lpt flag* + 16 lpt space flag* lpt error flag lpt error flag + 17 drum flag* drum flag* +*/ + +#define IOS_ION 0400000 /* interrupts on */ +#define IOS_PTR 0200000 /* tape reader */ +#define IOS_PTP 0100000 /* tape punch */ +#define IOS_TTI 0040000 /* keyboard */ +#define IOS_TTO 0020000 /* terminal */ +#define IOS_LPEN 0010000 /* light pen */ +#define IOS_CLK 0004000 /* clock */ +#define IOS_CLKON 0002000 /* clock enable */ +#define IOS_DTA 0000200 /* DECtape */ +#define IOS_RP 0000040 /* disk pack */ +#define IOS_RF 0000020 /* fixed head disk */ +#define IOS_DRM 0000001 /* drum */ +#if defined (PDP4) || defined (PDP7) +#define IOS_MTA 0001000 /* magtape */ +#define IOS_LPT 0000004 /* line printer */ +#define IOS_LPT1 0000002 /* line printer stat */ +#elif defined (PDP9) +#define IOS_PTRERR 0001000 /* reader empty */ +#define IOS_PTPERR 0000400 /* punch empty */ +#define IOS_MTA 0000100 /* magtape */ +#define IOS_LPT 0000004 /* line printer */ +#define IOS_LPT1 0000002 /* line printer stat */ +#elif defined (PDP15) +#define IOS_PTRERR 0001000 /* reader empty */ +#define IOS_PTPERR 0000400 /* punch empty */ +#define IOS_MTA 0000100 /* magtape */ +#define IOS_LPT 0000010 /* line printer */ +#define IOS_LPT1 0000000 /* not used */ +#endif + +/* Function prototypes */ + +t_stat set_devno (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_devno (FILE *st, UNIT *uptr, int32 val, void *desc); + +#endif diff --git a/PDP18B/pdp18b_diag.txt b/PDP18B/pdp18b_diag.txt new file mode 100644 index 0000000..74fbd1e --- /dev/null +++ b/PDP18B/pdp18b_diag.txt @@ -0,0 +1,110 @@ +18b PDP Diagnostics + +1. PDP-4 + +2. PDP-7 + +2.1 PDP-7 Instruction Test (Maindec 701) + +The diagnostic must be boot loaded, as it jumps dynamically out +of the RIM load process into its own loader. + +At start, set SR<1:16> to a non-zero value. The diagnostic +executes four HLT's as part of initial tests and then runs to +completion. Normal HLT is at 2623 (PC = 2624). + +sim> att -e ptr digital-7-54-m-rim.bin +sim> boot ptr + +HALT instruction, PC: 17670 (AND 17727) +sim> d sr 4 ; any even value between 2 and 377776 +sim> run 170 + +HALT instruction, PC: 00171 (CML CMA) +sim> ex ac,l +AC: 000000 +L: 0 +sim> c + +HALT instruction, PC: 00173 (SPA) +sim> ex ac,l +AC: 777777 +L: 1 +sim> c + +HALT instruction, PC: 00176 (SPA) +sim> ex ac,l +AC: 000000 +L: 0 +sim> c + +HALT instruction, PC: 00201 (LAC 4116) +sim> ex ac,l +AC: 000004 +L: 0 +sim> c + +HALT instruction, PC: 02624 (JMP 201) + +3. PDP-9 + +4. PDP-15 + +Operating Instructions, PDP-15 diagnostics + +MAINDEC-15-D0A1-PH Instruction test 1 + +Read in: 200 +Start: 200 +Breakpoint: 7274 for one pass + +MAINDEC-15-D0A2-PH Instruction test 1A + +Read in: 200 +Start: 200 +Breakpoint: 4437 for one pass + +MAINDEC-15-D0AA-PB Index register test + +Read in: 17700 (ignored, binary tape) +Start: 200 +Halts: at 214, set BANKM = 0 +Runs to: prints END at end of pass + +MAINDEC-15-D0BB-PH Instruction test 2 + +Read in: 200 +Start: 200 +SR: 1 to run clock test +Breakpoint: 6403 for one pass + +MAINDEC-15-D0CA-PH Memory address test + +Read in: 7200 +Start: 7200 +Breakpoint: 7577 for one pass + +MAINDEC-15-D0EA-PH JMP-Y interrupt test + +Read in: 7400 +Start: 7400 +Breakpoint: 7551 for one pass + +MAINDEC-15-D0FA-PH JMS-Y interrupt test + +Read in: 7400 +Start: 7400 +Breakpoint: 7577 for one pass + +MAINDEC-15-D0KA-PH ISZ test + +Read in: 200 +Start: 200 +Breakpoint: 7704 for one pass + +MAINDEC-15-D1CD-PB Extended memory test + +Read in: 200 (ignored, binary tape) +Start: 200 +Halts after printout, set SR = 30000 +Breakpoint: 563 for one pass diff --git a/PDP18B/pdp18b_drm.c b/PDP18B/pdp18b_drm.c new file mode 100644 index 0000000..7ed60f5 --- /dev/null +++ b/PDP18B/pdp18b_drm.c @@ -0,0 +1,248 @@ +/* pdp18b_drm.c: drum/fixed head disk simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + drm (PDP-4,PDP-7) Type 24 serial drum + + 14-Jan-04 RMS Revised IO device call interface + 26-Oct-03 RMS Cleaned up buffer copy code + 05-Dec-02 RMS Updated from Type 24 documentation + 22-Nov-02 RMS Added PDP-4 support + 05-Feb-02 RMS Added DIB, device number support + 03-Feb-02 RMS Fixed bug in reset routine (found by Robert Alan Byer) + 06-Jan-02 RMS Revised enable/disable support + 25-Nov-01 RMS Revised interrupt structure + 10-Jun-01 RMS Cleaned up IOT decoding to reflect hardware + 26-Apr-01 RMS Added device enable/disable support + 14-Apr-99 RMS Changed t_addr to unsigned +*/ + +#include "pdp18b_defs.h" +#include + +/* Constants */ + +#define DRM_NUMWDS 256 /* words/sector */ +#define DRM_NUMSC 2 /* sectors/track */ +#define DRM_NUMTR 256 /* tracks/drum */ +#define DRM_NUMDK 1 /* drum/controller */ +#define DRM_NUMWDT (DRM_NUMWDS * DRM_NUMSC) /* words/track */ +#define DRM_SIZE (DRM_NUMDK * DRM_NUMTR * DRM_NUMWDT) /* words/drum */ +#define DRM_SMASK ((DRM_NUMTR * DRM_NUMSC) - 1) /* sector mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define DRM_READ 000 /* read */ +#define DRM_WRITE 040 /* write */ + +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DRM_NUMWDT))) + +extern int32 M[]; +extern int32 int_hwre[API_HLVL+1]; +extern UNIT cpu_unit; + +int32 drm_da = 0; /* track address */ +int32 drm_ma = 0; /* memory address */ +int32 drm_err = 0; /* error flag */ +int32 drm_wlk = 0; /* write lock */ +int32 drm_time = 10; /* inter-word time */ +int32 drm_stopioe = 1; /* stop on error */ + +DEVICE drm_dev; +int32 drm60 (int32 dev, int32 pulse, int32 AC); +int32 drm61 (int32 dev, int32 pulse, int32 AC); +int32 drm62 (int32 dev, int32 pulse, int32 AC); +int32 drm_iors (void); +t_stat drm_svc (UNIT *uptr); +t_stat drm_reset (DEVICE *dptr); +t_stat drm_boot (int32 unitno, DEVICE *dptr); + +/* DRM data structures + + drm_dev DRM device descriptor + drm_unit DRM unit descriptor + drm_reg DRM register list +*/ + +DIB drm_dib = { DEV_DRM, 3 ,&drm_iors, { &drm60, &drm61, &drm62 } }; + +UNIT drm_unit = { + UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DRM_SIZE) + }; + +REG drm_reg[] = { + { ORDATA (DA, drm_da, 9) }, + { ORDATA (MA, drm_ma, 16) }, + { FLDATA (INT, int_hwre[API_DRM], INT_V_DRM) }, + { FLDATA (DONE, int_hwre[API_DRM], INT_V_DRM) }, + { FLDATA (ERR, drm_err, 0) }, + { ORDATA (WLK, drm_wlk, 32) }, + { DRDATA (TIME, drm_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, drm_stopioe, 0) }, + { ORDATA (DEVNO, drm_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB drm_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE drm_dev = { + "DRM", &drm_unit, drm_reg, drm_mod, + 1, 8, 20, 1, 8, 18, + NULL, NULL, &drm_reset, + &drm_boot, NULL, NULL, + &drm_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 drm60 (int32 dev, int32 pulse, int32 AC) +{ +if ((pulse & 027) == 06) { /* DRLR, DRLW */ + drm_ma = AC & 0177777; /* load mem addr */ + drm_unit.FUNC = pulse & DRM_WRITE; /* save function */ + } +return AC; +} + +int32 drm61 (int32 dev, int32 pulse, int32 AC) +{ +int32 t; + +if (pulse & 001) { /* DRSF */ + if (TST_INT (DRM)) AC = AC | IOT_SKP; + } +if (pulse & 002) { /* DRCF */ + CLR_INT (DRM); /* clear done */ + drm_err = 0; /* clear error */ + } +if (pulse & 004) { /* DRSS */ + drm_da = AC & DRM_SMASK; /* load sector # */ + t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); + if (t <= 0) t = t + DRM_NUMWDT; /* wrap around? */ + sim_activate (&drm_unit, t * drm_time); /* schedule op */ + } +return AC; +} + +int32 drm62 (int32 dev, int32 pulse, int32 AC) +{ +int32 t; + +if (pulse & 001) { /* DRSN */ + if (drm_err == 0) AC = AC | IOT_SKP; + } +if (pulse & 004) { /* DRCS */ + CLR_INT (DRM); /* clear done */ + drm_err = 0; /* clear error */ + t = ((drm_da % DRM_NUMSC) * DRM_NUMWDS) - GET_POS (drm_time); + if (t <= 0) t = t + DRM_NUMWDT; /* wrap around? */ + sim_activate (&drm_unit, t * drm_time); /* schedule op */ + } +return AC; +} + +/* Unit service + + This code assumes the entire drum is buffered. +*/ + +t_stat drm_svc (UNIT *uptr) +{ +int32 i; +uint32 da; +int32 *fbuf = uptr->filebuf; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + drm_err = 1; /* set error */ + SET_INT (DRM); /* set done */ + return IORETURN (drm_stopioe, SCPE_UNATT); + } + +da = drm_da * DRM_NUMWDS; /* compute dev addr */ +for (i = 0; i < DRM_NUMWDS; i++, da++) { /* do transfer */ + if (uptr->FUNC == DRM_READ) { /* read? */ + if (MEM_ADDR_OK (drm_ma)) /* if !nxm */ + M[drm_ma] = fbuf[da]; /* read word */ + } + else { /* write */ + if ((drm_wlk >> (drm_da >> 4)) & 1) drm_err = 1; + else { /* not locked */ + fbuf[da] = M[drm_ma]; /* write word */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + } + } + drm_ma = (drm_ma + 1) & 0177777; /* incr mem addr */ + } +drm_da = (drm_da + 1) & DRM_SMASK; /* incr dev addr */ +SET_INT (DRM); /* set done */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat drm_reset (DEVICE *dptr) +{ +drm_da = drm_ma = drm_err = 0; +CLR_INT (DRM); /* clear done */ +sim_cancel (&drm_unit); +return SCPE_OK; +} + +/* IORS routine */ + +int32 drm_iors (void) +{ +return (TST_INT (DRM)? IOS_DRM: 0); +} + +/* Bootstrap routine */ + +#define BOOT_START 02000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 0750000, /* CLA ; dev, mem addr */ + 0706006, /* DRLR ; load ma */ + 0706106, /* DRSS ; load da, start */ + 0706101, /* DRSF ; wait for done */ + 0602003, /* JMP .-1 */ + 0600000 /* JMP 0 ; enter boot */ + }; + +t_stat drm_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 PC; + +if (drm_dib.dev != DEV_DRM) return STOP_NONSTD; /* non-std addr? */ +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +PC = BOOT_START; +return SCPE_OK; +} diff --git a/PDP18B/pdp18b_dt.c b/PDP18B/pdp18b_dt.c new file mode 100644 index 0000000..ac29f01 --- /dev/null +++ b/PDP18B/pdp18b_dt.c @@ -0,0 +1,1484 @@ +/* pdp18b_dt.c: 18b DECtape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dt (PDP-4, PDP-7) Type 550/555 DECtape + (PDP-9) TC02/TU55 DECtape + (PDP-15) TC15/TU56 DECtape + + 23-Jun-06 RMS Fixed switch conflict in ATTACH + Revised Type 550 header based on DECTOG formatter + 13-Jun-06 RMS Fixed checksum calculation bug in Type 550 + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 25-Jan-04 RMS Revised for device debug support + 14-Jan-04 RMS Revised IO device call interface + Changed sim_fsize calling sequence, added STOP_OFFR + 26-Oct-03 RMS Cleaned up buffer copy code + 18-Oct-03 RMS Fixed reverse checksum in read all + Added DECtape off reel message + Simplified timing + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 17-Oct-02 RMS Fixed bug in end of reel logic + 05-Oct-02 RMS Added DIB, device number support + 12-Sep-02 RMS Added 16b format support + 13-Aug-02 RMS Corrected Type 550 unit select logic + 25-Jul-02 RMS Added PDP-4 support + 30-May-02 RMS Widened POS to 32b + 10-Feb-02 RMS Added PDP-7 support + 06-Jan-02 RMS Revised enable/disable support + 29-Nov-01 RMS Added read only unit support + 25-Nov-01 RMS Revised interrupt structure + Changed POS, STATT, LASTT, FLG to arrays + 29-Aug-01 RMS Added casts to PDP-8 unpack routine + 17-Jul-01 RMS Moved function prototype + 11-May-01 RMS Fixed bug in reset + 26-Apr-01 RMS Added device enable/disable support + 15-Mar-01 RMS Added 129th word to PDP-8 format + + 18b DECtapes are represented in memory by fixed length buffer of 32b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 12b DECtape file is read in, it is converted to 18b/36b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the PDP-7 formatter) is: + + reverse end zone 7144 reverse end zone codes ~ 12 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 7144 forward end zone codes ~ 12 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). PDP-4/7 DECtapes came in two + formats. The first 5 controllers used a 4 word header/trailer (missing + word 0/4). All later serial numbers used the standard header. The later, + standard header/trailer is simulated here. + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the interblock words in the + bit bucket. + + The Type 550 controller has a 4b unit select field, for units 1-8; the TC02 + has a 3b unit select field, with unit 8 being represented as 0. The code + assumes that the GETUNIT macro returns a unit number in the range of 0-7, + with 8 represented as 0, and an invalid unit as -1. +*/ + +#include "pdp18b_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define DT_WC 030 /* word count */ +#define DT_CA 031 /* current addr */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_BLKWD 1 /* blk no word in h/t */ +#define DT_CSMWD 4 /* checksum word in h/t */ +#define DT_HTWRD 5 /* header/trailer words */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ +#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ +#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ +#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 256 /* block size in 18b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ +#define D11_FILSIZ (D18_CAPAC * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 86 /* block size in 18b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ + +#define D8_NBSIZE ((D8_BSIZE * D18_WSIZE) / D8_WSIZE) +#define D8_FILSIZ (D8_NBSIZE * D8_TSIZE * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D18_CAPAC /* default */ +#define DT_WSIZE D18_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Status register A */ + +#if defined (TC02) /* TC02/TC15 */ +#define DTA_V_UNIT 15 /* unit select */ +#define DTA_M_UNIT 07 +#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) +#define DTA_V_MOT 13 /* motion */ +#define DTA_M_MOT 03 +#define DTA_V_MODE 12 /* mode */ +#define DTA_V_FNC 9 /* function */ +#define DTA_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_RALL 03 /* read all */ +#define FNC_WRIT 04 /* write */ +#define FNC_WALL 05 /* write all */ +#define FNC_WMRK 06 /* write timing */ +#define DTA_V_ENB 8 /* int enable */ +#define DTA_V_CERF 7 /* clr error flag */ +#define DTA_V_CDTF 6 /* clr DECtape flag */ +#define DTA_FWDRV (1u << (DTA_V_MOT + 1)) +#define DTA_STSTP (1u << DTA_V_MOT) +#define DTA_MODE (1u << DTA_V_MODE) +#define DTA_ENB (1u << DTA_V_ENB) +#define DTA_CERF (1u << DTA_V_CERF) +#define DTA_CDTF (1u << DTA_V_CDTF) +#define DTA_RW (0777700 & ~(DTA_CERF | DTA_CDTF)) +#define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) +#define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ + SET_INT (DTA); \ + else CLR_INT (DTA); + +#else /* Type 550 */ +#define DTA_V_UNIT 12 /* unit select */ +#define DTA_M_UNIT 017 +#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) +#define DTA_V_MOT 4 /* motion */ +#define DTA_M_MOT 03 +#define DTA_V_FNC 0 /* function */ +#define DTA_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_WRIT 03 /* write */ +#define FNC_RALL 05 /* read all */ +#define FNC_WALL 06 /* write all */ +#define FNC_WMRK 07 /* write timing */ +#define DTA_STSTP (1u << (DTA_V_MOT + 1)) +#define DTA_FWDRV (1u << DTA_V_MOT) +#define DTA_MODE 0 /* not implemented */ +#define DTA_RW 077 +#define DTA_GETUNIT(x) map_unit[(((x) >> DTA_V_UNIT) & DTA_M_UNIT)] +#define DT_UPDINT if (dtsb & (DTB_DTF | DTB_BEF | DTB_ERF)) \ + SET_INT (DTA); \ + else CLR_INT (DTA); +#endif + +#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) +#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) + +/* Status register B */ + +#if defined (TC02) /* TC02/TC15 */ +#define DTB_V_ERF 17 /* error flag */ +#define DTB_V_MRK 16 /* mark trk err */ +#define DTB_V_END 15 /* end zone err */ +#define DTB_V_SEL 14 /* select err */ +#define DTB_V_PAR 13 /* parity err */ +#define DTB_V_TIM 12 /* timing err */ +#define DTB_V_DTF 6 /* DECtape flag */ +#define DTB_ERF (1u << DTB_V_ERF) +#define DTB_MRK (1u << DTB_V_MRK) +#define DTB_END (1u << DTB_V_END) +#define DTB_SEL (1u << DTB_V_SEL) +#define DTB_PAR (1u << DTB_V_PAR) +#define DTB_TIM (1u << DTB_V_TIM) +#define DTB_DTF (1u << DTB_V_DTF) +#define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ + DTB_PAR | DTB_TIM) + +#else /* Type 550 */ +#define DTB_V_DTF 17 /* data flag */ +#define DTB_V_BEF 16 /* block end flag */ +#define DTB_V_ERF 15 /* error flag */ +#define DTB_V_END 14 /* end of tape */ +#define DTB_V_TIM 13 /* timing err */ +#define DTB_V_REV 12 /* reverse */ +#define DTB_V_GO 11 /* go */ +#define DTB_V_MRK 10 /* mark trk err */ +#define DTB_V_SEL 9 /* select err */ +#define DTB_DTF (1u << DTB_V_DTF) +#define DTB_BEF (1u << DTB_V_BEF) +#define DTB_ERF (1u << DTB_V_ERF) +#define DTB_END (1u << DTB_V_END) +#define DTB_TIM (1u << DTB_V_TIM) +#define DTB_REV (1u << DTB_V_REV) +#define DTB_GO (1u << DTB_V_GO) +#define DTB_MRK (1u << DTB_V_MRK) +#define DTB_SEL (1u << DTB_V_SEL) +#define DTB_ALLERR (DTB_END | DTB_TIM | DTB_MRK | DTB_SEL) +#endif + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR 7 /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Operation substates */ + +#define DTO_WCO 1 /* wc overflow */ +#define DTO_SOB 2 /* start of block */ + +/* Logging */ + +#define LOG_MS 001 /* move, search */ +#define LOG_RW 002 /* read, write */ +#define LOG_RA 004 /* read all */ +#define LOG_BL 010 /* block # lblk */ + +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern int32 M[]; +extern int32 int_hwre[API_HLVL+1]; +extern UNIT cpu_unit; +extern int32 sim_switches; +extern int32 sim_is_running; +extern FILE *sim_deb; + +int32 dtsa = 0; /* status A */ +int32 dtsb = 0; /* status B */ +int32 dtdb = 0; /* data buffer */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_dctime = 40000; /* decel time */ +int32 dt_substate = 0; +int32 dt_logblk = 0; +int32 dt_stopoffr = 0; /* stop on off reel */ +static const int32 map_unit[16] = { /* Type 550 unit map */ + -1, 1, 2, 3, 4, 5, 6, 7, + 0, -1, -1, -1, -1, -1, -1, -1 + }; + +DEVICE dt_dev; +int32 dt75 (int32 dev, int32 pulse, int32 dat); +int32 dt76 (int32 dev, int32 pulse, int32 dat); +int32 dt_iors (void); +t_stat dt_svc (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, char *cptr); +t_stat dt_detach (UNIT *uptr); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos); + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +DIB dt_dib = { DEV_DTA, 2, &dt_iors, { &dt75, &dt76 } }; + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, DT_CAPAC) } + }; + +REG dt_reg[] = { + { ORDATA (DTSA, dtsa, 18) }, + { ORDATA (DTSB, dtsb, 18) }, + { ORDATA (DTDB, dtdb, 18) }, + { FLDATA (INT, int_hwre[API_DTA], INT_V_DTA) }, +#if defined (DTA_V_ENB) + { FLDATA (ENB, dtsa, DTA_V_ENB) }, +#endif + { FLDATA (DTF, dtsb, DTB_V_DTF) }, +#if defined (DTB_V_BEF) + { FLDATA (BEF, dtsb, DTB_V_BEF) }, +#endif + { FLDATA (ERF, dtsb, DTB_V_ERF) }, +#if defined (TC02) /* TC02/TC15 */ + { ORDATA (WC, M[DT_WC], 18) }, + { ORDATA (CA, M[DT_CA], 18) }, +#endif + { DRDATA (LTIME, dt_ltime, 31), REG_NZ }, + { DRDATA (DCTIME, dt_dctime, 31), REG_NZ }, + { ORDATA (SUBSTATE, dt_substate, 2) }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO) }, + { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, + DT_NUMDR, REG_RO) }, + { URDATA (LASTT, dt_unit[0].LASTT, 10, T_ADDR_W, 0, + DT_NUMDR, REG_HRO) }, + { ORDATA (DEVNO, dt_dib.dev, 6), REG_HRO }, + { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, + { NULL } + }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEBTAB dt_deb[] = { + { "MOTION", LOG_MS }, + { "DATA", LOG_RW }, + { "READALL", LOG_RA }, + { "BLOCK", LOG_BL }, + { NULL, 0 } + }; + +DEVICE dt_dev = { + "DT", dt_unit, dt_reg, dt_mod, + DT_NUMDR, 8, 24, 1, 8, 18, + NULL, NULL, &dt_reset, + NULL, &dt_attach, &dt_detach, + &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, + dt_deb, NULL, NULL + }; + +/* IOT routines */ + +#if defined (TC02) /* TC02/TC15 */ +int32 dt75 (int32 dev, int32 pulse, int32 dat) +{ +int32 old_dtsa = dtsa, fnc; +UNIT *uptr; + +if (((pulse & 060) == 040) && (pulse & 05)) { /* select */ + if (pulse & 01) dtsa = 0; /* DTCA */ + if (pulse & 02) dat = dtsa; /* DTRA!... */ + if (pulse & 04) { /* DTXA */ + if ((dat & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; + if ((dat & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; + dtsa = dtsa ^ (dat & DTA_RW); + } + if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa); + uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ + fnc = DTA_GETFNC (dtsa); /* get fnc */ + if (((uptr->flags) & UNIT_DIS) || /* disabled? */ + (fnc >= FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT)) || + ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT))) + dt_seterr (uptr, DTB_SEL); /* select err */ + else dt_newsa (dtsa); /* new func */ + DT_UPDINT; + return dat; + } +if ((pulse & 067) == 042) return dtsa; /* DTRA */ +if ((pulse & 067) == 061) /* DTEF */ + return ((dtsb & DTB_ERF)? IOT_SKP + dat: dat); +if ((pulse & 067) == 062) return dtsb; /* DTRB */ +if ((pulse & 067) == 063) /* DTEF!DTRB */ + return ((dtsb & DTB_ERF)? IOT_SKP + dtsb: dtsb); +return dat; +} + +int32 dt76 (int32 dev, int32 pulse, int32 dat) +{ +if ((pulse & 01) && (dtsb & DTB_DTF)) /* DTDF */ + return IOT_SKP + dat; +return dat; +} + +#else /* Type 550 */ +int32 dt75 (int32 dev, int32 pulse, int32 dat) +{ +if (((pulse & 041) == 001) && (dtsb & DTB_DTF)) /* MMDF */ + dat = dat | IOT_SKP; +else if (((pulse & 041) == 041) && (dtsb & DTB_ERF)) /* MMEF */ + dat = dat | IOT_SKP; +if (pulse & 002) { /* MMRD */ + dat = (dat & ~DMASK) | dtdb; + dtsb = dtsb & ~(DTB_DTF | DTB_BEF); + } +if (pulse & 004) { /* MMWR */ + dtdb = dat & DMASK; + dtsb = dtsb & ~(DTB_DTF | DTB_BEF); + } +DT_UPDINT; +return dat; +} + +int32 dt76 (int32 dev, int32 pulse, int32 dat) +{ +int32 fnc, mot, unum; +UNIT *uptr = NULL; + +unum = DTA_GETUNIT (dtsa); /* get unit no */ +if (unum >= 0) uptr = dt_dev.units + unum; /* get unit */ +if ((pulse & 001) && (dtsb & DTB_BEF)) /* MMBF */ + dat = dat | IOT_SKP; +if (pulse & 002) { /* MMRS */ + dtsb = dtsb & ~(DTB_REV | DTB_GO); /* clr rev, go */ + if (uptr) { /* valid unit? */ + mot = DTS_GETMOT (uptr->STATE); /* get motion */ + if (mot & DTS_DIR) dtsb = dtsb | DTB_REV; /* rev? set */ + if ((mot >= DTS_ACCF) || (uptr->STATE & 0777700)) + dtsb = dtsb | DTB_GO; /* accel? go */ + } + dat = (dat & ~DMASK) | dtsb; + } +if ((pulse & 044) == 044) { /* MMSE */ + if ((dtsa ^ dat) & DTA_UNIT) dt_deselect (dtsa); /* new unit? */ + dtsa = (dtsa & ~DTA_UNIT) | (dat & DTA_UNIT); + dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); + } +else if ((pulse & 044) == 004) { /* MMLC */ + dtsa = (dtsa & ~DTA_RW) | (dat & DTA_RW); /* load dtsa */ + dtsb = dtsb & ~(DTB_DTF | DTB_BEF | DTB_ERF | DTB_ALLERR); + fnc = DTA_GETFNC (dtsa); /* get fnc */ + if ((uptr == NULL) || /* invalid? */ + ((uptr->flags) & UNIT_DIS) || /* disabled? */ + (fnc >= FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WLK)) || + ((fnc == FNC_WALL) && (uptr->flags & UNIT_WLK))) + dt_seterr (uptr, DTB_SEL); /* select err */ + else dt_newsa (dtsa); + } +DT_UPDINT; +return dat; +} +#endif + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit, old_mot; +UNIT *uptr; + +old_unit = DTA_GETUNIT (oldf); /* get unit no */ +if (old_unit < 0) return; /* invalid? */ +uptr = dt_dev.units + old_unit; /* get unit */ +old_mot = DTS_GETMOT (uptr->STATE); +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; +} + +/* Command register change + + 1. If change in motion, stop to start + - schedule acceleration + - set function as next state + 2. If change in motion, start to stop + - if not already decelerating (could be reversing), + schedule deceleration + 3. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 4. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 5. If not yet at speed, + - set function as next state + 6. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, new_fnc; +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +new_unit = DTA_GETUNIT (newf); /* new unit */ +if (new_unit < 0) return; /* invalid? */ +uptr = dt_dev.units + new_unit; +if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, DTB_SEL); /* no, error */ + return; + } +prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ +prev_mving = prev_mot != DTS_STOP; /* previous moving? */ +prev_dir = prev_mot & DTS_DIR; /* previous dir? */ +new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ +new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ +new_fnc = DTA_GETFNC (newf); /* new function? */ + +if ((prev_mving | new_mving) == 0) return; /* stop to stop */ + +if (new_mving & ~prev_mving) { /* start? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mving & ~new_mving) { /* stop? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + return; + } + +if (prev_dir ^ new_dir) { /* dir chg? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; + } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, newpos; +#if defined (TC02) +int32 relpos; +#endif +uint32 oldpos; + +oldpos = uptr->pos; /* save old pos */ +if (dt_setpos (uptr)) return; /* update pos */ +uptr->STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr->STATE); /* set variables */ +dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; +unum = (int32) (uptr - dt_dev.units); +if (oldpos == uptr->pos) /* bump pos */ + uptr->pos = uptr->pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr->pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, DTB_END); /* set ez flag, stop */ + return; + } +sim_cancel (uptr); /* cancel cur op */ +dt_substate = DTO_SOB; /* substate = block start */ +switch (fnc) { /* case function */ + + case DTS_OFR: /* off reel */ + if (dir) newpos = -1000; /* rev? < start */ + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; + + case FNC_MOVE: /* move */ + dt_schedez (uptr, dir); /* sched end zone */ + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", + unum, (dir? "backward": "forward")); + return; /* done */ + + case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s\n", + unum, (dir? "backward": "forward")); + break; + + case FNC_WRIT: /* write */ + case FNC_READ: /* read */ +#if defined (TC02) /* TC02/TC15 */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); + break; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dt_seterr (uptr, DTB_SEL); + return; + } + if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? + blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? + blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); + break; +#endif + + case FNC_RALL: /* read all */ + case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_WSIZE; + else newpos = DT_EZLIN + (DT_WSIZE - 1); + } + else { + newpos = ((uptr->pos) / DT_WSIZE) * DT_WSIZE; + if (!dir) newpos = newpos + (DT_WSIZE - 1); + } + if (DEBUG_PRI (dt_dev, LOG_RA) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: read all block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous]": " ")); + break; + + default: + dt_seterr (uptr, DTB_SEL); /* bad state */ + return; + } + +#if defined (TYPE550) /* Type 550 */ +if ((fnc == FNC_WRIT) || (fnc == FNC_WALL)) { /* write function? */ + dtsb = dtsb | DTB_DTF; /* set data flag */ + DT_UPDINT; + } +#endif + +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr->STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr->LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr->LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ + + case DTS_STOP: /* stop */ + delta = 0; + break; + + case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; + udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; + + case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; + udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; + + case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; + } + +if (mot & DTS_DIR) uptr->pos = uptr->pos - delta; /* update pos */ +else uptr->pos = uptr->pos + delta; +if (((int32) uptr->pos < 0) || + ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { + detach_unit (uptr); /* off reel? */ + uptr->STATE = uptr->pos = 0; + unum = (int32) (uptr - dt_dev.units); + if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ + dt_seterr (uptr, DTB_SEL); /* error */ + return TRUE; + } +return FALSE; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr->STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr->STATE); +int32 *fbuf = (int32 *) uptr->filebuf; +int32 unum = uptr - dt_dev.units; +int32 blk, wrd, ma, relpos; +uint32 ba; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { + + case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); + uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ + if (uptr->STATE) /* not stopped? */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* reversing */ + return SCPE_OK; + + case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ + return SCPE_OK; + + case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ + + default: /* other */ + dt_seterr (uptr, DTB_SEL); /* state error */ + return SCPE_OK; + } + +/* Functional cases + + Move - must be at end zone + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + } +blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ + +switch (fnc) { /* at speed, check fnc */ + + case FNC_MOVE: /* move */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + + case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr->STATE = uptr->pos = 0; /* no visible action */ + break; + +/* TC02/TC15 service */ +/* Search */ + +#if defined (TC02) /* TC02/TC15 */ + + case FNC_SRCH: /* search */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* inc WC */ + ma = M[DT_CA] & AMASK; /* get mem addr */ + if (MEM_ADDR_OK (ma)) M[ma] = blk; /* store block # */ + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + if (DEBUG_PRI (dt_dev, LOG_MS)) + fprintf (sim_deb, ">>DT%d: found block %d\n", unum, blk); + break; + +/* Read has four subcases + + Start of block, not wc ovf - check that DTF is clear, otherwise normal + Normal - increment MA, WC, copy word from tape to memory + if read dir != write dir, bits must be scrambled + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - if end of block reached, timing error, + otherwise, continue to next word +*/ + + case FNC_READ: /* read */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + + case DTO_SOB: /* start of block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous": " ")); + dt_substate = 0; /* fall through */ + case 0: /* normal read */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + ma = M[DT_CA] & AMASK; /* mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dtdb = fbuf[ba]; /* get tape word */ + if (dir) dtdb = dt_comobv (dtdb); /* rev? comp obv */ + if (MEM_ADDR_OK (ma)) M[ma] = dtdb; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; /* wc ovf? */ + case DTO_WCO: /* wc ovf, not sob */ + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { + dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + } + break; + + case DTO_WCO | DTO_SOB: /* next block */ + if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + else sim_activate (uptr, DT_WSIZE * dt_ltime); + break; + } /* end case subst */ + break; + +/* Write has four subcases + + Start of block, not wc ovf - check that DTF is clear, set block direction + Normal - increment MA, WC, copy word from memory to tape + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + copy 0 to tape + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - schedule end zone +*/ + + case FNC_WRIT: /* write */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + + case DTO_SOB: /* start block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk, + (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous": " ")); + dt_substate = 0; /* fall through */ + case 0: /* normal write */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + case DTO_WCO: /* wc ovflo */ + ma = M[DT_CA] & AMASK; /* mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dtdb = dt_substate? 0: M[ma]; /* get word */ + if (dir) dtdb = dt_comobv (dtdb); /* rev? comp obv */ + fbuf[ba] = dtdb; /* write word */ + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { + dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + } + break; + + case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } /* end case subst */ + break; + +/* Read all has two subcases + + Not word count overflow - increment MA, WC, copy word from tape to memory + Word count overflow - schedule end zone +*/ + + case FNC_RALL: /* read all */ + switch (dt_substate) { /* case on substate */ + + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + ma = M[DT_CA] & AMASK; /* mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + dtdb = fbuf[ba]; /* get tape word */ + } + else dtdb = dt_gethdr (uptr, blk, relpos); /* get hdr */ + if (dir) dtdb = dt_comobv (dtdb); /* rev? comp obv */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (MEM_ADDR_OK (ma)) M[ma] = dtdb; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } /* end case substate */ + break; + +/* Write all has two subcases + + Not word count overflow - increment MA, WC, copy word from memory to tape + Word count overflow - schedule end zone +*/ + + case FNC_WALL: /* write all */ + switch (dt_substate) { /* case on substate */ + + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & DMASK; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & DMASK; + ma = M[DT_CA] & AMASK; /* mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dtdb = M[ma]; /* get mem word */ + if (dir) dtdb = dt_comobv (dtdb); + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + fbuf[ba] = dtdb; /* write word */ + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + } +/* /* ignore hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } /* end case substate */ + break; + +/* Type 550 service */ +/* Search */ + +#else /* Type 550 */ + case FNC_SRCH: /* search */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ + dtdb = blk; /* store block # */ + dtsb = dtsb | DTB_DTF; /* set DTF */ + if (DEBUG_PRI (dt_dev, LOG_MS)) + fprintf (sim_deb, ">>DT%d: search found block %d\n", unum, blk); + break; + +/* Read and read all */ + + case FNC_READ: case FNC_RALL: + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + dtdb = fbuf[ba]; /* get tape word */ + dtsb = dtsb | DTB_DTF; /* set flag */ + } + else { + ma = (2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1; + wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ +#if defined (OLD_TYPE550) + if ((wrd == 0) || /* skip 1st, last */ + (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; +#endif + if ((fnc == FNC_READ) && /* read, skip if not */ + (wrd != DT_CSMWD) && /* fwd, rev cksum */ + (wrd != ma)) break; + dtdb = dt_gethdr (uptr, blk, relpos); + if (wrd == (dir? DT_CSMWD: ma)) /* at end csum? */ + dtsb = dtsb | DTB_BEF; /* end block */ + else dtsb = dtsb | DTB_DTF; /* else next word */ + } + if (dir) dtdb = dt_comobv (dtdb); + break; + +/* Write and write all */ + + case FNC_WRIT: case FNC_WALL: + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DT_WSIZE * dt_ltime); /* sched next word */ + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + if (dir) fbuf[ba] = dt_comobv (dtdb); /* get data word */ + else fbuf[ba] = dtdb; + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + if (wrd == (dir? 0: DTU_BSIZE (uptr) - 1)) + dtsb = dtsb | DTB_BEF; /* end block */ + else dtsb = dtsb | DTB_DTF; /* else next word */ + } + else { + wrd = relpos / DT_WSIZE; /* hdr start = wd 0 */ +#if defined (OLD_TYPE550) + if ((wrd == 0) || /* skip 1st, last */ + (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - 1))) break; +#endif + if ((fnc == FNC_WRIT) && /* wr, skip if !csm */ + (wrd != ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1))) + break; + dtsb = dtsb | DTB_DTF; /* set flag */ + } + break; +#endif + + default: + dt_seterr (uptr, DTB_SEL); /* impossible state */ + break; + } /* end case function */ + +DT_UPDINT; /* update interrupts */ +return SCPE_OK; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr->STATE); + +dtsa = dtsa & ~DTA_STSTP; /* clear go */ +dtsb = dtsb | DTB_ERF | e; /* set error flag */ +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) return; /* update position */ + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ + } +DT_UPDINT; +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */ +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Complement obverse routine */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ DMASK; /* compl obverse */ +dat = ((dat >> 15) & 07) | ((dat >> 9) & 070) | + ((dat >> 3) & 0700) | ((dat & 0700) << 3) | + ((dat & 070) << 9) | ((dat & 07) << 15); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int32 *fbuf = (int32 *) uptr->filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +#if defined (TC02) /* TC02/TC15 */ +csum = 077; /* init csum */ +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = fbuf[ba + i] ^ DMASK; /* get ~word */ + csum = csum ^ (wrd >> 12) ^ (wrd >> 6) ^ wrd; + } +return (csum & 077); +#else /* Type 550 */ +csum = 0777777; +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = fbuf[ba + i]; /* get word */ + csum = csum + wrd; /* 1's comp add */ + if (csum > DMASK) csum = (csum + 1) & DMASK; + } +return (csum ^ DMASK); /* 1's comp res */ +#endif +} + +/* Get header word */ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos) +{ +int32 wrd = relpos / DT_WSIZE; + +if (wrd == DT_BLKWD) return blk; /* fwd blknum */ +#if defined (TC02) /* TC02/TC15 */ +if (wrd == DT_CSMWD) return 077; /* rev csum */ +if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ + return (dt_csum (uptr, blk) << 12); +#else /* Type 550 */ +if (wrd == DT_CSMWD) return 0777777; /* rev csum */ +if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_CSMWD - 1)) /* fwd csum */ + return (dt_csum (uptr, blk)); +#endif +if (wrd == ((2 * DT_HTWRD) + DTU_BSIZE (uptr) - DT_BLKWD - 1)) /* rev blkno */ + return dt_comobv (blk); +return 0; /* all others */ +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all drives */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* CAF? */ + prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } + } + else { + sim_cancel (uptr); /* sim reset */ + uptr->STATE = 0; + uptr->LASTT = sim_grtime (); + } + } +dtsa = dtsb = 0; /* clear status */ +DT_UPDINT; /* reset interrupt */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 dt_iors (void) +{ +#if defined IOS_DTA +return ((dtsb & (DTB_ERF | DTB_DTF))? IOS_DTA: 0); +#else +return 0; +#endif +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 12b, read 12b format and convert to 18b in buffer + If 16b, read 16b format and convert to 18b in buffer + If 18b/36b, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, char *cptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, sz, k, *fbuf; +int32 u = uptr - dt_dev.units; +t_stat r; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error? */ +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default 18b */ + if (sim_switches & SWMASK ('T')) /* att 12b? */ + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = uptr->flags | UNIT_11FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D8_FILSIZ) + uptr->flags = uptr->flags | UNIT_8FMT; + else if (sz == D11_FILSIZ) + uptr->flags = uptr->flags | UNIT_11FMT; + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (uint32)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +printf ("%s%d: ", sim_dname (&dt_dev), u); +if (uptr->flags & UNIT_8FMT) printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) printf ("16b format"); +else printf ("18b/36b format"); +printf (", buffering file in memory\n"); +if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D8_NBSIZE; k++) pdp8b[k] = 0; + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop thru blk */ + fbuf[ba] = ((uint32) (pdp8b[k] & 07777) << 6) | + ((uint32) (pdp8b[k + 1] >> 6) & 077); + fbuf[ba + 1] = ((uint32) (pdp8b[k + 1] & 077) << 12) | + ((uint32) pdp8b[k + 2] & 07777); + ba = ba + 2; /* end blk loop */ + } + } /* end file loop */ + uptr->hwmark = ba; + } /* end if */ +else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + k = fxread (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D18_BSIZE; k++) pdp11b[k] = 0; + for (k = 0; k < D18_BSIZE; k++) + fbuf[ba++] = pdp11b[k]; + } + uptr->hwmark = ba; + } /* end elif */ +else uptr->hwmark = fxread (uptr->filebuf, sizeof (uint32), + uptr->capac, uptr->fileref); +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +uptr->LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If 12b, convert 18b buffer to 12b and write to file + If 16b, convert 18b buffer to 16b and write to file + If 18b/36b, write buffer to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint16 pdp8b[D8_NBSIZE]; +uint16 pdp11b[D18_BSIZE]; +uint32 ba, k, *fbuf; +int32 u = uptr - dt_dev.units; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; +if (sim_is_active (uptr)) { + sim_cancel (uptr); + if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { + dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; + DT_UPDINT; + } + uptr->STATE = uptr->pos = 0; + } +fbuf = (uint32 *) uptr->filebuf; /* file buffer */ +if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { /* any data? */ + printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); + rewind (uptr->fileref); /* start of file */ + if (uptr->flags & UNIT_8FMT) { /* 12b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D8_NBSIZE; k = k + 3) { /* loop blk */ + pdp8b[k] = (fbuf[ba] >> 6) & 07777; + pdp8b[k + 1] = ((fbuf[ba] & 077) << 6) | + ((fbuf[ba + 1] >> 12) & 077); + pdp8b[k + 2] = fbuf[ba + 1] & 07777; + ba = ba + 2; + } /* end loop blk */ + fxwrite (pdp8b, sizeof (uint16), D8_NBSIZE, uptr->fileref); + if (ferror (uptr->fileref)) break; + } /* end loop file */ + } /* end if 12b */ + else if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru file */ + for (k = 0; k < D18_BSIZE; k++) /* loop blk */ + pdp11b[k] = fbuf[ba++] & 0177777; + fxwrite (pdp11b, sizeof (uint16), D18_BSIZE, uptr->fileref); + if (ferror (uptr->fileref)) break; + } /* end loop file */ + } /* end if 16b */ + else fxwrite (uptr->filebuf, sizeof (uint32), /* write file */ + uptr->hwmark, uptr->fileref); + if (ferror (uptr->fileref)) perror ("I/O error"); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = uptr->flags & ~(UNIT_8FMT | UNIT_11FMT); /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} diff --git a/PDP18B/pdp18b_fpp.c b/PDP18B/pdp18b_fpp.c new file mode 100644 index 0000000..c574990 --- /dev/null +++ b/PDP18B/pdp18b_fpp.c @@ -0,0 +1,855 @@ +/* pdp18b_fpp.c: FP15 floating point processor simulator + + Copyright (c) 2003-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + fpp PDP-15 floating point processor + + 06-Jul-06 RMS Fixed bugs in left shift, multiply + 31-Oct-04 RMS Fixed URFST to mask low 9b of fraction + Fixed exception PC setting + 10-Apr-04 RMS JEA is 15b not 18b + + The FP15 instruction format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 1 1 1 0 0 1| subop | microcoded modifiers | floating point + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |in| address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + Indirection is always single level. + + The FP15 supports four data formats: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | S| 2's complement integer | A: integer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | S| 2's complement integer (high) | A: extended integer + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 2's complement integer (low) | A+1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | fraction (low) |SE|2's complement exponent| A: single floating + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |SF| fraction (high) | A+1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |SE| 2's complement exponent | A: double floating + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + |SF| fraction (high) | A+1 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | fraction (low) | A+2 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + +*/ + +#include "pdp18b_defs.h" + +/* Instruction */ + +#define FI_V_OP 8 /* subopcode */ +#define FI_M_OP 017 +#define FI_GETOP(x) (((x) >> FI_V_OP) & FI_M_OP) +#define FI_NOLOAD 0200 /* don't load */ +#define FI_DP 0100 /* single/double */ +#define FI_FP 0040 /* int/flt point */ +#define FI_NONORM 0020 /* don't normalize */ +#define FI_NORND 0010 /* don't round */ +#define FI_V_SGNOP 0 /* A sign change */ +#define FI_M_SGNOP 03 +#define FI_GETSGNOP(x) (((x) >> FI_V_SGNOP) & FI_M_SGNOP) + +/* Exception register */ + +#define JEA_V_SIGN 17 /* A sign */ +#define JEA_V_GUARD 16 /* guard */ +#define JEA_EAMASK 077777 /* exc address */ +#define JEA_OFF_OVF 0 /* ovf offset */ +#define JEA_OFF_UNF 2 /* unf offset */ +#define JEA_OFF_DIV 4 /* div offset */ +#define JEA_OFF_MM 6 /* mem mgt offset */ + +/* Status codes - must relate directly to JEA offsets */ + +#define FP_OK 0 /* no error - mbz */ +#define FP_OVF (JEA_OFF_OVF + 1) /* overflow */ +#define FP_UNF (JEA_OFF_UNF + 1) /* underflow */ +#define FP_DIV (JEA_OFF_DIV + 1) /* divide exception */ +#define FP_MM (JEA_OFF_MM + 1) /* mem mgt error */ + +/* Unpacked floating point fraction */ + +#define UFP_FH_CARRY 0400000 /* carry out */ +#define UFP_FH_NORM 0200000 /* normalized */ +#define UFP_FH_MASK 0377777 /* hi mask */ +#define UFP_FL_MASK 0777777 /* low mask */ +#define UFP_FL_SMASK 0777000 /* low mask, single */ +#define UFP_FL_SRND 0000400 /* round bit, single */ + +#define GET_SIGN(x) (((x) >> 17) & 1) +#define SEXT18(x) (((x) & SIGN)? ((x) | ~DMASK): ((x) & DMASK)) +#define SEXT9(x) (((x) & 0400)? ((x) | ~0377): ((x) & 0377)) + +enum fop { + FOP_TST, FOP_SUB, FOP_RSUB, FOP_MUL, + FOP_DIV, FOP_RDIV, FOP_LD, FOP_ST, + FOP_FLT, FOP_FIX, FOP_LFMQ, FOP_JEA, + FOP_ADD, FOP_BR, FOP_DIAG, FOP_UND + }; + +typedef struct { + int32 exp; /* exponent */ + int32 sign; /* sign */ + int32 hi; /* hi frac, 17b */ + int32 lo; /* lo frac, 18b */ + } UFP; + +static int32 fir; /* instruction */ +static int32 jea; /* exc address */ +static int32 fguard; /* guard bit */ +static int32 stop_fpp = STOP_RSRV; /* stop if fp dis */ +static UFP fma; /* FMA */ +static UFP fmb; /* FMB */ +static UFP fmq; /* FMQ - hi,lo only */ + +extern int32 M[MAXMEMSIZE]; +extern int32 pcq[PCQ_SIZE]; +extern int32 pcq_p; +extern int32 PC; +extern int32 trap_pending, usmd; + +t_stat fp15_reset (DEVICE *dptr); +t_stat fp15_opnd (int32 ir, int32 addr, UFP *a); +t_stat fp15_store (int32 ir, int32 addr, UFP *a); +t_stat fp15_iadd (int32 ir, UFP *a, UFP *b, t_bool sub); +t_stat fp15_imul (int32 ir, UFP *a, UFP *b); +t_stat fp15_idiv (int32 ir, UFP *a, UFP *b); +t_stat fp15_fadd (int32 ir, UFP *a, UFP *b, t_bool sub); +t_stat fp15_fmul (int32 ir, UFP *a, UFP *b); +t_stat fp15_fdiv (int32 ir, UFP *a, UFP *b); +t_stat fp15_fix (int32 ir, UFP *a); +t_stat fp15_norm (int32 ir, UFP *a, UFP *b, t_bool rnd); +t_stat fp15_exc (int32 sta); +void fp15_asign (int32 ir, UFP *a); +void dp_add (UFP *a, UFP *b); +void dp_sub (UFP *a, UFP *b); +void dp_inc (UFP *a); +int32 dp_cmp (UFP *a, UFP *b); +void dp_mul (UFP *a, UFP *b); +void dp_lsh_1 (UFP *a, UFP *b); +void dp_rsh_1 (UFP *a, UFP *b); +void dp_dnrm_r (int32 ir, UFP *a, int32 sc); +void dp_swap (UFP *a, UFP *b); + +extern t_stat Read (int32 ma, int32 *dat, int32 cyc); +extern t_stat Write (int32 ma, int32 dat, int32 cyc); +extern int32 Incr_addr (int32 addr); +extern int32 Jms_word (int32 t); + +/* FPP data structures + + fpp_dev FPP device descriptor + fpp_unit FPP unit + fpp_reg FPP register list + fpp_mod FPP modifier list +*/ + +UNIT fpp_unit = { UDATA (NULL, 0, 0) }; + +REG fpp_reg[] = { + { ORDATA (FIR, fir, 12) }, + { ORDATA (EPA, fma.exp, 18) }, + { FLDATA (FMAS, fma.sign, 0) }, + { ORDATA (FMAH, fma.hi, 17) }, + { ORDATA (FMAL, fma.lo, 18) }, + { ORDATA (EPB, fmb.exp, 18) }, + { FLDATA (FMBS, fmb.sign, 0) }, + { ORDATA (FMBH, fmb.hi, 17) }, + { ORDATA (FMBL, fmb.lo, 18) }, + { FLDATA (FGUARD, fguard, 0) }, + { ORDATA (FMQH, fmq.hi, 17) }, + { ORDATA (FMQL, fmq.lo, 18) }, + { ORDATA (JEA, jea, 15) }, + { FLDATA (STOP_FPP, stop_fpp, 0) }, + { NULL } + }; + +DEVICE fpp_dev = { + "FPP", &fpp_unit, fpp_reg, NULL, + 1, 8, 1, 1, 8, 18, + NULL, NULL, &fp15_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE + }; + +/* Instruction decode for FP15 + + The CPU actually fetches the instruction and the word after. If the + instruction is 71XXXX, the CPU executes it as a NOP, and the FP15 fools + the CPU into thinking that the second word is also a NOP. + + Indirect addresses are resolved during fetch, unless the NOLOAD modifier + is set and the instruction is not a store. */ + +t_stat fp15 (int32 ir) +{ +int32 ar, ma, fop, dat; +t_stat sta = FP_OK; + +if (fpp_dev.flags & DEV_DIS) /* disabled? */ + return (stop_fpp? STOP_FPDIS: SCPE_OK); +fir = ir & 07777; /* save subop + mods */ +ma = PC; /* fetch next word */ +PC = Incr_addr (PC); +if (Read (ma, &ar, RD)) return fp15_exc (FP_MM); /* error? MM exc */ +fop = FI_GETOP (fir); /* get subopcode */ +if ((ar & SIGN) && /* indirect? */ + ((fop == FOP_ST) || !(ir & FI_NOLOAD))) { /* store or load? */ + ma = ar & AMASK; /* fetch indirect */ + if (Read (ma, &ar, RD)) return fp15_exc (FP_MM); + } +fma.exp = SEXT18 (fma.exp); /* sext exponents */ +fmb.exp = SEXT18 (fmb.exp); +switch (fop) { /* case on subop */ + + case FOP_TST: /* NOP */ + break; + + case FOP_SUB: /* subtract */ + if (sta = fp15_opnd (fir, ar, &fmb)) break; /* fetch op to FMB */ + if (fir & FI_FP) /* fp? */ + sta = fp15_fadd (fir, &fma, &fmb, 1); /* yes, fp sub */ + else sta = fp15_iadd (fir, &fma, &fmb, 1); /* no, int sub */ + break; + + case FOP_RSUB: /* reverse sub */ + fmb = fma; /* FMB <- FMA */ + if (sta = fp15_opnd (fir, ar, &fma)) break; /* fetch op to FMA */ + if (fir & FI_FP) /* fp? */ + sta = fp15_fadd (fir, &fma, &fmb, 1); /* yes, fp sub */ + else sta = fp15_iadd (fir, &fma, &fmb, 1); /* no, int sub */ + break; + + case FOP_MUL: /* multiply */ + if (sta = fp15_opnd (fir, ar, &fmb)) break; /* fetch op to FMB */ + if (fir & FI_FP) /* fp? */ + sta = fp15_fmul (fir, &fma, &fmb); /* yes, fp mul */ + else sta = fp15_imul (fir, &fma, &fmb); /* no, int mul */ + break; + + case FOP_DIV: /* divide */ + if (sta = fp15_opnd (fir, ar, &fmb)) break; /* fetch op to FMB */ + if (fir & FI_FP) /* fp? */ + sta = fp15_fdiv (fir, &fma, &fmb); /* yes, fp div */ + else sta = fp15_idiv (fir, &fma, &fmb); /* no, int div */ + break; + + case FOP_RDIV: /* reverse divide */ + fmb = fma; /* FMB <- FMA */ + if (sta = fp15_opnd (fir, ar, &fma)) break; /* fetch op to FMA */ + if (fir & FI_FP) /* fp? */ + sta = fp15_fdiv (fir, &fma, &fmb); /* yes, fp div */ + else sta = fp15_idiv (fir, &fma, &fmb); /* no, int div */ + break; + + case FOP_LD: /* load */ + if (sta = fp15_opnd (fir, ar, &fma)) break; /* fetch op to FMA */ + fp15_asign (fir, &fma); /* modify A sign */ + if (fir & FI_FP) /* fp? */ + sta = fp15_norm (ir, &fma, NULL, 0); /* norm, no round */ + break; + + case FOP_ST: /* store */ + fp15_asign (fir, &fma); /* modify A sign */ + sta = fp15_store (fir, ar, &fma); /* store result */ + break; + + case FOP_FLT: /* float */ + if (sta = fp15_opnd (fir, ar, &fma)) break; /* fetch op to FMA */ + fma.exp = 35; + fp15_asign (fir, &fma); /* adjust A sign */ + sta = fp15_norm (ir, &fma, NULL, 0); /* norm, no found */ + break; + + case FOP_FIX: /* fix */ + if (sta = fp15_opnd (fir, ar, &fma)) break; /* fetch op to FMA */ + sta = fp15_fix (fir, &fma); /* fix */ + break; + + case FOP_LFMQ: /* load FMQ */ + if (sta = fp15_opnd (fir, ar, &fma)) break; /* fetch op to FMA */ + dp_swap (&fma, &fmq); /* swap FMA, FMQ */ + fp15_asign (fir, &fma); /* adjust A sign */ + if (fir & FI_FP) /* fp? */ + sta = fp15_norm (ir, &fma, &fmq, 0); /* yes, norm, no rnd */ + break; + + case FOP_JEA: /* JEA */ + if (ir & 0200) { /* store? */ + dat = jea | (fma.sign << JEA_V_SIGN) | (fguard << JEA_V_GUARD); + sta = Write (ar, dat, WR); + } + else { /* no, load */ + if (sta = Read (ar, &dat, RD)) break; + fguard = (dat >> JEA_V_GUARD) & 1; + jea = dat & JEA_EAMASK; + } + break; + + case FOP_ADD: /* add */ + if (sta = fp15_opnd (fir, ar, &fmb)) break; /* fetch op to FMB */ + if (fir & FI_FP) /* fp? */ + sta = fp15_fadd (fir, &fma, &fmb, 0); /* yes, fp add */ + else sta = fp15_iadd (fir, &fma, &fmb, 0); /* no, int add */ + break; + + case FOP_BR: /* branch */ + if (((fir & 001) && ((fma.hi | fma.lo) == 0)) || + ((fir & 002) && fma.sign) || + ((fir & 004) && !fma.sign) || + ((fir & 010) && ((fma.hi | fma.lo) != 0)) || + ((fir & 020) && fguard)) { /* cond met? */ + PCQ_ENTRY; /* save current PC */ + PC = (PC & BLKMASK) | (ar & IAMASK); /* branch within 32K */ + } + break; + + default: + break; + } /* end switch op */ + +fma.exp = fma.exp & DMASK; /* mask exp to 18b */ +fmb.exp = fmb.exp & DMASK; +if (sta != FP_OK) return fp15_exc (sta); /* error? */ +return SCPE_OK; +} + +/* Operand load and store */ + +t_stat fp15_opnd (int32 ir, int32 addr, UFP *fpn) +{ +int32 i, numwd, wd[3]; + +fguard = 0; /* clear guard */ +if (ir & FI_NOLOAD) return FP_OK; /* no load? */ +if (ir & FI_FP) numwd = 2; /* fp? at least 2 */ +else numwd = 1; /* else at least 1 */ +if (ir & FI_DP) numwd = numwd + 1; /* dp? 1 more */ +for (i = 0; i < numwd; i++) { /* fetch words */ + if (Read (addr, &wd[i], RD)) return FP_MM; + addr = (addr + 1) & AMASK; + } +if (ir & FI_FP) { /* fp? */ + fpn->sign = GET_SIGN (wd[1]); /* frac sign */ + fpn->hi = wd[1] & UFP_FH_MASK; /* frac high */ + if (ir & FI_DP) { /* dp? */ + fpn->exp = SEXT18 (wd[0]); /* exponent */ + fpn->lo = wd[2]; /* frac low */ + } + else { /* sp */ + fpn->exp = SEXT9 (wd[0]); /* exponent */ + fpn->lo = wd[0] & UFP_FL_SMASK; /* frac low */ + } + } +else { + fpn->sign = GET_SIGN (wd[0]); /* int, get sign */ + if (ir & FI_DP) { /* dp? */ + fpn->lo = wd[1]; /* 2 words */ + fpn->hi = wd[0]; + } + else { /* single */ + fpn->lo = wd[0]; /* 1 word */ + fpn->hi = fpn->sign? DMASK: 0; /* sign extended */ + } + if (fpn->sign) { /* negative? */ + fpn->lo = (-fpn->lo) & UFP_FL_MASK; /* take abs val */ + fpn->hi = (~fpn->hi + (fpn->lo == 0)) & UFP_FH_MASK; + } + } +return FP_OK; +} + +t_stat fp15_store (int32 ir, int32 addr, UFP *a) +{ +int32 i, numwd, wd[3]; +t_stat sta; + +fguard = 0; /* clear guard */ +if (ir & FI_FP) { /* fp? */ + if (sta = fp15_norm (ir, a, NULL, 0)) return sta; /* normalize */ + if (ir & FI_DP) { /* dp? */ + wd[0] = a->exp & DMASK; /* exponent */ + wd[1] = (a->sign << 17) | a->hi; /* hi frac */ + wd[2] = a->lo; /* low frac */ + numwd = 3; /* 3 words */ + } + else { /* single */ + if (!(ir & FI_NORND) && (a->lo & UFP_FL_SRND)) { /* round? */ + a->lo = (a->lo + UFP_FL_SRND) & UFP_FL_SMASK; + a->hi = (a->hi + (a->lo == 0)) & UFP_FH_MASK; + if ((a->hi | a->lo) == 0) { /* carry out? */ + a->hi = UFP_FH_NORM; /* shift back */ + a->exp = a->exp + 1; + } + } + if (a->exp > 0377) return FP_OVF; /* sp ovf? */ + if (a->exp < -0400) return FP_UNF; /* sp unf? */ + wd[0] = (a->exp & 0777) | (a->lo & UFP_FL_SMASK); /* low frac'exp */ + wd[1] = (a->sign << 17) | a->hi; /* hi frac */ + numwd = 2; /* 2 words */ + } + } +else { + fmb.lo = (-a->lo) & UFP_FL_MASK; /* 2's complement */ + fmb.hi = (~a->hi + (fmb.lo == 0)) & UFP_FH_MASK; /* to FMB */ + if (ir & FI_DP) { /* dp? */ + if (a->sign) { /* negative? */ + wd[0] = fmb.hi | SIGN; /* store FMB */ + wd[1] = fmb.lo; + } + else { /* pos, store FMA */ + wd[0] = a->hi; + wd[1] = a->lo; + } + numwd = 2; /* 2 words */ + } + else { /* single */ + if (a->hi || (a->lo & SIGN)) return FP_OVF; /* check int ovf */ + if (a->sign) wd[0] = fmb.lo; /* neg? store FMB */ + else wd[0] = a->lo; /* pos, store FMA */ + numwd = 1; /* 1 word */ + } + } +for (i = 0; i < numwd; i++) { /* store words */ + if (Write (addr, wd[i], WR)) return FP_MM; + addr = (addr + 1) & AMASK; + } +return FP_OK; +} + +/* Integer arithmetic routines */ + +/* Integer add - overflow only on add, if carry out of high fraction */ + +t_stat fp15_iadd (int32 ir, UFP *a, UFP *b, t_bool sub) +{ +fmq.hi = fmq.lo = 0; /* clear FMQ */ +if (a->sign ^ b->sign ^ sub) dp_sub (a, b); /* eff subtract? */ +else { + dp_add (a, b); /* no, add */ + if (a->hi & UFP_FH_CARRY) { /* carry out? */ + a->hi = a->hi & UFP_FH_MASK; /* mask to 35b */ + return FP_OVF; /* overflow */ + } + } +fp15_asign (ir, a); /* adjust A sign */ +return FP_OK; +} + +/* Integer multiply - overflow if high result (FMQ after swap) non-zero */ + +t_stat fp15_imul (int32 ir, UFP *a, UFP *b) +{ +a->sign = a->sign ^ b->sign; /* sign of result */ +dp_mul (a, b); /* a'FMQ <- a * b */ +dp_swap (a, &fmq); /* swap a, FMQ */ +if (fmq.hi | fmq.lo) return FP_OVF; /* FMQ != 0? ovf */ +fp15_asign (ir, a); /* adjust A sign */ +return FP_OK; +} + +/* Integer divide - actually done as fraction divide + + - If divisor zero, error + - If dividend zero, done + - Normalize dividend and divisor together + - If divisor normalized but dividend not, result is zero + - If divisor not normalized, normalize and count shifts + - Do fraction divide for number of shifts, +1, steps + + Note that dp_lsh_1 returns a 72b result; the last right shift + guarantees a 71b remainder. The quotient cannot exceed 71b */ + +t_stat fp15_idiv (int32 ir, UFP *a, UFP *b) +{ +int32 i, sc; + +a->sign = a->sign ^ b->sign; /* sign of result */ +fmq.hi = fmq.lo = 0; /* clear quotient */ +a->exp = 0; /* clear a exp */ +if ((b->hi | b->lo) == 0) return FP_DIV; /* div by 0? */ +if ((a->hi | a->lo) == 0) return FP_OK; /* div into 0? */ +while (((a->hi & UFP_FH_NORM) == 0) && /* normalize divd */ + ((b->hi & UFP_FH_NORM) == 0)) { /* and divr */ + dp_lsh_1 (a, NULL); /* lsh divd, divr */ + dp_lsh_1 (b, NULL); /* can't carry out */ + } +if (!(a->hi & UFP_FH_NORM) && (b->hi & UFP_FH_NORM)) { /* divr norm, divd not? */ + dp_swap (a, &fmq); /* quo = 0 (fmq), rem = a */ + return FP_OK; + } +while ((b->hi & UFP_FH_NORM) == 0) { /* normalize divr */ + dp_lsh_1 (b, NULL); /* can't carry out */ + a->exp = a->exp + 1; /* count steps */ + } +sc = a->exp; +for (i = 0; i <= sc; i++) { /* n+1 steps */ + dp_lsh_1 (&fmq, NULL); /* left shift quo */ + if (dp_cmp (a, b) >= 0) { /* sub work? */ + dp_sub (a, b); /* a -= b */ + if (i == 0) a->exp = a->exp + 1; /* first step? */ + fmq.lo = fmq.lo | 1; /* set quo bit */ + } + dp_lsh_1 (a, NULL); /* left shift divd */ + } +dp_rsh_1 (a, NULL); /* shift back */ +dp_swap (a, &fmq); /* swap a, FMQ */ +fp15_asign (ir, a); /* adjust A sign */ +return FP_OK; +} + +/* Floating point arithmetic routines */ + +/* Floating add + - Special add case, overflow if carry out increments exp out of range + - All cases, overflow/underflow detected in normalize */ + +t_stat fp15_fadd (int32 ir, UFP *a, UFP *b, t_bool sub) +{ +int32 ediff; + +fmq.hi = fmq.lo = 0; /* clear FMQ */ +ediff = a->exp - b->exp; /* exp diff */ +if (((a->hi | a->lo) == 0) || (ediff < -35)) { /* a = 0 or "small"? */ + *a = *b; /* rslt is b */ + a->sign = a->sign ^ sub; /* or -b if sub */ + } +else if (((b->hi | b->lo) != 0) && (ediff <= 35)) { /* b!=0 && ~"small"? */ + if (ediff > 0) dp_dnrm_r (ir, b, ediff); /* |a| > |b|? dnorm b */ + else if (ediff < 0) { /* |a| < |b|? */ + a->exp = b->exp; /* b exp is rslt */ + dp_dnrm_r (ir, a, -ediff); /* denorm A */ + } + if (a->sign ^ b->sign ^ sub) dp_sub (a, b); /* eff sub? */ + else { /* eff add */ + dp_add (a, b); /* add */ + if (a->hi & UFP_FH_CARRY) { /* carry out? */ + fguard = a->lo & 1; /* set guard */ + dp_rsh_1 (a, NULL); /* right shift */ + a->exp = a->exp + 1; /* incr exponent */ + if (!(ir & FI_NORND) && fguard) /* rounding? */ + dp_inc (a); + } + } + } /* end if b != 0 */ +fp15_asign (ir, a); /* adjust A sign */ +return fp15_norm (ir, a, NULL, 0); /* norm, no round */ +} + +/* Floating multiply - overflow/underflow detected in normalize */ + +t_stat fp15_fmul (int32 ir, UFP *a, UFP *b) +{ +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp + b->exp; /* exp of result */ +dp_mul (a, b); /* mul fractions */ +fp15_asign (ir, a); /* adjust A sign */ +return fp15_norm (ir, a, &fmq, 1); /* norm and round */ +} + +/* Floating divide - overflow/underflow detected in normalize */ + +t_stat fp15_fdiv (int32 ir, UFP *a, UFP *b) +{ +int32 i; + +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp - b->exp; /* exp of result */ +fmq.hi = fmq.lo = 0; /* clear quotient */ +if (!(b->hi & UFP_FH_NORM)) return FP_DIV; /* divr not norm? */ +if (a->hi | a->lo) { /* divd non-zero? */ + fp15_norm (0, a, NULL, 0); /* normalize divd */ + for (i = 0; (fmq.hi & UFP_FH_NORM) == 0; i++) { /* until quo */ + dp_lsh_1 (&fmq, NULL); /* left shift quo */ + if (dp_cmp (a, b) >= 0) { /* sub work? */ + dp_sub (a, b); /* a = a - b */ + if (i == 0) a->exp = a->exp + 1; + fmq.lo = fmq.lo | 1; /* set quo bit */ + } + dp_lsh_1 (a, NULL); /* left shift divd */ + } + dp_rsh_1 (a, NULL); /* shift back */ + dp_swap (a, &fmq); /* swap a, FMQ */ + } +fp15_asign (ir, a); /* adjust A sign */ +return fp15_norm (ir, a, &fmq, 1); /* norm and round */ +} + +/* Floating to integer - overflow only if exponent out of range */ + +t_stat fp15_fix (int32 ir, UFP *a) +{ +int32 i; + +fmq.hi = fmq.lo = 0; /* clear FMQ */ +if (a->exp > 35) return FP_OVF; /* exp > 35? ovf */ +if (a->exp < 0) a->hi = a->lo = 0; /* exp <0 ? rslt 0 */ +else { + for (i = a->exp; i < 35; i++) /* denorm frac */ + dp_rsh_1 (a, &fmq); + if (fmq.hi & UFP_FH_NORM) { /* last out = 1? */ + fguard = 1; /* set guard */ + if (!(ir & FI_NORND)) dp_inc (a); /* round */ + } + } +fp15_asign (ir, a); /* adjust A sign */ +return FP_OK; +} + +/* Double precision routines */ + +/* Double precision add - returns 72b result (including carry) */ + +void dp_add (UFP *a, UFP *b) +{ +a->lo = (a->lo + b->lo) & UFP_FL_MASK; /* add low */ +a->hi = a->hi + b->hi + (a->lo < b->lo); /* add hi + carry */ +return; +} + +/* Double precision increment - returns 72b result (including carry) */ + +void dp_inc (UFP *a) +{ +a->lo = (a->lo + 1) & UFP_FL_MASK; /* inc low */ +a->hi = a->hi + (a->lo == 0); /* propagate carry */ +return; +} + +/* Double precision subtract - result always fits in 71b */ + +void dp_sub (UFP *a, UFP *b) +{ +if (dp_cmp (a,b) >= 0) { /* |a| >= |b|? */ + a->hi = (a->hi - b->hi - (a->lo < b->lo)) & UFP_FH_MASK; + a->lo = (a->lo - b->lo) & UFP_FL_MASK; /* a - b */ + } +else { + a->hi = (b->hi - a->hi - (b->lo < a->lo)) & UFP_FH_MASK; + a->lo = (b->lo - a->lo) & UFP_FL_MASK; /* b - a */ + a->sign = a->sign ^ 1; /* change a sign */ + } +return; +} + +/* Double precision compare - returns +1 (>), 0 (=), -1 (<) */ + +int32 dp_cmp (UFP *a, UFP *b) +{ +if (a->hi < b->hi) return -1; +if (a->hi > b->hi) return +1; +if (a->lo < b->lo) return -1; +if (a->lo > b->lo) return +1; +return 0; +} + +/* Double precision multiply - returns 70b result in a'fmq */ + +void dp_mul (UFP *a, UFP *b) +{ +int32 i; + +fmq.hi = a->hi; /* FMQ <- a */ +fmq.lo = a->lo; +a->hi = a->lo = 0; /* a <- 0 */ +if ((fmq.hi | fmq.lo) == 0) return; +if ((b->hi | b->lo) == 0) { + fmq.hi = fmq.lo = 0; + return; + } +for (i = 0; i < 35; i++) { /* 35 iterations */ + if (fmq.lo & 1) dp_add (a, b); /* FMQ<35>? a += b */ + dp_rsh_1 (a, &fmq); /* rsh a'FMQ */ + } +return; +} + +/* Double (quad) precision left shift - returns 72b (143b) result */ + +void dp_lsh_1 (UFP *a, UFP *b) +{ +int32 t = b? b->hi: 0; + +a->hi = (a->hi << 1) | ((a->lo >> 17) & 1); +a->lo = ((a->lo << 1) | ((t >> 16) & 1)) & UFP_FL_MASK; +if (b) { + b->hi = ((b->hi << 1) | ((b->lo >> 17) & 1)) & UFP_FH_MASK; + b->lo = (b->lo << 1) & UFP_FL_MASK; + } +return; +} + +/* Double (quad) precision right shift - returns 71b (142b) result */ + +void dp_rsh_1 (UFP *a, UFP *b) +{ +if (b) { + b->lo = (b->lo >> 1) | ((b->hi & 1) << 17); + b->hi = (b->hi >> 1) | ((a->lo & 1) << 16); + } +a->lo = (a->lo >> 1) | ((a->hi & 1) << 17); +a->hi = a->hi >> 1; +return; +} + +/* Double precision denormalize and round - returns 71b result */ + +void dp_dnrm_r (int32 ir, UFP *a, int32 sc) +{ +int32 i; + +if (sc <= 0) return; /* legit? */ +for (i = 0; i < sc; i++) dp_rsh_1 (a, &fmq); /* dnorm to fmq */ +if (!(ir & FI_NORND) && (fmq.hi & UFP_FH_NORM)) /* round & fmq<1>? */ + dp_inc (a); /* incr a */ +return; +} + +/* Double precision swap */ + +void dp_swap (UFP *a, UFP *b) +{ +int32 t; + +t = a->hi; /* swap fractions */ +a->hi = b->hi; +b->hi = t; +t = a->lo; +a->lo = b->lo; +b->lo = t; +return; +} + +/* Support routines */ + +void fp15_asign (int32 fir, UFP *a) +{ +int32 sgnop = FI_GETSGNOP (fir); + +switch (sgnop) { /* modify FMA sign */ + + case 1: + a->sign = 0; + break; + + case 2: + a->sign = 1; + break; + + case 3: + a->sign = a->sign ^ 1; + break; + + default: + break; + } + +return; +} + +/* FP15 normalization and rounding + + - Do normalization if enabled (NOR phase, part 1) + Normalization also does zero detect + - Do rounding if enabled (NOR phase, part 2) */ + +t_stat fp15_norm (int32 ir, UFP *a, UFP *b, t_bool rnd) +{ +a->hi = a->hi & UFP_FH_MASK; /* mask a */ +a->lo = a->lo & UFP_FL_MASK; +if (b) { /* if b, mask */ + b->hi = b->hi & UFP_FH_MASK; + b->lo = b->lo & UFP_FL_MASK; + } +if (!(ir & FI_NONORM)) { /* norm enabled? */ + if ((a->hi | a->lo) || (b && (b->hi | b->lo))) { /* frac != 0? */ + while ((a->hi & UFP_FH_NORM) == 0) { /* until norm */ + dp_lsh_1 (a, b); /* lsh a'b, no cry */ + a->exp = a->exp - 1; /* decr exp */ + } + } + else a->sign = a->exp = 0; /* true zero */ + } +if (rnd && b && (b->hi & UFP_FH_NORM)) { /* rounding? */ + fguard = 1; /* set guard */ + if (!(ir & FI_NORND)) { /* round enabled? */ + dp_inc (a); /* add 1 */ + if (a->hi & UFP_FH_CARRY) { /* carry out? */ + a->hi = UFP_FH_NORM; /* set hi bit */ + a->exp = a->exp + 1; /* incr exp */ + } + } + } +if (a->exp > (int32) 0377777) return FP_OVF; /* overflow? */ +if (a->exp < (int32) -0400000) return FP_UNF; /* underflow? */ +return FP_OK; +} + +/* Exception */ + +t_stat fp15_exc (t_stat sta) +{ +int32 ma, mb; + +if (sta == FP_MM) trap_pending = 0; /* if mm, kill trap */ +ma = (jea & JEA_EAMASK) + sta - 1; /* JEA address */ +PCQ_ENTRY; /* record branch */ +PC = Incr_addr (PC); /* PC+1 for "JMS" */ +mb = Jms_word (usmd); /* form JMS word */ +if (Write (ma, mb, WR)) return SCPE_OK; /* store */ +PC = (ma + 1) & IAMASK; /* new PC */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat fp15_reset (DEVICE *dptr) +{ +jea = 0; +fir = 0; +fguard = 0; +fma.exp = fma.hi = fma.lo = fma.sign = 0; +fmb.exp = fmb.hi = fmb.lo = fmb.sign = 0; +fmq.exp = fmq.hi = fmq.lo = fmq.sign = 0; +return SCPE_OK; +} diff --git a/PDP18B/pdp18b_lp.c b/PDP18B/pdp18b_lp.c new file mode 100644 index 0000000..411b3c1 --- /dev/null +++ b/PDP18B/pdp18b_lp.c @@ -0,0 +1,851 @@ +/* pdp18b_lp.c: 18b PDP's line printer simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lp62 (PDP-4) Type 62 line printer + lp647 (PDP-7,9) Type 647 line printer + lp09 (PDP-9,15) LP09 line printer + lp15 (PDP-15) LP15 line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + 11-Jun-06 RMS Made character translation table global scope + 14-Jan-04 RMS Revised IO device call interface + 23-Jul-03 RMS Fixed overprint bug in Type 62 + 25-Apr-03 RMS Revised for extended file support + 05-Feb-03 RMS Added LP09, fixed conditionalization + 05-Oct-02 RMS Added DIB, device number support + 30-May-02 RMS Widened POS to 32b + 03-Feb-02 RMS Fixed typo (found by Robert Alan Byer) + 25-Nov-01 RMS Revised interrupt structure + 19-Sep-01 RMS Fixed bug in 647 + 13-Feb-01 RMS Revised for register arrays + 15-Feb-01 RMS Fixed 3 cycle data break sequence + 30-Oct-00 RMS Standardized register naming + 20-Aug-98 RMS Fixed compilation problem in BeOS + 03-Jan-97 RMS Fixed bug in Type 62 state handling +*/ + +#include "pdp18b_defs.h" +extern int32 int_hwre[API_HLVL+1]; +const char fio_to_asc[64] = { + ' ','1','2','3','4','5','6','7','8','9','\'','~','#','V','^','<', + '0','/','S','T','U','V','W','X','Y','Z','"',',','>','^','-','?', + 'o','J','K','L','M','N','O','P','Q','R','$','=','-',')','-','(', + '_','A','B','C','D','E','F','G','H','I','*','.','+',']','|','[' + }; + +#if defined (TYPE62) + +/* Type 62 line printer */ + +#define LP62_BSIZE 120 /* line size */ +#define BPTR_MAX 40 /* pointer max */ +#define BPTR_MASK 077 /* buf ptr max */ + +int32 lp62_spc = 0; /* print vs spc */ +int32 lp62_ovrpr = 0; /* overprint */ +int32 lp62_stopioe = 0; +int32 lp62_bp = 0; /* buffer ptr */ +char lp62_buf[LP62_BSIZE + 1] = { 0 }; +static const char *lp62_cc[] = { + "\n", + "\n\n", + "\n\n\n", + "\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f" + }; + +DEVICE lp62_dev; +int32 lp62_65 (int32 dev, int32 pulse, int32 dat); +int32 lp62_66 (int32 dev, int32 pulse, int32 dat); +int32 lp62_iors (void); +t_stat lp62_svc (UNIT *uptr); +t_stat lp62_reset (DEVICE *dptr); + +/* Type 62 LPT data structures + + lp62_dev LPT device descriptor + lp62_unit LPT unit + lp62_reg LPT register list +*/ + +DIB lp62_dib = { DEV_LPT, 2, &lp62_iors, { &lp62_65, &lp62_66 } }; + +UNIT lp62_unit = { + UDATA (&lp62_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lp62_reg[] = { + { ORDATA (BUF, lp62_unit.buf, 8) }, + { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, + { FLDATA (DONE, int_hwre[API_LPT], INT_V_LPT) }, + { FLDATA (SPC, int_hwre[API_LPTSPC], INT_V_LPTSPC) }, + { DRDATA (BPTR, lp62_bp, 6) }, + { ORDATA (STATE, lp62_spc, 6), REG_HRO }, + { FLDATA (OVRPR, lp62_ovrpr, 0), REG_HRO }, + { DRDATA (POS, lp62_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lp62_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lp62_stopioe, 0) }, + { BRDATA (LBUF, lp62_buf, 8, 8, LP62_BSIZE) }, + { ORDATA (DEVNO, lp62_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB lp62_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE lp62_dev = { + "LPT", &lp62_unit, lp62_reg, lp62_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lp62_reset, + NULL, NULL, NULL, + &lp62_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 lp62_65 (int32 dev, int32 pulse, int32 dat) +{ +int32 i; + +if ((pulse & 01) && TST_INT (LPT)) dat = IOT_SKP | dat; /* LPSF */ +if (pulse & 02) { + int32 sb = pulse & 060; /* subopcode */ + if (sb == 000) CLR_INT (LPT); /* LPCF */ + if ((sb == 040) && (lp62_bp < BPTR_MAX)) { /* LPLD */ + i = lp62_bp * 3; /* cvt to chr ptr */ + lp62_buf[i] = fio_to_asc[(dat >> 12) & 077]; + lp62_buf[i + 1] = fio_to_asc[(dat >> 6) & 077]; + lp62_buf[i + 2] = fio_to_asc[dat & 077]; + lp62_bp = (lp62_bp + 1) & BPTR_MASK; + } + } +if (pulse & 04) { /* LPSE */ + lp62_spc = 0; /* print */ + sim_activate (&lp62_unit, lp62_unit.wait); /* activate */ + } +return dat; +} + +int32 lp62_66 (int32 dev, int32 pulse, int32 dat) +{ +if ((pulse & 01) && TST_INT (LPTSPC)) /* LSSF */ + dat = IOT_SKP | dat; +if (pulse & 02) CLR_INT (LPTSPC); /* LSCF */ +if (pulse & 04) { /* LSPR */ + lp62_spc = 020 | (dat & 07); /* space */ + sim_activate (&lp62_unit, lp62_unit.wait); /* activate */ + } +return dat; +} + +/* Unit service, action based on lp62_spc + + lp62_spc = 0 write buffer to file, set overprint + lp62_spc = 2x space command x, clear overprint +*/ + +t_stat lp62_svc (UNIT *uptr) +{ +int32 i; + +if (lp62_spc) { /* space? */ + SET_INT (LPTSPC); /* set flag */ + if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lp62_stopioe, SCPE_UNATT); + fputs (lp62_cc[lp62_spc & 07], uptr->fileref); /* print cctl */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + lp62_ovrpr = 0; /* clear overprint */ + } +else { + SET_INT (LPT); /* print */ + if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (lp62_stopioe, SCPE_UNATT); + if (lp62_ovrpr) fputc ('\r', uptr->fileref); /* overprint? */ + fputs (lp62_buf, uptr->fileref); /* print buffer */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* test error */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + lp62_bp = 0; + for (i = 0; i <= LP62_BSIZE; i++) lp62_buf[i] = 0; /* clear buffer */ + lp62_ovrpr = 1; /* set overprint */ + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lp62_reset (DEVICE *dptr) +{ +int32 i; + +CLR_INT (LPT); /* clear intrs */ +CLR_INT (LPTSPC); +sim_cancel (&lp62_unit); /* deactivate unit */ +lp62_bp = 0; /* clear buffer ptr */ +for (i = 0; i <= LP62_BSIZE; i++) lp62_buf[i] = 0; /* clear buffer */ +lp62_spc = 0; /* clear state */ +lp62_ovrpr = 0; /* clear overprint */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 lp62_iors (void) +{ +return (TST_INT (LPT)? IOS_LPT: 0) | + (TST_INT (LPTSPC)? IOS_LPT1: 0); +} + +#endif + +#if defined (TYPE647) + +/* Type 647 line printer */ + +#define LP647_BSIZE 120 /* line size */ + +int32 lp647_don = 0; /* ready */ +int32 lp647_ie = 1; /* int enable */ +int32 lp647_err = 0; /* error */ +int32 lp647_iot = 0; /* saved state */ +int32 lp647_stopioe = 0; +int32 lp647_bp = 0; /* buffer ptr */ +char lp647_buf[LP647_BSIZE] = { 0 }; +static const char *lp647_cc[] = { + "\n", + "\n\n", + "\n\n\n", + "\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f" + }; + +DEVICE lp647_dev; +int32 lp647_65 (int32 dev, int32 pulse, int32 dat); +int32 lp647_66 (int32 dev, int32 pulse, int32 dat); +int32 lp647_iors (void); +t_stat lp647_svc (UNIT *uptr); +t_stat lp647_reset (DEVICE *dptr); +t_stat lp647_attach (UNIT *uptr, char *cptr); +t_stat lp647_detach (UNIT *uptr); + +/* Type 647 LPT data structures + + lp647_dev LPT device descriptor + lp647_unit LPT unit + lp647_reg LPT register list +*/ + +DIB lp647_dib = { DEV_LPT, 2, &lp647_iors, { &lp647_65, &lp647_66 } }; + +UNIT lp647_unit = { + UDATA (&lp647_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lp647_reg[] = { + { ORDATA (BUF, lp647_unit.buf, 8) }, + { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, + { FLDATA (DONE, lp647_don, 0) }, +#if defined (PDP9) + { FLDATA (ENABLE, lp647_ie, 0) }, +#endif + { FLDATA (ERR, lp647_err, 0) }, + { DRDATA (BPTR, lp647_bp, 7) }, + { ORDATA (SCMD, lp647_iot, 6), REG_HRO }, + { DRDATA (POS, lp647_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lp647_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lp647_stopioe, 0) }, + { BRDATA (LBUF, lp647_buf, 8, 8, LP647_BSIZE) }, + { ORDATA (DEVNO, lp647_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB lp647_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE lp647_dev = { + "LPT", &lp647_unit, lp647_reg, lp647_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lp647_reset, + NULL, &lp647_attach, &lp647_detach, + &lp647_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 lp647_65 (int32 dev, int32 pulse, int32 dat) +{ +int32 i, sb; + +sb = pulse & 060; /* subcode */ +if ((pulse & 01) && lp647_don) dat = IOT_SKP | dat; /* LPSF */ +if (pulse & 02) { /* pulse 02 */ + lp647_don = 0; /* clear done */ + CLR_INT (LPT); /* clear int req */ + if (sb == 000) { /* LPCB */ + for (i = 0; i < LP647_BSIZE; i++) lp647_buf[i] = 0; + lp647_bp = 0; /* reset buf ptr */ + lp647_don = 1; /* set done */ + if (lp647_ie) SET_INT (LPT); /* set int */ + } + } +if (pulse & 004) { /* LPDI */ + switch (sb) { /* case on subcode */ + + case 000: /* LPDI */ +#if defined (PDP9) + lp647_ie = 0; /* clear int enable */ + CLR_INT (LPT); /* clear int req */ +#endif + break; + + case 040: /* LPB3 */ + if (lp647_bp < LP647_BSIZE) { + lp647_buf[lp647_bp] = lp647_buf[lp647_bp] | ((dat >> 12) & 077); + lp647_bp = lp647_bp + 1; + } + + case 020: /* LPB2 */ + if (lp647_bp < LP647_BSIZE) { + lp647_buf[lp647_bp] = lp647_buf[lp647_bp] | ((dat >> 6) & 077); + lp647_bp = lp647_bp + 1; + } + + case 060: /* LPB1 */ + if (lp647_bp < LP647_BSIZE) { + lp647_buf[lp647_bp] = lp647_buf[lp647_bp] | (dat & 077); + lp647_bp = lp647_bp + 1; + } + lp647_don = 1; /* set done */ + if (lp647_ie) SET_INT (LPT); /* set int */ + break; + } /* end case */ + } +return dat; +} + +int32 lp647_66 (int32 dev, int32 pulse, int32 dat) +{ +if ((pulse & 01) && lp647_err) dat = IOT_SKP | dat; /* LPSE */ +if (pulse & 02) { /* LPCF */ + lp647_don = 0; /* clear done, int */ + CLR_INT (LPT); + } +if (pulse & 04) { + if ((pulse & 060) < 060) { /* LPLS, LPPB, LPPS */ + lp647_iot = (pulse & 060) | (dat & 07); /* save parameters */ + sim_activate (&lp647_unit, lp647_unit.wait); /* activate */ + } +#if defined (PDP9) + else { /* LPEI */ + lp647_ie = 1; /* set int enable */ + if (lp647_don) SET_INT (LPT); + } +#endif + } +return dat; +} + +/* Unit service. lp647_iot specifies the action to be taken + + lp647_iot = 0x print only + lp647_iot = 2x space only, x is spacing command + lp647_iot = 4x print then space, x is spacing command +*/ + +t_stat lp647_svc (UNIT *uptr) +{ +int32 i; +char pbuf[LP647_BSIZE + 2]; + +lp647_don = 1; +if (lp647_ie) SET_INT (LPT); /* set flag */ +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + lp647_err = 1; /* set error */ + return IORETURN (lp647_stopioe, SCPE_UNATT); + } +if ((lp647_iot & 020) == 0) { /* print? */ + for (i = 0; i < lp647_bp; i++) /* translate buffer */ + pbuf[i] = lp647_buf[i] | ((lp647_buf[i] >= 040)? 0: 0100); + if ((lp647_iot & 060) == 0) pbuf[lp647_bp++] = '\r'; + pbuf[lp647_bp++] = 0; /* append nul */ + for (i = 0; i < LP647_BSIZE; i++) lp647_buf[i] = 0; /* clear buffer */ + fputs (pbuf, uptr->fileref); /* print buffer */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + lp647_bp = 0; + return SCPE_IOERR; + } + lp647_bp = 0; /* clear buffer ptr */ + } +if (lp647_iot & 060) { /* space? */ + fputs (lp647_cc[lp647_iot & 07], uptr->fileref); /* write cctl */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lp647_reset (DEVICE *dptr) +{ +int32 i; + +lp647_don = 0; /* clear done */ +lp647_err = (lp647_unit.flags & UNIT_ATT)? 0: 1; /* clr/set error */ +lp647_ie = 1; /* set enable */ +CLR_INT (LPT); /* clear int */ +sim_cancel (&lp647_unit); /* deactivate unit */ +lp647_bp = 0; /* clear buffer ptr */ +lp647_iot = 0; /* clear state */ +for (i = 0; i < LP647_BSIZE; i++) lp647_buf[i] = 0; /* clear buffer */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 lp647_iors (void) +{ +return (lp647_don? IOS_LPT: 0) | (lp647_err? IOS_LPT1: 0); +} + +/* Attach routine */ + +t_stat lp647_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +lp647_err = (lp647_unit.flags & UNIT_ATT)? 0: 1; /* clr/set error */ +return reason; +} + +/* Detach routine */ + +t_stat lp647_detach (UNIT *uptr) +{ +lp647_err = 1; +return detach_unit (uptr); +} + +#endif + +#if defined (LP09) + +/* LP09 line printer */ + +#define LP09_BSIZE 132 /* line size */ + +int32 lp09_don = 0; /* ready */ +int32 lp09_err = 0; /* error */ +int32 lp09_ie = 1; /* int enable */ +int32 lp09_stopioe = 0; +DEVICE lp09_dev; + +int32 lp09_66 (int32 dev, int32 pulse, int32 dat); +int32 lp09_iors (void); +t_stat lp09_svc (UNIT *uptr); +t_stat lp09_reset (DEVICE *dptr); +t_stat lp09_attach (UNIT *uptr, char *cptr); +t_stat lp09_detach (UNIT *uptr); + +/* LP09 LPT data structures + + lp09_dev LPT device descriptor + lp09_unit LPT unit + lp09_reg LPT register list +*/ + +DIB lp09_dib = { DEV_LPT, 2, &lp09_iors, { NULL, &lp09_66 } }; + +UNIT lp09_unit = { + UDATA (&lp09_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lp09_reg[] = { + { ORDATA (BUF, lp09_unit.buf, 7) }, + { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, + { FLDATA (DONE, lp09_don, 0) }, + { FLDATA (ENABLE, lp09_ie, 0) }, + { FLDATA (ERR, lp09_err, 0) }, + { DRDATA (POS, lp09_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lp09_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lp09_stopioe, 0) }, + { ORDATA (DEVNO, lp09_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB lp09_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE lp09_dev = { + "LP9", &lp09_unit, lp09_reg, lp09_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lp09_reset, + NULL, &lp09_attach, &lp09_detach, + &lp09_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 lp09_66 (int32 dev, int32 pulse, int32 dat) +{ +int32 sb = pulse & 060; /* subopcode */ + +if (pulse & 001) { + if ((sb == 000) && lp09_don) dat = IOT_SKP | dat; /* LSDF */ + if ((sb == 020) && lp09_err) dat = IOT_SKP | dat; /* LSEF */ + } +if (pulse & 002) { + if (sb == 000) { /* LSCF */ + lp09_don = 0; /* clear done, int */ + CLR_INT (LPT); + } + else if (sb == 020) { /* LPLD */ + lp09_don = 0; /* clear done, int */ + CLR_INT (LPT); + lp09_unit.buf = dat & 0177; /* load char */ + if ((lp09_unit.buf == 015) || (lp09_unit.buf == 014) || + (lp09_unit.buf == 012)) + sim_activate (&lp09_unit, lp09_unit.wait); + else dat = dat | (lp09_svc (&lp09_unit) << IOT_V_REASON); + } + } +if (pulse & 004) { + if (sb == 000) { /* LIOF */ + lp09_ie = 0; /* clear int enab */ + CLR_INT (LPT); /* clear int */ + } + else if (sb == 040) { /* LION */ + lp09_ie = 1; /* set int enab */ + if (lp09_don) SET_INT (LPT); /* if done, set int */ + } + } +return dat; +} + +/* Unit service */ + +t_stat lp09_svc (UNIT *uptr) +{ +int32 c; + +lp09_don = 1; /* set done */ +if (lp09_ie) SET_INT (LPT); /* int enb? req int */ +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + lp09_err = 1; /* set error */ + return IORETURN (lp09_stopioe, SCPE_UNATT); + } +c = uptr->buf & 0177; /* get char */ +if ((c == 0) || (c == 0177)) return SCPE_OK; /* skip NULL, DEL */ +fputc (c, uptr->fileref); /* print char */ +uptr->pos = ftell (uptr->fileref); /* update position */ +if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lp09_reset (DEVICE *dptr) +{ +lp09_don = 0; /* clear done */ +lp09_err = (lp09_unit.flags & UNIT_ATT)? 0: 1; /* compute error */ +lp09_ie = 1; /* set enable */ +CLR_INT (LPT); /* clear int */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 lp09_iors (void) +{ +return (lp09_don? IOS_LPT: 0); +} + +/* Attach routine */ + +t_stat lp09_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +lp09_err = (lp09_unit.flags & UNIT_ATT)? 0: 1; /* clr/set error */ +return reason; +} + +/* Detach routine */ + +t_stat lp09_detach (UNIT *uptr) +{ +lp09_err = 1; +return detach_unit (uptr); +} + +#endif + +#if defined (LP15) + +/* LP15 line printer */ + +#define LP15_BSIZE 132 /* line size */ +#define LPT_WC 034 /* word count */ +#define LPT_CA 035 /* current addr */ + +/* Status register */ + +#define STA_ERR 0400000 /* error */ +#define STA_ALM 0200000 /* alarm */ +#define STA_OVF 0100000 /* line overflow */ +#define STA_IHT 0040000 /* illegal HT */ +#define STA_BUSY 0020000 /* busy */ +#define STA_DON 0010000 /* done */ +#define STA_ILK 0004000 /* interlock */ +#define STA_EFLGS (STA_ALM | STA_OVF | STA_IHT | STA_ILK) +#define STA_CLR 0003777 /* always clear */ + +extern int32 M[]; +int32 lp15_sta = 0; +int32 lp15_ie = 1; +int32 lp15_stopioe = 0; +int32 lp15_mode = 0; +int32 lp15_lc = 0; +int32 lp15_bp = 0; +char lp15_buf[LP15_BSIZE + 1] = { 0 }; + +DEVICE lp15_dev; +int32 lp15_65 (int32 dev, int32 pulse, int32 dat); +int32 lp15_66 (int32 dev, int32 pulse, int32 dat); +int32 lp15_iors (void); +t_stat lp15_svc (UNIT *uptr); +t_stat lp15_reset (DEVICE *dptr); + +int32 lp15_updsta (int32 new); + +/* LP15 LPT data structures + + lp15_dev LPT device descriptor + lp15_unit LPT unit + lp15_reg LPT register list +*/ + +DIB lp15_dib = { DEV_LPT, 2, &lp15_iors, { &lp15_65, &lp15_66 } }; + +UNIT lp15_unit = { + UDATA (&lp15_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lp15_reg[] = { + { ORDATA (STA, lp15_sta, 18) }, + { ORDATA (CA, M[LPT_CA], 18) }, + { FLDATA (INT, int_hwre[API_LPT], INT_V_LPT) }, + { FLDATA (ENABLE, lp15_ie, 0) }, + { DRDATA (LCNT, lp15_lc, 9) }, + { DRDATA (BPTR, lp15_bp, 8) }, + { FLDATA (MODE, lp15_mode, 0) }, + { DRDATA (POS, lp15_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lp15_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lp15_stopioe, 0) }, + { BRDATA (LBUF, lp15_buf, 8, 8, LP15_BSIZE) }, + { ORDATA (DEVNO, lp15_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB lp15_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE lp15_dev = { + "LPT", &lp15_unit, lp15_reg, lp15_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lp15_reset, + NULL, NULL, NULL, + &lp15_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 lp15_65 (int32 dev, int32 pulse, int32 dat) +{ +int32 header, sb; + +sb = pulse & 060; /* subopcode */ +if (pulse & 01) { + if ((sb == 000) && (lp15_sta & (STA_ERR | STA_DON))) /* LPSF */ + dat = IOT_SKP | dat; + else if ((sb == 020) || (sb == 040)) { /* LPP1, LPPM */ + sim_activate (&lp15_unit, lp15_unit.wait); /* activate */ + header = M[(M[LPT_CA] + 1) & AMASK]; /* get first word */ + M[LPT_CA] = (M[LPT_CA] + 2) & DMASK; + lp15_mode = header & 1; /* mode */ + if (sb == 040) lp15_lc = 1; /* line count */ + else lp15_lc = (header >> 9) & 0377; + if (lp15_lc == 0) lp15_lc = 256; + lp15_bp = 0; /* reset buf ptr */ + } + else if (sb == 060) lp15_ie = 0; /* LPDI */ + } +if ((pulse & 02) && (sb == 040)) dat = dat | lp15_updsta (0); /* LPOS, LPRS */ +if ((pulse & 04) && (sb == 040)) lp15_ie = 1; /* LPEI */ +lp15_updsta (0); /* update status */ +return dat; +} + +int32 lp15_66 (int32 dev, int32 pulse, int32 dat) +{ +if (pulse == 021) lp15_sta = lp15_sta & ~STA_DON; /* LPCD */ +if (pulse == 041) lp15_sta = 0; /* LPCF */ +lp15_updsta (0); /* update status */ +return dat; +} + +/* Unit service */ + +t_stat lp15_svc (UNIT *uptr) +{ +int32 i, ccnt, more, w0, w1; +char c[5]; +static const char *ctrl[040] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, "\n", "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\f", "\r", NULL, NULL, + "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n", + "\n\n", "\n\n\n", "\n", + "\n\n\n\n\n\n\n\n\n\n", NULL, NULL, NULL, + NULL, NULL, NULL, "\r", NULL, NULL, NULL, NULL + }; + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + lp15_updsta (STA_DON | STA_ALM); /* set done, err */ + return IORETURN (lp15_stopioe, SCPE_UNATT); + } + +for (more = 1; more != 0; ) { /* loop until ctrl */ + w0 = M[(M[LPT_CA] + 1) & AMASK]; /* get first word */ + w1 = M[(M[LPT_CA] + 2) & AMASK]; /* get second word */ + M[LPT_CA] = (M[LPT_CA] + 2) & DMASK; /* advance mem addr */ + if (lp15_mode) { /* unpacked? */ + c[0] = w0 & 0177; + c[1] = w1 & 0177; + ccnt = 2; + } + else { /* packed */ + c[0] = (w0 >> 11) & 0177; + c[1] = (w0 >> 4) & 0177; + c[2] = (((w0 << 3) | (w1 >> 15))) & 0177; + c[3] = (w1 >> 8) & 0177; + c[4] = (w1 >> 1) & 0177; + ccnt = 5; + } + for (i = 0; i < ccnt; i++) { /* loop through */ + if ((c[i] <= 037) && ctrl[c[i]]) { /* control char? */ + lp15_buf[lp15_bp] = 0; /* append nul */ + fputs (lp15_buf, uptr->fileref); /* print line */ + fputs (ctrl[c[i]], uptr->fileref); /* space */ + uptr->pos = ftell (uptr->fileref); + if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + lp15_bp = 0; + lp15_updsta (STA_DON | STA_ALM); + return SCPE_IOERR; + } + lp15_bp = more = 0; + } + else { + if (lp15_bp < LP15_BSIZE) lp15_buf[lp15_bp++] = c[i]; + else lp15_sta = lp15_sta | STA_OVF; + } + } + } + +lp15_lc = lp15_lc - 1; /* decr line count */ +if (lp15_lc) sim_activate (&lp15_unit, uptr->wait); /* more to do? */ +else lp15_updsta (STA_DON); /* no, set done */ +return SCPE_OK; +} + +/* Update status */ + +int32 lp15_updsta (int32 new) +{ +lp15_sta = (lp15_sta | new) & ~(STA_CLR | STA_ERR | STA_BUSY); +if (lp15_sta & STA_EFLGS) lp15_sta = lp15_sta | STA_ERR; /* update errors */ +if (sim_is_active (&lp15_unit)) lp15_sta = lp15_sta | STA_BUSY; +if (lp15_ie && (lp15_sta & STA_DON)) SET_INT (LPT); +else CLR_INT (LPT); /* update int */ +return lp15_sta; +} + +/* Reset routine */ + +t_stat lp15_reset (DEVICE *dptr) +{ +lp15_mode = lp15_lc = lp15_bp = 0; /* clear controls */ +sim_cancel (&lp15_unit); /* deactivate unit */ +lp15_sta = 0; /* clear status */ +lp15_ie = 1; /* enable interrupts */ +lp15_updsta (0); /* update status */ +return SCPE_OK; +} + +/* IORS routine */ + +int32 lp15_iors (void) +{ +return ((lp15_sta & STA_DON)? IOS_LPT: 0); +} + +#endif diff --git a/PDP18B/pdp18b_mt.c b/PDP18B/pdp18b_mt.c new file mode 100644 index 0000000..073c48e --- /dev/null +++ b/PDP18B/pdp18b_mt.c @@ -0,0 +1,515 @@ +/* pdp18b_mt.c: 18b PDP magnetic tape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt (PDP-9) TC59 magtape + (PDP-15) TC59D magtape + + 16-Feb-06 RMS Added tape capacity checking + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 18-Mar-05 RMS Added attached test to detach routine + 14-Jan-04 RMS Revised IO device call interface + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 04-Mar-03 RMS Fixed bug in MTTR + 01-Mar-03 RMS Fixed bug in interrupt handling + Revised for magtape library + 02-Feb-03 RMS Revised IOT decoding + 30-Oct-02 RMS Revised BOT handling, added error record handling + 05-Oct-02 RMS Added DIB, device number support + Revamped error recovery + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length test + 06-Jan-02 RMS Revised enabled/disable support + 29-Nov-01 RMS Added read only unit support + 25-Nov-01 RMS Revised interrupt structure + Changed UST, POS, FLG to arrays + 26-Apr-01 RMS Added device enable/disable support + 15-Feb-01 RMS Fixed 3-cycle data break sequence + 04-Oct-98 RMS V2.4 magtape format + 22-Jan-97 RMS V2.3 magtape format + 29-Jun-96 RMS Added unit enable/disable support + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32 byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "pdp18b_defs.h" +#include "sim_tape.h" + +#define MT_NUMDR 8 /* #drives */ +#define USTAT u3 /* unit status */ +#define MT_MAXFR (1 << 16) /* max record length */ +#define MT_WC 032 /* word count */ +#define MT_CA 033 /* current addr */ +#define WC_SIZE (1 << 12) /* max word count */ +#define WC_MASK (WC_SIZE - 1) + +/* Command/unit - mt_cu */ + +#define CU_V_UNIT 15 /* unit */ +#define CU_M_UNIT 07 +#define CU_PARITY 0040000 /* parity select */ +#define CU_DUMP 0020000 /* dump mode */ +#define CU_ERASE 0010000 /* ext rec gap */ +#define CU_V_CMD 9 /* command */ +#define CU_M_CMD 07 +#define FN_NOP 00 +#define FN_REWIND 01 +#define FN_READ 02 +#define FN_CMPARE 03 +#define FN_WRITE 04 +#define FN_WREOF 05 +#define FN_SPACEF 06 +#define FN_SPACER 07 +#define CU_IE 0000400 /* interrupt enable */ +#define CU_V_TYPE 6 /* drive type */ +#define CU_M_TYPE 03 +#define TY_9TK 3 +#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) +#define GET_CMD(x) (((x) >> CU_V_CMD) & CU_M_CMD) +#define GET_TYPE(x) (((x) >> CU_V_TYPE) & CU_M_TYPE) +#define PACKED(x) (((x) & CU_DUMP) || (GET_TYPE (x) != TY_9TK)) + +/* Status - stored in mt_sta or (*) uptr->USTAT */ + +#define STA_ERR 0400000 /* error */ +#define STA_REW 0200000 /* *rewinding */ +#define STA_BOT 0100000 /* *start of tape */ +#define STA_ILL 0040000 /* illegal cmd */ +#define STA_PAR 0020000 /* parity error */ +#define STA_EOF 0010000 /* *end of file */ +#define STA_EOT 0004000 /* *end of tape */ +#define STA_CPE 0002000 /* compare error */ +#define STA_RLE 0001000 /* rec lnt error */ +#define STA_DLT 0000400 /* data late */ +#define STA_BAD 0000200 /* bad tape */ +#define STA_DON 0000100 /* done */ + +#define STA_CLR 0000077 /* always clear */ +#define STA_DYN (STA_REW | STA_BOT | STA_EOF | STA_EOT) + /* kept in USTAT */ + +extern int32 M[]; +extern int32 int_hwre[API_HLVL+1]; +extern UNIT cpu_unit; + +int32 mt_cu = 0; /* command/unit */ +int32 mt_sta = 0; /* status register */ +int32 mt_time = 10; /* record latency */ +int32 mt_stopioe = 1; /* stop on error */ +int32 mt_log = 0; +uint8 *mtxb = NULL; /* transfer buffer */ + +DEVICE mt_dev; +int32 mt (int32 dev, int32 pulse, int32 dat); +int32 mt_iors (void); +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_detach (UNIT *uptr); +int32 mt_updcsta (UNIT *uptr, int32 val); +t_stat mt_map_err (UNIT *uptr, t_stat st); +UNIT *mt_busy (void); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +DIB mt_dib = { DEV_MT, 1, &mt_iors, { &mt } }; + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } + }; + +REG mt_reg[] = { + { ORDATA (STA, mt_sta, 18) }, + { ORDATA (CMD, mt_cu, 18) }, + { ORDATA (WC, M[MT_WC], 18) }, + { ORDATA (CA, M[MT_CA], 18) }, + { FLDATA (INT, int_hwre[API_MTA], INT_V_MTA) }, + { FLDATA (STOP_IOE, mt_stopioe, 0) }, + { DRDATA (TIME, mt_time, 24), PV_LEFT }, + { URDATA (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0) }, + { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR, PV_LEFT | REG_RO) }, + { FLDATA (LOG, mt_log, 0), REG_HIDDEN }, + { ORDATA (DEVNO, mt_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_devno, &show_devno, NULL }, + { 0 } + }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &mt_detach, + &mt_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 mt (int32 dev, int32 pulse, int32 dat) +{ +int32 f, sb; +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +mt_updcsta (uptr, 0); /* update status */ +sb = pulse & 060; /* subop */ +if (pulse & 01) { + if ((sb == 000) && (uptr->flags & UNIT_ATT) && /* MTTR */ + !sim_is_active (uptr)) + dat = IOT_SKP | dat; + else if ((sb == 020) && !mt_busy ()) /* MTCR */ + dat = IOT_SKP | dat; + else if ((sb == 040) && (mt_sta & (STA_ERR | STA_DON))) /* MTSF */ + dat = IOT_SKP | dat; + } +if ((pulse & 06) && mt_log) + printf ("[MT%d: IOT=%o, AC=%o, sta=%o]\n", + GET_UNIT (mt_cu), 0707300 + pulse, dat, mt_sta); +if (pulse & 02) { + if (sb == 000) dat = dat | (mt_cu & 0777700); /* MTRC */ + else if (sb == 020) { /* MTAF, MTLC */ + if (!mt_busy ()) mt_cu = mt_sta = 0; /* if not busy, clr */ + mt_sta = mt_sta & ~(STA_ERR | STA_DON); /* clear flags */ + } + else if (sb == 040) dat = dat | mt_sta; /* MTRS */ + } +if (pulse & 04) { + if (sb == 000) { /* MTGO */ + f = GET_CMD (mt_cu); /* get function */ + if (mt_busy () || + sim_is_active (uptr) || + (f == FN_NOP) || + (((f == FN_SPACER) || (f == FN_REWIND)) && (uptr->USTAT & STA_BOT)) || + (((f == FN_WRITE) || (f == FN_WREOF)) && sim_tape_wrp (uptr)) || + ((uptr->flags & UNIT_ATT) == 0)) + mt_sta = mt_sta | STA_ILL | STA_ERR; /* set illegal op */ + else { + if (f == FN_REWIND) uptr->USTAT = STA_REW; /* rewind? */ + else mt_sta = uptr->USTAT = 0; /* no, clear status */ + sim_activate (uptr, mt_time); /* start io */ + } + } + if (sb == 020) /* MTCM, MTLC */ + mt_cu = (mt_cu & 0770700) | (dat & 0777700); /* load status */ + } +mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ +return dat; +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt +*/ + +t_stat mt_svc (UNIT *uptr) +{ +int32 c, c1, c2, c3, f, i, p, u; +int32 wc, xma; +t_mtrlnt tbc, cbc; +t_bool passed_eot; +t_stat st, r = SCPE_OK; + +u = (int32) (uptr - mt_dev.units); /* get unit number */ +f = GET_CMD (mt_cu); /* get command */ +wc = WC_SIZE - (M[MT_WC] & WC_MASK); /* word count is 12b */ + +if (uptr->USTAT & STA_REW) { /* rewind? */ + sim_tape_rewind (uptr); /* rewind tape */ + if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_BOT; + else uptr->USTAT = 0; + if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr, STA_DON); + if (mt_log) printf ("[MT%d: rewind complete, sta=%o]\n", u, mt_sta); + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */ + mt_updcsta (uptr, STA_ILL); /* illegal operation */ + return IORETURN (mt_stopioe, SCPE_UNATT); + } + +passed_eot = sim_tape_eot (uptr); /* passed EOT? */ +switch (f) { /* case on function */ + + case FN_READ: /* read */ + case FN_CMPARE: /* read/compare */ + st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */ + if (st == MTSE_RECE) mt_sta = mt_sta | STA_PAR | STA_ERR; /* rec in err? */ + else if (st != MTSE_OK) { /* other error? */ + mt_sta = mt_sta | STA_RLE | STA_ERR; /* set RLE flag */ + r = mt_map_err (uptr, st); /* map error */ + break; + } + cbc = PACKED (mt_cu)? wc * 3: wc * 2; /* expected bc */ + if (tbc != cbc) mt_sta = mt_sta | STA_RLE | STA_ERR; /* wrong size? */ + if (tbc < cbc) { /* record small? */ + cbc = tbc; /* use smaller */ + wc = PACKED (mt_cu)? ((tbc + 2) / 3): ((tbc + 1) / 2); + } + for (i = p = 0; i < wc; i++) { /* copy buffer */ + M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC, CA */ + M[MT_CA] = (M[MT_CA] + 1) & DMASK; + xma = M[MT_CA] & AMASK; + if (PACKED (mt_cu)) { /* packed? */ + c1 = mtxb[p++] & 077; + c2 = mtxb[p++] & 077; + c3 = mtxb[p++] & 077; + c = (c1 << 12) | (c2 << 6) | c3; + } + else { + c1 = mtxb[p++]; + c2 = mtxb[p++]; + c = (c1 << 8) | c2; + } + if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c; + else if ((f == FN_CMPARE) && (c != (M[xma] & + (PACKED (mt_cu)? DMASK: 0177777)))) { + mt_updcsta (uptr, STA_CPE); + break; + } + } /* end for */ + break; + + case FN_WRITE: /* write */ + tbc = PACKED (mt_cu)? wc * 3: wc * 2; + xma = M[MT_CA] & AMASK; /* get mem addr */ + for (i = p = 0; i < wc; i++) { /* copy buf to tape */ + xma = (xma + 1) & AMASK; /* incr mem addr */ + if (PACKED (mt_cu)) { /* packed? */ + mtxb[p++] = (M[xma] >> 12) & 077; + mtxb[p++] = (M[xma] >> 6) & 077; + mtxb[p++] = M[xma] & 077; + } + else { + mtxb[p++] = (M[xma] >> 8) & 0377; + mtxb[p++] = M[xma] & 0377; + } + } /* end for */ + if (st = sim_tape_wrrecf (uptr, mtxb, tbc)) /* write rec, err? */ + r = mt_map_err (uptr, st); /* map error */ + else { + M[MT_CA] = (M[MT_CA] + wc) & DMASK; /* advance mem addr */ + M[MT_WC] = 0; /* clear word cnt */ + } + mt_cu = mt_cu & ~CU_ERASE; /* clear erase flag */ + break; + + case FN_WREOF: + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = mt_map_err (uptr, st); /* map error */ + else uptr->USTAT = STA_EOF; + mt_cu = mt_cu & ~CU_ERASE; /* clear erase flag */ + break; + + case FN_SPACEF: /* space forward */ + do { + M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC */ + if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; + } + } while ((M[MT_WC] != 0) && (passed_eot || !sim_tape_eot (uptr))); + break; + + case FN_SPACER: /* space reverse */ + do { + M[MT_WC] = (M[MT_WC] + 1) & DMASK; /* inc WC */ + if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; + } + } while (M[MT_WC] != 0); + break; + } /* end case */ + +if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ + uptr->USTAT = uptr->USTAT | STA_EOT; +mt_updcsta (uptr, STA_DON); /* set done */ +if (mt_log) printf ("MT%d: fnc=%d done, ma=%o, wc=%o, sta=%o]\n", + u, f, M[MT_CA], M[MT_WC], mt_sta); +return r; +} + +/* Update controller status */ + +int32 mt_updcsta (UNIT *uptr, int32 news) +{ +mt_sta = (mt_sta & ~(STA_DYN | STA_CLR)) | + (uptr->USTAT & STA_DYN) | news; +if ((mt_sta & (STA_ERR | STA_DON)) && (mt_cu & CU_IE)) + SET_INT (MTA); +else CLR_INT (MTA); /* int request */ +return mt_sta; +} + +/* Test if controller busy */ + +UNIT *mt_busy (void) +{ +int32 u; +UNIT *uptr; + +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + if (sim_is_active (uptr) && ((uptr->USTAT & STA_REW) == 0)) + return uptr; + } +return NULL; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* not attached */ + mt_sta = mt_sta | STA_ILL | STA_ERR; + case MTSE_OK: /* no error */ + return SCPE_IERR; + + case MTSE_TMK: /* end of file */ + uptr->USTAT = uptr->USTAT | STA_EOF; /* set EOF */ + mt_sta = mt_sta | STA_ERR; + break; + + case MTSE_IOERR: /* IO error */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + if (mt_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + break; + + case MTSE_EOM: /* end of medium */ + mt_sta = mt_sta | STA_BAD | STA_ERR; /* set end tape */ + break; + + case MTSE_BOT: /* reverse into BOT */ + uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */ + mt_sta = mt_sta | STA_ERR; + break; + + case MTSE_WRP: /* write protect */ + mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +mt_cu = mt_sta = 0; +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + sim_tape_reset (uptr); /* reset tape */ + sim_cancel (uptr); /* cancel activity */ + if (uptr->flags & UNIT_ATT) uptr->USTAT = STA_BOT; + else uptr->USTAT = 0; + } +mt_updcsta (&mt_unit[0], 0); /* update status */ +if (mtxb == NULL) mtxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); +if (mtxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* IORS routine */ + +int32 mt_iors (void) +{ +return (mt_sta & (STA_ERR | STA_DON))? IOS_MTA: 0; +} + +/* Attach routine */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->USTAT = STA_BOT; +mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ +return r; +} + +/* Detach routine */ + +t_stat mt_detach (UNIT* uptr) +{ +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if (!sim_is_active (uptr)) uptr->USTAT = 0; +mt_updcsta (mt_dev.units + GET_UNIT (mt_cu), 0); /* update status */ +return sim_tape_detach (uptr); +} diff --git a/PDP18B/pdp18b_rb.c b/PDP18B/pdp18b_rb.c new file mode 100644 index 0000000..af0725a --- /dev/null +++ b/PDP18B/pdp18b_rb.c @@ -0,0 +1,291 @@ +/* pdp18b_rb.c: RB09 fixed head disk simulator + + Copyright (c) 2003-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rb RB09 fixed head disk + + 14-Jan-04 RMS Revised IO device call interface + 26-Oct-03 RMS Cleaned up buffer copy code + + The RB09 is a head-per-track disk. It uses the single cycle data break + facility. To minimize overhead, the entire RB09 is buffered in memory. + + Two timing parameters are provided: + + rb_time Interword timing. Must be non-zero. + rb_burst Burst mode. If 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst. +*/ + +#include "pdp18b_defs.h" +#include + +/* Constants */ + +#define RB_NUMWD 64 /* words/sector */ +#define RB_NUMSC 80 /* sectors/track */ +#define RB_NUMTR 200 /* tracks/disk */ +#define RB_WLKTR 10 /* tracks/wlock switch */ +#define RB_SIZE (RB_NUMTR * RB_NUMSC * RB_NUMWD) /* words/drive */ + +/* Function/status register */ + +#define RBS_ERR 0400000 /* error */ +#define RBS_PAR 0200000 /* parity error */ +#define RBS_ILA 0100000 /* ill addr error */ +#define RBS_TIM 0040000 /* timing transfer */ +#define RBS_NRY 0020000 /* not ready error */ +#define RBS_DON 0010000 /* done */ +#define RBS_IE 0004000 /* int enable */ +#define RBS_BSY 0002000 /* busy */ +#define RBS_WR 0001000 /* read/write */ +#define RBS_XOR (RBS_IE|RBS_BSY|RBS_WR) /* set by XOR */ +#define RBS_MBZ 0000777 /* always clear */ +#define RBS_EFLGS (RBS_PAR|RBS_ILA|RBS_TIM|RBS_NRY) /* error flags */ + +/* BCD disk address */ + +#define RBA_V_TR 8 +#define RBA_M_TR 0x1FF +#define RBA_V_SC 0 +#define RBA_M_SC 0xFF +#define RBA_GETTR(x) (((x) >> RBA_V_TR) & RBA_M_TR) +#define RBA_GETSC(x) (((x) >> RBA_V_SC) & RBA_M_SC) + +#define GET_POS(x) ((int) fmod (sim_gtime () / ((double) (x)), \ + ((double) (RB_NUMSC * RB_NUMWD)))) + +extern int32 M[]; +extern int32 int_hwre[API_HLVL+1]; +extern UNIT cpu_unit; + +int32 rb_sta = 0; /* status register */ +int32 rb_da = 0; /* disk address */ +int32 rb_ma = 0; /* current addr */ +int32 rb_wc = 0; /* word count */ +int32 rb_wlk = 0; /* write lock */ +int32 rb_time = 10; /* inter-word time */ +int32 rb_burst = 1; /* burst mode flag */ +int32 rb_stopioe = 1; /* stop on error */ + +DEVICE rb_dev; +int32 rb71 (int32 dev, int32 pulse, int32 AC); +t_stat rb_svc (UNIT *uptr); +t_stat rb_reset (DEVICE *dptr); +int32 rb_updsta (int32 new); +int32 rb_make_da (int32 dat); +int32 rb_make_bcd (int32 dat); +int32 rb_set_da (int32 dat, int32 old); +int32 rb_set_bcd (int32 dat); + +/* RB data structures + + rb_dev RF device descriptor + rb_unit RF unit descriptor + rb_reg RF register list +*/ + +DIB rb_dib = { DEV_RB, 1, NULL, { &rb71 } }; + +UNIT rb_unit = { + UDATA (&rb_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + RB_SIZE) + }; + +REG rb_reg[] = { + { ORDATA (STA, rb_sta, 18) }, + { ORDATA (DA, rb_da, 20) }, + { ORDATA (WC, rb_wc, 16) }, + { ORDATA (MA, rb_ma, ADDRSIZE) }, + { FLDATA (INT, int_hwre[API_RB], INT_V_RB) }, + { ORDATA (WLK, rb_wlk, RB_NUMTR / RB_WLKTR) }, + { DRDATA (TIME, rb_time, 24), PV_LEFT + REG_NZ }, + { FLDATA (BURST, rb_burst, 0) }, + { FLDATA (STOP_IOE, rb_stopioe, 0) }, + { ORDATA (DEVNO, rb_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rb_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE rb_dev = { + "RB", &rb_unit, rb_reg, rb_mod, + 1, 8, 21, 1, 8, 18, + NULL, NULL, &rb_reset, + NULL, NULL, NULL, + &rb_dib, DEV_DIS | DEV_DISABLE + }; + +/* IOT routines */ + +int32 rb71 (int32 dev, int32 pulse, int32 AC) +{ +int32 tow, t, sb = pulse & 060; + +if (pulse & 001) { + if (sb == 000) rb_sta = rb_sta & /* DBCF */ + ~(RBS_ERR | RBS_EFLGS | RBS_DON); + if ((sb == 020) && (rb_sta & (RBS_ERR | RBS_DON))) + AC = AC | IOT_SKP; /* DBSF */ + if (sb == 040) rb_sta = 0; /* DBCS */ + } +if (pulse & 002) { + if (sb == 000) AC = AC | rb_make_da (rb_da); /* DBRD */ + if (sb == 020) AC = AC | rb_sta; /* DBRS */ + if (sb == 040) rb_ma = AC & AMASK; /* DBLM */ + } +if (pulse & 004) { + if (sb == 000) rb_da = rb_set_da (AC, rb_da); /* DBLD */ + if (sb == 020) rb_wc = AC & 0177777; /* DBLW */ + if (sb == 040) { /* DBLS */ + rb_sta = (rb_sta & RBS_XOR) ^ (AC & ~RBS_MBZ); + if (rb_sta & RBS_BSY) { /* busy set? */ + if (!sim_is_active (&rb_unit)) { /* schedule */ + tow = rb_da % (RB_NUMSC * RB_NUMWD); + t = tow - GET_POS (rb_time); + if (t < 0) t = t + (RB_NUMSC * RB_NUMWD); + sim_activate (&rb_unit, t * rb_time); + } + } + else sim_cancel (&rb_unit); /* no, stop */ + } + } +rb_updsta (0); /* update status */ +return AC; +} + +int32 rb_make_da (int32 da) +{ +int32 t = da / (RB_NUMSC * RB_NUMWD); /* bin track */ +int32 s = (da % (RB_NUMSC * RB_NUMWD)) / RB_NUMWD; /* bin sector */ +int32 bcd_t = rb_make_bcd (t); /* bcd track */ +int32 bcd_s = rb_make_bcd (s); /* bcd sector */ +return (bcd_t << RBA_V_TR) | (bcd_s << RBA_V_SC); +} + +int32 rb_set_da (int32 bcda, int32 old_da) +{ +int32 bcd_t = RBA_GETTR (bcda); /* bcd track */ +int32 bcd_s = RBA_GETSC (bcda); /* bcd sector */ +int32 t = rb_set_bcd (bcd_t); /* bin track */ +int32 s = rb_set_bcd (bcd_s); /* bin sector */ + +if ((t >= RB_NUMTR) || (t < 0) || /* invalid? */ + (s >= RB_NUMSC) || (s < 0)) { + rb_updsta (RBS_ILA); /* error */ + return old_da; /* don't change */ + } +else return (((t * RB_NUMSC) + s) * RB_NUMWD); /* new da */ +} + +int32 rb_make_bcd (int32 bin) +{ +int32 d, i, r; + +for (r = i = 0; bin != 0; bin = bin / 10) { /* while nz */ + d = bin % 10; /* dec digit */ + r = r | (d << i); /* insert bcd */ + i = i + 4; + } +return r; +} + +int32 rb_set_bcd (int32 bcd) +{ +int32 d, i, r; + +for (r = 0, i = 1; bcd != 0; bcd = bcd >> 4) { /* while nz */ + d = bcd & 0xF; /* bcd digit */ + if (d >= 10) return -1; /* invalid? */ + r = r + (d * i); /* insert bin */ + i = i * 10; + } +return r; +} + +/* Unit service - disk is buffered in memory */ + +t_stat rb_svc (UNIT *uptr) +{ +int32 t, sw; +int32 *fbuf = uptr->filebuf; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + rb_updsta (RBS_NRY | RBS_DON); /* set nxd, done */ + return IORETURN (rb_stopioe, SCPE_UNATT); + } + +do { + if (rb_sta & RBS_WR) { /* write? */ + t = rb_da / (RB_NUMSC * RB_NUMWD); /* track */ + sw = t / RB_WLKTR; /* switch */ + if ((rb_wlk >> sw) & 1) { /* write locked? */ + rb_updsta (RBS_ILA | RBS_DON); + break; + } + else { /* not locked */ + fbuf[rb_da] = M[rb_ma]; /* write word */ + if (((t_addr) rb_da) >= uptr->hwmark) uptr->hwmark = rb_da + 1; + } + } + else if (MEM_ADDR_OK (rb_ma)) /* read, valid addr? */ + M[rb_ma] = fbuf[rb_da]; /* read word */ + rb_wc = (rb_wc + 1) & 0177777; /* incr word count */ + rb_ma = (rb_ma + 1) & AMASK; /* incr mem addr */ + rb_da = rb_da + 1; /* incr disk addr */ + if (rb_da > RB_SIZE) rb_da = 0; /* disk wraparound? */ + } while ((rb_wc != 0) && (rb_burst != 0)); /* brk if wc, no brst */ + +if ((rb_wc != 0) && ((rb_sta & RBS_ERR) == 0)) /* more to do? */ + sim_activate (&rb_unit, rb_time); /* sched next */ +else rb_updsta (RBS_DON); /* set done */ +return SCPE_OK; +} + +/* Update status */ + +int32 rb_updsta (int32 new) +{ +rb_sta = (rb_sta | new) & ~(RBS_ERR | RBS_MBZ); /* clear err, mbz */ +if (rb_sta & RBS_EFLGS) rb_sta = rb_sta | RBS_ERR; /* error? */ +if (rb_sta & RBS_DON) rb_sta = rb_sta & ~RBS_BSY; /* done? clear busy */ +if ((rb_sta & (RBS_ERR | RBS_DON)) && (rb_sta & RBS_IE)) + SET_INT (RB); /* set or clr intr */ +else CLR_INT (RB); +return rb_sta; +} + +/* Reset routine */ + +t_stat rb_reset (DEVICE *dptr) +{ +rb_sta = rb_da = 0; +rb_wc = rb_ma = 0; +rb_updsta (0); +sim_cancel (&rb_unit); +return SCPE_OK; +} diff --git a/PDP18B/pdp18b_rf.c b/PDP18B/pdp18b_rf.c new file mode 100644 index 0000000..5165548 --- /dev/null +++ b/PDP18B/pdp18b_rf.c @@ -0,0 +1,355 @@ +/* pdp18b_rf.c: fixed head disk simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rf (PDP-9) RF09/RF09 + (PDP-15) RF15/RS09 + + 04-Oct-06 RMS Fixed bug, DSCD does not clear function register + 15-May-06 RMS Fixed bug in autosize attach (reported by David Gesswein) + 14-Jan-04 RMS Revised IO device call interface + Changed sim_fsize calling sequence + 26-Oct-03 RMS Cleaned up buffer copy code + 26-Jul-03 RMS Fixed bug in set size routine + 14-Mar-03 RMS Fixed variable platter interaction with save/restore + 03-Mar-03 RMS Fixed autosizing + 12-Feb-03 RMS Removed 8 platter sizing hack + 05-Feb-03 RMS Fixed decode bugs, added variable and autosizing + 05-Oct-02 RMS Added DIB, dev number support + 06-Jan-02 RMS Revised enable/disable support + 25-Nov-01 RMS Revised interrupt structure + 24-Nov-01 RMS Changed WLK to array + 26-Apr-01 RMS Added device enable/disable support + 15-Feb-01 RMS Fixed 3 cycle data break sequencing + 30-Nov-99 RMS Added non-zero requirement to rf_time + 14-Apr-99 RMS Changed t_addr to unsigned + + The RFxx is a head-per-track disk. It uses the multicycle data break + facility. To minimize overhead, the entire RFxx is buffered in memory. + + Two timing parameters are provided: + + rf_time Interword timing. Must be non-zero. + rf_burst Burst mode. If 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst. +*/ + +#include "pdp18b_defs.h" +#include + +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ +#define UNIT_M_PLAT 07 +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) +#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) + +/* Constants */ + +#define RF_NUMWD 2048 /* words/track */ +#define RF_NUMTR 128 /* tracks/disk */ +#define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */ +#define RF_NUMDK 8 /* disks/controller */ +#define RF_WMASK (RF_NUMWD - 1) /* word mask */ +#define RF_WC 036 /* word count */ +#define RF_CA 037 /* current addr */ + +/* Function/status register */ + +#define RFS_ERR 0400000 /* error */ +#define RFS_HDW 0200000 /* hardware error */ +#define RFS_APE 0100000 /* addr parity error */ +#define RFS_MXF 0040000 /* missed transfer */ +#define RFS_WCE 0020000 /* write check error */ +#define RFS_DPE 0010000 /* data parity error */ +#define RFS_WLO 0004000 /* write lock error */ +#define RFS_NED 0002000 /* non-existent disk */ +#define RFS_DCH 0001000 /* data chan timing */ +#define RFS_PGE 0000400 /* programming error */ +#define RFS_DON 0000200 /* transfer complete */ +#define RFS_V_FNC 1 /* function */ +#define RFS_M_FNC 03 +#define RFS_FNC (RFS_M_FNC << RFS_V_FNC) +#define FN_NOP 0 +#define FN_READ 1 +#define FN_WRITE 2 +#define FN_WCHK 3 +#define RFS_IE 0000001 /* interrupt enable */ + +#define RFS_CLR 0000170 /* always clear */ +#define RFS_EFLGS (RFS_HDW | RFS_APE | RFS_MXF | RFS_WCE | \ + RFS_DPE | RFS_WLO | RFS_NED ) /* error flags */ +#define RFS_FR (RFS_FNC|RFS_IE) +#define GET_FNC(x) (((x) >> RFS_V_FNC) & RFS_M_FNC) +#define GET_POS(x) ((int) fmod (sim_gtime () / ((double) (x)), \ + ((double) RF_NUMWD))) +#define RF_BUSY (sim_is_active (&rf_unit)) + +extern int32 M[]; +extern int32 int_hwre[API_HLVL+1]; +extern UNIT cpu_unit; + +int32 rf_sta = 0; /* status register */ +int32 rf_da = 0; /* disk address */ +int32 rf_dbuf = 0; /* data buffer */ +int32 rf_wlk[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; /* write lock */ +int32 rf_time = 10; /* inter-word time */ +int32 rf_burst = 1; /* burst mode flag */ +int32 rf_stopioe = 1; /* stop on error */ + +DEVICE rf_dev; +int32 rf70 (int32 dev, int32 pulse, int32 dat); +int32 rf72 (int32 dev, int32 pulse, int32 dat); +int32 rf_iors (void); +t_stat rf_svc (UNIT *uptr); +t_stat rf_reset (DEVICE *dptr); +int32 rf_updsta (int32 new); +t_stat rf_attach (UNIT *uptr, char *cptr); +t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* RF data structures + + rf_dev RF device descriptor + rf_unit RF unit descriptor + rf_reg RF register list +*/ + +DIB rf_dib = { DEV_RF, 3, &rf_iors, { &rf70, NULL, &rf72 } }; + +UNIT rf_unit = { + UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+UNIT_AUTO, + RF_DKSIZE) + }; + +REG rf_reg[] = { + { ORDATA (STA, rf_sta, 18) }, + { ORDATA (DA, rf_da, 22) }, + { ORDATA (WC, M[RF_WC], 18) }, + { ORDATA (CA, M[RF_CA], 18) }, + { ORDATA (BUF, rf_dbuf, 18) }, + { FLDATA (INT, int_hwre[API_RF], INT_V_RF) }, + { BRDATA (WLK, rf_wlk, 8, 16, RF_NUMDK) }, + { DRDATA (TIME, rf_time, 24), PV_LEFT + REG_NZ }, + { FLDATA (BURST, rf_burst, 0) }, + { FLDATA (STOP_IOE, rf_stopioe, 0) }, + { DRDATA (CAPAC, rf_unit.capac, 31), PV_LEFT + REG_HRO }, + { ORDATA (DEVNO, rf_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rf_mod[] = { + { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size }, + { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size }, + { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size }, + { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size }, + { UNIT_PLAT, (4 << UNIT_V_PLAT), NULL, "5P", &rf_set_size }, + { UNIT_PLAT, (5 << UNIT_V_PLAT), NULL, "6P", &rf_set_size }, + { UNIT_PLAT, (6 << UNIT_V_PLAT), NULL, "7P", &rf_set_size }, + { UNIT_PLAT, (7 << UNIT_V_PLAT), NULL, "8P", &rf_set_size }, + { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE rf_dev = { + "RF", &rf_unit, rf_reg, rf_mod, + 1, 8, 21, 1, 8, 18, + NULL, NULL, &rf_reset, + NULL, &rf_attach, NULL, + &rf_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 rf70 (int32 dev, int32 pulse, int32 dat) +{ +int32 t, sb; + +sb = pulse & 060; /* subopcode */ +if (pulse & 01) { + if ((sb == 000) && (rf_sta & (RFS_ERR | RFS_DON))) /* DSSF */ + dat = IOT_SKP | dat; + else if (sb == 020) rf_reset (&rf_dev); /* DSCC */ + else if (sb == 040) { /* DSCF */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_sta = rf_sta & ~(RFS_FNC | RFS_IE); /* clear func */ + } + } +if (pulse & 02) { + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */ + else if (sb == 000) dat = dat | rf_dbuf; /* DRBR */ + else if (sb == 020) /* DRAL */ + dat = dat | (rf_da & DMASK); + else if (sb == 040) /* DSFX */ + rf_sta = rf_sta ^ (dat & (RFS_FNC | RFS_IE)); /* xor func */ + else if (sb == 060) /* DRAH */ + dat = dat | (rf_da >> 18) | ((rf_sta & RFS_NED)? 010: 0); + } +if (pulse & 04) { + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */ + else if (sb == 000) rf_dbuf = dat & DMASK; /* DLBR */ + else if (sb == 020) /* DLAL */ + rf_da = (rf_da & ~DMASK) | (dat & DMASK); + else if (sb == 040) { /* DSCN */ + rf_sta = rf_sta & ~RFS_DON; /* clear done */ + if (GET_FNC (rf_sta) != FN_NOP) { + t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new */ + if (t < 0) t = t + RF_NUMWD; /* wrap around? */ + sim_activate (&rf_unit, t * rf_time); /* schedule op */ + } + } + else if (sb == 060) { /* DLAH */ + rf_da = (rf_da & DMASK) | ((dat & 07) << 18); + if ((uint32) rf_da >= rf_unit.capac) /* for sizing */ + rf_updsta (RFS_NED); + } + } +rf_updsta (0); /* update status */ +return dat; +} + +int32 rf72 (int32 dev, int32 pulse, int32 dat) +{ +int32 sb = pulse & 060; + +if (pulse & 02) { + if (sb == 000) dat = dat | GET_POS (rf_time) | /* DLOK */ + (sim_is_active (&rf_unit)? 0400000: 0); + else if (sb == 040) { /* DSCD */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy inhibits */ + else rf_sta = rf_sta & RFS_FR; + rf_updsta (0); + } + else if (sb == 060) { /* DSRS */ + if (RF_BUSY) rf_sta = rf_sta | RFS_PGE; /* busy sets PGE */ + dat = dat | rf_updsta (0); + } + } +return dat; +} + +/* Unit service - assumes the entire disk is buffered */ + +t_stat rf_svc (UNIT *uptr) +{ +int32 f, pa, d, t; +int32 *fbuf = uptr->filebuf; + +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + rf_updsta (RFS_NED | RFS_DON); /* set nxd, done */ + return IORETURN (rf_stopioe, SCPE_UNATT); + } + +f = GET_FNC (rf_sta); /* get function */ +do { + if ((uint32) rf_da >= uptr->capac) { /* disk overflow? */ + rf_updsta (RFS_NED); /* nx disk error */ + break; + } + M[RF_WC] = (M[RF_WC] + 1) & DMASK; /* incr word count */ + pa = M[RF_CA] = (M[RF_CA] + 1) & AMASK; /* incr mem addr */ + if ((f == FN_READ) && MEM_ADDR_OK (pa)) /* read? */ + M[pa] = fbuf[rf_da]; + if ((f == FN_WCHK) && (M[pa] != fbuf[rf_da])) { /* write check? */ + rf_updsta (RFS_WCE); /* flag error */ + break; + } + if (f == FN_WRITE) { /* write? */ + d = (rf_da >> 18) & 07; /* disk */ + t = (rf_da >> 14) & 017; /* track groups */ + if ((rf_wlk[d] >> t) & 1) { /* write locked? */ + rf_updsta (RFS_WLO); + break; + } + else { /* not locked */ + fbuf[rf_da] = M[pa]; /* write word */ + if (((uint32) rf_da) >= uptr->hwmark) uptr->hwmark = rf_da + 1; + } + } + rf_da = rf_da + 1; /* incr disk addr */ + } while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ + +if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */ + sim_activate (&rf_unit, rf_time); /* sched next */ +else rf_updsta (RFS_DON); +return SCPE_OK; +} + +/* Update status */ + +int32 rf_updsta (int32 new) +{ +rf_sta = (rf_sta | new) & ~(RFS_ERR | RFS_CLR); +if (rf_sta & RFS_EFLGS) rf_sta = rf_sta | RFS_ERR; +if ((rf_sta & (RFS_ERR | RFS_DON)) && (rf_sta & RFS_IE)) + SET_INT (RF); +else CLR_INT (RF); +return rf_sta; +} + +/* Reset routine */ + +t_stat rf_reset (DEVICE *dptr) +{ +rf_sta = rf_da = rf_dbuf = 0; +rf_updsta (0); +sim_cancel (&rf_unit); +return SCPE_OK; +} + +/* IORS routine */ + +int32 rf_iors (void) +{ +return ((rf_sta & (RFS_ERR | RFS_DON))? IOS_RF: 0); +} + +/* Attach routine */ + +t_stat rf_attach (UNIT *uptr, char *cptr) +{ +uint32 p, sz; +uint32 ds_bytes = RF_DKSIZE * sizeof (int32); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= RF_NUMDK) p = RF_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | + (p << UNIT_V_PLAT); + } +uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE; +return attach_unit (uptr, cptr); +} + +/* Change disk size */ + +t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val < 0) return SCPE_IERR; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = UNIT_GETP (val) * RF_DKSIZE; +uptr->flags = uptr->flags & ~UNIT_AUTO; +return SCPE_OK; +} diff --git a/PDP18B/pdp18b_rp.c b/PDP18B/pdp18b_rp.c new file mode 100644 index 0000000..b6df615 --- /dev/null +++ b/PDP18B/pdp18b_rp.c @@ -0,0 +1,493 @@ +/* pdp18b_rp.c: RP15/RP02 disk pack simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rp RP15/RP02 disk pack + + 14-Jan-04 RMS Revised IO device call interface + 06-Feb-03 RMS Revised IOT decoding, fixed bug in initiation + 05-Oct-02 RMS Added DIB, device number support + 06-Jan-02 RMS Revised enable/disable support + 29-Nov-01 RMS Added read only unit support + 25-Nov-01 RMS Revised interrupt structure + Changed FLG to array + 26-Apr-01 RMS Added device enable/disable support + 14-Apr-99 RMS Changed t_addr to unsigned + 29-Jun-96 RMS Added unit enable/disable support +*/ + +#include "pdp18b_defs.h" + +/* Constants */ + +#define RP_NUMWD 256 /* words/sector */ +#define RP_NUMSC 10 /* sectors/surface */ +#define RP_NUMSF 20 /* surfaces/cylinder */ +#define RP_NUMCY 203 /* cylinders/drive */ +#define RP_NUMDR 8 /* drives/controller */ +#define RP_SIZE (RP_NUMCY * RP_NUMSF * RP_NUMSC * RP_NUMWD) + /* words/drive */ + +/* Unit specific flags */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* Status register A */ + +#define STA_V_UNIT 15 /* unit select */ +#define STA_M_UNIT 07 +#define STA_V_FUNC 12 /* function */ +#define STA_M_FUNC 07 +#define FN_IDLE 0 +#define FN_READ 1 +#define FN_WRITE 2 +#define FN_RECAL 3 +#define FN_SEEK 4 +#define FN_RDALL 5 +#define FN_WRALL 6 +#define FN_WRCHK 7 +#define FN_2ND 010 /* second state flag */ +#define STA_IED 0004000 /* int enable done */ +#define STA_IEA 0002000 /* int enable attn */ +#define STA_GO 0001000 /* go */ +#define STA_WPE 0000400 /* write lock error */ +#define STA_NXC 0000200 /* nx cyl error */ +#define STA_NXF 0000100 /* nx surface error */ +#define STA_NXS 0000040 /* nx sector error */ +#define STA_HNF 0000020 /* hdr not found */ +#define STA_SUWP 0000010 /* sel unit wrt lock */ +#define STA_SUSI 0000004 /* sel unit seek inc */ +#define STA_DON 0000002 /* done */ +#define STA_ERR 0000001 /* error */ + +#define STA_RW 0777000 /* read/write */ +#define STA_EFLGS (STA_WPE | STA_NXC | STA_NXF | STA_NXS | \ + STA_HNF | STA_SUSI) /* error flags */ +#define STA_DYN (STA_SUWP | STA_SUSI) /* per unit status */ +#define GET_UNIT(x) (((x) >> STA_V_UNIT) & STA_M_UNIT) +#define GET_FUNC(x) (((x) >> STA_V_FUNC) & STA_M_FUNC) + +/* Status register B */ + +#define STB_V_ATT0 17 /* unit 0 attention */ +#define STB_ATTN 0776000 /* attention flags */ +#define STB_SUFU 0001000 /* sel unit unsafe */ +#define STB_PGE 0000400 /* programming error */ +#define STB_EOP 0000200 /* end of pack */ +#define STB_TME 0000100 /* timing error */ +#define STB_FME 0000040 /* format error */ +#define STB_WCE 0000020 /* write check error */ +#define STB_WPE 0000010 /* word parity error */ +#define STB_LON 0000004 /* long parity error */ +#define STB_SUSU 0000002 /* sel unit seeking */ +#define STB_SUNR 0000001 /* sel unit not rdy */ + +#define STB_EFLGS (STB_SUFU | STB_PGE | STB_EOP | STB_TME | STB_FME | \ + STB_WCE | STB_WPE | STB_LON ) /* error flags */ +#define STB_DYN (STB_SUFU | STB_SUSU | STB_SUNR) /* per unit */ + +/* Disk address */ + +#define DA_V_SECT 0 /* sector */ +#define DA_M_SECT 017 +#define DA_V_SURF 5 +#define DA_M_SURF 037 +#define DA_V_CYL 10 /* cylinder */ +#define DA_M_CYL 0377 +#define GET_SECT(x) (((x) >> DA_V_SECT) & DA_M_SECT) +#define GET_SURF(x) (((x) >> DA_V_SURF) & DA_M_SURF) +#define GET_CYL(x) (((x) >> DA_V_CYL) & DA_M_CYL) +#define GET_DA(x) ((((GET_CYL (x) * RP_NUMSF) + GET_SURF (x)) * \ + RP_NUMSC) + GET_SECT (x)) + +#define RP_MIN 2 +#define MAX(x,y) (((x) > (y))? (x): (y)) + +extern int32 M[]; +extern int32 int_hwre[API_HLVL+1], nexm; +extern UNIT cpu_unit; + +int32 rp_sta = 0; /* status A */ +int32 rp_stb = 0; /* status B */ +int32 rp_ma = 0; /* memory address */ +int32 rp_da = 0; /* disk address */ +int32 rp_wc = 0; /* word count */ +int32 rp_busy = 0; /* busy */ +int32 rp_stopioe = 1; /* stop on error */ +int32 rp_swait = 10; /* seek time */ +int32 rp_rwait = 10; /* rotate time */ + +DEVICE rp_dev; +int32 rp63 (int32 dev, int32 pulse, int32 dat); +int32 rp64 (int32 dev, int32 pulse, int32 dat); +int32 rp_iors (void); +t_stat rp_svc (UNIT *uptr); +void rp_updsta (int32 newa, int32 newb); +t_stat rp_reset (DEVICE *dptr); +t_stat rp_attach (UNIT *uptr, char *cptr); +t_stat rp_detach (UNIT *uptr); + +/* RP15 data structures + + rp_dev RP device descriptor + rp_unit RP unit list + rp_reg RP register list + rp_mod RP modifier list +*/ + +DIB rp_dib = { DEV_RP, 2, &rp_iors, { &rp63, &rp64 } }; + +UNIT rp_unit[] = { + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) }, + { UDATA (&rp_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE, RP_SIZE) } + }; + +REG rp_reg[] = { + { ORDATA (STA, rp_sta, 18) }, + { ORDATA (STB, rp_stb, 18) }, + { ORDATA (DA, rp_da, 18) }, + { ORDATA (MA, rp_ma, 18) }, + { ORDATA (WC, rp_wc, 18) }, + { FLDATA (INT, int_hwre[API_RP], INT_V_RP) }, + { FLDATA (BUSY, rp_busy, 0) }, + { FLDATA (STOP_IOE, rp_stopioe, 0) }, + { DRDATA (STIME, rp_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rp_rwait, 24), PV_LEFT }, + { ORDATA (DEVNO, rp_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rp_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", &set_devno, &show_devno }, + { 0 } + }; + +DEVICE rp_dev = { + "RP", rp_unit, rp_reg, rp_mod, + RP_NUMDR, 8, 24, 1, 8, 18, + NULL, NULL, &rp_reset, + NULL, &rp_attach, &rp_detach, + &rp_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 rp63 (int32 dev, int32 pulse, int32 dat) +{ +int32 sb = pulse & 060; /* subopcode */ + +rp_updsta (0, 0); +if (pulse & 01) { + if ((sb == 000) && /* DPSF */ + ((rp_sta & (STA_DON | STA_ERR)) || (rp_stb & STB_ATTN))) + dat = IOT_SKP | dat; + else if ((sb == 020) && (rp_stb & STB_ATTN)) /* DPSA */ + dat = IOT_SKP | dat; + else if ((sb == 040) && (rp_sta & STA_DON)) /* DPSJ */ + dat = IOT_SKP | dat; + else if ((sb == 060) && (rp_sta & STA_ERR)) /* DPSE */ + dat = IOT_SKP | dat; + } +if (pulse & 02) { + if (sb == 000) dat = dat | rp_sta; /* DPOSA */ + else if (sb == 020) dat = dat | rp_stb; /* DPOSB */ + } +if (pulse & 04) { + if (rp_busy) { /* busy? */ + rp_updsta (0, STB_PGE); + return dat; + } + else if (sb == 000) { /* DPLA */ + rp_da = dat & DMASK; + if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0); + if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0); + if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0); + } + else if (sb == 020) { /* DPCS */ + rp_sta = rp_sta & ~(STA_HNF | STA_DON); + rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE | + STB_TME | STB_PGE | STB_EOP); + rp_updsta (0, 0); + } + else if (sb == 040) rp_ma = dat & DMASK; /* DPCA */ + else if (sb == 060) rp_wc = dat & DMASK; /* DPWC */ + } +return dat; +} + +/* IOT 64 */ + +int32 rp64 (int32 dev, int32 pulse, int32 dat) +{ +int32 u, f, c, sb; +UNIT *uptr; + +sb = pulse & 060; +if (pulse & 01) { + if (sb == 020) dat = IOT_SKP | dat; /* DPSN */ + } +if (pulse & 02) { + if (sb == 000) /* DPOU */ + dat = dat | rp_unit[GET_UNIT (rp_sta)].CYL; + else if (sb == 020) dat = dat | rp_da; /* DPOA */ + else if (sb == 040) dat = dat | rp_ma; /* DPOC */ + else if (sb == 060) dat = dat | rp_wc; /* DPOW */ + } +if (pulse & 04) { + if (rp_busy) { /* busy? */ + rp_updsta (0, STB_PGE); + return dat; + } + if (sb == 000) /* DPCF */ + rp_sta = rp_sta & ~STA_RW; + else if (sb == 020) /* DPLZ */ + rp_sta = rp_sta & (dat | ~STA_RW); + else if (sb == 040) /* DPLO */ + rp_sta = rp_sta | (dat & STA_RW); + else if (sb == 060) /* DPLF */ + rp_sta = (rp_sta & ~STA_RW) | (dat & STA_RW); + rp_sta = rp_sta & ~STA_DON; /* clear done */ + u = GET_UNIT (rp_sta); /* get unit num */ + uptr = rp_dev.units + u; /* select unit */ + if ((rp_sta & STA_GO) && !sim_is_active (uptr)) { + f = uptr->FUNC = GET_FUNC (rp_sta); /* get function */ + rp_busy = 1; /* set ctrl busy */ + rp_sta = rp_sta & ~(STA_HNF | STA_DON); /* clear flags */ + rp_stb = rp_stb & ~(STB_FME | STB_WPE | STB_LON | STB_WCE | + STB_TME | STB_PGE | STB_EOP | (1 << (STB_V_ATT0 - u))); + if (((uptr->flags & UNIT_ATT) == 0) || (f == FN_IDLE) || + (f == FN_SEEK) || (f == FN_RECAL)) + sim_activate (uptr, RP_MIN); /* short delay */ + else { + c = GET_CYL (rp_da); + c = abs (c - uptr->CYL) * rp_swait; /* seek time */ + sim_activate (uptr, MAX (RP_MIN, c + rp_rwait)); + } + } + } +rp_updsta (0, 0); +return dat; +} + +/* Unit service + + If function = idle, clear busy + If seek or recal initial state, clear attention line, compute seek time, + put on cylinder, set second state + If unit not attached, give error + If seek or recal second state, set attention line, compute errors + Else complete data transfer command + + The unit control block contains the function and cylinder for + the current command. +*/ + +static int32 fill[RP_NUMWD] = { 0 }; +t_stat rp_svc (UNIT *uptr) +{ +int32 f, u, comp, cyl, sect, surf; +int32 err, pa, da, wc, awc, i; + +u = (int32) (uptr - rp_dev.units); /* get drv number */ +f = uptr->FUNC; /* get function */ +if (f == FN_IDLE) { /* idle? */ + rp_busy = 0; /* clear busy */ + return SCPE_OK; + } + +if ((f == FN_SEEK) || (f == FN_RECAL)) { /* seek or recal? */ + rp_busy = 0; /* not busy */ + cyl = (f == FN_SEEK)? GET_CYL (rp_da): 0; /* get cylinder */ + sim_activate (uptr, MAX (RP_MIN, abs (cyl - uptr->CYL) * rp_swait)); + uptr->CYL = cyl; /* on cylinder */ + uptr->FUNC = FN_SEEK | FN_2ND; /* set second state */ + rp_updsta (0, 0); /* update status */ + return SCPE_OK; + } + +if (f == (FN_SEEK | FN_2ND)) { /* seek done? */ + rp_updsta (0, rp_stb | (1 << (STB_V_ATT0 - u))); /* set attention */ + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */ + return IORETURN (rp_stopioe, SCPE_UNATT); + } + +if ((f == FN_WRITE) && (uptr->flags & UNIT_WPRT)) { /* write locked? */ + rp_updsta (STA_DON | STA_WPE, 0); /* error */ + return SCPE_OK; + } + +if (GET_SECT (rp_da) >= RP_NUMSC) rp_updsta (STA_NXS, 0); +if (GET_SURF (rp_da) >= RP_NUMSF) rp_updsta (STA_NXF, 0); +if (GET_CYL (rp_da) >= RP_NUMCY) rp_updsta (STA_NXC, 0); +if (rp_sta & (STA_NXS | STA_NXF | STA_NXC)) { /* or bad disk addr? */ + rp_updsta (STA_DON, STB_SUFU); /* done, unsafe */ + return SCPE_OK; + } + +pa = rp_ma & AMASK; /* get mem addr */ +da = GET_DA (rp_da) * RP_NUMWD; /* get disk addr */ +wc = 01000000 - rp_wc; /* get true wc */ +if (((uint32) (pa + wc)) > MEMSIZE) { /* memory overrun? */ + nexm = 1; /* set nexm flag */ + wc = MEMSIZE - pa; /* limit xfer */ + } +if ((da + wc) > RP_SIZE) { /* disk overrun? */ + rp_updsta (0, STB_EOP); /* error */ + wc = RP_SIZE - da; /* limit xfer */ + } + +err = fseek (uptr->fileref, da * sizeof (int), SEEK_SET); + +if ((f == FN_READ) && (err == 0)) { /* read? */ + awc = fxread (&M[pa], sizeof (int32), wc, uptr->fileref); + for ( ; awc < wc; awc++) M[pa + awc] = 0; + err = ferror (uptr->fileref); + } + +if ((f == FN_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int32), wc, uptr->fileref); + err = ferror (uptr->fileref); + if ((err == 0) && (i = (wc & (RP_NUMWD - 1)))) { + fxwrite (fill, sizeof (int), i, uptr->fileref); + err = ferror (uptr->fileref); + } + } + +if ((f == FN_WRCHK) && (err == 0)) { /* write check? */ + for (i = 0; (err == 0) && (i < wc); i++) { + awc = fxread (&comp, sizeof (int32), 1, uptr->fileref); + if (awc == 0) comp = 0; + if (comp != M[pa + i]) rp_updsta (0, STB_WCE); + } + err = ferror (uptr->fileref); + } + +rp_wc = (rp_wc + wc) & DMASK; /* final word count */ +rp_ma = (rp_ma + wc) & DMASK; /* final mem addr */ +da = (da + wc + (RP_NUMWD - 1)) / RP_NUMWD; /* final sector num */ +cyl = da / (RP_NUMSC * RP_NUMSF); /* get cyl */ +if (cyl >= RP_NUMCY) cyl = RP_NUMCY - 1; +surf = (da % (RP_NUMSC * RP_NUMSF)) / RP_NUMSC; /* get surface */ +sect = (da % (RP_NUMSC * RP_NUMSF)) % RP_NUMSC; /* get sector */ +rp_da = (cyl << DA_V_CYL) | (surf << DA_V_SURF) | (sect << DA_V_SECT); +rp_busy = 0; /* clear busy */ +rp_updsta (STA_DON, 0); /* set done */ + +if (err != 0) { /* error? */ + perror ("RP I/O error"); + clearerr (uptr->fileref); + return IORETURN (rp_stopioe, SCPE_IOERR); + } +return SCPE_OK; +} + +/* Update status */ + +void rp_updsta (int32 newa, int32 newb) +{ +int32 f; +UNIT *uptr; + +uptr = rp_dev.units + GET_UNIT (rp_sta); +rp_sta = (rp_sta & ~(STA_DYN | STA_ERR)) | newa; +rp_stb = (rp_stb & ~STB_DYN) | newb; +if (uptr->flags & UNIT_WPRT) rp_sta = rp_sta | STA_SUWP; +if ((uptr->flags & UNIT_ATT) == 0) rp_stb = rp_stb | STB_SUFU | STB_SUNR; +else if (sim_is_active (uptr)) { + f = (uptr->FUNC) & STA_M_FUNC; + if ((f == FN_SEEK) || (f == FN_RECAL)) + rp_stb = rp_stb | STB_SUSU | STB_SUNR; + } +else if (uptr->CYL >= RP_NUMCY) rp_sta = rp_sta | STA_SUSI; +if ((rp_sta & STA_EFLGS) || (rp_stb & STB_EFLGS)) rp_sta = rp_sta | STA_ERR; +if (((rp_sta & (STA_ERR | STA_DON)) && (rp_sta & STA_IED)) || + ((rp_stb & STB_ATTN) && (rp_sta & STA_IEA))) SET_INT (RP); +else CLR_INT (RP); +return; +} + +/* Reset routine */ + +t_stat rp_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rp_sta = rp_stb = rp_da = rp_wc = rp_ma = rp_busy = 0; +CLR_INT (RP); +for (i = 0; i < RP_NUMDR; i++) { + uptr = rp_dev.units + i; + sim_cancel (uptr); + uptr->CYL = uptr->FUNC = 0; + } +return SCPE_OK; +} + +/* IORS routine */ + +int32 rp_iors (void) +{ +return ((rp_sta & (STA_ERR | STA_DON)) || (rp_stb & STB_ATTN))? IOS_RP: 0; +} + +/* Attach unit */ + +t_stat rp_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +rp_updsta (0, 0); +return reason; +} + +/* Detach unit */ + +t_stat rp_detach (UNIT *uptr) +{ +t_stat reason; + +reason = detach_unit (uptr); +rp_updsta (0, 0); +return reason; +} diff --git a/PDP18B/pdp18b_stddev.c b/PDP18B/pdp18b_stddev.c new file mode 100644 index 0000000..d0521c1 --- /dev/null +++ b/PDP18B/pdp18b_stddev.c @@ -0,0 +1,1123 @@ +/* pdp18b_stddev.c: 18b PDP's standard devices + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + tti keyboard + tto teleprinter + clk clock + + 18-Jun-07 RMS Added UNIT_IDLE to console input, clock + 18-Oct-06 RMS Added PDP-15 programmable duplex control + Fixed handling of non-printable characters in KSR mode + Changed clock to be free-running + Fixed out-of-tape behavior for PDP-9 vs PDP-15 + Synced keyboard to clock + 30-Jun-06 RMS Fixed KSR-28 shift tracking + 20-Jun-06 RMS Added KSR ASCII reader support + 13-Jun-06 RMS Fixed Baudot letters/figures inversion for PDP-4 + Fixed PDP-4/PDP-7 default terminal to be local echo + 22-Nov-05 RMS Revised for new terminal processing routines + 28-May-04 RMS Removed SET TTI CTRL-C + 16-Feb-04 RMS Fixed bug in hardware read-in mode bootstrap + 14-Jan-04 RMS Revised IO device call interface + CAF does not turn off the clock + 29-Dec-03 RMS Added console backpressure support + 26-Jul-03 RMS Increased PTP, TTO timeouts for PDP-15 operating systems + Added hardware read-in mode support for PDP-7/9/15 + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Clean up flags on detach + 01-Mar-03 RMS Added SET/SHOW CLK freq, SET TTI CTRL-C + 22-Dec-02 RMS Added break support + 01-Nov-02 RMS Added 7B/8B support to terminal + 05-Oct-02 RMS Added DIBs, device number support, IORS call + 14-Jul-02 RMS Added ASCII reader/punch support (from Hans Pufal) + 30-May-02 RMS Widened POS to 32b + 29-Nov-01 RMS Added read only unit support + 25-Nov-01 RMS Revised interrupt structure + 17-Sep-01 RMS Removed multiconsole support + 07-Sep-01 RMS Added terminal multiplexor support + 17-Jul-01 RMS Moved function prototype + 10-Jun-01 RMS Cleaned up IOT decoding to reflect hardware + 27-May-01 RMS Added multiconsole support + 10-Mar-01 RMS Added funny format loader support + 05-Mar-01 RMS Added clock calibration support + 22-Dec-00 RMS Added PDP-9/15 half duplex support + 30-Nov-00 RMS Fixed PDP-4/7 bootstrap loader for 4K systems + 30-Oct-00 RMS Standardized register naming + 06-Jan-97 RMS Fixed PDP-4 console input + 16-Dec-96 RMS Fixed bug in binary ptr service +*/ + +#include "pdp18b_defs.h" +#include + +#define UNIT_V_RASCII (UNIT_V_UF + 0) /* reader ASCII */ +#define UNIT_RASCII (1 << UNIT_V_RASCII) +#define UNIT_V_KASCII (UNIT_V_UF + 1) /* KSR ASCII */ +#define UNIT_KASCII (1 << UNIT_V_KASCII) +#define UNIT_V_PASCII (UNIT_V_UF + 0) /* punch ASCII */ +#define UNIT_PASCII (1 << UNIT_V_PASCII) + +extern int32 M[]; +extern int32 int_hwre[API_HLVL+1], PC, ASW; +extern int32 sim_switches; +extern int32 sim_is_running; +extern UNIT cpu_unit; + +int32 clk_state = 0; +int32 ptr_err = 0, ptr_stopioe = 0, ptr_state = 0; +int32 ptp_err = 0, ptp_stopioe = 0; +int32 tti_2nd = 0; /* 2nd char waiting */ +int32 tty_shift = 0; /* KSR28 shift state */ +int32 tti_fdpx = 0; /* prog mode full duplex */ +int32 clk_tps = 60; /* ticks/second */ +int32 tmxr_poll = 16000; /* term mux poll */ +uint32 clk_task_last = 0; +uint32 clk_task_timer = 0; + +const int32 asc_to_baud[128] = { + 000,000,000,000,000,000,000,064, /* bell */ + 000,000,0110,000,000,0102,000,000, /* lf, cr */ + 000,000,000,000,000,000,000,000, + 000,000,000,000,000,000,000,000, + 0104,066,061,045,062,000,053,072, /* space - ' */ + 076,051,000,000,046,070,047,067, /* ( - / */ + 055,075,071,060,052,041,065,074, /* 0 - 7 */ + 054,043,056,057,000,000,000,063, /* 8 - ? */ + 000,030,023,016,022,020,026,013, /* @ - G */ + 005,014,032,036,011,007,006,003, /* H - O */ + 015,035,012,024,001,034,017,031, /* P - W */ + 027,025,021,000,000,000,000,000, /* X - _ */ + 000,030,023,016,022,020,026,013, /* ` - g */ + 005,014,032,036,011,007,006,003, /* h - o */ + 015,035,012,024,001,034,017,031, /* p - w */ + 027,025,021,000,000,000,000,000 /* x - DEL */ + }; + +const char baud_to_asc[64] = { + 0 ,'T',015,'O',' ','H','N','M', + 012,'L','R','G','I','P','C','V', + 'E','Z','D','B','S','Y','F','X', + 'A','W','J', 0 ,'U','Q','K', 0, + 0 ,'5','\r','9',' ','#',',','.', + 012,')','4','&','8','0',':',';', + '3','"','$','?','\a','6','!','/', + '-','2','\'',0 ,'7','1','(', 0 + }; + +int32 ptr (int32 dev, int32 pulse, int32 dat); +int32 ptp (int32 dev, int32 pulse, int32 dat); +int32 tti (int32 dev, int32 pulse, int32 dat); +int32 tto (int32 dev, int32 pulse, int32 dat); +int32 clk_iors (void); +int32 ptr_iors (void); +int32 ptp_iors (void); +int32 tti_iors (void); +int32 tto_iors (void); +t_stat clk_svc (UNIT *uptr); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat ptr_attach (UNIT *uptr, char *cptr); +t_stat ptp_attach (UNIT *uptr, char *cptr); +t_stat ptr_detach (UNIT *uptr); +t_stat ptp_detach (UNIT *uptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); +int32 clk_task_upd (t_bool clr); + +extern int32 upd_iors (void); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit + clk_reg CLK register list +*/ + +DIB clk_dib = { 0, 0, &clk_iors, { NULL } }; + +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), 16000 }; + +REG clk_reg[] = { + { FLDATA (INT, int_hwre[API_CLK], INT_V_CLK) }, + { FLDATA (DONE, int_hwre[API_CLK], INT_V_CLK) }, + { FLDATA (ENABLE, clk_state, 0) }, +#if defined (PDP15) + { ORDATA (TASKTIMER, clk_task_timer, 18) }, + { DRDATA (TASKLAST, clk_task_last, 32), REG_HRO }, +#endif + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &clk_show_freq, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, 0 + }; + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit + ptr_reg PTR register list +*/ + +DIB ptr_dib = { DEV_PTR, 1, &ptr_iors, { &ptr } }; + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 18) }, + { FLDATA (INT, int_hwre[API_PTR], INT_V_PTR) }, + { FLDATA (DONE, int_hwre[API_PTR], INT_V_PTR) }, +#if defined (IOS_PTRERR) + { FLDATA (ERR, ptr_err, 0) }, +#endif + { ORDATA (STATE, ptr_state, 5), REG_HRO }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } + }; + +MTAB ptr_mod[] = { + { UNIT_RASCII, UNIT_RASCII, "even parity ASCII", NULL }, + { UNIT_KASCII, UNIT_KASCII, "forced parity ASCII", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, &ptr_attach, &ptr_detach, + &ptr_dib, 0 + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit + ptp_reg PTP register list +*/ + +DIB ptp_dib = { DEV_PTP, 1, &ptp_iors, { &ptp } }; + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (INT, int_hwre[API_PTP], INT_V_PTP) }, + { FLDATA (DONE, int_hwre[API_PTP], INT_V_PTP) }, +#if defined (IOS_PTPERR) + { FLDATA (ERR, ptp_err, 0) }, +#endif + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +MTAB ptp_mod[] = { + { UNIT_PASCII, UNIT_PASCII, "7b ASCII", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, &ptp_attach, &ptp_detach, + &ptp_dib, 0 + }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit + tti_reg TTI register list +*/ + +#if defined (KSR28) +#define TTI_WIDTH 5 +#define TTI_FIGURES (1 << TTI_WIDTH) +#define TTI_BOTH (1 << (TTI_WIDTH + 1)) +#define BAUDOT_LETTERS 037 +#define BAUDOT_FIGURES 033 + +#else + +#define TTI_WIDTH 8 +#endif + +#define TTI_MASK ((1 << TTI_WIDTH) - 1) +#define TTUF_V_HDX (TTUF_V_UF + 0) /* half duplex */ +#define TTUF_HDX (1 << TTUF_V_HDX) + +DIB tti_dib = { DEV_TTI, 1, &tti_iors, { &tti } }; + +UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE+TT_MODE_KSR+TTUF_HDX, 0), 0 }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, TTI_WIDTH) }, +#if defined (KSR28) + { ORDATA (BUF2ND, tti_2nd, TTI_WIDTH), REG_HRO }, +#endif + { FLDATA (INT, int_hwre[API_TTI], INT_V_TTI) }, + { FLDATA (DONE, int_hwre[API_TTI], INT_V_TTI) }, +#if defined (PDP15) + { FLDATA (FDPX, tti_fdpx, 0) }, +#endif + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { +#if !defined (KSR28) + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, +#endif + { TTUF_HDX, 0 , "full duplex", "FDX", NULL }, + { TTUF_HDX, TTUF_HDX, "half duplex", "HDX", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno, NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, 0 + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit + tto_reg TTO register list +*/ + +#if defined (KSR28) +#define TTO_WIDTH 5 +#define TTO_FIGURES (1 << TTO_WIDTH) + +#else + +#define TTO_WIDTH 8 +#endif + +#define TTO_MASK ((1 << TTO_WIDTH) - 1) + +DIB tto_dib = { DEV_TTO, 1, &tto_iors, { &tto } }; + +UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), 1000 }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, TTO_WIDTH) }, +#if defined (KSR28) + { FLDATA (SHIFT, tty_shift, 0), REG_HRO }, +#endif + { FLDATA (INT, int_hwre[API_TTO], INT_V_TTO) }, + { FLDATA (DONE, int_hwre[API_TTO], INT_V_TTO) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { +#if !defined (KSR28) + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, +#endif + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_devno }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, 0 + }; + +/* Clock: IOT routine */ + +int32 clk (int32 dev, int32 pulse, int32 dat) +{ +if (pulse & 001) { /* CLSF */ + if (TST_INT (CLK)) dat = dat | IOT_SKP; + } +if (pulse & 004) { /* CLON/CLOF */ + CLR_INT (CLK); /* clear flag */ + if (pulse & 040) clk_state = 1; /* CLON */ + else clk_state = 0; /* CLOF */ + } +return dat; +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +int32 t; + +t = sim_rtc_calb (clk_tps); /* calibrate clock */ +tmxr_poll = t; /* set mux poll */ +sim_activate (&clk_unit, t); /* reactivate unit */ +#if defined (PDP15) +clk_task_upd (FALSE); /* update task timer */ +#endif +if (clk_state) { /* clock on? */ + M[7] = (M[7] + 1) & DMASK; /* incr counter */ + if (M[7] == 0) SET_INT (CLK); /* ovrflo? set flag */ + } +return SCPE_OK; +} + +#if defined (PDP15) + +/* Task timer update (PDP-15 XVM only) + + The task timer increments monotonically at 100Khz. Since this can't be + simulated accurately, updates are done by interpolation since the last + reading. The timer is also updated at clock events to keep the cycle + counters from wrapping around more than once between updates. */ + +int32 clk_task_upd (t_bool clr) +{ +uint32 delta, val, iusec10; +uint32 cur = sim_grtime (); +uint32 old = clk_task_timer; +double usec10; + +if (cur > clk_task_last) delta = cur - clk_task_last; +else delta = clk_task_last - cur; +usec10 = ((((double) delta) * 100000.0) / + (((double) tmxr_poll) * ((double) clk_tps))); +iusec10 = (int32) usec10; +val = (clk_task_timer + iusec10) & DMASK; +if (clr) clk_task_timer = 0; +else clk_task_timer = val; +clk_task_last = cur; +return ((int32) val); +} + +#endif + +/* IORS service */ + +int32 clk_iors (void) +{ +return (TST_INT (CLK)? IOS_CLK: 0); +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +int32 t; + +CLR_INT (CLK); /* clear flag */ +if (!sim_is_running) { /* RESET (not CAF)? */ + t = sim_rtc_init (clk_unit.wait); /* init calibration */ + tmxr_poll = t; /* set mux poll */ + sim_activate_abs (&clk_unit, t); /* activate unit */ + clk_state = 0; /* clock off */ + clk_task_timer = 0; + clk_task_last = 0; + } +return SCPE_OK; +} + +/* Set frequency */ + +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val != 50) && (val != 60)) return SCPE_IERR; +clk_tps = val; +return SCPE_OK; +} + +/* Show frequency */ + +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, (clk_tps == 50)? "50Hz": "60Hz"); +return SCPE_OK; +} + +/* Paper tape reader out-of-tape handling + + The PDP-4 and PDP-7 readers behaved like most early DEC readers; when + they ran out of tape, they hung. It was up to the program to sense this + condition by running a timer. + + The PDP-9 reader controller synthesized the out of tape condition by + noticing whether there was a transition on the feed hole within a window. + The out-of-tape flag was treated like the reader flag in most cases. + + The PDP-15 reader controller received the out-of-tape flag as a static + condition from the reader itself and simply reported it via IORS. */ + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 dev, int32 pulse, int32 dat) +{ +if (pulse & 001) { /* RSF */ + if (TST_INT (PTR)) dat = dat | IOT_SKP; + } +if (pulse & 002) { /* RRB, RCF */ + CLR_INT (PTR); /* clear flag */ + dat = dat | ptr_unit.buf; /* return buffer */ + } +if (pulse & 004) { /* RSA, RSB */ + ptr_state = (pulse & 040)? 18: 0; /* set mode */ + CLR_INT (PTR); /* clear flag */ +#if !defined (PDP15) /* except on PDP15 */ + ptr_err = 0; /* clear error */ +#endif + ptr_unit.buf = 0; /* clear buffer */ + sim_activate (&ptr_unit, ptr_unit.wait); + } +return dat; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */ +#if defined (IOS_PTRERR) + SET_INT (PTR); /* if err, set flag */ + ptr_err = 1; /* set error */ +#endif + return IORETURN (ptr_stopioe, SCPE_UNATT); + } +if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ +#if defined (IOS_PTRERR) + SET_INT (PTR); /* if err, set flag */ + ptr_err = 1; /* set error */ +#endif + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } +if (ptr_state == 0) { /* ASCII */ + if (ptr_unit.flags & UNIT_RASCII) { /* want parity? */ + ptr_unit.buf = temp = temp & 0177; /* parity off */ + while (temp = temp & (temp - 1)) + ptr_unit.buf = ptr_unit.buf ^ 0200; /* count bits */ + ptr_unit.buf = ptr_unit.buf ^ 0200; /* set even parity */ + } + else if (ptr_unit.flags & UNIT_KASCII) /* KSR ASCII? */ + ptr_unit.buf = (temp | 0200) & 0377; /* forced parity */ + else ptr_unit.buf = temp & 0377; + } +else if (temp & 0200) { /* binary */ + ptr_state = ptr_state - 6; + ptr_unit.buf = ptr_unit.buf | ((temp & 077) << ptr_state); + } +if (ptr_state == 0) SET_INT (PTR); /* if done, set flag */ +else sim_activate (&ptr_unit, ptr_unit.wait); /* else restart */ +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_state = 0; /* clear state */ +ptr_unit.buf = 0; +CLR_INT (PTR); /* clear flag */ +#if defined (PDP15) /* PDP15, static err */ +if (((ptr_unit.flags & UNIT_ATT) == 0) || feof (ptr_unit.fileref)) + ptr_err = 1; +else +#endif +ptr_err = 0; /* all other, clr err */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* IORS service */ + +int32 ptr_iors (void) +{ +return ((TST_INT (PTR)? IOS_PTR: 0) +#if defined (IOS_PTRERR) + | (ptr_err? IOS_PTRERR: 0) +#endif + ); +} + +/* Attach routine */ + +t_stat ptr_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +if (reason != SCPE_OK) return reason; +ptr_err = 0; /* attach clrs error */ +ptr_unit.flags = ptr_unit.flags & ~(UNIT_RASCII|UNIT_KASCII); +if (sim_switches & SWMASK ('A')) + ptr_unit.flags = ptr_unit.flags | UNIT_RASCII; +if (sim_switches & SWMASK ('K')) + ptr_unit.flags = ptr_unit.flags | UNIT_KASCII; +return SCPE_OK; +} + +/* Detach routine */ + +t_stat ptr_detach (UNIT *uptr) +{ +#if defined (PDP15) +ptr_err = 1; +#endif +ptr_unit.flags = ptr_unit.flags & ~UNIT_RASCII; +return detach_unit (uptr); +} + +/* Hardware RIM loader routines, PDP-7/9/15 */ + +int32 ptr_getw (UNIT *uptr, int32 *hi) +{ +int32 word, bits, st, ch; + +word = st = bits = 0; +do { + if ((ch = getc (uptr->fileref)) == EOF) return -1; + uptr->pos = uptr->pos + 1; + if (ch & 0200) { + word = (word << 6) | (ch & 077); + bits = (bits << 1) | ((ch >> 6) & 1); + st++; + } + } while (st < 3); +if (hi != NULL) *hi = bits; +return word; +} + +t_stat ptr_rim_load (UNIT *uptr, int32 origin) +{ +int32 bits, val; + +for (;;) { /* word loop */ + if ((val = ptr_getw (uptr, &bits)) < 0) return SCPE_FMT; + if (bits & 1) { /* end of tape? */ + if ((val & 0760000) == OP_JMP) { + PC = ((origin - 1) & 060000) | (val & 017777); + return SCPE_OK; + } + else if (val == OP_HLT) return STOP_HALT; + break; + } + else if (MEM_ADDR_OK (origin)) M[origin++] = val; + } +return SCPE_FMT; +} + +#if defined (PDP4) || defined (PDP7) + +/* Bootstrap routine, PDP-4 and PDP-7 + + In a 4K system, the boostrap resides at 7762-7776. + In an 8K or greater system, the bootstrap resides at 17762-17776. + Because the program is so small, simple masking can be + used to remove addr<5> for a 4K system. */ + +#define BOOT_START 017577 +#define BOOT_FPC 017577 /* funny format loader */ +#define BOOT_RPC 017770 /* RIM loader */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int)) + +static const int32 boot_rom[] = { + 0700144, /* rsb */ + 0117762, /* ff, jsb r1b */ + 0057666, /* dac done 1 */ + 0117762, /* jms r1b */ + 0057667, /* dac done 2 */ + 0117762, /* jms r1b */ + 0040007, /* dac conend */ + 0057731, /* dac conbeg */ + 0440007, /* isz conend */ + 0117762, /* blk, jms r1b */ + 0057673, /* dac cai */ + 0741100, /* spa */ + 0617665, /* jmp done */ + 0117762, /* jms r1b */ + 0057777, /* dac tem1 */ + 0317673, /* add cai */ + 0057775, /* dac cks */ + 0117713, /* jms r1a */ + 0140010, /* dzm word */ + 0457777, /* cont, isz tem1 */ + 0617632, /* jmp cont1 */ + 0217775, /* lac cks */ + 0740001, /* cma */ + 0740200, /* sza */ + 0740040, /* hlt */ + 0700144, /* rsb */ + 0617610, /* jmp blk */ + 0117713, /* cont1, jms r1a */ + 0057762, /* dac tem2 */ + 0117713, /* jms r1a */ + 0742010, /* rtl */ + 0742010, /* rtl */ + 0742010, /* rtl */ + 0742010, /* rtl */ + 0317762, /* add tem2 */ + 0057762, /* dac tem2 */ + 0117713, /* jms r1a */ + 0742020, /* rtr */ + 0317726, /* add cdsp */ + 0057713, /* dac r1a */ + 0517701, /* and ccma */ + 0740020, /* rar */ + 0317762, /* add tem2 */ + 0437713, /* xct i r1a */ + 0617622, /* jmp cont */ + 0617672, /* dsptch, jmp code0 */ + 0617670, /* jmp code1 */ + 0617700, /* jmp code2 */ + 0617706, /* jmp code3 */ + 0417711, /* xct code4 */ + 0617732, /* jmp const */ + 0740000, /* nop */ + 0740000, /* nop */ + 0740000, /* nop */ + 0200007, /* done, lac conend */ + 0740040, /* xx */ + 0740040, /* xx */ + 0517727, /* code1, and imsk */ + 0337762, /* add i tem2 */ + 0300010, /* code0, add word */ + 0740040, /* cai, xx */ + 0750001, /* clc */ + 0357673, /* tad cai */ + 0057673, /* dac cai */ + 0617621, /* jmp cont-1 */ + 0711101, /* code2, spa cla */ + 0740001, /* ccma, cma */ + 0277762, /* xor i tem2 */ + 0300010, /* add word */ + 0040010, /* code2a, dac word */ + 0617622, /* jmp cont */ + 0057711, /* code3, dac code4 */ + 0217673, /* lac cai */ + 0357701, /* tad ccma */ + 0740040, /* code4, xx */ + 0617622, /* jmp cont */ + 0000000, /* r1a, 0 */ + 0700101, /* rsf */ + 0617714, /* jmp .-1 */ + 0700112, /* rrb */ + 0700104, /* rsa */ + 0057730, /* dac tem */ + 0317775, /* add cks */ + 0057775, /* dac cks */ + 0217730, /* lac tem */ + 0744000, /* cll */ + 0637713, /* jmp i r1a */ + 0017654, /* cdsp, dsptch */ + 0760000, /* imsk, 760000 */ + 0000000, /* tem, 0 */ + 0000000, /* conbeg, 0 */ + 0300010, /* const, add word */ + 0060007, /* dac i conend */ + 0217731, /* lac conbeg */ + 0040010, /* dac index */ + 0220007, /* lac i conend */ + 0560010, /* con1, sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0560010, /* sad i index */ + 0617752, /* jmp find */ + 0617737, /* jmp con1 */ + 0200010, /* find, lac index */ + 0540007, /* sad conend */ + 0440007, /* isz conend */ + 0617704, /* jmp code2a */ + 0000000, + 0000000, + 0000000, + 0000000, + 0000000, /* r1b, 0 */ + 0700101, /* rsf */ + 0617763, /* jmp .-1 */ + 0700112, /* rrb */ + 0700144, /* rsb */ + 0637762, /* jmp i r1b */ + 0700144, /* go, rsb */ + 0117762, /* g, jms r1b */ + 0057775, /* dac cks */ + 0417775, /* xct cks */ + 0117762, /* jms r1b */ + 0000000, /* cks, 0 */ + 0617771 /* jmp g */ + }; + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +int32 i, mask, wd; +extern int32 sim_switches; + +#if defined (PDP7) +if (sim_switches & SWMASK ('H')) /* hardware RIM load? */ + return ptr_rim_load (&ptr_unit, ASW); +#endif +if (ptr_dib.dev != DEV_PTR) return STOP_NONSTD; /* non-std addr? */ +if (MEMSIZE < 8192) mask = 0767777; /* 4k? */ +else mask = 0777777; +for (i = 0; i < BOOT_LEN; i++) { + wd = boot_rom[i]; + if ((wd >= 0040000) && (wd < 0640000)) wd = wd & mask; + M[(BOOT_START & mask) + i] = wd; + } +PC = ((sim_switches & SWMASK ('F'))? BOOT_FPC: BOOT_RPC) & mask; +return SCPE_OK; +} + +#else + +/* PDP-9 and PDP-15 have built-in hardware RIM loaders */ + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +return ptr_rim_load (&ptr_unit, ASW); +} + +#endif + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 dev, int32 pulse, int32 dat) +{ +if (pulse & 001) { /* PSF */ + if (TST_INT (PTP)) dat = dat | IOT_SKP; + } +if (pulse & 002) CLR_INT (PTP); /* PCF */ +if (pulse & 004) { /* PSA, PSB, PLS */ + CLR_INT (PTP); /* clear flag */ + ptp_unit.buf = (pulse & 040)? /* load punch buf */ + (dat & 077) | 0200: dat & 0377; /* bin or alpha */ + sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ + } +return dat; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +SET_INT (PTP); /* set flag */ +if ((ptp_unit.flags & UNIT_ATT) == 0) { /* not attached? */ + ptp_err = 1; /* set error */ + return IORETURN (ptp_stopioe, SCPE_UNATT); + } +if (ptp_unit.flags & UNIT_PASCII) { /* ASCII mode? */ + ptp_unit.buf = ptp_unit.buf & 0177; /* force 7b */ + if ((ptp_unit.buf == 0) || (ptp_unit.buf == 0177)) + return SCPE_OK; /* skip null, del */ + } +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { /* I/O error? */ + ptp_err = 1; /* set error */ + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* IORS service */ + +int32 ptp_iors (void) +{ +return ((TST_INT (PTP)? IOS_PTP: 0) +#if defined (IOS_PTPERR) + | (ptp_err? IOS_PTPERR: 0) +#endif + ); +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +CLR_INT (PTP); /* clear flag */ +ptp_err = (ptp_unit.flags & UNIT_ATT)? 0: 1; +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat ptp_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +if (reason != SCPE_OK) return reason; +ptp_err = 0; +ptp_unit.flags = ptp_unit.flags & ~UNIT_PASCII; +if (sim_switches & SWMASK ('A')) + ptp_unit.flags = ptp_unit.flags | UNIT_PASCII; +return reason; +} + +/* Detach routine */ + +t_stat ptp_detach (UNIT *uptr) +{ +ptp_err = 1; +ptp_unit.flags = ptp_unit.flags & ~UNIT_PASCII; +return detach_unit (uptr); +} + +/* Terminal input: IOT routine */ + +int32 tti (int32 dev, int32 pulse, int32 dat) +{ +if (pulse & 001) { /* KSF */ + if (TST_INT (TTI)) dat = dat | IOT_SKP; + } +if (pulse & 002) { /* KRS/KRB */ + CLR_INT (TTI); /* clear flag */ + dat = dat | tti_unit.buf & TTI_MASK; /* return buffer */ +#if defined (PDP15) + if (pulse & 020) tti_fdpx = 1; /* KRS? */ + else tti_fdpx = 0; /* no, KRB */ +#endif + } +if (pulse & 004) { /* IORS */ + dat = dat | upd_iors (); + } +return dat; +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +#if defined (KSR28) /* Baudot... */ +int32 in, c, out; + +sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ +if (tti_2nd) { /* char waiting? */ + uptr->buf = tti_2nd; /* return char */ + tti_2nd = 0; /* not waiting */ + } +else { + if ((in = sim_poll_kbd ()) < SCPE_KFLAG) return in; + c = asc_to_baud[in & 0177]; /* translate char */ + if (c == 0) return SCPE_OK; /* untranslatable? */ + if ((c & TTI_BOTH) || /* case insensitive? */ + (((c & TTI_FIGURES)? 1: 0) == tty_shift)) /* right case? */ + uptr->buf = c & TTI_MASK; + else { /* send case change */ + if (c & TTI_FIGURES) { /* to figures? */ + uptr->buf = BAUDOT_FIGURES; + tty_shift = 1; + } + else { /* no, to letters */ + uptr->buf = BAUDOT_LETTERS; + tty_shift = 0; + } + tti_2nd = c & TTI_MASK; /* save actual char */ + } + if ((uptr->flags & TTUF_HDX) && /* half duplex? */ + ((out = sim_tt_outcvt (in, TT_GET_MODE (uptr->flags) | TTUF_KSR)) >= 0)) { + sim_putchar (out); + tto_unit.pos = tto_unit.pos + 1; + } + } + +#else /* ASCII... */ +int32 c, out; + +sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +out = c & 0177; /* mask echo to 7b */ +if (c & SCPE_BREAK) c = 0; /* break? */ +else c = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); +if ((uptr->flags & TTUF_HDX) && !tti_fdpx && out && /* half duplex and */ + ((out = sim_tt_outcvt (out, TT_GET_MODE (uptr->flags) | TTUF_KSR)) >= 0)) { + sim_putchar (out); /* echo */ + tto_unit.pos = tto_unit.pos + 1; + } +uptr->buf = c; /* got char */ + +#endif +uptr->pos = uptr->pos + 1; +SET_INT (TTI); /* set flag */ +return SCPE_OK; +} + +/* IORS service */ + +int32 tti_iors (void) +{ +return (TST_INT (TTI)? IOS_TTI: 0); +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; /* clear buffer */ +tti_2nd = 0; +tty_shift = 0; /* clear state */ +tti_fdpx = 0; /* clear dpx mode */ +CLR_INT (TTI); /* clear flag */ +sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmxr_poll)); +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 dev, int32 pulse, int32 dat) +{ +if (pulse & 001) { /* TSF */ + if (TST_INT (TTO)) dat = dat | IOT_SKP; + } +if (pulse & 002) CLR_INT (TTO); /* clear flag */ +if (pulse & 004) { /* load buffer */ + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + tto_unit.buf = dat & TTO_MASK; /* load buffer */ + } +return dat; +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +#if defined (KSR28) /* Baudot... */ +if (uptr->buf == BAUDOT_FIGURES) /* set figures? */ + tty_shift = 1; +else if (uptr->buf == BAUDOT_LETTERS) /* set letters? */ + tty_shift = 0; +else { + c = baud_to_asc[uptr->buf | (tty_shift << 5)]; /* translate */ + +#else +c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); +if (c >= 0) { + +#endif + + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* retry? */ + return ((r == SCPE_STALL)? SCPE_OK: r); + } + } +SET_INT (TTO); /* set flag */ +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +/* IORS service */ + +int32 tto_iors (void) +{ +return (TST_INT (TTO)? IOS_TTO: 0); +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; /* clear buffer */ +tty_shift = 0; /* clear state */ +CLR_INT (TTO); /* clear flag */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Set mode */ + +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; +tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; +return SCPE_OK; +} diff --git a/PDP18B/pdp18b_sys.c b/PDP18B/pdp18b_sys.c new file mode 100644 index 0000000..c68d400 --- /dev/null +++ b/PDP18B/pdp18b_sys.c @@ -0,0 +1,1210 @@ +/* pdp18b_sys.c: 18b PDP's simulator interface + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Oct-06 RMS Added infinite loop stop + 18-Oct-06 RMS Re-ordered device list + 02-Oct-06 RMS Added RDCLK instruction + 12-Jun-06 RMS Added Fiodec, Baudot display + RMS Generalized LOAD to handle HRI, RIM, or BIN files + 22-Jul-05 RMS Removed AAS, error in V1 reference manual + 09-Jan-04 RMS Fixed instruction table errors + 18-Oct-03 RMS Added DECtape off reel message + 30-Jul-03 RMS Fixed FPM class mask + 18-Jul-03 RMS Added FP15 support + 02-Mar-03 RMS Split loaders apart for greater flexibility + 09-Feb-03 RMS Fixed bug in FMTASC (found by Hans Pufal) + 31-Jan-03 RMS Added support for RB09 + 05-Oct-02 RMS Added variable device number support + 25-Jul-02 RMS Added PDP-4 DECtape support + 10-Feb-02 RMS Added PDP-7 DECtape IOT's + 03-Feb-02 RMS Fixed typo (found by Robert Alan Byer) + 17-Sep-01 RMS Removed multiconsole support + 27-May-01 RMS Added second Teletype support + 18-May-01 RMS Added PDP-9,-15 API IOT's + 12-May-01 RMS Fixed bug in RIM loaders + 14-Mar-01 RMS Added extension detection of RIM format tapes + 21-Jan-01 RMS Added DECtape support + 30-Nov-00 RMS Added PDP-9,-15 RIM/BIN loader format + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface + 20-Oct-97 RMS Fixed endian dependence in RIM loader + (found by Michael Somos) +*/ + +#include "pdp18b_defs.h" +#include + +extern DEVICE cpu_dev; +#if defined (PDP15) +extern DEVICE fpp_dev; +#endif +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern UNIT tti_unit, tto_unit; +extern DEVICE clk_dev; +#if defined (TYPE62) +extern DEVICE lp62_dev; +#endif +#if defined (TYPE647) +extern DEVICE lp647_dev; +#endif +#if defined (LP09) +extern DEVICE lp09_dev; +#endif +#if defined (LP15) +extern DEVICE lp15_dev; +#endif +extern DEVICE dt_dev; +#if defined (DRM) +extern DEVICE drm_dev; +#endif +#if defined (RB) +extern DEVICE rb_dev; +#endif +#if defined (RF) +extern DEVICE rf_dev; +#endif +#if defined (RP) +extern DEVICE rp_dev; +#endif +#if defined (MTA) +extern DEVICE mt_dev; +#endif +#if defined (TTY1) +extern DEVICE tti1_dev, tto1_dev; +extern UNIT tti1_unit, tto1_unit; +#endif +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern int32 M[]; +extern int32 memm; +extern int32 PC; +extern const char asc_to_baud[128]; +extern const char baud_to_asc[64]; +extern const char fio_to_asc[64]; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +#if defined (PDP4) +char sim_name[] = "PDP-4"; +#elif defined (PDP7) +char sim_name[] = "PDP-7"; +#elif defined (PDP9) +char sim_name[] = "PDP-9"; +#elif defined (PDP15) +char sim_name[] = "PDP-15"; +#endif + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 2; + +DEVICE *sim_devices[] = { + &cpu_dev, + &clk_dev, +#if defined (PDP15) + &fpp_dev, +#endif + &ptr_dev, + &ptp_dev, + &tti_dev, + &tto_dev, +#if defined (TYPE62) + &lp62_dev, +#endif +#if defined (TYPE647) + &lp647_dev, +#endif +#if defined (LP09) + &lp09_dev, +#endif +#if defined (LP15) + &lp15_dev, +#endif +#if defined (DRM) + &drm_dev, +#endif +#if defined (RB) + &rb_dev, +#endif +#if defined (RF) + &rf_dev, +#endif +#if defined (RP) + &rp_dev, +#endif + &dt_dev, +#if defined (MTA) + &mt_dev, +#endif +#if defined (TTY1) + &tti1_dev, &tto1_dev, +#endif + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Undefined instruction", + "HALT instruction", + "Breakpoint", + "Nested XCT's", + "Invalid API interrupt", + "Non-standard device number", + "Memory management error", + "FP15 instruction disabled", + "DECtape off reel", + "Infinite loop" + }; + +/* Binary loaders */ + +int32 getword (FILE *fileref, int32 *hi) +{ +int32 word, bits, st, ch; + +word = st = bits = 0; +do { + if ((ch = getc (fileref)) == EOF) return -1; + if (ch & 0200) { + word = (word << 6) | (ch & 077); + bits = (bits << 1) | ((ch >> 6) & 1); + st++; + } + } while (st < 3); +if (hi != NULL) *hi = bits; +return word; +} + +/* PDP-4/PDP-7 RIM format loader + + Tape format + dac addr + data + : + dac addr + data + jmp addr or hlt +*/ + +t_stat rim_load_47 (FILE *fileref, char *cptr) +{ +int32 origin, val; + +if (*cptr != 0) return SCPE_2MARG; +origin = 0200; +for (;;) { + if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; + if ((val & 0760000) == 0040000) { /* DAC? */ + origin = val & 017777; + if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; + if (MEM_ADDR_OK (origin)) M[origin++] = val; + } + else if ((val & 0760000) == OP_JMP) { /* JMP? */ + PC = ((origin - 1) & 060000) | (val & 017777); + return SCPE_OK; + } + else if (val == OP_HLT) break; /* HLT? */ + else return SCPE_FMT; /* error */ + } +return SCPE_OK; /* done */ +} + +/* PDP-7/9/15 hardware read-in format loader + + Tape format (read in address specified externally) + data + : + data + word to execute (bit 1 of last character set) +*/ + +t_stat hri_load_7915 (FILE *fileref, char *cptr) +{ +int32 bits, origin, val; +char gbuf[CBUFSIZE]; +t_stat r; + +if (*cptr != 0) { /* more input? */ + cptr = get_glyph (cptr, gbuf, 0); /* get origin */ + origin = get_uint (gbuf, 8, AMASK, &r); + if (r != SCPE_OK) return r; + if (*cptr != 0) return SCPE_ARG; /* no more */ + } +else origin = 0200; /* default 200 */ + +for (;;) { /* word loop */ + if ((val = getword (fileref, &bits)) < 0) return SCPE_FMT; + if (bits & 1) { /* end of tape? */ + if ((val & 0760000) == OP_JMP) PC = + ((origin - 1) & 060000) | (val & 017777); + else if (val != OP_HLT) return SCPE_FMT; + break; + } + else if (MEM_ADDR_OK (origin)) M[origin++] = val; + } +return SCPE_OK; +} + +/* PDP-9/15 BIN format loader + + BIN format (starts after RIM bootstrap) + block/ origin (>= 0) + count + checksum + data + : + data + block/ + : + endblock/ origin (< 0) +*/ + +t_stat bin_load_915 (FILE *fileref, char *cptr) +{ +int32 i, val, bits, origin, count, cksum; + +if (*cptr != 0) return SCPE_2MARG; /* no arguments */ +do { + val = getword (fileref, & bits); /* find end RIM */ + } while ((val >= 0) && ((bits & 1) == 0)); +if (val < 0) rewind (fileref); /* no RIM? rewind */ +for (;;) { /* block loop */ + if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; + if (val & SIGN) { + if (val != DMASK) PC = val & 077777; + break; + } + cksum = origin = val; /* save origin */ + if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; + cksum = cksum + val; /* add to cksum */ + count = (-val) & DMASK; /* save count */ + if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; + cksum = cksum + val; /* add to cksum */ + for (i = 0; i < count; i++) { + if ((val = getword (fileref, NULL)) < 0) return SCPE_FMT; + cksum = cksum + val; + if (MEM_ADDR_OK (origin)) M[origin++] = val; + } + if ((cksum & DMASK) != 0) return SCPE_CSUM; + } +return SCPE_OK; +} + +/* Binary loader, all formats */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +extern int32 sim_switches; + +if (flag != 0) return SCPE_NOFNC; +if (sim_switches & SWMASK ('S')) /* RIM format? */ + return rim_load_47 (fileref, cptr); +if (sim_switches & SWMASK ('R')) /* HRI format? */ + return hri_load_7915 (fileref, cptr); +if (!(sim_switches & SWMASK ('B')) && /* .rim extension? */ + match_ext (fnam, "RIM")) { + int32 val, bits; + do { /* look for HRI flag */ + val = getword (fileref, &bits); + } while ((val >= 0) && ((bits & 1) == 0)); + rewind (fileref); /* rewind file */ + if (val < 0) return rim_load_47 (fileref, cptr); /* eof reached? */ + return hri_load_7915 (fileref, cptr); /* no, HRI */ + } +return bin_load_915 (fileref, cptr); /* must be BIN */ +} + +/* Symbol tables */ + +#define I_V_FL 18 /* inst class */ +#define I_M_FL 017 /* class mask */ +#define I_V_DC 22 /* default count */ +#define I_V_NPN 0 /* no operand */ +#define I_V_NPI 1 /* no operand IOT */ +#define I_V_IOT 2 /* IOT */ +#define I_V_MRF 3 /* memory reference */ +#define I_V_OPR 4 /* OPR */ +#define I_V_LAW 5 /* LAW */ +#define I_V_XR 6 /* index */ +#define I_V_XR9 7 /* index literal */ +#define I_V_EST 8 /* EAE setup */ +#define I_V_ESH 9 /* EAE shift */ +#define I_V_EMD 10 /* EAE mul-div */ +#define I_V_FPM 11 /* FP15 mem ref */ +#define I_V_FPI 12 /* FP15 indirect */ +#define I_V_FPN 13 /* FP15 no operand */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_NPI (I_V_NPI << I_V_FL) +#define I_IOT (I_V_IOT << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_OPR (I_V_OPR << I_V_FL) +#define I_LAW (I_V_LAW << I_V_FL) +#define I_XR (I_V_XR << I_V_FL) +#define I_XR9 (I_V_XR9 << I_V_FL) +#define I_EST (I_V_EST << I_V_FL) +#define I_ESH (I_V_ESH << I_V_FL) +#define I_EMD (I_V_EMD << I_V_FL) +#define I_FPM (I_V_FPM << I_V_FL) +#define I_FPI (I_V_FPI << I_V_FL) +#define I_FPN (I_V_FPN << I_V_FL) +#define MD(x) ((I_EMD) + ((x) << I_V_DC)) + +static const int32 masks[] = { + 0777777, 0777767, 0770000, 0760000, + 0763730, 0760000, 0777000, 0777000, + 0740700, 0760700, 0777700, 0777777, + 0777777, 0777777 + }; + +/* If both NPN (clear AC) and NPI versions of an IOT are defined, + the NPN version must come first */ + +static const char *opcode[] = { + "CAL", "DAC", "JMS", "DZM", /* mem refs */ + "LAC", "XOR", "ADD", "TAD", + "XCT", "ISZ", "AND", "SAD", + "JMP", + +#if defined (PDP9) || defined (PDP15) /* mem ref ind */ + "CAL*", "DAC*", "JMS*", "DZM*", /* normal */ + "LAC*", "XOR*", "ADD*", "TAD*", + "XCT*", "ISZ*", "AND*", "SAD*", + "JMP*", +#else + "CAL I", "DAC I", "JMS I", "DZM I", /* decode only */ + "LAC I", "XOR I", "ADD I", "TAD I", + "XCT I", "ISZ I", "AND I", "SAD I", + "JMP I", +#endif + + "LAW", /* LAW */ + + "LACQ", "LACS", "ABS", "GSM", "LMQ", /* EAE */ + "MUL", "MULS", "DIV", "DIVS", + "IDIV", "IDIVS", "FRDIV", "FRDIVS", + "NORM", "NORMS", + "MUY", "LLK MUY", "DVI", "LLK DVI", + "NMI", "NMIS", "LRS", "LRSS", + "LLS", "LLSS", "ALS", "ALSS", + "EAE-setup", "EAE", /* setup, general */ + + "CLSF", "IOF", "ION", "CLOF", "CLON", /* standard IO devs */ + "RSF", "RRB", "RCF", "RSA", "RSB", + "PSF", "PCF", "PSA", "PSB", "PLS", + "KSF", "KRB", "KCF", "IORS", "IOOS", + "TSF", "TCF", "TPC", "TLS", +#if defined (TYPE62) /* Type 62 */ + "LPSF", "LPCF", "LPLD", "LPSE", + "LSSF", "LSCF", "LSPR", +#endif +#if defined (TYPE647) /* Type 647 */ + "LPSF", "LPCB", "LPCD", "LPCD", "LPCD", + "LPL2", "LPLD", "LPL1", + "LPEF", "LPCF", "LPCF", "LPCF", "LPCF", + "LPPB", "LPLS", "LPPS", +#endif +#if defined (LP09) + "LSDF", "LSEF", "LSCF", "LPLD", + "LIOF", "LION", +#endif +#if defined (LP15) /* LP15 */ + "LPSF", "LPPM", "LPP1", "LPDI", + "LPRS", "LPOS", "LPEI", "LPCD", "LPCF", +#endif +#if defined (DRM) /* drum */ + "DRLR", "DRLW", "DRSS", "DRCS", + "DRSF", "DRSN", "DRCF", + "DRLCRD", "DRLCWR", "DRLBLK", "DRCONT", + "DRSF", "DRSOK", "DRCF", +#endif +#if defined (RB) /* RB09 */ + "DBCF", "DBRD", "DBLD", + "DBSF", "DBRS", "DBLW", + "DBCS", "DBLM", "DBLS", +#endif +#if defined (RF) /* RF09 */ + "DSSF", "DSCC", "DSCF", + "DRBR", "DRAL", "DSFX", "DRAH", + "DLBR", "DLAL", "DSCN", "DLAH", + "DLOK", "DSCD", "DSRS", + "DGHS", "DGSS", +#endif +#if defined (RP) + "DPSF", "DPSA", "DPSJ", "DPSE", + "DPRSA", "DPOSA", "DPRSB", "DPOSB", + "DPRM", "DPOM", + "DPLA", "DPCS", "DPCA", "DPWC", + "DPLM", "DPEM", "DPSN", + "DPRU", "DPOU", "DPRA", "DPOA", + "DPRC", "DPOC", "DPRW", "DPOW", + "DPCF", "DPLZ", "DPCN", "DPLO", "DPLF", +#endif +#if defined (MTA) /* TC59 */ + "MTTR", "MTCR", "MTSF", "MTRC", "MTAF", + "MTRS", "MTGO", "MTCM", "MTLC", +#endif +#if defined (TYPE550) /* Type 550 */ + "MMDF", "MMEF", "MMRD", "MMWR", + "MMBF", "MMRS", "MMLC", "MMSE", +#elif defined (TC02) /* TC02/TC15 */ + "DTCA", "DTRA", "DTXA", "DTLA", + "DTEF", "DTRB", "DTDF", +#endif +#if defined (TTY1) + "KSF1", "KRB1", + "TSF1", "TCF1", "TLS1", "TCF1!TLS1", +#endif +#if defined (PDP7) + "ITON", "TTS", "SKP7", "CAF", + "SEM", "EEM", "EMIR", "LEM", +#endif +#if defined (PDP9) + "SKP7", "SEM", "EEM", "LEM", + "LPDI", "LPEI", +#endif +#if defined (PDP15) + "SPCO", "SKP15", "RES", + "SBA", "DBA", "EBA", + "RDMM", "ORMM", "LDMM", "MPLR", + "ENB", "INH", + "RDCLK","MPRC", "IPFH", + "PAX", "PAL", "AAC", "PXA", + "AXS", "PXL", "PLA", "PLX", + "CLAC","CLX", "CLLR", "AXR", + + "FPT", /* FP15 */ + "ISB", "ESB", /* mem ref */ + "FSB", "URFSB", "UNFSB", "UUFSB", + "DSB", "URDSB", "UNDSB", "UUDSB", + "IRS", "ERS", + "FRS", "URFRS", "UNFRS", "UUFRS", + "DRS", "URDRS", "UNDRS", "UUDRS", + "IMP", "EMP", + "FMP", "URFMP", "UNFMP", "UUFMP", + "DMP", "URDMP", "UNDMP", "UUDMP", + "IDV", "EDV", + "FDV", "URFDV", "UNFDV", "UUFDV", + "DDV", "URDDV", "UNDDV", "UUDDV", + "IRD", "ERD", + "FRD", "URFRD", "UNFRD", "UUFRD", + "DRD", "URDRD", "UNDRD", "UUDRD", + "ILD", "ELD", + "FLD", "UNFLD", "DLD", "UNDLD", + "IST", "EST", + "FST", "URFST", "UNFST", "UUFST", + "DST", "UNDST", + "ILF", "UNILF", "ELF", "UNELF", + "FLX", "URFLX", "DLX", "URDLX", + "ILQ", "ELQ", + "FLQ", "UNFLQ", "DLQ", "UNDLQ", + "LJE", "SJE", + "IAD", "EAD", + "FAD", "URFAD", "UNFAD", "UUFAD", + "DAD", "URDAD", "UNDAD", "UUDAD", + "BZA", "BMA", "BLE", "BPA", + "BRU", "BNA", "BAC", + "ISB*", "ESB*", /* indirect */ + "FSB*", "URFSB*", "UNFSB*", "UUFSB*", + "DSB*", "URDSB*", "UNDSB*", "UUDSB*", + "IRS*", "ERS*", + "FRS*", "URFRS*", "UNFRS*", "UUFRS*", + "DRS*", "URDRS*", "UNDRS*", "UUDRS*", + "IMP*", "EMP*", + "FMP*", "URFMP*", "UNFMP*", "UUFMP*", + "DMP*", "URDMP*", "UNDMP*", "UUDMP*", + "IDV*", "EDV*", + "FDV*", "URFDV*", "UNFDV*", "UUFDV*", + "DDV*", "URDDV*", "UNDDV*", "UUDDV*", + "IRD*", "ERD", + "FRD*", "URFRD*", "UNFRD*", "UUFRD*", + "DRD*", "URDRD*", "UNDRD*", "UUDRD*", + "ILD*", "ELD", + "FLD*", "UNFLD*", "DLD*", "UNDLD*", + "IST*", "EST", + "FST*", "URFST*", "UNFST*", "UUFST*", + "DST*", "UNDST*", + "ILF*", "UNILF*", "ELF*", "UNELF*", + "FLX*", "URFLX*", "DLX*", "URDLX*", + "ILQ*", "ELQ*", + "FLQ*", "UNFLQ*", "DLQ*", "UNDLQ*", + "LJE*", "SJE*", + "IAD*", "EAD*", + "FAD*", "URFAD*", "UNFAD*", "UUFAD*", + "DAD*", "URDAD*", "UNDAD*", "UUDAD*", + + "FLA", "UNFLA", "FXA", "URFXA", /* no operand */ + "SWQ", "UNSWQ", "FZR", + "FAB", "FNG", "FCM", "FNM", +#endif +#if defined (PDP9) || defined (PDP15) + "MPSK", "MPSNE", "MPCV", "MPEU", + "MPLD", "MPCNE", "PFSF", + "TTS", "CAF", "DBK", "DBR", + "SPI", "RPL", "ISA", +#endif + "IOT", /* general */ + + "NOP", "STL", "RCL", "RCR", + "CLC", "LAS", "GLK", + "OPR", "SMA", "SZA", "SZA SMA", + "SNL", "SNL SMA", "SNL SZA", "SNL SZA SMA", + "SKP", "SPA", "SNA", "SNA SPA", + "SZL", "SZL SPA", "SZL SNA", "SZL SZA SPA", + "RAL", "SMA RAL", "SZA RAL", "SZA SMA RAL", + "SNL RAL", "SNL SMA RAL", "SNL SZA RAL", "SNL SZA SMA RAL", + "SKP RAL", "SPA RAL", "SNA RAL", "SNA SPA RAL", + "SZL RAL", "SZL SPA RAL", "SZL SNA RAL", "SZL SZA SPA RAL", + "RAR", "SMA RAR", "SZA RAR", "SZA SMA RAR", + "SNL RAR", "SNL SMA RAR", "SNL SZA RAR", "SNL SZA SMA RAR", + "SKP RAR", "SPA RAR", "SNA RAR", "SNA SPA RAR", + "SZL RAR", "SZL SPA RAR", "SZL SNA RAR", "SZL SZA SPA RAR", +#if defined (PDP15) + "IAC", "SMA IAC", "SZA IAC", "SZA SMA IAC", + "SNL IAC", "SNL SMA IAC", "SNL SZA IAC", "SNL SZA SMA IAC", + "SKP IAC", "SPA IAC", "SNA IAC", "SNA SPA IAC", + "SZL IAC", "SZL SPA IAC", "SZL SNA IAC", "SZL SZA SPA IAC", +#else + "RAL RAR", "SMA RAL RAR", "SZA RAL RAR", "SZA SMA RAL RAR", + "SNL RAL RAR", "SNL SMA RAL RAR", "SNL SZA RAL RAR", "SNL SZA SMA RAL RAR", + "SKP RAL RAR", "SPA RAL RAR", "SNA RAL RAR", "SNA SPA RAL RAR", + "SZL RAL RAR", "SZL SPA RAL RAR", "SZL SNA RAL RAR", "SZL SZA SPA RAL RAR", +#endif + "RTWO", "SMA RTWO", "SZA RTWO", "SZA SMA RTWO", + "SNL RTWO", "SNL SMA RTWO", "SNL SZA RTWO", "SNL SZA SMA RTWO", + "SKP RTWO", "SPA RTWO", "SNA RTWO", "SNA SPA RTWO", + "SZL RTWO", "SZL SPA RTWO", "SZL SNA RTWO", "SZL SZA SPA RTWO", + "RTL", "SMA RTL", "SZA RTL", "SZA SMA RTL", + "SNL RTL", "SNL SMA RTL", "SNL SZA RTL", "SNL SZA SMA RTL", + "SKP RTL", "SPA RTL", "SNA RTL", "SNA SPA RTL", + "SZL RTL", "SZL SPA RTL", "SZL SNA RTL", "SZL SZA SPA RTL", + "RTR", "SMA RTR", "SZA RTR", "SZA SMA RTR", + "SNL RTR", "SNL SMA RTR", "SNL SZA RTR", "SNL SZA SMA RTR", + "SKP RTR", "SPA RTR", "SNA RTR", "SNA SPA RTR", + "SZL RTR", "SZL SPA RTR", "SZL SNA RTR", "SZL SZA SPA RTR", +#if defined (PDP15) + "BSW", "SMA BSW", "SZA BSW", "SZA SMA BSW", + "SNL BSW", "SNL SMA BSW", "SNL SZA BSW", "SNL SZA SMA BSW", + "SKP BSW", "SPA BSW", "SNA BSW", "SNA SPA BSW", + "SZL BSW", "SZL SPA BSW", "SZL SNA BSW", "SZL SZA SPA BSW", +#else + "RTL RTR", "SMA RTL RTR", "SZA RTL RTR", "SZA SMA RTL RTR", + "SNL RTL RTR", "SNL SMA RTL RTR", "SNL SZA RTL RTR", "SNL SZA SMA RTL RTR", + "SKP RTL RTR", "SPA RTL RTR", "SNA RTL RTR", "SNA SPA RTL RTR", + "SZL RTL RTR", "SZL SPA RTL RTR", "SZL SNA RTL RTR", "SZL SZA SPA RTL RTR", +#endif + + "LLK", "CLQ", "LSN", "OACQ", "ECLA", /* encode only masks */ + "CMQ", "OMQ", "OSC", + "CLA", "CLL", "CML", "CMA", + "OAS", "HLT", + NULL + }; + +static const int32 opc_val[] = { + 0000000+I_MRF, 0040000+I_MRF, 0100000+I_MRF, 0140000+I_MRF, + 0200000+I_MRF, 0240000+I_MRF, 0300000+I_MRF, 0340000+I_MRF, + 0400000+I_MRF, 0440000+I_MRF, 0500000+I_MRF, 0540000+I_MRF, + 0600000+I_MRF, + 0020000+I_MRF, 0060000+I_MRF, 0120000+I_MRF, 0160000+I_MRF, + 0220000+I_MRF, 0260000+I_MRF, 0320000+I_MRF, 0360000+I_MRF, + 0420000+I_MRF, 0460000+I_MRF, 0520000+I_MRF, 0560000+I_MRF, + 0620000+I_MRF, + + 0760000+I_LAW, + + 0641002+I_NPN, 0641001+I_NPN, 0644000+I_NPN, 0664000+I_NPN, 0652000+I_NPN, + 0653100+MD(022), 0657100+MD(022), 0640300+MD(023), 0644300+MD(023), + 0653300+MD(023), 0657300+MD(023), 0650300+MD(023), 0654300+MD(023), + 0640400+MD(044), 0660400+MD(044), + 0640100+I_ESH, 0660100+I_ESH, 0640300+I_ESH, 0660300+I_ESH, + 0640400+I_ESH, 0660400+I_ESH, 0640500+I_ESH, 0660500+I_ESH, + 0640600+I_ESH, 0660600+I_ESH, 0640700+I_ESH, 0660700+I_ESH, + 0640000+I_EST, 0640000+I_IOT, + + 0700001+I_NPI, 0700002+I_NPI, 0700042+I_NPI, 0700004+I_NPI, 0700044+I_NPI, + 0700101+I_NPI, 0700112+I_NPN, 0700102+I_NPI, 0700104+I_NPI, 0700144+I_NPI, + 0700201+I_NPI, 0700202+I_NPI, 0700204+I_NPI, 0700244+I_NPI, 0700206+I_NPI, + 0700301+I_NPI, 0700312+I_NPN, 0700302+I_NPI, 0700314+I_NPN, 0700304+I_NPI, + 0700401+I_NPI, 0700402+I_NPI, 0700404+I_NPI, 0700406+I_NPI, +#if defined (TYPE62) + 0706501+I_NPI, 0706502+I_NPI, 0706542+I_NPI, 0706506+I_NPI, + 0706601+I_NPI, 0706602+I_NPI, 0706606+I_NPI, +#endif +#if defined (TYPE647) + 0706501+I_NPI, 0706502+I_NPI, 0706522+I_NPI, 0706542+I_NPI, 0706562+I_NPI, + 0706526+I_NPI, 0706546+I_NPI, 0706566+I_NPI, + 0706601+I_NPI, 0706602+I_NPI, 0706622+I_NPI, 0706642+I_NPI, 0706662+I_NPI, + 0706606+I_NPI, 0706626+I_NPI, 0706646+I_NPI, +#endif +#if defined (LP09) + 0706601+I_NPI, 0706621+I_NPI, 0706602+I_NPI, 0706622+I_NPI, + 0706604+I_NPI, 0706644+I_NPI, +#endif +#if defined (LP15) + 0706501+I_NPI, 0706521+I_NPI, 0706541+I_NPI, 0706561+I_NPI, + 0706552+I_NPN, 0706542+I_NPI, 0706544+I_NPI, 0706621+I_NPI, 0706641+I_NPI, +#endif +#if defined (DRM) + 0706006+I_NPI, 0706046+I_NPI, 0706106+I_NPI, 0706204+I_NPI, + 0706101+I_NPI, 0706201+I_NPI, 0706102+I_NPI, + 0706006+I_NPI, 0706046+I_NPI, 0706106+I_NPI, 0706204+I_NPI, + 0706101+I_NPI, 0706201+I_NPI, 0706102+I_NPI, +#endif +#if defined (RB) + 0707101+I_NPI, 0707112+I_NPN, 0707104+I_NPI, + 0707121+I_NPI, 0707132+I_NPN, 0707124+I_NPI, + 0707141+I_NPI, 0707142+I_NPI, 0707144+I_NPI, +#endif +#if defined (RF) + 0707001+I_NPI, 0707021+I_NPI, 0707041+I_NPI, + 0707002+I_NPI, 0707022+I_NPI, 0707042+I_NPI, 0707062+I_NPI, + 0707004+I_NPI, 0707024+I_NPI, 0707044+I_NPI, 0707064+I_NPI, + 0707202+I_NPI, 0707242+I_NPI, 0707262+I_NPI, + 0707204+I_NPI, 0707224+I_NPI, +#endif +#if defined (RP) + 0706301+I_NPI, 0706321+I_NPI, 0706341+I_NPI, 0706361+I_NPI, + 0706312+I_NPN, 0706302+I_NPI, 0706332+I_NPN, 0706322+I_NPI, + 0706352+I_NPN, 0706342+I_NPI, + 0706304+I_NPI, 0706324+I_NPI, 0706344+I_NPI, 0706364+I_NPI, + 0706411+I_NPN, 0706401+I_NPI, 0706421+I_NPI, + 0706412+I_NPN, 0706402+I_NPI, 0706432+I_NPN, 0706422+I_NPI, + 0706452+I_NPN, 0706442+I_NPI, 0706472+I_NPN, 0706462+I_NPI, + 0706404+I_NPI, 0706424+I_NPI, 0706454+I_NPN, 0706444+I_NPI, 0706464+I_NPI, +#endif +#if defined (MTA) + 0707301+I_NPI, 0707321+I_NPI, 0707341+I_NPI, 0707312+I_NPN, 0707322+I_NPI, + 0707352+I_NPN, 0707304+I_NPI, 0707324+I_NPI, 0707326+I_NPI, +#endif +#if defined (TYPE550) /* Type 550 */ + 0707501+I_NPI, 0707541+I_NPI, 0707512+I_NPN, 0707504+I_NPI, + 0707601+I_NPI, 0707612+I_NPN, 0707604+I_NPI, 0707644+I_NPI, +#elif defined (TC02) /* TC02/TC15 */ + 0707541+I_NPI, 0707552+I_NPN, 0707544+I_NPI, 0707545+I_NPI, + 0707561+I_NPI, 0707572+I_NPN, 0707601+I_NPI, +#endif +#if defined (TTY1) + 0704101+I_NPI, 0704112+I_NPN, + 0704001+I_NPI, 0704002+I_NPI, 0704004+I_NPI, 0704006+I_NPI, +#endif +#if defined (PDP7) + 0703201+I_NPI, 0703301+I_NPI, 0703341+I_NPI, 0703302+I_NPI, + 0707701+I_NPI, 0707702+I_NPI, 0707742+I_NPI, 0707704+I_NPI, +#endif +#if defined (PDP9) + 0703341+I_NPI, 0707701+I_NPI, 0707702+I_NPI, 0707704+I_NPI, + 0706504+I_NPI, 0706604+I_NPI, +#endif +#if defined (PDP15) + 0703341+I_NPI, 0707741+I_NPI, 0707742+I_NPI, + 0707761+I_NPI, 0707762+I_NPI, 0707764+I_NPI, + 0700032+I_NPN, 0700022+I_NPI, 0700024+I_NPI, 0701724+I_NPI, + 0705521+I_NPI, 0705522+I_NPI, + 0701772+I_NPN, 0701762+I_NPI, 0701764+I_NPI, + 0721000+I_XR, 0722000+I_XR, 0723000+I_XR9, 0724000+I_XR, + 0725000+I_XR9, 0726000+I_XR, 0730000+I_XR, 0731000+I_XR, + 0734000+I_XR, 0735000+I_XR, 0736000+I_XR, 0737000+I_XR9, + + 0710314+I_FPN, + 0710400+I_FPM, 0710500+I_FPM, + 0710440+I_FPM, 0710450+I_FPM, 0710460+I_FPM, 0710470+I_FPM, + 0710540+I_FPM, 0710550+I_FPM, 0710560+I_FPM, 0710570+I_FPM, + 0711000+I_FPM, 0711100+I_FPM, + 0711040+I_FPM, 0711050+I_FPM, 0711060+I_FPM, 0711070+I_FPM, + 0711140+I_FPM, 0711150+I_FPM, 0711160+I_FPM, 0711170+I_FPM, + 0711400+I_FPM, 0711500+I_FPM, + 0711440+I_FPM, 0711450+I_FPM, 0711460+I_FPM, 0711470+I_FPM, + 0711540+I_FPM, 0711550+I_FPM, 0711560+I_FPM, 0711570+I_FPM, + 0712000+I_FPM, 0712100+I_FPM, + 0712040+I_FPM, 0712050+I_FPM, 0712060+I_FPM, 0712070+I_FPM, + 0712140+I_FPM, 0712150+I_FPM, 0712160+I_FPM, 0712170+I_FPM, + 0712400+I_FPM, 0712500+I_FPM, + 0712440+I_FPM, 0712450+I_FPM, 0712460+I_FPM, 0712470+I_FPM, + 0712540+I_FPM, 0712550+I_FPM, 0712560+I_FPM, 0712570+I_FPM, + 0713000+I_FPM, 0713100+I_FPM, + 0713050+I_FPM, 0713070+I_FPM, 0713150+I_FPM, 0713170+I_FPM, + 0713600+I_FPM, 0713700+I_FPM, + 0713640+I_FPM, 0713650+I_FPM, 0713660+I_FPM, 0713670+I_FPM, + 0713750+I_FPM, 0713770+I_FPM, + 0714010+I_FPM, 0714030+I_FPM, 0714110+I_FPM, 0714130+I_FPM, + 0714460+I_FPM, 0714470+I_FPM, 0714560+I_FPM, 0714570+I_FPM, + 0715000+I_FPM, 0715100+I_FPM, + 0715050+I_FPM, 0715070+I_FPM, 0715150+I_FPM, 0715170+I_FPM, + 0715400+I_FPM, 0715600+I_FPM, + 0716000+I_FPM, 0716100+I_FPM, + 0716040+I_FPM, 0716050+I_FPM, 0716060+I_FPM, 0716070+I_FPM, + 0716140+I_FPM, 0716150+I_FPM, 0716160+I_FPM, 0716170+I_FPM, + 0716601+I_FPM, 0716602+I_FPM, 0716603+I_FPM, + 0716604+I_FPM, 0716606+I_FPM, 0716610+I_FPM, 0716620+I_FPM, + 0710400+I_FPI, 0710500+I_FPI, /* indirect */ + 0710440+I_FPI, 0710450+I_FPI, 0710460+I_FPI, 0710470+I_FPI, + 0710540+I_FPI, 0710550+I_FPI, 0710560+I_FPI, 0710570+I_FPI, + 0711000+I_FPI, 0711100+I_FPI, + 0711040+I_FPI, 0711050+I_FPI, 0711060+I_FPI, 0711070+I_FPI, + 0711140+I_FPI, 0711150+I_FPI, 0711160+I_FPI, 0711170+I_FPI, + 0711400+I_FPI, 0711500+I_FPI, + 0711440+I_FPI, 0711450+I_FPI, 0711460+I_FPI, 0711470+I_FPI, + 0711540+I_FPI, 0711550+I_FPI, 0711560+I_FPI, 0711570+I_FPI, + 0712000+I_FPI, 0712100+I_FPI, + 0712040+I_FPI, 0712050+I_FPI, 0712060+I_FPI, 0712070+I_FPI, + 0712140+I_FPI, 0712150+I_FPI, 0712160+I_FPI, 0712170+I_FPI, + 0712400+I_FPI, 0712500+I_FPI, + 0712440+I_FPI, 0712450+I_FPI, 0712460+I_FPI, 0712470+I_FPI, + 0712540+I_FPI, 0712550+I_FPI, 0712560+I_FPI, 0712570+I_FPI, + 0713000+I_FPI, 0713100+I_FPI, + 0713050+I_FPI, 0713070+I_FPI, 0713150+I_FPI, 0713170+I_FPI, + 0713600+I_FPI, 0713700+I_FPI, + 0713640+I_FPI, 0713650+I_FPI, 0713660+I_FPI, 0713670+I_FPI, + 0713750+I_FPI, 0713770+I_FPI, + 0714010+I_FPI, 0714030+I_FPI, 0714110+I_FPI, 0714130+I_FPI, + 0714460+I_FPI, 0714470+I_FPI, 0714560+I_FPI, 0714570+I_FPI, + 0715000+I_FPI, 0715100+I_FPI, + 0715050+I_FPI, 0715070+I_FPI, 0715150+I_FPI, 0715170+I_FPI, + 0715400+I_FPI, 0715600+I_FPI, + 0716000+I_FPI, 0716100+I_FPI, + 0716040+I_FPI, 0716050+I_FPI, 0716060+I_FPI, 0716070+I_FPI, + 0716140+I_FPI, 0716150+I_FPI, 0716160+I_FPI, 0716170+I_FPI, + 0714210+I_FPN, 0714230+I_FPN, 0714660+I_FPN, 0714670+I_FPN, + 0715250+I_FPN, 0715270+I_FPN, 0711200+I_FPN, + 0713271+I_FPN, 0713272+I_FPN, 0713273+I_FPN, 0713250+I_FPN, +#endif +#if defined (PDP9) || defined (PDP15) + 0701701+I_NPI, 0701741+I_NPI, 0701702+I_NPI, 0701742+I_NPI, + 0701704+I_NPI, 0701744+I_NPI, 0703201+I_NPI, + 0703301+I_NPI, 0703302+I_NPI, 0703304+I_NPI, 0703344+I_NPI, + 0705501+I_NPI, 0705512+I_NPN, 0705504+I_NPI, +#endif + 0700000+I_IOT, + + 0740000+I_NPN, 0744002+I_NPN, 0744010+I_NPN, 0744020+I_NPN, + 0750001+I_NPN, 0750004+I_NPN, 0750010+I_NPN, + 0740000+I_OPR, 0740100+I_OPR, 0740200+I_OPR, 0740300+I_OPR, + 0740400+I_OPR, 0740500+I_OPR, 0740600+I_OPR, 0740700+I_OPR, + 0741000+I_OPR, 0741100+I_OPR, 0741200+I_OPR, 0741300+I_OPR, + 0741400+I_OPR, 0741500+I_OPR, 0741600+I_OPR, 0741700+I_OPR, + 0740010+I_OPR, 0740110+I_OPR, 0740210+I_OPR, 0740310+I_OPR, + 0740410+I_OPR, 0740510+I_OPR, 0740610+I_OPR, 0740710+I_OPR, + 0741010+I_OPR, 0741110+I_OPR, 0741210+I_OPR, 0741310+I_OPR, + 0741410+I_OPR, 0741510+I_OPR, 0741610+I_OPR, 0741710+I_OPR, + 0740020+I_OPR, 0740120+I_OPR, 0740220+I_OPR, 0740320+I_OPR, + 0740420+I_OPR, 0740520+I_OPR, 0740620+I_OPR, 0740720+I_OPR, + 0741020+I_OPR, 0741120+I_OPR, 0741220+I_OPR, 0741320+I_OPR, + 0741420+I_OPR, 0741520+I_OPR, 0741620+I_OPR, 0741720+I_OPR, + 0740030+I_OPR, 0740130+I_OPR, 0740230+I_OPR, 0740330+I_OPR, + 0740430+I_OPR, 0740530+I_OPR, 0740630+I_OPR, 0740730+I_OPR, + 0741030+I_OPR, 0741130+I_OPR, 0741230+I_OPR, 0741330+I_OPR, + 0741430+I_OPR, 0741530+I_OPR, 0741630+I_OPR, 0741730+I_OPR, + 0742000+I_OPR, 0742100+I_OPR, 0742200+I_OPR, 0742300+I_OPR, + 0742400+I_OPR, 0742500+I_OPR, 0742600+I_OPR, 0742700+I_OPR, + 0743000+I_OPR, 0743100+I_OPR, 0743200+I_OPR, 0743300+I_OPR, + 0743400+I_OPR, 0743500+I_OPR, 0743600+I_OPR, 0743700+I_OPR, + 0742010+I_OPR, 0742110+I_OPR, 0742210+I_OPR, 0742310+I_OPR, + 0742410+I_OPR, 0742510+I_OPR, 0742610+I_OPR, 0742710+I_OPR, + 0743010+I_OPR, 0743110+I_OPR, 0743210+I_OPR, 0743310+I_OPR, + 0743410+I_OPR, 0743510+I_OPR, 0743610+I_OPR, 0743710+I_OPR, + 0742020+I_OPR, 0742120+I_OPR, 0742220+I_OPR, 0742320+I_OPR, + 0742420+I_OPR, 0742520+I_OPR, 0742620+I_OPR, 0742720+I_OPR, + 0743020+I_OPR, 0743120+I_OPR, 0743220+I_OPR, 0743320+I_OPR, + 0743420+I_OPR, 0743520+I_OPR, 0743620+I_OPR, 0743720+I_OPR, + 0742030+I_OPR, 0742130+I_OPR, 0742230+I_OPR, 0742330+I_OPR, + 0742430+I_OPR, 0742530+I_OPR, 0742630+I_OPR, 0742730+I_OPR, + 0743030+I_OPR, 0743130+I_OPR, 0743230+I_OPR, 0743330+I_OPR, + 0743430+I_OPR, 0743530+I_OPR, 0743630+I_OPR, 0743730+I_OPR, + + 0660000+I_EST, 0650000+I_EST, 0644000+I_EST, 0642000+I_EST, 0641000+I_EST, + 0640004+I_EST, 0640002+I_EST, 0640001+I_EST, + 0750000+I_OPR, 0744000+I_OPR, 0740002+I_OPR, 0740001+I_OPR, + 0740004+I_OPR, 0740040+I_OPR, + -1 + }; + +/* Operate or EAE decode + + Inputs: + *of = output stream + inst = mask bits + class = instruction class code + sp = space needed? + Outputs: + status = space needed? +*/ + +int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) +{ +int32 i, j; + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; + } + } +return sp; +} + +static int32 rar (int32 c) +{ +c = c & 077; +return (c >> 1) | (c << 5); +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) (((x) < 040)? "<%03o>": "%c"), (x) +#define SIXTOASC(x) (((x) >= 040)? (x): ((x) + 0100)) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, k, sp, inst, disp, ma; + +inst = val[0]; +cflag = (uptr == NULL) || (uptr == &cpu_unit); +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, "%c", SIXTOASC ((inst >> 12) & 077)); + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; + } +#if defined (PDP4) || defined (PDP7) +if (sw & SWMASK ('F')) { /* FIODEC? */ + fprintf (of, "%c", fio_to_asc[(inst >> 12) & 077]); + fprintf (of, "%c", fio_to_asc[(inst >> 6) & 077]); + fprintf (of, "%c", fio_to_asc[inst & 077]); + return SCPE_OK; + } +if (sw & SWMASK ('B')) { /* Baudot? */ + fprintf (of, "%c", baud_to_asc[rar (inst >> 12) & 077]); + fprintf (of, "%c", baud_to_asc[rar (inst >> 6) & 077]); + fprintf (of, "%c", baud_to_asc[rar (inst) & 077]); + return SCPE_OK; + } +#endif +#if defined (PDP15) +if (sw & SWMASK ('P')) { /* packed ASCII? */ + i = val[1]; + fprintf (of, FMTASC ((inst >> 11) & 0177)); + fprintf (of, FMTASC ((inst >> 4) & 0177)); + fprintf (of, FMTASC (((inst << 3) | (i >> 15)) & 0177)); + fprintf (of, FMTASC ((i >> 8) & 0177)); + fprintf (of, FMTASC ((i >> 1) & 0177)); + return -1; + } +#endif +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + + case I_V_NPN: /* no operands */ + case I_V_XR: /* index no opers */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_NPI: /* IOT no operand */ + fprintf (of, "%s", opcode[i]); /* opcode */ + if (inst & 010) fprintf (of, " +10"); + break; + + case I_V_IOT: /* IOT or EAE */ + fprintf (of, "%s %-o", opcode[i], inst & 037777); + break; + + case I_V_MRF: /* mem ref */ +#if defined (PDP15) + if (memm) { + disp = inst & B_DAMASK; + ma = (addr & (AMASK & ~B_DAMASK)) | disp; + } + else { + disp = inst & P_DAMASK; + ma = (addr & (AMASK & ~P_DAMASK)) | disp; + } + fprintf (of, "%s %-o", opcode[i], (cflag? ma & AMASK: disp)); + if (!memm && (inst & I_IDX)) fprintf (of, ",X"); +#else + disp = inst & B_DAMASK; + ma = (addr & (AMASK & ~B_DAMASK)) | disp; + fprintf (of, "%s %-o", opcode[i], (cflag? ma & AMASK: disp)); +#endif + break; + + case I_V_OPR: /* operate */ + if (sp = (inst & 03730)) fprintf (of, "%s", opcode[i]); + fprint_opr (of, inst & 014047, I_V_OPR, sp); + break; + + case I_V_LAW: /* LAW */ + fprintf (of, "%s %-o", opcode[i], inst & 017777); + break; + + case I_V_XR9: /* index with lit */ + disp = inst & 0777; + if (disp & 0400) fprintf (of, "%s -%-o", opcode[i], 01000 - disp); + else fprintf (of, "%s %-o", opcode[i], disp); + break; + + case I_V_EST: /* EAE setup */ + fprint_opr (of, inst & 037007, I_V_EST, 0); + break; + + case I_V_ESH: /* EAE shift */ + sp = fprint_opr (of, inst & 017000, I_V_EST, 0); + fprintf (of, (sp? " %s %-o": "%s %-o"), opcode[i], inst & 077); + break; + + case I_V_EMD: /* EAE mul-div */ + disp = inst & 077; /* get actual val */ + k = (opc_val[i] >> I_V_DC) & 077; /* get default val */ + if (disp == k) fprintf (of, "%s", opcode[i]); + else if (disp < k) fprintf (of, "%s -%-o", opcode[i], k - disp); + else fprintf (of, "%s +%-o", opcode[i], disp - k); + break; + + case I_V_FPM: case I_V_FPI: /* FP15 mem ref */ + fprintf (of, "%s", opcode[i]); + if (val[1] & SIGN) fputc ('*', of); + fprintf (of, " %-o", val[1] & ~SIGN); + return -1; + + case I_V_FPN: /* FP15 no operand */ + fprintf (of, "%s", opcode[i]); + return -1; + } /* end case */ + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get 18b signed number + + Inputs: + *cptr = pointer to input string + *sign = pointer to sign + *status = pointer to error status + Outputs: + val = output value +*/ + +t_value get_sint (char *cptr, int32 *sign, t_stat *status) +{ +*sign = 0; +if (*cptr == '+') { + *sign = 1; + cptr++; + } +else if (*cptr == '-') { + *sign = -1; + cptr++; + } +return get_uint (cptr, 8, 0777777, status); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 cflag, d, i, j, k, sign, damask, epcmask; +t_stat r, sta = SCPE_OK; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; +for (i = 1; (i < 5) && (cptr[i] != 0); i++) + if (cptr[i] == 0) for (j = i + 1; j <= 5; j++) cptr[j] = 0; +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0] | 0200; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 077) << 12) | + (((t_value) cptr[1] & 077) << 6) | + ((t_value) cptr[2] & 077); + return SCPE_OK; + } +#if defined (PDP15) +if ((sw & SWMASK ('P')) || ((*cptr == '#') && cptr++)) { /* packed string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 0177) << 11) | + (((t_value) cptr[1] & 0177) << 4) | + (((t_value) cptr[2] & 0170) >> 3); + val[1] = (((t_value) cptr[2] & 0007) << 15) | + (((t_value) cptr[3] & 0177) << 8) | + (((t_value) cptr[4] & 0177) << 1); + return -1; + } +#endif + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & DMASK; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_XR: /* index */ + break; + + case I_V_XR9: /* index literal */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + d = get_sint (gbuf, &sign, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (((sign >= 0) && (d > 0377)) || ((sign < 0) && (d > 0400))) + return SCPE_ARG; + val[0] = val[0] | ((sign >= 0)? d: (01000 - d)); + break; + + case I_V_LAW: /* law */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + d = get_uint (gbuf, 8, 017777, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; + break; + + case I_V_MRF: /* mem ref */ +#if defined (PDP15) + if (memm) damask = B_DAMASK; + else damask = P_DAMASK; + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ +#else + damask = B_DAMASK; + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ +#endif +#if defined (PDP4) || defined (PDP7) + if (strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | I_IND; + cptr = get_glyph (cptr, gbuf, 0); + } +#endif + epcmask = AMASK & ~damask; /* get ePC */ + d = get_uint (gbuf, 8, AMASK, &r); /* get addr */ + if (r != SCPE_OK) return SCPE_ARG; + if (d <= damask) val[0] = val[0] | d; /* fit in 12/13b? */ + else if (cflag && (((addr ^ d) & epcmask) == 0)) + val[0] = val[0] | (d & damask); /* hi bits = ePC? */ + else return SCPE_ARG; +#if defined (PDP15) + if (!memm) { + cptr = get_glyph (cptr, gbuf, 0); + if (gbuf[0] != 0) { + if (strcmp (gbuf, "X") != 0) return SCPE_ARG; + val[0] = val[0] | I_IDX; + } + } +#endif + break; + + case I_V_EMD: /* or'able */ + val[0] = val[0] | ((opc_val[i] >> I_V_DC) & 077); /* default shift */ + case I_V_EST: case I_V_ESH: + case I_V_NPN: case I_V_NPI: + case I_V_IOT: case I_V_OPR: + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if (opcode[i] != NULL) { + k = opc_val[i] & DMASK; + if (((k ^ val[0]) & 0740000) != 0) return SCPE_ARG; + val[0] = val[0] | k; + } + else { + d = get_sint (gbuf, & sign, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (sign > 0) val[0] = val[0] + d; + else if (sign < 0) val[0] = val[0] - d; + else val[0] = val[0] | d; + } + } + break; + + case I_V_FPM: /* FP15 mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + val[1] = get_uint (gbuf, 8, AMASK, &r); /* get addr */ + if (r != SCPE_OK) return SCPE_ARG; + sta = -1; + break; + + case I_V_FPI: /* FP15 ind mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + val[1] = get_uint (gbuf, 8, AMASK, &r) | SIGN; /* get @addr */ + if (r != SCPE_OK) return SCPE_ARG; + sta = -1; + break; + + case I_V_FPN: /* FP15 no operand */ + val[1] = 0; + sta = -1; + break; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return sta; +} diff --git a/PDP18B/pdp18b_tt1.c b/PDP18B/pdp18b_tt1.c new file mode 100644 index 0000000..a44ae15 --- /dev/null +++ b/PDP18B/pdp18b_tt1.c @@ -0,0 +1,463 @@ +/* pdp15_ttx.c: PDP-15 additional terminals simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ttix,ttox LT15/LT19 terminal input/output + + 18-Jun-07 RMS Added UNIT_IDLE flag + 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode + 22-Nov-05 RMS Revised for new terminal processing routines + 29-Jun-05 RMS Added SET TTOXn DISCONNECT + 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS + 14-Jan-04 RMS Cloned from pdp8_ttx.c + + This module implements 16 individual serial interfaces similar in function + to the console. These interfaces are mapped to Telnet based connections as + though they were the four lines of a terminal multiplexor. The connection + polling mechanism is superimposed onto the keyboard of the first interface. +*/ + +#include "pdp18b_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#if defined (PDP15) +#define TTX_MAXL 16 /* max number of lines */ +#elif defined (PDP9) +#define TTX_MAXL 4 +#else +#define TTX_MAXL 1 +#endif + +uint32 ttix_done = 0; /* input flags */ +uint32 ttox_done = 0; /* output flags */ +uint8 ttix_buf[TTX_MAXL] = { 0 }; /* input buffers */ +uint8 ttox_buf[TTX_MAXL] = { 0 }; /* output buffers */ +TMLN ttx_ldsc[TTX_MAXL] = { 0 }; /* line descriptors */ +TMXR ttx_desc = { 1, 0, 0, ttx_ldsc }; /* mux descriptor */ +#define ttx_lines ttx_desc.lines /* current number of lines */ + +extern int32 int_hwre[API_HLVL+1]; +extern int32 tmxr_poll; +extern int32 stop_inst; + +DEVICE ttix_dev, ttox_dev; +int32 ttix (int32 dev, int32 pulse, int32 dat); +int32 ttox (int32 dev, int32 pulse, int32 dat); +t_stat ttix_svc (UNIT *uptr); +t_bool ttix_test_done (int32 ln); +void ttix_set_done (int32 ln); +void ttix_clr_done (int32 ln); +t_stat ttox_svc (UNIT *uptr); +t_bool ttox_test_done (int32 ln); +void ttox_set_done (int32 ln); +void ttox_clr_done (int32 ln); +int32 ttx_getln (int32 dev, int32 pulse); +t_stat ttx_attach (UNIT *uptr, char *cptr); +t_stat ttx_detach (UNIT *uptr); +t_stat ttx_reset (DEVICE *dptr); +void ttx_reset_ln (int32 i); +t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat ttx_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* TTIx data structures + + ttix_dev TTIx device descriptor + ttix_unit TTIx unit descriptor + ttix_reg TTIx register list + ttix_mod TTIx modifiers list +*/ + +DIB ttix_dib = { + DEV_TTO1, 8, NULL, + { &ttox, &ttix, &ttox, &ttix, &ttox, &ttix, &ttox, &ttix } + }; + +UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_IDLE|UNIT_ATTABLE, 0), KBD_POLL_WAIT }; + +REG ttx_nlreg = { DRDATA (NLINES, ttx_lines, 4), PV_LEFT }; + +REG ttix_reg[] = { + { BRDATA (BUF, ttix_buf, 8, 8, TTX_MAXL) }, + { ORDATA (DONE, ttix_done, TTX_MAXL) }, + { FLDATA (INT, int_hwre[API_TTI1], INT_V_TTI1) }, + { DRDATA (TIME, ttix_unit.wait, 24), REG_NZ + PV_LEFT }, + { ORDATA (DEVNUM, ttix_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB ttix_mod[] = { + { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES", + &ttx_vlines, NULL, &ttx_nlreg }, + { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &ttx_summ }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &ttx_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &ttx_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &ttx_show, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_devno, &show_devno, NULL }, + { 0 } + }; + +DEVICE tti1_dev = { + "TTIX", &ttix_unit, ttix_reg, ttix_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &ttx_reset, + NULL, &ttx_attach, &ttx_detach, + &ttix_dib, DEV_NET | DEV_DISABLE + }; + +/* TTOx data structures + + ttox_dev TTOx device descriptor + ttox_unit TTOx unit descriptor + ttox_reg TTOx register list +*/ + +UNIT ttox_unit[] = { + { UDATA (&ttox_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT } + }; + +REG ttox_reg[] = { + { BRDATA (BUF, ttox_buf, 8, 8, TTX_MAXL) }, + { ORDATA (DONE, ttox_done, TTX_MAXL) }, + { FLDATA (INT, int_hwre[API_TTO1], INT_V_TTO1) }, + { URDATA (TIME, ttox_unit[0].wait, 10, 24, 0, + TTX_MAXL, PV_LEFT) }, + { NULL } + }; + +MTAB ttox_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &ttx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &ttx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &ttx_desc }, + { 0 } + }; + +DEVICE tto1_dev = { + "TTOX", ttox_unit, ttox_reg, ttox_mod, + TTX_MAXL, 10, 31, 1, 8, 8, + NULL, NULL, &ttx_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE + }; + +/* Terminal input: IOT routine */ + +int32 ttix (int32 dev, int32 pulse, int32 dat) +{ +int32 ln = ttx_getln (dev, pulse); /* line # */ + +if (ln > ttx_lines) return dat; +if (pulse & 001) { /* KSF1 */ + if (ttix_test_done (ln)) dat = dat | IOT_SKP; + } +if (pulse & 002) { /* KRB1 */ + ttix_clr_done (ln); /* clear flag */ + dat = dat | ttix_buf[ln]; /* return buffer */ + } +return dat; +} + +/* Unit service */ + +t_stat ttix_svc (UNIT *uptr) +{ +int32 ln, c, temp; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +sim_activate (uptr, tmxr_poll); /* continue poll */ +ln = tmxr_poll_conn (&ttx_desc); /* look for connect */ +if (ln >= 0) ttx_ldsc[ln].rcve = 1; /* got one? rcv enab */ +tmxr_poll_rx (&ttx_desc); /* poll for input */ +for (ln = 0; ln < TTX_MAXL; ln++) { /* loop thru lines */ + if (ttx_ldsc[ln].conn) { /* connected? */ + if (temp = tmxr_getc_ln (&ttx_ldsc[ln])) { /* get char */ + if (temp & SCPE_BREAK) c = 0; /* break? */ + else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags) | TTUF_KSR); + ttix_buf[ln] = c; + ttix_set_done (ln); + } + } + } +return SCPE_OK; +} + +/* Interrupt handling routines */ + +t_bool ttix_test_done (int32 ln) +{ +if (ttix_done & (1 << ln)) return TRUE; +return FALSE; +} + +void ttix_set_done (int32 ln) +{ +ttix_done = ttix_done | (1 << ln); +SET_INT (TTI1); +return; +} + +void ttix_clr_done (int32 ln) +{ +ttix_done = ttix_done & ~(1 << ln); +if (ttix_done) { SET_INT (TTI1); } +else { + CLR_INT (TTI1); + } +return; +} + +/* Terminal output: IOT routine */ + +int32 ttox (int32 dev, int32 pulse, int32 dat) +{ +int32 ln = ttx_getln (dev, pulse); /* line # */ + +if (ln > ttx_lines) return dat; +if (pulse & 001) { /* TSF */ + if (ttox_test_done (ln)) dat = dat | IOT_SKP; + } +if (pulse & 002) ttox_clr_done (ln); /* clear flag */ +if (pulse & 004) { /* load buffer */ + sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate unit */ + ttox_buf[ln] = dat & 0377; /* load buffer */ + } +return dat; +} + +/* Unit service */ + +t_stat ttox_svc (UNIT *uptr) +{ +int32 c, ln = uptr - ttox_unit; /* line # */ + +if (ttx_ldsc[ln].conn) { /* connected? */ + if (ttx_ldsc[ln].xmte) { /* tx enabled? */ + TMLN *lp = &ttx_ldsc[ln]; /* get line */ + c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags) | TTUF_KSR); + if (c >= 0) tmxr_putc_ln (lp, c); /* output char */ + tmxr_poll_tx (&ttx_desc); /* poll xmt */ + } + else { + tmxr_poll_tx (&ttx_desc); /* poll xmt */ + sim_activate (uptr, ttox_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } +ttox_set_done (ln); /* set done */ +return SCPE_OK; +} + +/* Interrupt handling routines */ + +t_bool ttox_test_done (int32 ln) +{ +if (ttox_done & (1 << ln)) return TRUE; +return FALSE; +} + +void ttox_set_done (int32 ln) +{ +ttox_done = ttox_done | (1 << ln); +SET_INT (TTO1); +return; +} + +void ttox_clr_done (int32 ln) +{ +ttox_done = ttox_done & ~(1 << ln); +if (ttox_done) { SET_INT (TTO1); } +else { CLR_INT (TTO1); } +return; +} + +/* Compute relative line number + + This algorithm does not assign contiguous line numbers of ascending + LT19's. Rather, line numbers follow a simple progression based on + the relative IOT number and the subdevice select */ + +int32 ttx_getln (int32 dev, int32 pulse) +{ +int32 rdno = ((dev - ttix_dib.dev) >> 1) & 3; + +#if defined (PDP15) /* PDP-15? */ +int32 sub = (pulse >> 4) & 3; +return (rdno * 4) + sub; /* use dev, subdev */ +#else /* others */ +return rdno; /* use dev only */ +#endif +} + +/* Reset routine */ + +t_stat ttx_reset (DEVICE *dptr) +{ +int32 ln; + +if (dptr->flags & DEV_DIS) { /* sync enables */ + ttix_dev.flags = ttox_dev.flags | DEV_DIS; + ttox_dev.flags = ttox_dev.flags | DEV_DIS; + } +else { + ttix_dev.flags = ttix_dev.flags & ~DEV_DIS; + ttox_dev.flags = ttox_dev.flags & ~DEV_DIS; + } +if (ttix_unit.flags & UNIT_ATT) /* if attached, */ + sim_activate (&ttix_unit, tmxr_poll); /* activate */ +else sim_cancel (&ttix_unit); /* else stop */ +for (ln = 0; ln < TTX_MAXL; ln++) ttx_reset_ln (ln); /* for all lines */ +return SCPE_OK; +} + +/* Reset line n */ + +void ttx_reset_ln (int32 ln) +{ +ttix_buf[ln] = 0; /* clear buf, */ +ttox_buf[ln] = 0; +ttix_clr_done (ln); /* clear done */ +ttox_clr_done (ln); +sim_cancel (&ttox_unit[ln]); /* stop poll */ +return; +} + +/* Attach master unit */ + +t_stat ttx_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +sim_activate (uptr, tmxr_poll); /* start poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat ttx_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&ttx_desc, uptr); /* detach */ +sim_cancel (uptr); /* stop poll */ +for (i = 0; i < TTX_MAXL; i++) ttx_ldsc[i].rcve = 0; /* disable rcv */ +return r; +} + +/* Show summary processor */ + +t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < TTX_MAXL; i++) t = t + (ttx_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < TTX_MAXL; i++) t = t + (ttx_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < ttx_lines; i++) { + if (ttx_ldsc[i].conn) { + if (val) tmxr_fconns (st, &ttx_ldsc[i], i); + else tmxr_fstats (st, &ttx_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Change number of lines */ + +t_stat ttx_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newln = get_uint (cptr, 10, TTX_MAXL, &r); +if ((r != SCPE_OK) || (newln == ttx_lines)) return r; +if (newln == 0) return SCPE_ARG; +if (newln < ttx_lines) { + for (i = newln, t = 0; i < ttx_lines; i++) t = t | ttx_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < ttx_lines; i++) { + if (ttx_ldsc[i].conn) { + tmxr_linemsg (&ttx_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&ttx_ldsc[i]); /* reset line */ + } + ttox_unit[i].flags = ttox_unit[i].flags | UNIT_DIS; + ttx_reset_ln (i); + } + } +else { + for (i = ttx_lines; i < newln; i++) { + ttox_unit[i].flags = ttox_unit[i].flags & ~UNIT_DIS; + ttx_reset_ln (i); + } + } +ttx_lines = newln; +return SCPE_OK; +} + + diff --git a/PDP8/pdp8_clk.c b/PDP8/pdp8_clk.c new file mode 100644 index 0000000..ab01388 --- /dev/null +++ b/PDP8/pdp8_clk.c @@ -0,0 +1,183 @@ +/* pdp8_clk.c: PDP-8 real-time clock simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + clk real time clock + + 18-Jun-07 RMS Added UNIT_IDLE flag + 01-Mar-03 RMS Aded SET/SHOW CLK FREQ support + 04-Oct-02 RMS Added DIB, device number support + 30-Dec-01 RMS Removed for generalized timers + 05-Sep-01 RMS Added terminal multiplexor support + 17-Jul-01 RMS Moved function prototype + 05-Mar-01 RMS Added clock calibration support + + Note: includes the IOT's for both the PDP-8/E and PDP-8/A clocks +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, int_enable, dev_done, stop_inst; + +int32 clk_tps = 60; /* ticks/second */ +int32 tmxr_poll = 16000; /* term mux poll */ + +int32 clk (int32 IR, int32 AC); +t_stat clk_svc (UNIT *uptr); +t_stat clk_reset (DEVICE *dptr); +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +DIB clk_dib = { DEV_CLK, 1, { &clk } }; + +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), 16000 }; + +REG clk_reg[] = { + { FLDATA (DONE, dev_done, INT_V_CLK) }, + { FLDATA (ENABLE, int_enable, INT_V_CLK) }, + { FLDATA (INT, int_req, INT_V_CLK) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 8), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &clk_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &clk_show_freq, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, 0 + }; + +/* IOT routine + + IOT's 6131-6133 are the PDP-8/E clock + IOT's 6135-6137 are the PDP-8/A clock +*/ + +int32 clk (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* CLEI */ + int_enable = int_enable | INT_CLK; /* enable clk ints */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 2: /* CLDI */ + int_enable = int_enable & ~INT_CLK; /* disable clk ints */ + int_req = int_req & ~INT_CLK; /* update interrupts */ + return AC; + + case 3: /* CLSC */ + if (dev_done & INT_CLK) { /* flag set? */ + dev_done = dev_done & ~INT_CLK; /* clear flag */ + int_req = int_req & ~INT_CLK; /* clear int req */ + return IOT_SKP + AC; + } + return AC; + + case 5: /* CLLE */ + if (AC & 1) int_enable = int_enable | INT_CLK; /* test AC<11> */ + else int_enable = int_enable & ~INT_CLK; + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 6: /* CLCL */ + dev_done = dev_done & ~INT_CLK; /* clear flag */ + int_req = int_req & ~INT_CLK; /* clear int req */ + return AC; + + case 7: /* CLSK */ + return (dev_done & INT_CLK)? IOT_SKP + AC: AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +int32 t; + +dev_done = dev_done | INT_CLK; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ +sim_activate (&clk_unit, t); /* reactivate unit */ +tmxr_poll = t; /* set mux poll */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +int32 t; + +dev_done = dev_done & ~INT_CLK; /* clear done, int */ +int_req = int_req & ~INT_CLK; +int_enable = int_enable & ~INT_CLK; /* clear enable */ +t = sim_rtcn_init (clk_unit.wait, TMR_CLK); +sim_activate_abs (&clk_unit, t); /* activate unit */ +tmxr_poll = t; +return SCPE_OK; +} + +/* Set frequency */ + +t_stat clk_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val != 50) && (val != 60)) return SCPE_IERR; +clk_tps = val; +return SCPE_OK; +} + +/* Show frequency */ + +t_stat clk_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, (clk_tps == 50)? "50Hz": "60Hz"); +return SCPE_OK; +} diff --git a/PDP8/pdp8_cpu.c b/PDP8/pdp8_cpu.c new file mode 100644 index 0000000..63d3bfb --- /dev/null +++ b/PDP8/pdp8_cpu.c @@ -0,0 +1,1486 @@ +/* pdp8_cpu.c: PDP-8 CPU simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu central processor + + 28-Apr-07 RMS Removed clock initialization + 30-Oct-06 RMS Added idle and infinite loop detection + 30-Sep-06 RMS Fixed SC value after DVI overflow (found by Don North) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 06-Nov-04 RMS Added =n to SHOW HISTORY + 31-Dec-03 RMS Fixed bug in set_cpu_hist + 13-Oct-03 RMS Added instruction history + Added TSC8-75 support (from Bernhard Baehr) + 12-Mar-03 RMS Added logical name support + 04-Oct-02 RMS Revamped device dispatching, added device number support + 06-Jan-02 RMS Added device enable/disable routines + 30-Dec-01 RMS Added old PC queue + 16-Dec-01 RMS Fixed bugs in EAE + 07-Dec-01 RMS Revised to use new breakpoint package + 30-Nov-01 RMS Added RL8A, extended SET/SHOW support + 16-Sep-01 RMS Fixed bug in reset routine, added KL8A support + 10-Aug-01 RMS Removed register from declarations + 17-Jul-01 RMS Moved function prototype + 07-Jun-01 RMS Fixed bug in JMS to non-existent memory + 25-Apr-01 RMS Added device enable/disable support + 18-Mar-01 RMS Added DF32 support + 05-Mar-01 RMS Added clock calibration support + 15-Feb-01 RMS Added DECtape support + 14-Apr-99 RMS Changed t_addr to unsigned + + The register state for the PDP-8 is: + + AC<0:11> accumulator + MQ<0:11> multiplier-quotient + L link flag + PC<0:11> program counter + IF<0:2> instruction field + IB<0:2> instruction buffer + DF<0:2> data field + UF user flag + UB user buffer + SF<0:6> interrupt save field + + The PDP-8 has three instruction formats: memory reference, I/O transfer, + and operate. The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | op |in|zr| page offset | memory reference + +--+--+--+--+--+--+--+--+--+--+--+--+ + + <0:2> mnemonic action + + 000 AND AC = AC & M[MA] + 001 TAD L'AC = AC + M[MA] + 010 DCA M[MA] = AC, AC = 0 + 011 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0 + 100 JMS M[MA] = PC, PC = MA + 1 + 101 JMP PC = MA + + <3:4> mode action + 00 page zero MA = IF'0'IR<5:11> + 01 current page MA = IF'PC<0:4>'IR<5:11> + 10 indirect page zero MA = xF'M[IF'0'IR<5:11>] + 11 indirect current page MA = xF'M[IF'PC<0:4>'IR<5:11>] + + where x is D for AND, TAD, ISZ, DCA, and I for JMS, JMP. + + Memory reference instructions can access an address space of 32K words. + The address space is divided into eight 4K word fields; each field is + divided into thirty-two 128 word pages. An instruction can directly + address, via its 7b offset, locations 0-127 on page zero or on the current + page. All 32k words can be accessed via indirect addressing and the + instruction and data field registers. If an indirect address is in + locations 0010-0017 of any field, the indirect address is incremented + and rewritten to memory before use. + + The I/O transfer format is as follows: + + 0 1 2 3 4 5 6 7 8 9 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | op | device | pulse | I/O transfer + +--+--+--+--+--+--+--+--+--+--+--+--+ + + The IO transfer instruction sends the the specified pulse to the + specified I/O device. The I/O device may take data from the AC, + return data to the AC, initiate or cancel operations, or skip on + status. + + The operate format is as follows: + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 0| | | | | | | | | operate group 1 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | | + | | | | | | | +--- increment AC 3 + | | | | | | +--- rotate 1 or 2 4 + | | | | | +--- rotate left 4 + | | | | +--- rotate right 4 + | | | +--- complement L 2 + | | +--- complement AC 2 + | +--- clear L 1 + +-- clear AC 1 + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 1| | | | | | | | 0| operate group 2 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | | | | + | | | | | | +--- halt 3 + | | | | | +--- or switch register 3 + | | | | +--- reverse skip sense 1 + | | | +--- skip on L != 0 1 + | | +--- skip on AC == 0 1 + | +--- skip on AC < 0 1 + +-- clear AC 2 + + +--+--+--+--+--+--+--+--+--+--+--+--+ + | 1| 1| 1| 1| | | | | | | | 1| operate group 3 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | | | | \______/ + | | | | | + | | +--|-----+--- EAE command 3 + | | +--- AC -> MQ, 0 -> AC 2 + | +--- MQ v AC --> AC 2 + +-- clear AC 1 + + The operate instruction can be microprogrammed to perform operations + on the AC, MQ, and link. + + This routine is the instruction decode routine for the PDP-8. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + unimplemented instruction and stop_inst flag set + I/O error in I/O simulator + + 2. Interrupts. Interrupts are maintained by three parallel variables: + + dev_done device done flags + int_enable interrupt enable flags + int_req interrupt requests + + In addition, int_req contains the interrupt enable flag, the + CIF not pending flag, and the ION not pending flag. If all + three of these flags are set, and at least one interrupt request + is set, then an interrupt occurs. + + 3. Non-existent memory. On the PDP-8, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes outside the current field (indirect writes) need + be checked against actual memory size. + + 3. Adding I/O devices. These modules must be modified: + + pdp8_defs.h add device number and interrupt definitions + pdp8_sys.c add sim_devices table entry +*/ + +#include "pdp8_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = MA +#define UNIT_V_NOEAE (UNIT_V_UF) /* EAE absent */ +#define UNIT_NOEAE (1 << UNIT_V_NOEAE) +#define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) +#define OP_KSF 06031 /* for idle */ + +#define HIST_PC 0x40000000 +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + int32 pc; + int32 ea; + int16 ir; + int16 opnd; + int16 lac; + int16 mq; + } InstHistory; + +uint16 M[MAXMEMSIZE] = { 0 }; /* main memory */ +int32 saved_LAC = 0; /* saved L'AC */ +int32 saved_MQ = 0; /* saved MQ */ +int32 saved_PC = 0; /* saved IF'PC */ +int32 saved_DF = 0; /* saved Data Field */ +int32 IB = 0; /* Instruction Buffer */ +int32 SF = 0; /* Save Field */ +int32 emode = 0; /* EAE mode */ +int32 gtf = 0; /* EAE gtf flag */ +int32 SC = 0; /* EAE shift count */ +int32 UB = 0; /* User mode Buffer */ +int32 UF = 0; /* User mode Flag */ +int32 OSR = 0; /* Switch Register */ +int32 tsc_ir = 0; /* TSC8-75 IR */ +int32 tsc_pc = 0; /* TSC8-75 PC */ +int32 tsc_cdf = 0; /* TSC8-75 CDF flag */ +int32 tsc_enb = 0; /* TSC8-75 enabled */ +int16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 dev_done = 0; /* dev done flags */ +int32 int_enable = INT_INIT_ENABLE; /* intr enables */ +int32 int_req = 0; /* intr requests */ +int32 stop_inst = 0; /* trap on ill inst */ +int32 (*dev_tab[DEV_MAX])(int32 IR, int32 dat); /* device dispatch */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ + +extern int32 sim_interval; +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern DEVICE *sim_devices[]; +extern FILE *sim_log; +extern t_bool sim_idle_enab; + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_bool build_dev_tab (void); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (PC, saved_PC, 15) }, + { ORDATA (AC, saved_LAC, 12) }, + { FLDATA (L, saved_LAC, 12) }, + { ORDATA (MQ, saved_MQ, 12) }, + { ORDATA (SR, OSR, 12) }, + { GRDATA (IF, saved_PC, 8, 3, 12) }, + { GRDATA (DF, saved_DF, 8, 3, 12) }, + { GRDATA (IB, IB, 8, 3, 12) }, + { ORDATA (SF, SF, 7) }, + { FLDATA (UB, UB, 0) }, + { FLDATA (UF, UF, 0) }, + { ORDATA (SC, SC, 5) }, + { FLDATA (GTF, gtf, 0) }, + { FLDATA (EMODE, emode, 0) }, + { FLDATA (ION, int_req, INT_V_ION) }, + { FLDATA (ION_DELAY, int_req, INT_V_NO_ION_PENDING) }, + { FLDATA (CIF_DELAY, int_req, INT_V_NO_CIF_PENDING) }, + { FLDATA (PWR_INT, int_req, INT_V_PWR) }, + { FLDATA (UF_INT, int_req, INT_V_UF) }, + { ORDATA (INT, int_req, INT_V_ION+1), REG_RO }, + { ORDATA (DONE, dev_done, INT_V_DIRECT), REG_RO }, + { ORDATA (ENABLE, int_enable, INT_V_DIRECT), REG_RO }, + { BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { FLDATA (STOP_INST, stop_inst, 0) }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_NOEAE, UNIT_NOEAE, "no EAE", "NOEAE", NULL }, + { UNIT_NOEAE, 0, "EAE", "EAE", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 12288, NULL, "12K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 20480, NULL, "20K", &cpu_set_size }, + { UNIT_MSIZE, 24576, NULL, "24K", &cpu_set_size }, + { UNIT_MSIZE, 28672, NULL, "28K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 15, 1, 8, 12, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +t_stat sim_instr (void) +{ +int32 IR, MB, IF, DF, LAC, MQ; +uint32 PC, MA; +int32 device, pulse, temp, iot_data; +t_stat reason; + +/* Restore register state */ + +if (build_dev_tab ()) return SCPE_STOP; /* build dev_tab */ +PC = saved_PC & 007777; /* load local copies */ +IF = saved_PC & 070000; +DF = saved_DF & 070000; +LAC = saved_LAC & 017777; +MQ = saved_MQ & 07777; +int_req = INT_UPDATE; +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; + } + + if (int_req > INT_PENDING) { /* interrupt? */ + int_req = int_req & ~INT_ION; /* interrupts off */ + SF = (UF << 6) | (IF >> 9) | (DF >> 12); /* form save field */ + IF = IB = DF = UF = UB = 0; /* clear mem ext */ + PCQ_ENTRY; /* save old PC */ + M[0] = PC; /* save PC in 0 */ + PC = 1; /* fetch next from 1 */ + } + + MA = IF | PC; /* form PC */ + if (sim_brk_summ && sim_brk_test (MA, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + + IR = M[MA]; /* fetch instruction */ + PC = (PC + 1) & 07777; /* increment PC */ + int_req = int_req | INT_NO_ION_PENDING; /* clear ION delay */ + sim_interval = sim_interval - 1; + +/* Instruction decoding. + + The opcode (IR<0:2>), indirect flag (IR<3>), and page flag (IR<4>) + are decoded together. This produces 32 decode points, four per + major opcode. For IOT, the extra decode points are not useful; + for OPR, only the group flag (IR<3>) is used. + + AND, TAD, ISZ, DCA calculate a full 15b effective address. + JMS, JMP calculate a 12b field-relative effective address. + + Autoindex calculations always occur within the same field as the + instruction fetch. The field must exist; otherwise, the instruction + fetched would be 0000, and indirect addressing could not occur. + + Note that MA contains IF'PC. +*/ + + if (hst_lnt) { /* history enabled? */ + int32 ea; + + hst_p = (hst_p + 1); /* next entry */ + if (hst_p >= hst_lnt) hst_p = 0; + hst[hst_p].pc = MA | HIST_PC; /* save PC, IR, LAC, MQ */ + hst[hst_p].ir = IR; + hst[hst_p].lac = LAC; + hst[hst_p].mq = MQ; + if (IR < 06000) { /* mem ref? */ + if (IR & 0200) ea = (MA & 077600) | (IR & 0177); + else ea = IF | (IR & 0177); /* direct addr */ + if (IR & 0400) { /* indirect? */ + if (IR < 04000) { /* mem operand? */ + if ((ea & 07770) != 00010) ea = DF | M[ea]; + else ea = DF | ((M[ea] + 1) & 07777); + } + else { /* no, jms/jmp */ + if ((ea & 07770) != 00010) ea = IB | M[ea]; + else ea = IB | ((M[ea] + 1) & 07777); + } + } + hst[hst_p].ea = ea; /* save eff addr */ + hst[hst_p].opnd = M[ea]; /* save operand */ + } + } + +switch ((IR >> 7) & 037) { /* decode IR<0:4> */ + +/* Opcode 0, AND */ + + case 000: /* AND, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + LAC = LAC & (M[MA] | 010000); + break; + + case 001: /* AND, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + LAC = LAC & (M[MA] | 010000); + break; + + case 002: /* AND, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = LAC & (M[MA] | 010000); + break; + + case 003: /* AND, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = LAC & (M[MA] | 010000); + break; + +/* Opcode 1, TAD */ + + case 004: /* TAD, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + LAC = (LAC + M[MA]) & 017777; + break; + + case 005: /* TAD, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + LAC = (LAC + M[MA]) & 017777; + break; + + case 006: /* TAD, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = (LAC + M[MA]) & 017777; + break; + + case 007: /* TAD, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + LAC = (LAC + M[MA]) & 017777; + break; + +/* Opcode 2, ISZ */ + + case 010: /* ISZ, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ + if (MB == 0) PC = (PC + 1) & 07777; + break; + + case 011: /* ISZ, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + M[MA] = MB = (M[MA] + 1) & 07777; /* field must exist */ + if (MB == 0) PC = (PC + 1) & 07777; + break; + + case 012: /* ISZ, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + MB = (M[MA] + 1) & 07777; + if (MEM_ADDR_OK (MA)) M[MA] = MB; + if (MB == 0) PC = (PC + 1) & 07777; + break; + + case 013: /* ISZ, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + MB = (M[MA] + 1) & 07777; + if (MEM_ADDR_OK (MA)) M[MA] = MB; + if (MB == 0) PC = (PC + 1) & 07777; + break; + +/* Opcode 3, DCA */ + + case 014: /* DCA, dir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + + case 015: /* DCA, dir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + + case 016: /* DCA, indir, zero */ + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + + case 017: /* DCA, indir, curr */ + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; + LAC = LAC & 010000; + break; + +/* Opcode 4, JMS. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) the current JMS opcode is moved to the ERIOT register, the ECDF + flag is cleared. The address of the JMS instruction is loaded into the ERTB + register and the TSC8-75 I/O flag is raised. When the TSC8-75 is enabled, the + target addess of the JMS is loaded into PC, but nothing else (loading of IF, UF, + clearing the interrupt inhibit flag, storing of the return address in the first + word of the subroutine) happens. When the TSC8-75 is disabled, the JMS is performed + as usual. */ + + case 020: /* JMS, dir, zero */ + PCQ_ENTRY; + MA = IR & 0177; /* dir addr, page zero */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + + case 021: /* JMS, dir, curr */ + PCQ_ENTRY; + MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + + case 022: /* JMS, indir, zero */ + PCQ_ENTRY; + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */ + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + + case 023: /* JMS, indir, curr */ + PCQ_ENTRY; + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */ + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + if (UF && tsc_enb) { /* user mode, TSC enab? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + else { /* normal */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + MA = IF | MA; + if (MEM_ADDR_OK (MA)) M[MA] = PC; + } + PC = (MA + 1) & 07777; + break; + +/* Opcode 5, JMP. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) the current JMP opcode is moved to the ERIOT register, the ECDF + flag is cleared. The address of the JMP instruction is loaded into the ERTB + register and the TSC8-75 I/O flag is raised. Then the JMP is performed as usual + (including the setting of IF, UF and clearing the interrupt inhibit flag). */ + + + case 024: /* JMP, dir, zero */ + PCQ_ENTRY; + MA = IR & 0177; /* dir addr, page zero */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + +/* If JMP direct, also check for idle (KSF/JMP *-1) and infinite loop */ + + case 025: /* JMP, dir, curr */ + PCQ_ENTRY; + MA = (MA & 007600) | (IR & 0177); /* dir addr, curr page */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + if (sim_idle_enab && /* idling enabled? */ + (IF == IB)) { /* to same bank? */ + if (MA == ((PC - 2) & 07777)) { /* 1) JMP *-1? */ + if (!(int_req & (INT_ION|INT_TTI)) && /* iof, TTI flag off? */ + (M[IB|((PC - 2) & 07777)] == OP_KSF)) /* next is KSF? */ + sim_idle (TMR_CLK, FALSE); /* we're idle */ + } /* end JMP *-1 */ + else if (MA == ((PC - 1) & 07777)) { /* 2) JMP *? */ + if (!(int_req & INT_ION)) /* iof? */ + reason = STOP_LOOP; /* then infinite loop */ + else if (!(int_req & INT_ALL)) /* ion, not intr? */ + sim_idle (TMR_CLK, FALSE); /* we're idle */ + } /* end JMP */ + } /* end idle enabled */ + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + + case 026: /* JMP, indir, zero */ + PCQ_ENTRY; + MA = IF | (IR & 0177); /* dir addr, page zero */ + if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */ + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + + case 027: /* JMP, indir, curr */ + PCQ_ENTRY; + MA = (MA & 077600) | (IR & 0177); /* dir addr, curr page */ + if ((MA & 07770) != 00010) MA = M[MA]; /* indirect; autoinc? */ + else MA = (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (UF) { /* user mode? */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + if (tsc_enb) { /* TSC8 enabled? */ + tsc_pc = (PC - 1) & 07777; /* save PC */ + int_req = int_req | INT_TSC; /* request intr */ + } + } + IF = IB; /* change IF */ + UF = UB; /* change UF */ + int_req = int_req | INT_NO_CIF_PENDING; /* clr intr inhibit */ + PC = MA; + break; + +/* Opcode 7, OPR group 1 */ + + case 034:case 035: /* OPR, group 1 */ + switch ((IR >> 4) & 017) { /* decode IR<4:7> */ + case 0: /* nop */ + break; + case 1: /* CML */ + LAC = LAC ^ 010000; + break; + case 2: /* CMA */ + LAC = LAC ^ 07777; + break; + case 3: /* CMA CML */ + LAC = LAC ^ 017777; + break; + case 4: /* CLL */ + LAC = LAC & 07777; + break; + case 5: /* CLL CML = STL */ + LAC = LAC | 010000; + break; + case 6: /* CLL CMA */ + LAC = (LAC ^ 07777) & 07777; + break; + case 7: /* CLL CMA CML */ + LAC = (LAC ^ 07777) | 010000; + break; + case 010: /* CLA */ + LAC = LAC & 010000; + break; + case 011: /* CLA CML */ + LAC = (LAC & 010000) ^ 010000; + break; + case 012: /* CLA CMA = STA */ + LAC = LAC | 07777; + break; + case 013: /* CLA CMA CML */ + LAC = (LAC | 07777) ^ 010000; + break; + case 014: /* CLA CLL */ + LAC = 0; + break; + case 015: /* CLA CLL CML */ + LAC = 010000; + break; + case 016: /* CLA CLL CMA */ + LAC = 07777; + break; + case 017: /* CLA CLL CMA CML */ + LAC = 017777; + break; + } /* end switch opers */ + + if (IR & 01) LAC = (LAC + 1) & 017777; /* IAC */ + switch ((IR >> 1) & 07) { /* decode IR<8:10> */ + case 0: /* nop */ + break; + case 1: /* BSW */ + LAC = (LAC & 010000) | ((LAC >> 6) & 077) | ((LAC & 077) << 6); + break; + case 2: /* RAL */ + LAC = ((LAC << 1) | (LAC >> 12)) & 017777; + break; + case 3: /* RTL */ + LAC = ((LAC << 2) | (LAC >> 11)) & 017777; + break; + case 4: /* RAR */ + LAC = ((LAC >> 1) | (LAC << 12)) & 017777; + break; + case 5: /* RTR */ + LAC = ((LAC >> 2) | (LAC << 11)) & 017777; + break; + case 6: /* RAL RAR - undef */ + LAC = LAC & (IR | 010000); /* uses AND path */ + break; + case 7: /* RTL RTR - undef */ + LAC = (LAC & 010000) | (MA & 07600) | (IR & 0177); + break; /* uses address path */ + } /* end switch shifts */ + break; /* end group 1 */ + +/* OPR group 2. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) HLT (7402), OSR (7404) and microprogrammed combinations with + HLT and OSR: Additional to raising a user mode interrupt, the current OPR + opcode is moved to the ERIOT register and the ECDF flag is cleared. */ + + case 036:case 037: /* OPR, groups 2, 3 */ + if ((IR & 01) == 0) { /* group 2 */ + switch ((IR >> 3) & 017) { /* decode IR<6:8> */ + case 0: /* nop */ + break; + case 1: /* SKP */ + PC = (PC + 1) & 07777; + break; + case 2: /* SNL */ + if (LAC >= 010000) PC = (PC + 1) & 07777; + break; + case 3: /* SZL */ + if (LAC < 010000) PC = (PC + 1) & 07777; + break; + case 4: /* SZA */ + if ((LAC & 07777) == 0) PC = (PC + 1) & 07777; + break; + case 5: /* SNA */ + if ((LAC & 07777) != 0) PC = (PC + 1) & 07777; + break; + case 6: /* SZA | SNL */ + if ((LAC == 0) || (LAC >= 010000)) + PC = (PC + 1) & 07777; + break; + case 7: /* SNA & SZL */ + if ((LAC != 0) && (LAC < 010000)) PC = (PC + 1) & 07777; + break; + case 010: /* SMA */ + if ((LAC & 04000) != 0) PC = (PC + 1) & 07777; + break; + case 011: /* SPA */ + if ((LAC & 04000) == 0) PC = (PC + 1) & 07777; + break; + case 012: /* SMA | SNL */ + if (LAC >= 04000) PC = (PC + 1) & 07777; + break; + case 013: /* SPA & SZL */ + if (LAC < 04000) PC = (PC + 1) & 07777; + break; + case 014: /* SMA | SZA */ + if (((LAC & 04000) != 0) || ((LAC & 07777) == 0)) + PC = (PC + 1) & 07777; + break; + case 015: /* SPA & SNA */ + if (((LAC & 04000) == 0) && ((LAC & 07777) != 0)) + PC = (PC + 1) & 07777; + break; + case 016: /* SMA | SZA | SNL */ + if ((LAC >= 04000) || (LAC == 0)) PC = (PC + 1) & 07777; + break; + case 017: /* SPA & SNA & SZL */ + if ((LAC < 04000) && (LAC != 0)) PC = (PC + 1) & 07777; + break; + } /* end switch skips */ + if (IR & 0200) LAC = LAC & 010000; /* CLA */ + if ((IR & 06) && UF) { /* user mode? */ + int_req = int_req | INT_UF; /* request intr */ + tsc_ir = IR; /* save instruction */ + tsc_cdf = 0; /* clear flag */ + } + else { + if (IR & 04) LAC = LAC | OSR; /* OSR */ + if (IR & 02) reason = STOP_HALT; /* HLT */ + } + break; + } /* end if group 2 */ + +/* OPR group 3 standard + + MQA!MQL exchanges AC and MQ, as follows: + + temp = MQ; + MQ = LAC & 07777; + LAC = LAC & 010000 | temp; +*/ + + temp = MQ; /* group 3 */ + if (IR & 0200) LAC = LAC & 010000; /* CLA */ + if (IR & 0020) { /* MQL */ + MQ = LAC & 07777; + LAC = LAC & 010000; + } + if (IR & 0100) LAC = LAC | temp; /* MQA */ + if ((IR & 0056) && (cpu_unit.flags & UNIT_NOEAE)) { + reason = stop_inst; /* EAE not present */ + break; + } + +/* OPR group 3 EAE + + The EAE operates in two modes: + + Mode A, PDP-8/I compatible + Mode B, extended capability + + Mode B provides eight additional subfunctions; in addition, some + of the Mode A functions operate differently in Mode B. + + The mode switch instructions are decoded explicitly and cannot be + microprogrammed with other EAE functions (SWAB performs an MQL as + part of standard group 3 decoding). If mode switching is decoded, + all other EAE timing is suppressed. +*/ + + if (IR == 07431) { /* SWAB */ + emode = 1; /* set mode flag */ + break; + } + if (IR == 07447) { /* SWBA */ + emode = gtf = 0; /* clear mode, gtf */ + break; + } + +/* If not switching modes, the EAE operation is determined by the mode + and IR<6,8:10>: + + <6:10> mode A mode B comments + + 0x000 NOP NOP + 0x001 SCL ACS + 0x010 MUY MUY if mode B, next = address + 0x011 DVI DVI if mode B, next = address + 0x100 NMI NMI if mode B, clear AC if + result = 4000'0000 + 0x101 SHL SHL if mode A, extra shift + 0x110 ASR ASR if mode A, extra shift + 0x111 LSR LSR if mode A, extra shift + 1x000 SCA SCA + 1x001 SCA + SCL DAD + 1x010 SCA + MUY DST + 1x011 SCA + DVI SWBA NOP if not detected earlier + 1x100 SCA + NMI DPSZ + 1x101 SCA + SHL DPIC must be combined with MQA!MQL + 1x110 SCA + ASR DCM must be combined with MQA!MQL + 1x111 SCA + LSR SAM + + EAE instructions which fetch memory operands use the CPU's DEFER + state to read the first word; if the address operand is in locations + x0010 - x0017, it is autoincremented. +*/ + + if (emode == 0) gtf = 0; /* mode A? clr gtf */ + switch ((IR >> 1) & 027) { /* decode IR<6,8:10> */ + + case 020: /* mode A, B: SCA */ + LAC = LAC | SC; + break; + case 000: /* mode A, B: NOP */ + break; + + case 021: /* mode B: DAD */ + if (emode) { + MA = IF | PC; + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + MQ = MQ + M[MA]; + MA = DF | ((MA + 1) & 07777); + LAC = (LAC & 07777) + M[MA] + (MQ >> 12); + MQ = MQ & 07777; + PC = (PC + 1) & 07777; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 001: /* mode B: ACS */ + if (emode) { + SC = LAC & 037; + LAC = LAC & 010000; + } + else { /* mode A: SCL */ + SC = (~M[IF | PC]) & 037; + PC = (PC + 1) & 07777; + } + break; + + case 022: /* mode B: DST */ + if (emode) { + MA = IF | PC; + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + if (MEM_ADDR_OK (MA)) M[MA] = MQ & 07777; + MA = DF | ((MA + 1) & 07777); + if (MEM_ADDR_OK (MA)) M[MA] = LAC & 07777; + PC = (PC + 1) & 07777; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 002: /* MUY */ + MA = IF | PC; + if (emode) { /* mode B: defer */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + } + temp = (MQ * M[MA]) + (LAC & 07777); + LAC = (temp >> 12) & 07777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = 014; /* 12 shifts */ + break; + + case 023: /* mode B: SWBA */ + if (emode) break; + LAC = LAC | SC; /* mode A: SCA then */ + case 003: /* DVI */ + MA = IF | PC; + if (emode) { /* mode B: defer */ + if ((MA & 07770) != 00010) MA = DF | M[MA]; /* indirect; autoinc? */ + else MA = DF | (M[MA] = (M[MA] + 1) & 07777); /* incr before use */ + } + if ((LAC & 07777) >= M[MA]) { /* overflow? */ + LAC = LAC | 010000; /* set link */ + MQ = ((MQ << 1) + 1) & 07777; /* rotate MQ */ + SC = 0; /* no shifts */ + } + else { + temp = ((LAC & 07777) << 12) | MQ; + MQ = temp / M[MA]; + LAC = temp % M[MA]; + SC = 015; /* 13 shifts */ + } + PC = (PC + 1) & 07777; + break; + + case 024: /* mode B: DPSZ */ + if (emode) { + if (((LAC | MQ) & 07777) == 0) PC = (PC + 1) & 07777; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 004: /* NMI */ + temp = (LAC << 12) | MQ; /* preserve link */ + for (SC = 0; ((temp & 017777777) != 0) && + (temp & 040000000) == ((temp << 1) & 040000000); SC++) + temp = temp << 1; + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + if (emode && ((LAC & 07777) == 04000) && (MQ == 0)) + LAC = LAC & 010000; /* clr if 4000'0000 */ + break; + + case 025: /* mode B: DPIC */ + if (emode) { + temp = (LAC + 1) & 07777; /* SWP already done! */ + LAC = MQ + (temp == 0); + MQ = temp; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 5: /* SHL */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + if (SC > 25) temp = 0; /* >25? result = 0 */ + else temp = ((LAC << 12) | MQ) << SC; /* <=25? shift LAC:MQ */ + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + + case 026: /* mode B: DCM */ + if (emode) { + temp = (-LAC) & 07777; /* SWP already done! */ + LAC = (MQ ^ 07777) + (temp == 0); + MQ = temp; + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 6: /* ASR */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + temp = ((LAC & 07777) << 12) | MQ; /* sext from AC0 */ + if (LAC & 04000) temp = temp | ~037777777; + if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; + if (SC > 25) temp = (LAC & 04000)? -1: 0; + else temp = temp >> SC; + LAC = (temp >> 12) & 017777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + + case 027: /* mode B: SAM */ + if (emode) { + temp = LAC & 07777; + LAC = MQ + (temp ^ 07777) + 1; /* L'AC = MQ - AC */ + gtf = (temp <= MQ) ^ ((temp ^ MQ) >> 11); + break; + } + LAC = LAC | SC; /* mode A: SCA then */ + case 7: /* LSR */ + SC = (M[IF | PC] & 037) + (emode ^ 1); /* shift+1 if mode A */ + temp = ((LAC & 07777) << 12) | MQ; /* clear link */ + if (emode && (SC != 0)) gtf = (temp >> (SC - 1)) & 1; + if (SC > 24) temp = 0; /* >24? result = 0 */ + else temp = temp >> SC; /* <=24? shift AC:MQ */ + LAC = (temp >> 12) & 07777; + MQ = temp & 07777; + PC = (PC + 1) & 07777; + SC = emode? 037: 0; /* SC = 0 if mode A */ + break; + } /* end switch */ + break; /* end case 7 */ + +/* Opcode 6, IOT. From Bernhard Baehr's description of the TSC8-75: + + (In user mode) Additional to raising a user mode interrupt, the current IOT + opcode is moved to the ERIOT register. When the IOT is a CDF instruction (62x1), + the ECDF flag is set, otherwise it is cleared. */ + + case 030:case 031:case 032:case 033: /* IOT */ + if (UF) { /* privileged? */ + int_req = int_req | INT_UF; /* request intr */ + tsc_ir = IR; /* save instruction */ + if ((IR & 07707) == 06201) tsc_cdf = 1; /* set/clear flag */ + else tsc_cdf = 0; + break; + } + device = (IR >> 3) & 077; /* device = IR<3:8> */ + pulse = IR & 07; /* pulse = IR<9:11> */ + iot_data = LAC & 07777; /* AC unchanged */ + switch (device) { /* decode IR<3:8> */ + + case 000: /* CPU control */ + switch (pulse) { /* decode IR<9:11> */ + + case 0: /* SKON */ + if (int_req & INT_ION) PC = (PC + 1) & 07777; + int_req = int_req & ~INT_ION; + break; + + case 1: /* ION */ + int_req = (int_req | INT_ION) & ~INT_NO_ION_PENDING; + break; + + case 2: /* IOF */ + int_req = int_req & ~INT_ION; + break; + + case 3: /* SRQ */ + if (int_req & INT_ALL) PC = (PC + 1) & 07777; + break; + + case 4: /* GTF */ + LAC = (LAC & 010000) | + ((LAC & 010000) >> 1) | (gtf << 10) | + (((int_req & INT_ALL) != 0) << 9) | + (((int_req & INT_ION) != 0) << 7) | SF; + break; + + case 5: /* RTF */ + gtf = ((LAC & 02000) >> 10); + UB = (LAC & 0100) >> 6; + IB = (LAC & 0070) << 9; + DF = (LAC & 0007) << 12; + LAC = ((LAC & 04000) << 1) | iot_data; + int_req = (int_req | INT_ION) & ~INT_NO_CIF_PENDING; + break; + + case 6: /* SGT */ + if (gtf) PC = (PC + 1) & 07777; + break; + + case 7: /* CAF */ + gtf = 0; + emode = 0; + int_req = int_req & INT_NO_CIF_PENDING; + dev_done = 0; + int_enable = INT_INIT_ENABLE; + LAC = 0; + reset_all (1); /* reset all dev */ + break; + } /* end switch pulse */ + break; /* end case 0 */ + + case 020:case 021:case 022:case 023: + case 024:case 025:case 026:case 027: /* memory extension */ + switch (pulse) { /* decode IR<9:11> */ + + case 1: /* CDF */ + DF = (IR & 0070) << 9; + break; + + case 2: /* CIF */ + IB = (IR & 0070) << 9; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 3: /* CDF CIF */ + DF = IB = (IR & 0070) << 9; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 4: + switch (device & 07) { /* decode IR<6:8> */ + + case 0: /* CINT */ + int_req = int_req & ~INT_UF; + break; + + case 1: /* RDF */ + LAC = LAC | (DF >> 9); + break; + + case 2: /* RIF */ + LAC = LAC | (IF >> 9); + break; + + case 3: /* RIB */ + LAC = LAC | SF; + break; + + case 4: /* RMF */ + UB = (SF & 0100) >> 6; + IB = (SF & 0070) << 9; + DF = (SF & 0007) << 12; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 5: /* SINT */ + if (int_req & INT_UF) PC = (PC + 1) & 07777; + break; + + case 6: /* CUF */ + UB = 0; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + + case 7: /* SUF */ + UB = 1; + int_req = int_req & ~INT_NO_CIF_PENDING; + break; + } /* end switch device */ + break; + + default: + reason = stop_inst; + break; + } /* end switch pulse */ + break; /* end case 20-27 */ + + case 010: /* power fail */ + switch (pulse) { /* decode IR<9:11> */ + + case 1: /* SBE */ + break; + + case 2: /* SPL */ + if (int_req & INT_PWR) PC = (PC + 1) & 07777; + break; + + case 3: /* CAL */ + int_req = int_req & ~INT_PWR; + break; + + default: + reason = stop_inst; + break; + } /* end switch pulse */ + break; /* end case 10 */ + + default: /* I/O device */ + if (dev_tab[device]) { /* dev present? */ + iot_data = dev_tab[device] (IR, iot_data); + LAC = (LAC & 010000) | (iot_data & 07777); + if (iot_data & IOT_SKP) PC = (PC + 1) & 07777; + if (iot_data >= IOT_REASON) + reason = iot_data >> IOT_V_REASON; + } + else reason = stop_inst; /* stop on flag */ + break; + } /* end switch device */ + break; /* end case IOT */ + } /* end switch opcode */ + } /* end while */ + +/* Simulation halted */ + +saved_PC = IF | (PC & 07777); /* save copies */ +saved_DF = DF & 070000; +saved_LAC = LAC & 017777; +saved_MQ = MQ & 07777; +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} /* end sim_instr */ + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int_req = (int_req & ~INT_ION) | INT_NO_CIF_PENDING; +saved_DF = IB = saved_PC & 070000; +UF = UB = gtf = emode = 0; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & 07777; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & 07777; +return SCPE_OK; +} + +/* Memory size change */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* Change device number for a device */ + +t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newdev; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newdev = get_uint (cptr, 8, DEV_MAX - 1, &r); /* get new */ +if ((r != SCPE_OK) || (newdev == dibp->dev)) return r; +dibp->dev = newdev; /* store */ +return SCPE_OK; +} + +/* Show device number for a device */ + +t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +fprintf (st, "devno=%02o", dibp->dev); +if (dibp->num > 1) fprintf (st, "-%2o", dibp->dev + dibp->num - 1); +return SCPE_OK; +} + +/* CPU device handler - should never get here! */ + +int32 bad_dev (int32 IR, int32 AC) +{ +return (SCPE_IERR << IOT_V_REASON) | AC; /* broken! */ +} + +/* Build device dispatch table */ + +t_bool build_dev_tab (void) +{ +DEVICE *dptr; +DIB *dibp; +uint32 i, j; +static const uint8 std_dev[] = { + 000, 010, 020, 021, 022, 023, 024, 025, 026, 027 + }; + +for (i = 0; i < DEV_MAX; i++) dev_tab[i] = NULL; /* clr table */ +for (i = 0; i < ((uint32) sizeof (std_dev)); i++) /* std entries */ + dev_tab[std_dev[i]] = &bad_dev; +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* add devices */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* enabled? */ + for (j = 0; j < dibp->num; j++) { /* loop thru disp */ + if (dibp->dsp[j]) { /* any dispatch? */ + if (dev_tab[dibp->dev + j]) { /* already filled? */ + printf ("%s device number conflict at %02o\n", + sim_dname (dptr), dibp->dev + j); + if (sim_log) fprintf (sim_log, + "%s device number conflict at %02o\n", + sim_dname (dptr), dibp->dev + j); + return TRUE; + } + dev_tab[dibp->dev + j] = dibp->dsp[j]; /* fill */ + } /* end if dsp */ + } /* end for j */ + } /* end if enb */ + } /* end for i */ +return FALSE; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].pc = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 l, k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +t_value sim_eval; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC L AC MQ ea IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->pc & HIST_PC) { /* instruction? */ + l = (h->lac >> 12) & 1; /* link */ + fprintf (st, "%05o %o %04o %04o ", h->pc & ADDRMASK, l, h->lac & 07777, h->mq); + if (h->ir < 06000) fprintf (st, "%05o ", h->ea); + else fprintf (st, " "); + sim_eval = h->ir; + if ((fprint_sym (st, h->pc & ADDRMASK, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %04o", h->ir); + if (h->ir < 04000) fprintf (st, " [%04o]", h->opnd); + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/PDP8/pdp8_ct.c b/PDP8/pdp8_ct.c new file mode 100644 index 0000000..4451572 --- /dev/null +++ b/PDP8/pdp8_ct.c @@ -0,0 +1,711 @@ +/* pdp8_ct.c: PDP-8 cassette tape simulator + + Copyright (c) 2006-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ct TA8E/TU60 cassette tape + + 13-Aug-07 RMS Fixed handling of BEOT + 06-Aug-07 RMS Foward op at BOT skips initial file gap + 30-May-2007 RMS Fixed typo (from Norm Lastovica) + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. + + Cassette format differs in one very significant way: it has file gaps + rather than file marks. If the controller spaces or reads into a file + gap and then reverses direction, the file gap is not seen again. This + is in contrast to magnetic tapes, where the file mark is a character + sequence and is seen again if direction is reversed. In addition, + cassettes have an initial file gap which is automatically skipped on + forward operations from beginning of tape. + + Note that the read and write sequences for the cassette are asymmetric: + + Read: KLSA /SELECT READ + KGOA /INIT READ, CLEAR DF + + KGOA /READ 1ST CHAR, CLEAR DF + DCA CHAR + : + + KGOA /READ LAST CHAR, CLEAR DF + DCA CHAR + + KLSA /SELECT CRC MODE + KGOA /READ 1ST CRC + + KGOA /READ 2ND CRC + + + Write: KLSA /SELECT WRITE + TAD CHAR /1ST CHAR + KGOA /INIT WRITE, CHAR TO BUF, CLEAR DF + + : + TAD CHAR /LAST CHAR + KGOA /CHAR TO BUF, CLEAR DF + + KLSA /SELECT CRC MODE + KGOA /WRITE CRC, CLEAR DF + +*/ + +#include "pdp8_defs.h" +#include "sim_tape.h" + +#define CT_NUMDR 2 /* #drives */ +#define FNC u3 /* unit function */ +#define UST u4 /* unit status */ +#define CT_MAXFR (CT_SIZE) /* max record lnt */ +#define CT_SIZE 93000 /* chars/tape */ + +/* Status Register A */ + +#define SRA_ENAB 0200 /* enable */ +#define SRA_V_UNIT 6 /* unit */ +#define SRA_M_UNIT (CT_NUMDR - 1) +#define SRA_V_FNC 3 /* function */ +#define SRA_M_FNC 07 +#define SRA_READ 00 +#define SRA_REW 01 +#define SRA_WRITE 02 +#define SRA_SRF 03 +#define SRA_WFG 04 +#define SRA_SRB 05 +#define SRA_CRC 06 +#define SRA_SFF 07 +#define SRA_2ND 010 +#define SRA_IE 0001 /* int enable */ +#define GET_UNIT(x) (((x) >> SRA_V_UNIT) & SRA_M_UNIT) +#define GET_FNC(x) (((x) >> SRA_V_FNC) & SRA_M_FNC) + +/* Function code flags */ + +#define OP_WRI 01 /* op is a write */ +#define OP_REV 02 /* op is rev motion */ +#define OP_FWD 04 /* op is fwd motion */ + +/* Unit status flags */ + +#define UST_REV (OP_REV) /* last op was rev */ +#define UST_GAP 01 /* last op hit gap */ + +/* Status Register B, ^ = computed on the fly */ + +#define SRB_WLE 0400 /* "write lock err" */ +#define SRB_CRC 0200 /* CRC error */ +#define SRB_TIM 0100 /* timing error */ +#define SRB_BEOT 0040 /* ^BOT/EOT */ +#define SRB_EOF 0020 /* end of file */ +#define SRB_EMP 0010 /* ^drive empty */ +#define SRB_REW 0004 /* rewinding */ +#define SRB_WLK 0002 /* ^write locked */ +#define SRB_RDY 0001 /* ^ready */ +#define SRB_ALLERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_BEOT|SRB_EOF|SRB_EMP) +#define SRB_XFRERR (SRB_WLE|SRB_CRC|SRB_TIM|SRB_EOF) + +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; +extern FILE *sim_deb; + +uint32 ct_sra = 0; /* status reg A */ +uint32 ct_srb = 0; /* status reg B */ +uint32 ct_db = 0; /* data buffer */ +uint32 ct_df = 0; /* data flag */ +uint32 ct_write = 0; /* TU60 write flag */ +uint32 ct_bptr = 0; /* buf ptr */ +uint32 ct_blnt = 0; /* buf length */ +int32 ct_stime = 1000; /* start time */ +int32 ct_ctime = 100; /* char latency */ +uint32 ct_stopioe = 1; /* stop on error */ +uint8 *ct_xb = NULL; /* transfer buffer */ +static uint8 ct_fnc_tab[SRA_M_FNC + 1] = { + OP_FWD, 0 , OP_WRI|OP_FWD, OP_REV, + OP_WRI|OP_FWD, OP_REV, 0, OP_FWD + }; + +DEVICE ct_dev; +int32 ct70 (int32 IR, int32 AC); +t_stat ct_svc (UNIT *uptr); +t_stat ct_reset (DEVICE *dptr); +t_stat ct_attach (UNIT *uptr, char *cptr); +t_stat ct_detach (UNIT *uptr); +t_stat ct_boot (int32 unitno, DEVICE *dptr); +uint32 ct_updsta (UNIT *uptr); +int32 ct_go_start (int32 AC); +int32 ct_go_cont (UNIT *uptr, int32 AC); +t_stat ct_map_err (UNIT *uptr, t_stat st); +UNIT *ct_busy (void); +void ct_set_df (t_bool timchk); +t_bool ct_read_char (void); +uint32 ct_crc (uint8 *buf, uint32 cnt); + +/* CT data structures + + ct_dev CT device descriptor + ct_unit CT unit list + ct_reg CT register list + ct_mod CT modifier list +*/ + +DIB ct_dib = { DEV_CT, 1, { &ct70 } }; + +UNIT ct_unit[] = { + { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, + { UDATA (&ct_svc, UNIT_ATTABLE+UNIT_ROABLE, CT_SIZE) }, + }; + +REG ct_reg[] = { + { ORDATA (CTSRA, ct_sra, 8) }, + { ORDATA (CTSRB, ct_srb, 8) }, + { ORDATA (CTDB, ct_db, 8) }, + { FLDATA (CTDF, ct_df, 0) }, + { FLDATA (RDY, ct_srb, 0) }, + { FLDATA (WLE, ct_srb, 8) }, + { FLDATA (WRITE, ct_write, 0) }, + { FLDATA (INT, int_req, INT_V_CT) }, + { DRDATA (BPTR, ct_bptr, 17) }, + { DRDATA (BLNT, ct_blnt, 17) }, + { DRDATA (STIME, ct_stime, 24), PV_LEFT + REG_NZ }, + { DRDATA (CTIME, ct_ctime, 24), PV_LEFT + REG_NZ }, + { FLDATA (STOP_IOE, ct_stopioe, 0) }, + { URDATA (UFNC, ct_unit[0].FNC, 8, 4, 0, CT_NUMDR, 0), REG_HRO }, + { URDATA (UST, ct_unit[0].UST, 8, 2, 0, CT_NUMDR, 0), REG_HRO }, + { URDATA (POS, ct_unit[0].pos, 10, T_ADDR_W, 0, + CT_NUMDR, PV_LEFT | REG_RO) }, + { FLDATA (DEVNUM, ct_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB ct_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, +// { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", +// &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", NULL, + NULL, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE ct_dev = { + "CT", ct_unit, ct_reg, ct_mod, + CT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &ct_reset, + &ct_boot, &ct_attach, &ct_detach, + &ct_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG + }; + +/* IOT routines */ + +int32 ct70 (int32 IR, int32 AC) +{ +int32 srb; +UNIT *uptr; + +srb = ct_updsta (NULL); /* update status */ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* KCLR */ + ct_reset (&ct_dev); /* reset the world */ + break; + + case 1: /* KSDR */ + if (ct_df) AC |= IOT_SKP; + break; + + case 2: /* KSEN */ + if (srb & SRB_ALLERR) AC |= IOT_SKP; + break; + + case 3: /* KSBF */ + if ((srb & SRB_RDY) && !(srb & SRB_EMP)) + AC |= IOT_SKP; + break; + + case 4: /* KLSA */ + ct_sra = AC & 0377; + ct_updsta (NULL); + return ct_sra ^ 0377; + + case 5: /* KSAF */ + if (ct_df || (srb & (SRB_ALLERR|SRB_RDY))) + AC |= IOT_SKP; + break; + + case 6: /* KGOA */ + ct_df = 0; /* clear data flag */ + if (uptr = ct_busy ()) /* op in progress? */ + AC = ct_go_cont (uptr, AC); /* yes */ + else AC = ct_go_start (AC); /* no, start */ + ct_updsta (NULL); + break; + + case 7: /* KSRB */ + return srb & 0377; + } /* end switch */ + +return AC; +} + +/* Start a new operation - cassette is not busy */ + +int32 ct_go_start (int32 AC) +{ +UNIT *uptr = ct_dev.units + GET_UNIT (ct_sra); +uint32 fnc = GET_FNC (ct_sra); +uint32 flg = ct_fnc_tab[fnc]; +uint32 old_ust = uptr->UST; + +if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, + ">>CT start: op=%o, old_sta = %o, pos=%d\n", + fnc, uptr->UST, uptr->pos); +if ((ct_sra & SRA_ENAB) && (uptr->flags & UNIT_ATT)) { /* enabled, att? */ + ct_srb &= ~(SRB_XFRERR|SRB_REW); /* clear err, rew */ + if (flg & OP_WRI) { /* write-type op? */ + if (sim_tape_wrp (uptr)) { /* locked? */ + ct_srb |= SRB_WLE; /* set flag, abort */ + return AC; + } + ct_write = 1; /* set TU60 wr flag */ + ct_db = AC & 0377; + } + else { + ct_write = 0; + ct_db = 0; + } + ct_srb &= ~SRB_BEOT; /* tape in motion */ + if (fnc == SRA_REW) ct_srb |= SRB_REW; /* rew? set flag */ + if ((fnc != SRA_REW) && !(flg & OP_WRI)) { /* read cmd? */ + t_mtrlnt t; + t_stat st; + uptr->UST = flg & UST_REV; /* save direction */ + if (sim_tape_bot (uptr) && (flg & OP_FWD)) { /* spc/read fwd bot? */ + st = sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); /* skip file gap */ + if (st != MTSE_TMK) /* not there? */ + sim_tape_rewind (uptr); /* restore tap pos */ + else old_ust = 0; /* defang next */ + } + if ((old_ust ^ uptr->UST) == (UST_REV|UST_GAP)) { /* rev in gap? */ + if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, + ">>CT skip gap: op=%o, old_sta = %o, pos=%d\n", + fnc, uptr->UST, uptr->pos); + if (uptr->UST) /* skip file gap */ + sim_tape_rdrecr (uptr, ct_xb, &t, CT_MAXFR); + else sim_tape_rdrecf (uptr, ct_xb, &t, CT_MAXFR); + } + } + else uptr->UST = 0; + ct_bptr = 0; /* init buffer */ + ct_blnt = 0; + uptr->FNC = fnc; /* save function */ + sim_activate (uptr, ct_stime); /* schedule op */ + } +if ((fnc == SRA_READ) || (fnc == SRA_CRC)) /* read or CRC? */ + return 0; /* get "char" */ +return AC; +} + +/* Continue an in-progress operation - cassette is in motion */ + +int32 ct_go_cont (UNIT *uptr, int32 AC) +{ +int32 fnc = GET_FNC (ct_sra); + +switch (fnc) { /* case on function */ + + case SRA_READ: /* read */ + return ct_db; /* return data */ + + case SRA_WRITE: /* write */ + ct_db = AC & 0377; /* save data */ + break; + + case SRA_CRC: /* CRC */ + if ((uptr->FNC & SRA_M_FNC) != SRA_CRC) /* if not CRC */ + uptr->FNC = SRA_CRC; /* start CRC seq */ + if (!ct_write) return ct_db; /* read? AC <- buf */ + break; + + default: + break; + } + +return AC; +} + +/* Unit service */ + +t_stat ct_svc (UNIT *uptr) +{ +uint32 i, crc; +uint32 flgs = ct_fnc_tab[uptr->FNC & SRA_M_FNC]; +t_mtrlnt tbc; +t_stat st, r; + +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + ct_updsta (uptr); /* update status */ + return (ct_stopioe? SCPE_UNATT: SCPE_OK); + } +if (((flgs & OP_REV) && sim_tape_bot (uptr)) || /* rev at BOT or */ + ((flgs & OP_FWD) && sim_tape_eot (uptr))) { /* fwd at EOT? */ + ct_srb |= SRB_BEOT; /* error */ + ct_updsta (uptr); /* op done */ + return SCPE_OK; + } + +r = SCPE_OK; +switch (uptr->FNC) { /* case on function */ + + case SRA_READ: /* read start */ + st = sim_tape_rdrecf (uptr, ct_xb, &ct_blnt, CT_MAXFR); /* get rec */ + if (st == MTSE_RECE) ct_srb |= SRB_CRC; /* rec in err? */ + else if (st != MTSE_OK) { /* other error? */ + r = ct_map_err (uptr, st); /* map error */ + break; + } + crc = ct_crc (ct_xb, ct_blnt); /* calculate CRC */ + ct_xb[ct_blnt++] = (crc >> 8) & 0377; /* append to buffer */ + ct_xb[ct_blnt++] = crc & 0377; + uptr->FNC |= SRA_2ND; /* next state */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_READ|SRA_2ND: /* read char */ + if (!ct_read_char ()) break; /* read, overrun? */ + ct_set_df (TRUE); /* set data flag */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_WRITE: /* write start */ + for (i = 0; i < CT_MAXFR; i++) ct_xb[i] = 0; /* clear buffer */ + uptr->FNC |= SRA_2ND; /* next state */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_WRITE|SRA_2ND: /* write char */ + if ((ct_bptr < CT_MAXFR) && /* room in buf? */ + ((uptr->pos + ct_bptr) < uptr->capac)) /* room on tape? */ + ct_xb[ct_bptr++] = ct_db; /* store char */ + ct_set_df (TRUE); /* set data flag */ + sim_activate (uptr, ct_ctime); /* sched next char */ + return SCPE_OK; + + case SRA_CRC: /* CRC */ + if (ct_write) { /* write? */ + if (st = sim_tape_wrrecf (uptr, ct_xb, ct_bptr)) /* write, err? */ + r = ct_map_err (uptr, st); /* map error */ + break; /* write done */ + } + ct_read_char (); /* get second CRC */ + ct_set_df (FALSE); /* set df */ + uptr->FNC |= SRA_2ND; /* next state */ + sim_activate (uptr, ct_ctime); + return SCPE_OK; + + case SRA_CRC|SRA_2ND: /* second read CRC */ + if (ct_bptr != ct_blnt) { /* partial read? */ + crc = ct_crc (ct_xb, ct_bptr); /* actual CRC */ + if (crc != 0) ct_srb |= SRB_CRC; /* must be zero */ + } + break; /* read done */ + + case SRA_WFG: /* write file gap */ + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = ct_map_err (uptr, st); /* map error */ + break; + + case SRA_REW: /* rewind */ + sim_tape_rewind (uptr); + ct_srb |= SRB_BEOT; /* set BOT */ + break; + + case SRA_SRB: /* space rev blk */ + if (st = sim_tape_sprecr (uptr, &tbc)) /* space rev, err? */ + r = ct_map_err (uptr, st); /* map error */ + break; + + case SRA_SRF: /* space rev file */ + while ((st = sim_tape_sprecr (uptr, &tbc)) == MTSE_OK) ; + r = ct_map_err (uptr, st); /* map error */ + break; + + case SRA_SFF: /* space fwd file */ + while ((st = sim_tape_sprecf (uptr, &tbc)) == MTSE_OK) ; + r = ct_map_err (uptr, st); /* map error */ + break; + + default: /* never get here! */ + return SCPE_IERR; + } /* end case */ + +ct_updsta (uptr); /* update status */ +if (DEBUG_PRS (ct_dev)) fprintf (sim_deb, + ">>CT done: op=%o, statusA = %o, statusB = %o, pos=%d\n", + uptr->FNC, ct_sra, ct_srb, uptr->pos); +return r; +} + +/* Update controller status */ + +uint32 ct_updsta (UNIT *uptr) +{ +int32 srb; + +if (uptr == NULL) { /* unit specified? */ + uptr = ct_busy (); /* use busy unit */ + if ((uptr == NULL) && (ct_sra & SRA_ENAB)) /* none busy? */ + uptr = ct_dev.units + GET_UNIT (ct_sra); /* use sel unit */ + } +else if (ct_srb & SRB_EOF) uptr->UST |= UST_GAP; /* save gap */ +if (uptr) { /* any unit? */ + ct_srb &= ~(SRB_WLK|SRB_EMP|SRB_RDY); /* clear dyn flags */ + if ((uptr->flags & UNIT_ATT) == 0) /* unattached? */ + ct_srb = (ct_srb | SRB_EMP|SRB_WLK) & ~SRB_REW; /* empty, locked */ + if (!sim_is_active (uptr)) { /* not busy? */ + ct_srb = (ct_srb | SRB_RDY) & ~SRB_REW; /* ready, ~rew */ + } + if (sim_tape_wrp (uptr) || (ct_srb & SRB_REW)) /* locked or rew? */ + ct_srb |= SRB_WLK; /* set locked */ + } +if (ct_sra & SRA_ENAB) srb = ct_srb; /* can TA see TU60? */ +else srb = 0; /* no */ +if ((ct_sra & SRA_IE) && /* int enabled? */ + (ct_df || (srb & (SRB_ALLERR|SRB_RDY)))) /* any flag? */ + int_req |= INT_CT; /* set int req */ +else int_req &= ~INT_CT; /* no, clr int req */ +return srb; +} + +/* Set data flag */ + +void ct_set_df (t_bool timchk) +{ +if (ct_df && timchk) ct_srb |= SRB_TIM; /* flag still set? */ +ct_df = 1; /* set data flag */ +if (ct_sra & SRA_IE) int_req |= INT_CT; /* if ie, int req */ +return; +} + +/* Read character */ + +t_bool ct_read_char (void) +{ +if (ct_bptr < ct_blnt) { /* more chars? */ + ct_db = ct_xb[ct_bptr++]; + return TRUE; + } +ct_db = 0; +ct_srb |= SRB_CRC; /* overrun */ +return FALSE; +} + +/* Test if controller busy */ + +UNIT *ct_busy (void) +{ +uint32 u; +UNIT *uptr; + +for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ + uptr = ct_dev.units + u; + if (sim_is_active (uptr)) return uptr; + } +return NULL; +} + +/* Calculate CRC on buffer */ + +uint32 ct_crc (uint8 *buf, uint32 cnt) +{ +uint32 crc, i, j; + +crc = 0; +for (i = 0; i < cnt; i++) { + crc = crc ^ (((uint32) buf[i]) << 8); + for (j = 0; j < 8; j++) { + if (crc & 1) crc = (crc >> 1) ^ 0xA001; + else crc = crc >> 1; + } + } +return crc; +} + +/* Map error status */ + +t_stat ct_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* unattached */ + ct_srb |= SRB_CRC; + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_TMK: /* end of file */ + ct_srb |= SRB_EOF; + break; + + case MTSE_IOERR: /* IO error */ + ct_srb |= SRB_CRC; /* set crc err */ + if (ct_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + ct_srb |= SRB_CRC; /* set crc err */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + ct_srb |= SRB_CRC; /* set crc err */ + break; + + case MTSE_BOT: /* reverse into BOT */ + ct_srb |= SRB_BEOT; /* set BOT */ + break; + + case MTSE_WRP: /* write protect */ + ct_srb |= SRB_WLE; /* set wlk err */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ct_reset (DEVICE *dptr) +{ +uint32 u; +UNIT *uptr; + +ct_sra = 0; +ct_srb = 0; +ct_df = 0; +ct_db = 0; +ct_write = 0; +ct_bptr = 0; +ct_blnt = 0; +int_req = int_req & ~INT_CT; /* clear interrupt */ +for (u = 0; u < CT_NUMDR; u++) { /* loop thru units */ + uptr = ct_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + sim_tape_reset (uptr); /* reset tape */ + } +if (ct_xb == NULL) ct_xb = (uint8 *) calloc (CT_MAXFR + 2, sizeof (uint8)); +if (ct_xb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat ct_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +ct_updsta (NULL); +uptr->UST = 0; +return r; +} + +/* Detach routine */ + +t_stat ct_detach (UNIT* uptr) +{ +t_stat r; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* check attached */ +r = sim_tape_detach (uptr); +ct_updsta (NULL); +uptr->UST = 0; +return r; +} + +/* Bootstrap routine */ + +#define BOOT_START 04000 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 01237, /* BOOT, TAD M50 /change CRC to REW */ + 01206, /* CRCCHK, TAD L260 /crc op */ + 06704, /* KLSA /load op */ + 06706, /* KGOA /start */ + 06703, /* KSBF /ready? */ + 05204, /* RDCOD, JMP .-1 /loop */ + 07264, /* L260, CML STA RAL /L = 1, AC = halt */ + 06702, /* KSEN /error? */ + 07610, /* SKP CLA /halt on any error */ + 03211, /* DCA . /except REW or FFG */ + 03636, /* DCA I PTR /TAD I PTR mustn't change L */ + 01205, /* TAD RDCOD /read op */ + 06704, /* KLSA /load op */ + 06706, /* KGOA /start */ + 06701, /* LOOP, KSDF /data ready? */ + 05216, /* JMP .-1 /loop */ + 07002, /* BSW /to upper 6b */ + 07430, /* SZL /second byte? */ + 01636, /* TAD I PTR /yes */ + 07022, /* CML BSW /swap back */ + 03636, /* DCA I PTR /store in mem */ + 07420, /* SNL /done with both bytes? */ + 02236, /* ISZ PTR /yes, bump mem ptr */ + 02235, /* ISZ KNT /done with record? */ + 05215, /* JMP LOOP /next byte */ + 07346, /* STA CLL RTL */ + 07002, /* BSW /AC = 7757 */ + 03235, /* STA KNT /now read 200 byte record */ + 05201, /* JMP CRCCHK /go check CRC */ + 07737, /* KNT, 7737 /1's compl of byte count */ + 03557, /* PTR, 3557 /load point */ + 07730, /* M50, 7730 /CLA SPA SZL */ + }; + +t_stat ct_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 M[]; + +if ((ct_dib.dev != DEV_CT) || unitno) /* only std devno */ + return STOP_NOTSTD; +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/PDP8/pdp8_defs.h b/PDP8/pdp8_defs.h new file mode 100644 index 0000000..0a1967c --- /dev/null +++ b/PDP8/pdp8_defs.h @@ -0,0 +1,207 @@ +/* pdp8_defs.h: PDP-8 simulator definitions + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 21-Aug-07 RMS Added FPP8 support + 13-Dec-06 RMS Added TA8E support + 30-Oct-06 RMS Added infinite loop stop + 13-Oct-03 RMS Added TSC8-75 support + 04-Oct-02 RMS Added variable device number support + 20-Jan-02 RMS Fixed bug in TTx interrupt enable initialization + 25-Nov-01 RMS Added RL8A support + 16-Sep-01 RMS Added multiple KL support + 18-Mar-01 RMS Added DF32 support + 15-Feb-01 RMS Added DECtape support + 14-Apr-99 RMS Changed t_addr to unsigned + 19-Mar-95 RMS Added dynamic memory size + 02-May-94 RMS Added non-existent memory handling + + The author gratefully acknowledges the help of Max Burnet, Richie Lary, + and Bill Haygood in resolving questions about the PDP-8 +*/ + +#ifndef _PDP8_DEFS_H_ +#define _PDP8_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_NOTSTD 4 /* non-std devno */ +#define STOP_DTOFF 5 /* DECtape off reel */ +#define STOP_LOOP 6 /* infinite loop */ + +/* Memory */ + +#define MAXMEMSIZE 32768 /* max memory size */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define ADDRMASK (MAXMEMSIZE - 1) /* address mask */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) + +/* IOT subroutine return codes */ + +#define IOT_V_SKP 12 /* skip */ +#define IOT_V_REASON 13 /* reason */ +#define IOT_SKP (1 << IOT_V_SKP) +#define IOT_REASON (1 << IOT_V_REASON) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* stop on error */ + +/* Timers */ + +#define TMR_CLK 0 /* timer 0 = clock */ +#define TMR_TTX 1 /* timer 1 = TTx */ + +/* Device information block */ + +#define DEV_MAXBLK 8 /* max dev block */ +#define DEV_MAX 64 /* total devices */ + +typedef struct { + uint32 dev; /* base dev number */ + uint32 num; /* number of slots */ + int32 (*dsp[DEV_MAXBLK])(int32 IR, int32 dat); + } DIB; + +/* Standard device numbers */ + +#define DEV_PTR 001 /* paper tape reader */ +#define DEV_PTP 002 /* paper tape punch */ +#define DEV_TTI 003 /* console input */ +#define DEV_TTO 004 /* console output */ +#define DEV_CLK 013 /* clock */ +#define DEV_TSC 036 +#define DEV_KJ8 040 /* extra terminals */ +#define DEV_FPP 055 /* floating point */ +#define DEV_DF 060 /* DF32 */ +#define DEV_RF 060 /* RF08 */ +#define DEV_RL 060 /* RL8A */ +#define DEV_LPT 066 /* line printer */ +#define DEV_MT 070 /* TM8E */ +#define DEV_CT 070 /* TA8E */ +#define DEV_RK 074 /* RK8E */ +#define DEV_RX 075 /* RX8E/RX28 */ +#define DEV_DTA 076 /* TC08 */ +#define DEV_TD8E 077 /* TD8E */ + +/* Interrupt flags + + The interrupt flags consist of three groups: + + 1. Devices with individual interrupt enables. These record + their interrupt requests in device_done and their enables + in device_enable, and must occupy the low bit positions. + + 2. Devices without interrupt enables. These record their + interrupt requests directly in int_req, and must occupy + the middle bit positions. + + 3. Overhead. These exist only in int_req and must occupy the + high bit positions. + + Because the PDP-8 does not have priority interrupts, the order + of devices within groups does not matter. + + Note: all extra KL input and output interrupts must be assigned + to contiguous bits. +*/ + +#define INT_V_START 0 /* enable start */ +#define INT_V_LPT (INT_V_START+0) /* line printer */ +#define INT_V_PTP (INT_V_START+1) /* tape punch */ +#define INT_V_PTR (INT_V_START+2) /* tape reader */ +#define INT_V_TTO (INT_V_START+3) /* terminal */ +#define INT_V_TTI (INT_V_START+4) /* keyboard */ +#define INT_V_CLK (INT_V_START+5) /* clock */ +#define INT_V_TTO1 (INT_V_START+6) /* tto1 */ +#define INT_V_TTO2 (INT_V_START+7) /* tto2 */ +#define INT_V_TTO3 (INT_V_START+8) /* tto3 */ +#define INT_V_TTO4 (INT_V_START+9) /* tto4 */ +#define INT_V_TTI1 (INT_V_START+10) /* tti1 */ +#define INT_V_TTI2 (INT_V_START+11) /* tti2 */ +#define INT_V_TTI3 (INT_V_START+12) /* tti3 */ +#define INT_V_TTI4 (INT_V_START+13) /* tti4 */ +#define INT_V_DIRECT (INT_V_START+14) /* direct start */ +#define INT_V_RX (INT_V_DIRECT+0) /* RX8E */ +#define INT_V_RK (INT_V_DIRECT+1) /* RK8E */ +#define INT_V_RF (INT_V_DIRECT+2) /* RF08 */ +#define INT_V_DF (INT_V_DIRECT+3) /* DF32 */ +#define INT_V_MT (INT_V_DIRECT+4) /* TM8E */ +#define INT_V_DTA (INT_V_DIRECT+5) /* TC08 */ +#define INT_V_RL (INT_V_DIRECT+6) /* RL8A */ +#define INT_V_CT (INT_V_DIRECT+7) /* TA8E int */ +#define INT_V_PWR (INT_V_DIRECT+8) /* power int */ +#define INT_V_UF (INT_V_DIRECT+9) /* user int */ +#define INT_V_TSC (INT_V_DIRECT+10) /* TSC8-75 int */ +#define INT_V_FPP (INT_V_DIRECT+11) /* FPP8 */ +#define INT_V_OVHD (INT_V_DIRECT+12) /* overhead start */ +#define INT_V_NO_ION_PENDING (INT_V_OVHD+0) /* ion pending */ +#define INT_V_NO_CIF_PENDING (INT_V_OVHD+1) /* cif pending */ +#define INT_V_ION (INT_V_OVHD+2) /* interrupts on */ + +#define INT_LPT (1 << INT_V_LPT) +#define INT_PTP (1 << INT_V_PTP) +#define INT_PTR (1 << INT_V_PTR) +#define INT_TTO (1 << INT_V_TTO) +#define INT_TTI (1 << INT_V_TTI) +#define INT_CLK (1 << INT_V_CLK) +#define INT_TTO1 (1 << INT_V_TTO1) +#define INT_TTO2 (1 << INT_V_TTO2) +#define INT_TTO3 (1 << INT_V_TTO3) +#define INT_TTO4 (1 << INT_V_TTO4) +#define INT_TTI1 (1 << INT_V_TTI1) +#define INT_TTI2 (1 << INT_V_TTI2) +#define INT_TTI3 (1 << INT_V_TTI3) +#define INT_TTI4 (1 << INT_V_TTI4) +#define INT_RX (1 << INT_V_RX) +#define INT_RK (1 << INT_V_RK) +#define INT_RF (1 << INT_V_RF) +#define INT_DF (1 << INT_V_DF) +#define INT_MT (1 << INT_V_MT) +#define INT_DTA (1 << INT_V_DTA) +#define INT_RL (1 << INT_V_RL) +#define INT_CT (1 << INT_V_CT) +#define INT_PWR (1 << INT_V_PWR) +#define INT_UF (1 << INT_V_UF) +#define INT_TSC (1 << INT_V_TSC) +#define INT_FPP (1 << INT_V_FPP) +#define INT_NO_ION_PENDING (1 << INT_V_NO_ION_PENDING) +#define INT_NO_CIF_PENDING (1 << INT_V_NO_CIF_PENDING) +#define INT_ION (1 << INT_V_ION) +#define INT_DEV_ENABLE ((1 << INT_V_DIRECT) - 1) /* devices w/enables */ +#define INT_ALL ((1 << INT_V_OVHD) - 1) /* all interrupts */ +#define INT_INIT_ENABLE (INT_TTI+INT_TTO+INT_PTR+INT_PTP+INT_LPT) | \ + (INT_TTI1+INT_TTI2+INT_TTI3+INT_TTI4) | \ + (INT_TTO1+INT_TTO2+INT_TTO3+INT_TTO4) +#define INT_PENDING (INT_ION+INT_NO_CIF_PENDING+INT_NO_ION_PENDING) +#define INT_UPDATE ((int_req & ~INT_DEV_ENABLE) | (dev_done & int_enable)) + +/* Function prototypes */ + +t_stat set_dev (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_dev (FILE *st, UNIT *uptr, int32 val, void *desc); + +#endif diff --git a/PDP8/pdp8_df.c b/PDP8/pdp8_df.c new file mode 100644 index 0000000..b28d450 --- /dev/null +++ b/PDP8/pdp8_df.c @@ -0,0 +1,373 @@ +/* pdp8_df.c: DF32 fixed head disk simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + df DF32 fixed head disk + + 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) + 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) + 04-Jan-04 RMS Changed sim_fsize calling sequence + 26-Oct-03 RMS Cleaned up buffer copy code + 26-Jul-03 RMS Fixed bug in set size routine + 14-Mar-03 RMS Fixed variable platter interaction with save/restore + 03-Mar-03 RMS Fixed autosizing + 02-Feb-03 RMS Added variable platter and autosizing support + 04-Oct-02 RMS Added DIBs, device number support + 28-Nov-01 RMS Added RL8A support + 25-Apr-01 RMS Added device enable/disable support + + The DF32 is a head-per-track disk. It uses the three cycle data break + facility. To minimize overhead, the entire DF32 is buffered in memory. + + Two timing parameters are provided: + + df_time Interword timing, must be non-zero + df_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst +*/ + +#include "pdp8_defs.h" +#include + +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ +#define UNIT_M_PLAT 03 +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) +#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) + +/* Constants */ + +#define DF_NUMWD 2048 /* words/track */ +#define DF_NUMTR 16 /* tracks/disk */ +#define DF_DKSIZE (DF_NUMTR * DF_NUMWD) /* words/disk */ +#define DF_NUMDK 4 /* disks/controller */ +#define DF_WC 07750 /* word count */ +#define DF_MA 07751 /* mem address */ +#define DF_WMASK (DF_NUMWD - 1) /* word mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define DF_READ 2 /* read */ +#define DF_WRITE 4 /* write */ + +/* Status register */ + +#define DFS_PCA 04000 /* photocell status */ +#define DFS_DEX 03700 /* disk addr extension */ +#define DFS_MEX 00070 /* mem addr extension */ +#define DFS_DRL 00004 /* data late error */ +#define DFS_WLS 00002 /* write lock error */ +#define DFS_NXD 00002 /* non-existent disk */ +#define DFS_PER 00001 /* parity error */ +#define DFS_ERR (DFS_DRL | DFS_WLS | DFS_PER) +#define DFS_V_DEX 6 +#define DFS_V_MEX 3 + +#define GET_MEX(x) (((x) & DFS_MEX) << (12 - DFS_V_MEX)) +#define GET_DEX(x) (((x) & DFS_DEX) << (12 - DFS_V_DEX)) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) DF_NUMWD))) +#define UPDATE_PCELL if (GET_POS (df_time) < 6) df_sta = df_sta | DFS_PCA; \ + else df_sta = df_sta & ~DFS_PCA + +extern uint16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +int32 df_sta = 0; /* status register */ +int32 df_da = 0; /* disk address */ +int32 df_done = 0; /* done flag */ +int32 df_wlk = 0; /* write lock */ +int32 df_time = 10; /* inter-word time */ +int32 df_burst = 1; /* burst mode flag */ +int32 df_stopioe = 1; /* stop on error */ + +DEVICE df_dev; +int32 df60 (int32 IR, int32 AC); +int32 df61 (int32 IR, int32 AC); +int32 df62 (int32 IR, int32 AC); +t_stat df_svc (UNIT *uptr); +t_stat pcell_svc (UNIT *uptr); +t_stat df_reset (DEVICE *dptr); +t_stat df_boot (int32 unitno, DEVICE *dptr); +t_stat df_attach (UNIT *uptr, char *cptr); +t_stat df_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* DF32 data structures + + df_dev RF device descriptor + df_unit RF unit descriptor + pcell_unit photocell timing unit (orphan) + df_reg RF register list +*/ + +DIB df_dib = { DEV_DF, 3, { &df60, &df61, &df62 } }; + +UNIT df_unit = { + UDATA (&df_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DF_DKSIZE) + }; + +REG df_reg[] = { + { ORDATA (STA, df_sta, 12) }, + { ORDATA (DA, df_da, 12) }, + { ORDATA (WC, M[DF_WC], 12), REG_FIT }, + { ORDATA (MA, M[DF_MA], 12), REG_FIT }, + { FLDATA (DONE, df_done, 0) }, + { FLDATA (INT, int_req, INT_V_DF) }, + { ORDATA (WLS, df_wlk, 8) }, + { DRDATA (TIME, df_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (BURST, df_burst, 0) }, + { FLDATA (STOP_IOE, df_stopioe, 0) }, + { DRDATA (CAPAC, df_unit.capac, 18), REG_HRO }, + { ORDATA (DEVNUM, df_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB df_mod[] = { + { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &df_set_size }, + { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &df_set_size }, + { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &df_set_size }, + { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &df_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE df_dev = { + "DF", &df_unit, df_reg, df_mod, + 1, 8, 17, 1, 8, 12, + NULL, NULL, &df_reset, + &df_boot, &df_attach, NULL, + &df_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 df60 (int32 IR, int32 AC) +{ +int32 t; +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DCMA */ + df_da = 0; /* clear disk addr */ + df_done = 0; /* clear done */ + df_sta = df_sta & ~DFS_ERR; /* clear errors */ + int_req = int_req & ~INT_DF; /* clear int req */ + } +if (pulse & 6) { /* DMAR, DMAW */ + df_da = df_da | AC; /* disk addr |= AC */ + df_unit.FUNC = pulse & ~1; /* save function */ + t = (df_da & DF_WMASK) - GET_POS (df_time); /* delta to new loc */ + if (t < 0) t = t + DF_NUMWD; /* wrap around? */ + sim_activate (&df_unit, t * df_time); /* schedule op */ + AC = 0; /* clear AC */ + } +return AC; +} + +/* Based on the hardware implementation. DEAL and DEAC work as follows: + + 6615 pulse 1 = clear df_sta + pulse 4 = df_sta = df_sta | AC + AC = AC | old_df_sta + 6616 pulse 2 = clear AC, skip if address confirmed + pulse 4 = df_sta = df_sta | AC = 0 (nop) + AC = AC | old_df_sta +*/ + +int32 df61 (int32 IR, int32 AC) +{ +int32 old_df_sta = df_sta; +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) /* DCEA */ + df_sta = df_sta & ~(DFS_DEX | DFS_MEX); /* clear dex, mex */ +if (pulse & 2) /* DSAC */ + AC = ((df_da & DF_WMASK) == GET_POS (df_time))? IOT_SKP: 0; +if (pulse & 4) { + df_sta = df_sta | (AC & (DFS_DEX | DFS_MEX)); /* DEAL */ + AC = AC | old_df_sta; /* DEAC */ + } +return AC; +} + +int32 df62 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DFSE */ + if ((df_sta & DFS_ERR) == 0) AC = AC | IOT_SKP; + } +if (pulse & 2) { /* DFSC */ + if (pulse & 4) AC = AC & ~07777; /* for DMAC */ + else if (df_done) AC = AC | IOT_SKP; + } +if (pulse & 4) AC = AC | df_da; /* DMAC */ +return AC; +} + +/* Unit service + + Note that for reads and writes, memory addresses wrap around in the + current field. This code assumes the entire disk is buffered. +*/ + +t_stat df_svc (UNIT *uptr) +{ +int32 pa, t, mex; +uint32 da; +int16 *fbuf = uptr->filebuf; + +UPDATE_PCELL; /* update photocell */ +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + df_done = 1; + int_req = int_req | INT_DF; /* update int req */ + return IORETURN (df_stopioe, SCPE_UNATT); + } + +mex = GET_MEX (df_sta); +da = GET_DEX (df_sta) | df_da; /* form disk addr */ +do { + if (da >= uptr->capac) { /* nx disk addr? */ + df_sta = df_sta | DFS_NXD; + break; + } + M[DF_WC] = (M[DF_WC] + 1) & 07777; /* incr word count */ + M[DF_MA] = (M[DF_MA] + 1) & 07777; /* incr mem addr */ + pa = mex | M[DF_MA]; /* add extension */ + if (uptr->FUNC == DF_READ) { /* read? */ + if (MEM_ADDR_OK (pa)) M[pa] = fbuf[da]; /* if !nxm, read wd */ + } + else { /* write */ + t = (da >> 14) & 07; /* check wr lock */ + if ((df_wlk >> t) & 1) /* locked? set err */ + df_sta = df_sta | DFS_WLS; + else { /* not locked */ + fbuf[da] = M[pa]; /* write word */ + if (da >= uptr->hwmark) uptr->hwmark = da + 1; + } + } + da = (da + 1) & 0377777; /* incr disk addr */ + } while ((M[DF_WC] != 0) && (df_burst != 0)); /* brk if wc, no brst */ + +if ((M[DF_WC] != 0) && ((df_sta & DFS_ERR) == 0)) /* more to do? */ + sim_activate (&df_unit, df_time); /* sched next */ +else { + if (uptr->FUNC != DF_READ) da = (da - 1) & 0377777; + df_done = 1; /* done */ + int_req = int_req | INT_DF; /* update int req */ + } +df_sta = (df_sta & ~DFS_DEX) | ((da >> (12 - DFS_V_DEX)) & DFS_DEX); +df_da = da & 07777; /* separate disk addr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat df_reset (DEVICE *dptr) +{ +df_sta = df_da = 0; +df_done = 1; +int_req = int_req & ~INT_DF; /* clear interrupt */ +sim_cancel (&df_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define OS8_START 07750 +#define OS8_LEN (sizeof (os8_rom) / sizeof (int16)) +#define DM4_START 00200 +#define DM4_LEN (sizeof (dm4_rom) / sizeof (int16)) + +static const uint16 os8_rom[] = { + 07600, /* 7750, CLA CLL ; also word count */ + 06603, /* 7751, DMAR ; also address */ + 06622, /* 7752, DFSC ; done? */ + 05352, /* 7753, JMP .-1 ; no */ + 05752 /* 7754, JMP @.-2 ; enter boot */ + }; + +static const uint16 dm4_rom[] = { + 00200, 07600, /* 0200, CLA CLL */ + 00201, 06603, /* 0201, DMAR ; read */ + 00202, 06622, /* 0202, DFSC ; done? */ + 00203, 05202, /* 0203, JMP .-1 ; no */ + 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ + 07750, 07576, /* 7750, 7576 ; word count */ + 07751, 07576 /* 7751, 7576 ; address */ + }; + +t_stat df_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 sim_switches, saved_PC; + +if (sim_switches & SWMASK ('D')) { + for (i = 0; i < DM4_LEN; i = i + 2) + M[dm4_rom[i]] = dm4_rom[i + 1]; + saved_PC = DM4_START; + } +else { + for (i = 0; i < OS8_LEN; i++) + M[OS8_START + i] = os8_rom[i]; + saved_PC = OS8_START; + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat df_attach (UNIT *uptr, char *cptr) +{ +uint32 p, sz; +uint32 ds_bytes = DF_DKSIZE * sizeof (int16); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= DF_NUMDK) p = DF_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | + (p << UNIT_V_PLAT); + } +uptr->capac = UNIT_GETP (uptr->flags) * DF_DKSIZE; +return attach_unit (uptr, cptr); +} + +/* Change disk size */ + +t_stat df_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val < 0) return SCPE_IERR; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = UNIT_GETP (val) * DF_DKSIZE; +uptr->flags = uptr->flags & ~UNIT_AUTO; +return SCPE_OK; +} diff --git a/PDP8/pdp8_dt.c b/PDP8/pdp8_dt.c new file mode 100644 index 0000000..398d35b --- /dev/null +++ b/PDP8/pdp8_dt.c @@ -0,0 +1,1292 @@ +/* pdp8_dt.c: PDP-8 DECtape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dt TC08/TU56 DECtape + + 23-Jun-06 RMS Fixed switch conflict in ATTACH + 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 25-Jan-04 RMS Revised for device debug support + 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR + 18-Oct-03 RMS Fixed bugs in read all, tightened timing + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed sizing interaction with save/restore + 17-Oct-02 RMS Fixed bug in end of reel logic + 04-Oct-02 RMS Added DIB, device number support + 12-Sep-02 RMS Added support for 16b format + 30-May-02 RMS Widened POS to 32b + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Changed POS, STATT, LASTT, FLG to arrays + 29-Aug-01 RMS Added casts to PDP-18b packup routine + 17-Jul-01 RMS Moved function prototype + 11-May-01 RMS Fixed bug in reset + 25-Apr-01 RMS Added device enable/disable support + 18-Apr-01 RMS Changed to rewind tape before boot + 19-Mar-01 RMS Changed bootstrap to support 4k disk monitor + 15-Mar-01 RMS Added 129th word to PDP-8 format + + PDP-8 DECtapes are represented in memory by fixed length buffer of 16b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 18/36bb DECtape file is read in, it is converted to 12b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the TD8E formatter) is: + + reverse end zone 8192 reverse end zone codes ~ 10 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 8192 forward end zone codes ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of read all and write all. Read all assumes that the tape has been + conventionally written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write all writes only the data words and dumps the non-data words in the + bit bucket. +*/ + +#include "pdp8_defs.h" + +#define DT_NUMDR 8 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define DT_WC 07754 /* word count */ +#define DT_CA 07755 /* current addr */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_BLKWD 1 /* blk no word in h/t */ +#define DT_CSMWD 4 /* checksum word in h/t */ +#define DT_HTWRD 5 /* header/trailer words */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* buffer length */ +#define DT_BLKLN (DT_BLKWD * DT_LPERMC) /* blk no line in h/t */ +#define DT_CSMLN (DT_CSMWD * DT_LPERMC) /* csum line in h/t */ +#define DT_HTLIN (DT_HTWRD * DT_LPERMC) /* header/trailer lines */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word size in lines */ +#define D18_BSIZE 384 /* block size in 12b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ + +#define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) +#define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) +#define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 129 /* block size in 12b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ +#define D8_FILSIZ (D8_CAPAC * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D8_CAPAC /* default */ +#define DT_WSIZE D8_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) +#define DT_LIN2WD(p,u) ((DT_LIN2OF (p,u) - DT_HTLIN) / DT_WSIZE) +#define DT_BLK2LN(p,u) (((p) * DTU_LPERB (u)) + DT_EZLIN) +#define DT_QREZ(u) (((u)->pos) < DT_EZLIN) +#define DT_QFEZ(u) (((u)->pos) >= ((uint32) DTU_FWDEZ (u))) +#define DT_QEZ(u) (DT_QREZ (u) || DT_QFEZ (u)) + +/* Status register A */ + +#define DTA_V_UNIT 9 /* unit select */ +#define DTA_M_UNIT 07 +#define DTA_UNIT (DTA_M_UNIT << DTA_V_UNIT) +#define DTA_V_MOT 7 /* motion */ +#define DTA_M_MOT 03 +#define DTA_V_MODE 6 /* mode */ +#define DTA_V_FNC 3 /* function */ +#define DTA_M_FNC 07 +#define FNC_MOVE 00 /* move */ +#define FNC_SRCH 01 /* search */ +#define FNC_READ 02 /* read */ +#define FNC_RALL 03 /* read all */ +#define FNC_WRIT 04 /* write */ +#define FNC_WALL 05 /* write all */ +#define FNC_WMRK 06 /* write timing */ +#define DTA_V_ENB 2 /* int enable */ +#define DTA_V_CERF 1 /* clr error flag */ +#define DTA_V_CDTF 0 /* clr DECtape flag */ +#define DTA_FWDRV (1u << (DTA_V_MOT + 1)) +#define DTA_STSTP (1u << DTA_V_MOT) +#define DTA_MODE (1u << DTA_V_MODE) +#define DTA_ENB (1u << DTA_V_ENB) +#define DTA_CERF (1u << DTA_V_CERF) +#define DTA_CDTF (1u << DTA_V_CDTF) +#define DTA_RW (07777 & ~(DTA_CERF | DTA_CDTF)) + +#define DTA_GETUNIT(x) (((x) >> DTA_V_UNIT) & DTA_M_UNIT) +#define DTA_GETMOT(x) (((x) >> DTA_V_MOT) & DTA_M_MOT) +#define DTA_GETFNC(x) (((x) >> DTA_V_FNC) & DTA_M_FNC) + +/* Status register B */ + +#define DTB_V_ERF 11 /* error flag */ +#define DTB_V_MRK 10 /* mark trk err */ +#define DTB_V_END 9 /* end zone err */ +#define DTB_V_SEL 8 /* select err */ +#define DTB_V_PAR 7 /* parity err */ +#define DTB_V_TIM 6 /* timing err */ +#define DTB_V_MEX 3 /* memory extension */ +#define DTB_M_MEX 07 +#define DTB_MEX (DTB_M_MEX << DTB_V_MEX) +#define DTB_V_DTF 0 /* DECtape flag */ +#define DTB_ERF (1u << DTB_V_ERF) +#define DTB_MRK (1u << DTB_V_MRK) +#define DTB_END (1u << DTB_V_END) +#define DTB_SEL (1u << DTB_V_SEL) +#define DTB_PAR (1u << DTB_V_PAR) +#define DTB_TIM (1u << DTB_V_TIM) +#define DTB_DTF (1u << DTB_V_DTF) +#define DTB_ALLERR (DTB_ERF | DTB_MRK | DTB_END | DTB_SEL | \ + DTB_PAR | DTB_TIM) +#define DTB_GETMEX(x) (((x) & DTB_MEX) << (12 - DTB_V_MEX)) + +/* DECtape state */ + +#define DTS_V_MOT 3 /* motion */ +#define DTS_M_MOT 07 +#define DTS_STOP 0 /* stopped */ +#define DTS_DECF 2 /* decel, fwd */ +#define DTS_DECR 3 /* decel, rev */ +#define DTS_ACCF 4 /* accel, fwd */ +#define DTS_ACCR 5 /* accel, rev */ +#define DTS_ATSF 6 /* @speed, fwd */ +#define DTS_ATSR 7 /* @speed, rev */ +#define DTS_DIR 01 /* dir mask */ +#define DTS_V_FNC 0 /* function */ +#define DTS_M_FNC 07 +#define DTS_OFR 7 /* "off reel" */ +#define DTS_GETMOT(x) (((x) >> DTS_V_MOT) & DTS_M_MOT) +#define DTS_GETFNC(x) (((x) >> DTS_V_FNC) & DTS_M_FNC) +#define DTS_V_2ND 6 /* next state */ +#define DTS_V_3RD (DTS_V_2ND + DTS_V_2ND) /* next next */ +#define DTS_STA(y,z) (((y) << DTS_V_MOT) | ((z) << DTS_V_FNC)) +#define DTS_SETSTA(y,z) uptr->STATE = DTS_STA (y, z) +#define DTS_SET2ND(y,z) uptr->STATE = (uptr->STATE & 077) | \ + ((DTS_STA (y, z)) << DTS_V_2ND) +#define DTS_SET3RD(y,z) uptr->STATE = (uptr->STATE & 07777) | \ + ((DTS_STA (y, z)) << DTS_V_3RD) +#define DTS_NXTSTA(x) (x >> DTS_V_2ND) + +/* Operation substates */ + +#define DTO_WCO 1 /* wc overflow */ +#define DTO_SOB 2 /* start of block */ + +/* Logging */ + +#define LOG_MS 001 /* move, search */ +#define LOG_RW 002 /* read, write */ +#define LOG_BL 004 /* block # lblk */ + +#define DT_UPDINT if ((dtsa & DTA_ENB) && (dtsb & (DTB_ERF | DTB_DTF))) \ + int_req = int_req | INT_DTA; \ + else int_req = int_req & ~INT_DTA; +#define ABS(x) (((x) < 0)? (-(x)): (x)) + +extern uint16 M[]; +extern int32 int_req; +extern UNIT cpu_unit; +extern int32 sim_switches; +extern FILE *sim_deb; + +int32 dtsa = 0; /* status A */ +int32 dtsb = 0; /* status B */ +int32 dt_ltime = 12; /* interline time */ +int32 dt_dctime = 40000; /* decel time */ +int32 dt_substate = 0; +int32 dt_logblk = 0; +int32 dt_stopoffr = 0; + +DEVICE dt_dev; +int32 dt76 (int32 IR, int32 AC); +int32 dt77 (int32 IR, int32 AC); +t_stat dt_svc (UNIT *uptr); +t_stat dt_reset (DEVICE *dptr); +t_stat dt_attach (UNIT *uptr, char *cptr); +t_stat dt_detach (UNIT *uptr); +t_stat dt_boot (int32 unitno, DEVICE *dptr); +void dt_deselect (int32 oldf); +void dt_newsa (int32 newf); +void dt_newfnc (UNIT *uptr, int32 newsta); +t_bool dt_setpos (UNIT *uptr); +void dt_schedez (UNIT *uptr, int32 dir); +void dt_seterr (UNIT *uptr, int32 e); +int32 dt_comobv (int32 val); +int32 dt_csum (UNIT *uptr, int32 blk); +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir); +extern int32 sim_is_running; + +/* DT data structures + + dt_dev DT device descriptor + dt_unit DT unit list + dt_reg DT register list + dt_mod DT modifier list +*/ + +DIB dt_dib = { DEV_DTA, 2, { &dt76, &dt77 } }; + +UNIT dt_unit[] = { + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&dt_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } + }; + +REG dt_reg[] = { + { ORDATA (DTSA, dtsa, 12) }, + { ORDATA (DTSB, dtsb, 12) }, + { FLDATA (INT, int_req, INT_V_DTA) }, + { FLDATA (ENB, dtsa, DTA_V_ENB) }, + { FLDATA (DTF, dtsb, DTB_V_DTF) }, + { FLDATA (ERF, dtsb, DTB_V_ERF) }, + { ORDATA (WC, M[DT_WC], 12), REG_FIT }, + { ORDATA (CA, M[DT_CA], 12), REG_FIT }, + { DRDATA (LTIME, dt_ltime, 24), REG_NZ | PV_LEFT }, + { DRDATA (DCTIME, dt_dctime, 24), REG_NZ | PV_LEFT }, + { ORDATA (SUBSTATE, dt_substate, 2) }, + { DRDATA (LBLK, dt_logblk, 12), REG_HIDDEN }, + { URDATA (POS, dt_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO) }, + { URDATA (STATT, dt_unit[0].STATE, 8, 18, 0, + DT_NUMDR, REG_RO) }, + { URDATA (LASTT, dt_unit[0].LASTT, 10, 32, 0, + DT_NUMDR, REG_HRO) }, + { FLDATA (STOP_OFFR, dt_stopoffr, 0) }, + { ORDATA (DEVNUM, dt_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB dt_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEBTAB dt_deb[] = { + { "MOTION", LOG_MS }, + { "DATA", LOG_RW }, + { "BLOCK", LOG_BL }, + { NULL, 0 } + }; + +DEVICE dt_dev = { + "DT", dt_unit, dt_reg, dt_mod, + DT_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &dt_reset, + &dt_boot, &dt_attach, &dt_detach, + &dt_dib, DEV_DISABLE | DEV_DEBUG, 0, + dt_deb, NULL, NULL + }; + +/* IOT routines */ + +int32 dt76 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; +int32 old_dtsa = dtsa, fnc; +UNIT *uptr; + +if (pulse & 01) AC = AC | dtsa; /* DTRA */ +if (pulse & 06) { /* select */ + if (pulse & 02) dtsa = 0; /* DTCA */ + if (pulse & 04) { /* DTXA */ + if ((AC & DTA_CERF) == 0) dtsb = dtsb & ~DTB_ALLERR; + if ((AC & DTA_CDTF) == 0) dtsb = dtsb & ~DTB_DTF; + dtsa = dtsa ^ (AC & DTA_RW); + AC = 0; /* clr AC */ + } + if ((old_dtsa ^ dtsa) & DTA_UNIT) dt_deselect (old_dtsa); + uptr = dt_dev.units + DTA_GETUNIT (dtsa); /* get unit */ + fnc = DTA_GETFNC (dtsa); /* get fnc */ + if (((uptr->flags) & UNIT_DIS) || /* disabled? */ + (fnc >= FNC_WMRK) || /* write mark? */ + ((fnc == FNC_WALL) && (uptr->flags & UNIT_WPRT)) || + ((fnc == FNC_WRIT) && (uptr->flags & UNIT_WPRT))) + dt_seterr (uptr, DTB_SEL); /* select err */ + else dt_newsa (dtsa); + DT_UPDINT; + } +return AC; +} + +int32 dt77 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +if ((pulse & 01) && (dtsb & (DTB_ERF |DTB_DTF))) /* DTSF */ + AC = IOT_SKP | AC; +if (pulse & 02) AC = AC | dtsb; /* DTRB */ +if (pulse & 04) { /* DTLB */ + dtsb = (dtsb & ~DTB_MEX) | (AC & DTB_MEX); + AC = AC & ~07777; /* clear AC */ + } +return AC; +} + +/* Unit deselect */ + +void dt_deselect (int32 oldf) +{ +int32 old_unit = DTA_GETUNIT (oldf); +UNIT *uptr = dt_dev.units + old_unit; +int32 old_mot = DTS_GETMOT (uptr->STATE); + +if (old_mot >= DTS_ATSF) /* at speed? */ + dt_newfnc (uptr, DTS_STA (old_mot, DTS_OFR)); +else if (old_mot >= DTS_ACCF) /* accelerating? */ + DTS_SET2ND (DTS_ATSF | (old_mot & DTS_DIR), DTS_OFR); +return; +} + +/* Command register change + + 1. If change in motion, stop to start + - schedule acceleration + - set function as next state + 2. If change in motion, start to stop + - if not already decelerating (could be reversing), + schedule deceleration + 3. If change in direction, + - if not decelerating, schedule deceleration + - set accelerating (other dir) as next state + - set function as next next state + 4. If not accelerating or at speed, + - schedule acceleration + - set function as next state + 5. If not yet at speed, + - set function as next state + 6. If at speed, + - set function as current state, schedule function +*/ + +void dt_newsa (int32 newf) +{ +int32 new_unit, prev_mot, new_fnc; +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +new_unit = DTA_GETUNIT (newf); /* new, old units */ +uptr = dt_dev.units + new_unit; +if ((uptr->flags & UNIT_ATT) == 0) { /* new unit attached? */ + dt_seterr (uptr, DTB_SEL); /* no, error */ + return; + } +prev_mot = DTS_GETMOT (uptr->STATE); /* previous motion */ +prev_mving = prev_mot != DTS_STOP; /* previous moving? */ +prev_dir = prev_mot & DTS_DIR; /* previous dir? */ +new_mving = (newf & DTA_STSTP) != 0; /* new moving? */ +new_dir = (newf & DTA_FWDRV) != 0; /* new dir? */ +new_fnc = DTA_GETFNC (newf); /* new function? */ + +if ((prev_mving | new_mving) == 0) return; /* stop to stop */ + +if (new_mving & ~prev_mving) { /* start? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* schedule acc */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mving & ~new_mving) { /* stop? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + return; + } + +if (prev_dir ^ new_dir) { /* dir chg? */ + if ((prev_mot & ~DTS_DIR) != DTS_DECF) { /* !already stopping? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, dt_dctime); /* schedule decel */ + } + DTS_SETSTA (DTS_DECF | prev_dir, 0); /* state = decel */ + DTS_SET2ND (DTS_ACCF | new_dir, 0); /* next = accel */ + DTS_SET3RD (DTS_ATSF | new_dir, new_fnc); /* next next = fnc */ + return; + } + +if (prev_mot < DTS_ACCF) { /* not accel/at speed? */ + if (dt_setpos (uptr)) return; /* update pos */ + sim_cancel (uptr); /* cancel cur */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* sched accel */ + DTS_SETSTA (DTS_ACCF | new_dir, 0); /* state = accel */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +if (prev_mot < DTS_ATSF) { /* not at speed? */ + DTS_SET2ND (DTS_ATSF | new_dir, new_fnc); /* next = fnc */ + return; + } + +dt_newfnc (uptr, DTS_STA (DTS_ATSF | new_dir, new_fnc));/* state = fnc */ +return; +} + +/* Schedule new DECtape function + + This routine is only called if + - the selected unit is attached + - the selected unit is at speed (forward or backward) + + This routine + - updates the selected unit's position + - updates the selected unit's state + - schedules the new operation +*/ + +void dt_newfnc (UNIT *uptr, int32 newsta) +{ +int32 fnc, dir, blk, unum, relpos, newpos; +uint32 oldpos; + +oldpos = uptr->pos; /* save old pos */ +if (dt_setpos (uptr)) return; /* update pos */ +uptr->STATE = newsta; /* update state */ +fnc = DTS_GETFNC (uptr->STATE); /* set variables */ +dir = DTS_GETMOT (uptr->STATE) & DTS_DIR; +unum = (int32) (uptr - dt_dev.units); +if (oldpos == uptr->pos) /* bump pos */ + uptr->pos = uptr->pos + (dir? -1: 1); +blk = DT_LIN2BL (uptr->pos, uptr); + +if (dir? DT_QREZ (uptr): DT_QFEZ (uptr)) { /* wrong ez? */ + dt_seterr (uptr, DTB_END); /* set ez flag, stop */ + return; + } +sim_cancel (uptr); /* cancel cur op */ +dt_substate = DTO_SOB; /* substate = block start */ +switch (fnc) { /* case function */ + + case DTS_OFR: /* off reel */ + if (dir) newpos = -1000; /* rev? < start */ + else newpos = DTU_FWDEZ (uptr) + DT_EZLIN + 1000; /* fwd? > end */ + break; + + case FNC_MOVE: /* move */ + dt_schedez (uptr, dir); /* sched end zone */ + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: moving %s\n", + unum, (dir? "backward": "forward")); + return; /* done */ + + case FNC_SRCH: /* search */ + if (dir) newpos = DT_BLK2LN ((DT_QFEZ (uptr)? + DTU_TSIZE (uptr): blk), uptr) - DT_BLKLN - DT_WSIZE; + else newpos = DT_BLK2LN ((DT_QREZ (uptr)? + 0: blk + 1), uptr) + DT_BLKLN + (DT_WSIZE - 1); + if (DEBUG_PRI (dt_dev, LOG_MS)) fprintf (sim_deb, ">>DT%d: searching %s]\n", + unum, (dir? "backward": "forward")); + break; + + case FNC_WRIT: /* write */ + case FNC_READ: /* read */ + case FNC_RALL: /* read all */ + case FNC_WALL: /* write all */ + if (DT_QEZ (uptr)) { /* in "ok" end zone? */ + if (dir) newpos = DTU_FWDEZ (uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_EZLIN + DT_HTLIN + (DT_WSIZE - 1); + break; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dt_seterr (uptr, DTB_SEL); + return; + } + if (dir) newpos = DT_BLK2LN (((relpos >= (DTU_LPERB (uptr) - DT_HTLIN))? + blk + 1: blk), uptr) - DT_HTLIN - DT_WSIZE; + else newpos = DT_BLK2LN (((relpos < DT_HTLIN)? + blk: blk + 1), uptr) + DT_HTLIN + (DT_WSIZE - 1); + break; + + default: + dt_seterr (uptr, DTB_SEL); /* bad state */ + return; + } + +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/dt_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool dt_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 mot = DTS_GETMOT (uptr->STATE); +int32 unum, delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr->LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr->LASTT = new_time; /* update last time */ +switch (mot & ~DTS_DIR) { /* case on motion */ + + case DTS_STOP: /* stop */ + delta = 0; + break; + + case DTS_DECF: /* slowing */ + ulin = ut / (uint32) dt_ltime; + udelt = dt_dctime / dt_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; + + case DTS_ACCF: /* accelerating */ + ulin = ut / (uint32) dt_ltime; + udelt = (dt_dctime - (dt_dctime >> 2)) / dt_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; + + case DTS_ATSF: /* at speed */ + delta = ut / (uint32) dt_ltime; + break; + } + +if (mot & DTS_DIR) uptr->pos = uptr->pos - delta; /* update pos */ +else uptr->pos = uptr->pos + delta; +if (((int32) uptr->pos < 0) || + ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { + detach_unit (uptr); /* off reel? */ + uptr->STATE = uptr->pos = 0; + unum = (int32) (uptr - dt_dev.units); + if (unum == DTA_GETUNIT (dtsa)) /* if selected, */ + dt_seterr (uptr, DTB_SEL); /* error */ + return TRUE; + } +return FALSE; +} + +/* Unit service + + Unit must be attached, detach cancels operation +*/ + +t_stat dt_svc (UNIT *uptr) +{ +int32 mot = DTS_GETMOT (uptr->STATE); +int32 dir = mot & DTS_DIR; +int32 fnc = DTS_GETFNC (uptr->STATE); +int16 *fbuf = (int16 *) uptr->filebuf; +int32 unum = uptr - dt_dev.units; +int32 blk, wrd, ma, relpos, dat; +uint32 ba; + +/* Motion cases + + Decelerating - if next state != stopped, must be accel reverse + Accelerating - next state must be @speed, schedule function + At speed - do functional processing +*/ + +switch (mot) { + + case DTS_DECF: case DTS_DECR: /* decelerating */ + if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); + uptr->STATE = DTS_NXTSTA (uptr->STATE); /* advance state */ + if (uptr->STATE) /* not stopped? */ + sim_activate (uptr, dt_dctime - (dt_dctime >> 2)); /* must be reversing */ + return SCPE_OK; + + case DTS_ACCF: case DTS_ACCR: /* accelerating */ + dt_newfnc (uptr, DTS_NXTSTA (uptr->STATE)); /* adv state, sched */ + return SCPE_OK; + + case DTS_ATSF: case DTS_ATSR: /* at speed */ + break; /* check function */ + + default: /* other */ + dt_seterr (uptr, DTB_SEL); /* state error */ + return SCPE_OK; + } + +/* Functional cases + + Move - must be at end zone + Search - transfer block number, schedule next block + Off reel - detach unit (it must be deselected) +*/ + +if (dt_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (dt_stopoffr, STOP_DTOFF); +if (DT_QEZ (uptr)) { /* in end zone? */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + } +blk = DT_LIN2BL (uptr->pos, uptr); /* get block # */ +switch (fnc) { /* at speed, check fnc */ + + case FNC_MOVE: /* move */ + dt_seterr (uptr, DTB_END); /* end zone error */ + return SCPE_OK; + + case FNC_SRCH: /* search */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + sim_activate (uptr, DTU_LPERB (uptr) * dt_ltime);/* sched next block */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr word cnt */ + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if (MEM_ADDR_OK (ma)) M[ma] = blk & 07777; /* store block # */ + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTS_OFR: /* off reel */ + detach_unit (uptr); /* must be deselected */ + uptr->STATE = uptr->pos = 0; /* no visible action */ + break; + +/* Read has four subcases + + Start of block, not wc ovf - check that DTF is clear, otherwise normal + Normal - increment MA, WC, copy word from tape to memory + if read dir != write dir, bits must be scrambled + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - if end of block reached, timing error, + otherwise, continue to next word +*/ + + case FNC_READ: /* read */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + + case DTO_SOB: /* start of block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: reading block %d %s%s\n", + unum, blk, (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous": " ")); + dt_substate = 0; /* fall through */ + case 0: /* normal read */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = fbuf[ba]; /* get tape word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; /* wc ovf? */ + case DTO_WCO: /* wc ovf, not sob */ + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { + dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + } + break; + + case DTO_WCO | DTO_SOB: /* next block */ + if (wrd == (dir? 0: DTU_BSIZE (uptr))) /* end of block? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + else sim_activate (uptr, DT_WSIZE * dt_ltime); + break; + } + + break; + +/* Write has four subcases + + Start of block, not wc ovf - check that DTF is clear, set block direction + Normal - increment MA, WC, copy word from memory to tape + if wc overflow, next state is wc overflow + if end of block, possibly set DTF, next state is start of block + Wc ovf, not start of block - + copy 0 to tape + if end of block, possibly set DTF, next state is start of block + Wc ovf, start of block - schedule end zone +*/ + + case FNC_WRIT: /* write */ + wrd = DT_LIN2WD (uptr->pos, uptr); /* get word # */ + switch (dt_substate) { /* case on substate */ + + case DTO_SOB: /* start block */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + if (DEBUG_PRI (dt_dev, LOG_RW) || + (DEBUG_PRI (dt_dev, LOG_BL) && (blk == dt_logblk))) + fprintf (sim_deb, ">>DT%d: writing block %d %s%s\n", unum, blk, + (dir? "backward": "forward"), + ((dtsa & DTA_MODE)? " continuous": " ")); + dt_substate = 0; /* fall through */ + case 0: /* normal write */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + case DTO_WCO: /* wc ovflo */ + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + ba = (blk * DTU_BSIZE (uptr)) + wrd; /* buffer ptr */ + dat = dt_substate? 0: M[ma]; /* get word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + fbuf[ba] = dat; /* write word */ + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (wrd != (dir? 0: DTU_BSIZE (uptr) - 1)) /* not last? */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + else { + dt_substate = dt_substate | DTO_SOB; + sim_activate (uptr, ((2 * DT_HTLIN) + DT_WSIZE) * dt_ltime); + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + } + break; + + case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } + + break; + +/* Read all has two subcases + + Not word count overflow - increment MA, WC, copy word from tape to memory + Word count overflow - schedule end zone +*/ + + case FNC_RALL: + switch (dt_substate) { /* case on substate */ + + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + dat = fbuf[ba]; /* get tape word */ + if (dir) dat = dt_comobv (dat); /* rev? comp obv */ + } + else dat = dt_gethdr (uptr, blk, relpos, dir); /* get hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (MEM_ADDR_OK (ma)) M[ma] = dat; /* mem addr legal? */ + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } /* end case substate */ + + break; + +/* Write all has two subcases + + Not word count overflow - increment MA, WC, copy word from memory to tape + Word count overflow - schedule end zone +*/ + + case FNC_WALL: + switch (dt_substate) { /* case on substate */ + + case 0: case DTO_SOB: /* read in progress */ + if (dtsb & DTB_DTF) { /* DTF set? */ + dt_seterr (uptr, DTB_TIM); /* timing error */ + return SCPE_OK; + } + relpos = DT_LIN2OF (uptr->pos, uptr); /* cur pos in blk */ + M[DT_WC] = (M[DT_WC] + 1) & 07777; /* incr WC, CA */ + M[DT_CA] = (M[DT_CA] + 1) & 07777; + ma = DTB_GETMEX (dtsb) | M[DT_CA]; /* get mem addr */ + if ((relpos >= DT_HTLIN) && /* in data zone? */ + (relpos < (DTU_LPERB (uptr) - DT_HTLIN))) { + dat = M[ma]; /* get mem word */ + if (dir) dat = dt_comobv (dat); + wrd = DT_LIN2WD (uptr->pos, uptr); + ba = (blk * DTU_BSIZE (uptr)) + wrd; + fbuf[ba] = dat; /* write word */ + if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; + } +/* /* ignore hdr */ + sim_activate (uptr, DT_WSIZE * dt_ltime); + if (M[DT_WC] == 0) dt_substate = DTO_WCO; + if (((dtsa & DTA_MODE) == 0) || (M[DT_WC] == 0)) + dtsb = dtsb | DTB_DTF; /* set DTF */ + break; + + case DTO_WCO: case DTO_WCO | DTO_SOB: /* all done */ + dt_schedez (uptr, dir); /* sched end zone */ + break; + } /* end case substate */ + break; + + default: + dt_seterr (uptr, DTB_SEL); /* impossible state */ + break; + } + +DT_UPDINT; /* update interrupts */ +return SCPE_OK; +} + +/* Reading the header is complicated, because 18b words are being parsed + out 12b at a time. The sequence of word numbers is directionally + sensitive + + Forward Reverse + Word Word Content Word Word Content + (abs) (rel) (abs) (rel) + + 137 8 fwd csm'00 6 6 rev csm'00 + 138 9 0000 5 5 0000 + 139 10 0000 4 4 0000 + 140 11 0000 3 3 0000 + 141 12 00'lo rev blk 2 2 00'lo fwd blk + 142 13 hi rev blk 1 1 hi fwd blk + 143 14 0000 0 0 0000 + 0 0 0000 143 14 0000 + 1 1 0000 142 13 0000 + 2 2 hi fwd blk 141 12 hi rev blk + 3 3 lo fwd blk'00 140 11 lo rev blk'00 + 4 4 0000 139 10 0000 + 5 5 0000 138 9 0000 + 6 6 0000 137 8 0000 + 7 7 rev csm 136 7 00'fwd csm +*/ + +int32 dt_gethdr (UNIT *uptr, int32 blk, int32 relpos, int32 dir) +{ +if (relpos >= DT_HTLIN) relpos = relpos - (DT_WSIZE * DTU_BSIZE (uptr)); +if (dir) { /* reverse */ + switch (relpos / DT_WSIZE) { + case 6: /* rev csm */ + return 077; + case 2: /* lo fwd blk */ + return dt_comobv ((blk & 077) << 6); + case 1: /* hi fwd blk */ + return dt_comobv (blk >> 6); + case 12: /* hi rev blk */ + return (blk >> 6) & 07777; + case 11: /* lo rev blk */ + return ((blk & 077) << 6); + case 7: /* fwd csum */ + return (dt_comobv (dt_csum (uptr, blk)) << 6); + default: /* others */ + return 07777; + } + } +else { /* forward */ + switch (relpos / DT_WSIZE) { + case 8: /* fwd csum */ + return (dt_csum (uptr, blk) << 6); + case 12: /* lo rev blk */ + return dt_comobv ((blk & 077) << 6); + case 13: /* hi rev blk */ + return dt_comobv (blk >> 6); + case 2: /* hi fwd blk */ + return ((blk >> 6) & 07777); + case 3: /* lo fwd blk */ + return ((blk & 077) << 6); + case 7: /* rev csum */ + return 077; + default: /* others */ + break; + } + } +return 0; +} + +/* Utility routines */ + +/* Set error flag */ + +void dt_seterr (UNIT *uptr, int32 e) +{ +int32 mot = DTS_GETMOT (uptr->STATE); + +dtsa = dtsa & ~DTA_STSTP; /* clear go */ +dtsb = dtsb | DTB_ERF | e; /* set error flag */ +if (mot >= DTS_ACCF) { /* ~stopped or stopping? */ + sim_cancel (uptr); /* cancel activity */ + if (dt_setpos (uptr)) return; /* update position */ + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (mot & DTS_DIR), 0); /* state = decel */ + } +DT_UPDINT; +return; +} + +/* Schedule end zone */ + +void dt_schedez (UNIT *uptr, int32 dir) +{ +int32 newpos; + +if (dir) newpos = DT_EZLIN - DT_WSIZE; /* rev? rev ez */ +else newpos = DTU_FWDEZ (uptr) + DT_WSIZE; /* fwd? fwd ez */ +sim_activate (uptr, ABS (newpos - ((int32) uptr->pos)) * dt_ltime); +return; +} + +/* Complement obverse routine */ + +int32 dt_comobv (int32 dat) +{ +dat = dat ^ 07777; /* compl obverse */ +dat = ((dat >> 9) & 07) | ((dat >> 3) & 070) | + ((dat & 070) << 3) | ((dat & 07) << 9); +return dat; +} + +/* Checksum routine */ + +int32 dt_csum (UNIT *uptr, int32 blk) +{ +int16 *fbuf = (int16 *) uptr->filebuf; +int32 ba = blk * DTU_BSIZE (uptr); +int32 i, csum, wrd; + +csum = 077; /* init csum */ +for (i = 0; i < DTU_BSIZE (uptr); i++) { /* loop thru buf */ + wrd = fbuf[ba + i] ^ 07777; /* get ~word */ + csum = csum ^ (wrd >> 6) ^ wrd; + } +return (csum & 077); +} + +/* Reset routine */ + +t_stat dt_reset (DEVICE *dptr) +{ +int32 i, prev_mot; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ + uptr = dt_dev.units + i; + if (sim_is_running) { /* CAF? */ + prev_mot = DTS_GETMOT (uptr->STATE); /* get motion */ + if ((prev_mot & ~DTS_DIR) > DTS_DECF) { /* accel or spd? */ + if (dt_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, dt_dctime); /* sched decel */ + DTS_SETSTA (DTS_DECF | (prev_mot & DTS_DIR), 0); + } + } + else { + sim_cancel (uptr); /* sim reset */ + uptr->STATE = 0; + uptr->LASTT = sim_grtime (); + } + } +dtsa = dtsb = 0; /* clear status */ +DT_UPDINT; /* reset interrupt */ +return SCPE_OK; +} + +/* Bootstrap routine + + This is actually the 4K disk monitor bootstrap, which also + works with OS/8. The reverse is not true - the OS/8 bootstrap + doesn't work with the disk monitor. +*/ + +#define BOOT_START 0200 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 07600, /* 200, CLA CLL */ + 01216, /* TAD MVB ; move back */ + 04210, /* JMS DO ; action */ + 01217, /* TAD K7577 ; addr */ + 03620, /* DCA I CA */ + 01222, /* TAD RDF ; read fwd */ + 04210, /* JMS DO ; action */ + 05600, /* JMP I 200 ; enter boot */ + 00000, /* DO, 0 */ + 06766, /* DTCA!DTXA ; start tape */ + 03621, /* DCA I WC ; clear wc */ + 06771, /* DTSF ; wait */ + 05213, /* JMP .-1 */ + 05610, /* JMP I DO */ + 00600, /* MVB, 0600 */ + 07577, /* K7577, 7757 */ + 07755, /* CA, 7755 */ + 07754, /* WC, 7754 */ + 00220 /* RF, 0220 */ + }; + +t_stat dt_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +if (unitno) return SCPE_ARG; /* only unit 0 */ +if (dt_dib.dev != DEV_DTA) return STOP_NOTSTD; /* only std devno */ +dt_unit[unitno].pos = DT_EZLIN; +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 16b or 18b, read 16b or 18b format and convert to 12b in buffer + If 12b, read data into buffer +*/ + +t_stat dt_attach (UNIT *uptr, char *cptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k; +int32 u = uptr - dt_dev.units; +t_stat r; +uint32 ba, sz; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* fail? */ +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; + if (sim_switches & SWMASK ('F')) /* att 18b? */ + uptr->flags = uptr->flags & ~UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D11_FILSIZ) + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (sz > D8_FILSIZ) + uptr->flags = uptr->flags & ~UNIT_8FMT; + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (uint16)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint16 *) uptr->filebuf; /* file buffer */ +printf ("%s%d: ", sim_dname (&dt_dev), u); +if (uptr->flags & UNIT_8FMT) printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) printf ("16b format"); +else printf ("18b/36b format"); +printf (", buffering file in memory\n"); +if (uptr->flags & UNIT_8FMT) /* 12b? */ + uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), + uptr->capac, uptr->fileref); +else { /* 16b/18b */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + if (uptr->flags & UNIT_11FMT) { + k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); + for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i]; + } + else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; + for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ + fbuf[ba] = (pdp18b[k] >> 6) & 07777; + fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | + ((pdp18b[k + 1] >> 12) & 077); + fbuf[ba + 2] = pdp18b[k + 1] & 07777; + ba = ba + 3; + } /* end blk loop */ + } /* end file loop */ + uptr->hwmark = ba; + } /* end else */ +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +uptr->LASTT = sim_grtime (); /* last pos update */ +return SCPE_OK; +} + +/* Detach routine + + Cancel in progress operation + If 12b, write buffer to file + If 16b or 18b, convert 12b buffer to 16b or 18b and write to file + Deallocate buffer +*/ + +t_stat dt_detach (UNIT* uptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k; +int32 u = uptr - dt_dev.units; +uint32 ba; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if (sim_is_active (uptr)) { + sim_cancel (uptr); + if ((u == DTA_GETUNIT (dtsa)) && (dtsa & DTA_STSTP)) { + dtsb = dtsb | DTB_ERF | DTB_SEL | DTB_DTF; + DT_UPDINT; + } + uptr->STATE = uptr->pos = 0; + } +fbuf = (uint16 *) uptr->filebuf; /* file buffer */ +if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ + printf ("%s%d: writing buffer to file\n", sim_dname (&dt_dev), u); + rewind (uptr->fileref); /* start of file */ + if (uptr->flags & UNIT_8FMT) /* PDP8? */ + fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ + uptr->hwmark, uptr->fileref); + else { /* 16b/18b */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ + for (k = 0; k < D18_NBSIZE; k = k + 2) { + pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | + ((uint32) (fbuf[ba + 1] >> 6) & 077); + pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | + ((uint32) (fbuf[ba + 2] & 07777)); + ba = ba + 3; + } /* end loop blk */ + if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i]; + fxwrite (pdp11b, sizeof (uint16), + D18_NBSIZE, uptr->fileref); + } + else fxwrite (pdp18b, sizeof (uint32), + D18_NBSIZE, uptr->fileref); + } /* end loop buf */ + } /* end else */ + if (ferror (uptr->fileref)) perror ("I/O error"); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +return detach_unit (uptr); +} diff --git a/PDP8/pdp8_fpp.c b/PDP8/pdp8_fpp.c new file mode 100644 index 0000000..fb2e586 --- /dev/null +++ b/PDP8/pdp8_fpp.c @@ -0,0 +1,1387 @@ +/* pdp8_fpp.c: PDP-8 floating point processor (FPP8A) + + Copyright (c) 2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + fpp FPP8A floating point processor + + Floating point formats: + + 00 01 02 03 04 05 06 07 08 09 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | S| hi integer | : double precision + +--+--+--+--+--+--+--+--+--+--+--+--+ + | lo integer | + +--+--+--+--+--+--+--+--+--+--+--+--+ + + 00 01 02 03 04 05 06 07 08 09 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | S| exponent | : floating point + +--+--+--+--+--+--+--+--+--+--+--+--+ + | S| hi fraction | + +--+--+--+--+--+--+--+--+--+--+--+--+ + | lo fraction | + +--+--+--+--+--+--+--+--+--+--+--+--+ + + + 00 01 02 03 04 05 06 07 08 09 10 11 + +--+--+--+--+--+--+--+--+--+--+--+--+ + | S| exponent | : extended precision + +--+--+--+--+--+--+--+--+--+--+--+--+ + | S| hi fraction | + +--+--+--+--+--+--+--+--+--+--+--+--+ + | next fraction | + +--+--+--+--+--+--+--+--+--+--+--+--+ + | next fraction | + +--+--+--+--+--+--+--+--+--+--+--+--+ + | next fraction | + +--+--+--+--+--+--+--+--+--+--+--+--+ + | lo fraction | + +--+--+--+--+--+--+--+--+--+--+--+--+ + + Exponents are 2's complement, as are fractions. Normalized numbers have + the form: + + 0.0...0 + 0. + 1. + 1.1...0 + + Note that 1.0...0 is normalized but considered illegal, since it cannot + be represented as a positive number. When a result is normalized, 1.0...0 + is converted to 1.1...0 with exp+1. +*/ + +#include "pdp8_defs.h" + +extern int32 int_req; +extern int32 sim_switches; +extern int32 sim_interval; +extern uint16 M[]; +extern int32 stop_inst; +extern UNIT cpu_unit; + +#define SEXT12(x) (((x) & 04000)? (x) | ~07777: (x) & 03777) + +/* Index registers are in memory */ + +#define fpp_read_xr(xr) fpp_read (fpp_xra + xr) +#define fpp_write_xr(xr,d) fpp_write (fpp_xra +xr, d) + +/* Command register */ + +#define FPC_DP 04000 /* integer double */ +#define FPC_UNFX 02000 /* exit on fl undf */ +#define FPC_FIXF 01000 /* lock mem field */ +#define FPC_IE 00400 /* int enable */ +#define FPC_V_FAST 4 /* startup bits */ +#define FPC_M_FAST 017 +#define FPC_LOCK 00010 /* lockout */ +#define FPC_V_APTF 0 +#define FPC_M_APTF 07 /* apta field */ +#define FPC_STA (FPC_DP|FPC_LOCK) +#define FPC_GETFAST(x) (((x) >> FPC_V_FAST) & FPC_M_FAST) +#define FPC_GETAPTF(x) (((x) >> FPC_V_APTF) & FPC_M_APTF) + +/* Status register */ + +#define FPS_DP (FPC_DP) /* integer double */ +#define FPS_TRPX 02000 /* trap exit */ +#define FPS_HLTX 01000 /* halt exit */ +#define FPS_DVZX 00400 /* div zero exit */ +#define FPS_IOVX 00200 /* int ovf exit */ +#define FPS_FOVX 00100 /* flt ovf exit */ +#define FPS_UNF 00040 /* underflow */ +#define FPS_UNFX 00020 /* undf exit */ +#define FPS_XXXM 00010 /* FADDM/FMULM */ +#define FPS_LOCK (FPC_LOCK) /* lockout */ +#define FPS_EP 00004 /* ext prec */ +#define FPS_PAUSE 00002 /* paused */ +#define FPS_RUN 00001 /* running */ + +/* Floating point number: 3-6 words */ + +#define FPN_FRSIGN 04000 +#define FPN_NFR_FP 2 /* std precision */ +#define FPN_NFR_EP 5 /* ext precision */ +#define EXACT (uint32)((fpp_sta & FPS_EP)? FPN_NFR_EP: FPN_NFR_FP) +#define EXTEND ((uint32) FPN_NFR_EP) + +typedef struct { + int32 exp; + uint32 fr[FPN_NFR_EP]; + } FPN; + +uint32 fpp_apta; /* APT pointer */ +uint32 fpp_aptsvf; /* APT saved field */ +uint32 fpp_opa; /* operand pointer */ +uint32 fpp_fpc; /* FP PC */ +uint32 fpp_bra; /* base reg pointer */ +uint32 fpp_xra; /* indx reg pointer */ +uint32 fpp_cmd; /* command */ +uint32 fpp_sta; /* status */ +uint32 fpp_flag; /* flag */ +FPN fpp_ac; /* FAC */ +static FPN fpp_zero = { 0, { 0, 0, 0, 0, 0 } }; +static FPN fpp_one = { 1, { 02000, 0, 0, 0, 0 } }; + +DEVICE fpp_dev; +int32 fpp55 (int32 IR, int32 AC); +int32 fpp56 (int32 IR, int32 AC); +void fpp_load_apt (uint32 apta); +void fpp_dump_apt (uint32 apta, uint32 sta); +uint32 fpp_1wd_dir (uint32 ir); +uint32 fpp_2wd_dir (uint32 ir); +uint32 fpp_indir (uint32 ir); +uint32 fpp_ad15 (uint32 hi); +uint32 fpp_adxr (uint32 ir, uint32 base_ad); +t_bool fpp_add (FPN *a, FPN *b, uint32 sub); +t_bool fpp_mul (FPN *a, FPN *b); +t_bool fpp_div (FPN *a, FPN *b); +t_bool fpp_imul (FPN *a, FPN *b); +uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b); +void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b); +void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b); +t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b); +uint32 fpp_fr_neg (uint32 *a, uint32 cnt); +int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt); +int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt); +uint32 fpp_fr_abs (uint32 *a, uint32 *b, uint32 cnt); +void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt); +void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt); +void fpp_fr_lsh12 (uint32 *a, uint32 cnt); +void fpp_fr_lsh1 (uint32 *a, uint32 cnt); +void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt); +void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt); +t_bool fpp_cond_met (uint32 cond); +t_bool fpp_norm (FPN *a, uint32 cnt); +uint32 fpp_round (FPN *a); +t_bool fpp_test_xp (FPN *a); +void fpp_copy (FPN *a, FPN *b); +void fpp_zcopy (FPN *a, FPN *b); +void fpp_read_op (uint32 ea, FPN *a); +void fpp_write_op (uint32 ea, FPN *a); +uint32 fpp_read (uint32 ea); +void fpp_write (uint32 ea, uint32 val); +uint32 apt_read (uint32 ea); +void apt_write (uint32 ea, uint32 val); +t_stat fpp_svc (UNIT *uptr); +t_stat fpp_reset (DEVICE *dptr); + +/* FPP data structures + + fpp_dev FPP device descriptor + fpp_unit FPP unit descriptor + fpp_reg FPP register list +*/ + +DIB fpp_dib = { DEV_FPP, 2, { &fpp55, &fpp56 } }; + +UNIT fpp_unit = { UDATA (&fpp_svc, 0, 0) }; + +REG fpp_reg[] = { + { ORDATA (FPACE, fpp_ac.exp, 12) }, + { ORDATA (FPAC0, fpp_ac.fr[0], 12) }, + { ORDATA (FPAC1, fpp_ac.fr[1], 12) }, + { ORDATA (FPAC2, fpp_ac.fr[2], 12) }, + { ORDATA (FPAC3, fpp_ac.fr[3], 12) }, + { ORDATA (FPAC4, fpp_ac.fr[4], 12) }, + { ORDATA (CMD, fpp_cmd, 12) }, + { ORDATA (STA, fpp_sta, 12) }, + { ORDATA (APTA, fpp_apta, 15) }, + { GRDATA (APTSVF, fpp_aptsvf, 8, 3, 12) }, + { ORDATA (FPC, fpp_fpc, 15) }, + { ORDATA (BRA, fpp_bra, 15) }, + { ORDATA (XRA, fpp_xra, 15) }, + { ORDATA (OPA, fpp_opa, 15) }, + { FLDATA (FLAG, fpp_flag, 0) }, + { NULL } + }; + +DEVICE fpp_dev = { + "FPP", &fpp_unit, fpp_reg, NULL, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &fpp_reset, + NULL, NULL, NULL, + &fpp_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 fpp55 (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* FPINT */ + return (fpp_flag? IOT_SKP | AC: AC); /* skip on flag */ + + case 2: /* FPICL */ + fpp_reset (&fpp_dev); /* reset device */ + break; + + case 3: /* FPCOM */ + if (!fpp_flag && !(fpp_sta & FPS_RUN)) { /* flag clr, !run? */ + fpp_cmd = AC; /* load cmd */ + fpp_sta = (fpp_sta & ~FPC_STA) | /* copy flags */ + (fpp_cmd & FPC_STA); /* to status */ + } + break; + + case 4: /* FPHLT */ + if (fpp_sta & FPS_RUN) { /* running? */ + if (fpp_sta & FPS_PAUSE) /* paused? */ + fpp_fpc = (fpp_fpc - 1) & ADDRMASK; /* decr FPC */ + sim_cancel (&fpp_unit); /* stop execution */ + fpp_dump_apt (fpp_apta, FPS_HLTX); /* dump APT */ + } + else sim_activate (&fpp_unit, 0); /* single step */ + break; + + case 5: /* FPST */ + if (!fpp_flag && !(fpp_sta & FPS_RUN)) { /* flag clr, !run? */ + fpp_apta = (FPC_GETAPTF (fpp_cmd) << 12) | AC; + fpp_load_apt (fpp_apta); /* load APT */ + sim_activate (&fpp_unit, 0); /* start unit */ + return IOT_SKP | AC; + } + if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == (FPS_RUN|FPS_PAUSE)) { + fpp_sta &= ~FPS_PAUSE; /* continue */ + sim_activate (&fpp_unit, 0); /* start unit */ + return (IOT_SKP | AC); + } + break; + + case 6: /* FPRST */ + return fpp_sta; + + case 7: /* FPIST */ + if (fpp_flag) { /* if flag set */ + uint32 old_sta = fpp_sta; + fpp_flag = 0; /* clr flag, status */ + fpp_sta = 0; + int_req &= ~INT_FPP; /* clr int req */ + return IOT_SKP | old_sta; /* ret old status */ + } + break; + + default: + return (stop_inst << IOT_V_REASON) | AC; + } /* end switch */ + +return AC; +} + +int32 fpp56 (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 7: /* FPEP */ + if ((AC & 04000) && !(fpp_sta & FPS_RUN)) /* if AC0, not run, */ + fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP; /* set ep */ + break; + + default: + return (stop_inst << IOT_V_REASON) | AC; + } /* end switch */ + +return AC; +} + +/* Service routine */ + +t_stat fpp_svc (UNIT *uptr) +{ +FPN x; +uint32 ir, op, op2, op3, ad, ea, wd; +uint32 i; + +fpp_ac.exp = SEXT12 (fpp_ac.exp); /* sext AC exp */ +do { /* repeat */ + ir = fpp_read (fpp_fpc); /* get instr */ + fpp_fpc = (fpp_fpc + 1) & ADDRMASK; /* incr FP PC */ + op = (ir >> 7) & 037; /* get op+mode */ + op2 = (ir >> 3) & 017; /* get subop */ + op3 = ir & 07; /* get field/xr */ + fpp_sta &= ~FPS_XXXM; /* not mem op */ + + switch (op) { /* case on op+mode */ + case 000: /* operates */ + + switch (op2) { /* case on subop */ + case 000: /* no-operands */ + switch (op3) { /* case on subsubop */ + + case 0: /* FEXIT */ + fpp_dump_apt (fpp_apta, 0); + break; + + case 1: /* FPAUSE */ + fpp_sta |= FPS_PAUSE; + break; + + case 2: /* FCLA */ + fpp_copy (&fpp_ac, &fpp_zero); /* clear FAC */ + break; + + case 3: /* FNEG */ + fpp_fr_neg (fpp_ac.fr, EXACT); /* do exact length */ + break; + + case 4: /* FNORM */ + if (!(fpp_sta & FPS_DP)) { /* fp or ep only */ + fpp_copy (&x, &fpp_ac); /* copy AC */ + fpp_norm (&x, EXACT); /* do exact length */ + if (!fpp_test_xp (&x)) /* no trap? */ + fpp_copy (&fpp_ac, &x); /* copy back */ + } + break; + + case 5: /* STARTF */ + if (fpp_sta & FPS_EP) { /* if ep, */ + fpp_copy (&x, &fpp_ac); /* copy AC */ + fpp_round (&x); /* round */ + if (!fpp_test_xp (&x)) /* no trap? */ + fpp_copy (&fpp_ac, &x); /* copy back */ + } + fpp_sta &= ~(FPS_DP|FPS_EP); + break; + + case 6: /* STARTD */ + fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; + break; + + case 7: /* JAC */ + fpp_fpc = ((fpp_ac.fr[0] & 07) << 12) | fpp_ac.fr[1]; + break; + } + break; + + case 001: /* ALN */ + if (op3 != 0) /* if xr, */ + wd = fpp_read_xr (op3); /* use val */ + else wd = 027; /* else 23 */ + if (!(fpp_sta & FPS_DP)) { /* fp or ep? */ + int32 t = wd - fpp_ac.exp; /* alignment */ + fpp_ac.exp = SEXT12 (wd); /* new exp */ + wd = t & 07777; + } + if (wd & 04000) /* left? */ + fpp_fr_lshn (fpp_ac.fr, 04000 - wd, EXACT); + else fpp_fr_algn (fpp_ac.fr, wd, EXACT); + break; + + case 002: /* ATX */ + if (fpp_sta & FPS_DP) /* dp? */ + fpp_write_xr (op3, fpp_ac.fr[1]); /* xr<-FAC<12:23> */ + else { + fpp_copy (&x, &fpp_ac); /* copy AC */ + wd = (fpp_ac.exp - 027) & 07777; /* shift amt */ + if (wd & 04000) /* left? */ + fpp_fr_lshn (x.fr, 04000 - wd, EXACT); + else fpp_fr_algn (x.fr, wd, EXACT); + fpp_write_xr (op3, x.fr[1]); /* xr<-val<12:23> */ + } + break; + + case 003: /* XTA */ + for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++) + x.fr[i] = 0; /* clear FOP2-4 */ + x.fr[1] = fpp_read_xr (op3); /* get XR value */ + x.fr[0] = (x.fr[1] & 04000)? 07777: 0; + x.exp = 027; /* standard exp */ + if (!(fpp_sta & FPS_DP)) { /* fp or ep? */ + fpp_norm (&x, EXACT); /* normalize */ + if (fpp_test_xp (&x)) /* exception? */ + break; + } + fpp_copy (&fpp_ac, &x); /* result to AC */ + break; + + case 004: /* NOP */ + break; + + case 005: /* STARTE */ + if (!(fpp_sta & FPS_EP)) { + fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP; + for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++) + fpp_ac.fr[i] = 0; /* clear FAC2-4 */ + } + break; + + case 010: /* LDX */ + wd = fpp_ad15 (0); /* load XR immed */ + fpp_write_xr (op3, wd); + break; + + case 011: /* ADDX */ + wd = fpp_ad15 (0); + wd = wd + fpp_read_xr (op3); /* add to XR immed */ + fpp_write_xr (op3, wd); /* trims to 12b */ + break; + + default: + return stop_inst; + } /* end case subop */ + break; + + case 001: /* FLDA */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &fpp_ac); + break; + + case 002: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &fpp_ac); + break; + + case 003: + ea = fpp_indir (ir); + fpp_read_op (ea, &fpp_ac); + break; + + case 004: /* jumps and sets */ + ad = fpp_ad15 (op3); /* get 15b address */ + switch (op2) { /* case on subop */ + + case 000: case 001: case 002: case 003: /* cond jump */ + case 004: case 005: case 006: case 007: + if (fpp_cond_met (op2)) /* br if cond */ + fpp_fpc = ad; + break; + + case 010: /* SETX */ + fpp_xra = ad; + break; + + case 011: /* SETB */ + fpp_bra = ad; + break; + + case 012: /* JSA */ + fpp_write (ad, 01030 + (fpp_fpc >> 12)); /* save return */ + fpp_write (ad + 1, fpp_fpc); /* trims to 12b */ + fpp_fpc = (ad + 2) & ADDRMASK; + break; + + case 013: /* JSR */ + fpp_write (fpp_bra + 1, 01030 + (fpp_fpc >> 12)); + fpp_write (fpp_bra + 2, fpp_fpc); /* trims to 12b */ + fpp_fpc = ad; + break; + + default: + return stop_inst; + } /* end case subop */ + break; + + case 005: /* FADD */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 0); + break; + + case 006: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 0); + break; + + case 007: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 0); + break; + + case 010: /* JNX */ + ad = fpp_ad15 (op3); /* get 15b addr */ + wd = fpp_read_xr (op2 & 07); /* read xr */ + if (ir & 00100) { /* inc? */ + wd = (wd + 1) & 07777; + fpp_write_xr (op2 & 07, wd); /* ++xr */ + } + if (wd != 0) /* xr != 0? */ + fpp_fpc = ad; /* jump */ + break; + + case 011: /* FSUB */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 1); + break; + + case 012: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 1); + break; + + case 013: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_add (&fpp_ac, &x, 1); + break; + + case 014: /* TRAP3 */ + case 020: /* TRAP4 */ + fpp_opa = fpp_ad15 (op3); + fpp_dump_apt (fpp_apta, FPS_TRPX); + break; + + case 015: /* FDIV */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_div (&fpp_ac, &x); + break; + + case 016: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_div (&fpp_ac, &x); + break; + + case 017: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_div (&fpp_ac, &x); + break; + + case 021: /* FMUL */ + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + fpp_mul (&fpp_ac, &x); + break; + + case 022: + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + fpp_mul (&fpp_ac, &x); + break; + + case 023: + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + fpp_mul (&fpp_ac, &x); + break; + + case 024: /* LTR */ + fpp_copy (&fpp_ac, (fpp_cond_met (op2 & 07)? &fpp_one: &fpp_zero)); + break; + + case 025: /* FADDM */ + fpp_sta |= FPS_XXXM; + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + if (!fpp_add (&x, &fpp_ac, 0)) /* no trap? */ + fpp_write_op (ea, &x); /* store result */ + break; + + case 026: + fpp_sta |= FPS_XXXM; + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + if (!fpp_add (&x, &fpp_ac, 0)) /* no trap? */ + fpp_write_op (ea, &x); /* store result */ + break; + + case 027: + fpp_sta |= FPS_XXXM; + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + if (!fpp_add (&x, &fpp_ac, 0)) /* no trap? */ + fpp_write_op (ea, &x); /* store result */ + break; + + case 030: /* IMUL/LEA */ + ea = fpp_2wd_dir (ir); /* 2-word direct */ + if (fpp_sta & FPS_DP) { /* dp? */ + fpp_read_op (ea, &x); /* IMUL */ + fpp_imul (&fpp_ac, &x); + } + else { /* LEA */ + fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; /* set dp */ + fpp_ac.fr[0] = (ea >> 12) & 07; + fpp_ac.fr[1] = ea & 07777; + } + break; + + case 031: /* FSTA */ + ea = fpp_1wd_dir (ir); + fpp_write_op (ea, &fpp_ac); + break; + + case 032: + ea = fpp_2wd_dir (ir); + fpp_write_op (ea, &fpp_ac); + break; + + case 033: + ea = fpp_indir (ir); + fpp_write_op (ea, &fpp_ac); + break; + + case 034: /* IMULI/LEAI */ + ea = fpp_indir (ir); /* 1-word indir */ + if (fpp_sta & FPS_DP) { /* dp? */ + fpp_read_op (ea, &x); /* IMUL */ + fpp_imul (&fpp_ac, &x); + } + else { /* LEA */ + fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP; /* set dp */ + fpp_ac.fr[0] = (ea >> 12) & 07; + fpp_ac.fr[1] = ea & 07777; + } + break; + + case 035: /* FMULM */ + fpp_sta |= FPS_XXXM; + ea = fpp_1wd_dir (ir); + fpp_read_op (ea, &x); + if (!fpp_mul (&x, &fpp_ac)) /* no trap? */ + fpp_write_op (ea, &x); /* store result */ + break; + + case 036: + fpp_sta |= FPS_XXXM; + ea = fpp_2wd_dir (ir); + fpp_read_op (ea, &x); + if (!fpp_mul (&x, &fpp_ac)) /* no trap? */ + fpp_write_op (ea, &x); /* store result */ + break; + + case 037: + fpp_sta |= FPS_XXXM; + ea = fpp_indir (ir); + fpp_read_op (ea, &x); + if (!fpp_mul (&x, &fpp_ac)) /* no trap? */ + fpp_write_op (ea, &x); /* store result */ + break; + } /* end sw op+mode */ + + if (sim_interval) + sim_interval = sim_interval - 1; + } while ((sim_interval > 0) && + ((fpp_sta & (FPS_RUN|FPS_PAUSE|FPS_LOCK)) == (FPS_RUN|FPS_LOCK))); +if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == FPS_RUN) + sim_activate (uptr, 1); +fpp_ac.exp &= 07777; /* mask AC exp */ +return SCPE_OK; +} + +/* Address decoding routines */ + +uint32 fpp_1wd_dir (uint32 ir) +{ +uint32 ad; + +ad = fpp_bra + ((ir & 0177) * 3); /* base + 3*7b off */ +if (fpp_sta & FPS_DP) /* dp? skip exp */ + ad = ad + 1; +return ad & ADDRMASK; +} + +uint32 fpp_2wd_dir (uint32 ir) +{ +uint32 ad; + +ad = fpp_ad15 (ir); /* get 15b addr */ +return fpp_adxr (ir, ad); /* do indexing */ +} + +uint32 fpp_indir (uint32 ir) +{ +uint32 ad, iad, wd1, wd2; + +ad = fpp_bra + ((ir & 07) * 3); /* base + 3*3b off */ +iad = fpp_adxr (ir, ad); /* do indexing */ +wd1 = fpp_read (iad + 1); /* read wds 2,3 */ +wd2 = fpp_read (iad + 2); +return ((wd1 & 07) << 12) | wd2; /* return addr */ +} + +uint32 fpp_ad15 (uint32 hi) +{ +uint32 ad; + +ad = ((hi & 07) << 12) | fpp_read (fpp_fpc); /* 15b addr */ +fpp_fpc = (fpp_fpc + 1) & ADDRMASK; /* incr FPC */ +return ad; /* return addr */ +} + +uint32 fpp_adxr (uint32 ir, uint32 base_ad) +{ +uint32 xr, wd; + +xr = (ir >> 3) & 07; +wd = fpp_read_xr (xr); /* get xr */ +if (ir & 0100) { /* increment? */ + wd = (wd + 1) & 07777; /* inc, rewrite */ + fpp_write_xr (xr, wd); + } +if (xr != 0) { /* indexed? */ + if (fpp_sta & FPS_EP) wd = wd * 6; /* scale by len */ + else if (fpp_sta & FPS_DP) wd = wd * 2; + else wd = wd * 3; + return (base_ad + wd) & ADDRMASK; /* return index */ + } +else return base_ad & ADDRMASK; /* return addr */ +} + +/* Computation routines */ + +/* Fraction/floating add - return true if overflow */ + +t_bool fpp_add (FPN *a, FPN *b, uint32 sub) +{ +FPN x, y, z; +uint32 ediff, c; + +fpp_zcopy (&x, a); /* copy opnds */ +fpp_zcopy (&y, b); +if (sub) /* subtract? */ + fpp_fr_neg (y.fr, EXACT); /* neg B, exact */ +if (fpp_sta & FPS_DP) { /* dp? */ + fpp_fr_add (z.fr, x.fr, y.fr); /* z = a + b */ + if ((~x.fr[0] ^ y.fr[0]) & (x.fr[0] ^ z.fr[0]) & FPN_FRSIGN) { + fpp_dump_apt (fpp_apta, FPS_IOVX); /* int ovf? */ + return TRUE; + } + } +else { /* fp or ep */ + if (fpp_fr_test (b->fr, 0, EXACT) == 0) /* B == 0? */ + z = x; /* result is A */ + else if (fpp_fr_test (a->fr, 0, EXACT) == 0) /* A == 0? */ + z = y; /* result is B */ + else { /* fp or ep */ + if (x.exp < y.exp) { /* |a| < |b|? */ + z = x; /* exchange ops */ + x = y; + y = z; + } + ediff = x.exp - y.exp; /* exp diff */ + z.exp = x.exp; /* result exp */ + if (ediff <= (fpp_sta & FPS_EP)? 59: 24) { /* any add? */ + if (ediff != 0) /* any align? */ + fpp_fr_algn (y.fr, ediff, EXTEND); /* align, 60b */ + c = fpp_fr_add (z.fr, x.fr, y.fr); /* add fractions */ + if ((((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) == 0) && /* same signs? */ + (c || /* carry out? */ + ((~x.fr[0] & z.fr[0] & FPN_FRSIGN)))) { /* + to - change? */ + fpp_fr_rsh1 (z.fr, c << 11, EXTEND); /* rsh, insert cout */ + z.exp = z.exp + 1; /* incr exp */ + } /* end same signs */ + } /* end in range */ + } /* end ops != 0 */ + if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ + fpp_round (&z); /* round */ + if (fpp_test_xp (&z)) /* ovf, unf? */ + return TRUE; + } /* end else */ +fpp_copy (a, &z); /* result is z */ +return FALSE; +} + +/* Fraction/floating multiply - return true if overflow */ + +t_bool fpp_mul (FPN *a, FPN *b) +{ +FPN x, y, z; + +fpp_zcopy (&x, a); /* copy opnds */ +fpp_zcopy (&y, b); +if (fpp_sta & FPS_DP) /* dp? */ + fpp_fr_mul (z.fr, x.fr, y.fr); /* mult frac */ +else { /* fp or ep */ + z.exp = x.exp + y.exp; /* add exp */ + fpp_fr_mul (z.fr, x.fr, y.fr); /* mult frac */ + if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ + fpp_round (&z); /* round */ + if (fpp_test_xp (&z)) /* ovf, unf? */ + return TRUE; + } +fpp_copy (a, &z); /* result is z */ +return FALSE; +} + +/* Fraction/floating divide - return true if div by zero or overflow */ + +t_bool fpp_div (FPN *a, FPN *b) +{ +FPN x, y, z; + +if (fpp_fr_test (b->fr, 0, EXACT) == 0) { /* divisor 0? */ + fpp_dump_apt (fpp_apta, FPS_DVZX); /* error */ + return TRUE; + } +if (fpp_fr_test (a->fr, 0, EXACT) == 0) /* dividend 0? */ + return FALSE; /* quotient is 0 */ +fpp_zcopy (&x, a); /* copy opnds */ +fpp_zcopy (&y, b); +if (fpp_sta & FPS_DP) { /* dp? */ + if (fpp_fr_div (z.fr, x.fr, y.fr)) { /* fr div, ovflo? */ + fpp_dump_apt (fpp_apta, FPS_IOVX); /* error */ + return TRUE; + } + } +else { /* fp or ep */ + fpp_norm (&y, EXACT); /* norm divisor */ + if (fpp_fr_test (x.fr, 04000, EXACT) == 0) { /* divd 1.000...? */ + x.fr[0] = 06000; /* fix */ + x.exp = x.exp + 1; + } + z.exp = x.exp - y.exp; /* calc exp */ + if (fpp_fr_div (z.fr, x.fr, y.fr)) { /* fr div, ovflo? */ + uint32 cin = (a->fr[0] ^ b->fr[0]) & FPN_FRSIGN; + fpp_fr_rsh1 (z.fr, cin, EXTEND); /* rsh, insert sign */ + z.exp = z.exp + 1; /* incr exp */ + } + if (fpp_norm (&z, EXTEND)) /* norm, !exact? */ + fpp_round (&z); /* round */ + if (fpp_test_xp (&z)) /* ovf, unf? */ + return TRUE; + } +fpp_copy (a, &z); /* result is z */ +return FALSE; +} + +/* Integer multiply - returns true if overflow */ + +t_bool fpp_imul (FPN *a, FPN *b) +{ +uint32 sext; +FPN x, y, z; + +fpp_zcopy (&x, a); /* copy args */ +fpp_zcopy (&y, b); +fpp_fr_mul (z.fr, x.fr, y.fr); /* mult fracs */ +sext = (z.fr[2] & FPN_FRSIGN)? 07777: 0; +if (((z.fr[0] | z.fr[1] | sext) != 0) && /* hi 25b == 0 */ + ((z.fr[0] & z.fr[1] & sext) != 07777)) { /* or 777777774? */ + fpp_dump_apt (fpp_apta, FPS_IOVX); + return TRUE; + } +a->fr[0] = z.fr[2]; /* low 24b */ +a->fr[1] = z.fr[3]; +return FALSE; +} + +/* Auxiliary floating point routines */ + +t_bool fpp_cond_met (uint32 cond) +{ +switch (cond) { + + case 0: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0); + + case 1: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) >= 0); + + case 2: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) <= 0); + + case 3: + return 1; + + case 4: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) != 0); + + case 5: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) < 0); + + case 6: + return (fpp_fr_test (fpp_ac.fr, 0, EXACT) > 0); + + case 7: + return (fpp_ac.exp > 027); + } +return 0; +} + +/* Normalization - returns TRUE if rounding possible, FALSE if exact */ + +t_bool fpp_norm (FPN *a, uint32 cnt) +{ +if (fpp_fr_test (a->fr, 0, cnt) == 0) { /* zero? */ + a->exp = 0; /* clean exp */ + return FALSE; /* don't round */ + } +while (((a->fr[0] == 0) && !(a->fr[1] & 04000)) || /* lead 13b same? */ + ((a->fr[0] = 07777) & (a->fr[1] & 04000))) { + fpp_fr_lsh12 (a->fr, cnt); /* move word */ + a->exp = a->exp - 12; + } +while (((a->fr[0] ^ (a->fr[0] << 1)) & FPN_FRSIGN) == 0) { /* until norm */ + fpp_fr_lsh1 (a->fr, cnt); /* shift 1b */ + a->exp = a->exp - 1; + } +if (fpp_fr_test (a->fr, 04000, EXACT) == 0) { /* 4000...0000? */ + a->fr[0] = 06000; /* chg to 6000... */ + a->exp = a->exp + 1; /* with exp+1 */ + return FALSE; /* don't round */ + } +return TRUE; +} + +/* Exact fp number copy */ + +void fpp_copy (FPN *a, FPN *b) +{ +uint32 i; + +if (!(fpp_sta & FPS_DP)) + a->exp = b->exp; +for (i = 0; i < EXACT; i++) + a->fr[i] = b->fr[i]; +return; +} + +/* Zero extended fp number copy (60b) */ + +void fpp_zcopy (FPN *a, FPN *b) +{ +uint32 i; + +a->exp = b->exp; +for (i = 0; i < FPN_NFR_EP; i++) { + if ((i < FPN_NFR_FP) || (fpp_sta & FPS_EP)) + a->fr[i] = b->fr[i]; + else a->fr[i] = 0; + } +return; +} + +/* Test exp for overflow or underflow, returns TRUE on trap */ + +t_bool fpp_test_xp (FPN *a) +{ +if (a->exp > 2047) { /* overflow? */ + fpp_dump_apt (fpp_apta, FPS_FOVX); /* trap */ + return TRUE; + } +if (a->exp < -2048) { /* underflow? */ + fpp_sta |= FPS_UNF; /* set flag */ + if (fpp_sta & FPS_UNFX) { /* trap? */ + fpp_dump_apt (fpp_apta, FPS_UNFX); + return TRUE; + } + fpp_copy (a, &fpp_zero); /* flush to 0 */ + } +return FALSE; +} + +/* Round dp/fp value, returns carry out */ + +uint32 fpp_round (FPN *a) +{ +int32 i; +uint32 cin, afr0_sign; + +if (fpp_sta & FPS_EP) /* ep? */ + return FALSE; /* don't round */ +afr0_sign = a->fr[0] & FPN_FRSIGN; /* save input sign */ +cin = afr0_sign? 03777: 04000; +for (i = FPN_NFR_FP; i >= 0; i--) { /* 3 words */ + a->fr[i] = a->fr[i] + cin; /* add in carry */ + cin = (a->fr[i] >> 12) & 1; + a->fr[i] = a->fr[i] & 07777; + } +if (!(fpp_sta & FPS_DP) && /* fp? */ + (afr0_sign ^ (a->fr[0] & FPN_FRSIGN))) { /* sign change? */ + fpp_fr_rsh1 (a->fr, afr0_sign, EXACT); /* rsh, insert sign */ + a->exp = a->exp + 1; + } +return cin; +} + +/* N-precision integer routines */ + +/* Fraction add/sub - always carried out to 60b */ + +uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b) +{ +uint32 i, cin; + +for (i = FPN_NFR_EP, cin = 0; i > 0; i--) { + c[i - 1] = a[i - 1] + b[i - 1] + cin; + cin = (c[i - 1] >> 12) & 1; + c[i - 1] = c[i - 1] & 07777; + } +return cin; +} + +void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b) +{ +uint32 i, cin; + +for (i = FPN_NFR_EP, cin = 0; i > 0; i--) { + c[i - 1] = a[i - 1] - b[i - 1] - cin; + cin = (c[i - 1] >> 12) & 1; + c[i - 1] = c[i - 1] & 07777; + } +return; +} + +/* Fraction multiply - always develop 60b, multiply is + either 24b*24b or 60b*60b + + This is a signed multiply. The shift in for signed multiply is + technically ALU_N XOR ALU_V. This can be simplified as follows: + + a-sign c-sign result-sign cout overflow N XOR V = shift in + + 0 0 0 0 0 0 + 0 0 1 0 1 0 + 0 1 0 1 0 0 + 0 1 1 0 0 1 + 1 0 0 1 0 0 + 1 0 1 0 0 1 + 1 1 0 1 1 1 + 1 1 1 1 0 1 + + If a-sign == c-sign, shift-in = a-sign + If a-sign != c-sign, shift-in = result-sign + */ + +void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b) +{ +uint32 i, cnt, lo, c_old, cin; + +fpp_fr_fill (c, 0, EXTEND); /* clr answer */ +if (fpp_sta & FPS_EP) /* ep? */ + lo = FPN_NFR_EP - 1; /* test <59> */ +else lo = FPN_NFR_FP - 1; /* sp, test <23> */ +cnt = (lo + 1) * 12; /* # iterations */ +for (i = 0; i < cnt; i++) { /* loop thru mpcd */ + c_old = c[0]; + if (b[lo] & 1) /* mpcd bit set? */ + fpp_fr_add (c, a, c); /* add mpyr */ + cin = (((a[0] ^ c_old) & FPN_FRSIGN)? c[0]: a[0]) & FPN_FRSIGN; + fpp_fr_rsh1 (c, cin, EXTEND); /* shift answer */ + fpp_fr_rsh1 (b, 0, EXACT); /* shift mpcd */ + } +if (a[0] & FPN_FRSIGN) /* mpyr negative? */ + fpp_fr_sub (c, c, a); /* adjust result */ +return; +} + +/* Fraction divide */ + +t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b) +{ +uint32 i, old_c, lo, cnt, sign; + +fpp_fr_fill (c, 0, EXTEND); /* clr answer */ +sign = (a[0] ^ b[0]) & FPN_FRSIGN; /* sign of result */ +if (a[0] & FPN_FRSIGN) /* |a| */ + fpp_fr_neg (a, EXACT); +if (b[0] & FPN_FRSIGN); /* |b| */ + fpp_fr_neg (b, EXACT); +if (fpp_sta & FPS_EP) /* ep? 5 words */ + lo = FPN_NFR_EP - 1; +else lo = FPN_NFR_FP; /* fp, dp? 3 words */ +cnt = (lo + 1) * 12; +for (i = 0; i < cnt; i++) { /* loop */ + fpp_fr_lsh1 (c, EXTEND); /* shift quotient */ + if (fpp_fr_cmp (a, b, EXTEND) >= 0) { /* sub work? */ + fpp_fr_sub (a, a, b); /* divd - divr */ + if (a[0] & FPN_FRSIGN) /* sign flip? */ + return TRUE; /* no, overflow */ + c[lo] |= 1; /* set quo bit */ + } + fpp_fr_lsh1 (a, EXTEND); /* shift dividend */ + } +old_c = c[0]; /* save ho quo */ +if (sign) /* expect neg ans? */ + fpp_fr_neg (c, EXTEND); /* -quo */ +if (old_c & FPN_FRSIGN) /* sign set before */ + return TRUE; /* neg? */ +return FALSE; +} + +/* Negate - 24b or 60b */ + +uint32 fpp_fr_neg (uint32 *a, uint32 cnt) +{ +uint32 i, cin; + +for (i = cnt, cin = 1; i > 0; i--) { + a[i - 1] = (~a[i - 1] + cin) & 07777; + cin = (a[i - 1] == 0); + } +return cin; +} + +/* Test (compare to x'0...0) - 24b or 60b */ + +int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt) +{ +uint32 i; + +if (a[0] != v0) + return (a[0] & FPN_FRSIGN)? -1: +1; +for (i = 1; i < cnt; i++) { + if (a[i] != 0) + return (a[0] & FPN_FRSIGN)? -1: +1; + } +return 0; +} + +/* Fraction compare - 24b or 60b */ + +int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt) +{ +uint32 i; + +if ((a[0] ^ b[0]) & FPN_FRSIGN) + return (b[0] & FPN_FRSIGN)? +1: -1; +for (i = 0; i < cnt; i++) { + if (a[i] > b[i]) + return (b[0] & FPN_FRSIGN)? +1: -1; + if (a[i] < b[i]) + return (b[0] & FPN_FRSIGN)? -1: +1; + } +return 0; +} + +/* Fraction fill */ + +void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt) +{ +uint32 i; + +for (i = 0; i < cnt; i++) + a[i] = v; +return; +} + +/* Left shift n (unsigned) */ + +void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt) +{ +uint32 i; + +if (sc >= (cnt * 12)) { /* out of range? */ + fpp_fr_fill (a, 0, cnt); + return; + } +while (sc >= 12) { /* word shift? */ + fpp_fr_lsh12 (a, cnt); + sc = sc - 12; + } +if (sc == 0) /* any more? */ + return; +for (i = 1; i < cnt; i++) /* bit shift */ + a[i - 1] = ((a[i - 1] << sc) | (a[i] >> (12 - sc))) & 07777; +a[cnt - 1] = (a[cnt - 1] << sc) & 07777; +return; +} + +/* Left shift 12b (unsigned) */ + +void fpp_fr_lsh12 (uint32 *a, uint32 cnt) +{ +uint32 i; + +for (i = 1; i < cnt; i++) + a[i - 1] = a[i]; +a[cnt - 1] = 0; +return; +} + +/* Left shift 1b (unsigned) */ + +void fpp_fr_lsh1 (uint32 *a, uint32 cnt) +{ +uint32 i; + +for (i = 1; i < cnt; i++) + a[i - 1] = ((a[i - 1] << 1) | (a[i] >> 11)) & 07777; +a[cnt - 1] = (a[cnt - 1] << 1) & 07777; +return; +} + +/* Right shift 1b, with shift in */ + +void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt) +{ +uint32 i; + +for (i = cnt - 1; i > 0; i--) + a[i] = ((a[i] >> 1) | (a[i - 1] << 11)) & 07777; +a[0] = (a[0] >> 1) | sign; +return; +} + +/* Right shift n (signed) */ + +void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt) +{ +uint32 i, sign; + +sign = (a[0] & FPN_FRSIGN)? 07777: 0; +if (sc >= (cnt * 12)) { /* out of range? */ + fpp_fr_fill (a, sign, cnt); + return; + } +while (sc >= 12) { + for (i = cnt - 1; i > 0; i++) + a[i] = a[i - 1]; + a[0] = sign; + sc = sc - 12; + } +if (sc == 0) + return; +for (i = cnt - 1; i > 0; i--) + a[i] = ((a[i] >> sc) | (a[i - 1] << (12 - sc))) & 07777; +a[0] = ((a[0] >> sc) | (sign << (12 - sc))) & 07777; +return; +} + +/* Read/write routines */ + +void fpp_read_op (uint32 ea, FPN *a) +{ +uint32 i; + +fpp_opa = ea; +if (!(fpp_sta & FPS_DP)) { + a->exp = fpp_read (ea++); + a->exp = SEXT12 (a->exp); + } +for (i = 0; i < EXACT; i++) + a->fr[i] = fpp_read (ea + i); +return; +} + +void fpp_write_op (uint32 ea, FPN *a) +{ +uint32 i; + +fpp_opa = ea; +if (!(fpp_sta & FPS_DP)) + fpp_write (ea++, a->exp); +for (i = 0; i < EXACT; i++) + fpp_write (ea + i, a->fr[i]); +return; +} + +uint32 fpp_read (uint32 ea) +{ +ea = ea & ADDRMASK; +if (fpp_cmd & FPC_FIXF) + ea = fpp_aptsvf | (ea & 07777); +return M[ea]; +} + +void fpp_write (uint32 ea, uint32 val) +{ +ea = ea & ADDRMASK; +if (fpp_cmd & FPC_FIXF) + ea = fpp_aptsvf | (ea & 07777); +if (MEM_ADDR_OK (ea)) + M[ea] = val & 07777; +return; +} + +uint32 apt_read (uint32 ea) +{ +ea = ea & ADDRMASK; +return M[ea]; +} + +void apt_write (uint32 ea, uint32 val) +{ +ea = ea & ADDRMASK; +if (MEM_ADDR_OK (ea)) + M[ea] = val & 07777; +return; +} + +/* Utility routines */ + +void fpp_load_apt (uint32 ad) +{ +uint32 wd0, i; + +wd0 = apt_read (ad++); +fpp_fpc = ((wd0 & 07) << 12) | apt_read (ad++); +if (FPC_GETFAST (fpp_cmd) != 017) { + fpp_xra = ((wd0 & 00070) << 9) | apt_read (ad++); + fpp_bra = ((wd0 & 00700) << 6) | apt_read (ad++); + ad++; + fpp_ac.exp = apt_read (ad++); + for (i = 0; i < EXACT; i++) + fpp_ac.fr[i] = apt_read (ad++); + } +fpp_aptsvf = (ad - 1) & 070000; +fpp_sta |= FPS_RUN; +return; +} + +void fpp_dump_apt (uint32 ad, uint32 sta) +{ +uint32 wd0, i; + +wd0 = (fpp_fpc >> 12) & 07; +if (FPC_GETFAST (fpp_cmd) != 017) + wd0 = wd0 | + ((fpp_opa >> 3) & 07000) | + ((fpp_bra >> 6) & 00700) | + ((fpp_xra >> 9) & 00070); +apt_write (ad++, wd0); +apt_write (ad++, fpp_fpc); +if (FPC_GETFAST (fpp_cmd) != 017) { + apt_write (ad++, fpp_xra); + apt_write (ad++, fpp_bra); + apt_write (ad++, fpp_opa); + apt_write (ad++, fpp_ac.exp); + for (i = 0; i < EXACT; i++) + apt_write (ad++, fpp_ac.fr[i]); + } +fpp_sta = (fpp_sta | sta) & ~FPS_RUN; +fpp_flag = 1; +if (fpp_cmd & FPC_IE) + int_req |= INT_FPP; +return; +} + +/* Reset routine */ + +t_stat fpp_reset (DEVICE *dptr) +{ +sim_cancel (&fpp_unit); +fpp_sta = 0; +fpp_cmd = 0; +fpp_flag = 0; +int_req &= ~INT_FPP; +if (sim_switches & SWMASK ('P')) { + fpp_apta = 0; + fpp_aptsvf = 0; + fpp_fpc = 0; + fpp_bra = 0; + fpp_xra = 0; + fpp_opa = 0; + fpp_ac = fpp_zero; + } +return SCPE_OK; +} diff --git a/PDP8/pdp8_lp.c b/PDP8/pdp8_lp.c new file mode 100644 index 0000000..28f792f --- /dev/null +++ b/PDP8/pdp8_lp.c @@ -0,0 +1,183 @@ +/* pdp8_lp.c: PDP-8 line printer simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt LP8E line printer + + 19-Jan-07 RMS Added UNIT_TEXT + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIB, enable/disable, device number support + 30-May-02 RMS Widened POS to 32b +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, int_enable, dev_done, stop_inst; + +int32 lpt_err = 0; /* error flag */ +int32 lpt_stopioe = 0; /* stop on error */ + +DEVICE lpt_dev; +int32 lpt (int32 IR, int32 AC); +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat lpt_detach (UNIT *uptr); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { DEV_LPT, 1, { &lpt } }; + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0), SERIAL_OUT_WAIT + }; + +REG lpt_reg[] = { + { ORDATA (BUF, lpt_unit.buf, 8) }, + { FLDATA (ERR, lpt_err, 0) }, + { FLDATA (DONE, dev_done, INT_V_LPT) }, + { FLDATA (ENABLE, int_enable, INT_V_LPT) }, + { FLDATA (INT, int_req, INT_V_LPT) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, lpt_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { ORDATA (DEVNUM, lpt_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB lpt_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, &lpt_detach, + &lpt_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 lpt (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* PSKF */ + return (dev_done & INT_LPT)? IOT_SKP + AC: AC; + + case 2: /* PCLF */ + dev_done = dev_done & ~INT_LPT; /* clear flag */ + int_req = int_req & ~INT_LPT; /* clear int req */ + return AC; + + case 3: /* PSKE */ + return (lpt_err)? IOT_SKP + AC: AC; + + case 6: /* PCLF!PSTB */ + dev_done = dev_done & ~INT_LPT; /* clear flag */ + int_req = int_req & ~INT_LPT; /* clear int req */ + + case 4: /* PSTB */ + lpt_unit.buf = AC & 0177; /* load buffer */ + if ((lpt_unit.buf == 015) || (lpt_unit.buf == 014) || + (lpt_unit.buf == 012)) { + sim_activate (&lpt_unit, lpt_unit.wait); + return AC; + } + return (lpt_svc (&lpt_unit) << IOT_V_REASON) + AC; + + case 5: /* PSIE */ + int_enable = int_enable | INT_LPT; /* set enable */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 7: /* PCIE */ + int_enable = int_enable & ~INT_LPT; /* clear enable */ + int_req = int_req & ~INT_LPT; /* clear int req */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat lpt_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_LPT; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +if ((uptr->flags & UNIT_ATT) == 0) { + lpt_err = 1; + return IORETURN (lpt_stopioe, SCPE_UNATT); + } +fputc (uptr->buf, uptr->fileref); /* print char */ +uptr->pos = ftell (uptr->fileref); +if (ferror (uptr->fileref)) { /* error? */ + perror ("LPT I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +lpt_unit.buf = 0; +dev_done = dev_done & ~INT_LPT; /* clear done, int */ +int_req = int_req & ~INT_LPT; +int_enable = int_enable | INT_LPT; /* set enable */ +lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; +sim_cancel (&lpt_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +t_stat reason; + +reason = attach_unit (uptr, cptr); +lpt_err = (lpt_unit.flags & UNIT_ATT) == 0; +return reason; +} + +/* Detach routine */ + +t_stat lpt_detach (UNIT *uptr) +{ +lpt_err = 1; +return detach_unit (uptr); +} diff --git a/PDP8/pdp8_mt.c b/PDP8/pdp8_mt.c new file mode 100644 index 0000000..41dc2d3 --- /dev/null +++ b/PDP8/pdp8_mt.c @@ -0,0 +1,644 @@ +/* pdp8_mt.c: PDP-8 magnetic tape simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt TM8E/TU10 magtape + + 16-Feb-06 RMS Added tape capacity checking + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 18-Mar-05 RMS Added attached test to detach routine + 25-Apr-03 RMS Revised for extended file support + 29-Mar-03 RMS Added multiformat support + 04-Mar-03 RMS Fixed bug in SKTR + 01-Mar-03 RMS Fixed interrupt handling + Revised for magtape library + 30-Oct-02 RMS Revised BOT handling, added error record handling + 04-Oct-02 RMS Added DIBs, device number support + 30-Aug-02 RMS Revamped error handling + 28-Aug-02 RMS Added end of medium support + 30-May-02 RMS Widened POS to 32b + 22-Apr-02 RMS Added maximum record length test + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Changed UST, POS, FLG to arrays + 25-Apr-01 RMS Added device enable/disable support + 04-Oct-98 RMS V2.4 magtape format + 22-Jan-97 RMS V2.3 magtape format + 01-Jan-96 RMS Rewritten from TM8-E Maintenance Manual + + Magnetic tapes are represented as a series of variable records + of the form: + + 32b byte count + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b byte count + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a byte count of 0. +*/ + +#include "pdp8_defs.h" +#include "sim_tape.h" + +#define MT_NUMDR 8 /* #drives */ +#define USTAT u3 /* unit status */ +#define MT_MAXFR (1 << 16) /* max record lnt */ +#define WC_SIZE (1 << 12) /* max word count */ +#define WC_MASK (WC_SIZE - 1) + +/* Command/unit - mt_cu */ + +#define CU_V_UNIT 9 /* unit */ +#define CU_M_UNIT 07 +#define CU_PARITY 00400 /* parity select */ +#define CU_IEE 00200 /* error int enable */ +#define CU_IED 00100 /* done int enable */ +#define CU_V_EMA 3 /* ext mem address */ +#define CU_M_EMA 07 +#define CU_EMA (CU_M_EMA << CU_V_EMA) +#define CU_DTY 00002 /* drive type */ +#define CU_UNPAK 00001 /* 6b vs 8b mode */ +#define GET_UNIT(x) (((x) >> CU_V_UNIT) & CU_M_UNIT) +#define GET_EMA(x) (((x) & CU_EMA) << (12 - CU_V_EMA)) + +/* Function - mt_fn */ + +#define FN_V_FNC 9 /* function */ +#define FN_M_FNC 07 +#define FN_UNLOAD 00 +#define FN_REWIND 01 +#define FN_READ 02 +#define FN_CMPARE 03 +#define FN_WRITE 04 +#define FN_WREOF 05 +#define FN_SPACEF 06 +#define FN_SPACER 07 +#define FN_ERASE 00400 /* erase */ +#define FN_CRC 00200 /* read CRC */ +#define FN_GO 00100 /* go */ +#define FN_INC 00040 /* incr mode */ +#define FN_RMASK 07700 /* readable bits */ +#define GET_FNC(x) (((x) >> FN_V_FNC) & FN_M_FNC) + +/* Status - stored in mt_sta or (*) uptr->USTAT */ + +#define STA_ERR (04000 << 12) /* error */ +#define STA_REW (02000 << 12) /* *rewinding */ +#define STA_BOT (01000 << 12) /* *start of tape */ +#define STA_REM (00400 << 12) /* *offline */ +#define STA_PAR (00200 << 12) /* parity error */ +#define STA_EOF (00100 << 12) /* *end of file */ +#define STA_RLE (00040 << 12) /* rec lnt error */ +#define STA_DLT (00020 << 12) /* data late */ +#define STA_EOT (00010 << 12) /* *end of tape */ +#define STA_WLK (00004 << 12) /* *write locked */ +#define STA_CPE (00002 << 12) /* compare error */ +#define STA_ILL (00001 << 12) /* illegal */ +#define STA_9TK 00040 /* 9 track */ +/* #define STA_BAD 00020 /* bad tape?? */ +#define STA_INC 00010 /* increment error */ +#define STA_LAT 00004 /* lateral par error */ +#define STA_CRC 00002 /* CRC error */ +#define STA_LON 00001 /* long par error */ + +#define STA_CLR (FN_RMASK | 00020) /* always clear */ +#define STA_DYN (STA_REW | STA_BOT | STA_REM | STA_EOF | \ + STA_EOT | STA_WLK) /* kept in USTAT */ + +extern uint16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +int32 mt_cu = 0; /* command/unit */ +int32 mt_fn = 0; /* function */ +int32 mt_ca = 0; /* current address */ +int32 mt_wc = 0; /* word count */ +int32 mt_sta = 0; /* status register */ +int32 mt_db = 0; /* data buffer */ +int32 mt_done = 0; /* mag tape flag */ +int32 mt_time = 10; /* record latency */ +int32 mt_stopioe = 1; /* stop on error */ +uint8 *mtxb = NULL; /* transfer buffer */ + +DEVICE mt_dev; +int32 mt70 (int32 IR, int32 AC); +int32 mt71 (int32 IR, int32 AC); +int32 mt72 (int32 IR, int32 AC); +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_detach (UNIT *uptr); +int32 mt_updcsta (UNIT *uptr); +int32 mt_ixma (int32 xma); +t_stat mt_map_err (UNIT *uptr, t_stat st); +t_stat mt_vlock (UNIT *uptr, int32 val, char *cptr, void *desc); +UNIT *mt_busy (void); +void mt_set_done (void); + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit list + mt_reg MT register list + mt_mod MT modifier list +*/ + +DIB mt_dib = { DEV_MT, 3, { &mt70, &mt71, &mt72 } }; + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE+UNIT_DISABLE+UNIT_ROABLE, 0) } + }; + +REG mt_reg[] = { + { ORDATA (CMD, mt_cu, 12) }, + { ORDATA (FNC, mt_fn, 12) }, + { ORDATA (CA, mt_ca, 12) }, + { ORDATA (WC, mt_wc, 12) }, + { ORDATA (DB, mt_db, 12) }, + { GRDATA (STA, mt_sta, 8, 12, 12) }, + { ORDATA (STA2, mt_sta, 6) }, + { FLDATA (DONE, mt_done, 0) }, + { FLDATA (INT, int_req, INT_V_MT) }, + { FLDATA (STOP_IOE, mt_stopioe, 0) }, + { DRDATA (TIME, mt_time, 24), PV_LEFT }, + { URDATA (UST, mt_unit[0].USTAT, 8, 16, 0, MT_NUMDR, 0) }, + { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR, PV_LEFT | REG_RO) }, + { FLDATA (DEVNUM, mt_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", &mt_vlock }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", &mt_vlock }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + NULL, &mt_attach, &mt_detach, + &mt_dib, DEV_DISABLE + }; + +/* IOT routines */ + +int32 mt70 (int32 IR, int32 AC) +{ +int32 f; +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* LWCR */ + mt_wc = AC; /* load word count */ + return 0; + + case 2: /* CWCR */ + mt_wc = 0; /* clear word count */ + return AC; + + case 3: /* LCAR */ + mt_ca = AC; /* load mem address */ + return 0; + + case 4: /* CCAR */ + mt_ca = 0; /* clear mem address */ + return AC; + + case 5: /* LCMR */ + if (mt_busy ()) mt_sta = mt_sta | STA_ILL | STA_ERR; /* busy? illegal op */ + mt_cu = AC; /* load command reg */ + mt_updcsta (mt_dev.units + GET_UNIT (mt_cu)); + return 0; + + case 6: /* LFGR */ + if (mt_busy ()) mt_sta = mt_sta | STA_ILL | STA_ERR; /* busy? illegal op */ + mt_fn = AC; /* load function */ + if ((mt_fn & FN_GO) == 0) { /* go set? */ + mt_updcsta (uptr); /* update status */ + return 0; + } + f = GET_FNC (mt_fn); /* get function */ + if (((uptr->flags & UNIT_ATT) == 0) || + sim_is_active (uptr) || + (((f == FN_WRITE) || (f == FN_WREOF)) && sim_tape_wrp (uptr)) + || (((f == FN_SPACER) || (f == FN_REWIND)) && sim_tape_bot (uptr))) { + mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal op error */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return 0; + } + uptr->USTAT = uptr->USTAT & STA_WLK; /* clear status */ + if (f == FN_UNLOAD) { /* unload? */ + detach_unit (uptr); /* set offline */ + uptr->USTAT = STA_REW | STA_REM; /* rewinding, off */ + mt_set_done (); /* set done */ + } + else if (f == FN_REWIND) { /* rewind */ + uptr->USTAT = uptr->USTAT | STA_REW; /* rewinding */ + mt_set_done (); /* set done */ + } + else mt_done = 0; /* clear done */ + mt_updcsta (uptr); /* update status */ + sim_activate (uptr, mt_time); /* start io */ + return 0; + + case 7: /* LDBR */ + if (mt_busy ()) mt_sta = mt_sta | STA_ILL | STA_ERR; /* busy? illegal op */ + mt_db = AC; /* load buffer */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return 0; + } /* end switch */ + +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ +} + +int32 mt71 (int32 IR, int32 AC) +{ +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* RWCR */ + return mt_wc; /* read word count */ + + case 2: /* CLT */ + mt_reset (&mt_dev); /* reset everything */ + return AC; + + case 3: /* RCAR */ + return mt_ca; /* read mem address */ + + case 4: /* RMSR */ + return ((mt_updcsta (uptr) >> 12) & 07777); /* read status */ + + case 5: /* RCMR */ + return mt_cu; /* read command */ + + case 6: /* RFSR */ + return (((mt_fn & FN_RMASK) | (mt_updcsta (uptr) & ~FN_RMASK)) + & 07777); /* read function */ + + case 7: /* RDBR */ + return mt_db; /* read data buffer */ + } + +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ +} + +int32 mt72 (int32 IR, int32 AC) +{ +UNIT *uptr; + +uptr = mt_dev.units + GET_UNIT (mt_cu); /* get unit */ +switch (IR & 07) { /* decode IR<9:11> */ + + case 1: /* SKEF */ + return (mt_sta & STA_ERR)? IOT_SKP + AC: AC; + + case 2: /* SKCB */ + return (!mt_busy ())? IOT_SKP + AC: AC; + + case 3: /* SKJD */ + return mt_done? IOT_SKP + AC: AC; + + case 4: /* SKTR */ + return (!sim_is_active (uptr) && + (uptr->flags & UNIT_ATT))? IOT_SKP + AC: AC; + + case 5: /* CLF */ + if (!sim_is_active (uptr)) mt_reset (&mt_dev); /* if TUR, zap */ + else { /* just ctrl zap */ + mt_sta = 0; /* clear status */ + mt_done = 0; /* clear done */ + mt_updcsta (uptr); /* update status */ + } + return AC; + } /* end switch */ + +return (stop_inst << IOT_V_REASON) + AC; /* ill inst */ +} + +/* Unit service + + If rewind done, reposition to start of tape, set status + else, do operation, set done, interrupt +*/ + +t_stat mt_svc (UNIT *uptr) +{ +int32 f, i, p, u, wc, xma; +t_mtrlnt tbc, cbc; +t_bool passed_eot; +uint16 c, c1, c2; +t_stat st, r = SCPE_OK; + +u = (int32) (uptr - mt_dev.units); /* get unit number */ +f = GET_FNC (mt_fn); /* get command */ +xma = GET_EMA (mt_cu) + mt_ca; /* get mem addr */ +wc = WC_SIZE - mt_wc; /* get wc */ + +if (uptr->USTAT & STA_REW) { /* rewind? */ + sim_tape_rewind (uptr); /* update position */ + if (uptr->flags & UNIT_ATT) /* still on line? */ + uptr->USTAT = (uptr->USTAT & STA_WLK) | STA_BOT; + else uptr->USTAT = STA_REM; + if (u == GET_UNIT (mt_cu)) { /* selected? */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + } + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* if not attached */ + uptr->USTAT = STA_REM; /* unit off line */ + mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ + mt_set_done (); /* set done */ + mt_updcsta (uptr); /* update status */ + return IORETURN (mt_stopioe, SCPE_UNATT); + } + +passed_eot = sim_tape_eot (uptr); /* passed eot? */ +switch (f) { /* case on function */ + + case FN_READ: /* read */ + case FN_CMPARE: /* read/compare */ + st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); /* read rec */ + if (st == MTSE_RECE) mt_sta = mt_sta | STA_PAR | STA_ERR; /* rec in err? */ + else if (st != MTSE_OK) { /* other error? */ + r = mt_map_err (uptr, st); /* map error */ + mt_sta = mt_sta | STA_RLE | STA_ERR; /* err, eof/eom, tmk */ + break; + } + cbc = (mt_cu & CU_UNPAK)? wc: wc * 2; /* expected bc */ + if (tbc != cbc) mt_sta = mt_sta | STA_RLE | STA_ERR; /* wrong size? */ + if (tbc < cbc) { /* record small? */ + cbc = tbc; /* use smaller */ + wc = (mt_cu & CU_UNPAK)? cbc: (cbc + 1) / 2; + } + for (i = p = 0; i < wc; i++) { /* copy buffer */ + xma = mt_ixma (xma); /* increment xma */ + mt_wc = (mt_wc + 1) & 07777; /* incr word cnt */ + if (mt_cu & CU_UNPAK) c = mtxb[p++]; + else { + c1 = mtxb[p++] & 077; + c2 = mtxb[p++] & 077; + c = (c1 << 6) | c2; + } + if ((f == FN_READ) && MEM_ADDR_OK (xma)) M[xma] = c; + else if ((f == FN_CMPARE) && (M[xma] != c)) { + mt_sta = mt_sta | STA_CPE | STA_ERR; + break; + } + } + break; + + case FN_WRITE: /* write */ + tbc = (mt_cu & CU_UNPAK)? wc: wc * 2; + for (i = p = 0; i < wc; i++) { /* copy buf to tape */ + xma = mt_ixma (xma); /* incr mem addr */ + if (mt_cu & CU_UNPAK) mtxb[p++] = M[xma] & 0377; + else { + mtxb[p++] = (M[xma] >> 6) & 077; + mtxb[p++] = M[xma] & 077; + } + } + if (st = sim_tape_wrrecf (uptr, mtxb, tbc)) { /* write rec, err? */ + r = mt_map_err (uptr, st); /* map error */ + xma = GET_EMA (mt_cu) + mt_ca; /* restore xma */ + } + else mt_wc = 0; /* ok, clear wc */ + break; + + case FN_WREOF: + if (st = sim_tape_wrtmk (uptr)) /* write tmk, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; + + case FN_SPACEF: /* space forward */ + do { + mt_wc = (mt_wc + 1) & 07777; /* incr wc */ + if (st = sim_tape_sprecf (uptr, &tbc)) { /* space rec fwd, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* stop */ + } + } while ((mt_wc != 0) && (passed_eot || !sim_tape_eot (uptr))); + break; + + case FN_SPACER: /* space reverse */ + do { + mt_wc = (mt_wc + 1) & 07777; /* incr wc */ + if (st = sim_tape_sprecr (uptr, &tbc)) { /* space rec rev, err? */ + r = mt_map_err (uptr, st); /* map error */ + break; /* stop */ + } + } while (mt_wc != 0); + break; + } /* end case */ + +if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ + uptr->USTAT = uptr->USTAT | STA_EOT; +mt_cu = (mt_cu & ~CU_EMA) | ((xma >> (12 - CU_V_EMA)) & CU_EMA); +mt_ca = xma & 07777; /* update mem addr */ +mt_set_done (); /* set done */ +mt_updcsta (uptr); /* update status */ +return r; +} + +/* Update controller status */ + +int32 mt_updcsta (UNIT *uptr) +{ +mt_sta = (mt_sta & ~(STA_DYN | STA_CLR)) | (uptr->USTAT & STA_DYN); +if (((mt_sta & STA_ERR) && (mt_cu & CU_IEE)) || + (mt_done && (mt_cu & CU_IED))) int_req = int_req | INT_MT; +else int_req = int_req & ~INT_MT; +return mt_sta; +} + +/* Test if controller busy */ + +UNIT *mt_busy (void) +{ +int32 u; +UNIT *uptr; + +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + if (sim_is_active (uptr) && ((uptr->USTAT & STA_REW) == 0)) + return uptr; + } +return NULL; +} + +/* Increment extended memory address */ + +int32 mt_ixma (int32 xma) /* incr extended ma */ +{ +int32 v; + +v = ((xma + 1) & 07777) | (xma & 070000); /* wrapped incr */ +if (mt_fn & FN_INC) { /* increment mode? */ + if (xma == 077777) mt_sta = mt_sta | STA_INC | STA_ERR; /* at limit? error */ + else v = xma + 1; /* else 15b incr */ + } +return v; +} + +/* Set done */ + +void mt_set_done (void) +{ +mt_done = 1; /* set done */ +mt_fn = mt_fn & ~(FN_CRC | FN_GO | FN_INC); /* clear func<4:6> */ +return; +} + +/* Map tape error status */ + +t_stat mt_map_err (UNIT *uptr, t_stat st) +{ +switch (st) { + + case MTSE_FMT: /* illegal fmt */ + case MTSE_UNATT: /* unattached */ + mt_sta = mt_sta | STA_ILL | STA_ERR; + case MTSE_OK: /* no error */ + return SCPE_IERR; /* never get here! */ + + case MTSE_TMK: /* end of file */ + uptr->USTAT = uptr->USTAT | STA_EOF; /* set EOF */ + mt_sta = mt_sta | STA_ERR; + break; + + case MTSE_IOERR: /* IO error */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + if (mt_stopioe) return SCPE_IOERR; + break; + + case MTSE_INVRL: /* invalid rec lnt */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + return SCPE_MTRLNT; + + case MTSE_RECE: /* record in error */ + case MTSE_EOM: /* end of medium */ + mt_sta = mt_sta | STA_PAR | STA_ERR; /* set par err */ + break; + + case MTSE_BOT: /* reverse into BOT */ + uptr->USTAT = uptr->USTAT | STA_BOT; /* set status */ + mt_sta = mt_sta | STA_ERR; + break; + + case MTSE_WRP: /* write protect */ + mt_sta = mt_sta | STA_ILL | STA_ERR; /* illegal operation */ + break; + } + +return SCPE_OK; +} + +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 u; +UNIT *uptr; + +mt_cu = mt_fn = mt_wc = mt_ca = mt_db = mt_sta = mt_done = 0; +int_req = int_req & ~INT_MT; /* clear interrupt */ +for (u = 0; u < MT_NUMDR; u++) { /* loop thru units */ + uptr = mt_dev.units + u; + sim_cancel (uptr); /* cancel activity */ + sim_tape_reset (uptr); /* reset tape */ + if (uptr->flags & UNIT_ATT) uptr->USTAT = + (sim_tape_bot (uptr)? STA_BOT: 0) | + (sim_tape_wrp (uptr)? STA_WLK: 0); + else uptr->USTAT = STA_REM; + } +if (mtxb == NULL) mtxb = (uint8 *) calloc (MT_MAXFR, sizeof (uint8)); +if (mtxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +t_stat r; +int32 u = uptr - mt_dev.units; /* get unit number */ + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->USTAT = STA_BOT | (sim_tape_wrp (uptr)? STA_WLK: 0); +if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr); +return r; +} + +/* Detach routine */ + +t_stat mt_detach (UNIT* uptr) +{ +int32 u = uptr - mt_dev.units; /* get unit number */ + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* check for attached */ +if (!sim_is_active (uptr)) uptr->USTAT = STA_REM; +if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr); +return sim_tape_detach (uptr); +} + +/* Write lock/enable routine */ + +t_stat mt_vlock (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 u = uptr - mt_dev.units; /* get unit number */ + +if ((uptr->flags & UNIT_ATT) && (val || sim_tape_wrp (uptr))) + uptr->USTAT = uptr->USTAT | STA_WLK; +else uptr->USTAT = uptr->USTAT & ~STA_WLK; +if (u == GET_UNIT (mt_cu)) mt_updcsta (uptr); +return SCPE_OK; +} diff --git a/PDP8/pdp8_pt.c b/PDP8/pdp8_pt.c new file mode 100644 index 0000000..787ba31 --- /dev/null +++ b/PDP8/pdp8_pt.c @@ -0,0 +1,288 @@ +/* pdp8_pt.c: PDP-8 paper tape reader/punch simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr,ptp PC8E paper tape reader/punch + + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIBs + 30-May-02 RMS Widened POS to 32b + 30-Nov-01 RMS Added read only unit support + 30-Mar-98 RMS Added RIM loader as PTR bootstrap +*/ + +#include "pdp8_defs.h" + +extern int32 int_req, int_enable, dev_done, stop_inst; + +int32 ptr_stopioe = 0, ptp_stopioe = 0; /* stop on error */ + +int32 ptr (int32 IR, int32 AC); +int32 ptp (int32 IR, int32 AC); +t_stat ptr_svc (UNIT *uptr); +t_stat ptp_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit descriptor + ptr_reg PTR register list +*/ + +DIB ptr_dib = { DEV_PTR, 1, { &ptr } }; + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_PTR) }, + { FLDATA (ENABLE, int_enable, INT_V_PTR) }, + { FLDATA (INT, int_req, INT_V_PTR) }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } + }; + +MTAB ptr_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL, + &ptr_dib, 0 }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit descriptor + ptp_reg PTP register list +*/ + +DIB ptp_dib = { DEV_PTP, 1, { &ptp } }; + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_PTP) }, + { FLDATA (ENABLE, int_enable, INT_V_PTP) }, + { FLDATA (INT, int_req, INT_V_PTP) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +MTAB ptp_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + &ptp_dib, 0 + }; + +/* Paper tape reader: IOT routine */ + +int32 ptr (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* RPE */ + int_enable = int_enable | (INT_PTR+INT_PTP); /* set enable */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 1: /* RSF */ + return (dev_done & INT_PTR)? IOT_SKP + AC: AC; + + case 6: /* RFC!RRB */ + sim_activate (&ptr_unit, ptr_unit.wait); + case 2: /* RRB */ + dev_done = dev_done & ~INT_PTR; /* clear flag */ + int_req = int_req & ~INT_PTR; /* clear int req */ + return (AC | ptr_unit.buf); /* or data to AC */ + + case 4: /* RFC */ + sim_activate (&ptr_unit, ptr_unit.wait); + dev_done = dev_done & ~INT_PTR; /* clear flag */ + int_req = int_req & ~INT_PTR; /* clear int req */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptr_stopioe, SCPE_UNATT); +if ((temp = getc (ptr_unit.fileref)) == EOF) { + if (feof (ptr_unit.fileref)) { + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } +dev_done = dev_done | INT_PTR; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +ptr_unit.buf = temp & 0377; +ptr_unit.pos = ptr_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +ptr_unit.buf = 0; +dev_done = dev_done & ~INT_PTR; /* clear done, int */ +int_req = int_req & ~INT_PTR; +int_enable = int_enable | INT_PTR; /* set enable */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Paper tape punch: IOT routine */ + +int32 ptp (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* PCE */ + int_enable = int_enable & ~(INT_PTR+INT_PTP); /* clear enables */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 1: /* PSF */ + return (dev_done & INT_PTP)? IOT_SKP + AC: AC; + + case 2: /* PCF */ + dev_done = dev_done & ~INT_PTP; /* clear flag */ + int_req = int_req & ~INT_PTP; /* clear int req */ + return AC; + + case 6: /* PLS */ + dev_done = dev_done & ~INT_PTP; /* clear flag */ + int_req = int_req & ~INT_PTP; /* clear int req */ + case 4: /* PPC */ + ptp_unit.buf = AC & 0377; /* load punch buf */ + sim_activate (&ptp_unit, ptp_unit.wait); /* activate unit */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +dev_done = dev_done | INT_PTP; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +if ((ptp_unit.flags & UNIT_ATT) == 0) /* attached? */ + return IORETURN (ptp_stopioe, SCPE_UNATT); +if (putc (ptp_unit.buf, ptp_unit.fileref) == EOF) { + perror ("PTP I/O error"); + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +ptp_unit.pos = ptp_unit.pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +ptp_unit.buf = 0; +dev_done = dev_done & ~INT_PTP; /* clear done, int */ +int_req = int_req & ~INT_PTP; +int_enable = int_enable | INT_PTP; /* set enable */ +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 07756 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06014, /* 7756, RFC */ + 06011, /* 7757, LOOP, RSF */ + 05357, /* JMP .-1 */ + 06016, /* RFC RRB */ + 07106, /* CLL RTL*/ + 07006, /* RTL */ + 07510, /* SPA*/ + 05374, /* JMP 7774 */ + 07006, /* RTL */ + 06011, /* RSF */ + 05367, /* JMP .-1 */ + 06016, /* RFC RRB */ + 07420, /* SNL */ + 03776, /* DCA I 7776 */ + 03376, /* 7774, DCA 7776 */ + 05357, /* JMP 7757 */ + 00000, /* 7776, 0 */ + 05301 /* 7777, JMP 7701 */ + }; + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 M[]; + +if (ptr_dib.dev != DEV_PTR) return STOP_NOTSTD; /* only std devno */ +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/PDP8/pdp8_rf.c b/PDP8/pdp8_rf.c new file mode 100644 index 0000000..74f4043 --- /dev/null +++ b/PDP8/pdp8_rf.c @@ -0,0 +1,437 @@ +/* pdp8_rf.c: RF08 fixed head disk simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rf RF08 fixed head disk + + 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) + 07-Jan-06 RMS Fixed unaligned register access bug (found by Doug Carman) + 04-Jan-04 RMS Changed sim_fsize calling sequence + 26-Oct-03 RMS Cleaned up buffer copy code + 26-Jul-03 RMS Fixed bug in set size routine + 14-Mar-03 RMS Fixed variable platter interaction with save/restore + 03-Mar-03 RMS Fixed autosizing + 02-Feb-03 RMS Added variable platter and autosizing support + 04-Oct-02 RMS Added DIB, device number support + 28-Nov-01 RMS Added RL8A support + 25-Apr-01 RMS Added device enable/disable support + 19-Mar-01 RMS Added disk monitor bootstrap, fixed IOT decoding + 15-Feb-01 RMS Fixed 3 cycle data break sequence + 14-Apr-99 RMS Changed t_addr to unsigned + 30-Mar-98 RMS Fixed bug in RF bootstrap + + The RF08 is a head-per-track disk. It uses the three cycle data break + facility. To minimize overhead, the entire RF08 is buffered in memory. + + Two timing parameters are provided: + + rf_time Interword timing, must be non-zero + rf_burst Burst mode, if 0, DMA occurs cycle by cycle; otherwise, + DMA occurs in a burst +*/ + +#include "pdp8_defs.h" +#include + +#define UNIT_V_AUTO (UNIT_V_UF + 0) /* autosize */ +#define UNIT_V_PLAT (UNIT_V_UF + 1) /* #platters - 1 */ +#define UNIT_M_PLAT 03 +#define UNIT_GETP(x) ((((x) >> UNIT_V_PLAT) & UNIT_M_PLAT) + 1) +#define UNIT_AUTO (1 << UNIT_V_AUTO) +#define UNIT_PLAT (UNIT_M_PLAT << UNIT_V_PLAT) + +/* Constants */ + +#define RF_NUMWD 2048 /* words/track */ +#define RF_NUMTR 128 /* tracks/disk */ +#define RF_DKSIZE (RF_NUMTR * RF_NUMWD) /* words/disk */ +#define RF_NUMDK 4 /* disks/controller */ +#define RF_WC 07750 /* word count */ +#define RF_MA 07751 /* mem address */ +#define RF_WMASK (RF_NUMWD - 1) /* word mask */ + +/* Parameters in the unit descriptor */ + +#define FUNC u4 /* function */ +#define RF_READ 2 /* read */ +#define RF_WRITE 4 /* write */ + +/* Status register */ + +#define RFS_PCA 04000 /* photocell status */ +#define RFS_DRE 02000 /* data req enable */ +#define RFS_WLS 01000 /* write lock status */ +#define RFS_EIE 00400 /* error int enable */ +#define RFS_PIE 00200 /* photocell int enb */ +#define RFS_CIE 00100 /* done int enable */ +#define RFS_MEX 00070 /* memory extension */ +#define RFS_DRL 00004 /* data late error */ +#define RFS_NXD 00002 /* non-existent disk */ +#define RFS_PER 00001 /* parity error */ +#define RFS_ERR (RFS_WLS + RFS_DRL + RFS_NXD + RFS_PER) +#define RFS_V_MEX 3 + +#define GET_MEX(x) (((x) & RFS_MEX) << (12 - RFS_V_MEX)) +#define GET_POS(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) RF_NUMWD))) +#define UPDATE_PCELL if (GET_POS(rf_time) < 6) rf_sta = rf_sta | RFS_PCA; \ + else rf_sta = rf_sta & ~RFS_PCA +#define RF_INT_UPDATE if ((rf_done && (rf_sta & RFS_CIE)) || \ + ((rf_sta & RFS_ERR) && (rf_sta & RFS_EIE)) || \ + ((rf_sta & RFS_PCA) && (rf_sta & RFS_PIE))) \ + int_req = int_req | INT_RF; \ + else int_req = int_req & ~INT_RF + +extern uint16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +int32 rf_sta = 0; /* status register */ +int32 rf_da = 0; /* disk address */ +int32 rf_done = 0; /* done flag */ +int32 rf_wlk = 0; /* write lock */ +int32 rf_time = 10; /* inter-word time */ +int32 rf_burst = 1; /* burst mode flag */ +int32 rf_stopioe = 1; /* stop on error */ + +DEVICE rf_dev; +int32 rf60 (int32 IR, int32 AC); +int32 rf61 (int32 IR, int32 AC); +int32 rf62 (int32 IR, int32 AC); +int32 rf64 (int32 IR, int32 AC); +t_stat rf_svc (UNIT *uptr); +t_stat pcell_svc (UNIT *uptr); +t_stat rf_reset (DEVICE *dptr); +t_stat rf_boot (int32 unitno, DEVICE *dptr); +t_stat rf_attach (UNIT *uptr, char *cptr); +t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* RF08 data structures + + rf_dev RF device descriptor + rf_unit RF unit descriptor + pcell_unit photocell timing unit (orphan) + rf_reg RF register list +*/ + +DIB rf_dib = { DEV_RF, 5, { &rf60, &rf61, &rf62, NULL, &rf64 } }; + +UNIT rf_unit = { + UDATA (&rf_svc, UNIT_FIX+UNIT_ATTABLE+ + UNIT_BUFABLE+UNIT_MUSTBUF, RF_DKSIZE) + }; + +UNIT pcell_unit = { UDATA (&pcell_svc, 0, 0) }; + +REG rf_reg[] = { + { ORDATA (STA, rf_sta, 12) }, + { ORDATA (DA, rf_da, 20) }, + { ORDATA (WC, M[RF_WC], 12), REG_FIT }, + { ORDATA (MA, M[RF_MA], 12), REG_FIT }, + { FLDATA (DONE, rf_done, 0) }, + { FLDATA (INT, int_req, INT_V_RF) }, + { ORDATA (WLK, rf_wlk, 32) }, + { DRDATA (TIME, rf_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (BURST, rf_burst, 0) }, + { FLDATA (STOP_IOE, rf_stopioe, 0) }, + { DRDATA (CAPAC, rf_unit.capac, 21), REG_HRO }, + { ORDATA (DEVNUM, rf_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rf_mod[] = { + { UNIT_PLAT, (0 << UNIT_V_PLAT), NULL, "1P", &rf_set_size }, + { UNIT_PLAT, (1 << UNIT_V_PLAT), NULL, "2P", &rf_set_size }, + { UNIT_PLAT, (2 << UNIT_V_PLAT), NULL, "3P", &rf_set_size }, + { UNIT_PLAT, (3 << UNIT_V_PLAT), NULL, "4P", &rf_set_size }, + { UNIT_AUTO, UNIT_AUTO, "autosize", "AUTOSIZE", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rf_dev = { + "RF", &rf_unit, rf_reg, rf_mod, + 1, 8, 20, 1, 8, 12, + NULL, NULL, &rf_reset, + &rf_boot, &rf_attach, NULL, + &rf_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 rf60 (int32 IR, int32 AC) +{ +int32 t; +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DCMA */ + rf_da = rf_da & ~07777; /* clear DAR<8:19> */ + rf_done = 0; /* clear done */ + rf_sta = rf_sta & ~RFS_ERR; /* clear errors */ + RF_INT_UPDATE; /* update int req */ + } +if (pulse & 6) { /* DMAR, DMAW */ + rf_da = rf_da | AC; /* DAR<8:19> |= AC */ + rf_unit.FUNC = pulse & ~1; /* save function */ + t = (rf_da & RF_WMASK) - GET_POS (rf_time); /* delta to new loc */ + if (t < 0) t = t + RF_NUMWD; /* wrap around? */ + sim_activate (&rf_unit, t * rf_time); /* schedule op */ + AC = 0; /* clear AC */ + } +return AC; +} + +int32 rf61 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ + + case 1: /* DCIM */ + rf_sta = rf_sta & 07007; /* clear STA<3:8> */ + int_req = int_req & ~INT_RF; /* clear int req */ + sim_cancel (&pcell_unit); /* cancel photocell */ + return AC; + + case 2: /* DSAC */ + return ((rf_da & RF_WMASK) == GET_POS (rf_time))? IOT_SKP: 0; + + case 5: /* DIML */ + rf_sta = (rf_sta & 07007) | (AC & 0770); /* STA<3:8> <- AC */ + if (rf_sta & RFS_PIE) /* photocell int? */ + sim_activate (&pcell_unit, (RF_NUMWD - GET_POS (rf_time)) * + rf_time); + else sim_cancel (&pcell_unit); + RF_INT_UPDATE; /* update int req */ + return 0; /* clear AC */ + + case 6: /* DIMA */ + return rf_sta; /* AC <- STA<0:11> */ + } + +return AC; +} + +int32 rf62 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +if (pulse & 1) { /* DFSE */ + if (rf_sta & RFS_ERR) AC = AC | IOT_SKP; + } +if (pulse & 2) { /* DFSC */ + if (pulse & 4) AC = AC & ~07777; /* for DMAC */ + else if (rf_done) AC = AC | IOT_SKP; + } +if (pulse & 4) AC = AC | (rf_da & 07777); /* DMAC */ +return AC; +} + +int32 rf64 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; + +UPDATE_PCELL; /* update photocell */ +switch (pulse) { /* decode IR<9:11> */ + + case 1: /* DCXA */ + rf_da = rf_da & 07777; /* clear DAR<0:7> */ + break; + + case 3: /* DXAL */ + rf_da = rf_da & 07777; /* clear DAR<0:7> */ + case 2: /* DXAL w/o clear */ + rf_da = rf_da | ((AC & 0377) << 12); /* DAR<0:7> |= AC */ + AC = 0; /* clear AC */ + break; + + case 5: /* DXAC */ + AC = 0; /* clear AC */ + case 4: /* DXAC w/o clear */ + AC = AC | ((rf_da >> 12) & 0377); /* AC |= DAR<0:7> */ + break; + + default: + AC = (stop_inst << IOT_V_REASON) + AC; + break; + } /* end switch */ + +if ((uint32) rf_da >= rf_unit.capac) rf_sta = rf_sta | RFS_NXD; +else rf_sta = rf_sta & ~RFS_NXD; +RF_INT_UPDATE; +return AC; +} + +/* Unit service + + Note that for reads and writes, memory addresses wrap around in the + current field. This code assumes the entire disk is buffered. +*/ + +t_stat rf_svc (UNIT *uptr) +{ +int32 pa, t, mex; +int16 *fbuf = uptr->filebuf; + +UPDATE_PCELL; /* update photocell */ +if ((uptr->flags & UNIT_BUF) == 0) { /* not buf? abort */ + rf_sta = rf_sta | RFS_NXD; + rf_done = 1; + RF_INT_UPDATE; /* update int req */ + return IORETURN (rf_stopioe, SCPE_UNATT); + } + +mex = GET_MEX (rf_sta); +do { + if ((uint32) rf_da >= rf_unit.capac) { /* disk overflow? */ + rf_sta = rf_sta | RFS_NXD; + break; + } + M[RF_WC] = (M[RF_WC] + 1) & 07777; /* incr word count */ + M[RF_MA] = (M[RF_MA] + 1) & 07777; /* incr mem addr */ + pa = mex | M[RF_MA]; /* add extension */ + if (uptr->FUNC == RF_READ) { /* read? */ + if (MEM_ADDR_OK (pa)) /* if !nxm */ + M[pa] = fbuf[rf_da]; /* read word */ + } + else { /* write */ + t = ((rf_da >> 15) & 030) | ((rf_da >> 14) & 07); + if ((rf_wlk >> t) & 1) /* write locked? */ + rf_sta = rf_sta | RFS_WLS; + else { /* not locked */ + fbuf[rf_da] = M[pa]; /* write word */ + if (((uint32) rf_da) >= uptr->hwmark) uptr->hwmark = rf_da + 1; + } + } + rf_da = (rf_da + 1) & 03777777; /* incr disk addr */ + } while ((M[RF_WC] != 0) && (rf_burst != 0)); /* brk if wc, no brst */ + +if ((M[RF_WC] != 0) && ((rf_sta & RFS_ERR) == 0)) /* more to do? */ + sim_activate (&rf_unit, rf_time); /* sched next */ +else { + rf_done = 1; /* done */ + RF_INT_UPDATE; /* update int req */ + } +return SCPE_OK; +} + +/* Photocell unit service */ + +t_stat pcell_svc (UNIT *uptr) +{ +rf_sta = rf_sta | RFS_PCA; /* set photocell */ +if (rf_sta & RFS_PIE) { /* int enable? */ + sim_activate (&pcell_unit, RF_NUMWD * rf_time); + int_req = int_req | INT_RF; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat rf_reset (DEVICE *dptr) +{ +rf_sta = rf_da = 0; +rf_done = 1; +int_req = int_req & ~INT_RF; /* clear interrupt */ +sim_cancel (&rf_unit); +sim_cancel (&pcell_unit); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define OS8_START 07750 +#define OS8_LEN (sizeof (os8_rom) / sizeof (int16)) +#define DM4_START 00200 +#define DM4_LEN (sizeof (dm4_rom) / sizeof (int16)) + +static const uint16 os8_rom[] = { + 07600, /* 7750, CLA CLL ; also word count */ + 06603, /* 7751, DMAR ; also address */ + 06622, /* 7752, DFSC ; done? */ + 05352, /* 7753, JMP .-1 ; no */ + 05752 /* 7754, JMP @.-2 ; enter boot */ + }; + +static const uint16 dm4_rom[] = { + 00200, 07600, /* 0200, CLA CLL */ + 00201, 06603, /* 0201, DMAR ; read */ + 00202, 06622, /* 0202, DFSC ; done? */ + 00203, 05202, /* 0203, JMP .-1 ; no */ + 00204, 05600, /* 0204, JMP @.-4 ; enter boot */ + 07750, 07576, /* 7750, 7576 ; word count */ + 07751, 07576 /* 7751, 7576 ; address */ + }; + +t_stat rf_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 sim_switches, saved_PC; + +if (rf_dib.dev != DEV_RF) return STOP_NOTSTD; /* only std devno */ +if (sim_switches & SWMASK ('D')) { + for (i = 0; i < DM4_LEN; i = i + 2) + M[dm4_rom[i]] = dm4_rom[i + 1]; + saved_PC = DM4_START; + } +else { + for (i = 0; i < OS8_LEN; i++) + M[OS8_START + i] = os8_rom[i]; + saved_PC = OS8_START; + } +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rf_attach (UNIT *uptr, char *cptr) +{ +uint32 sz, p; +uint32 ds_bytes = RF_DKSIZE * sizeof (int16); + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + p = (sz + ds_bytes - 1) / ds_bytes; + if (p >= RF_NUMDK) p = RF_NUMDK - 1; + uptr->flags = (uptr->flags & ~UNIT_PLAT) | + (p << UNIT_V_PLAT); + } +uptr->capac = UNIT_GETP (uptr->flags) * RF_DKSIZE; +return attach_unit (uptr, cptr); +} + +/* Change disk size */ + +t_stat rf_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (val < 0) return SCPE_IERR; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = UNIT_GETP (val) * RF_DKSIZE; +uptr->flags = uptr->flags & ~UNIT_AUTO; +return SCPE_OK; +} diff --git a/PDP8/pdp8_rk.c b/PDP8/pdp8_rk.c new file mode 100644 index 0000000..4d5beb8 --- /dev/null +++ b/PDP8/pdp8_rk.c @@ -0,0 +1,449 @@ +/* pdp8_rk.c: RK8E cartridge disk simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rk RK8E/RK05 cartridge disk + + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIB, device number support + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted FLG to array, made register names consistent + 25-Apr-01 RMS Added device enable/disable support + 29-Jun-96 RMS Added unit enable/disable support +*/ + +#include "pdp8_defs.h" + +/* Constants */ + +#define RK_NUMSC 16 /* sectors/surface */ +#define RK_NUMSF 2 /* surfaces/cylinder */ +#define RK_NUMCY 203 /* cylinders/drive */ +#define RK_NUMWD 256 /* words/sector */ +#define RK_SIZE (RK_NUMCY * RK_NUMSF * RK_NUMSC * RK_NUMWD) + /* words/drive */ +#define RK_NUMDR 4 /* drives/controller */ +#define RK_M_NUMDR 03 + +/* Flags in the unit flags word */ + +#define UNIT_V_HWLK (UNIT_V_UF + 0) /* hwre write lock */ +#define UNIT_V_SWLK (UNIT_V_UF + 1) /* swre write lock */ +#define UNIT_HWLK (1 << UNIT_V_HWLK) +#define UNIT_SWLK (1 << UNIT_V_SWLK) +#define UNIT_WPRT (UNIT_HWLK|UNIT_SWLK|UNIT_RO) /* write protect */ + +/* Parameters in the unit descriptor */ + +#define CYL u3 /* current cylinder */ +#define FUNC u4 /* function */ + +/* Status register */ + +#define RKS_DONE 04000 /* transfer done */ +#define RKS_HMOV 02000 /* heads moving */ +#define RKS_SKFL 00400 /* drive seek fail */ +#define RKS_NRDY 00200 /* drive not ready */ +#define RKS_BUSY 00100 /* control busy error */ +#define RKS_TMO 00040 /* timeout error */ +#define RKS_WLK 00020 /* write lock error */ +#define RKS_CRC 00010 /* CRC error */ +#define RKS_DLT 00004 /* data late error */ +#define RKS_STAT 00002 /* drive status error */ +#define RKS_CYL 00001 /* cyl address error */ +#define RKS_ERR (RKS_BUSY+RKS_TMO+RKS_WLK+RKS_CRC+RKS_DLT+RKS_STAT+RKS_CYL) + +/* Command register */ + +#define RKC_M_FUNC 07 /* function */ +#define RKC_READ 0 +#define RKC_RALL 1 +#define RKC_WLK 2 +#define RKC_SEEK 3 +#define RKC_WRITE 4 +#define RKC_WALL 5 +#define RKC_V_FUNC 9 +#define RKC_IE 00400 /* interrupt enable */ +#define RKC_SKDN 00200 /* set done on seek done */ +#define RKC_HALF 00100 /* 128W sector */ +#define RKC_MEX 00070 /* memory extension */ +#define RKC_V_MEX 3 +#define RKC_M_DRV 03 /* drive select */ +#define RKC_V_DRV 1 +#define RKC_CYHI 00001 /* high cylinder addr */ + +#define GET_FUNC(x) (((x) >> RKC_V_FUNC) & RKC_M_FUNC) +#define GET_DRIVE(x) (((x) >> RKC_V_DRV) & RKC_M_DRV) +#define GET_MEX(x) (((x) & RKC_MEX) << (12 - RKC_V_MEX)) + +/* Disk address */ + +#define RKD_V_SECT 0 /* sector */ +#define RKD_M_SECT 017 +#define RKD_V_SUR 4 /* surface */ +#define RKD_M_SUR 01 +#define RKD_V_CYL 5 /* cylinder */ +#define RKD_M_CYL 0177 +#define GET_CYL(x,y) ((((x) & RKC_CYHI) << (12-RKD_V_CYL)) | \ + (((y) >> RKD_V_CYL) & RKD_M_CYL)) +#define GET_DA(x,y) ((((x) & RKC_CYHI) << 12) | y) + +/* Reset commands */ + +#define RKX_CLS 0 /* clear status */ +#define RKX_CLC 1 /* clear control */ +#define RKX_CLD 2 /* clear drive */ +#define RKX_CLSA 3 /* clear status alt */ + +#define RK_INT_UPDATE if (((rk_sta & (RKS_DONE + RKS_ERR)) != 0) && \ + ((rk_cmd & RKC_IE) != 0)) \ + int_req = int_req | INT_RK; \ + else int_req = int_req & ~INT_RK +#define RK_MIN 10 +#define MAX(x,y) (((x) > (y))? (x): (y)) + +extern uint16 M[]; +extern int32 int_req, stop_inst; +extern UNIT cpu_unit; + +int32 rk_busy = 0; /* controller busy */ +int32 rk_sta = 0; /* status register */ +int32 rk_cmd = 0; /* command register */ +int32 rk_da = 0; /* disk address */ +int32 rk_ma = 0; /* memory address */ +int32 rk_swait = 10, rk_rwait = 10; /* seek, rotate wait */ +int32 rk_stopioe = 1; /* stop on error */ + +DEVICE rk_dev; +int32 rk (int32 IR, int32 AC); +t_stat rk_svc (UNIT *uptr); +t_stat rk_reset (DEVICE *dptr); +t_stat rk_boot (int32 unitno, DEVICE *dptr); +void rk_go (int32 function, int32 cylinder); + +/* RK-8E data structures + + rk_dev RK device descriptor + rk_unit RK unit list + rk_reg RK register list + rk_mod RK modifiers list +*/ + +DIB rk_dib = { DEV_RK, 1, { &rk } }; + +UNIT rk_unit[] = { + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) }, + { UDATA (&rk_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+ + UNIT_ROABLE, RK_SIZE) } + }; + +REG rk_reg[] = { + { ORDATA (RKSTA, rk_sta, 12) }, + { ORDATA (RKCMD, rk_cmd, 12) }, + { ORDATA (RKDA, rk_da, 12) }, + { ORDATA (RKMA, rk_ma, 12) }, + { FLDATA (BUSY, rk_busy, 0) }, + { FLDATA (INT, int_req, INT_V_RK) }, + { DRDATA (STIME, rk_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rk_rwait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, rk_stopioe, 0) }, + { ORDATA (DEVNUM, rk_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rk_mod[] = { + { UNIT_HWLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_HWLK, UNIT_HWLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rk_dev = { + "RK", rk_unit, rk_reg, rk_mod, + RK_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &rk_reset, + &rk_boot, NULL, NULL, + &rk_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 rk (int32 IR, int32 AC) +{ +int32 i; +UNIT *uptr; + +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* unused */ + return (stop_inst << IOT_V_REASON) + AC; + + case 1: /* DSKP */ + return (rk_sta & (RKS_DONE + RKS_ERR))? /* skip on done, err */ + IOT_SKP + AC: AC; + + case 2: /* DCLR */ + rk_sta = 0; /* clear status */ + switch (AC & 03) { /* decode AC<10:11> */ + + case RKX_CLS: /* clear status */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + case RKX_CLSA: /* clear status alt */ + break; + + case RKX_CLC: /* clear control */ + rk_cmd = rk_busy = 0; /* clear registers */ + rk_ma = rk_da = 0; + for (i = 0; i < RK_NUMDR; i++) sim_cancel (&rk_unit[i]); + break; + + case RKX_CLD: /* reset drive */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else rk_go (RKC_SEEK, 0); /* seek to 0 */ + break; + } /* end switch AC */ + break; + + case 3: /* DLAG */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else { + rk_da = AC; /* load disk addr */ + rk_go (GET_FUNC (rk_cmd), GET_CYL (rk_cmd, rk_da)); + } + break; + + case 4: /* DLCA */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else rk_ma = AC; /* load curr addr */ + break; + + case 5: /* DRST */ + uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ + rk_sta = rk_sta & ~(RKS_HMOV + RKS_NRDY); /* clear dynamic */ + if ((uptr->flags & UNIT_ATT) == 0) rk_sta = rk_sta | RKS_NRDY; + if (sim_is_active (uptr)) rk_sta = rk_sta | RKS_HMOV; + return rk_sta; + + case 6: /* DLDC */ + if (rk_busy != 0) rk_sta = rk_sta | RKS_BUSY; + else { + rk_cmd = AC; /* load command */ + rk_sta = 0; /* clear status */ + } + break; + + case 7: /* DMAN */ + break; + } /* end case pulse */ + +RK_INT_UPDATE; /* update int req */ +return 0; /* clear AC */ +} + +/* Initiate new function + + Called with function, cylinder, to allow recalibrate as well as + load and go to be processed by this routine. + + Assumes that the controller is idle, and that updating of interrupt + request will be done by the caller. +*/ + +void rk_go (int32 func, int32 cyl) +{ +int32 t; +UNIT *uptr; + +if (func == RKC_RALL) func = RKC_READ; /* all? use standard */ +if (func == RKC_WALL) func = RKC_WRITE; +uptr = rk_dev.units + GET_DRIVE (rk_cmd); /* selected unit */ +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; + return; + } +if (sim_is_active (uptr) || (cyl >= RK_NUMCY)) { /* busy or bad cyl? */ + rk_sta = rk_sta | RKS_DONE | RKS_STAT; + return; + } +if ((func == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) { + rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */ + return; + } +if (func == RKC_WLK) { /* write lock? */ + uptr->flags = uptr->flags | UNIT_SWLK; + rk_sta = rk_sta | RKS_DONE; + return; + } +t = abs (cyl - uptr->CYL) * rk_swait; /* seek time */ +if (func == RKC_SEEK) { /* seek? */ + sim_activate (uptr, MAX (RK_MIN, t)); /* schedule */ + rk_sta = rk_sta | RKS_DONE; /* set done */ + } +else { + sim_activate (uptr, t + rk_rwait); /* schedule */ + rk_busy = 1; /* set busy */ + } +uptr->FUNC = func; /* save func */ +uptr->CYL = cyl; /* put on cylinder */ +return; +} + +/* Unit service + + If seek, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder address for + the current command. + + Note that memory addresses wrap around in the current field. +*/ + +static uint16 fill[RK_NUMWD/2] = { 0 }; +t_stat rk_svc (UNIT *uptr) +{ +int32 err, wc, wc1, awc, swc, pa, da; +UNIT *seluptr; + +if (uptr->FUNC == RKC_SEEK) { /* seek? */ + seluptr = rk_dev.units + GET_DRIVE (rk_cmd); /* see if selected */ + if ((uptr == seluptr) && ((rk_cmd & RKC_SKDN) != 0)) { + rk_sta = rk_sta | RKS_DONE; + RK_INT_UPDATE; + } + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* not att? abort */ + rk_sta = rk_sta | RKS_DONE | RKS_NRDY | RKS_STAT; + rk_busy = 0; + RK_INT_UPDATE; + return IORETURN (rk_stopioe, SCPE_UNATT); + } + +if ((uptr->FUNC == RKC_WRITE) && (uptr->flags & UNIT_WPRT)) { + rk_sta = rk_sta | RKS_DONE | RKS_WLK; /* write and locked? */ + rk_busy = 0; + RK_INT_UPDATE; + return SCPE_OK; + } + +pa = GET_MEX (rk_cmd) | rk_ma; /* phys address */ +da = GET_DA (rk_cmd, rk_da) * RK_NUMWD * sizeof (int16);/* disk address */ +swc = wc = (rk_cmd & RKC_HALF)? RK_NUMWD / 2: RK_NUMWD; /* get transfer size */ +if ((wc1 = ((rk_ma + wc) - 010000)) > 0) wc = wc - wc1; /* if wrap, limit */ +err = fseek (uptr->fileref, da, SEEK_SET); /* locate sector */ + +if ((uptr->FUNC == RKC_READ) && (err == 0) && MEM_ADDR_OK (pa)) { /* read? */ + awc = fxread (&M[pa], sizeof (int16), wc, uptr->fileref); + for ( ; awc < wc; awc++) M[pa + awc] = 0; /* fill if eof */ + err = ferror (uptr->fileref); + if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ + pa = pa & 070000; /* wrap phys addr */ + awc = fxread (&M[pa], sizeof (int16), wc1, uptr->fileref); + for ( ; awc < wc1; awc++) M[pa + awc] = 0; /* fill if eof */ + err = ferror (uptr->fileref); + } + } + +if ((uptr->FUNC == RKC_WRITE) && (err == 0)) { /* write? */ + fxwrite (&M[pa], sizeof (int16), wc, uptr->fileref); + err = ferror (uptr->fileref); + if ((wc1 > 0) && (err == 0)) { /* field wraparound? */ + pa = pa & 070000; /* wrap phys addr */ + fxwrite (&M[pa], sizeof (int16), wc1, uptr->fileref); + err = ferror (uptr->fileref); + } + if ((rk_cmd & RKC_HALF) && (err == 0)) { /* fill half sector */ + fxwrite (fill, sizeof (int16), RK_NUMWD/2, uptr->fileref); + err = ferror (uptr->fileref); + } + } + +rk_ma = (rk_ma + swc) & 07777; /* incr mem addr reg */ +rk_sta = rk_sta | RKS_DONE; /* set done */ +rk_busy = 0; +RK_INT_UPDATE; + +if (err != 0) { + perror ("RK I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat rk_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rk_cmd = rk_ma = rk_da = rk_sta = rk_busy = 0; +int_req = int_req & ~INT_RK; /* clear interrupt */ +for (i = 0; i < RK_NUMDR; i++) { /* stop all units */ + uptr = rk_dev.units + i; + sim_cancel (uptr); + uptr->flags = uptr->flags & ~UNIT_SWLK; + uptr->CYL = uptr->FUNC = 0; + } +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 023 +#define BOOT_UNIT 032 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06007, /* 23, CAF */ + 06744, /* 24, DLCA ; addr = 0 */ + 01032, /* 25, TAD UNIT ; unit no */ + 06746, /* 26, DLDC ; command, unit */ + 06743, /* 27, DLAG ; disk addr, go */ + 01032, /* 30, TAD UNIT ; unit no, for OS */ + 05031, /* 31, JMP . */ + 00000 /* UNIT, 0 ; in bits <9:10> */ + }; + +t_stat rk_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +if (rk_dib.dev != DEV_RK) return STOP_NOTSTD; /* only std devno */ +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +M[BOOT_UNIT] = (unitno & RK_M_NUMDR) << 1; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/PDP8/pdp8_rl.c b/PDP8/pdp8_rl.c new file mode 100644 index 0000000..ac2376c --- /dev/null +++ b/PDP8/pdp8_rl.c @@ -0,0 +1,679 @@ +/* pdp8_rl.c: RL8A cartridge disk simulator + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rl RL8A cartridge disk + + 25-Oct-05 RMS Fixed IOT 61 decode bug (found by David Gesswein) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 04-Jan-04 RMS Changed attach routine to use sim_fsize + 25-Apr-03 RMS Revised for extended file support + 04-Oct-02 RMS Added DIB, device number support + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Cloned from RL11 + + The RL8A is a four drive cartridge disk subsystem. An RL01 drive + consists of 256 cylinders, each with 2 surfaces containing 40 sectors + of 256 bytes. An RL02 drive has 512 cylinders. + + The RL8A controller has several serious complications. + - Seeking is relative to the current disk address; this requires + keeping accurate track of the current cylinder. + - The RL8A will not switch heads or cross cylinders during transfers. + - The RL8A operates in 8b and 12b mode, like the RX8E; in 12b mode, it + packs 2 12b words into 3 bytes, creating a 170 "word" sector with + one wasted byte. Multi-sector transfers in 12b mode don't work. +*/ + +#include "pdp8_defs.h" + +/* Constants */ + +#define RL_NUMBY 256 /* 8b bytes/sector */ +#define RL_NUMSC 40 /* sectors/surface */ +#define RL_NUMSF 2 /* surfaces/cylinder */ +#define RL_NUMCY 256 /* cylinders/drive */ +#define RL_NUMDR 4 /* drives/controller */ +#define RL_MAXFR (1 << 12) /* max transfer */ +#define RL01_SIZE (RL_NUMCY*RL_NUMSF*RL_NUMSC*RL_NUMBY) /* words/drive */ +#define RL02_SIZE (RL01_SIZE * 2) /* words/drive */ +#define RL_BBMAP 014 /* sector for bblk map */ +#define RL_BBID 0123 /* ID for bblk map */ + +/* Flags in the unit flags word */ + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write lock */ +#define UNIT_V_RL02 (UNIT_V_UF + 1) /* RL01 vs RL02 */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize enable */ +#define UNIT_V_DUMMY (UNIT_V_UF + 3) /* dummy flag */ +#define UNIT_DUMMY (1u << UNIT_V_DUMMY) +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_RL02 (1u << UNIT_V_RL02) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* Parameters in the unit descriptor */ + +#define TRK u3 /* current cylinder */ +#define STAT u4 /* status */ + +/* RLDS, NI = not implemented, * = kept in STAT, ^ = kept in TRK */ + +#define RLDS_LOAD 0 /* no cartridge */ +#define RLDS_LOCK 5 /* lock on */ +#define RLDS_BHO 0000010 /* brushes home NI */ +#define RLDS_HDO 0000020 /* heads out NI */ +#define RLDS_CVO 0000040 /* cover open NI */ +#define RLDS_HD 0000100 /* head select ^ */ +#define RLDS_RL02 0000200 /* RL02 */ +#define RLDS_DSE 0000400 /* drv sel err NI */ +#define RLDS_VCK 0001000 /* vol check * */ +#define RLDS_WGE 0002000 /* wr gate err * */ +#define RLDS_SPE 0004000 /* spin err * */ +#define RLDS_STO 0010000 /* seek time out NI */ +#define RLDS_WLK 0020000 /* wr locked */ +#define RLDS_HCE 0040000 /* hd curr err NI */ +#define RLDS_WDE 0100000 /* wr data err NI */ +#define RLDS_ATT (RLDS_HDO+RLDS_BHO+RLDS_LOCK) /* att status */ +#define RLDS_UNATT (RLDS_CVO+RLDS_LOAD) /* unatt status */ +#define RLDS_ERR (RLDS_WDE+RLDS_HCE+RLDS_STO+RLDS_SPE+RLDS_WGE+ \ + RLDS_VCK+RLDS_DSE) /* errors bits */ + +/* RLCSA, seek = offset/rw = address (also uptr->TRK) */ + +#define RLCSA_DIR 04000 /* direction */ +#define RLCSA_HD 02000 /* head select */ +#define RLCSA_CYL 00777 /* cyl offset */ +#define GET_CYL(x) ((x) & RLCSA_CYL) +#define GET_TRK(x) ((((x) & RLCSA_CYL) * RL_NUMSF) + \ + (((x) & RLCSA_HD)? 1: 0)) +#define GET_DA(x) ((GET_TRK(x) * RL_NUMSC) + rlsa) + +/* RLCSB, function/unit select */ + +#define RLCSB_V_FUNC 0 /* function */ +#define RLCSB_M_FUNC 07 +#define RLCSB_MNT 0 +#define RLCSB_CLRD 1 +#define RLCSB_GSTA 2 +#define RLCSB_SEEK 3 +#define RLCSB_RHDR 4 +#define RLCSB_WRITE 5 +#define RLCSB_READ 6 +#define RLCSB_RNOHDR 7 +#define RLCSB_V_MEX 3 /* memory extension */ +#define RLCSB_M_MEX 07 +#define RLCSB_V_DRIVE 6 /* drive */ +#define RLCSB_M_DRIVE 03 +#define RLCSB_V_IE 8 /* int enable */ +#define RLCSB_IE (1u << RLCSB_V_IE) +#define RLCSB_8B 01000 /* 12b/8b */ +#define RCLS_MNT 02000 /* maint NI */ +#define RLCSB_RW 0001777 /* read/write */ +#define GET_FUNC(x) (((x) >> RLCSB_V_FUNC) & RLCSB_M_FUNC) +#define GET_MEX(x) (((x) >> RLCSB_V_MEX) & RLCSB_M_MEX) +#define GET_DRIVE(x) (((x) >> RLCSB_V_DRIVE) & RLCSB_M_DRIVE) + +/* RLSA, disk sector */ + +#define RLSA_V_SECT 6 /* sector */ +#define RLSA_M_SECT 077 +#define GET_SECT(x) (((x) >> RLSA_V_SECT) & RLSA_M_SECT) + +/* RLER, error register */ + +#define RLER_DRDY 00001 /* drive ready */ +#define RLER_DRE 00002 /* drive error */ +#define RLER_HDE 01000 /* header error */ +#define RLER_INCMP 02000 /* incomplete */ +#define RLER_ICRC 04000 /* CRC error */ +#define RLER_MASK 07003 + +/* RLSI, silo register, used only in read header */ + +#define RLSI_V_TRK 6 /* track */ + +extern uint16 M[]; +extern int32 int_req; +extern UNIT cpu_unit; + +uint8 *rlxb = NULL; /* xfer buffer */ +int32 rlcsa = 0; /* control/status A */ +int32 rlcsb = 0; /* control/status B */ +int32 rlma = 0; /* memory address */ +int32 rlwc = 0; /* word count */ +int32 rlsa = 0; /* sector address */ +int32 rler = 0; /* error register */ +int32 rlsi = 0, rlsi1 = 0, rlsi2 = 0; /* silo queue */ +int32 rl_lft = 0; /* silo left/right */ +int32 rl_done = 0; /* done flag */ +int32 rl_erf = 0; /* error flag */ +int32 rl_swait = 10; /* seek wait */ +int32 rl_rwait = 10; /* rotate wait */ +int32 rl_stopioe = 1; /* stop on error */ + +DEVICE rl_dev; +int32 rl60 (int32 IR, int32 AC); +int32 rl61 (int32 IR, int32 AC); +t_stat rl_svc (UNIT *uptr); +t_stat rl_reset (DEVICE *dptr); +void rl_set_done (int32 error); +t_stat rl_boot (int32 unitno, DEVICE *dptr); +t_stat rl_attach (UNIT *uptr, char *cptr); +t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* RL8A data structures + + rl_dev RL device descriptor + rl_unit RL unit list + rl_reg RL register list + rl_mod RL modifier list +*/ + +DIB rl_dib = { DEV_RL, 2, { &rl60, &rl61 } }; + +UNIT rl_unit[] = { + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) }, + { UDATA (&rl_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_DISABLE+UNIT_AUTO+ + UNIT_ROABLE, RL01_SIZE) } + }; + +REG rl_reg[] = { + { ORDATA (RLCSA, rlcsa, 12) }, + { ORDATA (RLCSB, rlcsb, 12) }, + { ORDATA (RLMA, rlma, 12) }, + { ORDATA (RLWC, rlwc, 12) }, + { ORDATA (RLSA, rlsa, 6) }, + { ORDATA (RLER, rler, 12) }, + { ORDATA (RLSI, rlsi, 16) }, + { ORDATA (RLSI1, rlsi1, 16) }, + { ORDATA (RLSI2, rlsi2, 16) }, + { FLDATA (RLSIL, rl_lft, 0) }, + { FLDATA (INT, int_req, INT_V_RL) }, + { FLDATA (DONE, rl_done, INT_V_RL) }, + { FLDATA (IE, rlcsb, RLCSB_V_IE) }, + { FLDATA (ERR, rl_erf, 0) }, + { DRDATA (STIME, rl_swait, 24), PV_LEFT }, + { DRDATA (RTIME, rl_rwait, 24), PV_LEFT }, + { URDATA (CAPAC, rl_unit[0].capac, 10, T_ADDR_W, 0, + RL_NUMDR, PV_LEFT + REG_HRO) }, + { FLDATA (STOP_IOE, rl_stopioe, 0) }, + { ORDATA (DEVNUM, rl_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rl_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_DUMMY, 0, NULL, "BADBLOCK", &rl_set_bad }, + { (UNIT_RL02+UNIT_ATT), UNIT_ATT, "RL01", NULL, NULL }, + { (UNIT_RL02+UNIT_ATT), (UNIT_RL02+UNIT_ATT), "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), 0, "RL01", NULL, NULL }, + { (UNIT_AUTO+UNIT_RL02+UNIT_ATT), UNIT_RL02, "RL02", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_RL02), 0, NULL, "RL01", &rl_set_size }, + { (UNIT_AUTO+UNIT_RL02), UNIT_RL02, NULL, "RL02", &rl_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rl_dev = { + "RL", rl_unit, rl_reg, rl_mod, + RL_NUMDR, 8, 24, 1, 8, 8, + NULL, NULL, &rl_reset, + &rl_boot, &rl_attach, NULL, + &rl_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 rl60 (int32 IR, int32 AC) +{ +int32 curr, offs, newc, maxc; +UNIT *uptr; + +switch (IR & 07) { /* case IR<9:11> */ + + case 0: /* RLDC */ + rl_reset (&rl_dev); /* reset device */ + break; + + case 1: /* RLSD */ + if (rl_done) AC = IOT_SKP; /* skip if done */ + else AC = 0; + rl_done = 0; /* clear done */ + int_req = int_req & ~INT_RL; /* clear intr */ + return AC; + + case 2: /* RLMA */ + rlma = AC; + break; + + case 3: /* RLCA */ + rlcsa = AC; + break; + + case 4: /* RLCB */ + rlcsb = AC; + rl_done = 0; /* clear done */ + rler = rl_erf = 0; /* clear errors */ + int_req = int_req & ~INT_RL; /* clear intr */ + rl_lft = 0; /* clear silo ptr */ + uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */ + switch (GET_FUNC (rlcsb)) { /* case on func */ + + case RLCSB_CLRD: /* clear drive */ + uptr->STAT = uptr->STAT & ~RLDS_ERR; /* clear errors */ + case RLCSB_MNT: /* mnt */ + rl_set_done (0); + break; + + case RLCSB_SEEK: /* seek */ + curr = GET_CYL (uptr->TRK); /* current cylinder */ + offs = GET_CYL (rlcsa); /* offset */ + if (rlcsa & RLCSA_DIR) { /* in or out? */ + newc = curr + offs; /* out */ + maxc = (uptr->flags & UNIT_RL02)? + RL_NUMCY * 2: RL_NUMCY; + if (newc >= maxc) newc = maxc - 1; + } + else { + newc = curr - offs; /* in */ + if (newc < 0) newc = 0; + } + uptr->TRK = newc | (rlcsa & RLCSA_HD); + sim_activate (uptr, rl_swait * abs (newc - curr)); + break; + + default: /* data transfer */ + sim_activate (uptr, rl_swait); /* activate unit */ + break; + } /* end switch func */ + break; + + case 5: /* RLSA */ + rlsa = GET_SECT (AC); + break; + + case 6: /* spare */ + return 0; + + case 7: /* RLWC */ + rlwc = AC; + break; + } /* end switch pulse */ + +return 0; /* clear AC */ +} + +int32 rl61 (int32 IR, int32 AC) +{ +int32 dat; +UNIT *uptr; + +switch (IR & 07) { /* case IR<9:11> */ + + case 0: /* RRER */ + uptr = rl_dev.units + GET_DRIVE (rlcsb); /* select unit */ + if (!sim_is_active (uptr) && /* update drdy */ + (uptr->flags & UNIT_ATT)) + rler = rler | RLER_DRDY; + else rler = rler & ~RLER_DRDY; + dat = rler & RLER_MASK; + break; + + case 1: /* RRWC */ + dat = rlwc; + break; + + case 2: /* RRCA */ + dat = rlcsa; + break; + + case 3: /* RRCB */ + dat = rlcsb; + break; + + case 4: /* RRSA */ + dat = (rlsa << RLSA_V_SECT) & 07777; + break; + + case 5: /* RRSI */ + if (rl_lft) { /* silo left? */ + dat = (rlsi >> 8) & 0377; /* get left 8b */ + rlsi = rlsi1; /* ripple */ + rlsi1 = rlsi2; + } + else dat = rlsi & 0377; /* get right 8b */ + rl_lft = rl_lft ^ 1; /* change side */ + break; + + case 6: /* spare */ + return AC; + + case 7: /* RLSE */ + if (rl_erf) dat = IOT_SKP | AC; /* skip if err */ + else dat = AC; + rl_erf = 0; + break; + } /* end switch pulse */ + +return dat; +} + +/* Service unit timeout + + If seek in progress, complete seek command + Else complete data transfer command + + The unit control block contains the function and cylinder for + the current command. +*/ + +t_stat rl_svc (UNIT *uptr) +{ +int32 err, wc, maxc; +int32 i, j, func, da, bc, wbc; +uint32 ma; + +func = GET_FUNC (rlcsb); /* get function */ +if (func == RLCSB_GSTA) { /* get status? */ + rlsi = uptr->STAT | + ((uptr->TRK & RLCSA_HD)? RLDS_HD: 0) | + ((uptr->flags & UNIT_ATT)? RLDS_ATT: RLDS_UNATT); + if (uptr->flags & UNIT_RL02) rlsi = rlsi | RLDS_RL02; + if (uptr->flags & UNIT_WPRT) rlsi = rlsi | RLDS_WLK; + rlsi2 = rlsi1 = rlsi; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + uptr->STAT = uptr->STAT | RLDS_SPE; /* spin error */ + rl_set_done (RLER_INCMP); /* flag error */ + return IORETURN (rl_stopioe, SCPE_UNATT); + } + +if ((func == RLCSB_WRITE) && (uptr->flags & UNIT_WPRT)) { + uptr->STAT = uptr->STAT | RLDS_WGE; /* write and locked */ + rl_set_done (RLER_DRE); /* flag error */ + return SCPE_OK; + } + +if (func == RLCSB_SEEK) { /* seek? */ + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (func == RLCSB_RHDR) { /* read header? */ + rlsi = (GET_TRK (uptr->TRK) << RLSI_V_TRK) | rlsa; + rlsi1 = rlsi2 = 0; + rl_set_done (0); /* done */ + return SCPE_OK; + } + +if (((func != RLCSB_RNOHDR) && (GET_CYL (uptr->TRK) != GET_CYL (rlcsa))) + || (rlsa >= RL_NUMSC)) { /* bad cyl or sector? */ + rl_set_done (RLER_HDE | RLER_INCMP); /* flag error */ + return SCPE_OK; + } + +ma = (GET_MEX (rlcsb) << 12) | rlma; /* get mem addr */ +da = GET_DA (rlcsa) * RL_NUMBY; /* get disk addr */ +wc = 010000 - rlwc; /* get true wc */ +if (rlcsb & RLCSB_8B) { /* 8b mode? */ + bc = wc; /* bytes to xfr */ + maxc = (RL_NUMSC - rlsa) * RL_NUMBY; /* max transfer */ + if (bc > maxc) wc = bc = maxc; /* trk ovrun? limit */ + } +else { + bc = ((wc * 3) + 1) / 2; /* 12b mode */ + if (bc > RL_NUMBY) { /* > 1 sector */ + bc = RL_NUMBY; /* cap xfer */ + wc = (RL_NUMBY * 2) / 3; + } + } + +err = fseek (uptr->fileref, da, SEEK_SET); + +if ((func >= RLCSB_READ) && (err == 0) && /* read (no hdr)? */ + MEM_ADDR_OK (ma)) { /* valid bank? */ + i = fxread (rlxb, sizeof (int8), bc, uptr->fileref); + err = ferror (uptr->fileref); + for ( ; i < bc; i++) rlxb[i] = 0; /* fill buffer */ + for (i = j = 0; i < wc; i++) { /* store buffer */ + if (rlcsb & RLCSB_8B) /* 8b mode? */ + M[ma] = rlxb[i] & 0377; /* store */ + else if (i & 1) { /* odd wd 12b? */ + M[ma] = ((rlxb[j + 1] >> 4) & 017) | + (((uint16) rlxb[j + 2]) << 4); + j = j + 3; + } + else M[ma] = rlxb[j] | /* even wd 12b */ + ((((uint16) rlxb[j + 1]) & 017) << 8); + ma = (ma & 070000) + ((ma + 1) & 07777); + } /* end for */ + } /* end if wr */ + +if ((func == RLCSB_WRITE) && (err == 0)) { /* write? */ + for (i = j = 0; i < wc; i++) { /* fetch buffer */ + if (rlcsb & RLCSB_8B) /* 8b mode? */ + rlxb[i] = M[ma] & 0377; /* fetch */ + else if (i & 1) { /* odd wd 12b? */ + rlxb[j + 1] = rlxb[j + 1] | ((M[ma] & 017) << 4); + rlxb[j + 2] = ((M[ma] >> 4) & 0377); + j = j + 3; + } + else { /* even wd 12b */ + rlxb[j] = M[ma] & 0377; + rlxb[j + 1] = (M[ma] >> 8) & 017; + } + ma = (ma & 070000) + ((ma + 1) & 07777); + } /* end for */ + wbc = (bc + (RL_NUMBY - 1)) & ~(RL_NUMBY - 1); /* clr to */ + for (i = bc; i < wbc; i++) rlxb[i] = 0; /* end of blk */ + fxwrite (rlxb, sizeof (int8), wbc, uptr->fileref); + err = ferror (uptr->fileref); + } /* end write */ + +rlwc = (rlwc + wc) & 07777; /* final word count */ +if (rlwc != 0) rler = rler | RLER_INCMP; /* completed? */ +rlma = (rlma + wc) & 07777; /* final word addr */ +rlsa = rlsa + ((bc + (RL_NUMBY - 1)) / RL_NUMBY); +rl_set_done (0); + +if (err != 0) { /* error? */ + perror ("RL I/O error"); + clearerr (uptr->fileref); + return SCPE_IOERR; + } +return SCPE_OK; +} + +/* Set done and possibly errors */ + +void rl_set_done (int32 status) +{ +rl_done = 1; +rler = rler | status; +if (rler) rl_erf = 1; +if (rlcsb & RLCSB_IE) int_req = int_req | INT_RL; +else int_req = int_req & ~INT_RL; +return; +} + +/* Device reset + + Note that the RL8A does NOT recalibrate its drives on RESET +*/ + +t_stat rl_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +rlcsa = rlcsb = rlsa = rler = 0; +rlma = rlwc = 0; +rlsi = rlsi1 = rlsi2 = 0; +rl_lft = 0; +rl_done = 0; +rl_erf = 0; +int_req = int_req & ~INT_RL; +for (i = 0; i < RL_NUMDR; i++) { + uptr = rl_dev.units + i; + sim_cancel (uptr); + uptr->STAT = 0; + } +if (rlxb == NULL) rlxb = (uint8 *) calloc (RL_MAXFR, sizeof (uint8)); +if (rlxb == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rl_attach (UNIT *uptr, char *cptr) +{ +uint32 p; +t_stat r; + +uptr->capac = (uptr->flags & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +uptr->TRK = 0; /* cyl 0 */ +uptr->STAT = RLDS_VCK; /* new volume */ +if ((p = sim_fsize (uptr->fileref)) == 0) { /* new disk image? */ + if (uptr->flags & UNIT_RO) return SCPE_OK; + return rl_set_bad (uptr, 0, NULL, NULL); + } +if ((uptr->flags & UNIT_AUTO) == 0) return r; /* autosize? */ +if (p > (RL01_SIZE * sizeof (int16))) { + uptr->flags = uptr->flags | UNIT_RL02; + uptr->capac = RL02_SIZE; + } +else { + uptr->flags = uptr->flags & ~UNIT_RL02; + uptr->capac = RL01_SIZE; + } +return SCPE_OK; +} + +/* Set size routine */ + +t_stat rl_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +uptr->capac = (val & UNIT_RL02)? RL02_SIZE: RL01_SIZE; +return SCPE_OK; +} + +/* Factory bad block table creation routine + + This routine writes the OS/8 specific bad block map in track 0, sector 014 (RL_BBMAP): + + words 0 magic number = 0123 (RL_BBID) + words 1-n block numbers + : + words n+1 end of table = 0 + + Inputs: + uptr = pointer to unit + val = ignored + Outputs: + sta = status code +*/ + +t_stat rl_set_bad (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, da = RL_BBMAP * RL_NUMBY; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; +if (uptr->flags & UNIT_RO) return SCPE_RO; +if (!get_yn ("Create bad block table? [N]", FALSE)) return SCPE_OK; +if (fseek (uptr->fileref, da, SEEK_SET)) return SCPE_IOERR; +rlxb[0] = RL_BBID; +for (i = 1; i < RL_NUMBY; i++) rlxb[i] = 0; +fxwrite (rlxb, sizeof (uint8), RL_NUMBY, uptr->fileref); +if (ferror (uptr->fileref)) return SCPE_IOERR; +return SCPE_OK; +} + +/* Bootstrap */ + +#define BOOT_START 1 /* start */ +#define BOOT_UNIT 02006 /* unit number */ +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06600, /* BT, RLDC ; reset */ + 07201, /* 02, CLA IAC ; clr drv = 1 */ + 04027, /* 03, JMS GO ; do io */ + 01004, /* 04, TAD 4 ; rd hdr fnc */ + 04027, /* 05, JMS GO ; do io */ + 06615, /* 06, RRSI ; rd hdr lo */ + 07002, /* 07, BSW ; swap */ + 07012, /* 10, RTR ; lo cyl to L */ + 06615, /* 11, RRSI ; rd hdr hi */ + 00025, /* 12, AND 25 ; mask = 377 */ + 07004, /* 13, RTL ; get cyl */ + 06603, /* 14, RLCA ; set addr */ + 07325, /* 15, CLA STL IAC RAL ; seek = 3 */ + 04027, /* 16, JMS GO ; do io */ + 07332, /* 17, CLA STL RTR ; dir in = 2000 */ + 06605, /* 20, RLSA ; sector */ + 01026, /* 21, TAD (-200) ; one sector */ + 06607, /* 22, RLWC ; word cnt */ + 07327, /* 23, CLA STL IAC RTL ; read = 6*/ + 04027, /* 24, JMS GO ; do io */ + 00377, /* 25, JMP 377 ; start */ + 07600, /* 26, -200 ; word cnt */ + 00000, /* GO, 0 ; subr */ + 06604, /* 30, RLCB ; load fnc */ + 06601, /* 31, RLSD ; wait */ + 05031, /* 32, JMP .-1 ; */ + 06617, /* 33, RLSE ; error? */ + 05427, /* 34, JMP I GO ; no, ok */ + 05001 /* 35, JMP BT ; restart */ + }; + + +t_stat rl_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +if (unitno) return SCPE_ARG; /* only unit 0 */ +if (rl_dib.dev != DEV_RL) return STOP_NOTSTD; /* only std devno */ +rl_unit[unitno].TRK = 0; +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} diff --git a/PDP8/pdp8_rx.c b/PDP8/pdp8_rx.c new file mode 100644 index 0000000..fe99c7f --- /dev/null +++ b/PDP8/pdp8_rx.c @@ -0,0 +1,730 @@ +/* pdp8_rx.c: RX8E/RX01, RX28/RX02 floppy disk simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rx RX8E/RX01, RX28/RX02 floppy disk + + 15-May-06 RMS Fixed bug in autosize attach (reported by Dave Gesswein) + 04-Jan-04 RMS Changed sim_fsize calling sequence + 05-Nov-03 RMS Fixed bug in RX28 read status (found by Charles Dickman) + 26-Oct-03 RMS Cleaned up buffer copy code, fixed double density write + 25-Apr-03 RMS Revised for extended file support + 14-Mar-03 RMS Fixed variable size interaction with save/restore + 03-Mar-03 RMS Fixed autosizing + 08-Oct-02 RMS Added DIB, device number support + Fixed reset to work with disabled device + 15-Sep-02 RMS Added RX28/RX02 support + 06-Jan-02 RMS Changed enable/disable support + 30-Nov-01 RMS Added read only unit, extended SET/SHOW support + 24-Nov-01 RMS Converted FLG to array + 17-Jul-01 RMS Fixed warning from VC++ 6 + 26-Apr-01 RMS Added device enable/disable support + 13-Apr-01 RMS Revised for register arrays + 14-Apr-99 RMS Changed t_addr to unsigned + 15-Aug-96 RMS Fixed bug in LCD + + An RX01 diskette consists of 77 tracks, each with 26 sectors of 128B. + An RX02 diskette consists of 77 tracks, each with 26 sectors of 128B + (single density) or 256B (double density). Tracks are numbered 0-76, + sectors 1-26. The RX8E (RX28) can store data in 8b mode or 12b mode. + In 8b mode, the controller reads or writes 128 bytes (128B or 256B) + per sector. In 12b mode, it reads or writes 64 (64 or 128) 12b words + per sector. The 12b words are bit packed into the first 96 (192) bytes + of the sector; the last 32 (64) bytes are zeroed on writes. +*/ + +#include "pdp8_defs.h" + +#define RX_NUMTR 77 /* tracks/disk */ +#define RX_M_TRACK 0377 +#define RX_NUMSC 26 /* sectors/track */ +#define RX_M_SECTOR 0177 /* cf Jones!! */ +#define RX_NUMBY 128 /* bytes/sector */ +#define RX2_NUMBY 256 +#define RX_NUMWD (RX_NUMBY / 2) /* words/sector */ +#define RX2_NUMWD (RX2_NUMBY / 2) +#define RX_SIZE (RX_NUMTR * RX_NUMSC * RX_NUMBY) /* bytes/disk */ +#define RX2_SIZE (RX_NUMTR * RX_NUMSC * RX2_NUMBY) +#define RX_NUMDR 2 /* drives/controller */ +#define RX_M_NUMDR 01 +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_DEN (UNIT_V_UF + 1) /* double density */ +#define UNIT_V_AUTO (UNIT_V_UF + 2) /* autosize */ +#define UNIT_WLK (1u << UNIT_V_WLK) +#define UNIT_DEN (1u << UNIT_V_DEN) +#define UNIT_AUTO (1u << UNIT_V_AUTO) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define IDLE 0 /* idle state */ +#define CMD8 1 /* 8b cmd, ho next */ +#define RWDS 2 /* rw, sect next */ +#define RWDT 3 /* rw, track next */ +#define RWXFR 4 /* rw, transfer */ +#define FILL 5 /* fill buffer */ +#define EMPTY 6 /* empty buffer */ +#define SDCNF 7 /* set dens, conf next */ +#define SDXFR 8 /* set dens, transfer */ +#define CMD_COMPLETE 9 /* set done next */ +#define INIT_COMPLETE 10 /* init compl next */ + +#define RXCS_V_FUNC 1 /* function */ +#define RXCS_M_FUNC 7 +#define RXCS_FILL 0 /* fill buffer */ +#define RXCS_EMPTY 1 /* empty buffer */ +#define RXCS_WRITE 2 /* write sector */ +#define RXCS_READ 3 /* read sector */ +#define RXCS_SDEN 4 /* set density (RX28) */ +#define RXCS_RXES 5 /* read status */ +#define RXCS_WRDEL 6 /* write del data */ +#define RXCS_ECODE 7 /* read error code */ +#define RXCS_DRV 0020 /* drive */ +#define RXCS_MODE 0100 /* mode */ +#define RXCS_MAINT 0200 /* maintenance */ +#define RXCS_DEN 0400 /* density (RX28) */ +#define RXCS_GETFNC(x) (((x) >> RXCS_V_FUNC) & RXCS_M_FUNC) + +#define RXES_CRC 0001 /* CRC error NI */ +#define RXES_ID 0004 /* init done */ +#define RXES_RX02 0010 /* RX02 (RX28) */ +#define RXES_DERR 0020 /* density err (RX28) */ +#define RXES_DEN 0040 /* density (RX28) */ +#define RXES_DD 0100 /* deleted data */ +#define RXES_DRDY 0200 /* drive ready */ + +#define TRACK u3 /* current track */ +#define READ_RXDBR ((rx_csr & RXCS_MODE)? AC | (rx_dbr & 0377): rx_dbr) +#define CALC_DA(t,s,b) (((t) * RX_NUMSC) + ((s) - 1)) * b + +extern int32 int_req, int_enable, dev_done; + +int32 rx_28 = 0; /* controller type */ +int32 rx_tr = 0; /* xfer ready flag */ +int32 rx_err = 0; /* error flag */ +int32 rx_csr = 0; /* control/status */ +int32 rx_dbr = 0; /* data buffer */ +int32 rx_esr = 0; /* error status */ +int32 rx_ecode = 0; /* error code */ +int32 rx_track = 0; /* desired track */ +int32 rx_sector = 0; /* desired sector */ +int32 rx_state = IDLE; /* controller state */ +int32 rx_cwait = 100; /* command time */ +int32 rx_swait = 10; /* seek, per track */ +int32 rx_xwait = 1; /* tr set time */ +int32 rx_stopioe = 0; /* stop on error */ +uint8 rx_buf[RX2_NUMBY] = { 0 }; /* sector buffer */ +int32 rx_bptr = 0; /* buffer pointer */ + +DEVICE rx_dev; +int32 rx (int32 IR, int32 AC); +t_stat rx_svc (UNIT *uptr); +t_stat rx_reset (DEVICE *dptr); +t_stat rx_boot (int32 unitno, DEVICE *dptr); +t_stat rx_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rx_attach (UNIT *uptr, char *cptr); +void rx_cmd (void); +void rx_done (int32 esr_flags, int32 new_ecode); +t_stat rx_settype (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, void *desc); + +/* RX8E data structures + + rx_dev RX device descriptor + rx_unit RX unit list + rx_reg RX register list + rx_mod RX modifier list +*/ + +DIB rx_dib = { DEV_RX, 1, { &rx } }; + +UNIT rx_unit[] = { + { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+ + UNIT_ROABLE, RX_SIZE) }, + { UDATA (&rx_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF+ + UNIT_ROABLE, RX_SIZE) } + }; + +REG rx_reg[] = { + { ORDATA (RXCS, rx_csr, 12) }, + { ORDATA (RXDB, rx_dbr, 12) }, + { ORDATA (RXES, rx_esr, 12) }, + { ORDATA (RXERR, rx_ecode, 8) }, + { ORDATA (RXTA, rx_track, 8) }, + { ORDATA (RXSA, rx_sector, 8) }, + { DRDATA (STAPTR, rx_state, 4), REG_RO }, + { DRDATA (BUFPTR, rx_bptr, 8) }, + { FLDATA (TR, rx_tr, 0) }, + { FLDATA (ERR, rx_err, 0) }, + { FLDATA (DONE, dev_done, INT_V_RX) }, + { FLDATA (ENABLE, int_enable, INT_V_RX) }, + { FLDATA (INT, int_req, INT_V_RX) }, + { DRDATA (CTIME, rx_cwait, 24), PV_LEFT }, + { DRDATA (STIME, rx_swait, 24), PV_LEFT }, + { DRDATA (XTIME, rx_xwait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, rx_stopioe, 0) }, + { BRDATA (SBUF, rx_buf, 8, 8, RX2_NUMBY) }, + { FLDATA (RX28, rx_28, 0), REG_HRO }, + { URDATA (CAPAC, rx_unit[0].capac, 10, T_ADDR_W, 0, + RX_NUMDR, REG_HRO | PV_LEFT) }, + { ORDATA (DEVNUM, rx_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB rx_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "RX28", &rx_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, NULL, "RX8E", &rx_settype, NULL, NULL }, + { MTAB_XTD | MTAB_VDV, 0, "TYPE", NULL, NULL, &rx_showtype, NULL }, + { (UNIT_DEN+UNIT_ATT), UNIT_ATT, "single density", NULL, NULL }, + { (UNIT_DEN+UNIT_ATT), (UNIT_DEN+UNIT_ATT), "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), 0, "single density", NULL, NULL }, + { (UNIT_AUTO+UNIT_DEN+UNIT_ATT), UNIT_DEN, "double density", NULL, NULL }, + { (UNIT_AUTO+UNIT_ATT), UNIT_AUTO, "autosize", NULL, NULL }, + { UNIT_AUTO, UNIT_AUTO, NULL, "AUTOSIZE", NULL }, + { (UNIT_AUTO+UNIT_DEN), 0, NULL, "SINGLE", &rx_set_size }, + { (UNIT_AUTO+UNIT_DEN), UNIT_DEN, NULL, "DOUBLE", &rx_set_size }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE rx_dev = { + "RX", rx_unit, rx_reg, rx_mod, + RX_NUMDR, 8, 20, 1, 8, 8, + NULL, NULL, &rx_reset, + &rx_boot, &rx_attach, NULL, + &rx_dib, DEV_DISABLE + }; + +/* IOT routine */ + +int32 rx (int32 IR, int32 AC) +{ +int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */ + +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* unused */ + break; + + case 1: /* LCD */ + if (rx_state != IDLE) return AC; /* ignore if busy */ + dev_done = dev_done & ~INT_RX; /* clear done, int */ + int_req = int_req & ~INT_RX; + rx_tr = rx_err = 0; /* clear flags */ + rx_bptr = 0; /* clear buf pointer */ + if (rx_28 && (AC & RXCS_MODE)) { /* RX28 8b mode? */ + rx_dbr = rx_csr = AC & 0377; /* save 8b */ + rx_tr = 1; /* xfer is ready */ + rx_state = CMD8; /* wait for part 2 */ + } + else { + rx_dbr = rx_csr = AC; /* save new command */ + rx_cmd (); /* issue command */ + } + return 0; /* clear AC */ + + case 2: /* XDR */ + switch (rx_state & 017) { /* case on state */ + + case EMPTY: /* emptying buffer */ + sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */ + return READ_RXDBR; /* return data reg */ + + case CMD8: /* waiting for cmd */ + rx_dbr = AC & 0377; + rx_csr = (rx_csr & 0377) | ((AC & 017) << 8); + rx_cmd (); + break; + + case RWDS:case RWDT:case FILL:case SDCNF: /* waiting for data */ + rx_dbr = AC; /* save data */ + sim_activate (&rx_unit[drv], rx_xwait); /* schedule */ + break; + + default: /* default */ + return READ_RXDBR; /* return data reg */ + } + break; + + case 3: /* STR */ + if (rx_tr != 0) { + rx_tr = 0; + return IOT_SKP + AC; + } + break; + + case 4: /* SER */ + if (rx_err != 0) { + rx_err = 0; + return IOT_SKP + AC; + } + break; + + case 5: /* SDN */ + if ((dev_done & INT_RX) != 0) { + dev_done = dev_done & ~INT_RX; + int_req = int_req & ~INT_RX; + return IOT_SKP + AC; + } + break; + + case 6: /* INTR */ + if (AC & 1) int_enable = int_enable | INT_RX; + else int_enable = int_enable & ~INT_RX; + int_req = INT_UPDATE; + break; + + case 7: /* INIT */ + rx_reset (&rx_dev); /* reset device */ + break; + } /* end case pulse */ + +return AC; +} + +void rx_cmd (void) +{ +int32 drv = ((rx_csr & RXCS_DRV)? 1: 0); /* get drive number */ + +switch (RXCS_GETFNC (rx_csr)) { /* decode command */ + + case RXCS_FILL: + rx_state = FILL; /* state = fill */ + rx_tr = 1; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + + case RXCS_EMPTY: + rx_state = EMPTY; /* state = empty */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + sim_activate (&rx_unit[drv], rx_xwait); /* sched xfer */ + break; + + case RXCS_READ: case RXCS_WRITE: case RXCS_WRDEL: + rx_state = RWDS; /* state = get sector */ + rx_tr = 1; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + + case RXCS_SDEN: + if (rx_28) { /* RX28? */ + rx_state = SDCNF; /* state = get conf */ + rx_tr = 1; /* xfer is ready */ + rx_esr = rx_esr & RXES_ID; /* clear errors */ + break; + } /* else fall thru */ + default: + rx_state = CMD_COMPLETE; /* state = cmd compl */ + sim_activate (&rx_unit[drv], rx_cwait); /* sched done */ + break; + } /* end switch func */ + +return; +} + +/* Unit service; the action to be taken depends on the transfer state: + + IDLE Should never get here + RWDS Save sector, set TR, set RWDT + RWDT Save track, set RWXFR + RWXFR Read/write buffer + FILL copy dbr to rx_buf[rx_bptr], advance ptr + if rx_bptr > max, finish command, else set tr + EMPTY if rx_bptr > max, finish command, else + copy rx_buf[rx_bptr] to dbr, advance ptr, set tr + CMD_COMPLETE copy requested data to dbr, finish command + INIT_COMPLETE read drive 0, track 1, sector 1 to buffer, finish command + + For RWDT and CMD_COMPLETE, the input argument is the selected drive; + otherwise, it is drive 0. +*/ + +t_stat rx_svc (UNIT *uptr) +{ +int32 i, func, byptr, bps, wps; +int8 *fbuf = uptr->filebuf; +uint32 da; +#define PTR12(x) (((x) + (x) + (x)) >> 1) + +if (rx_28 && (uptr->flags & UNIT_DEN)) /* RX28 and double density? */ + bps = RX2_NUMBY; /* double bytes/sector */ +else bps = RX_NUMBY; /* RX8E, normal count */ +wps = bps / 2; +func = RXCS_GETFNC (rx_csr); /* get function */ +switch (rx_state) { /* case on state */ + + case IDLE: /* idle */ + return SCPE_IERR; + + case EMPTY: /* empty buffer */ + if (rx_csr & RXCS_MODE) { /* 8b xfer? */ + if (rx_bptr >= bps) { /* done? */ + rx_done (0, 0); /* set done */ + break; /* and exit */ + } + rx_dbr = rx_buf[rx_bptr]; /* else get data */ + } + else { + byptr = PTR12 (rx_bptr); /* 12b xfer */ + if (rx_bptr >= wps) { /* done? */ + rx_done (0, 0); /* set done */ + break; /* and exit */ + } + rx_dbr = (rx_bptr & 1)? /* get data */ + ((rx_buf[byptr] & 017) << 8) | rx_buf[byptr + 1]: + (rx_buf[byptr] << 4) | ((rx_buf[byptr + 1] >> 4) & 017); + } + rx_bptr = rx_bptr + 1; + rx_tr = 1; + break; + + case FILL: /* fill buffer */ + if (rx_csr & RXCS_MODE) { /* 8b xfer? */ + rx_buf[rx_bptr] = rx_dbr; /* fill buffer */ + rx_bptr = rx_bptr + 1; + if (rx_bptr < bps) rx_tr = 1; /* if more, set xfer */ + else rx_done (0, 0); /* else done */ + } + else { + byptr = PTR12 (rx_bptr); /* 12b xfer */ + if (rx_bptr & 1) { /* odd or even? */ + rx_buf[byptr] = (rx_buf[byptr] & 0360) | ((rx_dbr >> 8) & 017); + rx_buf[byptr + 1] = rx_dbr & 0377; + } + else { + rx_buf[byptr] = (rx_dbr >> 4) & 0377; + rx_buf[byptr + 1] = (rx_dbr & 017) << 4; + } + rx_bptr = rx_bptr + 1; + if (rx_bptr < wps) rx_tr = 1; /* if more, set xfer */ + else { + for (i = PTR12 (wps); i < bps; i++) + rx_buf[i] = 0; /* else fill sector */ + rx_done (0, 0); /* set done */ + } + } + break; + + case RWDS: /* wait for sector */ + rx_sector = rx_dbr & RX_M_SECTOR; /* save sector */ + rx_tr = 1; /* set xfer ready */ + rx_state = RWDT; /* advance state */ + return SCPE_OK; + + case RWDT: /* wait for track */ + rx_track = rx_dbr & RX_M_TRACK; /* save track */ + rx_state = RWXFR; + sim_activate (uptr, /* sched done */ + rx_swait * abs (rx_track - uptr->TRACK)); + return SCPE_OK; + + case RWXFR: /* transfer */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (0, 0110); /* done, error */ + return IORETURN (rx_stopioe, SCPE_UNATT); + } + if (rx_track >= RX_NUMTR) { /* bad track? */ + rx_done (0, 0040); /* done, error */ + break; + } + uptr->TRACK = rx_track; /* now on track */ + if ((rx_sector == 0) || (rx_sector > RX_NUMSC)) { /* bad sect? */ + rx_done (0, 0070); /* done, error */ + break; + } + if (rx_28 && /* RX28? */ + (((uptr->flags & UNIT_DEN) != 0) ^ + ((rx_csr & RXCS_DEN) != 0))) { /* densities agree? */ + rx_done (RXES_DERR, 0240); /* no, error */ + break; + } + da = CALC_DA (rx_track, rx_sector, bps); /* get disk address */ + if (func == RXCS_WRDEL) rx_esr = rx_esr | RXES_DD; /* del data? */ + if (func == RXCS_READ) { /* read? */ + for (i = 0; i < bps; i++) rx_buf[i] = fbuf[da + i]; + } + else { /* write */ + if (uptr->flags & UNIT_WPRT) { /* locked? */ + rx_done (0, 0100); /* done, error */ + break; + } + for (i = 0; i < bps; i++) fbuf[da + i] = rx_buf[i]; + da = da + bps; + if (da > uptr->hwmark) uptr->hwmark = da; + } + rx_done (0, 0); /* done */ + break; + + case SDCNF: /* confirm set density */ + if ((rx_dbr & 0377) != 0111) { /* confirmed? */ + rx_done (0, 0250); /* no, error */ + break; + } + rx_state = SDXFR; /* next state */ + sim_activate (uptr, rx_cwait * 100); /* schedule operation */ + break; + + case SDXFR: /* erase disk */ + for (i = 0; i < (int32) uptr->capac; i++) fbuf[i] = 0; + uptr->hwmark = uptr->capac; + if (rx_csr & RXCS_DEN) uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + rx_done (0, 0); + break; + + case CMD_COMPLETE: /* command complete */ + if (func == RXCS_ECODE) { /* read ecode? */ + rx_dbr = rx_ecode; /* set dbr */ + rx_done (0, -1); /* don't update */ + } + else if (rx_28) { /* no, read sta; RX28? */ + rx_esr = rx_esr & ~RXES_DERR; /* assume dens match */ + if (((uptr->flags & UNIT_DEN) != 0) ^ /* densities mismatch? */ + ((rx_csr & RXCS_DEN) != 0)) + rx_done (RXES_DERR, 0240); /* yes, error */ + else rx_done (0, 0); /* no, ok */ + } + else rx_done (0, 0); /* RX8E status */ + break; + + case INIT_COMPLETE: /* init complete */ + rx_unit[0].TRACK = 1; /* drive 0 to trk 1 */ + rx_unit[1].TRACK = 0; /* drive 1 to trk 0 */ + if ((rx_unit[0].flags & UNIT_BUF) == 0) { /* not buffered? */ + rx_done (RXES_ID, 0010); /* init done, error */ + break; + } + da = CALC_DA (1, 1, bps); /* track 1, sector 1 */ + for (i = 0; i < bps; i++) /* read sector */ + rx_buf[i] = fbuf[da + i]; + rx_done (RXES_ID, 0); /* set done */ + if ((rx_unit[1].flags & UNIT_ATT) == 0) rx_ecode = 0020; + break; + } /* end case state */ + +return SCPE_OK; +} + +/* Command complete. Set done and put final value in interface register, + return to IDLE state. +*/ + +void rx_done (int32 esr_flags, int32 new_ecode) +{ +int32 drv = (rx_csr & RXCS_DRV)? 1: 0; + +rx_state = IDLE; /* now idle */ +dev_done = dev_done | INT_RX; /* set done */ +int_req = INT_UPDATE; /* update ints */ +rx_esr = (rx_esr | esr_flags) & ~(RXES_DRDY|RXES_RX02|RXES_DEN); +if (rx_28) rx_esr = rx_esr | RXES_RX02; /* RX28? */ +if (rx_unit[drv].flags & UNIT_ATT) { /* update drv rdy */ + rx_esr = rx_esr | RXES_DRDY; + if (rx_unit[drv].flags & UNIT_DEN) /* update density */ + rx_esr = rx_esr | RXES_DEN; + } +if (new_ecode > 0) rx_err = 1; /* test for error */ +if (new_ecode < 0) return; /* don't update? */ +rx_ecode = new_ecode; /* update ecode */ +rx_dbr = rx_esr; /* update RXDB */ +return; +} + +/* Reset routine. The RX is one of the few devices that schedules + an I/O transfer as part of its initialization */ + +t_stat rx_reset (DEVICE *dptr) +{ +rx_dbr = rx_csr = 0; /* 12b mode, drive 0 */ +rx_esr = rx_ecode = 0; /* clear error */ +rx_tr = rx_err = 0; /* clear flags */ +rx_track = rx_sector = 0; /* clear address */ +rx_state = IDLE; /* ctrl idle */ +dev_done = dev_done & ~INT_RX; /* clear done, int */ +int_req = int_req & ~INT_RX; +int_enable = int_enable & ~INT_RX; +sim_cancel (&rx_unit[1]); /* cancel drive 1 */ +if (dptr->flags & DEV_DIS) sim_cancel (&rx_unit[0]); /* disabled? */ +else if (rx_unit[0].flags & UNIT_BUF) { /* attached? */ + rx_state = INIT_COMPLETE; /* yes, sched init */ + sim_activate (&rx_unit[0], rx_swait * abs (1 - rx_unit[0].TRACK)); + } +else rx_done (rx_esr | RXES_ID, 0010); /* no, error */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat rx_attach (UNIT *uptr, char *cptr) +{ +uint32 sz; + +if ((uptr->flags & UNIT_AUTO) && (sz = sim_fsize_name (cptr))) { + if (sz > RX_SIZE) uptr->flags = uptr->flags | UNIT_DEN; + else uptr->flags = uptr->flags & ~UNIT_DEN; + } +uptr->capac = (uptr->flags & UNIT_DEN)? RX2_SIZE: RX_SIZE; +return attach_unit (uptr, cptr); +} + +/* Set size routine */ + +t_stat rx_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +if ((rx_28 == 0) && val) return SCPE_NOFNC; /* not on RX8E */ +uptr->capac = val? RX2_SIZE: RX_SIZE; +return SCPE_OK; +} + +/* Set controller type */ + +t_stat rx_settype (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i; + +if ((val < 0) || (val > 1) || (cptr != NULL)) return SCPE_ARG; +if (val == rx_28) return SCPE_OK; +for (i = 0; i < RX_NUMDR; i++) { + if (rx_unit[i].flags & UNIT_ATT) return SCPE_ALATT; + } +for (i = 0; i < RX_NUMDR; i++) { + if (val) rx_unit[i].flags = rx_unit[i].flags | UNIT_DEN | UNIT_AUTO; + else rx_unit[i].flags = rx_unit[i].flags & ~(UNIT_DEN | UNIT_AUTO); + rx_unit[i].capac = val? RX2_SIZE: RX_SIZE; + } +rx_28 = val; +return SCPE_OK; +} + +/* Show controller type */ + +t_stat rx_showtype (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (rx_28) fprintf (st, "RX28"); +else fprintf (st, "RX8E"); +return SCPE_OK; +} + +/* Bootstrap routine */ + +#define BOOT_START 022 +#define BOOT_ENTRY 022 +#define BOOT_INST 060 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) +#define BOOT2_START 020 +#define BOOT2_ENTRY 033 +#define BOOT2_LEN (sizeof (boot2_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 06755, /* 22, SDN */ + 05022, /* 23, JMP .-1 */ + 07126, /* 24, CLL CML RTL ; read command + */ + 01060, /* 25, TAD UNIT ; unit no */ + 06751, /* 26, LCD ; load read+unit */ + 07201, /* 27, CLA IAC ; AC = 1 */ + 04053, /* 30, JMS LOAD ; load sector */ + 04053, /* 31, JMS LOAD ; load track */ + 07104, /* 32, CLL RAL ; AC = 2 */ + 06755, /* 33, SDN */ + 05054, /* 34, JMP LOAD+1 */ + 06754, /* 35, SER */ + 07450, /* 36, SNA ; more to do? */ + 07610, /* 37, CLA SKP ; error */ + 05046, /* 40, JMP 46 ; go empty */ + 07402, /* 41-45, HALT ; error */ + 07402, + 07402, + 07402, + 07402, + 06751, /* 46, LCD ; load empty */ + 04053, /* 47, JMS LOAD ; get data */ + 03002, /* 50, DCA 2 ; store */ + 02050, /* 51, ISZ 50 ; incr store */ + 05047, /* 52, JMP 47 ; loop until done */ + 00000, /* LOAD, 0 */ + 06753, /* 54, STR */ + 05033, /* 55, JMP 33 */ + 06752, /* 56, XDR */ + 05453, /* 57, JMP I LOAD */ + 07024, /* UNIT, CML RAL ; for unit 1 */ + 06030 /* 61, KCC */ + }; + +static const uint16 boot2_rom[] = { + 01061, /* READ, TAD UNIT ; next unit+den */ + 01046, /* 21, TAD CON360 ; add in 360 */ + 00060, /* 22, AND CON420 ; mask to 420 */ + 03061, /* 23, DCA UNIT ; 400,420,0,20... */ + 07327, /* 24, STL CLA IAC RTL ; AC = 6 = read */ + 01061, /* 25, TAD UNIT ; +unit+den */ + 06751, /* 26, LCD ; load cmd */ + 07201, /* 27, CLA IAC; ; AC = 1 = trksec */ + 04053, /* 30, JMS LOAD ; load trk */ + 04053, /* 31, JMS LOAD ; load sec */ + 07004, /* CN7004, RAL ; AC = 2 = empty */ + 06755, /* START, SDN ; done? */ + 05054, /* 34, JMP LOAD+1 ; check xfr */ + 06754, /* 35, SER ; error? */ + 07450, /* 36, SNA ; AC=0 on start */ + 05020, /* 37, JMP RD ; try next den,un */ + 01061, /* 40, TAD UNIT ; +unit+den */ + 06751, /* 41, LCD ; load cmd */ + 01061, /* 42, TAD UNIT ; set 60 for sec boot */ + 00046, /* 43, AND CON360 ; only density */ + 01032, /* 44, TAD CN7004 ; magic */ + 03060, /* 45, DCA 60 */ + 00360, /* CON360, 360 ; NOP */ + 04053, /* 47, JMS LOAD ; get data */ + 03002, /* 50, DCA 2 ; store */ + 02050, /* 51, ISZ .-1 ; incr store */ + 05047, /* 52, JMP .-3 ; loop until done */ + 00000, /* LOAD, 0 */ + 06753, /* 54, STR ; xfr ready? */ + 05033, /* 55, JMP 33 ; no, chk done */ + 06752, /* 56, XDR ; get word */ + 05453, /* 57, JMP I 53 ; return */ + 00420, /* CON420, 420 ; toggle */ + 00020 /* UNIT, 20 ; unit+density */ + }; + +t_stat rx_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; +extern uint16 M[]; + +if (rx_dib.dev != DEV_RX) return STOP_NOTSTD; /* only std devno */ +if (rx_28) { + for (i = 0; i < BOOT2_LEN; i++) M[BOOT2_START + i] = boot2_rom[i]; + saved_PC = BOOT2_ENTRY; + } +else { + for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; + M[BOOT_INST] = unitno? 07024: 07004; + saved_PC = BOOT_ENTRY; + } +return SCPE_OK; +} diff --git a/PDP8/pdp8_sys.c b/PDP8/pdp8_sys.c new file mode 100644 index 0000000..c8ffa52 --- /dev/null +++ b/PDP8/pdp8_sys.c @@ -0,0 +1,979 @@ +/* pdp8_sys.c: PDP-8 simulator interface + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 24-Jun-08 RMS Fixed bug in new rim loader (found by Don North) + 24-May-08 RMS Fixed signed/unsigned declaration inconsistency + 03-Sep-07 RMS Added FPP8 support + Rewrote rim and binary loaders + 15-Dec-06 RMS Added TA8E support, IOT disambiguation + 30-Oct-06 RMS Added infinite loop stop + 18-Oct-06 RMS Re-ordered device list + 17-Oct-03 RMS Added TSC8-75, TD8E support, DECtape off reel message + 25-Apr-03 RMS Revised for extended file support + 30-Dec-01 RMS Revised for new TTX + 26-Nov-01 RMS Added RL8A support + 17-Sep-01 RMS Removed multiconsole support + 16-Sep-01 RMS Added TSS/8 packed char support, added KL8A support + 27-May-01 RMS Added multiconsole support + 18-Mar-01 RMS Added DF32 support + 14-Mar-01 RMS Added extension detection of RIM binary tapes + 15-Feb-01 RMS Added DECtape support + 30-Oct-00 RMS Added support for examine to file + 27-Oct-98 RMS V2.4 load interface + 10-Apr-98 RMS Added RIM loader support + 17-Feb-97 RMS Fixed bug in handling of bin loader fields +*/ + +#include "pdp8_defs.h" +#include + +extern DEVICE cpu_dev; +extern UNIT cpu_unit; +extern DEVICE tsc_dev; +extern DEVICE ptr_dev, ptp_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE clk_dev, lpt_dev; +extern DEVICE rk_dev, rl_dev; +extern DEVICE rx_dev; +extern DEVICE df_dev, rf_dev; +extern DEVICE dt_dev, td_dev; +extern DEVICE mt_dev, ct_dev; +extern DEVICE ttix_dev, ttox_dev; +extern REG cpu_reg[]; +extern uint16 M[]; +extern int32 sim_switches; + +t_stat fprint_sym_fpp (FILE *of, t_value *val); +t_stat parse_sym_fpp (char *cptr, t_value *val); +char *parse_field (char *cptr, uint32 max, uint32 *val, uint32 c); +char *parse_fpp_xr (char *cptr, uint32 *xr, t_bool inc); +int32 test_fpp_addr (uint32 ad, uint32 max); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax maximum number of words for examine/deposit + sim_devices array of pointers to simulated devices + sim_consoles array of pointers to consoles (if more than one) + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "PDP-8"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 4; + +DEVICE *sim_devices[] = { + &cpu_dev, + &tsc_dev, + &clk_dev, + &ptr_dev, + &ptp_dev, + &tti_dev, + &tto_dev, + &ttix_dev, + &ttox_dev, + &lpt_dev, + &rk_dev, + &rl_dev, + &rx_dev, + &df_dev, + &rf_dev, + &dt_dev, + &td_dev, + &mt_dev, + &ct_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unimplemented instruction", + "HALT instruction", + "Breakpoint", + "Non-standard device number", + "DECtape off reel", + "Infinite loop" + }; + +/* Ambiguous device list - these devices have overlapped IOT codes */ + +DEVICE *amb_dev[] = { + &rl_dev, + &ct_dev, + &td_dev, + NULL + }; + +#define AMB_RL (1 << 12) +#define AMB_CT (2 << 12) +#define AMB_TD (3 << 12) + +/* RIM loader format consists of alternating pairs of addresses and 12-bit + words. It can only operate in field 0 and is not checksummed. +*/ + +t_stat sim_load_rim (FILE *fi) +{ +int32 origin, hi, lo, wd; + +origin = 0200; +do { /* skip leader */ + if ((hi = getc (fi)) == EOF) + return SCPE_FMT; + } while ((hi == 0) || (hi >= 0200)); +do { /* data block */ + if ((lo = getc (fi)) == EOF) + return SCPE_FMT; + wd = (hi << 6) | lo; + if (wd > 07777) origin = wd & 07777; + else M[origin++ & 07777] = wd; + if ((hi = getc (fi)) == EOF) + return SCPE_FMT; + } while (hi < 0200); /* until trailer */ +return SCPE_OK; +} + +/* BIN loader format consists of a string of 12-bit words (made up from + 7-bit characters) between leader and trailer (200). The last word on + tape is the checksum. A word with the "link" bit set is a new origin; + a character > 0200 indicates a change of field. +*/ + +int32 sim_bin_getc (FILE *fi, uint32 *newf) +{ +int32 c, rubout; + +rubout = 0; /* clear toggle */ +while ((c = getc (fi)) != EOF) { /* read char */ + if (rubout) /* toggle set? */ + rubout = 0; /* clr, skip */ + else if (c == 0377) /* rubout? */ + rubout = 1; /* set, skip */ + else if (c > 0200) /* channel 8 set? */ + *newf = (c & 070) << 9; /* change field */ + else return c; /* otherwise ok */ + } +return EOF; +} + +t_stat sim_load_bin (FILE *fi) +{ +int32 hi, lo, wd, csum, t; +uint32 field, newf, origin; + +do { /* skip leader */ + if ((hi = sim_bin_getc (fi, &newf)) == EOF) + return SCPE_FMT; + } while ((hi == 0) || (hi >= 0200)); +csum = origin = field = newf = 0; /* init */ +for (;;) { /* data blocks */ + if ((lo = sim_bin_getc (fi, &newf)) == EOF) /* low char */ + return SCPE_FMT; + wd = (hi << 6) | lo; /* form word */ + t = hi; /* save for csum */ + if ((hi = sim_bin_getc (fi, &newf)) == EOF) /* next char */ + return SCPE_FMT; + if (hi == 0200) { /* end of tape? */ + if ((csum - wd) & 07777) /* valid csum? */ + return SCPE_CSUM; + return SCPE_OK; + } + csum = csum + t + lo; /* add to csum */ + if (wd > 07777) /* chan 7 set? */ + origin = wd & 07777; /* new origin */ + else { /* no, data */ + if ((field | origin) >= MEMSIZE) + return SCPE_NXM; + M[field | origin] = wd; + origin = (origin + 1) & 07777; + } + field = newf; /* update field */ + } +return SCPE_IERR; +} + +/* Binary loader + Two loader formats are supported: RIM loader (-r) and BIN (-b) loader. */ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +if ((sim_switches & SWMASK ('R')) || /* RIM format? */ + (match_ext (fnam, "RIM") && !(sim_switches & SWMASK ('B')))) + return sim_load_rim (fileref); +else return sim_load_bin (fileref); /* no, BIN */ +} + +/* Symbol tables */ + +#define I_V_FL 18 /* flag start */ +#define I_M_FL 07 /* flag mask */ +#define I_V_NPN 0 /* no operand */ +#define I_V_FLD 1 /* field change */ +#define I_V_MRF 2 /* mem ref */ +#define I_V_IOT 3 /* general IOT */ +#define I_V_OP1 4 /* operate 1 */ +#define I_V_OP2 5 /* operate 2 */ +#define I_V_OP3 6 /* operate 3 */ +#define I_V_IOA 7 /* ambiguous IOT */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_FLD (I_V_FLD << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_IOT (I_V_IOT << I_V_FL) +#define I_OP1 (I_V_OP1 << I_V_FL) +#define I_OP2 (I_V_OP2 << I_V_FL) +#define I_OP3 (I_V_OP3 << I_V_FL) +#define I_IOA (I_V_IOA << I_V_FL) + +static const int32 masks[] = { + 07777, 07707, 07000, 07000, + 07416, 07571, 017457, 077777, + }; + +/* Ambiguous device mnemonics must precede default mnemonics */ + +static const char *opcode[] = { + "SKON", "ION", "IOF", "SRQ", /* std IOTs */ + "GTF", "RTF", "SGT", "CAF", + "RPE", "RSF", "RRB", "RFC", "RFC RRB", /* reader/punch */ + "PCE", "PSF", "PCF", "PPC", "PLS", + "KCF", "KSF", "KCC", "KRS", "KIE", "KRB", /* console */ + "TLF", "TSF", "TCF", "TPC", "SPI", "TLS", + "SBE", "SPL", "CAL", /* power fail */ + "CLEI", "CLDI", "CLSC", "CLLE", "CLCL", "CLSK", /* clock */ + "CINT", "RDF", "RIF", "RIB", /* mem mmgt */ + "RMF", "SINT", "CUF", "SUF", + "RLDC", "RLSD", "RLMA", "RLCA", /* RL - ambiguous */ + "RLCB", "RLSA", "RLWC", + "RRER", "RRWC", "RRCA", "RRCB", + "RRSA", "RRSI", "RLSE", + "KCLR", "KSDR", "KSEN", "KSBF", /* CT - ambiguous */ + "KLSA", "KSAF", "KGOA", "KRSB", + "SDSS", "SDST", "SDSQ", /* TD - ambiguous */ + "SDLC", "SDLD", "SDRC", "SDRD", + "ADCL", "ADLM", "ADST", "ADRB", /* A/D */ + "ADSK", "ADSE", "ADLE", "ADRS", + "DCMA", "DMAR", "DMAW", /* DF/RF */ + "DCIM", "DSAC", "DIML", "DIMA", + "DCEA", "DEAL", "DEAC", + "DFSE", "DFSC", "DISK", "DMAC", + "DCXA", "DXAL", "DXAC", + "PSKF", "PCLF", "PSKE", /* LPT */ + "PSTB", "PSIE", "PCLF PSTB", "PCIE", + "LWCR", "CWCR", "LCAR", /* MT */ + "CCAR", "LCMR", "LFGR", "LDBR", + "RWCR", "CLT", "RCAR", + "RMSR", "RCMR", "RFSR", "RDBR", + "SKEF", "SKCB", "SKJD", "SKTR", "CLF", + "DSKP", "DCLR", "DLAG", /* RK */ + "DLCA", "DRST", "DLDC", "DMAN", + "LCD", "XDR", "STR", /* RX */ + "SER", "SDN", "INTR", "INIT", + "DTRA", "DTCA", "DTXA", "DTLA", /* DT */ + "DTSF", "DTRB", "DTLB", + "ETDS", "ESKP", "ECTF", "ECDF", /* TSC75 */ + "ERTB", "ESME", "ERIOT", "ETEN", + "FFST", "FPINT", "FPICL", "FPCOM", /* FPP8 */ + "FPHLT", "FPST", "FPRST", "FPIST", + "FMODE", "FMRB", + "FMRP", "FMDO", "FPEP", + + "CDF", "CIF", "CIF CDF", + "AND", "TAD", "ISZ", "DCA", "JMS", "JMP", "IOT", + "NOP", "NOP2", "NOP3", "SWAB", "SWBA", + "STL", "GLK", "STA", "LAS", "CIA", + "BSW", "RAL", "RTL", "RAR", "RTR", "RAL RAR", "RTL RTR", + "SKP", "SNL", "SZL", + "SZA", "SNA", "SZA SNL", "SNA SZL", + "SMA", "SPA", "SMA SNL", "SPA SZL", + "SMA SZA", "SPA SNA", "SMA SZA SNL", "SPA SNA SZL", + "SCL", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", + "SCA", "SCA SCL", "SCA MUY", "SCA DVI", + "SCA NMI", "SCA SHL", "SCA ASR", "SCA LSR", + "ACS", "MUY", "DVI", "NMI", "SHL", "ASR", "LSR", + "SCA", "DAD", "DST", "SWBA", + "DPSZ", "DPIC", "DCIM", "SAM", + "CLA", "CLL", "CMA", "CML", "IAC", /* encode only */ + "CLA", "OAS", "HLT", + "CLA", "MQA", "MQL", + NULL, NULL, NULL, NULL, /* decode only */ + NULL + }; + +static const int32 opc_val[] = { + 06000+I_NPN, 06001+I_NPN, 06002+I_NPN, 06003+I_NPN, + 06004+I_NPN, 06005+I_NPN, 06006+I_NPN, 06007+I_NPN, + 06010+I_NPN, 06011+I_NPN, 06012+I_NPN, 06014+I_NPN, 06016+I_NPN, + 06020+I_NPN, 06021+I_NPN, 06022+I_NPN, 06024+I_NPN, 06026+I_NPN, + 06030+I_NPN, 06031+I_NPN, 06032+I_NPN, 06034+I_NPN, 06035+I_NPN, 06036+I_NPN, + 06040+I_NPN, 06041+I_NPN, 06042+I_NPN, 06044+I_NPN, 06045+I_NPN, 06046+I_NPN, + 06101+I_NPN, 06102+I_NPN, 06103+I_NPN, + 06131+I_NPN, 06132+I_NPN, 06133+I_NPN, 06135+I_NPN, 06136+I_NPN, 06137+I_NPN, + 06204+I_NPN, 06214+I_NPN, 06224+I_NPN, 06234+I_NPN, + 06244+I_NPN, 06254+I_NPN, 06264+I_NPN, 06274+I_NPN, + 06600+I_IOA+AMB_RL, 06601+I_IOA+AMB_RL, 06602+I_IOA+AMB_RL, 06603+I_IOA+AMB_RL, + 06604+I_IOA+AMB_RL, 06605+I_IOA+AMB_RL, 06607+I_IOA+AMB_RL, + 06610+I_IOA+AMB_RL, 06611+I_IOA+AMB_RL, 06612+I_IOA+AMB_RL, 06613+I_IOA+AMB_RL, + 06614+I_IOA+AMB_RL, 06615+I_IOA+AMB_RL, 06617+I_IOA+AMB_RL, + 06700+I_IOA+AMB_CT, 06701+I_IOA+AMB_CT, 06702+I_IOA+AMB_CT, 06703+I_IOA+AMB_CT, + 06704+I_IOA+AMB_CT, 06705+I_IOA+AMB_CT, 06706+I_IOA+AMB_CT, 06707+I_IOA+AMB_CT, + 06771+I_IOA+AMB_TD, 06772+I_IOA+AMB_TD, 06773+I_IOA+AMB_TD, + 06774+I_IOA+AMB_TD, 06775+I_IOA+AMB_TD, 06776+I_IOA+AMB_TD, 06777+I_IOA+AMB_TD, + 06530+I_NPN, 06531+I_NPN, 06532+I_NPN, 06533+I_NPN, /* AD */ + 06534+I_NPN, 06535+I_NPN, 06536+I_NPN, 06537+I_NPN, + 06601+I_NPN, 06603+I_NPN, 06605+I_NPN, /* DF/RF */ + 06611+I_NPN, 06612+I_NPN, 06615+I_NPN, 06616+I_NPN, + 06611+I_NPN, 06615+I_NPN, 06616+I_NPN, + 06621+I_NPN, 06622+I_NPN, 06623+I_NPN, 06626+I_NPN, + 06641+I_NPN, 06643+I_NPN, 06645+I_NPN, + 06661+I_NPN, 06662+I_NPN, 06663+I_NPN, /* LPT */ + 06664+I_NPN, 06665+I_NPN, 06666+I_NPN, 06667+I_NPN, + 06701+I_NPN, 06702+I_NPN, 06703+I_NPN, /* MT */ + 06704+I_NPN, 06705+I_NPN, 06706+I_NPN, 06707+I_NPN, + 06711+I_NPN, 06712+I_NPN, 06713+I_NPN, + 06714+I_NPN, 06715+I_NPN, 06716+I_NPN, 06717+I_NPN, + 06721+I_NPN, 06722+I_NPN, 06723+I_NPN, 06724+I_NPN, 06725+I_NPN, + 06741+I_NPN, 06742+I_NPN, 06743+I_NPN, /* RK */ + 06744+I_NPN, 06745+I_NPN, 06746+I_NPN, 06747+I_NPN, + 06751+I_NPN, 06752+I_NPN, 06753+I_NPN, /* RX */ + 06754+I_NPN, 06755+I_NPN, 06756+I_NPN, 06757+I_NPN, + 06761+I_NPN, 06762+I_NPN, 06764+I_NPN, 06766+I_NPN, /* DT */ + 06771+I_NPN, 06772+I_NPN, 06774+I_NPN, + 06360+I_NPN, 06361+I_NPN, 06362+I_NPN, 06363+I_NPN, /* TSC */ + 06364+I_NPN, 06365+I_NPN, 06366+I_NPN, 06367+I_NPN, + 06550+I_NPN, 06551+I_NPN, 06552+I_NPN, 06553+I_NPN, /* FPP8 */ + 06554+I_NPN, 06555+I_NPN, 06556+I_NPN, 06557+I_NPN, + 06561+I_NPN, 06563+I_NPN, + 06564+I_NPN, 06565+I_NPN, 06567+I_NPN, + + 06201+I_FLD, 06202+I_FLD, 06203+I_FLD, + 00000+I_MRF, 01000+I_MRF, 02000+I_MRF, 03000+I_MRF, + 04000+I_MRF, 05000+I_MRF, 06000+I_IOT, + 07000+I_NPN, 07400+I_NPN, 07401+I_NPN, 07431+I_NPN, 07447+I_NPN, + 07120+I_NPN, 07204+I_NPN, 07240+I_NPN, 07604+I_NPN, 07041+I_NPN, + 07002+I_OP1, 07004+I_OP1, 07006+I_OP1, + 07010+I_OP1, 07012+I_OP1, 07014+I_OP1, 07016+I_OP1, + 07410+I_OP2, 07420+I_OP2, 07430+I_OP2, + 07440+I_OP2, 07450+I_OP2, 07460+I_OP2, 07470+I_OP2, + 07500+I_OP2, 07510+I_OP2, 07520+I_OP2, 07530+I_OP2, + 07540+I_OP2, 07550+I_OP2, 07560+I_OP2, 07570+I_OP2, + 07403+I_OP3, 07405+I_OP3, 07407+I_OP3, + 07411+I_OP3, 07413+I_OP3, 07415+I_OP3, 07417+I_OP3, + 07441+I_OP3, 07443+I_OP3, 07445+I_OP3, 07447+I_OP3, + 07451+I_OP3, 07453+I_OP3, 07455+I_OP3, 07457+I_OP3, + 017403+I_OP3, 017405+I_OP3, 0174017+I_OP3, + 017411+I_OP3, 017413+I_OP3, 017415+I_OP3, 017417+I_OP3, + 017441+I_OP3, 017443+I_OP3, 017445+I_OP3, 017447+I_OP3, + 017451+I_OP3, 017453+I_OP3, 017455+I_OP3, 017457+I_OP3, + 07200+I_OP1, 07100+I_OP1, 07040+I_OP1, 07020+I_OP1, 07001+I_OP1, + 07600+I_OP2, 07404+I_OP2, 07402+I_OP2, + 07601+I_OP3, 07501+I_OP3, 07421+I_OP3, + 07000+I_OP1, 07400+I_OP2, 07401+I_OP3, 017401+I_OP3, + -1 + }; + +/* Symbol tables for FPP-8 */ + +#define F_V_FL 18 /* flag start */ +#define F_M_FL 017 /* flag mask */ +#define F_V_NOP12 0 /* no opnd 12b */ +#define F_V_NOP9 1 /* no opnd 9b */ +#define F_V_AD15 2 /* 15b dir addr */ +#define F_V_AD15X 3 /* 15b dir addr indx */ +#define F_V_IMMX 4 /* 12b immm indx */ +#define F_V_X 5 /* index */ +#define F_V_MRI 6 /* mem ref ind */ +#define F_V_MR1D 7 /* mem ref dir 1 word */ +#define F_V_MR2D 8 /* mem ref dir 2 word */ +#define F_V_LEMU 9 /* LEA/IMUL */ +#define F_V_LEMUI 10 /* LEAI/IMULI */ +#define F_V_LTR 11 /* LTR */ +#define F_V_MRD 12 /* mem ref direct (enc) */ +#define F_NOP12 (F_V_NOP12 << F_V_FL) +#define F_NOP9 (F_V_NOP9 << F_V_FL) +#define F_AD15 (F_V_AD15 << F_V_FL) +#define F_AD15X (F_V_AD15X << F_V_FL) +#define F_IMMX (F_V_IMMX << F_V_FL) +#define F_X (F_V_X << F_V_FL) +#define F_MRI (F_V_MRI << F_V_FL) +#define F_MR1D (F_V_MR1D << F_V_FL) +#define F_MR2D (F_V_MR2D << F_V_FL) +#define F_LEMU (F_V_LEMU << F_V_FL) +#define F_LEMUI (F_V_LEMUI << F_V_FL) +#define F_LTR (F_V_LTR << F_V_FL) +#define F_MRD (F_V_MRD << F_V_FL) + +static const uint32 fmasks[] = { + 07777, 07770, 07770, 07600, + 07770, 07770, 07600, 07600, + 07600, 017600, 017600, 07670, + 07777 + }; + +/* Memory references are encode dir / decode 1D / decode 2D / indirect */ + +static const char *fopcode[] = { + "FEXIT", "FPAUSE", "FCLA", "FNEG", + "FNORM", "STARTF", "STARTD", "JAC", + "ALN", "ATX", "XTA", + "FNOP", "STARTE", + "LDX", "ADDX", + "FLDA", "FLDA", "FLDA", "FLDAI", + "JEQ", "JGE", "JLE", "JA", + "JNE", "JLT", "JGT", "JAL", + "SETX", "SETB", "JSA", "JSR", + "FADD", "FADD", "FADD", "FADDI", + "JNX", + "FSUB", "FSUB", "FSUB", "FSUBI", + "TRAP3", + "FDIV", "FDIV", "FDIV", "FDIVI", + "TRAP4", + "FMUL", "FMUL", "FMUL", "FMULI", + "LTREQ", "LTRGE", "LTRLE", "LTRA", + "LTRNE", "LTRLT", "LTRGT", "LTRAL", + "FADDM", "FADDM", "FADDM", "FADDMI", + "IMUL", "LEA", + "FSTA", "FSTA", "FSTA", "FSTAI", + "IMULI", "LEAI", + "FMULM", "FMULM", "FMULM", "FMULMI", + NULL + }; + +static const int32 fop_val[] = { + 00000+F_NOP12, 00001+F_NOP12, 00002+F_NOP12, 00003+F_NOP12, + 00004+F_NOP12, 00005+F_NOP12, 00006+F_NOP12, 00007+F_NOP12, + 00010+F_X, 00020+F_X, 00030+F_X, + 00040+F_NOP9, 00050+F_NOP9, + 00100+F_IMMX, 00110+F_IMMX, + 00000+F_MRD, 00200+F_MR1D, 00400+F_MR2D, 00600+F_MRI, + 01000+F_AD15, 01010+F_AD15, 01020+F_AD15, 01030+F_AD15, + 01040+F_AD15, 01050+F_AD15, 01060+F_AD15, 01070+F_AD15, + 01100+F_AD15, 01110+F_AD15, 01120+F_AD15, 01130+F_AD15, + 01000+F_MRD, 01200+F_MR1D, 01400+F_MR2D, 01600+F_MRI, + 02000+F_AD15X, + 02000+F_MRD, 02200+F_MR1D, 02400+F_MR2D, 02600+F_MRI, + 03000+F_AD15, + 03000+F_MRD, 03200+F_MR1D, 03400+F_MR2D, 03600+F_MRI, + 04000+F_AD15, + 04000+F_MRD, 04200+F_MR1D, 04400+F_MR2D, 04600+F_MRI, + 05000+F_LTR, 05010+F_LTR, 05020+F_LTR, 05030+F_LTR, + 05040+F_LTR, 05050+F_LTR, 05060+F_LTR, 05070+F_LTR, + 05000+F_MRD, 05200+F_MR1D, 05400+F_MR2D, 05600+F_MRI, + 016000+F_LEMU, 006000+F_LEMU, + 06000+F_MRD, 06200+F_MR1D, 06400+F_MR2D, 06600+F_MRI, + 017000+F_LEMUI, 007000+F_LEMUI, + 07000+F_MRD, 07200+F_MR1D, 07400+F_MR2D, 07600+F_MRI, + -1 + }; + +/* Operate decode + + Inputs: + *of = output stream + inst = mask bits + class = instruction class code + sp = space needed? + Outputs: + status = space needed +*/ + +int32 fprint_opr (FILE *of, int32 inst, int32 class, int32 sp) +{ +int32 i, j; + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == class) && (opc_val[i] & inst)) { /* same class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; + } + } +return sp; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to data + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) +#define SIXTOASC(x) (((x) >= 040)? (x): (x) + 0100) +#define TSSTOASC(x) ((x) + 040) + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, i, j, sp, inst, disp, opc; +extern int32 emode; +t_stat r; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +inst = val[0]; +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* characters? */ + fprintf (of, "%c", SIXTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", SIXTOASC (inst & 077)); + return SCPE_OK; + } +if (sw & SWMASK ('T')) { /* TSS8 packed? */ + fprintf (of, "%c", TSSTOASC ((inst >> 6) & 077)); + fprintf (of, "%c", TSSTOASC (inst & 077)); + return SCPE_OK; + } +if ((sw & SWMASK ('F')) && /* FPP8? */ + ((r = fprint_sym_fpp (of, val)) != SCPE_ARG)) + return r; +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +opc = (inst >> 9) & 07; /* get major opcode */ +if (opc == 07) /* operate? */ + inst = inst | ((emode & 1) << 12); /* include EAE mode */ +if (opc == 06) { /* IOT? */ + DEVICE *dptr; + DIB *dibp; + uint32 dno = (inst >> 3) & 077; + for (i = 0; (dptr = amb_dev[i]) != NULL; i++) { /* check amb devices */ + if ((dptr->ctxt == NULL) || /* no DIB or */ + (dptr->flags & DEV_DIS)) continue; /* disabled? skip */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if ((dno >= dibp->dev) || /* IOT for this dev? */ + (dno < (dibp->dev + dibp->num))) { + inst = inst | ((i + 1) << 12); /* disambiguate */ + break; /* done */ + } + } + } + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & 077777) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + + case I_V_NPN: case I_V_IOA: /* no operands */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_FLD: /* field change */ + fprintf (of, "%s %-o", opcode[i], (inst >> 3) & 07); + break; + + case I_V_MRF: /* mem ref */ + disp = inst & 0177; /* displacement */ + fprintf (of, "%s%s", opcode[i], ((inst & 00400)? " I ": " ")); + if (inst & 0200) { /* current page? */ + if (cflag) fprintf (of, "%-o", (addr & 07600) | disp); + else fprintf (of, "C %-o", disp); + } + else fprintf (of, "%-o", disp); /* page zero */ + break; + + case I_V_IOT: /* IOT */ + fprintf (of, "%s %-o", opcode[i], inst & 0777); + break; + + case I_V_OP1: /* operate group 1 */ + sp = fprint_opr (of, inst & 0361, j, 0); + if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; + + case I_V_OP2: /* operate group 2 */ + if (opcode[i]) fprintf (of, "%s", opcode[i]); /* skips */ + fprint_opr (of, inst & 0206, j, opcode[i] != NULL); + break; + + case I_V_OP3: /* operate group 3 */ + sp = fprint_opr (of, inst & 0320, j, 0); + if (opcode[i]) fprintf (of, (sp? " %s": "%s"), opcode[i]); + break; + } /* end case */ + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +uint32 cflag, d, i, j, k; +t_stat r; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0] | 0200; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* sixbit string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) cptr[0] & 077) << 6) | + ((t_value) cptr[1] & 077); + return SCPE_OK; + } +if ((sw & SWMASK ('T')) || ((*cptr == '"') && cptr++)) { /* TSS8 string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (((t_value) (cptr[0] - 040) & 077) << 6) | + ((t_value) (cptr[1] - 040) & 077); + return SCPE_OK; + } +if ((r = parse_sym_fpp (cptr, val)) != SCPE_ARG) /* FPP8 inst? */ + return r; + +/* Instruction parse */ + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 07777; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_IOT: /* IOT */ + if ((cptr = parse_field (cptr, 0777, &d, 0)) == NULL) + return SCPE_ARG; /* get dev+pulse */ + val[0] = val[0] | d; + break; + + case I_V_FLD: /* field */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if (opcode[i] != NULL) { + k = (opc_val[i] >> I_V_FL) & I_M_FL; + if (k != j) return SCPE_ARG; + val[0] = val[0] | (opc_val[i] & 07777); + } + else { + d = get_uint (gbuf, 8, 07, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | (d << 3); + break; + } + } + break; + + case I_V_MRF: /* mem ref */ + cptr = get_glyph (cptr, gbuf, 0); /* get next field */ + if (strcmp (gbuf, "I") == 0) { /* indirect? */ + val[0] = val[0] | 0400; + cptr = get_glyph (cptr, gbuf, 0); + } + if ((k = (strcmp (gbuf, "C") == 0)) || (strcmp (gbuf, "Z") == 0)) { + if ((cptr = parse_field (cptr, 0177, &d, 0)) == NULL) + return SCPE_ARG; + val[0] = val[0] | d | (k? 0200: 0); + } + else { + d = get_uint (gbuf, 8, 07777, &r); + if (r != SCPE_OK) return SCPE_ARG; + if (d <= 0177) val[0] = val[0] | d; + else if (cflag && (((addr ^ d) & 07600) == 0)) + val[0] = val[0] | (d & 0177) | 0200; + else return SCPE_ARG; + } + break; + + case I_V_OP1: case I_V_OP2: case I_V_OP3: /* operates */ + case I_V_NPN: case I_V_IOA: + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + k = opc_val[i] & 07777; + if ((opcode[i] == NULL) || (((k ^ val[0]) & 07000) != 0)) + return SCPE_ARG; + val[0] = val[0] | k; + } + break; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} + +/* FPP8 instruction decode */ + +t_stat fprint_sym_fpp (FILE *of, t_value *val) +{ +uint32 wd1, wd2, xr4b, xr3b, ad15; +uint32 i, j; +extern uint32 fpp_bra, fpp_cmd; + +wd1 = (uint32) val[0] | ((fpp_cmd & 04000) << 1); +wd2 = (uint32) val[1]; +xr4b = (wd1 >> 3) & 017; +xr3b = wd1 & 07; +ad15 = (xr3b << 12) | wd2; + +for (i = 0; fop_val[i] >= 0; i++) { /* loop thru ops */ + j = (fop_val[i] >> F_V_FL) & F_M_FL; /* get class */ + if ((fop_val[i] & 017777) == (wd1 & fmasks[j])) { /* match? */ + + switch (j) { /* case on class */ + case F_V_NOP12: + case F_V_NOP9: + case F_V_LTR: /* no operands */ + fprintf (of, "%s", fopcode[i]); + break; + + case F_V_X: /* index */ + fprintf (of, "%s %o", fopcode[i], xr3b); + break; + + case F_V_IMMX: /* index imm */ + fprintf (of, "%s %-o,%o", fopcode[i], wd2, xr3b); + return -1; /* extra word */ + + case F_V_AD15: /* 15b address */ + fprintf (of, "%s %-o", fopcode[i], ad15); + return -1; /* extra word */ + + case F_V_AD15X: /* 15b addr, indx */ + fprintf (of, "%s %-o", fopcode[i], ad15); + if (xr4b >= 010) + fprintf (of, ",%o+", xr4b & 7); + else fprintf (of, ",%o", xr4b); + return -1; /* extra word */ + + case F_V_MR1D: /* 1 word direct */ + ad15 = (fpp_bra + (3 * (wd1 & 0177))) & ADDRMASK; + fprintf (of, "%s %-o", fopcode[i], ad15); + break; + + case F_V_LEMU: + case F_V_MR2D: /* 2 word direct */ + fprintf (of, "%s %-o", fopcode[i], ad15); + if (xr4b >= 010) + fprintf (of, ",%o+", xr4b & 7); + else if (xr4b != 0) + fprintf (of, ",%o", xr4b); + return -1; /* extra word */ + + case F_V_LEMUI: + case F_V_MRI: /* indirect */ + ad15 = (fpp_bra + (3 * xr3b)) & ADDRMASK; + fprintf (of, "%s %-o", fopcode[i], ad15); + if (xr4b >= 010) + fprintf (of, ",%o+", xr4b & 7); + else if (xr4b != 0) + fprintf (of, ",%o", xr4b); + break; + + case F_V_MRD: /* encode only */ + return SCPE_IERR; + } + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* FPP8 instruction parse */ + +t_stat parse_sym_fpp (char *cptr, t_value *val) +{ +uint32 i, j, ad, xr; +int32 broff, nwd; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (fopcode[i] != NULL) && (strcmp (fopcode[i], gbuf) != 0) ; i++) ; +if (fopcode[i] == NULL) return SCPE_ARG; +val[0] = fop_val[i] & 07777; /* get value */ +j = (fop_val[i] >> F_V_FL) & F_M_FL; /* get class */ +xr = 0; +nwd = 0; + +switch (j) { /* case on class */ + + case F_V_NOP12: + case F_V_NOP9: + case F_V_LTR: /* no operands */ + break; + + case F_V_X: /* 3b XR */ + if ((cptr = parse_field (cptr, 07, &xr, 0)) == NULL) + return SCPE_ARG; + val[0] |= xr; + break; + + case F_V_IMMX: /* 12b, XR */ + if ((cptr = parse_field (cptr, 07777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr == 0) || + ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL)) + return SCPE_ARG; + val[0] |= xr; + val[++nwd] = ad; + break; + + case F_V_AD15: /* 15b addr */ + if ((cptr = parse_field (cptr, 077777, &ad, 0)) == NULL) + return SCPE_ARG; + val[0] |= (ad >> 12) & 07; + val[++nwd] = ad & 07777; + break; + + case F_V_AD15X: /* 15b addr, idx */ + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr == 0) || + ((cptr = parse_fpp_xr (cptr, &xr, FALSE)) == NULL)) + return SCPE_ARG; + val[0] |= ((xr << 3) | ((ad >> 12) & 07)); + val[++nwd] = ad & 07777; + break; + + case F_V_LEMUI: + case F_V_MRI: /* indirect */ + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr != 0) && + ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) + return SCPE_ARG; + if ((broff = test_fpp_addr (ad, 07)) < 0) + return SCPE_ARG; + val[0] |= ((xr << 3) | broff); + break; + + case F_V_MRD: /* direct */ + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if (((broff = test_fpp_addr (ad, 0177)) < 0) || + (*cptr != 0)) { + if ((*cptr != 0) && + ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) + return SCPE_ARG; + val[0] |= (00400 | (xr << 3) | ((ad >> 12) & 07)); + val[++nwd] = ad & 07777; + } + else val[0] |= (00200 | broff); + break; + + case F_V_LEMU: + if ((cptr = parse_field (cptr, 077777, &ad, ',')) == NULL) + return SCPE_ARG; + if ((*cptr != 0) && + ((cptr = parse_fpp_xr (cptr, &xr, TRUE)) == NULL)) + return SCPE_ARG; + val[0] |= ((xr << 3) | ((ad >> 12) & 07)); + val[++nwd] = ad & 07777; + break; + + case F_V_MR1D: + case F_V_MR2D: + return SCPE_IERR; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return -nwd; +} + +/* Parse field */ + +char *parse_field (char *cptr, uint32 max, uint32 *val, uint32 c) +{ +char gbuf[CBUFSIZE]; +t_stat r; + +cptr = get_glyph (cptr, gbuf, c); /* get field */ +*val = get_uint (gbuf, 8, max, &r); +if (r != SCPE_OK) + return NULL; +return cptr; +} + +/* Parse index register */ + +char *parse_fpp_xr (char *cptr, uint32 *xr, t_bool inc) +{ +char gbuf[CBUFSIZE]; +uint32 len; +t_stat r; + +cptr = get_glyph (cptr, gbuf, 0); /* get field */ +len = strlen (gbuf); +if (gbuf[len - 1] == '+') { + if (!inc) + return NULL; + gbuf[len - 1] = 0; + *xr = 010; + } +else *xr = 0; +*xr += get_uint (gbuf, 8, 7, &r); +if (r != SCPE_OK) + return NULL; +return cptr; +} + +/* Test address in range of base register */ + +int32 test_fpp_addr (uint32 ad, uint32 max) +{ +uint32 off; +extern uint32 fpp_bra; + +off = ad - fpp_bra; +if (((off % 3) != 0) || + (off > (max * 3))) + return -1; +return ((int32) off / 3); +} diff --git a/PDP8/pdp8_td.c b/PDP8/pdp8_td.c new file mode 100644 index 0000000..2f1cdd3 --- /dev/null +++ b/PDP8/pdp8_td.c @@ -0,0 +1,913 @@ +/* pdp8_td.c: PDP-8 simple DECtape controller (TD8E) simulator + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module was inspired by Gerold Pauler's TD8E simulator for Doug Jones' + PDP8 simulator but tracks the hardware implementation more closely. + + td TD8E/TU56 DECtape + + 23-Jun-06 RMS Fixed switch conflict in ATTACH + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 09-Jan-04 RMS Changed sim_fsize calling sequence, added STOP_OFFR + + PDP-8 DECtapes are represented in memory by fixed length buffer of 12b words. + Three file formats are supported: + + 18b/36b 256 words per block [256 x 18b] + 16b 256 words per block [256 x 16b] + 12b 129 words per block [129 x 12b] + + When a 16b or 18/36b DECtape file is read in, it is converted to 12b format. + + DECtape motion is measured in 3b lines. Time between lines is 33.33us. + Tape density is nominally 300 lines per inch. The format of a DECtape (as + taken from the TD8E formatter) is: + + reverse end zone 8192 reverse end zone codes ~ 10 feet + reverse buffer 200 interblock codes + block 0 + : + block n + forward buffer 200 interblock codes + forward end zone 8192 forward end zone codes ~ 10 feet + + A block consists of five 18b header words, a tape-specific number of data + words, and five 18b trailer words. All systems except the PDP-8 use a + standard block length of 256 words; the PDP-8 uses a standard block length + of 86 words (x 18b = 129 words x 12b). + + Because a DECtape file only contains data, the simulator cannot support + write timing and mark track and can only do a limited implementation + of non-data words. Read assumes that the tape has been conventionally + written forward: + + header word 0 0 + header word 1 block number (for forward reads) + header words 2,3 0 + header word 4 checksum (for reverse reads) + : + trailer word 4 checksum (for forward reads) + trailer words 3,2 0 + trailer word 1 block number (for reverse reads) + trailer word 0 0 + + Write modifies only the data words and dumps the non-data words in the + bit bucket. +*/ + +#include "pdp8_defs.h" + +#define DT_NUMDR 2 /* #drives */ +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_V_8FMT (UNIT_V_UF + 1) /* 12b format */ +#define UNIT_V_11FMT (UNIT_V_UF + 2) /* 16b format */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_8FMT (1 << UNIT_V_8FMT) +#define UNIT_11FMT (1 << UNIT_V_11FMT) +#define STATE u3 /* unit state */ +#define LASTT u4 /* last time update */ +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +/* System independent DECtape constants */ + +#define DT_LPERMC 6 /* lines per mark track */ +#define DT_EZLIN (8192 * DT_LPERMC) /* end zone length */ +#define DT_BFLIN (200 * DT_LPERMC) /* end zone buffer */ +#define DT_HTLIN (5 * DT_LPERMC) /* lines per hdr/trlr */ + +/* 16b, 18b, 36b DECtape constants */ + +#define D18_WSIZE 6 /* word sizein lines */ +#define D18_BSIZE 384 /* block size in 12b */ +#define D18_TSIZE 578 /* tape size */ +#define D18_LPERB (DT_HTLIN + (D18_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D18_FWDEZ (DT_EZLIN + (D18_LPERB * D18_TSIZE)) +#define D18_CAPAC (D18_TSIZE * D18_BSIZE) /* tape capacity */ + +#define D18_NBSIZE ((D18_BSIZE * D8_WSIZE) / D18_WSIZE) +#define D18_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int32)) +#define D11_FILSIZ (D18_NBSIZE * D18_TSIZE * sizeof (int16)) + +/* 12b DECtape constants */ + +#define D8_WSIZE 4 /* word size in lines */ +#define D8_BSIZE 129 /* block size in 12b */ +#define D8_TSIZE 1474 /* tape size */ +#define D8_LPERB (DT_HTLIN + (D8_BSIZE * DT_WSIZE) + DT_HTLIN) +#define D8_FWDEZ (DT_EZLIN + (D8_LPERB * D8_TSIZE)) +#define D8_CAPAC (D8_TSIZE * D8_BSIZE) /* tape capacity */ +#define D8_FILSIZ (D8_CAPAC * sizeof (int16)) + +/* This controller */ + +#define DT_CAPAC D8_CAPAC /* default */ +#define DT_WSIZE D8_WSIZE + +/* Calculated constants, per unit */ + +#define DTU_BSIZE(u) (((u)->flags & UNIT_8FMT)? D8_BSIZE: D18_BSIZE) +#define DTU_TSIZE(u) (((u)->flags & UNIT_8FMT)? D8_TSIZE: D18_TSIZE) +#define DTU_LPERB(u) (((u)->flags & UNIT_8FMT)? D8_LPERB: D18_LPERB) +#define DTU_FWDEZ(u) (((u)->flags & UNIT_8FMT)? D8_FWDEZ: D18_FWDEZ) +#define DTU_CAPAC(u) (((u)->flags & UNIT_8FMT)? D8_CAPAC: D18_CAPAC) + +#define DT_LIN2BL(p,u) (((p) - DT_EZLIN) / DTU_LPERB (u)) +#define DT_LIN2OF(p,u) (((p) - DT_EZLIN) % DTU_LPERB (u)) + +/* Command register */ + +#define TDC_UNIT 04000 /* unit select */ +#define TDC_FWDRV 02000 /* fwd/rev */ +#define TDC_STPGO 01000 /* stop/go */ +#define TDC_RW 00400 /* read/write */ +#define TDC_MASK 07400 /* implemented */ +#define TDC_GETUNIT(x) (((x) & TDC_UNIT)? 1: 0) + +/* Status register */ + +#define TDS_WLO 00200 /* write lock */ +#define TDS_TME 00100 /* timing/sel err */ + +/* Mark track register and codes */ + +#define MTK_MASK 077 +#define MTK_REV_END 055 /* rev end zone */ +#define MTK_INTER 025 /* interblock */ +#define MTK_FWD_BLK 026 /* fwd block */ +#define MTK_REV_GRD 032 /* reverse guard */ +#define MTK_FWD_PRE 010 /* lock, etc */ +#define MTK_DATA 070 /* data */ +#define MTK_REV_PRE 073 /* lock, etc */ +#define MTK_FWD_GRD 051 /* fwd guard */ +#define MTK_REV_BLK 045 /* rev block */ +#define MTK_FWD_END 022 /* fwd end zone */ + +/* DECtape state */ + +#define STA_STOP 0 /* stopped */ +#define STA_DEC 2 /* decelerating */ +#define STA_ACC 4 /* accelerating */ +#define STA_UTS 6 /* up to speed */ +#define STA_DIR 1 /* fwd/rev */ + +#define ABS(x) (((x) < 0)? (-(x)): (x)) +#define MTK_BIT(c,p) (((c) >> (DT_LPERMC - 1 - ((p) % DT_LPERMC))) & 1) + +/* State and declarations */ + +int32 td_cmd = 0; /* command */ +int32 td_dat = 0; /* data */ +int32 td_mtk = 0; /* mark track */ +int32 td_slf = 0; /* single line flag */ +int32 td_qlf = 0; /* quad line flag */ +int32 td_tme = 0; /* timing error flag */ +int32 td_csum = 0; /* save check sum */ +int32 td_qlctr = 0; /* quad line ctr */ +int32 td_ltime = 20; /* interline time */ +int32 td_dctime = 40000; /* decel time */ +int32 td_stopoffr = 0; +static uint8 tdb_mtk[DT_NUMDR][D18_LPERB]; /* mark track bits */ + +DEVICE td_dev; +int32 td77 (int32 IR, int32 AC); +t_stat td_svc (UNIT *uptr); +t_stat td_reset (DEVICE *dptr); +t_stat td_attach (UNIT *uptr, char *cptr); +t_stat td_detach (UNIT *uptr); +t_stat td_boot (int32 unitno, DEVICE *dptr); +t_bool td_newsa (int32 newf); +t_bool td_setpos (UNIT *uptr); +int32 td_header (UNIT *uptr, int32 blk, int32 line); +int32 td_trailer (UNIT *uptr, int32 blk, int32 line); +int32 td_read (UNIT *uptr, int32 blk, int32 line); +void td_write (UNIT *uptr, int32 blk, int32 line, int32 datb); +int32 td_set_mtk (int32 code, int32 u, int32 k); +t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc); + +extern uint16 M[]; +extern int32 sim_switches; +extern int32 sim_is_running; + +/* TD data structures + + td_dev DT device descriptor + td_unit DT unit list + td_reg DT register list + td_mod DT modifier list +*/ + +DIB td_dib = { DEV_TD8E, 1, { &td77 } }; + +UNIT td_unit[] = { + { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) }, + { UDATA (&td_svc, UNIT_8FMT+UNIT_FIX+UNIT_ATTABLE+ + UNIT_DISABLE+UNIT_ROABLE, DT_CAPAC) } + }; + +REG td_reg[] = { + { GRDATA (TDCMD, td_cmd, 8, 4, 8) }, + { ORDATA (TDDAT, td_dat, 12) }, + { ORDATA (TDMTK, td_mtk, 6) }, + { FLDATA (TDSLF, td_slf, 0) }, + { FLDATA (TDQLF, td_qlf, 0) }, + { FLDATA (TDTME, td_tme, 0) }, + { ORDATA (TDQL, td_qlctr, 2) }, + { ORDATA (TDCSUM, td_csum, 6), REG_RO }, + { DRDATA (LTIME, td_ltime, 31), REG_NZ | PV_LEFT }, + { DRDATA (DCTIME, td_dctime, 31), REG_NZ | PV_LEFT }, + { URDATA (POS, td_unit[0].pos, 10, T_ADDR_W, 0, + DT_NUMDR, PV_LEFT | REG_RO) }, + { URDATA (STATT, td_unit[0].STATE, 8, 18, 0, + DT_NUMDR, REG_RO) }, + { URDATA (LASTT, td_unit[0].LASTT, 10, 32, 0, + DT_NUMDR, REG_HRO) }, + { FLDATA (STOP_OFFR, td_stopoffr, 0) }, + { ORDATA (DEVNUM, td_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB td_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { UNIT_8FMT + UNIT_11FMT, 0, "18b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_8FMT, "12b", NULL, NULL }, + { UNIT_8FMT + UNIT_11FMT, UNIT_11FMT, "16b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "POSITION", NULL, NULL, &td_show_pos }, + { 0 } + }; + +DEVICE td_dev = { + "TD", td_unit, td_reg, td_mod, + DT_NUMDR, 8, 24, 1, 8, 12, + NULL, NULL, &td_reset, + &td_boot, &td_attach, &td_detach, + &td_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routines */ + +int32 td77 (int32 IR, int32 AC) +{ +int32 pulse = IR & 07; +int32 u = TDC_GETUNIT (td_cmd); /* get unit */ +int32 diff, t; + +switch (pulse) { + + case 01: /* SDSS */ + if (td_slf) return AC | IOT_SKP; + break; + + case 02: /* SDST */ + if (td_tme) return AC | IOT_SKP; + break; + + case 03: /* SDSQ */ + if (td_qlf) return AC | IOT_SKP; + break; + + case 04: /* SDLC */ + td_tme = 0; /* clear tim err */ + diff = (td_cmd ^ AC) & TDC_MASK; /* cmd changes */ + td_cmd = AC & TDC_MASK; /* update cmd */ + if ((diff != 0) && (diff != TDC_RW)) { /* signif change? */ + if (td_newsa (td_cmd)) /* new command */ + return AC | (IORETURN (td_stopoffr, STOP_DTOFF) << IOT_V_REASON); + } + break; + + case 05: /* SDLD */ + td_slf = 0; /* clear flags */ + td_qlf = 0; + td_qlctr = 0; + td_dat = AC; /* load data reg */ + break; + + case 06: /* SDRC */ + td_slf = 0; /* clear flags */ + td_qlf = 0; + td_qlctr = 0; + t = td_cmd | td_mtk; /* form status */ + if (td_tme || !(td_unit[u].flags & UNIT_ATT)) /* tim/sel err? */ + t = t | TDS_TME; + if (td_unit[u].flags & UNIT_WPRT) /* write locked? */ + t = t | TDS_WLO; + return t; /* return status */ + + case 07: /* SDRD */ + td_slf = 0; /* clear flags */ + td_qlf = 0; + td_qlctr = 0; + return td_dat; /* return data */ + } + +return AC; +} + +/* Command register change (start/stop, forward/reverse, new unit) + + 1. If change in motion, stop to start + - schedule up to speed + - set function as next state + 2. If change in motion, start to stop, or change in direction + - schedule stop +*/ + +t_bool td_newsa (int32 newf) +{ +int32 prev_mving, new_mving, prev_dir, new_dir; +UNIT *uptr; + +uptr = td_dev.units + TDC_GETUNIT (newf); /* new unit */ +if ((uptr->flags & UNIT_ATT) == 0) return FALSE; /* new unit attached? */ + +new_mving = ((newf & TDC_STPGO) != 0); /* new moving? */ +prev_mving = (uptr->STATE != STA_STOP); /* previous moving? */ +new_dir = ((newf & TDC_FWDRV) != 0); /* new dir? */ +prev_dir = ((uptr->STATE & STA_DIR) != 0); /* previous dir? */ + +td_mtk = 0; /* mark trk reg cleared */ + +if (!prev_mving && !new_mving) return FALSE; /* stop from stop? */ + +if (new_mving && !prev_mving) { /* start from stop? */ + if (td_setpos (uptr)) return TRUE; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, td_dctime - (td_dctime >> 2)); /* sched accel */ + uptr->STATE = STA_ACC | new_dir; /* set status */ + td_slf = td_qlf = td_qlctr = 0; /* clear state */ + return FALSE; + } + +if ((prev_mving && !new_mving) || /* stop from moving? */ + (prev_dir != new_dir)) { /* dir chg while moving? */ + if (uptr->STATE >= STA_ACC) { /* not stopping? */ + if (td_setpos (uptr)) return TRUE; /* update pos */ + sim_cancel (uptr); /* stop current */ + sim_activate (uptr, td_dctime); /* schedule decel */ + uptr->STATE = STA_DEC | prev_dir; /* set status */ + td_slf = td_qlf = td_qlctr = 0; /* clear state */ + } + return FALSE; + } + +return FALSE; +} + +/* Update DECtape position + + DECtape motion is modeled as a constant velocity, with linear + acceleration and deceleration. The motion equations are as follows: + + t = time since operation started + tmax = time for operation (accel, decel only) + v = at speed velocity in lines (= 1/td_ltime) + + Then: + at speed dist = t * v + accel dist = (t^2 * v) / (2 * tmax) + decel dist = (((2 * t * tmax) - t^2) * v) / (2 * tmax) + + This routine uses the relative (integer) time, rather than the absolute + (floating point) time, to allow save and restore of the start times. +*/ + +t_bool td_setpos (UNIT *uptr) +{ +uint32 new_time, ut, ulin, udelt; +int32 delta; + +new_time = sim_grtime (); /* current time */ +ut = new_time - uptr->LASTT; /* elapsed time */ +if (ut == 0) return FALSE; /* no time gone? exit */ +uptr->LASTT = new_time; /* update last time */ +switch (uptr->STATE & ~STA_DIR) { /* case on motion */ + + case STA_STOP: /* stop */ + delta = 0; + break; + + case STA_DEC: /* slowing */ + ulin = ut / (uint32) td_ltime; + udelt = td_dctime / td_ltime; + delta = ((ulin * udelt * 2) - (ulin * ulin)) / (2 * udelt); + break; + + case STA_ACC: /* accelerating */ + ulin = ut / (uint32) td_ltime; + udelt = (td_dctime - (td_dctime >> 2)) / td_ltime; + delta = (ulin * ulin) / (2 * udelt); + break; + + case STA_UTS: /* at speed */ + delta = ut / (uint32) td_ltime; + break; + } + +if (uptr->STATE & STA_DIR) uptr->pos = uptr->pos - delta; /* update pos */ +else uptr->pos = uptr->pos + delta; +if (((int32) uptr->pos < 0) || + ((int32) uptr->pos > (DTU_FWDEZ (uptr) + DT_EZLIN))) { + detach_unit (uptr); /* off reel */ + sim_cancel (uptr); /* no timing pulses */ + return TRUE; + } +return FALSE; +} + +/* Unit service - unit is either changing speed, or it is up to speed */ + +t_stat td_svc (UNIT *uptr) +{ +int32 mot = uptr->STATE & ~STA_DIR; +int32 dir = uptr->STATE & STA_DIR; +int32 unum = uptr - td_dev.units; +int32 su = TDC_GETUNIT (td_cmd); +int32 mtkb, datb; + +/* Motion cases + + Decelerating - if go, next state must be accel as specified by td_cmd + Accelerating - next state must be up to speed, fall through + Up to speed - process line */ + +if (mot == STA_STOP) return SCPE_OK; /* stopped? done */ +if ((uptr->flags & UNIT_ATT) == 0) { /* not attached? */ + uptr->STATE = uptr->pos = 0; /* also done */ + return SCPE_UNATT; + } + +switch (mot) { /* case on motion */ + + case STA_DEC: /* deceleration */ + if (td_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (td_stopoffr, STOP_DTOFF); + if ((unum != su) || !(td_cmd & TDC_STPGO)) /* not sel or stop? */ + uptr->STATE = 0; /* stop */ + else { /* selected and go */ + uptr->STATE = STA_ACC | /* accelerating */ + ((td_cmd & TDC_FWDRV)? STA_DIR: 0); /* in new dir */ + sim_activate (uptr, td_dctime - (td_dctime >> 2)); + } + return SCPE_OK; + + case STA_ACC: /* accelerating */ + if (td_setpos (uptr)) /* upd pos; off reel? */ + return IORETURN (td_stopoffr, STOP_DTOFF); + uptr->STATE = STA_UTS | dir; /* set up to speed */ + break; + + case STA_UTS: /* up to speed */ + if (dir) uptr->pos = uptr->pos - 1; /* adjust position */ + else uptr->pos = uptr->pos + 1; + uptr->LASTT = sim_grtime (); /* save time */ + if (((int32) uptr->pos < 0) || /* off reel? */ + (uptr->pos >= (((uint32) DTU_FWDEZ (uptr)) + DT_EZLIN))) { + detach_unit (uptr); + return IORETURN (td_stopoffr, STOP_DTOFF); + } + break; /* check function */ + } + +/* At speed - process the current line + + Once the TD8E is running at speed, it operates line by line. If reading, + the current mark track bit is shifted into the mark track register, and + the current data nibble (3b) is shifted into the data register. If + writing, the current mark track bit is shifted into the mark track + register, the top nibble from the data register is written to tape, and + the data register is shifted up. The complexity here comes from + synthesizing the mark track, based on tape position, and the header data. */ + +sim_activate (uptr, td_ltime); /* sched next line */ +if (unum != su) return SCPE_OK; /* not sel? done */ +td_slf = 1; /* set single */ +td_qlctr = (td_qlctr + 1) % DT_WSIZE; /* count words */ +if (td_qlctr == 0) { /* lines mod 4? */ + if (td_qlf) { /* quad line set? */ + td_tme = 1; /* timing error */ + td_cmd = td_cmd & ~TDC_RW; /* clear write */ + } + else td_qlf = 1; /* no, set quad */ + } + +datb = 0; /* assume no data */ +if (uptr->pos < (DT_EZLIN - DT_BFLIN)) /* rev end zone? */ + mtkb = MTK_BIT (MTK_REV_END, uptr->pos); +else if (uptr->pos < DT_EZLIN) /* rev buffer? */ + mtkb = MTK_BIT (MTK_INTER, uptr->pos); +else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */ + int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */ + int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */ + if (lineno < DT_HTLIN) { /* header? */ + if ((td_cmd & TDC_RW) == 0) /* read? */ + datb = td_header (uptr, blkno, lineno); /* get nibble */ + } + else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) { /* data? */ + if (td_cmd & TDC_RW) /* write? */ + td_write (uptr, blkno, /* write data nibble */ + lineno - DT_HTLIN, /* data rel line num */ + (td_dat >> 9) & 07); + else datb = td_read (uptr, blkno, /* no, read */ + lineno - DT_HTLIN); + } + else if ((td_cmd & TDC_RW) == 0) /* trailer; read? */ + datb = td_trailer (uptr, blkno, lineno - /* get trlr nibble */ + (DTU_LPERB (uptr) - DT_HTLIN)); + mtkb = tdb_mtk[unum][lineno]; + } +else if (uptr->pos < (((uint32) DTU_FWDEZ (uptr)) + DT_BFLIN)) + mtkb = MTK_BIT (MTK_INTER, uptr->pos); /* fwd buffer? */ +else mtkb = MTK_BIT (MTK_FWD_END, uptr->pos); /* fwd end zone */ + +if (dir) { /* reverse? */ + mtkb = mtkb ^ 01; /* complement mark bit, */ + datb = datb ^ 07; /* data bits */ + } +td_mtk = ((td_mtk << 1) | mtkb) & MTK_MASK; /* shift mark reg */ +td_dat = ((td_dat << 3) | datb) & 07777; /* shift data reg */ +return SCPE_OK; +} + +/* Header read - reads out 18b words in 3b increments + + word lines contents + 0 0-5 0 + 1 6-11 block number + 2 12-17 0 + 3 18-23 0 + 4 24-29 reverse checksum (0777777) +*/ + +int32 td_header (UNIT *uptr, int32 blk, int32 line) +{ +int32 nibp; + +switch (line) { + + case 8: case 9: case 10: case 11: /* block num */ + nibp = 3 * (DT_LPERMC - 1 - (line % DT_LPERMC)); + return (blk >> nibp) & 07; + + case 24: case 25: case 26: case 27: case 28: case 29: /* rev csum */ + return 07; /* 777777 */ + + default: + return 0; + } +} + +/* Trailer read - reads out 18b words in 3b increments + Checksum is stored to avoid double calculation + + word lines contents + 0 0-5 forward checksum (lines 0-1, rest 0) + 1 6-11 0 + 2 12-17 0 + 3 18-23 reverse block mark + 4 24-29 0 + + Note that the reverse block mark (when read forward) appears + as the complement obverse (3b nibbles swapped end for end and + complemented). +*/ + +int32 td_trailer (UNIT *uptr, int32 blk, int32 line) +{ +int32 nibp, i, ba; +int16 *fbuf= (int16 *) uptr->filebuf; + +switch (line) { + + case 0: + td_csum = 07777; /* init csum */ + ba = blk * DTU_BSIZE (uptr); + for (i = 0; i < DTU_BSIZE (uptr); i++) /* loop thru buf */ + td_csum = (td_csum ^ ~fbuf[ba + i]) & 07777; + td_csum = ((td_csum >> 6) ^ td_csum) & 077; + return (td_csum >> 3) & 07; + + case 1: + return (td_csum & 07); + + case 18: case 19: case 20: case 21: + nibp = 3 * (line % DT_LPERMC); + return ((blk >> nibp) & 07) ^ 07; + + default: + return 0; + } +} + +/* Data read - convert block number/data line # to offset in data array */ + +int32 td_read (UNIT *uptr, int32 blk, int32 line) +{ +int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */ +uint32 ba = blk * DTU_BSIZE (uptr); /* block base */ +int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */ + +ba = ba + (line / DT_WSIZE); /* block addr */ +return (fbuf[ba] >> nibp) & 07; /* get data nibble */ +} + +/* Data write - convert block number/data line # to offset in data array */ + +void td_write (UNIT *uptr, int32 blk, int32 line, int32 dat) +{ +int16 *fbuf = (int16 *) uptr->filebuf; /* buffer */ +uint32 ba = blk * DTU_BSIZE (uptr); /* block base */ +int32 nibp = 3 * (DT_WSIZE - 1 - (line % DT_WSIZE)); /* nibble pos */ + +ba = ba + (line / DT_WSIZE); /* block addr */ +fbuf[ba] = (fbuf[ba] & ~(07 << nibp)) | (dat << nibp); /* upd data nibble */ +if (ba >= uptr->hwmark) uptr->hwmark = ba + 1; /* upd length */ +return; +} + +/* Reset routine */ + +t_stat td_reset (DEVICE *dptr) +{ +int32 i; +UNIT *uptr; + +for (i = 0; i < DT_NUMDR; i++) { /* stop all activity */ + uptr = td_dev.units + i; + if (sim_is_running) { /* CAF? */ + if (uptr->STATE >= STA_ACC) { /* accel or uts? */ + if (td_setpos (uptr)) continue; /* update pos */ + sim_cancel (uptr); + sim_activate (uptr, td_dctime); /* sched decel */ + uptr->STATE = STA_DEC | (uptr->STATE & STA_DIR); + } + } + else { + sim_cancel (uptr); /* sim reset */ + uptr->STATE = 0; + uptr->LASTT = sim_grtime (); + } + } +td_slf = td_qlf = td_qlctr = 0; /* clear state */ +td_cmd = td_dat = td_mtk = 0; +td_csum = 0; +return SCPE_OK; +} + +/* Bootstrap routine - OS/8 only + + 1) Read reverse until reverse end zone (mark track is complement obverse) + 2) Read forward until mark track code 031. This is a composite code from + the last 4b of the forward block number and the first two bits of the + reverse guard (01 -0110 01- 1010). There are 16 lines before the first + data word. + 3) Store data words from 7354 to end of page. This includes header and + trailer words. + 4) Continue at location 7400. +*/ + +#define BOOT_START 07300 +#define BOOT_LEN (sizeof (boot_rom) / sizeof (int16)) + +static const uint16 boot_rom[] = { + 01312, /* ST, TAD L4MT ;=2000, reverse */ + 04312, /* JMS L4MT ; rev lk for 022 */ + 04312, /* JMS L4MT ; fwd lk for 031 */ + 06773, /* DAT, SDSQ ; wait for 12b */ + 05303, /* JMP .-1 */ + 06777, /* SDRD ; read word */ + 03726, /* DCA I BUF ; store */ + 02326, /* ISZ BUF ; incr ptr */ + 05303, /* JMP DAT ; if not 0, cont */ + 05732, /* JMP I SCB ; jump to boot */ + 02000, /* L4MT,2000 ; overwritten */ + 01300, /* TAD ST ; =1312, go */ + 06774, /* SDLC ; new command */ + 06771, /* MTK, SDSS ; wait for mark */ + 05315, /* JMP .-1 */ + 06776, /* SDRC ; get mark code */ + 00331, /* AND K77 ; mask to 6b */ + 01327, /* CMP, TAD MCD ; got target code? */ + 07640, /* SZA CLA ; skip if yes */ + 05315, /* JMP MTK ; wait for mark */ + 02321, /* ISZ CMP ; next target */ + 05712, /* JMP I L4MT ; exit */ + 07354, /* BUF, 7354 ; loading point */ + 07756, /* MCD, -22 ; target 1 */ + 07747, /* -31 ; target 2 */ + 00077, /* 77 ; mask */ + 07400 /* SCB, 7400 ; secondary boot */ + }; + +t_stat td_boot (int32 unitno, DEVICE *dptr) +{ +int32 i; +extern int32 saved_PC; + +if (unitno) return SCPE_ARG; /* only unit 0 */ +if (td_dib.dev != DEV_TD8E) return STOP_NOTSTD; /* only std devno */ +td_unit[unitno].pos = DT_EZLIN; +for (i = 0; i < BOOT_LEN; i++) M[BOOT_START + i] = boot_rom[i]; +saved_PC = BOOT_START; +return SCPE_OK; +} + +/* Attach routine + + Determine 12b, 16b, or 18b/36b format + Allocate buffer + If 16b or 18b, read 16b or 18b format and convert to 12b in buffer + If 12b, read data into buffer + Set up mark track bit array +*/ + +t_stat td_attach (UNIT *uptr, char *cptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k, mtkpb; +int32 u = uptr - td_dev.units; +t_stat r; +uint32 ba, sz; + +r = attach_unit (uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* fail? */ +if ((sim_switches & SIM_SW_REST) == 0) { /* not from rest? */ + uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; + if (sim_switches & SWMASK ('F')) /* att 18b? */ + uptr->flags = uptr->flags & ~UNIT_8FMT; + else if (sim_switches & SWMASK ('S')) /* att 16b? */ + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (!(sim_switches & SWMASK ('A')) && /* autosize? */ + (sz = sim_fsize (uptr->fileref))) { + if (sz == D11_FILSIZ) + uptr->flags = (uptr->flags | UNIT_11FMT) & ~UNIT_8FMT; + else if (sz > D8_FILSIZ) + uptr->flags = uptr->flags & ~UNIT_8FMT; + } + } +uptr->capac = DTU_CAPAC (uptr); /* set capacity */ +uptr->filebuf = calloc (uptr->capac, sizeof (int16)); +if (uptr->filebuf == NULL) { /* can't alloc? */ + detach_unit (uptr); + return SCPE_MEM; + } +fbuf = (uint16 *) uptr->filebuf; /* file buffer */ +printf ("%s%d: ", sim_dname (&td_dev), u); +if (uptr->flags & UNIT_8FMT) printf ("12b format"); +else if (uptr->flags & UNIT_11FMT) printf ("16b format"); +else printf ("18b/36b format"); +printf (", buffering file in memory\n"); +if (uptr->flags & UNIT_8FMT) /* 12b? */ + uptr->hwmark = fxread (uptr->filebuf, sizeof (uint16), + uptr->capac, uptr->fileref); +else { /* 16b/18b */ + for (ba = 0; ba < uptr->capac; ) { /* loop thru file */ + if (uptr->flags & UNIT_11FMT) { + k = fxread (pdp11b, sizeof (uint16), D18_NBSIZE, uptr->fileref); + for (i = 0; i < k; i++) pdp18b[i] = pdp11b[i]; + } + else k = fxread (pdp18b, sizeof (uint32), D18_NBSIZE, uptr->fileref); + if (k == 0) break; + for ( ; k < D18_NBSIZE; k++) pdp18b[k] = 0; + for (k = 0; k < D18_NBSIZE; k = k + 2) { /* loop thru blk */ + fbuf[ba] = (pdp18b[k] >> 6) & 07777; + fbuf[ba + 1] = ((pdp18b[k] & 077) << 6) | + ((pdp18b[k + 1] >> 12) & 077); + fbuf[ba + 2] = pdp18b[k + 1] & 07777; + ba = ba + 3; + } /* end blk loop */ + } /* end file loop */ + uptr->hwmark = ba; + } /* end else */ +uptr->flags = uptr->flags | UNIT_BUF; /* set buf flag */ +uptr->pos = DT_EZLIN; /* beyond leader */ +uptr->LASTT = sim_grtime (); /* last pos update */ +uptr->STATE = STA_STOP; /* stopped */ + +mtkpb = (DTU_BSIZE (uptr) * DT_WSIZE) / DT_LPERMC; /* mtk codes per blk */ +k = td_set_mtk (MTK_INTER, u, 0); /* fill mark track */ +k = td_set_mtk (MTK_FWD_BLK, u, k); /* bit array */ +k = td_set_mtk (MTK_REV_GRD, u, k); +for (i = 0; i < 4; i++) k = td_set_mtk (MTK_FWD_PRE, u, k); +for (i = 0; i < (mtkpb - 4); i++) k = td_set_mtk (MTK_DATA, u, k); +for (i = 0; i < 4; i++) k = td_set_mtk (MTK_REV_PRE, u, k); +k = td_set_mtk (MTK_FWD_GRD, u, k); +k = td_set_mtk (MTK_REV_BLK, u, k); +k = td_set_mtk (MTK_INTER, u, k); +return SCPE_OK; +} + +/* Detach routine + + If 12b, write buffer to file + If 16b or 18b, convert 12b buffer to 16b or 18b and write to file + Deallocate buffer +*/ + +t_stat td_detach (UNIT* uptr) +{ +uint32 pdp18b[D18_NBSIZE]; +uint16 pdp11b[D18_NBSIZE], *fbuf; +int32 i, k; +int32 u = uptr - td_dev.units; +uint32 ba; + +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; +fbuf = (uint16 *) uptr->filebuf; /* file buffer */ +if (uptr->hwmark && ((uptr->flags & UNIT_RO)== 0)) { /* any data? */ + printf ("%s%d: writing buffer to file\n", sim_dname (&td_dev), u); + rewind (uptr->fileref); /* start of file */ + if (uptr->flags & UNIT_8FMT) /* PDP8? */ + fxwrite (uptr->filebuf, sizeof (uint16), /* write file */ + uptr->hwmark, uptr->fileref); + else { /* 16b/18b */ + for (ba = 0; ba < uptr->hwmark; ) { /* loop thru buf */ + for (k = 0; k < D18_NBSIZE; k = k + 2) { + pdp18b[k] = ((uint32) (fbuf[ba] & 07777) << 6) | + ((uint32) (fbuf[ba + 1] >> 6) & 077); + pdp18b[k + 1] = ((uint32) (fbuf[ba + 1] & 077) << 12) | + ((uint32) (fbuf[ba + 2] & 07777)); + ba = ba + 3; + } /* end loop blk */ + if (uptr->flags & UNIT_11FMT) { /* 16b? */ + for (i = 0; i < D18_NBSIZE; i++) pdp11b[i] = pdp18b[i]; + fxwrite (pdp11b, sizeof (uint16), + D18_NBSIZE, uptr->fileref); + } + else fxwrite (pdp18b, sizeof (uint32), + D18_NBSIZE, uptr->fileref); + } /* end loop buf */ + } /* end else */ + if (ferror (uptr->fileref)) perror ("I/O error"); + } /* end if hwmark */ +free (uptr->filebuf); /* release buf */ +uptr->flags = uptr->flags & ~UNIT_BUF; /* clear buf flag */ +uptr->filebuf = NULL; /* clear buf ptr */ +uptr->flags = (uptr->flags | UNIT_8FMT) & ~UNIT_11FMT; /* default fmt */ +uptr->capac = DT_CAPAC; /* default size */ +uptr->pos = uptr->STATE = 0; +sim_cancel (uptr); /* no more pulses */ +return detach_unit (uptr); +} + +/* Set mark track code into bit array */ + +int32 td_set_mtk (int32 code, int32 u, int32 k) +{ +int32 i; + +for (i = 5; i >= 0; i--) tdb_mtk[u][k++] = (code >> i) & 1; +return k; +} + +/* Show position */ + +t_stat td_show_pos (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; +if (uptr->pos < DT_EZLIN) /* rev end zone? */ + fprintf (st, "Reverse end zone\n"); +else if (uptr->pos < ((uint32) DTU_FWDEZ (uptr))) { /* data zone? */ + int32 blkno = DT_LIN2BL (uptr->pos, uptr); /* block # */ + int32 lineno = DT_LIN2OF (uptr->pos, uptr); /* line # within block */ + fprintf (st, "Block %d, line %d, ", blkno, lineno); + if (lineno < DT_HTLIN) /* header? */ + fprintf (st, "header cell %d, nibble %d\n", + lineno / DT_LPERMC, lineno % DT_LPERMC); + else if (lineno < (DTU_LPERB (uptr) - DT_HTLIN)) /* data? */ + fprintf (st, "data word %d, nibble %d\n", + (lineno - DT_HTLIN) / DT_WSIZE, (lineno - DT_HTLIN) % DT_WSIZE); + else fprintf (st, "trailer cell %d, nibble %d\n", + (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) / DT_LPERMC, + (lineno - (DTU_LPERB (uptr) - DT_HTLIN)) % DT_LPERMC); + } +else fprintf (st, "Forward end zone\n"); /* fwd end zone */ +return SCPE_OK; +} + diff --git a/PDP8/pdp8_tsc.c b/PDP8/pdp8_tsc.c new file mode 100644 index 0000000..15be184 --- /dev/null +++ b/PDP8/pdp8_tsc.c @@ -0,0 +1,158 @@ +/* pdp8_tsc.c: PDP-8 ETOS timesharing option board (TSC8-75) + + Copyright (c) 2003-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module is based on Bernhard Baehr's PDP-8/E simulator + + PDP-8/E Simulator Source Code + + Copyright ) 2001-2003 Bernhard Baehr + + TSC8iots.c - IOTs for the TSC8-75 Board plugin + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + tsc TSC8-75 option board +*/ + +#include "pdp8_defs.h" + +extern int32 int_req; +extern int32 SF; +extern int32 tsc_ir; /* "ERIOT" */ +extern int32 tsc_pc; /* "ERTB" */ +extern int32 tsc_cdf; /* "ECDF" */ +extern int32 tsc_enb; /* enable */ + +#define UNIT_V_SN699 (UNIT_V_UF + 0) /* SN 699 or above */ +#define UNIT_SN699 (1 << UNIT_V_SN699) + +DEVICE tsc_dev; +int32 tsc (int32 IR, int32 AC); +t_stat tsc_reset (DEVICE *dptr); + +/* TSC data structures + + tsc_dev TSC device descriptor + tsc_unit TSC unit descriptor + tsc_reg TSC register list +*/ + +DIB tsc_dib = { DEV_TSC, 1, { &tsc } }; + +UNIT tsc_unit = { UDATA (NULL, UNIT_SN699, 0) }; + +REG tsc_reg[] = { + { ORDATA (IR, tsc_ir, 12) }, + { ORDATA (PC, tsc_pc, 12) }, + { FLDATA (CDF, tsc_cdf, 0) }, + { FLDATA (ENB, tsc_enb, 0) }, + { FLDATA (INT, int_req, INT_V_TSC) }, + { NULL } + }; + +MTAB tsc_mod[] = { + { UNIT_SN699, UNIT_SN699, "ESME", "ESME", NULL }, + { UNIT_SN699, 0, "no ESME", "NOESME", NULL }, + { 0 } + }; + +DEVICE tsc_dev = { + "TSC", &tsc_unit, tsc_reg, tsc_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tsc_reset, + NULL, NULL, NULL, + &tsc_dib, DEV_DISABLE | DEV_DIS + }; + +/* IOT routine */ + +int32 tsc (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* ETDS */ + tsc_enb = 0; /* disable int req */ + int_req = int_req & ~INT_TSC; /* clear flag */ + break; + + case 1: /* ESKP */ + return (int_req & INT_TSC)? IOT_SKP + AC: AC; /* skip on int req */ + + case 2: /* ECTF */ + int_req = int_req & ~INT_TSC; /* clear int req */ + break; + + case 3: /* ECDF */ + AC = AC | ((tsc_ir >> 3) & 07); /* read "ERIOT"<6:8> */ + if (tsc_cdf) AC = AC | IOT_SKP; /* if cdf, skip */ + tsc_cdf = 0; + break; + + case 4: /* ERTB */ + return tsc_pc; + + case 5: /* ESME */ + if (tsc_unit.flags & UNIT_SN699) { /* enabled? */ + if (tsc_cdf && ((tsc_ir & 070) >> 3) == (SF & 07)) { + AC = AC | IOT_SKP; + tsc_cdf = 0; + } + } + break; + + case 6: /* ERIOT */ + return tsc_ir; + + case 7: /* ETEN */ + tsc_enb = 1; + break; + } /* end switch */ + +return AC; +} + +/* Reset routine */ + +t_stat tsc_reset (DEVICE *dptr) +{ +tsc_ir = 0; +tsc_pc = 0; +tsc_cdf = 0; +tsc_enb = 0; +int_req = int_req & ~INT_TSC; +return SCPE_OK; +} diff --git a/PDP8/pdp8_tt.c b/PDP8/pdp8_tt.c new file mode 100644 index 0000000..bd683ce --- /dev/null +++ b/PDP8/pdp8_tt.c @@ -0,0 +1,271 @@ +/* pdp8_tt.c: PDP-8 console terminal simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti,tto KL8E terminal input/output + + 18-Jun-07 RMS Added UNIT_IDLE flag to console input + 18-Oct-06 RMS Synced keyboard to clock + 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode + 22-Nov-05 RMS Revised for new terminal processing routines + 28-May-04 RMS Removed SET TTI CTRL-C + 29-Dec-03 RMS Added console output backpressure support + 25-Apr-03 RMS Revised for extended file support + 02-Mar-02 RMS Added SET TTI CTRL-C + 22-Dec-02 RMS Added break support + 01-Nov-02 RMS Added 7B/8B support + 04-Oct-02 RMS Added DIBs, device number support + 30-May-02 RMS Widened POS to 32b + 07-Sep-01 RMS Moved function prototypes +*/ + +#include "pdp8_defs.h" +#include + +extern int32 int_req, int_enable, dev_done, stop_inst; +extern int32 tmxr_poll; + +int32 tti (int32 IR, int32 AC); +int32 tto (int32 IR, int32 AC); +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list + tti_mod TTI modifiers list +*/ + +DIB tti_dib = { DEV_TTI, 1, { &tti } }; + +UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_KSR, 0), 0 }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_TTI) }, + { FLDATA (ENABLE, int_enable, INT_V_TTI) }, + { FLDATA (INT, int_req, INT_V_TTI) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7b", NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev, NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, 0 + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +DIB tto_dib = { DEV_TTO, 1, { &tto } }; + +UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 8) }, + { FLDATA (DONE, dev_done, INT_V_TTO) }, + { FLDATA (ENABLE, int_enable, INT_V_TTO) }, + { FLDATA (INT, int_req, INT_V_TTO) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tty_set_mode }, + { TT_MODE, TT_MODE_7B, "7b", "7B", &tty_set_mode }, + { TT_MODE, TT_MODE_8B, "8b", "8B", &tty_set_mode }, + { TT_MODE, TT_MODE_7P, "7p", "7P", &tty_set_mode }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, 0 + }; + +/* Terminal input: IOT routine */ + +int32 tti (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + case 0: /* KCF */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return AC; + + case 1: /* KSF */ + return (dev_done & INT_TTI)? IOT_SKP + AC: AC; + + case 2: /* KCC */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return 0; /* clear AC */ + + case 4: /* KRS */ + return (AC | tti_unit.buf); /* return buffer */ + + case 5: /* KIE */ + if (AC & 1) int_enable = int_enable | (INT_TTI+INT_TTO); + else int_enable = int_enable & ~(INT_TTI+INT_TTO); + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 6: /* KRB */ + dev_done = dev_done & ~INT_TTI; /* clear flag */ + int_req = int_req & ~INT_TTI; + return (tti_unit.buf); /* return buffer */ + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c; + +sim_activate (uptr, KBD_WAIT (uptr->wait, tmxr_poll)); /* continue poll */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +if (c & SCPE_BREAK) uptr->buf = 0; /* break? */ +else uptr->buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags) | TTUF_KSR); +uptr->pos = uptr->pos + 1; +dev_done = dev_done | INT_TTI; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; +dev_done = dev_done & ~INT_TTI; /* clear done, int */ +int_req = int_req & ~INT_TTI; +int_enable = int_enable | INT_TTI; /* set enable */ +sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmxr_poll)); +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 tto (int32 IR, int32 AC) +{ +switch (IR & 07) { /* decode IR<9:11> */ + + case 0: /* TLF */ + dev_done = dev_done | INT_TTO; /* set flag */ + int_req = INT_UPDATE; /* update interrupts */ + return AC; + + case 1: /* TSF */ + return (dev_done & INT_TTO)? IOT_SKP + AC: AC; + + case 2: /* TCF */ + dev_done = dev_done & ~INT_TTO; /* clear flag */ + int_req = int_req & ~INT_TTO; /* clear int req */ + return AC; + + case 5: /* SPI */ + return (int_req & (INT_TTI+INT_TTO))? IOT_SKP + AC: AC; + + case 6: /* TLS */ + dev_done = dev_done & ~INT_TTO; /* clear flag */ + int_req = int_req & ~INT_TTO; /* clear int req */ + case 4: /* TPC */ + sim_activate (&tto_unit, tto_unit.wait); /* activate unit */ + tto_unit.buf = AC; /* load buffer */ + return AC; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +c = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR); +if (c >= 0) { + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output char; error? */ + sim_activate (uptr, uptr->wait); /* try again */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* if !stall, report */ + } + } +dev_done = dev_done | INT_TTO; /* set done */ +int_req = INT_UPDATE; /* update interrupts */ +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +dev_done = dev_done & ~INT_TTO; /* clear done, int */ +int_req = int_req & ~INT_TTO; +int_enable = int_enable | INT_TTO; /* set enable */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +t_stat tty_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +tti_unit.flags = (tti_unit.flags & ~TT_MODE) | val; +tto_unit.flags = (tto_unit.flags & ~TT_MODE) | val; +return SCPE_OK; +} diff --git a/PDP8/pdp8_ttx.c b/PDP8/pdp8_ttx.c new file mode 100644 index 0000000..42ba5a2 --- /dev/null +++ b/PDP8/pdp8_ttx.c @@ -0,0 +1,424 @@ +/* pdp8_ttx.c: PDP-8 additional terminals simulator + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ttix,ttox PT08/KL8JA terminal input/output + + 07-Jun-06 RMS Added UNIT_IDLE flag + 06-Jul-06 RMS Fixed bug in DETACH routine + 22-Nov-05 RMS Revised for new terminal processing routines + 29-Jun-05 RMS Added SET TTOXn DISCONNECT + Fixed bug in SET LOG/NOLOG + 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS + 05-Jan-04 RMS Revised for tmxr library changes + 09-May-03 RMS Added network device flag + 25-Apr-03 RMS Revised for extended file support + 22-Dec-02 RMS Added break support + 02-Nov-02 RMS Added 7B/8B support + 04-Oct-02 RMS Added DIB, device number support + 22-Aug-02 RMS Updated for changes to sim_tmxr.c + 06-Jan-02 RMS Added device enable/disable support + 30-Dec-01 RMS Complete rebuild + 30-Nov-01 RMS Added extended SET/SHOW support + + This module implements four individual serial interfaces similar in function + to the console. These interfaces are mapped to Telnet based connections as + though they were the four lines of a terminal multiplexor. The connection + polling mechanism is superimposed onto the keyboard of the first interface. +*/ + +#include "pdp8_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define TTX_LINES 4 +#define TTX_MASK (TTX_LINES - 1) + +#define TTX_GETLN(x) (((x) >> 4) & TTX_MASK) + +extern int32 int_req, int_enable, dev_done, stop_inst; + +uint8 ttix_buf[TTX_LINES] = { 0 }; /* input buffers */ +uint8 ttox_buf[TTX_LINES] = { 0 }; /* output buffers */ +int32 ttx_tps = 100; /* polls per second */ +TMLN ttx_ldsc[TTX_LINES] = { 0 }; /* line descriptors */ +TMXR ttx_desc = { TTX_LINES, 0, 0, ttx_ldsc }; /* mux descriptor */ + +DEVICE ttix_dev, ttox_dev; +int32 ttix (int32 IR, int32 AC); +int32 ttox (int32 IR, int32 AC); +t_stat ttix_svc (UNIT *uptr); +t_stat ttix_reset (DEVICE *dptr); +t_stat ttox_svc (UNIT *uptr); +t_stat ttox_reset (DEVICE *dptr); +t_stat ttx_attach (UNIT *uptr, char *cptr); +t_stat ttx_detach (UNIT *uptr); +t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc); +void ttx_enbdis (int32 dis); + +/* TTIx data structures + + ttix_dev TTIx device descriptor + ttix_unit TTIx unit descriptor + ttix_reg TTIx register list + ttix_mod TTIx modifiers list +*/ + +DIB ttix_dib = { DEV_KJ8, 8, + { &ttix, &ttox, &ttix, &ttox, &ttix, &ttox, &ttix, &ttox } }; + +UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_IDLE|UNIT_ATTABLE, 0), KBD_POLL_WAIT }; + +REG ttix_reg[] = { + { BRDATA (BUF, ttix_buf, 8, 8, TTX_LINES) }, + { GRDATA (DONE, dev_done, 8, TTX_LINES, INT_V_TTI1) }, + { GRDATA (ENABLE, int_enable, 8, TTX_LINES, INT_V_TTI1) }, + { GRDATA (INT, int_req, 8, TTX_LINES, INT_V_TTI1) }, + { DRDATA (TIME, ttix_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, ttx_tps, 10), REG_NZ + PV_LEFT }, + { ORDATA (DEVNUM, ttix_dib.dev, 6), REG_HRO }, + { NULL } + }; + +MTAB ttix_mod[] = { + { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &ttx_summ }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &ttx_desc }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &ttx_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &ttx_show, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", + &set_dev, &show_dev, NULL }, + { 0 } + }; + +DEVICE ttix_dev = { + "TTIX", &ttix_unit, ttix_reg, ttix_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &ttix_reset, + NULL, &ttx_attach, &ttx_detach, + &ttix_dib, DEV_NET | DEV_DISABLE + }; + +/* TTOx data structures + + ttox_dev TTOx device descriptor + ttox_unit TTOx unit descriptor + ttox_reg TTOx register list +*/ + +UNIT ttox_unit[] = { + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, + { UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT } + }; + +REG ttox_reg[] = { + { BRDATA (BUF, ttox_buf, 8, 8, TTX_LINES) }, + { GRDATA (DONE, dev_done, 8, TTX_LINES, INT_V_TTO1) }, + { GRDATA (ENABLE, int_enable, 8, TTX_LINES, INT_V_TTO1) }, + { GRDATA (INT, int_req, 8, TTX_LINES, INT_V_TTO1) }, + { URDATA (TIME, ttox_unit[0].wait, 10, 24, 0, + TTX_LINES, PV_LEFT) }, + { NULL } + }; + +MTAB ttox_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &ttx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &ttx_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &ttx_desc }, + { 0 } + }; + +DEVICE ttox_dev = { + "TTOX", ttox_unit, ttox_reg, ttox_mod, + 4, 10, 31, 1, 8, 8, + NULL, NULL, &ttox_reset, + NULL, NULL, NULL, + NULL, DEV_DISABLE + }; + +/* Terminal input: IOT routine */ + +int32 ttix (int32 inst, int32 AC) +{ +int32 pulse = inst & 07; /* IOT pulse */ +int32 ln = TTX_GETLN (inst); /* line # */ +int32 itti = (INT_TTI1 << ln); /* rx intr */ +int32 itto = (INT_TTO1 << ln); /* tx intr */ + +switch (pulse) { /* case IR<9:11> */ + + case 0: /* KCF */ + dev_done = dev_done & ~itti; /* clear flag */ + int_req = int_req & ~itti; + break; + + case 1: /* KSF */ + return (dev_done & itti)? IOT_SKP + AC: AC; + + case 2: /* KCC */ + dev_done = dev_done & ~itti; /* clear flag */ + int_req = int_req & ~itti; + return 0; /* clear AC */ + + case 4: /* KRS */ + return (AC | ttix_buf[ln]); /* return buf */ + + case 5: /* KIE */ + if (AC & 1) int_enable = int_enable | (itti + itto); + else int_enable = int_enable & ~(itti + itto); + int_req = INT_UPDATE; /* update intr */ + break; + + case 6: /* KRB */ + dev_done = dev_done & ~itti; /* clear flag */ + int_req = int_req & ~itti; + return ttix_buf[ln]; /* return buf */ + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ + +return AC; +} + +/* Unit service */ + +t_stat ttix_svc (UNIT *uptr) +{ +int32 ln, c, temp; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +temp = sim_rtcn_calb (ttx_tps, TMR_TTX); /* calibrate */ +sim_activate (uptr, temp); /* continue poll */ +ln = tmxr_poll_conn (&ttx_desc); /* look for connect */ +if (ln >= 0) ttx_ldsc[ln].rcve = 1; /* got one? rcv enb*/ +tmxr_poll_rx (&ttx_desc); /* poll for input */ +for (ln = 0; ln < TTX_LINES; ln++) { /* loop thru lines */ + if (ttx_ldsc[ln].conn) { /* connected? */ + if (temp = tmxr_getc_ln (&ttx_ldsc[ln])) { /* get char */ + if (temp & SCPE_BREAK) c = 0; /* break? */ + else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags)); + ttix_buf[ln] = c; + dev_done = dev_done | (INT_TTI1 << ln); + int_req = INT_UPDATE; + } + } + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ttix_reset (DEVICE *dptr) +{ +int32 t, ln, itto; + +ttx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ +if (ttix_unit.flags & UNIT_ATT) { /* if attached, */ + if (!sim_is_active (&ttix_unit)) { + t = sim_rtcn_init (ttix_unit.wait, TMR_TTX); + sim_activate (&ttix_unit, t); /* activate */ + } + } +else sim_cancel (&ttix_unit); /* else stop */ +for (ln = 0; ln < TTX_LINES; ln++) { /* for all lines */ + ttix_buf[ln] = 0; /* clear buf, */ + itto = (INT_TTI1 << ln); /* interrupt */ + dev_done = dev_done & ~itto; /* clr done, int */ + int_req = int_req & ~itto; + int_enable = int_enable | itto; /* set enable */ + } +return SCPE_OK; +} + +/* Terminal output: IOT routine */ + +int32 ttox (int32 inst, int32 AC) +{ +int32 pulse = inst & 07; /* pulse */ +int32 ln = TTX_GETLN (inst); /* line # */ +int32 itti = (INT_TTI1 << ln); /* rx intr */ +int32 itto = (INT_TTO1 << ln); /* tx intr */ + +switch (pulse) { /* case IR<9:11> */ + + case 0: /* TLF */ + dev_done = dev_done | itto; /* set flag */ + int_req = INT_UPDATE; /* update intr */ + break; + + case 1: /* TSF */ + return (dev_done & itto)? IOT_SKP + AC: AC; + + case 2: /* TCF */ + dev_done = dev_done & ~itto; /* clear flag */ + int_req = int_req & ~itto; /* clear intr */ + break; + + case 5: /* SPI */ + return (int_req & (itti | itto))? IOT_SKP + AC: AC; + + case 6: /* TLS */ + dev_done = dev_done & ~itto; /* clear flag */ + int_req = int_req & ~itto; /* clear int req */ + case 4: /* TPC */ + sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate */ + ttox_buf[ln] = AC & 0377; /* load buffer */ + break; + + default: + return (stop_inst << IOT_V_REASON) + AC; + } /* end switch */ + +return AC; +} + +/* Unit service */ + +t_stat ttox_svc (UNIT *uptr) +{ +int32 c, ln = uptr - ttox_unit; /* line # */ + +if (ttx_ldsc[ln].conn) { /* connected? */ + if (ttx_ldsc[ln].xmte) { /* tx enabled? */ + TMLN *lp = &ttx_ldsc[ln]; /* get line */ + c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags)); + if (c >= 0) tmxr_putc_ln (lp, c); /* output char */ + tmxr_poll_tx (&ttx_desc); /* poll xmt */ + } + else { + tmxr_poll_tx (&ttx_desc); /* poll xmt */ + sim_activate (uptr, ttox_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } +dev_done = dev_done | (INT_TTO1 << ln); /* set done */ +int_req = INT_UPDATE; /* update intr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat ttox_reset (DEVICE *dptr) +{ +int32 ln, itto; + +ttx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ +for (ln = 0; ln < TTX_LINES; ln++) { /* for all lines */ + ttox_buf[ln] = 0; /* clear buf */ + itto = (INT_TTO1 << ln); /* interrupt */ + dev_done = dev_done & ~itto; /* clr done, int */ + int_req = int_req & ~itto; + int_enable = int_enable | itto; /* set enable */ + sim_cancel (&ttox_unit[ln]); /* deactivate */ + } +return SCPE_OK; +} + +/* Attach master unit */ + +t_stat ttx_attach (UNIT *uptr, char *cptr) +{ +int32 t; +t_stat r; + +r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +t = sim_rtcn_init (ttix_unit.wait, TMR_TTX); /* init calib */ +sim_activate (uptr, t); /* start poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat ttx_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&ttx_desc, uptr); /* detach */ +for (i = 0; i < TTX_LINES; i++) /* all lines, */ + ttx_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < TTX_LINES; i++) t = t + (ttx_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < TTX_LINES; i++) t = t + (ttx_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < TTX_LINES; i++) { + if (ttx_ldsc[i].conn) { + if (val) tmxr_fconns (st, &ttx_ldsc[i], i); + else tmxr_fstats (st, &ttx_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Enable/disable device */ + +void ttx_enbdis (int32 dis) +{ +if (dis) { + ttix_dev.flags = ttox_dev.flags | DEV_DIS; + ttox_dev.flags = ttox_dev.flags | DEV_DIS; + } +else { + ttix_dev.flags = ttix_dev.flags & ~DEV_DIS; + ttox_dev.flags = ttox_dev.flags & ~DEV_DIS; + } +return; +} diff --git a/S3/haltguide.txt b/S3/haltguide.txt new file mode 100644 index 0000000..24e90f8 --- /dev/null +++ b/S3/haltguide.txt @@ -0,0 +1,950 @@ + IBM System/3 Model 8/10 SCP + + ********** + Halt Guide + ********** + +This following list is my own reformatting and rewording of the +official IBM Halt Guide for the Model 8/10 SCP. + +The halts are those displayed on the message display unit. The +list is in alphabetical order for easy reference. + +When the system halts, the two 7-segment displays will display the +halt as listed here, and the system console (or printer if the log +device is the printer) will print the "SCP Message" below. To +respond to the halt, deposit one of the valid response numbers +(0 thru 3) into the SR, and then use the C command to continue. + +Unless otherwise stated, a response of 0 means to continue and accept +the error, 1 means to retry the operation or re-read the statement in +error, 2 means to cancel the job and retain added records (if any) and +3 means to end the job and discard any added records in files. + +This is a listing of those halts likely to be encountered using SCP on +the simuator, it is not the complete list of all possible halts. + +Halt SCP Message Description +---- ----------- ----------- + +00 Invalid response to another halt. Deposit a valid + value (0 thru 3) in SR. + +0A A 5448 Disk Unit not ready. + +0C 5448 Disk Equipment Check + +0E Permanent disk error during logging. + +0F ID0FXX 23 Invalid cylinder number on disk operation. + XX = Disk Drive. + +0H ID0HXX 23 Invalid sector number on disk operation + XX = Disk Drive. + +0Y IK0Y0X 123 3741 Error. X: 1=not ready 2=wrong mode + 3=parity error 5=record length error + +0 ID0 XX 23 Disk Data Check + XX = Disk Drive. + +0- ID0-XX Invalid disk operation code: Start cancels job. + XX = Disk Drive. + +10 3 No input file allocate, user error. + +11 0 23 Square root of a negative field + +12 0 23 Divide Overflow + +13 0 23 Divide by zero + +14 0 23 Varible index zero of out of range + +15 0 23 Sequenced table is out of sequence + +16 0 23 (RPG) OBject tables expected. /* Read. + +17 0 23 (RPG) Object table exceeds specified length + +18 0 23 (RPG) Terminal errors in source program. + +19 0 3 (RPG) Warning errors in source program. 0=continue. + +1A 3 (RPG) Out of core memory + +1C 23 Unidentified halt has been issued. Probable system + error. + +1E 0 3 (RPG) Demand file at end of file. + +1F 23 (RPG) End of file or end of extent. If during RPG + compilation, expand $SOURCE or $WORK. + +1H 0 23 Duplicate keys found during build of indexed file. + 0=skip this record and continue. + +1J 0 23 Out of sequence keys during build of indexed file. + 0=skip this record and continue. + +1L 0 23 Key changed during record update. User error. + 0=continue, do not update record. + +1P 01 Forms in printer need positioning. + +1U 123 No record found on direct or indexed file. + +1Y 0 23 (RPG) Invalid numeric data to DSPLY statement. + +1 0 3 Object program ready to punch tables. + +20 1 3 Disk Sort: Invalid header and no // SOURCE + +21 01 3 Disk Sort: Name on // SOURCE not found + +22 0 2 Disk Sort: Warning errors found. + +23 3 Disk Sort: Unrecoverable error. + +25 3 Disk Sort: Terminal errors in sort statements. + +27 0 Disk Sort: In debug mode, finished pass. + +2C 0 3 Disk Sort: No Input Records selected. + +2E 3 Disk Sort: Workfile too small. + +2F 23 Disk Sort: Output file too small. + +2L DT2LY2 3 Tape Record too large to process. + DT2LY7 3 No FILE statement for tape file open. + DT2LY9 3 No enough storage for tape operation + DT2LTC 3 Invalid tape header length + DT2LYF 123 Incorrect block length read from tape + +2P Permanent tape error. + +2U 12 Tape unit is write protected. + +2Y 3 Invalid device specification in object. + +2- 0 3 First statement was not a Tape Sort header. + +30 EG30 3 Space not available on R1 or F1. + UB30A1 0 3 Active files exist on output disk + UB30AF 0 3 Active files exist on 5448 disk + UB30H1 0 3 Wrong capacity or uninitialized pack + UB30NS 3 No 5448 disk for $PCOPY + UB30TP 0 3 Pack change required. + UC30AF 3 Active or system files on target + UC30BD 3 Volume label cannot be read + UC30SP 3 Not enough space for work file + UP30AF 3 Active or system files on target + +31 UI31AF 0 3 Active or system files on target + 0=proceed to next unit to initialize + UI30WP 01 3 Wrong pack, name does not match. + +32 UB32Bx 01 3 5444 pack is not a $PCOPY backup pack. + UB32NP 01 3 Unit specified is not a $PCOPY pack. + UC32BD 3 FROM pack is a TO pack from an interrupted + COPYPACK run. + UC32BP 3 Output pack is a $PCOPY output pack. + Must be initialized or reset by a RESET + statement to be used. + UC32DS 3 Packs FROM and TO are different sizes. + +33 UI33PU 0 3 Pack defective, cannot be initialized. + +34 Ux34 1 3 Keyword in a utility control statement is invalid. + +35 UC35xx 1 3 Error in $COPY or $KCOPY control statement. + +36 UI36CE 0 3 CE track defective on unit F1. + +37 UC37xx 0 3 Pack change required. xx: FP=mount on R1, + IP=pack on COPYIN, OP=pack on COPYO. + +38 UA38XX 0 3 Wrong pack mounted. + UB38DA 01 3 Dates do not match. + UB38DM 01 3 2nd 5444 pack not from same backup set as 1st. + UB38IP 01 3 PACKIN keyword pack not same as pack mounted. + UB38OP 01 3 PACKO keyword not same as pack mounted. + +3A UC3Axx 3 Key out of sequence (DP), invalid high key (HK), + out of space to copy file (XE), or disk I/O error. + +3C UC3CCS 3 COPYFILE out of core. + UC3CNF 3 Module not found, name is logged as R XXXXXX. + +3E UC3EOX 0 3 COPYFILE output not as big as size of input. + +3F UC3Fxx 1 3 Error in COPYFILE statement. + +3J UC3Fxx 3 Invalid specification for Copy/Dump. + +3P UC3Pxx 1 3 Error in COPYPACK, RESET, or LABELS statement. + +3Y UI3YIS 0 3 Requested secondary init when primary required. + +3 UI3 xx 1 3 Error in VOL statement. + +40 DD40 3 File has been referenced as an output or add file + and the file is already allocated. + +4A DD4A 3 File had already been opened and is re-opened. + +4C DD4C 3 Multivolumne file spec error. + +4E DD4E 3 FILE indicates a multivolule file being built, + but program compiled for single volume. + +4F DD4F 3 Print buffers not aligned in program. + +4H DD4H 0 3 Unordered load specified for ISAM. Ordered load + must be specified on RPG file specs. + +4J DD4J 3 All file specs have been checked and there were + errors. + +4L DD4L 3 Referenced file already allocated. + +4P DD4P 3 Prgram/FILE statement mismatch. + +4U DD4U 3 File referenced as update, already allocated. + +4Y DD4Y 3 File has an incorrect device specification. + +4 DD4 3 No FILE specification for referenced file. + +4' DD4' 3 Attempting reference to a file in 2 levels, one or + both using RETAIN-S. + +50 UA50ID 2 Bad track which can't be reassigned. + +51 UR51 12 Can't use Alternate Track program in procedure. + +52 ML52 12 EOJ for Card List program. + +53 IU53 1 3 Number of VOL statements does not agree with number + of units on UIN statement. + +54 EO54 3 End-of-file. + +55 UF55xx 1 3 Error in SCRATCH or REMOVE statement. + +56 UA56TS 0 3 ASSIGN track is over disk capacity. + UA56XX 0 3 Unit specified is uninitialized. + +57 UF57WP 01 3 File delete program. Wrong pack is mounted. + 0: Mount correct pack and continue. 1: + correct statement and retry. + +5A UA5Axx 012 Alternate track assignment error. + +5C MR5Cxx 1 3 Invalid reformat specs. + +5F UF5Fxx 1 3 Error in DISPLAY statement. + +5H UA5HEU 0123 Primary track is still defective. + +5L UF5LAF 0 3 PRogram try to delete files that are being used by + another program. + UF5LNF 0 3 File not found. + UF5LTM 23 Too many files specified. Max is 40. + +5U UI5Uxx 1 3 Error in UIN statement. + +5Y UR5Yxx 1 3 Error in REBUILD statment. + +5 UA5 xx 1 3 Error in ALT statement. + +5- 3 Tape Sort error occurred. + +5' UF5'N1 0 3 Pack cannot be used. Not inited. + UF5'NU 0 3 Pack was used as TO pack on a COPYPACK job + that required early termination. Can only + be used for another COPYPACK job. + +60 LM60SY 0 3 Cannot remove or change library size on pack + from which $MAINT was loaded. + +61 LM61EP 0 3 Trying to copy a system to a library that is + 1) not empty, 2) not allocated with large enough + SWA, or 3) not allocated with enough space. + LM61NS 0 3 System does not exist on FROM pack. + +62 LM62CS 01 3 Check Sum error. + LM62DR Can't determine if REMOVE is data or control. + LM62EF FROM, TO, or AFTER statement does not exist or + is out of sequence. + LM62ND NO data records following INSERT or REPLACE. + LM62SQ Records are out of sequence. + LM62TP Incorrect type record. + +63 LM63DE 0 3 Directory entry error. Name can't be found or + attributes don't match, or attempt to remove + dir entry with MODIFY. + +64 LM64DS 0 3 Syntax error in ALLOCATE. + +65 LM65UN 0 3 Pack not properly initialized. + +66 LA66xx 3 Error with LOAD * function. + +67 EL67NL 0 3 Library does not exist. + +68 EL68DF 0 3 No room in library or directory. + +69 XX69HE 3 Disk I/O Error while using library. + +6A LM6Axx 1 3 $MAINT has detected a syntax error on a control + statement. xx gives a hint of what might be wrong. + AL: SOURCE or OBJECT missing or invalid + AZ: SYSTEM missing or invalid + D2: FROM, TO, or WORK is R2 or F2, not available + DK: Duplicate keyword + DS: Invalid DIRSIZE + FL: Invalid or missing FILE keyword + FM: Invalid or missing FROM keyword + IK: Invalid keyword + IN: Invalid INCR keyword + IS: first 3 columns must be // blank + IV: Invalid statement identifier + LB: Invalid LIBRARY keyword + LS: Invalid LIST keyword + NK: No keywords + NM: Invalid NAME keyword + NU: Invalid NEWNAME keyword + OM: Invalid OMIT keyword + RL: Invalid RECL keyword + RS: Invalid RESER keyword + RT: Invalid RETAIN keyword + SF: INvalid SEQFLD keyword + SQ: Invalid FROM, TO, or AFTER in MODIFY mode + XC: Invalid record. + XD: Duplicate keyword + XF: $$SYFG could not be found. + XL: LIBRARY keyword missing + XM: NAME keyword missing + XN: NAME parameter is invalid + XP: Library does not exist on this pack + XS: Syntax error + XT: Invalid library type + XV: INvalid operation + +6C LM6CSP 0 3 Not enough space on pack. + LM6CSW 0 3 Space not available for work file. + +6E LM6EOF 0 3 Overflow in seq field during RESER. + LM6EDP 0 3 Entry with same name already exists in library. + +6H EL6HDT 0 3 Trying to replace perm with temp entry. + LM6HDP 0 3 NEWNAME is already in library. + +6J LM6JCC 0 3 Control statements are missing. + +6L UA6L 3 Log device is required for this program. + +6Y LM6YNN 1 3 No NEWNAME when copying to same library. + +6 LM6 BC 3 Invalid character in source record. + LM6 CM 0 3 Invalid object deck. + LM6 ND 0 3 No data between COPY and CEND. + +6- LM6-BC 01 Entry containing a blank card being placed in + library. 0: accept, 1: skip and read next card. + +6' LM6'CE 1 3 // CEND expected but not found. 1: Retry, + provide CEND. NOTE: For option 3, if a module + was being replaced, it may have been deleted but + new module not loaded. + +70 CR70 3 Too many overrides for procedure. Max is 25. + +71 CR71 0 3 OCL Syntax Error. + +73 CR73 0 // PARTITION given in invalid location. + +74 CR74 3 /& between LOAD and RUN or CALL and RUN. + +75 CR75 23 Extraneous statement. + +76 CR76 0 3 // Missing from OCL statement. + +77 CR77 23 Invalid OCL statement identifier. + +78 CR78 0 3 Unknown OCL keyword. + +79 CR79 23 Continuation expected but not received. + +7A CR7A 3 A second LOAD or CALL found before run, or a + CALL in procedure overrides. + +7C CR7C 0 3 // COMPILE found between jobs. + +7E CR7E 0 3 // DATE found between jobs. 0: Ignore and continue. + +7F CR7F 0 3 // FILE found between jobs. Must go between + // LOAD or // CALL and // RUN statements. + +7H CR7H 0 3 // SWITCH found between jobs. + +7J CR7J 23 // READER found between LOAD or CALL and RUN. + +7L CI7Lxx 23 Error when reading a tape file. + +7P New print chain expected. Load it and press START. + +7U CR7U 3 RUN statement not preceeded by LOAD or CALL. + +7Y CI7Yxx 23 Error outputing a tape file. + +7 CR7 3 Too many utility control statements, max is 25. + +7- CR7- 0 // PARTITION was read but system does not support + Dual Programming. + +7' Error during tape processing. + +80 CR80 0 // DATE card has not been entered. + +81 CR81 23 Error in LOAD statement. + +83 CR83 23 Error in LOAD * statement. + +84 CR84 23 Error in CALL statement + +85 CR85 23 Second SWITCH statement found. + +86 CR86 23 Invalid paramter in switch statement. + +88 CR88 1 3 Procedure not found. + +89 CR89 01 // DATE has already been given. + 0 - accept the new date as the date. + 1 - leave the old date as the current date. + +8A CR8A01 0 Invalid date specified. + CR8A02 0 DATE parameter missing. + +8C CR8C 23 Second DATE found. + +8E CR8E01 23 Date specified incorrectly. + +8F CR8Fxx 23 Invalid BSCA statement. + +8H CR8H 3 More than 9 levels of procedures have been called. + +8J CR8J 0 Invalid // READER parameter. + +8L CR8L 0 Desired system input device being used by other + program. + +8P CR8P 0 Output device not defined. + +8U CU8UIP 23 Invalid HIKEY in FILE statement: non-numeric. + CR8UKL Parameter length mismatch. + CR8ULO Key greater than 29. + CR8UPL HIKEY-P greater than 15. + CR8USQ HIKEY parameters not in sequence. + +8Y CR8Y 0 Not logging can be done. Log turned off by + other program level. + +8- CR8- 0 3 Logging requested but cannot be done. + +90 CR90 0 // PAUSE statement read. Check printer or console + for instructions and continue. PAUSE was outside + LOAD and RUN. + +91 CR90 0 // PAUSE statement read. Check printer or console + for instructions and continue. PAUSE was inside + LOAD and RUN. + +92 CR92 23 COMPILE already recieved for this job. + +93 CR93 23 Error in COMPILE statement. + +94 CR94 23 Error in COMPILE statement. + +95 CR95 23 Error in COMPILE statement. + +96 CR96 0 23 System error. An OCL error was found, but the system + cannot resolve the error. + +97 CR97 0 Error in LOG statement. + +98 CR98 23 Error in LOG statement. + +99 CR99 0 23 Error in LOG statement. + +9A CR9A 23 Indicated action on last OCL statement read will + be ignored due to previous errors detected. + +9C CR9Cxx 123 Incorrect tape volume online. + +9E CR9E 0 Logging device being used by other program level. + +9F CR9F 0 23 Logging device in use by other program. + +9H CR9H 23 Log device in use. + +9J CR9J 0 Error in FORMS statement. + +9L CR9L 0 23 Error in FORMS statement. + +9P CR9P 23 Error in FORMS statement. + +9U CR9U 0 3 Other program has gotten a // IMAGE or other + program level is using the printer. + +9Y CR9Y 0 23 Logging device not sysgenned or CCP has it. + +9 CR9 0 23 Same as 9Y. + +9- CR9- 0 3 Other program level received a // FORMS or + other level using the printer. + +9' CR9' 0 Same as 9Y. + +A0 CRa0xx 23 Syntax error in FILE statement. + +A1 CRA1xx 23 Keyword error in FILE statement. + +A2 CRA2xx 23 Parameter error on FILE statement. + xx gives parameter: + 01 NAME, 02 UNIT, 03 PACK, 04 LABEL, + 05 RETAIN, 06 DATE, 07 RECORDS, 08 TRACKS, + 09 LOCATION, AS ASCII, BL BLKL, CV CONVERT, + DF DEFER, DN DENSITY, EN END, PT PARITY, + RC RECL, RF RECFM, RL REEL, SP SPLIT, + TN TRANSLATE. + +A3 CRA3xx 23 Missing Parameter on FILE statement, xx = + NN: NAME, NP: PACK, NU: UNIT, OP: no + parameters. + +A4 CRA4xx 23 Invalid parameter combination in FILE statement: + AS: ASCII-YES and RECFM-D/DB on 7-track tape + AV: ASCII-YES and RECFB-V/VB + AY: RECFM-D/DB without ASCII-YES + CT: CONVERT-ON and TRANSLATE + DI: UNIT says tape but disk parameters given + DN: DENSITY-800 not supported. + FS: RECFM is fexed and block or rec len less than 18 + IL: Incorrected RECL or BLKL for RECFM + IP: SPLIT or LOCATION used with RECORDS / TRACKS. + IR: LABEL, DATE or RETAIN wirh REEL-NL or REEL-NS + NS: Not all units are 7-track + PC: CONVERT-ON and PARITY-EVEN + RC: CONVERT-ON not given with RECFM-V/VB for 7-track + SD: DENSITY-1600 invalid for 7-track + SL: LOCATION missing or invalid for SPLIT. + SM: SPLIT invalid for multivolume files. + ST: 7-track paras with 9-track unit + SU: SPlit can't be used with 5444 + TL: TRACKS/LOCATION invalid with unit + TP: UNIT is disk but tape paras given + TR: TRACKS and RECORDS both given + +A6 CRA6xx 23 Error in FILE statement for multivolumne files. + +A7 CRA7xx 23 Error in IMAGE statement. + +A8 CRA8xx 0 Error in IMAGE statements o disk. + +A9 CRA9xx 0 23 Same as A8. + +AA CRAAxx 23 Same as A8. + +AC CRAC 0 Invalid hex character in chain image. + +AE CRAE 0 23 Same as AC. + +AF CRAF 23 Same as AC. + +AH CRAH 0 Error in IMAGE statement. + +AJ CRAJ 0 23 Same as AH + +AL CRALxx 0 Error in PARTITION statement. + +AP CRMN 0 3 Either reocvery option has has been selected during + a job, or OCL errors have occurred for this job. + 0: Continue iwth next job, or no data cards in + reader for this job, otherwise, 3 to cancel. + +AU CRAUxx 23 Error in PARTITION statement. + +A CRA 23 Total number of volumes for a FILE statement + exceeds 40 (!). + +A- CRA-xx 0 23 Error in PARTITION statement. + +A' CRA' 3 No space remaining is System work area. Too many + FILE statements are in this job. + +C1-C9 IFC1 123 1442 Check, various causes. + +CL UDCLxx 1 3 5445 Data Interchange Utility error + +E7 DKE7 0 3 Incorrect record length for attached 3741 + +E8 UTE8xx 1 3 Error in Tape Init VOL statement. + +E9 UTE9xy 0 3 Error during Tape Init Processing. + +F8 DDF8 3 RPG--LIne counter specs omitted and skip past + page size for printer. + +F9 CIF9xy 23 Tape drive not available, x = drive #. + +FA CIFA 3 Program requesting Data Recorder, unsupported. + +FC CIFC 3 Program requesting CRT, unsupported. + +FE DDFE 0 3 Program requesting line line on printer that + exceeds sysgen value. + +FF RPQ routine error. Press start to continue. + +FH CIFH 123 BSCA line not supported. + +FJ CIFJ01 123 1442 not supported but requested + CIFJ02 123 3741 not supported but requested + +FL CIFL 123 Printer/keyboard not supported or unavailable. + +FP CIFP 123 Printer not supported or allocated to other level + +FU CIFU 123 MFCU not supported or allocated to other level + +FY CIFY 23 Device is not supported or in use. + +F CIF 23 Conflict with a resource being used by other level. + +H0-H9 0 23 RPG Programmed halt indicator is on. + +HA CIHA 3 Out of space on $SOURCE during compile. + +HC CIHC 3 Program given on LOAD statement not found. + +HE Hardware error. Simulator has messed up. + +HF CIHF 0 3 // COMPILE read but not required. + +HJ CIHJ01 1 3 Program not found on removable unit, 1: + mount new unit and retry. + CIHJ02 3 Program not found, but removable unit in use. + +HL CIHL 3 Inquiry request made but program is wrong type. + +HP CIHP 3 Insufficient main storage for program. + LMHP 3 $MAINT function out of storage. NOTE: After the + cancel, IPL from the system pack or the pack will + be unusable. + +HU CIHUxx 3 Source program not found on disk. IF a 1 option + is present, you can mount a new removeable pack. + +HY CCHYNN 0 A checkpoint is received and accepted. + +H CCH NN 0 23 A restart has been requested. + +H' CIH' 3 An uninitialized pack has been referenced. + +J0-J9 123 Record with specified match field out of sequence. + This is an RPG error, the 2nd digit indicates which + RPG file statement the error applies to in the + source program. 0=greater than statement 9, + otherwise indicates the file statement number. + +JA CIJA 3 Trying to laod a program that requires or allows + inquiry while another inquiry program is running + in the other level. + +JC CIJCxx 3 Program cannot be run for this reason (xx): + 01: Must be dedicated and other level active + 02: Program in other level must be dedicated + 03: $$RSTR cannot run in level 2 + 04: CHeckpointed program not allowed in level 2 + 05: Program can't run while checkpoint active + +JE CIJE 0 3 Level 1 partition too small. + +JF CIJF 3 Attempt to start inquiry program but keyboard + in use. + +JH CIJF 3 Attempt to start program which allows interrupts in + level 2. + +JJ CIJJ 3 No object library on pack requested for load. + +JL CIJL 3 Not enough storage for program. DPF only. + +JP System input device in use by other level. + +JU 0123 Cancel request made from interrupt key. 0: ignore + 1: continue, request ignored + +JY CIJYRD 0 2 Inquiry request made and accepted. + +J- 3 Attempt to run a CCP program, but CCP not running. + +J' 01 3 Inquiry request is completed, interrupted program + can now resume. + +L0-L9 123 RPG. Unidentified record, 2nd digit gives file + statement number in source program 1-9, 0 means + greater than 9. Can also occur if record is out + of sequence. + +LA CILA 23 Too little storage for number of files in program. + +LC CILC 23 Too little storage for requested allocation. + +LE CILE 23 No FILE or an incorrect FILE for a file requested + by current program. + +LH CILH 23 No space given for an output file on FILE statement. + +LJ CILJ 23 Attempt to output to existing permanent file. + +LL CILL 0 23 Attempt to output over an existing temporary file. + +LP CILP 23 File already exists. + +LU CILU 123 Pack name requested but wrong pack mounted. 1: + retry after mounting correct pack. + +LY CILYxx 23 Attempt to allocate space that isn't available. + xx=02 means space not available in split cylinder + area. + +L LML CP 01 3 $MAINT detected attempt to modify a program on + a pack with an active checkpoint. + +L- CIL- 3 Attempt to add a split cylinder to a split cyl + file while other level is fiddling with a split + cylinder file. + +L' CIl' 23 Trying to allocate a split cylinder file before + allocating the first split cylinder file in a group. + +P1-P8 Printer hardware errors, should not occur in sim. + +PC IPPC 0 23 Unprintable character. + +PH CIPH 23 LOCATION plus TRACKS goes past end of pack. + +PJ CIPJxx 1 A Pack is to be remounted, pack name printed before + half code, xx= unit. + +PU CIPU 3 Duplicate file names in the FILE statements. + +PY CIPY01 3 ISAM file requires at least 2 tracks. + CIPY02 3 ISAM file can't be split cylinder. + +P' CIP'xx 23 Too many scratch or work files. + +U0-U9 0123 RPG. Unidentified record in file, 2nd digit of + halt is file statement in RPG source, 0= greater + than 9. + +UA CIUA 3 Attempt to create a multivolume file in + invalid. + +UC CIUC 3 The printed actived file cannot be found in the + list of scratch files. + +UE CIUExx 1 3 PACK parameter does not match pack name on unit. + xx = Unit referenced. + 1 = Mount another pack and continue. + +UF CIUF 3 Disk file referenced by name and date not found. + +UH CIUH 3 Attempt to create multivolume file failed, + because name alreayd exists. + +UJ CIUJ 3 A LOCATION was specified for an existing disk file + and the file exists but not at that location. + +UL CIUL 3 File on // FILE statement not found, and no size + in TRACKS or RECORDS was given. + +UP CIUP 3 Permanent file referenced with RETAIN-S + +UU CIUU 3 Disk Pack not available. + +UY CIUY 3 File is a System/3 BASIC file which must be unique. + +U CIU 3 Existing file: TRACKS/RECORDS or LOCATION mismatch. + +U- General CCP halt. Press start to see subhalt. + Refer to CCP manual for more info. + +U' CIU' 23 VTOC is full, or more than 2 multivolume files per + pack, or more than 2 ISAM files using HIKEY + parameter. + +YH CRYH 0 3 Cards are being punched, but card read from + reader was not blank. This means you are trying to + punch with a file attached to the CDR device. + Unattach the file and take the zero option. + + 0 (blank 0) FILE WRITE switch in off position. + + 1 (blank 1) Permanent DIsk I/O Error + + 2 RC 211 3 COBOL. Out of room on $WORK. + RC 212 3 Out of room on $SOURCE. + RC 213 3 Out of room on $WORKX. + RC 214 3 Subprogram name table greater than 20. + RC 219 0 3 C or E level diagnostics during compile. + RC 2A1 23 Subscript invalid + RC 2A2 23 Negative exponent or 0 degrees in program + RC 2F1 23 MFCU File not open or opened improperly + RC 2F2 23 1442 File not open or opened improperly + RC 2F3 23 1403/5203 File not open or opened improperly + RC 2F4 23 5444 Disk File not open or opened improperly + RC 2F5 23 5444 File not open or opened improperly + RC 2F7 23 5444 File not open or opened improperly + RC 2F8 23 Tape File not open or opened improperly + RC 2H1 23 OPEN attempted after CLOSE WITH LOCK + RC 2H2 23 Error during ACCEPT + RC 2H3 23 $$STOP not found + RC 2H4 23 CHeckpoint could not be taken. + RC 2H5 23 $$STIC not found for ACCEPT + RC 2H6 23 Parameter mismatch CALL and USING + RC 2H7 23 ACCEPT after /& read + RC 2H8 23 OPEN for a file already OPEN + RC 2 0 3 Too little core for compile + RC 2 1 3 PROCEDURE or DATA division not found. + RC 2 3 3 Program has more than 65535 statements (!) + RC 2 4 3 Source name on COMPILE statement not found + + 3 R 3XX 0 3 COBOL Stop literal. XX is user-specified. + 0 continues program 3 cancels. + + 4 VF 4NF 3 Program not found. Program library and + not printed before halt message. + + 6 RF 6XX 0 23 FORTRAN stop statement. + + 7 RF 701 23 Source member on COMPILE not found + RF 702 23 Object program too large for core. + + 8 CS 8 1 3 System input device allocated to other level. + + 9 CS 9 1 3 System input device has an error. This usually + means the card hopper is empty (i.e. EOF on the + file attached to the reader but SCP wants more + input). + + A DC A 123 Number of characters entered from keyboard + incorrect. + + C DD C 0 23 Unprintable character for printer/keyboard. + + E DC E 123 Hardware error, PKB + + F DC F 0 23 End of forms, PKB + + L DD L 0 3 Records with duplicate keys have been loaded + into ISAM file. Each dup key is logged followed + by blank P halt. 0: continue. Index will + contain duplicate keys. 3: cancel, file is + not usable, reload it. + + P DD P 0 3 Duplicate key encountered. The key is printed + on the log. 0: continue, halt will recur for + any other duplicates, then blank L appears. + + U DD U 3 Disk I/O error while sorting ISAM index. + + Y DD Y 3 System error during file termination. + +-0 DD-0XX 3 ISAM multivolume file being used and high key + not found for current columme, or does not agree + with HIKEY spec. XX=unit number. + +-1 DD-1XX 123 Halt -P occurred and option 0 taken. But, the + pack mounted is not a part of volume set. + +-2 DD-2XX 123 Multivolume load sequence error. + +-3 DD-3XX 123 Multivolume load sequence error. + +-4 DD-4XX 0123 Warning that one or more volumes are about to be + bypassed. + +-5 DD-5XX 123 Multivolume file not found. 1: mount correct pack. + +-6 DD-6XX 0 23 Warning. ENd of volume and HIKEY not found. + +-7 DD-7XX 1 3 -A halt and option 1 taken. But the pack referenced + does not match pack name. + +-8 DD-8XX 3 Multivolume file referenced but file isn't + multivolume. + +-9 DD-9XX 3 Add to a multivolumen file, but last pack not + mounted. + +-A DD-AXX 1 3 Add to existing multivolume filebut no room. + +-C DD-CXX 3 Multivolume file error. Probably out of sequence + volume mounts. + +-E DD-EXX 123 Next volume cannot be processed, because the + location is not available or space is not available + or there are scratch files on the pack. + +-F DD-Fxx 123 Finished a volume, next cannot be processed, mount + the correct pack or cancel. + +-H DD-Hxx 3 HIKEY length does not match file. + +-J DD-Jxx 01 3 First volume referenced is not volumme 1. 0: + continue with this volume, 1: mount another pack. + +-L DD-Lxx 3 Output to multivolume, but file isn't multivolume + or referenced volume isn't first one of set. + +-P DD-PXX 0123 Mount next volume. XX=unit number. 0: continue + bypassing volumes, 1: mount next volume. + +-U DD-UXX 1 3 Halt -J just occurred and 0 or 1 taken. But the + pack name is incorrect or the file isn't found. + +- DD- 123 Multivolume key error. Key too low or high for + volume. + +-' DD-' 123 Sequential add to multivolume file, but HIKEY + record missing on previous volume. + +'0 GM'0DE 3 SYSGEN. I/O Error on reader. + GM'0EX 3 SYSGEN. End of extent on MACOUT or $SOURCE. + GM'0IC 1 3 SYSGEN. Option dependent on a preceding option, the + preceding one was omitted or invalid. + GM'0ID 1 3 SYSGEN invalid delimiter. + GM'0IK 1 3 SYSGEN invalid keyword. + GM'0IR 1 3 SYSGEN invalid option. + GM'0IS 1 3 SYSGEN sequence error. + GM'0NF 1 3 SYSGEN entry in cols 8-12 not found. + GM'0NS 3 SYSGEN Requested source program not found. + GM'0EM 1 3 SYSGEN. END statement not found. + GM'0NP 3 SYSGEN. Module $SGXP2, $SGXP3, $SGXP4, $SGXP5, or + $SGXP6 missing for sysgen, or $MPXP2, $MPXP3 or + $MPXP4 missing for macro processor. + +'1 GG'1 3 System Generation Errors. + +'2 0 3 Error during macro processor run. + +'3 3 Invalid 5445 disk label record. + +'4 GG'4EX 3 Out of room on Sysgen, or disk error. + +----------------------- End of haltguide.txt --------------------------- diff --git a/S3/readme_s3.txt b/S3/readme_s3.txt new file mode 100644 index 0000000..37afdd9 --- /dev/null +++ b/S3/readme_s3.txt @@ -0,0 +1,78 @@ + Welcome to the IBM System/3 Model 10 SIMH simulator. + --------------------------------------------------- + + To compile under linux: + + cc s3*.c scp*.c sim_rev.c -o s3 + + This code can be compiled and run as a console application using + Microsoft Visual C++. + + + + To IPL the provided SCP distribution disk: + + ./s3 + sim> at r1 m10scp.dsk + sim> at f1 f1f1f1.dsk + sim> at lpt print.txt + sim> d sr 5471 + sim> boot r1 + + + // DATE 06/14/01 + // NOHALT + // LOAD $MAINT,R1 + // RUN + // COPY FROM-R1,LIBRARY-ALL,NAME-DIR,TO-PRINT + // END + + + (A printout of the libraries and directories on the SCP DTR + disk will be in the file print.txt) + + + The text file "system3.txt" gives details on the simulators + implementation of System/3 hardware. + + A write up on the use of the SCP and the OCL job control language is + in the text file "userguide.txt". This includes examples of using the + utility programs, and a tutorial guiding you thru a sysgen. + + A nearly complete listing of all possible SCP halts is in the + document "haltguide.txt". + + IMPORTANT NOTES: + + 1) How to correct typing errors when using the System/3 console: + If you make an error, press ESC, which will cancel the current + line being typed and print a quote in position 1. Then you + can use CTRL/R to retype characters up until the error, then + type correctly. Or simply retype the line. BACKSPACE DOES NOT + WORK with the SCP. + + 2) While the simulator allows disk images to be independently + attached to any disk unit, on the real hardware R1 and F1 were on + a single spindle, and R2 and F2 likewise. It is not possible using + SCP to attach R1 without attaching a disk image to F1 also, because + SCP will always look at F1 even when IPLed off R1. + + The OS distributed with the simulator is version 16 of the Model + 10 SCP. This is sysgenned with support only for R1 and F1. If you + do a sysgen to support R2 amd F2 also, you must have images attached + to all 4 disks when you IPL, because SCP looks at all drives when + it starts up, and you will get an "Unattached Unit" error if you + fail to have one attached. + + 3) The 1442 card reader had in reality one card input hopper + and two stackers. This means the same path is used for reading and + punching cards. When punching cards, SCP does a read operation + and inspects the card read for blanks, and if it is not blank, + issues a YH halt. SCP will not punch data onto non-blank cards. + This feature causes problems in the simulator, and as a result + if you punch cards from SCP, YOU MUST not have any file attached + to the CDR device. Leaving this device unattached presents an + infinite supply of blank cards to SCP for punching. + + + -- End of README_S3.txt -- diff --git a/S3/s3_cd.c b/S3/s3_cd.c new file mode 100644 index 0000000..5221c77 --- /dev/null +++ b/S3/s3_cd.c @@ -0,0 +1,447 @@ +/* s3_cd.c: IBM 1442 card reader/punch + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + cdr card reader + cdp card punch + cdp2 card punch stacker 2 + + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher + + Normally, cards are represented as ASCII text streams terminated by newlines. + This allows cards to be created and edited as normal files. Set the EBCDIC + flag on the card unit allows cards to be read or punched in EBCDIC format, + suitable for binary data. +*/ + +#include "s3_defs.h" +#include + +extern uint8 M[]; +extern char ebcdic_to_ascii[256]; +extern char ascii_to_ebcdic[256]; +int32 s1sel, s2sel; +char rbuf[CBUFSIZE]; /* > CDR_WIDTH */ +t_stat cdr_svc (UNIT *uptr); +t_stat cdr_boot (int32 unitno, DEVICE *dptr); +t_stat cdr_attach (UNIT *uptr, char *cptr); +t_stat cd_reset (DEVICE *dptr); +t_stat read_card (int32 ilnt, int32 mod); +t_stat punch_card (int32 ilnt, int32 mod); + +int32 DAR; /* Data address register */ +int32 LCR; /* Length Count Register */ +int32 lastcard = 0; /* Last card switch */ +int32 carderr = 0; /* Error switch */ +int32 pcherror = 0; /* Punch error */ +int32 notready = 0; /* Not ready error */ +int32 cdr_ebcdic = 0; /* EBCDIC mode on reader */ +int32 cdp_ebcdic = 0; /* EBCDIC mode on punch */ + +extern int32 GetMem(int32 addr); +extern int32 PutMem(int32 addr, int32 data); + +/* Card reader data structures + + cdr_dev CDR descriptor + cdr_unit CDR unit descriptor + cdr_reg CDR register list +*/ + +UNIT cdr_unit = { UDATA (&cdr_svc, UNIT_SEQ+UNIT_ATTABLE, 0), 100 }; + +REG cdr_reg[] = { + { FLDATA (LAST, lastcard, 0) }, + { FLDATA (ERR, carderr, 0) }, + { FLDATA (NOTRDY, notready, 0) }, + { HRDATA (DAR, DAR, 16) }, + { HRDATA (LCR, LCR, 16) }, + { FLDATA (EBCDIC, cdr_ebcdic, 0) }, + { FLDATA (S2, s2sel, 0) }, + { DRDATA (POS, cdr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, cdr_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, rbuf, 8, 8, CDR_WIDTH) }, + { NULL } +}; + +DEVICE cdr_dev = { + "CDR", &cdr_unit, cdr_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + &cdr_boot, &cdr_attach, NULL +}; + +/* CDP data structures + + cdp_dev CDP device descriptor + cdp_unit CDP unit descriptor + cdp_reg CDP register list +*/ + +UNIT cdp_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; + +REG cdp_reg[] = { + { FLDATA (ERR, pcherror, 0) }, + { FLDATA (EBCDIC, cdp_ebcdic, 0) }, + { FLDATA (S2, s2sel, 0) }, + { FLDATA (NOTRDY, notready, 0) }, + { HRDATA (DAR, DAR, 16) }, + { HRDATA (LCR, LCR, 16) }, + { DRDATA (POS, cdp_unit.pos, T_ADDR_W), PV_LEFT }, + { NULL } +}; + +DEVICE cdp_dev = { + "CDP", &cdp_unit, cdp_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL +}; + +/* Stacker data structures + + stack_dev STACK device descriptor + stack_unit STACK unit descriptors + stack_reg STACK register list +*/ + +UNIT stack_unit[] = { + { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) } +}; + +REG stack_reg[] = { + { DRDATA (POS0, stack_unit[0].pos, 32), PV_LEFT }, + { NULL } +}; + +DEVICE stack_dev = { + "CDP2", stack_unit, stack_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &cd_reset, + NULL, NULL, NULL +}; + + +/* -------------------------------------------------------------------- */ + +/* 1442: master routine */ + +int32 crd (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata; + switch (op) { + case 0: /* SIO 1442 */ + /* if (n == 1) + return STOP_IBKPT; */ + switch (data) { /* Select stacker */ + case 0x00: + break; + case 0x01: + s2sel = 1; + break; + default: + break; + } + switch (n) { + case 0x00: /* Feed */ + iodata = SCPE_OK; + break; + case 0x01: /* Read only */ + if (cdr_ebcdic) + iodata = read_card(0, 1); + else + iodata = read_card(0, 0); + break; + case 0x02: /* Punch and feed */ + iodata = punch_card(0, 0); + break; + case 0x03: /* Read Col Binary */ + iodata = read_card(0, 1); + break; + case 0x04: /* Punch no feed */ + iodata = punch_card(0, 1); + break; + default: + return STOP_INVDEV; + } + return iodata; + case 1: /* LIO 1442 */ + switch (n) { + case 0x00: /* Load LCR */ + LCR = data & 0xffff; + break; + case 0x04: + DAR = data & 0xffff; + break; + default: + return STOP_INVDEV; + } + return SCPE_OK; + case 2: /* TIO 1442 */ + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (carderr || pcherror || notready) + iodata = 1; + if ((cdr_unit.flags & UNIT_ATT) == 0) + iodata = 1; /* attached? */ + break; + case 0x02: /* Busy */ + if (sim_is_active (&cdr_unit)) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + case 3: /* SNS 1442 */ + iodata = 0; + switch (n) { + case 0x01: + break; + case 0x02: + break; + case 0x03: + if (carderr) + iodata |= 0x80; + if (lastcard) + iodata |= 0x40; + if (pcherror) + iodata |= 0x20; + if ((cdr_unit.flags & UNIT_ATT) == 0) + iodata |= 0x08; + if (notready) + iodata |= 0x08; + break; + case 0x04: + iodata = DAR; + break; + default: + return (STOP_INVDEV << 16); + } + iodata |= ((SCPE_OK << 16) & 0xffff0000); + return (iodata); + case 4: /* APL 1442 */ + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (carderr || pcherror || notready) + iodata = 1; + if ((cdr_unit.flags & UNIT_ATT) == 0) + iodata = 1; /* attached? */ + break; + case 0x02: /* Busy */ + if (sim_is_active (&cdr_unit)) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + default: + break; + } + printf (">>CRD non-existent function %d\n", op); + return SCPE_OK; +} + +/* Card read routine + mod 0 = ASCII read + mod 1 = EBCDIC read +*/ + +t_stat read_card (int32 ilnt, int32 mod) +{ +int32 i; +t_stat r; + +if (sim_is_active (&cdr_unit)) { /* busy? */ + sim_cancel (&cdr_unit); /* cancel */ + if (r = cdr_svc (&cdr_unit)) return r; /* process */ +} + +if (((cdp_unit.flags & UNIT_ATT) != 0 || + (stack_unit[0].flags & UNIT_ATT) != 0) && /* Punch is attached and */ + (cdr_unit.flags & UNIT_ATT) == 0) { /* reader is not --- */ + for (i = 0; i < 80; i++) { /* Assume blank cards in hopper */ + PutMem(DAR, 0x40); + DAR++; + } + sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ + return SCPE_OK; +} + +if ((cdr_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; /* attached? */ + +lastcard = carderr = notready = s1sel = s2sel = 0; /* default stacker */ + +for (i = 0; i < CBUFSIZE; i++) rbuf[i] = 0x20; /* clear buffer */ +if (mod) { + for (i = 0; i < 80; i++) { + rbuf[i] = fgetc(cdr_unit.fileref); /* Read EBCDIC */ + } +} else { + fgets (rbuf, CBUFSIZE, cdr_unit.fileref); /* read Ascii */ +} +if (feof (cdr_unit.fileref)) { /* eof? */ + notready = 1; + return STOP_NOCD; +} +if (ferror (cdr_unit.fileref)) { /* error? */ + perror ("Card reader I/O error"); + clearerr (cdr_unit.fileref); + carderr = 1; + return SCPE_OK; } +cdr_unit.pos = ftell (cdr_unit.fileref); /* update position */ +i = getc (cdr_unit.fileref); /* see if more */ +if (feof (cdr_unit.fileref)) lastcard = 1; /* eof? set flag */ +fseek (cdr_unit.fileref, cdr_unit.pos, SEEK_SET); +for (i = 0; i < 80; i++) { + if (mod == 0) { /* If ASCII mode... */ + if (rbuf[i] == '\n' || /* remove ASCII CR/LF */ + rbuf[i] == '\r' || + rbuf[i] == 0x00) + rbuf[i] = ' '; + rbuf[i] = ascii_to_ebcdic[rbuf[i]]; /* convert to EBCDIC */ + } + PutMem(DAR, rbuf[i]); /* Copy to main memory */ + DAR++; +} +sim_activate (&cdr_unit, cdr_unit.wait); /* activate */ +return SCPE_OK; +} + +/* Card reader service. If a stacker select is active, copy to the + selected stacker. Otherwise, copy to the normal stacker. If the + unit is unattached, simply exit. +*/ + +t_stat cdr_svc (UNIT *uptr) +{ +int32 i; + +if (s2sel) uptr = &stack_unit[0]; /* stacker 1? */ +else uptr = &stack_unit[0]; /* then default */ +if ((uptr -> flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +for (i = 0; i < CDR_WIDTH; i++) rbuf[i] = ebcdic_to_ascii[rbuf[i]]; +for (i = CDR_WIDTH - 1; (i >= 0) && (rbuf[i] == ' '); i--) rbuf[i] = 0; +rbuf[CDR_WIDTH] = 0; /* null at end */ +fputs (rbuf, uptr -> fileref); /* write card */ +fputc ('\n', uptr -> fileref); /* plus new line */ +if (ferror (uptr -> fileref)) { /* error? */ + perror ("Card stacker I/O error"); + clearerr (uptr -> fileref); +} +uptr -> pos = ftell (uptr -> fileref); /* update position */ +return SCPE_OK; +} + +/* Card punch routine + + mod: not used +*/ + +t_stat punch_card (int32 ilnt, int32 mod) +{ +int32 i, colcount; +static char pbuf[CDP_WIDTH + 1]; /* + null */ +UNIT *uptr; + +if (s2sel) uptr = &stack_unit[0]; /* stack 2? */ +else uptr = &cdp_unit; /* normal output */ +if ((uptr -> flags & UNIT_ATT) == 0) { /* Attached? */ + notready = 1; + return SCPE_OK; +} +pcherror = s1sel = notready = 0; /* clear flags */ + +colcount = 128 - LCR; +for (i = 0; i < colcount; i++) { /* Fetch data */ + if (cdp_ebcdic) + pbuf[i] = GetMem(DAR) & 0xff; + else + pbuf[i] = ebcdic_to_ascii[GetMem(DAR)]; + DAR++; +} +for (i = CDP_WIDTH - 1; (i >= 0) && (pbuf[i] == ' '); i--) pbuf[i] = 0; +pbuf[CDP_WIDTH] = 0; /* trailing null */ +if (!cdp_ebcdic) { + fputs (pbuf, uptr -> fileref); /* output card */ + fputc ('\n', uptr -> fileref); /* plus new line */ +} else { + for (i = 0; i < 80; i++) { + fputc(pbuf[i], uptr -> fileref); + } +} +if (ferror (uptr -> fileref)) { /* error? */ + perror ("Card punch I/O error"); + clearerr (uptr -> fileref); + pcherror = 1; +} +uptr -> pos = ftell (uptr -> fileref); /* update position */ +return SCPE_OK; +} + +/* Select stack routine + + Modifiers have been checked by the caller + Modifiers are 1, 2, for the respective stack +*/ + +t_stat select_stack (int32 ilnt, int32 mod) +{ +if (mod == 1) s1sel = 1; +else if (mod == 2) s2sel = 1; +return SCPE_OK; +} + +/* Card reader/punch reset */ + +t_stat cd_reset (DEVICE *dptr) +{ +lastcard = carderr = notready = pcherror = 0; /* clear indicators */ +s1sel = s2sel = 0; /* clear stacker sel */ +sim_cancel (&cdr_unit); /* clear reader event */ +return SCPE_OK; +} + +/* Card reader attach */ + +t_stat cdr_attach (UNIT *uptr, char *cptr) +{ +carderr = lastcard = notready = 0; /* clear last card */ +return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ + +t_stat cdr_boot (int32 unitno, DEVICE *dptr) +{ +cdr_ebcdic = 1; +DAR = 0; +LCR = 80; +read_card(0, 1); +return SCPE_OK; +} diff --git a/S3/s3_cpu.c b/S3/s3_cpu.c new file mode 100644 index 0000000..4655773 --- /dev/null +++ b/S3/s3_cpu.c @@ -0,0 +1,1834 @@ +/* s3_cpu.c: IBM System/3 CPU simulator + + Copyright (c) 2001-2005, Charles E. Owen + HPL & SLC instruction code Copyright (c) 2001 by Henk Stegeman + Decimal Arithmetic Copyright (c) 2000 by Roger Bowler + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + ------------------------------------------------------------------------------ + + cpu System/3 (models 10 and 15) central processor + + The IBM System/3 was a popular small-business computing system introduced + in 1969 as an entry-level system for businesses that could not afford + the lowest rungs of the System/360. Its architecture is inspired by and + in some ways similar to the 360, but to save cost the instruction set is + much smaller and the I/O channel system greatly simplified. There is no + compatibilty between the two systems. + + The original System/3 had two models, 6 and 10, and these came in two + configurations: card system and disk system. The unique feature of + the /3 was the use of 96-column cards, although traditional 80-column + cards were supprted also via attachment of a 1442 reader/punch. + System/3 is a batch-oriented system, controlled by an operating + system known as SCP (System Control Program), with it's own job control + language known as OCL (simpler and more logical than the JCL used on + the mainframes). Original models did not support multiprogramming + or any form of interactivity. (There was a hardware dual-program + facility available on the model 10 at the high end). + + The line grew throughout the 1970s, overlapping the low end of the 360 + line with the introduction of the model 15. The 15 (and later larger + variations of the model 12) broke the 64K limit designed in the original + models by adding a simple address translation unit to support up to 512K + bytes. The model 15 added a system of storage protection and allowed + multiprogramming in up to 3 partitions. Communications were added to + allow support of multiple 3270 terminals and the models 12 and 15 broke + the batch orientation and facilitated interactive use via the CCP + (communications control program). The System/3 was effectively replaced + by the much easier to manage and use System/34 and System/36 at the + low and middle of the range, and by System/370 or System/38 at the + high end. + + This simulator implements the model 10 and model 15. Models 4, 6, + 8, and 12 are not supported (these were technical variations on the + design which offered no functionality not present on either 10 or 15). + + The System/3 is a byte-oriented machine with a data path of 8 bits + in all models, and an address width of 16 bits. + + The register state for the System/3 CPU is: + + BAR <0:15> Operand 1 address register + AAR <0:15> Operand 2 address register + XR1 <0:15> Index Register 1 + XR2 <0:15> Index Register 2 + PSR <0:15> Condition Register + IAR [0:9]<0:15> Instruction Address Register (p1, p2, plus 1 for each interrupt) + ARR [0:9]<0:15> Address Recall Register (p1, p2, plus 1 for each interrupt) + (The P2 IAR & ARR are used for the Dual Program feature) + + Instruction formats follow the same basic pattern: a 1-byte opcode, a + 1-byte "Q byte", and one or two addresses following in a format defined + by the first 4 bits of the opcode: + + Op Code Q Byte Address(es) + + 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 + +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--... + | A 1 | A 2 | operation | | (defined by operation)| | Format based on A1, A2 + +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--+--+ +--+--+--+--+--+--+--... + + { --- } <---------------- Bits 00 = Operand 2 specified by 2-byte direct addr + Bits 01 = Operand 2 is 1-byte displacement + XR1 + Bits 10 = Operand 2 is 1-byte displacement + XR2 + Bits 11 = Operand 2 is not used + + { --- } <---------------------- Bits 00 = Operand 1 specified by 2-byte direct addr + Bits 01 = Operand 1 is 1-byte displacement + XR1 + Bits 10 = Operand 1 is 1-byte displacement + XR2 + Bits 11 = Operand 1 is not used + + Instructions come in 3 basic formats, of varying lengths which are determined + by the top 4 bits of opcode defined above. Minimum instruction length is 3 bytes, + maximum is 6. + + 1) Command Format (Bits 0-3 are 1111): + + +------------+ +------------+ +------------+ + | Opcode | | Q-byte | | R-byte + + +------------+ +------------+ +------------+ + + (The meaning of Q-byte and R-byte defined by the operation) + + + 2) One Address Instructions (either bits 0-1 or bits 2-3 are 01): + + + Direct Addressing Format: + + +------------+ +------------+ +-----------+----------+ + | Opcode | | Q-byte | | MSB + LSB + + +------------+ +------------+ +-----------+----------+ + + Base-Displacement Format: + + +------------+ +------------+ +------------+ + | Opcode | | Q-byte | |displacement+ + +------------+ +------------+ +------------+ + + Opcodes are 0011xxxx or 1100xxxx. + + Q-byte can be: 1) An immediate operand + 2) A mask + 3) A branch condition + 4) A data selection + + 2) Two Address Instructions (neither bits 0-1 nor bits 2-3 are both 11): + + Operand 1 Address Direct (opcodes 0001 or 0010): + + +------------+ +------------+ +----------+----------+ +------------+ + | Opcode | | Q-byte | | MSB + LSB + |displacement| + +------------+ +------------+ +----------+----------+ +------------+ + + Operand 2 Address Direct (opcodes 0100 or 1000): + + +------------+ +------------+ +------------+ +----------+----------+ + | Opcode | | Q-byte | |displacement| | MSB + LSB + + +------------+ +------------+ +------------+ +----------+----------+ + + Both Addresses Direct (opcode 0000): + + +------------+ +------------+ +----------+----------+ +-----------+----------+ + | Opcode | | Q-byte | | MSB + LSB + + MSB + LSB + + +------------+ +------------+ +----------+----------+ +-----------+----------+ + + Both Addresses Displacement (opcodes 0101, 0110, 1001, or 1010): + + +------------+ +------------+ +------------+ +------------+ + | Opcode | | Q-byte | |displacement| |displacement| + +------------+ +------------+ +------------+ +------------+ + + +Assembler Mnemonic Format +------------------------- + + The assembler format contains the same elements as the machine language operation, +but not always in the same format. The operation code frequently specifies both +the opcode and the Q byte, and the top nybble of the opcode is determined by +the format of the addresses. + + Addresses take two forms: the direct address in hex, or a relative address +specified thusly: (byte,XRx) where 'byte' is a 1-byte offset, and XRx is +either XR1 or XR2 for the two index registers. Use these formats when +'address' is indicated below: + + When 'reg' is mentioned, a mnemonic may be used for the register, thusly: + IAR Instruction Address Register for the current program level + ARR Address Recall Register for the current program level + P1IAR IAR for Program Level 1 + P2IAR IAR for Program Level 2 + PSR Program Status Register + 0x01 - Equal + 0x02 - Low + 0x04 - High + 0x08 - Decimal overflow + 0x10 - Test false + 0x20 - Binary overflow + 0x40 - Not used + 0x80 - Not used + XR1 Index Register 1 + XR2 Index Register 2 + IARx IAR for the interrupt level x (x = 0 thru 7) + + All other operands mentioned below are single-byte hex, except for the +length (len) operand of the two-address instructions, which is a decimal length +in the range 1-256. + + No-address formats: + ------------------ + + HPL hex,hex Halt Program Level, the operands are the Q and R bytes + + + One-address formats: + ------------------- + + A reg,address Add to register + CLI address,byte Compare Logical Immediate + MVI address,byte Move Immediate + TBF address,mask Test Bits Off + TBN address,mask Test Bits On + SBF address,mask Set Bits Off + SBN address,mask Set Bits On + ST reg,address Store Register + L reg,address Load Register + LA reg,address Load Address + JC address,cond Jump on Condition + BC address,cond Branch on Condition + + These operations do not specify a qbyte, it is implicit in the opcode: + + B address Unconditional branch to address + BE address Branch Equal + BNE address Branch Not Equal + BH address Branch High + BNH address Branch Not High + BL address Branch Low + BNL address Branch Not Low + BT address Branch True + BF address Branch False + BP address Branch Plus + BM address Branch Minus + BNP address Branch Not Plus + BNM address Branch Not Minus + BZ address Branch Zero + BNZ address Branch Not Zero + BOZ address Branch Overflow Zoned + BOL address Branch Overflow Logical + BNOZ address Branch No Overflow Zoned + BNOL address Branch No Overflow Logical + NOPB address No - never jump + + (substitute J for B above for a set of Jumps -- 1-byte operand (not 2), + always jumps forward up to 255 bytes. In this case, 'address' cannot be + less than the current address, nor greater than the current address + 255) + + Two-address formats (first address is destination, len is decimal 1-256): + ------------------- + + MVC address,address,len Move Characters + CLC address,address,len Compare Logical Characters + ALC address,address,len Add Logical Characters + SLC address,address,len Subtract Logical Characters + ED address,address,len Edit + ITC address,address,len Insert and Test Characters + AZ address,address,len Add Zoned Decimal + SZ address,address,len Subtract Zoned Decimal + + MNN address,address Move Numeric to Numeric + MNZ address,address Move Numeric to Zone + MZZ address,address Move Zone to Zone + MZN address,address Move Zone to Numeric + + I/O Format + ---------- + + In the I/O format, there are always 3 fields: + + da - Device Address 0-15 (decimal) + m - Modifier 0-1 + n - Function 0-7 + + The meaning of these is entirely defined by the device addressed. + + There may be an optional control byte, or an optional address (based on +the type of instruction). + + SNS da,m,n,address Sense I/O + LIO da,m,n,address Load I/O + TIO da,m,n,address Test I/O + + SIO da,m,n,cc Start I/O -- cc is a control byte + + APL da,m,n Advance Program Level + + + + --------------------------------------------- + Here is a handy opcode cross-reference table: + --------------------------------------------- + + | x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF +---+------------------------------------------------------------------ +0x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +1x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +2x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +3x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - + | +4x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +5x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +6x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +7x | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - + | +8x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +9x | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +Ax | - - - - ZAZ - AZ SZ MVX - ED ITC MVC CLC ALC SLC +Bx | SNS LIO - - ST L A - TBN TBF SBN SBF MVI CLI - - + | +Cx | BC TIO LA - - - - - - - - - - - - - +Dx | BC TIO LA - - - - - - - - - - - - - +Ex | BC TIO LA - - - - - - - - - - - - - +Fx | HPL APL JC SIO - - - - - - - - - - - - + + + This routine is the instruction decode routine for System/3. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + program check caused by invalid opcode or qbyte or address or I/O spec + unknown I/O device and STOP_DEV flag set + I/O error in I/O simulator + + 2. Interrupts. + + There are 8 levels of interrupt, each with it's own IAR (program + counter). When an interrupt occurs, execution begins at the + location in the IAR for that level interrupt. The program + must save and restore state. Each device is assigned both a + level and a priority in hardware. Interrupts are reset via + an SIO instruction, when this happens, the program level + IAR resumes control. + + Interrupts are maintained in the global variable int_req, + which is zero if no interrupts are pending, otherwise, the + lower 16 bits represent devices, rightmost bit being device + 0. Each device requesting an interrupt sets its bit on. + + + 3. Non-existent memory. On the System/3, any reference to non-existent + memory (read or write) causes a program check and machine stop. + + 4. Adding I/O devices. These modules must be modified: + + ibms3_defs.h add interrupt request definition + ibms3_cpu.c add IOT mask, PI mask, and routine to dev_table + ibms3_sys.c add pointer to data structures to sim_devices +*/ + +#include "s3_defs.h" + +#define UNIT_V_M15 (UNIT_V_UF) /* Model 15 extensions */ +#define UNIT_M15 (1 << UNIT_V_M15) +#define UNIT_V_DPF (UNIT_V_UF+1) /* Dual Programming */ +#define UNIT_DPF (1 << UNIT_V_DPF) +#define UNIT_V_MSIZE (UNIT_V_UF+3) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +uint8 M[MAXMEMSIZE] = { 0 }; /* memory */ +int32 AAR = 0; /* Operand 1 addr reg */ +int32 BAR = 0; /* Operand 2 addr reg */ +int32 XR1 = 0; /* Index register 1 */ +int32 XR2 = 0; /* Index register 2 */ +int32 PSR = 0; /* Condition Register */ +int32 IAR[10] = { 0 }; /* IAR 0-7 = int level 8=P1 9=P2 */ +int32 ARR[10] = { 0 }; /* ARR 0-7 = int level 8=P1 9=P2 */ +int32 dev_disable = 0; /* interrupt disable mask */ +int32 int_req = 0; /* Interrupt request device bitmap */ +int32 level = 8; /* Current Execution Level*/ +int32 stop_dev = 0; /* stop on ill dev */ +int32 SR = 0; /* Switch Register */ +int32 saved_PC; /* Saved (old) PC) */ +int32 debug_reg = 0; /* set for debug/trace */ +int32 debug_flag = 0; /* 1 when trace.log open */ +FILE *trace; +extern int32 sim_int_char; +extern int32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_boot (int32 unitno, DEVICE *dptr1); +extern int32 pkb (int32 op, int32 m, int32 n, int32 data); +extern int32 crd (int32 op, int32 m, int32 n, int32 data); +extern int32 lpt (int32 op, int32 m, int32 n, int32 data); +extern int32 dsk1 (int32 op, int32 m, int32 n, int32 data); +extern int32 dsk2 (int32 op, int32 m, int32 n, int32 data); +extern int32 cpu (int32 op, int32 m, int32 n, int32 data); +extern t_stat sim_activate (UNIT *uptr, int32 delay); +extern int32 fprint_sym (FILE *of, int32 addr, uint32 *val, + UNIT *uptr, int32 sw); +int32 nulldev (int32 opcode, int32 m, int32 n, int32 data); +int add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); +int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2); +static int32 compare(int32 byte1, int32 byte2, int32 cond); +static int32 condition(int32 qbyte); +static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign); +static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign); +static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count); +static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign); +int32 GetMem(int32 addr); +int32 PutMem(int32 addr, int32 data); + +/* IOT dispatch table */ + +/* System/3 supports only 16 unique device addresses! */ + +struct ndev dev_table[16] = { + { 0, 0, &cpu }, /* Device 0: CPU control */ + { 1, 0, &pkb }, /* Device 1: 5471 console printer/keyboard */ + { 0, 0, &nulldev }, + { 0, 0, &nulldev }, + { 0, 0, &nulldev }, + { 0, 0, &crd }, /* Device 5: 1442 card reader/punch */ + { 0, 0, &nulldev }, /* Device 6: 3410 Tape drives 1 & 2 */ + { 0, 0, &nulldev }, /* Device 7: 3410 Tape drives 3 & 4 */ + { 0, 0, &nulldev }, + { 0, 0, &nulldev }, + { 0, 0, &dsk1 }, /* Device 10: 5444 Disk Drive 1 */ + { 0, 0, &dsk2 }, /* Device 11: 5444 Disk Drive 2 */ + { 0, 0, &nulldev }, /* Device 12: 5448 Disk Drive 1 */ + { 0, 0, &nulldev }, /* DEvice 13: 5448 Disk Drive 2 */ + { 0, 0, &lpt }, /* Device 14: 1403/5203 Printer */ + { 0, 0, &nulldev } /* Device 15: 5424 MFCU */ +}; + +/* Priority assigned to interrupt levels */ + +int32 priority[8] = {8, 7, 5, 4, 3, 6, 2, 1}; + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { HRDATA (IAR, saved_PC, 16), REG_RO }, + { HRDATA (IAR-P1, IAR[8], 16) }, + { HRDATA (IAR-P2, IAR[9], 16) }, + { HRDATA (ARR-P1, ARR[8], 16) }, + { HRDATA (ARR-P2, ARR[9], 16) }, + { HRDATA (AAR, AAR, 16) }, + { HRDATA (BAR, BAR, 16) }, + { HRDATA (XR1, XR1, 16) }, + { HRDATA (XR2, XR2, 16) }, + { HRDATA (PSR, PSR, 16) }, + { HRDATA (SR, SR, 16) }, + { HRDATA (INT, int_req, 16), REG_RO }, + { HRDATA (LEVEL, level, 16) }, + { HRDATA (IAR0, IAR[0], 16) }, + { HRDATA (IAR1, IAR[1], 16) }, + { HRDATA (IAR2, IAR[2], 16) }, + { HRDATA (IAR3, IAR[3], 16) }, + { HRDATA (IAR4, IAR[4], 16) }, + { HRDATA (IAR5, IAR[5], 16) }, + { HRDATA (IAR6, IAR[6], 16) }, + { HRDATA (IAR7, IAR[7], 16) }, + { HRDATA (ARR0, ARR[0], 16) }, + { HRDATA (ARR1, ARR[1], 16) }, + { HRDATA (ARR2, ARR[2], 16) }, + { HRDATA (ARR3, ARR[3], 16) }, + { HRDATA (ARR4, ARR[4], 16) }, + { HRDATA (ARR5, ARR[5], 16) }, + { HRDATA (ARR6, ARR[6], 16) }, + { HRDATA (ARR7, ARR[7], 16) }, + { HRDATA (DISABLE, dev_disable, 16), REG_RO }, + { FLDATA (STOP_DEV, stop_dev, 0) }, + { HRDATA (WRU, sim_int_char, 8) }, + { HRDATA (DEBUG, debug_reg, 16) }, + { NULL } +}; + +MTAB cpu_mod[] = { + { UNIT_M15, UNIT_M15, "M15", "M15", NULL }, + { UNIT_M15, 0, "M10", "M10", NULL }, + { UNIT_DPF, UNIT_DPF, "DPF", "DPF", NULL }, + { UNIT_DPF, 0, "NODPF", "NODPF", NULL }, + { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65535, NULL, "64K", &cpu_set_size }, + { 0 } +}; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 16, 1, 16, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL +}; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +register int32 PC, IR; +int32 i, j, carry, zero, op1, op2; +int32 opcode = 0, qbyte = 0, rbyte = 0; +int32 opaddr, addr1, addr2, dlen1, dlen2, r; +int32 int_savelevel = 8, intpri, intlev, intdev, intmask; +int32 devno, devm, devn; +char display[3][9]; +int32 val [32]; +register t_stat reason; + +/* Restore register state */ + +PC = IAR[level]; /* load local PC */ +reason = 0; + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ +if (sim_interval <= 0) { /* check clock queue */ + if (reason = sim_process_event ()) break; +} + +if (int_req) { /* interrupt? */ + intpri = 16; + for (i = 0; i < 16; i++) { /* Get highest priority device */ + if ((int_req >> i) & 0x01) { + intlev = dev_table[i].level; + if (priority[intlev] < intpri) { + intdev = i; + intpri = priority[intlev]; + } + } + } + intmask = 1 << intdev; /* mask is interrupting dev bit */ + int_req = ~int_req & intmask; /* Turn off int_req for device */ + int_savelevel = level; /* save current level for reset */ + level = dev_table[intdev].level; /* get int level from device */ + PC = IAR[level]; /* Use int level IAR for new PC */ +} /* end interrupt */ + +if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; +} + +/* Machine Instruction Execution Here */ + +if ((debug_reg == 0) && debug_flag == 1) { + fclose(trace); + debug_flag = 0; +} +if (debug_reg) { + if (!debug_flag) { + trace = fopen("trace.log", "w"); + debug_flag = 1; + } +} + +if (debug_reg & 0x01) { + fprintf(trace, "ARR=%04X XR1=%04X XR2=%04X IAR=%04X ", ARR[level], XR1, XR2, PC); + val[0] = GetMem(PC); + val[1] = GetMem(PC+1); + val[2] = GetMem(PC+2); + val[3] = GetMem(PC+3); + val[4] = GetMem(PC+4); + val[5] = GetMem(PC+5); + fprint_sym(trace, PC, (uint32 *) val, &cpu_unit, SWMASK('M')); + fprintf(trace, "\n"); +} + +saved_PC = PC; +opaddr = GetMem(PC) & 0xf0; /* fetch addressing mode */ +opcode = GetMem(PC) & 0x0f; /* fetch opcode */ +PC = (PC + 1) & AMASK; +sim_interval = sim_interval - 1; + +qbyte = GetMem(PC) & 0xff; /* fetch qbyte */ +PC = (PC + 1) & AMASK; + +if (opaddr == 0xf0) { /* Is it command format? */ + rbyte = GetMem(PC) & 0xff; + PC = (PC + 1) & AMASK; + switch (opcode) { + case 0x00: /* HPL: Halt Program Level */ + for (i = 0; i < 3; i++) { + for (j = 0; j < 9; j++) { + display[i][j] = ' '; + } + } + /* First line */ + if (qbyte & 0x04) display[0][2] = '_' ; + if (rbyte & 0x04) display[0][6] = '_' ; + /* Second line */ + if (qbyte & 0x08) display[1][1] = '|' ; + if (rbyte & 0x08) display[1][5] = '|' ; + if (qbyte & 0x10) display[1][2] = '_' ; + if (rbyte & 0x10) display[1][6] = '_' ; + if (qbyte & 0x02) display[1][3] = '|' ; + if (rbyte & 0x02) display[1][7] = '|' ; + /* Third line */ + if (qbyte & 0x20) display[2][1] = '|' ; + if (rbyte & 0x20) display[2][5] = '|' ; + if (qbyte & 0x40) display[2][2] = '_' ; + if (rbyte & 0x40) display[2][6] = '_' ; + if (qbyte & 0x01) display[2][3] = '|' ; + if (rbyte & 0x01) display[2][7] = '|' ; + /* Print display segment array */ + printf("\n\r"); + for (i = 0; i < 3; i++) { + for (j = 0; j < 9; j++) { + printf ("%c", display[i][j]); + } + printf ("\n\r"); + } + reason = STOP_HALT; + break; + case 0x01: /* APL: Advance Program Level */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + op1 = dev_table[devno].routine(4, devm, devn, rbyte); + if (op1 & 0x01) { + if (cpu_unit.flags & UNIT_DPF) { /* Dual Programming? */ + if (level == 8) /* Yes: switch program levels */ + level = 9; + else + level = 8; + PC = IAR[level]; + } else { /* No: Loop on this inst */ + PC = PC - 3; + } + } + reason = (op1 >> 16) & 0xffff; + break; + case 0x02: /* JC: Jump on Condition */ + if (condition(qbyte) == 1) { + PC = (PC + rbyte) & AMASK; + } + break; + case 0x03: /* SIO: Start I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + reason = dev_table[devno].routine(0, devm, devn, rbyte); + if (reason == RESET_INTERRUPT) { + reason = SCPE_OK; + IAR[level] = PC; + level = int_savelevel; + PC = IAR[level]; + } + break; + default: + reason = STOP_INVOP; + break; + } /* switch (opcode) */ + IAR[level] = PC; + continue; +} + +/* Not command format: fetch the addresses */ + +addr1 = (opaddr >> 6) & 3; +addr2 = (opaddr >> 4) & 3; + +switch (addr1) { + case 0: + BAR = GetMem(PC) << 8; + PC = (PC + 1) & AMASK; + BAR |=GetMem(PC); + PC = (PC + 1) & AMASK; + break; + case 1: + BAR = GetMem(PC); + BAR = (BAR + XR1) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 2: + BAR = GetMem(PC); + BAR = (BAR + XR2) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 3: + break; + default: + break; +} /* switch (addr1) */ + +switch (addr2) { + case 0: + AAR = GetMem(PC) << 8; + PC = (PC + 1) & AMASK; + AAR |= GetMem(PC); + PC = (PC + 1) & AMASK; + break; + case 1: + AAR = GetMem(PC); + AAR = (AAR + XR1) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 2: + AAR = GetMem(PC); + AAR = (AAR + XR2) & AMASK; + PC = (PC + 1) & AMASK; + break; + case 3: + break; + default: + break; +} /* switch (addr1) */ + +switch (opaddr) { + case 0x00: + case 0x10: + case 0x20: + case 0x40: + case 0x50: + case 0x60: + case 0x80: + case 0x90: + case 0xa0: + switch (opcode) { + case 4: /* ZAZ: Zero and Add Zoned */ + dlen2 = qbyte & 0x0f; + dlen1 = (qbyte >> 4) & 0xf; + dlen1 += dlen2; + op1 = BAR; + for (i = 0; i < (dlen1+1); i++) { + PutMem(op1, 0xf0); + op1--; + } + r = add_zoned(BAR, dlen1+1, AAR, dlen2+1); + PSR &= 0xF8; /* HJS mod */ + switch (r) { + case 0: + PSR |= 0x01; + break; + case 1: + PSR |= 0x02; + break; + case 2: + PSR |= 0x04; + break; + default: + break; + } + break; + case 6: /* AZ: Add Zoned */ + dlen2 = qbyte & 0x0f; + dlen1 = (qbyte >> 4) & 0xf; + dlen1 += dlen2; + r = add_zoned(BAR, dlen1+1, AAR, dlen2+1); + PSR &= 0xF0; + switch (r) { + case 0: + PSR |= 0x01; + break; + case 1: + PSR |= 0x02; + break; + case 2: + PSR |= 0x04; + break; + case 3: + PSR |= 0x08; + break; + default: + break; + } + break; + case 7: /* SZ: Subtract Zoned */ + dlen2 = qbyte & 0x0f; + dlen1 = (qbyte >> 4) & 0xf; + dlen1 += dlen2; + r = subtract_zoned(BAR, dlen1+1, AAR, dlen2+1); + PSR &= 0xF0; + switch (r) { + case 0: + PSR |= 0x01; + break; + case 1: + PSR |= 0x02; + break; + case 2: + PSR |= 0x04; + break; + case 3: + PSR |= 0x08; + break; + default: + break; + } + break; + case 8: /* MVX: Move Hex */ + op1 = GetMem(BAR); + op2 = GetMem(AAR); + switch (qbyte) { + case 0: /* Zone to zone */ + op1 = (op1 & 0x0F) | (op2 & 0xF0); + break; + case 1: /* Numeric to zone */ + op1 = (op1 & 0x0F) | (op2 << 4); + break; + case 2: /* Zone to numeric */ + op1 = (op1 & 0xF0) | (op2 >> 4); + break; + case 3: /* Numeric to numeric */ + op1 = (op1 & 0xF0) | (op2 & 0x0F); + break; + default: + reason = STOP_INVQ; + break; + } + PutMem(BAR, op1); + break; + case 0xa: /* ED: Edit */ + zero = 1; + PSR &= 0xF8; + IR = GetMem(AAR); + if ((IR & 0xf0) != 0xF0) + PSR |= 0x02; + else + PSR |= 0x04; + while (qbyte > -1) { + op2 = GetMem(AAR); + op1 = GetMem(BAR); + if (op1 == 0x20) { + op2 |= 0xf0; + PutMem(BAR, op2); + AAR--; + if (op2 != 0xF0) zero = 0; + } + BAR--; + qbyte--; + } + if (zero) + PSR |= 0x01; + break; + case 0xb: /* ITC: Insert and Test Chars */ + op2 = GetMem(AAR); + while (qbyte > -1) { + op1 = GetMem(BAR); + if (op1 >= 0xF1 && op1 <= 0xF9) + break; + PutMem(BAR, op2); + BAR++; + qbyte--; + } + ARR[level] = BAR; + break; + case 0xc: /* MVC: Move Characters */ + while (qbyte > -1) { + PutMem(BAR, GetMem(AAR)); + BAR--; + AAR--; + qbyte--; + } + break; + case 0xd: /* CLC: Compare Characters */ + PSR &= 0xF8; + i = BAR = BAR - qbyte; + j = AAR = AAR - qbyte; + while (qbyte > -1) { + if (GetMem(i) > GetMem(j)) { + PSR |= 0x04; + break; + } + if (GetMem(i) < GetMem(j)) { + PSR |= 0x02; + break; + } + i++; + j++; + qbyte--; + } + if (qbyte == -1) + PSR |= 0x01; + break; + case 0xe: /* ALC: Add Logical Characters */ + carry = 0; + zero = 1; + while (qbyte > -1) { + IR = GetMem(BAR) + GetMem(AAR) + carry; + if (IR & 0x100) + carry = 1; + else + carry = 0; + if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */ + PutMem(BAR,(IR & 0xFF)); + BAR--; + AAR--; + qbyte--; + } + PSR &= 0xD8; + if (zero) + PSR |= 0x01; /* Equal */ + if (!zero && !carry) + PSR |= 0x02; /* Low */ + if (!zero && carry) + PSR |= 0x04; /* High */ + if (carry) + PSR |= 0x20; /* Overflow */ + break; + case 0xf: /* SLC: Subtract Logical Characters */ + carry = 1; + zero = 1; + while (qbyte > -1) { + IR = GetMem(BAR) + (0xFF - GetMem(AAR)) + carry; + if (IR & 0x100) + carry = 1; + else + carry = 0; + if ((IR & 0xFF) != 0) zero = 0; /* HJS mod */ + PutMem(BAR,(IR & 0xFF)); + BAR--; + AAR--; + qbyte--; + } + PSR &= 0xF8; + if (zero) + PSR |= 0x01; /* Equal */ + if (!zero && !carry) + PSR |= 0x02; /* Low */ + if (!zero && carry) + PSR |= 0x04; /* High */ + break; + default: + reason = STOP_INVOP; + break; + } + IAR[level] = PC; + continue; + break; + case 0x30: + case 0x70: + case 0xb0: + switch (opcode) { + case 0: /* SNS: Sense I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + i = dev_table[devno].routine(3, devm, devn, rbyte); + PutMem(BAR, i & 0xff); + BAR--; + PutMem(BAR, (i >> 8) & 0xff); + reason = (i >> 16) & 0xffff; + break; + case 1: /* LIO: Load I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + op1 = GetMem(BAR); + BAR--; + op1 |= (GetMem(BAR) << 8) & 0xff00; + reason = dev_table[devno].routine(1, devm, devn, op1); + break; + case 4: /* ST: Store Register */ + switch (qbyte) { + case 0x01: + PutMem(BAR, XR1 & 0xff); + BAR--; + PutMem(BAR, (XR1 >> 8) & 0xff); + break; + case 0x02: + PutMem(BAR, XR2 & 0xff); + BAR--; + PutMem(BAR, (XR2 >> 8) & 0xff); + break; + case 0x04: + PutMem(BAR, PSR & 0xFF); + BAR--; + PutMem(BAR, 0); /* LCRR, not imp. */ + break; + case 0x08: + PutMem(BAR, ARR[level] & 0xff); + BAR--; + PutMem(BAR, (ARR[level] >> 8) & 0xff); + break; + case 0x10: + PutMem(BAR, IAR[level] & 0xff); + BAR--; + PutMem(BAR, (IAR[level] >> 8) & 0xff); + break; + case 0x20: + PutMem(BAR, IAR[8] & 0xff); + BAR--; + PutMem(BAR, (IAR[8] >> 8) & 0xff); + break; + case 0x40: + PutMem(BAR, IAR[9] & 0xff); + BAR--; + PutMem(BAR, (IAR[9] >> 8) & 0xff); + break; + case 0x80: + PutMem(BAR, IAR[0] & 0xff); + BAR--; + PutMem(BAR, (IAR[0] >> 8) & 0xff); + break; + case 0x81: + PutMem(BAR, IAR[7] & 0xff); + BAR--; + PutMem(BAR, (IAR[7] >> 8) & 0xff); + break; + case 0x82: + PutMem(BAR, IAR[6] & 0xff); + BAR--; + PutMem(BAR, (IAR[6] >> 8) & 0xff); + break; + case 0x84: + PutMem(BAR, IAR[5] & 0xff); + BAR--; + PutMem(BAR, (IAR[5] >> 8) & 0xff); + break; + case 0x88: + PutMem(BAR, IAR[4] & 0xff); + BAR--; + PutMem(BAR, (IAR[4] >> 8) & 0xff); + break; + case 0x90: + PutMem(BAR, IAR[3] & 0xff); + BAR--; + PutMem(BAR, (IAR[3] >> 8) & 0xff); + break; + case 0xA0: + PutMem(BAR, IAR[2] & 0xff); + BAR--; + PutMem(BAR, (IAR[2] >> 8) & 0xff); + break; + case 0xC0: + PutMem(BAR, IAR[1] & 0xff); + BAR--; + PutMem(BAR, (IAR[1] >> 8) & 0xff); + break; + default: + reason = STOP_INVQ; + break; + } + break; + case 5: /* L: Load Register */ + switch (qbyte) { + case 0x01: + XR1 = GetMem(BAR) & 0xff; + BAR--; + XR1 |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x02: + XR2 = GetMem(BAR) & 0xff; + BAR--; + XR2 |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x04: + PSR = GetMem(BAR) & 0xff; + BAR--; + break; + case 0x08: + ARR[level] = GetMem(BAR) & 0xff; + BAR--; + ARR[level] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x10: + IAR[level] = GetMem(BAR) & 0xff; + BAR--; + IAR[level] |= (GetMem(BAR) << 8) & 0xff00; + PC = IAR[level]; + break; + case 0x20: + IAR[8] = GetMem(BAR) & 0xff; + BAR--; + IAR[8] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x40: + IAR[9] = GetMem(BAR) & 0xff; + BAR--; + IAR[9] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x80: + IAR[0] = GetMem(BAR) & 0xff; + BAR--; + IAR[0] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x81: + IAR[7] = GetMem(BAR) & 0xff; + BAR--; + IAR[7] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x82: + IAR[6] = GetMem(BAR) & 0xff; + BAR--; + IAR[6] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x84: + IAR[5] = GetMem(BAR) & 0xff; + BAR--; + IAR[5] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x88: + IAR[4] = GetMem(BAR) & 0xff; + BAR--; + IAR[4] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0x90: + IAR[3] = GetMem(BAR) & 0xff; + BAR--; + IAR[3] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0xA0: + IAR[2] = GetMem(BAR) & 0xff; + BAR--; + IAR[2] |= (GetMem(BAR) << 8) & 0xff00; + break; + case 0xC0: + IAR[1] = GetMem(BAR) & 0xff; + BAR--; + IAR[1] |= (GetMem(BAR) << 8) & 0xff00; + break; + default: + reason = STOP_INVQ; + break; + } + break; + case 6: /* A: Add to Register */ + IR = GetMem(BAR) & 0x00ff; + BAR--; + IR |= (GetMem(BAR) << 8) & 0xff00; + switch (qbyte) { + case 0x01: + IR += XR1; + XR1 = IR & AMASK; + break; + case 0x02: + IR += XR2; + XR2 = IR & AMASK; + break; + case 0x04: + IR += PSR; + PSR = IR & AMASK; + break; + case 0x08: + IR += ARR[level]; + ARR[level] = IR & AMASK; + break; + case 0x10: + IR += IAR[level]; + IAR[level] = IR & AMASK; + break; + case 0x20: + IR += IAR[8]; + IAR[8] = IR & AMASK; + break; + case 0x40: + IR += IAR[9]; + IAR[9] = IR & AMASK; + break; + case 0x80: + IR += IAR[0]; + IAR[0] = IR & AMASK; + break; + case 0x81: + IR += IAR[7]; + IAR[7] = IR & AMASK; + break; + case 0x82: + IR += IAR[6]; + IAR[6] = IR & AMASK; + break; + case 0x84: + IR += IAR[5]; + IAR[5] = IR & AMASK; + break; + case 0x88: + IR += IAR[4]; + IAR[4] = IR & AMASK; + break; + case 0x90: + IR += IAR[3]; + IAR[3] = IR & AMASK; + break; + case 0xA0: + IR += IAR[2]; + IAR[2] = IR & AMASK; + break; + case 0xC0: + IR += IAR[1]; + IAR[1] = IR & AMASK; + break; + default: + reason = STOP_INVQ; + break; + } + PSR &= 0xD8; + if ((IR & 0xffff) == 0) + PSR |= 0x01; /* Zero */ + if ((IR & 0xffff) != 0 && !(IR & 0x10000)) + PSR |= 0x02; /* Low */ + if ((IR & 0xffff) != 0 && (IR & 0x10000)) + PSR |= 0x04; /* High */ + if ((IR & 0x10000)) + PSR |= 0x20; /* Bin overflow */ + break; + case 8: /* TBN: Test Bits On */ + IR = GetMem(BAR); + PSR &= 0xFF; + if ((IR & qbyte) != qbyte) + PSR |= 0x10; + break; + case 9: /* TBF: Test Bits Off */ + IR = GetMem(BAR); + PSR &= 0xFF; + if ((IR & qbyte)) + PSR |= 0x10; + break; + case 0xa: /* SBN: Set Bits On */ + IR = GetMem(BAR); + IR |= qbyte; + PutMem(BAR, IR); + break; + case 0xb: /* SBF: Set Bits Off */ + IR = GetMem(BAR); + IR &= ~qbyte; + PutMem(BAR, IR); + break; + case 0xc: /* MVI: Move Immediate */ + PutMem(BAR, qbyte); + break; + case 0xd: /* CLI: Compare Immediate */ + PSR = compare(GetMem(BAR), qbyte, PSR); + break; + default: + reason = STOP_INVOP; + break; + } + IAR[level] = PC; + continue; + break; + case 0xc0: + case 0xd0: + case 0xe0: + switch (opcode) { + case 0: /* BC: Branch on Condition */ + ARR[level] = AAR & AMASK; + if (condition(qbyte) == 1) { + IR = ARR[level]; + ARR[level] = PC & AMASK; + PC = IR; + } + break; + case 1: /* TIO: Test I/O */ + devno = (qbyte >> 4) & 0x0f; + devm = (qbyte >> 3) & 0x01; + devn = qbyte & 0x07; + op1 = dev_table[devno].routine(2, devm, devn, rbyte); + if (op1 & 0x01) { + ARR[level] = AAR & AMASK; + IR = ARR[level]; + ARR[level] = PC & AMASK; + PC = IR; + } + reason = (op1 >> 16) & 0xffff; + break; + case 2: /* LA: Load Address */ + switch (qbyte) { + case 1: + XR1 = AAR; + break; + case 2: + XR2 = AAR; + break; + default: + reason = STOP_INVQ; + break; + } + break; + default: + reason = STOP_INVOP; + break; + } /* switch (opcode) */ + IAR[level] = PC; + continue; + + default: + reason = STOP_INVOP; + break; +} /* switch (opaddr) */ + +} /* end while (reason == 0) */ + +/* Simulation halted */ + +saved_PC = PC; +return reason; +} + +/* On models 4-12, these memory functions could be inline, but + on a model 15 with ATU address mapping must be performed so + they are in functions here for future expansion. +*/ + +/* Fetch a byte from memory */ + +int32 GetMem(int32 addr) +{ + return M[addr] & 0xff; +} + +/* Place a byte in memory */ + +int32 PutMem(int32 addr, int32 data) +{ + M[addr] = data & 0xff; + return 0; +} + +/* Check the condition register against the qbyte and return 1 if true */ + +int32 condition(int32 qbyte) +{ + int32 r = 0, t, q; + t = (qbyte & 0xf0) >> 4; + q = qbyte & 0x0f; + if (qbyte & 0x80) { /* True if any condition tested = 1*/ + if (((qbyte & 0x3f) & PSR) != 0) r = 1; + } else { /* True if all conditions tested = 0 */ + if (((qbyte & 0x3f) & PSR) == 0) r = 1; + } + /* these bits reset by a test */ + if (qbyte & 0x10) + PSR &= 0xEF; /* Reset test false if used */ + if (qbyte & 0x08) + PSR &= 0xF7; /* Reset decimal overflow if tested */ + if (qbyte == 0x00) + r = 1; /* unconditional branch */ + if (qbyte == 0x80) + r = 0; /* force no branch */ + if (t >=0 && t < 8 && (q == 7 || q == 0xf)) + r = 0; /* no-op */ + if (t > 7 && t < 0x10 && (q == 7 || q == 0xf)) + r = 1; /* Force branch */ +return (r); +} +/* Given operand 1 and operand 2, compares the two and returns + the System/3 condition register bits appropriately, given the + condition register initial state in parameter 3 +*/ + +int32 compare(int32 byte1, int32 byte2, int32 cond) +{ + int32 r; + + r = cond & 0xF8; /* mask off all but unaffected bits 2,3,4 */ + if (byte1 == byte2) + r |= 0x01; /* set equal bit */ + if (byte1 < byte2) + r |= 0x02; /* set less-than bit */ + if (byte1 > byte2) + r |= 0x04; /* set greater than bit */ + return r; +} + +/*-------------------------------------------------------------------*/ +/* Add two zoned decimal operands */ +/* */ +/* Input: */ +/* addr1 Logical address of packed decimal storage operand 1 */ +/* len1 Length minus one of storage operand 1 (range 0-15) */ +/* addr2 Logical address of packed decimal storage operand 2 */ +/* len2 Length minus one of storage operand 2 (range 0-15) */ +/* Output: */ +/* The return value is the condition code: */ +/* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */ +/* */ +/* A program check may be generated if either logical address */ +/* causes an addressing, translation, or fetch protection */ +/* exception, or if either operand causes a data exception */ +/* because of invalid decimal digits or sign, or if the */ +/* first operand is store protected. Depending on the PSW */ +/* program mask, decimal overflow may cause a program check. */ +/*-------------------------------------------------------------------*/ +int32 add_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2) +{ +int cc; /* Condition code */ +uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */ +uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */ +uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */ +int count1, count2, count3; /* Significant digit counters*/ +int sign1, sign2, sign3; /* Sign of operands & result */ + + /* Load operands into work areas */ + load_decimal (addr1, len1, dec1, &count1, &sign1); + load_decimal (addr2, len2, dec2, &count2, &sign2); + + /* Add or subtract operand values */ + if (count2 == 0) + { + /* If second operand is zero then result is first operand */ + memcpy (dec3, dec1, MAX_DECIMAL_DIGITS); + count3 = count1; + sign3 = sign1; + } + else if (count1 == 0) + { + /* If first operand is zero then result is second operand */ + memcpy (dec3, dec2, MAX_DECIMAL_DIGITS); + count3 = count2; + sign3 = sign2; + } + else if (sign1 == sign2) + { + /* If signs are equal then add operands */ + add_decimal (dec1, dec2, dec3, &count3); + sign3 = sign1; + } + else + { + /* If signs are opposite then subtract operands */ + subtract_decimal (dec1, dec2, dec3, &count3, &sign3); + if (sign1 < 0) sign3 = -sign3; + } + + /* Set condition code */ + cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2; + + /* Overflow if result exceeds first operand length */ + if (count3 > len1) + cc = 3; + + /* Set positive sign if result is zero */ + if (count3 == 0) + sign3 = 1; + + /* Store result into first operand location */ + store_decimal (addr1, len1, dec3, sign3); + + /* Return condition code */ + return cc; + +} /* end function add_packed */ + +/*-------------------------------------------------------------------*/ +/* Subtract two zoned decimal operands */ +/* */ +/* Input: */ +/* addr1 Logical address of packed decimal storage operand 1 */ +/* len1 Length minus one of storage operand 1 (range 0-15) */ +/* addr2 Logical address of packed decimal storage operand 2 */ +/* len2 Length minus one of storage operand 2 (range 0-15) */ +/* Output: */ +/* The return value is the condition code: */ +/* 0=result zero, 1=result -ve, 2=result +ve, 3=overflow */ +/* */ +/* A program check may be generated if either logical address */ +/* causes an addressing, translation, or fetch protection */ +/* exception, or if either operand causes a data exception */ +/* because of invalid decimal digits or sign, or if the */ +/* first operand is store protected. Depending on the PSW */ +/* program mask, decimal overflow may cause a program check. */ +/*-------------------------------------------------------------------*/ +int32 subtract_zoned (int32 addr1, int32 len1, int32 addr2, int32 len2) +{ +int cc; /* Condition code */ +uint8 dec1[MAX_DECIMAL_DIGITS]; /* Work area for operand 1 */ +uint8 dec2[MAX_DECIMAL_DIGITS]; /* Work area for operand 2 */ +uint8 dec3[MAX_DECIMAL_DIGITS]; /* Work area for result */ +int count1, count2, count3; /* Significant digit counters*/ +int sign1, sign2, sign3; /* Sign of operands & result */ + + /* Load operands into work areas */ + load_decimal (addr1, len1, dec1, &count1, &sign1); + load_decimal (addr2, len2, dec2, &count2, &sign2); + + /* Add or subtract operand values */ + if (count2 == 0) + { + /* If second operand is zero then result is first operand */ + memcpy (dec3, dec1, MAX_DECIMAL_DIGITS); + count3 = count1; + sign3 = sign1; + } + else if (count1 == 0) + { + /* If first operand is zero then result is -second operand */ + memcpy (dec3, dec2, MAX_DECIMAL_DIGITS); + count3 = count2; + sign3 = -sign2; + } + else if (sign1 != sign2) + { + /* If signs are opposite then add operands */ + add_decimal (dec1, dec2, dec3, &count3); + sign3 = sign1; + } + else + { + /* If signs are equal then subtract operands */ + subtract_decimal (dec1, dec2, dec3, &count3, &sign3); + if (sign1 < 0) sign3 = -sign3; + } + + /* Set condition code */ + cc = (count3 == 0) ? 0 : (sign3 < 1) ? 1 : 2; + + /* Overflow if result exceeds first operand length */ + if (count3 > len1) + cc = 3; + + /* Set positive sign if result is zero */ + if (count3 == 0) + sign3 = 1; + + /* Store result into first operand location */ + store_decimal (addr1, len1, dec3, sign3); + + /* Return condition code */ + return cc; + +} /* end function subtract_packed */ + + +/*-------------------------------------------------------------------*/ +/* Add two decimal byte strings as unsigned decimal numbers */ +/* */ +/* Input: */ +/* dec1 A 31-byte area containing the decimal digits of */ +/* the first operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* dec2 A 31-byte area containing the decimal digits of */ +/* the second operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* Output: */ +/* result Points to a 31-byte area to contain the result */ +/* digits. One decimal digit is placed in the low-order */ +/* 4 bits of each byte. */ +/* count Points to an integer to receive the number of */ +/* digits in the result excluding leading zeroes. */ +/* This field is set to zero if the result is all zero, */ +/* or to MAX_DECIMAL_DIGITS+1 if overflow occurred. */ +/*-------------------------------------------------------------------*/ +static void add_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int32 *count) +{ +int d; /* Decimal digit */ +int i; /* Array subscript */ +int n = 0; /* Significant digit counter */ +int carry = 0; /* Carry indicator */ + + /* Add digits from right to left */ + for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--) + { + /* Add digits from first and second operands */ + d = dec1[i] + dec2[i] + carry; + + /* Check for carry into next digit */ + if (d > 9) { + d -= 10; + carry = 1; + } else { + carry = 0; + } + + /* Check for significant digit */ + if (d != 0) + n = MAX_DECIMAL_DIGITS - i; + + /* Store digit in result */ + result[i] = d; + + } /* end for */ + + /* Check for carry out of leftmost digit */ + if (carry) + n = MAX_DECIMAL_DIGITS + 1; + + /* Return significant digit counter */ + *count = n; + +} /* end function add_decimal */ + +/*-------------------------------------------------------------------*/ +/* Subtract two decimal byte strings as unsigned decimal numbers */ +/* */ +/* Input: */ +/* dec1 A 31-byte area containing the decimal digits of */ +/* the first operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* dec2 A 31-byte area containing the decimal digits of */ +/* the second operand. Each byte contains one decimal */ +/* digit in the low-order 4 bits of the byte. */ +/* Output: */ +/* result Points to a 31-byte area to contain the result */ +/* digits. One decimal digit is placed in the low-order */ +/* 4 bits of each byte. */ +/* count Points to an integer to receive the number of */ +/* digits in the result excluding leading zeroes. */ +/* This field is set to zero if the result is all zero. */ +/* sign -1 if the result is negative (operand2 > operand1) */ +/* +1 if the result is positive (operand2 <= operand1) */ +/*-------------------------------------------------------------------*/ +static void subtract_decimal (uint8 *dec1, uint8 *dec2, uint8 *result, int *count, int *sign) +{ +int d; /* Decimal digit */ +int i; /* Array subscript */ +int n = 0; /* Significant digit counter */ +int borrow = 0; /* Borrow indicator */ +int rc; /* Return code */ +uint8 *higher; /* -> Higher value operand */ +uint8 *lower; /* -> Lower value operand */ + + /* Compare digits to find which operand has higher numeric value */ + rc = memcmp (dec1, dec2, MAX_DECIMAL_DIGITS); + + /* Return positive zero result if both operands are equal */ + if (rc == 0) { + memset (result, 0, MAX_DECIMAL_DIGITS); + *count = 0; + *sign = +1; + return; + } + + /* Point to higher and lower value operands and set sign */ + if (rc > 0) { + higher = dec1; + lower = dec2; + *sign = +1; + } else { + lower = dec1; + higher = dec2; + *sign = -1; + } + + /* Subtract digits from right to left */ + for (i = MAX_DECIMAL_DIGITS - 1; i >= 0; i--) + { + /* Subtract lower operand digit from higher operand digit */ + d = higher[i] - lower[i] - borrow; + + /* Check for borrow from next digit */ + if (d < 0) { + d += 10; + borrow = 1; + } else { + borrow = 0; + } + + /* Check for significant digit */ + if (d != 0) + n = MAX_DECIMAL_DIGITS - i; + + /* Store digit in result */ + result[i] = d; + + } /* end for */ + + /* Return significant digit counter */ + *count = n; + +} /* end function subtract_decimal */ + +/*-------------------------------------------------------------------*/ +/* Load a zoned decimal storage operand into a decimal byte string */ +/* */ +/* Input: */ +/* addr Logical address of zoned decimal storage operand */ +/* len Length minus one of storage operand (range 0-15) */ +/* Output: */ +/* result Points to a 31-byte area into which the decimal */ +/* digits are loaded. One decimal digit is loaded */ +/* into the low-order 4 bits of each byte, and the */ +/* result is padded to the left with high-order zeroes */ +/* if the storage operand contains less than 31 digits. */ +/* count Points to an integer to receive the number of */ +/* digits in the result excluding leading zeroes. */ +/* This field is set to zero if the result is all zero. */ +/* sign Points to an integer which will be set to -1 if a */ +/* negative sign was loaded from the operand, or +1 if */ +/* a positive sign was loaded from the operand. */ +/* */ +/* A program check may be generated if the logical address */ +/* causes an addressing, translation, or fetch protection */ +/* exception, or if the operand causes a data exception */ +/* because of invalid decimal digits or sign. */ +/*-------------------------------------------------------------------*/ +static void load_decimal (int32 addr, int32 len, uint8 *result, int32 *count, int32 *sign) +{ +int h; /* Hexadecimal digit */ +int i, j; /* Array subscripts */ +int n; /* Significant digit counter */ + + if ((GetMem(addr) & 0xf0) == 0xD0) + *sign = -1; + else + *sign = 1; + j = len; + for (i = MAX_DECIMAL_DIGITS; i > -1; i--) { + if (j) { + h = GetMem(addr) & 0x0f; + addr--; + j--; + } else { + h = 0; + } + result [i-1] = h; + if (h > 0) n = i; + } + *count = 32 - n; + +} /* end function load_decimal */ + +/*-------------------------------------------------------------------*/ +/* Store decimal byte string into packed decimal storage operand */ +/* */ +/* Input: */ +/* addr Logical address of packed decimal storage operand */ +/* len Length minus one of storage operand (range 0-15) */ +/* dec A 31-byte area containing the decimal digits to be */ +/* stored. Each byte contains one decimal digit in */ +/* the low-order 4 bits of the byte. */ +/* sign -1 if a negative sign is to be stored, or +1 if a */ +/* positive sign is to be stored. */ +/* */ +/* A program check may be generated if the logical address */ +/* causes an addressing, translation, or protection exception. */ +/*-------------------------------------------------------------------*/ +static void store_decimal (int32 addr, int32 len, uint8 *dec, int sign) +{ +int i, j, a; /* Array subscripts */ + + j = len; + a = addr; + for (i = MAX_DECIMAL_DIGITS; i > -1; i--) { + if (j) { + PutMem(a, (dec[i-1] | 0xf0)); + a--; + j--; + } else { + break; + } + } + if (sign == -1) { + PutMem(addr, (GetMem(addr) & 0x0f)); + PutMem(addr, (GetMem(addr) | 0xf0)); + } + +} /* end function store_decimal */ + +/* CPU Device Control */ + +int32 cpu (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata = 0; + + switch (op) { + case 0x00: /* Start IO */ + return SCPE_OK; + case 0x01: /* LIO */ + return SCPE_OK; + case 0x02: /* TIO */ + break; + case 0x03: /* SNS */ + /* SNS CPU gets the data switches */ + iodata = SR; + break; + case 0x04: /* APL */ + break; + default: + break; + } + return ((SCPE_OK << 16) | iodata); +} + + + +/* Null device */ + +int32 nulldev (int32 opcode, int32 m, int32 n, int32 data) +{ +if (opcode == 1) + return SCPE_OK; /* Ok to LIO unconfigured devices? */ +return STOP_INVDEV; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +int_req = 0; +level = 8; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[addr] & 0xff; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +if (addr >= MEMSIZE) return SCPE_NXM; +M[addr] = val & 0xff; +return SCPE_OK; +} + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 07777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ +level = 8; +IAR[8] = 0; +return SCPE_OK; +} diff --git a/S3/s3_defs.h b/S3/s3_defs.h new file mode 100644 index 0000000..b218e50 --- /dev/null +++ b/S3/s3_defs.h @@ -0,0 +1,94 @@ +/* s3_defs.h: IBM System/3 simulator definitions + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. +*/ + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_RSRV 1 /* must be 1 */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_INVOP 4 /* program check - invalid op */ +#define STOP_INVQ 5 /* Prog check - invalid Q */ +#define STOP_INVADDR 6 /* Prog check - invalid addr */ +#define STOP_INVDEV 7 /* Prog check - invalid dev cmd */ +#define STOP_NOCD 8 /* ATTN card reader */ +#define RESET_INTERRUPT 77 /* special return from SIO */ + +/* Memory */ + +#define MAXMEMSIZE 65536 /* max memory size */ +#define AMASK (MAXMEMSIZE - 1) /* logical addr mask */ +#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ + +#define MAX_DECIMAL_DIGITS 31 /* max size of a decimal number */ +#define CDR_WIDTH 80 /* Max card size */ +#define CDP_WIDTH 80 /* Punch width */ +#define LPT_WIDTH 132 +#define CCT_LNT 132 + +#define DSK_SECTSIZE 256 /* Sector length */ +#define DSK_CYLSIZE 256*48 /* Cylinder length */ + +/* I/O structure + + The I/O structure is tied together by dev_table, indexed by + the device number. Each entry in dev_table consists of + + level Interrupt level for device (0-7) + priority Priority for device (1-8) + routine IOT action routine +*/ + +struct ndev { + int32 level; /* interrupt level */ + int32 pri; /* Device priority */ + int32 (*routine)(); /* dispatch routine */ +}; + +/* Structure to define operation codes */ + +struct opdef { + char op[6]; /* Mnemonic for op */ + int32 opmask; /* Bits set on in opcode */ + int32 q; /* Qbyte */ + int32 form; /* Forms are: + 0 - 1-byte hex operand + 1 - 1-byte register addr, A-Addr + 2 - A-addr,B-addr,Qbyte + 3 - A-addr,Qbyte + 4 - da,m,n + 5 - da,m,n,cc + 6 - da,m,n,A-addr + 7 - 1-address implict Q + 8 - 2-address implict Q */ + int32 group; /* Group Code: + 0 - Command Format (0xFx) + 1 - 1-address A (0xx) + 2 - 2-address (0x<0,1,2,4,5,6,8,9,A>x) + 3 - 1-address B (0x<3,7,B>x) */ +}; diff --git a/S3/s3_disk.c b/S3/s3_disk.c new file mode 100644 index 0000000..d3588cf --- /dev/null +++ b/S3/s3_disk.c @@ -0,0 +1,792 @@ +/* s3_disk.c: IBM 5444 Disk Drives + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + r1 Removeable disk 1 + f1 Fixed disk 1 + r2 Removeable disk 2 + f2 Fixed disk 2 + + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher +*/ + +#include "s3_defs.h" +#include + +extern uint8 M[]; +extern int32 IAR[], level; +extern FILE *trace; +extern int32 debug_reg; +char dbuf[DSK_SECTSIZE]; /* Disk buffer */ +int32 dsk (int32 disk, int32 op, int32 m, int32 n, int32 data); +int32 read_sector(UNIT *uptr, char *dbuf, int32 sect); +int32 write_sector(UNIT *uptr, char *dbuf, int32 sect); +t_stat r1_svc (UNIT *uptr); +t_stat r1_boot (int32 unitno, DEVICE *dptr); +t_stat r1_attach (UNIT *uptr, char *cptr); +t_stat r1_reset (DEVICE *dptr); +t_stat f1_svc (UNIT *uptr); +t_stat f1_boot (int32 unitno, DEVICE *dptr); +t_stat f1_attach (UNIT *uptr, char *cptr); +t_stat f1_reset (DEVICE *dptr); +t_stat r2_svc (UNIT *uptr); +t_stat r2_boot (int32 unitno, DEVICE *dptr); +t_stat r2_attach (UNIT *uptr, char *cptr); +t_stat r2_reset (DEVICE *dptr); +t_stat f2_svc (UNIT *uptr); +t_stat f2_boot (int32 unitno, DEVICE *dptr); +t_stat f2_attach (UNIT *uptr, char *cptr); +t_stat f2_reset (DEVICE *dptr); +extern int32 GetMem(int32 addr); +extern int32 PutMem(int32 addr, int32 data); + +char opstr[5][5] = { "SIO", "LIO", "TIO", "SNS", "APL" }; + +int32 DDAR[2]; /* Data address register */ +int32 DCAR[2]; /* Disk Control Address Register */ +int32 diskerr[2] = { 0, 0 }; /* Error status */ +int32 notrdy[2] = { 0, 0 }; /* Not ready error */ +int32 seekbusy[2] = { 0, 0 }; /* Drive busy flags */ +int32 seekhead[2] = { 0, 0 }; /* Disk head 0,1 */ +int32 found[2] = { 0, 0 }; /* Scan found bit */ +int32 RIDsect[2] = { 0, 0 }; /* for Read ID */ + +/* Disk data structures + + xy_dev CDR descriptor + xy_unit CDR unit descriptor + xy_reg CDR register list + + x = F or R + y = 1 or 2 +*/ + +UNIT r1_unit = { UDATA (&r1_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG r1_reg[] = { + { FLDATA (NOTRDY, notrdy[0], 0) }, + { FLDATA (SEEK, seekbusy[0], 0) }, + { HRDATA (DAR, DDAR[0], 16) }, + { HRDATA (CAR, DCAR[0], 16) }, + { HRDATA (ERR, diskerr[0], 16) }, + { DRDATA (CYL, r1_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[0], 8) }, + { DRDATA (POS, r1_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, r1_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE r1_dev = { + "R1", &r1_unit, r1_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &r1_reset, + &r1_boot, &r1_attach, NULL +}; + +UNIT f1_unit = { UDATA (&f1_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG f1_reg[] = { + { FLDATA (NOTRDY, notrdy[0], 0) }, + { FLDATA (SEEK, seekbusy[0], 0) }, + { HRDATA (DAR, DDAR[0], 16) }, + { HRDATA (CAR, DCAR[0], 16) }, + { HRDATA (ERR, diskerr[0], 16) }, + { DRDATA (CYL, f1_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[0], 8) }, + { DRDATA (POS, f1_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, f1_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE f1_dev = { + "F1", &f1_unit, f1_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &f1_reset, + &f1_boot, &f1_attach, NULL +}; + +UNIT r2_unit = { UDATA (&r2_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG r2_reg[] = { + { FLDATA (NOTRDY, notrdy[1], 0) }, + { FLDATA (SEEK, seekbusy[1], 0) }, + { HRDATA (DAR, DDAR[1], 16) }, + { HRDATA (CAR, DCAR[1], 16) }, + { HRDATA (ERR, diskerr[1], 16) }, + { DRDATA (CYL, r2_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[1], 8) }, + { DRDATA (POS, r2_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, r2_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE r2_dev = { + "R2", &r2_unit, r2_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &r2_reset, + &r2_boot, &r2_attach, NULL +}; + +UNIT f2_unit = { UDATA (&f2_svc, UNIT_FIX+UNIT_ATTABLE, 0), 100 }; + +REG f2_reg[] = { + { FLDATA (NOTRDY, notrdy[1], 0) }, + { FLDATA (SEEK, seekbusy[1], 0) }, + { HRDATA (DAR, DDAR[1], 16) }, + { HRDATA (CAR, DCAR[1], 16) }, + { HRDATA (ERR, diskerr[1], 16) }, + { DRDATA (CYL, f2_unit.u3, 8) }, + { DRDATA (HEAD, seekhead[1], 8) }, + { DRDATA (POS, f2_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, f2_unit.wait, 24), PV_LEFT }, + { BRDATA (BUF, dbuf, 8, 8, 256) }, + { NULL } +}; + +DEVICE f2_dev = { + "F2", &f2_unit, f2_reg, NULL, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &f2_reset, + &f2_boot, &f2_attach, NULL +}; + +/* -------------------------------------------------------------------- */ + +/* 5444: master routines */ + +int32 dsk1 (int32 op, int32 m, int32 n, int32 data) +{ + int32 r; + + r = dsk(0, op, m, n, data); + return (r); +} + +int32 dsk2 (int32 op, int32 m, int32 n, int32 data) +{ + int32 r; + + r = dsk(1, op, m, n, data); + return (r); +} + +/* 5444: operational routine */ + +int32 dsk (int32 disk, int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata, i, j, u, sect, nsects, addr, r, c, res; + int32 F, C, S, N, usave; + UNIT *uptr; + + u = m; + if (disk == 1) u += 2; + F = GetMem(DCAR[disk]+0); /* Flag bits */ + C = GetMem(DCAR[disk]+1); /* Cylinder */ + S = GetMem(DCAR[disk]+2); /* Sector */ + N = GetMem(DCAR[disk]+3); /* Number of sectors */ + switch (u) { + case 0: + uptr = r1_dev.units; + break; + case 1: + uptr = f1_dev.units; + break; + case 2: + uptr = r2_dev.units; + break; + case 3: + uptr = f2_dev.units; + break; + default: + break; + } + if (debug_reg & 0x02) + fprintf(trace, "==> %04X %s %01X,%d,%04X DAR=%04X CAR=%04X C=%02X, S=%02X, N=%02X\n", + IAR[level], + opstr[op], + m, n, data, + DDAR[disk], + DCAR[disk], + C, S, N); + + switch (op) { + + /* SIO 5444 */ + case 0: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + diskerr[disk] = 0; /* SIO resets errors */ + found[disk] = 0; /* ... and found bit */ + iodata = 0; + switch (n) { + case 0x00: /* Seek */ + if (S & 0x80) + seekhead[disk] = 1; + else + seekhead[disk] = 0; + if (S & 1) { + uptr -> u3 += N; + } else { + uptr -> u3 -= N; + } + if (uptr -> u3 < 0) + uptr -> u3 = 0; + if (uptr -> u3 > 203) { + uptr -> u3 = 0; + diskerr[disk] |= 0x0100; + if (debug_reg & 0x02) + fprintf(trace, "==> Seek Past End of Disk\n"); + } + + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + + /* Seek arms are the same for both disks on a drive: + update the other arm */ + + usave = uptr -> u3; + if (u == 0) uptr = f1_dev.units; + if (u == 1) uptr = r1_dev.units; + if (u == 2) uptr = f2_dev.units; + if (u == 3) uptr = r2_dev.units; + uptr -> u3 = usave; + + seekbusy[disk] = 1; + iodata = SCPE_OK; + break; + + case 0x01: /* Read */ + switch (data) { + case 0: /* Read data */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + + for (i = 0; i < nsects; i++) { + r = read_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0800; + break; + } + for (j = 0; j < DSK_SECTSIZE; j++) { + PutMem(addr, dbuf[j]); + addr++; + } + + if ((sect == 55) ) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; /* HJS mod */ + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; /* HJS mod */ + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + case 1: /* Read ID */ + if (uptr -> u3 > 0 && uptr -> u3 < 4) + PutMem(DCAR[disk], 1); + else + PutMem(DCAR[disk], 0); + PutMem(DCAR[disk]+1, uptr -> u3); + PutMem(DCAR[disk]+2, RIDsect[disk]); + RIDsect[disk]++; + if (RIDsect[disk] > 23) + RIDsect[disk] = 32; + if (RIDsect[disk] > 55) + RIDsect[disk] = 0; + break; + case 2: /* Read Diagnostic */ + iodata = STOP_INVDEV; + break; + case 3: /* Verify */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + r = read_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0800; + break; + } + if ((sect == 55) ) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + default: + return STOP_INVDEV; + } + break; + case 0x02: /* Write */ + switch (data) { + case 0: /* Write Data */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + for (j = 0; j < DSK_SECTSIZE; j++) { + dbuf[j] = GetMem(addr); + addr++; + } + r = write_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0400; + break; + } + if ((sect == 55) ) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; /* HJS mod */ + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + case 1: /* Write identifier */ + if (seekhead[disk] == 0) + S = 0; + else + S = 0x80; + N = 23; + + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + for (j = 0; j < DSK_SECTSIZE; j++) { + dbuf[j] = GetMem(addr); + } + r = write_sector(uptr, dbuf, sect); + if (r != 1) { + diskerr[disk] |= 0x0400; + break; + } + if ((sect == 55) ) { + S = sect; + N = nsects - i - 2; + if (N > 0) diskerr[disk] |= 0x0020; + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + default: + return STOP_INVDEV; + } + break; + case 0x03: /* Scan */ + sect = (S >> 2) & 0x3F; + nsects = N + 1; + addr = DDAR[disk]; + for (i = 0; i < nsects; i++) { + r = read_sector(uptr, dbuf, sect); + if (r != 1 || uptr->u3 != C) { + diskerr[disk] |= 0x0800; + break; + } + res = 0; + for (j = 0; j < DSK_SECTSIZE; j++) { + c = GetMem(addr); + if (j != 0xff) { + if (dbuf[i] < c) + res = 1; + if (dbuf[i] > c) + res = 3; + } + addr++; + } + if (res == 0) + found[disk] = 1; + if (res == data) + break; + if ((sect == 55) ) { /* HJS MODS */ + S = sect; + N = nsects - i - 2; + if (N > -1) diskerr[disk] |= 0x0020; /* end of cyl. */ + DDAR[disk] = addr & 0xFFFF; + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + sim_activate(uptr, 1); + iodata = SCPE_OK; + break; + } + sect++; + S = sect - 1; + N = nsects - i - 2; + if (sect == 24) + sect = 32; + } + PutMem(DCAR[disk]+2, S << 2); + PutMem(DCAR[disk]+3, N); + /*sim_activate(uptr, uptr -> wait);*/ + sim_activate(uptr, 1); + break; + default: + return STOP_INVDEV; + } + return iodata; + + /* LIO 5444 */ + case 1: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT; + switch (n) { + case 0x04: /* Data Addr */ + DDAR[disk] = data; + break; + case 0x06: /* Control Addr */ + DCAR[disk] = data; + break; + default: + return STOP_INVDEV; + } + return SCPE_OK; + /* TIO 5444 */ + case 2: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT << 16; + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (diskerr[disk] || notrdy[disk]) + iodata = 1; + if ((uptr -> flags & UNIT_ATT) == 0) + iodata = 1; + break; + case 0x02: /* Busy */ + if (sim_is_active (uptr)) + iodata = 1; + break; + case 0x04: + if (found[disk]) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + + /* SNS 5444 */ + case 3: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT << 16; + iodata = 0; + switch (n) { + case 0x01: + break; + case 0x02: + iodata = diskerr[disk]; + if (notrdy[disk]) + iodata |= 0x4000; + if ((uptr -> flags & UNIT_ATT) == 0) + iodata |= 0x4000; + if (seekbusy[disk]) + iodata |= 0x0010; + if (uptr -> u3 == 0) + iodata |= 0x0040; + break; + case 0x03: + iodata = 0; + break; + case 0x04: + iodata = DDAR[disk]; + break; + case 0x06: + iodata = DCAR[disk]; + break; + default: + return (STOP_INVDEV << 16); + } + iodata |= ((SCPE_OK << 16) & 0xffff0000); + return (iodata); + + /* APL 5444 */ + case 4: + if ((uptr->flags & UNIT_ATT) == 0) + return SCPE_UNATT << 16; + iodata = 0; + switch (n) { + case 0x00: /* Error */ + if (diskerr[disk] || notrdy[disk]) + iodata = 1; + if ((uptr -> flags & UNIT_ATT) == 0) + iodata = 1; + break; + case 0x02: /* Busy */ + if (sim_is_active (uptr)) + iodata = 1; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + default: + break; + } + printf (">>DSK%d non-existent function %d\n", disk, op); + return SCPE_OK; +} + +/* Disk unit service. If a stacker select is active, copy to the + selected stacker. Otherwise, copy to the normal stacker. If the + unit is unattached, simply exit. +*/ + +t_stat r1_svc (UNIT *uptr) +{ +seekbusy[0] = 0; +return SCPE_OK; +} +t_stat f1_svc (UNIT *uptr) +{ +seekbusy[0] = 0; +return SCPE_OK; +} +t_stat r2_svc (UNIT *uptr) +{ +seekbusy[1] = 0; +return SCPE_OK; +} +t_stat f2_svc (UNIT *uptr) +{ +seekbusy[1] = 0; +return SCPE_OK; +} + + +/* Disk reset */ + +t_stat r1_reset (DEVICE *dptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear indicators */ +found[0] = 0; +sim_cancel (&r1_unit); /* clear event */ +r1_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} +t_stat f1_reset (DEVICE *dptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear indicators */ +found[0] = 0; +sim_cancel (&f1_unit); /* clear event */ +f1_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} +t_stat r2_reset (DEVICE *dptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear indicators */ +found[1] = 0; +sim_cancel (&r2_unit); /* clear event */ +r2_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} +t_stat f2_reset (DEVICE *dptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear indicators */ +found[1] = 0; +sim_cancel (&f2_unit); /* clear event */ +f2_unit.u3 = 0; /* cylinder 0 */ +return SCPE_OK; +} + +/* Disk unit attach */ + +t_stat r1_attach (UNIT *uptr, char *cptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear status */ +found[0] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} +t_stat f1_attach (UNIT *uptr, char *cptr) +{ +diskerr[0] = notrdy[0] = seekbusy[0] = 0; /* clear status */ +found[0] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} +t_stat r2_attach (UNIT *uptr, char *cptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear status */ +found[1] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} +t_stat f2_attach (UNIT *uptr, char *cptr) +{ +diskerr[1] = notrdy[1] = seekbusy[1] = 0; /* clear status */ +found[1] = 0; +uptr -> u3 = 0; /* cylinder 0 */ +return attach_unit (uptr, cptr); +} + +/* Bootstrap routine */ + +t_stat r1_boot (int32 unitno, DEVICE *dptr) +{ +int i; +r1_unit.u3 = 0; +read_sector(r1_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} +t_stat f1_boot (int32 unitno, DEVICE *dptr) +{ +int i; +f1_unit.u3 = 0; +read_sector(f1_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} +t_stat r2_boot (int32 unitno, DEVICE *dptr) +{ +int i; +r2_unit.u3 = 0; +read_sector(r2_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} +t_stat f2_boot (int32 unitno, DEVICE *dptr) +{ +int i; +f2_unit.u3 = 0; +read_sector(f2_dev.units, dbuf, 0); +for (i = 0; i < 256; i++) { + M[i] = dbuf[i]; +} +return SCPE_OK; +} + + +/* Raw Disk Data In/Out */ + +int32 read_sector(UNIT *uptr, char *dbuf, int32 sect) +{ + static int32 rtn, realsect; + static long pos; + + /* calculate real sector no */ + if (sect > 23) + realsect = sect - 8; + else + realsect = sect; + /* physically read the sector */ + pos = DSK_CYLSIZE * uptr -> u3; + pos += DSK_SECTSIZE * realsect; + rtn = fseek(uptr -> fileref, pos, 0); + rtn = fread(dbuf, DSK_SECTSIZE, 1, uptr -> fileref); + return (rtn); +} + +int32 write_sector(UNIT *uptr, char *dbuf, int32 sect) +{ + static int32 rtn, realsect; + static long pos; + + /* calculate real sector no */ + if (sect > 23) + realsect = sect - 8; + else + realsect = sect; + if (uptr -> u3 == 0 && realsect == 32) + rtn = 0; + /* physically write the sector */ + pos = DSK_CYLSIZE * uptr -> u3; + pos += DSK_SECTSIZE * realsect; + rtn = fseek(uptr -> fileref, pos, 0); + rtn = fwrite(dbuf, DSK_SECTSIZE, 1, uptr -> fileref); + return (rtn); +} diff --git a/S3/s3_lp.c b/S3/s3_lp.c new file mode 100644 index 0000000..cf21a12 --- /dev/null +++ b/S3/s3_lp.c @@ -0,0 +1,363 @@ +/* s3_lp.c: IBM 1403 line printer simulator + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + lpt 1403 line printer + + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher +*/ + +#include "s3_defs.h" + +extern uint8 M[]; +extern char bcd_to_ascii[64]; +extern int32 iochk, ind[64]; +int32 cct[CCT_LNT] = { 03 }; +int32 cctlnt = 66, cctptr = 0, lines = 0, lflag = 0; +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat write_line (int32 ilnt, int32 mod); +t_stat space (int32 lines, int32 lflag); +t_stat carriage_control (int32 action, int32 mod); +extern unsigned char ebcdic_to_ascii[256]; + +#define UNIT_V_PCHAIN (UNIT_V_UF + 0) +#define UNIT_M_PCHAIN 03 +#define M_UCS 00 /* Universal */ +#define M_PCF 00 /* full */ +#define M_PCA 01 /* business */ +#define M_PCH 02 /* Fortran */ +#define UNIT_PCHAIN (UNIT_M_PCHAIN << UNIT_V_PCHAIN) +#define UCS (M_UCS << UNIT_V_PCHAIN) +#define PCF (M_PCF << UNIT_V_PCHAIN) +#define PCA (M_PCA << UNIT_V_PCHAIN) +#define PCH (M_PCH << UNIT_V_PCHAIN) +#define GET_PCHAIN(x) (((x) >> UNIT_V_PCHAIN) & UNIT_M_PCHAIN) +#define CHP(ch,val) ((val) & (1 << (ch))) + +int32 LPDAR; /* Data Address */ +int32 LPFLR; /* Forms Length */ +int32 LPIAR; /* Image address */ +int32 linectr; /* current line # */ +int32 lpterror = 0; +int32 CC9 = 0; +int32 CC12 = 0; + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +UNIT lpt_unit = { UDATA (NULL, UNIT_SEQ+UNIT_ATTABLE, 0) }; + +REG lpt_reg[] = { + { FLDATA (ERR, lpterror, 0) }, + { HRDATA (LPDAR, LPDAR, 16) }, + { HRDATA (LPFLR, LPFLR, 8) }, + { HRDATA (LPIAR, LPIAR, 16) }, + { DRDATA (LINECT, linectr, 8) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { BRDATA (CCT, cct, 8, 32, CCT_LNT) }, + { DRDATA (LINES, lines, 8), PV_LEFT }, + { DRDATA (CCTP, cctptr, 8), PV_LEFT }, + { DRDATA (CCTL, cctlnt, 8), REG_RO + PV_LEFT }, + { GRDATA (CHAIN, lpt_unit.flags, 10, 2, UNIT_V_PCHAIN), REG_HRO }, + { NULL } +}; + +MTAB lpt_mod[] = { + { UNIT_PCHAIN, UCS, "UCS", "UCS", NULL }, + { UNIT_PCHAIN, PCA, "A chain", "PCA", NULL }, + { UNIT_PCHAIN, PCH, "H chain", "PCH", NULL }, + { 0 } +}; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &lpt_reset, + NULL, NULL, NULL +}; + + +/* -------------------------------------------------------------------- */ + +/* Printer: master routine */ + +int32 lpt (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata; + switch (op) { + case 0: /* SIO 1403 */ + iodata = 0; + printf("\0"); + switch (n) { + case 0x00: /* Spacing only */ + if (data > 0 && data < 4) + iodata = carriage_control(2, data); + break; + case 0x02: /* Print & space */ + iodata = write_line(0, 0); + if (data > 3) data = 0; + if (iodata == SCPE_OK) + iodata = carriage_control(2, data); + break; + case 0x04: /* Skip only */ + iodata = carriage_control(4, data); + break; + case 0x06: /* Print and skip */ + iodata = write_line(0, 0); + if (iodata == SCPE_OK) + iodata = carriage_control(4, data); + break; + default: + return STOP_INVDEV; + } + return iodata; + case 1: /* LIO 1403 */ + switch (n) { + case 0x00: /* LPFLR */ + LPFLR = (data >> 8) & 0xff; + break; + case 0x04: + LPIAR = data & 0xffff; + break; + case 0x06: + LPDAR = data & 0xffff; + break; + default: + return STOP_INVDEV; + } + return SCPE_OK; + case 2: /* TIO 1403 */ + iodata = 0; + switch (n) { + case 0x00: /* Not ready/check */ + if (lpterror) + iodata = 1; + if ((lpt_unit.flags & UNIT_ATT) == 0) + iodata = 1; + break; + case 0x02: /* Buffer Busy */ + iodata = 0; + break; + case 0x04: /* Carriage Busy */ + iodata = 0; + break; + case 0x06: /* Printer busy */ + iodata = 0; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + case 3: /* SNS 1403 */ + switch (n) { + case 0x00: /* Line count */ + iodata = (linectr << 8); + break; + case 0x02: /* Timing data */ + iodata = 0; + break; + case 0x03: /* Check data */ + iodata = 0; + break; + case 0x04: /* LPIAR */ + iodata = LPIAR; + break; + case 0x06: /* LPDAR */ + iodata = LPDAR; + break; + default: + return (STOP_INVDEV << 16); + } + return ((SCPE_OK << 16) | iodata); + case 4: /* APL 1403 */ + iodata = 0; + return ((SCPE_OK << 16) | iodata); + default: + break; + } + printf (">>LPT non-existent function %d\n", op); + return SCPE_OK; +} + + +/* Print routine + + Modifiers have been checked by the caller + S = suppress automatic newline +*/ + +t_stat write_line (int32 ilnt, int32 mod) +{ +int32 i, t, lc; +static char lbuf[LPT_WIDTH + 1]; /* + null */ + +if ((lpt_unit.flags & UNIT_ATT) == 0) + return SCPE_UNATT; + +lpterror = 0; +lc = LPDAR; /* clear error */ +for (i = 0; i < LPT_WIDTH; i++) { /* convert print buf */ + t = M[lc]; + lbuf[i] = ebcdic_to_ascii[t & 0xff]; + M[lc] = 0x40; /* HJS MOD */ + lc++; +} +for (i = LPT_WIDTH - 1; (i >= 0) && (lbuf[i] == ' '); i--) lbuf[i] = 0; +fputs (lbuf, lpt_unit.fileref); /* write line */ +if (lines) space (lines, lflag); /* cc action? do it */ +else if (mod == 0) space (1, FALSE); /* default? 1 line */ +else { + fputc ('\r', lpt_unit.fileref); /* sup -> overprint */ + lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +} +lines = lflag = 0; /* clear cc action */ +if (ferror (lpt_unit.fileref)) { /* error? */ + perror ("Line printer I/O error"); + clearerr (lpt_unit.fileref); + lpterror = 1; +} +return SCPE_OK; +} + +/* Carriage control routine + + Parameters: + action = 00, skip to channel now + = 01, space lines after + = 02, space lines now + = 03, skip to channel after + = 04, skip to line number + mod = number of lines or channel number or line number +*/ + +t_stat carriage_control (int32 action, int32 mod) +{ +int32 i; + +if ((lpt_unit.flags & UNIT_ATT) == 0) + return SCPE_UNATT; + +switch (action) { +case 0: /* to channel now */ + if ((mod == 0) || (mod > 12) || CHP (mod, cct[cctptr])) return SCPE_OK; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) + return space (i, TRUE); + } + return STOP_INVDEV; /* runaway channel */ +case 1: /* space after */ + if (mod <= 3) { + lines = mod; /* save # lines */ + lflag = FALSE; /* flag spacing */ + CC9 = CC12 = 0; + } + return SCPE_OK; +case 2: /* space now */ + if (mod <= 3) return space (mod, FALSE); + return SCPE_OK; +case 3: /* to channel after */ + if ((mod == 0) || (mod > 12)) return SCPE_OK; /* check channel */ + CC9 = CC12 = 0; + for (i = 1; i < cctlnt + 1; i++) { /* sweep thru cct */ + if (CHP (mod, cct[(cctptr + i) % cctlnt])) { + lines = i; /* save # lines */ + lflag = TRUE; /* flag skipping */ + return SCPE_OK; + } + } + return STOP_INVDEV; +case 4: /* To line # */ + if (mod < 2) { + fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ + linectr = 1; + } else { + if (mod <= linectr) { + fputs ("\n\f", lpt_unit.fileref); + linectr = 1; + } + while (1) { + if (linectr == mod) + break; + space(1, 0); + } + } + return SCPE_OK; +} +return SCPE_OK; +} + +/* Space routine - space or skip n lines + + Inputs: + count = number of lines to space or skip + sflag = skip (TRUE) or space (FALSE) +*/ + +t_stat space (int32 count, int32 sflag) +{ +int32 i; + +if ((lpt_unit.flags & UNIT_ATT) == 0) return SCPE_UNATT; +cctptr = (cctptr + count) % cctlnt; /* adv cct, mod lnt */ +if (sflag && CHP (0, cct[cctptr])) { /* skip, top of form? */ + fputs ("\n\f", lpt_unit.fileref); /* nl, ff */ + linectr = 1; +} else { + for (i = 0; i < count; i++) fputc ('\n', lpt_unit.fileref); +} +lpt_unit.pos = ftell (lpt_unit.fileref); /* update position */ +CC9 = CHP (9, cct[cctptr]) != 0; /* set indicators */ +CC12 = CHP (12, cct[cctptr]) != 0; +linectr += count; +if (linectr > LPFLR) + linectr -= LPFLR; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = linectr = lflag = 0; /* no cc action */ +lpterror = 0; +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +cctptr = 0; /* clear cct ptr */ +lines = 0; /* no cc action */ +lpterror = 0; +linectr = 0; +return attach_unit (uptr, cptr); +} diff --git a/S3/s3_pkb.c b/S3/s3_pkb.c new file mode 100644 index 0000000..0ba6209 --- /dev/null +++ b/S3/s3_pkb.c @@ -0,0 +1,315 @@ +/* s3_pkb.c: System/3 5471 console terminal simulator + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. + + pkb 5471 printer/keyboard + + 25-Apr-03 RMS Revised for extended file support + 08-Oct-02 RMS Added impossible function catcher +*/ + +#include "s3_defs.h" +#include + +extern int32 int_req, dev_busy, dev_done, dev_disable; +t_stat pkb_svc (UNIT *uptr); +t_stat pkb_reset (DEVICE *dptr); +extern t_stat sim_poll_kbd (void); +extern t_stat sim_putchar (int32 out); +extern int32 IAR[], level; +extern int32 debug_reg; + +/* 5471 data structures + + pkb_dev TTI device descriptor + pkb_unit TTI unit descriptor + pkb_reg TTI register list + pkb_mod TTI/TTO modifiers list +*/ + +/* Flag bits : (kept in pkb_unit.u3) */ + +#define PRT_INTREQ 0x800 /* Printer interrupt pending */ +#define KBD_INTREQ 0x400 /* Request key interrupt pending */ +#define KBD_INTEND 0x200 /* End or cancel key interrupt pending */ +#define KBD_INTKEY 0x100 /* Return or other key interrupt pending */ +#define KBD_REQLIGHT 0x20 /* Request Pending Indicator (light on/off) */ +#define KBD_PROLIGHT 0x10 /* Proceed indicator (light on/off) */ +#define KBD_REQINT 0x04 /* Req key interrupts enabled */ +#define KBD_KEYINT 0x02 /* Other key interrupts enabled */ +#define PRT_PRTINT 0x01 /* Printer interrupts enabled */ + +/* Keys mapped to 5471 functions */ + +int32 key_req = 0x01; /* Request key: ^A */ +int32 key_rtn = 0x12; /* Return key: ^R */ +int32 key_can = 0x1B; /* Cancel key: ESC */ +int32 key_end = 0x0d; /* End key - CR */ + +UNIT pkb_unit = { UDATA (&pkb_svc, 0, 0), KBD_POLL_WAIT }; + +REG pkb_reg[] = { + { HRDATA (FLAG, pkb_unit.u3, 16) }, + { HRDATA (IBUF, pkb_unit.buf, 8) }, + { HRDATA (OBUF, pkb_unit.u4, 8) }, + { HRDATA (REQKEY, key_req, 8) }, + { HRDATA (RTNKEY, key_rtn, 8) }, + { HRDATA (CANKEY, key_can, 8) }, + { HRDATA (ENDKEY, key_end, 8) }, + { DRDATA (POS, pkb_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, pkb_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } +}; + +MTAB pkb_mod[] = { + { 0 } +}; + +DEVICE pkb_dev = { + "PKB", &pkb_unit, pkb_reg, pkb_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &pkb_reset, + NULL, NULL, NULL +}; + + +/*-------------------------------------------------------------------*/ +/* EBCDIC to ASCII translate table */ +/*-------------------------------------------------------------------*/ +unsigned char ebcdic_to_ascii[] = { +"\x00\x01\x02\x03\xA6\x09\xA7\x7F\xA9\xB0\xB1\x0B\x0C\x0D\x0E\x0F" +"\x10\x11\x12\x13\xB2\xB4\x08\xB7\x18\x19\x1A\xB8\xBA\x1D\xBB\x1F" +"\xBD\xC0\x1C\xC1\xC2\x0A\x17\x1B\xC3\xC4\xC5\xC6\xC7\x05\x06\x07" +"\xC8\xC9\x16\xCB\xCC\x1E\xCD\x04\xCE\xD0\xD1\xD2\x14\x15\xD3\xFC" +"\x20\xD4\x83\x84\x85\xA0\xD5\x86\x87\xA4\xD6\x2E\x3C\x28\x2B\xD7" +"\x26\x82\x88\x89\x8A\xA1\x8C\x8B\x8D\xD8\x21\x24\x2A\x29\x3B\x5E" +"\x2D\x2F\xD9\x8E\xDB\xDC\xDD\x8F\x80\xA5\x7C\x2C\x25\x5F\x3E\x3F" +"\xDE\x90\xDF\xE0\xE2\xE3\xE4\xE5\xE6\x60\x3A\x23\x40\x27\x3D\x22" +"\xE7\x61\x62\x63\x64\x65\x66\x67\x68\x69\xAE\xAF\xE8\xE9\xEA\xEC" +"\xF0\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\xF1\xF2\x91\xF3\x92\xF4" +"\xF5\x7E\x73\x74\x75\x76\x77\x78\x79\x7A\xAD\xA8\xF6\x5B\xF7\xF8" +"\x9B\x9C\x9D\x9E\x9F\xB5\xB6\xAC\xAB\xB9\xAA\xB3\xBC\x5D\xBE\xBF" +"\x7B\x41\x42\x43\x44\x45\x46\x47\x48\x49\xCA\x93\x94\x95\xA2\xCF" +"\x7D\x4A\x4B\x4C\x4D\x4E\x4F\x50\x51\x52\xDA\x96\x81\x97\xA3\x98" +"\x5C\xE1\x53\x54\x55\x56\x57\x58\x59\x5A\xFD\xEB\x99\xED\xEE\xEF" +"\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\xFE\xFB\x9A\xF9\xFA\xFF" +}; + +/*-------------------------------------------------------------------*/ +/* ASCII to EBCDIC translate table */ +/*-------------------------------------------------------------------*/ +unsigned char ascii_to_ebcdic[] = { +"\x00\x01\x02\x03\x37\x2D\x2E\x2F\x16\x05\x25\x0B\x0C\x0D\x0E\x0F" +"\x10\x11\x12\x13\x3C\x3D\x32\x26\x18\x19\x1A\x27\x22\x1D\x35\x1F" +"\x40\x5A\x7F\x7B\x5B\x6C\x50\x7D\x4D\x5D\x5C\x4E\x6B\x60\x4B\x61" +"\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7\xF8\xF9\x7A\x5E\x4C\x7E\x6E\x6F" +"\x7C\xC1\xC2\xC3\xC4\xC5\xC6\xC7\xC8\xC9\xD1\xD2\xD3\xD4\xD5\xD6" +"\xD7\xD8\xD9\xE2\xE3\xE4\xE5\xE6\xE7\xE8\xE9\xAD\xE0\xBD\x5F\x6D" +"\x79\x81\x82\x83\x84\x85\x86\x87\x88\x89\x91\x92\x93\x94\x95\x96" +"\x97\x98\x99\xA2\xA3\xA4\xA5\xA6\xA7\xA8\xA9\xC0\x6A\xD0\xA1\x07" +"\x68\xDC\x51\x42\x43\x44\x47\x48\x52\x53\x54\x57\x56\x58\x63\x67" +"\x71\x9C\x9E\xCB\xCC\xCD\xDB\xDD\xDF\xEC\xFC\xB0\xB1\xB2\xB3\xB4" +"\x45\x55\xCE\xDE\x49\x69\x04\x06\xAB\x08\xBA\xB8\xB7\xAA\x8A\x8B" +"\x09\x0A\x14\xBB\x15\xB5\xB6\x17\x1B\xB9\x1C\x1E\xBC\x20\xBE\xBF" +"\x21\x23\x24\x28\x29\x2A\x2B\x2C\x30\x31\xCA\x33\x34\x36\x38\xCF" +"\x39\x3A\x3B\x3E\x41\x46\x4A\x4F\x59\x62\xDA\x64\x65\x66\x70\x72" +"\x73\xE1\x74\x75\x76\x77\x78\x80\x8C\x8D\x8E\xEB\x8F\xED\xEE\xEF" +"\x90\x9A\x9B\x9D\x9F\xA0\xAC\xAE\xAF\xFD\xFE\xFB\x3F\xEA\xFA\xFF" +}; + +/* -------------------------------------------------------------------- */ + +/* Console Input: master routine */ + +int32 pkb (int32 op, int32 m, int32 n, int32 data) +{ + int32 iodata= 0, ec, ac; + switch (op) { + case 0: /* SIO 5471 */ + if (n != 0) + return STOP_INVDEV; + /*printf("%04X SIO %d,%d,%02X\n\r", IAR[level]-4, m, n, data);*/ + if (m == 0) { /* Keyboard */ + pkb_unit.u3 &= 0xFC1; + pkb_unit.u3 |= data; + if (data & 0x01) { + pkb_unit.u3 &= ~KBD_INTREQ; + pkb_unit.u3 &= ~KBD_INTKEY; + pkb_unit.u3 &= ~KBD_INTEND; + return RESET_INTERRUPT; + } + } else { /* Printer */ + if (data & 0x80) { /* start print bit */ + if (debug_reg & 0x80) + return STOP_IBKPT; + ec = pkb_unit.u4 & 0xff; + ac = ebcdic_to_ascii[ec]; + sim_putchar(ac); + pkb_unit.u3 |= PRT_INTREQ; + } + if (data & 0x40) { /* Carr. Return */ + sim_putchar('\n'); + sim_putchar('\r'); + pkb_unit.u3 |= PRT_INTREQ; + } + pkb_unit.u3 &= 0xFFe; + if (data & 0x04) /* Print interrupt flag */ + pkb_unit.u3 |= PRT_PRTINT; + if (data & 0x01) { /* Reset Interrupt */ + if (level < 8) { + if (!(data & 0x80)) + pkb_unit.u3 &= ~PRT_INTREQ; + return RESET_INTERRUPT; + } + } + } + return SCPE_OK; + case 1: /* LIO 5471 */ + if (n != 0) + return STOP_INVDEV; + if (m != 1) + return STOP_INVDEV; + pkb_unit.u4 = (data >> 8) & 0xff; + return SCPE_OK; + break; + case 2: /* TIO 5471 */ + return STOP_INVDEV; + case 3: /* SNS 5471 */ + if (n != 1 && n != 3) + return (STOP_INVDEV << 16); + if (m == 0) { /* Keyboard data */ + if (n == 1) { /* Sense bytes 0 & 1 */ + iodata = (pkb_unit.buf << 8) & 0xff00; + if (pkb_unit.u3 & KBD_INTREQ) + iodata |= 0x80; + if (pkb_unit.u3 & KBD_INTEND) + iodata |= 0x40; + if (pkb_unit.u3 & KBD_INTKEY) + iodata |= 0x08; + if (pkb_unit.buf == 0x12) /* Return key */ + iodata |= 0x04; + if (pkb_unit.buf == 0x03) /* Cancel key */ + iodata |= 0x20; + if (pkb_unit.buf == 0x0d) /* End key */ + iodata |= 0x10; + iodata |= ((SCPE_OK << 16) & 0xffff0000); + } else { /* Sense bytes 2 & 3 */ + iodata = 0; /* Manual says CE use only */ + } + } else { /* Printer Data */ + if (n == 1) { /* Sense bytes 0 & 1 */ + iodata = 0; + if (pkb_unit.u3 & PRT_INTREQ) + iodata |= 0x80; + } else { + iodata = 0; /* CE use only */ + } + } + iodata |= ((SCPE_OK << 16) & 0xffff0000); + return (iodata); + case 4: /* APL 5471 */ + return STOP_INVDEV; + default: + break; + } + printf (">>PKB non-existent function %d\n", op); + return SCPE_OK; +} + +/* Unit service */ + +t_stat pkb_svc (UNIT *uptr) +{ +int32 temp, ac, ec; + +sim_activate (&pkb_unit, pkb_unit.wait); /* continue poll */ + +if (pkb_unit.u3 & PRT_INTREQ) { /* Printer Interrupt */ + int_req |= 2; + return SCPE_OK; +} + +/* Keyboard : handle input */ + +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ + +ac = temp & 0x7f; /* placed type ASCII char in ac */ +if (pkb_unit.u3 & KBD_REQINT) { + if (ac == key_req) { /* Request Key */ + pkb_unit.u3 |= KBD_INTREQ; + int_req |= 2; + return SCPE_OK; + } +} +if (islower(ac)) + ac = toupper(ac); +ec = ascii_to_ebcdic[ac]; /* Translate */ +pkb_unit.buf = ec; /* put in buf */ +pkb_unit.pos = pkb_unit.pos + 1; +if (ac == key_end) { /* End key */ + if (pkb_unit.u3 & KBD_KEYINT) { + pkb_unit.u3 |= KBD_INTEND; + pkb_unit.buf = 0x0d; + int_req |= 2; + } + return SCPE_OK; +} +if (ac == key_can) { /* Cancel key */ + if (pkb_unit.u3 & KBD_KEYINT) { + pkb_unit.u3 |= KBD_INTEND; + pkb_unit.buf = 0x03; + int_req |= 2; + } + return SCPE_OK; +} +if (ac == key_rtn) { /* Return key */ + if (pkb_unit.u3 & KBD_KEYINT) { + pkb_unit.u3 |= KBD_INTKEY; + pkb_unit.buf = 0x12; + int_req |= 2; + } + return SCPE_OK; +} +if (pkb_unit.u3 & KBD_KEYINT) { /* Key interupts enabled ? */ + int_req |= 2; /* Device 1 Interrupt! */ + pkb_unit.u3 |= KBD_INTKEY; /* Set pending flag */ +} +return SCPE_OK; +} + +/* Reset routine */ + +t_stat pkb_reset (DEVICE *dptr) +{ +pkb_unit.buf = 0; +int_req = int_req & ~0x02; /* reset interrupt */ +sim_activate (&pkb_unit, pkb_unit.wait); /* activate unit */ +return SCPE_OK; +} + +t_stat pkb_setmod (UNIT *uptr, int32 value) +{ +return SCPE_OK; +} + diff --git a/S3/s3_sys.c b/S3/s3_sys.c new file mode 100644 index 0000000..0fa2c1e --- /dev/null +++ b/S3/s3_sys.c @@ -0,0 +1,948 @@ +/* s3_sys.c: IBM System/3 system interface + + Copyright (c) 2001-2005, Charles E. Owen + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Charles E. Owen shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Charles E. Owen. +*/ + +#include +#include "s3_defs.h" + +extern DEVICE cpu_dev; +extern DEVICE pkb_dev; +extern DEVICE cdr_dev; +extern DEVICE cdp_dev; +extern DEVICE stack_dev; +extern DEVICE lpt_dev; +extern DEVICE r1_dev; +extern DEVICE f1_dev; +extern DEVICE r2_dev; +extern DEVICE f2_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern unsigned char M[]; +extern int32 saved_PC, IAR[]; +extern char ebcdic_to_ascii[256]; +char *parse_addr(char *cptr, char *gbuf, int32 *addr, int32 *addrtype); + +int32 printf_sym (FILE *of, char *strg, int32 addr, uint32 *val, + UNIT *uptr, int32 sw); + +/* SCP data structures + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words needed for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "System/3"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 6; + +DEVICE *sim_devices[] = { + &cpu_dev, + &pkb_dev, + &cdr_dev, + &cdp_dev, + &stack_dev, + &lpt_dev, + &r1_dev, + &f1_dev, + &r2_dev, + &f2_dev, + NULL +}; + +const char *sim_stop_messages[] = { + "Unknown error", + "Unknown I/O Instruction", + "HALT instruction", + "Breakpoint", + "Invalid Opcode", + "Invalid Qbyte", + "Invalid Address", + "Invalid Device Command", + "ATTN Card Reader" +}; + +/* This is the opcode master defintion table. Each possible opcode mnemonic + is defined here, with enough information to translate to and from + symbolic to binary machine code. + First field is the opcode's mnemonic + Second field is the hex of the right nybble of the binary opcode + Third field is the Q code for those with implicit Q codes + Fourth field is the symbolic format of the operands: + 0 - (Q-byte),(R-byte) + 1 - (Q-byte),(Address) + 2 - (Address),(Address),(Qbyte) + 3 - (Address),(Qbyte) + 4 - (device),(modifier),(function) -- these 3 make up qbyte + 5 - (device),(modifier),(function),(control) + 6 - (device),(modifier),(function),(Address) + 7 - (displacement) -- Q byte is implicit in opcode + 8 - (address) -- Qbyte is implicit in opcode + 9 - (Address),(Address) -- Qbyte is implicit in opcode + Fifth Field is the group number: + 0 - Command Group (left op nybble is F) + 1 - One Address Operations A (Left Nybble C, D, or E) + 2 - Two Address Operations (Left Nybble 0,1,2,4,5,6,8,9, or A) + 3 - One Address Operations B (left Nybble 3, 7, or B) + + There is duplication in this table -- IBM defines different opcodes + that resolve to the same binary machine instruction -- e.g. JE and + JZ. On input this is no problem, on output, define the one you + want to appear first, the second will never appear on output. +*/ + +int32 nopcode = 75; +struct opdef opcode[75] = { + "HPL", 0x00,0,0,0, /* Halt Program Level */ + "A", 0x06,0,1,3, /* Add to Register: A R,AADD */ + "ST", 0x04,0,1,3, /* Store Register */ + "L", 0x05,0,1,3, /* Load Register */ + "LA", 0x02,0,1,1, /* Load Address */ + "ZAZ", 0x04,0,2,2, /* Zero and Add Zoned */ + "AZ", 0x06,0,2,2, /* Add Zoned Decimal */ + "SZ", 0x07,0,2,2, /* Subtract Zoned Decimal */ + "ALC", 0x0E,0,2,2, /* Add Logical: ALC BADD,AADD,LEN */ + "SLC", 0x0F,0,2,2, /* Sub Logical: SLC BADD,AADD,LEN */ + "MVC", 0x0C,0,2,2, /* Move Chars MVX BADD,AADD,LEN */ + "ED", 0x0A,0,2,2, /* Edit: ED BADD,AADD,LEN */ + "ITC", 0x0B,0,2,2, /* Insert Chars: ITC BADD,AADD,LEN */ + "CLC", 0x0D,0,2,2, /* Compare Logical: CLC BADD,AADD,LEN */ + "MVI", 0x0C,0,3,3, /* Move Immediate */ + "SBN", 0x0A,0,3,3, /* Set Bits On */ + "SBF", 0x0B,0,3,3, /* Set Bits Off */ + "CLI", 0x0D,0,3,3, /* Compare Immediate */ + "TBN", 0x08,0,3,3, /* Test Bits On */ + "TBF", 0x09,0,3,3, /* Test Bits Off */ + "APL", 0x01,0,4,0, /* Advance Program Level */ + "SIO", 0x03,0,5,0, /* Start I/O */ + "SNS", 0x00,0,6,3, /* Sense I/O */ + "LIO", 0x01,0,6,3, /* Load I/O */ + "TIO", 0x01,0,6,1, /* Test I/O */ + "J", 0x02,0,7,0, /* Jump Unconditional */ + "J", 0x02,0x87,7,0, /* Alternate J */ + "JH", 0x02,132,7,0, /* Jump if High */ + "JL", 0x02,130,7,0, /* Jump if Low */ + "JE", 0x02,129,7,0, /* Jump if Equal */ + "JNH", 0x02,4,7,0, /* Jump if Not High */ + "JNL", 0x02,2,7,0, /* Jump if Not Low */ + "JNE", 0x02,1,7,0, /* Jump if Not Equal */ + "JOZ", 0x02,136,7,0, /* Jump if Overflow Zoned */ + "JOL", 0x02,160,7,0, /* Jump if Overflow Logical */ + "JNOZ", 0x02,8,7,0, /* Jump if No Overflow Zoned */ + "JNOL", 0x02,32,7,0, /* Jump if No Overflow Logical */ + "JT", 0x02,16,7,0, /* Jump if True */ + "JF", 0x02,144,7,0, /* Jump if False */ + "JP", 0x02,132,7,0, /* Jump if Plus */ + "JM", 0x02,130,7,0, /* Jump if Minus */ + "JZ", 0x02,129,7,0, /* Jump if Zero */ + "JNP", 0x02,4,7,0, /* Jump if Not Plus */ + "JNM", 0x02,2,7,0, /* Jump if Not Minus */ + "JNZ", 0x02,1,7,0, /* Jump if Not Zero */ + "NOPJ", 0x02,0x80,7,0, /* Never Jump - NOP */ + "B", 0x00,0x00,8,1, /* Branch Unconditional */ + "B", 0x00,0x87,8,1, /* Alternate B */ + "BH", 0x00,0x84,8,1, /* Branch if High */ + "BL", 0x00,0x82,8,1, /* Branch if Low */ + "BE", 0x00,0x81,8,1, /* Branch if Equal */ + "BNH", 0x00,0x04,8,1, /* Branch if Not High */ + "BNL", 0x00,0x02,8,1, /* Branch if Not Low */ + "BNE", 0x00,0x01,8,1, /* Branch if Not Equal */ + "BOZ", 0x00,0x88,8,1, /* Branch if Overflow Zoned */ + "BOL", 0x00,0xA0,8,1, /* Branch if Overflow Logical */ + "BNOZ", 0x00,0x08,8,1, /* Branch if No Overflow Zoned */ + "BNOL", 0x00,0x20,8,1, /* Branch if No Overflow Logical */ + "BT", 0x00,0x10,8,1, /* Branch if True */ + "BF", 0x00,0x90,8,1, /* Branch if False */ + "BP", 0x00,0x84,8,1, /* Branch if Plus */ + "BM", 0x00,0x82,8,1, /* Branch if Minus */ + "BZ", 0x00,0x81,8,1, /* Branch if Zero */ + "BNP", 0x00,0x04,8,1, /* Branch if Not Plus */ + "BNM", 0x00,0x02,8,1, /* Branch if Not Minus */ + "BNZ", 0x00,0x01,8,1, /* Branch if Not Zero */ + "NOPB", 0x00,0x80,8,1, /* Never Branch - NOP */ + "MZZ", 0x08,0,9,2, /* Move Zone to Zone */ + "MNZ", 0x08,1,9,2, /* Move Numeric to Zone */ + "MZN", 0x08,2,9,2, /* Move Zone to Numeric */ + "MNN", 0x08,3,9,2, /* Move Numeric to Numeric */ + "MVX", 0x08,0,2,2, /* Move Hex: MVX BADD,AADD,CODE */ + "JC", 0x02,0,3,0, /* Jump on Specified Condition bits */ + "BC", 0x00,0,3,1, /* Branch on Specified Condition */ + "***", 0x00,0,0,0 +}; + +int32 regcode[15] = { 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, + 0x80, 0xC0, 0xA0, 0x90, 0x88, 0x84, 0x82, 0x81 +}; + +char regname[15][8] = { "(P2IAR)", + "(P1IAR)", + "(IAR)", + "(ARR)", + "(PSR)", + "(XR2)", + "(XR1)", + "(IAR0)", + "(IAR1)", + "(IAR2)", + "(IAR3)", + "(IAR4)", + "(IAR5)", + "(IAR6)", + "(IAR7)" +}; + +/* This is the binary loader. The input file is considered to be + a string of literal bytes with no special format. The + load starts at the current value of the P1IAR. +*/ + +int32 sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 i, addr = 0, cnt = 0; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +addr = IAR[8]; +while ((i = getc (fileref)) != EOF) { + M[addr] = i & 0xff; + addr++; + cnt++; +} /* end while */ +printf ("%d Bytes loaded.\n", cnt); +return (SCPE_OK); +} + +/* Symbolic output + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + status = error code +*/ + +int32 fprint_sym (FILE *of, int32 addr, uint32 *val, + UNIT *uptr, int32 sw) +{ + int32 r; + char strg[256]; + + strcpy(strg, ""); + r = printf_sym(of, strg, addr, val, uptr, sw); + if (sw & SWMASK ('A')) + strcpy(strg, ""); + else + fprintf(of, "%s", strg); + return (r); +} + +int32 printf_sym (FILE *of, char *strg, int32 addr, uint32 *val, + UNIT *uptr, int32 sw) +{ +int32 cflag, c1, c2, group, len1, len2, inst, aaddr, baddr; +int32 oplen, groupno, i, j, vpos, qbyte, da, m, n; +char bld[128], bldaddr[32], boperand[32], aoperand[32]; +int32 blk[16], blt[16]; +int32 blkadd; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +c1 = val[0] & 0xff; +if (sw & SWMASK ('A')) { + for (i = 0; i < 16; i++) { + blkadd = addr + (i*16); + for (j = 0; j < 16; j++) { + blk[j] = M[blkadd+j] & 0xff; + c2 = ebcdic_to_ascii[blk[j]]; + if (c2 < 040 || c2 > 0177 || blk[j] == 07) { + blt[j] = '.'; + } else { + blt[j] = c2; + } + } + if (i == 0) { + fprintf(of, "%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", + blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], + blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], + blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], + blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); + } else { + fprintf(of, "%X\t%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X [%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c]\n ", + blkadd, blk[0], blk[1], blk[2], blk[3], blk[4], blk[5], blk[6], blk[7], + blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15], + blt[0], blt[1], blt[2], blt[3], blt[4], blt[5], blt[6], blt[7], + blt[8], blt[9], blt[10], blt[11], blt[12], blt[13], blt[14], blt[15]); + } + } + return SCPE_OK; } +if (sw & SWMASK ('C')) { + c2 = ebcdic_to_ascii[c1]; + if (c2 < 040 || c2 > 0177) { + sprintf(strg, "<%02X>", c1 & 0xff); + } else { + sprintf (strg, "%c", c2 & 0xff); + } + return SCPE_OK; } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +inst = val[0] & 0x0f; +len1 = (val[0] >> 6) & 3; +len2 = (val[0] >> 4) & 3; +group = (val[0] >> 4) & 0x0f; +qbyte = val[1]; + +/* Get total length of instruction */ + +if (group == 0x0f) { + oplen = 3; +} else { + oplen = 2; + if (len1 == 0) oplen += 2; + if (len1 == 1 || len1 == 2) oplen++; + if (len2 == 0) oplen += 2; + if (len2 == 1 || len2 == 2) oplen++; +} + +/* Find which group it belongs to */ + +switch (group) { + case 0x0f: + groupno = 0; + break; + case 0x0c: + case 0x0d: + case 0x0e: + groupno = 1; + break; + case 0x03: + case 0x07: + case 0x0b: + groupno = 3; + break; + default: + groupno = 2; + break; +} + +/* find the table entry */ + +for (i = 0; i < nopcode; i++) { + if (opcode[i].form < 7) { /* Explicit Q */ + if (opcode[i].group == groupno && + opcode[i].opmask == inst) break; + } else { /* Implicit Q */ + if (opcode[i].group == groupno && + opcode[i].opmask == inst && + opcode[i].q == qbyte) break; + } +} + +/* print the opcode */ + +if (i >= nopcode) { + sprintf(strg, "%02X", val[0]); + oplen = 1; +} else { + sprintf(bld, "%s ", opcode[i].op); + + /* Extract the addresses into aaddr and baddr */ + + strcpy(aoperand, "ERROR"); + strcpy(boperand, "ERROR"); + vpos = 2; + aaddr = baddr = 0; + switch (len1) { + case 0: + baddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); + sprintf(boperand, "%04X", baddr); + vpos = 4; + break; + case 1: + baddr = val[vpos] & 255; + sprintf(boperand, "(%02X,XR1)", baddr); + vpos = 3; + break; + case 2: + baddr = val[vpos] & 255; + sprintf(boperand, "(%02X,XR2)", baddr); + vpos = 3; + break; + default: + baddr = 0; + break; + } + switch (len2) { + case 0: + aaddr = ((val[vpos] << 8) & 0xff00) | (val[vpos + 1] & 0x00ff); + if (group == 0x0C || group == 0x0D || group == 0x0E) + sprintf(boperand, "%04X", aaddr); + else + sprintf(aoperand, "%04X", aaddr); + break; + case 1: + aaddr = val[vpos] & 255; + if (group == 0x0C || group == 0x0D || group == 0x0E) + sprintf(boperand, "(%02X,XR1)", aaddr); + else + sprintf(aoperand, "(%02X,XR1)", aaddr); + break; + case 2: + aaddr = val[vpos] & 255; + if (group == 0x0C || group == 0x0D || group == 0x0E) + sprintf(boperand, "(%02X,XR2)", aaddr); + else + sprintf(aoperand, "(%02X,XR2)", aaddr); + break; + default: + aaddr = 0; + break; + } + + /* Display the operands in the correct format */ + + da = (qbyte >> 4) & 0x0f; + m = (qbyte >> 3) & 0x01; + n = (qbyte) & 0x07; + + switch (opcode[i].form) { + case 0: + sprintf(bldaddr, "%02X,%02X", qbyte, val[2]); + break; + case 1: + if (inst == 2 || inst == 4 || inst == 5 || inst == 6) { + for (i = 0; i < 16; i++) { + if (regcode[i] == qbyte) + break; + } + if (i < 16) { + sprintf(bldaddr, "%s,%s", regname[i], boperand); + } else { + sprintf(bldaddr, "%02X,%s", qbyte, boperand); + } + } else { + sprintf(bldaddr, "%02X,%s", qbyte, boperand); + } + break; + case 2: + if (inst > 9 || inst == 4 || inst == 6 || inst == 7) + qbyte++; /* special +1 for length display */ + sprintf(bldaddr, "%s,%s,%d", boperand, aoperand, qbyte); + break; + case 3: + if (strcmp(opcode[i].op, "JC") == 0) { + sprintf(bldaddr, "%04X,%02X", addr+oplen+val[2], qbyte); + } else { + sprintf(bldaddr, "%s,%02X", boperand, qbyte); + } + break; + case 4: + sprintf(bldaddr, "%d,%d,%d", da, m, n); + break; + case 5: + sprintf(bldaddr, "%d,%d,%d,%02X", da, m, n, val[2]); + break; + case 6: + sprintf(bldaddr, "%d,%d,%d,%s", da, m, n, boperand); + break; + case 7: + sprintf(bldaddr, "%04X", addr+oplen+val[2]); + break; + case 8: + sprintf(bldaddr, "%s", boperand); + break; + default: + sprintf(bldaddr, "%s,%s", boperand, aoperand); + break; + } + sprintf(strg, "%s%s", bld, bldaddr); +} + +return -(oplen - 1); +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +int32 parse_sym (char *cptr, int32 addr, UNIT *uptr, uint32 *val, int32 sw) +{ +int32 cflag, i = 0, j, r, oplen, addtyp, saveaddr, vptr; +char gbuf[CBUFSIZE]; + +cflag = (uptr == NULL) || (uptr == &cpu_unit); +while (isspace (*cptr)) cptr++; /* absorb spaces */ +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (unsigned int) cptr[0]; + return SCPE_OK; +} +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* ASCII string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = ((unsigned int) cptr[0] << 8) + (unsigned int) cptr[1]; + return SCPE_OK; +} + +/* An instruction: get opcode (all characters until null, comma, left paren, + or numeric (including spaces). +*/ + +while (1) { + if (*cptr == ',' || *cptr == '\0' || *cptr == '(' || + isdigit(*cptr)) + break; + gbuf[i] = toupper(*cptr); + cptr++; + i++; +} + +/* kill trailing spaces if any */ +gbuf[i] = '\0'; +for (j = i - 1; gbuf[j] == ' '; j--) { + gbuf[j] = '\0'; +} + +/* find opcode in table */ +for (j = 0; j < nopcode; j++) { + if (strcmp(gbuf, opcode[j].op) == 0) + break; +} +if (j >= nopcode) /* not found */ + return SCPE_ARG; + +oplen = 2; /* start with op & q */ + +val[0] = opcode[j].opmask; /* store opcode right nybble */ + +switch (opcode[j].form) { /* Get operands based on operand format */ + case 0: /* Single Byte Operand */ + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); /* Get Q Byte */ + sscanf(gbuf, "%x", &r); + val[1] = r; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); /* Get R Byte */ + sscanf(gbuf, "%x", &r); + val[2] = r; + oplen = 3; + val[0] = 0xf0 | opcode[j].opmask; + break; + case 1: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + if (opcode[j].opmask == 2 || + opcode[j].opmask == 4 || + opcode[j].opmask == 5 || + opcode[j].opmask == 6) { + if (isdigit(gbuf[0])) { + sscanf(gbuf, "%x", &r); + } else { + for (i = 0; i < 16; i++) { + if (strcmp(gbuf, regname[i]) == 0) + break; + } + if (i < 16) { + r = regcode[i]; + } else { + return SCPE_ARG; + } + } + } else { + sscanf(gbuf, "%x", &r); + } + if (r > 255) return SCPE_ARG; + val[1] = r; + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + if (opcode[j].group == 1) + val[0] = 0xC0 | opcode[j].opmask; + else + val[0] = 0x30 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xD0 | opcode[j].opmask; + else + val[0] = 0x70 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xE0 | opcode[j].opmask; + else + val[0] = 0xB0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + break; + case 2: + oplen = 2; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0xff; + val[3] = addr & 0xff; + oplen += 2; + vptr = 4; + val[0] = 0x00 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x40 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x80 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[vptr] = (addr >> 8) & 0xff; + val[vptr+1] = addr & 0xff; + oplen += 2; + break; + case 1: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x10 | val[0]; + break; + case 2: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x20 | val[0]; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%d", &r); + if (opcode[j].opmask > 9 || + opcode[j].opmask == 4 || + opcode[j].opmask == 6 || + opcode[j].opmask == 7) r--; /* special: length -1 */ + val[1] = r; + if (*cptr == ',') cptr++; + break; + case 3: + saveaddr = addr; + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + if (opcode[j].group == 0) { /* Group 0 form 3 is JC with explicit Q */ + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if ((addr - (saveaddr+3)) > 255 || (addr - (saveaddr+3)) < 1) + return SCPE_ARG; + val[2] = addr - (saveaddr+3); + val[1] = r; + val[0] = 0xf0 | opcode[j].opmask; + oplen = 3; + + } else { + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + if (opcode[j].group == 1) + val[0] = 0xC0 | opcode[j].opmask; + else + val[0] = 0x30 | opcode[j].opmask; + } + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xD0 | opcode[j].opmask; + else + val[0] = 0x70 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xE0 | opcode[j].opmask; + else + val[0] = 0xB0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if (r > 255) return SCPE_ARG; + val[1] = r; + break; + case 4: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 15) return SCPE_ARG; + val[1] = (r << 4) & 0xf0; + val[0] = 0xf0 | opcode[j].opmask; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 1) return SCPE_ARG; + val[1] |= (r << 3) & 0x08; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%d", &r); + if (r > 7) return SCPE_ARG; + val[1] |= r & 0x07; + val[2] = 0; + oplen = 3; + break; + case 5: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 15) return SCPE_ARG; + val[1] = (r << 4) & 0xf0; + val[0] = 0xf0 | opcode[j].opmask; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 1) return SCPE_ARG; + val[1] |= (r << 3) & 0x08; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 7) return SCPE_ARG; + val[1] |= r & 0x07; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if (r > 255) return SCPE_ARG; + val[2] = r; + oplen = 3; + break; + case 6: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 15) return SCPE_ARG; + val[1] = (r << 4) & 0xf0; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 1) return SCPE_ARG; + val[1] |= (r << 3) & 0x08; + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + sscanf(gbuf, "%d", &r); + if (r > 7) return SCPE_ARG; + val[1] |= r & 0x07; + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + if (opcode[j].group == 1) + val[0] = 0xC0 | opcode[j].opmask; + else + val[0] = 0x30 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xD0 | opcode[j].opmask; + else + val[0] = 0x70 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + if (opcode[j].group == 1) + val[0] = 0xE0 | opcode[j].opmask; + else + val[0] = 0xB0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + break; + case 7: + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, 0); + sscanf(gbuf, "%x", &r); + if ((r - (addr+3)) > 255 || (r - (addr+3)) < 1) return SCPE_ARG; + val[2] = r - (addr+3); + val[1] = opcode[j].q; + val[0] = 0xf0 | opcode[j].opmask; + oplen = 3; + break; + + case 8: + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0x00ff; + val[3] = addr & 0xff; + oplen = 4; + val[0] = 0xC0 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen = 3; + val[0] = 0xD0 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen = 3; + val[0] = 0xE0 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + val[1] = opcode[j].q; + break; + case 9: + oplen = 2; + val[0] = 0; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[2] = (addr >> 8) & 0xff; + val[3] = addr & 0xff; + oplen += 2; + vptr = 4; + val[0] = 0x00 | opcode[j].opmask; + break; + case 1: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x40 | opcode[j].opmask; + break; + case 2: + val[2] = addr & 0xff; + oplen += 1; + vptr = 3; + val[0] = 0x80 | opcode[j].opmask; + break; + default: + return SCPE_ARG; + break; + } + if (*cptr == ',') cptr++; + cptr = parse_addr(cptr, gbuf, &addr, &addtyp); + switch(addtyp) { + case 0: + val[vptr] = (addr >> 8) & 0xff; + val[vptr+1] = addr & 0xff; + oplen += 2; + break; + case 1: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x10 | val[0]; + break; + case 2: + val[vptr] = addr & 0xff; + oplen += 1; + val[0] = 0x20 | val[0]; + break; + default: + return SCPE_ARG; + break; + } + val[1] = opcode[j].q; + break; + default: + break; +} + + +return (-(oplen-1)); +} + +char *parse_addr(char *cptr, char *gbuf, int32 *addr, int32 *addrtype) +{ +int32 nybble = 0; +char temp[32]; + +cptr = get_glyph(cptr, gbuf, ','); +if (gbuf[0] == '(') { /* XR relative */ + strcpy(temp, gbuf+1); + sscanf(temp, "%x", addr); + if (*cptr == ',') cptr++; + cptr = get_glyph(cptr, gbuf, ','); + nybble = -1; + if (strcmp(gbuf, "XR1)") == 0) + nybble = 1; + if (strcmp(gbuf, "XR2)") == 0) + nybble = 2; +} else { /* Direct */ + sscanf(gbuf, "%x", addr); + nybble = 0; +} +*addrtype = nybble; +return cptr; +} + diff --git a/S3/system3.txt b/S3/system3.txt new file mode 100644 index 0000000..e01a97d --- /dev/null +++ b/S3/system3.txt @@ -0,0 +1,472 @@ + The IBM System/3 simulator is configured as follows: + + CPU 5410 (Model 10) CPU with 64KB of memory. + PKB 5471 Printer/Keyboard console. + CDR 1442 Card Reader + CDP 1442 Card Punch + CDP2 1442 2nd stacker + LPT 1403 Line Printer + R1 5444 Top Drive (removeable) + F1 5444 Top Drive (fixed) + R2 5444 Bottom Drive (removeable) + F2 5444 Bottom Drive (fixed + + The only CPU options are to set Model 15 mode (not implemented), DPF +(Dual Programming Facility, not implemented), and the memory size 8K, 16K, +32K, 48K, or 64K. + + CPU registers are the standard System/3 set: + + name size Description + + IAR-P1 16 Instruction Address Register for Program Level 1 + ARR-P2 16 Address Recall Register for Program Level 1 + IAR-P2 16 IAR for Program Level 2 (not implemented) + ARR-P2 16 ARR for Program Level 2 (not implemented) + AAR 16 A-Address Register + BAR 16 B-Address Register + PSR 16 Program Status Register + XR1 16 Index Register 1 + XR2 16 Index Register 2 + IAR<0:7> 16 IAR for interrupt level 0 thru 7 + ARR<0:7> 16 ARR for interrupt level 0 thru 7 + + Plus these simulator registers: + + IAR 16 Value of last IAR used. + LEVEL 8 Current operating level (8=P1, 9=P2, + 0 thru 7 = Interrupt level) + SR 16 Front Panel switches + INT 16 Interrupt Request Flags + WRU 8 Simulator Interrupt Character + BREAK 17 Breakpoint Address + DEBUG 16 Debugging bits: + 0x01: Write all instructions executed to + file trace.log. + 0x02: Write details of all Disk I/O + requests to trace.log. + 0x80: Breakpoint on first character + written to 5471 printer. + +1 5471 Printer/Keyboard + + This is the operator console. It has the following registers: + + FLAG 5471 Flag Bytes + IBUF: Input character from keyboard + OBUF: Output character to printer + POS: Number of characters printed + TIME: Delay for device operation + REQKEY: ASCII value of key mapped to 5471 REQUEST key + RTNKEY: ASCII value of key mapped to 5471 RETURN key + ENDKEY: ASCII value of key mapped to 5471 END key + CANKEY: ASCII value of key mapped to 5471 CANCEL key + + +2 1442 Card Reader. This reader reads 80-column cards; the input + is usually an ASCII file which is translated to EBCDIC when read, + but optionally can be a binary file in EBCDIC format (such as an + object program). + + LAST Last card switch + ERR Card Reader Error + NOTRDY 1442 reader not ready (not attached or past EOF) + DAR Data Address Register (shared with punch) + LCR Length Count Register (shared with punch) + EBCDIC EBCDIC mode flag: if 1, input is 80-col EBCDIC binary. + (IPL from 1442 automatically sets this to 1). + S2 Stacker 2 is selected when this is 1 + POS Number of cards read + TIME Device Delay + + The real hardware 1442 had only 1 hopper for cards, whether + these were used for blank cards for punching, or cards to be + read. Cards could be read without a feed cycle, then + punched. When punching cards, the SCP does a read of a card, + makes sure it is blank, and then punches it. To simulate + this without requiring that a stack of blank lines be attached + to the card reader device, a special feature of the simulator + is this: if no file is attached to the cdr device, but a file + is attached to the cdp or the cdp2 devices, any read from the + reader will return a blank card -- i.e. when punching, an + unattached cdr device is assumed to be loaded with an unlimited + supply of blank cards. + + +3 1442 Card Punch. Normally cards are written to the attached + disk file as ASCII with newline/cr delimiters. But an optional + flag allows writing as 80-column binary EBCDIC. + + ERR Card Punch Error + EBCDIC When this is 1, output will be 80-col EBCDIC. + S2 When this is 1, output is placed in stacker 2 + NOTRDY 1442 punch not ready (not attached) + DAR Data Address Register (shared with reader) + LCR Length Count Register (shared with reader) + POS Number of cards punched + TIME Device Delay + +4 1442 Stacker 2. When cards are to be punched in stacker 2, + attach a disk file to this device (cdp2) to hold that output. + Note: When SCP punches cards, the default is to punch in + stacker 2. + + POS0 Number of cards punched. + +5 1403 Printer. This is a 132-column output device, emulating + the famous IBM 1403, models 2, 6, and N1. Output is always + translated to ASCII with newline/CR delimiters. Page advance + is output as a form feed. + + ERR 1403 error flags + LPDAR Data Address Register + LPFLR Forms Length Register + LPIAR Image Address Register + LINECT Current Line on form + POS Number of lines printed + +6 5444 Disk Drives (R1, R2, F1, F2) + + The 5444 came as a set of two drives, each with two disks. The + top disk in a drive was removable, the bottom fixed. The first + drive consists of disks R1 and F1, the second drive R2 and F2. + Each disk holds 2,467,600 bytes of user data, plus 3 alternate + tracks and a CE track. Flagging of alternate tracks is not + supported in this version of the simulator. + + NOTRDY Drive not ready (not attached) + SEEK Drive is busy with a seek operation + DAR Data Address Register + CAR Control Address Register + ERR Error Flags (16 bits) + CYL Current Cylinder (0 thru 203) + HEAD Current head (0 or 1) + POS Current position in attached disk file + TIME Device Delay + +7 Symbolic Display and Input + + The System/3 Simulator supports symbolic display and input. + Display is controlled by command line switches: + + (none) display as hex EBCDIC + -c display bytes as characters + -m display instruction mnemonics. + -a display a 256-byte block of memory in both hex and ASCII. + + The symbolic format contains the same elements as the machine + language operation, but not always in the same order. The + operation code frequently specifies both the opcode and the Q byte, + and the top nybble of the opcode is determined by the format of the + addresses. + + Addresses take two forms: the direct address in hex, or a relative + address specified thusly: (byte,XRx) where 'byte' is a 1-byte + offset, and XRx is either XR1 or XR2 for the two index registers. + Use these formats when 'address' is indicated below: + + When 'reg' is mentioned, a mnemonic may be used for the register, + thusly: + + IAR Instruction Address Register for the current program level + ARR Address Recall Register for the current program level + P1IAR IAR for Program Level 1 + P2IAR IAR for Program Level 2 + PSR Program Status Register + XR1 Index Register 1 + XR2 Index Register 2 + IARx IAR for the interrupt level x (x = 0 thru 7) + + All other operands mentioned below are single-byte hex, except for + the length (len) operand of the two-address instructions, which is a + decimal length in the range 1-256. + + In operations where there is a source and a destination, the + destination is always operand 1, the source is operand 2. + + No-address formats: + ------------------ + + HPL hex,hex Halt Program Level, the operands are the + Q and R bytes. + + + One-address formats: + ------------------- + + A reg,address Add to register + CLI address,byte Compare Logical Immediate + MVI address,byte Move Immediate + TBF address,mask Test Bits Off + TBN address,mask Test Bits On + SBF address,mask Set Bits Off + SBN address,mask Set Bits On + ST reg,address Store Register + L reg,address Load Register + LA reg,address Load Address (reg can only be XR1 or XR2) + JC address,cond Jump on Condition + BC address,cond Branch on Condition + + These operations do not specify a qbyte, it is implicit in the + opcode: + + B address Unconditional branch to address + BE address Branch Equal + BNE address Branch Not Equal + BH address Branch High + BNH address Branch Not High + BL address Branch Low + BNL address Branch Not Low + BT address Branch True + BF address Branch False + BP address Branch Plus + BM address Branch Minus + BNP address Branch Not Plus + BNM address Branch Not Minus + BZ address Branch Zero + BNZ address Branch Not Zero + BOZ address Branch Overflow Zoned + BOL address Branch Overflow Logical + BNOZ address Branch No Overflow Zoned + BNOL address Branch No Overflow Logical + NOPB address No - never branch + + (substitute J for B above for a set of Jumps -- 1-byte operand (not + 2), always jumps forward up to 255 bytes from the address following + the Jump instruction. In this case, 'address' cannot be less than + the current address, nor greater than the current address + 255) + + Two-address formats (first address is destination, len is decimal 1-256): + ------------------- + + MVC address,address,len Move Characters + CLC address,address,len Compare Logical Characters + ALC address,address,len Add Logical Characters + SLC address,address,len Subtract Logical Characters + ED address,address,len Edit + ITC address,address,len Insert and Test Characters + AZ address,address,len Add Zoned Decimal + SZ address,address,len Subtract Zoned Decimal + + MNN address,address Move Numeric to Numeric + MNZ address,address Move Numeric to Zone + MZZ address,address Move Zone to Zone + MZN address,address Move Zone to Numeric + + I/O Format + ---------- + + In the I/O format, there are always 3 fields: + + da - Device Address 0-15 (decimal) + m - Modifier 0-1 + n - Function 0-7 + + The meaning of these is entirely defined by the device addressed. + + There may be an optional control byte, or an optional address (based + on the type of instruction). + + SNS da,m,n,address Sense I/O + LIO da,m,n,address Load I/O + TIO da,m,n,address Test I/O + + SIO da,m,n,cc Start I/O -- cc is a control byte + + APL da,m,n Advance Program Level + + +8 Device Programming. + + Note: On a model 15, interrupts are used for all devices. On + other models, interrupts are only used for the printer/keyboard. + + This is a summary of the DA, M, N, and CC codes for all supported + devices: + + 5471 Printer Keyboard + --------------------- + + The PKB has 2 visible indicators: Proceed and Request + Pending. It has a normal keyboard and 4 special keys: + Request, Return, End, and Cancel. + + SIO 1,0,0,XX Start Keyboard Operation, bit masks for XX are: + X'20': Request Pending Indicator On + X'10': Proceed Indicator On + X'04': Request Key Interrupts Enable (1) Disable (0) + X'02': Other Key Interrupts Enable (1) Disable (0) + X'01': Reset request key and other key interrupts + + SIO 1,1,0,XX Start Printer Operation, bit masks for XX are: + X'80': Start Printing + X'40': Start Carrier Return + X'04': Printer Interrupt Enable(1) or Disable (0) + X'01': Reset Printer Interrupt + + LIO 1,1,0,addr Load Printer Output Character + addr is address of low-order (highest numbered) + byte of two-byte field, and high-order byte + (that is, addr - 1) is loaded into output + register to print. Printing is done one character + at a time. + + SNS 1,0,1,addr Sense Status Bytes 0 and 1: + Byte 0 (leftmost) is the character typed in + in EBCDIC. + Byte 1 is status: + X'80': Request key interrupt pending + X'40': End or Cancel key interrupt pending + X'20': Cancel key pressed + X'10': End Key Pressed + X'08': Return or data key interrupt pending + X'04': Return key pressed + + SNS 1,0,3,addr Sense Status Bytes 2 and 3: returns 0000 in + this sim. + + 1442 Reader/Punch + ----------------- + + SIO 5,0,0,XX Feed Card without reading/punching + XX is stacker select for all functions: 0 = stacker + 1 (normal), 1 = stacker 2. + + SIO 5,0,1,XX Read Card + SIO 5,0,2,XX Punch and Feed + SIO 5,0,3,XX Read Column Binary + SIO 5,0,4,XX Punch with no feed + + TIO 5,0,0,addr Branch to addr if not ready or busy + TIO 5,0,2,addr Branch to addr if busy + TIO 5,0,5,addr (mod 15 only) branch if interrupt pending + + APL 5,0,0 Loop (or switch prog levels) if not ready/busy + APL 5,0,2 Loop (or switch) if busy + APL 5,0,5 Loop (or switch) if interrupt pending (mod 15 only) + + LIO 5,0,0,addr Load 2-byte field to Length Count Register + LIO 5,0,4,addr Load 2-byte field to Data Address Register + (DAR is incremented by a read/punch operation and must + be reset every card) + + SNS 5,0,1,addr Sense CE indicators (0000 returned in this sim) + SNS 5,0,2,addr Sense CE indicators (0000 returned in this sim) + SNS 5,0,3,addr Sense Status Indicators: (only simulated bits shown) + X'8000': Read Check + X'4000': Last Card + X'2000': Punch Check + X'1000': Data Overrun + X'0800': Not Ready + + + 1403 Printer + ------------ + + SIO 14,0,0,XX Line space XX lines (0-3 valid in XX) + SIO 14,0,2,XX Print a line space (0-3 valid in XX) + SIO 14,0,4,XX Skip Only (line number 1-112 in XX) + + SIO 14,0,6,XX Print and Skip (line number 0-112 in XX) + + TIO 14,0,0,addr Branch to addr if not ready + TIO 14,0,2,addr Branch to addr if buffer busy + TIO 14,0,3,addr Branch to addr if interrupt pending (mod 15 only) + TIO 14,0,4,addr Branch if carriage busy + TIO 14,0,6,addr Branch if printer busy + + APL 14,0,0 Loop (or switch prog levels) if not ready/check + APL 14,0,2 Loop (or switch) if buffer busy + APL 14,0,3 Loop (or switch) if interrupt pending (mod 15 only) + APL 14,0,4 Loop (or switch) if carriage busy + APL 14,0,6 Loop (or switch) if printer busy + + LIO 14,0,0,addr Load 1 byte to Forms Length Reg at addr-1 + LIO 14,0,4,addr Load 2 bytes to Chain Image Address Register + LIO 14,0,6,addr Load 2 bytes to Data Address Register + + SNS 14,0,0,addr Sense Character Count + SNS 14,0,4,addr Sense LPIAR (Image Address Register) + SNS 14,0,6,addr Sense LPDAR (data addres register) + + + 5444 Disk Drives + ---------------- + + Each drive has two disks (upper and lower), each disk + has two surfaces (upper and lower), each surface has + 24 256-byte sectors, sectors are number 0 thru 23 on + upper surface, 32 thru 55 on lower. + + d = drive, 0 is R1/F1, 1 is R2/F2 + s = surface, 0 = upper (removable), 1 = lower (fixed) + + The Control register points to the leftmost byte of a 4-byte + control field in memory with these bytes: + + F - Flag byte (not supported in this sim) + C - Cylinder Address (0-203) + S - Sector Number (0-23, or 32-55) in top 6 bits + N - Number of sectors minus 1 + + These have meaning for all operations except seek, seek uses + the fields differently. + + SIO 1d,s,0,XX Seek, XX not used, control field is used: + + F - not used + C - not used + S - high bit is head to be used 0-upper 1-lower + low bit is direction to move 0-back 1-forward + N - number of cylinders to move + + SIO 1d,s,1,XX Read, values of XX are as follows: + X'00': Read Data + X'01': Read Identifier (updates control field, no + data is read) + X'02': Read Data Diagnostic + X'03': Verify (does not read, but checks) + + SIO 1d,s,2,XX Write, values of XX are as follows: + X'00': Write Data + X'01': Write Identifier (24 sectors with byte at + data address register) + + SIO 1d,s,3,XX Scan. All 256 bytes in memory at data + address register are compared to disk + sectors on current track, except those + bytes of X'FF' are not compared. Values of + XX are: + + X'00': Scan Equal + X'01': Scan Low or Equal + X'02': Scan High or Equal + + LIO 1d,0,4,addr Load Data Address Register + LIO 1d,0,6,addr Load Disk Control Address Register + + TIO 1d,0,0,addr Branch if not ready/unit check + TIO 1d,0,2,addr Branch if busy + TIO 1d,0,4,addr Branch if Scan Found + + APL 1d,0,0 Loop if not ready/unit check + APL 1d,0,2 Loop if busy + APL 1d,0,4 Loop if scan found + + SNS 1d,0,2,addr Sense Status Bytes 0 and 1: (simulated + bits only are shown, otehrs are 0): + X'1000': equipment check + X'0800': data check + X'0400': No record found + X'0100': Seek Check (past cyl 203) + X'0080': Scan equal Hit + X'0040': Cylinder 0 + X'0020': End of Cylinder + X'0010': Seek Busy + SNS 1d,0,3,addr Sense bytes 2 and 3 (0000 in this sim) + SNS 1d,0,4,addr Sense Data Address Register + SNS 1d,0,6,addr Sense Control Address Register + + + + diff --git a/SDS/sds_cpu.c b/SDS/sds_cpu.c new file mode 100644 index 0000000..38c2e80 --- /dev/null +++ b/SDS/sds_cpu.c @@ -0,0 +1,1502 @@ +/* sds_cpu.c: SDS 940 CPU simulator + + Copyright (c) 2001-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu central processor + rtc real time clock + + 28-Apr-07 RMS Removed clock initialization + 29-Dec-06 RMS Fixed breakpoint variable declarations + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 07-Nov-04 RMS Added instruction history + 01-Mar-03 RMS Added SET/SHOW RTC FREQ support + + The system state for the SDS 940 is: + + A<0:23> A register + B<0:23> B register + X<0:23> X (index) register + OV overflow indicator + P<0:13> program counter + nml_mode compatible (1) vs 940 (0) mode + usr_mode user (1) vs monitor (0) mode + RL1<0:23> user map low + RL2<0:23> user map high + RL4<12:23> monitor map high + EM2<0:2> memory extension, block 2 + EM3<0:2> memory extension, block 3 + bpt breakpoint switches + + The SDS 940 has three instruction format -- memory reference, register change, + and I/O. The memory reference format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | U| X| P| opcode |IN| address | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + U force user mode addressing (monitor mode only) + X indexed + P opcode is a programmed operator + opcode opcode + IN indirect addressing + address virtual address + + Virtual addresses are 14b. Depending on the operating mode (normal, user, + or monitor), virtual addresses are translated to 15b or 16b physical addresses. + + normal virtual [000000:017777] are unmapped + EM2 and EM3 extend virtual [020000:037777] to 15b + user RL1 and RL2 map virtual [000000:037777] to 16b + monitor virtual [000000:017777] are unmapped + EM2 extends virtual [020000:027777] to 15b + RL4 maps virtual [030000:037777] to 16b + + The register change format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0| m| 0| opcode | microcoded register change instruction | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + The I/O format is: + + 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 23 23 + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + | 0|CH| 0| opcode |mode | I/O function | + +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+ + + This routine is the instruction decode routine for the SDS 940. + It is called from the simulator control program to execute + instructions in simulated memory, starting at the simulated PC. + It runs until 'reason' is set non-zero. + + General notes: + + 1. Reasons to stop. The simulator can be stopped by: + + HALT instruction + breakpoint encountered + invalid instruction and stop_invins flag set + invalid I/O device and stop_invdev flag set + invalid I/O operation and stop_inviop flag set + I/O error in I/O simulator + indirect loop exceeding limit + EXU loop exceeding limit + mapping exception in interrupt or trap instruction + + 2. Interrupts. The interrupt structure consists of the following: + + int_req interrupt requests (low bit reserved) + api_lvl active interrupt levels + int_reqhi highest interrupt request + api_lvlhi highest interrupt service (0 if none) + ion interrupt enable + ion_defer interrupt defer (one instruction) + + 3. Channels. The SDS 940 has a channel-based I/O structure. Each + channel is represented by a set of registers. Channels test the + I/O transfer requests from devices, which are kept in xfr_req. + + 4. Non-existent memory. On the SDS 940, reads to non-existent memory + return zero, and writes are ignored. In the simulator, the + largest possible memory is instantiated and initialized to zero. + Thus, only writes need be checked against actual memory size. + + 5. Adding I/O devices. These modules must be modified: + + sds_defs.h add interrupt, transfer, and alert definitions + sds_io.c add alert dispatches aldisp + sds_sys.c add pointer to data structures to sim_devices +*/ + +#include "sds_defs.h" + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = pc +#define UNIT_V_MSIZE (UNIT_V_GENIE + 1) /* dummy mask */ +#define UNIT_MSIZE (1 << UNIT_V_MSIZE) + +#define HIST_XCT 1 /* instruction */ +#define HIST_INT 2 /* interrupt cycle */ +#define HIST_TRP 3 /* trap cycle */ +#define HIST_MIN 64 +#define HIST_MAX 65536 +#define HIST_NOEA 0x40000000 + +typedef struct { + uint32 typ; + uint32 pc; + uint32 ir; + uint32 a; + uint32 b; + uint32 x; + uint32 ea; + } InstHistory; + +uint32 M[MAXMEMSIZE] = { 0 }; /* memory */ +uint32 A, B, X; /* registers */ +uint32 P; /* program counter */ +uint32 OV; /* overflow */ +uint32 xfr_req = 0; /* xfr req */ +uint32 ion = 0; /* int enable */ +uint32 ion_defer = 0; /* int defer */ +uint32 int_req = 0; /* int requests */ +uint32 int_reqhi = 0; /* highest int request */ +uint32 api_lvl = 0; /* api active */ +uint32 api_lvlhi = 0; /* highest api active */ +t_bool chan_req; /* chan request */ +uint32 nml_mode = 1; /* normal mode */ +uint32 usr_mode = 0; /* user mode */ +uint32 mon_usr_trap = 0; /* mon-user trap */ +uint32 EM2 = 2, EM3 = 3; /* extension registers */ +uint32 RL1, RL2, RL4; /* relocation maps */ +uint32 bpt; /* breakpoint switches */ +uint32 alert; /* alert dispatch */ +uint32 em2_dyn, em3_dyn; /* extensions, dynamic */ +uint32 usr_map[8]; /* user map, dynamic */ +uint32 mon_map[8]; /* mon map, dynamic */ +int32 ind_lim = 32; /* indirect limit */ +int32 exu_lim = 32; /* EXU limit */ +int32 cpu_genie = 0; /* Genie flag */ +int32 cpu_astop = 0; /* address stop */ +int32 stop_invins = 1; /* stop inv inst */ +int32 stop_invdev = 1; /* stop inv dev */ +int32 stop_inviop = 1; /* stop inv io op */ +uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +int32 pcq_p = 0; /* PC queue ptr */ +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +InstHistory *hst = NULL; /* instruction history */ +int32 rtc_pie = 0; /* rtc pulse ie */ +int32 rtc_tps = 60; /* rtc ticks/sec */ + +extern int32 sim_int_char; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_type (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat Ea (uint32 wd, uint32 *va); +t_stat EaSh (uint32 wd, uint32 *va); +t_stat Read (uint32 va, uint32 *dat); +t_stat Write (uint32 va, uint32 dat); +void set_dyn_map (void); +uint32 api_findreq (void); +void api_dismiss (void); +uint32 Add24 (uint32 s1, uint32 s2, uint32 cin); +uint32 AddM24 (uint32 s1, uint32 s2); +void Mul48 (uint32 mplc, uint32 mplr); +void Div48 (uint32 dvdh, uint32 dvdl, uint32 dvr); +void RotR48 (uint32 sc); +void ShfR48 (uint32 sc, uint32 sgn); +t_stat one_inst (uint32 inst, uint32 pc, uint32 mode); +void inst_hist (uint32 inst, uint32 pc, uint32 typ); +t_stat rtc_inst (uint32 inst); +t_stat rtc_svc (UNIT *uptr); +t_stat rtc_reset (DEVICE *dptr); +t_stat rtc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc); + +extern t_bool io_init (void); +extern t_stat op_wyim (uint32 inst, uint32 *dat); +extern t_stat op_miwy (uint32 inst, uint32 dat); +extern t_stat op_pin (uint32 *dat); +extern t_stat op_pot (uint32 dat); +extern t_stat op_eomd (uint32 inst); +extern t_stat op_sks (uint32 inst, uint32 *skp); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit descriptor + cpu_reg CPU register list + cpu_mod CPU modifiers list +*/ + +UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, MAXMEMSIZE) }; + +REG cpu_reg[] = { + { ORDATA (P, P, 14) }, + { ORDATA (A, A, 24) }, + { ORDATA (B, B, 24) }, + { ORDATA (X, X, 24) }, + { FLDATA (OV, OV, 0) }, + { ORDATA (EM2, EM2, 3) }, + { ORDATA (EM3, EM3, 3) }, + { ORDATA (RL1, RL1, 24) }, + { ORDATA (RL2, RL2, 24) }, + { ORDATA (RL4, RL4, 12) }, + { FLDATA (NML, nml_mode, 0) }, + { FLDATA (USR, usr_mode, 0) }, + { FLDATA (MONUSR, mon_usr_trap, 0) }, + { FLDATA (ION, ion, 0) }, + { FLDATA (INTDEF, ion_defer, 0) }, + { ORDATA (INTREQ, int_req, 32) }, + { ORDATA (APILVL, api_lvl, 32) }, + { DRDATA (INTRHI, int_reqhi, 5) }, + { DRDATA (APILHI, api_lvlhi, 5), REG_RO }, + { ORDATA (XFRREQ, xfr_req, 32) }, + { FLDATA (BPT1, bpt, 3) }, + { FLDATA (BPT2, bpt, 2) }, + { FLDATA (BPT3, bpt, 1) }, + { FLDATA (BPT4, bpt, 0) }, + { ORDATA (ALERT, alert, 6) }, + { FLDATA (STOP_INVINS, stop_invins, 0) }, + { FLDATA (STOP_INVDEV, stop_invdev, 0) }, + { FLDATA (STOP_INVIOP, stop_inviop, 0) }, + { DRDATA (INDLIM, ind_lim, 8), REG_NZ+PV_LEFT }, + { DRDATA (EXULIM, exu_lim, 8), REG_NZ+PV_LEFT }, + { BRDATA (PCQ, pcq, 8, 14, PCQ_SIZE), REG_RO+REG_CIRC }, + { ORDATA (PCQP, pcq_p, 6), REG_HRO }, + { ORDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_GENIE, 0, "standard peripherals", "SDS", &cpu_set_type }, + { UNIT_GENIE, UNIT_GENIE, "Genie peripherals", "GENIE", &cpu_set_type }, + { UNIT_MSIZE, 16384, NULL, "16K", &cpu_set_size }, + { UNIT_MSIZE, 32768, NULL, "32K", &cpu_set_size }, + { UNIT_MSIZE, 49152, NULL, "48K", &cpu_set_size }, + { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 8, 16, 1, 8, 24, + &cpu_ex, &cpu_dep, &cpu_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* Clock data structures + + rtc_dev RTC device descriptor + rtc_unit RTC unit + rtc_reg RTC register list +*/ + +UNIT rtc_unit = { UDATA (&rtc_svc, 0, 0), 16000 }; + +REG rtc_reg[] = { + { FLDATA (PIE, rtc_pie, 0) }, + { DRDATA (TIME, rtc_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, rtc_tps, 8), PV_LEFT + REG_HRO }, + { NULL } + }; + +MTAB rtc_mod[] = { + { MTAB_XTD|MTAB_VDV, 50, NULL, "50HZ", + &rtc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 60, NULL, "60HZ", + &rtc_set_freq, NULL, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "FREQUENCY", NULL, + NULL, &rtc_show_freq, NULL }, + { 0 } + }; + +DEVICE rtc_dev = { + "RTC", &rtc_unit, rtc_reg, rtc_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &rtc_reset, + NULL, NULL, NULL + }; + +/* Interrupt tables */ + +static const uint32 api_mask[32] = { + 0xFFFFFFFE, 0xFFFFFFFC, 0xFFFFFFF8, 0xFFFFFFF0, + 0xFFFFFFE0, 0xFFFFFFC0, 0xFFFFFF80, 0xFFFFFF00, + 0xFFFFFE00, 0xFFFFFC00, 0xFFFFF800, 0xFFFFF000, + 0xFFFFE000, 0xFFFFC000, 0xFFFF8000, 0xFFFF0000, + 0xFFFE0000, 0xFFFC0000, 0xFFF80000, 0xFFF00000, + 0xFFE00000, 0xFFC00000, 0xFF800000, 0xFF000000, + 0xFE000000, 0xFC000000, 0xF8000000, 0xF0000000, + 0xE0000000, 0xC0000000, 0x80000000, 0x00000000 + }; + +static const uint32 int_vec[32] = { + 0, 0, 0, 0, + VEC_FORK, VEC_DRM, VEC_MUXCF,VEC_MUXCO, + VEC_MUXT, VEC_MUXR, VEC_HEOR, VEC_HZWC, + VEC_GEOR, VEC_GZWC, VEC_FEOR, VEC_FZWC, + VEC_EEOR, VEC_EZWC, VEC_DEOR, VEC_DZWC, + VEC_CEOR, VEC_CZWC, VEC_WEOR, VEC_YEOR, + VEC_WZWC, VEC_YZWC, VEC_RTCP, VEC_RTCS, + VEC_IPAR, VEC_CPAR, VEC_PWRF, VEC_PWRO + }; + +t_stat sim_instr (void) +{ +extern int32 sim_interval; +uint32 inst, tinst, pa, save_P, save_mode; +t_stat reason, tr; + +/* Restore register state */ + +if (io_init ()) return SCPE_STOP; /* init IO; conflict? */ +reason = 0; +xfr_req = xfr_req & ~1; /* <0> reserved */ +int_req = int_req & ~1; /* <0> reserved */ +api_lvl = api_lvl & ~1; /* <0> reserved */ +set_dyn_map (); /* set up mapping */ +int_reqhi = api_findreq (); /* recalc int req */ +chan_req = chan_testact (); /* recalc chan act */ + +/* Main instruction fetch/decode loop */ + +while (reason == 0) { /* loop until halted */ + + if (cpu_astop) { /* debug stop? */ + cpu_astop = 0; + return SCPE_STOP; + } + + if (sim_interval <= 0) { /* event queue? */ + if (reason = sim_process_event ()) break; /* process */ + int_reqhi = api_findreq (); /* recalc int req */ + chan_req = chan_testact (); /* recalc chan act */ + } + + if (chan_req) { /* channel request? */ + if (reason = chan_process ()) break; /* process */ + int_reqhi = api_findreq (); /* recalc int req */ + chan_req = chan_testact (); /* recalc chan act */ + } + + sim_interval = sim_interval - 1; /* count down */ + if (ion && !ion_defer && int_reqhi) { /* int request? */ + pa = int_vec[int_reqhi]; /* get vector */ + if (pa == 0) { /* bad value? */ + reason = STOP_ILLVEC; + break; + } + tinst = ReadP (pa); /* get inst */ + save_mode = usr_mode; /* save mode */ + usr_mode = 0; /* switch to mon */ + if (hst_lnt) inst_hist (tinst, P, HIST_INT); /* record inst */ + if (pa != VEC_RTCP) { /* normal intr? */ + tr = one_inst (tinst, P, save_mode); /* exec intr inst */ + if (tr) { /* stop code? */ + usr_mode = save_mode; /* restore mode */ + reason = (tr > 0)? tr: STOP_MMINT; + break; + } + api_lvl = api_lvl | (1u << int_reqhi); /* set level active */ + api_lvlhi = int_reqhi; /* elevate api */ + } + else { /* clock intr */ + tr = rtc_inst (tinst); /* exec RTC inst */ + usr_mode = save_mode; /* restore mode */ + if (tr) { /* stop code? */ + reason = (tr > 0)? tr: STOP_MMINT; + break; + } + int_req = int_req & ~INT_RTCP; /* clr clkp intr */ + } + int_reqhi = api_findreq (); /* recalc int req */ + } + else { /* normal instr */ + if (sim_brk_summ && sim_brk_test (P, SWMASK ('E'))) { /* breakpoint? */ + reason = STOP_IBKPT; /* stop simulation */ + break; + } + reason = Read (save_P = P, &inst); /* get instr */ + P = (P + 1) & VA_MASK; /* incr PC */ + if (reason == SCPE_OK) { /* fetch ok? */ + ion_defer = 0; /* clear ion */ + if (hst_lnt) inst_hist (inst, save_P, HIST_XCT); + reason = one_inst (inst, save_P, usr_mode); /* exec inst */ + if (reason > 0) { /* stop code? */ + if (reason != STOP_HALT) P = save_P; + if (reason == STOP_IONRDY) reason = 0; + } + } /* end if r == 0 */ + if (reason < 0) { /* mm (fet or ex)? */ + pa = -reason; /* get vector */ + reason = 0; /* defang */ + tinst = ReadP (pa); /* get inst */ + if (I_GETOP (tinst) != BRM) { /* not BRM? */ + reason = STOP_TRPINS; /* fatal err */ + break; + } + save_mode = usr_mode; /* save mode */ + usr_mode = 0; /* switch to mon */ + mon_usr_trap = 0; + if (hst_lnt) inst_hist (tinst, save_P, HIST_TRP); + tr = one_inst (tinst, save_P, save_mode); /* trap inst */ + if (tr) { /* stop code? */ + usr_mode = save_mode; /* restore mode */ + P = save_P; /* restore PC */ + reason = (tr > 0)? tr: STOP_MMTRP; + break; + } + } /* end if reason */ + } /* end else int */ + } /* end while */ + +/* Simulation halted */ + +pcq_r->qptr = pcq_p; /* update pc q ptr */ +return reason; +} + +/* Simulate one instruction */ + +t_stat one_inst (uint32 inst, uint32 pc, uint32 mode) +{ +uint32 op, shf_op, va, dat; +uint32 old_A, old_B, old_X; +int32 i, exu_cnt, sc; +t_stat r; + +exu_cnt = 0; /* init EXU count */ +EXU_LOOP: +op = I_GETOP (inst); /* get opcode */ +if (inst & I_POP) { /* POP? */ + dat = (EM3 << 18) | (EM2 << 15) | I_IND | pc; /* data to save */ + if (nml_mode) { /* normal mode? */ + dat = (OV << 23) | dat; /* ov in <0> */ + WriteP (0, dat); + } + else if (usr_mode) { /* user mode? */ + if (inst & I_USR) { /* SYSPOP? */ + dat = I_USR | (OV << 21) | dat; /* ov in <2> */ + WriteP (0, dat); + usr_mode = 0; /* set mon mode */ + } + else { /* normal POP */ + dat = (OV << 23) | dat; /* ov in <0> */ + if (r = Write (0, dat)) return r; + } + } + else { /* mon mode */ + dat = (OV << 21) | dat; /* ov in <2> */ + WriteP (0, dat); /* store return */ + } + PCQ_ENTRY; /* save PC */ + P = 0100 | op; /* new PC */ + OV = 0; /* clear ovflo */ + return SCPE_OK; /* end POP */ + } + +switch (op) { /* case on opcode */ + +/* Loads and stores */ + + case LDA: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &A)) return r; /* get operand */ + break; + + case LDB: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &B)) return r; /* get operand */ + break; + + case LDX: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &X)) return r; /* get operand */ + break; + + case STA: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Write (va, A)) return r; /* write operand */ + break; + + case STB: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Write (va, B)) return r; /* write operand */ + break; + + case STX: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Write (va, X)) return r; /* write operand */ + break; + + case EAX: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (nml_mode || usr_mode) /* normal or user? */ + X = (X & ~VA_MASK) | (va & VA_MASK); /* only 14b */ + else X = (X & ~XVA_MASK) | (va & XVA_MASK); /* mon, 15b */ + break; + + case XMA: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (r = Write (va, A)) return r; /* write A */ + A = dat; /* load A */ + break; + +/* Arithmetic and logical */ + + case ADD: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + A = Add24 (A, dat, 0); /* add */ + break; + + case ADC: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + OV = 0; /* clear overflow */ + A = Add24 (A, dat, X >> 23); /* add with carry */ + break; + + case SUB: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + A = Add24 (A, dat ^ DMASK, 1); /* subtract */ + break; + + case SUC: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + OV = 0; /* clear overflow */ + A = Add24 (A, dat ^ DMASK, X >> 23); /* sub with carry */ + break; + + case ADM: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + dat = AddM24 (dat, A); /* mem + A */ + if (r = Write (va, dat)) return r; /* rewrite */ + break; + + case MIN: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + dat = AddM24 (dat, 1); /* mem + 1 */ + if (r = Write (va, dat)) return r; /* rewrite */ + break; + + case MUL: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + Mul48 (A, dat); /* multiply */ + break; + + case DIV: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + Div48 (A, B, dat); /* divide */ + break; + + case ETR: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + A = A & dat; /* and */ + break; + + case MRG: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + A = A | dat; /* or */ + break; + + case EOR: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + A = A ^ dat; /* xor */ + break; + +/* Skips */ + + case SKE: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (A == dat) P = (P + 1) & VA_MASK; /* if A = op, skip */ + break; + + case SKG: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (SXT (A) > SXT (dat)) P = (P + 1) & VA_MASK; /* if A > op, skip */ + break; + + case SKM: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (((A ^ dat) & B) == 0) P = (P + 1) & VA_MASK; /* if A = op masked */ + break; + + case SKA: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if ((A & dat) == 0) P = (P + 1) & VA_MASK; /* if !(A & op), skip */ + break; + + case SKB: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if ((B & dat) == 0) P = (P + 1) & VA_MASK; /* if !(B & op), skip */ + break; + + case SKN: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (dat & SIGN) P = (P + 1) & VA_MASK; /* if op < 0, skip */ + break; + + case SKR: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + dat = AddM24 (dat, DMASK); /* decr operand */ + if (r = Write (va, dat)) return r; /* rewrite operand */ + if (dat & SIGN) P = (P + 1) & VA_MASK; /* if op < 0, skip */ + break; + + case SKD: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (SXT_EXP (B) < SXT_EXP (dat)) { /* B < dat? */ + X = (dat - B) & DMASK; /* X = dat - B */ + P = (P + 1) & VA_MASK; /* skip */ + } + else X = (B - dat) & DMASK; /* X = B - dat */ + break; + +/* Control */ + + case NOP: + break; + + case HLT: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + return STOP_HALT; /* halt CPU */ + + case EXU: + exu_cnt = exu_cnt + 1; /* count chained EXU */ + if (exu_cnt > exu_lim) return STOP_EXULIM; /* too many? */ + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + inst = dat; + goto EXU_LOOP; + + case BRU: + if (nml_mode && (inst & I_IND)) api_dismiss (); /* normal BRU*, dism */ + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* test dest access */ + PCQ_ENTRY; + P = va & VA_MASK; /* branch */ + break; + + case BRX: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + X = (X + 1) & DMASK; /* incr X */ + if (X & I_IND) { /* bit 9 set? */ + if (r = Read (va, &dat)) return r; /* test dest access */ + PCQ_ENTRY; + P = va & VA_MASK; /* branch */ + } + break; + + case BRM: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + dat = (EM3 << 18) | (EM2 << 15) | pc; /* form return word */ + if (!nml_mode && !usr_mode) /* monitor mode? */ + dat = dat | (mode << 23) | (OV << 21); + else dat = dat | (OV << 23); /* normal or user */ + if (r = Write (va, dat)) return r; /* write ret word */ + PCQ_ENTRY; + P = (va + 1) & VA_MASK; /* branch */ + break; + + case BRR: + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + PCQ_ENTRY; + P = (dat + 1) & VA_MASK; /* branch */ + if (!nml_mode && !usr_mode) { /* monitor mode? */ + OV = OV | ((dat >> 21) & 1); /* restore OV */ + if ((va & VA_USR) | (dat & I_USR)) { /* mode change? */ + usr_mode = 1; + if (mon_usr_trap) return MM_MONUSR; + } + } + else OV = OV | ((dat >> 23) & 1); /* restore OV */ + break; + + case BRI: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + api_dismiss (); /* dismiss hi api */ + PCQ_ENTRY; + P = dat & VA_MASK; /* branch */ + if (!nml_mode) { /* monitor mode? */ + OV = (dat >> 21) & 1; /* restore OV */ + if ((va & VA_USR) | (dat & I_USR)) { /* mode change? */ + usr_mode = 1; + if (mon_usr_trap) return MM_MONUSR; + } + } + else OV = (dat >> 23) & 1; /* restore OV */ + break; + +/* Register change (microprogrammed) */ + + case RCH: + old_A = A; /* save orig reg */ + old_B = B; + old_X = X; + if (inst & 000001211) { /* A change? */ + if (inst & 01000) dat = (~old_A + 1) & DMASK; /* CNA */ + else dat = 0; + if (inst & 00200) dat = dat | old_X; + if (inst & 00010) dat = dat | old_B; + if (inst & 00100) A = (A & ~EXPMASK) | (dat & EXPMASK); + else A = dat; + } + if (inst & 000000046) { /* B change? */ + if (inst & 00040) dat = old_X; + else dat = 0; + if (inst & 00004) dat = dat | old_A; + if (inst & 00100) B = (B & ~EXPMASK) | (dat & EXPMASK); + else B = dat; + } + if (inst & 020000420) { /* X change? */ + if (inst & 00400) dat = old_A; + else dat = 0; + if (inst & 00020) dat = dat | old_B; + if (inst & 00100) X = SXT_EXP (dat) & DMASK; + else X = dat; + } + break; + +/* Overflow instruction */ + + case OVF: + if ((inst & 0100) & OV) P = (P + 1) & VA_MASK; + if (inst & 0001) OV = 0; + if ((inst & 0010) && (((X >> 1) ^ X) & EXPS)) OV = 1; + break; + +/* Shifts */ + + case RSH: + if (r = EaSh (inst, &va)) return r; /* decode eff addr */ + shf_op = I_GETSHFOP (va); /* get eff op */ + sc = va & I_SHFMSK; /* get eff count */ + switch (shf_op) { /* case on sub-op */ + case 00: /* right arithmetic */ + if (sc) ShfR48 (sc, (A & SIGN)? DMASK: 0); + break; + case 04: /* right cycle */ + sc = sc % 48; /* mod 48 */ + if (sc) RotR48 (sc); + break; + case 05: /* right logical */ + if (sc) ShfR48 (sc, 0); + break; + default: + CRETINS; /* invalid inst */ + break; + } /* end case shf op */ + break; + + case LSH: + if (r = EaSh (inst, &va)) return r; /* decode eff addr */ + shf_op = I_GETSHFOP (va); /* get eff op */ + sc = va & I_SHFMSK; /* get eff count */ + switch (shf_op) { /* case on sub-op */ + case 00: /* left arithmetic */ + dat = A; /* save sign */ + if (sc > 48) sc = 48; + for (i = 0; i < sc; i++) { /* loop */ + A = ((A << 1) | (B >> 23)) & DMASK; + B = (B << 1) & DMASK; + if ((A ^ dat) & SIGN) OV = 1; + } + break; + case 02: /* normalize */ + if (sc > 48) sc = 48; + for (i = 0; i < sc; i++) { /* until max count */ + if ((A ^ (A << 1)) & SIGN) break; + A = ((A << 1) | (B >> 23)) & DMASK; + B = (B << 1) & DMASK; + } + X = (X - i) & DMASK; + break; + case 04: /* left cycle */ + sc = sc % 48; /* mod 48 */ + if (sc) RotR48 (48 - sc); /* rotate */ + break; + case 06: /* cycle normalize */ + if (sc > 48) sc = 48; + for (i = 0; i < sc; i++) { /* until max count */ + if ((A ^ (A << 1)) & SIGN) break; + old_A = A; /* cyclic shift */ + A = ((A << 1) | (B >> 23)) & DMASK; + B = ((B << 1) | (old_A >> 23)) & DMASK; + } + X = (X - i) & DMASK; + break; + default: + CRETINS; /* invalid inst */ + break; + } /* end case shf op */ + break; + +/* I/O instructions */ + + case MIW: case MIY: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (r = op_miwy (inst, dat)) return r; /* process inst */ + int_reqhi = api_findreq (); /* recalc int req */ + chan_req = chan_testact (); /* recalc chan act */ + break; + + case WIM: case YIM: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = op_wyim (inst, &dat)) return r; /* process inst */ + if (r = Write (va, dat)) return r; /* write result */ + int_reqhi = api_findreq (); /* recalc int req */ + chan_req = chan_testact (); /* recalc chan act */ + break; + + case EOM: case EOD: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + if (r = op_eomd (inst)) return r; /* process inst */ + int_reqhi = api_findreq (); /* recalc int req */ + chan_req = chan_testact (); /* recalc chan act */ + ion_defer = 1; + break; + + case POT: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = Read (va, &dat)) return r; /* get operand */ + if (r = op_pot (dat)) return r; /* process inst */ + int_reqhi = api_findreq (); /* recalc int req */ + chan_req = chan_testact (); /* recalc chan act */ + break; + + case PIN: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + if (r = Ea (inst, &va)) return r; /* decode eff addr */ + if (r = op_pin (&dat)) return r; /* process inst */ + if (r = Write (va, dat)) return r; /* write result */ + int_reqhi = api_findreq (); /* recalc int req */ + chan_req = chan_testact (); /* recalc chan act */ + break; + + case SKS: + if (!nml_mode && usr_mode) return MM_PRVINS; /* priv inst */ + if (r = op_sks (inst, &dat)) return r; /* process inst */ + if (dat) P = (P + 1) & VA_MASK; + break; + + default: + if (!nml_mode && usr_mode) return MM_PRVINS; /* usr? priv viol */ + CRETINS; /* invalid inst */ + break; + } + +return SCPE_OK; +} + +/* Effective address calculation */ + +t_stat Ea (uint32 inst, uint32 *addr) +{ +int32 i; +uint32 wd = inst; /* homeable */ +uint32 va = wd & XVA_MASK; /* initial va */ +t_stat r; + +for (i = 0; i < ind_lim; i++) { /* count indirects */ + if (wd & I_IDX) va = (va & VA_USR) | ((va + X) & VA_MASK); + *addr = va; + if ((wd & I_IND) == 0) { /* end of ind chain? */ + if (hst_lnt) hst[hst_p].ea = *addr; /* record */ + return SCPE_OK; + } + if (r = Read (va, &wd)) return r; /* read ind; fails? */ + va = (va & VA_USR) | (wd & XVA_MASK); + } +return STOP_INDLIM; /* too many indirects */ +} + +/* Effective address calculation for shifts - direct indexing is 9b */ + +t_stat EaSh (uint32 inst, uint32 *addr) +{ +int32 i; +uint32 wd = inst; /* homeable */ +uint32 va = wd & XVA_MASK; /* initial va */ +t_stat r; + +for (i = 0; i < ind_lim; i++) { /* count indirects */ + if ((wd & I_IND) == 0) { /* end of ind chain? */ + if (wd & I_IDX) *addr = (va & (VA_MASK & ~I_SHFMSK)) | + ((va + X) & I_SHFMSK); /* 9b indexing */ + else *addr = va & VA_MASK; + if (hst_lnt) hst[hst_p].ea = *addr; /* record */ + return SCPE_OK; + } + if (wd & I_IDX) va = (va & VA_USR) | ((va + X) & VA_MASK); + if (r = Read (va, &wd)) return r; /* read ind; fails? */ + va = (va & VA_USR) | (wd & XVA_MASK); + } +return STOP_INDLIM; /* too many indirects */ +} + +/* Read word from virtual address */ + +t_stat Read (uint32 va, uint32 *dat) +{ +uint32 pgn, map, pa; + +if (nml_mode) { /* normal? */ + va = va & VA_MASK; /* ignore user */ + if (va < 020000) pa = va; /* first 8K: 1 for 1 */ + else if (va < 030000) pa = va + em2_dyn; /* next 4K: ext EM2 */ + else pa = va + em3_dyn; /* next 4K: ext EM3 */ + } +else if (usr_mode || (va & VA_USR)) { /* user mapping? */ + pgn = VA_GETPN (va); /* get page no */ + map = usr_map[pgn]; /* get map entry */ + if (map == MAP_PROT) return MM_NOACC; /* prot? no access */ + pa = (map & ~MAP_PROT) | (va & VA_POFF); /* map address */ + } +else { + pgn = VA_GETPN (va); /* mon, get page no */ + map = mon_map[pgn]; /* get map entry */ + if (map & MAP_PROT) return MM_NOACC; /* prot? no access */ + pa = map | (va & VA_POFF); /* map address */ + } +*dat = M[pa]; /* return word */ +return SCPE_OK; +} + +/* Write word to virtual address */ + +t_stat Write (uint32 va, uint32 dat) +{ +uint32 pgn, map, pa; + +if (nml_mode) { /* normal? */ + va = va & VA_MASK; /* ignore user */ + if (va < 020000) pa = va; /* first 8K: 1 for 1 */ + else if (va < 030000) pa = va + em2_dyn; /* next 4K: ext EM2 */ + else pa = va + em3_dyn; /* next 4K: ext EM3 */ + } +else if (usr_mode || (va & VA_USR)) { /* user mapping? */ + pgn = VA_GETPN (va); /* get page no */ + map = usr_map[pgn]; /* get map entry */ + if (map & MAP_PROT) { /* protected page? */ + if (map == MAP_PROT) return MM_NOACC; /* zero? no access */ + else return MM_WRITE; /* else, write prot */ + } + pa = map | (va & VA_POFF); /* map address */ + } +else { + pgn = VA_GETPN (va); /* mon, get page no */ + map = mon_map[pgn]; /* get map entry */ + if (map & MAP_PROT) return MM_NOACC; /* prot? no access */ + pa = map | (va & VA_POFF); /* map address */ + } +if (MEM_ADDR_OK (pa)) M[pa] = dat; +return SCPE_OK; +} + +/* Relocate addr for console access */ + +uint32 RelocC (int32 va, int32 sw) +{ +uint32 nml = nml_mode, usr = usr_mode; +uint32 pa, pgn, map; + +if (sw & SWMASK ('N')) nml = 1; /* -n: normal */ +else if (sw & SWMASK ('X')) nml = usr = 0; /* -x: mon */ +else if (sw & SWMASK ('U')) nml = 0, usr = 1; /* -u: user */ +else if (!(sw & SWMASK ('V'))) return va; /* -v: curr */ +set_dyn_map (); +if (nml) { /* normal? */ + if (va < 020000) pa = va; /* first 8K: 1 for 1 */ + else if (va < 030000) pa = va + em2_dyn; /* next 4K: ext EM2 */ + else pa = va + em3_dyn; /* next 4K: ext EM3 */ + } +else { + pgn = VA_GETPN (va); /* get page no */ + map = usr? usr_map[pgn]: mon_map[pgn]; /* get map entry */ + if (map == MAP_PROT) return MAXMEMSIZE + 1; /* no access page? */ + pa = (map & ~MAP_PROT) | (va & VA_POFF); /* map address */ + } +return pa; +} + +/* Arithmetic routines */ + +uint32 Add24 (uint32 s1, uint32 s2, uint32 cin) +{ +uint32 t = s1 + s2 + cin; /* add with carry in */ +if (t > DMASK) X = X | SIGN; /* carry to X<0> */ +else X = X & ~SIGN; +if (((s1 ^ ~s2) & (s1 ^ t)) & SIGN) OV = 1; /* overflow */ +return t & DMASK; +} + +uint32 AddM24 (uint32 s1, uint32 s2) +{ +uint32 t = s1 + s2; /* add */ +if (((s1 ^ ~s2) & (s1 ^ t)) & SIGN) OV = 1; /* overflow */ +return t & DMASK; +} + +void Mul48 (uint32 s1, uint32 s2) +{ +uint32 a = ABS (s1); +uint32 b = ABS (s2); +uint32 hi, md, lo, t, u; + +if ((a == 0) || (b == 0)) { /* ops zero? */ + A = B = 0; + return; + } +t = a >> 12; /* split op1 */ +a = a & 07777; +u = b >> 12; /* split op2 */ +b = b & 07777; +md = (a * u) + (b * t); /* cross product */ +lo = (a * b) + ((md & 07777) << 12); /* low result */ +hi = (t * u) + (md >> 12) + (lo >> 24); /* hi result */ +A = ((hi << 1) & DMASK) | ((lo & DMASK) >> 23); +B = (lo << 1) & DMASK; +if ((s1 ^ s2) & SIGN) { + B = ((B ^ DMASK) + 1) & DMASK; + A = ((A ^ DMASK) + (B == 0)) & DMASK; + } +else if (A & SIGN) OV = 1; +return; +} + +/* Divide - the SDS 940 uses a non-restoring divide. The algorithm + runs even for overflow cases. Hence it must be emulated precisely + to give the right answers for diagnostics. If the dividend is + negative, AB are 2's complemented starting at B<22>, and B<23> + is unchanged. */ + +void Div48 (uint32 ar, uint32 br, uint32 m) +{ +int32 i; +uint32 quo = 0; /* quotient */ +uint32 dvdh = ar, dvdl = br; /* dividend */ +uint32 dvr = ABS (m); /* make dvr pos */ + +if (TSTS (dvdh)) { /* dvd < 0? */ + dvdl = (((dvdl ^ DMASK) + 2) & (DMASK & ~1)) | /* 23b negate */ + (dvdl & 1); /* low bit unch */ + dvdh = ((dvdh ^ DMASK) + (dvdl <= 1)) & DMASK; + } +if ((dvdh > dvr) || /* divide fail? */ + ((dvdh == dvr) && dvdl) || + ((dvdh == dvr) && !TSTS (ar ^ m))) OV = 1; +dvdh = (dvdh - dvr) & DMASK; /* initial sub */ +for (i = 0; i < 23; i++) { /* 23 iterations */ + quo = (quo << 1) | ((dvdh >> 23) ^ 1); /* quo bit = ~sign */ + dvdh = ((dvdh << 1) | (dvdl >> 23)) & DMASK; /* shift divd */ + dvdl = (dvdl << 1) & DMASK; + if (quo & 1) /* test ~sign */ + dvdh = (dvdh - dvr) & DMASK; /* sign was +, sub */ + else dvdh = (dvdh + dvr) & DMASK; /* sign was -, add */ + } +quo = quo << 1; /* shift quo */ +if (dvdh & SIGN) dvdh = (dvdh + dvr) & DMASK; /* last op -? restore */ +else quo = quo | 1; /* +, set quo bit */ +if (TSTS (ar ^ m)) A = NEG (quo); /* sign of quo */ +else A = quo; /* A = quo */ +if (TSTS (ar)) B = NEG (dvdh); /* sign of rem */ +else B = dvdh; /* B = rem */ +return; +} + +void RotR48 (uint32 sc) +{ +uint32 t = A; + +if (sc >= 24) { + sc = sc - 24; + A = ((B >> sc) | (A << (24 - sc))) & DMASK; + B = ((t >> sc) | (B << (24 - sc))) & DMASK; + } +else { + A = ((A >> sc) | (B << (24 - sc))) & DMASK; + B = ((B >> sc) | (t << (24 - sc))) & DMASK; + } +return; +} + +void ShfR48 (uint32 sc, uint32 sgn) +{ +if (sc >= 48) A = B = sgn; +if (sc >= 24) { + sc = sc - 24; + B = ((A >> sc) | (sgn << (24 - sc))) & DMASK; + A = sgn; + } +else { + B = ((B >> sc) | (A << (24 - sc)) & DMASK); + A = ((A >> sc) | (sgn << (24 - sc))) & DMASK; + } +return; +} + +/* POT routines for RL1, RL2, RL4 */ + +t_stat pot_RL1 (uint32 num, uint32 *dat) +{ +RL1 = *dat; +set_dyn_map (); +return SCPE_OK; +} + +t_stat pot_RL2 (uint32 num, uint32 *dat) +{ +RL2 = *dat; +set_dyn_map (); +return SCPE_OK; +} + +t_stat pot_RL4 (uint32 num, uint32 *dat) +{ +RL4 = (*dat) & 03737; +set_dyn_map (); +return SCPE_OK; +} + +/* Map EM2, EM3, RL1, RL2, RL4 to dynamic forms + + EM2, EM3 - left shifted 12, base virtual address subtracted + RL1, RL2 - page left shifted 11 + RL3 - filled in as 1 to 1 map + RL4 - EM2 or page left shifted 11, PROT bit inserted +*/ + +void set_dyn_map (void) +{ +em2_dyn = ((EM2 & 07) << 12) - 020000; +em3_dyn = ((EM3 & 07) << 12) - 030000; +usr_map[0] = (RL1 >> 7) & (MAP_PROT | MAP_PAGE); +usr_map[1] = (RL1 >> 1) & (MAP_PROT | MAP_PAGE); +usr_map[2] = (RL1 << 5) & (MAP_PROT | MAP_PAGE); +usr_map[3] = (RL1 << 11) & (MAP_PROT | MAP_PAGE); +usr_map[4] = (RL2 >> 7) & (MAP_PROT | MAP_PAGE); +usr_map[5] = (RL2 >> 1) & (MAP_PROT | MAP_PAGE); +usr_map[6] = (RL2 << 5) & (MAP_PROT | MAP_PAGE); +usr_map[7] = (RL2 << 11) & (MAP_PROT | MAP_PAGE); +mon_map[0] = (0 << VA_V_PN); +mon_map[1] = (1 << VA_V_PN); +mon_map[2] = (2 << VA_V_PN); +mon_map[3] = (3 << VA_V_PN); +mon_map[4] = ((EM2 & 07) << 12); +mon_map[5] = ((EM2 & 07) << 12) + (1 << VA_V_PN); +mon_map[6] = (RL4 << 5) & MAP_PAGE; +mon_map[7] = (RL4 << 11) & MAP_PAGE; +if (mon_map[6] == 0) mon_map[6] = MAP_PROT; +if (mon_map[7] == 0) mon_map[7] = MAP_PROT; +return; +} + +/* Recalculate api requests */ + +uint32 api_findreq (void) +{ +uint32 i, t; + +t = (int_req & ~1) & api_mask[api_lvlhi]; /* unmasked int */ +for (i = 31; t && (i > 0); i--) { /* find highest */ + if ((t >> i) & 1) return i; + } +return 0; /* none */ +} + +/* Dismiss highest priority interrupt */ + +void api_dismiss (void) +{ +uint32 i, t; + +t = 1u << api_lvlhi; /* highest active */ +int_req = int_req & ~t; /* clear int req */ +api_lvl = api_lvl & ~t; /* clear api level */ +api_lvlhi = 0; /* assume all clear */ +for (i = 31; api_lvl && (i > 0); i--) { /* find highest api */ + if ((api_lvl >> i) & 1) { /* bit set? */ + api_lvlhi = i; /* record level */ + break; /* done */ + } + } +int_reqhi = api_findreq (); /* recalc intreq */ +return; +} + +/* Reset routine */ + +t_stat cpu_reset (DEVICE *dptr) +{ +OV = 0; +EM2 = 2; +EM3 = 3; +RL1 = RL2 = RL4 = 0; +ion = ion_defer = 0; +nml_mode = 1; +usr_mode = 0; +mon_usr_trap = 0; +int_req = 0; +int_reqhi = 0; +api_lvl = 0; +api_lvlhi = 0; +alert = 0; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return SCPE_OK; +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +uint32 pa; + +pa = RelocC (addr, sw); +if (pa > MAXMEMSIZE) return SCPE_REL; +if (pa >= MEMSIZE) return SCPE_NXM; +if (vptr != NULL) *vptr = M[pa] & DMASK; +return SCPE_OK; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +uint32 pa; + +pa = RelocC (addr, sw); +if (pa > MAXMEMSIZE) return SCPE_REL; +if (pa >= MEMSIZE) return SCPE_NXM; +M[pa] = val & DMASK; +return SCPE_OK; +} + +/* Set memory size */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i; + +if ((val <= 0) || (val > MAXMEMSIZE) || ((val & 037777) != 0)) + return SCPE_ARG; +for (i = val; i < MEMSIZE; i++) mc = mc | M[i]; +if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE))) + return SCPE_OK; +MEMSIZE = val; +for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0; +return SCPE_OK; +} + +/* Set system type (1 = Genie, 0 = standard) */ + +t_stat cpu_set_type (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +extern t_stat drm_reset (DEVICE *dptr); +extern DEVICE drm_dev, mux_dev, muxl_dev; +extern UNIT drm_unit, mux_unit; +extern DIB mux_dib; + +if ((cpu_unit.flags & UNIT_GENIE) == (uint32) val) return SCPE_OK; +if ((drm_unit.flags & UNIT_ATT) || /* attached? */ + (mux_unit.flags & UNIT_ATT)) return SCPE_NOFNC; /* can't do it */ +if (val) { /* Genie? */ + drm_dev.flags = drm_dev.flags & ~DEV_DIS; /* enb drum */ + mux_dev.flags = mux_dev.flags & ~DEV_DIS; /* enb mux */ + muxl_dev.flags = muxl_dev.flags & ~DEV_DIS; + mux_dib.dev = DEV3_GMUX; /* Genie mux */ + } +else { + drm_dev.flags = drm_dev.flags | DEV_DIS; /* dsb drum */ + mux_dib.dev = DEV3_SMUX; /* std mux */ + return drm_reset (&drm_dev); + } +return SCPE_OK; +} + +/* The real time clock runs continuously; therefore, it only has + a unit service routine and a reset routine. The service routine + sets an interrupt that invokes the clock counter. The clock counter + is a "one instruction interrupt", and only MIN/SKR are valid. +*/ + +t_stat rtc_svc (UNIT *uptr) +{ +if (rtc_pie) int_req = int_req | INT_RTCP; /* set pulse intr */ +sim_activate (&rtc_unit, sim_rtcn_calb (rtc_tps, TMR_RTC)); /* reactivate */ +return SCPE_OK; +} + +/* Clock interrupt instruction */ + +t_stat rtc_inst (uint32 inst) +{ +uint32 op, dat, val, va; +t_stat r; + +op = I_GETOP (inst); /* get opcode */ +if (op == MIN) val = 1; /* incr */ +else if (op == SKR) val = DMASK; /* decr */ +else return STOP_RTCINS; /* can't do it */ +if (r = Ea (inst, &va)) return r; /* decode eff addr */ +if (r = Read (va, &dat)) return r; /* get operand */ +dat = AddM24 (dat, val); /* mem +/- 1 */ +if (r = Write (va, dat)) return r; /* rewrite */ +if (dat == 0) int_req = int_req | INT_RTCS; /* set clk sync int */ +return SCPE_OK; +} + +/* Clock reset */ + +t_stat rtc_reset (DEVICE *dptr) +{ +rtc_pie = 0; /* disable pulse */ +sim_activate (&rtc_unit, rtc_unit.wait); /* activate unit */ +return SCPE_OK; +} + +/* Set frequency */ + +t_stat rtc_set_freq (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr) return SCPE_ARG; +if ((val != 50) && (val != 60)) return SCPE_IERR; +rtc_tps = val; +return SCPE_OK; +} + +/* Show frequency */ + +t_stat rtc_show_freq (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, (rtc_tps == 50)? "50Hz": "60Hz"); +return SCPE_OK; +} + +/* Record history */ + +void inst_hist (uint32 ir, uint32 pc, uint32 tp) +{ +hst_p = (hst_p + 1); /* next entry */ +if (hst_p >= hst_lnt) hst_p = 0; +hst[hst_p].typ = tp | (OV << 4); +hst[hst_p].pc = pc; +hst[hst_p].ir = ir; +hst[hst_p].a = A; +hst[hst_p].b = B; +hst[hst_p].x = X; +hst[hst_p].ea = HIST_NOEA; +return; +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].typ = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 ov, k, di, lnt; +char *cptr = (char *) desc; +t_stat r; +t_value sim_eval; +InstHistory *h; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); +static char *cyc[] = { " ", " ", "INT", "TRP" }; + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "CYC PC OV A B X EA IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(++di) % hst_lnt]; /* entry pointer */ + if (h->typ) { /* instruction? */ + ov = (h->typ >> 4) & 1; /* overflow */ + fprintf (st, "%s %05o %o %08o %08o %08o ", cyc[h->typ & 3], + h->pc, ov, h->a, h->b, h->x); + if (h->ea & HIST_NOEA) fprintf (st, " "); + else fprintf (st, "%05o ", h->ea); + sim_eval = h->ir; + if ((fprint_sym (st, h->pc, &sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "(undefined) %08o", h->ir); + fputc ('\n', st); /* end line */ + } /* end else instruction */ + } /* end for */ +return SCPE_OK; +} diff --git a/SDS/sds_defs.h b/SDS/sds_defs.h new file mode 100644 index 0000000..4b715f1 --- /dev/null +++ b/SDS/sds_defs.h @@ -0,0 +1,409 @@ +/* sds_defs.h: SDS 940 simulator definitions + + Copyright (c) 2001-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 25-Apr-03 RMS Revised for extended file support +*/ + +#ifndef _SDS_DEFS_H_ +#define _SDS_DEFS_H_ 0 + +#include "sim_defs.h" /* simulator defns */ + +/* Simulator stop codes */ + +#define STOP_IONRDY 1 /* I/O dev not ready */ +#define STOP_HALT 2 /* HALT */ +#define STOP_IBKPT 3 /* breakpoint */ +#define STOP_INVDEV 4 /* invalid dev */ +#define STOP_INVINS 5 /* invalid instr */ +#define STOP_INVIOP 6 /* invalid I/O op */ +#define STOP_INDLIM 7 /* indirect limit */ +#define STOP_EXULIM 8 /* EXU limit */ +#define STOP_MMINT 9 /* mm in intr */ +#define STOP_MMTRP 10 /* mm in trap */ +#define STOP_TRPINS 11 /* trap inst not BRM */ +#define STOP_RTCINS 12 /* rtc inst not MIN/SKR */ +#define STOP_ILLVEC 13 /* zero vector */ +#define STOP_CCT 14 /* runaway CCT */ + +/* Trap codes */ + +#define MM_PRVINS -040 /* privileged */ +#define MM_NOACC -041 /* no access */ +#define MM_WRITE -043 /* write protect */ +#define MM_MONUSR -044 /* mon to user */ + +/* Conditional error returns */ + +#define CRETINS return ((stop_invins)? STOP_INVINS: SCPE_OK) +#define CRETDEV return ((stop_invdev)? STOP_INVDEV: SCPE_OK) +#define CRETIOP return ((stop_inviop)? STOP_INVIOP: SCPE_OK) +#define CRETIOE(f,c) return ((f)? c: SCPE_OK) + +/* Architectural constants */ + +#define SIGN 040000000 /* sign */ +#define DMASK 077777777 /* data mask */ +#define EXPS 0400 /* exp sign */ +#define EXPMASK 0777 /* exp mask */ +#define SXT(x) ((int32) (((x) & SIGN)? ((x) | ~DMASK): \ + ((x) & DMASK))) +#define SXT_EXP(x) ((int32) (((x) & EXPS)? ((x) | ~EXPMASK): \ + ((x) & EXPMASK))) + +/* Memory */ + +#define MAXMEMSIZE (1 << 16) /* max memory size */ +#define PAMASK (MAXMEMSIZE - 1) /* physical addr mask */ +#define MEMSIZE (cpu_unit.capac) /* actual memory size */ +#define MEM_ADDR_OK(x) (((uint32) (x)) < MEMSIZE) +#define ReadP(x) M[x] +#define WriteP(x,y) if (MEM_ADDR_OK (x)) M[x] = y + +/* Virtual addressing */ + +#define VA_SIZE (1 << 14) /* virtual addr size */ +#define VA_MASK (VA_SIZE - 1) /* virtual addr mask */ +#define VA_V_PN 11 /* page number */ +#define VA_M_PN 07 +#define VA_GETPN(x) (((x) >> VA_V_PN) & VA_M_PN) +#define VA_POFF ((1 << VA_V_PN) - 1) /* offset */ +#define VA_USR (I_USR) /* user flag in addr */ +#define XVA_MASK (VA_USR | VA_MASK) + +/* Arithmetic */ + +#define TSTS(x) ((x) & SIGN) +#define NEG(x) (-((int32) (x)) & DMASK) +#define ABS(x) (TSTS (x)? NEG(x): (x)) + +/* Memory map */ + +#define MAP_PROT (040 << VA_V_PN) /* protected */ +#define MAP_PAGE (037 << VA_V_PN) /* phys page number */ + +/* Instruction format */ + +#define I_USR (1 << 23) /* user */ +#define I_IDX (1 << 22) /* indexed */ +#define I_POP (1 << 21) /* programmed op */ +#define I_V_TAG 21 /* tag */ +#define I_V_OP 15 /* opcode */ +#define I_M_OP 077 +#define I_GETOP(x) (((x) >> I_V_OP) & I_M_OP) +#define I_IND (1 << 14) /* indirect */ +#define I_V_SHFOP 11 /* shift op */ +#define I_M_SHFOP 07 +#define I_GETSHFOP(x) (((x) >> I_V_SHFOP) & I_M_SHFOP) +#define I_SHFMSK 0777 /* shift count */ +#define I_V_IOMD 12 /* IO inst mode */ +#define I_M_IOMD 03 +#define I_GETIOMD(x) (((x) >> I_V_IOMD) & I_M_IOMD) +#define I_V_SKCND 7 /* SKS skip cond */ +#define I_M_SKCND 037 +#define I_GETSKCND(x) (((x) >> I_V_SKCND) & I_M_SKCND) +#define I_EOB2 000400000 /* chan# bit 2 */ +#define I_SKB2 000040000 /* skschan# bit 2 */ +#define I_EOB1 020000000 /* chan# bit 1 */ +#define I_EOB0 000000100 /* chan# bit 0 */ +#define I_GETEOCH(x) ((((x) & I_EOB2)? 4: 0) | \ + (((x) & I_EOB1)? 2: 0) | \ + (((x) & I_EOB0)? 1: 0)) +#define I_SETEOCH(x) ((((x) & 4)? I_EOB2: 0) | \ + (((x) & 2)? I_EOB1: 0) | \ + (((x) & 1)? I_EOB0: 0)) +#define I_GETSKCH(x) ((((x) & I_SKB2)? 4: 0) | \ + (((x) & I_EOB1)? 2: 0) | \ + (((x) & I_EOB0)? 1: 0)) +#define I_SETSKCH(x) ((((x) & 4)? I_SKB2: 0) | \ + (((x) & 2)? I_EOB1: 0) | \ + (((x) & 1)? I_EOB0: 0)) + +/* Globally visible flags */ + +#define UNIT_V_GENIE (UNIT_V_UF + 0) +#define UNIT_GENIE (1 << UNIT_V_GENIE) + +/* Timers */ + +#define TMR_RTC 0 /* clock */ +#define TMR_MUX 1 /* mux */ + +/* I/O routine functions */ + +#define IO_CONN 0 /* connect */ +#define IO_EOM1 1 /* EOM mode 1 */ +#define IO_DISC 2 /* disconnect */ +#define IO_READ 3 /* read */ +#define IO_WRITE 4 /* write */ +#define IO_WREOR 5 /* write eor */ +#define IO_SKS 6 /* skip signal */ + +/* Dispatch template */ + +struct sdsdspt { + uint32 num; /* # entries */ + uint32 off; /* offset from base */ + }; + +typedef struct sdsdspt DSPT; + +/* Device information block */ + +struct sdsdib { + int32 chan; /* channel */ + int32 dev; /* base dev no */ + int32 xfr; /* xfer flag */ + DSPT *tplt; /* dispatch templates */ + t_stat (*iop) (uint32 fnc, uint32 dev, uint32 *dat); + }; + +typedef struct sdsdib DIB; + +/* Channels */ + +#define NUM_CHAN 8 /* max num chan */ +#define CHAN_W 0 /* TMCC */ +#define CHAN_Y 1 +#define CHAN_C 2 +#define CHAN_D 3 +#define CHAN_E 4 /* DACC */ +#define CHAN_F 5 +#define CHAN_G 6 +#define CHAN_H 7 + +/* I/O control EOM */ + +#define CHC_REV 04000 /* reverse */ +#define CHC_NLDR 02000 /* no leader */ +#define CHC_BIN 01000 /* binary */ +#define CHC_V_CPW 7 /* char/word */ +#define CHC_M_CPW 03 +#define CHC_GETCPW(x) (((x) >> CHC_V_CPW) & CHC_M_CPW) + +/* Buffer control (extended) EOM */ + +#define CHM_CE 04000 /* compat/ext */ +#define CHM_ER 02000 /* end rec int */ +#define CHM_ZC 01000 /* zero wc int */ +#define CHM_V_FNC 7 /* term func */ +#define CHM_M_FNC 03 +#define CHM_GETFNC(x) (((x) & CHM_CE)? (((x) >> CHM_V_FNC) & CHM_M_FNC): CHM_COMP) +#define CHM_IORD 0 /* record, disc */ +#define CHM_IOSD 1 /* signal, disc */ +#define CHM_IORP 2 /* record, proc */ +#define CHM_IOSP 3 /* signal, proc */ +#define CHM_COMP 5 /* compatible */ +#define CHM_SGNL 1 /* signal bit */ +#define CHM_PROC 2 /* proceed bit */ +#define CHM_V_HMA 5 /* hi mem addr */ +#define CHM_M_HMA 03 +#define CHM_GETHMA(x) (((x) >> CHM_V_HMA) & CHM_M_HMA) +#define CHM_V_HWC 0 /* hi word count */ +#define CHM_M_HWC 037 +#define CHM_GETHWC(x) (((x) >> CHM_V_HWC) & CHM_M_HWC) + +/* Channel flags word */ + +#define CHF_ERR 00001 /* error */ +#define CHF_IREC 00002 /* interrecord */ +#define CHF_ILCE 00004 /* interlace */ +#define CHF_DCHN 00010 /* data chain */ +#define CHF_EOR 00020 /* end of record */ +#define CHF_12B 00040 /* 12 bit mode */ +#define CHF_24B 00100 /* 24 bit mode */ +#define CHF_OWAK 00200 /* output wake */ +#define CHF_SCAN 00400 /* scan */ +#define CHF_TOP 01000 /* TOP pending */ +#define CHF_N_FLG 9 /* <= 16 */ + +/* Interrupts and vectors (0 is reserved), highest bit is highest priority */ + +#define INT_V_PWRO 31 /* power on */ +#define INT_V_PWRF 30 /* power off */ +#define INT_V_CPAR 29 /* CPU parity err */ +#define INT_V_IPAR 28 /* IO parity err */ +#define INT_V_RTCS 27 /* clock sync */ +#define INT_V_RTCP 26 /* clock pulse */ +#define INT_V_YZWC 25 /* chan Y zero wc */ +#define INT_V_WZWC 24 /* chan W zero wc */ +#define INT_V_YEOR 23 /* chan Y end rec */ +#define INT_V_WEOR 22 /* chan W end rec */ +#define INT_V_CZWC 21 /* chan C */ +#define INT_V_CEOR 20 +#define INT_V_DZWC 19 /* chan D */ +#define INT_V_DEOR 18 +#define INT_V_EZWC 17 /* chan E */ +#define INT_V_EEOR 16 +#define INT_V_FZWC 15 /* chan F */ +#define INT_V_FEOR 14 +#define INT_V_GZWC 13 /* chan G */ +#define INT_V_GEOR 12 +#define INT_V_HZWC 11 /* chan H */ +#define INT_V_HEOR 10 +#define INT_V_MUXR 9 /* mux receive */ +#define INT_V_MUXT 8 /* mux transmit */ +#define INT_V_MUXCO 7 /* SDS carrier on */ +#define INT_V_MUXCF 6 /* SDS carrier off */ +#define INT_V_DRM 5 /* Genie drum */ +#define INT_V_FORK 4 /* fork */ + +#define INT_PWRO (1 << INT_V_PWRO) +#define INT_PWRF (1 << INT_V_PWRF) +#define INT_CPAR (1 << INT_V_CPAR) +#define INT_IPAR (1 << INT_V_IPAR) +#define INT_RTCS (1 << INT_V_RTCS) +#define INT_RTCP (1 << INT_V_RTCP) +#define INT_YZWC (1 << INT_V_YZWC) +#define INT_WZWC (1 << INT_V_WZWC) +#define INT_YEOR (1 << INT_V_YEOR) +#define INT_WEOR (1 << INT_V_WEOR) +#define INT_CZWC (1 << INT_V_CZWC) +#define INT_CEOR (1 << INT_V_CEOR) +#define INT_DZWC (1 << INT_V_DZWC) +#define INT_DEOR (1 << INT_V_DEOR) +#define INT_EZWC (1 << INT_V_EZWC) +#define INT_EEOR (1 << INT_V_EEOR) +#define INT_FZWC (1 << INT_V_FZWC) +#define INT_FEOR (1 << INT_V_FEOR) +#define INT_GZWC (1 << INT_V_GZWC) +#define INT_GEOR (1 << INT_V_GEOR) +#define INT_HZWC (1 << INT_V_HZWC) +#define INT_HEOR (1 << INT_V_HEOR) +#define INT_MUXR (1 << INT_V_MUXR) +#define INT_MUXT (1 << INT_V_MUXT) +#define INT_MUXCO (1 << INT_V_MUXCO) +#define INT_MUXCF (1 << INT_V_MUXCF) +#define INT_DRM (1 << INT_V_DRM) +#define INT_FORK (1 << INT_V_FORK) + +#define VEC_PWRO 0036 +#define VEC_PWRF 0037 +#define VEC_CPAR 0056 +#define VEC_IPAR 0057 +#define VEC_RTCS 0074 +#define VEC_RTCP 0075 +#define VEC_YZWC 0030 +#define VEC_WZWC 0031 +#define VEC_YEOR 0032 +#define VEC_WEOR 0033 +#define VEC_CZWC 0060 +#define VEC_CEOR 0061 +#define VEC_DZWC 0062 +#define VEC_DEOR 0063 +#define VEC_EZWC 0064 +#define VEC_EEOR 0065 +#define VEC_FZWC 0066 +#define VEC_FEOR 0067 +#define VEC_GZWC 0070 +#define VEC_GEOR 0071 +#define VEC_HZWC 0072 +#define VEC_HEOR 0073 +#define VEC_MUXR 0200 /* term mux rcv */ +#define VEC_MUXT 0201 /* term mux xmt */ +#define VEC_MUXCO 0202 /* SDS: mux carrier on */ +#define VEC_MUXCF 0203 /* SDS: mux carrier off */ +#define VEC_DRM 0202 /* Genie: drum */ +#define VEC_FORK 0216 /* "fork" */ + +/* Device constants */ + +#define DEV_MASK 077 /* device mask */ +#define DEV_TTI 001 /* teletype */ +#define DEV_PTR 004 /* paper tape rdr */ +#define DEV_MT 010 /* magtape */ +#define DEV_RAD 026 /* fixed head disk */ +#define DEV_DSK 026 /* moving head disk */ +#define DEV_TTO 041 /* teletype */ +#define DEV_PTP 044 /* paper tape punch */ +#define DEV_LPT 060 /* line printer */ +#define DEV_MTS 020 /* MT scan/erase */ +#define DEV_OUT 040 /* output flag */ +#define DEV3_GDRM 004 /* Genie drum */ +#define DEV3_GMUX 001 /* Genie mux */ +#define DEV3_SMUX (DEV_MASK) /* standard mux */ + +#define LPT_WIDTH 132 /* line print width */ +#define CCT_LNT 132 /* car ctrl length */ + +/* Transfer request flags for devices (0 is reserved) */ + +#define XFR_V_TTI 1 /* console */ +#define XFR_V_TTO 2 +#define XFR_V_PTR 3 /* paper tape */ +#define XFR_V_PTP 4 +#define XFR_V_LPT 5 /* line printer */ +#define XFR_V_RAD 6 /* fixed hd disk */ +#define XFR_V_DSK 7 /* mving hd disk */ +#define XFR_V_MT0 8 /* magtape */ + +#define XFR_TTI (1 << XFR_V_TTI) +#define XFR_TTO (1 << XFR_V_TTO) +#define XFR_PTR (1 << XFR_V_PTR) +#define XFR_PTP (1 << XFR_V_PTP) +#define XFR_LPT (1 << XFR_V_LPT) +#define XFR_RAD (1 << XFR_V_RAD) +#define XFR_DSK (1 << XFR_V_DSK) +#define XFR_MT0 (1 << XFR_V_MT0) + +/* PIN/POT ordinals (0 is reserved) */ + +#define POT_ILCY 1 /* interlace */ +#define POT_DCRY (POT_ILCY + NUM_CHAN) /* data chain */ +#define POT_ADRY (POT_DCRY + NUM_CHAN) /* address reg */ +#define POT_RL1 (POT_ADRY + NUM_CHAN) /* RL1 */ +#define POT_RL2 (POT_RL1 + 1) /* RL2 */ +#define POT_RL4 (POT_RL2 + 1) /* RL4 */ +#define POT_RADS (POT_RL4 + 1) /* fhd sector */ +#define POT_RADA (POT_RADS + 1) /* fhd addr */ +#define POT_DSK (POT_RADA + 1) /* mhd sec/addr */ +#define POT_SYSI (POT_DSK + 1) /* sys intr */ +#define POT_MUX (POT_SYSI + 1) /* multiplexor */ + +/* Opcodes */ + +enum opcodes { + HLT, BRU, EOM, EOD = 006, + MIY = 010, BRI, MIW, POT, ETR, MRG = 016, EOR, + NOP, OVF = 022, EXU, + YIM = 030, WIM = 032, PIN, STA = 035, STB, STX, + SKS, BRX, BRM = 043, RCH = 046, + SKE = 050, BRR, SKB, SKN, SUB, ADD, SUC, ADC, + SKR, MIN, XMA, ADM, MUL, DIV, RSH, LSH, + SKM, LDX, SKA, SKG, SKD, LDB, LDA, EAX + }; + +/* Channel function prototypes */ + +void chan_set_flag (int32 ch, uint32 fl); +void chan_set_ordy (int32 ch); +void chan_disc (int32 ch); +void chan_set_uar (int32 ch, uint32 dev); +t_stat set_chan (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat chan_process (void); +t_bool chan_testact (void); + +#endif diff --git a/SDS/sds_diag.txt b/SDS/sds_diag.txt new file mode 100644 index 0000000..6f095a7 --- /dev/null +++ b/SDS/sds_diag.txt @@ -0,0 +1,113 @@ +SDS Diagnostics, using the SDS 930/940 Master Diagnostic Tape image (D930X4A.TAP) + +Summary + +930 0-16K Memory Test passed +930 16K-32K Memory Test passed +930 Instruction Test passed +930 P&S Register Test passed + +--- +930 0-16K Memory Test + +sim> att mt diag.tap +sim> d a 1 +sim> d bpt4 1 ; stop every 1/2 cycle +sim> boot mt + +HALT instruction, P: 00050 (STA 122,4) +sim> ex a +A: 00000000 ; error count +sim> c + +HALT instruction, P: 37650 (STA 37722,4) + +sim> ex a +A: 00000000 ; error count + +--- +930 16K-32K Memory Test + +sim> att mt diag.tap +sim> d a 2 +sim> d bpt4 2 ; stop every 1/2 cycle +sim> boot mt + + +HALT instruction, P: 00050 (STA 6) +sim> ex a +A: 00000000 ; error count +sim> c + +HALT instruction, P: 37650 (STA 37406) +sim> ex a +A: 00000000 ; error count +sim> c + +--- +930 Instruction Diagnostic + +sim> att mt diag.tap +sim> d a 3 +sim> br 17 ; catch start of diagnostic +sim> boot mt + +Breakpoint, P: 00017 (BRR 12,2) +sim> nobr 17 +sim> br 112 ; catch end of diagnostic +sim> c + +Breakpoint, P: 00112 (BRU 3) + +--- +930 P&S Register Test + +sim> att mt diag.tap +sim> d a 4 +sim> br 60 ; catch end of pass +sim> boot mt + +Breakpoint, P: 00060 (BRU 22) + +--- +Bugs + +1. IO: Channel WAR not cleared after memory store +2. IO: dev_map should contain _flags, not _v_flags +3. SYS: Errors in system tables +4. SYS: Character conversion table had 0 (space) as illegal, should be -1 +5. IO: Channel CPW calculation wrong for 12b mode +6. RAD, DSK, MT: Instruction masks wrong for RAD, DSK, MT +7. IO: Missing subscripts in dev_disp references +8. RAD: typos referencing DSK +9. IO: SKS 3 call incorrect +10. DRM: Drum track mask width incorrect +11. CPU: Memory management trap left reason in bogus state, stopped simulator +12. CPU: Interrupts require api_lvl as well as api_lvlhi, like PDP-10, PDP-15 +13. CPU: Bug in find interrupt request +14. CPU: Interrupt priority scheme recoded for left to right priority +15. CPU: overflow test coded backwards +16. CPU: Rotates operate mod 48, not with upper limit of 48 (manual incorrect) +17. CPU: RSH not handling >= 48 correctly +18. CPU: CNA is 2's complement not 1's complement +19. CPU: MUL failed to mask cross product correctly +20. CPU: EM2, EM3 test using wrong 'channel' +21. CPU: EM3 test tested EM2 instead +22. CPU: POP must save EM2, EM3 like BRM (manual incorrect) +23. CPU: Shifts need special EA calculation, direct cycles using 9b indexing +24. CPU: Shifts ignore addr<13:14> +25. CPU: Diagnostic uses undefined shift 'normalize cyclic' +26. CPU: Divide 2'c complement of AB leaves B<23> unchanged +27. CPU: Divide overflow test requires special cases for divd.h == divr +28. CPU: Divide uses non-restoring algorithm +29. CPU: Channel terminate output must be deferred until channel buffer clears +30. CPU: Channel terminate output to magtape is convert to scan, must be + handled in channel logic +31. SYS: duplicate entries for shifts +32. SYS: mask for shifts did not include indirect flag +33. MUX: Genie/SDS use inverted meanings for line enable flag +34. MT: missing fseek before write eof +35. MT: displayed characters only 7b wide instead of 8b +36. CPU: EOD 20000 used by diagnostic (EM change is NOP) +37. CPU: SKD sets all 24b of X, not just exponent +38. CPU: reset should not clear A, B, X diff --git a/SDS/sds_drm.c b/SDS/sds_drm.c new file mode 100644 index 0000000..9c468df --- /dev/null +++ b/SDS/sds_drm.c @@ -0,0 +1,293 @@ +/* sds_drm.c: SDS 940 Project Genie drum simulator + + Copyright (c) 2002-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + drm drum + + The drum is buffered in memory. + + Note: the Project Genie documentation and the actual monitor sources disagree + on the I/O instruction definitions for the drum. The simulator follows the + monitor sources, as follows: + + DCC OP 00230404B RESET DRUM CHANNEL + DSC OP 00230204B START DRUM CHANNEL (NO CHAIN) + DRA OP 00230504B READ DRUM TIMING COUNTER INTO 21B + DSR OP 04030204B SKIP IF DRUM NOT BUSY + DSE OP 04037404B SKIP IF NO DRUM ERROR +*/ + +#include "sds_defs.h" +#include + +/* Constants */ + +#define DRM_N_WD 11 /* word addr width */ +#define DRM_V_WD 0 /* position */ +#define DRM_M_WD ((1 << DRM_N_WD) - 1) /* word mask */ +#define DRM_NUMWD (1 << DRM_N_WD) /* words/sector */ +#define DRM_NUMGP 236 /* gap/sector */ +#define DRM_PHYWD (DRM_NUMWD + DRM_NUMGP) /* phys wds/sector */ +#define DRM_N_SC 3 /* sect addr width */ +#define DRM_V_SC (DRM_N_WD) /* position */ +#define DRM_M_SC ((1 << DRM_N_SC) - 1) /* sector mask */ +#define DRM_NUMSC (1 << DRM_N_SC) /* sectors/track */ +#define DRM_N_TR 7 /* track addr width */ +#define DRM_V_TR (DRM_N_WD+DRM_N_SC) /* position */ +#define DRM_M_TR ((1 << DRM_N_TR) - 1) /* track mask */ +#define DRM_NUMTR 84 /* tracks/drum */ +#define DRM_N_ADDR (DRM_N_WD+DRM_N_SC+DRM_N_TR) /* drum addr width */ +#define DRM_SWMASK ((1 << (DRM_N_WD+DRM_N_SC)) - 1)/* sector+word mask */ +#define DRM_DAMASK ((1 << DRM_N_ADDR) - 1) /* drum addr mask */ +#define DRM_SIZE (DRM_NUMTR*DRM_NUMSC*DRM_NUMWD) /* words/disk */ +#define DRM_WCMASK 037777 /* wc mask */ +#define DRM_GETSC(x) (((x) >> DRM_V_SC) & DRM_M_SC) + +#define DRM_PC 020 +#define DRM_AD 021 +#define DRM_ADAT (1 << (DRM_N_WD + DRM_N_SC)) /* data flag */ + +#define DRM_SFET 0 /* fetch state */ +#define DRM_SFCA 1 /* fetch CA */ +#define DRM_SFDA 2 /* fetch DA */ +#define DRM_SXFR 3 /* xfer */ + +#define DRM_V_OP 21 /* drum op */ +#define DRM_M_OP 07 +#define DRM_V_RW 20 +#define DRM_GETOP(x) (((x) >> DRM_V_OP) & DRM_M_OP) +#define DRM_GETRW(x) (((x) >> DRM_V_RW) & 1) +#define DRM_OXF 0 /* xfer */ +#define DRM_OCX 1 /* cond xfer */ +#define DRM_OBR 2 /* branch */ +#define DRM_ORS 3 /* reset error */ +#define DRM_END 4 /* end prog */ +#define DRM_EIE 5 /* end int if err */ +#define DRM_EIU 7 /* end int uncond */ + +#define GET_TWORD(x) ((int32) fmod (sim_gtime() / ((double) (x)), \ + ((double) (DRM_NUMSC * DRM_PHYWD)))) + +extern uint32 M[]; /* memory */ +extern uint32 alert, int_req; +extern int32 stop_invins, stop_invdev, stop_inviop; +uint32 drm_da = 0; /* disk address */ +uint32 drm_ca = 0; /* core address */ +uint32 drm_wc = 0; /* word count */ +int32 drm_par = 0; /* cumulative par */ +int32 drm_err = 0; /* error */ +int32 drm_rw = 0; /* read/write */ +int32 drm_sta = 0; /* drum state */ +int32 drm_ftime = 3; /* time to fetch */ +int32 drm_xtime = 1; /* time to xfr */ +int32 drm_stopioe = 1; /* stop on error */ + +DEVICE drm_dev; +t_stat drm (uint32 fnc, uint32 inst, uint32 *dat); +t_stat drm_svc (UNIT *uptr); +t_stat drm_reset (DEVICE *dptr); + +/* DRM data structures + + drm_dev device descriptor + drm_unit unit descriptor + drm_reg register list +*/ + +DIB drm_dib = { -1, DEV3_GDRM, 0, NULL, &drm }; + +UNIT drm_unit = { + UDATA (&drm_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + DRM_SIZE) + }; + +REG drm_reg[] = { + { ORDATA (DA, drm_da, DRM_N_ADDR) }, + { ORDATA (CA, drm_ca, 16) }, + { ORDATA (WC, drm_wc, 14) }, + { ORDATA (PAR, drm_par, 12) }, + { FLDATA (RW, drm_rw, 0) }, + { FLDATA (ERR, drm_err, 0) }, + { ORDATA (STA, drm_sta, 2) }, + { DRDATA (FTIME, drm_ftime, 24), REG_NZ + PV_LEFT }, + { DRDATA (XTIME, drm_xtime, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, drm_stopioe, 0) }, + { NULL } + }; + +DEVICE drm_dev = { + "DRM", &drm_unit, drm_reg, NULL, + 1, 8, DRM_N_ADDR, 1, 8, 24, + NULL, NULL, &drm_reset, + NULL, NULL, NULL, + &drm_dib, DEV_DISABLE | DEV_DIS + }; + +/* Drum routine - EOM/SKS 3xx04 */ + +t_stat drm (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 t, op = inst & 07700; + +switch (fnc) { + + case IO_CONN: /* connect */ + if (op == 00400) return drm_reset (&drm_dev); /* EOM 404 = reset */ + if (op == 00500) { /* EOM 504 = read DA */ + if (sim_is_active (&drm_unit)) return SCPE_OK; /* must be idle */ + t = GET_TWORD (drm_xtime); /* get position */ + if (t < DRM_NUMGP) M[DRM_AD] = DRM_NUMWD - t; /* in gap? */ + else M[DRM_AD] = (t - DRM_NUMGP) | DRM_ADAT;/* in data */ + } + else if (op == 00200) { /* EOM 204 = start */ + if (sim_is_active (&drm_unit)) return SCPE_OK; /* must be idle */ + drm_sta = DRM_SFET; /* state = fetch */ + sim_activate (&drm_unit, drm_ftime); /* activate */ + } + else CRETINS; + break; + + case IO_SKS: /* SKS */ + if (((op == 07400) && !drm_err) || /* 37404: no err */ + ((op == 00200) && !sim_is_active (&drm_unit))) /* 30204: idle */ + *dat = 1; + break; + + default: + return SCPE_IERR; + } + +return SCPE_OK; +} + +/* Unit service */ + +t_stat drm_svc (UNIT *uptr) +{ +int32 t, rda; +uint32 dpc, dwd; +uint32 *fbuf = uptr->filebuf; + +if (drm_sta != DRM_SXFR) { /* fetch drum prog? */ + dpc = M[DRM_PC]; /* get drum PC */ + dwd = M[dpc & PAMASK]; /* get drum inst */ + M[DRM_PC] = (dpc + 1) & PAMASK; /* update drum PC */ + if (drm_sta == DRM_SFCA) { /* fetch core addr? */ + drm_rw = DRM_GETRW (dwd); /* set op */ + drm_ca = dwd & PAMASK; /* set core addr */ + drm_sta = DRM_SFDA; /* next is disk addr */ + } + else if (drm_sta == DRM_SFDA) { /* fetch disk addr? */ + drm_da = dwd & DRM_DAMASK; /* set disk addr */ + drm_sta = DRM_SXFR; /* next is xfer */ + drm_par = 0; /* init parity */ + rda = (drm_da & DRM_SWMASK) + (DRM_GETSC (drm_da) * DRM_NUMGP); + t = rda - GET_TWORD (drm_xtime); /* difference */ + if (t <= 0) t = t + (DRM_NUMSC * DRM_PHYWD); /* add trk lnt */ + sim_activate (&drm_unit, t * drm_xtime); /* activate */ + } + else { + switch (DRM_GETOP (dwd)) { + + case DRM_OCX: /* cond xfr */ + if (drm_err) { /* error? */ + int_req = int_req | INT_DRM; /* req int */ + return SCPE_OK; /* done */ + } + case DRM_OXF: /* transfer */ + drm_wc = dwd & DRM_WCMASK; /* save wc */ + drm_sta = DRM_SFCA; /* next state */ + break; + + case DRM_OBR: /* branch */ + M[DRM_PC] = dwd & PAMASK; /* new drum PC */ + break; + + case DRM_END: /* end */ + return SCPE_OK; + + case DRM_EIE: /* end, int if err */ + if (!drm_err) return SCPE_OK; + + case DRM_EIU: /* end, int uncond */ + int_req = int_req | INT_DRM; + return SCPE_OK; + } /* end switch */ + } /* end else sta */ + sim_activate (uptr, drm_ftime); /* fetch next word */ + } /* end if !xfr */ +else { /* transfer word */ + if ((uptr->flags & UNIT_BUF) == 0) { /* not buffered? */ + drm_err = 1; /* error */ + CRETIOE (drm_stopioe, SCPE_UNATT); + } + if (drm_rw) { /* write? */ + dwd = M[drm_ca]; /* get mem word */ + fbuf[drm_da] = dwd; /* write to drum */ + if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; + } + else { /* read */ + dwd = fbuf[drm_da]; /* get drum word */ + M[drm_ca] = dwd; /* write to mem */ + } + drm_da = drm_da + 1; /* inc drum addr */ + if (drm_da >= DRM_SIZE) drm_da = 0; /* wrap */ + drm_ca = (drm_ca + 1) & PAMASK; /* inc core addr */ + drm_wc = (drm_wc - 1) & DRM_WCMASK; /* dec word cnt */ + drm_par = drm_par ^ (dwd >> 12); /* parity */ + drm_par = ((drm_par << 1) | (drm_par >> 11)) & 07777; + drm_par = drm_par ^ (dwd & 07777); + if (drm_wc) { /* more to do */ + if (drm_da & DRM_M_WD) sim_activate (uptr, drm_xtime); + else sim_activate (uptr, drm_xtime * DRM_NUMGP); + } + else { /* end xfr */ +#if defined (DRM_PAR) + if ((drm_da & DRM_M_WD) && drm_rw) { /* wr end mid sector? */ + M[drm_da] = drm_par << 12; /* clobber data */ + if (drm_da >= uptr->hwmark) uptr->hwmark = drm_da + 1; + } +#endif + drm_sta = DRM_SFET; /* back to fetch */ + sim_activate (uptr, drm_ftime); /* schedule */ + } /* end else end xfr */ + } /* end else xfr */ +return SCPE_OK; +} + +/* Reset routine */ + +t_stat drm_reset (DEVICE *dptr) +{ +drm_da = 0; /* clear state */ +drm_ca = 0; +drm_wc = 0; +drm_par = 0; +drm_sta = 0; +drm_err = 0; +drm_rw = 0; +int_req = int_req & ~INT_DRM; /* clear intr */ +sim_cancel (&drm_unit); /* deactivate */ +return SCPE_OK; +} diff --git a/SDS/sds_dsk.c b/SDS/sds_dsk.c new file mode 100644 index 0000000..8165f50 --- /dev/null +++ b/SDS/sds_dsk.c @@ -0,0 +1,376 @@ +/* sds_dsk.c: SDS 940 moving head disk simulator + + Copyright (c) 2001-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + dsk moving head disk + + The SDS 9164 disk has a subsector feature, allowing each 64W sector to be + viewed as 16W packets. In addition, it has a chaining feature, allowing + records to be extended beyond a sector boundary. To accomodate this, the + first word of each sector has 3 extra bits: + + <26> = end of chain flag + <25:24> = 4 - number of packets + + These values were chosen so that 000 = continue chain, full sector. +*/ + +#include "sds_defs.h" + +#define UNIT_V_WLK (UNIT_V_UF + 0) /* write locked */ +#define UNIT_WLK (1 << UNIT_V_WLK) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define DSK_PKTWD 16 /* words/packet */ +#define DSK_NUMPKT 4 /* packets/sector */ +#define DSK_NUMWD (DSK_PKTWD*DSK_NUMPKT) /* words/sector */ +#define DSK_N_SC 5 /* sect addr width */ +#define DSK_V_SC 0 /* position */ +#define DSK_M_SC ((1 << DSK_N_SC) - 1) /* mask */ +#define DSK_NUMSC (1 << DSK_N_SC) /* sectors/track */ +#define DSK_N_TR 8 /* track addr width */ +#define DSK_V_TR (DSK_N_SC) /* position */ +#define DSK_M_TR ((1 << DSK_N_TR) - 1) /* mask */ +#define DSK_NUMTR (1 << DSK_N_TR) /* tracks/surface */ +#define DSK_N_SF 5 /* surf addr width */ +#define DSK_V_SF (DSK_N_SC + DSK_N_TR) /* position */ +#define DSK_M_SF ((1 << DSK_N_SF) - 1) /* mask */ +#define DSK_NUMSF (1 << DSK_N_SF) /* surfaces/drive */ +#define DSK_SCSIZE (DSK_NUMSF*DSK_NUMTR*DSK_NUMSC) /* sectors/drive */ +#define DSK_AMASK (DSK_SCSIZE - 1) /* address mask */ +#define DSK_SIZE (DSK_SCSIZE * DSK_NUMWD) /* words/drive */ +#define DSK_GETTR(x) (((x) >> DSK_V_TR) & DSK_M_TR) +#define cyl u3 /* curr cylinder */ +#define DSK_SIP (1 << (DSK_N_TR + 2)) +#define DSK_V_PKT 24 +#define DSK_M_PKT 03 +#define DSK_V_CHN 26 +#define DSK_GETPKT(x) (4 - (((x) >> DSK_V_PKT) & DSK_M_PKT)) +#define DSK_ENDCHN(x) ((x) & (1 << DSK_V_CHN)) + +extern uint32 xfr_req; +extern uint32 alert; +extern int32 stop_invins, stop_invdev, stop_inviop; +int32 dsk_da = 0; /* disk addr */ +int32 dsk_op = 0; /* operation */ +int32 dsk_err = 0; /* error flag */ +uint32 dsk_buf[DSK_NUMWD]; /* sector buf */ +int32 dsk_bptr = 0; /* byte ptr */ +int32 dsk_blnt = 0; /* byte lnt */ +int32 dsk_time = 5; /* time per char */ +int32 dsk_stime = 200; /* seek time */ +int32 dsk_stopioe = 1; +DSPT dsk_tplt[] = { /* template */ + { 1, 0 }, + { 1, DEV_OUT }, + { 0, 0 } + }; + +DEVICE dsk_dev; +t_stat dsk_svc (UNIT *uptr); +t_stat dsk_reset (DEVICE *dptr); +t_stat dsk_fill (uint32 dev); +t_stat dsk_read_buf (uint32 dev); +t_stat dsk_write_buf (uint32 dev); +void dsk_end_op (uint32 fl); +t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat); + +/* DSK data structures + + dsk_dev device descriptor + dsk_unit unit descriptor + dsk_reg register list +*/ + +DIB dsk_dib = { CHAN_F, DEV_DSK, XFR_DSK, dsk_tplt, &dsk }; + +UNIT dsk_unit = { UDATA (&dsk_svc, UNIT_FIX+UNIT_ATTABLE, DSK_SIZE) }; + +REG dsk_reg[] = { + { BRDATA (BUF, dsk_buf, 8, 24, DSK_NUMWD) }, + { DRDATA (BPTR, dsk_bptr, 9), PV_LEFT }, + { DRDATA (BLNT, dsk_bptr, 9), PV_LEFT }, + { ORDATA (DA, dsk_da, 21) }, + { ORDATA (INST, dsk_op, 24) }, + { FLDATA (XFR, xfr_req, XFR_V_DSK) }, + { FLDATA (ERR, dsk_err, 0) }, + { DRDATA (WTIME, dsk_time, 24), REG_NZ + PV_LEFT }, + { DRDATA (STIME, dsk_stime,24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, dsk_stopioe, 0) }, + { NULL } + }; + +MTAB dsk_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, NULL }, + { 0 } + }; + +DEVICE dsk_dev = { + "DSK", &dsk_unit, dsk_reg, dsk_mod, + 1, 8, 24, 1, 8, 27, + NULL, NULL, &dsk_reset, + NULL, NULL, NULL, + &dsk_dib, DEV_DISABLE + }; + +/* Moving head disk routine + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result +*/ + +t_stat dsk (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 i, t, new_ch, dsk_wptr, dsk_byte; +t_stat r; + +switch (fnc) { /* case on function */ + + case IO_CONN: /* connect */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != dsk_dib.chan) return SCPE_IERR; /* wrong chan? */ + dsk_op = inst; /* save instr */ + dsk_bptr = dsk_blnt = 0; /* init ptrs */ + for (i = 0; i < DSK_NUMWD; i++) dsk_buf[i] = 0; /* clear buffer */ + xfr_req = xfr_req & ~XFR_DSK; /* clr xfr flg */ + sim_activate (&dsk_unit, dsk_stime); /* activate */ + break; + + case IO_EOM1: /* EOM mode 1 */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != dsk_dib.chan) return SCPE_IERR; /* wrong chan? */ + if (inst & 07600) CRETIOP; /* inv inst? */ + alert = POT_DSK; /* alert */ + break; + + case IO_DISC: /* disconnect */ + dsk_end_op (0); /* normal term */ + if (inst & DEV_OUT) return dsk_fill (inst); /* fill write */ + break; + + case IO_WREOR: /* write eor */ + dsk_end_op (CHF_EOR); /* eor term */ + return dsk_fill (inst); /* fill write */ + + case IO_SKS: /* SKS */ + new_ch = I_GETSKCH (inst); /* sks chan */ + if (new_ch != dsk_dib.chan) return SCPE_IERR; /* wrong chan? */ + t = I_GETSKCND (inst); /* sks cond */ + if (((t == 000) && !sim_is_active (&dsk_unit) &&/* 10026: ready */ + (dsk_unit.flags & UNIT_ATT)) || + ((t == 004) && !dsk_err && /* 11026: !err */ + (dsk_unit.flags & UNIT_ATT)) || + ((t == 010) && ((dsk_unit.cyl & DSK_SIP) == 0)) || /* 12026: on trk */ + ((t == 014) && !(dsk_unit.flags & UNIT_WPRT)) || /* 13026: !wrprot */ + ((t == 001) && (dsk_unit.flags & UNIT_ATT))) /* 10226: online */ + *dat = 1; + break; + + case IO_READ: + xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */ + if (dsk_bptr >= dsk_blnt) { /* no more data? */ + if (r = dsk_read_buf (inst)) return r; /* read sector */ + } + dsk_wptr = dsk_bptr >> 2; /* word pointer */ + dsk_byte = dsk_bptr & 03; /* byte in word */ + *dat = (dsk_buf[dsk_wptr] >> ((3 - dsk_byte) * 6)) & 077; + dsk_bptr = dsk_bptr + 1; /* incr buf ptr */ + if ((dsk_bptr >= dsk_blnt) && /* end sector, */ + ((dsk_op & CHC_BIN) || DSK_ENDCHN (dsk_buf[0])))/* sec mode | eoch? */ + dsk_end_op (CHF_EOR); /* eor term */ + break; + + case IO_WRITE: + xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */ + if (dsk_bptr >= (DSK_NUMWD * 4)) { /* full? */ + if (r = dsk_write_buf (inst)) return r; /* write sector */ + } + dsk_wptr = dsk_bptr >> 2; /* word pointer */ + dsk_buf[dsk_wptr] = ((dsk_buf[dsk_wptr] << 6) | (*dat & 077)) & DMASK; + dsk_bptr = dsk_bptr + 1; /* incr buf ptr */ + break; + + default: + CRETINS; + } + +return SCPE_OK; +} + +/* PIN routine - return disk address */ + +t_stat pin_dsk (uint32 num, uint32 *dat) +{ +*dat = dsk_da; /* ret disk addr */ +return SCPE_OK; +} + +/* POT routine - start seek */ + +t_stat pot_dsk (uint32 num, uint32 *dat) +{ +int32 st; + +if (sim_is_active (&dsk_unit)) return STOP_IONRDY; /* busy? wait */ +dsk_da = (*dat) & DSK_AMASK; /* save dsk addr */ +st = abs (DSK_GETTR (dsk_da) - /* calc seek time */ + (dsk_unit.cyl & DSK_M_TR)) * dsk_stime; +if (st == 0) st = dsk_stime; /* min time */ +sim_activate (&dsk_unit, st); /* set timer */ +dsk_unit.cyl = dsk_unit.cyl | DSK_SIP; /* seeking */ +return SCPE_OK; +} + +/* Unit service and read/write */ + +t_stat dsk_svc (UNIT *uptr) +{ +if (uptr->cyl & DSK_SIP) { /* end seek? */ + uptr->cyl = DSK_GETTR (dsk_da); /* on cylinder */ + if (dsk_op) sim_activate (&dsk_unit, dsk_stime); /* sched r/w */ + } +else { + xfr_req = xfr_req | XFR_DSK; /* set xfr req */ + sim_activate (&dsk_unit, dsk_time); /* activate */ + } +return SCPE_OK; +} + +/* Read sector */ + +t_stat dsk_read_buf (uint32 dev) +{ +int32 da, pkts, awc; + +if ((dsk_unit.flags & UNIT_ATT) == 0) { /* !attached? */ + dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ + CRETIOE (dsk_stopioe, SCPE_UNATT); + } +da = dsk_da * DSK_NUMWD * sizeof (uint32); +fseek (dsk_unit.fileref, da, SEEK_SET); /* locate sector */ +awc = fxread (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref); +if (ferror (dsk_unit.fileref)) { /* error? */ + dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ + return SCPE_IOERR; + } +for ( ; awc < DSK_NUMWD; awc++) dsk_buf[awc] = 0; +pkts = DSK_GETPKT (dsk_buf[0]); /* get packets */ +dsk_blnt = pkts * DSK_PKTWD * 4; /* new buf size */ +dsk_bptr = 0; /* init bptr */ +dsk_da = (dsk_da + 1) & DSK_AMASK; /* incr disk addr */ +return SCPE_OK; +} + +/* Write sector. If this routine is called directly, then the sector + buffer is full, and there is at least one more character to write; + therefore, there are 4 packets in the sector, and the sector is not + the end of the chain. +*/ + +t_stat dsk_write_buf (uint32 dev) +{ +int32 i, da; + +if ((dsk_unit.flags & UNIT_ATT) == 0) { /* !attached? */ + dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ + CRETIOE (dsk_stopioe, SCPE_UNATT); + } +if (dsk_unit.flags & UNIT_WPRT) { /* write prot? */ + dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ + return SCPE_OK; + } +da = dsk_da * DSK_NUMWD * sizeof (uint32); +fseek (dsk_unit.fileref, da, SEEK_SET); /* locate sector */ +fxwrite (dsk_buf, sizeof (uint32), DSK_NUMWD, dsk_unit.fileref); +if (ferror (dsk_unit.fileref)) { /* error? */ + dsk_end_op (CHF_ERR | CHF_EOR); /* disk error */ + return SCPE_IOERR; + } +dsk_bptr = 0; /* init bptr */ +dsk_da = (dsk_da + 1) & DSK_AMASK; /* incr disk addr */ +for (i = 0; i < DSK_NUMWD; i++) dsk_buf[i] = 0; /* clear buffer */ +return SCPE_OK; +} + +/* Fill incomplete sector at end of operation. Calculate the number + of packets and set the end of chain flag. +*/ + +t_stat dsk_fill (uint32 dev) +{ +int32 nochn = (dsk_op & CHC_BIN)? 0: 1; /* chain? */ +int32 pktend = (dsk_bptr + ((DSK_PKTWD * 4) - 1)) & /* end pkt */ + ~((DSK_PKTWD * 4) - 1); +int32 pkts = pktend / (DSK_PKTWD * 4); /* # packets */ + +if (dsk_bptr == 0) return SCPE_OK; /* no fill? */ +for ( ; dsk_bptr < pktend; dsk_bptr++) { /* fill packet */ + int32 dsk_wptr = dsk_bptr >> 2; + dsk_buf[dsk_wptr] = (dsk_buf[dsk_wptr] << 6) & DMASK; + } +dsk_buf[0] = dsk_buf[0] | (nochn << DSK_V_CHN) | /* insert chain, */ + ((4 - pkts) << DSK_V_PKT); /* num pkts */ +return dsk_write_buf (dev); /* write sec */ +} + +/* Terminate DSK operation */ + +void dsk_end_op (uint32 fl) +{ +if (fl) chan_set_flag (dsk_dib.chan, fl); /* set flags */ +dsk_op = 0; /* clear op */ +xfr_req = xfr_req & ~XFR_DSK; /* clear xfr */ +sim_cancel (&dsk_unit); /* stop */ +if (fl & CHF_ERR) { /* error? */ + chan_disc (dsk_dib.chan); /* disconnect */ + dsk_err = 1; /* set disk err */ + } +return; +} + +/* Reset routine */ + +t_stat dsk_reset (DEVICE *dptr) +{ +int32 i; + +chan_disc (dsk_dib.chan); /* disconnect */ +dsk_da = 0; /* clear state */ +dsk_op = 0; +dsk_err = 0; +dsk_bptr = dsk_blnt = 0; +xfr_req = xfr_req & ~XFR_DSK; /* clr xfr req */ +sim_cancel (&dsk_unit); /* deactivate */ +dsk_unit.cyl = 0; +for (i = 0; i < DSK_NUMWD; i++) dsk_buf[i] = 0; /* clear buffer */ +return SCPE_OK; +} diff --git a/SDS/sds_io.c b/SDS/sds_io.c new file mode 100644 index 0000000..9e2e37e --- /dev/null +++ b/SDS/sds_io.c @@ -0,0 +1,940 @@ +/* sds_io.c: SDS 940 I/O simulator + + Copyright (c) 2001-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include "sds_defs.h" + +/* Data chain word */ + +#define CHD_INT 040 /* int on chain */ +#define CHD_PAGE 037 /* new page # */ + +/* Interlace POT */ + +#define CHI_V_WC 14 /* word count */ +#define CHI_M_WC 01777 +#define CHI_GETWC(x) (((x) >> CHI_V_WC) & CHI_M_WC) +#define CHI_V_MA 0 /* mem address */ +#define CHI_M_MA 037777 +#define CHI_GETMA(x) (((x) >> CHI_V_MA) & CHI_M_MA) + +/* System interrupt POT */ + +#define SYI_V_GRP 18 /* group */ +#define SYI_M_GRP 077 +#define SYI_GETGRP(x) (((x) >> SYI_V_GRP) & SYI_M_GRP) +#define SYI_DIS (1 << 17) /* disarm if 0 */ +#define SYI_ARM (1 << 16) /* arm if 1 */ +#define SYI_M_INT 0177777 /* interrupt */ + +/* Pseudo-device number for EOM/SKS mode 3 */ + +#define I_GETDEV3(x) ((((x) & 020046000) != 020046000)? ((x) & DEV_MASK): DEV_MASK) + +#define TST_XFR(d,c) (xfr_req && dev_map[d][c]) +#define SET_XFR(d,c) xfr_req = xfr_req | dev_map[d][c] +#define CLR_XFR(d,c) xfr_req = xfr_req & ~dev_map[d][c] +#define INV_DEV(d,c) (dev_dsp[d][c] == NULL) +#define VLD_DEV(d,c) (dev_dsp[d][c] != NULL) +#define TST_EOR(c) (chan_flag[c] & CHF_EOR) +#define QAILCE(a) (((a) >= POT_ILCY) && ((a) < (POT_ILCY + NUM_CHAN))) + +uint8 chan_uar[NUM_CHAN]; /* unit addr */ +uint16 chan_wcr[NUM_CHAN]; /* word count */ +uint16 chan_mar[NUM_CHAN]; /* mem addr */ +uint8 chan_dcr[NUM_CHAN]; /* data chain */ +uint32 chan_war[NUM_CHAN]; /* word assembly */ +uint8 chan_cpw[NUM_CHAN]; /* char per word */ +uint8 chan_cnt[NUM_CHAN]; /* char count */ +uint16 chan_mode[NUM_CHAN]; /* mode */ +uint16 chan_flag[NUM_CHAN]; /* flags */ +static const char *chname[NUM_CHAN] = { + "W", "Y", "C", "D", "E", "F", "G", "H" + }; + +extern uint32 M[MAXMEMSIZE]; /* memory */ +extern uint32 int_req; /* int req */ +extern uint32 xfr_req; /* xfer req */ +extern uint32 alert; /* pin/pot alert */ +extern uint32 X, EM2, EM3, OV, ion, bpt; +extern uint32 nml_mode, usr_mode, rtc_pie; +extern int32 stop_invins, stop_invdev, stop_inviop; +extern int32 mon_usr_trap; +extern UNIT cpu_unit; +extern FILE *sim_log; +extern DEVICE *sim_devices[]; + +t_stat chan_reset (DEVICE *dptr); +t_stat chan_read (int32 ch); +t_stat chan_write (int32 ch); +void chan_write_mem (int32 ch); +void chan_flush_war (int32 ch); +uint32 chan_mar_inc (int32 ch); +t_stat chan_eor (int32 ch); +t_stat pot_ilc (uint32 num, uint32 *dat); +t_stat pot_dcr (uint32 num, uint32 *dat); +t_stat pin_adr (uint32 num, uint32 *dat); +t_stat pot_fork (uint32 num, uint32 *dat); +t_stat dev_disc (uint32 ch, uint32 dev); +t_stat dev_wreor (uint32 ch, uint32 dev); +extern t_stat pot_RL1 (uint32 num, uint32 *dat); +extern t_stat pot_RL2 (uint32 num, uint32 *dat); +extern t_stat pot_RL4 (uint32 num, uint32 *dat); +extern t_stat pin_rads (uint32 num, uint32 *dat); +extern t_stat pot_rada (uint32 num, uint32 *dat); +extern t_stat pin_dsk (uint32 num, uint32 *dat); +extern t_stat pot_dsk (uint32 num, uint32 *dat); +t_stat pin_mux (uint32 num, uint32 *dat); +t_stat pot_mux (uint32 num, uint32 *dat); +extern void set_dyn_map (void); + +/* SDS I/O model + + A device is modeled by its interactions with a channel. Devices can only be + accessed via channels. Each channel has its own device address space. This + means devices can only be accessed from a specific channel. + + I/O operations start with a channel connect. The EOM instruction is passed + to the device via the conn routine. This routine is also used for non-channel + EOM's to the device. For channel connects, the device must remember the + channel number. + + The device responds (after a delay) by setting its XFR_RDY flag. This causes + the channel to invoke either the read or write routine (for input or output) + to get or put the next character. If the device is an asynchronous output + device, it calls routine chan_set_ordy to see if there is output available. + If there is, XFR_RDY is set; if not, the channel is marked to wake the + attached device when output is available. This prevents invalid rate errors. + + Output may be terminated by a write end of record, a disconnect, or both. + Write end of record occurs when the word count reaches zero on an IORD or IORP + operation. It also occurs if a TOP instruction is issued. The device is + expected to respond by setting the end of record indicator in the channel, + which will in turn trigger an end of record interrupt. + + When the channel operation completes, the channel disconnects and calls the + disconnect processor to perform any device specific cleanup. The differences + between write end of record and disconnect are subtle. On magtape output, + for example, both signal end of record; but write end of record allows the + magtape to continue moving, while disconnect halts its motion. + + Valid devices supply a routine to handle potentially all I/O operations + (connect, disconnect, read, write, write end of record, sks). There are + separate routines for PIN and POT. + + Channels could, optionally, handle 12b or 24b characters. The simulator can + support all widths. +*/ + +t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc); + +struct aldisp { + t_stat (*pin) (uint32 num, uint32 *dat); /* altnum, *dat */ + t_stat (*pot) (uint32 num, uint32 *dat); /* altnum, *dat */ + }; + +/* Channel data structures + + chan_dev channel device descriptor + chan_unit channel unit descriptor + chan_reg channel register list +*/ + +UNIT chan_unit = { UDATA (NULL, 0, 0) }; + +REG chan_reg[] = { + { BRDATA (UAR, chan_uar, 8, 6, NUM_CHAN) }, + { BRDATA (WCR, chan_wcr, 8, 15, NUM_CHAN) }, + { BRDATA (MAR, chan_mar, 8, 16, NUM_CHAN) }, + { BRDATA (DCR, chan_dcr, 8, 6, NUM_CHAN) }, + { BRDATA (WAR, chan_war, 8, 24, NUM_CHAN) }, + { BRDATA (CPW, chan_cpw, 8, 2, NUM_CHAN) }, + { BRDATA (CNT, chan_cnt, 8, 3, NUM_CHAN) }, + { BRDATA (MODE, chan_mode, 8, 12, NUM_CHAN) }, + { BRDATA (FLAG, chan_flag, 8, CHF_N_FLG, NUM_CHAN) }, + { NULL } + }; + +MTAB chan_mod[] = { + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_W, "W", NULL, + NULL, &chan_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_Y, "Y", NULL, + NULL, &chan_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_C, "C", NULL, + NULL, &chan_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_D, "D", NULL, + NULL, &chan_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_E, "E", NULL, + NULL, &chan_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_F, "F", NULL, + NULL, &chan_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_G, "G", NULL, + NULL, &chan_show_reg, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, CHAN_H, "H", NULL, + NULL, &chan_show_reg, NULL } + }; + +DEVICE chan_dev = { + "CHAN", &chan_unit, chan_reg, chan_mod, + 1, 8, 8, 1, 8, 8, + NULL, NULL, &chan_reset, + NULL, NULL, NULL + }; + +/* Tables */ + +static const uint32 int_zc[8] = { + INT_WZWC, INT_YZWC, INT_CZWC, INT_DZWC, + INT_EZWC, INT_FZWC, INT_GZWC, INT_HZWC + }; + +static const uint32 int_er[8] = { + INT_WEOR, INT_YEOR, INT_CEOR, INT_DEOR, + INT_EEOR, INT_FEOR, INT_GEOR, INT_HEOR + }; + +/* dev_map maps device and channel numbers to a transfer flag masks */ + +uint32 dev_map[64][NUM_CHAN]; + +/* dev_dsp maps device and channel numbers to dispatch routines */ + +t_stat (*dev_dsp[64][NUM_CHAN])() = { NULL }; + +/* dev3_dsp maps system device numbers to dispatch routines */ + +t_stat (*dev3_dsp[64])() = { NULL }; + +/* dev_alt maps alert numbers to dispatch routines */ + +struct aldisp dev_alt[] = { + { NULL, NULL }, + { NULL, &pot_ilc }, { NULL, &pot_ilc }, + { NULL, &pot_ilc }, { NULL, &pot_ilc }, + { NULL, &pot_ilc }, { NULL, &pot_ilc }, + { NULL, &pot_ilc }, { NULL, &pot_ilc }, + { NULL, &pot_dcr }, { NULL, &pot_dcr }, + { NULL, &pot_dcr }, { NULL, &pot_dcr }, + { NULL, &pot_dcr }, { NULL, &pot_dcr }, + { NULL, &pot_dcr }, { NULL, &pot_dcr }, + { &pin_adr, NULL }, { &pin_adr, NULL }, + { &pin_adr, NULL }, { &pin_adr, NULL }, + { &pin_adr, NULL }, { &pin_adr, NULL }, + { &pin_adr, NULL }, { &pin_adr, NULL }, + { NULL, &pot_RL1 }, { NULL, &pot_RL2 }, + { NULL, &pot_RL4 }, + { &pin_rads, NULL }, { NULL, &pot_rada }, + { &pin_dsk, &pot_dsk }, { NULL, &pot_fork }, + { &pin_mux, &pot_mux } + }; + +/* Single word I/O instructions */ + +t_stat op_wyim (uint32 inst, uint32 *dat) +{ +int32 ch, dev; + +ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */ +dev = chan_uar[ch] & DEV_MASK; /* get dev # */ +if (chan_cnt[ch] <= chan_cpw[ch]) { /* buffer empty? */ + if (dev == 0) return STOP_INVIOP; /* no device? dead */ + return STOP_IONRDY; /* hang until full */ + } +*dat = chan_war[ch]; /* get data */ +chan_war[ch] = 0; /* reset war */ +chan_cnt[ch] = 0; /* reset cnt */ +return SCPE_OK; +} + +t_stat op_miwy (uint32 inst, uint32 dat) +{ +int32 ch, dev; + +ch = (inst & 000200000)? CHAN_W: CHAN_Y; /* get chan# */ +dev = chan_uar[ch] & DEV_MASK; /* get dev # */ +if (chan_cnt[ch] != 0) { /* buffer full? */ + if (dev == 0) return STOP_INVIOP; /* no device? dead */ + return STOP_IONRDY; /* hang until full */ + } +chan_war[ch] = dat; /* get data */ +chan_cnt[ch] = chan_cpw[ch] + 1; /* buffer full */ +if (chan_flag[ch] & CHF_OWAK) { /* output wake? */ + if (VLD_DEV (dev, ch)) SET_XFR (dev, ch); /* wake channel */ + chan_flag[ch] = chan_flag[ch] & ~CHF_OWAK; /* clear wake */ + } +return SCPE_OK; +} + +t_stat op_pin (uint32 *dat) +{ +uint32 al = alert; /* local copy */ + +alert = 0; /* clear alert */ +if ((al == 0) || (dev_alt[al].pin == NULL)) CRETIOP; /* inv alert? */ +return dev_alt[al].pin (al, dat); /* PIN from dev */ +} + +t_stat op_pot (uint32 dat) +{ +uint32 al = alert; /* local copy */ + +alert = 0; /* clear alert */ +if ((al == 0) || (dev_alt[al].pot == NULL)) CRETIOP; /* inv alert? */ +return dev_alt[al].pot (al, &dat); /* POT to dev */ +} + +/* EOM/EOD */ + +t_stat op_eomd (uint32 inst) +{ +uint32 mod = I_GETIOMD (inst); /* get mode */ +uint32 ch = I_GETEOCH (inst); /* get chan # */ +uint32 dev = inst & DEV_MASK; /* get dev # */ +uint32 ch_dev = chan_uar[ch] & DEV_MASK; /* chan curr dev # */ +t_stat r; + +switch (mod) { + + case 0: /* IO control */ + if (dev) { /* new dev? */ + if (ch_dev) CRETIOP; /* chan act? err */ + if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */ + chan_war[ch] = chan_cnt[ch] = 0; /* init chan */ + chan_flag[ch] = chan_dcr[ch] = 0; + chan_mode[ch] = chan_uar[ch] = 0; + if (ch >= CHAN_E) chan_mode[ch] = CHM_CE; + if (r = dev_dsp[dev][ch] (IO_CONN, inst, NULL)) /* connect */ + return r; + if ((inst & I_IND) || (ch >= CHAN_C)) { /* C-H? alert ilc */ + alert = POT_ILCY + ch; + chan_mar[ch] = chan_wcr[ch] = 0; + } + if (chan_flag[ch] & CHF_24B) chan_cpw[ch] = 0; /* 24B? 1 ch/wd */ + else if (chan_flag[ch] & CHF_12B) /* 12B? 2 ch/wd */ + chan_cpw[ch] = CHC_GETCPW (inst) & 1; + else chan_cpw[ch] = CHC_GETCPW (inst); /* 6b, 1-4 ch/wd */ + chan_uar[ch] = dev; /* connected */ + if ((dev & DEV_OUT) && ion && !QAILCE (alert)) /* out, prog IO? */ + int_req = int_req | int_zc[ch]; /* initial intr */ + } + else return dev_disc (ch, ch_dev); /* disconnect */ + break; + + case 1: /* buf control */ + if (QAILCE (alert)) { /* ilce alerted? */ + ch = alert - POT_ILCY; /* derive chan */ + if (ch >= CHAN_E) inst = inst | CHM_CE; /* DACC? ext */ + chan_mode[ch] = inst; /* save mode */ + chan_mar[ch] = (CHM_GETHMA (inst) << 14) | /* get hi mar */ + (chan_mar[ch] & CHI_M_MA); + chan_wcr[ch] = (CHM_GETHWC (inst) << 10) | /* get hi wc */ + (chan_wcr[ch] & CHI_M_WC); + } + else if (dev) { /* dev EOM */ + if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */ + return dev_dsp[dev][ch] (IO_EOM1, inst, NULL); + } + else { /* chan EOM */ + inst = inst & 047677; + if (inst == 040000) { /* alert ilce */ + alert = POT_ILCY + ch; + chan_mar[ch] = chan_wcr[ch] = 0; + } + else if (inst == 002000) alert = POT_ADRY + ch; /* alert addr */ + else if (inst == 001000) alert = POT_DCRY + ch; /* alert DCR */ + else if (inst == 004000) { /* term output */ + if (ch_dev & DEV_OUT) { /* to output dev? */ + if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* busy, DMA? */ + chan_flag[ch] = chan_flag[ch] | CHF_TOP; /* TOP pending */ + else return dev_wreor (ch, ch_dev); /* idle, write EOR */ + } /* end else TOP */ + else if (ch_dev & DEV_MT) { /* change to scan? */ + chan_uar[ch] = chan_uar[ch] | DEV_MTS; /* change dev addr */ + chan_flag[ch] = chan_flag[ch] | CHF_SCAN; /* set scan flag */ + } /* end else change scan */ + } /* end else term output */ + } /* end else chan EOM */ + break; + + case 2: /* internal */ + if (ch >= CHAN_E) { /* EOD? */ + if (inst & 00300) { /* set EM? */ + if (inst & 00100) EM2 = inst & 07; + if (inst & 00200) EM3 = (inst >> 3) & 07; + set_dyn_map (); + } + break; + } /* end if EOD */ + if (inst & 00001) OV = 0; /* clr OV */ + if (inst & 00002) ion = 1; /* ion */ + else if (inst & 00004) ion = 0; /* iof */ + if ((inst & 00010) && (((X >> 1) ^ X) & EXPS)) OV = 1; + if (inst & 00020) alert = POT_SYSI; /* alert sys int */ + if (inst & 00100) rtc_pie = 1; /* arm clk pls */ + else if (inst & 00200) rtc_pie = 0; /* disarm pls */ + if ((inst & 01400) == 01400) alert = POT_RL4; /* alert RL4 */ + else if (inst & 00400) alert = POT_RL1; /* alert RL1 */ + else if (inst & 01000) alert = POT_RL2; /* alert RL2 */ + if (inst & 02000) { /* nml to mon */ + nml_mode = usr_mode = 0; + if (inst & 00400) mon_usr_trap = 1; + } + break; + + case 3: /* special */ + dev = I_GETDEV3 (inst); /* special device */ + if (dev3_dsp[dev]) /* defined? */ + return dev3_dsp[dev] (IO_CONN, inst, NULL); + CRETINS; + } /* end case */ + +return SCPE_OK; +} + +/* Skip if not signal */ + +t_stat op_sks (uint32 inst, uint32 *dat) +{ +uint32 mod = I_GETIOMD (inst); /* get mode */ +uint32 ch = I_GETSKCH (inst); /* get chan # */ +uint32 dev = inst & DEV_MASK; /* get dev # */ + +*dat = 0; +if ((ch == 4) && !(inst & 037774)) { /* EM test */ + if (((inst & 0001) && (EM2 != 2)) || + ((inst & 0002) && (EM3 != 3))) *dat = 1; + return SCPE_OK; + } +switch (mod) { + + case 1: /* ch, dev */ + if (dev) { /* device */ + if (INV_DEV (dev, ch)) CRETDEV; /* inv dev? err */ + dev_dsp[dev][ch] (IO_SKS, inst, dat); /* do test */ + } + else { /* channel */ + if (((inst & 04000) && (chan_uar[ch] == 0)) || + ((inst & 02000) && (chan_wcr[ch] == 0)) || + ((inst & 01000) && ((chan_flag[ch] & CHF_ERR) == 0)) || + ((inst & 00400) && (chan_flag[ch] & CHF_IREC))) *dat = 1; + } + break; + + case 2: /* internal test */ + if (inst & 0001) { /* test OV */ + *dat = OV ^ 1; /* skip if off */ + OV = 0; /* and reset */ + break; + } + if (((inst & 00002) && !ion) || /* ion, bpt test */ + ((inst & 00004) && ion) || + ((inst & 00010) && ((chan_flag[CHAN_W] & CHF_ERR) == 0)) || + ((inst & 00020) && ((chan_flag[CHAN_Y] & CHF_ERR) == 0)) || + ((inst & 00040) && ((bpt & 001) == 0)) || + ((inst & 00100) && ((bpt & 002) == 0)) || + ((inst & 00200) && ((bpt & 004) == 0)) || + ((inst & 00400) && ((bpt & 010) == 0)) || + ((inst & 01000) && (chan_uar[CHAN_W] == 0)) || + ((inst & 02000) && (chan_uar[CHAN_Y] == 0))) *dat = 1; + break; + + case 3: /* special */ + dev = I_GETDEV3 (inst); /* special device */ + if (dev3_dsp[dev]) dev3_dsp[dev] (IO_SKS, inst, dat); + else CRETINS; + } /* end case */ + +return SCPE_OK; +} + +/* PIN/POT routines */ + +t_stat pot_ilc (uint32 num, uint32 *dat) +{ +uint32 ch = num - POT_ILCY; + +chan_mar[ch] = (chan_mar[ch] & ~CHI_M_MA) | CHI_GETMA (*dat); +chan_wcr[ch] = (chan_wcr[ch] & ~CHI_M_WC) | CHI_GETWC (*dat); +chan_flag[ch] = chan_flag[ch] | CHF_ILCE; +return SCPE_OK; +} + +t_stat pot_dcr (uint32 num, uint32 *dat) +{ +uint32 ch = num - POT_DCRY; + +chan_dcr[ch] = (*dat) & (CHD_INT | CHD_PAGE); +chan_flag[ch] = chan_flag[ch] | CHF_DCHN; +return SCPE_OK; +} + +t_stat pin_adr (uint32 num, uint32 *dat) +{ +uint32 ch = num - POT_ADRY; + +*dat = chan_mar[ch] & PAMASK; +return SCPE_OK; +} + +/* System interrupt POT. + + The SDS 940 timesharing system uses a permanently asserted + system interrupt as a way of forking the teletype input + interrupt handler to a lower priority. The interrupt is + armed to set up the fork, and disarmed in the fork routine */ + +t_stat pot_fork (uint32 num, uint32 *dat) +{ +uint32 igrp = SYI_GETGRP (*dat); /* get group */ +uint32 fbit = (1 << (VEC_FORK & 017)); /* bit in group */ + +if (igrp == (VEC_FORK / 020)) { /* right group? */ + if ((*dat & SYI_ARM) && (*dat & fbit)) /* arm, bit set? */ + int_req = int_req | INT_FORK; + if ((*dat & SYI_DIS) && !(*dat & fbit)) /* disarm, bit clr? */ + int_req = int_req & ~INT_FORK; + } +return SCPE_OK; +} + +/* Channel read invokes the I/O device to get the next character and, + if not end of record, assembles it into the word assembly register. + If the interlace is on, the full word is stored in memory. + The key difference points for the various terminal functions are + + end of record comp: EOT interrupt + IORD, IOSD: EOR interrupt, disconnect + IORP, IOSP: EOR interrupt, interrecord + interlace off: comp: EOW interrupt + IORD, IORP: ignore + IOSD, IOSP: overrun error + --wcr == 0: comp: clear interlace + IORD, IORP, IOSP: ZWC interrupt + IOSD: ZWC interrupt, EOR interrupt, disconnect + + Note that the channel can be disconnected if CHN_EOR is set, but must + not be if XFR_REQ is set */ + +t_stat chan_read (int32 ch) +{ +uint32 dat = 0; +uint32 dev = chan_uar[ch] & DEV_MASK; +uint32 tfnc = CHM_GETFNC (chan_mode[ch]); +t_stat r = SCPE_OK; + +if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */ + if (INV_DEV (dev, ch)) CRETIOP; /* can't read? */ + r = dev_dsp[dev][ch] (IO_READ, dev, &dat); /* read data */ + if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */ + if (chan_flag[ch] & CHF_24B) chan_war[ch] = dat; /* 24B? */ + else if (chan_flag[ch] & CHF_12B) /* 12B? */ + chan_war[ch] = ((chan_war[ch] << 12) | (dat & 07777)) & DMASK; + else chan_war[ch] = ((chan_war[ch] << 6) | (dat & 077)) & DMASK; + if (chan_flag[ch] & CHF_SCAN) /* scanning? */ + chan_cnt[ch] = chan_cpw[ch]; /* never full */ + else chan_cnt[ch] = chan_cnt[ch] + 1; /* insert char */ + if (chan_cnt[ch] > chan_cpw[ch]) { /* full? */ + if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */ + chan_write_mem (ch); /* write to mem */ + if (chan_wcr[ch] == 0) { /* wc zero? */ + chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* clr interlace */ + if ((tfnc != CHM_COMP) && (chan_mode[ch] & CHM_ZC)) + int_req = int_req | int_zc[ch]; /* zwc interrupt */ + if (tfnc == CHM_IOSD) { /* IOSD? also EOR */ + if (chan_mode[ch] & CHM_ER) int_req = int_req | int_er[ch]; + dev_disc (ch, dev); /* disconnect */ + } /* end if IOSD */ + } /* end if wcr == 0 */ + } /* end if ilce on */ + else { /* interlace off */ + if (TST_EOR (ch)) return chan_eor (ch); /* eor? */ + if (tfnc == CHM_COMP) { /* C: EOW, intr */ + if (ion) int_req = int_req | int_zc[ch]; + } + else if (tfnc & CHM_SGNL) /* Sx: error */ + chan_flag[ch] = chan_flag[ch] | CHF_ERR; + else chan_cnt[ch] = chan_cpw[ch]; /* Rx: ignore */ + } /* end else ilce */ + } /* end if full */ + } /* end if xfr */ +if (TST_EOR (ch)) { /* end record? */ + if (tfnc == CHM_COMP) chan_flush_war (ch); /* C: fill war */ + else if (chan_cnt[ch]) { /* RX, CX: fill? */ + chan_flush_war (ch); /* fill war */ + if (chan_flag[ch] & CHF_ILCE) /* ilce on? store */ + chan_write_mem (ch); + } /* end else if cnt */ + return chan_eor (ch); /* eot/eor int */ + } +return r; +} + +void chan_write_mem (int32 ch) +{ +WriteP (chan_mar[ch], chan_war[ch]); /* write to mem */ +chan_mar[ch] = chan_mar_inc (ch); /* incr mar */ +chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr wcr */ +chan_war[ch] = 0; /* reset war */ +chan_cnt[ch] = 0; /* reset cnt */ +return; +} + +void chan_flush_war (int32 ch) +{ +int32 i = (chan_cpw[ch] - chan_cnt[ch]) + 1; + +if (i) { + if (chan_flag[ch] & CHF_24B) chan_war[ch] = 0; + else if (chan_flag[ch] & CHF_12B) + chan_war[ch] = (chan_war[ch] << 12) & DMASK; + else chan_war[ch] = (chan_war[ch] << (i * 6)) & DMASK; + chan_cnt[ch] = chan_cpw[ch] + 1; + } +return; +} + +/* Channel write gets the next character and sends it to the I/O device. + If this is the last character in an interlace operation, the end of + record operation is invoked. + The key difference points for the various terminal functions are + + end of record: comp: EOT interrupt + IORD, IOSD: EOR interrupt, disconnect + IORP, IOSP: EOR interrupt, interrecord + interlace off: if not end of record, EOW interrupt + --wcr == 0: comp: EOT interrupt, disconnect + IORD, IORP: ignore + IOSD: ZWC interrupt, disconnect + IOSP: ZWC interrupt, interrecord +*/ +t_stat chan_write (int32 ch) +{ +uint32 dat = 0; +uint32 dev = chan_uar[ch] & DEV_MASK; +uint32 tfnc = CHM_GETFNC (chan_mode[ch]); +t_stat r = SCPE_OK; + +if (dev && TST_XFR (dev, ch)) { /* ready to xfr? */ + if (INV_DEV (dev, ch)) CRETIOP; /* invalid dev? */ + if (chan_cnt[ch] == 0) { /* buffer empty? */ + if (chan_flag[ch] & CHF_ILCE) { /* interlace on? */ + chan_war[ch] = ReadP (chan_mar[ch]); + chan_mar[ch] = chan_mar_inc (ch); /* incr mar */ + chan_wcr[ch] = (chan_wcr[ch] - 1) & 077777; /* decr mar */ + chan_cnt[ch] = chan_cpw[ch] + 1; /* set cnt */ + } + else { /* ilce off */ + CLR_XFR (dev, ch); /* cant xfr */ + if (TST_EOR (dev)) return chan_eor (ch); /* EOR? */ + chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* rate err */ + return SCPE_OK; + } /* end else ilce */ + } /* end if cnt */ + chan_cnt[ch] = chan_cnt[ch] - 1; /* decr cnt */ + if (chan_flag[ch] & CHF_24B) dat = chan_war[ch]; /* 24B? */ + else if (chan_flag[ch] & CHF_12B) { /* 12B? */ + dat = (chan_war[ch] >> 12) & 07777; /* get halfword */ + chan_war[ch] = (chan_war[ch] << 12) & DMASK; /* remove from war */ + } + else { /* 6B */ + dat = (chan_war[ch] >> 18) & 077; /* get char */ + chan_war[ch] = (chan_war[ch] << 6) & DMASK; /* remove from war */ + } + r = dev_dsp[dev][ch] (IO_WRITE, dev, &dat); /* write */ + if (r) chan_flag[ch] = chan_flag[ch] | CHF_ERR; /* error? */ + if (chan_cnt[ch] == 0) { /* buf empty? */ + if (chan_flag[ch] & CHF_ILCE) { /* ilce on? */ + if (chan_wcr[ch] == 0) { /* wc now 0? */ + chan_flag[ch] = chan_flag[ch] & ~CHF_ILCE; /* ilc off */ + if (tfnc == CHM_COMP) { /* compatible? */ + if (ion) int_req = int_req | int_zc[ch]; + dev_disc (ch, dev); /* disconnnect */ + } /* end if comp */ + else { /* extended */ + if (chan_mode[ch] & CHM_ZC) /* ZWC int */ + int_req = int_req | int_zc[ch]; + if (tfnc == CHM_IOSD) { /* SD */ + if (chan_mode[ch] & CHM_ER) /* EOR int */ + int_req = int_req | int_er[ch]; + dev_disc (ch, dev); /* disconnnect */ + } /* end if SD */ + else if (!(tfnc && CHM_SGNL) || /* IORx or IOSP TOP? */ + (chan_flag[ch] & CHF_TOP)) + dev_wreor (ch, dev); /* R: write EOR */ + chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; + } /* end else comp */ + } /* end if wcr */ + } /* end if ilce */ + else if (chan_flag[ch] & CHF_TOP) { /* off, TOP pending? */ + chan_flag[ch] = chan_flag[ch] & ~CHF_TOP; /* clear TOP */ + dev_wreor (ch, dev); /* write EOR */ + } + else if (ion) int_req = int_req | int_zc[ch]; /* no TOP, EOW intr */ + } /* end if cnt */ + } /* end if xfr */ +if (TST_EOR (ch)) return chan_eor (ch); /* eor rcvd? */ +return r; +} + +/* MAR increment */ + +uint32 chan_mar_inc (int32 ch) +{ +uint32 t = (chan_mar[ch] + 1) & PAMASK; /* incr mar */ + +if ((chan_flag[ch] & CHF_DCHN) && ((t & VA_POFF) == 0)) { /* chain? */ + chan_flag[ch] = chan_flag[ch] & ~CHF_DCHN; /* clr flag */ + if (chan_dcr[ch] & CHD_INT) /* if armed, intr */ + int_req = int_req | int_zc[ch]; + t = (chan_dcr[ch] & CHD_PAGE) << VA_V_PN; /* new mar */ + } +return t; +} + +/* End of record action */ + +t_stat chan_eor (int32 ch) +{ +uint32 tfnc = CHM_GETFNC (chan_mode[ch]); +uint32 dev = chan_uar[ch] & DEV_MASK; + +chan_flag[ch] = chan_flag[ch] & ~(CHF_EOR | CHF_ILCE); /* clr eor, ilce */ +if (((tfnc == CHM_COMP) && ion) || (chan_mode[ch] & CHM_ER)) + int_req = int_req | int_er[ch]; /* EOT/EOR? */ +if (dev && (tfnc & CHM_PROC)) /* P, still conn? */ + chan_flag[ch] = chan_flag[ch] | CHF_IREC; /* interrecord */ +else return dev_disc (ch, dev); /* disconnect */ +return SCPE_OK; +} + +/* Utility routines */ + +t_stat dev_disc (uint32 ch, uint32 dev) +{ +chan_uar[ch] = 0; /* disconnect */ +if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_DISC, dev, NULL); +return SCPE_OK; +} + +t_stat dev_wreor (uint32 ch, uint32 dev) +{ +if (dev_dsp[dev][ch]) return dev_dsp[dev][ch] (IO_WREOR, dev, NULL); +chan_flag[ch] = chan_flag[ch] | CHF_EOR; /* set eor */ +return SCPE_OK; +} + +/* Externally visible routines */ +/* Channel driver */ + +t_stat chan_process (void) +{ +int32 i, dev; +t_stat r; + +for (i = 0; i < NUM_CHAN; i++) { /* loop thru */ + dev = chan_uar[i] & DEV_MASK; /* get dev */ + if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) { /* chan active? */ + if (dev & DEV_OUT) r = chan_write (i); /* write */ + else r = chan_read (i); /* read */ + if (r) return r; + } + } +return SCPE_OK; +} + +/* Test for channel active */ + +t_bool chan_testact (void) +{ +int32 i, dev; + +for (i = 0; i < NUM_CHAN; i++) { + dev = chan_uar[i] & DEV_MASK; + if ((dev && TST_XFR (dev, i)) || TST_EOR (i)) return 1; + } +return 0; +} + +/* Async output device ready for more data */ + +void chan_set_ordy (int32 ch) +{ +if ((ch >= 0) && (ch < NUM_CHAN)) { + int32 dev = chan_uar[ch] & DEV_MASK; /* get dev */ + if (chan_cnt[ch] || (chan_flag[ch] & CHF_ILCE)) /* buf or ilce? */ + SET_XFR (dev, ch); /* set xfr flg */ + else chan_flag[ch] = chan_flag[ch] | CHF_OWAK; /* need wakeup */ + } +return; +} + +/* Set flag in channel */ + +void chan_set_flag (int32 ch, uint32 fl) +{ +if ((ch >= 0) && (ch < NUM_CHAN)) chan_flag[ch] = chan_flag[ch] | fl; +return; +} + +/* Set UAR in channel */ + +void chan_set_uar (int32 ch, uint32 dev) +{ +if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = dev & DEV_MASK; +return; +} + +/* Disconnect channel */ + +void chan_disc (int32 ch) +{ +if ((ch >= 0) && (ch < NUM_CHAN)) chan_uar[ch] = 0; +return; +} + +/* Reset channels */ + +t_stat chan_reset (DEVICE *dptr) +{ +int32 i; + +xfr_req = 0; +for (i = 0; i < NUM_CHAN; i++) { + chan_uar[i] = 0; + chan_wcr[i] = 0; + chan_mar[i] = 0; + chan_dcr[i] = 0; + chan_war[i] = 0; + chan_cpw[i] = 0; + chan_cnt[i] = 0; + chan_mode[i] = 0; + chan_flag[i] = 0; + } +return SCPE_OK; +} + +/* Channel assignment routines */ + +t_stat set_chan (UNIT *uptr, int32 val, char *sptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +int32 i; + +if (sptr == NULL) return SCPE_ARG; /* valid args? */ +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +for (i = 0; i < NUM_CHAN; i++) { /* match input */ + if (strcmp (sptr, chname[i]) == 0) { /* find string */ + if (val && !(val & (1 << i))) return SCPE_ARG; /* legal? */ + dibp->chan = i; /* store new */ + return SCPE_OK; + } + } +return SCPE_ARG; +} + +t_stat show_chan (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +fprintf (st, "channel=%s", chname[dibp->chan]); +return SCPE_OK; +} + +/* Init device tables */ + +t_bool io_init (void) +{ +DEVICE *dptr; +DIB *dibp; +DSPT *tplp; +int32 ch; +uint32 i, j, dev, doff; + +/* Clear dispatch table, device map */ + +for (i = 0; i < NUM_CHAN; i++) { + for (j = 0; j < (DEV_MASK + 1); j++) { + dev_dsp[j][i] = NULL; + dev_map[j][i] = 0; + } + } + +/* Test each device for conflict; add to map; init tables */ + +for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru devices */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if ((dibp == NULL) || (dptr->flags & DEV_DIS)) continue; /* exist, enabled? */ + ch = dibp->chan; /* get channel */ + dev = dibp->dev; /* get device num */ + if (ch < 0) dev3_dsp[dev] = dibp->iop; /* special device */ + else { + if (dibp->tplt == NULL) return TRUE; /* must have template */ + for (tplp = dibp->tplt; tplp->num; tplp++) { /* loop thru templates */ + for (j = 0; j < tplp->num; j++) { /* repeat as needed */ + doff = dev + tplp->off + j; /* get offset dnum */ + if (dev_map[doff][ch]) { /* slot in use? */ + printf ("Device number conflict, chan = %s, devno = %02o\n", + chname[ch], doff); + if (sim_log) fprintf (sim_log, + "Device number conflict, chan = %s, dev = %02o\n", + chname[ch], doff); + return TRUE; + } + dev_map[doff][ch] = dibp->xfr; /* set xfr flag */ + dev_dsp[doff][ch] = dibp->iop; /* set dispatch */ + } /* end for j */ + } /* end for tplt */ + } /* end else */ + } /* end for i */ +return FALSE; +} + +/* Display channel state */ + +t_stat chan_show_reg (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if ((val < 0) || (val >= NUM_CHAN)) return SCPE_IERR; +fprintf (st, "UAR: %02o\n", chan_uar[val]); +fprintf (st, "WCR: %05o\n", chan_wcr[val]); +fprintf (st, "MAR: %06o\n", chan_mar[val]); +fprintf (st, "DCR: %02o\n", chan_dcr[val]); +fprintf (st, "WAR: %08o\n", chan_war[val]); +fprintf (st, "CPW: %o\n", chan_cpw[val]); +fprintf (st, "CNT: %o\n", chan_cnt[val]); +fprintf (st, "MODE: %03o\n", chan_mode[val]); +fprintf (st, "FLAG: %04o\n", chan_flag[val]); +return SCPE_OK; +} diff --git a/SDS/sds_lp.c b/SDS/sds_lp.c new file mode 100644 index 0000000..b8dd52e --- /dev/null +++ b/SDS/sds_lp.c @@ -0,0 +1,309 @@ +/* sds_lp.c: SDS 940 line printer simulator + + Copyright (c) 2001-2007, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + lpt line printer + + 19-Jan-07 RMS Added UNIT_TEXT flag + 25-Apr-03 RMS Revised for extended file support +*/ + +#include "sds_defs.h" + +#define LPT_V_LN 9 +#define LPT_M_LN 07 +#define LPT_GETLN(x) (((x) >> LPT_V_LN) & LPT_M_LN) +#define CHP(ch,val) ((val) & (1 << (ch))) /* CCL chan test */ +#define SET_XFR 1 /* set xfr */ +#define SET_EOR 2 /* print, set eor */ +#define SET_SPC 4 /* space */ + +extern char sds_to_ascii[64]; +extern uint32 xfr_req; +extern int32 stop_invins, stop_invdev, stop_inviop; +int32 lpt_spc = 0; /* space instr */ +int32 lpt_sta = 0; /* timeout state */ +int32 lpt_bptr = 0; /* line buf ptr */ +int32 lpt_err = 0; /* error */ +int32 lpt_ccl = 1, lpt_ccp = 0; /* cctl lnt, ptr */ +int32 lpt_ctime = 10; /* char time */ +int32 lpt_ptime = 1000; /* print time */ +int32 lpt_stime = 10000; /* space time */ +int32 lpt_stopioe = 1; /* stop on err */ +char lpt_buf[LPT_WIDTH + 1] = { 0 }; /* line buffer */ +uint8 lpt_cct[CCT_LNT] = { 0377 }; /* car ctl tape */ +DSPT lpt_tplt[] = { /* template */ + { 1, 0 }, + { 0, 0 } + }; + +DEVICE lpt_dev; +t_stat lpt_svc (UNIT *uptr); +t_stat lpt_reset (DEVICE *dptr); +t_stat lpt_attach (UNIT *uptr, char *cptr); +t_stat lpt_crctl (int32 ch); +t_stat lpt_status (UNIT *uptr); +t_stat lpt_bufout (UNIT *uptr); +void lpt_end_op (int32 fl); +t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat); + +/* LPT data structures + + lpt_dev LPT device descriptor + lpt_unit LPT unit descriptor + lpt_reg LPT register list +*/ + +DIB lpt_dib = { CHAN_W, DEV_LPT, XFR_LPT, lpt_tplt, &lpt }; + +UNIT lpt_unit = { + UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_TEXT, 0) + }; + +REG lpt_reg[] = { + { BRDATA (BUF, lpt_buf, 8, 8, LPT_WIDTH) }, + { DRDATA (BPTR, lpt_bptr, 8), PV_LEFT }, + { FLDATA (XFR, xfr_req, XFR_V_LPT) }, + { FLDATA (ERR, lpt_err, 0) }, + { ORDATA (STA, lpt_sta, 3) }, + { BRDATA (CCT, lpt_cct, 8, 8, CCT_LNT) }, + { DRDATA (CCTP, lpt_ccp, 8), PV_LEFT }, + { DRDATA (CCTL, lpt_ccl, 8), REG_RO + PV_LEFT }, + { ORDATA (SPCINST, lpt_spc, 24) }, + { DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (CTIME, lpt_ctime, 24), REG_NZ + PV_LEFT }, + { DRDATA (PTIME, lpt_ptime, 24), REG_NZ + PV_LEFT }, + { DRDATA (STIME, lpt_stime, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, lpt_stopioe, 0) }, + { NULL } + }; + +MTAB lpt_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, NULL }, + { 0 } + }; + +DEVICE lpt_dev = { + "LPT", &lpt_unit, lpt_reg, lpt_mod, + 1, 10, 31, 1, 8, 7, + NULL, NULL, &lpt_reset, + NULL, &lpt_attach, NULL, + &lpt_dib, DEV_DISABLE + }; + +/* Line printer routine + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result + + The line printer is an asynchronous output device, that is, it + can never set the channel rate error flag. +*/ + +t_stat lpt (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 i, t, new_ch; +char asc; + +switch (fnc) { /* case function */ + + case IO_CONN: /* connect */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != lpt_dib.chan) return SCPE_IERR; /* wrong chan? */ + for (i = 0; i < LPT_WIDTH; i++) lpt_buf[i] = 0; /* clr buffer */ + lpt_bptr = 0; /* clr buf ptr */ + lpt_err = 0; /* err = 0 */ + xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */ + lpt_sta = lpt_sta | SET_XFR; /* need xfr */ + sim_activate (&lpt_unit, lpt_ctime); /* start timer */ + break; + + case IO_EOM1: /* EOM mode 1 */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != lpt_dib.chan) CRETIOP; /* wrong chan? */ + if (inst & 0400) { /* space? */ + lpt_spc = inst; /* save instr */ + lpt_sta = lpt_sta | SET_SPC; /* need space */ + sim_cancel (&lpt_unit); /* cancel timer */ + sim_activate (&lpt_unit, lpt_stime); /* start timer */ + } + break; + + case IO_DISC: /* disconnect */ + lpt_end_op (0); /* normal term */ + return lpt_bufout (&lpt_unit); /* dump output */ + + case IO_WREOR: /* write eor */ + lpt_sta = (lpt_sta | SET_EOR) & ~SET_XFR; /* need eor */ + sim_activate (&lpt_unit, lpt_ptime); /* start timer */ + break; + + case IO_SKS: /* SKS */ + new_ch = I_GETSKCH (inst); /* sks chan */ + if (new_ch != lpt_dib.chan) return SCPE_IERR; /* wrong chan? */ + t = I_GETSKCND (inst); /* sks cond */ + if (((t == 020) && (!CHP (7, lpt_cct[lpt_ccp]))) || /* 14062: !ch 7 */ + ((t == 010) && (lpt_unit.flags & UNIT_ATT)) || /* 12062: !online */ + (t == 004) && !lpt_err) *dat = 1; /* 11062: !err */ + break; + + case IO_WRITE: /* write */ + asc = sds_to_ascii[(*dat) & 077]; /* convert data */ + xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */ + if (lpt_bptr < LPT_WIDTH) lpt_buf[lpt_bptr++] = asc;/* store data */ + lpt_sta = lpt_sta | SET_XFR; /* need xfr */ + sim_activate (&lpt_unit, lpt_ctime); /* start ch timer */ + break; + + default: + CRETINS; + } + +return SCPE_OK; +} + +/* Unit service and write */ + +t_stat lpt_svc (UNIT *uptr) +{ +t_stat r = SCPE_OK; +static const char *lpt_stabl[] = { + "\r", "\n", "\n\n", "\n\n\n", + "\n\n\n\n", "\n\n\n\n\n", + "\n\n\n\n\n\n", "\n\n\n\n\n\n\n" + }; + +if (lpt_sta & SET_XFR) chan_set_ordy (lpt_dib.chan); /* need lpt xfr? */ +if (lpt_sta & SET_EOR) { /* printing? */ + chan_set_flag (lpt_dib.chan, CHF_EOR); /* set eor flg */ + r = lpt_bufout (uptr); /* output buf */ + } +if (lpt_sta & SET_SPC) { /* spacing? */ + if (uptr->flags & UNIT_ATT) { /* attached? */ + int32 ln = LPT_GETLN (lpt_spc); /* get lines, ch */ + if (lpt_spc & 0200) /* n lines? */ + fputs (lpt_stabl[ln], uptr->fileref); /* upspace */ + else lpt_crctl (ln); /* carriage ctl */ + } + r = lpt_status (uptr); /* update status */ + } +lpt_sta = 0; /* clear state */ +return r; +} + +/* Trim and output buffer */ + +t_stat lpt_bufout (UNIT *uptr) +{ +int32 i; + +if ((uptr->flags & UNIT_ATT) && lpt_bptr) { /* attached? */ + for (i = LPT_WIDTH - 1; (i >= 0) && (lpt_buf[i] == ' '); i--) + lpt_buf[i] = 0; /* trim line */ + fputs (lpt_buf, uptr->fileref); /* write line */ + lpt_bptr = 0; + } +return lpt_status (uptr); /* return status */ +} + +/* Status update after I/O */ + +t_stat lpt_status (UNIT *uptr) +{ +if (uptr->flags & UNIT_ATT) { /* attached? */ + uptr->pos = ftell (uptr->fileref); /* update position */ + if (ferror (uptr->fileref)) { /* I/O error? */ + lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */ + perror ("LPT I/O error"); /* print msg */ + clearerr (uptr->fileref); + return SCPE_IOERR; /* ret error */ + } + } +else { + lpt_end_op (CHF_EOR | CHF_ERR); /* set err, disc */ + CRETIOE (lpt_stopioe, SCPE_UNATT); /* ret error */ + } +return SCPE_OK; +} + +/* Terminate LPT operation */ + +void lpt_end_op (int32 fl) +{ +if (fl) chan_set_flag (lpt_dib.chan, fl); /* set flags */ +xfr_req = xfr_req & ~XFR_LPT; /* clear xfr */ +sim_cancel (&lpt_unit); /* stop */ +if (fl & CHF_ERR) { /* error? */ + chan_disc (lpt_dib.chan); /* disconnect */ + lpt_err = 1; /* set lpt err */ + } +return; +} + +/* Carriage control */ + +t_stat lpt_crctl (int32 ch) +{ +int32 i, j; + +if ((ch == 1) && CHP (ch, lpt_cct[0])) { /* top of form? */ + fputs ("\f\n", lpt_unit.fileref); /* ff + nl */ + lpt_ccp = 0; /* top of page */ + return SCPE_OK; + } +for (i = 1; i < lpt_ccl + 1; i++) { /* sweep thru cct */ + lpt_ccp = (lpt_ccp + 1) %lpt_ccl; /* adv pointer */ + if (CHP (ch, lpt_cct[lpt_ccp])) { /* chan punched? */ + for (j = 0; j < i; j++) fputc ('\n', lpt_unit.fileref); + return SCPE_OK; + } + } +return STOP_CCT; /* runaway channel */ +} + +/* Reset routine */ + +t_stat lpt_reset (DEVICE *dptr) +{ +chan_disc (lpt_dib.chan); /* disconnect */ +lpt_spc = 0; /* clr state */ +lpt_sta = 0; +xfr_req = xfr_req & ~XFR_LPT; /* clr xfr flag */ +sim_cancel (&lpt_unit); /* deactivate */ +return SCPE_OK; +} + +/* Attach routine */ + +t_stat lpt_attach (UNIT *uptr, char *cptr) +{ +lpt_ccp = 0; /* top of form */ +return attach_unit (uptr, cptr); +} diff --git a/SDS/sds_mt.c b/SDS/sds_mt.c new file mode 100644 index 0000000..d3e4347 --- /dev/null +++ b/SDS/sds_mt.c @@ -0,0 +1,480 @@ +/* sds_mt.c: SDS 940 magnetic tape simulator + + Copyright (c) 2001-2006, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mt 7 track magnetic tape + + 16-Feb-06 RMS Added tape capacity checking + 07-Dec-04 RMS Added read-only file support + 25-Apr-03 RMS Revised for extended file support + 28-Mar-03 RMS Added multiformat support + 28-Feb-03 RMS Revised for magtape library + + Magnetic tapes are represented as a series of variable 8b records + of the form: + + 32b record length in bytes - exact number + byte 0 + byte 1 + : + byte n-2 + byte n-1 + 32b record length in bytes - exact number + + If the byte count is odd, the record is padded with an extra byte + of junk. File marks are represented by a single record length of 0. + End of tape is two consecutive end of file marks. +*/ + +#include "sds_defs.h" +#include "sim_tape.h" + +#define MT_MAXFR (32768 * 4) +#define MT_NUMDR 8 /* number drives */ +#define MT_UNIT 07 +#define botf u3 /* bot tape flag */ +#define eotf u4 /* eot tape flag */ + +extern uint32 xfr_req; +extern int32 stop_invins, stop_invdev, stop_inviop; +int32 mt_inst = 0; /* saved instr */ +int32 mt_eof = 0; /* end of file */ +int32 mt_gap = 0; /* in gap */ +int32 mt_skip = 0; /* skip rec */ +int32 mt_bptr = 0; /* buf ptr */ +int32 mt_blnt = 0; /* buf length */ +int32 mt_ctime = 10; /* char time */ +int32 mt_gtime = 1000; /* gap time */ +int32 mt_stopioe = 1; /* stop on err */ +uint8 mtxb[MT_MAXFR]; /* record buffer */ +DSPT mt_tplt[] = { /* template */ + { MT_NUMDR, 0 }, + { MT_NUMDR, DEV_MTS }, + { MT_NUMDR, DEV_OUT }, + { MT_NUMDR, DEV_MTS+DEV_OUT }, + { 0, 0 } + }; + +DEVICE mt_dev; +t_stat mt_svc (UNIT *uptr); +t_stat mt_reset (DEVICE *dptr); +t_stat mt_boot (int32 unitno, DEVICE *dptr); +t_stat mt_attach (UNIT *uptr, char *cptr); +t_stat mt_detach (UNIT *uptr); +t_stat mt_readrec (UNIT *uptr); +t_mtrlnt mt_readbc (UNIT *uptr); +void mt_readend (UNIT *uptr); +t_stat mt_wrend (uint32 dev); +void mt_set_err (UNIT *uptr); +t_stat mt (uint32 fnc, uint32 inst, uint32 *dat); + +static const char sds_to_bcd[64] = { + 012, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 012, 013, 014, 015, 016, 017, + 060, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 072, 073, 074, 075, 076, 077, + 040, 041, 042, 043, 044, 045, 046, 047, + 050, 051, 052, 053, 054, 055, 056, 057, + 020, 021, 022, 023, 024, 025, 026, 027, + 030, 031, 032, 033, 034, 035, 036, 037 + }; + +static const char bcd_to_sds[64] = { + 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 000, 013, 014, 015, 016, 017, + 060, 061, 062, 063, 064, 065, 066, 067, + 070, 071, 072, 073, 074, 075, 076, 077, + 040, 041, 042, 043, 044, 045, 046, 047, + 050, 051, 052, 053, 054, 055, 056, 057, + 020, 021, 022, 023, 024, 025, 026, 027, + 030, 031, 032, 033, 034, 035, 036, 037 + }; + +/* MT data structures + + mt_dev MT device descriptor + mt_unit MT unit descriptor + mt_reg MT register list +*/ + +DIB mt_dib = { CHAN_W, DEV_MT, XFR_MT0, mt_tplt, &mt }; + +UNIT mt_unit[] = { + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) }, + { UDATA (&mt_svc, UNIT_ATTABLE + UNIT_ROABLE + UNIT_DISABLE, 0) } + }; + +REG mt_reg[] = { + { BRDATA (BUF, mtxb, 8, 8, MT_MAXFR) }, + { DRDATA (BPTR, mt_bptr, 18), PV_LEFT }, + { DRDATA (BLNT, mt_blnt, 18), PV_LEFT }, + { FLDATA (XFR, xfr_req, XFR_V_MT0) }, + { ORDATA (INST, mt_inst, 24) }, + { FLDATA (EOF, mt_eof, 0) }, + { FLDATA (GAP, mt_gap, 0) }, + { FLDATA (SKIP, mt_skip, 0) }, + { DRDATA (CTIME, mt_ctime, 24), REG_NZ + PV_LEFT }, + { DRDATA (GTIME, mt_gtime, 24), REG_NZ + PV_LEFT }, + { URDATA (POS, mt_unit[0].pos, 10, T_ADDR_W, 0, + MT_NUMDR, PV_LEFT | REG_RO) }, + { URDATA (BOT, mt_unit[0].botf, 10, 1, 0, MT_NUMDR, REG_RO) }, + { URDATA (EOT, mt_unit[0].eotf, 10, 1, 0, MT_NUMDR, REG_RO) }, + { FLDATA (STOP_IOE, mt_stopioe, 0) }, + { NULL } + }; + +MTAB mt_mod[] = { + { MTUF_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { MTUF_WLK, MTUF_WLK, "write locked", "LOCKED", NULL }, + { MTAB_XTD|MTAB_VUN, 0, "FORMAT", "FORMAT", + &sim_tape_set_fmt, &sim_tape_show_fmt, NULL }, + { MTAB_XTD|MTAB_VUN, 0, "CAPACITY", "CAPACITY", + &sim_tape_set_capac, &sim_tape_show_capac, NULL }, + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, NULL }, + { 0 } + }; + +DEVICE mt_dev = { + "MT", mt_unit, mt_reg, mt_mod, + MT_NUMDR, 10, 31, 1, 8, 8, + NULL, NULL, &mt_reset, + &mt_boot, &mt_attach, NULL, + &mt_dib, DEV_DISABLE + }; + +/* Mag tape routine + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result +*/ + +t_stat mt (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 u = inst & MT_UNIT; /* get unit */ +UNIT *uptr = mt_dev.units + u; /* get unit ptr */ +int32 t, new_ch; +uint8 chr; +t_stat r; + +switch (fnc) { /* case function */ + + case IO_CONN: /* connect */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != mt_dib.chan) return SCPE_IERR; /* wrong chan? */ + if (mt_gap) { /* in gap? */ + mt_gap = 0; /* clr gap flg */ + sim_cancel (uptr); /* cancel timer */ + } + else if (sim_is_active (uptr)) CRETIOP; /* busy? */ + uptr->eotf = 0; /* clr eot flag */ + mt_eof = 0; /* clr eof flag */ + mt_skip = 0; /* clr skp flag */ + mt_bptr = mt_blnt = 0; /* init buffer */ + if ((inst & DEV_MTS)? (CHC_GETCPW (inst) < 2): /* scn & cpw<3? */ + (inst & CHC_REV)) return STOP_INVIOP; /* rw & rev? */ + mt_inst = inst; /* save inst */ + if ((inst & DEV_MTS) && !(inst && DEV_OUT)) /* scanning? */ + chan_set_flag (mt_dib.chan, CHF_SCAN); /* set chan flg */ + xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ + sim_activate (uptr, mt_gtime); /* start timer */ + break; + + case IO_EOM1: /* EOM mode 1 */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != mt_dib.chan) CRETIOP; /* wrong chan? */ + t = inst & 07670; /* get command */ + if ((t == 04010) && !sim_is_active (uptr)) { /* rewind? */ + sim_tape_rewind (uptr); /* rewind unit */ + uptr->eotf = 0; /* clr eot */ + uptr->botf = 1; /* set bot */ + } + else if ((t == 03610) && sim_is_active (uptr) &&/* skip rec? */ + ((mt_inst & DEV_OUT) == 0)) mt_skip = 1; /* set flag */ + else CRETINS; + break; + + case IO_DISC: /* disconnect */ + sim_cancel (uptr); /* no more xfr's */ + if (inst & DEV_OUT) { /* write? */ + if (r = mt_wrend (inst)) return r; /* end record */ + } + break; + + case IO_WREOR: /* write eor */ + chan_set_flag (mt_dib.chan, CHF_EOR); /* set eor flg */ + if (r = mt_wrend (inst)) return r; /* end record */ + mt_gap = 1; /* in gap */ + sim_activate (uptr, mt_gtime); /* start timer */ + break; + + case IO_SKS: /* SKS */ + new_ch = I_GETSKCH (inst); /* get chan # */ + if (new_ch != mt_dib.chan) return SCPE_IERR; /* wrong chan? */ + if ((inst & (DEV_OUT | DEV_MTS)) == 0) { /* not sks 1n? */ + t = I_GETSKCND (inst); /* get skip cond */ + switch (t) { /* case sks cond */ + case 001: /* sks 1021n */ + *dat = 1; /* not magpak */ + break; + case 002: /* sks 1041n */ + if (!(uptr->flags & UNIT_ATT) || /* not ready */ + sim_is_active (uptr)) *dat = 1; + break; + case 004: /* sks 1101n */ + if (!uptr->eotf) *dat = 1; /* not EOT */ + break; + case 010: /* sks 1201n */ + if (!uptr->botf) *dat = 1; /* not BOT */ + break; + case 013: /* sks 12610 */ + if (!mt_gap) *dat = 1; /* not in gap */ + break; + case 017: /* sks 13610 */ + if (!mt_eof) *dat = 1; /* not EOF */ + break; + case 020: /* sks 1401n */ + if (!sim_tape_wrp (uptr)) *dat = 1; /* not wrp */ + break; + case 031: /* sks 1621n */ + case 033: /* sks 1661n */ + *dat = 1; /* not 556bpi */ + case 035: /* sks 1721n */ + break; /* not 800bpi */ + } + } /* end if */ + break; + + case IO_READ: /* read */ + xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ + if (mt_blnt == 0) { /* first read? */ + r = mt_readrec (uptr); /* get data */ + if ((r != SCPE_OK) || (mt_blnt == 0)) return r; /* err, inv reclnt? */ + } + uptr->botf = 0; /* off BOT */ + if (mt_inst & CHC_REV) chr = mtxb[--mt_bptr] & 077; /* get next rev */ + else chr = mtxb[mt_bptr++] & 077; /* get next fwd */ + if (!(mt_inst & CHC_BIN)) chr = bcd_to_sds[chr];/* bcd? */ + *dat = chr & 077; /* give to chan */ + if ((mt_inst & CHC_REV)? (mt_bptr <= 0): /* rev or fwd, */ + (mt_bptr >= mt_blnt)) mt_readend (uptr); /* recd done? */ + break; + + case IO_WRITE: /* write */ + uptr->botf = 0; /* off BOT */ + chr = (*dat) & 077; + xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ + if (!(mt_inst & CHC_BIN)) chr = sds_to_bcd[chr];/* bcd? */ + if (mt_bptr < MT_MAXFR) mtxb[mt_bptr++] = chr; /* insert in buf */ + break; + + default: + CRETINS; + } + +return SCPE_OK; +} + +/* Unit service */ + +t_stat mt_svc (UNIT *uptr) +{ +if (mt_gap) { /* gap timeout */ + mt_gap = 0; /* clr gap flg */ + chan_disc (mt_dib.chan); /* disc chan */ + } +else if (mt_skip) mt_readend (uptr); /* skip record */ +else { /* normal xfr */ + xfr_req = xfr_req | XFR_MT0; /* set xfr req */ + sim_activate (uptr, mt_ctime); /* reactivate */ + } +return SCPE_OK; +} + +/* Read start (get new record) */ + +t_stat mt_readrec (UNIT *uptr) +{ +t_mtrlnt tbc; +t_stat st; + +if ((uptr->flags & UNIT_ATT) == 0) { /* attached? */ + mt_set_err (uptr); /* no, err, disc */ + return SCPE_UNATT; + } +if (mt_inst & CHC_REV) /* reverse? */ + st = sim_tape_rdrecr (uptr, mtxb, &tbc, MT_MAXFR); /* read rec rev */ +else { /* no, fwd */ + t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */ + st = sim_tape_rdrecf (uptr, mtxb, &tbc, MT_MAXFR); + if (!passed_eot && sim_tape_eot (uptr)) /* just passed eot? */ + uptr->eotf = 1; + } +if (st == MTSE_TMK) { /* tape mark? */ + mt_eof = 1; /* set eof flag */ + mtxb[0] = mtxb[1] = 017; /* EOR char */ + mt_blnt = 2; /* store 2 */ + return SCPE_OK; + } +if (st != MTSE_OK) { /* other error? */ + mt_set_err (uptr); /* err, disc */ + if (st == MTSE_IOERR) return SCPE_IOERR; /* IO error? */ + if (st == MTSE_INVRL) return SCPE_MTRLNT; /* inv rec lnt? */ + if (st == MTSE_EOM) uptr->eotf = 1; /* eom? set eot */ + return SCPE_OK; + } +mt_blnt = tbc; /* set buf lnt */ +return SCPE_OK; +} + +/* Read done (eof, end of record) */ + +void mt_readend (UNIT *uptr) +{ +sim_cancel (uptr); /* stop timer */ +mt_skip = 0; /* clr skp flg */ +chan_set_flag (mt_dib.chan, CHF_EOR); /* end record */ +if (mt_eof) chan_disc (mt_dib.chan); /* EOF? */ +else { + mt_gap = 1; /* no, in gap */ + sim_activate (uptr, mt_gtime); /* start timer */ + } +return; +} + +/* Write complete (end of record or disconnect) */ + +t_stat mt_wrend (uint32 dev) +{ +UNIT *uptr = mt_dev.units + (dev & MT_UNIT); +t_mtrlnt tbc; +t_stat st; + +sim_cancel (uptr); /* no more xfr's */ +if (mt_bptr == 0) return SCPE_OK; /* buf empty? */ +if (!(uptr->flags & UNIT_ATT)) { /* attached? */ + mt_set_err (uptr); /* no, err, disc */ + return SCPE_UNATT; + } +if (sim_tape_wrp (uptr)) { /* write lock? */ + mt_set_err (uptr); /* yes, err, disc */ + return SCPE_OK; + } +if (dev & DEV_MTS) { /* erase? */ + if (mt_inst & CHC_REV) /* reverse? */ + sim_tape_sprecr (uptr, &tbc); /* backspace */ + st = sim_tape_wreom (uptr); /* write eom */ + } +else { + t_bool passed_eot = sim_tape_eot (uptr); /* passed EOT? */ + if ((mt_bptr == 1) && (mtxb[0] == 017) && /* wr eof? */ + ((mt_inst & 01670) == 00050)) + st = sim_tape_wrtmk (uptr); /* write tape mark */ + else st = sim_tape_wrrecf (uptr, mtxb, mt_bptr); /* write record */ + if (!passed_eot && sim_tape_eot (uptr)) /* just passed EOT? */ + uptr->eotf = 1; + } +mt_bptr = 0; +if (st != MTSE_OK) mt_set_err (uptr); /* error? */ +if (st == MTSE_IOERR) return SCPE_IOERR; +return SCPE_OK; +} + +/* Fatal error */ + +void mt_set_err (UNIT *uptr) +{ +chan_set_flag (mt_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */ +chan_disc (mt_dib.chan); /* disconnect */ +xfr_req = xfr_req & ~XFR_MT0; /* clear xfr */ +sim_cancel (uptr); /* stop */ +mt_bptr = 0; /* buf empty */ +return; +} +/* Reset routine */ + +t_stat mt_reset (DEVICE *dptr) +{ +int32 i; + +chan_disc (mt_dib.chan); /* disconnect */ +mt_eof = 0; /* clear state */ +mt_gap = 0; +mt_skip = 0; +mt_inst = 0; +mt_bptr = mt_blnt = 0; +xfr_req = xfr_req & ~XFR_MT0; /* clr xfr flag */ +for (i = 0; i < MT_NUMDR; i++) { /* deactivate */ + sim_cancel (&mt_unit[i]); + sim_tape_reset (&mt_unit[i]); + mt_unit[i].eotf = 0; + } +return SCPE_OK; +} + +/* Attach and detach routines */ + +t_stat mt_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +r = sim_tape_attach (uptr, cptr); +if (r != SCPE_OK) return r; +uptr->botf = 1; +uptr->eotf = 0; +return SCPE_OK; +} + +t_stat mt_detach (UNIT *uptr) +{ +uptr->botf = uptr->eotf = 0; +return sim_tape_detach (uptr); +} + +/* Boot routine - simulate FILL console command */ + +t_stat mt_boot (int32 unitno, DEVICE *dptr) +{ +extern uint32 P, M[]; + +if (unitno) return SCPE_ARG; /* only unit 0 */ +M[0] = 077777771; /* -7B */ +M[1] = 007100000; /* LDX 0 */ +M[2] = 000203610; /* EOM 3610B */ +M[3] = 003200002; /* WIM 2 */ +M[4] = 000100002; /* BRU 2 */ +P = 1; /* start at 1 */ +return SCPE_OK; +} diff --git a/SDS/sds_mux.c b/SDS/sds_mux.c new file mode 100644 index 0000000..ed14360 --- /dev/null +++ b/SDS/sds_mux.c @@ -0,0 +1,549 @@ +/* sds_mux.c: SDS 940 terminal multiplexor simulator + + Copyright (c) 2001-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mux terminal multiplexor + + 29-Dec-06 RMS Revised to use console conversion routines + 29-Jun-05 RMS Added SET MUXLn DISCONNECT + 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS + 05-Jan-04 RMS Revised for tmxr library changes + 09-May-03 RMS Added network device flag + + This module implements up to 32 individual serial interfaces, representing + either the project Genie terminal multiplexor or the SDS 940 CTE option. +*/ + +#include "sds_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define PROJ_GENIE (cpu_unit.flags & UNIT_GENIE) +#define MUX_NUMLIN mux_desc.lines + +#define MUX_LINES 32 /* lines */ +#define MUX_FLAGS 4 /* intr per line */ +#define MUX_FLAGMASK (MUX_FLAGS - 1) +#define MUX_SCANMAX (MUX_LINES * MUX_FLAGS) /* flags to scan */ +#define MUX_SCANMASK (MUX_SCANMAX - 1) +#define MUX_INIT_POLL 8000 +#define MUXL_WAIT 500 +#define MUX_SETFLG(l,x) mux_flags[((l) * MUX_FLAGS) + (x)] = 1 +#define MUX_SETINT(x) int_req = int_req | (INT_MUXR >> (x)) +#define MUX_CLRINT(x) int_req = int_req & ~(INT_MUXR >> (x)) + +/* PIN/POT */ + +#define P_V_CHAR 16 /* char */ +#define P_M_CHAR 0377 +#define P_CHAR(x) (((x) >> P_V_CHAR) & P_M_CHAR) +#define PIN_OVR 000100000 /* overrun */ +#define POT_NOX 000100000 /* no xmit */ +#define POT_XMI 000040000 /* xmit int */ +#define POT_GLNE 000020000 /* Genie: enable */ +#define POT_SCDT 000020000 /* 940: clr DTR */ +#define P_V_CHAN 0 /* channel */ +#define P_M_CHAN (MUX_LINES - 1) +#define P_CHAN(x) (((x) >> P_V_CHAN) & P_M_CHAN) + +/* SKS 940 */ + +#define SKS_XBE 000001000 /* xmt buf empty */ +#define SKS_CRO 000000400 /* carrier on */ +#define SKS_DSR 000000200 /* data set ready */ +#define SKS_CHAN(x) P_CHAN(x) + +/* SKS Genie */ + +#define SKG_V_CHAN 7 +#define SKG_M_CHAN (MUX_LINES - 1) +#define SKG_CHAN(x) (((x) >> SKG_V_CHAN) & SKG_M_CHAN) + +/* Flags */ + +#define MUX_FRCV 0 /* receive */ +#define MUX_FXMT 1 /* transmit */ +#define MUX_FCRN 2 /* carrier on */ +#define MUX_FCRF 3 /* carrier off */ + +/* Line status */ + +#define MUX_SCHP 001 /* char pending */ +#define MUX_SOVR 002 /* overrun */ +#define MUX_SLNE 004 /* line enabled */ +#define MUX_SXIE 010 /* xmt int enab */ +#define MUX_SCRO 020 /* carrier on */ +#define MUX_SDSR 040 /* data set ready */ + +/* Data */ + +extern uint32 alert, int_req; +extern int32 stop_invins, stop_invdev, stop_inviop; +extern UNIT cpu_unit; + +uint8 mux_rbuf[MUX_LINES]; /* rcv buf */ +uint8 mux_xbuf[MUX_LINES]; /* xmt buf */ +uint8 mux_sta[MUX_LINES]; /* status */ +uint8 mux_flags[MUX_SCANMAX]; /* flags */ +uint32 mux_tps = 100; /* polls/second */ +uint32 mux_scan = 0; /* scanner */ +uint32 mux_slck = 0; /* scanner locked */ + +TMLN mux_ldsc[MUX_LINES] = { 0 }; /* line descriptors */ +TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc }; /* mux descriptor */ + +t_stat mux (uint32 fnc, uint32 inst, uint32 *dat); +t_stat muxi_svc (UNIT *uptr); +t_stat muxo_svc (UNIT *uptr); +t_stat mux_reset (DEVICE *dptr); +t_stat mux_attach (UNIT *uptr, char *cptr); +t_stat mux_detach (UNIT *uptr); +t_stat mux_summ (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat mux_show (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat mux_vlines (UNIT *uptr, int32 val, char *cptr, void *desc); +void mux_reset_ln (int32 ln); +void mux_scan_next (void); + +/* MUX data structures + + mux_dev MUX device descriptor + mux_unit MUX unit descriptor + mux_reg MUX register list + mux_mod MUX modifiers list +*/ + +DIB mux_dib = { -1, DEV3_GMUX, 0, NULL, &mux }; + +REG mux_nlreg = { DRDATA (NLINES, MUX_NUMLIN, 6), PV_LEFT }; + +UNIT mux_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), MUX_INIT_POLL }; + +REG mux_reg[] = { + { BRDATA (STA, mux_sta, 8, 6, MUX_LINES) }, + { BRDATA (RBUF, mux_rbuf, 8, 8, MUX_LINES) }, + { BRDATA (XBUF, mux_xbuf, 8, 8, MUX_LINES) }, + { BRDATA (INT, mux_flags, 8, 1, MUX_SCANMAX) }, + { ORDATA (SCAN, mux_scan, 7) }, + { FLDATA (SLCK, mux_slck, 0) }, + { DRDATA (TPS, mux_tps, 8), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB mux_mod[] = { + { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES", + &mux_vlines, NULL, &mux_nlreg }, + { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &mux_desc }, + { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &mux_summ }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, + NULL, &mux_show, NULL }, + { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, + NULL, &mux_show, NULL }, + { 0 } + }; + +DEVICE mux_dev = { + "MUX", &mux_unit, mux_reg, mux_mod, + 1, 10, 31, 1, 8, 8, + &tmxr_ex, &tmxr_dep, &mux_reset, + NULL, &mux_attach, &mux_detach, + &mux_dib, DEV_NET | DEV_DISABLE + }; + +/* MUXL data structures + + muxl_dev MUXL device descriptor + muxl_unit MUXL unit descriptor + muxl_reg MUXL register list + muxl_mod MUXL modifiers list +*/ + +UNIT muxl_unit[] = { + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT }, + { UDATA (&muxo_svc, TT_MODE_UC, 0), MUXL_WAIT } + }; + +MTAB muxl_mod[] = { + { TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", + &tmxr_dscln, NULL, &mux_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", + &tmxr_set_log, &tmxr_show_log, &mux_desc }, + { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", + &tmxr_set_nolog, NULL, &mux_desc }, + { 0 } + }; + +REG muxl_reg[] = { + { URDATA (TIME, muxl_unit[0].wait, 10, 24, 0, + MUX_LINES, REG_NZ + PV_LEFT) }, + { NULL } + }; + +DEVICE muxl_dev = { + "MUXL", muxl_unit, muxl_reg, muxl_mod, + MUX_LINES, 10, 31, 1, 8, 8, + NULL, NULL, &mux_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* MUX: IO routine */ + +/* Mux routine - EOM 30001 or EOM 77777,2 */ + +t_stat mux (uint32 fnc, uint32 inst, uint32 *dat) +{ +uint32 ln; + +switch (fnc) { + + case IO_CONN: /* connect */ + if ((PROJ_GENIE && (inst == 000230001)) || /* set alert */ + (!PROJ_GENIE && (inst == 020277777))) alert = POT_MUX; + else CRETINS; + break; + + case IO_SKS: /* skip */ + if (PROJ_GENIE && ((inst & 077770077) == 004030001)) { + ln = SKG_CHAN (inst); /* get line */ + if (!sim_is_active (&muxl_unit[ln])) *dat = 1; + } + else if (!PROJ_GENIE && ((inst & 077776000) == 024076000)) { + ln = SKS_CHAN (inst); /* get line */ + if (inst & (SKS_XBE|SKS_CRO|SKS_DSR)) *dat = 1; + if (((inst & SKS_XBE) && sim_is_active (&muxl_unit[ln])) || + ((inst & SKS_CRO) && !(mux_sta[ln] & MUX_SCRO)) || + ((inst & SKS_DSR) && !(mux_sta[ln] & MUX_SDSR))) + *dat = 0; /* no skip if fail */ + } + else CRETINS; + + default: + return SCPE_IERR; + } /* end case */ + +return SCPE_OK; +} + +/* PIN routine */ + +t_stat pin_mux (uint32 num, uint32 *dat) +{ +uint32 ln = mux_scan >> 2; +uint32 flag = mux_scan & MUX_FLAGMASK; + +mux_scan = mux_scan & MUX_SCANMASK; /* mask scan */ +mux_flags[mux_scan] = 0; /* clear flag */ +if (flag == MUX_FRCV) { /* rcv event? */ + *dat = ln | ((uint32) mux_rbuf[ln] << P_V_CHAR) | /* line + char + */ + ((mux_sta[ln] & MUX_SOVR)? PIN_OVR: 0); /* overrun */ + mux_sta[ln] = mux_sta[ln] & ~(MUX_SCHP | MUX_SOVR); + } +else *dat = ln; /* just line */ +mux_slck = 0; /* unlock scanner */ +mux_scan_next (); /* kick scanner */ +return SCPE_OK; +} + +t_stat pot_mux (uint32 num, uint32 *dat) +{ +uint32 ln = P_CHAN (*dat); +uint32 chr = P_CHAR (*dat); + +if (PROJ_GENIE && ((*dat & POT_GLNE) == 0)) { /* Genie disable? */ + mux_sta[ln] = mux_sta[ln] & ~MUX_SLNE; /* clear status */ + mux_ldsc[ln].rcve = 0; + } +else if (!PROJ_GENIE && (*dat & POT_SCDT)) { /* SDS disable? */ + if (mux_ldsc[ln].conn) { /* connected? */ + tmxr_linemsg (&mux_ldsc[ln], "\r\nLine hangup\r\n"); + tmxr_reset_ln (&mux_ldsc[ln]); /* reset line */ + mux_reset_ln (ln); /* reset state */ + MUX_SETFLG (ln, MUX_FCRF); /* set carrier off */ + mux_scan_next (); /* kick scanner */ + } + mux_sta[ln] = mux_sta[ln] & ~MUX_SLNE; /* clear status */ + mux_ldsc[ln].rcve = 0; + } +else { /* enabled */ + if ((*dat & POT_NOX) == 0) { /* output char? */ + mux_xbuf[ln] = chr; /* store char */ + sim_activate (&muxl_unit[ln], muxl_unit[ln].wait); + } + if (*dat & POT_XMI) mux_sta[ln] = mux_sta[ln] | MUX_SXIE; + else mux_sta[ln] = mux_sta[ln] & ~MUX_SXIE; + mux_sta[ln] = mux_sta[ln] | MUX_SLNE; /* line is enabled */ + mux_ldsc[ln].rcve = 1; + } +return SCPE_OK; +} + +/* Unit service - receive side + + Poll all active lines for input + Poll for new connections +*/ + +t_stat muxi_svc (UNIT *uptr) +{ +int32 ln, c, t; + +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ +t = sim_rtcn_calb (mux_tps, TMR_MUX); /* calibrate */ +sim_activate (uptr, t); /* continue poll */ +ln = tmxr_poll_conn (&mux_desc); /* look for connect */ +if (ln >= 0) { /* got one? */ + if (!PROJ_GENIE && (mux_sta[ln] & MUX_SLNE)) { /* modem & DTR? */ + mux_sta[ln] = mux_sta[ln] | (MUX_SCRO|MUX_SDSR);/* carrier on */ + MUX_SETFLG (ln, MUX_FCRN); /* set carr on flag */ + mux_scan_next (); /* kick scanner */ + } + mux_ldsc[ln].rcve = 1; /* set rcv enable */ + } +tmxr_poll_rx (&mux_desc); /* poll for input */ +for (ln = 0; ln < MUX_NUMLIN; ln++) { /* loop thru lines */ + if (mux_ldsc[ln].conn) { /* connected? */ + if (c = tmxr_getc_ln (&mux_ldsc[ln])) { /* get char */ + if (mux_sta[ln] & MUX_SCHP) /* already got one? */ + mux_sta[ln] = mux_sta[ln] | MUX_SOVR; /* overrun */ + else mux_sta[ln] = mux_sta[ln] | MUX_SCHP; /* char pending */ + if (c & SCPE_BREAK) c = 0; /* break? */ + else c = sim_tt_inpcvt (c, TT_GET_MODE (muxl_unit[ln].flags)); + mux_rbuf[ln] = c; /* save char */ + MUX_SETFLG (ln, MUX_FRCV); /* set rcv flag */ + mux_scan_next (); /* kick scanner */ + } + } + else mux_sta[ln] = 0; /* disconnected */ + } /* end for */ +return SCPE_OK; +} + +/* Unit service - transmit side */ + +t_stat muxo_svc (UNIT *uptr) +{ +int32 c; +uint32 ln = uptr - muxl_unit; /* line # */ + +if (mux_ldsc[ln].conn) { /* connected? */ + if (mux_ldsc[ln].xmte) { /* xmt enabled? */ + c = sim_tt_outcvt (mux_xbuf[ln], TT_GET_MODE (muxl_unit[ln].flags)); + if (c >= 0) tmxr_putc_ln (&mux_ldsc[ln], c); /* output char */ + tmxr_poll_tx (&mux_desc); /* poll xmt */ + } + else { /* buf full */ + tmxr_poll_tx (&mux_desc); /* poll xmt */ + sim_activate (uptr, muxl_unit[ln].wait); /* wait */ + return SCPE_OK; + } + } +if (mux_sta[ln] & MUX_SXIE) { + MUX_SETFLG (ln, MUX_FXMT); /* set flag */ + mux_scan_next (); /* kick scanner */ + } +return SCPE_OK; +} + +/* Kick scanner */ + +void mux_scan_next (void) +{ +int32 i; + +if (mux_slck) return; /* locked? */ +for (i = 0; i < MUX_SCANMAX; i++) { /* scan flags */ + mux_scan = (mux_scan + 1) & MUX_SCANMASK; /* next flag */ + if (mux_flags[mux_scan]) { /* flag set? */ + mux_slck = 1; /* lock scanner */ + MUX_SETINT (mux_scan & MUX_FLAGMASK); /* request int */ + return; + } + } +return; +} + +/* Reset routine */ + +t_stat mux_reset (DEVICE *dptr) +{ +int32 i, t; + +if (mux_dev.flags & DEV_DIS) /* master disabled? */ + muxl_dev.flags = muxl_dev.flags | DEV_DIS; /* disable lines */ +else muxl_dev.flags = muxl_dev.flags & ~DEV_DIS; +if (mux_unit.flags & UNIT_ATT) { /* master att? */ + if (!sim_is_active (&mux_unit)) { + t = sim_rtcn_init (mux_unit.wait, TMR_MUX); + sim_activate (&mux_unit, t); /* activate */ + } + } +else sim_cancel (&mux_unit); /* else stop */ +for (i = 0; i < MUX_LINES; i++) mux_reset_ln (i); +for (i = 0; i < MUX_FLAGS; i++) MUX_CLRINT (i); /* clear all ints */ +return SCPE_OK; +} + +/* Attach master unit */ + +t_stat mux_attach (UNIT *uptr, char *cptr) +{ +t_stat r; +int32 t; + +r = tmxr_attach (&mux_desc, uptr, cptr); /* attach */ +if (r != SCPE_OK) return r; /* error */ +t = sim_rtcn_init (mux_unit.wait, TMR_MUX); +sim_activate (uptr, t); /* start poll */ +return SCPE_OK; +} + +/* Detach master unit */ + +t_stat mux_detach (UNIT *uptr) +{ +int32 i; +t_stat r; + +r = tmxr_detach (&mux_desc, uptr); /* detach */ +for (i = 0; i < MUX_LINES; i++) mux_ldsc[i].rcve = 0; /* disable rcv */ +sim_cancel (uptr); /* stop poll */ +return r; +} + +/* Show summary processor */ + +t_stat mux_summ (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < MUX_LINES; i++) t = t + (mux_ldsc[i].conn != 0); +if (t == 1) fprintf (st, "1 connection"); +else fprintf (st, "%d connections", t); +return SCPE_OK; +} + +/* SHOW CONN/STAT processor */ + +t_stat mux_show (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, t; + +for (i = t = 0; i < MUX_LINES; i++) t = t + (mux_ldsc[i].conn != 0); +if (t) { + for (i = 0; i < MUX_LINES; i++) { + if (mux_ldsc[i].conn) { + if (val) tmxr_fconns (st, &mux_ldsc[i], i); + else tmxr_fstats (st, &mux_ldsc[i], i); + } + } + } +else fprintf (st, "all disconnected\n"); +return SCPE_OK; +} + +/* Change number of lines */ + +t_stat mux_vlines (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 newln, i, t; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +newln = get_uint (cptr, 10, MUX_LINES, &r); +if ((r != SCPE_OK) || (newln == MUX_NUMLIN)) return r; +if (newln == 0) return SCPE_ARG; +if (newln < MUX_NUMLIN) { + for (i = newln, t = 0; i < MUX_NUMLIN; i++) t = t | mux_ldsc[i].conn; + if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) + return SCPE_OK; + for (i = newln; i < MUX_NUMLIN; i++) { + if (mux_ldsc[i].conn) { + tmxr_linemsg (&mux_ldsc[i], "\r\nOperator disconnected line\r\n"); + tmxr_reset_ln (&mux_ldsc[i]); /* reset line */ + } + muxl_unit[i].flags = muxl_unit[i].flags | UNIT_DIS; + mux_reset_ln (i); + } + } +else { + for (i = MUX_NUMLIN; i < newln; i++) { + muxl_unit[i].flags = muxl_unit[i].flags & ~UNIT_DIS; + mux_reset_ln (i); + } + } +MUX_NUMLIN = newln; +return SCPE_OK; +} + +/* Reset an individual line */ + +void mux_reset_ln (int32 ln) +{ +int32 flg = ln * MUX_FLAGS; + +if (mux_ldsc[ln].conn) mux_sta[ln] = MUX_SCRO | MUX_SDSR; +else mux_sta[ln] = 0; +sim_cancel (&muxl_unit[ln]); +mux_flags[flg + MUX_FRCV] = 0; +mux_flags[flg + MUX_FXMT] = 0; +mux_flags[flg + MUX_FCRN] = 0; +mux_flags[flg + MUX_FCRF] = 0; +return; +} diff --git a/SDS/sds_rad.c b/SDS/sds_rad.c new file mode 100644 index 0000000..2add471 --- /dev/null +++ b/SDS/sds_rad.c @@ -0,0 +1,307 @@ +/* sds_rad.c: SDS 940 fixed head disk simulator + + Copyright (c) 2001-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + rad fixed head disk + + The fixed head disk is a head-per-track disk, with up to four disks. Each + disk is divided into two logical units. Reads and writes cannot cross logical + unit boundaries. The fixed head disk transfers 12b characters, rather than 6b + characters. To minimize overhead, the disk is buffered in memory. +*/ + +#include "sds_defs.h" +#include + +/* Constants */ + +#define RAD_NUMWD 64 /* words/sector */ +#define RAD_NUMSC 64 /* sectors/track */ +#define RAD_NUMTR 64 /* tracks/log unit */ +#define RAD_NUMLU 8 /* log units/ctrl */ +#define RAD_SCSIZE (RAD_NUMLU*RAD_NUMTR*RAD_NUMSC) /* sectors/disk */ +#define RAD_AMASK (RAD_SCSIZE - 1) /* sec addr mask */ +#define RAD_SIZE (RAD_SCSIZE * RAD_NUMWD) /* words/disk */ +#define RAD_GETLUN(x) ((x) / (RAD_NUMTR * RAD_NUMSC)) +#define RAD_SCMASK (RAD_NUMSC - 1) /* sector mask */ +#define RAD_TRSCMASK ((RAD_NUMSC * RAD_NUMTR) - 1) /* track/sec mask */ + +#define GET_SECTOR(x) ((int) fmod (sim_gtime() / ((double) (x)), \ + ((double) RAD_NUMSC))) + +extern uint32 xfr_req; +extern uint32 alert; +extern int32 stop_invins, stop_invdev, stop_inviop; +int32 rad_err = 0; /* error */ +int32 rad_nobi = 0; /* !incr x track */ +int32 rad_da = 0; /* disk address */ +int32 rad_sba = 0; /* sec byte addr */ +int32 rad_wrp = 0; /* write prot */ +int32 rad_time = 2; /* time per 12b */ +int32 rad_stopioe = 1; /* stop on error */ +DSPT rad_tplt[] = { /* template */ + { 1, 0 }, + { 1, DEV_OUT }, + { 0, 0 } + }; + +DEVICE rad_dev; +t_stat rad_svc (UNIT *uptr); +t_stat rad_reset (DEVICE *dptr); +t_stat rad_fill (int32 sba); +void rad_end_op (int32 fl); +int32 rad_adjda (int32 sba, int32 inc); +t_stat rad (uint32 fnc, uint32 inst, uint32 *dat); + +/* RAD data structures + + rad_dev device descriptor + rad_unit unit descriptor + rad_reg register list +*/ + +DIB rad_dib = { CHAN_E, DEV_RAD, XFR_RAD, rad_tplt, &rad }; + +UNIT rad_unit = { + UDATA (&rad_svc, UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, + RAD_SIZE) + }; + +REG rad_reg[] = { + { ORDATA (DA, rad_da, 15) }, + { GRDATA (SA, rad_sba, 8, 6, 1) }, + { FLDATA (BP, rad_sba, 0) }, + { FLDATA (XFR, xfr_req, XFR_V_RAD) }, + { FLDATA (NOBD, rad_nobi, 0) }, + { FLDATA (ERR, rad_err, 0) }, + { ORDATA (PROT, rad_wrp, 8) }, + { DRDATA (TIME, rad_time, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, rad_stopioe, 0) }, + { NULL } + }; + +MTAB rad_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, NULL }, + { 0 } + }; + +DEVICE rad_dev = { + "RAD", &rad_unit, rad_reg, rad_mod, + 1, 8, 21, 1, 8, 24, + NULL, NULL, &rad_reset, + NULL, NULL, NULL, + &rad_dib, DEV_DISABLE + }; + +/* Fixed head disk routine + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result +*/ + +t_stat rad (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 t, lun, new_ch; +uint32 p; +uint32 *fbuf = rad_unit.filebuf; + +switch (fnc) { /* case function */ + + case IO_CONN: /* connect */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */ + if (CHC_GETCPW (inst) > 1) return STOP_INVIOP; /* 1-2 char/word? */ + if (sim_is_active (&rad_unit) || (alert == POT_RADA)) /* protocol viol? */ + return STOP_INVIOP; + rad_err = 0; /* clr error */ + rad_sba = 0; /* clr sec bptr */ + chan_set_flag (rad_dib.chan, CHF_12B); /* 12B mode */ + t = (rad_da & RAD_SCMASK) - GET_SECTOR (rad_time * RAD_NUMWD); + if (t <= 0) t = t + RAD_NUMSC; /* seek */ + sim_activate (&rad_unit, t * rad_time * (RAD_NUMWD / 2)); + xfr_req = xfr_req & ~XFR_RAD; /* clr xfr flg */ + break; + + case IO_EOM1: /* EOM mode 1 */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */ + if ((inst & 00600) == 00200) alert = POT_RADS; /* alert for sec */ + else if ((inst & 06600) == 0) { /* alert for addr */ + if (sim_is_active (&rad_unit)) rad_err = 1; /* busy? */ + else { + rad_nobi = (inst & 01000)? 1: 0; /* save inc type */ + alert = POT_RADA; /* set alert */ + } + } + break; + + case IO_DISC: /* disconnect */ + rad_end_op (0); /* normal term */ + if (inst & DEV_OUT) return rad_fill (rad_sba); /* fill write */ + break; + + case IO_WREOR: /* write eor */ + rad_end_op (CHF_EOR); /* eor term */ + return rad_fill (rad_sba); /* fill write */ + + case IO_SKS: /* SKS */ + new_ch = I_GETSKCH (inst); /* sks chan */ + if (new_ch != rad_dib.chan) return SCPE_IERR; /* wrong chan? */ + t = I_GETSKCND (inst); /* sks cond */ + lun = RAD_GETLUN (rad_da); + if (((t == 000) && !sim_is_active (&rad_unit)) || /* 10026: ready */ + ((t == 004) && !rad_err) || /* 11026: !err */ + ((t == 014) && !(rad_wrp & (1 << lun)))) /* 13026: !wrprot */ + *dat = 1; + break; + + case IO_READ: /* read */ + p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); /* buf wd addr */ + xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */ + if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */ + rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ + CRETIOE (rad_stopioe, SCPE_UNATT); + } + if (p >= rad_unit.capac) { /* end of disk? */ + rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ + return SCPE_OK; + } + if (rad_sba & 1) *dat = fbuf[p] & 07777; /* odd byte? */ + else *dat = (fbuf[p] >> 12) & 07777; /* even */ + rad_sba = rad_adjda (rad_sba, 1); /* next byte */ + break; + + case IO_WRITE: + p = (rad_da * RAD_NUMWD) + (rad_sba >> 1); + xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */ + if ((rad_unit.flags & UNIT_BUF) == 0) { /* not buffered? */ + rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ + CRETIOE (rad_stopioe, SCPE_UNATT); + } + if ((p >= rad_unit.capac) || /* end of disk? */ + (rad_wrp & (1 << RAD_GETLUN (rad_da)))) { /* write prot? */ + rad_end_op (CHF_ERR | CHF_EOR); /* set rad err */ + return SCPE_OK; + } + if (rad_sba & 1) fbuf[p] = fbuf[p] | (*dat & 07777); /* odd byte? */ + else fbuf[p] = (*dat & 07777) << 12; /* even */ + if (p >= rad_unit.hwmark) rad_unit.hwmark = p + 1; /* mark hiwater */ + rad_sba = rad_adjda (rad_sba, 1); /* next byte */ + break; + + default: + CRETINS; + } + +return SCPE_OK; +} + +/* PIN routine */ + +t_stat pin_rads (uint32 num, uint32 *dat) +{ +*dat = GET_SECTOR (rad_time * RAD_NUMWD); /* ret curr sec */ +return SCPE_OK; +} + +/* POT routine */ + +t_stat pot_rada (uint32 num, uint32 *dat) +{ +rad_da = (*dat) & RAD_AMASK; /* save dsk addr */ +return SCPE_OK; +} + +/* Unit service and read/write */ + +t_stat rad_svc (UNIT *uptr) +{ +xfr_req = xfr_req | XFR_RAD; /* set xfr req */ +sim_activate (&rad_unit, rad_time); /* activate */ +return SCPE_OK; +} + +/* Fill incomplete sector */ + +t_stat rad_fill (int32 sba) +{ +uint32 p = rad_da * RAD_NUMWD; +uint32 *fbuf = rad_unit.filebuf; +int32 wa = (sba + 1) >> 1; /* whole words */ + +if (sba && (p < rad_unit.capac)) { /* fill needed? */ + for ( ; wa < RAD_NUMWD; wa++) fbuf[p + wa] = 0; + if ((p + wa) >= rad_unit.hwmark) rad_unit.hwmark = p + wa + 1; + rad_adjda (sba, RAD_NUMWD - 1); /* inc da */ + } +return SCPE_OK; +} + +/* Adjust disk address */ + +int32 rad_adjda (int32 sba, int32 inc) +{ +sba = sba + inc; +if (rad_sba >= (RAD_NUMWD * 2)) { /* next sector? */ + if (rad_nobi) rad_da = (rad_da & ~RAD_SCMASK) + /* within band? */ + ((rad_da + 1) & RAD_SCMASK); + else rad_da = (rad_da & ~RAD_TRSCMASK) + /* cross band */ + ((rad_da + 1) & RAD_TRSCMASK); + sba = 0; /* start new sec */ + } +return sba; +} + +/* Terminate disk operation */ + +void rad_end_op (int32 fl) +{ +if (fl) chan_set_flag (rad_dib.chan, fl); /* set flags */ +xfr_req = xfr_req & ~XFR_RAD; /* clear xfr */ +sim_cancel (&rad_unit); /* stop */ +if (fl & CHF_ERR) { /* error? */ + chan_disc (rad_dib.chan); /* disconnect */ + rad_err = 1; /* set rad err */ + } +return; +} + +/* Reset routine */ + +t_stat rad_reset (DEVICE *dptr) +{ +chan_disc (rad_dib.chan); /* disconnect */ +rad_nobi = 0; /* clear state */ +rad_da = 0; +rad_sba = 0; +xfr_req = xfr_req & ~XFR_RAD; /* clr xfr req */ +sim_cancel (&rad_unit); /* deactivate */ +return SCPE_OK; +} diff --git a/SDS/sds_stddev.c b/SDS/sds_stddev.c new file mode 100644 index 0000000..29c6baf --- /dev/null +++ b/SDS/sds_stddev.c @@ -0,0 +1,602 @@ +/* sds_stddev.c: SDS 940 standard devices + + Copyright (c) 2001-2005, Robert M. Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + ptr paper tape reader + ptp paper tape punch + tti keyboard + tto teleprinter + + 29-Dec-03 RMS Added console backpressure support + 25-Apr-03 RMS Revised for extended file support +*/ + +#include "sds_defs.h" + +#define TT_CR 052 /* typewriter */ +#define TT_TB 072 +#define TT_BS 032 + +extern uint32 xfr_req; +extern int32 stop_invins, stop_invdev, stop_inviop; +int32 ptr_sor = 0; /* start of rec */ +int32 ptr_stopioe = 1; /* stop on err */ +int32 ptp_ldr = 0; /* no leader */ +int32 ptp_stopioe = 1; +DSPT std_tplt[] = { { 1, 0 }, { 0, 0 } }; /* template */ + +DEVICE ptr_dev, ptp_dev; +t_stat ptr (uint32 fnc, uint32 inst, uint32 *dat); +t_stat ptr_svc (UNIT *uptr); +t_stat ptr_reset (DEVICE *dptr); +t_stat ptr_boot (int32 unitno, DEVICE *dptr); +void ptr_set_err (void); +t_stat ptp (uint32 fnc, uint32 inst, uint32 *dat); +t_stat ptp_svc (UNIT *uptr); +t_stat ptp_reset (DEVICE *dptr); +t_stat ptp_out (int32 dat); +void ptp_set_err (void); +t_stat tti (uint32 fnc, uint32 inst, uint32 *dat); +t_stat tti_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto (uint32 fnc, uint32 inst, uint32 *dat); +t_stat tto_svc (UNIT *uptr); +t_stat tto_reset (DEVICE *dptr); + +extern const char ascii_to_sds[128]; +extern const char sds_to_ascii[64]; +extern const char odd_par[64]; + +/* PTR data structures + + ptr_dev PTR device descriptor + ptr_unit PTR unit + ptr_reg PTR register list +*/ + +DIB ptr_dib = { CHAN_W, DEV_PTR, XFR_PTR, std_tplt, &ptr }; + +UNIT ptr_unit = { + UDATA (&ptr_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_ROABLE, 0), + SERIAL_IN_WAIT + }; + +REG ptr_reg[] = { + { ORDATA (BUF, ptr_unit.buf, 7) }, + { FLDATA (XFR, xfr_req, XFR_V_PTR) }, + { FLDATA (SOR, ptr_sor, 0) }, + { DRDATA (POS, ptr_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptr_unit.wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, ptr_stopioe, 0) }, + { NULL } + }; + +MTAB ptr_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, NULL }, + { 0 } + }; + +DEVICE ptr_dev = { + "PTR", &ptr_unit, ptr_reg, ptr_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptr_reset, + &ptr_boot, NULL, NULL, + &ptr_dib, DEV_DISABLE + }; + +/* PTP data structures + + ptp_dev PTP device descriptor + ptp_unit PTP unit + ptp_reg PTP register list +*/ + +DIB ptp_dib = { CHAN_W, DEV_PTP, XFR_PTP, std_tplt, &ptp }; + +UNIT ptp_unit = { + UDATA (&ptp_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT + }; + +REG ptp_reg[] = { + { ORDATA (BUF, ptp_unit.buf, 7) }, + { FLDATA (XFR, xfr_req, XFR_V_PTP) }, + { FLDATA (LDR, ptp_ldr, 0) }, + { DRDATA (POS, ptp_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, ptp_unit.wait, 24), REG_NZ + PV_LEFT }, + { FLDATA (STOP_IOE, ptp_stopioe, 0) }, + { NULL } + }; + +MTAB ptp_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, NULL }, + { 0 } + }; + +DEVICE ptp_dev = { + "PTP", &ptp_unit, ptp_reg, ptp_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &ptp_reset, + NULL, NULL, NULL, + &ptp_dib, DEV_DISABLE + }; + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit + tti_reg TTI register list +*/ + +DIB tti_dib = { CHAN_W, DEV_TTI, XFR_TTI, std_tplt, &tti }; + +UNIT tti_unit = { UDATA (&tti_svc, 0, 0), KBD_POLL_WAIT }; + +REG tti_reg[] = { + { ORDATA (BUF, tti_unit.buf, 6) }, + { FLDATA (XFR, xfr_req, XFR_V_TTI) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, &tti_dib }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, 0 + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit + tto_reg TTO register list +*/ + +DIB tto_dib = { CHAN_W, DEV_TTO, XFR_TTO, std_tplt, &tto }; + +UNIT tto_unit = { UDATA (&tto_svc, 0, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { ORDATA (BUF, tto_unit.buf, 6) }, + { FLDATA (XFR, xfr_req, XFR_V_TTO) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "CHANNEL", "CHANNEL", + &set_chan, &show_chan, &tto_dib }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, 0 + }; + +/* Paper tape reader + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result + + The paper tape reader is a streaming input device. Once started, it + continues to read until disconnected. Leader before the current record + is ignored; leader after the current record sets channel EndOfRecord. +*/ + +t_stat ptr (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 new_ch; + +switch (fnc) { /* case function */ + + case IO_CONN: /* connect */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != ptr_dib.chan) return SCPE_IERR; /* inv conn? err */ + ptr_sor = 1; /* start of rec */ + xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ + sim_activate (&ptr_unit, ptr_unit.wait); /* activate */ + break; + + case IO_DISC: /* disconnect */ + ptr_sor = 0; /* clear state */ + xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ + sim_cancel (&ptr_unit); /* deactivate unit */ + break; + + case IO_READ: /* read */ + xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ + *dat = ptr_unit.buf & 077; /* get buf data */ + if (ptr_unit.buf != odd_par[*dat]) /* good parity? */ + chan_set_flag (ptr_dib.chan, CHF_ERR); /* no, error */ + break; + + case IO_WREOR: /* write eor */ + break; + + case IO_EOM1: /* EOM mode 1*/ + case IO_WRITE: /* write */ + CRETINS; /* error */ + } + +return SCPE_OK; +} + +/* Unit service */ + +t_stat ptr_svc (UNIT *uptr) +{ +int32 temp; + +if ((ptr_unit.flags & UNIT_ATT) == 0) { /* attached? */ + ptr_set_err (); /* no, err, disc */ + CRETIOE (ptr_stopioe, SCPE_UNATT); + } +if ((temp = getc (ptr_unit.fileref)) == EOF) { /* end of file? */ + ptr_set_err (); /* yes, err, disc */ + if (feof (ptr_unit.fileref)) { /* end of file? */ + if (ptr_stopioe) printf ("PTR end of file\n"); + else return SCPE_OK; + } + else perror ("PTR I/O error"); /* I/O error */ + clearerr (ptr_unit.fileref); + return SCPE_IOERR; + } +ptr_unit.pos = ptr_unit.pos + 1; /* inc position */ +if (temp) { /* leader/gap? */ + ptr_unit.buf = temp & 0177; /* no, save char */ + xfr_req = xfr_req | XFR_PTR; /* set xfr flag */ + ptr_sor = 0; /* in record */ + } +else if (!ptr_sor) /* end record? */ + chan_set_flag (ptr_dib.chan, CHF_EOR); /* ignore leader */ +sim_activate (&ptr_unit, ptr_unit.wait); /* get next char */ +return SCPE_OK; +} + +/* Fatal error */ + +void ptr_set_err (void) +{ +chan_set_flag (ptr_dib.chan, CHF_EOR | CHF_ERR); /* eor, error */ +chan_disc (ptr_dib.chan); /* disconnect */ +xfr_req = xfr_req & ~XFR_PTR; /* clear xfr */ +sim_cancel (&ptr_unit); /* stop */ +return; +} + +/* Reset routine */ + +t_stat ptr_reset (DEVICE *dptr) +{ +chan_disc (ptr_dib.chan); /* disconnect */ +ptr_sor = 0; /* clear state */ +ptr_unit.buf = 0; +xfr_req = xfr_req & ~XFR_PTR; /* clr xfr flag */ +sim_cancel (&ptr_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Boot routine - simulate FILL console command */ + +t_stat ptr_boot (int32 unitno, DEVICE *dptr) +{ +extern uint32 P, M[]; + +M[0] = 077777771; /* -7B */ +M[1] = 007100000; /* LDX 0 */ +M[2] = 000203604; /* EOM 3604B */ +M[3] = 003200002; /* WIM 2 */ +M[4] = 000100002; /* BRU 2 */ +P = 1; /* start at 1 */ +return SCPE_OK; +} + +/* Paper tape punch + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result + + The paper tape punch is an asynchronous streaming output device. That is, + it can never cause a channel rate error; if no data is available, it waits. +*/ + +t_stat ptp (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 new_ch; + +switch (fnc) { /* case function */ + + case IO_CONN: + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != ptp_dib.chan) return SCPE_IERR; /* inv conn? err */ + ptp_ldr = (inst & CHC_NLDR)? 0: 1; /* leader? */ + xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ + sim_activate (&ptp_unit, ptp_unit.wait); /* activate */ + break; + + case IO_DISC: /* disconnect */ + ptp_ldr = 0; /* clear state */ + xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ + sim_cancel (&ptp_unit); /* deactivate unit */ + break; + + case IO_WRITE: /* write */ + xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ + sim_activate (&ptp_unit, ptp_unit.wait); /* activate */ + ptp_unit.buf = odd_par[(*dat) & 077]; /* save data */ + return ptp_out (ptp_unit.buf); /* punch w/ par */ + + case IO_WREOR: /* write eor */ + break; + + case IO_EOM1: /* EOM mode 1*/ + case IO_READ: /* read */ + CRETINS; /* error */ + } + +return SCPE_OK; +} + +/* Unit service */ + +t_stat ptp_svc (UNIT *uptr) +{ +int32 i; +t_stat r = SCPE_OK; + +if (ptp_ldr) { /* need leader? */ + for (i = 0; i < 12; i++) { /* punch leader */ + if (r = ptp_out (0)) break; + } + } +ptp_ldr = 0; /* clear flag */ +chan_set_ordy (ptp_dib.chan); /* ptp ready */ +return r; +} + +/* Punch I/O */ + +t_stat ptp_out (int32 dat) +{ +if ((ptp_unit.flags & UNIT_ATT) == 0) { /* attached? */ + ptp_set_err (); /* no, disc, err */ + CRETIOE (ptp_stopioe, SCPE_UNATT); + } +if (putc (dat, ptp_unit.fileref) == EOF) { /* I/O error? */ + ptp_set_err (); /* yes, disc, err */ + perror ("PTP I/O error"); /* print msg */ + clearerr (ptp_unit.fileref); + return SCPE_IOERR; + } +ptp_unit.pos = ptp_unit.pos + 1; /* inc position */ +return SCPE_OK; +} + +/* Fatal error */ + +void ptp_set_err (void) +{ +chan_set_flag (ptp_dib.chan, CHF_ERR); /* error */ +chan_disc (ptp_dib.chan); /* disconnect */ +xfr_req = xfr_req & ~XFR_PTP; /* clear xfr */ +sim_cancel (&ptp_unit); /* stop */ +return; +} + +/* Reset routine */ + +t_stat ptp_reset (DEVICE *dptr) +{ +chan_disc (ptp_dib.chan); /* disconnect */ +ptp_ldr = 0; /* clear state */ +ptp_unit.buf = 0; +xfr_req = xfr_req & ~XFR_PTP; /* clr xfr flag */ +sim_cancel (&ptp_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Typewriter input + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result + + The typewriter input is an asynchronous input device. That is, it can + never cause a channel rate error; if no data is available, it waits. +*/ + +t_stat tti (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 new_ch; + +switch (fnc) { /* case function */ + + case IO_CONN: /* connect */ + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != tti_dib.chan) return SCPE_IERR; /* inv conn? err */ + xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ + break; + + case IO_DISC: /* disconnect */ + xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ + break; + + case IO_READ: /* read */ + xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ + *dat = tti_unit.buf; /* get buf data */ + break; + + case IO_WREOR: /* write eor */ + break; + + case IO_EOM1: /* EOM mode 1*/ + case IO_WRITE: /* write */ + CRETINS; /* error */ + } + +return SCPE_OK; +} + +/* Unit service */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 temp; + +sim_activate (&tti_unit, tti_unit.wait); /* continue poll */ +if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */ +if (temp & SCPE_BREAK) return SCPE_OK; /* ignore break */ +temp = temp & 0177; +tti_unit.pos = tti_unit.pos + 1; +if (ascii_to_sds[temp] >= 0) { + tti_unit.buf = ascii_to_sds[temp]; /* internal rep */ + sim_putchar (temp); /* echo */ + if (temp == '\r') sim_putchar ('\n'); /* lf after cr */ + xfr_req = xfr_req | XFR_TTI; /* set xfr flag */ + } +else sim_putchar (007); /* ding! */ +return SCPE_OK; +} + +t_stat tti_reset (DEVICE *dptr) +{ +chan_disc (tti_dib.chan); /* disconnect */ +tti_unit.buf = 0; /* clear state */ +xfr_req = xfr_req & ~XFR_TTI; /* clr xfr flag */ +sim_activate (&tti_unit, tti_unit.wait); /* start poll */ +return SCPE_OK; +} + +/* Typewriter output + + conn - inst = EOM0, dat = NULL + eom1 - inst = EOM1, dat = NULL + sks - inst = SKS, dat = ptr to result + disc - inst = device number, dat = NULL + wreor - inst = device number, dat = NULL + read - inst = device number, dat = ptr to data + write - inst = device number, dat = ptr to result + + The typewriter output is an asynchronous streaming output device. That is, + it can never cause a channel rate error; if no data is available, it waits. +*/ + +t_stat tto (uint32 fnc, uint32 inst, uint32 *dat) +{ +int32 new_ch; + +switch (fnc) { /* case function */ + + case IO_CONN: + new_ch = I_GETEOCH (inst); /* get new chan */ + if (new_ch != tto_dib.chan) return SCPE_IERR; /* inv conn? err */ + xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ + sim_activate (&tto_unit, tto_unit.wait); /* activate */ + break; + + case IO_DISC: /* disconnect */ + xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ + sim_cancel (&tto_unit); /* deactivate unit */ + break; + + case IO_WRITE: /* write */ + xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ + tto_unit.buf = (*dat) & 077; /* save data */ + sim_activate (&tto_unit, tto_unit.wait); /* activate */ + break; + + case IO_WREOR: /* write eor */ + break; + + case IO_EOM1: /* EOM mode 1*/ + case IO_READ: /* read */ + CRETINS; /* error */ + } + +return SCPE_OK; +} + +/* Unit service */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 asc; +t_stat r; + +if (uptr->buf == TT_CR) asc = '\r'; /* control chars? */ +else if (uptr->buf == TT_BS) asc = '\b'; +else if (uptr->buf == TT_TB) asc = '\t'; +else asc = sds_to_ascii[uptr->buf]; /* translate */ +if ((r = sim_putchar_s (asc)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* retry */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } +uptr->pos = uptr->pos + 1; /* inc position */ +chan_set_ordy (tto_dib.chan); /* tto rdy */ +if (asc == '\r') { /* CR? */ + sim_putchar ('\n'); /* add lf */ + uptr->pos = uptr->pos + 1; /* inc position */ + } +return SCPE_OK; +} + +/* Reset routine */ + +t_stat tto_reset (DEVICE *dptr) +{ +chan_disc (tto_dib.chan); /* disconnect */ +tto_unit.buf = 0; /* clear state */ +xfr_req = xfr_req & ~XFR_TTO; /* clr xfr flag */ +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} diff --git a/SDS/sds_sys.c b/SDS/sds_sys.c new file mode 100644 index 0000000..c00823a --- /dev/null +++ b/SDS/sds_sys.c @@ -0,0 +1,618 @@ +/* sds_sys.c: SDS 940 simulator interface + + Copyright (c) 2001-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#include "sds_defs.h" +#include +#define FMTASC(x) ((x) < 040)? "<%03o>": "%c", (x) + +extern DEVICE cpu_dev; +extern DEVICE chan_dev; +extern DEVICE ptr_dev; +extern DEVICE ptp_dev; +extern DEVICE tti_dev; +extern DEVICE tto_dev; +extern DEVICE lpt_dev; +extern DEVICE rtc_dev; +extern DEVICE drm_dev; +extern DEVICE rad_dev; +extern DEVICE dsk_dev; +extern DEVICE mt_dev; +extern DEVICE mux_dev, muxl_dev; +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern uint32 M[MAXMEMSIZE]; + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +char sim_name[] = "SDS 940"; + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 1; + +DEVICE *sim_devices[] = { + &cpu_dev, + &chan_dev, + &ptr_dev, + &ptp_dev, + &tti_dev, + &tto_dev, + &lpt_dev, + &rtc_dev, + &drm_dev, + &rad_dev, + &dsk_dev, + &mt_dev, + &mux_dev, + &muxl_dev, + NULL + }; + +const char *sim_stop_messages[] = { + "Unknown error", + "IO device not ready", + "HALT instruction", + "Breakpoint", + "Invalid IO device", + "Invalid instruction", + "Invalid I/O operation", + "Nested indirects exceed limit", + "Nested EXU's exceed limit", + "Memory management trap during interrupt", + "Memory management trap during trap", + "Trap instruction not BRM", + "RTC instruction not MIN or SKR", + "Interrupt vector zero", + "Runaway carriage control tape" + }; + +/* Character conversion tables */ + +const char sds_to_ascii[64] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', ' ', '=', '\'', ':', '>', '%', /* 17 = check mark */ + '+', 'A', 'B', 'C', 'D', 'E', 'F', 'G', + 'H', 'I', '?', '.', ')', '[', '<', '@', /* 37 = stop code */ + '-', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', '!', '$', '*', ']', ';', '^', /* 57 = triangle */ + '_', '/', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', '?', ',', '(', '~', '\\', '#' /* 72 = rec mark */ + }; /* 75 = squiggle, 77 = del */ + +const char ascii_to_sds[128] = { + -1, -1, -1, -1, -1, -1, -1, -1, /* 0 - 37 */ + 032, 072, -1, -1, -1, 052, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + 012, 052, -1, 077, 053, 017, -1, 014, /* 40 - 77 */ + 074, 034, 054, 020, 073, 040, 033, 061, + 000, 001, 002, 003, 004, 005, 006, 007, + 010, 011, 015, 056, 036, 013, 016, 072, + 037, 021, 022, 023, 024, 025, 026, 027, /* 100 - 137 */ + 030, 031, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 062, 063, 064, 065, 066, + 067, 070, 071, 035, 076, 055, 057, 060, + 000, 021, 022, 023, 024, 025, 026, 027, /* 140 - 177 */ + 030, 031, 041, 042, 043, 044, 045, 046, + 047, 050, 051, 062, 063, 064, 065, 066, + 067, 070, 071, -1, -1, -1, -1, -1 + }; + +const char odd_par[64] = { + 0100, 0001, 0002, 0103, 0004, 0105, 0106, 0007, + 0010, 0111, 0112, 0013, 0114, 0015, 0016, 0117, + 0020, 0121, 0122, 0023, 0124, 0025, 0026, 0127, + 0130, 0031, 0032, 0133, 0034, 0135, 0136, 0037, + 0040, 0141, 0142, 0043, 0144, 0045, 0046, 0147, + 0150, 0051, 0052, 0153, 0054, 0155, 0156, 0057, + 0160, 0061, 0062, 0163, 0064, 0165, 0166, 0067, + 0070, 0171, 0172, 0073, 0174, 0075, 0076, 0177 + }; + +/* Load carriage control tape + + A carriage control tape consists of entries of the form + + (repeat count) column number,column number,column number,... + + The CCT entries are stored in lpt_cct[0:lnt-1], lpt_ccl contains the + number of entries +*/ + +t_stat sim_load_cct (FILE *fileref) +{ +int32 col, rpt, ptr, mask, cctbuf[CCT_LNT]; +t_stat r; +extern int32 lpt_ccl, lpt_ccp, lpt_cct[CCT_LNT]; +char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE]; + +ptr = 0; +for ( ; (cptr = fgets (cbuf, CBUFSIZE, fileref)) != NULL; ) { /* until eof */ + mask = 0; + if (*cptr == '(') { /* repeat count? */ + cptr = get_glyph (cptr + 1, gbuf, ')'); /* get 1st field */ + rpt = get_uint (gbuf, 10, CCT_LNT, &r); /* repeat count */ + if (r != SCPE_OK) return SCPE_FMT; + } + else rpt = 1; + while (*cptr != 0) { /* get col no's */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + col = get_uint (gbuf, 10, 7, &r); /* column number */ + if (r != SCPE_OK) return SCPE_FMT; + mask = mask | (1 << col); /* set bit */ + } + for ( ; rpt > 0; rpt--) { /* store vals */ + if (ptr >= CCT_LNT) return SCPE_FMT; + cctbuf[ptr++] = mask; + } + } +if (ptr == 0) return SCPE_FMT; +lpt_ccl = ptr; +lpt_ccp = 0; +for (rpt = 0; rpt < lpt_ccl; rpt++) lpt_cct[rpt] = cctbuf[rpt]; +return SCPE_OK; +} + +/* Load command. -l means load a line printer tape. Otherwise, load + a bootstrap paper tape. +*/ + +int32 get_word (FILE *fileref, int32 *ldr) +{ +int32 i, c, wd; + +for (i = wd = 0; i < 4; ) { + if ((c = fgetc (fileref)) == EOF) return -1; + if ((c == 0) && (*ldr == 0)) return -1; + if (c == 0) continue; + *ldr = 0; + wd = (wd << 6) | (c & 077); + i++; + } +return wd; +} + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +int32 i, wd, buf[8]; +int32 ldr = 1; +extern int32 sim_switches; +extern uint32 P; + +if ((*cptr != 0) || (flag != 0)) return SCPE_ARG; +if (sim_switches & SWMASK ('L')) return sim_load_cct (fileref); +for (i = 0; i < 8; i++) { /* read boot */ + if ((wd = get_word (fileref, &ldr)) < 0) return SCPE_FMT; + buf[i] = wd; + } +if ((buf[0] != 023200012) || /* 2 = WIM 12,2 */ + (buf[1] != 004100002) || /* 3 = BRX 2 */ + (buf[2] != 007100011) || /* 4 = LDX 11 */ + (buf[3] != 023200000) || /* 5 = WIM 0,2 */ + (buf[4] != 004021000) || /* 6 = SKS 21000 */ + (buf[5] != 004100005)) return SCPE_FMT; /* 7 = BRX 5 */ +for (i = 0; i < 8; i++) M[i + 2] = buf[i]; /* copy boot */ +if (I_GETOP (buf[6]) == BRU) P = buf[6] & VA_MASK; +for (i = buf[7] & VA_MASK; i <= VA_MASK; i++) { /* load data */ + if ((wd = get_word (fileref, &ldr)) < 0) return SCPE_OK; + M[i] = wd; + } +return SCPE_NXM; +} + +/* Symbol tables */ + +#define I_V_FL 24 /* inst class */ +#define I_M_FL 017 /* class mask */ +#define I_V_NPN 000 /* no operand */ +#define I_V_PPO 001 /* POP */ +#define I_V_IOI 002 /* IO */ +#define I_V_MRF 003 /* memory reference */ +#define I_V_REG 004 /* register change */ +#define I_V_SHF 005 /* shift */ +#define I_V_OPO 006 /* opcode only */ +#define I_V_CHC 007 /* chan cmd */ +#define I_V_CHT 010 /* chan test */ +#define I_NPN (I_V_NPN << I_V_FL) +#define I_PPO (I_V_PPO << I_V_FL) +#define I_IOI (I_V_IOI << I_V_FL) +#define I_MRF (I_V_MRF << I_V_FL) +#define I_REG (I_V_REG << I_V_FL) +#define I_SHF (I_V_SHF << I_V_FL) +#define I_OPO (I_V_OPO << I_V_FL) +#define I_CHC (I_V_CHC << I_V_FL) +#define I_CHT (I_V_CHT << I_V_FL) + +static const int32 masks[] = { + 037777777, 010000000, 017700000, + 017740000, 017700000, 017774000, + 017700000, 017377677, 027737677 + }; + +static const char *opcode[] = { + "POP", "EIR", "DIR", + "ROV", "REO", "OTO", "OVT", + "IDT", "IET", + "BPT4", "BPT3", "BPT2", "BPT1", + "CLAB", "ABC", "BAC", "XAB", + "XXB", "STE", "LDE", "XEE", + "CLEAR", + + "HLT", "BRU", "EOM", "EOD", + "MIY", "BRI", "MIW", "POT", + "ETR", "MRG", "EOR", + "NOP", "EXU", + "YIM", "WIM", "PIN", + "STA", "STB", "STX", + "SKS", "BRX", "BRM", + "SKE", "BRR", "SKB", "SKN", + "SUB", "ADD", "SUC", "ADC", + "SKR", "MIN", "XMA", "ADM", + "MUL", "DIV", + "SKM", "LDX", "SKA", "SKG", + "SKD", "LDB", "LDA", "EAX", + + "BRU*", + "MIY*", "BRI*", "MIW*", "POT*", + "ETR*", "MRG*", "EOR*", + "EXU*", + "YIM*", "WIM*", "PIN*", + "STA*", "STB*", "STX*", + "BRX*", "BRM*", + "SKE*", "BRR*", "SKB*", "SKN*", + "SUB*", "ADD*", "SUC*", "ADC*", + "SKR*", "MIN*", "XMA*", "ADM*", + "MUL*", "DIV*", + "SKM*", "LDX*", "SKA*", "SKG*", + "SKD*", "LDB*", "LDA*", "EAX*", + + "RSH", "RCY", "LRSH", + "LSH", "NOD", "LCY", + "RSH*", "LSH*", + + "ALC", "DSC", "ASC", "TOP", + "CAT", "CET", "CZT", "CIT", + + "CLA", "CLB", "CAB", /* encode only */ + "CBA", "CBX", "CXB", + "XPO", "CXA", "CAX", + "CNA", "CLX", NULL, + NULL + }; + +static const int32 opc_val[] = { + 010000000+I_PPO, 000220002+I_NPN, 000220004+I_NPN, + 002200001+I_NPN, 002200010+I_NPN, 002200100+I_NPN, 002200101+I_NPN, + 004020002+I_NPN, 004020004+I_NPN, + 004020040+I_NPN, 004020100+I_NPN, 004020200+I_NPN, 004020400+I_NPN, + 004600003+I_NPN, 004600005+I_NPN, 004600012+I_NPN, 004600014+I_NPN, + 004600060+I_NPN, 004600122+I_NPN, 004600140+I_NPN, 004600160+I_NPN, + 024600003+I_NPN, + + 000000000+I_NPN, 000100000+I_MRF, 000200000+I_IOI, 000600000+I_IOI, + 001000000+I_MRF, 001100000+I_MRF, 001200000+I_MRF, 001300000+I_MRF, + 001400000+I_MRF, 001600000+I_MRF, 001700000+I_MRF, + 002000000+I_OPO, 002300000+I_MRF, + 003000000+I_MRF, 003200000+I_MRF, 003300000+I_MRF, + 003500000+I_MRF, 003600000+I_MRF, 003700000+I_MRF, + 004000000+I_IOI, 004100000+I_MRF, 004300000+I_MRF, + 005000000+I_MRF, 005100000+I_MRF, 005200000+I_MRF, 005300000+I_MRF, + 005400000+I_MRF, 005500000+I_MRF, 005600000+I_MRF, 005700000+I_MRF, + 006000000+I_MRF, 006100000+I_MRF, 006200000+I_MRF, 006300000+I_MRF, + 006400000+I_MRF, 006500000+I_MRF, + 007000000+I_MRF, 007100000+I_MRF, 007200000+I_MRF, 007300000+I_MRF, + 007400000+I_MRF, 007500000+I_MRF, 007600000+I_MRF, 007700000+I_MRF, + + 000140000+I_MRF, + 001040000+I_MRF, 001140000+I_MRF, 001240000+I_MRF, 001340000+I_MRF, + 001440000+I_MRF, 001640000+I_MRF, 001740000+I_MRF, + 002340000+I_MRF, + 003040000+I_MRF, 003240000+I_MRF, 003340000+I_MRF, + 003540000+I_MRF, 003640000+I_MRF, 003740000+I_MRF, + 004140000+I_MRF, 004340000+I_MRF, + 005040000+I_MRF, 005140000+I_MRF, 005240000+I_MRF, 005340000+I_MRF, + 005440000+I_MRF, 005540000+I_MRF, 005640000+I_MRF, 005740000+I_MRF, + 006040000+I_MRF, 006140000+I_MRF, 006240000+I_MRF, 006340000+I_MRF, + 006440000+I_MRF, 006540000+I_MRF, + 007040000+I_MRF, 007140000+I_MRF, 007240000+I_MRF, 007340000+I_MRF, + 007440000+I_MRF, 007540000+I_MRF, 007640000+I_MRF, 007740000+I_MRF, + + 006600000+I_SHF, 006620000+I_SHF, 006624000+I_SHF, + 006700000+I_SHF, 006710000+I_SHF, 006720000+I_SHF, + 006640000+I_MRF, 006740000+I_MRF, + + 000250000+I_CHC, 000200000+I_CHC, 000212000+I_CHC, 000214000+I_CHC, + 004014000+I_CHT, 004011000+I_CHT, 004012000+I_CHT, 004010400+I_CHT, + + 004600001+I_REG, 004600002+I_REG, 004600004+I_REG, + 004600010+I_REG, 004600020+I_REG, 004600040+I_REG, + 004600100+I_REG, 004600200+I_REG, 004600400+I_REG, + 004601000+I_REG, 024600000+I_REG, 004600000+I_REG, + -1 + }; + +static const char *chname[] = { + "W", "Y", "C", "D", "E", "F", "G", "H", NULL + }; + +/* Register change decode + + Inputs: + *of = output stream + inst = mask bits +*/ + +void fprint_reg (FILE *of, int32 inst) +{ +int32 i, j, sp; + +inst = inst & ~(I_M_OP << I_V_OP); /* clear opcode */ +for (i = sp = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((j == I_V_REG) && (opc_val[i] & inst)) { /* reg class? */ + inst = inst & ~opc_val[i]; /* mask bit set? */ + fprintf (of, (sp? " %s": "%s"), opcode[i]); + sp = 1; + } + } +return; +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = pointer to values + *uptr = pointer to unit + sw = switches + Outputs: + return = status code +*/ + +t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, + UNIT *uptr, int32 sw) +{ +int32 i, j, ch; +int32 inst, op, tag, va, shf, nonop; + +inst = val[0]; /* get inst */ +op = I_GETOP (inst); /* get fields */ +tag = (inst >> 21) & 06; +va = inst & VA_MASK; +shf = inst & I_SHFMSK; +nonop = inst & 077777; + +if (sw & SWMASK ('A')) { /* ASCII? */ + if (inst > 0377) return SCPE_ARG; + fprintf (of, FMTASC (inst & 0177)); + return SCPE_OK; + } +if (sw & SWMASK ('C')) { /* character? */ + fprintf (of, "%c", sds_to_ascii[(inst >> 18) & 077]); + fprintf (of, "%c", sds_to_ascii[(inst >> 12) & 077]); + fprintf (of, "%c", sds_to_ascii[(inst >> 6) & 077]); + fprintf (of, "%c", sds_to_ascii[inst & 077]); + return SCPE_OK; + } +if (!(sw & SWMASK ('M'))) return SCPE_ARG; + +/* Instruction decode */ + +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + if ((opc_val[i] & DMASK) == (inst & masks[j])) { /* match? */ + + switch (j) { /* case on class */ + + case I_V_NPN: /* no operands */ + case I_V_OPO: /* opcode only */ + fprintf (of, "%s", opcode[i]); /* opcode */ + break; + + case I_V_SHF: /* shift */ + fprintf (of, "%s %-o", opcode[i], shf); + if (tag) fprintf (of, ",%-o", tag); + break; + + case I_V_PPO: /* pop */ + fprintf (of, "POP %-o,%-o", op, nonop); + if (tag) fprintf (of, ",%-o", tag); + break; + + case I_V_IOI: /* I/O */ + fprintf (of, "%s %-o", opcode[i], nonop); + if (tag) fprintf (of, ",%-o", tag); + break; + + case I_V_MRF: /* mem ref */ + fprintf (of, "%s %-o", opcode[i], va); + if (tag) fprintf (of, ",%-o", tag); + break; + + case I_V_REG: /* reg change */ + fprint_reg (of, inst); /* decode */ + break; + + case I_V_CHC: /* chan cmd */ + ch = I_GETEOCH (inst); /* get chan */ + fprintf (of, "%s %s", opcode[i], chname[ch]); + break; + + case I_V_CHT: /* chan test */ + ch = I_GETSKCH (inst); /* get chan */ + fprintf (of, "%s %s", opcode[i], chname[ch]); + break; + } /* end case */ + + return SCPE_OK; + } /* end if */ + } /* end for */ +return SCPE_ARG; +} + +/* Get (optional) tag + + Inputs: + *cptr = pointer to input string + *tag = pointer to tag + Outputs: + cptr = updated pointer to input string +*/ + +char *get_tag (char *cptr, t_value *tag) +{ +char *tptr, gbuf[CBUFSIZE]; +t_stat r; + +tptr = get_glyph (cptr, gbuf, 0); /* get next field */ +*tag = get_uint (gbuf, 8, 07, &r) << I_V_TAG; /* parse */ +if (r == SCPE_OK) return tptr; /* ok? advance */ +*tag = 0; +return cptr; /* no change */ +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = error status +*/ + +t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw) +{ +int32 i, j, k; +t_value d, tag; +t_stat r; +char gbuf[CBUFSIZE]; + +while (isspace (*cptr)) cptr++; +for (i = 1; (i < 4) && (cptr[i] != 0); i++) + if (cptr[i] == 0) for (j = i + 1; j <= 4; j++) cptr[j] = 0; +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) { /* ASCII char? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + val[0] = (t_value) cptr[0] | 0200; + return SCPE_OK; + } +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) { /* string? */ + if (cptr[0] == 0) return SCPE_ARG; /* must have 1 char */ + for (i = j = 0, val[0] = 0; i < 4; i++) { + if (cptr[i] == 0) j = 1; /* latch str end */ + k = ascii_to_sds[cptr[i] & 0177]; /* cvt char */ + if (j || (k < 0)) k = 0; /* bad, end? spc */ + val[0] = (val[0] << 6) | k; + } + return SCPE_OK; + } + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & DMASK; /* get value */ +j = (opc_val[i] >> I_V_FL) & I_M_FL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_NPN: case I_V_OPO: /* opcode only */ + break; + + case I_V_SHF: /* shift */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + d = get_uint (gbuf, 8, I_SHFMSK, &r); /* shift count */ + if (r != SCPE_OK) return SCPE_ARG; + cptr = get_tag (cptr, &tag); /* get opt tag */ + val[0] = val[0] | d | tag; + break; + + case I_V_PPO: /* pop */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + d = get_uint (gbuf, 8, 077, &r); /* opcode */ + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; /* fall thru */ + + case I_V_IOI: /* I/O */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + d = get_uint (gbuf, 8, 077777, &r); /* 15b address */ + if (r != SCPE_OK) return SCPE_ARG; + cptr = get_tag (cptr, &tag); /* get opt tag */ + val[0] = val[0] | d | tag; + break; + + case I_V_MRF: /* mem ref */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + d = get_uint (gbuf, 8, VA_MASK, &r); /* virt address */ + if (r != SCPE_OK) return SCPE_ARG; + cptr = get_tag (cptr, &tag); /* get opt tag */ + val[0] = val[0] | d | tag; + break; + + case I_V_REG: /* register */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0); i++) ; + if (opcode[i] != NULL) { + k = opc_val[i] & DMASK;; + if (I_GETOP (k) != RCH) return SCPE_ARG; + val[0] = val[0] | k; + } + else { + d = get_uint (gbuf, 8, 077777, &r); + if (r != SCPE_OK) return SCPE_ARG; + else val[0] = val[0] | d; + } + } + break; + + case I_V_CHC: case I_V_CHT: /* channel */ + cptr = get_glyph (cptr, gbuf, ','); /* get next field */ + for (i = 0; (chname[i] != NULL) && (strcmp (chname[i], gbuf) != 0); + i++); + if (chname[i] != NULL) d = i; /* named chan */ + else { + d = get_uint (gbuf, 8, NUM_CHAN - 1, &r); + if (r != SCPE_OK) return SCPE_ARG; /* numbered chan */ + } + val[0] = val[0] | ((j == I_V_CHC)? I_SETEOCH (d): I_SETSKCH (d)); + break; + } /* end case */ + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +return SCPE_OK; +} diff --git a/VAX/ka655_patch.com b/VAX/ka655_patch.com new file mode 100644 index 0000000..e35cb74 --- /dev/null +++ b/VAX/ka655_patch.com @@ -0,0 +1,509 @@ +$! +$! This procedure patches the base KA655.BIN Boot ROM image to work under +$! the SIMH simulator +$ +$! The second part of the patch adds support for Extended Memory in the +$! simulator. A simulated system can have up to 512MB of RAM. +$! +$ PATCH /ABSOLUTE /NEW_VERSION /OUTPUT=cp$exe:KA655.BIN cp$src:ka655_orig.BIN +! CVAX Bootstrap Notes +! +! [2004c87e] - de$read_script +! [2004c916] - launch next test, r2 = test #, r4 = header, r5 = offset to main routine +! +! Script BA +! --------- +! +! Test 9D - Utility - ok +! Test 42 - Wait for interrupt - ok +! Test C6 - SSC register check - ok +! Test 60 [2004de37] - Serial line - requires diagnostic loopback mode and +! break detection - bypass +! 2004de99/ brw 2004e0a7 +Replace/Instruction 0DE99 = 'MOVB #03,B^76(R9)' +'BRW 0000E0A7' +Exit +! Test 62 - QDSS disable - ok +! +! Script BC +! --------- +! +! 40. Test 91 - CQBIC init check - ok +! 39. Test 90 [2004d748] - CQBIC registers - ok +! 38. Test 33 [2004d54e] - CMCTL init check - ok +! 37. Test 32 [2004d5b0] - CMCTL registers - ok +! 36. Test 31 [200512d0] - CMCTL CSR setup to map memory - ok +! 35. Test 49 [20052a4b] - CMCTL FDM test - requires fast diagnostic mode +! and XMX counters - bypass +! 20052a55:20052a58/ nop +Delete/Instruction 12A55 = 'BBC #26,(R9),00012A5C' +! 34. Test 30 [20051909] - init map - ok +! +! Script BD +! --------- +! +! 33. Test 52 [2004e656] - prog timer 0 - ok +! 32. Test 52 [2004e656] - prog timer 1 - ok +! 31. Test 53 [2004e918] - toy clock - ok +! 30. Test C1 [2004f8f1] - SSC RAM - ok +! 29. Test 34 [2004d4a0] - ROM - checksum off due to other patches - patch +! 2004d52c:2004d52d/ nop +Delete/Instruction 0D52C = 'BNEQ 0000D531' ! 2004D52C +! 28. Test C5 [2004fc0e] - SSC registers - ok +! 27. Test 55 [2004ea8c] - interval timer - ok +! 26. Test 51 [2004e42d] - CFPA - ok +! 25. Test C7 [2004D3D3] - CBTCR<31:30> - ok +! 24. Test 46 [2004ef1a] - L1 cache diagnostic mode - bypass +! 2004ef80/ brw 2004f47a +Replace/Instruction 0EF80 = 'MOVB #06,B^76(R9)' +'BRW 0000F47A' ! 2004FE80 +Exit +! 23. Test 35 [20050554] - L2 cache integrity test - bypass +! 20050554/ brw 20050a48 +Replace/Instruction 10554 = 'INSV #00,#10,#02,(R9)' +'BRW 00010A48' ! 20050554 +Exit +! 22. Test 43 [20050d65] - L1 with L2 test - bypass +! 20050d65/ brw 20050fca +Replace/Instruction 10D65 = 'MOVAL B^00010D65,W^0080(R9)' +'BRW 00010FCA' ! 20050D65 +Exit +! 21. (Rerun of C2) +! 20. Test 4F [20051d4f] - memory data - bypass, run from ROM +! 20055205/ 0 +Replace/Byte 15205 = 3 +0 ! 20055205 +Exit +! 20051d4f/ brw 2005163a +Replace/Instruction 11D4F = 'MOVAL B^00011D4F,W^0080(R9)' +'BRW 0001163A' ! 20051D4F +Exit +! 19. Test 4E [20051edb] - memory byte write - ok, run from ROM +! 2005521c/ 0 +Replace/Byte 1521C = 3 +0 ! 2005521C +Exit +! 18. Test 4D [20051ff3] - memory addressing - ok, run from ROM +! 20055233/ 0 +Replace/Byte 15233 = 3 +0 ! 20055233 +Exit +! 17. Test 4C [20052190] - ECC test - bypass, run from ROM +! 2005524a/ 0 +Replace/Byte 1524A = 3 +0 ! 2005524A +Exit +! 20052190/ brw 2005163a +Replace/Instruction 12190 = 'MOVAL B^00012190,W^0080(R9)' +'BRW 0001163A' ! 20052190 +Exit +! 16. Test 4B [2005264e] - masked writes with errors - bypass, run from ROM +! 20055261/ 0 +Replace/Byte 15261 = 3 +0 ! 20055261 +Exit +! 2005264e/ brw 2005163a +Replace/Instruction 1264E = 'MOVAL B^0001264E,W^0080(R9)' +'BRW 0001163A' ! 2005264E +Exit +! 15. Test 4A [20052823] - single bit correction - bypass, run from ROM +! 20055278/ 0 +Replace/Byte 15278 = 3 +0 ! 20055278 +Exit +! 20052823/ brw 2005163a +Replace/Instruction 12823 = 'MOVAL B^00012823,W^0080(R9)' +'BRW 0001163A' ! 20052823 +Exit +! 14. Test 48 [20053062] - memory address shorts - bypass, run from ROM +! 2005528f/ 0 +Replace/Byte 1528F = 3 +0 ! 2005528F +Exit +! 20053062/ brw 2005163a +Replace/Instruction 13062 = 'MOVAL B^00013062,W^0080(R9)' +'BRW 0001163A' ! 20053062 +Exit +! 13. Test 47 [200536c3] - verify refresh - run from ROM +! 200552aa/ 0 +Replace/Byte 152AA = 3 +0 ! 200552AA +Exit +! 12. Test 41 [] - count bad pages, relocate bit map +! 11. Test 44 [20050d34] - L1 with memory - bypass +! 20050d34/ brw 20050fca +Replace/Instruction 10D34 = 'MOVAL B^00010D34,W^0080(R9)' +'BRW 00010FCA' ! 20050D34 +Exit +! 10. Test 36 [2004ffdf] - L2 with memory - bypass +! 2004ffdf/ brw 20050428 +Replace/Instruction 0FFDF = 'JSB L^0000CEFD' +'BRW 00010428' ! 2004FFDF +Exit +! 9. Test 80 [2004d7de] - CQBIC memory - bypass last 2 subtests, run from ROM +! 2004dbc0/ brw 2004dd8a +Replace/Instruction 0DBC0 = 'MOVB #1B,B^76(R9)' +'BRW 0000DD8A' ! 2004DBC0 +Exit +! 200552f6/ 0 +Replace/Byte 152F6 = 3 +0 ! 200552F6 +Exit +! 8. Test 54 [] - virtual mode - ok +! 7. Test 34 [] - ROM in virtual mode - see previous notes +! 6. Test C5 [] - SSC registers in virtual mode - ok +! 5. Test 45 [2004ec5d] - cache, memory, CQBIC - bypass +! 2004ec5d/ brw 2004ee90 +Replace/Instruction 0EC5D = 'BICL2 #03,B^28(R9)' +'BRW 0000EE90' ! 2004EC5D +Exit +! 4. Test 5A [2004eb5f] - CVAX CMCTL DAL drivers - ok +! 3. Test 41 [20051096] - reset board +! +! =========================================================================== +! +! +! All of the above patches were done against the base ROM image extracted +! from a genuine MicroVAX 3900. These were all part of SIMH prior to +! extended memory support. +! +! The Diagnostic State Variable DST$W_BITMAP_LENGTH, being 16 bits, can only +! describe a PFN Bitmap describing up to, but NOT including 256MB of RAM. To +! get to 256MB and beyond, we must correctly determine a correct bitmap size. +! all of the Diagnostic state space is in use, either since it is already +! defined, and the space beyond that is used for stack. So, we change the +! references to DST$W_BITMAP_LENGTH to call a subroutine to properly determine +! the PFN BITMAP size. +! +! Most of the code which references DST$W_BITMAP_LENGTH are done from a +! diagnostic test routine which may be relocated to RAM before execution. +! The assumption of such relocating activity is that none of the relocated code +! references any other instructions or data in the ROM image via any PC +! relative addressing modes. Given this requirement, each of the above +! patches must be done with a JSB to an explicit ROM address. As a +! consequence, the patched instruction will generally be longer than the +! instruction which is being replaced. To cleanly affect this +! we must overwrite multiple instructions and incorporate the activities of +! each of the overwritten instructions into the target subroutine. +! Additionally, even without the relocation concerns, numerous places which +! reference these extra routines may be beyond a PC relative displacement +! due to the size of the ROM. +! +! The KA655 ROM size is 128KB. As it turns out, the ROM image is only using +! approximately 105,136 bytes of the total 131072 bytes. We use this unused +! ROM space to store code which implements the required extra functionality +! +Define PATCH_BASE = 00019C00 +Define P$END = PATCH_BASE +Define CP$K_MEMSIZE = 20080148 +Define CP$K_QBMBR = 20080010 +Define DST_BASE = 20140758 +Define CTX_BASE = 201404B2 +Define CTX$L_R2 = 8 +Define CTX$A_GPTR = 66 +Define CTX$L_ERROR_COUNT = 54 +Define CTX$L_ERROR_STATUS = 58 +Define DST$W_BITMAP_LENGTH = 20 +Define DST$A_BITMAP = 1C +Define DST$A_BUSMAP = 24 +Define DST$W_BITMAP_CHECKSUM = 22 +Define CP$CHECKSUM_RTN = 20041C6A +Define GUARD$S_GUARD_BLOCK = 12 +Define GUARD$l_pc = 0 +Define GUARD$a_gptr = 4 +Define GUARD$w_rmask = 8 +Define GUARD$l_save_error_count = 0A +Define GUARD$l_save_error_status = 0E +! +! This routine determines the memory size of the current system. This is +! done by referencing the CMCTL18 memory size register. On an older simulator +! or one with less than 64MB of RAM configured, this register may not exist. +! If it doesn't exist the machine check generated by the reference to the +! non-existant register is caught, and the appropriate memory size is +! determined from the existing PFN Bitmap size. +! +DEFINE GETMEMSIZE_R0 = P$End+1 +Deposit/Instruction GETMEMSIZE_R0 +' pushr #0 ' +' subl2 #guard$s_guard_block, sp' +' movw #0, B^guard$w_rmask (sp)' +' movab B^G$ERR, B^guard$l_pc (sp)' +' movl @#, B^guard$a_gptr (sp)' +' movl @#, B^guard$l_save_error_count (sp)' +' movl @#, B^guard$l_save_error_status (sp)' +' movl sp, @#' +' brb G$RD ' +'G$ERR: movzwl @#,R0' +' clrl @#' +' clrl @#' +' ashl #^d12,r0,r0 ' +' brb G$DON ' +'G$RD: movl @#CP$K_MEMSIZE,R0 ' +'G$DON: movl @#, sp' +' movl B^guard$a_gptr (sp), @#' +' movl B^guard$l_save_error_count (sp), @#' +' movl B^guard$l_save_error_status (sp), @#' +' addl2 #guard$s_guard_block - 2, sp' +' popr (sp)+ ' +'P$End: rsb ' +Exit +! +Define GETMAPENDADDR_R6 = P$End+1 +Deposit/Instruction GETMAPENDADDR_R6 +' MOVZWL @#,R6' +' BNEQ X$1 ' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ashl #-^D12,R0,R6 ' +' MOVL (SP)+, R0 ' +'X$1: addl @#,R6' +'P$End: rsb ' +Exit + +! DE_QDSS_ANY [2004E2A8] Uses R6 for BitMap Size +! 2004E390/ BSBW GETMAPSIZE_R6 +Replace/Instruction 0E390 +' MOVZWL B^20(R9),R6 ' +' ADDL3 R6,B^1C(R9),R6 ' +Exit +' JSB GETMAPENDADDR_R6 ' +Exit +! +! +DEFINE GETMAPSIZEMAPADDR_STACK = P$End+1 +Deposit/Instruction GETMAPSIZEMAPADDR_STACK +' PUSHL @#,' +' MOVL B^4(SP),-(SP) ' +' MOVZWL @#,B^8(SP)' +' BNEQ X$2' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,B^0C(SP) ' +' MOVL (SP)+, R0 ' +'X$2: NOP ' +'P$END: RSB' +Exit + +! CP_FIND [200419E8] Uses (SP) for BitMap Size R1 Scratch +! 20041A16/ BSBW GETMAPSIZE_STACK +Replace/Instruction 01A16 +' MOVZWL B^20(R0),-(SP) ' +' PUSHL B^1C(R0) ' +Exit +' JSB GETMAPSIZEMAPADDR_STACK ' +Exit +! +! CP_SCAN [200459D0] Uses R3 for BitMap Size +DEFINE GETMBMEMSIZE_STACK = P$End+1 +Deposit/Instruction GETMBMEMSIZE_STACK +' MOVAB L^0000AACF,-(SP) ' +' MOVL B^4(SP),-(SP) ' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D20,R0,B^0C(SP) ' +' MOVL (SP)+, R0 ' +' RSB ' +'GETMAPSIZE_R3: MOVZWL @#,R3' +' BNEQ X$3' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,R3 ' +' MOVL (SP)+, R0 ' +'X$3: RSB' +'P$END: NOP' +Exit +! 20045B05/ BSBW GETMBMEMSIZE_STACK +Replace/Instruction 05B05 +' MOVL R8,-(SP) ' +' MOVAB L^0000AACF,-(SP) ' +Exit +' JSB GETMBMEMSIZE_STACK ' +Exit +! 20045B80/ BSBW GETMAPSIZE_R3 +Replace/Instruction 05B80 +' MOVZWL @#20140778,R3 ' +Exit +' JSB GETMAPSIZE_R3 ' +Exit +! DE_CQBIC_MEMORY [2004D7B2] +DEFINE GETBITMAPPAGES_R5 = P$End+1 +Deposit/Instruction GETBITMAPPAGES_R5 +' MOVZWL @#,R5' +' BNEQ X$4 ' +' MOVL R0,-(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,R5 ' +' MOVL (SP)+,R0 ' +'X$4: ASHL #3,R5,R5 ' +' RSB ' +'GETBITMAPMEMSIZE_R3: MOVZWL @#,R3' +' BNEQ X$5 ' +' MOVL R0,-(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,R3 ' +' MOVL (SP)+,R0 ' +'X$5: ASHL #^D12,R3,R3 ' +'P$END: RSB' +Exit +! 2004D8A5/ BSBW GETMAPSIZE_R5 +Replace/Instruction 0D8A5 +' MOVZWL B^20(R9),R5 ' +' ASHL #03,R5,R5 ' +Exit +' JSB GETBITMAPPAGES_R5 ' +Exit +! 2004DA41/ BSBW GETMAPSIZE_R5 +Replace/Instruction 0DA41 +' MOVZWL B^20(R9),R5 ' +' ASHL #03,R5,R5 ' +Exit +' JSB GETBITMAPPAGES_R5 ' +Exit +! 2004DA8C/ BSBW GETMAPSIZE_R5 +Replace/Instruction 0DA8C +' MOVZWL B^20(R9),R5 ' +' ASHL #03,R5,R5 ' +Exit +' JSB GETBITMAPPAGES_R5 ' +Exit +! DE_CACHE_MEM_CQBIC [2004EBF0] +! 2004ECD0/ BSBW GETMAPSIZE_R3 +Replace/Instruction 0ECD0 +' MOVZWL B^20(R9),R3 ' +' ASHL #0C,R3,R3 ' +Exit +' JSB GETBITMAPMEMSIZE_R3 ' +Exit +! CP_BOOTSTRAP +DEFINE GET_X_PFNMAP_SIZEADDR_STACK = P$End+1 +Deposit/Instruction GET_X_PFNMAP_SIZEADDR_STACK +' movl B^dst$a_bitmap (r11), r2' +' movzwl B^dst$w_bitmap_length (r11), r1' +' bneq X$20 ' ! Zero Bitmap size means extended mem +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +'X$10: brw X$70 ' ! already fixed +'X$20: cmpl r1, #^D16384 ' ! Original Map of 64MB? +' blss X$10 ' ! Too Small For Extended +' JSB GETMEMSIZE_R0 ' +' ashl #-^D12, R0, r1 ' ! Map Size = MEMSIZE/512/8 +' cmpl r1, #^D16384 ' +' beql X$10 ' ! Normal 64MB map +!; +!; First move the Console Scratch Area (16KB), and the Qbus Map (32KB) +!; to the end of physical memory. +!; +' movl @#CP$K_MEMSIZE, r1 ' ! MEMSIZE +' subl3 #^D48*^D1024, r1, r3 ' ! New Destination Address +' addl #^D16384, r2 ' ! Point at end of prior Map +' clrl r0 ' ! Index +'X$63: movb (r2)[r0], (r3)[r0] ' ! Move the Console Scratch Pad and QBMRs +' aoblss #^D48*^D1024, r0, X$63 ' +' movab W^4000(r3), B^DST$A_BUSMAP(r11)' ! Save Qbus Map Register Space +' movab W^4000(r3), @#CP$K_QBMBR' ! Save Qbus Map Register Space +!; +!; Fixup the boot device descriptor previously saved in the scratchpad RAM +!; +' subl3 #^D512, B^DST$A_BUSMAP (r11), r1' +' movab B^8(r1), B^4(r1)' +!; +!; Now we build a new bitmap, with all bits set except for the reserved +!; area containing the bitmap itself, and the console scratch area and +!; the Qbus Map. +!; +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +' subl3 r1, r3, r2 ' ! Point at new Destination Address +' movl r2, B^dst$a_bitmap (r11)' ! Save Bitmap address +' ashl #-9, @#CP$K_MEMSIZE, r1 ' ! PFN count = MEMSIZE/512 +' ashl #-^D12, @#CP$K_MEMSIZE, r0' ! Map Size = MEMSIZE/512/8 +' addl #^D48*^D1024+^D511, r0 ' ! Plus other reserved page size +' ashl #-9, r0, r0 ' +' subl r0, r1 ' ! Adjust for bitmap of reserved pages +' clrl r0 ' +' pushl r1 ' ! Save total Bit Count +' ashl #-5, r1, r1 ' ! Convert limit to Longword Count +'X$632: movl #-1,(r2)[r0] ' ! Set bitmap entry for 32 pages +' aoblss r1, r0, X$632 ' +' ashl #5, r0, r0 ' ! Convert back to Bit Count +' movl (SP)+, r1 ' ! Restore total Bit Count +' cmpl r0, r1' +' bgeq X$651' +'X$64: bbss r0, (r2), X$65 ' ! Set page bitmap entry +'X$65: aoblss r1, r0, X$64 ' ! Done ? +'X$651: ashl #-9, @#CP$K_MEMSIZE, r1 ' ! PFN count = MEMSIZE/512 +'X$66: bbcc r0, (r2), X$67 ' ! Clear reserved page bitmap entry +'X$67: aoblss r1, r0, X$66 ' ! Done? +' clrl r0 ' ! Zero Initial checksum value +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +' jsb @#cp$checksum_rtn ' ! Compute checksum for revised bitmap +' movw r0, B^dst$w_bitmap_checksum (r11)' ! save it +' clrw B^dst$w_bitmap_length (r11)' ! Mark as extended bitmap +' ashl #-^D12, @#CP$K_MEMSIZE, r1' ! Map Size = MEMSIZE/512/8 +' movl B^dst$a_bitmap (r11), r2' +'X$70: jmp GETMAPSIZEMAPADDR_STACK' +! +'GETMAPSIZE_CTX_R2: movzwl @#,@#' +' bneq X$71' +' MOVL R0, -(SP) ' +' JSB GETMEMSIZE_R0 ' +' ASHL #-^D12,R0,@#' +' MOVL (SP)+, R0 ' +'X$71: rsb' +Exit +Replace/Instruction 517F = 'movzwl B^20(r11), @#201404BA' +' jsb GETMAPSIZE_CTX_R2 ' +Exit +Replace/Instruction 514B +' MOVZWL B^20(R11),-(SP) ' +' PUSHL B^1C(R11) ' +Exit +' JSB GET_X_PFNMAP_SIZEADDR_STACK' +Exit +! +! DE_MEMORY [200512AC] +! CP_UTIL [] +! +! Identify the Extended Memory Mode in the Console Banner +! (i.e. KA655X-B vs KA655-B) +! +Replace 83D8 = 00303436 +58353536 +Exit +Replace/Byte 83DC = 4B +0 +Exit +Deposit/Instruction 1C04 +' PUSHAB L^000083E2 ' +' JSB GETMEMSIZE_R0 ' +' CMPL R0, #<^D64*^D1024*^D1024>' +' BLEQ B$1 ' +' MOVAB L^000083D6,(SP) ' +'B$1: NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +' NOP ' +Exit +! +! Extended Memory Patches: +! 9. Test 80 [2004d7de] - CQBIC memory - bypass last 2 subtests, run from ROM +! MP Revised to bypass tests starting at test of NXM reference through MAP +! 2004db2e/ brw 2004dd8a +Replace/Instruction 0db2e = 'MOVB #17,B^76(R9)' +'BRW 0DD8A' +EXIT +! +! Interval Timer Patch +! In operational environments, Test 82 subtests 17 and 20 have been observed +! to ocaisionally fail. Disable the timer portion of the interval timer tests. +! 2004e7c1/ brw 2004e870 +Replace/Instruction 0e7c1 = 'MOVB #10,B^76(R9)' +'BRW 0e870' +EXIT +! +UPDATE +EXIT +$ diff --git a/VAX/ka655x.bin b/VAX/ka655x.bin new file mode 100644 index 0000000000000000000000000000000000000000..ade93ab765eb01b498dd1a0bc326ea420a65b745 GIT binary patch literal 131072 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 literal 0 HcmV?d00001 diff --git a/VAX/ka655x.bin_old b/VAX/ka655x.bin_old new file mode 100644 index 0000000000000000000000000000000000000000..d15f41da67768f03398b6973a1c0bb948f369d49 GIT binary patch literal 131072 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%0ADM$fE1er-d1V>H582`6Kd#gKp%-#q6ILGK8(yT zm`ldj8Zb2td=QICbE&cP?XkMkqh)kI6E&nnnE)SYQBg1$NAe0ao#SkeRoi2Q9fG$r zyqQ1a?WXHj!^zTzU3;w5T3}{(9}k~{84Wpo@^_^C_P zrt4QDXmZz{6ly0(|FzVH?meP(4KlVayYaS4E0gBA zbVMFwAE2)Iu_uh_2aMJ7J~(fVb0wT_g3Jr&6aEEPy&pD-rbZMI!!K%|rzXl8UyN7t zvHaC&vRnGixWb;5pF$0`N0^8sSWJz4a5@oAWgt*HL_GK^VAi_pUV(W|O~la1J0)?z zGhizgc!Pw|2qkoP?S=@7Mw=OoAJn~K+CmFlHK!&j9VX>Svf|Zhf=CQ@wJFJnPK}mk z&%4EdAaJ^OO45LTfK;39{g(ouQ=iO2!4ASG_nOW?O=iMNH36*cz7(kO!_2HYKaS=0un&31mT3oiC}mHU1;`K}7$QL~7)&OBe!7Or)0Wn!+$rf_hPNrkGt) zj6`AXVH5k!5FD>#odLc^hc09)j=-G&g){Pt#u!9(Fu!3#>o;9QGSAUx57d4a{S5=P zbYt+E0sGXu*fWC0kU@3QGgcxclG1NRH00RBGBEnn0j6i<33WM2xCz<%ivtdBuXU7` z;mhjGY??Bp;LOH&fs|Y73F4wGMSqUq(5J{t zSB_-HCUZ93>pdpw6Z7R7eRB63vCH4XvU|d(@jRO=KA2JL9%@1#E~)I>_pGPwZXY!w z?QZ2bTh2#)7!YU**<1N2k1VUx&zMxvO6}|P!+Ge`>E zY8K3Q78A*VMnq=U*8-OUpZWSuXD*%CA!0FBAAj-OKA*~gH9oRKaHO{ z-JU7qFz9tFr?8b#u#^iBzi}~9eO{Zg{PKIErn493lAK{0s`JREm+f8qu)=~=YtyLLxsZ=tcn>A zp?IeW^zJ+qu9KzDs6*H;+drm0peH;OLqYNs$*&;7KNvWdJ8yrj`z69PSf>`m@qcTn@}7Q3+;qEmvtQ`t30m>L_zj@TU{ zH8jmfJ%aR4YE*Eo>Xbdwd+<~^?(qzG_uh_Sk@Og~GUq=TB0}N zUGus3n+r10GpH5$=#?_Su*zhX&Qe39UjFPM;!OeA0{F%x@4gKt!9if#}(c|9TRC z!1VtQW+NRf{NWijO2T(~CX6IrPglMKl^9d`2c6N7R zgcK9lIYsA9Gx?8r-5&mCi=ItNefQm6cXwF5$KCE@ihIESS6Ui7Gr|3iUVZT-M#Lf; zwpQ<>7M4j?=T47LN*|U+myyQnV6RyqYqb7uyW>@ZM#@VJ@pcD!0pICJHERE3UCOXb zBhq)VL`2f-VtAz8Rvv5jV62aLEWF08YOZNY!Y|ZZ*P(AolSE+DcCI|-yjH+^Ic33R z^9N=~L-0Z%o8(~k<}Tw!_!Rl{gj-QF*?7bwNPU{wi(Mf~kCLsK{b=7%Zf*_!K|$Y1 zukl*VL^F2#q-nIAJB%!y^mhBC6j1e~NG7$+d7_6*;lvWsjF(Vv1=DH#54n&jTTh#p z^-}%)FuSE*URH!bacLgt>{OjJAhlw!c;p*W^E?0yJY26r=NIZaA1#|2wWo{In}cK$AghDRxBseyhqHvugheK87R(OwYMYD|OUHjfOJ z4X)Bzb)#{>R5OO8eCU=QEq*5!BTngL`TyYdrv;u;`7%0?%rYq=&yMnH!yGsDrI71+-kRI@@4Ngi`KVzJmDHR@c>g&XU zS`-~6%I;RTAeB%La(aw>74;rxlH|NIR{3(n_|}0c-tRu>EDWd4fYCjk+_68!VwBd? zvfG~Ux$?fNkWu&278c$6i=*#>yMvhd(O7p_r0t0G^g(BF^c6g(Xn(`LtIyqU*^)n3 z3(c+vn5!&C{STEPJ^Bnn!>kO88NJp8a*u4I4rxTGN)0x^{9Njx28(nlbxNm`<>}PL z6o&5#@j{@n;BC65ic2IBabZJ|^vqM4YZp~Tk6K_u!PH#KAja)($d*$EZ@^@}g*-}p zzJ4qtuuv)M5$0h2S4IaZIPk|TCL6CeM)igwGyM=XI`2+*x4>w@!uq0HQAZf!4-E-0 zCBzJ6JPd`oH2=7?BNZkpHAk>dePawNnQ&_9j?83^Ie_VA*$P2_c`!7N*g}Y%wDGXv=BSp>wYv~S0~$pQFKtB1X5#r@+4YEJWZ@dglPGJTynW=a%pE1z86hLit*(5 zS!)h_e%{fHgDdMBc=FsO*GNz72$Myt+?20R~73**e$#t-rsO~xDo^Ki5;ta#W3iy<6(H!TcKU9X~wxKo!y zDPF)U7X)})F*?dC0e~H6RKr7ch=zveZuA_2hfhF)&jhz`-o9D?9q>eH{KlUeA<^gQ zlQ0@@-2_cJ;Q7ROM76jn=WliG9PVT)lN@LSgb5u+ zct%*1nZjBS4t?SS3`4j9xtlwi{*g*;jMuD6L|HSSd}G9-F<+Q`rSKBdS6n@vUPU@W z(%w$REUDPYCq3Hf6{L1vjY24c-8!Ng@ewJeYhu2Q0)ix%yb%>q;0OBm^EqMx$}#!eg$I#I3bE zVW8r(fFgJf<#V~U)9KovF)52n3yueR7;+GoQn_g0C!mM1kAsqcZH3-Ih6w`s`|+6< z38rt~d-~o*eFmr91Aixdrp13bA9Fh4X#O4-TX=)q#pL*^9VY83W*LSoZkluVGpiX* z7e3}x&Q!soi98WYV#vu}!dk{m=5K6TyABKCq@WyFqGbDoEcUWc&E64;*vCQw=MfW` zuZmeJSx3*&HJ>{1^eAbW9>&dqJ!~%b_S!}LsEt{79n9|$3b1cB%+6&x$OGZKOh=$= zc~?W%Bjbt{Vt^MM8gX!a?Ue{N!Invh4}S#-y?&Sk%BT z_)Cw*v{Iv`|qh;@)AKd4^zxR4mR_$csRFk-Hz!nu*+x3;2!+rOy zyYNcaS%`QNW+pzk93s)Mu>)I{`gU2dm)OjMQ5d3G4kyAlGK^!Nc9B;jbcv$5?>g!J z%pre?GKXSgQhLG;2M$N2V0g@fnc~6Vbb-OX{278WIA$19q6}H65n_fj1~}#ng&Og) zF(I^py@zuxr|NdMJj19+7B#2E%jQ~=%?0{U1joj}N-Z#R>CpW-e1^qbt()KYa}2nQ zzyHPpS0Tc5NcJ}^oGV%!YW`6FryJR3iu?^%bNmPV7<2^ohtI_VJ*gNt&(Iw0bW*?O zuOVU$EX0-2sK*lJXwo=&@3}L3&mEkUd;~*OO|H}2L0EKP604c`PNzRG@fg<;SeZFM zOEO%%ijmAzJynHLqS%|{Iz&}Em70kkp~@dN!;Xh&)XJUjn5jv3tJTuqsWo@Y(qHhD zSoQ0i;?8@(t7qXQ4#At>;B7Mej;H`!Yb^71={MBTLL2@Y^7QH~2PVF1BpNS2rU{-C zi5`2xu|!XY)pJzzkh*m?k0PlI6NjD|wRA3wik`5HvSP-eXGJ~mG9RWOM%GZo&Kfg% zu>&URP@UtGG|WVtauOA=nfHx8zx2Zn5ycx;7_u<%^nXC+1Z|npwG0(xWP%Lz)HP)p z84;W>TX~}2S}D;f+=zfg$99mTG&bP5Dq^S~T(5{h*q53#FRDE!a3Jt6$_%rE^Uh?w z%m;Y=8wF1LFA7Xvb;An`gP?Pn2?c&hAoGJWN9V1U^R|Laft}#39=l71s*c=0cxkWv zKa~__24_zqFR9HaDVZCnq(^y4;XSy3N{VX{m9!Z1YHB#tR0OQ8jI3cQqw#|9`u!?a zvrUEKncD@k-(agE!v$vz3uVu}0fjxHx;6i@;f2ROV+0`+gtK0N_c3|BVU~pTOV}BJ z&6Q@IfFGY^cEV$WtO(Q;$`h&9(#aS=Wf?tKYwM{Dn_wc<8$VtpuxQi)0oDL z!l~?*wpBFCUKLrT7I$x@>9wvU;P7gpK^nKt;)=Yubr$H5Dsj53G$%(;P4SRY@pO?( z8(e^$qd6PpoQ?4(P)Q=3TqNGGzvx04=)$&mdyF|Kq|zrtBxq@@g{ ztKU0>R8qQRC&qI#a{$r*{u`zHPbl3t;7y|D;5S#z#jjI!rbmDNI(5C|uTG;0r#104 zQP+jA?x2w!5C81)pIu%P<6l#>)ARuZLot>$(M zhA~oFl!-MmsmW-DsFnp<1F|&jxYs-2`7JrK4=C!!@VT0rZm+ac*L!O2#4V`Y`J%xS zh5*jB2?kHp{~u@f;yK{urYi-fHh09C1XVI*4)TZ z)&f4#k(=D10pB-BbgLOYI)Q+}@Nopw!$4h<>#kPkIWhX9qJh&Mp(FBj-D&PP}MV29?8oaZk{R*4!1za}m{YnNmeNj5e6AVn}86aa#AK!2@&!%O2ztYp!jm4Mna%1a>uhkc> zL$iTL$E7HvS|CWDp~g>Q6hzk1pTyNfE?7BZ0vC8m08EZth8F+>rZ7GR#ArtLRc6dS z@x$REE=Pzug_>1l7NUP^!UWBUhN;Nds^y{<$|UK61dgHM%wxk+;nDLD{r4tJyJnL_ z7zy0@YepT#@eAT>ynm=-|8M&To5|=Oa5xGTx`59-F$aVGmpv6?RTBqA*@HD0lJ{_l*T$chQHX^tg z$q*ZG@S!9qQ=W|3&gs%HI`&yXKHZ8P*#WISG|6JTGsd(7V;rZI88l0L7Q){Fc~dz0 z<`d-BCy+NPdXLnJpNt$7ynNpIM(n>H6rfR=LPadY`~!UUh_4ODr(%3j7{d>@Xqyr=UUZ6!>~^oClf{k~Qs1=j{Sz z%NLK=3}$+bZVZ&AIW)mR(va59rSrdo z;X|5h%Pv$Mo{cF`s&KW*<*qho;A$hCROm7aL9pw^*(KZT(AIdL;#_CGeH^3n6c|Nl zAMG97-dl28uxW`yp)V5hea!j@#@O65ncg(KI3e<;mUWv-k+BO=?&sDE?%YO830=>V z8O;I5etOf=Fg3x?u)bliw;&@Ww^)KgMoLY$D#GxXiWqU@qPyP>Se~w zWwwk~^>X9pa@(j@brxbqc6;rY zoHdEEMVe0SiG>3Fo^&`<27>V+dE)7d8=;EWJKmEX(lEYPKNXe7TDAiCsS{7I#a&fHkSI5GnsN0btPxhMR+QW=TeKO`3&_c-5&IGy9JAL zb++pob27ek02RUi$OCZ@2Sqfy;~t4LI2M@c2Hj*D4>Zg_G47s74qm!*BKGmDt-Wm( z9TJDVGfC)f(bN@tYNsMj7)8skb_-UjCYo{LrWVGs0?znI#p=;Fs4nckyAm%wDiaHW zXkbH+-SikuN%7dn@)IiG@oY4CJRKg4Rh843inSkmRn&Z2oe`Y|vPD>d$L?2K^)Z+X z0J^)Rh|xA2aw&8)-J<3|7f`L#oXK3C2-z){?UOo0ayjI+@!?7NEJEeV6aJ*=F|0)7$9*;*C*LyeIby=_*#ag_9!vLD-FL+K#dLBaoO0>mtU~n)h2xXy zbP>;Kk1}4DF6rmkJj403_fXbB5soGBxSe{yOD{EivNf*{mXyp?jI?!hibeCfZBO^J z6j9ATEb1?=+e1##hW=1E?T*DbjnQMjs6)iWO|OZ++b)WVCTv82FBiR^`G-X8#X%*z zy*eLW=VtKin_}(s`X-ujU{|Ntuzh$BWucSzJcq@Jgahgo#%p*cGdKz*g1;3dcSZN+ zGYRgG4<%3;lr9;aFb=ASx!q6R#rM9xmnxf{l?UAY`(<^Sg(|*P-4Ol`R>rApn2Zgn z^M)`3J539*)3neMA2pO3dwI+D^Qy!~IPM2QelwMtS`F*lHnpUmCBP@)%w)ddSVSLP zayUbCc(lDKUW|@E^qPO6mVh&|KYe-#9Yl#K6RkfH60wci{=cbv6M(3$>wo;dnK#2Q zvdn@CE;#N)iFu=9j3E)X#H1#9@6E!9O9c0&YL=#^6#|;JX_^YOCheE_9T7#u0m0OC zQQI$Q7MrAb4+AFOCL$!UO0z(s#0A3qKj+O1;_~gkzyA7Pexr}~-o5wT<=k`6J@?#m z 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%0DD77vd3K}1vGcJCPIc3a}oE8L=g7PfMO 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 as high word. +5. UBA: DMA addresses must be masked to Unibus width (18b). +6. HK: thread used multiple times if SEEK is followed by NOP or DCLR. +7. HK: only DCLR clears ATA. +8. MEM: MS780E size declaration off-by-1. +9. MEM: MS780E array start off by >> 4. +10. MEM: CSR-C register write logic incorrect. +11. CIS: CMPP3/CMPP4 using wrong arguments to ReadDstr. +12. CPU, OCTA: CVTfi with integer overflow not setting trap if PSW = 1. +13. STDDEV: read of ICR was missing the call parameter. +14. ACBD/G: testing wrong operand register to get limit sign. +15. CPU: faults not clearing PSL. +16. ADAWI: register mode implemented incorrectly. +17. MOVTC: condition codes not preserved through page fault. +18. MOVTUC: condition codes not preserved through page fault. +19. MOVTUC: escape tested against untranslated rather than translated character. +20. CVTPT: condition code and decimal overflow calculation incorrect. +21. CVTPS: condition code and decimal overflow calculation incorrect. +22. CVTPL: if destination is register, result is stored after register updates. +23. CVTPL: integer overflow set rather than . +24. all decimal string: 11/780 does not validate characters in decimal strings. +25. EDITPC: condition codes not preserved through page fault. +26. EDITPC EO$INSERT: inserts sign instead of fill. +27. EDITPC EO$BLANK_ZERO: address off by one. +28. EDITPC EO$BLANK_ZERO: not testing for set. +29. EDITPC EO$LOAD_PLUS: not skipping character if test fails. +30. EDITPC EO$LOAD_MINUS: not skipping character if test fails. +31. Compatibility mode: SXT not implemented. +32. Compatibility mode: XOR operands fetched in wrong order. +33. MNEGH: condition codes set from original sign. +34. MNEGH: not cleared. +35. H_floating quad precision integer routines (add, inc, neg): carry propagation incorrect. +36. H_floating packup routines: test for zero used exponent not fraction. +37. MULH: carries out of floating accumulator lost. +38. DIVH: stores wrong operand as result. +39. POLYF/D/G: truncation after add not needed. +40. POLYF/D/G: early SRM requires truncation to 31b/63b, not 32b/64b. +41. POLYF/D/G/H: exits too early if argument is zero. +42. POLYD/G/H: calculates address of residual pointer result incorrectly. +43. POLYD/G: performs single precision rather than double precision multiply. +44. POLYH: fails to truncate intermediate result from 128b to 127b. +45. POLYF/D/G/H: internal add routine must test fraction rather than exponent to + detect zero, POLYx can create "denormalized" intermediate result. +46. EMODH: concatenate 16b of extension operand instead of 15b. +47. Specifier flows: modify flows testing for read access rather than write access. +48. Quad/octa writes: wrong address reported on faulting cross-page writes. +49. Memory management: 11/780 implements access control test on first level PTE's. +50. LDPCTX: 11/780 implements mbz tests on PCB fields. +51. LDPCTX/MTPR: 11/780 validity checks PCBB, SCBB, SBR, SLR, P0BR, P0LR, P1BR, P1LR. +52. TMR: tmr_inc not updated in standard (100Hz) mode. +53. MTPR SBR/PCBB/SCBB: 11/780 only checks that bits<1:0> == 0. +54. MTPR xLR: 11/780 excludes bits<31:24> from mbz test. +55. MTPR PxBR: 11/780 only checks bits<31,1:0> == 1,00. + + + + + + + + diff --git a/VAX/vax780_defs.h b/VAX/vax780_defs.h new file mode 100644 index 0000000..87d4dd7 --- /dev/null +++ b/VAX/vax780_defs.h @@ -0,0 +1,453 @@ + +/* vax780_defs.h: VAX 780 model-specific definitions file + + Copyright (c) 2004-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 29-Apr-07 RMS Modified model-specific reserved operand check macros + to reflect 780 microcode patches (found by Naoki Hamada) + 29-Oct-06 RMS Added clock coscheduler function + 17-May-06 RMS Added CR11/CD11 support (from John Dundas) + 10-May-06 RMS Added model-specific reserved operand check macros + + This file covers the VAX 11/780, the first VAX. + + System memory map + + 0000 0000 - 1FFF FFFF main memory + + 2000 0000 - 2001 FFFF nexus register space + 2002 0000 - 200F FFFF reserved + 2010 0000 - 2013 FFFF Unibus address space, Unibus 0 + 2014 0000 - 2017 FFFF Unibus address space, Unibus 1 + 2018 0000 - 201B FFFF Unibus address space, Unibus 2 + 201C 0000 - 201F FFFF Unibus address space, Unibus 3 + 2020 0000 - 3FFF FFFF reserved +*/ + +#ifndef FULL_VAX +#define FULL_VAX 1 +#endif + +#ifndef _VAX_780_DEFS_H_ +#define _VAX_780_DEFS_H_ 1 + +/* Microcode constructs */ + +#define VAX780_SID (1 << 24) /* system ID */ +#define VAX780_ECO (7 << 19) /* ucode revision */ +#define VAX780_PLANT (0 << 12) /* plant (Salem NH) */ +#define VAX780_SN (1234) +#define CON_HLTPIN 0x0200 /* external CPU halt */ +#define CON_HLTINS 0x0600 /* HALT instruction */ +#define MCHK_RD_F 0x00 /* read fault */ +#define MCHK_RD_A 0xF4 /* read abort */ +#define MCHK_IBUF 0x0D /* read istream */ +#define VER_FPLA 0x0C /* FPLA version */ +#define VER_WCSP (VER_FPLA) /* WCS primary version */ +#define VER_WCSS 0x12 /* WCS secondary version */ +#define VER_PCS ((VER_WCSS >> 4) & 0x3) /* PCS version */ + +/* Interrupts */ + +#define IPL_HMAX 0x17 /* highest hwre level */ +#define IPL_HMIN 0x14 /* lowest hwre level */ +#define IPL_HLVL (IPL_HMAX - IPL_HMIN + 1) /* # hardware levels */ +#define IPL_SMAX 0xF /* highest swre level */ + +/* Nexus constants */ + +#define NEXUS_NUM 16 /* number of nexus */ +#define MCTL_NUM 2 /* number of mem ctrl */ +#define MBA_NUM 2 /* number of MBA's */ +#define TR_MCTL0 1 /* nexus assignments */ +#define TR_MCTL1 2 +#define TR_UBA 3 +#define TR_MBA0 8 +#define TR_MBA1 9 +#define NEXUS_HLVL (IPL_HMAX - IPL_HMIN + 1) +#define SCB_NEXUS 0x100 /* nexus intr base */ +#define SBI_FAULTS 0xFC000000 /* SBI fault flags */ + +/* Internal I/O interrupts - relative except for clock and console */ + +#define IPL_CLKINT 0x18 /* clock IPL */ +#define IPL_TTINT 0x14 /* console IPL */ + +#define IPL_MCTL0 (0x15 - IPL_HMIN) +#define IPL_MCTL1 (0x15 - IPL_HMIN) +#define IPL_UBA (0x15 - IPL_HMIN) +#define IPL_MBA0 (0x15 - IPL_HMIN) +#define IPL_MBA1 (0x15 - IPL_HMIN) + +/* Nexus interrupt macros */ + +#define SET_NEXUS_INT(dv) nexus_req[IPL_##dv] |= (1 << TR_##dv) +#define CLR_NEXUS_INT(dv) nexus_req[IPL_##dv] &= ~(1 << TR_##dv) + +/* Machine specific IPRs */ + +#define MT_ACCS 40 /* FPA control */ +#define MT_ACCR 41 /* FPA maint */ +#define MT_WCSA 44 /* WCS address */ +#define MT_WCSD 45 /* WCS data */ +#define MT_SBIFS 48 /* SBI fault status */ +#define MT_SBIS 49 /* SBI silo */ +#define MT_SBISC 50 /* SBI silo comparator */ +#define MT_SBIMT 51 /* SBI maint */ +#define MT_SBIER 52 /* SBI error */ +#define MT_SBITA 53 /* SBI timeout addr */ +#define MT_SBIQC 54 /* SBI timeout clear */ +#define MT_MBRK 60 /* microbreak */ + +/* Machine specific reserved operand tests */ + +/* 780 microcode patch 37 - only test LR<23:0> for appropriate length */ + +#define ML_LR_TEST(r) if ((uint32)((r) & 0xFFFFFF) > 0x200000) RSVD_OPND_FAULT + +/* 780 microcode patch 38 - only test PxBR<31>=1 and xBR<1:0> = 0 */ + +#define ML_PXBR_TEST(r) if ((((r) & 0x80000000) == 0) || \ + ((r) & 0x00000003)) RSVD_OPND_FAULT +#define ML_SBR_TEST(r) if ((r) & 0x00000003) RSVD_OPND_FAULT + +/* 780 microcode patch 78 - only test xCBB<1:0> = 0 */ + +#define ML_PA_TEST(r) if ((r) & 0x00000003) RSVD_OPND_FAULT + +#define LP_AST_TEST(r) if ((r) > AST_MAX) RSVD_OPND_FAULT +#define LP_MBZ84_TEST(r) if ((r) & 0xF8C00000) RSVD_OPND_FAULT +#define LP_MBZ92_TEST(r) if ((r) & 0x7FC00000) RSVD_OPND_FAULT + +/* Memory */ + +#define MAXMEMWIDTH 23 /* max mem, MS780C */ +#define MAXMEMSIZE (1 << MAXMEMWIDTH) +#define MAXMEMWIDTH_X 27 /* max mem, MS780E */ +#define MAXMEMSIZE_X (1 << MAXMEMWIDTH_X) +#define INITMEMSIZE (1 << MAXMEMWIDTH) /* initial memory size */ +#define MEMSIZE (cpu_unit.capac) +#define ADDR_IS_MEM(x) (((uint32) (x)) < MEMSIZE) + +/* Unibus I/O registers */ + +#define UBADDRWIDTH 18 /* Unibus addr width */ +#define UBADDRSIZE (1u << UBADDRWIDTH) /* Unibus addr length */ +#define UBADDRMASK (UBADDRSIZE - 1) /* Unibus addr mask */ +#define IOPAGEAWIDTH 13 /* IO addr width */ +#define IOPAGESIZE (1u << IOPAGEAWIDTH) /* IO page length */ +#define IOPAGEMASK (IOPAGESIZE - 1) /* IO addr mask */ +#define UBADDRBASE 0x20100000 /* Unibus addr base */ +#define IOPAGEBASE 0x2013E000 /* IO page base */ +#define ADDR_IS_IO(x) ((((uint32) (x)) >= UBADDRBASE) && \ + (((uint32) (x)) < (UBADDRBASE + UBADDRSIZE))) +#define ADDR_IS_IOP(x) (((uint32) (x)) >= IOPAGEBASE) + +/* Nexus register space */ + +#define REGAWIDTH 17 /* REG addr width */ +#define REG_V_NEXUS 13 /* nexus number */ +#define REG_M_NEXUS 0xF +#define REG_V_OFS 2 /* register number */ +#define REG_M_OFS 0x7FF +#define REGSIZE (1u << REGAWIDTH) /* REG length */ +#define REGBASE 0x20000000 /* REG addr base */ +#define ADDR_IS_REG(x) ((((uint32) (x)) >= REGBASE) && \ + (((uint32) (x)) < (REGBASE + REGSIZE))) +#define NEXUS_GETNEX(x) (((x) >> REG_V_NEXUS) & REG_M_NEXUS) +#define NEXUS_GETOFS(x) (((x) >> REG_V_OFS) & REG_M_OFS) + +/* ROM address space in memory controllers */ + +#define ROMAWIDTH 12 /* ROM addr width */ +#define ROMSIZE (1u << ROMAWIDTH) /* ROM size */ +#define ROM0BASE (REGBASE + (TR_MCTL0 << REG_V_NEXUS) + 0x1000) +#define ROM1BASE (REGBASE + (TR_MCTL1 << REG_V_NEXUS) + 0x1000) +#define ADDR_IS_ROM0(x) ((((uint32) (x)) >= ROM0BASE) && \ + (((uint32) (x)) < (ROM0BASE + ROMSIZE))) +#define ADDR_IS_ROM1(x) ((((uint32) (x)) >= ROM1BASE) && \ + (((uint32) (x)) < (ROM1BASE + ROMSIZE))) +#define ADDR_IS_ROM(x) (ADDR_IS_ROM0 (x) || ADDR_IS_ROM1 (x)) + +/* Other address spaces */ + +#define ADDR_IS_CDG(x) (0) +#define ADDR_IS_NVR(x) (0) + +/* Unibus I/O modes */ + +#define READ 0 /* PDP-11 compatibility */ +#define WRITE (L_WORD) +#define WRITEB (L_BYTE) + +/* Common CSI flags */ + +#define CSR_V_GO 0 /* go */ +#define CSR_V_IE 6 /* interrupt enable */ +#define CSR_V_DONE 7 /* done */ +#define CSR_V_BUSY 11 /* busy */ +#define CSR_V_ERR 15 /* error */ +#define CSR_GO (1u << CSR_V_GO) +#define CSR_IE (1u << CSR_V_IE) +#define CSR_DONE (1u << CSR_V_DONE) +#define CSR_BUSY (1u << CSR_V_BUSY) +#define CSR_ERR (1u << CSR_V_ERR) + +/* Timers */ + +#define TMR_CLK 0 /* 100Hz clock */ + +/* I/O system definitions */ + +#define DZ_MUXES 4 /* max # of DZV muxes */ +#define DZ_LINES 8 /* lines per DZV mux */ +#define VH_MUXES 4 /* max # of DHQ muxes */ +#define MT_MAXFR (1 << 16) /* magtape max rec */ + +#define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ +#define DEV_V_MBUS (DEV_V_UF + 1) /* Massbus */ +#define DEV_V_NEXUS (DEV_V_UF + 2) /* Nexus */ +#define DEV_V_FLTA (DEV_V_UF + 3) /* flt addr */ +#define DEV_V_FFUF (DEV_V_UF + 4) /* first free flag */ +#define DEV_UBUS (1u << DEV_V_UBUS) +#define DEV_MBUS (1u << DEV_V_MBUS) +#define DEV_NEXUS (1u << DEV_V_NEXUS) +#define DEV_FLTA (1u << DEV_V_FLTA) +#define DEV_QBUS (0) +#define DEV_Q18 (0) + +#define UNIBUS TRUE /* Unibus only */ + +#define DEV_RDX 16 /* default device radix */ + +/* Device information block + + For Massbus devices, + ba = Massbus number + lnt = Massbus ctrl type + ack[0] = abort routine + + For Nexus devices, + ba = Nexus number + lnt = number of consecutive nexi */ + +#define VEC_DEVMAX 4 /* max device vec */ + +typedef struct { + uint32 ba; /* base addr */ + uint32 lnt; /* length */ + t_stat (*rd)(int32 *dat, int32 ad, int32 md); + t_stat (*wr)(int32 dat, int32 ad, int32 md); + int32 vnum; /* vectors: number */ + int32 vloc; /* locator */ + int32 vec; /* value */ + int32 (*ack[VEC_DEVMAX])(void); /* ack routine */ + } DIB; + +/* Unibus I/O page layout - XUB,RQB,RQC,RQD float based on number of DZ's + Massbus devices (RP, TU) do not appear in the Unibus IO page */ + +#define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */ +#define IOLN_DZ 010 +#define IOBA_XUB (IOPAGEBASE + 000330 + (020 * (DZ_MUXES / 2))) +#define IOLN_XUB 010 +#define IOBA_RQB (IOPAGEBASE + 000334 + (020 * (DZ_MUXES / 2))) +#define IOLN_RQB 004 +#define IOBA_RQC (IOPAGEBASE + IOBA_RQB + IOLN_RQB) +#define IOLN_RQC 004 +#define IOBA_RQD (IOPAGEBASE + IOBA_RQC + IOLN_RQC) +#define IOLN_RQD 004 +#define IOBA_RQ (IOPAGEBASE + 012150) /* UDA50 */ +#define IOLN_RQ 004 +#define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */ +#define IOLN_TS 004 +#define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */ +#define IOLN_RL 012 +#define IOBA_XQ (IOPAGEBASE + 014440) /* DEQNA/DELQA */ +#define IOLN_XQ 020 +#define IOBA_XQB (IOPAGEBASE + 014460) /* 2nd DEQNA/DELQA */ +#define IOLN_XQB 020 +#define IOBA_TQ (IOPAGEBASE + 014500) /* TMSCP */ +#define IOLN_TQ 004 +#define IOBA_XU (IOPAGEBASE + 014510) /* DEUNA/DELUA */ +#define IOLN_XU 010 +#define IOBA_CR (IOPAGEBASE + 017160) /* CD/CR/CM */ +#define IOLN_CR 010 +#define IOBA_RX (IOPAGEBASE + 017170) /* RX11 */ +#define IOLN_RX 004 +#define IOBA_RY (IOPAGEBASE + 017170) /* RXV21 */ +#define IOLN_RY 004 +#define IOBA_QDSS (IOPAGEBASE + 017400) /* QDSS */ +#define IOLN_QDSS 002 +#define IOBA_HK (IOPAGEBASE + 017440) /* RK611 */ +#define IOLN_HK 040 +#define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */ +#define IOLN_LPT 004 +#define IOBA_PTR (IOPAGEBASE + 017550) /* PC11 reader */ +#define IOLN_PTR 004 +#define IOBA_PTP (IOPAGEBASE + 017554) /* PC11 punch */ +#define IOLN_PTP 004 + +/* Interrupt assignments; within each level, priority is right to left */ + +#define INT_V_DZRX 0 /* BR5 */ +#define INT_V_DZTX 1 +#define INT_V_HK 2 +#define INT_V_RL 3 +#define INT_V_RQ 4 +#define INT_V_TQ 5 +#define INT_V_TS 6 +#define INT_V_RY 7 +#define INT_V_XU 8 + +#define INT_V_LPT 0 /* BR4 */ +#define INT_V_PTR 1 +#define INT_V_PTP 2 +#define INT_V_CR 3 + +#define INT_DZRX (1u << INT_V_DZRX) +#define INT_DZTX (1u << INT_V_DZTX) +#define INT_HK (1u << INT_V_HK) +#define INT_RL (1u << INT_V_RL) +#define INT_RQ (1u << INT_V_RQ) +#define INT_TQ (1u << INT_V_TQ) +#define INT_TS (1u << INT_V_TS) +#define INT_RY (1u << INT_V_RY) +#define INT_XU (1u << INT_V_XU) +#define INT_LPT (1u << INT_V_LPT) +#define INT_PTR (1u << INT_V_PTR) +#define INT_PTP (1u << INT_V_PTP) +#define INT_CR (1u << INT_V_CR) + +#define IPL_DZRX (0x15 - IPL_HMIN) +#define IPL_DZTX (0x15 - IPL_HMIN) +#define IPL_HK (0x15 - IPL_HMIN) +#define IPL_RL (0x15 - IPL_HMIN) +#define IPL_RQ (0x15 - IPL_HMIN) +#define IPL_TQ (0x15 - IPL_HMIN) +#define IPL_TS (0x15 - IPL_HMIN) +#define IPL_RY (0x15 - IPL_HMIN) +#define IPL_XU (0x15 - IPL_HMIN) +#define IPL_LPT (0x14 - IPL_HMIN) +#define IPL_PTR (0x14 - IPL_HMIN) +#define IPL_PTP (0x14 - IPL_HMIN) +#define IPL_CR (0x14 - IPL_HMIN) + +/* Device vectors */ + +#define VEC_Q 0000 +#define VEC_PTR 0070 +#define VEC_PTP 0074 +#define VEC_XQ 0120 +#define VEC_XU 0120 +#define VEC_RQ 0154 +#define VEC_RL 0160 +#define VEC_LPT 0200 +#define VEC_HK 0210 +#define VEC_TS 0224 +#define VEC_CR 0230 +#define VEC_TQ 0260 +#define VEC_RX 0264 +#define VEC_RY 0264 +#define VEC_DZRX 0300 +#define VEC_DZTX 0304 + +/* Interrupt macros */ + +#define IVCL(dv) ((IPL_##dv * 32) + INT_V_##dv) +#define NVCL(dv) ((IPL_##dv * 32) + TR_##dv) +#define IREQ(dv) int_req[IPL_##dv] +#define SET_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] | (INT_##dv) +#define CLR_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] & ~(INT_##dv) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +/* Logging */ + +#define LOG_CPU_I 0x1 /* intexc */ +#define LOG_CPU_R 0x2 /* REI */ +#define LOG_CPU_P 0x4 /* context */ + +/* Massbus definitions */ + +#define MBA_RP (TR_MBA0 - TR_MBA0) /* MBA for RP */ +#define MBA_TU (TR_MBA1 - TR_MBA0) /* MBA for TU */ +#define MBA_RMASK 0x1F /* max 32 reg */ +#define MBE_NXD 1 /* nx drive */ +#define MBE_NXR 2 /* nx reg */ +#define MBE_GOE 3 /* err on GO */ + +/* Boot definitions */ + +#define BOOT_MB 0 /* device codes */ +#define BOOT_HK 1 /* for VMB */ +#define BOOT_RL 2 +#define BOOT_UDA 17 +#define BOOT_TK 18 + +/* Function prototypes for virtual memory interface */ + +int32 Read (uint32 va, int32 lnt, int32 acc); +void Write (uint32 va, int32 val, int32 lnt, int32 acc); + +/* Function prototypes for physical memory interface (inlined) */ + +SIM_INLINE_GCC int32 ReadB (uint32 pa); +SIM_INLINE_GCC int32 ReadW (uint32 pa); +SIM_INLINE_GCC int32 ReadL (uint32 pa); +SIM_INLINE_GCC int32 ReadLP (uint32 pa); +SIM_INLINE_GCC void WriteB (uint32 pa, int32 val); +SIM_INLINE_GCC void WriteW (uint32 pa, int32 val); +SIM_INLINE_GCC void WriteL (uint32 pa, int32 val); +void WriteLP (uint32 pa, int32 val); + +/* Function prototypes for I/O */ + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat set_vec (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat auto_config (char *name, int32 num); + +int32 mba_rdbufW (uint32 mbus, int32 bc, uint16 *buf); +int32 mba_wrbufW (uint32 mbus, int32 bc, uint16 *buf); +int32 mba_chbufW (uint32 mbus, int32 bc, uint16 *buf); +int32 mba_get_bc (uint32 mbus); +void mba_upd_ata (uint32 mbus, uint32 val); +void mba_set_exc (uint32 mbus); +void mba_set_don (uint32 mbus); +void mba_set_enbdis (uint32 mbus, t_bool dis); +t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc); + +t_stat show_nexus (FILE *st, UNIT *uptr, int32 val, void *desc); + +void sbi_set_errcnf (void); +int32 clk_cosched (int32 wait); + +#endif diff --git a/VAX/vax780_fload.c b/VAX/vax780_fload.c new file mode 100644 index 0000000..c74e3f9 --- /dev/null +++ b/VAX/vax780_fload.c @@ -0,0 +1,250 @@ +/* vax780_fload.c: VAX780 FLOAD command + + Copyright (c) 2006-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This code is based on the CP/M RT11 utility, which bears the following + copyrights: + + copyright (c) 1980, William C. Colley, III + + Rev. 1.2 -- Craig Davenport - Incitec Ltd (Feb 1984) + P O Box 140 + Morningside, + Qld 4170, + Australia. + -- Modified for Digital Research C compiler under CP/M-86 + -- Assebmbly language routines added for BIOS calls etc. + + Thanks to Phil Budne for the original adaptation of RT11 to SimH. + + 28-May-08 RMS Inlined physical memory routines +*/ + +#include "vax_defs.h" +#include + +#define BLK_SIZE 256 /* RT11 block size */ + +/* Floppy disk parameters */ + +#define BPT 26 /* blocks/track */ +#define NTRACKS 77 +#define SECTOR_SKEW 2 +#define TRACK_SKEW 6 +#define TRACK_OFFSET 1 /* track 0 unused */ + +/* RT11 directory segment (2 blocks = 512 16b words) */ + +#define DS_TOTAL 0 /* segments available */ +#define DS_MAX 31 /* segment max */ +#define DS_NEXT 1 /* zero for last segment */ +#define DS_HIGHEST 2 /* only in 1st segment */ +#define DS_EXTRA 3 /* extra bytes/entry */ +#define DS_FIRST 4 /* first block */ +#define DS_ENTRIES 5 /* start of entries */ +#define DS_SIZE (2 * BLK_SIZE) /* segment size, words */ + +/* RT11 directory entry offsets */ + +#define DE_STATUS 0 /* status (odd byte) */ +#define TENTAT 001 /* tentative */ +#define EMPTY 002 +#define PERM 004 +#define ENDSEG 010 /* end of segment */ +#define DE_NAME 1 /* file name */ +#define DE_FLNT 4 /* file length */ +#define DE_SIZE 7 /* entry size in words */ +#define DE_GET_STAT(x) (((x) >> 8) & 0377) + +extern UNIT cpu_unit; +extern UNIT fl_unit; + +t_bool rtfile_parse (char *pntr, uint16 *file_name); +uint32 rtfile_lookup (uint16 *file_name, uint32 *start); +uint32 rtfile_ator50 (uint32 ascii); +t_bool rtfile_read (uint32 block, uint32 count, uint16 *buffer); +uint32 rtfile_find (uint32 block, uint32 sector); + +/* FLOAD file_name {file_origin} */ + +t_stat vax780_fload (int flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +uint16 file_name[3], blkbuf[BLK_SIZE]; +t_stat r; +uint32 i, j, start, size, origin; + +if ((fl_unit.flags & UNIT_ATT) == 0) /* floppy attached? */ + return SCPE_UNATT; +if (*cptr == 0) return SCPE_2FARG; +cptr = get_glyph (cptr, gbuf, 0); /* get file name */ +if (!rtfile_parse (gbuf, file_name)) /* legal file name? */ + return SCPE_ARG; +if ((size = rtfile_lookup (file_name, &start)) == 0) /* file on floppy? */ + return SCPE_ARG; +if (*cptr) { /* origin? */ + origin = (uint32) get_uint (cptr, 16, MEMSIZE, &r); + if ((r != SCPE_OK) || (origin & 1)) /* must be even */ + return SCPE_ARG; + } +else origin = 512; /* no, use default */ + +for (i = 0; i < size; i++) { /* loop thru blocks */ + if (!rtfile_read (start + i, 1, blkbuf)) /* read block */ + return SCPE_FMT; + for (j = 0; j < BLK_SIZE; j++) { /* loop thru words */ + if (ADDR_IS_MEM (origin)) + WriteW (origin, blkbuf[j]); + else return SCPE_NXM; + origin = origin + 2; + } + } +return SCPE_OK; +} + +/* Parse an RT11 file name and convert it to radix-50 */ + +t_bool rtfile_parse (char *pntr, uint16 *file_name) +{ +char c; +uint16 d; +uint32 i, j; + +file_name[0] = file_name[1] = file_name[2] = 0; /* zero file name */ +for (i = 0; i < 2; i++) { /* 6 characters */ + for (j = 0; j < 3; j++) { + c = *pntr; + if ((c == '.') || (c == 0)) d = 0; /* fill if . or end */ + else { + if ((d = rtfile_ator50 (c)) == 0) return FALSE; + pntr++; + } + file_name[i] = (file_name[i] * 050) + d; /* merge into name */ + } + } +if (file_name[0] == 0) return FALSE; /* no name? lose */ +while ((c = *pntr++) != '.') { /* scan for . */ + if (c == 0) return TRUE; /* end? done */ + } +for (i = 0; i < 3; i++) { /* 3 characters */ + c = *pntr; + if (c == 0) d = 0; /* fill if end */ + else { + if ((d = rtfile_ator50 (c)) == 0) return FALSE; + pntr++; + } + file_name[2] = (file_name[2] * 050) + d; /* merge into ext */ + } +return TRUE; +} + +/* ASCII to radix-50 conversion */ + +uint32 rtfile_ator50 (uint32 ascii) +{ +static char *r50 = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; +char *fptr; + +ascii = toupper (ascii); +if ((fptr = strchr (r50, toupper (ascii))) != NULL) + return ((uint32) (fptr - r50)); +else return 0; +} + +/* Lookup an RT11 file name in the directory */ + +uint32 rtfile_lookup (uint16 *file_name, uint32 *start) +{ +uint16 dirseg[DS_SIZE]; +uint32 segnum, dirent; + +for (segnum = 1; /* loop thru segments */ + (segnum != 0) && (segnum <= DS_MAX); + segnum = dirseg[DS_NEXT]) { + if (!rtfile_read ((segnum * 2) + 4, 2, dirseg)) /* read segment */ + return 0; /* error? */ + *start = dirseg[DS_FIRST]; /* init file start */ + for (dirent = DS_ENTRIES; /* loop thru entries */ + (dirent < DS_SIZE) && + (DE_GET_STAT (dirseg[dirent + DE_STATUS]) != ENDSEG); + dirent += DE_SIZE + (dirseg[DS_EXTRA] / 2)) { + if ((DE_GET_STAT (dirseg[dirent + DE_STATUS]) == PERM) && + (dirseg[dirent + DE_NAME + 0] == file_name[0]) && + (dirseg[dirent + DE_NAME + 1] == file_name[1]) && + (dirseg[dirent + DE_NAME + 2] == file_name[2])) + return dirseg[dirent + DE_FLNT]; + *start += dirseg[dirent + DE_FLNT]; /* incr file start */ + } + } +return 0; +} + +/* Read blocks */ + +t_stat rtfile_read (uint32 block, uint32 count, uint16 *buffer) +{ +uint32 i, j; +uint32 pos; +uint8 *fbuf = fl_unit.filebuf; + +for (; count > 0; count--, block++) { + for (i = 0; i < 4; i++) { /* 4 sectors/block */ + pos = rtfile_find (block, i); /* position */ + if ((pos + 128) >= (uint32) fl_unit.capac) /* off end of disk? */ + return FALSE; + for (j = 0; j < 128; j = j + 2) /* copy 128 bytes */ + *buffer++ = (((uint16) fbuf[pos + j + 1]) << 8) | + ((uint16) fbuf[pos + j]); + } + } +return TRUE; +} + +/* Map an RT-11 block number to a physical byte number */ + +uint32 rtfile_find (uint32 block, uint32 sector) +{ +uint32 ls, lt, pt, ps; +uint32 off, bb; + +/* get logical block, track & sector */ + +bb = (block * 4) + sector; + +lt = bb / BPT; +ls = bb % BPT; + +/* logic from 4.3BSD rx.c + * calculate phys track & sector + * 2:1 skew, 6 sector skew for each track + */ + +pt = lt + TRACK_OFFSET; +ps = ((ls * SECTOR_SKEW) + (ls / (BPT / SECTOR_SKEW)) + (TRACK_SKEW * lt)) % BPT; + +/* byte offset in logical disk */ + +off = (pt * BPT + ps) * 128; +return off; +} diff --git a/VAX/vax780_mba.c b/VAX/vax780_mba.c new file mode 100644 index 0000000..b7cd550 --- /dev/null +++ b/VAX/vax780_mba.c @@ -0,0 +1,765 @@ +/* vax780_mba.c: VAX 11/780 Massbus adapter + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + mba0, mba1 RH780 Massbus adapter + + 28-May-08 RMS Inlined physical memory routines +*/ + +#include "vax_defs.h" + +/* Massbus */ + +#define MBA_NMAPR 256 /* number of map reg */ +#define MBA_V_RTYPE 10 /* nexus addr: reg type */ +#define MBA_M_RTYPE 0x3 +#define MBART_INT 0x0 /* internal */ +#define MBART_EXT 0x1 /* external */ +#define MBART_MAP 0x2 /* map */ +#define MBA_V_INTOFS 2 /* int reg: reg ofs */ +#define MBA_M_INTOFS 0xFF +#define MBA_V_DRV 7 /* ext reg: drive num */ +#define MBA_M_DRV 0x7 +#define MBA_V_DEVOFS 2 /* ext reg: reg ofs */ +#define MBA_M_DEVOFS 0x1F +#define MBA_RTYPE(x) (((x) >> MBA_V_RTYPE) & MBA_M_RTYPE) +#define MBA_INTOFS(x) (((x) >> MBA_V_INTOFS) & MBA_M_INTOFS) +#define MBA_EXTDRV(x) (((x) >> MBA_V_DRV) & MBA_M_DRV) +#define MBA_EXTOFS(x) (((x) >> MBA_V_DEVOFS) & MBA_M_DEVOFS) + +/* Massbus configuration register */ + +#define MBACNF_OF 0x0 +#define MBACNF_ADPDN 0x00800000 /* adap pdn - ni */ +#define MBACNF_ADPUP 0x00400000 /* adap pup - ni */ +#define MBACNF_CODE 0x00000020 +#define MBACNF_RD (SBI_FAULTS|MBACNF_W1C) +#define MBACNF_W1C 0x00C00000 + +/* Control register */ + +#define MBACR_OF 0x1 +#define MBACR_MNT 0x00000008 /* maint */ +#define MBACR_IE 0x00000004 /* int enable */ +#define MBACR_ABORT 0x00000002 /* abort */ +#define MBACR_INIT 0x00000001 +#define MBACR_RD 0x0000000E +#define MBACR_WR 0x0000000E + +/* Status register */ + +#define MBASR_OF 0x2 +#define MBASR_DTBUSY 0x80000000 /* DT busy RO */ +#define MBASR_NRCONF 0x40000000 /* no conf - ni W1C */ +#define MBASR_CRD 0x20000000 /* CRD - ni W1C */ +#define MBASR_CBH 0x00800000 /* CBHUNG - ni W1C */ +#define MBASR_PGE 0x00080000 /* prog err - W1C int */ +#define MBASR_NFD 0x00040000 /* nx drive - W1C int */ +#define MBASR_MCPE 0x00020000 /* ctl perr - ni W1C int */ +#define MBASR_ATA 0x00010000 /* attn - W1C int */ +#define MBASR_SPE 0x00004000 /* silo perr - ni W1C int */ +#define MBASR_DTCMP 0x00002000 /* xfr done - W1C int */ +#define MBASR_DTABT 0x00001000 /* abort - W1C int */ +#define MBASR_DLT 0x00000800 /* dat late - ni W1C abt */ +#define MBASR_WCEU 0x00000400 /* wrchk upper - W1C abt */ +#define MBASR_WCEL 0x00000200 /* wrchk lower - W1C abt */ +#define MBASR_MXF 0x00000100 /* miss xfr - ni W1C abt */ +#define MBASR_MBEXC 0x00000080 /* except - ni W1C abt */ +#define MBASR_MBDPE 0x00000040 /* dat perr - ni W1C abt */ +#define MBASR_MAPPE 0x00000020 /* map perr - ni W1C abt */ +#define MBASR_INVM 0x00000010 /* inv map - W1C abt */ +#define MBASR_ERCONF 0x00000008 /* err conf - ni W1C abt */ +#define MBASR_RDS 0x00000004 /* RDS - ni W1C abt */ +#define MBASR_ITMO 0x00000002 /* timeout - W1C abt */ +#define MBASR_RTMO 0x00000001 /* rd timeout - W1C abt */ +#define MBASR_RD 0xE08F7FFF +#define MBASR_W1C 0x608F7FFF +#define MBASR_ABORTS 0x00000FFF +#define MBASR_ERRORS 0x608E49FF +#define MBASR_INTR 0x000F7000 + +/* Virtual address register */ + +#define MBAVA_OF 0x3 +#define MBAVA_RD 0x0001FFFF +#define MBAVA_WR (MBAVA_RD) + +/* Byte count */ + +#define MBABC_OF 0x4 +#define MBABC_WR 0x0000FFFF +#define MBABC_V_MBC 16 /* MB count */ + +/* Diagnostic register */ + +#define MBADR_OF 0x5 +#define MBADR_RD 0xFFFFFFFF +#define MBADR_WR 0xFFC00000 + +/* Selected map entry - read only */ + +#define MBASMR_OF 0x6 +#define MBASMR_RD (MBAMAP_RD) + +/* Command register (SBI) - read only */ + +#define MBACMD_OF 0x7 + +/* External registers */ + +#define MBA_CS1 0x00 /* device CSR1 */ +#define MBA_CS1_WR 0x3F /* writeable bits */ +#define MBA_CS1_DT 0x28 /* >= for data xfr */ + +/* Map registers */ + +#define MBAMAP_VLD 0x80000000 /* valid */ +#define MBAMAP_PAG 0x001FFFFF +#define MBAMAP_RD (MBAMAP_VLD | MBAMAP_PAG) +#define MBAMAP_WR (MBAMAP_RD) + +/* Debug switches */ + +#define MBA_DEB_RRD 0x01 /* reg reads */ +#define MBA_DEB_RWR 0x02 /* reg writes */ +#define MBA_DEB_MRD 0x04 /* map reads */ +#define MBA_DEB_MWR 0x08 /* map writes */ +#define MBA_DEB_XFR 0x10 /* transfers */ +#define MBA_DEB_ERR 0x20 /* errors */ + +uint32 mba_cnf[MBA_NUM]; /* config reg */ +uint32 mba_cr[MBA_NUM]; /* control reg */ +uint32 mba_sr[MBA_NUM]; /* status reg */ +uint32 mba_va[MBA_NUM]; /* virt addr */ +uint32 mba_bc[MBA_NUM]; /* byte count */ +uint32 mba_dr[MBA_NUM]; /* diag reg */ +uint32 mba_smr[MBA_NUM]; /* sel map reg */ +uint32 mba_map[MBA_NUM][MBA_NMAPR]; /* map */ + +extern uint32 nexus_req[NEXUS_HLVL]; +extern UNIT cpu_unit; +extern FILE *sim_log; +extern FILE *sim_deb; +extern int32 sim_switches; + +t_stat mba_reset (DEVICE *dptr); +t_stat mba_rdreg (int32 *val, int32 pa, int32 mode); +t_stat mba_wrreg (int32 val, int32 pa, int32 lnt); +t_bool mba_map_addr (uint32 va, uint32 *ma, uint32 mb); +void mba_set_int (uint32 mb); +void mba_clr_int (uint32 mb); +void mba_upd_sr (uint32 set, uint32 clr, uint32 mb); +DIB mba0_dib, mba1_dib; + +/* Massbus register dispatches */ + +static t_stat (*mbregR[MBA_NUM])(int32 *dat, int32 ad, int32 md); +static t_stat (*mbregW[MBA_NUM])(int32 dat, int32 ad, int32 md); +static int32 (*mbabort[MBA_NUM])(void); + +/* Massbus adapter data structures + + mba_dev MBA device descriptors + mbax_unit MBA unit + mbax_reg MBA register list +*/ + +DIB mba0_dib = { TR_MBA0, 0, &mba_rdreg, &mba_wrreg, 0, NVCL (MBA0) }; + +UNIT mba0_unit = { UDATA (NULL, 0, 0) }; + +REG mba0_reg[] = { + { HRDATA (CNFR, mba_cnf[0], 32) }, + { HRDATA (CR, mba_cr[0], 4) }, + { HRDATA (SR, mba_sr[0], 32) }, + { HRDATA (VA, mba_va[0], 17) }, + { HRDATA (BC, mba_bc[0], 16) }, + { HRDATA (DR, mba_dr[0], 32) }, + { HRDATA (SMR, mba_dr[0], 32) }, + { BRDATA (MAP, mba_map[0], 16, 32, MBA_NMAPR) }, + { FLDATA (NEXINT, nexus_req[IPL_MBA0], TR_MBA0) }, + { NULL } + }; + +MTAB mba0_mod[] = { + { MTAB_XTD|MTAB_VDV, TR_MBA0, "NEXUS", NULL, + NULL, &show_nexus }, + { 0 } + }; + +DIB mba1_dib = { TR_MBA1, 0, &mba_rdreg, &mba_wrreg, 0, NVCL (MBA1) }; + +UNIT mba1_unit = { UDATA (NULL, 0, 0) }; + +MTAB mba1_mod[] = { + { MTAB_XTD|MTAB_VDV, TR_MBA1, "NEXUS", NULL, + NULL, &show_nexus }, + { 0 } + }; + +REG mba1_reg[] = { + { HRDATA (CNFR, mba_cnf[1], 32) }, + { HRDATA (CR, mba_cr[1], 4) }, + { HRDATA (SR, mba_sr[1], 32) }, + { HRDATA (VA, mba_va[1], 17) }, + { HRDATA (BC, mba_bc[1], 16) }, + { HRDATA (DR, mba_dr[1], 32) }, + { HRDATA (SMR, mba_dr[1], 32) }, + { BRDATA (MAP, mba_map[1], 16, 32, MBA_NMAPR) }, + { FLDATA (NEXINT, nexus_req[IPL_MBA1], TR_MBA1) }, + { NULL } + }; + +DEBTAB mba_deb[] = { + { "REGREAD", MBA_DEB_RRD }, + { "REGWRITE", MBA_DEB_RWR }, + { "MAPREAD", MBA_DEB_MRD }, + { "MAPWRITE", MBA_DEB_MWR }, + { "XFER", MBA_DEB_XFR }, + { "ERROR", MBA_DEB_ERR }, + { NULL, 0 } + }; + +DEVICE mba_dev[] = { + { + "MBA0", &mba0_unit, mba0_reg, mba0_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &mba_reset, + NULL, NULL, NULL, + &mba0_dib, DEV_NEXUS | DEV_DEBUG, 0, + mba_deb, 0, 0 + }, + { + "MBA1", &mba1_unit, mba1_reg, mba1_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &mba_reset, + NULL, NULL, NULL, + &mba1_dib, DEV_NEXUS | DEV_DEBUG, 0, + mba_deb, 0, 0 + } + }; + +/* Read Massbus adapter register */ + +t_stat mba_rdreg (int32 *val, int32 pa, int32 lnt) +{ +int32 mb, ofs, drv, rtype; +uint32 t; +t_stat r; + +mb = NEXUS_GETNEX (pa) - TR_MBA0; /* get MBA */ +if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ + printf (">>MBA%d: invalid adapter read mask, pa = %X, lnt = %d\r\n", mb, pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + return SCPE_OK; + } +if (mb >= MBA_NUM) return SCPE_NXM; /* valid? */ +rtype = MBA_RTYPE (pa); /* get reg type */ + +switch (rtype) { /* case on type */ + + case MBART_INT: /* internal */ + ofs = MBA_INTOFS (pa); /* check range */ + switch (ofs) { + + case MBACNF_OF: /* CNF */ + *val = (mba_cnf[mb] & MBACNF_RD) | MBACNF_CODE; + break; + + case MBACR_OF: /* CR */ + *val = mba_cr[mb] & MBACR_RD; + break; + + case MBASR_OF: /* SR */ + *val = mba_sr[mb] & MBASR_RD; + break; + + case MBAVA_OF: /* VA */ + *val = mba_va[mb] & MBAVA_RD; + break; + + case MBABC_OF: /* BC */ + t = mba_bc[mb] & MBABC_WR; + *val = (t << MBABC_V_MBC) | t; + break; + + case MBADR_OF: /* DR */ + *val = mba_dr[mb] & MBADR_RD; + break; + + case MBASMR_OF: /* SMR */ + *val = mba_smr[mb] & MBASMR_RD; + break; + + case MBACMD_OF: /* CMD */ + *val = 0; + break; + + default: + return SCPE_NXM; + } + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RRD)) + fprintf (sim_deb, ">>MBA%d: int reg %d read, value = %X\n", mb, ofs, *val); + break; + + case MBART_EXT: /* external */ + if (!mbregR[mb]) return SCPE_NXM; /* device there? */ + drv = MBA_EXTDRV (pa); /* get dev num */ + ofs = MBA_EXTOFS (pa); /* get reg offs */ + r = mbregR[mb] (val, ofs, drv); /* call device */ + if (r == MBE_NXD) mba_upd_sr (MBASR_NFD, 0, mb);/* nx drive? */ + else if (r == MBE_NXR) return SCPE_NXM; /* nx reg? */ + *val |= (mba_sr[mb] & ~WMASK); /* upper 16b from SR */ + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RRD)) + fprintf (sim_deb, ">>MBA%d: drv %d ext reg %d read, value = %X\n", mb, drv, ofs, *val); + break; + + case MBART_MAP: /* map */ + ofs = MBA_INTOFS (pa); + *val = mba_map[mb][ofs] & MBAMAP_RD; + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_MRD)) + fprintf (sim_deb, ">>MBA%d: map %d read, value = %X\n", mb, ofs, *val); + break; + + default: + return SCPE_NXM; + } + +return SCPE_OK; +} + +/* Write Massbus adapter register */ + +t_stat mba_wrreg (int32 val, int32 pa, int32 lnt) +{ +int32 mb, ofs, drv, rtype; +t_stat r; +t_bool cs1dt; + +mb = NEXUS_GETNEX (pa) - TR_MBA0; /* get MBA */ +if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ + printf (">>MBA%d: invalid adapter write mask, pa = %X, lnt = %d\r\n", mb, pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + return SCPE_OK; + } +if (mb >= MBA_NUM) return SCPE_NXM; /* valid? */ +rtype = MBA_RTYPE (pa); /* get reg type */ + +switch (rtype) { /* case on type */ + + case MBART_INT: /* internal */ + ofs = MBA_INTOFS (pa); /* check range */ + switch (ofs) { + + case MBACNF_OF: /* CNF */ + mba_cnf[mb] &= ~(val & MBACNF_W1C); + break; + + case MBACR_OF: /* CR */ + if (val & MBACR_INIT) /* init? */ + mba_reset (&mba_dev[mb]); /* reset MBA */ + if ((val & MBACR_ABORT) && + (mba_sr[mb] & MBASR_DTBUSY)) { + if (mbabort[mb]) mbabort[mb] (); /* abort? */ + mba_upd_sr (MBASR_DTABT, 0, mb); + } + if ((val & MBACR_MNT) && + (mba_sr[mb] & MBASR_DTBUSY)) { + mba_upd_sr (MBASR_PGE, 0, mb); /* mnt & xfer? */ + val = val & ~MBACR_MNT; + } + if ((val & MBACR_IE) == 0) mba_clr_int (mb); + mba_cr[mb] = (mba_cr[mb] & ~MBACR_WR) | + (val & MBACR_WR); + break; + + case MBASR_OF: /* SR */ + mba_sr[mb] = mba_sr[mb] & ~(val & MBASR_W1C); + break; + + case MBAVA_OF: /* VA */ + if (mba_sr[mb] & MBASR_DTBUSY) /* err if xfr */ + mba_upd_sr (MBASR_PGE, 0, mb); + else mba_va[mb] = val & MBAVA_WR; + break; + + case MBABC_OF: /* BC */ + if (mba_sr[mb] & MBASR_DTBUSY) /* err if xfr */ + mba_upd_sr (MBASR_PGE, 0, mb); + else mba_bc[mb] = val & MBABC_WR; + break; + + case MBADR_OF: /* DR */ + mba_dr[mb] = (mba_dr[mb] & ~MBADR_WR) | + (val & MBADR_WR); + break; + + default: + return SCPE_NXM; + } + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RWR)) + fprintf (sim_deb, ">>MBA%d: int reg %d write, value = %X\n", mb, ofs, val); + break; + + case MBART_EXT: /* external */ + if (!mbregW[mb]) return SCPE_NXM; /* device there? */ + drv = MBA_EXTDRV (pa); /* get dev num */ + ofs = MBA_EXTOFS (pa); /* get reg offs */ + cs1dt = (ofs == MBA_CS1) && (val & CSR_GO) && /* starting xfr? */ + ((val & MBA_CS1_WR) >= MBA_CS1_DT); + if (cs1dt && (mba_sr[mb] & MBASR_DTBUSY)) { /* xfr while busy? */ + mba_upd_sr (MBASR_PGE, 0, mb); /* prog error */ + break; + } + r = mbregW[mb] (val & WMASK, ofs, drv); /* write dev reg */ + if (r == MBE_NXD) mba_upd_sr (MBASR_NFD, 0, mb);/* nx drive? */ + else if (r == MBE_NXR) return SCPE_NXM; /* nx reg? */ + if (cs1dt && (r == SCPE_OK)) /* did dt start? */ + mba_sr[mb] = (mba_sr[mb] | MBASR_DTBUSY) & ~MBASR_W1C; + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_RWR)) + fprintf (sim_deb, ">>MBA%d: drv %d ext reg %d write, value = %X\n", mb, drv, ofs, val); + break; + + case MBART_MAP: /* map */ + ofs = MBA_INTOFS (pa); + mba_map[mb][ofs] = val & MBAMAP_WR; + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_MWR)) + fprintf (sim_deb, ">>MBA%d: map %d write, value = %X\n", mb, ofs, val); + break; + + default: + return SCPE_NXM; + } + +return SCPE_OK; +} + +/* Massbus I/O routine + + mb_rdbufW - fetch word buffer from memory + mb_wrbufW - store word buffer into memory + mb_chbufW - compare word buffer with memory + + Returns number of bytes successfully transferred/checked +*/ + +int32 mba_rdbufW (uint32 mb, int32 bc, uint16 *buf) +{ +int32 i, j, ba, mbc, pbc; +uint32 pa, dat; + +if (mb >= MBA_NUM) return 0; /* valid MBA? */ +ba = mba_va[mb]; /* get virt addr */ +mbc = (MBABC_WR + 1) - mba_bc[mb]; /* get Mbus bc */ +if (bc > mbc) bc = mbc; /* use smaller */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (!mba_map_addr (ba + i, &pa, mb)) break; /* page inv? */ + if (!ADDR_IS_MEM (pa)) { /* NXM? */ + mba_upd_sr (MBASR_RTMO, 0, mb); + break; + } + pbc = VA_PAGSIZE - VA_GETOFF (pa); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_XFR)) + fprintf (sim_deb, ">>MBA%d: read, pa = %X, bc = %X\n", mb, pa, pbc); + if ((pa | pbc) & 1) { /* aligned word? */ + for (j = 0; j < pbc; pa++, j++) { /* no, bytes */ + if ((i + j) & 1) { /* odd byte? */ + *buf = (*buf & BMASK) | (ReadB (pa) << 8); + buf++; + } + else *buf = (*buf & ~BMASK) | ReadB (pa); + } + } + else if ((pa | pbc) & 3) { /* aligned LW? */ + for (j = 0; j < pbc; pa = pa + 2, j = j + 2) { /* no, words */ + *buf++ = ReadW (pa); /* get word */ + } + } + else { /* yes, do by LW */ + for (j = 0; j < pbc; pa = pa + 4, j = j + 4) { + dat = ReadL (pa); /* get lw */ + *buf++ = dat & WMASK; /* low 16b */ + *buf++ = (dat >> 16) & WMASK; /* high 16b */ + } + } + } +mba_bc[mb] = (mba_bc[mb] + i) & MBABC_WR; +mba_va[mb] = (mba_va[mb] + i) & MBAVA_WR; +return i; +} + +int32 mba_wrbufW (uint32 mb, int32 bc, uint16 *buf) +{ +int32 i, j, ba, mbc, pbc; +uint32 pa, dat; + +if (mb >= MBA_NUM) return 0; /* valid MBA? */ +ba = mba_va[mb]; /* get virt addr */ +mbc = (MBABC_WR + 1) - mba_bc[mb]; /* get Mbus bc */ +if (bc > mbc) bc = mbc; /* use smaller */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (!mba_map_addr (ba + i, &pa, mb)) break; /* page inv? */ + if (!ADDR_IS_MEM (pa)) { /* NXM? */ + mba_upd_sr (MBASR_RTMO, 0, mb); + break; + } + pbc = VA_PAGSIZE - VA_GETOFF (pa); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_XFR)) + fprintf (sim_deb, ">>MBA%d: write, pa = %X, bc = %X\n", mb, pa, pbc); + if ((pa | pbc) & 1) { /* aligned word? */ + for (j = 0; j < pbc; pa++, j++) { /* no, bytes */ + if ((i + j) & 1) { + WriteB (pa, (*buf >> 8) & BMASK); + buf++; + } + else WriteB (pa, *buf & BMASK); + } + } + else if ((pa | pbc) & 3) { /* aligned LW? */ + for (j = 0; j < pbc; pa = pa + 2, j = j + 2) { /* no, words */ + WriteW (pa, *buf); /* write word */ + buf++; + } + } + else { /* yes, do by LW */ + for (j = 0; j < pbc; pa = pa + 4, j = j + 4) { + dat = (uint32) *buf++; /* get low 16b */ + dat = dat | (((uint32) *buf++) << 16); /* merge hi 16b */ + WriteL (pa, dat); /* store LW */ + } + } + } +mba_bc[mb] = (mba_bc[mb] + i) & MBABC_WR; +mba_va[mb] = (mba_va[mb] + i) & MBAVA_WR; +return i; +} + +int32 mba_chbufW (uint32 mb, int32 bc, uint16 *buf) +{ +int32 i, j, ba, mbc, pbc; +uint32 pa, dat, cmp; + +if (mb >= MBA_NUM) return 0; /* valid MBA? */ +ba = mba_va[mb]; /* get virt addr */ +mbc = (MBABC_WR + 1) - mba_bc[mb]; /* get Mbus bc */ +if (bc > mbc) bc = mbc; /* use smaller */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (!mba_map_addr (ba + i, &pa, mb)) break; /* page inv? */ + if (!ADDR_IS_MEM (pa)) { /* NXM? */ + mba_upd_sr (MBASR_RTMO, 0, mb); + break; + } + pbc = VA_PAGSIZE - VA_GETOFF (pa); /* left in page */ + if (DEBUG_PRI (mba_dev[mb], MBA_DEB_XFR)) + fprintf (sim_deb, ">>MBA%d: check, pa = %X, bc = %X\n", mb, pa, pbc); + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + for (j = 0; j < pbc; j++, pa++) { /* byte by byte */ + cmp = ReadB (pa); + if ((i + j) & 1) dat = (*buf++ >> 8) & BMASK; + else dat = *buf & BMASK; + if (cmp != dat) { + mba_upd_sr ((j & 1)? MBASR_WCEU: MBASR_WCEL, 0, mb); + break; + } /* end if */ + } /* end for j */ + } /* end for i */ +mba_bc[mb] = (mba_bc[mb] + i) & MBABC_WR; +mba_va[mb] = (mba_va[mb] + i) & MBAVA_WR; +return i; +} + +/* Map an address via the translation map */ + +t_bool mba_map_addr (uint32 va, uint32 *ma, uint32 mb) +{ +uint32 vblk = (va >> VA_V_VPN); /* map index */ +uint32 mmap = mba_map[mb][vblk]; /* get map */ + +mba_smr[mb] = mmap; /* save map reg */ +if (mmap & MBAMAP_VLD) { /* valid? */ + *ma = ((mmap & MBAMAP_PAG) << VA_V_VPN) + VA_GETOFF (va); + return 1; /* legit addr */ + } +mba_upd_sr (MBASR_INVM, 0, mb); /* invalid map */ +return 0; +} + +/* Device access, status, and interrupt routines */ + +void mba_set_don (uint32 mb) +{ +mba_upd_sr (MBASR_DTCMP, 0, mb); +return; +} + +void mba_upd_ata (uint32 mb, uint32 val) +{ +if (val) mba_upd_sr (MBASR_ATA, 0, mb); +else mba_upd_sr (0, MBASR_ATA, mb); +return; +} + +void mba_set_exc (uint32 mb) +{ +mba_upd_sr (MBASR_MBEXC, 0, mb); +if (DEBUG_PRI (mba_dev[mb], MBA_DEB_ERR)) + fprintf (sim_deb, ">>MBA%d: EXC write\n", mb); +return; +} + +int32 mba_get_bc (uint32 mb) +{ +if (mb >= MBA_NUM) return 0; +return (MBABC_WR + 1) - mba_bc[mb]; +} + +void mba_set_int (uint32 mb) +{ +DIB *dibp; + +if (mb >= MBA_NUM) return; +dibp = (DIB *) mba_dev[mb].ctxt; +if (dibp) + nexus_req[dibp->vloc >> 5] |= (1u << (dibp->vloc & 0x1F)); +return; +} + +void mba_clr_int (uint32 mb) +{ +DIB *dibp; + +if (mb >= MBA_NUM) return; +dibp = (DIB *) mba_dev[mb].ctxt; +if (dibp) + nexus_req[dibp->vloc >> 5] &= ~(1u << (dibp->vloc & 0x1F)); +return; +} + +void mba_upd_sr (uint32 set, uint32 clr, uint32 mb) +{ +if (mb >= MBA_NUM) return; +if (set & MBASR_ABORTS) set |= (MBASR_DTCMP|MBASR_DTABT); +if (set & (MBASR_DTCMP|MBASR_DTABT)) mba_sr[mb] &= ~MBASR_DTBUSY; +mba_sr[mb] = (mba_sr[mb] | set) & ~clr; +if ((set & MBASR_INTR) && (mba_cr[mb] & MBACR_IE)) + mba_set_int (mb); +if ((set & MBASR_ERRORS) && (DEBUG_PRI (mba_dev[mb], MBA_DEB_ERR))) + fprintf (sim_deb, ">>MBA%d: CS error = %X\n", mb, mba_sr[mb]); +return; +} + +/* Reset Massbus adapter */ + +t_stat mba_reset (DEVICE *dptr) +{ +int32 i, mb; +DIB *dibp; + +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +mb = dibp->ba - TR_MBA0; +if ((mb < 0) || (mb >= MBA_NUM)) return SCPE_IERR; +mba_cnf[mb] = 0; +mba_cr[mb] &= MBACR_MNT; +mba_sr[mb] = 0; +mba_bc[mb] = 0; +mba_va[mb] = 0; +mba_dr[mb] = 0; +mba_smr[mb] = 0; +if (sim_switches & SWMASK ('P')) { + for (i = 0; i < MBA_NMAPR; i++) mba_map[mb][i] = 0; + } +if (mbabort[mb]) mbabort[mb] (); /* reset device */ +return SCPE_OK; +} + +/* Show Massbus adapter number */ + +t_stat mba_show_num (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr = find_dev_from_unit (uptr); +DIB *dibp; + +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +fprintf (st, "Massbus adapter %d", dibp->ba); +return SCPE_OK; +} + +/* Enable/disable Massbus adapter */ + +void mba_set_enbdis (uint32 mb, t_bool dis) +{ +if (mb >= MBA_NUM) return; /* valid MBA? */ +if (dis) mba_dev[mb].flags |= DEV_DIS; +else mba_dev[mb].flags &= ~DEV_DIS; +return; +} + +/* Init Mbus tables */ + +void init_mbus_tab (void) +{ +uint32 i; + +for (i = 0; i < MBA_NUM; i++) { + mbregR[i] = NULL; + mbregW[i] = NULL; + mbabort[i] = NULL; + } +return; +} + +/* Build dispatch tables */ + +t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp) +{ +uint32 idx; + +if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */ +idx = dibp->ba; /* Mbus # */ +if (idx >= MBA_NUM) return SCPE_STOP; +if ((mbregR[idx] && dibp->rd && /* conflict? */ + (mbregR[idx] != dibp->rd)) || + (mbregW[idx] && dibp->wr && + (mbregW[idx] != dibp->wr)) || + (mbabort[idx] && dibp->ack[0] && + (mbabort[idx] != dibp->ack[0]))) { + printf ("Massbus %s assignment conflict at %d\n", + sim_dname (dptr), dibp->ba); + if (sim_log) fprintf (sim_log, + "Massbus %s assignment conflict at %d\n", + sim_dname (dptr), dibp->ba); + return SCPE_STOP; + } +if (dibp->rd) mbregR[idx] = dibp->rd; /* set rd dispatch */ +if (dibp->wr) mbregW[idx] = dibp->wr; /* set wr dispatch */ +if (dibp->ack[0]) mbabort[idx] = dibp->ack[0]; /* set abort dispatch */ +return SCPE_OK; +} + diff --git a/VAX/vax780_mem.c b/VAX/vax780_mem.c new file mode 100644 index 0000000..5e9b05c --- /dev/null +++ b/VAX/vax780_mem.c @@ -0,0 +1,276 @@ +/* vax780_mem.c: VAX 11/780 memory controllers + + Copyright (c) 2004-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module contains the VAX 11/780 system-specific registers and devices. + + mctl0, mctl1 MS780C/E memory controllers +*/ + +#include "vax_defs.h" + +/* Memory controller register A */ + +#define MCRA_OF 0x0 +#define MCRA_SUMM 0x00100000 /* err summ (MS780E) */ +#define MCRA_C_SIZE 0x00007E00 /* array size - fixed */ +#define MCRA_V_SIZE 9 +#define MCRA_ILVE 0x00000100 /* interleave wr enab */ +#define MCRA_TYPE 0x000000F8 /* type */ +#define MCRA_C_TYPE 0x00000010 /* 16k uninterleaved */ +#define MCRA_E_TYPE 0x0000006A /* 256k upper + lower */ +#define MCRA_ILV 0x00000007 /* interleave */ +#define MCRA_RD (0x00107FFF|SBI_FAULTS) +#define MCRA_WR 0x00000100 + +/* Memory controller register B */ + +#define MCRB_OF 0x1 +#define MCRB_FP 0xF0000000 /* file pointers */ +#define MCRB_V_SA 15 /* start addr */ +#define MCRB_M_SA 0x1FFF +#define MCRB_SA (MCRB_M_SA << MCRB_V_SA) +#define MCRB_SAE 0x00004000 /* start addr wr enab */ +#define MCRB_INIT 0x00003000 /* init state */ +#define MCRB_REF 0x00000400 /* refresh */ +#define MCRB_ECC 0x000003FF /* ECC for diags */ +#define MCRB_RD 0xFFFFF7FF +#define MCRB_WR 0x000043FF + +/* Memory controller register C,D */ + +#define MCRC_OF 0x2 +#define MCRD_OF 0x3 +#define MCRC_DCRD 0x40000000 /* disable CRD */ +#define MCRC_HER 0x20000000 /* high error rate */ +#define MCRC_ERL 0x10000000 /* log error */ +#define MCRC_C_ER 0x0FFFFFFF /* MS780C error */ +#define MCRC_E_PE1 0x00080000 /* MS780E par ctl 1 */ +#define MCRC_E_PE0 0x00040000 /* MS780E par ctl 0 */ +#define MCRC_E_CRD 0x00000200 /* MS780E CRD */ +#define MCRC_E_PEW 0x00000100 /* MS780E par err wr */ +#define MCRC_E_USEQ 0x00000080 /* MS780E seq err */ +#define MCRC_C_RD 0x7FFFFFFF +#define MCRC_E_RD 0x700C0380 +#define MCRC_WR 0x40000000 +#define MCRC_C_W1C 0x30000000 +#define MCRC_E_W1C 0x300C0380 + +#define MCRROM_OF 0x400 + +uint32 mcr_a[MCTL_NUM]; +uint32 mcr_b[MCTL_NUM]; +uint32 mcr_c[MCTL_NUM]; +uint32 mcr_d[MCTL_NUM]; +uint32 rom_lw[MCTL_NUM][ROMSIZE >> 2]; + +extern UNIT cpu_unit; + +t_stat mctl_reset (DEVICE *dptr); +t_stat mctl_rdreg (int32 *val, int32 pa, int32 mode); +t_stat mctl_wrreg (int32 val, int32 pa, int32 mode); + +/* MCTLx data structures + + mctlx_dev MCTLx device descriptor + mctlx_unit MCTLx unit + mctlx_reg MCTLx register list +*/ + +DIB mctl0_dib[] = { TR_MCTL0, 0, &mctl_rdreg, &mctl_wrreg, 0 }; + +UNIT mctl0_unit = { UDATA (NULL, 0, 0) }; + +REG mctl0_reg[] = { + { HRDATA (CRA, mcr_a[0], 32) }, + { HRDATA (CRB, mcr_b[0], 32) }, + { HRDATA (CRC, mcr_c[0], 32) }, + { HRDATA (CRD, mcr_d[0], 32) }, + { BRDATA (ROM, rom_lw[0], 16, 32, ROMSIZE >> 2) }, + { NULL } + }; + +MTAB mctl0_mod[] = { + { MTAB_XTD|MTAB_VDV, TR_MCTL0, "NEXUS", NULL, + NULL, &show_nexus }, + { 0 } + }; + +DIB mctl1_dib[] = { TR_MCTL1, 0, &mctl_rdreg, &mctl_wrreg, 0 }; + +UNIT mctl1_unit = { UDATA (NULL, 0, 0) }; + +MTAB mctl1_mod[] = { + { MTAB_XTD|MTAB_VDV, TR_MCTL1, "NEXUS", NULL, + NULL, &show_nexus }, + { 0 } }; + +REG mctl1_reg[] = { + { HRDATA (CRA, mcr_a[1], 32) }, + { HRDATA (CRB, mcr_b[1], 32) }, + { HRDATA (CRC, mcr_c[1], 32) }, + { HRDATA (CRD, mcr_d[1], 32) }, + { BRDATA (ROM, rom_lw[1], 16, 32, ROMSIZE >> 2) }, + { NULL } + }; + +DEVICE mctl_dev[] = { + { + "MCTL0", &mctl0_unit, mctl0_reg, mctl0_mod, + 1, 16, 16, 1, 16, 8, + NULL, NULL, &mctl_reset, + NULL, NULL, NULL, + &mctl0_dib, DEV_NEXUS + }, + { + "MCTL1", &mctl1_unit, mctl1_reg, mctl1_mod, + 1, 16, 16, 1, 16, 8, + NULL, NULL, &mctl_reset, + NULL, NULL, NULL, + &mctl1_dib, DEV_NEXUS + } + }; + +/* Memory controller register read */ + +t_stat mctl_rdreg (int32 *val, int32 pa, int32 lnt) +{ +int32 mctl, ofs; +t_bool extmem = MEMSIZE > MAXMEMSIZE; + +if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ + printf (">>MCTL: invalid adapter read mask, pa = %X, lnt = %d\r\n", pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + return SCPE_OK; + } +mctl = NEXUS_GETNEX (pa) - TR_MCTL0; /* get mctl num */ +ofs = NEXUS_GETOFS (pa); /* get offset */ +if (ofs >= MCRROM_OF) { /* ROM? */ + *val = rom_lw[mctl][ofs - MCRROM_OF]; /* get lw */ + return SCPE_OK; + } +switch (ofs) { + + case MCRA_OF: /* CR A */ + *val = mcr_a[mctl] & MCRA_RD; + break; + + case MCRB_OF: /* CR B */ + *val = (mcr_b[mctl] & MCRB_RD) | MCRB_INIT; + break; + + case MCRC_OF: /* CR C */ + *val = mcr_c[mctl] & (extmem? MCRC_E_RD: MCRC_C_RD); + break; + + case MCRD_OF: /* CR D */ + if (!extmem) return SCPE_NXM; /* MS780E only */ + *val = mcr_d[mctl] & MCRC_E_RD; + break; + + default: + return SCPE_NXM; + } + +return SCPE_OK; +} + +/* Memory controller register write */ + +t_stat mctl_wrreg (int32 val, int32 pa, int32 lnt) +{ +int32 mctl, ofs, mask; +t_bool extmem = MEMSIZE > MAXMEMSIZE; + +if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ + printf (">>MCTL: invalid adapter write mask, pa = %X, lnt = %d\r\n", pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + return SCPE_OK; + } +mctl = NEXUS_GETNEX (pa) - TR_MCTL0; /* get mctl num */ +ofs = NEXUS_GETOFS (pa); /* get offset */ +switch (ofs) { + + case MCRA_OF: /* CR A */ + mask = MCRA_WR | ((val & MCRA_ILVE)? MCRA_ILV: 0); + mcr_a[mctl] = (mcr_a[mctl] & ~mask) | (val & mask); + break; + + case MCRB_OF: /* CR B */ + mask = MCRB_WR | ((val & MCRB_SAE)? MCRB_SA: 0); + mcr_b[mctl] = (mcr_b[mctl] & ~mask) | (val & mask); + break; + + case MCRC_OF: /* CR C */ + mcr_c[mctl] = ((mcr_c[mctl] & ~MCRC_WR) | (val & MCRC_WR)) & + ~(val & (extmem? MCRC_E_W1C: MCRC_C_W1C)); + break; + + case MCRD_OF: /* CR D */ + if (!extmem) return SCPE_NXM; /* MS780E only */ + mcr_d[mctl] = ((mcr_d[mctl] & ~MCRC_WR) | (val & MCRC_WR)) & + ~(val & MCRC_E_W1C); + break; + + default: + return SCPE_NXM; + } + +return SCPE_OK; +} + +/* Used by CPU and loader */ + +void rom_wr_B (int32 pa, int32 val) +{ +uint32 mctl = NEXUS_GETNEX (pa) - TR_MCTL0; /* get mctl num */ +uint32 ofs = NEXUS_GETOFS (pa) - MCRROM_OF; /* get offset */ +int32 sc = (pa & 3) << 3; + +rom_lw[mctl][ofs] = ((val & 0xFF) << sc) | (rom_lw[mctl][ofs] & ~(0xFF << sc)); +return; +} + +/* MEMCTL reset */ + +t_stat mctl_reset (DEVICE *dptr) +{ +int32 i, amb; +t_bool extmem = MEMSIZE > MAXMEMSIZE; + +amb = (int32) (MEMSIZE / 2) >> 20; /* array size MB */ +for (i = 0; i < MCTL_NUM; i++) { /* init for MS780C */ + if (extmem) { /* extended memory? */ + mcr_a[i] = ((amb - 1) << MCRA_V_SIZE) | MCRA_E_TYPE; + mcr_b[i] = MCRB_INIT | ((i * amb) << (MCRB_V_SA + 4)); + } + else { + mcr_a[i] = MCRA_C_SIZE | MCRA_C_TYPE; + mcr_b[i] = MCRB_INIT | (i << 21); + } + mcr_c[i] = 0; + mcr_d[i] = 0; + } +return SCPE_OK; +} diff --git a/VAX/vax780_sbi.c b/VAX/vax780_sbi.c new file mode 100644 index 0000000..be7088f --- /dev/null +++ b/VAX/vax780_sbi.c @@ -0,0 +1,753 @@ +/* vax780_sbi.c: VAX 11/780 SBI + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module contains the VAX 11/780 system-specific registers and devices. + + sbi bus controller + + 31-May-2008 RMS Fixed machine_check calling sequence (found by Peter Schorn) + 03-May-2006 RMS Fixed writes to ACCS + 28-May-08 RMS Inlined physical memory routines +*/ + +#include "vax_defs.h" + +/* 11/780 specific IPRs */ + +/* Writeable control store */ + +#define WCSA_RW 0xFFFF /* writeable */ +#define WCSA_ADDR 0x1FFF /* addr */ +#define WCSA_CTR 0x6000 /* counter */ +#define WCSA_CTR_INC 0x2000 /* increment */ +#define WCSA_CTR_MAX 0x6000 /* max value */ +#define WCSD_RD_VAL 0xFF /* fixed read val */ +#define WCSD_WR 0xFFFFFFFF /* write */ +#define MBRK_RW 0x1FFF /* microbreak */ + +/* System registers */ + +#define SBIFS_RD (0x031F0000|SBI_FAULTS) /* SBI faults */ +#define SBIFS_WR 0x03140000 +#define SBIFS_W1C 0x00080000 + +#define SBISC_RD 0xFFFF0000 /* SBI silo comp */ +#define SBISC_WR 0x7FFF0000 +#define SBISC_LOCK 0x80000000 /* lock */ + +#define SBIMT_RD 0xFFFFFF00 /* SBI maint */ +#define SBIMT_WR 0xFFFFF900 + +#define SBIER_CRDIE 0x00008000 /* SBI error, CRD IE */ +#define SBIER_CRD 0x00004000 /* CRD */ +#define SBIER_RDS 0x00002000 /* RDS */ +#define SBIER_TMO 0x00001000 /* timeout */ +#define SBIER_STA 0x00000C00 /* timeout status (0) */ +#define SBIER_CNF 0x00000100 /* error confirm */ +#define SBIER_IBRDS 0x00000080 +#define SBIER_IBTMO 0x00000040 +#define SBIER_IBSTA 0x00000030 +#define SBIER_IBCNF 0x00000008 +#define SBIER_MULT 0x00000004 /* multiple errors */ +#define SBIER_FREE 0x00000002 /* SBI free */ +#define SBIER_RD 0x0000FDFE +#define SBIER_WR 0x00008000 +#define SBIER_W1C 0x000070C0 +#define SBIER_TMOW1C (SBIER_TMO|SBIER_STA|SBIER_CNF|SBIER_MULT) +#define SBIER_IBTW1C (SBIER_IBTMO|SBIER_STA|SBIER_IBCNF) + +#define SBITMO_V_MODE 30 /* mode */ +#define SBITMO_VIRT 0x20000000 /* physical */ + +#define SBIQC_MBZ 0xC0000007 /* MBZ */ + +/* VAX-11/780 boot device definitions */ + +struct boot_dev { + char *name; + int32 code; + int32 let; + }; + +uint32 wcs_addr = 0; +uint32 wcs_data = 0; +uint32 wcs_mbrk = 0; +uint32 nexus_req[NEXUS_HLVL]; /* nexus int req */ +uint32 sbi_fs = 0; /* SBI fault status */ +uint32 sbi_sc = 0; /* SBI silo comparator */ +uint32 sbi_mt = 0; /* SBI maintenance */ +uint32 sbi_er = 0; /* SBI error status */ +uint32 sbi_tmo = 0; /* SBI timeout addr */ + +static t_stat (*nexusR[NEXUS_NUM])(int32 *dat, int32 ad, int32 md); +static t_stat (*nexusW[NEXUS_NUM])(int32 dat, int32 ad, int32 md); + +static struct boot_dev boot_tab[] = { + { "RP", BOOT_MB, 0 }, + { "HK", BOOT_HK, 0 }, + { "RL", BOOT_RL, 0 }, + { "RQ", BOOT_UDA, 1 << 24 }, + { "TQ", BOOT_TK, 1 << 24 }, + { NULL } + }; + +extern int32 R[16]; +extern int32 PSL; +extern int32 ASTLVL, SISR; +extern int32 mapen, pme, trpirq; +extern int32 in_ie; +extern int32 mchk_va, mchk_ref; +extern int32 crd_err, mem_err, hlt_pin; +extern int32 tmr_int, tti_int, tto_int; +extern jmp_buf save_env; +extern int32 p1; +extern int32 sim_switches; +extern DEVICE *sim_devices[]; +extern FILE *sim_log; +extern CTAB *sim_vm_cmd; + +t_stat sbi_reset (DEVICE *dptr); +void sbi_set_tmo (int32 pa); +void uba_eval_int (void); +t_stat vax780_boot (int32 flag, char *ptr); + +extern t_stat vax780_fload (int flag, char *cptr); +extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); +extern int32 iccs_rd (void); +extern int32 nicr_rd (void); +extern int32 icr_rd (t_bool interp); +extern int32 todr_rd (void); +extern int32 rxcs_rd (void); +extern int32 rxdb_rd (void); +extern int32 txcs_rd (void); +extern void iccs_wr (int32 dat); +extern void nicr_wr (int32 dat); +extern void todr_wr (int32 dat); +extern void rxcs_wr (int32 dat); +extern void txcs_wr (int32 dat); +extern void txdb_wr (int32 dat); +extern void init_mbus_tab (void); +extern void init_ubus_tab (void); +extern t_stat build_mbus_tab (DEVICE *dptr, DIB *dibp); +extern t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp); + +/* SBI data structures + + sbi_dev SBI device descriptor + sbi_unit SBI unit + sbi_reg SBI register list +*/ + +UNIT sbi_unit = { UDATA (NULL, 0, 0) }; + +REG sbi_reg[] = { + { HRDATA (NREQ14, nexus_req[0], 16) }, + { HRDATA (NREQ15, nexus_req[1], 16) }, + { HRDATA (NREQ16, nexus_req[2], 16) }, + { HRDATA (NREQ17, nexus_req[3], 16) }, + { HRDATA (WCSA, wcs_addr, 16) }, + { HRDATA (WCSD, wcs_data, 32) }, + { HRDATA (MBRK, wcs_mbrk, 13) }, + { HRDATA (SBIFS, sbi_fs, 32) }, + { HRDATA (SBISC, sbi_sc, 32) }, + { HRDATA (SBIMT, sbi_mt, 32) }, + { HRDATA (SBIER, sbi_er, 32) }, + { HRDATA (SBITMO, sbi_tmo, 32) }, + { NULL } + }; + +DEVICE sbi_dev = { + "SBI", &sbi_unit, sbi_reg, NULL, + 1, 16, 16, 1, 16, 8, + NULL, NULL, &sbi_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* Special boot command, overrides regular boot */ + +CTAB vax780_cmd[] = { + { "BOOT", &vax780_boot, RU_BOOT, + "bo{ot} {/R5:flg} boot device\n" }, + { "FLOAD", &vax780_fload, 0, + "fl{oad} {} load file from console floppy\n" }, + { NULL } + }; + +/* The VAX 11/780 has three sources of interrupts + + - internal device interrupts (CPU, console, clock) + - nexus interupts (e.g., memory controller, MBA, UBA) + - external device interrupts (Unibus) + + Internal devices vector to fixed SCB locations. + + Nexus interrupts vector to an SCB location based on this + formula: SCB_NEXUS + ((IPL - 0x14) * 0x40) + (TR# * 0x4) + + External device interrupts do not vector directly. + Instead, the interrupt handler for a given UBA IPL + reads a vector register that contains the Unibus vector + for that IPL. + +/* Find highest priority vectorable interrupt */ + +int32 eval_int (void) +{ +int32 ipl = PSL_GETIPL (PSL); +int32 i, t; + +static const int32 sw_int_mask[IPL_SMAX] = { + 0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, /* 0 - 3 */ + 0xFFE0, 0xFFC0, 0xFF80, 0xFF00, /* 4 - 7 */ + 0xFE00, 0xFC00, 0xF800, 0xF000, /* 8 - B */ + 0xE000, 0xC000, 0x8000 /* C - E */ + }; + +if (hlt_pin) return IPL_HLTPIN; /* hlt pin int */ +if ((ipl < IPL_MEMERR) && mem_err) return IPL_MEMERR; /* mem err int */ +if ((ipl < IPL_CRDERR) && crd_err) return IPL_CRDERR; /* crd err int */ +if ((ipl < IPL_CLKINT) && tmr_int) return IPL_CLKINT; /* clock int */ +uba_eval_int (); /* update UBA */ +for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */ + if (i <= ipl) return 0; /* at ipl? no int */ + if (nexus_req[i - IPL_HMIN]) return i; /* req != 0? int */ + } +if ((ipl < IPL_TTINT) && (tti_int || tto_int)) /* console int */ + return IPL_TTINT; +if (ipl >= IPL_SMAX) return 0; /* ipl >= sw max? */ +if ((t = SISR & sw_int_mask[ipl]) == 0) return 0; /* eligible req */ +for (i = IPL_SMAX; i > ipl; i--) { /* check swre int */ + if ((t >> i) & 1) return i; /* req != 0? int */ + } +return 0; +} + +/* Return vector for highest priority hardware interrupt at IPL lvl */ + +int32 get_vector (int32 lvl) +{ +int32 i, l; + +if (lvl == IPL_MEMERR) { /* mem error? */ + mem_err = 0; + return SCB_MEMERR; + } +if (lvl == IPL_CRDERR) { /* CRD error? */ + crd_err = 0; + return SCB_CRDERR; + } +if (lvl == IPL_CLKINT) { /* clock? */ + tmr_int = 0; /* clear req */ + return SCB_INTTIM; /* return vector */ + } +if (lvl > IPL_HMAX) { /* error req lvl? */ + ABORT (STOP_UIPL); /* unknown intr */ + } +if ((lvl <= IPL_HMAX) && (lvl >= IPL_HMIN)) { /* nexus? */ + l = lvl - IPL_HMIN; + for (i = 0; nexus_req[l] && (i < NEXUS_NUM); i++) { + if ((nexus_req[l] >> i) & 1) { + nexus_req[l] = nexus_req[l] & ~(1u << i); + return SCB_NEXUS + (l << 6) + (i << 2); /* return vector */ + } + } + } +if (lvl == IPL_TTINT) { /* console? */ + if (tti_int) { /* input? */ + tti_int = 0; /* clear req */ + return SCB_TTI; /* return vector */ + } + if (tto_int) { /* output? */ + tto_int = 0; /* clear req */ + return SCB_TTO; /* return vector */ + } + } +return 0; +} + +/* Read 780-specific IPR's */ + +int32 ReadIPR (int32 rg) +{ +int32 val; + +switch (rg) { + + case MT_ICCS: /* ICCS */ + val = iccs_rd (); + break; + + case MT_NICR: /* NICR */ + val = nicr_rd (); + break; + + case MT_ICR: /* ICR */ + val = icr_rd (FALSE); + break; + + case MT_TODR: /* TODR */ + val = todr_rd (); + break; + + case MT_ACCS: /* ACCS (not impl) */ + val = 0; + break; + + case MT_WCSA: /* WCSA */ + val = wcs_addr & WCSA_RW; + break; + + case MT_WCSD: /* WCSD */ + val = WCSD_RD_VAL; + break; + + case MT_RXCS: /* RXCS */ + val = rxcs_rd (); + break; + + case MT_RXDB: /* RXDB */ + val = rxdb_rd (); + break; + + case MT_TXCS: /* TXCS */ + val = txcs_rd (); + break; + + case MT_SBIFS: /* SBIFS */ + val = sbi_fs & SBIFS_RD; + break; + + case MT_SBIS: /* SBIS */ + val = 0; + break; + + case MT_SBISC: /* SBISC */ + val = sbi_sc & SBISC_RD; + break; + + case MT_SBIMT: /* SBIMT */ + val = sbi_mt & SBIMT_RD; + break; + + case MT_SBIER: /* SBIER */ + val = sbi_er & SBIER_RD; + break; + + case MT_SBITA: /* SBITA */ + val = sbi_tmo; + break; + + case MT_MBRK: /* MBRK */ + val = wcs_mbrk & MBRK_RW; + break; + + case MT_SID: /* SID */ + val = VAX780_SID | VAX780_ECO | VAX780_PLANT | VAX780_SN; + break; + + default: + RSVD_OPND_FAULT; + } + +return val; +} + +/* Write 780-specific IPR's */ + +void WriteIPR (int32 rg, int32 val) +{ +switch (rg) { + + case MT_ICCS: /* ICCS */ + iccs_wr (val); + break; + + case MT_NICR: /* NICR */ + nicr_wr (val); + break; + + case MT_TODR: /* TODR */ + todr_wr (val); + break; + + case MT_ACCS: /* ACCS (not impl) */ + break; + + case MT_WCSA: /* WCSA */ + wcs_addr = val & WCSA_RW; + break; + + case MT_WCSD: /* WCSD */ + wcs_data = val & WCSD_WR; + wcs_addr = (wcs_addr & ~WCSA_CTR) | + ((wcs_addr + WCSA_CTR_INC) & WCSA_CTR); + if ((wcs_addr & WCSA_CTR) == WCSA_CTR_MAX) + wcs_addr = (wcs_addr & ~WCSA_ADDR) | + ((wcs_addr + 1) & WCSA_ADDR); + break; + + case MT_RXCS: /* RXCS */ + rxcs_wr (val); + break; + + case MT_TXCS: /* TXCS */ + txcs_wr (val); + break; + + case MT_TXDB: /* TXDB */ + txdb_wr (val); + break; + + case MT_SBIFS: /* SBIFS */ + sbi_fs = (sbi_fs & ~SBIFS_WR) | (val & SBIFS_WR); + sbi_fs = sbi_fs & ~(val & SBIFS_W1C); + break; + + case MT_SBISC: /* SBISC */ + sbi_sc = (sbi_sc & ~(SBISC_LOCK|SBISC_WR)) | (val & SBISC_WR); + break; + + case MT_SBIMT: /* SBIMT */ + sbi_mt = (sbi_mt & ~SBIMT_WR) | (val & SBIMT_WR); + break; + + case MT_SBIER: /* SBIER */ + sbi_er = (sbi_er & ~SBIER_WR) | (val & SBIER_WR); + sbi_er = sbi_er & ~(val & SBIER_W1C); + if (val & SBIER_TMO) sbi_er = sbi_er & ~SBIER_TMOW1C; + if (val & SBIER_IBTMO) sbi_er = sbi_er & ~SBIER_IBTW1C; + if ((sbi_er & SBIER_CRDIE) && (sbi_er & SBIER_CRD)) + crd_err = 1; + else crd_err = 0; + break; + + case MT_SBIQC: /* SBIQC */ + if (val & SBIQC_MBZ) { RSVD_OPND_FAULT; } + WriteLP (val, 0); + WriteLP (val + 4, 0); + break; + + case MT_MBRK: /* MBRK */ + wcs_mbrk = val & MBRK_RW; + break; + + default: + RSVD_OPND_FAULT; + } + +return; +} + +/* ReadReg - read register space + + Inputs: + pa = physical address + lnt = length (BWLQ) + Output: + longword of data +*/ + +int32 ReadReg (int32 pa, int32 lnt) +{ +int32 nexus, val; + +if (ADDR_IS_REG (pa)) { /* reg space? */ + nexus = NEXUS_GETNEX (pa); /* get nexus */ + if (nexusR[nexus] && /* valid? */ + (nexusR[nexus] (&val, pa, lnt) == SCPE_OK)) { + SET_IRQL; + return val; + } + } +sbi_set_tmo (pa); /* timeout */ +MACH_CHECK (MCHK_RD_F); /* machine check */ +return 0; +} + +/* WriteReg - write register space + + Inputs: + pa = physical address + val = data to write, right justified in 32b longword + lnt = length (BWLQ) + Outputs: + none +*/ + +void WriteReg (int32 pa, int32 val, int32 lnt) +{ +int32 nexus; + +if (ADDR_IS_REG (pa)) { /* reg space? */ + nexus = NEXUS_GETNEX (pa); /* get nexus */ + if (nexusW[nexus] && /* valid? */ + (nexusW[nexus] (val, pa, lnt) == SCPE_OK)) { + SET_IRQL; + return; + } + } +sbi_set_tmo (pa); /* timeout */ +mem_err = 1; /* interrupt */ +eval_int (); +return; +} + +/* Set SBI timeout - machine checks only on reads */ + +void sbi_set_tmo (int32 pa) +{ +if ((sbi_er & SBIER_TMO) == 0) { /* not yet set? */ + sbi_tmo = pa >> 2; /* save addr */ + if (mchk_ref == REF_V) sbi_tmo |= SBITMO_VIRT | /* virt? add mode */ + (PSL_GETCUR (PSL) << SBITMO_V_MODE); + sbi_er |= SBIER_TMO; /* set tmo flag */ + } +else sbi_er |= SBIER_MULT; /* yes, multiple */ +return; +} + +/* Set SBI error confirmation - always machine checks */ + +void sbi_set_errcnf (void) +{ +if (sbi_er & SBIER_CNF) sbi_er |= SBIER_MULT; +else sbi_er |= SBIER_CNF; +MACH_CHECK (MCHK_RD_F); +return; +} + +/* Machine check + + Error status word format + <2:0> = ASTLVL + <3> = PME + <6:4> = arith trap code + Rest will be zero +*/ + +int32 machine_check (int32 p1, int32 opc, int32 cc, int32 delta) +{ +int32 acc, err; + +err = (GET_TRAP (trpirq) << 4) | (pme << 3) | ASTLVL; /* error word */ +cc = intexc (SCB_MCHK, cc, 0, IE_SVE); /* take exception */ +acc = ACC_MASK (KERN); /* in kernel mode */ +in_ie = 1; +SP = SP - 44; /* push 11 words */ +Write (SP, 40, L_LONG, WA); /* # bytes */ +Write (SP + 4, p1, L_LONG, WA); /* mcheck type */ +Write (SP + 8, err, L_LONG, WA); /* CPU error status */ +Write (SP + 12, 0, L_LONG, WA); /* uPC */ +Write (SP + 16, mchk_va, L_LONG, WA); /* VA */ +Write (SP + 20, 0, L_LONG, WA); /* D register */ +Write (SP + 24, mapen, L_LONG, WA); /* TB status 1 */ +Write (SP + 28, 0, L_LONG, WA); /* TB status 2 */ +Write (SP + 32, sbi_tmo, L_LONG, WA); /* SBI timeout addr */ +Write (SP + 36, 0, L_LONG, WA); /* cache status */ +Write (SP + 40, sbi_er, L_LONG, WA); /* SBI error */ +in_ie = 0; +sbi_er = sbi_er & ~SBIER_TMOW1C; /* clr SBIER etc */ +return cc; +} + +/* Console entry */ + +int32 con_halt (int32 code, int32 cc) +{ +ABORT (STOP_HALT); +return cc; +} + +/* Special boot command - linked into SCP by initial reset + + Syntax: BOOT {/R5:val} + + Sets up R0-R5, calls SCP boot processor with effective BOOT CPU +*/ + +t_stat vax780_boot (int32 flag, char *ptr) +{ +char gbuf[CBUFSIZE]; +char *slptr, *regptr; +int32 i, r5v, unitno; +DEVICE *dptr; +UNIT *uptr; +DIB *dibp; +t_stat r; + +regptr = get_glyph (ptr, gbuf, 0); /* get glyph */ +if (slptr = strchr (gbuf, '/')) { /* found slash? */ + regptr = strchr (ptr, '/'); /* locate orig */ + *slptr = 0; /* zero in string */ + } +dptr = find_unit (gbuf, &uptr); /* find device */ +if ((dptr == NULL) || (uptr == NULL)) return SCPE_ARG; +dibp = (DIB *) dptr->ctxt; /* get DIB */ +if (dibp == NULL) return SCPE_ARG; +unitno = (int32) (uptr - dptr->units); +r5v = 0; +if ((strncmp (regptr, "/R5:", 4) == 0) || + (strncmp (regptr, "/R5=", 4) == 0) || + (strncmp (regptr, "/r5:", 4) == 0) || + (strncmp (regptr, "/r5=", 4) == 0)) { + r5v = (int32) get_uint (regptr + 4, 16, LMASK, &r); + if (r != SCPE_OK) return r; + } +else if (*regptr != 0) return SCPE_ARG; +for (i = 0; boot_tab[i].name != NULL; i++) { + if (strcmp (dptr->name, boot_tab[i].name) == 0) { + R[0] = boot_tab[i].code; + if (dptr->flags & DEV_MBUS) { + R[1] = dibp->ba + TR_MBA0; + R[2] = unitno; + } + else { + R[1] = TR_UBA; + R[2] = boot_tab[i].let | (dibp->ba & UBADDRMASK); + } + R[3] = unitno; + R[4] = 0; + R[5] = r5v; + return run_cmd (flag, "CPU"); + } + } +return SCPE_NOFNC; +} + +/* Bootstrap - finish up bootstrap process */ + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ +t_stat r; + +printf ("Loading boot code from vmb.exe\n"); +if (sim_log) fprintf (sim_log, + "Loading boot code from vmb.exe\n"); +r = load_cmd (0, "-O vmb.exe 200"); +if (r != SCPE_OK) return r; +SP = PC = 512; +return SCPE_OK; +} + +/* SBI reset */ + +t_stat sbi_reset (DEVICE *dptr) +{ +wcs_addr = 0; +wcs_data = 0; +wcs_mbrk = 0; +sbi_fs = 0; +sbi_sc = 0; +sbi_mt = 0; +sbi_er = 0; +sbi_tmo = 0; +sim_vm_cmd = vax780_cmd; +return SCPE_OK; +} + +/* Show nexus */ + +t_stat show_nexus (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "nexus=%d", val); +return SCPE_OK; +} + +/* Init nexus tables */ + +void init_nexus_tab (void) +{ +uint32 i; + +for (i = 0; i < NEXUS_NUM; i++) { + nexusR[i] = NULL; + nexusW[i] = NULL; + } +return; +} + +/* Build nexus tables + + Inputs: + dptr = pointer to device + dibp = pointer to DIB + Outputs: + status +*/ + + +t_stat build_nexus_tab (DEVICE *dptr, DIB *dibp) +{ +uint32 idx; + +if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; +idx = dibp->ba; +if (idx >= NEXUS_NUM) return SCPE_IERR; +if ((nexusR[idx] && dibp->rd && /* conflict? */ + (nexusR[idx] != dibp->rd)) || + (nexusW[idx] && dibp->wr && + (nexusW[idx] != dibp->wr))) { + printf ("Nexus %s conflict at %d\n", sim_dname (dptr), dibp->ba); + if (sim_log) fprintf (sim_log, + "Nexus %s conflict at %d\n", sim_dname (dptr), dibp->ba); + return SCPE_STOP; + } +if (dibp->rd) nexusR[idx] = dibp->rd; /* set rd dispatch */ +if (dibp->wr) nexusW[idx] = dibp->wr; /* set wr dispatch */ +return SCPE_OK; +} + +/* Build dib_tab from device list */ + +t_stat build_dib_tab (void) +{ +uint32 i; +DEVICE *dptr; +DIB *dibp; +t_stat r; + +init_nexus_tab (); +init_ubus_tab (); +init_mbus_tab (); +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ + if (dptr->flags & DEV_NEXUS) { /* Nexus? */ + if (r = build_nexus_tab (dptr, dibp)) /* add to dispatch table */ + return r; + } + else if (dptr->flags & DEV_MBUS) { /* Massbus? */ + if (r = build_mbus_tab (dptr, dibp)) + return r; + } + else { /* no, Unibus device */ + if (r = build_ubus_tab (dptr, dibp)) /* add to dispatch tab */ + return r; + } /* end else */ + } /* end if enabled */ + } /* end for */ +return SCPE_OK; +} diff --git a/VAX/vax780_stddev.c b/VAX/vax780_stddev.c new file mode 100644 index 0000000..d5ce4af --- /dev/null +++ b/VAX/vax780_stddev.c @@ -0,0 +1,941 @@ +/* vax780_stddev.c: VAX 11/780 standard I/O devices + + Copyright (c) 1998-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti console input + tto console output + rx console floppy + todr TODR clock + tmr interval timer + + 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock + 29-Oct-2006 RMS Added clock coscheduler function + Synced keyboard to clock for idling + 11-May-06 RMS Revised timer logic for EVKAE + 22-Nov-05 RMS Revised for new terminal processing routines + 10-Mar-05 RMS Fixed bug in timer schedule routine (from Mark Hittinger) + 08-Sep-04 RMS Cloned from vax_stddev.c, vax_sysdev.c, and pdp11_rx.c + + The console floppy protocol is based on the description in the 1982 VAX + Architecture Reference Manual: + + TXDB<11:8> = 0 -> normal console output + TXDB<11:8> = 1 -> data output to floppy + TXDB<11:8> = 3 -> read communications region + TXDB<11:8> = 9 -> command output to floppy + TXDB<11:8> = F -> flag output (e.g., reboot) + + RXDB<11:8> = 0 -> normal terminal input + RXDB<11:8> = 1 -> data input from floppy + RXDB<11:8> = 3 -> communications region data + RXDB<11:8> = 2 -> status input from floppy + RXDB<11:8> = 9 -> "command" input from floppy (protocol error) +*/ + +#include "vax_defs.h" +#include + +/* Terminal definitions */ + +#define RXCS_RD (CSR_DONE + CSR_IE) /* terminal input */ +#define RXCS_WR (CSR_IE) +#define RXDB_ERR 0x8000 /* error */ +#define RXDB_OVR 0x4000 /* overrun */ +#define RXDB_FRM 0x2000 /* framing error */ +#define TXCS_RD (CSR_DONE + CSR_IE) /* terminal output */ +#define TXCS_WR (CSR_IE) +#define TXDB_V_SEL 8 /* unit select */ +#define TXDB_M_SEL 0xF +#define TXDB_FDAT 0x1 /* floppy data */ +#define TXDB_COMM 0x3 /* console mem read */ +#define TXDB_FCMD 0x9 /* floppy cmd */ +#define TXDB_MISC 0xF /* console misc */ +#define COMM_LNT 0200 /* comm region lnt */ +#define COMM_MASK (COMM_LNT - 1) /* comm region mask */ +#define COMM_GH 0144 /* GH flag */ +#define COMM_WRMS 0145 /* warm start */ +#define COMM_CLDS 0146 /* cold start */ +#define COMM_APTL 0147 /* APT load */ +#define COMM_LAST 0150 /* last position */ +#define COMM_AUTO 0151 /* auto restart */ +#define COMM_PCSV 0152 /* PCS version */ +#define COMM_WCSV 0153 /* WCS version */ +#define COMM_WCSS 0154 /* WCS secondary */ +#define COMM_FPLV 0155 /* FPLA version */ +#define COMM_DATA 0x300 /* comm data return */ +#define MISC_MASK 0xFF /* console data mask */ +#define MISC_SWDN 0x1 /* software done */ +#define MISC_BOOT 0x2 /* reboot */ +#define MISC_CLWS 0x3 /* clear warm start */ +#define MISC_CLCS 0x4 /* clear cold start */ +#define TXDB_SEL (TXDB_M_SEL << TXDB_V_SEL) /* non-terminal */ +#define TXDB_GETSEL(x) (((x) >> TXDB_V_SEL) & TXDB_M_SEL) + +/* Clock definitions */ + +#define TMR_CSR_ERR 0x80000000 /* error W1C */ +#define TMR_CSR_DON 0x00000080 /* done W1C */ +#define TMR_CSR_IE 0x00000040 /* int enb RW */ +#define TMR_CSR_SGL 0x00000020 /* single WO */ +#define TMR_CSR_XFR 0x00000010 /* xfer WO */ +#define TMR_CSR_RUN 0x00000001 /* run RW */ +#define TMR_CSR_RD (TMR_CSR_W1C | TMR_CSR_WR) +#define TMR_CSR_W1C (TMR_CSR_ERR | TMR_CSR_DON) +#define TMR_CSR_WR (TMR_CSR_IE | TMR_CSR_RUN) +#define TMR_INC 10000 /* usec/interval */ +#define CLK_DELAY 5000 /* 100 Hz */ +#define TMXR_MULT 1 /* 100 Hz */ + +/* Floppy definitions */ + +#define FL_NUMTR 77 /* tracks/disk */ +#define FL_M_TRACK 0377 +#define FL_NUMSC 26 /* sectors/track */ +#define FL_M_SECTOR 0177 +#define FL_NUMBY 128 /* bytes/sector */ +#define FL_SIZE (FL_NUMTR * FL_NUMSC * FL_NUMBY) /* bytes/disk */ +#define UNIT_V_WLK (UNIT_V_UF) /* write locked */ +#define UNIT_WLK (1u << UNIT_V_UF) +#define UNIT_WPRT (UNIT_WLK | UNIT_RO) /* write protect */ + +#define FL_IDLE 0 /* idle state */ +#define FL_RWDS 1 /* rw, sect next */ +#define FL_RWDT 2 /* rw, track next */ +#define FL_READ 3 /* read */ +#define FL_READ1 4 +#define FL_WRITE 5 /* write */ +#define FL_WRITE1 6 +#define FL_FILL 7 /* fill buffer */ +#define FL_EMPTY 8 /* empty buffer */ +#define FL_READSTA 9 /* read status */ +#define FL_DONE 10 /* cmd done */ + +#define FL_V_FNC 0 /* floppy function */ +#define FL_M_FNC 0xFF +#define FL_FNCRD 0x0 /* read */ +#define FL_FNCWR 0x1 /* write */ +#define FL_FNCRS 0x2 /* read status */ +#define FL_FNCWD 0x3 /* write del data */ +#define FL_FNCCA 0x4 /* cancel */ +#define FL_CDATA 0x100 /* returned data */ +#define FL_CDONE 0x200 /* completion code */ +#define FL_STACRC 0x001 /* status bits */ +#define FL_STAPAR 0x002 +#define FL_STAINC 0x004 +#define FL_STADDA 0x040 +#define FL_STAERR 0x080 +#define FL_CPROT 0x905 /* protocol error */ +#define FL_GETFNC(x) (((x) >> FL_V_FNC) & FL_M_FNC) + +#define TRACK u3 /* current track */ +#define CALC_DA(t,s) (((t) * FL_NUMSC) + ((s) - 1)) * FL_NUMBY + +int32 tti_csr = 0; /* control/status */ +int32 tti_buf = 0; /* buffer */ +int32 tti_int = 0; /* interrupt */ +int32 tto_csr = 0; /* control/status */ +int32 tto_buf = 0; /* buffer */ +int32 tto_int = 0; /* interrupt */ + +int32 tmr_iccs = 0; /* interval timer csr */ +uint32 tmr_icr = 0; /* curr interval */ +uint32 tmr_nicr = 0; /* next interval */ +uint32 tmr_inc = 0; /* timer increment */ +int32 tmr_sav = 0; /* timer save */ +int32 tmr_int = 0; /* interrupt */ +int32 tmr_use_100hz = 1; /* use 100Hz for timer */ +int32 clk_tps = 100; /* ticks/second */ +int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ +int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ +int32 todr_reg = 0; /* TODR register */ + +int32 fl_fnc = 0; /* function */ +int32 fl_esr = 0; /* error status */ +int32 fl_ecode = 0; /* error code */ +int32 fl_track = 0; /* desired track */ +int32 fl_sector = 0; /* desired sector */ +int32 fl_state = FL_IDLE; /* controller state */ +int32 fl_stopioe = 1; /* stop on error */ +int32 fl_swait = 100; /* seek, per track */ +int32 fl_cwait = 50; /* command time */ +int32 fl_xwait = 20; /* tr set time */ +uint8 fl_buf[FL_NUMBY] = { 0 }; /* sector buffer */ +int32 fl_bptr = 0; /* buffer pointer */ + +uint8 comm_region[COMM_LNT] = { 0 }; /* comm region */ + +extern int32 sim_switches; +extern jmp_buf save_env; + +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat clk_svc (UNIT *uptr); +t_stat tmr_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat clk_reset (DEVICE *dptr); +t_stat tmr_reset (DEVICE *dptr); +t_stat fl_svc (UNIT *uptr); +t_stat fl_reset (DEVICE *dptr); +int32 icr_rd (t_bool interp); +void tmr_incr (uint32 inc); +void tmr_sched (void); +t_stat todr_powerup (void); +t_stat fl_wr_txdb (int32 data); +t_bool fl_test_xfr (UNIT *uptr, t_bool wr); +void fl_protocol_error (void); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list +*/ + +UNIT tti_unit = { UDATA (&tti_svc, TT_MODE_8B, 0), 0 }; + +REG tti_reg[] = { + { HRDATA (RXDB, tti_buf, 16) }, + { HRDATA (RXCS, tti_csr, 16) }, + { FLDATA (INT, tti_int, 0) }, + { FLDATA (DONE, tti_csr, CSR_V_DONE) }, + { FLDATA (IE, tti_csr, CSR_V_IE) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 16, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_8B, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { HRDATA (TXDB, tto_buf, 16) }, + { HRDATA (TXCS, tto_csr, 16) }, + { FLDATA (INT, tto_int, 0) }, + { FLDATA (DONE, tto_csr, CSR_V_DONE) }, + { FLDATA (IE, tto_csr, CSR_V_IE) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT + REG_NZ }, + { NULL } + }; + +MTAB tto_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 16, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* TODR and TMR data structures */ + +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; /* 100Hz */ + +REG clk_reg[] = { + { DRDATA (TODR, todr_reg, 32), PV_LEFT }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (TPS, clk_tps, 8), REG_HIDDEN + REG_NZ + PV_LEFT }, + { NULL } + }; + +DEVICE clk_dev = { + "TODR", &clk_unit, clk_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +UNIT tmr_unit = { UDATA (&tmr_svc, 0, 0) }; /* timer */ + +REG tmr_reg[] = { + { HRDATA (ICCS, tmr_iccs, 32) }, + { HRDATA (ICR, tmr_icr, 32) }, + { HRDATA (NICR, tmr_nicr, 32) }, + { HRDATA (INCR, tmr_inc, 32), REG_HIDDEN }, + { HRDATA (SAVE, tmr_sav, 32), REG_HIDDEN }, + { FLDATA (USE100HZ, tmr_use_100hz, 0), REG_HIDDEN }, + { FLDATA (INT, tmr_int, 0) }, + { NULL } + }; + +DEVICE tmr_dev = { + "TMR", &tmr_unit, tmr_reg, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &tmr_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* RX01 data structures + + fl_dev RX device descriptor + fl_unit RX unit list + fl_reg RX register list + fl_mod RX modifier list +*/ + +UNIT fl_unit = { UDATA (&fl_svc, + UNIT_FIX+UNIT_ATTABLE+UNIT_BUFABLE+UNIT_MUSTBUF, FL_SIZE) }; + +REG fl_reg[] = { + { HRDATA (FNC, fl_fnc, 8) }, + { HRDATA (ES, fl_esr, 8) }, + { HRDATA (ECODE, fl_ecode, 8) }, + { HRDATA (TA, fl_track, 8) }, + { HRDATA (SA, fl_sector, 8) }, + { DRDATA (STATE, fl_state, 4), REG_RO }, + { DRDATA (BPTR, fl_bptr, 7) }, + { DRDATA (CTIME, fl_cwait, 24), PV_LEFT }, + { DRDATA (STIME, fl_swait, 24), PV_LEFT }, + { DRDATA (XTIME, fl_xwait, 24), PV_LEFT }, + { FLDATA (STOP_IOE, fl_stopioe, 0) }, + { BRDATA (DBUF, fl_buf, 16, 8, FL_NUMBY) }, + { BRDATA (COMM, comm_region, 16, 8, COMM_LNT) }, + { NULL } + }; + +MTAB fl_mod[] = { + { UNIT_WLK, 0, "write enabled", "WRITEENABLED", NULL }, + { UNIT_WLK, UNIT_WLK, "write locked", "LOCKED", NULL }, + { 0 } + }; + +DEVICE fl_dev = { + "RX", &fl_unit, fl_reg, fl_mod, + 1, DEV_RDX, 20, 1, DEV_RDX, 8, + NULL, NULL, &fl_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* Terminal MxPR routines + + rxcs_rd/wr input control/status + rxdb_rd input buffer + txcs_rd/wr output control/status + txdb_wr output buffer +*/ + +int32 rxcs_rd (void) +{ +return (tti_csr & RXCS_RD); +} + +void rxcs_wr (int32 data) +{ +if ((data & CSR_IE) == 0) tto_int = 0; +else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + tti_int = 1; +tti_csr = (tti_csr & ~RXCS_WR) | (data & RXCS_WR); +return; +} + +int32 rxdb_rd (void) +{ +int32 t = tti_buf; /* char + error */ + +tti_csr = tti_csr & ~CSR_DONE; /* clr done */ +tti_buf = tti_buf & BMASK; /* clr errors */ +tti_int = 0; +return t; +} + +int32 txcs_rd (void) +{ +return (tto_csr & TXCS_RD); +} + +void txcs_wr (int32 data) +{ +if ((data & CSR_IE) == 0) tto_int = 0; +else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + tto_int = 1; +tto_csr = (tto_csr & ~TXCS_WR) | (data & TXCS_WR); +return; +} + +void txdb_wr (int32 data) +{ +tto_buf = data & WMASK; /* save data */ +tto_csr = tto_csr & ~CSR_DONE; /* clear flag */ +tto_int = 0; /* clear int */ +if (tto_buf & TXDB_SEL) fl_wr_txdb (tto_buf); /* floppy? */ +else sim_activate (&tto_unit, tto_unit.wait); /* no, console */ +return; +} + +/* Terminal input service (poll for character) */ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c; + +sim_activate (uptr, KBD_WAIT (uptr->wait, tmr_poll)); /* continue poll */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +if (c & SCPE_BREAK) /* break? */ + tti_buf = RXDB_ERR | RXDB_FRM; +else tti_buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); +uptr->pos = uptr->pos + 1; +tti_csr = tti_csr | CSR_DONE; +if (tti_csr & CSR_IE) tti_int = 1; +return SCPE_OK; +} + +/* Terminal input reset */ + +t_stat tti_reset (DEVICE *dptr) +{ +tti_buf = 0; +tti_csr = 0; +tti_int = 0; +sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); +return SCPE_OK; +} + +/* Terminal output service (output character) */ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +if ((tto_buf & TXDB_SEL) == 0) { /* for console? */ + c = sim_tt_outcvt (tto_buf, TT_GET_MODE (uptr->flags)); + if (c >= 0) { + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* retry */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } + } + uptr->pos = uptr->pos + 1; + } +tto_csr = tto_csr | CSR_DONE; +if (tto_csr & CSR_IE) tto_int = 1; +return SCPE_OK; +} + +/* Terminal output reset */ + +t_stat tto_reset (DEVICE *dptr) +{ +tto_buf = 0; +tto_csr = CSR_DONE; +tto_int = 0; +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Programmable timer + + The architected VAX timer, which increments at 1Mhz, cannot be + accurately simulated due to the overhead that would be required + for 1M clock events per second. Instead, a hidden calibrated + 100Hz timer is run (because that's what VMS expects), and a + hack is used for the interval timer. + + When the timer is started, the timer interval is inspected. + + if the interval is >= 10msec, then the 100Hz timer drives the + next interval + if the interval is < 10mec, then count instructions + + If the interval register is read, then its value between events + is interpolated using the current instruction count versus the + count when the most recent event started, the result is scaled + to the calibrated system clock, unless the interval being timed + is less than a calibrated system clock tick (or the calibrated + clock is running very slowly) at which time the result will be + the elapsed instruction count. +*/ + +int32 iccs_rd (void) +{ +return tmr_iccs & TMR_CSR_RD; +} + +void iccs_wr (int32 val) +{ +if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */ + sim_cancel (&tmr_unit); /* cancel timer */ + tmr_use_100hz = 0; + if (tmr_iccs & TMR_CSR_RUN) /* run 1 -> 0? */ + tmr_icr = icr_rd (TRUE); /* update itr */ + } +tmr_iccs = tmr_iccs & ~(val & TMR_CSR_W1C); /* W1C csr */ +tmr_iccs = (tmr_iccs & ~TMR_CSR_WR) | /* new r/w */ + (val & TMR_CSR_WR); +if (val & TMR_CSR_XFR) tmr_icr = tmr_nicr; /* xfr set? */ +if (val & TMR_CSR_RUN) { /* run? */ + if (val & TMR_CSR_XFR) /* new tir? */ + sim_cancel (&tmr_unit); /* stop prev */ + if (!sim_is_active (&tmr_unit)) /* not running? */ + tmr_sched (); /* activate */ + } +else if (val & TMR_CSR_SGL) { /* single step? */ + tmr_incr (1); /* incr tmr */ + if (tmr_icr == 0) /* if ovflo, */ + tmr_icr = tmr_nicr; /* reload tir */ + } +if ((tmr_iccs & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ + (TMR_CSR_DON | TMR_CSR_IE)) tmr_int = 0; +return; +} + +int32 icr_rd (t_bool interp) +{ +uint32 delta; + +if (interp || (tmr_iccs & TMR_CSR_RUN)) { /* interp, running? */ + delta = sim_grtime () - tmr_sav; /* delta inst */ + if (tmr_use_100hz && (tmr_poll > TMR_INC)) /* scale large int */ + delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll); + if (delta >= tmr_inc) delta = tmr_inc - 1; + return tmr_icr + delta; + } +return tmr_icr; +} + +int32 nicr_rd () +{ +return tmr_nicr; +} + +void nicr_wr (int32 val) +{ +tmr_nicr = val; +} + +/* 100Hz base clock unit service */ + +t_stat clk_svc (UNIT *uptr) +{ +tmr_poll = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ +sim_activate (&clk_unit, tmr_poll); /* reactivate unit */ +tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ +todr_reg = todr_reg + 1; /* incr TODR */ +if ((tmr_iccs & TMR_CSR_RUN) && tmr_use_100hz) /* timer on, std intvl? */ + tmr_incr (TMR_INC); /* do timer service */ +return SCPE_OK; +} + +/* Interval timer unit service */ + +t_stat tmr_svc (UNIT *uptr) +{ +tmr_incr (tmr_inc); /* incr timer */ +return SCPE_OK; +} + +/* Timer increment */ + +void tmr_incr (uint32 inc) +{ +uint32 new_icr = (tmr_icr + inc) & LMASK; /* add incr */ + +if (new_icr < tmr_icr) { /* ovflo? */ + tmr_icr = 0; /* now 0 */ + if (tmr_iccs & TMR_CSR_DON) /* done? set err */ + tmr_iccs = tmr_iccs | TMR_CSR_ERR; + else tmr_iccs = tmr_iccs | TMR_CSR_DON; /* set done */ + if (tmr_iccs & TMR_CSR_RUN) { /* run? */ + tmr_icr = tmr_nicr; /* reload */ + tmr_sched (); /* reactivate */ + } + if (tmr_iccs & TMR_CSR_IE) tmr_int = 1; /* ie? set int req */ + else tmr_int = 0; + } +else { + tmr_icr = new_icr; /* no, update icr */ + if (tmr_iccs & TMR_CSR_RUN) /* still running? */ + tmr_sched (); /* reactivate */ + } +return; +} + +/* Timer scheduling */ + +void tmr_sched (void) +{ +tmr_sav = sim_grtime (); /* save intvl base */ +tmr_inc = (~tmr_icr + 1); /* inc = interval */ +if (tmr_inc == 0) tmr_inc = 1; +if (tmr_inc < TMR_INC) { /* 100Hz multiple? */ + sim_activate (&tmr_unit, tmr_inc); /* schedule timer */ + tmr_use_100hz = 0; + } +else tmr_use_100hz = 1; /* let clk handle */ +return; +} + +/* Clock coscheduling routine */ + +int32 clk_cosched (int32 wait) +{ +int32 t; + +t = sim_is_active (&clk_unit); +return (t? t - 1: wait); +} + +/* 100Hz clock reset */ + +t_stat clk_reset (DEVICE *dptr) +{ +tmr_poll = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init 100Hz timer */ +sim_activate_abs (&clk_unit, tmr_poll); /* activate 100Hz unit */ +tmxr_poll = tmr_poll * TMXR_MULT; /* set mux poll */ +return SCPE_OK; +} + +/* Interval timer reset */ + +t_stat tmr_reset (DEVICE *dptr) +{ +tmr_iccs = 0; +tmr_icr = 0; +tmr_nicr = 0; +tmr_int = 0; +tmr_use_100hz = 1; +sim_cancel (&tmr_unit); /* cancel timer */ +if (sim_switches & SWMASK ('P')) todr_powerup (); /* powerup? set TODR */ +return SCPE_OK; +} + +/* TODR routines */ + +int32 todr_rd (void) +{ +return todr_reg; +} + +void todr_wr (int32 data) +{ +todr_reg = data; +return; +} + +t_stat todr_powerup (void) +{ +uint32 base; +time_t curr; +struct tm *ctm; + +curr = time (NULL); /* get curr time */ +if (curr == (time_t) -1) return SCPE_NOFNC; /* error? */ +ctm = localtime (&curr); /* decompose */ +if (ctm == NULL) return SCPE_NOFNC; /* error? */ +base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ + ctm->tm_hour) * 60) + + ctm->tm_min) * 60) + + ctm->tm_sec; +todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ +return SCPE_OK; +} + +/* Console write, txdb<11:8> != 0 (console unit) */ + +t_stat fl_wr_txdb (int32 data) +{ +int32 sel = TXDB_GETSEL (data); /* get selection */ + +if (sel == TXDB_FCMD) { /* floppy command? */ + fl_fnc = FL_GETFNC (data); /* get function */ + if (fl_state != FL_IDLE) switch (fl_fnc) { /* cmd in prog? */ + + case FL_FNCCA: /* cancel? */ + sim_cancel (&fl_unit); /* stop op */ + fl_state = FL_DONE; + break; + + default: /* all others */ + fl_protocol_error (); + return SCPE_OK; + } + + else switch (fl_fnc) { /* idle, case */ + + case FL_FNCRS: /* read status */ + fl_state = FL_READSTA; + break; + + case FL_FNCCA: /* cancel, nop */ + fl_state = FL_DONE; + break; + + case FL_FNCRD: case FL_FNCWR: /* data xfer */ + case FL_FNCWD: + fl_esr = 0; /* clear errors */ + fl_ecode = 0; + fl_bptr = 0; /* init buffer */ + fl_state = FL_RWDS; /* sector next */ + break; + + default: /* all others */ + fl_protocol_error (); + return SCPE_OK; + } + + sim_activate (&fl_unit, fl_cwait); /* sched command */ + } /* end command */ +else if (sel == TXDB_FDAT) { /* floppy data? */ + switch (fl_state) { /* data */ + + case FL_RWDS: /* expecting sector */ + fl_sector = data & FL_M_SECTOR; + fl_state = FL_RWDT; + break; + + case FL_RWDT: /* expecting track */ + fl_track = data & FL_M_TRACK; + if (fl_fnc == FL_FNCRD) fl_state = FL_READ; + else fl_state = FL_FILL; + break; + + case FL_FILL: /* expecting wr data */ + fl_buf[fl_bptr++] = data & BMASK; + if (fl_bptr >= FL_NUMBY) fl_state = FL_WRITE; + break; + + default: + fl_protocol_error (); + return SCPE_OK; + } + + sim_activate (&fl_unit, fl_xwait); /* schedule xfer */ + } /* end else data */ +else { + sim_activate (&tto_unit, tto_unit.wait); /* set up timeout */ + if (sel == TXDB_COMM) { /* read comm region? */ + data = data & COMM_MASK; /* byte to select */ + tti_buf = comm_region[data] | COMM_DATA; + tti_csr = tti_csr | CSR_DONE; /* set input flag */ + if (tti_csr & CSR_IE) tti_int = 1; + } + else if (sel == TXDB_MISC) { /* misc function? */ + switch (data & MISC_MASK) { /* case on function */ + case MISC_CLWS: + comm_region[COMM_WRMS] = 0; + case MISC_CLCS: + comm_region[COMM_CLDS] = 0; + break; + case MISC_SWDN: + ABORT (STOP_SWDN); + break; + case MISC_BOOT: + ABORT (STOP_BOOT); + break; + } + } + } +return SCPE_OK; +} + +/* Unit service; the action to be taken depends on the transfer state: + + FL_IDLE Should never get here + FL_RWDS Set TXCS (driver sends sector, sets FL_RWDT) + FL_RWDT Set TXCS (driver sends track, sets FL_READ/FL_FILL) + FL_READ Set TXCS, schedule FL_READ1 + FL_READ1 Read sector, schedule FL_EMPTY + FL_EMPTY Copy data to RXDB, set RXCS + if fl_bptr >= max, schedule completion, else continue + FL_FILL Set TXCS (driver sends next byte, sets FL_WRITE) + FL_WRITE Set TXCS, schedule FL_WRITE1 + FL_WRITE1 Write sector, schedule FL_DONE + FL_DONE Copy requested data to TXDB, set FL_IDLE +*/ + +t_stat fl_svc (UNIT *uptr) +{ +int32 i, t; +uint32 da; +int8 *fbuf = uptr->filebuf; + +switch (fl_state) { /* case on state */ + + case FL_IDLE: /* idle */ + return SCPE_IERR; /* done */ + + case FL_READ: case FL_WRITE: /* read, write */ + fl_state = fl_state + 1; /* set next state */ + t = abs (fl_track - uptr->TRACK); /* # tracks to seek */ + if (t == 0) t = 1; /* minimum 1 */ + sim_activate (uptr, fl_swait * t); /* schedule seek */ + /* fall thru, set flag */ + case FL_RWDS: case FL_RWDT: case FL_FILL: /* rwds, rwdt, fill */ + tto_csr = tto_csr | CSR_DONE; /* set output done */ + if (tto_csr & CSR_IE) tto_int = 1; + break; + + case FL_READ1: /* read, seek done */ + if (fl_test_xfr (uptr, FALSE)) { /* transfer ok? */ + da = CALC_DA (fl_track, fl_sector); /* get disk address */ + for (i = 0; i < FL_NUMBY; i++) /* copy sector to buf */ + fl_buf[i] = fbuf[da + i]; + tti_buf = fl_esr | FL_CDONE; /* completion code */ + tti_csr = tti_csr | CSR_DONE; /* set input flag */ + if (tti_csr & CSR_IE) tti_int = 1; + fl_state = FL_EMPTY; /* go empty */ + } + else fl_state = FL_DONE; /* error? cmd done */ + sim_activate (uptr, fl_xwait); /* schedule next */ + break; + + case FL_EMPTY: /* empty buffer */ + if ((tti_csr & CSR_DONE) == 0) { /* prev data taken? */ + tti_buf = FL_CDATA | fl_buf[fl_bptr++]; /* get next byte */ + tti_csr = tti_csr | CSR_DONE; /* set input flag */ + if (tti_csr & CSR_IE) tti_int = 1; + if (fl_bptr >= FL_NUMBY) { /* buffer empty? */ + fl_state = FL_IDLE; /* cmd done */ + break; + } + } + sim_activate (uptr, fl_xwait); /* schedule next */ + break; + + case FL_WRITE1: /* write, seek done */ + if (fl_test_xfr (uptr, TRUE)) { /* transfer ok? */ + da = CALC_DA (fl_track, fl_sector); /* get disk address */ + for (i = 0; i < FL_NUMBY; i++) /* copy buf to sector */ + fbuf[da + i] = fl_buf[i]; + da = da + FL_NUMBY; + if (da > uptr->hwmark) uptr->hwmark = da; /* update hwmark */ + } + if (fl_fnc == FL_FNCWD) fl_esr |= FL_STADDA; /* wrdel? set status*/ + fl_state = FL_DONE; /* command done */ + sim_activate (uptr, fl_xwait); /* schedule */ + break; + + case FL_DONE: /* command done */ + if (tti_csr & CSR_DONE) /* input buf empty? */ + sim_activate (uptr, fl_xwait); /* no, wait */ + else { /* yes */ + tti_buf = fl_esr | FL_CDONE; /* completion code */ + tti_csr = tti_csr | CSR_DONE; /* set input flag */ + if (tti_csr & CSR_IE) tti_int = 1; + fl_state = FL_IDLE; /* floppy idle */ + } + break; + + case FL_READSTA: /* read status */ + if ((tti_csr & CSR_DONE) == 0) { /* input buf empty? */ + tti_buf = fl_ecode; /* return err code */ + tti_csr = tti_csr | CSR_DONE; /* set input flag */ + if (tti_csr & CSR_IE) tti_int = 1; + fl_state = FL_DONE; /* command done */ + } + sim_activate (uptr, fl_xwait); + break; + } +return SCPE_OK; +} + +/* Test for data transfer okay */ + +t_bool fl_test_xfr (UNIT *uptr, t_bool wr) +{ +if ((uptr->flags & UNIT_BUF) == 0) /* not buffered? */ + fl_ecode = 0110; +else if (fl_track >= FL_NUMTR) /* bad track? */ + fl_ecode = 0040; /* done, error */ +else if ((fl_sector == 0) || (fl_sector > FL_NUMSC)) /* bad sect? */ + fl_ecode = 0070; /* done, error */ +else if (wr && (uptr->flags & UNIT_WPRT)) /* write and locked? */ + fl_ecode = 0100; /* done, error */ +else { + uptr->TRACK = fl_track; /* now on track */ + return TRUE; + } +fl_esr = fl_esr | FL_STAERR; /* set error */ +return FALSE; +} + +/* Set protocol error */ + +void fl_protocol_error (void) +{ +if ((tto_csr & CSR_DONE) == 0) { /* output busy? */ + tto_csr = tto_csr | CSR_DONE; /* set done */ + if (tto_csr & CSR_IE) tto_int = 1; + } +if ((tti_csr & CSR_DONE) == 0) { /* input idle? */ + tti_csr = tti_csr | CSR_DONE; /* set done */ + if (tti_csr & CSR_IE) tti_int = 1; + } +tti_buf = FL_CPROT; /* status */ +fl_state = FL_IDLE; /* floppy idle */ +return; +} + +/* Reset */ + +t_stat fl_reset (DEVICE *dptr) +{ +uint32 i; + +fl_esr = FL_STAINC; +fl_ecode = 0; /* clear error */ +fl_sector = 0; /* clear addr */ +fl_track = 0; +fl_state = FL_IDLE; /* ctrl idle */ +fl_bptr = 0; +sim_cancel (&fl_unit); /* cancel drive */ +fl_unit.TRACK = 0; +for (i = 0; i < COMM_LNT; i++) comm_region[i] = 0; +comm_region[COMM_FPLV] = VER_FPLA; +comm_region[COMM_PCSV] = VER_PCS; +comm_region[COMM_WCSV] = VER_WCSP; +comm_region[COMM_WCSS] = VER_WCSS; +comm_region[COMM_GH] = 1; +return SCPE_OK; +} diff --git a/VAX/vax780_syslist.c b/VAX/vax780_syslist.c new file mode 100644 index 0000000..6514c54 --- /dev/null +++ b/VAX/vax780_syslist.c @@ -0,0 +1,138 @@ +/* vax_syslist.c: VAX device list + + Copyright (c) 1998-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 17-May-06 RMS Added CR11/CD11 support (from John Dundas) + 01-Oct-04 RMS Cloned from vax_sys.c +*/ + +#include "vax_defs.h" + +char sim_name[] = "VAX780"; + +extern DEVICE cpu_dev; +extern DEVICE tlb_dev; +extern DEVICE sbi_dev; +extern DEVICE mctl_dev[MCTL_NUM]; +extern DEVICE uba_dev; +extern DEVICE mba_dev[MBA_NUM]; +extern DEVICE clk_dev; +extern DEVICE tmr_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE fl_dev; +extern DEVICE cr_dev; +extern DEVICE lpt_dev; +extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; +extern DEVICE rl_dev; +extern DEVICE hk_dev; +extern DEVICE rp_dev; +extern DEVICE ry_dev; +extern DEVICE ts_dev; +extern DEVICE tq_dev; +extern DEVICE tu_dev; +extern DEVICE dz_dev; +extern DEVICE xu_dev, xub_dev; + +extern int32 sim_switches; +extern UNIT cpu_unit; +extern void WriteB (uint32 pa, int32 val); +extern void rom_wr_B (int32 pa, int32 val); + +DEVICE *sim_devices[] = { + &cpu_dev, + &tlb_dev, + &sbi_dev, + &mctl_dev[0], + &mctl_dev[1], + &uba_dev, + &mba_dev[0], + &mba_dev[1], + &clk_dev, + &tmr_dev, + &tti_dev, + &tto_dev, + &fl_dev, + &dz_dev, + &cr_dev, + &lpt_dev, + &rp_dev, + &rl_dev, + &hk_dev, + &rq_dev, + &rqb_dev, + &rqc_dev, + &rqd_dev, + &ry_dev, + &tu_dev, + &ts_dev, + &tq_dev, + &xu_dev, + &xub_dev, + NULL + }; + +/* Binary loader + + The binary loader handles absolute system images, that is, system + images linked /SYSTEM. These are simply a byte stream, with no + origin or relocation information. + + -r load ROM0 + -s load ROM1 + -o for memory, specify origin +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +t_stat r; +int32 val; +uint32 origin, limit; + +if (flag) return SCPE_ARG; /* dump? */ +origin = 0; /* memory */ +limit = (uint32) cpu_unit.capac; +if (sim_switches & SWMASK ('O')) { /* origin? */ + origin = (int32) get_uint (cptr, 16, 0xFFFFFFFF, &r); + if (r != SCPE_OK) return SCPE_ARG; + } + +while ((val = getc (fileref)) != EOF) { /* read byte stream */ + if (sim_switches & SWMASK ('R')) { /* ROM0? */ + if (origin >= ROMSIZE) return SCPE_NXM; + rom_wr_B (ROM0BASE + origin, val); + } + else if (sim_switches & SWMASK ('S')) { /* ROM1? */ + if (origin >= ROMSIZE) return SCPE_NXM; + rom_wr_B (ROM1BASE + origin, val); + } + else { + if (origin >= limit) return SCPE_NXM; /* NXM? */ + WriteB (origin, val); /* memory */ + } + origin = origin + 1; + } +return SCPE_OK; +} + + diff --git a/VAX/vax780_uba.c b/VAX/vax780_uba.c new file mode 100644 index 0000000..206e059 --- /dev/null +++ b/VAX/vax780_uba.c @@ -0,0 +1,1322 @@ +/* vax780_uba.c: VAX 11/780 Unibus adapter + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + uba DW780 Unibus adapter + + 28-May-08 RMS Inlined physical memory routines + 25-Jan-08 RMS Fixed declarations (from Mark Pizzolato) +*/ + +#include "vax_defs.h" + +/* Unibus adapter */ + +#define UBA_NDPATH 16 /* number of data paths */ +#define UBA_NMAPR 496 /* number of map reg */ + +/* Unibus configuration register */ + +#define UBACNF_OF 0x00 +#define UBACNF_ADPDN 0x00800000 /* adap pdn - ni */ +#define UBACNF_ADPUP 0x00400000 /* adap pup - ni */ +#define UBACNF_UBINIT 0x00040000 /* UB INIT - ni */ +#define UBACNF_UBPDN 0x00020000 /* UB pdn */ +#define UBACNF_UBIC 0x00010000 /* UB init done */ +#define UBACNF_CODE 0x00000028 /* adapter code */ +#define UBACNF_RD (SBI_FAULTS|UBACNF_W1C) +#define UBACNF_W1C 0x00C70000 + +/* Control register */ + +#define UBACR_OF 0x01 +#define UBACR_V_DSB 26 /* map disable */ +#define UBACR_M_DSB 0x1F +#define UBACR_GETDSB(x) (((x) >> (UBACR_V_DSB - 4)) & (UBACR_M_DSB << 4)) +#define UBACR_IFS 0x00000040 /* SBI field intr */ +#define UBACR_BRIE 0x00000020 /* BR int enable */ +#define UBACR_USEFIE 0x00000010 /* UB to SBI int enb */ +#define UBACR_SUEFIE 0x00000008 /* SBI to UB int enb */ +#define UBACR_CNFIE 0x00000004 /* config int enb */ +#define UBACR_UPF 0x00000002 /* power fail UB */ +#define UBACR_ADINIT 0x00000001 /* adapter init */ +#define UBACR_RD ((UBACR_M_DSB << UBACR_V_DSB) | 0x0000007E) +#define UBACR_WR (UBACR_RD) + +#define UBA_USEFIE_SR (UBASR_RDTO|UBASR_RDS|UBASR_CRD|UBASR_CXTER| \ + UBASR_CXTO|UBASR_DPPE|UBASR_IVMR|UBASR_MRPF) +#define UBA_SUEFIE_SR (UBASR_UBSTO|UBASR_UBTMO) +#define UBA_CNFIE_CR (UBACNF_ADPDN|UBACNF_ADPUP|UBACNF_UBINIT|\ + UBACNF_UBPDN|UBACNF_UBIC) + +/* Status register */ + +#define UBASR_OF 0x02 +#define UBASR_V_BR4 24 /* filled br's - ni */ +#define UBASR_RDTO 0x00000400 /* read tmo - ni */ +#define UBASR_RDS 0x00000200 /* read sub - ni */ +#define UBASR_CRD 0x00000100 /* read crd - ni */ +#define UBASR_CXTER 0x00000080 /* cmd xfr err - ni */ +#define UBASR_CXTO 0x00000040 /* cmd xfr tmo - ni */ +#define UBASR_DPPE 0x00000020 /* parity err - ni */ +#define UBASR_IVMR 0x00000010 /* invalid map reg */ +#define UBASR_MRPF 0x00000008 /* map reg par - ni */ +#define UBASR_LEB 0x00000004 /* locked err */ +#define UBASR_UBSTO 0x00000002 /* UB select tmo - ni */ +#define UBASR_UBTMO 0x00000001 /* UB nxm */ +#define UBASR_RD 0x0F0007FF +#define UBASR_W1C 0x000007FF + +/* Diagnostic register */ + +#define UBADR_OF 0x03 +#define UBADR_SPARE 0x80000000 /* spare */ +#define UBADR_DINTR 0x40000000 /* disable intr */ +#define UBADR_DMP 0x20000000 +#define UBADR_DDPP 0x10000000 +#define UBADR_MICOK 0x08000000 /* microseq ok */ +#define UBADR_RD 0xF8000000 +#define UBADR_WR 0xF0000000 +#define UBADR_CNF_RD 0x07FFFFFF + +/* Failing map entry - read only */ + +#define UBAFMER_OF 0x04 +#define UBAFMER_OF1 0x06 +#define UBAFMER_RD 0x1FF + +/* Failing Unibus address - read only */ + +#define UBAFUBAR_OF 0x05 +#define UBAFUBAR_OF1 0x07 +#define UBAFUBAR_RD 0xFFFF + +/* Spare registers - read/write */ + +#define UBABRSVR_OF 0x08 + +/* Vector registers - read only */ + +#define UBABRRVR_OF 0x0C +#define UBA_UVEC 0x80000000 + +/* Data path registers */ + +#define UBADPR_OF 0x010 +#define UBADPR_BNE 0x80000000 /* buf not empty - ni */ +#define UBADPR_BTE 0x40000000 /* buf xfr err - ni */ +#define UBADPR_DIR 0x20000000 /* buf rd/wr */ +#define UBADPR_STATE 0x00FF0000 /* byte full state - ni */ +#define UBADPR_UA 0x0000FFFF /* Unibus addr<17:2> */ +#define UBADPR_UA 0x0000FFFF /* last UB addr */ +#define UBADPR_RD 0xC0FFFFFF +#define UBADPR_W1C 0xC0000000 + +/* Map registers */ + +#define UBAMAP_OF 0x200 +#define UBAMAP_VLD 0x80000000 /* valid */ +#define UBAMAP_LWAE 0x04000000 /* LW access enb - ni */ +#define UBAMAP_ODD 0x02000000 /* odd byte */ +#define UBAMAP_V_DP 21 /* data path */ +#define UBAMAP_M_DP 0xF +#define UBAMAP_DP (UBAMAP_M_DP << UBAMAP_V_DP) +#define UBAMAP_GETDP(x) (((x) >> UBAMAP_V_DP) & UBAMAP_M_DP) +#define UBAMAP_PAG 0x001FFFFF +#define UBAMAP_RD (0x86000000 | UBAMAP_DP | UBAMAP_PAG) +#define UBAMAP_WR (UBAMAP_RD) + +/* Debug switches */ + +#define UBA_DEB_RRD 0x01 /* reg reads */ +#define UBA_DEB_RWR 0x02 /* reg writes */ +#define UBA_DEB_MRD 0x04 /* map reads */ +#define UBA_DEB_MWR 0x08 /* map writes */ +#define UBA_DEB_XFR 0x10 /* transfers */ +#define UBA_DEB_ERR 0x20 /* errors */ + +int32 int_req[IPL_HLVL] = { 0 }; /* intr, IPL 14-17 */ +uint32 uba_cnf = 0; /* config reg */ +uint32 uba_cr = 0; /* control reg */ +uint32 uba_sr = 0; /* status reg */ +uint32 uba_dr = 0; /* diag ctrl */ +uint32 uba_int = 0; /* UBA interrupt */ +uint32 uba_fmer = 0; /* failing map reg */ +uint32 uba_fubar = 0; /* failing Unibus addr */ +uint32 uba_svr[4] = { 0 }; /* diag registers */ +uint32 uba_rvr[4] = { 0 }; /* vector reg */ +uint32 uba_dpr[UBA_NDPATH] = { 0 }; /* number data paths */ +uint32 uba_map[UBA_NMAPR] = { 0 }; /* map registers */ +uint32 uba_aiip = 0; /* adapter init in prog */ +uint32 uba_uiip = 0; /* Unibus init in prog */ +uint32 uba_aitime = 250; /* adapter init time */ +uint32 uba_uitime = 12250; /* Unibus init time */ +int32 autcon_enb = 1; /* autoconfig enable */ + +extern int32 trpirq; +extern int32 autcon_enb; +extern jmp_buf save_env; +extern DEVICE *sim_devices[]; +extern UNIT cpu_unit; +extern uint32 nexus_req[NEXUS_HLVL]; +extern int32 sim_switches; +extern FILE *sim_log, *sim_deb; + +t_stat uba_svc (UNIT *uptr); +t_stat uba_reset (DEVICE *dptr); +t_stat uba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat uba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_stat uba_rdreg (int32 *val, int32 pa, int32 mode); +t_stat uba_wrreg (int32 val, int32 pa, int32 lnt); +int32 uba_get_ubvector (int32 lvl); +void uba_ub_nxm (int32 ua); +void uba_inv_map (int32 ublk); +void uba_eval_int (void); +void uba_adap_set_int (int32 flg); +void uba_adap_clr_int (); +void uba_set_dpr (uint32 ua, t_bool wr); +void uba_ubpdn (int32 time); +t_bool uba_map_addr (uint32 ua, uint32 *ma); +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat uba_show_virt (FILE *st, UNIT *uptr, int32 val, void *desc); + +extern int32 eval_int (void); +extern t_stat build_dib_tab (void); + +/* Unibus IO page dispatches */ + +static t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); +static t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); +static DIB *iodibp[IOPAGESIZE >> 1]; + +/* Unibus interrupt request to interrupt action map */ + +int32 (*int_ack[IPL_HLVL][32])(); /* int ack routines */ + +/* Unibus interrupt request to vector map */ + +int32 int_vec[IPL_HLVL][32]; /* int req to vector */ + +/* Unibus adapter data structures + + uba_dev UBA device descriptor + uba_unit UBA units + uba_reg UBA register list +*/ + +DIB uba_dib = { TR_UBA, 0, &uba_rdreg, &uba_wrreg, 0, 0 }; + +UNIT uba_unit = { UDATA (&uba_svc, 0, 0) }; + +REG uba_reg[] = { + { HRDATA (IPL14, int_req[0], 32), REG_RO }, + { HRDATA (IPL15, int_req[1], 32), REG_RO }, + { HRDATA (IPL16, int_req[2], 32), REG_RO }, + { HRDATA (IPL17, int_req[3], 32), REG_RO }, + { HRDATA (CNFR, uba_cnf, 32) }, + { HRDATA (CR, uba_cr, 32) }, + { HRDATA (SR, uba_sr, 32) }, + { HRDATA (DR, uba_dr, 32) }, + { FLDATA (INT, uba_int, 0) }, + { FLDATA (NEXINT, nexus_req[IPL_UBA], TR_UBA) }, + { FLDATA (AIIP, uba_aiip, 0) }, + { FLDATA (UIIP, uba_uiip, 0) }, + { HRDATA (FMER, uba_fmer, 32) }, + { HRDATA (FUBAR, uba_fubar, 32) }, + { HRDATA (BRSVR0, uba_svr[0], 32) }, + { HRDATA (BRSVR1, uba_svr[1], 32) }, + { HRDATA (BRSVR2, uba_svr[2], 32) }, + { HRDATA (BRSVR3, uba_svr[3], 32) }, + { HRDATA (BRRVR4, uba_rvr[0], 32) }, + { HRDATA (BRRVR5, uba_rvr[1], 32) }, + { HRDATA (BRRVR6, uba_rvr[2], 32) }, + { HRDATA (BRRVR7, uba_rvr[3], 32) }, + { BRDATA (DPR, uba_dpr, 16, 32, 16) }, + { BRDATA (MAP, uba_map, 16, 32, 496) }, + { DRDATA (AITIME, uba_aitime, 24), PV_LEFT + REG_NZ }, + { DRDATA (UITIME, uba_uitime, 24), PV_LEFT + REG_NZ }, + { FLDATA (AUTOCON, autcon_enb, 0), REG_HRO }, + { NULL } + }; + +MTAB uba_mod[] = { + { MTAB_XTD|MTAB_VDV, TR_UBA, "NEXUS", NULL, + NULL, &show_nexus }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, + NULL, &show_iospace }, + { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG", + &set_autocon, &show_autocon }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG", + &set_autocon, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, + NULL, &uba_show_virt }, + { 0 } + }; + +DEBTAB uba_deb[] = { + { "REGREAD", UBA_DEB_RRD }, + { "REGWRITE", UBA_DEB_RWR }, + { "MAPREAD", UBA_DEB_MRD }, + { "MAPWRITE", UBA_DEB_MWR }, + { "XFER", UBA_DEB_XFR }, + { "ERROR", UBA_DEB_ERR }, + { NULL, 0 } + }; + +DEVICE uba_dev = { + "UBA", &uba_unit, uba_reg, uba_mod, + 1, 16, UBADDRWIDTH, 2, 16, 16, + &uba_ex, &uba_dep, &uba_reset, + NULL, NULL, NULL, + &uba_dib, DEV_NEXUS | DEV_DEBUG, 0, + uba_deb, 0, 0 + }; + +/* Read Unibus adapter register - aligned lw only */ + +t_stat uba_rdreg (int32 *val, int32 pa, int32 lnt) +{ +int32 idx, ofs; + +if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ + printf (">>UBA: invalid adapter read mask, pa = %X, lnt = %d\r\n", pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + return SCPE_OK; + } +ofs = NEXUS_GETOFS (pa); /* get offset */ +if (uba_aiip && (ofs != UBACNF_OF) /* init in prog? */ + && (ofs != UBADR_OF)) return SCPE_NXM; /* only cnf, dr */ +if (ofs >= UBAMAP_OF) { /* map? */ + idx = ofs - UBAMAP_OF; + if (idx >= UBA_NMAPR) return SCPE_NXM; /* valid? */ + *val = uba_map[idx] & UBAMAP_RD; + if (DEBUG_PRI (uba_dev, UBA_DEB_MRD)) + fprintf (sim_deb, ">>UBA: map %d read, value = %X\n", idx, *val); + return SCPE_OK; + } + +switch (ofs) { /* case on offset */ + + case UBACNF_OF: /* CNF */ + *val = (uba_cnf & UBACNF_RD) | UBACNF_CODE; + break; + + case UBACR_OF: /* CR */ + *val = uba_cr & UBACR_RD; + break; + + case UBASR_OF: /* SR */ + *val = uba_sr & UBASR_RD; + break; + + case UBADR_OF: /* DR */ + *val = (uba_dr & UBADR_RD) | UBADR_MICOK | + ((uba_cnf & UBADR_CNF_RD) | UBACNF_CODE); + break; + + case UBAFMER_OF: case UBAFMER_OF1: /* FMER */ + *val = uba_fmer & UBAFMER_RD; + break; + + case UBAFUBAR_OF: case UBAFUBAR_OF1: /* FUBAR */ + *val = uba_fubar & UBAFUBAR_RD; + break; + + case UBABRSVR_OF + 0: case UBABRSVR_OF + 1: /* BRSVR */ + case UBABRSVR_OF + 2: case UBABRSVR_OF + 3: + idx = ofs - UBABRSVR_OF; + *val = uba_svr[idx]; + break; + + case UBABRRVR_OF + 0: case UBABRRVR_OF + 1: /* BRRVR */ + case UBABRRVR_OF + 2: case UBABRRVR_OF + 3: + idx = ofs - UBABRRVR_OF; + uba_rvr[idx] = uba_get_ubvector (idx); + *val = uba_rvr[idx]; + break; + + case UBADPR_OF + 0: /* DPR */ + *val = 0; /* direct */ + break; + + case UBADPR_OF + 1: + case UBADPR_OF + 2: case UBADPR_OF + 3: + case UBADPR_OF + 4: case UBADPR_OF + 5: + case UBADPR_OF + 6: case UBADPR_OF + 7: + case UBADPR_OF + 8: case UBADPR_OF + 9: + case UBADPR_OF + 10: case UBADPR_OF + 11: + case UBADPR_OF + 12: case UBADPR_OF + 13: + case UBADPR_OF + 14: case UBADPR_OF + 15: + idx = ofs - UBADPR_OF; + *val = uba_dpr[idx] & UBADPR_RD; + break; + + default: + return SCPE_NXM; + } + +if (DEBUG_PRI (uba_dev, UBA_DEB_RRD)) + fprintf (sim_deb, ">>UBA: reg %d read, value = %X\n", ofs, *val); +return SCPE_OK; +} + +/* Write Unibus adapter register */ + +t_stat uba_wrreg (int32 val, int32 pa, int32 lnt) +{ +int32 idx, ofs, old_cr; + +if ((pa & 3) || (lnt != L_LONG)) { /* unaligned or not lw? */ + printf (">>UBA: invalid adapter write mask, pa = %X, lnt = %d\r\n", pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + return SCPE_OK; + } +ofs = NEXUS_GETOFS (pa); /* get offset */ +if (uba_aiip && (ofs != UBACNF_OF) && (ofs != UBADR_OF) && + (ofs != UBACR_OF) && (ofs != UBASR_OF)) return SCPE_NXM; +if (ofs >= UBAMAP_OF) { /* map? */ + idx = ofs - UBAMAP_OF; + if (idx >= UBA_NMAPR) return SCPE_NXM; /* valid? */ + uba_map[idx] = val & UBAMAP_WR; + if (DEBUG_PRI (uba_dev, UBA_DEB_MWR)) + fprintf (sim_deb, ">>UBA: map %d write, value = %X\n", idx, val); + return SCPE_OK; + } + +switch (ofs) { /* case on offset */ + + case UBACNF_OF: /* CNF */ + uba_cnf = uba_cnf & ~(val & UBACNF_W1C); /* W1C bits */ + uba_adap_clr_int (); /* possible clr int */ + break; + + case UBACR_OF: /* CR */ + old_cr = uba_cr; + if (val & UBACR_ADINIT) { /* adapter init */ + uba_reset (&uba_dev); /* reset adapter */ + uba_aiip = 1; /* set init in prog */ + uba_ubpdn (uba_aitime); /* power fail UB */ + } + if ((val & UBACR_UPF) && !(old_cr & UBACR_UPF) /* Unibus power clear */ + && !sim_is_active (&uba_unit)) + uba_ubpdn (uba_aitime + uba_uitime); /* power fail UB */ + uba_cr = (uba_cr & ~UBACR_WR) | (val & UBACR_WR); + uba_adap_set_int (uba_cr & ~old_cr); /* possible int set */ + uba_adap_clr_int (); /* possible int clr */ + break; + + case UBASR_OF: /* SR */ + uba_sr = uba_sr & ~(val & UBASR_W1C); /* W1C bits */ + uba_adap_clr_int (); /* possible clr int */ + break; + + case UBADR_OF: /* DR */ + uba_dr = (uba_dr & ~UBADR_WR) | (val & UBADR_WR); + uba_cnf = uba_cnf & ~(val & UBACNF_W1C); + uba_adap_clr_int (); /* possible clr int */ + break; + + case UBABRSVR_OF + 0: case UBABRSVR_OF + 1: /* BRSVR */ + case UBABRSVR_OF + 2: case UBABRSVR_OF + 3: + idx = ofs - UBABRSVR_OF; + uba_svr[idx] = val; + break; + + case UBADPR_OF + 0: /* DPR */ + break; /* direct */ + + case UBADPR_OF + 1: + case UBADPR_OF + 2: case UBADPR_OF + 3: + case UBADPR_OF + 4: case UBADPR_OF + 5: + case UBADPR_OF + 6: case UBADPR_OF + 7: + case UBADPR_OF + 8: case UBADPR_OF + 9: + case UBADPR_OF + 10: case UBADPR_OF + 11: + case UBADPR_OF + 12: case UBADPR_OF + 13: + case UBADPR_OF + 14: case UBADPR_OF + 15: + idx = ofs - UBADPR_OF; + uba_dpr[idx] = uba_dpr[idx] & ~(val & UBADPR_W1C); + break; + + default: + return SCPE_NXM; + } + +if (DEBUG_PRI (uba_dev, UBA_DEB_RWR)) + fprintf (sim_deb, ">>UBA: reg %d write, value = %X\n", ofs, val); +return SCPE_OK; +} + +/* Read and write Unibus I/O space */ + +int32 ReadUb (uint32 pa) +{ +int32 idx, val; + +if (ADDR_IS_IOP (pa) && !uba_uiip) { /* iopage,!init */ + idx = (pa & IOPAGEMASK) >> 1; + if (iodispR[idx]) { + iodispR[idx] (&val, pa, READ); + return val; + } + } +uba_ub_nxm (pa); /* UB nxm */ +return 0; +} + +void WriteUb (uint32 pa, int32 val, int32 mode) +{ +int32 idx; + +if (ADDR_IS_IOP (pa) && !uba_uiip) { /* iopage,!init */ + idx = (pa & IOPAGEMASK) >> 1; + if (iodispW[idx]) { + iodispW[idx] (val, pa, mode); + return; + } + } +uba_ub_nxm (pa); /* UB nxm */ +return; +} + +/* ReadIO - read from IO - UBA only responds to byte, aligned word + + Inputs: + pa = physical address + lnt = length (BWLQ) + Output: + longword of data +*/ + +int32 ReadIO (uint32 pa, int32 lnt) +{ +uint32 iod; + +if ((lnt == L_BYTE) || /* byte? */ + ((lnt == L_WORD) && ((pa & 1) == 0))) { /* aligned word? */ + iod = ReadUb (pa); /* DATI from Unibus */ + if (pa & 2) iod = iod << 16; /* position */ + } +else { + printf (">>UBA: invalid read mask, pa = %x, lnt = %d\n", pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + iod = 0; + } +SET_IRQL; +return iod; +} + +/* WriteIO - write to IO - UBA only responds to byte, aligned word + + Inputs: + pa = physical address + val = data to write, right justified in 32b longword + lnt = length (BWL) + Outputs: + none +*/ + +void WriteIO (uint32 pa, int32 val, int32 lnt) +{ +if (lnt == L_BYTE) WriteUb (pa, val, WRITEB); /* byte? DATOB */ +else if ((lnt == L_WORD) && ((pa & 1) == 0)) /* aligned word? */ + WriteUb (pa, val, WRITE); /* DATO */ +else { + printf (">>UBA: invalid write mask, pa = %x, lnt = %d\n", pa, lnt); + sbi_set_errcnf (); /* err confirmation */ + } +SET_IRQL; /* update ints */ +return; +} + +/* Update UBA nexus interrupts */ + +void uba_eval_int (void) +{ +int32 i; + +for (i = 0; i < (IPL_HMAX - IPL_HMIN); i++) /* clear all UBA req */ + nexus_req[i] &= ~(1 << TR_UBA); +if (((uba_dr & UBADR_DINTR) == 0) && !uba_uiip && /* intr enabled? */ + (uba_cr & UBACR_IFS) && (uba_cr & UBACR_BRIE)) { + for (i = 0; i < (IPL_HMAX - IPL_HMIN); i++) { + if (int_req[i]) nexus_req[i] |= (1 << TR_UBA); + } + } +if (uba_int) SET_NEXUS_INT (UBA); /* adapter int? */ +return; +} + +/* Return vector for Unibus interrupt at relative IPL level [0-3] */ + +int32 uba_get_ubvector (int32 lvl) +{ +int32 i, vec; + +vec = 0; +if ((lvl == (IPL_UBA - IPL_HMIN)) && uba_int) { /* UBA lvl, int? */ + vec = UBA_UVEC; /* set flag */ + uba_int = 0; /* clear int */ + } +if (((uba_dr & UBADR_DINTR) == 0) && !uba_uiip && /* intr enabled? */ + (uba_cr & UBACR_IFS) && (uba_cr & UBACR_BRIE)) { + for (i = 0; int_req[lvl] && (i < 32); i++) { + if ((int_req[lvl] >> i) & 1) { + int_req[lvl] = int_req[lvl] & ~(1u << i); + if (int_ack[lvl][i]) return (vec | int_ack[lvl][i]()); + return (vec | int_vec[lvl][i]); + } + } + } +return vec; +} + +/* Unibus I/O buffer routines + + Map_ReadB - fetch byte buffer from memory + Map_ReadW - fetch word buffer from memory + Map_WriteB - store byte buffer into memory + Map_WriteW - store word buffer into memory +*/ + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) +{ +int32 i, j, pbc; +uint32 ma, dat; + +ba = ba & UBADDRMASK; /* mask UB addr */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (!uba_map_addr (ba + i, &ma)) return (bc - i); /* page inv or NXM? */ + pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) + fprintf (sim_deb, ">>UBA: 8b read, ma = %X, bc = %X\n", ma, pbc); + if ((ma | pbc) & 3) { /* aligned LW? */ + for (j = 0; j < pbc; ma++, j++) { /* no, do by bytes */ + *buf++ = ReadB (ma); + } + } + else { /* yes, do by LW */ + for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { + dat = ReadL (ma); /* get lw */ + *buf++ = dat & BMASK; /* low 8b */ + *buf++ = (dat >> 8) & BMASK; /* next 8b */ + *buf++ = (dat >> 16) & BMASK; /* next 8b */ + *buf++ = (dat >> 24) & BMASK; + } + } + uba_set_dpr (ba + i + pbc - L_BYTE, FALSE); + } +return 0; +} + +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) +{ +int32 i, j, pbc; +uint32 ma, dat; + +ba = ba & UBADDRMASK; /* mask UB addr */ +bc = bc & ~01; +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (!uba_map_addr (ba + i, &ma)) return (bc - i); /* page inv or NXM? */ + pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) + fprintf (sim_deb, ">>UBA: 16b read, ma = %X, bc = %X\n", ma, pbc); + if ((ma | pbc) & 1) { /* aligned word? */ + for (j = 0; j < pbc; ma++, j++) { /* no, do by bytes */ + if ((i + j) & 1) { /* odd byte? */ + *buf = (*buf & BMASK) | (ReadB (ma) << 8); + buf++; + } + else *buf = (*buf & ~BMASK) | ReadB (ma); + } + } + else if ((ma | pbc) & 3) { /* aligned LW? */ + for (j = 0; j < pbc; ma = ma + 2, j = j + 2) { /* no, words */ + *buf++ = ReadW (ma); /* get word */ + } + } + else { /* yes, do by LW */ + for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { + dat = ReadL (ma); /* get lw */ + *buf++ = dat & WMASK; /* low 16b */ + *buf++ = (dat >> 16) & WMASK; /* high 16b */ + } + } + uba_set_dpr (ba + i + pbc - L_WORD, FALSE); + } +return 0; +} + +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) +{ +int32 i, j, pbc; +uint32 ma, dat; + +ba = ba & UBADDRMASK; /* mask UB addr */ +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (!uba_map_addr (ba + i, &ma)) return (bc - i); /* page inv or NXM? */ + pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) + fprintf (sim_deb, ">>UBA: 8b write, ma = %X, bc = %X\n", ma, pbc); + if ((ma | pbc) & 3) { /* aligned LW? */ + for (j = 0; j < pbc; ma++, j++) { /* no, do by bytes */ + WriteB (ma, *buf); + buf++; + } + } + else { /* yes, do by LW */ + for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { + dat = (uint32) *buf++; /* get low 8b */ + dat = dat | (((uint32) *buf++) << 8); /* merge next 8b */ + dat = dat | (((uint32) *buf++) << 16); /* merge next 8b */ + dat = dat | (((uint32) *buf++) << 24); /* merge hi 8b */ + WriteL (ma, dat); /* store lw */ + } + } + uba_set_dpr (ba + i + pbc - L_BYTE, TRUE); + } +return 0; +} + +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) +{ +int32 i, j, pbc; +uint32 ma, dat; + +ba = ba & UBADDRMASK; /* mask UB addr */ +bc = bc & ~01; +for (i = 0; i < bc; i = i + pbc) { /* loop by pages */ + if (!uba_map_addr (ba + i, &ma)) return (bc - i); /* page inv or NXM? */ + pbc = VA_PAGSIZE - VA_GETOFF (ma); /* left in page */ + if (pbc > (bc - i)) pbc = bc - i; /* limit to rem xfr */ + if (DEBUG_PRI (uba_dev, UBA_DEB_XFR)) + fprintf (sim_deb, ">>UBA: 16b write, ma = %X, bc = %X\n", ma, pbc); + if ((ma | pbc) & 1) { /* aligned word? */ + for (j = 0; j < pbc; ma++, j++) { /* no, bytes */ + if ((i + j) & 1) { + WriteB (ma, (*buf >> 8) & BMASK); + buf++; + } + else WriteB (ma, *buf & BMASK); + } + } + else if ((ma | pbc) & 3) { /* aligned LW? */ + for (j = 0; j < pbc; ma = ma + 2, j = j + 2) { /* no, words */ + WriteW (ma, *buf); /* write word */ + buf++; + } + } + else { /* yes, do by LW */ + for (j = 0; j < pbc; ma = ma + 4, j = j + 4) { + dat = (uint32) *buf++; /* get low 16b */ + dat = dat | (((uint32) *buf++) << 16); /* merge hi 16b */ + WriteL (ma, dat); /* store LW */ + } + } + uba_set_dpr (ba + i + pbc - L_WORD, TRUE); + } +return 0; +} + +/* Map an address via the translation map */ + +t_bool uba_map_addr (uint32 ua, uint32 *ma) +{ +uint32 ublk, umap; + +ublk = ua >> VA_V_VPN; /* Unibus blk */ +if ((ublk < UBACR_GETDSB (uba_cr)) || /* map disabled? */ + (ublk >= UBA_NMAPR)) return FALSE; /* unimplemented? */ +umap = uba_map[ublk]; /* get map */ +if (umap & UBAMAP_VLD) { /* valid? */ + *ma = ((umap & UBAMAP_PAG) << VA_V_VPN) + VA_GETOFF (ua); + if ((umap & UBAMAP_DP) && (umap & UBAMAP_ODD)) /* buffered dp? */ + *ma = *ma + 1; /* byte offset? */ + return (ADDR_IS_MEM (*ma)); /* legit addr */ + } +uba_inv_map (ua); /* invalid map */ +return FALSE; +} + +/* Map an address via the translation map - console version (no status changes) */ + +t_bool uba_map_addr_c (uint32 ua, uint32 *ma) +{ +uint32 ublk, umap; + +ublk = ua >> VA_V_VPN; /* Unibus blk */ +if ((ublk < UBACR_GETDSB (uba_cr)) || /* map disabled? */ + (ublk >= UBA_NMAPR)) return FALSE; /* unimplemented? */ +umap = uba_map[ublk]; /* get map */ +if (umap & UBAMAP_VLD) { /* valid? */ + *ma = ((umap & UBAMAP_PAG) << VA_V_VPN) + VA_GETOFF (ua); + if ((umap & UBAMAP_DP) && (umap & UBAMAP_ODD)) /* buffered dp? */ + *ma = *ma + 1; /* byte offset? */ + return TRUE; /* legit addr */ + } +return FALSE; +} + +/* At end of page or transfer, update DPR register, in case next page + gets an error */ + +void uba_set_dpr (uint32 ua, t_bool wr) +{ +uint32 ublk, umap, dpr; + +ublk = ua >> VA_V_VPN; /* Unibus blk */ +if (ublk >= UBA_NMAPR) return; /* paranoia */ +umap = uba_map[ublk]; /* get map */ +dpr = UBAMAP_GETDP (umap); /* get bdp */ +if (dpr) uba_dpr[dpr] = (uba_dpr[dpr] & ~(UBADPR_UA|UBADPR_DIR)) | + (wr? UBADPR_DIR: 0) | + (((ua >> 2) + ((umap & UBAMAP_ODD)? 1: 0)) & UBADPR_UA); +return; +} + +/* Error routines + + uba_ub_nxm SBI read/write to nx Unibus address + uba_inv_map Unibus reference to invalid map reg +*/ + +void uba_ub_nxm (int32 ua) +{ +if ((uba_sr & UBASR_UBTMO) == 0) { + uba_sr |= UBASR_UBTMO; + uba_adap_set_int (uba_cr & UBACR_SUEFIE); + uba_fubar = (ua >> 2) & UBAFUBAR_RD; + } +else uba_sr |= UBASR_LEB; +if (DEBUG_PRI (uba_dev, UBA_DEB_ERR)) + fprintf (sim_deb, ">>UBA: nxm error, ua = %X\n", ua); +return; +} + +void uba_inv_map (int32 ublk) +{ +if ((uba_sr & UBASR_IVMR) == 0) { + uba_sr |= UBASR_IVMR; + uba_adap_set_int (uba_cr & UBACR_USEFIE); + uba_fmer = ublk & UBAFMER_RD; + } +else uba_sr |= UBASR_LEB; +if (DEBUG_PRI (uba_dev, UBA_DEB_ERR)) + fprintf (sim_deb, ">>UBA: inv map error, ublk = %X\n", ublk); +return; +} + +/* Unibus power fail routines */ + +void uba_ubpdn (int32 time) +{ +int32 i; +DEVICE *dptr; + +uba_cnf = (uba_cnf & ~UBACNF_UBIC) | UBACNF_UBPDN; /* update cnf */ +sim_activate (&uba_unit, time); /* schedule */ +uba_uiip = 1; /* UB init in prog */ +for (i = 0; sim_devices[i] != NULL; i++) { /* reset Unibus */ + dptr = sim_devices[i]; + if (dptr->reset && (dptr->flags & DEV_UBUS)) + dptr->reset (dptr); + } +return; +} + +/* Init timeout service routine */ + +t_stat uba_svc (UNIT *uptr) +{ +if (uba_aiip) { /* adapter init? */ + uba_aiip = 0; /* clear in prog */ + sim_activate (uptr, uba_uitime); /* schedule UB */ + } +else { + uba_uiip = 0; /* no, UB */ + uba_cnf = (uba_cnf & ~UBACNF_UBPDN) | UBACNF_UBIC; + uba_adap_set_int (uba_cr & UBACR_CNFIE); /* possible int */ + } +return SCPE_OK; +} + +/* Interrupt routines */ + +void uba_adap_set_int (int32 flg) +{ +if (((flg & UBACR_SUEFIE) && (uba_sr & UBA_SUEFIE_SR)) || + ((flg & UBACR_USEFIE) && (uba_sr & UBA_USEFIE_SR)) || + ((flg & UBACR_CNFIE) && (uba_cr & UBA_CNFIE_CR))) { + uba_int = 1; + if (DEBUG_PRI (uba_dev, UBA_DEB_ERR)) fprintf (sim_deb, + ">>UBA: adapter int req, sr = %X, cr = %X\n", uba_sr, uba_cr); + } +return; +} + +void uba_adap_clr_int () +{ +if ((!(uba_cr & UBACR_SUEFIE) || !(uba_sr & UBA_SUEFIE_SR)) && + (!(uba_cr & UBACR_USEFIE) || !(uba_sr & UBA_USEFIE_SR)) && + (!(uba_cr & UBACR_CNFIE) || !(uba_cr & UBA_CNFIE_CR))) + uba_int = 0; +return; +} + +/* Reset Unibus adapter */ + +t_stat uba_reset (DEVICE *dptr) +{ +int32 i; + +uba_int = 0; +uba_aiip = uba_uiip = 0; +sim_cancel (&uba_unit); +for (i = 0; i < IPL_HLVL; i++) { + nexus_req[i] &= ~(1 << TR_UBA); + int_req[i] = 0; + uba_svr[i] = 0; + uba_rvr[i] = 0; + } +for (i = 0; i < UBA_NMAPR; i++) uba_map[i] = 0; +for (i = 0; i < UBA_NDPATH; i++) uba_dpr[i] = 0; +uba_sr = 0; +uba_cr = 0; +uba_dr = 0; +uba_cnf = UBACNF_UBIC; +return SCPE_OK; +} + +/* Memory examine via map (word only) */ + +t_stat uba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 ua = (uint32) exta, pa; + +if ((vptr == NULL) || (ua >= UBADDRSIZE)) return SCPE_ARG; +if (uba_map_addr_c (ua, &pa) && ADDR_IS_MEM (pa)) { + *vptr = (uint32) ReadW (pa); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Memory deposit via map (word only) */ + +t_stat uba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 ua = (uint32) exta, pa; + +if (ua >= UBADDRSIZE) return SCPE_ARG; +if (uba_map_addr_c (ua, &pa) && ADDR_IS_MEM (pa)) { + WriteW (pa, (int32) val); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Enable/disable autoconfiguration */ + +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr != NULL) return SCPE_ARG; +autcon_enb = val; +return auto_config (NULL, 0); +} + +/* Show autoconfiguration status */ + +t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "autoconfiguration "); +fprintf (st, autcon_enb? "enabled": "disabled"); +return SCPE_OK; +} + +/* Change device address */ + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newba; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if ((val == 0) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newba = (uint32) get_uint (cptr, 16, IOPAGEBASE+IOPAGEMASK, &r); +if (r != SCPE_OK) return r; +if ((newba <= IOPAGEBASE) || /* must be > 0 */ + (newba % ((uint32) val))) return SCPE_ARG; /* check modulus */ +dibp->ba = newba; /* store */ +dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ +autcon_enb = 0; /* autoconfig off */ +return SCPE_OK; +} + +/* Show device address */ + +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR; +fprintf (st, "address=%08X", dibp->ba); +if (dibp->lnt > 1) + fprintf (st, "-%08X", dibp->ba + dibp->lnt - 1); +if (dptr->flags & DEV_FLTA) fprintf (st, "*"); +return SCPE_OK; +} + +/* Set address floating */ + +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; + +if (cptr == NULL) return SCPE_ARG; +if ((val == 0) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dptr->flags = dptr->flags | DEV_FLTA; /* floating */ +return auto_config (NULL, 0); /* autoconfigure */ +} + +/* Change device vector */ + +t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newvec; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newvec = (uint32) get_uint (cptr, 16, 01000, &r); +if ((r != SCPE_OK) || + ((newvec + (dibp->vnum * 4)) >= 01000) || + (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG; +dibp->vec = newvec; +dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ +autcon_enb = 0; /* autoconfig off */ +return SCPE_OK; +} + +/* Show device vector */ + +t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 vec, numvec; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +vec = dibp->vec; +if (arg) numvec = arg; +else numvec = dibp->vnum; +if (vec == 0) fprintf (st, "no vector"); +else { + fprintf (st, "vector=%X", vec); + if (numvec > 1) fprintf (st, "-%X", vec + (4 * (numvec - 1))); + } +return SCPE_OK; +} + +/* Init Unibus tables */ + +void init_ubus_tab (void) +{ +int32 i, j; + +for (i = 0; i < IPL_HLVL; i++) { /* clear int tables */ + for (j = 0; j < 32; j++) { + int_vec[i][j] = 0; + int_ack[i][j] = NULL; + } + } +for (i = 0; i < (IOPAGESIZE >> 1); i++) { /* clear dispatch tab */ + iodispR[i] = NULL; + iodispW[i] = NULL; + iodibp[i] = NULL; + } +return; +} + +/* Build Unibus tables */ + +t_stat build_ubus_tab (DEVICE *dptr, DIB *dibp) +{ +int32 i, idx, vec, ilvl, ibit; + +if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */ +if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR; +for (i = 0; i < dibp->vnum; i++) { /* loop thru vec */ + idx = dibp->vloc + i; /* vector index */ + vec = dibp->vec? (dibp->vec + (i * 4)): 0; /* vector addr */ + ilvl = idx / 32; + ibit = idx % 32; + if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */ + (int_ack[ilvl][ibit] != dibp->ack[i])) || + (int_vec[ilvl][ibit] && vec && + (int_vec[ilvl][ibit] != vec))) { + printf ("Device %s interrupt slot conflict at %d\n", + sim_dname (dptr), idx); + if (sim_log) fprintf (sim_log, + "Device %s interrupt slot conflict at %d\n", + sim_dname (dptr), idx); + return SCPE_STOP; + } + if (dibp->ack[i]) int_ack[ilvl][ibit] = dibp->ack[i]; + else if (vec) int_vec[ilvl][ibit] = vec; + } +for (i = 0; i < (int32) dibp->lnt; i = i + 2) { /* create entries */ + idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */ + if ((iodispR[idx] && dibp->rd && /* conflict? */ + (iodispR[idx] != dibp->rd)) || + (iodispW[idx] && dibp->wr && + (iodispW[idx] != dibp->wr))) { + printf ("Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + if (sim_log) fprintf (sim_log, + "Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + return SCPE_STOP; + } + if (dibp->rd) iodispR[idx] = dibp->rd; /* set rd dispatch */ + if (dibp->wr) iodispW[idx] = dibp->wr; /* set wr dispatch */ + iodibp[idx] = dibp; /* remember DIB */ + } +return SCPE_OK; +} + +/* Show IO space */ + +t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i, j; +DEVICE *dptr; +DIB *dibp; + +if (build_dib_tab ()) return SCPE_OK; /* build IO page */ +for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) { /* loop thru entries */ + if (iodibp[i] && (iodibp[i] != dibp)) { /* new block? */ + dibp = iodibp[i]; /* DIB for block */ + for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) { + if (((DIB*) sim_devices[j]->ctxt) == dibp) { + dptr = sim_devices[j]; /* locate device */ + break; + } /* end if */ + } /* end for j */ + fprintf (st, "%08X - %08X%c\t%s\n", dibp->ba, + dibp->ba + dibp->lnt - 1, + (dptr && (dptr->flags & DEV_FLTA))? '*': ' ', + dptr? sim_dname (dptr): "CPU"); + } /* end if */ + } /* end for i */ +return SCPE_OK; +} + +/* Show UBA virtual address */ + +t_stat uba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) +{ +t_stat r; +char *cptr = (char *) desc; +uint32 ua, pa; + +if (cptr) { + ua = (uint32) get_uint (cptr, 16, UBADDRSIZE - 1, &r); + if (r == SCPE_OK) { + if (uba_map_addr_c (ua, &pa)) + fprintf (of, "Unibus %-X = physical %-X\n", ua, pa); + else fprintf (of, "Unibus %-X: invalid mapping\n", ua); + return SCPE_OK; + } + } +fprintf (of, "Invalid argument\n"); +return SCPE_OK; +} + +/* Autoconfiguration + + The table reflects the MicroVAX 3900 microcode, with one addition - the + number of controllers field handles devices where multiple instances + are simulated through a single DEVICE structure (e.g., DZ, VH). + + A minus number of vectors indicates a field that should be calculated + but not placed in the DIB (RQ, TQ dynamic vectors) */ + +#define AUTO_MAXC 4 +#define AUTO_CSRBASE 0010 +#define AUTO_VECBASE 0300 + +typedef struct { + char *dnam[AUTO_MAXC]; + int32 numc; + int32 numv; + uint32 amod; + uint32 vmod; + uint32 fixa[AUTO_MAXC]; + uint32 fixv[AUTO_MAXC]; + } AUTO_CON; + +AUTO_CON auto_tab[] = { + { { NULL }, 1, 2, 0, 8, { 0 } }, /* DLV11J - fx CSRs */ + { { NULL }, 1, 2, 8, 8 }, /* DJ11 */ + { { NULL }, 1, 2, 16, 8 }, /* DH11 */ + { { NULL }, 1, 2, 8, 8 }, /* DQ11 */ + { { NULL }, 1, 2, 8, 8 }, /* DU11 */ + { { NULL }, 1, 2, 8, 8 }, /* DUP11 */ + { { NULL }, 10, 2, 8, 8 }, /* LK11A */ + { { NULL }, 1, 2, 8, 8 }, /* DMC11 */ + { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */ + { { NULL }, 1, 2, 8, 8 }, /* KMC11 */ + { { NULL }, 1, 2, 8, 8 }, /* LPP11 */ + { { NULL }, 1, 2, 8, 8 }, /* VMV21 */ + { { NULL }, 1, 2, 16, 8 }, /* VMV31 */ + { { NULL }, 1, 2, 8, 8 }, /* DWR70 */ + { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */ + { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */ + {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12}, + {VEC_TS} }, + { { NULL }, 1, 2, 16, 8 }, /* LPA11K */ + { { NULL }, 1, 2, 8, 8 }, /* KW11C */ + { { NULL }, 1, 1, 8, 8 }, /* reserved */ + { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */ + { { NULL }, 1, 1, 8, 4 }, /* DR11W */ + { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */ + { { NULL }, 1, 2, 8, 8 }, /* DMP11 */ + { { NULL }, 1, 2, 8, 8 }, /* DPV11 */ + { { NULL }, 1, 2, 8, 8 }, /* ISB11 */ + { { NULL }, 1, 2, 16, 8 }, /* DMV11 */ + { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */ + { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */ + {IOBA_XQ,IOBA_XQB}, {VEC_XQ} }, /* */ + { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */ + {IOBA_RQ}, {VEC_RQ} }, + { { NULL }, 1, 8, 32, 4 }, /* DMF32 */ + { { NULL }, 1, 2, 16, 8 }, /* KMS11 */ + { { NULL }, 1, 1, 16, 4 }, /* VS100 */ + { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */ + { { NULL }, 1, 2, 16, 8 }, /* KMV11 */ + { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */ + { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */ + { { NULL }, 1, 6, 32, 4 }, /* CP132 */ + { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* VS31 */ + { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */ + { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */ + { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* DSV11 */ + { { NULL }, 1, 2, 8, 8 }, /* CSAM */ + { { NULL }, 1, 2, 8, 8 }, /* ADV11C */ + { { NULL }, 1, 0, 8, 0 }, /* AAV11C */ + { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */ + { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */ + { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */ + { { NULL }, -1 } /* end table */ +}; + +t_stat auto_config (char *name, int32 nctrl) +{ +uint32 csr = IOPAGEBASE + AUTO_CSRBASE; +uint32 vec = VEC_Q + AUTO_VECBASE; +AUTO_CON *autp; +DEVICE *dptr; +DIB *dibp; +uint32 j, k, vmask, amask; + +if (autcon_enb == 0) return SCPE_OK; /* enabled? */ +if (name) { /* updating? */ + if (nctrl < 0) return SCPE_ARG; + for (autp = auto_tab; autp->numc >= 0; autp++) { + for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { + if (strcmp (name, autp->dnam[j]) == 0) + autp->numc = nctrl; + } + } + } +for (autp = auto_tab; autp->numc >= 0; autp++) { /* loop thru table */ + if (autp->amod) { /* floating csr? */ + amask = autp->amod - 1; + csr = (csr + amask) & ~amask; /* align csr */ + } + for (j = k = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { + dptr = find_dev (autp->dnam[j]); /* find ctrl */ + if ((dptr == NULL) || (dptr->flags & DEV_DIS) || + !(dptr->flags & DEV_FLTA)) continue; /* enabled, floating? */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp == NULL) return SCPE_IERR; /* not there??? */ + if (autp->amod) { /* dyn csr needed? */ + if (autp->fixa[k]) /* fixed csr avail? */ + dibp->ba = autp->fixa[k]; /* use it */ + else { /* no fixed left */ + dibp->ba = csr; /* set CSR */ + csr += (autp->numc * autp->amod); /* next CSR */ + } /* end else */ + } /* end if dyn csr */ + if (autp->numv && autp->vmod) { /* dyn vec needed? */ + uint32 numv = abs (autp->numv); /* get num vec */ + if (autp->fixv[k]) { /* fixed vec avail? */ + if (autp->numv > 0) + dibp->vec = autp->fixv[k]; /* use it */ + } + else { /* no fixed left */ + vmask = autp->vmod - 1; + vec = (vec + vmask) & ~vmask; /* align vector */ + if (autp->numv > 0) + dibp->vec = vec; /* set vector */ + vec += (autp->numc * numv * 4); + } /* end else */ + } /* end if dyn vec */ + k++; /* next instance */ + } /* end for j */ + if (autp->amod) csr = csr + 2; /* flt CSR? gap */ + } /* end for i */ +return SCPE_OK; +} diff --git a/VAX/vax_cis.c b/VAX/vax_cis.c new file mode 100644 index 0000000..e8b3c90 --- /dev/null +++ b/VAX/vax_cis.c @@ -0,0 +1,1661 @@ +/* vax_cis.c: VAX CIS instructions + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + On a full VAX, this module simulates the VAX commercial instruction set (CIS). + On a subset VAX, this module implements the emulated instruction fault. + + 28-May-08 RMS Inlined physical memory routines + 16-May-06 RMS Fixed bug in length calculation (found by Tim Stark) + 03-May-06 RMS Fixed MOVTC, MOVTUC to preserve cc's through page faults + Fixed MOVTUC to stop on translated == escape + Fixed CVTPL to set registers before destination reg write + Fixed CVTPL to set correct cc bit on overflow + Fixed EDITPC to preserve cc's through page faults + Fixed EDITPC EO$BLANK_ZERO count, cc test + Fixed EDITPC EO$INSERT to insert fill instead of blank + Fixed EDITPC EO$LOAD_PLUS/MINUS to skip character + (all reported by Tim Stark) + 12-Apr-04 RMS Cloned from pdp11_cis.c and vax_cpu1.c + + Zero length decimal strings require either zero bytes (trailing) or one byte + (separate, packed). + + CIS instructions can run for a very long time, so they are interruptible + and restartable. In the simulator, string instructions (and EDITPC) are + interruptible by faults, but decimal instructions run to completion. + The code is unoptimized. +*/ + +#include "vax_defs.h" + +#if defined (FULL_VAX) + +/* Decimal string structure */ + +#define DSTRLNT 4 +#define DSTRMAX (DSTRLNT - 1) +#define MAXDVAL 429496730 /* 2^32 / 10 */ + +#define C_SPACE 0x20 /* ASCII chars */ +#define C_PLUS 0x2B +#define C_MINUS 0x2D +#define C_ZERO 0x30 +#define C_NINE 0x39 + +typedef struct { + uint32 sign; + uint32 val[DSTRLNT]; + } DSTR; + +static DSTR Dstr_zero = { 0, 0, 0, 0, 0 }; +static DSTR Dstr_one = { 0, 0x10, 0, 0, 0 }; + +extern int32 R[16]; +extern int32 PSL; +extern int32 trpirq; +extern int32 p1; +extern int32 fault_PC; +extern int32 ibcnt, ppc; +extern int32 sim_interval; +extern jmp_buf save_env; + +int32 ReadDstr (int32 lnt, int32 addr, DSTR *dec, int32 acc); +int32 WriteDstr (int32 lnt, int32 addr, DSTR *dec, int32 v, int32 acc); +int32 SetCCDstr (int32 lnt, DSTR *src, int32 pslv); +int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin); +void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst); +int32 CmpDstr (DSTR *src1, DSTR *src2); +int32 TestDstr (DSTR *dsrc); +void ProbeDstr (int32 lnt, int32 addr, int32 acc); +int32 LntDstr (DSTR *dsrc, int32 nz); +uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin); +uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin); +int32 WordLshift (DSTR *dsrc, int32 sc); +void WordRshift (DSTR *dsrc, int32 sc); +void CreateTable (DSTR *dsrc, DSTR mtable[10]); +int32 do_crc_4b (int32 crc, int32 tbl, int32 acc); +int32 edit_read_src (int32 inc, int32 acc); +void edit_adv_src (int32 inc); +int32 edit_read_sign (int32 acc); + +extern int32 eval_int (void); + +/* CIS emulator */ + +int32 op_cis (int32 *op, int32 cc, int32 opc, int32 acc) +{ +int32 i, j, c, t, pop, rpt, V; +int32 match, fill, sign, shift; +int32 ldivd, ldivr; +int32 lenl, lenp; +uint32 nc, d, result; +t_stat r; +DSTR accum, src1, src2, dst; +DSTR mptable[10]; + +switch (opc) { /* case on opcode */ + +/* MOVTC + + Operands if PSL = 0: + op[0:1] = source string descriptor + op[2] = fill character + op[3] = table address + op[4:5] = destination string descriptor + + Registers if PSL = 1: + R[0] = delta-PC/fill/source string length + R[1] = source string address + R[2] = number of bytes remaining to move + R[3] = table address + R[4] = saved cc's/destination string length + R[5] = destination string address + + Condition codes: + NZC = set from op[0]:op[4] + V = 0 + + Registers: + R0 = src length remaining, or 0 + R1 = addr of end of source string + 1 + R2 = 0 + R3 = table address + R4 = 0 + R5 = addr of end of dest string + 1 +*/ + + case MOVTC: + if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get fill */ + R[2] = R[2] & STR_LNMASK; /* remaining move */ + cc = (R[4] >> 16) & CC_MASK; /* restore cc's */ + } + else { + CC_CMP_W (op[0], op[4]); /* set cc's */ + R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ + R[1] = op[1]; /* src addr */ + fill = op[2]; /* set fill */ + R[3] = op[3]; /* table addr */ + R[4] = op[4] | ((cc & CC_MASK) << 16); /* dst len + cc's */ + R[5] = op[5]; /* dst addr */ + R[2] = (op[0] < op[4])? op[0]: op[4]; /* remaining move */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + if (R[2]) { /* move to do? */ + int32 mvl; + mvl = R[0] & STR_LNMASK; /* orig move len */ + if (mvl >= (R[4] & STR_LNMASK)) mvl = R[4] & STR_LNMASK; + if (((uint32) R[1]) < ((uint32) R[5])) { /* backward? */ + while (R[2]) { /* loop thru char */ + t = Read ((R[1] + R[2] - 1) & LMASK, L_BYTE, RA); + c = Read ((R[3] + t) & LMASK, L_BYTE, RA); + Write ((R[5] + R[2] - 1) & LMASK, c, L_BYTE, WA); + R[2] = (R[2] - 1) & STR_LNMASK; + } + R[1] = (R[1] + mvl) & LMASK; /* adv src, dst */ + R[5] = (R[5] + mvl) & LMASK; + } + else { /* forward */ + while (R[2]) { /* loop thru char */ + t = Read (R[1], L_BYTE, RA); /* read src */ + c = Read ((R[3] + t) & LMASK, L_BYTE, RA); + Write (R[5], c, L_BYTE, WA); /* write dst */ + R[1] = (R[1] + 1) & LMASK; /* adv src, dst */ + R[2] = (R[2] - 1) & STR_LNMASK; + R[5] = (R[5] + 1) & LMASK; + } + } /* update lengths */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - mvl) & STR_LNMASK); + R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - mvl) & STR_LNMASK); + } + while (R[4] & STR_LNMASK) { /* fill if needed */ + Write (R[5], fill, L_BYTE, WA); + R[4] = (R[4] & ~STR_LNMASK) | ((R[4] - 1) & STR_LNMASK); + R[5] = (R[5] + 1) & LMASK; /* adv dst */ + } + R[0] = R[0] & STR_LNMASK; /* mask off state */ + R[4] = 0; + PSL = PSL & ~PSL_FPD; + return cc; + +/* MOVTUC + + Operands: + op[0:1] = source string descriptor + op[2] = escape character + op[3] = table address + op[4:5] = destination string descriptor + + Registers if PSL = 1: + R[0] = delta-PC/match/source string length + R[1] = source string address + R[2] = saved condition codes + R[3] = table address + R[4] = destination string length + R[5] = destination string address + + Condition codes: + NZC = set from op[0]:op[4] + V = 1 if match to escape character + + Registers: + R0 = src length remaining, or 0 + R1 = addr of end of source string + 1 + R2 = 0 + R3 = table address + R4 = dst length remaining, or 0 + R5 = addr of end of dest string + 1 +*/ + + case MOVTUC: + if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get match */ + R[4] = R[4] & STR_LNMASK; + cc = R[2] & CC_MASK; /* restore cc's */ + } + else { + CC_CMP_W (op[0], op[4]); /* set cc's */ + R[0] = STR_PACK (op[2], op[0]); /* src len, fill */ + R[1] = op[1]; /* src addr */ + fill = op[2]; /* set match */ + R[3] = op[3]; /* table addr */ + R[4] = op[4]; /* dst len */ + R[5] = op[5]; /* dst addr */ + R[2] = cc; /* save cc's */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + while ((R[0] & STR_LNMASK) && R[4]) { /* while src & dst */ + t = Read (R[1], L_BYTE, RA); /* read src */ + c = Read ((R[3] + t) & LMASK, L_BYTE, RA); /* translate */ + if (c == fill) { /* stop char? */ + cc = cc | CC_V; /* set V, done */ + break; + } + Write (R[5], c, L_BYTE, WA); /* write dst */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = (R[1] + 1) & LMASK; + R[4] = (R[4] - 1) & STR_LNMASK; /* adv src, dst */ + R[5] = (R[5] + 1) & LMASK; + } + R[0] = R[0] & STR_LNMASK; /* mask off state */ + R[2] = 0; + PSL = PSL & ~PSL_FPD; + return cc; + +/* MATCHC + + Operands: + op[0:1] = substring descriptor + op[2:3] = string descriptor + + Registers if PSL = 1: + R[0] = delta-PC/match/substring length + R[1] = substring address + R[2] = source string length + R[3] = source string address + + Condition codes: + NZ = set from R0 + VC = 0 + + Registers: + R0 = if match, 0, else, op[0] + R1 = if match, op[0] + op[1], else, op[1] + R2 = if match, src bytes remaining, else, 0 + R3 = if match, end of substr, else, op[2] + op[3] + + Notes: + - If the string is zero length, and the substring is not, + the outer loop exits immediately, and the result is + "no match" + - If the substring is zero length, the inner loop always + exits immediately, and the result is a "match" + - If the string is zero length, and the substring is as + well, the outer loop executes, the inner loop exits + immediately, and the result is a match, but the result + is the length of the string (zero) + - This instruction can potentially run a very long time - worst + case execution on a real VAX-11/780 was more than 30 minutes. + Accordingly, the instruction tests for interrupts and stops + if one is found. +*/ + + case MATCHC: + if (PSL & PSL_FPD) { /* FPD? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + R[2] = R[2] & STR_LNMASK; + } + else { + R[0] = STR_PACK (0, op[0]); /* srclen + FPD data */ + R[1] = op[1]; /* save operands */ + R[2] = op[2]; + R[3] = op[3]; + PSL = PSL | PSL_FPD; /* set FPD */ + } + for (match = 0; R[2] >= (R[0] & STR_LNMASK); ) { + for (i = 0, match = 1; match && (i < (R[0] & STR_LNMASK)); i++) { + c = Read ((R[1] + i) & LMASK, L_BYTE, RA); + t = Read ((R[3] + i) & LMASK, L_BYTE, RA); + match = (c == t); /* continue if match */ + } /* end for substring */ + if (match) break; /* exit if match */ + R[2] = (R[2] - 1) & STR_LNMASK; /* decr src length */ + R[3] = (R[3] + 1) & LMASK; /* next string char */ + if (i >= sim_interval) { /* done with interval? */ + sim_interval = 0; + if (r = sim_process_event ()) { /* presumably WRU */ + PC = fault_PC; /* backup up PC */ + ABORT (r); /* abort flushes IB */ + } + SET_IRQL; /* update interrupts */ + if (trpirq) ABORT (ABORT_INTR); /* pending? stop */ + } + else sim_interval = sim_interval - i; + } /* end for string */ + R[0] = R[0] & STR_LNMASK; + if (match) { /* if match */ + R[1] = (R[1] + R[0]) & LMASK; + R[2] = (R[2] - R[0]) & STR_LNMASK; + R[3] = (R[3] + R[0]) & LMASK; + R[0] = 0; + } + else { /* if no match */ + R[3] = (R[3] + R[2]) & LMASK; + R[2] = 0; + } + PSL = PSL & ~PSL_FPD; + CC_IIZZ_L (R[0]); /* set cc's */ + return cc; + +/* CRC + + Operands: + op[0] = table address + op[1] = initial CRC + op[2:3] = source string descriptor + + Registers if PSL = 1: + R[0] = result + R[1] = table address + R[2] = delta-PC/0/source string length + R[3] = source string address + + Condition codes: + NZ = set from result + VC = 0 + + Registers: + R0 = result + R1 = 0 + R2 = 0 + R3 = addr + 1 of last byte in source string +*/ + + case CRC: + if (PSL & PSL_FPD) { /* FPD? */ + SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ + } + else { + R[2] = STR_PACK (0, op[2]); /* srclen + FPD data */ + R[0] = op[1]; /* save operands */ + R[1] = op[0]; + R[3] = op[3]; + PSL = PSL | PSL_FPD; /* set FPD */ + } + while ((R[2] & STR_LNMASK) != 0) { /* loop thru chars */ + c = Read (R[3], L_BYTE, RA); /* get char */ + t = R[0] ^ c; /* XOR to CRC */ + t = do_crc_4b (t, R[1], acc); /* do 4b shift */ + R[0] = do_crc_4b (t, R[1], acc); /* do 4b shift */ + R[3] = (R[3] + 1) & LMASK; + R[2] = R[2] - 1; + } + R[1] = 0; + R[2] = 0; + PSL = PSL & ~PSL_FPD; + CC_IIZZ_L (R[0]); /* set cc's */ + return cc; + +/* MOVP + + Operands: + op[0] = length + op[1] = source string address + op[2] = dest string address + + Condition codes: + NZ = set from result + V = 0 + C = unchanged + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case MOVP: + if ((PSL & PSL_FPD) || (op[0] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &dst, acc); /* read source */ + cc = WriteDstr (op[0], op[2], &dst, 0, acc) | /* write dest */ + (cc & CC_C); /* preserve C */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[2]; + return cc; + +/* ADDP4, ADDP6, SUBP4, SUBP6 + + Operands: + op[0:1] = src1 string descriptor + op[2:3] = src2 string descriptor + (ADDP6, SUBP6 only) + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string + (ADDP6, SUBP6 only) + R4 = 0 + R5 = addr of dest string +*/ + + case ADDP4: case SUBP4: + op[4] = op[2]; /* copy dst */ + op[5] = op[3]; + case ADDP6: case SUBP6: + if ((PSL & PSL_FPD) || (op[0] > 31) || + (op[2] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ + ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ + if (opc & 2) src1.sign = src1.sign ^ 1; /* sub? invert sign */ + if (src1.sign ^ src2.sign) { /* opp signs? sub */ + if (CmpDstr (&src1, &src2) < 0) { /* src1 < src2? */ + SubDstr (&src1, &src2, &dst); /* src2 - src1 */ + dst.sign = src2.sign; /* sign = src2 */ + } + else { + SubDstr (&src2, &src1, &dst); /* src1 - src2 */ + dst.sign = src1.sign; /* sign = src1 */ + } + V = 0; /* can't carry */ + } + else { /* addition */ + V = AddDstr (&src1, &src2, &dst, 0); /* add magnitudes */ + dst.sign = src1.sign; /* set result sign */ + } + cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + if (opc & 1) { /* ADDP6, SUBP6? */ + R[4] = 0; + R[5] = op[5]; + } + return cc; + +/* MULP + + Operands: + op[0:1] = src1 string descriptor + op[2:3] = src2 string descriptor + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string + R4 = 0 + R5 = addr of dest string +*/ + + case MULP: + if ((PSL & PSL_FPD) || (op[0] > 31) || + (op[2] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + if (ReadDstr (op[0], op[1], &src1, acc) && /* read src1, src2 */ + ReadDstr (op[2], op[3], &src2, acc)) { /* if both > 0 */ + dst.sign = src1.sign ^ src2.sign; /* sign of result */ + accum = Dstr_zero; /* clear accum */ + NibbleRshift (&src1, 1, 0); /* shift out sign */ + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 1; i < (DSTRLNT * 8); i++) { /* 31 iterations */ + d = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (d > 0) /* add in digit*mpcnd */ + AddDstr (&mptable[d], &accum, &accum, 0); + nc = NibbleRshift (&accum, 1, 0); /* ac right 4 */ + NibbleRshift (&dst, 1, nc); /* result right 4 */ + } + V = TestDstr (&accum) != 0; /* if ovflo, set V */ + } + else V = 0; /* result = 0 */ + cc = WriteDstr (op[4], op[5], &dst, V, acc); /* store result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + R[4] = 0; + R[5] = op[5]; + return cc; + +/* DIVP + + Operands: + op[0:1] = src1 string descriptor + op[2:3] = src2 string descriptor + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string + R4 = 0 + R5 = addr of dest string +*/ + + case DIVP: + if ((PSL & PSL_FPD) || (op[0] > 31) || + (op[2] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + ldivr = ReadDstr (op[0], op[1], &src1, acc); /* get divisor */ + if (ldivr == 0) { /* divisor = 0? */ + SET_TRAP (TRAP_FLTDIV); /* dec div trap */ + return cc; + } + ldivr = LntDstr (&src1, ldivr); /* get exact length */ + ldivd = ReadDstr (op[2], op[3], &src2, acc); /* get dividend */ + ldivd = LntDstr (&src2, ldivd); /* get exact length */ + dst = Dstr_zero; /* clear dest */ + NibbleRshift (&src1, 1, 0); /* right justify ops */ + NibbleRshift (&src2, 1, 0); + if ((t = ldivd - ldivr) >= 0) { /* any divide to do? */ + dst.sign = src1.sign ^ src2.sign; /* calculate sign */ + WordLshift (&src1, t / 8); /* align divr to divd */ + NibbleLshift (&src1, t % 8, 0); + CreateTable (&src1, mptable); /* create *1, *2, ... */ + for (i = 0; i <= t; i++) { /* divide loop */ + for (d = 9; d > 0; d--) { /* find digit */ + if (CmpDstr (&src2, &mptable[d]) >= 0) { + SubDstr (&mptable[d], &src2, &src2); + dst.val[0] = dst.val[0] | d; + break; + } /* end if */ + } /* end for */ + NibbleLshift (&src2, 1, 0); /* shift dividend */ + NibbleLshift (&dst, 1, 0); /* shift quotient */ + } /* end divide loop */ + } /* end if */ + cc = WriteDstr (op[4], op[5], &dst, 0, acc); /* store result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + R[4] = 0; + R[5] = op[5]; + return cc; + +/* CMPP3, CMPP4 + + Operands (CMPP3): + op[0] = string length + op[1], op[2] = string lengths + + Operands (CMPP4): + op[0:1] = string1 descriptor + op[2:3] = string2 descriptor + + Condition codes: + NZ = set from comparison + VC = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string +*/ + + case CMPP3: + op[3] = op[2]; /* reposition ops */ + op[2] = op[0]; + case CMPP4: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &src1, acc); /* get src1 */ + ReadDstr (op[2], op[3], &src2, acc); /* get src2 */ + cc = 0; + if (src1.sign != src2.sign) cc = (src1.sign)? CC_N: 0; + else { + t = CmpDstr (&src1, &src2); /* compare strings */ + if (t < 0) cc = (src1.sign? 0: CC_N); + else if (t > 0) cc = (src1.sign? CC_N: 0); + else cc = CC_Z; + } + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + return cc ; + +/* ASHP + + Operands: + op[0] = shift count + op[1:2] = source string descriptor + op[3] = round digit + op[4:5] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of src1 string + R2 = 0 + R3 = addr of src2 string +*/ + + case ASHP: + if ((PSL & PSL_FPD) || (op[1] > 31) || (op[4] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[1], op[2], &src1, acc); /* get source */ + V = 0; /* init V */ + shift = op[0]; /* get shift count */ + if (shift & BSIGN) { /* right shift? */ + shift = BMASK + 1 - shift; /* !shift! */ + WordRshift (&src1, shift / 8); /* do word shifts */ + NibbleRshift (&src1, shift % 8, 0); /* do nibble shifts */ + t = op[3] & 0xF; /* get round nibble */ + if ((t + (src1.val[0] & 0xF)) > 9) /* rounding needed? */ + AddDstr (&src1, &Dstr_one, &src1, 0); /* round */ + src1.val[0] = src1.val[0] & ~0xF; /* clear sign */ + } /* end right shift */ + else if (shift) { /* left shift? */ + if (WordLshift (&src1, shift / 8)) V = 1; /* do word shifts */ + if (NibbleLshift (&src1, shift % 8, 0)) V = 1; + } /* end left shift */ + cc = WriteDstr (op[4], op[5], &src1, V, acc); /* store result */ + R[0] = 0; + R[1] = op[2]; + R[2] = 0; + R[3] = op[5]; + return cc ; + +/* CVTPL + + Operands: + op[0:1] = source string descriptor + op[2] = memory flag/register number + op[3] = memory address + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = 0 +*/ + + case CVTPL: + if ((PSL & PSL_FPD) || (op[0] > 31)) + RSVD_OPND_FAULT; + ReadDstr (op[0], op[1], &src1, acc); /* get source */ + V = result = 0; /* clear V, result */ + for (i = (DSTRLNT * 8) - 1; i > 0; i--) { /* loop thru digits */ + d = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF; + if (d || result || V) { /* skip initial 0's */ + if (result >= MAXDVAL) V = 1; + result = ((result * 10) + d) & LMASK; + if (result < d) V = 1; + } /* end if */ + } /* end for */ + if (src1.sign) result = (~result + 1) & LMASK; /* negative? */ + if (src1.sign ^ ((result & LSIGN) != 0)) V = 1; /* test for overflow */ + if (op[2] < 0) /* if mem, store result */ + Write (op[3], result, L_LONG, WA); /* before reg update */ + R[0] = 0; /* update registers */ + R[1] = op[1]; + R[2] = 0; + R[3] = 0; + if (op[2] >= 0) /* if reg, store result */ + R[op[2]] = result; /* after reg update */ + if (V && (PSL & PSW_IV)) SET_TRAP (TRAP_INTOV); /* ovflo and IV? trap */ + CC_IIZZ_L (result); + return cc | (V? CC_V: 0); + +/* CVTLP + + Operands: + op[0] = source long + op[1:2] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = 0 + R2 = 0 + R3 = addr of dest string +*/ + + case CVTLP: + if ((PSL & PSL_FPD) || (op[1] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + result = op[0]; + if ((result & LSIGN) != 0) { + dst.sign = 1; + result = (~result + 1) & LMASK; + } + for (i = 1; (i < (DSTRLNT * 8)) && result; i++) { + d = result % 10; + result = result / 10; + dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); + } + cc = WriteDstr (op[1], op[2], &dst, 0, acc); /* write result */ + R[0] = 0; + R[1] = 0; + R[2] = 0; + R[3] = op[2]; + return cc; + +/* CVTSP + + Operands: + op[0:1] = source string descriptor + op[2:3] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = address of sign byte of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTSP: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + t = Read (op[1], L_BYTE, RA); /* read source sign */ + if (t == C_MINUS) dst.sign = 1; /* sign -, */ + else if ((t != C_PLUS) && (t != C_SPACE)) /* + or blank? */ + RSVD_OPND_FAULT; + for (i = 1; i <= op[0]; i++) { /* loop thru chars */ + c = Read ((op[1] + op[0] + 1 - i) & LMASK, L_BYTE, RA); + if ((c < C_ZERO) || (c > C_NINE)) /* [0:9]? */ + RSVD_OPND_FAULT; + d = c & 0xF; + dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); + } + TestDstr (&dst); /* correct -0 */ + cc = WriteDstr (op[2], op[3], &dst, 0, acc); /* write result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + return cc; + +/* CVTPS + + Operands: + op[0:1] = source string descriptor + op[2:3] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTPS: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[2] > 31)) + RSVD_OPND_FAULT; + lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ + lenp = LntDstr (&dst, lenl); /* get exact nz src len */ + ProbeDstr (op[2], op[3], WA); /* test dst write */ + Write (op[3], dst.sign? C_MINUS: C_PLUS, L_BYTE, WA); + for (i = 1; i <= op[2]; i++) { /* loop thru chars */ + d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF;/* get digit */ + c = d | C_ZERO; /* cvt to ASCII */ + Write ((op[3] + op[2] + 1 - i) & LMASK, c, L_BYTE, WA); + } + cc = SetCCDstr (op[0], &dst, 0); /* set cc's */ + if (lenp > op[2]) { /* src fit in dst? */ + cc = cc | CC_V; /* set ovflo */ + if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ + } + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[3]; + return cc; + +/* CVTTP + + Operands: + op[0:1] = source string descriptor + op[2] = table address + op[3:4] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTTP: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) + RSVD_OPND_FAULT; + dst = Dstr_zero; /* clear result */ + for (i = 1; i <= op[0]; i++) { /* loop thru char */ + c = Read ((op[1] + op[0] - i) & LMASK, L_BYTE, RA); /* read char */ + if (i != 1) { /* normal byte? */ + if ((c < C_ZERO) || (c > C_NINE)) /* valid digit? */ + RSVD_OPND_FAULT; + d = c & 0xF; + } + else { /* highest byte */ + t = Read ((op[2] + c) & LMASK, L_BYTE, RA); /* xlate */ + d = (t >> 4) & 0xF; /* digit */ + t = t & 0xF; /* sign */ + if ((d > 0x9) || (t < 0xA)) RSVD_OPND_FAULT; + if ((t == 0xB) || (t == 0xD)) dst.sign = 1; + } + dst.val[i / 8] = dst.val[i / 8] | (d << ((i % 8) * 4)); + } + TestDstr (&dst); /* correct -0 */ + cc = WriteDstr (op[3], op[4], &dst, 0, acc); /* write result */ + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[4]; + return cc; + +/* CVTPT + + Operands: + op[0:1] = source string descriptor + op[2] = table address + op[3:4] = dest string descriptor + + Condition codes: + NZV = set from result + C = 0 + + Registers: + R0 = 0 + R1 = addr of source string + R2 = 0 + R3 = addr of dest string +*/ + + case CVTPT: + if ((PSL & PSL_FPD) || (op[0] > 31) || (op[3] > 31)) + RSVD_OPND_FAULT; + lenl = ReadDstr (op[0], op[1], &dst, acc); /* get source, lw len */ + lenp = LntDstr (&dst, lenl); /* get exact src len */ + ProbeDstr (op[3], op[4], WA); /* test writeability */ + for (i = 1; i <= op[3]; i++) { /* loop thru chars */ + if (i != 1) { /* not last? */ + d = (dst.val[i / 8] >> ((i % 8) * 4)) & 0xF; /* get digit */ + c = d + C_ZERO; /* convert */ + } + else { /* translate last */ + t = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA); + c = Read ((op[2] + t) & LMASK, L_BYTE, RA); + } + Write ((op[4] + op[3] - i) & LMASK, c, L_BYTE, WA); + } + cc = SetCCDstr (op[0], &dst, 0); /* set cc's from src */ + if (lenp > op[3]) { /* src fit in dst? */ + cc = cc | CC_V; /* set ovflo */ + if (PSL & PSW_DV) SET_TRAP (TRAP_DECOVF); /* if enabled, trap */ + } + R[0] = 0; + R[1] = op[1]; + R[2] = 0; + R[3] = op[4]; + return cc; + +/* EDITPC + + Operands: + op[0:1] = source string descriptor + op[2] = pattern string address + op[3] = dest string address + + Condition codes: + N = source is negative + Z = source is zero + V = significant digits lost + C = significant digits seen + + Registers at packup: + R0<31:16> = -count of source zeroes to supply + R0<15:0> = remaining source length + R1 = source address + R2<31:24> = delta PC + R2<19:16> = condition codes + R2<15:8> = sign char + R2<7:0> = fill char + R3 = pattern string address + R4 = original source length + R5 = dest string addr + + Registers at end: + R0 = source length + R1 = source addr + R2 = 0 + R3 = addr of byte containing EO$END + R4 = 0 + R5 = addr of end of dst string + 1 + + Fault and abort conditions for EDITPC are complicated. In general: + - It is safe to take a memory management fault on the read of + any pattern byte. After correction of the fault, the pattern + operator is fetched and executed again. + - It is safe to take a memory management fault on a write-only + operation, like fill. After correction of the fault, the + pattern operator is fetched and executed again. + - The move operators do not alter visible state (registers or saved cc) + until all memory operations are complete. +*/ + + case EDITPC: + if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[2])); /* reset PC */ + fill = ED_GETFILL (R[2]); /* get fill */ + sign = ED_GETSIGN (R[2]); /* get sign */ + cc = ED_GETCC (R[2]); /* get cc's */ + R[0] = R[0] & ~0xFFE0; /* src len <= 31 */ + } + else { /* new instr */ + if (op[0] > 31) RSVD_OPND_FAULT; /* lnt > 31? */ + t = Read ((op[1] + (op[0] / 2)) & LMASK, L_BYTE, RA) & 0xF; + if ((t == 0xB) || (t == 0xD)) { + cc = CC_N | CC_Z; + sign = C_MINUS; + } + else { + cc = CC_Z; + sign = C_SPACE; + } + fill = C_SPACE; + R[0] = R[4] = op[0]; /* src len */ + R[1] = op[1]; /* src addr */ + R[2] = STR_PACK (cc, (sign << ED_V_SIGN) | (fill << ED_V_FILL)); + /* delta PC, cc, sign, fill */ + R[3] = op[2]; /* pattern */ + R[5] = op[3]; /* dst addr */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + + for ( ;; ) { /* loop thru pattern */ + pop = Read (R[3], L_BYTE, RA); /* rd pattern op */ + if (pop == EO_END) break; /* end? */ + if (pop & EO_RPT_FLAG) { /* repeat class? */ + rpt = pop & EO_RPT_MASK; /* isolate count */ + if (rpt == 0) RSVD_OPND_FAULT; /* can't be zero */ + pop = pop & ~EO_RPT_MASK; /* isolate op */ + } + switch (pop) { /* case on op */ + + case EO_END_FLOAT: /* end float */ + if (!(cc & CC_C)) { /* not signif? */ + Write (R[5], sign, L_BYTE, WA); /* write sign */ + R[5] = (R[5] + 1) & LMASK; /* now fault safe */ + cc = cc | CC_C; /* set signif */ + } + break; + + case EO_CLR_SIGNIF: /* clear signif */ + cc = cc & ~CC_C; /* clr C */ + break; + + case EO_SET_SIGNIF: /* set signif */ + cc = cc | CC_C; /* set C */ + break; + + case EO_STORE_SIGN: /* store sign */ + Write (R[5], sign, L_BYTE, WA); /* write sign */ + R[5] = (R[5] + 1) & LMASK; /* now fault safe */ + break; + + case EO_LOAD_FILL: /* load fill */ + fill = Read ((R[3] + 1) & LMASK, L_BYTE, RA); + R[2] = ED_PUTFILL (R[2], fill); /* now fault safe */ + R[3]++; + break; + + case EO_LOAD_SIGN: /* load sign */ + sign = edit_read_sign (acc); + R[3]++; + break; + + case EO_LOAD_PLUS: /* load sign if + */ + if (!(cc & CC_N)) sign = edit_read_sign (acc); + R[3]++; + break; + + case EO_LOAD_MINUS: /* load sign if - */ + if (cc & CC_N) sign = edit_read_sign (acc); + R[3]++; + break; + + case EO_INSERT: /* insert char */ + c = Read ((R[3] + 1) & LMASK, L_BYTE, RA); + Write (R[5], ((cc & CC_C)? c: fill), L_BYTE, WA); + R[5] = (R[5] + 1) & LMASK; /* now fault safe */ + R[3]++; + break; + + case EO_BLANK_ZERO: /* blank zero */ + t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); + if (t == 0) RSVD_OPND_FAULT; + if (cc & CC_Z) { /* zero? */ + do { /* repeat and blank */ + Write ((R[5] - t) & LMASK, fill, L_BYTE, WA); + } while (--t); + } + R[3]++; /* now fault safe */ + break; + + case EO_REPL_SIGN: /* replace sign */ + t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); + if (t == 0) RSVD_OPND_FAULT; + if (cc & CC_Z) + Write ((R[5] - t) & LMASK, fill, L_BYTE, WA); + R[3]++; /* now fault safe */ + break; + + case EO_ADJUST_LNT: /* adjust length */ + t = Read ((R[3] + 1) & LMASK, L_BYTE, RA); + if ((t == 0) || (t > 31)) RSVD_OPND_FAULT; + R[0] = R[0] & WMASK; /* clr old ld zero */ + if (R[0] > t) { /* decrease */ + for (i = 0; i < (R[0] - t); i++) { /* loop thru src */ + d = edit_read_src (i, acc); /* get nibble */ + if (d) cc = (cc | CC_V | CC_C) & ~CC_Z; + } /* end for */ + edit_adv_src (R[0] - t); /* adv src ptr */ + } /* end else */ + else R[0] = R[0] | (((R[0] - t) & WMASK) << 16); + R[3]++; + break; + + case EO_FILL: /* fill */ + for (i = 0; i < rpt; i++) /* fill string */ + Write ((R[5] + i) & LMASK, fill, L_BYTE, WA); + R[5] = (R[5] + rpt) & LMASK; /* now fault safe */ + break; + + case EO_MOVE: + for (i = 0; i < rpt; i++) { /* for repeat */ + d = edit_read_src (i, acc); /* get nibble */ + if (d) cc = (cc | CC_C) & ~CC_Z; /* test for non-zero */ + c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ + Write ((R[5] + i) & LMASK, c, L_BYTE, WA); + } /* end for */ + edit_adv_src (rpt); /* advance src */ + R[5] = (R[5] + rpt) & LMASK; /* advance dst */ + break; + + case EO_FLOAT: + for (i = j = 0; i < rpt; i++, j++) { /* for repeat */ + d = edit_read_src (i, acc); /* get nibble */ + if (d && !(cc & CC_C)) { /* nz, signif clear? */ + Write ((R[5] + j) & LMASK, sign, L_BYTE, WA); + cc = (cc | CC_C) & ~CC_Z; /* set signif */ + j++; /* extra dst char */ + } /* end if */ + c = (cc & CC_C)? (d | 0x30): fill; /* test for signif */ + Write ((R[5] + j) & LMASK, c, L_BYTE, WA); + } /* end for */ + edit_adv_src (rpt); /* advance src */ + R[5] = (R[5] + j) & LMASK; /* advance dst */ + break; + + default: /* undefined */ + RSVD_OPND_FAULT; + } /* end case pattern */ + + R[3] = (R[3] + 1) & LMASK; /* next pattern byte */ + R[2] = ED_PUTCC (R[2], cc); /* update cc's */ + } /* end for pattern */ + + if (R[0]) RSVD_OPND_FAULT; /* pattern too short */ + PSL = PSL & ~PSL_FPD; /* clear FPD */ + if (cc & CC_Z) cc = cc & ~CC_N; /* zero? clear n */ + if ((cc & CC_V) && (PSL & PSW_DV)) /* overflow & trap enabled? */ + SET_TRAP (TRAP_DECOVF); + R[0] = R[4]; /* restore src len */ + R[1] = R[1] - (R[0] >> 1); /* restore src addr */ + R[2] = R[4] = 0; + return cc; + + default: + RSVD_INST_FAULT; + } + /* end case op */ +return cc; +} + +/* Get packed decimal string + + Arguments: + lnt = decimal string length + adr = decimal string address + src = decimal string structure + acc = access mode + + The routine returns the length in int32's of the non-zero part of + the string. + + To simplify the code elsewhere, digits are range checked, + and bad digits cause a fault. +*/ + +int32 ReadDstr (int32 lnt, int32 adr, DSTR *src, int32 acc) +{ +int32 c, i, end, t; + +*src = Dstr_zero; /* clear result */ +end = lnt / 2; /* last byte */ +for (i = 0; i <= end; i++) { /* loop thru string */ + c = Read ((adr + end - i) & LMASK, L_BYTE, RA); /* get byte */ + if (i == 0) { /* sign char? */ + t = c & 0xF; /* save sign */ + c = c & 0xF0; /* erase sign */ + } + if ((i == end) && ((lnt & 1) == 0)) c = c & 0xF; +/* if (((c & 0xF0) > 0x90) || /* check hi digit */ +/* ((c & 0x0F) > 0x09)) RSVD_OPND_FAULT; /* check lo digit */ + src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8)); + } /* end for */ +if ((t == 0xB) || (t == 0xD)) src->sign = 1; /* if -, set sign */ +return TestDstr (src); /* clean -0 */ +} + +/* Store decimal string + + Arguments: + lnt = decimal string length + adr = decimal string address + dst = decimal string structure + V = initial overflow flag + acc = access mode + + Returns condition codes. + + PSL.NZ are also set to their proper values + PSL.V will be set on overflow; it must be initialized elsewhere + (to allow for external overflow calculations) + + The rules for the stored sign and the PSW sign are: + + - Stored sign is negative if input is negative, and the result + is non-zero or there was overflow + - PSL sign is negative if input is negative, and the result is + non-zero + + Thus, the stored sign and the PSL sign will differ in one case: + a negative zero generated by overflow is stored with a negative + sign, but PSL.N is clear +*/ + +int32 WriteDstr (int32 lnt, int32 adr, DSTR *dst, int32 pslv, int32 acc) +{ +int32 c, i, cc, end; + +end = lnt / 2; /* end of string */ +ProbeDstr (end, adr, WA); /* test writeability */ +cc = SetCCDstr (lnt, dst, pslv); /* set cond codes */ +dst->val[0] = dst->val[0] | 0xC | dst->sign; /* set sign */ +for (i = 0; i <= end; i++) { /* store string */ + c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF; + Write ((adr + end - i) & LMASK, c, L_BYTE, WA); + } /* end for */ +return cc; +} + +/* Set CC for decimal string + + Arguments: + lnt = string length + dst = decimal string structure + pslv = initial V + + Output: + cc = condition codes +*/ + +int32 SetCCDstr (int32 lnt, DSTR *dst, int32 pslv) +{ +int32 psln, pslz, i, limit; +uint32 mask; +static uint32 masktab[8] = { + 0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000, + 0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000 + }; + +mask = 0; /* can't ovflo */ +pslz = 1; /* assume all 0's */ +limit = lnt / 8; /* limit for test */ +for (i = 0; i < DSTRLNT; i++) { /* loop thru value */ + if (i == limit) mask = masktab[lnt % 8]; /* at limit, get mask */ + else if (i > limit) mask = 0xFFFFFFFF; /* beyond, all ovflo */ + if (dst->val[i] & mask) pslv = 1; /* test for ovflo */ + dst->val[i] = dst->val[i] & ~mask; /* clr digits past end */ + if (dst->val[i]) pslz = 0; /* test nz */ + } +dst->sign = dst->sign & ~(pslz & ~pslv); +psln = dst->sign & ~pslz; /* N = sign, if ~zero */ +if (pslv && (PSL & PSW_DV)) SET_TRAP (TRAP_DECOVF); +return (psln? CC_N: 0) | (pslz? CC_Z: 0) | (pslv? CC_V: 0); +} + +/* Probe decimal string for accessibility */ + +void ProbeDstr (int32 lnt, int32 addr, int32 acc) +{ +Read (addr, L_BYTE, acc); +Read ((addr + lnt) & LMASK, L_BYTE, acc); +return; +} + +/* Add decimal string magnitudes + + Arguments: + s1 = src1 decimal string + s2 = src2 decimal string + ds = dest decimal string + cy = carry in + Output = 1 if carry, 0 if no carry + + This algorithm courtesy Anton Chernoff, circa 1992 or even earlier. + + We trace the history of a pair of adjacent digits to see how the + carry is fixed; each parenthesized item is a 4b digit. + + Assume we are adding: + + (a)(b) I + + (x)(y) J + + First compute I^J: + + (a^x)(b^y) TMP + + Note that the low bit of each digit is the same as the low bit of + the sum of the digits, ignoring the carry, since the low bit of the + sum is the xor of the bits. + + Now compute I+J+66 to get decimal addition with carry forced left + one digit: + + (a+x+6+carry mod 16)(b+y+6 mod 16) SUM + + Note that if there was a carry from b+y+6, then the low bit of the + left digit is different from the expected low bit from the xor. + If we xor this SUM into TMP, then the low bit of each digit is 1 + if there was a carry, and 0 if not. We need to subtract 6 from each + digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift + it right 4 to the digits that are affected, and subtract 6*adjustment + (actually, shift it right 3 and subtract 3*adjustment). +*/ + +int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy) +{ +int32 i; +uint32 sm1, sm2, tm1, tm2, tm3, tm4; + +for (i = 0; i < DSTRLNT; i++) { /* loop low to high */ + tm1 = s1->val[i] ^ (s2->val[i] + cy); /* xor operands */ + sm1 = s1->val[i] + (s2->val[i] + cy); /* sum operands */ + sm2 = sm1 + 0x66666666; /* force carry out */ + cy = ((sm1 < s1->val[i]) || (sm2 < sm1)); /* check for overflow */ + tm2 = tm1 ^ sm2; /* get carry flags */ + tm3 = (tm2 >> 3) | (cy << 29); /* compute adjustment */ + tm4 = 0x22222222 & ~tm3; /* clear where carry */ + ds->val[i] = (sm2 - (3 * tm4)) & LMASK; /* final result */ + } +return cy; +} + +/* Subtract decimal string magnitudes + + Arguments: + s1 = src1 decimal string + s2 = src2 decimal string + ds = dest decimal string + Outputs: s2 - s1 in ds + + Note: the routine assumes that s1 <= s2 + +*/ + +void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds) +{ +int32 i; +DSTR compl; + +for (i = 0; i < DSTRLNT; i++) /* 10's comp s2 */ + compl.val[i] = 0x99999999 - s1->val[i]; +AddDstr (&compl, s2, ds, 1); /* s1 + ~s2 + 1 */ +return; +} + +/* Compare decimal string magnitudes + + Arguments: + s1 = src1 decimal string + s2 = src2 decimal string + Output = 1 if >, 0 if =, -1 if < +*/ + +int32 CmpDstr (DSTR *s1, DSTR *s2) +{ +int32 i; + +for (i = DSTRMAX; i >=0; i--) { + if (s1->val[i] > s2->val[i]) return 1; + if (s1->val[i] < s2->val[i]) return -1; + } +return 0; +} + +/* Test decimal string for zero + + Arguments: + dsrc = decimal string structure + + Returns the non-zero length of the string, in int32 units + If the string is zero, the sign is cleared +*/ + +int32 TestDstr (DSTR *dsrc) +{ +int32 i; + +for (i = DSTRMAX; i >= 0; i--) if (dsrc->val[i]) return (i + 1); +dsrc->sign = 0; +return 0; +} + +/* Get exact length of decimal string + + Arguments: + dsrc = decimal string structure + nz = result from TestDstr +*/ + +int32 LntDstr (DSTR *dsrc, int32 nz) +{ +int32 i; + +if (nz == 0) return 0; +for (i = 7; i > 0; i--) { + if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF) break; + } +return ((nz - 1) * 8) + i; +} + +/* Create table of multiples + + Arguments: + dsrc = base decimal string structure + mtable[10] = array of decimal string structures + + Note that dsrc has a high order zero nibble; this + guarantees that the largest multiple won't overflow + Also note that mtable[0] is not filled in +*/ + +void CreateTable (DSTR *dsrc, DSTR mtable[10]) +{ +int32 (i); + +mtable[1] = *dsrc; +for (i = 2; i < 10; i++) + AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0); +return; +} + +/* Word shift right + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +void WordRshift (DSTR *dsrc, int32 sc) +{ +int32 i; + +if (sc) { + for (i = 0; i < DSTRLNT; i++) { + if ((i + sc) < DSTRLNT) dsrc->val[i] = dsrc->val[i + sc]; + else dsrc->val[i] = 0; + } + } +return; +} + +/* Word shift left + + Arguments: + dsrc = decimal string structure + sc = shift count +*/ + +int32 WordLshift (DSTR *dsrc, int32 sc) +{ +int32 i, c; + +c = 0; +if (sc) { + for (i = DSTRMAX; i >= 0; i--) { + if (i > (DSTRMAX - sc)) c = c | dsrc->val[i]; + if ((i - sc) >= 0) dsrc->val[i] = dsrc->val[i - sc]; + else dsrc->val[i] = 0; + } + } +return c; +} + +/* Nibble shift decimal string right + + Arguments: + dsrc = decimal string structure + sc = shift count + cin = carry in +*/ + +uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin) +{ +int32 i, s, rs, nc; + +if (s = sc * 4) { + rs = 32 - s; + for (i = DSTRMAX; i >= 0; i--) { + nc = dsrc->val[i]; + dsrc->val[i] = ((dsrc->val[i] >> s) | + (cin << rs)) & LMASK; + cin = nc; + } + return cin; + } +return 0; +} + +/* Nibble shift decimal string left + + Arguments: + dsrc = decimal string structure + sc = shift count + cin = carry in +*/ + +uint32 NibbleLshift (DSTR *dsrc, int32 sc, uint32 cin) +{ +int32 i, s, rs, nc; + +if (s = sc * 4) { + rs = 32 - s; + for (i = 0; i < DSTRLNT; i++) { + nc = dsrc->val[i]; + dsrc->val[i] = ((dsrc->val[i] << s) | + (cin >> rs)) & LMASK; + cin = nc; + } + return cin; + } +return 0; +} + +/* Do 4b of CRC calculation + + Arguments: + crc = current CRC ^ char + tbl = 16 lw table base + + Output: + new CRC +*/ + +int32 do_crc_4b (int32 crc, int32 tbl, int32 acc) +{ +int32 idx = (crc & 0xF) << 2; +int32 t; + +crc = (crc >> 4) & 0x0FFFFFFF; +t = Read ((tbl + idx) & LMASK, L_LONG, RA); +return crc ^ t; +} + +/* Edit routines */ + +int32 edit_read_src (int32 inc, int32 acc) +{ +int32 c, r0, r1; + +if (R[0] & LSIGN) { /* ld zeroes? */ + r0 = (R[0] + (inc << 16)) & LMASK; /* retire increment */ + if (r0 & LSIGN) return 0; /* more? return 0 */ + inc = (r0 >> 16) & 0x1F; /* effective inc */ + } +r1 = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK; /* eff addr */ +r0 = (R[0] - inc) & 0x1F; /* eff lnt left */ +if (r0 == 0) { /* nothing left? */ + R[0] = -1; /* out of input */ + RSVD_OPND_FAULT; + } +c = Read (r1, L_BYTE, RA); +return (((r0 & 1)? (c >> 4): c) & 0xF); +} + +void edit_adv_src (int32 inc) +{ +if (R[0] & LSIGN) { /* ld zeroes? */ + R[0] = (R[0] + (inc << 16)) & LMASK; /* retire 0's */ + if (R[0] & LSIGN) return; /* more to do? */ + inc = (R[0] >> 16) & 0x1F; /* get excess */ + if (inc == 0) return; /* more to do? */ + } +R[1] = (R[1] + (inc / 2) + ((~R[0] & inc) & 1)) & LMASK;/* retire src */ +R[0] = (R[0] - inc) & 0x1F; +return; +} + +int32 edit_read_sign (int32 acc) +{ +int32 sign; + +sign = Read ((R[3] + 1) & LMASK, L_BYTE, RA); /* read */ +R[2] = ED_PUTSIGN (R[2], sign); /* now fault safe */ +return sign; +} + +#else + +extern int32 R[16]; +extern int32 PSL; +extern int32 SCBB; +extern int32 fault_PC; +extern int32 ibcnt, ppc; +extern int32 pcq[PCQ_SIZE]; +extern int32 pcq_p; +extern jmp_buf save_env; + +/* CIS instructions - invoke emulator interface + + opnd[0:5] = six operands to be pushed (if PSL = 0) + cc = condition codes + opc = opcode + + If FPD is set, push old PC and PSL on stack, vector thru SCB. + If FPD is clear, push opcode, old PC, operands, new PC, and PSL + on stack, vector thru SCB. + In both cases, the exception occurs in the current mode. +*/ + +int32 op_cis (int32 *opnd, int32 cc, int32 opc, int32 acc) +{ +int32 vec; + +if (PSL & PSL_FPD) { /* FPD set? */ + Read (SP - 1, L_BYTE, WA); /* wchk stack */ + Write (SP - 8, fault_PC, L_LONG, WA); /* push old PC */ + Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ + SP = SP - 8; /* decr stk ptr */ + vec = ReadLP ((SCBB + SCB_EMULFPD) & PAMASK); + } +else { + if (opc == CVTPL) /* CVTPL? .wl */ + opnd[2] = (opnd[2] >= 0)? ~opnd[2]: opnd[3]; + Read (SP - 1, L_BYTE, WA); /* wchk stack */ + Write (SP - 48, opc, L_LONG, WA); /* push opcode */ + Write (SP - 44, fault_PC, L_LONG, WA); /* push old PC */ + Write (SP - 40, opnd[0], L_LONG, WA); /* push operands */ + Write (SP - 36, opnd[1], L_LONG, WA); + Write (SP - 32, opnd[2], L_LONG, WA); + Write (SP - 28, opnd[3], L_LONG, WA); + Write (SP - 24, opnd[4], L_LONG, WA); + Write (SP - 20, opnd[5], L_LONG, WA); + Write (SP - 8, PC, L_LONG, WA); /* push cur PC */ + Write (SP - 4, PSL | cc, L_LONG, WA); /* push PSL */ + SP = SP - 48; /* decr stk ptr */ + vec = ReadLP ((SCBB + SCB_EMULATE) & PAMASK); + } +PSL = PSL & ~(PSL_TP | PSL_FPD | PSW_DV | PSW_FU | PSW_IV | PSW_T); +JUMP (vec & ~03); /* set new PC */ +return 0; /* set new cc's */ +} + +#endif diff --git a/VAX/vax_cmode.c b/VAX/vax_cmode.c new file mode 100644 index 0000000..b48fb7e --- /dev/null +++ b/VAX/vax_cmode.c @@ -0,0 +1,1109 @@ +/* vax_cmode.c: VAX compatibility mode + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + On a full VAX, this module implements PDP-11 compatibility mode. + On a subset VAX, this module forces a fault if REI attempts to set PSL. + + 28-May-08 RMS Inlined physical memory routines + 25-Jan-08 RMS Fixed declaration (from Mark Pizzolato) + 03-May-06 RMS Fixed omission of SXT + Fixed order of operand fetching in XOR + 24-Aug-04 RMS Cloned from PDP-11 CPU + + In compatibility mode, the Istream prefetch mechanism is not used. The + prefetcher will be explicitly resynchronized through intexc on any exit + from compatibility mode. +*/ + +#include "vax_defs.h" + +#if defined (FULL_VAX) + +#define RdMemB(a) Read (a, L_BYTE, RA) +#define RdMemMB(a) Read (a, L_BYTE, WA) +#define WrMemB(d,a) Write (a, d, L_BYTE, WA) +#define BRANCH_F(x) CMODE_JUMP ((PC + (((x) + (x)) & BMASK)) & WMASK) +#define BRANCH_B(x) CMODE_JUMP ((PC + (((x) + (x)) | 0177400)) & WMASK) +#define CC_XOR_NV(x) ((((x) & CC_N) != 0) ^ (((x) & CC_V) != 0)) +#define CC_XOR_NC(x) ((((x) & CC_N) != 0) ^ (((x) & CC_C) != 0)) + +extern int32 R[16]; +extern int32 PSL; +extern int32 trpirq; +extern int32 p1; +extern int32 fault_PC; +extern int32 recq[]; /* recovery queue */ +extern int32 recqptr; /* recq pointer */ +extern int32 pcq[]; +extern int32 pcq_p; +extern int32 ibcnt, ppc; +extern int32 sim_interval; +extern uint32 sim_brk_summ; +extern jmp_buf save_env; + +int32 GeteaB (int32 spec); +int32 GeteaW (int32 spec); +int32 RdMemW (int32 a); +int32 RdMemMW (int32 a); +void WrMemW (int32 d, int32 a); +int32 RdRegB (int32 rn); +int32 RdRegW (int32 rn); +void WrRegB (int32 val, int32 rn); +void WrRegW (int32 val, int32 rn); + +/* Validate PSL for compatibility mode */ + +t_bool BadCmPSL (int32 newpsl) +{ +if ((newpsl & (PSL_FPD|PSL_IS|PSL_CUR|PSL_PRV|PSL_IPL)) != + ((USER << PSL_V_CUR) | (USER << PSL_V_PRV))) + return TRUE; +else return FALSE; +} + +/* Compatibility mode execution */ + +int32 op_cmode (int32 cc) +{ +int32 IR, srcspec, dstspec, srcreg, dstreg, ea; +int32 i, t, src, src2, dst, sign, oc; +int32 acc = ACC_MASK (USER); + +PC = PC & WMASK; /* PC must be 16b */ +if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */ + ABORT (STOP_IBKPT); /* stop simulation */ + } +sim_interval = sim_interval - 1; /* count instr */ + +IR = RdMemW (PC); /* fetch instruction */ +PC = (PC + 2) & WMASK; /* incr PC, mod 65k */ +srcspec = (IR >> 6) & 077; /* src, dst specs */ +dstspec = IR & 077; +srcreg = (srcspec <= 07); /* src, dst = rmode? */ +dstreg = (dstspec <= 07); +switch ((IR >> 12) & 017) { /* decode IR<15:12> */ + +/* Opcode 0: no operands, specials, branches, JSR, SOPs */ + + case 000: /* 00xxxx */ + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + case 000: /* 0000xx */ + switch (IR) { /* decode IR<5:0> */ + case 3: /* BPT */ + CMODE_FAULT (CMODE_BPT); + break; + + case 4: /* IOT */ + CMODE_FAULT (CMODE_IOT); + break; + + case 2: /* RTI */ + case 6: /* RTT */ + src = RdMemW (R[6] & WMASK); /* new PC */ + src2 = RdMemW ((R[6] + 2) & WMASK); /* new PSW */ + R[6] = (R[6] + 4) & WMASK; + cc = src2 & CC_MASK; /* update cc, T */ + if (src2 & PSW_T) PSL = PSL | PSW_T; + else PSL = PSL & ~PSW_T; + CMODE_JUMP (src); /* update PC */ + break; + + default: /* undefined */ + CMODE_FAULT (CMODE_RSVI); + break; + } /* end switch IR */ + break; /* end case 0000xx */ + + case 001: /* JMP */ + if (dstreg) CMODE_FAULT (CMODE_ILLI); /* mode 0 illegal */ + else { CMODE_JUMP (GeteaW (dstspec)); } + break; + + case 002: /* 0002xx */ + if (IR < 000210) { /* RTS */ + dstspec = dstspec & 07; + if (dstspec != 7) { /* PC <- r */ + CMODE_JUMP (RdRegW (dstspec)); + } + dst = RdMemW (R[6]); /* t <- (sp)+ */ + R[6] = (R[6] + 2) & WMASK; + WrRegW (dst, dstspec); /* r <- t */ + break; /* end if RTS */ + } + if (IR < 000240) { /* [210:237] */ + CMODE_FAULT (CMODE_RSVI); + break; + } + if (IR < 000260) cc = cc & ~(IR & CC_MASK); /* clear CC */ + else cc = cc | (IR & CC_MASK); /* set CC */ + break; + + case 003: /* SWAB */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = ((src & BMASK) << 8) | ((src >> 8) & BMASK); + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_B ((dst & BMASK)); + break; + + case 004: case 005: /* BR */ + BRANCH_F (IR); + break; + + case 006: case 007: /* BR */ + BRANCH_B (IR); + break; + + case 010: case 011: /* BNE */ + if ((cc & CC_Z) == 0) { BRANCH_F (IR); } + break; + + case 012: case 013: /* BNE */ + if ((cc & CC_Z) == 0) { BRANCH_B (IR); } + break; + + case 014: case 015: /* BEQ */ + if (cc & CC_Z) { BRANCH_F (IR); } + break; + + case 016: case 017: /* BEQ */ + if (cc & CC_Z) { BRANCH_B (IR); } + break; + + case 020: case 021: /* BGE */ + if (CC_XOR_NV (cc) == 0) { BRANCH_F (IR); } + break; + + case 022: case 023: /* BGE */ + if (CC_XOR_NV (cc) == 0) { BRANCH_B (IR); } + break; + + case 024: case 025: /* BLT */ + if (CC_XOR_NV (cc)) { BRANCH_F (IR); } + break; + + case 026: case 027: /* BLT */ + if (CC_XOR_NV (cc)) { BRANCH_B (IR); } + break; + + case 030: case 031: /* BGT */ + if (((cc & CC_Z) || CC_XOR_NV (cc)) == 0) { BRANCH_F (IR); } + break; + + case 032: case 033: /* BGT */ + if (((cc & CC_Z) || CC_XOR_NV (cc)) == 0) { BRANCH_B (IR); } + break; + + case 034: case 035: /* BLE */ + if ((cc & CC_Z) || CC_XOR_NV (cc)) { BRANCH_F (IR); } + break; + + case 036: case 037: /* BLE */ + if ((cc & CC_Z) || CC_XOR_NV (cc)) { BRANCH_B (IR); } + break; + + case 040: case 041: case 042: case 043: /* JSR */ + case 044: case 045: case 046: case 047: + if (dstreg) CMODE_FAULT (CMODE_ILLI); /* mode 0 illegal */ + else { + srcspec = srcspec & 07; /* get reg num */ + dst = GeteaW (dstspec); /* get dst addr */ + src = RdRegW (srcspec); /* get src reg */ + WrMemW (src, (R[6] - 2) & WMASK); /* -(sp) <- r */ + R[6] = (R[6] - 2) & WMASK; + if (srcspec != 7) WrRegW (PC, srcspec); /* r <- PC */ + CMODE_JUMP (dst); /* PC <- dst */ + } + break; /* end JSR */ + + case 050: /* CLR */ + if (dstreg) WrRegW (0, dstspec); + else WrMemW (0, GeteaW (dstspec)); + cc = CC_Z; + break; + + case 051: /* COM */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = src ^ WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + cc = cc | CC_C; + break; + + case 052: /* INC */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src + 1) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + if (dst == 0100000) cc = cc | CC_V; + break; + + case 053: /* DEC */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src - 1) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + if (dst == 077777) cc = cc | CC_V; + break; + + case 054: /* NEG */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (-src) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (dst == 0100000) cc = cc | CC_V; + if (dst) cc = cc | CC_C; + break; + + case 055: /* ADC */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src + (cc & CC_C)) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if ((src == 077777) && (dst == 0100000)) cc = cc | CC_V; + if ((src == 0177777) && (dst == 0)) cc = cc | CC_C; + break; + + case 056: /* SBC */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src - (cc & CC_C)) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if ((src == 0100000) && (dst == 077777)) cc = cc | CC_V; + if ((src == 0) && (dst == 0177777)) cc = cc | CC_C; + break; + + case 057: /* TST */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemW (GeteaW (dstspec)); + CC_IIZZ_W (src); + break; + + case 060: /* ROR */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src >> 1) | ((cc & CC_C)? WSIGN: 0); + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & 1) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 061: /* ROL */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = ((src << 1) | ((cc & CC_C)? 1: 0)) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & WSIGN) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 062: /* ASR */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src & WSIGN) | (src >> 1); + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & 1) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 063: /* ASL */ + if (dstreg) src = RdRegW (dstspec); + else src = RdMemMW (ea = GeteaW (dstspec)); + dst = (src << 1) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (src & WSIGN) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 065: /* MFPI */ + if (dstreg) dst = RdRegW (dstspec); /* "mov dst,-(sp)" */ + else dst = RdMemW (GeteaW (dstspec)); + WrMemW (dst, (R[6] - 2) & WMASK); + R[6] = (R[6] - 2) & WMASK; + CC_IIZP_W (dst); + break; + + case 066: /* MTPI */ + dst = RdMemW (R[6] & WMASK); /* "mov (sp)+,dst" */ + R[6] = (R[6] + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, 6); + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, (GeteaW (dstspec) & WMASK)); + CC_IIZP_W (dst); + break; + + case 067: /* SXT */ + dst = (cc & CC_N)? 0177777: 0; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, GeteaW (dstspec)); + CC_IIZP_W (dst); + break; + + default: /* undefined */ + CMODE_FAULT (CMODE_RSVI); + break; + } /* end switch SOPs */ + break; /* end case 000 */ + +/* Opcodes 01 - 06: double operand word instructions + + Compatibility mode requires source address decode, source fetch, + dest address decode, dest fetch/store. + + Add: v = [sign (src) = sign (src2)] and [sign (src) != sign (result)] + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] +*/ + + case 001: /* MOV */ + if (srcreg) src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) WrRegW (src, dstspec); + else WrMemW (src, GeteaW (dstspec)); + CC_IIZP_W (src); + break; + + case 002: /* CMP */ + if (srcreg) src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + dst = (src - src2) & WMASK; + CC_IIZZ_W (dst); + if (((src ^ src2) & (~src2 ^ dst)) & WSIGN) cc = cc | CC_V; + if (src < src2) cc = cc | CC_C; + break; + + case 003: /* BIT */ + if (srcreg) src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) src2 = RdRegW (dstspec); + else src2 = RdMemW (GeteaW (dstspec)); + dst = src2 & src; + CC_IIZP_W (dst); + break; + + case 004: /* BIC */ + if (srcreg) src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = src2 & ~src; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + break; + + case 005: /* BIS */ + if (srcreg) src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = src2 | src; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZP_W (dst); + break; + + case 006: /* ADD */ + if (srcreg) src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = (src2 + src) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_ADD_W (dst, src, src2); + break; + +/* Opcode 07: EIS, FIS (not implemented), CIS + + Notes: + - MUL carry: C is set if the (signed) result doesn't fit in 16 bits. + - Divide has three error cases: + 1. Divide by zero. + 2. Divide largest negative number by -1. + 3. (Signed) quotient doesn't fit in 16 bits. + Cases 1 and 2 must be tested in advance, to avoid C runtime errors. + - ASHx left: overflow if the bits shifted out do not equal the sign + of the result (convert shift out to 1/0, xor against sign). + - ASHx right: if right shift sign extends, then the shift and + conditional or of shifted -1 is redundant. If right shift zero + extends, then the shift and conditional or does sign extension. +*/ + + case 007: /* EIS */ + srcspec = srcspec & 07; /* get src reg */ + switch ((IR >> 9) & 07) { /* decode IR<11:9> */ + + case 0: /* MUL */ + if (dstreg) src2 = RdRegW (dstspec); /* get src2 */ + else src2 = RdMemW (GeteaW (dstspec)); + src = RdRegW (srcspec); /* get src */ + if (src2 & WSIGN) src2 = src2 | ~WMASK; /* sext src, src2 */ + if (src & WSIGN) src = src | ~WMASK; + dst = src * src2; /* multiply */ + WrRegW ((dst >> 16) & WMASK, srcspec); /* high 16b */ + WrRegW (dst & WMASK, srcspec | 1); /* low 16b */ + CC_IIZZ_L (dst & LMASK); + if ((dst > 077777) || (dst < -0100000)) cc = cc | CC_C; + break; + + case 1: /* DIV */ + if (dstreg) src2 = RdRegW (dstspec); /* get src2 */ + else src2 = RdMemW (GeteaW (dstspec)); + t = RdRegW (srcspec); + src = (((uint32) t) << 16) | RdRegW (srcspec | 1); + if (src2 == 0) { /* div by 0? */ + cc = CC_V | CC_C; /* set cc's */ + break; /* done */ + } + if ((src == LSIGN) && (src2 == WMASK)) { /* -2^31 / -1? */ + cc = CC_V; /* overflow */ + break; /* done */ + } + if (src2 & WSIGN) src2 = src2 | ~WMASK; /* sext src, src2 */ + if (t & WSIGN) src = src | ~LMASK; + dst = src / src2; /* divide */ + if ((dst > 077777) || (dst < -0100000)) { /* out of range? */ + cc = CC_V; /* overflow */ + break; + } + CC_IIZZ_W (dst & WMASK); /* set cc's */ + WrRegW (dst & WMASK, srcspec); /* quotient */ + WrRegW ((src - (src2 * dst)) & WMASK, srcspec | 1); + break; + + case 2: /* ASH */ + if (dstreg) src2 = RdRegW (dstspec); /* get src2 */ + else src2 = RdMemW (GeteaW (dstspec)); + src2 = src2 & 077; + src = RdRegW (srcspec); /* get src */ + if (sign = ((src & WSIGN)? 1: 0)) src = src | ~WMASK; + if (src2 == 0) { /* [0] */ + dst = src; /* result */ + oc = 0; /* last bit out */ + } + else if (src2 <= 15) { /* [1,15] */ + dst = src << src2; + i = (src >> (16 - src2)) & WMASK; + oc = (i & 1)? CC_C: 0; + if ((dst & WSIGN)? (i != WMASK): (i != 0)) oc = oc | CC_V; + } + else if (src2 <= 31) { /* [16,31] */ + dst = 0; + oc = ((src << (src2 - 16)) & 1)? CC_C: 0; + if (src) oc = oc | CC_V; + } + else if (src2 == 32) { /* [32] = -32 */ + dst = -sign; + oc = sign? CC_C: 0; + } + else { /* [33,63] = -31,-1 */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + oc = ((src >> (63 - src2)) & 1)? CC_C: 0; + } + WrRegW (dst = dst & WMASK, srcspec); /* result */ + CC_IIZZ_W (dst); + cc = cc | oc; + break; + + case 3: /* ASHC */ + if (dstreg) src2 = RdRegW (dstspec); /* get src2 */ + else src2 = RdMemW (GeteaW (dstspec)); + src2 = src2 & 077; + t = RdRegW (srcspec); + src = (((uint32) t) << 16) | RdRegW (srcspec | 1); + sign = (t & WSIGN)? 1: 0; /* get src sign */ + if (src2 == 0) { /* [0] */ + dst = src; /* result */ + oc = 0; /* last bit out */ + } + else if (src2 <= 31) { /* [1,31] */ + dst = ((uint32) src) << src2; + i = ((src >> (32 - src2)) | (-sign << src2)) & LMASK; + oc = (i & 1)? CC_C: 0; + if ((dst & LSIGN)? (i != LMASK): (i != 0)) oc = oc | CC_V; + } + else if (src2 == 32) { /* [32] = -32 */ + dst = -sign; + oc = sign? CC_C: 0; + } + else { /* [33,63] = -31,-1 */ + dst = (src >> (64 - src2)) | (-sign << (src2 - 32)); + oc = ((src >> (63 - src2)) & 1)? CC_C: 0; + } + WrRegW ((dst >> 16) & WMASK, srcspec); /* high result */ + WrRegW (dst & WMASK, srcspec | 1); /* low result */ + CC_IIZZ_L (dst & LMASK); + cc = cc | oc; + break; + + case 4: /* XOR */ + src = RdRegW (srcspec); /* get src */ + if (dstreg) src2 = RdRegW (dstspec); /* get dst */ + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = src2 ^ src; + if (dstreg) WrRegW (dst, dstspec); /* result */ + else WrMemW (dst, ea); + CC_IIZP_W (dst); + break; + + case 7: /* SOB */ + dst = (RdRegW (srcspec) - 1) & WMASK; /* decr reg */ + WrRegW (dst, srcspec); /* result */ + if (dst != 0) { /* br if zero */ + CMODE_JUMP ((PC - dstspec - dstspec) & WMASK); + } + break; + + default: + CMODE_FAULT (CMODE_RSVI); /* end switch EIS */ + } + break; /* end case 007 */ + +/* Opcode 10: branches, traps, SOPs */ + + case 010: + switch ((IR >> 6) & 077) { /* decode IR<11:6> */ + case 000: case 001: /* BPL */ + if ((cc & CC_N) == 0) { BRANCH_F (IR); } + break; + + case 002: case 003: /* BPL */ + if ((cc & CC_N) == 0) { BRANCH_B (IR); } + break; + + case 004: case 005: /* BMI */ + if (cc & CC_N) { BRANCH_F (IR); } + break; + + case 006: case 007: /* BMI */ + if (cc & CC_N) { BRANCH_B (IR); } + break; + + case 010: case 011: /* BHI */ + if ((cc & (CC_C | CC_Z)) == 0) { BRANCH_F (IR); } + break; + + case 012: case 013: /* BHI */ + if ((cc & (CC_C | CC_Z)) == 0) { BRANCH_B (IR); } + break; + + case 014: case 015: /* BLOS */ + if (cc & (CC_C | CC_Z)) { BRANCH_F (IR); } + break; + + case 016: case 017: /* BLOS */ + if (cc & (CC_C | CC_Z)) { BRANCH_B (IR); } + break; + + case 020: case 021: /* BVC */ + if ((cc & CC_V) == 0) { BRANCH_F (IR); } + break; + + case 022: case 023: /* BVC */ + if ((cc & CC_V) == 0) { BRANCH_B (IR); } + break; + + case 024: case 025: /* BVS */ + if (cc & CC_V) { BRANCH_F (IR); } + break; + + case 026: case 027: /* BVS */ + if (cc & CC_V) { BRANCH_B (IR); } + break; + + case 030: case 031: /* BCC */ + if ((cc & CC_C) == 0) { BRANCH_F (IR); } + break; + + case 032: case 033: /* BCC */ + if ((cc & CC_C) == 0) { BRANCH_B (IR); } + break; + + case 034: case 035: /* BCS */ + if (cc & CC_C) { BRANCH_F (IR); } + break; + + case 036: case 037: /* BCS */ + if (cc & CC_C) { BRANCH_B (IR); } + break; + + case 040: case 041: case 042: case 043: /* EMT */ + CMODE_FAULT (CMODE_EMT); + break; + + case 044: case 045: case 046: case 047: /* TRAP */ + CMODE_FAULT (CMODE_TRAP); + break; + + case 050: /* CLRB */ + if (dstreg) WrRegB (0, dstspec); + else WrMemB (0, GeteaB (dstspec)); + cc = CC_Z; + break; + + case 051: /* COMB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = src ^ BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + cc = cc | CC_C; + break; + + case 052: /* INCB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src + 1) & BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + if (dst == 0200) cc = cc | CC_V; + break; + + case 053: /* DECB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src - 1) & BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + if (dst == 0177) cc = cc | CC_V; + break; + + case 054: /* NEGB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (-src) & BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (dst == 0200) cc = cc | CC_V; + if (dst) cc = cc | CC_C; + break; + + case 055: /* ADCB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src + (cc & CC_C)) & BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if ((src == 0177) && (dst == 0200)) cc = cc | CC_V; + if ((src == 0377) && (dst == 0)) cc = cc | CC_C; + break; + + case 056: /* SBCB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src - (cc & CC_C)) & BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if ((src == 0200) && (dst == 0177)) cc = cc | CC_V; + if ((src == 0) && (dst == 0377)) cc = cc | CC_C; + break; + + case 057: /* TSTB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemB (GeteaB (dstspec)); + CC_IIZZ_B (src); + break; + + case 060: /* RORB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src >> 1) | ((cc & CC_C)? BSIGN: 0); + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & 1) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 061: /* ROLB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = ((src << 1) | ((cc & CC_C)? 1: 0)) & BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & BSIGN) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 062: /* ASRB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src >> 1) | (src & BSIGN); + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & 1) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 063: /* ASLB */ + if (dstreg) src = RdRegB (dstspec); + else src = RdMemMB (ea = GeteaB (dstspec)); + dst = (src << 1) & BMASK; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZZ_B (dst); + if (src & BSIGN) cc = cc | CC_C; + if (CC_XOR_NC (cc)) cc = cc | CC_V; + break; + + case 065: /* MFPD */ + if (dstreg) dst = RdRegW (dstspec); /* "mov dst,-(sp)" */ + else dst = RdMemW (GeteaW (dstspec)); + WrMemW (dst, (R[6] - 2) & WMASK); + R[6] = (R[6] - 2) & WMASK; + CC_IIZP_W (dst); + break; + + case 066: /* MTPD */ + dst = RdMemW (R[6] & WMASK); /* "mov (sp)+,dst" */ + R[6] = (R[6] + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, 6); + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, (GeteaW (dstspec) & WMASK)); + CC_IIZP_W (dst); + break; + + default: + CMODE_FAULT (CMODE_RSVI); + break; } /* end switch SOPs */ + break; /* end case 010 */ + +/* Opcodes 11 - 16: double operand byte instructions + + Cmp: v = [sign (src) != sign (src2)] and [sign (src2) = sign (result)] + Sub: v = [sign (src) != sign (src2)] and [sign (src) = sign (result)] +*/ + + case 011: /* MOVB */ + if (srcreg) src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) WrRegW ((src & BSIGN)? (0xFF00 | src): src, dstspec); + else WrMemB (src, GeteaB (dstspec)); + CC_IIZP_B (src); + break; + + case 012: /* CMPB */ + if (srcreg) src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) src2 = RdRegB (dstspec); + else src2 = RdMemB (GeteaB (dstspec)); + dst = (src - src2) & BMASK; + CC_IIZZ_B (dst); + if (((src ^ src2) & (~src2 ^ dst)) & BSIGN) cc = cc | CC_V; + if (src < src2) cc = cc | CC_C; + break; + + case 013: /* BITB */ + if (srcreg) src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) src2 = RdRegB (dstspec); + else src2 = RdMemB (GeteaB (dstspec)); + dst = src2 & src; + CC_IIZP_B (dst); + break; + + case 014: /* BICB */ + if (srcreg) src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) src2 = RdRegB (dstspec); + else src2 = RdMemMB (ea = GeteaB (dstspec)); + dst = src2 & ~src; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + break; + + case 015: /* BISB */ + if (srcreg) src = RdRegB (srcspec); + else src = RdMemB (GeteaB (srcspec)); + if (dstreg) src2 = RdRegB (dstspec); + else src2 = RdMemMB (ea = GeteaB (dstspec)); + dst = src2 | src; + if (dstreg) WrRegB (dst, dstspec); + else WrMemB (dst, ea); + CC_IIZP_B (dst); + break; + + case 016: /* SUB */ + if (srcreg) src = RdRegW (srcspec); + else src = RdMemW (GeteaW (srcspec)); + if (dstreg) src2 = RdRegW (dstspec); + else src2 = RdMemMW (ea = GeteaW (dstspec)); + dst = (src2 - src) & WMASK; + if (dstreg) WrRegW (dst, dstspec); + else WrMemW (dst, ea); + CC_IIZZ_W (dst); + if (((src ^ src2) & (~src ^ dst)) & WSIGN) cc = cc | CC_V; + if (src2 < src) cc = cc | CC_C; + break; + + default: + CMODE_FAULT (CMODE_RSVI); + break; + } /* end switch op */ + +return cc; +} + +/* Effective address calculations + + Inputs: + spec = specifier <5:0> + Outputs: + ea = effective address +*/ + +int32 GeteaW (int32 spec) +{ +int32 adr, reg; + +reg = spec & 07; /* register number */ +switch (spec >> 3) { /* decode spec<5:3> */ + + default: /* can't get here */ + case 1: /* (R) */ + if (reg == 7) return (PC & WMASK); + else return (R[reg] & WMASK); + + case 2: /* (R)+ */ + if (reg == 7) PC = ((adr = PC) + 2) & WMASK; + else { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + return adr; + + case 3: /* @(R)+ */ + if (reg == 7) PC = ((adr = PC) + 2) & WMASK; + else { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + return RdMemW (adr); + + case 4: /* -(R) */ + if (reg == 7) adr = PC = (PC - 2) & WMASK; + else { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + return adr; + + case 5: /* @-(R) */ + if (reg == 7) adr = PC = (PC - 2) & WMASK; + else { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + return RdMemW (adr); + + case 6: /* d(r) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) return ((PC + adr) & WMASK); + else return ((R[reg] + adr) & WMASK); + + case 7: /* @d(R) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) adr = (PC + adr) & WMASK; + else adr = (R[reg] + adr) & WMASK; + return RdMemW (adr); + } /* end switch */ +} + +int32 GeteaB (int32 spec) +{ +int32 adr, reg; + +reg = spec & 07; /* reg number */ +switch (spec >> 3) { /* decode spec<5:3> */ + + default: /* can't get here */ + case 1: /* (R) */ + if (reg == 7) return (PC & WMASK); + else return (R[reg] & WMASK); + + case 2: /* (R)+ */ + if (reg == 7) PC = ((adr = PC) + 2) & WMASK; + else if (reg == 6) { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + else { + R[reg] = ((adr = R[reg]) + 1) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RB, reg); + } + return adr; + + case 3: /* @(R)+ */ + if (reg == 7) PC = ((adr = PC) + 2) & WMASK; + else { + R[reg] = ((adr = R[reg]) + 2) & WMASK; + recq[recqptr++] = RQ_REC (AIN|RW, reg); + } + return RdMemW (adr); + + case 4: /* -(R) */ + if (reg == 7) adr = PC = (PC - 2) & WMASK; + else if (reg == 6) { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + else { + adr = R[reg] = (R[reg] - 1) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RB, reg); + } + return adr; + + case 5: /* @-(R) */ + if (reg == 7) adr = PC = (PC - 2) & WMASK; + else { + adr = R[reg] = (R[reg] - 2) & WMASK; + recq[recqptr++] = RQ_REC (ADC|RW, reg); + } + return RdMemW (adr); + + case 6: /* d(r) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) return ((PC + adr) & WMASK); + else return ((R[reg] + adr) & WMASK); + + case 7: /* @d(R) */ + adr = RdMemW (PC); + PC = (PC + 2) & WMASK; + if (reg == 7) adr = (PC + adr) & WMASK; + else adr = (R[reg] + adr) & WMASK; + return RdMemW (adr); + } /* end switch */ +} + +/* Memory and register access routines */ + +int32 RdMemW (int32 a) +{ +int32 acc = ACC_MASK (USER); + +if (a & 1) CMODE_FAULT (CMODE_ODD); +return Read (a, L_WORD, RA); +} + +int32 RdMemMW (int32 a) +{ +int32 acc = ACC_MASK (USER); + +if (a & 1) CMODE_FAULT (CMODE_ODD); +return Read (a, L_WORD, WA); +} + +void WrMemW (int32 d, int32 a) +{ +int32 acc = ACC_MASK (USER); + +if (a & 1) CMODE_FAULT (CMODE_ODD); +Write (a, d, L_WORD, WA); +return; +} + +int32 RdRegB (int32 rn) +{ +if (rn == 7) return (PC & BMASK); +else return (R[rn] & BMASK); +} + +int32 RdRegW (int32 rn) +{ +if (rn == 7) return (PC & WMASK); +else return (R[rn] & WMASK); +} + +void WrRegB (int32 val, int32 rn) +{ +if (rn == 7) { CMODE_JUMP ((PC & ~BMASK) | val); } +else R[rn] = (R[rn] & ~BMASK) | val; +return; +} + +void WrRegW (int32 val, int32 rn) +{ +if (rn == 7) { CMODE_JUMP (val); } +else R[rn] = val; +return; +} + +#else + +/* Subset VAX + + Never legal to set CM in PSL + Should never get to instruction execution +*/ + +extern jmp_buf save_env; + +t_bool BadCmPSL (int32 newpsl) +{ +return TRUE; /* always bad */ +} + +int32 op_cmode (int32 cc) +{ +RSVD_INST_FAULT; +return cc; +} + +#endif diff --git a/VAX/vax_cpu.c b/VAX/vax_cpu.c new file mode 100644 index 0000000..7b690e2 --- /dev/null +++ b/VAX/vax_cpu.c @@ -0,0 +1,3281 @@ +/* vax_cpu.c: VAX CPU + + Copyright (c) 1998-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + cpu VAX central processor + + 28-May-08 RMS Inlined instruction prefetch, physical memory routines + 13-Aug-07 RMS Fixed bug in read access g-format indexed specifiers + 28-Apr-07 RMS Removed clock initialization + 29-Oct-06 RMS Added idle support + 22-May-06 RMS Fixed format error in CPU history (found by Peter Schorn) + 10-May-06 RMS Added -kesu switches for virtual addressing modes + Fixed bugs in examine virtual + Rewrote history function for greater usability + Fixed bug in reported VA on faulting cross-page write + 02-May-06 RMS Fixed fault cleanup to clear PSL + Fixed ADAWI r-mode to preserve dst<31:16> + Fixed ACBD/G to test correct operand + Fixed access checking on modify-class specifiers + Fixed branch displacements in history buffer + (all reported by Tim Stark) + 17-Nov-05 RMS Fixed CVTfi with integer overflow to trap if PSW set + 13-Nov-05 RMS Fixed breakpoint test with 64b addresses + 25-Oct-05 RMS Removed cpu_extmem + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 13-Jan-05 RMS Fixed initial state of cpu_extmem + 06-Nov-04 RMS Added =n to SHOW HISTORY + 30-Sep-04 RMS Added octaword specifier decodes and instructions + Moved model-specific routines to system module + 02-Sep-04 RMS Fixed bug in EMODD/G, second word of quad dst not probed + 28-Jun-04 RMS Fixed bug in DIVBx, DIVWx (reported by Peter Trimmel) + 18-Apr-04 RMS Added octaword macros + 25-Jan-04 RMS Removed local debug logging support + RMS,MP Added extended physical memory support + 31-Dec-03 RMS Fixed bug in set_cpu_hist + 21-Dec-03 RMS Added autoconfiguration controls + 29-Oct-03 RMS Fixed WriteB declaration (found by Mark Pizzolato) + 23-Sep-03 RMS Revised instruction history for dynamic sizing + 17-May-03 RMS Fixed operand order in EMODx + 23-Apr-03 RMS Revised for 32b/64b t_addr + 05-Jan-02 RMS Added memory size restore support + 25-Dec-02 RMS Added instruction history (from Mark Pizzolato) + 29-Sep-02 RMS Revised to build dib_tab dynamically + 14-Jul-02 RMS Added halt to console, infinite loop detection + (from Mark Pizzolato) + 02-May-02 RMS Fixed bug in indexed autoincrement register logging + 30-Apr-02 RMS Added TODR powerup routine + 18-Apr-02 RMS Cleanup ambiguous signed left shifts + 15-Apr-02 RMS Fixed bug in CASEL condition codes + + The register state for the VAX is: + + R[0:15] general registers + PSL<31:0> processor status longword + TP<30> trace pending + FPD<27> first part done + IS<26> interrupt stack + CM<25:24> current mode + PM<23:22> previous mode + IPL<20:16> interrupt priority level + PSW<15:0> non-privileged processor status word + DV<7> decimal overflow trap enable + FU<6> floating underflow fault enable + IV<5> integer overflow trap enable + T<4> trace trap enable + CC<3:0> condition codes + SCBB system control block base + PCBB process control block base + SBR system page table base + SLR system page table length + P0BR process region 0 page table base + P0LR process region 0 page table length + P1BR process region 1 page table base + P1LR process region 1 page table length + SIRR/SISR software interrupt request/summary register + ASTLVL AST level register + + The VAX has a variable length instruction format with up to six operands: + + opcode byte + operand 1 specifier + : + operand n specifier + + Each operand specifier is a byte consisting of an addressing mode, a + register, and possibly 1-8 bytes of extension: + + number name extension mnemonic operation + + 0-3 short literal - #n op <- specifier + 4 index - [Rn] index by Rn + 5 register - Rn op <- Rn + 6 register def - (Rn) op <- M[Rn] + 7 autodecrement - -(Rn) Rn <- Rn - length + op <- M[Rn] + 8 autoincrement - (Rn)+ op <- M[Rn] + Rn <- Rn + length + 9 auto deferred - @(Rn)+ op <- M[M[Rn]] + Rn <- Rn + 4 + A byte displ byte d d(Rn) op <- M[Rn + sxt.d] + B byte displ def byte d @d(Rn) op <- M[M[Rn + sxt.d]] + C word displ word d d(Rn) op <- M[Rn + sxt.d] + D word displ def word d @d(Rn) op <- M[M[Rn + sxt.d]] + E long displ long d d(Rn) op <- M[Rn + d] + F long displ def long d @d(Rn) op <- M[M[Rn + d]] + + When the general register is the PC, certain modes are forbidden, and + others have special interpretations: + + 4F index fault + 5F register fault + 6F register def fault + 7F autodecrement fault + 8F immediate 1-8B #imm op <- imm + 9 absolute 4B @#imm op <- M[imm] + A byte relative byte d d(Rn) op <- M[PC + sxt.d] + B byte rel def byte d @d(Rn) op <- M[M[PC + sxt.d]] + C word relative word d d(Rn) op <- M[PC + sxt.d] + D word rel def word d @d(Rn) op <- M[M[PC + sxt.d]] + E long relative long d d(Rn) op <- M[PC + d] + F long rel def long d @d(Rn) op <- M[M[PC + d]] + + This routine is the instruction decode routine for the VAX. It + is called from the simulator control program to execute instructions + in simulated memory, starting at the simulated PC. It runs until an + enabled exception is encountered. + + General notes: + + 1. Traps and interrupts. Variable trpirq microencodes the outstanding + trap request (if any) and the level of the highest outstanding + interrupt (if any). + + 2. Interrupt requests are maintained in the int_req array, one word per + interrupt level, one bit per device. + + 3. Adding I/O devices. These modules must be modified: + + vax_defs.h add device address and interrupt definitions + vax_sys.c add sim_devices table entry +*/ + +/* Definitions */ + +#include "vax_defs.h" + +#define OP_MEM -1 +#define UNIT_V_CONH (UNIT_V_UF + 0) /* halt to console */ +#define UNIT_V_MSIZE (UNIT_V_UF + 1) /* dummy */ +#define UNIT_CONH (1u << UNIT_V_CONH) +#define UNIT_MSIZE (1u << UNIT_V_MSIZE) +#define GET_CUR acc = ACC_MASK (PSL_GETCUR (PSL)) + +#define OPND_SIZE 16 +#define INST_SIZE 52 +#define op0 opnd[0] +#define op1 opnd[1] +#define op2 opnd[2] +#define op3 opnd[3] +#define op4 opnd[4] +#define op5 opnd[5] +#define op6 opnd[6] +#define op7 opnd[7] +#define op8 opnd[8] +#define CHECK_FOR_PC if (rn == nPC) RSVD_ADDR_FAULT +#define CHECK_FOR_SP if (rn >= nSP) RSVD_ADDR_FAULT +#define CHECK_FOR_AP if (rn >= nAP) RSVD_ADDR_FAULT +#define WRITE_B(r) if (spec > (GRN | nPC)) Write (va, r, L_BYTE, WA); \ + else R[rn] = (R[rn] & ~BMASK) | ((r) & BMASK) +#define WRITE_W(r) if (spec > (GRN | nPC)) Write (va, r, L_WORD, WA); \ + else R[rn] = (R[rn] & ~WMASK) | ((r) & WMASK) +#define WRITE_L(r) if (spec > (GRN | nPC)) Write (va, r, L_LONG, WA); \ + else R[rn] = (r) +#define WRITE_Q(rl,rh) if (spec > (GRN | nPC)) { \ + if ((Test (va + 7, WA, &mstat) >= 0) || \ + (Test (va, WA, &mstat) < 0)) \ + Write (va, rl, L_LONG, WA); \ + Write (va + 4, rh, L_LONG, WA); \ + } \ + else { \ + if (rn >= nSP) RSVD_ADDR_FAULT; \ + R[rn] = rl; \ + R[rn + 1] = rh; \ + } + +#define HIST_MIN 64 +#define HIST_MAX 65536 + +typedef struct { + int32 iPC; + int32 PSL; + int32 opc; + uint8 inst[INST_SIZE]; + int32 opnd[OPND_SIZE]; + } InstHistory; + +uint32 *M = NULL; /* memory */ +int32 R[16]; /* registers */ +int32 STK[5]; /* stack pointers */ +int32 PSL; /* PSL */ +int32 SCBB = 0; /* SCB base */ +int32 PCBB = 0; /* PCB base */ +int32 P0BR = 0; /* P0 mem mgt */ +int32 P0LR = 0; +int32 P1BR = 0; /* P1 mem mgt */ +int32 P1LR = 0; +int32 SBR = 0; /* S0 mem mgt */ +int32 SLR = 0; +int32 SISR; /* swre int req */ +int32 ASTLVL; /* AST level */ +int32 mapen; /* map enable */ +int32 pme; /* perf mon enable */ +int32 trpirq; /* trap/intr req */ +int32 in_ie = 0; /* in exc, int */ +int32 recq[6]; /* recovery queue */ +int32 recqptr; /* recq pointer */ +int32 hlt_pin = 0; /* HLT pin intr */ +int32 mem_err = 0; +int32 crd_err = 0; +int32 p1 = 0, p2 = 0; /* fault parameters */ +int32 fault_PC; /* fault PC */ +int32 pcq_p = 0; /* PC queue ptr */ +int32 hst_p = 0; /* history pointer */ +int32 hst_lnt = 0; /* history length */ +int32 badabo = 0; +int32 cpu_astop = 0; +int32 mchk_va, mchk_ref; /* mem ref param */ +int32 ibufl, ibufh; /* prefetch buf */ +int32 ibcnt, ppc; /* prefetch ctl */ +uint32 cpu_idle_ipl_mask = 0x8; /* idle if on IPL 3 */ +uint32 cpu_idle_type = 1; /* default to VMS */ +int32 cpu_idle_wait = 1000; /* for these cycles */ +jmp_buf save_env; +REG *pcq_r = NULL; /* PC queue reg ptr */ +int32 pcq[PCQ_SIZE] = { 0 }; /* PC queue */ +InstHistory *hst = NULL; /* instruction history */ + +const uint32 byte_mask[33] = { 0x00000000, + 0x00000001, 0x00000003, 0x00000007, 0x0000000F, + 0x0000001F, 0x0000003F, 0x0000007F, 0x000000FF, + 0x000001FF, 0x000003FF, 0x000007FF, 0x00000FFF, + 0x00001FFF, 0x00003FFF, 0x00007FFF, 0x0000FFFF, + 0x0001FFFF, 0x0003FFFF, 0x0007FFFF, 0x000FFFFF, + 0x001FFFFF, 0x003FFFFF, 0x007FFFFF, 0x00FFFFFF, + 0x01FFFFFF, 0x03FFFFFF, 0x07FFFFFF, 0x0FFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF + }; +const uint32 byte_sign[33] = { 0x00000000, + 0x00000001, 0x00000002, 0x00000004, 0x00000008, + 0x00000010, 0x00000020, 0x00000040, 0x00000080, + 0x00000100, 0x00000200, 0x00000400, 0x00000800, + 0x00001000, 0x00002000, 0x00004000, 0x00008000, + 0x00010000, 0x00020000, 0x00040000, 0x00080000, + 0x00100000, 0x00200000, 0x00400000, 0x00800000, + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000 + }; +const uint32 align[4] = { + 0xFFFFFFFF, 0x00FFFFFF, 0x0000FFFF, 0x000000FF + }; + +/* External and forward references */ + +extern int32 sim_interval; +extern int32 sim_int_char; +extern int32 sim_switches; +extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */ +extern t_bool sim_idle_enab; + +extern t_stat build_dib_tab (void); +extern UNIT rom_unit, nvr_unit; +extern int32 op_ashq (int32 *opnd, int32 *rh, int32 *flg); +extern int32 op_emul (int32 mpy, int32 mpc, int32 *rh); +extern int32 op_ediv (int32 *opnd, int32 *rh, int32 *flg); +extern int32 op_bb_n (int32 *opnd, int32 acc); +extern int32 op_bb_x (int32 *opnd, int32 newb, int32 acc); +extern int32 op_extv (int32 *opnd, int32 vfldrp1, int32 acc); +extern int32 op_ffs (uint32 fld, int32 size); +extern void op_insv (int32 *opnd, int32 vfldrp1, int32 acc); +extern int32 op_call (int32 *opnd, t_bool gs, int32 acc); +extern int32 op_ret (int32 acc); +extern int32 op_insque (int32 *opnd, int32 acc); +extern int32 op_remque (int32 *opnd, int32 acc); +extern int32 op_insqhi (int32 *opnd, int32 acc); +extern int32 op_insqti (int32 *opnd, int32 acc); +extern int32 op_remqhi (int32 *opnd, int32 acc); +extern int32 op_remqti (int32 *opnd, int32 acc); +extern void op_pushr (int32 *opnd, int32 acc); +extern void op_popr (int32 *opnd, int32 acc); +extern int32 op_movc (int32 *opnd, int32 opc, int32 acc); +extern int32 op_cmpc (int32 *opnd, int32 opc, int32 acc); +extern int32 op_locskp (int32 *opnd, int32 opc, int32 acc); +extern int32 op_scnspn (int32 *opnd, int32 opc, int32 acc); +extern int32 op_chm (int32 *opnd, int32 cc, int32 opc); +extern int32 op_rei (int32 acc); +extern void op_ldpctx (int32 acc); +extern void op_svpctx (int32 acc); +extern int32 op_probe (int32 *opnd, int32 opc); +extern int32 op_mtpr (int32 *opnd); +extern int32 op_mfpr (int32 *opnd); +extern int32 op_movfd (int32 val); +extern int32 op_movg (int32 val); +extern int32 op_mnegfd (int32 val); +extern int32 op_mnegg (int32 val); +extern int32 op_cmpfd (int32 h1, int32 l1, int32 h2, int32 l2); +extern int32 op_cmpg (int32 h1, int32 l1, int32 h2, int32 l2); +extern int32 op_cvtifdg (int32 val, int32 *rh, int32 opc); +extern int32 op_cvtfdgi (int32 *opnd, int32 *flg, int32 opc); +extern int32 op_cvtdf (int32 *opnd); +extern int32 op_cvtgf (int32 *opnd); +extern int32 op_cvtfg (int32 *opnd, int32 *rh); +extern int32 op_cvtgh (int32 *opnd, int32 *hflt); +extern int32 op_addf (int32 *opnd, t_bool sub); +extern int32 op_addd (int32 *opnd, int32 *rh, t_bool sub); +extern int32 op_addg (int32 *opnd, int32 *rh, t_bool sub); +extern int32 op_mulf (int32 *opnd); +extern int32 op_muld (int32 *opnd, int32 *rh); +extern int32 op_mulg (int32 *opnd, int32 *rh); +extern int32 op_divf (int32 *opnd); +extern int32 op_divd (int32 *opnd, int32 *rh); +extern int32 op_divg (int32 *opnd, int32 *rh); +extern int32 op_emodf (int32 *opnd, int32 *intgr, int32 *flg); +extern int32 op_emodd (int32 *opnd, int32 *rh, int32 *intgr, int32 *flg); +extern int32 op_emodg (int32 *opnd, int32 *rh, int32 *intgr, int32 *flg); +extern void op_polyf (int32 *opnd, int32 acc); +extern void op_polyd (int32 *opnd, int32 acc); +extern void op_polyg (int32 *opnd, int32 acc); +extern int32 op_cmode (int32 cc); +extern int32 op_cis (int32 *opnd, int32 cc, int32 opc, int32 acc); +extern int32 op_octa (int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va); +extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); +extern int32 Test (uint32 va, int32 acc, int32 *status); +extern int32 BadCmPSL (int32 newpsl); +extern int32 eval_int (void); +extern int32 get_vector (int32 lvl); +extern void set_map_reg (void); +extern void rom_wr_B (int32 pa, int32 val); +extern int32 machine_check (int32 p1, int32 opc, int32 cc, int32 delta); +extern const uint16 drom[NUM_INST][MAX_SPEC + 1]; +extern t_stat cpu_boot (int32 unitno, DEVICE *dptr); +extern int32 con_halt (int32 code, int32 cc); + +t_stat cpu_reset (DEVICE *dptr); +t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_show_virt (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc); +int32 cpu_get_vsw (int32 sw); +SIM_INLINE int32 get_istr (int32 lnt, int32 acc); +int32 ReadOcta (int32 va, int32 *opnd, int32 j, int32 acc); +t_bool cpu_show_opnd (FILE *st, InstHistory *h, int32 line); +int32 cpu_psl_ipl_idle (int32 newpsl); +t_stat cpu_idle_svc (UNIT *uptr); + +/* CPU data structures + + cpu_dev CPU device descriptor + cpu_unit CPU unit + cpu_reg CPU register list + cpu_mod CPU modifier list +*/ + +UNIT cpu_unit = { + UDATA (&cpu_idle_svc, UNIT_FIX|UNIT_BINK, INITMEMSIZE) + }; + +REG cpu_reg[] = { + { HRDATA (PC, R[nPC], 32) }, + { HRDATA (R0, R[0], 32) }, + { HRDATA (R1, R[1], 32) }, + { HRDATA (R2, R[2], 32) }, + { HRDATA (R3, R[3], 32) }, + { HRDATA (R4, R[4], 32) }, + { HRDATA (R5, R[5], 32) }, + { HRDATA (R6, R[6], 32) }, + { HRDATA (R7, R[7], 32) }, + { HRDATA (R8, R[8], 32) }, + { HRDATA (R9, R[9], 32) }, + { HRDATA (R10, R[10], 32) }, + { HRDATA (R11, R[11], 32) }, + { HRDATA (R12, R[12], 32) }, + { HRDATA (R13, R[13], 32) }, + { HRDATA (R14, R[14], 32) }, + { HRDATA (AP, R[nAP], 32) }, + { HRDATA (FP, R[nFP], 32) }, + { HRDATA (SP, R[nSP], 32) }, + { HRDATA (PSL, PSL, 32) }, + { HRDATA (CC, PSL, 4) }, + { HRDATA (KSP, KSP, 32) }, + { HRDATA (ESP, ESP, 32) }, + { HRDATA (SSP, SSP, 32) }, + { HRDATA (USP, USP, 32) }, + { HRDATA (IS, IS, 32) }, + { HRDATA (SCBB, SCBB, 32) }, + { HRDATA (PCBB, PCBB, 32) }, + { HRDATA (P0BR, P0BR, 32) }, + { HRDATA (P0LR, P0LR, 22) }, + { HRDATA (P1BR, P1BR, 32) }, + { HRDATA (P1LR, P1LR, 22) }, + { HRDATA (SBR, SBR, 32) }, + { HRDATA (SLR, SLR, 22) }, + { HRDATA (SISR, SISR, 16) }, + { HRDATA (ASTLVL, ASTLVL, 4) }, + { FLDATA (MAPEN, mapen, 0) }, + { FLDATA (PME, pme, 0) }, + { HRDATA (TRPIRQ, trpirq, 8) }, + { FLDATA (CRDERR, crd_err, 0) }, + { FLDATA (MEMERR, mem_err, 0) }, + { FLDATA (HLTPIN, hlt_pin, 0) }, + { HRDATA (IDLE_IPL, cpu_idle_ipl_mask, 16), REG_HIDDEN }, + { DRDATA (IDLE_TYPE, cpu_idle_type, 4), REG_HRO }, + { DRDATA (IDLE_WAIT, cpu_idle_wait, 16), REG_HIDDEN }, + { BRDATA (PCQ, pcq, 16, 32, PCQ_SIZE), REG_RO+REG_CIRC }, + { HRDATA (PCQP, pcq_p, 6), REG_HRO }, + { HRDATA (BADABO, badabo, 32), REG_HRO }, + { HRDATA (WRU, sim_int_char, 8) }, + { NULL } + }; + +MTAB cpu_mod[] = { + { UNIT_CONH, 0, "HALT to SIMH", "SIMHALT", NULL }, + { UNIT_CONH, UNIT_CONH, "HALT to console", "CONHALT", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "IDLE", "IDLE", &cpu_set_idle, &cpu_show_idle }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL }, + { UNIT_MSIZE, (1u << 23), NULL, "8M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 24), NULL, "16M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 25), NULL, "32M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 25) + (1u << 24), NULL, "48M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 26), NULL, "64M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 27), NULL, "128M", &cpu_set_size }, +#if !defined (VAX_780) + { UNIT_MSIZE, (1u << 28), NULL, "256M", &cpu_set_size }, + { UNIT_MSIZE, (1u << 29), NULL, "512M", &cpu_set_size }, +#endif + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY", + &cpu_set_hist, &cpu_show_hist }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, + NULL, &cpu_show_virt }, + { 0 } + }; + +DEBTAB cpu_deb[] = { + { "INTEXC", LOG_CPU_I }, + { "REI", LOG_CPU_R }, + { "CONTEXT", LOG_CPU_P }, + { NULL, 0 } + }; + +DEVICE cpu_dev = { + "CPU", &cpu_unit, cpu_reg, cpu_mod, + 1, 16, 32, 1, 16, 8, + &cpu_ex, &cpu_dep, &cpu_reset, + &cpu_boot, NULL, NULL, + NULL, DEV_DYNM | DEV_DEBUG, 0, + cpu_deb, &cpu_set_size, NULL + }; + +t_stat sim_instr (void) +{ +volatile int32 opc, cc; /* used by setjmp */ +int32 acc; /* set by setjmp */ +int abortval; +t_stat r; + +if ((r = build_dib_tab ()) != SCPE_OK) return r; /* build, chk dib_tab */ +if ((PSL & PSL_MBZ) || /* validate PSL */ + ((PSL & PSL_CM) && BadCmPSL (PSL)) || /* validate PSL */ + ((PSL_GETCUR (PSL) != KERN) && /* esu => is, ipl = 0 */ + (PSL & (PSL_IS|PSL_IPL))) || + ((PSL & PSL_IS) && ((PSL & PSL_IPL) == 0))) /* is => ipl > 0 */ + return SCPE_STOP; +cc = PSL & CC_MASK; /* split PSL */ +PSL = PSL & ~CC_MASK; +in_ie = 0; /* not in exc */ +set_map_reg (); /* set map reg */ +GET_CUR; /* set access mask */ +SET_IRQL; /* eval interrupts */ +FLUSH_ISTR; /* clear prefetch */ + +abortval = setjmp (save_env); /* set abort hdlr */ +if (abortval > 0) { /* sim stop? */ + PSL = PSL | cc; /* put PSL together */ + pcq_r->qptr = pcq_p; /* update pc q ptr */ + return abortval; /* return to SCP */ + } +else if (abortval < 0) { /* mm or rsrv or int */ + int32 i, delta; + if ((PSL & PSL_FPD) == 0) { /* FPD? no recovery */ + for (i = 0; i < recqptr; i++) { /* unwind inst */ + int32 rrn, rlnt; + rrn = RQ_GETRN (recq[i]); /* recover reg # */ + rlnt = DR_LNT (RQ_GETLNT (recq[i])); /* recovery lnt */ + if (recq[i] & RQ_DIR) R[rrn] = R[rrn] - rlnt; + else R[rrn] = R[rrn] + rlnt; + } + } + PSL = PSL & ~PSL_TP; /* clear */ + recqptr = 0; /* clear queue */ + delta = PC - fault_PC; /* save delta PC */ + SETPC (fault_PC); /* restore PC */ + switch (-abortval) { /* case on abort code */ + + case SCB_RESIN: /* rsrv inst fault */ + case SCB_RESAD: /* rsrv addr fault */ + case SCB_RESOP: /* rsrv opnd fault */ + if (in_ie) ABORT (STOP_INIE); /* in exc? panic */ + cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ + GET_CUR; /* PSL changed */ + break; + + case SCB_CMODE: /* comp mode fault */ + case SCB_ARITH: /* arithmetic fault */ + if (in_ie) ABORT (STOP_INIE); /* in exc? panic */ + cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ + GET_CUR; + in_ie = 1; + Write (SP - 4, p1, L_LONG, WA); /* write arith param */ + SP = SP - 4; + in_ie = 0; + break; + + case SCB_ACV: /* ACV fault */ + case SCB_TNV: /* TNV fault */ + if (in_ie) { /* in exception? */ + if (PSL & PSL_IS) ABORT (STOP_INIE); /* on is? panic */ + cc = intexc (SCB_KSNV, cc, 0, IE_SVE); /* ksnv */ + GET_CUR; + } + else { + cc = intexc (-abortval, cc, 0, IE_EXC); /* take exception */ + GET_CUR; + in_ie = 1; + Write (SP - 8, p1, L_LONG, WA); /* write mm params */ + Write (SP - 4, p2, L_LONG, WA); + SP = SP - 8; + in_ie = 0; + } + break; + + case SCB_MCHK: /* machine check */ + if (in_ie) ABORT (STOP_INIE); /* in exception? */ + cc = machine_check (p1, opc, cc, delta); /* system specific */ + in_ie = 0; + GET_CUR; /* PSL changed */ + break; + + case 1: /* interrupt */ + break; /* just proceed */ + default: /* other */ + badabo = abortval; /* save code */ + ABORT (STOP_UNKABO); /* panic */ + } /* end case */ + } /* end else */ + +/* Main instruction loop */ + +for ( ;; ) { + + int32 spec, disp, rn, index, numspec; + int32 vfldrp1, brdisp, flg, mstat; + int32 i, j, r, rh, temp; + uint32 va, iad; + int32 opnd[OPND_SIZE]; /* operand queue */ + + if (cpu_astop) { + cpu_astop = 0; + ABORT (SCPE_STOP); + } + fault_PC = PC; + recqptr = 0; /* clr recovery q */ + if (sim_interval <= 0) { /* chk clock queue */ + temp = sim_process_event (); + if (temp) ABORT (temp); + SET_IRQL; /* update interrupts */ + } + +/* Test for non-instruction dispatches, in SRM order + + - trap or interrupt (trpirq != 0) + - PSL set + + If any of these conditions are met, re-dispatch; otherwise, + set PSL from PSL. +*/ + + if (trpirq) { /* trap or interrupt? */ + if (temp = GET_TRAP (trpirq)) { /* trap? */ + cc = intexc (SCB_ARITH, cc, 0, IE_EXC); /* take, clear trap */ + GET_CUR; /* set cur mode */ + in_ie = 1; + Write (SP - 4, temp, L_LONG, WA); /* write parameter */ + SP = SP - 4; + in_ie = 0; + } + else if (temp = GET_IRQL (trpirq)) { /* interrupt? */ + int32 vec; + if (temp == IPL_HLTPIN) { /* console halt? */ + hlt_pin = 0; /* clear intr */ + trpirq = 0; /* clear everything */ + cc = con_halt (CON_HLTPIN, cc); /* invoke firmware */ + continue; /* continue */ + } + else if (temp >= IPL_HMIN) /* hardware req? */ + vec = get_vector (temp); /* get vector */ + else if (temp > IPL_SMAX) ABORT (STOP_UIPL); + else { + vec = SCB_IPLSOFT + (temp << 2); + SISR = SISR & ~(1u << temp); + } + if (vec) cc = intexc (vec, cc, temp, IE_INT);/* take intr */ + GET_CUR; /* set cur mode */ + } + else trpirq = 0; /* clear everything */ + SET_IRQL; /* eval interrupts */ + continue; + } + + if (PSL & (PSL_CM|PSL_TP|PSW_T)) { /* PSL event? */ + if (PSL & PSL_TP) { /* trace trap? */ + PSL = PSL & ~PSL_TP; /* clear */ + cc = intexc (SCB_TP, cc, 0, IE_EXC); /* take trap */ + GET_CUR; /* set cur mode */ + continue; + } + if (PSL & PSW_T) PSL = PSL | PSL_TP; /* if T, set TP */ + if (PSL & PSL_CM) { /* compat mode? */ + cc = op_cmode (cc); /* exec instr */ + continue; /* skip fetch */ + } + } /* end PSL event */ + + if (sim_brk_summ && + sim_brk_test ((uint32) PC, SWMASK ('E'))) { /* breakpoint? */ + ABORT (STOP_IBKPT); /* stop simulation */ + } + + sim_interval = sim_interval - 1; /* count instr */ + GET_ISTR (opc, L_BYTE); /* get opcode */ + if (opc == 0xFD) { /* 2 byte op? */ + GET_ISTR (opc, L_BYTE); /* get second byte */ + opc = opc | 0x100; /* flag */ + } + numspec = drom[opc][0]; /* get # specs */ + if (PSL & PSL_FPD) { + if ((numspec & DR_F) == 0) RSVD_INST_FAULT; + } + else { + numspec = numspec & DR_NSPMASK; /* get # specifiers */ + +/* Specifier flows. Operands are parsed and placed into queue opnd. + + r.bwl opnd[j] = value of operand + r.q opnd[j:j+1] = value of operand + r.o opnd[j:j+3] = value of operand + a.bwlqo opnd[j] = address of operand + m.bwl opnd[j] = value of operand + m.q opnd[j:j+1] = value of operand + m.o opnd[j:j+3] = value of operand + w.bwlqo opnd[j] = register/memory flag + opnd[j+1] = memory address + + For the last memory specifier, the specifier is in spec, the register + number is in rn, and the effective address is in va. Modify specifiers + (always last) can test spec > reg+PC, as short literal are illegal for + modifiers specifiers, and final index specifiers are always illegal. +*/ + + for (i = 1, j = 0; i <= numspec; i++) { /* loop thru specs */ + disp = drom[opc][i]; /* get dispatch */ + if (disp >= BB) { + GET_ISTR (brdisp, DR_LNT (disp & 1)); + break; + } + GET_ISTR (spec, L_BYTE); /* get spec byte */ + rn = spec & RGMASK; /* get reg # */ + disp = (spec & ~RGMASK) | disp; /* merge w dispatch */ + switch (disp) { /* dispatch spec */ + +/* Short literal - only read access permitted */ + + case SH0|RB: case SH0|RW: case SH0|RL: + case SH1|RB: case SH1|RW: case SH1|RL: + case SH2|RB: case SH2|RW: case SH2|RL: + case SH3|RB: case SH3|RW: case SH3|RL: + opnd[j++] = spec; + break; + + case SH0|RQ: case SH1|RQ: case SH2|RQ: case SH3|RQ: + opnd[j++] = spec; + opnd[j++] = 0; + break; + + case SH0|RO: case SH1|RO: case SH2|RO: case SH3|RO: + opnd[j++] = spec; + opnd[j++] = 0; + opnd[j++] = 0; + opnd[j++] = 0; + break; + + case SH0|RF: case SH1|RF: case SH2|RF: case SH3|RF: + opnd[j++] = (spec << 4) | 0x4000; + break; + + case SH0|RD: case SH1|RD: case SH2|RD: case SH3|RD: + opnd[j++] = (spec << 4) | 0x4000; + opnd[j++] = 0; + break; + + case SH0|RG: case SH1|RG: case SH2|RG: case SH3|RG: + opnd[j++] = (spec << 1) | 0x4000; + opnd[j++] = 0; + break; + + case SH0|RH: case SH1|RH: case SH2|RH: case SH3|RH: + opnd[j++] = ((spec & 0x7) << 29) | (0x4000 | ((spec >> 3) & 0x7)); + opnd[j++] = 0; + opnd[j++] = 0; + opnd[j++] = 0; + break; + +/* Register */ + + case GRN|RB: case GRN|MB: + CHECK_FOR_PC; + opnd[j++] = R[rn] & BMASK; + break; + + case GRN|RW: case GRN|MW: + CHECK_FOR_PC; + opnd[j++] = R[rn] & WMASK; + break; + + case GRN|VB: + vfldrp1 = R[(rn + 1) & RGMASK]; + case GRN|WB: case GRN|WW: case GRN|WL: case GRN|WQ: case GRN|WO: + opnd[j++] = rn; + case GRN|RL: case GRN|RF: case GRN|ML: + CHECK_FOR_PC; + opnd[j++] = R[rn]; + break; + + case GRN|RQ: case GRN|RD: case GRN|RG: case GRN|MQ: + CHECK_FOR_SP; + opnd[j++] = R[rn]; + opnd[j++] = R[rn + 1]; + break; + + case GRN|RO: case GRN|RH: case GRN|MO: + CHECK_FOR_AP; + opnd[j++] = R[rn]; + opnd[j++] = R[rn + 1]; + opnd[j++] = R[rn + 2]; + opnd[j++] = R[rn + 3]; + break; + +/* Register deferred, autodecrement */ + + case RGD|VB: + case RGD|WB: case RGD|WW: case RGD|WL: case RGD|WQ: case RGD|WO: + opnd[j++] = OP_MEM; + case RGD|AB: case RGD|AW: case RGD|AL: case RGD|AQ: case RGD|AO: + CHECK_FOR_PC; + va = opnd[j++] = R[rn]; + break; + + case ADC|VB: + case ADC|WB: case ADC|WW: case ADC|WL: case ADC|WQ: case ADC|WO: + opnd[j++] = OP_MEM; + case ADC|AB: case ADC|AW: case ADC|AL: case ADC|AQ: case ADC|AO: + CHECK_FOR_PC; + va = opnd[j++] = R[rn] = R[rn] - DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + break; + + case ADC|RB: case ADC|RW: case ADC|RL: case ADC|RF: + R[rn] = R[rn] - (DR_LNT (disp)); + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|RB: case RGD|RW: case RGD|RL: case RGD|RF: + CHECK_FOR_PC; + opnd[j++] = Read (va = R[rn], DR_LNT (disp), RA); + break; + + case ADC|RQ: case ADC|RD: case ADC|RG: + R[rn] = R[rn] - 8; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|RQ: case RGD|RD: case RGD|RG: + CHECK_FOR_PC; + opnd[j++] = Read (va = R[rn], L_LONG, RA); + opnd[j++] = Read (R[rn] + 4, L_LONG, RA); + break; + + case ADC|RO: case ADC|RH: + R[rn] = R[rn] - 16; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|RO: case RGD|RH: + CHECK_FOR_PC; + j = ReadOcta (va = R[rn], opnd, j, RA); + break; + + case ADC|MB: case ADC|MW: case ADC|ML: + R[rn] = R[rn] - (DR_LNT (disp)); + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|MB: case RGD|MW: case RGD|ML: + CHECK_FOR_PC; + opnd[j++] = Read (va = R[rn], DR_LNT (disp), WA); + break; + + case ADC|MQ: + R[rn] = R[rn] - 8; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|MQ: + CHECK_FOR_PC; + opnd[j++] = Read (va = R[rn], L_LONG, WA); + opnd[j++] = Read (R[rn] + 4, L_LONG, WA); + break; + + case ADC|MO: + R[rn] = R[rn] - 16; + recq[recqptr++] = RQ_REC (disp, rn); + case RGD|MO: + CHECK_FOR_PC; + j = ReadOcta (va = R[rn], opnd, j, WA); + break; + +/* Autoincrement */ + + case AIN|VB: + case AIN|WB: case AIN|WW: case AIN|WL: case AIN|WQ: case AIN|WO: +/* CHECK_FOR_PC; */ + opnd[j++] = OP_MEM; + case AIN|AB: case AIN|AW: case AIN|AL: case AIN|AQ: case AIN|AO: + va = opnd[j++] = R[rn]; + if (rn == nPC) { + if (DR_LNT (disp) >= L_QUAD) { + GET_ISTR (temp, L_LONG); + GET_ISTR (temp, L_LONG); + if (DR_LNT (disp) == L_OCTA) { + GET_ISTR (temp, L_LONG); + GET_ISTR (temp, L_LONG); + } + } + else GET_ISTR (temp, DR_LNT (disp)); + } + else { + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|RB: case AIN|RW: case AIN|RL: case AIN|RF: + va = R[rn]; + if (rn == nPC) { + GET_ISTR (opnd[j++], DR_LNT (disp)); + } + else { + opnd[j++] = Read (R[rn], DR_LNT (disp), RA); + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|RQ: case AIN|RD: case AIN|RG: + va = R[rn]; + if (rn == nPC) { + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + } + else { + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + R[rn] = R[rn] + 8; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|RO: case AIN|RH: + va = R[rn]; + if (rn == nPC) { + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + } + else { + j = ReadOcta (va, opnd, j, RA); + R[rn] = R[rn] + 16; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|MB: case AIN|MW: case AIN|ML: + va = R[rn]; + if (rn == nPC) { + GET_ISTR (opnd[j++], DR_LNT (disp)); + } + else { + opnd[j++] = Read (R[rn], DR_LNT (disp), WA); + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|MQ: + va = R[rn]; + if (rn == nPC) { + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + } + else { + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + R[rn] = R[rn] + 8; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + + case AIN|MO: + va = R[rn]; + if (rn == nPC) { + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + GET_ISTR (opnd[j++], L_LONG); + } + else { + j = ReadOcta (va, opnd, j, WA); + R[rn] = R[rn] + 16; + recq[recqptr++] = RQ_REC (disp, rn); + } + break; + +/* Autoincrement deferred */ + + case AID|VB: + case AID|WB: case AID|WW: case AID|WL: case AID|WQ: case AID|WO: + opnd[j++] = OP_MEM; + case AID|AB: case AID|AW: case AID|AL: case AID|AQ: case AID|AO: + if (rn == nPC) { GET_ISTR (va = opnd[j++], L_LONG); } + else { + va = opnd[j++] = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + break; + + case AID|RB: case AID|RW: case AID|RL: case AID|RF: + if (rn == nPC) { + GET_ISTR (va, L_LONG); + } + else { + va = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (va, DR_LNT (disp), RA); + break; + + case AID|RQ: case AID|RD: case AID|RG: + if (rn == nPC) { + GET_ISTR (va, L_LONG); + } + else { + va = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + break; + + case AID|RO: case AID|RH: + if (rn == nPC) { + GET_ISTR (va, L_LONG); + } + else { + va = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + j = ReadOcta (va, opnd, j, RA); + break; + + case AID|MB: case AID|MW: case AID|ML: + if (rn == nPC) { + GET_ISTR (va, L_LONG); + } + else { + va = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (va, DR_LNT (disp), WA); + break; + + case AID|MQ: + if (rn == nPC) { + GET_ISTR (va, L_LONG); + } + else { + va = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + break; + + case AID|MO: + if (rn == nPC) { + GET_ISTR (va, L_LONG); + } + else { + va = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + j = ReadOcta (va, opnd, j, WA); + break; + +/* Byte displacement */ + + case BDP|VB: + case BDP|WB: case BDP|WW: case BDP|WL: case BDP|WQ: case BDP|WO: + opnd[j++] = OP_MEM; + case BDP|AB: case BDP|AW: case BDP|AL: case BDP|AQ: case BDP|AO: + GET_ISTR (temp, L_BYTE); + va = opnd[j++] = R[rn] + SXTB (temp); + break; + + case BDP|RB: case BDP|RW: case BDP|RL: case BDP|RF: + GET_ISTR (temp, L_BYTE); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (va, DR_LNT (disp), RA); + break; + + case BDP|RQ: case BDP|RD: case BDP|RG: + GET_ISTR (temp, L_BYTE); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + break; + + case BDP|RO: case BDP|RH: + GET_ISTR (temp, L_BYTE); + va = R[rn] + SXTB (temp); + j = ReadOcta (va, opnd, j, RA); + break; + + case BDP|MB: case BDP|MW: case BDP|ML: + GET_ISTR (temp, L_BYTE); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (va, DR_LNT (disp), WA); + break; + + case BDP|MQ: + GET_ISTR (temp, L_BYTE); + va = R[rn] + SXTB (temp); + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + break; + + case BDP|MO: + GET_ISTR (temp, L_BYTE); + va = R[rn] + SXTB (temp); + j = ReadOcta (va, opnd, j, WA); + break; + +/* Byte displacement deferred */ + + case BDD|VB: + case BDD|WB: case BDD|WW: case BDD|WL: case BDD|WQ: case BDD|WO: + opnd[j++] = OP_MEM; + case BDD|AB: case BDD|AW: case BDD|AL: case BDD|AQ: case BDD|AO: + GET_ISTR (temp, L_BYTE); + iad = R[rn] + SXTB (temp); + va = opnd[j++] = Read (iad, L_LONG, RA); + break; + + case BDD|RB: case BDD|RW: case BDD|RL: case BDD|RF: + GET_ISTR (temp, L_BYTE); + iad = R[rn] + SXTB (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, DR_LNT (disp), RA); + break; + + case BDD|RQ: case BDD|RD: case BDD|RG: + GET_ISTR (temp, L_BYTE); + iad = R[rn] + SXTB (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + break; + + case BDD|RO: case BDD|RH: + GET_ISTR (temp, L_BYTE); + iad = R[rn] + SXTB (temp); + va = Read (iad, L_LONG, RA); + j = ReadOcta (va, opnd, j, RA); + break; + + case BDD|MB: case BDD|MW: case BDD|ML: + GET_ISTR (temp, L_BYTE); + iad = R[rn] + SXTB (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, DR_LNT (disp), WA); + break; + + case BDD|MQ: + GET_ISTR (temp, L_BYTE); + iad = R[rn] + SXTB (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + break; + + case BDD|MO: + GET_ISTR (temp, L_BYTE); + iad = R[rn] + SXTB (temp); + va = Read (iad, L_LONG, RA); + j = ReadOcta (va, opnd, j, WA); + break; + +/* Word displacement */ + + case WDP|VB: + case WDP|WB: case WDP|WW: case WDP|WL: case WDP|WQ: case WDP|WO: + opnd[j++] = OP_MEM; + case WDP|AB: case WDP|AW: case WDP|AL: case WDP|AQ: case WDP|AO: + GET_ISTR (temp, L_WORD); + va = opnd[j++] = R[rn] + SXTW (temp); + break; + + case WDP|RB: case WDP|RW: case WDP|RL: case WDP|RF: + GET_ISTR (temp, L_WORD); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (va, DR_LNT (disp), RA); + break; + + case WDP|RQ: case WDP|RD: case WDP|RG: + GET_ISTR (temp, L_WORD); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + break; + + case WDP|RO: case WDP|RH: + GET_ISTR (temp, L_WORD); + va = R[rn] + SXTW (temp); + j = ReadOcta (va, opnd, j, RA); + break; + + case WDP|MB: case WDP|MW: case WDP|ML: + GET_ISTR (temp, L_WORD); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (va, DR_LNT (disp), WA); + break; + + case WDP|MQ: + GET_ISTR (temp, L_WORD); + va = R[rn] + SXTW (temp); + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + break; + + case WDP|MO: + GET_ISTR (temp, L_WORD); + va = R[rn] + SXTW (temp); + j = ReadOcta (va, opnd, j, WA); + break; + +/* Word displacement deferred */ + + case WDD|VB: + case WDD|WB: case WDD|WW: case WDD|WL: case WDD|WQ: case WDD|WO: + opnd[j++] = OP_MEM; + case WDD|AB: case WDD|AW: case WDD|AL: case WDD|AQ: case WDD|AO: + GET_ISTR (temp, L_WORD); + iad = R[rn] + SXTW (temp); + va = opnd[j++] = Read (iad, L_LONG, RA); + break; + + case WDD|RB: case WDD|RW: case WDD|RL: case WDD|RF: + GET_ISTR (temp, L_WORD); + iad = R[rn] + SXTW (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, DR_LNT (disp), RA); + break; + + case WDD|RQ: case WDD|RD: case WDD|RG: + GET_ISTR (temp, L_WORD); + iad = R[rn] + SXTW (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + break; + + case WDD|RO: case WDD|RH: + GET_ISTR (temp, L_WORD); + iad = R[rn] + SXTW (temp); + va = Read (iad, L_LONG, RA); + j = ReadOcta (va, opnd, j, RA); + break; + + case WDD|MB: case WDD|MW: case WDD|ML: + GET_ISTR (temp, L_WORD); + iad = R[rn] + SXTW (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, DR_LNT (disp), WA); + break; + + case WDD|MQ: + GET_ISTR (temp, L_WORD); + iad = R[rn] + SXTW (temp); + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + break; + + case WDD|MO: + GET_ISTR (temp, L_WORD); + iad = R[rn] + SXTW (temp); + va = Read (iad, L_LONG, RA); + j = ReadOcta (va, opnd, j, WA); + break; + +/* Longword displacement */ + + case LDP|VB: + case LDP|WB: case LDP|WW: case LDP|WL: case LDP|WQ: case LDP|WO: + opnd[j++] = OP_MEM; + case LDP|AB: case LDP|AW: case LDP|AL: case LDP|AQ: case LDP|AO: + GET_ISTR (temp, L_LONG); + va = opnd[j++] = R[rn] + temp; + break; + + case LDP|RB: case LDP|RW: case LDP|RL: case LDP|RF: + GET_ISTR (temp, L_LONG); + va = R[rn] + temp; + opnd[j++] = Read (va, DR_LNT (disp), RA); + break; + + case LDP|RQ: case LDP|RD: case LDP|RG: + GET_ISTR (temp, L_LONG); + va = R[rn] + temp; + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + break; + + case LDP|RO: case LDP|RH: + GET_ISTR (temp, L_LONG); + va = R[rn] + temp; + j = ReadOcta (va, opnd, j, RA); + break; + + case LDP|MB: case LDP|MW: case LDP|ML: + GET_ISTR (temp, L_LONG); + va = R[rn] + temp; + opnd[j++] = Read (va, DR_LNT (disp), WA); + break; + + case LDP|MQ: + GET_ISTR (temp, L_LONG); + va = R[rn] + temp; + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + break; + + case LDP|MO: + GET_ISTR (temp, L_LONG); + va = R[rn] + temp; + j = ReadOcta (va, opnd, j, WA); + break; + +/* Longword displacement deferred */ + + case LDD|VB: + case LDD|WB: case LDD|WW: case LDD|WL: case LDD|WQ: case LDD|WO: + opnd[j++] = OP_MEM; + case LDD|AB: case LDD|AW: case LDD|AL: case LDD|AQ: case LDD|AO: + GET_ISTR (temp, L_LONG); + iad = R[rn] + temp; + va = opnd[j++] = Read (iad, L_LONG, RA); + break; + + case LDD|RB: case LDD|RW: case LDD|RL: case LDD|RF: + GET_ISTR (temp, L_LONG); + iad = R[rn] + temp; + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, DR_LNT (disp), RA); + break; + + case LDD|RQ: case LDD|RD: case LDD|RG: + GET_ISTR (temp, L_LONG); + iad = R[rn] + temp; + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, L_LONG, RA); + opnd[j++] = Read (va + 4, L_LONG, RA); + break; + + case LDD|RO: case LDD|RH: + GET_ISTR (temp, L_LONG); + iad = R[rn] + temp; + va = Read (iad, L_LONG, RA); + j = ReadOcta (va, opnd, j, RA); + break; + + case LDD|MB: case LDD|MW: case LDD|ML: + GET_ISTR (temp, L_LONG); + iad = R[rn] + temp; + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, DR_LNT (disp), WA); + break; + + case LDD|MQ: + GET_ISTR (temp, L_LONG); + iad = R[rn] + temp; + va = Read (iad, L_LONG, RA); + opnd[j++] = Read (va, L_LONG, WA); + opnd[j++] = Read (va + 4, L_LONG, WA); + break; + + case LDD|MO: + GET_ISTR (temp, L_LONG); + iad = R[rn] + temp; + va = Read (iad, L_LONG, RA); + j = ReadOcta (va, opnd, j, WA); + break; + +/* Index */ + + case IDX|VB: + case IDX|WB: case IDX|WW: case IDX|WL: case IDX|WQ: case IDX|WO: + case IDX|AB: case IDX|AW: case IDX|AL: case IDX|AQ: case IDX|AO: + case IDX|MB: case IDX|MW: case IDX|ML: case IDX|MQ: case IDX|MO: + case IDX|RB: case IDX|RW: case IDX|RL: case IDX|RQ: case IDX|RO: + case IDX|RF: case IDX|RD: case IDX|RG: case IDX|RH: + CHECK_FOR_PC; + index = R[rn] << (disp & DR_LNMASK); + GET_ISTR (spec, L_BYTE); + rn = spec & RGMASK; + switch (spec & ~RGMASK) { + case ADC: + R[rn] = R[rn] - DR_LNT (disp); + recq[recqptr++] = RQ_REC (ADC | (disp & DR_LNMASK), rn); + case RGD: + CHECK_FOR_PC; + index = index + R[rn]; + break; + + case AIN: + CHECK_FOR_PC; + index = index + R[rn]; + R[rn] = R[rn] + DR_LNT (disp); + recq[recqptr++] = RQ_REC (AIN | (disp & DR_LNMASK), rn); + break; + + case AID: + if (rn == nPC) { + GET_ISTR (temp, L_LONG); + } + else { + temp = Read (R[rn], L_LONG, RA); + R[rn] = R[rn] + 4; + recq[recqptr++] = RQ_REC (AID|RL, rn); + } + index = temp + index; + break; + + case BDP: + GET_ISTR (temp, L_BYTE); + index = index + R[rn] + SXTB (temp); + break; + + case BDD: + GET_ISTR (temp, L_BYTE); + index = index + Read (R[rn] + SXTB (temp), L_LONG, RA); + break; + + case WDP: + GET_ISTR (temp, L_WORD); + index = index + R[rn] + SXTW (temp); + break; + + case WDD: + GET_ISTR (temp, L_WORD); + index = index + Read (R[rn] + SXTW (temp), L_LONG, RA); + break; + + case LDP: + GET_ISTR (temp, L_LONG); + index = index + R[rn] + temp; + break; + + case LDD: + GET_ISTR (temp, L_LONG); + index = index + Read (R[rn] + temp, L_LONG, RA); + break; + + default: + RSVD_ADDR_FAULT; /* end case idxspec */ + } + + switch (disp & (DR_ACMASK|DR_SPFLAG|DR_LNMASK)) { /* case acc+lnt */ + case VB: + case WB: case WW: case WL: case WQ: case WO: + opnd[j++] = OP_MEM; + case AB: case AW: case AL: case AQ: case AO: + va = opnd[j++] = index; + break; + + case RB: case RW: case RL: case RF: + opnd[j++] = Read (va = index, DR_LNT (disp), RA); + break; + + case RQ: case RD: case RG: + opnd[j++] = Read (va = index, L_LONG, RA); + opnd[j++] = Read (index + 4, L_LONG, RA); + break; + + case RO: case RH: + j = ReadOcta (va = index, opnd, j, RA); + break; + + case MB: case MW: case ML: + opnd[j++] = Read (va = index, DR_LNT (disp), WA); + break; + + case MQ: + opnd[j++] = Read (va = index, L_LONG, WA); + opnd[j++] = Read (index + 4, L_LONG, WA); + break; + + case MO: + j = ReadOcta (va = index, opnd, j, WA); + break; + + default: /* all others */ + RSVD_ADDR_FAULT; /* fault */ + break; + } /* end case access/lnt */ + break; /* end index */ + + default: /* all others */ + RSVD_ADDR_FAULT; /* fault */ + break; + } /* end case spec */ + } /* end for */ + } /* end if not FPD */ + +/* Optionally record instruction history */ + + if (hst_lnt) { + int32 lim; + t_value wd; + + hst[hst_p].iPC = fault_PC; + hst[hst_p].PSL = PSL | cc; + hst[hst_p].opc = opc; + for (i = 0; i < j; i++) + hst[hst_p].opnd[i] = opnd[i]; + lim = PC - fault_PC; + if ((uint32) lim > INST_SIZE) lim = INST_SIZE; + for (i = 0; i < lim; i++) { + if ((cpu_ex (&wd, fault_PC + i, &cpu_unit, SWMASK ('V'))) == SCPE_OK) + hst[hst_p].inst[i] = (uint8) wd; + else { + hst[hst_p].inst[0] = hst[hst_p].inst[1] = 0xFF; + break; + } + } + hst_p = hst_p + 1; + if (hst_p >= hst_lnt) hst_p = 0; + } + +/* Dispatch to instructions */ + + switch (opc) { + +/* Single operand instructions with dest, write only - CLRx dst.wx + + spec = reg/memory flag + rn = register number + va = virtual address +*/ + + case CLRB: + WRITE_B (0); /* store result */ + CC_ZZ1P; /* set cc's */ + break; + + case CLRW: + WRITE_W (0); /* store result */ + CC_ZZ1P; /* set cc's */ + break; + + case CLRL: + WRITE_L (0); /* store result */ + CC_ZZ1P; /* set cc's */ + break; + + case CLRQ: + WRITE_Q (0, 0); /* store result */ + CC_ZZ1P; /* set cc's */ + break; + +/* Single operand instructions with source, read only - TSTx src.rx + + opnd[0] = source +*/ + + case TSTB: + CC_IIZZ_B (op0); /* set cc's */ + break; + + case TSTW: + CC_IIZZ_W (op0); /* set cc's */ + break; + + case TSTL: + CC_IIZZ_L (op0); /* set cc's */ + break; + +/* Single operand instructions with source, read/write - op src.mx + + opnd[0] = operand + spec = reg/mem flag + rn = register number + va = operand address +*/ + + case INCB: + r = (op0 + 1) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_ADD_B (r, 1, op0); /* set cc's */ + break; + + case INCW: + r = (op0 + 1) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_ADD_W (r, 1, op0); /* set cc's */ + break; + + case INCL: + r = (op0 + 1) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_ADD_L (r, 1, op0); /* set cc's */ + break; + + case DECB: + r = (op0 - 1) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_SUB_B (r, 1, op0); /* set cc's */ + break; + + case DECW: + r = (op0 - 1) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_SUB_W (r, 1, op0); /* set cc's */ + break; + + case DECL: + r = (op0 - 1) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, 1, op0); /* set cc's */ + break; + +/* Push instructions - PUSHL src.rl or PUSHAx src.ax + + opnd[0] = source +*/ + + case PUSHL: case PUSHAB: case PUSHAW: case PUSHAL: case PUSHAQ: + Write (SP - 4, op0, L_LONG, WA); /* push operand */ + SP = SP - 4; /* decr stack ptr */ + CC_IIZP_L (op0); /* set cc's */ + break; + +/* Moves, converts, and ADAWI - op src.rx, dst.wx + + opnd[0] = source + spec = reg/mem flag + rn = register number + va = operand address +*/ + + case MOVB: + WRITE_B (op0); /* result */ + CC_IIZP_B (op0); /* set cc's */ + break; + + case MOVW: case MOVZBW: + WRITE_W (op0); /* result */ + CC_IIZP_W (op0); /* set cc's */ + break; + + case MOVL: case MOVZBL: case MOVZWL: + case MOVAB: case MOVAW: case MOVAL: case MOVAQ: + WRITE_L (op0); /* result */ + CC_IIZP_L (op0); /* set cc's */ + break; + + case MCOMB: + r = op0 ^ BMASK; /* compl opnd */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case MCOMW: + r = op0 ^ WMASK; /* compl opnd */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case MCOML: + r = op0 ^ LMASK; /* compl opnd */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + case MNEGB: + r = (-op0) & BMASK; /* negate opnd */ + WRITE_B (r); /* store result */ + CC_SUB_B (r, op0, 0); /* set cc's */ + break; + + case MNEGW: + r = (-op0) & WMASK; /* negate opnd */ + WRITE_W (r); /* store result */ + CC_SUB_W (r, op0, 0); /* set cc's */ + break; + + case MNEGL: + r = (-op0) & LMASK; /* negate opnd */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, op0, 0); /* set cc's */ + break; + + case CVTBW: + r = SXTBW (op0); /* ext sign */ + WRITE_W (r); /* store result */ + CC_IIZZ_W (r); /* set cc's */ + break; + + case CVTBL: + r = SXTB (op0); /* ext sign */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + break; + + case CVTWL: + r = SXTW (op0); /* ext sign */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + break; + + case CVTLB: + r = op0 & BMASK; /* set result */ + WRITE_B (r); /* store result */ + CC_IIZZ_B (r); /* initial cc's */ + if ((op0 > 127) || (op0 < -128)) { V_INTOV; } + break; + + case CVTLW: + r = op0 & WMASK; /* set result */ + WRITE_W (r); /* store result */ + CC_IIZZ_W (r); /* initial cc's */ + if ((op0 > 32767) || (op0 < -32768)) { V_INTOV; } + break; + + case CVTWB: + r = op0 & BMASK; /* set result */ + WRITE_B (r); /* store result */ + CC_IIZZ_B (r); /* initial cc's */ + temp = SXTW (op0); /* cvt op to long */ + if ((temp > 127) || (temp < -128)) { V_INTOV; } + break; + + case ADAWI: + if (op1 >= 0) temp = R[op1] & WMASK; /* reg? ADDW2 */ + else { + if (op2 & 1) RSVD_OPND_FAULT; /* mem? chk align */ + temp = Read (op2, L_WORD, WA); /* ok, ADDW2 */ + } + r = (op0 + temp) & WMASK; + WRITE_W (r); + CC_ADD_W (r, op0, temp); /* set cc's */ + break; + +/* Integer operates, 2 operand, read only - op src1.rx, src2.rx + + opnd[0] = source1 + opnd[1] = source2 +*/ + + case CMPB: + CC_CMP_B (op0, op1); /* set cc's */ + break; + + case CMPW: + CC_CMP_W (op0, op1); /* set cc's */ + break; + + case CMPL: + CC_CMP_L (op0, op1); /* set cc's */ + break; + + case BITB: + r = op1 & op0; /* calc result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case BITW: + r = op1 & op0; /* calc result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case BITL: + r = op1 & op0; /* calc result */ + CC_IIZP_L (r); /* set cc's */ + break; + +/* Integer operates, 2 operand read/write, and 3 operand, also MOVQ + op2 src.rx, dst.mx op3 src.rx, src.rx, dst.wx + + opnd[0] = source1 + opnd[1] = source2 + spec = register/memory flag + rn = register number + va = memory address +*/ + + case ADDB2: case ADDB3: + r = (op1 + op0) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_ADD_B (r, op0, op1); /* set cc's */ + break; + + case ADDW2: case ADDW3: + r = (op1 + op0) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_ADD_W (r, op0, op1); /* set cc's */ + break; + + case ADWC: + r = (op1 + op0 + (cc & CC_C)) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_ADD_L (r, op0, op1); /* set cc's */ + if ((r == op1) && op0) cc = cc | CC_C; /* special case */ + break; + + case ADDL2: case ADDL3: + r = (op1 + op0) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_ADD_L (r, op0, op1); /* set cc's */ + break; + + case SUBB2: case SUBB3: + r = (op1 - op0) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_SUB_B (r, op0, op1); /* set cc's */ + break; + + case SUBW2: case SUBW3: + r = (op1 - op0) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_SUB_W (r, op0, op1); /* set cc's */ + break; + + case SBWC: + r = (op1 - op0 - (cc & CC_C)) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, op0, op1); /* set cc's */ + if ((op0 == op1) && r) cc = cc | CC_C; /* special case */ + break; + + case SUBL2: case SUBL3: + r = (op1 - op0) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_SUB_L (r, op0, op1); /* set cc's */ + break; + + case MULB2: case MULB3: + temp = SXTB (op0) * SXTB (op1); /* multiply */ + r = temp & BMASK; /* mask to result */ + WRITE_B (r); /* store result */ + CC_IIZZ_B (r); /* set cc's */ + if ((temp > 127) || (temp < -128)) { V_INTOV; } + break; + + case MULW2: case MULW3: + temp = SXTW (op0) * SXTW (op1); /* multiply */ + r = temp & WMASK; /* mask to result */ + WRITE_W (r); /* store result */ + CC_IIZZ_W (r); /* set cc's */ + if ((temp > 32767) || (temp < -32768)) { V_INTOV; } + break; + + case MULL2: case MULL3: + r = op_emul (op0, op1, &rh); /* get 64b result */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + if (rh != ((r & LSIGN)? -1: 0)) { V_INTOV; } /* chk overflow */ + break; + + case DIVB2: case DIVB3: + if (op0 == 0) { /* div by zero? */ + r = op1; + temp = CC_V; + SET_TRAP (TRAP_DIVZRO); + } + else if ((op0 == BMASK) && (op1 == BSIGN)) { /* overflow? */ + r = op1; + temp = CC_V; + INTOV; + } + else { + r = SXTB (op1) / SXTB (op0); /* ok, divide */ + temp = 0; + } + r = r & BMASK; /* mask to result */ + WRITE_B (r); /* write result */ + CC_IIZZ_B (r); /* set cc's */ + cc = cc | temp; /* error? set V */ + break; + + case DIVW2: case DIVW3: + if (op0 == 0) { /* div by zero? */ + r = op1; + temp = CC_V; + SET_TRAP (TRAP_DIVZRO); + } + else if ((op0 == WMASK) && (op1 == WSIGN)) { /* overflow? */ + r = op1; + temp = CC_V; + INTOV; + } + else { + r = SXTW (op1) / SXTW (op0); /* ok, divide */ + temp = 0; + } + r = r & WMASK; /* mask to result */ + WRITE_W (r); /* write result */ + CC_IIZZ_W (r); /* set cc's */ + cc = cc | temp; /* error? set V */ + break; + + case DIVL2: case DIVL3: + if (op0 == 0) { /* div by zero? */ + r = op1; + temp = CC_V; + SET_TRAP (TRAP_DIVZRO); + } + else if ((op0 == LMASK) && (op1 == LSIGN)) { /* overflow? */ + r = op1; + temp = CC_V; + INTOV; + } + else { + r = op1 / op0; /* ok, divide */ + temp = 0; + } + r = r & LMASK; /* mask to result */ + WRITE_L (r); /* write result */ + CC_IIZZ_L (r); /* set cc's */ + cc = cc | temp; /* error? set V */ + break; + + case BISB2: case BISB3: + r = op1 | op0; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case BISW2: case BISW3: + r = op1 | op0; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case BISL2: case BISL3: + r = op1 | op0; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + case BICB2: case BICB3: + r = op1 & ~op0; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case BICW2: case BICW3: + r = op1 & ~op0; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case BICL2: case BICL3: + r = op1 & ~op0; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + case XORB2: case XORB3: + r = op1 ^ op0; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + break; + + case XORW2: case XORW3: + r = op1 ^ op0; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + break; + + case XORL2: case XORL3: + r = op1 ^ op0; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + +/* MOVQ - movq src.rq, dst.wq + + opnd[0:1] = source + spec = register/memory flag + rn = register number + va = memory address + +*/ + + case MOVQ: + WRITE_Q (op0, op1); /* store result */ + CC_IIZP_Q (op0, op1); + break; + +/* Shifts - op shf.rb,src.rl,dst.wl + + opnd[0] = shift count + opnd[1] = source + spec = register/memory flag + rn = register number + va = memory address +*/ + + case ROTL: + j = op0 % 32; /* reduce sc, mod 32 */ + if (j) r = ((((uint32) op1) << j) | + (((uint32) op1) >> (32 - j))) & LMASK; + else r = op1; + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + break; + + case ASHL: + if (op0 & BSIGN) { /* right shift? */ + temp = 0x100 - op0; /* get |shift| */ + if (temp > 31) r = (op1 & LSIGN)? LMASK: 0; /* sc > 31? */ + else r = op1 >> temp; /* shift */ + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + break; + } + else { + if (op0 > 31) r = temp = 0; /* sc > 31? */ + else { + r = (((uint32) op1) << op0) & LMASK; /* shift */ + temp = r >> op0; /* shift back */ + } + WRITE_L (r); /* store result */ + CC_IIZZ_L (r); /* set cc's */ + if (op1 != temp) { V_INTOV; } /* bits lost? */ + } + break; + + case ASHQ: + r = op_ashq (opnd, &rh, &flg); /* do qw shift */ + WRITE_Q (r, rh); /* store results */ + CC_IIZZ_Q (r, rh); /* set cc's */ + if (flg) { V_INTOV; } /* if ovflo, set */ + break; + +/* EMUL - emul mplr.rl,mpcn.rl,add.rl,dst.wq + + op0 = multiplier + op1 = multiplicand + op2 = adder + op3:op4 = destination (.wq) +*/ + + case EMUL: + r = op_emul (op0, op1, &rh); /* calc 64b result */ + r = r + op2; /* add 32b value */ + rh = rh + (((uint32) r) < ((uint32) op2)) - /* into 64b result */ + ((op2 & LSIGN)? 1: 0); + WRITE_Q (r, rh); /* write result */ + CC_IIZZ_Q (r, rh); /* set cc's */ + break; + +/* EDIV - ediv dvr.rl,dvd.rq,quo.wl,rem.wl + + op0 = divisor (.rl) + op1:op2 = dividend (.rq) + op3:op4 = quotient address (.wl) + op5:op6 = remainder address (.wl) +*/ + + case EDIV: + if (op5 < 0) Read (op6, L_LONG, WA); /* wtest remainder */ + if (op0 == 0) { /* divide by zero? */ + flg = CC_V; /* set V */ + r = opnd[1]; /* quo = low divd */ + rh = 0; /* rem = 0 */ + SET_TRAP (TRAP_DIVZRO); /* set trap */ + } + else { + r = op_ediv (opnd, &rh, &flg); /* extended divide */ + if (flg) { INTOV; } /* if ovf+IV, set trap */ + } + if (op3 >= 0) R[op3] = r; /* store quotient */ + else Write (op4, r, L_LONG, WA); + if (op5 >= 0) R[op5] = rh; /* store remainder */ + else Write (op6, rh, L_LONG, WA); + CC_IIZZ_L (r); /* set cc's */ + cc = cc | flg; /* set V if required */ + break; + +/* Control instructions */ + +/* Simple branches and subroutine calls */ + + case BRB: + BRANCHB (brdisp); /* branch */ + if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F)) + ABORT (STOP_LOOP); + break; + + case BRW: + BRANCHW (brdisp); /* branch */ + if ((PC == fault_PC) && (PSL_GETIPL (PSL) == 0x1F)) + ABORT (STOP_LOOP); + break; + + case BSBB: + Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ + SP = SP - 4; /* decr stk ptr */ + BRANCHB (brdisp); /* branch */ + break; + + case BSBW: + Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ + SP = SP - 4; /* decr stk ptr */ + BRANCHW (brdisp); /* branch */ + break; + + case BGEQ: + if (!(cc & CC_N)) BRANCHB (brdisp); /* br if N = 0 */ + break; + + case BLSS: + if (cc & CC_N) BRANCHB (brdisp); /* br if N = 1 */ + break; + + case BNEQ: + if (!(cc & CC_Z)) BRANCHB (brdisp); /* br if Z = 0 */ + break; + + case BEQL: + if (cc & CC_Z) BRANCHB (brdisp); /* br if Z = 1 */ + break; + + case BVC: + if (!(cc & CC_V)) BRANCHB (brdisp); /* br if V = 0 */ + break; + + case BVS: + if (cc & CC_V) BRANCHB (brdisp); /* br if V = 1 */ + break; + + case BGEQU: + if (!(cc & CC_C)) BRANCHB (brdisp); /* br if C = 0 */ + break; + + case BLSSU: + if (cc & CC_C) BRANCHB (brdisp); /* br if C = 1 */ + break; + + case BGTR: + if (!(cc & (CC_N | CC_Z))) BRANCHB (brdisp); /* br if N | Z = 0 */ + break; + + case BLEQ: + if (cc & (CC_N | CC_Z)) BRANCHB (brdisp); /* br if N | Z = 1 */ + break; + + case BGTRU: + if (!(cc & (CC_C | CC_Z))) BRANCHB (brdisp); /* br if C | Z = 0 */ + break; + + case BLEQU: + if (cc & (CC_C | CC_Z)) BRANCHB (brdisp); /* br if C | Z = 1 */ + break; + +/* Simple jumps and subroutine calls - op addr.ab + + opnd[0] = address +*/ + + case JSB: + Write (SP - 4, PC, L_LONG, WA); /* push PC on stk */ + SP = SP - 4; /* decr stk ptr */ + + case JMP: + JUMP (op0); /* jump */ + break; + + case RSB: + temp = Read (SP, L_LONG, RA); /* get top of stk */ + SP = SP + 4; /* incr stk ptr */ + JUMP (temp); + break; + +/* SOB instructions - op idx.ml,disp.bb + + opnd[0] = index + spec = register/memory flag + rn = register number + va = memory address +*/ + + case SOBGEQ: + r = op0 - 1; /* decr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_SUB_L (r, 1, op0); /* test for ovflo */ + if (r >= 0) BRANCHB (brdisp); /* if >= 0, branch */ + break; + + case SOBGTR: + r = op0 - 1; /* decr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_SUB_L (r, 1, op0); /* test for ovflo */ + if (r > 0) BRANCHB (brdisp); /* if >= 0, branch */ + break; + +/* AOB instructions - op limit.rl,idx.ml,disp.bb + + opnd[0] = limit + opnd[1] = index + spec = register/memory flag + rn = register number + va = memory address +*/ + + case AOBLSS: + r = op1 + 1; /* incr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_ADD_L (r, 1, op1); /* test for ovflo */ + if (r < op0) BRANCHB (brdisp); /* if < lim, branch */ + break; + + case AOBLEQ: + r = op1 + 1; /* incr index */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_ADD_L (r, 1, op1); /* test for ovflo */ + if (r <= op0) BRANCHB (brdisp); /* if < lim, branch */ + break; + +/* ACB instructions - op limit.rx,add.rx,index.mx,disp.bw + + opnd[0] = limit + opnd[1] = adder + opnd[2] = index + spec = register/memory flag + rn = register number + va = memory address +*/ + + case ACBB: + r = (op2 + op1) & BMASK; /* calc result */ + WRITE_B (r); /* store result */ + CC_IIZP_B (r); /* set cc's */ + V_ADD_B (r, op1, op2); /* test for ovflo */ + if ((op1 & BSIGN)? (SXTB (r) >= SXTB (op0)): + (SXTB (r) <= SXTB (op0))) BRANCHW (brdisp); + break; + + case ACBW: + r = (op2 + op1) & WMASK; /* calc result */ + WRITE_W (r); /* store result */ + CC_IIZP_W (r); /* set cc's */ + V_ADD_W (r, op1, op2); /* test for ovflo */ + if ((op1 & WSIGN)? (SXTW (r) >= SXTW (op0)): + (SXTW (r) <= SXTW (op0))) BRANCHW (brdisp); + break; + + case ACBL: + r = (op2 + op1) & LMASK; /* calc result */ + WRITE_L (r); /* store result */ + CC_IIZP_L (r); /* set cc's */ + V_ADD_L (r, op1, op2); /* test for ovflo */ + if ((op1 & LSIGN)? (r >= op0): (r <= op0)) + BRANCHW (brdisp); + break; + +/* CASE instructions - casex sel.rx,base.rx,lim.rx + + opnd[0] = selector + opnd[1] = base + opnd[2] = limit +*/ + + case CASEB: + r = (op0 - op1) & BMASK; /* sel - base */ + CC_CMP_B (r, op2); /* r:limit, set cc's */ + if (r > op2) JUMP (PC + ((op2 + 1) * 2)); /* r > limit (unsgnd)? */ + else { + temp = Read (PC + (r * 2), L_WORD, RA); + BRANCHW (temp); + } + break; + + case CASEW: + r = (op0 - op1) & WMASK; /* sel - base */ + CC_CMP_W (r, op2); /* r:limit, set cc's */ + if (r > op2) JUMP (PC + ((op2 + 1) * 2)); /* r > limit (unsgnd)? */ + else { + temp = Read (PC + (r * 2), L_WORD, RA); + BRANCHW (temp); + } + break; + + case CASEL: + r = (op0 - op1) & LMASK; /* sel - base */ + CC_CMP_L (r, op2); /* r:limit, set cc's */ + if (((uint32) r) > ((uint32) op2)) /* r > limit (unsgnd)? */ + JUMP (PC + ((op2 + 1) * 2)); + else { + temp = Read (PC + (r * 2), L_WORD, RA); + BRANCHW (temp); + } + break; + +/* Branch on bit instructions - bbxy pos.rl,op.wb,disp.bb + + opnd[0] = position + opnd[1] = register number/memory flag + opnd[2] = memory address, if memory +*/ + + case BBS: + if (op_bb_n (opnd, acc)) BRANCHB (brdisp); /* br if bit set */ + break; + + case BBC: + if (!op_bb_n (opnd, acc)) BRANCHB (brdisp); /* br if bit clr */ + break; + + case BBSS: case BBSSI: + if (op_bb_x (opnd, 1, acc)) BRANCHB (brdisp); /* br if set, set */ + break; + + case BBCC: case BBCCI: + if (!op_bb_x (opnd, 0, acc)) BRANCHB (brdisp); /* br if clr, clr*/ + break; + + case BBSC: + if (op_bb_x (opnd, 0, acc)) BRANCHB (brdisp); /* br if clr, set */ + break; + + case BBCS: + if (!op_bb_x (opnd, 1, acc)) BRANCHB (brdisp); /* br if set, clr */ + break; + + case BLBS: + if (op0 & 1) BRANCHB (brdisp); /* br if bit set */ + break; + + case BLBC: + if ((op0 & 1) == 0) BRANCHB (brdisp); /* br if bit clear */ + break; + +/* Extract field instructions - ext?v pos.rl,size.rb,base.wb,dst.wl + + opnd[0] = position + opnd[1] = size + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + spec = register/memory flag + rn = register number + va = memory address +*/ + + case EXTV: + r = op_extv (opnd, vfldrp1, acc); /* get field */ + if (r & byte_sign[op1]) r = r | ~byte_mask[op1]; + WRITE_L (r); /* store field */ + CC_IIZP_L (r); /* set cc's */ + break; + + case EXTZV: + r = op_extv (opnd, vfldrp1, acc); /* get field */ + WRITE_L (r); /* store field */ + CC_IIZP_L (r); /* set cc's */ + break; + +/* Compare field instructions - cmp?v pos.rl,size.rb,base.wb,src2.rl + + opnd[0] = position + opnd[1] = size + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + opnd[4] = source2 +*/ + + case CMPV: + r = op_extv (opnd, vfldrp1, acc); /* get field */ + if (r & byte_sign[op1]) r = r | ~byte_mask[op1]; + CC_CMP_L (r, op4); /* set cc's */ + break; + + case CMPZV: + r = op_extv (opnd, vfldrp1, acc); /* get field */ + CC_CMP_L (r, op4); /* set cc's */ + break; + +/* Find first field instructions - ff? pos.rl,size.rb,base.wb,dst.wl + + opnd[0] = position + opnd[1] = size + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + spec = register/memory flag + rn = register number + va = memory address +*/ + + case FFS: + r = op_extv (opnd, vfldrp1, acc); /* get field */ + temp = op_ffs (r, op1); /* find first 1 */ + WRITE_L (op0 + temp); /* store result */ + cc = r? 0: CC_Z; /* set cc's */ + break; + + case FFC: + r = op_extv (opnd, vfldrp1, acc); /* get field */ + r = r ^ byte_mask[op1]; /* invert bits */ + temp = op_ffs (r, op1); /* find first 1 */ + WRITE_L (op0 + temp); /* store result */ + cc = r? 0: CC_Z; /* set cc's */ + break; + +/* Insert field instruction - insv src.rl,pos.rb,size.rl,base.wb + + opnd[0] = source + opnd[1] = position + opnd[2] = size + opnd[3] = register number/memory flag + opnd[4] = register content/memory address +*/ + + case INSV: + op_insv (opnd, vfldrp1, acc); /* insert field */ + break; + +/* Call and return - call? arg.rx,proc.ab + + opnd[0] = argument + opnd[1] = procedure address +*/ + + case CALLS: + cc = op_call (opnd, TRUE, acc); + break; + + case CALLG: + cc = op_call (opnd, FALSE, acc); + break; + + case RET: + cc = op_ret (acc); + break; + +/* Miscellaneous instructions */ + + case HALT: + if (PSL & PSL_CUR) RSVD_INST_FAULT; /* not kern? rsvd inst */ + else if (cpu_unit.flags & UNIT_CONH) /* halt to console? */ + cc = con_halt (CON_HLTINS, cc); /* enter firmware */ + else { + ABORT (STOP_HALT); /* halt to simulator */ + } + + case NOP: + break; + + case BPT: + SETPC (fault_PC); + cc = intexc (SCB_BPT, cc, 0, IE_EXC); + GET_CUR; + break; + + case XFC: + SETPC (fault_PC); + cc = intexc (SCB_XFC, cc, 0, IE_EXC); + GET_CUR; + break; + + case BISPSW: + if (opnd[0] & PSW_MBZ) RSVD_OPND_FAULT; + PSL = PSL | (opnd[0] & ~CC_MASK); + cc = cc | (opnd[0] & CC_MASK); + break; + + case BICPSW: + if (opnd[0] & PSW_MBZ) RSVD_OPND_FAULT; + PSL = PSL & ~opnd[0]; + cc = cc & ~opnd[0]; + break; + + case MOVPSL: + r = PSL | cc; + WRITE_L (r); + break; + + case PUSHR: + op_pushr (opnd, acc); + break; + + case POPR: + op_popr (opnd, acc); + break; + + case INDEX: + if ((op0 < op1) || (op0 > op2)) SET_TRAP (TRAP_SUBSCR); + r = (op0 + op4) * op3; + WRITE_L (r); + CC_IIZZ_L (r); + break; + +/* Queue and interlocked queue */ + + case INSQUE: + cc = op_insque (opnd, acc); + break; + + case REMQUE: + cc = op_remque (opnd, acc); + break; + + case INSQHI: + cc = op_insqhi (opnd, acc); + break; + + case INSQTI: + cc = op_insqti (opnd, acc); + break; + + case REMQHI: + cc = op_remqhi (opnd, acc); + break; + + case REMQTI: + cc = op_remqti (opnd, acc); + break; + +/* String instructions */ + + case MOVC3: case MOVC5: + cc = op_movc (opnd, opc & 4, acc); + break; + + case CMPC3: case CMPC5: + cc = op_cmpc (opnd, opc & 4, acc); + break; + + case LOCC: case SKPC: + cc = op_locskp (opnd, opc & 1, acc); + break; + + case SCANC: case SPANC: + cc = op_scnspn (opnd, opc & 1, acc); + break; + +/* Floating point instructions */ + + case TSTF: case TSTD: + r = op_movfd (op0); + CC_IIZZ_FP (r); + break; + + case TSTG: + r = op_movg (op0); + CC_IIZZ_FP (r); + break; + + case MOVF: + r = op_movfd (op0); + WRITE_L (r); + CC_IIZP_FP (r); + break; + + case MOVD: + if ((r = op_movfd (op0)) == 0) op1 = 0; + WRITE_Q (r, op1); + CC_IIZP_FP (r); + break; + + case MOVG: + if ((r = op_movg (op0)) == 0) op1 = 0; + WRITE_Q (r, op1); + CC_IIZP_FP (r); + break; + + case MNEGF: + r = op_mnegfd (op0); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case MNEGD: + if ((r = op_mnegfd (op0)) == 0) op1 = 0; + WRITE_Q (r, op1); + CC_IIZZ_FP (r); + break; + + case MNEGG: + if ((r = op_mnegg (op0)) == 0) op1 = 0; + WRITE_Q (r, op1); + CC_IIZZ_FP (r); + break; + + case CMPF: + cc = op_cmpfd (op0, 0, op1, 0); + break; + + case CMPD: + cc = op_cmpfd (op0, op1, op2, op3); + break; + + case CMPG: + cc = op_cmpg (op0, op1, op2, op3); + break; + + case CVTBF: + r = op_cvtifdg (SXTB (op0), NULL, opc); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTWF: + r = op_cvtifdg (SXTW (op0), NULL, opc); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTLF: + r = op_cvtifdg (op0, NULL, opc); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTBD: case CVTBG: + r = op_cvtifdg (SXTB (op0), &rh, opc); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTWD: case CVTWG: + r = op_cvtifdg (SXTW (op0), &rh, opc); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTLD: case CVTLG: + r = op_cvtifdg (op0, &rh, opc); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTFB: case CVTDB: case CVTGB: + r = op_cvtfdgi (opnd, &flg, opc) & BMASK; + WRITE_B (r); + CC_IIZZ_B (r); + if (flg) { V_INTOV; } + break; + + case CVTFW: case CVTDW: case CVTGW: + r = op_cvtfdgi (opnd, &flg, opc) & WMASK; + WRITE_W (r); + CC_IIZZ_W (r); + if (flg) { V_INTOV; } + break; + + case CVTFL: case CVTDL: case CVTGL: + case CVTRFL: case CVTRDL: case CVTRGL: + r = op_cvtfdgi (opnd, &flg, opc) & LMASK; + WRITE_L (r); + CC_IIZZ_L (r); + if (flg) { V_INTOV; } + break; + + case CVTFD: + r = op_movfd (op0); + WRITE_Q (r, 0); + CC_IIZZ_FP (r); + break; + + case CVTDF: + r = op_cvtdf (opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case CVTFG: + r = op_cvtfg (opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case CVTGF: + r = op_cvtgf (opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case ADDF2: case ADDF3: + r = op_addf (opnd, FALSE); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case ADDD2: case ADDD3: + r = op_addd (opnd, &rh, FALSE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case ADDG2: case ADDG3: + r = op_addg (opnd, &rh, FALSE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case SUBF2: case SUBF3: + r = op_addf (opnd, TRUE); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case SUBD2: case SUBD3: + r = op_addd (opnd, &rh, TRUE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case SUBG2: case SUBG3: + r = op_addg (opnd, &rh, TRUE); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case MULF2: case MULF3: + r = op_mulf (opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case MULD2: case MULD3: + r = op_muld (opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case MULG2: case MULG3: + r = op_mulg (opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case DIVF2: case DIVF3: + r = op_divf (opnd); + WRITE_L (r); + CC_IIZZ_FP (r); + break; + + case DIVD2: case DIVD3: + r = op_divd (opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case DIVG2: case DIVG3: + r = op_divg (opnd, &rh); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + break; + + case ACBF: + r = op_addf (opnd + 1, FALSE); /* add + index */ + temp = op_cmpfd (r, 0, op0, 0); /* result : limit */ + WRITE_L (r); /* write result */ + CC_IIZP_FP (r); /* set cc's */ + if ((temp & CC_Z) || ((op1 & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) BRANCHW (brdisp); + break; + + case ACBD: + r = op_addd (opnd + 2, &rh, FALSE); + temp = op_cmpfd (r, rh, op0, op1); + WRITE_Q (r, rh); + CC_IIZP_FP (r); + if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) BRANCHW (brdisp); + break; + + case ACBG: + r = op_addg (opnd + 2, &rh, FALSE); + temp = op_cmpg (r, rh, op0, op1); + WRITE_Q (r, rh); + CC_IIZP_FP (r); + if ((temp & CC_Z) || ((op2 & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) BRANCHW (brdisp); + break; + +/* EMODF + + op0 = multiplier + op1 = extension + op2 = multiplicand + op3:op4 = integer destination (int.wl) + op5:op6 = floating destination (flt.wl) +*/ + + case EMODF: + r = op_emodf (opnd, &temp, &flg); + if (op5 < 0) Read (op6, L_LONG, WA); + if (op3 >= 0) R[op3] = temp; + else Write (op4, temp, L_LONG, WA); + WRITE_L (r); + CC_IIZZ_FP (r); + if (flg) { V_INTOV; } + break; + +/* EMODD, EMODG + + op0:op1 = multiplier + op2 = extension + op3:op4 = multiplicand + op5:op6 = integer destination (int.wl) + op7:op8 = floating destination (flt.wq) +*/ + + case EMODD: + r = op_emodd (opnd, &rh, &temp, &flg); + if (op7 < 0) { + Read (op8, L_BYTE, WA); + Read ((op8 + 7) & LMASK, L_BYTE, WA); + } + if (op5 >= 0) R[op5] = temp; + else Write (op6, temp, L_LONG, WA); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + if (flg) { V_INTOV; } + break; + + case EMODG: + r = op_emodg (opnd, &rh, &temp, &flg); + if (op7 < 0) { + Read (op8, L_BYTE, WA); + Read ((op8 + 7) & LMASK, L_BYTE, WA); + } + if (op5 >= 0) R[op5] = temp; + else Write (op6, temp, L_LONG, WA); + WRITE_Q (r, rh); + CC_IIZZ_FP (r); + if (flg) { V_INTOV; } + break; + +/* POLY */ + + case POLYF: + op_polyf (opnd, acc); + CC_IIZZ_FP (R[0]); + break; + + case POLYD: + op_polyd (opnd, acc); + CC_IIZZ_FP (R[0]); + break; + + case POLYG: + op_polyg (opnd, acc); + CC_IIZZ_FP (R[0]); + break; + +/* Operating system instructions */ + + case CHMK: case CHME: case CHMS: case CHMU: + cc = op_chm (opnd, cc, opc); /* CHMx */ + GET_CUR; /* update cur mode */ + SET_IRQL; /* update intreq */ + break; + + case REI: + cc = op_rei (acc); /* REI */ + GET_CUR; /* update cur mode */ + SET_IRQL; /* update intreq */ + break; + + case LDPCTX: + op_ldpctx (acc); + break; + + case SVPCTX: + op_svpctx (acc); + break; + + case PROBER: case PROBEW: + cc = (cc & CC_C) | op_probe (opnd, opc & 1); + break; + + case MTPR: + cc = (cc & CC_C) | op_mtpr (opnd); + SET_IRQL; /* update intreq */ + break; + + case MFPR: + r = op_mfpr (opnd); + WRITE_L (r); + CC_IIZP_L (r); + break; + +/* CIS or emulated instructions */ + + case CVTPL: + case MOVP: case CMPP3: case CMPP4: case CVTLP: + case CVTPS: case CVTSP: case CVTTP: case CVTPT: + case ADDP4: case ADDP6: case SUBP4: case SUBP6: + case MULP: case DIVP: case ASHP: case CRC: + case MOVTC: case MOVTUC: case MATCHC: case EDITPC: + cc = op_cis (opnd, cc, opc, acc); + break; + +/* Octaword or reserved instructions */ + + case PUSHAO: case MOVAO: case CLRO: case MOVO: + case TSTH: case MOVH: case MNEGH: case CMPH: + case CVTBH: case CVTWH: case CVTLH: + case CVTHB: case CVTHW: case CVTHL: case CVTRHL: + case CVTFH: case CVTDH: case CVTGH: + case CVTHF: case CVTHD: case CVTHG: + case ADDH2: case ADDH3: case SUBH2: case SUBH3: + case MULH2: case MULH3: case DIVH2: case DIVH3: + case ACBH: case POLYH: case EMODH: + cc = op_octa (opnd, cc, opc, acc, spec, va); + if (cc & LSIGN) { /* ACBH branch? */ + BRANCHW (brdisp); + cc = cc & CC_MASK; /* mask off flag */ + } + break; + + default: + RSVD_INST_FAULT; + break; + } /* end case op */ + } /* end for */ +ABORT (STOP_UNKNOWN); +} /* end sim_instr */ + +/* Prefetch buffer routine + + Prefetch buffer state + + ibufl, ibufh = the prefetch buffer + ibcnt = number of bytes available (0, 4, 8) + ppc = physical PC + + The get_istr routines fetches the indicated number of bytes from + the prefetch buffer. Although it is complicated, it is faster + than calling Read on every byte of the instruction stream. + + If the prefetch buffer has enough bytes, the required bytes are + extracted from the prefetch buffer and returned. If it does not + have enough bytes, enough prefetch words are fetched until there + are. A longword is only prefetched if data is needed from it, + so any translation errors are real. +*/ + +SIM_INLINE_GCC int32 get_istr (int32 lnt, int32 acc) +{ +int32 bo = PC & 3; +int32 sc, val, t; + +while ((bo + lnt) > ibcnt) { /* until enuf bytes */ + if ((ppc < 0) || (VA_GETOFF (ppc) == 0)) { /* PPC inv, xpg? */ + ppc = Test ((PC + ibcnt) & ~03, RD, &t); /* xlate PC */ + if (ppc < 0) Read ((PC + ibcnt) & ~03, L_LONG, RA); + } + if (ibcnt == 0) ibufl = ReadLP (ppc); /* fill low */ + else ibufh = ReadLP (ppc); /* or high */ + ppc = ppc + 4; /* incr phys PC */ + ibcnt = ibcnt + 4; /* incr ibuf cnt */ + } +PC = PC + lnt; /* incr PC */ +if (lnt == L_BYTE) val = (ibufl >> (bo << 3)) & BMASK; /* byte? */ +else if (lnt == L_WORD) { /* word? */ + if (bo == 3) val = ((ibufl >> 24) & 0xFF) | ((ibufh & 0xFF) << 8); + else val = (ibufl >> (bo << 3)) & WMASK; + } +else if (bo) { /* unaligned lw? */ + sc = bo << 3; + val = (((ibufl >> sc) & align[bo]) | (((uint32) ibufh) << (32 - sc))); + } +else val = ibufl; /* aligned lw */ +if ((bo + lnt) >= 4) { /* retire ibufl? */ + ibufl = ibufh; + ibcnt = ibcnt - 4; + } +return val; +} + +/* Read octaword specifier */ + +int32 ReadOcta (int32 va, int32 *opnd, int32 j, int32 acc) +{ +opnd[j++] = Read (va, L_LONG, acc); +opnd[j++] = Read (va + 4, L_LONG, acc); +opnd[j++] = Read (va + 8, L_LONG, acc); +opnd[j++] = Read (va + 12, L_LONG, acc); +return j; +} + +/* Check new PSL IPL for idle start + Checked only on exception or REI, not on MTPR #IPL, + to allow for local locking within the idle loop */ + +int32 cpu_psl_ipl_idle (int32 newpsl) +{ +if (((newpsl ^ PSL) & PSL_IPL) != 0) { + sim_cancel (&cpu_unit); + if (sim_idle_enab && ((newpsl & PSL_CUR) == 0)) { + uint32 newipl = PSL_GETIPL (newpsl); + if (cpu_idle_ipl_mask & (1u << newipl)) + sim_activate (&cpu_unit, cpu_idle_wait); + } + } +return newpsl; +} + +/* Idle timer has expired with no PSL change */ + +t_stat cpu_idle_svc (UNIT *uptr) +{ +if (sim_idle_enab) + sim_idle (TMR_CLK, FALSE); +return SCPE_OK; +} + +/* Reset */ + +t_stat cpu_reset (DEVICE *dptr) +{ +hlt_pin = 0; +mem_err = 0; +crd_err = 0; +PSL = PSL_IS | PSL_IPL1F; +SISR = 0; +ASTLVL = 4; +mapen = 0; +if (M == NULL) M = (uint32 *) calloc (((uint32) MEMSIZE) >> 2, sizeof (uint32)); +if (M == NULL) return SCPE_MEM; +pcq_r = find_reg ("PCQ", NULL, dptr); +if (pcq_r) pcq_r->qptr = 0; +else return SCPE_IERR; +sim_brk_types = sim_brk_dflt = SWMASK ('E'); +return build_dib_tab (); +} + +/* Memory examine */ + +t_stat cpu_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +int32 st; +uint32 addr = (uint32) exta; + +if (vptr == NULL) return SCPE_ARG; +if (sw & SWMASK ('V')) { + int32 acc = cpu_get_vsw (sw); + addr = Test (addr, acc, &st); + } +else addr = addr & PAMASK; +if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || + ADDR_IS_ROM (addr) || ADDR_IS_NVR (addr)) { + *vptr = (uint32) ReadB (addr); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Memory deposit */ + +t_stat cpu_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +int32 st; +uint32 addr = (uint32) exta; + +if (sw & SWMASK ('V')) { + int32 acc = cpu_get_vsw (sw); + addr = Test (addr, acc, &st); + } +else addr = addr & PAMASK; +if (ADDR_IS_MEM (addr) || ADDR_IS_CDG (addr) || + ADDR_IS_NVR (addr)) { + WriteB (addr, (int32) val); + return SCPE_OK; + } +if (ADDR_IS_ROM (addr)) { + rom_wr_B (addr, (int32) val); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Memory allocation */ + +t_stat cpu_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 mc = 0; +uint32 i, clim; +uint32 *nM = NULL; + +if ((val <= 0) || (val > MAXMEMSIZE_X)) return SCPE_ARG; +for (i = val; i < MEMSIZE; i = i + 4) mc = mc | M[i >> 2]; +if ((mc != 0) && !get_yn ("Really truncate memory [N]?", FALSE)) + return SCPE_OK; +nM = (uint32 *) calloc (val >> 2, sizeof (uint32)); +if (nM == NULL) return SCPE_MEM; +clim = (uint32) ((((uint32) val) < MEMSIZE)? val: MEMSIZE); +for (i = 0; i < clim; i = i + 4) nM[i >> 2] = M[i >> 2]; +free (M); +M = nM; +MEMSIZE = val; +return SCPE_OK; +} + +/* Virtual address translation */ + +t_stat cpu_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) +{ +t_stat r; +char *cptr = (char *) desc; +uint32 va, pa; +int32 st; +static const char *mm_str[] = { + "Access control violation", + "Length violation", + "Process PTE access control violation", + "Process PTE length violation", + "Translation not valid", + "Internal error", + "Process PTE translation not valid" + }; + +if (cptr) { + va = (uint32) get_uint (cptr, 16, 0xFFFFFFFF, &r); + if (r == SCPE_OK) { + int32 acc = cpu_get_vsw (sim_switches); + pa = Test (va, acc, &st); + if (st == PR_OK) fprintf (of, "Virtual %-X = physical %-X\n", va, pa); + else fprintf (of, "Virtual %-X: %s\n", va, mm_str[st]); + return SCPE_OK; + } + } +fprintf (of, "Invalid argument\n"); +return SCPE_OK; +} + +/* Get access mode for examine, deposit, show virtual */ + +int32 cpu_get_vsw (int32 sw) +{ +int32 md; + +set_map_reg (); /* update dyn reg */ +if (sw & SWMASK ('K')) md = KERN; +else if (sw & SWMASK ('E')) md = EXEC; +else if (sw & SWMASK ('S')) md = SUPV; +else if (sw & SWMASK ('U')) md = USER; +else md = PSL_GETCUR (PSL); +return ACC_MASK (md); +} + +/* Set history */ + +t_stat cpu_set_hist (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 i, lnt; +t_stat r; + +if (cptr == NULL) { + for (i = 0; i < hst_lnt; i++) hst[i].iPC = 0; + hst_p = 0; + return SCPE_OK; + } +lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r); +if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG; +hst_p = 0; +if (hst_lnt) { + free (hst); + hst_lnt = 0; + hst = NULL; + } +if (lnt) { + hst = (InstHistory *) calloc (lnt, sizeof (InstHistory)); + if (hst == NULL) return SCPE_MEM; + hst_lnt = lnt; + } +return SCPE_OK; +} + +/* Show history */ + +t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 i, k, di, lnt, numspec; +char *cptr = (char *) desc; +t_stat r; +InstHistory *h; +extern const char *opcode[]; +extern t_value *sim_eval; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); + +if (hst_lnt == 0) return SCPE_NOFNC; /* enabled? */ +if (cptr) { + lnt = (int32) get_uint (cptr, 10, hst_lnt, &r); + if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG; + } +else lnt = hst_lnt; +di = hst_p - lnt; /* work forward */ +if (di < 0) di = di + hst_lnt; +fprintf (st, "PC PSL IR\n\n"); +for (k = 0; k < lnt; k++) { /* print specified */ + h = &hst[(di++) % hst_lnt]; /* entry pointer */ + if (h->iPC == 0) continue; /* filled in? */ + fprintf(st, "%08X %08X| ", h->iPC, h->PSL); /* PC, PSL */ + numspec = drom[h->opc][0] & DR_NSPMASK; /* #specifiers */ + if (opcode[h->opc] == NULL) /* undefined? */ + fprintf (st, "%03X (undefined)", h->opc); + else if (h->PSL & PSL_FPD) /* FPD set? */ + fprintf (st, "%s FPD set", opcode[h->opc]); + else { /* normal */ + for (i = 0; i < INST_SIZE; i++) sim_eval[i] = h->inst[i]; + if ((fprint_sym (st, h->iPC, sim_eval, &cpu_unit, SWMASK ('M'))) > 0) + fprintf (st, "%03X (undefined)", h->opc); + if ((numspec > 1) || + ((numspec == 1) && (drom[h->opc][1] < BB))) { + if (cpu_show_opnd (st, h, 0)) { /* operands; more? */ + if (cpu_show_opnd (st, h, 1)) { /* 2nd line; more? */ + cpu_show_opnd (st, h, 2); /* octa, 3rd/4th */ + cpu_show_opnd (st, h, 3); + } + } + } + } /* end else */ + fputc ('\n', st); /* end line */ + } /* end for */ +return SCPE_OK; +} + +t_bool cpu_show_opnd (FILE *st, InstHistory *h, int32 line) +{ + +int32 numspec, i, j, disp; +t_bool more; + +numspec = drom[h->opc][0] & DR_NSPMASK; /* #specifiers */ +fputs ("\n ", st); /* space */ +for (i = 1, j = 0, more = FALSE; i <= numspec; i++) { /* loop thru specs */ + disp = drom[h->opc][i]; /* specifier type */ + if (disp == RG) disp = RQ; /* fix specials */ + else if (disp >= BB) break; /* ignore branches */ + else switch (disp & (DR_LNMASK|DR_ACMASK)) { + + case RB: case RW: case RL: /* read */ + case AB: case AW: case AL: case AQ: case AO: /* address */ + case MB: case MW: case ML: /* modify */ + if (line == 0) fprintf (st, " %08X", h->opnd[j]); + else fputs (" ", st); + j = j + 1; + break; + case RQ: case MQ: /* read, modify quad */ + if (line <= 1) fprintf (st, " %08X", h->opnd[j + line]); + else fputs (" ", st); + if (line == 0) more = TRUE; + j = j + 2; + break; + case RO: case MO: /* read, modify octa */ + fprintf (st, " %08X", h->opnd[j + line]); + more = TRUE; + j = j + 4; + break; + case WB: case WW: case WL: case WQ: case WO: /* write */ + if (line == 0) fprintf (st, " %08X", h->opnd[j + 1]); + else fputs (" ", st); + j = j + 2; + break; + } /* end case */ + } /* end for */ +return more; +} + +struct os_idle { + char *name; + uint32 mask; + }; + +static struct os_idle os_tab[] = { + { "VMS", 0x8 }, + { "NETBSD", 0x2 }, + { "ULTRIX", 0x2 }, + { "OPENBSD", 0x1 }, + { "32V", 0x1 }, + { NULL, 0 } + }; + +/* Set and show idle */ + +t_stat cpu_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +uint32 i; + +if (cptr != NULL) { + for (i = 0; os_tab[i].name != NULL; i++) { + if (strcmp (os_tab[i].name, cptr) == 0) { + cpu_idle_type = i + 1; + cpu_idle_ipl_mask = os_tab[i].mask; + return sim_set_idle (uptr, val, cptr, desc); + } + } + return SCPE_ARG; + } +return sim_set_idle (uptr, val, cptr, desc); +} + +t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (sim_idle_enab && (cpu_idle_type != 0)) + fprintf (st, "idle enabled=%s", os_tab[cpu_idle_type - 1].name); +else fprintf (st, "idle disabled"); +return SCPE_OK; +} diff --git a/VAX/vax_cpu1.c b/VAX/vax_cpu1.c new file mode 100644 index 0000000..d0ba90c --- /dev/null +++ b/VAX/vax_cpu1.c @@ -0,0 +1,1569 @@ +/* vax_cpu1.c: VAX complex instructions + + Copyright (c) 1998-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 28-May-08 RMS Inlined physical memory routines + 29-Apr-07 RMS Separated base register access checks for 11/780 + 10-May-06 RMS Added access check on system PTE for 11/780 + Added mbz check in LDPCTX for 11/780 + 22-Sep-06 RMS Fixed declarations (from Sterling Garwood) + 30-Sep-04 RMS Added conditionals for full VAX + Moved emulation to vax_cis.c + Moved model-specific IPRs to system module + 27-Jan-04 RMS Added device logging support + Fixed EXTxV, INSV double register PC reference fault + 30-Apr-02 RMS Fixed interrupt/exception handler to clear traps + 17-Apr-02 RMS Fixed pos > 31 test in bit fields (should be unsigned) + 14-Apr-02 RMS Fixed prv_mode handling for interrupts (found by Tim Stark) + Fixed PROBEx to mask mode to 2b (found by Kevin Handy) + + This module contains the instruction simulators for + + Field instructions: + - BBS, BBC, BBSSI, BBCCI + - BBSC, BBCC, BBCS, BBSS + - EXTV, EXTZV, CMPV, CMPZV + - FFS, FFC, INSV + + Call/return and push/pop instructions: + - CALLS, CALLG, RET + - PUSHR, POPR + + Queue instructions: + - INSQUE, REMQUE + - INSQHI, INSQTI, REMQHI, REMQTI + + String instructions: + - MOVC3, MOVC5, CMPC3, CMPC5 + - LOCC, SKPC, SCANC, SPANC + + Operating system interface instructions: + - CHMK, CHME, CHMS, CHMU + - PROBER, PROBEW, REI + - MTPR, MFPR + - LDPCTX, SVPCTX + - (interrupt and exception routine) +*/ + +#include "vax_defs.h" + +static const uint8 rcnt[128] = { + 0, 4, 4, 8, 4, 8, 8,12, 4, 8, 8,12, 8,12,12,16, /* 00 - 0F */ + 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 10 - 1F */ + 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 20 - 2F */ + 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 30 - 3F */ + 4, 8, 8,12, 8,12,12,16, 8,12,12,16,12,16,16,20, /* 40 - 4F */ + 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 50 - 5F */ + 8,12,12,16,12,16,16,20,12,16,16,20,16,20,20,24, /* 60 - 6F */ +12,16,16,20,16,20,20,24,16,20,20,24,20,24,24,28 /* 70 - 7F */ +}; + +int32 last_chm = 0; + +extern uint32 *M; +extern const uint32 byte_mask[33]; +extern int32 R[16]; +extern int32 STK[5]; +extern int32 PSL; +extern int32 SCBB, PCBB, SBR, SLR; +extern int32 P0BR, P0LR, P1BR, P1LR; +extern int32 ASTLVL, SISR, mapen; +extern int32 pme; +extern int32 trpirq; +extern int32 p1, p2; +extern int32 fault_PC; +extern int32 pcq[PCQ_SIZE]; +extern int32 pcq_p; +extern int32 in_ie; +extern int32 sim_interval; +extern int32 ibcnt, ppc; +extern FILE *sim_deb; +extern DEVICE cpu_dev; + +extern int32 Test (uint32 va, int32 acc, int32 *status); +extern void set_map_reg (void); +extern void zap_tb (int stb); +extern void zap_tb_ent (uint32 va); +extern t_bool chk_tb_ent (uint32 va); +extern int32 ReadIPR (int32 rg); +extern void WriteIPR (int32 rg, int32 val); +extern t_bool BadCmPSL (int32 newpsl); +extern int32 cpu_psl_ipl_idle (int32 newpsl); + +extern jmp_buf save_env; + +/* Branch on bit and no modify + Branch on bit and modify + + opnd[0] = position (pos.rl) + opnd[1] = register number/memory flag + opnd[2] = memory address, if memory + Returns bit to be tested +*/ + +int32 op_bb_n (int32 *opnd, int32 acc) +{ +int32 pos = opnd[0]; +int32 rn = opnd[1]; +int32 ea, by; + +if (rn >= 0) { /* register? */ + if (((uint32) pos) > 31) RSVD_OPND_FAULT; /* pos > 31? fault */ + return (R[rn] >> pos) & 1; /* get bit */ + } +ea = opnd[2] + (pos >> 3); /* base byte addr */ +pos = pos & 07; /* pos in byte */ +by = Read (ea, L_BYTE, RA); /* read byte */ +return ((by >> pos) & 1); /* get bit */ +} + +int32 op_bb_x (int32 *opnd, int32 newb, int32 acc) +{ +int32 pos = opnd[0]; +int32 rn = opnd[1]; +int32 ea, by, bit; + +if (rn >= 0) { /* register? */ + if (((uint32) pos) > 31) RSVD_OPND_FAULT; /* pos > 31? fault */ + bit = (R[rn] >> pos) & 1; /* get bit */ + R[rn] = newb? (R[rn] | (1u << pos)): (R[rn] & ~(1u << pos)); + return bit; + } +ea = opnd[2] + (pos >> 3); /* base byte addr */ +pos = pos & 07; /* pos in byte */ +by = Read (ea, L_BYTE, WA); /* read byte */ +bit = (by >> pos) & 1; /* get bit */ +by = newb? (by | (1u << pos)): (by & ~(1u << pos)); /* change bit */ +Write (ea, by, L_BYTE, WA); /* rewrite byte */ +return bit; +} + +/* Extract field + + opnd[0] = position (pos.rl) + opnd[1] = size (size.rb) + opnd[2] = register number/memory flag + opnd[3] = register content/memory address + + If the field is in a register, rn + 1 is in vfldrp1 +*/ + +int32 op_extv (int32 *opnd, int32 vfldrp1, int32 acc) +{ +int32 pos = opnd[0]; +int32 size = opnd[1]; +int32 rn = opnd[2]; +uint32 wd = opnd[3]; +int32 ba, wd1 = 0; + +if (size == 0) return 0; /* size 0? field = 0 */ +if (size > 32) RSVD_OPND_FAULT; /* size > 32? fault */ +if (rn >= 0) { /* register? */ + if (((uint32) pos) > 31) RSVD_OPND_FAULT; /* pos > 31? fault */ + if (((pos + size) > 32) && (rn >= nSP)) /* span 2 reg, PC? */ + RSVD_ADDR_FAULT; /* fault */ + if (pos) wd = (wd >> pos) | (((uint32) vfldrp1) << (32 - pos)); + } +else { + ba = wd + (pos >> 3); /* base byte addr */ + pos = (pos & 07) | ((ba & 03) << 3); /* bit offset */ + ba = ba & ~03; /* lw align base */ + wd = Read (ba, L_LONG, RA); /* read field */ + if ((size + pos) > 32) wd1 = Read (ba + 4, L_LONG, RA); + if (pos) wd = (wd >> pos) | (((uint32) wd1) << (32 - pos)); + } +return wd & byte_mask[size]; +} + +/* Insert field + + opnd[0] = field (src.rl) + opnd[1] = position (pos.rl) + opnd[2] = size (size.rb) + opnd[3] = register number/memory flag + opnd[4] = register content/memory address + + If the field is in a register, rn + 1 is in vfldrp1 +*/ + +void op_insv (int32 *opnd, int32 vfldrp1, int32 acc) +{ +uint32 ins = opnd[0]; +int32 pos = opnd[1]; +int32 size = opnd[2]; +int32 rn = opnd[3]; +int32 val, mask, ba, wd, wd1; + +if (size == 0) return; /* size = 0? done */ +if (size > 32) RSVD_OPND_FAULT; /* size > 32? fault */ +if (rn >= 0) { /* in registers? */ + if (((uint32) pos) > 31) RSVD_OPND_FAULT; /* pos > 31? fault */ + if ((pos + size) > 32) { /* span two reg? */ + if (rn >= nSP) RSVD_ADDR_FAULT; /* if PC, fault */ + mask = byte_mask[pos + size - 32]; /* insert fragment */ + val = ins >> (32 - pos); + R[rn + 1] = (vfldrp1 & ~mask) | (val & mask); + } + mask = byte_mask[size] << pos; /* insert field */ + val = ins << pos; + R[rn] = (R[rn] & ~mask) | (val & mask); + } +else { + ba = opnd[4] + (pos >> 3); /* base byte addr */ + pos = (pos & 07) | ((ba & 03) << 3); /* bit offset */ + ba = ba & ~03; /* lw align base */ + wd = Read (ba, L_LONG, WA); /* read field */ + if ((size + pos) > 32) { /* field span lw? */ + wd1 = Read (ba + 4, L_LONG, WA); /* read 2nd lw */ + mask = byte_mask[pos + size - 32]; /* insert fragment */ + val = ins >> (32 - pos); + Write (ba + 4, (wd1 & ~mask) | (val & mask), L_LONG, WA); + } + mask = byte_mask[size] << pos; /* insert field */ + val = ins << pos; + Write (ba, (wd & ~mask) | (val & mask), L_LONG, WA); + } +return; +} + +/* Find first */ + +int32 op_ffs (uint32 wd, int32 size) +{ +int32 i; + +for (i = 0; wd; i++, wd = wd >> 1) if (wd & 1) return i; +return size; +} + +#define CALL_DV 0x8000 /* DV set */ +#define CALL_IV 0x4000 /* IV set */ +#define CALL_MBZ 0x3000 /* MBZ */ +#define CALL_MASK 0x0FFF /* mask */ +#define CALL_V_SPA 30 /* SPA */ +#define CALL_M_SPA 03 +#define CALL_V_S 29 /* S flag */ +#define CALL_S (1 << CALL_V_S) +#define CALL_V_MASK 16 +#define CALL_PUSH(n) if ((mask >> (n)) & 1) { \ + tsp = tsp - 4; \ + Write (tsp, R[n], L_LONG, WA); \ + } +#define CALL_GETSPA(x) (((x) >> CALL_V_SPA) & CALL_M_SPA) +#define RET_POP(n) if ((spamask >> (n + CALL_V_MASK)) & 1) { \ + R[n] = Read (tsp, L_LONG, RA); \ + tsp = tsp + 4; \ + } +#define PUSHR_PUSH(n) CALL_PUSH(n) +#define POPR_POP(n) if ((mask >> (n)) & 1) { \ + R[n] = Read (SP, L_LONG, RA); \ + SP = SP + 4; \ + } + +/* CALLG, CALLS + + opnd[0] = argument (arg.rx) + opnd[1] = procedure address (adr.ab) + flg = CALLG (0), CALLS (1) + acc = access mask + + These instructions implement a generalized procedure call and return facility. + The principal data structure involved is the stack frame. + CALLS and CALLG build a stack frame in the following format: + + + +---------------------------------------------------------------+ + | condition handler (initially 0) | + +---+-+-+-----------------------+--------------------+----------+ + |SPA|S|0| entry mask<11:0> | saved PSW<15:5> | 0 0 0 0 0| + +---+-+-+-----------------------+--------------------+----------+ + | saved AP | + +---------------------------------------------------------------+ + | saved FP | + +---------------------------------------------------------------+ + | saved PC | + +---------------------------------------------------------------+ + | saved R0 (...) | + +---------------------------------------------------------------+ + . . + . (according to entry mask<11:0>) . + . . + +---------------------------------------------------------------+ + | saved R11 (...) | + +---------------+-----------------------------------------------+ + | #args (CALLS) | (0-3 bytes needed to align stack) | + +---------------+-----------------------------------------------+ + | | 0 0 0 (CALLS) | + +---------------+-----------------------------------------------+ + + RET expects to find this structure based at the frame pointer (FP). + + For CALLG and CALLS, the entry mask specifies the new settings of + DV and IV, and also which registers are to be saved on entry: + + 15 14 13 12 11 0 + +--+--+-----+----------------------------------+ + |DV|IV| MBZ | register mask | + +--+--+-----+----------------------------------+ + + CALLG/CALLS operation: + + read the procedure entry mask + make sure that the stack frame will be accessible + if CALLS, push the number of arguments onto the stack + align the stack to the next lower longword boundary + push the registers specified by the procedure entry mask + push PC, AP, FP, saved SPA/S0/mask/PSW, condition handler + update PC, SP, FP, AP + update PSW traps, clear condition codes +*/ + +int32 op_call (int32 *opnd, t_bool gs, int32 acc) +{ +int32 addr = opnd[1]; +int32 mask, stklen, tsp, wd; + +mask = Read (addr, L_WORD, RA); /* get proc mask */ +if (mask & CALL_MBZ) RSVD_OPND_FAULT; /* test mbz */ +stklen = rcnt[mask & 077] + rcnt[(mask >> 6) & 077] + (gs? 24: 20); +Read (SP - stklen, L_BYTE, WA); /* wchk stk */ +if (gs) { + Write (SP - 4, opnd[0], L_LONG, WA); /* if S, push #arg */ + SP = SP - 4; /* stack is valid */ + } +tsp = SP & ~CALL_M_SPA; /* lw align stack */ +CALL_PUSH (11); /* check mask bits, */ +CALL_PUSH (10); /* push sel reg */ +CALL_PUSH (9); +CALL_PUSH (8); +CALL_PUSH (7); +CALL_PUSH (6); +CALL_PUSH (5); +CALL_PUSH (4); +CALL_PUSH (3); +CALL_PUSH (2); +CALL_PUSH (1); +CALL_PUSH (0); +Write (tsp - 4, PC, L_LONG, WA); /* push PC */ +Write (tsp - 8, FP, L_LONG, WA); /* push AP */ +Write (tsp - 12, AP, L_LONG, WA); /* push FP */ +wd = ((SP & CALL_M_SPA) << CALL_V_SPA) | (gs << CALL_V_S) | + ((mask & CALL_MASK) << CALL_V_MASK) | (PSL & 0xFFE0); +Write (tsp - 16, wd, L_LONG, WA); /* push spa/s/mask/psw */ +Write (tsp - 20, 0, L_LONG, WA); /* push cond hdlr */ +if (gs) AP = SP; /* update AP */ +else AP = opnd[0]; +SP = FP = tsp - 20; /* update FP, SP */ +PSL = (PSL & ~(PSW_DV | PSW_FU | PSW_IV)) | /* update PSW */ + ((mask & CALL_DV)? PSW_DV: 0) | + ((mask & CALL_IV)? PSW_IV: 0); +JUMP (addr + 2); /* new PC */ +return 0; /* new cc's */ +} + +int32 op_ret (int32 acc) +{ +int32 spamask, stklen, newpc, nargs; +int32 tsp = FP; + +spamask = Read (tsp + 4, L_LONG, RA); /* spa/s/mask/psw */ +if (spamask & PSW_MBZ) RSVD_OPND_FAULT; /* test mbz */ +stklen = rcnt[(spamask >> CALL_V_MASK) & 077] + + rcnt[(spamask >> (CALL_V_MASK + 6)) & 077] + ((spamask & CALL_S)? 23: 19); +Read (tsp + stklen, L_BYTE, RA); /* rchk stk end */ +AP = Read (tsp + 8, L_LONG, RA); /* restore AP */ +FP = Read (tsp + 12, L_LONG, RA); /* restore FP */ +newpc = Read (tsp + 16, L_LONG, RA); /* get new PC */ +tsp = tsp + 20; /* update stk ptr */ +RET_POP (0); /* chk mask bits, */ +RET_POP (1); /* pop sel regs */ +RET_POP (2); +RET_POP (3); +RET_POP (4); +RET_POP (5); +RET_POP (6); +RET_POP (7); +RET_POP (8); +RET_POP (9); +RET_POP (10); +RET_POP (11); +SP = tsp + CALL_GETSPA (spamask); /* dealign stack */ +if (spamask & CALL_S) { /* CALLS? */ + nargs = Read (SP, L_LONG, RA); /* read #args */ + SP = SP + 4 + ((nargs & BMASK) << 2); /* pop arg list */ + } +PSL = (PSL & ~(PSW_DV | PSW_FU | PSW_IV | PSW_T)) | /* reset PSW */ + (spamask & (PSW_DV | PSW_FU | PSW_IV | PSW_T)); +JUMP (newpc); /* set new PC */ +return spamask & (CC_MASK); /* return cc's */ +} + +/* PUSHR and POPR */ + +void op_pushr (int32 *opnd, int32 acc) +{ +int32 mask = opnd[0] & 0x7FFF; +int32 stklen, tsp; + +if (mask == 0) return; +stklen = rcnt[(mask >> 7) & 0177] + rcnt[mask & 0177] + + ((mask & 0x4000)? 4: 0); +Read (SP - stklen, L_BYTE, WA); /* wchk stk end */ +tsp = SP; /* temp stk ptr */ +PUSHR_PUSH (14); /* check mask bits, */ +PUSHR_PUSH (13); /* push sel reg */ +PUSHR_PUSH (12); +PUSHR_PUSH (11); +PUSHR_PUSH (10); +PUSHR_PUSH (9); +PUSHR_PUSH (8); +PUSHR_PUSH (7); +PUSHR_PUSH (6); +PUSHR_PUSH (5); +PUSHR_PUSH (4); +PUSHR_PUSH (3); +PUSHR_PUSH (2); +PUSHR_PUSH (1); +PUSHR_PUSH (0); +SP = tsp; /* update stk ptr */ +return; +} + +void op_popr (int32 *opnd, int32 acc) +{ +int32 mask = opnd[0] & 0x7FFF; +int32 stklen; + +if (mask == 0) return; +stklen = rcnt[(mask >> 7) & 0177] + rcnt[mask & 0177] + + ((mask & 0x4000)? 4: 0); +Read (SP + stklen - 1, L_BYTE, RA); /* rchk stk end */ +POPR_POP (0); /* check mask bits, */ +POPR_POP (1); /* pop sel regs */ +POPR_POP (2); +POPR_POP (3); +POPR_POP (4); +POPR_POP (5); +POPR_POP (6); +POPR_POP (7); +POPR_POP (8); +POPR_POP (9); +POPR_POP (10); +POPR_POP (11); +POPR_POP (12); +POPR_POP (13); +if (mask & 0x4000) SP = Read (SP, L_LONG, RA); /* if pop SP, no inc */ +return; +} + +/* INSQUE + + opnd[0] = entry address (ent.ab) + opnd[1] = predecessor address (pred.ab) + + Condition codes returned to caller on comparison of (ent):(ent+4). + All writes must be checked before any writes are done. + + Pictorially: + + BEFORE AFTER + + P: S P: E W + P+4: (n/a) P+4: (n/a) + + E: --- E: S W + E+4: --- E+4: P W + + S: (n/a) S: (n/a) + S+4: P S+4: E W + + s+4 must be tested with a read modify rather than a probe, as it + might be misaligned. +*/ + +int32 op_insque (int32 *opnd, int32 acc) +{ +int32 p = opnd[1]; +int32 e = opnd[0]; +int32 s, cc; + +s = Read (p, L_LONG, WA); /* s <- (p), wchk */ +Read (s + 4, L_LONG, WA); /* wchk s+4 */ +Read (e + 4, L_LONG, WA); /* wchk e+4 */ +Write (e, s, L_LONG, WA); /* (e) <- s */ +Write (e + 4, p, L_LONG, WA); /* (e+4) <- p */ +Write (s + 4, e, L_LONG, WA); /* (s+4) <- ent */ +Write (p, e, L_LONG, WA); /* (p) <- e */ +CC_CMP_L (s, p); /* set cc's */ +return cc; +} + +/* REMQUE + + opnd[0] = entry address (ent.ab) + opnd[1:2] = destination address (dst.wl) + + Condition codes returned to caller based on (ent):(ent+4). + All writes must be checked before any writes are done. + + Pictorially: + + BEFORE AFTER + + P: E P: S W + P+4: (n/a) P+4: (n/a) + + E: S W E: S + E+4: P W E+4: P + + S: (n/a) S: (n/a) + S+4: E W S+4: P + +*/ + +int32 op_remque (int32 *opnd, int32 acc) +{ +int32 e = opnd[0]; +int32 s, p, cc; + +s = Read (e, L_LONG, RA); /* s <- (e) */ +p = Read (e + 4, L_LONG, RA); /* p <- (e+4) */ +CC_CMP_L (s, p); /* set cc's */ +if (e != p) { /* queue !empty? */ + Read (s + 4, L_LONG, WA); /* wchk (s+4) */ + if (opnd[1] < 0) Read (opnd[2], L_LONG, WA); /* wchk dest */ + Write (p, s, L_LONG, WA); /* (p) <- s */ + Write (s + 4, p, L_LONG, WA); /* (s+4) <- p */ + } +else cc = cc | CC_V; /* else set v */ +if (opnd[1] >= 0) R[opnd[1]] = e; /* store result */ +else Write (opnd[2], e, L_LONG, WA); +return cc; +} + +/* Interlocked insert instructions + + opnd[0] = entry (ent.ab) + opnd[1] = header (hdr.aq) + + Pictorially: + + BEFORE AFTER INSQHI AFTER INSQTI + + H: A-H H: D-H W H: A-H W for interlock + H+4: C-H H+4: C-H H+4: D-H W + + A: B-A A: B-A A: B-A + A+4: H-A A+4: D-A W A+4: H-A + + B: C-B B: C-B B: C-B + B+4: A-B B+4: A-B B+4: A-B + + C: H-C C: H-C C: D-C W + C+4: B-C C+4: B-C C+4: B-C + + D: --- D: A-D W D: H-D W + D+4: --- D+4: H-D W D+4: C-D W + + Note that the queue header, the entry to be inserted, and all + the intermediate entries that are "touched" in any way must be + QUADWORD aligned. In addition, the header and the entry must + not be equal. +*/ + +int32 op_insqhi (int32 *opnd, int32 acc) +{ +int32 h = opnd[1]; +int32 d = opnd[0]; +int32 a, t; + +if ((h == d) || ((h | d) & 07)) RSVD_OPND_FAULT; /* h, d quad align? */ +Read (d, L_BYTE, WA); /* wchk ent */ +a = Read (h, L_LONG, WA); /* a <- (h), wchk */ +if (a & 06) RSVD_OPND_FAULT; /* chk quad align */ +if (a & 01) return CC_C; /* busy, cc = 0001 */ +Write (h, a | 1, L_LONG, WA); /* get interlock */ +a = a + h; /* abs addr of a */ +if (Test (a, WA, &t) < 0) Write (h, a - h, L_LONG, WA); /* wtst a, rls if err */ +Write (a + 4, d - a, L_LONG, WA); /* (a+4) <- d-a, flt ok */ +Write (d, a - d, L_LONG, WA); /* (d) <- a-d */ +Write (d + 4, h - d, L_LONG, WA); /* (d+4) <- h-d */ +Write (h, d - h, L_LONG, WA); /* (h) <- d-h, rls int */ +return (a == h)? CC_Z: 0; /* Z = 1 if a = h */ +} + +int32 op_insqti (int32 *opnd, int32 acc) +{ +int32 h = opnd[1]; +int32 d = opnd[0]; +int32 a, c, t; + +if ((h == d) || ((h | d) & 07)) RSVD_OPND_FAULT; /* h, d quad align? */ +Read (d, L_BYTE, WA); /* wchk ent */ +a = Read (h, L_LONG, WA); /* a <- (h), wchk */ +if (a == 0) return op_insqhi (opnd, acc); /* if empty, ins hd */ +if (a & 06) RSVD_OPND_FAULT; /* chk quad align */ +if (a & 01) return CC_C; /* busy, cc = 0001 */ +Write (h, a | 1, L_LONG, WA); /* acquire interlock */ +c = Read (h + 4, L_LONG, RA) + h; /* c <- (h+4) + h */ +if (c & 07) { /* c quad aligned? */ + Write (h, a, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } +if (Test (c, WA, &t) < 0) Write (h, a, L_LONG, WA); /* wtst c, rls if err */ +Write (c, d - c, L_LONG, WA); /* (c) <- d-c, flt ok */ +Write (d, h - d, L_LONG, WA); /* (d) <- h-d */ +Write (d + 4, c - d, L_LONG, WA); /* (d+4) <- c-d */ +Write (h + 4, d - h, L_LONG, WA); /* (h+4) <- d-h */ +Write (h, a, L_LONG, WA); /* release interlock */ +return 0; /* q >= 2 entries */ +} + +/* Interlocked remove instructions + + opnd[0] = header (hdr.aq) + opnd[1:2] = destination address (dst.al) + + Pictorially: + + BEFORE AFTER REMQHI AFTER REMQTI + + H: A-H H: B-H W H: A-H W for interlock + H+4: C-H H+4: C-H H+4: B-H W + + A: B-A A: B-A R A: B-A + A+4: H-A A+4: H-A A+4: H-A + + B: C-B B: C-B B: H-B W + B+4: A-B B+4: H-B W B+4: A-B + + C: H-C C: H-C C: H-C + C+4: B-C C+4: B-C C+4: B-C R + + Note that the queue header and all the entries that are + "touched" in any way must be QUADWORD aligned. In addition, + the header and the destination must not be equal. +*/ + +int32 op_remqhi (int32 *opnd, int32 acc) +{ +int32 h = opnd[0]; +int32 ar, a, b, t; + +if (h & 07) RSVD_OPND_FAULT; /* h quad aligned? */ +if (opnd[1] < 0) { /* mem destination? */ + if (h == opnd[2]) RSVD_OPND_FAULT; /* hdr = dst? */ + Read (opnd[2], L_LONG, WA); /* wchk dst */ + } +ar = Read (h, L_LONG, WA); /* ar <- (h) */ +if (ar & 06) RSVD_OPND_FAULT; /* a quad aligned? */ +if (ar & 01) return CC_V | CC_C; /* busy, cc = 0011 */ +a = ar + h; /* abs addr of a */ +if (ar) { /* queue not empty? */ + Write (h, ar | 1, L_LONG, WA); /* acquire interlock */ + if (Test (a, RA, &t) < 0) /* read tst a */ + Write (h, ar, L_LONG, WA); /* release if error */ + b = Read (a, L_LONG, RA) + a; /* b <- (a)+a, flt ok */ + if (b & 07) { /* b quad aligned? */ + Write (h, ar, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } + if (Test (b, WA, &t) < 0) /* write test b */ + Write (h, ar, L_LONG, WA); /* release if err */ + Write (b + 4, h - b, L_LONG, WA); /* (b+4) <- h-b, flt ok */ + Write (h, b - h, L_LONG, WA); /* (h) <- b-h, rls int */ + } +if (opnd[1] >= 0) R[opnd[1]] = a; /* store result */ +else Write (opnd[2], a, L_LONG, WA); +if (ar == 0) return CC_Z | CC_V; /* empty, cc = 0110 */ +return (b == h)? CC_Z: 0; /* if b = h, q empty */ +} + +int32 op_remqti (int32 *opnd, int32 acc) +{ +int32 h = opnd[0]; +int32 ar, b, c, t; + +if (h & 07) RSVD_OPND_FAULT; /* h quad aligned? */ +if (opnd[1] < 0) { /* mem destination? */ + if (h == opnd[2]) RSVD_OPND_FAULT; /* hdr = dst? */ + Read (opnd[2], L_LONG, WA); /* wchk dst */ + } +ar = Read (h, L_LONG, WA); /* a <- (h) */ +if (ar & 06) RSVD_OPND_FAULT; /* a quad aligned? */ +if (ar & 01) return CC_V | CC_C; /* busy, cc = 0011 */ +if (ar) { /* queue not empty */ + Write (h, ar | 1, L_LONG, WA); /* acquire interlock */ + c = Read (h + 4, L_LONG, RA); /* c <- (h+4) */ + if (ar == c) { /* single entry? */ + Write (h, ar, L_LONG, WA); /* release interlock */ + return op_remqhi (opnd, acc); /* treat as remqhi */ + } + if (c & 07) { /* c quad aligned? */ + Write (h, ar, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } + c = c + h; /* abs addr of c */ + if (Test (c + 4, RA, &t) < 0) /* read test c+4 */ + Write (h, ar, L_LONG, WA); /* release if error */ + b = Read (c + 4, L_LONG, RA) + c; /* b <- (c+4)+c, flt ok */ + if (b & 07) { /* b quad aligned? */ + Write (h, ar, L_LONG, WA); /* release interlock */ + RSVD_OPND_FAULT; /* fault */ + } + if (Test (b, WA, &t) < 0) /* write test b */ + Write (h, ar, L_LONG, WA); /* release if error */ + Write (b, h - b, L_LONG, WA); /* (b) <- h-b */ + Write (h + 4, b - h, L_LONG, WA); /* (h+4) <- b-h */ + Write (h, ar, L_LONG, WA); /* release interlock */ + } +else c = h; /* empty, result = h */ +if (opnd[1] >= 0) R[opnd[1]] = c; /* store result */ +else Write (opnd[2], c, L_LONG, WA); +if (ar == 0) return CC_Z | CC_V; /* empty, cc = 0110 */ +return 0; /* q can't be empty */ +} + +/* String instructions */ + +#define MVC_FRWD 0 /* movc state codes */ +#define MVC_BACK 1 +#define MVC_FILL 3 /* must be 3 */ +#define MVC_M_STATE 3 +#define MVC_V_CC 2 + +/* MOVC3, MOVC5 + + if PSL = 0 and MOVC3, + opnd[0] = length + opnd[1] = source address + opnd[2] = dest address + + if PSL = 0 and MOVC5, + opnd[0] = source length + opnd[1] = source address + opnd[2] = fill + opnd[3] = dest length + opnd[4] = dest address + + if PSL = 1, + R0 = delta-PC/fill/initial move length + R1 = current source address + R2 = current move length + R3 = current dest address + R4 = dstlen - srclen (loop count if fill state) + R5 = cc/state +*/ + +int32 op_movc (int32 *opnd, int32 movc5, int32 acc) +{ +int32 i, cc, fill, wd; +int32 j, lnt, mlnt[3]; +static const int32 looplnt[3] = { L_BYTE, L_LONG, L_BYTE }; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get fill */ + R[2] = R[2] & STR_LNMASK; /* mask lengths */ + if (R[4] > 0) R[4] = R[4] & STR_LNMASK; + } +else { + R[1] = opnd[1]; /* src addr */ + if (movc5) { /* MOVC5? */ + R[2] = (opnd[0] < opnd[3])? opnd[0]: opnd[3]; + R[3] = opnd[4]; /* dst addr */ + R[4] = opnd[3] - opnd[0]; /* dstlen - srclen */ + fill = opnd[2]; /* set fill */ + CC_CMP_W (opnd[0], opnd[3]); /* set cc's */ + } + else { + R[2] = opnd[0]; /* mvlen = srclen */ + R[3] = opnd[2]; /* dst addr */ + R[4] = fill = 0; /* no fill */ + cc = CC_Z; /* set cc's */ + } + R[0] = STR_PACK (fill, R[2]); /* initial mvlen */ + if (R[2]) { /* any move? */ + if (((uint32) R[1]) < ((uint32) R[3])) { + R[1] = R[1] + R[2]; /* backward, adjust */ + R[3] = R[3] + R[2]; /* addr to end */ + R[5] = MVC_BACK; /* set state */ + } + else R[5] = MVC_FRWD; /* fwd, set state */ + } + else R[5] = MVC_FILL; /* fill, set state */ + R[5] = R[5] | (cc << MVC_V_CC); /* pack with state */ + PSL = PSL | PSL_FPD; /* set FPD */ + } + +/* At this point, + + R0 = delta PC'fill'initial move length + R1 = current src addr + R2 = current move length + R3 = current dst addr + R4 = dst length - src length + R5 = cc'state +*/ + +switch (R[5] & MVC_M_STATE) { /* case on state */ + + case MVC_FRWD: /* move forward */ + mlnt[0] = (4 - R[3]) & 3; /* length to align */ + if (mlnt[0] > R[2]) mlnt[0] = R[2]; /* cant exceed total */ + mlnt[1] = (R[2] - mlnt[0]) & ~03; /* aligned length */ + mlnt[2] = R[2] - mlnt[0] - mlnt[1]; /* tail */ + for (i = 0; i < 3; i++) { /* head, align, tail */ + lnt = looplnt[i]; /* length for loop */ + for (j = 0; j < mlnt[i]; j = j + lnt, sim_interval--) { + wd = Read (R[1], lnt, RA); /* read src */ + Write (R[3], wd, lnt, WA); /* write dst */ + R[1] = R[1] + lnt; /* inc src addr */ + R[3] = R[3] + lnt; /* inc dst addr */ + R[2] = R[2] - lnt; /* dec move lnt */ + } + } + goto FILL; /* check for fill */ + + case MVC_BACK: /* move backward */ + mlnt[0] = R[3] & 03; /* length to align */ + if (mlnt[0] > R[2]) mlnt[0] = R[2]; /* cant exceed total */ + mlnt[1] = (R[2] - mlnt[0]) & ~03; /* aligned length */ + mlnt[2] = R[2] - mlnt[0] - mlnt[1]; /* tail */ + for (i = 0; i < 3; i++) { /* head, align, tail */ + lnt = looplnt[i]; /* length for loop */ + for (j = 0; j < mlnt[i]; j = j + lnt, sim_interval--) { + wd = Read (R[1] - lnt, lnt, RA); /* read src */ + Write (R[3] - lnt, wd, lnt, WA); /* write dst */ + R[1] = R[1] - lnt; /* dec src addr */ + R[3] = R[3] - lnt; /* dec dst addr */ + R[2] = R[2] - lnt; /* dec move lnt */ + } + } + R[1] = R[1] + (R[0] & STR_LNMASK); /* final src addr */ + R[3] = R[3] + (R[0] & STR_LNMASK); /* final dst addr */ + + case MVC_FILL: /* fill */ + FILL: + if (R[4] <= 0) break; /* any fill? */ + R[5] = R[5] | MVC_FILL; /* set state */ + mlnt[0] = (4 - R[3]) & 3; /* length to align */ + if (mlnt[0] > R[4]) mlnt[0] = R[4]; /* cant exceed total */ + mlnt[1] = (R[4] - mlnt[0]) & ~03; /* aligned length */ + mlnt[2] = R[4] - mlnt[0] - mlnt[1]; /* tail */ + for (i = 0; i < 3; i++) { /* head, align, tail */ + lnt = looplnt[i]; /* length for loop */ + fill = fill & BMASK; /* fill for loop */ + if (lnt == L_LONG) fill = + (((uint32) fill) << 24) | (fill << 16) | (fill << 8) | fill; + for (j = 0; j < mlnt[i]; j = j + lnt, sim_interval--) { + Write (R[3], fill, lnt, WA); /* write fill */ + R[3] = R[3] + lnt; /* inc dst addr */ + R[4] = R[4] - lnt; /* dec fill lnt */ + } + } + break; + + default: /* bad state */ + RSVD_OPND_FAULT; /* you lose */ + } + +PSL = PSL & ~PSL_FPD; /* clear FPD */ +cc = (R[5] >> MVC_V_CC) & CC_MASK; /* get cc's */ +R[0] = NEG (R[4]); /* set R0 */ +R[2] = R[4] = R[5] = 0; /* clear reg */ +return cc; +} + +/* CMPC3, CMPC5 + + if PSL = 0 and CMPC3, + opnd[0] = length + opnd[1] = source1 address + opnd[2] = source2 address + + if PSL = 0 and CMPC5, + opnd[0] = source1 length + opnd[1] = source1 address + opnd[2] = fill + opnd[3] = source2 length + opnd[4] = source2 address + + if PSL = 1, + R0 = delta-PC/fill/source1 length + R1 = source1 address + R2 = source2 length + R3 = source2 address +*/ + +int32 op_cmpc (int32 *opnd, int32 cmpc5, int32 acc) +{ +int32 cc, s1, s2, fill; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + fill = STR_GETCHR (R[0]); /* get fill */ + } +else { + R[1] = opnd[1]; /* src1len */ + if (cmpc5) { /* CMPC5? */ + R[2] = opnd[3]; /* get src2 opnds */ + R[3] = opnd[4]; + fill = opnd[2]; + } + else { + R[2] = opnd[0]; /* src2len = src1len */ + R[3] = opnd[2]; + fill = 0; + } + R[0] = STR_PACK (fill, opnd[0]); /* src1len + FPD data */ + PSL = PSL | PSL_FPD; + } +R[2] = R[2] & STR_LNMASK; /* mask src2len */ +for (s1 = s2 = 0; ((R[0] | R[2]) & STR_LNMASK) != 0; sim_interval--) { + if (R[0] & STR_LNMASK) s1 = Read (R[1], L_BYTE, RA); /* src1? read */ + else s1 = fill; /* no, use fill */ + if (R[2]) s2 = Read (R[3], L_BYTE, RA); /* src2? read */ + else s2 = fill; /* no, use fill */ + if (s1 != s2) break; /* src1 = src2? */ + if (R[0] & STR_LNMASK) { /* if src1, decr */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = R[1] + 1; + } + if (R[2]) { /* if src2, decr */ + R[2] = (R[2] - 1) & STR_LNMASK; + R[3] = R[3] + 1; + } + } +PSL = PSL & ~PSL_FPD; /* clear FPD */ +CC_CMP_B (s1, s2); /* set cc's */ +R[0] = R[0] & STR_LNMASK; /* clear packup */ +return cc; +} + +/* LOCC, SKPC + + if PSL = 0, + opnd[0] = match character + opnd[1] = source length + opnd[2] = source address + + if PSL = 1, + R0 = delta-PC/match/source length + R1 = source address +*/ + +int32 op_locskp (int32 *opnd, int32 skpc, int32 acc) +{ +int32 c, match; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + match = STR_GETCHR (R[0]); /* get match char */ + } +else { + match = opnd[0]; /* get operands */ + R[0] = STR_PACK (match, opnd[1]); /* src len + FPD data */ + R[1] = opnd[2]; /* src addr */ + PSL = PSL | PSL_FPD; + } +for ( ; (R[0] & STR_LNMASK) != 0; sim_interval-- ) { /* loop thru string */ + c = Read (R[1], L_BYTE, RA); /* get src byte */ + if ((c == match) ^ skpc) break; /* match & locc? */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = R[1] + 1; /* incr src1adr */ + } +PSL = PSL & ~PSL_FPD; /* clear FPD */ +R[0] = R[0] & STR_LNMASK; /* clear packup */ +return (R[0]? 0: CC_Z); /* set cc's */ +} + +/* SCANC, SPANC + + if PSL = 0, + opnd[0] = source length + opnd[1] = source address + opnd[2] = table address + opnd[3] = mask + + if PSL = 1, + R0 = delta-PC/char/source length + R1 = source address + R3 = table address +*/ + +int32 op_scnspn (int32 *opnd, int32 spanc, int32 acc) +{ +int32 c, t, mask; + +if (PSL & PSL_FPD) { /* FPD set? */ + SETPC (fault_PC + STR_GETDPC (R[0])); /* reset PC */ + mask = STR_GETCHR (R[0]); /* get mask */ + } +else { + R[1] = opnd[1]; /* src addr */ + R[3] = opnd[2]; /* tblad */ + mask = opnd[3]; /* mask */ + R[0] = STR_PACK (mask, opnd[0]); /* srclen + FPD data */ + PSL = PSL | PSL_FPD; + } +for ( ; (R[0] & STR_LNMASK) != 0; sim_interval-- ) { /* loop thru string */ + c = Read (R[1], L_BYTE, RA); /* get byte */ + t = Read (R[3] + c, L_BYTE, RA); /* get table ent */ + if (((t & mask) != 0) ^ spanc) break; /* test vs instr */ + R[0] = (R[0] & ~STR_LNMASK) | ((R[0] - 1) & STR_LNMASK); + R[1] = R[1] + 1; + } +PSL = PSL & ~PSL_FPD; +R[0] = R[0] & STR_LNMASK; /* clear packup */ +R[2] = 0; +return (R[0]? 0: CC_Z); +} + +/* Operating system interfaces */ + +/* Interrupt or exception + + vec = SCB vector + cc = condition codes + ipl = new IPL if interrupt + ei = -1: severe exception + 0: normal exception + 1: interrupt +*/ + +int32 intexc (int32 vec, int32 cc, int32 ipl, int ei) +{ +int32 oldpsl = PSL | cc; +int32 oldcur = PSL_GETCUR (oldpsl); +int32 oldsp = SP; +int32 newpsl; +int32 newpc; +int32 acc; + +in_ie = 1; /* flag int/exc */ +CLR_TRAPS; /* clear traps */ +newpc = ReadLP ((SCBB + vec) & PAMASK); /* read new PC */ +if (ei < 0) newpc = newpc | 1; /* severe? on istk */ +if (newpc & 2) ABORT (STOP_ILLVEC); /* bad flags? */ +if (oldpsl & PSL_IS) newpsl = PSL_IS; /* on int stk? */ +else { + STK[oldcur] = SP; /* no, save cur stk */ + if (newpc & 1) { /* to int stk? */ + newpsl = PSL_IS; /* flag */ + SP = IS; /* new stack */ + } + else { + newpsl = 0; /* to ker stk */ + SP = KSP; /* new stack */ + } + } +if (ei > 0) /* if int, new IPL */ + PSL = cpu_psl_ipl_idle (newpsl | (ipl << PSL_V_IPL)); +else PSL = cpu_psl_ipl_idle (newpsl | /* exc, old IPL/1F */ + ((newpc & 1)? PSL_IPL1F: (oldpsl & PSL_IPL)) | (oldcur << PSL_V_PRV)); +if (DEBUG_PRI (cpu_dev, LOG_CPU_I)) fprintf (sim_deb, + ">>IEX: PC=%08x, PSL=%08x, SP=%08x, VEC=%08x, nPSL=%08x, nSP=%08x\n", + PC, oldpsl, oldsp, vec, PSL, SP); +acc = ACC_MASK (KERN); /* new mode is kernel */ +Write (SP - 4, oldpsl, L_LONG, WA); /* push old PSL */ +Write (SP - 8, PC, L_LONG, WA); /* push old PC */ +SP = SP - 8; /* update stk ptr */ +JUMP (newpc & ~3); /* change PC */ +in_ie = 0; /* out of flows */ +return 0; +} + +/* CHMK, CHME, CHMS, CHMU + + opnd[0] = operand +*/ + +int32 op_chm (int32 *opnd, int32 cc, int32 opc) +{ +int32 mode = opc & PSL_M_MODE; +int32 cur = PSL_GETCUR (PSL); +int32 tsp, newpc, acc, sta; + +if (PSL & PSL_IS) ABORT (STOP_CHMFI); +newpc = ReadLP ((SCBB + SCB_CHMK + (mode << 2)) & PAMASK); +if (cur < mode) mode = cur; /* only inward */ +STK[cur] = SP; /* save stack */ +tsp = STK[mode]; /* get new stk */ +acc = ACC_MASK (mode); /* set new mode */ +if (Test (p2 = tsp - 1, WA, &sta) < 0) { /* probe stk */ + p1 = MM_WRITE | (sta & MM_EMASK); + ABORT ((sta & 4)? ABORT_TNV: ABORT_ACV); + } +if (Test (p2 = tsp - 12, WA, &sta) < 0) { + p1 = MM_WRITE | (sta & MM_EMASK); + ABORT ((sta & 4)? ABORT_TNV: ABORT_ACV); + } +Write (tsp - 12, SXTW (opnd[0]), L_LONG, WA); /* push argument */ +Write (tsp - 8, PC, L_LONG, WA); /* push PC */ +Write (tsp - 4, PSL | cc, L_LONG, WA); /* push PSL */ +SP = tsp - 12; /* set new stk */ +PSL = (mode << PSL_V_CUR) | (PSL & PSL_IPL) | /* set new PSL */ + (cur << PSL_V_PRV); /* IPL unchanged */ +last_chm = fault_PC; +JUMP (newpc & ~03); /* set new PC */ +return 0; /* cc = 0 */ +} + +/* REI - return from exception or interrupt + +The lengthiest part of the REI instruction is the validity checking of the PSL +popped off the stack. The new PSL is checked against the following eight rules: + +let tmp = new PSL popped off the stack +let PSL = current PSL + +Rule SRM formulation Comment +---- --------------- ------- + 1 tmp<25:24> GEQ PSL<25:24> tmp GEQ PSL + 2 tmp<26> LEQ PSL<26> tmp LEQ PSL + 3 tmp<26> = 1 => tmp<25:24> = 0 tmp = 1 => tmp = ker + 4 tmp<26> = 1 => tmp<20:16> > 0 tmp = 1 => tmp > 0 + 5 tmp<20:16> > 0 => tmp<25:24> = 0 tmp > 0 => tmp = ker + 6 tmp<25:24> LEQ tmp<23:22> tmp LEQ tmp + 7 tmp<20:16> LEQ PSL<20:16> tmp LEQ PSL + 8 tmp<31,29:28,21,15:8> = 0 tmp = 0 + 9 tmp<31> = 1 => tmp = 3, tmp = 3>, tmp = 0 +*/ + +int32 op_rei (int32 acc) +{ +int32 newpc = Read (SP, L_LONG, RA); +int32 newpsl = Read (SP + 4, L_LONG, RA); +int32 newcur = PSL_GETCUR (newpsl); +int32 oldcur = PSL_GETCUR (PSL); +int32 newipl, i; + +if ((newpsl & PSL_MBZ) || /* rule 8 */ + (newcur < oldcur)) RSVD_OPND_FAULT; /* rule 1 */ +if (newcur) { /* to esu, skip 2,4,7 */ + if ((newpsl & (PSL_IS | PSL_IPL)) || /* rules 3,5 */ + (newcur > PSL_GETPRV (newpsl))) /* rule 6 */ + RSVD_OPND_FAULT; /* end rei to esu */ + } +else { /* to k, skip 3,5,6 */ + newipl = PSL_GETIPL (newpsl); /* get new ipl */ + if ((newpsl & PSL_IS) && /* setting IS? */ + (((PSL & PSL_IS) == 0) || (newipl == 0))) /* test rules 2,4 */ + RSVD_OPND_FAULT; /* else skip 2,4 */ + if (newipl > PSL_GETIPL (PSL)) RSVD_OPND_FAULT; /* test rule 7 */ + } /* end if kernel */ +if (newpsl & PSL_CM) { /* setting cmode? */ + if (BadCmPSL (newpsl)) RSVD_OPND_FAULT; /* validate PSL */ + for (i = 0; i < 7; i++) R[i] = R[i] & WMASK; /* mask R0-R6, PC */ + newpc = newpc & WMASK; + } +SP = SP + 8; /* pop stack */ +if (PSL & PSL_IS) IS = SP; /* save stack */ +else STK[oldcur] = SP; +if (DEBUG_PRI (cpu_dev, LOG_CPU_R)) fprintf (sim_deb, + ">>REI: PC=%08x, PSL=%08x, SP=%08x, nPC=%08x, nPSL=%08x, nSP=%08x\n", + PC, PSL, SP - 8, newpc, newpsl, ((newpsl & IS)? IS: STK[newcur])); +PSL = cpu_psl_ipl_idle ((PSL & PSL_TP) | (newpsl & ~CC_MASK)); /* set PSL */ +if (PSL & PSL_IS) SP = IS; /* set new stack */ +else { + SP = STK[newcur]; /* if ~IS, chk AST */ + if (newcur >= ASTLVL) { + if (DEBUG_PRI (cpu_dev, LOG_CPU_R)) fprintf (sim_deb, + ">>REI: AST delivered\n"); + SISR = SISR | SISR_2; + } + } +JUMP (newpc); /* set new PC */ +return newpsl & CC_MASK; /* set new cc */ +} + +/* LDCPTX - load process context */ + +void op_ldpctx (int32 acc) +{ +int32 newpc, newpsl, pcbpa, t; + +if (PSL & PSL_CUR) RSVD_INST_FAULT; /* must be kernel */ +pcbpa = PCBB & PAMASK; /* phys address */ +KSP = ReadLP (pcbpa); /* restore stk ptrs */ +ESP = ReadLP (pcbpa + 4); +SSP = ReadLP (pcbpa + 8); +USP = ReadLP (pcbpa + 12); +R[0] = ReadLP (pcbpa + 16); /* restore registers */ +R[1] = ReadLP (pcbpa + 20); +R[2] = ReadLP (pcbpa + 24); +R[3] = ReadLP (pcbpa + 28); +R[4] = ReadLP (pcbpa + 32); +R[5] = ReadLP (pcbpa + 36); +R[6] = ReadLP (pcbpa + 40); +R[7] = ReadLP (pcbpa + 44); +R[8] = ReadLP (pcbpa + 48); +R[9] = ReadLP (pcbpa + 52); +R[10] = ReadLP (pcbpa + 56); +R[11] = ReadLP (pcbpa + 60); +R[12] = ReadLP (pcbpa + 64); +R[13] = ReadLP (pcbpa + 68); +newpc = ReadLP (pcbpa + 72); /* get PC, PSL */ +newpsl = ReadLP (pcbpa + 76); + +t = ReadLP (pcbpa + 80); +ML_PXBR_TEST (t); /* validate P0BR */ +P0BR = t & BR_MASK; /* restore P0BR */ +t = ReadLP (pcbpa + 84); +LP_MBZ84_TEST (t); /* test mbz */ +ML_LR_TEST (t & LR_MASK); /* validate P0LR */ +P0LR = t & LR_MASK; /* restore P0LR */ +t = (t >> 24) & AST_MASK; +LP_AST_TEST (t); /* validate AST */ +ASTLVL = t; /* restore AST */ +t = ReadLP (pcbpa + 88); +ML_PXBR_TEST (t + 0x800000); /* validate P1BR */ +P1BR = t & BR_MASK; /* restore P1BR */ +t = ReadLP (pcbpa + 92); +LP_MBZ92_TEST (t); /* test MBZ */ +ML_LR_TEST (t & LR_MASK); /* validate P1LR */ +P1LR = t & LR_MASK; /* restore P1LR */ +pme = (t >> 31) & 1; /* restore PME */ + +zap_tb (0); /* clear process TB */ +set_map_reg (); +if (DEBUG_PRI (cpu_dev, LOG_CPU_P)) fprintf (sim_deb, + ">>LDP: PC=%08x, PSL=%08x, SP=%08x, nPC=%08x, nPSL=%08x, nSP=%08x\n", + PC, PSL, SP, newpc, newpsl, KSP); +if (PSL & PSL_IS) IS = SP; /* if istk, */ +PSL = PSL & ~PSL_IS; /* switch to kstk */ +SP = KSP - 8; +Write (SP, newpc, L_LONG, WA); /* push PC, PSL */ +Write (SP + 4, newpsl, L_LONG, WA); +return; +} + +/* SVPCTX - save processor context */ + +void op_svpctx (int32 acc) +{ +int32 savpc, savpsl, pcbpa; + +if (PSL & PSL_CUR) RSVD_INST_FAULT; /* must be kernel */ +savpc = Read (SP, L_LONG, RA); /* pop PC, PSL */ +savpsl = Read (SP + 4, L_LONG, RA); +if (DEBUG_PRI (cpu_dev, LOG_CPU_P)) fprintf (sim_deb, + ">>SVP: PC=%08x, PSL=%08x, SP=%08x, oPC=%08x, oPSL=%08x\n", + PC, PSL, SP, savpc, savpsl); +if (PSL & PSL_IS) SP = SP + 8; /* int stack? */ +else { + KSP = SP + 8; /* pop kernel stack */ + SP = IS; /* switch to int stk */ + if ((PSL & PSL_IPL) == 0) /* make IPL > 0 */ + PSL = PSL | PSL_IPL1; + PSL = PSL | PSL_IS; /* set PSL */ + } +pcbpa = PCBB & PAMASK; +WriteLP (pcbpa, KSP); /* save stk ptrs */ +WriteLP (pcbpa + 4, ESP); +WriteLP (pcbpa + 8, SSP); +WriteLP (pcbpa + 12, USP); +WriteLP (pcbpa + 16, R[0]); /* save registers */ +WriteLP (pcbpa + 20, R[1]); +WriteLP (pcbpa + 24, R[2]); +WriteLP (pcbpa + 28, R[3]); +WriteLP (pcbpa + 32, R[4]); +WriteLP (pcbpa + 36, R[5]); +WriteLP (pcbpa + 40, R[6]); +WriteLP (pcbpa + 44, R[7]); +WriteLP (pcbpa + 48, R[8]); +WriteLP (pcbpa + 52, R[9]); +WriteLP (pcbpa + 56, R[10]); +WriteLP (pcbpa + 60, R[11]); +WriteLP (pcbpa + 64, R[12]); +WriteLP (pcbpa + 68, R[13]); +WriteLP (pcbpa + 72, savpc); /* save PC, PSL */ +WriteLP (pcbpa + 76, savpsl); +return; +} + +/* PROBER and PROBEW + + opnd[0] = mode + opnd[1] = length + opnd[2] = base address +*/ + +int32 op_probe (int32 *opnd, int32 rw) +{ +int32 mode = opnd[0] & PSL_M_MODE; /* mask mode */ +int32 length = opnd[1]; +int32 ba = opnd[2]; +int32 prv = PSL_GETPRV (PSL); +int32 acc, sta, sta1; + +if (prv > mode) mode = prv; /* maximize mode */ +acc = ACC_MASK (mode) << (rw? TLB_V_WACC: 0); /* set acc mask */ +Test (ba, acc, &sta); /* probe */ +switch (sta) { /* case on status */ + + case PR_PTNV: /* pte TNV */ + p1 = MM_PARAM (rw, PR_PTNV); + p2 = ba; + ABORT (ABORT_TNV); /* force TNV */ + + case PR_TNV: case PR_OK: /* TNV or ok */ + break; /* continue */ + + default: /* other */ + return CC_Z; /* lose */ + } + +Test (ba + length - 1, acc, &sta1); /* probe end addr */ +switch (sta1) { /* case on status */ + + case PR_PTNV: /* pte TNV */ + p1 = MM_PARAM (rw, PR_PTNV); + p2 = ba + length - 1; + ABORT (ABORT_TNV); /* force TNV */ + + case PR_TNV: case PR_OK: /* TNV or ok */ + break; /* win */ + + default: /* other */ + return CC_Z; /* lose */ + } + +return 0; +} + +/* MTPR - move to processor register + + opnd[0] = data + opnd[1] = register number +*/ + +int32 op_mtpr (int32 *opnd) +{ +int32 val = opnd[0]; +int32 prn = opnd[1]; +int32 cc; + +if (PSL & PSL_CUR) RSVD_INST_FAULT; /* must be kernel */ +if (prn > 63) RSVD_OPND_FAULT; /* reg# > 63? fault */ +CC_IIZZ_L (val); /* set cc's */ +switch (prn) { /* case on reg # */ + + case MT_KSP: /* KSP */ + if (PSL & PSL_IS) KSP = val; /* on IS? store KSP */ + else SP = val; /* else store SP */ + break; + + case MT_ESP: case MT_SSP: case MT_USP: /* ESP, SSP, USP */ + STK[prn] = val; /* store stack */ + break; + + case MT_IS: /* IS */ + if (PSL & PSL_IS) SP = val; /* on IS? store SP */ + else IS = val; /* else store IS */ + break; + + case MT_P0BR: /* P0BR */ + ML_PXBR_TEST (val); /* validate */ + P0BR = val & BR_MASK; /* lw aligned */ + zap_tb (0); /* clr proc TLB */ + set_map_reg (); + break; + + case MT_P0LR: /* P0LR */ + ML_LR_TEST (val & LR_MASK); /* validate */ + P0LR = val & LR_MASK; + zap_tb (0); /* clr proc TLB */ + set_map_reg (); + break; + + case MT_P1BR: /* P1BR */ + ML_PXBR_TEST (val + 0x800000); /* validate */ + P1BR = val & BR_MASK; /* lw aligned */ + zap_tb (0); /* clr proc TLB */ + set_map_reg (); + break; + + case MT_P1LR: /* P1LR */ + ML_LR_TEST (val & LR_MASK); /* validate */ + P1LR = val & LR_MASK; + zap_tb (0); /* clr proc TLB */ + set_map_reg (); + break; + + case MT_SBR: /* SBR */ + ML_SBR_TEST (val); /* validate */ + SBR = val & BR_MASK; /* lw aligned */ + zap_tb (1); /* clr entire TLB */ + set_map_reg (); + break; + + case MT_SLR: /* SLR */ + ML_LR_TEST (val & LR_MASK); /* validate */ + SLR = val & LR_MASK; + zap_tb (1); /* clr entire TLB */ + set_map_reg (); + break; + + case MT_SCBB: /* SCBB */ + ML_PA_TEST (val); /* validate */ + SCBB = val & BR_MASK; /* lw aligned */ + break; + + case MT_PCBB: /* PCBB */ + ML_PA_TEST (val); /* validate */ + PCBB = val & BR_MASK; /* lw aligned */ + break; + + case MT_IPL: /* IPL */ + PSL = (PSL & ~PSL_IPL) | ((val & PSL_M_IPL) << PSL_V_IPL); + break; + + case MT_ASTLVL: /* ASTLVL */ + if (val > AST_MAX) RSVD_OPND_FAULT; /* > 4? fault */ + ASTLVL = val; + break; + + case MT_SIRR: /* SIRR */ + if ((val > 0xF) || (val == 0)) RSVD_OPND_FAULT; + SISR = SISR | (1 << val); /* set bit in SISR */ + break; + + case MT_SISR: /* SISR */ + SISR = val & SISR_MASK; + break; + + case MT_MAPEN: /* MAPEN */ + mapen = val & 1; + case MT_TBIA: /* TBIA */ + zap_tb (1); /* clr entire TLB */ + break; + + case MT_TBIS: /* TBIS */ + zap_tb_ent (val); + break; + + case MT_TBCHK: /* TBCHK */ + if (chk_tb_ent (val)) cc = cc | CC_V; + break; + + case MT_PME: /* PME */ + pme = val & 1; + break; + + default: + WriteIPR (prn, val); /* others */ + break; + } + +return cc; +} + +int32 op_mfpr (int32 *opnd) +{ +int32 prn = opnd[0]; +int32 val; + +if (PSL & PSL_CUR) RSVD_INST_FAULT; /* must be kernel */ +if (prn > 63) RSVD_OPND_FAULT; /* reg# > 63? fault */ +switch (prn) { /* case on reg# */ + + case MT_KSP: /* KSP */ + val = (PSL & PSL_IS)? KSP: SP; /* return KSP or SP */ + break; + + case MT_ESP: case MT_SSP: case MT_USP: /* ESP, SSP, USP */ + val = STK[prn]; /* return stk ptr */ + break; + + case MT_IS: /* IS */ + val = (PSL & PSL_IS)? SP: IS; /* return SP or IS */ + break; + + case MT_P0BR: /* P0BR */ + val = P0BR; + break; + + case MT_P0LR: /* P0LR */ + val = P0LR; + break; + + case MT_P1BR: /* P1BR */ + val = P1BR; + break; + + case MT_P1LR: /* P1LR */ + val = P1LR; + break; + + case MT_SBR: /* SBR */ + val = SBR; + break; + + case MT_SLR: /* SLR */ + val = SLR; + break; + + case MT_SCBB: /* SCBB */ + val = SCBB; + break; + + case MT_PCBB: /* PCBB */ + val = PCBB; + break; + + case MT_IPL: /* IPL */ + val = PSL_GETIPL (PSL); + break; + + case MT_ASTLVL: /* ASTLVL */ + val = ASTLVL; + break; + + case MT_SISR: /* SISR */ + val = SISR & SISR_MASK; + break; + + case MT_MAPEN: /* MAPEN */ + val = mapen & 1; + break; + + case MT_PME: + val = pme & 1; + break; + + case MT_SIRR: + case MT_TBIA: + case MT_TBIS: + case MT_TBCHK: + RSVD_OPND_FAULT; /* write only */ + + default: /* others */ + val = ReadIPR (prn); /* read from SSC */ + break; + } + +return val; +} diff --git a/VAX/vax_defs.h b/VAX/vax_defs.h new file mode 100644 index 0000000..3b0b16f --- /dev/null +++ b/VAX/vax_defs.h @@ -0,0 +1,727 @@ +/* vax_defs.h: VAX architecture definitions file + + Copyright (c) 1998-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + The author gratefully acknowledges the help of Stephen Shirron, Antonio + Carlini, and Kevin Peterson in providing specifications for the Qbus VAX's + + 09-May-06 RMS Added system PTE ACV error code + 03-May-06 RMS Added EDITPC get/put cc's macros + 03-Nov-05 RMS Added 780 stop codes + 22-Jul-05 RMS Fixed warning from Solaris C (from Doug Gwyn) + 02-Sep-04 RMS Added octa specifier definitions + 30-Aug-04 RMS Added octa, h_floating instruction definitions + 24-Aug-04 RMS Added compatibility mode definitions + 18-Apr-04 RMS Added octa, fp, string definitions + 19-May-03 RMS Revised for new conditional compilation scheme + 14-Jul-02 RMS Added infinite loop message + 30-Apr-02 RMS Added CLR_TRAPS macro +*/ + +#ifndef _VAX_DEFS_H +#define _VAX_DEFS_H 0 + +#ifndef VM_VAX +#define VM_VAX 0 +#endif + +#include "sim_defs.h" +#include + +/* Stops and aborts */ + +#define STOP_HALT 1 /* halt */ +#define STOP_IBKPT 2 /* breakpoint */ +#define STOP_CHMFI 3 /* chg mode IS */ +#define STOP_ILLVEC 4 /* illegal vector */ +#define STOP_INIE 5 /* exc in intexc */ +#define STOP_PPTE 6 /* proc pte in Px */ +#define STOP_UIPL 7 /* undefined IPL */ +#define STOP_RQ 8 /* fatal RQ err */ +#define STOP_LOOP 9 /* infinite loop */ +#define STOP_SANITY 10 /* sanity timer exp */ +#define STOP_SWDN 11 /* software done (780) */ +#define STOP_BOOT 12 /* reboot (780) */ +#define STOP_UNKNOWN 13 /* unknown reason */ +#define STOP_UNKABO 14 /* unknown abort */ +#define ABORT_INTR -1 /* interrupt */ +#define ABORT_MCHK (-SCB_MCHK) /* machine check */ +#define ABORT_RESIN (-SCB_RESIN) /* rsvd instruction */ +#define ABORT_RESAD (-SCB_RESAD) /* rsvd addr mode */ +#define ABORT_RESOP (-SCB_RESOP) /* rsvd operand */ +#define ABORT_CMODE (-SCB_CMODE) /* comp mode fault */ +#define ABORT_ARITH (-SCB_ARITH) /* arithmetic trap */ +#define ABORT_ACV (-SCB_ACV) /* access violation */ +#define ABORT_TNV (-SCB_TNV) /* transl not vaid */ +#define ABORT(x) longjmp (save_env, (x)) /* abort */ +#define RSVD_INST_FAULT ABORT (ABORT_RESIN) +#define RSVD_ADDR_FAULT ABORT (ABORT_RESAD) +#define RSVD_OPND_FAULT ABORT (ABORT_RESOP) +#define FLT_OVFL_FAULT p1 = FLT_OVRFLO, ABORT (ABORT_ARITH) +#define FLT_DZRO_FAULT p1 = FLT_DIVZRO, ABORT (ABORT_ARITH) +#define FLT_UNFL_FAULT p1 = FLT_UNDFLO, ABORT (ABORT_ARITH) +#define CMODE_FAULT(cd) p1 = (cd), ABORT (ABORT_CMODE) +#define MACH_CHECK(cd) p1 = (cd), ABORT (ABORT_MCHK) + +/* Recovery queue */ + +#define RQ_RN 0xF /* register */ +#define RQ_V_LNT 4 /* length */ +#define RQ_M_LNT 0x7 /* 0,1,2,3,4 */ +#define RQ_DIR 0x800 /* 0 = -, 1 = + */ +#define RQ_REC(d,r) (((d) << RQ_V_LNT) | (r)) +#define RQ_GETRN(x) ((x) & RQ_RN) +#define RQ_GETLNT(x) (((x) >> RQ_V_LNT) & RQ_M_LNT) + +/* Address space */ + +#define VAMASK 0xFFFFFFFF /* virt addr mask */ +#define PAWIDTH 30 /* phys addr width */ +#define PASIZE (1 << PAWIDTH) /* phys addr size */ +#define PAMASK (PASIZE - 1) /* phys addr mask */ +#define IOPAGE (1 << (PAWIDTH - 1)) /* start of I/O page */ + +/* Architectural constants */ + +#define BMASK 0x000000FF /* byte */ +#define BSIGN 0x00000080 +#define WMASK 0x0000FFFF /* word */ +#define WSIGN 0x00008000 +#define LMASK 0xFFFFFFFF /* longword */ +#define LSIGN 0x80000000 +#define FPSIGN 0x00008000 /* floating point */ +#define L_BYTE 1 /* bytes per */ +#define L_WORD 2 /* data type */ +#define L_LONG 4 +#define L_QUAD 8 +#define L_OCTA 16 +#define NUM_INST 512 /* one byte+two byte */ +#define MAX_SPEC 6 /* max spec/instr */ + +/* Floating point formats */ + +#define FD_V_EXP 7 /* f/d exponent */ +#define FD_M_EXP 0xFF +#define FD_BIAS 0x80 /* f/d bias */ +#define FD_EXP (FD_M_EXP << FD_V_EXP) +#define FD_HB (1 << FD_V_EXP) /* f/d hidden bit */ +#define FD_GUARD (15 - FD_V_EXP) /* # guard bits */ +#define FD_GETEXP(x) (((x) >> FD_V_EXP) & FD_M_EXP) + +#define G_V_EXP 4 /* g exponent */ +#define G_M_EXP 0x7FF +#define G_BIAS 0x400 /* g bias */ +#define G_EXP (G_M_EXP << G_V_EXP) +#define G_HB (1 << G_V_EXP) /* g hidden bit */ +#define G_GUARD (15 - G_V_EXP) /* # guard bits */ +#define G_GETEXP(x) (((x) >> G_V_EXP) & G_M_EXP) + +#define H_V_EXP 0 /* h exponent */ +#define H_M_EXP 0x7FFF +#define H_BIAS 0x4000 /* h bias */ +#define H_EXP (H_M_EXP << H_V_EXP) +#define H_HB (1 << H_V_EXP) /* h hidden bit */ +#define H_GUARD (15 - H_V_EXP) /* # guard bits */ +#define H_GETEXP(x) (((x) >> H_V_EXP) & H_M_EXP) + +/* Memory management modes */ + +#define KERN 0 +#define EXEC 1 +#define SUPV 2 +#define USER 3 + +/* Register and stack aliases */ + +#define nAP 12 +#define nFP 13 +#define nSP 14 +#define nPC 15 +#define AP R[nAP] +#define FP R[nFP] +#define SP R[nSP] +#define PC R[nPC] +#define RGMASK 0xF +#define KSP STK[KERN] +#define ESP STK[EXEC] +#define SSP STK[SUPV] +#define USP STK[USER] +#define IS STK[4] + +/* PSL, PSW, and condition codes */ + +#define PSL_V_CM 31 /* compatibility mode */ +#define PSL_CM (1u << PSL_V_CM) +#define PSL_V_TP 30 /* trace pending */ +#define PSL_TP (1 << PSL_V_TP) +#define PSL_V_FPD 27 /* first part done */ +#define PSL_FPD (1 << PSL_V_FPD) +#define PSL_V_IS 26 /* interrupt stack */ +#define PSL_IS (1 << PSL_V_IS) +#define PSL_V_CUR 24 /* current mode */ +#define PSL_V_PRV 22 /* previous mode */ +#define PSL_M_MODE 0x3 /* mode mask */ +#define PSL_CUR (PSL_M_MODE << PSL_V_CUR) +#define PSL_PRV (PSL_M_MODE << PSL_V_PRV) +#define PSL_V_IPL 16 /* int priority lvl */ +#define PSL_M_IPL 0x1F +#define PSL_IPL (PSL_M_IPL << PSL_V_IPL) +#define PSL_IPL1 (0x01 << PSL_V_IPL) +#define PSL_IPL1F (0x1F << PSL_V_IPL) +#define PSL_MBZ (0x30200000 | PSW_MBZ) /* must be zero */ +#define PSW_MBZ 0xFF00 /* must be zero */ +#define PSW_DV 0x80 /* dec ovflo enable */ +#define PSW_FU 0x40 /* flt undflo enable */ +#define PSW_IV 0x20 /* int ovflo enable */ +#define PSW_T 0x10 /* trace enable */ +#define CC_N 0x08 /* negative */ +#define CC_Z 0x04 /* zero */ +#define CC_V 0x02 /* overflow */ +#define CC_C 0x01 /* carry */ +#define CC_MASK (CC_N | CC_Z | CC_V | CC_C) +#define PSL_GETCUR(x) (((x) >> PSL_V_CUR) & PSL_M_MODE) +#define PSL_GETPRV(x) (((x) >> PSL_V_PRV) & PSL_M_MODE) +#define PSL_GETIPL(x) (((x) >> PSL_V_IPL) & PSL_M_IPL) + +/* Software interrupt summary register */ + +#define SISR_MASK 0xFFFE +#define SISR_2 (1 << 2) + +/* AST register */ + +#define AST_MASK 7 +#define AST_MAX 4 + +/* Virtual address */ + +#define VA_N_OFF 9 /* offset size */ +#define VA_PAGSIZE (1u << VA_N_OFF) /* page size */ +#define VA_M_OFF ((1u << VA_N_OFF) - 1) /* offset mask */ +#define VA_V_VPN VA_N_OFF /* vpn start */ +#define VA_N_VPN (31 - VA_N_OFF) /* vpn size */ +#define VA_M_VPN ((1u << VA_N_VPN) - 1) /* vpn mask */ +#define VA_S0 (1u << 31) /* S0 space */ +#define VA_P1 (1u << 30) /* P1 space */ +#define VA_N_TBI 12 /* TB index size */ +#define VA_TBSIZE (1u << VA_N_TBI) /* TB size */ +#define VA_M_TBI ((1u << VA_N_TBI) - 1) /* TB index mask */ +#define VA_GETOFF(x) ((x) & VA_M_OFF) +#define VA_GETVPN(x) (((x) >> VA_V_VPN) & VA_M_VPN) +#define VA_GETTBI(x) ((x) & VA_M_TBI) + +/* PTE */ + +#define PTE_V_V 31 /* valid */ +#define PTE_V (1u << PTE_V_V) +#define PTE_V_ACC 27 /* access */ +#define PTE_M_ACC 0xF +#define PTE_ACC (PTE_M_ACC << PTE_V_ACC) +#define PTE_V_M 26 /* modified */ +#define PTE_M (1u << PTE_V_M) +#define PTE_GETACC(x) (((x) >> PTE_V_ACC) & PTE_M_ACC) + +/* TLB entry */ + +#define TLB_V_RACC 0 /* rd acc field */ +#define TLB_V_WACC 4 /* wr acc field */ +#define TLB_M_ACC 0xF +#define TLB_RACC (TLB_M_ACC << TLB_V_RACC) +#define TLB_WACC (TLB_M_ACC << TLB_V_WACC) +#define TLB_V_M 8 /* m bit */ +#define TLB_M (1u << TLB_V_M) +#define TLB_N_PFN (PAWIDTH - VA_N_OFF) /* ppfn size */ +#define TLB_M_PFN ((1u << TLB_N_PFN) - 1) /* ppfn mask */ +#define TLB_PFN (TLB_M_PFN << VA_V_VPN) + +/* Traps and interrupt requests */ + +#define TIR_V_IRQL 0 /* int request lvl */ +#define TIR_V_TRAP 5 /* trap requests */ +#define TIR_M_TRAP 07 +#define TIR_TRAP (TIR_M_TRAP << TIR_V_TRAP) +#define TRAP_INTOV (1 << TIR_V_TRAP) /* integer overflow */ +#define TRAP_DIVZRO (2 << TIR_V_TRAP) /* divide by zero */ +#define TRAP_FLTOVF (3 << TIR_V_TRAP) /* flt overflow */ +#define TRAP_FLTDIV (4 << TIR_V_TRAP) /* flt/dec div by zero */ +#define TRAP_FLTUND (5 << TIR_V_TRAP) /* flt underflow */ +#define TRAP_DECOVF (6 << TIR_V_TRAP) /* decimal overflow */ +#define TRAP_SUBSCR (7 << TIR_V_TRAP) /* subscript range */ +#define SET_TRAP(x) trpirq = (trpirq & PSL_M_IPL) | (x) +#define CLR_TRAPS trpirq = trpirq & ~TIR_TRAP +#define SET_IRQL trpirq = (trpirq & TIR_TRAP) | eval_int () +#define GET_TRAP(x) (((x) >> TIR_V_TRAP) & TIR_M_TRAP) +#define GET_IRQL(x) (((x) >> TIR_V_IRQL) & PSL_M_IPL) + +/* Floating point fault parameters */ + +#define FLT_OVRFLO 0x8 /* flt overflow */ +#define FLT_DIVZRO 0x9 /* flt div by zero */ +#define FLT_UNDFLO 0xA /* flt underflow */ + +/* Compatability mode fault parameters */ + +#define CMODE_RSVI 0x0 /* reserved instr */ +#define CMODE_BPT 0x1 /* BPT */ +#define CMODE_IOT 0x2 /* IOT */ +#define CMODE_EMT 0x3 /* EMT */ +#define CMODE_TRAP 0x4 /* TRAP */ +#define CMODE_ILLI 0x5 /* illegal instr */ +#define CMODE_ODD 0x6 /* odd address */ + +/* EDITPC suboperators */ + +#define EO_END 0x00 /* end */ +#define EO_END_FLOAT 0x01 /* end float */ +#define EO_CLR_SIGNIF 0x02 /* clear signif */ +#define EO_SET_SIGNIF 0x03 /* set signif */ +#define EO_STORE_SIGN 0x04 /* store sign */ +#define EO_LOAD_FILL 0x40 /* load fill */ +#define EO_LOAD_SIGN 0x41 /* load sign */ +#define EO_LOAD_PLUS 0x42 /* load sign if + */ +#define EO_LOAD_MINUS 0x43 /* load sign if - */ +#define EO_INSERT 0x44 /* insert */ +#define EO_BLANK_ZERO 0x45 /* blank zero */ +#define EO_REPL_SIGN 0x46 /* replace sign */ +#define EO_ADJUST_LNT 0x47 /* adjust length */ +#define EO_FILL 0x80 /* fill */ +#define EO_MOVE 0x90 /* move */ +#define EO_FLOAT 0xA0 /* float */ +#define EO_RPT_MASK 0x0F /* rpt mask */ +#define EO_RPT_FLAG 0x80 /* rpt flag */ + +/* EDITPC R2 packup parameters */ + +#define ED_V_CC 16 /* condition codes */ +#define ED_M_CC 0xFF +#define ED_CC (ED_M_CC << ED_V_CC) +#define ED_V_SIGN 8 /* sign */ +#define ED_M_SIGN 0xFF +#define ED_SIGN (ED_M_SIGN << ED_V_SIGN) +#define ED_V_FILL 0 /* fill */ +#define ED_M_FILL 0xFF +#define ED_FILL (ED_M_FILL << ED_V_FILL) +#define ED_GETCC(x) (((x) >> ED_V_CC) & CC_MASK) +#define ED_GETSIGN(x) (((x) >> ED_V_SIGN) & ED_M_SIGN) +#define ED_GETFILL(x) (((x) >> ED_V_FILL) & ED_M_FILL) +#define ED_PUTCC(r,x) (((r) & ~ED_CC) | (((x) << ED_V_CC) & ED_CC)) +#define ED_PUTSIGN(r,x) (((r) & ~ED_SIGN) | (((x) << ED_V_SIGN) & ED_SIGN)) +#define ED_PUTFILL(r,x) (((r) & ~ED_FILL) | (((x) << ED_V_FILL) & ED_FILL)) + +/* SCB offsets */ + +#define SCB_MCHK 0x04 /* machine chk */ +#define SCB_KSNV 0x08 /* ker stk invalid */ +#define SCB_PWRFL 0x0C /* power fail */ +#define SCB_RESIN 0x10 /* rsvd/priv instr */ +#define SCB_XFC 0x14 /* XFC instr */ +#define SCB_RESOP 0x18 /* rsvd operand */ +#define SCB_RESAD 0x1C /* rsvd addr mode */ +#define SCB_ACV 0x20 /* ACV */ +#define SCB_TNV 0x24 /* TNV */ +#define SCB_TP 0x28 /* trace pending */ +#define SCB_BPT 0x2C /* BPT instr */ +#define SCB_CMODE 0x30 /* comp mode fault */ +#define SCB_ARITH 0x34 /* arith fault */ +#define SCB_CHMK 0x40 /* CHMK */ +#define SCB_CHME 0x44 /* CHME */ +#define SCB_CHMS 0x48 /* CHMS */ +#define SCB_CHMU 0x4C /* CHMU */ +#define SCB_CRDERR 0x54 /* CRD err intr */ +#define SCB_MEMERR 0x60 /* mem err intr */ +#define SCB_IPLSOFT 0x80 /* software intr */ +#define SCB_INTTIM 0xC0 /* timer intr */ +#define SCB_EMULATE 0xC8 /* emulation */ +#define SCB_EMULFPD 0xCC /* emulation, FPD */ +#define SCB_CSI 0xF0 /* constor input */ +#define SCB_CSO 0xF4 /* constor output */ +#define SCB_TTI 0xF8 /* console input */ +#define SCB_TTO 0xFC /* console output */ +#define SCB_INTR 0x100 /* hardware intr */ + +#define IPL_HLTPIN 0x1F /* halt pin IPL */ +#define IPL_MEMERR 0x1D /* mem err IPL */ +#define IPL_CRDERR 0x1A /* CRD err IPL */ + +/* Interrupt and exception types */ + +#define IE_SVE -1 /* severe exception */ +#define IE_EXC 0 /* normal exception */ +#define IE_INT 1 /* interrupt */ + +/* Decode ROM: opcode entry */ + +#define DR_F 0x80 /* FPD ok flag */ +#define DR_NSPMASK 0x07 /* #specifiers */ +#define DR_V_USPMASK 4 +#define DR_M_USPMASK 0x70 /* #spec, sym_ */ +#define DR_GETNSP(x) ((x) & DR_NSPMASK) +#define DR_GETUSP(x) (((x) >> DR_V_USPMASK) & DR_M_USPMASK) + +/* Decode ROM: specifier entry */ + +#define DR_ACMASK 0x300 /* type */ +#define DR_SPFLAG 0x008 /* special decode */ +#define DR_LNMASK 0x007 /* length mask */ +#define DR_LNT(x) (1 << (x & DR_LNMASK)) /* disp to lnt */ + +/* Decode ROM: length */ + +#define DR_BYTE 0x000 /* byte */ +#define DR_WORD 0x001 /* word */ +#define DR_LONG 0x002 /* long */ +#define DR_QUAD 0x003 /* quad */ +#define DR_OCTA 0x004 /* octa */ + +/* Decode ROM: operand type */ + +#define SH0 0x000 /* short literal */ +#define SH1 0x010 +#define SH2 0x020 +#define SH3 0x030 +#define IDX 0x040 /* indexed */ +#define GRN 0x050 /* register */ +#define RGD 0x060 /* register def */ +#define ADC 0x070 /* autodecrement */ +#define AIN 0x080 /* autoincrement */ +#define AID 0x090 /* autoinc def */ +#define BDP 0x0A0 /* byte disp */ +#define BDD 0x0B0 /* byte disp def */ +#define WDP 0x0C0 /* word disp */ +#define WDD 0x0D0 /* word disp def */ +#define LDP 0x0E0 /* long disp */ +#define LDD 0x0F0 /* long disp def */ + +/* Decode ROM: access type */ + +#define DR_R 0x000 /* read */ +#define DR_M 0x100 /* modify */ +#define DR_A 0x200 /* address */ +#define DR_W 0x300 /* write */ + +/* Decode ROM: access type and length */ + +#define RB (DR_R|DR_BYTE) +#define RW (DR_R|DR_WORD) +#define RL (DR_R|DR_LONG) +#define RQ (DR_R|DR_QUAD) +#define RO (DR_R|DR_OCTA) +#define MB (DR_M|DR_BYTE) +#define MW (DR_M|DR_WORD) +#define ML (DR_M|DR_LONG) +#define MQ (DR_M|DR_QUAD) +#define MO (DR_M|DR_OCTA) +#define AB (DR_A|DR_BYTE) +#define AW (DR_A|DR_WORD) +#define AL (DR_A|DR_LONG) +#define AQ (DR_A|DR_QUAD) +#define AO (DR_A|DR_OCTA) +#define WB (DR_W|DR_BYTE) +#define WW (DR_W|DR_WORD) +#define WL (DR_W|DR_LONG) +#define WQ (DR_W|DR_QUAD) +#define WO (DR_W|DR_OCTA) + +/* Special dispatches. + + vb = variable bit field, treated as wb except for register + rf = f_floating, treated as rl except for short literal + rd = d_floating, treated as rq except for short literal + rg = g_floating, treated as rq except for short literal + rh = h_floating, treated as ro except for short literal + bb = branch byte displacement + bw = branch word displacement + + Length field must be correct +*/ + +#define VB (DR_SPFLAG|WB) /* .vb */ +#define RF (DR_SPFLAG|RL) /* .rf */ +#define RD (DR_SPFLAG|RQ) /* .rd */ +#define RG (DR_SPFLAG|MQ) /* .rg */ +#define RH (DR_SPFLAG|RO) /* .rh */ +#define BB (DR_SPFLAG|WB|6) /* byte branch */ +#define BW (DR_SPFLAG|WB|7) /* word branch */ + +/* Probe results and memory management fault codes */ + +#define PR_ACV 0 /* ACV */ +#define PR_LNV 1 /* length viol */ +#define PR_PACV 2 /* pte ACV (780) */ +#define PR_PLNV 3 /* pte len viol */ +#define PR_TNV 4 /* TNV */ +/* #define PR_TB 5 /* impossible */ +#define PR_PTNV 6 /* pte TNV */ +#define PR_OK 7 /* ok */ +#define MM_PARAM(w,p) (((w)? 4: 0) | ((p) & 3)) /* fault param */ + +/* Memory management errors */ + +#define MM_WRITE 4 /* write */ +#define MM_EMASK 3 /* against probe */ + +/* Privileged registers */ + +#define MT_KSP 0 +#define MT_ESP 1 +#define MT_SSP 2 +#define MT_USP 3 +#define MT_IS 4 +#define MT_P0BR 8 +#define MT_P0LR 9 +#define MT_P1BR 10 +#define MT_P1LR 11 +#define MT_SBR 12 +#define MT_SLR 13 +#define MT_PCBB 16 +#define MT_SCBB 17 +#define MT_IPL 18 +#define MT_ASTLVL 19 +#define MT_SIRR 20 +#define MT_SISR 21 +#define MT_ICCS 24 +#define MT_NICR 25 +#define MT_ICR 26 +#define MT_TODR 27 +#define MT_CSRS 28 +#define MT_CSRD 29 +#define MT_CSTS 30 +#define MT_CSTD 31 +#define MT_RXCS 32 +#define MT_RXDB 33 +#define MT_TXCS 34 +#define MT_TXDB 35 +#define MT_MAPEN 56 +#define MT_TBIA 57 +#define MT_TBIS 58 +#define MT_PME 61 +#define MT_SID 62 +#define MT_TBCHK 63 + +#define BR_MASK 0xFFFFFFFC +#define LR_MASK 0x003FFFFF + +/* Opcodes */ + +enum opcodes { + HALT, NOP, REI, BPT, RET, RSB, LDPCTX, SVPCTX, + CVTPS, CVTSP, INDEX, CRC, PROBER, PROBEW, INSQUE, REMQUE, + BSBB, BRB, BNEQ, BEQL, BGTR, BLEQ, JSB, JMP, + BGEQ, BLSS, BGTRU, BLEQU, BVC, BVS, BGEQU, BLSSU, + ADDP4, ADDP6, SUBP4, SUBP6, CVTPT, MULP, CVTTP, DIVP, + MOVC3, CMPC3, SCANC, SPANC, MOVC5, CMPC5, MOVTC, MOVTUC, + BSBW, BRW, CVTWL, CVTWB, MOVP, CMPP3, CVTPL, CMPP4, + EDITPC, MATCHC, LOCC, SKPC, MOVZWL, ACBW, MOVAW, PUSHAW, + ADDF2, ADDF3, SUBF2, SUBF3, MULF2, MULF3, DIVF2, DIVF3, + CVTFB, CVTFW, CVTFL, CVTRFL, CVTBF, CVTWF, CVTLF, ACBF, + MOVF, CMPF, MNEGF, TSTF, EMODF, POLYF, CVTFD, + ADAWI = 0x58, INSQHI = 0x5C, INSQTI, REMQHI, REMQTI, + ADDD2, ADDD3, SUBD2, SUBD3, MULD2, MULD3, DIVD2, DIVD3, + CVTDB, CVTDW, CVTDL, CVTRDL, CVTBD, CVTWD, CVTLD, ACBD, + MOVD, CMPD, MNEGD, TSTD, EMODD, POLYD, CVTDF, + ASHL = 0x78, ASHQ, EMUL, EDIV, CLRQ, MOVQ, MOVAQ, PUSHAQ, + ADDB2, ADDB3, SUBB2, SUBB3, MULB2, MULB3, DIVB2, DIVB3, + BISB2, BISB3, BICB2, BICB3, XORB2, XORB3, MNEGB, CASEB, + MOVB, CMPB, MCOMB, BITB, CLRB, TSTB, INCB, DECB, + CVTBL, CVTBW, MOVZBL, MOVZBW, ROTL, ACBB, MOVAB, PUSHAB, + ADDW2, ADDW3, SUBW2, SUBW3, MULW2, MULW3, DIVW2, DIVW3, + BISW2, BISW3, BICW2, BICW3, XORW2, XORW3, MNEGW, CASEW, + MOVW, CMPW, MCOMW, BITW, CLRW, TSTW, INCW, DECW, + BISPSW, BICPSW, POPR, PUSHR, CHMK, CHME, CHMS, CHMU, + ADDL2, ADDL3, SUBL2, SUBL3, MULL2, MULL3, DIVL2, DIVL3, + BISL2, BISL3, BICL2, BICL3, XORL2, XORL3, MNEGL, CASEL, + MOVL, CMPL, MCOML, BITL, CLRL, TSTL, INCL, DECL, + ADWC, SBWC, MTPR, MFPR, MOVPSL, PUSHL, MOVAL, PUSHAL, + BBS, BBC, BBSS, BBCS, BBSC, BBCC, BBSSI, BBCCI, + BLBS, BLBC, FFS, FFC, CMPV, CMPZV, EXTV, EXTZV, + INSV, ACBL, AOBLSS, AOBLEQ, SOBGEQ, SOBGTR, CVTLB, CVTLW, + ASHP, CVTLP, CALLG, CALLS, XFC, CVTDH = 0x132, CVTGF = 0x133, + ADDG2 = 0x140, ADDG3, SUBG2, SUBG3, MULG2, MULG3, DIVG2, DIVG3, + CVTGB, CVTGW, CVTGL, CVTRGL, CVTBG, CVTWG, CVTLG, ACBG, + MOVG, CMPG, MNEGG, TSTG, EMODG, POLYG, CVTGH, + ADDH2 = 0x160, ADDH3, SUBH2, SUBH3, MULH2, MULH3, DIVH2, DIVH3, + CVTHB, CVTHW, CVTHL, CVTRHL, CVTBH, CVTWH, CVTLH, ACBH, + MOVH, CMPH, MNEGH, TSTH, EMODH, POLYH, CVTHG, + CLRO = 0x17C, MOVO, MOVAO, PUSHAO, + CVTFH = 0x198, CVTFG = 0x199, + CVTHF = 0x1F6, CVTHD = 0x1F7 }; + +/* Repeated operations */ + +#define SXTB(x) (((x) & BSIGN)? ((x) | ~BMASK): ((x) & BMASK)) +#define SXTW(x) (((x) & WSIGN)? ((x) | ~WMASK): ((x) & WMASK)) +#define SXTBW(x) (((x) & BSIGN)? ((x) | (WMASK - BMASK)): ((x) & BMASK)) +#define SXTL(x) (((x) & LSIGN)? ((x) | ~LMASK): ((x) & LMASK)) +#define INTOV if (PSL & PSW_IV) SET_TRAP (TRAP_INTOV) +#define V_INTOV cc = cc | CC_V; INTOV +#define NEG(x) ((~(x) + 1) & LMASK) + +/* Istream access */ + +#define PCQ_SIZE 64 /* must be 2**n */ +#define PCQ_MASK (PCQ_SIZE - 1) +#define PCQ_ENTRY pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = fault_PC +#define GET_ISTR(d,l) d = get_istr (l, acc) +#define BRANCHB(d) PCQ_ENTRY, PC = PC + SXTB (d), FLUSH_ISTR +#define BRANCHW(d) PCQ_ENTRY, PC = PC + SXTW (d), FLUSH_ISTR +#define JUMP(d) PCQ_ENTRY, PC = (d), FLUSH_ISTR +#define CMODE_JUMP(d) PCQ_ENTRY, PC = (d) +#define SETPC(d) PC = (d), FLUSH_ISTR +#define FLUSH_ISTR ibcnt = 0, ppc = -1 + +/* Character string instructions */ + +#define STR_V_DPC 24 /* delta PC */ +#define STR_M_DPC 0xFF +#define STR_V_CHR 16 /* char argument */ +#define STR_M_CHR 0xFF +#define STR_LNMASK 0xFFFF /* string length */ +#define STR_GETDPC(x) (((x) >> STR_V_DPC) & STR_M_DPC) +#define STR_GETCHR(x) (((x) >> STR_V_CHR) & STR_M_CHR) +#define STR_PACK(m,x) ((((PC - fault_PC) & STR_M_DPC) << STR_V_DPC) | \ + (((m) & STR_M_CHR) << STR_V_CHR) | ((x) & STR_LNMASK)) + +/* Read and write */ + +#define RA (acc) +#define WA ((acc) << TLB_V_WACC) +#define ACC_MASK(x) (1 << (x)) +#define TLB_ACCR(x) (ACC_MASK (x) << TLB_V_RACC) +#define TLB_ACCW(x) (ACC_MASK (x) << TLB_V_WACC) +#define REF_V 0 +#define REF_P 1 + +/* Condition code macros */ + +#define CC_ZZ1P cc = CC_Z | (cc & CC_C) + +#define CC_IIZZ_B(r) \ + if ((r) & BSIGN) cc = CC_N; \ + else if ((r) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_W(r) \ + if ((r) & WSIGN) cc = CC_N; \ + else if ((r) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_L(r) \ + if ((r) & LSIGN) cc = CC_N; \ + else if ((r) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_Q(rl,rh) \ + if ((rh) & LSIGN) cc = CC_N; \ + else if (((rl) | (rh)) == 0) cc = CC_Z; \ + else cc = 0 +#define CC_IIZZ_FP CC_IIZZ_W + +#define CC_IIZP_B(r) \ + if ((r) & BSIGN) cc = CC_N | (cc & CC_C); \ + else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_W(r) \ + if ((r) & WSIGN) cc = CC_N | (cc & CC_C); \ + else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_L(r) \ + if ((r) & LSIGN) cc = CC_N | (cc & CC_C); \ + else if ((r) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_Q(rl,rh) \ + if ((rh) & LSIGN) cc = CC_N | (cc & CC_C); \ + else if (((rl) | (rh)) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_O(rl,rm2,rm1,rh) \ + if ((rh) & LSIGN) cc = CC_N | (cc & CC_C); \ + else if (((rl) | (rm2) | (rm1) | (rh)) == 0) cc = CC_Z | (cc & CC_C); \ + else cc = cc & CC_C +#define CC_IIZP_FP CC_IIZP_W + +#define V_ADD_B(r,s1,s2) \ + if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & BSIGN) { V_INTOV; } +#define V_ADD_W(r,s1,s2) \ + if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & WSIGN) { V_INTOV; } +#define V_ADD_L(r,s1,s2) \ + if (((~(s1) ^ (s2)) & ((s1) ^ (r))) & LSIGN) { V_INTOV; } +#define C_ADD(r,s1,s2) \ + if (((uint32) r) < ((uint32) s2)) cc = cc | CC_C + +#define CC_ADD_B(r,s1,s2) \ + CC_IIZZ_B (r); \ + V_ADD_B (r, s1, s2); \ + C_ADD (r, s1, s2) +#define CC_ADD_W(r,s1,s2) \ + CC_IIZZ_W (r); \ + V_ADD_W (r, s1, s2); \ + C_ADD (r, s1, s2) +#define CC_ADD_L(r,s1,s2) \ + CC_IIZZ_L (r); \ + V_ADD_L (r, s1, s2); \ + C_ADD (r, s1, s2) + +#define V_SUB_B(r,s1,s2) \ + if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & BSIGN) { V_INTOV; } +#define V_SUB_W(r,s1,s2) \ + if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & WSIGN) { V_INTOV; } +#define V_SUB_L(r,s1,s2) \ + if ((((s1) ^ (s2)) & (~(s1) ^ (r))) & LSIGN) { V_INTOV; } +#define C_SUB(r,s1,s2) \ + if (((uint32) s2) < ((uint32) s1)) cc = cc | CC_C + +#define CC_SUB_B(r,s1,s2) \ + CC_IIZZ_B (r); \ + V_SUB_B (r, s1, s2); \ + C_SUB (r, s1, s2) +#define CC_SUB_W(r,s1,s2) \ + CC_IIZZ_W (r); \ + V_SUB_W (r, s1, s2); \ + C_SUB (r, s1, s2) +#define CC_SUB_L(r,s1,s2) \ + CC_IIZZ_L (r); \ + V_SUB_L (r, s1, s2); \ + C_SUB (r, s1, s2) + +#define CC_CMP_B(s1,s2) \ + if (SXTB (s1) < SXTB (s2)) cc = CC_N; \ + else if ((s1) == (s2)) cc = CC_Z; \ + else cc = 0; \ + if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C +#define CC_CMP_W(s1,s2) \ + if (SXTW (s1) < SXTW (s2)) cc = CC_N; \ + else if ((s1) == (s2)) cc = CC_Z; \ + else cc = 0; \ + if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C +#define CC_CMP_L(s1,s2) \ + if ((s1) < (s2)) cc = CC_N; \ + else if ((s1) == (s2)) cc = CC_Z; \ + else cc = 0; \ + if (((uint32) s1) < ((uint32) s2)) cc = cc | CC_C + +/* Model dependent definitions */ + +#if defined (VAX_780) +#include "vax780_defs.h" +#else +#include "vaxmod_defs.h" +#endif + +#endif /* _VAX_DEFS_H */ diff --git a/VAX/vax_fpa.c b/VAX/vax_fpa.c new file mode 100644 index 0000000..5dea5ad --- /dev/null +++ b/VAX/vax_fpa.c @@ -0,0 +1,1463 @@ +/* vax_fpa.c - VAX f_, d_, g_floating instructions + + Copyright (c) 1998-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 28-May-08 RMS Inlined physical memory routines + 16-May-06 RMS Fixed bug in 32b floating multiply routine + Fixed bug in 64b extended modulus routine + 03-May-06 RMS Fixed POLYD, POLYG to clear R4, R5 + Fixed POLYD, POLYG to set R3 correctly + Fixed POLYD, POLYG to not exit prematurely if arg = 0 + Fixed POLYD, POLYG to do full 64b multiply + Fixed POLYF, POLYD, POLYG to remove truncation on add + Fixed POLYF, POLYD, POLYG to mask mul reslt to 31b/63b/63b + Fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYF, POLYD, POLYG + (all reported by Tim Stark) + 27-Sep-05 RMS Fixed bug in 32b structure definitions (from Jason Stevens) + 30-Sep-04 RMS Comment and formating changes based on vax_octa.c + 18-Apr-04 RMS Moved format definitions to vax_defs.h + 19-Jun-03 RMS Simplified add algorithm + 16-May-03 RMS Fixed bug in floating to integer convert overflow + Fixed multiple bugs in EMODx + Integrated 32b only code + 05-Jul-02 RMS Changed internal routine names for C library conflict + 17-Apr-02 RMS Fixed bug in EDIV zero quotient + + This module contains the instruction simulators for + + - 64 bit arithmetic (ASHQ, EMUL, EDIV) + - single precision floating point + - double precision floating point, D and G format +*/ + +#include "vax_defs.h" +#include + +extern int32 R[16]; +extern int32 PSL; +extern int32 p1; +extern jmp_buf save_env; + +#if defined (USE_INT64) + +#define M64 0xFFFFFFFFFFFFFFFF /* 64b */ +#define FD_FRACW (0xFFFF & ~(FD_EXP | FPSIGN)) +#define FD_FRACL (FD_FRACW | 0xFFFF0000) /* f/d fraction */ +#define G_FRACW (0xFFFF & ~(G_EXP | FPSIGN)) +#define G_FRACL (G_FRACW | 0xFFFF0000) /* g fraction */ +#define UNSCRAM(h,l) (((((t_uint64) (h)) << 48) & 0xFFFF000000000000) | \ + ((((t_uint64) (h)) << 16) & 0x0000FFFF00000000) | \ + ((((t_uint64) (l)) << 16) & 0x00000000FFFF0000) | \ + ((((t_uint64) (l)) >> 16) & 0x000000000000FFFF)) +#define CONCAT(h,l) ((((t_uint64) (h)) << 32) | ((uint32) (l))) + +typedef struct { + int32 sign; + int32 exp; + t_uint64 frac; + } UFP; + +#define UF_NM 0x8000000000000000 /* normalized */ +#define UF_FRND 0x0000008000000000 /* F round */ +#define UF_DRND 0x0000000000000080 /* D round */ +#define UF_GRND 0x0000000000000400 /* G round */ +#define UF_V_NM 63 +#define UF_V_FDHI 40 +#define UF_V_FDLO (UF_V_FDHI - 32) +#define UF_V_GHI 43 +#define UF_V_GLO (UF_V_GHI - 32) +#define UF_GETFDHI(x) (int32) ((((x) >> (16 + UF_V_FDHI)) & FD_FRACW) | \ + (((x) >> (UF_V_FDHI - 16)) & ~0xFFFF)) +#define UF_GETFDLO(x) (int32) ((((x) >> (16 + UF_V_FDLO)) & 0xFFFF) | \ + (((x) << (16 - UF_V_FDLO)) & ~0xFFFF)) +#define UF_GETGHI(x) (int32) ((((x) >> (16 + UF_V_GHI)) & G_FRACW) | \ + (((x) >> (UF_V_GHI - 16)) & ~0xFFFF)) +#define UF_GETGLO(x) (int32) ((((x) >> (16 + UF_V_GLO)) & 0xFFFF) | \ + (((x) << (16 - UF_V_GLO)) & ~0xFFFF)) + +void unpackf (int32 hi, UFP *a); +void unpackd (int32 hi, int32 lo, UFP *a); +void unpackg (int32 hi, int32 lo, UFP *a); +void norm (UFP *a); +int32 rpackfd (UFP *a, int32 *rh); +int32 rpackg (UFP *a, int32 *rh); +void vax_fadd (UFP *a, UFP *b); +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo); +void vax_fdiv (UFP *b, UFP *a, int32 prec, int32 bias); +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg); + +/* Quadword arithmetic shift + + opnd[0] = shift count (cnt.rb) + opnd[1:2] = source (src.rq) + opnd[3:4] = destination (dst.wq) +*/ + +int32 op_ashq (int32 *opnd, int32 *rh, int32 *flg) +{ +t_int64 src, r; +int32 sc = opnd[0]; + +src = CONCAT (opnd[2], opnd[1]); /* build src */ +if (sc & BSIGN) { /* right shift? */ + *flg = 0; /* no ovflo */ + sc = 0x100 - sc; /* |shift| */ + if (sc > 63) r = (opnd[2] & LSIGN)? -1: 0; /* sc > 63? */ + else r = src >> sc; + } +else { + if (sc > 63) { /* left shift */ + r = 0; /* sc > 63? */ + *flg = (src != 0); /* ovflo test */ + } + else { + r = src << sc; /* do shift */ + *flg = (src != (r >> sc)); /* ovflo test */ + } + } +*rh = (int32) ((r >> 32) & LMASK); /* hi result */ +return ((int32) (r & LMASK)); /* lo result */ +} + +/* Extended multiply subroutine */ + +int32 op_emul (int32 mpy, int32 mpc, int32 *rh) +{ +t_int64 lmpy = mpy; +t_int64 lmpc = mpc; + +lmpy = lmpy * lmpc; +*rh = (int32) ((lmpy >> 32) & LMASK); +return ((int32) (lmpy & LMASK)); +} + +/* Extended divide + + opnd[0] = divisor (non-zero) + opnd[1:2] = dividend +*/ + +int32 op_ediv (int32 *opnd, int32 *rh, int32 *flg) +{ +t_int64 ldvd, ldvr; +int32 quo, rem; + +*flg = CC_V; /* assume error */ +*rh = 0; +ldvr = ((opnd[0] & LSIGN)? -opnd[0]: opnd[0]) & LMASK; /* |divisor| */ +ldvd = CONCAT (opnd[2], opnd[1]); /* 64b dividend */ +if (opnd[2] & LSIGN) ldvd = -ldvd; /* |dividend| */ +if (((ldvd >> 32) & LMASK) >= ldvr) return opnd[1]; /* divide work? */ +quo = (int32) (ldvd / ldvr); /* do divide */ +rem = (int32) (ldvd % ldvr); +if ((opnd[0] ^ opnd[2]) & LSIGN) { /* result -? */ + quo = -quo; /* negate */ + if (quo && ((quo & LSIGN) == 0)) return opnd[1]; /* right sign? */ + } +else if (quo & LSIGN) return opnd[1]; +if (opnd[2] & LSIGN) rem = -rem; /* sign of rem */ +*flg = 0; /* no overflow */ +*rh = rem & LMASK; /* set rem */ +return (quo & LMASK); /* return quo */ +} + +/* Compare floating */ + +int32 op_cmpfd (int32 h1, int32 l1, int32 h2, int32 l2) +{ +t_uint64 n1, n2; + +if ((h1 & FD_EXP) == 0) { + if (h1 & FPSIGN) RSVD_OPND_FAULT; + h1 = l1 = 0; + } +if ((h2 & FD_EXP) == 0) { + if (h2 & FPSIGN) RSVD_OPND_FAULT; + h2 = l2 = 0; + } +if ((h1 ^ h2) & FPSIGN) return ((h1 & FPSIGN)? CC_N: 0); +n1 = UNSCRAM (h1, l1); +n2 = UNSCRAM (h2, l2); +if (n1 == n2) return CC_Z; +return (((n1 < n2) ^ ((h1 & FPSIGN) != 0))? CC_N: 0); +} + +int32 op_cmpg (int32 h1, int32 l1, int32 h2, int32 l2) +{ +t_uint64 n1, n2; + +if ((h1 & G_EXP) == 0) { + if (h1 & FPSIGN) RSVD_OPND_FAULT; + h1 = l1 = 0; + } +if ((h2 & G_EXP) == 0) { + if (h2 & FPSIGN) RSVD_OPND_FAULT; + h2 = l2 = 0; + } +if ((h1 ^ h2) & FPSIGN) return ((h1 & FPSIGN)? CC_N: 0); +n1 = UNSCRAM (h1, l1); +n2 = UNSCRAM (h2, l2); +if (n1 == n2) return CC_Z; +return (((n1 < n2) ^ ((h1 & FPSIGN) != 0))? CC_N: 0); +} + +/* Integer to floating convert */ + +int32 op_cvtifdg (int32 val, int32 *rh, int32 opc) +{ +UFP a; + +if (val == 0) { + if (rh) *rh = 0; + return 0; + } +if (val < 0) { + a.sign = FPSIGN; + val = - val; + } +else a.sign = 0; +a.exp = 32 + ((opc & 0x100)? G_BIAS: FD_BIAS); +a.frac = ((t_uint64) val) << (UF_V_NM - 31); +norm (&a); +if (opc & 0x100) return rpackg (&a, rh); +return rpackfd (&a, rh); +} + +/* Floating to integer convert */ + +int32 op_cvtfdgi (int32 *opnd, int32 *flg, int32 opc) +{ +UFP a; +int32 lnt = opc & 03; +int32 ubexp; +static t_uint64 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; + +*flg = 0; +if (opc & 0x100) { + unpackg (opnd[0], opnd[1], &a); + ubexp = a.exp - G_BIAS; + } +else { + if (opc & 0x20) unpackd (opnd[0], opnd[1], &a); + else unpackf (opnd[0], &a); + ubexp = a.exp - FD_BIAS; + } +if ((a.exp == 0) || (ubexp < 0)) return 0; +if (ubexp <= UF_V_NM) { + a.frac = a.frac >> (UF_V_NM - ubexp); /* leave rnd bit */ + if ((opc & 03) == 03) a.frac = a.frac + 1; /* if CVTR, round */ + a.frac = a.frac >> 1; /* now justified */ + if (a.frac > (maxv[lnt] + (a.sign? 1: 0))) *flg = CC_V; + } +else { + *flg = CC_V; /* set overflow */ + if (ubexp > (UF_V_NM + 32)) return 0; + a.frac = a.frac << (ubexp - UF_V_NM - 1); /* no rnd bit */ + } +return ((int32) ((a.sign? (a.frac ^ LMASK) + 1: a.frac) & LMASK)); +} + +/* Extended modularize + + One of three floating point instructions dropped from the architecture, + EMOD presents two sets of complications. First, it requires an extended + fraction multiply, with precise (and unusual) truncation conditions. + Second, it has two write operands, a dubious distinction it shares + with EDIV. +*/ + +int32 op_emodf (int32 *opnd, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* unpack operands */ +unpackf (opnd[2], &b); +a.frac = a.frac | (((t_uint64) opnd[1]) << 32); /* extend src1 */ +vax_fmul (&a, &b, 0, FD_BIAS, 0, LMASK); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (&a, NULL); /* return frac */ +} + +int32 op_emodd (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* unpack operands */ +unpackd (opnd[3], opnd[4], &b); +a.frac = a.frac | opnd[2]; /* extend src1 */ +vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (&a, flo); /* return frac */ +} + +int32 op_emodg (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* unpack operands */ +unpackg (opnd[3], opnd[4], &b); +a.frac = a.frac | (opnd[2] >> 5); /* extend src1 */ +vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, G_BIAS, intgr, flg); /* sep int & frac */ +return rpackg (&a, flo); /* return frac */ +} + +/* Unpacked floating point routines */ + +void vax_fadd (UFP *a, UFP *b) +{ +int32 ediff; +UFP t; + +if (a->frac == 0) { /* s1 = 0? */ + *a = *b; + return; + } +if (b->frac == 0) return; /* s2 = 0? */ +if ((a->exp < b->exp) || /* |s1| < |s2|? swap */ + ((a->exp == b->exp) && (a->frac < b->frac))) { + t = *a; + *a = *b; + *b = t; + } +ediff = a->exp - b->exp; /* exp diff */ +if (a->sign ^ b->sign) { /* eff sub? */ + if (ediff) { /* exp diff? */ + if (ediff > 63) b->frac = M64; /* retain sticky */ + else b->frac = ((-((t_int64) b->frac) >> ediff) | /* denormalize */ + (M64 << (64 - ediff))); /* preserve sign */ + a->frac = a->frac + b->frac; /* add frac */ + } + else a->frac = a->frac - b->frac; /* sub frac */ + norm (a); /* normalize */ + } +else { + if (ediff > 63) b->frac = 0; /* add */ + else if (ediff) b->frac = b->frac >> ediff; /* denormalize */ + a->frac = a->frac + b->frac; /* add frac */ + if (a->frac < b->frac) { /* chk for carry */ + a->frac = UF_NM | (a->frac >> 1); /* shift in carry */ + a->exp = a->exp + 1; /* skip norm */ + } + } +return; +} + +/* Floating multiply - 64b * 64b with cross products */ + +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo) +{ +t_uint64 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; +t_uint64 mask = (((t_uint64) mhi) << 32) | ((t_uint64) mlo); + +if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ + a->frac = a->sign = a->exp = 0; /* result is zero */ + return; + } +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp + b->exp - bias; /* add exponents */ +ah = (a->frac >> 32) & LMASK; /* split operands */ +bh = (b->frac >> 32) & LMASK; /* into 32b chunks */ +rhi = ah * bh; /* high result */ +if (qd) { /* 64b needed? */ + al = a->frac & LMASK; + bl = b->frac & LMASK; + rmid1 = ah * bl; + rmid2 = al * bh; + rlo = al * bl; + rhi = rhi + ((rmid1 >> 32) & LMASK) + ((rmid2 >> 32) & LMASK); + rmid1 = rlo + (rmid1 << 32); /* add mid1 to lo */ + if (rmid1 < rlo) rhi = rhi + 1; /* carry? incr hi */ + rmid2 = rmid1 + (rmid2 << 32); /* add mid2 to lo */ + if (rmid2 < rmid1) rhi = rhi + 1; /* carry? incr hi */ + } +a->frac = rhi & ~mask; +norm (a); /* normalize */ +return; +} + +/* Floating modulus - there are three cases + + exp <= bias - integer is 0, fraction is input, + no overflow + bias < exp <= bias+64 - separate integer and fraction, + integer overflow may occur + bias+64 < exp - result is integer, fraction is 0 + integer overflow +*/ + +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg) +{ +if (a->exp <= bias) *intgr = *flg = 0; /* 0 or <1? int = 0 */ +else if (a->exp <= (bias + 64)) { /* in range [1,64]? */ + *intgr = (int32) (a->frac >> (64 - (a->exp - bias))); + if ((a->exp > (bias + 32)) || /* test ovflo */ + ((a->exp == (bias + 32)) && + (((uint32) *intgr) > (a->sign? 0x80000000: 0x7FFFFFFF)))) + *flg = CC_V; + else *flg = 0; + if (a->sign) *intgr = -*intgr; /* -? comp int */ + if (a->exp == (bias + 64)) a->frac = 0; /* special case 64 */ + else a->frac = a->frac << (a->exp - bias); + a->exp = bias; + } +else { + *intgr = 0; /* out of range */ + a->frac = a->sign = a->exp = 0; /* result 0 */ + *flg = CC_V; /* overflow */ + } +norm (a); /* normalize */ +return; +} + +/* Floating divide + Needs to develop at least one rounding bit. Since the first + divide step can fail, caller should specify 2 more bits than + the precision of the fraction. +*/ + +void vax_fdiv (UFP *a, UFP *b, int32 prec, int32 bias) +{ +int32 i; +t_uint64 quo = 0; + +if (a->exp == 0) FLT_DZRO_FAULT; /* divr = 0? */ +if (b->exp == 0) return; /* divd = 0? */ +b->sign = b->sign ^ a->sign; /* result sign */ +b->exp = b->exp - a->exp + bias + 1; /* unbiased exp */ +a->frac = a->frac >> 1; /* allow 1 bit left */ +b->frac = b->frac >> 1; +for (i = 0; (i < prec) && b->frac; i++) { /* divide loop */ + quo = quo << 1; /* shift quo */ + if (b->frac >= a->frac) { /* div step ok? */ + b->frac = b->frac - a->frac; /* subtract */ + quo = quo + 1; /* quo bit = 1 */ + } + b->frac = b->frac << 1; /* shift divd */ + } +b->frac = quo << (UF_V_NM - i + 1); /* shift quo */ +norm (b); /* normalize */ +return; +} + +/* Support routines */ + +void unpackf (int32 hi, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac = 0; /* else 0 */ + return; + } +hi = (((hi & FD_FRACW) | FD_HB) << 16) | ((hi >> 16) & 0xFFFF); +r->frac = ((t_uint64) hi) << (32 + UF_V_FDLO); +return; +} + +void unpackd (int32 hi, int32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac = 0; /* else 0 */ + return; + } +hi = (hi & FD_FRACL) | FD_HB; /* canonical form */ +r->frac = UNSCRAM (hi, lo) << UF_V_FDLO; /* guard bits */ +return; +} + +void unpackg (int32 hi, int32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = G_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac = 0; /* else 0 */ + return; + } +hi = (hi & G_FRACL) | G_HB; /* canonical form */ +r->frac = UNSCRAM (hi, lo) << UF_V_GLO; /* guard bits */ +return; +} + +void norm (UFP *r) +{ +int32 i; +static t_uint64 normmask[5] = { + 0xc000000000000000, 0xf000000000000000, 0xff00000000000000, + 0xffff000000000000, 0xffffffff00000000 + }; +static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; + +if (r->frac == 0) { /* if fraction = 0 */ + r->sign = r->exp = 0; /* result is 0 */ + return; + } +while ((r->frac & UF_NM) == 0) { /* normalized? */ + for (i = 0; i < 5; i++) { /* find first 1 */ + if (r->frac & normmask[i]) break; + } + r->frac = r->frac << normtab[i]; /* shift frac */ + r->exp = r->exp - normtab[i]; /* decr exp */ + } +return; +} + +int32 rpackfd (UFP *r, int32 *rh) +{ +if (rh) *rh = 0; /* assume 0 */ +if (r->frac == 0) return 0; /* result 0? */ +r->frac = r->frac + (rh? UF_DRND: UF_FRND); /* round */ +if ((r->frac & UF_NM) == 0) { /* carry out? */ + r->frac = r->frac >> 1; /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) FD_M_EXP) FLT_OVFL_FAULT; /* ovflo? fault */ +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) FLT_UNFL_FAULT; /* fault if fu */ + return 0; /* else 0 */ + } +if (rh) *rh = UF_GETFDLO (r->frac); /* get low */ +return r->sign | (r->exp << FD_V_EXP) | UF_GETFDHI (r->frac); +} + +int32 rpackg (UFP *r, int32 *rh) +{ +*rh = 0; /* assume 0 */ +if (r->frac == 0) return 0; /* result 0? */ +r->frac = r->frac + UF_GRND; /* round */ +if ((r->frac & UF_NM) == 0) { /* carry out? */ + r->frac = r->frac >> 1; /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) G_M_EXP) FLT_OVFL_FAULT; /* ovflo? fault */ +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) FLT_UNFL_FAULT; /* fault if fu */ + return 0; /* else 0 */ + } +if (rh) *rh = UF_GETGLO (r->frac); /* get low */ +return r->sign | (r->exp << G_V_EXP) | UF_GETGHI (r->frac); +} + +#else /* 32b code */ + +#define WORDSWAP(x) ((((x) & WMASK) << 16) | (((x) >> 16) & WMASK)) + +typedef struct { + uint32 lo; + uint32 hi; + } UDP; + +typedef struct { + int32 sign; + int32 exp; + UDP frac; + } UFP; + +#define UF_NM_H 0x80000000 /* normalized */ +#define UF_FRND_H 0x00000080 /* F round */ +#define UF_FRND_L 0x00000000 +#define UF_DRND_H 0x00000000 /* D round */ +#define UF_DRND_L 0x00000080 +#define UF_GRND_H 0x00000000 /* G round */ +#define UF_GRND_L 0x00000400 +#define UF_V_NM 63 + +void unpackf (uint32 hi, UFP *a); +void unpackd (uint32 hi, uint32 lo, UFP *a); +void unpackg (uint32 hi, uint32 lo, UFP *a); +void norm (UFP *a); +int32 rpackfd (UFP *a, int32 *rh); +int32 rpackg (UFP *a, int32 *rh); +void vax_fadd (UFP *a, UFP *b); +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo); +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg); +void vax_fdiv (UFP *b, UFP *a, int32 prec, int32 bias); +void dp_add (UDP *a, UDP *b); +void dp_inc (UDP *a); +void dp_sub (UDP *a, UDP *b); +void dp_imul (uint32 a, uint32 b, UDP *r); +void dp_lsh (UDP *a, uint32 sc); +void dp_rsh (UDP *a, uint32 sc); +void dp_rsh_s (UDP *a, uint32 sc, uint32 neg); +void dp_neg (UDP *a); +int32 dp_cmp (UDP *a, UDP *b); + +/* Quadword arithmetic shift + + opnd[0] = shift count (cnt.rb) + opnd[1:2] = source (src.rq) + opnd[3:4] = destination (dst.wq) +*/ + +int32 op_ashq (int32 *opnd, int32 *rh, int32 *flg) +{ +UDP r, sr; +uint32 sc = opnd[0]; + +r.lo = opnd[1]; /* get source */ +r.hi = opnd[2]; +*flg = 0; /* assume no ovflo */ +if (sc & BSIGN) /* right shift? */ + dp_rsh_s (&r, 0x100 - sc, r.hi & LSIGN); /* signed right */ +else { + dp_lsh (&r, sc); /* left shift */ + sr = r; /* copy result */ + dp_rsh_s (&sr, sc, sr.hi & LSIGN); /* signed right */ + if ((sr.hi != ((uint32) opnd[2])) || /* reshift != orig? */ + (sr.lo != ((uint32) opnd[1]))) *flg = 1; /* overflow */ + } +*rh = r.hi; /* hi result */ +return r.lo; /* lo result */ +} + +/* Extended multiply subroutine */ + +int32 op_emul (int32 mpy, int32 mpc, int32 *rh) +{ +UDP r; +int32 sign = mpy ^ mpc; /* sign of result */ + +if (mpy & LSIGN) mpy = -mpy; /* abs value */ +if (mpc & LSIGN) mpc = -mpc; +dp_imul (mpy & LMASK, mpc & LMASK, &r); /* 32b * 32b -> 64b */ +if (sign & LSIGN) dp_neg (&r); /* negative result? */ +*rh = r.hi; +return r.lo; +} + +/* Extended divide + + opnd[0] = divisor (non-zero) + opnd[1:2] = dividend +*/ + +int32 op_ediv (int32 *opnd, int32 *rh, int32 *flg) +{ +UDP dvd; +uint32 i, dvr, quo; + +dvr = opnd[0]; /* get divisor */ +dvd.lo = opnd[1]; /* get dividend */ +dvd.hi = opnd[2]; +*flg = CC_V; /* assume error */ +*rh = 0; +if (dvd.hi & LSIGN) dp_neg (&dvd); /* |dividend| */ +if (dvr & LSIGN) dvr = NEG (dvr); /* |divisor| */ +if (dvd.hi >= dvr) return opnd[1]; /* divide work? */ +for (i = quo = 0; i < 32; i++) { /* 32 iterations */ + quo = quo << 1; /* shift quotient */ + dp_lsh (&dvd, 1); /* shift dividend */ + if (dvd.hi >= dvr) { /* step work? */ + dvd.hi = (dvd.hi - dvr) & LMASK; /* subtract dvr */ + quo = quo + 1; + } + } +if ((opnd[0] ^ opnd[2]) & LSIGN) { /* result -? */ + quo = NEG (quo); /* negate */ + if (quo && ((quo & LSIGN) == 0)) return opnd[1]; /* right sign? */ + } +else if (quo & LSIGN) return opnd[1]; +if (opnd[2] & LSIGN) *rh = NEG (dvd.hi); /* sign of rem */ +else *rh = dvd.hi; +*flg = 0; /* no overflow */ +return quo; /* return quo */ +} + +/* Compare floating */ + +int32 op_cmpfd (int32 h1, int32 l1, int32 h2, int32 l2) +{ +UFP a, b; +int32 r; + +unpackd (h1, l1, &a); +unpackd (h2, l2, &b); +if (a.sign != b.sign) return (a.sign? CC_N: 0); +r = a.exp - b.exp; +if (r == 0) r = dp_cmp (&a.frac, &b.frac); +if (r < 0) return (a.sign? 0: CC_N); +if (r > 0) return (a.sign? CC_N: 0); +return CC_Z; +} + +int32 op_cmpg (int32 h1, int32 l1, int32 h2, int32 l2) +{ +UFP a, b; +int32 r; + +unpackg (h1, l1, &a); +unpackg (h2, l2, &b); +if (a.sign != b.sign) return (a.sign? CC_N: 0); +r = a.exp - b.exp; +if (r == 0) r = dp_cmp (&a.frac, &b.frac); +if (r < 0) return (a.sign? 0: CC_N); +if (r > 0) return (a.sign? CC_N: 0); +return CC_Z; +} + +/* Integer to floating convert */ + +int32 op_cvtifdg (int32 val, int32 *rh, int32 opc) +{ +UFP a; + +if (val == 0) { /* zero? */ + if (rh) *rh = 0; /* return true 0 */ + return 0; + } +if (val < 0) { /* negative? */ + a.sign = FPSIGN; /* sign = - */ + val = -val; + } +else a.sign = 0; /* else sign = + */ +a.exp = 32 + ((opc & 0x100)? G_BIAS: FD_BIAS); /* initial exp */ +a.frac.hi = val & LMASK; /* fraction */ +a.frac.lo = 0; +norm (&a); /* normalize */ +if (opc & 0x100) return rpackg (&a, rh); /* pack and return */ +return rpackfd (&a, rh); +} + +/* Floating to integer convert */ + +int32 op_cvtfdgi (int32 *opnd, int32 *flg, int32 opc) +{ +UFP a; +int32 lnt = opc & 03; +int32 ubexp; +static uint32 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; + +*flg = 0; +if (opc & 0x100) { /* G? */ + unpackg (opnd[0], opnd[1], &a); /* unpack */ + ubexp = a.exp - G_BIAS; /* unbiased exp */ + } +else { + if (opc & 0x20) unpackd (opnd[0], opnd[1], &a); /* F or D */ + else unpackf (opnd[0], &a); /* unpack */ + ubexp = a.exp - FD_BIAS; /* unbiased exp */ + } +if ((a.exp == 0) || (ubexp < 0)) return 0; /* true zero or frac? */ +if (ubexp <= UF_V_NM) { /* exp in range? */ + dp_rsh (&a.frac, UF_V_NM - ubexp); /* leave rnd bit */ + if (lnt == 03) dp_inc (&a.frac); /* if CVTR, round */ + dp_rsh (&a.frac, 1); /* now justified */ + if ((a.frac.hi != 0) || + (a.frac.lo > (maxv[lnt] + (a.sign? 1: 0)))) *flg = CC_V; + } +else { + *flg = CC_V; /* always ovflo */ + if (ubexp > (UF_V_NM + 32)) return 0; /* in ext range? */ + dp_lsh (&a.frac, ubexp - UF_V_NM - 1); /* no rnd bit */ + } +return (a.sign? NEG (a.frac.lo): a.frac.lo); /* return lo frac */ +} + +/* Extended modularize + + One of three floating point instructions dropped from the architecture, + EMOD presents two sets of complications. First, it requires an extended + fraction multiply, with precise (and unusual) truncation conditions. + Second, it has two write operands, a dubious distinction it shares + with EDIV. +*/ + +int32 op_emodf (int32 *opnd, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* unpack operands */ +unpackf (opnd[2], &b); +a.frac.hi = a.frac.hi | opnd[1]; /* extend src1 */ +vax_fmul (&a, &b, 0, FD_BIAS, 0, LMASK); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (&a, NULL); /* return frac */ +} + +int32 op_emodd (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* unpack operands */ +unpackd (opnd[3], opnd[4], &b); +a.frac.lo = a.frac.lo | opnd[2]; /* extend src1 */ +vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, FD_BIAS, intgr, flg); /* sep int & frac */ +return rpackfd (&a, flo); /* return frac */ +} + +int32 op_emodg (int32 *opnd, int32 *flo, int32 *intgr, int32 *flg) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* unpack operands */ +unpackg (opnd[3], opnd[4], &b); +a.frac.lo = a.frac.lo | (opnd[2] >> 5); /* extend src1 */ +vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* multiply */ +vax_fmod (&a, G_BIAS, intgr, flg); /* sep int & frac */ +return rpackg (&a, flo); /* return frac */ +} + +/* Unpacked floating point routines */ + +/* Floating add */ + +void vax_fadd (UFP *a, UFP *b) +{ +int32 ediff; +UFP t; + +if ((a->frac.hi == 0) && (a->frac.lo == 0)) { /* s1 = 0? */ + *a = *b; + return; + } +if ((b->frac.hi == 0) && (b->frac.lo == 0)) return; /* s2 = 0? */ +if ((a->exp < b->exp) || /* |s1| < |s2|? swap */ + ((a->exp == b->exp) && (dp_cmp (&a->frac, &b->frac) < 0))) { + t = *a; + *a = *b; + *b = t; + } +ediff = a->exp - b->exp; /* exp diff */ +if (a->sign ^ b->sign) { /* eff sub? */ + if (ediff) { /* exp diff? */ + dp_neg (&b->frac); /* negate fraction */ + dp_rsh_s (&b->frac, ediff, 1); /* signed right */ + dp_add (&a->frac, &b->frac); /* "add" frac */ + } + else dp_sub (&a->frac, &b->frac); /* a >= b */ + norm (a); /* normalize */ + } +else { + if (ediff) dp_rsh (&b->frac, ediff); /* add, denormalize */ + dp_add (&a->frac, &b->frac); /* add frac */ + if (dp_cmp (&a->frac, &b->frac) < 0) { /* chk for carry */ + dp_rsh (&a->frac, 1); /* renormalize */ + a->frac.hi = a->frac.hi | UF_NM_H; /* add norm bit */ + a->exp = a->exp + 1; /* skip norm */ + } + } +return; +} + +/* Floating multiply - 64b * 64b with cross products */ + +void vax_fmul (UFP *a, UFP *b, t_bool qd, int32 bias, uint32 mhi, uint32 mlo) +{ +UDP rhi, rlo, rmid1, rmid2; + +if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ + a->frac.hi = a->frac.lo = 0; /* result is zero */ + a->sign = a->exp = 0; + return; + } +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp + b->exp - bias; /* add exponents */ +dp_imul (a->frac.hi, b->frac.hi, &rhi); /* high result */ +if (qd) { /* 64b needed? */ + dp_imul (a->frac.hi, b->frac.lo, &rmid1); /* cross products */ + dp_imul (a->frac.lo, b->frac.hi, &rmid2); + dp_imul (a->frac.lo, b->frac.lo, &rlo); /* low result */ + rhi.lo = (rhi.lo + rmid1.hi) & LMASK; /* add hi cross */ + if (rhi.lo < rmid1.hi) /* to low high res */ + rhi.hi = (rhi.hi + 1) & LMASK; + rhi.lo = (rhi.lo + rmid2.hi) & LMASK; + if (rhi.lo < rmid2.hi) + rhi.hi = (rhi.hi + 1) & LMASK; + rlo.hi = (rlo.hi + rmid1.lo) & LMASK; /* add mid1 to low res */ + if (rlo.hi < rmid1.lo) dp_inc (&rhi); /* carry? incr high res */ + rlo.hi = (rlo.hi + rmid2.lo) & LMASK; /* add mid2 to low res */ + if (rlo.hi < rmid2.lo) dp_inc (&rhi); /* carry? incr high res */ + } +a->frac.hi = rhi.hi & ~mhi; /* mask fraction */ +a->frac.lo = rhi.lo & ~mlo; +norm (a); /* normalize */ +return; +} + +/* Floating modulus - there are three cases + + exp <= bias - integer is 0, fraction is input, + no overflow + bias < exp <= bias+64 - separate integer and fraction, + integer overflow may occur + bias+64 < exp - result is integer, fraction is 0 + integer overflow +*/ + +void vax_fmod (UFP *a, int32 bias, int32 *intgr, int32 *flg) +{ +UDP ifr; + +if (a->exp <= bias) *intgr = *flg = 0; /* 0 or <1? int = 0 */ +else if (a->exp <= (bias + 64)) { /* in range [1,64]? */ + ifr = a->frac; + dp_rsh (&ifr, 64 - (a->exp - bias)); /* separate integer */ + if ((a->exp > (bias + 32)) || /* test ovflo */ + ((a->exp == (bias + 32)) && + (ifr.lo > (a->sign? 0x80000000: 0x7FFFFFFF)))) + *flg = CC_V; + else *flg = 0; + *intgr = ifr.lo; + if (a->sign) *intgr = -*intgr; /* -? comp int */ + dp_lsh (&a->frac, a->exp - bias); /* excise integer */ + a->exp = bias; + } +else { + *intgr = 0; /* out of range */ + a->frac.hi = a->frac.lo = a->sign = a->exp = 0; /* result 0 */ + *flg = CC_V; /* overflow */ + } +norm (a); /* normalize */ +return; +} + +/* Floating divide + Needs to develop at least one rounding bit. Since the first + divide step can fail, caller should specify 2 more bits than + the precision of the fraction. +*/ + +void vax_fdiv (UFP *a, UFP *b, int32 prec, int32 bias) +{ +int32 i; +UDP quo = { 0, 0 }; + +if (a->exp == 0) FLT_DZRO_FAULT; /* divr = 0? */ +if (b->exp == 0) return; /* divd = 0? */ +b->sign = b->sign ^ a->sign; /* result sign */ +b->exp = b->exp - a->exp + bias + 1; /* unbiased exp */ +dp_rsh (&a->frac, 1); /* allow 1 bit left */ +dp_rsh (&b->frac, 1); +for (i = 0; i < prec; i++) { /* divide loop */ + dp_lsh (&quo, 1); /* shift quo */ + if (dp_cmp (&b->frac, &a->frac) >= 0) { /* div step ok? */ + dp_sub (&b->frac, &a->frac); /* subtract */ + quo.lo = quo.lo + 1; /* quo bit = 1 */ + } + dp_lsh (&b->frac, 1); /* shift divd */ + } +dp_lsh (&quo, UF_V_NM - prec + 1); /* put in position */ +b->frac = quo; +norm (b); /* normalize */ +return; +} + +/* Double precision integer routines */ + +int32 dp_cmp (UDP *a, UDP *b) +{ +if (a->hi < b->hi) return -1; /* compare hi */ +if (a->hi > b->hi) return +1; +if (a->lo < b->lo) return -1; /* hi =, compare lo */ +if (a->lo > b->lo) return +1; +return 0; /* hi, lo equal */ +} + +void dp_add (UDP *a, UDP *b) +{ +a->lo = (a->lo + b->lo) & LMASK; /* add lo */ +if (a->lo < b->lo) a->hi = a->hi + 1; /* carry? */ +a->hi = (a->hi + b->hi) & LMASK; /* add hi */ +return; +} + +void dp_inc (UDP *a) +{ +a->lo = (a->lo + 1) & LMASK; /* inc lo */ +if (a->lo == 0) a->hi = (a->hi + 1) & LMASK; /* carry? inc hi */ +return; +} + +void dp_sub (UDP *a, UDP *b) +{ +if (a->lo < b->lo) a->hi = a->hi - 1; /* borrow? decr hi */ +a->lo = (a->lo - b->lo) & LMASK; /* sub lo */ +a->hi = (a->hi - b->hi) & LMASK; /* sub hi */ +return; +} + +void dp_lsh (UDP *r, uint32 sc) +{ +if (sc > 63) r->hi = r->lo = 0; /* > 63? result 0 */ +else if (sc > 31) { /* [32,63]? */ + r->hi = (r->lo << (sc - 32)) & LMASK; + r->lo = 0; + } +else if (sc != 0) { + r->hi = ((r->hi << sc) | (r->lo >> (32 - sc))) & LMASK; + r->lo = (r->lo << sc) & LMASK; + } +return; +} + +void dp_rsh (UDP *r, uint32 sc) +{ +if (sc > 63) r->hi = r->lo = 0; /* > 63? result 0 */ +else if (sc > 31) { /* [32,63]? */ + r->lo = (r->hi >> (sc - 32)) & LMASK; + r->hi = 0; + } +else if (sc != 0) { + r->lo = ((r->lo >> sc) | (r->hi << (32 - sc))) & LMASK; + r->hi = (r->hi >> sc) & LMASK; + } +return; +} + +void dp_rsh_s (UDP *r, uint32 sc, uint32 neg) +{ +dp_rsh (r, sc); /* do unsigned right */ +if (neg && sc) { /* negative? */ + if (sc > 63) r->hi = r->lo = LMASK; /* > 63? result -1 */ + else { + UDP ones = { LMASK, LMASK }; + dp_lsh (&ones, 64 - sc); /* shift ones */ + r->hi = r->hi | ones.hi; /* or into result */ + r->lo = r->lo | ones.lo; + } + } +return; +} + +void dp_imul (uint32 a, uint32 b, UDP *r) +{ +uint32 ah, bh, al, bl, rhi, rlo, rmid1, rmid2; + +if ((a == 0) || (b == 0)) { /* zero argument? */ + r->hi = r->lo = 0; /* result is zero */ + return; + } +ah = (a >> 16) & WMASK; /* split operands */ +bh = (b >> 16) & WMASK; /* into 16b chunks */ +al = a & WMASK; +bl = b & WMASK; +rhi = ah * bh; /* high result */ +rmid1 = ah * bl; +rmid2 = al * bh; +rlo = al * bl; +rhi = rhi + ((rmid1 >> 16) & WMASK) + ((rmid2 >> 16) & WMASK); +rmid1 = (rlo + (rmid1 << 16)) & LMASK; /* add mid1 to lo */ +if (rmid1 < rlo) rhi = rhi + 1; /* carry? incr hi */ +rmid2 = (rmid1 + (rmid2 << 16)) & LMASK; /* add mid2 to to */ +if (rmid2 < rmid1) rhi = rhi + 1; /* carry? incr hi */ +r->hi = rhi & LMASK; /* mask result */ +r->lo = rmid2; +return; +} + +void dp_neg (UDP *r) +{ +r->lo = NEG (r->lo); +r->hi = (~r->hi + (r->lo == 0)) & LMASK; +return; +} + +/* Support routines */ + +void unpackf (uint32 hi, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac.hi = r->frac.lo = 0; /* else 0 */ + return; + } +r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); +r->frac.lo = 0; +dp_lsh (&r->frac, FD_GUARD); +return; +} + +void unpackd (uint32 hi, uint32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac.hi = r->frac.lo = 0; /* else 0 */ + return; + } +r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); +r->frac.lo = WORDSWAP (lo); +dp_lsh (&r->frac, FD_GUARD); +return; +} + +void unpackg (uint32 hi, uint32 lo, UFP *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = G_GETEXP (hi); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac.hi = r->frac.lo = 0; /* else 0 */ + return; + } +r->frac.hi = WORDSWAP ((hi & ~(FPSIGN | G_EXP)) | G_HB); +r->frac.lo = WORDSWAP (lo); +dp_lsh (&r->frac, G_GUARD); +return; +} + +void norm (UFP *r) +{ +int32 i; +static uint32 normmask[5] = { + 0xc0000000, 0xf0000000, 0xff000000, 0xffff0000, 0xffffffff + }; +static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; + +if ((r->frac.hi == 0) && (r->frac.lo == 0)) { /* if fraction = 0 */ + r->sign = r->exp = 0; /* result is 0 */ + return; + } +while ((r->frac.hi & UF_NM_H) == 0) { /* normalized? */ + for (i = 0; i < 5; i++) { /* find first 1 */ + if (r->frac.hi & normmask[i]) break; + } + dp_lsh (&r->frac, normtab[i]); /* shift frac */ + r->exp = r->exp - normtab[i]; /* decr exp */ + } +return; +} + +int32 rpackfd (UFP *r, int32 *rh) +{ +static UDP f_round = { UF_FRND_L, UF_FRND_H }; +static UDP d_round = { UF_DRND_L, UF_DRND_H }; + +if (rh) *rh = 0; /* assume 0 */ +if ((r->frac.hi == 0) && (r->frac.lo == 0)) return 0; /* result 0? */ +if (rh) dp_add (&r->frac, &d_round); /* round */ +else dp_add (&r->frac, &f_round); +if ((r->frac.hi & UF_NM_H) == 0) { /* carry out? */ + dp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) FD_M_EXP) FLT_OVFL_FAULT; /* ovflo? fault */ +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) FLT_UNFL_FAULT; /* fault if fu */ + return 0; /* else 0 */ + } +dp_rsh (&r->frac, FD_GUARD); /* remove guard */ +if (rh) *rh = WORDSWAP (r->frac.lo); /* get low */ +return r->sign | (r->exp << FD_V_EXP) | + (WORDSWAP (r->frac.hi) & ~(FD_HB | FPSIGN | FD_EXP)); +} + +int32 rpackg (UFP *r, int32 *rh) +{ +static UDP g_round = { UF_GRND_L, UF_GRND_H }; + +*rh = 0; /* assume 0 */ +if ((r->frac.hi == 0) && (r->frac.lo == 0)) return 0; /* result 0? */ +dp_add (&r->frac, &g_round); /* round */ +if ((r->frac.hi & UF_NM_H) == 0) { /* carry out? */ + dp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) G_M_EXP) FLT_OVFL_FAULT; /* ovflo? fault */ +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) FLT_UNFL_FAULT; /* fault if fu */ + return 0; /* else 0 */ + } +dp_rsh (&r->frac, G_GUARD); /* remove guard */ +*rh = WORDSWAP (r->frac.lo); /* get low */ +return r->sign | (r->exp << G_V_EXP) | + (WORDSWAP (r->frac.hi) & ~(G_HB | FPSIGN | G_EXP)); +} + +#endif + +/* Floating point instructions */ + +/* Move/test/move negated floating + + Note that only the high 32b is processed. + If the high 32b is not zero, it is unchanged. +*/ + +int32 op_movfd (int32 val) +{ +if (val & FD_EXP) return val; +if (val & FPSIGN) RSVD_OPND_FAULT; +return 0; +} + +int32 op_mnegfd (int32 val) +{ +if (val & FD_EXP) return (val ^ FPSIGN); +if (val & FPSIGN) RSVD_OPND_FAULT; +return 0; +} + +int32 op_movg (int32 val) +{ +if (val & G_EXP) return val; +if (val & FPSIGN) RSVD_OPND_FAULT; +return 0; +} + +int32 op_mnegg (int32 val) +{ +if (val & G_EXP) return (val ^ FPSIGN); +if (val & FPSIGN) RSVD_OPND_FAULT; +return 0; +} + +/* Floating to floating convert - F to D is essentially done with MOVFD */ + +int32 op_cvtdf (int32 *opnd) +{ +UFP a; + +unpackd (opnd[0], opnd[1], &a); +return rpackfd (&a, NULL); +} + +int32 op_cvtfg (int32 *opnd, int32 *rh) +{ +UFP a; + +unpackf (opnd[0], &a); +a.exp = a.exp - FD_BIAS + G_BIAS; +return rpackg (&a, rh); +} + +int32 op_cvtgf (int32 *opnd) +{ +UFP a; + +unpackg (opnd[0], opnd[1], &a); +a.exp = a.exp - G_BIAS + FD_BIAS; +return rpackfd (&a, NULL); +} + +/* Floating add and subtract */ + +int32 op_addf (int32 *opnd, t_bool sub) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* F format */ +unpackf (opnd[1], &b); +if (sub) a.sign = a.sign ^ FPSIGN; /* sub? -s1 */ +vax_fadd (&a, &b); /* add fractions */ +return rpackfd (&a, NULL); +} + +int32 op_addd (int32 *opnd, int32 *rh, t_bool sub) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); +unpackd (opnd[2], opnd[3], &b); +if (sub) a.sign = a.sign ^ FPSIGN; /* sub? -s1 */ +vax_fadd (&a, &b); /* add fractions */ +return rpackfd (&a, rh); +} + +int32 op_addg (int32 *opnd, int32 *rh, t_bool sub) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); +unpackg (opnd[2], opnd[3], &b); +if (sub) a.sign = a.sign ^ FPSIGN; /* sub? -s1 */ +vax_fadd (&a, &b); /* add fractions */ +return rpackg (&a, rh); /* round and pack */ +} + +/* Floating multiply */ + +int32 op_mulf (int32 *opnd) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* F format */ +unpackf (opnd[1], &b); +vax_fmul (&a, &b, 0, FD_BIAS, 0, 0); /* do multiply */ +return rpackfd (&a, NULL); /* round and pack */ +} + +int32 op_muld (int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* D format */ +unpackd (opnd[2], opnd[3], &b); +vax_fmul (&a, &b, 1, FD_BIAS, 0, 0); /* do multiply */ +return rpackfd (&a, rh); /* round and pack */ +} + +int32 op_mulg (int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* G format */ +unpackg (opnd[2], opnd[3], &b); +vax_fmul (&a, &b, 1, G_BIAS, 0, 0); /* do multiply */ +return rpackg (&a, rh); /* round and pack */ +} + +/* Floating divide */ + +int32 op_divf (int32 *opnd) +{ +UFP a, b; + +unpackf (opnd[0], &a); /* F format */ +unpackf (opnd[1], &b); +vax_fdiv (&a, &b, 26, FD_BIAS); /* do divide */ +return rpackfd (&b, NULL); /* round and pack */ +} + +int32 op_divd (int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackd (opnd[0], opnd[1], &a); /* D format */ +unpackd (opnd[2], opnd[3], &b); +vax_fdiv (&a, &b, 58, FD_BIAS); /* do divide */ +return rpackfd (&b, rh); /* round and pack */ +} + +int32 op_divg (int32 *opnd, int32 *rh) +{ +UFP a, b; + +unpackg (opnd[0], opnd[1], &a); /* G format */ +unpackg (opnd[2], opnd[3], &b); +vax_fdiv (&a, &b, 55, G_BIAS); /* do divide */ +return rpackg (&b, rh); /* round and pack */ +} + +/* Polynomial evaluation + The most mis-implemented instruction in the VAX (probably here too). + POLY requires a precise combination of masking versus normalizing + to achieve the desired answer. In particular, the multiply step + is masked prior to normalization. In addition, negative small + fractions must not be treated as 0 during denorm. +*/ + +void op_polyf (int32 *opnd, int32 acc) +{ +UFP r, a, c; +int32 deg = opnd[1]; +int32 ptr = opnd[2]; +int32 i, wd, res; + +if (deg > 31) RSVD_OPND_FAULT; /* degree > 31? fault */ +unpackf (opnd[0], &a); /* unpack arg */ +wd = Read (ptr, L_LONG, RD); /* get C0 */ +ptr = ptr + 4; +unpackf (wd, &r); /* unpack C0 */ +res = rpackfd (&r, NULL); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + unpackf (res, &r); /* unpack result */ + vax_fmul (&r, &a, 0, FD_BIAS, 1, LMASK); /* r = r * arg, mask */ + wd = Read (ptr, L_LONG, RD); /* get Cnext */ + ptr = ptr + 4; + unpackf (wd, &c); /* unpack Cnext */ + vax_fadd (&r, &c); /* r = r + Cnext */ + res = rpackfd (&r, NULL); /* round and pack */ + } +R[0] = res; +R[1] = R[2] = 0; +R[3] = ptr; +return; +} + +void op_polyd (int32 *opnd, int32 acc) +{ +UFP r, a, c; +int32 deg = opnd[2]; +int32 ptr = opnd[3]; +int32 i, wd, wd1, res, resh; + +if (deg > 31) RSVD_OPND_FAULT; /* degree > 31? fault */ +unpackd (opnd[0], opnd[1], &a); /* unpack arg */ +wd = Read (ptr, L_LONG, RD); /* get C0 */ +wd1 = Read (ptr + 4, L_LONG, RD); +ptr = ptr + 8; +unpackd (wd, wd1, &r); /* unpack C0 */ +res = rpackfd (&r, &resh); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + unpackd (res, resh, &r); /* unpack result */ + vax_fmul (&r, &a, 1, FD_BIAS, 0, 1); /* r = r * arg, mask */ + wd = Read (ptr, L_LONG, RD); /* get Cnext */ + wd1 = Read (ptr + 4, L_LONG, RD); + ptr = ptr + 8; + unpackd (wd, wd1, &c); /* unpack Cnext */ + vax_fadd (&r, &c); /* r = r + Cnext */ + res = rpackfd (&r, &resh); /* round and pack */ + } +R[0] = res; +R[1] = resh; +R[2] = 0; +R[3] = ptr; +R[4] = 0; +R[5] = 0; +return; +} + +void op_polyg (int32 *opnd, int32 acc) +{ +UFP r, a, c; +int32 deg = opnd[2]; +int32 ptr = opnd[3]; +int32 i, wd, wd1, res, resh; + +if (deg > 31) RSVD_OPND_FAULT; /* degree > 31? fault */ +unpackg (opnd[0], opnd[1], &a); /* unpack arg */ +wd = Read (ptr, L_LONG, RD); /* get C0 */ +wd1 = Read (ptr + 4, L_LONG, RD); +ptr = ptr + 8; +unpackg (wd, wd1, &r); /* unpack C0 */ +res = rpackg (&r, &resh); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + unpackg (res, resh, &r); /* unpack result */ + vax_fmul (&r, &a, 1, G_BIAS, 0, 1); /* r = r * arg */ + wd = Read (ptr, L_LONG, RD); /* get Cnext */ + wd1 = Read (ptr + 4, L_LONG, RD); + ptr = ptr + 8; + unpackg (wd, wd1, &c); /* unpack Cnext */ + vax_fadd (&r, &c); /* r = r + Cnext */ + res = rpackg (&r, &resh); /* round and pack */ + } +R[0] = res; +R[1] = resh; +R[2] = 0; +R[3] = ptr; +R[4] = 0; +R[5] = 0; +return; +} diff --git a/VAX/vax_io.c b/VAX/vax_io.c new file mode 100644 index 0000000..c8ebf2d --- /dev/null +++ b/VAX/vax_io.c @@ -0,0 +1,1162 @@ +/* vax_io.c: VAX 3900 Qbus IO simulator + + Copyright (c) 1998-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + qba Qbus adapter + + 28-May-08 RMS Inlined physical memory routines + 25-Jan-08 RMS Fixed declarations (from Mark Pizzolato) + 03-Dec-05 RMS Added SHOW QBA VIRT and ex/dep via map + 05-Oct-05 RMS Fixed bug in autoconfiguration (missing XU) + 25-Jul-05 RMS Revised autoconfiguration algorithm and interface + 30-Sep-04 RMS Revised Qbus interface + Moved mem_err, crd_err interrupts here from vax_cpu.c + 09-Sep-04 RMS Integrated powerup into RESET (with -p) + 05-Sep-04 RMS Added CRD interrupt handling + 28-May-04 RMS Revised I/O dispatching (from John Dundas) + 21-Mar-04 RMS Added RXV21 support + 21-Dec-03 RMS Fixed bug in autoconfigure vector assignment; added controls + 21-Nov-03 RMS Added check for interrupt slot conflict (found by Dave Hittner) + 29-Oct-03 RMS Fixed WriteX declaration (found by Mark Pizzolato) + 19-Apr-03 RMS Added optimized byte and word DMA routines + 12-Mar-03 RMS Added logical name support + 22-Dec-02 RMS Added console halt support + 12-Oct-02 RMS Added autoconfigure support + Added SHOW IO space routine + 29-Sep-02 RMS Added dynamic table support + 07-Sep-02 RMS Added TMSCP and variable vector support +*/ + +#include "vax_defs.h" + +/* CQBIC system configuration register */ + +#define CQSCR_POK 0x00008000 /* power ok RO1 */ +#define CQSCR_BHL 0x00004000 /* BHALT enb */ +#define CQSCR_AUX 0x00000400 /* aux mode RONI */ +#define CQSCR_DBO 0x0000000C /* offset NI */ +#define CQSCR_RW (CQSCR_BHL | CQSCR_DBO) +#define CQSCR_MASK (CQSCR_RW | CQSCR_POK | CQSCR_AUX) + +/* CQBIC DMA system error register - W1C */ + +#define CQDSER_BHL 0x00008000 /* BHALT NI */ +#define CQDSER_DCN 0x00004000 /* DC ~OK NI */ +#define CQDSER_MNX 0x00000080 /* master NXM */ +#define CQDSER_MPE 0x00000020 /* master par NI */ +#define CQDSER_SME 0x00000010 /* slv mem err NI */ +#define CQDSER_LST 0x00000008 /* lost err */ +#define CQDSER_TMO 0x00000004 /* no grant NI */ +#define CQDSER_SNX 0x00000001 /* slave NXM */ +#define CQDSER_ERR (CQDSER_MNX | CQDSER_MPE | CQDSER_TMO | CQDSER_SNX) +#define CQDSER_MASK 0x0000C0BD + +/* CQBIC master error address register */ + +#define CQMEAR_MASK 0x00001FFF /* Qbus page */ + +/* CQBIC slave error address register */ + +#define CQSEAR_MASK 0x000FFFFF /* mem page */ + +/* CQBIC map base register */ + +#define CQMBR_MASK 0x1FFF8000 /* 32KB aligned */ + +/* CQBIC IPC register */ + +#define CQIPC_QME 0x00008000 /* Qbus read NXM W1C */ +#define CQIPC_INV 0x00004000 /* CAM inval NIWO */ +#define CQIPC_AHLT 0x00000100 /* aux halt NI */ +#define CQIPC_DBIE 0x00000040 /* dbell int enb NI */ +#define CQIPC_LME 0x00000020 /* local mem enb */ +#define CQIPC_DB 0x00000001 /* doorbell req NI */ +#define CQIPC_W1C CQIPC_QME +#define CQIPC_RW (CQIPC_AHLT | CQIPC_DBIE | CQIPC_LME | CQIPC_DB) +#define CQIPC_MASK (CQIPC_RW | CQIPC_QME ) + +/* CQBIC map entry */ + +#define CQMAP_VLD 0x80000000 /* valid */ +#define CQMAP_PAG 0x000FFFFF /* mem page */ + +int32 int_req[IPL_HLVL] = { 0 }; /* intr, IPL 14-17 */ +int32 cq_scr = 0; /* SCR */ +int32 cq_dser = 0; /* DSER */ +int32 cq_mear = 0; /* MEAR */ +int32 cq_sear = 0; /* SEAR */ +int32 cq_mbr = 0; /* MBR */ +int32 cq_ipc = 0; /* IPC */ +int32 autcon_enb = 1; /* autoconfig enable */ + +extern uint32 *M; +extern UNIT cpu_unit; +extern int32 PSL, SISR, trpirq, mem_err, crd_err, hlt_pin; +extern int32 p1; +extern int32 ssc_bto; +extern jmp_buf save_env; +extern int32 sim_switches; +extern DEVICE *sim_devices[]; +extern FILE *sim_log; + +t_stat dbl_rd (int32 *data, int32 addr, int32 access); +t_stat dbl_wr (int32 data, int32 addr, int32 access); +int32 eval_int (void); +void cq_merr (int32 pa); +void cq_serr (int32 pa); +t_stat qba_reset (DEVICE *dptr); +t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_bool qba_map_addr (uint32 qa, uint32 *ma); +t_bool qba_map_addr_c (uint32 qa, uint32 *ma); +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat qba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc); + +/* Qbus adapter data structures + + qba_dev QBA device descriptor + qba_unit QBA units + qba_reg QBA register list +*/ + +DIB qba_dib = { IOBA_DBL, IOLN_DBL, &dbl_rd, &dbl_wr, 0 }; + +UNIT qba_unit = { UDATA (NULL, 0, 0) }; + +REG qba_reg[] = { + { HRDATA (SCR, cq_scr, 16) }, + { HRDATA (DSER, cq_dser, 8) }, + { HRDATA (MEAR, cq_mear, 13) }, + { HRDATA (SEAR, cq_sear, 20) }, + { HRDATA (MBR, cq_mbr, 29) }, + { HRDATA (IPC, cq_ipc, 16) }, + { HRDATA (IPL17, int_req[3], 32), REG_RO }, + { HRDATA (IPL16, int_req[2], 32), REG_RO }, + { HRDATA (IPL15, int_req[1], 32), REG_RO }, + { HRDATA (IPL14, int_req[0], 32), REG_RO }, + { FLDATA (AUTOCON, autcon_enb, 0), REG_HRO }, + { NULL } + }; + +MTAB qba_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "IOSPACE", NULL, + NULL, &show_iospace }, + { MTAB_XTD|MTAB_VDV, 1, "AUTOCONFIG", "AUTOCONFIG", + &set_autocon, &show_autocon }, + { MTAB_XTD|MTAB_VDV, 0, NULL, "NOAUTOCONFIG", + &set_autocon, NULL }, + { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "VIRTUAL", NULL, + NULL, &qba_show_virt }, + { 0 } + }; + +DEVICE qba_dev = { + "QBA", &qba_unit, qba_reg, qba_mod, + 1, 16, CQMAWIDTH, 2, 16, 16, + &qba_ex, &qba_dep, &qba_reset, + NULL, NULL, NULL, + &qba_dib, DEV_QBUS + }; + +/* IO page dispatches */ + +static t_stat (*iodispR[IOPAGESIZE >> 1])(int32 *dat, int32 ad, int32 md); +static t_stat (*iodispW[IOPAGESIZE >> 1])(int32 dat, int32 ad, int32 md); +static DIB *iodibp[IOPAGESIZE >> 1]; + +/* Interrupt request to interrupt action map */ + +int32 (*int_ack[IPL_HLVL][32])(); /* int ack routines */ + +/* Interrupt request to vector map */ + +int32 int_vec[IPL_HLVL][32]; /* int req to vector */ + +/* The KA65x handles errors in I/O space as follows + + - read: set DSER<7>, latch addr in MEAR, machine check + - write: set DSER<7>, latch addr in MEAR, MEMERR interrupt +*/ + +int32 ReadQb (uint32 pa) +{ +int32 idx, val; + +idx = (pa & IOPAGEMASK) >> 1; +if (iodispR[idx]) { + iodispR[idx] (&val, pa, READ); + return val; + } +cq_merr (pa); +MACH_CHECK (MCHK_READ); +return 0; +} + +void WriteQb (uint32 pa, int32 val, int32 mode) +{ +int32 idx; + +idx = (pa & IOPAGEMASK) >> 1; +if (iodispW[idx]) { + iodispW[idx] (val, pa, mode); + return; + } +cq_merr (pa); +mem_err = 1; +return; +} + +/* ReadIO - read I/O space + + Inputs: + pa = physical address + lnt = length (BWLQ) + Output: + longword of data +*/ + +int32 ReadIO (uint32 pa, int32 lnt) +{ +int32 iod; + +iod = ReadQb (pa); /* wd from Qbus */ +if (lnt < L_LONG) iod = iod << ((pa & 2)? 16: 0); /* bw? position */ +else iod = (ReadQb (pa + 2) << 16) | iod; /* lw, get 2nd wd */ +SET_IRQL; +return iod; +} + +/* WriteIO - write I/O space + + Inputs: + pa = physical address + val = data to write, right justified in 32b longword + lnt = length (BWLQ) + Outputs: + none +*/ + +void WriteIO (uint32 pa, int32 val, int32 lnt) +{ +if (lnt == L_BYTE) WriteQb (pa, val, WRITEB); +else if (lnt == L_WORD) WriteQb (pa, val, WRITE); +else { + WriteQb (pa, val & 0xFFFF, WRITE); + WriteQb (pa + 2, (val >> 16) & 0xFFFF, WRITE); + } +SET_IRQL; +return; +} + +/* Find highest priority outstanding interrupt */ + +int32 eval_int (void) +{ +int32 ipl = PSL_GETIPL (PSL); +int32 i, t; + +static const int32 sw_int_mask[IPL_SMAX] = { + 0xFFFE, 0xFFFC, 0xFFF8, 0xFFF0, /* 0 - 3 */ + 0xFFE0, 0xFFC0, 0xFF80, 0xFF00, /* 4 - 7 */ + 0xFE00, 0xFC00, 0xF800, 0xF000, /* 8 - B */ + 0xE000, 0xC000, 0x8000 /* C - E */ + }; + +if (hlt_pin) return IPL_HLTPIN; /* hlt pin int */ +if ((ipl < IPL_MEMERR) && mem_err) return IPL_MEMERR; /* mem err int */ +if ((ipl < IPL_CRDERR) && crd_err) return IPL_CRDERR; /* crd err int */ +for (i = IPL_HMAX; i >= IPL_HMIN; i--) { /* chk hwre int */ + if (i <= ipl) return 0; /* at ipl? no int */ + if (int_req[i - IPL_HMIN]) return i; /* req != 0? int */ + } +if (ipl >= IPL_SMAX) return 0; /* ipl >= sw max? */ +if ((t = SISR & sw_int_mask[ipl]) == 0) return 0; /* eligible req */ +for (i = IPL_SMAX; i > ipl; i--) { /* check swre int */ + if ((t >> i) & 1) return i; /* req != 0? int */ + } +return 0; +} + +/* Return vector for highest priority hardware interrupt at IPL lvl */ + +int32 get_vector (int32 lvl) +{ +int32 i; +int32 l = lvl - IPL_HMIN; + +if (lvl == IPL_MEMERR) { /* mem error? */ + mem_err = 0; + return SCB_MEMERR; + } +if (lvl == IPL_CRDERR) { /* CRD error? */ + crd_err = 0; + return SCB_CRDERR; + } +if (lvl > IPL_HMAX) { /* error req lvl? */ + ABORT (STOP_UIPL); /* unknown intr */ + } +for (i = 0; int_req[l] && (i < 32); i++) { + if ((int_req[l] >> i) & 1) { + int_req[l] = int_req[l] & ~(1u << i); + if (int_ack[l][i]) return int_ack[l][i](); + return int_vec[l][i]; + } + } +return 0; +} + +/* CQBIC registers + + SCR system configuration register + DSER DMA system error register (W1C) + MEAR master error address register (RO) + SEAR slave error address register (RO) + MBR map base register + IPC inter-processor communication register +*/ + +int32 cqbic_rd (int32 pa) +{ +int32 rg = (pa - CQBICBASE) >> 2; + +switch (rg) { + + case 0: /* SCR */ + return (cq_scr | CQSCR_POK) & CQSCR_MASK; + + case 1: /* DSER */ + return cq_dser & CQDSER_MASK; + + case 2: /* MEAR */ + return cq_mear & CQMEAR_MASK; + + case 3: /* SEAR */ + return cq_sear & CQSEAR_MASK; + + case 4: /* MBR */ + return cq_mbr & CQMBR_MASK; + } + +return 0; +} + +void cqbic_wr (int32 pa, int32 val, int32 lnt) +{ +int32 nval, rg = (pa - CQBICBASE) >> 2; + +if (lnt < L_LONG) { + int32 sc = (pa & 3) << 3; + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = cqbic_rd (pa); + nval = ((val & mask) << sc) | (t & ~(mask << sc)); + val = val << sc; + } +else nval = val; +switch (rg) { + + case 0: /* SCR */ + cq_scr = ((cq_scr & ~CQSCR_RW) | (nval & CQSCR_RW)) & CQSCR_MASK; + break; + + case 1: /* DSER */ + cq_dser = (cq_dser & ~val) & CQDSER_MASK; + if (val & CQDSER_SME) cq_ipc = cq_ipc & ~CQIPC_QME; + break; + + case 2: case 3: + cq_merr (pa); /* MEAR, SEAR */ + MACH_CHECK (MCHK_WRITE); + break; + + case 4: /* MBR */ + cq_mbr = nval & CQMBR_MASK; + break; + } +return; +} + +/* IPC can be read as local register or as Qbus I/O + Because of the W1C */ + +int32 cqipc_rd (int32 pa) +{ +return cq_ipc & CQIPC_MASK; /* IPC */ +} + +void cqipc_wr (int32 pa, int32 val, int32 lnt) +{ +int32 nval = val; + +if (lnt < L_LONG) { + int32 sc = (pa & 3) << 3; + nval = val << sc; + } + +cq_ipc = cq_ipc & ~(nval & CQIPC_W1C); /* W1C */ +if ((pa & 3) == 0) /* low byte only */ + cq_ipc = ((cq_ipc & ~CQIPC_RW) | (val & CQIPC_RW)) & CQIPC_MASK; +return; +} + +/* I/O page routines */ + +t_stat dbl_rd (int32 *data, int32 addr, int32 access) +{ +*data = cq_ipc & CQIPC_MASK; +return SCPE_OK; +} + +t_stat dbl_wr (int32 data, int32 addr, int32 access) +{ +cqipc_wr (addr, data, (access == WRITEB)? L_BYTE: L_WORD); +return SCPE_OK; +} + +/* CQBIC map read and write (reflects to main memory) + + Read error: set DSER<0>, latch slave address, machine check + Write error: set DSER<0>, latch slave address, memory error interrupt +*/ + +int32 cqmap_rd (int32 pa) +{ +int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */ + +if (ADDR_IS_MEM (ma)) return M[ma >> 2]; +cq_serr (ma); /* set err */ +MACH_CHECK (MCHK_READ); /* mcheck */ +return 0; +} + +void cqmap_wr (int32 pa, int32 val, int32 lnt) +{ +int32 ma = (pa & CQMAPAMASK) + cq_mbr; /* mem addr */ + +if (ADDR_IS_MEM (ma)) { + if (lnt < L_LONG) { + int32 sc = (pa & 3) << 3; + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = M[ma >> 2]; + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } + M[ma >> 2] = val; + } +else { + cq_serr (ma); /* error */ + mem_err = 1; + } +return; +} + +/* CQBIC Qbus memory read and write (reflects to main memory) + + May give master or slave error, depending on where the failure occurs +*/ + +int32 cqmem_rd (int32 pa) +{ +int32 qa = pa & CQMAMASK; /* Qbus addr */ +uint32 ma; + +if (qba_map_addr (qa, &ma)) return M[ma >> 2]; /* map addr */ +MACH_CHECK (MCHK_READ); /* err? mcheck */ +return 0; +} + +void cqmem_wr (int32 pa, int32 val, int32 lnt) +{ +int32 qa = pa & CQMAMASK; /* Qbus addr */ +uint32 ma; + +if (qba_map_addr (qa, &ma)) { /* map addr */ + if (lnt < L_LONG) { + int32 sc = (pa & 3) << 3; + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = M[ma >> 2]; + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } + M[ma >> 2] = val; + } +else mem_err = 1; +return; +} + +/* Map an address via the translation map */ + +t_bool qba_map_addr (uint32 qa, uint32 *ma) +{ +int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */ +int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */ + +if (ADDR_IS_MEM (qmma)) { /* legit? */ + int32 qmap = M[qmma >> 2]; /* get map */ + if (qmap & CQMAP_VLD) { /* valid? */ + *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa); + if (ADDR_IS_MEM (*ma)) return TRUE; /* legit addr */ + cq_serr (*ma); /* slave nxm */ + return FALSE; + } + cq_merr (qa); /* master nxm */ + return FALSE; + } +cq_serr (0); /* inv mem */ +return FALSE; +} + +/* Map an address via the translation map - console version (no status changes) */ + +t_bool qba_map_addr_c (uint32 qa, uint32 *ma) +{ +int32 qblk = (qa >> VA_V_VPN); /* Qbus blk */ +int32 qmma = ((qblk << 2) & CQMAPAMASK) + cq_mbr; /* map entry */ + +if (ADDR_IS_MEM (qmma)) { /* legit? */ + int32 qmap = M[qmma >> 2]; /* get map */ + if (qmap & CQMAP_VLD) { /* valid? */ + *ma = ((qmap & CQMAP_PAG) << VA_V_VPN) + VA_GETOFF (qa); + return TRUE; /* legit addr */ + } + } +return FALSE; +} + +/* Set master error */ + +void cq_merr (int32 pa) +{ +if (cq_dser & CQDSER_ERR) cq_dser = cq_dser | CQDSER_LST; +cq_dser = cq_dser | CQDSER_MNX; /* master nxm */ +cq_mear = (pa >> VA_V_VPN) & CQMEAR_MASK; /* page addr */ +return; +} + +/* Set slave error */ + +void cq_serr (int32 pa) +{ +if (cq_dser & CQDSER_ERR) cq_dser = cq_dser | CQDSER_LST; +cq_dser = cq_dser | CQDSER_SNX; /* slave nxm */ +cq_sear = (pa >> VA_V_VPN) & CQSEAR_MASK; +return; +} + +/* Reset I/O bus */ + +void ioreset_wr (int32 data) +{ +reset_all (5); /* from qba on... */ +return; +} + +/* Powerup CQBIC */ + +t_stat qba_powerup (void) +{ +cq_mbr = 0; +cq_scr = CQSCR_POK; +return SCPE_OK; +} + +/* Reset CQBIC */ + +t_stat qba_reset (DEVICE *dptr) +{ +int32 i; + +if (sim_switches & SWMASK ('P')) qba_powerup (); +cq_scr = (cq_scr & CQSCR_BHL) | CQSCR_POK; +cq_dser = cq_mear = cq_sear = cq_ipc = 0; +for (i = 0; i < IPL_HLVL; i++) int_req[i] = 0; +return SCPE_OK; +} + +/* Qbus I/O buffer routines, aligned access + + Map_ReadB - fetch byte buffer from memory + Map_ReadW - fetch word buffer from memory + Map_WriteB - store byte buffer into memory + Map_WriteW - store word buffer into memory +*/ + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) +{ +int32 i; +uint32 ma, dat; + +if ((ba | bc) & 03) { /* check alignment */ + for (i = ma = 0; i < bc; i++, buf++) { /* by bytes */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + *buf = ReadB (ma); + ma = ma + 1; + } + } +else { + for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = ReadL (ma); /* get lw */ + *buf++ = dat & BMASK; /* low 8b */ + *buf++ = (dat >> 8) & BMASK; /* next 8b */ + *buf++ = (dat >> 16) & BMASK; /* next 8b */ + *buf = (dat >> 24) & BMASK; + ma = ma + 4; + } + } +return 0; +} + +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) +{ +int32 i; +uint32 ma,dat; + +ba = ba & ~01; +bc = bc & ~01; +if ((ba | bc) & 03) { /* check alignment */ + for (i = ma = 0; i < bc; i = i + 2, buf++) { /* by words */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + *buf = ReadW (ma); + ma = ma + 2; + } + } +else { + for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = ReadL (ma); /* get lw */ + *buf++ = dat & WMASK; /* low 16b */ + *buf = (dat >> 16) & WMASK; /* high 16b */ + ma = ma + 4; + } + } +return 0; +} + +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) +{ +int32 i; +uint32 ma, dat; + +if ((ba | bc) & 03) { /* check alignment */ + for (i = ma = 0; i < bc; i++, buf++) { /* by bytes */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + WriteB (ma, *buf); + ma = ma + 1; + } + } +else { + for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = (uint32) *buf++; /* get low 8b */ + dat = dat | (((uint32) *buf++) << 8); /* merge next 8b */ + dat = dat | (((uint32) *buf++) << 16); /* merge next 8b */ + dat = dat | (((uint32) *buf) << 24); /* merge hi 8b */ + WriteL (ma, dat); /* store lw */ + ma = ma + 4; + } + } +return 0; +} + +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) +{ +int32 i; +uint32 ma, dat; + +ba = ba & ~01; +bc = bc & ~01; +if ((ba | bc) & 03) { /* check alignment */ + for (i = ma = 0; i < bc; i = i + 2, buf++) { /* by words */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + WriteW (ma, *buf); + ma = ma + 2; + } + } +else { + for (i = ma = 0; i < bc; i = i + 4, buf++) { /* by longwords */ + if ((ma & VA_M_OFF) == 0) { /* need map? */ + if (!qba_map_addr (ba + i, &ma)) /* inv or NXM? */ + return (bc - i); + } + dat = (uint32) *buf++; /* get low 16b */ + dat = dat | (((uint32) *buf) << 16); /* merge hi 16b */ + WriteL (ma, dat); /* store lw */ + ma = ma + 4; + } + } +return 0; +} + +/* Memory examine via map (word only) */ + +t_stat qba_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 qa = (uint32) exta, pa; + +if ((vptr == NULL) || (qa >= CQMSIZE)) return SCPE_ARG; +if (qba_map_addr_c (qa, &pa) && ADDR_IS_MEM (pa)) { + *vptr = (uint32) ReadW (pa); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Memory deposit via map (word only) */ + +t_stat qba_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 qa = (uint32) exta, pa; + +if (qa >= CQMSIZE) return SCPE_ARG; +if (qba_map_addr_c (qa, &pa) && ADDR_IS_MEM (pa)) { + WriteW (pa, (int32) val); + return SCPE_OK; + } +return SCPE_NXM; +} + +/* Enable/disable autoconfiguration */ + +t_stat set_autocon (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (cptr != NULL) return SCPE_ARG; +autcon_enb = val; +return auto_config (NULL, 0); +} + +/* Show autoconfiguration status */ + +t_stat show_autocon (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, "autoconfiguration "); +fprintf (st, autcon_enb? "enabled": "disabled"); +return SCPE_OK; +} + +/* Change device address */ + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newba; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if ((val == 0) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newba = (uint32) get_uint (cptr, 16, IOPAGEBASE+IOPAGEMASK, &r); /* get new */ +if (r != SCPE_OK) return r; +if ((newba <= IOPAGEBASE) || /* must be > 0 */ + (newba % ((uint32) val))) return SCPE_ARG; /* check modulus */ +dibp->ba = newba; /* store */ +dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ +autcon_enb = 0; /* autoconfig off */ +return SCPE_OK; +} + +/* Show device address */ + +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +DEVICE *dptr; +DIB *dibp; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if ((dibp == NULL) || (dibp->ba <= IOPAGEBASE)) return SCPE_IERR; +fprintf (st, "address=%08X", dibp->ba); +if (dibp->lnt > 1) + fprintf (st, "-%08X", dibp->ba + dibp->lnt - 1); +if (dptr->flags & DEV_FLTA) fprintf (st, "*"); +return SCPE_OK; +} + +/* Set address floating */ + +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +DEVICE *dptr; + +if (cptr == NULL) return SCPE_ARG; +if ((val == 0) || (uptr == NULL)) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dptr->flags = dptr->flags | DEV_FLTA; /* floating */ +return auto_config (NULL, 0); /* autoconfigure */ +} + +/* Change device vector */ + +t_stat set_vec (UNIT *uptr, int32 arg, char *cptr, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 newvec; +t_stat r; + +if (cptr == NULL) return SCPE_ARG; +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +newvec = (uint32) get_uint (cptr, 16, VEC_Q + 01000, &r); +if ((r != SCPE_OK) || (newvec <= VEC_Q) || + ((newvec + (dibp->vnum * 4)) >= (VEC_Q + 01000)) || + (newvec & ((dibp->vnum > 1)? 07: 03))) return SCPE_ARG; +dibp->vec = newvec; +dptr->flags = dptr->flags & ~DEV_FLTA; /* not floating */ +autcon_enb = 0; /* autoconfig off */ +return SCPE_OK; +} + +/* Show device vector */ + +t_stat show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) +{ +DEVICE *dptr; +DIB *dibp; +uint32 vec, numvec; + +if (uptr == NULL) return SCPE_IERR; +dptr = find_dev_from_unit (uptr); +if (dptr == NULL) return SCPE_IERR; +dibp = (DIB *) dptr->ctxt; +if (dibp == NULL) return SCPE_IERR; +vec = dibp->vec; +if (arg) numvec = arg; +else numvec = dibp->vnum; +if (vec == 0) fprintf (st, "no vector"); +else { + fprintf (st, "vector=%X", vec); + if (numvec > 1) fprintf (st, "-%X", vec + (4 * (numvec - 1))); + } +return SCPE_OK; +} + +/* Build dispatch tables */ + +t_stat build_dsp_tab (DEVICE *dptr, DIB *dibp) +{ +uint32 i, idx; + +if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */ +for (i = 0; i < dibp->lnt; i = i + 2) { /* create entries */ + idx = ((dibp->ba + i) & IOPAGEMASK) >> 1; /* index into disp */ + if ((iodispR[idx] && dibp->rd && /* conflict? */ + (iodispR[idx] != dibp->rd)) || + (iodispW[idx] && dibp->wr && + (iodispW[idx] != dibp->wr))) { + printf ("Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + if (sim_log) fprintf (sim_log, + "Device %s address conflict at %08o\n", + sim_dname (dptr), dibp->ba); + return SCPE_STOP; + } + if (dibp->rd) iodispR[idx] = dibp->rd; /* set rd dispatch */ + if (dibp->wr) iodispW[idx] = dibp->wr; /* set wr dispatch */ + iodibp[idx] = dibp; /* remember DIB */ + } +return SCPE_OK; +} + + +/* Build interrupt tables */ + +t_stat build_int_vec (DEVICE *dptr, DIB *dibp) +{ +int32 i, idx, vec, ilvl, ibit; + +if ((dptr == NULL) || (dibp == NULL)) return SCPE_IERR; /* validate args */ +if (dibp->vnum > VEC_DEVMAX) return SCPE_IERR; +for (i = 0; i < dibp->vnum; i++) { /* loop thru vec */ + idx = dibp->vloc + i; /* vector index */ + vec = dibp->vec? (dibp->vec + (i * 4)): 0; /* vector addr */ + ilvl = idx / 32; + ibit = idx % 32; + if ((int_ack[ilvl][ibit] && dibp->ack[i] && /* conflict? */ + (int_ack[ilvl][ibit] != dibp->ack[i])) || + (int_vec[ilvl][ibit] && vec && + (int_vec[ilvl][ibit] != vec))) { + printf ("Device %s interrupt slot conflict at %d\n", + sim_dname (dptr), idx); + if (sim_log) fprintf (sim_log, + "Device %s interrupt slot conflict at %d\n", + sim_dname (dptr), idx); + return SCPE_STOP; + } + if (dibp->ack[i]) int_ack[ilvl][ibit] = dibp->ack[i]; + else if (vec) int_vec[ilvl][ibit] = vec; + } +return SCPE_OK; +} + +/* Build dib_tab from device list */ + +t_stat build_dib_tab (void) +{ +int32 i, j; +DEVICE *dptr; +DIB *dibp; +t_stat r; + +for (i = 0; i < IPL_HLVL; i++) { /* clear int tables */ + for (j = 0; j < 32; j++) { + int_vec[i][j] = 0; + int_ack[i][j] = NULL; + } + } +for (i = 0; i < (IOPAGESIZE >> 1); i++) { /* clear dispatch tab */ + iodispR[i] = NULL; + iodispW[i] = NULL; + iodibp[i] = NULL; + } +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp && !(dptr->flags & DEV_DIS)) { /* defined, enabled? */ + if (r = build_int_vec (dptr, dibp)) /* add to intr tab */ + return r; + if (r = build_dsp_tab (dptr, dibp)) /* add to dispatch tab */ + return r; + } /* end if enabled */ + } /* end for */ +return SCPE_OK; +} + +/* Show IO space */ + +t_stat show_iospace (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +uint32 i, j; +DEVICE *dptr; +DIB *dibp; + +if (build_dib_tab ()) return SCPE_OK; /* build IO page */ +for (i = 0, dibp = NULL; i < (IOPAGESIZE >> 1); i++) { /* loop thru entries */ + if (iodibp[i] && (iodibp[i] != dibp)) { /* new block? */ + dibp = iodibp[i]; /* DIB for block */ + for (j = 0, dptr = NULL; sim_devices[j] != NULL; j++) { + if (((DIB*) sim_devices[j]->ctxt) == dibp) { + dptr = sim_devices[j]; /* locate device */ + break; + } /* end if */ + } /* end for j */ + fprintf (st, "%08X - %08X%c\t%s\n", dibp->ba, + dibp->ba + dibp->lnt - 1, + (dptr && (dptr->flags & DEV_FLTA))? '*': ' ', + dptr? sim_dname (dptr): "CPU"); + } /* end if */ + } /* end for i */ +return SCPE_OK; +} + +/* Show QBA virtual address */ + +t_stat qba_show_virt (FILE *of, UNIT *uptr, int32 val, void *desc) +{ +t_stat r; +char *cptr = (char *) desc; +uint32 qa, pa; + +if (cptr) { + qa = (uint32) get_uint (cptr, 16, CQMSIZE - 1, &r); + if (r == SCPE_OK) { + if (qba_map_addr_c (qa, &pa)) + fprintf (of, "Qbus %-X = physical %-X\n", qa, pa); + else fprintf (of, "Qbus %-X: invalid mapping\n", qa); + return SCPE_OK; + } + } +fprintf (of, "Invalid argument\n"); +return SCPE_OK; +} + +/* Autoconfiguration + + The table reflects the MicroVAX 3900 microcode, with one addition - the + number of controllers field handles devices where multiple instances + are simulated through a single DEVICE structure (e.g., DZ, VH). + + A minus number of vectors indicates a field that should be calculated + but not placed in the DIB (RQ, TQ dynamic vectors) */ + +#define AUTO_MAXC 4 +#define AUTO_CSRBASE 0010 +#define AUTO_VECBASE 0300 + +typedef struct { + char *dnam[AUTO_MAXC]; + int32 numc; + int32 numv; + uint32 amod; + uint32 vmod; + uint32 fixa[AUTO_MAXC]; + uint32 fixv[AUTO_MAXC]; + } AUTO_CON; + +AUTO_CON auto_tab[] = { + { { NULL }, 1, 2, 0, 8, { 0 } }, /* DLV11J - fx CSRs */ + { { NULL }, 1, 2, 8, 8 }, /* DJ11 */ + { { NULL }, 1, 2, 16, 8 }, /* DH11 */ + { { NULL }, 1, 2, 8, 8 }, /* DQ11 */ + { { NULL }, 1, 2, 8, 8 }, /* DU11 */ + { { NULL }, 1, 2, 8, 8 }, /* DUP11 */ + { { NULL }, 10, 2, 8, 8 }, /* LK11A */ + { { NULL }, 1, 2, 8, 8 }, /* DMC11 */ + { { "DZ" }, DZ_MUXES, 2, 8, 8 }, /* DZ11 */ + { { NULL }, 1, 2, 8, 8 }, /* KMC11 */ + { { NULL }, 1, 2, 8, 8 }, /* LPP11 */ + { { NULL }, 1, 2, 8, 8 }, /* VMV21 */ + { { NULL }, 1, 2, 16, 8 }, /* VMV31 */ + { { NULL }, 1, 2, 8, 8 }, /* DWR70 */ + { { "RL", "RLB" }, 1, 1, 8, 4, {IOBA_RL}, {VEC_RL} }, /* RL11 */ + { { "TS", "TSB", "TSC", "TSD" }, 1, 1, 0, 4, /* TS11 */ + {IOBA_TS, IOBA_TS + 4, IOBA_TS + 8, IOBA_TS + 12}, + {VEC_TS} }, + { { NULL }, 1, 2, 16, 8 }, /* LPA11K */ + { { NULL }, 1, 2, 8, 8 }, /* KW11C */ + { { NULL }, 1, 1, 8, 8 }, /* reserved */ + { { "RX", "RY" }, 1, 1, 8, 4, {IOBA_RX} , {VEC_RX} }, /* RX11/RX211 */ + { { NULL }, 1, 1, 8, 4 }, /* DR11W */ + { { NULL }, 1, 1, 8, 4, { 0, 0 }, { 0 } }, /* DR11B - fx CSRs,vec */ + { { NULL }, 1, 2, 8, 8 }, /* DMP11 */ + { { NULL }, 1, 2, 8, 8 }, /* DPV11 */ + { { NULL }, 1, 2, 8, 8 }, /* ISB11 */ + { { NULL }, 1, 2, 16, 8 }, /* DMV11 */ + { { "XU", "XUB" }, 1, 1, 8, 4, {IOBA_XU}, {VEC_XU} }, /* DEUNA */ + { { "XQ", "XQB" }, 1, 1, 0, 4, /* DEQNA */ + {IOBA_XQ,IOBA_XQB}, {VEC_XQ} }, + { { "RQ", "RQB", "RQC", "RQD" }, 1, -1, 4, 4, /* RQDX3 */ + {IOBA_RQ}, {VEC_RQ} }, + { { NULL }, 1, 8, 32, 4 }, /* DMF32 */ + { { NULL }, 1, 2, 16, 8 }, /* KMS11 */ + { { NULL }, 1, 1, 16, 4 }, /* VS100 */ + { { "TQ", "TQB" }, 1, -1, 4, 4, {IOBA_TQ}, {VEC_TQ} }, /* TQK50 */ + { { NULL }, 1, 2, 16, 8 }, /* KMV11 */ + { { "VH" }, VH_MUXES, 2, 16, 8 }, /* DHU11/DHQ11 */ + { { NULL }, 1, 6, 32, 4 }, /* DMZ32 */ + { { NULL }, 1, 6, 32, 4 }, /* CP132 */ + { { NULL }, 1, 2, 64, 8, { 0 } }, /* QVSS - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* VS31 */ + { { NULL }, 1, 1, 0, 4, { 0 } }, /* LNV11 - fx CSR */ + { { NULL }, 1, 1, 16, 4 }, /* LNV21/QPSS */ + { { NULL }, 1, 1, 8, 4, { 0 } }, /* QTA - fx CSR */ + { { NULL }, 1, 1, 8, 4 }, /* DSV11 */ + { { NULL }, 1, 2, 8, 8 }, /* CSAM */ + { { NULL }, 1, 2, 8, 8 }, /* ADV11C */ + { { NULL }, 1, 0, 8, 0 }, /* AAV11C */ + { { NULL }, 1, 2, 8, 8, { 0 }, { 0 } }, /* AXV11C - fx CSR,vec */ + { { NULL }, 1, 2, 4, 8, { 0 } }, /* KWV11C - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* ADV11D - fx CSR */ + { { NULL }, 1, 2, 8, 8, { 0 } }, /* AAV11D - fx CSR */ + { { "QDSS" }, 1, 3, 0, 16, {IOBA_QDSS} }, /* QDSS - fx CSR */ + { { NULL }, -1 } /* end table */ +}; + +t_stat auto_config (char *name, int32 nctrl) +{ +uint32 csr = IOPAGEBASE + AUTO_CSRBASE; +uint32 vec = VEC_Q + AUTO_VECBASE; +AUTO_CON *autp; +DEVICE *dptr; +DIB *dibp; +uint32 j, k, vmask, amask; + +if (autcon_enb == 0) return SCPE_OK; /* enabled? */ +if (name) { /* updating? */ + if (nctrl < 0) return SCPE_ARG; + for (autp = auto_tab; autp->numc >= 0; autp++) { + for (j = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { + if (strcmp (name, autp->dnam[j]) == 0) + autp->numc = nctrl; + } + } + } +for (autp = auto_tab; autp->numc >= 0; autp++) { /* loop thru table */ + if (autp->amod) { /* floating csr? */ + amask = autp->amod - 1; + csr = (csr + amask) & ~amask; /* align csr */ + } + for (j = k = 0; (j < AUTO_MAXC) && autp->dnam[j]; j++) { + dptr = find_dev (autp->dnam[j]); /* find ctrl */ + if ((dptr == NULL) || (dptr->flags & DEV_DIS) || + !(dptr->flags & DEV_FLTA)) continue; /* enabled, floating? */ + dibp = (DIB *) dptr->ctxt; /* get DIB */ + if (dibp == NULL) return SCPE_IERR; /* not there??? */ + if (autp->amod) { /* dyn csr needed? */ + if (autp->fixa[k]) /* fixed csr avail? */ + dibp->ba = autp->fixa[k]; /* use it */ + else { /* no fixed left */ + dibp->ba = csr; /* set CSR */ + csr += (autp->numc * autp->amod); /* next CSR */ + } /* end else */ + } /* end if dyn csr */ + if (autp->numv && autp->vmod) { /* dyn vec needed? */ + uint32 numv = abs (autp->numv); /* get num vec */ + if (autp->fixv[k]) { /* fixed vec avail? */ + if (autp->numv > 0) + dibp->vec = autp->fixv[k]; /* use it */ + } + else { /* no fixed left */ + vmask = autp->vmod - 1; + vec = (vec + vmask) & ~vmask; /* align vector */ + if (autp->numv > 0) + dibp->vec = vec; /* set vector */ + vec += (autp->numc * numv * 4); + } /* end else */ + } /* end if dyn vec */ + k++; /* next instance */ + } /* end for j */ + if (autp->amod) csr = csr + 2; /* flt CSR? gap */ + } /* end for i */ +return SCPE_OK; +} diff --git a/VAX/vax_mmu.c b/VAX/vax_mmu.c new file mode 100644 index 0000000..be4ff63 --- /dev/null +++ b/VAX/vax_mmu.c @@ -0,0 +1,583 @@ +/* vax_mmu.c - VAX memory management + + Copyright (c) 1998-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 28-May-08 RMS Inlined physical memory routines + 29-Apr-07 RMS Added address masking for system page table reads + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 30-Sep-04 RMS Comment and formating changes + 19-Sep-03 RMS Fixed upper/lower case linkage problems on VMS + 01-Jun-03 RMS Fixed compilation problem with USE_ADDR64 + + This module contains the instruction simulators for + + Read - read virtual + Write - write virtual + ReadL(P) - read aligned physical longword (physical context) + WriteL(P) - write aligned physical longword (physical context) + ReadB(W) - read aligned physical byte (word) + WriteB(W) - write aligned physical byte (word) + Test - test acccess + + zap_tb - clear TB + zap_tb_ent - clear TB entry + chk_tb_ent - check TB entry + set_map_reg - set up working map registers +*/ + +#include "vax_defs.h" +#include + +typedef struct { + int32 tag; /* tag */ + int32 pte; /* pte */ + } TLBENT; + +extern uint32 *M; +extern const uint32 align[4]; +extern int32 PSL; +extern int32 mapen; +extern int32 p1, p2; +extern int32 P0BR, P0LR; +extern int32 P1BR, P1LR; +extern int32 SBR, SLR; +extern int32 SISR; +extern jmp_buf save_env; +extern UNIT cpu_unit; + +int32 d_p0br, d_p0lr; /* dynamic copies */ +int32 d_p1br, d_p1lr; /* altered per ucode */ +int32 d_sbr, d_slr; +extern int32 mchk_va, mchk_ref; /* for mcheck */ +TLBENT stlb[VA_TBSIZE], ptlb[VA_TBSIZE]; +static const int32 insert[4] = { + 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF + }; +static const int32 cvtacc[16] = { 0, 0, + TLB_ACCW (KERN)+TLB_ACCR (KERN), + TLB_ACCR (KERN), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+TLB_ACCW (USER)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCR (KERN)+TLB_ACCR (EXEC), + TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC), + TLB_ACCR (KERN)+TLB_ACCR (EXEC), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCW (KERN)+TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+TLB_ACCW (SUPV)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCW (KERN)+TLB_ACCW (EXEC)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCW (KERN)+ + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER), + TLB_ACCR (KERN)+TLB_ACCR (EXEC)+TLB_ACCR (SUPV)+TLB_ACCR (USER) + }; + +t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +t_stat tlb_reset (DEVICE *dptr); + +TLBENT fill (uint32 va, int32 lnt, int32 acc, int32 *stat); +extern int32 ReadIO (uint32 pa, int32 lnt); +extern void WriteIO (uint32 pa, int32 val, int32 lnt); +extern int32 ReadReg (uint32 pa, int32 lnt); +extern void WriteReg (uint32 pa, int32 val, int32 lnt); + +/* TLB data structures + + tlb_dev pager device descriptor + tlb_unit pager units + pager_reg pager register list +*/ + +UNIT tlb_unit[] = { + { UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2) }, + { UDATA (NULL, UNIT_FIX, VA_TBSIZE * 2) } + }; + +REG tlb_reg[] = { + { NULL } + }; + +DEVICE tlb_dev = { + "TLB", tlb_unit, tlb_reg, NULL, + 2, 16, VA_N_TBI * 2, 1, 16, 32, + &tlb_ex, &tlb_dep, &tlb_reset, + NULL, NULL, NULL + }; + +/* Read and write virtual + + These routines logically fall into three phases: + + 1. Look up the virtual address in the translation buffer, calling + the fill routine on a tag mismatch or access mismatch (invalid + tlb entries have access = 0 and thus always mismatch). The + fill routine handles all errors. If the resulting physical + address is aligned, do an aligned physical read or write. + 2. Test for unaligned across page boundaries. If cross page, look + up the physical address of the second page. If not cross page, + the second physical address is the same as the first. + 3. Using the two physical addresses, do an unaligned read or + write, with three cases: unaligned long, unaligned word within + a longword, unaligned word crossing a longword boundary. + + Note that these routines do not handle quad or octa references. +*/ + +/* Read virtual + + Inputs: + va = virtual address + lnt = length code (BWL) + acc = access code (KESU) + Output: + returned data, right justified in 32b longword +*/ + +int32 Read (uint32 va, int32 lnt, int32 acc) +{ +int32 vpn, off, tbi, pa; +int32 pa1, bo, sc, wl, wh; +TLBENT xpte; + +mchk_va = va; +if (mapen) { /* mapping on? */ + vpn = VA_GETVPN (va); /* get vpn, offset */ + off = VA_GETOFF (va); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || + ((acc & TLB_WACC) && ((xpte.pte & TLB_M) == 0))) + xpte = fill (va, lnt, acc, NULL); /* fill if needed */ + pa = (xpte.pte & TLB_PFN) | off; /* get phys addr */ + } +else pa = va & PAMASK; +if ((pa & (lnt - 1)) == 0) { /* aligned? */ + if (lnt >= L_LONG) return ReadL (pa); /* long, quad? */ + if (lnt == L_WORD) return ReadW (pa); /* word? */ + return ReadB (pa); /* byte */ + } +if (mapen && ((off + lnt) > VA_PAGSIZE)) { /* cross page? */ + vpn = VA_GETVPN (va + lnt); /* vpn 2nd page */ + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || + ((acc & TLB_WACC) && ((xpte.pte & TLB_M) == 0))) + xpte = fill (va + lnt, lnt, acc, NULL); /* fill if needed */ + pa1 = (xpte.pte & TLB_PFN) | VA_GETOFF (va + 4); + } +else pa1 = (pa + 4) & PAMASK; /* not cross page */ +bo = pa & 3; +if (lnt >= L_LONG) { /* lw unaligned? */ + sc = bo << 3; + wl = ReadL (pa); /* read both lw */ + wh = ReadL (pa1); /* extract */ + return ((((wl >> sc) & align[bo]) | (wh << (32 - sc))) & LMASK); + } +else if (bo == 1) return ((ReadL (pa) >> 8) & WMASK); +else { + wl = ReadL (pa); /* word cross lw */ + wh = ReadL (pa1); /* read, extract */ + return (((wl >> 24) & 0xFF) | ((wh & 0xFF) << 8)); + } +} + +/* Write virtual + + Inputs: + va = virtual address + val = data to be written, right justified in 32b lw + lnt = length code (BWL) + acc = access code (KESU) + Output: + none +*/ + +void Write (uint32 va, int32 val, int32 lnt, int32 acc) +{ +int32 vpn, off, tbi, pa; +int32 pa1, bo, sc, wl, wh; +TLBENT xpte; + +mchk_va = va; +if (mapen) { + vpn = VA_GETVPN (va); + off = VA_GETOFF (va); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || + ((xpte.pte & TLB_M) == 0)) + xpte = fill (va, lnt, acc, NULL); + pa = (xpte.pte & TLB_PFN) | off; + } +else pa = va & PAMASK; +if ((pa & (lnt - 1)) == 0) { /* aligned? */ + if (lnt >= L_LONG) WriteL (pa, val); /* long, quad? */ + else if (lnt == L_WORD) WriteW (pa, val); /* word? */ + else WriteB (pa, val); /* byte */ + return; + } +if (mapen && ((off + lnt) > VA_PAGSIZE)) { + vpn = VA_GETVPN (va + 4); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if (((xpte.pte & acc) == 0) || (xpte.tag != vpn) || + ((xpte.pte & TLB_M) == 0)) + xpte = fill (va + lnt, lnt, acc, NULL); + pa1 = (xpte.pte & TLB_PFN) | VA_GETOFF (va + 4); + } +else pa1 = (pa + 4) & PAMASK; +bo = pa & 3; +wl = ReadL (pa); +if (lnt >= L_LONG) { + sc = bo << 3; + wh = ReadL (pa1); + wl = (wl & insert[bo]) | ((val << sc) & LMASK); + wh = (wh & ~insert[bo]) | ((val >> (32 - sc)) & insert[bo]); + WriteL (pa, wl); + WriteL (pa1, wh); + } +else if (bo == 1) { + wl = (wl & 0xFF0000FF) | (val << 8); + WriteL (pa, wl); + } +else { + wh = ReadL (pa1); + wl = (wl & 0x00FFFFFF) | ((val & 0xFF) << 24); + wh = (wh & 0xFFFFFF00) | ((val >> 8) & 0xFF); + WriteL (pa, wl); + WriteL (pa1, wh); + } +return; +} + +/* Test access to a byte (VAX PROBEx) */ + +int32 Test (uint32 va, int32 acc, int32 *status) +{ +int32 vpn, off, tbi; +TLBENT xpte; + +*status = PR_OK; /* assume ok */ +if (mapen) { /* mapping on? */ + vpn = VA_GETVPN (va); /* get vpn, off */ + off = VA_GETOFF (va); + tbi = VA_GETTBI (vpn); + xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; /* access tlb */ + if ((xpte.pte & acc) && (xpte.tag == vpn)) /* TB hit, acc ok? */ + return (xpte.pte & TLB_PFN) | off; + xpte = fill (va, L_BYTE, acc, status); /* fill TB */ + if (*status == PR_OK) return (xpte.pte & TLB_PFN) | off; + else return -1; + } +return va & PAMASK; /* ret phys addr */ +} + +/* Read aligned physical (in virtual context, unless indicated) + + Inputs: + pa = physical address, naturally aligned + Output: + returned data, right justified in 32b longword +*/ + +SIM_INLINE int32 ReadB (uint32 pa) +{ +int32 dat; + +if (ADDR_IS_MEM (pa)) dat = M[pa >> 2]; +else { + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) dat = ReadIO (pa, L_BYTE); + else dat = ReadReg (pa, L_BYTE); + } +return ((dat >> ((pa & 3) << 3)) & BMASK); +} + +SIM_INLINE int32 ReadW (uint32 pa) +{ +int32 dat; + +if (ADDR_IS_MEM (pa)) dat = M[pa >> 2]; +else { + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) dat = ReadIO (pa, L_WORD); + else dat = ReadReg (pa, L_WORD); + } +return ((dat >> ((pa & 2)? 16: 0)) & WMASK); +} + +SIM_INLINE int32 ReadL (uint32 pa) +{ +if (ADDR_IS_MEM (pa)) return M[pa >> 2]; +mchk_ref = REF_V; +if (ADDR_IS_IO (pa)) return ReadIO (pa, L_LONG); +return ReadReg (pa, L_LONG); +} + +SIM_INLINE int32 ReadLP (uint32 pa) +{ +if (ADDR_IS_MEM (pa)) return M[pa >> 2]; +mchk_va = pa; +mchk_ref = REF_P; +if (ADDR_IS_IO (pa)) return ReadIO (pa, L_LONG); +return ReadReg (pa, L_LONG); +} + +/* Write aligned physical (in virtual context, unless indicated) + + Inputs: + pa = physical address, naturally aligned + val = data to be written, right justified in 32b longword + Output: + none +*/ + +SIM_INLINE void WriteB (uint32 pa, int32 val) +{ +if (ADDR_IS_MEM (pa)) { + int32 id = pa >> 2; + int32 sc = (pa & 3) << 3; + int32 mask = 0xFF << sc; + M[id] = (M[id] & ~mask) | (val << sc); + } +else { + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_BYTE); + else WriteReg (pa, val, L_BYTE); + } +return; +} + +SIM_INLINE void WriteW (uint32 pa, int32 val) +{ +if (ADDR_IS_MEM (pa)) { + int32 id = pa >> 2; + M[id] = (pa & 2)? (M[id] & 0xFFFF) | (val << 16): + (M[id] & ~0xFFFF) | val; + } +else { + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_WORD); + else WriteReg (pa, val, L_WORD); + } +return; +} + +SIM_INLINE void WriteL (uint32 pa, int32 val) +{ +if (ADDR_IS_MEM (pa)) M[pa >> 2] = val; +else { + mchk_ref = REF_V; + if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_LONG); + else WriteReg (pa, val, L_LONG); + } +return; +} + +void WriteLP (uint32 pa, int32 val) +{ +if (ADDR_IS_MEM (pa)) M[pa >> 2] = val; +else { + mchk_va = pa; + mchk_ref = REF_P; + if (ADDR_IS_IO (pa)) WriteIO (pa, val, L_LONG); + else WriteReg (pa, val, L_LONG); + } +return; +} + +/* TLB fill + + This routine fills the TLB after a tag or access mismatch, or + on a write if pte = 0. It fills the TLB and returns the + pte to the caller. On an error, it aborts directly to the + fault handler in the CPU. + + If called from map (VAX PROBEx), the error status is returned + to the caller, and no fault occurs. +*/ + +#define MM_ERR(param) { \ + if (stat) { *stat = param; return zero_pte; } \ + p1 = MM_PARAM (acc & TLB_WACC, param); \ + p2 = va; \ + ABORT ((param & PR_TNV)? ABORT_TNV: ABORT_ACV); } + +TLBENT fill (uint32 va, int32 lnt, int32 acc, int32 *stat) +{ +int32 ptidx = (((uint32) va) >> 7) & ~03; +int32 tlbpte, ptead, pte, tbi, vpn; +static TLBENT zero_pte = { 0, 0 }; + +if (va & VA_S0) { /* system space? */ + if (ptidx >= d_slr) /* system */ + MM_ERR (PR_LNV); + ptead = (d_sbr + ptidx) & PAMASK; + } +else { + if (va & VA_P1) { /* P1? */ + if (ptidx < d_p1lr) MM_ERR (PR_LNV); + ptead = d_p1br + ptidx; + } + else { /* P0 */ + if (ptidx >= d_p0lr) MM_ERR (PR_LNV); + ptead = d_p0br + ptidx; + } + if ((ptead & VA_S0) == 0) + ABORT (STOP_PPTE); /* ppte must be sys */ + vpn = VA_GETVPN (ptead); /* get vpn, tbi */ + tbi = VA_GETTBI (vpn); + if (stlb[tbi].tag != vpn) { /* in sys tlb? */ + ptidx = ((uint32) ptead) >> 7; /* xlate like sys */ + if (ptidx >= d_slr) + MM_ERR (PR_PLNV); + pte = ReadLP ((d_sbr + ptidx) & PAMASK); /* get system pte */ +#if defined (VAX_780) + if ((pte & PTE_ACC) == 0) MM_ERR (PR_PACV); /* spte ACV? */ +#endif + if ((pte & PTE_V) == 0) MM_ERR (PR_PTNV); /* spte TNV? */ + stlb[tbi].tag = vpn; /* set stlb tag */ + stlb[tbi].pte = cvtacc[PTE_GETACC (pte)] | + ((pte << VA_N_OFF) & TLB_PFN); /* set stlb data */ + } + ptead = (stlb[tbi].pte & TLB_PFN) | VA_GETOFF (ptead); + } +pte = ReadL (ptead); /* read pte */ +tlbpte = cvtacc[PTE_GETACC (pte)] | /* cvt access */ + ((pte << VA_N_OFF) & TLB_PFN); /* set addr */ +if ((tlbpte & acc) == 0) MM_ERR (PR_ACV); /* chk access */ +if ((pte & PTE_V) == 0) MM_ERR (PR_TNV); /* check valid */ +if (acc & TLB_WACC) { /* write? */ + if ((pte & PTE_M) == 0) WriteL (ptead, pte | PTE_M); + tlbpte = tlbpte | TLB_M; /* set M */ + } +vpn = VA_GETVPN (va); +tbi = VA_GETTBI (vpn); +if ((va & VA_S0) == 0) { /* process space? */ + ptlb[tbi].tag = vpn; /* store tlb ent */ + ptlb[tbi].pte = tlbpte; + return ptlb[tbi]; + } +stlb[tbi].tag = vpn; /* system space */ +stlb[tbi].pte = tlbpte; /* store tlb ent */ +return stlb[tbi]; +} + +/* Utility routines */ + +extern void set_map_reg (void) +{ +d_p0br = P0BR & ~03; +d_p1br = (P1BR - 0x800000) & ~03; /* VA<30> >> 7 */ +d_sbr = (SBR - 0x1000000) & ~03; /* VA<31> >> 7 */ +d_p0lr = (P0LR << 2); +d_p1lr = (P1LR << 2) + 0x800000; /* VA<30> >> 7 */ +d_slr = (SLR << 2) + 0x1000000; /* VA<31> >> 7 */ +return; +} + +/* Zap process (0) or whole (1) tb */ + +void zap_tb (int stb) +{ +int32 i; + +for (i = 0; i < VA_TBSIZE; i++) { + ptlb[i].tag = ptlb[i].pte = -1; + if (stb) stlb[i].tag = stlb[i].pte = -1; + } +return; +} + +/* Zap single tb entry corresponding to va */ + +void zap_tb_ent (uint32 va) +{ +int32 tbi = VA_GETTBI (VA_GETVPN (va)); + +if (va & VA_S0) stlb[tbi].tag = stlb[tbi].pte = -1; +else ptlb[tbi].tag = ptlb[tbi].pte = -1; +return; +} + +/* Check for tlb entry corresponding to va */ + +t_bool chk_tb_ent (uint32 va) +{ +int32 vpn = VA_GETVPN (va); +int32 tbi = VA_GETTBI (vpn); +TLBENT xpte; + +xpte = (va & VA_S0)? stlb[tbi]: ptlb[tbi]; +if (xpte.tag == vpn) return TRUE; +return FALSE; +} + +/* TLB examine */ + +t_stat tlb_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 tlbn = uptr - tlb_unit; +int32 idx = (uint32) addr >> 1; + +if (idx >= VA_TBSIZE) return SCPE_NXM; +if (addr & 1) *vptr = ((uint32) (tlbn? stlb[idx].pte: ptlb[idx].pte)); +else *vptr = ((uint32) (tlbn? stlb[idx].tag: ptlb[idx].tag)); +return SCPE_OK; +} + +/* TLB deposit */ + +t_stat tlb_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +int32 tlbn = uptr - tlb_unit; +int32 idx = (uint32) addr >> 1; + +if (idx >= VA_TBSIZE) return SCPE_NXM; +if (addr & 1) { + if (tlbn) stlb[idx].pte = (int32) val; + else ptlb[idx].pte = (int32) val; + } +else { + if (tlbn) stlb[idx].tag = (int32) val; + else ptlb[idx].tag = (int32) val; + } +return SCPE_OK; +} + +/* TLB reset */ + +t_stat tlb_reset (DEVICE *dptr) +{ +int32 i; + +for (i = 0; i < VA_TBSIZE; i++) + stlb[i].tag = ptlb[i].tag = stlb[i].pte = ptlb[i].pte = -1; +return SCPE_OK; +} diff --git a/VAX/vax_octa.c b/VAX/vax_octa.c new file mode 100644 index 0000000..bbed2a7 --- /dev/null +++ b/VAX/vax_octa.c @@ -0,0 +1,1188 @@ +/* vax_octa.c - VAX octaword and h_floating instructions + + Copyright (c) 2004-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module simulates the VAX h_floating instruction set. + + 28-May-08 RMS Inlined physical memory routines + 10-May-06 RMS Fixed bug in reported VA on faulting cross-page write + 03-May-06 RMS Fixed MNEGH to test negated sign, clear C + Fixed carry propagation in qp_inc, qp_neg, qp_add + Fixed pack routines to test for zero via fraction + Fixed ACBH to set cc's on result + Fixed POLYH to set R3 correctly + Fixed POLYH to not exit prematurely if arg = 0 + Fixed POLYH to mask mul reslt to 127b + Fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYH + Fixed EMODH to concatenate 15b of 16b extension + (all reported by Tim Stark) + 15-Jul-04 RMS Cloned from 32b VAX floating point implementation +*/ + +#include "vax_defs.h" + +#if defined (FULL_VAX) + +extern int32 R[16]; +extern int32 PSL; +extern int32 trpirq; +extern int32 p1; +extern jmp_buf save_env; + +extern int32 Test (uint32 va, int32 acc, int32 *status); + +#define WORDSWAP(x) ((((x) & WMASK) << 16) | (((x) >> 16) & WMASK)) + +typedef struct { + uint32 f0; /* low */ + uint32 f1; + uint32 f2; + uint32 f3; /* high */ + } UQP; + +typedef struct { + int32 sign; + int32 exp; + UQP frac; + } UFPH; + +#define UH_NM_H 0x80000000 /* normalized */ +#define UH_FRND 0x00000080 /* F round */ +#define UH_DRND 0x00000080 /* D round */ +#define UH_GRND 0x00000400 /* G round */ +#define UH_HRND 0x00004000 /* H round */ +#define UH_V_NM 127 + +int32 op_tsth (int32 val); +int32 op_cmph (int32 *hf1, int32 *hf2); +int32 op_cvtih (int32 val, int32 *hf); +int32 op_cvthi (int32 *hf, int32 *flg, int32 opc); +int32 op_cvtfdh (int32 vl, int32 vh, int32 *hf); +int32 op_cvtgh (int32 vl, int32 vh, int32 *hf); +int32 op_cvthfd (int32 *hf, int32 *vh); +int32 op_cvthg (int32 *hf, int32 *vh); +int32 op_addh (int32 *opnd, int32 *hf, t_bool sub); +int32 op_mulh (int32 *opnd, int32 *hf); +int32 op_divh (int32 *opnd, int32 *hf); +int32 op_emodh (int32 *opnd, int32 *hflt, int32 *intgr, int32 *flg); +void op_polyh (int32 *opnd, int32 acc); +void h_write_b (int32 spec, int32 va, int32 val, int32 acc); +void h_write_w (int32 spec, int32 va, int32 val, int32 acc); +void h_write_l (int32 spec, int32 va, int32 val, int32 acc); +void h_write_q (int32 spec, int32 va, int32 vl, int32 vh, int32 acc); +void h_write_o (int32 spec, int32 va, int32 *val, int32 acc); +void vax_hadd (UFPH *a, UFPH *b); +void vax_hmul (UFPH *a, UFPH *b, uint32 mlo); +void vax_hmod (UFPH *a, int32 *intgr, int32 *flg); +void vax_hdiv (UFPH *a, UFPH *b); +uint32 qp_add (UQP *a, UQP *b); +uint32 qp_sub (UQP *a, UQP *b); +void qp_inc (UQP *a); +void qp_lsh (UQP *a, uint32 sc); +void qp_rsh (UQP *a, uint32 sc); +void qp_rsh_s (UQP *a, uint32 sc, uint32 neg); +void qp_neg (UQP *a); +int32 qp_cmp (UQP *a, UQP *b); +void h_unpackfd (int32 hi, int32 lo, UFPH *a); +void h_unpackg (int32 hi, int32 lo, UFPH *a); +void h_unpackh (int32 *hflt, UFPH *a); +void h_normh (UFPH *a); +int32 h_rpackfd (UFPH *a, int32 *rl); +int32 h_rpackg (UFPH *a, int32 *rl); +int32 h_rpackh (UFPH *a, int32 *hflt); + +static int32 z_octa[4] = { 0, 0, 0, 0 }; + +/* Octaword instructions */ + +int32 op_octa (int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va) +{ +int32 r, rh, temp, flg; +int32 r_octa[4]; + +switch (opc) { + +/* PUSHAO + + opnd[0] = src.ao +*/ + + case PUSHAO: + Write (SP - 4, opnd[0], L_LONG, WA); /* push operand */ + SP = SP - 4; /* decr stack ptr */ + CC_IIZP_L (opnd[0]); /* set cc's */ + break; + +/* MOVAO + + opnd[0] = src.ro + opnd[1:2] = dst.wl + spec = last specifier + va = address if last specifier is memory +*/ + + case MOVAO: + h_write_l (spec, va, opnd[0], acc); /* write operand */ + CC_IIZP_L (opnd[0]); /* set cc's */ + break; + +/* CLRO + + opnd[0:1] = dst.wl + spec = last specifier + va = address if last specifier is memory +*/ + + case CLRO: + h_write_o (spec, va, z_octa, acc); /* write 0's */ + CC_ZZ1P; /* set cc's */ + break; + +/* TSTH + + opnd[0:3] = src.rh +*/ + + case TSTH: + r = op_tsth (opnd[0]); /* test for 0 */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* MOVO, MOVH, MNEGH + + opnd[0:3] = src.ro + opnd[4:5] = dst.wo + spec = last specifier + va = address if last specifier is memory +*/ + + case MOVO: + h_write_o (spec, va, opnd, acc); /* write src */ + CC_IIZP_O (opnd[0], opnd[1], opnd[2], opnd[3]); /* set cc's */ + break; + + case MOVH: + if (r = op_tsth (opnd[0])) { /* test for 0 */ + h_write_o (spec, va, opnd, acc); /* nz, write result */ + CC_IIZP_FP (r); /* set cc's */ + } + else { /* zero */ + h_write_o (spec, va, z_octa, acc); /* write 0 */ + cc = (cc & CC_C) | CC_Z; /* set cc's */ + } + break; + + case MNEGH: + if (r = op_tsth (opnd[0])) { /* test for 0 */ + opnd[0] = opnd[0] ^ FPSIGN; /* nz, invert sign */ + h_write_o (spec, va, opnd, acc); /* write result */ + CC_IIZZ_FP (opnd[0]); /* set cc's */ + } + else { /* zero */ + h_write_o (spec, va, z_octa, acc); /* write 0 */ + cc = CC_Z; /* set cc's */ + } + break; + +/* CMPH + + opnd[0:3] = src1.rh + opnd[4:7] = src2.rh +*/ + + case CMPH: + cc = op_cmph (opnd + 0, opnd + 4); /* set cc's */ + break; + +/* CVTBH, CVTWH, CVTLH + + opnd[0] = src.rx + opnd[1:2] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTBH: + r = op_cvtih (SXTB (opnd[0]), r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write reslt */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTWH: + r = op_cvtih (SXTW (opnd[0]), r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTLH: + r = op_cvtih (opnd[0], r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* CVTHB, CVTHW, CVTHL, CVTRHL + + opnd[0:3] = src.rh + opnd[4:5] = dst.wx + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTHB: + r = op_cvthi (opnd, &flg, opc) & BMASK; /* convert */ + h_write_b (spec, va, r, acc); /* write result */ + CC_IIZZ_B (r); /* set cc's */ + if (flg) { V_INTOV; } + break; + + case CVTHW: + r = op_cvthi (opnd, &flg, opc) & WMASK; /* convert */ + h_write_w (spec, va, r, acc); /* write result */ + CC_IIZZ_W (r); /* set cc's */ + if (flg) { V_INTOV; } + break; + + case CVTHL: case CVTRHL: + r = op_cvthi (opnd, &flg, opc) & LMASK; /* convert */ + h_write_l (spec, va, r, acc); /* write result */ + CC_IIZZ_L (r); /* set cc's */ + if (flg) { V_INTOV; } + break; + +/* CVTFH + + opnd[0] = src.rf + opnd[1:2] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTFH: + r = op_cvtfdh (opnd[0], 0, r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* CVTDH, CVTGH + + opnd[0:1] = src.rx + opnd[2:3] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTDH: + r = op_cvtfdh (opnd[0], opnd[1], r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTGH: + r = op_cvtgh (opnd[0], opnd[1], r_octa); /* convert */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* CVTHF, CVTHD, CVTHG + + opnd[0:3] = src.rh + opnd[4:5] = dst.wx + spec = last specifier + va = address if last specifier is memory +*/ + + case CVTHF: + r = op_cvthfd (opnd, NULL); /* convert */ + h_write_l (spec, va, r, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTHD: + r = op_cvthfd (opnd, &rh); /* convert */ + h_write_q (spec, va, r, rh, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case CVTHG: + r = op_cvthg (opnd, &rh); /* convert */ + h_write_q (spec, va, r, rh, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* ADDH2, SUBH2, MULH2, DIVH2 + + op[0:3] = src.rh + op[4:7] = dst.mh + spec = last specifier + va = address if last specifier is memory + + ADDH3, SUBH3, MULH3, DIVH3 + + op[0:3] = src1.rh + op[4:7] = src2.rh + op[8:9] = dst.wh + spec = last specifier + va = address if last specifier is memory +*/ + + + case ADDH2: case ADDH3: + r = op_addh (opnd, r_octa, FALSE); /* add */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case SUBH2: case SUBH3: + r = op_addh (opnd, r_octa, TRUE); /* subtract */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case MULH2: case MULH3: + r = op_mulh (opnd, r_octa); /* multiply */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + + case DIVH2: case DIVH3: + r = op_divh (opnd, r_octa); /* divide */ + h_write_o (spec, va, r_octa, acc); /* write result */ + CC_IIZZ_FP (r); /* set cc's */ + break; + +/* ACBH + + opnd[0:3] = limit.rh + opnd[4:7] = add.rh + opnd[8:11] = index.mh + spec = last specifier + va = last va + brdest = branch destination +*/ + + case ACBH: + r = op_addh (opnd + 4, r_octa, FALSE); /* add + index */ + CC_IIZP_FP (r); /* set cc's */ + temp = op_cmph (r_octa, opnd); /* result : limit */ + h_write_o (spec, va, r_octa, acc); /* write 2nd */ + if ((temp & CC_Z) || ((opnd[4] & FPSIGN)? /* test br cond */ + !(temp & CC_N): (temp & CC_N))) + cc = cc | LSIGN; /* hack for branch */ + break; + +/* POLYH + + opnd[0:3] = arg.rh + opnd[4] = deg.rb + opnd[5] = table.ah +*/ + + case POLYH: + op_polyh (opnd, acc); /* eval polynomial */ + CC_IIZZ_FP (R[0]); /* set cc's */ + break; + +/* EMODH + + opnd[0:3] = multiplier + opnd[4] = extension + opnd[5:8] = multiplicand + opnd[9:10] = integer destination (int.wl) + opnd[11:12] = floating destination (flt.wh) + spec = last specifier + va = address if last specifier is memory +*/ + + case EMODH: + r = op_emodh (opnd, r_octa, &temp, &flg); /* extended mod */ + if (opnd[11] < 0) { /* 2nd memory? */ + Read (opnd[12], L_BYTE, WA); /* prove write */ + Read ((opnd[12] + 15) & LMASK, L_BYTE, WA); + } + if (opnd[9] >= 0) R[opnd[9]] = temp; /* store 1st */ + else Write (opnd[10], temp, L_LONG, WA); + h_write_o (spec, va, r_octa, acc); /* write 2nd */ + CC_IIZZ_FP (r); /* set cc's */ + if (flg) { V_INTOV; } /* int ovflo? */ + break; + + default: + RSVD_INST_FAULT; + } + +return cc; +} + +/* Test h_floating + + Note that only the high 32b is processed. + If the high 32b is not zero, the rest of the fraction is unchanged. */ + +int32 op_tsth (int32 val) +{ +if (val & H_EXP) return val; /* non-zero? */ +if (val & FPSIGN) RSVD_OPND_FAULT; /* reserved? */ +return 0; /* clean 0 */ +} + +/* Compare h_floating */ + +int32 op_cmph (int32 *hf1, int32 *hf2) +{ +UFPH a, b; +int32 r; + +h_unpackh (hf1, &a); /* unpack op1 */ +h_unpackh (hf2, &b); /* unpack op2 */ +if (a.sign != b.sign) return (a.sign? CC_N: 0); /* opp signs? */ +if (a.exp != b.exp) r = a.exp - b.exp; /* cmp exp */ +else r = qp_cmp (&a.frac, &b.frac); /* if =, cmp frac */ +if (r < 0) return (a.sign? 0: CC_N); /* !=, maybe set N */ +if (r > 0) return (a.sign? CC_N: 0); +return CC_Z; /* =, set Z */ +} + +/* Integer to h_floating convert */ + +int32 op_cvtih (int32 val, int32 *hf) +{ +UFPH a; + +if (val == 0) { /* zero? */ + hf[0] = hf[1] = hf[2] = hf[3] = 0; /* result is 0 */ + return 0; + } +if (val < 0) { /* negative? */ + a.sign = FPSIGN; /* sign = - */ + val = -val; + } +else a.sign = 0; /* else sign = + */ +a.exp = 32 + H_BIAS; /* initial exp */ +a.frac.f3 = val & LMASK; /* fraction hi */ +a.frac.f2 = a.frac.f1 = a.frac.f0 = 0; +h_normh (&a); /* normalize */ +return h_rpackh (&a, hf); /* round and pack */ +} + +/* H_floating to integer convert */ + +int32 op_cvthi (int32 *hf, int32 *flg, int32 opc) +{ +UFPH a; +int32 lnt = opc & 03; +int32 ubexp; +static uint32 maxv[4] = { 0x7F, 0x7FFF, 0x7FFFFFFF, 0x7FFFFFFF }; + +*flg = 0; /* clear ovflo */ +h_unpackh (hf, &a); /* unpack */ +ubexp = a.exp - H_BIAS; /* unbiased exp */ +if ((a.exp == 0) || (ubexp < 0)) return 0; /* true zero or frac? */ +if (ubexp <= UH_V_NM) { /* exp in range? */ + qp_rsh (&a.frac, UH_V_NM - ubexp); /* leave rnd bit */ + if (lnt == 03) qp_inc (&a.frac); /* if CVTR, round */ + qp_rsh (&a.frac, 1); /* now justified */ + if (a.frac.f3 || a.frac.f2 || a.frac.f1 || + (a.frac.f0 > (maxv[lnt] + (a.sign? 1: 0)))) *flg = CC_V; + } +else { + *flg = CC_V; /* always ovflo */ + if (ubexp > (UH_V_NM + 32)) return 0; /* in ext range? */ + qp_lsh (&a.frac, ubexp - UH_V_NM - 1); /* no rnd bit */ + } +return (a.sign? NEG (a.frac.f0): a.frac.f0); /* return lo frac */ +} + +/* Floating to floating convert - F/D to H, G to H, H to F/D, H to G */ + +int32 op_cvtfdh (int32 vl, int32 vh, int32 *hflt) +{ +UFPH a; + +h_unpackfd (vl, vh, &a); /* unpack f/d */ +a.exp = a.exp - FD_BIAS + H_BIAS; /* if nz, adjust exp */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +int32 op_cvtgh (int32 vl, int32 vh, int32 *hflt) +{ +UFPH a; + +h_unpackg (vl, vh, &a); /* unpack g */ +a.exp = a.exp - G_BIAS + H_BIAS; /* if nz, adjust exp */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +int32 op_cvthfd (int32 *hflt, int32 *rh) +{ +UFPH a; + +h_unpackh (hflt, &a); /* unpack h */ +a.exp = a.exp - H_BIAS + FD_BIAS; /* if nz, adjust exp */ +return h_rpackfd (&a, rh); /* round and pack */ +} + +int32 op_cvthg (int32 *hflt, int32 *rh) +{ +UFPH a; + +h_unpackh (hflt, &a); /* unpack h */ +a.exp = a.exp - H_BIAS + G_BIAS; /* if nz, adjust exp */ +return h_rpackg (&a, rh); /* round and pack */ +} + +/* Floating add and subtract */ + +int32 op_addh (int32 *opnd, int32 *hflt, t_bool sub) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ +h_unpackh (&opnd[4], &b); +if (sub) a.sign = a.sign ^ FPSIGN; /* sub? -s1 */ +vax_hadd (&a, &b); /* do add */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +/* Floating multiply */ + +int32 op_mulh (int32 *opnd, int32 *hflt) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ +h_unpackh (&opnd[4], &b); +vax_hmul (&a, &b, 0); /* do multiply */ +return h_rpackh (&a, hflt); /* round and pack */ +} + +/* Floating divide */ + +int32 op_divh (int32 *opnd, int32 *hflt) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack s1, s2 */ +h_unpackh (&opnd[4], &b); +vax_hdiv (&a, &b); /* do divide */ +return h_rpackh (&b, hflt); /* round and pack */ +} + +/* Polynomial evaluation + + The most mis-implemented instruction in the VAX (probably here too). + POLY requires a precise combination of masking versus normalizing + to achieve the desired answer. In particular, both the multiply + and add steps are masked prior to normalization. In addition, + negative small fractions must not be treated as 0 during denorm. */ + +void op_polyh (int32 *opnd, int32 acc) +{ +UFPH r, a, c; +int32 deg = opnd[4]; +int32 ptr = opnd[5]; +int32 i, wd[4], res[4]; + +if (deg > 31) RSVD_OPND_FAULT; /* deg > 31? fault */ +h_unpackh (&opnd[0], &a); /* unpack arg */ +wd[0] = Read (ptr, L_LONG, RD); /* get C0 */ +wd[1] = Read (ptr + 4, L_LONG, RD); +wd[2] = Read (ptr + 8, L_LONG, RD); +wd[3] = Read (ptr + 12, L_LONG, RD); +ptr = ptr + 16; /* adv ptr */ +h_unpackh (wd, &r); /* unpack C0 */ +h_rpackh (&r, res); /* first result */ +for (i = 0; i < deg; i++) { /* loop */ + h_unpackh (res, &r); /* unpack result */ + vax_hmul (&r, &a, 1); /* r = r * arg */ + wd[0] = Read (ptr, L_LONG, RD); /* get Cn */ + wd[1] = Read (ptr + 4, L_LONG, RD); + wd[2] = Read (ptr + 8, L_LONG, RD); + wd[3] = Read (ptr + 12, L_LONG, RD); + ptr = ptr + 16; + h_unpackh (wd, &c); /* unpack Cnext */ + vax_hadd (&r, &c); /* r = r + Cnext */ + h_rpackh (&r, res); /* round and pack */ + } +R[0] = res[0]; /* result */ +R[1] = res[1]; +R[2] = res[2]; +R[3] = res[3]; +R[4] = 0; +R[5] = ptr; +return; +} + +/* Extended modularize + + EMOD presents two sets of complications. First, it requires an extended + fraction multiply, with precise (and unusual) truncation conditions. + Second, it has two write operands, a dubious distinction it shares + with EDIV. */ + +int32 op_emodh (int32 *opnd, int32 *hflt, int32 *intgr, int32 *flg) +{ +UFPH a, b; + +h_unpackh (&opnd[0], &a); /* unpack operands */ +h_unpackh (&opnd[5], &b); +a.frac.f0 = a.frac.f0 | (opnd[4] >> 1); /* extend src1 */ +vax_hmul (&a, &b, 0); /* multiply */ +vax_hmod (&a, intgr, flg); /* sep int & frac */ +return h_rpackh (&a, hflt); /* round and pack frac */ +} + +/* Unpacked floating point routines */ + +/* Floating add */ + +void vax_hadd (UFPH *a, UFPH *b) +{ +int32 ediff; +UFPH t; + +if ((a->frac.f3 == 0) && (a->frac.f2 == 0) && /* s1 = 0? */ + (a->frac.f1 == 0) && (a->frac.f0 == 0)) { + *a = *b; /* result is s2 */ + return; + } +if ((b->frac.f3 == 0) && (b->frac.f2 == 0) && /* s2 = 0? */ + (b->frac.f1 == 0) && (b->frac.f0 == 0)) + return; +if ((a->exp < b->exp) || /* |s1| < |s2|? */ + ((a->exp == b->exp) && (qp_cmp (&a->frac, &b->frac) < 0))) { + t = *a; /* swap */ + *a = *b; + *b = t; + } +ediff = a->exp - b->exp; /* exp diff */ +if (a->sign ^ b->sign) { /* eff sub? */ + qp_neg (&b->frac); /* negate fraction */ + if (ediff) qp_rsh_s (&b->frac, ediff, 1); /* denormalize */ + qp_add (&a->frac, &b->frac); /* "add" frac */ + h_normh (a); /* normalize */ + } +else { + if (ediff) qp_rsh (&b->frac, ediff); /* add, denormalize */ + if (qp_add (&a->frac, &b->frac)) { /* add frac, carry? */ + qp_rsh (&a->frac, 1); /* renormalize */ + a->frac.f3 = a->frac.f3 | UH_NM_H; /* add norm bit */ + a->exp = a->exp + 1; /* incr exp */ + } + } +return; +} + +/* Floating multiply - 128b * 128b */ + +void vax_hmul (UFPH *a, UFPH *b, uint32 mlo) +{ +int32 i, c; +UQP accum = { 0, 0, 0, 0 }; + +if ((a->exp == 0) || (b->exp == 0)) { /* zero argument? */ + a->frac.f0 = a->frac.f1 = 0; /* result is zero */ + a->frac.f2 = a->frac.f3 = 0; + a->sign = a->exp = 0; + return; + } +a->sign = a->sign ^ b->sign; /* sign of result */ +a->exp = a->exp + b->exp - H_BIAS; /* add exponents */ +for (i = 0; i < 128; i++) { /* quad precision */ + if (a->frac.f0 & 1) c = qp_add (&accum, &b->frac); /* mplr low? add */ + else c = 0; + qp_rsh (&accum, 1); /* shift result */ + if (c) accum.f3 = accum.f3 | UH_NM_H; /* add carry out */ + qp_rsh (&a->frac, 1); /* shift mplr */ + } +a->frac = accum; /* result */ +a->frac.f0 = a->frac.f0 & ~mlo; /* mask low frac */ +h_normh (a); /* normalize */ +return; +} + +/* Floating modulus - there are three cases + + exp <= bias - integer is 0, fraction is input, + no overflow + bias < exp <= bias+128 - separate integer and fraction, + integer overflow may occur + bias+128 < exp - result is integer, fraction is 0 + integer overflow +*/ + +void vax_hmod (UFPH *a, int32 *intgr, int32 *flg) +{ +UQP ifr; + +if (a->exp <= H_BIAS) *intgr = *flg = 0; /* 0 or <1? int = 0 */ +else if (a->exp <= (H_BIAS + 128)) { /* in range? */ + ifr = a->frac; + qp_rsh (&ifr, 128 - (a->exp - H_BIAS)); /* separate integer */ + if ((a->exp > (H_BIAS + 32)) || /* test ovflo */ + ((a->exp == (H_BIAS + 32)) && + (ifr.f0 > (a->sign? 0x80000000: 0x7FFFFFFF)))) + *flg = CC_V; + else *flg = 0; + *intgr = ifr.f0; + if (a->sign) *intgr = -*intgr; /* -? comp int */ + qp_lsh (&a->frac, a->exp - H_BIAS); /* excise integer */ + a->exp = H_BIAS; + } +else { + *intgr = 0; /* out of range */ + a->frac.f0 = a->frac.f1 = 0; /* result 0 */ + a->frac.f2 = a->frac.f3 = 0; + a->sign = a->exp = 0; + *flg = CC_V; /* overflow */ + } +h_normh (a); /* normalize */ +return; +} + +/* Floating divide + + Carried out to 128 bits, although fewer are required */ + +void vax_hdiv (UFPH *a, UFPH *b) +{ +int32 i; +UQP quo = { 0, 0, 0, 0 }; + +if (a->exp == 0) FLT_DZRO_FAULT; /* divr = 0? */ +if (b->exp == 0) return; /* divd = 0? */ +b->sign = b->sign ^ a->sign; /* result sign */ +b->exp = b->exp - a->exp + H_BIAS + 1; /* unbiased exp */ +qp_rsh (&a->frac, 1); /* allow 1 bit left */ +qp_rsh (&b->frac, 1); +for (i = 0; i < 128; i++) { /* divide loop */ + qp_lsh (&quo, 1); /* shift quo */ + if (qp_cmp (&b->frac, &a->frac) >= 0) { /* div step ok? */ + qp_sub (&b->frac, &a->frac); /* subtract */ + quo.f0 = quo.f0 + 1; /* quo bit = 1 */ + } + qp_lsh (&b->frac, 1); /* shift divd */ + } +b->frac = quo; +h_normh (b); /* normalize */ +return; +} + +/* Quad precision integer routines */ + +int32 qp_cmp (UQP *a, UQP *b) +{ +if (a->f3 < b->f3) return -1; /* compare hi */ +if (a->f3 > b->f3) return +1; +if (a->f2 < b->f2) return -1; /* hi =, compare mid1 */ +if (a->f2 > b->f2) return +1; +if (a->f1 < b->f1) return -1; /* mid1 =, compare mid2 */ +if (a->f1 > b->f1) return +1; +if (a->f0 < b->f0) return -1; /* mid2 =, compare lo */ +if (a->f0 > b->f0) return +1; +return 0; /* all equal */ +} + +uint32 qp_add (UQP *a, UQP *b) +{ +uint32 cry1, cry2, cry3, cry4; + +a->f0 = (a->f0 + b->f0) & LMASK; /* add lo */ +cry1 = (a->f0 < b->f0); /* carry? */ +a->f1 = (a->f1 + b->f1 + cry1) & LMASK; /* add mid2 */ +cry2 = (a->f1 < b->f1) || (cry1 && (a->f1 == b->f1)); /* carry? */ +a->f2 = (a->f2 + b->f2 + cry2) & LMASK; /* add mid1 */ +cry3 = (a->f2 < b->f2) || (cry2 && (a->f2 == b->f2)); /* carry? */ +a->f3 = (a->f3 + b->f3 + cry3) & LMASK; /* add hi */ +cry4 = (a->f3 < b->f3) || (cry3 && (a->f3 == b->f3)); /* carry? */ +return cry4; /* return carry out */ +} + +void qp_inc (UQP *a) +{ +a->f0 = (a->f0 + 1) & LMASK; /* inc lo */ +if (a->f0 == 0) { /* propagate carry */ + a->f1 = (a->f1 + 1) & LMASK; + if (a->f1 == 0) { + a->f2 = (a->f2 + 1) & LMASK; + if (a->f2 == 0) { + a->f3 = (a->f3 + 1) & LMASK; + } + } + } +return; +} + +uint32 qp_sub (UQP *a, UQP *b) +{ +uint32 brw1, brw2, brw3, brw4; + +brw1 = (a->f0 < b->f0); /* borrow? */ +a->f0 = (a->f0 - b->f0) & LMASK; /* sub lo */ +brw2 = (a->f1 < b->f1) || (brw1 && (a->f1 == b->f1)); /* borrow? */ +a->f1 = (a->f1 - b->f1 - brw1) & LMASK; /* sub mid1 */ +brw3 = (a->f2 < b->f2) || (brw2 && (a->f2 == b->f2)); /* borrow? */ +a->f2 = (a->f2 - b->f2 - brw2) & LMASK; /* sub mid2 */ +brw4 = (a->f3 < b->f3) || (brw3 && (a->f3 == b->f3)); /* borrow? */ +a->f3 = (a->f3 - b->f3 - brw3) & LMASK; /* sub high */ +return brw4; +} + +void qp_neg (UQP *a) +{ +uint32 cryin; + +cryin = 1; +a->f0 = (~a->f0 + cryin) & LMASK; +if (a->f0 != 0) cryin = 0; +a->f1 = (~a->f1 + cryin) & LMASK; +if (a->f1 != 0) cryin = 0; +a->f2 = (~a->f2 + cryin) & LMASK; +if (a->f2 != 0) cryin = 0; +a->f3 = (~a->f3 + cryin) & LMASK; +return; +} + +void qp_lsh (UQP *r, uint32 sc) +{ +if (sc >= 128) r->f3 = r->f2 = r->f1 = r->f0 = 0; /* > 127? result 0 */ +else if (sc >= 96) { /* [96,127]? */ + r->f3 = (r->f0 << (sc - 96)) & LMASK; + r->f2 = r->f1 = r->f0 = 0; + } +else if (sc > 64) { /* [65,95]? */ + r->f3 = ((r->f1 << (sc - 64)) | (r->f0 >> (96 - sc))) & LMASK; + r->f2 = (r->f0 << (sc - 64)) & LMASK; + r->f1 = r->f0 = 0; + } +else if (sc == 64) { /* [64]? */ + r->f3 = r->f1; + r->f2 = r->f0; + r->f1 = r->f0 = 0; + } +else if (sc > 32) { /* [33,63]? */ + r->f3 = ((r->f2 << (sc - 32)) | (r->f1 >> (64 - sc))) & LMASK; + r->f2 = ((r->f1 << (sc - 32)) | (r->f0 >> (64 - sc))) & LMASK; + r->f1 = (r->f0 << (sc - 32)) & LMASK; + r->f0 = 0; + } +else if (sc == 32) { /* [32]? */ + r->f3 = r->f2; + r->f2 = r->f1; + r->f1 = r->f0; + r->f0 = 0; + } +else if (sc != 0) { /* [31,1]? */ + r->f3 = ((r->f3 << sc) | (r->f2 >> (32 - sc))) & LMASK; + r->f2 = ((r->f2 << sc) | (r->f1 >> (32 - sc))) & LMASK; + r->f1 = ((r->f1 << sc) | (r->f0 >> (32 - sc))) & LMASK; + r->f0 = (r->f0 << sc) & LMASK; + } +return; +} + +void qp_rsh (UQP *r, uint32 sc) +{ +if (sc >= 128) r->f3 = r->f2 = r->f1 = r->f0 = 0; /* > 127? result 0 */ +else if (sc >= 96) { /* [96,127]? */ + r->f0 = (r->f3 >> (sc - 96)) & LMASK; + r->f1 = r->f2 = r->f3 = 0; + } +else if (sc > 64) { /* [65,95]? */ + r->f0 = ((r->f2 >> (sc - 64)) | (r->f3 << (96 - sc))) & LMASK; + r->f1 = (r->f3 >> (sc - 64)) & LMASK; + r->f2 = r->f3 = 0; + } +else if (sc == 64) { /* [64]? */ + r->f0 = r->f2; + r->f1 = r->f3; + r->f2 = r->f3 = 0; + } +else if (sc > 32) { /* [33,63]? */ + r->f0 = ((r->f1 >> (sc - 32)) | (r->f2 << (64 - sc))) & LMASK; + r->f1 = ((r->f2 >> (sc - 32)) | (r->f3 << (64 - sc))) & LMASK; + r->f2 = (r->f3 >> (sc - 32)) & LMASK; + r->f3 = 0; + } +else if (sc == 32) { /* [32]? */ + r->f0 = r->f1; + r->f1 = r->f2; + r->f2 = r->f3; + r->f3 = 0; + } +else if (sc != 0) { /* [31,1]? */ + r->f0 = ((r->f0 >> sc) | (r->f1 << (32 - sc))) & LMASK; + r->f1 = ((r->f1 >> sc) | (r->f2 << (32 - sc))) & LMASK; + r->f2 = ((r->f2 >> sc) | (r->f3 << (32 - sc))) & LMASK; + r->f3 = (r->f3 >> sc) & LMASK; + } +return; +} + +void qp_rsh_s (UQP *r, uint32 sc, uint32 neg) +{ +qp_rsh (r, sc); /* do unsigned right */ +if (neg && sc) { /* negative? */ + if (sc >= 128) + r->f0 = r->f1 = r->f2 = r->f3 = LMASK; /* > 127? result -1 */ + else { + UQP ones = { LMASK, LMASK, LMASK, LMASK }; + qp_lsh (&ones, 128 - sc); /* shift ones */ + r->f0 = r->f0 | ones.f0; /* or into result */ + r->f1 = r->f1 | ones.f1; + r->f2 = r->f2 | ones.f2; + r->f3 = r->f3 | ones.f3; + } + } +return; +} + +/* Support routines */ + +void h_unpackfd (int32 hi, int32 lo, UFPH *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = FD_GETEXP (hi); /* get exponent */ +r->frac.f0 = r->frac.f1 = 0; /* low bits 0 */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac.f2 = r->frac.f3 = 0; /* else 0 */ + return; + } +r->frac.f3 = WORDSWAP ((hi & ~(FPSIGN | FD_EXP)) | FD_HB); +r->frac.f2 = WORDSWAP (lo); +qp_lsh (&r->frac, FD_GUARD); +return; +} + +void h_unpackg (int32 hi, int32 lo, UFPH *r) +{ +r->sign = hi & FPSIGN; /* get sign */ +r->exp = G_GETEXP (hi); /* get exponent */ +r->frac.f0 = r->frac.f1 = 0; /* low bits 0 */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac.f2 = r->frac.f3 = 0; /* else 0 */ + return; + } +r->frac.f3 = WORDSWAP ((hi & ~(FPSIGN | G_EXP)) | G_HB); +r->frac.f2 = WORDSWAP (lo); +qp_lsh (&r->frac, G_GUARD); +return; +} + +void h_unpackh (int32 *hflt, UFPH *r) +{ +r->sign = hflt[0] & FPSIGN; /* get sign */ +r->exp = H_GETEXP (hflt[0]); /* get exponent */ +if (r->exp == 0) { /* exp = 0? */ + if (r->sign) RSVD_OPND_FAULT; /* if -, rsvd op */ + r->frac.f0 = r->frac.f1 = 0; /* else 0 */ + r->frac.f2 = r->frac.f3 = 0; + return; + } +r->frac.f3 = WORDSWAP ((hflt[0] & ~(FPSIGN | H_EXP)) | H_HB); +r->frac.f2 = WORDSWAP (hflt[1]); +r->frac.f1 = WORDSWAP (hflt[2]); +r->frac.f0 = WORDSWAP (hflt[3]); +qp_lsh (&r->frac, H_GUARD); +return; +} + +void h_normh (UFPH *r) +{ +int32 i; +static uint32 normmask[5] = { + 0xc0000000, 0xf0000000, 0xff000000, 0xffff0000, 0xffffffff }; +static int32 normtab[6] = { 1, 2, 4, 8, 16, 32}; + +if ((r->frac.f0 == 0) && (r->frac.f1 == 0) && + (r->frac.f2 == 0) && (r->frac.f3 == 0)) { /* if fraction = 0 */ + r->sign = r->exp = 0; /* result is 0 */ + return; + } +while ((r->frac.f3 & UH_NM_H) == 0) { /* normalized? */ + for (i = 0; i < 5; i++) { /* find first 1 */ + if (r->frac.f3 & normmask[i]) break; + } + qp_lsh (&r->frac, normtab[i]); /* shift frac */ + r->exp = r->exp - normtab[i]; /* decr exp */ + } +return; +} + +int32 h_rpackfd (UFPH *r, int32 *rh) +{ +static UQP f_round = { 0, 0, 0, UH_FRND }; +static UQP d_round = { 0, 0, UH_DRND, 0 }; + +if (rh) *rh = 0; /* assume 0 */ +if ((r->frac.f3 == 0) && (r->frac.f2 == 0)) return 0; /* frac = 0? done */ +qp_add (&r->frac, rh? &d_round: &f_round); +if ((r->frac.f3 & UH_NM_H) == 0) { /* carry out? */ + qp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) FD_M_EXP) FLT_OVFL_FAULT; /* ovflo? fault */ +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) FLT_UNFL_FAULT; /* fault if fu */ + return 0; /* else 0 */ + } +qp_rsh (&r->frac, FD_GUARD); /* remove guard */ +if (rh) *rh = WORDSWAP (r->frac.f2); +return r->sign | (r->exp << FD_V_EXP) | + (WORDSWAP (r->frac.f3) & ~(FD_HB | FPSIGN | FD_EXP)); +} + +int32 h_rpackg (UFPH *r, int32 *rh) +{ +static UQP g_round = { 0, 0, UH_GRND, 0 }; + +*rh = 0; /* assume 0 */ +if ((r->frac.f3 == 0) && (r->frac.f2 == 0)) return 0; /* frac = 0? done */ +qp_add (&r->frac, &g_round); /* round */ +if ((r->frac.f3 & UH_NM_H) == 0) { /* carry out? */ + qp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) G_M_EXP) FLT_OVFL_FAULT; /* ovflo? fault */ +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) FLT_UNFL_FAULT; /* fault if fu */ + return 0; /* else 0 */ + } +qp_rsh (&r->frac, G_GUARD); /* remove guard */ +*rh = WORDSWAP (r->frac.f2); /* get low */ +return r->sign | (r->exp << G_V_EXP) | + (WORDSWAP (r->frac.f3) & ~(G_HB | FPSIGN | G_EXP)); +} + +int32 h_rpackh (UFPH *r, int32 *hflt) +{ +static UQP h_round = { UH_HRND, 0, 0, 0 }; + +hflt[0] = hflt[1] = hflt[2] = hflt[3] = 0; /* assume 0 */ +if ((r->frac.f3 == 0) && (r->frac.f2 == 0) && /* frac = 0? done */ + (r->frac.f1 == 0) && (r->frac.f0 == 0)) return 0; +if (qp_add (&r->frac, &h_round)) { /* round, carry out? */ + qp_rsh (&r->frac, 1); /* renormalize */ + r->exp = r->exp + 1; + } +if (r->exp > (int32) H_M_EXP) FLT_OVFL_FAULT; /* ovflo? fault */ +if (r->exp <= 0) { /* underflow? */ + if (PSL & PSW_FU) FLT_UNFL_FAULT; /* fault if fu */ + return 0; /* else 0 */ + } +qp_rsh (&r->frac, H_GUARD); /* remove guard */ +hflt[0] = r->sign | (r->exp << H_V_EXP) | + (WORDSWAP (r->frac.f3) & ~(H_HB | FPSIGN | H_EXP)); +hflt[1] = WORDSWAP (r->frac.f2); +hflt[2] = WORDSWAP (r->frac.f1); +hflt[3] = WORDSWAP (r->frac.f0); +return hflt[0]; +} + +void h_write_b (int32 spec, int32 va, int32 val, int32 acc) +{ +int32 rn; + +if (spec > (GRN | nPC)) Write (va, val, L_BYTE, WA); +else { + rn = spec & 0xF; + R[rn] = (R[rn] & ~BMASK) | val; + } +return; +} + +void h_write_w (int32 spec, int32 va, int32 val, int32 acc) +{ +int32 rn; + +if (spec > (GRN | nPC)) Write (va, val, L_WORD, WA); +else { + rn = spec & 0xF; + R[rn] = (R[rn] & ~WMASK) | val; + } +return; +} + +void h_write_l (int32 spec, int32 va, int32 val, int32 acc) +{ +if (spec > (GRN | nPC)) Write (va, val, L_LONG, WA); +else R[spec & 0xF] = val; +return; +} + +void h_write_q (int32 spec, int32 va, int32 vl, int32 vh, int32 acc) +{ +int32 rn, mstat; + +if (spec > (GRN | nPC)) { + if ((Test (va + 7, WA, &mstat) >= 0) || + (Test (va, WA, &mstat) < 0)) + Write (va, vl, L_LONG, WA); + Write (va + 4, vh, L_LONG, WA); + } +else { + rn = spec & 0xF; + if (rn >= nSP) RSVD_ADDR_FAULT; + R[rn] = vl; + R[rn + 1] = vh; + } +return; +} + +void h_write_o (int32 spec, int32 va, int32 *val, int32 acc) +{ +int32 rn, mstat; + +if (spec > (GRN | nPC)) { + if ((Test (va + 15, WA, &mstat) >= 0) || + (Test (va, WA, &mstat) < 0)) + Write (va, val[0], L_LONG, WA); + Write (va + 4, val[1], L_LONG, WA); + Write (va + 8, val[2], L_LONG, WA); + Write (va + 12, val[3], L_LONG, WA); + } +else { + rn = spec & 0xF; + if (rn >= nAP) RSVD_ADDR_FAULT; + R[rn] = val[0]; + R[rn + 1] = val[1]; + R[rn + 2] = val[2]; + R[rn + 3] = val[3]; + } +return; +} + +#else + +extern jmp_buf save_env; + +int32 op_octa (int32 *opnd, int32 cc, int32 opc, int32 acc, int32 spec, int32 va) +{ +RSVD_INST_FAULT; +return cc; +} + +#endif diff --git a/VAX/vax_stddev.c b/VAX/vax_stddev.c new file mode 100644 index 0000000..f0b2263 --- /dev/null +++ b/VAX/vax_stddev.c @@ -0,0 +1,407 @@ +/* vax_stddev.c: VAX 3900 standard I/O devices + + Copyright (c) 1998-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + tti terminal input + tto terminal output + clk 100Hz and TODR clock + + 18-Jun-07 RMS Added UNIT_IDLE flag to console input, clock + 17-Oct-06 RMS Synced keyboard poll to real-time clock for idling + 22-Nov-05 RMS Revised for new terminal processing routines + 09-Sep-04 RMS Integrated powerup into RESET (with -p) + 28-May-04 RMS Removed SET TTI CTRL-C + 29-Dec-03 RMS Added console backpressure support + 25-Apr-03 RMS Revised for extended file support + 02-Mar-02 RMS Added SET TTI CTRL-C + 22-Dec-02 RMS Added console halt capability + 01-Nov-02 RMS Added 7B/8B capability to terminal + 12-Sep-02 RMS Removed paper tape, added variable vector support + 30-May-02 RMS Widened POS to 32b + 30-Apr-02 RMS Automatically set TODR to VMS-correct value during boot +*/ + +#include "vax_defs.h" +#include + +#define TTICSR_IMP (CSR_DONE + CSR_IE) /* terminal input */ +#define TTICSR_RW (CSR_IE) +#define TTIBUF_ERR 0x8000 /* error */ +#define TTIBUF_OVR 0x4000 /* overrun */ +#define TTIBUF_FRM 0x2000 /* framing error */ +#define TTIBUF_RBR 0x0400 /* receive break */ +#define TTOCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */ +#define TTOCSR_RW (CSR_IE) +#define CLKCSR_IMP (CSR_IE) /* real-time clock */ +#define CLKCSR_RW (CSR_IE) +#define CLK_DELAY 5000 /* 100 Hz */ +#define TMXR_MULT 1 /* 100 Hz */ + +extern int32 int_req[IPL_HLVL]; +extern int32 hlt_pin; +extern int32 sim_switches; + +int32 tti_csr = 0; /* control/status */ +int32 tto_csr = 0; /* control/status */ +int32 clk_csr = 0; /* control/status */ +int32 clk_tps = 100; /* ticks/second */ +int32 todr_reg = 0; /* TODR register */ +int32 todr_blow = 1; /* TODR battery low */ +int32 tmxr_poll = CLK_DELAY * TMXR_MULT; /* term mux poll */ +int32 tmr_poll = CLK_DELAY; /* pgm timer poll */ + +t_stat tti_svc (UNIT *uptr); +t_stat tto_svc (UNIT *uptr); +t_stat clk_svc (UNIT *uptr); +t_stat tti_reset (DEVICE *dptr); +t_stat tto_reset (DEVICE *dptr); +t_stat clk_reset (DEVICE *dptr); + +extern int32 sysd_hlt_enb (void); + +/* TTI data structures + + tti_dev TTI device descriptor + tti_unit TTI unit descriptor + tti_reg TTI register list +*/ + +DIB tti_dib = { 0, 0, NULL, NULL, 1, IVCL (TTI), SCB_TTI, { NULL } }; + +UNIT tti_unit = { UDATA (&tti_svc, UNIT_IDLE|TT_MODE_8B, 0), 0 }; + +REG tti_reg[] = { + { HRDATA (BUF, tti_unit.buf, 16) }, + { HRDATA (CSR, tti_csr, 16) }, + { FLDATA (INT, int_req[IPL_TTI], INT_V_TTI) }, + { FLDATA (DONE, tti_csr, CSR_V_DONE) }, + { FLDATA (IE, tti_csr, CSR_V_IE) }, + { DRDATA (POS, tti_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tti_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tti_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, + NULL, &show_vec, NULL }, + { 0 } + }; + +DEVICE tti_dev = { + "TTI", &tti_unit, tti_reg, tti_mod, + 1, 10, 31, 1, 16, 8, + NULL, NULL, &tti_reset, + NULL, NULL, NULL, + &tti_dib, 0 + }; + +/* TTO data structures + + tto_dev TTO device descriptor + tto_unit TTO unit descriptor + tto_reg TTO register list +*/ + +DIB tto_dib = { 0, 0, NULL, NULL, 1, IVCL (TTO), SCB_TTO, { NULL } }; + +UNIT tto_unit = { UDATA (&tto_svc, TT_MODE_8B, 0), SERIAL_OUT_WAIT }; + +REG tto_reg[] = { + { HRDATA (BUF, tto_unit.buf, 8) }, + { HRDATA (CSR, tto_csr, 16) }, + { FLDATA (INT, int_req[IPL_TTO], INT_V_TTO) }, + { FLDATA (DONE, tto_csr, CSR_V_DONE) }, + { FLDATA (IE, tto_csr, CSR_V_IE) }, + { DRDATA (POS, tto_unit.pos, T_ADDR_W), PV_LEFT }, + { DRDATA (TIME, tto_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB tto_mod[] = { + { TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, + { TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, + { TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE tto_dev = { + "TTO", &tto_unit, tto_reg, tto_mod, + 1, 10, 31, 1, 16, 8, + NULL, NULL, &tto_reset, + NULL, NULL, NULL, + &tto_dib, 0 + }; + +/* CLK data structures + + clk_dev CLK device descriptor + clk_unit CLK unit descriptor + clk_reg CLK register list +*/ + +DIB clk_dib = { 0, 0, NULL, NULL, 1, IVCL (CLK), SCB_INTTIM, { NULL } }; + +UNIT clk_unit = { UDATA (&clk_svc, UNIT_IDLE, 0), CLK_DELAY }; + +REG clk_reg[] = { + { HRDATA (CSR, clk_csr, 16) }, + { FLDATA (INT, int_req[IPL_CLK], INT_V_CLK) }, + { FLDATA (IE, clk_csr, CSR_V_IE) }, + { DRDATA (TODR, todr_reg, 32), PV_LEFT }, + { FLDATA (BLOW, todr_blow, 0) }, + { DRDATA (TIME, clk_unit.wait, 24), REG_NZ + PV_LEFT }, + { DRDATA (POLL, tmr_poll, 24), REG_NZ + PV_LEFT + REG_HRO }, + { DRDATA (TPS, clk_tps, 8), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB clk_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE clk_dev = { + "CLK", &clk_unit, clk_reg, clk_mod, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &clk_reset, + NULL, NULL, NULL, + &clk_dib, 0 + }; + +/* Clock and terminal MxPR routines + + iccs_rd/wr interval timer + todr_rd/wr time of year clock + rxcs_rd/wr input control/status + rxdb_rd input buffer + txcs_rd/wr output control/status + txdb_wr output buffer +*/ + +int32 iccs_rd (void) +{ +return (clk_csr & CLKCSR_IMP); +} + +int32 todr_rd (void) +{ +return todr_reg; +} + +int32 rxcs_rd (void) +{ +return (tti_csr & TTICSR_IMP); +} + +int32 rxdb_rd (void) +{ +int32 t = tti_unit.buf; /* char + error */ + +tti_csr = tti_csr & ~CSR_DONE; /* clr done */ +tti_unit.buf = tti_unit.buf & 0377; /* clr errors */ +CLR_INT (TTI); +return t; +} + +int32 txcs_rd (void) +{ +return (tto_csr & TTOCSR_IMP); +} + +void iccs_wr (int32 data) +{ +if ((data & CSR_IE) == 0) CLR_INT (CLK); +clk_csr = (clk_csr & ~CLKCSR_RW) | (data & CLKCSR_RW); +return; +} + +void todr_wr (int32 data) +{ +todr_reg = data; +if (data) todr_blow = 0; +return; +} + +void rxcs_wr (int32 data) +{ +if ((data & CSR_IE) == 0) CLR_INT (TTI); +else if ((tti_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (TTI); +tti_csr = (tti_csr & ~TTICSR_RW) | (data & TTICSR_RW); +return; +} + +void txcs_wr (int32 data) +{ +if ((data & CSR_IE) == 0) CLR_INT (TTO); +else if ((tto_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (TTO); +tto_csr = (tto_csr & ~TTOCSR_RW) | (data & TTOCSR_RW); +return; +} + +void txdb_wr (int32 data) +{ +tto_unit.buf = data & 0377; +tto_csr = tto_csr & ~CSR_DONE; +CLR_INT (TTO); +sim_activate (&tto_unit, tto_unit.wait); +return; +} + +/* Terminal input routines + + tti_svc process event (character ready) + tti_reset process reset +*/ + +t_stat tti_svc (UNIT *uptr) +{ +int32 c; + +sim_activate (uptr, KBD_WAIT (uptr->wait, tmr_poll)); /* continue poll */ +if ((c = sim_poll_kbd ()) < SCPE_KFLAG) return c; /* no char or error? */ +if (c & SCPE_BREAK) { /* break? */ + if (sysd_hlt_enb ()) hlt_pin = 1; /* if enabled, halt */ + tti_unit.buf = TTIBUF_ERR | TTIBUF_FRM | TTIBUF_RBR; + } +else tti_unit.buf = sim_tt_inpcvt (c, TT_GET_MODE (uptr->flags)); +uptr->pos = uptr->pos + 1; +tti_csr = tti_csr | CSR_DONE; +if (tti_csr & CSR_IE) SET_INT (TTI); +return SCPE_OK; +} + +t_stat tti_reset (DEVICE *dptr) +{ +tti_unit.buf = 0; +tti_csr = 0; +CLR_INT (TTI); +sim_activate_abs (&tti_unit, KBD_WAIT (tti_unit.wait, tmr_poll)); +return SCPE_OK; +} + +/* Terminal output routines + + tto_svc process event (character typed) + tto_reset process reset +*/ + +t_stat tto_svc (UNIT *uptr) +{ +int32 c; +t_stat r; + +c = sim_tt_outcvt (tto_unit.buf, TT_GET_MODE (uptr->flags)); +if (c >= 0) { + if ((r = sim_putchar_s (c)) != SCPE_OK) { /* output; error? */ + sim_activate (uptr, uptr->wait); /* retry */ + return ((r == SCPE_STALL)? SCPE_OK: r); /* !stall? report */ + } + } +tto_csr = tto_csr | CSR_DONE; +if (tto_csr & CSR_IE) SET_INT (TTO); +uptr->pos = uptr->pos + 1; +return SCPE_OK; +} + +t_stat tto_reset (DEVICE *dptr) +{ +tto_unit.buf = 0; +tto_csr = CSR_DONE; +CLR_INT (TTO); +sim_cancel (&tto_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* Clock routines + + clk_svc process event (clock tick) + clk_reset process reset + todr_powerup powerup for TODR (get date from system) +*/ + +t_stat clk_svc (UNIT *uptr) +{ +int32 t; + +if (clk_csr & CSR_IE) SET_INT (CLK); +t = sim_rtcn_calb (clk_tps, TMR_CLK); /* calibrate clock */ +sim_activate (&clk_unit, t); /* reactivate unit */ +tmr_poll = t; /* set tmr poll */ +tmxr_poll = t * TMXR_MULT; /* set mux poll */ +if (!todr_blow) todr_reg = todr_reg + 1; /* incr TODR */ +return SCPE_OK; +} + +/* Clock coscheduling routine */ + +int32 clk_cosched (int32 wait) +{ +int32 t; + +t = sim_is_active (&clk_unit); +return (t? t - 1: wait); +} + +/* Powerup routine */ + +t_stat todr_powerup (void) +{ +uint32 base; +time_t curr; +struct tm *ctm; + +curr = time (NULL); /* get curr time */ +if (curr == (time_t) -1) return SCPE_NOFNC; /* error? */ +ctm = localtime (&curr); /* decompose */ +if (ctm == NULL) return SCPE_NOFNC; /* error? */ +base = (((((ctm->tm_yday * 24) + /* sec since 1-Jan */ + ctm->tm_hour) * 60) + + ctm->tm_min) * 60) + + ctm->tm_sec; +todr_reg = (base * 100) + 0x10000000; /* cvt to VAX form */ +todr_blow = 0; +return SCPE_OK; +} + +/* Reset routine */ + +t_stat clk_reset (DEVICE *dptr) +{ +int32 t; + +if (sim_switches & SWMASK ('P')) todr_powerup (); /* powerup? */ +clk_csr = 0; +CLR_INT (CLK); +t = sim_rtcn_init (clk_unit.wait, TMR_CLK); /* init timer */ +sim_activate_abs (&clk_unit, t); /* activate unit */ +tmr_poll = t; /* set tmr poll */ +tmxr_poll = t * TMXR_MULT; /* set mux poll */ +return SCPE_OK; +} + diff --git a/VAX/vax_sys.c b/VAX/vax_sys.c new file mode 100644 index 0000000..63c22d5 --- /dev/null +++ b/VAX/vax_sys.c @@ -0,0 +1,1415 @@ +/* vax_sys.c: VAX simulator interface + + Copyright (c) 1998-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 03-Nov-05 RMS Added 780 stop codes + 04-Sep-05 RMS Fixed missing assignment (found by Peter Schorn) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 15-Sep-04 RMS Fixed bugs in character display and parse + 30-Sep-04 RMS Fixed bugs in parsing indirect displacement modes + Added compatibility mode support + 04-Sep-04 RMS Added octa instruction support + 02-Sep-04 RMS Fixed parse branch return status + 13-Jul-04 RMS Fixed bad block routine + 16-Jun-04 RMS Added DHQ11 support + 21-Mar-04 RMS Added RXV21 support + 06-May-03 RMS Added support for second DELQA + 12-Oct-02 RMS Added multiple RQ controller support + 10-Oct-02 RMS Added DELQA support + 21-Sep-02 RMS Extended symbolic ex/mod to all byte devices + 06-Sep-02 RMS Added TMSCP support + 14-Jul-02 RMS Added infinite loop message +*/ + +#include "vax_defs.h" +#include + +#if defined (FULL_VAX) +#define ODC(x) (x) +#else +#define ODC(x) ((x) << DR_V_USPMASK) +#endif + +extern UNIT cpu_unit; +extern REG cpu_reg[]; +extern int32 saved_PC; +extern int32 PSL; +extern int32 sim_switches; + +t_stat fprint_sym_m (FILE *of, uint32 addr, t_value *val); +int32 fprint_sym_qoimm (FILE *of, t_value *val, int32 vp, int32 lnt); +t_stat parse_char (char *cptr, t_value *val, int32 lnt); +t_stat parse_sym_m (char *cptr, uint32 addr, t_value *val); +int32 parse_brdisp (char *cptr, uint32 addr, t_value *val, + int32 vp, int32 lnt, t_stat *r); +int32 parse_spec (char *cptr, uint32 addr, t_value *val, + int32 vp, int32 disp, t_stat *r); +char *parse_rnum (char *cptr, int32 *rn); +int32 parse_sym_qoimm (int32 *lit, t_value *val, int32 vp, + int lnt, int32 minus); + +extern t_stat fprint_sym_cm (FILE *of, t_addr addr, t_value *bytes, int32 sw); +t_stat parse_sym_cm (char *cptr, t_addr addr, t_value *bytes, int32 sw); + +/* SCP data structures and interface routines + + sim_name simulator name string + sim_PC pointer to saved PC register descriptor + sim_emax number of words for examine + sim_devices array of pointers to simulated devices + sim_stop_messages array of pointers to stop messages + sim_load binary loader +*/ + +REG *sim_PC = &cpu_reg[0]; + +int32 sim_emax = 60; + +const char *sim_stop_messages[] = { + "Unknown error", + "HALT instruction", + "Breakpoint", + "CHMx on interrupt stack", + "Invalid SCB vector", + "Exception in interrupt or exception", + "Process PTE in P0 or P1 space", + "Interrupt at undefined IPL", + "Fatal RQDX3 error", + "Infinite loop", + "Sanity timer expired", + "Software done", + "Reboot requested", + "Unknown error", + "Unknown abort code" + }; + +/* Factory bad block table creation routine + + This routine writes a DEC standard 044 compliant bad block table on the + last track of the specified unit. The bad block table consists of 10 + repetitions of the same table, formatted as follows: + + words 0-1 pack id number + words 2-3 cylinder/sector/surface specifications + : + words n-n+1 end of table (-1,-1) + + Inputs: + uptr = pointer to unit + sec = number of sectors per surface + wds = number of words per sector + Outputs: + sta = status code +*/ + +t_stat pdp11_bad_block (UNIT *uptr, int32 sec, int32 wds) +{ +int32 i; +t_addr da; +uint16 *buf; + +if ((sec < 2) || (wds < 16)) return SCPE_ARG; +if ((uptr->flags & UNIT_ATT) == 0) return SCPE_UNATT; +if (!get_yn ("Overwrite last track? [N]", FALSE)) return SCPE_OK; +da = (uptr->capac - (sec * wds)) * sizeof (uint16); +if (sim_fseek (uptr->fileref, da, SEEK_SET)) return SCPE_IOERR; +if ((buf = (uint16 *) malloc (wds * sizeof (uint16))) == NULL) return SCPE_MEM; +buf[0] = 0x1234; +buf[1] = 0x5678; +buf[2] = buf[3] = 0; +for (i = 4; i < wds; i++) buf[i] = 0xFFFF; +for (i = 0; (i < sec) && (i < 10); i++) + sim_fwrite (buf, sizeof (uint16), wds, uptr->fileref); +free (buf); +if (ferror (uptr->fileref)) return SCPE_IOERR; +return SCPE_OK; +} + +/* Dispatch/decoder table + + The first entry contains: + - FPD legal flag (DR_F) + - number of specifiers for decode bits 2:0> + - number of specifiers for unimplemented instructions bits<6:4> + */ + +const uint16 drom[NUM_INST][MAX_SPEC + 1] = { +0, 0, 0, 0, 0, 0, 0, /* HALT */ +0, 0, 0, 0, 0, 0, 0, /* NOP */ +0, 0, 0, 0, 0, 0, 0, /* REI */ +0, 0, 0, 0, 0, 0, 0, /* BPT */ +0, 0, 0, 0, 0, 0, 0, /* RET */ +0, 0, 0, 0, 0, 0, 0, /* RSB */ +0, 0, 0, 0, 0, 0, 0, /* LDPCTX */ +0, 0, 0, 0, 0, 0, 0, /* SVPCTX */ +4+DR_F, RW, AB, RW, AB, 0, 0, /* CVTPS */ +4+DR_F, RW, AB, RW, AB, 0, 0, /* CVTSP */ +6, RL, RL, RL, RL, RL, WL, /* INDEX */ +4+DR_F, AB, RL, RW, AB, 0, 0, /* CRC */ +3, RB, RW, AB, 0, 0, 0, /* PROBER */ +3, RB, RW, AB, 0, 0, 0, /* PROBEW */ +2, AB, AB, 0, 0, 0, 0, /* INSQUE */ +2, AB, WL, 0, 0, 0, 0, /* REMQUE */ +1, BB, 0, 0, 0, 0, 0, /* BSBB */ +1, BB, 0, 0, 0, 0, 0, /* BRB */ +1, BB, 0, 0, 0, 0, 0, /* BNEQ */ +1, BB, 0, 0, 0, 0, 0, /* BEQL */ +1, BB, 0, 0, 0, 0, 0, /* BGTR */ +1, BB, 0, 0, 0, 0, 0, /* BLEQ */ +1, AB, 0, 0, 0, 0, 0, /* JSB */ +1, AB, 0, 0, 0, 0, 0, /* JMP */ +1, BB, 0, 0, 0, 0, 0, /* BGEQ */ +1, BB, 0, 0, 0, 0, 0, /* BLSS */ +1, BB, 0, 0, 0, 0, 0, /* BGTRU */ +1, BB, 0, 0, 0, 0, 0, /* BLEQU */ +1, BB, 0, 0, 0, 0, 0, /* BVC */ +1, BB, 0, 0, 0, 0, 0, /* BVS */ +1, BB, 0, 0, 0, 0, 0, /* BCC */ +1, BB, 0, 0, 0, 0, 0, /* BCS */ +4+DR_F, RW, AB, RW, AB, 0, 0, /* ADDP4 */ +6+DR_F, RW, AB, RW, AB, RW, AB, /* ADDP6 */ +4+DR_F, RW, AB, RW, AB, 0, 0, /* SUBP4 */ +6+DR_F, RW, AB, RW, AB, RW, AB, /* SUBP6 */ +5+DR_F, RW, AB, AB, RW, AB, 0, /* CVTPT */ +6+DR_F, RW, AB, RW, AB, RW, AB, /* MULP6 */ +5+DR_F, RW, AB, AB, RW, AB, 0, /* CVTTP */ +6+DR_F, RW, AB, RW, AB, RW, AB, /* DIVP6 */ +3+DR_F, RW, AB, AB, 0, 0, 0, /* MOVC3 */ +3+DR_F, RW, AB, AB, 0, 0, 0, /* CMPC3 */ +4+DR_F, RW, AB, AB, RB, 0, 0, /* SCANC */ +4+DR_F, RW, AB, AB, RB, 0, 0, /* SPANC */ +5+DR_F, RW, AB, RB, RW, AB, 0, /* MOVC5 */ +5+DR_F, RW, AB, RB, RW, AB, 0, /* CMPC5 */ +6+DR_F, RW, AB, RB, AB, RW, AB, /* MOVTC */ +6+DR_F, RW, AB, RB, AB, RW, AB, /* MOVTUC */ +1, BW, 0, 0, 0, 0, 0, /* BSBW */ +1, BW, 0, 0, 0, 0, 0, /* BRW */ +2, RW, WL, 0, 0, 0, 0, /* CVTWL */ +2, RW, WB, 0, 0, 0, 0, /* CVTWB */ +3+DR_F, RW, AB, AB, 0, 0, 0, /* MOVP */ +3+DR_F, RW, AB, AB, 0, 0, 0, /* CMPP3 */ +3+DR_F, RW, AB, WL, 0, 0, 0, /* CVTPL */ +4+DR_F, RW, AB, RW, AB, 0, 0, /* CMPP4 */ +4+DR_F, RW, AB, AB, AB, 0, 0, /* EDITPC */ +4+DR_F, RW, AB, RW, AB, 0, 0, /* MATCHC */ +3+DR_F, RB, RW, AB, 0, 0, 0, /* LOCC */ +3+DR_F, RB, RW, AB, 0, 0, 0, /* SKPC */ +2, RW, WL, 0, 0, 0, 0, /* MOVZWL */ +4, RW, RW, MW, BW, 0, 0, /* ACBW */ +2, AW, WL, 0, 0, 0, 0, /* MOVAW */ +1, AW, 0, 0, 0, 0, 0, /* PUSHAW */ +2, RF, ML, 0, 0, 0, 0, /* ADDF2 */ +3, RF, RF, WL, 0, 0, 0, /* ADDF3 */ +2, RF, ML, 0, 0, 0, 0, /* SUBF2 */ +3, RF, RF, WL, 0, 0, 0, /* SUBF3 */ +2, RF, ML, 0, 0, 0, 0, /* MULF2 */ +3, RF, RF, WL, 0, 0, 0, /* MULF3 */ +2, RF, ML, 0, 0, 0, 0, /* DIVF2 */ +3, RF, RF, WL, 0, 0, 0, /* DIVF3 */ +2, RF, WB, 0, 0, 0, 0, /* CVTFB */ +2, RF, WW, 0, 0, 0, 0, /* CVTFW */ +2, RF, WL, 0, 0, 0, 0, /* CVTFL */ +2, RF, WL, 0, 0, 0, 0, /* CVTRFL */ +2, RB, WL, 0, 0, 0, 0, /* CVTBF */ +2, RW, WL, 0, 0, 0, 0, /* CVTWF */ +2, RL, WL, 0, 0, 0, 0, /* CVTLF */ +4, RF, RF, ML, BW, 0, 0, /* ACBF */ +2, RF, WL, 0, 0, 0, 0, /* MOVF */ +2, RF, RF, 0, 0, 0, 0, /* CMPF */ +2, RF, WL, 0, 0, 0, 0, /* MNEGF */ +1, RF, 0, 0, 0, 0, 0, /* TSTF */ +5, RF, RB, RF, WL, WL, 0, /* EMODF */ +3, RF, RW, AB, 0, 0, 0, /* POLYF */ +2, RF, WQ, 0, 0, 0, 0, /* CVTFD */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +2, RW, WW, 0, 0, 0, 0, /* ADAWI */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +2, AB, AQ, 0, 0, 0, 0, /* INSQHI */ +2, AB, AQ, 0, 0, 0, 0, /* INSQTI */ +2, AQ, WL, 0, 0, 0, 0, /* REMQHI */ +2, AQ, WL, 0, 0, 0, 0, /* REMQTI */ +2, RD, MQ, 0, 0, 0, 0, /* ADDD2 */ +3, RD, RD, WQ, 0, 0, 0, /* ADDD3 */ +2, RD, MQ, 0, 0, 0, 0, /* SUBD2 */ +3, RD, RD, WQ, 0, 0, 0, /* SUBD3 */ +2, RD, MQ, 0, 0, 0, 0, /* MULD2 */ +3, RD, RD, WQ, 0, 0, 0, /* MULD3 */ +2, RD, MQ, 0, 0, 0, 0, /* DIVD2 */ +3, RD, RD, WQ, 0, 0, 0, /* DIVD3 */ +2, RD, WB, 0, 0, 0, 0, /* CVTDB */ +2, RD, WW, 0, 0, 0, 0, /* CVTDW */ +2, RD, WL, 0, 0, 0, 0, /* CVTDL */ +2, RD, WL, 0, 0, 0, 0, /* CVTRDL */ +2, RB, WQ, 0, 0, 0, 0, /* CVTBD */ +2, RW, WQ, 0, 0, 0, 0, /* CVTWD */ +2, RL, WQ, 0, 0, 0, 0, /* CVTLD */ +4, RD, RD, MQ, BW, 0, 0, /* ACBD */ +2, RD, WQ, 0, 0, 0, 0, /* MOVD */ +2, RD, RD, 0, 0, 0, 0, /* CMPD */ +2, RD, WQ, 0, 0, 0, 0, /* MNEGD */ +1, RD, 0, 0, 0, 0, 0, /* TSTD */ +5, RD, RB, RD, WL, WQ, 0, /* EMODD */ +3, RD, RW, AB, 0, 0, 0, /* POLYD */ +2, RD, WL, 0, 0, 0, 0, /* CVTDF */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +3, RB, RL, WL, 0, 0, 0, /* ASHL */ +3, RB, RQ, WQ, 0, 0, 0, /* ASHQ */ +4, RL, RL, RL, WQ, 0, 0, /* EMUL */ +4, RL, RQ, WL, WL, 0, 0, /* EDIV */ +1, WQ, 0, 0, 0, 0, 0, /* CLRQ */ +2, RQ, WQ, 0, 0, 0, 0, /* MOVQ */ +2, AQ, WL, 0, 0, 0, 0, /* MOVAQ */ +1, AQ, 0, 0, 0, 0, 0, /* PUSHAQ */ +2, RB, MB, 0, 0, 0, 0, /* ADDB2 */ +3, RB, RB, WB, 0, 0, 0, /* ADDB3 */ +2, RB, MB, 0, 0, 0, 0, /* SUBB2 */ +3, RB, RB, WB, 0, 0, 0, /* SUBB3 */ +2, RB, MB, 0, 0, 0, 0, /* MULB2 */ +3, RB, RB, WB, 0, 0, 0, /* MULB3 */ +2, RB, MB, 0, 0, 0, 0, /* DIVB2 */ +3, RB, RB, WB, 0, 0, 0, /* DIVB3 */ +2, RB, MB, 0, 0, 0, 0, /* BISB2 */ +3, RB, RB, WB, 0, 0, 0, /* BISB3 */ +2, RB, MB, 0, 0, 0, 0, /* BICB2 */ +3, RB, RB, WB, 0, 0, 0, /* BICB3 */ +2, RB, MB, 0, 0, 0, 0, /* XORB2 */ +3, RB, RB, WB, 0, 0, 0, /* XORB3 */ +2, RB, WB, 0, 0, 0, 0, /* MNEGB */ +3, RB, RB, RB, 0, 0, 0, /* CASEB */ +2, RB, WB, 0, 0, 0, 0, /* MOVB */ +2, RB, RB, 0, 0, 0, 0, /* CMPB */ +2, RB, WB, 0, 0, 0, 0, /* MCOMB */ +2, RB, RB, 0, 0, 0, 0, /* BITB */ +1, WB, 0, 0, 0, 0, 0, /* CLRB */ +1, RB, 0, 0, 0, 0, 0, /* TSTB */ +1, MB, 0, 0, 0, 0, 0, /* INCB */ +1, MB, 0, 0, 0, 0, 0, /* DECB */ +2, RB, WL, 0, 0, 0, 0, /* CVTBL */ +2, RB, WW, 0, 0, 0, 0, /* CVTBW */ +2, RB, WL, 0, 0, 0, 0, /* MOVZBL */ +2, RB, WW, 0, 0, 0, 0, /* MOVZBW */ +3, RB, RL, WL, 0, 0, 0, /* ROTL */ +4, RB, RB, MB, BW, 0, 0, /* ACBB */ +2, AB, WL, 0, 0, 0, 0, /* MOVAB */ +1, AB, 0, 0, 0, 0, 0, /* PUSHAB */ +2, RW, MW, 0, 0, 0, 0, /* ADDW2 */ +3, RW, RW, WW, 0, 0, 0, /* ADDW3 */ +2, RW, MW, 0, 0, 0, 0, /* SUBW2 */ +3, RW, RW, WW, 0, 0, 0, /* SUBW3 */ +2, RW, MW, 0, 0, 0, 0, /* MULW2 */ +3, RW, RW, WW, 0, 0, 0, /* MULW3 */ +2, RW, MW, 0, 0, 0, 0, /* DIVW2 */ +3, RW, RW, WW, 0, 0, 0, /* DIVW3 */ +2, RW, MW, 0, 0, 0, 0, /* BISW2 */ +3, RW, RW, WW, 0, 0, 0, /* BISW3 */ +2, RW, MW, 0, 0, 0, 0, /* BICW2 */ +3, RW, RW, WW, 0, 0, 0, /* BICW3 */ +2, RW, MW, 0, 0, 0, 0, /* XORW2 */ +3, RW, RW, WW, 0, 0, 0, /* XORW3 */ +2, RW, WW, 0, 0, 0, 0, /* MNEGW */ +3, RW, RW, RW, 0, 0, 0, /* CASEW */ +2, RW, WW, 0, 0, 0, 0, /* MOVW */ +2, RW, RW, 0, 0, 0, 0, /* CMPW */ +2, RW, WW, 0, 0, 0, 0, /* MCOMW */ +2, RW, RW, 0, 0, 0, 0, /* BITW */ +1, WW, 0, 0, 0, 0, 0, /* CLRW */ +1, RW, 0, 0, 0, 0, 0, /* TSTW */ +1, MW, 0, 0, 0, 0, 0, /* INCW */ +1, MW, 0, 0, 0, 0, 0, /* DECW */ +1, RW, 0, 0, 0, 0, 0, /* BISPSW */ +1, RW, 0, 0, 0, 0, 0, /* BICPSW */ +1, RW, 0, 0, 0, 0, 0, /* POPR */ +1, RW, 0, 0, 0, 0, 0, /* PUSHR */ +1, RW, 0, 0, 0, 0, 0, /* CHMK */ +1, RW, 0, 0, 0, 0, 0, /* CHME */ +1, RW, 0, 0, 0, 0, 0, /* CHMS */ +1, RW, 0, 0, 0, 0, 0, /* CHMU */ +2, RL, ML, 0, 0, 0, 0, /* ADDL2 */ +3, RL, RL, WL, 0, 0, 0, /* ADDL3 */ +2, RL, ML, 0, 0, 0, 0, /* SUBL2 */ +3, RL, RL, WL, 0, 0, 0, /* SUBL3 */ +2, RL, ML, 0, 0, 0, 0, /* MULL2 */ +3, RL, RL, WL, 0, 0, 0, /* MULL3 */ +2, RL, ML, 0, 0, 0, 0, /* DIVL2 */ +3, RL, RL, WL, 0, 0, 0, /* DIVL3 */ +2, RL, ML, 0, 0, 0, 0, /* BISL2 */ +3, RL, RL, WL, 0, 0, 0, /* BISL3 */ +2, RL, ML, 0, 0, 0, 0, /* BICL2 */ +3, RL, RL, WL, 0, 0, 0, /* BICL3 */ +2, RL, ML, 0, 0, 0, 0, /* XORL2 */ +3, RL, RL, WL, 0, 0, 0, /* XORL3 */ +2, RL, WL, 0, 0, 0, 0, /* MNEGL */ +3, RL, RL, RL, 0, 0, 0, /* CASEL */ +2, RL, WL, 0, 0, 0, 0, /* MOVL */ +2, RL, RL, 0, 0, 0, 0, /* CMPL */ +2, RL, WL, 0, 0, 0, 0, /* MCOML */ +2, RL, RL, 0, 0, 0, 0, /* BITL */ +1, WL, 0, 0, 0, 0, 0, /* CLRL */ +1, RL, 0, 0, 0, 0, 0, /* TSTL */ +1, ML, 0, 0, 0, 0, 0, /* INCL */ +1, ML, 0, 0, 0, 0, 0, /* DECL */ +2, RL, ML, 0, 0, 0, 0, /* ADWC */ +2, RL, ML, 0, 0, 0, 0, /* SBWC */ +2, RL, RL, 0, 0, 0, 0, /* MTPR */ +2, RL, WL, 0, 0, 0, 0, /* MFPR */ +1, WL, 0, 0, 0, 0, 0, /* MOVPSL */ +1, RL, 0, 0, 0, 0, 0, /* PUSHL */ +2, AL, WL, 0, 0, 0, 0, /* MOVAL */ +1, AL, 0, 0, 0, 0, 0, /* PUSHAL */ +3, RL, VB, BB, 0, 0, 0, /* BBS */ +3, RL, VB, BB, 0, 0, 0, /* BBC */ +3, RL, VB, BB, 0, 0, 0, /* BBSS */ +3, RL, VB, BB, 0, 0, 0, /* BBCS */ +3, RL, VB, BB, 0, 0, 0, /* BBSC */ +3, RL, VB, BB, 0, 0, 0, /* BBCC */ +3, RL, VB, BB, 0, 0, 0, /* BBSSI */ +3, RL, VB, BB, 0, 0, 0, /* BBCCI */ +2, RL, BB, 0, 0, 0, 0, /* BLBS */ +2, RL, BB, 0, 0, 0, 0, /* BLBC */ +4, RL, RB, VB, WL, 0, 0, /* FFS */ +4, RL, RB, VB, WL, 0, 0, /* FFC */ +4, RL, RB, VB, RL, 0, 0, /* CMPV */ +4, RL, RB, VB, RL, 0, 0, /* CMPZV */ +4, RL, RB, VB, WL, 0, 0, /* EXTV */ +4, RL, RB, VB, WL, 0, 0, /* EXTZV */ +4, RL, RL, RB, VB, 0, 0, /* INSV */ +4, RL, RL, ML, BW, 0, 0, /* ACBL */ +3, RL, ML, BB, 0, 0, 0, /* AOBLSS */ +3, RL, ML, BB, 0, 0, 0, /* AOBLEQ */ +2, ML, BB, 0, 0, 0, 0, /* SOBGEQ */ +2, ML, BB, 0, 0, 0, 0, /* SOBGTR */ +2, RL, WB, 0, 0, 0, 0, /* CVTLB */ +2, RL, WW, 0, 0, 0, 0, /* CVTLW */ +6+DR_F, RB, RW, AB, RB, RW, AB, /* ASHP */ +3+DR_F, RL, RW, AB, 0, 0, 0, /* CVTLP */ +2, AB, AB, 0, 0, 0, 0, /* CALLG */ +2, RL, AB, 0, 0, 0, 0, /* CALLS */ +0, 0, 0, 0, 0, 0, 0, /* XFC */ +0, 0, 0, 0, 0, 0, 0, /* 0FD */ +0, 0, 0, 0, 0, 0, 0, /* 0FE */ +0, 0, 0, 0, 0, 0, 0, /* 0FF */ +0, 0, 0, 0, 0, 0, 0, /* 100-10F */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 110-11F */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 120-12F */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 130-13F */ +0, 0, 0, 0, 0, 0, 0, +ODC(2), RD, WO, 0, 0, 0, 0, /* CVTDH */ +2, RG, WL, 0, 0, 0, 0, /* CVTGF */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +2, RG, MQ, 0, 0, 0, 0, /* ADDG2 */ +3, RG, RG, WQ, 0, 0, 0, /* ADDG3 */ +2, RG, MQ, 0, 0, 0, 0, /* SUBG2 */ +3, RG, RG, WQ, 0, 0, 0, /* SUBG3 */ +2, RG, MQ, 0, 0, 0, 0, /* MULG2 */ +3, RG, RG, WQ, 0, 0, 0, /* MULG3 */ +2, RG, MQ, 0, 0, 0, 0, /* DIVG2 */ +3, RG, RG, WQ, 0, 0, 0, /* DIVG3 */ +2, RG, WB, 0, 0, 0, 0, /* CVTGB */ +2, RG, WW, 0, 0, 0, 0, /* CVTGW */ +2, RG, WL, 0, 0, 0, 0, /* CVTGL */ +2, RG, WL, 0, 0, 0, 0, /* CVTRGL */ +2, RB, WQ, 0, 0, 0, 0, /* CVTBG */ +2, RW, WQ, 0, 0, 0, 0, /* CVTWG */ +2, RL, WQ, 0, 0, 0, 0, /* CVTLG */ +4, RG, RG, MQ, BW, 0, 0, /* ACBG */ +2, RG, WQ, 0, 0, 0, 0, /* MOVG */ +2, RG, RG, 0, 0, 0, 0, /* CMPG */ +2, RG, WQ, 0, 0, 0, 0, /* MNEGG */ +1, RG, 0, 0, 0, 0, 0, /* TSTG */ +5, RG, RW, RG, WL, WQ, 0, /* EMODG */ +3, RG, RW, AB, 0, 0, 0, /* POLYG */ +ODC(2), RG, WO, 0, 0, 0, 0, /* CVTGH */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +ODC(2), RH, MO, 0, 0, 0, 0, /* ADDH2 */ +ODC(3), RH, RH, WO, 0, 0, 0, /* ADDH3 */ +ODC(2), RH, MO, 0, 0, 0, 0, /* SUBH2 */ +ODC(3), RH, RH, WO, 0, 0, 0, /* SUBH3 */ +ODC(2), RH, MO, 0, 0, 0, 0, /* MULH2 */ +ODC(3), RH, RH, WO, 0, 0, 0, /* MULH3 */ +ODC(2), RH, MO, 0, 0, 0, 0, /* DIVH2 */ +ODC(3), RH, RH, WO, 0, 0, 0, /* DIVH3 */ +ODC(2), RH, WB, 0, 0, 0, 0, /* CVTHB */ +ODC(2), RH, WW, 0, 0, 0, 0, /* CVTHW */ +ODC(2), RH, WL, 0, 0, 0, 0, /* CVTHL */ +ODC(2), RH, WL, 0, 0, 0, 0, /* CVTRHL */ +ODC(2), RB, WO, 0, 0, 0, 0, /* CVTBH */ +ODC(2), RW, WO, 0, 0, 0, 0, /* CVTWH */ +ODC(2), RL, WO, 0, 0, 0, 0, /* CVTLH */ +ODC(4), RH, RH, MO, BW, 0, 0, /* ACBH */ +ODC(2), RH, RO, 0, 0, 0, 0, /* MOVH */ +ODC(2), RH, RH, 0, 0, 0, 0, /* CMPH */ +ODC(2), RH, WO, 0, 0, 0, 0, /* MNEGH */ +ODC(1), RH, 0, 0, 0, 0, 0, /* TSTH */ +ODC(5), RH, RW, RH, WL, WO, 0, /* EMODH */ +ODC(3), RH, RW, AB, 0, 0, 0, /* POLYH */ +ODC(2), RH, WQ, 0, 0, 0, 0, /* CVTHG */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +0, 0, 0, 0, 0, 0, 0, /* reserved */ +ODC(1), WO, 0, 0, 0, 0, 0, /* CLRO */ +ODC(2), RO, RO, 0, 0, 0, 0, /* MOVO */ +ODC(2), AO, WL, 0, 0, 0, 0, /* MOVAO*/ +ODC(1), AO, 0, 0, 0, 0, 0, /* PUSHAO*/ +0, 0, 0, 0, 0, 0, 0, /* 180-18F */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 190-19F */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +ODC(2), RF, WO, 0, 0, 0, 0, /* CVTFH */ +2, RF, WQ, 0, 0, 0, 0, /* CVTFG */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 1A0-1AF */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 1B0-1BF */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 1C0-1CF */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 1D0-1DF */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 1E0-1EF */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, /* 1F0-1FF */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +ODC(2), RH, WL, 0, 0, 0, 0, /* CVTHF */ +ODC(2), RH, WQ, 0, 0, 0, 0, /* CVTHD */ +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0, +0, 0, 0, 0, 0, 0, 0 +}; + +/* Opcode mnemonics table */ + +const char *opcode[] = { +"HALT", "NOP", "REI", "BPT", "RET", "RSB", "LDPCTX", "SVPCTX", +"CVTPS", "CVTSP", "INDEX", "CRC", "PROBER", "PROBEW", "INSQUE", "REMQUE", +"BSBB", "BRB", "BNEQ", "BEQL", "BGTR", "BLEQ", "JSB", "JMP", +"BGEQ", "BLSS", "BGTRU", "BLEQU", "BVC", "BVS", "BGEQU", "BLSSU", +"ADDP4", "ADDP6", "SUBP4", "SUBP6", "CVTPT", "MULP", "CVTTP", "DIVP", +"MOVC3", "CMPC3", "SCANC", "SPANC", "MOVC5", "CMPC5", "MOVTC", "MOVTUC", +"BSBW", "BRW", "CVTWL", "CVTWB", "MOVP", "CMPP3", "CVTPL", "CMPP4", +"EDITPC", "MATCHC", "LOCC", "SKPC", "MOVZWL", "ACBW", "MOVAW", "PUSHAW", +"ADDF2", "ADDF3", "SUBF2", "SUBF3", "MULF2", "MULF3", "DIVF2", "DIVF3", +"CVTFB", "CVTFW", "CVTFL", "CVTRFL", "CVTBF", "CVTWF", "CVTLF", "ACBF", +"MOVF", "CMPF", "MNEGF", "TSTF", "EMODF", "POLYF", "CVTFD", NULL, +"ADAWI", NULL, NULL, NULL, "INSQHI", "INSQTI", "REMQHI", "REMQTI", +"ADDD2", "ADDD3", "SUBD2", "SUBD3", "MULD2", "MULD3", "DIVD2", "DIVD3", +"CVTDB", "CVTDW", "CVTDL", "CVTRDL", "CVTBD", "CVTWD", "CVTLD", "ACBD", +"MOVD", "CMPD", "MNEGD", "TSTD", "EMODD", "POLYD", "CVTDF", NULL, +"ASHL", "ASHQ", "EMUL", "EDIV", "CLRQ", "MOVQ", "MOVAQ", "PUSHAQ", +"ADDB2", "ADDB3", "SUBB2", "SUBB3", "MULB2", "MULB3", "DIVB2", "DIVB3", +"BISB2", "BISB3", "BICB2", "BICB3", "XORB2", "XORB3", "MNEGB", "CASEB", +"MOVB", "CMPB", "MCOMB", "BITB", "CLRB", "TSTB", "INCB", "DECB", +"CVTBL", "CVTBW", "MOVZBL", "MOVZBW", "ROTL", "ACBB", "MOVAB", "PUSHAB", +"ADDW2", "ADDW3", "SUBW2", "SUBW3", "MULW2", "MULW3", "DIVW2", "DIVW3", +"BISW2", "BISW3", "BICW2", "BICW3", "XORW2", "XORW3", "MNEGW", "CASEW", +"MOVW", "CMPW", "MCOMW", "BITW", "CLRW", "TSTW", "INCW", "DECW", +"BISPSW", "BICPSW", "POPR", "PUSHR", "CHMK", "CHME", "CHMS", "CHMU", +"ADDL2", "ADDL3", "SUBL2", "SUBL3", "MULL2", "MULL3", "DIVL2", "DIVL3", +"BISL2", "BISL3", "BICL2", "BICL3", "XORL2", "XORL3", "MNEGL", "CASEL", +"MOVL", "CMPL", "MCOML", "BITL", "CLRL", "TSTL", "INCL", "DECL", +"ADWC", "SBWC", "MTPR", "MFPR", "MOVPSL", "PUSHL", "MOVAL", "PUSHAL", +"BBS", "BBC", "BBSS", "BBCS", "BBSC", "BBCC", "BBSSI", "BBCCI", +"BLBS", "BLBC", "FFS", "FFC", "CMPV", "CMPZV", "EXTV", "EXTZV", +"INSV", "ACBL", "AOBLSS", "AOBLEQ", "SOBGEQ", "SOBGTR", "CVTLB", "CVTLW", +"ASHP", "CVTLP", "CALLG", "CALLS", "XFC", NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 100 - 11F */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 120 - 13F */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, "CVTDH", "CVTGF", NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"ADDG2", "ADDG3", "SUBG2", "SUBG3", "MULG2", "MULG3", "DIVG2", "DIVG3", +"CVTGB", "CVTGW", "CVTGL", "CVTRGL", "CVTBG", "CVTWG", "CVTLG", "ACBG", +"MOVG", "CMPG", "MNEGG", "TSTG", "EMODG", "POLYG", "CVTGH", NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"ADDH2", "ADDH3", "SUBH2", "SUBH3", "MULH2", "MULH3", "DIVH2", "DIVH3", +"CVTHB", "CVTHW", "CVTHL", "CVTRHL", "CVTBH", "CVTWH", "CVTLH", "ACBH", +"MOVH", "CMPH", "MNEGH", "TSTH", "EMODH", "POLYH", "CVTHG", NULL, +NULL, NULL, NULL, NULL, "CLRO", "MOVO", "MOVAO", "PUSHAO", +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 180 - 19F */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +"CVTFH", "CVTFG", NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1A0 - 1BF */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1C0 - 1DF */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, /* 1E0 - 1FF */ +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +NULL, NULL, NULL, NULL, NULL, NULL, "CVTHF", "CVTHD", +NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL +}; + +const char *altcod[] = { +"CLRF", "CLRD", "CLRG", "CLRH", "MOVAF", "MOVAD", "MOVAG", "MOVAH", +"PUSHAF", "PUSHAD", "PUSHAG", "PUSHAH", "BNEQU", "BEQLU", "BCC", "BCS", +NULL +}; + +const int32 altop[] = { + 0xD4, 0x7C, 0x7C, 0x17C, 0xDE, 0x7E, 0x7E, 0x17E, + 0xDF, 0x7F, 0x7F, 0x17F, 0x12, 0x13, 0x1E, 0x1F + }; + +const char* regname[] = { + "R0", "R1", "R2", "R3", "R4", "R5", "R6", "R7", + "R8", "R9", "R10", "R11", "AP", "FP", "SP", "PC" + }; + +#define GETNUM(d,n) for (k = d = 0; k < n; k++) \ + d = d | (((int32) val[vp++]) << (k * 8)) + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym (FILE *of, t_addr exta, t_value *val, + UNIT *uptr, int32 sw) +{ +uint32 addr = (uint32) exta; +int32 c, k, num, vp, lnt, rdx; +t_stat r; +DEVICE *dptr; + +if (uptr == NULL) uptr = &cpu_unit; /* anon = CPU */ +if ((sw & SIM_SW_STOP) && (PSL & PSL_CM)) /* stop in CM? */ + sw = sw | SWMASK ('P'); /* force CM print */ +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) return SCPE_IERR; +if (dptr->dwidth != 8) return SCPE_ARG; /* byte dev only */ +if (sw & SWMASK ('B')) lnt = 1; /* get length */ +else if (sw & SWMASK ('W')) lnt = 2; +else if (sw & SWMASK ('L')) lnt = 4; +else lnt = (uptr == &cpu_unit)? 4: 1; +if (sw & SWMASK ('D')) rdx = 10; /* get radix */ +else if (sw & SWMASK ('O')) rdx = 8; +else if (sw & SWMASK ('H')) rdx = 16; +else rdx = dptr->dradix; +if ((sw & SWMASK ('A')) || (sw & SWMASK ('C'))) { /* char format? */ + for (vp = lnt - 1; vp >= 0; vp--) { + c = (int32) val[vp] & 0x7F; + fprintf (of, (c < 0x20)? "<%02X>": "%c", c); + } + return -(lnt - 1); /* return # chars */ + } + +if ((sw & (SWMASK ('P') | SWMASK ('R'))) && /* cmode or rad50? */ + (uptr == &cpu_unit)) { + r = fprint_sym_cm (of, addr, val, sw); /* decode inst */ + if (r <= 0) return r; + } + +if ((sw & SWMASK ('M')) && (uptr == &cpu_unit)) { /* inst format? */ + r = fprint_sym_m (of, addr, val); /* decode inst */ + if (r <= 0) return r; + } + +vp = 0; /* init ptr */ +GETNUM (num, lnt); /* get number */ +fprint_val (of, (uint32) num, rdx, lnt * 8, PV_RZRO); +return -(vp - 1); +} + +/* Symbolic decode for -m + + Inputs: + of = output stream + addr = current PC + *val = values to decode + Outputs: + return = if >= 0, error code + if < 0, number of extra bytes retired +*/ + +t_stat fprint_sym_m (FILE *of, uint32 addr, t_value *val) +{ +int32 i, k, vp, inst, numspec; +int32 num, spec, rn, disp, index; + +vp = 0; /* init ptr */ +inst = (int32) val[vp++]; /* get opcode */ +if (inst == 0xFD) inst = 0x100 | (int32) val[vp++]; /* 2 byte op? */ +if (opcode[inst] == NULL) return SCPE_ARG; /* defined? */ +numspec = DR_GETNSP (drom[inst][0]); /* get # spec */ +if (numspec == 0) numspec = DR_GETUSP (drom[inst][0]); +fprintf (of, "%s", opcode[inst]); /* print name */ +for (i = 0; i < numspec; i++) { /* loop thru spec */ + fputc (i? ',': ' ', of); /* separator */ + disp = drom[inst][i + 1]; /* get drom value */ + if (disp == BB) { /* byte br disp? */ + GETNUM (num, 1); + fprintf (of, "%-X", SXTB (num) + addr + vp); + } + else if (disp == BW) { /* word br disp? */ + GETNUM (num, 2); + fprintf (of, "%-X", SXTW (num) + addr + vp); + } + else { + spec = (int32) val[vp++]; /* get specifier */ + if ((spec & 0xF0) == IDX) { /* index? */ + index = spec; /* copy, get next */ + spec = (int32) val[vp++]; + } + else index = 0; + rn = spec & 0xF; /* get reg # */ + switch (spec & 0xF0) { /* case on mode */ + + case SH0: case SH1: case SH2: case SH3: /* s^# */ + fprintf (of, "#%-X", spec); + break; + + case GRN: /* Rn */ + fprintf (of, "%-s", regname[rn]); + break; + + case RGD: /* (Rn) */ + fprintf (of, "(%-s)", regname[rn]); + break; + + case ADC: /* -(Rn) */ + fprintf (of, "-(%-s)", regname[rn]); + break; + + case AIN: /* (Rn)+, #n */ + if (rn != nPC) fprintf (of, "(%-s)+", regname[rn]); + else { + if (DR_LNT (disp) == L_OCTA) + vp = fprint_sym_qoimm (of, val, vp, 4); + else if (DR_LNT (disp) == L_QUAD) + vp = fprint_sym_qoimm (of, val, vp, 2); + else { + GETNUM (num, DR_LNT (disp)); + fprintf (of, "#%-X", num); + } + } + break; + + case AID: /* @(Rn)+, @#n */ + if (rn != nPC) fprintf (of, "@(%-s)+", regname[rn]); + else { + GETNUM (num, 4); + fprintf (of, "@#%-X", num); + } + break; + + case BDD: /* @b^d(r),@b^n */ + fputc ('@', of); + case BDP: /* b^d(r), b^n */ + GETNUM (num, 1); + if (rn == nPC) fprintf (of, "%-X", addr + vp + SXTB (num)); + else if (num & BSIGN) fprintf (of, "-%-X(%-s)", + -num & BMASK, regname[rn]); + else fprintf (of, "%-X(%-s)", num, regname[rn]); + break; + + case WDD: /* @w^d(r),@w^n */ + fputc ('@', of); + case WDP: /* w^d(r), w^n */ + GETNUM (num, 2); + if (rn == nPC) fprintf (of, "%-X", addr + vp + SXTW (num)); + else if (num & WSIGN) fprintf (of, "-%-X(%-s)", + -num & WMASK, regname[rn]); + else fprintf (of, "%-X(%-s)", num, regname[rn]); + break; + + case LDD: /* @l^d(r),@l^n */ + fputc ('@', of); + case LDP: /* l^d(r),l^n */ + GETNUM (num, 4); + if (rn == nPC) fprintf (of, "%-X", addr + vp + num); + else if (num & LSIGN) fprintf (of, "-%-X(%-s)", + -num, regname[rn]); + else fprintf (of, "%-X(%-s)", num, regname[rn]); + break; + } /* end case */ + if (index) fprintf (of, "[%-s]", regname[index & 0xF]); + } /* end else */ + } /* end for */ +return -(vp - 1); +} + +/* Symbolic decode, quad/octa immediates + + Inputs: + *of = output stream + *val = pointer to input values + vp = current index into val + lnt = number of longwords in immediate + Outputs: + vp = updated index into val +*/ + +int32 fprint_sym_qoimm (FILE *of, t_value *val, int32 vp, int32 lnt) +{ +int32 i, k, startp, num[4]; + +for (i = 0; i < lnt; i++) { GETNUM (num[lnt - 1 - i], 4); } +for (i = startp = 0; i < lnt; i++) { + if (startp) fprintf (of, "%08X", num[i]); + else if (num[i] || (i == (lnt - 1))) { + fprintf (of, "#%-X", num[i]); + startp = 1; + } + } +return vp; +} + +#define PUTNUM(d,n) for (k = 0; k < n; k++) val[vp++] = (d >> (k * 8)) & 0xFF + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym (char *cptr, t_addr exta, UNIT *uptr, t_value *val, int32 sw) +{ +uint32 addr = (uint32) exta; +int32 k, rdx, lnt, num, vp; +t_stat r; +DEVICE *dptr; +static const uint32 maxv[5] = { 0, 0xFF, 0xFFFF, 0, 0xFFFFFFFF }; + +if (uptr == NULL) uptr = &cpu_unit; /* anon = CPU */ +dptr = find_dev_from_unit (uptr); /* find dev */ +if (dptr == NULL) return SCPE_IERR; +if (dptr->dwidth != 8) return SCPE_ARG; /* byte dev only */ +if (sw & SWMASK ('B')) lnt = 1; /* get length */ +else if (sw & SWMASK ('W')) lnt = 2; +else if (sw & SWMASK ('L')) lnt = 4; +else lnt = (uptr == &cpu_unit)? 4: 1; +if (sw & SWMASK ('D')) rdx = 10; /* get radix */ +else if (sw & SWMASK ('O')) rdx = 8; +else if (sw & SWMASK ('H')) rdx = 16; +else rdx = dptr->dradix; + +if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) /* ASCII char? */ + return parse_char (cptr, val, lnt); +if ((sw & SWMASK ('C')) || ((*cptr == '"') && cptr++)) /* ASCII string? */ + return parse_char (cptr, val, sim_emax); + +if ((sw & (SWMASK ('P') | SWMASK ('R'))) && /* cmode or rad50? */ + (uptr == &cpu_unit)) { + r = parse_sym_cm (cptr, addr, val, sw); /* try to parse */ + if (r <= 0) return r; + } + +if (uptr == &cpu_unit) { /* cpu only */ + r = parse_sym_m (cptr, addr, val); /* try to parse inst */ + if (r <= 0) return r; + } + +num = (int32) get_uint (cptr, rdx, maxv[lnt], &r); /* get number */ +if (r != SCPE_OK) return r; +vp = 0; +PUTNUM (num, lnt); /* store */ +return -(lnt - 1); +} + +/* Character input for -a or -c + + Inputs: + *cptr = pointer to input string + addr = current PC + *val = pointer to output values + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_char (char *cptr, t_value *val, int32 lnt) +{ +int32 vp; + +if (*cptr == 0) return SCPE_ARG; +vp = 0; +while ((vp < lnt) && *cptr) { /* get chars */ + val[vp++] = *cptr++; + } +return -(vp - 1); /* return # chars */ +} + +/* Symbolic input for -m + + Inputs: + *cptr = pointer to input string + addr = current PC + *val = pointer to output values + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym_m (char *cptr, uint32 addr, t_value *val) +{ +int32 i, numspec, disp, opc, vp; +t_stat r; +char gbuf[CBUFSIZE]; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +for (i = 0, opc = -1; (i < NUM_INST) && (opc < 0); i++) { + if (opcode[i] && strcmp (gbuf, opcode[i]) == 0) opc = i; + } +if (opc < 0) { /* check alternates */ + for (i = 0; altcod[i] && (opc < 0); i++) { + if (strcmp (gbuf, altcod[i]) == 0) opc = altop[i]; + } + } +if (opc < 0) return SCPE_ARG; /* undefined? */ +vp = 0; +if (opc >= 0x100) val[vp++] = 0xFD; /* 2 byte? */ +val[vp++] = opc & 0xFF; /* store opcode */ +numspec = DR_GETNSP (drom[opc][0]); /* get # specifiers */ +if (numspec == 0) numspec = DR_GETUSP (drom[opc][0]); +for (i = 1; i <= numspec; i++) { /* loop thru specs */ + if (i == numspec) cptr = get_glyph (cptr, gbuf, 0); + else cptr = get_glyph (cptr, gbuf, ','); /* get specifier */ + disp = drom[opc][i]; /* get drom value */ + if (disp == BB) vp = parse_brdisp (gbuf, addr, val, vp, 0, &r); + else if (disp == BW) vp = parse_brdisp (gbuf, addr, val, vp, 1, &r); + else vp = parse_spec (gbuf, addr, val, vp, disp, &r); + if (r != SCPE_OK) return r; + } +if (*cptr != 0) return SCPE_ARG; +return -(vp - 1); +} + +/* Parse a branch displacement + + Inputs: + cptr = pointer to input buffer + addr = current address + val = pointer to output array + vp = current pointer in output array + lnt = length (0 = byte, 1 = word) + r = pointer to status + Outputs: + vp = updated output pointer +*/ + +int32 parse_brdisp (char *cptr, uint32 addr, t_value *val, int32 vp, + int32 lnt, t_stat *r) +{ +int32 k, dest, num; + +dest = (int32) get_uint (cptr, 16, 0xFFFFFFFF, r); /* get value */ +num = dest - (addr + vp + lnt + 1); /* compute offset */ +if ((num > (lnt? 32767: 127)) || (num < (lnt? -32768: -128))) + *r = SCPE_ARG; +else { + PUTNUM (num, lnt + 1); /* store offset */ + *r = SCPE_OK; + } +return vp; +} + +/* Parse a specifier + + Inputs: + cptr = pointer to input buffer + addr = current address + val = pointer to output array + vp = current pointer in output array + disp = specifier dispatch + r = pointer to status + Outputs: + vp = updated output pointer +*/ + +#define SP_IND 0x200 /* indirect */ +#define SP_V_FORCE 6 +#define SP_FS 0x040 /* S^ */ +#define SP_FI 0x080 /* I^ */ +#define SP_FB 0x0C0 /* B^ */ +#define SP_FW 0x100 /* W^ */ +#define SP_FL 0x140 /* L^ */ +#define SP_LIT 0x020 /* # */ +#define SP_PLUS 0x010 /* plus */ +#define SP_MINUS 0x008 /* minus */ +#define SP_NUM 0x004 /* number */ +#define SP_IDX 0x002 /* (Rn) */ +#define SP_POSTP 0x001 /* trailing + */ +#define M1C(c,v) if (*cptr == c) { cptr++; fl = fl | v; } +#define SPUTNUM(v,d) if (fl & SP_MINUS) v = -v; PUTNUM (v, d) +#define PARSE_LOSE { *r = SCPE_ARG; return vp; } +#define SEL_LIM(p,m,u) ((fl & SP_PLUS)? (p): ((fl & SP_MINUS)? (m): (u))) + +int32 parse_spec (char *cptr, uint32 addr, t_value *val, int32 vp, int32 disp, t_stat *r) +{ +int32 i, k, litsize, rn, index; +int32 num, dispsize, mode; +int32 lit[4] = { 0 }; +int32 fl = 0; +char c, *tptr; +const char *force[] = { "S^", "I^", "B^", "W^", "L^", NULL }; + +*r = SCPE_OK; /* assume ok */ +M1C ('@', SP_IND); /* look for @ */ +if (tptr = parse_rnum (cptr, &rn)) { /* look for Rn */ + if (*cptr == '[') { /* look for [Rx] */ + cptr = parse_rnum (++cptr, &index); + if ((cptr == NULL) || (*cptr++ != ']')) PARSE_LOSE; + val[vp++] = index | IDX; + } + else val[vp++] = rn | GRN | (fl? 1: 0); /* Rn or @Rn */ + if (*tptr != 0) *r = SCPE_ARG; /* must be done */ + return vp; + } +for (i = 0; force[i]; i++) { /* look for x^ */ + if (strncmp (cptr, force[i], 2) == 0) { + cptr = cptr + 2; + fl = fl | ((i + 1) << SP_V_FORCE); + break; + } + } +M1C ('#', SP_LIT); /* look for # */ +M1C ('+', SP_PLUS); /* look for + */ +M1C ('-', SP_MINUS); /* look for - */ +for (litsize = 0;; cptr++) { /* look for mprec int */ + c = *cptr; + if ((c < '0') || (c > 'F') || ((c > '9') && (c < 'A'))) break; + num = (c <= '9')? c - '0': c - 'A' + 10; + fl = fl | SP_NUM; + for (i = 3; i >= 0; i--) { + lit[i] = lit[i] << 4; + if (i > 0) lit[i] = lit[i] | ((lit[i - 1] >> 28) & 0xF); + else lit[i] = lit[i] | num; + if (lit[i] && (i > litsize)) litsize = i; + } + } +if (*cptr == '(') { /* look for (Rn) */ + cptr = parse_rnum (++cptr, &rn); + if ((cptr == NULL) || (*cptr++ != ')')) PARSE_LOSE; + fl = fl | SP_IDX; + } +M1C ('+', SP_POSTP); /* look for + */ +if (*cptr == '[') { /* look for [Rx] */ + cptr = parse_rnum (++cptr, &index); + if ((cptr == NULL) || (*cptr++ != ']')) PARSE_LOSE; + val[vp++] = index | IDX; + } +switch (fl) { /* case on state */ + + case SP_FS|SP_LIT|SP_NUM: /* S^#n */ + case SP_FS|SP_LIT|SP_PLUS|SP_NUM: /* S^#+n */ + if ((litsize > 0) || (lit[0] & ~0x3F)) PARSE_LOSE; + val[vp++] = lit[0]; + break; + + case SP_IDX: /* (Rn) */ + val[vp++] = rn | RGD; + break; + + case SP_MINUS|SP_IDX: /* -(Rn) */ + val[vp++] = rn | ADC; + break; + + case SP_IDX|SP_POSTP: /* (Rn)+ */ + val[vp++] = rn | AIN; + break; + + case SP_LIT|SP_NUM: /* #n */ + case SP_LIT|SP_PLUS|SP_NUM: /* #+n */ + if ((litsize == 0) && ((lit[0] & ~0x3F) == 0)) { + val[vp++] = lit[0]; + break; + } /* fall thru */ + case SP_LIT|SP_MINUS|SP_NUM: /* #-n */ + case SP_FI|SP_LIT|SP_NUM: /* I^#n */ + case SP_FI|SP_LIT|SP_PLUS|SP_NUM: /* I^#+n */ + case SP_FI|SP_LIT|SP_MINUS|SP_NUM: /* I^#-n */ + val[vp++] = nPC | AIN; + disp = disp & DR_LNMASK; + switch (disp) { /* case spec lnt */ + case 00: /* check fit */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7F, 0x80, 0xFF))) PARSE_LOSE; + SPUTNUM (lit[0], 1); /* store */ + break; + case 01: /* check fit */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7FFF, 0x8000, 0xFFFF))) PARSE_LOSE; + SPUTNUM (lit[0], 2); + break; + case 02: /* check 1 lw */ + if (litsize > 0) PARSE_LOSE; + SPUTNUM (lit[0], 4); + break; + case 03: /* check 2 lw */ + if (litsize > 1) PARSE_LOSE; + vp = parse_sym_qoimm (lit, val, vp, 2, fl & SP_MINUS); + break; + case 04: + vp = parse_sym_qoimm (lit, val, vp, 4, fl & SP_MINUS); + break; + } /* end case lnt */ + break; + + case SP_IND|SP_IDX|SP_POSTP: /* @(Rn)+ */ + val[vp++] = rn | AID; + break; + + case SP_IND|SP_LIT|SP_NUM: /* @#n */ + if (litsize > 0) PARSE_LOSE; + val[vp++] = nPC | AID; + PUTNUM (lit[0], 4); + break; + case SP_NUM|SP_IDX: /* d(rn) */ + case SP_PLUS|SP_NUM|SP_IDX: /* +d(rn) */ + case SP_MINUS|SP_NUM|SP_IDX: /* -d(rn) */ + case SP_IND|SP_NUM|SP_IDX: /* @d(rn) */ + case SP_IND|SP_PLUS|SP_NUM|SP_IDX: /* @+d(rn) */ + case SP_IND|SP_MINUS|SP_NUM|SP_IDX: /* @-d(rn) */ + if (litsize > 0) PARSE_LOSE; + dispsize = 4; /* find fit for */ + mode = LDP; /* displacement */ + if (lit[0] >= 0) { + if (lit[0] <= SEL_LIM (0x7F, 0x80, 0xFF)) { + dispsize = 1; + mode = BDP; + } + else if (lit[0] <= SEL_LIM (0x7FFF, 0x8000, 0xFFFF)) { + dispsize = 2; + mode = WDP; + } + } + val[vp++] = mode | rn | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], dispsize); + break; + + case SP_FB|SP_NUM|SP_IDX: /* B^d(rn) */ + case SP_FB|SP_PLUS|SP_NUM|SP_IDX: /* B^+d(rn) */ + case SP_FB|SP_MINUS|SP_NUM|SP_IDX: /* B^-d(rn) */ + case SP_IND|SP_FB|SP_NUM|SP_IDX: /* @B^d(rn) */ + case SP_IND|SP_FB|SP_PLUS|SP_NUM|SP_IDX: /* @B^+d(rn) */ + case SP_IND|SP_FB|SP_MINUS|SP_NUM|SP_IDX: /* @B^-d(rn) */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7F, 0x80, 0xFF))) PARSE_LOSE; + val[vp++] = rn | BDP | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], 1); + break; + + case SP_FW|SP_NUM|SP_IDX: /* W^d(rn) */ + case SP_FW|SP_PLUS|SP_NUM|SP_IDX: /* W^+d(rn) */ + case SP_FW|SP_MINUS|SP_NUM|SP_IDX: /* W^-d(rn) */ + case SP_IND|SP_FW|SP_NUM|SP_IDX: /* @W^d(rn) */ + case SP_IND|SP_FW|SP_PLUS|SP_NUM|SP_IDX: /* @W^+d(rn) */ + case SP_IND|SP_FW|SP_MINUS|SP_NUM|SP_IDX: /* @W^-d(rn) */ + if ((litsize > 0) || (lit[0] < 0) || + (lit[0] > SEL_LIM (0x7FFF, 0x8000, 0xFFFF))) PARSE_LOSE; + val[vp++] = rn | WDP | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], 2); + break; + + case SP_FL|SP_NUM|SP_IDX: /* L^d(rn) */ + case SP_FL|SP_PLUS|SP_NUM|SP_IDX: /* L^+d(rn) */ + case SP_FL|SP_MINUS|SP_NUM|SP_IDX: /* L^-d(rn) */ + case SP_IND|SP_FL|SP_NUM|SP_IDX: /* @L^d(rn) */ + case SP_IND|SP_FL|SP_PLUS|SP_NUM|SP_IDX: /* @L^+d(rn) */ + case SP_IND|SP_FL|SP_MINUS|SP_NUM|SP_IDX: /* @L^-d(rn) */ + if ((litsize > 0) || (lit[0] < 0)) PARSE_LOSE; + val[vp++] = rn | LDP | ((fl & SP_IND)? 0x10: 0); + SPUTNUM (lit[0], 4); + break; + + case SP_NUM: /* n */ + case SP_IND|SP_NUM: /* @n */ + if (litsize > 0) PARSE_LOSE; + num = lit[0] - (addr + vp + 2); /* fit in byte? */ + if ((num >= -128) && (num <= 127)) { + mode = BDP; + dispsize = 1; + } + else { + num = lit[0] - (addr + vp + 3); /* fit in word? */ + if ((num >= -32768) && (num <= 32767)) { + mode = WDP; + dispsize = 2; + } + else { + num = lit[0] - (addr + vp + 5); /* no, use lw */ + mode = LDP; + dispsize = 4; + } + } + val[vp++] = mode | nPC | ((fl & SP_IND)? 1: 0); + PUTNUM (num, dispsize); + break; + + case SP_FB|SP_NUM: /* B^n */ + case SP_IND|SP_FB|SP_NUM: /* @B^n */ + num = lit[0] - (addr + vp + 2); + if ((litsize > 0) || (num > 127) || (num < -128)) PARSE_LOSE; + val[vp++] = nPC | BDP | ((fl & SP_IND)? 1: 0); + PUTNUM (num, 1); + break; + + case SP_FW|SP_NUM: /* W^n */ + case SP_IND|SP_FW|SP_NUM: /* @W^n */ + num = lit[0] - (addr + vp + 3); + if ((litsize > 0) || (num > 32767) || (num < -32768)) PARSE_LOSE; + val[vp++] = nPC | WDP | ((fl & SP_IND)? 1: 0); + PUTNUM (num, 2); + break; + + case SP_FL|SP_NUM: /* L^n */ + case SP_IND|SP_FL|SP_NUM: /* @L^n */ + num = lit[0] - (addr + vp + 5); + if (litsize > 0) PARSE_LOSE; + val[vp++] = nPC | LDP | ((fl & SP_IND)? 1: 0); + PUTNUM (num, 4); + break; + + default: + PARSE_LOSE; + } /* end case */ + +if (*cptr != 0) *r = SCPE_ARG; /* must be done */ +return vp; +} + +char *parse_rnum (char *cptr, int32 *rn) +{ +int32 i, lnt; +t_value regnum; +char *tptr; + +for (i = 15; i >= 0; i--) { /* chk named reg */ + lnt = strlen (regname[i]); + if (strncmp (cptr, regname[i], lnt) == 0) { + *rn = i; + return cptr + lnt; + } + } +if (*cptr++ != 'R') return NULL; /* look for R */ +regnum = strtotv (cptr, &tptr, 10); /* look for reg # */ +if ((cptr == tptr) || (regnum > 15)) return NULL; +*rn = (int32) regnum; +return tptr; +} + +int32 parse_sym_qoimm (int32 *lit, t_value *val, int32 vp, int lnt, int32 minus) +{ +int32 i, k, prev; + +for (i = prev = 0; i < lnt; i++) { + if (minus) prev = lit[i] = ~lit[i] + (prev == 0); + PUTNUM (lit[i], 4); + } +return vp; +} + diff --git a/VAX/vax_syscm.c b/VAX/vax_syscm.c new file mode 100644 index 0000000..c209104 --- /dev/null +++ b/VAX/vax_syscm.c @@ -0,0 +1,664 @@ +/* vax_syscm.c: PDP-11 compatibility mode symbolic decode and parse + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 12-Nov-06 RMS Fixed operand order in EIS instructions (found by W.F.J. Mueller) + 27-Sep-05 RMS Fixed warnings compiling with 64b addresses + 15-Sep-04 RMS Cloned from pdp11_sys.c +*/ + +#include "vax_defs.h" +#include + +extern UNIT cpu_unit; + +/* Symbol tables */ +/* Warning: for literals, the class number MUST equal the field width!! */ + +#define I_V_CL 18 /* class bits */ +#define I_M_CL 017 /* class mask */ +#define I_V_NPN 0 /* no operands */ +#define I_V_REG 1 /* reg */ +#define I_V_SOP 2 /* operand */ +#define I_V_3B 3 /* 3b literal */ +#define I_V_RSOP 4 /* reg, operand */ +#define I_V_BR 5 /* cond branch */ +#define I_V_6B 6 /* 6b literal */ +#define I_V_SOB 7 /* reg, disp */ +#define I_V_8B 8 /* 8b literal */ +#define I_V_DOP 9 /* double operand */ +#define I_V_CCC 10 /* CC clear */ +#define I_V_CCS 11 /* CC set */ +#define I_V_SOPR 12 /* operand, reg */ +#define I_NPN (I_V_NPN << I_V_CL) +#define I_REG (I_V_REG << I_V_CL) +#define I_SOP (I_V_SOP << I_V_CL) +#define I_3B (I_V_3B << I_V_CL) +#define I_6B (I_V_6B << I_V_CL) +#define I_BR (I_V_BR << I_V_CL) +#define I_8B (I_V_8B << I_V_CL) +#define I_RSOP (I_V_RSOP << I_V_CL) +#define I_SOB (I_V_SOB << I_V_CL) +#define I_DOP (I_V_DOP << I_V_CL) +#define I_CCC (I_V_CCC << I_V_CL) +#define I_CCS (I_V_CCS << I_V_CL) +#define I_SOPR (I_V_SOPR << I_V_CL) + +static const int32 masks[] = { + 0177777, 0177770, 0177700, 0177770, + 0177000, 0177400, 0177700, 0177000, + 0177400, 0170000, 0177777, 0177777, + 0177000 + }; + +static const char *opcode[] = { +"HALT","WAIT","RTI","BPT", +"IOT","RESET","RTT","MFPT", +"JMP","RTS","SPL", +"NOP","CLC","CLV","CLV CLC", +"CLZ","CLZ CLC","CLZ CLV","CLZ CLV CLC", +"CLN","CLN CLC","CLN CLV","CLN CLV CLC", +"CLN CLZ","CLN CLZ CLC","CLN CLZ CLC","CCC", +"NOP","SEC","SEV","SEV SEC", +"SEZ","SEZ SEC","SEZ SEV","SEZ SEV SEC", +"SEN","SEN SEC","SEN SEV","SEN SEV SEC", +"SEN SEZ","SEN SEZ SEC","SEN SEZ SEC","SCC", +"SWAB","BR","BNE","BEQ", +"BGE","BLT","BGT","BLE", +"JSR", +"CLR","COM","INC","DEC", +"NEG","ADC","SBC","TST", +"ROR","ROL","ASR","ASL", +"MARK","MFPI","MTPI","SXT", +"CSM", "TSTSET","WRTLCK", +"MOV","CMP","BIT","BIC", +"BIS","ADD", +"MUL","DIV","ASH","ASHC", +"XOR", +"FADD","FSUB","FMUL","FDIV", +"L2DR", +"MOVC","MOVRC","MOVTC", +"LOCC","SKPC","SCANC","SPANC", +"CMPC","MATC", +"ADDN","SUBN","CMPN","CVTNL", +"CVTPN","CVTNP","ASHN","CVTLN", +"L3DR", +"ADDP","SUBP","CMPP","CVTPL", +"MULP","DIVP","ASHP","CVTLP", +"MOVCI","MOVRCI","MOVTCI", +"LOCCI","SKPCI","SCANCI","SPANCI", +"CMPCI","MATCI", +"ADDNI","SUBNI","CMPNI","CVTNLI", +"CVTPNI","CVTNPI","ASHNI","CVTLNI", +"ADDPI","SUBPI","CMPPI","CVTPLI", +"MULPI","DIVPI","ASHPI","CVTLPI", +"SOB", +"BPL","BMI","BHI","BLOS", +"BVC","BVS","BCC","BCS", +"BHIS","BLO", /* encode only */ +"EMT","TRAP", +"CLRB","COMB","INCB","DECB", +"NEGB","ADCB","SBCB","TSTB", +"RORB","ROLB","ASRB","ASLB", +"MTPS","MFPD","MTPD","MFPS", +"MOVB","CMPB","BITB","BICB", +"BISB","SUB", +NULL +}; + +static const int32 opc_val[] = { +0000000+I_NPN, 0000001+I_NPN, 0000002+I_NPN, 0000003+I_NPN, +0000004+I_NPN, 0000005+I_NPN, 0000006+I_NPN, 0000007+I_NPN, +0000100+I_SOP, 0000200+I_REG, 0000230+I_3B, +0000240+I_CCC, 0000241+I_CCC, 0000242+I_CCC, 0000243+I_NPN, +0000244+I_CCC, 0000245+I_NPN, 0000246+I_NPN, 0000247+I_NPN, +0000250+I_CCC, 0000251+I_NPN, 0000252+I_NPN, 0000253+I_NPN, +0000254+I_NPN, 0000255+I_NPN, 0000256+I_NPN, 0000257+I_CCC, +0000260+I_CCS, 0000261+I_CCS, 0000262+I_CCS, 0000263+I_NPN, +0000264+I_CCS, 0000265+I_NPN, 0000266+I_NPN, 0000267+I_NPN, +0000270+I_CCS, 0000271+I_NPN, 0000272+I_NPN, 0000273+I_NPN, +0000274+I_NPN, 0000275+I_NPN, 0000276+I_NPN, 0000277+I_CCS, +0000300+I_SOP, 0000400+I_BR, 0001000+I_BR, 0001400+I_BR, +0002000+I_BR, 0002400+I_BR, 0003000+I_BR, 0003400+I_BR, +0004000+I_RSOP, +0005000+I_SOP, 0005100+I_SOP, 0005200+I_SOP, 0005300+I_SOP, +0005400+I_SOP, 0005500+I_SOP, 0005600+I_SOP, 0005700+I_SOP, +0006000+I_SOP, 0006100+I_SOP, 0006200+I_SOP, 0006300+I_SOP, +0006400+I_6B, 0006500+I_SOP, 0006600+I_SOP, 0006700+I_SOP, +0007000+I_SOP, 0007200+I_SOP, 0007300+I_SOP, +0010000+I_DOP, 0020000+I_DOP, 0030000+I_DOP, 0040000+I_DOP, +0050000+I_DOP, 0060000+I_DOP, +0070000+I_SOPR, 0071000+I_SOPR, 0072000+I_SOPR, 0073000+I_SOPR, +0074000+I_RSOP, +0075000+I_REG, 0075010+I_REG, 0075020+I_REG, 0075030+I_REG, +0076020+I_REG, +0076030+I_NPN, 0076031+I_NPN, 0076032+I_NPN, +0076040+I_NPN, 0076041+I_NPN, 0076042+I_NPN, 0076043+I_NPN, +0076044+I_NPN, 0076045+I_NPN, +0076050+I_NPN, 0076051+I_NPN, 0076052+I_NPN, 0076053+I_NPN, +0076054+I_NPN, 0076055+I_NPN, 0076056+I_NPN, 0076057+I_NPN, +0076060+I_REG, +0076070+I_NPN, 0076071+I_NPN, 0076072+I_NPN, 0076073+I_NPN, +0076074+I_NPN, 0076075+I_NPN, 0076076+I_NPN, 0076077+I_NPN, +0076130+I_NPN, 0076131+I_NPN, 0076132+I_NPN, +0076140+I_NPN, 0076141+I_NPN, 0076142+I_NPN, 0076143+I_NPN, +0076144+I_NPN, 0076145+I_NPN, +0076150+I_NPN, 0076151+I_NPN, 0076152+I_NPN, 0076153+I_NPN, +0076154+I_NPN, 0076155+I_NPN, 0076156+I_NPN, 0076157+I_NPN, +0076170+I_NPN, 0076171+I_NPN, 0076172+I_NPN, 0076173+I_NPN, +0076174+I_NPN, 0076175+I_NPN, 0076176+I_NPN, 0076177+I_NPN, +0077000+I_SOB, +0100000+I_BR, 0100400+I_BR, 0101000+I_BR, 0101400+I_BR, +0102000+I_BR, 0102400+I_BR, 0103000+I_BR, 0103400+I_BR, +0103000+I_BR, 0103400+I_BR, +0104000+I_8B, 0104400+I_8B, +0105000+I_SOP, 0105100+I_SOP, 0105200+I_SOP, 0105300+I_SOP, +0105400+I_SOP, 0105500+I_SOP, 0105600+I_SOP, 0105700+I_SOP, +0106000+I_SOP, 0106100+I_SOP, 0106200+I_SOP, 0106300+I_SOP, +0106400+I_SOP, 0106500+I_SOP, 0106600+I_SOP, 0106700+I_SOP, +0110000+I_DOP, 0120000+I_DOP, 0130000+I_DOP, 0140000+I_DOP, +0150000+I_DOP, 0160000+I_DOP, +-1 +}; + +static const char *rname [] = { + "R0", "R1", "R2", "R3", "R4", "R5", "SP", "PC" + }; + +static const char r50_to_asc[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ$._0123456789"; + +/* Specifier decode + + Inputs: + *of = output stream + addr = current PC + spec = specifier + nval = next word + flag = TRUE if decoding for CPU + iflag = TRUE if decoding integer instruction + Outputs: + count = -number of extra words retired +*/ + +int32 fprint_spec (FILE *of, t_addr addr, int32 spec, int32 nval) +{ +int32 reg, mode; +static const int32 rgwd[8] = { 0, 0, 0, 0, 0, 0, -1, -1 }; +static const int32 pcwd[8] = { 0, 0, -1, -1, 0, 0, -1, -1 }; + +reg = spec & 07; +mode = ((spec >> 3) & 07); +switch (mode) { + + case 0: + fprintf (of, "%s", rname[reg]); + break; + + case 1: + fprintf (of, "(%s)", rname[reg]); + break; + + case 2: + if (reg != 7) fprintf (of, "(%s)+", rname[reg]); + else fprintf (of, "#%-X", nval); + break; + + case 3: + if (reg != 7) fprintf (of, "@(%s)+", rname[reg]); + else fprintf (of, "@#%-X", nval); + break; + + case 4: + fprintf (of, "-(%s)", rname[reg]); + break; + + case 5: + fprintf (of, "@-(%s)", rname[reg]); + break; + + case 6: + if (reg != 7) fprintf (of, "%-X(%s)", nval, rname[reg]); + else fprintf (of, "%-X", (nval + addr + 4) & 0177777); + break; + + case 7: + if (reg != 7) fprintf (of, "@%-X(%s)", nval, rname[reg]); + else fprintf (of, "@%-X", (nval + addr + 4) & 0177777); + break; + } /* end case */ + +return ((reg == 07)? pcwd[mode]: rgwd[mode]); +} + +/* Symbolic decode + + Inputs: + *of = output stream + addr = current PC + *val = values to decode + *uptr = pointer to unit + sw = switches + Outputs: + return = if >= 0, error code + if < 0, number of extra words retired +*/ + +t_stat fprint_sym_cm (FILE *of, t_addr addr, t_value *bytes, int32 sw) +{ +int32 i, j, c1, c2, c3, inst, srcm, srcr, dstm, dstr; +int32 l8b, brdisp, wd1; +uint32 val[3]; + +for (i = j = 0; i < 3; i++, j = j + 2) + val[i] = (int32) (bytes[j] | (bytes[j + 1] << 8)); + +if (sw & SWMASK ('R')) { /* radix 50? */ + if (val[0] > 0174777) return SCPE_ARG; /* max value */ + c3 = val[0] % 050; + c2 = (val[0] / 050) % 050; + c1 = val[0] / (050 * 050); + fprintf (of, "%c%c%c", r50_to_asc[c1], + r50_to_asc[c2], r50_to_asc[c3]); + return -1; + } +if (!(sw & SWMASK ('P')) || (addr & 1) || (addr > WMASK)) + return SCPE_ARG; + +inst = val[0]; /* inst */ +wd1 = 0; +for (i = 0; opc_val[i] >= 0; i++) { /* loop thru ops */ + j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + if ((opc_val[i] & 0177777) == (inst & masks[j])) { /* match? */ + srcm = (inst >> 6) & 077; /* opr fields */ + srcr = srcm & 07; + dstm = inst & 077; + dstr = dstm & 07; + l8b = inst & 0377; + switch (j) { /* case on class */ + + case I_V_NPN: case I_V_CCC: case I_V_CCS: /* no operands */ + fprintf (of, "%s", opcode[i]); + break; + + case I_V_REG: /* reg */ + fprintf (of, "%s %-s", opcode[i], rname[dstr]); + break; + + case I_V_SOP: /* sop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, dstm, val[1]); + break; + + case I_V_3B: /* 3b */ + fprintf (of, "%s %-X", opcode[i], dstr); + break; + + case I_V_6B: /* 6b */ + fprintf (of, "%s %-X", opcode[i], dstm); + break; + + case I_V_BR: /* cond branch */ + fprintf (of, "%s ", opcode[i]); + brdisp = (l8b + l8b + ((l8b & 0200)? 0177002: 2)) & 0177777; + fprintf (of, "%-X", (addr + brdisp) & 0177777); + break; + + case I_V_8B: /* 8b */ + fprintf (of, "%s %-X", opcode[i], l8b); + break; + + case I_V_SOB: /* sob */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + brdisp = (dstm * 2) - 2; + fprintf (of, "%-X", (addr - brdisp) & 0177777); + break; + + case I_V_RSOP: /* rsop */ + fprintf (of, "%s %s,", opcode[i], rname[srcr]); + wd1 = fprint_spec (of, addr, dstm, val[1]); + break; + + case I_V_SOPR: /* sopr */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, dstm, val[1]); + fprintf (of, ",%s", rname[srcr]); + break; + + case I_V_DOP: /* dop */ + fprintf (of, "%s ", opcode[i]); + wd1 = fprint_spec (of, addr, srcm, val[1]); + fprintf (of, ","); + wd1 += fprint_spec (of, addr - wd1 - wd1, dstm, + val[1 - wd1]); + break; + } /* end case */ + + return ((wd1 * 2) - 1); + } /* end if */ + } /* end for */ +return SCPE_ARG; /* no match */ +} + +#define A_PND 100 /* # seen */ +#define A_MIN 040 /* -( seen */ +#define A_PAR 020 /* (Rn) seen */ +#define A_REG 010 /* Rn seen */ +#define A_PLS 004 /* + seen */ +#define A_NUM 002 /* number seen */ +#define A_REL 001 /* relative addr seen */ + +/* Register number + + Inputs: + *cptr = pointer to input string + mchar = character to match after register name + Outputs: + rnum = 0..7 if a legitimate register + < 0 if error +*/ + +int32 get_reg (char *cptr, char mchar) +{ +int32 i; + +if (*(cptr + 2) != mchar) return -1; +for (i = 0; i < 8; i++) { + if (strncmp (cptr, rname[i], 2) == 0) return i; + } +return -1; +} + +/* Number or memory address + + Inputs: + *cptr = pointer to input string + *dptr = pointer to output displacement + *pflag = pointer to accumulating flags + Outputs: + cptr = pointer to next character in input string + NULL if parsing error + + Flags: 0 (no result), A_NUM (number), A_REL (relative) +*/ + +char *get_addr (char *cptr, int32 *dptr, int32 *pflag) +{ +int32 val, minus; +char *tptr; + +minus = 0; + +if (*cptr == '.') { /* relative? */ + *pflag = *pflag | A_REL; + cptr++; + } +if (*cptr == '+') { /* +? */ + *pflag = *pflag | A_NUM; + cptr++; + } +if (*cptr == '-') { /* -? */ + *pflag = *pflag | A_NUM; + minus = 1; + cptr++; + } +errno = 0; +val = strtoul (cptr, &tptr, 16); +if (cptr == tptr) { /* no number? */ + if (*pflag == (A_REL + A_NUM)) return NULL; /* .+, .-? */ + *dptr = 0; + return cptr; + } +if (errno || (*pflag == A_REL)) return NULL; /* .n? */ +*dptr = (minus? -val: val) & 0177777; +*pflag = *pflag | A_NUM; +return tptr; +} + +/* Specifier decode + + Inputs: + *cptr = pointer to input string + addr = current PC + n1 = 0 if no extra word used + -1 if extra word used in prior decode + *sptr = pointer to output specifier + *dptr = pointer to output displacement + Outputs: + status = = -1 extra word decoded + = 0 ok + = +1 error +*/ + +t_stat get_spec (char *cptr, int32 addr, int32 n1, int32 *sptr, int32 *dptr) +{ +int32 reg, indir, pflag, disp; + +indir = 0; /* no indirect */ +pflag = 0; + +if (*cptr == '@') { /* indirect? */ + indir = 010; + cptr++; + } +if (*cptr == '#') { /* literal? */ + pflag = pflag | A_PND; + cptr++; + } +if (strncmp (cptr, "-(", 2) == 0) { /* autodecrement? */ + pflag = pflag | A_MIN; + cptr++; + } +else if ((cptr = get_addr (cptr, &disp, &pflag)) == NULL) return 1; +if (*cptr == '(') { /* register index? */ + pflag = pflag | A_PAR; + if ((reg = get_reg (cptr + 1, ')')) < 0) return 1; + cptr = cptr + 4; + if (*cptr == '+') { /* autoincrement? */ + pflag = pflag | A_PLS; + cptr++; + } + } +else if ((reg = get_reg (cptr, 0)) >= 0) { + pflag = pflag | A_REG; + cptr = cptr + 2; + } +if (*cptr != 0) return 1; /* all done? */ + +switch (pflag) { /* case on syntax */ + + case A_REG: /* Rn, @Rn */ + *sptr = indir + reg; + return 0; + + case A_PAR: /* (Rn), @(Rn) */ + if (indir) { /* @(Rn) = @0(Rn) */ + *sptr = 070 + reg; + *dptr = 0; + return -1; + } + else *sptr = 010 + reg; + return 0; + + case A_PAR+A_PLS: /* (Rn)+, @(Rn)+ */ + *sptr = 020 + indir + reg; + return 0; + + case A_MIN+A_PAR: /* -(Rn), @-(Rn) */ + *sptr = 040 + indir + reg; + return 0; + + case A_NUM+A_PAR: /* d(Rn), @d(Rn) */ + *sptr = 060 + indir + reg; + *dptr = disp; + return -1; + + case A_PND+A_REL: case A_PND+A_REL+A_NUM: /* #.+n, @#.+n */ + disp = (disp + addr) & 0177777; /* fall through */ + case A_PND+A_NUM: /* #n, @#n */ + *sptr = 027 + indir; + *dptr = disp; + return -1; + + case A_REL: case A_REL+A_NUM: /* .+n, @.+n */ + *sptr = 067 + indir; + *dptr = (disp - 4 + (2 * n1)) & 0177777; + return -1; + + case A_NUM: /* n, @n */ + *sptr = 067 + indir; + *dptr = (disp - addr - 4 + (2 * n1)) & 0177777; + return -1; + + default: + return 1; + } /* end case */ +} + +/* Symbolic input + + Inputs: + *cptr = pointer to input string + addr = current PC + *uptr = pointer to unit + *val = pointer to output values + sw = switches + Outputs: + status = > 0 error code + <= 0 -number of extra words +*/ + +t_stat parse_sym_cm (char *cptr, t_addr addr, t_value *bytes, int32 sw) +{ +int32 d, i, j, reg, spec, n1, n2, disp, pflag; +int32 val[3]; +int32 ad32 = (int32) addr; +t_stat r; +char *tptr, gbuf[CBUFSIZE]; + +if (sw & SWMASK ('R')) return SCPE_ARG; /* radix 50 */ +if (!(sw & SWMASK ('P')) || (ad32 & 1) || (ad32 > WMASK)) + return SCPE_ARG; + +cptr = get_glyph (cptr, gbuf, 0); /* get opcode */ +n1 = n2 = pflag = 0; +for (i = 0; (opcode[i] != NULL) && (strcmp (opcode[i], gbuf) != 0) ; i++) ; +if (opcode[i] == NULL) return SCPE_ARG; +val[0] = opc_val[i] & 0177777; /* get value */ +j = (opc_val[i] >> I_V_CL) & I_M_CL; /* get class */ + +switch (j) { /* case on class */ + + case I_V_NPN: /* no operand */ + break; + + case I_V_REG: /* register */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | reg; + break; + + case I_V_3B: case I_V_6B: case I_V_8B: /* xb literal */ + cptr = get_glyph (cptr, gbuf, 0); /* get literal */ + d = (int32) get_uint (gbuf, 16, (1 << j) - 1, &r); + if (r != SCPE_OK) return SCPE_ARG; + val[0] = val[0] | d; /* put in place */ + break; + + case I_V_BR: /* cond br */ + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + tptr = get_addr (gbuf, &disp, &pflag); /* parse */ + if ((tptr == NULL) || (*tptr != 0)) return SCPE_ARG; + if ((pflag & A_REL) == 0) + disp = (disp - ad32) & 0177777; + if ((disp & 1) || (disp > 0400) && (disp < 0177402)) return SCPE_ARG; + val[0] = val[0] | (((disp - 2) >> 1) & 0377); + break; + + case I_V_SOB: /* sob */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | (reg << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get address */ + tptr = get_addr (gbuf, &disp, &pflag); /* parse */ + if ((tptr == NULL) || (*tptr != 0)) return SCPE_ARG; + if ((pflag & A_REL) == 0) + disp = (disp - ad32) & 0177777; + if ((disp & 1) || ((disp > 2) && (disp < 0177604))) return SCPE_ARG; + val[0] = val[0] | (((2 - disp) >> 1) & 077); + break; + + case I_V_RSOP: /* reg, sop */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | (reg << 6); /* fall through */ + case I_V_SOP: /* sop */ + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; + + case I_V_SOPR: /* dop, reg */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((reg = get_reg (gbuf, 0)) < 0) return SCPE_ARG; + val[0] = val[0] | (reg << 6); + break; + + case I_V_DOP: /* double op */ + cptr = get_glyph (cptr, gbuf, ','); /* get glyph */ + if ((n1 = get_spec (gbuf, ad32, 0, &spec, &val[1])) > 0) + return SCPE_ARG; + val[0] = val[0] | (spec << 6); + cptr = get_glyph (cptr, gbuf, 0); /* get glyph */ + if ((n2 = get_spec (gbuf, ad32, n1, &spec, &val[1 - n1])) > 0) + return SCPE_ARG; + val[0] = val[0] | spec; + break; + + case I_V_CCC: case I_V_CCS: /* cond code oper */ + for (cptr = get_glyph (cptr, gbuf, 0); gbuf[0] != 0; + cptr = get_glyph (cptr, gbuf, 0)) { + for (i = 0; (opcode[i] != NULL) && + (strcmp (opcode[i], gbuf) != 0) ; i++) ; + if ((((opc_val[i] >> I_V_CL) & I_M_CL) != j) || + (opcode[i] == NULL)) return SCPE_ARG; + val[0] = val[0] | (opc_val[i] & 0177777); + } + break; + + default: + return SCPE_ARG; + } + +if (*cptr != 0) return SCPE_ARG; /* junk at end? */ +for (i = j = 0; i < 3; i++, j = j + 2) { + bytes[j] = val[i] & BMASK; + bytes[j + 1] = (val[i] >> 8) & BMASK; + } +return ((2 * (n1 + n2)) - 1); +} diff --git a/VAX/vax_sysdev.c b/VAX/vax_sysdev.c new file mode 100644 index 0000000..3940f92 --- /dev/null +++ b/VAX/vax_sysdev.c @@ -0,0 +1,1570 @@ +/* vax_sysdev.c: VAX 3900 system-specific logic + + Copyright (c) 1998-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + This module contains the CVAX chip and VAX 3900 system-specific registers + and devices. + + rom bootstrap ROM (no registers) + nvr non-volatile ROM (no registers) + csi console storage input + cso console storage output + sysd system devices (SSC miscellany) + + 25-Oct-05 RMS Automated CMCTL extended memory + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 10-Mar-05 RMS Fixed bug in timer schedule routine (from Mark Hittinger) + 30-Sep-04 RMS Moved CADR, MSER, CONPC, CONPSL, machine_check, cpu_boot, + con_halt here from vax_cpu.c + Moved model-specific IPR's here from vax_cpu1.c + 09-Sep-04 RMS Integrated powerup into RESET (with -p) + Added model-specific registers and routines from CPU + 23-Jan-04 MP Added extended physical memory support (Mark Pizzolato) + 07-Jun-03 MP Added calibrated delay to ROM reads (Mark Pizzolato) + Fixed calibration problems interval timer (Mark Pizzolato) + 12-May-03 RMS Fixed compilation warnings from VC.Net + 23-Apr-03 RMS Revised for 32b/64b t_addr + 19-Aug-02 RMS Removed unused variables (found by David Hittner) + Allowed NVR to be attached to file + 30-May-02 RMS Widened POS to 32b + 28-Feb-02 RMS Fixed bug, missing end of table (found by Lars Brinkhoff) +*/ + +#include "vax_defs.h" + +#define UNIT_V_NODELAY (UNIT_V_UF + 0) /* ROM access equal to RAM access */ +#define UNIT_NODELAY (1u << UNIT_V_NODELAY) + +/* Console storage control/status */ + +#define CSICSR_IMP (CSR_DONE + CSR_IE) /* console input */ +#define CSICSR_RW (CSR_IE) +#define CSOCSR_IMP (CSR_DONE + CSR_IE) /* console output */ +#define CSOCSR_RW (CSR_IE) + +/* CMCTL configuration registers */ + +#define CMCNF_VLD 0x80000000 /* addr valid */ +#define CMCNF_BA 0x1FF00000 /* base addr */ +#define CMCNF_LOCK 0x00000040 /* lock NI */ +#define CMCNF_SRQ 0x00000020 /* sig req WO */ +#define CMCNF_SIG 0x0000001F /* signature */ +#define CMCNF_RW (CMCNF_VLD | CMCNF_BA) /* read/write */ +#define CMCNF_MASK (CMCNF_RW | CMCNF_SIG) +#define MEM_BANK (1 << 22) /* bank size 4MB */ +#define MEM_SIG (0x17) /* ECC, 4 x 4MB */ + +/* CMCTL error register */ + +#define CMERR_RDS 0x80000000 /* uncorr err NI */ +#define CMERR_FRQ 0x40000000 /* 2nd RDS NI */ +#define CMERR_CRD 0x20000000 /* CRD err NI */ +#define CMERR_PAG 0x1FFFFC00 /* page addr NI */ +#define CMERR_DMA 0x00000100 /* DMA err NI */ +#define CMERR_BUS 0x00000080 /* bus err NI */ +#define CMERR_SYN 0x0000007F /* syndrome NI */ +#define CMERR_W1C (CMERR_RDS | CMERR_FRQ | CMERR_CRD | \ + CMERR_DMA | CMERR_BUS) + +/* CMCTL control/status register */ + +#define CMCSR_PMI 0x00002000 /* PMI speed NI */ +#define CMCSR_CRD 0x00001000 /* enb CRD int NI */ +#define CMCSR_FRF 0x00000800 /* force ref WONI */ +#define CMCSR_DET 0x00000400 /* dis err NI */ +#define CMCSR_FDT 0x00000200 /* fast diag NI */ +#define CMCSR_DCM 0x00000080 /* diag mode NI */ +#define CMCSR_SYN 0x0000007F /* syndrome NI */ +#define CMCSR_MASK (CMCSR_PMI | CMCSR_CRD | CMCSR_DET | \ + CMCSR_FDT | CMCSR_DCM | CMCSR_SYN) + +/* KA655 boot/diagnostic register */ + +#define BDR_BRKENB 0x00000080 /* break enable */ + +/* KA655 cache control register */ + +#define CACR_DRO 0x00FFFF00 /* diag bits RO */ +#define CACR_V_DPAR 24 /* data parity */ +#define CACR_FIXED 0x00000040 /* fixed bits */ +#define CACR_CPE 0x00000020 /* parity err W1C */ +#define CACR_CEN 0x00000010 /* enable */ +#define CACR_DPE 0x00000004 /* disable par NI */ +#define CACR_WWP 0x00000002 /* write wrong par NI */ +#define CACR_DIAG 0x00000001 /* diag mode */ +#define CACR_W1C (CACR_CPE) +#define CACR_RW (CACR_CEN | CACR_DPE | CACR_WWP | CACR_DIAG) + +/* SSC base register */ + +#define SSCBASE_MBO 0x20000000 /* must be one */ +#define SSCBASE_RW 0x1FFFFC00 /* base address */ + +/* SSC configuration register */ + +#define SSCCNF_BLO 0x80000000 /* batt low W1C */ +#define SSCCNF_IVD 0x08000000 /* int dsbl NI */ +#define SSCCNF_IPL 0x03000000 /* int IPL NI */ +#define SSCCNF_ROM 0x00F70000 /* ROM param NI */ +#define SSCCNF_CTLP 0x00008000 /* ctrl P enb */ +#define SSCCNF_BAUD 0x00007700 /* baud rates NI */ +#define SSCCNF_ADS 0x00000077 /* addr strb NI */ +#define SSCCNF_W1C SSCCNF_BLO +#define SSCCNF_RW 0x0BF7F777 + +/* SSC timeout register */ + +#define SSCBTO_BTO 0x80000000 /* timeout W1C */ +#define SSCBTO_RWT 0x40000000 /* read/write W1C */ +#define SSCBTO_INTV 0x00FFFFFF /* interval NI */ +#define SSCBTO_W1C (SSCBTO_BTO | SSCBTO_RWT) +#define SSCBTO_RW SSCBTO_INTV + +/* SSC output port */ + +#define SSCOTP_MASK 0x0000000F /* output port */ + +/* SSC timer control/status */ + +#define TMR_CSR_ERR 0x80000000 /* error W1C */ +#define TMR_CSR_DON 0x00000080 /* done W1C */ +#define TMR_CSR_IE 0x00000040 /* int enb */ +#define TMR_CSR_SGL 0x00000020 /* single WO */ +#define TMR_CSR_XFR 0x00000010 /* xfer WO */ +#define TMR_CSR_STP 0x00000004 /* stop */ +#define TMR_CSR_RUN 0x00000001 /* run */ +#define TMR_CSR_W1C (TMR_CSR_ERR | TMR_CSR_DON) +#define TMR_CSR_RW (TMR_CSR_IE | TMR_CSR_STP | TMR_CSR_RUN) + +/* SSC timer intervals */ + +#define TMR_INC 10000 /* usec/interval */ + +/* SSC timer vector */ + +#define TMR_VEC_MASK 0x000003FC /* vector */ + +/* SSC address strobes */ + +#define SSCADS_MASK 0x3FFFFFFC /* match or mask */ + +extern int32 R[16]; +extern int32 STK[5]; +extern int32 PSL; +extern int32 SISR; +extern int32 mapen; +extern int32 pcq[PCQ_SIZE]; +extern int32 pcq_p; +extern int32 ibcnt, ppc; +extern int32 in_ie; +extern int32 mchk_va, mchk_ref; +extern int32 fault_PC; +extern int32 int_req[IPL_HLVL]; +extern UNIT cpu_unit; +extern UNIT clk_unit; +extern jmp_buf save_env; +extern int32 p1; +extern int32 sim_switches; +extern int32 MSER; +extern int32 tmr_poll; + +uint32 *rom = NULL; /* boot ROM */ +uint32 *nvr = NULL; /* non-volatile mem */ +int32 CADR = 0; /* cache disable reg */ +int32 MSER = 0; /* mem sys error reg */ +int32 conpc, conpsl; /* console reg */ +int32 csi_csr = 0; /* control/status */ +int32 cso_csr = 0; /* control/status */ +int32 cmctl_reg[CMCTLSIZE >> 2] = { 0 }; /* CMCTL reg */ +int32 ka_cacr = 0; /* KA655 cache ctl */ +int32 ka_bdr = BDR_BRKENB; /* KA655 boot diag */ +int32 ssc_base = SSCBASE; /* SSC base */ +int32 ssc_cnf = 0; /* SSC conf */ +int32 ssc_bto = 0; /* SSC timeout */ +int32 ssc_otp = 0; /* SSC output port */ +int32 tmr_csr[2] = { 0 }; /* SSC timers */ +uint32 tmr_tir[2] = { 0 }; /* curr interval */ +uint32 tmr_tnir[2] = { 0 }; /* next interval */ +int32 tmr_tivr[2] = { 0 }; /* vector */ +uint32 tmr_inc[2] = { 0 }; /* tir increment */ +uint32 tmr_sav[2] = { 0 }; /* saved inst cnt */ +int32 ssc_adsm[2] = { 0 }; /* addr strobes */ +int32 ssc_adsk[2] = { 0 }; +int32 cdg_dat[CDASIZE >> 2]; /* cache data */ +static uint32 rom_delay = 0; + +t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_stat rom_reset (DEVICE *dptr); +t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw); +t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw); +t_stat nvr_reset (DEVICE *dptr); +t_stat nvr_attach (UNIT *uptr, char *cptr); +t_stat nvr_detach (UNIT *uptr); +t_stat csi_reset (DEVICE *dptr); +t_stat cso_reset (DEVICE *dptr); +t_stat cso_svc (UNIT *uptr); +t_stat tmr_svc (UNIT *uptr); +t_stat sysd_reset (DEVICE *dptr); + +int32 rom_rd (int32 pa); +int32 nvr_rd (int32 pa); +void nvr_wr (int32 pa, int32 val, int32 lnt); +int32 csrs_rd (void); +int32 csrd_rd (void); +int32 csts_rd (void); +void csrs_wr (int32 dat); +void csts_wr (int32 dat); +void cstd_wr (int32 dat); +int32 cmctl_rd (int32 pa); +void cmctl_wr (int32 pa, int32 val, int32 lnt); +int32 ka_rd (int32 pa); +void ka_wr (int32 pa, int32 val, int32 lnt); +int32 cdg_rd (int32 pa); +void cdg_wr (int32 pa, int32 val, int32 lnt); +int32 ssc_rd (int32 pa); +void ssc_wr (int32 pa, int32 val, int32 lnt); +int32 tmr_tir_rd (int32 tmr, t_bool interp); +void tmr_csr_wr (int32 tmr, int32 val); +void tmr_sched (int32 tmr); +void tmr_incr (int32 tmr, uint32 inc); +int32 tmr0_inta (void); +int32 tmr1_inta (void); +int32 parity (int32 val, int32 odd); +t_stat sysd_powerup (void); + +extern int32 intexc (int32 vec, int32 cc, int32 ipl, int ei); +extern int32 cqmap_rd (int32 pa); +extern void cqmap_wr (int32 pa, int32 val, int32 lnt); +extern int32 cqipc_rd (int32 pa); +extern void cqipc_wr (int32 pa, int32 val, int32 lnt); +extern int32 cqbic_rd (int32 pa); +extern void cqbic_wr (int32 pa, int32 val, int32 lnt); +extern int32 cqmem_rd (int32 pa); +extern void cqmem_wr (int32 pa, int32 val, int32 lnt); +extern int32 iccs_rd (void); +extern int32 todr_rd (void); +extern int32 rxcs_rd (void); +extern int32 rxdb_rd (void); +extern int32 txcs_rd (void); +extern void iccs_wr (int32 dat); +extern void todr_wr (int32 dat); +extern void rxcs_wr (int32 dat); +extern void txcs_wr (int32 dat); +extern void txdb_wr (int32 dat); +extern void ioreset_wr (int32 dat); +extern uint32 sim_os_msec(); + +/* ROM data structures + + rom_dev ROM device descriptor + rom_unit ROM units + rom_reg ROM register list +*/ + +UNIT rom_unit = { UDATA (NULL, UNIT_FIX+UNIT_BINK, ROMSIZE) }; + +REG rom_reg[] = { + { NULL } + }; + +MTAB rom_mod[] = { + { UNIT_NODELAY, UNIT_NODELAY, "fast access", "NODELAY", NULL }, + { UNIT_NODELAY, 0, "1usec calibrated access", "DELAY", NULL }, + { 0 } + }; + +DEVICE rom_dev = { + "ROM", &rom_unit, rom_reg, rom_mod, + 1, 16, ROMAWIDTH, 4, 16, 32, + &rom_ex, &rom_dep, &rom_reset, + NULL, NULL, NULL, + NULL, 0 + }; + +/* NVR data structures + + nvr_dev NVR device descriptor + nvr_unit NVR units + nvr_reg NVR register list +*/ + +UNIT nvr_unit = + { UDATA (NULL, UNIT_FIX+UNIT_BINK, NVRSIZE) }; + +REG nvr_reg[] = { + { NULL } + }; + +DEVICE nvr_dev = { + "NVR", &nvr_unit, nvr_reg, NULL, + 1, 16, NVRAWIDTH, 4, 16, 32, + &nvr_ex, &nvr_dep, &nvr_reset, + NULL, &nvr_attach, &nvr_detach, + NULL, 0 + }; + +/* CSI data structures + + csi_dev CSI device descriptor + csi_unit CSI unit descriptor + csi_reg CSI register list +*/ + +DIB csi_dib = { 0, 0, NULL, NULL, 1, IVCL (CSI), SCB_CSI, { NULL } }; + +UNIT csi_unit = { UDATA (NULL, 0, 0), KBD_POLL_WAIT }; + +REG csi_reg[] = { + { ORDATA (BUF, csi_unit.buf, 8) }, + { ORDATA (CSR, csi_csr, 16) }, + { FLDATA (INT, int_req[IPL_CSI], INT_V_CSI) }, + { FLDATA (DONE, csi_csr, CSR_V_DONE) }, + { FLDATA (IE, csi_csr, CSR_V_IE) }, + { DRDATA (POS, csi_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, csi_unit.wait, 24), REG_NZ + PV_LEFT }, + { NULL } + }; + +MTAB csi_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE csi_dev = { + "CSI", &csi_unit, csi_reg, csi_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &csi_reset, + NULL, NULL, NULL, + &csi_dib, 0 + }; + +/* CSO data structures + + cso_dev CSO device descriptor + cso_unit CSO unit descriptor + cso_reg CSO register list +*/ + +DIB cso_dib = { 0, 0, NULL, NULL, 1, IVCL (CSO), SCB_CSO, { NULL } }; + +UNIT cso_unit = { UDATA (&cso_svc, UNIT_SEQ+UNIT_ATTABLE, 0), SERIAL_OUT_WAIT }; + +REG cso_reg[] = { + { ORDATA (BUF, cso_unit.buf, 8) }, + { ORDATA (CSR, cso_csr, 16) }, + { FLDATA (INT, int_req[IPL_CSO], INT_V_CSO) }, + { FLDATA (DONE, cso_csr, CSR_V_DONE) }, + { FLDATA (IE, cso_csr, CSR_V_IE) }, + { DRDATA (POS, cso_unit.pos, 32), PV_LEFT }, + { DRDATA (TIME, cso_unit.wait, 24), PV_LEFT }, + { NULL } + }; + +MTAB cso_mod[] = { + { MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, NULL, &show_vec }, + { 0 } + }; + +DEVICE cso_dev = { + "CSO", &cso_unit, cso_reg, cso_mod, + 1, 10, 31, 1, 8, 8, + NULL, NULL, &cso_reset, + NULL, NULL, NULL, + &cso_dib, 0 + }; + +/* SYSD data structures + + sysd_dev SYSD device descriptor + sysd_unit SYSD units + sysd_reg SYSD register list +*/ + +DIB sysd_dib[] = { + 0, 0, NULL, NULL, + 2, IVCL (TMR0), 0, { &tmr0_inta, &tmr1_inta } + }; + +UNIT sysd_unit[] = { + { UDATA (&tmr_svc, 0, 0) }, + { UDATA (&tmr_svc, 0, 0) } + }; + +REG sysd_reg[] = { + { HRDATA (CADR, CADR, 8) }, + { HRDATA (MSER, MSER, 8) }, + { HRDATA (CONPC, conpc, 32) }, + { HRDATA (CONPSL, conpsl, 32) }, + { BRDATA (CMCSR, cmctl_reg, 16, 32, CMCTLSIZE >> 2) }, + { HRDATA (CACR, ka_cacr, 8) }, + { HRDATA (BDR, ka_bdr, 8) }, + { HRDATA (BASE, ssc_base, 29) }, + { HRDATA (CNF, ssc_cnf, 32) }, + { HRDATA (BTO, ssc_bto, 32) }, + { HRDATA (OTP, ssc_otp, 4) }, + { HRDATA (TCSR0, tmr_csr[0], 32) }, + { HRDATA (TIR0, tmr_tir[0], 32) }, + { HRDATA (TNIR0, tmr_tnir[0], 32) }, + { HRDATA (TIVEC0, tmr_tivr[0], 9) }, + { HRDATA (TINC0, tmr_inc[0], 32) }, + { HRDATA (TSAV0, tmr_sav[0], 32) }, + { HRDATA (TCSR1, tmr_csr[1], 32) }, + { HRDATA (TIR1, tmr_tir[1], 32) }, + { HRDATA (TNIR1, tmr_tnir[1], 32) }, + { HRDATA (TIVEC1, tmr_tivr[1], 9) }, + { HRDATA (TINC1, tmr_inc[1], 32) }, + { HRDATA (TSAV1, tmr_sav[1], 32) }, + { HRDATA (ADSM0, ssc_adsm[0], 32) }, + { HRDATA (ADSK0, ssc_adsk[0], 32) }, + { HRDATA (ADSM1, ssc_adsm[1], 32) }, + { HRDATA (ADSK1, ssc_adsk[1], 32) }, + { BRDATA (CDGDAT, cdg_dat, 16, 32, CDASIZE >> 2) }, + { NULL } + }; + +DEVICE sysd_dev = { + "SYSD", sysd_unit, sysd_reg, NULL, + 2, 16, 16, 1, 16, 8, + NULL, NULL, &sysd_reset, + NULL, NULL, NULL, + &sysd_dib, 0 + }; + +/* ROM: read only memory - stored in a buffered file + Register space access routines see ROM twice + + ROM access has been 'regulated' to about 1Mhz to avoid issues + with testing the interval timers in self-test. Specifically, + the VAX boot ROM (ka655.bin) contains code which presumes that + the VAX runs at a particular slower speed when code is running + from ROM (which is not cached). These assumptions are built + into instruction based timing loops. As the host platform gets + much faster than the original VAX, the assumptions embedded in + these code loops are no longer valid. + + Code has been added to the ROM implementation to limit CPU speed + to about 500K instructions per second. This heads off any future + issues with the embedded timing loops. +*/ + +int32 rom_swapb(int32 val) +{ +return ((val << 24) & 0xff000000) | (( val << 8) & 0xff0000) | + ((val >> 8) & 0xff00) | ((val >> 24) & 0xff); +} + +int32 rom_read_delay (int32 val) +{ +uint32 i, l = rom_delay; +int32 loopval = 0; + +if (rom_unit.flags & UNIT_NODELAY) return val; + +/* Calibrate the loop delay factor when first used. + Do this 4 times to and use the largest value computed. */ + +if (rom_delay == 0) { + uint32 ts, te, c = 10000, samples = 0; + while (1) { + c = c * 2; + te = sim_os_msec(); + while (te == (ts = sim_os_msec ())); /* align on ms tick */ + +/* This is merely a busy wait with some "work" that won't get optimized + away by a good compiler. loopval always is zero. To avoid smart compilers, + the loopval variable is referenced in the function arguments so that the + function expression is not loop invariant. It also must be referenced + by subsequent code or to avoid the whole computation being eliminated. */ + + for (i = 0; i < c; i++) + loopval |= (loopval + ts) ^ rom_swapb (rom_swapb (loopval + ts)); + te = sim_os_msec (); + if ((te - ts) < 50) continue; /* sample big enough? */ + if (rom_delay < (loopval + (c / (te - ts) / 1000) + 1)) + rom_delay = loopval + (c / (te - ts) / 1000) + 1; + if (++samples >= 4) break; + c = c / 2; + } + if (rom_delay < 5) rom_delay = 5; + } + +for (i = 0; i < l; i++) + loopval |= (loopval + val) ^ rom_swapb (rom_swapb (loopval + val)); +return val + loopval; +} + +int32 rom_rd (int32 pa) +{ +int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; + +return rom_read_delay (rom[rg]); +} + +void rom_wr_B (int32 pa, int32 val) +{ +int32 rg = ((pa - ROMBASE) & ROMAMASK) >> 2; +int32 sc = (pa & 3) << 3; + +rom[rg] = ((val & 0xFF) << sc) | (rom[rg] & ~(0xFF << sc)); +return; +} + +/* ROM examine */ + +t_stat rom_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 addr = (uint32) exta; + +if ((vptr == NULL) || (addr & 03)) return SCPE_ARG; +if (addr >= ROMSIZE) return SCPE_NXM; +*vptr = rom[addr >> 2]; +return SCPE_OK; +} + +/* ROM deposit */ + +t_stat rom_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 addr = (uint32) exta; + +if (addr & 03) return SCPE_ARG; +if (addr >= ROMSIZE) return SCPE_NXM; +rom[addr >> 2] = (uint32) val; +return SCPE_OK; +} + +/* ROM reset */ + +t_stat rom_reset (DEVICE *dptr) +{ +if (rom == NULL) rom = (uint32 *) calloc (ROMSIZE >> 2, sizeof (uint32)); +if (rom == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* NVR: non-volatile RAM - stored in a buffered file */ + +int32 nvr_rd (int32 pa) +{ +int32 rg = (pa - NVRBASE) >> 2; + +return nvr[rg]; +} + +void nvr_wr (int32 pa, int32 val, int32 lnt) +{ +int32 rg = (pa - NVRBASE) >> 2; + +if (lnt < L_LONG) { /* byte or word? */ + int32 sc = (pa & 3) << 3; /* merge */ + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + nvr[rg] = ((val & mask) << sc) | (nvr[rg] & ~(mask << sc)); + } +else nvr[rg] = val; +return; +} + +/* NVR examine */ + +t_stat nvr_ex (t_value *vptr, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 addr = (uint32) exta; + +if ((vptr == NULL) || (addr & 03)) return SCPE_ARG; +if (addr >= NVRSIZE) return SCPE_NXM; +*vptr = nvr[addr >> 2]; +return SCPE_OK; +} + +/* NVR deposit */ + +t_stat nvr_dep (t_value val, t_addr exta, UNIT *uptr, int32 sw) +{ +uint32 addr = (uint32) exta; + +if (addr & 03) return SCPE_ARG; +if (addr >= NVRSIZE) return SCPE_NXM; +nvr[addr >> 2] = (uint32) val; +return SCPE_OK; +} + +/* NVR reset */ + +t_stat nvr_reset (DEVICE *dptr) +{ +if (nvr == NULL) { + nvr = (uint32 *) calloc (NVRSIZE >> 2, sizeof (uint32)); + nvr_unit.filebuf = nvr; + ssc_cnf = ssc_cnf | SSCCNF_BLO; + } +if (nvr == NULL) return SCPE_MEM; +return SCPE_OK; +} + +/* NVR attach */ + +t_stat nvr_attach (UNIT *uptr, char *cptr) +{ +t_stat r; + +uptr->flags = uptr->flags | (UNIT_ATTABLE | UNIT_BUFABLE); +r = attach_unit (uptr, cptr); +if (r != SCPE_OK) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +else { + uptr->hwmark = (uint32) uptr->capac; + ssc_cnf = ssc_cnf & ~SSCCNF_BLO; + } +return r; +} + +/* NVR detach */ + +t_stat nvr_detach (UNIT *uptr) +{ +t_stat r; + +r = detach_unit (uptr); +if ((uptr->flags & UNIT_ATT) == 0) + uptr->flags = uptr->flags & ~(UNIT_ATTABLE | UNIT_BUFABLE); +return r; +} + +/* CSI: console storage input */ + +int32 csrs_rd (void) +{ +return (csi_csr & CSICSR_IMP); +} + +int32 csrd_rd (void) +{ +csi_csr = csi_csr & ~CSR_DONE; +CLR_INT (CSI); +return (csi_unit.buf & 0377); +} + +void csrs_wr (int32 data) +{ +if ((data & CSR_IE) == 0) CLR_INT (CSI); +else if ((csi_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (CSI); +csi_csr = (csi_csr & ~CSICSR_RW) | (data & CSICSR_RW); +return; +} + +t_stat csi_reset (DEVICE *dptr) +{ +csi_unit.buf = 0; +csi_csr = 0; +CLR_INT (CSI); +return SCPE_OK; +} + +/* CSO: console storage output */ + +int32 csts_rd (void) +{ +return (cso_csr & CSOCSR_IMP); +} + +void csts_wr (int32 data) +{ +if ((data & CSR_IE) == 0) CLR_INT (CSO); +else if ((cso_csr & (CSR_DONE + CSR_IE)) == CSR_DONE) + SET_INT (CSO); +cso_csr = (cso_csr & ~CSOCSR_RW) | (data & CSOCSR_RW); +return; +} + +void cstd_wr (int32 data) +{ +cso_unit.buf = data & 0377; +cso_csr = cso_csr & ~CSR_DONE; +CLR_INT (CSO); +sim_activate (&cso_unit, cso_unit.wait); +return; +} + +t_stat cso_svc (UNIT *uptr) +{ +cso_csr = cso_csr | CSR_DONE; +if (cso_csr & CSR_IE) SET_INT (CSO); +if ((cso_unit.flags & UNIT_ATT) == 0) return SCPE_OK; +if (putc (cso_unit.buf, cso_unit.fileref) == EOF) { + perror ("CSO I/O error"); + clearerr (cso_unit.fileref); + return SCPE_IOERR; + } +cso_unit.pos = cso_unit.pos + 1; +return SCPE_OK; +} + +t_stat cso_reset (DEVICE *dptr) +{ +cso_unit.buf = 0; +cso_csr = CSR_DONE; +CLR_INT (CSO); +sim_cancel (&cso_unit); /* deactivate unit */ +return SCPE_OK; +} + +/* SYSD: SSC access mechanisms and devices + + - IPR space read/write routines + - register space read/write routines + - SSC local register read/write routines + - SSC console storage UART + - SSC timers + - CMCTL local register read/write routines +*/ + +/* Read/write IPR register space + + These routines implement the SSC's response to IPR's which are + sent off the CPU chip for processing. +*/ + +int32 ReadIPR (int32 rg) +{ +int32 val; + +switch (rg) { + + case MT_ICCS: /* ICCS */ + val = iccs_rd (); + break; + + case MT_CSRS: /* CSRS */ + val = csrs_rd (); + break; + + case MT_CSRD: /* CSRD */ + val = csrd_rd (); + break; + + case MT_CSTS: /* CSTS */ + val = csts_rd (); + break; + + case MT_CSTD: /* CSTD */ + val = 0; + break; + + case MT_RXCS: /* RXCS */ + val = rxcs_rd (); + break; + + case MT_RXDB: /* RXDB */ + val = rxdb_rd (); + break; + + case MT_TXCS: /* TXCS */ + val = txcs_rd (); + break; + + case MT_TXDB: /* TXDB */ + val = 0; + break; + + case MT_TODR: /* TODR */ + val = todr_rd (); + break; + + case MT_CADR: /* CADR */ + val = CADR & 0xFF; + break; + + case MT_MSER: /* MSER */ + val = MSER & 0xFF; + break; + + case MT_CONPC: /* console PC */ + val = conpc; + break; + + case MT_CONPSL: /* console PSL */ + val = conpsl; + break; + + case MT_SID: /* SID */ + val = CVAX_SID | CVAX_UREV; + break; + + default: + ssc_bto = ssc_bto | SSCBTO_BTO; /* set BTO */ + val = 0; + break; + } + +return val; +} + +void WriteIPR (int32 rg, int32 val) +{ +switch (rg) { + + case MT_ICCS: /* ICCS */ + iccs_wr (val); + break; + + case MT_TODR: /* TODR */ + todr_wr (val); + break; + + case MT_CSRS: /* CSRS */ + csrs_wr (val); + break; + + case MT_CSRD: /* CSRD */ + break; + + case MT_CSTS: /* CSTS */ + csts_wr (val); + break; + + case MT_CSTD: /* CSTD */ + cstd_wr (val); + break; + + case MT_RXCS: /* RXCS */ + rxcs_wr (val); + break; + + case MT_RXDB: /* RXDB */ + break; + + case MT_TXCS: /* TXCS */ + txcs_wr (val); + break; + + case MT_TXDB: /* TXDB */ + txdb_wr (val); + break; + + case MT_CADR: /* CADR */ + CADR = (val & CADR_RW) | CADR_MBO; + break; + + case MT_MSER: /* MSER */ + MSER = MSER & MSER_HM; + break; + + case MT_IORESET: /* IORESET */ + ioreset_wr (val); + break; + + case MT_SID: + case MT_CONPC: + case MT_CONPSL: /* halt reg */ + RSVD_OPND_FAULT; + + default: + ssc_bto = ssc_bto | SSCBTO_BTO; /* set BTO */ + break; + } + +return; +} + +/* Read/write I/O register space + + These routines are the 'catch all' for address space map. Any + address that doesn't explicitly belong to memory, I/O, or ROM + is given to these routines for processing. +*/ + +struct reglink { /* register linkage */ + uint32 low; /* low addr */ + uint32 high; /* high addr */ + t_stat (*read)(int32 pa); /* read routine */ + void (*write)(int32 pa, int32 val, int32 lnt); /* write routine */ + }; + +struct reglink regtable[] = { + { CQMAPBASE, CQMAPBASE+CQMAPSIZE, &cqmap_rd, &cqmap_wr }, + { ROMBASE, ROMBASE+ROMSIZE+ROMSIZE, &rom_rd, NULL }, + { NVRBASE, NVRBASE+NVRSIZE, &nvr_rd, &nvr_wr }, + { CMCTLBASE, CMCTLBASE+CMCTLSIZE, &cmctl_rd, &cmctl_wr }, + { SSCBASE, SSCBASE+SSCSIZE, &ssc_rd, &ssc_wr }, + { KABASE, KABASE+KASIZE, &ka_rd, &ka_wr }, + { CQBICBASE, CQBICBASE+CQBICSIZE, &cqbic_rd, &cqbic_wr }, + { CQIPCBASE, CQIPCBASE+CQIPCSIZE, &cqipc_rd, &cqipc_wr }, + { CQMBASE, CQMBASE+CQMSIZE, &cqmem_rd, &cqmem_wr }, + { CDGBASE, CDGBASE+CDGSIZE, &cdg_rd, &cdg_wr }, + { 0, 0, NULL, NULL } + }; + +/* ReadReg - read register space + + Inputs: + pa = physical address + lnt = length (BWLQ) - ignored + Output: + longword of data +*/ + +int32 ReadReg (uint32 pa, int32 lnt) +{ +struct reglink *p; + +for (p = ®table[0]; p->low != 0; p++) { + if ((pa >= p->low) && (pa < p->high) && p->read) + return p->read (pa); + } +ssc_bto = ssc_bto | SSCBTO_BTO | SSCBTO_RWT; +MACH_CHECK (MCHK_READ); +return 0; +} + +/* WriteReg - write register space + + Inputs: + pa = physical address + val = data to write, right justified in 32b longword + lnt = length (BWLQ) + Outputs: + none +*/ + +void WriteReg (uint32 pa, int32 val, int32 lnt) +{ +struct reglink *p; + +for (p = ®table[0]; p->low != 0; p++) { + if ((pa >= p->low) && (pa < p->high) && p->write) { + p->write (pa, val, lnt); + return; + } + } +ssc_bto = ssc_bto | SSCBTO_BTO | SSCBTO_RWT; +MACH_CHECK (MCHK_WRITE); +return; +} + +/* CMCTL registers + + CMCTL00 - 15 configure memory banks 00 - 15. Note that they are + here merely to entertain the firmware; the actual configuration + of memory is unaffected by the settings here. + + CMCTL16 - error status register + + CMCTL17 - control/diagnostic status register + + The CMCTL registers are cleared at power up. +*/ + +int32 cmctl_rd (int32 pa) +{ +int32 rg = (pa - CMCTLBASE) >> 2; + +switch (rg) { + + default: /* config reg */ + return cmctl_reg[rg] & CMCNF_MASK; + + case 16: /* err status */ + return cmctl_reg[rg]; + + case 17: /* csr */ + return cmctl_reg[rg] & CMCSR_MASK; + + case 18: /* KA655X ext mem */ + if (MEMSIZE > MAXMEMSIZE) /* more than 128MB? */ + return ((int32) MEMSIZE); + MACH_CHECK (MCHK_READ); + } + +return 0; +} + +void cmctl_wr (int32 pa, int32 val, int32 lnt) +{ +int32 i, rg = (pa - CMCTLBASE) >> 2; + +if (lnt < L_LONG) { /* LW write only */ + int32 sc = (pa & 3) << 3; /* shift data to */ + val = val << sc; /* proper location */ + } +switch (rg) { + + default: /* config reg */ + if (val & CMCNF_SRQ) { /* sig request? */ + int32 rg_g = rg & ~3; /* group of 4 */ + for (i = rg_g; i < (rg_g + 4); i++) { + cmctl_reg[i] = cmctl_reg[i] & ~CMCNF_SIG; + if (ADDR_IS_MEM (i * MEM_BANK)) + cmctl_reg[i] = cmctl_reg[i] | MEM_SIG; + } + } + cmctl_reg[rg] = (cmctl_reg[rg] & ~CMCNF_RW) | (val & CMCNF_RW); + break; + + case 16: /* err status */ + cmctl_reg[rg] = cmctl_reg[rg] & ~(val & CMERR_W1C); + break; + + case 17: /* csr */ + cmctl_reg[rg] = val & CMCSR_MASK; + break; + + case 18: + MACH_CHECK (MCHK_WRITE); + } + +return; +} + +/* KA655 registers */ + +int32 ka_rd (int32 pa) +{ +int32 rg = (pa - KABASE) >> 2; + +switch (rg) { + + case 0: /* CACR */ + return ka_cacr; + + case 1: /* BDR */ + return ka_bdr; + } + +return 0; +} + +void ka_wr (int32 pa, int32 val, int32 lnt) +{ +int32 rg = (pa - KABASE) >> 2; + +if ((rg == 0) && ((pa & 3) == 0)) { /* lo byte only */ + ka_cacr = (ka_cacr & ~(val & CACR_W1C)) | CACR_FIXED; + ka_cacr = (ka_cacr & ~CACR_RW) | (val & CACR_RW); + } +return; +} + +int32 sysd_hlt_enb (void) +{ +return ka_bdr & BDR_BRKENB; +} + +/* Cache diagnostic space */ + +int32 cdg_rd (int32 pa) +{ +int32 t, row = CDG_GETROW (pa); + +t = cdg_dat[row]; +ka_cacr = ka_cacr & ~CACR_DRO; /* clear diag */ +ka_cacr = ka_cacr | + (parity ((t >> 24) & 0xFF, 1) << (CACR_V_DPAR + 3)) | + (parity ((t >> 16) & 0xFF, 0) << (CACR_V_DPAR + 2)) | + (parity ((t >> 8) & 0xFF, 1) << (CACR_V_DPAR + 1)) | + (parity (t & 0xFF, 0) << CACR_V_DPAR); +return t; +} + +void cdg_wr (int32 pa, int32 val, int32 lnt) +{ +int32 row = CDG_GETROW (pa); + +if (lnt < L_LONG) { /* byte or word? */ + int32 sc = (pa & 3) << 3; /* merge */ + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = cdg_dat[row]; + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } +cdg_dat[row] = val; /* store data */ +return; +} + +int32 parity (int32 val, int32 odd) +{ +for ( ; val != 0; val = val >> 1) { + if (val & 1) odd = odd ^ 1; + } +return odd; +} + +/* SSC registers - byte/word merges done in WriteReg */ + +int32 ssc_rd (int32 pa) +{ +int32 rg = (pa - SSCBASE) >> 2; + +switch (rg) { + + case 0x00: /* base reg */ + return ssc_base; + + case 0x04: /* conf reg */ + return ssc_cnf; + + case 0x08: /* bus timeout */ + return ssc_bto; + + case 0x0C: /* output port */ + return ssc_otp & SSCOTP_MASK; + + case 0x1B: /* TODR */ + return todr_rd (); + + case 0x1C: /* CSRS */ + return csrs_rd (); + + case 0x1D: /* CSRD */ + return csrd_rd (); + + case 0x1E: /* CSTS */ + return csts_rd (); + + case 0x20: /* RXCS */ + return rxcs_rd (); + + case 0x21: /* RXDB */ + return rxdb_rd (); + + case 0x22: /* TXCS */ + return txcs_rd (); + + case 0x40: /* T0CSR */ + return tmr_csr[0]; + + case 0x41: /* T0INT */ + return tmr_tir_rd (0, FALSE); + + case 0x42: /* T0NI */ + return tmr_tnir[0]; + + case 0x43: /* T0VEC */ + return tmr_tivr[0]; + + case 0x44: /* T1CSR */ + return tmr_csr[1]; + + case 0x45: /* T1INT */ + return tmr_tir_rd (1, FALSE); + + case 0x46: /* T1NI */ + return tmr_tnir[1]; + + case 0x47: /* T1VEC */ + return tmr_tivr[1]; + + case 0x4C: /* ADS0M */ + return ssc_adsm[0]; + + case 0x4D: /* ADS0K */ + return ssc_adsk[0]; + + case 0x50: /* ADS1M */ + return ssc_adsm[1]; + + case 0x51: /* ADS1K */ + return ssc_adsk[1]; + } + +return 0; +} + +void ssc_wr (int32 pa, int32 val, int32 lnt) +{ +int32 rg = (pa - SSCBASE) >> 2; + +if (lnt < L_LONG) { /* byte or word? */ + int32 sc = (pa & 3) << 3; /* merge */ + int32 mask = (lnt == L_WORD)? 0xFFFF: 0xFF; + int32 t = ssc_rd (pa); + val = ((val & mask) << sc) | (t & ~(mask << sc)); + } + +switch (rg) { + + case 0x00: /* base reg */ + ssc_base = (val & SSCBASE_RW) | SSCBASE_MBO; + break; + + case 0x04: /* conf reg */ + ssc_cnf = ssc_cnf & ~(val & SSCCNF_W1C); + ssc_cnf = (ssc_cnf & ~SSCCNF_RW) | (val & SSCCNF_RW); + break; + + case 0x08: /* bus timeout */ + ssc_bto = ssc_bto & ~(val & SSCBTO_W1C); + ssc_bto = (ssc_bto & ~SSCBTO_RW) | (val & SSCBTO_RW); + break; + + case 0x0C: /* output port */ + ssc_otp = val & SSCOTP_MASK; + break; + + case 0x1B: /* TODR */ + todr_wr (val); + break; + + case 0x1C: /* CSRS */ + csrs_wr (val); + break; + + case 0x1E: /* CSTS */ + csts_wr (val); + break; + + case 0x1F: /* CSTD */ + cstd_wr (val); + break; + + case 0x20: /* RXCS */ + rxcs_wr (val); + break; + + case 0x22: /* TXCS */ + txcs_wr (val); + break; + + case 0x23: /* TXDB */ + txdb_wr (val); + break; + + case 0x40: /* T0CSR */ + tmr_csr_wr (0, val); + break; + + case 0x42: /* T0NI */ + tmr_tnir[0] = val; + break; + + case 0x43: /* T0VEC */ + tmr_tivr[0] = val & TMR_VEC_MASK; + break; + + case 0x44: /* T1CSR */ + tmr_csr_wr (1, val); + break; + + case 0x46: /* T1NI */ + tmr_tnir[1] = val; + break; + + case 0x47: /* T1VEC */ + tmr_tivr[1] = val & TMR_VEC_MASK; + break; + + case 0x4C: /* ADS0M */ + ssc_adsm[0] = val & SSCADS_MASK; + break; + + case 0x4D: /* ADS0K */ + ssc_adsk[0] = val & SSCADS_MASK; + break; + + case 0x50: /* ADS1M */ + ssc_adsm[1] = val & SSCADS_MASK; + break; + + case 0x51: /* ADS1K */ + ssc_adsk[1] = val & SSCADS_MASK; + break; + } + +return; +} + +/* Programmable timers + + The SSC timers, which increment at 1Mhz, cannot be accurately + simulated due to the overhead that would be required for 1M + clock events per second. Instead, a gross hack is used. When + a timer is started, the clock interval is inspected. + + if (int < 0 and small) then testing timer, count instructions. + Small is determined by when the requested interval is less + than the size of a 100hz system clock tick. + if (int >= 0 or large) then counting a real interval, schedule + clock events at 100Hz using calibrated line clock delay + and when the remaining time value gets small enough, behave + like the small case above. + + If the interval register is read, then its value between events + is interpolated using the current instruction count versus the + count when the most recent event started, the result is scaled + to the calibrated system clock, unless the interval being timed + is less than a calibrated system clock tick (or the calibrated + clock is running very slowly) at which time the result will be + the elapsed instruction count. + + The powerup TOY Test sometimes fails its tolerance test. This was + due to varying system load causing varying calibration values to be + used at different times while referencing the TIR. While timing long + intervals, we now synchronize the stepping (and calibration) of the + system tick with the opportunity to reference the value. This gives + precise tolerance measurement values (when interval timers are used + to measure the system clock), regardless of other load issues on the + host system which might cause varying values of the system clock's + calibration factor. +*/ + +int32 tmr_tir_rd (int32 tmr, t_bool interp) +{ +uint32 delta; + +if (interp || (tmr_csr[tmr] & TMR_CSR_RUN)) { /* interp, running? */ + delta = sim_grtime () - tmr_sav[tmr]; /* delta inst */ + if ((tmr_inc[tmr] == TMR_INC) && /* scale large int */ + (tmr_poll > TMR_INC)) + delta = (uint32) ((((double) delta) * TMR_INC) / tmr_poll); + if (delta >= tmr_inc[tmr]) delta = tmr_inc[tmr] - 1; + return tmr_tir[tmr] + delta; + } +return tmr_tir[tmr]; +} + +void tmr_csr_wr (int32 tmr, int32 val) +{ +if ((tmr < 0) || (tmr > 1)) return; +if ((val & TMR_CSR_RUN) == 0) { /* clearing run? */ + sim_cancel (&sysd_unit[tmr]); /* cancel timer */ + if (tmr_csr[tmr] & TMR_CSR_RUN) /* run 1 -> 0? */ + tmr_tir[tmr] = tmr_tir_rd (tmr, TRUE); /* update itr */ + } +tmr_csr[tmr] = tmr_csr[tmr] & ~(val & TMR_CSR_W1C); /* W1C csr */ +tmr_csr[tmr] = (tmr_csr[tmr] & ~TMR_CSR_RW) | /* new r/w */ + (val & TMR_CSR_RW); +if (val & TMR_CSR_XFR) tmr_tir[tmr] = tmr_tnir[tmr]; /* xfr set? */ +if (val & TMR_CSR_RUN) { /* run? */ + if (val & TMR_CSR_XFR) /* new tir? */ + sim_cancel (&sysd_unit[tmr]); /* stop prev */ + if (!sim_is_active (&sysd_unit[tmr])) /* not running? */ + tmr_sched (tmr); /* activate */ + } +else if (val & TMR_CSR_SGL) { /* single step? */ + tmr_incr (tmr, 1); /* incr tmr */ + if (tmr_tir[tmr] == 0) /* if ovflo, */ + tmr_tir[tmr] = tmr_tnir[tmr]; /* reload tir */ + } +if ((tmr_csr[tmr] & (TMR_CSR_DON | TMR_CSR_IE)) != /* update int */ + (TMR_CSR_DON | TMR_CSR_IE)) { + if (tmr) CLR_INT (TMR1); + else CLR_INT (TMR0); + } +return; +} + +/* Unit service */ + +t_stat tmr_svc (UNIT *uptr) +{ +int32 tmr = uptr - sysd_dev.units; /* get timer # */ + +tmr_incr (tmr, tmr_inc[tmr]); /* incr timer */ +return SCPE_OK; +} + +/* Timer increment */ + +void tmr_incr (int32 tmr, uint32 inc) +{ +uint32 new_tir = tmr_tir[tmr] + inc; /* add incr */ + +if (new_tir < tmr_tir[tmr]) { /* ovflo? */ + tmr_tir[tmr] = 0; /* now 0 */ + if (tmr_csr[tmr] & TMR_CSR_DON) /* done? set err */ + tmr_csr[tmr] = tmr_csr[tmr] | TMR_CSR_ERR; + else tmr_csr[tmr] = tmr_csr[tmr] | TMR_CSR_DON; /* set done */ + if (tmr_csr[tmr] & TMR_CSR_STP) /* stop? */ + tmr_csr[tmr] = tmr_csr[tmr] & ~TMR_CSR_RUN; /* clr run */ + if (tmr_csr[tmr] & TMR_CSR_RUN) { /* run? */ + tmr_tir[tmr] = tmr_tnir[tmr]; /* reload */ + tmr_sched (tmr); /* reactivate */ + } + if (tmr_csr[tmr] & TMR_CSR_IE) { /* set int req */ + if (tmr) SET_INT (TMR1); + else SET_INT (TMR0); + } + } +else { + tmr_tir[tmr] = new_tir; /* no, upd tir */ + if (tmr_csr[tmr] & TMR_CSR_RUN) /* still running? */ + tmr_sched (tmr); /* reactivate */ + } +return; +} + +/* Timer scheduling */ + +void tmr_sched (int32 tmr) +{ +int32 clk_time = sim_is_active (&clk_unit) - 1; +int32 tmr_time; + +tmr_sav[tmr] = sim_grtime (); /* save intvl base */ +if (tmr_tir[tmr] > (0xFFFFFFFFu - TMR_INC)) { /* short interval? */ + tmr_inc[tmr] = (~tmr_tir[tmr] + 1); /* inc = interval */ + tmr_time = tmr_inc[tmr]; + } +else { + tmr_inc[tmr] = TMR_INC; /* usec/interval */ + tmr_time = tmr_poll; + } +if (tmr_time == 0) tmr_time = 1; +if ((tmr_inc[tmr] == TMR_INC) && (tmr_time > clk_time)) { + +/* Align scheduled event to be identical to the event for the next clock + tick. This lets us always see a consistent calibrated value, both for + this scheduling, AND for any query of the current timer register that + may happen in tmr_tir_rd (). This presumes that sim_activate will + queue the interval timer behind the event for the clock tick. */ + + tmr_inc[tmr] = (uint32) (((double) clk_time * TMR_INC) / tmr_poll); + tmr_time = clk_time; + } +sim_activate (&sysd_unit[tmr], tmr_time); +return; +} + +int32 tmr0_inta (void) +{ +return tmr_tivr[0]; +} + +int32 tmr1_inta (void) +{ +return tmr_tivr[1]; +} + +/* Machine check */ + +int32 machine_check (int32 p1, int32 opc, int32 cc, int32 delta) +{ +int32 i, st1, st2, p2, hsir, acc; + +if (p1 & 0x80) p1 = p1 + mchk_ref; /* mref? set v/p */ +p2 = mchk_va + 4; /* save vap */ +for (i = hsir = 0; i < 16; i++) { /* find hsir */ + if ((SISR >> i) & 1) hsir = i; + } +st1 = ((((uint32) opc) & 0xFF) << 24) | + (hsir << 16) | + ((CADR & 0xFF) << 8) | + (MSER & 0xFF); +st2 = 0x00C07000 + (delta & 0xFF); +cc = intexc (SCB_MCHK, cc, 0, IE_SVE); /* take exception */ +acc = ACC_MASK (KERN); /* in kernel mode */ +in_ie = 1; +SP = SP - 20; /* push 5 words */ +Write (SP, 16, L_LONG, WA); /* # bytes */ +Write (SP + 4, p1, L_LONG, WA); /* mcheck type */ +Write (SP + 8, p2, L_LONG, WA); /* address */ +Write (SP + 12, st1, L_LONG, WA); /* state 1 */ +Write (SP + 16, st2, L_LONG, WA); /* state 2 */ +in_ie = 0; +return cc; +} + +/* Console entry */ + +int32 con_halt (int32 code, int32 cc) +{ +int32 temp; + +conpc = PC; /* save PC */ +conpsl = ((PSL | cc) & 0xFFFF00FF) | CON_HLTINS; /* PSL, param */ +temp = (PSL >> PSL_V_CUR) & 0x7; /* get is'cur */ +if (temp > 4) conpsl = conpsl | CON_BADPSL; /* invalid? */ +else STK[temp] = SP; /* save stack */ +if (mapen) conpsl = conpsl | CON_MAPON; /* mapping on? */ +mapen = 0; /* turn off map */ +SP = IS; /* set SP from IS */ +PSL = PSL_IS | PSL_IPL1F; /* PSL = 41F0000 */ +JUMP (ROMBASE); /* PC = 20040000 */ +return 0; /* new cc = 0 */ +} + +/* Bootstrap */ + +t_stat cpu_boot (int32 unitno, DEVICE *dptr) +{ +extern t_stat load_cmd (int32 flag, char *cptr); +extern FILE *sim_log; +t_stat r; + +PC = ROMBASE; +PSL = PSL_IS | PSL_IPL1F; +conpc = 0; +conpsl = PSL_IS | PSL_IPL1F | CON_PWRUP; +if (rom == NULL) return SCPE_IERR; +if (*rom == 0) { /* no boot? */ + printf ("Loading boot code from ka655x.bin\n"); + if (sim_log) fprintf (sim_log, + "Loading boot code from ka655x.bin\n"); + r = load_cmd (0, "-R ka655x.bin"); + if (r != SCPE_OK) return r; + } +return SCPE_OK; +} + +/* SYSD reset */ + +t_stat sysd_reset (DEVICE *dptr) +{ +int32 i; + +if (sim_switches & SWMASK ('P')) sysd_powerup (); /* powerup? */ +for (i = 0; i < 2; i++) { + tmr_csr[i] = tmr_tnir[i] = tmr_tir[i] = 0; + tmr_inc[i] = tmr_sav[i] = 0; + sim_cancel (&sysd_unit[i]); + } +csi_csr = 0; +csi_unit.buf = 0; +sim_cancel (&csi_unit); +CLR_INT (CSI); +cso_csr = CSR_DONE; +cso_unit.buf = 0; +sim_cancel (&cso_unit); +CLR_INT (CSO); +return SCPE_OK; +} + +/* SYSD powerup */ + +t_stat sysd_powerup (void) +{ +int32 i; + +for (i = 0; i < (CMCTLSIZE >> 2); i++) cmctl_reg[i] = 0; +for (i = 0; i < 2; i++) { + tmr_tivr[i] = 0; + ssc_adsm[i] = ssc_adsk[i] = 0; + } +ka_cacr = 0; +ssc_base = SSCBASE; +ssc_cnf = ssc_cnf & SSCCNF_BLO; +ssc_bto = 0; +ssc_otp = 0; +return SCPE_OK; +} diff --git a/VAX/vax_syslist.c b/VAX/vax_syslist.c new file mode 100644 index 0000000..f19fb28 --- /dev/null +++ b/VAX/vax_syslist.c @@ -0,0 +1,135 @@ +/* vax_sys.c: VAX simulator interface + + Copyright (c) 1998-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 17-Oct-06 RMS Re-ordered device list + 17-May-06 RMS Added CR11/CD11 support (from John Dundas) + 01-Oct-2004 RMS Cloned from vax_sys.c +*/ + +#include "vax_defs.h" + +char sim_name[] = "VAX"; + +extern DEVICE cpu_dev; +extern DEVICE tlb_dev; +extern DEVICE rom_dev; +extern DEVICE nvr_dev; +extern DEVICE sysd_dev; +extern DEVICE qba_dev; +extern DEVICE tti_dev, tto_dev; +extern DEVICE cr_dev; +extern DEVICE lpt_dev; +extern DEVICE clk_dev; +extern DEVICE rq_dev, rqb_dev, rqc_dev, rqd_dev; +extern DEVICE rl_dev; +extern DEVICE ry_dev; +extern DEVICE ts_dev; +extern DEVICE tq_dev; +extern DEVICE dz_dev; +extern DEVICE csi_dev, cso_dev; +extern DEVICE xq_dev, xqb_dev; +extern DEVICE vh_dev; + +extern int32 sim_switches; +extern void WriteB (uint32 pa, int32 val); +extern void rom_wr_B (int32 pa, int32 val); +extern UNIT cpu_unit; + +DEVICE *sim_devices[] = { + &cpu_dev, + &tlb_dev, + &rom_dev, + &nvr_dev, + &sysd_dev, + &qba_dev, + &clk_dev, + &tti_dev, + &tto_dev, + &csi_dev, + &cso_dev, + &dz_dev, + &vh_dev, + &cr_dev, + &lpt_dev, + &rl_dev, + &rq_dev, + &rqb_dev, + &rqc_dev, + &rqd_dev, + &ry_dev, + &ts_dev, + &tq_dev, + &xq_dev, + &xqb_dev, + NULL + }; + +/* Binary loader + + The binary loader handles absolute system images, that is, system + images linked /SYSTEM. These are simply a byte stream, with no + origin or relocation information. + + -r load ROM + -n load NVR + -o for memory, specify origin +*/ + +t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag) +{ +t_stat r; +int32 i; +uint32 origin, limit; +extern int32 ssc_cnf; +#define SSCCNF_BLO 0x80000000 + +if (flag) return SCPE_ARG; /* dump? */ +if (sim_switches & SWMASK ('R')) { /* ROM? */ + origin = ROMBASE; + limit = ROMBASE + ROMSIZE; + } +else if (sim_switches & SWMASK ('N')) { /* NVR? */ + origin = NVRBASE; + limit = NVRBASE + NVRSIZE; + ssc_cnf = ssc_cnf & ~SSCCNF_BLO; + } +else { + origin = 0; /* memory */ + limit = (uint32) cpu_unit.capac; + if (sim_switches & SWMASK ('O')) { /* origin? */ + origin = (int32) get_uint (cptr, 16, 0xFFFFFFFF, &r); + if (r != SCPE_OK) return SCPE_ARG; + } + } +while ((i = getc (fileref)) != EOF) { /* read byte stream */ + if (origin >= limit) return SCPE_NXM; /* NXM? */ + if (sim_switches & SWMASK ('R')) /* ROM? */ + rom_wr_B (origin, i); /* not writeable */ + else WriteB (origin, i); /* store byte */ + origin = origin + 1; + } +return SCPE_OK; +} + diff --git a/VAX/vaxmod_defs.h b/VAX/vaxmod_defs.h new file mode 100644 index 0000000..ee97ea4 --- /dev/null +++ b/VAX/vaxmod_defs.h @@ -0,0 +1,476 @@ +/* vaxmod_defs.h: VAX model-specific definitions file + + Copyright (c) 1998-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 29-Apr-07 RMS Separated checks for PxBR and SBR + 17-May-06 RMS Added CR11/CD11 support + 10-May-06 RMS Added NOP'd reserved operand checking macros + 05-Oct-05 RMS Added XU definitions for autoconfigure + 15-Jun-05 RMS Added QDSS support + 12-Sep-04 RMS Removed map_address prototype + 16-Jun-04 RMS Added DHQ11 support + 21-Mar-04 RMS Added RXV21 support + 25-Jan-04 RMS Removed local debug logging support + RMS,MP Added "KA655X" support + 29-Dec-03 RMS Added Q18 definition for PDP11 compatibility + 22-Dec-02 RMS Added BDR halt enable definition + 11-Nov-02 RMS Added log bits for XQ + 10-Oct-02 RMS Added DEQNA/DELQA, multiple RQ, autoconfigure support + 29-Sep-02 RMS Revamped bus support macros + 06-Sep-02 RMS Added TMSCP support + 14-Jul-02 RMS Added additional console halt codes + 28-Apr-02 RMS Fixed DZV vector base and number of lines + + This file covers the KA65x ("Mayfair") series of CVAX-based Qbus systems. + The simulator defines an extended physical memory variant of the KA655, + called the KA655X. It has a maximum memory size of 512MB instead of 64MB. + + System memory map + + 0000 0000 - 03FF FFFF main memory (KA655) + 0400 0000 - 0FFF FFFF reserved (KA655), main memory (KA655X) + 1000 0000 - 13FF FFFF cache diagnostic space (KA655), main memory (KA655X) + 1400 0000 - 1FFF FFFF reserved (KA655), main memory (KA655X) + + 2000 0000 - 2000 1FFF Qbus I/O page + 2000 2000 - 2003 FFFF reserved + 2004 0000 - 2005 FFFF ROM space, halt protected + 2006 0000 - 2007 FFFF ROM space, halt unprotected + 2008 0000 - 201F FFFF Local register space + 2020 0000 - 2FFF FFFF reserved + 3000 0000 - 303F FFFF Qbus memory space + 3400 0000 - 3FFF FFFF reserved +*/ + +#ifdef FULL_VAX /* subset VAX */ +#undef FULL_VAX +#endif + +#ifndef _VAXMOD_DEFS_H_ +#define _VAXMOD_DEFS_H_ 1 + +/* Microcode constructs */ + +#define CVAX_SID (10 << 24) /* system ID */ +#define CVAX_UREV 6 /* ucode revision */ +#define CON_HLTPIN 0x0200 /* external CPU halt */ +#define CON_PWRUP 0x0300 /* powerup code */ +#define CON_HLTINS 0x0600 /* HALT instruction */ +#define CON_BADPSL 0x4000 /* invalid PSL flag */ +#define CON_MAPON 0x8000 /* mapping on flag */ +#define MCHK_TBM_P0 0x05 /* PPTE in P0 */ +#define MCHK_TBM_P1 0x06 /* PPTE in P1 */ +#define MCHK_M0_P0 0x07 /* PPTE in P0 */ +#define MCHK_M0_P1 0x08 /* PPTE in P1 */ +#define MCHK_INTIPL 0x09 /* invalid ireq */ +#define MCHK_READ 0x80 /* read check */ +#define MCHK_WRITE 0x82 /* write check */ + +/* Machine specific IPRs */ + +#define MT_CADR 37 +#define MT_MSER 39 +#define MT_CONPC 42 +#define MT_CONPSL 43 +#define MT_IORESET 55 + +/* Memory system error register */ + +#define MSER_HM 0x80 /* hit/miss */ +#define MSER_CPE 0x40 /* CDAL par err */ +#define MSER_CPM 0x20 /* CDAL mchk */ + +/* Cache disable register */ + +#define CADR_RW 0xF3 +#define CADR_MBO 0x0C + +/* Memory */ + +#define MAXMEMWIDTH 26 /* max mem, std KA655 */ +#define MAXMEMSIZE (1 << MAXMEMWIDTH) /* max mem size */ +#define MAXMEMWIDTH_X 29 /* max mem, KA655X */ +#define MAXMEMSIZE_X (1 << MAXMEMWIDTH_X) +#define INITMEMSIZE (1 << 24) /* initial memory size */ +#define MEMSIZE (cpu_unit.capac) +#define ADDR_IS_MEM(x) (((uint32) (x)) < MEMSIZE) + +/* Cache diagnostic space */ + +#define CDAAWIDTH 16 /* cache dat addr width */ +#define CDASIZE (1u << CDAAWIDTH) /* cache dat length */ +#define CDAMASK (CDASIZE - 1) /* cache dat mask */ +#define CTGAWIDTH 10 /* cache tag addr width */ +#define CTGSIZE (1u << CTGAWIDTH) /* cache tag length */ +#define CTGMASK (CTGSIZE - 1) /* cache tag mask */ +#define CDGSIZE (CDASIZE * CTGSIZE) /* diag addr length */ +#define CDGBASE 0x10000000 /* diag addr base */ +#define CDG_GETROW(x) (((x) & CDAMASK) >> 2) +#define CDG_GETTAG(x) (((x) >> CDAAWIDTH) & CTGMASK) +#define CTG_V (1u << (CTGAWIDTH + 0)) /* tag valid */ +#define CTG_WP (1u << (CTGAWIDTH + 1)) /* wrong parity */ +#define ADDR_IS_CDG(x) ((((uint32) (x)) >= CDGBASE) && \ + (((uint32) (x)) < (CDGBASE + CDGSIZE))) + +/* Qbus I/O registers */ + +#define IOPAGEAWIDTH 13 /* IO addr width */ +#define IOPAGESIZE (1u << IOPAGEAWIDTH) /* IO page length */ +#define IOPAGEMASK (IOPAGESIZE - 1) /* IO addr mask */ +#define IOPAGEBASE 0x20000000 /* IO page base */ +#define ADDR_IS_IO(x) ((((uint32) (x)) >= IOPAGEBASE) && \ + (((uint32) (x)) < (IOPAGEBASE + IOPAGESIZE))) + +/* Read only memory - appears twice */ + +#define ROMAWIDTH 17 /* ROM addr width */ +#define ROMSIZE (1u << ROMAWIDTH) /* ROM length */ +#define ROMAMASK (ROMSIZE - 1) /* ROM addr mask */ +#define ROMBASE 0x20040000 /* ROM base */ +#define ADDR_IS_ROM(x) ((((uint32) (x)) >= ROMBASE) && \ + (((uint32) (x)) < (ROMBASE + ROMSIZE + ROMSIZE))) + +/* Local register space */ + +#define REGAWIDTH 19 /* REG addr width */ +#define REGSIZE (1u << REGAWIDTH) /* REG length */ +#define REGBASE 0x20080000 /* REG addr base */ + +/* KA655 board registers */ + +#define KAAWIDTH 3 /* KA reg width */ +#define KASIZE (1u << KAAWIDTH) /* KA reg length */ +#define KABASE (REGBASE + 0x4000) /* KA650 addr base */ + +/* CQBIC registers */ + +#define CQBICSIZE (5 << 2) /* 5 registers */ +#define CQBICBASE (REGBASE) /* CQBIC addr base */ +#define CQMAPASIZE 15 /* map addr width */ +#define CQMAPSIZE (1u << CQMAPASIZE) /* map length */ +#define CQMAPAMASK (CQMAPSIZE - 1) /* map addr mask */ +#define CQMAPBASE (REGBASE + 0x8000) /* map addr base */ +#define CQIPCSIZE 2 /* 2 bytes only */ +#define CQIPCBASE (REGBASE + 0x1F40) /* ipc reg addr */ + +/* CMCTL registers */ + +/* #define CMCTLSIZE (18 << 2) /* 18 registers */ +#define CMCTLSIZE (19 << 2) /* KA655X extra reg */ +#define CMCTLBASE (REGBASE + 0x100) /* CMCTL addr base */ + +/* SSC registers */ + +#define SSCSIZE 0x150 /* SSC size */ +#define SSCBASE 0x20140000 /* SSC base */ + +/* Non-volatile RAM - 1KB long */ + +#define NVRAWIDTH 10 /* NVR addr width */ +#define NVRSIZE (1u << NVRAWIDTH) /* NVR length */ +#define NVRAMASK (NVRSIZE - 1) /* NVR addr mask */ +#define NVRBASE 0x20140400 /* NVR base */ +#define ADDR_IS_NVR(x) ((((uint32) (x)) >= NVRBASE) && \ + (((uint32) (x)) < (NVRBASE + NVRSIZE))) + +/* CQBIC Qbus memory space (seen from CVAX) */ + +#define CQMAWIDTH 22 /* Qmem addr width */ +#define CQMSIZE (1u << CQMAWIDTH) /* Qmem length */ +#define CQMAMASK (CQMSIZE - 1) /* Qmem addr mask */ +#define CQMBASE 0x30000000 /* Qmem base */ + +/* Machine specific reserved operand tests (all NOPs) */ + +#define ML_PA_TEST(r) +#define ML_LR_TEST(r) +#define ML_SBR_TEST(r) +#define ML_PXBR_TEST(r) +#define LP_AST_TEST(r) +#define LP_MBZ84_TEST(r) +#define LP_MBZ92_TEST(r) + +/* Qbus I/O modes */ + +#define READ 0 /* PDP-11 compatibility */ +#define WRITE (L_WORD) +#define WRITEB (L_BYTE) + +/* Common CSI flags */ + +#define CSR_V_GO 0 /* go */ +#define CSR_V_IE 6 /* interrupt enable */ +#define CSR_V_DONE 7 /* done */ +#define CSR_V_BUSY 11 /* busy */ +#define CSR_V_ERR 15 /* error */ +#define CSR_GO (1u << CSR_V_GO) +#define CSR_IE (1u << CSR_V_IE) +#define CSR_DONE (1u << CSR_V_DONE) +#define CSR_BUSY (1u << CSR_V_BUSY) +#define CSR_ERR (1u << CSR_V_ERR) + +/* Timers */ + +#define TMR_CLK 0 /* 100Hz clock */ + +/* I/O system definitions */ + +#define DZ_MUXES 4 /* max # of DZV muxes */ +#define DZ_LINES 4 /* lines per DZV mux */ +#define VH_MUXES 4 /* max # of DHQ muxes */ +#define MT_MAXFR (1 << 16) /* magtape max rec */ +#define AUTO_LNT 34 /* autoconfig ranks */ + +#define DEV_V_UBUS (DEV_V_UF + 0) /* Unibus */ +#define DEV_V_QBUS (DEV_V_UF + 1) /* Qbus */ +#define DEV_V_Q18 (DEV_V_UF + 2) /* Qbus, mem <= 256KB */ +#define DEV_V_FLTA (DEV_V_UF + 3) /* flt addr */ +#define DEV_UBUS (1u << DEV_V_UBUS) +#define DEV_QBUS (1u << DEV_V_QBUS) +#define DEV_Q18 (1u << DEV_V_Q18) +#define DEV_FLTA (1u << DEV_V_FLTA) + +#define UNIBUS FALSE /* 22b only */ + +#define DEV_RDX 16 /* default device radix */ + +/* Device information block */ + +#define VEC_DEVMAX 4 /* max device vec */ + +typedef struct { + uint32 ba; /* base addr */ + uint32 lnt; /* length */ + t_stat (*rd)(int32 *dat, int32 ad, int32 md); + t_stat (*wr)(int32 dat, int32 ad, int32 md); + int32 vnum; /* vectors: number */ + int32 vloc; /* locator */ + int32 vec; /* value */ + int32 (*ack[VEC_DEVMAX])(void); /* ack routine */ + } DIB; + +/* I/O page layout - RQB,RQC,RQD float based on number of DZ's */ + +#define IOBA_DZ (IOPAGEBASE + 000100) /* DZ11 */ +#define IOLN_DZ 010 +#define IOBA_RQB (IOPAGEBASE + 000334 + (020 * (DZ_MUXES / 2))) +#define IOLN_RQB 004 +#define IOBA_RQC (IOPAGEBASE + IOBA_RQB + IOLN_RQB) +#define IOLN_RQC 004 +#define IOBA_RQD (IOPAGEBASE + IOBA_RQC + IOLN_RQC) +#define IOLN_RQD 004 +#define IOBA_VH (IOPAGEBASE + 000440) /* DHQ11 */ +#define IOLN_VH 020 +#define IOBA_RQ (IOPAGEBASE + 012150) /* RQDX3 */ +#define IOLN_RQ 004 +#define IOBA_TS (IOPAGEBASE + 012520) /* TS11 */ +#define IOLN_TS 004 +#define IOBA_RL (IOPAGEBASE + 014400) /* RL11 */ +#define IOLN_RL 012 +#define IOBA_XQ (IOPAGEBASE + 014440) /* DEQNA/DELQA */ +#define IOLN_XQ 020 +#define IOBA_XQB (IOPAGEBASE + 014460) /* 2nd DEQNA/DELQA */ +#define IOLN_XQB 020 +#define IOBA_TQ (IOPAGEBASE + 014500) /* TMSCP */ +#define IOLN_TQ 004 +#define IOBA_XU (IOPAGEBASE + 014510) /* DEUNA/DELUA */ +#define IOLN_XU 010 +#define IOBA_RP (IOPAGEBASE + 016700) /* RP/RM */ +#define IOLN_RP 054 +#define IOBA_CR (IOPAGEBASE + 017160) /* CD/CR/CM */ +#define IOLN_CR 010 +#define IOBA_RX (IOPAGEBASE + 017170) /* RXV11 */ +#define IOLN_RX 004 +#define IOBA_RY (IOPAGEBASE + 017170) /* RXV21 */ +#define IOLN_RY 004 +#define IOBA_QDSS (IOPAGEBASE + 017400) /* QDSS */ +#define IOLN_QDSS 002 +#define IOBA_DBL (IOPAGEBASE + 017500) /* doorbell */ +#define IOLN_DBL 002 +#define IOBA_LPT (IOPAGEBASE + 017514) /* LP11 */ +#define IOLN_LPT 004 +#define IOBA_PTR (IOPAGEBASE + 017550) /* PC11 reader */ +#define IOLN_PTR 004 +#define IOBA_PTP (IOPAGEBASE + 017554) /* PC11 punch */ +#define IOLN_PTP 004 + +/* The KA65x maintains 4 separate hardware IPL levels, IPL 17 to IPL 14 + Within each IPL, priority is right to left +*/ + +/* IPL 17 */ + +/* IPL 16 */ + +#define INT_V_CLK 0 /* clock */ + +/* IPL 15 */ + +#define INT_V_RQ 0 /* RQDX3 */ +#define INT_V_RL 1 /* RLV12/RL02 */ +#define INT_V_DZRX 2 /* DZ11 */ +#define INT_V_DZTX 3 +#define INT_V_RP 4 /* RP,RM drives */ +#define INT_V_TS 5 /* TS11/TSV05 */ +#define INT_V_TQ 6 /* TMSCP */ +#define INT_V_XQ 7 /* DEQNA/DELQA */ +#define INT_V_RY 8 /* RXV21 */ + +/* IPL 14 */ + +#define INT_V_TTI 0 /* console */ +#define INT_V_TTO 1 +#define INT_V_PTR 2 /* PC11 */ +#define INT_V_PTP 3 +#define INT_V_LPT 4 /* LP11 */ +#define INT_V_CSI 5 /* SSC cons UART */ +#define INT_V_CSO 6 +#define INT_V_TMR0 7 /* SSC timers */ +#define INT_V_TMR1 8 +#define INT_V_VHRX 9 /* DHQ11 */ +#define INT_V_VHTX 10 +#define INT_V_QDSS 11 /* QDSS */ +#define INT_V_CR 12 + +#define INT_CLK (1u << INT_V_CLK) +#define INT_RQ (1u << INT_V_RQ) +#define INT_RL (1u << INT_V_RL) +#define INT_DZRX (1u << INT_V_DZRX) +#define INT_DZTX (1u << INT_V_DZTX) +#define INT_RP (1u << INT_V_RP) +#define INT_TS (1u << INT_V_TS) +#define INT_TQ (1u << INT_V_TQ) +#define INT_XQ (1u << INT_V_XQ) +#define INT_RY (1u << INT_V_RY) +#define INT_TTI (1u << INT_V_TTI) +#define INT_TTO (1u << INT_V_TTO) +#define INT_PTR (1u << INT_V_PTR) +#define INT_PTP (1u << INT_V_PTP) +#define INT_LPT (1u << INT_V_LPT) +#define INT_CSI (1u << INT_V_CSI) +#define INT_CSO (1u << INT_V_CSO) +#define INT_TMR0 (1u << INT_V_TMR0) +#define INT_TMR1 (1u << INT_V_TMR1) +#define INT_VHRX (1u << INT_V_VHRX) +#define INT_VHTX (1u << INT_V_VHTX) +#define INT_QDSS (1u << INT_V_QDSS) +#define INT_CR (1u << INT_V_CR) + +#define IPL_CLK (0x16 - IPL_HMIN) /* relative IPL */ +#define IPL_RQ (0x15 - IPL_HMIN) +#define IPL_RL (0x15 - IPL_HMIN) +#define IPL_DZRX (0x15 - IPL_HMIN) +#define IPL_DZTX (0x15 - IPL_HMIN) +#define IPL_RP (0x15 - IPL_HMIN) +#define IPL_TS (0x15 - IPL_HMIN) +#define IPL_TQ (0x15 - IPL_HMIN) +#define IPL_XQ (0x15 - IPL_HMIN) +#define IPL_RY (0x15 - IPL_HMIN) +#define IPL_TTI (0x14 - IPL_HMIN) +#define IPL_TTO (0x14 - IPL_HMIN) +#define IPL_PTR (0x14 - IPL_HMIN) +#define IPL_PTP (0x14 - IPL_HMIN) +#define IPL_LPT (0x14 - IPL_HMIN) +#define IPL_CSI (0x14 - IPL_HMIN) +#define IPL_CSO (0x14 - IPL_HMIN) +#define IPL_TMR0 (0x14 - IPL_HMIN) +#define IPL_TMR1 (0x14 - IPL_HMIN) +#define IPL_VHRX (0x14 - IPL_HMIN) +#define IPL_VHTX (0x14 - IPL_HMIN) +#define IPL_QDSS (0x14 - IPL_HMIN) +#define IPL_CR (0x14 - IPL_HMIN) + +#define IPL_HMAX 0x17 /* highest hwre level */ +#define IPL_HMIN 0x14 /* lowest hwre level */ +#define IPL_HLVL (IPL_HMAX - IPL_HMIN + 1) /* # hardware levels */ +#define IPL_SMAX 0xF /* highest swre level */ + +/* Device vectors */ + +#define VEC_Q 0x200 /* Qbus vector offset */ +#define VEC_PTR (VEC_Q + 0070) +#define VEC_PTP (VEC_Q + 0074) +#define VEC_XQ (VEC_Q + 0120) +#define VEC_XU (VEC_Q + 0120) +#define VEC_RQ (VEC_Q + 0154) +#define VEC_RL (VEC_Q + 0160) +#define VEC_LPT (VEC_Q + 0200) +#define VEC_TS (VEC_Q + 0224) +#define VEC_CR (VEC_Q + 0230) +#define VEC_RP (VEC_Q + 0254) +#define VEC_TQ (VEC_Q + 0260) +#define VEC_RX (VEC_Q + 0264) +#define VEC_RY (VEC_Q + 0264) +#define VEC_DZRX (VEC_Q + 0300) +#define VEC_DZTX (VEC_Q + 0304) +#define VEC_VHRX (VEC_Q + 0310) +#define VEC_VHTX (VEC_Q + 0314) + +/* Interrupt macros */ + +#define IVCL(dv) ((IPL_##dv * 32) + INT_V_##dv) +#define IREQ(dv) int_req[IPL_##dv] +#define SET_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] | (INT_##dv) +#define CLR_INT(dv) int_req[IPL_##dv] = int_req[IPL_##dv] & ~(INT_##dv) +#define IORETURN(f,v) ((f)? (v): SCPE_OK) /* cond error return */ + +/* Logging */ + +#define LOG_CPU_I 0x1 /* intexc */ +#define LOG_CPU_R 0x2 /* REI */ +#define LOG_CPU_P 0x4 /* context */ + +/* Function prototypes for virtual memory interface */ + +int32 Read (uint32 va, int32 lnt, int32 acc); +void Write (uint32 va, int32 val, int32 lnt, int32 acc); + +/* Function prototypes for physical memory interface (inlined) */ + +SIM_INLINE_GCC int32 ReadB (uint32 pa); +SIM_INLINE_GCC int32 ReadW (uint32 pa); +SIM_INLINE_GCC int32 ReadL (uint32 pa); +SIM_INLINE_GCC int32 ReadLP (uint32 pa); +SIM_INLINE_GCC void WriteB (uint32 pa, int32 val); +SIM_INLINE_GCC void WriteW (uint32 pa, int32 val); +SIM_INLINE_GCC void WriteL (uint32 pa, int32 val); +void WriteLP (uint32 pa, int32 val); + +/* Function prototypes for I/O */ + +int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf); +int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf); +int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf); + +t_stat set_addr (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_addr (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat set_addr_flt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat set_vec (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat auto_config (char *name, int32 num); + +int32 clk_cosched (int32 wait); + +#endif diff --git a/VAX/vmb.exe b/VAX/vmb.exe new file mode 100644 index 0000000000000000000000000000000000000000..5f7cb8f858e16f15e4571a2a0f17ff24bdcf9715 GIT binary patch literal 44544 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

=Ndl5kO(boLWxi&6-CF4Rxt&RWr87c={8y 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 WKr;+hs+?91-M0>=#b>yk?LPt02=3(o literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_cr.o b/VAX780/pdp11_cr.o new file mode 100644 index 0000000000000000000000000000000000000000..171b24d2b2060bf68c2f86479749c510da2f9d3b GIT binary patch literal 51252 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 B0Qdj^ literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_dz.o b/VAX780/pdp11_dz.o new file mode 100644 index 0000000000000000000000000000000000000000..73ae5cf293d37645850644af70c0db4099819467 GIT binary patch literal 26448 zcmb_^3wV^p+4j7d;39e=RR*&u(vyy^KNg~8=W6{yOtj8y#AmSU&haM+4xlPlMmTYkEADW z!yZ&d%A2?0zmYoh89us~qJD4QiyM$us;O-MUz)l$3}za6g1ftec31wPZOBX&U%&r1 zj&Kfa?K z`wkuZjF0Yo2acWM(J46g6)zlnpDxI^67!&@bDy zccbHen4u2!VCb}8>^@)Y-&pt~7HY?4ZS;A%f=9Z8M>$5it0gRGfjE-IOxbe6w@K|Z^X#n6UNBi?wY-b5k2_u>xM$K-n~4v_vlL3_GcM2 zt|&zOmAtKeVJ2pp$53Z`ul<+ZB}s23n|Ky|O3}q|rX(t0N!k%S$X*{>Z^X9m^Lm2^ z`*-5eZbr|=-8Ba>UEk|9vt=%$oI?N)_Ju}Y@=KjNxN)BS5}p3uKCxH#s55xb!h)BT zcl&BAKrk4ys;TQ9$n`fQ`;eQQu%o+Eypf!+uF!dg@|;rc&~dp}uY!3Sy0qH9m`aX7 z)Aob5o>nIcu}6wgb^F15y||~ids|m+sn79DSMYG>^@r73ES85k>0M6yQryV`Se0A= zOLqj1q807w;1R~yk<>2ugGW1qN8rXI-WxvZy#A=l+weZ*-I_7to!9rOyjyNTfy79B z9iA0kh=)_QA3Vtd;!(v()TbOF(~Dv2+U<<`*zVT(l#2%(&+m=6uhEoD+=Zqx!+y>s}I^WjA=NJ9w;21@y9* z#rhBP>2OTN{TLa{Ie+k2XYeRIdNgZIrY?9AHFolebTOXu6L$m;v&lpE;sZmvqw1!; z+99y$2ZEH7d#dMCklqRu~RjZx&)BiF1I2{0<%Rn138DGYfXC*ZVVMbq0GeXuVlCJo^$W>haRk z)-*DVTM|<>d7_aj-WxvNp?8I>r-RdkraK}fJk7AWpG2^|yRyn`d;H?$o}t}Cs4MO9 z$yv$?jT-qJI2hDbXwFtiYx}VSTN;Sc->l(o8_M+K2P+t!Zif$TaqO3MF>eF6N~Uk@ zI_mxcpr59?pO^||Iswf&aFERyavTNePg0~!C#zEQWkT;i&^IDgU%-+6QZ)A0y`)oI zkMQ0eoEhY?-Mt>#xt#m-gvI#lzMK>6UH1X}VkOFq&vegnQY|^hu>_|oJrp*(JrB98 zBU^#AKn?H|MC31EI_s)a#p`Sr!quy=ibJzn$1AU~2e*myMnD6*O@B9v9 zDh?1UjwJL$Gr0Fq7ZhP@sp2>+9l;|9qmgW+1!Yc!yxaexbsoVk%$fH?v5j$_Wo+4x zhZgo#QswBnecY7BOF1wsMm$ELQAZnWVKdA=#1KT|P<-en@HC|`s3MuAXBYL|k2+58 zQcIJ$OONX5+qHfBp_4ZH2B2?a-|p11$ffv z=K-uXD@_%uCzp^9LDUJBkPo|qA7a)&LXqyJ$5Kxb*fH*vIa8!1HWsEWLD(M_a;l>q zCLEU)cQiWl%?XvmI#eDt(K zz{-6E0k;gUF%|7cgalkdo}Xxu+3pDg9{ms8I{7M=A^$}6Ru z_6H=*`uf=b38amRgF>OWoNz%`&gL9G_RFm7)Fh zw1KaB1IbUv&S)~HFM%6GU2kn)Rx-shk`2k6z69Ij8gPfa`%1LW*OT8TZ>F6W>pAI% zoP;1AO}gwU@aNLX-1T1i2W79Ml{tM#uahSBZ_zi>q+y$m1^UCCP7_O^0~9Pf!OI$C zmE{&>dvizT4zdPY`B(!yr7Ao7+-xtJ@D5ep+_SUK$*wNIw3qx`TMuc5Cm`eTs;~hVyb} zUZV9SX=V1XgYqihX4b|2hjNn`|=T(EOt{z`Tq&2R*lTg94XBs|zzN}%W z=JREog-Sj<<6~rI__BPStXr9HW&YVmuxkjutn*Cf=d9|SyFzWytr8V!(h7u^!XZ z=MS)Ss8HKK4r()o3C;1>qOTc6Li7C>Ltn;lp@shI&`QP#p+)|ysd=Q(V*dhaE*2W_ zzs$NP3oZ5kkPV$8bc}yCX+Y>We<8Y(ajNwk>Uc&?^z+n}QEI)UX@$Rtnnz2Iru%QC z@aaNl`0r%tIICNCu+o1S=|qdmvu9M5e=~(AS+vzN>U{rnYMyMprZvy?|BOaXwf?WB z^ZXC8?s*pPJw2o5`$IHxp5Cl@M%DVSCao5FslS8t0-;y<53%lrLRa{2Wa)fsoK_X` z|CS9c5SyF*x3F}P^>f{BR1Nq)^z8HY-=HHW;|Fv@b}mJ>8jq;#t2i*P7>D?>k5L@A zgf!DuS-$^3g)AQ&!kH;hg%!~e~9@` zZ0^{A#f{v9;%WGe&U8I|up71^1m2rcxV z#JT~YMgArlTOzdBKai!T3Jv&ovUHTtQvX-jP^tA3-Ow2SX>4e;^#@JI`9G%c7%LYW zHpb*1DLmF1t7(P*>*!wQ8CI31)BOub$6J?aI>X;XmrRggsPrEoEthty{6D2t6NS$6 z|AKUqb(^j`-~S7mG1+=n(?$MUX-0+2lv}BFe<$r^+mx~oXe>pW> zAysYvw@8;;FKWd({)b4f6dUvX-(>&l#Hd35ZkDdF7&M+yMWzS!Qd;bPohCO}Ga%#{ z707FEi>b^;p=0tI5=|;IBy}hH$J45?(2Bgo6qVT|Y8rAt2l^R%*CjJEx&*~8m}=J?AvC>w?5 z`=4g%?LrGphwl(ta+<@is9wyaDE<$OQS2U%G{3-eZo z8&#Gqv?#B>@oJUjx8B!vi=|KB1skM4IK0jnGo9t5ZKmgJ&kT`U>P6({e1ebP;jJGG zUNuBFm*rztK~?o^l{LT`jP%f7!xJ;Sc6l~7L)o)2b?u4Ng{?&PnJBU+nT6PweJ)&M zPd3cXejYOR6eF3FU5yUf6^7+!4`$h!h81RKl1(+ND0?s2G{cIs-yxfBSO9xgu(J#+ z&3>A#pKaKf>@Ko%3>%mIB<-AG*u?CQSvJ$Ga~0WtfbZ>dAA>H>sOiHx);1{nJa*1A zYR0gRh7M&{l6gnH;u#ia;Vhv(^Vl4TLhZBT%G*oxs*b}=b}qE$+vi_RFGZL=*R-y( z|4duv8Rp9#$=0h4v$H>^Wf!oLXH*VGj>^3|SiNR6l10`LRJMGUp4;sHQgFV0fzsiR zAYpkeMuBIO8nI{=nO42YH+%wAC#gL$bgi;7q_7=|55o>?tWiv*7i09eIk^E|Hj2lH zv{CFZQS4Bo_;eAyVq`>FX9!s_vayeZieF_Yt4-Mt*zx7p%X?Q?)vP&vAkBlqJA<^RNsK%k~aP_Cq zQ)G>$f=4V}hnjzf_Xgxm#^GQDefF@ZQ|#KKW^!+en%+3Hojr9(T%Bxnuour*7PY;O zqv12qcn>sE$Frhvg|^mV;va`L7k>e7K$f*Un-*zzJ?FSf<#3Q6N0Ch4ldZAvjP;&n zv9-S=HhAZnfvvu7@P%^&A-NR@_Q@_ z4M4|lgWwiNtL-sCk8_`OIbA=4V6>xo_&wA++QWJ7+2o^($4HrY)uGh;0cYV1 zqnA2Mp>GECJ^>v5BWgOw!!&~%nr0?7T?C;d#bd|!kq+nQ7&R9febjYwo>I*a{S)=+ z$#Q{5Cd=?I*ry9Tj{#_ChqC5-xP|a+qGdm3X@k*3Eob3<4>XMe8cp?%CP;sa(h(y~ z$=C7T2I>2NBVw3{%^tn{Zj!pqQr9c@kkgF}s9B^*ZJlRICTyDT{N|Zk16B!M-ohf9qSu zKHByq-c_(K1AkJ}K6xV5Cr_mMuD)%XNSHg9g2>Owqh9-J#>F5yZ4$gf(9JH`#~6?V30B5 z)l?&1O*P`xR3lz>j9{y;ORMd=)ord;EzZLqn^xH(FMZxdt1Ho}cIvKFr|wF1>Ml_w zPTiGk6v6#YvYAs#=-}JA-dk4S2;;0q7h2XSm$B$hP9Ts?EZW5RVp%zV1v~{a ziP?8Qh#dW-QmUi?hFbTyb6%l>ueozP9D;k@IhCw+pF8J6%G~eD$@vUzje$FywrA7I z2a1s6wEY6>e4RT&C+9u3{S9}{Av*U#-5qAb;iXUvag> zzoS~4S3%BcDVv`7AFhf{P9gPf|06a_PR_3>^AjkIQ@Nl+T0#$pEOC8Ct@At z(MUu2%Erd>NX7K21BPpc3ArglQ$E!0v@f)m*_*bm*?F100RJX0izMu; zJhO&)77VesZnodIH`yyaO+&WWou1PQws_7M`aa|fJfjLWZ+#RpJ3SSJo>L2U*_*d+ zwF~ToUB1P8t!J#|&EE6=)<-vcMh|%(HOmT6<#ZHpUB1kl+qC9@ea^$157_1S_r7Or zfv2=^>$0uO>~oi09XYTSHkI$|ojK*^ty?xx=N7xfGj`yE-T`~I+6U0WCcE=hJK?!v z$WCwUDR2IsYa(0O1t{OT6{dJb7uf&l_3weXJH4x(@($iJWhP5^O`6G%2>P?dKI8Gt zpy|1DAKyGBvUBH7_$0A&^Aky)gyro6w&rR~<0PF$Gi5k4^R;?K3`D=J6m> z)q?re3xZ43oF$9Z;@L~jSMwJwR<&~$s~Tj~RxMUbW(60_R*NsKUQnxQ^=D00&79i# zYSz-)g;h&bRq*0jRgeQ-9Gs)3HK=K`)$|56eYTp`uBI(gRn<%8EL^Z4IHwlj+u9m# zj7Qp9V@mR>7cN+$mekIwtzJ@FJ!grcuHcd-3c6|+F4m1e^8D(#OBV+b!mZ($G6v6I zI9DxL$j_?F)W8ArQ50ydZ(W&aA2>kOw1vVg0W=ryXp08w(d6p-csO*T`~hlKBHk7V zMPg#1tI7#W2B=AFK{}+$LM|3l_0gzHUpR!NOR3uCNGz}>l2Q~%#KIl1a{}!hZH?h@ zC~*0L<>yg$;DCkg;g0%vTStnX0DXWS5&~tRvjddH?nL2h8^N$Ty=6PtZt4^@fcQY4# zryfr)=ef$Ad>%TgT>6}Ro-t1EC!fa&J~W;79eJJ}`qRV1LVtRAF1Vr}Jv;*Zwx4`% z;ki*tZQjWvH4kS@e%6oQxCNZik32VU+xyAqo~plg zx$Eh0$s@^U3Z5l#ACY5-v`u8pcvJ!pMf1{^3py|EyBK(>N!vumkXx4g5}gB#-wc` z=ZRaEyiDiCeZ}{ERaq=d4?n z{A!&S_Yqr6+9q-yyJg8gH|0L!A5GdO@|5G25tE;}OwJq_8zN|u4W3;2LV=e!d&dVig z3&$mcmq^IarS$LJyZSDI38X$AuWxMD zHzi=9ux7mN0E;DBTJ&88)6H#b^c@4!;dpCQAO5AFj#u|QY@5QmXxmDCLKfQE#tfcT z9eHw&B6wr0L%n8_9sG;gZj+9-cc_m{GTzvsj+$f(gflTps22})=yMoV)`!*ECKql} z^F;v3D^0ScF&X#;&XpO|xVUuo+ zc7#{z6B5)g$)gaGEo~va{`1jq!UjhSc{$&jAnjP&h`~-^#3xNvb=3=@qdrvE9`8_f zjm`CuR)rGet)fmHPO-?f;kvluICRA6Cls*X2&}M@VOz~V{PC1R_oBQp%2B6wRH$pH zkA-zf3#Q>CmK-AXjHW&kJ3js;Bt>LwCqr0Qd1RM&b z{RHg?HO}eNPqoqlI@h5ZMUDf@4O>fn>}qy+%4Aj76p4mg+xlD*ZVjn5^>BQ=E*7tk z8>^|1drXK&>sQ8-U8HbbW1^$3u`QZtX;p9}n$QMfaHSV!p4HW@O2Fe#*jnEbR&{OB zP+eP76K95lahe%V4iv0|1_l^yR@^<~%o$n}3B{Y$+yzSma{~I%N+|}XnPzo$?e(Dw zT{EQGF?QP6mimsX!>}wCZ(tEdL}57ytCW!td(G?t;Y?v37Md>)TPt;NsMmBvu-?#g z-7(r4<0+(iPEZ+JTf=K`!xUay-@<!{#3q5pc)0=X8HH@CHf%bT%+#Utf0916ly%F(L` zE(F>W&{B@s(5ydJPnka6$ho}4cxjKtJ0h(s^<12e#^YEG*&IzFc7!q4xHiza>?3C( zlIK^?4=UKmj>c3x0^?dZNp(N;3R}lOf;4Q!VLr|GpaYSE z$UgG{#l?@xab#_WBKT{P0jw-!Q_v!=-V>Pcq-UzSL@N%OA@~6ySl8YbjWYhO)(Z7g zE?&n)da9nm&eDh&M3B>f2KB#n8D})cv0~D&*qR7}3$roCwVye9 zlAxYE+>z?r2-F*QBTt4+?-yWvB%~T!TVm#-tK4YC`N99aHTpMRr$81*v*dTxE0A_Su${*^&#gETC13d$;g5ni z^$kM~>r;-0z_G%4HstGJ>(i~5-F^tVCkI4S~@advk#;JC6?5Gd>YcsEc%) zy#nv`h+papAI&kOyle`PuxyK~G8+--c z^YEtrZvZbwyahzxqu|@{4&cr5?*jMXy$$8$&msLV0+xTbC4U**dC}%K@pkSn+1@+g zyhGu=CHc>R&WrNDatM(8cWL}HaOWPA_5YSu&M%@k_n#~u1n%5#k{5$J_nPEm!Flh| z0X^hrg0H~fTnf(q%m8;@w5KYK*MRd5>uHp+KJPT0`(5%C;Lg1+`Bjw9GWX%+9pKK3 z`qzOw_u?$S9o)HJCciID{*g4kC5`_8+_^7j{T<-WJvsT$!JT_>^8W^R?!U=D1$SPw zkKaLeUgUm^z4Ia;3hvz7vwm4xIq!{}7v(F{_`)=PMH+8P8sC)0A4=no zrSb2kaem|8dC`9URKR(Wzm>*+md5!RRBEbGt+=R!+`3e2hcf$>XRWj4=w;)4B``~ zJ*Ig-r|(eY6o))#!_PB3sJSSiEYB?F_DCN@a4&@u6ms+x98sTcaIDiuX?=1uDP-~T zO+<8>r#}|rh^LQz;JB`%1PbJ8h{@!|f>tL5yhhL_gx4Cx5uA0+Z3Ys3hakE2L7u*$ zN}iz5bJlR5HbKp0R8mMaMz6*-O$-MjbEb9v8p*1CryvlTs(tB{n3JuP7^2nctEAXU zW45-pj>k~vV27b}nWaxtJcGsKb!~}w<7(V6X-TA0tLcj-UB+%E%Q{v;IYtW>p58Gm8VS=p^ zL9m-w}KR=s8QNUF6vO>92bLKPDfFUy&1w@$8`-_Q-!Co`W~mSIYCNndF7S z`JCeK;m#!%;u%GRT|Xwmjvm30SRXvtV4M!LX5yQx#j-D|I_@ zr1_o}`H#rqhh0GW;ip9O`!nXFzj=5T6Hg-!M?Z+@|3!k0f~y2?61OQU5(6 z+U3veENt$$Kk`)Jj6)1!-2_tanSvJ)QSVYB>{|}>oR70FaW01~FCaf(sU^g@=r{8bKP$-*FOQI; zA5W5Bi1Rsp2tE_&!KSvF9QK5WXn&pH%S71EpB@lD5IjJ{Jn-Tm!~9&qVMOGQ5k6UP zrr=`1h~QTQ9{@5gzePkG?`1yx^0}0MA!rXU?Oj5I{So0e3f>|34Z#-#e%m@li)3q{{WEv*-Vc4zL$st%5Mbs z3w}aGdw&uQ7YvfCmdPwkPDSwUSwMzYv9QAh-(cTAAzK`Xoe}EkIKNa-jEJXdeK-Ql?4tu7M zFGYNi!(UfP{@p~#-_LT$KPmiq@{17{#=(!AcC{oTZ08{QRDgQdlvCe*5@}D6OV*U_k z;BU;#hrU9@6yvR!2wp;*f_M?WSa>~<;~AIy$H*^-zlm2U6+leV{&FDo@GqU7<@gG{ z);5o^1=s`H^evKiXY|h`-DN4;C4=7csfF)S} zh@)^mCPMFVp)=u4$=o;NwqRtOIfVbgly8;F>14-0>s zh#*v{7Hmg_;q>amlDS)RUy2RI2L!7!mkh>72YlwC!UV@Bc7quEs}qm;GM*A z*ryT4BMv41DdGh9N%)(BZxhQAPsCFAP4f2>C*mgsMA&^y@|6#K64p5)^7&JB_9sA` zjDAV}3ds)!E$7wGb{1!oD)6I?8aDWv6A2(A=t6-)?TCwQaaZG!g+ zen;^8g4+dm3;ss%Q^79;b1c*TNWs$u`QIn7pZu@Ph?fZRm)+zEK|L?P?-u^3;Io2y zexUrP!hbFJiQq9o{l#&}jbI!D#|rA-p@Gj8&i@O6is#&^>5&S{NEW^U&l94$1`w?K_I`u2(q!uL0^A zD#)K}lP?y0Sa6Hrj|KVjV#*y7>=n$ygwpy1#|fS#I8Tt@L!?}jU_x+%;N61tm}``4 z6}(>XW5Le^v#?fBZmFQ&KZEn?ocRLEfbt125upT z{F_q#k>Eh?pV7{6BIHgIEFnVvbSa-Fc!^+>;2nZn1YagX&#QvliO{oG%Kt2w#eFr} zEg(XEnBYhvY&VyPdV>Xr5>aow zlwT;=ESM1N6ueV#i{KB4=-cbU-yx#ScZL6&i1)|Bj}WnM{*!PI_x;#6XAxmvArYIU zvBIYkv2Q*{_&g%^%{9U=BVylNCp=2TCTo@O8;IC9-!1%MBF;fPcd)(Zh}a*#DEv+0 z6nsBh_#PrQYaa+dNIX@kPlW%KSc>(5=Mm^DCYC8RN%*!!V> Y!p8{b&$3xwDIAY|W#Pj?gsQIhzjzQ`EdT%j literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_hk.o b/VAX780/pdp11_hk.o new file mode 100644 index 0000000000000000000000000000000000000000..0da7d2b681c6b0194638d6ff1fcec01327dfb9ab GIT binary patch literal 30240 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 R)6aN}D+@mkVz}sf|0h_<3QPb1 literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_lp.o b/VAX780/pdp11_lp.o new file mode 100644 index 0000000000000000000000000000000000000000..76f0a67b7a5ea6632fa28ebf9bd98aa5c98d51e4 GIT binary patch literal 11300 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 E2NIZC3;+NC literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_rl.o b/VAX780/pdp11_rl.o new file mode 100644 index 0000000000000000000000000000000000000000..0fb3950a67567173e71a8ab1c823cf6fe4989cb7 GIT binary patch literal 18688 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 `Y$f+rqHg$4s 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 literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_rq.o b/VAX780/pdp11_rq.o new file mode 100644 index 0000000000000000000000000000000000000000..eeb78a411d2e3327a850fe2044fbdb8f0bef97bd GIT binary patch literal 73596 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

}zv&iXSs4}c>D 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 literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_tu.o b/VAX780/pdp11_tu.o new file mode 100644 index 0000000000000000000000000000000000000000..c42e2241bd5e55d07572922c1b11cfebd8f03340 GIT binary patch literal 26508 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 literal 0 HcmV?d00001 diff --git a/VAX780/pdp11_xu.o b/VAX780/pdp11_xu.o new file mode 100644 index 0000000000000000000000000000000000000000..51660cac68dc5e2f5cffe54c02790337f2e26d07 GIT binary patch literal 53396 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 literal 0 HcmV?d00001 diff --git a/VAX780/vax780_stddev.o b/VAX780/vax780_stddev.o new file mode 100644 index 0000000000000000000000000000000000000000..848c37138ee255088bc8948acb14cee5857340da GIT binary patch literal 29460 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 literal 0 HcmV?d00001 diff --git a/VAX780/vax780_syslist.o b/VAX780/vax780_syslist.o new file mode 100644 index 0000000000000000000000000000000000000000..23658907469c9fe0dd8776f0fea120299819a85e GIT binary patch literal 9152 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

>oRmwA?gBp<)>Q3;Yf{pUjrBumSS&4URf%aG*L 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 literal 0 HcmV?d00001 diff --git a/VAX780/vax_sys.o b/VAX780/vax_sys.o new file mode 100644 index 0000000000000000000000000000000000000000..16a9d68dd8ed8e9edc18c39503256841c438f69f GIT binary patch literal 39876 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 literal 0 HcmV?d00001 diff --git a/VAX780/vax_syscm.o b/VAX780/vax_syscm.o new file mode 100644 index 0000000000000000000000000000000000000000..4b6564b04f505807838d7ecbf5f4f3cb02f0af85 GIT binary patch literal 20300 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 literal 0 HcmV?d00001 diff --git a/build_mingw.bat b/build_mingw.bat new file mode 100644 index 0000000..187cea2 --- /dev/null +++ b/build_mingw.bat @@ -0,0 +1,14 @@ +@echo off +rem Compile all of SIMH using MINGW make and gcc environment +rem Individual simulator sources are in .\simulator_name +rem Individual simulator executables are to .\bin +rem +rem If needed, define the path for the MINGW bin directory. +rem (this should already be set if MINGW was installed correctly) +rem +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 path C:\MinGW\bin;%path% +if not exist BIN mkdir BIN +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 echo "MinGW Environment Unavailable" +mingw32-make WIN32=1 -f makefile %1 %2 %3 %4 diff --git a/build_mingw_ether.bat b/build_mingw_ether.bat new file mode 100644 index 0000000..15c9a7c --- /dev/null +++ b/build_mingw_ether.bat @@ -0,0 +1,15 @@ +@echo off +rem 12-Nov-02 rms Ethernet support +rem Compile all of SIMH using MINGW make and gcc environment +rem Individual simulator sources are in .\simulator_name +rem Individual simulator executables are to .\bin +rem +rem If needed, define the path for the MINGW bin directory. +rem (this should already be set if MINGW was installed correctly) +rem +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 path C:\MinGW\bin;%path% +if not exist BIN mkdir BIN +gcc -v 1>NUL 2>NUL +if ERRORLEVEL 1 echo "MinGW Environment Unavailable" +mingw32-make WIN32=1 USE_NETWORK=1 -f makefile %1 %2 %3 %4 diff --git a/common_int64_adr64/scp.o b/common_int64_adr64/scp.o new file mode 100644 index 0000000000000000000000000000000000000000..ee1f9c1d314525498dd2664b1a765b5b1aae23b6 GIT binary patch literal 127380 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�v|1I#^1%T8uC5P#(igjkCJ!s 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

m$4k=E0ub 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 z

r$Ig^}49#76CKSnMf7n4iK>&O-4;CxZt-d*H_)Q^%+kx!H3T8HaxW3}ESa&K}v zIfFckoI{>W&L__&FCdqa%g7ta)#N&IJ=xbY_wOq>&s6IP&MQ@pi3`^koL8zGPfoVV ZDdbFY7CD!kN7mCW5~ctApKW-Z{V(rnt?mE- literal 0 HcmV?d00001 diff --git a/common_int64_adr64/sim_console.o b/common_int64_adr64/sim_console.o new file mode 100644 index 0000000000000000000000000000000000000000..691d312d764ded30eff143ff361252c4fb71323c GIT binary patch literal 26156 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 UTNbwG3ym~F)A3;=rbhMqU#Ht)-v9sr literal 0 HcmV?d00001 diff --git a/common_int64_adr64/sim_fio.o b/common_int64_adr64/sim_fio.o new file mode 100644 index 0000000000000000000000000000000000000000..2207a316d15a601cd6be0ce0512ec7a35f3ebe2b GIT binary patch literal 7868 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 i{0`~Q2h&?@o( literal 0 HcmV?d00001 diff --git a/common_int64_adr64/sim_readline.o b/common_int64_adr64/sim_readline.o new file mode 100644 index 0000000000000000000000000000000000000000..60638723d8acb1f0f2fd1344a3462d110e20ec87 GIT binary patch literal 4672 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 M;=da5F)VTTe``J?%>V!Z literal 0 HcmV?d00001 diff --git a/descrip.mms b/descrip.mms new file mode 100644 index 0000000..6f92ee2 --- /dev/null +++ b/descrip.mms @@ -0,0 +1,1202 @@ +# DESCRIP.MMS +# Written By: Robert Alan Byer / byer@mail.ourservers.net +# Modified By: Mark Pizzolato / mark@infocomm.com +# Norman Lastovica / norman.lastovica@oracle.com +# +# This MMS/MMK build script is used to compile the various simulators in +# the SIMH package for OpenVMS using DEC C v6.0-001(AXP), v6.5-001(AXP), +# HP C V7.2-001 (IA64) and v6.4-005(VAX). +# +# Notes: On VAX, the PDP-10 and Eclipse simulators will not be built +# due to the fact that INT64 is required for that simulator. +# +# This build script will accept the following build options. +# +# ALL Just Build "Everything". +# ALTAIR Just Build The MITS Altair. +# ALTAIRZ80 Just Build The MITS Altair Z80. +# ECLIPSE Just Build The Data General Eclipse. +# GRI Just Build The GRI Corporation GRI-909. +# LGP Just Build The Royal-McBee LGP-30. +# H316 Just Build The Honewell 316/516. +# HP2100 Just Build The Hewlett-Packard HP-2100. +# I1401 Just Build The IBM 1401. +# I1620 Just Build The IBM 1620. +# IBM1130 Just Build The IBM 1130. +# ID16 Just Build The Interdata 16-bit CPU. +# ID32 Just Build The Interdata 32-bit CPU. +# NOVA Just Build The Data General Nova. +# PDP1 Just Build The DEC PDP-1. +# PDP4 Just Build The DEC PDP-4. +# PDP7 Just Build The DEC PDP-7. +# PDP8 Just Build The DEC PDP-8. +# PDP9 Just Build The DEC PDP-9. +# PDP10 Just Build The DEC PDP-10. +# PDP11 Just Build The DEC PDP-11. +# PDP15 Just Build The DEC PDP-15. +# S3 Just Build The IBM System 3. +# SDS Just Build The SDS 940. +# VAX Just Build The DEC VAX. +# VAX780 Just Build The DEC VAX780. +# CLEAN Will Clean Files Back To Base Kit. +# +# To build with debugging enabled (which will also enable traceback +# information) use.. +# +# MMK/MACRO=(DEBUG=1) +# +# This will produce an executable named {Simulator}-{I64|VAX|AXP}-DBG.EXE +# + +# Let's See If We Are Going To Build With DEBUG Enabled. Always compile +# /DEBUG so that the traceback and debug information is always available +# in the object files. + +CC_DEBUG = /DEBUG + +.IFDEF DEBUG +LINK_DEBUG = /DEBUG/TRACEBACK +CC_OPTIMIZE = /NOOPTIMIZE + +.IFDEF MMSALPHA +ALPHA_OR_IA64 = 1 +CC_FLAGS = /PREF=ALL +ARCH = AXP-DBG +CC_DEFS = "_LARGEFILE" +.ENDIF + +.IFDEF MMSIA64 +ALPHA_OR_IA64 = 1 +CC_FLAGS = /PREF=ALL +ARCH = I64-DBG +CC_DEFS = "_LARGEFILE" +.ENDIF + +.IFDEF MMSVAX +ALPHA_OR_IA64 = 0 +CC_FLAGS = $(CC_FLAGS) +ARCH = VAX-DBG +CC_DEFS = "__VAX" +.ENDIF + +.ELSE +LINK_DEBUG = /NODEBUG/NOTRACEBACK + +.IFDEF MMSALPHA +ALPHA_OR_IA64 = 1 +CC_OPTIMIZE = /OPT=(LEV=5)/ARCH=HOST +CC_FLAGS = /PREF=ALL +ARCH = AXP +CC_DEFS = "_LARGEFILE" +LINK_SECTION_BINDING = /SECTION_BINDING +.ENDIF + +.IFDEF MMSIA64 +ALPHA_OR_IA64 = 1 +CC_OPTIMIZE = /OPT=(LEV=5) +CC_FLAGS = /PREF=ALL +ARCH = I64 +CC_DEFS = "_LARGEFILE" +.ENDIF + +.IFDEF MMSVAX +ALPHA_OR_IA64 = 0 +CC_OPTIMIZE = /OPTIMIZE +CC_FLAGS = $(CC_FLAGS) +ARCH = VAX +CC_DEFS = "__VAX" +.ENDIF + +.ENDIF + +# Define Our Compiler Flags & Define The Compile Command +OUR_CC_FLAGS = $(CC_FLAGS)$(CC_DEBUG)$(CC_OPTIMIZE) \ + /NEST=PRIMARY/NAME=(AS_IS,SHORT) +CC = CC/DECC$(OUR_CC_FLAGS) + +# Define The BIN Directory Where The Executables Will Go. +# Define Our Library Directory. +# Define The platform specific Build Directory Where The Objects Will Go. +# +BIN_DIR = SYS$DISK:[.BIN] +LIB_DIR = SYS$DISK:[.LIB] +BLD_DIR = SYS$DISK:[.LIB.BLD-$(ARCH)] + +# Check To Make Sure We Have SYS$DISK:[.BIN] & SYS$DISK:[.LIB] Directory. +# +.FIRST + @ IF (F$SEARCH("SYS$DISK:[]BIN.DIR").EQS."") THEN CREATE/DIRECTORY $(BIN_DIR) + @ IF (F$SEARCH("SYS$DISK:[]LIB.DIR").EQS."") THEN CREATE/DIRECTORY $(LIB_DIR) + @ IF (F$SEARCH("SYS$DISK:[.LIB]BLD-$(ARCH).DIR").EQS."") THEN CREATE/DIRECTORY $(BLD_DIR) + @ IF (F$SEARCH("$(BLD_DIR)*.*").NES."") THEN DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.*;* + @ IF "".NES."''CC'" THEN DELETE/SYMBOL/GLOBAL CC + +# Core SIMH File Definitions. +# +SIMH_DIR = SYS$DISK:[] +SIMH_LIB = $(LIB_DIR)SIMH-$(ARCH).OLB +SIMH_SOURCE = $(SIMH_DIR)SIM_CONSOLE.C,$(SIMH_DIR)SIM_SOCK.C,\ + $(SIMH_DIR)SIM_TMXR.C,$(SIMH_DIR)SIM_ETHER.C,\ + $(SIMH_DIR)SIM_TAPE.C,$(SIMH_DIR)SIM_FIO.C,\ + $(SIMH_DIR)SIM_TIMER.C + +# VMS PCAP File Definitions. +# +PCAP_DIR = SYS$DISK:[.PCAP-VMS.PCAP-VCI] +PCAP_LIB = $(LIB_DIR)PCAP-$(ARCH).OLB +PCAP_SOURCE = \ + $(PCAP_DIR)PCAPVCI.C,$(PCAP_DIR)VCMUTIL.C,\ + $(PCAP_DIR)BPF_DUMP.C,$(PCAP_DIR)BPF_FILTER.C,\ + $(PCAP_DIR)BPF_IMAGE.C,$(PCAP_DIR)ETHERENT.C,\ + $(PCAP_DIR)FAD-GIFC.C,$(PCAP_DIR)GENCODE.C,\ + $(PCAP_DIR)GRAMMAR.C,$(PCAP_DIR)INET.C,\ + $(PCAP_DIR)NAMETOADDR.C,$(PCAP_DIR)OPTIMIZE.C,\ + $(PCAP_DIR)PCAP.C,$(PCAP_DIR)SAVEFILE.C,\ + $(PCAP_DIR)SCANNER.C,$(PCAP_DIR)SNPRINTF.C,\ + $(PCAP_DIR)PCAP-VMS.C +PCAP_VCMDIR = SYS$DISK:[.PCAP-VMS.PCAPVCM] +PCAP_VCM_SOURCES = $(PCAP_VCMDIR)PCAPVCM.C,$(PCAP_VCMDIR)PCAPVCM_INIT.MAR,\ + $(PCAP_VCMDIR)VCI_JACKET.MAR,$(PCAP_VCMDIR)VCMUTIL.C +PCAP_VCI = SYS$COMMON:[SYS$LDR]PCAPVCM.EXE + +# PCAP is not available on OpenVMS VAX or IA64 right now +# +.IFDEF MMSALPHA +PCAP_EXECLET = $(PCAP_VCI) +PCAP_INC = ,$(PCAP_DIR) +PCAP_LIBD = $(PCAP_LIB) +PCAP_LIBR = ,$(PCAP_LIB)/LIB/SYSEXE +PCAP_DEFS = ,"USE_NETWORK=1" +PCAP_SIMH_INC = /INCL=($(PCAP_DIR)) +.ENDIF + +# MITS Altair Simulator Definitions. +# +ALTAIR_DIR = SYS$DISK:[.ALTAIR] +ALTAIR_LIB = $(LIB_DIR)ALTAIR-$(ARCH).OLB +ALTAIR_SOURCE = $(ALTAIR_DIR)ALTAIR_SIO.C,$(ALTAIR_DIR)ALTAIR_CPU.C,\ + $(ALTAIR_DIR)ALTAIR_DSK.C,$(ALTAIR_DIR)ALTAIR_SYS.C +ALTAIR_OPTIONS = /INCL=($(SIMH_DIR),$(ALTAIR_DIR))/DEF=($(CC_DEFS)) + +# +# MITS Altair Z80 Simulator Definitions. +# +ALTAIRZ80_DIR = SYS$DISK:[.ALTAIRZ80] +ALTAIRZ80_LIB = $(LIB_DIR)ALTAIRZ80-$(ARCH).OLB +ALTAIRZ80_SOURCE = $(ALTAIRZ80_DIR)/ALTAIRZ80_CPU.C,$(ALTAIRZ80_DIR)/ALTAIRZ80_CPU_NOMMU.C,\ + $(ALTAIRZ80_DIR)/ALTAIRZ80_DSK.C,$(ALTAIRZ80_DIR)/DISASM.C,\ + $(ALTAIRZ80_DIR)/ALTAIRZ80_SIO.C,$(ALTAIRZ80_DIR)/ALTAIRZ80_SYS.C,\ + $(ALTAIRZ80_DIR)/ALTAIRZ80_HDSK.C,$(ALTAIRZ80_DIR)/ALTAIRZ80_NET.C,\ + $(ALTAIRZ80_DIR)/FLASHWRITER2.C,$(ALTAIRZ80_DIR)/I86_DECODE.C,\ + $(ALTAIRZ80_DIR)/I86_OPS.C,$(ALTAIRZ80_DIR)/I86_PRIM_OPS.C,\ + $(ALTAIRZ80_DIR)/I8272.C,$(ALTAIRZ80_DIR)/INSNSA.C,$(ALTAIRZ80_DIR)/INSNSD.C,\ + $(ALTAIRZ80_DIR)/MFDC.C,$(ALTAIRZ80_DIR)/N8VEM.C,$(ALTAIRZ80_DIR)/VFDHD.C,\ + $(ALTAIRZ80_DIR)/S100_DISK1A.C,$(ALTAIRZ80_DIR)/S100_DISK2.C,\ + $(ALTAIRZ80_DIR)/S100_FIF.C,$(ALTAIRZ80_DIR)/S100_MDRIVEH.C,\ + $(ALTAIRZ80_DIR)/S100_MDSAD.C,$(ALTAIRZ80_DIR)/S100_SELCHAN.C,\ + $(ALTAIRZ80_DIR)/S100_SS1.C,$(ALTAIRZ80_DIR)/S100_64FDC.C,\ + $(ALTAIRZ80_DIR)/S100_SCP300F.C,$(ALTAIRZ80_DIR)/SIM_IMD.C,\ + $(ALTAIRZ80_DIR)/WD179X.C +ALTAIRZ80_OPTIONS = /INCL=($(SIMH_DIR),$(ALTAIRZ80_DIR))/DEF=($(CC_DEFS)) + +# +# Data General Nova Simulator Definitions. +# +NOVA_DIR = SYS$DISK:[.NOVA] +NOVA_LIB = $(LIB_DIR)NOVA-$(ARCH).OLB +NOVA_SOURCE = $(NOVA_DIR)NOVA_SYS.C,$(NOVA_DIR)NOVA_CPU.C,\ + $(NOVA_DIR)NOVA_DKP.C,$(NOVA_DIR)NOVA_DSK.C,\ + $(NOVA_DIR)NOVA_LP.C,$(NOVA_DIR)NOVA_MTA.C,\ + $(NOVA_DIR)NOVA_PLT.C,$(NOVA_DIR)NOVA_PT.C,\ + $(NOVA_DIR)NOVA_CLK.C,$(NOVA_DIR)NOVA_TT.C,\ + $(NOVA_DIR)NOVA_TT1.C,$(NOVA_DIR)NOVA_QTY.C +NOVA_OPTIONS = /INCL=($(SIMH_DIR),$(NOVA_DIR))/DEF=($(CC_DEFS)) + +# +# Data General Eclipse Simulator Definitions. +# +ECLIPSE_LIB = $(LIB_DIR)ECLIPSE-$(ARCH).OLB +ECLIPSE_SOURCE = $(NOVA_DIR)ECLIPSE_CPU.C,$(NOVA_DIR)ECLIPSE_TT.C,\ + $(NOVA_DIR)NOVA_SYS.C,$(NOVA_DIR)NOVA_DKP.C,\ + $(NOVA_DIR)NOVA_DSK.C,$(NOVA_DIR)NOVA_LP.C,\ + $(NOVA_DIR)NOVA_MTA.C,$(NOVA_DIR)NOVA_PLT.C,\ + $(NOVA_DIR)NOVA_PT.C,$(NOVA_DIR)NOVA_CLK.C,\ + $(NOVA_DIR)NOVA_TT1.C,$(NOVA_DIR)NOVA_QTY.C +ECLIPSE_OPTIONS = /INCL=($(SIMH_DIR),$(NOVA_DIR))\ + /DEF=($(CC_DEFS),"USE_INT64=1","ECLIPSE=1") + +# +# GRI Corporation GRI-909 Simulator Definitions. +# +GRI_DIR = SYS$DISK:[.GRI] +GRI_LIB = $(LIB_DIR)GRI-$(ARCH).OLB +GRI_SOURCE = $(GRI_DIR)GRI_CPU.C,$(GRI_DIR)GRI_STDDEV.C,$(GRI_DIR)GRI_SYS.C +GRI_OPTIONS = /INCL=($(SIMH_DIR),$(GRI_DIR))/DEF=($(CC_DEFS)) + +# +# Royal-McBee LGP-30 Simulator Definitions. +# +LGP_DIR = SYS$DISK:[.LGP] +LGP_LIB = $(LIB_DIR)LGP-$(ARCH).OLB +LGP_SOURCE = $(LGP_DIR)LGP_CPU.C,$(LGP_DIR)LGP_STDDEV.C,$(LGP_DIR)LGP_SYS.C +LGP_OPTIONS = /INCL=($(SIMH_DIR),$(LGP_DIR))/DEF=($(CC_DEFS)) + +# +# Honeywell 316/516 Simulator Definitions. +# +H316_DIR = SYS$DISK:[.H316] +H316_LIB = $(LIB_DIR)H316-$(ARCH).OLB +H316_SOURCE = $(H316_DIR)H316_STDDEV.C,$(H316_DIR)H316_LP.C,\ + $(H316_DIR)H316_CPU.C,$(H316_DIR)H316_SYS.C,\ + $(H316_DIR)H316_FHD.C,$(H316_DIR)H316_MT.C,\ + $(H316_DIR)H316_DP.C +H316_OPTIONS = /INCL=($(SIMH_DIR),$(H316_DIR))/DEF=($(CC_DEFS)) + +# +# Hewlett-Packard HP-2100 Simulator Definitions. +# +HP2100_DIR = SYS$DISK:[.HP2100] +HP2100_LIB = $(LIB_DIR)HP2100-$(ARCH).OLB +HP2100_SOURCE = $(HP2100_DIR)HP2100_STDDEV.C,$(HP2100_DIR)HP2100_DP.C,\ + $(HP2100_DIR)HP2100_DQ.C,$(HP2100_DIR)HP2100_DR.C,\ + $(HP2100_DIR)HP2100_LPS.C,$(HP2100_DIR)HP2100_MS.C,\ + $(HP2100_DIR)HP2100_MT.C,$(HP2100_DIR)HP2100_MUX.C,\ + $(HP2100_DIR)HP2100_CPU.C,$(HP2100_DIR)HP2100_FP.C,\ + $(HP2100_DIR)HP2100_SYS.C,$(HP2100_DIR)HP2100_LPT.C,\ + $(HP2100_DIR)HP2100_IPL.C,$(HP2100_DIR)HP2100_DS.C,\ + $(HP2100_DIR)HP2100_CPU0.C,$(HP2100_DIR)HP2100_CPU1.C,\ + $(HP2100_DIR)HP2100_CPU2.C,$(HP2100_DIR)HP2100_CPU3.C,\ + $(HP2100_DIR)HP2100_CPU4.C,$(HP2100_DIR)HP2100_CPU5.C,\ + $(HP2100_DIR)HP2100_CPU6.C,$(HP2100_DIR)HP2100_CPU7.C,\ + $(HP2100_DIR)HP2100_FP1.C,$(HP2100_DIR)HP2100_BACI.C +.IF ALPHA_OR_IA64 +HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))\ + /DEF=($(CC_DEFS),"HAVE_INT64=1") +.ELSE +HP2100_OPTIONS = /INCL=($(SIMH_DIR),$(HP2100_DIR))/DEF=($(CC_DEFS)) +.ENDIF + +# +# Interdata 16-bit CPU. +# +ID16_DIR = SYS$DISK:[.INTERDATA] +ID16_LIB = $(LIB_DIR)ID16-$(ARCH).OLB +ID16_SOURCE = $(ID16_DIR)ID16_CPU.C,$(ID16_DIR)ID16_SYS.C,$(ID16_DIR)ID_DP.C,\ + $(ID16_DIR)ID_FD.C,$(ID16_DIR)ID_FP.C,$(ID16_DIR)ID_IDC.C,\ + $(ID16_DIR)ID_IO.C,$(ID16_DIR)ID_LP.C,$(ID16_DIR)ID_MT.C,\ + $(ID16_DIR)ID_PAS.C,$(ID16_DIR)ID_PT.C,$(ID16_DIR)ID_TT.C,\ + $(ID16_DIR)ID_UVC.C,$(ID16_DIR)ID16_DBOOT.C,$(ID16_DIR)ID_TTP.C +ID16_OPTIONS = /INCL=($(SIMH_DIR),$(ID16_DIR))/DEF=($(CC_DEFS)) + +# +# Interdata 32-bit CPU. +# +ID32_DIR = SYS$DISK:[.INTERDATA] +ID32_LIB = $(LIB_DIR)ID32-$(ARCH).OLB +ID32_SOURCE = $(ID32_DIR)ID32_CPU.C,$(ID32_DIR)ID32_SYS.C,$(ID32_DIR)ID_DP.C,\ + $(ID32_DIR)ID_FD.C,$(ID32_DIR)ID_FP.C,$(ID32_DIR)ID_IDC.C,\ + $(ID32_DIR)ID_IO.C,$(ID32_DIR)ID_LP.C,$(ID32_DIR)ID_MT.C,\ + $(ID32_DIR)ID_PAS.C,$(ID32_DIR)ID_PT.C,$(ID32_DIR)ID_TT.C,\ + $(ID32_DIR)ID_UVC.C,$(ID32_DIR)ID32_DBOOT.C,$(ID32_DIR)ID_TTP.C +ID32_OPTIONS = /INCL=($(SIMH_DIR),$(ID32_DIR))/DEF=($(CC_DEFS)) + +# +# IBM 1130 Simulator Definitions. +# +IBM1130_DIR = SYS$DISK:[.IBM1130] +IBM1130_LIB = $(LIB_DIR)IBM1130-$(ARCH).OLB +IBM1130_SOURCE = $(IBM1130_DIR)IBM1130_CPU.C,$(IBM1130_DIR)IBM1130_CR.C,\ + $(IBM1130_DIR)IBM1130_DISK.C,$(IBM1130_DIR)IBM1130_STDDEV.C,\ + $(IBM1130_DIR)IBM1130_SYS.C,$(IBM1130_DIR)IBM1130_GDU.C,\ + $(IBM1130_DIR)IBM1130_GUI.C,$(IBM1130_DIR)IBM1130_PRT.C,\ + $(IBM1130_DIR)IBM1130_FMT.C,$(IBM1130_DIR)IBM1130_PTRP.C,\ + $(IBM1130_DIR)IBM1130_PLOT.C,$(IBM1130_DIR)IBM1130_SCA.C,\ + $(IBM1130_DIR)IBM1130_T2741.C +IBM1130_OPTIONS = /INCL=($(SIMH_DIR),$(IBM1130_DIR))/DEF=($(CC_DEFS)) + +# +# IBM 1401 Simulator Definitions. +# +I1401_DIR = SYS$DISK:[.I1401] +I1401_LIB = $(LIB_DIR)I1401-$(ARCH).OLB +I1401_SOURCE = $(I1401_DIR)I1401_LP.C,$(I1401_DIR)I1401_CPU.C,\ + $(I1401_DIR)I1401_IQ.C,$(I1401_DIR)I1401_CD.C,\ + $(I1401_DIR)I1401_MT.C,$(I1401_DIR)I1401_DP.C,\ + $(I1401_DIR)I1401_SYS.C +I1401_OPTIONS = /INCL=($(SIMH_DIR),$(I1401_DIR))/DEF=($(CC_DEFS)) + + +# +# IBM 1620 Simulators Definitions. +# +I1620_DIR = SYS$DISK:[.I1620] +I1620_LIB = $(LIB_DIR)I1620-$(ARCH).OLB +I1620_SOURCE = $(I1620_DIR)I1620_CD.C,$(I1620_DIR)I1620_DP.C,\ + $(I1620_DIR)I1620_PT.C,$(I1620_DIR)I1620_TTY.C,\ + $(I1620_DIR)I1620_CPU.C,$(I1620_DIR)I1620_LP.C,\ + $(I1620_DIR)I1620_FP.C,$(I1620_DIR)I1620_SYS.C +I1620_OPTIONS = /INCL=($(SIMH_DIR),$(I1620_DIR))/DEF=($(CC_DEFS)) + +# +# PDP-1 Simulator Definitions. +# +PDP1_DIR = SYS$DISK:[.PDP1] +PDP1_LIB = $(LIB_DIR)PDP1-$(ARCH).OLB +PDP1_SOURCE = $(PDP1_DIR)PDP1_LP.C,$(PDP1_DIR)PDP1_CPU.C,\ + $(PDP1_DIR)PDP1_STDDEV.C,$(PDP1_DIR)PDP1_SYS.C,\ + $(PDP1_DIR)PDP1_DT.C,$(PDP1_DIR)PDP1_DRM.C,\ + $(PDP1_DIR)PDP1_CLK.C,$(PDP1_DIR)PDP1_DCS.C +PDP1_OPTIONS = /INCL=($(SIMH_DIR),$(PDP1_DIR))/DEF=($(CC_DEFS)) + +# +# Digital Equipment PDP-8 Simulator Definitions. +# +PDP8_DIR = SYS$DISK:[.PDP8] +PDP8_LIB = $(LIB_DIR)PDP8-$(ARCH).OLB +PDP8_SOURCE = $(PDP8_DIR)PDP8_CPU.C,$(PDP8_DIR)PDP8_CLK.C,\ + $(PDP8_DIR)PDP8_DF.C,$(PDP8_DIR)PDP8_DT.C,\ + $(PDP8_DIR)PDP8_LP.C,$(PDP8_DIR)PDP8_MT.C,\ + $(PDP8_DIR)PDP8_PT.C,$(PDP8_DIR)PDP8_RF.C,\ + $(PDP8_DIR)PDP8_RK.C,$(PDP8_DIR)PDP8_RX.C,\ + $(PDP8_DIR)PDP8_SYS.C,$(PDP8_DIR)PDP8_TT.C,\ + $(PDP8_DIR)PDP8_TTX.C,$(PDP8_DIR)PDP8_RL.C,\ + $(PDP8_DIR)PDP8_TSC.C,$(PDP8_DIR)PDP8_TD.C,\ + $(PDP8_DIR)PDP8_CT.C +PDP8_OPTIONS = /INCL=($(SIMH_DIR),$(PDP8_DIR))/DEF=($(CC_DEFS)) + +# +# Digital Equipment PDP-4, PDP-7, PDP-9 And PDP-15 Simulator Definitions. +# +PDP18B_DIR = SYS$DISK:[.PDP18B] +PDP4_LIB = $(LIB_DIR)PDP4-$(ARCH).OLB +PDP7_LIB = $(LIB_DIR)PDP7-$(ARCH).OLB +PDP9_LIB = $(LIB_DIR)PDP9-$(ARCH).OLB +PDP15_LIB = $(LIB_DIR)PDP15-$(ARCH).OLB +PDP18B_SOURCE = $(PDP18B_DIR)PDP18B_DT.C,$(PDP18B_DIR)PDP18B_DRM.C,\ + $(PDP18B_DIR)PDP18B_CPU.C,$(PDP18B_DIR)PDP18B_LP.C,\ + $(PDP18B_DIR)PDP18B_MT.C,$(PDP18B_DIR)PDP18B_RF.C,\ + $(PDP18B_DIR)PDP18B_RP.C,$(PDP18B_DIR)PDP18B_STDDEV.C,\ + $(PDP18B_DIR)PDP18B_SYS.C,$(PDP18B_DIR)PDP18B_TT1.C,\ + $(PDP18B_DIR)PDP18B_RB.C,$(PDP18B_DIR)PDP18B_FPP.C +PDP4_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP4=1") +PDP7_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP7=1") +PDP9_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP9=1") +PDP15_OPTIONS = /INCL=($(SIMH_DIR),$(PDP18B_DIR))/DEF=($(CC_DEFS),"PDP15=1") + +# +# Digital Equipment PDP-11 Simulator Definitions. +# +PDP11_DIR = SYS$DISK:[.PDP11] +PDP11_LIB1 = $(LIB_DIR)PDP11L1-$(ARCH).OLB +PDP11_SOURCE1 = $(PDP11_DIR)PDP11_FP.C,$(PDP11_DIR)PDP11_CPU.C,\ + $(PDP11_DIR)PDP11_DZ.C,$(PDP11_DIR)PDP11_CIS.C,\ + $(PDP11_DIR)PDP11_LP.C,$(PDP11_DIR)PDP11_RK.C,\ + $(PDP11_DIR)PDP11_RL.C,$(PDP11_DIR)PDP11_RP.C,\ + $(PDP11_DIR)PDP11_RX.C,$(PDP11_DIR)PDP11_STDDEV.C,\ + $(PDP11_DIR)PDP11_SYS.C,$(PDP11_DIR)PDP11_TC.C, \ + $(PDP11_DIR)PDP11_CPUMOD.C,$(PDP11_DIR)PDP11_CR.C,\ + $(PDP11_DIR)PDP11_TA.C +PDP11_LIB2 = $(LIB_DIR)PDP11L2-$(ARCH).OLB +PDP11_SOURCE2 = $(PDP11_DIR)PDP11_TM.C,$(PDP11_DIR)PDP11_TS.C,\ + $(PDP11_DIR)PDP11_IO.C,$(PDP11_DIR)PDP11_RQ.C,\ + $(PDP11_DIR)PDP11_TQ.C,$(PDP11_DIR)PDP11_PCLK.C,\ + $(PDP11_DIR)PDP11_RY.C,$(PDP11_DIR)PDP11_PT.C,\ + $(PDP11_DIR)PDP11_HK.C,$(PDP11_DIR)PDP11_XQ.C,\ + $(PDP11_DIR)PDP11_VH.C,$(PDP11_DIR)PDP11_RH.C,\ + $(PDP11_DIR)PDP11_XU.C,$(PDP11_DIR)PDP11_TU.C,\ + $(PDP11_DIR)PDP11_DL.C,$(PDP11_DIR)PDP11_RF.C, \ + $(PDP11_DIR)PDP11_RC.C,$(PDP11_DIR)PDP11_KG.C,\ + $(PDP11_DIR)PDP11_KE.C,$(PDP11_DIR)PDP11_DC.C +PDP11_OPTIONS = /INCL=($(SIMH_DIR),$(PDP11_DIR)$(PCAP_INC))\ + /DEF=($(CC_DEFS),"VM_PDP11=1"$(PCAP_DEFS)) + +# +# Digital Equipment PDP-10 Simulator Definitions. +# +PDP10_DIR = SYS$DISK:[.PDP10] +PDP10_LIB = $(LIB_DIR)PDP10-$(ARCH).OLB +PDP10_SOURCE = $(PDP10_DIR)PDP10_FE.C,\ + $(PDP10_DIR)PDP10_CPU.C,$(PDP10_DIR)PDP10_KSIO.C,\ + $(PDP10_DIR)PDP10_LP20.C,$(PDP10_DIR)PDP10_MDFP.C,\ + $(PDP10_DIR)PDP10_PAG.C,$(PDP10_DIR)PDP10_XTND.C,\ + $(PDP10_DIR)PDP10_RP.C,$(PDP10_DIR)PDP10_SYS.C,\ + $(PDP10_DIR)PDP10_TIM.C,$(PDP10_DIR)PDP10_TU.C,\ + $(PDP11_DIR)PDP11_PT.C,$(PDP11_DIR)PDP11_DZ.C,\ + $(PDP11_DIR)PDP11_RY.C,$(PDP11_DIR)PDP11_XU.C,\ + $(PDP11_DIR)PDP11_CR.C +PDP10_OPTIONS = /INCL=($(SIMH_DIR),$(PDP10_DIR),$(PDP11_DIR))\ + /DEF=($(CC_DEFS),"USE_INT64=1","VM_PDP10=1"$(PCAP_DEFS)) + +# +# IBM System 3 Simulator Definitions. +# +S3_DIR = SYS$DISK:[.S3] +S3_LIB = $(LIB_DIR)S3-$(ARCH).OLB +S3_SOURCE = $(S3_DIR)S3_CD.C,$(S3_DIR)S3_CPU.C,$(S3_DIR)S3_DISK.C,\ + $(S3_DIR)S3_LP.C,$(S3_DIR)S3_PKB.C,$(S3_DIR)S3_SYS.C +S3_OPTIONS = /INCL=($(SIMH_DIR),$(S3_DIR))/DEF=($(CC_DEFS)) + +# +# SDS 940 +# +SDS_DIR = SYS$DISK:[.SDS] +SDS_LIB = $(LIB_DIR)SDS-$(ARCH).OLB +SDS_SOURCE = $(SDS_DIR)SDS_CPU.C,$(SDS_DIR)SDS_DRM.C,$(SDS_DIR)SDS_DSK.C,\ + $(SDS_DIR)SDS_IO.C,$(SDS_DIR)SDS_LP.C,$(SDS_DIR)SDS_MT.C,\ + $(SDS_DIR)SDS_MUX.C,$(SDS_DIR)SDS_RAD.C,$(SDS_DIR)SDS_STDDEV.C,\ + $(SDS_DIR)SDS_SYS.C +SDS_OPTIONS = /INCL=($(SIMH_DIR),$(SDS_DIR))/DEF=($(CC_DEFS)) + +# +# Digital Equipment VAX Simulator Definitions. +# +VAX_DIR = SYS$DISK:[.VAX] +VAX_LIB = $(LIB_DIR)VAX-$(ARCH).OLB +VAX_SOURCE = $(VAX_DIR)VAX_CIS.C,$(VAX_DIR)VAX_CMODE.C,\ + $(VAX_DIR)VAX_CPU.C,$(VAX_DIR)VAX_CPU1.C,\ + $(VAX_DIR)VAX_FPA.C,$(VAX_DIR)VAX_MMU.C,\ + $(VAX_DIR)VAX_OCTA.C,$(VAX_DIR)VAX_SYS.C,\ + $(VAX_DIR)VAX_SYSCM.C,$(VAX_DIR)VAX_SYSDEV.C,\ + $(VAX_DIR)VAX_SYSLIST.C,$(VAX_DIR)VAX_IO.C,\ + $(VAX_DIR)VAX_STDDEV.C,\ + $(PDP11_DIR)PDP11_RL.C,$(PDP11_DIR)PDP11_RQ.C,\ + $(PDP11_DIR)PDP11_TS.C,$(PDP11_DIR)PDP11_DZ.C,\ + $(PDP11_DIR)PDP11_LP.C,$(PDP11_DIR)PDP11_TQ.C,\ + $(PDP11_DIR)PDP11_XQ.C,$(PDP11_DIR)PDP11_CR.C,\ + $(PDP11_DIR)PDP11_RY.C,$(PDP11_DIR)PDP11_VH.C +VAX_OPTIONS = /INCL=($(SIMH_DIR),$(VAX_DIR),$(PDP11_DIR)$(PCAP_INC))\ + /DEF=($(CC_DEFS),"VM_VAX=1"$(PCAP_DEFS)) + +# Digital Equipment VAX780 Simulator Definitions. +# +VAX780_DIR = SYS$DISK:[.VAX] +VAX780_LIB1 = $(LIB_DIR)VAX780L1-$(ARCH).OLB +VAX780_SOURCE1 = $(VAX780_DIR)VAX_CPU.C,$(VAX780_DIR)VAX_CPU1.C,\ + $(VAX780_DIR)VAX_FPA.C,$(VAX780_DIR)VAX_CIS.C,\ + $(VAX780_DIR)VAX_OCTA.C,$(VAX780_DIR)VAX_CMODE.C,\ + $(VAX780_DIR)VAX_MMU.C,$(VAX780_DIR)VAX_SYS.C,\ + $(VAX780_DIR)VAX_SYSCM.C,$(VAX780_DIR)VAX780_STDDEV.C,\ + $(VAX780_DIR)VAX780_SBI.C,$(VAX780_DIR)VAX780_MEM.C,\ + $(VAX780_DIR)VAX780_UBA.C,$(VAX780_DIR)VAX780_MBA.C,\ + $(VAX780_DIR)VAX780_FLOAD.C,$(VAX780_DIR)VAX780_SYSLIST.C +VAX780_LIB2 = $(LIB_DIR)VAX780L2-$(ARCH).OLB +VAX780_SOURCE2 = $(PDP11_DIR)PDP11_RL.C,$(PDP11_DIR)PDP11_RQ.C,\ + $(PDP11_DIR)PDP11_TS.C,$(PDP11_DIR)PDP11_DZ.C,\ + $(PDP11_DIR)PDP11_LP.C,$(PDP11_DIR)PDP11_TQ.C,\ + $(PDP11_DIR)PDP11_XU.C,$(PDP11_DIR)PDP11_RY.C,\ + $(PDP11_DIR)PDP11_CR.C,$(PDP11_DIR)PDP11_RP.C,\ + $(PDP11_DIR)PDP11_TU.C,$(PDP11_DIR)PDP11_HK.C +VAX780_OPTIONS = /INCL=($(SIMH_DIR),$(VAX780_DIR),$(PDP11_DIR)$(PCAP_INC))\ + /DEF=($(CC_DEFS),"VM_VAX=1"$(PCAP_DEFS),"VAX_780=1") + +# IBM 7094 Simulator Definitions. +# +I7094_DIR = SYS$DISK:[.I7094] +I7094_LIB = $(LIB_DIR)I7094-$(ARCH).OLB +I7094_SOURCE = $(I7094_DIR)I7094_CPU.C,$(I7094_DIR)I7094_CPU1.C,\ + $(I7094_DIR)I7094_IO.C,$(I7094_DIR)I7094_CD.C,\ + $(I7094_DIR)I7094_CLK.C,$(I7094_DIR)I7094_COM.C,\ + $(I7094_DIR)I7094_DRM.C,$(I7094_DIR)I7094_DSK.C,\ + $(I7094_DIR)I7094_SYS.C,$(I7094_DIR)I7094_LP.C,\ + $(I7094_DIR)I7094_MT.C,$(I7094_DIR)I7094_BINLOADER.C +I7094_OPTIONS = /INCL=($(SIMH_DIR),$(I7094_DIR))/DEF=($(CC_DEFS)) + +# If we're not a VAX, Build Everything +# +.IF ALPHA_OR_IA64 +ALL : ALTAIR ALTAIRZ80 ECLIPSE GRI LGP H316 HP2100 I1401 I1620 IBM1130 ID16 \ + ID32 NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP10 PDP11 PDP15 S3 VAX VAX780 SDS \ + I7094 + @CONTINUE +.ELSE +# +# Else We Are On VAX And Build Everything EXCEPT the 64b simulators +# +ALL : ALTAIR ALTAIRZ80 GRI H316 HP2100 I1401 I1620 IBM1130 ID16 ID32 \ + NOVA PDP1 PDP4 PDP7 PDP8 PDP9 PDP11 PDP15 S3 VAX VAX780 SDS + @CONTINUE +.ENDIF + +CLEAN : + $! + $! Clean out all targets and building Remnants + $! + $ IF (F$SEARCH("$(BIN_DIR)*.EXE;*").NES."") THEN - + DELETE/NOLOG/NOCONFIRM $(BIN_DIR)*.EXE;* + $ IF (F$SEARCH("$(LIB_DIR)*.OLB;*").NES."") THEN - + DELETE/NOLOG/NOCONFIRM $(LIB_DIR)*.OLB;* + $ IF (F$SEARCH("SYS$DISK:[...]*.OBJ;*").NES."") THEN - + DELETE/NOLOG/NOCONFIRM SYS$DISK:[...]*.OBJ;* + $ IF (F$SEARCH("SYS$DISK:[...]*.LIS;*").NES."") THEN - + DELETE/NOLOG/NOCONFIRM SYS$DISK:[...]*.LIS;* + $ IF (F$SEARCH("SYS$DISK:[...]*.MAP;*").NES."") THEN - + DELETE/NOLOG/NOCONFIRM SYS$DISK:[...]*.MAP;* + +# +# Build The Libraries. +# +$(SIMH_LIB) : $(SIMH_SOURCE) + $! + $! Building The $(SIMH_LIB) Library. + $! + $ $(CC)/DEF=($(CC_DEFS)$(PCAP_DEFS))$(PCAP_SIMH_INC) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(ALTAIR_LIB) : $(ALTAIR_SOURCE) + $! + $! Building The $(ALTAIR_LIB) Library. + $! + $ $(CC)$(ALTAIR_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(ALTAIRZ80_LIB) : $(ALTAIRZ80_SOURCE) + $! + $! Building The $(ALTAIRZ80_LIB) Library. + $! + $ $(CC)$(ALTAIRZ80_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +# +# If Not On VAX, Build The Eclipse Library. +# +.IF ALPHA_OR_IA64 +$(ECLIPSE_LIB) : $(ECLIPSE_SOURCE) + $! + $! Building The $(ECLIPSE_LIB) Library. + $! + $ $(CC)$(ECLIPSE_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.ELSE +# +# We Are On VAX And Due To The Use of INT64 We Can't Build It. +# +$(ECLIPSE_LIB) : + $! + $! Due To The Use Of INT64 We Can't Build The + $! $(LIB_DIR)ECLIPSE-$(ARCH).OLB Library On VAX. + $! +.ENDIF + +$(GRI_LIB) : $(GRI_SOURCE) + $! + $! Building The $(GRI_LIB) Library. + $! + $ $(CC)$(GRI_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(LGP_LIB) : $(LGP_SOURCE) + $! + $! Building The $(LGP_LIB) Library. + $! + $ $(CC)$(LGP_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(H316_LIB) : $(H316_SOURCE) + $! + $! Building The $(H316_LIB) Library. + $! + $ $(CC)$(H316_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(HP2100_LIB) : $(HP2100_SOURCE) + $! + $! Building The $(HP2100_LIB) Library. + $! + $ $(CC)$(HP2100_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(I1401_LIB) : $(I1401_SOURCE) + $! + $! Building The $(I1401_LIB) Library. + $! + $ $(CC)$(I1401_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(I1620_LIB) : $(I1620_SOURCE) + $! + $! Building The $(I1620_LIB) Library. + $! + $ $(CC)$(I1620_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(IBM1130_LIB) : $(IBM1130_SOURCE) + $! + $! Building The $(IBM1130_LIB) Library. + $! + $ $(CC)$(IBM1130_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(ID16_LIB) : $(ID16_SOURCE) + $! + $! Building The $(ID16_LIB) Library. + $! + $ $(CC)$(ID16_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(ID32_LIB) : $(ID32_SOURCE) + $! + $! Building The $(ID32_LIB) Library. + $! + $ $(CC)$(ID32_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(NOVA_LIB) : $(NOVA_SOURCE) + $! + $! Building The $(NOVA_LIB) Library. + $! + $ $(CC)$(NOVA_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PDP1_LIB) : $(PDP1_SOURCE) + $! + $! Building The $(PDP1_LIB) Library. + $! + $ $(CC)$(PDP1_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PDP4_LIB) : $(PDP18B_SOURCE) + $! + $! Building The $(PDP4_LIB) Library. + $! + $ $(CC)$(PDP4_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PDP7_LIB) : $(PDP18B_SOURCE) + $! + $! Building The $(PDP7_LIB) Library. + $! + $ $(CC)$(PDP7_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PDP8_LIB) : $(PDP8_SOURCE) + $! + $! Building The $(PDP8_LIB) Library. + $! + $ $(CC)$(PDP8_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PDP9_LIB) : $(PDP18B_SOURCE) + $! + $! Building The $(PDP9_LIB) Library. + $! + $ $(CC)$(PDP9_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +# +# If Not On VAX, Build The PDP-10 Library. +# +.IF ALPHA_OR_IA64 +$(PDP10_LIB) : $(PDP10_SOURCE) + $! + $! Building The $(PDP10_LIB) Library. + $! + $ $(CC)$(PDP10_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.ELSE +# +# We Are On VAX And Due To The Use of INT64 We Can't Build It. +# +$(PDP10_LIB) : + $! Due To The Use Of INT64 We Can't Build The + $! $(LIB_DIR)PDP10-$(ARCH).OLB Library On VAX. +.ENDIF + +$(PDP11_LIB1) : $(PDP11_SOURCE1) + $! + $! Building The $(PDP11_LIB1) Library. + $! + $(CC)$(PDP11_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PDP11_LIB2) : $(PDP11_SOURCE2) + $! + $! Building The $(PDP11_LIB2) Library. + $! + $(CC)$(PDP11_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PDP15_LIB) : $(PDP18B_SOURCE) + $! + $! Building The $(PDP15_LIB) Library. + $! + $ $(CC)$(PDP15_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(S3_LIB) : $(S3_SOURCE) + $! + $! Building The $(S3_LIB) Library. + $! + $ $(CC)$(S3_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(SDS_LIB) : $(SDS_SOURCE) + $! + $! Building The $(SDS_LIB) Library. + $! + $ $(CC)$(SDS_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(VAX_LIB) : $(VAX_SOURCE) + $! + $! Building The $(VAX_LIB) Library. + $! + $ $(CC)$(VAX_OPTIONS)/OBJ=$(VAX_DIR) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(VAX780_LIB1) : $(VAX780_SOURCE1) + $! + $! Building The $(VAX780_LIB1) Library. + $! + $ $(CC)$(VAX780_OPTIONS)/OBJ=$(VAX780_DIR) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(VAX780_LIB2) : $(VAX780_SOURCE2) + $! + $! Building The $(VAX780_LIB2) Library. + $! + $ $(CC)$(VAX780_OPTIONS)/OBJ=$(VAX780_DIR) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +$(PCAP_LIB) : $(PCAP_SOURCE) + $! + $! Building The $(PCAP_LIB) Library. + $! + $ SET DEFAULT $(PCAP_DIR) + $ @VMS_PCAP $(DEBUG) + $ SET DEFAULT [--] + $ IF (F$SEARCH("$(PCAP_LIB)").NES."") THEN - + DELETE $(PCAP_LIB); + $ COPY $(PCAP_DIR)PCAP.OLB $(PCAP_LIB) + $ DELETE/NOLOG/NOCONFIRM $(PCAP_DIR)*.OBJ;*,$(PCAP_DIR)*.OLB;* + +# +# If Not On VAX, Build The IBM 7094 Library. +# +.IF ALPHA_OR_IA64 +$(I7094_LIB) : $(I7094_SOURCE) + $! + $! Building The $(I7094_LIB) Library. + $! + $ $(CC)$(I7094_OPTIONS) - + /OBJ=$(BLD_DIR) $(MMS$CHANGED_LIST) + $ IF (F$SEARCH("$(MMS$TARGET)").EQS."") THEN - + LIBRARY/CREATE $(MMS$TARGET) + $ LIBRARY/REPLACE $(MMS$TARGET) $(BLD_DIR)*.OBJ + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.ELSE +# +# We Are On VAX And Due To The Use of INT64 We Can't Build It. +# +$(I7094_LIB) : + $! Due To The Use Of INT64 We Can't Build The + $! $(LIB_DIR)I7094-$(ARCH).OLB Library On VAX. +.ENDIF + +# +# Individual Simulator Builds. +# +ALTAIR : $(SIMH_LIB) $(ALTAIR_LIB) + $! + $! Building The $(BIN_DIR)ALTAIR-$(ARCH).EXE Simulator. + $! + $ $(CC)$(ALTAIR_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ALTAIR-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(ALTAIR_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +ALTAIRZ80 : $(SIMH_LIB) $(ALTAIRZ80_LIB) + $! + $! Building The $(BIN_DIR)ALTAIRZ80-$(ARCH).EXE Simulator. + $! + $ $(CC)$(ALTAIRZ80_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ALTAIRZ80-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(ALTAIRZ80_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +# +# If Not On VAX, Build The PDP-10 Simulator. +# +.IF ALPHA_OR_IA64 +ECLIPSE : $(SIMH_LIB) $(ECLIPSE_LIB) + $! + $! Building The $(BIN_DIR)ECLIPSE-$(ARCH).EXE Simulator. + $! + $ $(CC)$(ECLIPSE_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ECLIPSE-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(ECLIPSE_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.ELSE +# +# Else We Are On VAX And Tell The User We Can't Build On VAX +# Due To The Use Of INT64. +# +ECLIPSE : + $! Sorry, Can't Build $(BIN_DIR)ECLIPSE-$(ARCH).EXE Simulator + $! Because It Requires The Use Of INT64. +.ENDIF + +GRI : $(SIMH_LIB) $(GRI_LIB) + $! + $! Building The $(BIN_DIR)GRI-$(ARCH).EXE Simulator. + $! + $ $(CC)$(GRI_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)GRI-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(GRI_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +LGP : $(SIMH_LIB) $(LGP_LIB) + $! + $! Building The $(BIN_DIR)LGP-$(ARCH).EXE Simulator. + $! + $ $(CC)$(LGP_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)LGP-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(LGP_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +H316 : $(SIMH_LIB) $(H316_LIB) + $! + $! Building The $(BIN_DIR)H316-$(ARCH).EXE Simulator. + $! + $ $(CC)$(H316_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)H316-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(H316_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +HP2100 : $(SIMH_LIB) $(HP2100_LIB) + $! + $! Building The $(BIN_DIR)HP2100-$(ARCH).EXE Simulator. + $! + $ $(CC)$(HP2100_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)HP2100-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(HP2100_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +I1401 : $(SIMH_LIB) $(I1401_LIB) + $! + $! Building The $(BIN_DIR)I1401-$(ARCH).EXE Simulator. + $! + $ $(CC)$(I1401_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)I1401-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(I1401_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +I1620 : $(SIMH_LIB) $(I1620_LIB) + $! + $! Building The $(BIN_DIR)I1620-$(ARCH).EXE Simulator. + $! + $ $(CC)$(I1620_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)I1620-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(I1620_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +IBM1130 : $(SIMH_LIB) $(IBM1130_LIB) + $! + $! Building The $(BIN_DIR)IBM1130-$(ARCH).EXE Simulator. + $! + $ $(CC)$(IBM1130_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)IBM1130-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(IBM1130_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +ID16 : $(SIMH_LIB) $(ID16_LIB) + $! + $! Building The $(BIN_DIR)ID16-$(ARCH).EXE Simulator. + $! + $ $(CC)$(ID16_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ID16-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(ID16_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +ID32 : $(SIMH_LIB) $(ID32_LIB) + $! + $! Building The $(BIN_DIR)ID32-$(ARCH).EXE Simulator. + $! + $ $(CC)$(ID32_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)ID32-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(ID32_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +NOVA : $(SIMH_LIB) $(NOVA_LIB) + $! + $! Building The $(BIN_DIR)NOVA-$(ARCH).EXE Simulator. + $! + $ $(CC)$(NOVA_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)NOVA-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(NOVA_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +PDP1 : $(SIMH_LIB) $(PDP1_LIB) + $! + $! Building The $(BIN_DIR)PDP1-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP1_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP1-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP1_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +PDP4 : $(SIMH_LIB) $(PDP4_LIB) + $! + $! Building The $(BIN_DIR)PDP4-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP4_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP4-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP4_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +PDP7 : $(SIMH_LIB) $(PDP7_LIB) + $! + $! Building The $(BIN_DIR)PDP7-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP7_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP7-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP7_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +PDP8 : $(SIMH_LIB) $(PDP8_LIB) + $! + $! Building The $(BIN_DIR)PDP8-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP8_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP8-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP8_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +PDP9 : $(SIMH_LIB) $(PDP9_LIB) + $! + $! Building The $(BIN_DIR)PDP9-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP9_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP9-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP9_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +# +# If Not On VAX, Build The PDP-10 Simulator. +# +.IF ALPHA_OR_IA64 +PDP10 : $(SIMH_LIB) $(PCAP_LIBD) $(PDP10_LIB) $(PCAP_EXECLET) + $! + $! Building The $(BIN_DIR)PDP10-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP10_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP10-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP10_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY$(PCAP_LIBR) + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.ELSE +# +# Else We Are On VAX And Tell The User We Can't Build On VAX +# Due To The Use Of INT64. +# +PDP10 : + $! Sorry, Can't Build $(BIN_DIR)PDP10-$(ARCH).EXE Simulator + $! Because It Requires The Use Of INT64. +.ENDIF + +PDP11 : $(SIMH_LIB) $(PCAP_LIBD) $(PDP11_LIB1) $(PDP11_LIB2) $(PCAP_EXECLET) + $! + $! Building The $(BIN_DIR)PDP11-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP11_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP11-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP11_LIB1)/LIBRARY,$(PDP11_LIB2)/LIBRARY,$(SIMH_LIB)/LIBRARY$(PCAP_LIBR) + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +PDP15 : $(SIMH_LIB) $(PDP15_LIB) + $! + $! Building The $(BIN_DIR)PDP15-$(ARCH).EXE Simulator. + $! + $ $(CC)$(PDP15_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)PDP15-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(PDP15_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +S3 : $(SIMH_LIB) $(S3_LIB) + $! + $! Building The $(BIN_DIR)S3-$(ARCH).EXE Simulator. + $! + $ $(CC)$(S3_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)S3-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(S3_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +SDS : $(SIMH_LIB) $(SDS_LIB) + $! + $! Building The $(BIN_DIR)SDS-$(ARCH).EXE Simulator. + $! + $ $(CC)$(SDS_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)SDS-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(SDS_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +VAX : $(SIMH_LIB) $(PCAP_LIBD) $(VAX_LIB) $(PCAP_EXECLET) + $! + $! Building The $(BIN_DIR)VAX-$(ARCH).EXE Simulator. + $! + $ $(CC)$(VAX_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)$(LINK_SECTION_BINDING)- + /EXE=$(BIN_DIR)VAX-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(VAX_LIB)/LIBRARY,- + $(SIMH_LIB)/LIBRARY$(PCAP_LIBR) + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +VAX780 : $(SIMH_LIB) $(PCAP_LIBD) $(VAX780_LIB1) $(VAX780_LIB2) $(PCAP_EXECLET) + $! + $! Building The $(BIN_DIR)VAX780-$(ARCH).EXE Simulator. + $! + $ $(CC)$(VAX780_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)$(LINK_SECTION_BINDING)- + /EXE=$(BIN_DIR)VAX780-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,- + $(VAX780_LIB1)/LIBRARY,$(VAX780_LIB2)/LIBRARY,- + $(SIMH_LIB)/LIBRARY$(PCAP_LIBR) + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* + +# +# If Not On VAX, Build The IBM 7094 Simulator. +# +.IF ALPHA_OR_IA64 +I7094 : $(SIMH_LIB) $(I7094_LIB) + $! + $! Building The $(BIN_DIR)I7094-$(ARCH).EXE Simulator. + $! + $ $(CC)$(I7094_OPTIONS)/OBJ=$(BLD_DIR) SCP.C + $ LINK $(LINK_DEBUG)/EXE=$(BIN_DIR)I7094-$(ARCH).EXE - + $(BLD_DIR)SCP.OBJ,$(I7094_LIB)/LIBRARY,$(SIMH_LIB)/LIBRARY$(PCAP_LIBR) + $ DELETE/NOLOG/NOCONFIRM $(BLD_DIR)*.OBJ;* +.ELSE +# +# Else We Are On VAX And Tell The User We Can't Build On VAX +# Due To The Use Of INT64. +# +I7094 : + $! Sorry, Can't Build $(BIN_DIR)I7094-$(ARCH).EXE Simulator + $! Because It Requires The Use Of INT64. +.ENDIF + +# +# PCAP VCI Components +# +$(PCAP_VCI) : $(PCAP_VCMDIR)PCAPVCM.EXE + $! + $! Installing the PCAP VCI Execlet in SYS$LOADABLE_IMAGES + $! + $ COPY $(PCAP_VCMDIR)PCAPVCM.EXE SYS$COMMON:[SYS$LDR]PCAPVCM.EXE + +$(PCAP_VCMDIR)PCAPVCM.EXE : $(PCAP_VCM_SOURCES) + $! + $! Building The PCAP VCI Execlet + $! + $ @SYS$DISK:[.PCAP-VMS.PCAPVCM]BUILD_PCAPVCM + $ DELETE/NOLOG/NOCONFIRM $(PCAP_VCMDIR)*.OBJ;*,$(PCAP_VCMDIR)*.MAP;* diff --git a/readline-hack-08091901.zip b/readline-hack-08091901.zip new file mode 100644 index 0000000000000000000000000000000000000000..19068f0373756bea893c7a5365e0daa1be826e44 GIT binary patch literal 43136 zcmZs>Q;aTL(5>6HZQC|i+qP}ncCWT=+qT|bZQHhu{r&qSXXosknbgg^8>t#e)l;P? z0}6%)1Ox;Hq!7W%U3+9uNs9~wv?lyNKj=VmMmA>VR<>qps?b277=4S9{QrfTCm7Hf z=o#oV@3+PwSCZgi|DWLo2LOBts$rIp!3}(_>Dd*?exmE^)Xvs1cbly5TeThJ#MREP zzgj8czc|o+eOCUS2sN5iX|>X-q$|m^Xa4R3#H)qA!#qOAlTm71L`K1eVROihx>Nr> zO6X5R-bcQc;)#!)rkdOS2SP_v!Oz5h2Vyf#Tu1L)@%EYg!`=d?bUNnTi0hyh&0&Nc zRA#6jvM2syNo?+(Y0N;F?So0{1C-h0uteETX%9Yvk1jX<7FxH02#g=w1MwK~X~#au zzeDaG?t+N*2#7d7@G;E7cY6{MkQnj0yNjkiyxjy)8OPKxX`?~8AJ{|^hk*Lk5b@$O z`pvwn+YM?s_f8GLn@QD!;#9Eq+@2Ej7@IsjTlU0@vMrl26VvM z8YyhyR4>70lg_vikikV6_+lfjGaS9; z1S)?;-Mo!)_%)}-QveXm@xzgIfUx+E*nVWJ(KtsRRg*ucROh^xUx#F z)C$l>&XYXhYU&D-L2TE$%CrH+qC45-yYGb+z_Eq|Lrr7ON}XV4bqKdUWAEVV#od+l z62S>CqjQ>i;$~dljzr2Zm6tqzj*PHa;x<&51e~D=IR5p*_-2Qc_lE$OJG;9GWEALcW1Id-JrU6?WWiyshXEblK;(cr zK6PpP8#)Eufp3?u-5UcuG6BXifw-N!9qe|<4)?r_O|$J+=xOCvg))(D=9Ad=~@0vT> zVpAXdBN&JX{Hld+q~U_F#+sXK{R{)--sn(k?a*+yoLVQ|v!FiQ*!Gl^;x2UtFpJ>1 z!1GteY(^|51w*8Nk(G0MsmA^?107h54>sy z$;fgQZ7ZFJ_-Qp6F{$NWjwqX+4b|uCE%Q^p1L~<6HD3n}*#PzrSM&XlDgC7qdm#li zc6c}?m9;$R0Vxa+$6FL=Qx6a*GLq2yYF1%*sRS9JG+iwVb%DNfB^b{~hv|1hlhYv3?6X)Mw0RAu8HIJosr-MQ!1q&5?PAsB z792Jl+0uWce>5TgHteXb19e0Sa)t}A-mJyMF4`(%a^q!hzLH2-xI?Xzx?^UP6r!5R+A+pd}0V+Jq#k3F|*&lhM_LKaOU)o#hFbuTOc_S(x>Sv&J z1c5TagS*n`MSqHtrh}|I4g`ml6h{Nfx2`$yH;4Y5B+u1z9HU)ghMw1-GmkSEC^fLXQ#i$>Oap z@u~n41HWag9`;vKg^_eL(kj)dI?)Ar#Ho@PJyOy-^4A}>sTp}GB2XS7#hcJDa!uW4VE7Uo=c@=Wd2x^aQ`w}H*j zDx)=S^y&w#MkT)^qgKdZT(f)F8Xm`1wHB zJ+93?aGrk&CFT9M8B@K|!_AiS8|T9IJDu+7KEN8`*T%8_`R-wFccMR@cX@;+v1o3_ zu3(8k_Cv5}386;z&6W-gwOtrbRiyChnw$i$2MWDv`b#Y-Ap%Bs%Jy&WdUc6D(q^4G zMezk^b%Qc>6C5QINj1c0>B(Z~-GQ1hR0j^79e~3us8D10ja+$Prd&Sp7*=zjt~^d` z+ajyAts>Rfzns#P-I95uz|8MWU87=QsZI_N9MFZu$ogJ9{3U{Se+-HUvJ#=U6o1|B zAb{7ltNtfX@vVS}!5Zz$MUTEsEm8$poXHa-73-u3c_=xM3A2>b1ALDn`dzyk`+C=T zc3o@R+1=4#K?zr0OGSrROU(+1`Qo^;xX;3^tgd`9rsA{+tzH42O|z0e@%jM2(4Z6$I_);0NKPnCCL037_gGtC#K5QtJWwP+j2dVO5DxOpNyPU%**Wvu@i+Im4&StZ- zStT96MQ3^WW|*B=jCiX*6qz_7gNhUmsvcB$X-ib(nE&rk84OI4Y><(yer-OOcGlG1W)*1}dUqfq+?yl(cOvkw<$%NL93iIkS)qB`A*-45 zp&}$c9}?llGg_?z8O7)#!CMMrCIYK1Ve1A4|2e8xdM1`L<_T`bkPcLg5d%<;1n~P2 zCCa5V*;FskR*7X;?Ii&h&>7Lz7NRE zV`&j-D>6({(Y8u%Xt?ndB^wij}2dN;t&iT*3ueXYd9JTD>#Mb(QRfdgHLp^Hgxzim6B zQJq*N90#n7rdbQj22+kDa+!*wG|a!O%VKI%F$(2k?CIh?9SWp1b3Sd}GG#p>Avz;U` z_MmEHPo5vM8tc!^k_SGG@j3<)#`V5DYW4@9!_}!6Xm+%hbgiqIi$XS!Qo7(7L~U6lc|r(MLZtefa^*oZlu_~&JGHZkGt5?lr1 zya1!>G%=$LZbjI1OS*y@5RdTk=ErZ#V3YHlOs_q-{*y7;yB3g zANsNiA7Xb9^4T}eY!wdV;!$tp0x^g0c3SzPApyjH?K(t{ArH2$zCn&0#KoGA{=RAe z(sYx%`zAblKW}$bu0@PiT5~^JZf`%EQ72>xNn-Wmj6K?EUH(v(U!aDVbU84LH6NSy z_`Ozmx#~rboP#lr4wpVC`^`MGeGh&T-Bj@!J7w2;(y5%yC8gzu!VCVUhlZ-@?=ylO zI`hqxNt@rh2V6JJ{7W}Gp~g9552;^O?D}9pPY{$3YS8T0rGe-7KK@jk6Rm{>=GVoy zB_B|?y>RWSO@m*=_4vRgbBMiwAu>Q9C(y!mY1K}}Y9p%zh5e~&qn4D#JFuv8^ z`RBjdzwQS_36K-3pB@gbPC=#T2O{xejb2#j3ZV1jC>eYcTW=;s2r!+3A|A z^wI^?sxQ&UxGs>c#tx}O4*nvdD)A-}6=buUkSXHcFuBKIsYY17sE2?e32j*N{6(oI zTzR1R9w6bOfpE@?-bx1WqRBwK%a@3EF)|Nt+BI|sb>;O@bRXcw4;)PMGX^GPzX#=; zgtLhPl*bkSOwfexy}AdI=bhBbIr^+Y{)OYi$q1mKo|=<4FHG;QaFR0&Ve|E4<>}|{ zu8|vrD0jo|ND}-jL+k+K2GI+l8vAL%CYR6c6Up2R_;jI$^&%^(v9e;We(trOS>R|%iUqn`+c(!KP*OtCvjqp2%jDR z^3WwH9W!8QOEg7mob^8%LljMIoE?sVb!X4oJJ3lu*ybf-1h`M?n+jZlFO{1!M1X1P z3Bn0nsw0LRowpX-V>7?k*AdS$5;IeHRaAW9Y2*I|nhIDnZY))giSajU^~{65Pk4Qo zJ}C)#kj+VFoL$EmG(zpZ?KKt1oF88cPDvAlV^eoOth(9MWg8h&*H2XjIHzM zr;k@H=QF7m_qOW#$cW-a|A+$}&G%0nU^6S(hU#xQ6PGMz*-cxpbL$1KqAODEW-hmu zrqk5qo_$bZCr=Ji0_I$-Y#>g&rHX}taf+0x#bT5^ccqrIof;%l9JVXVJmcsJmbel( zPPY>Im~@e>b0Hv`K$EGRTJODvB)MIz$h1}h+oVD^`Mlykwy!yl&9t+jLFWFva}M%Q zjI7ZNJb!@MZdB6I&%f-89@TUSdqtO&iI>{r^`E(Kg4=Od(J<#*iy1|9z4R6{)}3-H zt>3u9zp6DWLZyUtFS@Sva7C;j0iO;^W*m?(#Eq6G8Ohw||C7$zgAu<%@`{dILq193 z@<+p9*g+lZFnxHzV`xl9-P=fCcl~^UP)$JwT14FELhZ|CQEBW?`T|Mjz9x+v6!M&x zrTekXq0M)&jsD(|XQ95Xch@MbR{1CVCHqbt{pBx^;{QuoM#5RSrHz`TV*X<;Xa5Dl zf0V_=%Fe*q%*fQ%%HE8@@_(}yq~8B8YjKO^haF-<3V;1Xht#U#IzLloQWVC+(-pB| zcoIr>g!cdTR$()QEcdzj1_2tK(Q)=%xV~u+v|bFN5%kHDx++s#&10h%LC_QV_0 zccfn0lsV6Yo)VoN$10kGSwEe|LG6b@94!B;HA?w!RBo)bX&=BiH6bm>J6ivO-40{@ zE)z;@zq;@O{XbgR{v$@;Ph+HmAV5I)5dS|~O#Zi)qyJlrs-40n6VgwfffHC1g`y?I zCsXae5_=o5(m3e>4mmhAs{^E%kr`Xa75Jae8TTX#vu5q(zSYy4yy|9!#n2K*fp;D~ zJ$cG}jK`SDW(BPr4d@_`U{NV~Y;5-QM zhSa%!qi+UovuG$qycM-9lzDCGk8D@^m@`cJs-X!T(ooHmD%6=idTO^hX`Dl|(hsh; zU#AjPN$}X_LpOp(pW#|6b$rWiiAU*X+pZk$2-5LJTi)F~J`qN#0~qM$d6!hE#qc@uEz7I%`X`P+6MO;Zqx#9nOhK~5XjhTe0fbc^&Tfm3 zxfSzbw}M$P;xUbEN8~6uRPsrexN0#JO$Tib@4mlzMLIeJ;jFO5*%%kThdAeCQb4e% zq13P#PAk<7c1aPEE<n4g;8spyhJ=|BnN{|8w(FAqwMpA`p;WBnS}r|G3%2k>UR-e82yz@RxLdT@J+4 ze*LOn0?u-4y8=N?rlvf{c%`bCCOEL+1VZ@%C(f#*>;z``C?8T(eEeq0q>UvSSH_0 z-jTh_JZA9a0Qs4jotEz0nFx&Ra9HH>tE_b`G*5%2J6vRk9J7dY!l5BxQePmEMN$oAlNvKK-~WkT&G;H4<&2kP}n z>=ANwp$9~CH&WqFCJxLCi6sT-1W;r!Cfc3>v-Rx68W_!#n9Z}jm80Pt-I?+?;h;7H z`p>p+KUJdwh!z^jo7f@9i#wh#tl7E^J8u>*R$K%v8PAK;OHFnhnJ8O#ALeW=d+&~0 zgxd(=hcfO6F5}7AxbXE6tU!5kmP+89Ew*4iZDrrIZyUUXapBGRrAG|m&CJFIvSl^n z_TG!@4FS9ta_iIAPv*utLSsM$J>Q~ob%c~|eNCl2*AxSmaP`~Q3Ou&EMXSneK|+OKhu$_-`@e1`8Ow954*EUl9GAbrH;ji-(&WrLD)93Wt% zWMDV;Mg{2w+96e8Kq$98q6zOK`xLU^z?54N8PYa!U(YceXp z|IF^#UVan_pfJ`b)o$OWr9QL9uV)~`hx*#?3ElU%LDyq!!!F+ohrbU~2^j_$j2l7n ziRT#a*0h?(Sf&Q<1`0tYG2IWp?-f^MymdeuC07hl3O zuaGxg=8NoX+-(iy;)KIy%SX@+w~{ObBw6(nc_|{35w8hZ`?tLu{^%A5JOP;vt61=3 zzEXP@W^51IynP{yO5BhENSoFW16{)S?SlfyAdp%^E`k^jm!J(p z(`WVptX{1s=Xj^#N*`)lsnw`5L=)h&Tjgt^_n?Xo5vq$bkd@#q*@Td%6smM@=~Pc3 zh9#taBt(c5hQ!(MJbJMX6~l3oioK=kZX1FRhVi`IR#7{DF`ZWbmg?@bQ_64LqH*8 z^d+dTo)5hlItX{-bLNl=OsH>(z`+F`4yiE{b|7d4MV2B<4_%N`t~%(@0V@SrDFxC6 zkrI?pki-pZ-iBNiw#08jIpc1?L~uzh0GmNfi|2H6*6$v>4}?f#&<&=86656SNJbDjMG32Oso95DlEwA(ZE3&ifyW+= zri~LA3|Lk4^H!fg4e@cJEruk(|+2N~|2sx&E^oB*swWdR{S?YmjN1)&rB(Ba`o zXX{jI;sL*k!+}S@!8`o#0(a#|^s^3(=)8~>vZwbU#BdK-x3b9--}(ClO!;G>al*RR zpT@MlEGXC1@&Z^~VT9Wxk?fbx^@jRMMdJm!+{b(Q%(6=fYVYOkkXR)8!IFtowa19K z6~H6}lpumK1ik6zYQ%YYb|yR}oEmaq9Wlf2W*Yj~a^xloo1ptGybjZV-^`$1B#Y~v zT=1Ph2O*Jyy++%KEILD3G>QQ<=ZBX&Z~K6}Ltemvybqo|y79Wt`@XdpH9*-rRrCaA z&ving-+5T0e(%|OPhkhppt8|_ zbePKCHHbc^YT{~Qk0Hzs&I~4+%+-jQXp;ou&$z7#4p*thi=5cD&ooJDPXd?wx|P(I zt437ih(Zm=Vyy@*a~}F2#ICJ=hK{5ASGzY|xW!nlI)L44NL;=Py_p;{rQf(0dqc2_ z(h-9s7B+4zAR)Lq-2++3A8s|FVgNIb&22`!uRi!dHz;)>ZOF#z%kD@yF+SY0=6xx! zW207a=z*k8=#jF2*jQ17X@A6Y7M<)iC^CO!5YMbTQ>Hu&8|~fuS@pe@uhARK!dh?U zw$3L+IkXbbA%vq`wha%%a7sTFY?48^JIxG(i|4!blk<78#^=tcqgz$9fDfDJa>$G` zNn))?r4jD1Zhuj-=v1Zp_1p#~m8GgZN(lSW9sz@9q;3C*`Xw#bxccoUR~Fh|k@3Q< zlkdZu1xp{UwsL#Jhxr;lTyR-sQA0|QW&%aUU)uEzbFc}DCPmq6i9dP%bb8<2d zQrIXbbin*rJUK-ghm}__(WnS){uO-Ol`$r=Fz>K*qtmb|>7d+14NjoBWBw=1ncNw% zyVYXi*C`4#^@vqQRoWN1HxN2BtnlNh*3@szTS=TPkwM5cGqtN@NbaFDzdTfYAi+d?yoEjZCeIz zP~7OzK5dJT9Ry0q(q>p=wF}*a0|qk`&8Z03;=zz<-W!N6j_hW5-X=>H+#0gxaDNeRYS z1%}Fho0@Bs3#V%M6E&WCbqDLatU>TAHu%K|NvY!!72eh>n+XB!DL)|m2fSv+B9D(q zpYRKqaN_ey$bDRyvN_qOu7z}W#3-N{Ios>OEL&>NXt@|~_diW)5v{9|=S%h0+lfryUQL*rgb5MsPaoVhOHAW6A-<@K#Xr@3=2dgY3FaQD2h>}C$)u|MK)^|a@VoVg z;D*3IFvoD!RuhHHPCEWb85;UW8p2sJexQ6jF%jGdd16m6c+iX-KA2YHTt_8 zGVL@VLsEim{@a9zkfEWT&6to``UB)8_-b$n=$A;AnQR<{t*qaGRqCi;i<~8`aX0g1 zVZk3q%w=4w9@90*<44)O@MzwzPLaq6=f#)nI?7 zg&KkAgfy*ifwTK+qTe8y9Vd?CFUP7uaVmG;AI~UMAfXyaIhh)d+7qkjbicXG`fpa|ip4ovQ0?0TT+Kv)^adrK=6 z-t|E}ZYP%Vvf>s)D+2&Qj8~ir$1Jty(csOgOV&ilfB>V=bx32~YXs9Dmh(%gHMHT|Q$()B&3e2>6Vj$}0Q&CC^W4OFe3K2JAG;l{2x z*+-b2nSUNKqL|}E^m)l1rF)zo-IeHgSK&{ZJ=17gb0#8pVV_DhC&dlN<0nIYr|Un` z@VySZg_P7cruX{%X?|YP0OouR?nxh3fRg1kjejLaX`{||zU-vbD@NC&hRcSnw{YjF zEUyc886k`(z}Ct~O{F^Vs6-sdusW>*-o!wB8D3eU+8Sy>5K8Nq=N%H!d<5ZU{@=)e zm(ZR@fTY(}Cs@fq{CQ! z$BT_qMZUHmfyyGccDvggCs5^!Cdr9(KPe<0o4kLREvnPeqzdRl#dafb z;l*N?K9)FjJSVsO)E%W>GD_HavOP>7usxEk!IAM`lh z-_sg>4$kP_@#DU{nz;F=^*tT+OMqrdob51PB?p-ad0lKwoS7ainwy=P9;zDEcHd{5 z*Au^g7s<%CD?`{)TR4-Y;*XvgW zGZ67r3BAJ=(t*b6yggQYt)C%CQQDvb8}O!u<==TPmXgYbM6V9C1*ajW-Yy%)e+|1o zj!<9C2Q*I_lFN-!7&juYvxd7Ah7&Fpo)cM8w)?Xb$_!eW&E~4cGw}xY$@_dmjx_|a zW!(PN%l7f*M~Yz?|NbOIHZuFhN6H*76CjfwE@$(9z=7=E)SC5px3{O=U${EZA1?bG zruwnBTNnUhn$nO*QX79QgldMjfyH+X$5~aUfTcz@QqZRiLADw#9_MpMc)W~xfr{&7 zDyy+%^{1#3Ir!Tne%abq&-?tWeZt2V>03~a?!5g4zS#)lB8r)re(fYa_9cigl_nR$ z>O&;awH?^9zI{PSdioUuMl&|Eg&oYNoz84M?bvU!my*Zvpp{#`4%=&-W;ZTY2sH1~ zKBwca`c1(}>Mzk*jrmIx2lqI)%}0-nUwX4j4Kj_Jf4K?SwxEC-_W5@2&e9?L9e($2 z_%Pc(=`RhQ%N`&1;V`(Ynhw7{>!(qsT983wwHHKVkmW)E4*SkgRbOCj?WQO@W`A`y zK{BW1chfLOueuYz<~Q0nVpbg9ADB?{nr{2cIe1D;M#>SVDK~c(R%B`JW1GI~jkUpt4L4rBfi?xQ zO%$AEiigfl2T?BbOtZsB>wOw#_XwwUND!UAejT2D^5S}Xx>R8fe`Q_riV2hlSa6rR z`&-OiJ|7)d`kSu8QB&Ixc8${d5Oc4v5G+Z~4>#v4Dzg#YeYt2^*GTautep+ZvHqP^4=2Z$liYH%g z>;5`RQGhp9i$nfH^SWEQNjDHYVT4%OZg6NM&HPr`cj(L0PJh^z>-UVrFH`-qz}rL4 zxx059s^-O^k9$(-^xGR`u`@jh>_0)=p$kVW#v8rRQF@1e_=plYPAE|LvL{swOh!l& zw|4g#0v>&t>m7x(iAbMTg{yK$_!T_9H^8k50kEK2u(+ZkC0WAh7WsIDEiUkeSSEKw ztu!V^%`uGK{5Tu&YSHUbCSii018S-pYGaKrxu9P6d($@( z%f7T2T=7+fFO&+5korOu#t4B0{Yr6b)7+!l-pCxp`Px7?SI%~kj^>G}fv|)Dx`Qy~ z#=xuOBZ@~;+q0^OwEE>4NERFhEB%b3_+WmRFE+$*5u>QK1w}PtKPg38GbKd?rds~K zu=$9kP%oh}Y-r>F6cgBX_3kDy5IIgs8!YXpJ@VFPLUDKx?k& z^l_-;qvT2~=sEsSAL(Jh-N5kCb4e*iQ=Vxm^P!x=Kms5D@#<&#t>OF)L;(jj#D{pC z6?}9;^hZqvS~yPo#KkXZP0>h)%V+qE(<}?bHQwj8@w0e${DU97`!Qq_)aM@FobiA$ z#Sra%bVud`ITU8b{|pT~>j#ZX_~x=RSzUqrRV6B6vgNp;(YYBCcNdn$0F7^ez;}v;Dx2t)EW|O$)uoQ^ zN0-hIDg?DecYqj|YFecMBw-bS*FMH#2&zTxm0I7}C)~SjE}O}WF0)ARp)>6-vGyS% z`g3$%;hWLDSIDmzwvv4&_h^@BOWi1rnSydnvPQOAp7^f#WF}>GAP;UT>oJ6rwX90z zN3>N%Qqt+psd8+S#5lSc1UnO~uVg3j`_{=8^pq|7g*|EnSW10Gl4VzN#^uG2&a=#X z6E>25gOU zGI<3bj-1wsDKe%}&mnUj-T{5!0P-hwH|q2;bzd~;^G_F#`{37t0s-MFx$?#Cb6E3& zA*d=cSQw#A3ax4<#n(7iZ5n5<;pCyJ*wR)?l3QBWJ_FOL)ZGXZVy~N+idC(|iE8O9 z778LiRjB+h{AIbhF4gd;R$#W|sS;gL$OP$)9&#eVyAjs48iZNCYWm*_@BEepL=Pt! ze_fwyHzW5u5{L@2&-0D@$J{BO zgE2E?e(`r%s&6vdBPGWyTkaWmeW21eOjgxlavhh#gxl#8pgxJImU2jojHeto`<-=K zKy%UQK6xPqMtp_IL;vl{njL@oBKNC-_09+|f4ME%eHOg#{no*iXkz;K^vZh_j9s@$ zh;1NVpT1#-=m$F-@vyrC&M}QYJVsssiYEVCgdGHJ{`<1|JioncP>kH>(R|wD?T*iO zQFmVVKTly%gxMPC zfY1*EJvn*D)cj;xT73xyRSlv-jnkdzf@NIZ?ZkpZ*r6hPDTv{-A%Kp%y1UE&AD<=| z0?!_X<380|%wXXlmSimZj(iKuw#wG;7wQdGFCS%(g?$s}tb3eC(@N8cbgr%5cYW@@ zvLtvY5o5 zA#e~Me~G1c{fXXolU(uxB!R@P?`M+joo3l|n@#IAiw_7IUGFB88LA>0VYn0@UmWJX z{-WpR*h}!1@m{O*3BnSI&p?U-y+2TeZHQgLU0ARXtNHbf+pNur(AyE%tPS+NpsKoP z^Aug|=a`V8i&b*pB!WdfRy22tM|R`O5lolimkC=om%5jbmRr;2ar#jtxsNDt_|+PZ zNfZF@^8P`}$v!Ij^`xitq)_hy9GSv-CXykhcqVQi0spc@70r4?ZM-Lv+dpa7V-Pr# z2f}3+nb8fgO{}|9H~jpqub0i_hdtWSkr1!%TmA;uIb&>}V$C5XTN>o78xD5X2Z9llMDwCN*h^YCqSfreCnWTYW|v>6FdK6 z2@X}*SNI(Dr2hrFsWQnmr24^acz8UpHETn;7tQcv*;-~4A{)6wuwZ9X zE-Ex09NJ)-bdnDuz5uJkVFL~R6Y;33li5DdvhK9tS_znY+?4yLJzp993)-hfhGeAy z{Zw&g8NvK6>%aXr_Wkp$G5CtJN*idG?eZl3X|HhANOD>MPUVWF(oDDe$hPWbeM!iX z;&``j^L9FWH|z2_?D7Tv`_20Esrxh0|1kU2y(rW^C+CXWo@iy3mFcc8vbn&~YWF=> z)N0AF0f}#lxGwhMCE&qL&jpzWIv0c$H+*^S?)EvxP=X6oCwhA_P4E z=7$s|eu)`E zt00YMoy81wP*J;g5S=k+XW(%2%KY^BUp&wFRP9F?33V(dVD_-|s?DUp*%NYr{2OTq zT*DWZr=OVfbMp$Ih|ji}`D6L≫jqYiC;anuU4(S`$3n=eJJu+!o}x;P7)`;Zu(o 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 F{Vyi6c^v=% literal 0 HcmV?d00001 diff --git a/readline-hack-08092001.zip b/readline-hack-08092001.zip new file mode 100644 index 0000000000000000000000000000000000000000..0c5f6c081f5e098fdf114694d6d6bf9a603e9d09 GIT binary patch literal 49791 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 literal 0 HcmV?d00001 diff --git a/scp.c b/scp.c new file mode 100644 index 0000000..41ec48c --- /dev/null +++ b/scp.c @@ -0,0 +1,4427 @@ +/* scp.c: simulator control program + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 31-Mar-08 RMS Fixed bug in local/global register search (found by Mark Pizzolato) + Fixed bug in restore of RO units (from Mark Pizzolato) + 06-Feb-08 RMS Added SET/SHO/NO BR with default argument + 18-Jul-07 RMS Modified match_ext for VMS ext;version support + 28-Apr-07 RMS Modified sim_instr invocation to call sim_rtcn_init_all + Fixed bug in get_sim_opt + Fixed bug in restoration with changed memory size + 08-Mar-07 JDB Fixed breakpoint actions in DO command file processing + 30-Jan-07 RMS Fixed bugs in get_ipaddr + 17-Oct-06 RMS Added idle support + 04-Oct-06 JDB DO cmd failure now echoes cmd unless -q + 14-Jul-06 RMS Added sim_activate_abs + 02-Jun-06 JDB Fixed do_cmd to exit nested files on assertion failure + Added -E switch to do_cmd to exit on any error + 14-Feb-06 RMS Upgraded save file format to V3.5 + 18-Jan-06 RMS Added fprint_stopped_gen + Added breakpoint spaces + Fixed unaligned register access (found by Doug Carman) + 22-Sep-05 RMS Fixed declarations (from Sterling Garwood) + 30-Aug-05 RMS Revised to trim trailing spaces on file names + 25-Aug-05 RMS Added variable default device support + 23-Aug-05 RMS Added Linux line history support + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 01-May-05 RMS Revised syntax for SET DEBUG (from Dave Bryan) + 22-Mar-05 JDB Modified DO command to allow ten-level nesting + 18-Mar-05 RMS Moved DETACH tests into detach_unit (from Dave Bryan) + Revised interface to fprint_sym, fparse_sym + 07-Feb-05 RMS Added ASSERT command (from Dave Bryan) + 02-Feb-05 RMS Fixed bug in global register search + 26-Dec-04 RMS Qualified SAVE examine, RESTORE deposit with SIM_SW_REST + 10-Nov-04 JDB Fixed logging of errors from cmds in "do" file + 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy + Renamed unit OFFLINE/ONLINE to DISABLED/ENABLED (from Dave Bryan) + Revised to flush output files after simulation stop (from Dave Bryan) + 15-Oct-04 RMS Fixed HELP to suppress duplicate descriptions + 27-Sep-04 RMS Fixed comma-separation options in set (from David Bryan) + 09-Sep-04 RMS Added -p option for RESET + 13-Aug-04 RMS Qualified RESTORE detach with SIM_SW_REST + 17-Jul-04 RMS Added ECHO command (from Dave Bryan) + 12-Jul-04 RMS Fixed problem ATTACHing to read only files + (found by John Dundas) + 28-May-04 RMS Added SET/SHOW CONSOLE + 14-Feb-04 RMS Updated SAVE/RESTORE (V3.2) + RMS Added debug print routines (from Dave Hittner) + RMS Added sim_vm_parse_addr and sim_vm_fprint_addr + RMS Added REG_VMAD support + RMS Split out libraries + RMS Moved logging function to SCP + RMS Exposed step counter interface(s) + RMS Fixed double logging of SHOW BREAK (found by Mark Pizzolato) + RMS Fixed implementation of REG_VMIO + RMS Added SET/SHOW DEBUG, SET/SHOW DEBUG, + SHOW MODIFIERS, SHOW RADIX + RMS Changed sim_fsize to take uptr argument + 29-Dec-03 RMS Added Telnet console output stall support + 01-Nov-03 RMS Cleaned up implicit detach on attach/restore + Fixed bug in command line read while logging (found by Mark Pizzolato) + 01-Sep-03 RMS Fixed end-of-file problem in dep, idep + Fixed error on trailing spaces in dep, idep + 15-Jul-03 RMS Removed unnecessary test in reset_all + 15-Jun-03 RMS Added register flag REG_VMIO + 25-Apr-03 RMS Added extended address support (V3.0) + Fixed bug in SAVE (found by Peter Schorn) + Added u5, u6 fields + Added logical name support + 03-Mar-03 RMS Added sim_fsize + 27-Feb-03 RMS Fixed bug in multiword deposits to files + 08-Feb-03 RMS Changed sim_os_sleep to void, match_ext to char* + Added multiple actions, .ini file support + Added multiple switch evaluations per line + 07-Feb-03 RMS Added VMS support for ! (from Mark Pizzolato) + 01-Feb-03 RMS Added breakpoint table extension, actions + 14-Jan-03 RMS Added missing function prototypes + 10-Jan-03 RMS Added attach/restore flag, dynamic memory size support, + case sensitive SET options + 22-Dec-02 RMS Added ! (OS command) feature (from Mark Pizzolato) + 17-Dec-02 RMS Added get_ipaddr + 02-Dec-02 RMS Added EValuate command + 16-Nov-02 RMS Fixed bug in register name match algorithm + 13-Oct-02 RMS Fixed Borland compiler warnings (found by Hans Pufal) + 05-Oct-02 RMS Fixed bugs in set_logon, ssh_break (found by David Hittner) + Added support for fixed buffer devices + Added support for Telnet console, removed VT support + Added help + Added VMS file optimizations (from Robert Alan Byer) + Added quiet mode, DO with parameters, GUI interface, + extensible commands (from Brian Knittel) + Added device enable/disable commands + 14-Jul-02 RMS Fixed exit bug in do, added -v switch (from Brian Knittel) + 17-May-02 RMS Fixed bug in fxread/fxwrite error usage (found by + Norm Lastovic) + 02-May-02 RMS Added VT emulation interface, changed {NO}LOG to SET {NO}LOG + 22-Apr-02 RMS Fixed laptop sleep problem in clock calibration, added + magtape record length error (found by Jonathan Engdahl) + 26-Feb-02 RMS Fixed initialization bugs in do_cmd, get_aval + (found by Brian Knittel) + 10-Feb-02 RMS Fixed problem in clock calibration + 06-Jan-02 RMS Moved device enable/disable to simulators + 30-Dec-01 RMS Generalized timer packaged, added circular arrays + 19-Dec-01 RMS Fixed DO command bug (found by John Dundas) + 07-Dec-01 RMS Implemented breakpoint package + 05-Dec-01 RMS Fixed bug in universal register logic + 03-Dec-01 RMS Added read-only units, extended SET/SHOW, universal registers + 24-Nov-01 RMS Added unit-based registers + 16-Nov-01 RMS Added DO command + 28-Oct-01 RMS Added relative range addressing + 08-Oct-01 RMS Added SHOW VERSION + 30-Sep-01 RMS Relaxed attach test in BOOT + 27-Sep-01 RMS Added queue count routine, fixed typo in ex/mod + 17-Sep-01 RMS Removed multiple console support + 07-Sep-01 RMS Removed conditional externs on function prototypes + Added special modifier print + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze (V2.7) + 18-Jul-01 RMS Minor changes for Macintosh port + 12-Jun-01 RMS Fixed bug in big-endian I/O (found by Dave Conroy) + 27-May-01 RMS Added multiple console support + 16-May-01 RMS Added logging + 15-May-01 RMS Added features from Tim Litt + 12-May-01 RMS Fixed missing return in disable_cmd + 25-Mar-01 RMS Added ENABLE/DISABLE + 14-Mar-01 RMS Revised LOAD/DUMP interface (again) + 05-Mar-01 RMS Added clock calibration support + 05-Feb-01 RMS Fixed bug, DETACH buffered unit with hwmark = 0 + 04-Feb-01 RMS Fixed bug, RESTORE not using device's attach routine + 21-Jan-01 RMS Added relative time + 22-Dec-00 RMS Fixed find_device for devices ending in numbers + 08-Dec-00 RMS V2.5a changes + 30-Oct-00 RMS Added output file option to examine + 11-Jul-99 RMS V2.5 changes + 13-Apr-99 RMS Fixed handling of 32b addresses + 04-Oct-98 RMS V2.4 changes + 20-Aug-98 RMS Added radix commands + 05-Jun-98 RMS Fixed bug in ^D handling for UNIX + 10-Apr-98 RMS Added switches to all commands + 26-Oct-97 RMS Added search capability + 25-Jan-97 RMS Revised data types + 23-Jan-97 RMS Added bi-endian I/O + 06-Sep-96 RMS Fixed bug in variable length IEXAMINE + 16-Jun-96 RMS Changed interface to parse/print_sym + 06-Apr-96 RMS Added error checking in reset all + 07-Jan-96 RMS Added register buffers in save/restore + 11-Dec-95 RMS Fixed ordering bug in save/restore + 22-May-95 RMS Added symbolic input + 13-Apr-95 RMS Added symbolic printouts +*/ + +/* Macros and data structures */ + +#include "sim_defs.h" +#include "sim_rev.h" +#include +#include + +#ifdef HAVE_READLINE +#include +#endif + +#define EX_D 0 /* deposit */ +#define EX_E 1 /* examine */ +#define EX_I 2 /* interactive */ +#define SCH_OR 0 /* search logicals */ +#define SCH_AND 1 +#define SCH_XOR 2 +#define SCH_E 0 /* search booleans */ +#define SCH_N 1 +#define SCH_G 2 +#define SCH_L 3 +#define SCH_EE 4 +#define SCH_NE 5 +#define SCH_GE 6 +#define SCH_LE 7 +#define SSH_ST 0 /* set */ +#define SSH_SH 1 /* show */ +#define SSH_CL 2 /* clear */ + +#define DO_NEST_LVL 10 /* DO cmd nesting level */ +#define SRBSIZ 1024 /* save/restore buffer */ +#define SIM_BRK_INILNT 4096 /* bpt tbl length */ +#define SIM_BRK_ALLTYP 0xFFFFFFFF +#define UPDATE_SIM_TIME(x) sim_time = sim_time + (x - sim_interval); \ + sim_rtime = sim_rtime + ((uint32) (x - sim_interval)); \ + x = sim_interval + +#define SZ_D(dp) (size_map[((dp)->dwidth + CHAR_BIT - 1) / CHAR_BIT]) +#define SZ_R(rp) \ + (size_map[((rp)->width + (rp)->offset + CHAR_BIT - 1) / CHAR_BIT]) +#if defined (USE_INT64) +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint32)) v = *(((uint32 *) mb) + ((uint32) j)); \ + else v = *(((t_uint64 *) mb) + ((uint32) j)); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + j) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ + else if (sz == sizeof (uint32)) *(((uint32 *) mb) + ((uint32) j)) = (uint32) v; \ + else *(((t_uint64 *) mb) + ((uint32) j)) = v; +#else +#define SZ_LOAD(sz,v,mb,j) \ + if (sz == sizeof (uint8)) v = *(((uint8 *) mb) + ((uint32) j)); \ + else if (sz == sizeof (uint16)) v = *(((uint16 *) mb) + ((uint32) j)); \ + else v = *(((uint32 *) mb) + ((uint32) j)); +#define SZ_STORE(sz,v,mb,j) \ + if (sz == sizeof (uint8)) *(((uint8 *) mb) + ((uint32) j)) = (uint8) v; \ + else if (sz == sizeof (uint16)) *(((uint16 *) mb) + ((uint32) j)) = (uint16) v; \ + else *(((uint32 *) mb) + ((uint32) j)) = v; +#endif +#define GET_SWITCHES(cp) \ + if ((cp = get_sim_sw (cp)) == NULL) return SCPE_INVSW +#define GET_RADIX(val,dft) \ + if (sim_switches & SWMASK ('O')) val = 8; \ + else if (sim_switches & SWMASK ('D')) val = 10; \ + else if (sim_switches & SWMASK ('H')) val = 16; \ + else val = dft; + +/* VM interface */ + +extern char sim_name[]; +extern DEVICE *sim_devices[]; +extern REG *sim_PC; +extern const char *sim_stop_messages[]; +extern t_stat sim_instr (void); +extern t_stat sim_load (FILE *ptr, char *cptr, char *fnam, int32 flag); +extern int32 sim_emax; +extern t_stat fprint_sym (FILE *ofile, t_addr addr, t_value *val, + UNIT *uptr, int32 sw); +extern t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, + int32 sw); + +/* The per-simulator init routine is a weak global that defaults to NULL + The other per-simulator pointers can be overrriden by the init routine */ + +void (*sim_vm_init) (void); +char* (*sim_vm_read) (char *ptr, int32 size, FILE *stream) = NULL; +void (*sim_vm_post) (t_bool from_scp) = NULL; +CTAB *sim_vm_cmd = NULL; +void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr) = NULL; +t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr) = NULL; + +/* Prototypes */ + +/* Set and show command processors */ + +t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat ssh_break (FILE *st, char *cptr, int32 flg); +t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr); +t_stat show_config (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_queue (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_mod_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_log_names (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat show_device (FILE *st, DEVICE *dptr, int32 flag); +t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag); +t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg); +t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, char *cptr, int32 flag); +t_stat sim_check_console (int32 sec); +t_stat sim_save (FILE *sfile); +t_stat sim_rest (FILE *rfile); + +/* Breakpoint package */ + +t_stat sim_brk_init (void); +t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act); +t_stat sim_brk_clr (t_addr loc, int32 sw); +t_stat sim_brk_clrall (int32 sw); +t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw); +t_stat sim_brk_showall (FILE *st, int32 sw); +char *sim_brk_getact (char *buf, int32 size); +void sim_brk_clract (void); +void sim_brk_npc (uint32 cnt); +BRKTAB *sim_brk_new (t_addr loc); + +/* Commands support routines */ + +SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr); +int32 test_search (t_value val, SCHTAB *schptr); +char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc); +int32 get_switches (char *cptr); +char *get_sim_sw (char *cptr); +t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr); +t_value get_rval (REG *rptr, uint32 idx); +void put_rval (REG *rptr, uint32 idx, t_value val); +t_value strtotv (char *inptr, char **endptr, uint32 radix); +void fprint_help (FILE *st); +void fprint_stopped (FILE *st, t_stat r); +void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr); +char *read_line (char *ptr, int32 size, FILE *stream); +REG *find_reg_glob (char *ptr, char **optr, DEVICE **gdptr); +char *sim_trim_endspc (char *cptr); + +/* Forward references */ + +t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr); +t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr); +t_bool qdisable (DEVICE *dptr); +t_stat attach_err (UNIT *uptr, t_stat stat); +t_stat detach_all (int32 start_device, t_bool shutdown); +t_stat assign_device (DEVICE *dptr, char *cptr); +t_stat deassign_device (DEVICE *dptr); +t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr); +t_stat run_boot_prep (void); +t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + REG *lowr, REG *highr, uint32 lows, uint32 highs); +t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx); +t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx); +t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr); +t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr); +t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr, + UNIT *uptr, int32 dfltinc); +t_stat step_svc (UNIT *ptr); +void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *do_arg[]); + +/* Global data */ + +DEVICE *sim_dflt_dev = NULL; +UNIT *sim_clock_queue = NULL; +int32 sim_interval = 0; +int32 sim_switches = 0; +FILE *sim_ofile = NULL; +SCHTAB *sim_schptr = FALSE; +DEVICE *sim_dfdev = NULL; +UNIT *sim_dfunit = NULL; +int32 sim_opt_out = 0; +int32 sim_is_running = 0; +uint32 sim_brk_summ = 0; +uint32 sim_brk_types = 0; +uint32 sim_brk_dflt = 0; +char *sim_brk_act = NULL; +BRKTAB *sim_brk_tab = NULL; +int32 sim_brk_ent = 0; +int32 sim_brk_lnt = 0; +int32 sim_brk_ins = 0; +t_bool sim_brk_pend[SIM_BKPT_N_SPC] = { FALSE }; +t_addr sim_brk_ploc[SIM_BKPT_N_SPC] = { 0 }; +int32 sim_quiet = 0; +int32 sim_step = 0; +static double sim_time; +static uint32 sim_rtime; +static int32 noqueue_time; +volatile int32 stop_cpu = 0; +t_value *sim_eval = NULL; +int32 sim_deb_close = 0; /* 1 = close debug */ +FILE *sim_log = NULL; /* log file */ +FILE *sim_deb = NULL; /* debug file */ +static SCHTAB sim_stab; + +static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) }; +#if defined USE_INT64 +static const char *sim_si64 = "64b data"; +#else +static const char *sim_si64 = "32b data"; +#endif +#if defined USE_ADDR64 +static const char *sim_sa64 = "64b addresses"; +#else +static const char *sim_sa64 = "32b addresses"; +#endif +#if defined USE_NETWORK +static const char *sim_snet = "Ethernet support"; +#else +static const char *sim_snet = "no Ethernet"; +#endif + +/* Tables and strings */ + +const char save_vercur[] = "V3.5"; +const char save_ver32[] = "V3.2"; +const char save_ver30[] = "V3.0"; +const char *scp_error_messages[] = { + "Address space exceeded", + "Unit not attached", + "I/O error", + "Checksum error", + "Format error", + "Unit not attachable", + "File open error", + "Memory exhausted", + "Invalid argument", + "Step expired", + "Unknown command", + "Read only argument", + "Command not completed", + "Simulation stopped", + "Goodbye", + "Console input I/O error", + "Console output I/O error", + "End of file", + "Relocation error", + "No settable parameters", + "Unit already attached", + "Hardware timer error", + "SIGINT handler setup error", + "Console terminal setup error", + "Subscript out of range", + "Command not allowed", + "Unit disabled", + "Read only operation not allowed", + "Invalid switch", + "Missing value", + "Too few arguments", + "Too many arguments", + "Non-existent device", + "Non-existent unit", + "Non-existent register", + "Non-existent parameter", + "Nested DO command limit exceeded", + "Internal error", + "Invalid magtape record length", + "Console Telnet connection lost", + "Console Telnet connection timed out", + "Console Telnet output stall", + "Assertion failed" + }; + +const size_t size_map[] = { sizeof (int8), + sizeof (int8), sizeof (int16), sizeof (int32), sizeof (int32) +#if defined (USE_INT64) + , sizeof (t_int64), sizeof (t_int64), sizeof (t_int64), sizeof (t_int64) +#endif +}; + +const t_value width_mask[] = { 0, + 0x1, 0x3, 0x7, 0xF, + 0x1F, 0x3F, 0x7F, 0xFF, + 0x1FF, 0x3FF, 0x7FF, 0xFFF, + 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF, + 0x1FFFF, 0x3FFFF, 0x7FFFF, 0xFFFFF, + 0x1FFFFF, 0x3FFFFF, 0x7FFFFF, 0xFFFFFF, + 0x1FFFFFF, 0x3FFFFFF, 0x7FFFFFF, 0xFFFFFFF, + 0x1FFFFFFF, 0x3FFFFFFF, 0x7FFFFFFF, 0xFFFFFFFF +#if defined (USE_INT64) + , 0x1FFFFFFFF, 0x3FFFFFFFF, 0x7FFFFFFFF, 0xFFFFFFFFF, + 0x1FFFFFFFFF, 0x3FFFFFFFFF, 0x7FFFFFFFFF, 0xFFFFFFFFFF, + 0x1FFFFFFFFFF, 0x3FFFFFFFFFF, 0x7FFFFFFFFFF, 0xFFFFFFFFFFF, + 0x1FFFFFFFFFFF, 0x3FFFFFFFFFFF, 0x7FFFFFFFFFFF, 0xFFFFFFFFFFFF, + 0x1FFFFFFFFFFFF, 0x3FFFFFFFFFFFF, 0x7FFFFFFFFFFFF, 0xFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFF, 0x3FFFFFFFFFFFFF, 0x7FFFFFFFFFFFFF, 0xFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFF, + 0x1FFFFFFFFFFFFFFF, 0x3FFFFFFFFFFFFFFF, + 0x7FFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF +#endif + }; + +static CTAB cmd_table[] = { + { "RESET", &reset_cmd, 0, + "r{eset} {ALL|} reset simulator\n" }, + { "EXAMINE", &exdep_cmd, EX_E, + "e{xamine} examine memory or registers\n" }, + { "IEXAMINE", &exdep_cmd, EX_E+EX_I, + "ie{xamine} interactive examine memory or registers\n" }, + { "DEPOSIT", &exdep_cmd, EX_D, + "d{eposit} deposit in memory or registers\n" }, + { "IDEPOSIT", &exdep_cmd, EX_D+EX_I, + "id{eposit} interactive deposit in memory or registers\n" }, + { "EVALUATE", &eval_cmd, 0, + "ev{aluate} evaluate symbolic expression\n" }, + { "RUN", &run_cmd, RU_RUN, + "ru{n} {new PC} reset and start simulation\n" }, + { "GO", &run_cmd, RU_GO, + "go {new PC} start simulation\n" }, + { "STEP", &run_cmd, RU_STEP, + "s{tep} {n} simulate n instructions\n" }, + { "CONT", &run_cmd, RU_CONT, + "c{ont} continue simulation\n" }, + { "BOOT", &run_cmd, RU_BOOT, + "b{oot} bootstrap unit\n" }, + { "BREAK", &brk_cmd, SSH_ST, + "br{eak} set breakpoints\n" }, + { "NOBREAK", &brk_cmd, SSH_CL, + "nobr{eak} clear breakpoints\n" }, + { "ATTACH", &attach_cmd, 0, + "at{tach} attach file to simulated unit\n" }, + { "DETACH", &detach_cmd, 0, + "det{ach} detach file from simulated unit\n" }, + { "ASSIGN", &assign_cmd, 0, + "as{sign} assign logical name for device\n" }, + { "DEASSIGN", &deassign_cmd, 0, + "dea{ssign} deassign logical name for device\n" }, + { "SAVE", &save_cmd, 0, + "sa{ve} save simulator to file\n" }, + { "RESTORE", &restore_cmd, 0, + "rest{ore}|ge{t} restore simulator from file\n" }, + { "GET", &restore_cmd, 0, NULL }, + { "LOAD", &load_cmd, 0, + "l{oad} {} load binary file\n" }, + { "DUMP", &load_cmd, 1, + "du(mp) {} dump binary file\n" }, + { "EXIT", &exit_cmd, 0, + "exi{t}|q{uit}|by{e} exit from simulation\n" }, + { "QUIT", &exit_cmd, 0, NULL }, + { "BYE", &exit_cmd, 0, NULL }, + { "SET", &set_cmd, 0, + "set console arg{,arg...} set console options\n" + "set break set breakpoints\n" + "set nobreak clear breakpoints\n" + "set throttle x{M|K|%%} set simulation rate\n" + "set nothrottle set simulation rate to maximum\n" + "set OCT|DEC|HEX set device display radix\n" + "set ENABLED enable device\n" + "set DISABLED disable device\n" + "set DEBUG{=arg} set device debug flags\n" + "set NODEBUG={arg} clear device debug flags\n" + "set arg{,arg...} set device parameters\n" + "set ENABLED enable unit\n" + "set DISABLED disable unit\n" + "set arg{,arg...} set unit parameters\n" + }, + { "SHOW", &show_cmd, 0, + "sh{ow} br{eak} show breakpoints\n" + "sh{ow} con{figuration} show configuration\n" + "sh{ow} cons{ole} {arg} show console options\n" + "sh{ow} dev{ices} show devices\n" + "sh{ow} m{odifiers} show modifiers\n" + "sh{ow} n{ames} show logical names\n" + "sh{ow} q{ueue} show event queue\n" + "sh{ow} ti{me} show simulated time\n" + "sh{ow} th{rottle} show simulation rate\n" + "sh{ow} ve{rsion} show simulator version\n" + "sh{ow} RADIX show device display radix\n" + "sh{ow} DEBUG show device debug flags\n" + "sh{ow} MODIFIERS show device modifiers\n" + "sh{ow} {arg,...} show device parameters\n" + "sh{ow} {arg,...} show unit parameters\n" }, + { "DO", &do_cmd, 1, + "do {arg,arg...} process command file\n" }, + { "ECHO", &echo_cmd, 0, + "echo display \n" }, + { "ASSERT", &assert_cmd, 0, + "assert {} test simulator state against condition\n" }, + { "HELP", &help_cmd, 0, + "h{elp} type this message\n" + "h{elp} type help for command\n" }, + { "!", &spawn_cmd, 0, + "! execute local command interpreter\n" + "! execute local host command\n" }, + { NULL, NULL, 0 } + }; + +/* Main command loop */ + +int main (int argc, char *argv[]) +{ +char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr; +int32 i, sw; +t_bool lookswitch; +t_stat stat; +CTAB *cmdp; + +#ifdef HAVE_READLINE + readline_read_history(); +#endif + +#if defined (__MWERKS__) && defined (macintosh) +argc = ccommand (&argv); +#endif + +*cbuf = 0; /* init arg buffer */ +sim_switches = 0; /* init switches */ +lookswitch = TRUE; +for (i = 1; i < argc; i++) { /* loop thru args */ + if (argv[i] == NULL) continue; /* paranoia */ + if ((*argv[i] == '-') && lookswitch) { /* switch? */ + if ((sw = get_switches (argv[i])) < 0) { + fprintf (stderr, "Invalid switch %s\n", argv[i]); + return 0; + } + sim_switches = sim_switches | sw; + } + else { + if ((strlen (argv[i]) + strlen (cbuf) + 1) >= CBUFSIZE) { + fprintf (stderr, "Argument string too long\n"); + return 0; + } + if (*cbuf) strcat (cbuf, " "); /* concat args */ + strcat (cbuf, argv[i]); + lookswitch = FALSE; /* no more switches */ + } + } /* end for */ +sim_quiet = sim_switches & SWMASK ('Q'); /* -q means quiet */ + +if (sim_vm_init != NULL) (*sim_vm_init)(); /* call once only */ +sim_finit (); /* init fio package */ +stop_cpu = 0; +sim_interval = 0; +sim_time = sim_rtime = 0; +noqueue_time = 0; +sim_clock_queue = NULL; +sim_is_running = 0; +sim_log = NULL; +if (sim_emax <= 0) sim_emax = 1; +sim_timer_init (); + +if ((stat = sim_ttinit ()) != SCPE_OK) { + fprintf (stderr, "Fatal terminal initialization error\n%s\n", + scp_error_messages[stat - SCPE_BASE]); + return 0; + } +if ((sim_eval = (t_value *) calloc (sim_emax, sizeof (t_value))) == NULL) { + fprintf (stderr, "Unable to allocate examine buffer\n"); + return 0; + }; +if ((stat = reset_all_p (0)) != SCPE_OK) { + fprintf (stderr, "Fatal simulator initialization error\n%s\n", + scp_error_messages[stat - SCPE_BASE]); + return 0; + } +if ((stat = sim_brk_init ()) != SCPE_OK) { + fprintf (stderr, "Fatal breakpoint table initialization error\n%s\n", + scp_error_messages[stat - SCPE_BASE]); + return 0; + } +if (!sim_quiet) { + printf ("\n"); + show_version (stdout, NULL, NULL, 0, NULL); + } +if (sim_dflt_dev == NULL) sim_dflt_dev = sim_devices[0]; /* if no default */ + +if (*cbuf) /* cmd file arg? */ + stat = do_cmd (0, cbuf); /* proc cmd file */ +else if (*argv[0]) { /* sim name arg? */ + char nbuf[PATH_MAX + 7], *np; /* "path.ini" */ + nbuf[0] = '"'; /* starting " */ + strncpy (nbuf + 1, argv[0], PATH_MAX + 1); /* copy sim name */ + if (np = match_ext (nbuf, "EXE")) *np = 0; /* remove .exe */ + strcat (nbuf, ".ini\""); /* add .ini" */ + stat = do_cmd (-1, nbuf); /* proc cmd file */ + } + +while (stat != SCPE_EXIT) { /* in case exit */ + + if (cptr = sim_brk_getact (cbuf, CBUFSIZE)) /* pending action? */ + printf ("sim> %s\n", cptr); /* echo */ + else if (sim_vm_read != NULL) /* sim routine? */ + cptr = (*sim_vm_read) (cbuf, CBUFSIZE, stdin); + + else + #ifdef HAVE_READLINE + cptr = readline_read_line(cbuf,CBUFSIZE,"sim>"); + #else + printf("sim>"); + cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */ + #endif + if (cptr == NULL){ /* ignore EOF */ + printf("\r"); + continue; + } + if (*cptr == 0) continue; /* ignore blank */ + + if (sim_log) fprintf (sim_log, "sim> %s\n", cptr); /* log cmd */ + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + sim_switches = 0; /* init switches */ + if (cmdp = find_cmd (gbuf)) /* lookup command */ + stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */ + else stat = SCPE_UNK; + if (stat >= SCPE_BASE) { /* error? */ + printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); + if (sim_log) fprintf (sim_log, "%s\n", + scp_error_messages[stat - SCPE_BASE]); + } + if (sim_vm_post != NULL) (*sim_vm_post) (TRUE); + } /* end while */ + +detach_all (0, TRUE); /* close files */ +sim_set_deboff (0, NULL); /* close debug */ +sim_set_logoff (0, NULL); /* close log */ +sim_set_notelnet (0, NULL); /* close Telnet */ +sim_ttclose (); /* close console */ + +/* Write command history back to file */ +#ifdef HAVE_READLINE + readline_write_history(); +#endif + +return 0; +} + +/* Find command routine */ + +CTAB *find_cmd (char *gbuf) +{ +CTAB *cmdp = NULL; + +if (sim_vm_cmd) cmdp = find_ctab (sim_vm_cmd, gbuf); /* try ext commands */ +if (cmdp == NULL) cmdp = find_ctab (cmd_table, gbuf); /* try regular cmds */ +return cmdp; +} + +/* Exit command */ + +t_stat exit_cmd (int32 flag, char *cptr) +{ +return SCPE_EXIT; +} + +/* Help command */ + +void fprint_help (FILE *st) +{ +CTAB *cmdp; + +for (cmdp = sim_vm_cmd; cmdp && (cmdp->name != NULL); cmdp++) { + if (cmdp->help) fprintf (st, cmdp->help); + } +for (cmdp = cmd_table; cmdp && (cmdp->name != NULL); cmdp++) { + if (cmdp->help && (!sim_vm_cmd || !find_ctab (sim_vm_cmd, cmdp->name))) + fprintf (st, cmdp->help); + } +return; +} + +t_stat help_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +CTAB *cmdp; + +GET_SWITCHES (cptr); +if (*cptr) { + cptr = get_glyph (cptr, gbuf, 0); + if (*cptr) return SCPE_2MARG; + if (cmdp = find_cmd (gbuf)) { + printf (cmdp->help); + if (sim_log) fprintf (sim_log, cmdp->help); + } + else return SCPE_ARG; + } +else { + fprint_help (stdout); + if (sim_log) fprint_help (sim_log); + } +return SCPE_OK; +} + +/* Spawn command */ + +t_stat spawn_cmd (int32 flag, char *cptr) +{ +if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("SHELL"); +if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = getenv("ComSpec"); +#if defined (VMS) +if ((cptr == NULL) || (strlen (cptr) == 0)) cptr = "SPAWN/INPUT=SYS$COMMAND:"; +#endif +fflush(stdout); /* flush stdout */ +if (sim_log) fflush (sim_log); /* flush log if enabled */ +system (cptr); +#if defined (VMS) +printf ("\n"); +#endif + +return SCPE_OK; +} + +/* Echo command */ + +t_stat echo_cmd (int32 flag, char *cptr) +{ +puts (cptr); +if (sim_log) fprintf (sim_log, "%s\n", cptr); +return SCPE_OK; +} + +/* Do command + + Syntax: DO {-E} {-V} {...} + + -E causes all command errors to be fatal; without it, only EXIT and ASSERT + failure will stop a command file. + + -V causes commands to be echoed before execution. + + Note that SCPE_STEP ("Step expired") is considered a note and not an error + and so does not abort command execution when using -E. + + Inputs: + flag = caller and nesting level indicator + fcptr = filename and optional arguments, space-separated + Outputs: + status = error status + + The "flag" input value indicates the source of the call, as follows: + + -1 = initialization file (no error if not found) + 0 = command line file + 1 = "DO" command + >1 = nested "DO" command +*/ + +#define SCPE_DOFAILED 0040000 /* fail in DO, not subproc */ + +t_stat do_cmd (int32 flag, char *fcptr) +{ +char *cptr, cbuf[CBUFSIZE], gbuf[CBUFSIZE], *c, quote, *do_arg[10]; +FILE *fpin; +CTAB *cmdp; +int32 echo, nargs, errabort; +t_bool interactive, isdo, staying; +t_stat stat; +char *ocptr; + +stat = SCPE_OK; +staying = TRUE; +interactive = (flag > 0); /* issued interactively? */ +if (interactive) { GET_SWITCHES (fcptr); } /* get switches */ +echo = sim_switches & SWMASK ('V'); /* -v means echo */ +errabort = sim_switches & SWMASK ('E'); /* -e means abort on error */ + +c = fcptr; +for (nargs = 0; nargs < 10; ) { /* extract arguments */ + while (isspace (*c)) c++; /* skip blanks */ + if (*c == 0) break; /* all done */ + if (*c == '\'' || *c == '"') quote = *c++; /* quoted string? */ + else quote = 0; + do_arg[nargs++] = c; /* save start */ + while (*c && (quote? (*c != quote): !isspace (*c))) c++; + if (*c) *c++ = 0; /* term at quote/spc */ + } /* end for */ +if (nargs <= 0) return SCPE_2FARG; /* need at least 1 */ +if ((fpin = fopen (do_arg[0], "r")) == NULL) { /* file failed to open? */ + if (flag == 0) /* cmd line file? */ + fprintf (stderr, "Can't open file %s\n", do_arg[0]); + if (flag > 1) + return SCPE_OPENERR | SCPE_DOFAILED; /* return failure with flag */ + else + return SCPE_OPENERR; /* return failure */ + } +if (flag < 1) flag = 1; /* start at level 1 */ + +do { + ocptr = cptr = sim_brk_getact (cbuf, CBUFSIZE); /* get bkpt action */ + if (!ocptr) /* no pending action? */ + ocptr = cptr = read_line (cbuf, CBUFSIZE, fpin); /* get cmd line */ + sub_args (cbuf, gbuf, CBUFSIZE, nargs, do_arg); /* substitute args */ + if (cptr == NULL) { /* EOF? */ + stat = SCPE_OK; /* set good return */ + break; + } + if (*cptr == 0) continue; /* ignore blank */ + if (echo) printf("do> %s\n", cptr); /* echo if -v */ + if (echo && sim_log) fprintf (sim_log, "do> %s\n", cptr); + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + sim_switches = 0; /* init switches */ + isdo = FALSE; + if (cmdp = find_cmd (gbuf)) { /* lookup command */ + isdo = (cmdp->action == &do_cmd); + if (isdo) { /* DO command? */ + if (flag >= DO_NEST_LVL) stat = SCPE_NEST; /* nest too deep? */ + else stat = do_cmd (flag + 1, cptr); /* exec DO cmd */ + } + else stat = cmdp->action (cmdp->arg, cptr); /* exec other cmd */ + } + else stat = SCPE_UNK; /* bad cmd given */ + staying = (stat != SCPE_EXIT) && /* decide if staying */ + (stat != SCPE_AFAIL) && + (!errabort || (stat < SCPE_BASE) || (stat == SCPE_STEP)); + if ((stat >= SCPE_BASE) && (stat != SCPE_EXIT) && /* error from cmd? */ + (stat != SCPE_STEP)) { + if (!echo && !sim_quiet && /* report if not echoing */ + (!isdo || (stat & SCPE_DOFAILED))) { /* and not from DO return */ + printf("%s> %s\n", do_arg[0], ocptr); + if (sim_log) + fprintf (sim_log, "%s> %s\n", do_arg[0], ocptr); + } + stat = stat & ~SCPE_DOFAILED; /* remove possible flag */ + } + if ((staying || !interactive) && /* report error if staying */ + (stat >= SCPE_BASE)) { /* or in cmdline file */ + printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); + if (sim_log) fprintf (sim_log, "%s\n", + scp_error_messages[stat - SCPE_BASE]); + } + if (sim_vm_post != NULL) (*sim_vm_post) (TRUE); + } while (staying); + +fclose (fpin); /* close file */ +return stat; +} + +/* Substitute_args - replace %n tokens in 'instr' with the do command's arguments + + Calling sequence + instr = input string + tmpbuf = temp buffer + maxstr = min (len (instr), len (tmpbuf)) + nargs = number of arguments + do_arg[10] = arguments +*/ + +void sub_args (char *instr, char *tmpbuf, int32 maxstr, int32 nargs, char *do_arg[]) +{ +char *ip, *op, *ap, *oend = tmpbuf + maxstr - 2; + +for (ip = instr, op = tmpbuf; *ip && (op < oend); ) { + if ((*ip == '\\') && (ip[1] == '%')) { /* \% = literal % */ + ip++; /* skip \ */ + if (*ip) *op++ = *ip++; /* copy next */ + } + else if ((*ip == '%') && /* %n = sub */ + ((ip[1] >= '1') && (ip[1] <= ('0'+ nargs - 1)))) { + ap = do_arg[ip[1] - '0']; + ip = ip + 2; + while (*ap && (op < oend)) *op++ = *ap++; /* copy the argument */ + } + else *op++ = *ip++; /* literal character */ + } +*op = 0; /* term buffer */ +strcpy (instr, tmpbuf); +return; +} + +/* Assert command + + Syntax: ASSERT {} {} + + If is not specified, CPU is assumed. is expressed in the radix + specified for . and are the same as that + allowed for examine and deposit search specifications. */ + +t_stat assert_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE], *gptr, *aptr, *tptr; +REG *rptr; +uint32 idx; +t_value val; +t_stat r; + +aptr = cptr; /* save assertion */ +cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_DFT, cptr, &r); /* get sw, default */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph (cptr, gbuf, 0); /* get register */ +rptr = find_reg (gbuf, &gptr, sim_dfdev); /* parse register */ +if (!rptr) return SCPE_NXREG; /* not there */ +if (*gptr == '[') { /* subscript? */ + if (rptr->depth <= 1) return SCPE_ARG; /* array register? */ + idx = (uint32) strtotv (++gptr, &tptr, 10); /* convert index */ + if ((gptr == tptr) || (*tptr++ != ']')) return SCPE_ARG; + gptr = tptr; /* update */ + } +else idx = 0; /* not array */ +if (idx >= rptr->depth) return SCPE_SUB; /* validate subscript */ +if (*gptr != 0) get_glyph (gptr, gbuf, 0); /* more? must be search */ +else { + if (*cptr == 0) return SCPE_2FARG; /* must be more */ + cptr = get_glyph (cptr, gbuf, 0); /* get search cond */ + } +if (*cptr != 0) return SCPE_2MARG; /* must be done */ +if (!get_search (gbuf, rptr->radix, &sim_stab)) /* parse condition */ + return SCPE_MISVAL; +val = get_rval (rptr, idx); /* get register value */ +if (test_search (val, &sim_stab)) return SCPE_OK; /* test condition */ +return SCPE_AFAIL; /* condition fails */ +} + +/* Set command */ + +t_stat set_cmd (int32 flag, char *cptr) +{ +int32 lvl; +t_stat r; +char gbuf[CBUFSIZE], *cvptr, *svptr; +DEVICE *dptr; +UNIT *uptr; +MTAB *mptr; +CTAB *gcmdp; +C1TAB *ctbr, *glbr; + +static CTAB set_glob_tab[] = { + { "CONSOLE", &sim_set_console, 0 }, + { "BREAK", &brk_cmd, SSH_ST }, + { "TELNET", &sim_set_telnet, 0 }, /* deprecated */ + { "NOTELNET", &sim_set_notelnet, 0 }, /* deprecated */ + { "LOG", &sim_set_logon, 0 }, /* deprecated */ + { "NOLOG", &sim_set_logoff, 0 }, /* deprecated */ + { "DEBUG", &sim_set_debon, 0 }, /* deprecated */ + { "NODEBUG", &sim_set_deboff, 0 }, /* deprecated */ + { "THROTTLE", &sim_set_throt, 1 }, + { "NOTHROTTLE", &sim_set_throt, 0 }, + { NULL, NULL, 0 } + }; + +static C1TAB set_dev_tab[] = { + { "OCTAL", &set_dev_radix, 8 }, + { "DECIMAL", &set_dev_radix, 10 }, + { "HEX", &set_dev_radix, 16 }, + { "ENABLED", &set_dev_enbdis, 1 }, + { "DISABLED", &set_dev_enbdis, 0 }, + { "DEBUG", &set_dev_debug, 1 }, + { "NODEBUG", &set_dev_debug, 0 }, + { NULL, NULL, 0 } + }; + +static C1TAB set_unit_tab[] = { + { "ENABLED", &set_unit_enbdis, 1 }, + { "DISABLED", &set_unit_enbdis, 0 }, + { NULL, NULL, 0 } + }; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph (cptr, gbuf, 0); /* get glob/dev/unit */ + +if (dptr = find_dev (gbuf)) { /* device match? */ + uptr = dptr->units; /* first unit */ + ctbr = set_dev_tab; /* global table */ + lvl = MTAB_VDV; /* device match */ + } +else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */ + if (uptr == NULL) return SCPE_NXUN; /* invalid unit */ + ctbr = set_unit_tab; /* global table */ + lvl = MTAB_VUN; /* unit match */ + } +else if (gcmdp = find_ctab (set_glob_tab, gbuf)) /* global? */ + return gcmdp->action (gcmdp->arg, cptr); /* do the rest */ +else return SCPE_NXDEV; /* no match */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ + +while (*cptr != 0) { /* do all mods */ + cptr = get_glyph (svptr = cptr, gbuf, ','); /* get modifier */ + if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */ + for (mptr = dptr->modifiers; mptr && (mptr->mask != 0); mptr++) { + if ((mptr->mstring) && /* match string */ + (MATCH_CMD (gbuf, mptr->mstring) == 0)) { /* matches option? */ + if (mptr->mask & MTAB_XTD) { /* extended? */ + if ((lvl & mptr->mask) == 0) return SCPE_ARG; + if ((lvl & MTAB_VUN) && (uptr->flags & UNIT_DIS)) + return SCPE_UDIS; /* unit disabled? */ + if (mptr->valid) { /* validation rtn? */ + if (cvptr && (mptr->mask & MTAB_NC)) + get_glyph_nc (svptr, gbuf, ','); + r = mptr->valid (uptr, mptr->match, cvptr, mptr->desc); + if (r != SCPE_OK) return r; + } + else if (!mptr->desc) break; /* value desc? */ + else if (mptr->mask & MTAB_VAL) { /* take a value? */ + if (!cvptr) return SCPE_MISVAL; /* none? error */ + r = dep_reg (0, cvptr, (REG *) mptr->desc, 0); + if (r != SCPE_OK) return r; + } + else if (cvptr) return SCPE_ARG; /* = value? */ + else *((int32 *) mptr->desc) = mptr->match; + } /* end if xtd */ + else { /* old style */ + if (cvptr) return SCPE_ARG; /* = value? */ + if (uptr->flags & UNIT_DIS) /* disabled? */ + return SCPE_UDIS; + if ((mptr->valid) && ((r = mptr->valid + (uptr, mptr->match, cvptr, mptr->desc)) + != SCPE_OK)) return r; /* invalid? */ + uptr->flags = (uptr->flags & ~(mptr->mask)) | + (mptr->match & mptr->mask); /* set new value */ + } /* end else xtd */ + break; /* terminate for */ + } /* end if match */ + } /* end for */ + if (!mptr || (mptr->mask == 0)) { /* no match? */ + if (glbr = find_c1tab (ctbr, gbuf)) { /* global match? */ + r = glbr->action (dptr, uptr, glbr->arg, cvptr); /* do global */ + if (r != SCPE_OK) return r; + } + else if (!dptr->modifiers) return SCPE_NOPARAM; /* no modifiers? */ + else return SCPE_NXPAR; + } /* end if no mat */ + } /* end while */ +return SCPE_OK; /* done all */ +} + +/* Match CTAB/CTAB1 name */ + +CTAB *find_ctab (CTAB *tab, char *gbuf) +{ +for (; tab->name != NULL; tab++) { + if (MATCH_CMD (gbuf, tab->name) == 0) return tab; + } +return NULL; +} + +C1TAB *find_c1tab (C1TAB *tab, char *gbuf) +{ +for (; tab->name != NULL; tab++) { + if (MATCH_CMD (gbuf, tab->name) == 0) return tab; + } +return NULL; +} + +/* Set device data radix routine */ + +t_stat set_dev_radix (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr) return SCPE_ARG; +dptr->dradix = flag & 037; +return SCPE_OK; +} + +/* Set device enabled/disabled routine */ + +t_stat set_dev_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +UNIT *up; +uint32 i; + +if (cptr) return SCPE_ARG; +if ((dptr->flags & DEV_DISABLE) == 0) return SCPE_NOFNC;/* allowed? */ +if (flag) { /* enable? */ + if ((dptr->flags & DEV_DIS) == 0) /* already enb? ok */ + return SCPE_OK; + dptr->flags = dptr->flags & ~DEV_DIS; /* no, enable */ + } +else { + if (dptr->flags & DEV_DIS) return SCPE_OK; /* already dsb? ok */ + for (i = 0; i < dptr->numunits; i++) { /* check units */ + up = (dptr->units) + i; /* att or active? */ + if ((up->flags & UNIT_ATT) || sim_is_active (up)) + return SCPE_NOFNC; /* can't do it */ + } + dptr->flags = dptr->flags | DEV_DIS; /* disable */ + } +if (dptr->reset) return dptr->reset (dptr); /* reset device */ +else return SCPE_OK; +} + +/* Set unit enabled/disabled routine */ + +t_stat set_unit_enbdis (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr) return SCPE_ARG; +if (!(uptr->flags & UNIT_DISABLE)) return SCPE_NOFNC; /* allowed? */ +if (flag) uptr->flags = uptr->flags & ~UNIT_DIS; /* enb? enable */ +else { + if ((uptr->flags & UNIT_ATT) || /* dsb */ + sim_is_active (uptr)) return SCPE_NOFNC; /* more tests */ + uptr->flags = uptr->flags | UNIT_DIS; /* disable */ + } +return SCPE_OK; +} + +/* Set device debug enabled/disabled routine */ + +t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEBTAB *dep; + +if ((dptr->flags & DEV_DEBUG) == 0) return SCPE_NOFNC; +if (cptr == NULL) { /* no arguments? */ + dptr->dctrl = flag; /* disable/enable w/o table */ + if (flag && dptr->debflags) { /* enable with table? */ + for (dep = dptr->debflags; dep->name != NULL; dep++) + dptr->dctrl = dptr->dctrl | dep->mask; /* set all */ + } + return SCPE_OK; + } +if (dptr->debflags == NULL) return SCPE_ARG; /* must have table */ +while (*cptr) { + cptr = get_glyph (cptr, gbuf, ';'); /* get debug flag */ + for (dep = dptr->debflags; dep->name != NULL; dep++) { + if (strcmp (dep->name, gbuf) == 0) { /* match? */ + if (flag) dptr->dctrl = dptr->dctrl | dep->mask; + else dptr->dctrl = dptr->dctrl & ~dep->mask; + break; + } + } /* end for */ + if (dep->mask == 0) return SCPE_ARG; /* no match? */ + } /* end while */ +return SCPE_OK; +} + +/* Show command */ + +t_stat show_cmd (int32 flag, char *cptr) +{ +t_stat r; + +cptr = get_sim_opt (CMD_OPT_SW|CMD_OPT_OF, cptr, &r); /* get sw, ofile */ +if (!cptr) return r; /* error? */ +if (sim_ofile) { /* output file? */ + r = show_cmd_fi (sim_ofile, flag, cptr); /* do show */ + fclose (sim_ofile); + } +else { + r = show_cmd_fi (stdout, flag, cptr); /* no, stdout, log */ + if (sim_log) show_cmd_fi (sim_log, flag, cptr); + } +return r; +} + +t_stat show_cmd_fi (FILE *ofile, int32 flag, char *cptr) +{ +int32 lvl; +char gbuf[CBUFSIZE], *cvptr; +DEVICE *dptr; +UNIT *uptr; +MTAB *mptr; +SHTAB *shtb, *shptr; + +static SHTAB show_glob_tab[] = { + { "CONFIGURATION", &show_config, 0 }, + { "DEVICES", &show_config, 1 }, + { "QUEUE", &show_queue, 0 }, + { "TIME", &show_time, 0 }, + { "MODIFIERS", &show_mod_names, 0 }, + { "NAMES", &show_log_names, 0 }, + { "VERSION", &show_version, 1 }, + { "CONSOLE", &sim_show_console, 0 }, + { "BREAK", &show_break, 0 }, + { "LOG", &sim_show_log, 0 }, /* deprecated */ + { "TELNET", &sim_show_telnet, 0 }, /* deprecated */ + { "DEBUG", &sim_show_debug, 0 }, /* deprecated */ + { "THROTTLE", &sim_show_throt, 0 }, + { NULL, NULL, 0 } + }; + +static SHTAB show_dev_tab[] = { + { "RADIX", &show_dev_radix, 0 }, + { "DEBUG", &show_dev_debug, 0 }, + { "MODIFIERS", &show_dev_modifiers, 0 }, + { "NAMES", &show_dev_logicals, 0 }, + { NULL, NULL, 0 } + }; + +static SHTAB show_unit_tab[] = { + { NULL, NULL, 0 } + }; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (shptr = find_shtab (show_glob_tab, gbuf)) /* global? */ + return shptr->action (ofile, NULL, NULL, shptr->arg, cptr); + +if (dptr = find_dev (gbuf)) { /* device match? */ + uptr = dptr->units; /* first unit */ + shtb = show_dev_tab; /* global table */ + lvl = MTAB_VDV; /* device match */ + } +else if (dptr = find_unit (gbuf, &uptr)) { /* unit match? */ + if (uptr == NULL) return SCPE_NXUN; /* invalid unit */ + if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */ + shtb = show_unit_tab; /* global table */ + lvl = MTAB_VUN; /* unit match */ + } +else return SCPE_NXDEV; /* no match */ + +if (*cptr == 0) { /* now eol? */ + return (lvl == MTAB_VDV)? + show_device (ofile, dptr, 0): + show_unit (ofile, dptr, uptr, -1); + } +if (dptr->modifiers == NULL) return SCPE_NOPARAM; /* any modifiers? */ + +while (*cptr != 0) { /* do all mods */ + cptr = get_glyph (cptr, gbuf, ','); /* get modifier */ + if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */ + for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { + if (((mptr->mask & MTAB_XTD)? /* right level? */ + (mptr->mask & lvl): (MTAB_VUN & lvl)) && + ((mptr->disp && mptr->pstring && /* named disp? */ + (MATCH_CMD (gbuf, mptr->pstring) == 0)) || + ((mptr->mask & MTAB_VAL) && /* named value? */ + mptr->mstring && + (MATCH_CMD (gbuf, mptr->mstring) == 0)))) { + if (cvptr && !(mptr->mask & MTAB_SHP)) return SCPE_ARG; + show_one_mod (ofile, dptr, uptr, mptr, cvptr, 1); + break; + } /* end if */ + } /* end for */ + if (mptr->mask == 0) { /* no match? */ + if (shptr = find_shtab (shtb, gbuf)) /* global match? */ + shptr->action (ofile, dptr, uptr, shptr->arg, cptr); + else return SCPE_ARG; + } /* end if */ + } /* end while */ +return SCPE_OK; +} + +SHTAB *find_shtab (SHTAB *tab, char *gbuf) +{ +for (; tab->name != NULL; tab++) { + if (MATCH_CMD (gbuf, tab->name) == 0) return tab; + } +return NULL; +} + +/* Show device and unit */ + +t_stat show_device (FILE *st, DEVICE *dptr, int32 flag) +{ +uint32 j, udbl, ucnt; +UNIT *uptr; + +fprintf (st, "%s", sim_dname (dptr)); /* print dev name */ +if (qdisable (dptr)) { /* disabled? */ + fprintf (st, ", disabled\n"); + return SCPE_OK; + } +for (j = ucnt = udbl = 0; j < dptr->numunits; j++) { /* count units */ + uptr = dptr->units + j; + if (uptr->flags & UNIT_DISABLE) udbl++; + if (!(uptr->flags & UNIT_DIS)) ucnt++; + } +show_all_mods (st, dptr, dptr->units, MTAB_VDV); /* show dev mods */ +if (dptr->numunits == 0) fprintf (st, "\n"); +else { + if (udbl && (ucnt == 0)) fprintf (st, ", all units disabled\n"); + else if (ucnt > 1) fprintf (st, ", %d units\n", ucnt); + else if (flag) fprintf (st, "\n"); + } +if (flag) return SCPE_OK; /* dev only? */ +for (j = 0; j < dptr->numunits; j++) { /* loop thru units */ + uptr = dptr->units + j; + if ((uptr->flags & UNIT_DIS) == 0) + show_unit (st, dptr, uptr, ucnt); + } +return SCPE_OK; +} + +t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag) +{ +int32 u = uptr - dptr->units; + +if (flag > 1) fprintf (st, " %s%d", sim_dname (dptr), u); +else if (flag < 0) fprintf (st, "%s%d", sim_dname (dptr), u); +if (uptr->flags & UNIT_FIX) { + fprintf (st, ", "); + fprint_capac (st, dptr, uptr); + } +if (uptr->flags & UNIT_ATT) { + fprintf (st, ", attached to %s", uptr->filename); + if (uptr->flags & UNIT_RO) fprintf (st, ", read only"); + } +else if (uptr->flags & UNIT_ATTABLE) + fprintf (st, ", not attached"); +show_all_mods (st, dptr, uptr, MTAB_VUN); /* show unit mods */ +fprintf (st, "\n"); +return SCPE_OK; +} + +void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr) +{ +t_addr kval = (uptr->flags & UNIT_BINK)? 1024: 1000; +t_addr mval = kval * kval; +t_addr psize = uptr->capac; +char scale, width; + +if ((dptr->dwidth / dptr->aincr) > 8) width = 'W'; +else width = 'B'; +if (uptr->capac < (kval * 10)) scale = 0; +else if (uptr->capac < (mval * 10)) { + scale = 'K'; + psize = psize / kval; + } +else { + scale = 'M'; + psize = psize / mval; + } +fprint_val (st, (t_value) psize, 10, T_ADDR_W, PV_LEFT); +if (scale) fputc (scale, st); +fputc (width, st); +return; +} + +/* Show processors */ + +t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +int32 vmaj = SIM_MAJOR, vmin = SIM_MINOR, vpat = SIM_PATCH, vdelt = SIM_DELTA; + +if (cptr && (*cptr != 0)) return SCPE_2MARG; +fprintf (st, "%s simulator V%d.%d-%d", sim_name, vmaj, vmin, vpat); +if (vdelt) fprintf (st, "(%d)", vdelt); +if (flag) fprintf (st, " [%s, %s, %s]", sim_si64, sim_sa64, sim_snet); +fprintf (st, "\n"); +return SCPE_OK; +} + +t_stat show_config (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ +int32 i; +DEVICE *dptr; + +if (cptr && (*cptr != 0)) return SCPE_2MARG; +fprintf (st, "%s simulator configuration\n\n", sim_name); +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + show_device (st, dptr, flag); +return SCPE_OK; +} + +t_stat show_log_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ +int32 i; +DEVICE *dptr; + +if (cptr && (*cptr != 0)) return SCPE_2MARG; +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + show_dev_logicals (st, dptr, NULL, 1, cptr); +return SCPE_OK; +} + +t_stat show_dev_logicals (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (dptr->lname) fprintf (st, "%s -> %s\n", dptr->lname, dptr->name); +else if (!flag) fputs ("no logical name assigned\n", st); +return SCPE_OK; +} + +t_stat show_queue (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ +DEVICE *dptr; +UNIT *uptr; +int32 accum; + +if (cptr && (*cptr != 0)) return SCPE_2MARG; +if (sim_clock_queue == NULL) { + fprintf (st, "%s event queue empty, time = %.0f\n", + sim_name, sim_time); + return SCPE_OK; + } +fprintf (st, "%s event queue status, time = %.0f\n", + sim_name, sim_time); +accum = 0; +for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) { + if (uptr == &sim_step_unit) fprintf (st, " Step timer"); + else if ((dptr = find_dev_from_unit (uptr)) != NULL) { + fprintf (st, " %s", sim_dname (dptr)); + if (dptr->numunits > 1) fprintf (st, " unit %d", + (int32) (uptr - dptr->units)); + } + else fprintf (st, " Unknown"); + fprintf (st, " at %d\n", accum + uptr->time); + accum = accum + uptr->time; + } +return SCPE_OK; +} + +t_stat show_time (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) return SCPE_2MARG; +fprintf (st, "Time:\t%.0f\n", sim_time); +return SCPE_OK; +} + +t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +t_stat r; + +if (cptr && (*cptr != 0)) r = ssh_break (st, cptr, 1); /* more? */ +else r = sim_brk_showall (st, sim_switches); +return r; +} + +t_stat show_dev_radix (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +fprintf (st, "Radix=%d\n", dptr->dradix); +return SCPE_OK; +} + +t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +int32 any = 0; +DEBTAB *dep; + +if (dptr->flags & DEV_DEBUG) { + if (dptr->dctrl == 0) fputs ("Debugging disabled", st); + else if (dptr->debflags == NULL) fputs ("Debugging enabled", st); + else { + fputs ("Debug=", st); + for (dep = dptr->debflags; dep->name != NULL; dep++) { + if (dptr->dctrl & dep->mask) { + if (any) fputc (';', st); + fputs (dep->name, st); + any = 1; + } + } + } + fputc ('\n', st); + return SCPE_OK; + } +else return SCPE_NOFNC; +} + +/* Show modifiers */ + +t_stat show_mod_names (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ +int32 i; +DEVICE *dptr; + +if (cptr && (*cptr != 0)) return SCPE_2MARG; /* now eol? */ +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) + show_dev_modifiers (st, dptr, NULL, flag, cptr); +return SCPE_OK; +} + +t_stat show_dev_modifiers (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +int any, enb; +MTAB *mptr; +DEBTAB *dep; + +any = enb = 0; +if (dptr->modifiers) { + for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { + if (mptr->mstring) { + if (strcmp (mptr->mstring, "ENABLED") == 0) enb = 1; + if (any++) fprintf (st, ", %s", mptr->mstring); + else fprintf (st, "%s\t%s", sim_dname (dptr), mptr->mstring); + } + } + } +if (dptr->flags & DEV_DEBUG) { + if (any++) fprintf (st, ", DEBUG, NODEBUG"); + else fprintf (st, "%s\tDEBUG, NODEBUG", sim_dname (dptr)); + } +if (!enb && (dptr->flags & DEV_DISABLE)) { + if (any++) fprintf (st, ", ENABLED, DISABLED"); + else fprintf (st, "%s\tENABLED, DISABLED", sim_dname (dptr)); + } +if (any) fprintf (st, "\n"); +if ((dptr->flags & DEV_DEBUG) && dptr->debflags) { + fprintf (st, "%s\tDEBUG=", sim_dname (dptr)); + for (dep = dptr->debflags; dep->name != NULL; dep++) + fprintf (st, "%s%s", ((dep == dptr->debflags) ? "" : ","), dep->name); + fprintf (st, "\n"); + } +return SCPE_OK; +} + +t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag) +{ +MTAB *mptr; + +if (dptr->modifiers == NULL) return SCPE_OK; +for (mptr = dptr->modifiers; mptr->mask != 0; mptr++) { + if (mptr->pstring && ((mptr->mask & MTAB_XTD)? + ((mptr->mask & flag) && !(mptr->mask & MTAB_NMO)): + ((MTAB_VUN & flag) && ((uptr->flags & mptr->mask) == mptr->match)))) { + fputs (", ", st); + show_one_mod (st, dptr, uptr, mptr, NULL, 0); + } + } +return SCPE_OK; +} + +t_stat show_one_mod (FILE *st, DEVICE *dptr, UNIT *uptr, MTAB *mptr, + char *cptr, int32 flag) +{ +t_value val; + +if (mptr->disp) mptr->disp (st, uptr, mptr->match, cptr? cptr: mptr->desc); +else if ((mptr->mask & MTAB_XTD) && (mptr->mask & MTAB_VAL)) { + REG *rptr = (REG *) mptr->desc; + fprintf (st, "%s=", mptr->pstring); + val = get_rval (rptr, 0); + fprint_val (st, val, rptr->radix, rptr->width, + rptr->flags & REG_FMT); + } +else fputs (mptr->pstring, st); +if (flag && !((mptr->mask & MTAB_XTD) && + (mptr->mask & MTAB_NMO))) fputc ('\n', st); +return SCPE_OK; +} + +/* Breakpoint commands */ + +t_stat brk_cmd (int32 flg, char *cptr) +{ +GET_SWITCHES (cptr); /* get switches */ +return ssh_break (NULL, cptr, flg); /* call common code */ +} + +t_stat ssh_break (FILE *st, char *cptr, int32 flg) +{ +char gbuf[CBUFSIZE], *tptr, *t1ptr, *aptr; +DEVICE *dptr = sim_dflt_dev; +UNIT *uptr = dptr->units; +t_stat r; +t_addr lo, hi, max = uptr->capac - 1; +int32 cnt; + +if (sim_brk_types == 0) return SCPE_NOFNC; +if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR; +if (aptr = strchr (cptr, ';')) { /* ;action? */ + if (flg != SSH_ST) return SCPE_ARG; /* only on SET */ + *aptr++ = 0; /* separate strings */ + } +if (*cptr == 0) { /* no argument? */ + lo = (t_addr) get_rval (sim_PC, 0); /* use PC */ + return ssh_break_one (st, flg, lo, 0, aptr); + } +while (*cptr) { + cptr = get_glyph (cptr, gbuf, ','); + tptr = get_range (dptr, gbuf, &lo, &hi, dptr->aradix, max, 0); + if (tptr == NULL) return SCPE_ARG; + if (*tptr == '[') { + cnt = (int32) strtotv (tptr + 1, &t1ptr, 10); + if ((tptr == t1ptr) || (*t1ptr != ']') || + (flg != SSH_ST)) return SCPE_ARG; + tptr = t1ptr + 1; + } + else cnt = 0; + if (*tptr != 0) return SCPE_ARG; + if ((lo == 0) && (hi == max)) { + if (flg == SSH_CL) sim_brk_clrall (sim_switches); + else if (flg == SSH_SH) sim_brk_showall (st, sim_switches); + else return SCPE_ARG; + } + else { + for ( ; lo <= hi; lo = lo + 1) { + r = ssh_break_one (st, flg, lo, cnt, aptr); + if (r != SCPE_OK) return r; + } + } + } +return SCPE_OK; +} + +t_stat ssh_break_one (FILE *st, int32 flg, t_addr lo, int32 cnt, char *aptr) +{ +switch (flg) { + + case SSH_ST: + return sim_brk_set (lo, sim_switches, cnt, aptr); + break; + + case SSH_CL: + return sim_brk_clr (lo, sim_switches); + break; + + case SSH_SH: + return sim_brk_show (st, lo, sim_switches); + break; + + default: + return SCPE_ARG; + } +} + +/* Reset command and routines + + re[set] reset all devices + re[set] all reset all devices + re[set] device reset specific device +*/ + +t_stat reset_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return (reset_all (0)); /* reset(cr) */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr != 0) return SCPE_2MARG; /* now eol? */ +if (strcmp (gbuf, "ALL") == 0) return (reset_all (0)); +dptr = find_dev (gbuf); /* locate device */ +if (dptr == NULL) return SCPE_NXDEV; /* found it? */ +if (dptr->reset != NULL) return dptr->reset (dptr); +else return SCPE_OK; +} + +/* Reset devices start..end + + Inputs: + start = number of starting device + Outputs: + status = error status +*/ + +t_stat reset_all (uint32 start) +{ +DEVICE *dptr; +uint32 i; +t_stat reason; + +for (i = 0; i < start; i++) { + if (sim_devices[i] == NULL) return SCPE_IERR; + } +for (i = start; (dptr = sim_devices[i]) != NULL; i++) { + if (dptr->reset != NULL) { + reason = dptr->reset (dptr); + if (reason != SCPE_OK) return reason; + } + } +return SCPE_OK; +} + +/* Reset to powerup state + + Inputs: + start = number of starting device + Outputs: + status = error status +*/ + +t_stat reset_all_p (uint32 start) +{ +t_stat r; +int32 old_sw = sim_switches; + +sim_switches = SWMASK ('P'); +r = reset_all (start); +sim_switches = old_sw; +return r; +} + +/* Load and dump commands + + lo[ad] filename {arg} load specified file + du[mp] filename {arg} dump to specified file +*/ + +t_stat load_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +FILE *loadfile; +t_stat reason; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ +loadfile = sim_fopen (gbuf, flag? "wb": "rb"); /* open for wr/rd */ +if (loadfile == NULL) return SCPE_OPENERR; +GET_SWITCHES (cptr); /* get switches */ +reason = sim_load (loadfile, cptr, gbuf, flag); /* load or dump */ +fclose (loadfile); +return reason; +} + +/* Attach command + + at[tach] unit file attach specified unit to file +*/ + +t_stat attach_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; +UNIT *uptr; +t_stat r; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* now eol? */ +dptr = find_unit (gbuf, &uptr); /* locate unit */ +if (dptr == NULL) return SCPE_NXDEV; /* found dev? */ +if (uptr == NULL) return SCPE_NXUN; /* valid unit? */ +if (uptr->flags & UNIT_ATT) { /* already attached? */ + r = scp_detach_unit (dptr, uptr); /* detach it */ + if (r != SCPE_OK) return r; /* error? */ + } +sim_trim_endspc (cptr); /* trim trailing spc */ +return scp_attach_unit (dptr, uptr, cptr); /* attach */ +} + +/* Call device-specific or file-oriented attach unit routine */ + +t_stat scp_attach_unit (DEVICE *dptr, UNIT *uptr, char *cptr) +{ +if (dptr->attach != NULL) /* device routine? */ + return dptr->attach (uptr, cptr); /* call it */ +return attach_unit (uptr, cptr); /* no, std routine */ +} + +/* Attach unit to file */ + +t_stat attach_unit (UNIT *uptr, char *cptr) +{ +DEVICE *dptr; + +if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */ +if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOATT; /* not attachable? */ +if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; +if (dptr->flags & DEV_RAWONLY) return SCPE_NOFNC; /* raw mode only? */ +uptr->filename = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc name buf */ +if (uptr->filename == NULL) return SCPE_MEM; +strncpy (uptr->filename, cptr, CBUFSIZE); /* save name */ +if (sim_switches & SWMASK ('R')) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return attach_err (uptr, SCPE_NORO); /* no, error */ + uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr)); + } +else { /* normal */ + uptr->fileref = sim_fopen (cptr, "rb+"); /* open r/w */ + if (uptr->fileref == NULL) { /* open fail? */ + if ((errno == EROFS) || (errno == EACCES)) { /* read only? */ + if ((uptr->flags & UNIT_ROABLE) == 0) /* allowed? */ + return attach_err (uptr, SCPE_NORO); /* no error */ + uptr->fileref = sim_fopen (cptr, "rb"); /* open rd only */ + if (uptr->fileref == NULL) /* open fail? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + uptr->flags = uptr->flags | UNIT_RO; /* set rd only */ + if (!sim_quiet) printf ("%s: unit is read only\n", sim_dname (dptr)); + } + else { /* doesn't exist */ + if (sim_switches & SWMASK ('E')) /* must exist? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + uptr->fileref = sim_fopen (cptr, "wb+"); /* open new file */ + if (uptr->fileref == NULL) /* open fail? */ + return attach_err (uptr, SCPE_OPENERR); /* yes, error */ + if (!sim_quiet) printf ("%s: creating new file\n", sim_dname (dptr)); + } + } /* end if null */ + } /* end else */ +if (uptr->flags & UNIT_BUFABLE) { /* buffer? */ + uint32 cap = ((uint32) uptr->capac) / dptr->aincr; /* effective size */ + if (uptr->flags & UNIT_MUSTBUF) /* dyn alloc? */ + uptr->filebuf = calloc (cap, SZ_D (dptr)); /* allocate */ + if (uptr->filebuf == NULL) /* no buffer? */ + return attach_err (uptr, SCPE_MEM); /* error */ + if (!sim_quiet) printf ("%s: buffering file in memory\n", sim_dname (dptr)); + uptr->hwmark = sim_fread (uptr->filebuf, /* read file */ + SZ_D (dptr), cap, uptr->fileref); + uptr->flags = uptr->flags | UNIT_BUF; /* set buffered */ + } +uptr->flags = uptr->flags | UNIT_ATT; +uptr->pos = 0; +return SCPE_OK; +} + +t_stat attach_err (UNIT *uptr, t_stat stat) +{ +free (uptr->filename); +uptr->filename = NULL; +return stat; +} + +/* Detach command + + det[ach] all detach all units + det[ach] unit detach specified unit +*/ + +t_stat detach_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; +UNIT *uptr; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr != 0) return SCPE_2MARG; /* now eol? */ +if (strcmp (gbuf, "ALL") == 0) return (detach_all (0, FALSE)); +dptr = find_unit (gbuf, &uptr); /* locate unit */ +if (dptr == NULL) return SCPE_NXDEV; /* found dev? */ +if (uptr == NULL) return SCPE_NXUN; /* valid unit? */ +return scp_detach_unit (dptr, uptr); /* detach */ +} + +/* Detach devices start..end + + Inputs: + start = number of starting device + shutdown = TRUE if simulator shutting down + Outputs: + status = error status + + Note that during shutdown, detach routines for non-attachable devices + will be called. These routines can implement simulator shutdown. +*/ + +t_stat detach_all (int32 start, t_bool shutdown) +{ +uint32 i, j; +DEVICE *dptr; +UNIT *uptr; +t_stat r; + +if ((start < 0) || (start > 1)) return SCPE_IERR; +for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */ + for (j = 0; j < dptr->numunits; j++) { /* loop thru units */ + uptr = (dptr->units) + j; + if ((uptr->flags & UNIT_ATT) || /* attached? */ + (shutdown && dptr->detach && /* shutdown, spec rtn, */ + !(uptr->flags & UNIT_ATTABLE))) { /* !attachable? */ + r = scp_detach_unit (dptr, uptr); /* detach unit */ + if (r != SCPE_OK) return r; + } + } + } +return SCPE_OK; +} + +/* Call device-specific or file-oriented detach unit routine */ + +t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr) +{ +if (dptr->detach != NULL) return dptr->detach (uptr); /* device routine? */ +return detach_unit (uptr); /* no, standard */ +} + +/* Detach unit from file */ + +t_stat detach_unit (UNIT *uptr) +{ +DEVICE *dptr; + +if (uptr == NULL) return SCPE_IERR; +if (!(uptr->flags & UNIT_ATTABLE)) return SCPE_NOATT; /* attachable? */ +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_OK; +if (uptr->flags & UNIT_BUF) { + uint32 cap = (uptr->hwmark + dptr->aincr - 1) / dptr->aincr; + if (uptr->hwmark && ((uptr->flags & UNIT_RO) == 0)) { + if (!sim_quiet) printf ("%s: writing buffer to file\n", sim_dname (dptr)); + rewind (uptr->fileref); + sim_fwrite (uptr->filebuf, SZ_D (dptr), cap, uptr->fileref); + if (ferror (uptr->fileref)) perror ("I/O error"); + } + if (uptr->flags & UNIT_MUSTBUF) { /* dyn alloc? */ + free (uptr->filebuf); /* free buf */ + uptr->filebuf = NULL; + } + uptr->flags = uptr->flags & ~UNIT_BUF; + } +uptr->flags = uptr->flags & ~(UNIT_ATT | UNIT_RO); +free (uptr->filename); +uptr->filename = NULL; +if (fclose (uptr->fileref) == EOF) return SCPE_IOERR; +return SCPE_OK; +} + +/* Assign command + + as[sign] device name assign logical name to device +*/ + +t_stat assign_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* now eol? */ +dptr = find_dev (gbuf); /* locate device */ +if (dptr == NULL) return SCPE_NXDEV; /* found dev? */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr != 0) return SCPE_2MARG; /* must be eol */ +if (find_dev (gbuf)) return SCPE_ARG; /* name in use */ +deassign_device (dptr); /* release current */ +return assign_device (dptr, gbuf); +} + +t_stat assign_device (DEVICE *dptr, char *cptr) +{ +dptr->lname = (char *) calloc (CBUFSIZE, sizeof (char)); +if (dptr->lname == NULL) return SCPE_MEM; +strncpy (dptr->lname, cptr, CBUFSIZE); +return SCPE_OK; +} + +/* Deassign command + + dea[ssign] device deassign logical name +*/ + +t_stat deassign_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (*cptr != 0) return SCPE_2MARG; /* now eol? */ +dptr = find_dev (gbuf); /* locate device */ +if (dptr == NULL) return SCPE_NXDEV; /* found dev? */ +return deassign_device (dptr); +} + +t_stat deassign_device (DEVICE *dptr) +{ +if (dptr->lname) free (dptr->lname); +dptr->lname = NULL; +return SCPE_OK; +} + +/* Get device display name */ + +char *sim_dname (DEVICE *dptr) +{ +return (dptr->lname? dptr->lname: dptr->name); +} + +/* Save command + + sa[ve] filename save state to specified file +*/ + +t_stat save_cmd (int32 flag, char *cptr) +{ +FILE *sfile; +t_stat r; +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +sim_trim_endspc (cptr); +if ((sfile = sim_fopen (cptr, "wb")) == NULL) return SCPE_OPENERR; +r = sim_save (sfile); +fclose (sfile); +return r; +} + +t_stat sim_save (FILE *sfile) +{ +void *mbuf; +int32 l, t; +uint32 i, j; +t_addr k, high; +t_value val; +t_stat r; +t_bool zeroflg; +size_t sz; +DEVICE *dptr; +UNIT *uptr; +REG *rptr; + +#define WRITE_I(xx) sim_fwrite (&(xx), sizeof (xx), 1, sfile) + +fprintf (sfile, "%s\n%s\n%s\n%s\n%s\n%.0f\n", + save_vercur, /* [V2.5] save format */ + sim_name, /* sim name */ + sim_si64, sim_sa64, sim_snet, /* [V3.5] options */ + sim_time); /* [V3.2] sim time */ +WRITE_I (sim_rtime); /* [V2.6] sim rel time */ + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */ + fputs (dptr->name, sfile); /* device name */ + fputc ('\n', sfile); + if (dptr->lname) fputs (dptr->lname, sfile); /* [V3.0] logical name */ + fputc ('\n', sfile); + WRITE_I (dptr->flags); /* [V2.10] flags */ + for (j = 0; j < dptr->numunits; j++) { + uptr = dptr->units + j; + t = sim_is_active (uptr); + WRITE_I (j); /* unit number */ + WRITE_I (t); /* activation time */ + WRITE_I (uptr->u3); /* unit specific */ + WRITE_I (uptr->u4); + WRITE_I (uptr->u5); /* [V3.0] more unit */ + WRITE_I (uptr->u6); + WRITE_I (uptr->flags); /* [V2.10] flags */ + WRITE_I (uptr->capac); /* [V3.5] capacity */ + if (uptr->flags & UNIT_ATT) fputs (uptr->filename, sfile); + fputc ('\n', sfile); + if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) == UNIT_FIX) && + (dptr->examine != NULL) && + ((high = uptr->capac) != 0)) { /* memory-like unit? */ + WRITE_I (high); /* [V2.5] write size */ + sz = SZ_D (dptr); + if ((mbuf = calloc (SRBSIZ, sz)) == NULL) { + fclose (sfile); + return SCPE_MEM; + } + for (k = 0; k < high; ) { /* loop thru mem */ + zeroflg = TRUE; + for (l = 0; (l < SRBSIZ) && (k < high); l++, + k = k + (dptr->aincr)) { /* check for 0 block */ + r = dptr->examine (&val, k, uptr, SIM_SW_REST); + if (r != SCPE_OK) return r; + if (val) zeroflg = FALSE; + SZ_STORE (sz, val, mbuf, l); + } /* end for l */ + if (zeroflg) { /* all zero's? */ + l = -l; /* invert block count */ + WRITE_I (l); /* write only count */ + } + else { + WRITE_I (l); /* block count */ + sim_fwrite (mbuf, sz, l, sfile); + } + } /* end for k */ + free (mbuf); /* dealloc buffer */ + } /* end if mem */ + else { /* no memory */ + high = 0; /* write 0 */ + WRITE_I (high); + } /* end else mem */ + } /* end unit loop */ + t = -1; /* end units */ + WRITE_I (t); /* write marker */ + for (rptr = dptr->registers; (rptr != NULL) && /* loop thru regs */ + (rptr->name != NULL); rptr++) { + fputs (rptr->name, sfile); /* name */ + fputc ('\n', sfile); + WRITE_I (rptr->depth); /* [V2.10] depth */ + for (j = 0; j < rptr->depth; j++) { /* loop thru values */ + val = get_rval (rptr, j); /* get value */ + WRITE_I (val); /* store */ + } + } + fputc ('\n', sfile); /* end registers */ + } +fputc ('\n', sfile); /* end devices */ +return (ferror (sfile))? SCPE_IOERR: SCPE_OK; /* error during save? */ +} + +/* Restore command + + re[store] filename restore state from specified file +*/ + +t_stat restore_cmd (int32 flag, char *cptr) +{ +FILE *rfile; +t_stat r; + +GET_SWITCHES (cptr); /* get switches */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +sim_trim_endspc (cptr); +if ((rfile = sim_fopen (cptr, "rb")) == NULL) return SCPE_OPENERR; +r = sim_rest (rfile); +fclose (rfile); +return r; +} + +t_stat sim_rest (FILE *rfile) +{ +char buf[CBUFSIZE]; +void *mbuf; +int32 j, blkcnt, limit, unitno, time, flg; +uint32 us, depth; +t_addr k, high, old_capac; +t_value val, mask; +t_stat r; +size_t sz; +t_bool v35, v32; +DEVICE *dptr; +UNIT *uptr; +REG *rptr; + +#define READ_S(xx) if (read_line ((xx), CBUFSIZE, rfile) == NULL) \ + return SCPE_IOERR; +#define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \ + return SCPE_IOERR; + +READ_S (buf); /* [V2.5+] read version */ +v35 = v32 = FALSE; +if (strcmp (buf, save_vercur) == 0) v35 = v32 = TRUE; /* version 3.5? */ +else if (strcmp (buf, save_ver32) == 0) v32 = TRUE; /* version 3.2? */ +else if (strcmp (buf, save_ver30) != 0) { /* version 3.0? */ + printf ("Invalid file version: %s\n", buf); + return SCPE_INCOMP; + } +READ_S (buf); /* read sim name */ +if (strcmp (buf, sim_name)) { /* name match? */ + printf ("Wrong system type: %s\n", buf); + return SCPE_INCOMP; + } +if (v35) { /* [V3.5+] options */ + READ_S (buf); /* integer size */ + if (strcmp (buf, sim_si64) != 0) { + printf ("Incompatible integer size, save file = %s\n", buf); + return SCPE_INCOMP; + } + READ_S (buf); /* address size */ + if (strcmp (buf, sim_sa64) != 0) { + printf ("Incompatible address size, save file = %s\n", buf); + return SCPE_INCOMP; + } + READ_S (buf); /* Ethernet */ + } +if (v32) { /* [V3.2+] time as string */ + READ_S (buf); + sscanf (buf, "%lf", &sim_time); + } +else READ_I (sim_time); /* sim time */ +READ_I (sim_rtime); /* [V2.6+] sim rel time */ + +for ( ;; ) { /* device loop */ + READ_S (buf); /* read device name */ + if (buf[0] == 0) break; /* last? */ + if ((dptr = find_dev (buf)) == NULL) { /* locate device */ + printf ("Invalid device name: %s\n", buf); + return SCPE_INCOMP; + } + READ_S (buf); /* [V3.0+] logical name */ + deassign_device (dptr); /* delete old name */ + if ((buf[0] != 0) && + ((r = assign_device (dptr, buf)) != SCPE_OK)) return r; + READ_I (flg); /* [V2.10+] ctlr flags */ + if (!v32) flg = ((flg & DEV_UFMASK_31) << (DEV_V_UF - DEV_V_UF_31)) | + (flg & ~DEV_UFMASK_31); /* [V3.2+] flags moved */ + dptr->flags = (dptr->flags & ~DEV_RFLAGS) | /* restore ctlr flags */ + (flg & DEV_RFLAGS); + for ( ;; ) { /* unit loop */ + sim_switches = SIM_SW_REST; /* flag rstr, clr RO */ + READ_I (unitno); /* unit number */ + if (unitno < 0) break; /* end units? */ + if ((uint32) unitno >= dptr->numunits) { /* too big? */ + printf ("Invalid unit number: %s%d\n", sim_dname (dptr), unitno); + return SCPE_INCOMP; + } + READ_I (time); /* event time */ + uptr = (dptr->units) + unitno; + sim_cancel (uptr); + if (time > 0) sim_activate (uptr, time - 1); + READ_I (uptr->u3); /* device specific */ + READ_I (uptr->u4); + READ_I (uptr->u5); /* [V3.0+] more dev spec */ + READ_I (uptr->u6); + READ_I (flg); /* [V2.10+] unit flags */ + old_capac = uptr->capac; /* save current capacity */ + if (v35) { /* [V3.5+] capacity */ + READ_I (uptr->capac); + } + if (!v32) flg = ((flg & UNIT_UFMASK_31) << (UNIT_V_UF - UNIT_V_UF_31)) | + (flg & ~UNIT_UFMASK_31); /* [V3.2+] flags moved */ + uptr->flags = (uptr->flags & ~UNIT_RFLAGS) | + (flg & UNIT_RFLAGS); /* restore */ + READ_S (buf); /* attached file */ + if ((uptr->flags & UNIT_ATTABLE) && /* if attachable and */ + (!(dptr->flags & DEV_NET) || /* not net dev or */ + !(uptr->flags & UNIT_ATT) || /* not currently att */ + (buf[0] == 0))) { /* or will not be att */ + r = scp_detach_unit (dptr, uptr); /* detach old */ + if (r != SCPE_OK) return r; + if (buf[0] != 0) { /* any file? */ + uptr->flags = uptr->flags & ~UNIT_DIS; + if (flg & UNIT_RO) /* [V2.10+] saved flgs & RO? */ + sim_switches |= SWMASK ('R'); /* RO attach */ + r = scp_attach_unit (dptr, uptr, buf); + if (r != SCPE_OK) return r; + } + } + READ_I (high); /* memory capacity */ + if (high > 0) { /* [V2.5+] any memory? */ + if (((uptr->flags & (UNIT_FIX + UNIT_ATTABLE)) != UNIT_FIX) || + (dptr->deposit == NULL)) { + printf ("Can't restore memory: %s%d\n", sim_dname (dptr), unitno); + return SCPE_INCOMP; + } + if (high != old_capac) { /* size change? */ + uptr->capac = old_capac; /* temp restore old */ + if ((dptr->flags & DEV_DYNM) && + ((dptr->msize == NULL) || + (dptr->msize (uptr, (int32) high, NULL, NULL) != SCPE_OK))) { + printf ("Can't change memory size: %s%d\n", + sim_dname (dptr), unitno); + return SCPE_INCOMP; + } + uptr->capac = high; /* new memory size */ + printf ("Memory size changed: %s%d = ", sim_dname (dptr), unitno); + fprint_capac (stdout, dptr, uptr); + printf ("\n"); + } + sz = SZ_D (dptr); /* allocate buffer */ + if ((mbuf = calloc (SRBSIZ, sz)) == NULL) + return SCPE_MEM; + for (k = 0; k < high; ) { /* loop thru mem */ + READ_I (blkcnt); /* block count */ + if (blkcnt < 0) limit = -blkcnt; /* compressed? */ + else limit = sim_fread (mbuf, sz, blkcnt, rfile); + if (limit <= 0) return SCPE_IOERR; /* invalid or err? */ + for (j = 0; j < limit; j++, k = k + (dptr->aincr)) { + if (blkcnt < 0) val = 0; /* compressed? */ + else SZ_LOAD (sz, val, mbuf, j); /* saved value */ + r = dptr->deposit (val, k, uptr, SIM_SW_REST); + if (r != SCPE_OK) return r; + } /* end for j */ + } /* end for k */ + free (mbuf); /* dealloc buffer */ + } /* end if high */ + } /* end unit loop */ + for ( ;; ) { /* register loop */ + READ_S (buf); /* read reg name */ + if (buf[0] == 0) break; /* last? */ + READ_I (depth); /* [V2.10+] depth */ + if ((rptr = find_reg (buf, NULL, dptr)) == NULL) { + printf ("Invalid register name: %s %s\n", sim_dname (dptr), buf); + for (us = 0; us < depth; us++) { /* skip values */ + READ_I (val); + } + continue; + } + if (depth != rptr->depth) /* [V2.10+] mismatch? */ + printf ("Register depth mismatch: %s %s, file = %d, sim = %d\n", + sim_dname (dptr), buf, depth, rptr->depth); + mask = width_mask[rptr->width]; /* get mask */ + for (us = 0; us < depth; us++) { /* loop thru values */ + READ_I (val); /* read value */ + if (val > mask) /* value ok? */ + printf ("Invalid register value: %s %s\n", sim_dname (dptr), buf); + else if (us < rptr->depth) /* in range? */ + put_rval (rptr, us, val); + } + } + } /* end device loop */ +return SCPE_OK; +} + +/* Run, go, cont, step commands + + ru[n] [new PC] reset and start simulation + go [new PC] start simulation + co[nt] start simulation + s[tep] [step limit] start simulation for 'limit' instructions + b[oot] device bootstrap from device and start simulation +*/ + +t_stat run_cmd (int32 flag, char *cptr) +{ +char *tptr, gbuf[CBUFSIZE]; +uint32 i, j; +int32 unitno; +t_value pcv; +t_stat r; +DEVICE *dptr; +UNIT *uptr; +void int_handler (int signal); + +GET_SWITCHES (cptr); /* get switches */ +sim_step = 0; +if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */ + if (*cptr != 0) { /* argument? */ + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) return SCPE_2MARG; /* should be end */ + if (sim_vm_parse_addr) /* address parser? */ + pcv = sim_vm_parse_addr (sim_dflt_dev, gbuf, &tptr); + else pcv = strtotv (gbuf, &tptr, sim_PC->radix);/* parse PC */ + if ((tptr == gbuf) || (*tptr != 0) || /* error? */ + (pcv > width_mask[sim_PC->width])) return SCPE_ARG; + put_rval (sim_PC, 0, pcv); + } + if ((flag == RU_RUN) && /* run? */ + ((r = run_boot_prep ()) != SCPE_OK)) return r; /* reset sim */ + } + +else if (flag == RU_STEP) { /* step */ + if (*cptr != 0) { /* argument? */ + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) return SCPE_2MARG; /* should be end */ + sim_step = (int32) get_uint (gbuf, 10, INT_MAX, &r); + if ((r != SCPE_OK) || (sim_step <= 0)) /* error? */ + return SCPE_ARG; + } + else sim_step = 1; + } + +else if (flag == RU_BOOT) { /* boot */ + if (*cptr == 0) return SCPE_2FARG; /* must be more */ + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) return SCPE_2MARG; /* should be end */ + dptr = find_unit (gbuf, &uptr); /* locate unit */ + if (dptr == NULL) return SCPE_NXDEV; /* found dev? */ + if (uptr == NULL) return SCPE_NXUN; /* valid unit? */ + if (dptr->boot == NULL) return SCPE_NOFNC; /* can it boot? */ + if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */ + if ((uptr->flags & UNIT_ATTABLE) && /* if attable, att? */ + !(uptr->flags & UNIT_ATT)) return SCPE_UNATT; + unitno = (int32) (uptr - dptr->units); /* recover unit# */ + if ((r = run_boot_prep ()) != SCPE_OK) return r; /* reset sim */ + if ((r = dptr->boot (unitno, dptr)) != SCPE_OK) /* boot device */ + return r; + } + +else if (flag != RU_CONT) return SCPE_IERR; /* must be cont */ + +for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* reposition all */ + for (j = 0; j < dptr->numunits; j++) { /* seq devices */ + uptr = dptr->units + j; + if ((uptr->flags & (UNIT_ATT + UNIT_SEQ)) == + (UNIT_ATT + UNIT_SEQ)) + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); + } + } +stop_cpu = 0; +if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */ + return SCPE_SIGERR; + } +if (sim_ttrun () != SCPE_OK) { /* set console mode */ + sim_ttcmd (); + return SCPE_TTYERR; + } +if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */ + sim_ttcmd (); + return r; + } +if (sim_step) sim_activate (&sim_step_unit, sim_step); /* set step timer */ +sim_throt_sched (); /* set throttle */ +sim_is_running = 1; /* flag running */ +sim_brk_clract (); /* defang actions */ +sim_rtcn_init_all (); /* re-init clocks */ +r = sim_instr(); + +sim_is_running = 0; /* flag idle */ +sim_ttcmd (); /* restore console */ +signal (SIGINT, SIG_DFL); /* cancel WRU */ +sim_cancel (&sim_step_unit); /* cancel step timer */ +sim_throt_cancel (); /* cancel throttle */ +if (sim_clock_queue != NULL) { /* update sim time */ + UPDATE_SIM_TIME (sim_clock_queue->time); + } +else { + UPDATE_SIM_TIME (noqueue_time); + } +if (sim_log) fflush (sim_log); /* flush console log */ +if (sim_deb) fflush (sim_deb); /* flush debug log */ +for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files */ + for (j = 0; j < dptr->numunits; j++) { /* if not buffered in mem */ + uptr = dptr->units + j; + if ((uptr->flags & UNIT_ATT) && /* attached, */ + !(uptr->flags & UNIT_BUF) && /* not buffered, */ + (uptr->fileref) && /* real file, */ + !(uptr->flags & UNIT_RAW) && /* not raw, */ + !(uptr->flags & UNIT_RO)) /* not read only? */ + fflush (uptr->fileref); + } + } +#if defined (VMS) +printf ("\n"); +#endif +fprint_stopped (stdout, r); /* print msg */ +if (sim_log) fprint_stopped (sim_log, r); /* log if enabled */ +return SCPE_OK; +} + +/* Common setup for RUN or BOOT */ + +t_stat run_boot_prep (void) +{ +sim_interval = 0; /* reset queue */ +sim_time = sim_rtime = 0; +noqueue_time = 0; +sim_clock_queue = NULL; +return reset_all_p (0); +} + +/* Print stopped message */ + +void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr) +{ +int32 i; +t_stat r = 0; +t_addr k; +t_value pcval; + +if (v >= SCPE_BASE) fprintf (st, "\n%s, %s: ", + scp_error_messages[v - SCPE_BASE], pc->name); +else fprintf (st, "\n%s, %s: ", sim_stop_messages[v], pc->name); +pcval = get_rval (pc, 0); +if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, (t_addr) pcval); +else fprint_val (st, pcval, pc->radix, pc->width, + pc->flags & REG_FMT); +if ((dptr != NULL) && (dptr->examine != NULL)) { + for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; + for (i = 0, k = (t_addr) pcval; i < sim_emax; i++, k = k + dptr->aincr) { + if ((r = dptr->examine (&sim_eval[i], k, dptr->units, + SWMASK ('V'))) != SCPE_OK) break; + } + if ((r == SCPE_OK) || (i > 0)) { + fprintf (st, " ("); + if (fprint_sym (st, (t_addr) pcval, sim_eval, NULL, SWMASK('M')|SIM_SW_STOP) > 0) + fprint_val (st, sim_eval[0], dptr->dradix, dptr->dwidth, PV_RZRO); + fprintf (st, ")"); + } + } +fprintf (st, "\n"); +return; +} + +void fprint_stopped (FILE *st, t_stat v) +{ +fprint_stopped_gen (st, v, sim_PC, sim_dflt_dev); +return; +} + +/* Unit service for step timeout, originally scheduled by STEP n command + Return step timeout SCP code, will cause simulation to stop */ + +t_stat step_svc (UNIT *uptr) +{ +return SCPE_STEP; +} + +/* Cancel scheduled step service */ + +t_stat sim_cancel_step (void) +{ +return sim_cancel (&sim_step_unit); +} + +/* Signal handler for ^C signal - set stop simulation flag */ + +void int_handler (int sig) +{ +stop_cpu = 1; +return; +} + +/* Examine/deposit commands + + ex[amine] [modifiers] list examine + de[posit] [modifiers] list val deposit + ie[xamine] [modifiers] list interactive examine + id[eposit] [modifiers] list interactive deposit + + modifiers + @filename output file + -letter(s) switches + devname'n device name and unit number + [{&|^}value]{=|==|!|!=|>|>=|<|<=} value search specification + + list list of addresses and registers + addr[:addr|-addr] address range + ALL all addresses + register[:register|-register] register range + STATE all registers +*/ + +t_stat exdep_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE], *gptr, *tptr; +int32 opt; +t_addr low, high; +t_stat reason; +DEVICE *tdptr; +REG *lowr, *highr; +FILE *ofile; + +opt = CMD_OPT_SW|CMD_OPT_SCH|CMD_OPT_DFT; /* options for all */ +if (flag == EX_E) opt = opt | CMD_OPT_OF; /* extra for EX */ +cptr = get_sim_opt (opt, cptr, &reason); /* get cmd options */ +if (!cptr) return reason; /* error? */ +if (*cptr == 0) return SCPE_2FARG; /* must be more */ +if (sim_dfunit == NULL) return SCPE_NXUN; /* got a unit? */ +cptr = get_glyph (cptr, gbuf, 0); /* get list */ +if ((flag == EX_D) && (*cptr == 0)) return SCPE_2FARG; /* deposit needs more */ +ofile = sim_ofile? sim_ofile: stdout; /* no ofile? use stdout */ + +for (gptr = gbuf, reason = SCPE_OK; + (*gptr != 0) && (reason == SCPE_OK); gptr = tptr) { + tdptr = sim_dfdev; /* working dptr */ + if (strncmp (gptr, "STATE", strlen ("STATE")) == 0) { + tptr = gptr + strlen ("STATE"); + if (*tptr && (*tptr++ != ',')) return SCPE_ARG; + if ((lowr = sim_dfdev->registers) == NULL) return SCPE_NXREG; + for (highr = lowr; highr->name != NULL; highr++) ; + sim_switches = sim_switches | SIM_SW_HIDE; + reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr, + lowr, --highr, 0, 0); + continue; + } + + if ((lowr = find_reg (gptr, &tptr, tdptr)) || /* local reg or */ + (!(sim_opt_out & CMD_OPT_DFT) && /* no dflt, global? */ + (lowr = find_reg_glob (gptr, &tptr, &tdptr)))) { + low = high = 0; + if ((*tptr == '-') || (*tptr == ':')) { + highr = find_reg (tptr + 1, &tptr, tdptr); + if (highr == NULL) return SCPE_NXREG; + } + else { + highr = lowr; + if (*tptr == '[') { + if (lowr->depth <= 1) return SCPE_ARG; + tptr = get_range (NULL, tptr + 1, &low, &high, + 10, lowr->depth - 1, ']'); + if (tptr == NULL) return SCPE_ARG; + } + } + if (*tptr && (*tptr++ != ',')) return SCPE_ARG; + reason = exdep_reg_loop (ofile, sim_schptr, flag, cptr, + lowr, highr, (uint32) low, (uint32) high); + continue; + } + + tptr = get_range (sim_dfdev, gptr, &low, &high, sim_dfdev->aradix, + (((sim_dfunit->capac == 0) || (flag == EX_E))? 0: + sim_dfunit->capac - sim_dfdev->aincr), 0); + if (tptr == NULL) return SCPE_ARG; + if (*tptr && (*tptr++ != ',')) return SCPE_ARG; + reason = exdep_addr_loop (ofile, sim_schptr, flag, cptr, low, high, + sim_dfdev, sim_dfunit); + } /* end for */ +if (sim_ofile) fclose (sim_ofile); /* close output file */ +return reason; +} + +/* Loop controllers for examine/deposit + + exdep_reg_loop examine/deposit range of registers + exdep_addr_loop examine/deposit range of addresses +*/ + +t_stat exdep_reg_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + REG *lowr, REG *highr, uint32 lows, uint32 highs) +{ +t_stat reason; +uint32 idx; +t_value val; +REG *rptr; + +if ((lowr == NULL) || (highr == NULL)) return SCPE_IERR; +if (lowr > highr) return SCPE_ARG; +for (rptr = lowr; rptr <= highr; rptr++) { + if ((sim_switches & SIM_SW_HIDE) && + (rptr->flags & REG_HIDDEN)) continue; + for (idx = lows; idx <= highs; idx++) { + if (idx >= rptr->depth) return SCPE_SUB; + val = get_rval (rptr, idx); + if (schptr && !test_search (val, schptr)) continue; + if (flag != EX_D) { + reason = ex_reg (ofile, val, flag, rptr, idx); + if (reason != SCPE_OK) return reason; + if (sim_log && (ofile == stdout)) + ex_reg (sim_log, val, flag, rptr, idx); + } + if (flag != EX_E) { + reason = dep_reg (flag, cptr, rptr, idx); + if (reason != SCPE_OK) return reason; + } + } + } +return SCPE_OK; +} + +t_stat exdep_addr_loop (FILE *ofile, SCHTAB *schptr, int32 flag, char *cptr, + t_addr low, t_addr high, DEVICE *dptr, UNIT *uptr) +{ +t_addr i, mask; +t_stat reason; + +if (uptr->flags & UNIT_DIS) return SCPE_UDIS; /* disabled? */ +mask = (t_addr) width_mask[dptr->awidth]; +if ((low > mask) || (high > mask) || (low > high)) return SCPE_ARG; +for (i = low; i <= high; ) { /* all paths must incr!! */ + reason = get_aval (i, dptr, uptr); /* get data */ + if (reason != SCPE_OK) return reason; /* return if error */ + if (schptr && !test_search (sim_eval[0], schptr)) + i = i + dptr->aincr; /* sch fails, incr */ + else { /* no sch or success */ + if (flag != EX_D) { /* ex, ie, or id? */ + reason = ex_addr (ofile, flag, i, dptr, uptr); + if (reason > SCPE_OK) return reason; + if (sim_log && (ofile == stdout)) + ex_addr (sim_log, flag, i, dptr, uptr); + } + else reason = 1 - dptr->aincr; /* no, dflt incr */ + if (flag != EX_E) { /* ie, id, or d? */ + reason = dep_addr (flag, cptr, i, dptr, uptr, reason); + if (reason > SCPE_OK) return reason; + } + i = i + (1 - reason); /* incr */ + } + } +return SCPE_OK; +} + +/* Examine register routine + + Inputs: + ofile = output stream + val = current register value + flag = type of ex/mod command (ex, iex, idep) + rptr = pointer to register descriptor + idx = index + Outputs: + return = error status +*/ + +t_stat ex_reg (FILE *ofile, t_value val, int32 flag, REG *rptr, uint32 idx) +{ +int32 rdx; + +if (rptr == NULL) return SCPE_IERR; +if (rptr->depth > 1) fprintf (ofile, "%s[%d]:\t", rptr->name, idx); +else fprintf (ofile, "%s:\t", rptr->name); +if (!(flag & EX_E)) return SCPE_OK; +GET_RADIX (rdx, rptr->radix); +if ((rptr->flags & REG_VMAD) && sim_vm_fprint_addr) + sim_vm_fprint_addr (ofile, sim_dflt_dev, (t_addr) val); +else if (!(rptr->flags & REG_VMIO) || + (fprint_sym (ofile, rdx, &val, NULL, sim_switches | SIM_SW_REG) > 0)) + fprint_val (ofile, val, rdx, rptr->width, rptr->flags & REG_FMT); +if (flag & EX_I) fprintf (ofile, "\t"); +else fprintf (ofile, "\n"); +return SCPE_OK; +} + +/* Get register value + + Inputs: + rptr = pointer to register descriptor + idx = index + Outputs: + return = register value +*/ + +t_value get_rval (REG *rptr, uint32 idx) +{ +size_t sz; +t_value val; +UNIT *uptr; + +sz = SZ_R (rptr); +if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) { + idx = idx + rptr->qptr; + if (idx >= rptr->depth) idx = idx - rptr->depth; + } +if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) { + uptr = ((UNIT *) rptr->loc) + idx; +#if defined (USE_INT64) + if (sz <= sizeof (uint32)) val = *((uint32 *) uptr); + else val = *((t_uint64 *) uptr); +#else + val = *((uint32 *) uptr); +#endif + } +else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint8))) + val = *(((uint8 *) rptr->loc) + idx); +else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint16))) + val = *(((uint16 *) rptr->loc) + idx); +#if defined (USE_INT64) +else if (sz <= sizeof (uint32)) + val = *(((uint32 *) rptr->loc) + idx); +else val = *(((t_uint64 *) rptr->loc) + idx); +#else +else val = *(((uint32 *) rptr->loc) + idx); +#endif +val = (val >> rptr->offset) & width_mask[rptr->width]; +return val; +} + +/* Deposit register routine + + Inputs: + flag = type of deposit (normal/interactive) + cptr = pointer to input string + rptr = pointer to register descriptor + idx = index + Outputs: + return = error status +*/ + +t_stat dep_reg (int32 flag, char *cptr, REG *rptr, uint32 idx) +{ +t_stat r; +t_value val, mask; +int32 rdx; +char *tptr, gbuf[CBUFSIZE]; + +if ((cptr == NULL) || (rptr == NULL)) return SCPE_IERR; +if (rptr->flags & REG_RO) return SCPE_RO; +if (flag & EX_I) { + cptr = read_line (gbuf, CBUFSIZE, stdin); + if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr); + if (cptr == NULL) return 1; /* force exit */ + if (*cptr == 0) return SCPE_OK; /* success */ + } +mask = width_mask[rptr->width]; +GET_RADIX (rdx, rptr->radix); +if ((rptr->flags & REG_VMAD) && sim_vm_parse_addr) { /* address form? */ + val = sim_vm_parse_addr (sim_dflt_dev, cptr, &tptr); + if ((tptr == cptr) || (*tptr != 0) || + (val > mask)) return SCPE_ARG; + } +else if (!(rptr->flags & REG_VMIO) || /* dont use sym? */ + (parse_sym (cptr, rdx, NULL, &val, sim_switches | SIM_SW_REG) > SCPE_OK)) { + val = get_uint (cptr, rdx, mask, &r); + if (r != SCPE_OK) return SCPE_ARG; + } +if ((rptr->flags & REG_NZ) && (val == 0)) return SCPE_ARG; +put_rval (rptr, idx, val); +return SCPE_OK; +} + +/* Put register value + + Inputs: + rptr = pointer to register descriptor + idx = index + val = new value + mask = mask + Outputs: + none +*/ + +void put_rval (REG *rptr, uint32 idx, t_value val) +{ +size_t sz; +t_value mask; +UNIT *uptr; + +#define PUT_RVAL(sz,rp,id,v,m) \ + *(((sz *) rp->loc) + id) = \ + (*(((sz *) rp->loc) + id) & \ + ~((m) << (rp)->offset)) | ((v) << (rp)->offset) + +if (rptr == sim_PC) sim_brk_npc (0); +sz = SZ_R (rptr); +mask = width_mask[rptr->width]; +if ((rptr->depth > 1) && (rptr->flags & REG_CIRC)) { + idx = idx + rptr->qptr; + if (idx >= rptr->depth) idx = idx - rptr->depth; + } +if ((rptr->depth > 1) && (rptr->flags & REG_UNIT)) { + uptr = ((UNIT *) rptr->loc) + idx; +#if defined (USE_INT64) + if (sz <= sizeof (uint32)) + *((uint32 *) uptr) = (*((uint32 *) uptr) & + ~(((uint32) mask) << rptr->offset)) | + (((uint32) val) << rptr->offset); + else *((t_uint64 *) uptr) = (*((t_uint64 *) uptr) + & ~(mask << rptr->offset)) | (val << rptr->offset); +#else + *((uint32 *) uptr) = (*((uint32 *) uptr) & + ~(((uint32) mask) << rptr->offset)) | + (((uint32) val) << rptr->offset); +#endif + } +else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint8))) + PUT_RVAL (uint8, rptr, idx, (uint32) val, (uint32) mask); +else if (((rptr->depth > 1) || (rptr->flags & REG_FIT)) && + (sz == sizeof (uint16))) + PUT_RVAL (uint16, rptr, idx, (uint32) val, (uint32) mask); +#if defined (USE_INT64) +else if (sz <= sizeof (uint32)) + PUT_RVAL (uint32, rptr, idx, (int32) val, (uint32) mask); +else PUT_RVAL (t_uint64, rptr, idx, val, mask); +#else +else PUT_RVAL (uint32, rptr, idx, val, mask); +#endif +return; +} + +/* Examine address routine + + Inputs: (sim_eval is an implicit argument) + ofile = output stream + flag = type of ex/mod command (ex, iex, idep) + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + Outputs: + return = if > 0, error status + if <= 0,-number of extra addr units retired +*/ + +t_stat ex_addr (FILE *ofile, int32 flag, t_addr addr, DEVICE *dptr, UNIT *uptr) +{ +t_stat reason; +int32 rdx; + +if (sim_vm_fprint_addr) sim_vm_fprint_addr (ofile, dptr, addr); +else fprint_val (ofile, addr, dptr->aradix, dptr->awidth, PV_LEFT); +fprintf (ofile, ":\t"); +if (!(flag & EX_E)) return (1 - dptr->aincr); + +GET_RADIX (rdx, dptr->dradix); +if ((reason = fprint_sym (ofile, addr, sim_eval, uptr, sim_switches)) > 0) { + fprint_val (ofile, sim_eval[0], rdx, dptr->dwidth, PV_RZRO); + reason = 1 - dptr->aincr; + } +if (flag & EX_I) fprintf (ofile, "\t"); +else fprintf (ofile, "\n"); +return reason; +} + +/* Get address routine + + Inputs: + flag = type of ex/mod command (ex, iex, idep) + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + Outputs: (sim_eval is an implicit output) + return = error status +*/ + +t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr) +{ +int32 i; +t_value mask; +t_addr j, loc; +size_t sz; +t_stat reason = SCPE_OK; + +if ((dptr == NULL) || (uptr == NULL)) return SCPE_IERR; +mask = width_mask[dptr->dwidth]; +for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; +for (i = 0, j = addr; i < sim_emax; i++, j = j + dptr->aincr) { + if (dptr->examine != NULL) { + reason = dptr->examine (&sim_eval[i], j, uptr, sim_switches); + if (reason != SCPE_OK) break; + } + else { + if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT; + if (uptr->flags & UNIT_RAW) return SCPE_NOFNC; + if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) { + reason = SCPE_NXM; + break; + } + sz = SZ_D (dptr); + loc = j / dptr->aincr; + if (uptr->flags & UNIT_BUF) { + SZ_LOAD (sz, sim_eval[i], uptr->filebuf, loc); + } + else { + sim_fseek (uptr->fileref, sz * loc, SEEK_SET); + sim_fread (&sim_eval[i], sz, 1, uptr->fileref); + if ((feof (uptr->fileref)) && + !(uptr->flags & UNIT_FIX)) { + reason = SCPE_EOF; + break; + } + else if (ferror (uptr->fileref)) { + clearerr (uptr->fileref); + reason = SCPE_IOERR; + break; + } + } + } + sim_eval[i] = sim_eval[i] & mask; + } +if ((reason != SCPE_OK) && (i == 0)) return reason; +return SCPE_OK; +} + +/* Deposit address routine + + Inputs: + flag = type of deposit (normal/interactive) + cptr = pointer to input string + addr = address to examine + dptr = pointer to device + uptr = pointer to unit + dfltinc = value to return on cr input + Outputs: + return = if > 0, error status + if <= 0, -number of extra address units retired +*/ + +t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr, + UNIT *uptr, int32 dfltinc) +{ +int32 i, count, rdx; +t_addr j, loc; +t_stat r, reason; +t_value mask; +size_t sz; +char gbuf[CBUFSIZE]; + +if (dptr == NULL) return SCPE_IERR; +if (flag & EX_I) { + cptr = read_line (gbuf, CBUFSIZE, stdin); + if (sim_log) fprintf (sim_log, (cptr? "%s\n": "\n"), cptr); + if (cptr == NULL) return 1; /* force exit */ + if (*cptr == 0) return dfltinc; /* success */ + } +if (uptr->flags & UNIT_RO) return SCPE_RO; /* read only? */ +mask = width_mask[dptr->dwidth]; + +GET_RADIX (rdx, dptr->dradix); +if ((reason = parse_sym (cptr, addr, uptr, sim_eval, sim_switches)) > 0) { + sim_eval[0] = get_uint (cptr, rdx, mask, &reason); + if (reason != SCPE_OK) return reason; + } +count = (1 - reason + (dptr->aincr - 1)) / dptr->aincr; + +for (i = 0, j = addr; i < count; i++, j = j + dptr->aincr) { + sim_eval[i] = sim_eval[i] & mask; + if (dptr->deposit != NULL) { + r = dptr->deposit (sim_eval[i], j, uptr, sim_switches); + if (r != SCPE_OK) return r; + } + else { + if (!(uptr->flags & UNIT_ATT)) return SCPE_UNATT; + if (uptr->flags & UNIT_RAW) return SCPE_NOFNC; + if ((uptr->flags & UNIT_FIX) && (j >= uptr->capac)) + return SCPE_NXM; + sz = SZ_D (dptr); + loc = j / dptr->aincr; + if (uptr->flags & UNIT_BUF) { + SZ_STORE (sz, sim_eval[i], uptr->filebuf, loc); + if (loc >= uptr->hwmark) uptr->hwmark = (uint32) loc + 1; + } + else { + sim_fseek (uptr->fileref, sz * loc, SEEK_SET); + sim_fwrite (&sim_eval[i], sz, 1, uptr->fileref); + if (ferror (uptr->fileref)) { + clearerr (uptr->fileref); + return SCPE_IOERR; + } + } + } + } +return reason; +} + +/* Evaluate command */ + +t_stat eval_cmd (int32 flg, char *cptr) +{ +DEVICE *dptr = sim_dflt_dev; +int32 i, rdx, a, lim; +t_stat r; + +GET_SWITCHES (cptr); +GET_RADIX (rdx, dptr->dradix); +for (i = 0; i < sim_emax; i++) sim_eval[i] = 0; +if (*cptr == 0) return SCPE_2FARG; +if ((r = parse_sym (cptr, 0, dptr->units, sim_eval, sim_switches)) > 0) { + sim_eval[0] = get_uint (cptr, rdx, width_mask[dptr->dwidth], &r); + if (r != SCPE_OK) return r; + } +lim = 1 - r; +for (i = a = 0; a < lim; ) { + printf ("%d:\t", a); + if ((r = fprint_sym (stdout, a, &sim_eval[i], dptr->units, sim_switches)) > 0) + r = fprint_val (stdout, sim_eval[i], rdx, dptr->dwidth, PV_RZRO); + printf ("\n"); + if (sim_log) { + fprintf (sim_log, "%d\t", i); + if ((r = fprint_sym (sim_log, a, &sim_eval[i], dptr->units, sim_switches)) > 0) + r = fprint_val (sim_log, sim_eval[i], rdx, dptr->dwidth, PV_RZRO); + fprintf (sim_log, "\n"); + } + if (r < 0) a = a + 1 - r; + else a = a + dptr->aincr; + i = a / dptr->aincr; + } +return SCPE_OK; +} + +/* String processing routines + + read_line read line + + Inputs: + cptr = pointer to buffer + size = maximum size + stream = pointer to input stream + Outputs: + optr = pointer to first non-blank character + NULL if EOF +*/ + +char *read_line (char *cptr, int32 size, FILE *stream) +{ +char *tptr; + +cptr = fgets (cptr, size, stream); /* get cmd line */ +if (cptr == NULL) { + clearerr (stream); /* clear error */ + return NULL; /* ignore EOF */ + } +for (tptr = cptr; tptr < (cptr + size); tptr++) { /* remove cr or nl */ + if ((*tptr == '\n') || (*tptr == '\r') || + (tptr == (cptr + size - 1))) { /* str max length? */ + *tptr = 0; /* terminate */ + break; + } + } +while (isspace (*cptr)) cptr++; /* trim leading spc */ +if (*cptr == ';') *cptr = 0; /* ignore comment */ + + +return cptr; +} + +/* get_glyph get next glyph (force upper case) + get_glyph_nc get next glyph (no conversion) + get_glyph_gen get next glyph (general case) + + Inputs: + iptr = pointer to input string + optr = pointer to output string + mchar = optional end of glyph character + flag = TRUE for convert to upper case (_gen only) + Outputs + result = pointer to next character in input string +*/ + +char *get_glyph_gen (char *iptr, char *optr, char mchar, t_bool uc) +{ +while ((isspace (*iptr) == 0) && (*iptr != 0) && (*iptr != mchar)) { + if (islower (*iptr) && uc) *optr = toupper (*iptr); + else *optr = *iptr; + iptr++; optr++; + } +*optr = 0; +if (mchar && (*iptr == mchar)) iptr++; /* skip terminator */ +while (isspace (*iptr)) iptr++; /* absorb spaces */ +return iptr; +} + +char *get_glyph (char *iptr, char *optr, char mchar) +{ +return get_glyph_gen (iptr, optr, mchar, TRUE); +} + +char *get_glyph_nc (char *iptr, char *optr, char mchar) +{ +return get_glyph_gen (iptr, optr, mchar, FALSE); +} + +/* Trim trailing spaces from a string + + Inputs: + cptr = pointer to string + Outputs: + cptr = pointer to string +*/ + +char *sim_trim_endspc (char *cptr) +{ +char *tptr; + +tptr = cptr + strlen (cptr); +while ((--tptr >= cptr) && isspace (*tptr)) *tptr = 0; +return cptr; +} + +/* get_yn yes/no question + + Inputs: + cptr = pointer to question + deflt = default answer + Outputs: + result = true if yes, false if no +*/ + +t_stat get_yn (char *ques, t_stat deflt) +{ +char cbuf[CBUFSIZE], *cptr; + +printf ("%s ", ques); +cptr = read_line (cbuf, CBUFSIZE, stdin); +if ((cptr == NULL) || (*cptr == 0)) return deflt; +if ((*cptr == 'Y') || (*cptr == 'y')) return TRUE; +return FALSE; +} + +/* get_uint unsigned number + + Inputs: + cptr = pointer to input string + radix = input radix + max = maximum acceptable value + *status = pointer to error status + Outputs: + val = value +*/ + +t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status) +{ +t_value val; +char *tptr; + +*status = SCPE_OK; +val = strtotv (cptr, &tptr, radix); +if ((cptr == tptr) || (val > max)) *status = SCPE_ARG; +else { + while (isspace (*tptr)) tptr++; + if (*tptr != 0) *status = SCPE_ARG; + } +return val; +} + +/* get_range range specification + + Inputs: + dptr = pointer to device (NULL if none) + cptr = pointer to input string + *lo = pointer to low result + *hi = pointer to high result + aradix = radix + max = default high value + term = terminating character, 0 if none + Outputs: + tptr = input pointer after processing + NULL if error +*/ + +char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi, + uint32 rdx, t_addr max, char term) +{ +char *tptr; + +if (max && strncmp (cptr, "ALL", strlen ("ALL")) == 0) { /* ALL? */ + tptr = cptr + strlen ("ALL"); + *lo = 0; + *hi = max; + } +else { + if (dptr && sim_vm_parse_addr) /* get low */ + *lo = sim_vm_parse_addr (dptr, cptr, &tptr); + else *lo = (t_addr) strtotv (cptr, &tptr, rdx); + if (cptr == tptr) return NULL; /* error? */ + if ((*tptr == '-') || (*tptr == ':')) { /* range? */ + cptr = tptr + 1; + if (dptr && sim_vm_parse_addr) /* get high */ + *hi = sim_vm_parse_addr (dptr, cptr, &tptr); + else *hi = (t_addr) strtotv (cptr, &tptr, rdx); + if (cptr == tptr) return NULL; + if (*lo > *hi) return NULL; + } + else if (*tptr == '/') { /* relative? */ + cptr = tptr + 1; + *hi = (t_addr) strtotv (cptr, &tptr, rdx); /* get high */ + if ((cptr == tptr) || (*hi == 0)) return NULL; + *hi = *lo + *hi - 1; + } + else *hi = *lo; + } +if (term && (*tptr++ != term)) return NULL; +return tptr; +} + +/* get_ipaddr IP address:port + + Inputs: + cptr = pointer to input string + Outputs: + ipa = pointer to IP address (may be NULL), 0 = none + ipp = pointer to IP port (may be NULL), 0 = none + result = status +*/ + +t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp) +{ +char gbuf[CBUFSIZE]; +char *addrp, *portp, *octetp; +uint32 i, addr, port, octet; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_ARG; +strncpy (gbuf, cptr, CBUFSIZE); +addrp = gbuf; /* default addr */ +if (portp = strchr (gbuf, ':')) *portp++ = 0; /* x:y? split */ +else if (strchr (gbuf, '.')) portp = NULL; /* x.y...? */ +else { + portp = gbuf; /* port only */ + addrp = NULL; /* no addr */ + } +if (portp) { /* port string? */ + if (ipp == NULL) return SCPE_ARG; /* not wanted? */ + port = (int32) get_uint (portp, 10, 65535, &r); + if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG; + } +else port = 0; +if (addrp) { /* addr string? */ + if (ipa == NULL) return SCPE_ARG; /* not wanted? */ + for (i = addr = 0; i < 4; i++) { /* four octets */ + octetp = strchr (addrp, '.'); /* find octet end */ + if (octetp != NULL) *octetp++ = 0; /* split string */ + else if (i < 3) return SCPE_ARG; /* except last */ + octet = (int32) get_uint (addrp, 10, 255, &r); + if (r != SCPE_OK) return SCPE_ARG; + addr = (addr << 8) | octet; + addrp = octetp; + } + if (((addr & 0377) == 0) || ((addr & 0377) == 255)) + return SCPE_ARG; + } +else addr = 0; +if (ipp) *ipp = port; /* return req values */ +if (ipa) *ipa = addr; +return SCPE_OK; +} + +/* Find_device find device matching input string + + Inputs: + cptr = pointer to input string + Outputs: + result = pointer to device +*/ + +DEVICE *find_dev (char *cptr) +{ +int32 i; +DEVICE *dptr; + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + if ((strcmp (cptr, dptr->name) == 0) || + (dptr->lname && + (strcmp (cptr, dptr->lname) == 0))) return dptr; + } +return NULL; +} + +/* Find_unit find unit matching input string + + Inputs: + cptr = pointer to input string + uptr = pointer to unit pointer + Outputs: + result = pointer to device (null if no dev) + *iptr = pointer to unit (null if nx unit) +*/ + +DEVICE *find_unit (char *cptr, UNIT **uptr) +{ +uint32 i, u; +char *nptr, *tptr; +t_stat r; +DEVICE *dptr; + +if (uptr == NULL) return NULL; /* arg error? */ +if (dptr = find_dev (cptr)) { /* exact match? */ + if (qdisable (dptr)) return NULL; /* disabled? */ + *uptr = dptr->units; /* unit 0 */ + return dptr; + } + +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */ + if (dptr->numunits && /* any units? */ + (((nptr = dptr->name) && + (strncmp (cptr, nptr, strlen (nptr)) == 0)) || + ((nptr = dptr->lname) && + (strncmp (cptr, nptr, strlen (nptr)) == 0)))) { + tptr = cptr + strlen (nptr); + if (isdigit (*tptr)) { + if (qdisable (dptr)) return NULL; /* disabled? */ + u = (uint32) get_uint (tptr, 10, dptr->numunits - 1, &r); + if (r != SCPE_OK) *uptr = NULL; /* error? */ + else *uptr = dptr->units + u; + return dptr; + } + } + } +return NULL; +} + +/* Find_dev_from_unit find device for unit + + Inputs: + uptr = pointer to unit + Outputs: + result = pointer to device +*/ + +DEVICE *find_dev_from_unit (UNIT *uptr) +{ +DEVICE *dptr; +uint32 i, j; + +if (uptr == NULL) return NULL; +for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { + for (j = 0; j < dptr->numunits; j++) { + if (uptr == (dptr->units + j)) return dptr; + } + } +return NULL; +} + +/* Test for disabled device */ + +t_bool qdisable (DEVICE *dptr) +{ +return (dptr->flags & DEV_DIS? TRUE: FALSE); +} + +/* find_reg_glob find globally unique register + + Inputs: + cptr = pointer to input string + optr = pointer to output pointer (can be null) + gdptr = pointer to global device + Outputs: + result = pointer to register, NULL if error + *optr = pointer to next character in input string + *gdptr = pointer to device where found +*/ + +REG *find_reg_glob (char *cptr, char **optr, DEVICE **gdptr) +{ +int32 i; +DEVICE *dptr; +REG *rptr, *srptr = NULL; + +for (i = 0; (dptr = sim_devices[i]) != 0; i++) { /* all dev */ + if (dptr->flags & DEV_DIS) continue; /* skip disabled */ + if (rptr = find_reg (cptr, optr, dptr)) { /* found? */ + if (srptr) return NULL; /* ambig? err */ + srptr = rptr; /* save reg */ + *gdptr = dptr; /* save unit */ + } + } +return srptr; +} + +/* find_reg find register matching input string + + Inputs: + cptr = pointer to input string + optr = pointer to output pointer (can be null) + dptr = pointer to device + Outputs: + result = pointer to register, NULL if error + *optr = pointer to next character in input string +*/ + +REG *find_reg (char *cptr, char **optr, DEVICE *dptr) +{ +char *tptr; +REG *rptr; +uint32 slnt; + +if ((cptr == NULL) || (dptr == NULL) || + (dptr->registers == NULL)) return NULL; +tptr = cptr; +do { + tptr++; + } while (isalnum (*tptr) || (*tptr == '*') || (*tptr == '_')); +slnt = tptr - cptr; +for (rptr = dptr->registers; rptr->name != NULL; rptr++) { + if ((slnt == strlen (rptr->name)) && + (strncmp (cptr, rptr->name, slnt) == 0)) { + if (optr != NULL) *optr = tptr; + return rptr; + } + } +return NULL; +} + +/* get_switches get switches from input string + + Inputs: + cptr = pointer to input string + Outputs: + sw = switch bit mask + 0 if no switches, -1 if error +*/ + +int32 get_switches (char *cptr) +{ +int32 sw; + +if (*cptr != '-') return 0; +sw = 0; +for (cptr++; (isspace (*cptr) == 0) && (*cptr != 0); cptr++) { + if (isalpha (*cptr) == 0) return -1; + sw = sw | SWMASK (toupper (*cptr)); + } +return sw; +} + +/* get_sim_sw accumulate sim_switches + + Inputs: + cptr = pointer to input string + Outputs: + ptr = pointer to first non-string glyph + NULL if error +*/ + +char *get_sim_sw (char *cptr) +{ +int32 lsw; +char gbuf[CBUFSIZE]; + +while (*cptr == '-') { /* while switches */ + cptr = get_glyph (cptr, gbuf, 0); /* get switch glyph */ + lsw = get_switches (gbuf); /* parse */ + if (lsw <= 0) return NULL; /* invalid? */ + sim_switches = sim_switches | lsw; /* accumulate */ + } +return cptr; +} + +/* get_sim_opt get simulator command options + + Inputs: + opt = command options + cptr = pointer to input string + Outputs: + ptr = pointer to next glypsh, NULL if error + *stat = error status +*/ + +char *get_sim_opt (int32 opt, char *cptr, t_stat *st) +{ +int32 t; +char *svptr, gbuf[CBUFSIZE]; +DEVICE *tdptr; +UNIT *tuptr; + +sim_switches = 0; /* no switches */ +sim_ofile = NULL; /* no output file */ +sim_schptr = NULL; /* no search */ +sim_stab.logic = SCH_OR; /* default search params */ +sim_stab.boolop = SCH_GE; +sim_stab.mask = 0; +sim_stab.comp = 0; +sim_dfdev = sim_dflt_dev; +sim_dfunit = sim_dfdev->units; +sim_opt_out = 0; /* no options yet */ +*st = SCPE_OK; +while (*cptr) { /* loop through modifiers */ + svptr = cptr; /* save current position */ + if ((opt & CMD_OPT_OF) && (*cptr == '@')) { /* output file spec? */ + if (sim_ofile) { /* already got one? */ + fclose (sim_ofile); /* one per customer */ + *st = SCPE_ARG; + return NULL; + } + cptr = get_glyph_nc (cptr + 1, gbuf, 0); + sim_ofile = sim_fopen (gbuf, "a"); /* open for append */ + if (sim_ofile == NULL) { /* open failed? */ + *st = SCPE_OPENERR; + return NULL; + } + sim_opt_out |= CMD_OPT_OF; /* got output file */ + continue; + } + cptr = get_glyph (cptr, gbuf, 0); + if ((t = get_switches (gbuf)) != 0) { /* try for switches */ + if (t < 0) { /* err if bad switch */ + *st = SCPE_INVSW; + return NULL; + } + sim_switches = sim_switches | t; /* or in new switches */ + } + else if ((opt & CMD_OPT_SCH) && /* if allowed, */ + get_search (gbuf, sim_dfdev->dradix, &sim_stab)) { /* try for search */ + sim_schptr = &sim_stab; /* set search */ + sim_opt_out |= CMD_OPT_SCH; /* got search */ + } + else if ((opt & CMD_OPT_DFT) && /* default allowed? */ + ((sim_opt_out & CMD_OPT_DFT) == 0) && /* none yet? */ + (tdptr = find_unit (gbuf, &tuptr)) && /* try for default */ + (tuptr != NULL)) { + sim_dfdev = tdptr; /* set as default */ + sim_dfunit = tuptr; + sim_opt_out |= CMD_OPT_DFT; /* got default */ + } + else return svptr; /* not rec, break out */ + } +return cptr; +} + +/* Match file extension + + Inputs: + fnam = file name + ext = extension, without period + Outputs: + cp = pointer to final '.' if match, NULL if not +*/ + +char *match_ext (char *fnam, char *ext) +{ +char *pptr, *fptr, *eptr; + +if ((fnam == NULL) || (ext == NULL)) /* bad arguments? */ + return NULL; +pptr = strrchr (fnam, '.'); /* find last . */ +if (pptr) { /* any? */ + for (fptr = pptr + 1, eptr = ext; /* match characters */ +#if defined (VMS) /* VMS: stop at ; or null */ + (*fptr != 0) && (*fptr != ';'); +#else + *fptr != 0; /* others: stop at null */ +#endif + fptr++, eptr++) { + if (toupper (*fptr) != toupper (*eptr)) + return NULL; + } + if (*eptr != 0) return NULL; /* ext exhausted? */ + } +return pptr; +} + +/* Get search specification + + Inputs: + cptr = pointer to input string + radix = radix for numbers + schptr = pointer to search table + Outputs: + return = NULL if error + schptr if valid search specification +*/ + +SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr) +{ +int32 c, logop, cmpop; +t_value logval, cmpval; +char *sptr, *tptr; +const char logstr[] = "|&^", cmpstr[] = "=!><"; + +logval = cmpval = 0; +if (*cptr == 0) return NULL; /* check for clause */ +for (logop = cmpop = -1; c = *cptr++; ) { /* loop thru clauses */ + if (sptr = strchr (logstr, c)) { /* check for mask */ + logop = sptr - logstr; + logval = strtotv (cptr, &tptr, radix); + if (cptr == tptr) return NULL; + cptr = tptr; + } + else if (sptr = strchr (cmpstr, c)) { /* check for boolop */ + cmpop = sptr - cmpstr; + if (*cptr == '=') { + cmpop = cmpop + strlen (cmpstr); + cptr++; + } + cmpval = strtotv (cptr, &tptr, radix); + if (cptr == tptr) return NULL; + cptr = tptr; + } + else return NULL; + } /* end for */ +if (logop >= 0) { + schptr->logic = logop; + schptr->mask = logval; + } +if (cmpop >= 0) { + schptr->boolop = cmpop; + schptr->comp = cmpval; + } +return schptr; +} + +/* Test value against search specification + + Inputs: + val = value to test + schptr = pointer to search table + Outputs: + return = 1 if value passes search criteria, 0 if not +*/ + +int32 test_search (t_value val, SCHTAB *schptr) +{ +if (schptr == NULL) return 0; + +switch (schptr->logic) { /* case on logical */ + + case SCH_OR: + val = val | schptr->mask; + break; + + case SCH_AND: + val = val & schptr->mask; + break; + + case SCH_XOR: + val = val ^ schptr->mask; + break; + } + +switch (schptr->boolop) { /* case on comparison */ + + case SCH_E: case SCH_EE: + return (val == schptr->comp); + + case SCH_N: case SCH_NE: + return (val != schptr->comp); + + case SCH_G: + return (val > schptr->comp); + + case SCH_GE: + return (val >= schptr->comp); + + case SCH_L: + return (val < schptr->comp); + + case SCH_LE: + return (val <= schptr->comp); + } + +return 0; +} + +/* Radix independent input/output package + + strtotv - general radix input routine + + Inputs: + inptr = string to convert + endptr = pointer to first unconverted character + radix = radix for input + Outputs: + value = converted value + + On an error, the endptr will equal the inptr. +*/ + +t_value strtotv (char *inptr, char **endptr, uint32 radix) +{ +int32 nodigit; +t_value val; +uint32 c, digit; + +*endptr = inptr; /* assume fails */ +if ((radix < 2) || (radix > 36)) return 0; +while (isspace (*inptr)) inptr++; /* bypass white space */ +val = 0; +nodigit = 1; +for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */ + if (islower (c)) c = toupper (c); + if (isdigit (c)) digit = c - (uint32) '0'; /* digit? */ + else if (radix <= 10) break; /* stop if not expected */ + else digit = c + 10 - (uint32) 'A'; /* convert letter */ + if (digit >= radix) return 0; /* valid in radix? */ + val = (val * radix) + digit; /* add to value */ + nodigit = 0; + } +if (nodigit) return 0; /* no digits? */ +*endptr = inptr; /* result pointer */ +return val; +} + +/* fprint_val - general radix printing routine + + Inputs: + stream = stream designator + val = value to print + radix = radix to print + width = width to print + format = leading zeroes format + Outputs: + status = error status +*/ + +t_stat fprint_val (FILE *stream, t_value val, uint32 radix, + uint32 width, uint32 format) +{ +#define MAX_WIDTH ((int) (CHAR_BIT * sizeof (t_value))) +t_value owtest, wtest; +int32 d, digit, ndigits; +char dbuf[MAX_WIDTH + 1]; + +for (d = 0; d < MAX_WIDTH; d++) dbuf[d] = (format == PV_RZRO)? '0': ' '; +dbuf[MAX_WIDTH] = 0; +d = MAX_WIDTH; +do { + d = d - 1; + digit = (int32) (val % radix); + val = val / radix; + dbuf[d] = (digit <= 9)? '0' + digit: 'A' + (digit - 10); + } while ((d > 0) && (val != 0)); + +if (format != PV_LEFT) { + wtest = owtest = radix; + ndigits = 1; + while ((wtest < width_mask[width]) && (wtest >= owtest)) { + owtest = wtest; + wtest = wtest * radix; + ndigits = ndigits + 1; + } + if ((MAX_WIDTH - ndigits) < d) d = MAX_WIDTH - ndigits; + } +if (fputs (&dbuf[d], stream) == EOF) return SCPE_IOERR; +return SCPE_OK; +} + +/* Event queue package + + sim_activate add entry to event queue + sim_cancel remove entry from event queue + sim_process_event process entries on event queue + sim_is_active see if entry is on event queue + sim_atime return absolute time for an entry + sim_gtime return global time + sim_qcount return event queue entry count + + Asynchronous events are set up by queueing a unit data structure + to the event queue with a timeout (in simulator units, relative + to the current time). Each simulator 'times' these events by + counting down interval counter sim_interval. When this reaches + zero the simulator calls sim_process_event to process the event + and to see if further events need to be processed, or sim_interval + reset to count the next one. + + The event queue is maintained in clock order; entry timeouts are + RELATIVE to the time in the previous entry. + + sim_process_event - process event + + Inputs: + none + Outputs: + reason = reason code returned by any event processor, + or 0 (SCPE_OK) if no exceptions +*/ + +t_stat sim_process_event (void) +{ +UNIT *uptr; +t_stat reason; + +if (stop_cpu) return SCPE_STOP; /* stop CPU? */ +if (sim_clock_queue == NULL) { /* queue empty? */ + UPDATE_SIM_TIME (noqueue_time); /* update sim time */ + sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */ + return SCPE_OK; + } +UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */ +do { + uptr = sim_clock_queue; /* get first */ + sim_clock_queue = uptr->next; /* remove first */ + uptr->next = NULL; /* hygiene */ + uptr->time = 0; + if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time; + else sim_interval = noqueue_time = NOQUEUE_WAIT; + if (uptr->action != NULL) reason = uptr->action (uptr); + else reason = SCPE_OK; + } while ((reason == SCPE_OK) && (sim_interval == 0)); + +/* Empty queue forces sim_interval != 0 */ + +return reason; +} + +/* sim_activate - activate (queue) event + + Inputs: + uptr = pointer to unit + event_time = relative timeout + Outputs: + reason = result (SCPE_OK if ok) +*/ + +t_stat sim_activate (UNIT *uptr, int32 event_time) +{ +UNIT *cptr, *prvptr; +int32 accum; + +if (event_time < 0) return SCPE_IERR; +if (sim_is_active (uptr)) return SCPE_OK; /* already active? */ +if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } +else { UPDATE_SIM_TIME (sim_clock_queue->time); } /* update sim time */ + +prvptr = NULL; +accum = 0; +for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) { + if (event_time < (accum + cptr->time)) break; + accum = accum + cptr->time; + prvptr = cptr; + } +if (prvptr == NULL) { /* insert at head */ + cptr = uptr->next = sim_clock_queue; + sim_clock_queue = uptr; + } +else { + cptr = uptr->next = prvptr->next; /* insert at prvptr */ + prvptr->next = uptr; + } +uptr->time = event_time - accum; +if (cptr != NULL) cptr->time = cptr->time - uptr->time; +sim_interval = sim_clock_queue->time; +return SCPE_OK; +} + +/* sim_activate_abs - activate (queue) event even if event already scheduled + + Inputs: + uptr = pointer to unit + event_time = relative timeout + Outputs: + reason = result (SCPE_OK if ok) +*/ + +t_stat sim_activate_abs (UNIT *uptr, int32 event_time) +{ +sim_cancel (uptr); +return sim_activate (uptr, event_time); +} + +/* sim_cancel - cancel (dequeue) event + + Inputs: + uptr = pointer to unit + Outputs: + reason = result (SCPE_OK if ok) + +*/ + +t_stat sim_cancel (UNIT *uptr) +{ +UNIT *cptr, *nptr; + +if (sim_clock_queue == NULL) return SCPE_OK; +UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */ +nptr = NULL; +if (sim_clock_queue == uptr) nptr = sim_clock_queue = uptr->next; +else { + for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) { + if (cptr->next == uptr) { + nptr = cptr->next = uptr->next; + break; /* end queue scan */ + } + } + } +if (nptr != NULL) nptr->time = nptr->time + uptr->time; +uptr->next = NULL; /* hygiene */ +uptr->time = 0; +if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time; +else sim_interval = noqueue_time = NOQUEUE_WAIT; +return SCPE_OK; +} + +/* sim_is_active - test for entry in queue, return activation time + + Inputs: + uptr = pointer to unit + Outputs: + result = absolute activation time + 1, 0 if inactive +*/ + +int32 sim_is_active (UNIT *uptr) +{ +UNIT *cptr; +int32 accum; + +accum = 0; +for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) { + if (cptr == sim_clock_queue) { + if (sim_interval > 0) accum = accum + sim_interval; + } + else accum = accum + cptr->time; + if (cptr == uptr) return accum + 1; + } +return 0; +} + +/* sim_gtime - return global time + sim_grtime - return global time with rollover + + Inputs: none + Outputs: + time = global time +*/ + +double sim_gtime (void) +{ +if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } +else { UPDATE_SIM_TIME (sim_clock_queue->time); } +return sim_time; +} + +uint32 sim_grtime (void) +{ +if (sim_clock_queue == NULL) { UPDATE_SIM_TIME (noqueue_time); } +else { UPDATE_SIM_TIME (sim_clock_queue->time); } +return sim_rtime; +} + +/* sim_qcount - return queue entry count + + Inputs: none + Outputs: + count = number of entries on the queue +*/ + +int32 sim_qcount (void) +{ +int32 cnt; +UNIT *uptr; + +cnt = 0; +for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) cnt++; +return cnt; +} + +/* Breakpoint package. This module replaces the VM-implemented one + instruction breakpoint capability. + + Breakpoints are stored in table sim_brk_tab, which is ordered by address for + efficient binary searching. A breakpoint consists of a four entry structure: + + addr address of the breakpoint + type types of breakpoints set on the address + a bit mask representing letters A-Z + cnt number of iterations before breakp is taken + action pointer command string to be executed + when break is taken + + sim_brk_summ is a summary of the types of breakpoints that are currently set (it + is the bitwise OR of all the type fields). A simulator need only check for + a breakpoint of type X if bit SWMASK('X') is set in sim_brk_sum. + + The package contains the following public routines: + + sim_brk_init initialize + sim_brk_set set breakpoint + sim_brk_clr clear breakpoint + sim_brk_clrall clear all breakpoints + sim_brk_show show breakpoint + sim_brk_showall show all breakpoints + sim_brk_test test for breakpoint + sim_brk_npc PC has been changed + sim_brk_getact get next action + sim_brk_clract clear pending actions + + Initialize breakpoint system. +*/ + +t_stat sim_brk_init (void) +{ +sim_brk_lnt = SIM_BRK_INILNT; +sim_brk_tab = (BRKTAB *) calloc (sim_brk_lnt, sizeof (BRKTAB)); +if (sim_brk_tab == NULL) return SCPE_MEM; +sim_brk_ent = sim_brk_ins = 0; +sim_brk_act = NULL; +sim_brk_npc (0); +return SCPE_OK; +} + +/* Search for a breakpoint in the sorted breakpoint table */ + +BRKTAB *sim_brk_fnd (t_addr loc) +{ +int32 lo, hi, p; +BRKTAB *bp; + +if (sim_brk_ent == 0) { /* table empty? */ + sim_brk_ins = 0; /* insrt at head */ + return NULL; /* sch fails */ + } +lo = 0; /* initial bounds */ +hi = sim_brk_ent - 1; +do { + p = (lo + hi) >> 1; /* probe */ + bp = sim_brk_tab + p; /* table addr */ + if (loc == bp->addr) return bp; /* match? */ + else if (loc < bp->addr) hi = p - 1; /* go down? p is upper */ + else lo = p + 1; /* go up? p is lower */ + } while (lo <= hi); +if (loc < bp->addr) sim_brk_ins = p; /* insrt before or */ +else sim_brk_ins = p + 1; /* after last sch */ +return NULL; +} + +/* Insert a breakpoint */ + +BRKTAB *sim_brk_new (t_addr loc) +{ +int32 i, t; +BRKTAB *bp, *newp; + +if (sim_brk_ins < 0) return NULL; +if (sim_brk_ent >= sim_brk_lnt) { /* out of space? */ + t = sim_brk_lnt + SIM_BRK_INILNT; /* new size */ + newp = (BRKTAB *) calloc (t, sizeof (BRKTAB)); /* new table */ + if (newp == NULL) return NULL; /* can't extend */ + for (i = 0; i < sim_brk_lnt; i++) /* copy table */ + *(newp + i) = *(sim_brk_tab + i); + free (sim_brk_tab); /* free old table */ + sim_brk_tab = newp; /* new base, lnt */ + sim_brk_lnt = t; + } +if (sim_brk_ins != sim_brk_ent) { /* move needed? */ + for (bp = sim_brk_tab + sim_brk_ent; + bp > sim_brk_tab + sim_brk_ins; bp--) + *bp = *(bp - 1); + } +bp = sim_brk_tab + sim_brk_ins; +bp->addr = loc; +bp->typ = 0; +bp->cnt = 0; +bp->act = NULL; +sim_brk_ent = sim_brk_ent + 1; +return bp; +} + +/* Set a breakpoint of type sw */ + +t_stat sim_brk_set (t_addr loc, int32 sw, int32 ncnt, char *act) +{ +BRKTAB *bp; + +if (sw == 0) sw = sim_brk_dflt; +if ((sim_brk_types & sw) == 0) return SCPE_NOFNC; +bp = sim_brk_fnd (loc); /* present? */ +if (!bp) bp = sim_brk_new (loc); /* no, allocate */ +if (!bp) return SCPE_MEM; /* still no? mem err */ +bp->typ = sw; /* set type */ +bp->cnt = ncnt; /* set count */ +if ((bp->act != NULL) && (act != NULL)) { /* replace old action? */ + free (bp->act); /* deallocate */ + bp->act = NULL; /* now no action */ + } +if ((act != NULL) && (*act != 0)) { /* new action? */ + char *newp = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc buf */ + if (newp == NULL) return SCPE_MEM; /* mem err? */ + strncpy (newp, act, CBUFSIZE); /* copy action */ + bp->act = newp; /* set pointer */ + } +sim_brk_summ = sim_brk_summ | sw; +return SCPE_OK; +} + +/* Clear a breakpoint */ + +t_stat sim_brk_clr (t_addr loc, int32 sw) +{ +BRKTAB *bp = sim_brk_fnd (loc); + +if (!bp) return SCPE_OK; /* not there? ok */ +if (sw == 0) sw = SIM_BRK_ALLTYP; +bp->typ = bp->typ & ~sw; +if (bp->typ) return SCPE_OK; /* clear all types? */ +if (bp->act != NULL) free (bp->act); /* deallocate action */ +for ( ; bp < (sim_brk_tab + sim_brk_ent - 1); bp++) /* erase entry */ + *bp = *(bp + 1); +sim_brk_ent = sim_brk_ent - 1; /* decrement count */ +sim_brk_summ = 0; /* recalc summary */ +for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) + sim_brk_summ = sim_brk_summ | bp->typ; +return SCPE_OK; +} + +/* Clear all breakpoints */ + +t_stat sim_brk_clrall (int32 sw) +{ +BRKTAB *bp; + +if (sw == 0) sw = SIM_BRK_ALLTYP; +for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); ) { + if (bp->typ & sw) sim_brk_clr (bp->addr, sw); + else bp++; + } +return SCPE_OK; +} + +/* Show a breakpoint */ + +t_stat sim_brk_show (FILE *st, t_addr loc, int32 sw) +{ +BRKTAB *bp = sim_brk_fnd (loc); +DEVICE *dptr; +int32 i, any; + +if (sw == 0) sw = SIM_BRK_ALLTYP; +if (!bp || (!(bp->typ & sw))) return SCPE_OK; +dptr = sim_dflt_dev; +if (dptr == NULL) return SCPE_OK; +if (sim_vm_fprint_addr) sim_vm_fprint_addr (st, dptr, loc); +else fprint_val (st, loc, dptr->aradix, dptr->awidth, PV_LEFT); +fprintf (st, ":\t"); +for (i = any = 0; i < 26; i++) { + if ((bp->typ >> i) & 1) { + if (any) fprintf (st, ", "); + fputc (i + 'A', st); + any = 1; + } + } +if (bp->cnt > 0) fprintf (st, " [%d]", bp->cnt); +if (bp->act != NULL) fprintf (st, "; %s", bp->act); +fprintf (st, "\n"); +return SCPE_OK; +} + +/* Show all breakpoints */ + +t_stat sim_brk_showall (FILE *st, int32 sw) +{ +BRKTAB *bp; + +if (sw == 0) sw = SIM_BRK_ALLTYP; +for (bp = sim_brk_tab; bp < (sim_brk_tab + sim_brk_ent); bp++) { + if (bp->typ & sw) sim_brk_show (st, bp->addr, sw); + } +return SCPE_OK; +} + +/* Test for breakpoint */ + +uint32 sim_brk_test (t_addr loc, uint32 btyp) +{ +BRKTAB *bp; +uint32 spc = (btyp >> SIM_BKPT_V_SPC) & (SIM_BKPT_N_SPC - 1); + +if ((bp = sim_brk_fnd (loc)) && (btyp & bp->typ)) { /* in table, type match? */ + if ((sim_brk_pend[spc] && (loc == sim_brk_ploc[spc])) || /* previous location? */ + (--bp->cnt > 0)) return 0; /* count > 0? */ + bp->cnt = 0; /* reset count */ + sim_brk_ploc[spc] = loc; /* save location */ + sim_brk_pend[spc] = TRUE; /* don't do twice */ + sim_brk_act = bp->act; /* set up actions */ + return (btyp & bp->typ); + } +sim_brk_pend[spc] = FALSE; +return 0; +} + +/* Get next pending action, if any */ + +char *sim_brk_getact (char *buf, int32 size) +{ +char *ep; +size_t lnt; + +if (sim_brk_act == NULL) return NULL; /* any action? */ +while (isspace (*sim_brk_act)) sim_brk_act++; /* skip spaces */ +if (*sim_brk_act == 0) return (sim_brk_act = NULL); /* now empty? */ +if (ep = strchr (sim_brk_act, ';')) { /* cmd delimiter? */ + lnt = ep - sim_brk_act; /* cmd length */ + memcpy (buf, sim_brk_act, lnt + 1); /* copy with ; */ + buf[lnt] = 0; /* erase ; */ + sim_brk_act = sim_brk_act + lnt + 1; /* adv ptr */ + } +else { + strncpy (buf, sim_brk_act, size); /* copy action */ + sim_brk_act = NULL; /* no more */ + } +return buf; +} + +/* Clear pending actions */ + +void sim_brk_clract (void) +{ +sim_brk_act = NULL; +} + +/* New PC */ + +void sim_brk_npc (uint32 cnt) +{ +uint32 i; + +if ((cnt == 0) || (cnt > SIM_BKPT_N_SPC)) cnt = SIM_BKPT_N_SPC; +for (i = 0; i < cnt; i++) { + sim_brk_pend[i] = FALSE; + sim_brk_ploc[i] = 0; + } +return; +} + +/* Clear breakpoint space */ + +void sim_brk_clrspc (uint32 spc) +{ +if (spc < SIM_BKPT_N_SPC) { + sim_brk_pend[spc] = FALSE; + sim_brk_ploc[spc] = 0; + } +return; +} + +/* Debug printout routines, from Dave Hittner */ + +const char* debug_bstates = "01_^"; +const char* debug_fmt = "DBG> %s %s: "; +int32 debug_unterm = 0; + +/* Finds debug phrase matching bitmask from from device DEBTAB table */ + +static char* get_dbg_verb (uint32 dbits, DEVICE* dptr) +{ +static char* debtab_none = "DEBTAB_ISNULL"; +static char* debtab_nomatch = "DEBTAB_NOMATCH"; +int32 offset = 0; + +if (dptr->debflags == 0) return debtab_none; + +/* Find matching words for bitmask */ + +while (dptr->debflags[offset].name && (offset < 32)) { + if (dptr->debflags[offset].mask & dbits) + return dptr->debflags[offset].name; + offset++; + } +return debtab_nomatch; +} + +/* Prints standard debug prefix unless previous call unterminated */ + +static void sim_debug_prefix (uint32 dbits, DEVICE* dptr) +{ +if (!debug_unterm) { + char* debug_type = get_dbg_verb (dbits, dptr); + fprintf(sim_deb, debug_fmt, dptr->name, debug_type); + } +} + +/* Prints state of a register: bit translation + state (0,1,_,^) + indicating the state and transition of the bit. States: + 0=steady(0->0), 1=steady(1->1), _=falling(1->0), ^=rising(0->1) */ + +void sim_debug_u16(uint32 dbits, DEVICE* dptr, const char* const* bitdefs, + uint16 before, uint16 after, int terminate) +{ +if (sim_deb && (dptr->dctrl & dbits)) { + int32 i; + + sim_debug_prefix(dbits, dptr); /* print prefix if required */ + for (i = 15; i >= 0; i--) { /* print xlation, transition */ + int off = ((after >> i) & 1) + (((before ^ after) >> i) & 1) * 2; + fprintf(sim_deb, "%s%c ", bitdefs[i], debug_bstates[off]); + } + if (terminate) fprintf(sim_deb, "\r\n"); + debug_unterm = terminate ? 0 : 1; /* set unterm for next */ + } +} + +#if defined (_WIN32) +#define vsnprintf _vsnprintf +#endif +#if defined (__DECC) && defined (__VMS) +#define NO_vsnprintf +#endif +#if defined( NO_vsnprintf) +#define STACKBUFSIZE 16384 +#else +#define STACKBUFSIZE 2048 +#endif + +/* Inline debugging - will print debug message if debug file is + set and the bitmask matches the current device debug options. + Extra returns are added for un*x systems, since the output + device is set into 'raw' mode when the cpu is booted, + and the extra returns don't hurt any other systems. */ + +void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...) +{ +if (sim_deb && (dptr->dctrl & dbits)) { + + char stackbuf[STACKBUFSIZE]; + int32 bufsize = sizeof(stackbuf); + char *buf = stackbuf; + va_list arglist; + int32 i, j, len; + + buf[bufsize-1] = '\0'; + sim_debug_prefix(dbits, dptr); /* print prefix if required */ + + while (1) { /* format passed string, args */ + va_start (arglist, fmt); +#if defined(NO_vsnprintf) +#if defined(HAS_vsprintf_void) + +/* Note, this could blow beyond the buffer, and we couldn't tell */ +/* That is a limitation of the C runtime library available on this platform */ + + vsprintf (buf, fmt, arglist); + for (len = 0; len < bufsize-1; len++) + if (buf[len] == 0) break; +#else + len = vsprintf (buf, fmt, arglist); +#endif /* HAS_vsprintf_void */ +#else /* NO_vsnprintf */ +#if defined(HAS_vsnprintf_void) + vsnprintf (buf, bufsize-1, fmt, arglist); + for (len = 0; len < bufsize-1; len++) + if (buf[len] == 0) break; +#else + len = vsnprintf (buf, bufsize-1, fmt, arglist); +#endif /* HAS_vsnprintf_void */ +#endif /* NO_vsnprintf */ + va_end (arglist); + +/* If it didn't fit into the buffer, then grow it and try again */ + + if ((len < 0) || (len >= bufsize-1)) { + if (buf != stackbuf) free (buf); + bufsize = bufsize * 2; + buf = (char *) malloc (bufsize); + if (buf == NULL) return; /* out of memory */ + buf[bufsize-1] = '\0'; + continue; + } + break; + } + +/* Output the formatted data expanding newlines where they exist */ + + for (i = j = 0; i < len; ++i) { + if ('\n' == buf[i]) { + if (i > j) fwrite (&buf[j], 1, i-j, sim_deb); + j = i; + fputc('\r', sim_deb); + } + } + if (i > j) fwrite (&buf[j], 1, i-j, sim_deb); + +/* Set unterminated flag for next time */ + + debug_unterm = (len && (buf[len-1]=='\n')) ? 0 : 1; + if (buf != stackbuf) free (buf); + } +return; +} diff --git a/scp.diff b/scp.diff new file mode 100644 index 0000000..e18a835 --- /dev/null +++ b/scp.diff @@ -0,0 +1,197 @@ +*** ../simhv38/scp.c 2008-06-01 15:59:14.000000000 +0200 +--- scp.c 2008-09-19 16:55:57.000000000 +0200 +*************** +*** 169,188 **** +--- 169,192 ---- + 13-Apr-95 RMS Added symbolic printouts + */ + + /* Macros and data structures */ + + #include "sim_defs.h" + #include "sim_rev.h" + #include + #include + ++ #ifdef HAVE_READLINE ++ #include ++ #endif ++ + #define EX_D 0 /* deposit */ + #define EX_E 1 /* examine */ + #define EX_I 2 /* interactive */ + #define SCH_OR 0 /* search logicals */ + #define SCH_AND 1 + #define SCH_XOR 2 + #define SCH_E 0 /* search booleans */ + #define SCH_N 1 + #define SCH_G 2 + #define SCH_L 3 +*************** +*** 572,591 **** +--- 576,599 ---- + /* Main command loop */ + + int main (int argc, char *argv[]) + { + char cbuf[CBUFSIZE], gbuf[CBUFSIZE], *cptr; + int32 i, sw; + t_bool lookswitch; + t_stat stat; + CTAB *cmdp; + ++ #ifdef HAVE_READLINE ++ readline_read_history(); ++ #endif ++ + #if defined (__MWERKS__) && defined (macintosh) + argc = ccommand (&argv); + #endif + + *cbuf = 0; /* init arg buffer */ + sim_switches = 0; /* init switches */ + lookswitch = TRUE; + for (i = 1; i < argc; i++) { /* loop thru args */ + if (argv[i] == NULL) continue; /* paranoia */ + if ((*argv[i] == '-') && lookswitch) { /* switch? */ +*************** +*** 649,695 **** + else if (*argv[0]) { /* sim name arg? */ + char nbuf[PATH_MAX + 7], *np; /* "path.ini" */ + nbuf[0] = '"'; /* starting " */ + strncpy (nbuf + 1, argv[0], PATH_MAX + 1); /* copy sim name */ + if (np = match_ext (nbuf, "EXE")) *np = 0; /* remove .exe */ + strcat (nbuf, ".ini\""); /* add .ini" */ + stat = do_cmd (-1, nbuf); /* proc cmd file */ + } + + while (stat != SCPE_EXIT) { /* in case exit */ +! printf ("sim> "); /* prompt */ + if (cptr = sim_brk_getact (cbuf, CBUFSIZE)) /* pending action? */ +! printf ("%s\n", cptr); /* echo */ + else if (sim_vm_read != NULL) /* sim routine? */ + cptr = (*sim_vm_read) (cbuf, CBUFSIZE, stdin); +! else cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */ +! if (cptr == NULL) continue; /* ignore EOF */ +! if (*cptr == 0) continue; /* ignore blank */ + if (sim_log) fprintf (sim_log, "sim> %s\n", cptr); /* log cmd */ + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + sim_switches = 0; /* init switches */ + if (cmdp = find_cmd (gbuf)) /* lookup command */ + stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */ + else stat = SCPE_UNK; + if (stat >= SCPE_BASE) { /* error? */ + printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); + if (sim_log) fprintf (sim_log, "%s\n", + scp_error_messages[stat - SCPE_BASE]); + } + if (sim_vm_post != NULL) (*sim_vm_post) (TRUE); + } /* end while */ + + detach_all (0, TRUE); /* close files */ + sim_set_deboff (0, NULL); /* close debug */ + sim_set_logoff (0, NULL); /* close log */ + sim_set_notelnet (0, NULL); /* close Telnet */ + sim_ttclose (); /* close console */ + return 0; + } + + /* Find command routine */ + + CTAB *find_cmd (char *gbuf) + { + CTAB *cmdp = NULL; + + if (sim_vm_cmd) cmdp = find_ctab (sim_vm_cmd, gbuf); /* try ext commands */ +--- 657,720 ---- + else if (*argv[0]) { /* sim name arg? */ + char nbuf[PATH_MAX + 7], *np; /* "path.ini" */ + nbuf[0] = '"'; /* starting " */ + strncpy (nbuf + 1, argv[0], PATH_MAX + 1); /* copy sim name */ + if (np = match_ext (nbuf, "EXE")) *np = 0; /* remove .exe */ + strcat (nbuf, ".ini\""); /* add .ini" */ + stat = do_cmd (-1, nbuf); /* proc cmd file */ + } + + while (stat != SCPE_EXIT) { /* in case exit */ +! + if (cptr = sim_brk_getact (cbuf, CBUFSIZE)) /* pending action? */ +! printf ("sim> %s\n", cptr); /* echo */ + else if (sim_vm_read != NULL) /* sim routine? */ + cptr = (*sim_vm_read) (cbuf, CBUFSIZE, stdin); +! +! else +! #ifdef HAVE_READLINE +! cptr = readline_read_line(cbuf,CBUFSIZE,"sim>"); +! #else +! printf("sim>"); +! cptr = read_line (cbuf, CBUFSIZE, stdin); /* read command line */ +! #endif +! if (cptr == NULL){ /* ignore EOF */ +! printf("\r"); +! continue; +! } +! if (*cptr == 0) continue; /* ignore blank */ +! + if (sim_log) fprintf (sim_log, "sim> %s\n", cptr); /* log cmd */ + cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ + sim_switches = 0; /* init switches */ + if (cmdp = find_cmd (gbuf)) /* lookup command */ + stat = cmdp->action (cmdp->arg, cptr); /* if found, exec */ + else stat = SCPE_UNK; + if (stat >= SCPE_BASE) { /* error? */ + printf ("%s\n", scp_error_messages[stat - SCPE_BASE]); + if (sim_log) fprintf (sim_log, "%s\n", + scp_error_messages[stat - SCPE_BASE]); + } + if (sim_vm_post != NULL) (*sim_vm_post) (TRUE); + } /* end while */ + + detach_all (0, TRUE); /* close files */ + sim_set_deboff (0, NULL); /* close debug */ + sim_set_logoff (0, NULL); /* close log */ + sim_set_notelnet (0, NULL); /* close Telnet */ + sim_ttclose (); /* close console */ ++ ++ /* Write command history back to file */ ++ #ifdef HAVE_READLINE ++ readline_write_history(); ++ #endif ++ + return 0; + } + + /* Find command routine */ + + CTAB *find_cmd (char *gbuf) + { + CTAB *cmdp = NULL; + + if (sim_vm_cmd) cmdp = find_ctab (sim_vm_cmd, gbuf); /* try ext commands */ +*************** +*** 3068,3091 **** + for (tptr = cptr; tptr < (cptr + size); tptr++) { /* remove cr or nl */ + if ((*tptr == '\n') || (*tptr == '\r') || + (tptr == (cptr + size - 1))) { /* str max length? */ + *tptr = 0; /* terminate */ + break; + } + } + while (isspace (*cptr)) cptr++; /* trim leading spc */ + if (*cptr == ';') *cptr = 0; /* ignore comment */ + +- #if defined (HAVE_READLINE) +- add_history (cptr); + +- #endif + return cptr; + } + + /* get_glyph get next glyph (force upper case) + get_glyph_nc get next glyph (no conversion) + get_glyph_gen get next glyph (general case) + + Inputs: + iptr = pointer to input string + optr = pointer to output string +--- 3093,3113 ---- diff --git a/scp.h b/scp.h new file mode 100644 index 0000000..026e74b --- /dev/null +++ b/scp.h @@ -0,0 +1,122 @@ +/* scp.h: simulator control program headers + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 09-Aug-06 JDB Added assign_device and deassign_device + 14-Jul-06 RMS Added sim_activate_abs + 06-Jan-06 RMS Added fprint_stopped_gen + Changed arg type in sim_brk_test + 07-Feb-05 RMS Added ASSERT command + 09-Sep-04 RMS Added reset_all_p + 14-Feb-04 RMS Added debug prototypes (from Dave Hittner) + 02-Jan-04 RMS Split out from SCP +*/ + +#ifndef _SIM_SCP_H_ +#define _SIM_SCP_H_ 0 + +/* run_cmd parameters */ + +#define RU_RUN 0 /* run */ +#define RU_GO 1 /* go */ +#define RU_STEP 2 /* step */ +#define RU_CONT 3 /* continue */ +#define RU_BOOT 4 /* boot */ + +/* get_sim_opt parameters */ + +#define CMD_OPT_SW 001 /* switches */ +#define CMD_OPT_OF 002 /* output file */ +#define CMD_OPT_SCH 004 /* search */ +#define CMD_OPT_DFT 010 /* defaults */ + +/* Command processors */ + +t_stat reset_cmd (int32 flag, char *ptr); +t_stat exdep_cmd (int32 flag, char *ptr); +t_stat eval_cmd (int32 flag, char *ptr); +t_stat load_cmd (int32 flag, char *ptr); +t_stat run_cmd (int32 flag, char *ptr); +t_stat attach_cmd (int32 flag, char *ptr); +t_stat detach_cmd (int32 flag, char *ptr); +t_stat assign_cmd (int32 flag, char *ptr); +t_stat deassign_cmd (int32 flag, char *ptr); +t_stat save_cmd (int32 flag, char *ptr); +t_stat restore_cmd (int32 flag, char *ptr); +t_stat exit_cmd (int32 flag, char *ptr); +t_stat set_cmd (int32 flag, char *ptr); +t_stat show_cmd (int32 flag, char *ptr); +t_stat brk_cmd (int32 flag, char *ptr); +t_stat do_cmd (int32 flag, char *ptr); +t_stat assert_cmd (int32 flag, char *ptr); +t_stat help_cmd (int32 flag, char *ptr); +t_stat spawn_cmd (int32 flag, char *ptr); +t_stat echo_cmd (int32 flag, char *ptr); + +/* Utility routines */ + +t_stat sim_process_event (void); +t_stat sim_activate (UNIT *uptr, int32 interval); +t_stat sim_activate_abs (UNIT *uptr, int32 interval); +t_stat sim_cancel (UNIT *uptr); +int32 sim_is_active (UNIT *uptr); +double sim_gtime (void); +uint32 sim_grtime (void); +int32 sim_qcount (void); +t_stat attach_unit (UNIT *uptr, char *cptr); +t_stat detach_unit (UNIT *uptr); +t_stat assign_device (DEVICE *dptr, char *cptr); +t_stat deassign_device (DEVICE *dptr); +t_stat reset_all (uint32 start_device); +t_stat reset_all_p (uint32 start_device); +char *sim_dname (DEVICE *dptr); +t_stat get_yn (char *ques, t_stat deflt); +char *get_sim_opt (int32 opt, char *cptr, t_stat *st); +char *get_glyph (char *iptr, char *optr, char mchar); +char *get_glyph_nc (char *iptr, char *optr, char mchar); +t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status); +char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi, + uint32 rdx, t_addr max, char term); +t_stat get_ipaddr (char *cptr, uint32 *ipa, uint32 *ipp); +t_value strtotv (char *cptr, char **endptr, uint32 radix); +t_stat fprint_val (FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt); +CTAB *find_cmd (char *gbuf); +DEVICE *find_dev (char *ptr); +DEVICE *find_unit (char *ptr, UNIT **uptr); +DEVICE *find_dev_from_unit (UNIT *uptr); +REG *find_reg (char *ptr, char **optr, DEVICE *dptr); +CTAB *find_ctab (CTAB *tab, char *gbuf); +C1TAB *find_c1tab (C1TAB *tab, char *gbuf); +SHTAB *find_shtab (SHTAB *tab, char *gbuf); +BRKTAB *sim_brk_fnd (t_addr loc); +uint32 sim_brk_test (t_addr bloc, uint32 btyp); +void sim_brk_clrspc (uint32 spc); +char *match_ext (char *fnam, char *ext); +t_stat sim_cancel_step (void); +void sim_debug_u16 (uint32 dbits, DEVICE* dptr, const char* const* bitdefs, + uint16 before, uint16 after, int terminate); +void sim_debug (uint32 dbits, DEVICE* dptr, const char* fmt, ...); +void fprint_stopped_gen (FILE *st, t_stat v, REG *pc, DEVICE *dptr); + +#endif diff --git a/settings b/settings new file mode 100644 index 0000000..321246e --- /dev/null +++ b/settings @@ -0,0 +1,209 @@ + +PDP1D = PDP1 +PDP1 = pdp1_lp pdp1_cpu pdp1_stddev \ + pdp1_sys pdp1_dt pdp1_drm \ + pdp1_clk pdp1_dcs +PDP1_OPT = -I ${PDP1D} + + +NOVAD = NOVA +NOVA = nova_sys nova_cpu nova_dkp \ + nova_dsk nova_lp nova_mta \ + nova_plt nova_pt nova_clk \ + nova_tt nova_tt1 nova_qty +NOVA_OPT = -I ${NOVAD} + + +ECLIPSE = eclipse_cpu eclipse_tt nova_sys \ + nova_dkp nova_dsk nova_lp \ + nova_mta nova_plt nova_pt \ + nova_clk nova_tt1 nova_qty +ECLIPSE_OPT = -I ${NOVAD} -DECLIPSE -DUSE_INT64 + + +PDP18BD = PDP18B +PDP18B = pdp18b_dt pdp18b_drm pdp18b_cpu \ + pdp18b_lp pdp18b_mt pdp18b_rf \ + pdp18b_rp pdp18b_stddev pdp18b_sys \ + pdp18b_rb pdp18b_tt1 pdp18b_fpp +PDP4_OPT = -DPDP4 -I ${PDP18BD} +PDP7_OPT = -DPDP7 -I ${PDP18BD} +PDP9_OPT = -DPDP9 -I ${PDP18BD} +PDP15_OPT = -DPDP15 -I ${PDP18BD} + + +PDP11D = PDP11 +PDP11 = pdp11_fp pdp11_cpu pdp11_dz \ + pdp11_cis pdp11_lp pdp11_rk \ + pdp11_rl pdp11_rp pdp11_rx \ + pdp11_stddev pdp11_sys pdp11_tc \ + pdp11_tm pdp11_ts pdp11_io \ + pdp11_rq pdp11_tq pdp11_pclk \ + pdp11_ry pdp11_pt pdp11_hk \ + pdp11_xq pdp11_xu pdp11_vh \ + pdp11_rh pdp11_tu pdp11_cpumod \ + pdp11_cr pdp11_rf pdp11_dl \ + pdp11_ta pdp11_rc pdp11_kg \ + pdp11_ke pdp11_dc +PDP11_OPT = -DVM_PDP11 -I ${PDP11D} ${NETWORK_OPT} + + +VAXD = VAX +VAX = vax_cpu vax_cpu1 vax_fpa vax_io \ + vax_cis vax_octa vax_cmode \ + vax_mmu vax_stddev vax_sysdev \ + vax_sys vax_syscm vax_syslist \ + pdp11_rl pdp11_rq pdp11_ts \ + pdp11_dz pdp11_lp pdp11_tq \ + pdp11_xq pdp11_ry \ + pdp11_vh pdp11_cr +VAX_OPT = -DVM_VAX -DUSE_INT64 -DUSE_ADDR64 -I ${VAXD} -I ${PDP11D} ${NETWORK_OPT} + + +VAX780 = vax_cpu.c vax_cpu1.c vax_fpa.c \ + vax_cis.c vax_octa.c vax_cmode.c \ + vax_mmu.c vax_sys.c vax_syscm.c \ + vax780_stddev.c vax780_sbi.c \ + vax780_mem.c vax780_uba.c vax780_mba.c \ + vax780_fload.c vax780_syslist.c \ + pdp11_rl.c pdp11_rq.c pdp11_ts.c \ + pdp11_dz.c pdp11_lp.c pdp11_tq.c \ + pdp11_xu.c pdp11_ry.c pdp11_cr.c \ + pdp11_rp.c pdp11_tu.c pdp11_hk.c +VAX780_OPT = -DVM_VAX -DVAX_780 -DUSE_INT64 -DUSE_ADDR64 -I VAX -I ${PDP11D} ${NETWORK_OPT} + + +PDP10D = PDP10 +PDP10 = ${PDP10D}/pdp10_fe.c ${PDP11D}/pdp11_dz.c ${PDP10D}/pdp10_cpu.c \ + ${PDP10D}/pdp10_ksio.c ${PDP10D}/pdp10_lp20.c ${PDP10D}/pdp10_mdfp.c \ + ${PDP10D}/pdp10_pag.c ${PDP10D}/pdp10_rp.c ${PDP10D}/pdp10_sys.c \ + ${PDP10D}/pdp10_tim.c ${PDP10D}/pdp10_tu.c ${PDP10D}/pdp10_xtnd.c \ + ${PDP11D}/pdp11_pt.c ${PDP11D}/pdp11_ry.c ${PDP11D}/pdp11_xu.c \ + ${PDP11D}/pdp11_cr.c +PDP10_OPT = -DVM_PDP10 -DUSE_INT64 -I ${PDP10D} -I ${PDP11D} ${NETWORK_OPT} + + + +PDP8D = PDP8 +PDP8 = ${PDP8D}/pdp8_cpu.c ${PDP8D}/pdp8_clk.c ${PDP8D}/pdp8_df.c \ + ${PDP8D}/pdp8_dt.c ${PDP8D}/pdp8_lp.c ${PDP8D}/pdp8_mt.c \ + ${PDP8D}/pdp8_pt.c ${PDP8D}/pdp8_rf.c ${PDP8D}/pdp8_rk.c \ + ${PDP8D}/pdp8_rx.c ${PDP8D}/pdp8_sys.c ${PDP8D}/pdp8_tt.c \ + ${PDP8D}/pdp8_ttx.c ${PDP8D}/pdp8_rl.c ${PDP8D}/pdp8_tsc.c \ + ${PDP8D}/pdp8_td.c ${PDP8D}/pdp8_ct.c ${PDP8D}/pdp8_fpp.c +PDP8_OPT = -I ${PDP8D} + + +H316D = H316 +H316 = ${H316D}/h316_stddev.c ${H316D}/h316_lp.c ${H316D}/h316_cpu.c \ + ${H316D}/h316_sys.c ${H316D}/h316_mt.c ${H316D}/h316_fhd.c \ + ${H316D}/h316_dp.c +H316_OPT = -I ${H316D} + + +HP2100D = HP2100 +HP2100 = ${HP2100D}/hp2100_stddev.c ${HP2100D}/hp2100_dp.c ${HP2100D}/hp2100_dq.c \ + ${HP2100D}/hp2100_dr.c ${HP2100D}/hp2100_lps.c ${HP2100D}/hp2100_ms.c \ + ${HP2100D}/hp2100_mt.c ${HP2100D}/hp2100_mux.c ${HP2100D}/hp2100_cpu.c \ + ${HP2100D}/hp2100_fp.c ${HP2100D}/hp2100_sys.c ${HP2100D}/hp2100_lpt.c \ + ${HP2100D}/hp2100_ipl.c ${HP2100D}/hp2100_ds.c ${HP2100D}/hp2100_cpu0.c \ + ${HP2100D}/hp2100_cpu1.c ${HP2100D}/hp2100_cpu2.c ${HP2100D}/hp2100_cpu3.c \ + ${HP2100D}/hp2100_cpu4.c ${HP2100D}/hp2100_cpu5.c ${HP2100D}/hp2100_cpu6.c \ + ${HP2100D}/hp2100_cpu7.c ${HP2100D}/hp2100_fp1.c ${HP2100D}/hp2100_baci.c +HP2100_OPT = -DHAVE_INT64 -I ${HP2100D} + + +I1401D = I1401 +I1401 = ${I1401D}/i1401_lp.c ${I1401D}/i1401_cpu.c ${I1401D}/i1401_iq.c \ + ${I1401D}/i1401_cd.c ${I1401D}/i1401_mt.c ${I1401D}/i1401_dp.c \ + ${I1401D}/i1401_sys.c +I1401_OPT = -I ${I1401D} + + +I1620D = I1620 +I1620 = ${I1620D}/i1620_cd.c ${I1620D}/i1620_dp.c ${I1620D}/i1620_pt.c \ + ${I1620D}/i1620_tty.c ${I1620D}/i1620_cpu.c ${I1620D}/i1620_lp.c \ + ${I1620D}/i1620_fp.c ${I1620D}/i1620_sys.c +I1620_OPT = -I ${I1620D} + + +I7094D = I7094 +I7094 = ${I7094D}/i7094_cpu.c ${I7094D}/i7094_cpu1.c ${I7094D}/i7094_io.c \ + ${I7094D}/i7094_cd.c ${I7094D}/i7094_clk.c ${I7094D}/i7094_com.c \ + ${I7094D}/i7094_drm.c ${I7094D}/i7094_dsk.c ${I7094D}/i7094_sys.c \ + ${I7094D}/i7094_lp.c ${I7094D}/i7094_mt.c ${I7094D}/i7094_binloader.c +I7094_OPT = -DUSE_INT64 -I ${I7094D} + + +IBM1130D = Ibm1130 +IBM1130 = ${IBM1130D}/ibm1130_cpu.c ${IBM1130D}/ibm1130_cr.c \ + ${IBM1130D}/ibm1130_disk.c ${IBM1130D}/ibm1130_stddev.c \ + ${IBM1130D}/ibm1130_sys.c ${IBM1130D}/ibm1130_gdu.c \ + ${IBM1130D}/ibm1130_gui.c ${IBM1130D}/ibm1130_prt.c \ + ${IBM1130D}/ibm1130_fmt.c ${IBM1130D}/ibm1130_ptrp.c \ + ${IBM1130D}/ibm1130_plot.c ${IBM1130D}/ibm1130_sca.c \ + ${IBM1130D}/ibm1130_t2741.c +IBM1130_OPT = -I ${IBM1130D} + + +ID16D = Interdata +ID16 = ${ID16D}/id16_cpu.c ${ID16D}/id16_sys.c ${ID16D}/id_dp.c \ + ${ID16D}/id_fd.c ${ID16D}/id_fp.c ${ID16D}/id_idc.c ${ID16D}/id_io.c \ + ${ID16D}/id_lp.c ${ID16D}/id_mt.c ${ID16D}/id_pas.c ${ID16D}/id_pt.c \ + ${ID16D}/id_tt.c ${ID16D}/id_uvc.c ${ID16D}/id16_dboot.c ${ID16D}/id_ttp.c +ID16_OPT = -I ${ID16D} + + +ID32D = Interdata +ID32 = ${ID32D}/id32_cpu.c ${ID32D}/id32_sys.c ${ID32D}/id_dp.c \ + ${ID32D}/id_fd.c ${ID32D}/id_fp.c ${ID32D}/id_idc.c ${ID32D}/id_io.c \ + ${ID32D}/id_lp.c ${ID32D}/id_mt.c ${ID32D}/id_pas.c ${ID32D}/id_pt.c \ + ${ID32D}/id_tt.c ${ID32D}/id_uvc.c ${ID32D}/id32_dboot.c ${ID32D}/id_ttp.c +ID32_OPT = -I ${ID32D} + + +S3D = S3 +S3 = ${S3D}/s3_cd.c ${S3D}/s3_cpu.c ${S3D}/s3_disk.c ${S3D}/s3_lp.c \ + ${S3D}/s3_pkb.c ${S3D}/s3_sys.c +S3_OPT = -I ${S3D} + + +ALTAIRD = ALTAIR +ALTAIR = ${ALTAIRD}/altair_sio.c ${ALTAIRD}/altair_cpu.c ${ALTAIRD}/altair_dsk.c \ + ${ALTAIRD}/altair_sys.c +ALTAIR_OPT = -I ${ALTAIRD} + + +ALTAIRZ80D = AltairZ80 +ALTAIRZ80 = ${ALTAIRZ80D}/altairz80_cpu.c ${ALTAIRZ80D}/altairz80_cpu_nommu.c \ + ${ALTAIRZ80D}/altairz80_dsk.c ${ALTAIRZ80D}/disasm.c \ + ${ALTAIRZ80D}/altairz80_sio.c ${ALTAIRZ80D}/altairz80_sys.c \ + ${ALTAIRZ80D}/altairz80_hdsk.c ${ALTAIRZ80D}/altairz80_net.c \ + ${ALTAIRZ80D}/flashwriter2.c ${ALTAIRZ80D}/i86_decode.c \ + ${ALTAIRZ80D}/i86_ops.c ${ALTAIRZ80D}/i86_prim_ops.c \ + ${ALTAIRZ80D}/i8272.c ${ALTAIRZ80D}/insnsa.c ${ALTAIRZ80D}/insnsd.c \ + ${ALTAIRZ80D}/mfdc.c ${ALTAIRZ80D}/n8vem.c ${ALTAIRZ80D}/vfdhd.c \ + ${ALTAIRZ80D}/s100_disk1a.c ${ALTAIRZ80D}/s100_disk2.c \ + ${ALTAIRZ80D}/s100_fif.c ${ALTAIRZ80D}/s100_mdriveh.c \ + ${ALTAIRZ80D}/s100_mdsad.c ${ALTAIRZ80D}/s100_selchan.c \ + ${ALTAIRZ80D}/s100_ss1.c ${ALTAIRZ80D}/s100_64fdc.c \ + ${ALTAIRZ80D}/s100_scp300f.c ${ALTAIRZ80D}/sim_imd.c \ + ${ALTAIRZ80D}/wd179x.c +ALTAIRZ80_OPT = -I ${ALTAIRZ80D} + + +GRID = GRI +GRI = ${GRID}/gri_cpu.c ${GRID}/gri_stddev.c ${GRID}/gri_sys.c +GRI_OPT = -I ${GRID} + + +LGPD = LGP +LGP = ${LGPD}/lgp_cpu.c ${LGPD}/lgp_stddev.c ${LGPD}/lgp_sys.c +LGP_OPT = -I ${LGPD} + + +SDSD = SDS +SDS = ${SDSD}/sds_cpu.c ${SDSD}/sds_drm.c ${SDSD}/sds_dsk.c ${SDSD}/sds_io.c \ + ${SDSD}/sds_lp.c ${SDSD}/sds_mt.c ${SDSD}/sds_mux.c ${SDSD}/sds_rad.c \ + ${SDSD}/sds_stddev.c ${SDSD}/sds_sys.c +SDS_OPT = -I ${SDSD} diff --git a/sim_console.c b/sim_console.c new file mode 100644 index 0000000..9a0044d --- /dev/null +++ b/sim_console.c @@ -0,0 +1,1094 @@ +/* sim_console.c: simulator console I/O library + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Sep-06 RMS Fixed non-printable characters in KSR mode + 22-Jun-06 RMS Implemented SET/SHOW PCHAR + 31-May-06 JDB Fixed bug if SET CONSOLE DEBUG with no argument + 22-Nov-05 RMS Added central input/output conversion support + 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy + 28-Oct-04 JDB Fixed SET CONSOLE to allow comma-separated parameters + 20-Aug-04 RMS Added OS/2 EMX fixes (from Holger Veit) + 14-Jul-04 RMS Revised Windows console code (from Dave Bryan) + 28-May-04 RMS Added SET/SHOW CONSOLE + RMS Added break, delete character maps + 02-Jan-04 RMS Removed timer routines, added Telnet console routines + RMS Moved console logging to OS-independent code + 25-Apr-03 RMS Added long seek support from Mark Pizzolato + Added Unix priority control from Mark Pizzolato + 24-Sep-02 RMS Removed VT support, added Telnet console support + Added CGI support (from Brian Knittel) + Added MacOS sleep (from Peter Schorn) + 14-Jul-02 RMS Added Windows priority control from Mark Pizzolato + 20-May-02 RMS Added Windows VT support from Fischer Franz + 01-Feb-02 RMS Added VAX fix from Robert Alan Byer + 19-Sep-01 RMS More Mac changes + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 20-Jul-01 RMS Added Macintosh support (from Louis Chretien, Peter Schorn, + and Ben Supnik) + 15-May-01 RMS Added logging support + 05-Mar-01 RMS Added clock calibration support + 08-Dec-00 BKR Added OS/2 support (from Bruce Ray) + 18-Aug-98 RMS Added BeOS support + 13-Oct-97 RMS Added NetBSD terminal support + 25-Jan-97 RMS Added POSIX terminal I/O support + 02-Jan-97 RMS Fixed bug in sim_poll_kbd + + This module implements the following routines to support terminal I/O: + + sim_poll_kbd - poll for keyboard input + sim_putchar - output character to console + sim_putchar_s - output character to console, stall if congested + sim_set_console - set console parameters + sim_show_console - show console parameters + sim_tt_inpcvt - convert input character per mode + sim_tt_outcvt - convert output character per mode + + sim_ttinit - called once to get initial terminal state + sim_ttrun - called to put terminal into run state + sim_ttcmd - called to return terminal to command state + sim_ttclose - called once before the simulator exits + sim_os_poll_kbd - poll for keyboard input + sim_os_putchar - output character to console + + The first group is OS-independent; the second group is OS-dependent. + + The following routines are exposed but deprecated: + + sim_set_telnet - set console to Telnet port + sim_set_notelnet - close console Telnet port + sim_show_telnet - show console status +*/ + +#include "sim_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +#define KMAP_WRU 0 +#define KMAP_BRK 1 +#define KMAP_DEL 2 +#define KMAP_MASK 0377 +#define KMAP_NZ 0400 + +int32 sim_int_char = 005; /* interrupt character */ +int32 sim_brk_char = 000; /* break character */ +int32 sim_tt_pchar = 0x00002780; +#if defined (_WIN32) || defined (__OS2__) || (defined (__MWERKS__) && defined (macintosh)) +int32 sim_del_char = '\b'; /* delete character */ +#else +int32 sim_del_char = 0177; +#endif +TMLN sim_con_ldsc = { 0 }; /* console line descr */ +TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc }; /* console line mux */ + +extern volatile int32 stop_cpu; +extern int32 sim_quiet, sim_deb_close; +extern FILE *sim_log, *sim_deb; +extern DEVICE *sim_devices[]; + +/* Set/show data structures */ + +static CTAB set_con_tab[] = { + { "WRU", &sim_set_kmap, KMAP_WRU | KMAP_NZ }, + { "BRK", &sim_set_kmap, KMAP_BRK }, + { "DEL", &sim_set_kmap, KMAP_DEL |KMAP_NZ }, + { "PCHAR", &sim_set_pchar, 0 }, + { "TELNET", &sim_set_telnet, 0 }, + { "NOTELNET", &sim_set_notelnet, 0 }, + { "LOG", &sim_set_logon, 0 }, + { "NOLOG", &sim_set_logoff, 0 }, + { "DEBUG", &sim_set_debon, 0 }, + { "NODEBUG", &sim_set_deboff, 0 }, + { NULL, NULL, 0 } + }; + +static SHTAB show_con_tab[] = { + { "WRU", &sim_show_kmap, KMAP_WRU }, + { "BRK", &sim_show_kmap, KMAP_BRK }, + { "DEL", &sim_show_kmap, KMAP_DEL }, + { "PCHAR", &sim_show_pchar, 0 }, + { "LOG", &sim_show_log, 0 }, + { "TELNET", &sim_show_telnet, 0 }, + { "DEBUG", &sim_show_debug, 0 }, + { NULL, NULL, 0 } + }; + +static int32 *cons_kmap[] = { + &sim_int_char, + &sim_brk_char, + &sim_del_char + }; + +/* Console I/O package. + + The console terminal can be attached to the controlling window + or to a Telnet connection. If attached to a Telnet connection, + the console is described by internal terminal multiplexor + sim_con_tmxr and internal terminal line description sim_con_ldsc. +*/ + +/* SET CONSOLE command */ + +t_stat sim_set_console (int32 flag, char *cptr) +{ +char *cvptr, gbuf[CBUFSIZE]; +CTAB *ctptr; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; +while (*cptr != 0) { /* do all mods */ + cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ + if (cvptr = strchr (gbuf, '=')) *cvptr++ = 0; /* = value? */ + get_glyph (gbuf, gbuf, 0); /* modifier to UC */ + if (ctptr = find_ctab (set_con_tab, gbuf)) { /* match? */ + r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ + if (r != SCPE_OK) return r; + } + else return SCPE_NOPARAM; + } +return SCPE_OK; +} + +/* SHOW CONSOLE command */ + +t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; +SHTAB *shptr; +int32 i; + +if (*cptr == 0) { /* show all */ + for (i = 0; show_con_tab[i].name; i++) + show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr); + return SCPE_OK; + } +while (*cptr != 0) { + cptr = get_glyph (cptr, gbuf, ','); /* get modifier */ + if (shptr = find_shtab (show_con_tab, gbuf)) + shptr->action (st, dptr, uptr, shptr->arg, cptr); + else return SCPE_NOPARAM; + } +return SCPE_OK; +} + +/* Set keyboard map */ + +t_stat sim_set_kmap (int32 flag, char *cptr) +{ +DEVICE *dptr = sim_devices[0]; +int32 val, rdx; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; +if (dptr->dradix == 16) rdx = 16; +else rdx = 8; +val = (int32) get_uint (cptr, rdx, 0177, &r); +if ((r != SCPE_OK) || + ((val == 0) && (flag & KMAP_NZ))) return SCPE_ARG; +*(cons_kmap[flag & KMAP_MASK]) = val; +return SCPE_OK; +} + +/* Show keyboard map */ + +t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (sim_devices[0]->dradix == 16) + fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); +else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); +return SCPE_OK; +} + +/* Set printable characters */ + +t_stat sim_set_pchar (int32 flag, char *cptr) +{ +DEVICE *dptr = sim_devices[0]; +uint32 val, rdx; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; +if (dptr->dradix == 16) rdx = 16; +else rdx = 8; +val = (uint32) get_uint (cptr, rdx, 0xFFFFFFFF, &r); +if ((r != SCPE_OK) || + ((val & 0x00002400) == 0)) return SCPE_ARG; +sim_tt_pchar = val; +return SCPE_OK; +} + +/* Show printable characters */ + +t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (sim_devices[0]->dradix == 16) + fprintf (st, "pchar mask = %X\n", sim_tt_pchar); +else fprintf (st, "pchar mask = %o\n", sim_tt_pchar); +return SCPE_OK; +} + +/* Set log routine */ + +t_stat sim_set_logon (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE]; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; /* need arg */ +cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ +if (*cptr != 0) return SCPE_2MARG; /* now eol? */ +sim_set_logoff (0, NULL); /* close cur log */ +sim_log = sim_fopen (gbuf, "a"); /* open log */ +if (sim_log == NULL) return SCPE_OPENERR; /* error? */ +if (!sim_quiet) printf ("Logging to file \"%s\"\n", gbuf); +fprintf (sim_log, "Logging to file \"%s\"\n", gbuf); /* start of log */ +return SCPE_OK; +} + +/* Set nolog routine */ + +t_stat sim_set_logoff (int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) return SCPE_2MARG; /* now eol? */ +if (sim_log == NULL) return SCPE_OK; /* no log? */ +if (!sim_quiet) printf ("Log file closed\n"); +fprintf (sim_log, "Log file closed\n"); /* close log */ +fclose (sim_log); +sim_log = NULL; +return SCPE_OK; +} + +/* Show log status */ + +t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) return SCPE_2MARG; +if (sim_log) fputs ("Logging enabled\n", st); +else fputs ("Logging disabled\n", st); +return SCPE_OK; +} + +/* Set debug routine */ + +t_stat sim_set_debon (int32 flag, char *cptr) +{ +char *tptr, gbuf[CBUFSIZE]; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; /* too few arguments? */ +tptr = get_glyph (cptr, gbuf, 0); /* get file name */ +if (*tptr != 0) return SCPE_2MARG; /* now eol? */ +sim_set_deboff (0, NULL); /* close cur debug */ +if (strcmp (gbuf, "LOG") == 0) { /* debug to log? */ + if (sim_log == NULL) return SCPE_ARG; /* any log? */ + sim_deb = sim_log; + } +else if (strcmp (gbuf, "STDOUT") == 0) sim_deb = stdout; /* debug to stdout? */ +else if (strcmp (gbuf, "STDERR") == 0) sim_deb = stderr; /* debug to stderr? */ +else { + cptr = get_glyph_nc (cptr, gbuf, 0); /* reparse */ + sim_deb = sim_fopen (gbuf, "a"); /* open debug */ + if (sim_deb == NULL) return SCPE_OPENERR; /* error? */ + sim_deb_close = 1; /* need close */ + } +if (!sim_quiet) printf ("Debug output to \"%s\"\n", gbuf); +if (sim_log) fprintf (sim_log, "Debug output to \"%s\"\n", gbuf); +return SCPE_OK; +} + +/* Set nodebug routine */ + +t_stat sim_set_deboff (int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) return SCPE_2MARG; /* now eol? */ +if (sim_deb == NULL) return SCPE_OK; /* no log? */ +if (!sim_quiet) printf ("Debug output disabled\n"); +if (sim_log) fprintf (sim_log, "Debug output disabled\n"); +if (sim_deb_close) fclose (sim_deb); /* close if needed */ +sim_deb_close = 0; +sim_deb = NULL; +return SCPE_OK; +} + +/* Show debug routine */ + +t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) return SCPE_2MARG; +if (sim_deb) fputs ("Debug output enabled\n", st); +else fputs ("Debug output disabled\n", st); +return SCPE_OK; +} + +/* Set console to Telnet port */ + +t_stat sim_set_telnet (int32 flg, char *cptr) +{ +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; /* too few arguments? */ +if (sim_con_tmxr.master) return SCPE_ALATT; /* already open? */ +return tmxr_open_master (&sim_con_tmxr, cptr); /* open master socket */ +} + +/* Close console Telnet port */ + +t_stat sim_set_notelnet (int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) return SCPE_2MARG; /* too many arguments? */ +if (sim_con_tmxr.master == 0) return SCPE_OK; /* ignore if already closed */ +return tmxr_close_master (&sim_con_tmxr); /* close master socket */ +} + +/* Show console Telnet status */ + +t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) return SCPE_2MARG; +if (sim_con_tmxr.master == 0) + fprintf (st, "Connected to console window\n"); +else if (sim_con_ldsc.conn == 0) + fprintf (st, "Listening on port %d\n", sim_con_tmxr.port); +else { + fprintf (st, "Listening on port %d, connected to socket %d\n", + sim_con_tmxr.port, sim_con_ldsc.conn); + tmxr_fconns (st, &sim_con_ldsc, -1); + tmxr_fstats (st, &sim_con_ldsc, -1); + } +return SCPE_OK; +} + +/* Check connection before executing */ + +t_stat sim_check_console (int32 sec) +{ +int32 c, i; + +if (sim_con_tmxr.master == 0) return SCPE_OK; /* not Telnet? done */ +if (sim_con_ldsc.conn) { /* connected? */ + tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */ + if (sim_con_ldsc.conn) return SCPE_OK; /* still connected? */ + } +for (i = 0; i < sec; i++) { /* loop */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + if (i) { /* if delayed */ + printf ("Running\n"); /* print transition */ + fflush (stdout); + } + return SCPE_OK; /* ready to proceed */ + } + c = sim_os_poll_kbd (); /* check for stop char */ + if ((c == SCPE_STOP) || stop_cpu) return SCPE_STOP; + if ((i % 10) == 0) { /* Status every 10 sec */ + printf ("Waiting for console Telnet connection\n"); + fflush (stdout); + } + sim_os_sleep (1); /* wait 1 second */ + } +return SCPE_TTMO; /* timed out */ +} + +/* Poll for character */ + +t_stat sim_poll_kbd (void) +{ +int32 c; + +c = sim_os_poll_kbd (); /* get character */ +if ((c == SCPE_STOP) || (sim_con_tmxr.master == 0)) /* ^E or not Telnet? */ + return c; /* in-window */ +if (sim_con_ldsc.conn == 0) return SCPE_LOST; /* no Telnet conn? */ +tmxr_poll_rx (&sim_con_tmxr); /* poll for input */ +if (c = tmxr_getc_ln (&sim_con_ldsc)) /* any char? */ + return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG; +return SCPE_OK; +} + +/* Output character */ + +t_stat sim_putchar (int32 c) +{ +if (sim_log) fputc (c, sim_log); /* log file? */ +if (sim_con_tmxr.master == 0) /* not Telnet? */ + return sim_os_putchar (c); /* in-window version */ +if (sim_con_ldsc.conn == 0) return SCPE_LOST; /* no Telnet conn? */ +tmxr_putc_ln (&sim_con_ldsc, c); /* output char */ +tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ +return SCPE_OK; +} + +t_stat sim_putchar_s (int32 c) +{ +t_stat r; + +if (sim_log) fputc (c, sim_log); /* log file? */ +if (sim_con_tmxr.master == 0) /* not Telnet? */ + return sim_os_putchar (c); /* in-window version */ +if (sim_con_ldsc.conn == 0) return SCPE_LOST; /* no Telnet conn? */ +if (sim_con_ldsc.xmte == 0) r = SCPE_STALL; /* xmt disabled? */ +else r = tmxr_putc_ln (&sim_con_ldsc, c); /* no, Telnet output */ +tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ +return r; /* return status */ +} + +/* Input character processing */ + +int32 sim_tt_inpcvt (int32 c, uint32 mode) +{ +uint32 md = mode & TTUF_M_MODE; + +if (md != TTUF_MODE_8B) { + c = c & 0177; + if (md == TTUF_MODE_UC) { + if (islower (c)) c = toupper (c); + if (mode & TTUF_KSR) c = c | 0200; + } + } +else c = c & 0377; +return c; +} + +/* Output character processing */ + +int32 sim_tt_outcvt (int32 c, uint32 mode) +{ +uint32 md = mode & TTUF_M_MODE; + +if (md != TTUF_MODE_8B) { + c = c & 0177; + if (md == TTUF_MODE_UC) { + if (islower (c)) c = toupper (c); + if ((mode & TTUF_KSR) && (c >= 0140)) + return -1; + } + if (((md == TTUF_MODE_UC) || (md == TTUF_MODE_7P)) && + ((c == 0177) || + ((c < 040) && !((sim_tt_pchar >> c) & 1)))) + return -1; + } +else c = c & 0377; +return c; +} + +/* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */ + +#if defined (VMS) + +#if defined(__VAX) +#define sys$assign SYS$ASSIGN +#define sys$qiow SYS$QIOW +#endif + +#include +#include +#include +#include +#include +#include +#include + +#define EFN 0 +uint32 tty_chan = 0; + +typedef struct { + unsigned short sense_count; + unsigned char sense_first_char; + unsigned char sense_reserved; + unsigned int stat; + unsigned int stat2; } SENSE_BUF; + +typedef struct { + unsigned short status; + unsigned short count; + unsigned int dev_status; } IOSB; + +SENSE_BUF cmd_mode = { 0 }; +SENSE_BUF run_mode = { 0 }; + +t_stat sim_ttinit (void) +{ +unsigned int status; +IOSB iosb; +$DESCRIPTOR (terminal_device, "tt"); + +status = sys$assign (&terminal_device, &tty_chan, 0, 0); +if (status != SS$_NORMAL) return SCPE_TTIERR; +status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE, &iosb, 0, 0, + &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +run_mode = cmd_mode; +run_mode.stat = cmd_mode.stat | TT$M_NOECHO & ~(TT$M_HOSTSYNC | TT$M_TTSYNC); +run_mode.stat2 = cmd_mode.stat2 | TT2$M_PASTHRU; +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +unsigned int status; +IOSB iosb; + +status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, + &run_mode, sizeof (run_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +unsigned int status; +IOSB iosb; + +status = sys$qiow (EFN, tty_chan, IO$_SETMODE, &iosb, 0, 0, + &cmd_mode, sizeof (cmd_mode), 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return sim_ttcmd (); +} + +t_stat sim_os_poll_kbd (void) +{ +unsigned int status, term[2]; +unsigned char buf[4]; +IOSB iosb; +SENSE_BUF sense; + +term[0] = 0; term[1] = 0; +status = sys$qiow (EFN, tty_chan, IO$_SENSEMODE | IO$M_TYPEAHDCNT, &iosb, + 0, 0, &sense, 8, 0, term, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTIERR; +if (sense.sense_count == 0) return SCPE_OK; +term[0] = 0; term[1] = 0; +status = sys$qiow (EFN, tty_chan, + IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, + &iosb, 0, 0, buf, 1, 0, term, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_OK; +if (buf[0] == sim_int_char) return SCPE_STOP; +if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; +return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_os_putchar (int32 out) +{ +unsigned int status; +char c; +IOSB iosb; + +c = out; +status = sys$qiow (EFN, tty_chan, IO$_WRITELBLK | IO$M_NOFORMAT, + &iosb, 0, 0, &c, 1, 0, 0, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_TTOERR; +return SCPE_OK; +} + +/* Win32 routines */ + +#elif defined (_WIN32) + +#include +#include +#include +#include +#define RAW_MODE 0 +static HANDLE std_input; +static DWORD saved_mode; + +t_stat sim_ttinit (void) +{ +std_input = GetStdHandle (STD_INPUT_HANDLE); +if ((std_input == INVALID_HANDLE_VALUE) || + !GetConsoleMode (std_input, &saved_mode)) return SCPE_TTYERR; +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +if (!GetConsoleMode(std_input, &saved_mode) || + !SetConsoleMode(std_input, RAW_MODE)) return SCPE_TTYERR; +if (sim_log) { + fflush (sim_log); + _setmode (_fileno (sim_log), _O_BINARY); + } +SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +if (sim_log) { + fflush (sim_log); + _setmode (_fileno (sim_log), _O_TEXT); + } +SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_NORMAL); +if (!SetConsoleMode(std_input, saved_mode)) return SCPE_TTYERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return SCPE_OK; +} + +t_stat sim_os_poll_kbd (void) +{ +int c; + +if (!_kbhit ()) return SCPE_OK; +c = _getch (); +if ((c & 0177) == sim_del_char) c = 0177; +if ((c & 0177) == sim_int_char) return SCPE_STOP; +if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; +return c | SCPE_KFLAG; +} + +t_stat sim_os_putchar (int32 c) +{ +if (c != 0177) _putch (c); +return SCPE_OK; +} + +/* OS/2 routines, from Bruce Ray and Holger Veit */ + +#elif defined (__OS2__) + +#include + +t_stat sim_ttinit (void) +{ +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return SCPE_OK; +} + +t_stat sim_os_poll_kbd (void) +{ +int c; + +#if defined (__EMX__) +switch (c = _read_kbd(0,0,0)) { /* EMX has _read_kbd */ + + case -1: /* no char*/ + return SCPE_OK; + + case 0: /* char pending */ + c = _read_kbd(0,1,0); + break; + + default: /* got char */ + break; + } +#else +if (!kbhit ()) return SCPE_OK; +c = getch(); +#endif +if ((c & 0177) == sim_del_char) c = 0177; +if ((c & 0177) == sim_int_char) return SCPE_STOP; +if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; +return c | SCPE_KFLAG; +} + +t_stat sim_os_putchar (int32 c) +{ +if (c != 0177) { +#if defined (__EMX__) + putchar (c); +#else + putch (c); +#endif + fflush (stdout); + } +return SCPE_OK; +} + +/* Metrowerks CodeWarrior Macintosh routines, from Louis Chretien and + Peter Schorn */ + +#elif defined (__MWERKS__) && defined (macintosh) + +#include +#include +#include +#include +#include +#include +#include +#include + +/* function prototypes */ + +Boolean SIOUXIsAppWindow(WindowPtr window); +void SIOUXDoMenuChoice(long menuValue); +void SIOUXUpdateMenuItems(void); +void SIOUXUpdateScrollbar(void); +int ps_kbhit(void); +int ps_getch(void); + +extern char sim_name[]; +extern pSIOUXWin SIOUXTextWindow; +static CursHandle iBeamCursorH = NULL; /* contains the iBeamCursor */ + +static void updateCursor(void) { + WindowPtr window; + window = FrontWindow(); + if (SIOUXIsAppWindow(window)) { + GrafPtr savePort; + Point localMouse; + GetPort(&savePort); + SetPort(window); +#if TARGET_API_MAC_CARBON + GetGlobalMouse(&localMouse); +#else + localMouse = LMGetMouseLocation(); +#endif + GlobalToLocal(&localMouse); + if (PtInRect(localMouse, &(*SIOUXTextWindow->edit)->viewRect) && iBeamCursorH) { + SetCursor(*iBeamCursorH); + } + else { + SetCursor(&qd.arrow); + } + TEIdle(SIOUXTextWindow->edit); + SetPort(savePort); + } + else { + SetCursor(&qd.arrow); + TEIdle(SIOUXTextWindow->edit); + } + return; +} + +int ps_kbhit(void) { + EventRecord event; + int c; + updateCursor(); + SIOUXUpdateScrollbar(); + while (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | + highLevelEventMask | diskEvt, &event)) { + SIOUXHandleOneEvent(&event); + } + if (SIOUXQuitting) { + exit(1); + } + if (EventAvail(keyDownMask,&event)) { + c = event.message&charCodeMask; + if ((event.modifiers & cmdKey) && (c > 0x20)) { + GetNextEvent(keyDownMask, &event); + SIOUXHandleOneEvent(&event); + if (SIOUXQuitting) { + exit(1); + } + return false; + } + return true; + } + else { + return false; + } +} + +int ps_getch(void) { + int c; + EventRecord event; + fflush(stdout); + updateCursor(); + while(!GetNextEvent(keyDownMask,&event)) { + if (GetNextEvent(updateMask | osMask | mDownMask | mUpMask | activMask | + highLevelEventMask | diskEvt, &event)) { + SIOUXUpdateScrollbar(); + SIOUXHandleOneEvent(&event); + } + } + if (SIOUXQuitting) { + exit(1); + } + c = event.message&charCodeMask; + if ((event.modifiers & cmdKey) && (c > 0x20)) { + SIOUXUpdateMenuItems(); + SIOUXDoMenuChoice(MenuKey(c)); + } + if (SIOUXQuitting) { + exit(1); + } + return c; +} + +/* Note that this only works if the call to sim_ttinit comes before any output to the console */ + +t_stat sim_ttinit (void) { + int i; + /* this blank will later be replaced by the number of characters */ + char title[50] = " "; + unsigned char ptitle[50]; + SIOUXSettings.autocloseonquit = TRUE; + SIOUXSettings.asktosaveonclose = FALSE; + SIOUXSettings.showstatusline = FALSE; + SIOUXSettings.columns = 80; + SIOUXSettings.rows = 40; + SIOUXSettings.toppixel = 42; + SIOUXSettings.leftpixel = 6; + iBeamCursorH = GetCursor(iBeamCursor); + strcat(title, sim_name); + strcat(title, " Simulator"); + title[0] = strlen(title) - 1; /* Pascal string done */ + for (i = 0; i <= title[0]; i++) { /* copy to unsigned char */ + ptitle[i] = title[i]; + } + SIOUXSetTitle(ptitle); + return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return SCPE_OK; +} + +t_stat sim_os_poll_kbd (void) +{ +int c; + +if (!ps_kbhit ()) return SCPE_OK; +c = ps_getch(); +if ((c & 0177) == sim_del_char) c = 0177; +if ((c & 0177) == sim_int_char) return SCPE_STOP; +if (sim_brk_char && ((c & 0177) == sim_brk_char)) return SCPE_BREAK; +return c | SCPE_KFLAG; +} + +t_stat sim_os_putchar (int32 c) +{ +if (c != 0177) { + putchar (c); + fflush (stdout); + } +return SCPE_OK; +} + +/* BSD UNIX routines */ + +#elif defined (BSDTTY) + +#include +#include +#include + +struct sgttyb cmdtty,runtty; /* V6/V7 stty data */ +struct tchars cmdtchars,runtchars; /* V7 editing */ +struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */ +int cmdfl,runfl; /* TTY flags */ + +t_stat sim_ttinit (void) +{ +cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */ +runfl = cmdfl | FNDELAY; +if (ioctl (0, TIOCGETP, &cmdtty) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCGETC, &cmdtchars) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCGLTC, &cmdltchars) < 0) return SCPE_TTIERR; +runtty = cmdtty; /* initial run state */ +runtty.sg_flags = cmdtty.sg_flags & ~(ECHO|CRMOD) | CBREAK; +runtchars.t_intrc = sim_int_char; /* interrupt */ +runtchars.t_quitc = 0xFF; /* no quit */ +runtchars.t_startc = 0xFF; /* no host sync */ +runtchars.t_stopc = 0xFF; +runtchars.t_eofc = 0xFF; +runtchars.t_brkc = 0xFF; +runltchars.t_suspc = 0xFF; /* no specials of any kind */ +runltchars.t_dsuspc = 0xFF; +runltchars.t_rprntc = 0xFF; +runltchars.t_flushc = 0xFF; +runltchars.t_werasc = 0xFF; +runltchars.t_lnextc = 0xFF; +return SCPE_OK; /* return success */ +} + +t_stat sim_ttrun (void) +{ +runtchars.t_intrc = sim_int_char; /* in case changed */ +fcntl (0, F_SETFL, runfl); /* non-block mode */ +if (ioctl (0, TIOCSETP, &runtty) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSETC, &runtchars) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSLTC, &runltchars) < 0) return SCPE_TTIERR; +nice (10); /* lower priority */ +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +nice (-10); /* restore priority */ +fcntl (0, F_SETFL, cmdfl); /* block mode */ +if (ioctl (0, TIOCSETP, &cmdtty) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSETC, &cmdtchars) < 0) return SCPE_TTIERR; +if (ioctl (0, TIOCSLTC, &cmdltchars) < 0) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return sim_ttcmd (); +} + +t_stat sim_os_poll_kbd (void) +{ +int status; +unsigned char buf[1]; + +status = read (0, buf, 1); +if (status != 1) return SCPE_OK; +if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; +else return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_os_putchar (int32 out) +{ +char c; + +c = out; +write (1, &c, 1); +return SCPE_OK; +} + +/* POSIX UNIX routines, from Leendert Van Doorn */ + +#else + +#include +#include + +struct termios cmdtty, runtty; +static int prior_norm = 1; + +t_stat sim_ttinit (void) +{ +if (!isatty (fileno (stdin))) return SCPE_OK; /* skip if !tty */ +if (tcgetattr (0, &cmdtty) < 0) return SCPE_TTIERR; /* get old flags */ +runtty = cmdtty; +runtty.c_lflag = runtty.c_lflag & ~(ECHO | ICANON); /* no echo or edit */ +runtty.c_oflag = runtty.c_oflag & ~OPOST; /* no output edit */ +runtty.c_iflag = runtty.c_iflag & ~ICRNL; /* no cr conversion */ +runtty.c_cc[VINTR] = sim_int_char; /* interrupt */ +runtty.c_cc[VQUIT] = 0; /* no quit */ +runtty.c_cc[VERASE] = 0; +runtty.c_cc[VKILL] = 0; +runtty.c_cc[VEOF] = 0; +runtty.c_cc[VEOL] = 0; +runtty.c_cc[VSTART] = 0; /* no host sync */ +runtty.c_cc[VSUSP] = 0; +runtty.c_cc[VSTOP] = 0; +#if defined (VREPRINT) +runtty.c_cc[VREPRINT] = 0; /* no specials */ +#endif +#if defined (VDISCARD) +runtty.c_cc[VDISCARD] = 0; +#endif +#if defined (VWERASE) +runtty.c_cc[VWERASE] = 0; +#endif +#if defined (VLNEXT) +runtty.c_cc[VLNEXT] = 0; +#endif +runtty.c_cc[VMIN] = 0; /* no waiting */ +runtty.c_cc[VTIME] = 0; +#if defined (VDSUSP) +runtty.c_cc[VDSUSP] = 0; +#endif +#if defined (VSTATUS) +runtty.c_cc[VSTATUS] = 0; +#endif +return SCPE_OK; +} + +t_stat sim_ttrun (void) +{ +if (!isatty (fileno (stdin))) return SCPE_OK; /* skip if !tty */ +runtty.c_cc[VINTR] = sim_int_char; /* in case changed */ +if (tcsetattr (0, TCSAFLUSH, &runtty) < 0) return SCPE_TTIERR; +if (prior_norm) { /* at normal pri? */ + errno = 0; + nice (10); /* try to lower pri */ + prior_norm = errno; /* if no error, done */ + } +return SCPE_OK; +} + +t_stat sim_ttcmd (void) +{ +if (!isatty (fileno (stdin))) return SCPE_OK; /* skip if !tty */ +if (!prior_norm) { /* priority down? */ + errno = 0; + nice (-10); /* try to raise pri */ + prior_norm = (errno == 0); /* if no error, done */ + } +if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0) return SCPE_TTIERR; +return SCPE_OK; +} + +t_stat sim_ttclose (void) +{ +return sim_ttcmd (); +} + +t_stat sim_os_poll_kbd (void) +{ +int status; +unsigned char buf[1]; + +status = read (0, buf, 1); +if (status != 1) return SCPE_OK; +if (sim_brk_char && (buf[0] == sim_brk_char)) return SCPE_BREAK; +else return (buf[0] | SCPE_KFLAG); +} + +t_stat sim_os_putchar (int32 out) +{ +char c; + +c = out; +write (1, &c, 1); +return SCPE_OK; +} + +#endif diff --git a/sim_console.h b/sim_console.h new file mode 100644 index 0000000..7a69d7e --- /dev/null +++ b/sim_console.h @@ -0,0 +1,81 @@ +/* sim_console.h: simulator console I/O library headers + + Copyright (c) 1993-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 22-Jun-06 RMS Implemented SET/SHOW PCHAR + 22-Nov-05 RMS Added central input/output conversion support + 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy + 28-May-04 RMS Added SET/SHOW CONSOLE + 02-Jan-04 RMS Removed timer routines, added Telnet console routines +*/ + +#ifndef _SIM_CONSOLE_H_ +#define _SIM_CONSOLE_H_ 0 + +#define TTUF_V_MODE (UNIT_V_UF + 0) +#define TTUF_W_MODE 2 +#define TTUF_MODE_7B 0 +#define TTUF_MODE_8B 1 +#define TTUF_MODE_UC 2 +#define TTUF_MODE_7P 3 +#define TTUF_KSR (1u << TTUF_W_MODE) +#define TTUF_M_MODE ((1u << TTUF_W_MODE) - 1) +#define TTUF_V_UF (TTUF_V_MODE + TTUF_W_MODE) +#define TT_MODE (TTUF_M_MODE << TTUF_V_MODE) +#define TT_MODE_7B (TTUF_MODE_7B << TTUF_V_MODE) +#define TT_MODE_8B (TTUF_MODE_8B << TTUF_V_MODE) +#define TT_MODE_UC (TTUF_MODE_UC << TTUF_V_MODE) +#define TT_MODE_7P (TTUF_MODE_7P << TTUF_V_MODE) +#define TT_MODE_KSR (TT_MODE_UC) +#define TT_GET_MODE(x) (((x) >> TTUF_V_MODE) & TTUF_M_MODE) + +t_stat sim_set_console (int32 flag, char *cptr); +t_stat sim_set_kmap (int32 flag, char *cptr); +t_stat sim_set_telnet (int32 flag, char *cptr); +t_stat sim_set_notelnet (int32 flag, char *cptr); +t_stat sim_set_logon (int32 flag, char *cptr); +t_stat sim_set_logoff (int32 flag, char *cptr); +t_stat sim_set_debon (int32 flag, char *cptr); +t_stat sim_set_deboff (int32 flag, char *cptr); +t_stat sim_set_pchar (int32 flag, char *cptr); +t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_telnet (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_check_console (int32 sec); +t_stat sim_poll_kbd (void); +t_stat sim_putchar (int32 c); +t_stat sim_putchar_s (int32 c); +t_stat sim_ttinit (void); +t_stat sim_ttrun (void); +t_stat sim_ttcmd (void); +t_stat sim_ttclose (void); +t_stat sim_os_poll_kbd (void); +t_stat sim_os_putchar (int32 out); +int32 sim_tt_inpcvt (int32 c, uint32 mode); +int32 sim_tt_outcvt (int32 c, uint32 mode); + +#endif diff --git a/sim_defs.h b/sim_defs.h new file mode 100644 index 0000000..d8a5f60 --- /dev/null +++ b/sim_defs.h @@ -0,0 +1,538 @@ + +/* sim_defs.h: simulator definitions + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 28-May-08 RMS Added inlining support + 28-Jun-07 RMS Added IA64 VMS support (from Norm Lastovica) + 18-Jun-07 RMS Added UNIT_IDLE flag + 18-Mar-07 RMS Added UNIT_TEXT flag + 07-Mar-07 JDB Added DEBUG_PRJ macro + 18-Oct-06 RMS Added limit check for clock synchronized keyboard waits + 13-Jul-06 RMS Guarantee CBUFSIZE is at least 256 + 07-Jan-06 RMS Added support for breakpoint spaces + Added REG_FIT flag + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 11-Mar-05 RMS Moved 64b data type definitions outside USE_INT64 + 07-Feb-05 RMS Added assertion fail stop + 05-Nov-04 RMS Added support for SHOW opt=val + 20-Oct-04 RMS Converted all base types to typedefs + 21-Sep-04 RMS Added switch to flag stop message printout + 06-Feb-04 RMS Moved device and unit user flags fields (V3.2) + RMS Added REG_VMAD + 29-Dec-03 RMS Added output stall status + 15-Jun-03 RMS Added register flag REG_VMIO + 23-Apr-03 RMS Revised for 32b/64b t_addr + 14-Mar-03 RMS Lengthened default serial output wait + 31-Mar-03 RMS Added u5, u6 fields + 18-Mar-03 RMS Added logical name support + Moved magtape definitions to sim_tape.h + Moved breakpoint definitions from scp.c + 03-Mar-03 RMS Added sim_fsize + 08-Feb-03 RMS Changed sim_os_sleep to void, added match_ext + 05-Jan-03 RMS Added hidden switch definitions, device dyn memory support, + parameters for function pointers, case sensitive SET support + 22-Dec-02 RMS Added break flag + 08-Oct-02 RMS Increased simulator error code space + Added Telnet errors + Added end of medium support + Added help messages to CTAB + Added flag and context fields to DEVICE + Added restore flag masks + Revised 64b definitions + 02-May-02 RMS Removed log status codes + 22-Apr-02 RMS Added magtape record length error + 30-Dec-01 RMS Generalized timer package, added circular arrays + 07-Dec-01 RMS Added breakpoint package + 01-Dec-01 RMS Added read-only unit support, extended SET/SHOW features, + improved error messages + 24-Nov-01 RMS Added unit-based registers + 27-Sep-01 RMS Added queue count prototype + 17-Sep-01 RMS Removed multiple console support + 07-Sep-01 RMS Removed conditional externs on function prototypes + 31-Aug-01 RMS Changed int64 to t_int64 for Windoze + 17-Jul-01 RMS Added additional function prototypes + 27-May-01 RMS Added multiple console support + 15-May-01 RMS Increased string buffer size + 25-Feb-01 RMS Revisions for V2.6 + 15-Oct-00 RMS Editorial revisions for V2.5 + 11-Jul-99 RMS Added unsigned int data types + 14-Apr-99 RMS Converted t_addr to unsigned + 04-Oct-98 RMS Additional definitions for V2.4 + + The interface between the simulator control package (SCP) and the + simulator consists of the following routines and data structures + + sim_name simulator name string + sim_devices[] array of pointers to simulated devices + sim_PC pointer to saved PC register descriptor + sim_interval simulator interval to next event + sim_stop_messages[] array of pointers to stop messages + sim_instr() instruction execution routine + sim_load() binary loader routine + sim_emax maximum number of words in an instruction + + In addition, the simulator must supply routines to print and parse + architecture specific formats + + print_sym print symbolic output + parse_sym parse symbolic input +*/ + +#ifndef _SIM_DEFS_H_ +#define _SIM_DEFS_H_ 0 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +/* Length specific integer declarations */ + +typedef signed char int8; +typedef signed short int16; +typedef signed int int32; +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef int t_stat; /* status */ +typedef int t_bool; /* boolean */ + +/* 64b integers */ + +#if defined (__GNUC__) /* GCC */ +typedef signed long long t_int64; +typedef unsigned long long t_uint64; +#elif defined (_WIN32) /* Windows */ +typedef signed __int64 t_int64; +typedef unsigned __int64 t_uint64; +#elif (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */ +typedef signed __int64 t_int64; +typedef unsigned __int64 t_uint64; +#elif defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ +typedef signed long t_int64; +typedef unsigned long t_uint64; +#else /* default */ +#define t_int64 signed long long +#define t_uint64 unsigned long long +#endif /* end 64b */ + +#if defined (USE_INT64) /* 64b data */ +typedef t_int64 t_svalue; /* signed value */ +typedef t_uint64 t_value; /* value */ +#else /* 32b data */ +typedef int32 t_svalue; +typedef uint32 t_value; +#endif /* end 64b data */ + +#if defined (USE_INT64) && defined (USE_ADDR64) /* 64b address */ +typedef t_uint64 t_addr; +#define T_ADDR_W 64 +#else /* 32b address */ +typedef uint32 t_addr; +#define T_ADDR_W 32 +#endif /* end 64b address */ + +/* Inlining */ + +#if defined (__GNUC__) /* GCC */ +#define SIM_INLINE inline +#define SIM_INLINE_GCC +#elif defined (_MSC_VER) /* Microsoft C Compilers */ +#define SIM_INLINE __inline +#else /* default */ +#define SIM_INLINE +#endif + +/* System independent definitions */ + +#define FLIP_SIZE (1 << 16) /* flip buf size */ +#if !defined (PATH_MAX) /* usually in limits */ +#define PATH_MAX 512 +#endif +#if (PATH_MAX >= 128) +#define CBUFSIZE (128 + PATH_MAX) /* string buf size */ +#else +#define CBUFSIZE 256 +#endif + +/* Breakpoint spaces definitions */ + +#define SIM_BKPT_N_SPC 64 /* max number spaces */ +#define SIM_BKPT_V_SPC 26 /* location in arg */ + +/* Extended switch definitions (bits >= 26) */ + +#define SIM_SW_HIDE (1u << 26) /* enable hiding */ +#define SIM_SW_REST (1u << 27) /* attach/restore */ +#define SIM_SW_REG (1u << 28) /* register value */ +#define SIM_SW_STOP (1u << 29) /* stop message */ + +/* Simulator status codes + + 0 ok + 1 - (SCPE_BASE - 1) simulator specific + SCPE_BASE - n general +*/ + +#define SCPE_OK 0 /* normal return */ +#define SCPE_BASE 64 /* base for messages */ +#define SCPE_NXM (SCPE_BASE + 0) /* nxm */ +#define SCPE_UNATT (SCPE_BASE + 1) /* no file */ +#define SCPE_IOERR (SCPE_BASE + 2) /* I/O error */ +#define SCPE_CSUM (SCPE_BASE + 3) /* loader cksum */ +#define SCPE_FMT (SCPE_BASE + 4) /* loader format */ +#define SCPE_NOATT (SCPE_BASE + 5) /* not attachable */ +#define SCPE_OPENERR (SCPE_BASE + 6) /* open error */ +#define SCPE_MEM (SCPE_BASE + 7) /* alloc error */ +#define SCPE_ARG (SCPE_BASE + 8) /* argument error */ +#define SCPE_STEP (SCPE_BASE + 9) /* step expired */ +#define SCPE_UNK (SCPE_BASE + 10) /* unknown command */ +#define SCPE_RO (SCPE_BASE + 11) /* read only */ +#define SCPE_INCOMP (SCPE_BASE + 12) /* incomplete */ +#define SCPE_STOP (SCPE_BASE + 13) /* sim stopped */ +#define SCPE_EXIT (SCPE_BASE + 14) /* sim exit */ +#define SCPE_TTIERR (SCPE_BASE + 15) /* console tti err */ +#define SCPE_TTOERR (SCPE_BASE + 16) /* console tto err */ +#define SCPE_EOF (SCPE_BASE + 17) /* end of file */ +#define SCPE_REL (SCPE_BASE + 18) /* relocation error */ +#define SCPE_NOPARAM (SCPE_BASE + 19) /* no parameters */ +#define SCPE_ALATT (SCPE_BASE + 20) /* already attached */ +#define SCPE_TIMER (SCPE_BASE + 21) /* hwre timer err */ +#define SCPE_SIGERR (SCPE_BASE + 22) /* signal err */ +#define SCPE_TTYERR (SCPE_BASE + 23) /* tty setup err */ +#define SCPE_SUB (SCPE_BASE + 24) /* subscript err */ +#define SCPE_NOFNC (SCPE_BASE + 25) /* func not imp */ +#define SCPE_UDIS (SCPE_BASE + 26) /* unit disabled */ +#define SCPE_NORO (SCPE_BASE + 27) /* rd only not ok */ +#define SCPE_INVSW (SCPE_BASE + 28) /* invalid switch */ +#define SCPE_MISVAL (SCPE_BASE + 29) /* missing value */ +#define SCPE_2FARG (SCPE_BASE + 30) /* too few arguments */ +#define SCPE_2MARG (SCPE_BASE + 31) /* too many arguments */ +#define SCPE_NXDEV (SCPE_BASE + 32) /* nx device */ +#define SCPE_NXUN (SCPE_BASE + 33) /* nx unit */ +#define SCPE_NXREG (SCPE_BASE + 34) /* nx register */ +#define SCPE_NXPAR (SCPE_BASE + 35) /* nx parameter */ +#define SCPE_NEST (SCPE_BASE + 36) /* nested DO */ +#define SCPE_IERR (SCPE_BASE + 37) /* internal error */ +#define SCPE_MTRLNT (SCPE_BASE + 38) /* tape rec lnt error */ +#define SCPE_LOST (SCPE_BASE + 39) /* Telnet conn lost */ +#define SCPE_TTMO (SCPE_BASE + 40) /* Telnet conn timeout */ +#define SCPE_STALL (SCPE_BASE + 41) /* Telnet conn stall */ +#define SCPE_AFAIL (SCPE_BASE + 42) /* assert failed */ +#define SCPE_KFLAG 0010000 /* tti data flag */ +#define SCPE_BREAK 0020000 /* tti break flag */ + +/* Print value format codes */ + +#define PV_RZRO 0 /* right, zero fill */ +#define PV_RSPC 1 /* right, space fill */ +#define PV_LEFT 2 /* left justify */ + +/* Default timing parameters */ + +#define KBD_POLL_WAIT 5000 /* keyboard poll */ +#define KBD_MAX_WAIT 500000 +#define SERIAL_IN_WAIT 100 /* serial in time */ +#define SERIAL_OUT_WAIT 100 /* serial output */ +#define NOQUEUE_WAIT 10000 /* min check time */ +#define KBD_LIM_WAIT(x) (((x) > KBD_MAX_WAIT)? KBD_MAX_WAIT: (x)) +#define KBD_WAIT(w,s) ((w)? w: KBD_LIM_WAIT (s)) + +/* Convert switch letter to bit mask */ + +#define SWMASK(x) (1u << (((int) (x)) - ((int) 'A'))) + +/* String match */ + +#define MATCH_CMD(ptr,cmd) strncmp ((ptr), (cmd), strlen (ptr)) + +/* Device data structure */ + +struct sim_device { + char *name; /* name */ + struct sim_unit *units; /* units */ + struct sim_reg *registers; /* registers */ + struct sim_mtab *modifiers; /* modifiers */ + uint32 numunits; /* #units */ + uint32 aradix; /* address radix */ + uint32 awidth; /* address width */ + uint32 aincr; /* addr increment */ + uint32 dradix; /* data radix */ + uint32 dwidth; /* data width */ + t_stat (*examine)(t_value *v, t_addr a, struct sim_unit *up, + int32 sw); /* examine routine */ + t_stat (*deposit)(t_value v, t_addr a, struct sim_unit *up, + int32 sw); /* deposit routine */ + t_stat (*reset)(struct sim_device *dp);/* reset routine */ + t_stat (*boot)(int32 u, struct sim_device *dp); + /* boot routine */ + t_stat (*attach)(struct sim_unit *up, char *cp); + /* attach routine */ + t_stat (*detach)(struct sim_unit *up); /* detach routine */ + void *ctxt; /* context */ + uint32 flags; /* flags */ + uint32 dctrl; /* debug control */ + struct sim_debtab *debflags; /* debug flags */ + t_stat (*msize)(struct sim_unit *up, int32 v, char *cp, void *dp); + /* mem size routine */ + char *lname; /* logical name */ + }; + +/* Device flags */ + +#define DEV_V_DIS 0 /* dev disabled */ +#define DEV_V_DISABLE 1 /* dev disable-able */ +#define DEV_V_DYNM 2 /* mem size dynamic */ +#define DEV_V_NET 3 /* network attach */ +#define DEV_V_DEBUG 4 /* debug capability */ +#define DEV_V_RAW 5 /* raw supported */ +#define DEV_V_RAWONLY 6 /* only raw supported */ +#define DEV_V_UF_31 12 /* user flags, V3.1 */ +#define DEV_V_UF 16 /* user flags */ +#define DEV_V_RSV 31 /* reserved */ + +#define DEV_DIS (1 << DEV_V_DIS) +#define DEV_DISABLE (1 << DEV_V_DISABLE) +#define DEV_DYNM (1 << DEV_V_DYNM) +#define DEV_NET (1 << DEV_V_NET) +#define DEV_DEBUG (1 << DEV_V_DEBUG) +#define DEV_RAW (1 << DEV_V_RAW) +#define DEV_RAWONLY (1 << DEV_V_RAWONLY) + +#define DEV_UFMASK_31 (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF_31) - 1)) +#define DEV_UFMASK (((1u << DEV_V_RSV) - 1) & ~((1u << DEV_V_UF) - 1)) +#define DEV_RFLAGS (DEV_UFMASK|DEV_DIS) /* restored flags */ + +/* Unit data structure + + Parts of the unit structure are device specific, that is, they are + not referenced by the simulator control package and can be freely + used by device simulators. Fields starting with 'buf', and flags + starting with 'UF', are device specific. The definitions given here + are for a typical sequential device. +*/ + +struct sim_unit { + struct sim_unit *next; /* next active */ + t_stat (*action)(struct sim_unit *up); /* action routine */ + char *filename; /* open file name */ + FILE *fileref; /* file reference */ + void *filebuf; /* memory buffer */ + uint32 hwmark; /* high water mark */ + int32 time; /* time out */ + uint32 flags; /* flags */ + t_addr capac; /* capacity */ + t_addr pos; /* file position */ + int32 buf; /* buffer */ + int32 wait; /* wait */ + int32 u3; /* device specific */ + int32 u4; /* device specific */ + int32 u5; /* device specific */ + int32 u6; /* device specific */ + }; + +/* Unit flags */ + +#define UNIT_V_UF_31 12 /* dev spec, V3.1 */ +#define UNIT_V_UF 16 /* device specific */ +#define UNIT_V_RSV 31 /* reserved!! */ + +#define UNIT_ATTABLE 000001 /* attachable */ +#define UNIT_RO 000002 /* read only */ +#define UNIT_FIX 000004 /* fixed capacity */ +#define UNIT_SEQ 000010 /* sequential */ +#define UNIT_ATT 000020 /* attached */ +#define UNIT_BINK 000040 /* K = power of 2 */ +#define UNIT_BUFABLE 000100 /* bufferable */ +#define UNIT_MUSTBUF 000200 /* must buffer */ +#define UNIT_BUF 000400 /* buffered */ +#define UNIT_ROABLE 001000 /* read only ok */ +#define UNIT_DISABLE 002000 /* disable-able */ +#define UNIT_DIS 004000 /* disabled */ +#define UNIT_RAW 010000 /* raw mode */ +#define UNIT_TEXT 020000 /* text mode */ +#define UNIT_IDLE 040000 /* idle eligible */ + +#define UNIT_UFMASK_31 (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF_31) - 1)) +#define UNIT_UFMASK (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1)) +#define UNIT_RFLAGS (UNIT_UFMASK|UNIT_DIS) /* restored flags */ + +/* Register data structure */ + +struct sim_reg { + char *name; /* name */ + void *loc; /* location */ + uint32 radix; /* radix */ + uint32 width; /* width */ + uint32 offset; /* starting bit */ + uint32 depth; /* save depth */ + uint32 flags; /* flags */ + uint32 qptr; /* circ q ptr */ + }; + +#define REG_FMT 00003 /* see PV_x */ +#define REG_RO 00004 /* read only */ +#define REG_HIDDEN 00010 /* hidden */ +#define REG_NZ 00020 /* must be non-zero */ +#define REG_UNIT 00040 /* in unit struct */ +#define REG_CIRC 00100 /* circular array */ +#define REG_VMIO 00200 /* use VM data print/parse */ +#define REG_VMAD 00400 /* use VM addr print/parse */ +#define REG_FIT 01000 /* fit access to size */ +#define REG_HRO (REG_RO | REG_HIDDEN) /* hidden, read only */ + +/* Command tables, base and alternate formats */ + +struct sim_ctab { + char *name; /* name */ + t_stat (*action)(int32 flag, char *cptr); + /* action routine */ + int32 arg; /* argument */ + char *help; /* help string */ + }; + +struct sim_c1tab { + char *name; /* name */ + t_stat (*action)(struct sim_device *dptr, struct sim_unit *uptr, + int32 flag, char *cptr); /* action routine */ + int32 arg; /* argument */ + char *help; /* help string */ + }; + +struct sim_shtab { + char *name; /* name */ + t_stat (*action)(FILE *st, struct sim_device *dptr, + struct sim_unit *uptr, int32 flag, char *cptr); + int32 arg; /* argument */ + char *help; /* help string */ + }; + +/* Modifier table - only extended entries have disp, reg, or flags */ + +struct sim_mtab { + uint32 mask; /* mask */ + uint32 match; /* match */ + char *pstring; /* print string */ + char *mstring; /* match string */ + t_stat (*valid)(struct sim_unit *up, int32 v, char *cp, void *dp); + /* validation routine */ + t_stat (*disp)(FILE *st, struct sim_unit *up, int32 v, void *dp); + /* display routine */ + void *desc; /* value descriptor */ + /* REG * if MTAB_VAL */ + /* int * if not */ + }; + +#define MTAB_XTD (1u << UNIT_V_RSV) /* ext entry flag */ +#define MTAB_VDV 001 /* valid for dev */ +#define MTAB_VUN 002 /* valid for unit */ +#define MTAB_VAL 004 /* takes a value */ +#define MTAB_NMO 010 /* only if named */ +#define MTAB_NC 020 /* no UC conversion */ +#define MTAB_SHP 040 /* show takes parameter */ + +/* Search table */ + +struct sim_schtab { + int32 logic; /* logical operator */ + int32 boolop; /* boolean operator */ + t_value mask; /* mask for logical */ + t_value comp; /* comparison for boolean */ + }; + +/* Breakpoint table */ + +struct sim_brktab { + t_addr addr; /* address */ + int32 typ; /* mask of types */ + int32 cnt; /* proceed count */ + char *act; /* action string */ + }; + +/* Debug table */ + +struct sim_debtab { + char *name; /* control name */ + uint32 mask; /* control bit */ + }; + +#define DEBUG_PRS(d) (sim_deb && d.dctrl) +#define DEBUG_PRD(d) (sim_deb && d->dctrl) +#define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m))) +#define DEBUG_PRJ(d,m) (sim_deb && (d->dctrl & (m))) + +/* The following macros define structure contents */ + +#define UDATA(act,fl,cap) NULL,act,NULL,NULL,NULL,0,0,(fl),(cap),0,0 + +#if defined (__STDC__) || defined (_WIN32) +#define ORDATA(nm,loc,wd) #nm, &(loc), 8, (wd), 0, 1 +#define DRDATA(nm,loc,wd) #nm, &(loc), 10, (wd), 0, 1 +#define HRDATA(nm,loc,wd) #nm, &(loc), 16, (wd), 0, 1 +#define FLDATA(nm,loc,pos) #nm, &(loc), 2, 1, (pos), 1 +#define GRDATA(nm,loc,rdx,wd,pos) #nm, &(loc), (rdx), (wd), (pos), 1 +#define BRDATA(nm,loc,rdx,wd,dep) #nm, (loc), (rdx), (wd), 0, (dep) +#define URDATA(nm,loc,rdx,wd,off,dep,fl) \ + #nm, &(loc), (rdx), (wd), (off), (dep), ((fl) | REG_UNIT) +#else +#define ORDATA(nm,loc,wd) "nm", &(loc), 8, (wd), 0, 1 +#define DRDATA(nm,loc,wd) "nm", &(loc), 10, (wd), 0, 1 +#define HRDATA(nm,loc,wd) "nm", &(loc), 16, (wd), 0, 1 +#define FLDATA(nm,loc,pos) "nm", &(loc), 2, 1, (pos), 1 +#define GRDATA(nm,loc,rdx,wd,pos) "nm", &(loc), (rdx), (wd), (pos), 1 +#define BRDATA(nm,loc,rdx,wd,dep) "nm", (loc), (rdx), (wd), 0, (dep) +#define URDATA(nm,loc,rdx,wd,off,dep,fl) \ + "nm", &(loc), (rdx), (wd), (off), (dep), ((fl) | REG_UNIT) +#endif + +/* Typedefs for principal structures */ + +typedef struct sim_device DEVICE; +typedef struct sim_unit UNIT; +typedef struct sim_reg REG; +typedef struct sim_ctab CTAB; +typedef struct sim_c1tab C1TAB; +typedef struct sim_shtab SHTAB; +typedef struct sim_mtab MTAB; +typedef struct sim_schtab SCHTAB; +typedef struct sim_brktab BRKTAB; +typedef struct sim_debtab DEBTAB; + +/* Function prototypes */ + +#include "scp.h" +#include "sim_console.h" +#include "sim_timer.h" +#include "sim_fio.h" + +#endif diff --git a/sim_ether.c b/sim_ether.c new file mode 100644 index 0000000..09bd134 --- /dev/null +++ b/sim_ether.c @@ -0,0 +1,1470 @@ +/* sim_ether.c: OS-dependent network routines + ------------------------------------------------------------------------------ + Copyright (c) 2002-2007, David T. Hittner + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + This ethernet simulation is based on the PCAP and WinPcap packages. + + PCAP/WinPcap was chosen as the basis for network code since it is the most + "universal" of the various network packages available. Using this style has + allowed rapid network development for the major SIMH platforms. Developing + a network package specifically for SIMH was rejected due to the time required; + the advantage would be a more easily compiled and integrated code set. + + There are various problems associated with use of ethernet networking, which + would be true regardless of the network package used, since there are no + universally accepted networking methods. The most serious of these is getting + the proper networking package loaded onto the system, since most environments + do not come with the network interface packages loaded. + + The second most serious network issue relates to security. The network + simulation needs to simulate operating system level functionality (packet + driving). However, the host network programming interfaces tend to operate at + the user level of functionality, so getting to the full functionality of + the network interface usually requires that the person executing the + network code be a privileged user of the host system. See the PCAP/WinPcap + documentation for the appropriate host platform if unprivileged use of + networking is needed - there may be known workarounds. + + Define one of the two macros below to enable networking: + USE_NETWORK - Create statically linked network code + USE_SHARED - Create dynamically linked network code (_WIN32 only) + + ------------------------------------------------------------------------------ + + Supported/Tested Platforms: + + Windows(NT,2K,XP,2K3) WinPcap V3.0+ + Linux libpcap at least 0.9 + OpenBSD,FreeBSD,NetBSD libpcap at least 0.9 + MAC OS/X libpcap at least 0.9 + Solaris Sparc libpcap at least 0.9 + Solaris Intel libpcap at least 0.9 + AIX ?? + HP/UX ?? + Compaq Tru64 Unix ?? + VMS Alpha/Itanium VMS only, needs VMS libpcap + + WinPcap is available from: + http://winpcap.polito.it/ + libpcap for VMS is available from: + http://simh.trailing-edge.com/sources/vms-pcap.zip + libpcap for other Unix platforms is available at: + Current Version: http://www.tcpdump.org/daily/libpcap-current.tar.gz + Released Version: http://www.tcpdump.org/release/ + Note: You can only use the released version if it is at least + version 0.9 + + + We've gotten the tarball, unpacked, built and installed it with: + gzip -dc libpcap-current.tar.gz | tar xvf - + cd libpcap-directory-name + ./configure + make + make install + Note: The "make install" step generally will have to be done as root. + This will install libpcap in /usr/local/lib and /usr/local/include + It is then important to make sure that you get the just installed + libpcap components referenced during your build. This is generally + achieved by invoking gcc with: + -isystem /usr/local/include -L /usr/local/lib + + + Note: Building for the platforms indicated above, with the indicated libpcap, + should automatically leverage the appropriate mechanisms contained here. + Things are structured so that it is likely to work for any other as yet + untested platform. If it works for you, please let the author know so we + can update the table above. If it doesn't work, then the following #define + variables can influence the operation on an untested platform. + + USE_BPF - Determines if this code leverages a libpcap/WinPcap + provided bpf packet filtering facility. All tested + environments have bpf facilities that work the way we + need them to. However a new one might not. undefine + this variable to let this code do its own filtering. + USE_SETNONBLOCK - Specifies whether the libpcap environment's non-blocking + semantics are to be leveraged. This helps to manage the + varying behaviours of the kernel packet facilities + leveraged by libpcap. + USE_READER_THREAD - Specifies that packet reading should be done in the + context of a separate thread. The Posix threading + APIs are used. This option is less efficient than the + default non-threaded approach, but it exists since some + platforms don't want to work with nonblocking libpcap + semantics. OpenBSD and NetBSD either don't have pthread + APIs available, or they are too buggy to be useful. + Using the threaded approach may require special compile + and/or link time switches (i.e. -lpthread or -pthread, + etc.) Consult the documentation for your platform as + needed. + MUST_DO_SELECT - Specifies that when USE_READER_THREAD is active, that + select() should be used to determin when available + packets are ready for reading. Otherwise, we depend + on the libpcap/kernel packet timeout specified on + pcap_open_live. If USE_READER_THREAD is not set, then + MUST_DO_SELECT is irrelevant + + NEED_PCAP_SENDPACKET + - Specifies that you are using an older version of libpcap + which doesn't provide a pcap_sendpacket API. + + NOTE: Changing these defines is done in either sim_ether.h OR on the global + compiler command line which builds all of the modules included in a + simulator. + + ------------------------------------------------------------------------------ + + Modification history: + + 17-May-07 DTH Fixed non-ethernet device removal loop (from Naoki Hamada) + 15-May-07 DTH Added dynamic loading of wpcap.dll; + Corrected exceed max index bug in ethX lookup + 04-May-07 DTH Corrected failure to look up ethernet device names in + the registry on Windows XP x64 + 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) + 02-Jun-06 JDB Fixed compiler warning for incompatible sscanf parameter + 15-Dec-05 DTH Patched eth_host_devices [remove non-ethernet devices] + (from Mark Pizzolato and Galen Tackett, 08-Jun-05) + Patched eth_open [tun fix](from Antal Ritter, 06-Oct-05) + 30-Nov-05 DTH Added option to regenerate CRC on received packets; some + ethernet devices need to pass it on to the simulation, and by + the time libpcap/winpcap gets the packet, the host OS network + layer has already stripped CRC out of the packet + 01-Dec-04 DTH Added Windows user-defined adapter names (from Timothe Litt) + 25-Mar-04 MP Revised comments and minor #defines to deal with updated + libpcap which now provides pcap_sendpacket on all platforms. + 04-Feb-04 MP Returned success/fail status from eth_write to support + determining if the current libpcap connection can successfully + write packets. + Added threaded approach to reading packets since + this works better on some platforms (solaris intel) than the + inconsistently implemented non-blocking read approach. + 04-Feb-04 DTH Converted ETH_DEBUG to sim_debug + 13-Jan-04 MP tested and fixed on OpenBSD, NetBS and FreeBSD. + 09-Jan-04 MP removed the BIOCSHDRCMPLT ioctl() for OS/X + 05-Jan-04 DTH Added eth_mac_scan + 30-Dec-03 DTH Cleaned up queue routines, added no network support message + 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq + 15-Dec-03 MP polished generic libpcap support. + 05-Dec-03 DTH Genericized eth_devices() and #ifdefs + 03-Dec-03 MP Added Solaris support + 02-Dec-03 DTH Corrected decnet fix to use reflection counting + 01-Dec-03 DTH Added BPF source filtering and reflection counting + 28-Nov-03 DTH Rewrote eth_devices using universal pcap_findalldevs() + 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code + 19-Nov-03 MP Fixed BPF functionality on Linux/BSD. + 17-Nov-03 DTH Added xBSD simplification + 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code + 13-Nov-03 DTH Merged in __FreeBSD__ support + 21-Oct-03 MP Added enriched packet dumping for debugging + 20-Oct-03 MP Added support for multiple ethernet devices on VMS + 20-Sep-03 Ankan Add VMS support (Alpha only) + 29-Sep-03 MP Changed separator character in eth_fmt_mac to be ":" to + format ethernet addresses the way the BPF compile engine + wants to see them. + Added BPF support to filter packets + Added missing printf in eth_close + 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. + 06-Jun-03 MP Fixed formatting of Ethernet Protocol Type in eth_packet_trace + 30-May-03 DTH Changed WIN32 to _WIN32 for consistency + 07-Mar-03 MP Fixed Linux implementation of PacketGetAdapterNames to also + work on Red Hat 6.2-sparc and Debian 3.0r1-sparc. + 03-Mar-03 MP Changed logging to be consistent on stdout and sim_log + 01-Feb-03 MP Changed type of local variables in eth_packet_trace to + conform to the interface needs of eth_mac_fmt wich produces + char data instead of unsigned char data. Suggested by the + DECC compiler. + 15-Jan-03 DTH Corrected PacketGetAdapterNames parameter2 datatype + 26-Dec-02 DTH Merged Mark Pizzolato's enhancements with main source + Added networking documentation + Changed _DEBUG to ETH_DEBUG + 20-Dec-02 MP Added display of packet CRC to the eth_packet_trace. + This helps distinguish packets with identical lengths + and protocols. + 05-Dec-02 MP With the goal of draining the input buffer more rapidly + changed eth_read to call pcap_dispatch repeatedly until + either a timeout returns nothing or a packet allowed by + the filter is seen. This more closely reflects how the + pcap layer will work when the filtering is actually done + by a bpf filter. + 31-Oct-02 DTH Added USE_NETWORK conditional + Reworked not attached test + Added OpenBSD support (from Federico Schwindt) + Added ethX detection simplification (from Megan Gentry) + Removed sections of temporary code + Added parameter validation + 23-Oct-02 DTH Beta 5 released + 22-Oct-02 DTH Added all_multicast and promiscuous support + Fixed not attached behavior + 21-Oct-02 DTH Added NetBSD support (from Jason Thorpe) + Patched buffer size to make sure entire packet is read in + Made 'ethX' check characters passed as well as length + Corrected copyright again + 16-Oct-02 DTH Beta 4 released + Corrected copyright + 09-Oct-02 DTH Beta 3 released + Added pdp11 write acceleration (from Patrick Caulfield) + 08-Oct-02 DTH Beta 2 released + Integrated with 2.10-0p4 + Added variable vector and copyrights + 04-Oct-02 DTH Added linux support (from Patrick Caulfield) + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 24-Sep-02 DTH Finished eth_devices, eth_getname + 18-Sep-02 DTH Callbacks implemented + 13-Sep-02 DTH Basic packet read/write written + 20-Aug-02 DTH Created Sim_Ether for O/S independant ethernet implementation + + ------------------------------------------------------------------------------ +*/ + +#include +#include "sim_ether.h" +#include "sim_sock.h" + +extern FILE *sim_log; + + +/*============================================================================*/ +/* OS-independant ethernet routines */ +/*============================================================================*/ + +t_stat eth_mac_scan (ETH_MAC* mac, char* strmac) +{ + int i, j; + short unsigned int num; + char cptr[18]; + int len = strlen(strmac); + const ETH_MAC zeros = {0,0,0,0,0,0}; + const ETH_MAC ones = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + ETH_MAC newmac; + + /* format of string must be 6 double-digit hex bytes with valid separators + ideally, this mac scanner could allow more flexible formatting later */ + if (len != 17) return SCPE_ARG; + + /* copy string to local storage for mangling */ + strcpy(cptr, strmac); + + /* make sure byte separators are OK */ + for (i=2; i> 8) ^ crcTable[ (crc ^ (*buf++)) & 0xFF ]; + return(crc ^ mask); +} + +void eth_add_crc32(ETH_PACK* packet) +{ + if (packet->len <= ETH_MAX_PACKET) { + uint32 crc = eth_crc32(0, packet->msg, packet->len); /* calculate CRC */ + uint32 ncrc = htonl(crc); /* CRC in network order */ + int size = sizeof(ncrc); /* size of crc field */ + memcpy(&packet->msg[packet->len], &ncrc, size); /* append crc to packet */ + packet->crc_len = packet->len + size; /* set packet crc length */ + } else { + packet->crc_len = 0; /* appending crc would destroy packet */ + } +} + +void eth_setcrc(ETH_DEV* dev, int need_crc) +{ + dev->need_crc = need_crc; +} + +void eth_packet_trace_ex(ETH_DEV* dev, const uint8 *msg, int len, char* txt, int dmp) +{ + if (dev->dptr->dctrl & dev->dbit) { + char src[20]; + char dst[20]; + unsigned short* proto = (unsigned short*) &msg[12]; + uint32 crc = eth_crc32(0, msg, len); + eth_mac_fmt((ETH_MAC*)&msg[0], dst); + eth_mac_fmt((ETH_MAC*)&msg[6], src); + sim_debug(dev->dbit, dev->dptr, "%s dst: %s src: %s proto: 0x%04X len: %d crc: %X\n", + txt, dst, src, ntohs(*proto), len, crc); + if (dmp) { + int i, same, group, sidx, oidx; + char outbuf[80], strbuf[18]; + static char hex[] = "0123456789ABCDEF"; + for (i=same=0; i 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) { + ++same; + continue; + } + if (same > 0) { + sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), i-1); + same = 0; + } + group = (((len - i) > 16) ? 16 : (len - i)); + for (sidx=oidx=0; sidx>4)&0xf]; + outbuf[oidx++] = hex[msg[i+sidx]&0xf]; + if (isprint(msg[i+sidx])) + strbuf[sidx] = msg[i+sidx]; + else + strbuf[sidx] = '.'; + } + outbuf[oidx] = '\0'; + strbuf[sidx] = '\0'; + sim_debug(dev->dbit, dev->dptr, "%04X%-48s %s\r\n", i, outbuf, strbuf); + } + if (same > 0) + sim_debug(dev->dbit, dev->dptr, "%04X thru %04X same as above\r\n", i-(16*same), len-1); + } + } +} + +void eth_packet_trace(ETH_DEV* dev, const uint8 *msg, int len, char* txt) +{ + eth_packet_trace_ex(dev, msg, len, txt, 1/*len > ETH_MAX_PACKET*/); +} + +char* eth_getname(int number, char* name) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int count = eth_devices(ETH_MAX_DEVICE, list); + + if (count <= number) return 0; + strcpy(name, list[number].name); + return name; +} + +char* eth_getname_bydesc(char* desc, char* name) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int count = eth_devices(ETH_MAX_DEVICE, list); + int i; + int j=strlen(desc); + + for (i=0; i s2) + return 1; + if (s1 == 0) return 0; + } + return 0; +} + +char* eth_getname_byname(char* name, char* temp) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int count = eth_devices(ETH_MAX_DEVICE, list); + int i, n, found; + + found = 0; + n = strlen(name); + for (i=0; ireflections = -1; /* not established yet */ +} + +t_stat eth_show (FILE* st, UNIT* uptr, int32 val, void* desc) +{ + ETH_LIST list[ETH_MAX_DEVICE]; + int number = eth_devices(ETH_MAX_DEVICE, list); + + fprintf(st, "ETH devices:\n"); + if (number == -1) + fprintf(st, " network support not available in simulator\n"); + else + if (number == 0) + fprintf(st, " no network devices are available\n"); + else { + int i, min, len; + for (i=0, min=0; i min) min = len; + for (i=0; iitem) { + size_t size = sizeof(struct eth_item) * max; + que->max = max; + que->item = (struct eth_item *) malloc(size); + if (que->item) { + /* init dynamic memory */ + memset(que->item, 0, size); + } else { + /* failed to allocate memory */ + char* msg = "EthQ: failed to allocate dynamic queue[%d]\r\n"; + printf(msg, max); + if (sim_log) fprintf(sim_log, msg, max); + return SCPE_MEM; + }; + }; + return SCPE_OK; +} + +void ethq_clear(ETH_QUE* que) +{ + /* clear packet array */ + memset(que->item, 0, sizeof(struct eth_item) * que->max); + /* clear rest of structure */ + que->count = que->head = que->tail = que->loss = que->high = 0; +} + +void ethq_remove(ETH_QUE* que) +{ + struct eth_item* item = &que->item[que->head]; + + if (que->count) { + memset(item, 0, sizeof(struct eth_item)); + if (++que->head == que->max) + que->head = 0; + que->count--; + } +} + +void ethq_insert(ETH_QUE* que, int32 type, ETH_PACK* pack, int32 status) +{ + struct eth_item* item; + + /* if queue empty, set pointers to beginning */ + if (!que->count) { + que->head = 0; + que->tail = -1; + } + + /* find new tail of the circular queue */ + if (++que->tail == que->max) + que->tail = 0; + if (++que->count > que->max) { + que->count = que->max; + /* lose oldest packet */ + if (++que->head == que->max) + que->head = 0; + que->loss++; + } + if (que->count > que->high) + que->high = que->count; + + /* set information in (new) tail item */ + item = &que->item[que->tail]; + item->type = type; + item->packet.len = pack->len; + item->packet.used = 0; + item->packet.crc_len = pack->crc_len; + memcpy(item->packet.msg, pack->msg, ((pack->len > pack->crc_len) ? pack->len : pack->crc_len)); + item->packet.status = status; +} + +/*============================================================================*/ +/* Non-implemented versions */ +/*============================================================================*/ + +#if !defined (USE_NETWORK) && !defined(USE_SHARED) +t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) + {return SCPE_NOFNC;} +t_stat eth_close (ETH_DEV* dev) + {return SCPE_NOFNC;} +t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) + {return SCPE_NOFNC;} +t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) + {return SCPE_NOFNC;} +t_stat eth_filter (ETH_DEV* dev, int addr_count, ETH_MAC* addresses, + ETH_BOOL all_multicast, ETH_BOOL promiscuous) + {return SCPE_NOFNC;} +int eth_devices (int max, ETH_LIST* dev) + {return -1;} +#else /* endif unimplemented */ + +/*============================================================================*/ +/* WIN32, Linux, and xBSD routines use WinPcap and libpcap packages */ +/* OpenVMS Alpha uses a WinPcap port and an associated execlet */ +/*============================================================================*/ + +#if defined (xBSD) && !defined(__APPLE__) +#include +#include +#endif /* xBSD */ + +#include +#include + +/* Allows windows to look up user-defined adapter names */ +#if defined(_WIN32) +#include +#endif + +#if defined(_WIN32) && defined(USE_SHARED) +/* Dynamic DLL loading technique and modified source comes from + Etherial/WireShark capture_pcap.c */ + +/* Dynamic DLL load variables */ +static HINSTANCE hDll = 0; /* handle to DLL */ +static int dll_loaded = 0; /* 0=not loaded, 1=loaded, 2=DLL load failed, 3=Func load failed */ +static char* no_wpcap = "wpcap load failure"; + +/* define pointers to pcap functions needed */ +static void (*p_pcap_close) (pcap_t *); +static int (*p_pcap_compile) (pcap_t *, struct bpf_program *, char *, int, bpf_u_int32); +static int (*p_pcap_datalink) (pcap_t *); +static int (*p_pcap_dispatch) (pcap_t *, int, pcap_handler, u_char *); +static int (*p_pcap_findalldevs) (pcap_if_t **, char *); +static void (*p_pcap_freealldevs) (pcap_if_t *); +static void (*p_pcap_freecode) (struct bpf_program *); +static char* (*p_pcap_geterr) (pcap_t *); +static int (*p_pcap_lookupnet) (const char *, bpf_u_int32 *, bpf_u_int32 *, char *); +static pcap_t* (*p_pcap_open_live) (const char *, int, int, int, char *); +static int (*p_pcap_sendpacket) (pcap_t* handle, const u_char* msg, int len); +static int (*p_pcap_setfilter) (pcap_t *, struct bpf_program *); +static char* (*p_pcap_lib_version) (void); + +/* load function pointer from DLL */ +void load_function(char* function, void** func_ptr) { + *func_ptr = GetProcAddress(hDll, function); + if (*func_ptr == 0) { + char* msg = "Eth: Failed to find function '%s' in wpcap.dll\r\n"; + printf (msg, function); + if (sim_log) fprintf (sim_log, msg, function); + dll_loaded = 3; + } +} + +/* load wpcap.dll as required */ +int load_wpcap(void) { + switch(dll_loaded) { + case 0: /* not loaded */ + /* attempt to load DLL */ + hDll = LoadLibrary(TEXT("wpcap.dll")); + if (hDll == 0) { + /* failed to load DLL */ + char* msg = "Eth: Failed to load wpcap.dll\r\n"; + char* msg2 = "Eth: You must install WinPcap 4.x to use networking\r\n"; + printf (msg); + printf (msg2); + if (sim_log) { + fprintf (sim_log, msg); + fprintf (sim_log, msg2); + } + dll_loaded = 2; + break; + } else { + /* DLL loaded OK */ + dll_loaded = 1; + } + + /* load required functions; sets dll_load=3 on error */ + load_function("pcap_close", (void**) &p_pcap_close); + load_function("pcap_compile", (void**) &p_pcap_compile); + load_function("pcap_datalink", (void**) &p_pcap_datalink); + load_function("pcap_dispatch", (void**) &p_pcap_dispatch); + load_function("pcap_findalldevs", (void**) &p_pcap_findalldevs); + load_function("pcap_freealldevs", (void**) &p_pcap_freealldevs); + load_function("pcap_freecode", (void**) &p_pcap_freecode); + load_function("pcap_geterr", (void**) &p_pcap_geterr); + load_function("pcap_lookupnet", (void**) &p_pcap_lookupnet); + load_function("pcap_open_live", (void**) &p_pcap_open_live); + load_function("pcap_sendpacket", (void**) &p_pcap_sendpacket); + load_function("pcap_setfilter", (void**) &p_pcap_setfilter); + load_function("pcap_lib_version", (void**) &p_pcap_lib_version); + + if (dll_loaded == 1) { + /* log successful load */ + char* version = p_pcap_lib_version(); + printf("%s\n", version); + if (sim_log) + fprintf(sim_log, "%s\n", version); + } + break; + default: /* loaded or failed */ + break; + } + return (dll_loaded == 1) ? 1 : 0; +} + +/* define functions with dynamic revectoring */ +void pcap_close(pcap_t* a) { + if (load_wpcap() != 0) { + p_pcap_close(a); + } +} + +int pcap_compile(pcap_t* a, struct bpf_program* b, char* c, int d, bpf_u_int32 e) { + if (load_wpcap() != 0) { + return p_pcap_compile(a, b, c, d, e); + } else { + return 0; + } +} + +int pcap_datalink(pcap_t* a) { + if (load_wpcap() != 0) { + return p_pcap_datalink(a); + } else { + return 0; + } +} + +int pcap_dispatch(pcap_t* a, int b, pcap_handler c, u_char* d) { + if (load_wpcap() != 0) { + return p_pcap_dispatch(a, b, c, d); + } else { + return 0; + } +} + +int pcap_findalldevs(pcap_if_t** a, char* b) { + if (load_wpcap() != 0) { + return p_pcap_findalldevs(a, b); + } else { + *a = 0; + strcpy(b, no_wpcap); + return -1; + } +} + +void pcap_freealldevs(pcap_if_t* a) { + if (load_wpcap() != 0) { + p_pcap_freealldevs(a); + } +} + +void pcap_freecode(struct bpf_program* a) { + if (load_wpcap() != 0) { + p_pcap_freecode(a); + } +} + +char* pcap_geterr(pcap_t* a) { + if (load_wpcap() != 0) { + return p_pcap_geterr(a); + } else { + return (char*) 0; + } +} + +int pcap_lookupnet(const char* a, bpf_u_int32* b, bpf_u_int32* c, char* d) { + if (load_wpcap() != 0) { + return p_pcap_lookupnet(a, b, c, d); + } else { + return 0; + } +} + +pcap_t* pcap_open_live(const char* a, int b, int c, int d, char* e) { + if (load_wpcap() != 0) { + return p_pcap_open_live(a, b, c, d, e); + } else { + return (pcap_t*) 0; + } +} + +int pcap_sendpacket(pcap_t* a, const u_char* b, int c) { + if (load_wpcap() != 0) { + return p_pcap_sendpacket(a, b, c); + } else { + return 0; + } +} + +int pcap_setfilter(pcap_t* a, struct bpf_program* b) { + if (load_wpcap() != 0) { + return p_pcap_setfilter(a, b); + } else { + return 0; + } +} +#endif + +/* Some platforms have always had pcap_sendpacket */ +#if defined(_WIN32) || defined(VMS) +#define HAS_PCAP_SENDPACKET 1 +#else +/* The latest libpcap and WinPcap all have pcap_sendpacket */ +#if !defined (NEED_PCAP_SENDPACKET) +#define HAS_PCAP_SENDPACKET 1 +#endif +#endif + +#if !defined (HAS_PCAP_SENDPACKET) +/* libpcap has no function to write a packet, so we need to implement + pcap_sendpacket() for compatibility with the WinPcap base code. + Return value: 0=Success, -1=Failure */ +int pcap_sendpacket(pcap_t* handle, const u_char* msg, int len) +{ +#if defined (__linux) + return (send(pcap_fileno(handle), msg, len, 0) == len)? 0 : -1; +#else + return (write(pcap_fileno(handle), msg, len) == len)? 0 : -1; +#endif /* linux */ +} +#endif /* !HAS_PCAP_SENDPACKET */ + +#if defined (USE_READER_THREAD) +#include + +void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data); + +static void * +_eth_reader(void *arg) +{ +ETH_DEV* volatile dev = (ETH_DEV*)arg; +int status; +struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 200*1000; + + sim_debug(dev->dbit, dev->dptr, "Reader Thread Starting\n"); + + while (dev->handle) { +#if defined (MUST_DO_SELECT) + int sel_ret; + + fd_set setl; + FD_ZERO(&setl); + FD_SET(pcap_get_selectable_fd((pcap_t *)dev->handle), &setl); + sel_ret = select(1+pcap_get_selectable_fd((pcap_t *)dev->handle), &setl, NULL, NULL, &timeout); + if (sel_ret < 0 && errno != EINTR) break; + if (sel_ret > 0) { + /* dispatch read request queue available packets */ + status = pcap_dispatch((pcap_t*)dev->handle, -1, ð_callback, (u_char*)dev); + } +#else + /* dispatch read request queue available packets */ + status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); +#endif + } + + sim_debug(dev->dbit, dev->dptr, "Reader Thread Exiting\n"); + return NULL; +} +#endif + +t_stat eth_open(ETH_DEV* dev, char* name, DEVICE* dptr, uint32 dbit) +{ + const int bufsz = (BUFSIZ < ETH_MAX_PACKET) ? ETH_MAX_PACKET : BUFSIZ; + char errbuf[PCAP_ERRBUF_SIZE]; + char temp[1024]; + char* savname = name; + int num; + char* msg; + + /* initialize device */ + eth_zero(dev); + + /* translate name of type "ethX" to real device name */ + if ((strlen(name) == 4) + && (tolower(name[0]) == 'e') + && (tolower(name[1]) == 't') + && (tolower(name[2]) == 'h') + && isdigit(name[3]) + ) { + num = atoi(&name[3]); + savname = eth_getname(num, temp); + if (savname == 0) /* didn't translate */ + return SCPE_OPENERR; + } else { + /* are they trying to use device description? */ + savname = eth_getname_bydesc(name, temp); + if (savname == 0) { /* didn't translate */ + /* probably is not ethX and has no description */ + savname = eth_getname_byname(name, temp); + if (savname == 0) /* didn't translate */ + return SCPE_OPENERR; + } + } + + /* attempt to connect device */ + memset(errbuf, 0, sizeof(errbuf)); + dev->handle = (void*) pcap_open_live(savname, bufsz, ETH_PROMISC, PCAP_READ_TIMEOUT, errbuf); + if (!dev->handle) { /* can't open device */ + msg = "Eth: pcap_open_live error - %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + return SCPE_OPENERR; + } else { + msg = "Eth: opened %s\r\n"; + printf (msg, savname); + if (sim_log) fprintf (sim_log, msg, savname); + } + + /* save name of device */ + dev->name = malloc(strlen(savname)+1); + strcpy(dev->name, savname); + + /* save debugging information */ + dev->dptr = dptr; + dev->dbit = dbit; + +#if !defined(HAS_PCAP_SENDPACKET) && defined (xBSD) && !defined (__APPLE__) + /* Tell the kernel that the header is fully-formed when it gets it. + This is required in order to fake the src address. */ + { + int one = 1; + ioctl(pcap_fileno(dev->handle), BIOCSHDRCMPLT, &one); + } +#endif /* xBSD */ + +#if defined (USE_READER_THREAD) + { + pthread_attr_t attr; + + ethq_init (&dev->read_queue, 200); /* initialize FIFO queue */ + pthread_mutex_init (&dev->lock, NULL); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + pthread_create (&dev->reader_thread, &attr, _eth_reader, (void *)dev); + pthread_attr_destroy(&attr); + } +#else /* !defined (USE_READER_THREAD */ +#ifdef USE_SETNONBLOCK + /* set ethernet device non-blocking so pcap_dispatch() doesn't hang */ + if (pcap_setnonblock (dev->handle, 1, errbuf) == -1) { + msg = "Eth: Failed to set non-blocking: %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } +#endif +#endif /* !defined (USE_READER_THREAD */ + return SCPE_OK; +} + +t_stat eth_close(ETH_DEV* dev) +{ + char* msg = "Eth: closed %s\r\n"; + pcap_t *pcap; + + /* make sure device exists */ + if (!dev) return SCPE_UNATT; + + /* close the device */ + pcap = (pcap_t *)dev->handle; + dev->handle = NULL; + pcap_close(pcap); + printf (msg, dev->name); + if (sim_log) fprintf (sim_log, msg, dev->name); + +#if defined (USE_READER_THREAD) + pthread_join (dev->reader_thread, NULL); +#endif + + /* clean up the mess */ + free(dev->name); + eth_zero(dev); + + return SCPE_OK; +} + +t_stat eth_reflect(ETH_DEV* dev, ETH_MAC mac) +{ + ETH_PACK send, recv; + t_stat status; + int i; + struct timeval delay; + + /* build a packet */ + memset (&send, 0, sizeof(ETH_PACK)); + send.len = ETH_MIN_PACKET; /* minimum packet size */ + memcpy(&send.msg[0], mac, sizeof(ETH_MAC)); /* target address */ + memcpy(&send.msg[6], mac, sizeof(ETH_MAC)); /* source address */ + send.msg[12] = 0x90; /* loopback packet type */ + for (i=14; ireflections = 0; + eth_filter(dev, 1, (ETH_MAC *)mac, 0, 0); + + /* send the packet */ + status = eth_write (dev, &send, NULL); + if (status != SCPE_OK) { + char *msg; + msg = "Eth: Error Transmitting packet: %s\r\n" + "You may need to run as root, or install a libpcap version\r\n" + "which is at least 0.9 from www.tcpdump.org\r\n"; + printf(msg, strerror(errno)); + if (sim_log) fprintf (sim_log, msg, strerror(errno)); + return status; + } + + /* if/when we have a sim_os_msleep() we'll use it here instead of this select() */ + delay.tv_sec = 0; + delay.tv_usec = 50*1000; + select(0, NULL, NULL, NULL, &delay); /* make sure things settle into the read path */ + + /* empty the read queue and count the reflections */ + do { + memset (&recv, 0, sizeof(ETH_PACK)); + status = eth_read (dev, &recv, NULL); + if (memcmp(send.msg, recv.msg, ETH_MIN_PACKET)== 0) + dev->reflections++; + } while (recv.len > 0); + + sim_debug(dev->dbit, dev->dptr, "Reflections = %d\n", dev->reflections); + return dev->reflections; +} + +t_stat eth_write(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +{ + int status = 1; /* default to failure */ + + /* make sure device exists */ + if (!dev) return SCPE_UNATT; + + /* make sure packet exists */ + if (!packet) return SCPE_ARG; + + /* make sure packet is acceptable length */ + if ((packet->len >= ETH_MIN_PACKET) && (packet->len <= ETH_MAX_PACKET)) { + eth_packet_trace (dev, packet->msg, packet->len, "writing"); + + /* dispatch write request (synchronous; no need to save write info to dev) */ + status = pcap_sendpacket((pcap_t*)dev->handle, (u_char*)packet->msg, packet->len); + + /* detect sending of decnet loopback packet */ + if ((status == 0) && DECNET_SELF_FRAME(dev->decnet_addr, packet->msg)) + dev->decnet_self_sent += dev->reflections; + + } /* if packet->len */ + + /* call optional write callback function */ + if (routine) + (routine)(status); + + return ((status == 0) ? SCPE_OK : SCPE_IOERR); +} + +void eth_callback(u_char* info, const struct pcap_pkthdr* header, const u_char* data) +{ + ETH_DEV* dev = (ETH_DEV*) info; +#ifdef USE_BPF + int to_me = 1; +#else /* !USE_BPF */ + int to_me = 0; + int from_me = 0; + int i; + +#ifdef ETH_DEBUG +// eth_packet_trace (dev, data, header->len, "received"); +#endif + for (i = 0; i < dev->addr_count; i++) { + if (memcmp(data, dev->filter_address[i], 6) == 0) to_me = 1; + if (memcmp(&data[6], dev->filter_address[i], 6) == 0) from_me = 1; + } + + /* all multicast mode? */ + if (dev->all_multicast && (data[0] & 0x01)) to_me = 1; + + /* promiscuous mode? */ + if (dev->promiscuous) to_me = 1; +#endif /* USE_BPF */ + + /* detect sending of decnet loopback packet */ + if (DECNET_SELF_FRAME(dev->decnet_addr, data)) { + /* lower reflection count - if already zero, pass it on */ + if (dev->decnet_self_sent > 0) { + dev->decnet_self_sent--; + to_me = 0; + } +#ifndef USE_BPF + else + from_me = 0; +#endif + } + +#ifdef USE_BPF + if (to_me) { +#else /* !USE_BPF */ + if (to_me && !from_me) { +#endif +#if defined (USE_READER_THREAD) + ETH_PACK tmp_packet; + + /* set data in passed read packet */ + tmp_packet.len = header->len; + memcpy(tmp_packet.msg, data, header->len); + if (dev->need_crc) + eth_add_crc32(&tmp_packet); + + eth_packet_trace (dev, tmp_packet.msg, tmp_packet.len, "rcvqd"); + + pthread_mutex_lock (&dev->lock); + ethq_insert(&dev->read_queue, 2, &tmp_packet, 0); + pthread_mutex_unlock (&dev->lock); +#else + /* set data in passed read packet */ + dev->read_packet->len = header->len; + memcpy(dev->read_packet->msg, data, header->len); + if (dev->need_crc) + eth_add_crc32(dev->read_packet); + + eth_packet_trace (dev, dev->read_packet->msg, dev->read_packet->len, "reading"); + + /* call optional read callback function */ + if (dev->read_callback) + (dev->read_callback)(0); +#endif + } +} + +t_stat eth_read(ETH_DEV* dev, ETH_PACK* packet, ETH_PCALLBACK routine) +{ + int status; + + /* make sure device exists */ + + if (!dev) return SCPE_UNATT; + + /* make sure packet exists */ + if (!packet) return SCPE_ARG; + +#if !defined (USE_READER_THREAD) + /* set read packet */ + dev->read_packet = packet; + packet->len = 0; + + /* set optional callback routine */ + dev->read_callback = routine; + + /* dispatch read request to either receive a filtered packet or timeout */ + do { + status = pcap_dispatch((pcap_t*)dev->handle, 1, ð_callback, (u_char*)dev); + } while ((status) && (0 == packet->len)); + +#else /* USE_READER_THREAD */ + + status = 0; + pthread_mutex_lock (&dev->lock); + if (dev->read_queue.count > 0) { + ETH_ITEM* item = &dev->read_queue.item[dev->read_queue.head]; + packet->len = item->packet.len; + memcpy(packet->msg, item->packet.msg, packet->len); + if (routine) + routine(status); + ethq_remove(&dev->read_queue); + } + pthread_mutex_unlock (&dev->lock); +#endif + + return SCPE_OK; +} + +t_stat eth_filter(ETH_DEV* dev, int addr_count, ETH_MAC* addresses, + ETH_BOOL all_multicast, ETH_BOOL promiscuous) +{ + int i; + bpf_u_int32 bpf_subnet, bpf_netmask; + char buf[110+66*ETH_FILTER_MAX]; + char errbuf[PCAP_ERRBUF_SIZE]; + char mac[20]; + char* buf2; + t_stat status; +#ifdef USE_BPF + struct bpf_program bpf; + char* msg; +#endif + + /* make sure device exists */ + if (!dev) return SCPE_UNATT; + + /* filter count OK? */ + if ((addr_count < 0) || (addr_count > ETH_FILTER_MAX)) + return SCPE_ARG; + else + if (!addresses) return SCPE_ARG; + + /* set new filter addresses */ + for (i = 0; i < addr_count; i++) + memcpy(dev->filter_address[i], addresses[i], sizeof(ETH_MAC)); + dev->addr_count = addr_count; + + /* store other flags */ + dev->all_multicast = all_multicast; + dev->promiscuous = promiscuous; + + /* print out filter information if debugging */ + if (dev->dptr->dctrl & dev->dbit) { + sim_debug(dev->dbit, dev->dptr, "Filter Set\n"); + for (i = 0; i < addr_count; i++) { + char mac[20]; + eth_mac_fmt(&dev->filter_address[i], mac); + sim_debug(dev->dbit, dev->dptr, " Addr[%d]: %s\n", i, mac); + } + if (dev->all_multicast) + sim_debug(dev->dbit, dev->dptr, "All Multicast\n"); + if (dev->promiscuous) + sim_debug(dev->dbit, dev->dptr, "Promiscuous\n"); + } + + /* test reflections */ + if (dev->reflections == -1) + status = eth_reflect(dev, dev->filter_address[0]); + + /* setup BPF filters and other fields to minimize packet delivery */ + strcpy(buf, ""); + + /* construct destination filters - since the real ethernet interface was set + into promiscuous mode by eth_open(), we need to filter out the packets that + our simulated interface doesn't want. */ + if (!dev->promiscuous) { + for (i = 0; i < addr_count; i++) { + eth_mac_fmt(&dev->filter_address[i], mac); + if (!strstr(buf, mac)) /* eliminate duplicates */ + sprintf(&buf[strlen(buf)], "%s(ether dst %s)", (*buf) ? " or " : "", mac); + } + if (dev->all_multicast) + sprintf(&buf[strlen(buf)], "%s(ether multicast)", (*buf) ? " or " : ""); + } + + /* construct source filters - this prevents packets from being reflected back + by systems where WinPcap and libpcap cause packet reflections. Note that + some systems do not reflect packets at all. This *assumes* that the + simulated NIC will not send out packets with multicast source fields. */ + if ((addr_count > 0) && (dev->reflections > 0)) { + if (strlen(buf) > 0) + sprintf(&buf[strlen(buf)], " and "); + sprintf (&buf[strlen(buf)], "not ("); + buf2 = &buf[strlen(buf)]; + for (i = 0; i < addr_count; i++) { + if (dev->filter_address[i][0] & 0x01) continue; /* skip multicast addresses */ + eth_mac_fmt(&dev->filter_address[i], mac); + if (!strstr(buf2, mac)) /* eliminate duplicates */ + sprintf(&buf2[strlen(buf2)], "%s(ether src %s)", (*buf2) ? " or " : "", mac); + } + sprintf (&buf[strlen(buf)], ")"); + } + /* When starting, DECnet sends out a packet with the source and destination + addresses set to the same value as the DECnet MAC address. This packet is + designed to find and help diagnose DECnet address conflicts. Normally, this + packet would not be seen by the sender, only by the other machine that has + the same DECnet address. If the ethernet subsystem is reflecting packets, + DECnet will fail to start if it sees the reflected packet, since it thinks + another system is using this DECnet address. We have to let these packets + through, so that if another machine has the same DECnet address that we + can detect it. Both eth_write() and eth_callback() help by checking the + reflection count - eth_write() adds the reflection count to + dev->decnet_self_sent, and eth_callback() check the value - if the + dev->decnet_self_sent count is zero, then the packet has come from another + machine with the same address, and needs to be passed on to the simulated + machine. */ + memset(dev->decnet_addr, 0, sizeof(ETH_MAC)); + /* check for decnet address in filters */ + if ((addr_count) && (dev->reflections > 0)) { + for (i = 0; i < addr_count; i++) { + eth_mac_fmt(&dev->filter_address[i], mac); + if (memcmp(mac, "AA:00:04", 8) == 0) { + memcpy(dev->decnet_addr, &dev->filter_address[i], sizeof(ETH_MAC)); + /* let packets through where dst and src are the same as our decnet address */ + sprintf (&buf[strlen(buf)], " or ((ether dst %s) and (ether src %s))", mac, mac); + break; + } + } + } + sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); + + + /* get netmask, which is required for compiling */ + if (pcap_lookupnet(dev->handle, &bpf_subnet, &bpf_netmask, errbuf)<0) { + bpf_netmask = 0; + } + +#ifdef USE_BPF + /* compile filter string */ + if ((status = pcap_compile(dev->handle, &bpf, buf, 1, bpf_netmask)) < 0) { + sprintf(errbuf, "%s", pcap_geterr(dev->handle)); + msg = "Eth: pcap_compile error: %s\r\n"; + printf(msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + /* show erroneous BPF string */ + msg = "Eth: BPF string is: |%s|\r\n"; + printf (msg, buf); + if (sim_log) fprintf (sim_log, msg, buf); + } else { + /* apply compiled filter string */ + if ((status = pcap_setfilter(dev->handle, &bpf)) < 0) { + sprintf(errbuf, "%s", pcap_geterr(dev->handle)); + msg = "Eth: pcap_setfilter error: %s\r\n"; + printf(msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } else { +#ifdef USE_SETNONBLOCK + /* set file non-blocking */ + status = pcap_setnonblock (dev->handle, 1, errbuf); +#endif /* USE_SETNONBLOCK */ + } + pcap_freecode(&bpf); + } +#endif /* USE_BPF */ + + return SCPE_OK; +} + +/* + The libpcap provided API pcap_findalldevs() on most platforms, will + leverage the getifaddrs() API if it is available in preference to + alternate platform specific methods of determining the interface list. + + A limitation of getifaddrs() is that it returns only interfaces which + have associated addresses. This may not include all of the interesting + interfaces that we are interested in since a host may have dedicated + interfaces for a simulator, which is otherwise unused by the host. + + One could hand craft the the build of libpcap to specifically use + alternate methods to implement pcap_findalldevs(). However, this can + get tricky, and would then result in a sort of deviant libpcap. + + This routine exists to allow platform specific code to validate and/or + extend the set of available interfaces to include any that are not + returned by pcap_findalldevs. + +*/ +int eth_host_devices(int used, int max, ETH_LIST* list) +{ + pcap_t* conn; + int i, j, datalink; + char errbuf[PCAP_ERRBUF_SIZE]; + + for (i=0; i sizeof(regval))) { + RegCloseKey (reghnd); + continue; + } + /* registry value seems OK, finish up and replace description */ + RegCloseKey (reghnd ); + sprintf (list[i].desc, "%s", regval); + } + } /* for */ +#endif + + return used; +} + +int eth_devices(int max, ETH_LIST* list) +{ + pcap_if_t* alldevs; + pcap_if_t* dev; + int i = 0; + char errbuf[PCAP_ERRBUF_SIZE]; + +#ifndef DONT_USE_PCAP_FINDALLDEVS + /* retrieve the device list */ + if (pcap_findalldevs(&alldevs, errbuf) == -1) { + char* msg = "Eth: error in pcap_findalldevs: %s\r\n"; + printf (msg, errbuf); + if (sim_log) fprintf (sim_log, msg, errbuf); + } else { + /* copy device list into the passed structure */ + for (i=0, dev=alldevs; dev; dev=dev->next) { + if ((dev->flags & PCAP_IF_LOOPBACK) || (!strcmp("any", dev->name))) continue; + list[i].num = i; + sprintf(list[i].name, "%s", dev->name); + if (dev->description) + sprintf(list[i].desc, "%s", dev->description); + else + sprintf(list[i].desc, "%s", "No description available"); + if (i++ >= max) break; + } + + /* free device list */ + pcap_freealldevs(alldevs); + } +#endif + + /* Add any host specific devices and/or validate those already found */ + i = eth_host_devices(i, max, list); + + /* return device count */ + return i; +} + +#endif /* USE_NETWORK */ diff --git a/sim_ether.h b/sim_ether.h new file mode 100644 index 0000000..371ee3b --- /dev/null +++ b/sim_ether.h @@ -0,0 +1,212 @@ +/* sim_ether.h: OS-dependent network information + ------------------------------------------------------------------------------ + + Copyright (c) 2002-2005, David T. Hittner + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of the author shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from the author. + + ------------------------------------------------------------------------------ + + Modification history: + + 30-Nov-05 DTH Added CRC length to packet and more field comments + 04-Feb-04 DTH Added debugging information + 14-Jan-04 MP Generalized BSD support issues + 05-Jan-04 DTH Added eth_mac_scan + 26-Dec-03 DTH Added ethernet show and queue functions from pdp11_xq + 23-Dec-03 DTH Added status to packet + 01-Dec-03 DTH Added reflections, tweaked decnet fix items + 25-Nov-03 DTH Verified DECNET_FIX, reversed ifdef to mainstream code + 14-Nov-03 DTH Added #ifdef DECNET_FIX for problematic duplicate detection code + 07-Jun-03 MP Added WIN32 support for DECNET duplicate address detection. + 05-Jun-03 DTH Added used to struct eth_packet + 01-Feb-03 MP Changed some uint8 strings to char* to reflect usage + 22-Oct-02 DTH Added all_multicast and promiscuous support + 21-Oct-02 DTH Corrected copyright again + 16-Oct-02 DTH Fixed copyright + 08-Oct-02 DTH Integrated with 2.10-0p4, added variable vector and copyrights + 03-Oct-02 DTH Beta version of xq/sim_ether released for SIMH 2.09-11 + 15-Aug-02 DTH Started XQ simulation + + ------------------------------------------------------------------------------ +*/ + +#ifndef _SIM_ETHER_H +#define _SIM_ETHER_H + +#include "sim_defs.h" + +/* make common BSD code a bit easier to read in this file */ +/* OS/X seems to define and compile using one of these BSD types */ +#if defined(__NetBSD__) || defined (__OpenBSD__) || defined (__FreeBSD__) +#define xBSD 1 +#endif +#if !defined(__FreeBSD__) && !defined(_WIN32) && !defined(VMS) +#define USE_SETNONBLOCK 1 +#endif + +#if defined(__sun__) && defined(__i386__) +#define USE_READER_THREAD 1 +#endif + +/* make common winpcap code a bit easier to read in this file */ +#if defined(_WIN32) || defined(VMS) +#define PCAP_READ_TIMEOUT -1 +#else +#define PCAP_READ_TIMEOUT 1 +#endif + +/* set related values to have correct relationships */ +#if defined (USE_READER_THREAD) +#if defined (USE_SETNONBLOCK) +#undef USE_SETNONBLOCK +#endif +#undef PCAP_READ_TIMEOUT +#define PCAP_READ_TIMEOUT 15 +#if !defined (xBSD) && !defined(_WIN32) && !defined(VMS) +#define MUST_DO_SELECT +#endif +#endif + +/* + USE_BPF is defined to let this code leverage the libpcap/OS kernel provided + BPF packet filtering. This generally will enhance performance. It may not + be available in some environments and/or it may not work correctly, so + undefining this will still provide working code here. +*/ +#define USE_BPF 1 + +#if defined (USE_READER_THREAD) +#include +#endif + +/* structure declarations */ + +#define ETH_PROMISC 1 /* promiscuous mode = true */ +#define ETH_TIMEOUT -1 /* read timeout in milliseconds (immediate) */ +#define ETH_FILTER_MAX 20 /* maximum address filters */ +#define ETH_DEV_NAME_MAX 256 /* maximum device name size */ +#define ETH_DEV_DESC_MAX 256 /* maximum device description size */ +#define ETH_MIN_PACKET 60 /* minimum ethernet packet size */ +#define ETH_MAX_PACKET 1514 /* maximum ethernet packet size */ +#define ETH_MAX_DEVICE 10 /* maximum ethernet devices */ +#define ETH_CRC_SIZE 4 /* ethernet CRC size */ +#define ETH_FRAME_SIZE 1518 /* ethernet maximum frame size */ + +#define DECNET_SELF_FRAME(dnet_mac, msg) \ + ((memcmp(dnet_mac, msg , 6) == 0) && \ + (memcmp(dnet_mac, msg+6, 6) == 0)) + +struct eth_packet { + uint8 msg[ETH_FRAME_SIZE]; /* ethernet frame (message) */ + int len; /* packet length without CRC */ + int used; /* bytes processed (used in packet chaining) */ + int status; /* transmit/receive status */ + int crc_len; /* packet length with CRC */ +}; + +struct eth_item { + int type; /* receive (0=setup, 1=loopback, 2=normal) */ + struct eth_packet packet; +}; + +struct eth_queue { + int max; + int count; + int head; + int tail; + int loss; + int high; + struct eth_item* item; +}; + +struct eth_list { + int num; + char name[ETH_DEV_NAME_MAX]; + char desc[ETH_DEV_DESC_MAX]; +}; + +typedef int ETH_BOOL; +typedef unsigned char ETH_MAC[6]; +typedef struct eth_packet ETH_PACK; +typedef void (*ETH_PCALLBACK)(int status); +typedef struct eth_list ETH_LIST; +typedef struct eth_queue ETH_QUE; +typedef struct eth_item ETH_ITEM; + +struct eth_device { + char* name; /* name of ethernet device */ + void* handle; /* handle of implementation-specific device */ + ETH_PCALLBACK read_callback; /* read callback function */ + ETH_PCALLBACK write_callback; /* write callback function */ + ETH_PACK* read_packet; /* read packet */ + ETH_PACK* write_packet; /* write packet */ + ETH_MAC filter_address[ETH_FILTER_MAX]; /* filtering addresses */ + int addr_count; /* count of filtering addresses */ + ETH_BOOL promiscuous; /* promiscuous mode flag */ + ETH_BOOL all_multicast; /* receive all multicast messages */ + int32 decnet_self_sent; /* loopback packets sent but not seen */ + ETH_MAC decnet_addr; /* decnet address of interface */ + DEVICE* dptr; /* device ethernet is attached to */ + uint32 dbit; /* debugging bit */ + int reflections; /* packet reflections on interface */ + int need_crc; /* device needs CRC (Cyclic Redundancy Check) */ +#if defined (USE_READER_THREAD) + ETH_QUE read_queue; + pthread_mutex_t lock; + pthread_t reader_thread; /* Reader Thread Id */ +#endif +}; + +typedef struct eth_device ETH_DEV; + +/* prototype declarations*/ + +t_stat eth_open (ETH_DEV* dev, char* name, /* open ethernet interface */ + DEVICE* dptr, uint32 dbit); +t_stat eth_close (ETH_DEV* dev); /* close ethernet interface */ +t_stat eth_write (ETH_DEV* dev, ETH_PACK* packet, /* write sychronous packet; */ + ETH_PCALLBACK routine); /* callback when done */ +t_stat eth_read (ETH_DEV* dev, ETH_PACK* packet, /* read single packet; */ + ETH_PCALLBACK routine); /* callback when done*/ +t_stat eth_filter (ETH_DEV* dev, int addr_count, /* set filter on incoming packets */ + ETH_MAC* addresses, + ETH_BOOL all_multicast, + ETH_BOOL promiscuous); +int eth_devices (int max, ETH_LIST* dev); /* get ethernet devices on host */ +void eth_setcrc (ETH_DEV* dev, int need_crc); /* enable/disable CRC mode */ + +void eth_packet_trace (ETH_DEV* dev, const uint8 *msg, int len, char* txt); /* trace ethernet packet */ +t_stat eth_show (FILE* st, UNIT* uptr, /* show ethernet devices */ + int32 val, void* desc); + +void eth_mac_fmt (ETH_MAC* add, char* buffer); /* format ethernet mac address */ +t_stat eth_mac_scan (ETH_MAC* mac, char* strmac); /* scan string for mac, put in mac */ + +t_stat ethq_init (ETH_QUE* que, int max); /* initialize FIFO queue */ +void ethq_clear (ETH_QUE* que); /* clear FIFO queue */ +void ethq_remove (ETH_QUE* que); /* remove item from FIFO queue */ +void ethq_insert (ETH_QUE* que, int32 type, /* insert item into FIFO queue */ + ETH_PACK* packet, int32 status); + + +#endif /* _SIM_ETHER_H */ diff --git a/sim_fio.c b/sim_fio.c new file mode 100644 index 0000000..d7b4d62 --- /dev/null +++ b/sim_fio.c @@ -0,0 +1,321 @@ +/* sim_fio.c: simulator file I/O library + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 28-Jun-07 RMS Added VMS IA64 support (from Norm Lastovica) + 10-Jul-06 RMS Fixed linux conditionalization (from Chaskiel Grundman) + 15-May-06 RMS Added sim_fsize_name + 21-Apr-06 RMS Added FreeBSD large file support (from Mark Martinec) + 19-Nov-05 RMS Added OS/X large file support (from Peter Schorn) + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 17-Jul-04 RMS Fixed bug in optimized sim_fread (reported by Scott Bailey) + 26-May-04 RMS Optimized sim_fread (suggested by John Dundas) + 02-Jan-04 RMS Split out from SCP + + This library includes: + + sim_finit - initialize package + sim_fopen - open file + sim_fread - endian independent read (formerly fxread) + sim_write - endian independent write (formerly fxwrite) + sim_fseek - extended (>32b) seek (formerly fseek_ext) + sim_fsize - get file size + + sim_fopen and sim_fseek are OS-dependent. The other routines are not. + sim_fsize is always a 32b routine (it is used only with small capacity random + access devices like fixed head disks and DECtapes). +*/ + +#include "sim_defs.h" + +static unsigned char sim_flip[FLIP_SIZE]; +int32 sim_end = 1; /* 1 = little */ + +/* OS-independent, endian independent binary I/O package + + For consistency, all binary data read and written by the simulator + is stored in little endian data order. That is, in a multi-byte + data item, the bytes are written out right to left, low order byte + to high order byte. On a big endian host, data is read and written + from high byte to low byte. Consequently, data written on a little + endian system must be byte reversed to be usable on a big endian + system, and vice versa. + + These routines are analogs of the standard C runtime routines + fread and fwrite. If the host is little endian, or the data items + are size char, then the calls are passed directly to fread or + fwrite. Otherwise, these routines perform the necessary byte swaps. + Sim_fread swaps in place, sim_fwrite uses an intermediate buffer. +*/ + +int32 sim_finit (void) +{ +union {int32 i; char c[sizeof (int32)]; } end_test; + +end_test.i = 1; /* test endian-ness */ +sim_end = end_test.c[0]; +return sim_end; +} + +size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr) +{ +size_t c, j; +int32 k; +unsigned char by, *sptr, *dptr; + +if ((size == 0) || (count == 0)) return 0; /* check arguments */ +c = fread (bptr, size, count, fptr); /* read buffer */ +if (sim_end || (size == sizeof (char)) || (c == 0)) /* le, byte, or err? */ + return c; /* done */ +for (j = 0, dptr = sptr = (unsigned char *) bptr; j < c; j++) { /* loop on items */ + for (k = size - 1; k >= (((int32) size + 1) / 2); k--) { + by = *sptr; /* swap end-for-end */ + *sptr++ = *(dptr + k); + *(dptr + k) = by; + } + sptr = dptr = dptr + size; /* next item */ + } +return c; +} + +size_t sim_fwrite (void *bptr, size_t size, size_t count, FILE *fptr) +{ +size_t c, j, nelem, nbuf, lcnt, total; +int32 i, k; +unsigned char *sptr, *dptr; + +if ((size == 0) || (count == 0)) return 0; /* check arguments */ +if (sim_end || (size == sizeof (char))) /* le or byte? */ + return fwrite (bptr, size, count, fptr); /* done */ +nelem = FLIP_SIZE / size; /* elements in buffer */ +nbuf = count / nelem; /* number buffers */ +lcnt = count % nelem; /* count in last buf */ +if (lcnt) nbuf = nbuf + 1; +else lcnt = nelem; +total = 0; +sptr = (unsigned char *) bptr; /* init input ptr */ +for (i = nbuf; i > 0; i--) { /* loop on buffers */ + c = (i == 1)? lcnt: nelem; + for (j = 0, dptr = sim_flip; j < c; j++) { /* loop on items */ + for (k = size - 1; k >= 0; k--) *(dptr + k) = *sptr++; + dptr = dptr + size; + } + c = fwrite (sim_flip, size, c, fptr); + if (c == 0) return total; + total = total + c; + } +return total; +} + +/* Get file size */ + +uint32 sim_fsize_name (char *fname) +{ +FILE *fp; +uint32 sz; + +if ((fp = sim_fopen (fname, "rb")) == NULL) return 0; +sz = sim_fsize (fp); +fclose (fp); +return sz; +} + +uint32 sim_fsize (FILE *fp) +{ +uint32 pos, sz; + +if (fp == NULL) return 0; +pos = ftell (fp); +fseek (fp, 0, SEEK_END); +sz = ftell (fp); +fseek (fp, pos, SEEK_SET); +return sz; +} + +/* OS-dependent routines */ + +/* Optimized file open */ + +FILE *sim_fopen (const char *file, const char *mode) +{ +#if defined (VMS) +return fopen (file, mode, "ALQ=32", "DEQ=4096", + "MBF=6", "MBC=127", "FOP=cbt,tef", "ROP=rah,wbh", "CTX=stm"); +#elif defined (USE_INT64) && defined (USE_ADDR64) && defined (__linux) +return fopen64 (file, mode); +#else +return fopen (file, mode); +#endif +} + +/* Long seek */ + +#if defined (USE_INT64) && defined (USE_ADDR64) + +/* 64b VMS */ + +#if (defined (__ALPHA) || defined (__ia64)) && defined (VMS) /* 64b VMS */ +#define _SIM_IO_FSEEK_EXT_ 1 + +static t_int64 fpos_t_to_int64 (fpos_t *pos) +{ +unsigned short *w = (unsigned short *) pos; /* endian dep! */ +t_int64 result; + +result = w[1]; +result <<= 16; +result += w[0]; +result <<= 9; +result += w[2]; +return result; +} + +static void int64_to_fpos_t (t_int64 ipos, fpos_t *pos, size_t mbc) +{ +unsigned short *w = (unsigned short *) pos; +int bufsize = mbc << 9; + +w[3] = 0; +w[2] = (unsigned short) (ipos % bufsize); +ipos -= w[2]; +ipos >>= 9; +w[0] = (unsigned short) ipos; +ipos >>= 16; +w[1] = (unsigned short) ipos; +if ((w[2] == 0) && (w[0] || w[1])) { + w[2] = bufsize; + w[0] -= mbc; + } +return; +} + +int sim_fseek (FILE *st, t_addr offset, int whence) +{ +t_addr fileaddr; +fpos_t filepos; + +switch (whence) { + + case SEEK_SET: + fileaddr = offset; + break; + + case SEEK_CUR: + if (fgetpos (st, &filepos)) return (-1); + fileaddr = fpos_t_to_int64 (&filepos); + fileaddr = fileaddr + offset; + break; + + default: + errno = EINVAL; + return (-1); + } + +int64_to_fpos_t (fileaddr, &filepos, 127); +return fsetpos (st, &filepos); +} + +#endif + +/* Alpha UNIX - natively 64b */ + +#if defined (__ALPHA) && defined (__unix__) /* Alpha UNIX */ +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (FILE *st, t_addr offset, int whence) +{ +return fseek (st, offset, whence); +} + +#endif + +/* Windows */ + +#if defined (_WIN32) +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (FILE *st, t_addr offset, int whence) +{ +fpos_t fileaddr; + +switch (whence) { + + case SEEK_SET: + fileaddr = offset; + break; + + case SEEK_CUR: + if (fgetpos (st, &fileaddr)) return (-1); + fileaddr = fileaddr + offset; + break; + + default: + errno = EINVAL; + return (-1); + } + +return fsetpos (st, &fileaddr); +} + +#endif /* end Windows */ + +/* Linux */ + +#if defined (__linux) +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (FILE *st, t_addr xpos, int origin) +{ +return fseeko64 (st, xpos, origin); +} + +#endif /* end Linux with LFS */ + +/* Apple OS/X */ + +#if defined (__APPLE__) || defined (__FreeBSD__) +#define _SIM_IO_FSEEK_EXT_ 1 + +int sim_fseek (FILE *st, t_addr xpos, int origin) +{ +return fseeko (st, xpos, origin); +} + +#endif /* end Apple OS/X */ + +#endif /* end 64b seek defs */ + +/* Default: no OS-specific routine has been defined */ + +#if !defined (_SIM_IO_FSEEK_EXT_) +#define _SIM_IO_FSEEK_EXT_ 0 + +int sim_fseek (FILE *st, t_addr xpos, int origin) +{ +return fseek (st, (int32) xpos, origin); +} + +#endif + +uint32 sim_taddr_64 = _SIM_IO_FSEEK_EXT_; diff --git a/sim_fio.h b/sim_fio.h new file mode 100644 index 0000000..70cd153 --- /dev/null +++ b/sim_fio.h @@ -0,0 +1,46 @@ +/* sim_fio.h: simulator file I/O library headers + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + be used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 15-May-06 RMS Added sim_fsize_name + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 02-Jan-04 RMS Split out from SCP +*/ + +#ifndef _SIM_FIO_H_ +#define _SIM_FIO_H_ 0 + +#define FLIP_SIZE (1 << 16) /* flip buf size */ +#define fxread(a,b,c,d) sim_fread (a, b, c, d) +#define fxwrite(a,b,c,d) sim_fwrite (a, b, c, d) + +int32 sim_finit (void); +FILE *sim_fopen (const char *file, const char *mode); +int sim_fseek (FILE *st, t_addr offset, int whence); +size_t sim_fread (void *bptr, size_t size, size_t count, FILE *fptr); +size_t sim_fwrite (void *bptr, size_t size, size_t count, FILE *fptr); +uint32 sim_fsize (FILE *fptr); +uint32 sim_fsize_name (char *fname); + +#endif diff --git a/sim_readline.c b/sim_readline.c new file mode 100644 index 0000000..d7d8d81 --- /dev/null +++ b/sim_readline.c @@ -0,0 +1,128 @@ +/* sim_readline.c: libreadline wrapper + + Written by Philipp Hachtmann + + + 01-OCT-08 PH Include stdio.h + 19-SEP-08 PH Initial version + + +*/ + +#ifdef HAVE_READLINE + +#include +#include +#include +#include "sim_defs.h" + +#ifdef READLINE_SAVED_HISTORY + +#include +#include +#include + +#define HISTORYFILE ".simh_history" + +/* Simulator name */ +extern char sim_name[]; + +static char historyfile[240]; + +#endif + +void readline_read_history(){ + +#ifdef READLINE_SAVED_HISTORY + + char filename[40]; + int c; + +#endif + + using_history(); + +#ifdef READLINE_SAVED_HISTORY + + /* Generate lower case file name like ".nova_history" */ + strcpy(filename,"."); + strncat (filename,sim_name,30); + filename[39]=0; + for (c=0;cline)!=0) + add_history(cpre); + } else { + add_history(cpre); + } + } + + /* Skip spaces and TAB characters */ + while ((*result==' ')||(*result=='\t')) result++; + + /* We're finished here */ + return result; +} + + +#endif diff --git a/sim_readline.h b/sim_readline.h new file mode 100644 index 0000000..10c60c1 --- /dev/null +++ b/sim_readline.h @@ -0,0 +1,20 @@ +/* sim_readline.h: declarations for libreadline wrapper + + Written by Philipp Hachtmann + + + 19-SEP-08 PH Initial version + + +*/ + +#ifndef READLINE_H +#define READLINE_H +#ifdef HAVE_READLINE + +extern char *readline_read_line(char *cpre, int32 size, const char * prompt); +extern void readline_read_history(); +extern void readline_write_history(); + +#endif +#endif diff --git a/sim_rev.h b/sim_rev.h new file mode 100644 index 0000000..9cda332 --- /dev/null +++ b/sim_rev.h @@ -0,0 +1,2191 @@ +/* sim_rev.h: simulator revisions and current rev level + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. +*/ + +#ifndef _SIM_REV_H_ +#define _SIM_REV_H_ 0 + +#define SIM_MAJOR 3 +#define SIM_MINOR 8 +#define SIM_PATCH 0 +#define SIM_DELTA 0 + +/* V3.8 revision history + +patch date module(s) and fix(es) + + 0 tbd scp.c: + - fixed bug in local/global register search (found by Mark Pizzolato) + - fixed bug in restore of RO units (from Mark Pizzolato) + - added SET/SHO/NO BR with default argument (from Dave Bryan) + + sim_tmxr.c + - worked around Telnet negotiation problem with QCTerm (from Dave Bryan) + + gri_defs.h, gri_cpu.c, gri_sys.c: + - added GRI-99 support + + hp2100_baci.c (from Dave Bryan): + - Implemented 12966A Buffered Asynchronous Communications Interface simulator + + hp2100_cpu.c (from Dave Bryan): + - Memory ex/dep and bkpt type default to current map mode + - Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA + - Corrected MP W5 (JSB) jumper action, SET/SHOW reversal, + mp_mevff clear on interrupt with I/O instruction in trap cell + - Removed DBI support from 1000-M (was temporary for RTE-6/VM) + - Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags + - Enabled SIGNAL instructions, SIG debug flag + - Fixed single stepping through interrupts + + hp2100_cpu0.c (from Dave Bryan and Holger Veit): + - Removed and implemented "cpu_rte_vma" and "cpu_rte_os" + - Removed and implemented "cpu_vis" and "cpu_signal" + - Removed and implemented "cpu_rte_ema" + + hp2100_cpu1.c (from Dave Bryan): + - Added fprint_ops, fprint_regs for debug printouts + - Enabled DIAG as NOP on 1000 F-Series + - Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64 + + hp2100_cpu3.c (from Dave Bryan): + - Fixed unsigned divide bug in .DDI + - Fixed unsigned multiply bug in .DMP + - Added implementation of DBI self-test + + hp2100_cpu4.c (from Dave Bryan): + - Fixed B register return bug in /CMRT + + hp2100_cpu5.c (from Holger Veit): + - Implemented RTE-6/VM Virtual Memory Area firmware + - Implemented RTE-IV Extended Memory Area firmware + + hp2100_cpu6.c (from Dave Bryan): + - Implemented RTE-6/VM OS accelerator firmware + + hp2100_cpu7.c (from Holger Veit): + - Implemented Vector Instruction Set and SIGNAL/1000 firmware + + hp2100_ds.c (from Dave Bryan): + - Corrected and verified ioCRS action + - Corrected DPTR register definition from FLDATA to DRDATA + + hp2100_fp.c (from Mark Pizzolato) + - Corrected fp_unpack mantissa high-word return + + hp2100_fp1.c (from Dave Bryan): + - Reworked "complement" to avoid inlining bug in gcc-4.x + - Fixed uninitialized return in fp_accum when setting + + hp2100_mux.c (from Dave Bryan): + - Sync mux poll with console poll for idle compatibility + + hp2100_stddev.c (from Dave Bryan): + - Fixed PTR trailing null counter for tape re-read + - Added IPTICK register to CLK to display CPU instr/tick + - Corrected and verified ioCRS actions + - Changed TTY console poll to 10 msec. real time + - Synchronized CLK with TTY if set for 10 msec. + - Added UNIT_IDLE to TTY and CLK + - Removed redundant control char handling definitions + - Changed TTY output wait from 100 to 200 for MSU BASIC + + hp2100_sys.c (from Dave Bryan): + - Added BACI device + - Added RTE OS/VMA/EMA mnemonics + - Changed fprint_sym to handle step with irq pending + + hp2100_cpu.h (from Dave Bryan): + - Added calc_defer() prototype + - Added extern sim_deb, cpu_dev, DEB flags for debug printouts + - Added extern intaddr, mp_viol, mp_mevff, calc_int, dev_ctl, + ReadIO, WriteIO for RTE-6/VM microcode support + + hp2100_cpu1.h (from Dave Bryan): + - Corrected OP_AFF to OP_AAFF for SIGNAL/1000 + - Removed unused operand patterns + - Added fprint_ops, fprint_regs for debug printouts + - Revised OP_KKKAKK operand profile to OP_CCCACC for $LOC + + hp2100_defs.h (from Dave Bryan): + - Added BACI device + - Added 16/32-bit unsigned-to-signed conversions + - Changed TMR_MUX to TMR_POLL for idle support + - Added POLLMODE, sync_poll() declaration + - Added I_MRG, I_ISZ, I_IOG, I_STF, and I_SFS instruction masks + - Added I_MRG_I, I_JSB, I_JSB_I, and I_JMP instruction masks + + nova_defs.h (from Bruce Ray): + - added support for third-party 64KW memory + + nova_clk.c (from Bruce Ray): + - renamed to RTC, to match DG literature + + nova_cpu.c (from Bruce Ray): + - added support for third-party 64KW memory + - added Nova 3 "secret" instructions + - added CPU history support + + nova_dkp.c (from Bruce Ray): + - renamed to DKP, to match DG literature + - fixed numerous bugs in both documented and undocumented behavior + - changed bootstrap code to DG official sequence + + nova_dsk.c (from Bruce Ray): + - renamed to DSK, to match DG literature + - added support for undocumented behavior + - changed bootstrap code to DG official sequence + + nova_mta.c (from Bruce Ray): + - renamed to MTA, to match DG literature + - changed bootstrap code to DG official sequence + + nova_plt.c, nova_pt.c (from Bruce Ray): + - added 7B/8B support + + nova_sys.c (from Bruce Ray): + - fixed mistaken instruction mnemonics + + pdp11_cpu.c, pdp11_io.c, pdp11_rh.c: + - fixed DMA memory address limit test (found by John Dundas) + - fixed MMR0 treatment in RESET (found by Walter Mueller) + + pdp11_cpumod.h, pdp11_cpumod.c: + - fixed write behavior of 11/70 MBRK, LOSIZE, HISIZE (found by Walter Mueller) + - added support to set default state of KDJ11B,E clock control register + + pdp11_dc.c: + - added support for DC11 + + pdp11_defs.h: + - added KE, KG, RC, DC support + - renamed DL11 devices + + pdp11_dl.c: + - renamed devices to DLI/DLO, to match DC11 + - added modem control + + pdp11_io.c: + - added autoconfigure support for DC11 + + pdp11_ke.c: + - added support for KE11A + + pdp11_kg.c (from John Dundas): + - added support for KG11A + + pdp11_rc.c (from John Dundas): + - added support for RC11 + + pdp11_sys.c: + - modified to allow -A, -B use with 8b devices + - added KE, KG, RC, DC support + - renamed DL11 devices + + vax_cmode.c, vax_io.c, vax780_uba.c: + - fixed declarations (from Mark Pizzolato) + + +/* V3.7 revision history + + 3 02-Sep-07 scp.c: + - fixed bug in SET THROTTLE command + + pdp10_cpu.c: + - fixed non-portable usage in SHOW HISTORY routine + + pdp11_ta.c: + - forward op at BOT skips initial file gap + + pdp8_ct.c: + - forward op at BOT skips initial file gap + - fixed handling of BEOT + + vax_cpu.c: + - fixed bug in read access g-format indexed specifiers + + 2 12-Jul-07 sim_ether.c (from Dave Hittner): + - fixed non-ethernet device removal loop (from Naoki Hamada) + - added dynamic loading of wpcap.dll; + - corrected exceed max index bug in ethX lookup + - corrected failure to look up ethernet device names in + the registry on Windows XP x64 + + sim_timer.c: + - fixed idle timer event selection algorithm + + h316_lp.c: + - fixed loss of last print line (from Theo Engel) + + h316_mt.c: + - fixed bug in write without stop (from Theo Engel) + + h316_stddev.c: + - fixed bug in clock increment (from Theo Engel) + + i1401_cpu.c: + - added recognition of overlapped operation modifiers + - remove restriction on load-mode binary tape operations + + i1401_mt.c: + - fixed read tape mark operation (found by Van Snyder) + - remove restriction on load-mode binary tape operations + + pdp1_cpu.c: + - fixed typo in SBS clear (from Norm Lastovica) + + pdp11_rh.c, pdp11_rp.c, pdp11_tu.c: + - CS1 DVA is in the device, not the MBA + + pdp8_ct.c: + - fixed typo (from Norm Lastovica) + + vax_cpu.c: + - revised idle detector + + 1 14-May-07 scp.c: + - modified sim_instr invocation to call sim_rtcn_init_all + - fixed bug in get_sim_opt (reported by Don North) + - fixed bug in RESTORE with changed memory size + - added global 'RESTORE in progress' flag + - fixed breakpoint actions in DO command file processing + (from Dave Bryan) + + all CPU's with clocks: + - removed clock initialization (now done in SCP) + + hp2100_cpu.c (from Dave Bryan): + - EDT passes input flag and DMA channel in dat parameter + + hp2100_ipl.c (from Dave Bryan): + - IPLI EDT delays DMA completion interrupt for TSB + + hp2100_mux.c (from Dave Bryan): + - corrected "mux_sta" size from 16 to 21 elements + - fixed "muxc_reset" to clear lines 16-20 + - fixed control card OTx to set current channel number + - fixed to set "muxl_ibuf" in response to a transmit interrupt + - changed "mux_xbuf", "mux_rbuf" declarations from 8 to 16 bits + - fixed to set "mux_rchp" when a line break is received + - fixed incorrect "odd_par" table values + - reversed test in "RCV_PAR" to return "LIL_PAR" on odd parity + - rixed mux reset (ioCRS) to clear port parameters + - fixed to use PUT_DCH instead of PUT_CCH for data channel status + - added DIAG/TERM modifiers to implement diagnostic mode + + pdp11_cpumod.c: + - changed memory size routine to work with RESTORE + + pdp11_hk.c: + - NOP and DCLR (at least) do not check drive type + - MR2 and MR3 only updated on NOP + + pdp10_tu.c, pdp11_tu.c: + - TMK sets FCE only on read (found by Naoki Hamada) + + pdp11_xu.c: + - added missing FC_RMAL command + - cleared multicast on write + + vax_moddefs.h, vax_cpu1.c: + - separated PxBR and SBR mbz checks + + vax780_defs.h + - separated PxBR and SBR mbz checks + - modified mbz checks to reflect 780 microcode patches + (found by Naoki Hamada) + + vax_mmu.c: + - added address masking to all SBR-based memory reads + + 0 30-Jan-07 scp.c: + - implemented throttle commands + - added -e to control error processing in DO command files + (from Dave Bryan) + + sim_console.c: + - fixed handling of non-printable characters in KSR mode + + sim_tape.c: + - fixed bug in reverse operations for P7B-format tapes + - fixed bug in reverse operations across erase gaps + + sim_timer.c: + - added throttle support + - added idle support (based on work by Mark Pizzolato) + + gri_stddev.c, h316_stddev.c, pdp18b_tt1.c + - fixed handling of non-printable characters in KSR mode + + hp2100_cpu.c, hp2100_cpu0.c, hp2100_cpu1.c, hp2100_cpu2.c, + hp2100_cpu3.c, hp2100_cpu4.c (from Dave Bryan): + - reorganized CPU modules for easier addition of new instructions + - added Double Integer instructions, 1000-F CPU, 2114 and + 2115 CPUs, 12K and 24K memory sizes, 12607B and 12578A DMA + controllers, and 21xx binary loader protection + - fixed DMS self-test instruction execution on 1000-M + - fixed indirect interrupt holdoff logic + + hp2100_ds.c: + - fixed REQUEST STATUS to clear status-1 (from Dave Bryan) + + hp2100_fp1.c: + - Added Floating Point Processor (from Dave Bryan) + + hp2100_lps.c: + - fixed diag-mode CLC response + + i7094_cpu.c: + - fixed new bug in halt IO wait loop + - added IFT, EFT expanded core test instructions + + id16_cpu.c, id32_cpu.c: + - removed separate multiplexor clock + - added idle support + + id_pas.c: + - synced multiplexor poll to real-time clock + + id_tt.c, id_ttp.c: + - fixed handling of non-printable characters in KSR mode + - synced keyboard poll to real-time clock + + id_uvc.c: + - changed line-time clock to be free-running + + pdp1_cpu.c: + - added 16-channel sequence break system (API) support + - added PDP-1D support + + pdp1_clk.c: + - first release + + pdp1_dcs.c: + - first release + + pdp1_stddev.c: + - separated TTI, TTO for API support + + pdp1_sys.c: + - added PDP-1D, 16-channel SBS, clock, DCS support + - fixed bugs in character input, block loader + + pdp10_cpu.c: + - added idle support + + pdp10_defs.h, pdp10_sys.c: + - added CR support + + pdp10_fe.c, pdp10_tim.c: + - synced keyboard poll to real-time clock + + pdp11_cr.c: + - revised for PDP-10 compatibility (CD11 only) + + pdp11_cpu.c: + - added idle support + - fixed bug in ASH -32 C value + + pdp11_rf.c: + - fixed unit mask (found by John Dundas) + + pdp11_stddev.c, vax_stddev.c, vax780_stddev.c: + - synced keyboard poll to real-time clock + - added clock coscheduling support + + pdp11_ta.c: + - first release + + pdp11_vh.c: + - synced service poll to real-time clock + - changed device to be off by default + + pdp11_dz.c, pdp11_xq.c, pdp11_xu.c: + - synced service poll to real-time clock + + pdp11_sys.c: + - fixed operand order in EIS instructions (found by W.F.J. Mueller) + - added TA11 support + + pdp18b_cpu.c: + - fixed incorrect value of PC on instruction fetch mem mmgt error + - fixed PDP-15 handling of mem mmgt traps (sets API 3) + - fixed PDP-15 handling of CAL API 4 (sets only if 0-3 inactive) + - fixed PDP-15 CAF to clear memory management mode register + - fixed boundary test in KT15/XVM (reported by Andrew Warkentin) + - added XVM RDCLK instruction + - added idle support and infinite loop detection + + pdp18b_rf.c: + - fixed bug, DSCD does not clear function register + + pdp18b_stddev.c: + - added PDP-15 program-selectable duplex handling instruction + - fixed PDP-15 handling of reader out-of-tape + - fixed handling of non-printable characters in KSR mode + - added XVM RDCLK instruction + - changed real-time clock to be free running + - synced keyboard poll to real-time clock + + pdp18b_tt1.c + - fixed handling of non-printable characters in KSR mode + + pdp18b_sys.c: + - added XVM RDCLK instruction + + pdp8_cpu.c: + - fixed SC value after DVI overflow (found by Don North) + - added idle support and infinite loop detection + + pdp8_ct.c: + - first release + + pdp8_clk.c: + - changed real-time clock to be free running + + pdp8_sys.c: + - added TA8E support + - added ability to disambiguate overlapping IOT definitions + + pdp8_tt.c: + - fixed handling of non-printable characters in KSR mode + - synced keyboard poll to real-time clock + + vax_cpu.c, vax_cpu1.c: + - added idle support + + vax_syscm.c: + - fixed operand order in EIS instructions (found by W.F.J. Mueller) + + +/* V3.6 revision history + + 1 25-Jul-06 sim_console.c: + - implemented SET/SHOW PCHAR + + all DECtapes: + - fixed conflict in ATTACH switches + + hp2100_ms.c (from Dave Bryan): + - added CAPACITY as alternate for REEL + - fixed EOT test for unlimited reel size + + i1620_cd.c (from Tom McBride): + - fixed card reader fgets call + - fixed card reader boot sequence + + i7094_cd.c: + - fixed problem with 80 column full cards + + i7094_cpu.c: + - fixed bug in halt IO wait loop + + i7094_sys.c: + - added binary loader (courtesy of Dave Pitt) + + pdp1_cpu.c: + - fixed bugs in MUS and DIV + + pdp11_cis.c: + - added interrupt tests to character instructions + - added 11/44 stack probe test to MOVCx (only) + + pdp11_dl.c: + - first release + + pdp11_rf.c: + - first release + + pdp11_stddev.c: + - added UC support to TTI, TTO + + pdp18b_cpu.c: + - fixed RESET to clear AC, L, and MQ + + pdp18b_dt.c: + - fixed checksum calculation bug for Type 550 + + pdp18b_fpp.c: + - fixed bugs in left shift, multiply + + pdp18b_stddev.c: + - fixed Baudot letters/figures inversion for PDP-4 + - fixed letters/figures tracking for PDP-4 + - fixed PDP-4/PDP-7 default terminal to be local echo + + pdp18b_sys.c: + - added Fiodec, Baudot display + - generalized LOAD to handle HRI, RIM, and BIN files + + pdp8_ttx.c: + - fixed bug in DETACH routine + + 0 15-May-06 scp.c: + - revised save file format to save options, unit capacity + + sim_tape.c, sim_tape.h: + - added support for finite reel size + - fixed bug in P7B write record + + most magtapes: + - added support for finite reel size + + h316_cpu.c: fixed bugs in LLL, LRL (found by Theo Engel) + + h316_lp.c: fixed bug in blanks backscanning (found by Theo Engel) + + h316_stddev.c: fixed bugs in punch state handling (found by Theo Engel) + + i1401_cpu.c: fixed bug in divide (reported by Van Snyder) + + i16_cpu.c: fixed bug in DH (found by Mark Hittinger) + + i32_cpu.c: + - fixed bug in DH (found by Mark Hittinger) + - added support for 8 register banks in 8/32 + + i7094: first release + + id_io.c: fixed bug, GO preserves EXA and SSTA (found by Davis Johnson) + + id_idc.c: + - fixed WD/WH handling (found by Davis Johnson) + - fixed bug, nop command should be ignored (found by Davis Johnson) + + nova_cpu.c: fixed bug in DIVS (found by Mark Hittinger) + + pdp11_cis.c: (all reported by John Dundas) + - fixed bug in decode table + - fixed bug in ASHP + - fixed bug in write decimal string with mmgt enabled + - fixed bug in 0-length strings in multiply/divide + + pdp11_cpu.c: fixed order of operand fetching in XOR for SDSD models + + pdp11_cr.c: added CR11/CD11 support + + pdp11_tc.c: + - fixed READ to set extended data bits in TCST (found by Alan Frisbie) + + vax780_fload.c: added FLOAD command + + vax780_sbi.c: fixed writes to ACCS + + vax780_stddev.c: revised timer logic for EVKAE (reported by Tim Stark) + + vax_cis.c: (all reported by Tim Stark) + - fixed MOVTC, MOVTUC to preserve cc's through page faults + - fixed MOVTUC to stop on translated == escape + - fixed CVTPL to set registers before destination reg write + - fixed CVTPL to set correct cc bit on overflow + - fixed EDITPC to preserve cc's through page faults + - fixed EDITPC EO$BLANK_ZERO count, cc test + - fixed EDITPC EO$INSERT to insert fill instead of blank + - fixed EDITPC EO$LOAD_PLUS/MINUS to skip character + + vax_cpu.c: + - added KESU capability to virtual examine + - fixed bugs in virtual examine + - rewrote CPU history function for improved usability + (bugs below reported by Tim Stark) + - fixed fault cleanup to clear PSL + - fixed ADAWI r-mode to preserve dst<31:16> + - fixed ACBD/G to test correct operand + - fixed access checking on modify-class specifiers + - fixed branch address calculation in CPU history + - fixed bug in reported VA on faulting cross-page write + + vax_cpu1.c: (all reported by Tim Stark) + - added access check on system PTE for 11/780 + - added mbz check in LDPCTX for 11/780 + + vax_cmode.c: (all reported by Tim Stark) + - fixed omission of SXT + - fixed order of operand fetching in XOR + + vax_fpa.c: (all reported by Tim Stark) + - fixed POLYD, POLYG to clear R4, R5 + - fixed POLYD, POLYG to set R3 correctly + - fixed POLYD, POLYG to not exit prematurely if arg = 0 + - fixed POLYD, POLYG to do full 64b multiply + - fixed POLYF, POLYD, POLYG to remove truncation on add + - fixed POLYF, POLYD, POLYG to mask mul reslt to 31b/63b/63b + - fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYF, POLYD, POLYG + - fixed bug in 32b floating multiply routine + - fixed bug in 64b extended modulus routine + + vax_mmu.c: + - added access check on system PTE for 11/780 + + vax_octa.c: (all reported by Tim Stark) + - fixed MNEGH to test negated sign, clear C + - fixed carry propagation in qp_inc, qp_neg, qp_add + - fixed pack routines to test for zero via fraction + - fixed ACBH to set cc's on result + - fixed POLYH to set R3 correctly + - fixed POLYH to not exit prematurely if arg = 0 + - fixed POLYH to mask mul reslt to 127b + - fixed fp add routine to test for zero via fraction + to support "denormal" argument from POLYH + - fixed EMODH to concatenate 15b of 16b extension + - fixed bug in reported VA on faulting cross-page write + + +/* V3.5 revision history + +patch date module(s) and fix(es) + + 2 07-Jan-06 scp.c: + - added breakpoint spaces + - added REG_FIT support + + sim_console.c: added ASCII character processing routines + + sim_tape.c, sim_tape.h: + - added write support for P7B format + - fixed bug in write forward (found by Dave Bryan) + + h316_stddev.c, hp2100_stddev.c, hp2100_mux.c, id_tt.c, + id_ttp.c, id_pas.c, pdp8_tt.c, pdp8_ttx.c, pdp11_stddev.c, + pdp11_dz.c, pdp18b_stddev.c, pdp18b_tt1.c, vax_stddev, + gri_stddev.c: + - revised to support new character handling routines + + pdp10_rp.c: fixed DCLR not to clear disk address + + pdp11_hk.c: fixed overlapped seek interaction with NOP, etc + + pdp11_rh.c: added enable/disable routine + + pdp11_rq.c, pdp11_tm.c, pdp11_tq.c, pdp11_ts.c + - widened address display to 64b when USE_ADDR64 + + pdp11_rp.c: + - fixed DCLR not to clear disk address + - fixed device enable/disable logic to include Massbus adapter + - widened address display to 64b when USE_ADDR64 + + pdp11_tu.c: + - fixed device enable/disable logic to include Massbus adapter + - widened address display to 64b when USE_ADDR64 + - changed default adapter to TM03 (for VMS) + + pdp8_df.c, pdp8_dt.c, pdp8_rf.c: + - fixed unaligned access bug (found by Doug Carman) + + pdp8_rl.c: fixed IOT 61 decoding bug (found by David Gesswein) + + vax_cpu.c: + - fixed breakpoint detection when USE_ADDR64 option is active + - fixed CVTfi to trap on integer overflow if PSW set + + 1 15-Oct-05 All CPU's, other sources: fixed declaration inconsistencies + (from Sterling Garwood) + + i1401_cpu.c: added control for old/new character encodings + + i1401_cd.c, i1401_lpt.c, i1401_tty.c: + - changed character encodings to be consistent with 7094 + - changed column binary format to be consistent with 7094 + - added choice of business or Fortran set for output encoding + + i1401_sys.c: changed WM character to ` under new encodings + + i1620_cd.c, i1620_lpt.c, i1620_tty.c: + - changed character encodings to be consistent with 7094 + + pdp10_cpu.c: changed MOVNI to eliminate gcc warning + + pdp11_io.c: fixed bug in autoconfiguration (missing XU) + + vax_io.c: fixed bug in autoconfiguration (missing XU) + + vax_fpa.c: fixed bug in 32b structure definitions (from Jason Stevens) + + 0 1-Sep-05 Note: most source modules have been edited to improve + readability and to fix declaration and cast problems in C++ + + all instruction histories: fixed reversed arguments to calloc + + scp.c: revised to trim trailing spaces on file inputs + + sim_sock.c: fixed SIGPIPE error on Unix + + sim_ether.c: added Windows user-defined adapter names (from Timothe Litt) + + sim_tape.c: fixed misallocation of TPC map array + + sim_tmxr.c: added support for SET DISCONNECT + + hp2100_mux.c: added SET MUXLn DISCONNECT + + i1401_cpu.c: + - fixed SSB-SSG clearing on RESET (reported by Ralph Reinke) + - removed error stops in MCE + + i1401_cd.c: fixed read, punch to ignore modifier on 1, 4 char inst + (reported by Van Snyder) + + id_pas.c: + - fixed bug in SHOW CONN/STATS + - added SET PASLn DISCONNECT + + pdp10_ksio.c: revised for new autoconfiguration interface + + pdp11_cpu.c: replaced WAIT clock queue check with API call + + pdp11_cpumod.c: added additional 11/60 registers + + pdp11_io.c: revised autoconfiguration algorithm and interface + + pdp11_dz.c: revised for new autoconfiguration interface + + pdp11_vh.c: + - revised for new autoconfiguration interface + - fixed bug in vector display routine + + pdp11_xu.c: fixed runt packet processing (found by Tim Chapman) + + pdp18b_cpu.c, pdp18b_sys.c: + - removed spurious AAS instruction + + pdp18b_tt1.c: + - fixed bug in SHOW CONN/STATS + - fixed bug in SET LOG/NOLOG + - added SET TTOXn DISCONNECT + + pdp8_ttx.c: + - fixed bug in SHOW CONN/STATS + - fixed bug in SET LOG/NOLOG + - added SET TTOXn DISCONNECT + + sds_mux.c: + - fixed bug in SHOW CONN/STATS + - added SET MUXLn DISCONNECT + + vaxmod_defs.h: added QDSS support + + vax_io.c: revised autoconfiguration algorithm and interface + +/* V3.4 revision history + + 0 01-May-04 scp.c: + - fixed ASSERT code + - revised syntax for SET DEBUG (from Dave Bryan) + - revised interpretation of fprint_sym, fparse_sym returns + - moved DETACH sanity tests into detach_unit + + sim_sock.h and sim_sock.c: + - added test for WSAEINPROGRESS (from Tim Riker) + + many: revised detach routines to test for attached state + + hp2100_cpu.c: reorganized CPU options (from Dave Bryan) + + hp2100_cpu1.c: reorganized EIG routines (from Dave Bryan) + + hp2100_fp1.c: added FFP support (from Dave Bryan) + + id16_cpu.c: + - fixed bug in show history routine (from Mark Hittinger) + - revised examine/deposit to do words rather than bytes + + id32_cpu.c: + - fixed bug in initial memory allocation + - fixed bug in show history routine (from Mark Hittinger) + - revised examine/deposit to do words rather than bytes + + id16_sys.c, id32_sys: + - revised examine/deposit to do words rather than bytes + + pdp10_tu.c: + - fixed bug, ERASE and WREOF should not clear done (reported + by Rich Alderson) + - fixed error reporting + + pdp11_tu.c: fixed error reporting + +/* V3.3 revision history + + 2 08-Mar-05 scp.c: added ASSERT command (from Dave Bryan) + + h316_defs.h: fixed IORETURN macro + + h316_mt.c: fixed error reporting from OCP (found by Philipp Hachtmann) + + h316_stddev.c: fixed bug in OCP '0001 (found by Philipp Hachtmann) + + hp2100_cpu.c: split out EAU and MAC instructions + + hp2100_cpu1.c: (from Dave Bryan) + - fixed missing MPCK on JRS target + - removed EXECUTE instruction (is NOP in actual microcode) + + hp2100_fp: (from Dave Bryan) + - fixed missing negative overflow renorm in StoreFP + + i1401_lp.c: fixed bug in write_line (reported by Van Snyder) + + id32_cpu.c: fixed branches to mask new PC (from Greg Johnson) + + pdp11_cpu.c: fixed bugs in RESET for 11/70 (reported by Tim Chapman) + + pdp11_cpumod.c: + - fixed bug in SHOW MODEL (from Sergey Okhapkin) + - made SYSID variable for 11/70 (from Tim Chapman) + - added MBRK write case for 11/70 (from Tim Chapman) + + pdp11_rq: added RA60, RA71, RA81 disks + + pdp11_ry: fixed bug in boot code (reported by Graham Toal) + + vax_cpu.c: fixed initial state of cpu_extmem + + 1 05-Jan-05 h316_cpu.c: fixed bug in DIV + + h316_stddev.c: + - fixed bug in SKS '104 (reported by Philipp Hachtmann) + - fixed bug in SKS '504 + - adder reader/punch ASCII file support + - added Teletype reader/punch support + + h316_dp.c: fixed bug in skip on !seeking + + h316_mt.c: fixed bug in DMA/DMC support + + h316_lp.c: fixed bug in DMA/DMC support + + hp2100_cpu.c: + - fixed DMA reset to clear alternate CTL flop (from Dave Bryan) + - fixed DMA reset to not clear control words (from Dave Bryan) + - fixed SBS, CBS, TBS to do virtual reads + - separated A/B from M[0/1], for DMA IO (from Dave Bryan) + - added SET CPU 21MX-M, 21MX-E (from Dave Brian) + - disabled TIMER/EXECUTE/DIAG instructions for 21MX-M (from Dave Bryan) + - added post-processor to maintain T/M consistency (from Dave Bryan) + + hp2100_ds.c: first release + + hp2100_lps.c (all changes from Dave Bryan) + - added restart when set online, etc. + - fixed col count for non-printing chars + + hp2100_lpt.c (all changes from Dave Bryan) + - added restart when set online, etc. + + hp2100_sys.c (all changes from Dave Bryan): + - added STOP_OFFLINE, STOP_PWROFF messages + + i1401_sys.c: added address argument support (from Van Snyder) + + id_mt.c: added read-only file support + + lgp_cpu.c, lgp_sys.c: modified VM pointer setup + + pdp11_cpu.c: fixed WAIT to work in all modes (from John Dundas) + + pdp11_tm.c, pdp11_ts.c: added read-only file support + + sds_mt.c: added read-only file support + + 0 23-Nov-04 scp.c: + - added reset_all_p (powerup) + - fixed comma-separated SET options (from Dave Bryan) + - changed ONLINE/OFFLINE to ENABLED/DISABLED (from Dave Bryan) + - modified to flush device buffers on stop (from Dave Bryan) + - changed HELP to suppress duplicate command displays + + sim_console.c: + - moved SET/SHOW DEBUG under CONSOLE hierarchy + + hp2100_cpu.c: (all fixes by Dave Bryan) + - moved MP into its own device; added MP option jumpers + - modified DMA to allow disabling + - modified SET CPU 2100/2116 to truncate memory > 32K + - added -F switch to SET CPU to force memory truncation + - fixed S-register behavior on 2116 + - fixed LIx/MIx behavior for DMA on 2116 and 2100 + - fixed LIx/MIx behavior for empty I/O card slots + - modified WRU to be REG_HRO + - added BRK and DEL to save console settings + - fixed use of "unsigned int16" in cpu_reset + + hp2100_dp.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + - fixed ANY ERROR status for 12557A interface + - fixed unattached drive status for 12557A interface + - status cmd without prior STC DC now completes (12557A) + - OTA/OTB CC on 13210A interface also does CLC CC + - fixed RAR model + - fixed seek check on 13210 if sector out of range + + hp2100_dq.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + - shortened xtime from 5 to 3 (drive avg 156KW/second) + - fixed not ready/any error status + - fixed RAR model + + hp2100_dr.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + - fixed sector return in status word + - provided protected tracks and "Writing Enabled" status bit + - fixed DMA last word write, incomplete sector fill value + - added "parity error" status return on writes for 12606 + - added track origin test for 12606 + - added SCP test for 12606 + - fixed 12610 SFC operation + - added "Sector Flag" status bit + - added "Read Inhibit" status bit for 12606 + - fixed current-sector determination + - added TRACKPROT modifier + + hp2100_ipl.c, hp2100_ms.c: (all fixes by Dave Bryan) + - fixed enable/disable from either device + + hp2100_lps.c: (all fixes by Dave Bryan) + - added SET OFFLINE/ONLINE, POWEROFF/POWERON + - fixed status returns for error conditions + - fixed handling of non-printing characters + - fixed handling of characters after column 80 + - improved timing model accuracy for RTE + - added fast/realistic timing + - added debug printouts + + hp2100_lpt.c: (all fixes by Dave Bryan) + - added SET OFFLINE/ONLINE, POWEROFF/POWERON + - fixed status returns for error conditions + - fixed TOF handling so form remains on line 0 + + hp2100_stddev.c (all fixes by Dave Bryan) + - added paper tape loop mode, DIAG/READER modifiers to PTR + - added PV_LEFT to PTR TRLLIM register + - modified CLK to permit disable + + hp2100_sys.c: (all fixes by Dave Bryan) + - added memory protect device + - fixed display of CCA/CCB/CCE instructions + + i1401_cpu.c: added =n to SHOW HISTORY + + id16_cpu.c: added instruction history + + id32_cpu.c: added =n to SHOW HISTORY + + pdp10_defs.h: revised Unibus DMA API's + + pdp10_ksio.c: revised Unibus DMA API's + + pdp10_lp20.c: revised Unibus DMA API's + + pdp10_rp.c: replicated register state per drive + + pdp10_tu.c: + - fixed to set FCE on short record + - fixed to return bit<15> in drive type + - fixed format specification, 1:0 are don't cares + - implemented write check + - TMK is cleared by new motion command, not DCLR + - DONE is set on data transfers, ATA on non data transfers + + pdp11_defs.h: + - revised Unibus/Qbus DMA API's + - added CPU type and options flags + + pdp11_cpumod.h, pdp11_cpumod.c: + - new routines for setting CPU type and options + + pdp11_io.c: revised Unibus/Qbus DMA API's + + all PDP-11 DMA peripherals: + - revised Unibus/Qbus DMA API's + + pdp11_hk.c: CS2 OR must be zero for M+ + + pdp11_rh.c, pdp11_rp.c, pdp11_tu.c: + - split Massbus adapter from controllers + - replicated RP register state per drive + - added TM02/TM03 with TE16/TU45/TU77 drives + + pdp11_rq.c, pdp11_tq.c: + - provided different default timing for PDP-11, VAX + - revised to report CPU bus type in stage 1 + - revised to report controller type reflecting bus type + - added -L switch (LBNs) to RAUSER size specification + + pdp15_cpu.c: added =n to SHOW HISTORY + + pdp15_fpp.c: + - fixed URFST to mask low 9b of fraction + - fixed exception PC setting + + pdp8_cpu.c: added =n to SHOW HISTORY + + vax_defs.h: + - added octaword, compatibility mode support + + vax_moddefs.h: + - revised Unibus/Qbus DMA API's + + vax_cpu.c: + - moved processor-specific code to vax_sysdev.c + - added =n to SHOW HISTORY + + vax_cpu1.c: + - moved processor-specific IPR's to vax_sysdev.c + - moved emulation trap to vax_cis.c + - added support for compatibility mode + + vax_cis.c: new full VAX CIS instruction emulator + + vax_octa.c: new full VAX octaword and h_floating instruction emulator + + vax_cmode.c: new full VAX compatibility mode instruction emulator + + vax_io.c: + - revised Unibus/Qbus DMA API's + + vax_io.c, vax_stddev.c, vax_sysdev.c: + - integrated powerup into RESET (with -p) + + vax_sys.c: + - fixed bugs in parsing indirect displacement modes + - fixed bugs in displaying and parsing character data + + vax_syscm.c: added display and parse for compatibility mode + + vax_syslist.c: + - split from vax_sys.c + - removed PTR, PTP + +/* V3.2 revision history + + 3 03-Sep-04 scp.c: + - added ECHO command (from Dave Bryan) + - qualified RESTORE detach with SIM_SW_REST + + sim_console: added OS/2 EMX fixes (from Holger Veit) + + sim_sock.h: added missing definition for OS/2 (from Holger Veit) + + hp2100_cpu.c: changed error stops to report PC not PC + 1 + (from Dave Bryan) + + hp2100_dp.c: functional and timing fixes (from Dave Bryan) + - controller sets ATN for all commands except read status + - controller resumes polling for ATN interrupts after read status + - check status on unattached drive set busy and not ready + - check status tests wrong unit for write protect status + - drive on line sets ATN, will set FLG if polling + + hp2100_dr.c: fixed CLC to stop operation (from Dave Bryan) + + hp2100_ms.c: functional and timing fixes (from Dave Bryan) + - fixed erroneous execution of rejected command + - fixed erroneous execution of select-only command + - fixed erroneous execution of clear command + - fixed odd byte handling for read + - fixed spurious odd byte status on 13183A EOF + - modified handling of end of medium + - added detailed timing, with fast and realistic modes + - added reel sizes to simulate end of tape + - added debug printouts + + hp2100_mt.c: modified handling of end of medium (from Dave Bryan) + + hp2100_stddev.c: added tab to control char set (from Dave Bryan) + + pdp11_rq.c: VAX controllers luns start at 0 (from Andreas Cejna) + + vax_cpu.c: fixed bug in EMODD/G, second word of quad dst not probed + + 2 17-Jul-04 scp.c: fixed problem ATTACHing to read only files + (found by John Dundas) + + sim_console.c: revised Windows console code (from Dave Bryan) + + sim_fio.c: fixed problem in big-endian read + (reported by Scott Bailey) + + gri_cpu.c: updated MSR, EAO functions + + hp_stddev.c: generalized handling of control char echoing + (from Dave Bryan) + + vax_sys.c: fixed bad block initialization routine + + 1 10-Jul-04 scp.c: added SET/SHOW CONSOLE subhierarchy + + hp2100_cpu.c: fixes and added features (from Dave Bryan) + - SBT increments B after store + - DMS console map must check dms_enb + - SFS x,C and SFC x,C work + - MP violation clears automatically on interrupt + - SFS/SFC 5 is not gated by protection enabled + - DMS enable does not disable mem prot checks + - DMS status inconsistent at simulator halt + - Examine/deposit are checking wrong addresses + - Physical addresses are 20b not 15b + - Revised DMS to use memory rather than internal format + - Added instruction printout to HALT message + - Added M and T internal registers + - Added N, S, and U breakpoints + Revised IBL facility to conform to microcode + Added DMA EDT I/O pseudo-opcode + Separated DMA SRQ (service request) from FLG + + all HP2100 peripherals: + - revised to make SFS x,C and SFC x,C work + - revised to separate SRQ from FLG + + all HP2100 IBL bootable peripherals: + - revised boot ROMs to use IBL facility + - revised SR values to preserve SR<5:3> + + hp2100_lps.c, hp2100_lpt.c: fixed timing + + hp2100_dp.c: fixed interpretation of SR<0> + + hp2100_dr.c: revised boot code to use IBL algorithm + + hp2100_mt.c, hp2100_ms.c: fixed spurious timing error after CLC + (found by Dave Bryan) + + hp2100_stddev.c: + - fixed input behavior during typeout for RTE-IV + - suppressed nulls on TTY output for RTE-IV + + hp2100_sys.c: added SFS x,C and SFC x,C to print/parse routines + + pdp10_fe.c, pdp11_stddev.c, pdp18b_stddev.c, pdp8_tt.c, vax_stddev.c: + - removed SET TTI CTRL-C option + + pdp11_tq.c: + - fixed bug in reporting write protect (reported by Lyle Bickley) + - fixed TK70 model number and media ID (found by Robert Schaffrath) + + pdp11_vh.c: added DHQ11 support (from John Dundas) + + pdp11_io.c, vax_io.c: fixed DHQ11 autoconfigure (from John Dundas) + + pdp11_sys.c, vax_sys.c: added DHQ11 support (from John Dundas) + + vax_cpu.c: fixed bug in DIVBx, DIVWx (reported by Peter Trimmel) + + 0 04-Apr-04 scp.c: + - added sim_vm_parse_addr and sim_vm_fprint_addr + - added REG_VMAD + - moved console logging to SCP + - changed sim_fsize to use descriptor rather than name + - added global device/unit show modifiers + - added device debug support (Dave Hittner) + - moved device and unit flags, updated save format + + sim_ether.c: + - further generalizations (Dave Hittner, Mark Pizzolato) + + sim_tmxr.h, sim_tmxr.c: + - added tmxr_linemsg + - changed TMXR definition to support variable number of lines + + sim_libraries: + - new console library (sim_console.h, sim_console.c) + - new file I/O library (sim_fio.h, sim_fio.c) + - new timer library (sim_timer.h, sim_timer.c) + + all terminal multiplexors: revised for tmxr library changes + + all DECtapes: + - added STOP_EOR to enable end-of-reel stop + - revised for device debug support + + all variable-sized devices: revised for sim_fsize change + + eclipse_cpu.c, nova_cpu.c: fixed device enable/disable support + (found by Bruce Ray) + + nova_defs.h, nova_sys.c, nova_qty.c: + - added QTY and ALM support (Bruce Ray) + + id32_cpu.c, id_dp.c: revised for device debug support + + lgp: added LGP-30 [LGP-21] simulator + + pdp1_sys.c: fixed bug in LOAD (found by Mark Crispin) + + pdp10_mdfp.c: + - fixed bug in floating unpack + - fixed bug in FIXR (found by Philip Stone, fixed by Chris Smith) + + pdp11_dz.c: added per-line logging + + pdp11_rk.c: + - added formatting support + - added address increment inhibit support + - added transfer overrun detection + + pdp11_hk.c, pdp11_rp.c: revised for device debug support + + pdp11_rq.c: fixed bug in interrupt control (found by Tom Evans) + + pdp11_ry.c: added VAX support + + pdp11_tm.c, pdp11_tq.c, pdp11_ts.c: revised for device debug support + + pdp11_xu.c: replaced stub with real implementation (Dave Hittner) + + pdp18b_cpu.c: + - fixed bug in XVM g_mode implementation + - fixed bug in PDP-15 indexed address calculation + - fixed bug in PDP-15 autoindexed address calculation + + pdp18b_fpp.c: fixed bugs in instruction decode + + pdp18b_stddev.c: + - fixed clock response to CAF + - fixed bug in hardware read-in mode bootstrap + + pdp18b_sys.c: fixed XVM instruction decoding errors + + pdp18b_tt1.c: added support for 1-16 additional terminals + + vax_moddef.h, vax_cpu.c, vax_sysdev.c: + - added extended physical memory support (Mark Pizzolato) + - added RXV21 support + + vax_cpu1.c: + - added PC read fault in EXTxV + - fixed PC write fault in INSV + +/* V3.1 revision history + + 0 29-Dec-03 sim_defs.h, scp.c: added output stall status + + all console emulators: added output stall support + + sim_ether.c (Dave Hittner, Mark Pizzolato, Anders Ahgren): + - added Alpha/VMS support + - added FreeBSD, Mac OS/X support + - added TUN/TAP support + - added DECnet duplicate address detection + + all memory buffered devices (fixed head disks, floppy disks): + - cleaned up buffer copy code + + all DECtapes: + - fixed reverse checksum in read all + - added DECtape off reel message + - simplified timing + + eclipse_cpu.c (Charles Owen): + - added floating point support + - added programmable interval timer support + - bug fixes + + h316_cpu.c: + - added instruction history + - added DMA/DMC support + - added device ENABLE/DISABLE support + - change default to HSA option included + + h316_dp.c: added moving head disk support + + h316_fhd.c: added fixed head disk support + + h316_mt.c: added magtape support + + h316_sys.c: added new device support + + nova_dkp.c (Charles Owen): + - fixed bug in flag clear sequence + - added diagnostic mode support for disk sizing + +` nova_mt.c (Charles Owen): + - fixed bug, space operations return record count + - fixed bug, reset doesn't cancel rewind + + nova_sys.c: added floating point, timer support (from Charles Owen) + + i1620_cpu.c: fixed bug in branch digit (found by Dave Babcock) + + pdp1_drm.c: + - added parallel drum support + - fixed bug in serial drum instructin decoding + + pdp1_sys.c: added parallel drum support, mnemonics + + pdp11_cpu.c: + - added autoconfiguration controls + - added support for 18b-only Qbus devices + - cleaned up addressing/bus definitions + + pdp11_rk.c, pdp11_ry.c, pdp11_tm.c, pdp11_hk.c: + - added Q18 attribute + + pdp11_io.c: + - added autoconfiguration controls + - fixed bug in I/O configuration (found by Dave Hittner) + + pdp11_rq.c: + - revised MB->LBN conversion for greater accuracy + - fixed bug with multiple RAUSER drives + + pdp11_tc.c: changed to be off by default (base config is Qbus) + + pdp11_xq.c (Dave Hittner, Mark Pizzolato): + - fixed second controller interrupts + - fixed bugs in multicast and promiscuous setup + + pdp18b_cpu.c: + - added instruction history + - fixed PDP-4,-7,-9 autoincrement bug + - change PDP-7,-9 default to API option included + + pdp8_defs.h, pdp8_sys.c: + - added DECtape off reel message + - added support for TSC8-75 (ETOS) option + - added support for TD8E controller + + pdp8_cpu.c: added instruction history + + pdp8_rx.c: + - fixed bug in RX28 read status (found by Charles Dickman) + - fixed double density write + + pdp8_td.c: added TD8E controller + + pdp8_tsc.c: added TSC8-75 option + + vax_cpu.c: + - revised instruction history for dynamic sizing + - added autoconfiguration controls + + vax_io.c: + - added autoconfiguration controls + - fixed bug in I/O configuration (found by Dave Hittner) + + id16_cpu.c: revised instruction decoding + + id32_cpu.c: + - revised instruction decoding + - added instruction history + +/* V3.0 revision history + + 2 15-Sep-03 scp.c: + - fixed end-of-file problem in dep, idep + - fixed error on trailing spaces in dep, idep + + pdp1_stddev.c + - fixed system hang if continue after PTR error + - added PTR start/stop functionality + - added address switch functionality to PTR BOOT + + pdp1_sys.c: added multibank capability to LOAD + + pdp18b_cpu.c: + - fixed priorities in PDP-15 API (PI between 3 and 4) + - fixed sign handling in PDP-15 unsigned mul/div + - fixed bug in CAF, must clear API subsystem + + i1401_mt.c: + - fixed tape read end-of-record handling based on real 1401 + - added diagnostic read (space forward) + + i1620_cpu.c + - fixed bug in immediate index add (found by Michael Short) + + 1 27-Jul-03 pdp1_cpu.c: updated to detect indefinite I/O wait + + pdp1_drm.c: fixed incorrect logical, missing activate, break + + pdp1_lp.c: + - fixed bugs in instruction decoding, overprinting + - updated to detect indefinite I/O wait + + pdp1_stddev.c: + - changed RIM loader to be "hardware" + - updated to detect indefinite I/O wait + + pdp1_sys.c: added block loader format support to LOAD + + pdp10_rp.c: fixed bug in read header + + pdp11_rq: fixed bug in user disk size (found by Chaskiel M Grundman) + + pdp18b_cpu.c: + - added FP15 support + - added XVM support + - added EAE support to the PDP-4 + - added PDP-15 "re-entrancy ECO" + - fixed memory protect/skip interaction + - fixed CAF to only reset peripherals + + pdp18b_fpp.c: added FP15 + + pdp18b_lp.c: fixed bug in Type 62 overprinting + + pdp18b_rf.c: fixed bug in set size routine + + pdp18b_stddev.c: + - increased PTP TIME for PDP-15 operating systems + - added hardware RIM loader for PDP-7, PDP-9, PDP-15 + + pdp18b_sys.c: added FP15, KT15, XVM instructions + + pdp8b_df.c, pdp8_rf.c: fixed bug in set size routine + + hp2100_dr.c: + - fixed drum sizes + - fixed variable capacity interaction with SAVE/RESTORE + + i1401_cpu.c: revised fetch to model hardware more closely + + ibm1130: fixed bugs found by APL 1130 + + nova_dsk.c: fixed bug in set size routine + + altairz80: fixed bug in real-time clock on Windows host + + 0 15-Jun-03 scp.c: + - added ASSIGN/DEASSIGN + - changed RESTORE to detach files + - added u5, u6 unit fields + - added USE_ADDR64 support + - changed some structure fields to unsigned + + scp_tty.c: added extended file seek + + sim_sock.c: fixed calling sequence in stubs + + sim_tape.c: + - added E11 and TPC format support + - added extended file support + + sim_tmxr.c: fixed bug in SHOW CONNECTIONS + + all magtapes: + - added multiformat support + - added extended file support + + i1401_cpu.c: + - fixed mnemonic, instruction lengths, and reverse + scan length check bug for MCS + - fixed MCE bug, BS off by 1 if zero suppress + - fixed chaining bug, D lost if return to SCP + - fixed H branch, branch occurs after continue + - added check for invalid 8 character MCW, LCA + + i1401_mt.c: fixed load-mode end of record response + + nova_dsk.c: fixed variable size interaction with restore + + pdp1_dt.c: fixed variable size interaction with restore + + pdp10_rp.c: fixed ordering bug in attach + + pdp11_cpu.c: + - fixed bug in MMR1 update (found by Tim Stark) + - fixed bug in memory size table + + pdp11_lp.c, pdp11_rq.c: added extended file support + + pdp11_rl.c, pdp11_rp.c, pdp11_ry.c: fixed ordering bug in attach + + pdp11_tc.c: fixed variable size interaction with restore + + pdp11_xq.c: + - corrected interrupts on IE state transition (code by Tom Evans) + - added interrupt clear on soft reset (first noted by Bob Supnik) + - removed interrupt when setting XL or RL (multiple people) + - added SET/SHOW XQ STATS + - added SHOW XQ FILTERS + - added ability to split received packet into multiple buffers + - added explicit runt & giant packet processing + + vax_fpa.c: + - fixed integer overflow bug in CVTfi + - fixed multiple bugs in EMODf + + vax_io.c: optimized byte and word DMA routines + + vax_sysdev.c: + - added calibrated delay to ROM reads (from Mark Pizzolato) + - fixed calibration problems in interval timer (from Mark Pizzolato) + + pdp1_dt.c: fixed variable size interaction with restore + + pdp18b_dt.c: fixed variable size interaction with restore + + pdp18b_mt.c: fixed bug in MTTR + + pdp18b_rf.c: fixed variable size interaction with restore + + pdp8_df.c, pdp8_rf.c: fixed variable size interaction + with restore + + pdp8_dt.c: fixed variable size interaction with restore + + pdp8_mt.c: fixed bug in SKTR + + hp2100_dp.c,hp2100_dq.c: + - fixed bug in read status (13210A controller) + - fixed bug in seek completion + + id_pt.c: fixed type declaration (found by Mark Pizzolato) + + gri_cpu.c: fixed bug in SC queue pointer management + +/* V2.10 revision history + + 4 03-Mar-03 scp.c + - added .ini startup file capability + - added multiple breakpoint actions + - added multiple switch evaluation points + - fixed bug in multiword deposits to file + + sim_tape.c: magtape simulation library + + h316_stddev.c: added set line frequency command + + hp2100_mt.c, hp2100_ms.c: revised to use magtape library + + i1401_mt.c: revised to use magtape library + + id_dp.c, id_idc.c: fixed cylinder overflow on writes + + id_mt.c: + - fixed error handling to stop selector channel + - revised to use magtape library + + id16_sys.c, id32_sys.c: added relative addressing support + + id_uvc.c: + - added set frequency command to line frequency clock + - improved calibration algorithm for precision clock + + nova_clk.c: added set line frequency command + + nova_dsk.c: fixed autosizing algorithm + + nova_mt.c: revised to use magtape library + + pdp10_tu.c: revised to use magtape library + + pdp11_cpu.c: fixed bug in MMR1 update (found by Tim Stark) + + pdp11_stddev.c + - added set line frequency command + - added set ctrl-c command + + pdp11_rq.c: + - fixed ordering problem in queue process + - fixed bug in vector calculation for VAXen + - added user defined drive support + + pdp11_ry.c: fixed autosizing algorithm + + pdp11_tm.c, pdp11_ts.c: revised to use magtape library + + pdp11_tq.c: + - fixed ordering problem in queue process + - fixed overly restrictive test for bad modifiers + - fixed bug in vector calculation for VAXen + - added variable controller, user defined drive support + - revised to use magtape library + + pdp18b_cpu.c: fixed three EAE bugs (found by Hans Pufal) + + pdp18b_mt.c: + - fixed bugs in BOT error handling, interrupt handling + - revised to use magtape library + + pdp18b_rf.c: + - removed 22nd bit from disk address + - fixed autosizing algorithm + + pdp18b_stddev.c: + - added set line frequency command + - added set ctrl-c command + + pdp18b_sys.c: fixed FMTASC printouts (found by Hans Pufal) + + pdp8_clk.c: added set line frequency command + + pdp8_df.c, pdp8_rf.c, pdp8_rx.c: fixed autosizing algorithm + + pdp8_mt.c: + - fixed bug in BOT error handling + - revised to use magtape library + + pdp8_tt.c: added set ctrl-c command + + sds_cpu.c: added set line frequency command + + sds_mt.c: revised to use magtape library + + vax_stddev.c: added set ctrl-c command + + 3 06-Feb-03 scp.c: + - added dynamic extension of the breakpoint table + - added breakpoint actions + + hp2100_cpu.c: fixed last cycle bug in DMA output (found by + Mike Gemeny) + + hp2100_ipl.c: individual links are full duplex (found by + Mike Gemeny) + + pdp11_cpu.c: changed R, SP to track PSW respectively + + pdp18b_defs.h, pdp18b_sys.c: added RB09 fixed head disk, + LP09 printer + + pdp18b_rf.c: + - fixed IOT decoding (found by Hans Pufal) + - fixed address overrun logic + - added variable number of platters and autosizing + + pdp18b_rf.c: + - fixed IOT decoding + - fixed bug in command initiation + + pdp18b_rb.c: new RB09 fixed head disk + + pdp18b_lp.c: new LP09 line printer + + pdp8_df.c: added variable number of platters and autosizing + + pdp8_rf.c: added variable number of platters and autosizing + + nova_dsk.c: added variable number of platters and autosizing + + id16_cpu.c: fixed bug in SETM, SETMR (found by Mark Pizzolato) + + 2 15-Jan-03 scp.c: + - added dynamic memory size flag and RESTORE support + - added EValuate command + - added get_ipaddr routine + - added ! (OS command) feature (from Mark Pizzolato) + - added BREAK support to sim_poll_kbd (from Mark Pizzolato) + + sim_tmxr.c: + - fixed bugs in IAC+IAC handling (from Mark Pizzolato) + - added IAC+BRK handling (from Mark Pizzolato) + + sim_sock.c: + - added use count for Windows start/stop + - added sim_connect_sock + + pdp1_defs.h, pdp1_cpu.c, pdp1_sys.c, pdp1_drm.c: + added Type 24 serial drum + + pdp18_defs.h: added PDP-4 drum support + + hp2100_cpu.c: added 21MX IOP support + + hp2100_ipl.c: added HP interprocessor link support + + pdp11_tq.c: fixed bug in transfer end packet length + + pdp11_xq.c: + - added VMScluster support (thanks to Mark Pizzolato) + - added major performance enhancements (thanks to Mark Pizzolato) + - added local packet processing + - added system id broadcast + + pdp11_stddev.c: changed default to 7b (for early UNIX) + + vax_cpu.c, vax_io.c, vax_stddev.c, vax_sysdev.c: + added console halt capability (from Mark Pizzolato) + + all terminals and multiplexors: added BREAK support + + 1 21-Nov-02 pdp1_stddev.c: changed typewriter to half duplex + (found by Derek Peschel) + + pdp10_tu.c: + - fixed bug in bootstrap (reported by Michael Thompson) + - fixed bug in read (reported by Harris Newman) + + 0 15-Nov-02 SCP and libraries + scp.c: + - added Telnet console support + - removed VT emulation support + - added support for statically buffered devices + - added HELP + - fixed bugs in set_logon, ssh_break (found by David Hittner) + - added VMS file optimization (from Robert Alan Byer) + - added quiet mode, DO with parameters, GUI interface, + extensible commands (from Brian Knittel) + - added DEVICE context and flags + - added central device enable/disable support + - modified SAVE/GET to save and restore flags + - modified boot routine calling sequence + scp_tty.c: + - removed VT emulation support + - added sim_os_sleep, renamed sim_poll_kbd, sim_putchar + sim_tmxr.c: + - modified for Telnet console support + - fixed bug in binary (8b) support + sim_sock.c: modified for Telnet console support + sim_ether.c: new library for Ethernet (from David Hittner) + + all magtapes: + - added support for end of medium + - cleaned up BOT handling + + all DECtapes: added support for RT11 image file format + + most terminals and multiplexors: + - added support for 7b vs 8b character processing + + PDP-1 + pdp1_cpu.c, pdp1_sys.c, pdp1_dt.c: added PDP-1 DECtape support + + PDP-8 + pdp8_cpu.c, all peripherals: + - added variable device number support + - added new device enabled/disable support + pdp8_rx.c: added RX28/RX02 support + + PDP-11 + pdp11_defs.h, pdp11_io.c, pdp11_sys.c, all peripherals: + - added variable vector support + - added new device enable/disable support + - added autoconfiguration support + all bootstraps: modified to support variable addresses + dec_mscp.h, pdp11_tq.c: added TK50 support + pdp11_rq.c: + - added multicontroller support + - fixed bug in HBE error log packet + - fixed bug in ATP processing + pdp11_ry.c: added RX211/RX02 support + pdp11_hk.c: added RK611/RK06/RK07 support + pdp11_tq.c: added TMSCP support + pdp11_xq.c: added DEQNA/DELQA support (from David Hittner) + pdp11_pclk.c: added KW11P support + pdp11_ts.c: + - fixed bug in CTL decoding + - fixed bug in extended status XS0_MOT + pdp11_stddev.c: removed paper tape to its own module + + PDP-18b + pdp18b_cpu.c, all peripherals: + - added variable device number support + - added new device enabled/disabled support + + VAX + dec_dz.h: fixed bug in number of boards calculation + vax_moddefs.h, vax_io.c, vax_sys.c, all peripherals: + - added variable vector support + - added new device enable/disable support + - added autoconfiguration support + vax_sys.c: + - generalized examine/deposit + - added TMSCP, multiple RQDX3, DEQNA/DELQA support + vax_stddev.c: removed paper tape, now uses PDP-11 version + vax_sysdev.c: + - allowed NVR to be attached to file + - removed unused variables (found by David Hittner) + + PDP-10 + pdp10_defs.h, pdp10_ksio.c, all peripherals: + - added variable vector support + - added new device enable/disable support + pdp10_defs.h, pdp10_ksio.c: added support for standard PDP-11 + peripherals, added RX211 support + pdp10_pt.c: rewritten to reference common implementation + + Nova, Eclipse: + nova_cpu.c, eclipse_cpu.c, all peripherals: + - added new device enable/disable support + + HP2100 + hp2100_cpu: + - fixed bugs in the EAU, 21MX, DMS, and IOP instructions + - fixed bugs in the memory protect and DMS functions + - created new options to enable/disable EAU, MPR, DMS + - added new device enable/disable support + hp2100_fp.c: + - recoded to conform to 21MX microcode algorithms + hp2100_stddev.c: + - fixed bugs in TTY reset, OTA, time base generator + - revised BOOT support to conform to RBL loader + - added clock calibration + hp2100_dp.c: + - changed default to 13210A + - added BOOT support + hp2100_dq.c: + - finished incomplete functions, fixed head switching + - added BOOT support + hp2100_ms.c: + - fixed bugs found by diagnostics + - added 13183 support + - added BOOT support + hp2100_mt.c: + - fixed bugs found by diagnostics + - disabled by default + hp2100_lpt.c: implemented 12845A controller + hp2100_lps.c: + - renamed 12653A controller + - added diagnostic mode for MPR, DCPC diagnostics + - disabled by default + + IBM 1620: first release + +/* V2.9 revision history + + 11 20-Jul-02 i1401_mt.c: on read, end of record stores group mark + without word mark (found by Van Snyder) + + i1401_dp.c: reworked address generation and checking + + vax_cpu.c: added infinite loop detection and halt to + boot ROM option (from Mark Pizzolato) + + vax_fpa.c: changed function names to prevent conflict + with C math library + + pdp11_cpu.c: fixed bug in MMR0 update logic (from + John Dundas) + + pdp18b_stddev.c: added "ASCII mode" for reader and + punch (from Hans Pufal) + + gri_*.c: added GRI-909 simulator + + scp.c: added DO echo, DO exit (from Brian Knittel) + + scp_tty.c: added Windows priority hacking (from + Mark Pizzolato) + + 10 15-Jun-02 scp.c: fixed error checking on calls to fxread/fxwrite + (found by Norm Lastovic) + + scp_tty.c, sim_vt.h, sim_vt.c: added VTxxx emulation + support for Windows (from Fischer Franz) + + sim_sock.c: added OS/2 support (from Holger Veit) + + pdp11_cpu.c: fixed bugs (from John Dundas) + - added special case for PS<15:12> = 1111 to MFPI + - removed special case from MTPI + - added masking of relocation adds + + i1401_cpu.c: + - added multiply/divide + - fixed bugs (found by Van Snyder) + o 5 and 7 character H, 7 character doesn't branch + o 8 character NOP + o 1401-like memory dump + + i1401_dp.c: added 1311 disk + + 9 04-May-02 pdp11_rq: fixed bug in polling routine + + 8 03-May-02 scp.c: + - changed LOG/NOLOG to SET LOG/NOLOG + - added SHOW LOG + - added SET VT/NOVT and SHOW VT for VT emulation + + sim_sock.h: changed VMS stropt.h include to ioctl.h + + vax_cpu.c + - added TODR powerup routine to set date, time on boot + - fixed exception flows to clear trap request + - fixed register logging in autoincrement indexed + + vax_stddev.c: added TODR powerup routine + + vax_cpu1.c: fixed exception flows to clear trap request + + 7 30-Apr-02 scp.c: fixed bug in clock calibration when (real) clock + jumps forward due too far (found by Jonathan Engdahl) + + pdp11_cpu.c: fixed bugs, added features (from John Dundas + and Wolfgang Helbig) + - added HTRAP and BPOK to maintenance register + - added trap on kernel HALT if MAINT set + - fixed red zone trap, clear odd address and nxm traps + - fixed RTS SP, don't increment restored SP + - fixed TSTSET, write dst | 1 rather than prev R0 | 1 + - fixed DIV, set N=0,Z=1 on div by zero (J11, 11/70) + - fixed DIV, set set N=Z=0 on overfow (J11, 11/70) + - fixed ASH, ASHC, count = -32 used implementation- + dependent 32 bit right shift + - fixed illegal instruction test to detect 000010 + - fixed write-only page test + + pdp11_rp.c: fixed SHOW ADDRESS command + + vaxmod_defs.h: fixed DZ vector base and number of lines + + dec_dz.h: + - fixed interrupt acknowledge routines + - fixed SHOW ADDRESS command + + all magtape routines: added test for badly formed + record length (suggested by Jonathan Engdahl) + + 6 18-Apr-02 vax_cpu.c: fixed CASEL condition codes + + vax_cpu1.c: fixed vfield pos > 31 test to be unsigned + + vax_fpu.c: fixed EDIV overflow test for 0 quotient + + 5 14-Apr-02 vax_cpu1.c: + - fixed interrupt, prv_mode set to 0 (found by Tim Stark) + - fixed PROBEx to mask mode to 2b (found by Kevin Handy) + + 4 1-Apr-02 pdp11_rq.c: fixed bug, reset cleared write protect status + + pdp11_ts.c: fixed bug in residual frame count after space + + 3 15-Mar-02 pdp11_defs.h: changed default model to KDJ11A (11/73) + + pdp11_rq.c: adjusted delays for M+ timing bugs + + hp2100_cpu.c, pdp10_cpu.c, pdp11_cpu.c: tweaked abort + code for ANSI setjmp/longjmp compliance + + hp2100_cpu.c, hp2100_fp.c, hp2100_stddev.c, hp2100_sys.c: + revised to allocate memory dynamically + + 2 01-Mar-02 pdp11_cpu.c: + - fixed bugs in CPU registers + - fixed double operand evaluation order for M+ + + pdp11_rq.c: added delays to initialization for + RSX11M+ prior to V4.5 + + 1 20-Feb-02 scp.c: fixed bug in clock calibration when (real) + time runs backwards + + pdp11_rq.c: fixed bug in host timeout logic + + pdp11_ts.c: fixed bug in message header logic + + pdp18b_defs.h, pdp18b_dt.c, pdp18b_sys.c: added + PDP-7 DECtape support + + hp2100_cpu.c: + - added floating point and DMS + - fixed bugs in DIV, ASL, ASR, LBT, SBT, CBT, CMW + + hp2100_sys.c: added floating point, DMS + + hp2100_fp.c: added floating point + + ibm1130: added Brian Knittel's IBM 1130 simulator + + 0 30-Jan-02 scp.c: + - generalized timer package for multiple timers + - added circular register arrays + - fixed bugs, line spacing in modifier display + - added -e switch to attach + - moved device enable/disable to simulators + + scp_tty.c: VAX specific fix (from Robert Alan Byer) + + sim_tmxr.c, sim_tmxr.h: + - added tmxr_fstats, tmxr_dscln + - renamed tmxr_fstatus to tmxr_fconns + + sim_sock.c, sim_sock.h: added VMS support (from + Robert Alan Byer) + + pdp_dz.h, pdp18b_tt1.c, nova_tt1.c: + - added SET DISCONNECT + - added SHOW STATISTICS + + pdp8_defs.h: fixed bug in interrupt enable initialization + + pdp8_ttx.c: rewrote as unified multiplexor + + pdp11_cpu.c: fixed calc_MMR1 macro (found by Robert Alan Byer) + + pdp11_stddev.c: fixed bugs in KW11L (found by John Dundas) + + pdp11_rp.c: fixed bug in 18b mode boot + + pdp11 bootable I/O devices: fixed register setup at boot + exit (found by Doug Carman) + + hp2100_cpu.c: + - fixed DMA register tables (found by Bill McDermith) + - fixed SZx,SLx,RSS bug (found by Bill McDermith) + - fixed flop restore logic (found by Bill McDermith) + + hp2100_mt.c: fixed bug on write of last character + + hp2100_dq,dr,ms,mux.c: added new disk, magtape, and terminal + multiplexor controllers + + i1401_cd.c, i1401_mt.c: new zero footprint bootstraps + (from Van Snyder) + + i1401_sys.c: fixed symbolic display of H, NOP with no trailing + word mark (found by Van Snyder) + + most CPUs: + - replaced OLDPC with PC queue + - implemented device enable/disable locally + + V2.8 revision history + +5 25-Dec-01 scp.c: fixed bug in DO command (found by John Dundas) + + pdp10_cpu.c: + - moved trap-in-progress to separate variable + - cleaned up declarations + - cleaned up volatile state for GNU C longjmp + + pdp11_cpu.c: cleaned up declarations + + pdp11_rq.c: added RA-class disks + +4 17-Dec-01 pdp11_rq.c: added delayed processing of packets + +3 16-Dec-01 pdp8_cpu.c: + - mode A EAE instructions didn't clear GTF + - ASR shift count > 24 mis-set GTF + - effective shift count == 32 didn't work + +2 07-Dec-01 scp.c: added breakpoint package + + all CPU's: revised to use new breakpoint package + +1 05-Dec-01 scp.c: fixed bug in universal register name logic + +0 30-Nov-01 Reorganized simh source and documentation tree + + scp: Added DO command, universal registers, extended + SET/SHOW logic + + pdp11: overhauled PDP-11 for DMA map support, shared + sources with VAX, dynamic buffer allocation + + 18b pdp: overhauled interrupt structure + + pdp8: added RL8A + + pdp10: fixed two ITS-related bugs (found by Dave Conroy) + + V2.7 revision history + +patch date module(s) and fix(es) + +15 23-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: fixed bugs + error interrupt handling + + pdp10_defs.h, pdp10_ksio.c, pdp10_fe.c, pdp10_fe.c, + pdp10_rp.c, pdp10_tu.c: reworked I/O page interface + to use symbolic base addresses and lengths + +14 20-Oct-01 dec_dz.h, sim_tmxr_h, sim_tmxr.c: fixed bug in Telnet + state handling (found by Thord Nilson), removed + tmxr_getchar, added tmxr_rqln and tmxr_tqln + +13 18-Oct-01 pdp11_tm.c: added stub diagnostic register clock + for RSTS/E (found by Thord Nilson) + +12 15-Oct-01 pdp11_defs.h, pdp11_cpu.c, pdp11_tc.c, pdp11_ts.c, + pdp11_rp.c: added operations logging + +11 8-Oct-01 scp.c: added sim_rev.h include and version print + + pdp11_cpu.c: fixed bug in interrupt acknowledge, + multiple outstanding interrupts caused the lowest + rather than the highest to be acknowledged + +10 7-Oct-01 pdp11_stddev.c: added monitor bits (CSR<7>) for full + KW11L compatibility, needed for RSTS/E autoconfiguration + +9 6-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: rewrote interrupt + logic from RH11/RH70 schematics, to mimic hardware quirks + + dec_dz.c: fixed bug in carrier detect logic, carrier + detect was being cleared on next modem poll + +8 4-Oct-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: undid edit of + 28-Sep-01; real problem was level-sensitive nature of + CS1_SC, but CS1_SC can only trigger an interrupt if + DONE is set + +7 2-Oct-01 pdp11_rp.c, pdp10_rp.c: CS1_SC is evaluated as a level- + sensitive, rather than an edge-sensitive, input to + interrupt request + +6 30-Sep-01 pdp11_rp.c, pdp10_rp.c: separated out CS1<5:0> to per- + drive registers + + pdp10_tu.c: based on above, cleaned up handling of + non-existent formatters, fixed non-data transfer + commands clearing DONE + +5 28-Sep-01 pdp11_rp.c, pdp10_rp.c, pdp10_tu.c: controller should + interrupt if ATA or SC sets when IE is set, was + interrupting only if DON = 1 as well + +4 27-Sep-01 pdp11_ts.c: + - NXM errors should return TC4 or TC5; were returning TC3 + - extended features is part of XS2; was returned in XS3 + - extended characteristics (fifth) word needed for RSTS/E + + pdp11_tc.c: stop, stop all do cause an interrupt + + dec_dz.h: scanner should find a ready output line, even + if there are no connections; needed for RSTS/E autoconfigure + + scp.c: + - added routine sim_qcount for 1130 + - added "simulator exit" detach routine for 1130 + + sim_defs.h: added header for sim_qcount + +3 20-Sep-01 pdp11_ts.c: boot code binary was incorrect + +2 19-Sep-01 pdp18b_cpu.c: EAE should interpret initial count of 00 + as 100 + + scp.c: modified Macintosh support + +1 17-Sep-01 pdp8_ttx.c: new module for PDP-8 multi-terminal support + + pdp18b_tt1.c: modified to use sim_tmxr library + + nova_tt1.c: modified to use sim_tmxr library + + dec_dz.h: added autodisconnect support + + scp.c: removed old multiconsole support + + sim_tmxr.c: modified calling sequence for sim_putchar_ln + + sim_sock.c: added Macintosh sockets support +*/ + +#endif diff --git a/sim_sock.c b/sim_sock.c new file mode 100644 index 0000000..3bc95d9 --- /dev/null +++ b/sim_sock.c @@ -0,0 +1,314 @@ +/* sim_sock.c: OS-dependent socket routines + + Copyright (c) 2001-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 19-Nov-05 RMS Added conditional for OpenBSD (from Federico G. Schwindt) + 16-Aug-05 RMS Fixed spurious SIGPIPE signal error in Unix + 14-Apr-05 RMS Added WSAEINPROGRESS test (from Tim Riker) + 09-Jan-04 RMS Fixed typing problem in Alpha Unix (found by Tim Chapman) + 17-Apr-03 RMS Fixed non-implemented version of sim_close_sock + (found by Mark Pizzolato) + 17-Dec-02 RMS Added sim_connect_socket, sim_create_socket + 08-Oct-02 RMS Revised for .NET compatibility + 22-Aug-02 RMS Changed calling sequence for sim_accept_conn + 22-May-02 RMS Added OS2 EMX support from Holger Veit + 06-Feb-02 RMS Added VMS support from Robert Alan Byer + 16-Sep-01 RMS Added Macintosh support from Peter Schorn + 02-Sep-01 RMS Fixed UNIX bugs found by Mirian Lennox and Tom Markson +*/ + +#include "sim_defs.h" +#include "sim_sock.h" +#include + +/* OS dependent routines + + sim_master_sock create master socket + sim_accept_conn accept connection + sim_read_sock read from socket + sim_write_sock write from socket + sim_close_sock close socket + sim_setnonblock set socket non-blocking + sim_msg_sock send message to socket +*/ + +int32 sim_sock_cnt = 0; + +/* First, all the non-implemented versions */ + +#if defined (__OS2__) && !defined (__EMX__) + +SOCKET sim_master_sock (int32 port) +{ +return INVALID_SOCKET; +} + +SOCKET sim_connect_sock (int32 ip, int32 port) +{ +return INVALID_SOCKET; +} + +SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr) +{ +return INVALID_SOCKET; +} + +int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes) +{ +return -1; +} + +int32 sim_write_sock (SOCKET sock, char *msg, int32 nbytes) +{ +return 0; +} + +void sim_close_sock (SOCKET sock, t_bool master) +{ +return; +} + +SOCKET sim_setnonblock (SOCKET sock) +{ +return SOCKET_ERROR; +} + +#else /* endif unimpl */ + +/* UNIX, Win32, Macintosh, VMS, OS2 (Berkeley socket) routines */ + +SOCKET sim_err_sock (SOCKET s, char *emsg, int32 flg) +{ +int32 err = WSAGetLastError (); + +printf ("Sockets: %s error %d\n", emsg, err); +sim_close_sock (s, flg); +return INVALID_SOCKET; +} + +SOCKET sim_create_sock (void) +{ +SOCKET newsock; +int32 err; + +#if defined (_WIN32) +WORD wVersionRequested; +WSADATA wsaData; +wVersionRequested = MAKEWORD (1, 1); + +if (sim_sock_cnt == 0) { + err = WSAStartup (wVersionRequested, &wsaData); /* start Winsock */ + if (err != 0) { + printf ("Winsock: startup error %d\n", err); + return INVALID_SOCKET; + } + } +sim_sock_cnt = sim_sock_cnt + 1; +#endif /* endif Win32 */ +#if defined (SIGPIPE) +signal (SIGPIPE, SIG_IGN); /* no pipe signals */ +#endif + +newsock = socket (AF_INET, SOCK_STREAM, 0); /* create socket */ +if (newsock == INVALID_SOCKET) { /* socket error? */ + err = WSAGetLastError (); + printf ("Sockets: socket error %d\n", err); + return INVALID_SOCKET; + } +return newsock; +} + +SOCKET sim_master_sock (int32 port) +{ +SOCKET newsock; +struct sockaddr_in name; +int32 sta; + +newsock = sim_create_sock (); /* create socket */ +if (newsock == INVALID_SOCKET) return newsock; /* socket error? */ + +name.sin_family = AF_INET; /* name socket */ +name.sin_port = htons ((unsigned short) port); /* insert port */ +name.sin_addr.s_addr = htonl (INADDR_ANY); /* insert addr */ + +sta = bind (newsock, (struct sockaddr *) &name, sizeof (name)); +if (sta == SOCKET_ERROR) /* bind error? */ + return sim_err_sock (newsock, "bind", 1); +sta = sim_setnonblock (newsock); /* set nonblocking */ +if (sta == SOCKET_ERROR) /* fcntl error? */ + return sim_err_sock (newsock, "fcntl", 1); +sta = listen (newsock, 1); /* listen on socket */ +if (sta == SOCKET_ERROR) /* listen error? */ + return sim_err_sock (newsock, "listen", 1); +return newsock; /* got it! */ +} + +SOCKET sim_connect_sock (int32 ip, int32 port) +{ +SOCKET newsock; +struct sockaddr_in name; +int32 sta; + +newsock = sim_create_sock (); /* create socket */ +if (newsock == INVALID_SOCKET) return newsock; /* socket error? */ + +name.sin_family = AF_INET; /* name socket */ +name.sin_port = htons ((unsigned short) port); /* insert port */ +name.sin_addr.s_addr = htonl (ip); /* insert addr */ + +sta = sim_setnonblock (newsock); /* set nonblocking */ +if (sta == SOCKET_ERROR) /* fcntl error? */ + return sim_err_sock (newsock, "fcntl", 1); +sta = connect (newsock, (struct sockaddr *) &name, sizeof (name)); +if ((sta == SOCKET_ERROR) && + (WSAGetLastError () != WSAEWOULDBLOCK) && + (WSAGetLastError () != WSAEINPROGRESS)) + return sim_err_sock (newsock, "connect", 1); + +return newsock; /* got it! */ +} + +SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr) +{ +int32 sta, err; +#if defined (macintosh) || defined (__linux) || \ + defined (__APPLE__) || defined (__OpenBSD__) +socklen_t size; +#elif defined (_WIN32) || defined (__EMX__) ||\ + (defined (__ALPHA) && defined (__unix__)) +int size; +#else +size_t size; +#endif +SOCKET newsock; +struct sockaddr_in clientname; + +if (master == 0) return INVALID_SOCKET; /* not attached? */ +size = sizeof (clientname); +newsock = accept (master, (struct sockaddr *) &clientname, &size); +if (newsock == INVALID_SOCKET) { /* error? */ + err = WSAGetLastError (); + if (err != WSAEWOULDBLOCK) + printf ("Sockets: accept error %d\n", err); + return INVALID_SOCKET; + } +if (ipaddr != NULL) *ipaddr = ntohl (clientname.sin_addr.s_addr); + +sta = sim_setnonblock (newsock); /* set nonblocking */ +if (sta == SOCKET_ERROR) /* fcntl error? */ + return sim_err_sock (newsock, "fcntl", 0); +return newsock; +} + +int32 sim_check_conn (SOCKET sock, t_bool rd) +{ +fd_set rw_set, er_set; +fd_set *rw_p = &rw_set; +fd_set *er_p = &er_set; +struct timeval tz; + +timerclear (&tz); +FD_ZERO (rw_p); +FD_ZERO (er_p); +FD_SET (sock, rw_p); +FD_SET (sock, er_p); +if (rd) select ((int) sock + 1, rw_p, NULL, er_p, &tz); +else select ((int) sock + 1, NULL, rw_p, er_p, &tz); +if (FD_ISSET (sock, rw_p)) return 1; +if (FD_ISSET (sock, er_p)) return -1; +return 0; +} + +int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes) +{ +int32 rbytes, err; + +rbytes = recv (sock, buf, nbytes, 0); +if (rbytes == 0) return -1; /* disconnect */ +if (rbytes == SOCKET_ERROR) { + err = WSAGetLastError (); + if (err == WSAEWOULDBLOCK) return 0; /* no data */ + printf ("Sockets: read error %d\n", err); + return -1; + } +return rbytes; +} + +int32 sim_write_sock (SOCKET sock, char *msg, int32 nbytes) +{ +return send (sock, msg, nbytes, 0); +} + +void sim_close_sock (SOCKET sock, t_bool master) +{ +#if defined (_WIN32) +closesocket (sock); +if (master) { + sim_sock_cnt = sim_sock_cnt - 1; + if (sim_sock_cnt <= 0) { + WSACleanup (); + sim_sock_cnt = 0; + } + } +#else +close (sock); +#endif +return; +} + +#if defined (_WIN32) /* Windows */ +SOCKET sim_setnonblock (SOCKET sock) +{ +unsigned long non_block = 1; + +return ioctlsocket (sock, FIONBIO, &non_block); /* set nonblocking */ +} + +#elif defined (VMS) /* VMS */ +SOCKET sim_setnonblock (SOCKET sock) +{ +int non_block = 1; + +return ioctl (sock, FIONBIO, &non_block); /* set nonblocking */ +} + +#else /* Mac, Unix, OS/2 */ +int32 sim_setnonblock (SOCKET sock) +{ +int32 fl, sta; + +fl = fcntl (sock, F_GETFL,0); /* get flags */ +if (fl == -1) return SOCKET_ERROR; +sta = fcntl (sock, F_SETFL, fl | O_NONBLOCK); /* set nonblock */ +if (sta == -1) return SOCKET_ERROR; +#if !defined (macintosh) && !defined (__EMX__) /* Unix only */ +sta = fcntl (sock, F_SETOWN, getpid()); /* set ownership */ +if (sta == -1) return SOCKET_ERROR; +#endif +return 0; +} + +#endif /* endif !Win32 && !VMS */ + +#endif /* end else !implemented */ diff --git a/sim_sock.h b/sim_sock.h new file mode 100644 index 0000000..9ac90e3 --- /dev/null +++ b/sim_sock.h @@ -0,0 +1,88 @@ +/* sim_sock.h: OS-dependent socket routines header file + + Copyright (c) 2001-2009, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 04-Jun-08 RMS Addes sim_create_sock, for IBM 1130 + 14-Apr-05 RMS Added WSAEINPROGRESS (from Tim Riker) + 20-Aug-04 HV Added missing definition for OS/2 (from Holger Veit) + 22-Oct-03 MP Changed WIN32 winsock include to use winsock2.h to + avoid a conflict if sim_sock.h and sim_ether.h get + included by the same module. + 20-Mar-03 RMS Added missing timerclear definition for VMS (from + Robert Alan Byer) + 15-Feb-03 RMS Added time.h for EMX (from Holger Veit) + 17-Dec-02 RMS Added sim_connect_sock + 08-Oct-02 RMS Revised for .NET compatibility + 20-Aug-02 RMS Changed calling sequence for sim_accept_conn + 30-Apr-02 RMS Changed VMS stropts include to ioctl + 06-Feb-02 RMS Added VMS support from Robert Alan Byer + 16-Sep-01 RMS Added Macintosh support from Peter Schorn +*/ + +#ifndef _SIM_SOCK_H_ +#define _SIM_SOCK_H_ 0 + +#if defined (_WIN32) /* Windows */ +#undef INT_PTR /* hack, hack */ +#include + +#elif !defined (__OS2__) || defined (__EMX__) /* VMS, Mac, Unix, OS/2 EMX */ +#define WSAGetLastError() errno /* Windows macros */ +#define SOCKET int32 +#define WSAEWOULDBLOCK EWOULDBLOCK +#define WSAEINPROGRESS EINPROGRESS +#define INVALID_SOCKET -1 +#define SOCKET_ERROR -1 +#include /* for fcntl, getpid */ +#include /* for sockets */ +#include +#include +#include /* for sockaddr_in */ +#include +#include /* for EMX */ +#endif + +#if defined (VMS) /* VMS unique */ +#include /* for ioctl */ +#if !defined (timerclear) +#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +#endif +#if defined(__EMX__) /* OS/2 unique */ +#if !defined (timerclear) +#define timerclear(tvp) (tvp)->tv_sec = (tvp)->tv_usec = 0 +#endif +#endif + +SOCKET sim_master_sock (int32 port); +SOCKET sim_connect_sock (int32 ip, int32 port); +SOCKET sim_create_sock (void); +SOCKET sim_accept_conn (SOCKET master, uint32 *ipaddr); +int32 sim_check_conn (SOCKET sock, t_bool rd); +int32 sim_read_sock (SOCKET sock, char *buf, int32 nbytes); +int32 sim_write_sock (SOCKET sock, char *msg, int32 nbytes); +void sim_close_sock (SOCKET sock, t_bool master); +SOCKET sim_setnonblock (SOCKET sock); + +#endif diff --git a/sim_tape.c b/sim_tape.c new file mode 100644 index 0000000..b53cc06 --- /dev/null +++ b/sim_tape.c @@ -0,0 +1,937 @@ +/* sim_tape.c: simulator tape support library + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + Ultimately, this will be a place to hide processing of various tape formats, + as well as OS-specific direct hardware access. + + 23-Jan-07 JDB Fixed backspace over gap at BOT + 22-Jan-07 RMS Fixed bug in P7B format read reclnt rev (found by Rich Cornwell) + 15-Dec-06 RMS Added support for small capacity tapes + 30-Aug-06 JDB Added erase gap support + 14-Feb-06 RMS Added variable tape capacity + 23-Jan-06 JDB Fixed odd-byte-write problem in sim_tape_wrrecf + 17-Dec-05 RMS Added write support for Paul Pierce 7b format + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 02-May-05 RMS Added support for Pierce 7b format + 28-Jul-04 RMS Fixed bug in writing error records (found by Dave Bryan) + RMS Fixed incorrect error codes (found by Dave Bryan) + 05-Jan-04 RMS Revised for file I/O library + 25-Apr-03 RMS Added extended file support + 28-Mar-03 RMS Added E11 and TPC format support + + Public routines: + + sim_tape_attach attach tape unit + sim_tape_detach detach tape unit + sim_tape_rdrecf read tape record forward + sim_tape_rdrecr read tape record reverse + sim_tape_wrrecf write tape record forward + sim_tape_sprecf space tape record forward + sim_tape_sprecr space tape record reverse + sim_tape_wrtmk write tape mark + sim_tape_wreom erase remainder of tape + sim_tape_wrgap write erase gap + sim_tape_rewind rewind + sim_tape_reset reset unit + sim_tape_bot TRUE if at beginning of tape + sim_tape_eot TRUE if at or beyond end of tape + sim_tape_wrp TRUE if write protected + sim_tape_set_fmt set tape format + sim_tape_show_fmt show tape format + sim_tape_set_capac set tape capacity + sim_tape_show_capac show tape capacity +*/ + +#include "sim_defs.h" +#include "sim_tape.h" + +struct sim_tape_fmt { + char *name; /* name */ + int32 uflags; /* unit flags */ + t_addr bot; /* bot test */ + }; + +static struct sim_tape_fmt fmts[MTUF_N_FMT] = { + { "SIMH", 0, sizeof (t_mtrlnt) - 1 }, + { "E11", 0, sizeof (t_mtrlnt) - 1 }, + { "TPC", UNIT_RO, sizeof (t_tpclnt) - 1 }, + { "P7B", 0, 0 }, +/* { "TPF", UNIT_RO, 0 }, */ + { NULL, 0, 0 } + }; + +extern int32 sim_switches; + +t_stat sim_tape_ioerr (UNIT *uptr); +t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat); +uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map); +t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map); + +/* Attach tape unit */ + +t_stat sim_tape_attach (UNIT *uptr, char *cptr) +{ +uint32 objc; +char gbuf[CBUFSIZE]; +t_stat r; + +if (sim_switches & SWMASK ('F')) { /* format spec? */ + cptr = get_glyph (cptr, gbuf, 0); /* get spec */ + if (*cptr == 0) return SCPE_2FARG; /* must be more */ + if (sim_tape_set_fmt (uptr, 0, gbuf, NULL) != SCPE_OK) + return SCPE_ARG; + } +r = attach_unit (uptr, cptr); /* attach unit */ +if (r != SCPE_OK) return r; /* error? */ +switch (MT_GET_FMT (uptr)) { /* case on format */ + + case MTUF_F_TPC: /* TPC */ + objc = sim_tape_tpc_map (uptr, NULL); /* get # objects */ + if (objc == 0) { /* tape empty? */ + sim_tape_detach (uptr); + return SCPE_FMT; /* yes, complain */ + } + uptr->filebuf = calloc (objc + 1, sizeof (t_addr)); + if (uptr->filebuf == NULL) { /* map allocated? */ + sim_tape_detach (uptr); + return SCPE_MEM; /* no, complain */ + } + uptr->hwmark = objc + 1; /* save map size */ + sim_tape_tpc_map (uptr, (t_addr *) uptr->filebuf); /* fill map */ + break; + + default: + break; + } + +sim_tape_rewind (uptr); +return SCPE_OK; +} + +/* Detach tape unit */ + +t_stat sim_tape_detach (UNIT *uptr) +{ +uint32 f = MT_GET_FMT (uptr); +t_stat r; + +r = detach_unit (uptr); /* detach unit */ +if (r != SCPE_OK) return r; +switch (f) { /* case on format */ + + case MTUF_F_TPC: /* TPC */ + if (uptr->filebuf) free (uptr->filebuf); /* free map */ + uptr->filebuf = NULL; + uptr->hwmark = 0; + break; + + default: + break; + } + +sim_tape_rewind (uptr); +return SCPE_OK; +} + +/* Read record length forward (internal routine) + + Inputs: + uptr = pointer to tape unit + bc = pointer to returned record length + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + tape mark updated + data record updated, sim_fread will read record forward + + See notes at "sim_tape_wrgap" regarding erase gap implementation. +*/ + +t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) +{ +uint8 c; +t_bool all_eof; +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt sbc; +t_tpclnt tpcbc; + +MT_CLR_PNU (uptr); +if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */ +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set tape pos */ +switch (f) { /* switch on fmt */ + + case MTUF_F_STD: case MTUF_F_E11: + do { + sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */ + sbc = MTR_L (*bc); /* save rec lnt */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); /* pos not upd */ + return sim_tape_ioerr (uptr); + } + if (feof (uptr->fileref) || (*bc == MTR_EOM)) { /* eof or eom? */ + MT_SET_PNU (uptr); /* pos not upd */ + return MTSE_EOM; + } + uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* spc over rec lnt */ + if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */ + if (*bc == MTR_FHGAP) { /* half gap? */ + uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space fwd */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ + } + else if (*bc != MTR_GAP) + uptr->pos = uptr->pos + sizeof (t_mtrlnt) + /* spc over record */ + ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc); + } + while ((*bc == MTR_GAP) || (*bc == MTR_FHGAP)); + break; + + case MTUF_F_TPC: + sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); + *bc = tpcbc; /* save rec lnt */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); /* pos not upd */ + return sim_tape_ioerr (uptr); + } + if (feof (uptr->fileref)) { /* eof? */ + MT_SET_PNU (uptr); /* pos not upd */ + return MTSE_EOM; + } + uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */ + if (tpcbc == TPC_TMK) return MTSE_TMK; /* tape mark? */ + uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */ + break; + + case MTUF_F_P7B: + for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */ + sim_fread (&c, sizeof (uint8), 1, uptr->fileref); + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); /* pos not upd */ + return sim_tape_ioerr (uptr); + } + if (feof (uptr->fileref)) { /* eof? */ + if (sbc == 0) return MTSE_EOM; /* no data? eom */ + break; /* treat like eor */ + } + if ((sbc != 0) && (c & P7B_SOR)) break; /* next record? */ + if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; + } + *bc = sbc; /* save rec lnt */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ + uptr->pos = uptr->pos + sbc; /* spc over record */ + if (all_eof) return MTSE_TMK; /* tape mark? */ + break; + + default: + return MTSE_FMT; + } + +return MTSE_OK; +} + +/* Read record length reverse (internal routine) + + Inputs: + uptr = pointer to tape unit + bc = pointer to returned record length + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + beginning of tape unchanged + read error unchanged + end of file unchanged + end of medium updated + tape mark updated + data record updated, sim_fread will read record forward + + See notes at "sim_tape_wrgap" regarding erase gap implementation. +*/ + +t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) +{ +uint8 c; +t_bool all_eof; +uint32 f = MT_GET_FMT (uptr); +t_addr ppos; +t_mtrlnt sbc; +t_tpclnt tpcbc; + +MT_CLR_PNU (uptr); +if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */ +if (sim_tape_bot (uptr)) return MTSE_BOT; /* at BOT? */ +switch (f) { /* switch on fmt */ + + case MTUF_F_STD: case MTUF_F_E11: + do { + sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET); + sim_fread (bc, sizeof (t_mtrlnt), 1, uptr->fileref); /* read rec lnt */ + sbc = MTR_L (*bc); + if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); + if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */ + uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* spc over rec lnt */ + if (*bc == MTR_EOM) return MTSE_EOM; /* eom? */ + if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */ + if ((*bc & MTR_M_RHGAP) == MTR_RHGAP) { /* half gap? */ + uptr->pos = uptr->pos + sizeof (t_mtrlnt) / 2; /* half space rev */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* resync */ + } + else if (*bc != MTR_GAP) { + uptr->pos = uptr->pos - sizeof (t_mtrlnt) - /* spc over record */ + ((f == MTUF_F_STD)? ((sbc + 1) & ~1): sbc); + sim_fseek (uptr->fileref, uptr->pos + sizeof (t_mtrlnt), SEEK_SET); + } + else if (sim_tape_bot (uptr)) /* backed into BOT? */ + return MTSE_BOT; + } + while ((*bc == MTR_GAP) || (*bc == MTR_RHGAP)); + break; + + case MTUF_F_TPC: + ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */ + sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */ + sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); + *bc = tpcbc; /* save rec lnt */ + if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); + if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */ + uptr->pos = ppos; /* spc over record */ + if (*bc == MTR_TMK) return MTSE_TMK; /* tape mark? */ + sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET); + break; + + case MTUF_F_P7B: + for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) { + sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET); + sim_fread (&c, sizeof (uint8), 1, uptr->fileref); + if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); + if (feof (uptr->fileref)) return MTSE_EOM; /* eof? */ + if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; + if (c & P7B_SOR) break; /* start of record? */ + } + uptr->pos = uptr->pos - sbc; /* update position */ + *bc = sbc; /* save rec lnt */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ + if (all_eof) return MTSE_TMK; /* tape mark? */ + break; + + default: + return MTSE_FMT; + } + +return MTSE_OK; +} + +/* Read record forward + + Inputs: + uptr = pointer to tape unit + buf = pointer to buffer + bc = pointer to returned record length + max = maximum record size + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged, PNU set + end of file/medium unchanged, PNU set + invalid record unchanged, PNU set + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) +{ +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt i, tbc, rbc; +t_addr opos; +t_stat st; + +opos = uptr->pos; /* old position */ +if (st = sim_tape_rdlntf (uptr, &tbc)) return st; /* read rec lnt */ +*bc = rbc = MTR_L (tbc); /* strip error flag */ +if (rbc > max) { /* rec out of range? */ + MT_SET_PNU (uptr); + uptr->pos = opos; + return MTSE_INVRL; + } +i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ +if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + uptr->pos = opos; + return sim_tape_ioerr (uptr); + } +for ( ; i < rbc; i++) buf[i] = 0; /* fill with 0's */ +if (f == MTUF_F_P7B) buf[0] = buf[0] & P7B_DPAR; /* p7b? strip SOR */ +return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); +} + +/* Read record reverse + + Inputs: + uptr = pointer to tape unit + buf = pointer to buffer + bc = pointer to returned record length + max = maximum record size + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + read error unchanged + end of file unchanged + end of medium updated + invalid record unchanged + tape mark updated + data record updated + data record error updated +*/ + +t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max) +{ +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt i, rbc, tbc; +t_stat st; + +if (st = sim_tape_rdlntr (uptr, &tbc)) return st; /* read rec lnt */ +*bc = rbc = MTR_L (tbc); /* strip error flag */ +if (rbc > max) return MTSE_INVRL; /* rec out of range? */ +i = sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ +if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); +for ( ; i < rbc; i++) buf[i] = 0; /* fill with 0's */ +if (f == MTUF_F_P7B) buf[0] = buf[0] & P7B_DPAR; /* p7b? strip SOR */ +return (MTR_F (tbc)? MTSE_RECE: MTSE_OK); +} + +/* Write record forward + + Inputs: + uptr = pointer to tape unit + buf = pointer to buffer + bc = record length + Outputs: + status = operation status + + exit condition position + + unit unattached unchanged + write protect unchanged + write error unchanged, PNU set + data record updated +*/ + +t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc) +{ +uint32 f = MT_GET_FMT (uptr); +t_mtrlnt sbc; + +MT_CLR_PNU (uptr); +sbc = MTR_L (bc); +if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */ +if (sim_tape_wrp (uptr)) return MTSE_WRP; /* write prot? */ +if (sbc == 0) return MTSE_OK; /* nothing to do? */ +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ +switch (f) { /* case on format */ + + case MTUF_F_STD: /* standard */ + sbc = MTR_L ((bc + 1) & ~1); /* pad odd length */ + case MTUF_F_E11: /* E11 */ + sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); + sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); + sim_fwrite (&bc, sizeof (t_mtrlnt), 1, uptr->fileref); + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + return sim_tape_ioerr (uptr); + } + uptr->pos = uptr->pos + sbc + (2 * sizeof (t_mtrlnt)); /* move tape */ + break; + + case MTUF_F_P7B: /* Pierce 7B */ + buf[0] = buf[0] | P7B_SOR; /* mark start of rec */ + sim_fwrite (buf, sizeof (uint8), sbc, uptr->fileref); + sim_fwrite (buf, sizeof (uint8), 1, uptr->fileref); /* delimit rec */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + return sim_tape_ioerr (uptr); + } + uptr->pos = uptr->pos + sbc; /* move tape */ + break; + } + +return MTSE_OK; +} + +/* Write metadata forward (internal routine) */ + +t_stat sim_tape_wrdata (UNIT *uptr, uint32 dat) +{ +MT_CLR_PNU (uptr); +if ((uptr->flags & UNIT_ATT) == 0) return MTSE_UNATT; /* not attached? */ +if (sim_tape_wrp (uptr)) return MTSE_WRP; /* write prot? */ +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ +sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref); +if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + return sim_tape_ioerr (uptr); + } +uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* move tape */ +return MTSE_OK; +} + +/* Write tape mark */ + +t_stat sim_tape_wrtmk (UNIT *uptr) +{ +if (MT_GET_FMT (uptr) == MTUF_F_P7B) { /* P7B? */ + uint8 buf = P7B_EOF; /* eof mark */ + return sim_tape_wrrecf (uptr, &buf, 1); /* write char */ + } +return sim_tape_wrdata (uptr, MTR_TMK); +} + +/* Write end of medium */ + +t_stat sim_tape_wreom (UNIT *uptr) +{ +if (MT_GET_FMT (uptr) == MTUF_F_P7B) return MTSE_FMT; /* cant do P7B */ +return sim_tape_wrdata (uptr, MTR_EOM); +} + +/* Write erase gap + + Inputs: + uptr = pointer to tape unit + gaplen = length of gap in tenths of an inch + bpi = current recording density in bytes per inch + + Outputs: + status = operation status + + exit condition position + ------------------ ------------------ + unit unattached unchanged + unsupported format unchanged + write protected unchanged + read error unchanged, PNU set + write error unchanged, PNU set + gap written updated + + + An erase gap is represented in the tape image file by a special metadata + value. This value is chosen so that it is still recognizable even if it has + been "cut in half" by a subsequent data overwrite that does not end on a + metadatum-sized boundary. In addition, a range of metadata values are + reserved for detection in the reverse direction. Erase gaps are supported + only in SIMH tape format. + + This implementation supports erasing gaps in the middle of a populated tape + image and will always produce a valid image. It also produces valid images + when overwriting gaps with data records, with one exception: a data write + that leaves only two bytes of gap remaining will produce an invalid tape. + This limitation is deemed acceptable, as it is analogous to the existing + limitation that data records cannot overwrite other data records without + producing an invalid tape. + + Because SIMH tape images do not carry physical parameters (e.g., recording + density), overwriting a tape image file containing gap metadata is + problematic if the density setting is not the same as that used during + recording. There is no way to establish a gap of a certain length + unequivocally in an image file, so this implementation establishes a gap of a + certain number of bytes that reflect the desired gap length at the bpi used + during writing. + + To write an erase gap, the implementation uses one of two approaches, + depending on whether or not the current tape position is at EOM. Erasing at + EOM presents no special difficulties; gap metadata markers are written for + the prescribed number of bytes. If the tape is not at EOM, then erasing must + take into account the existing record structure to ensure that a valid tape + image is maintained. + + The general approach is to erase for the nominal number of bytes but to + increase that length, if necessary, to ensure that a partially overwritten + data record at the end of the gap can be altered to maintain validity. + Because the smallest legal tape record requires space for two metadata + markers plus two data bytes, an erasure that would leave less than that + is increased to consume the entire record. Otherwise, the final record is + truncated appropriately. + + When reading in either direction, gap metadata markers are ignored (skipped) + until a record length header, EOF marker, EOM marker, or physical EOF is + encountered. Thus, tape images containing gap metadata are transparent to + the calling simulator. + + The permissibility of data record lengths that are not multiples of the + metadatum size presents a difficulty when reading. If such an "odd length" + record is written over a gap, half of a metadata marker will exist + immediately after the trailing record length. + + This condition is detected when reading forward by the appearance of a + "reversed" marker. The value appears reversed because the value is made up + of half of one marker and half of the next. This is handled by seeking + forward two bytes to resync (the stipulation above that the overwrite cannot + leave only two bytes of gap means that at least one "whole" metadata marker + will follow). Reading in reverse presents a more complex problem, because + half of the marker is from the preceding trailing record length marker and + therefore could be any of a range of values. However, that range is + restricted by the SIMH tape specification requirement that record length + metadata values must have bits 30:24 set to zero. This allows unambiguous + detection of the condition. + + The value chosen for gap metadata and the values reserved for "half-gap" + detection are: + + 0xFFFFFFFE - primary gap value + 0xFFFEFFFF - reserved (indicates half-gap in forward reads) + 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads) + 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads) + */ + +t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi) +{ +t_stat st; +t_mtrlnt meta, sbc, new_len, rec_size; +t_addr gap_pos = uptr->pos; +uint32 file_size, marker_count; +uint32 format = MT_GET_FMT (uptr); +uint32 gap_alloc = 0; /* gap allocated from tape */ +int32 gap_needed = (gaplen * bpi) / 10; /* gap remainder still needed */ +const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ +const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ + +MT_CLR_PNU (uptr); + +if ((uptr->flags & UNIT_ATT) == 0) /* not attached? */ + return MTSE_UNATT; +if (format != MTUF_F_STD) /* not SIMH fmt? */ + return MTSE_FMT; +if (sim_tape_wrp (uptr)) /* write protected? */ + return MTSE_WRP; + +file_size = sim_fsize (uptr->fileref); /* get file size */ +sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ + +/* Read tape records and allocate to gap until amount required is consumed. + + Read next metadatum from tape: + - EOF or EOM: allocate remainder of bytes needed. + - TMK or GAP: allocate sizeof(metadatum) bytes. + - Reverse GAP: allocate sizeof(metadatum) / 2 bytes. + - Data record: see below. + + Loop until bytes needed = 0. +*/ + +do { + sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */ + + if (ferror (uptr->fileref)) { /* read error? */ + uptr->pos = gap_pos; /* restore original position */ + MT_SET_PNU (uptr); /* position not updated */ + return sim_tape_ioerr (uptr); /* translate error */ + } + else + uptr->pos = uptr->pos + meta_size; /* move tape over datum */ + + if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */ + gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ + gap_needed = 0; + } + + else if ((meta == MTR_GAP) || (meta == MTR_TMK)) { /* gap or tape mark? */ + gap_alloc = gap_alloc + meta_size; /* allocate marker space */ + gap_needed = gap_needed - meta_size; /* reduce requirement */ + } + + else if (meta == MTR_FHGAP) { /* half gap? */ + uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ + gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */ + gap_needed = gap_needed - meta_size / 2; /* reduce requirement */ + } + + else if (uptr->pos + + MTR_L (meta) + meta_size > file_size) { /* rec len out of range? */ + gap_alloc = gap_alloc + gap_needed; /* presume overwritten tape */ + gap_needed = 0; /* allocate remainder */ + } + +/* Allocate a data record: + - Determine record size in bytes (including metadata) + - If record size - bytes needed < smallest allowed record size, + allocate entire record to gap, else allocate needed amount and + truncate data record to reflect remainder. +*/ + else { /* data record */ + sbc = MTR_L (meta); /* get record data length */ + rec_size = ((sbc + 1) & ~1) + meta_size * 2; /* overall size in bytes */ + + if (rec_size < gap_needed + min_rec_size) { /* rec too small? */ + uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* move tape */ + gap_alloc = gap_alloc + rec_size; /* allocate record */ + gap_needed = gap_needed - rec_size; /* reduce requirement */ + } + + else { /* record size OK */ + uptr->pos = uptr->pos - meta_size + gap_needed; /* position to end of gap */ + new_len = MTR_F (meta) | (sbc - gap_needed); /* truncate to new len */ + st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ + + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + + uptr->pos = uptr->pos + sbc - gap_needed; /* position to end of data */ + st = sim_tape_wrdata (uptr, new_len); /* write new rec len */ + + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + + gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ + gap_needed = 0; + } + } + } +while (gap_needed > 0); + +uptr->pos = gap_pos; /* reposition to gap start */ + +if (gap_alloc & (meta_size - 1)) { /* gap size "odd?" */ + st = sim_tape_wrdata (uptr, MTR_FHGAP); /* write half gap marker */ + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + uptr->pos = uptr->pos - meta_size / 2; /* realign position */ + gap_alloc = gap_alloc - 2; /* decrease gap to write */ + } + +marker_count = gap_alloc / meta_size; /* count of gap markers */ + +do { + st = sim_tape_wrdata (uptr, MTR_GAP); /* write gap markers */ + if (st != MTSE_OK) { /* write OK? */ + uptr->pos = gap_pos; /* restore orig pos */ + return st; /* PNU was set by wrdata */ + } + } +while (--marker_count > 0); + +return MTSE_OK; +} + +/* Space record forward */ + +t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc) +{ +t_stat st; + +st = sim_tape_rdlntf (uptr, bc); /* get record length */ +*bc = MTR_L (*bc); +return st; +} + +/* Space record reverse */ + +t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc) +{ +t_stat st; + +if (MT_TST_PNU (uptr)) { + MT_CLR_PNU (uptr); + *bc = 0; + return MTSE_OK; + } +st = sim_tape_rdlntr (uptr, bc); /* get record length */ +*bc = MTR_L (*bc); +return st; +} + +/* Rewind tape */ + +t_stat sim_tape_rewind (UNIT *uptr) +{ +uptr->pos = 0; +MT_CLR_PNU (uptr); +return MTSE_OK; +} + +/* Reset tape */ + +t_stat sim_tape_reset (UNIT *uptr) +{ +MT_CLR_PNU (uptr); +return SCPE_OK; +} + +/* Test for BOT */ + +t_bool sim_tape_bot (UNIT *uptr) +{ +uint32 f = MT_GET_FMT (uptr); + +return (uptr->pos <= fmts[f].bot)? TRUE: FALSE; +} + +/* Test for end of tape */ + +t_bool sim_tape_eot (UNIT *uptr) +{ +return (uptr->capac && (uptr->pos >= uptr->capac))? TRUE: FALSE; +} + +/* Test for write protect */ + +t_bool sim_tape_wrp (UNIT *uptr) +{ +return (uptr->flags & MTUF_WRP)? TRUE: FALSE; +} + +/* Process I/O error */ + +t_stat sim_tape_ioerr (UNIT *uptr) +{ +perror ("Magtape library I/O error"); +clearerr (uptr->fileref); +return MTSE_IOERR; +} + +/* Set tape format */ + +t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +int32 f; + +if (uptr == NULL) return SCPE_IERR; +if (cptr == NULL) return SCPE_ARG; +for (f = 0; f < MTUF_N_FMT; f++) { + if (fmts[f].name && (strcmp (cptr, fmts[f].name) == 0)) { + uptr->flags = (uptr->flags & ~MTUF_FMT) | + (f << MTUF_V_FMT) | fmts[f].uflags; + return SCPE_OK; + } + } +return SCPE_ARG; +} + +/* Show tape format */ + +t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +int32 f = MT_GET_FMT (uptr); + +if (fmts[f].name) fprintf (st, "%s format", fmts[f].name); +else fprintf (st, "invalid format"); +return SCPE_OK; +} + +/* Map a TPC format tape image */ + +uint32 sim_tape_tpc_map (UNIT *uptr, t_addr *map) +{ +t_addr tpos; +t_tpclnt bc; +uint32 i, objc; + +if ((uptr == NULL) || (uptr->fileref == NULL)) return 0; +for (objc = 0, tpos = 0;; ) { + sim_fseek (uptr->fileref, tpos, SEEK_SET); + i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref); + if (i == 0) break; + if (map) map[objc] = tpos; + objc++; + tpos = tpos + ((bc + 1) & ~1) + sizeof (t_tpclnt); + } +if (map) map[objc] = tpos; +return objc; +} + +/* Find the preceding record in a TPC file */ + +t_addr sim_tape_tpc_fnd (UNIT *uptr, t_addr *map) +{ +uint32 lo, hi, p; + + +if (map == NULL) return 0; +lo = 0; +hi = uptr->hwmark - 1; +do { + p = (lo + hi) >> 1; + if (uptr->pos == map[p]) + return ((p == 0)? map[p]: map[p - 1]); + else if (uptr->pos < map[p]) hi = p - 1; + else lo = p + 1; + } +while (lo <= hi); +return ((p == 0)? map[p]: map[p - 1]); +} + +/* Set tape capacity */ + +t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +extern uint32 sim_taddr_64; +t_addr cap; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_ARG; +if (uptr->flags & UNIT_ATT) return SCPE_ALATT; +cap = (t_addr) get_uint (cptr, 10, sim_taddr_64? 2000000: 2000, &r); +if (r != SCPE_OK) return SCPE_ARG; +uptr->capac = cap * ((t_addr) 1000000); +return SCPE_OK; +} + +/* Show tape capacity */ + +t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +if (uptr->capac) { + if (uptr->capac >= (t_addr) 1000000) + fprintf (st, "capacity=%dMB", (uint32) (uptr->capac / ((t_addr) 1000000))); + else if (uptr->capac >= (t_addr) 1000) + fprintf (st, "capacity=%dKB", (uint32) (uptr->capac / ((t_addr) 1000))); + else fprintf (st, "capacity=%dB", (uint32) uptr->capac); + } +else fprintf (st, "unlimited capacity"); +return SCPE_OK; +} diff --git a/sim_tape.h b/sim_tape.h new file mode 100644 index 0000000..dfaadce --- /dev/null +++ b/sim_tape.h @@ -0,0 +1,129 @@ +/* sim_tape.h: simulator tape support library definitions + + Copyright (c) 1993-2006, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 30-Aug-06 JDB Added erase gap support + 14-Feb-06 RMS Added variable tape capacity + 17-Dec-05 RMS Added write support for Paul Pierce 7b format + 02-May-05 RMS Added support for Paul Pierce 7b format +*/ + +#ifndef _SIM_TAPE_H_ +#define _SIM_TAPE_H_ 0 + +/* SIMH/E11 tape format */ + +typedef uint32 t_mtrlnt; /* magtape rec lnt */ + +#define MTR_TMK 0x00000000 /* tape mark */ +#define MTR_EOM 0xFFFFFFFF /* end of medium */ +#define MTR_GAP 0xFFFFFFFE /* primary gap */ +#define MTR_FHGAP 0xFFFEFFFF /* fwd half gap (overwrite) */ +#define MTR_RHGAP 0xFFFF0000 /* rev half gap (overwrite) */ +#define MTR_M_RHGAP (~0x000080FF) /* range mask for rev gap */ +#define MTR_MAXLEN 0x00FFFFFF /* max len is 24b */ +#define MTR_ERF 0x80000000 /* error flag */ +#define MTR_F(x) ((x) & MTR_ERF) /* record error flg */ +#define MTR_L(x) ((x) & ~MTR_ERF) /* record length */ + +/* TPC tape format */ + +typedef uint16 t_tpclnt; /* magtape rec lnt */ + +/* P7B tape format */ + +#define P7B_SOR 0x80 /* start of record */ +#define P7B_PAR 0x40 /* parity */ +#define P7B_DATA 0x3F /* data */ +#define P7B_DPAR (P7B_PAR|P7B_DATA) /* data and parity */ +#define P7B_EOF 0x0F /* eof character */ + +#define TPC_TMK 0x0000 /* tape mark */ + +/* Unit flags */ + +#define MTUF_V_PNU (UNIT_V_UF + 0) /* position not upd */ +#define MTUF_V_WLK (UNIT_V_UF + 1) /* write locked */ +#define MTUF_V_FMT (UNIT_V_UF + 2) /* tape file format */ +#define MTUF_W_FMT 3 /* 3b of formats */ +#define MTUF_N_FMT (1u << MTUF_W_FMT) /* number of formats */ +#define MTUF_M_FMT ((1u << MTUF_W_FMT) - 1) +#define MTUF_F_STD 0 /* SIMH format */ +#define MTUF_F_E11 1 /* E11 format */ +#define MTUF_F_TPC 2 /* TPC format */ +#define MTUF_F_P7B 3 /* P7B format */ +#define MUTF_F_TDF 4 /* TDF format */ +#define MTUF_V_UF (MTUF_V_FMT + MTUF_W_FMT) +#define MTUF_PNU (1u << MTUF_V_PNU) +#define MTUF_WLK (1u << MTUF_V_WLK) +#define MTUF_FMT (MTUF_M_FMT << MTUF_V_FMT) +#define MTUF_WRP (MTUF_WLK | UNIT_RO) + +#define MT_F_STD (MTUF_F_STD << MTUF_V_FMT) +#define MT_F_E11 (MTUF_F_E11 << MTUF_V_FMT) +#define MT_F_TPC (MTUF_F_TPC << MTUF_V_FMT) +#define MT_F_P7B (MTUF_F_P7B << MTUF_V_FMT) +#define MT_F_TDF (MTUF_F_TDF << MTUF_V_FMT) + +#define MT_SET_PNU(u) (u)->flags = (u)->flags | MTUF_PNU +#define MT_CLR_PNU(u) (u)->flags = (u)->flags & ~MTUF_PNU +#define MT_TST_PNU(u) ((u)->flags & MTUF_PNU) +#define MT_GET_FMT(u) (((u)->flags >> MTUF_V_FMT) & MTUF_M_FMT) + +/* Return status codes */ + +#define MTSE_OK 0 /* no error */ +#define MTSE_TMK 1 /* tape mark */ +#define MTSE_UNATT 2 /* unattached */ +#define MTSE_IOERR 3 /* IO error */ +#define MTSE_INVRL 4 /* invalid rec lnt */ +#define MTSE_FMT 5 /* invalid format */ +#define MTSE_BOT 6 /* beginning of tape */ +#define MTSE_EOM 7 /* end of medium */ +#define MTSE_RECE 8 /* error in record */ +#define MTSE_WRP 9 /* write protected */ + +/* Prototypes */ + +t_stat sim_tape_attach (UNIT *uptr, char *cptr); +t_stat sim_tape_detach (UNIT *uptr); +t_stat sim_tape_rdrecf (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); +t_stat sim_tape_rdrecr (UNIT *uptr, uint8 *buf, t_mtrlnt *bc, t_mtrlnt max); +t_stat sim_tape_wrrecf (UNIT *uptr, uint8 *buf, t_mtrlnt bc); +t_stat sim_tape_wrtmk (UNIT *uptr); +t_stat sim_tape_wreom (UNIT *uptr); +t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen, uint32 bpi); +t_stat sim_tape_sprecf (UNIT *uptr, t_mtrlnt *bc); +t_stat sim_tape_sprecr (UNIT *uptr, t_mtrlnt *bc); +t_stat sim_tape_rewind (UNIT *uptr); +t_stat sim_tape_reset (UNIT *uptr); +t_bool sim_tape_bot (UNIT *uptr); +t_bool sim_tape_wrp (UNIT *uptr); +t_bool sim_tape_eot (UNIT *uptr); +t_stat sim_tape_set_fmt (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_tape_show_fmt (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat sim_tape_set_capac (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_tape_show_capac (FILE *st, UNIT *uptr, int32 val, void *desc); + +#endif diff --git a/sim_timer.c b/sim_timer.c new file mode 100644 index 0000000..d2dbfa7 --- /dev/null +++ b/sim_timer.c @@ -0,0 +1,650 @@ +/* sim_timer.c: simulator timer library + + Copyright (c) 1993-2008, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 27-May-08 RMS Fixed bug in Linux idle routines (from Walter Mueller) + 18-Jun-07 RMS Modified idle to exclude counted delays + 22-Mar-07 RMS Added sim_rtcn_init_all + 17-Oct-06 RMS Added idle support (based on work by Mark Pizzolato) + Added throttle support + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 02-Jan-04 RMS Split out from SCP + + This library includes the following routines: + + sim_timer_init - initialize timing system + sim_rtc_init - initialize calibration + sim_rtc_calb - calibrate clock + sim_timer_init - initialize timing system + sim_idle - virtual machine idle + sim_os_msec - return elapsed time in msec + sim_os_sleep - sleep specified number of seconds + sim_os_ms_sleep - sleep specified number of milliseconds + + The calibration, idle, and throttle routines are OS-independent; the _os_ + routines are not. +*/ + +#include "sim_defs.h" +#include + +t_bool sim_idle_enab = FALSE; /* global flag */ + +static uint32 sim_idle_rate_ms = 0; +static uint32 sim_throt_ms_start = 0; +static uint32 sim_throt_ms_stop = 0; +static uint32 sim_throt_type = 0; +static uint32 sim_throt_val = 0; +static uint32 sim_throt_state = 0; +static int32 sim_throt_wait = 0; +extern int32 sim_interval, sim_switches; +extern FILE *sim_log; +extern UNIT *sim_clock_queue; + +t_stat sim_throt_svc (UNIT *uptr); + +UNIT sim_throt_unit = { UDATA (&sim_throt_svc, 0, 0) }; + +/* OS-dependent timer and clock routines */ + +/* VMS */ + +#if defined (VMS) + +#if defined (__VAX) +#define sys$gettim SYS$GETTIM +#endif + +#include +#include +#include + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ +uint32 quo, htod, tod[2]; +int32 i; + +sys$gettim (tod); /* time 0.1usec */ + +/* To convert to msec, must divide a 64b quantity by 10000. This is actually done + by dividing the 96b quantity 0'time by 10000, producing 64b of quotient, the + high 32b of which are discarded. This can probably be done by a clever multiply... +*/ + +quo = htod = 0; +for (i = 0; i < 64; i++) { /* 64b quo */ + htod = (htod << 1) | ((tod[1] >> 31) & 1); /* shift divd */ + tod[1] = (tod[1] << 1) | ((tod[0] >> 31) & 1); + tod[0] = tod[0] << 1; + quo = quo << 1; /* shift quo */ + if (htod >= 10000) { /* divd work? */ + htod = htod - 10000; /* subtract */ + quo = quo | 1; /* set quo bit */ + } + } +return quo; +} + +void sim_os_sleep (unsigned int sec) +{ +sleep (sec); +return; +} + +uint32 sim_os_ms_sleep_init (void) +{ +#if defined (__VAX) +return 10; /* VAX/VMS is 10ms */ +#else +return 1; /* Alpha/VMS is 1ms */ +#endif +} + +uint32 sim_os_ms_sleep (unsigned int msec) +{ +uint32 stime = sim_os_msec (); +uint32 qtime[2]; +int32 nsfactor = -10000; +static int32 zero = 0; + +lib$emul (&msec, &nsfactor, &zero, qtime); +sys$setimr (2, qtime, 0, 0); +sys$waitfr (2); +return sim_os_msec () - stime; +} + +/* Win32 routines */ + +#elif defined (_WIN32) + +#include + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ +if (sim_idle_rate_ms) return timeGetTime (); +else return GetTickCount (); +} + +void sim_os_sleep (unsigned int sec) +{ +Sleep (sec * 1000); +return; +} + +void sim_timer_exit (void) +{ +timeEndPeriod (sim_idle_rate_ms); +return; +} + +uint32 sim_os_ms_sleep_init (void) +{ +TIMECAPS timers; + +if (timeGetDevCaps (&timers, sizeof (timers)) != TIMERR_NOERROR) + return 0; +if ((timers.wPeriodMin == 0) || (timers.wPeriodMin > SIM_IDLE_MAX)) + return 0; +if (timeBeginPeriod (timers.wPeriodMin) != TIMERR_NOERROR) + return 0; +atexit (sim_timer_exit); +Sleep (1); +Sleep (1); +Sleep (1); +Sleep (1); +Sleep (1); +return timers.wPeriodMin; /* sim_idle_rate_ms */ +} + +uint32 sim_os_ms_sleep (unsigned int msec) +{ +uint32 stime = sim_os_msec(); + +Sleep (msec); +return sim_os_msec () - stime; +} + +/* OS/2 routines, from Bruce Ray */ + +#elif defined (__OS2__) + +const t_bool rtc_avail = FALSE; + +uint32 sim_os_msec () +{ +return 0; +} + +void sim_os_sleep (unsigned int sec) +{ +return; +} + +uint32 sim_os_ms_sleep_init (void) +{ +return FALSE; +} + +uint32 sim_os_ms_sleep (unsigned int msec) +{ +return 0; +} + +/* Metrowerks CodeWarrior Macintosh routines, from Ben Supnik */ + +#elif defined (__MWERKS__) && defined (macintosh) + +#include +#include +#include +#include +#include +#define NANOS_PER_MILLI 1000000 +#define MILLIS_PER_SEC 1000 + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec (void) +{ +unsigned long long micros; +UnsignedWide macMicros; +unsigned long millis; + +Microseconds (&macMicros); +micros = *((unsigned long long *) &macMicros); +millis = micros / 1000LL; +return (uint32) millis; +} + +void sim_os_sleep (unsigned int sec) +{ +sleep (sec); +return; +} + +uint32 sim_os_ms_sleep_init (void) +{ +return 1; +} + +uint32 sim_os_ms_sleep (unsigned int milliseconds) +{ +uint32 stime = sim_os_msec (); +struct timespec treq; + +treq.tv_sec = milliseconds / MILLIS_PER_SEC; +treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; +(void) nanosleep (&treq, NULL); +return sim_os_msec () - stime; +} + +#else + +/* UNIX routines */ + +#include +#include +#include +#define NANOS_PER_MILLI 1000000 +#define MILLIS_PER_SEC 1000 +#define sleep1Samples 100 + +const t_bool rtc_avail = TRUE; + +uint32 sim_os_msec () +{ +struct timeval cur; +struct timezone foo; +uint32 msec; + +gettimeofday (&cur, &foo); +msec = (((uint32) cur.tv_sec) * 1000) + (((uint32) cur.tv_usec) / 1000); +return msec; +} + +void sim_os_sleep (unsigned int sec) +{ +sleep (sec); +return; +} + +uint32 sim_os_ms_sleep_init (void) +{ +#if defined (_POSIX_SOURCE) /* POSIX-compliant */ + +struct timespec treq; +uint32 msec; + +if (clock_getres (CLOCK_REALTIME, &treq) != 0) + return 0; +msec = (treq.tv_nsec + (NANOS_PER_MILLI - 1)) / NANOS_PER_MILLI; +if (msec > SIM_IDLE_MAX) return 0; +return msec; + +#else /* others */ + +uint32 i, t1, t2, tot, tim; + +for (i = 0, tot = 0; i < sleep1Samples; i++) { + t1 = sim_os_msec (); + sim_os_ms_sleep (1); + t2 = sim_os_msec (); + tot += (t2 - t1); + } +tim = (tot + (sleep1Samples - 1)) / sleep1Samples; +if (tim == 0) tim = 1; +else if (tim > SIM_IDLE_MAX) tim = 0; +return tim; + +#endif +} + +uint32 sim_os_ms_sleep (unsigned int milliseconds) +{ +uint32 stime = sim_os_msec (); +struct timespec treq; + +treq.tv_sec = milliseconds / MILLIS_PER_SEC; +treq.tv_nsec = (milliseconds % MILLIS_PER_SEC) * NANOS_PER_MILLI; +(void) nanosleep (&treq, NULL); +return sim_os_msec () - stime; +} + +#endif + +/* OS independent clock calibration package */ + +static int32 rtc_ticks[SIM_NTIMERS] = { 0 }; /* ticks */ +static int32 rtc_hz[SIM_NTIMERS] = { 0 }; /* tick rate */ +static uint32 rtc_rtime[SIM_NTIMERS] = { 0 }; /* real time */ +static uint32 rtc_vtime[SIM_NTIMERS] = { 0 }; /* virtual time */ +static uint32 rtc_nxintv[SIM_NTIMERS] = { 0 }; /* next interval */ +static int32 rtc_based[SIM_NTIMERS] = { 0 }; /* base delay */ +static int32 rtc_currd[SIM_NTIMERS] = { 0 }; /* current delay */ +static int32 rtc_initd[SIM_NTIMERS] = { 0 }; /* initial delay */ + +void sim_rtcn_init_all (void) +{ +uint32 i; + +for (i = 0; i < SIM_NTIMERS; i++) { + if (rtc_initd[i] != 0) sim_rtcn_init (rtc_initd[i], i); + } +return; +} + +int32 sim_rtcn_init (int32 time, int32 tmr) +{ +if (time == 0) time = 1; +if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return time; +rtc_rtime[tmr] = sim_os_msec (); +rtc_vtime[tmr] = rtc_rtime[tmr]; +rtc_nxintv[tmr] = 1000; +rtc_ticks[tmr] = 0; +rtc_hz[tmr] = 0; +rtc_based[tmr] = time; +rtc_currd[tmr] = time; +rtc_initd[tmr] = time; +return time; +} + +int32 sim_rtcn_calb (int32 ticksper, int32 tmr) +{ +uint32 new_rtime, delta_rtime; +int32 delta_vtime; + +if ((tmr < 0) || (tmr >= SIM_NTIMERS)) return 10000; +rtc_hz[tmr] = ticksper; +rtc_ticks[tmr] = rtc_ticks[tmr] + 1; /* count ticks */ +if (rtc_ticks[tmr] < ticksper) return rtc_currd[tmr]; /* 1 sec yet? */ +rtc_ticks[tmr] = 0; /* reset ticks */ +if (!rtc_avail) return rtc_currd[tmr]; /* no timer? */ +new_rtime = sim_os_msec (); /* wall time */ +if (new_rtime < rtc_rtime[tmr]) { /* time running backwards? */ + rtc_rtime[tmr] = new_rtime; /* reset wall time */ + return rtc_currd[tmr]; /* can't calibrate */ + } +delta_rtime = new_rtime - rtc_rtime[tmr]; /* elapsed wtime */ +rtc_rtime[tmr] = new_rtime; /* adv wall time */ +rtc_vtime[tmr] = rtc_vtime[tmr] + 1000; /* adv sim time */ +if (delta_rtime > 30000) /* gap too big? */ + return rtc_initd[tmr]; /* can't calibr */ +if (delta_rtime == 0) /* gap too small? */ + rtc_based[tmr] = rtc_based[tmr] * ticksper; /* slew wide */ +else rtc_based[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / + ((double) delta_rtime)); /* new base rate */ +delta_vtime = rtc_vtime[tmr] - rtc_rtime[tmr]; /* gap */ +if (delta_vtime > SIM_TMAX) delta_vtime = SIM_TMAX; /* limit gap */ +else if (delta_vtime < -SIM_TMAX) delta_vtime = -SIM_TMAX; +rtc_nxintv[tmr] = 1000 + delta_vtime; /* next wtime */ +rtc_currd[tmr] = (int32) (((double) rtc_based[tmr] * (double) rtc_nxintv[tmr]) / + 1000.0); /* next delay */ +if (rtc_based[tmr] <= 0) rtc_based[tmr] = 1; /* never negative or zero! */ +if (rtc_currd[tmr] <= 0) rtc_currd[tmr] = 1; /* never negative or zero! */ +return rtc_currd[tmr]; +} + +/* Prior interfaces - default to timer 0 */ + +int32 sim_rtc_init (int32 time) +{ +return sim_rtcn_init (time, 0); +} + +int32 sim_rtc_calb (int32 ticksper) +{ +return sim_rtcn_calb (ticksper, 0); +} + +/* sim_timer_init - get minimum sleep time available on this host */ + +t_bool sim_timer_init (void) +{ +sim_idle_enab = FALSE; /* init idle off */ +sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */ +return (sim_idle_rate_ms != 0); +} + +/* sim_idle - idle simulator until next event or for specified interval + + Inputs: + tmr = calibrated timer to use + + Must solve the linear equation + + ms_to_wait = w * ms_per_wait + + Or + w = ms_to_wait / ms_per_wait +*/ + +t_bool sim_idle (uint32 tmr, t_bool sin_cyc) +{ +uint32 cyc_ms, w_ms, w_idle, act_ms; +int32 act_cyc; + +if ((sim_clock_queue == NULL) || /* clock queue empty? */ + ((sim_clock_queue->flags & UNIT_IDLE) == 0)) { /* event not idle-able? */ + if (sin_cyc) sim_interval = sim_interval - 1; + return FALSE; + } +cyc_ms = (rtc_currd[tmr] * rtc_hz[tmr]) / 1000; /* cycles per msec */ +if ((sim_idle_rate_ms == 0) || (cyc_ms == 0)) { /* not possible? */ + if (sin_cyc) sim_interval = sim_interval - 1; + return FALSE; + } +w_ms = (uint32) sim_interval / cyc_ms; /* ms to wait */ +w_idle = w_ms / sim_idle_rate_ms; /* intervals to wait */ +if (w_idle == 0) { /* none? */ + if (sin_cyc) sim_interval = sim_interval - 1; + return FALSE; + } +act_ms = sim_os_ms_sleep (w_idle); /* wait */ +act_cyc = act_ms * cyc_ms; +if (sim_interval > act_cyc) + sim_interval = sim_interval - act_cyc; +else sim_interval = 1; +return TRUE; +} + +/* Set idling - implicitly disables throttling */ + +t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +if (sim_idle_rate_ms == 0) return SCPE_NOFNC; +if ((val != 0) && (sim_idle_rate_ms > (uint32) val)) + return SCPE_NOFNC; +sim_idle_enab = TRUE; +if (sim_throt_type != SIM_THROT_NONE) { + sim_set_throt (0, NULL); + printf ("Throttling disabled\n"); + if (sim_log) fprintf (sim_log, "Throttling disabled\n"); + } +return SCPE_OK; +} + +/* Clear idling */ + +t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +sim_idle_enab = FALSE; +return SCPE_OK; +} + +/* Show idling */ + +t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +fprintf (st, sim_idle_enab? "idle enabled": "idle disabled"); +return SCPE_OK; +} + +/* Throttling package */ + +t_stat sim_set_throt (int32 arg, char *cptr) +{ +char *tptr, c; +t_value val; + +if (arg == 0) { + if ((cptr != 0) && (*cptr != 0)) return SCPE_ARG; + sim_throt_type = SIM_THROT_NONE; + sim_throt_cancel (); + } +else if (sim_idle_rate_ms == 0) return SCPE_NOFNC; +else { + val = strtotv (cptr, &tptr, 10); + if (cptr == tptr) return SCPE_ARG; + c = toupper (*tptr++); + if (*tptr != 0) return SCPE_ARG; + if (c == 'M') sim_throt_type = SIM_THROT_MCYC; + else if (c == 'K') sim_throt_type = SIM_THROT_KCYC; + else if ((c == '%') && (val > 0) && (val < 100)) + sim_throt_type = SIM_THROT_PCT; + else return SCPE_ARG; + if (sim_idle_enab) { + printf ("Idling disabled\n"); + if (sim_log) fprintf (sim_log, "Idling disabled\n"); + sim_clr_idle (NULL, 0, NULL, NULL); + } + sim_throt_val = (uint32) val; + } +return SCPE_OK; +} + +t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr) +{ +if (sim_idle_rate_ms == 0) + fprintf (st, "Throttling not available\n"); +else { + switch (sim_throt_type) { + + case SIM_THROT_MCYC: + fprintf (st, "Throttle = %d megacycles\n", sim_throt_val); + break; + + case SIM_THROT_KCYC: + fprintf (st, "Throttle = %d kilocycles\n", sim_throt_val); + break; + + case SIM_THROT_PCT: + fprintf (st, "Throttle = %d%%\n", sim_throt_val); + break; + + default: + fprintf (st, "Throttling disabled\n"); + break; + } + + if (sim_switches & SWMASK ('D')) { + fprintf (st, "Wait rate = %d ms\n", sim_idle_rate_ms); + if (sim_throt_type != 0) + fprintf (st, "Throttle interval = %d cycles\n", sim_throt_wait); + } + } +return SCPE_OK; +} + +void sim_throt_sched (void) +{ +sim_throt_state = 0; +if (sim_throt_type) + sim_activate (&sim_throt_unit, SIM_THROT_WINIT); +return; +} + +void sim_throt_cancel (void) +{ +sim_cancel (&sim_throt_unit); +} + +/* Throttle service + + Throttle service has three distinct states + + 0 take initial measurement + 1 take final measurement, calculate wait values + 2 periodic waits to slow down the CPU +*/ + +t_stat sim_throt_svc (UNIT *uptr) +{ +uint32 delta_ms; +double a_cps, d_cps; + +switch (sim_throt_state) { + + case 0: /* take initial reading */ + sim_throt_ms_start = sim_os_msec (); + sim_throt_wait = SIM_THROT_WST; + sim_throt_state++; /* next state */ + break; /* reschedule */ + + case 1: /* take final reading */ + sim_throt_ms_stop = sim_os_msec (); + delta_ms = sim_throt_ms_stop - sim_throt_ms_start; + if (delta_ms < SIM_THROT_MSMIN) { /* not enough time? */ + if (sim_throt_wait >= 100000000) { /* too many inst? */ + sim_throt_state = 0; /* fails in 32b! */ + return SCPE_OK; + } + sim_throt_wait = sim_throt_wait * SIM_THROT_WMUL; + sim_throt_ms_start = sim_throt_ms_stop; + } + else { /* long enough */ + a_cps = ((double) sim_throt_wait) * 1000.0 / (double) delta_ms; + if (sim_throt_type == SIM_THROT_MCYC) /* calc desired cps */ + d_cps = (double) sim_throt_val * 1000000.0; + else if (sim_throt_type == SIM_THROT_KCYC) + d_cps = (double) sim_throt_val * 1000.0; + else d_cps = (a_cps * ((double) sim_throt_val)) / 100.0; + if (d_cps >= a_cps) { + sim_throt_state = 0; + return SCPE_OK; + } + sim_throt_wait = (int32) /* time between waits */ + ((a_cps * d_cps * ((double) sim_idle_rate_ms)) / + (1000.0 * (a_cps - d_cps))); + if (sim_throt_wait < SIM_THROT_WMIN) { /* not long enough? */ + sim_throt_state = 0; + return SCPE_OK; + } + sim_throt_state++; +// fprintf (stderr, "Throttle values a_cps = %f, d_cps = %f, wait = %d\n", +// a_cps, d_cps, sim_throt_wait); + } + break; + + case 2: /* throttling */ + sim_os_ms_sleep (1); + break; + } + +sim_activate (uptr, sim_throt_wait); /* reschedule */ +return SCPE_OK; +} diff --git a/sim_timer.h b/sim_timer.h new file mode 100644 index 0000000..c7660fd --- /dev/null +++ b/sim_timer.h @@ -0,0 +1,69 @@ +/* sim_timer.h: simulator timer library headers + + Copyright (c) 1993-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + 28-Apr-07 RMS Added sim_rtc_init_all + 17-Oct-06 RMS Added idle support + 02-Jan-04 RMS Split out from SCP +*/ + +#ifndef _SIM_TIMER_H_ +#define _SIM_TIMER_H_ 0 + +#define SIM_NTIMERS 8 /* # timers */ +#define SIM_TMAX 500 /* max timer makeup */ + +#define SIM_IDLE_CAL 10 /* ms to calibrate */ +#define SIM_IDLE_MAX 10 /* max granularity idle */ + +#define SIM_THROT_WINIT 1000 /* cycles to skip */ +#define SIM_THROT_WST 10000 /* initial wait */ +#define SIM_THROT_WMUL 4 /* multiplier */ +#define SIM_THROT_WMIN 100 /* min wait */ +#define SIM_THROT_MSMIN 10 /* min for measurement */ +#define SIM_THROT_NONE 0 /* throttle parameters */ +#define SIM_THROT_MCYC 1 +#define SIM_THROT_KCYC 2 +#define SIM_THROT_PCT 3 + +t_bool sim_timer_init (void); +int32 sim_rtcn_init (int32 time, int32 tmr); +void sim_rtcn_init_all (void); +int32 sim_rtcn_calb (int32 ticksper, int32 tmr); +int32 sim_rtc_init (int32 time); +int32 sim_rtc_calb (int32 ticksper); +t_bool sim_idle (uint32 tmr, t_bool sin_cyc); +t_stat sim_set_throt (int32 arg, char *cptr); +t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr); +t_stat sim_set_idle (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_clr_idle (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat sim_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc); +void sim_throt_sched (void); +void sim_throt_cancel (void); +uint32 sim_os_msec (void); +void sim_os_sleep (unsigned int sec); +uint32 sim_os_ms_sleep (unsigned int msec); +uint32 sim_os_ms_sleep_init (void); + +#endif diff --git a/sim_tmxr.c b/sim_tmxr.c new file mode 100644 index 0000000..16b958a --- /dev/null +++ b/sim_tmxr.c @@ -0,0 +1,690 @@ +/* sim_tmxr.c: Telnet terminal multiplexor library + + Copyright (c) 2001-2007, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + Based on the original DZ11 simulator by Thord Nilson, as updated by + Arthur Krewat. + + 11-Apr-07 JDB Worked around Telnet negotiation problem with QCTerm + 16-Aug-05 RMS Fixed C++ declaration and cast problems + 29-Jun-05 RMS Extended tmxr_dscln to support unit array devices + Fixed bug in SET LOG/NOLOG + 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array + Added tmxr_linemsg, circular output pointers, logging + (from Mark Pizzolato) + 29-Dec-03 RMS Added output stall support + 01-Nov-03 RMS Cleaned up attach routine + 09-Mar-03 RMS Fixed bug in SHOW CONN + 22-Dec-02 RMS Fixed bugs in IAC+IAC receive and transmit sequences + Added support for received break (all from by Mark Pizzolato) + Fixed bug in attach + 31-Oct-02 RMS Fixed bug in 8b (binary) support + 22-Aug-02 RMS Added tmxr_open_master, tmxr_close_master + 30-Dec-01 RMS Added tmxr_fstats, tmxr_dscln, renamed tmxr_fstatus + 03-Dec-01 RMS Changed tmxr_fconns for extended SET/SHOW + 20-Oct-01 RMS Fixed bugs in read logic (found by Thord Nilson). + Added tmxr_rqln, tmxr_tqln + + This library includes: + + tmxr_poll_conn - poll for connection + tmxr_reset_ln - reset line + tmxr_getc_ln - get character for line + tmxr_poll_rx - poll receive + tmxr_putc_ln - put character for line + tmxr_poll_tx - poll transmit + tmxr_open_master - open master connection + tmxr_close_master - close master connection + tmxr_attach - attach terminal multiplexor + tmxr_detach - detach terminal multiplexor + tmxr_ex - (null) examine + tmxr_dep - (null) deposit + tmxr_msg - send message to socket + tmxr_linemsg - send message to line + tmxr_fconns - output connection status + tmxr_fstats - output connection statistics + tmxr_dscln - disconnect line (SET routine) + tmxr_rqln - number of available characters for line + tmxr_tqln - number of buffered characters for line + + All routines are OS-independent. +*/ + +#include "sim_defs.h" +#include "sim_sock.h" +#include "sim_tmxr.h" +#include + +/* Telnet protocol constants - negatives are for init'ing signed char data */ + +#define TN_IAC -1 /* protocol delim */ +#define TN_DONT -2 /* dont */ +#define TN_DO -3 /* do */ +#define TN_WONT -4 /* wont */ +#define TN_WILL -5 /* will */ +#define TN_BRK -13 /* break */ +#define TN_BIN 0 /* bin */ +#define TN_ECHO 1 /* echo */ +#define TN_SGA 3 /* sga */ +#define TN_LINE 34 /* line mode */ +#define TN_CR 015 /* carriage return */ +#define TN_LF 012 /* line feed */ +#define TN_NUL 000 /* null */ + +/* Telnet line states */ + +#define TNS_NORM 000 /* normal */ +#define TNS_IAC 001 /* IAC seen */ +#define TNS_WILL 002 /* WILL seen */ +#define TNS_WONT 003 /* WONT seen */ +#define TNS_SKIP 004 /* skip next cmd */ +#define TNS_CRPAD 005 /* CR padding */ + +void tmxr_rmvrc (TMLN *lp, int32 p); +int32 tmxr_send_buffered_data (TMLN *lp); +TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp); + +extern int32 sim_switches; +extern char sim_name[]; +extern FILE *sim_log; +extern uint32 sim_os_msec (void); + +/* Poll for new connection + + Called from unit service routine to test for new connection + + Inputs: + *mp = pointer to terminal multiplexor descriptor + Outputs: + line number activated, -1 if none +*/ + +int32 tmxr_poll_conn (TMXR *mp) +{ +SOCKET newsock; +TMLN *lp; +int32 i; +uint32 ipaddr; +static char mantra[] = { + TN_IAC, TN_WILL, TN_LINE, + TN_IAC, TN_WILL, TN_SGA, + TN_IAC, TN_WILL, TN_ECHO, + TN_IAC, TN_WILL, TN_BIN, + TN_IAC, TN_DO, TN_BIN + }; + +newsock = sim_accept_conn (mp->master, &ipaddr); /* poll connect */ +if (newsock != INVALID_SOCKET) { /* got a live one? */ + for (i = 0; i < mp->lines; i++) { /* find avail line */ + lp = mp->ldsc + i; /* ptr to ln desc */ + if (lp->conn == 0) break; /* available? */ + } + if (i >= mp->lines) { /* all busy? */ + tmxr_msg (newsock, "All connections busy\r\n"); + sim_close_sock (newsock, 0); + } + else { + lp = mp->ldsc + i; /* get line desc */ + lp->conn = newsock; /* record connection */ + lp->ipad = ipaddr; /* ip address */ + lp->cnms = sim_os_msec (); /* time of conn */ + lp->rxbpr = lp->rxbpi = 0; /* init buf pointers */ + lp->txbpr = lp->txbpi = 0; + lp->rxcnt = lp->txcnt = 0; /* init counters */ + lp->tsta = 0; /* init telnet state */ + lp->xmte = 1; /* enable transmit */ + lp->dstb = 0; /* default bin mode */ + sim_write_sock (newsock, mantra, 15); + tmxr_linemsg (lp, "\n\r\nConnected to the "); + tmxr_linemsg (lp, sim_name); + tmxr_linemsg (lp, " simulator\r\n\n"); + tmxr_poll_tx (mp); /* flush output */ + return i; + } + } /* end if newsock */ +return -1; +} + +/* Reset line */ + +void tmxr_reset_ln (TMLN *lp) +{ +if (lp->txlog) fflush (lp->txlog); /* dump log */ +tmxr_send_buffered_data (lp); /* send buffered data */ +sim_close_sock (lp->conn, 0); /* reset conn */ +lp->conn = lp->tsta = 0; /* reset state */ +lp->rxbpr = lp->rxbpi = 0; +lp->txbpr = lp->txbpi = 0; +lp->xmte = 1; +lp->dstb = 0; +return; +} + +/* Get character from specific line + + Inputs: + *lp = pointer to terminal line descriptor + Output: + valid + char, 0 if line +*/ + +int32 tmxr_getc_ln (TMLN *lp) +{ +int32 j, val = 0; +uint32 tmp; + +if (lp->conn && lp->rcve) { /* conn & enb? */ + j = lp->rxbpi - lp->rxbpr; /* # input chrs */ + if (j) { /* any? */ + tmp = lp->rxb[lp->rxbpr]; /* get char */ + val = TMXR_VALID | (tmp & 0377); /* valid + chr */ + if (lp->rbr[lp->rxbpr]) val = val | SCPE_BREAK; /* break? */ + lp->rxbpr = lp->rxbpr + 1; /* adv pointer */ + } + } /* end if conn */ +if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */ + lp->rxbpi = lp->rxbpr = 0; +return val; +} + +/* Poll for input + + Inputs: + *mp = pointer to terminal multiplexor descriptor + Outputs: none +*/ + +void tmxr_poll_rx (TMXR *mp) +{ +int32 i, nbytes, j; +TMLN *lp; + +for (i = 0; i < mp->lines; i++) { /* loop thru lines */ + lp = mp->ldsc + i; /* get line desc */ + if (!lp->conn || !lp->rcve) continue; /* skip if !conn */ + + nbytes = 0; + if (lp->rxbpi == 0) /* need input? */ + nbytes = sim_read_sock (lp->conn, /* yes, read */ + &(lp->rxb[lp->rxbpi]), /* leave spc for */ + TMXR_MAXBUF - TMXR_GUARD); /* Telnet cruft */ + else if (lp->tsta) /* in Telnet seq? */ + nbytes = sim_read_sock (lp->conn, /* yes, read to end */ + &(lp->rxb[lp->rxbpi]), + TMXR_MAXBUF - lp->rxbpi); + if (nbytes < 0) tmxr_reset_ln (lp); /* closed? reset ln */ + else if (nbytes > 0) { /* if data rcvd */ + j = lp->rxbpi; /* start of data */ + memset (&lp->rbr[j], 0, nbytes); /* clear status */ + lp->rxbpi = lp->rxbpi + nbytes; /* adv pointers */ + lp->rxcnt = lp->rxcnt + nbytes; + +/* Examine new data, remove TELNET cruft before making input available */ + + for (; j < lp->rxbpi; ) { /* loop thru char */ + signed char tmp = lp->rxb[j]; /* get char */ + switch (lp->tsta) { /* case tlnt state */ + + case TNS_NORM: /* normal */ + if (tmp == TN_IAC) { /* IAC? */ + lp->tsta = TNS_IAC; /* change state */ + tmxr_rmvrc (lp, j); /* remove char */ + break; + } + if ((tmp == TN_CR) && lp->dstb) /* CR, no bin */ + lp->tsta = TNS_CRPAD; /* skip pad char */ + j = j + 1; /* advance j */ + break; + + case TNS_IAC: /* IAC prev */ + if ((tmp == TN_IAC) & !lp->dstb) { /* IAC + IAC, bin? */ + lp->tsta = TNS_NORM; /* treat as normal */ + j = j + 1; /* advance j */ + break; /* keep IAC */ + } + if (tmp == TN_BRK) { /* IAC + BRK? */ + lp->tsta = TNS_NORM; /* treat as normal */ + lp->rxb[j] = 0; /* char is null */ + lp->rbr[j] = 1; /* flag break */ + j = j + 1; /* advance j */ + break; + } + if (tmp == TN_WILL) /* IAC + WILL? */ + lp->tsta = TNS_WILL; + else if (tmp == TN_WONT) /* IAC + WONT? */ + lp->tsta = TNS_WONT; + else lp->tsta = TNS_SKIP; /* IAC + other */ + tmxr_rmvrc (lp, j); /* remove char */ + break; + + case TNS_WILL: case TNS_WONT: /* IAC+WILL/WONT prev */ + if (tmp == TN_BIN) { /* BIN? */ + if (lp->tsta == TNS_WILL) lp->dstb = 0; + else lp->dstb = 1; + } + + /* Negotiation with the HP terminal emulator "QCTerm" is not working. + QCTerm says "WONT BIN" but sends bare CRs. RFC 854 says: + + Note that "CR LF" or "CR NUL" is required in both directions + (in the default ASCII mode), to preserve the symmetry of the + NVT model. ...The protocol requires that a NUL be inserted + following a CR not followed by a LF in the data stream. + + Until full negotiation is implemented, we work around the problem + by checking the character following the CR in non-BIN mode and + strip it only if it is LF or NUL. This should not affect + conforming clients. + */ + + case TNS_CRPAD: /* only LF or NUL should follow CR */ + lp->tsta = TNS_NORM; /* next normal */ + if ((tmp == TN_LF) || /* CR + LF ? */ + (tmp == TN_NUL)) /* CR + NUL? */ + tmxr_rmvrc (lp, j); /* remove it */ + break; + + case TNS_SKIP: default: /* skip char */ + lp->tsta = TNS_NORM; /* next normal */ + tmxr_rmvrc (lp, j); /* remove char */ + break; + } /* end case state */ + } /* end for char */ + } /* end else nbytes */ + } /* end for lines */ +for (i = 0; i < mp->lines; i++) { /* loop thru lines */ + lp = mp->ldsc + i; /* get line desc */ + if (lp->rxbpi == lp->rxbpr) /* if buf empty, */ + lp->rxbpi = lp->rxbpr = 0; /* reset pointers */ + } /* end for */ +return; +} + +/* Return count of available characters for line */ + +int32 tmxr_rqln (TMLN *lp) +{ +return (lp->rxbpi - lp->rxbpr); +} + +/* Remove character p (and matching status) from line l input buffer */ + +void tmxr_rmvrc (TMLN *lp, int32 p) +{ +for ( ; p < lp->rxbpi; p++) { + lp->rxb[p] = lp->rxb[p + 1]; + lp->rbr[p] = lp->rbr[p + 1]; + } +lp->rxbpi = lp->rxbpi - 1; +return; +} + +/* Store character in line buffer + + Inputs: + *lp = pointer to line descriptor + chr = characters + Outputs: + status = ok, connection lost, or stall +*/ + +t_stat tmxr_putc_ln (TMLN *lp, int32 chr) +{ +if (lp->txlog) fputc (chr, lp->txlog); /* log if available */ +if (lp->conn == 0) return SCPE_LOST; /* no conn? lost */ +if (tmxr_tqln (lp) < (TMXR_MAXBUF - 1)) { /* room for char (+ IAC)? */ + lp->txb[lp->txbpi] = (char) chr; /* buffer char */ + lp->txbpi = lp->txbpi + 1; /* adv pointer */ + if (lp->txbpi >= TMXR_MAXBUF) lp->txbpi = 0; /* wrap? */ + if ((char) chr == TN_IAC) { /* IAC? */ + lp->txb[lp->txbpi] = (char) chr; /* IAC + IAC */ + lp->txbpi = lp->txbpi + 1; /* adv pointer */ + if (lp->txbpi >= TMXR_MAXBUF) lp->txbpi = 0; /* wrap? */ + } + if (tmxr_tqln (lp) > (TMXR_MAXBUF - TMXR_GUARD)) /* near full? */ + lp->xmte = 0; /* disable line */ + return SCPE_OK; /* char sent */ + } +lp->xmte = 0; /* no room, dsbl line */ +return SCPE_STALL; /* char not sent */ +} + +/* Poll for output + + Inputs: + *mp = pointer to terminal multiplexor descriptor + Outputs: + none +*/ + +void tmxr_poll_tx (TMXR *mp) +{ +int32 i, nbytes; +TMLN *lp; + +for (i = 0; i < mp->lines; i++) { /* loop thru lines */ + lp = mp->ldsc + i; /* get line desc */ + if (lp->conn == 0) continue; /* skip if !conn */ + nbytes = tmxr_send_buffered_data (lp); /* buffered bytes */ + if (nbytes == 0) lp->xmte = 1; /* buf empty? enab line */ + } /* end for */ +return; +} + +/* Send buffered data across network + + Inputs: + *lp = pointer to line descriptor + Outputs: + returns number of bytes still buffered +*/ + +int32 tmxr_send_buffered_data (TMLN *lp) +{ +int32 nbytes, sbytes; + +nbytes = tmxr_tqln(lp); /* avail bytes */ +if (nbytes) { /* >0? write */ + if (lp->txbpr < lp->txbpi) /* no wrap? */ + sbytes = sim_write_sock (lp->conn, /* write all data */ + &(lp->txb[lp->txbpr]), nbytes); + else sbytes = sim_write_sock (lp->conn, /* write to end buf */ + &(lp->txb[lp->txbpr]), TMXR_MAXBUF - lp->txbpr); + if (sbytes != SOCKET_ERROR) { /* ok? */ + lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ + if (lp->txbpr >= TMXR_MAXBUF) lp->txbpr = 0; /* wrap? */ + lp->txcnt = lp->txcnt + sbytes; /* update counts */ + nbytes = nbytes - sbytes; + } + if (nbytes && (lp->txbpr == 0)) { /* more data and wrap? */ + sbytes = sim_write_sock (lp->conn, lp->txb, nbytes); + if (sbytes != SOCKET_ERROR) { /* ok */ + lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ + if (lp->txbpr >= TMXR_MAXBUF) lp->txbpr = 0;/* wrap? */ + lp->txcnt = lp->txcnt + sbytes; /* update counts */ + nbytes = nbytes - sbytes; + } + } + } /* end if nbytes */ +return nbytes; +} + +/* Return count of buffered characters for line */ + +int32 tmxr_tqln (TMLN *lp) +{ +return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? TMXR_MAXBUF: 0)); +} + +/* Open master socket */ + +t_stat tmxr_open_master (TMXR *mp, char *cptr) +{ +int32 i, port; +SOCKET sock; +TMLN *lp; +t_stat r; + +port = (int32) get_uint (cptr, 10, 65535, &r); /* get port */ +if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG; +sock = sim_master_sock (port); /* make master socket */ +if (sock == INVALID_SOCKET) return SCPE_OPENERR; /* open error */ +printf ("Listening on port %d (socket %d)\n", port, sock); +if (sim_log) fprintf (sim_log, + "Listening on port %d (socket %d)\n", port, sock); +mp->port = port; /* save port */ +mp->master = sock; /* save master socket */ +for (i = 0; i < mp->lines; i++) { /* initialize lines */ + lp = mp->ldsc + i; + lp->conn = lp->tsta = 0; + lp->rxbpi = lp->rxbpr = 0; + lp->txbpi = lp->txbpr = 0; + lp->rxcnt = lp->txcnt = 0; + lp->xmte = 1; + lp->dstb = 0; + } +return SCPE_OK; +} + +/* Attach unit to master socket */ + +t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr) +{ +char* tptr; +t_stat r; + +tptr = (char *) malloc (strlen (cptr) + 1); /* get string buf */ +if (tptr == NULL) return SCPE_MEM; /* no more mem? */ +r = tmxr_open_master (mp, cptr); /* open master socket */ +if (r != SCPE_OK) { /* error? */ + free (tptr); /* release buf */ + return SCPE_OPENERR; + } +strcpy (tptr, cptr); /* copy port */ +uptr->filename = tptr; /* save */ +uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ +return SCPE_OK; +} + +/* Close master socket */ + +t_stat tmxr_close_master (TMXR *mp) +{ +int32 i; +TMLN *lp; + +for (i = 0; i < mp->lines; i++) { /* loop thru conn */ + lp = mp->ldsc + i; + if (lp->conn) { + tmxr_linemsg (lp, "\r\nDisconnected from the "); + tmxr_linemsg (lp, sim_name); + tmxr_linemsg (lp, " simulator\r\n\n"); + tmxr_reset_ln (lp); + } /* end if conn */ + } /* end for */ +sim_close_sock (mp->master, 1); /* close master socket */ +mp->master = 0; +return SCPE_OK; +} + +/* Detach unit from master socket */ + +t_stat tmxr_detach (TMXR *mp, UNIT *uptr) +{ +if (!(uptr->flags & UNIT_ATT)) return SCPE_OK; /* attached? */ +tmxr_close_master (mp); /* close master socket */ +free (uptr->filename); /* free port string */ +uptr->filename = NULL; +uptr->flags = uptr->flags & ~UNIT_ATT; /* not attached */ +return SCPE_OK; +} + +/* Stub examine and deposit */ + +t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) +{ +return SCPE_NOFNC; +} + +t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw) +{ +return SCPE_NOFNC; +} + +/* Output message to socket or line descriptor */ + +void tmxr_msg (SOCKET sock, char *msg) +{ +if (sock) sim_write_sock (sock, msg, strlen (msg)); +return; +} + +void tmxr_linemsg (TMLN *lp, char *msg) +{ +int32 len; + +for (len = strlen (msg); len > 0; --len) + tmxr_putc_ln (lp, *msg++); +return; +} + +/* Print connections - used only in named SHOW command */ + +void tmxr_fconns (FILE *st, TMLN *lp, int32 ln) +{ +if (ln >= 0) fprintf (st, "line %d: ", ln); +if (lp->conn) { + int32 o1, o2, o3, o4, hr, mn, sc; + uint32 ctime; + + o1 = (lp->ipad >> 24) & 0xFF; + o2 = (lp->ipad >> 16) & 0xFF; + o3 = (lp->ipad >> 8) & 0xFF; + o4 = (lp->ipad) & 0xFF; + ctime = (sim_os_msec () - lp->cnms) / 1000; + hr = ctime / 3600; + mn = (ctime / 60) % 60; + sc = ctime % 60; + fprintf (st, "IP address %d.%d.%d.%d", o1, o2, o3, o4); + if (ctime) fprintf (st, ", connected %02d:%02d:%02d\n", hr, mn, sc); + } +else fprintf (st, "line disconnected\n"); +if (lp->txlog) fprintf (st, "Logging to %s\n", lp->txlogname); +return; +} + +/* Print statistics - used only in named SHOW command */ + +void tmxr_fstats (FILE *st, TMLN *lp, int32 ln) +{ +static const char *enab = "on"; +static const char *dsab = "off"; + +if (ln >= 0) fprintf (st, "line %d: ", ln); +if (lp->conn) { + fprintf (st, "input (%s) queued/total = %d/%d, ", + (lp->rcve? enab: dsab), + lp->rxbpi - lp->rxbpr, lp->rxcnt); + fprintf (st, "output (%s) queued/total = %d/%d\n", + (lp->xmte? enab: dsab), + lp->txbpi - lp->txbpr, lp->txcnt); + } +else fprintf (st, "line disconnected\n"); +return; +} + +/* Disconnect line */ + +t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; +int32 ln; +t_stat r; + +if (mp == NULL) return SCPE_IERR; +if (val) { /* = n form */ + if (cptr == NULL) return SCPE_ARG; + ln = (int32) get_uint (cptr, 10, mp->lines - 1, &r); + if (r != SCPE_OK) return SCPE_ARG; + lp = mp->ldsc + ln; + } +else { + lp = tmxr_find_ldsc (uptr, 0, mp); + if (lp == NULL) return SCPE_IERR; + } +if (lp->conn) { + tmxr_linemsg (lp, "\r\nOperator disconnected line\r\n\n"); + tmxr_reset_ln (lp); + } +return SCPE_OK; +} + +/* Enable logging for line */ + +t_stat tmxr_set_log (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; + +if (cptr == NULL) return SCPE_2FARG; /* no file name? */ +lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ +if (lp == NULL) return SCPE_IERR; +if (lp->txlog) tmxr_set_nolog (NULL, val, NULL, desc); /* close existing log */ +lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */ +if (lp->txlogname == NULL) return SCPE_MEM; /* can't? */ +strncpy (lp->txlogname, cptr, CBUFSIZE); /* save file name */ +lp->txlog = fopen (cptr, "ab"); /* open log */ +if (lp->txlog == NULL) { /* error? */ + free (lp->txlogname); /* free buffer */ + return SCPE_OPENERR; + } +return SCPE_OK; +} + +/* Disable logging for line */ + +t_stat tmxr_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; + +if (cptr) return SCPE_2MARG; /* no arguments */ +lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ +if (lp == NULL) return SCPE_IERR; +if (lp->txlog) { /* logging? */ + fclose (lp->txlog); /* close log */ + free (lp->txlogname); /* free namebuf */ + lp->txlog = NULL; + lp->txlogname = NULL; + } +return SCPE_OK; +} + +/* Show logging status for line */ + +t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, void *desc) +{ +TMXR *mp = (TMXR *) desc; +TMLN *lp; + +lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ +if (lp == NULL) return SCPE_IERR; +if (lp->txlog) fprintf (st, "logging to %s", lp->txlogname); +else fprintf (st, "no logging"); +return SCPE_OK; +} + +/* Find line descriptor */ + +TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp) +{ +if (uptr) { /* called from SET? */ + DEVICE *dptr = find_dev_from_unit (uptr); /* find device */ + if (dptr == NULL) return NULL; /* what?? */ + val = (int32) (uptr - dptr->units); /* implicit line # */ + } +if ((val < 0) || (val >= mp->lines)) return NULL; /* invalid line? */ +return mp->ldsc + val; /* line descriptor */ +} diff --git a/sim_tmxr.h b/sim_tmxr.h new file mode 100644 index 0000000..c9a299e --- /dev/null +++ b/sim_tmxr.h @@ -0,0 +1,103 @@ +/* sim_tmxr.h: terminal multiplexor definitions + + Copyright (c) 2001-2005, Robert M Supnik + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of Robert M Supnik shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from Robert M Supnik. + + Based on the original DZ11 simulator by Thord Nilson, as updated by + Arthur Krewat. + + 04-Jan-04 RMS Changed TMXR ldsc to be pointer to linedesc array + Added tmxr_linemsg, logging (from Mark Pizzolato) + 29-Dec-03 RMS Added output stall support, increased buffer size + 22-Dec-02 RMS Added break support (from Mark Pizzolato) + 20-Aug-02 RMS Added tmxr_open_master, tmxr_close_master, tmxr.port + 30-Dec-01 RMS Renamed tmxr_fstatus, added tmxr_fstats + 20-Oct-01 RMS Removed tmxr_getchar, formalized buffer guard, + added tmxr_rqln, tmxr_tqln +*/ + +#ifndef _SIM_TMXR_H_ +#define _SIM_TMXR_H_ 0 + +#define TMXR_V_VALID 15 +#define TMXR_VALID (1 << TMXR_V_VALID) +#define TMXR_MAXBUF 256 /* buffer size */ +#define TMXR_GUARD 12 /* buffer guard */ + +struct tmln { + SOCKET conn; /* line conn */ + uint32 ipad; /* IP address */ + uint32 cnms; /* conn time */ + int32 tsta; /* Telnet state */ + int32 rcve; /* rcv enable */ + int32 xmte; /* xmt enable */ + int32 dstb; /* disable Tlnt bin */ + int32 rxbpr; /* rcv buf remove */ + int32 rxbpi; /* rcv buf insert */ + int32 rxcnt; /* rcv count */ + int32 txbpr; /* xmt buf remove */ + int32 txbpi; /* xmt buf insert */ + int32 txcnt; /* xmt count */ + FILE *txlog; /* xmt log file */ + char *txlogname; /* xmt log file name */ + char rxb[TMXR_MAXBUF]; /* rcv buffer */ + char rbr[TMXR_MAXBUF]; /* rcv break */ + char txb[TMXR_MAXBUF]; /* xmt buffer */ + }; + +typedef struct tmln TMLN; + +struct tmxr { + int32 lines; /* # lines */ + int32 port; /* listening port */ + SOCKET master; /* master socket */ + TMLN *ldsc; /* line descriptors */ + }; + +typedef struct tmxr TMXR; + +int32 tmxr_poll_conn (TMXR *mp); +void tmxr_reset_ln (TMLN *lp); +int32 tmxr_getc_ln (TMLN *lp); +void tmxr_poll_rx (TMXR *mp); +t_stat tmxr_putc_ln (TMLN *lp, int32 chr); +void tmxr_poll_tx (TMXR *mp); +t_stat tmxr_open_master (TMXR *mp, char *cptr); +t_stat tmxr_close_master (TMXR *mp); +t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr); +t_stat tmxr_detach (TMXR *mp, UNIT *uptr); +t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); +t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); +void tmxr_msg (SOCKET sock, char *msg); +void tmxr_linemsg (TMLN *lp, char *msg); +void tmxr_fconns (FILE *st, TMLN *lp, int32 ln); +void tmxr_fstats (FILE *st, TMLN *lp, int32 ln); +t_stat tmxr_set_log (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tmxr_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat tmxr_show_log (FILE *st, UNIT *uptr, int32 val, void *desc); +t_stat tmxr_dscln (UNIT *uptr, int32 val, char *cptr, void *desc); +int32 tmxr_rqln (TMLN *lp); +int32 tmxr_tqln (TMLN *lp); + +#endif + -- 2.32.0

IFX&JS-T%qK@gA zf>}hAKaYs=!$6jQ6KTu^m4eF!?-hJl@J%4~Y>lNcu?LX(`U(ya93dDIoDQ_lf}at4 z!aseVE8K+uLRj87Hp2c-Uaf;Ggm@eY{S2mPLi`lg}J zusuV>?pRL{k$%76`$W|D6CmsRFG2P#(&>Vo1$zr#L409t%2gOMS#D1v%I$`+ne$&yAmyAdc%k4J!RrLeiIBIH zi1uD4_)m$~D1+s+#~;h-NE+?hP4FDS^8~X63&6|zTn(fiGX&=gE)~3si1IfRk#`%A z<-AH7B8~hHlSclxC7!;{_|`yUHzMrw z0uel~F&+B7C%8xOYr!_?qtx$wBIJw!QqD{wbgv-7KEEZxKIg*ssn0cn_Yq;I*NL!G zJ&^LgCc;h%eTUeJ2t7Ipo+j8&a3m4=#tVG~5o5ztp=T1&$FC=%@7^r&w+jAJ@L|Cx z1YaehUT*?fFF(d8>K_79pGySC3tlZaO>ia=dfX-SYQe_^pC&@@=LI)O{NI5rCmn6U z@;d=peqY$qlnbQY#tS`3utM;DNnb7WBZ5y0zRq;W-y!rTf}aZ>5NwSx%I=RlKtN-E z!GRJ#ROpd{S4sSIp?@q`E?C2Kl>36v4WvFqFPGk*}sa!(QLAvluh$UlWN@)wgv z{!&RV6TDIIZl({w*JXv?EchDZ2V#9F^xp;R1wR%1N-zz5hx!ZvQooTxj}@HFc$D+3 z&^t+kZyyom9F%y4K1Mmcf#e@6bSY`@KPfc-8+6j26On(v#2*q&g-@{l=|I-M6KTlp zD%f4{LcuYDR}qo#S|Ib4kcR)y6I>>EFA@FzA;Cw8kpH;gTERaFz9{&L;9mv*Cb*x7 za=!tx+%Bj)$Inc`UV;Mz&lkKI$o3up8*p4KCBl|JCBhb+1_5EG2$1vpX<0zjrw1uqjU61-Y)y5Mz!*9$Hd{FUGu!KVfPELbb}k>Fmze+z2M z)#nt!a|Lq*X9~^{EEBv*utG2*U)n_%A^l zec9MgFio(NAb&c6={*F`5zG=a=b}g-DfAe@@q!ZtuMsR3G~aASz6C;?b5+nwgyt9B zS?=wED+KQsd{FSWf=>zZXU&-JFM=-$zAE^(;NJz!`78MO3k=M+R}f!9H}t;+Q!s`y zT?+;UPZsPZXug#QzWzd+^IOm(gw7SbRM32H8|haFeXZb3!MTFwd>4GT2pthL=e&ro z5&CYym4g2(_?RGno`LmPCuq)pLDvdhC%8k9=fdRQBiJOkPw-noj)~-R1OtNn{cXmZ zb7Nq4q5B995*#XcvEXPyzO!cjBEhQ#rwd*uc)j3_f)#>46RZ}zOYlCyUkg4W$RDU- zd4Ci%=g*+|dp?YRMer>_a}JI8_k}iJk^}v@&|eFh^Jv8TF@aD{Tfua}E`phYJq7y- z4iV(99Wj57;3a~W3r-fCA~-{Ej$l~OoL@r@f1jT6qJsQo9@4)Qd_eGF!AAwnSCzr{ zoX{Huw+ZssBq(RQ;Jbn!2{sCTDaapsA%8o}mBbE$rwX1ac(&j`!Se+#5-b$t{{=uf z(*#Qe=L_B-$lv=RUzOl8!8---6C+#tABkUz9TzP}2Z_X?mt6#5fE{*n^;4hkL?Y}Lk% zPZi7%JVo#fLG#`L`T5H{lyjcog@XJ&F2?5xP7_=%Xx=j*{eMYg&it+5<3ub1^GzEhCz37Gzb;C4ZC?vMDxLbq${@|`0%jEM3t5;W)a;J;qza>1L3 z(CZKp=?=oo$N%Spm`23kC?e8-CHM#t>5mCMMa17uBKZC(__bhqsvF-;aDdMgbZ_T4!O&O?`}!IUvQQ1UDD1iX95v&CJJ6jgq(XN{daKqK6a2)=Z|jzsWvmGsGi#gaZta5fQq<&yr6;2ueD5d4$~zONNI01#BG+jqRqPhYLMfXtUn}e^}@n zC4HIDcL@EE&})R=AoLcYcM82r=)FSk7n(omL4C}=3*}`B&0m6|zUK=)TuMv8^&>Mt)Tj-raHwnF0XiUTSAr|*8ArbCj^!h)KdR>_S literal 0 HcmV?d00001 diff --git a/VAX780/vax_cmode.o b/VAX780/vax_cmode.o new file mode 100644 index 0000000000000000000000000000000000000000..640c4044f7ff1b2f76f7ac2644a3490bddd97a55 GIT binary patch literal 31316 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 RA;(Jl>h%KtSd10F{|hzxR&oFU literal 0 HcmV?d00001 diff --git a/VAX780/vax_cpu.o b/VAX780/vax_cpu.o new file mode 100644 index 0000000000000000000000000000000000000000..ee35c6ca69b41ff570a93d4708bb1fa078323707 GIT binary patch literal 124048 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 literal 0 HcmV?d00001 diff --git a/VAX780/vax_cpu1.o b/VAX780/vax_cpu1.o new file mode 100644 index 0000000000000000000000000000000000000000..adda64c858085bb0609314b843184e1a350bdc47 GIT binary patch literal 47036 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 literal 0 HcmV?d00001 diff --git a/VAX780/vax_fpa.o b/VAX780/vax_fpa.o new file mode 100644 index 0000000000000000000000000000000000000000..3c556b0ffab1978e7988c8477f81280b2a0fcf78 GIT binary patch literal 26492 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 Ov3yxJ{uoS`dGPOyRN{yL literal 0 HcmV?d00001 diff --git a/VAX780/vax_mmu.o b/VAX780/vax_mmu.o new file mode 100644 index 0000000000000000000000000000000000000000..635dc7c6c2cb15ae9e0a9d6c5395b913aaaa91ec GIT binary patch literal 22224 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